I'm now trying to transfer files between Android devices via bluetooth. I already implemented my sender side. I'm not using InputStream/OutputStream. I'm using Intent.ACTION_SEND. Everything in there sender side works fine, but when it comes to the receiver side, I'm facing two problems.
There's the pop out notification saying "Do you want to receive this file?". Is there any way I can avoid this thing?
How can I know that there's a file coming in and the file transfer is finished or stopped at the receiver side?
It seems that these two problems can be solved using InputStream/OutputStream, but I don't really want to use them. Maybe a listener that monitors Bluetooth, or some functions in BluetoothAdapter/BluetoothDevice can do this?
Thanks for help. My code is like below: (in my MainActivity.java)
public void beginBT() {
if (isSender) {
File file = new File(Environment.getExternalStorageDirectory().getPath()+"/log.txt");
Intent intent = new Intent();
intent.setAction(Intent.ACTION_SEND);
intent.setType("text/plain");
intent.putExtra(Intent.EXTRA_STREAM, Uri.fromFile(file));
if (!findBluetoothForIntent(intent)){
Toast.makeText(this, "Bluetooth Not Found.", Toast.LENGTH_SHORT).show();
} else {
//intent will send the file via bluetooth
startActivity(intent);
}
} else { //receiver side
//make device be discoverable
Intent discoverableIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE);
discoverableIntent.putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, 300);
startActivity(discoverableIntent);
}
}
public boolean findBluetoothForIntent(Intent intent){
List appsList = getPackageManager().queryIntentActivities(intent, 0);
String packageName = null;
String className = null;
for (Object info: appsList){
if (info instanceof ResolveInfo) {
packageName = ((ResolveInfo) info).activityInfo.packageName;
if (packageName.equals("com.android.bluetooth")){
className = ((ResolveInfo) info).activityInfo.name;
break;
}
}
}
if (className != null) {
intent.setClassName(packageName, className);
return true;
} else {
return false;
}
}
Answering my own question is always so much fun!!
About the pop out notification, I can't do anything about it, unless I use InStream/OutStream.
For the receiver side, use BroadcastReceiver to monitor actions of the device. Here I monitor the disconnecting action of bluetooth. Because there will be a connecting action when the device begin to receive file, and when it finishes, there will be a disconnecting action.
Don't know if the following code would help anybody, :)
MainActivity.java
private static final String BT_DISCONNECTED = "android.bluetooth.device.action.ACL_DISCONNECTED";
private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (action == BT_DISCONNECTED) {
//now file transmitting has finished, can do something to the file
//if you know the file name, better to check if the file is actually there
// - make sure this disconnection not initiated by any other reason.
}
}
IntentFileter filter = new IntentFilter(BluetoothDevice.ACTION_ACL_DISCONNECTED);
this.registerReceiver(mReceiver, filter);
Warning: remember to unregister this receiver when you exit the activity, or simply when you don't need it
Related
Recently I have uploaded my android apk on the app store and its been told that the next upload to Google play store will get rejected and we need to check and resolve it. Below is the screenshot of the message:
They are referring to package name also. Below is the code:
#Override
public void onDestroy() {
cleanup();
super.onDestroy();
Intent intent = new Intent("com.test.dummyapp");
sendBroadcast(intent);
}
Please assist me how to resolve this.
Below is the code where the component is triggered:
IntentFilter restartFilter = new IntentFilter("com.test.dummyapp");
registerReceiver(restartBroadcastReciver, restartFilter);
private BroadcastReceiver restartBroadcastReciver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
doBindService();
}
};
When you do this, you are broadcasting an "implicit Intent". This is dangerous because any app can register to get this (potential leak of information) and any app can also broadcast this Intent (triggering your app).
Intent intent = new Intent("com.test.dummyapp");
sendBroadcast(intent);
To fix this you can use LocalBroadcastManager (it is deprecated, but still works). Using a local broadcast ensures that other apps cannot see your broadcast Intent and other apps cannot trigger your app this way.
See https://developer.android.com/reference/androidx/localbroadcastmanager/content/LocalBroadcastManager
As an alternative, you should be able to make the Intent explicit by setting the package name:
Intent intent = new Intent("com.test.dummyapp");
intent.setPackage("my.package.name");
sendBroadcast(intent);
It seems really weird to send a Broadcast in onDestroy. I can't possibly see a use for that, and I can see a lot of problems due to onDestroy being called unexpectedly (rotation, screen size change, etc).
But if you have to do it, use new Intent(getPackageName()). What they're looking for is a hardcoded package name like that. The problem is that if you run 'com.facebook.whateveritscalled' and a piece of malware is installed that named itself that, you would be sending the intent to it. Which if you have extras in the intent could be leaking information to it.
Thanks for the information.
I made some changes to the posted code. Let me know if this works fine.
#Override
public void onDestroy() {
cleanup();
super.onDestroy();
openApp((Context) context,"com.test.dummyapp");
}
public static boolean openApp(Context context, String packageName) {
PackageManager manager = context.getPackageManager();
try {
Intent i = manager.getLaunchIntentForPackage(packageName);
if (i == null) {
return false;
}
i.addCategory(Intent.CATEGORY_LAUNCHER);
context.sendBroadcast(i);
return true;
} catch (ActivityNotFoundException e) {
return false;
}
}
I'm a beginner in Android programming since I only started 3 months ago. I'm doing a project which connects the android app to arduino using bluetooth. I already have a code for the android app (bluetooth.adapter,sockets,.etc.). The code for connection is already working. One of the goal is for the android app to automatically input the password when pairing with the bluetooth device without asking user to input the PIN.
The old posts on this forum do not help much. (many suggested using insecure mode, but I do need secure mode, also in my case, the arduino is the server while cellphone app is the client, so the createInsecureRfcommSocketToServiceRecord() server method does not work for me)
I searched and found this in android developer site about bluetoothdevice class:
setPairingConfirmation(boolean confirm)
Confirm passkey for PAIRING_VARIANT_PASSKEY_CONFIRMATION pairing.
PAIRING_VARIANT_PIN = "The user will be prompted to enter a pin or an app will enter a pin for user".
PAIRING_VARIANT_PASSKEY_CONFIRMATION = "The user will be prompted to confirm the passkey displayed on the screen or an app will confirm the passkey for the user"
Seems using the code, the app will be the one to input the password and confirm
the password making it an "auto-connect" features but the android site does not give a sample code on how to use this. Does any of you have a sample code in using this or related process? I appreciate your help!
First to clarify, this solution is designed for newer version of API (15 or later?)
I found the answer written in another post (see Roldofo's answer in Here). Here is my reorganized answer with detailed code.
In a nutshell, you need to setup a broadcast receiver to trap the ACTION_PAIRING_REQUEST, and then programmatically pass the PIN and confirm.
Register a broadcast receiver:
IntentFilter filter = new IntentFilter(BluetoothDevice.ACTION_PAIRING_REQUEST);
getActivity().registerReceiver(mPairingRequestReceiver, filter);
The definition of the receiver:
private final BroadcastReceiver mPairingRequestReceiver = new BroadcastReceiver() {
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (action.equals(BluetoothDevice.ACTION_PAIRING_REQUEST)) {
try {
BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
int pin=intent.getIntExtra("android.bluetooth.device.extra.PAIRING_KEY", 1234);
//the pin in case you need to accept for an specific pin
Log.d(TAG, "Start Auto Pairing. PIN = " + intent.getIntExtra("android.bluetooth.device.extra.PAIRING_KEY",1234));
byte[] pinBytes;
pinBytes = (""+pin).getBytes("UTF-8");
device.setPin(pinBytes);
//setPairing confirmation if neeeded
device.setPairingConfirmation(true);
} catch (Exception e) {
Log.e(TAG, "Error occurs when trying to auto pair");
e.printStackTrace();
}
}
}
};
Then at your activity or fragment (wherever you want to initiate the pairing), you can call the following defined pairDevice() method to invoke pairing attempt (which will generate a ACTION_PAIRING_REQUEST)
private void pairDevice(BluetoothDevice device) {
try {
Log.d(TAG, "Start Pairing... with: " + device.getName());
device.createBond();
Log.d(TAG, "Pairing finished.");
} catch (Exception e) {
Log.e(TAG, e.getMessage());
}
}
I also faced the same problem and after all the research, I figured out the below solution.
(Tested and working!!!)
I am basically looking for a particular bluetooth device (I know MAC address) and pair with it once found. The first thing to do is to create pair request using boradcast receiver and handle the request as below.
IntentFilter intentFilter = new IntentFilter(BluetoothDevice.ACTION_PAIRING_REQUEST);
intentFilter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
registerReceiver(broadCastReceiver,intentFilter);
You need to write the broadcastReceiver and handle it as below.
String BLE_PIN = "1234"
private BroadcastReceiver broadCastReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if(BluetoothDevice.ACTION_PAIRING_REQUEST.equals(action))
{
BluetoothDevice bluetoothDevice = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
bluetoothDevice.setPin(BLE_PIN.getBytes());
Log.e(TAG,"Auto-entering pin: " + BLE_PIN);
bluetoothDevice.createBond();
Log.e(TAG,"pin entered and request sent...");
}
}
};
Voila! You should be able to pair to bluetooth device without ANY MANUAL INTERVENTION.
Hope this helps :-) Please make it right answer if it works for you.
Yes this possible to do by code
In you main activity add the following code
BluetoothReceiver myreceiver = new BluetoothReceiver();
var intentfilterparingrequest = new IntentFilter(BluetoothDevice.ActionPairingRequest);
RegisterReceiver(myreceiver, intentfilterparingrequest);
In your broadcast receiver write following code, if not create a new broadcast receiver
public class BluetoothReceiver : BroadcastReceiver
{
public override void OnReceive(Context context, Intent intent)
{
string BLE_PIN = "0000";
var action = intent.Action;
switch (action)
{
case BluetoothDevice.ActionPairingRequest:
BluetoothDevice bluetoothDevice =
(BluetoothDevice)intent.GetParcelableExtra(BluetoothDevice.ExtraDevice);
bluetoothDevice.SetPin(Encoding.ASCII.GetBytes(BLE_PIN));
bluetoothDevice.CreateBond();
break;
}
}
}
I'm currently trying to get broadcast receivers running in the background of my android application, which I've been told to use an event service for. At present my broadcast receivers work fine if you're in the activity within which they're registered
private BroadcastReceiver mBatInfoReceiver = new BroadcastReceiver(){
#Override
public void onReceive(Context arg0, Intent intent) {
if(intent.getAction().equals(Intent.ACTION_BATTERY_LOW)) {
Toast.makeText(arg0, "Battery's dying!!", Toast.LENGTH_LONG).show();
Log.e("LOW", "LOW");
intent = null;
}else if(intent.getAction().equals(Intent.ACTION_POWER_DISCONNECTED)) {
Toast.makeText(arg0, "Battery's discharging!!", Toast.LENGTH_LONG).show();
Log.e("discharge", "discharge");
intent = null;
}else if(intent.getAction().equals(Intent.ACTION_POWER_CONNECTED)) {
Toast.makeText(arg0, "Battery's charging!!", Toast.LENGTH_LONG).show();
Log.e("charge", "charge");
intent = null;
}else if(intent.getAction().equals(Intent.ACTION_BATTERY_OKAY)) {
Toast.makeText(arg0, "Battery's okay!!", Toast.LENGTH_LONG).show();
Log.e("OKAY", "OKAY");
intent = null;
}
}
};
in OnCreate:
registerReceiver(this.mBatInfoReceiver,
new IntentFilter(Intent.ACTION_BATTERY_LOW));
registerReceiver(this.mBatInfoReceiver,
new IntentFilter(Intent.ACTION_BATTERY_OKAY));
registerReceiver(this.mBatInfoReceiver,
new IntentFilter(Intent.ACTION_POWER_DISCONNECTED));
registerReceiver(this.mBatInfoReceiver,
new IntentFilter(Intent.ACTION_POWER_CONNECTED));
Despite the issues that arise with leaking intentfilters by making them in oncreate, my current issue that that when I change activity these no longer run, I've read from the following: http://www.vogella.com/tutorials/AndroidBroadcastReceiver/article.html#startingservices_alarmmanager
That by placing these into an IntentService and starting this service, they'll run consistently, however this involves registering the receivers in the manifest, which gives me issues in that my application is planning on allowing the user to listen out for specific events IE: these cannot be created dynamically.
Is there a way to dynamically create broadcast receivers within a class, which runs in the background of the application and is triggered when the broadcast occurs?
Can u help me find out why the registration of broadcast receiver returns null?
this is the code:
ScoIntent = new IntentFilter(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED);
sReceiver = new ScoReceiver(context, Tmp);
if (context.registerReceiver(sReceiver, ScoIntent) == null) {
Log("FBR.GetBlueConnect:Error", "Can not find receiver ACTION_CONNECTION_STATE_CHANGED");
HFS.DisplayText("Can not connect to Bluetooth Headset, Please Exit", true);
}
and this is the reciver:
class ScoReceiver extends BroadcastReceiver {
public ScoReceiver(Context mcontext, Tools t){
bContext = mcontext;
tools = t;
}
#Override
public void onReceive(Context context, Intent arg1) {
tools.Log("ScoReceiver:onReceive", "In");
//arg1 = new Intent(AudioManager.ACTION_SCO_AUDIO_STATE_UPDATED);
String action = arg1.getAction();
tools.Log("ScoReceiver:onReceive", ">>> Bluetooth SCO state changed !!! ");
if(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED.equals(action)) {
int status = arg1.getIntExtra(BluetoothHeadset.EXTRA_STATE, AudioManager.SCO_AUDIO_STATE_ERROR );
}
The javadocs say,
Returns the first sticky intent found that matches filter, or null if
there are none.
Does this receiver have a sticky intent? Here's a post that talks about the difference between a stick and non-sticky intent,
what is the difference between sendStickyBroadcast and sendBroadcast in Android
I want to listen for connection/disconnection with a number of specific bluetooth devices whose MAC addresses I know, but which are not necessarily paired (I don't want to mess with the user's list of paired devices and vice versa). I'm only interested in discovering their presence, not communicating with them.
This works very well with my code below! But my problem is that I cannot find out which specific device is connecting/disconnecting, only that it happens to someone of them. How can I find out which one the action concerns?
First I instantiate objects for my two specific physical bluetooth devices and add them to my intent filter:
BluetoothDevice myPinkHeadset = mBluetoothAdapter.getRemoteDevice("18:17:0C:EB:9C:81");
BluetoothDevice myPcBluetoothDongle = mBluetoothAdapter.getRemoteDevice("5A:7A:CC:4B:C5:08");
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(myPinkHeadset.ACTION_ACL_CONNECTED);
intentFilter.addAction(myPinkHeadset.ACTION_ACL_DISCONNECTED);
intentFilter.addAction(myPcBluetoothDongle.ACTION_ACL_CONNECTED);
intentFilter.addAction(myPcBluetoothDongle.ACTION_ACL_DISCONNECTED);
Then I listen for broadcasts about them:
final BroadcastReceiver intentReceiver = new BroadcastReceiver() {
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
Now I want to find out which one has been connected and/or disconnected, and I don't see how I can do that.
Either 1) I use "BluetoothDevice" directly. It reacts to the broadcast alright, but it doesn't tell me which of the two physical devices the action concerns. Is their a way to find out? Bluetooth.getName() is not allowed because it's not a static class.
if (BluetoothDevice.ACTION_ACL_CONNECTED.equals(action)) {
}
or 2) I listen for both actions for both devices.
if (myPinkHeadset .ACTION_ACL_CONNECTED.equals(action)) {
Log.v(TAG, "Connected to myPinkHeadset ");
}
else if (myPinkHeadset .ACTION_ACL_DISCONNECTED.equals(action)) {
Log.v(TAG, "Disconnected from myPinkHeadset ");
}
else if (myPcBluetoothDongle .ACTION_ACL_CONNECTED.equals(action)) {
Log.v(TAG, "Connected to myPcBluetoothDongle ");
}
else if (myPcBluetoothDongle .ACTION_ACL_DISCONNECTED.equals(action)) {
Log.v(TAG, "Disconnected from myPcBluetoothDongle ");
But then it logs that it connects with myPinkHeadset even if it is myPvBluetoothDongle I activate physically. It always goes for the one which comes first of the if tests. It cares only about the action itself, not about which object it concerns.
I saw that EXTRA_DEVICE is "Used as a Parcelable BluetoothDevice extra field in every intent broadcast by this class." But it only returns null to me:
String extra = intent.getStringExtra(BluetoothDevice.EXTRA_DEVICE);
This gives the device connected to:
BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
As a newbie, I misunderstood the parcelable concept. EXTRA_DEVICE is a String, but it's just a tag for the object. So there's no need to register or listen to individual instances of BluetoothDevice. When an action is broadcasted, the intent will tell which physical device caused it. (Can I +1 myself for this :-D)
intentFilter.addAction(myPinkHeadset.ACTION_ACL_CONNECTED);
intentFilter.addAction(myPcBluetoothDongle.ACTION_ACL_CONNECTED);
and
intentFilter.addAction(myPinkHeadset.ACTION_ACL_DISCONNECTED);
intentFilter.addAction(myPcBluetoothDongle.ACTION_ACL_DISCONNECTED);
are the same value. It's static value.
BluetoothDevice.ACTION_ACL_CONNECTED and BluetoothDeviceACTION_ACL_DISCONNECTED
private void register() {
context.registerReceiver(bluetoothBroadCast, new IntentFilter(BluetoothDevice.ACTION_ACL_DISCONNECTED));
context.registerReceiver(bluetoothBroadCast, new IntentFilter(BluetoothDevice.ACTION_ACL_CONNECTED ));
}
private final BroadcastReceiver bluetoothBroadCast = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
switch (action) {
case BluetoothDevice.ACTION_ACL_CONNECTED: {
BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
if(device.getAddress().equals(myPinkHeadset.getAddress)) {
//Do what you want
}
break;
}
case BluetoothDevice.ACTION_ACL_DISCONNECTED: {
BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
break;
}
}
}
};
I hope this can help you