diff --git a/AndroidTutorial4_1Update.md b/AndroidTutorial4_1Update.md new file mode 100755 index 0000000..c58e904 --- /dev/null +++ b/AndroidTutorial4_1Update.md @@ -0,0 +1,166 @@ +# 錯誤修正:4-1 使用照相機與麥克風 + +這一章加入的照相與錄音功能,把照片與錄音檔案名稱儲存在同一個欄位。在完成這一章的內容後依照下列的步驟修正錯誤: + +1. 開啟「Item.java」,加入下列的欄位與方法宣告: + + // 錄音檔案名稱 + private String recFileName; + + public String getRecFileName() { + return recFileName; + } + + public void setRecFileName(String recFileName) { + this.recFileName = recFileName; + } + +2. 同樣在「Item.java」,為建構子加入錄音檔案名稱參數: + + // 錄音檔案名稱參數:String recFileName + public Item(long id, long datetime, Colors color, String title, + String content, String fileName, String recFileName, + double latitude, double longitude, long lastModify) { + this.id = id; + this.datetime = datetime; + this.color = color; + this.title = title; + this.content = content; + this.fileName = fileName; + // 錄音檔案名稱 + this.recFileName = recFileName; + this.latitude = latitude; + this.longitude = longitude; + this.lastModify = lastModify; + } + +3. 開啟「ItemDAO.java」,加入與修改下列的欄位宣告: + + ... + // 錄音檔案名稱 + public static final String RECFILENAME_COLUMN = "recfilename"; + ... + // 在「FILENAME_COLUMN」下方加入錄音檔案名稱欄位 + public static final String CREATE_TABLE = + "CREATE TABLE " + TABLE_NAME + " (" + + ... + FILENAME_COLUMN + " TEXT, " + + RECFILENAME_COLUMN + " TEXT, " + // 增加錄音檔案名稱 + ..."; + +4. 同樣在「ItemDAO.java」,修改「insert」方法: + + public Item insert(Item item) { + ContentValues cv = new ContentValues(); + ... + cv.put(FILENAME_COLUMN, item.getFileName()); + // 錄音檔案名稱 + cv.put(RECFILENAME_COLUMN, item.getRecFileName()); + ... + } + +5. 同樣在「ItemDAO.java」,修改「update」方法: + + public boolean update(Item item) { + ContentValues cv = new ContentValues(); + + ... + cv.put(FILENAME_COLUMN, item.getFileName()); + // 錄音檔案名稱 + cv.put(RECFILENAME_COLUMN, item.getRecFileName()); + ... + } + +6. 同樣在「ItemDAO.java」,修改「getRecord」方法: + + public Item getRecord(Cursor cursor) { + ... + result.setFileName(cursor.getString(5)); + // 錄音檔案名稱 + result.setRecFileName(cursor.getString(6)); + // 後續的編號都要加一 + result.setLatitude(cursor.getDouble(7)); + ... + } + +7. 同樣在「ItemDAO.java」,修改「sample」方法: + + public void sample() { + // 增加錄音檔案名稱參數「""」 + Item item = new Item(0, new Date().getTime(), Colors.RED, "關於Android Tutorial的事情.", "Hello content", "", "", 0, 0, 0); + Item item2 = new Item(0, new Date().getTime(), Colors.BLUE, "一隻非常可愛的小狗狗!", "她的名字叫「大熱狗」,又叫\n作「奶嘴」,是一隻非常可愛\n的小狗。", "", "", 25.04719, 121.516981, 0); + Item item3 = new Item(0, new Date().getTime(), Colors.GREEN, "一首非常好聽的音樂!", "Hello content", "", "", 0, 0, 0); + Item item4 = new Item(0, new Date().getTime(), Colors.ORANGE, "儲存在資料庫的資料", "Hello content", "", "", 0, 0, 0); + + ... + } + +8. 開啟「MyDBHelper.java」,增加資料庫版本編號: + + // 資料庫版本,資料結構改變的時候要更改這個數字,通常是加一 + public static final int VERSION = 2; + +9. 開啟「ItemActivity.java」,增加錄音檔案名稱欄位變數: + + // 錄音檔案名稱 + private String recFileName; + +10. 同樣在「ItemActivity.java」,增加取得錄音檔案名稱的方法: + + private File configRecFileName(String prefix, String extension) { + // 如果記事資料已經有檔案名稱 + if (item.getRecFileName() != null && item.getRecFileName().length() > 0) { + recFileName = item.getRecFileName(); + } + // 產生檔案名稱 + else { + recFileName = FileUtil.getUniqueFileName(); + } + + return new File(FileUtil.getExternalStorageDir(FileUtil.APP_DIR), + prefix + recFileName + extension); + } + +11. 同樣在「ItemActivity.java」,修改啟動錄音元件的方法: + + public void clickFunction(View view) { + int id = view.getId(); + + switch (id) { + ... + case R.id.record_sound: + // 修改呼叫方法的名稱為「configRecFileName」 + final File recordFile = configRecFileName("R", ".mp3"); + + if (recordFile.exists()) { + ... + } + // 如果沒有錄音檔,啟動錄音元件 + else { + goToRecord(recordFile); + } + + break; + ... + } + + } + +12. 同樣在「ItemActivity.java」,找到「onActivityResult」方法,修改設定錄音檔案名稱呼叫的方法: + + @Override + protected void onActivityResult(int requestCode, int resultCode, Intent data) { + if (resultCode == Activity.RESULT_OK) { + switch (requestCode) { + ... + case START_RECORD: + // 修改設定錄音檔案名稱 + item.setRecFileName(recFileName); + break; + ... + } + } + } + + +完成全部的修改以後執行應用程式,測試同一個記事資料照相與錄音的功能。 diff --git a/README.md b/README.md index 30650cd..8327779 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,7 @@ #Android Tutorial +### 2015/10/22:Android Tutorial系列專欄已經更新為Android 6,於2015/10/22開始連載,每週一篇,第一篇專欄 [http://www.codedata.com.tw/mobile/android-6-tutorial-1-1/](http://www.codedata.com.tw/mobile/android-6-tutorial-1-1/) 。GitHub在 [https://github.com/macdidi5/Android-6-Tutorial](https://github.com/macdidi5/Android-6-Tutorial) 。 + ### 2015/03/02:專欄與範例程式改為Android 5與Android Studio ### 需要從Eclipse ADT專案轉移到Android Studio,請參考AndroidTutorial5_Migrate.md文件的說明 @@ -10,21 +12,31 @@ 這個Android APP會在六小時的課程內容,帶領你認識大部份Android APP開發時會遇到的主題,從APP的規劃開始,畫面、互動、資料庫、多媒體、檔案、服務、通知、位置、地圖還有小工具,這些就是你會學到的東西。 * Android Tutorial 第一堂 - * [西遊記裡的那隻潑猴](http://www.codedata.com.tw/mobile/android-tutorial-the-1st-class-1-sunwukong/) - * [準備Android Studio開發環境](http://www.codedata.com.tw/mobile/android-tutorial-the-1st-class-2-android-sdk/) - * [開始設計Android應用程式](http://www.codedata.com.tw/mobile/android-tutorial-the-1st-class-3-app-project/) - * [開發Android應用程式的準備工作](http://www.codedata.com.tw/mobile/android-tutorial-the-1st-class-4-before-developing-an-app/) + * [(1)西遊記裡的那隻潑猴](http://www.codedata.com.tw/mobile/android-tutorial-the-1st-class-1-sunwukong/) + * [(2)準備Android Studio開發環境](http://www.codedata.com.tw/mobile/android-tutorial-the-1st-class-2-android-sdk/) + * [(3)開始設計Android應用程式](http://www.codedata.com.tw/mobile/android-tutorial-the-1st-class-3-app-project/) + * [(4)開發Android應用程式的準備工作](http://www.codedata.com.tw/mobile/android-tutorial-the-1st-class-4-before-developing-an-app/) * Android Tutorial 第二堂 - * [建立應用程式需要的資源](http://www.codedata.com.tw/mobile/android-tutorial-the-2nd-class-1-res/) - * [設計應用程式使用者介面](http://www.codedata.com.tw/mobile/android-tutorial-the-2nd-class-2-ui/) - * [應用程式與使用者的互動](http://www.codedata.com.tw/mobile/android-tutorial-the-2nd-class-3-interaction/) - * [建立與使用Activity元件](http://www.codedata.com.tw/mobile/android-tutorial-the-2nd-class-4-activity/) + * [(1)建立應用程式需要的資源](http://www.codedata.com.tw/mobile/android-tutorial-the-2nd-class-1-res/) + * [(2)設計應用程式使用者介面](http://www.codedata.com.tw/mobile/android-tutorial-the-2nd-class-2-ui/) + * [(3)應用程式與使用者的互動](http://www.codedata.com.tw/mobile/android-tutorial-the-2nd-class-3-interaction/) + * [(4)建立與使用Activity元件](http://www.codedata.com.tw/mobile/android-tutorial-the-2nd-class-4-activity/) * Android Tutorial 第三堂 - * [為ListView元件建立自定畫面](http://www.codedata.com.tw/mobile/android-tutorial-the-3rd-class-1-listview/) - * [儲存與讀取應用程式資訊](http://www.codedata.com.tw/mobile/android-tutorial-the-3rd-class-2-preference) - * [使用Android內建的SQLite資料庫](http://www.codedata.com.tw/mobile/android-tutorial-the-3rd-class-3-sqlite/) + * [(1)為ListView元件建立自定畫面](http://www.codedata.com.tw/mobile/android-tutorial-the-3rd-class-1-listview/) + * [(2)儲存與讀取應用程式資訊](http://www.codedata.com.tw/mobile/android-tutorial-the-3rd-class-2-preference) + * [(3)使用Android內建的SQLite資料庫](http://www.codedata.com.tw/mobile/android-tutorial-the-3rd-class-3-sqlite/) * Android Tutorial 第四堂 - * [使用照相機與麥克風](http://www.codedata.com.tw/mobile/android-tutorial-the-4th-class-1-camera-microphone/) + * [(1)使用照相機與麥克風](http://www.codedata.com.tw/mobile/android-tutorial-the-4th-class-1-camera-microphone/) + * [(2)設計地圖應用程式 - Google Maps Android API v2](http://www.codedata.com.tw/mobile/android-tutorial-the-4th-class-google-maps-android-api-v2/) + * [(3)讀取裝置目前的位置 - Google Services Location](http://www.codedata.com.tw/mobile/android-tutorial-the-4th-class-3-google-services-location/) +* Android Tutorial 第五堂 + * [(1) 建立廣播接收元件 - BroadcastReceiver](http://www.codedata.com.tw/mobile/android-tutorial-the-5th-class-1-broadcastreceiver-alarmmanager/) + * [(2) 系統通知服務 - Notification](http://www.codedata.com.tw/mobile/android-tutorial-the-5th-class-2-notification/) + * [(3) 設計小工具元件 - AppWidget](http://www.codedata.com.tw/mobile/android-tutorial-the-5th-class-3-appwidget/) +* Android Tutorial 第六堂 + * [(1) Material Design - Theme與Transition](http://www.codedata.com.tw/mobile/android-tutorial-the-6th-class-material-design-theme-transition/) + * [(2) Material Design - RecylerView](http://www.codedata.com.tw/mobile/android-tutorial-the-6th-class-2-material-design-recylerview/) + * [(3) Material Design - Shared Element與自定動畫效果](http://www.codedata.com.tw/mobile/android-tutorial-the-6th-class-material-design-shared-element/) =============== http://www.codedata.com.tw/author/michael diff --git a/examples/0401/MyAndroidTutorial/MyAndroidTutorial.iml b/examples/0401/MyAndroidTutorial/MyAndroidTutorial.iml index 2a02201..42f4ed5 100644 --- a/examples/0401/MyAndroidTutorial/MyAndroidTutorial.iml +++ b/examples/0401/MyAndroidTutorial/MyAndroidTutorial.iml @@ -1,5 +1,5 @@ - + @@ -7,7 +7,7 @@ - + diff --git a/examples/0401/MyAndroidTutorial/app/app.iml b/examples/0401/MyAndroidTutorial/app/app.iml index c6c55c4..86cbf3a 100644 --- a/examples/0401/MyAndroidTutorial/app/app.iml +++ b/examples/0401/MyAndroidTutorial/app/app.iml @@ -1,5 +1,5 @@ - + @@ -9,6 +9,7 @@ + diff --git a/examples/0401/MyAndroidTutorial/app/src/main/java/net/macdidi/myandroidtutorial/Item.java b/examples/0401/MyAndroidTutorial/app/src/main/java/net/macdidi/myandroidtutorial/Item.java index b015021..7fa030a 100644 --- a/examples/0401/MyAndroidTutorial/app/src/main/java/net/macdidi/myandroidtutorial/Item.java +++ b/examples/0401/MyAndroidTutorial/app/src/main/java/net/macdidi/myandroidtutorial/Item.java @@ -5,13 +5,14 @@ public class Item implements java.io.Serializable { - // 編號、日期時間、顏色、標題、內容、檔案名稱、經緯度、修改、已選擇 + // 編號、日期時間、顏色、標題、內容、檔案名稱、錄音檔案名稱、經緯度、修改、已選擇 private long id; private long datetime; private Colors color; private String title; private String content; private String fileName; + private String recFileName; private double latitude; private double longitude; private long lastModify; @@ -24,14 +25,15 @@ public Item() { } public Item(long id, long datetime, Colors color, String title, - String content, String fileName, double latitude, double longitude, - long lastModify) { + String content, String fileName, String recFileName, + double latitude, double longitude, long lastModify) { this.id = id; this.datetime = datetime; this.color = color; this.title = title; this.content = content; this.fileName = fileName; + this.recFileName = recFileName; this.latitude = latitude; this.longitude = longitude; this.lastModify = lastModify; @@ -100,6 +102,14 @@ public void setFileName(String fileName) { this.fileName = fileName; } + public String getRecFileName() { + return recFileName; + } + + public void setRecFileName(String recFileName) { + this.recFileName = recFileName; + } + public double getLatitude() { return latitude; } diff --git a/examples/0401/MyAndroidTutorial/app/src/main/java/net/macdidi/myandroidtutorial/ItemActivity.java b/examples/0401/MyAndroidTutorial/app/src/main/java/net/macdidi/myandroidtutorial/ItemActivity.java index 7941f0b..ad8fc29 100644 --- a/examples/0401/MyAndroidTutorial/app/src/main/java/net/macdidi/myandroidtutorial/ItemActivity.java +++ b/examples/0401/MyAndroidTutorial/app/src/main/java/net/macdidi/myandroidtutorial/ItemActivity.java @@ -33,6 +33,7 @@ public class ItemActivity extends Activity { // 檔案名稱 private String fileName; + private String recFileName; // 照片 private ImageView picture; @@ -99,7 +100,7 @@ protected void onActivityResult(int requestCode, int resultCode, Intent data) { break; case START_RECORD: // 設定錄音檔案名稱 - item.setFileName(fileName); + item.setRecFileName(recFileName); break; case START_LOCATION: break; @@ -197,7 +198,7 @@ public void clickFunction(View view) { break; case R.id.record_sound: // 錄音檔案名稱 - final File recordFile = configFileName("R", ".mp3"); + final File recordFile = configRecFileName("R", ".mp3"); // 如果已經有錄音檔,詢問播放或重新錄製 if (recordFile.exists()) { @@ -268,4 +269,18 @@ private File configFileName(String prefix, String extension) { prefix + fileName + extension); } + private File configRecFileName(String prefix, String extension) { + // 如果記事資料已經有檔案名稱 + if (item.getRecFileName() != null && item.getRecFileName().length() > 0) { + recFileName = item.getRecFileName(); + } + // 產生檔案名稱 + else { + recFileName = FileUtil.getUniqueFileName(); + } + + return new File(FileUtil.getExternalStorageDir(FileUtil.APP_DIR), + prefix + recFileName + extension); + } + } diff --git a/examples/0401/MyAndroidTutorial/app/src/main/java/net/macdidi/myandroidtutorial/ItemDAO.java b/examples/0401/MyAndroidTutorial/app/src/main/java/net/macdidi/myandroidtutorial/ItemDAO.java index f5a3f62..ac5a57a 100644 --- a/examples/0401/MyAndroidTutorial/app/src/main/java/net/macdidi/myandroidtutorial/ItemDAO.java +++ b/examples/0401/MyAndroidTutorial/app/src/main/java/net/macdidi/myandroidtutorial/ItemDAO.java @@ -23,6 +23,7 @@ public class ItemDAO { public static final String TITLE_COLUMN = "title"; public static final String CONTENT_COLUMN = "content"; public static final String FILENAME_COLUMN = "filename"; + public static final String RECFILENAME_COLUMN = "recfilename"; public static final String LATITUDE_COLUMN = "latitude"; public static final String LONGITUDE_COLUMN = "longitude"; public static final String LASTMODIFY_COLUMN = "lastmodify"; @@ -36,6 +37,7 @@ public class ItemDAO { TITLE_COLUMN + " TEXT NOT NULL, " + CONTENT_COLUMN + " TEXT NOT NULL, " + FILENAME_COLUMN + " TEXT, " + + RECFILENAME_COLUMN + " TEXT, " + LATITUDE_COLUMN + " REAL, " + LONGITUDE_COLUMN + " REAL, " + LASTMODIFY_COLUMN + " INTEGER)"; @@ -65,6 +67,7 @@ public Item insert(Item item) { cv.put(TITLE_COLUMN, item.getTitle()); cv.put(CONTENT_COLUMN, item.getContent()); cv.put(FILENAME_COLUMN, item.getFileName()); + cv.put(RECFILENAME_COLUMN, item.getRecFileName()); cv.put(LATITUDE_COLUMN, item.getLatitude()); cv.put(LONGITUDE_COLUMN, item.getLongitude()); cv.put(LASTMODIFY_COLUMN, item.getLastModify()); @@ -93,6 +96,7 @@ public boolean update(Item item) { cv.put(TITLE_COLUMN, item.getTitle()); cv.put(CONTENT_COLUMN, item.getContent()); cv.put(FILENAME_COLUMN, item.getFileName()); + cv.put(RECFILENAME_COLUMN, item.getRecFileName()); cv.put(LATITUDE_COLUMN, item.getLatitude()); cv.put(LONGITUDE_COLUMN, item.getLongitude()); cv.put(LASTMODIFY_COLUMN, item.getLastModify()); @@ -160,9 +164,10 @@ public Item getRecord(Cursor cursor) { result.setTitle(cursor.getString(3)); result.setContent(cursor.getString(4)); result.setFileName(cursor.getString(5)); - result.setLatitude(cursor.getDouble(6)); - result.setLongitude(cursor.getDouble(7)); - result.setLastModify(cursor.getLong(8)); + result.setRecFileName(cursor.getString(6)); + result.setLatitude(cursor.getDouble(7)); + result.setLongitude(cursor.getDouble(8)); + result.setLastModify(cursor.getLong(9)); // 回傳結果 return result; @@ -182,10 +187,10 @@ public int getCount() { // 建立範例資料 public void sample() { - Item item = new Item(0, new Date().getTime(), Colors.RED, "關於Android Tutorial的事情.", "Hello content", "", 0, 0, 0); - Item item2 = new Item(0, new Date().getTime(), Colors.BLUE, "一隻非常可愛的小狗狗!", "她的名字叫「大熱狗」,又叫\n作「奶嘴」,是一隻非常可愛\n的小狗。", "", 25.04719, 121.516981, 0); - Item item3 = new Item(0, new Date().getTime(), Colors.GREEN, "一首非常好聽的音樂!", "Hello content", "", 0, 0, 0); - Item item4 = new Item(0, new Date().getTime(), Colors.ORANGE, "儲存在資料庫的資料", "Hello content", "", 0, 0, 0); + Item item = new Item(0, new Date().getTime(), Colors.RED, "關於Android Tutorial的事情.", "Hello content", "", "", 0, 0, 0); + Item item2 = new Item(0, new Date().getTime(), Colors.BLUE, "一隻非常可愛的小狗狗!", "她的名字叫「大熱狗」,又叫\n作「奶嘴」,是一隻非常可愛\n的小狗。", "", "", 25.04719, 121.516981, 0); + Item item3 = new Item(0, new Date().getTime(), Colors.GREEN, "一首非常好聽的音樂!", "Hello content", "", "", 0, 0, 0); + Item item4 = new Item(0, new Date().getTime(), Colors.ORANGE, "儲存在資料庫的資料", "Hello content", "", "", 0, 0, 0); insert(item); insert(item2); diff --git a/examples/0401/MyAndroidTutorial/app/src/main/java/net/macdidi/myandroidtutorial/MyDBHelper.java b/examples/0401/MyAndroidTutorial/app/src/main/java/net/macdidi/myandroidtutorial/MyDBHelper.java index b1dc3c4..b8b77d6 100644 --- a/examples/0401/MyAndroidTutorial/app/src/main/java/net/macdidi/myandroidtutorial/MyDBHelper.java +++ b/examples/0401/MyAndroidTutorial/app/src/main/java/net/macdidi/myandroidtutorial/MyDBHelper.java @@ -10,7 +10,7 @@ public class MyDBHelper extends SQLiteOpenHelper { // 資料庫名稱 public static final String DATABASE_NAME = "mydata.db"; // 資料庫版本,資料結構改變的時候要更改這個數字,通常是加一 - public static final int VERSION = 1; + public static final int VERSION = 2; // 資料庫物件,固定的欄位變數 private static SQLiteDatabase database; diff --git a/examples/0402/MyAndroidTutorial/.gitignore b/examples/0402/MyAndroidTutorial/.gitignore new file mode 100644 index 0000000..afbdab3 --- /dev/null +++ b/examples/0402/MyAndroidTutorial/.gitignore @@ -0,0 +1,6 @@ +.gradle +/local.properties +/.idea/workspace.xml +/.idea/libraries +.DS_Store +/build diff --git a/examples/0402/MyAndroidTutorial/.idea/.name b/examples/0402/MyAndroidTutorial/.idea/.name new file mode 100644 index 0000000..5bb7a85 --- /dev/null +++ b/examples/0402/MyAndroidTutorial/.idea/.name @@ -0,0 +1 @@ +MyAndroidTutorial \ No newline at end of file diff --git a/examples/0402/MyAndroidTutorial/.idea/compiler.xml b/examples/0402/MyAndroidTutorial/.idea/compiler.xml new file mode 100644 index 0000000..217af47 --- /dev/null +++ b/examples/0402/MyAndroidTutorial/.idea/compiler.xml @@ -0,0 +1,23 @@ + + + + + + diff --git a/examples/0402/MyAndroidTutorial/.idea/copyright/profiles_settings.xml b/examples/0402/MyAndroidTutorial/.idea/copyright/profiles_settings.xml new file mode 100644 index 0000000..e7bedf3 --- /dev/null +++ b/examples/0402/MyAndroidTutorial/.idea/copyright/profiles_settings.xml @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/examples/0402/MyAndroidTutorial/.idea/encodings.xml b/examples/0402/MyAndroidTutorial/.idea/encodings.xml new file mode 100644 index 0000000..e206d70 --- /dev/null +++ b/examples/0402/MyAndroidTutorial/.idea/encodings.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/examples/0402/MyAndroidTutorial/.idea/gradle.xml b/examples/0402/MyAndroidTutorial/.idea/gradle.xml new file mode 100644 index 0000000..fe865d3 --- /dev/null +++ b/examples/0402/MyAndroidTutorial/.idea/gradle.xml @@ -0,0 +1,19 @@ + + + + + + + diff --git a/examples/0402/MyAndroidTutorial/.idea/misc.xml b/examples/0402/MyAndroidTutorial/.idea/misc.xml new file mode 100644 index 0000000..9076de5 --- /dev/null +++ b/examples/0402/MyAndroidTutorial/.idea/misc.xml @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/examples/0402/MyAndroidTutorial/.idea/modules.xml b/examples/0402/MyAndroidTutorial/.idea/modules.xml new file mode 100644 index 0000000..327df67 --- /dev/null +++ b/examples/0402/MyAndroidTutorial/.idea/modules.xml @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/examples/0402/MyAndroidTutorial/.idea/scopes/scope_settings.xml b/examples/0402/MyAndroidTutorial/.idea/scopes/scope_settings.xml new file mode 100644 index 0000000..922003b --- /dev/null +++ b/examples/0402/MyAndroidTutorial/.idea/scopes/scope_settings.xml @@ -0,0 +1,5 @@ + + + + \ No newline at end of file diff --git a/examples/0402/MyAndroidTutorial/.idea/vcs.xml b/examples/0402/MyAndroidTutorial/.idea/vcs.xml new file mode 100644 index 0000000..def6a6a --- /dev/null +++ b/examples/0402/MyAndroidTutorial/.idea/vcs.xml @@ -0,0 +1,7 @@ + + + + + + + diff --git a/examples/0402/MyAndroidTutorial/MyAndroidTutorial.iml b/examples/0402/MyAndroidTutorial/MyAndroidTutorial.iml new file mode 100644 index 0000000..0bb6048 --- /dev/null +++ b/examples/0402/MyAndroidTutorial/MyAndroidTutorial.iml @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + diff --git a/examples/0402/MyAndroidTutorial/app/.gitignore b/examples/0402/MyAndroidTutorial/app/.gitignore new file mode 100644 index 0000000..796b96d --- /dev/null +++ b/examples/0402/MyAndroidTutorial/app/.gitignore @@ -0,0 +1 @@ +/build diff --git a/examples/0402/MyAndroidTutorial/app/app.iml b/examples/0402/MyAndroidTutorial/app/app.iml new file mode 100644 index 0000000..b767e16 --- /dev/null +++ b/examples/0402/MyAndroidTutorial/app/app.iml @@ -0,0 +1,113 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/examples/0402/MyAndroidTutorial/app/build.gradle b/examples/0402/MyAndroidTutorial/app/build.gradle new file mode 100644 index 0000000..e1475ee --- /dev/null +++ b/examples/0402/MyAndroidTutorial/app/build.gradle @@ -0,0 +1,26 @@ +apply plugin: 'com.android.application' + +android { + compileSdkVersion 21 + buildToolsVersion "21.1.2" + + defaultConfig { + applicationId "net.macdidi.myandroidtutorial" + minSdkVersion 15 + targetSdkVersion 21 + versionCode 1 + versionName "1.0" + } + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' + } + } +} + +dependencies { + compile fileTree(dir: 'libs', include: ['*.jar']) + compile 'com.android.support:appcompat-v7:22.0.0' + compile 'com.google.android.gms:play-services:7.0.0' +} diff --git a/examples/0402/MyAndroidTutorial/app/proguard-rules.pro b/examples/0402/MyAndroidTutorial/app/proguard-rules.pro new file mode 100644 index 0000000..b5fa7ec --- /dev/null +++ b/examples/0402/MyAndroidTutorial/app/proguard-rules.pro @@ -0,0 +1,17 @@ +# Add project specific ProGuard rules here. +# By default, the flags in this file are appended to flags specified +# in /Users/macdidi5/Library/Android/sdk/tools/proguard/proguard-android.txt +# You can edit the include path and order by changing the proguardFiles +# directive in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# Add any project specific keep options here: + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} diff --git a/examples/0402/MyAndroidTutorial/app/src/androidTest/java/net/macdidi/myandroidtutorial/ApplicationTest.java b/examples/0402/MyAndroidTutorial/app/src/androidTest/java/net/macdidi/myandroidtutorial/ApplicationTest.java new file mode 100644 index 0000000..2cb214e --- /dev/null +++ b/examples/0402/MyAndroidTutorial/app/src/androidTest/java/net/macdidi/myandroidtutorial/ApplicationTest.java @@ -0,0 +1,13 @@ +package net.macdidi.myandroidtutorial; + +import android.app.Application; +import android.test.ApplicationTestCase; + +/** + * Testing Fundamentals + */ +public class ApplicationTest extends ApplicationTestCase { + public ApplicationTest() { + super(Application.class); + } +} \ No newline at end of file diff --git a/examples/0402/MyAndroidTutorial/app/src/debug/res/values/google_maps_api.xml b/examples/0402/MyAndroidTutorial/app/src/debug/res/values/google_maps_api.xml new file mode 100644 index 0000000..7341c8d --- /dev/null +++ b/examples/0402/MyAndroidTutorial/app/src/debug/res/values/google_maps_api.xml @@ -0,0 +1,18 @@ + + + + AIzaSyCZg9YWlfokPA96VxWGYr6u4C12jL16VhM + + diff --git a/examples/0402/MyAndroidTutorial/app/src/main/AndroidManifest.xml b/examples/0402/MyAndroidTutorial/app/src/main/AndroidManifest.xml new file mode 100644 index 0000000..1a59a4f --- /dev/null +++ b/examples/0402/MyAndroidTutorial/app/src/main/AndroidManifest.xml @@ -0,0 +1,93 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/examples/0402/MyAndroidTutorial/app/src/main/java/net/macdidi/myandroidtutorial/AboutActivity.java b/examples/0402/MyAndroidTutorial/app/src/main/java/net/macdidi/myandroidtutorial/AboutActivity.java new file mode 100644 index 0000000..42dddeb --- /dev/null +++ b/examples/0402/MyAndroidTutorial/app/src/main/java/net/macdidi/myandroidtutorial/AboutActivity.java @@ -0,0 +1,24 @@ +package net.macdidi.myandroidtutorial; + +import android.app.Activity; +import android.os.Bundle; +import android.view.View; +import android.view.Window; + +public class AboutActivity extends Activity { + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + // 取消元件的應用程式標題 + requestWindowFeature(Window.FEATURE_NO_TITLE); + setContentView(R.layout.activity_about); + } + + // 結束按鈕 + public void clickOk(View view) { + // 呼叫這個方法結束Activity元件 + finish(); + } + +} \ No newline at end of file diff --git a/examples/0402/MyAndroidTutorial/app/src/main/java/net/macdidi/myandroidtutorial/ColorActivity.java b/examples/0402/MyAndroidTutorial/app/src/main/java/net/macdidi/myandroidtutorial/ColorActivity.java new file mode 100644 index 0000000..182cd55 --- /dev/null +++ b/examples/0402/MyAndroidTutorial/app/src/main/java/net/macdidi/myandroidtutorial/ColorActivity.java @@ -0,0 +1,75 @@ +package net.macdidi.myandroidtutorial; + +import android.app.Activity; +import android.content.Intent; +import android.content.SharedPreferences; +import android.os.Bundle; +import android.preference.PreferenceManager; +import android.view.View; +import android.view.View.OnClickListener; +import android.widget.Button; +import android.widget.LinearLayout; + +public class ColorActivity extends Activity { + + private LinearLayout color_gallery; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_color); + + processViews(); + + ColorListener listener = new ColorListener(); + + for (Colors c : Colors.values()) { + Button button = new Button(this); + button.setId(c.parseColor()); + LinearLayout.LayoutParams layout = + new LinearLayout.LayoutParams(128, 128); + layout.setMargins(6, 6, 6, 6); + button.setLayoutParams(layout); + button.setBackgroundColor(c.parseColor()); + + button.setOnClickListener(listener); + + color_gallery.addView(button); + } + } + + private void processViews() { + color_gallery = (LinearLayout) findViewById(R.id.color_gallery); + } + + private class ColorListener implements OnClickListener { + + @Override + public void onClick(View view) { + String action = ColorActivity.this.getIntent().getAction(); + + // 經由設定元件啟動 + if (action != null && + action.equals("net.macdidi.myandroidtutorial.CHOOSE_COLOR")) { + // 建立SharedPreferences物件 + SharedPreferences.Editor editor = + PreferenceManager.getDefaultSharedPreferences( + ColorActivity.this).edit(); + // 儲存預設顏色 + editor.putInt("DEFAULT_COLOR", view.getId()); + // 寫入設定值 + editor.commit(); + finish(); + } + // 經由新增或修改記事的元件啟動 + else { + Intent result = getIntent(); + result.putExtra("colorId", view.getId()); + setResult(Activity.RESULT_OK, result); + finish(); + } + } + + } + +} \ No newline at end of file diff --git a/examples/0402/MyAndroidTutorial/app/src/main/java/net/macdidi/myandroidtutorial/Colors.java b/examples/0402/MyAndroidTutorial/app/src/main/java/net/macdidi/myandroidtutorial/Colors.java new file mode 100644 index 0000000..1462149 --- /dev/null +++ b/examples/0402/MyAndroidTutorial/app/src/main/java/net/macdidi/myandroidtutorial/Colors.java @@ -0,0 +1,24 @@ +package net.macdidi.myandroidtutorial; + +import android.graphics.Color; + +public enum Colors { + + LIGHTGREY("#D3D3D3"), BLUE("#33B5E5"), PURPLE("#AA66CC"), + GREEN("#99CC00"), ORANGE("#FFBB33"), RED("#FF4444"); + + private String code; + + private Colors(String code) { + this.code = code; + } + + public String getCode() { + return code; + } + + public int parseColor() { + return Color.parseColor(code); + } + +} \ No newline at end of file diff --git a/examples/0402/MyAndroidTutorial/app/src/main/java/net/macdidi/myandroidtutorial/FileUtil.java b/examples/0402/MyAndroidTutorial/app/src/main/java/net/macdidi/myandroidtutorial/FileUtil.java new file mode 100644 index 0000000..1fb41be --- /dev/null +++ b/examples/0402/MyAndroidTutorial/app/src/main/java/net/macdidi/myandroidtutorial/FileUtil.java @@ -0,0 +1,112 @@ +package net.macdidi.myandroidtutorial; + +import android.content.Context; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.os.Environment; +import android.util.Log; +import android.widget.ImageView; + +import java.io.File; +import java.text.SimpleDateFormat; +import java.util.Date; + +public class FileUtil { + + // 應用程式儲存檔案的目錄 + public static final String APP_DIR = "androidtutorial"; + + // 外部儲存設備是否可寫入 + public static boolean isExternalStorageWritable() { + // 取得目前外部儲存設備的狀態 + String state = Environment.getExternalStorageState(); + + // 判斷是否可寫入 + if (Environment.MEDIA_MOUNTED.equals(state)) { + return true; + } + + return false; + } + + // 外部儲存設備是否可讀取 + public static boolean isExternalStorageReadable() { + // 取得目前外部儲存設備的狀態 + String state = Environment.getExternalStorageState(); + + // 判斷是否可讀取 + if (Environment.MEDIA_MOUNTED.equals(state) || + Environment.MEDIA_MOUNTED_READ_ONLY.equals(state)) { + return true; + } + + return false; + } + + // 建立並傳回在公用相簿下參數指定的路徑 + public static File getPublicAlbumStorageDir(String albumName) { + // 取得公用的照片路徑 + File pictures = Environment.getExternalStoragePublicDirectory( + Environment.DIRECTORY_PICTURES); + // 準備在照片路徑下建立一個指定的路徑 + File file = new File(pictures, albumName); + + // 如果建立路徑不成功 + if (!file.mkdirs()) { + Log.e("getAlbumStorageDir", "Directory not created"); + } + + return file; + } + + // 建立並傳回在應用程式專用相簿下參數指定的路徑 + public static File getAlbumStorageDir(Context context, String albumName) { + // 取得應用程式專用的照片路徑 + File pictures = context.getExternalFilesDir( + Environment.DIRECTORY_PICTURES); + // 準備在照片路徑下建立一個指定的路徑 + File file = new File(pictures, albumName); + + // 如果建立路徑不成功 + if (!file.mkdirs()) { + Log.e("getAlbumStorageDir", "Directory not created"); + } + + return file; + } + + // 建立並傳回外部儲存媒體參數指定的路徑 + public static File getExternalStorageDir(String dir) { + File result = new File( + Environment.getExternalStorageDirectory(), dir); + + if (!isExternalStorageWritable()) { + return null; + } + + if (!result.exists() && !result.mkdirs()) { + return null; + } + + return result; + } + + // 讀取指定的照片檔案名稱設定給ImageView元件 + public static void fileToImageView(String fileName, ImageView imageView) { + if (new File(fileName).exists()) { + Bitmap bitmap = BitmapFactory.decodeFile(fileName); + imageView.setImageBitmap(bitmap); + } + else { + Log.e("fileToImageView", fileName + " not found."); + } + } + + // 產生唯一的檔案名稱 + public static String getUniqueFileName() { + // 使用年月日_時分秒格式為檔案名稱 + SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd_HHmmss"); + return sdf.format(new Date()); + } + +} diff --git a/examples/0402/MyAndroidTutorial/app/src/main/java/net/macdidi/myandroidtutorial/Item.java b/examples/0402/MyAndroidTutorial/app/src/main/java/net/macdidi/myandroidtutorial/Item.java new file mode 100644 index 0000000..d65ad74 --- /dev/null +++ b/examples/0402/MyAndroidTutorial/app/src/main/java/net/macdidi/myandroidtutorial/Item.java @@ -0,0 +1,146 @@ +package net.macdidi.myandroidtutorial; + +import java.util.Date; +import java.util.Locale; + +public class Item implements java.io.Serializable { + + // 編號、日期時間、顏色、標題、內容、檔案名稱、經緯度、修改、已選擇 + private long id; + private long datetime; + private Colors color; + private String title; + private String content; + private String fileName; + private String recFileName; + private double latitude; + private double longitude; + private long lastModify; + private boolean selected; + + public Item() { + title = ""; + content = ""; + color = Colors.LIGHTGREY; + } + + public Item(long id, long datetime, Colors color, String title, + String content, String fileName, String recFileName, + double latitude, double longitude, long lastModify) { + this.id = id; + this.datetime = datetime; + this.color = color; + this.title = title; + this.content = content; + this.fileName = fileName; + // 錄音檔案名稱 + this.recFileName = recFileName; + this.latitude = latitude; + this.longitude = longitude; + this.lastModify = lastModify; + } + + public long getId() { + return id; + } + + public void setId(long id) { + this.id = id; + } + + public long getDatetime() { + return datetime; + } + + // 裝置區域的日期時間 + public String getLocaleDatetime() { + return String.format(Locale.getDefault(), "%tF % 0) { + // 照片檔案物件 + File file = configFileName("P", ".jpg"); + + // 如果照片檔案存在 + if (file.exists()) { + // 顯示照片元件 + picture.setVisibility(View.VISIBLE); + // 設定照片 + FileUtil.fileToImageView(file.getAbsolutePath(), picture); + } + } + } + + @Override + protected void onActivityResult(int requestCode, int resultCode, Intent data) { + if (resultCode == Activity.RESULT_OK) { + switch (requestCode) { + // 照像 + case START_CAMERA: + // 設定照片檔案名稱 + item.setFileName(fileName); + break; + case START_RECORD: + // 設定錄音檔案名稱 + item.setRecFileName(recFileName); + break; + case START_LOCATION: + break; + case START_ALARM: + break; + // 設定顏色 + case START_COLOR: + int colorId = data.getIntExtra( + "colorId", Colors.LIGHTGREY.parseColor()); + item.setColor(getColors(colorId)); + break; + } + } + } + + public static Colors getColors(int color) { + Colors result = Colors.LIGHTGREY; + + if (color == Colors.BLUE.parseColor()) { + result = Colors.BLUE; + } + else if (color == Colors.PURPLE.parseColor()) { + result = Colors.PURPLE; + } + else if (color == Colors.GREEN.parseColor()) { + result = Colors.GREEN; + } + else if (color == Colors.ORANGE.parseColor()) { + result = Colors.ORANGE; + } + else if (color == Colors.RED.parseColor()) { + result = Colors.RED; + } + + return result; + } + + private void processViews() { + title_text = (EditText) findViewById(R.id.title_text); + content_text = (EditText) findViewById(R.id.content_text); + // 取得顯示照片的ImageView元件 + picture = (ImageView) findViewById(R.id.picture); + } + + // 點擊確定與取消按鈕都會呼叫這個方法 + public void onSubmit(View view) { + + if (view.getId() == R.id.ok_teim) { + String titleText = title_text.getText().toString(); + String contentText = content_text.getText().toString(); + + item.setTitle(titleText); + item.setContent(contentText); + + if (getIntent().getAction().equals( + "net.macdidi.myandroidtutorial.EDIT_ITEM")) { + item.setLastModify(new Date().getTime()); + } + // 新增記事 + else { + item.setDatetime(new Date().getTime()); + // 建立SharedPreferences物件 + SharedPreferences sharedPreferences = + PreferenceManager.getDefaultSharedPreferences(this); + // 讀取設定的預設顏色 + int color = sharedPreferences.getInt("DEFAULT_COLOR", -1); + item.setColor(getColors(color)); + } + + Intent result = getIntent(); + result.putExtra("net.macdidi.myandroidtutorial.Item", item); + setResult(Activity.RESULT_OK, result); + } + + // 結束 + finish(); + } + + public void clickFunction(View view) { + int id = view.getId(); + + switch (id) { + case R.id.take_picture: + // 啟動相機元件用的Intent物件 + Intent intentCamera = + new Intent(MediaStore.ACTION_IMAGE_CAPTURE); + + // 照片檔案名稱 + File pictureFile = configFileName("P", ".jpg"); + Uri uri = Uri.fromFile(pictureFile); + // 設定檔案名稱 + intentCamera.putExtra(MediaStore.EXTRA_OUTPUT, uri); + // 啟動相機元件 + startActivityForResult(intentCamera, START_CAMERA); + break; + case R.id.record_sound: + // 錄音檔案名稱 + final File recordFile = configRecFileName("R", ".mp3"); + + // 如果已經有錄音檔,詢問播放或重新錄製 + if (recordFile.exists()) { + // 詢問播放還是重新錄製的對話框 + AlertDialog.Builder d = new AlertDialog.Builder(this); + + d.setTitle(R.string.title_record) + .setCancelable(false); + d.setPositiveButton(R.string.record_play, + new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int which) { + // 播放 + Intent playIntent = new Intent( + ItemActivity.this, PlayActivity.class); + playIntent.putExtra("fileName", + recordFile.getAbsolutePath()); + startActivity(playIntent); + } + }); + d.setNeutralButton(R.string.record_new, + new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int which) { + goToRecord(recordFile); + } + }); + d.setNegativeButton(android.R.string.cancel, null); + + // 顯示對話框 + d.show(); + } + // 如果沒有錄音檔,啟動錄音元件 + else { + goToRecord(recordFile); + } + + break; + case R.id.set_location: + // 啟動地圖元件用的Intent物件 + Intent intentMap = new Intent(this, MapsActivity.class); + // 啟動地圖元件 + startActivityForResult(intentMap, START_LOCATION); + break; + case R.id.set_alarm: + break; + case R.id.select_color: + // 啟動設定顏色的Activity元件 + startActivityForResult( + new Intent(this, ColorActivity.class), START_COLOR); + break; + } + + } + + private void goToRecord(File recordFile) { + // 錄音 + Intent recordIntent = new Intent(this, RecordActivity.class); + recordIntent.putExtra("fileName", recordFile.getAbsolutePath()); + startActivityForResult(recordIntent, START_RECORD); + } + + private File configFileName(String prefix, String extension) { + // 如果記事資料已經有檔案名稱 + if (item.getFileName() != null && item.getFileName().length() > 0) { + fileName = item.getFileName(); + } + // 產生檔案名稱 + else { + fileName = FileUtil.getUniqueFileName(); + } + + return new File(FileUtil.getExternalStorageDir(FileUtil.APP_DIR), + prefix + fileName + extension); + } + + private File configRecFileName(String prefix, String extension) { + // 如果記事資料已經有檔案名稱 + if (item.getRecFileName() != null && item.getRecFileName().length() > 0) { + recFileName = item.getRecFileName(); + } + // 產生檔案名稱 + else { + recFileName = FileUtil.getUniqueFileName(); + } + + return new File(FileUtil.getExternalStorageDir(FileUtil.APP_DIR), + prefix + recFileName + extension); + } + +} diff --git a/examples/0402/MyAndroidTutorial/app/src/main/java/net/macdidi/myandroidtutorial/ItemAdapter.java b/examples/0402/MyAndroidTutorial/app/src/main/java/net/macdidi/myandroidtutorial/ItemAdapter.java new file mode 100644 index 0000000..85b40ba --- /dev/null +++ b/examples/0402/MyAndroidTutorial/app/src/main/java/net/macdidi/myandroidtutorial/ItemAdapter.java @@ -0,0 +1,80 @@ +package net.macdidi.myandroidtutorial; + +import android.content.Context; +import android.graphics.drawable.GradientDrawable; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ArrayAdapter; +import android.widget.ImageView; +import android.widget.LinearLayout; +import android.widget.RelativeLayout; +import android.widget.TextView; + +import java.util.List; + +public class ItemAdapter extends ArrayAdapter { + + // 畫面資源編號 + private int resource; + // 包裝的記事資料 + private List items; + + public ItemAdapter(Context context, int resource, List items) { + super(context, resource, items); + this.resource = resource; + this.items = items; + } + + @Override + public View getView(int position, View convertView, ViewGroup parent) { + LinearLayout itemView; + // 讀取目前位置的記事物件 + final Item item = getItem(position); + + if (convertView == null) { + // 建立項目畫面元件 + itemView = new LinearLayout(getContext()); + String inflater = Context.LAYOUT_INFLATER_SERVICE; + LayoutInflater li = (LayoutInflater) + getContext().getSystemService(inflater); + li.inflate(resource, itemView, true); + } + else { + itemView = (LinearLayout) convertView; + } + + // 讀取記事顏色、已選擇、標題與日期時間元件 + RelativeLayout typeColor = (RelativeLayout) itemView.findViewById(R.id.type_color); + ImageView selectedItem = (ImageView) itemView.findViewById(R.id.selected_item); + TextView titleView = (TextView) itemView.findViewById(R.id.title_text); + TextView dateView = (TextView) itemView.findViewById(R.id.date_text); + + // 設定記事顏色 + GradientDrawable background = (GradientDrawable)typeColor.getBackground(); + background.setColor(item.getColor().parseColor()); + + // 設定標題與日期時間 + titleView.setText(item.getTitle()); + dateView.setText(item.getLocaleDatetime()); + + // 設定是否已選擇 + selectedItem.setVisibility(item.isSelected() ? View.VISIBLE : View.INVISIBLE); + + return itemView; + } + + // 設定指定編號的記事資料 + public void set(int index, Item item) { + if (index >= 0 && index < items.size()) { + items.set(index, item); + notifyDataSetChanged(); + } + } + + // 讀取指定編號的記事資料 + public Item get(int index) { + return items.get(index); + } + +} \ No newline at end of file diff --git a/examples/0402/MyAndroidTutorial/app/src/main/java/net/macdidi/myandroidtutorial/ItemDAO.java b/examples/0402/MyAndroidTutorial/app/src/main/java/net/macdidi/myandroidtutorial/ItemDAO.java new file mode 100644 index 0000000..ac5a57a --- /dev/null +++ b/examples/0402/MyAndroidTutorial/app/src/main/java/net/macdidi/myandroidtutorial/ItemDAO.java @@ -0,0 +1,201 @@ +package net.macdidi.myandroidtutorial; + +import android.content.ContentValues; +import android.content.Context; +import android.database.Cursor; +import android.database.sqlite.SQLiteDatabase; + +import java.util.ArrayList; +import java.util.Date; +import java.util.List; + +// 資料功能類別 +public class ItemDAO { + // 表格名稱 + public static final String TABLE_NAME = "item"; + + // 編號表格欄位名稱,固定不變 + public static final String KEY_ID = "_id"; + + // 其它表格欄位名稱 + public static final String DATETIME_COLUMN = "datetime"; + public static final String COLOR_COLUMN = "color"; + public static final String TITLE_COLUMN = "title"; + public static final String CONTENT_COLUMN = "content"; + public static final String FILENAME_COLUMN = "filename"; + public static final String RECFILENAME_COLUMN = "recfilename"; + public static final String LATITUDE_COLUMN = "latitude"; + public static final String LONGITUDE_COLUMN = "longitude"; + public static final String LASTMODIFY_COLUMN = "lastmodify"; + + // 使用上面宣告的變數建立表格的SQL指令 + public static final String CREATE_TABLE = + "CREATE TABLE " + TABLE_NAME + " (" + + KEY_ID + " INTEGER PRIMARY KEY AUTOINCREMENT, " + + DATETIME_COLUMN + " INTEGER NOT NULL, " + + COLOR_COLUMN + " INTEGER NOT NULL, " + + TITLE_COLUMN + " TEXT NOT NULL, " + + CONTENT_COLUMN + " TEXT NOT NULL, " + + FILENAME_COLUMN + " TEXT, " + + RECFILENAME_COLUMN + " TEXT, " + + LATITUDE_COLUMN + " REAL, " + + LONGITUDE_COLUMN + " REAL, " + + LASTMODIFY_COLUMN + " INTEGER)"; + + // 資料庫物件 + private SQLiteDatabase db; + + // 建構子,一般的應用都不需要修改 + public ItemDAO(Context context) { + db = MyDBHelper.getDatabase(context); + } + + // 關閉資料庫,一般的應用都不需要修改 + public void close() { + db.close(); + } + + // 新增參數指定的物件 + public Item insert(Item item) { + // 建立準備新增資料的ContentValues物件 + ContentValues cv = new ContentValues(); + + // 加入ContentValues物件包裝的新增資料 + // 第一個參數是欄位名稱, 第二個參數是欄位的資料 + cv.put(DATETIME_COLUMN, item.getDatetime()); + cv.put(COLOR_COLUMN, item.getColor().parseColor()); + cv.put(TITLE_COLUMN, item.getTitle()); + cv.put(CONTENT_COLUMN, item.getContent()); + cv.put(FILENAME_COLUMN, item.getFileName()); + cv.put(RECFILENAME_COLUMN, item.getRecFileName()); + cv.put(LATITUDE_COLUMN, item.getLatitude()); + cv.put(LONGITUDE_COLUMN, item.getLongitude()); + cv.put(LASTMODIFY_COLUMN, item.getLastModify()); + + // 新增一筆資料並取得編號 + // 第一個參數是表格名稱 + // 第二個參數是沒有指定欄位值的預設值 + // 第三個參數是包裝新增資料的ContentValues物件 + long id = db.insert(TABLE_NAME, null, cv); + + // 設定編號 + item.setId(id); + // 回傳結果 + return item; + } + + // 修改參數指定的物件 + public boolean update(Item item) { + // 建立準備修改資料的ContentValues物件 + ContentValues cv = new ContentValues(); + + // 加入ContentValues物件包裝的修改資料 + // 第一個參數是欄位名稱, 第二個參數是欄位的資料 + cv.put(DATETIME_COLUMN, item.getDatetime()); + cv.put(COLOR_COLUMN, item.getColor().parseColor()); + cv.put(TITLE_COLUMN, item.getTitle()); + cv.put(CONTENT_COLUMN, item.getContent()); + cv.put(FILENAME_COLUMN, item.getFileName()); + cv.put(RECFILENAME_COLUMN, item.getRecFileName()); + cv.put(LATITUDE_COLUMN, item.getLatitude()); + cv.put(LONGITUDE_COLUMN, item.getLongitude()); + cv.put(LASTMODIFY_COLUMN, item.getLastModify()); + + // 設定修改資料的條件為編號 + // 格式為「欄位名稱=資料」 + String where = KEY_ID + "=" + item.getId(); + + // 執行修改資料並回傳修改的資料數量是否成功 + return db.update(TABLE_NAME, cv, where, null) > 0; + } + + // 刪除參數指定編號的資料 + public boolean delete(long id){ + // 設定條件為編號,格式為「欄位名稱=資料」 + String where = KEY_ID + "=" + id; + // 刪除指定編號資料並回傳刪除是否成功 + return db.delete(TABLE_NAME, where , null) > 0; + } + + // 讀取所有記事資料 + public List getAll() { + List result = new ArrayList<>(); + Cursor cursor = db.query( + TABLE_NAME, null, null, null, null, null, null, null); + + while (cursor.moveToNext()) { + result.add(getRecord(cursor)); + } + + cursor.close(); + return result; + } + + // 取得指定編號的資料物件 + public Item get(long id) { + // 準備回傳結果用的物件 + Item item = null; + // 使用編號為查詢條件 + String where = KEY_ID + "=" + id; + // 執行查詢 + Cursor result = db.query( + TABLE_NAME, null, where, null, null, null, null, null); + + // 如果有查詢結果 + if (result.moveToFirst()) { + // 讀取包裝一筆資料的物件 + item = getRecord(result); + } + + // 關閉Cursor物件 + result.close(); + // 回傳結果 + return item; + } + + // 把Cursor目前的資料包裝為物件 + public Item getRecord(Cursor cursor) { + // 準備回傳結果用的物件 + Item result = new Item(); + + result.setId(cursor.getLong(0)); + result.setDatetime(cursor.getLong(1)); + result.setColor(ItemActivity.getColors(cursor.getInt(2))); + result.setTitle(cursor.getString(3)); + result.setContent(cursor.getString(4)); + result.setFileName(cursor.getString(5)); + result.setRecFileName(cursor.getString(6)); + result.setLatitude(cursor.getDouble(7)); + result.setLongitude(cursor.getDouble(8)); + result.setLastModify(cursor.getLong(9)); + + // 回傳結果 + return result; + } + + // 取得資料數量 + public int getCount() { + int result = 0; + Cursor cursor = db.rawQuery("SELECT COUNT(*) FROM " + TABLE_NAME, null); + + if (cursor.moveToNext()) { + result = cursor.getInt(0); + } + + return result; + } + + // 建立範例資料 + public void sample() { + Item item = new Item(0, new Date().getTime(), Colors.RED, "關於Android Tutorial的事情.", "Hello content", "", "", 0, 0, 0); + Item item2 = new Item(0, new Date().getTime(), Colors.BLUE, "一隻非常可愛的小狗狗!", "她的名字叫「大熱狗」,又叫\n作「奶嘴」,是一隻非常可愛\n的小狗。", "", "", 25.04719, 121.516981, 0); + Item item3 = new Item(0, new Date().getTime(), Colors.GREEN, "一首非常好聽的音樂!", "Hello content", "", "", 0, 0, 0); + Item item4 = new Item(0, new Date().getTime(), Colors.ORANGE, "儲存在資料庫的資料", "Hello content", "", "", 0, 0, 0); + + insert(item); + insert(item2); + insert(item3); + insert(item4); + } + +} \ No newline at end of file diff --git a/examples/0402/MyAndroidTutorial/app/src/main/java/net/macdidi/myandroidtutorial/MainActivity.java b/examples/0402/MyAndroidTutorial/app/src/main/java/net/macdidi/myandroidtutorial/MainActivity.java new file mode 100644 index 0000000..2a30f6a --- /dev/null +++ b/examples/0402/MyAndroidTutorial/app/src/main/java/net/macdidi/myandroidtutorial/MainActivity.java @@ -0,0 +1,302 @@ +package net.macdidi.myandroidtutorial; + +import android.app.Activity; +import android.app.AlertDialog; +import android.content.DialogInterface; +import android.content.Intent; +import android.os.Bundle; +import android.support.v7.app.ActionBarActivity; +import android.view.Menu; +import android.view.MenuInflater; +import android.view.MenuItem; +import android.view.View; +import android.widget.AdapterView; +import android.widget.ListView; +import android.widget.TextView; + +import java.util.List; + +public class MainActivity extends ActionBarActivity { + private ListView item_list; + private TextView show_app_name; + + // ListView使用的自定Adapter物件 + private ItemAdapter itemAdapter; + // 儲存所有記事本的List物件 + private List items; + + // 選單項目物件 + private MenuItem add_item, search_item, revert_item, share_item, delete_item; + + // 已選擇項目數量 + private int selectedCount = 0; + + // 宣告資料庫功能類別欄位變數 + private ItemDAO itemDAO; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_main); + + processViews(); + processControllers(); + + // 建立資料庫物件 + itemDAO = new ItemDAO(getApplicationContext()); + + // 如果資料庫是空的,就建立一些範例資料 + // 這是為了方便測試用的,完成應用程式以後可以拿掉 + if (itemDAO.getCount() == 0) { + itemDAO.sample(); + } + + // 取得所有記事資料 + items = itemDAO.getAll(); + + itemAdapter = new ItemAdapter(this, R.layout.single_item, items); + item_list.setAdapter(itemAdapter); + } + + @Override + protected void onActivityResult(int requestCode, int resultCode, Intent data) { + if (resultCode == Activity.RESULT_OK) { + Item item = (Item) data.getExtras().getSerializable( + "net.macdidi.myandroidtutorial.Item"); + + if (requestCode == 0) { + // 新增記事資料到資料庫 + item = itemDAO.insert(item); + + items.add(item); + itemAdapter.notifyDataSetChanged(); + } + else if (requestCode == 1) { + int position = data.getIntExtra("position", -1); + + if (position != -1) { + // 修改資料庫中的記事資料 + itemDAO.update(item); + + items.set(position, item); + itemAdapter.notifyDataSetChanged(); + } + } + } + } + + private void processViews() { + item_list = (ListView)findViewById(R.id.item_list); + show_app_name = (TextView) findViewById(R.id.show_app_name); + } + + private void processControllers() { + + // 建立選單項目點擊監聽物件 + AdapterView.OnItemClickListener itemListener = new AdapterView.OnItemClickListener() { + @Override + public void onItemClick(AdapterView parent, View view, + int position, long id) { + // 讀取選擇的記事物件 + Item item = itemAdapter.getItem(position); + + // 如果已經有勾選的項目 + if (selectedCount > 0) { + // 處理是否顯示已選擇項目 + processMenu(item); + // 重新設定記事項目 + itemAdapter.set(position, item); + } + else { + Intent intent = new Intent( + "net.macdidi.myandroidtutorial.EDIT_ITEM"); + + // 設定記事編號與記事物件 + intent.putExtra("position", position); + intent.putExtra("net.macdidi.myandroidtutorial.Item", item); + + startActivityForResult(intent, 1); + } + } + }; + + // 註冊選單項目點擊監聽物件 + item_list.setOnItemClickListener(itemListener); + + // 建立記事項目長按監聽物件 + AdapterView.OnItemLongClickListener itemLongListener = new AdapterView.OnItemLongClickListener() { + @Override + public boolean onItemLongClick(AdapterView parent, View view, + int position, long id) { + // 讀取選擇的記事物件 + Item item = itemAdapter.getItem(position); + // 處理是否顯示已選擇項目 + processMenu(item); + // 重新設定記事項目 + itemAdapter.set(position, item); + return true; + } + }; + + // 註冊記事項目長按監聽物件 + item_list.setOnItemLongClickListener(itemLongListener); + + // 建立長按監聽物件 + View.OnLongClickListener listener = new View.OnLongClickListener() { + + @Override + public boolean onLongClick(View view) { + AlertDialog.Builder dialog = + new AlertDialog.Builder(MainActivity.this); + dialog.setTitle(R.string.app_name) + .setMessage(R.string.about) + .show(); + return false; + } + + }; + + // 註冊長按監聽物件 + show_app_name.setOnLongClickListener(listener); + } + + // 處理是否顯示已選擇項目 + private void processMenu(Item item) { + // 如果需要設定記事項目 + if (item != null) { + // 設定已勾選的狀態 + item.setSelected(!item.isSelected()); + + // 計算已勾選數量 + if (item.isSelected()) { + selectedCount++; + } + else { + selectedCount--; + } + } + + // 根據選擇的狀況,設定是否顯示選單項目 + add_item.setVisible(selectedCount == 0); + search_item.setVisible(selectedCount == 0); + revert_item.setVisible(selectedCount > 0); + share_item.setVisible(selectedCount > 0); + delete_item.setVisible(selectedCount > 0); + } + + // 載入選單資源 + @Override + public boolean onCreateOptionsMenu(Menu menu) { + MenuInflater menuInflater = getMenuInflater(); + menuInflater.inflate(R.menu.menu_main, menu); + + // 取得選單項目物件 + add_item = menu.findItem(R.id.add_item); + search_item = menu.findItem(R.id.search_item); + revert_item = menu.findItem(R.id.revert_item); + share_item = menu.findItem(R.id.share_item); + delete_item = menu.findItem(R.id.delete_item); + + // 設定選單項目 + processMenu(null); + + return true; + } + + // 使用者選擇所有的選單項目都會呼叫這個方法 + public void clickMenuItem(MenuItem item) { + // 使用參數取得使用者選擇的選單項目元件編號 + int itemId = item.getItemId(); + + // 判斷該執行什麼工作,目前還沒有加入需要執行的工作 + switch (itemId) { + case R.id.search_item: + break; + // 使用者選擇新增選單項目 + case R.id.add_item: + // 使用Action名稱建立啟動另一個Activity元件需要的Intent物件 + Intent intent = new Intent("net.macdidi.myandroidtutorial.ADD_ITEM"); + // 呼叫「startActivityForResult」,,第二個參數「0」表示執行新增 + startActivityForResult(intent, 0); + break; + // 取消所有已勾選的項目 + case R.id.revert_item: + for (int i = 0; i < itemAdapter.getCount(); i++) { + Item ri = itemAdapter.getItem(i); + + if (ri.isSelected()) { + ri.setSelected(false); + itemAdapter.set(i, ri); + } + } + + selectedCount = 0; + processMenu(null); + + break; + // 刪除 + case R.id.delete_item: + if (selectedCount == 0) { + break; + } + + AlertDialog.Builder d = new AlertDialog.Builder(this); + String message = getString(R.string.delete_item); + d.setTitle(R.string.delete) + .setMessage(String.format(message, selectedCount)); + d.setPositiveButton(android.R.string.yes, + new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + // 取得最後一個元素的編號 + int index = itemAdapter.getCount() - 1; + + while (index > -1) { + Item item = itemAdapter.get(index); + + if (item.isSelected()) { + itemAdapter.remove(item); + // 刪除資料庫中的記事資料 + itemDAO.delete(item.getId()); + } + + index--; + } + + itemAdapter.notifyDataSetChanged(); + selectedCount = 0; + processMenu(null); + } + }); + d.setNegativeButton(android.R.string.no, null); + d.show(); + + break; + case R.id.googleplus_item: + break; + case R.id.facebook_item: + break; + } + + } + + // 方法名稱與onClick的設定一樣,參數的型態是android.view.View + public void aboutApp(View view) { + // 建立啟動另一個Activity元件需要的Intent物件 + // 建構式的第一個參數:「this」 + // 建構式的第二個參數:「Activity元件類別名稱.class」 + Intent intent = new Intent(this, AboutActivity.class); + // 呼叫「startActivity」,參數為一個建立好的Intent物件 + // 這行敘述執行以後,如果沒有任何錯誤,就會啟動指定的元件 + startActivity(intent); + } + + // 設定 + public void clickPreferences(MenuItem item) { + // 啟動設定元件 + startActivity(new Intent(this, PrefActivity.class)); + } + +} + + diff --git a/examples/0402/MyAndroidTutorial/app/src/main/java/net/macdidi/myandroidtutorial/MapsActivity.java b/examples/0402/MyAndroidTutorial/app/src/main/java/net/macdidi/myandroidtutorial/MapsActivity.java new file mode 100644 index 0000000..775305a --- /dev/null +++ b/examples/0402/MyAndroidTutorial/app/src/main/java/net/macdidi/myandroidtutorial/MapsActivity.java @@ -0,0 +1,83 @@ +package net.macdidi.myandroidtutorial; + +import android.os.Bundle; +import android.support.v4.app.FragmentActivity; + +import com.google.android.gms.maps.CameraUpdateFactory; +import com.google.android.gms.maps.GoogleMap; +import com.google.android.gms.maps.SupportMapFragment; +import com.google.android.gms.maps.model.BitmapDescriptor; +import com.google.android.gms.maps.model.BitmapDescriptorFactory; +import com.google.android.gms.maps.model.CameraPosition; +import com.google.android.gms.maps.model.LatLng; +import com.google.android.gms.maps.model.MarkerOptions; + +public class MapsActivity extends FragmentActivity { + + private GoogleMap mMap; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_maps); + setUpMapIfNeeded(); + } + + @Override + protected void onResume() { + super.onResume(); + setUpMapIfNeeded(); + } + + private void setUpMapIfNeeded() { + if (mMap == null) { + mMap = ((SupportMapFragment) getSupportFragmentManager(). + findFragmentById(R.id.map)).getMap(); + + if (mMap != null) { + setUpMap(); + } + } + } + + private void setUpMap() { + // 刪除原來預設的內容 + //mMap.addMarker(new MarkerOptions().position(new LatLng(0, 0)).title("Marker")); + + // 建立位置的座標物件 + LatLng place = new LatLng(25.033408, 121.564099); + // 移動地圖 + moveMap(place); + + // 加入地圖標記 + addMarker(place, "Hello!", " Google Maps v2!"); + } + + // 移動地圖到參數指定的位置 + private void moveMap(LatLng place) { + // 建立地圖攝影機的位置物件 + CameraPosition cameraPosition = + new CameraPosition.Builder() + .target(place) + .zoom(17) + .build(); + + // 使用動畫的效果移動地圖 + mMap.animateCamera(CameraUpdateFactory.newCameraPosition(cameraPosition)); + } + + // 在地圖加入指定位置與標題的標記 + private void addMarker(LatLng place, String title, String snippet) { + BitmapDescriptor icon = + BitmapDescriptorFactory.fromResource(R.drawable.ic_launcher); + + MarkerOptions markerOptions = new MarkerOptions(); + markerOptions.position(place) + .title(title) + .snippet(snippet) + .icon(icon); + + mMap.addMarker(markerOptions); + } + +} diff --git a/examples/0402/MyAndroidTutorial/app/src/main/java/net/macdidi/myandroidtutorial/MyDBHelper.java b/examples/0402/MyAndroidTutorial/app/src/main/java/net/macdidi/myandroidtutorial/MyDBHelper.java new file mode 100644 index 0000000..b8b77d6 --- /dev/null +++ b/examples/0402/MyAndroidTutorial/app/src/main/java/net/macdidi/myandroidtutorial/MyDBHelper.java @@ -0,0 +1,47 @@ +package net.macdidi.myandroidtutorial; + +import android.content.Context; +import android.database.sqlite.SQLiteDatabase; +import android.database.sqlite.SQLiteDatabase.CursorFactory; +import android.database.sqlite.SQLiteOpenHelper; + +public class MyDBHelper extends SQLiteOpenHelper { + + // 資料庫名稱 + public static final String DATABASE_NAME = "mydata.db"; + // 資料庫版本,資料結構改變的時候要更改這個數字,通常是加一 + public static final int VERSION = 2; + // 資料庫物件,固定的欄位變數 + private static SQLiteDatabase database; + + // 建構子,在一般的應用都不需要修改 + public MyDBHelper(Context context, String name, CursorFactory factory, + int version) { + super(context, name, factory, version); + } + + // 需要資料庫的元件呼叫這個方法,這個方法在一般的應用都不需要修改 + public static SQLiteDatabase getDatabase(Context context) { + if (database == null || !database.isOpen()) { + database = new MyDBHelper(context, DATABASE_NAME, + null, VERSION).getWritableDatabase(); + } + + return database; + } + + @Override + public void onCreate(SQLiteDatabase db) { + // 建立應用程式需要的表格 + db.execSQL(ItemDAO.CREATE_TABLE); + } + + @Override + public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { + // 刪除原有的表格 + db.execSQL("DROP TABLE IF EXISTS " + ItemDAO.TABLE_NAME); + // 呼叫onCreate建立新版的表格 + onCreate(db); + } + +} \ No newline at end of file diff --git a/examples/0402/MyAndroidTutorial/app/src/main/java/net/macdidi/myandroidtutorial/PlayActivity.java b/examples/0402/MyAndroidTutorial/app/src/main/java/net/macdidi/myandroidtutorial/PlayActivity.java new file mode 100644 index 0000000..5cfb523 --- /dev/null +++ b/examples/0402/MyAndroidTutorial/app/src/main/java/net/macdidi/myandroidtutorial/PlayActivity.java @@ -0,0 +1,64 @@ +package net.macdidi.myandroidtutorial; + +import android.app.Activity; +import android.content.Intent; +import android.media.MediaPlayer; +import android.net.Uri; +import android.os.Bundle; +import android.view.View; + +public class PlayActivity extends Activity { + + private MediaPlayer mediaPlayer; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_play); + + Intent intent = getIntent(); + String fileName = intent.getStringExtra("fileName"); + + // 建立指定資源的MediaPlayer物件 + Uri uri = Uri.parse(fileName); + mediaPlayer = MediaPlayer.create(this, uri); + } + + @Override + protected void onStop() { + if (mediaPlayer.isPlaying()) { + // 停止播放 + mediaPlayer.stop(); + } + + // 清除MediaPlayer物件 + mediaPlayer.release(); + super.onStop(); + } + + public void onSubmit(View view) { + // 結束Activity元件 + finish(); + } + + public void clickPlay(View view) { + // 開始播放 + mediaPlayer.start(); + } + + public void clickPause(View view) { + // 暫停播放 + mediaPlayer.pause(); + } + + public void clickStop(View view) { + // 停止播放 + if (mediaPlayer.isPlaying()) { + mediaPlayer.stop(); + } + + // 回到開始的位置 + mediaPlayer.seekTo(0); + } + +} \ No newline at end of file diff --git a/examples/0402/MyAndroidTutorial/app/src/main/java/net/macdidi/myandroidtutorial/PrefActivity.java b/examples/0402/MyAndroidTutorial/app/src/main/java/net/macdidi/myandroidtutorial/PrefActivity.java new file mode 100644 index 0000000..b2dcc4a --- /dev/null +++ b/examples/0402/MyAndroidTutorial/app/src/main/java/net/macdidi/myandroidtutorial/PrefActivity.java @@ -0,0 +1,38 @@ +package net.macdidi.myandroidtutorial; + +import android.content.SharedPreferences; +import android.os.Bundle; +import android.preference.Preference; +import android.preference.PreferenceActivity; +import android.preference.PreferenceManager; + +public class PrefActivity extends PreferenceActivity { + + private SharedPreferences sharedPreferences; + private Preference defaultColor; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + // 指定使用的設定畫面配置資源 + addPreferencesFromResource(R.xml.mypreference); + defaultColor = (Preference)findPreference("DEFAULT_COLOR"); + // 建立SharedPreferences物件 + sharedPreferences = + PreferenceManager.getDefaultSharedPreferences(this); + } + + @Override + protected void onResume() { + super.onResume(); + // 讀取設定的預設顏色 + int color = sharedPreferences.getInt("DEFAULT_COLOR", -1); + + if (color != -1) { + // 設定顏色說明 + defaultColor.setSummary(getString(R.string.default_color_summary) + + ": " + ItemActivity.getColors(color)); + } + } + +} \ No newline at end of file diff --git a/examples/0402/MyAndroidTutorial/app/src/main/java/net/macdidi/myandroidtutorial/RecordActivity.java b/examples/0402/MyAndroidTutorial/app/src/main/java/net/macdidi/myandroidtutorial/RecordActivity.java new file mode 100644 index 0000000..62b5e93 --- /dev/null +++ b/examples/0402/MyAndroidTutorial/app/src/main/java/net/macdidi/myandroidtutorial/RecordActivity.java @@ -0,0 +1,180 @@ +package net.macdidi.myandroidtutorial; + +import android.app.Activity; +import android.content.Intent; +import android.media.MediaRecorder; +import android.os.AsyncTask; +import android.os.Bundle; +import android.util.Log; +import android.view.View; +import android.widget.ImageButton; +import android.widget.ProgressBar; + +import java.io.IOException; + +public class RecordActivity extends Activity { + + private ImageButton record_button; + private boolean isRecording = false; + private ProgressBar record_volumn; + + private MyRecoder myRecoder; + + private String fileName; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_record); + + processViews(); + + // 讀取檔案名稱 + Intent intent = getIntent(); + fileName = intent.getStringExtra("fileName"); + } + + public void onSubmit(View view) { + if (isRecording) { + // 停止錄音 + myRecoder.stop(); + } + + // 確定 + if (view.getId() == R.id.record_ok) { + Intent result = getIntent(); + setResult(Activity.RESULT_OK, result); + } + + finish(); + } + + private void processViews() { + record_button = (ImageButton) findViewById(R.id.record_button); + record_volumn = (ProgressBar) findViewById(R.id.record_volumn); + // 隱藏狀態列ProgressBar + setProgressBarIndeterminateVisibility(false); + } + + public void clickRecord(View view) { + // 切換 + isRecording = !isRecording; + + // 開始錄音 + if (isRecording) { + // 設定按鈕圖示為錄音中 + record_button.setImageResource(R.drawable.record_red_icon); + // 建立錄音物件 + myRecoder = new MyRecoder(fileName); + // 開始錄音 + myRecoder.start(); + // 建立並執行顯示麥克風音量的AsyncTask物件 + new MicLevelTask().execute(); + } + // 停止錄音 + else { + // 設定按鈕圖示為停止錄音 + record_button.setImageResource(R.drawable.record_dark_icon); + // 麥克風音量歸零 + record_volumn.setProgress(0); + // 停止錄音 + myRecoder.stop(); + } + } + + // 在錄音過程中顯示麥克風音量 + private class MicLevelTask extends AsyncTask { + @Override + protected Void doInBackground(Void... args) { + while (isRecording) { + publishProgress(); + + try { + Thread.sleep(200); + } + catch (InterruptedException e) { + Log.d("RecordActivity", e.toString()); + } + } + + return null; + } + + @Override + protected void onProgressUpdate(Void... values) { + record_volumn.setProgress((int) myRecoder.getAmplitudeEMA()); + } + + } + + // 執行錄音並且可以取得麥克風音量的錄音物件 + private class MyRecoder { + + private static final double EMA_FILTER = 0.6; + private MediaRecorder recorder = null; + private double mEMA = 0.0; + private String output; + + // 建立錄音物件,參數為錄音儲存的位置與檔名 + MyRecoder(String output) { + this.output = output; + } + + // 開始錄音 + public void start() { + if (recorder == null) { + // 建立錄音用的MediaRecorder物件 + recorder = new MediaRecorder(); + // 設定錄音來源為麥克風,必須在setOutputFormat方法之前呼叫 + recorder.setAudioSource(MediaRecorder.AudioSource.MIC); + // 設定輸出格式為3GP壓縮格式,必須在setAudioSource方法之後, + // 在prepare方法之前呼叫 + recorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP); + // 設定錄音的編碼方式,必須在setOutputFormat方法之後, + // 在prepare方法之前呼叫 + recorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB); + // 設定輸出的檔案名稱,必須在setOutputFormat方法之後, + // 在prepare方法之前呼叫 + recorder.setOutputFile(output); + + try { + // 準備執行錄音工作,必須在所有設定之後呼叫 + recorder.prepare(); + } + catch (IOException e) { + Log.d("RecordActivity", e.toString()); + } + + // 開始錄音 + recorder.start(); + mEMA = 0.0; + } + } + + // 停止錄音 + public void stop() { + if (recorder != null) { + // 停止錄音 + recorder.stop(); + // 清除錄音資源 + recorder.release(); + recorder = null; + } + } + + public double getAmplitude() { + if (recorder != null) + return (recorder.getMaxAmplitude() / 2700.0); + else + return 0; + } + + // 取得麥克風音量 + public double getAmplitudeEMA() { + double amp = getAmplitude(); + mEMA = EMA_FILTER * amp + (1.0 - EMA_FILTER) * mEMA; + return mEMA; + } + } + +} \ No newline at end of file diff --git a/examples/0402/MyAndroidTutorial/app/src/main/res/drawable-hdpi/ic_launcher.png b/examples/0402/MyAndroidTutorial/app/src/main/res/drawable-hdpi/ic_launcher.png new file mode 100644 index 0000000..96a442e Binary files /dev/null and b/examples/0402/MyAndroidTutorial/app/src/main/res/drawable-hdpi/ic_launcher.png differ diff --git a/examples/0402/MyAndroidTutorial/app/src/main/res/drawable-mdpi/ic_launcher.png b/examples/0402/MyAndroidTutorial/app/src/main/res/drawable-mdpi/ic_launcher.png new file mode 100644 index 0000000..359047d Binary files /dev/null and b/examples/0402/MyAndroidTutorial/app/src/main/res/drawable-mdpi/ic_launcher.png differ diff --git a/examples/0402/MyAndroidTutorial/app/src/main/res/drawable-xhdpi/ic_launcher.png b/examples/0402/MyAndroidTutorial/app/src/main/res/drawable-xhdpi/ic_launcher.png new file mode 100644 index 0000000..71c6d76 Binary files /dev/null and b/examples/0402/MyAndroidTutorial/app/src/main/res/drawable-xhdpi/ic_launcher.png differ diff --git a/examples/0402/MyAndroidTutorial/app/src/main/res/drawable-xxhdpi/ic_launcher.png b/examples/0402/MyAndroidTutorial/app/src/main/res/drawable-xxhdpi/ic_launcher.png new file mode 100644 index 0000000..4df1894 Binary files /dev/null and b/examples/0402/MyAndroidTutorial/app/src/main/res/drawable-xxhdpi/ic_launcher.png differ diff --git a/examples/0402/MyAndroidTutorial/app/src/main/res/drawable/alarm_icon.png b/examples/0402/MyAndroidTutorial/app/src/main/res/drawable/alarm_icon.png new file mode 100644 index 0000000..c6cac88 Binary files /dev/null and b/examples/0402/MyAndroidTutorial/app/src/main/res/drawable/alarm_icon.png differ diff --git a/examples/0402/MyAndroidTutorial/app/src/main/res/drawable/item_drawable.xml b/examples/0402/MyAndroidTutorial/app/src/main/res/drawable/item_drawable.xml new file mode 100644 index 0000000..37607e2 --- /dev/null +++ b/examples/0402/MyAndroidTutorial/app/src/main/res/drawable/item_drawable.xml @@ -0,0 +1,13 @@ + + + + + + + + \ No newline at end of file diff --git a/examples/0402/MyAndroidTutorial/app/src/main/res/drawable/location_icon.png b/examples/0402/MyAndroidTutorial/app/src/main/res/drawable/location_icon.png new file mode 100644 index 0000000..4c3c514 Binary files /dev/null and b/examples/0402/MyAndroidTutorial/app/src/main/res/drawable/location_icon.png differ diff --git a/examples/0402/MyAndroidTutorial/app/src/main/res/drawable/pause_icon.png b/examples/0402/MyAndroidTutorial/app/src/main/res/drawable/pause_icon.png new file mode 100755 index 0000000..a5aee6f Binary files /dev/null and b/examples/0402/MyAndroidTutorial/app/src/main/res/drawable/pause_icon.png differ diff --git a/examples/0402/MyAndroidTutorial/app/src/main/res/drawable/play_icon.png b/examples/0402/MyAndroidTutorial/app/src/main/res/drawable/play_icon.png new file mode 100755 index 0000000..6a40cd5 Binary files /dev/null and b/examples/0402/MyAndroidTutorial/app/src/main/res/drawable/play_icon.png differ diff --git a/examples/0402/MyAndroidTutorial/app/src/main/res/drawable/record_dark_icon.png b/examples/0402/MyAndroidTutorial/app/src/main/res/drawable/record_dark_icon.png new file mode 100755 index 0000000..bcf83ca Binary files /dev/null and b/examples/0402/MyAndroidTutorial/app/src/main/res/drawable/record_dark_icon.png differ diff --git a/examples/0402/MyAndroidTutorial/app/src/main/res/drawable/record_red_icon.png b/examples/0402/MyAndroidTutorial/app/src/main/res/drawable/record_red_icon.png new file mode 100755 index 0000000..2b44af0 Binary files /dev/null and b/examples/0402/MyAndroidTutorial/app/src/main/res/drawable/record_red_icon.png differ diff --git a/examples/0402/MyAndroidTutorial/app/src/main/res/drawable/record_sound_icon.png b/examples/0402/MyAndroidTutorial/app/src/main/res/drawable/record_sound_icon.png new file mode 100755 index 0000000..a1382ac Binary files /dev/null and b/examples/0402/MyAndroidTutorial/app/src/main/res/drawable/record_sound_icon.png differ diff --git a/examples/0402/MyAndroidTutorial/app/src/main/res/drawable/retangle_drawable.xml b/examples/0402/MyAndroidTutorial/app/src/main/res/drawable/retangle_drawable.xml new file mode 100644 index 0000000..51d1e84 --- /dev/null +++ b/examples/0402/MyAndroidTutorial/app/src/main/res/drawable/retangle_drawable.xml @@ -0,0 +1,13 @@ + + + + + + + + \ No newline at end of file diff --git a/examples/0402/MyAndroidTutorial/app/src/main/res/drawable/select_color_icon.png b/examples/0402/MyAndroidTutorial/app/src/main/res/drawable/select_color_icon.png new file mode 100644 index 0000000..8567d5e Binary files /dev/null and b/examples/0402/MyAndroidTutorial/app/src/main/res/drawable/select_color_icon.png differ diff --git a/examples/0402/MyAndroidTutorial/app/src/main/res/drawable/selected_icon.png b/examples/0402/MyAndroidTutorial/app/src/main/res/drawable/selected_icon.png new file mode 100755 index 0000000..b891571 Binary files /dev/null and b/examples/0402/MyAndroidTutorial/app/src/main/res/drawable/selected_icon.png differ diff --git a/examples/0402/MyAndroidTutorial/app/src/main/res/drawable/stop_icon.png b/examples/0402/MyAndroidTutorial/app/src/main/res/drawable/stop_icon.png new file mode 100755 index 0000000..20df415 Binary files /dev/null and b/examples/0402/MyAndroidTutorial/app/src/main/res/drawable/stop_icon.png differ diff --git a/examples/0402/MyAndroidTutorial/app/src/main/res/drawable/take_picture_icon.png b/examples/0402/MyAndroidTutorial/app/src/main/res/drawable/take_picture_icon.png new file mode 100644 index 0000000..08fb514 Binary files /dev/null and b/examples/0402/MyAndroidTutorial/app/src/main/res/drawable/take_picture_icon.png differ diff --git a/examples/0402/MyAndroidTutorial/app/src/main/res/layout/activity_about.xml b/examples/0402/MyAndroidTutorial/app/src/main/res/layout/activity_about.xml new file mode 100644 index 0000000..8e78611 --- /dev/null +++ b/examples/0402/MyAndroidTutorial/app/src/main/res/layout/activity_about.xml @@ -0,0 +1,35 @@ + + + + + + +