diff --git a/README.md b/README.md
index 97af9eb..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文件的說明
@@ -27,6 +29,14 @@
* [(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/0601/MyAndroidTutorial/.gitignore b/examples/0601/MyAndroidTutorial/.gitignore
new file mode 100644
index 0000000..afbdab3
--- /dev/null
+++ b/examples/0601/MyAndroidTutorial/.gitignore
@@ -0,0 +1,6 @@
+.gradle
+/local.properties
+/.idea/workspace.xml
+/.idea/libraries
+.DS_Store
+/build
diff --git a/examples/0601/MyAndroidTutorial/.idea/.name b/examples/0601/MyAndroidTutorial/.idea/.name
new file mode 100644
index 0000000..5bb7a85
--- /dev/null
+++ b/examples/0601/MyAndroidTutorial/.idea/.name
@@ -0,0 +1 @@
+MyAndroidTutorial
\ No newline at end of file
diff --git a/examples/0601/MyAndroidTutorial/.idea/compiler.xml b/examples/0601/MyAndroidTutorial/.idea/compiler.xml
new file mode 100644
index 0000000..9a8b7e5
--- /dev/null
+++ b/examples/0601/MyAndroidTutorial/.idea/compiler.xml
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/examples/0601/MyAndroidTutorial/.idea/copyright/profiles_settings.xml b/examples/0601/MyAndroidTutorial/.idea/copyright/profiles_settings.xml
new file mode 100644
index 0000000..e7bedf3
--- /dev/null
+++ b/examples/0601/MyAndroidTutorial/.idea/copyright/profiles_settings.xml
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/examples/0601/MyAndroidTutorial/.idea/gradle.xml b/examples/0601/MyAndroidTutorial/.idea/gradle.xml
new file mode 100644
index 0000000..c595ad9
--- /dev/null
+++ b/examples/0601/MyAndroidTutorial/.idea/gradle.xml
@@ -0,0 +1,19 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/examples/0601/MyAndroidTutorial/.idea/misc.xml b/examples/0601/MyAndroidTutorial/.idea/misc.xml
new file mode 100644
index 0000000..b0c0cbc
--- /dev/null
+++ b/examples/0601/MyAndroidTutorial/.idea/misc.xml
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/examples/0601/MyAndroidTutorial/.idea/modules.xml b/examples/0601/MyAndroidTutorial/.idea/modules.xml
new file mode 100644
index 0000000..5edca7e
--- /dev/null
+++ b/examples/0601/MyAndroidTutorial/.idea/modules.xml
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/examples/0601/MyAndroidTutorial/.idea/vcs.xml b/examples/0601/MyAndroidTutorial/.idea/vcs.xml
new file mode 100644
index 0000000..6564d52
--- /dev/null
+++ b/examples/0601/MyAndroidTutorial/.idea/vcs.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/examples/0601/MyAndroidTutorial/MyAndroidTutorial.iml b/examples/0601/MyAndroidTutorial/MyAndroidTutorial.iml
new file mode 100644
index 0000000..62d899f
--- /dev/null
+++ b/examples/0601/MyAndroidTutorial/MyAndroidTutorial.iml
@@ -0,0 +1,19 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/examples/0601/MyAndroidTutorial/app/.gitignore b/examples/0601/MyAndroidTutorial/app/.gitignore
new file mode 100644
index 0000000..796b96d
--- /dev/null
+++ b/examples/0601/MyAndroidTutorial/app/.gitignore
@@ -0,0 +1 @@
+/build
diff --git a/examples/0601/MyAndroidTutorial/app/app.iml b/examples/0601/MyAndroidTutorial/app/app.iml
new file mode 100644
index 0000000..486024a
--- /dev/null
+++ b/examples/0601/MyAndroidTutorial/app/app.iml
@@ -0,0 +1,113 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/examples/0601/MyAndroidTutorial/app/build.gradle b/examples/0601/MyAndroidTutorial/app/build.gradle
new file mode 100644
index 0000000..9423d38
--- /dev/null
+++ b/examples/0601/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 16
+ 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/0601/MyAndroidTutorial/app/proguard-rules.pro b/examples/0601/MyAndroidTutorial/app/proguard-rules.pro
new file mode 100644
index 0000000..b5fa7ec
--- /dev/null
+++ b/examples/0601/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/0601/MyAndroidTutorial/app/src/androidTest/java/net/macdidi/myandroidtutorial/ApplicationTest.java b/examples/0601/MyAndroidTutorial/app/src/androidTest/java/net/macdidi/myandroidtutorial/ApplicationTest.java
new file mode 100644
index 0000000..2cb214e
--- /dev/null
+++ b/examples/0601/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/0601/MyAndroidTutorial/app/src/debug/res/values/google_maps_api.xml b/examples/0601/MyAndroidTutorial/app/src/debug/res/values/google_maps_api.xml
new file mode 100644
index 0000000..7341c8d
--- /dev/null
+++ b/examples/0601/MyAndroidTutorial/app/src/debug/res/values/google_maps_api.xml
@@ -0,0 +1,18 @@
+
+
+
+ AIzaSyCZg9YWlfokPA96VxWGYr6u4C12jL16VhM
+
+
diff --git a/examples/0601/MyAndroidTutorial/app/src/main/AndroidManifest.xml b/examples/0601/MyAndroidTutorial/app/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..50449c2
--- /dev/null
+++ b/examples/0601/MyAndroidTutorial/app/src/main/AndroidManifest.xml
@@ -0,0 +1,128 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/examples/0601/MyAndroidTutorial/app/src/main/java/net/macdidi/myandroidtutorial/AboutActivity.java b/examples/0601/MyAndroidTutorial/app/src/main/java/net/macdidi/myandroidtutorial/AboutActivity.java
new file mode 100644
index 0000000..42dddeb
--- /dev/null
+++ b/examples/0601/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/0601/MyAndroidTutorial/app/src/main/java/net/macdidi/myandroidtutorial/AlarmReceiver.java b/examples/0601/MyAndroidTutorial/app/src/main/java/net/macdidi/myandroidtutorial/AlarmReceiver.java
new file mode 100644
index 0000000..368165c
--- /dev/null
+++ b/examples/0601/MyAndroidTutorial/app/src/main/java/net/macdidi/myandroidtutorial/AlarmReceiver.java
@@ -0,0 +1,86 @@
+package net.macdidi.myandroidtutorial;
+
+import android.app.Notification;
+import android.app.NotificationManager;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.support.v4.app.NotificationCompat;
+
+import java.io.File;
+
+public class AlarmReceiver extends BroadcastReceiver {
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ // 讀取記事標題
+ //String title = intent.getStringExtra("title");
+ // 顯示訊息框
+ //Toast.makeText(context, title, Toast.LENGTH_LONG).show();
+
+ // 讀取記事編號
+ long id = intent.getLongExtra("id", 0);
+
+ if (id != 0) {
+ sendNotify(context, id);
+ }
+ }
+
+ private void sendNotify(Context context, long id) {
+ // 建立資料庫物件
+ ItemDAO itemDAO = new ItemDAO(context.getApplicationContext());
+ // 讀取指定編號的記事物件
+ Item item = itemDAO.get(id);
+
+ // 建立照片檔案物件
+ File file = new File(FileUtil.getExternalStorageDir(FileUtil.APP_DIR),
+ "P" + item.getFileName() + ".jpg");
+
+ // 是否儲存照片檔案
+ boolean isPicture = (item.getFileName() != null &&
+ item.getFileName().length() > 0 &&
+ file.exists());
+
+ // 取得NotificationManager物件
+ NotificationManager nm = (NotificationManager)
+ context.getSystemService(Context.NOTIFICATION_SERVICE);
+
+ // 如果有儲存照片檔案
+ if (isPicture) {
+ // 建立Notification.Builder物件,因為要設定大型圖片樣式
+ // 所以不能使用NotificationCompat.Builder
+ Notification.Builder builder = new Notification.Builder(context);
+ builder.setSmallIcon(android.R.drawable.star_on)
+ .setWhen(System.currentTimeMillis())
+ .setContentTitle(context.getString(R.string.app_name));
+
+ // 建立大型圖片樣式物件
+ Notification.BigPictureStyle bigPictureStyle =
+ new Notification.BigPictureStyle();
+ // 設定圖片與簡介
+ Bitmap bitmap = BitmapFactory.decodeFile(file.getAbsolutePath());
+ bigPictureStyle.bigPicture(bitmap)
+ .setSummaryText(item.getTitle());
+ // 設定樣式為大型圖片
+ builder.setStyle(bigPictureStyle);
+ // 發出通知
+ nm.notify((int)item.getId(), builder.build());
+ }
+ // 如果沒有儲存照片檔案
+ else {
+ // 建立NotificationCompat.Builder物件
+ NotificationCompat.Builder builder =
+ new NotificationCompat.Builder(context);
+ // 設定圖示、時間、內容標題和內容訊息
+ builder.setSmallIcon(android.R.drawable.star_big_on)
+ .setWhen(System.currentTimeMillis())
+ .setContentTitle(context.getString(R.string.app_name))
+ .setContentText(item.getTitle());
+ // 發出通知
+ nm.notify((int)item.getId(), builder.build());
+ }
+ }
+
+}
diff --git a/examples/0601/MyAndroidTutorial/app/src/main/java/net/macdidi/myandroidtutorial/ColorActivity.java b/examples/0601/MyAndroidTutorial/app/src/main/java/net/macdidi/myandroidtutorial/ColorActivity.java
new file mode 100644
index 0000000..182cd55
--- /dev/null
+++ b/examples/0601/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/0601/MyAndroidTutorial/app/src/main/java/net/macdidi/myandroidtutorial/Colors.java b/examples/0601/MyAndroidTutorial/app/src/main/java/net/macdidi/myandroidtutorial/Colors.java
new file mode 100644
index 0000000..1462149
--- /dev/null
+++ b/examples/0601/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/0601/MyAndroidTutorial/app/src/main/java/net/macdidi/myandroidtutorial/FileUtil.java b/examples/0601/MyAndroidTutorial/app/src/main/java/net/macdidi/myandroidtutorial/FileUtil.java
new file mode 100644
index 0000000..1fb41be
--- /dev/null
+++ b/examples/0601/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/0601/MyAndroidTutorial/app/src/main/java/net/macdidi/myandroidtutorial/InitAlarmReceiver.java b/examples/0601/MyAndroidTutorial/app/src/main/java/net/macdidi/myandroidtutorial/InitAlarmReceiver.java
new file mode 100644
index 0000000..959785f
--- /dev/null
+++ b/examples/0601/MyAndroidTutorial/app/src/main/java/net/macdidi/myandroidtutorial/InitAlarmReceiver.java
@@ -0,0 +1,49 @@
+package net.macdidi.myandroidtutorial;
+
+import android.app.AlarmManager;
+import android.app.PendingIntent;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+
+import java.util.Calendar;
+import java.util.List;
+
+public class InitAlarmReceiver extends BroadcastReceiver {
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ // 建立資料庫物件
+ ItemDAO itemDAO = new ItemDAO(context.getApplicationContext());
+ // 讀取資料庫所有記事資料
+ List- items = itemDAO.getAll();
+
+ // 讀取目前時間
+ long current = Calendar.getInstance().getTimeInMillis();
+
+ AlarmManager am = (AlarmManager)
+ context.getSystemService(Context.ALARM_SERVICE);
+
+ for (Item item : items) {
+ long alarm = item.getAlarmDatetime();
+
+ // 如果沒有設定提醒或是提醒已經過期
+ if (alarm == 0 || alarm <= current) {
+ continue;
+ }
+
+ // 設定提醒
+ Intent alarmIntent = new Intent(context, AlarmReceiver.class);
+ //alarmIntent.putExtra("title", item.getTitle());
+
+ // 加入記事編號
+ intent.putExtra("id", item.getId());
+
+ PendingIntent pi = PendingIntent.getBroadcast(
+ context, (int)item.getId(),
+ alarmIntent, PendingIntent.FLAG_ONE_SHOT);
+ am.set(AlarmManager.RTC_WAKEUP, item.getAlarmDatetime(), pi);
+ }
+ }
+
+}
diff --git a/examples/0601/MyAndroidTutorial/app/src/main/java/net/macdidi/myandroidtutorial/Item.java b/examples/0601/MyAndroidTutorial/app/src/main/java/net/macdidi/myandroidtutorial/Item.java
new file mode 100644
index 0000000..e2bc93d
--- /dev/null
+++ b/examples/0601/MyAndroidTutorial/app/src/main/java/net/macdidi/myandroidtutorial/Item.java
@@ -0,0 +1,156 @@
+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;
+
+ // 提醒日期時間
+ private long alarmDatetime;
+
+ 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(fileName);
+ break;
+ case START_LOCATION:
+ // 讀取與設定座標
+ double lat = data.getDoubleExtra("lat", 0.0);
+ double lng = data.getDoubleExtra("lng", 0.0);
+ item.setLatitude(lat);
+ item.setLongitude(lng);
+ 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());
+ }
+
+ 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);
+
+ // 設定儲存的座標
+ intentMap.putExtra("lat", item.getLatitude());
+ intentMap.putExtra("lng", item.getLongitude());
+ intentMap.putExtra("title", item.getTitle());
+ intentMap.putExtra("datetime", item.getLocaleDatetime());
+
+ // 啟動地圖元件
+ startActivityForResult(intentMap, START_LOCATION);
+ break;
+ case R.id.set_alarm:
+ // 設定提醒日期時間
+ processSetAlarm();
+ break;
+ case R.id.select_color:
+ // 啟動設定顏色的Activity元件
+ startActivityForResult(
+ new Intent(this, ColorActivity.class), START_COLOR);
+ break;
+ }
+
+ }
+
+ // 設定提醒日期時間
+ private void processSetAlarm() {
+ Calendar calendar = Calendar.getInstance();
+
+ if (item.getAlarmDatetime() != 0) {
+ // 設定為已經儲存的提醒日期時間
+ calendar.setTimeInMillis(item.getAlarmDatetime());
+ }
+
+ // 讀取年、月、日、時、分
+ int year = calendar.get(Calendar.YEAR);
+ int month = calendar.get(Calendar.MONTH);
+ int day = calendar.get(Calendar.DAY_OF_MONTH);
+ int hour = calendar.get(Calendar.HOUR_OF_DAY);
+ int minute = calendar.get(Calendar.MINUTE);
+
+ // 儲存設定的提醒日期時間
+ final Calendar alarm = Calendar.getInstance();
+
+ // 設定提醒時間
+ TimePickerDialog.OnTimeSetListener timeSetListener =
+ new TimePickerDialog.OnTimeSetListener() {
+ @Override
+ public void onTimeSet(TimePicker view,
+ int hourOfDay, int minute) {
+ alarm.set(Calendar.HOUR_OF_DAY, hourOfDay);
+ alarm.set(Calendar.MINUTE, minute);
+
+ item.setAlarmDatetime(alarm.getTimeInMillis());
+ }
+ };
+
+ // 選擇時間對話框
+ final TimePickerDialog tpd = new TimePickerDialog(
+ this, timeSetListener, hour, minute, true);
+
+ // 設定提醒日期
+ DatePickerDialog.OnDateSetListener dateSetListener =
+ new DatePickerDialog.OnDateSetListener() {
+ @Override
+ public void onDateSet(DatePicker view,
+ int year,
+ int monthOfYear,
+ int dayOfMonth) {
+ alarm.set(Calendar.YEAR, year);
+ alarm.set(Calendar.MONTH, monthOfYear);
+ alarm.set(Calendar.DAY_OF_MONTH, dayOfMonth);
+
+ // 繼續選擇提醒時間
+ tpd.show();
+ }
+ };
+
+ // 建立與顯示選擇日期對話框
+ final DatePickerDialog dpd = new DatePickerDialog(
+ this, dateSetListener, year, month, day);
+ dpd.show();
+ }
+
+ 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/0601/MyAndroidTutorial/app/src/main/java/net/macdidi/myandroidtutorial/ItemAdapter.java b/examples/0601/MyAndroidTutorial/app/src/main/java/net/macdidi/myandroidtutorial/ItemAdapter.java
new file mode 100644
index 0000000..85b40ba
--- /dev/null
+++ b/examples/0601/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/0601/MyAndroidTutorial/app/src/main/java/net/macdidi/myandroidtutorial/ItemAppWidget.java b/examples/0601/MyAndroidTutorial/app/src/main/java/net/macdidi/myandroidtutorial/ItemAppWidget.java
new file mode 100644
index 0000000..9301f57
--- /dev/null
+++ b/examples/0601/MyAndroidTutorial/app/src/main/java/net/macdidi/myandroidtutorial/ItemAppWidget.java
@@ -0,0 +1,72 @@
+package net.macdidi.myandroidtutorial;
+
+import android.app.PendingIntent;
+import android.appwidget.AppWidgetManager;
+import android.appwidget.AppWidgetProvider;
+import android.content.Context;
+import android.content.Intent;
+import android.widget.RemoteViews;
+
+
+public class ItemAppWidget extends AppWidgetProvider {
+
+ @Override
+ public void onUpdate(Context context,
+ AppWidgetManager appWidgetManager,
+ int[] appWidgetIds) {
+ final int N = appWidgetIds.length;
+
+ for (int i = 0; i < N; i++) {
+ updateAppWidget(context, appWidgetManager, appWidgetIds[i]);
+ }
+ }
+
+ @Override
+ public void onDeleted(Context context, int[] appWidgetIds) {
+ final int N = appWidgetIds.length;
+ for (int i = 0; i < N; i++) {
+ // 刪除小工具已經儲存的記事編號
+ ItemAppWidgetConfigureActivity.deleteItemPref(
+ context, appWidgetIds[i]);
+ }
+ }
+
+ @Override
+ public void onEnabled(Context context) {
+
+ }
+
+ @Override
+ public void onDisabled(Context context) {
+
+ }
+
+ static void updateAppWidget(Context context,
+ AppWidgetManager appWidgetManager,
+ int appWidgetId) {
+ // 讀取小工具儲存的記事編號
+ long id = ItemAppWidgetConfigureActivity.loadItemPref(
+ context, appWidgetId);
+ // 建立小工具畫面元件
+ RemoteViews views = new RemoteViews(
+ context.getPackageName(), R.layout.item_app_widget);
+ // 讀取指定編號的記事物件
+ ItemDAO itemDAO = new ItemDAO(context.getApplicationContext());
+ Item item = itemDAO.get(id);
+
+ // 設定小工具畫面顯示記事標題
+ views.setTextViewText(R.id.appwidget_text,
+ item != null ? item.getTitle() : "NA");
+
+ // 點選小工具畫面的記事標題後,啟動記事應用程式
+ Intent intent = new Intent(context, MainActivity.class);
+ PendingIntent pending = PendingIntent.getActivity(
+ context, 0, intent, 0);
+ views.setOnClickPendingIntent(R.id.appwidget_text, pending);
+
+ // 更新小工具
+ appWidgetManager.updateAppWidget(appWidgetId, views);
+ }
+}
+
+
diff --git a/examples/0601/MyAndroidTutorial/app/src/main/java/net/macdidi/myandroidtutorial/ItemAppWidgetConfigureActivity.java b/examples/0601/MyAndroidTutorial/app/src/main/java/net/macdidi/myandroidtutorial/ItemAppWidgetConfigureActivity.java
new file mode 100644
index 0000000..f83be71
--- /dev/null
+++ b/examples/0601/MyAndroidTutorial/app/src/main/java/net/macdidi/myandroidtutorial/ItemAppWidgetConfigureActivity.java
@@ -0,0 +1,120 @@
+package net.macdidi.myandroidtutorial;
+
+import android.app.Activity;
+import android.appwidget.AppWidgetManager;
+import android.content.Context;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.os.Bundle;
+import android.view.View;
+import android.widget.AdapterView;
+import android.widget.ListView;
+
+import java.util.List;
+
+
+public class ItemAppWidgetConfigureActivity extends Activity {
+
+ int mAppWidgetId = AppWidgetManager.INVALID_APPWIDGET_ID;
+
+ private static final String PREFS_NAME =
+ "net.macdidi.myandroidtutorial.ItemAppWidget";
+ private static final String PREF_PREFIX_KEY = "appwidget_";
+
+ // 選擇小工具使用的記事項目
+ private ListView item_list;
+ private ItemAdapter itemAdapter;
+ private List
- items;
+ private ItemDAO itemDAO;
+
+ public ItemAppWidgetConfigureActivity() {
+ super();
+ }
+
+ @Override
+ public void onCreate(Bundle icicle) {
+ super.onCreate(icicle);
+
+ setResult(RESULT_CANCELED);
+
+ // 改為使用應用程式主畫面
+ setContentView(R.layout.activity_main);
+
+ // 建立與設定選擇小工具使用的記事項目需要的物件
+ item_list = (ListView)findViewById(R.id.item_list);
+ itemDAO = new ItemDAO(getApplicationContext());
+ items = itemDAO.getAll();
+ itemAdapter = new ItemAdapter(this, R.layout.single_item, items);
+ item_list.setAdapter(itemAdapter);
+ item_list.setOnItemClickListener(itemListener);
+
+ Intent intent = getIntent();
+ Bundle extras = intent.getExtras();
+
+ if (extras != null) {
+ mAppWidgetId = extras.getInt(
+ AppWidgetManager.EXTRA_APPWIDGET_ID,
+ AppWidgetManager.INVALID_APPWIDGET_ID);
+ }
+
+ if (mAppWidgetId == AppWidgetManager.INVALID_APPWIDGET_ID) {
+ finish();
+ return;
+ }
+
+ }
+
+ // 選擇記事項目
+ AdapterView.OnItemClickListener itemListener =
+ new AdapterView.OnItemClickListener() {
+ @Override
+ public void onItemClick(AdapterView> parent, View view,
+ int position, long id) {
+ final Context context = ItemAppWidgetConfigureActivity.this;
+
+ // 讀取與儲存選擇的記事物件
+ Item item = itemAdapter.getItem(position);
+ saveItemPref(context, mAppWidgetId, item.getId());
+
+ AppWidgetManager appWidgetManager =
+ AppWidgetManager.getInstance(context);
+ ItemAppWidget.updateAppWidget(
+ context, appWidgetManager, mAppWidgetId);
+ Intent resultValue = new Intent();
+ resultValue.putExtra(
+ AppWidgetManager.EXTRA_APPWIDGET_ID, mAppWidgetId);
+ setResult(RESULT_OK, resultValue);
+
+ finish();
+ }
+ };
+
+ // 儲存選擇的記事編號
+ static void saveItemPref(Context context, int appWidgetId, long id) {
+ SharedPreferences.Editor prefs =
+ context.getSharedPreferences(PREFS_NAME, 0).edit();
+ prefs.putLong(PREF_PREFIX_KEY + appWidgetId, id);
+ prefs.commit();
+ }
+
+ // 讀取記事編號
+ static long loadItemPref(Context context, int appWidgetId) {
+ SharedPreferences prefs =
+ context.getSharedPreferences(PREFS_NAME, 0);
+ long idValue = prefs.getLong(PREF_PREFIX_KEY + appWidgetId, 0);
+
+ return idValue;
+ }
+
+ // 刪除記事編號
+ static void deleteItemPref(Context context, int appWidgetId) {
+ SharedPreferences.Editor prefs =
+ context.getSharedPreferences(PREFS_NAME, 0).edit();
+ prefs.remove(PREF_PREFIX_KEY + appWidgetId);
+ prefs.commit();
+ }
+
+}
+
+
+
diff --git a/examples/0601/MyAndroidTutorial/app/src/main/java/net/macdidi/myandroidtutorial/ItemDAO.java b/examples/0601/MyAndroidTutorial/app/src/main/java/net/macdidi/myandroidtutorial/ItemDAO.java
new file mode 100644
index 0000000..b3c7b49
--- /dev/null
+++ b/examples/0601/MyAndroidTutorial/app/src/main/java/net/macdidi/myandroidtutorial/ItemDAO.java
@@ -0,0 +1,214 @@
+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";
+
+ // 提醒日期時間
+ public static final String ALARMDATETIME_COLUMN = "alarmdatetime";
+
+ // 使用上面宣告的變數建立表格的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, " +
+ ALARMDATETIME_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());
+
+ // 提醒日期時間
+ cv.put(ALARMDATETIME_COLUMN, item.getAlarmDatetime());
+
+ // 新增一筆資料並取得編號
+ // 第一個參數是表格名稱
+ // 第二個參數是沒有指定欄位值的預設值
+ // 第三個參數是包裝新增資料的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());
+
+ // 提醒日期時間
+ cv.put(ALARMDATETIME_COLUMN, item.getAlarmDatetime());
+
+ // 設定修改資料的條件為編號
+ // 格式為「欄位名稱=資料」
+ 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));
+
+ // 提醒日期時間
+ result.setAlarmDatetime(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/0601/MyAndroidTutorial/app/src/main/java/net/macdidi/myandroidtutorial/MainActivity.java b/examples/0601/MyAndroidTutorial/app/src/main/java/net/macdidi/myandroidtutorial/MainActivity.java
new file mode 100644
index 0000000..3434bdd
--- /dev/null
+++ b/examples/0601/MyAndroidTutorial/app/src/main/java/net/macdidi/myandroidtutorial/MainActivity.java
@@ -0,0 +1,351 @@
+package net.macdidi.myandroidtutorial;
+
+import android.app.Activity;
+import android.app.ActivityOptions;
+import android.app.AlarmManager;
+import android.app.AlertDialog;
+import android.app.PendingIntent;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.os.Build;
+import android.os.Bundle;
+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 Activity {
+
+ 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");
+
+ // 是否修改提醒設定
+ boolean updateAlarm = false;
+
+ 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) {
+ // 讀取原來的提醒設定
+ Item ori = itemDAO.get(item.getId());
+ // 判斷是否需要設定提醒
+ updateAlarm = (item.getAlarmDatetime() != ori.getAlarmDatetime());
+
+ // 修改資料庫中的記事資料
+ itemDAO.update(item);
+
+ items.set(position, item);
+ itemAdapter.notifyDataSetChanged();
+ }
+ }
+
+ // 設定提醒
+ if (item.getAlarmDatetime() != 0 && updateAlarm) {
+ Intent intent = new Intent(this, AlarmReceiver.class);
+ //intent.putExtra("title", item.getTitle());
+
+ // 加入記事編號
+ intent.putExtra("id", item.getId());
+
+ PendingIntent pi = PendingIntent.getBroadcast(
+ this, (int)item.getId(),
+ intent, PendingIntent.FLAG_ONE_SHOT);
+
+ AlarmManager am = (AlarmManager)
+ getSystemService(Context.ALARM_SERVICE);
+ am.set(AlarmManager.RTC_WAKEUP, item.getAlarmDatetime(), pi);
+ }
+ }
+ }
+
+ 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);
+
+ // 依照版本啟動Acvitity元件
+ startActivityForVersion(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");
+ // 依照版本啟動Acvitity元件
+ startActivityForVersion(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;
+ }
+
+ }
+
+ public void aboutApp(View view) {
+ Intent intent = new Intent(this, AboutActivity.class);
+ startActivity(intent);
+ }
+
+ public void clickPreferences(MenuItem item) {
+ // 依照版本啟動Acvitity元件
+ startActivityForVersion(new Intent(this, PrefActivity.class));
+ }
+
+ private void startActivityForVersion(Intent intent, int requestCode) {
+ // 如果裝置的版本是LOLLIPOP
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
+ // 加入畫面轉換設定
+ startActivityForResult(intent, requestCode,
+ ActivityOptions.makeSceneTransitionAnimation(
+ MainActivity.this).toBundle());
+ }
+ else {
+ startActivityForResult(intent, requestCode);
+ }
+ }
+
+ private void startActivityForVersion(Intent intent) {
+ // 如果裝置的版本是LOLLIPOP
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
+ // 加入畫面轉換設定
+ startActivity(intent,
+ ActivityOptions.makeSceneTransitionAnimation(
+ MainActivity.this).toBundle());
+ }
+ else {
+ startActivity(intent);
+ }
+ }
+
+}
+
+
diff --git a/examples/0601/MyAndroidTutorial/app/src/main/java/net/macdidi/myandroidtutorial/MapsActivity.java b/examples/0601/MyAndroidTutorial/app/src/main/java/net/macdidi/myandroidtutorial/MapsActivity.java
new file mode 100644
index 0000000..abab911
--- /dev/null
+++ b/examples/0601/MyAndroidTutorial/app/src/main/java/net/macdidi/myandroidtutorial/MapsActivity.java
@@ -0,0 +1,338 @@
+package net.macdidi.myandroidtutorial;
+
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.location.Location;
+import android.os.Bundle;
+import android.support.v4.app.FragmentActivity;
+import android.widget.Toast;
+
+import com.google.android.gms.common.ConnectionResult;
+import com.google.android.gms.common.api.GoogleApiClient;
+import com.google.android.gms.common.api.GoogleApiClient.ConnectionCallbacks;
+import com.google.android.gms.common.api.GoogleApiClient.OnConnectionFailedListener;
+
+import com.google.android.gms.location.LocationListener;
+import com.google.android.gms.location.LocationRequest;
+import com.google.android.gms.location.LocationServices;
+
+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.Marker;
+import com.google.android.gms.maps.model.MarkerOptions;
+
+public class MapsActivity extends FragmentActivity
+ implements ConnectionCallbacks,
+ OnConnectionFailedListener,
+ LocationListener {
+
+ private GoogleMap mMap;
+
+ // Google API用戶端物件
+ private GoogleApiClient googleApiClient;
+
+ // Location請求物件
+ private LocationRequest locationRequest;
+
+ // 記錄目前最新的位置
+ private Location currentLocation;
+
+ // 顯示目前與儲存位置的標記物件
+ private Marker currentMarker, itemMarker;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_maps);
+ setUpMapIfNeeded();
+
+ // 建立Google API用戶端物件
+ configGoogleApiClient();
+
+ // 建立Location請求物件
+ configLocationRequest();
+
+ // 讀取記事儲存的座標
+ Intent intent = getIntent();
+ double lat = intent.getDoubleExtra("lat", 0.0);
+ double lng = intent.getDoubleExtra("lng", 0.0);
+
+ // 如果記事已經儲存座標
+ if (lat != 0.0 && lng != 0.0) {
+ // 建立座標物件
+ LatLng itemPlace = new LatLng(lat, lng);
+ // 加入地圖標記
+ addMarker(itemPlace, intent.getStringExtra("title"),
+ intent.getStringExtra("datetime"));
+ // 移動地圖
+ moveMap(itemPlace);
+ }
+ else {
+ // 連線到Google API用戶端
+ if (!googleApiClient.isConnected()) {
+ googleApiClient.connect();
+ }
+ }
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+ setUpMapIfNeeded();
+
+ // 連線到Google API用戶端
+ if (!googleApiClient.isConnected() && currentMarker != null) {
+ googleApiClient.connect();
+ }
+ }
+
+ @Override
+ protected void onPause() {
+ super.onPause();
+
+ // 移除位置請求服務
+ if (googleApiClient.isConnected()) {
+ LocationServices.FusedLocationApi.removeLocationUpdates(
+ googleApiClient, this);
+ }
+ }
+
+ @Override
+ protected void onStop() {
+ super.onStop();
+
+ // 移除Google API用戶端連線
+ if (googleApiClient.isConnected()) {
+ googleApiClient.disconnect();
+ }
+ }
+
+ // 建立Google API用戶端物件
+ private synchronized void configGoogleApiClient() {
+ googleApiClient = new GoogleApiClient.Builder(this)
+ .addConnectionCallbacks(this)
+ .addOnConnectionFailedListener(this)
+ .addApi(LocationServices.API)
+ .build();
+ }
+
+ // 建立Location請求物件
+ private void configLocationRequest() {
+ locationRequest = new LocationRequest();
+ // 設定讀取位置資訊的間隔時間為一秒(1000ms)
+ locationRequest.setInterval(1000);
+ // 設定讀取位置資訊最快的間隔時間為一秒(1000ms)
+ locationRequest.setFastestInterval(1000);
+ // 設定優先讀取高精確度的位置資訊(GPS)
+ locationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
+ }
+
+ private void setUpMapIfNeeded() {
+ if (mMap == null) {
+ mMap = ((SupportMapFragment) getSupportFragmentManager().
+ findFragmentById(R.id.map)).getMap();
+
+ if (mMap != null) {
+ // 移除地圖設定
+ //setUpMap();
+ processController();
+ }
+ }
+ }
+
+ // 移除地圖設定方法
+ private void setUpMap() {
+ // 建立位置的座標物件
+ LatLng place = new LatLng(25.033408, 121.564099);
+ // 移動地圖
+ moveMap(place);
+
+ // 加入地圖標記
+ addMarker(place, "Hello!", " Google Maps v2!");
+ }
+
+ private void processController() {
+ // 對話框按鈕事件
+ final DialogInterface.OnClickListener listener =
+ new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ switch (which) {
+ // 更新位置資訊
+ case DialogInterface.BUTTON_POSITIVE:
+ // 連線到Google API用戶端
+ if (!googleApiClient.isConnected()) {
+ googleApiClient.connect();
+ }
+ break;
+ // 清除位置資訊
+ case DialogInterface.BUTTON_NEUTRAL:
+ Intent result = new Intent();
+ result.putExtra("lat", 0);
+ result.putExtra("lng", 0);
+ setResult(Activity.RESULT_OK, result);
+ finish();
+ break;
+ // 取消
+ case DialogInterface.BUTTON_NEGATIVE:
+ break;
+ }
+ }
+ };
+
+ // 標記訊息框點擊事件
+ mMap.setOnInfoWindowClickListener(new GoogleMap.OnInfoWindowClickListener() {
+ @Override
+ public void onInfoWindowClick(Marker marker) {
+ // 如果是記事儲存的標記
+ if (marker.equals(itemMarker)) {
+ AlertDialog.Builder ab = new AlertDialog.Builder(MapsActivity.this);
+
+ ab.setTitle(R.string.title_update_location)
+ .setMessage(R.string.message_update_location)
+ .setCancelable(true);
+
+ ab.setPositiveButton(R.string.update, listener);
+ ab.setNeutralButton(R.string.clear, listener);
+ ab.setNegativeButton(android.R.string.cancel, listener);
+
+ ab.show();
+ }
+ }
+ });
+
+ // 標記點擊事件
+ mMap.setOnMarkerClickListener(new GoogleMap.OnMarkerClickListener() {
+ @Override
+ public boolean onMarkerClick(Marker marker) {
+ // 如果是目前位置標記
+ if (marker.equals(currentMarker)) {
+ AlertDialog.Builder ab = new AlertDialog.Builder(MapsActivity.this);
+
+ ab.setTitle(R.string.title_current_location)
+ .setMessage(R.string.message_current_location)
+ .setCancelable(true);
+
+ ab.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ Intent result = new Intent();
+ result.putExtra("lat", currentLocation.getLatitude());
+ result.putExtra("lng", currentLocation.getLongitude());
+ setResult(Activity.RESULT_OK, result);
+ finish();
+ }
+ });
+ ab.setNegativeButton(android.R.string.cancel, null);
+
+ ab.show();
+
+ return true;
+ }
+
+ return false;
+ }
+ });
+ }
+
+ // 移動地圖到參數指定的位置
+ private void moveMap(LatLng place) {
+ // 建立地圖攝影機的位置物件
+ CameraPosition cameraPosition =
+ new CameraPosition.Builder()
+ .target(place)
+ .zoom(17)
+ .build();
+
+ // 使用動畫的效果移動地圖
+ mMap.animateCamera(CameraUpdateFactory.newCameraPosition(cameraPosition),
+ new GoogleMap.CancelableCallback() {
+ @Override
+ public void onFinish() {
+ if (itemMarker != null) {
+ itemMarker.showInfoWindow();
+ }
+ }
+
+ @Override
+ public void onCancel() {
+
+ }
+ });
+ }
+
+ // 在地圖加入指定位置與標題的標記
+ 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);
+
+ // 加入並設定記事儲存的位置標記
+ itemMarker = mMap.addMarker(markerOptions);
+ }
+
+ // ConnectionCallbacks
+ @Override
+ public void onConnected(Bundle bundle) {
+ // 已經連線到Google Services
+ // 啟動位置更新服務
+ // 位置資訊更新的時候,應用程式會自動呼叫LocationListener.onLocationChanged
+ LocationServices.FusedLocationApi.requestLocationUpdates(
+ googleApiClient, locationRequest, MapsActivity.this);
+ }
+
+ // ConnectionCallbacks
+ @Override
+ public void onConnectionSuspended(int i) {
+ // Google Services連線中斷
+ // int參數是連線中斷的代號
+ }
+
+ // OnConnectionFailedListener
+ @Override
+ public void onConnectionFailed(ConnectionResult connectionResult) {
+ // Google Services連線失敗
+ // ConnectionResult參數是連線失敗的資訊
+ int errorCode = connectionResult.getErrorCode();
+
+ // 裝置沒有安裝Google Play服務
+ if (errorCode == ConnectionResult.SERVICE_MISSING) {
+ Toast.makeText(this, R.string.google_play_service_missing,
+ Toast.LENGTH_LONG).show();
+ }
+ }
+
+ // LocationListener
+ @Override
+ public void onLocationChanged(Location location) {
+ // 位置改變
+ // Location參數是目前的位置
+ currentLocation = location;
+ LatLng latLng = new LatLng(
+ location.getLatitude(), location.getLongitude());
+
+ // 設定目前位置的標記
+ if (currentMarker == null) {
+ currentMarker = mMap.addMarker(new MarkerOptions().position(latLng));
+ }
+ else {
+ currentMarker.setPosition(latLng);
+ }
+
+ // 移動地圖到目前的位置
+ moveMap(latLng);
+ }
+
+}
diff --git a/examples/0601/MyAndroidTutorial/app/src/main/java/net/macdidi/myandroidtutorial/MyDBHelper.java b/examples/0601/MyAndroidTutorial/app/src/main/java/net/macdidi/myandroidtutorial/MyDBHelper.java
new file mode 100644
index 0000000..9350577
--- /dev/null
+++ b/examples/0601/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 = 3;
+ // 資料庫物件,固定的欄位變數
+ 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/0601/MyAndroidTutorial/app/src/main/java/net/macdidi/myandroidtutorial/PlayActivity.java b/examples/0601/MyAndroidTutorial/app/src/main/java/net/macdidi/myandroidtutorial/PlayActivity.java
new file mode 100644
index 0000000..5cfb523
--- /dev/null
+++ b/examples/0601/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/0601/MyAndroidTutorial/app/src/main/java/net/macdidi/myandroidtutorial/PrefActivity.java b/examples/0601/MyAndroidTutorial/app/src/main/java/net/macdidi/myandroidtutorial/PrefActivity.java
new file mode 100644
index 0000000..b2dcc4a
--- /dev/null
+++ b/examples/0601/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/0601/MyAndroidTutorial/app/src/main/java/net/macdidi/myandroidtutorial/RecordActivity.java b/examples/0601/MyAndroidTutorial/app/src/main/java/net/macdidi/myandroidtutorial/RecordActivity.java
new file mode 100644
index 0000000..62b5e93
--- /dev/null
+++ b/examples/0601/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/0601/MyAndroidTutorial/app/src/main/res/drawable-hdpi/ic_launcher.png b/examples/0601/MyAndroidTutorial/app/src/main/res/drawable-hdpi/ic_launcher.png
new file mode 100644
index 0000000..96a442e
Binary files /dev/null and b/examples/0601/MyAndroidTutorial/app/src/main/res/drawable-hdpi/ic_launcher.png differ
diff --git a/examples/0601/MyAndroidTutorial/app/src/main/res/drawable-mdpi/ic_launcher.png b/examples/0601/MyAndroidTutorial/app/src/main/res/drawable-mdpi/ic_launcher.png
new file mode 100644
index 0000000..359047d
Binary files /dev/null and b/examples/0601/MyAndroidTutorial/app/src/main/res/drawable-mdpi/ic_launcher.png differ
diff --git a/examples/0601/MyAndroidTutorial/app/src/main/res/drawable-nodpi/example_appwidget_preview.png b/examples/0601/MyAndroidTutorial/app/src/main/res/drawable-nodpi/example_appwidget_preview.png
new file mode 100644
index 0000000..894b069
Binary files /dev/null and b/examples/0601/MyAndroidTutorial/app/src/main/res/drawable-nodpi/example_appwidget_preview.png differ
diff --git a/examples/0601/MyAndroidTutorial/app/src/main/res/drawable-xhdpi/ic_launcher.png b/examples/0601/MyAndroidTutorial/app/src/main/res/drawable-xhdpi/ic_launcher.png
new file mode 100644
index 0000000..71c6d76
Binary files /dev/null and b/examples/0601/MyAndroidTutorial/app/src/main/res/drawable-xhdpi/ic_launcher.png differ
diff --git a/examples/0601/MyAndroidTutorial/app/src/main/res/drawable-xxhdpi/ic_launcher.png b/examples/0601/MyAndroidTutorial/app/src/main/res/drawable-xxhdpi/ic_launcher.png
new file mode 100644
index 0000000..4df1894
Binary files /dev/null and b/examples/0601/MyAndroidTutorial/app/src/main/res/drawable-xxhdpi/ic_launcher.png differ
diff --git a/examples/0601/MyAndroidTutorial/app/src/main/res/drawable/alarm_icon.png b/examples/0601/MyAndroidTutorial/app/src/main/res/drawable/alarm_icon.png
new file mode 100644
index 0000000..c6cac88
Binary files /dev/null and b/examples/0601/MyAndroidTutorial/app/src/main/res/drawable/alarm_icon.png differ
diff --git a/examples/0601/MyAndroidTutorial/app/src/main/res/drawable/item_drawable.xml b/examples/0601/MyAndroidTutorial/app/src/main/res/drawable/item_drawable.xml
new file mode 100644
index 0000000..37607e2
--- /dev/null
+++ b/examples/0601/MyAndroidTutorial/app/src/main/res/drawable/item_drawable.xml
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/examples/0601/MyAndroidTutorial/app/src/main/res/drawable/location_icon.png b/examples/0601/MyAndroidTutorial/app/src/main/res/drawable/location_icon.png
new file mode 100644
index 0000000..4c3c514
Binary files /dev/null and b/examples/0601/MyAndroidTutorial/app/src/main/res/drawable/location_icon.png differ
diff --git a/examples/0601/MyAndroidTutorial/app/src/main/res/drawable/pause_icon.png b/examples/0601/MyAndroidTutorial/app/src/main/res/drawable/pause_icon.png
new file mode 100755
index 0000000..a5aee6f
Binary files /dev/null and b/examples/0601/MyAndroidTutorial/app/src/main/res/drawable/pause_icon.png differ
diff --git a/examples/0601/MyAndroidTutorial/app/src/main/res/drawable/play_icon.png b/examples/0601/MyAndroidTutorial/app/src/main/res/drawable/play_icon.png
new file mode 100755
index 0000000..6a40cd5
Binary files /dev/null and b/examples/0601/MyAndroidTutorial/app/src/main/res/drawable/play_icon.png differ
diff --git a/examples/0601/MyAndroidTutorial/app/src/main/res/drawable/record_dark_icon.png b/examples/0601/MyAndroidTutorial/app/src/main/res/drawable/record_dark_icon.png
new file mode 100755
index 0000000..bcf83ca
Binary files /dev/null and b/examples/0601/MyAndroidTutorial/app/src/main/res/drawable/record_dark_icon.png differ
diff --git a/examples/0601/MyAndroidTutorial/app/src/main/res/drawable/record_red_icon.png b/examples/0601/MyAndroidTutorial/app/src/main/res/drawable/record_red_icon.png
new file mode 100755
index 0000000..2b44af0
Binary files /dev/null and b/examples/0601/MyAndroidTutorial/app/src/main/res/drawable/record_red_icon.png differ
diff --git a/examples/0601/MyAndroidTutorial/app/src/main/res/drawable/record_sound_icon.png b/examples/0601/MyAndroidTutorial/app/src/main/res/drawable/record_sound_icon.png
new file mode 100755
index 0000000..a1382ac
Binary files /dev/null and b/examples/0601/MyAndroidTutorial/app/src/main/res/drawable/record_sound_icon.png differ
diff --git a/examples/0601/MyAndroidTutorial/app/src/main/res/drawable/retangle_drawable.xml b/examples/0601/MyAndroidTutorial/app/src/main/res/drawable/retangle_drawable.xml
new file mode 100644
index 0000000..51d1e84
--- /dev/null
+++ b/examples/0601/MyAndroidTutorial/app/src/main/res/drawable/retangle_drawable.xml
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/examples/0601/MyAndroidTutorial/app/src/main/res/drawable/select_color_icon.png b/examples/0601/MyAndroidTutorial/app/src/main/res/drawable/select_color_icon.png
new file mode 100644
index 0000000..8567d5e
Binary files /dev/null and b/examples/0601/MyAndroidTutorial/app/src/main/res/drawable/select_color_icon.png differ
diff --git a/examples/0601/MyAndroidTutorial/app/src/main/res/drawable/selected_icon.png b/examples/0601/MyAndroidTutorial/app/src/main/res/drawable/selected_icon.png
new file mode 100755
index 0000000..b891571
Binary files /dev/null and b/examples/0601/MyAndroidTutorial/app/src/main/res/drawable/selected_icon.png differ
diff --git a/examples/0601/MyAndroidTutorial/app/src/main/res/drawable/stop_icon.png b/examples/0601/MyAndroidTutorial/app/src/main/res/drawable/stop_icon.png
new file mode 100755
index 0000000..20df415
Binary files /dev/null and b/examples/0601/MyAndroidTutorial/app/src/main/res/drawable/stop_icon.png differ
diff --git a/examples/0601/MyAndroidTutorial/app/src/main/res/drawable/take_picture_icon.png b/examples/0601/MyAndroidTutorial/app/src/main/res/drawable/take_picture_icon.png
new file mode 100644
index 0000000..08fb514
Binary files /dev/null and b/examples/0601/MyAndroidTutorial/app/src/main/res/drawable/take_picture_icon.png differ
diff --git a/examples/0601/MyAndroidTutorial/app/src/main/res/layout/activity_about.xml b/examples/0601/MyAndroidTutorial/app/src/main/res/layout/activity_about.xml
new file mode 100644
index 0000000..211a9b6
--- /dev/null
+++ b/examples/0601/MyAndroidTutorial/app/src/main/res/layout/activity_about.xml
@@ -0,0 +1,36 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/examples/0601/MyAndroidTutorial/app/src/main/res/layout/activity_color.xml b/examples/0601/MyAndroidTutorial/app/src/main/res/layout/activity_color.xml
new file mode 100644
index 0000000..d25bbc5
--- /dev/null
+++ b/examples/0601/MyAndroidTutorial/app/src/main/res/layout/activity_color.xml
@@ -0,0 +1,16 @@
+
+
+
+
+
diff --git a/examples/0601/MyAndroidTutorial/app/src/main/res/layout/activity_item.xml b/examples/0601/MyAndroidTutorial/app/src/main/res/layout/activity_item.xml
new file mode 100644
index 0000000..4ec4362
--- /dev/null
+++ b/examples/0601/MyAndroidTutorial/app/src/main/res/layout/activity_item.xml
@@ -0,0 +1,125 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/examples/0601/MyAndroidTutorial/app/src/main/res/layout/activity_main.xml b/examples/0601/MyAndroidTutorial/app/src/main/res/layout/activity_main.xml
new file mode 100644
index 0000000..121511b
--- /dev/null
+++ b/examples/0601/MyAndroidTutorial/app/src/main/res/layout/activity_main.xml
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
diff --git a/examples/0601/MyAndroidTutorial/app/src/main/res/layout/activity_maps.xml b/examples/0601/MyAndroidTutorial/app/src/main/res/layout/activity_maps.xml
new file mode 100644
index 0000000..5de477b
--- /dev/null
+++ b/examples/0601/MyAndroidTutorial/app/src/main/res/layout/activity_maps.xml
@@ -0,0 +1,7 @@
+
diff --git a/examples/0601/MyAndroidTutorial/app/src/main/res/layout/activity_play.xml b/examples/0601/MyAndroidTutorial/app/src/main/res/layout/activity_play.xml
new file mode 100644
index 0000000..52db308
--- /dev/null
+++ b/examples/0601/MyAndroidTutorial/app/src/main/res/layout/activity_play.xml
@@ -0,0 +1,38 @@
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/examples/0601/MyAndroidTutorial/app/src/main/res/layout/activity_record.xml b/examples/0601/MyAndroidTutorial/app/src/main/res/layout/activity_record.xml
new file mode 100644
index 0000000..63d9e36
--- /dev/null
+++ b/examples/0601/MyAndroidTutorial/app/src/main/res/layout/activity_record.xml
@@ -0,0 +1,51 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/examples/0601/MyAndroidTutorial/app/src/main/res/layout/item_app_widget.xml b/examples/0601/MyAndroidTutorial/app/src/main/res/layout/item_app_widget.xml
new file mode 100644
index 0000000..92bb45a
--- /dev/null
+++ b/examples/0601/MyAndroidTutorial/app/src/main/res/layout/item_app_widget.xml
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/examples/0601/MyAndroidTutorial/app/src/main/res/layout/single_item.xml b/examples/0601/MyAndroidTutorial/app/src/main/res/layout/single_item.xml
new file mode 100644
index 0000000..40ddbc9
--- /dev/null
+++ b/examples/0601/MyAndroidTutorial/app/src/main/res/layout/single_item.xml
@@ -0,0 +1,50 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/examples/0601/MyAndroidTutorial/app/src/main/res/menu/menu_main.xml b/examples/0601/MyAndroidTutorial/app/src/main/res/menu/menu_main.xml
new file mode 100644
index 0000000..3ea061c
--- /dev/null
+++ b/examples/0601/MyAndroidTutorial/app/src/main/res/menu/menu_main.xml
@@ -0,0 +1,56 @@
+
diff --git a/examples/0601/MyAndroidTutorial/app/src/main/res/transition/explode_transition.xml b/examples/0601/MyAndroidTutorial/app/src/main/res/transition/explode_transition.xml
new file mode 100644
index 0000000..d2320b1
--- /dev/null
+++ b/examples/0601/MyAndroidTutorial/app/src/main/res/transition/explode_transition.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/examples/0601/MyAndroidTutorial/app/src/main/res/transition/fade_transition.xml b/examples/0601/MyAndroidTutorial/app/src/main/res/transition/fade_transition.xml
new file mode 100644
index 0000000..ea54111
--- /dev/null
+++ b/examples/0601/MyAndroidTutorial/app/src/main/res/transition/fade_transition.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/examples/0601/MyAndroidTutorial/app/src/main/res/transition/slide_transition.xml b/examples/0601/MyAndroidTutorial/app/src/main/res/transition/slide_transition.xml
new file mode 100644
index 0000000..afd9869
--- /dev/null
+++ b/examples/0601/MyAndroidTutorial/app/src/main/res/transition/slide_transition.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/examples/0601/MyAndroidTutorial/app/src/main/res/values-en/strings.xml b/examples/0601/MyAndroidTutorial/app/src/main/res/values-en/strings.xml
new file mode 100644
index 0000000..c532533
--- /dev/null
+++ b/examples/0601/MyAndroidTutorial/app/src/main/res/values-en/strings.xml
@@ -0,0 +1,12 @@
+
+
+
+ MyAndroidTutorial
+ Hello world!
+ Settings
+
+ Title
+ Enter title
+ Content
+ Enter content
+
diff --git a/examples/0601/MyAndroidTutorial/app/src/main/res/values-v14/dimens.xml b/examples/0601/MyAndroidTutorial/app/src/main/res/values-v14/dimens.xml
new file mode 100644
index 0000000..4db8c59
--- /dev/null
+++ b/examples/0601/MyAndroidTutorial/app/src/main/res/values-v14/dimens.xml
@@ -0,0 +1,10 @@
+
+
+
+
+ 0dp
+
+
\ No newline at end of file
diff --git a/examples/0601/MyAndroidTutorial/app/src/main/res/values-v21/styles.xml b/examples/0601/MyAndroidTutorial/app/src/main/res/values-v21/styles.xml
new file mode 100644
index 0000000..606e45f
--- /dev/null
+++ b/examples/0601/MyAndroidTutorial/app/src/main/res/values-v21/styles.xml
@@ -0,0 +1,12 @@
+
+
+
+
\ No newline at end of file
diff --git a/examples/0601/MyAndroidTutorial/app/src/main/res/values-w820dp/dimens.xml b/examples/0601/MyAndroidTutorial/app/src/main/res/values-w820dp/dimens.xml
new file mode 100644
index 0000000..63fc816
--- /dev/null
+++ b/examples/0601/MyAndroidTutorial/app/src/main/res/values-w820dp/dimens.xml
@@ -0,0 +1,6 @@
+
+
+ 64dp
+
diff --git a/examples/0601/MyAndroidTutorial/app/src/main/res/values/colors.xml b/examples/0601/MyAndroidTutorial/app/src/main/res/values/colors.xml
new file mode 100644
index 0000000..6b13c1d
--- /dev/null
+++ b/examples/0601/MyAndroidTutorial/app/src/main/res/values/colors.xml
@@ -0,0 +1,7 @@
+
+
+ #CCCCCC
+ #AAAAAA
+ #DD999999
+ #111111
+
\ No newline at end of file
diff --git a/examples/0601/MyAndroidTutorial/app/src/main/res/values/dimens.xml b/examples/0601/MyAndroidTutorial/app/src/main/res/values/dimens.xml
new file mode 100644
index 0000000..9923269
--- /dev/null
+++ b/examples/0601/MyAndroidTutorial/app/src/main/res/values/dimens.xml
@@ -0,0 +1,15 @@
+
+
+ 16dp
+ 16dp
+
+ 6dp
+ 24sp
+ 2dp
+
+
+ 8dp
+
diff --git a/examples/0601/MyAndroidTutorial/app/src/main/res/values/strings.xml b/examples/0601/MyAndroidTutorial/app/src/main/res/values/strings.xml
new file mode 100644
index 0000000..a59f440
--- /dev/null
+++ b/examples/0601/MyAndroidTutorial/app/src/main/res/values/strings.xml
@@ -0,0 +1,59 @@
+
+
+
+ MyAndroidTutorial
+ Hello world!
+ Settings
+ 標題
+ 輸入標題
+ 內容
+ 輸入內容
+ 這是Android Tutorial應用程式
+ AboutActivity
+ 版本:AndroidTutorial_0.2.4
+ ItemActivity
+ ColorActivity
+ 刪除
+ 確定要刪除 %1$d 個項目?
+ 預設的顏色
+ 新增記事的預設顏色
+
+ 預設提醒時間
+ 在指定的時間之前通知
+
+
+
- 五分鐘
+ - 十分鐘
+ - 二十分鐘
+ - 三十分鐘
+ - 六十分鐘
+
+
+
+ - 5
+ - 10
+ - 20
+ - 30
+ - 60
+
+
+ 語音備忘
+ 播放語音備忘
+ 播放
+ 重新錄製
+ Map
+
+ 記事儲存的位置
+ 更新或清除儲存的位置資訊?
+ 更新
+ 清除
+
+ 目前位置
+ 是否儲存目前位置?
+
+ 裝置沒有安裝Google Play服務
+ EXAMPLE
+ Configure
+ Add widget
+
+
diff --git a/examples/0601/MyAndroidTutorial/app/src/main/res/values/styles.xml b/examples/0601/MyAndroidTutorial/app/src/main/res/values/styles.xml
new file mode 100644
index 0000000..766ab99
--- /dev/null
+++ b/examples/0601/MyAndroidTutorial/app/src/main/res/values/styles.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
diff --git a/examples/0601/MyAndroidTutorial/app/src/main/res/xml/item_app_widget_info.xml b/examples/0601/MyAndroidTutorial/app/src/main/res/xml/item_app_widget_info.xml
new file mode 100644
index 0000000..043d27b
--- /dev/null
+++ b/examples/0601/MyAndroidTutorial/app/src/main/res/xml/item_app_widget_info.xml
@@ -0,0 +1,15 @@
+
+
+
\ No newline at end of file
diff --git a/examples/0601/MyAndroidTutorial/app/src/main/res/xml/mypreference.xml b/examples/0601/MyAndroidTutorial/app/src/main/res/xml/mypreference.xml
new file mode 100644
index 0000000..33e714c
--- /dev/null
+++ b/examples/0601/MyAndroidTutorial/app/src/main/res/xml/mypreference.xml
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/examples/0601/MyAndroidTutorial/app/src/release/res/values/google_maps_api.xml b/examples/0601/MyAndroidTutorial/app/src/release/res/values/google_maps_api.xml
new file mode 100644
index 0000000..c4e2431
--- /dev/null
+++ b/examples/0601/MyAndroidTutorial/app/src/release/res/values/google_maps_api.xml
@@ -0,0 +1,18 @@
+
+
+
+ YOUR_KEY_HERE
+
+
diff --git a/examples/0601/MyAndroidTutorial/build.gradle b/examples/0601/MyAndroidTutorial/build.gradle
new file mode 100644
index 0000000..6356aab
--- /dev/null
+++ b/examples/0601/MyAndroidTutorial/build.gradle
@@ -0,0 +1,19 @@
+// Top-level build file where you can add configuration options common to all sub-projects/modules.
+
+buildscript {
+ repositories {
+ jcenter()
+ }
+ dependencies {
+ classpath 'com.android.tools.build:gradle:1.0.0'
+
+ // NOTE: Do not place your application dependencies here; they belong
+ // in the individual module build.gradle files
+ }
+}
+
+allprojects {
+ repositories {
+ jcenter()
+ }
+}
diff --git a/examples/0601/MyAndroidTutorial/gradle.properties b/examples/0601/MyAndroidTutorial/gradle.properties
new file mode 100644
index 0000000..1d3591c
--- /dev/null
+++ b/examples/0601/MyAndroidTutorial/gradle.properties
@@ -0,0 +1,18 @@
+# Project-wide Gradle settings.
+
+# IDE (e.g. Android Studio) users:
+# Gradle settings configured through the IDE *will override*
+# any settings specified in this file.
+
+# For more details on how to configure your build environment visit
+# http://www.gradle.org/docs/current/userguide/build_environment.html
+
+# Specifies the JVM arguments used for the daemon process.
+# The setting is particularly useful for tweaking memory settings.
+# Default value: -Xmx10248m -XX:MaxPermSize=256m
+# org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
+
+# When configured, Gradle will run in incubating parallel mode.
+# This option should only be used with decoupled projects. More details, visit
+# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
+# org.gradle.parallel=true
\ No newline at end of file
diff --git a/examples/0601/MyAndroidTutorial/gradle/wrapper/gradle-wrapper.jar b/examples/0601/MyAndroidTutorial/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 0000000..8c0fb64
Binary files /dev/null and b/examples/0601/MyAndroidTutorial/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/examples/0601/MyAndroidTutorial/gradle/wrapper/gradle-wrapper.properties b/examples/0601/MyAndroidTutorial/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 0000000..0c71e76
--- /dev/null
+++ b/examples/0601/MyAndroidTutorial/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,6 @@
+#Wed Apr 10 15:27:10 PDT 2013
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip
diff --git a/examples/0601/MyAndroidTutorial/gradlew b/examples/0601/MyAndroidTutorial/gradlew
new file mode 100755
index 0000000..91a7e26
--- /dev/null
+++ b/examples/0601/MyAndroidTutorial/gradlew
@@ -0,0 +1,164 @@
+#!/usr/bin/env bash
+
+##############################################################################
+##
+## Gradle start up script for UN*X
+##
+##############################################################################
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS=""
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn ( ) {
+ echo "$*"
+}
+
+die ( ) {
+ echo
+ echo "$*"
+ echo
+ exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+case "`uname`" in
+ CYGWIN* )
+ cygwin=true
+ ;;
+ Darwin* )
+ darwin=true
+ ;;
+ MINGW* )
+ msys=true
+ ;;
+esac
+
+# For Cygwin, ensure paths are in UNIX format before anything is touched.
+if $cygwin ; then
+ [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
+fi
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+ ls=`ls -ld "$PRG"`
+ link=`expr "$ls" : '.*-> \(.*\)$'`
+ if expr "$link" : '/.*' > /dev/null; then
+ PRG="$link"
+ else
+ PRG=`dirname "$PRG"`"/$link"
+ fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >&-
+APP_HOME="`pwd -P`"
+cd "$SAVED" >&-
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD="$JAVA_HOME/jre/sh/java"
+ else
+ JAVACMD="$JAVA_HOME/bin/java"
+ fi
+ if [ ! -x "$JAVACMD" ] ; then
+ die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+ fi
+else
+ JAVACMD="java"
+ which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
+ MAX_FD_LIMIT=`ulimit -H -n`
+ if [ $? -eq 0 ] ; then
+ if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+ MAX_FD="$MAX_FD_LIMIT"
+ fi
+ ulimit -n $MAX_FD
+ if [ $? -ne 0 ] ; then
+ warn "Could not set maximum file descriptor limit: $MAX_FD"
+ fi
+ else
+ warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+ fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+ GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin, switch paths to Windows format before running java
+if $cygwin ; then
+ APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+ CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+
+ # We build the pattern for arguments to be converted via cygpath
+ ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+ SEP=""
+ for dir in $ROOTDIRSRAW ; do
+ ROOTDIRS="$ROOTDIRS$SEP$dir"
+ SEP="|"
+ done
+ OURCYGPATTERN="(^($ROOTDIRS))"
+ # Add a user-defined pattern to the cygpath arguments
+ if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+ OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+ fi
+ # Now convert the arguments - kludge to limit ourselves to /bin/sh
+ i=0
+ for arg in "$@" ; do
+ CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+ CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
+
+ if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
+ eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+ else
+ eval `echo args$i`="\"$arg\""
+ fi
+ i=$((i+1))
+ done
+ case $i in
+ (0) set -- ;;
+ (1) set -- "$args0" ;;
+ (2) set -- "$args0" "$args1" ;;
+ (3) set -- "$args0" "$args1" "$args2" ;;
+ (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+ (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+ (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+ (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+ (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+ (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+ esac
+fi
+
+# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
+function splitJvmOpts() {
+ JVM_OPTS=("$@")
+}
+eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
+JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
+
+exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
diff --git a/examples/0601/MyAndroidTutorial/gradlew.bat b/examples/0601/MyAndroidTutorial/gradlew.bat
new file mode 100644
index 0000000..aec9973
--- /dev/null
+++ b/examples/0601/MyAndroidTutorial/gradlew.bat
@@ -0,0 +1,90 @@
+@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS=
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto init
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto init
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:init
+@rem Get command-line arguments, handling Windowz variants
+
+if not "%OS%" == "Windows_NT" goto win9xME_args
+if "%@eval[2+2]" == "4" goto 4NT_args
+
+:win9xME_args
+@rem Slurp the command line arguments.
+set CMD_LINE_ARGS=
+set _SKIP=2
+
+:win9xME_args_slurp
+if "x%~1" == "x" goto execute
+
+set CMD_LINE_ARGS=%*
+goto execute
+
+:4NT_args
+@rem Get arguments from the 4NT Shell from JP Software
+set CMD_LINE_ARGS=%$
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/examples/0601/MyAndroidTutorial/settings.gradle b/examples/0601/MyAndroidTutorial/settings.gradle
new file mode 100644
index 0000000..e7b4def
--- /dev/null
+++ b/examples/0601/MyAndroidTutorial/settings.gradle
@@ -0,0 +1 @@
+include ':app'
diff --git a/examples/0602/MyAndroidTutorial/.gitignore b/examples/0602/MyAndroidTutorial/.gitignore
new file mode 100644
index 0000000..afbdab3
--- /dev/null
+++ b/examples/0602/MyAndroidTutorial/.gitignore
@@ -0,0 +1,6 @@
+.gradle
+/local.properties
+/.idea/workspace.xml
+/.idea/libraries
+.DS_Store
+/build
diff --git a/examples/0602/MyAndroidTutorial/.idea/.name b/examples/0602/MyAndroidTutorial/.idea/.name
new file mode 100644
index 0000000..5bb7a85
--- /dev/null
+++ b/examples/0602/MyAndroidTutorial/.idea/.name
@@ -0,0 +1 @@
+MyAndroidTutorial
\ No newline at end of file
diff --git a/examples/0602/MyAndroidTutorial/.idea/compiler.xml b/examples/0602/MyAndroidTutorial/.idea/compiler.xml
new file mode 100644
index 0000000..9a8b7e5
--- /dev/null
+++ b/examples/0602/MyAndroidTutorial/.idea/compiler.xml
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/examples/0602/MyAndroidTutorial/.idea/copyright/profiles_settings.xml b/examples/0602/MyAndroidTutorial/.idea/copyright/profiles_settings.xml
new file mode 100644
index 0000000..e7bedf3
--- /dev/null
+++ b/examples/0602/MyAndroidTutorial/.idea/copyright/profiles_settings.xml
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/examples/0602/MyAndroidTutorial/.idea/gradle.xml b/examples/0602/MyAndroidTutorial/.idea/gradle.xml
new file mode 100644
index 0000000..c595ad9
--- /dev/null
+++ b/examples/0602/MyAndroidTutorial/.idea/gradle.xml
@@ -0,0 +1,19 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/examples/0602/MyAndroidTutorial/.idea/misc.xml b/examples/0602/MyAndroidTutorial/.idea/misc.xml
new file mode 100644
index 0000000..b0c0cbc
--- /dev/null
+++ b/examples/0602/MyAndroidTutorial/.idea/misc.xml
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/examples/0602/MyAndroidTutorial/.idea/modules.xml b/examples/0602/MyAndroidTutorial/.idea/modules.xml
new file mode 100644
index 0000000..5edca7e
--- /dev/null
+++ b/examples/0602/MyAndroidTutorial/.idea/modules.xml
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/examples/0602/MyAndroidTutorial/.idea/vcs.xml b/examples/0602/MyAndroidTutorial/.idea/vcs.xml
new file mode 100644
index 0000000..6564d52
--- /dev/null
+++ b/examples/0602/MyAndroidTutorial/.idea/vcs.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/examples/0602/MyAndroidTutorial/MyAndroidTutorial.iml b/examples/0602/MyAndroidTutorial/MyAndroidTutorial.iml
new file mode 100644
index 0000000..62d899f
--- /dev/null
+++ b/examples/0602/MyAndroidTutorial/MyAndroidTutorial.iml
@@ -0,0 +1,19 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/examples/0602/MyAndroidTutorial/app/.gitignore b/examples/0602/MyAndroidTutorial/app/.gitignore
new file mode 100644
index 0000000..796b96d
--- /dev/null
+++ b/examples/0602/MyAndroidTutorial/app/.gitignore
@@ -0,0 +1 @@
+/build
diff --git a/examples/0602/MyAndroidTutorial/app/app.iml b/examples/0602/MyAndroidTutorial/app/app.iml
new file mode 100644
index 0000000..2041401
--- /dev/null
+++ b/examples/0602/MyAndroidTutorial/app/app.iml
@@ -0,0 +1,114 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/examples/0602/MyAndroidTutorial/app/build.gradle b/examples/0602/MyAndroidTutorial/app/build.gradle
new file mode 100644
index 0000000..c5d367d
--- /dev/null
+++ b/examples/0602/MyAndroidTutorial/app/build.gradle
@@ -0,0 +1,27 @@
+apply plugin: 'com.android.application'
+
+android {
+ compileSdkVersion 21
+ buildToolsVersion "21.1.2"
+
+ defaultConfig {
+ applicationId "net.macdidi.myandroidtutorial"
+ minSdkVersion 16
+ 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'
+ compile 'com.android.support:recyclerview-v7:21.0.+'
+}
diff --git a/examples/0602/MyAndroidTutorial/app/proguard-rules.pro b/examples/0602/MyAndroidTutorial/app/proguard-rules.pro
new file mode 100644
index 0000000..b5fa7ec
--- /dev/null
+++ b/examples/0602/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/0602/MyAndroidTutorial/app/src/androidTest/java/net/macdidi/myandroidtutorial/ApplicationTest.java b/examples/0602/MyAndroidTutorial/app/src/androidTest/java/net/macdidi/myandroidtutorial/ApplicationTest.java
new file mode 100644
index 0000000..2cb214e
--- /dev/null
+++ b/examples/0602/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/0602/MyAndroidTutorial/app/src/debug/res/values/google_maps_api.xml b/examples/0602/MyAndroidTutorial/app/src/debug/res/values/google_maps_api.xml
new file mode 100644
index 0000000..7341c8d
--- /dev/null
+++ b/examples/0602/MyAndroidTutorial/app/src/debug/res/values/google_maps_api.xml
@@ -0,0 +1,18 @@
+
+
+
+ AIzaSyCZg9YWlfokPA96VxWGYr6u4C12jL16VhM
+
+
diff --git a/examples/0602/MyAndroidTutorial/app/src/main/AndroidManifest.xml b/examples/0602/MyAndroidTutorial/app/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..50449c2
--- /dev/null
+++ b/examples/0602/MyAndroidTutorial/app/src/main/AndroidManifest.xml
@@ -0,0 +1,128 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/examples/0602/MyAndroidTutorial/app/src/main/java/net/macdidi/myandroidtutorial/AboutActivity.java b/examples/0602/MyAndroidTutorial/app/src/main/java/net/macdidi/myandroidtutorial/AboutActivity.java
new file mode 100644
index 0000000..42dddeb
--- /dev/null
+++ b/examples/0602/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/0602/MyAndroidTutorial/app/src/main/java/net/macdidi/myandroidtutorial/AlarmReceiver.java b/examples/0602/MyAndroidTutorial/app/src/main/java/net/macdidi/myandroidtutorial/AlarmReceiver.java
new file mode 100644
index 0000000..368165c
--- /dev/null
+++ b/examples/0602/MyAndroidTutorial/app/src/main/java/net/macdidi/myandroidtutorial/AlarmReceiver.java
@@ -0,0 +1,86 @@
+package net.macdidi.myandroidtutorial;
+
+import android.app.Notification;
+import android.app.NotificationManager;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.support.v4.app.NotificationCompat;
+
+import java.io.File;
+
+public class AlarmReceiver extends BroadcastReceiver {
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ // 讀取記事標題
+ //String title = intent.getStringExtra("title");
+ // 顯示訊息框
+ //Toast.makeText(context, title, Toast.LENGTH_LONG).show();
+
+ // 讀取記事編號
+ long id = intent.getLongExtra("id", 0);
+
+ if (id != 0) {
+ sendNotify(context, id);
+ }
+ }
+
+ private void sendNotify(Context context, long id) {
+ // 建立資料庫物件
+ ItemDAO itemDAO = new ItemDAO(context.getApplicationContext());
+ // 讀取指定編號的記事物件
+ Item item = itemDAO.get(id);
+
+ // 建立照片檔案物件
+ File file = new File(FileUtil.getExternalStorageDir(FileUtil.APP_DIR),
+ "P" + item.getFileName() + ".jpg");
+
+ // 是否儲存照片檔案
+ boolean isPicture = (item.getFileName() != null &&
+ item.getFileName().length() > 0 &&
+ file.exists());
+
+ // 取得NotificationManager物件
+ NotificationManager nm = (NotificationManager)
+ context.getSystemService(Context.NOTIFICATION_SERVICE);
+
+ // 如果有儲存照片檔案
+ if (isPicture) {
+ // 建立Notification.Builder物件,因為要設定大型圖片樣式
+ // 所以不能使用NotificationCompat.Builder
+ Notification.Builder builder = new Notification.Builder(context);
+ builder.setSmallIcon(android.R.drawable.star_on)
+ .setWhen(System.currentTimeMillis())
+ .setContentTitle(context.getString(R.string.app_name));
+
+ // 建立大型圖片樣式物件
+ Notification.BigPictureStyle bigPictureStyle =
+ new Notification.BigPictureStyle();
+ // 設定圖片與簡介
+ Bitmap bitmap = BitmapFactory.decodeFile(file.getAbsolutePath());
+ bigPictureStyle.bigPicture(bitmap)
+ .setSummaryText(item.getTitle());
+ // 設定樣式為大型圖片
+ builder.setStyle(bigPictureStyle);
+ // 發出通知
+ nm.notify((int)item.getId(), builder.build());
+ }
+ // 如果沒有儲存照片檔案
+ else {
+ // 建立NotificationCompat.Builder物件
+ NotificationCompat.Builder builder =
+ new NotificationCompat.Builder(context);
+ // 設定圖示、時間、內容標題和內容訊息
+ builder.setSmallIcon(android.R.drawable.star_big_on)
+ .setWhen(System.currentTimeMillis())
+ .setContentTitle(context.getString(R.string.app_name))
+ .setContentText(item.getTitle());
+ // 發出通知
+ nm.notify((int)item.getId(), builder.build());
+ }
+ }
+
+}
diff --git a/examples/0602/MyAndroidTutorial/app/src/main/java/net/macdidi/myandroidtutorial/ColorActivity.java b/examples/0602/MyAndroidTutorial/app/src/main/java/net/macdidi/myandroidtutorial/ColorActivity.java
new file mode 100644
index 0000000..182cd55
--- /dev/null
+++ b/examples/0602/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/0602/MyAndroidTutorial/app/src/main/java/net/macdidi/myandroidtutorial/Colors.java b/examples/0602/MyAndroidTutorial/app/src/main/java/net/macdidi/myandroidtutorial/Colors.java
new file mode 100644
index 0000000..1462149
--- /dev/null
+++ b/examples/0602/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/0602/MyAndroidTutorial/app/src/main/java/net/macdidi/myandroidtutorial/FileUtil.java b/examples/0602/MyAndroidTutorial/app/src/main/java/net/macdidi/myandroidtutorial/FileUtil.java
new file mode 100644
index 0000000..1fb41be
--- /dev/null
+++ b/examples/0602/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/0602/MyAndroidTutorial/app/src/main/java/net/macdidi/myandroidtutorial/InitAlarmReceiver.java b/examples/0602/MyAndroidTutorial/app/src/main/java/net/macdidi/myandroidtutorial/InitAlarmReceiver.java
new file mode 100644
index 0000000..959785f
--- /dev/null
+++ b/examples/0602/MyAndroidTutorial/app/src/main/java/net/macdidi/myandroidtutorial/InitAlarmReceiver.java
@@ -0,0 +1,49 @@
+package net.macdidi.myandroidtutorial;
+
+import android.app.AlarmManager;
+import android.app.PendingIntent;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+
+import java.util.Calendar;
+import java.util.List;
+
+public class InitAlarmReceiver extends BroadcastReceiver {
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ // 建立資料庫物件
+ ItemDAO itemDAO = new ItemDAO(context.getApplicationContext());
+ // 讀取資料庫所有記事資料
+ List- items = itemDAO.getAll();
+
+ // 讀取目前時間
+ long current = Calendar.getInstance().getTimeInMillis();
+
+ AlarmManager am = (AlarmManager)
+ context.getSystemService(Context.ALARM_SERVICE);
+
+ for (Item item : items) {
+ long alarm = item.getAlarmDatetime();
+
+ // 如果沒有設定提醒或是提醒已經過期
+ if (alarm == 0 || alarm <= current) {
+ continue;
+ }
+
+ // 設定提醒
+ Intent alarmIntent = new Intent(context, AlarmReceiver.class);
+ //alarmIntent.putExtra("title", item.getTitle());
+
+ // 加入記事編號
+ intent.putExtra("id", item.getId());
+
+ PendingIntent pi = PendingIntent.getBroadcast(
+ context, (int)item.getId(),
+ alarmIntent, PendingIntent.FLAG_ONE_SHOT);
+ am.set(AlarmManager.RTC_WAKEUP, item.getAlarmDatetime(), pi);
+ }
+ }
+
+}
diff --git a/examples/0602/MyAndroidTutorial/app/src/main/java/net/macdidi/myandroidtutorial/Item.java b/examples/0602/MyAndroidTutorial/app/src/main/java/net/macdidi/myandroidtutorial/Item.java
new file mode 100644
index 0000000..e2bc93d
--- /dev/null
+++ b/examples/0602/MyAndroidTutorial/app/src/main/java/net/macdidi/myandroidtutorial/Item.java
@@ -0,0 +1,156 @@
+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;
+
+ // 提醒日期時間
+ private long alarmDatetime;
+
+ 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(fileName);
+ break;
+ case START_LOCATION:
+ // 讀取與設定座標
+ double lat = data.getDoubleExtra("lat", 0.0);
+ double lng = data.getDoubleExtra("lng", 0.0);
+ item.setLatitude(lat);
+ item.setLongitude(lng);
+ 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());
+ }
+
+ 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);
+
+ // 設定儲存的座標
+ intentMap.putExtra("lat", item.getLatitude());
+ intentMap.putExtra("lng", item.getLongitude());
+ intentMap.putExtra("title", item.getTitle());
+ intentMap.putExtra("datetime", item.getLocaleDatetime());
+
+ // 啟動地圖元件
+ startActivityForResult(intentMap, START_LOCATION);
+ break;
+ case R.id.set_alarm:
+ // 設定提醒日期時間
+ processSetAlarm();
+ break;
+ case R.id.select_color:
+ // 啟動設定顏色的Activity元件
+ startActivityForResult(
+ new Intent(this, ColorActivity.class), START_COLOR);
+ break;
+ }
+
+ }
+
+ // 設定提醒日期時間
+ private void processSetAlarm() {
+ Calendar calendar = Calendar.getInstance();
+
+ if (item.getAlarmDatetime() != 0) {
+ // 設定為已經儲存的提醒日期時間
+ calendar.setTimeInMillis(item.getAlarmDatetime());
+ }
+
+ // 讀取年、月、日、時、分
+ int year = calendar.get(Calendar.YEAR);
+ int month = calendar.get(Calendar.MONTH);
+ int day = calendar.get(Calendar.DAY_OF_MONTH);
+ int hour = calendar.get(Calendar.HOUR_OF_DAY);
+ int minute = calendar.get(Calendar.MINUTE);
+
+ // 儲存設定的提醒日期時間
+ final Calendar alarm = Calendar.getInstance();
+
+ // 設定提醒時間
+ TimePickerDialog.OnTimeSetListener timeSetListener =
+ new TimePickerDialog.OnTimeSetListener() {
+ @Override
+ public void onTimeSet(TimePicker view,
+ int hourOfDay, int minute) {
+ alarm.set(Calendar.HOUR_OF_DAY, hourOfDay);
+ alarm.set(Calendar.MINUTE, minute);
+
+ item.setAlarmDatetime(alarm.getTimeInMillis());
+ }
+ };
+
+ // 選擇時間對話框
+ final TimePickerDialog tpd = new TimePickerDialog(
+ this, timeSetListener, hour, minute, true);
+
+ // 設定提醒日期
+ DatePickerDialog.OnDateSetListener dateSetListener =
+ new DatePickerDialog.OnDateSetListener() {
+ @Override
+ public void onDateSet(DatePicker view,
+ int year,
+ int monthOfYear,
+ int dayOfMonth) {
+ alarm.set(Calendar.YEAR, year);
+ alarm.set(Calendar.MONTH, monthOfYear);
+ alarm.set(Calendar.DAY_OF_MONTH, dayOfMonth);
+
+ // 繼續選擇提醒時間
+ tpd.show();
+ }
+ };
+
+ // 建立與顯示選擇日期對話框
+ final DatePickerDialog dpd = new DatePickerDialog(
+ this, dateSetListener, year, month, day);
+ dpd.show();
+ }
+
+ 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/0602/MyAndroidTutorial/app/src/main/java/net/macdidi/myandroidtutorial/ItemAdapter.java b/examples/0602/MyAndroidTutorial/app/src/main/java/net/macdidi/myandroidtutorial/ItemAdapter.java
new file mode 100644
index 0000000..85b40ba
--- /dev/null
+++ b/examples/0602/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/0602/MyAndroidTutorial/app/src/main/java/net/macdidi/myandroidtutorial/ItemAdapterRV.java b/examples/0602/MyAndroidTutorial/app/src/main/java/net/macdidi/myandroidtutorial/ItemAdapterRV.java
new file mode 100644
index 0000000..454df42
--- /dev/null
+++ b/examples/0602/MyAndroidTutorial/app/src/main/java/net/macdidi/myandroidtutorial/ItemAdapterRV.java
@@ -0,0 +1,79 @@
+package net.macdidi.myandroidtutorial;
+
+import android.graphics.drawable.GradientDrawable;
+import android.support.v7.widget.RecyclerView;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ImageView;
+import android.widget.RelativeLayout;
+import android.widget.TextView;
+
+import java.util.List;
+
+public class ItemAdapterRV extends RecyclerView.Adapter{
+
+ // 包裝的記事資料
+ private List
- items;
+
+ public ItemAdapterRV(List
- items) {
+ this.items = items;
+ }
+
+ @Override
+ public ItemAdapterRV.ViewHolder onCreateViewHolder(
+ ViewGroup parent, int viewType) {
+ View v = LayoutInflater.from(parent.getContext()).inflate(
+ R.layout.single_item, parent, false);
+ ViewHolder viewHolder = new ViewHolder(v);
+
+ return viewHolder;
+ }
+
+ @Override
+ public void onBindViewHolder(final ViewHolder holder, int position) {
+ final Item item = items.get(position);
+
+ // 設定記事顏色
+ GradientDrawable background = (GradientDrawable)
+ holder.typeColor.getBackground();
+ background.setColor(item.getColor().parseColor());
+
+ // 設定標題與日期時間
+ holder.titleView.setText(item.getTitle());
+ holder.dateView.setText(item.getLocaleDatetime());
+
+ // 設定是否已選擇
+ holder.selectedItem.setVisibility(
+ item.isSelected() ? View.VISIBLE : View.INVISIBLE);
+ }
+
+ @Override
+ public int getItemCount() {
+ return items.size();
+ }
+
+ // 一定要使用ViewHolder包裝畫面元件
+ public class ViewHolder extends RecyclerView.ViewHolder {
+
+ protected RelativeLayout typeColor;
+ protected ImageView selectedItem;
+ protected TextView titleView;
+ protected TextView dateView;
+
+ protected View rootView;
+
+ public ViewHolder(View view) {
+ super(view);
+
+ typeColor = (RelativeLayout) itemView.findViewById(R.id.type_color);
+ selectedItem = (ImageView) itemView.findViewById(R.id.selected_item);
+ titleView = (TextView) itemView.findViewById(R.id.title_text);
+ dateView = (TextView) itemView.findViewById(R.id.date_text);
+
+ rootView = view;
+ }
+
+ }
+
+}
diff --git a/examples/0602/MyAndroidTutorial/app/src/main/java/net/macdidi/myandroidtutorial/ItemAppWidget.java b/examples/0602/MyAndroidTutorial/app/src/main/java/net/macdidi/myandroidtutorial/ItemAppWidget.java
new file mode 100644
index 0000000..9301f57
--- /dev/null
+++ b/examples/0602/MyAndroidTutorial/app/src/main/java/net/macdidi/myandroidtutorial/ItemAppWidget.java
@@ -0,0 +1,72 @@
+package net.macdidi.myandroidtutorial;
+
+import android.app.PendingIntent;
+import android.appwidget.AppWidgetManager;
+import android.appwidget.AppWidgetProvider;
+import android.content.Context;
+import android.content.Intent;
+import android.widget.RemoteViews;
+
+
+public class ItemAppWidget extends AppWidgetProvider {
+
+ @Override
+ public void onUpdate(Context context,
+ AppWidgetManager appWidgetManager,
+ int[] appWidgetIds) {
+ final int N = appWidgetIds.length;
+
+ for (int i = 0; i < N; i++) {
+ updateAppWidget(context, appWidgetManager, appWidgetIds[i]);
+ }
+ }
+
+ @Override
+ public void onDeleted(Context context, int[] appWidgetIds) {
+ final int N = appWidgetIds.length;
+ for (int i = 0; i < N; i++) {
+ // 刪除小工具已經儲存的記事編號
+ ItemAppWidgetConfigureActivity.deleteItemPref(
+ context, appWidgetIds[i]);
+ }
+ }
+
+ @Override
+ public void onEnabled(Context context) {
+
+ }
+
+ @Override
+ public void onDisabled(Context context) {
+
+ }
+
+ static void updateAppWidget(Context context,
+ AppWidgetManager appWidgetManager,
+ int appWidgetId) {
+ // 讀取小工具儲存的記事編號
+ long id = ItemAppWidgetConfigureActivity.loadItemPref(
+ context, appWidgetId);
+ // 建立小工具畫面元件
+ RemoteViews views = new RemoteViews(
+ context.getPackageName(), R.layout.item_app_widget);
+ // 讀取指定編號的記事物件
+ ItemDAO itemDAO = new ItemDAO(context.getApplicationContext());
+ Item item = itemDAO.get(id);
+
+ // 設定小工具畫面顯示記事標題
+ views.setTextViewText(R.id.appwidget_text,
+ item != null ? item.getTitle() : "NA");
+
+ // 點選小工具畫面的記事標題後,啟動記事應用程式
+ Intent intent = new Intent(context, MainActivity.class);
+ PendingIntent pending = PendingIntent.getActivity(
+ context, 0, intent, 0);
+ views.setOnClickPendingIntent(R.id.appwidget_text, pending);
+
+ // 更新小工具
+ appWidgetManager.updateAppWidget(appWidgetId, views);
+ }
+}
+
+
diff --git a/examples/0602/MyAndroidTutorial/app/src/main/java/net/macdidi/myandroidtutorial/ItemAppWidgetConfigureActivity.java b/examples/0602/MyAndroidTutorial/app/src/main/java/net/macdidi/myandroidtutorial/ItemAppWidgetConfigureActivity.java
new file mode 100644
index 0000000..f83be71
--- /dev/null
+++ b/examples/0602/MyAndroidTutorial/app/src/main/java/net/macdidi/myandroidtutorial/ItemAppWidgetConfigureActivity.java
@@ -0,0 +1,120 @@
+package net.macdidi.myandroidtutorial;
+
+import android.app.Activity;
+import android.appwidget.AppWidgetManager;
+import android.content.Context;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.os.Bundle;
+import android.view.View;
+import android.widget.AdapterView;
+import android.widget.ListView;
+
+import java.util.List;
+
+
+public class ItemAppWidgetConfigureActivity extends Activity {
+
+ int mAppWidgetId = AppWidgetManager.INVALID_APPWIDGET_ID;
+
+ private static final String PREFS_NAME =
+ "net.macdidi.myandroidtutorial.ItemAppWidget";
+ private static final String PREF_PREFIX_KEY = "appwidget_";
+
+ // 選擇小工具使用的記事項目
+ private ListView item_list;
+ private ItemAdapter itemAdapter;
+ private List
- items;
+ private ItemDAO itemDAO;
+
+ public ItemAppWidgetConfigureActivity() {
+ super();
+ }
+
+ @Override
+ public void onCreate(Bundle icicle) {
+ super.onCreate(icicle);
+
+ setResult(RESULT_CANCELED);
+
+ // 改為使用應用程式主畫面
+ setContentView(R.layout.activity_main);
+
+ // 建立與設定選擇小工具使用的記事項目需要的物件
+ item_list = (ListView)findViewById(R.id.item_list);
+ itemDAO = new ItemDAO(getApplicationContext());
+ items = itemDAO.getAll();
+ itemAdapter = new ItemAdapter(this, R.layout.single_item, items);
+ item_list.setAdapter(itemAdapter);
+ item_list.setOnItemClickListener(itemListener);
+
+ Intent intent = getIntent();
+ Bundle extras = intent.getExtras();
+
+ if (extras != null) {
+ mAppWidgetId = extras.getInt(
+ AppWidgetManager.EXTRA_APPWIDGET_ID,
+ AppWidgetManager.INVALID_APPWIDGET_ID);
+ }
+
+ if (mAppWidgetId == AppWidgetManager.INVALID_APPWIDGET_ID) {
+ finish();
+ return;
+ }
+
+ }
+
+ // 選擇記事項目
+ AdapterView.OnItemClickListener itemListener =
+ new AdapterView.OnItemClickListener() {
+ @Override
+ public void onItemClick(AdapterView> parent, View view,
+ int position, long id) {
+ final Context context = ItemAppWidgetConfigureActivity.this;
+
+ // 讀取與儲存選擇的記事物件
+ Item item = itemAdapter.getItem(position);
+ saveItemPref(context, mAppWidgetId, item.getId());
+
+ AppWidgetManager appWidgetManager =
+ AppWidgetManager.getInstance(context);
+ ItemAppWidget.updateAppWidget(
+ context, appWidgetManager, mAppWidgetId);
+ Intent resultValue = new Intent();
+ resultValue.putExtra(
+ AppWidgetManager.EXTRA_APPWIDGET_ID, mAppWidgetId);
+ setResult(RESULT_OK, resultValue);
+
+ finish();
+ }
+ };
+
+ // 儲存選擇的記事編號
+ static void saveItemPref(Context context, int appWidgetId, long id) {
+ SharedPreferences.Editor prefs =
+ context.getSharedPreferences(PREFS_NAME, 0).edit();
+ prefs.putLong(PREF_PREFIX_KEY + appWidgetId, id);
+ prefs.commit();
+ }
+
+ // 讀取記事編號
+ static long loadItemPref(Context context, int appWidgetId) {
+ SharedPreferences prefs =
+ context.getSharedPreferences(PREFS_NAME, 0);
+ long idValue = prefs.getLong(PREF_PREFIX_KEY + appWidgetId, 0);
+
+ return idValue;
+ }
+
+ // 刪除記事編號
+ static void deleteItemPref(Context context, int appWidgetId) {
+ SharedPreferences.Editor prefs =
+ context.getSharedPreferences(PREFS_NAME, 0).edit();
+ prefs.remove(PREF_PREFIX_KEY + appWidgetId);
+ prefs.commit();
+ }
+
+}
+
+
+
diff --git a/examples/0602/MyAndroidTutorial/app/src/main/java/net/macdidi/myandroidtutorial/ItemDAO.java b/examples/0602/MyAndroidTutorial/app/src/main/java/net/macdidi/myandroidtutorial/ItemDAO.java
new file mode 100644
index 0000000..b3c7b49
--- /dev/null
+++ b/examples/0602/MyAndroidTutorial/app/src/main/java/net/macdidi/myandroidtutorial/ItemDAO.java
@@ -0,0 +1,214 @@
+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";
+
+ // 提醒日期時間
+ public static final String ALARMDATETIME_COLUMN = "alarmdatetime";
+
+ // 使用上面宣告的變數建立表格的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, " +
+ ALARMDATETIME_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());
+
+ // 提醒日期時間
+ cv.put(ALARMDATETIME_COLUMN, item.getAlarmDatetime());
+
+ // 新增一筆資料並取得編號
+ // 第一個參數是表格名稱
+ // 第二個參數是沒有指定欄位值的預設值
+ // 第三個參數是包裝新增資料的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());
+
+ // 提醒日期時間
+ cv.put(ALARMDATETIME_COLUMN, item.getAlarmDatetime());
+
+ // 設定修改資料的條件為編號
+ // 格式為「欄位名稱=資料」
+ 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));
+
+ // 提醒日期時間
+ result.setAlarmDatetime(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/0602/MyAndroidTutorial/app/src/main/java/net/macdidi/myandroidtutorial/MainActivity.java b/examples/0602/MyAndroidTutorial/app/src/main/java/net/macdidi/myandroidtutorial/MainActivity.java
new file mode 100644
index 0000000..391eb24
--- /dev/null
+++ b/examples/0602/MyAndroidTutorial/app/src/main/java/net/macdidi/myandroidtutorial/MainActivity.java
@@ -0,0 +1,333 @@
+package net.macdidi.myandroidtutorial;
+
+import android.app.Activity;
+import android.app.ActivityOptions;
+import android.app.AlarmManager;
+import android.app.AlertDialog;
+import android.app.PendingIntent;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.os.Build;
+import android.os.Bundle;
+import android.support.v7.widget.LinearLayoutManager;
+import android.support.v7.widget.RecyclerView;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
+import android.view.View;
+import android.widget.TextView;
+
+import java.util.List;
+
+public class MainActivity extends Activity {
+
+ // 移除原來的ListView元件
+ //private ListView item_list;
+
+ // 加入下列需要的元件
+ private RecyclerView item_list;
+ private RecyclerView.Adapter itemAdapter;
+ private RecyclerView.LayoutManager rvLayoutManager;
+
+ private TextView show_app_name;
+
+ // 移除原來的ItemAdapter
+ //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();
+
+ // 移除原來ListView元件執行的工作
+ //itemAdapter = new ItemAdapter(this, R.layout.single_item, items);
+ //item_list.setAdapter(itemAdapter);
+
+ // 執行RecyclerView元件的設定
+ item_list.setHasFixedSize(true);
+ rvLayoutManager = new LinearLayoutManager(this);
+ item_list.setLayoutManager(rvLayoutManager);
+
+ // 在這裡執行註冊監聽事件的工作
+ processControllers();
+ }
+
+ @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");
+
+ boolean updateAlarm = false;
+
+ 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) {
+ Item ori = itemDAO.get(item.getId());
+ updateAlarm = (item.getAlarmDatetime() != ori.getAlarmDatetime());
+
+ itemDAO.update(item);
+
+ items.set(position, item);
+ itemAdapter.notifyDataSetChanged();
+ }
+ }
+
+ if (item.getAlarmDatetime() != 0 && updateAlarm) {
+ Intent intent = new Intent(this, AlarmReceiver.class);
+ intent.putExtra("id", item.getId());
+ PendingIntent pi = PendingIntent.getBroadcast(
+ this, (int)item.getId(),
+ intent, PendingIntent.FLAG_ONE_SHOT);
+ AlarmManager am = (AlarmManager)
+ getSystemService(Context.ALARM_SERVICE);
+ am.set(AlarmManager.RTC_WAKEUP, item.getAlarmDatetime(), pi);
+ }
+ }
+ }
+
+ private void processViews() {
+ item_list = (RecyclerView) findViewById(R.id.item_list);
+ show_app_name = (TextView) findViewById(R.id.show_app_name);
+ }
+
+ private void processControllers() {
+
+ itemAdapter = new ItemAdapterRV(items) {
+ @Override
+ public void onBindViewHolder(final ViewHolder holder, final int position) {
+ super.onBindViewHolder(holder, position);
+
+ // 建立與註冊項目點擊監聽物件
+ holder.rootView.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ // 讀取選擇的記事物件
+ Item item = items.get(position);
+
+ // 如果已經有勾選的項目
+ if (selectedCount > 0) {
+ // 處理是否顯示已選擇項目
+ processMenu(item);
+ // 重新設定記事項目
+ items.set(position, item);
+ } else {
+ Intent intent = new Intent(
+ "net.macdidi.myandroidtutorial.EDIT_ITEM");
+
+ // 設定記事編號與記事物件
+ intent.putExtra("position", position);
+ intent.putExtra("net.macdidi.myandroidtutorial.Item", item);
+
+ // 依照版本啟動Acvitity元件
+ startActivityForVersion(intent, 1);
+ }
+ }
+ });
+
+ // 建立與註冊項目長按監聽物件
+ holder.rootView.setOnLongClickListener(new View.OnLongClickListener() {
+ @Override
+ public boolean onLongClick(View v) {
+ // 讀取選擇的記事物件
+ Item item = items.get(position);
+ // 處理是否顯示已選擇項目
+ processMenu(item);
+ // 重新設定記事項目
+ items.set(position, item);
+ return true;
+ }
+ });
+ }
+ };
+
+ item_list.setAdapter(itemAdapter);
+ }
+
+ // 處理是否顯示已選擇項目
+ 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);
+
+ // 通知項目勾選狀態改變
+ itemAdapter.notifyDataSetChanged();
+ }
+
+ // 載入選單資源
+ @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:
+ Intent intent = new Intent("net.macdidi.myandroidtutorial.ADD_ITEM");
+ startActivityForVersion(intent, 0);
+ break;
+ // 取消所有已勾選的項目
+ case R.id.revert_item:
+ for (int i = 0; i < items.size(); i++) {
+ Item ri = items.get(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 = items.size() - 1;
+
+ while (index > -1) {
+ // 改為使用items物件
+ Item item = items.get(index);
+
+ if (item.isSelected()) {
+ // 改為使用items物件
+ items.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;
+ }
+
+ }
+
+ public void aboutApp(View view) {
+ Intent intent = new Intent(this, AboutActivity.class);
+ startActivity(intent);
+ }
+
+ public void clickPreferences(MenuItem item) {
+ // 依照版本啟動Acvitity元件
+ startActivityForVersion(new Intent(this, PrefActivity.class));
+ }
+
+ private void startActivityForVersion(Intent intent, int requestCode) {
+ // 如果裝置的版本是LOLLIPOP
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
+ // 加入畫面轉換設定
+ startActivityForResult(intent, requestCode,
+ ActivityOptions.makeSceneTransitionAnimation(
+ MainActivity.this).toBundle());
+ }
+ else {
+ startActivityForResult(intent, requestCode);
+ }
+ }
+
+ private void startActivityForVersion(Intent intent) {
+ // 如果裝置的版本是LOLLIPOP
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
+ // 加入畫面轉換設定
+ startActivity(intent,
+ ActivityOptions.makeSceneTransitionAnimation(
+ MainActivity.this).toBundle());
+ }
+ else {
+ startActivity(intent);
+ }
+ }
+
+}
+
+
diff --git a/examples/0602/MyAndroidTutorial/app/src/main/java/net/macdidi/myandroidtutorial/MapsActivity.java b/examples/0602/MyAndroidTutorial/app/src/main/java/net/macdidi/myandroidtutorial/MapsActivity.java
new file mode 100644
index 0000000..abab911
--- /dev/null
+++ b/examples/0602/MyAndroidTutorial/app/src/main/java/net/macdidi/myandroidtutorial/MapsActivity.java
@@ -0,0 +1,338 @@
+package net.macdidi.myandroidtutorial;
+
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.location.Location;
+import android.os.Bundle;
+import android.support.v4.app.FragmentActivity;
+import android.widget.Toast;
+
+import com.google.android.gms.common.ConnectionResult;
+import com.google.android.gms.common.api.GoogleApiClient;
+import com.google.android.gms.common.api.GoogleApiClient.ConnectionCallbacks;
+import com.google.android.gms.common.api.GoogleApiClient.OnConnectionFailedListener;
+
+import com.google.android.gms.location.LocationListener;
+import com.google.android.gms.location.LocationRequest;
+import com.google.android.gms.location.LocationServices;
+
+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.Marker;
+import com.google.android.gms.maps.model.MarkerOptions;
+
+public class MapsActivity extends FragmentActivity
+ implements ConnectionCallbacks,
+ OnConnectionFailedListener,
+ LocationListener {
+
+ private GoogleMap mMap;
+
+ // Google API用戶端物件
+ private GoogleApiClient googleApiClient;
+
+ // Location請求物件
+ private LocationRequest locationRequest;
+
+ // 記錄目前最新的位置
+ private Location currentLocation;
+
+ // 顯示目前與儲存位置的標記物件
+ private Marker currentMarker, itemMarker;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_maps);
+ setUpMapIfNeeded();
+
+ // 建立Google API用戶端物件
+ configGoogleApiClient();
+
+ // 建立Location請求物件
+ configLocationRequest();
+
+ // 讀取記事儲存的座標
+ Intent intent = getIntent();
+ double lat = intent.getDoubleExtra("lat", 0.0);
+ double lng = intent.getDoubleExtra("lng", 0.0);
+
+ // 如果記事已經儲存座標
+ if (lat != 0.0 && lng != 0.0) {
+ // 建立座標物件
+ LatLng itemPlace = new LatLng(lat, lng);
+ // 加入地圖標記
+ addMarker(itemPlace, intent.getStringExtra("title"),
+ intent.getStringExtra("datetime"));
+ // 移動地圖
+ moveMap(itemPlace);
+ }
+ else {
+ // 連線到Google API用戶端
+ if (!googleApiClient.isConnected()) {
+ googleApiClient.connect();
+ }
+ }
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+ setUpMapIfNeeded();
+
+ // 連線到Google API用戶端
+ if (!googleApiClient.isConnected() && currentMarker != null) {
+ googleApiClient.connect();
+ }
+ }
+
+ @Override
+ protected void onPause() {
+ super.onPause();
+
+ // 移除位置請求服務
+ if (googleApiClient.isConnected()) {
+ LocationServices.FusedLocationApi.removeLocationUpdates(
+ googleApiClient, this);
+ }
+ }
+
+ @Override
+ protected void onStop() {
+ super.onStop();
+
+ // 移除Google API用戶端連線
+ if (googleApiClient.isConnected()) {
+ googleApiClient.disconnect();
+ }
+ }
+
+ // 建立Google API用戶端物件
+ private synchronized void configGoogleApiClient() {
+ googleApiClient = new GoogleApiClient.Builder(this)
+ .addConnectionCallbacks(this)
+ .addOnConnectionFailedListener(this)
+ .addApi(LocationServices.API)
+ .build();
+ }
+
+ // 建立Location請求物件
+ private void configLocationRequest() {
+ locationRequest = new LocationRequest();
+ // 設定讀取位置資訊的間隔時間為一秒(1000ms)
+ locationRequest.setInterval(1000);
+ // 設定讀取位置資訊最快的間隔時間為一秒(1000ms)
+ locationRequest.setFastestInterval(1000);
+ // 設定優先讀取高精確度的位置資訊(GPS)
+ locationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
+ }
+
+ private void setUpMapIfNeeded() {
+ if (mMap == null) {
+ mMap = ((SupportMapFragment) getSupportFragmentManager().
+ findFragmentById(R.id.map)).getMap();
+
+ if (mMap != null) {
+ // 移除地圖設定
+ //setUpMap();
+ processController();
+ }
+ }
+ }
+
+ // 移除地圖設定方法
+ private void setUpMap() {
+ // 建立位置的座標物件
+ LatLng place = new LatLng(25.033408, 121.564099);
+ // 移動地圖
+ moveMap(place);
+
+ // 加入地圖標記
+ addMarker(place, "Hello!", " Google Maps v2!");
+ }
+
+ private void processController() {
+ // 對話框按鈕事件
+ final DialogInterface.OnClickListener listener =
+ new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ switch (which) {
+ // 更新位置資訊
+ case DialogInterface.BUTTON_POSITIVE:
+ // 連線到Google API用戶端
+ if (!googleApiClient.isConnected()) {
+ googleApiClient.connect();
+ }
+ break;
+ // 清除位置資訊
+ case DialogInterface.BUTTON_NEUTRAL:
+ Intent result = new Intent();
+ result.putExtra("lat", 0);
+ result.putExtra("lng", 0);
+ setResult(Activity.RESULT_OK, result);
+ finish();
+ break;
+ // 取消
+ case DialogInterface.BUTTON_NEGATIVE:
+ break;
+ }
+ }
+ };
+
+ // 標記訊息框點擊事件
+ mMap.setOnInfoWindowClickListener(new GoogleMap.OnInfoWindowClickListener() {
+ @Override
+ public void onInfoWindowClick(Marker marker) {
+ // 如果是記事儲存的標記
+ if (marker.equals(itemMarker)) {
+ AlertDialog.Builder ab = new AlertDialog.Builder(MapsActivity.this);
+
+ ab.setTitle(R.string.title_update_location)
+ .setMessage(R.string.message_update_location)
+ .setCancelable(true);
+
+ ab.setPositiveButton(R.string.update, listener);
+ ab.setNeutralButton(R.string.clear, listener);
+ ab.setNegativeButton(android.R.string.cancel, listener);
+
+ ab.show();
+ }
+ }
+ });
+
+ // 標記點擊事件
+ mMap.setOnMarkerClickListener(new GoogleMap.OnMarkerClickListener() {
+ @Override
+ public boolean onMarkerClick(Marker marker) {
+ // 如果是目前位置標記
+ if (marker.equals(currentMarker)) {
+ AlertDialog.Builder ab = new AlertDialog.Builder(MapsActivity.this);
+
+ ab.setTitle(R.string.title_current_location)
+ .setMessage(R.string.message_current_location)
+ .setCancelable(true);
+
+ ab.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ Intent result = new Intent();
+ result.putExtra("lat", currentLocation.getLatitude());
+ result.putExtra("lng", currentLocation.getLongitude());
+ setResult(Activity.RESULT_OK, result);
+ finish();
+ }
+ });
+ ab.setNegativeButton(android.R.string.cancel, null);
+
+ ab.show();
+
+ return true;
+ }
+
+ return false;
+ }
+ });
+ }
+
+ // 移動地圖到參數指定的位置
+ private void moveMap(LatLng place) {
+ // 建立地圖攝影機的位置物件
+ CameraPosition cameraPosition =
+ new CameraPosition.Builder()
+ .target(place)
+ .zoom(17)
+ .build();
+
+ // 使用動畫的效果移動地圖
+ mMap.animateCamera(CameraUpdateFactory.newCameraPosition(cameraPosition),
+ new GoogleMap.CancelableCallback() {
+ @Override
+ public void onFinish() {
+ if (itemMarker != null) {
+ itemMarker.showInfoWindow();
+ }
+ }
+
+ @Override
+ public void onCancel() {
+
+ }
+ });
+ }
+
+ // 在地圖加入指定位置與標題的標記
+ 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);
+
+ // 加入並設定記事儲存的位置標記
+ itemMarker = mMap.addMarker(markerOptions);
+ }
+
+ // ConnectionCallbacks
+ @Override
+ public void onConnected(Bundle bundle) {
+ // 已經連線到Google Services
+ // 啟動位置更新服務
+ // 位置資訊更新的時候,應用程式會自動呼叫LocationListener.onLocationChanged
+ LocationServices.FusedLocationApi.requestLocationUpdates(
+ googleApiClient, locationRequest, MapsActivity.this);
+ }
+
+ // ConnectionCallbacks
+ @Override
+ public void onConnectionSuspended(int i) {
+ // Google Services連線中斷
+ // int參數是連線中斷的代號
+ }
+
+ // OnConnectionFailedListener
+ @Override
+ public void onConnectionFailed(ConnectionResult connectionResult) {
+ // Google Services連線失敗
+ // ConnectionResult參數是連線失敗的資訊
+ int errorCode = connectionResult.getErrorCode();
+
+ // 裝置沒有安裝Google Play服務
+ if (errorCode == ConnectionResult.SERVICE_MISSING) {
+ Toast.makeText(this, R.string.google_play_service_missing,
+ Toast.LENGTH_LONG).show();
+ }
+ }
+
+ // LocationListener
+ @Override
+ public void onLocationChanged(Location location) {
+ // 位置改變
+ // Location參數是目前的位置
+ currentLocation = location;
+ LatLng latLng = new LatLng(
+ location.getLatitude(), location.getLongitude());
+
+ // 設定目前位置的標記
+ if (currentMarker == null) {
+ currentMarker = mMap.addMarker(new MarkerOptions().position(latLng));
+ }
+ else {
+ currentMarker.setPosition(latLng);
+ }
+
+ // 移動地圖到目前的位置
+ moveMap(latLng);
+ }
+
+}
diff --git a/examples/0602/MyAndroidTutorial/app/src/main/java/net/macdidi/myandroidtutorial/MyDBHelper.java b/examples/0602/MyAndroidTutorial/app/src/main/java/net/macdidi/myandroidtutorial/MyDBHelper.java
new file mode 100644
index 0000000..9350577
--- /dev/null
+++ b/examples/0602/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 = 3;
+ // 資料庫物件,固定的欄位變數
+ 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/0602/MyAndroidTutorial/app/src/main/java/net/macdidi/myandroidtutorial/PlayActivity.java b/examples/0602/MyAndroidTutorial/app/src/main/java/net/macdidi/myandroidtutorial/PlayActivity.java
new file mode 100644
index 0000000..5cfb523
--- /dev/null
+++ b/examples/0602/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/0602/MyAndroidTutorial/app/src/main/java/net/macdidi/myandroidtutorial/PrefActivity.java b/examples/0602/MyAndroidTutorial/app/src/main/java/net/macdidi/myandroidtutorial/PrefActivity.java
new file mode 100644
index 0000000..b2dcc4a
--- /dev/null
+++ b/examples/0602/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/0602/MyAndroidTutorial/app/src/main/java/net/macdidi/myandroidtutorial/RecordActivity.java b/examples/0602/MyAndroidTutorial/app/src/main/java/net/macdidi/myandroidtutorial/RecordActivity.java
new file mode 100644
index 0000000..62b5e93
--- /dev/null
+++ b/examples/0602/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/0602/MyAndroidTutorial/app/src/main/res/drawable-hdpi/ic_launcher.png b/examples/0602/MyAndroidTutorial/app/src/main/res/drawable-hdpi/ic_launcher.png
new file mode 100644
index 0000000..96a442e
Binary files /dev/null and b/examples/0602/MyAndroidTutorial/app/src/main/res/drawable-hdpi/ic_launcher.png differ
diff --git a/examples/0602/MyAndroidTutorial/app/src/main/res/drawable-mdpi/ic_launcher.png b/examples/0602/MyAndroidTutorial/app/src/main/res/drawable-mdpi/ic_launcher.png
new file mode 100644
index 0000000..359047d
Binary files /dev/null and b/examples/0602/MyAndroidTutorial/app/src/main/res/drawable-mdpi/ic_launcher.png differ
diff --git a/examples/0602/MyAndroidTutorial/app/src/main/res/drawable-nodpi/example_appwidget_preview.png b/examples/0602/MyAndroidTutorial/app/src/main/res/drawable-nodpi/example_appwidget_preview.png
new file mode 100644
index 0000000..894b069
Binary files /dev/null and b/examples/0602/MyAndroidTutorial/app/src/main/res/drawable-nodpi/example_appwidget_preview.png differ
diff --git a/examples/0602/MyAndroidTutorial/app/src/main/res/drawable-xhdpi/ic_launcher.png b/examples/0602/MyAndroidTutorial/app/src/main/res/drawable-xhdpi/ic_launcher.png
new file mode 100644
index 0000000..71c6d76
Binary files /dev/null and b/examples/0602/MyAndroidTutorial/app/src/main/res/drawable-xhdpi/ic_launcher.png differ
diff --git a/examples/0602/MyAndroidTutorial/app/src/main/res/drawable-xxhdpi/ic_launcher.png b/examples/0602/MyAndroidTutorial/app/src/main/res/drawable-xxhdpi/ic_launcher.png
new file mode 100644
index 0000000..4df1894
Binary files /dev/null and b/examples/0602/MyAndroidTutorial/app/src/main/res/drawable-xxhdpi/ic_launcher.png differ
diff --git a/examples/0602/MyAndroidTutorial/app/src/main/res/drawable/alarm_icon.png b/examples/0602/MyAndroidTutorial/app/src/main/res/drawable/alarm_icon.png
new file mode 100644
index 0000000..c6cac88
Binary files /dev/null and b/examples/0602/MyAndroidTutorial/app/src/main/res/drawable/alarm_icon.png differ
diff --git a/examples/0602/MyAndroidTutorial/app/src/main/res/drawable/item_drawable.xml b/examples/0602/MyAndroidTutorial/app/src/main/res/drawable/item_drawable.xml
new file mode 100644
index 0000000..37607e2
--- /dev/null
+++ b/examples/0602/MyAndroidTutorial/app/src/main/res/drawable/item_drawable.xml
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/examples/0602/MyAndroidTutorial/app/src/main/res/drawable/location_icon.png b/examples/0602/MyAndroidTutorial/app/src/main/res/drawable/location_icon.png
new file mode 100644
index 0000000..4c3c514
Binary files /dev/null and b/examples/0602/MyAndroidTutorial/app/src/main/res/drawable/location_icon.png differ
diff --git a/examples/0602/MyAndroidTutorial/app/src/main/res/drawable/pause_icon.png b/examples/0602/MyAndroidTutorial/app/src/main/res/drawable/pause_icon.png
new file mode 100755
index 0000000..a5aee6f
Binary files /dev/null and b/examples/0602/MyAndroidTutorial/app/src/main/res/drawable/pause_icon.png differ
diff --git a/examples/0602/MyAndroidTutorial/app/src/main/res/drawable/play_icon.png b/examples/0602/MyAndroidTutorial/app/src/main/res/drawable/play_icon.png
new file mode 100755
index 0000000..6a40cd5
Binary files /dev/null and b/examples/0602/MyAndroidTutorial/app/src/main/res/drawable/play_icon.png differ
diff --git a/examples/0602/MyAndroidTutorial/app/src/main/res/drawable/record_dark_icon.png b/examples/0602/MyAndroidTutorial/app/src/main/res/drawable/record_dark_icon.png
new file mode 100755
index 0000000..bcf83ca
Binary files /dev/null and b/examples/0602/MyAndroidTutorial/app/src/main/res/drawable/record_dark_icon.png differ
diff --git a/examples/0602/MyAndroidTutorial/app/src/main/res/drawable/record_red_icon.png b/examples/0602/MyAndroidTutorial/app/src/main/res/drawable/record_red_icon.png
new file mode 100755
index 0000000..2b44af0
Binary files /dev/null and b/examples/0602/MyAndroidTutorial/app/src/main/res/drawable/record_red_icon.png differ
diff --git a/examples/0602/MyAndroidTutorial/app/src/main/res/drawable/record_sound_icon.png b/examples/0602/MyAndroidTutorial/app/src/main/res/drawable/record_sound_icon.png
new file mode 100755
index 0000000..a1382ac
Binary files /dev/null and b/examples/0602/MyAndroidTutorial/app/src/main/res/drawable/record_sound_icon.png differ
diff --git a/examples/0602/MyAndroidTutorial/app/src/main/res/drawable/retangle_drawable.xml b/examples/0602/MyAndroidTutorial/app/src/main/res/drawable/retangle_drawable.xml
new file mode 100644
index 0000000..51d1e84
--- /dev/null
+++ b/examples/0602/MyAndroidTutorial/app/src/main/res/drawable/retangle_drawable.xml
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/examples/0602/MyAndroidTutorial/app/src/main/res/drawable/select_color_icon.png b/examples/0602/MyAndroidTutorial/app/src/main/res/drawable/select_color_icon.png
new file mode 100644
index 0000000..8567d5e
Binary files /dev/null and b/examples/0602/MyAndroidTutorial/app/src/main/res/drawable/select_color_icon.png differ
diff --git a/examples/0602/MyAndroidTutorial/app/src/main/res/drawable/selected_icon.png b/examples/0602/MyAndroidTutorial/app/src/main/res/drawable/selected_icon.png
new file mode 100755
index 0000000..b891571
Binary files /dev/null and b/examples/0602/MyAndroidTutorial/app/src/main/res/drawable/selected_icon.png differ
diff --git a/examples/0602/MyAndroidTutorial/app/src/main/res/drawable/stop_icon.png b/examples/0602/MyAndroidTutorial/app/src/main/res/drawable/stop_icon.png
new file mode 100755
index 0000000..20df415
Binary files /dev/null and b/examples/0602/MyAndroidTutorial/app/src/main/res/drawable/stop_icon.png differ
diff --git a/examples/0602/MyAndroidTutorial/app/src/main/res/drawable/take_picture_icon.png b/examples/0602/MyAndroidTutorial/app/src/main/res/drawable/take_picture_icon.png
new file mode 100644
index 0000000..08fb514
Binary files /dev/null and b/examples/0602/MyAndroidTutorial/app/src/main/res/drawable/take_picture_icon.png differ
diff --git a/examples/0602/MyAndroidTutorial/app/src/main/res/layout/activity_about.xml b/examples/0602/MyAndroidTutorial/app/src/main/res/layout/activity_about.xml
new file mode 100644
index 0000000..211a9b6
--- /dev/null
+++ b/examples/0602/MyAndroidTutorial/app/src/main/res/layout/activity_about.xml
@@ -0,0 +1,36 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/examples/0602/MyAndroidTutorial/app/src/main/res/layout/activity_color.xml b/examples/0602/MyAndroidTutorial/app/src/main/res/layout/activity_color.xml
new file mode 100644
index 0000000..d25bbc5
--- /dev/null
+++ b/examples/0602/MyAndroidTutorial/app/src/main/res/layout/activity_color.xml
@@ -0,0 +1,16 @@
+
+
+
+
+
diff --git a/examples/0602/MyAndroidTutorial/app/src/main/res/layout/activity_item.xml b/examples/0602/MyAndroidTutorial/app/src/main/res/layout/activity_item.xml
new file mode 100644
index 0000000..4ec4362
--- /dev/null
+++ b/examples/0602/MyAndroidTutorial/app/src/main/res/layout/activity_item.xml
@@ -0,0 +1,125 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/examples/0602/MyAndroidTutorial/app/src/main/res/layout/activity_main.xml b/examples/0602/MyAndroidTutorial/app/src/main/res/layout/activity_main.xml
new file mode 100644
index 0000000..30cdf98
--- /dev/null
+++ b/examples/0602/MyAndroidTutorial/app/src/main/res/layout/activity_main.xml
@@ -0,0 +1,43 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/examples/0602/MyAndroidTutorial/app/src/main/res/layout/activity_maps.xml b/examples/0602/MyAndroidTutorial/app/src/main/res/layout/activity_maps.xml
new file mode 100644
index 0000000..5de477b
--- /dev/null
+++ b/examples/0602/MyAndroidTutorial/app/src/main/res/layout/activity_maps.xml
@@ -0,0 +1,7 @@
+
diff --git a/examples/0602/MyAndroidTutorial/app/src/main/res/layout/activity_play.xml b/examples/0602/MyAndroidTutorial/app/src/main/res/layout/activity_play.xml
new file mode 100644
index 0000000..52db308
--- /dev/null
+++ b/examples/0602/MyAndroidTutorial/app/src/main/res/layout/activity_play.xml
@@ -0,0 +1,38 @@
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/examples/0602/MyAndroidTutorial/app/src/main/res/layout/activity_record.xml b/examples/0602/MyAndroidTutorial/app/src/main/res/layout/activity_record.xml
new file mode 100644
index 0000000..63d9e36
--- /dev/null
+++ b/examples/0602/MyAndroidTutorial/app/src/main/res/layout/activity_record.xml
@@ -0,0 +1,51 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/examples/0602/MyAndroidTutorial/app/src/main/res/layout/item_app_widget.xml b/examples/0602/MyAndroidTutorial/app/src/main/res/layout/item_app_widget.xml
new file mode 100644
index 0000000..92bb45a
--- /dev/null
+++ b/examples/0602/MyAndroidTutorial/app/src/main/res/layout/item_app_widget.xml
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/examples/0602/MyAndroidTutorial/app/src/main/res/layout/single_item.xml b/examples/0602/MyAndroidTutorial/app/src/main/res/layout/single_item.xml
new file mode 100644
index 0000000..8acc60a
--- /dev/null
+++ b/examples/0602/MyAndroidTutorial/app/src/main/res/layout/single_item.xml
@@ -0,0 +1,61 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/examples/0602/MyAndroidTutorial/app/src/main/res/menu/menu_main.xml b/examples/0602/MyAndroidTutorial/app/src/main/res/menu/menu_main.xml
new file mode 100644
index 0000000..3ea061c
--- /dev/null
+++ b/examples/0602/MyAndroidTutorial/app/src/main/res/menu/menu_main.xml
@@ -0,0 +1,56 @@
+
+
+
+
+
+
+
+
+
+
+
+ -
+
+
+
+
+
+
+
diff --git a/examples/0602/MyAndroidTutorial/app/src/main/res/transition/explode_transition.xml b/examples/0602/MyAndroidTutorial/app/src/main/res/transition/explode_transition.xml
new file mode 100644
index 0000000..d2320b1
--- /dev/null
+++ b/examples/0602/MyAndroidTutorial/app/src/main/res/transition/explode_transition.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/examples/0602/MyAndroidTutorial/app/src/main/res/transition/fade_transition.xml b/examples/0602/MyAndroidTutorial/app/src/main/res/transition/fade_transition.xml
new file mode 100644
index 0000000..ea54111
--- /dev/null
+++ b/examples/0602/MyAndroidTutorial/app/src/main/res/transition/fade_transition.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/examples/0602/MyAndroidTutorial/app/src/main/res/transition/slide_transition.xml b/examples/0602/MyAndroidTutorial/app/src/main/res/transition/slide_transition.xml
new file mode 100644
index 0000000..afd9869
--- /dev/null
+++ b/examples/0602/MyAndroidTutorial/app/src/main/res/transition/slide_transition.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/examples/0602/MyAndroidTutorial/app/src/main/res/values-en/strings.xml b/examples/0602/MyAndroidTutorial/app/src/main/res/values-en/strings.xml
new file mode 100644
index 0000000..c532533
--- /dev/null
+++ b/examples/0602/MyAndroidTutorial/app/src/main/res/values-en/strings.xml
@@ -0,0 +1,12 @@
+
+
+
+ MyAndroidTutorial
+ Hello world!
+ Settings
+
+ Title
+ Enter title
+ Content
+ Enter content
+
diff --git a/examples/0602/MyAndroidTutorial/app/src/main/res/values-v14/dimens.xml b/examples/0602/MyAndroidTutorial/app/src/main/res/values-v14/dimens.xml
new file mode 100644
index 0000000..4db8c59
--- /dev/null
+++ b/examples/0602/MyAndroidTutorial/app/src/main/res/values-v14/dimens.xml
@@ -0,0 +1,10 @@
+
+
+
+
+ 0dp
+
+
\ No newline at end of file
diff --git a/examples/0602/MyAndroidTutorial/app/src/main/res/values-v21/styles.xml b/examples/0602/MyAndroidTutorial/app/src/main/res/values-v21/styles.xml
new file mode 100644
index 0000000..606e45f
--- /dev/null
+++ b/examples/0602/MyAndroidTutorial/app/src/main/res/values-v21/styles.xml
@@ -0,0 +1,12 @@
+
+
+
+
\ No newline at end of file
diff --git a/examples/0602/MyAndroidTutorial/app/src/main/res/values-w820dp/dimens.xml b/examples/0602/MyAndroidTutorial/app/src/main/res/values-w820dp/dimens.xml
new file mode 100644
index 0000000..63fc816
--- /dev/null
+++ b/examples/0602/MyAndroidTutorial/app/src/main/res/values-w820dp/dimens.xml
@@ -0,0 +1,6 @@
+
+
+ 64dp
+
diff --git a/examples/0602/MyAndroidTutorial/app/src/main/res/values/colors.xml b/examples/0602/MyAndroidTutorial/app/src/main/res/values/colors.xml
new file mode 100644
index 0000000..6b13c1d
--- /dev/null
+++ b/examples/0602/MyAndroidTutorial/app/src/main/res/values/colors.xml
@@ -0,0 +1,7 @@
+
+
+ #CCCCCC
+ #AAAAAA
+ #DD999999
+ #111111
+
\ No newline at end of file
diff --git a/examples/0602/MyAndroidTutorial/app/src/main/res/values/dimens.xml b/examples/0602/MyAndroidTutorial/app/src/main/res/values/dimens.xml
new file mode 100644
index 0000000..7fa5a6f
--- /dev/null
+++ b/examples/0602/MyAndroidTutorial/app/src/main/res/values/dimens.xml
@@ -0,0 +1,13 @@
+
+
+ 16dp
+ 16dp
+
+ 6dp
+ 24sp
+ 2dp
+
+ 8dp
+
+ 2sp
+
diff --git a/examples/0602/MyAndroidTutorial/app/src/main/res/values/strings.xml b/examples/0602/MyAndroidTutorial/app/src/main/res/values/strings.xml
new file mode 100644
index 0000000..a59f440
--- /dev/null
+++ b/examples/0602/MyAndroidTutorial/app/src/main/res/values/strings.xml
@@ -0,0 +1,59 @@
+
+
+
+ MyAndroidTutorial
+ Hello world!
+ Settings
+ 標題
+ 輸入標題
+ 內容
+ 輸入內容
+ 這是Android Tutorial應用程式
+ AboutActivity
+ 版本:AndroidTutorial_0.2.4
+ ItemActivity
+ ColorActivity
+ 刪除
+ 確定要刪除 %1$d 個項目?
+ 預設的顏色
+ 新增記事的預設顏色
+
+ 預設提醒時間
+ 在指定的時間之前通知
+
+
+ - 五分鐘
+ - 十分鐘
+ - 二十分鐘
+ - 三十分鐘
+ - 六十分鐘
+
+
+
+ - 5
+ - 10
+ - 20
+ - 30
+ - 60
+
+
+ 語音備忘
+ 播放語音備忘
+ 播放
+ 重新錄製
+ Map
+
+ 記事儲存的位置
+ 更新或清除儲存的位置資訊?
+ 更新
+ 清除
+
+ 目前位置
+ 是否儲存目前位置?
+
+ 裝置沒有安裝Google Play服務
+ EXAMPLE
+ Configure
+ Add widget
+
+
diff --git a/examples/0602/MyAndroidTutorial/app/src/main/res/values/styles.xml b/examples/0602/MyAndroidTutorial/app/src/main/res/values/styles.xml
new file mode 100644
index 0000000..766ab99
--- /dev/null
+++ b/examples/0602/MyAndroidTutorial/app/src/main/res/values/styles.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
diff --git a/examples/0602/MyAndroidTutorial/app/src/main/res/xml/item_app_widget_info.xml b/examples/0602/MyAndroidTutorial/app/src/main/res/xml/item_app_widget_info.xml
new file mode 100644
index 0000000..043d27b
--- /dev/null
+++ b/examples/0602/MyAndroidTutorial/app/src/main/res/xml/item_app_widget_info.xml
@@ -0,0 +1,15 @@
+
+
+
\ No newline at end of file
diff --git a/examples/0602/MyAndroidTutorial/app/src/main/res/xml/mypreference.xml b/examples/0602/MyAndroidTutorial/app/src/main/res/xml/mypreference.xml
new file mode 100644
index 0000000..33e714c
--- /dev/null
+++ b/examples/0602/MyAndroidTutorial/app/src/main/res/xml/mypreference.xml
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/examples/0602/MyAndroidTutorial/app/src/release/res/values/google_maps_api.xml b/examples/0602/MyAndroidTutorial/app/src/release/res/values/google_maps_api.xml
new file mode 100644
index 0000000..c4e2431
--- /dev/null
+++ b/examples/0602/MyAndroidTutorial/app/src/release/res/values/google_maps_api.xml
@@ -0,0 +1,18 @@
+
+
+
+ YOUR_KEY_HERE
+
+
diff --git a/examples/0602/MyAndroidTutorial/build.gradle b/examples/0602/MyAndroidTutorial/build.gradle
new file mode 100644
index 0000000..6356aab
--- /dev/null
+++ b/examples/0602/MyAndroidTutorial/build.gradle
@@ -0,0 +1,19 @@
+// Top-level build file where you can add configuration options common to all sub-projects/modules.
+
+buildscript {
+ repositories {
+ jcenter()
+ }
+ dependencies {
+ classpath 'com.android.tools.build:gradle:1.0.0'
+
+ // NOTE: Do not place your application dependencies here; they belong
+ // in the individual module build.gradle files
+ }
+}
+
+allprojects {
+ repositories {
+ jcenter()
+ }
+}
diff --git a/examples/0602/MyAndroidTutorial/gradle.properties b/examples/0602/MyAndroidTutorial/gradle.properties
new file mode 100644
index 0000000..1d3591c
--- /dev/null
+++ b/examples/0602/MyAndroidTutorial/gradle.properties
@@ -0,0 +1,18 @@
+# Project-wide Gradle settings.
+
+# IDE (e.g. Android Studio) users:
+# Gradle settings configured through the IDE *will override*
+# any settings specified in this file.
+
+# For more details on how to configure your build environment visit
+# http://www.gradle.org/docs/current/userguide/build_environment.html
+
+# Specifies the JVM arguments used for the daemon process.
+# The setting is particularly useful for tweaking memory settings.
+# Default value: -Xmx10248m -XX:MaxPermSize=256m
+# org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
+
+# When configured, Gradle will run in incubating parallel mode.
+# This option should only be used with decoupled projects. More details, visit
+# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
+# org.gradle.parallel=true
\ No newline at end of file
diff --git a/examples/0602/MyAndroidTutorial/gradle/wrapper/gradle-wrapper.jar b/examples/0602/MyAndroidTutorial/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 0000000..8c0fb64
Binary files /dev/null and b/examples/0602/MyAndroidTutorial/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/examples/0602/MyAndroidTutorial/gradle/wrapper/gradle-wrapper.properties b/examples/0602/MyAndroidTutorial/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 0000000..0c71e76
--- /dev/null
+++ b/examples/0602/MyAndroidTutorial/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,6 @@
+#Wed Apr 10 15:27:10 PDT 2013
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip
diff --git a/examples/0602/MyAndroidTutorial/gradlew b/examples/0602/MyAndroidTutorial/gradlew
new file mode 100755
index 0000000..91a7e26
--- /dev/null
+++ b/examples/0602/MyAndroidTutorial/gradlew
@@ -0,0 +1,164 @@
+#!/usr/bin/env bash
+
+##############################################################################
+##
+## Gradle start up script for UN*X
+##
+##############################################################################
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS=""
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn ( ) {
+ echo "$*"
+}
+
+die ( ) {
+ echo
+ echo "$*"
+ echo
+ exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+case "`uname`" in
+ CYGWIN* )
+ cygwin=true
+ ;;
+ Darwin* )
+ darwin=true
+ ;;
+ MINGW* )
+ msys=true
+ ;;
+esac
+
+# For Cygwin, ensure paths are in UNIX format before anything is touched.
+if $cygwin ; then
+ [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
+fi
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+ ls=`ls -ld "$PRG"`
+ link=`expr "$ls" : '.*-> \(.*\)$'`
+ if expr "$link" : '/.*' > /dev/null; then
+ PRG="$link"
+ else
+ PRG=`dirname "$PRG"`"/$link"
+ fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >&-
+APP_HOME="`pwd -P`"
+cd "$SAVED" >&-
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD="$JAVA_HOME/jre/sh/java"
+ else
+ JAVACMD="$JAVA_HOME/bin/java"
+ fi
+ if [ ! -x "$JAVACMD" ] ; then
+ die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+ fi
+else
+ JAVACMD="java"
+ which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
+ MAX_FD_LIMIT=`ulimit -H -n`
+ if [ $? -eq 0 ] ; then
+ if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+ MAX_FD="$MAX_FD_LIMIT"
+ fi
+ ulimit -n $MAX_FD
+ if [ $? -ne 0 ] ; then
+ warn "Could not set maximum file descriptor limit: $MAX_FD"
+ fi
+ else
+ warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+ fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+ GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin, switch paths to Windows format before running java
+if $cygwin ; then
+ APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+ CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+
+ # We build the pattern for arguments to be converted via cygpath
+ ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+ SEP=""
+ for dir in $ROOTDIRSRAW ; do
+ ROOTDIRS="$ROOTDIRS$SEP$dir"
+ SEP="|"
+ done
+ OURCYGPATTERN="(^($ROOTDIRS))"
+ # Add a user-defined pattern to the cygpath arguments
+ if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+ OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+ fi
+ # Now convert the arguments - kludge to limit ourselves to /bin/sh
+ i=0
+ for arg in "$@" ; do
+ CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+ CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
+
+ if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
+ eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+ else
+ eval `echo args$i`="\"$arg\""
+ fi
+ i=$((i+1))
+ done
+ case $i in
+ (0) set -- ;;
+ (1) set -- "$args0" ;;
+ (2) set -- "$args0" "$args1" ;;
+ (3) set -- "$args0" "$args1" "$args2" ;;
+ (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+ (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+ (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+ (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+ (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+ (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+ esac
+fi
+
+# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
+function splitJvmOpts() {
+ JVM_OPTS=("$@")
+}
+eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
+JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
+
+exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
diff --git a/examples/0602/MyAndroidTutorial/gradlew.bat b/examples/0602/MyAndroidTutorial/gradlew.bat
new file mode 100644
index 0000000..aec9973
--- /dev/null
+++ b/examples/0602/MyAndroidTutorial/gradlew.bat
@@ -0,0 +1,90 @@
+@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS=
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto init
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto init
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:init
+@rem Get command-line arguments, handling Windowz variants
+
+if not "%OS%" == "Windows_NT" goto win9xME_args
+if "%@eval[2+2]" == "4" goto 4NT_args
+
+:win9xME_args
+@rem Slurp the command line arguments.
+set CMD_LINE_ARGS=
+set _SKIP=2
+
+:win9xME_args_slurp
+if "x%~1" == "x" goto execute
+
+set CMD_LINE_ARGS=%*
+goto execute
+
+:4NT_args
+@rem Get arguments from the 4NT Shell from JP Software
+set CMD_LINE_ARGS=%$
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/examples/0602/MyAndroidTutorial/settings.gradle b/examples/0602/MyAndroidTutorial/settings.gradle
new file mode 100644
index 0000000..e7b4def
--- /dev/null
+++ b/examples/0602/MyAndroidTutorial/settings.gradle
@@ -0,0 +1 @@
+include ':app'
diff --git a/examples/0603/MyAndroidTutorial/.gitignore b/examples/0603/MyAndroidTutorial/.gitignore
new file mode 100644
index 0000000..afbdab3
--- /dev/null
+++ b/examples/0603/MyAndroidTutorial/.gitignore
@@ -0,0 +1,6 @@
+.gradle
+/local.properties
+/.idea/workspace.xml
+/.idea/libraries
+.DS_Store
+/build
diff --git a/examples/0603/MyAndroidTutorial/.idea/.name b/examples/0603/MyAndroidTutorial/.idea/.name
new file mode 100644
index 0000000..5bb7a85
--- /dev/null
+++ b/examples/0603/MyAndroidTutorial/.idea/.name
@@ -0,0 +1 @@
+MyAndroidTutorial
\ No newline at end of file
diff --git a/examples/0603/MyAndroidTutorial/.idea/compiler.xml b/examples/0603/MyAndroidTutorial/.idea/compiler.xml
new file mode 100644
index 0000000..9a8b7e5
--- /dev/null
+++ b/examples/0603/MyAndroidTutorial/.idea/compiler.xml
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/examples/0603/MyAndroidTutorial/.idea/copyright/profiles_settings.xml b/examples/0603/MyAndroidTutorial/.idea/copyright/profiles_settings.xml
new file mode 100644
index 0000000..e7bedf3
--- /dev/null
+++ b/examples/0603/MyAndroidTutorial/.idea/copyright/profiles_settings.xml
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/examples/0603/MyAndroidTutorial/.idea/gradle.xml b/examples/0603/MyAndroidTutorial/.idea/gradle.xml
new file mode 100644
index 0000000..c595ad9
--- /dev/null
+++ b/examples/0603/MyAndroidTutorial/.idea/gradle.xml
@@ -0,0 +1,19 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/examples/0603/MyAndroidTutorial/.idea/misc.xml b/examples/0603/MyAndroidTutorial/.idea/misc.xml
new file mode 100644
index 0000000..b0c0cbc
--- /dev/null
+++ b/examples/0603/MyAndroidTutorial/.idea/misc.xml
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/examples/0603/MyAndroidTutorial/.idea/modules.xml b/examples/0603/MyAndroidTutorial/.idea/modules.xml
new file mode 100644
index 0000000..5edca7e
--- /dev/null
+++ b/examples/0603/MyAndroidTutorial/.idea/modules.xml
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/examples/0603/MyAndroidTutorial/.idea/vcs.xml b/examples/0603/MyAndroidTutorial/.idea/vcs.xml
new file mode 100644
index 0000000..6564d52
--- /dev/null
+++ b/examples/0603/MyAndroidTutorial/.idea/vcs.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/examples/0603/MyAndroidTutorial/MyAndroidTutorial.iml b/examples/0603/MyAndroidTutorial/MyAndroidTutorial.iml
new file mode 100644
index 0000000..62d899f
--- /dev/null
+++ b/examples/0603/MyAndroidTutorial/MyAndroidTutorial.iml
@@ -0,0 +1,19 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/examples/0603/MyAndroidTutorial/app/.gitignore b/examples/0603/MyAndroidTutorial/app/.gitignore
new file mode 100644
index 0000000..796b96d
--- /dev/null
+++ b/examples/0603/MyAndroidTutorial/app/.gitignore
@@ -0,0 +1 @@
+/build
diff --git a/examples/0603/MyAndroidTutorial/app/app.iml b/examples/0603/MyAndroidTutorial/app/app.iml
new file mode 100644
index 0000000..2041401
--- /dev/null
+++ b/examples/0603/MyAndroidTutorial/app/app.iml
@@ -0,0 +1,114 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/examples/0603/MyAndroidTutorial/app/build.gradle b/examples/0603/MyAndroidTutorial/app/build.gradle
new file mode 100644
index 0000000..c5d367d
--- /dev/null
+++ b/examples/0603/MyAndroidTutorial/app/build.gradle
@@ -0,0 +1,27 @@
+apply plugin: 'com.android.application'
+
+android {
+ compileSdkVersion 21
+ buildToolsVersion "21.1.2"
+
+ defaultConfig {
+ applicationId "net.macdidi.myandroidtutorial"
+ minSdkVersion 16
+ 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'
+ compile 'com.android.support:recyclerview-v7:21.0.+'
+}
diff --git a/examples/0603/MyAndroidTutorial/app/proguard-rules.pro b/examples/0603/MyAndroidTutorial/app/proguard-rules.pro
new file mode 100644
index 0000000..b5fa7ec
--- /dev/null
+++ b/examples/0603/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/0603/MyAndroidTutorial/app/src/androidTest/java/net/macdidi/myandroidtutorial/ApplicationTest.java b/examples/0603/MyAndroidTutorial/app/src/androidTest/java/net/macdidi/myandroidtutorial/ApplicationTest.java
new file mode 100644
index 0000000..2cb214e
--- /dev/null
+++ b/examples/0603/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/0603/MyAndroidTutorial/app/src/debug/res/values/google_maps_api.xml b/examples/0603/MyAndroidTutorial/app/src/debug/res/values/google_maps_api.xml
new file mode 100644
index 0000000..7341c8d
--- /dev/null
+++ b/examples/0603/MyAndroidTutorial/app/src/debug/res/values/google_maps_api.xml
@@ -0,0 +1,18 @@
+
+
+
+ AIzaSyCZg9YWlfokPA96VxWGYr6u4C12jL16VhM
+
+
diff --git a/examples/0603/MyAndroidTutorial/app/src/main/AndroidManifest.xml b/examples/0603/MyAndroidTutorial/app/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..06271c2
--- /dev/null
+++ b/examples/0603/MyAndroidTutorial/app/src/main/AndroidManifest.xml
@@ -0,0 +1,131 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/examples/0603/MyAndroidTutorial/app/src/main/java/net/macdidi/myandroidtutorial/AboutActivity.java b/examples/0603/MyAndroidTutorial/app/src/main/java/net/macdidi/myandroidtutorial/AboutActivity.java
new file mode 100644
index 0000000..42dddeb
--- /dev/null
+++ b/examples/0603/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/0603/MyAndroidTutorial/app/src/main/java/net/macdidi/myandroidtutorial/AlarmReceiver.java b/examples/0603/MyAndroidTutorial/app/src/main/java/net/macdidi/myandroidtutorial/AlarmReceiver.java
new file mode 100644
index 0000000..368165c
--- /dev/null
+++ b/examples/0603/MyAndroidTutorial/app/src/main/java/net/macdidi/myandroidtutorial/AlarmReceiver.java
@@ -0,0 +1,86 @@
+package net.macdidi.myandroidtutorial;
+
+import android.app.Notification;
+import android.app.NotificationManager;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.support.v4.app.NotificationCompat;
+
+import java.io.File;
+
+public class AlarmReceiver extends BroadcastReceiver {
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ // 讀取記事標題
+ //String title = intent.getStringExtra("title");
+ // 顯示訊息框
+ //Toast.makeText(context, title, Toast.LENGTH_LONG).show();
+
+ // 讀取記事編號
+ long id = intent.getLongExtra("id", 0);
+
+ if (id != 0) {
+ sendNotify(context, id);
+ }
+ }
+
+ private void sendNotify(Context context, long id) {
+ // 建立資料庫物件
+ ItemDAO itemDAO = new ItemDAO(context.getApplicationContext());
+ // 讀取指定編號的記事物件
+ Item item = itemDAO.get(id);
+
+ // 建立照片檔案物件
+ File file = new File(FileUtil.getExternalStorageDir(FileUtil.APP_DIR),
+ "P" + item.getFileName() + ".jpg");
+
+ // 是否儲存照片檔案
+ boolean isPicture = (item.getFileName() != null &&
+ item.getFileName().length() > 0 &&
+ file.exists());
+
+ // 取得NotificationManager物件
+ NotificationManager nm = (NotificationManager)
+ context.getSystemService(Context.NOTIFICATION_SERVICE);
+
+ // 如果有儲存照片檔案
+ if (isPicture) {
+ // 建立Notification.Builder物件,因為要設定大型圖片樣式
+ // 所以不能使用NotificationCompat.Builder
+ Notification.Builder builder = new Notification.Builder(context);
+ builder.setSmallIcon(android.R.drawable.star_on)
+ .setWhen(System.currentTimeMillis())
+ .setContentTitle(context.getString(R.string.app_name));
+
+ // 建立大型圖片樣式物件
+ Notification.BigPictureStyle bigPictureStyle =
+ new Notification.BigPictureStyle();
+ // 設定圖片與簡介
+ Bitmap bitmap = BitmapFactory.decodeFile(file.getAbsolutePath());
+ bigPictureStyle.bigPicture(bitmap)
+ .setSummaryText(item.getTitle());
+ // 設定樣式為大型圖片
+ builder.setStyle(bigPictureStyle);
+ // 發出通知
+ nm.notify((int)item.getId(), builder.build());
+ }
+ // 如果沒有儲存照片檔案
+ else {
+ // 建立NotificationCompat.Builder物件
+ NotificationCompat.Builder builder =
+ new NotificationCompat.Builder(context);
+ // 設定圖示、時間、內容標題和內容訊息
+ builder.setSmallIcon(android.R.drawable.star_big_on)
+ .setWhen(System.currentTimeMillis())
+ .setContentTitle(context.getString(R.string.app_name))
+ .setContentText(item.getTitle());
+ // 發出通知
+ nm.notify((int)item.getId(), builder.build());
+ }
+ }
+
+}
diff --git a/examples/0603/MyAndroidTutorial/app/src/main/java/net/macdidi/myandroidtutorial/ColorActivity.java b/examples/0603/MyAndroidTutorial/app/src/main/java/net/macdidi/myandroidtutorial/ColorActivity.java
new file mode 100644
index 0000000..182cd55
--- /dev/null
+++ b/examples/0603/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/0603/MyAndroidTutorial/app/src/main/java/net/macdidi/myandroidtutorial/Colors.java b/examples/0603/MyAndroidTutorial/app/src/main/java/net/macdidi/myandroidtutorial/Colors.java
new file mode 100644
index 0000000..1462149
--- /dev/null
+++ b/examples/0603/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/0603/MyAndroidTutorial/app/src/main/java/net/macdidi/myandroidtutorial/FileUtil.java b/examples/0603/MyAndroidTutorial/app/src/main/java/net/macdidi/myandroidtutorial/FileUtil.java
new file mode 100644
index 0000000..1fb41be
--- /dev/null
+++ b/examples/0603/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/0603/MyAndroidTutorial/app/src/main/java/net/macdidi/myandroidtutorial/InitAlarmReceiver.java b/examples/0603/MyAndroidTutorial/app/src/main/java/net/macdidi/myandroidtutorial/InitAlarmReceiver.java
new file mode 100644
index 0000000..959785f
--- /dev/null
+++ b/examples/0603/MyAndroidTutorial/app/src/main/java/net/macdidi/myandroidtutorial/InitAlarmReceiver.java
@@ -0,0 +1,49 @@
+package net.macdidi.myandroidtutorial;
+
+import android.app.AlarmManager;
+import android.app.PendingIntent;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+
+import java.util.Calendar;
+import java.util.List;
+
+public class InitAlarmReceiver extends BroadcastReceiver {
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ // 建立資料庫物件
+ ItemDAO itemDAO = new ItemDAO(context.getApplicationContext());
+ // 讀取資料庫所有記事資料
+ List- items = itemDAO.getAll();
+
+ // 讀取目前時間
+ long current = Calendar.getInstance().getTimeInMillis();
+
+ AlarmManager am = (AlarmManager)
+ context.getSystemService(Context.ALARM_SERVICE);
+
+ for (Item item : items) {
+ long alarm = item.getAlarmDatetime();
+
+ // 如果沒有設定提醒或是提醒已經過期
+ if (alarm == 0 || alarm <= current) {
+ continue;
+ }
+
+ // 設定提醒
+ Intent alarmIntent = new Intent(context, AlarmReceiver.class);
+ //alarmIntent.putExtra("title", item.getTitle());
+
+ // 加入記事編號
+ intent.putExtra("id", item.getId());
+
+ PendingIntent pi = PendingIntent.getBroadcast(
+ context, (int)item.getId(),
+ alarmIntent, PendingIntent.FLAG_ONE_SHOT);
+ am.set(AlarmManager.RTC_WAKEUP, item.getAlarmDatetime(), pi);
+ }
+ }
+
+}
diff --git a/examples/0603/MyAndroidTutorial/app/src/main/java/net/macdidi/myandroidtutorial/Item.java b/examples/0603/MyAndroidTutorial/app/src/main/java/net/macdidi/myandroidtutorial/Item.java
new file mode 100644
index 0000000..e2bc93d
--- /dev/null
+++ b/examples/0603/MyAndroidTutorial/app/src/main/java/net/macdidi/myandroidtutorial/Item.java
@@ -0,0 +1,156 @@
+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;
+
+ // 提醒日期時間
+ private long alarmDatetime;
+
+ 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(fileName);
+ break;
+ case START_LOCATION:
+ // 讀取與設定座標
+ double lat = data.getDoubleExtra("lat", 0.0);
+ double lng = data.getDoubleExtra("lng", 0.0);
+ item.setLatitude(lat);
+ item.setLongitude(lng);
+ 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());
+ }
+
+ 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);
+
+ // 設定儲存的座標
+ intentMap.putExtra("lat", item.getLatitude());
+ intentMap.putExtra("lng", item.getLongitude());
+ intentMap.putExtra("title", item.getTitle());
+ intentMap.putExtra("datetime", item.getLocaleDatetime());
+
+ // 啟動地圖元件
+ startActivityForResult(intentMap, START_LOCATION);
+ break;
+ case R.id.set_alarm:
+ // 設定提醒日期時間
+ processSetAlarm();
+ break;
+ case R.id.select_color:
+ // 啟動設定顏色的Activity元件
+ startActivityForResult(
+ new Intent(this, ColorActivity.class), START_COLOR);
+ break;
+ }
+
+ }
+
+ // 設定提醒日期時間
+ private void processSetAlarm() {
+ Calendar calendar = Calendar.getInstance();
+
+ if (item.getAlarmDatetime() != 0) {
+ // 設定為已經儲存的提醒日期時間
+ calendar.setTimeInMillis(item.getAlarmDatetime());
+ }
+
+ // 讀取年、月、日、時、分
+ int year = calendar.get(Calendar.YEAR);
+ int month = calendar.get(Calendar.MONTH);
+ int day = calendar.get(Calendar.DAY_OF_MONTH);
+ int hour = calendar.get(Calendar.HOUR_OF_DAY);
+ int minute = calendar.get(Calendar.MINUTE);
+
+ // 儲存設定的提醒日期時間
+ final Calendar alarm = Calendar.getInstance();
+
+ // 設定提醒時間
+ TimePickerDialog.OnTimeSetListener timeSetListener =
+ new TimePickerDialog.OnTimeSetListener() {
+ @Override
+ public void onTimeSet(TimePicker view,
+ int hourOfDay, int minute) {
+ alarm.set(Calendar.HOUR_OF_DAY, hourOfDay);
+ alarm.set(Calendar.MINUTE, minute);
+
+ item.setAlarmDatetime(alarm.getTimeInMillis());
+ }
+ };
+
+ // 選擇時間對話框
+ final TimePickerDialog tpd = new TimePickerDialog(
+ this, timeSetListener, hour, minute, true);
+
+ // 設定提醒日期
+ DatePickerDialog.OnDateSetListener dateSetListener =
+ new DatePickerDialog.OnDateSetListener() {
+ @Override
+ public void onDateSet(DatePicker view,
+ int year,
+ int monthOfYear,
+ int dayOfMonth) {
+ alarm.set(Calendar.YEAR, year);
+ alarm.set(Calendar.MONTH, monthOfYear);
+ alarm.set(Calendar.DAY_OF_MONTH, dayOfMonth);
+
+ // 繼續選擇提醒時間
+ tpd.show();
+ }
+ };
+
+ // 建立與顯示選擇日期對話框
+ final DatePickerDialog dpd = new DatePickerDialog(
+ this, dateSetListener, year, month, day);
+ dpd.show();
+ }
+
+ 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);
+ }
+
+ // 點擊畫面右下角的照片縮圖元件
+ public void clickPicture(View view) {
+ Intent intent = new Intent(this, PictureActivity.class);
+
+ // 設定圖片檔案名稱
+ intent.putExtra("pictureName", configFileName("P", ".jpg").getAbsolutePath());
+
+ // 如果裝置的版本是LOLLIPOP
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
+ ActivityOptions options = ActivityOptions
+ .makeSceneTransitionAnimation(this, picture, "picture");
+ startActivity(intent, options.toBundle());
+ }
+ else {
+ startActivity(intent);
+ }
+ }
+
+}
diff --git a/examples/0603/MyAndroidTutorial/app/src/main/java/net/macdidi/myandroidtutorial/ItemAdapter.java b/examples/0603/MyAndroidTutorial/app/src/main/java/net/macdidi/myandroidtutorial/ItemAdapter.java
new file mode 100644
index 0000000..85b40ba
--- /dev/null
+++ b/examples/0603/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/0603/MyAndroidTutorial/app/src/main/java/net/macdidi/myandroidtutorial/ItemAdapterRV.java b/examples/0603/MyAndroidTutorial/app/src/main/java/net/macdidi/myandroidtutorial/ItemAdapterRV.java
new file mode 100644
index 0000000..9e104d4
--- /dev/null
+++ b/examples/0603/MyAndroidTutorial/app/src/main/java/net/macdidi/myandroidtutorial/ItemAdapterRV.java
@@ -0,0 +1,84 @@
+package net.macdidi.myandroidtutorial;
+
+import android.graphics.drawable.GradientDrawable;
+import android.support.v7.widget.RecyclerView;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ImageView;
+import android.widget.RelativeLayout;
+import android.widget.TextView;
+
+import java.util.List;
+
+public class ItemAdapterRV extends RecyclerView.Adapter{
+
+ // 包裝的記事資料
+ private List
- items;
+
+ public ItemAdapterRV(List
- items) {
+ this.items = items;
+ }
+
+ @Override
+ public ItemAdapterRV.ViewHolder onCreateViewHolder(
+ ViewGroup parent, int viewType) {
+ View v = LayoutInflater.from(parent.getContext()).inflate(
+ R.layout.single_item, parent, false);
+ ViewHolder viewHolder = new ViewHolder(v);
+
+ return viewHolder;
+ }
+
+ @Override
+ public void onBindViewHolder(final ViewHolder holder, int position) {
+ final Item item = items.get(position);
+
+ // 設定記事顏色
+ GradientDrawable background = (GradientDrawable)
+ holder.typeColor.getBackground();
+ background.setColor(item.getColor().parseColor());
+
+ // 設定標題與日期時間
+ holder.titleView.setText(item.getTitle());
+ holder.dateView.setText(item.getLocaleDatetime());
+
+ // 設定是否已選擇
+ holder.selectedItem.setVisibility(
+ item.isSelected() ? View.VISIBLE : View.INVISIBLE);
+ }
+
+ @Override
+ public int getItemCount() {
+ return items.size();
+ }
+
+ public void add(Item item) {
+ items.add(item);
+ notifyItemInserted(items.size());
+ }
+
+ // 一定要使用ViewHolder包裝畫面元件
+ public class ViewHolder extends RecyclerView.ViewHolder {
+
+ protected RelativeLayout typeColor;
+ protected ImageView selectedItem;
+ protected TextView titleView;
+ protected TextView dateView;
+
+ protected View rootView;
+
+ public ViewHolder(View view) {
+ super(view);
+
+ typeColor = (RelativeLayout) itemView.findViewById(R.id.type_color);
+ selectedItem = (ImageView) itemView.findViewById(R.id.selected_item);
+ titleView = (TextView) itemView.findViewById(R.id.title_text);
+ dateView = (TextView) itemView.findViewById(R.id.date_text);
+
+ rootView = view;
+ }
+
+ }
+
+}
diff --git a/examples/0603/MyAndroidTutorial/app/src/main/java/net/macdidi/myandroidtutorial/ItemAppWidget.java b/examples/0603/MyAndroidTutorial/app/src/main/java/net/macdidi/myandroidtutorial/ItemAppWidget.java
new file mode 100644
index 0000000..9301f57
--- /dev/null
+++ b/examples/0603/MyAndroidTutorial/app/src/main/java/net/macdidi/myandroidtutorial/ItemAppWidget.java
@@ -0,0 +1,72 @@
+package net.macdidi.myandroidtutorial;
+
+import android.app.PendingIntent;
+import android.appwidget.AppWidgetManager;
+import android.appwidget.AppWidgetProvider;
+import android.content.Context;
+import android.content.Intent;
+import android.widget.RemoteViews;
+
+
+public class ItemAppWidget extends AppWidgetProvider {
+
+ @Override
+ public void onUpdate(Context context,
+ AppWidgetManager appWidgetManager,
+ int[] appWidgetIds) {
+ final int N = appWidgetIds.length;
+
+ for (int i = 0; i < N; i++) {
+ updateAppWidget(context, appWidgetManager, appWidgetIds[i]);
+ }
+ }
+
+ @Override
+ public void onDeleted(Context context, int[] appWidgetIds) {
+ final int N = appWidgetIds.length;
+ for (int i = 0; i < N; i++) {
+ // 刪除小工具已經儲存的記事編號
+ ItemAppWidgetConfigureActivity.deleteItemPref(
+ context, appWidgetIds[i]);
+ }
+ }
+
+ @Override
+ public void onEnabled(Context context) {
+
+ }
+
+ @Override
+ public void onDisabled(Context context) {
+
+ }
+
+ static void updateAppWidget(Context context,
+ AppWidgetManager appWidgetManager,
+ int appWidgetId) {
+ // 讀取小工具儲存的記事編號
+ long id = ItemAppWidgetConfigureActivity.loadItemPref(
+ context, appWidgetId);
+ // 建立小工具畫面元件
+ RemoteViews views = new RemoteViews(
+ context.getPackageName(), R.layout.item_app_widget);
+ // 讀取指定編號的記事物件
+ ItemDAO itemDAO = new ItemDAO(context.getApplicationContext());
+ Item item = itemDAO.get(id);
+
+ // 設定小工具畫面顯示記事標題
+ views.setTextViewText(R.id.appwidget_text,
+ item != null ? item.getTitle() : "NA");
+
+ // 點選小工具畫面的記事標題後,啟動記事應用程式
+ Intent intent = new Intent(context, MainActivity.class);
+ PendingIntent pending = PendingIntent.getActivity(
+ context, 0, intent, 0);
+ views.setOnClickPendingIntent(R.id.appwidget_text, pending);
+
+ // 更新小工具
+ appWidgetManager.updateAppWidget(appWidgetId, views);
+ }
+}
+
+
diff --git a/examples/0603/MyAndroidTutorial/app/src/main/java/net/macdidi/myandroidtutorial/ItemAppWidgetConfigureActivity.java b/examples/0603/MyAndroidTutorial/app/src/main/java/net/macdidi/myandroidtutorial/ItemAppWidgetConfigureActivity.java
new file mode 100644
index 0000000..f83be71
--- /dev/null
+++ b/examples/0603/MyAndroidTutorial/app/src/main/java/net/macdidi/myandroidtutorial/ItemAppWidgetConfigureActivity.java
@@ -0,0 +1,120 @@
+package net.macdidi.myandroidtutorial;
+
+import android.app.Activity;
+import android.appwidget.AppWidgetManager;
+import android.content.Context;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.os.Bundle;
+import android.view.View;
+import android.widget.AdapterView;
+import android.widget.ListView;
+
+import java.util.List;
+
+
+public class ItemAppWidgetConfigureActivity extends Activity {
+
+ int mAppWidgetId = AppWidgetManager.INVALID_APPWIDGET_ID;
+
+ private static final String PREFS_NAME =
+ "net.macdidi.myandroidtutorial.ItemAppWidget";
+ private static final String PREF_PREFIX_KEY = "appwidget_";
+
+ // 選擇小工具使用的記事項目
+ private ListView item_list;
+ private ItemAdapter itemAdapter;
+ private List
- items;
+ private ItemDAO itemDAO;
+
+ public ItemAppWidgetConfigureActivity() {
+ super();
+ }
+
+ @Override
+ public void onCreate(Bundle icicle) {
+ super.onCreate(icicle);
+
+ setResult(RESULT_CANCELED);
+
+ // 改為使用應用程式主畫面
+ setContentView(R.layout.activity_main);
+
+ // 建立與設定選擇小工具使用的記事項目需要的物件
+ item_list = (ListView)findViewById(R.id.item_list);
+ itemDAO = new ItemDAO(getApplicationContext());
+ items = itemDAO.getAll();
+ itemAdapter = new ItemAdapter(this, R.layout.single_item, items);
+ item_list.setAdapter(itemAdapter);
+ item_list.setOnItemClickListener(itemListener);
+
+ Intent intent = getIntent();
+ Bundle extras = intent.getExtras();
+
+ if (extras != null) {
+ mAppWidgetId = extras.getInt(
+ AppWidgetManager.EXTRA_APPWIDGET_ID,
+ AppWidgetManager.INVALID_APPWIDGET_ID);
+ }
+
+ if (mAppWidgetId == AppWidgetManager.INVALID_APPWIDGET_ID) {
+ finish();
+ return;
+ }
+
+ }
+
+ // 選擇記事項目
+ AdapterView.OnItemClickListener itemListener =
+ new AdapterView.OnItemClickListener() {
+ @Override
+ public void onItemClick(AdapterView> parent, View view,
+ int position, long id) {
+ final Context context = ItemAppWidgetConfigureActivity.this;
+
+ // 讀取與儲存選擇的記事物件
+ Item item = itemAdapter.getItem(position);
+ saveItemPref(context, mAppWidgetId, item.getId());
+
+ AppWidgetManager appWidgetManager =
+ AppWidgetManager.getInstance(context);
+ ItemAppWidget.updateAppWidget(
+ context, appWidgetManager, mAppWidgetId);
+ Intent resultValue = new Intent();
+ resultValue.putExtra(
+ AppWidgetManager.EXTRA_APPWIDGET_ID, mAppWidgetId);
+ setResult(RESULT_OK, resultValue);
+
+ finish();
+ }
+ };
+
+ // 儲存選擇的記事編號
+ static void saveItemPref(Context context, int appWidgetId, long id) {
+ SharedPreferences.Editor prefs =
+ context.getSharedPreferences(PREFS_NAME, 0).edit();
+ prefs.putLong(PREF_PREFIX_KEY + appWidgetId, id);
+ prefs.commit();
+ }
+
+ // 讀取記事編號
+ static long loadItemPref(Context context, int appWidgetId) {
+ SharedPreferences prefs =
+ context.getSharedPreferences(PREFS_NAME, 0);
+ long idValue = prefs.getLong(PREF_PREFIX_KEY + appWidgetId, 0);
+
+ return idValue;
+ }
+
+ // 刪除記事編號
+ static void deleteItemPref(Context context, int appWidgetId) {
+ SharedPreferences.Editor prefs =
+ context.getSharedPreferences(PREFS_NAME, 0).edit();
+ prefs.remove(PREF_PREFIX_KEY + appWidgetId);
+ prefs.commit();
+ }
+
+}
+
+
+
diff --git a/examples/0603/MyAndroidTutorial/app/src/main/java/net/macdidi/myandroidtutorial/ItemDAO.java b/examples/0603/MyAndroidTutorial/app/src/main/java/net/macdidi/myandroidtutorial/ItemDAO.java
new file mode 100644
index 0000000..b3c7b49
--- /dev/null
+++ b/examples/0603/MyAndroidTutorial/app/src/main/java/net/macdidi/myandroidtutorial/ItemDAO.java
@@ -0,0 +1,214 @@
+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";
+
+ // 提醒日期時間
+ public static final String ALARMDATETIME_COLUMN = "alarmdatetime";
+
+ // 使用上面宣告的變數建立表格的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, " +
+ ALARMDATETIME_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());
+
+ // 提醒日期時間
+ cv.put(ALARMDATETIME_COLUMN, item.getAlarmDatetime());
+
+ // 新增一筆資料並取得編號
+ // 第一個參數是表格名稱
+ // 第二個參數是沒有指定欄位值的預設值
+ // 第三個參數是包裝新增資料的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());
+
+ // 提醒日期時間
+ cv.put(ALARMDATETIME_COLUMN, item.getAlarmDatetime());
+
+ // 設定修改資料的條件為編號
+ // 格式為「欄位名稱=資料」
+ 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));
+
+ // 提醒日期時間
+ result.setAlarmDatetime(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/0603/MyAndroidTutorial/app/src/main/java/net/macdidi/myandroidtutorial/MainActivity.java b/examples/0603/MyAndroidTutorial/app/src/main/java/net/macdidi/myandroidtutorial/MainActivity.java
new file mode 100644
index 0000000..496391c
--- /dev/null
+++ b/examples/0603/MyAndroidTutorial/app/src/main/java/net/macdidi/myandroidtutorial/MainActivity.java
@@ -0,0 +1,336 @@
+package net.macdidi.myandroidtutorial;
+
+import android.app.Activity;
+import android.app.ActivityOptions;
+import android.app.AlarmManager;
+import android.app.AlertDialog;
+import android.app.PendingIntent;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.os.Build;
+import android.os.Bundle;
+import android.support.v7.widget.LinearLayoutManager;
+import android.support.v7.widget.RecyclerView;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
+import android.view.View;
+import android.widget.TextView;
+
+import java.util.List;
+
+public class MainActivity extends Activity {
+
+ // 移除原來的ListView元件
+ //private ListView item_list;
+
+ // 加入下列需要的元件
+ private RecyclerView item_list;
+ private RecyclerView.Adapter itemAdapter;
+ private RecyclerView.LayoutManager rvLayoutManager;
+
+ private TextView show_app_name;
+
+ // 移除原來的ItemAdapter
+ //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();
+
+ // 移除原來ListView元件執行的工作
+ //itemAdapter = new ItemAdapter(this, R.layout.single_item, items);
+ //item_list.setAdapter(itemAdapter);
+
+ // 執行RecyclerView元件的設定
+ item_list.setHasFixedSize(true);
+ rvLayoutManager = new LinearLayoutManager(this);
+ item_list.setLayoutManager(rvLayoutManager);
+
+ // 在這裡執行註冊監聽事件的工作
+ processControllers();
+
+ // 設定自定動畫效果物件
+ item_list.setItemAnimator(new MyItemAnimator());
+ }
+
+ @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");
+
+ boolean updateAlarm = false;
+
+ if (requestCode == 0) {
+ item = itemDAO.insert(item);
+
+ //items.add(item);
+ //itemAdapter.notifyDataSetChanged();
+ ((ItemAdapterRV)itemAdapter).add(item);
+ }
+ else if (requestCode == 1) {
+ int position = data.getIntExtra("position", -1);
+
+ if (position != -1) {
+ Item ori = itemDAO.get(item.getId());
+ updateAlarm = (item.getAlarmDatetime() != ori.getAlarmDatetime());
+
+ itemDAO.update(item);
+
+ items.set(position, item);
+ itemAdapter.notifyDataSetChanged();
+ }
+ }
+
+ if (item.getAlarmDatetime() != 0 && updateAlarm) {
+ Intent intent = new Intent(this, AlarmReceiver.class);
+ intent.putExtra("id", item.getId());
+ PendingIntent pi = PendingIntent.getBroadcast(
+ this, (int)item.getId(),
+ intent, PendingIntent.FLAG_ONE_SHOT);
+ AlarmManager am = (AlarmManager)
+ getSystemService(Context.ALARM_SERVICE);
+ am.set(AlarmManager.RTC_WAKEUP, item.getAlarmDatetime(), pi);
+ }
+ }
+ }
+
+ private void processViews() {
+ item_list = (RecyclerView) findViewById(R.id.item_list);
+ show_app_name = (TextView) findViewById(R.id.show_app_name);
+ }
+
+ private void processControllers() {
+ itemAdapter = new ItemAdapterRV(items) {
+ @Override
+ public void onBindViewHolder(final ViewHolder holder, final int position) {
+ super.onBindViewHolder(holder, position);
+
+ // 建立與註冊項目點擊監聽物件
+ holder.rootView.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ // 讀取選擇的記事物件
+ Item item = items.get(position);
+
+ // 如果已經有勾選的項目
+ if (selectedCount > 0) {
+ // 處理是否顯示已選擇項目
+ processMenu(item);
+ // 重新設定記事項目
+ items.set(position, item);
+ } else {
+ Intent intent = new Intent(
+ "net.macdidi.myandroidtutorial.EDIT_ITEM");
+
+ // 設定記事編號與記事物件
+ intent.putExtra("position", position);
+ intent.putExtra("net.macdidi.myandroidtutorial.Item", item);
+
+ // 依照版本啟動Acvitity元件
+ startActivityForVersion(intent, 1);
+ }
+ }
+ });
+
+ // 建立與註冊項目長按監聽物件
+ holder.rootView.setOnLongClickListener(new View.OnLongClickListener() {
+ @Override
+ public boolean onLongClick(View v) {
+ // 讀取選擇的記事物件
+ Item item = items.get(position);
+ // 處理是否顯示已選擇項目
+ processMenu(item);
+ // 重新設定記事項目
+ items.set(position, item);
+ return true;
+ }
+ });
+ }
+ };
+
+ item_list.setAdapter(itemAdapter);
+ }
+
+ // 處理是否顯示已選擇項目
+ 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);
+
+ // 通知項目勾選狀態改變
+ itemAdapter.notifyDataSetChanged();
+ }
+
+ // 載入選單資源
+ @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:
+ Intent intent = new Intent("net.macdidi.myandroidtutorial.ADD_ITEM");
+ startActivityForVersion(intent, 0);
+ break;
+ // 取消所有已勾選的項目
+ case R.id.revert_item:
+ for (int i = 0; i < items.size(); i++) {
+ Item ri = items.get(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 = items.size() - 1;
+
+ while (index > -1) {
+ // 改為使用items物件
+ Item item = items.get(index);
+
+ if (item.isSelected()) {
+ // 改為使用items物件
+ items.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;
+ }
+
+ }
+
+ public void aboutApp(View view) {
+ Intent intent = new Intent(this, AboutActivity.class);
+ startActivity(intent);
+ }
+
+ public void clickPreferences(MenuItem item) {
+ // 依照版本啟動Acvitity元件
+ startActivityForVersion(new Intent(this, PrefActivity.class));
+ }
+
+ private void startActivityForVersion(Intent intent, int requestCode) {
+ // 如果裝置的版本是LOLLIPOP
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
+ // 加入畫面轉換設定
+ startActivityForResult(intent, requestCode,
+ ActivityOptions.makeSceneTransitionAnimation(
+ MainActivity.this).toBundle());
+ }
+ else {
+ startActivityForResult(intent, requestCode);
+ }
+ }
+
+ private void startActivityForVersion(Intent intent) {
+ // 如果裝置的版本是LOLLIPOP
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
+ // 加入畫面轉換設定
+ startActivity(intent,
+ ActivityOptions.makeSceneTransitionAnimation(
+ MainActivity.this).toBundle());
+ }
+ else {
+ startActivity(intent);
+ }
+ }
+
+}
+
+
diff --git a/examples/0603/MyAndroidTutorial/app/src/main/java/net/macdidi/myandroidtutorial/MapsActivity.java b/examples/0603/MyAndroidTutorial/app/src/main/java/net/macdidi/myandroidtutorial/MapsActivity.java
new file mode 100644
index 0000000..abab911
--- /dev/null
+++ b/examples/0603/MyAndroidTutorial/app/src/main/java/net/macdidi/myandroidtutorial/MapsActivity.java
@@ -0,0 +1,338 @@
+package net.macdidi.myandroidtutorial;
+
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.location.Location;
+import android.os.Bundle;
+import android.support.v4.app.FragmentActivity;
+import android.widget.Toast;
+
+import com.google.android.gms.common.ConnectionResult;
+import com.google.android.gms.common.api.GoogleApiClient;
+import com.google.android.gms.common.api.GoogleApiClient.ConnectionCallbacks;
+import com.google.android.gms.common.api.GoogleApiClient.OnConnectionFailedListener;
+
+import com.google.android.gms.location.LocationListener;
+import com.google.android.gms.location.LocationRequest;
+import com.google.android.gms.location.LocationServices;
+
+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.Marker;
+import com.google.android.gms.maps.model.MarkerOptions;
+
+public class MapsActivity extends FragmentActivity
+ implements ConnectionCallbacks,
+ OnConnectionFailedListener,
+ LocationListener {
+
+ private GoogleMap mMap;
+
+ // Google API用戶端物件
+ private GoogleApiClient googleApiClient;
+
+ // Location請求物件
+ private LocationRequest locationRequest;
+
+ // 記錄目前最新的位置
+ private Location currentLocation;
+
+ // 顯示目前與儲存位置的標記物件
+ private Marker currentMarker, itemMarker;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_maps);
+ setUpMapIfNeeded();
+
+ // 建立Google API用戶端物件
+ configGoogleApiClient();
+
+ // 建立Location請求物件
+ configLocationRequest();
+
+ // 讀取記事儲存的座標
+ Intent intent = getIntent();
+ double lat = intent.getDoubleExtra("lat", 0.0);
+ double lng = intent.getDoubleExtra("lng", 0.0);
+
+ // 如果記事已經儲存座標
+ if (lat != 0.0 && lng != 0.0) {
+ // 建立座標物件
+ LatLng itemPlace = new LatLng(lat, lng);
+ // 加入地圖標記
+ addMarker(itemPlace, intent.getStringExtra("title"),
+ intent.getStringExtra("datetime"));
+ // 移動地圖
+ moveMap(itemPlace);
+ }
+ else {
+ // 連線到Google API用戶端
+ if (!googleApiClient.isConnected()) {
+ googleApiClient.connect();
+ }
+ }
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+ setUpMapIfNeeded();
+
+ // 連線到Google API用戶端
+ if (!googleApiClient.isConnected() && currentMarker != null) {
+ googleApiClient.connect();
+ }
+ }
+
+ @Override
+ protected void onPause() {
+ super.onPause();
+
+ // 移除位置請求服務
+ if (googleApiClient.isConnected()) {
+ LocationServices.FusedLocationApi.removeLocationUpdates(
+ googleApiClient, this);
+ }
+ }
+
+ @Override
+ protected void onStop() {
+ super.onStop();
+
+ // 移除Google API用戶端連線
+ if (googleApiClient.isConnected()) {
+ googleApiClient.disconnect();
+ }
+ }
+
+ // 建立Google API用戶端物件
+ private synchronized void configGoogleApiClient() {
+ googleApiClient = new GoogleApiClient.Builder(this)
+ .addConnectionCallbacks(this)
+ .addOnConnectionFailedListener(this)
+ .addApi(LocationServices.API)
+ .build();
+ }
+
+ // 建立Location請求物件
+ private void configLocationRequest() {
+ locationRequest = new LocationRequest();
+ // 設定讀取位置資訊的間隔時間為一秒(1000ms)
+ locationRequest.setInterval(1000);
+ // 設定讀取位置資訊最快的間隔時間為一秒(1000ms)
+ locationRequest.setFastestInterval(1000);
+ // 設定優先讀取高精確度的位置資訊(GPS)
+ locationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
+ }
+
+ private void setUpMapIfNeeded() {
+ if (mMap == null) {
+ mMap = ((SupportMapFragment) getSupportFragmentManager().
+ findFragmentById(R.id.map)).getMap();
+
+ if (mMap != null) {
+ // 移除地圖設定
+ //setUpMap();
+ processController();
+ }
+ }
+ }
+
+ // 移除地圖設定方法
+ private void setUpMap() {
+ // 建立位置的座標物件
+ LatLng place = new LatLng(25.033408, 121.564099);
+ // 移動地圖
+ moveMap(place);
+
+ // 加入地圖標記
+ addMarker(place, "Hello!", " Google Maps v2!");
+ }
+
+ private void processController() {
+ // 對話框按鈕事件
+ final DialogInterface.OnClickListener listener =
+ new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ switch (which) {
+ // 更新位置資訊
+ case DialogInterface.BUTTON_POSITIVE:
+ // 連線到Google API用戶端
+ if (!googleApiClient.isConnected()) {
+ googleApiClient.connect();
+ }
+ break;
+ // 清除位置資訊
+ case DialogInterface.BUTTON_NEUTRAL:
+ Intent result = new Intent();
+ result.putExtra("lat", 0);
+ result.putExtra("lng", 0);
+ setResult(Activity.RESULT_OK, result);
+ finish();
+ break;
+ // 取消
+ case DialogInterface.BUTTON_NEGATIVE:
+ break;
+ }
+ }
+ };
+
+ // 標記訊息框點擊事件
+ mMap.setOnInfoWindowClickListener(new GoogleMap.OnInfoWindowClickListener() {
+ @Override
+ public void onInfoWindowClick(Marker marker) {
+ // 如果是記事儲存的標記
+ if (marker.equals(itemMarker)) {
+ AlertDialog.Builder ab = new AlertDialog.Builder(MapsActivity.this);
+
+ ab.setTitle(R.string.title_update_location)
+ .setMessage(R.string.message_update_location)
+ .setCancelable(true);
+
+ ab.setPositiveButton(R.string.update, listener);
+ ab.setNeutralButton(R.string.clear, listener);
+ ab.setNegativeButton(android.R.string.cancel, listener);
+
+ ab.show();
+ }
+ }
+ });
+
+ // 標記點擊事件
+ mMap.setOnMarkerClickListener(new GoogleMap.OnMarkerClickListener() {
+ @Override
+ public boolean onMarkerClick(Marker marker) {
+ // 如果是目前位置標記
+ if (marker.equals(currentMarker)) {
+ AlertDialog.Builder ab = new AlertDialog.Builder(MapsActivity.this);
+
+ ab.setTitle(R.string.title_current_location)
+ .setMessage(R.string.message_current_location)
+ .setCancelable(true);
+
+ ab.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ Intent result = new Intent();
+ result.putExtra("lat", currentLocation.getLatitude());
+ result.putExtra("lng", currentLocation.getLongitude());
+ setResult(Activity.RESULT_OK, result);
+ finish();
+ }
+ });
+ ab.setNegativeButton(android.R.string.cancel, null);
+
+ ab.show();
+
+ return true;
+ }
+
+ return false;
+ }
+ });
+ }
+
+ // 移動地圖到參數指定的位置
+ private void moveMap(LatLng place) {
+ // 建立地圖攝影機的位置物件
+ CameraPosition cameraPosition =
+ new CameraPosition.Builder()
+ .target(place)
+ .zoom(17)
+ .build();
+
+ // 使用動畫的效果移動地圖
+ mMap.animateCamera(CameraUpdateFactory.newCameraPosition(cameraPosition),
+ new GoogleMap.CancelableCallback() {
+ @Override
+ public void onFinish() {
+ if (itemMarker != null) {
+ itemMarker.showInfoWindow();
+ }
+ }
+
+ @Override
+ public void onCancel() {
+
+ }
+ });
+ }
+
+ // 在地圖加入指定位置與標題的標記
+ 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);
+
+ // 加入並設定記事儲存的位置標記
+ itemMarker = mMap.addMarker(markerOptions);
+ }
+
+ // ConnectionCallbacks
+ @Override
+ public void onConnected(Bundle bundle) {
+ // 已經連線到Google Services
+ // 啟動位置更新服務
+ // 位置資訊更新的時候,應用程式會自動呼叫LocationListener.onLocationChanged
+ LocationServices.FusedLocationApi.requestLocationUpdates(
+ googleApiClient, locationRequest, MapsActivity.this);
+ }
+
+ // ConnectionCallbacks
+ @Override
+ public void onConnectionSuspended(int i) {
+ // Google Services連線中斷
+ // int參數是連線中斷的代號
+ }
+
+ // OnConnectionFailedListener
+ @Override
+ public void onConnectionFailed(ConnectionResult connectionResult) {
+ // Google Services連線失敗
+ // ConnectionResult參數是連線失敗的資訊
+ int errorCode = connectionResult.getErrorCode();
+
+ // 裝置沒有安裝Google Play服務
+ if (errorCode == ConnectionResult.SERVICE_MISSING) {
+ Toast.makeText(this, R.string.google_play_service_missing,
+ Toast.LENGTH_LONG).show();
+ }
+ }
+
+ // LocationListener
+ @Override
+ public void onLocationChanged(Location location) {
+ // 位置改變
+ // Location參數是目前的位置
+ currentLocation = location;
+ LatLng latLng = new LatLng(
+ location.getLatitude(), location.getLongitude());
+
+ // 設定目前位置的標記
+ if (currentMarker == null) {
+ currentMarker = mMap.addMarker(new MarkerOptions().position(latLng));
+ }
+ else {
+ currentMarker.setPosition(latLng);
+ }
+
+ // 移動地圖到目前的位置
+ moveMap(latLng);
+ }
+
+}
diff --git a/examples/0603/MyAndroidTutorial/app/src/main/java/net/macdidi/myandroidtutorial/MyDBHelper.java b/examples/0603/MyAndroidTutorial/app/src/main/java/net/macdidi/myandroidtutorial/MyDBHelper.java
new file mode 100644
index 0000000..9350577
--- /dev/null
+++ b/examples/0603/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 = 3;
+ // 資料庫物件,固定的欄位變數
+ 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/0603/MyAndroidTutorial/app/src/main/java/net/macdidi/myandroidtutorial/MyItemAnimator.java b/examples/0603/MyAndroidTutorial/app/src/main/java/net/macdidi/myandroidtutorial/MyItemAnimator.java
new file mode 100644
index 0000000..0a0892f
--- /dev/null
+++ b/examples/0603/MyAndroidTutorial/app/src/main/java/net/macdidi/myandroidtutorial/MyItemAnimator.java
@@ -0,0 +1,124 @@
+package net.macdidi.myandroidtutorial;
+
+import android.animation.Animator;
+import android.animation.AnimatorSet;
+import android.animation.ObjectAnimator;
+import android.support.v7.widget.RecyclerView;
+import android.view.View;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class MyItemAnimator extends RecyclerView.ItemAnimator {
+
+ // 儲存動畫的畫面包裝元件
+ private List viewHolders = new ArrayList<>();
+
+ @Override
+ public void runPendingAnimations() {
+
+ if (!viewHolders.isEmpty()) {
+
+ // 動畫物件
+ AnimatorSet animator;
+ // 執行動畫的元件
+ View target;
+
+ for (final RecyclerView.ViewHolder viewHolder : viewHolders) {
+ // 取得執行動畫的元件
+ target = viewHolder.itemView;
+ // 建立動畫物件
+ animator = new AnimatorSet();
+
+ // 設定動畫效果
+ // 由左側出現與淡入效果
+ animator.playTogether(
+ ObjectAnimator.ofFloat(target, "translationX", -target.getMeasuredWidth(), 0.0f),
+ ObjectAnimator.ofFloat(target, "alpha", 0.5F, 1.0F)
+ );
+
+ // 設定動畫套用的元件與時間
+ animator.setTarget(target);
+ animator.setDuration(1000);
+
+ // 動畫結束監聽事件
+ animator.addListener(new AnimatorListenerWrapper() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ // 移除完成動畫的元件
+ viewHolders.remove(viewHolder);
+
+ if (!isRunning()) {
+ dispatchAnimationsFinished();
+ }
+ }
+ });
+
+ animator.start();
+ }
+ }
+ }
+
+ @Override
+ public boolean animateAdd(RecyclerView.ViewHolder viewHolder) {
+ return viewHolders.add(viewHolder);
+ }
+
+ @Override
+ public boolean animateRemove(RecyclerView.ViewHolder viewHolder) {
+ return false;
+ }
+
+ @Override
+ public boolean animateMove(RecyclerView.ViewHolder viewHolder,
+ int i, int i2, int i3, int i4) {
+ return false;
+ }
+
+ @Override
+ public boolean animateChange(RecyclerView.ViewHolder oldHolder,
+ RecyclerView.ViewHolder newHolder,
+ int fromLeft, int fromTop,
+ int toLeft, int toTop) {
+ return false;
+ }
+
+ @Override
+ public void endAnimation(RecyclerView.ViewHolder viewHolder) {
+
+ }
+
+ @Override
+ public void endAnimations() {
+
+ }
+
+ @Override
+ public boolean isRunning() {
+ return !viewHolders.isEmpty();
+ }
+
+ public class AnimatorListenerWrapper implements Animator.AnimatorListener {
+
+ @Override
+ public void onAnimationStart(Animator animation) {
+
+ }
+
+ @Override
+ public void onAnimationEnd(Animator animation) {
+
+ }
+
+ @Override
+ public void onAnimationCancel(Animator animation) {
+
+ }
+
+ @Override
+ public void onAnimationRepeat(Animator animation) {
+
+ }
+ }
+
+}
diff --git a/examples/0603/MyAndroidTutorial/app/src/main/java/net/macdidi/myandroidtutorial/PictureActivity.java b/examples/0603/MyAndroidTutorial/app/src/main/java/net/macdidi/myandroidtutorial/PictureActivity.java
new file mode 100644
index 0000000..59ac50e
--- /dev/null
+++ b/examples/0603/MyAndroidTutorial/app/src/main/java/net/macdidi/myandroidtutorial/PictureActivity.java
@@ -0,0 +1,42 @@
+package net.macdidi.myandroidtutorial;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.os.Build;
+import android.os.Bundle;
+import android.view.View;
+import android.view.Window;
+import android.widget.ImageView;
+
+public class PictureActivity extends Activity {
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ requestWindowFeature(Window.FEATURE_NO_TITLE);
+ setContentView(R.layout.activity_picture);
+
+ // 取得照片元件
+ ImageView picture_view = (ImageView) findViewById(R.id.picture_view);
+
+ // 讀取照片檔案名稱
+ Intent intent = getIntent();
+ String pictureName = intent.getStringExtra("pictureName");
+
+ if (pictureName != null) {
+ // 設定照片元件
+ FileUtil.fileToImageView(pictureName, picture_view);
+ }
+ }
+
+ public void clickPicture(View view) {
+ // 如果裝置的版本是LOLLIPOP
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
+ finishAfterTransition();
+ }
+ else {
+ finish();
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/examples/0603/MyAndroidTutorial/app/src/main/java/net/macdidi/myandroidtutorial/PlayActivity.java b/examples/0603/MyAndroidTutorial/app/src/main/java/net/macdidi/myandroidtutorial/PlayActivity.java
new file mode 100644
index 0000000..5cfb523
--- /dev/null
+++ b/examples/0603/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/0603/MyAndroidTutorial/app/src/main/java/net/macdidi/myandroidtutorial/PrefActivity.java b/examples/0603/MyAndroidTutorial/app/src/main/java/net/macdidi/myandroidtutorial/PrefActivity.java
new file mode 100644
index 0000000..b2dcc4a
--- /dev/null
+++ b/examples/0603/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/0603/MyAndroidTutorial/app/src/main/java/net/macdidi/myandroidtutorial/RecordActivity.java b/examples/0603/MyAndroidTutorial/app/src/main/java/net/macdidi/myandroidtutorial/RecordActivity.java
new file mode 100644
index 0000000..62b5e93
--- /dev/null
+++ b/examples/0603/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/0603/MyAndroidTutorial/app/src/main/res/drawable-hdpi/ic_launcher.png b/examples/0603/MyAndroidTutorial/app/src/main/res/drawable-hdpi/ic_launcher.png
new file mode 100644
index 0000000..96a442e
Binary files /dev/null and b/examples/0603/MyAndroidTutorial/app/src/main/res/drawable-hdpi/ic_launcher.png differ
diff --git a/examples/0603/MyAndroidTutorial/app/src/main/res/drawable-mdpi/ic_launcher.png b/examples/0603/MyAndroidTutorial/app/src/main/res/drawable-mdpi/ic_launcher.png
new file mode 100644
index 0000000..359047d
Binary files /dev/null and b/examples/0603/MyAndroidTutorial/app/src/main/res/drawable-mdpi/ic_launcher.png differ
diff --git a/examples/0603/MyAndroidTutorial/app/src/main/res/drawable-nodpi/example_appwidget_preview.png b/examples/0603/MyAndroidTutorial/app/src/main/res/drawable-nodpi/example_appwidget_preview.png
new file mode 100644
index 0000000..894b069
Binary files /dev/null and b/examples/0603/MyAndroidTutorial/app/src/main/res/drawable-nodpi/example_appwidget_preview.png differ
diff --git a/examples/0603/MyAndroidTutorial/app/src/main/res/drawable-xhdpi/ic_launcher.png b/examples/0603/MyAndroidTutorial/app/src/main/res/drawable-xhdpi/ic_launcher.png
new file mode 100644
index 0000000..71c6d76
Binary files /dev/null and b/examples/0603/MyAndroidTutorial/app/src/main/res/drawable-xhdpi/ic_launcher.png differ
diff --git a/examples/0603/MyAndroidTutorial/app/src/main/res/drawable-xxhdpi/ic_launcher.png b/examples/0603/MyAndroidTutorial/app/src/main/res/drawable-xxhdpi/ic_launcher.png
new file mode 100644
index 0000000..4df1894
Binary files /dev/null and b/examples/0603/MyAndroidTutorial/app/src/main/res/drawable-xxhdpi/ic_launcher.png differ
diff --git a/examples/0603/MyAndroidTutorial/app/src/main/res/drawable/alarm_icon.png b/examples/0603/MyAndroidTutorial/app/src/main/res/drawable/alarm_icon.png
new file mode 100644
index 0000000..c6cac88
Binary files /dev/null and b/examples/0603/MyAndroidTutorial/app/src/main/res/drawable/alarm_icon.png differ
diff --git a/examples/0603/MyAndroidTutorial/app/src/main/res/drawable/item_drawable.xml b/examples/0603/MyAndroidTutorial/app/src/main/res/drawable/item_drawable.xml
new file mode 100644
index 0000000..37607e2
--- /dev/null
+++ b/examples/0603/MyAndroidTutorial/app/src/main/res/drawable/item_drawable.xml
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/examples/0603/MyAndroidTutorial/app/src/main/res/drawable/location_icon.png b/examples/0603/MyAndroidTutorial/app/src/main/res/drawable/location_icon.png
new file mode 100644
index 0000000..4c3c514
Binary files /dev/null and b/examples/0603/MyAndroidTutorial/app/src/main/res/drawable/location_icon.png differ
diff --git a/examples/0603/MyAndroidTutorial/app/src/main/res/drawable/pause_icon.png b/examples/0603/MyAndroidTutorial/app/src/main/res/drawable/pause_icon.png
new file mode 100755
index 0000000..a5aee6f
Binary files /dev/null and b/examples/0603/MyAndroidTutorial/app/src/main/res/drawable/pause_icon.png differ
diff --git a/examples/0603/MyAndroidTutorial/app/src/main/res/drawable/play_icon.png b/examples/0603/MyAndroidTutorial/app/src/main/res/drawable/play_icon.png
new file mode 100755
index 0000000..6a40cd5
Binary files /dev/null and b/examples/0603/MyAndroidTutorial/app/src/main/res/drawable/play_icon.png differ
diff --git a/examples/0603/MyAndroidTutorial/app/src/main/res/drawable/record_dark_icon.png b/examples/0603/MyAndroidTutorial/app/src/main/res/drawable/record_dark_icon.png
new file mode 100755
index 0000000..bcf83ca
Binary files /dev/null and b/examples/0603/MyAndroidTutorial/app/src/main/res/drawable/record_dark_icon.png differ
diff --git a/examples/0603/MyAndroidTutorial/app/src/main/res/drawable/record_red_icon.png b/examples/0603/MyAndroidTutorial/app/src/main/res/drawable/record_red_icon.png
new file mode 100755
index 0000000..2b44af0
Binary files /dev/null and b/examples/0603/MyAndroidTutorial/app/src/main/res/drawable/record_red_icon.png differ
diff --git a/examples/0603/MyAndroidTutorial/app/src/main/res/drawable/record_sound_icon.png b/examples/0603/MyAndroidTutorial/app/src/main/res/drawable/record_sound_icon.png
new file mode 100755
index 0000000..a1382ac
Binary files /dev/null and b/examples/0603/MyAndroidTutorial/app/src/main/res/drawable/record_sound_icon.png differ
diff --git a/examples/0603/MyAndroidTutorial/app/src/main/res/drawable/retangle_drawable.xml b/examples/0603/MyAndroidTutorial/app/src/main/res/drawable/retangle_drawable.xml
new file mode 100644
index 0000000..51d1e84
--- /dev/null
+++ b/examples/0603/MyAndroidTutorial/app/src/main/res/drawable/retangle_drawable.xml
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/examples/0603/MyAndroidTutorial/app/src/main/res/drawable/select_color_icon.png b/examples/0603/MyAndroidTutorial/app/src/main/res/drawable/select_color_icon.png
new file mode 100644
index 0000000..8567d5e
Binary files /dev/null and b/examples/0603/MyAndroidTutorial/app/src/main/res/drawable/select_color_icon.png differ
diff --git a/examples/0603/MyAndroidTutorial/app/src/main/res/drawable/selected_icon.png b/examples/0603/MyAndroidTutorial/app/src/main/res/drawable/selected_icon.png
new file mode 100755
index 0000000..b891571
Binary files /dev/null and b/examples/0603/MyAndroidTutorial/app/src/main/res/drawable/selected_icon.png differ
diff --git a/examples/0603/MyAndroidTutorial/app/src/main/res/drawable/stop_icon.png b/examples/0603/MyAndroidTutorial/app/src/main/res/drawable/stop_icon.png
new file mode 100755
index 0000000..20df415
Binary files /dev/null and b/examples/0603/MyAndroidTutorial/app/src/main/res/drawable/stop_icon.png differ
diff --git a/examples/0603/MyAndroidTutorial/app/src/main/res/drawable/take_picture_icon.png b/examples/0603/MyAndroidTutorial/app/src/main/res/drawable/take_picture_icon.png
new file mode 100644
index 0000000..08fb514
Binary files /dev/null and b/examples/0603/MyAndroidTutorial/app/src/main/res/drawable/take_picture_icon.png differ
diff --git a/examples/0603/MyAndroidTutorial/app/src/main/res/layout/activity_about.xml b/examples/0603/MyAndroidTutorial/app/src/main/res/layout/activity_about.xml
new file mode 100644
index 0000000..211a9b6
--- /dev/null
+++ b/examples/0603/MyAndroidTutorial/app/src/main/res/layout/activity_about.xml
@@ -0,0 +1,36 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/examples/0603/MyAndroidTutorial/app/src/main/res/layout/activity_color.xml b/examples/0603/MyAndroidTutorial/app/src/main/res/layout/activity_color.xml
new file mode 100644
index 0000000..d25bbc5
--- /dev/null
+++ b/examples/0603/MyAndroidTutorial/app/src/main/res/layout/activity_color.xml
@@ -0,0 +1,16 @@
+
+
+
+
+
diff --git a/examples/0603/MyAndroidTutorial/app/src/main/res/layout/activity_item.xml b/examples/0603/MyAndroidTutorial/app/src/main/res/layout/activity_item.xml
new file mode 100644
index 0000000..80dc337
--- /dev/null
+++ b/examples/0603/MyAndroidTutorial/app/src/main/res/layout/activity_item.xml
@@ -0,0 +1,149 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/examples/0603/MyAndroidTutorial/app/src/main/res/layout/activity_main.xml b/examples/0603/MyAndroidTutorial/app/src/main/res/layout/activity_main.xml
new file mode 100644
index 0000000..30cdf98
--- /dev/null
+++ b/examples/0603/MyAndroidTutorial/app/src/main/res/layout/activity_main.xml
@@ -0,0 +1,43 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/examples/0603/MyAndroidTutorial/app/src/main/res/layout/activity_maps.xml b/examples/0603/MyAndroidTutorial/app/src/main/res/layout/activity_maps.xml
new file mode 100644
index 0000000..5de477b
--- /dev/null
+++ b/examples/0603/MyAndroidTutorial/app/src/main/res/layout/activity_maps.xml
@@ -0,0 +1,7 @@
+
diff --git a/examples/0603/MyAndroidTutorial/app/src/main/res/layout/activity_picture.xml b/examples/0603/MyAndroidTutorial/app/src/main/res/layout/activity_picture.xml
new file mode 100644
index 0000000..b667420
--- /dev/null
+++ b/examples/0603/MyAndroidTutorial/app/src/main/res/layout/activity_picture.xml
@@ -0,0 +1,10 @@
+
+
\ No newline at end of file
diff --git a/examples/0603/MyAndroidTutorial/app/src/main/res/layout/activity_play.xml b/examples/0603/MyAndroidTutorial/app/src/main/res/layout/activity_play.xml
new file mode 100644
index 0000000..52db308
--- /dev/null
+++ b/examples/0603/MyAndroidTutorial/app/src/main/res/layout/activity_play.xml
@@ -0,0 +1,38 @@
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/examples/0603/MyAndroidTutorial/app/src/main/res/layout/activity_record.xml b/examples/0603/MyAndroidTutorial/app/src/main/res/layout/activity_record.xml
new file mode 100644
index 0000000..63d9e36
--- /dev/null
+++ b/examples/0603/MyAndroidTutorial/app/src/main/res/layout/activity_record.xml
@@ -0,0 +1,51 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/examples/0603/MyAndroidTutorial/app/src/main/res/layout/item_app_widget.xml b/examples/0603/MyAndroidTutorial/app/src/main/res/layout/item_app_widget.xml
new file mode 100644
index 0000000..92bb45a
--- /dev/null
+++ b/examples/0603/MyAndroidTutorial/app/src/main/res/layout/item_app_widget.xml
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/examples/0603/MyAndroidTutorial/app/src/main/res/layout/single_item.xml b/examples/0603/MyAndroidTutorial/app/src/main/res/layout/single_item.xml
new file mode 100644
index 0000000..8acc60a
--- /dev/null
+++ b/examples/0603/MyAndroidTutorial/app/src/main/res/layout/single_item.xml
@@ -0,0 +1,61 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/examples/0603/MyAndroidTutorial/app/src/main/res/menu/menu_main.xml b/examples/0603/MyAndroidTutorial/app/src/main/res/menu/menu_main.xml
new file mode 100644
index 0000000..3ea061c
--- /dev/null
+++ b/examples/0603/MyAndroidTutorial/app/src/main/res/menu/menu_main.xml
@@ -0,0 +1,56 @@
+
+
+
+
+
+
+
+
+
+
+
+ -
+
+
+
+
+
+
+
diff --git a/examples/0603/MyAndroidTutorial/app/src/main/res/transition/explode_transition.xml b/examples/0603/MyAndroidTutorial/app/src/main/res/transition/explode_transition.xml
new file mode 100644
index 0000000..d2320b1
--- /dev/null
+++ b/examples/0603/MyAndroidTutorial/app/src/main/res/transition/explode_transition.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/examples/0603/MyAndroidTutorial/app/src/main/res/transition/fade_transition.xml b/examples/0603/MyAndroidTutorial/app/src/main/res/transition/fade_transition.xml
new file mode 100644
index 0000000..ea54111
--- /dev/null
+++ b/examples/0603/MyAndroidTutorial/app/src/main/res/transition/fade_transition.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/examples/0603/MyAndroidTutorial/app/src/main/res/transition/slide_transition.xml b/examples/0603/MyAndroidTutorial/app/src/main/res/transition/slide_transition.xml
new file mode 100644
index 0000000..afd9869
--- /dev/null
+++ b/examples/0603/MyAndroidTutorial/app/src/main/res/transition/slide_transition.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/examples/0603/MyAndroidTutorial/app/src/main/res/values-en/strings.xml b/examples/0603/MyAndroidTutorial/app/src/main/res/values-en/strings.xml
new file mode 100644
index 0000000..c532533
--- /dev/null
+++ b/examples/0603/MyAndroidTutorial/app/src/main/res/values-en/strings.xml
@@ -0,0 +1,12 @@
+
+
+
+ MyAndroidTutorial
+ Hello world!
+ Settings
+
+ Title
+ Enter title
+ Content
+ Enter content
+
diff --git a/examples/0603/MyAndroidTutorial/app/src/main/res/values-v14/dimens.xml b/examples/0603/MyAndroidTutorial/app/src/main/res/values-v14/dimens.xml
new file mode 100644
index 0000000..4db8c59
--- /dev/null
+++ b/examples/0603/MyAndroidTutorial/app/src/main/res/values-v14/dimens.xml
@@ -0,0 +1,10 @@
+
+
+
+
+ 0dp
+
+
\ No newline at end of file
diff --git a/examples/0603/MyAndroidTutorial/app/src/main/res/values-v21/styles.xml b/examples/0603/MyAndroidTutorial/app/src/main/res/values-v21/styles.xml
new file mode 100644
index 0000000..606e45f
--- /dev/null
+++ b/examples/0603/MyAndroidTutorial/app/src/main/res/values-v21/styles.xml
@@ -0,0 +1,12 @@
+
+
+
+
\ No newline at end of file
diff --git a/examples/0603/MyAndroidTutorial/app/src/main/res/values-w820dp/dimens.xml b/examples/0603/MyAndroidTutorial/app/src/main/res/values-w820dp/dimens.xml
new file mode 100644
index 0000000..63fc816
--- /dev/null
+++ b/examples/0603/MyAndroidTutorial/app/src/main/res/values-w820dp/dimens.xml
@@ -0,0 +1,6 @@
+
+
+ 64dp
+
diff --git a/examples/0603/MyAndroidTutorial/app/src/main/res/values/colors.xml b/examples/0603/MyAndroidTutorial/app/src/main/res/values/colors.xml
new file mode 100644
index 0000000..6b13c1d
--- /dev/null
+++ b/examples/0603/MyAndroidTutorial/app/src/main/res/values/colors.xml
@@ -0,0 +1,7 @@
+
+
+ #CCCCCC
+ #AAAAAA
+ #DD999999
+ #111111
+
\ No newline at end of file
diff --git a/examples/0603/MyAndroidTutorial/app/src/main/res/values/dimens.xml b/examples/0603/MyAndroidTutorial/app/src/main/res/values/dimens.xml
new file mode 100644
index 0000000..7fa5a6f
--- /dev/null
+++ b/examples/0603/MyAndroidTutorial/app/src/main/res/values/dimens.xml
@@ -0,0 +1,13 @@
+
+
+ 16dp
+ 16dp
+
+ 6dp
+ 24sp
+ 2dp
+
+ 8dp
+
+ 2sp
+
diff --git a/examples/0603/MyAndroidTutorial/app/src/main/res/values/strings.xml b/examples/0603/MyAndroidTutorial/app/src/main/res/values/strings.xml
new file mode 100644
index 0000000..a59f440
--- /dev/null
+++ b/examples/0603/MyAndroidTutorial/app/src/main/res/values/strings.xml
@@ -0,0 +1,59 @@
+
+
+
+ MyAndroidTutorial
+ Hello world!
+ Settings
+ 標題
+ 輸入標題
+ 內容
+ 輸入內容
+ 這是Android Tutorial應用程式
+ AboutActivity
+ 版本:AndroidTutorial_0.2.4
+ ItemActivity
+ ColorActivity
+ 刪除
+ 確定要刪除 %1$d 個項目?
+ 預設的顏色
+ 新增記事的預設顏色
+
+ 預設提醒時間
+ 在指定的時間之前通知
+
+
+ - 五分鐘
+ - 十分鐘
+ - 二十分鐘
+ - 三十分鐘
+ - 六十分鐘
+
+
+
+ - 5
+ - 10
+ - 20
+ - 30
+ - 60
+
+
+ 語音備忘
+ 播放語音備忘
+ 播放
+ 重新錄製
+ Map
+
+ 記事儲存的位置
+ 更新或清除儲存的位置資訊?
+ 更新
+ 清除
+
+ 目前位置
+ 是否儲存目前位置?
+
+ 裝置沒有安裝Google Play服務
+ EXAMPLE
+ Configure
+ Add widget
+
+
diff --git a/examples/0603/MyAndroidTutorial/app/src/main/res/values/styles.xml b/examples/0603/MyAndroidTutorial/app/src/main/res/values/styles.xml
new file mode 100644
index 0000000..766ab99
--- /dev/null
+++ b/examples/0603/MyAndroidTutorial/app/src/main/res/values/styles.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
diff --git a/examples/0603/MyAndroidTutorial/app/src/main/res/xml/item_app_widget_info.xml b/examples/0603/MyAndroidTutorial/app/src/main/res/xml/item_app_widget_info.xml
new file mode 100644
index 0000000..043d27b
--- /dev/null
+++ b/examples/0603/MyAndroidTutorial/app/src/main/res/xml/item_app_widget_info.xml
@@ -0,0 +1,15 @@
+
+
+
\ No newline at end of file
diff --git a/examples/0603/MyAndroidTutorial/app/src/main/res/xml/mypreference.xml b/examples/0603/MyAndroidTutorial/app/src/main/res/xml/mypreference.xml
new file mode 100644
index 0000000..33e714c
--- /dev/null
+++ b/examples/0603/MyAndroidTutorial/app/src/main/res/xml/mypreference.xml
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/examples/0603/MyAndroidTutorial/app/src/release/res/values/google_maps_api.xml b/examples/0603/MyAndroidTutorial/app/src/release/res/values/google_maps_api.xml
new file mode 100644
index 0000000..c4e2431
--- /dev/null
+++ b/examples/0603/MyAndroidTutorial/app/src/release/res/values/google_maps_api.xml
@@ -0,0 +1,18 @@
+
+
+
+ YOUR_KEY_HERE
+
+
diff --git a/examples/0603/MyAndroidTutorial/build.gradle b/examples/0603/MyAndroidTutorial/build.gradle
new file mode 100644
index 0000000..6356aab
--- /dev/null
+++ b/examples/0603/MyAndroidTutorial/build.gradle
@@ -0,0 +1,19 @@
+// Top-level build file where you can add configuration options common to all sub-projects/modules.
+
+buildscript {
+ repositories {
+ jcenter()
+ }
+ dependencies {
+ classpath 'com.android.tools.build:gradle:1.0.0'
+
+ // NOTE: Do not place your application dependencies here; they belong
+ // in the individual module build.gradle files
+ }
+}
+
+allprojects {
+ repositories {
+ jcenter()
+ }
+}
diff --git a/examples/0603/MyAndroidTutorial/gradle.properties b/examples/0603/MyAndroidTutorial/gradle.properties
new file mode 100644
index 0000000..1d3591c
--- /dev/null
+++ b/examples/0603/MyAndroidTutorial/gradle.properties
@@ -0,0 +1,18 @@
+# Project-wide Gradle settings.
+
+# IDE (e.g. Android Studio) users:
+# Gradle settings configured through the IDE *will override*
+# any settings specified in this file.
+
+# For more details on how to configure your build environment visit
+# http://www.gradle.org/docs/current/userguide/build_environment.html
+
+# Specifies the JVM arguments used for the daemon process.
+# The setting is particularly useful for tweaking memory settings.
+# Default value: -Xmx10248m -XX:MaxPermSize=256m
+# org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
+
+# When configured, Gradle will run in incubating parallel mode.
+# This option should only be used with decoupled projects. More details, visit
+# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
+# org.gradle.parallel=true
\ No newline at end of file
diff --git a/examples/0603/MyAndroidTutorial/gradle/wrapper/gradle-wrapper.jar b/examples/0603/MyAndroidTutorial/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 0000000..8c0fb64
Binary files /dev/null and b/examples/0603/MyAndroidTutorial/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/examples/0603/MyAndroidTutorial/gradle/wrapper/gradle-wrapper.properties b/examples/0603/MyAndroidTutorial/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 0000000..0c71e76
--- /dev/null
+++ b/examples/0603/MyAndroidTutorial/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,6 @@
+#Wed Apr 10 15:27:10 PDT 2013
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip
diff --git a/examples/0603/MyAndroidTutorial/gradlew b/examples/0603/MyAndroidTutorial/gradlew
new file mode 100755
index 0000000..91a7e26
--- /dev/null
+++ b/examples/0603/MyAndroidTutorial/gradlew
@@ -0,0 +1,164 @@
+#!/usr/bin/env bash
+
+##############################################################################
+##
+## Gradle start up script for UN*X
+##
+##############################################################################
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS=""
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn ( ) {
+ echo "$*"
+}
+
+die ( ) {
+ echo
+ echo "$*"
+ echo
+ exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+case "`uname`" in
+ CYGWIN* )
+ cygwin=true
+ ;;
+ Darwin* )
+ darwin=true
+ ;;
+ MINGW* )
+ msys=true
+ ;;
+esac
+
+# For Cygwin, ensure paths are in UNIX format before anything is touched.
+if $cygwin ; then
+ [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
+fi
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+ ls=`ls -ld "$PRG"`
+ link=`expr "$ls" : '.*-> \(.*\)$'`
+ if expr "$link" : '/.*' > /dev/null; then
+ PRG="$link"
+ else
+ PRG=`dirname "$PRG"`"/$link"
+ fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >&-
+APP_HOME="`pwd -P`"
+cd "$SAVED" >&-
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD="$JAVA_HOME/jre/sh/java"
+ else
+ JAVACMD="$JAVA_HOME/bin/java"
+ fi
+ if [ ! -x "$JAVACMD" ] ; then
+ die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+ fi
+else
+ JAVACMD="java"
+ which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
+ MAX_FD_LIMIT=`ulimit -H -n`
+ if [ $? -eq 0 ] ; then
+ if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+ MAX_FD="$MAX_FD_LIMIT"
+ fi
+ ulimit -n $MAX_FD
+ if [ $? -ne 0 ] ; then
+ warn "Could not set maximum file descriptor limit: $MAX_FD"
+ fi
+ else
+ warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+ fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+ GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin, switch paths to Windows format before running java
+if $cygwin ; then
+ APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+ CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+
+ # We build the pattern for arguments to be converted via cygpath
+ ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+ SEP=""
+ for dir in $ROOTDIRSRAW ; do
+ ROOTDIRS="$ROOTDIRS$SEP$dir"
+ SEP="|"
+ done
+ OURCYGPATTERN="(^($ROOTDIRS))"
+ # Add a user-defined pattern to the cygpath arguments
+ if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+ OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+ fi
+ # Now convert the arguments - kludge to limit ourselves to /bin/sh
+ i=0
+ for arg in "$@" ; do
+ CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+ CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
+
+ if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
+ eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+ else
+ eval `echo args$i`="\"$arg\""
+ fi
+ i=$((i+1))
+ done
+ case $i in
+ (0) set -- ;;
+ (1) set -- "$args0" ;;
+ (2) set -- "$args0" "$args1" ;;
+ (3) set -- "$args0" "$args1" "$args2" ;;
+ (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+ (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+ (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+ (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+ (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+ (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+ esac
+fi
+
+# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
+function splitJvmOpts() {
+ JVM_OPTS=("$@")
+}
+eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
+JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
+
+exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
diff --git a/examples/0603/MyAndroidTutorial/gradlew.bat b/examples/0603/MyAndroidTutorial/gradlew.bat
new file mode 100644
index 0000000..aec9973
--- /dev/null
+++ b/examples/0603/MyAndroidTutorial/gradlew.bat
@@ -0,0 +1,90 @@
+@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS=
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto init
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto init
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:init
+@rem Get command-line arguments, handling Windowz variants
+
+if not "%OS%" == "Windows_NT" goto win9xME_args
+if "%@eval[2+2]" == "4" goto 4NT_args
+
+:win9xME_args
+@rem Slurp the command line arguments.
+set CMD_LINE_ARGS=
+set _SKIP=2
+
+:win9xME_args_slurp
+if "x%~1" == "x" goto execute
+
+set CMD_LINE_ARGS=%*
+goto execute
+
+:4NT_args
+@rem Get arguments from the 4NT Shell from JP Software
+set CMD_LINE_ARGS=%$
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/examples/0603/MyAndroidTutorial/settings.gradle b/examples/0603/MyAndroidTutorial/settings.gradle
new file mode 100644
index 0000000..e7b4def
--- /dev/null
+++ b/examples/0603/MyAndroidTutorial/settings.gradle
@@ -0,0 +1 @@
+include ':app'
diff --git a/images/icons/alarm_icon.png b/images/icons/alarm_icon.png
new file mode 100755
index 0000000..c6cac88
Binary files /dev/null and b/images/icons/alarm_icon.png differ
diff --git a/images/icons/location_icon.png b/images/icons/location_icon.png
new file mode 100755
index 0000000..4c3c514
Binary files /dev/null and b/images/icons/location_icon.png differ
diff --git a/images/icons/pause_icon.png b/images/icons/pause_icon.png
new file mode 100755
index 0000000..a5aee6f
Binary files /dev/null and b/images/icons/pause_icon.png differ
diff --git a/images/icons/play_icon.png b/images/icons/play_icon.png
new file mode 100755
index 0000000..6a40cd5
Binary files /dev/null and b/images/icons/play_icon.png differ
diff --git a/images/icons/record_dark_icon.png b/images/icons/record_dark_icon.png
new file mode 100755
index 0000000..bcf83ca
Binary files /dev/null and b/images/icons/record_dark_icon.png differ
diff --git a/images/icons/record_red_icon.png b/images/icons/record_red_icon.png
new file mode 100755
index 0000000..2b44af0
Binary files /dev/null and b/images/icons/record_red_icon.png differ
diff --git a/images/icons/record_sound_icon.png b/images/icons/record_sound_icon.png
new file mode 100755
index 0000000..a1382ac
Binary files /dev/null and b/images/icons/record_sound_icon.png differ
diff --git a/images/icons/select_color_icon.png b/images/icons/select_color_icon.png
new file mode 100755
index 0000000..8567d5e
Binary files /dev/null and b/images/icons/select_color_icon.png differ
diff --git a/images/icons/selected_icon.png b/images/icons/selected_icon.png
new file mode 100755
index 0000000..b891571
Binary files /dev/null and b/images/icons/selected_icon.png differ
diff --git a/images/icons/stop_icon.png b/images/icons/stop_icon.png
new file mode 100755
index 0000000..20df415
Binary files /dev/null and b/images/icons/stop_icon.png differ
diff --git a/images/icons/take_picture_icon.png b/images/icons/take_picture_icon.png
new file mode 100755
index 0000000..08fb514
Binary files /dev/null and b/images/icons/take_picture_icon.png differ
diff --git a/images/launcher/mipmap-hdpi/ic_launcher.png b/images/launcher/mipmap-hdpi/ic_launcher.png
new file mode 100755
index 0000000..1a74b60
Binary files /dev/null and b/images/launcher/mipmap-hdpi/ic_launcher.png differ
diff --git a/images/launcher/mipmap-mdpi/ic_launcher.png b/images/launcher/mipmap-mdpi/ic_launcher.png
new file mode 100755
index 0000000..6d99fde
Binary files /dev/null and b/images/launcher/mipmap-mdpi/ic_launcher.png differ
diff --git a/images/launcher/mipmap-xhdpi/ic_launcher.png b/images/launcher/mipmap-xhdpi/ic_launcher.png
new file mode 100755
index 0000000..8b6e57a
Binary files /dev/null and b/images/launcher/mipmap-xhdpi/ic_launcher.png differ
diff --git a/images/launcher/mipmap-xxhdpi/ic_launcher.png b/images/launcher/mipmap-xxhdpi/ic_launcher.png
new file mode 100755
index 0000000..43a1744
Binary files /dev/null and b/images/launcher/mipmap-xxhdpi/ic_launcher.png differ
diff --git a/images/launcher/mipmap-xxxhdpi/ic_launcher.png b/images/launcher/mipmap-xxxhdpi/ic_launcher.png
new file mode 100755
index 0000000..779edb4
Binary files /dev/null and b/images/launcher/mipmap-xxxhdpi/ic_launcher.png differ
diff --git a/images/migrate/work/2015-02-23_15-44-38.tiff b/images/migrate/work/2015-02-23_15-44-38.tiff
deleted file mode 100644
index bd2663d..0000000
Binary files a/images/migrate/work/2015-02-23_15-44-38.tiff and /dev/null differ
diff --git a/images/migrate/work/2015-02-23_15-44-46.tiff b/images/migrate/work/2015-02-23_15-44-46.tiff
deleted file mode 100644
index b412de5..0000000
Binary files a/images/migrate/work/2015-02-23_15-44-46.tiff and /dev/null differ
diff --git a/images/migrate/work/2015-02-23_15-44-52.tiff b/images/migrate/work/2015-02-23_15-44-52.tiff
deleted file mode 100644
index 9f894a9..0000000
Binary files a/images/migrate/work/2015-02-23_15-44-52.tiff and /dev/null differ
diff --git a/images/migrate/work/2015-02-23_15-44-57.tiff b/images/migrate/work/2015-02-23_15-44-57.tiff
deleted file mode 100644
index 117fc87..0000000
Binary files a/images/migrate/work/2015-02-23_15-44-57.tiff and /dev/null differ
diff --git a/images/migrate/work/2015-02-23_15-45-04.tiff b/images/migrate/work/2015-02-23_15-45-04.tiff
deleted file mode 100644
index d252a80..0000000
Binary files a/images/migrate/work/2015-02-23_15-45-04.tiff and /dev/null differ
diff --git a/images/migrate/work/2015-02-23_15-45-39.tiff b/images/migrate/work/2015-02-23_15-45-39.tiff
deleted file mode 100644
index c8763b5..0000000
Binary files a/images/migrate/work/2015-02-23_15-45-39.tiff and /dev/null differ
diff --git a/images/migrate/work/2015-02-23_15-46-09.tiff b/images/migrate/work/2015-02-23_15-46-09.tiff
deleted file mode 100644
index 461844e..0000000
Binary files a/images/migrate/work/2015-02-23_15-46-09.tiff and /dev/null differ
diff --git a/images/migrate/work/2015-02-23_15-46-53.tiff b/images/migrate/work/2015-02-23_15-46-53.tiff
deleted file mode 100644
index de3211c..0000000
Binary files a/images/migrate/work/2015-02-23_15-46-53.tiff and /dev/null differ
diff --git a/images/migrate/work/2015-02-23_15-47-24.tiff b/images/migrate/work/2015-02-23_15-47-24.tiff
deleted file mode 100644
index 9ef255f..0000000
Binary files a/images/migrate/work/2015-02-23_15-47-24.tiff and /dev/null differ
diff --git a/images/migrate/work/2015-02-23_15-48-10.tiff b/images/migrate/work/2015-02-23_15-48-10.tiff
deleted file mode 100644
index 8075251..0000000
Binary files a/images/migrate/work/2015-02-23_15-48-10.tiff and /dev/null differ