關於WIFI就很少介紹啦,直接來個段子吧。java
問:「WiFi對人體有傷害麼?」android
答:「不清楚,反正沒有WiFi我就渾身不舒服。sql
比較重要的一點就是WifiManager wm=(WifiManager)Android_Wifi.this.getSystemService(Context.WIFI_SERVICE);數據庫
關閉打開搜索均可以經過調用wm的相關方法實現。可能要開發wifi萬能鑰匙那一類的程序纔用到這個吧,普通應用程序主要就識別是wifi網絡仍是移動網絡。很少講服務器
代碼參考博客:http://blog.csdn.net/sbvfhp/article/details/7007090網絡
重點藍牙:app
一:什麼是藍牙less
1:Bluetooth是目前使用最普遍的無線通信協議,近距離無線通信的標準。傳說瑞典有個國王特別愛吃藍莓致使本身的牙齒每天都是藍色的,在他執政期間這位國王很是善於交際,能說會到,和鄰國的搞得關係很是好,這個Bluetooth的發明者以爲藍牙它的做用就是在近距離溝通周圍的設備,跟這個國王很相似,因而起名叫藍牙。socket
2:主要針對短距離設備通信(10米)ide
3:無線耳機,無線鼠標,無線鍵盤
二:藍牙工做流程圖
首先兩個設備上都要有藍牙設備或者專業一點叫藍牙適配器,以手機和電腦爲例我畫了以下流程圖。其次在手機上進行掃描,掃描周圍藍藍牙設備,先找到手機附近的電腦,而後給它發出一個信號須要進行藍牙的配對,再次返回一個信號說明手機和電腦已經配對成功了,最後配對成功後能夠進行文件傳輸了。這是一個最基本的一個流程。
三:藍牙開發相關類
在Android手機中開發藍牙程序時,離不開幾個類:
BluetoothSocket:close() connect() getInputStream()......
BluetoothServerSocket : accept()
BlutoothAdapter :表明本地的藍牙適配器設備,經過此類可讓用戶能執行基本的藍牙任務。
BluetoothClass: 表明了一個描述設備通用特性和功能的藍牙類。好比一個藍牙類會指定如電話、計算機或耳機的通用設備類型。
BluetoothClass.Service:
BluetoothClass.Device:
BluetoothClass.Device.Major: 定義了主要設備類的常量
其中與藍牙相關的最重要的兩個API
1:BuletoothAdapter
這個類的對象表明了本地的藍牙適配器,至關於藍牙工做流程圖中的手機裏的藍牙適配器,也就是說好比這個應用程序是運行在手機上,那麼手機上的藍牙適配器就是本地藍牙適配器。
2:BuletoothDevice
這個類的對象表明了遠程的藍牙設備,至關於藍牙工做流程圖中的計算機裏的藍牙適配器,也就是說好比這個應用程序是運行在手機上,那麼BuletoothDevice表明了你要鏈接的遠程的那個設備上面的藍牙適配器。
四:藍牙開發步驟:
此部分轉載於 http://zhouyunan2010.iteye.com/blog/1186021
從查找藍牙設備到可以相互通訊要通過幾個基本步驟(本機作爲服務器):
1.設置權限
在manifest中配置
<uses-permission android:name="android.permission.BLUETOOTH"/> <uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>
2.啓動藍牙
首先要查看本機是否支持藍牙,獲取BluetoothAdapter藍牙適配器對象
BluetoothAdapter mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter(); if(mBluetoothAdapter == null){ //代表此手機不支持藍牙 return; } if(!mBluetoothAdapter.isEnabled()){ //藍牙未開啓,則開啓藍牙 Intent enableIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE); startActivityForResult(enableIntent, REQUEST_ENABLE_BT); } //...... public void onActivityResult(int requestCode, int resultCode, Intent data){ if(requestCode == REQUEST_ENABLE_BT){ if(requestCode == RESULT_OK){ //藍牙已經開啓 } } }
3。發現藍牙設備
這裏能夠細分爲幾個方面
(1)使本機藍牙處於可見(即處於易被搜索到狀態),便於其餘設備發現本機藍牙
//使本機藍牙在300秒內可被搜索 private void ensureDiscoverable() { if (mBluetoothAdapter.getScanMode() != BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE) { Intent discoverableIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE); discoverableIntent.putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, 300); startActivity(discoverableIntent); } }
(2)查找已經配對的藍牙設備,即之前已經配對過的設備
Set<BluetoothDevice> pairedDevices = mBluetoothAdapter.getBondedDevices(); if (pairedDevices.size() > 0) { findViewById(R.id.title_paired_devices).setVisibility(View.VISIBLE); for (BluetoothDevice device : pairedDevices) { //device.getName() +" "+ device.getAddress()); } } else { mPairedDevicesArrayAdapter.add("沒有找到已匹對的設備"); }
(3)經過mBluetoothAdapter.startDiscovery();搜索設備,要得到此搜索的結果須要註冊
一個BroadcastReceiver來獲取。先註冊再獲取信息,而後處理
//註冊,當一個設備被發現時調用onReceive IntentFilter filter = new IntentFilter(BluetoothDevice.ACTION_FOUND); this.registerReceiver(mReceiver, filter); //當搜索結束後調用onReceive filter = new IntentFilter(BluetoothAdapter.ACTION_DISCOVERY_FINISHED); this.registerReceiver(mReceiver, filter); //....... private BroadcastReceiver mReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { String action = intent.getAction(); if(BluetoothDevice.ACTION_FOUND.equals(action)){ BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); // 已經配對的則跳過 if (device.getBondState() != BluetoothDevice.BOND_BONDED) { mNewDevicesArrayAdapter.add(device.getName() + "\n" + device.getAddress()); //保存設備地址與名字 } }else if (BluetoothAdapter.ACTION_DISCOVERY_FINISHED.equals(action)) { //搜索結束 if (mNewDevicesArrayAdapter.getCount() == 0) { mNewDevicesArrayAdapter.add("沒有搜索到設備"); } } } };
4.創建鏈接
查找到設備 後,則須要創建本機與其餘設備之間的鏈接。
通常用本機搜索其餘藍牙設備時,本機能夠做爲一個服務端,接收其餘設備的鏈接。
啓動一個服務器端的線程,死循環等待客戶端的鏈接,這與ServerSocket極爲類似。
這個線程在準備鏈接以前啓動
//UUID能夠看作一個端口號 private static final UUID MY_UUID = UUID.fromString("fa87c0d0-afac-11de-8a39-0800200c9a66"); //像一個服務器同樣時刻監聽是否有鏈接創建 private class AcceptThread extends Thread{ private BluetoothServerSocket serverSocket; public AcceptThread(boolean secure){ BluetoothServerSocket temp = null; try { temp = mBluetoothAdapter.listenUsingRfcommWithServiceRecord( NAME_INSECURE, MY_UUID); } catch (IOException e) { Log.e("app", "listen() failed", e); } serverSocket = temp; } public void run(){ BluetoothSocket socket=null; while(true){ try { socket = serverSocket.accept(); } catch (IOException e) { Log.e("app", "accept() failed", e); break; } } if(socket!=null){ //此時能夠新建一個數據交換線程,把此socket傳進去 } } //取消監聽 public void cancel(){ try { serverSocket.close(); } catch (IOException e) { Log.e("app", "Socket Type" + socketType + "close() of server failed", e); } } }
5.交換數據
搜索到設備後能夠獲取設備的地址,經過此地址獲取一個BluetoothDeviced對象,能夠看作客戶端,經過此對象device.createRfcommSocketToServiceRecord(MY_UUID);同一個UUID可與服務器創建鏈接獲取另外一個socket對象,由此服務端與客戶端各有一個socket對象,此時
他們能夠互相交換數據了。
創立客戶端socket可創建線程
//另外一個設備去鏈接本機,至關於客戶端 private class ConnectThread extends Thread{ private BluetoothSocket socket; private BluetoothDevice device; public ConnectThread(BluetoothDevice device,boolean secure){ this.device = device; BluetoothSocket tmp = null; try { tmp = device.createRfcommSocketToServiceRecord(MY_UUID_SECURE); } catch (IOException e) { Log.e("app", "create() failed", e); } } public void run(){ mBluetoothAdapter.cancelDiscovery(); //取消設備查找 try { socket.connect(); } catch (IOException e) { try { socket.close(); } catch (IOException e1) { Log.e("app", "unable to close() "+ " socket during connection failure", e1); } connetionFailed(); //鏈接失敗 return; } //此時能夠新建一個數據交換線程,把此socket傳進去 } public void cancel() { try { socket.close(); } catch (IOException e) { Log.e("app", "close() of connect socket failed", e); } } }
6.創建數據通訊線程,進行讀取數據
//創建鏈接後,進行數據通訊的線程 private class ConnectedThread extends Thread{ private BluetoothSocket socket; private InputStream inStream; private OutputStream outStream; public ConnectedThread(BluetoothSocket socket){ this.socket = socket; try { //得到輸入輸出流 inStream = socket.getInputStream(); outStream = socket.getOutputStream(); } catch (IOException e) { Log.e("app", "temp sockets not created", e); } } public void run(){ byte[] buff = new byte[1024]; int len=0; //讀數據需不斷監聽,寫不須要 while(true){ try { len = inStream.read(buff); //把讀取到的數據發送給UI進行顯示 Message msg = handler.obtainMessage(BluetoothChat.MESSAGE_READ, len, -1, buff); msg.sendToTarget(); } catch (IOException e) { Log.e("app", "disconnected", e); connectionLost(); //失去鏈接 start(); //從新啓動服務器 break; } } } public void write(byte[] buffer) { try { outStream.write(buffer); // Share the sent message back to the UI Activity handler.obtainMessage(BluetoothChat.MESSAGE_WRITE, -1, -1, buffer) .sendToTarget(); } catch (IOException e) { Log.e("app", "Exception during write", e); } } public void cancel() { try { socket.close(); } catch (IOException e) { Log.e("app", "close() of connect socket failed", e); } } }
到這裏,藍牙通訊的基本操做已經所有完成。
五:藍牙聊天室開發
源碼參考 http://download.csdn.net/detail/mr_raptor/8033951#comment
此demo界面優美,實現基本功能,但不能保存聊天記錄,黑屏自動斷線,在此基礎作出修改。
在EditText中插入表情圖片 (CharacterStyle&SpannableString)
源碼分析:
Utils.java
1 package cn.com.farsgiht.bluetoothdemo.utils; 2 3 import android.app.Activity; 4 import android.app.Notification; 5 import android.app.NotificationManager; 6 import android.app.PendingIntent; 7 import android.content.Context; 8 import android.content.Intent; 9 import cn.com.farsgiht.bluetoothdemo.R; 10 11 public class Utils { 12 public static final int NOTIFY_ID1 = 1001; 13 14 public static void notifyMessage(Context context, String msg, Activity activity){ 15 //Notification builder; 16 PendingIntent contentIntent = null; 17 NotificationManager nm; 18 // 發送通知須要用到NotificationManager對象 19 nm = (NotificationManager)context.getSystemService(Context.NOTIFICATION_SERVICE); 20 // 消息對象 21 Intent notificationIntent = new Intent(context, activity.getClass()); 22 // PendingIntent.getActivity(Context context, int requestCode, Intent intent, int flags) 23 // 用來得到一個掛起的PendingIntent,讓該Intent去啓動新的Activity來處理通知 24 contentIntent = PendingIntent.getActivity(context, 0, notificationIntent, 0); 25 26 27 //定義通知欄展示的內容信息 28 int icon = R.drawable.icon; 29 long when = System.currentTimeMillis(); 30 Notification notification = new Notification(icon, msg, when); 31 notification.defaults |= Notification.DEFAULT_VIBRATE; 32 notification.defaults |= Notification.DEFAULT_SOUND; // 調用系統自帶聲音 33 notification.flags |= Notification.FLAG_AUTO_CANCEL; // 點擊清除按鈕或點擊通知後會自動消失 34 notification.defaults |= Notification.DEFAULT_LIGHTS; 35 notification.vibrate = new long[]{300, 500}; 36 notification.setLatestEventInfo(context, "BluetoothChat", msg, contentIntent); 37 /* // 定製咱們要在狀態欄顯示的通知樣式 38 builder = new Notification(context); 39 builder.setContentIntent(contentIntent) 40 .setSmallIcon(R.drawable.ic_launcher)//設置狀態欄裏面的圖標(小圖標) .setLargeIcon(BitmapFactory.decodeResource(res, R.drawable.i5))//下拉下拉列表裏面的圖標(大圖標) .setTicker("this is bitch!") //設置狀態欄的顯示的信息 41 .setWhen(System.currentTimeMillis())//設置時間發生時間 42 .setAutoCancel(true)//設置能夠清除 43 .setContentTitle("This is ContentTitle")//設置下拉列表裏的標題 44 .setContentText("this is ContentText");//設置上下文內容 45 */ 46 // 得到剛纔建立的通知對象 47 // Notification notification = builder.getNotification();//獲取一個Notification 48 49 // 經過NotificationManger來發送通知消息 50 // 參數1通知的ID,參數2發送哪一個通知 51 nm.notify(NOTIFY_ID1, notification); 52 } 53 }
通知欄相關代碼
2. PageIndicatorView.java
1 package cn.com.farsgiht.bluetoothdemo.UI; 2 3 import cn.com.farsgiht.bluetoothdemo.R; 4 import android.content.Context; 5 import android.graphics.Bitmap; 6 import android.graphics.BitmapFactory; 7 import android.widget.ImageView; 8 import android.widget.LinearLayout; 9 10 public class PageIndicatorView extends ImageView { 11 private final String TAG = "PageIndicatorView"; 12 private LinearLayout mPageIndicLayout; 13 14 public PageIndicatorView(Context context) { 15 super(context); 16 setSelectedView(false); 17 } 18 19 public void setSelectedView(boolean selected){ 20 Bitmap bitmap; 21 if(selected){ 22 bitmap = BitmapFactory.decodeResource(getResources(), 23 R.drawable.page_select); 24 }else{ 25 bitmap = BitmapFactory.decodeResource(getResources(), 26 R.drawable.page_item); 27 } 28 this.setImageBitmap(bitmap); 29 } 30 }
表情頁頁面指示器
3. DrawerHScrollView.java
1 package cn.com.farsgiht.bluetoothdemo.UI; 2 3 import java.util.Hashtable; 4 5 import android.content.Context; 6 import android.util.AttributeSet; 7 import android.util.Log; 8 import android.view.Gravity; 9 import android.view.ViewGroup; 10 import android.view.ViewParent; 11 import android.widget.HorizontalScrollView; 12 import android.widget.LinearLayout; 13 14 public class DrawerHScrollView extends HorizontalScrollView { 15 private static final String TAG = "DrawerHScrollView"; 16 17 private int currentPage = 0; 18 private int totalPages = 1; 19 private static Hashtable<Integer, Integer> positionLeftTopOfPages = new Hashtable(); 20 private LinearLayout mPageIndicLayout; 21 private Context mContext; 22 23 public DrawerHScrollView(Context context) { 24 super(context); 25 this.mContext = context; 26 } 27 28 public DrawerHScrollView(Context context, AttributeSet attrs) { 29 super(context, attrs); 30 this.mContext = context; 31 } 32 33 public DrawerHScrollView(Context context, AttributeSet attrs, int defStyle) { 34 super(context, attrs, defStyle); 35 this.mContext = context; 36 } 37 38 public void cleanup(){ 39 currentPage = 0; 40 totalPages = 1; 41 if(positionLeftTopOfPages != null){ 42 positionLeftTopOfPages.clear(); 43 } 44 } 45 46 public void setParameters(int totalPages, int currentPage, int scrollDisX, int space) { 47 Log.d(TAG, "~~~~~setParameters totalPages:"+totalPages +",currentPage:"+ currentPage +",scrollDisX:"+scrollDisX); 48 this.totalPages = totalPages; 49 this.currentPage = currentPage; 50 positionLeftTopOfPages.clear(); 51 for (int i = 0;i < totalPages;i++){ 52 int posx = (scrollDisX) * i - space; 53 positionLeftTopOfPages.put(i, posx); 54 Log.d(TAG, "~~~~~setParameters i:"+i +",posx:"+posx); 55 } 56 smoothScrollTo(0, 0); 57 setPageIndicLayout(); 58 if(mPageIndicLayout != null){ 59 updateDrawerPageLayout(totalPages, currentPage); 60 } 61 } 62 63 public void setPageIndicLayout(){ 64 // 添加表情多頁圖標佈局 65 mPageIndicLayout = new LinearLayout(mContext); 66 mPageIndicLayout.setGravity(Gravity.CENTER); 67 ViewGroup.LayoutParams params = new ViewGroup.LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.WRAP_CONTENT); 68 mPageIndicLayout.setLayoutParams(params); 69 ViewParent parent = this.getParent(); 70 if(parent instanceof LinearLayout){ 71 LinearLayout layout = (LinearLayout)parent; 72 layout.addView(mPageIndicLayout, new LinearLayout.LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.WRAP_CONTENT)); 73 } 74 } 75 76 @Override 77 public void fling(int velocityX) { 78 Log.v(TAG, "-->fling velocityX:"+velocityX); 79 boolean change_flag = false; 80 if (velocityX > 0 && (currentPage < totalPages - 1)){ 81 currentPage++; 82 change_flag = true; 83 } else if (velocityX < 0 && (currentPage > 0)){ 84 currentPage--; 85 change_flag = true; 86 } 87 if (change_flag){ 88 int postionTo = (Integer)positionLeftTopOfPages.get(new Integer(currentPage)).intValue(); 89 Log.v(TAG, "------smoothScrollTo posx:"+postionTo); 90 smoothScrollTo(postionTo, 0); 91 updateDrawerPageLayout(totalPages, currentPage); 92 } 93 //super.fling(velocityX); 94 } 95 96 public void updateDrawerPageLayout(int total_pages, int sel_page) { 97 Log.e(TAG, "~~~updateBooksPageLayout total_pages:" + total_pages 98 + ",sel_page:" + sel_page); 99 mPageIndicLayout.removeAllViews(); 100 if (total_pages <= 0 || sel_page < 0 || sel_page >= total_pages) { 101 Log.e(TAG, "total_pages or sel_page is outofrange."); 102 return; 103 } 104 for (int i = 0; i < total_pages; i++) { 105 if (i != 0) { 106 LinearLayout.LayoutParams params = new LinearLayout.LayoutParams( 107 LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT); 108 params.setMargins(5, 0, 0, 0); 109 mPageIndicLayout.addView(new PageIndicatorView(mContext), params); 110 } else { 111 mPageIndicLayout.addView(new PageIndicatorView(mContext)); 112 } 113 } 114 PageIndicatorView selItem = (PageIndicatorView) mPageIndicLayout 115 .getChildAt(sel_page); 116 selItem.setSelectedView(true); 117 } 118 }
定製的表情頁面,繼承HorizontalScrollView,支持水平滑動。須要說明的是頁面指示器的佈局是經過代碼實現的,而不是在xml文件中
4. ChatListViewAdapter.java
消息適配器。發送的消息存放在mDatalist中
在activity_chat.xml設置android:listSelector="#00FFFFFF"能夠避免點擊消息時出現的矩形框。
重寫的getView中實現本身發送的消息顯示在右邊,別人發送的消息顯示在左邊。
TouchListener實現觸摸消息變成密碼形態,並在onclick方法中再次調用。我想大概是由於觸摸和點擊原本就很差區分吧。同時在點擊事件裏不要忘了設置消息顯示的形態,由於listview在重繪時須要考慮這一點。
Adapter的做用就是ListView界面與數據之間的橋樑,當列表裏的每一項顯示到頁面時,都會調用Adapter的getView方法返回一個View。
優化的思路兩種:
1. View的重用
View的每次建立是比較耗時的,所以對於getview方法傳入的convertView應充分利用 != null的判斷
2.ViewHolder的應用
View的findViewById()方法也是比較耗時的,所以須要考慮只調用一次,ViewHolder就是一個持有者的類,他裏面通常沒有方法,只有屬性,做用就是一個臨時的儲存器,把你getView方法中每次返回的View存起來,能夠下次再用。這樣作的好處就是沒必要每次都到佈局文件中去拿到你的View,提升了效率。
5. Task.java定義了一些常量
6. TaskService.java 在Service中新開線程和直接新開線程的區別與意義
前面寫到的藍牙通訊的幾步走也是在這裏面實現的
分爲幾個線程:
AcceptThread 等待客戶端鏈接線程
ConnectThread(BluetoothDevice device) 做爲客戶端鏈接指定的藍牙設備線程
ConnectedThread(BluetoothSocket socket) 在客戶端,使用一個單獨的BluetoothSocket類去初始化一個外接鏈接和管理該鏈接,發送消息也在這個線程裏面
TaskThread總線程和mTaskList一塊兒協調上面三個線程的運行工做。
要搞清TaskServic的原理,理清兩個變量很重要:mServiceHandler mActivityHandler
mServiceHandler:監控着鏈接狀態的變化,如斷線,鏈接成功,鏈接中
mActivityHandler:監控着信息狀態的變化,起到通知主Activity的做用。如:發消息,收消息。
7. SoundEffect.java
音效相關的類,實現了一個OnLoadCompleteListener接口,還有一個play() 方法,決定播放哪一首歌曲。
8. DataProtocol 和 Message
DataProtocol 是對不一樣的消息類型進行打包和解包
Message 定義了消息的幾個參數
9. SelectDevice DownloadActivity ChatActivity 是應用程序裏面的三個Activity
本身修改說明:
ChatActivity加入再按一次退出功能,更加人性化。加入消息記錄功能。
消息記錄實現概述:當在服務中開始收發發送消息時,就經過mActivityHandler通知ChatActivity,完成數據庫讀寫操做。
ChatActivity.java:
1 package cn.com.farsgiht.bluetoothdemo; 2 3 import java.text.SimpleDateFormat; 4 import java.util.ArrayList; 5 import java.util.HashMap; 6 import android.app.Activity; 7 import android.app.AlertDialog; 8 import android.bluetooth.BluetoothAdapter; 9 import android.bluetooth.BluetoothDevice; 10 import android.content.ContentValues; 11 import android.content.Context; 12 import android.content.DialogInterface; 13 import android.content.DialogInterface.OnClickListener; 14 import android.content.Intent; 15 import android.database.sqlite.SQLiteDatabase; 16 import android.graphics.Bitmap; 17 import android.graphics.BitmapFactory; 18 import android.os.Bundle; 19 import android.os.Handler; 20 import android.os.Message; 21 import android.text.Spannable; 22 import android.text.SpannableString; 23 import android.text.style.ImageSpan; 24 import android.util.DisplayMetrics; 25 import android.util.Log; 26 import android.view.Display; 27 import android.view.Gravity; 28 import android.view.KeyEvent; 29 import android.view.LayoutInflater; 30 import android.view.Menu; 31 import android.view.MenuInflater; 32 import android.view.MenuItem; 33 import android.view.View; 34 import android.view.ViewGroup; 35 import android.view.inputmethod.InputMethodManager; 36 import android.widget.AdapterView; 37 import android.widget.AdapterView.OnItemClickListener; 38 import android.widget.Button; 39 import android.widget.EditText; 40 import android.widget.GridView; 41 import android.widget.ImageView; 42 import android.widget.LinearLayout; 43 import android.widget.LinearLayout.LayoutParams; 44 import android.widget.ListView; 45 import android.widget.SimpleAdapter; 46 import android.widget.Toast; 47 import cn.com.farsgiht.bluetoothdemo.UI.ChatListViewAdapter; 48 import cn.com.farsgiht.bluetoothdemo.UI.DrawerHScrollView; 49 import cn.com.farsgiht.bluetoothdemo.sound.SoundEffect; 50 import cn.com.farsgiht.bluetoothdemo.task.Task; 51 import cn.com.farsgiht.bluetoothdemo.task.TaskService; 52 import cn.com.farsgiht.bluetoothdemo.utils.Utils; 53 54 public class ChatActivity extends Activity implements View.OnClickListener{ 55 private final String TAG = "ChatActivity"; 56 public static int sAliveCount = 0; 57 public static final String EXTRA_MESSAGER = "cn.com.farsgiht.bluetoothdemo.BUNDLE"; 58 public static final String DEVICE_NAME = "device_name"; 59 // 藍牙狀態變量 60 private static int sBTState = -1; 61 62 private final int REQUES_BT_ENABLE_CODE = 123; 63 private final int REQUES_SELECT_BT_CODE = 222; 64 65 private ListView mList; 66 private EditText mInput; 67 private Button mSendBtn; 68 private ImageView mEmoButton; 69 private GridView mGridView; 70 private boolean isUpdate = false; 71 private BluetoothDevice mRemoteDevice; 72 73 private LinearLayout mRootLayout, mChatLayout; 74 75 private View mEmoView; 76 private boolean isShowEmo = false; 77 private boolean isHaspressed = false; 78 private int mScrollHeight; 79 80 private DrawerHScrollView mScrollView; 81 private ChatListViewAdapter mAdapter2; 82 private ArrayList<HashMap<String, Object>> mChatContent2 = new ArrayList<HashMap<String, Object>>(); 83 private BluetoothAdapter mBluetoothAdapter; 84 85 private ArrayList<HashMap<String, Object>> mEmoList = new ArrayList<HashMap<String, Object>>(); 86 // 已鏈接設備的名字 87 private String mConnectedDeviceName = null; 88 89 private Handler mHandler = new Handler(){ 90 @Override 91 public void handleMessage(Message msg) { 92 //設置聊天信息的時間 93 SimpleDateFormat df0 = new SimpleDateFormat("MM-dd HH:mm:ss"); 94 String pdate=df0.format(System.currentTimeMillis()).toString(); 95 switch(msg.what){ 96 case -1: 97 showToast("沒有鏈接其它用戶,點擊\"Menu\"掃描並選擇周國用戶"); 98 SoundEffect.getInstance(ChatActivity.this).play(SoundEffect.SOUND_ERR); 99 break; 100 case Task.TASK_SEND_MSG: 101 // showToast(msg.obj.toString()); 102 String writeMessage = msg.obj.toString(); 103 if(writeMessage!=null && isHaspressed) 104 { 105 //將發送的信息插入到數據庫 106 ContentValues values=new ContentValues(); 107 values.put("name", "我"); 108 values.put("pdate",pdate); 109 values.put("informations", writeMessage); 110 //建立數據庫 111 DatabaseHelper insertdbHelper=new DatabaseHelper(ChatActivity.this,"zhsf_db"); 112 SQLiteDatabase insertdb=insertdbHelper.getWritableDatabase(); 113 insertdb.insert("info", null, values); 114 } 115 if(sAliveCount <= 0){ 116 Utils.notifyMessage(ChatActivity.this, msg.obj.toString(), ChatActivity.this); 117 } 118 break; 119 case Task.TASK_RECV_MSG: 120 String readMessage =((HashMap<String,Object>) msg.obj).get(ChatListViewAdapter.KEY_TEXT).toString(); 121 mConnectedDeviceName = ((HashMap<String,Object>) msg.obj).get(ChatListViewAdapter.KEY_NAME).toString(); 122 if(readMessage!=null) 123 { 124 //將接受的信息插入到數據庫 125 ContentValues values2=new ContentValues(); 126 values2.put("name", mConnectedDeviceName); 127 values2.put("pdate",pdate); 128 values2.put("informations", readMessage); 129 DatabaseHelper insertdbHelper2=new DatabaseHelper(ChatActivity.this,"zhsf_db"); 130 SQLiteDatabase insertdb2=insertdbHelper2.getWritableDatabase(); 131 insertdb2.insert("info", null, values2); 132 } 133 134 if(msg.obj == null) 135 return; 136 if(msg.obj instanceof HashMap<?, ?>){ 137 showTargetMessage((HashMap<String, Object>) msg.obj); 138 } 139 if(sAliveCount <= 0){ 140 Utils.notifyMessage(ChatActivity.this, "您有未讀取消息", ChatActivity.this); 141 } 142 break; 143 case Task.TASK_GET_REMOTE_STATE: 144 setTitle((String)msg.obj); 145 if(sAliveCount <= 0){ 146 if(isBTStateChanged(msg.arg1) && msg.arg1 != TaskService.BT_STAT_WAIT) 147 Utils.notifyMessage(ChatActivity.this, (String)msg.obj, ChatActivity.this); 148 } 149 break; 150 151 } 152 } 153 }; 154 155 @Override 156 protected void onCreate(Bundle savedInstanceState) { 157 super.onCreate(savedInstanceState); 158 setContentView(R.layout.activity_chat); 159 160 // 得到藍牙管理器 161 mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter(); 162 if (mBluetoothAdapter == null) { 163 Log.e(TAG, "Your device is not support Bluetooth!"); 164 Toast.makeText(this, "該設備沒有藍牙設備", Toast.LENGTH_LONG).show(); 165 return; 166 } 167 168 mRootLayout = (LinearLayout) findViewById(R.id.root); 169 mChatLayout = (LinearLayout) findViewById(R.id.topPanel); 170 mList = (ListView) findViewById(R.id.listView1); 171 172 mAdapter2 = new ChatListViewAdapter(this, mChatContent2); 173 174 mList.setAdapter(mAdapter2); 175 176 // 初始化表情 177 mEmoView = initEmoView(); 178 179 mInput = (EditText) findViewById(R.id.inputEdit); 180 mInput.setOnClickListener(new android.view.View.OnClickListener() { 181 @Override 182 public void onClick(View v) { 183 // 點擊輸入框後,隱藏表情,顯示輸入法 184 InputMethodManager imm = (InputMethodManager)getSystemService(Context.INPUT_METHOD_SERVICE); 185 imm.showSoftInput(mInput, 0); 186 showEmoPanel(false); 187 } 188 }); 189 190 mSendBtn = (Button) findViewById(R.id.sendBtn); 191 mEmoButton = (ImageView) findViewById(R.id.emotionBtn); 192 193 mSendBtn.setOnClickListener(this); 194 mEmoButton.setOnClickListener(this); 195 196 //--------------------------------------------------------------------- 197 // 打開藍牙設備 198 if (!mBluetoothAdapter.isEnabled()) { 199 Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE); 200 startActivityForResult(enableBtIntent, REQUES_BT_ENABLE_CODE); 201 }else{ 202 // 默認設備做爲服務端 203 startServiceAsServer(); 204 } 205 //--------------------------------------------------------------------- 206 } 207 208 private View initEmoView(){ 209 if(mEmoView == null){ 210 LayoutInflater inflater = getLayoutInflater(); 211 mEmoView = inflater.inflate(R.layout.emo_layout, null); 212 213 mScrollView = (DrawerHScrollView) mEmoView.findViewById(R.id.scrollView); 214 mGridView = (GridView) mEmoView.findViewById(R.id.gridView); 215 mGridView.setOnItemClickListener(new OnItemClickListener() { 216 @Override 217 public void onItemClick(AdapterView<?> parent, View view, 218 int position, long id) { 219 // 在android中要顯示圖片信息,必須使用Bitmap位圖的對象來裝載 220 Bitmap bitmap = BitmapFactory.decodeResource(getResources(), (Integer) mEmoList.get(position).get("img")); 221 ImageSpan imageSpan = new ImageSpan(ChatActivity.this, bitmap); 222 SpannableString spannableString = new SpannableString((String) mEmoList.get(position).get("text"));//face就是圖片的前綴名 223 spannableString.setSpan(imageSpan, 0, 8,Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); 224 mInput.append(spannableString); 225 System.out.println("mInput:"+mInput.getText()); 226 } 227 }); 228 229 mScrollHeight = setScrollGridView(mScrollView, mGridView, 3); 230 System.out.println("mScrollHeight:" + mScrollHeight); 231 } 232 return mEmoView; 233 } 234 235 236 237 private void startServiceAsServer(){ 238 TaskService.start(this, mHandler); 239 TaskService.newTask(new Task(mHandler, Task.TASK_START_ACCEPT, null)); 240 SoundEffect.getInstance(this).play(SoundEffect.SOUND_PLAY); 241 } 242 243 @Override 244 protected void onResume() { 245 sAliveCount++; 246 super.onResume(); 247 } 248 249 @Override 250 protected void onPause() { 251 sAliveCount--; 252 super.onPause(); 253 } 254 255 @Override 256 protected void onDestroy() { 257 super.onDestroy(); 258 // 關閉藍牙 259 if(mBluetoothAdapter.isEnabled()) 260 mBluetoothAdapter.disable(); 261 // 中止服務 262 TaskService.stop(this); 263 } 264 265 266 @Override 267 public void onClick(View v) { 268 if(v == mSendBtn){ 269 String msg = mInput.getText().toString().trim(); 270 TaskService.newTask(new Task(mHandler, Task.TASK_GET_REMOTE_STATE, null));//經過點擊按鈕觸發相應線程的啓動,比較巧妙,值得學習 271 if(msg.length() == 0){ 272 showToast("聊天內容爲空"); 273 SoundEffect.getInstance(ChatActivity.this).play(SoundEffect.SOUND_ERR); 274 return; 275 } 276 277 //------ DEUBG ------ 278 TaskService.newTask(new Task(mHandler, Task.TASK_SEND_MSG, new Object[]{msg})); 279 showOwnMessage(msg);//立馬顯示本身發送的消息,因此在handler裏面就沒有再作處理 280 isHaspressed = true;//數據庫能夠開始記錄消息啦 281 mInput.setText(""); 282 }else if(v == mEmoButton){ 283 System.out.println("Emo btn clicked"); 284 // 關閉輸入法 285 InputMethodManager imm = (InputMethodManager)getSystemService(Context.INPUT_METHOD_SERVICE); 286 imm.hideSoftInputFromWindow(mInput.getWindowToken(),0); 287 if(isShowEmo){ 288 showEmoPanel(false); 289 }else{ 290 showEmoPanel(true); 291 } 292 } 293 } 294 295 private void showEmoPanel(boolean show){ 296 if(show && !isShowEmo){ 297 mEmoView.setVisibility(View.VISIBLE); 298 mEmoButton.setImageResource(R.drawable.emo_collapse); 299 ViewGroup.LayoutParams params = mChatLayout.getLayoutParams(); 300 params.height = mChatLayout.getHeight() - mScrollHeight; 301 mChatLayout.setLayoutParams(params); 302 isShowEmo = true; 303 }else if(!show && isShowEmo){ 304 mEmoView.setVisibility(View.GONE); 305 mEmoButton.setImageResource(R.drawable.emo_bkg); 306 ViewGroup.LayoutParams params = mChatLayout.getLayoutParams(); 307 params.height = mChatLayout.getHeight() + mScrollHeight; 308 mChatLayout.setLayoutParams(params); 309 isShowEmo = false; 310 } 311 if(!isUpdate && show){ 312 LinearLayout.LayoutParams para = new LinearLayout.LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.WRAP_CONTENT); 313 mRootLayout.addView(mEmoView, para); 314 isUpdate = true; 315 } 316 } 317 318 319 320 private boolean isBTStateChanged(int now){ 321 if(sBTState != now){ 322 sBTState = now; 323 return true; 324 }else{ 325 return false; 326 } 327 } 328 329 /** 330 * 顯示對方信息 331 * @param data 332 */ 333 private void showTargetMessage(HashMap<String, Object> data){ 334 SimpleDateFormat df1 = new SimpleDateFormat("E MM月dd日 HH:mm "); 335 data.put(ChatListViewAdapter.KEY_DATE, df1.format(System.currentTimeMillis()).toString()); 336 data.put(ChatListViewAdapter.KEY_SHOW_MSG, true); 337 mChatContent2.add(data); 338 mAdapter2.notifyDataSetChanged(); 339 SoundEffect.getInstance(ChatActivity.this).play(SoundEffect.SOUND_RECV); 340 } 341 342 /** 343 * 顯示本身信息 344 * @param data 345 */ 346 private void showOwnMessage(String msg){ 347 HashMap<String, Object> map = new HashMap<String, Object>(); 348 map.put(ChatListViewAdapter.KEY_ROLE, ChatListViewAdapter.ROLE_OWN);//哪一個角色的消息 349 map.put(ChatListViewAdapter.KEY_NAME, mBluetoothAdapter.getName()); 350 map.put(ChatListViewAdapter.KEY_TEXT, msg); 351 SimpleDateFormat df2 = new SimpleDateFormat("E MM月dd日 HH:mm "); 352 map.put(ChatListViewAdapter.KEY_DATE, df2.format(System.currentTimeMillis()).toString()); 353 map.put(ChatListViewAdapter.KEY_SHOW_MSG, true); 354 mChatContent2.add(map); 355 mAdapter2.notifyDataSetChanged(); 356 SoundEffect.getInstance(ChatActivity.this).play(SoundEffect.SOUND_SEND); 357 } 358 359 @Override 360 public boolean onCreateOptionsMenu(Menu menu) { 361 MenuInflater inflater = getMenuInflater(); 362 inflater.inflate(R.menu.option_menu, menu); 363 return true; 364 } 365 366 @Override 367 public boolean onOptionsItemSelected(MenuItem item) { 368 switch (item.getItemId()) { 369 case R.id.scan: 370 startActivityForResult(new Intent(this, SelectDevice.class), REQUES_SELECT_BT_CODE); 371 break; 372 case R.id.discoverable: 373 // 調用設置用戶名方法 374 AlertDialog.Builder dlg = new AlertDialog.Builder(this); 375 final EditText devNameEdit = new EditText(this); 376 dlg.setView(devNameEdit); 377 dlg.setTitle("請輸入用戶名"); 378 dlg.setPositiveButton("設置", new OnClickListener() { 379 public void onClick(DialogInterface dialog, int which) { 380 if(devNameEdit.getText().toString().length() != 0) 381 mBluetoothAdapter.setName(devNameEdit.getText().toString()); 382 } 383 }); 384 dlg.create(); 385 dlg.show(); 386 return true; 387 case R.id.record: 388 Intent recordIntent = new Intent(ChatActivity.this, RecordListActivity.class); 389 startActivity(recordIntent); 390 return true; 391 case R.id.exit: 392 Intent aboutIntent = new Intent(ChatActivity.this, DownloadActivity.class); 393 startActivity(aboutIntent); 394 return true; 395 } 396 return false; 397 } 398 399 @Override 400 protected void onActivityResult(int requestCode, int resultCode, Intent data) { 401 if(requestCode == REQUES_BT_ENABLE_CODE && resultCode == RESULT_OK){ 402 startServiceAsServer(); 403 }else if(requestCode == REQUES_SELECT_BT_CODE && resultCode == RESULT_OK){ 404 mRemoteDevice = data.getParcelableExtra("DEVICE"); 405 if(mRemoteDevice == null) 406 return; 407 TaskService.newTask(new Task(mHandler, Task.TASK_START_CONN_THREAD, new Object[]{mRemoteDevice})); 408 } 409 super.onActivityResult(requestCode, resultCode, data); 410 } 411 412 private void showToast(String msg){ 413 Toast tst = Toast.makeText(this, msg, Toast.LENGTH_SHORT); 414 tst.setGravity(Gravity.CENTER | Gravity.TOP, 0, 240); 415 tst.show(); 416 } 417 418 419 // 設置表情的多頁滾動顯示控件 420 public int setScrollGridView(DrawerHScrollView scrollView, GridView gridView, 421 int lines) { 422 423 DisplayMetrics dm = new DisplayMetrics(); 424 getWindowManager().getDefaultDisplay().getMetrics(dm); 425 Display display = getWindowManager().getDefaultDisplay(); 426 System.out.println("Width:" + display.getWidth()); 427 System.out.println("Height:" + display.getHeight()); 428 429 430 int scrollWid = display.getWidth(); 431 int scrollHei; 432 System.out.println("scrollWid:" + scrollWid); 433 if (scrollWid <= 0 ){ 434 Log.d(TAG, "scrollWid or scrollHei is less than 0"); 435 return 0; 436 } 437 438 439 float density = dm.density; // 屏幕密度(像素比例:0.75/1.0/1.5/2.0) 440 441 int readlViewWidht = 56; 442 // 圖片都放在了Hdpi中,因此計算出圖片的像素獨立寬度 443 int viewWidth = (int) (readlViewWidht * density / 1.5); 444 int viewHeight = viewWidth; 445 System.out.println("viewWidth:" + viewWidth + " viewHeight:" + viewHeight); 446 447 int numColsPage = scrollWid / viewWidth; 448 int spaceing = (scrollWid - viewWidth * numColsPage)/(numColsPage); 449 System.out.println("Space:" + spaceing); 450 451 452 SimpleAdapter adapter = getEmoAdapter(); 453 int pages = adapter.getCount() / (numColsPage * lines); 454 455 if (pages * numColsPage * lines < adapter.getCount()){ 456 pages++; 457 } 458 459 System.out.println("pages:" + pages); 460 461 scrollHei = lines * viewHeight + spaceing * (lines + 1); 462 463 LayoutParams params = new LayoutParams(pages * scrollWid, LayoutParams.WRAP_CONTENT); 464 gridView.setLayoutParams(params); 465 gridView.setColumnWidth(viewWidth); 466 gridView.setHorizontalSpacing(spaceing); 467 gridView.setVerticalSpacing(spaceing); 468 gridView.setStretchMode(GridView.NO_STRETCH); 469 gridView.setNumColumns(numColsPage * pages); 470 471 //adapter = new DrawerListAdapter(this, colWid, colHei); 472 //listener = new DrawerItemClickListener(); 473 gridView.setAdapter(adapter); 474 //mGridView.setOnItemClickListener(listener); 475 476 scrollView.setParameters(pages, 0, scrollWid, spaceing); 477 //updateDrawerPageLayout(pageNum, 0); 478 // 表情區域還要加上分佈顯示區 479 int pageNumHei = (int) (18 * density); 480 return scrollHei + pageNumHei; 481 } 482 483 484 private SimpleAdapter getEmoAdapter(){ 485 HashMap<String, Object> map = new HashMap<String, Object>(); 486 map.put("img", R.drawable.emo001); 487 map.put("text", "<emo001>"); 488 mEmoList.add(map); 489 map = new HashMap<String, Object>(); 490 map.put("img", R.drawable.emo002); 491 map.put("text", "<emo002>"); 492 mEmoList.add(map); 493 map = new HashMap<String, Object>(); 494 map.put("img", R.drawable.emo003); 495 map.put("text", "<emo003>"); 496 mEmoList.add(map); 497 map = new HashMap<String, Object>(); 498 map.put("img", R.drawable.emo004); 499 map.put("text", "<emo004>"); 500 mEmoList.add(map); 501 map = new HashMap<String, Object>(); 502 map.put("img", R.drawable.emo005); 503 map.put("text", "<emo005>"); 504 mEmoList.add(map); 505 map = new HashMap<String, Object>(); 506 map.put("img", R.drawable.emo006); 507 map.put("text", "<emo006>"); 508 mEmoList.add(map); 509 map = new HashMap<String, Object>(); 510 map.put("img", R.drawable.emo007); 511 map.put("text", "<emo007>"); 512 mEmoList.add(map); 513 map = new HashMap<String, Object>(); 514 map.put("img", R.drawable.emo008); 515 map.put("text", "<emo008>"); 516 mEmoList.add(map); 517 map = new HashMap<String, Object>(); 518 map.put("img", R.drawable.emo009); 519 map.put("text", "<emo009>"); 520 mEmoList.add(map); 521 map = new HashMap<String, Object>(); 522 map.put("img", R.drawable.emo010); 523 map.put("text", "<emo010>"); 524 mEmoList.add(map); 525 map = new HashMap<String, Object>(); 526 map.put("img", R.drawable.emo011); 527 map.put("text", "<emo011>"); 528 mEmoList.add(map); 529 map = new HashMap<String, Object>(); 530 map.put("img", R.drawable.emo012); 531 map.put("text", "<emo012>"); 532 mEmoList.add(map); 533 map = new HashMap<String, Object>(); 534 map.put("img", R.drawable.emo013); 535 map.put("text", "<emo013>"); 536 mEmoList.add(map); 537 map = new HashMap<String, Object>(); 538 map.put("img", R.drawable.emo014); 539 map.put("text", "<emo014>"); 540 mEmoList.add(map); 541 map = new HashMap<String, Object>(); 542 map.put("img", R.drawable.emo015); 543 map.put("text", "<emo015>"); 544 mEmoList.add(map); 545 map = new HashMap<String, Object>(); 546 map.put("img", R.drawable.emo016); 547 map.put("text", "<emo016>"); 548 mEmoList.add(map); 549 map = new HashMap<String, Object>(); 550 map.put("img", R.drawable.emo017); 551 map.put("text", "<emo017>"); 552 mEmoList.add(map); 553 map = new HashMap<String, Object>(); 554 map.put("img", R.drawable.emo018); 555 map.put("text", "<emo018>"); 556 mEmoList.add(map); 557 map = new HashMap<String, Object>(); 558 map.put("img", R.drawable.emo019); 559 map.put("text", "<emo019>"); 560 mEmoList.add(map); 561 map = new HashMap<String, Object>(); 562 map.put("img", R.drawable.emo020); 563 map.put("text", "<emo020>"); 564 mEmoList.add(map); 565 map = new HashMap<String, Object>(); 566 map.put("img", R.drawable.emo021); 567 map.put("text", "<emo021>"); 568 mEmoList.add(map); 569 map = new HashMap<String, Object>(); 570 map.put("img", R.drawable.emo022); 571 map.put("text", "<emo022>"); 572 mEmoList.add(map); 573 map = new HashMap<String, Object>(); 574 map.put("img", R.drawable.emo023); 575 map.put("text", "<emo023>"); 576 mEmoList.add(map); 577 map = new HashMap<String, Object>(); 578 map.put("img", R.drawable.emo024); 579 map.put("text", "<emo024>"); 580 mEmoList.add(map); 581 map = new HashMap<String, Object>(); 582 map.put("img", R.drawable.emo025); 583 map.put("text", "<emo025>"); 584 mEmoList.add(map); 585 map = new HashMap<String, Object>(); 586 map.put("img", R.drawable.emo026); 587 map.put("text", "<emo026>"); 588 mEmoList.add(map); 589 map = new HashMap<String, Object>(); 590 map.put("img", R.drawable.emo027); 591 map.put("text", "<emo027>"); 592 mEmoList.add(map); 593 map = new HashMap<String, Object>(); 594 map.put("img", R.drawable.emo028); 595 map.put("text", "<emo028>"); 596 mEmoList.add(map); 597 map = new HashMap<String, Object>(); 598 map.put("img", R.drawable.emo029); 599 map.put("text", "<emo029>"); 600 mEmoList.add(map); 601 map = new HashMap<String, Object>(); 602 map.put("img", R.drawable.emo030); 603 map.put("text", "<emo030>"); 604 mEmoList.add(map); 605 map = new HashMap<String, Object>(); 606 map.put("img", R.drawable.emo031); 607 map.put("text", "<emo031>"); 608 mEmoList.add(map); 609 map = new HashMap<String, Object>(); 610 map.put("img", R.drawable.emo032); 611 map.put("text", "<emo032>"); 612 mEmoList.add(map); 613 map = new HashMap<String, Object>(); 614 map.put("img", R.drawable.emo033); 615 map.put("text", "<emo033>"); 616 mEmoList.add(map); 617 map = new HashMap<String, Object>(); 618 map.put("img", R.drawable.emo034); 619 map.put("text", "<emo034>"); 620 mEmoList.add(map); 621 map = new HashMap<String, Object>(); 622 map.put("img", R.drawable.emo035); 623 map.put("text", "<emo035>"); 624 mEmoList.add(map); 625 map = new HashMap<String, Object>(); 626 map.put("img", R.drawable.emo036); 627 map.put("text", "<emo036>"); 628 mEmoList.add(map); 629 map = new HashMap<String, Object>(); 630 map.put("img", R.drawable.emo037); 631 map.put("text", "<emo037>"); 632 mEmoList.add(map); 633 map = new HashMap<String, Object>(); 634 map.put("img", R.drawable.emo038); 635 map.put("text", "<emo038>"); 636 mEmoList.add(map); 637 map = new HashMap<String, Object>(); 638 map.put("img", R.drawable.emo039); 639 map.put("text", "<emo039>"); 640 mEmoList.add(map); 641 map = new HashMap<String, Object>(); 642 map.put("img", R.drawable.emo040); 643 map.put("text", "<emo040>"); 644 mEmoList.add(map); 645 map = new HashMap<String, Object>(); 646 map.put("img", R.drawable.emo041); 647 map.put("text", "<emo041>"); 648 mEmoList.add(map); 649 map = new HashMap<String, Object>(); 650 map.put("img", R.drawable.emo042); 651 map.put("text", "<emo042>"); 652 mEmoList.add(map); 653 map = new HashMap<String, Object>(); 654 map.put("img", R.drawable.emo043); 655 map.put("text", "<emo043>"); 656 mEmoList.add(map); 657 map = new HashMap<String, Object>(); 658 map.put("img", R.drawable.emo044); 659 map.put("text", "<emo044>"); 660 mEmoList.add(map); 661 map = new HashMap<String, Object>(); 662 map.put("img", R.drawable.emo045); 663 map.put("text", "<emo045>"); 664 mEmoList.add(map); 665 map = new HashMap<String, Object>(); 666 map.put("img", R.drawable.emo046); 667 map.put("text", "<emo046>"); 668 mEmoList.add(map); 669 map = new HashMap<String, Object>(); 670 map.put("img", R.drawable.emo047); 671 map.put("text", "<emo047>"); 672 mEmoList.add(map); 673 map = new HashMap<String, Object>(); 674 map.put("img", R.drawable.emo048); 675 map.put("text", "<emo048>"); 676 mEmoList.add(map); 677 map = new HashMap<String, Object>(); 678 map.put("img", R.drawable.emo049); 679 map.put("text", "<emo049>"); 680 mEmoList.add(map); 681 map = new HashMap<String, Object>(); 682 map.put("img", R.drawable.emo050); 683 map.put("text", "<emo050>"); 684 mEmoList.add(map); 685 map = new HashMap<String, Object>(); 686 map.put("img", R.drawable.emo051); 687 map.put("text", "<emo051>"); 688 mEmoList.add(map); 689 map = new HashMap<String, Object>(); 690 map.put("img", R.drawable.emo052); 691 map.put("text", "<emo052>"); 692 mEmoList.add(map); 693 map = new HashMap<String, Object>(); 694 map.put("img", R.drawable.emo053); 695 map.put("text", "<emo053>"); 696 mEmoList.add(map); 697 map = new HashMap<String, Object>(); 698 map.put("img", R.drawable.emo054); 699 map.put("text", "<emo054>"); 700 mEmoList.add(map); 701 map = new HashMap<String, Object>(); 702 map.put("img", R.drawable.emo055); 703 map.put("text", "<emo055>"); 704 mEmoList.add(map); 705 map = new HashMap<String, Object>(); 706 map.put("img", R.drawable.emo056); 707 map.put("text", "<emo056>"); 708 mEmoList.add(map); 709 map = new HashMap<String, Object>(); 710 map.put("img", R.drawable.emo057); 711 map.put("text", "<emo057>"); 712 mEmoList.add(map); 713 map = new HashMap<String, Object>(); 714 map.put("img", R.drawable.emo058); 715 map.put("text", "<emo058>"); 716 mEmoList.add(map); 717 map = new HashMap<String, Object>(); 718 map.put("img", R.drawable.emo059); 719 map.put("text", "<emo059>"); 720 mEmoList.add(map); 721 map = new HashMap<String, Object>(); 722 map.put("img", R.drawable.emo060); 723 map.put("text", "<emo060>"); 724 mEmoList.add(map); 725 726 /** 727 * 上述添加表情效率高,可是代碼太冗餘,下面的方式代碼簡單,可是效率較低 728 */ 729 /* 730 HashMap<String, Integer> map; 731 for(int i = 0; i < 100; i++){ 732 map = new HashMap<String, Integer>(); 733 Field field=R.drawable.class.getDeclaredField("image"+i); 734 int resourceId=Integer.parseInt(field.get(null).toString()); 735 map.put("img", resourceId); 736 mEmoList.add(map); 737 } 738 */ 739 return new SimpleAdapter(this, mEmoList, R.layout.grid_view_item, 740 new String[]{"img"}, new int[]{R.id.imageView}); 741 } 742 743 744 long waitTime = 2000; 745 long touchTime = 0; 746 @Override 747 public boolean onKeyDown(int keyCode, KeyEvent event) { 748 if(event.getAction() == KeyEvent.ACTION_DOWN && KeyEvent.KEYCODE_BACK == keyCode) { 749 long currentTime = System.currentTimeMillis(); 750 if((currentTime-touchTime)>=waitTime) { 751 Toast.makeText(this, "再按一次退出", Toast.LENGTH_SHORT).show(); 752 touchTime = currentTime; 753 }else { 754 finish(); 755 } 756 return true; 757 } 758 return super.onKeyDown(keyCode, event); 759 } 760 761 }
TaskService.java:
1 package cn.com.farsgiht.bluetoothdemo.task; 2 3 import java.io.BufferedInputStream; 4 import java.io.BufferedOutputStream; 5 import java.io.IOException; 6 import java.io.InputStream; 7 import java.io.OutputStream; 8 import java.io.UnsupportedEncodingException; 9 import java.util.ArrayList; 10 import java.util.Calendar; 11 import java.util.HashMap; 12 import java.util.UUID; 13 import android.app.Service; 14 import android.bluetooth.BluetoothAdapter; 15 import android.bluetooth.BluetoothDevice; 16 import android.bluetooth.BluetoothServerSocket; 17 import android.bluetooth.BluetoothSocket; 18 import android.content.ContentValues; 19 import android.content.Context; 20 import android.content.Intent; 21 import android.database.sqlite.SQLiteDatabase; 22 import android.os.Bundle; 23 import android.os.Handler; 24 import android.os.IBinder; 25 import android.util.Log; 26 import android.widget.ArrayAdapter; 27 import android.widget.Toast; 28 import cn.com.farsgiht.bluetoothdemo.ChatActivity; 29 import cn.com.farsgiht.bluetoothdemo.UI.ChatListViewAdapter; 30 import cn.com.farsgiht.bluetoothdemo.protocol.DataProtocol; 31 import cn.com.farsgiht.bluetoothdemo.protocol.Message; 32 import cn.com.farsgiht.bluetoothdemo.sound.SoundEffect; 33 34 /** 35 * 任務處理服務 36 * @author Administrator 37 */ 38 public class TaskService extends Service { 39 public static final int BT_STAT_WAIT = 0; 40 public static final int BT_STAT_CONN = 1; 41 public static final int BT_STAT_ONLINE = 2; 42 public static final int BT_STAT_UNKNOWN = 3; 43 44 45 public static final String DEVICE_NAME = "device_name"; 46 47 private final String TAG = "TaskService"; 48 private TaskThread mThread; 49 50 private BluetoothAdapter mBluetoothAdapter; 51 52 private AcceptThread mAcceptThread; 53 private ConnectThread mConnectThread; 54 private ConnectedThread mCommThread; 55 56 private boolean isServerMode = true; 57 58 private static Handler mActivityHandler; 59 60 // 任務隊列 61 private static ArrayList<Task> mTaskList = new ArrayList<Task>(); 62 63 private Handler mServiceHandler = new Handler() { 64 @Override 65 public void handleMessage(android.os.Message msg) { 66 switch (msg.what) { 67 case Task.TASK_GET_REMOTE_STATE: 68 android.os.Message activityMsg = mActivityHandler.obtainMessage(); 69 activityMsg.what = msg.what; 70 if (mAcceptThread != null && mAcceptThread.isAlive()) { 71 activityMsg.obj = "等待鏈接..."; 72 activityMsg.arg1 = BT_STAT_WAIT; 73 } else if (mCommThread != null && mCommThread.isAlive()) { 74 activityMsg.obj = mCommThread.getRemoteName() + "[在線]"; 75 activityMsg.arg1 = BT_STAT_ONLINE; 76 } else if (mConnectThread != null && mConnectThread.isAlive()) { 77 SoundEffect.getInstance(TaskService.this).play(3); 78 activityMsg.obj = "正在鏈接:" 79 + mConnectThread.getDevice().getName(); 80 activityMsg.arg1 = BT_STAT_CONN; 81 } else { 82 activityMsg.obj = "未知狀態"; 83 activityMsg.arg1 = BT_STAT_UNKNOWN; 84 SoundEffect.getInstance(TaskService.this).play(2); 85 // 從新等待鏈接 86 mAcceptThread = new AcceptThread(); 87 mAcceptThread.start(); 88 isServerMode = true; 89 } 90 91 mActivityHandler.sendMessage(activityMsg); 92 break; 93 default: 94 break; 95 } 96 super.handleMessage(msg); 97 } 98 }; 99 100 101 102 @Override 103 public void onCreate() { 104 super.onCreate(); 105 mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter(); 106 if (mBluetoothAdapter == null) { 107 Log.e(TAG, "Your device is not support Bluetooth!"); 108 return; 109 } 110 mThread = new TaskThread(); 111 mThread.start(); 112 } 113 114 115 public static void start(Context c, Handler handler){ 116 mActivityHandler = handler; 117 Intent intent = new Intent(c, TaskService.class); 118 c.startService(intent); 119 } 120 121 public static void stop(Context c){ 122 Intent intent = new Intent(c, TaskService.class); 123 c.stopService(intent); 124 } 125 126 127 128 public static void newTask(Task target) { 129 synchronized (mTaskList) { 130 mTaskList.add(target); 131 } 132 } 133 134 private class TaskThread extends Thread { 135 private boolean isRun = true; 136 private int mCount = 0; 137 138 public void cancel() { 139 isRun = false; 140 } 141 142 @Override 143 public void run() { 144 Task task; 145 while (isRun) { 146 147 // 有任務 148 if (mTaskList.size() > 0) { 149 synchronized (mTaskList) { 150 // 得到任務 151 task = mTaskList.get(0); 152 doTask(task); 153 } 154 } else { 155 try { 156 Thread.sleep(200); 157 mCount++; 158 } catch (InterruptedException e) { 159 } 160 // 每過10秒鐘進行一次狀態檢查 161 if (mCount >= 50) { 162 mCount = 0; 163 // 檢查遠程設備狀態 164 android.os.Message handlerMsg = mServiceHandler 165 .obtainMessage(); 166 handlerMsg.what = Task.TASK_GET_REMOTE_STATE; 167 mServiceHandler.sendMessage(handlerMsg); 168 } 169 } 170 } 171 } 172 173 } 174 //對應三個線程,其中mCommThread是在mConnectThread的run()方法中new出來的 175 private void doTask(Task task) { 176 switch (task.getTaskID()) { 177 case Task.TASK_START_ACCEPT: 178 mAcceptThread = new AcceptThread(); 179 mAcceptThread.start(); 180 isServerMode = true; 181 break; 182 case Task.TASK_START_CONN_THREAD: 183 if (task.mParams == null || task.mParams.length == 0) { 184 break; 185 } 186 BluetoothDevice remote = (BluetoothDevice) task.mParams[0]; 187 mConnectThread = new ConnectThread(remote); 188 mConnectThread.start(); 189 isServerMode = false; 190 break; 191 case Task.TASK_SEND_MSG: 192 boolean sucess = false; 193 if (mCommThread == null || !mCommThread.isAlive() 194 || task.mParams == null || task.mParams.length == 0) { 195 Log.e(TAG, "mCommThread or task.mParams null"); 196 }else{ 197 byte[] msg = null; 198 try { 199 200 msg = DataProtocol.packMsg((String) task.mParams[0]); 201 sucess = mCommThread.write(msg); 202 203 } catch (UnsupportedEncodingException e) { 204 sucess = false; 205 } 206 } 207 if (!sucess) { 208 android.os.Message returnMsg = mActivityHandler.obtainMessage(); 209 returnMsg.what = Task.TASK_SEND_MSG_FAIL; 210 returnMsg.obj = "消息發送失敗"; 211 mActivityHandler.sendMessage(returnMsg); 212 } 213 break; 214 } 215 216 // 移除任務 217 mTaskList.remove(task);//每次保證任務列表裏面只有一個任務,task = mTaskList.get(0); 218 } 219 220 @Override 221 public void onDestroy() { 222 super.onDestroy(); 223 mThread.cancel(); 224 } 225 226 private final String UUID_STR = "00001101-0000-1000-8000-00805F9B34FB"; 227 228 /** 229 * 等待客戶端鏈接線程 230 * 231 * @author Administrator 232 */ 233 private class AcceptThread extends Thread { 234 private final BluetoothServerSocket mmServerSocket; 235 private boolean isCancel = false; 236 237 public AcceptThread() { 238 Log.d(TAG, "AcceptThread"); 239 BluetoothServerSocket tmp = null; 240 try { 241 tmp = mBluetoothAdapter.listenUsingRfcommWithServiceRecord( 242 "MT_Chat_Room", UUID.fromString(UUID_STR)); 243 } catch (IOException e) { 244 } 245 mmServerSocket = tmp; 246 } 247 248 public void run() { 249 BluetoothSocket socket = null; 250 while (true) { 251 try { 252 // 阻塞等待 253 socket = mmServerSocket.accept(); 254 } catch (IOException e) { 255 if (!isCancel) { 256 try { 257 mmServerSocket.close(); 258 } catch (IOException e1) { 259 } 260 mAcceptThread = new AcceptThread(); 261 mAcceptThread.start(); 262 isServerMode = true; 263 } 264 break; 265 } 266 if (socket != null) { 267 manageConnectedSocket(socket); 268 try { 269 mmServerSocket.close(); 270 } catch (IOException e) { 271 } 272 mAcceptThread = null; 273 break; 274 } 275 } 276 } 277 278 public void cancel() { 279 try { 280 Log.d(TAG, "AcceptThread canceled"); 281 isCancel = true; 282 isServerMode = false; 283 mmServerSocket.close(); 284 mAcceptThread = null; 285 if (mCommThread != null && mCommThread.isAlive()) { 286 mCommThread.cancel(); 287 } 288 } catch (IOException e) { 289 } 290 } 291 } 292 293 /** 294 * 做爲客戶端鏈接指定的藍牙設備線程 295 * 296 * @author Administrator 297 */ 298 private class ConnectThread extends Thread { 299 private final BluetoothSocket mmSocket; 300 private final BluetoothDevice mmDevice; 301 302 public ConnectThread(BluetoothDevice device) { 303 304 Log.d(TAG, "ConnectThread"); 305 306 if (mAcceptThread != null && mAcceptThread.isAlive()) { 307 mAcceptThread.cancel(); 308 } 309 310 if (mCommThread != null && mCommThread.isAlive()) { 311 mCommThread.cancel(); 312 } 313 314 // Use a temporary object that is later assigned to mmSocket, 315 // because mmSocket is final 316 BluetoothSocket tmp = null; 317 mmDevice = device; 318 try { 319 tmp = device.createRfcommSocketToServiceRecord(UUID 320 .fromString(UUID_STR)); 321 } catch (IOException e) { 322 Log.d(TAG, "createRfcommSocketToServiceRecord error!"); 323 } 324 325 mmSocket = tmp; 326 } 327 328 public BluetoothDevice getDevice() { 329 return mmDevice; 330 } 331 332 public void run() { 333 // Cancel discovery because it will slow down the connection 334 mBluetoothAdapter.cancelDiscovery(); 335 try { 336 // Connect the device through the socket. This will block 337 // until it succeeds or throws an exception 338 mmSocket.connect(); 339 } catch (IOException connectException) { 340 // Unable to connect; close the socket and get out 341 Log.e(TAG, "Connect server failed"); 342 try { 343 mmSocket.close(); 344 } catch (IOException closeException) { 345 } 346 mAcceptThread = new AcceptThread(); 347 mAcceptThread.start(); 348 isServerMode = true; 349 return; 350 } // Do work to manage the connection (in a separate thread) 351 manageConnectedSocket(mmSocket); 352 } 353 354 public void cancel() { 355 try { 356 mmSocket.close(); 357 } catch (IOException e) { 358 } 359 mConnectThread = null; 360 } 361 } 362 363 private void manageConnectedSocket(BluetoothSocket socket) { 364 // 啓動子線程來維持鏈接 365 mCommThread = new ConnectedThread(socket); 366 mCommThread.start(); 367 } 368 369 private class ConnectedThread extends Thread { 370 private final BluetoothSocket mmSocket; 371 private final InputStream mmInStream; 372 private final OutputStream mmOutStream; 373 private BufferedOutputStream mmBos; 374 private byte[] buffer; 375 376 public ConnectedThread(BluetoothSocket socket) { 377 Log.d(TAG, "ConnectedThread"); 378 mmSocket = socket; 379 InputStream tmpIn = null; 380 OutputStream tmpOut = null; 381 try { 382 tmpIn = socket.getInputStream(); 383 tmpOut = socket.getOutputStream(); 384 } catch (IOException e) { 385 } 386 mmInStream = tmpIn; 387 mmOutStream = tmpOut; 388 mmBos = new BufferedOutputStream(mmOutStream); 389 } 390 391 public OutputStream getOutputStream() { 392 return mmOutStream; 393 } 394 395 public boolean write(byte[] msg) { 396 if (msg == null) 397 return false; 398 try { 399 mmBos.write(msg); 400 mmBos.flush(); 401 402 mActivityHandler.obtainMessage(Task.TASK_SEND_MSG, -1, -1, new String(msg)).sendToTarget(); 403 System.out.println("Write:" + msg); 404 } catch (IOException e) { 405 return false; 406 } 407 return true; 408 } 409 410 public String getRemoteName() { 411 return mmSocket.getRemoteDevice().getName(); 412 } 413 414 415 416 417 public void cancel() { 418 try { 419 mmSocket.close(); 420 } catch (IOException e) { 421 } 422 mCommThread = null; 423 } 424 425 public void run() { 426 try { 427 write(DataProtocol.packMsg(mBluetoothAdapter.getName() 428 + "已經上線\n"));//獲取本地藍牙適配器的藍牙名稱,這一條消息默認發送出去啦, 429 //但消息記錄裏面不該該有這條消息,消息記錄裏面記錄按過發送鍵的消息 430 } catch (UnsupportedEncodingException e2) { 431 } 432 int size; 433 Message msg; 434 android.os.Message handlerMsg; 435 buffer = new byte[1024]; 436 437 BufferedInputStream bis = new BufferedInputStream(mmInStream); 438 // BufferedReader br = new BufferedReader(new 439 // InputStreamReader(mmInStream)); 440 HashMap<String, Object> data; 441 while (true) { 442 try { 443 size = bis.read(buffer); 444 msg = DataProtocol.unpackData(buffer); 445 if (msg == null) 446 continue; 447 448 if (mActivityHandler == null) { 449 return; 450 } 451 452 msg.remoteDevName = mmSocket.getRemoteDevice().getName();//獲得對方設備的名字 453 if (msg.type == DataProtocol.TYPE_FILE) { 454 // 文件接收處理忽略 455 456 } else if (msg.type == DataProtocol.TYPE_MSG) { 457 data = new HashMap<String, Object>(); 458 System.out.println("Read data."); 459 data.put(ChatListViewAdapter.KEY_ROLE, 460 ChatListViewAdapter.ROLE_TARGET); 461 data.put(ChatListViewAdapter.KEY_NAME, 462 msg.remoteDevName); 463 data.put(ChatListViewAdapter.KEY_TEXT, msg.msg); 464 465 // 經過Activity更新到UI上 466 handlerMsg = mActivityHandler.obtainMessage(); 467 handlerMsg.what = Task.TASK_RECV_MSG; 468 handlerMsg.obj = data; 469 mActivityHandler.sendMessage(handlerMsg); 470 } 471 } catch (IOException e) { 472 try { 473 mmSocket.close(); 474 } catch (IOException e1) { 475 } 476 mCommThread = null; 477 if (isServerMode) { 478 // 檢查遠程設備狀態 479 handlerMsg = mServiceHandler.obtainMessage(); 480 handlerMsg.what = Task.TASK_GET_REMOTE_STATE; 481 mServiceHandler.sendMessage(handlerMsg); 482 SoundEffect.getInstance(TaskService.this).play(2); 483 mAcceptThread = new AcceptThread(); 484 mAcceptThread.start(); 485 } 486 break; 487 } 488 } 489 } 490 } 491 492 // ================================================================ 493 494 @Override 495 public IBinder onBind(Intent intent) { 496 // TODO Auto-generated method stub 497 return null; 498 } 499 500 }
實現效果: