关于WIFI就不多介绍啦,直接来个段子吧。
问:“WiFi对人体有伤害么?”
答:“不清楚,反正没有WiFi我就浑身不舒服。
比较重要的一点就是WifiManager wm=(WifiManager)Android_Wifi.this.getSystemService(Context.WIFI_SERVICE);
关闭打开搜索都可以通过调用wm的相关方法实现。可能要开发wifi万能钥匙那一类的程序才用到这个吧,普通应用程序主要就识别是wifi网络还是移动网络。不多讲
代码参考博客:http://blog.iyunv.com/sbvfhp/article/details/7007090
重点蓝牙:
一:什么是蓝牙
1:Bluetooth是目前使用最广泛的无线通讯协议,近距离无线通讯的标准。传说瑞典有个国王特别爱吃蓝莓导致自己的牙齿天天都是蓝色的,在他执政期间这位国王非常善于交际,能说会到,和邻国的搞得关系非常好,这个Bluetooth的发明者觉得蓝牙它的作用就是在近距离沟通周围的设备,跟这个国王很类似,于是起名叫蓝牙。
2:主要针对短距离设备通讯(10米)
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);
}
}
}
View Code 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);
}
}
}
View Code 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.iyunv.com/detail/mr_raptor/8033951#comment
此demo界面优美,实现基本功能,但不能保存聊天记录,黑屏自动断线,在此基础做出修改。
相关知识:
在EditText中插入表情图片 (CharacterStyle&SpannableString)
源码分析:
- Android中pendingIntent的深入理解
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 }
View Code 通知栏相关代码
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 }
View Code 表情页页面指示器
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 }
View Code 定制的表情页面,继承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
保存和恢复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 }
View Code 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 }
View Code 实现效果:
源码传送门
|