Android状态栏禁用时,自动接收蓝牙文件以及显示多文件传输进
首先,android原生的蓝牙接收流程是,在有文件从其他设备传过来时,会弹出蓝牙文件接收的缺人框且默认是以notification的形式显示在状态栏,当用户点击之后才会弹出一个dialog。那么当状态栏被禁用时,如何实现文件接受全程不需用户点击而自动接收呢?
第一个问题:
如何不让用户点击状态栏直接弹确认的dialog。
在BluetoothOppNotification.java的updateIncomingFileConfirmNotification()方法中会对接受到来的文件进行一定的处理同时会构造一个Notification,来显示接受和拒绝的信息,那么解决的思路就在这里。
private void updateIncomingFileConfirmNotification() {
//省略若干…
Intent intent = new Intent(Constants.ACTION_INCOMING_FILE_CONFIRM);//这句比较关键,传递一个action到BluetoothOppReceiver
intent.setClassName(Constants.THIS_PACKAGE_NAME, BluetoothOppReceiver.class.getName());
intent.setDataAndNormalize(contentUri);
intent构造了之后在这里并没有发送广播出去,而是在下面构造notification之后,点击时才将广播发送出去,所以问题的解决点就在这里。如果不需要用户点击状态栏直接显示文件接收和拒绝的确认界面可以直接在这里mContext.sendBroadcast(intent);将广播发送出去
//省略若干…
{
//构造notification
Notification n = new Notification();
n.icon = R.drawable.bt_incomming_file_notification;
n.flags |= Notification.FLAG_ONLY_ALERT_ONCE;
n.flags |= Notification.FLAG_ONGOING_EVENT;
n.defaults = Notification.DEFAULT_SOUND;
n.tickerText = title;
n.when = timeStamp;
n.color = mContext.getResources().getColor(
com.android.internal.R.color.system_notification_accent_color);
n.setLatestEventInfo(mContext, title, caption, PendingIntent.getBroadcast(mContext, 0,
intent, 0));
intent = new Intent(Constants.ACTION_HIDE);
intent.setClassName(Constants.THIS_PACKAGE_NAME, BluetoothOppReceiver.class.getName());
intent.setDataAndNormalize(contentUri);
n.deleteIntent = PendingIntent.getBroadcast(mContext, 0, intent, 0);//用户点击之后将广播发送出去
mNotificationMgr.notify(id, n);
}
}
}
第二个问题
用户不点击确认文件接收的按钮直接进行文件接收。这个相对来说比较简单
继续上面说的,当广播发送之后在BluetoothOppReceiver.java直接启动BluetoothOppIncomingFileConfirmActivity。在这个activity中作进一步的处理。
可以看到的是在这个activity中主要是构造上面所说的接收文件确认和拒绝的dialog。
要想达到需要的效果,只需要将确认接收按钮事件的代码外移即可。可以直接移动的oncreate中执行,完了之后将dialog dismiss掉。主要就是如下几句代码
if (!mTimeout) {
// Update database
mUpdateValues = new ContentValues();
mUpdateValues.put(BluetoothShare.USER_CONFIRMATION,
BluetoothShare.USER_CONFIRMATION_CONFIRMED);
this.getContentResolver().update(mUri, mUpdateValues, null, null);
Toast.makeText(this, getString(R.string.bt_toast_1), Toast.LENGTH_SHORT).show();
}
第三个问题
如何显示进度条。
当上面的文件开始接受之时就需要弹出进度条进行显示进度。所以在上面的代码中还需要加入启动进度条界面的代码。具体是
Intent in = new Intent(this, BluetoothOppTransferActivity.class);
in.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
in.setDataAndNormalize(mUri);
this.startActivity(in);
至此,单文件就开始传输并且已经显示进度条。
第四个问题
文件传输完场之后,进度条界面如何三秒之后自动消失。
进入BluetoothOppTransferActivity这个activity,首先先定义一个消失的方法。如下
private void dismissNowDialog(){
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
dismiss();
}
}, 2000);
}
之后再setUpDialog()中mWhichDialog == DIALOG_RECEIVE_COMPLETE_SUCCESS和mWhichDialog == DIALOG_RECEIVE_COMPLETE_FAIL时调用这个方法即可。
写到这里,单文件文件传输全程不需要用户进行任何点击就可以自动接收完成。
但是不知道您有没有想过一个问题,在接受完之后dismiss掉了界面,那么在多文件传输时后面的那些文件进度条是否还会显示呢?答案是不会的。
第五个问题
多文件时如何显示所有文件传输的进度条。
思路就是,再多文件传输时,单个文件传输完,状态栏会进行更新显示其他文件的进度信息,考虑到这里,继续进入BluetoothOppNotification.java这个类,在updateActiveNotification()方法中可以看到多文件在传输时,它是通过Notification.Builder来进行刷新显示的,我们的需求并不是这样,所以这些并不可取。继续往下看可以看到重点是Intent intent = new Intent(Constants.ACTION_LIST);这个可以理解为处理多文件的。原生的代码并没有很好地办法来区分多文件还是单文件,所以需要在这里想办法进行处理。笔者在做的时候看到这个很是兴奋,一想这不很简单吗,和单文件传输如出一辙我只需要将广播手动发送一遍即可。结果会让你崩溃的,这里简单说下,假如十个文件在传输时那么这个广播他会发几遍呢?最终的结果就是后面的界面不停的闪烁加重叠。所以这里要做的就是在文件传输时只将这个广播发送一次,但是并没有现成的方法或变量来标示是否多文件传输。
第六个问题
如何在一个循环中只根据自己的需求将广播发送一次出去呢?
笔者这里采用的思路是定义一个任意类型的变量,给定一个初始值,找一个在文件接收时肯定会调用的一个方法,在这个方法中改变变脸的值,完了之后在发送广播时加上对这个变量的判断,完了之后将变量的值回复默认值。下次的话,他肯定就不会再发广播出去,保证广播只发送了一次,即可达到需求。
Private Int temp =0;
private void updateActiveNotification() {
……
if(temp==1){//通过这个判断保证广播只会发送一次
mContext.sendBroadcast(intent);
temp=temp+1;
}
……
}
rivate void updateIncomingFileConfirmNotification() {
//这个方法中加入如下代码
if(temp==1){
temp=temp+1;
return;
}
……
temp=1;
}
至此,整个需求处理完毕。当让如果在接受完毕之后还想显示多少文件传输完成,多少文件传输失败的话可以通过在代码中BluetoothOppTransferActivity.java中动态的改变dialog的显示信息来进行处理,需要注意的是在这个类里面是不知道有多少文件传输完成和失败的,需要从BluetoothOppNotification.java 的updateCompletedNotification()方法中,将
int outboundSuccNumber = 0;
int outboundFailNumber = 0;
int outboundNum;
int inboundNum;
int inboundSuccNumber = 0;
int inboundFailNumber = 0;
参数选择性的进行传输或者保存,从而在上面说的界面显示出来。
最后再来一个小知识点结束。不知有没有想过,蓝牙文件在传输时如何判断文件是正在传输还是已经传输完毕呢?原生的蓝牙代码之后提供蓝牙的配对,连接等状态,并不会提供文件传输的状态,那么就需要自己来实现。思路就是蓝牙文件的传输是通过流来进行的,那么我只需要知道它所对应的刘是否关闭即可知道文件是否传输完成。
在framework\base\obex\javax\obex下面有个ServerSession类,在这个里面会通过判断ObexTransport; InputStream OutputStream来判断是否关闭,可以自己在这里加接口提供给外部,用来判断蓝牙文件是否传输完成,比较简单。