一、藍牙文件傳輸彈窗
Android原生藍牙傳輸文件時,會彈出藍牙文件接收的確認框且默認是以notification的形式顯示在狀態欄,當用戶點擊之后才會彈出一個dialog。那么當狀態欄被禁用時,如何實現文件接受全程不需用戶點擊而自動接收呢?
1.如何不讓用戶點擊狀態欄直接彈確認的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); } } }
2.用戶不點擊確認文件接收的按鈕如何直接進行文件接收?
繼續上面說的,當廣播發送之后在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(); }
3.如何顯示進度條?
當上面的文件開始接受之時就需要彈出進度條進行顯示進度,所以在上面的代碼中還需要加入啟動進度條界面的代碼:
Intent in = new Intent(this, BluetoothOppTransferActivity.class); in.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); in.setDataAndNormalize(mUri); this.startActivity(in);
至此,單文件就開始傳輸並且已經顯示進度條。
4.文件傳輸完成后,進度條界面如何三秒之后自動消失?
進入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掉了界面,那么在多文件傳輸時后面的那些文件進度條是否還會顯示呢?答案是不會的。
5.多文件時如何顯示所有文件傳輸的進度條?
思路就是,再多文件傳輸時,單個文件傳輸完,狀態欄會進行更新顯示其他文件的進度信息,考慮到這里,繼續進入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來判斷是否關閉,可以自己在這里加接口提供給外部,用來判斷藍牙文件是否傳輸完成,比較簡單。