Location Based Services - LBS 系統實例 – 單車練習曲 講師:蘇怡仁
Outline 系統簡介 系統規劃 系統環境建置 單車練習曲建置
Chapter1. 系統簡介
系統簡介 1. 創作動機 近年來單車活動逐漸盛行,車隊活動的人員管理以及安全這兩方面將是個重要議題,為了解決這兩方面的問題,便開發了這項系統來降低意外事件的發生。
系統簡介 1. 創作動機 5.2 km 在團隊單車行徑中,難以掌握各個團員的即時狀況 居然完全沒人發現我摔車了
系統簡介 1. 創作動機 單車練習曲因應而生 因此若透過單車練習曲來辦活動的話則可以幫助解決這個問題
系統簡介 2. 系統功能 會員註冊 好友清單 行動好友 使用者 活動建立 邀請參加 單車活動 顯示路線 隊員位置 路線規劃 位置異常
系統簡介 3. 系統架構 Client Web Server Web Service Database Internet
系統簡介 4. 系統操作流程 會員註冊 好友清單 行動好友 暱稱 登入 1. 輸入使用者名稱 2. 點選登入 3. 透過 simid 完成註冊, 若已註冊則僅更新名稱 登入頁面 朋友清單 活動 4. 點選朋友清單 功能選單 朋友清單 Andy Marcus Marry 5. 列出所已註冊過的人
系統簡介 4. 系統操作流程 活動建立 邀請參加 單車活動 朋友清單 單車活動 1. 點選活動 功能選單 活動清單 夜戰阿里山 燕巢一日遊 阿公店車道 建立活動 2. 點選建立活動 活動新增與邀請 Andy Marcus Marry 5. 輸入活動標題 活動標題 建立 7. 點選建立 6. 選取參與人 下一頁 3. 點擊活動目的地 4. 點選下一頁 活動位置(地圖打點)
系統簡介 4. 系統操作流程 顯示路線 隊員位置 路線規劃 異常偵測 功能選單 活動清單 地圖顯示 3. 顯示規劃路線 異常偵測 與領隊超過一段距離 隊員會由藍色轉紫色 建立活動 夜戰阿里山 朋友清單 燕巢一日遊 2. 選擇參與活動 阿公店車道 單車活動 4. 顯示隊員位置 粉:起點與終點 橘:領隊 黃:自己 藍:其他隊員 紫:狀態異常 1. 點選活動
Chapter2. 系統規劃
系統規劃 1. 系統功能介面規劃 單車活動 3 4 6 7 1 2 5 8
系統規劃 2.ER-Model 1 n m 1 simid name aid aid title Join User Activity simid:varchar 20, 用來記錄手機 sim card ID, 作為 User 的主鍵並當作登入帳號 name:varchar 20, 用來記錄使用者姓名,手機端每次登入即會更新使用者姓名 lat:double, 用來記錄使用者目前位置的(緯度) lng : double, 用來記錄使用者目前位置的(經度) Join aid : int, 用來記錄活度被那些人所參加 simid : varchar 20, 用來記錄使用者參加哪些活動 Activity aid : int (auto_increment), 用來當做 Activity 的主鍵,作為每一個單車活動的編號,設定為自動編號 title : varchar 20, 用來記錄單車活動的標題 direction : text, 紀錄經過 Google Directions 所規劃的路徑編碼 master_simid : varchar 20, 紀錄活動領隊的simid (活動建立者即為領隊) lat lng simid direction master_simid
系統規劃 3. Web Service 規劃 Service 功能描述 需求參數 返回格式 返回參數 備註 user.php 列出所有好友清單 無 json simid name location.php 目前User所在經緯度 lat lng activity.php 列出目前User所參與活動清單 aid title
系統規劃 3. Web Service 規劃 Service 功能描述 需求參數 返回格式 返回參數 備註 updateLocation.php 更新目前 Use經緯度 simid json response lat lng login.php 帳號登入(若沒帳號則會註冊,若有帳號則會更新姓名) name addActivity.php 新增單車活動,與新增邀請好友與紀錄領隊(創建活動者)與規畫路徑功能 title direction master aid friend[simid] 請使用","做間隔 joinUser.php 查詢參與活動人的相關資訊
系統規劃 4.單車練習曲 Use Case Diagram 單車練習曲 Service 使用者 活動申請 地圖打點 上傳使用者 列出會員 <<include>> 活動申請 <<include>> 上傳使用者 位置 列出會員 Service 使用者 Google Maps <<extend>> <<include>> 活動列表 <<include>> 異常偵測 路徑規劃
系統規劃 5.單車練習曲 Class Diagram <<persistent>> User - simid : String - name : String - lat : Double - lng : Double + getName(simid): String + getPosition(simid): String <<persistent>> Join - simid : String - aid : Int + invite(simid,aid) + getParticipants(aid): List + getActivity(simid): List <<persistent>> Activity - aid : Int - title : String - direction : String - master_simid : String + getActivityInfo(aid): Array + getDirection(aid): String 0…* 0…* 1 1 1 1 <<transient>> Background Service + setPosition(simid,aid)
系統規劃 6. 單車練習曲 Sequence Diagram :會員 :參加 :單車活動 Web Android Service getUserList():array getUserList():json return return getLocation(simid):array getLocation(simid):json return return getJoinActivity(simid):array getJoinActivity(simid):json getTitle(aid):String return return return
(title,direction,master,friend[]):boolean 系統規劃 6. 單車練習曲 Sequence Diagram Web Service Android :會員 :參加 :單車活動 login(simid,name):boolean checkUser(simid,name):json return return addActivity (title,direction,master,friend[]):boolean addActivity(title,lat,lng) return:aid addParticipant(aid,friend[]):json return
系統規劃 6. 單車練習曲 Sequence Diagram :會員 :參加 :單車活動 Web Android Service getJoinUser(aid,simid):array getJoinUser(aid,simid):json return:title,direction,master_simid getParticipant(aid) return:array() return
系統規劃 6. 單車練習曲 Sequence Diagram :會員 Android (Background Service) Web updateLocation(simid,lat,lng):boolean updateLocation(simid,lat,lng):json return return
Chapter3. 系統環境建置
系統環境建置 2.建置資料庫 點擊Admin,啟動MySQL管理介面
系統環境建置 2.建置資料庫 1 4 bicyclemaps utf8_general_ci 2 3
系統環境建置 3.建置資料庫欄位 3 user 4 1 2
系統環境建置 2.建置資料庫欄位 1 建立user相關欄位 2
系統環境建置 2.建置資料庫欄位 3 join 3 1 2
系統環境建置 2.建置資料庫欄位 1 建立join相關欄位 2
系統環境建置 2.建置資料庫欄位 3 activity 4 1 2
系統環境建置 2.建置資料庫欄位 1 建立activity相關欄位 2
Chapter4. 單車練習曲建置
單車練習曲建置 3. 權限說明 Uses Permission INTERNET 網路存取權限 ACCESS_FINE_LOCATION GPS定位權限 ACCESS_COARSE_LOCATION 無線網路定位 ACCESS_NETWORK_STATE 允許應用程序訪問有關GSM網絡信息 WRITE_EXTERNAL_STORAGE 允許應用程序寫入外部存儲器
單車練習曲建置 3. 權限說明 Uses Permission Permission MAPS_RECEIVE 允許下載圖資 READ_PHONE_STATE 允許存取 SIM Card 資訊 READ_GSERVICES 允許 API 訪問 Google Web 的服務 Permission MAPS_RECEIVE 允許下載圖資
在其他user-permission底下貼上READ_GSERVICES權限 單車練習曲建置 在其他user-permission底下貼上READ_GSERVICES權限 只有READ_GSERVICES的權限需要透過手動的方式加入到AndroidManifest.xml程式碼當中 2 1 <uses-permission android:name="com.google.android.providers.gsf.permission.READ_GSERVICES"/>
單車練習曲建置 4. 物件參數設定 value.java 1 2 3 4 5 6 7 8 9 package com.stu.bicycle_maps; 2 public final class value { 3 public static String simid = ""; 4 public static String name = ""; 5 public static String service = 6 "http://localhost/service/bicyclemaps/"; 7 /* 每個頁面可能會用到的參數, 8 都會先宣告在 value 物件當中 9 而 service 參數要設定成自己的位置 /* 10 11 } 打開 value.java
單車練習曲建置 5. 登入功能描述 Android Web Service MainActivity.java 1 Require 將 SIMID 與 NAME 傳送給 Service 後,便可直接登入到功能頁面 Require response simid Web Service name JSON login.php GET 第一次登入會透過SIM CARD ID 當作帳號與輸入姓名來新增一筆帳號,再次登入僅會更新姓名資料 Response
單車練習曲建置 5-1. 登入功能 Service login.php 1 2 3 4 5 6 7 8 9 10 11 if (isset($_GET['simid']) && isset($_GET['name'])) 2 { 3 $simid = mysql_real_escape_string($_GET['simid']); 4 $name = mysql_real_escape_string($_GET['name']); 5 //透過 GET 代入 simid (SIM Card ID) 與 name (名稱) 參數 6 include("../connect.php"); 7 $sql = "SELECT * FROM user WHERE simid = '$simid'"; 8 $result = mysql_query($sql, $link) or die("oops"); 9 //指定查詢 $simid 是否有註冊紀錄,若回傳資料筆數為 0 則表示未註冊 1 則已註冊 10 if(mysql_num_rows($result) != 0) { 11 $sql = "Update user SET name = '$name' WHERE simid = '$simid'";
單車練習曲建置 12 // 若已註冊則更新這個 $simid 的 name 欄位,更改名稱 13 $result = mysql_query($sql, $link) or die("oops"); 14 }else{ 15 $sql = "INSERT INTO user(simid,name,lat,lng) VALUES('$simid','$name','0','0')"; 16 //若未註冊則新增一筆使用者紀錄,預設 lat 與 lng 經緯度位置為 0 17 18 } 19 if($result) { 20 $data['responese'] = 'success'; 21 echo json_encode($data); 22 23 24
單車練習曲建置 5-2. 登入功能 Layout activity_main.xml 1 2 3 4 5 6 7 8 9 10 11 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" 2 xmlns:tools="http://schemas.android.com/tools" 3 android:layout_width="match_parent" 4 android:layout_height="match_parent" 5 android:paddingBottom="@dimen/activity_vertical_margin" 6 android:paddingLeft="@dimen/activity_horizontal_margin" 7 android:paddingRight="@dimen/activity_horizontal_margin" 8 android:paddingTop="@dimen/activity_vertical_margin" 9 tools:context=".MainActivity" > 10 <Button 11 android:id="@+id/button1"
單車練習曲建置 12 android:layout_width="wrap_content" 13 android:layout_height="wrap_content" 14 android:layout_alignParentBottom="true" 15 android:layout_centerHorizontal="true" 16 android:layout_marginBottom="78dp" 17 android:text="登入" /> 18 <EditText 19 android:id="@+id/editText1" 20 21 22 android:layout_alignParentTop="true" 23 24 android:layout_marginTop="95dp"
單車練習曲建置 25 android:ems="10" 26 android:inputType="textMultiLine" > 27 <requestFocus /> 28 </EditText> 29 <TextView 30 android:id="@+id/textView1" 31 android:layout_width="wrap_content" 32 android:layout_height="wrap_content" 33 android:layout_alignBottom="@+id/editText1" 34 android:layout_centerHorizontal="true" 35 android:layout_marginBottom="51dp" 36 android:text="名稱" 37 android:textAppearance="?android:attr/textAppearanceLarge" /> 38 </RelativeLayout>
單車練習曲建置 5-3. 登入功能 MainActivity.java 1 2 3 4 5 6 7 8 9 10 11 import org.apache.http.HttpEntity; 3 import org.apache.http.HttpResponse; 4 import org.apache.http.client.HttpClient; 5 import org.apache.http.client.methods.HttpGet; 6 import org.apache.http.impl.client.DefaultHttpClient; 7 import org.apache.http.util.EntityUtils; 8 import android.app.Activity; 9 import android.content.Context; 10 import android.content.Intent; 11 import android.os.Bundle; Android:MainActivity.java 打開 MainActivity.java
單車練習曲建置 12 import android.telephony.TelephonyManager; 13 import android.util.Log; 14 import android.view.View; 15 import android.view.View.OnClickListener; 16 import android.widget.Button; 17 import android.widget.EditText; 18 19 public class MainActivity extends Activity { 20 EditText name; 21 Button login; 22 23 protected void onCreate(Bundle savedInstanceState) { 24 super.onCreate(savedInstanceState);
單車練習曲建置 25 setContentView(R.layout.activity_main); 26 findViwe(); 27 //前置先與 Layout 元件作連結 28 simid(); 29 //取得 SIM Card ID 30 initListener(); 31 32 } 33 public void findViwe() { 34 name = (EditText) findViewById(R.id.editText1); 35 login = (Button) findViewById(R.id.button1); 36 //連結 EditText 與 Button 元件,並給定到參數中 37
單車練習曲建置 38 39 40 41 42 43 44 45 46 47 48 49 50 public void simid() { TelephonyManager manager = 40 (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE); 41 value.simid = manager.getDeviceId(); 42 //透過 TelephonyManager 取得 SimCard ID,存放到 value.simid 43 } 44 45 public void initListener() { 46 login.setOnClickListener(new OnClickListener() { 47 public void onClick(View v) { 48 value.name = name.getText().toString(); 49 login Login = new login(); 50 Login.start();
單車練習曲建置 51 52 53 54 55 並在執行 Login 執行緒後,轉跳功能選擇頁 /* 56 } 57 }); 58 59 60 Intent intent = new Intent(); 52 intent.setClass(MainActivity.this, select.class); 53 startActivity(intent); 54 /* 將 name 當中輸入的字串設定給 value 物件, 55 並在執行 Login 執行緒後,轉跳功能選擇頁 /* 56 } 57 }); 58 59 60 61 62 63
單車練習曲建置 64 class login extends Thread { 65 66 public void run() { 67 super.run(); 68 try { 69 HttpClient client = new DefaultHttpClient(); 70 String url = value.service + "login.php?simid=" + value.simid 71 + "&name=" + value.name; 72 //將 value 物件中的 service 與 simid 與 name 透過 GET 呼叫 login.php 73 HttpGet get = new HttpGet(url); 74 HttpResponse response = client.execute(get); 75 //執行 url 設定的 Login Service 76
單車練習曲建置 77 HttpEntity resEntity = response.getEntity(); 78 String result = EntityUtils.toString(resEntity); 79 //回傳執行的結果並存放在 result 當中 80 } catch (Exception e) { 81 e.printStackTrace(); 82 } 83 84 85 86 87 88 89
單車練習曲建置 6. 功能清單功能描述 Android Web Service 3 select.php 單車活動 select.php backgroundService.php Require 登入後提供兩個按鈕功能選單,並啟動 Background Service 來不斷更新當前位置資訊 (當觸發 Location Change 事件後作更新) 2 simid response lat Web Service lng JSON GET updateLocation.php 更新該 simid 用戶目前所處經緯度位置 Response
單車練習曲建置 6-1. 更新用戶位置資訊 Service updateLocation.php 1 2 3 4 5 6 7 8 9 10 if (isset($_GET['simid']) && isset($_GET['lat']) && isset($_GET['lng'])) { 3 { 4 $simid = mysql_real_escape_string($_GET['simid']); 5 $lat = mysql_real_escape_string($_GET['lat']); 6 $lng = mysql_real_escape_string($_GET['lng']); 7 //透過 GET 代入 simid (SIM Card ID) 與 lat, lng (經緯度)參數 8 include("../connect.php"); 9 $sql = "Update user SET lat = '$lat',lng = '$lng' WHERE simid = '$simid'"; 10 11
單車練習曲建置 12 $result = mysql_query($sql, $link) or die("oops"); 13 //指定查詢 $simid 是否有註冊紀錄,若回傳資料筆數為 0 則表示未註冊 1 則已註冊 14 15 if($result) 16 { 17 $data['responese'] = 'success'; 18 echo json_encode($data); 19 } 20 21 22 23 24
單車練習曲建置 6-2. 功能選單 Layout select.xml 1 2 3 4 5 6 7 8 9 10 11 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" 2 android:id="@+id/RelativeLayout1" 3 android:layout_width="match_parent" 4 android:layout_height="match_parent" 5 android:orientation="vertical" > 6 <Button 7 android:id="@+id/button1" 8 android:layout_width="wrap_content" 9 android:layout_height="wrap_content" 10 android:layout_alignParentLeft="true" 11 android:layout_alignParentTop="true"
單車練習曲建置 12 android:layout_marginLeft="105dp" 13 android:layout_marginTop="68dp" 14 android:text="朋友清單" /> 15 <Button 16 android:id="@+id/button2" 17 android:layout_width="wrap_content" 18 android:layout_height="wrap_content" 19 android:layout_alignLeft="@+id/button1" 20 android:layout_alignRight="@+id/button1" 21 android:layout_below="@+id/button1" 22 android:layout_marginTop="131dp" 23 android:text="單車活動" /> 24 </RelativeLayout>
單車練習曲建置 6-3. Activity 註冊 AndroidManifest.xml 3 1 2
單車練習曲建置 6-3. Activity 註冊 新增一組 Activity
將新增的 Activity 選取對應的程式位置 單車練習曲建置 6-3. Activity 註冊 1 將新增的 Activity 選取對應的程式位置 ※此後新增一個 Activity,皆需要執行此註冊動作
單車練習曲建置 6-4.功能選單 select.java 1 2 3 4 5 6 7 8 9 Button friend; 10 import android.app.Activity; 2 import android.content.Intent; 3 import android.os.Bundle; 4 import android.view.View; 5 import android.view.View.OnClickListener; 6 import android.widget.Button; 7 8 public class select extends Activity { 9 Button friend; 10 Button activity; 11 打開 select.java
單車練習曲建置 12 protected void onCreate(Bundle savedInstanceState) { 13 super.onCreate(savedInstanceState); 14 setContentView(R.layout.select); 15 findView(); 16 //前置先與 Layout 元件作連結 17 setListener(); 18 //設置觸發事件 19 } 20 public void findView() { 21 friend = (Button) findViewById(R.id.button1); 22 activity = (Button) findViewById(R.id.button2); 23 24
單車練習曲建置 25 public void setListener(){ 26 activity.setOnClickListener(new OnClickListener() { 27 public void onClick(View v) { 28 Intent intent = new Intent(); 29 intent.setClass(select.this, activity.class); 30 startActivity(intent); 31 } 32 }); 33 //設置觸發點擊活動按鈕事件即轉跳到活動功能清單 34 friend.setOnClickListener(new OnClickListener() { 35 36 37 intent.setClass(select.this, friend_list.class);
單車練習曲建置 38 39 40 41 42 43 44 45 46 47 48 49 50 startActivity(intent); } 40 }); 41 42 //設置觸發點擊活動按鈕事件即轉跳到朋友清單 43 protected void onResume() { 44 super.onResume(); 45 Intent intent = new Intent(this,backgroundService.class); 46 startService(intent); 47 //啟動更新用戶位置的 Background Service 48 49 50
單車練習曲建置 6-5. Background Service Service Lifecycle Background Service與Activity兩者最大區別就是「Service完全沒有UI」,且擁有獨立的生命週期,如圖二所示。而Service 與App的Activity生命週期互不影響,Service 生命週期只繼承其中的onCreate()、onStart()、onDestroy(),一旦啟動,服務就會直接在背景執行,而除非重新開機或透過呼叫stopService()方法停止該服務,否則Android系統並不會自動銷毀該服務,而本章魔法地圖的Background Service 設計讓使用者一打開程式,就馬上執行Service,當偵測到位置改變時,手機馬上會上傳更新該用戶目前所在位置(當程式關閉還是會持續更新),進而達到魔法地圖可以觀察活動受邀好友的所在位置。 Service Lifecycle
單車練習曲建置 6-5. Background Service AndroidManifest.xml 3 1 2
單車練習曲建置 6-5. Background Service 新增一組 Service
將 Service 設定給 backgroundService 物件 單車練習曲建置 6-5. Background Service 1 將 Service 設定給 backgroundService 物件
打開 backgroundService.java 單車練習曲建置 6-5. Background Service backgroundService.java 1 import org.apache.http.HttpEntity; 2 import org.apache.http.HttpResponse; 3 import org.apache.http.client.HttpClient; 4 import org.apache.http.client.methods.HttpGet; 5 import org.apache.http.impl.client.DefaultHttpClient; 6 import org.apache.http.util.EntityUtils; 7 import android.app.Service; 8 import android.content.Context; 9 import android.content.Intent; 10 import android.location.Criteria; 11 import android.location.Location; 打開 backgroundService.java
單車練習曲建置 12 import android.location.LocationListener; 13 import android.location.LocationManager; 14 import android.os.Bundle; 15 import android.os.IBinder; 16 17 public class backgroundService extends Service { 18 private Location locationInfo; 19 public void onCreate() { 20 LocationManager locationManager = 21 (LocationManager) getSystemService(Context.LOCATION_SERVICE); 22 String bestProvider = locationManager.getBestProvider(new Criteria(), true); 23 //初始化 locationManager 並取得最佳定位提供者 24
單車練習曲建置 25 26 27 28 29 30 31 32 33 34 35 36 37 locationManager. requestLocationUpdates(bestProvider, 10000, 1, locationListener); 27 //定位更新(bestProvider ,時間間隔10000ms ,距離間隔1m ,監聽物件) 28 super.onCreate(); 29 } 30 31 public IBinder onBind(Intent arg0) { 32 return null; 33 34 35 private void updateLoaction() { 36 String url = value.service + "updateLocation.php?simid=" + value.simid 37 + "&lat=" + locationInfo.getLatitude() + "&lng="
單車練習曲建置 38 + locationInfo.getLongitude(); 39 HttpGet get = new HttpGet(url); 40 try { 41 HttpClient client = new DefaultHttpClient(); 42 HttpResponse response = client.execute(get); 43 HttpEntity resEntity = response.getEntity(); 44 String result = EntityUtils.toString(resEntity); 45 } catch (Exception e) { 46 } 47 48 49 private LocationListener locationListener = new LocationListener() { 50 public void onLocationChanged(Location location) {
單車練習曲建置 51 locationInfo = location; 52 if (location != null) { 53 Thread theard = new Thread() { 54 public void run() { 55 updateLoaction(); 56 /* 當觸發LocationChanged事件,即呼叫 updateLocation() 更新最新經緯度到 57 Server 上 */ 58 } 59 }; 60 theard.start(); 61 62 63
單車練習曲建置 64 public void onProviderDisabled(String arg0) { 65 } 66 67 public void onProviderEnabled(String arg0) { 68 69 70 public void onStatusChanged(String arg0, int arg1, Bundle arg2) { 71 72 }; 73 74 75 private void updateLoaction() { 76 String url = value.service + "updateLocation.php?simid="
單車練習曲建置 77 + value.simid + "&lat=" + locationInfo.getLatitude() 78 + "&lng=" + locationInfo.getLongitude(); 79 80 HttpGet get = new HttpGet(url); 81 try { 82 HttpClient client = new DefaultHttpClient(); 83 HttpResponse response = client.execute(get); 84 HttpEntity resEntity = response.getEntity(); 85 String result = EntityUtils.toString(resEntity); 86 } catch (Exception e) { 87 } 88 89
單車練習曲建置 7. 用戶清單功能描述 Android Web Service friend_list.java 4 Require 列出所有用戶清單,此區目前僅列出所有註冊用戶的名稱 simid name none Web Service JSON GET user.php 查詢出所有用戶的名稱清單 Response
單車練習曲建置 7-1. 查詢所有用戶資訊 Service user.php 1 2 3 4 5 6 7 8 9 10 11 include("../connect.php"); 2 $sql = "SELECT simid,name FROM user"; 3 $result = mysql_query($sql, $link) or die("oops"); 4 //查詢所有使用者 5 $data = array(); 6 while($row = mysql_fetch_array($result,MYSQL_ASSOC)){ 7 array_push($data,$row); 8 //將所有查詢出來的資料放入 $data 陣列當中 9 } 10 echo json_encode($data); 11 //透過 json 來制定格式傳遞資料
單車練習曲建置 7-2.用戶清單功能 Layout friend_list.xml 1 2 3 4 5 6 7 8 9 10 11 <?xml version="1.0" encoding="utf-8"?> 2 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 3 android:layout_width="match_parent" 4 android:layout_height="match_parent" 5 android:orientation="vertical" > 6 <ListView 7 android:id="@+id/listView1" 8 9 android:layout_height="wrap_content" > 10 </ListView> 11 </LinearLayout>
單車練習曲建置 7-3.用戶清單功能 friend_list.java 1 2 3 4 5 6 7 8 9 10 11 import org.apache.http.HttpEntity; 2 import org.apache.http.HttpResponse; 3 import org.apache.http.client.HttpClient; 4 import org.apache.http.client.methods.HttpGet; 5 import org.apache.http.impl.client.DefaultHttpClient; 6 import org.apache.http.util.EntityUtils; 7 import org.json.JSONArray; 8 import org.json.JSONException; 9 import org.json.JSONObject; 10 import android.app.Activity; 11 import android.os.Bundle; 打開 friend_list.java
單車練習曲建置 12 import android.os.Handler; 13 import android.os.Message; 14 import android.util.Log; 15 import android.widget.ArrayAdapter; 16 import android.widget.ListView; 17 18 public class friend_list extends Activity { 19 ListView listView; 20 21 protected void onCreate(Bundle savedInstanceState) { 22 super.onCreate(savedInstanceState); 23 setContentView(R.layout.friend_list); 24 findView();
單車練習曲建置 25 26 27 28 29 30 31 32 33 34 35 36 37 //前置先與 Layout 元件作連結 FriendList fl = new FriendList(); 27 fl.start(); 28 //執行 fl 執行緒 來向 Service 請求所有使用者的資料 29 } 30 31 public void findView() { 32 listView = (ListView) findViewById(R.id.listView1); 33 34 35 class FriendList extends Thread { 36 public void run() { 37 super.run();
單車練習曲建置 38 try { 39 HttpClient client = new DefaultHttpClient(); 40 String url = value.service + "user.php"; 41 HttpGet get = new HttpGet(url); 42 HttpResponse response = client.execute(get); 43 HttpEntity resEntity = response.getEntity(); 44 String result = EntityUtils.toString(resEntity); 45 46 Bundle countBundle = new Bundle(); 47 countBundle.putString("list", result); 48 //打包朋友清單相關資料,並命名為 list 49 Message msg = new Message(); 50 msg.setData(countBundle);
單車練習曲建置 51 mHandler.sendMessage(msg); 52 //將打包資料方置放在 Message 物件當中,並傳送給 handler 53 } catch (Exception e) { 54 e.printStackTrace(); 55 } 56 57 58 59 private Handler mHandler = new Handler() { 60 public void handleMessage(Message msg) { 61 super.handleMessage(msg); 62 setList(msg.getData().getString("list")); 63 /*
單車練習曲建置 64 透過 handler 來請 main thread 處理 setList 65 sub thread 不能值接對 UI 作控制,必須透過 handler 來請主程序作 66 */ 67 } 68 }; 69 70 public void setList(String list) { 71 JSONArray json; 72 try { 73 json = new JSONArray(list); 74 String[] values = new String[json.length()]; 75 for (int i = 0; i < json.length(); i++) { 76 JSONObject jsonData = new JSONObject(json.get(i).toString());
單車練習曲建置 77 values[i] = jsonData.getString("name"); 78 } 79 listView.setAdapter(new ArrayAdapter<String>(this, 80 android.R.layout.simple_list_item_1, values)); 81 //將字串 list 回傳的 JSON 透過解析後存到 values 陣列並傳給 listView 作清單的顯示 82 } catch (JSONException e) { 83 e.printStackTrace(); 84 85 86 87 88 89
單車練習曲建置 8. 活動清單功能描述 Android Web Service activity.java 5 Require aid 建立活動 Require 列出所有活動清單,並增加一個建立活動的按鈕 aid title simid Web Service JSON GET activity.php 查詢出所有活動的名稱與活動編號 Response
單車練習曲建置 8-1. 參與活動清單 Service activity.php 1 2 3 4 5 6 7 8 9 10 11 if (isset($_GET['simid'])) { 2 $simid = mysql_real_escape_string($_GET['simid']); 3 //透過 GET 代入 simid (SIM Card ID) 參數 4 include("../connect.php"); 5 $sql = "SELECT title,c.aid FROM `join` c JOIN `activity` a on c.aid = a.aid 6 WHERE c.simid = '$simid'"; 7 $result = mysql_query($sql, $link) or die("oops"); 8 /* JOIN join 與 activity 兩張資料表, 9 並篩選出 $simid 用戶有參與的活動 title (活動名稱) 與 aid (活動編號) 10 */ 11 $data = array();
單車練習曲建置 12 while($row = mysql_fetch_array($result,MYSQL_ASSOC)){ 13 array_push($data,$row); 14 //將所有查詢出來的資料放入 $data 陣列當中 15 } 16 echo json_encode($data); 17 //透過 json 來制定格式傳遞資料 18 19 20 21 22 23 24
單車練習曲建置 8-2.活動清單功能 Layout add_activity.xml 1 2 3 4 5 6 7 8 9 10 11 <?xml version="1.0" encoding="utf-8"?> 2 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 3 android:layout_width="match_parent" 4 android:layout_height="match_parent" 5 android:orientation="vertical" > 6 7 <Button 8 android:id="@+id/button1" 9 10 android:layout_height="wrap_content" 11 android:text="建立活動" />
單車練習曲建置 12 <ListView 13 android:id="@+id/listView1" 14 android:layout_width="match_parent" 15 android:layout_height="wrap_content" > 16 </ListView> 17 18 </LinearLayout> 19 20 21 22 23 24
單車練習曲建置 8-3.活動清單功能 activity.java 1 2 3 4 5 6 7 8 9 10 11 import org.apache.http.HttpEntity; 2 import org.apache.http.HttpResponse; 3 import org.apache.http.client.HttpClient; 4 import org.apache.http.client.methods.HttpGet; 5 import org.apache.http.impl.client.DefaultHttpClient; 6 import org.apache.http.util.EntityUtils; 7 import org.json.JSONArray; 8 import org.json.JSONException; 9 import org.json.JSONObject; 10 import android.app.Activity; 11 import android.content.Intent; 打開 activity.java
單車練習曲建置 12 import android.os.Bundle; 13 import android.os.Handler; 14 import android.os.Message; 15 import android.util.Log; 16 import android.view.View; 17 import android.view.View.OnClickListener; 18 import android.widget.AdapterView; 19 import android.widget.ArrayAdapter; 20 import android.widget.Button; 21 import android.widget.ListView; 22 import android.widget.AdapterView.OnItemClickListener; 23 24
單車練習曲建置 25 public class activity extends Activity { 26 ListView listView; 27 Button button; 28 String actionList[]; 29 protected void onCreate(Bundle savedInstanceState) { 30 super.onCreate(savedInstanceState); 31 setContentView(R.layout.add_activity); 32 findView(); 33 //前置先與 Layout 元件作連結 34 setListener(); 35 //設置觸發事件 36 ActionList al = new ActionList(); 37 al.start();
單車練習曲建置 38 } 39 40 public void findView() { 41 listView = (ListView) findViewById(R.id.listView1); 42 button = (Button) findViewById(R.id.button1); 43 44 45 public void setListener(){ 46 button.setOnClickListener(new OnClickListener() { 47 public void onClick(View v) { 48 Intent intent = new Intent(); 49 intent.setClass(activity.this, activity_map.class); 50 startActivity(intent);
單車練習曲建置 51 52 53 54 55 56 57 58 59 60 61 62 63 //觸發按鈕點擊事件後,轉跳到新增活動地圖頁面 } 53 }); 54 55 listView.setOnItemClickListener(new OnItemClickListener() { 56 public void onItemClick(AdapterView<?> parent, View v, 57 int position, long id) { 58 Intent intent = new Intent(); 59 Bundle bundle = new Bundle(); 60 bundle.putString("aid", actionList[position]); 61 //預先設定傳遞打包資料 62 intent.putExtras(bundle); 63 intent.setClass(activity.this, Map.class);
單車練習曲建置 64 65 66 67 68 69 70 71 72 73 74 75 76 startActivity(intent); //設定當觸發活動清單項目,轉跳到活動地圖功能頁面 66 } 67 }); 68 69 70 class ActionList extends Thread { 71 public void run() { 72 super.run(); 73 try { 74 HttpClient client = new DefaultHttpClient(); 75 String url = value.service + "activity.php?simid=" 76 + value.simid;
單車練習曲建置 77 HttpGet get = new HttpGet(url); 78 HttpResponse response = client.execute(get); 79 HttpEntity resEntity = response.getEntity(); 80 String result = EntityUtils.toString(resEntity); 81 Bundle countBundle = new Bundle(); 82 countBundle.putString("list", result); 83 //打包活動清單相關資料,並命名為 list 84 Message msg = new Message(); 85 msg.setData(countBundle); 86 mHandler.sendMessage(msg); 87 } catch (Exception e) { 88 e.printStackTrace(); 89 }
單車練習曲建置 90 } 91 92 93 private Handler mHandler = new Handler() { 94 public void handleMessage(Message msg) { 95 super.handleMessage(msg); 96 setList(msg.getData().getString("list")); 97 //透過 handler 來呼叫 setList() 傳送清單內容與更新活動清單 98 99 }; 100 101 public void setList(String list) { 102 JSONArray json;
單車練習曲建置 103 try { 104 json = new JSONArray(list); 105 String[] values = new String[json.length()]; 106 actionList = new String[json.length()]; 107 108 for (int i = 0; i < json.length(); i++) { 109 JSONObject jsonData = new JSONObject(json.get(i).toString()); 110 values[i] = jsonData.getString("title"); 111 actionList[i] = jsonData.getString("aid"); 112 } 113 114 //Json 回傳 title(活動標題) 與 aid(活動ID) 的資料,分別使用陣列來承接 115
單車練習曲建置 116 listView.setAdapter(new ArrayAdapter<String>(this, 117 android.R.layout.simple_list_item_1, values)); 118 } catch (JSONException e) { 119 e.printStackTrace(); 120 } 121 122 123 124 125 126 127 128
單車練習曲建置 9. 建立活動功能描述 Android Web Service 6 7 activity_map.java activity_title.java Require 設定活動標題,並列出好友選單透過勾選方式來加入參與人,且透過選取地圖位置來設定活動位置 title direction master friend[simid] response aid Web Service JSON addActivity.php GET 建立一筆新活動,設定活動名稱與位置,並可記錄所有參與人 Response
單車練習曲建置 9-1. 參與活動清單 Service addActivity.php 1 2 3 4 5 6 7 8 9 10 11 if (isset($_GET['title']) 2 && isset($_GET['direction']) 3 && isset($_GET['friend']) 4 && isset($_GET['master']) ) { 5 $title = mysql_real_escape_string($_GET['title']); 6 $direction = mysql_real_escape_string($_GET['direction']); 7 $friend = mysql_real_escape_string($_GET['friend']); 8 $master = mysql_real_escape_string($_GET['master']); 9 //透過 GET 代入 title (活動名稱) direction (路徑) 10 //friend (參與人) master (創立活動者)參數 11 $addFriends = explode(',',$friend);
單車練習曲建置 12 13 14 15 16 17 18 19 20 21 22 23 24 //透過 explode 作字串的分割 include("../connect.php"); 14 $sql = "INSERT INTO activity(title,direction,master_simid) " 15 ."VALUES('$title','$direction','$master')"; 16 $result = mysql_query($sql, $link) or die("oops"); 17 //先建立出一筆新活動,並設定 title (活動名稱) direction (路徑) 18 $aid = mysql_insert_id(); 19 //因 aid 為自動編號,故需透過 mysql_insert_id 來取得產生之活動編號 20 for($i=0;$i<count($addFriends);$i++) 21 { 22 $sql = "INSERT INTO `join`(simid,aid) VALUES('$addFriends[$i]','$aid')"; 23 $result = mysql_query($sql, $link) or die("oops"); 24 //將所有參與人ID與對應活動編號一筆筆新增到 `join` 資料表當中
單車練習曲建置 25 26 27 28 29 30 31 32 33 34 35 36 37 } if($result) { } 26 if($result) 27 { 28 $data['responese'] = 'success'; 29 $data['aid'] = $aid; 30 echo json_encode($data); 31 } 32 } 33 34 35 36 37
單車練習曲建置 9-2.活動地圖打點功能 Layout activity_map.xml 1 2 3 4 5 6 7 8 9 10 11 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" 2 xmlns:tools="http://schemas.android.com/tools" 3 android:layout_width="match_parent" 4 android:layout_height="match_parent" 5 android:paddingBottom="@dimen/activity_vertical_margin" 6 android:paddingLeft="@dimen/activity_horizontal_margin" 7 android:paddingRight="@dimen/activity_horizontal_margin" 8 android:paddingTop="@dimen/activity_vertical_margin" 9 tools:context=".MainActivity" > 10 11 <fragment
單車練習曲建置 12 13 14 15 16 17 18 19 20 21 22 23 24 android:id="@+id/map" android:layout_width="match_parent" 14 android:layout_height="match_parent" 15 class="com.google.android.gms.maps.MapFragment" /> 16 <Button 17 android:id="@+id/next" 18 android:layout_width="wrap_content" 19 android:layout_height="wrap_content" 20 android:layout_alignParentBottom="true" 21 android:layout_centerHorizontal="true" 22 android:layout_marginBottom="14dp" 23 android:text="下一頁" /> 24 </RelativeLayout>
單車練習曲建置 9-3.活動地圖打點功能 activity_map.java 打開 activity_map.java 1 import java.text.MessageFormat; 2 import org.apache.http.HttpResponse; 3 import org.apache.http.client.HttpClient; 4 import org.apache.http.client.methods.HttpGet; 5 import org.apache.http.impl.client.DefaultHttpClient; 6 import org.apache.http.util.EntityUtils; 7 import org.json.JSONArray; 8 import org.json.JSONObject; 9 import android.app.Activity; 10 import android.content.Context; 11 import android.content.Intent; 打開 activity_map.java
單車練習曲建置 12 import android.location.Criteria; 13 import android.location.Location; 14 import android.location.LocationListener; 15 import android.location.LocationManager; 16 import android.os.Bundle; 17 import android.os.Handler; 18 import android.os.Message; 19 import android.util.Log; 20 import android.view.View; 21 import android.view.View.OnClickListener; 22 import android.widget.Button; 23 import android.widget.Toast; 24 import com.google.android.gms.maps.CameraUpdateFactory;
單車練習曲建置 25 import com.google.android.gms.maps.GoogleMap; 26 import com.google.android.gms.maps.MapFragment; 27 import com.google.android.gms.maps.GoogleMap.OnMapClickListener; 28 import com.google.android.gms.maps.GoogleMap.OnMarkerClickListener; 29 import com.google.android.gms.maps.model.BitmapDescriptorFactory; 30 import com.google.android.gms.maps.model.LatLng; 31 import com.google.android.gms.maps.model.Marker; 32 import com.google.android.gms.maps.model.MarkerOptions; 33 public class activity_map extends Activity implements OnMarkerClickListener, 34 OnMapClickListener { 35 Location location; 36 GoogleMap mMap; 37 LatLng latlng = new LatLng(22.764702, 120.371935);
單車練習曲建置 38 Button next; 39 String lat, lng; 40 Location locationInfo; 41 Marker MyLocation; 42 Marker DestinationLocation; 43 String startLocation; 44 String endLocation; 45 @Override 46 protected void onCreate(Bundle savedInstanceState) { 47 super.onCreate(savedInstanceState); 48 setContentView(R.layout.activity_map); 49 findView(); 50 // 前置先與 Layout 元件作連結
單車練習曲建置 51 52 53 54 55 56 57 58 59 60 61 62 63 setMap(); // 設置地圖 // 設置地圖 53 setListener(); 54 // 設置觸發事件 55 setLocation(); 56 } 57 58 public void findView() { 59 next = (Button) findViewById(R.id.next); 60 mMap = ((MapFragment)getFragmentManager().findFragmentById(R.id.map)) 61 .getMap(); 62 63
單車練習曲建置 64 65 66 67 68 69 70 71 72 73 74 75 76 public void setMap() { mMap.moveCamera(CameraUpdateFactory.newLatLngZoom(latlng, 15.0f)); 66 mMap.setOnMapClickListener(this); 67 } 68 69 public void setLocation() { 70 LocationManager locationManager = 71 (LocationManager) getSystemService(Context.LOCATION_SERVICE); 72 String bestProvider = locationManager.getBestProvider(new Criteria(),true); 73 // 初始化 locationManager 並取得最佳定位提供者 74 locationManager.requestLocationUpdates(bestProvider, 10000, 1, locationListener); 75 // 定位更新(bestProvider ,時間間隔1000ms ,距離間隔1m ,監聽物件) 76 }
單車練習曲建置 77 public void setListener() { 78 next.setOnClickListener(new OnClickListener() { 79 @Override 80 public void onClick(View v) { 81 // TODO Auto-generated method stub 82 if (MyLocation == null) { 83 Toast.makeText(activity_map.this, "請等待定位", 84 Toast.LENGTH_LONG).show(); 85 return; 86 } 87 if (lat.length() == 0 && lng.length() == 0) { 88 Toast.makeText(activity_map.this, "請點選活動位置", 89 Toast.LENGTH_LONG).show();
單車練習曲建置 90 } else { 91 // 單車活動起始位置(手機定位位置) 92 startLocation = MyLocation.getPosition().latitude + "," 93 + MyLocation.getPosition().longitude; 94 //單車活動目的地位置(透過打點方式選擇) 95 endLocation = DestinationLocation.getPosition().latitude 96 + "," + DestinationLocation.getPosition().longitude; 97 Thread theard = new Thread() { 98 public void run() { 99 String DirectionContent = GetDirection(startLocation, endLocation); 100 Bundle countBundle = new Bundle(); 101 Message msg = new Message(); 102 msg.setData(countBundle);
單車練習曲建置 103 mHandler.sendMessage(msg); 104 } 105 }; 106 theard.start(); 107 } 108 } 109 }); 110 } 111 112 private Handler mHandler = new Handler() { 113 public void handleMessage(Message msg) { 114 super.handleMessage(msg); 115 // 跳頁並夾帶路徑的值
單車練習曲建置 116 toActivity(msg.getData().getString("direction")); 117 } 118 }; 119 120 public void toActivity(String DirectionContent) { 121 Intent intent = new Intent(); 122 intent.setClass(activity_map.this, activity_title.class); 123 Bundle bundle = new Bundle(); 124 bundle.putString("direction", DirectionContent); 125 intent.putExtras(bundle); 126 startActivity(intent); 127 finish(); 128 }
單車練習曲建置 129 @Override 130 public boolean onMarkerClick(Marker marker) { 131 return false; 132 } 133 134 135 public void onMapClick(LatLng point) { 136 // TODO Auto-generated method stub 137 // 若是尚未標記打點(定單車活動位置)則建立新的標記點 138 if (DestinationLocation == null) { 139 MarkerOptions lonlng = new MarkerOptions().position(point).title( 140 point.toString()); 141 DestinationLocation = mMap.addMarker(lonlng);
單車練習曲建置 142 } else 143 // 有標籤則移動 144 { 145 DestinationLocation.setPosition(point); 146 } 147 lat = point.latitude + ""; 148 lng = point.longitude + ""; 149 } 150 private LocationListener locationListener = new LocationListener() { 151 public void onLocationChanged(Location location) { 152 LatLng latLng1 = new LatLng(location.getLatitude(),location.getLongitude()); 153 if (MyLocation == null) { 154 MarkerOptions lonlng = new MarkerOptions()
單車練習曲建置 155 .position(latLng1) 156 .title("My location") 157 .icon(BitmapDescriptorFactory 158 .defaultMarker(BitmapDescriptorFactory.HUE_BLUE)); 159 MyLocation = mMap.addMarker(lonlng); 160 // 當前位置的定位標記 161 } else { 162 MyLocation.setPosition(latLng1); 163 } 164 } 165 @Override 166 public void onProviderDisabled(String arg0) { 167 // TODO Auto-generated method stub
單車練習曲建置 169 } 170 @Override 171 public void onProviderEnabled(String arg0) { 172 // TODO Auto-generated method stub 173 174 175 public void onStatusChanged(String arg0, int arg1, Bundle arg2) { 176 177 178 }; 179 180 public String GetDirection(String startLocation, String endLocation) { 181 // 透過Google Directions 取得路徑規劃 (起始位置,終點位置)
單車練習曲建置 182 String mapAPI ="http://maps.google.com/maps/api/directions/json?" 183 + "origin={0}&destination={1}&language=zh-TW&sensor=true"; 184 String url = MessageFormat.format(mapAPI, startLocation, endLocation); 185 HttpGet get = new HttpGet(url); 186 try { 187 HttpClient httpClient = new DefaultHttpClient(); 188 HttpResponse httpResponse = httpClient.execute(get); 189 if (httpResponse.getStatusLine().getStatusCode() == 200) { 190 String strResult = EntityUtils.toString(httpResponse.getEntity()); 191 JSONObject jsonObject = new JSONObject(strResult); 192 JSONArray routeObject = jsonObject.getJSONArray("routes"); 193 String polyline = routeObject.getJSONObject(0) 194
單車練習曲建置 195 .getJSONObject("overview_polyline").getString("points"); 196 return polyline; 197 } catch (Exception e) { 198 Log.e("map", "MapRoute:" + e.toString()); 199 } 200 return null; 201 // return _points; 202 } 203 } 204 205 206 207
單車練習曲建置 9-4.活動新增功能 Layout activity_title.xml 1 2 3 4 5 6 7 8 9 10 11 <?xml version="1.0" encoding="utf-8"?> 2 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 3 android:layout_width="match_parent" 4 android:layout_height="match_parent" 5 android:orientation="vertical" > 6 7 <Button 8 android:id="@+id/button1" 9 10 android:layout_height="wrap_content" 11 android:layout_alignParentBottom="true"
單車練習曲建置 12 android:layout_alignParentLeft="true" 13 android:layout_alignParentRight="true" 14 android:text="下一頁" /> 15 16 <EditText 17 android:id="@+id/editText1" 18 android:layout_width="wrap_content" 19 android:layout_height="wrap_content" 20 android:layout_alignParentTop="true" 21 android:layout_centerHorizontal="true" 22 android:ems="10" 23 android:gravity="center" 24 android:text="活動名稱" >
單車練習曲建置 25 26 27 28 29 30 31 32 33 34 35 36 37 <requestFocus /> </EditText> 27 28 <ListView 29 android:id="@+id/listView1" 30 android:layout_width="match_parent" 31 android:layout_height="wrap_content" 32 android:layout_above="@+id/button1" 33 android:layout_alignParentLeft="true" 34 android:layout_below="@+id/editText1" > 35 </ListView> 36 </RelativeLayout> 37
打開 activity_title.java 單車練習曲建置 9-5.活動新增功能 activity_title.java import java.net.URLEncoder; 1 import java.text.MessageFormat; 2 import java.util.ArrayList; 3 import org.apache.http.HttpEntity; 4 import org.apache.http.HttpResponse; 5 import org.apache.http.client.HttpClient; 6 import org.apache.http.client.methods.HttpGet; 7 import org.apache.http.impl.client.DefaultHttpClient; 8 import org.apache.http.util.EntityUtils; 9 import org.json.JSONArray; 10 import org.json.JSONException; 11 import org.json.JSONObject; 打開 activity_title.java
單車練習曲建置 12 import android.app.Activity; 13 import android.content.Context; 14 import android.content.Intent; 15 import android.os.Bundle; 16 import android.os.Handler; 17 import android.os.Message; 18 import android.telephony.TelephonyManager; 19 import android.util.Log; 20 import android.view.View; 21 import android.view.View.OnClickListener; 22 import android.widget.AdapterView; 23 import android.widget.AdapterView.OnItemClickListener; 24 import android.widget.ArrayAdapter;
單車練習曲建置 25 import android.widget.Button; 26 import android.widget.EditText; 27 import android.widget.ListView; 28 29 public class activity_title extends Activity { 30 EditText editText; 31 Button button; 32 ListView listView; 33 ArrayList checkedList; 34 String DirectionContent; 35 String[] simidList; 36 37
單車練習曲建置 38 @Override 39 protected void onCreate(Bundle savedInstanceState) { 40 super.onCreate(savedInstanceState); 41 setContentView(R.layout.activity_title); 42 getValue(); 43 // 取得前頁 Intent 到此頁地圖標記位置資訊 44 findView(); 45 // 前置先與 Layout 元件作連結 46 setListener(); 47 // 設置觸發按鈕事件 48 FriendList fl = new FriendList(); 49 fl.start(); 50 // 執行FriendList執行序
單車練習曲建置 51 } 52 53 public void getValue() { 54 Bundle bundle = new Bundle(); 55 bundle = this.getIntent().getExtras(); 56 DirectionContent = bundle.getString("direction"); 57 // 取出路徑規劃編碼 58 } 59 60 public void findView() { 61 listView = (ListView) findViewById(R.id.listView1); 62 button = (Button) findViewById(R.id.button1); 63 editText = (EditText) findViewById(R.id.editText1);
單車練習曲建置 64 65 66 public void setListener() { 67 68 69 70 71 72 73 74 checkedList = new ArrayList(); 65 } 66 67 public void setListener() { 68 button.setOnClickListener(new OnClickListener() { 69 @Override 70 public void onClick(View v) { 71 AddAction AA = new AddAction(); 72 AA.start(); 73 } 74 }); 75 // 設定觸發點擊事件 "下一頁" 的按鈕,會執行 AddAction Thread 76
單車練習曲建置 77 listView.setOnItemClickListener(new OnItemClickListener() { 78 public void onItemClick(AdapterView parent, View v, 79 int position, long id) { 80 // Log.d("test","position:"+position+""); 81 if (listView.getCheckedItemPositions().get(position)) { 82 checkedList.add(position); 83 // Log.d("test", "checkList:"+checkedList.toString()); 84 } else { 85 checkedList.remove(new Integer(position)); 86 // Log.d("checkList", checkedList.toString()); 87 } 88 } 89 });
單車練習曲建置 90 /* 設定觸發點擊事件好友清單的項目(Item) 91 * 會加入參與人陣列(checkedList) 92 * 打勾則加入清單資料,取消打勾則移除陣列清單資料 93 */ 94 95 editText.setOnClickListener(new OnClickListener() { 96 @Override 97 public void onClick(View v) { 98 editText.setText(""); 99 } 100 }); 101 // 設定觸發點擊文字輸入(Title 文字欄位),即清空文字內容(預設為"活動名稱") 102 }
單車練習曲建置 103 // 取得好友清單 104 class FriendList extends Thread { 105 106 @Override 107 public void run() { 108 // TODO Auto-generated method stub 109 super.run(); 110 try { 111 HttpClient client = new DefaultHttpClient(); 112 String url = value.service + "user.php"; 113 HttpGet get = new HttpGet(url); 114 HttpResponse response = client.execute(get); 115
單車練習曲建置 116 HttpEntity resEntity = response.getEntity(); 117 String result = EntityUtils.toString(resEntity); 118 // Log.d("result", result); 119 Bundle countBundle = new Bundle(); 120 countBundle.putString("list", result); 121 Message msg = new Message(); 122 msg.setData(countBundle); 123 mHandler.sendMessage(msg); 124 } catch (Exception e) { 125 e.printStackTrace(); 126 } 127 } 128
單車練習曲建置 129 } 130 131 private Handler mHandler = new Handler() { 132 public void handleMessage(Message msg) { 133 super.handleMessage(msg); 134 setList(msg.getData().getString("list")); 135 } 136 }; 137 138 public void setList(String list) { 139 JSONArray json; 140 try { 141
單車練習曲建置 142 json = new JSONArray(list); 143 String[] values = new String[json.length()]; 144 simidList = new String[json.length()]; 145 for (int i = 0; i < json.length(); i++) { 146 JSONObject jsonData = new JSONObject(json.get(i).toString()); 147 values[i] = jsonData.getString("name"); 148 simidList[i] = jsonData.getString("simid"); 149 } 150 listView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE); 151 // 設定ListView 為 複選模式 152 listView.setAdapter(new ArrayAdapter(this, 153 android.R.layout.simple_list_item_multiple_choice, values)); 154
單車練習曲建置 155 } catch (JSONException e) { 156 // TODO Auto-generated catch block 157 e.printStackTrace(); 158 } 159 160 } 161 162 163 class AddAction extends Thread { 164 // 建立活動 165 @Override 166 public void run() { 167
單車練習曲建置 169 // TODO Auto-generated method stub 170 super.run(); 171 String list = ""; 172 for (int i = 0; i < checkedList.size(); i++) { 173 if (checkedList.size() - 1 == i) { 174 list += simidList[checkedList.get(i)]; 175 } else { 176 list += simidList[checkedList.get(i)] + ","; 177 } 178 // Log.d("test", "list:"+list); 179 } 180 try { 181
單車練習曲建置 182 // 新增活動,並且記錄創辦活動者 183 String mapAPI = value.service 184 + "addActivity.php?title={0}" 185 + "&direction={1}&friend={2}&master={3}"; 186 String url = MessageFormat.format(mapAPI, editText.getText() 187 .toString(), URLEncoder.encode(DirectionContent, 188 "UTF-8"), list, value.simid); 189 /* 透過 Http Get 傳送到Web Service 190 (1:活動名稱,2:路徑編碼,3:邀請人[],4:領隊Simid) */ 191 HttpClient client = new DefaultHttpClient(); 192 HttpGet get = new HttpGet(url); 193 HttpResponse response = client.execute(get); 194
單車練習曲建置 195 HttpEntity resEntity = response.getEntity(); 196 String result = EntityUtils.toString(resEntity); 197 // Log.d("test", result); 198 Bundle countBundle = new Bundle(); 199 countBundle.putString("list", result); 200 // Log.d("test", "result:"+result); 201 Message msg = new Message(); 202 msg.setData(countBundle); 203 mHandler2.sendMessage(msg); 204 } catch (Exception e) { 205 e.printStackTrace(); 206 } 207
單車練習曲建置 208 } 209 } 210 211 private Handler mHandler2 = new Handler() { 212 public void handleMessage(Message msg) { 213 super.handleMessage(msg); 214 changePage(msg.getData().getString("list")); 215 216 }; 217 218 public void changePage(String list) { 219 220
單車練習曲建置 221 try { 222 JSONObject jsonObj = new JSONObject(list); 223 String aid = jsonObj.getString("aid"); 224 Intent intent = new Intent(); 225 Bundle bundle = new Bundle(); 226 // Log.d("test",aid); 227 bundle.putString("aid", aid); 228 229 intent.putExtras(bundle); 230 intent.setClass(activity_title.this, Map.class); 231 startActivity(intent); 232 finish(); 233
單車練習曲建置 234 235 236 237 238 /* 完成頁面轉跳後,即關閉此Activity 目的在於不讓使用者返回上一頁(新增活動功能頁面) */ 236 } catch (JSONException e) { 237 // TODO Auto-generated catch block 238 e.printStackTrace(); 239 } 240 } 241 } 242 243 244 245 246
單車練習曲建置 10. 單車練習曲功能描述 Android Web Service 9 map.java Require name lat 在地圖上顯示活動位置與所有參與人目前位置資訊 name lat lng aid simid Web Service GET JSON joinUser.php 查詢此活動以及所有參與人的相關位置資訊 Response
單車練習曲建置 10-1. 單車活動參與人資訊 Service joinUser.php 1 2 3 4 5 6 7 8 9 10 11 if (isset($_GET['aid'])&&isset($_GET['simid'])) { 2 $aid = mysql_real_escape_string($_GET['aid']); 3 $simid = mysql_real_escape_string($_GET['simid']); 4 //透過 GET 代入 aid (活動編號) simid (手機編號) 參數 5 include("../connect.php"); 6 $sql = "SELECT * FROM activity WHERE aid = '$aid'"; 7 $result = mysql_query($sql, $link) or die("oops1"); 8 $row = mysql_fetch_array($result,MYSQL_ASSOC); 9 $activity['title'] = $row['title']; 10 $activity['direction'] = $row['direction']; 11 $master_simid = $row['master_simid'];
單車練習曲建置 12 //查詢活動的相關資訊,活動名稱(title),活動路徑(dirction),領隊(master_simid) 13 $sql = "SELECT u.simid,name,lat,lng FROM `join` c JOIN `user` u " 14 ."on u.simid = c.simid WHERE c.aid = '$aid'"; 15 $result = mysql_query($sql, $link) or die(mysql_error()); 16 /* JOIN join 與 user 兩張資料表, 17 並篩選出所有參與 $aid 活動中參與人的 name (名稱) 與 lat, lng (經緯度) 18 */ 19 $data = array(); 20 while($row = mysql_fetch_array($result,MYSQL_ASSOC)){ 21 if($row['simid']==$master_simid) 22 { 23 $activity['master_simid']=$row['simid']; 24 $activity['master_name']=$row['name'];
單車練習曲建置 25 $activity['master_lat']=$row['lat']; 26 $activity['master_lng']=$row['lng']; 27 }else if($row['simid']==$simid) 28 { 29 $activity['user_simid']=$row['simid']; 30 $activity['user_name']=$row['name']; 31 $activity['user_lat']=$row['lat']; 32 $activity['user_lng']=$row['lng']; 33 }else 34 35 array_push($data,$row); 36 } 37 //將所有查詢出來的資料放入 $data 陣列當中
單車練習曲建置 25 } 26 27 $activity['list']=$data; 28 29 echo json_encode($activity); 30 } 31 32 33 34 35 36 37
單車練習曲建置 10-2.單車練習曲功能 Layout map.xml 1 2 3 4 5 6 7 8 9 10 11 <?xml version="1.0" encoding="utf-8"?> 2 <fragment xmlns:android="http://schemas.android.com/apk/res/android" 3 android:id="@+id/map" 4 android:layout_width="match_parent" 5 android:layout_height="match_parent" 6 class="com.google.android.gms.maps.MapFragment"/> 7 8 9 10 11
單車練習曲建置 10-3.單車練習曲功能 map.java 打開 map.java 1 import java.util.ArrayList; 2 import java.util.HashMap; 3 import org.apache.http.HttpEntity; 4 import org.apache.http.HttpResponse; 5 import org.apache.http.client.HttpClient; 6 import org.apache.http.client.methods.HttpGet; 7 import org.apache.http.impl.client.DefaultHttpClient; 8 import org.apache.http.util.EntityUtils; 9 import org.json.JSONArray; 10 import org.json.JSONException; 11 import org.json.JSONObject; 打開 map.java
單車練習曲建置 13 import android.app.Activity; 14 import android.content.Context; 15 import android.graphics.Color; 16 import android.location.Location; 17 import android.os.Bundle; 18 import android.os.Handler; 19 import android.os.Message; 20 import android.telephony.TelephonyManager; 21 import android.util.Log; 22 import com.google.android.gms.maps.CameraUpdateFactory; 23 import com.google.android.gms.maps.GoogleMap; 24 import com.google.android.gms.maps.MapFragment;
單車練習曲建置 25 import com.google.android.gms.maps.model.BitmapDescriptorFactory; 26 import com.google.android.gms.maps.model.CameraPosition; 27 import com.google.android.gms.maps.model.CircleOptions; 28 import com.google.android.gms.maps.model.LatLng; 29 import com.google.android.gms.maps.model.Marker; 30 import com.google.android.gms.maps.model.MarkerOptions; 31 import com.google.android.gms.maps.model.PolylineOptions; 32 33 public class Map extends Activity { 34 Location location; 35 GoogleMap mMap; 36 LatLng latlng = new LatLng(22.764702, 120.371935);
單車練習曲建置 37 String aid = ""; 38 boolean checkDFirst= true; 39 private Handler handler = new Handler(); 40 ArrayList ALDirection; 41 HashMap<String,Marker> Member=new HashMap<String,Marker>(); 42 double setDistance=5; 43 //設定隊員與領隊相差多少距離(公尺)提醒 44 45 @Override 46 protected void onCreate(Bundle savedInstanceState) { 47 super.onCreate(savedInstanceState); 48
單車練習曲建置 49 setContentView(R.layout.map); 50 findView(); 51 52 getValue(); 53 //取得前頁 Intent 的相關參數 54 setMap(); 55 //設置地圖 56 simid(); 57 //取得手機編號 58 SetMarker Stm = new SetMarker(); 59 Stm.start(); 60 //啟用 Thread 來執行 Service
單車練習曲建置 61 } 62 63 public void findView() { 64 } 62 63 public void findView() { 64 mMap = ((MapFragment) getFragmentManager().findFragmentById(R.id.map)) 65 .getMap(); 66 67 68 public void getValue() { 69 Bundle bundle = new Bundle(); 70 bundle = this.getIntent().getExtras(); 71 aid = bundle.getString("aid"); 72 //設定活動地圖標記位置經緯度
單車練習曲建置 73 } 74 public void setMap(){ 75 mMap.moveCamera(CameraUpdateFactory.newLatLngZoom(latlng, 15.0f)); 76 //設置地圖 Camera 移動位置與縮放大小 77 } 78 79 public void simid() { 80 TelephonyManager manager = 81 (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE); 82 value.simid = manager.getDeviceId(); 83 //透過 TelephonyManager 取得 SimCard ID,存放到 value.simid 84
單車練習曲建置 85 class SetMarker extends Thread { 86 @Override 87 public void run() { 88 // TODO Auto-generated method stub 89 super.run(); 90 try { 91 while(true) 92 { 93 HttpClient client = new DefaultHttpClient(); 94 String url = value.service + "joinUser.php?aid=" 95 + aid + "&simid=" + value.simid; 96 HttpGet get = new HttpGet(url);
單車練習曲建置 97 HttpResponse response = client.execute(get); 98 HttpEntity resEntity = response.getEntity(); 99 String result = EntityUtils.toString(resEntity); 100 //Log.d("test", "result"+result); 101 Bundle countBundle = new Bundle(); 102 countBundle.putString("list", result); 103 Message msg = new Message(); 104 msg.setData(countBundle); 105 //將 Service 資料回傳並存放在 Message 當中 106 mHandler.sendMessage(msg); 107 //透過 Handler 來傳遞資料並更新UI(地圖標記) 108 Thread.sleep(5000);
單車練習曲建置 private Handler mHandler = new Handler() { 109 } 110 } 110 } catch (Exception e) { 111 e.printStackTrace(); 112 } 113 } 114 } 115 116 private Handler mHandler = new Handler() { 117 public void handleMessage(Message msg) { 118 super.handleMessage(msg); 119 try { 120 String JsonStr=msg.getData().getString("list");
單車練習曲建置 121 Log.i("Json", JsonStr); 122 JSONObject AllObject=new JSONObject(JsonStr); 123 //路徑規劃(繪製路徑),僅執行一次 124 if(checkDFirst) 125 { 126 String Direction=AllObject.getString("direction"); 127 ALDirection=decodePoly(Direction); 128 drawPath(ALDirection); 129 checkDFirst=false; 130 } 131 double[] master_location=addMasterMO(AllObject); 132 //設定領隊標記,並且回傳座標
單車練習曲建置 } 133 if(!AllObject.isNull("user_name")) 134 { 135 { 135 addUserMO(AllObject,master_location); 136 //若使用者非領隊則新增活動參與人標記 137 } 138 JSONArray jsonData = AllObject.getJSONArray("list"); 139 JSONObject jsonObject; 140 //取出其他活動參與人資訊 141 142 for (int i = 0; i < jsonData.length(); i++) { 143 jsonObject = jsonData.getJSONObject(i); 144 String simid=jsonObject.getString("simid");
單車練習曲建置 146 double lat = Double.parseDouble(jsonObject 147 .getString("lat")); 148 double lng = Double.parseDouble(jsonObject 149 .getString("lng")); 150 String title = jsonObject.getString("name"); 151 double distance=getDistance(master_location[0] 152 ,master_location[1],lat,lng); 153 //計算領隊與所有參與人位置之間的距離 154 LatLng latLng = new LatLng(lat, lng); 155 setMarker(simid, latLng, title, distance 156 , BitmapDescriptorFactory.HUE_VIOLET 157 , BitmapDescriptorFactory.HUE_AZURE);
單車練習曲建置 } 158 159 //設定標記屬性(超出位置呈現紫色,未超出呈現藍色) 160 } 159 //設定標記屬性(超出位置呈現紫色,未超出呈現藍色) 160 CameraPosition cameraPosition = new CameraPosition.Builder() 161 .target(Member.get(value.simid).getPosition()) 162 .zoom(19) 163 .build(); 164 //鏡頭移到定位地點 165 mMap.animateCamera(CameraUpdateFactory 166 .newCameraPosition(cameraPosition)); 167 } catch (JSONException e) { 168 e.printStackTrace(); 169 }
單車練習曲建置 170 } 171 }; 172 173 //領隊標記屬性設定 174 } 171 }; 172 173 //領隊標記屬性設定 174 private double[] addMasterMO(JSONObject AllObject) 175 throws NumberFormatException, JSONException 176 { 177 String simid = AllObject.getString("master_simid"); 178 double lat = Double.parseDouble(AllObject 179 .getString("master_lat")); 180 double lng = Double.parseDouble(AllObject 181 .getString("master_lng"));
單車練習曲建置 182 String title = AllObject.getString("master_name"); 183 //解析並取出領隊經緯度與名稱 184 LatLng latLng = new LatLng(lat, lng); 185 Marker marker=Member.get(simid); 186 187 if(marker==null) 188 { 189 //如果沒有這個標記則建立一個新的橘色標記 190 MarkerOptions markerOptions = new MarkerOptions(); 191 markerOptions.position(latLng); 192 markerOptions.title(title); 193 markerOptions
單車練習曲建置 194 .icon(BitmapDescriptorFactory 195 .defaultMarker(BitmapDescriptorFactory.HUE_ORANGE)); 196 Member.put(simid,mMap.addMarker(markerOptions)); 197 }else 198 { 199 marker.setPosition(latLng); 200 marker.setTitle(title); 201 } 202 return new double[]{lat,lng}; 203 } 204 205
單車練習曲建置 206 //自己標記屬性設定 207 private void addUserMO(JSONObject AllObject,double[] master_location) 208 throws NumberFormatException, JSONException 209 { 210 String simid=AllObject.getString("user_simid"); 211 double lat = Double.parseDouble(AllObject 212 .getString("user_lat")); 213 double lng = Double.parseDouble(AllObject 214 .getString("user_lng")); 215 String title = AllObject.getString("user_name"); 216 double distance=getDistance(master_location[0],master_location[1],lat,lng); 217 //計算領隊與自己當前位置之間的距離
單車練習曲建置 218 LatLng latLng = new LatLng(lat, lng); 219 setMarker(simid 220 ,latLng 221 ,title 222 ,distance 223 ,BitmapDescriptorFactory.HUE_VIOLET 224 ,BitmapDescriptorFactory.HUE_YELLOW); 225 } 226 //設定標記屬性(超出位置呈現紫色,未超出呈現黃色) 227 private void setMarker(String simid 228 ,LatLng latLng 229 ,String title
單車練習曲建置 230 ,double distance 231 ,float exceed 232 ,float notExceed) 233 { 234 Marker marker=Member.get(simid); 235 if(marker==null) 236 { 237 //如果沒有這個標籤則建立新標記 238 MarkerOptions markerOptions = new MarkerOptions(); 239 markerOptions.position(latLng); 240 markerOptions.title(title); 241 //超出距離切換座標顏色
單車練習曲建置 242 if(setDistance < distance) 243 { 244 { 244 markerOptions.icon(BitmapDescriptorFactory.defaultMarker(exceed)); 245 //兩點間超出距離設定標記顏色(exceed) 246 }else 247 248 markerOptions.icon(BitmapDescriptorFactory.defaultMarker(notExceed)); 249 //兩點間未超出距離設定標記顏色(notExceed) 250 } 251 Member.put(simid,mMap.addMarker(markerOptions)); 252 }else 253 {
單車練習曲建置 254 marker.setPosition(latLng); 255 marker.setTitle(title); 256 if(setDistance<distance) 257 { 258 marker.setIcon(BitmapDescriptorFactory.defaultMarker(exceed)); 259 //兩點間超出距離設定標記顏色(exceed) 260 }else 261 262 marker.setIcon(BitmapDescriptorFactory.defaultMarker(notExceed)); 263 //兩點間未超出距離設定標記顏色(notExceed) 264 } 265 }
單車練習曲建置 266 } 267 268 public double getDistance(double lat1, double lon1, double lat2, double lon2) { 269 float[] data = new float[1]; 270 Location.distanceBetween(lat1, lon1, lat2, lon2, data); 271 //計算兩座標之間的距離 272 return data[0]; 273 274 //路線規劃繪圖 275 private void drawPath(final ArrayList points) 276 { 277 Runnable r1=new Runnable() {
單車練習曲建置 278 @Override 279 public void run() { 280 // TODO Auto-generated method stub 281 mMap.addPolyline(new PolylineOptions(). 282 addAll(points). 283 width(5). 284 color(Color.BLUE)); 285 286 for(int i=0;i<points.size();i++) 287 { 288 mMap.addCircle(new CircleOptions(). 289 center(points.get(i)).
單車練習曲建置 290 radius(5). 291 strokeWidth(5). 292 strokeColor(Color.RED)); 293 } 294 mMap.addMarker(new MarkerOptions() 295 .position(points.get(0)) 296 .title("我是起點") 297 .snippet("我是起點")); 298 299 .position(points.get(points.size()-1)) 300 .title("我是終點") 301 .snippet("我是終點"));
單車練習曲建置 } 303 304 }; 305 handler.post(r1); 306 } 307 308 /** 309 } 304 }; 305 handler.post(r1); 306 } 307 308 /** 309 * 解碼折線點的方法 310 * */ 311 private ArrayList decodePoly(String encoded) { 312 ArrayList poly = new ArrayList(); 313 int index = 0, len = encoded.length(); 314 int lat = 0, lng = 0;
單車練習曲建置 315 while (index < len) { 316 int b, shift = 0, result = 0; 317 do { 318 b = encoded.charAt(index++) - 63; 319 result |= (b & 0x1f) << shift; 320 shift += 5; 321 } while (b >= 0x20); 322 int dlat = ((result & 1) != 0 ? ~(result >> 1) : (result >> 1)); 323 lat += dlat; 324 shift = 0; 325 result = 0; 326
單車練習曲建置 328 do { 329 b = encoded.charAt(index++) - 63; 330 result |= (b & 0x1f) << shift; 331 shift += 5; 332 } while (b >= 0x20); 333 int dlng = ((result & 1) != 0 ? ~(result >> 1) : (result >> 1)); 334 lng += dlng; 335 336 LatLng p = new LatLng((((double) lat / 1E5)), 337 (((double) lng / 1E5))); 338 poly.add(p); 339 }
單車練習曲建置 340 return poly; 341 } 342 } 343 344 345 346 347 348 349 350 } 342 } 343 344 345 346 347 348 349 350 351