I have an app that requires the use of Bluetooth to send data like 4-5 numbers and 2-3 texts between devices but i am new to java and the guide from google developers page is a little difficult for me to understand.
In order to make it easier for anyone whiling to help me i will write down in steps what i have done so far.
1. I have asked for permissions in manifest:
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
2. I got the default adapter in my activity:
final BluetoothAdapter mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
3. I created a button that when clicked the device scans for other devices:
bt_scan.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
//first checks if bluettoth is enabled
if (!mBluetoothAdapter.isEnabled()) {
//if not it enables it
Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
}
// Register for broadcasts when a device is discovered.
IntentFilter filter = new IntentFilter(BluetoothDevice.ACTION_FOUND);
registerReceiver(mReceiver, filter);
}
});
4. When a device is found, get the device information:
// Create a BroadcastReceiver for ACTION_FOUND.
private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (BluetoothDevice.ACTION_FOUND.equals(action)) {
// Discovery has found a device. Get the BluetoothDevice
// object and its info from the Intent.
BluetoothDevice device =
intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
String deviceName = device.getName();
String deviceHardwareAddress = device.getAddress(); // MAC address
}
}
};
5. On destroy unregister the ACTION_FOUND receiver:
#Override
protected void onDestroy() {
super.onDestroy();
// Don't forget to unregister the *ACTION_FOUND* receiver.
unregisterReceiver(mReceiver);
}
6. I have created a second button that when clicked it enables discoverability (which must be clicked first from device A in order for scan button in device B, finds the device A):
bt_enable.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Intent discoverableIntent = new
Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE);
discoverableIntent.putExtra( BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION,300);
startActivity(discoverableIntent);
}
});
Questions :
A. In step 3 what REQUEST_ENABLE_BT is for?
B. Is this all i need to establish a connection without any problems between two devices?
C. If i want to connect more than two devices, is there anything else i must add?
Q. What is REQUEST_ENABLE_BT for?
Let me try to comment the code
// If bluetooth is not switched on
if ( ! mBluetoothAdapter.isEnabled() ) {
// Create a system request to enable bluetooth
Intent enableBtIntent = new Intent( BluetoothAdapter.ACTION_REQUEST_ENABLE );
// Queue the request to pops the bluetooth dialogue
startActivityForResult( enableBtIntent, REQUEST_ENABLE_BT );
}
// Standby for device found, Bluetooth may still be disabled at this point
IntentFilter filter = new IntentFilter( BluetoothDevice.ACTION_FOUND );
registerReceiver( mReceiver, filter );
// Now that we are done, bluetooth dialogue will popup
// When it's done Android calls our onActivityResult
// (because we queued it with startActivityForResult).
Q. Is this all I need to establish a connection?
I think there are two more more steps you need to do:
A. Find the device, between step 3 and 4.
Just enabling Bluetooth does not mean it'll automatically scan nearby devices, because scanning cost battery and degrades existing connections.
But with BLUETOOTH_ADMIN permission your app can start and stop the scan.
You can also get previously paired device without scan.
Start Scan: BluetoothAdapter.startDiscovery() This will call ACTION_FOUND for each device found, which fit your code.
List of paired devices: BluetoothAdapter.getBondedDevices() Previously paired Bluetooths can be found in this Set without scanning or BLUETOOTH_ADMIN, and can be connected directly. Whether the device is actually online is unknown until successful or failure, though.
B. Establish the data connection, between 4 and 5.
A device connection is like connecting a network cable.
The computers are physically connected with IP, but your programs still need to open sockets for actual data transfer.
Connection: BluetoothDevice.createRfcommSocketToServiceRecord(uuid) Creates a socket from which data can be go through InputStream and OutputStream.
Don't forget to stop the scan: BluetoothAdapter.cancelDiscovery()
You seems to want to make an M2M messaging app, so I've skipped Bluetooth Low Energy (BLE).
Q. If I want to connect more than two devices, is that all?
As far as "multiple connection" part is concerned, no, you don't need to do anything special to connect to multiple devices at the same time.
The "management" is arguably the hard part, though.
For example you'd normally want to keep a list of the devices, and have a UI that let user selectively disconnect a device, all of which must respond to unexpected disconnections or switching off bluetooth (by user, by another app, or by the system such as low-power mode).
Depends on what you actually want to do with the bluetooth devices, you may need code to remember each one's state too.
Again with error handling.
So, do expect the final code will be a bit more developed.
I would like to answer Question A
A. In step 3 what REQUEST_ENABLE_BT is for?
private static final int REQUEST_ENABLE_BT = 1;
REQUEST_ENABLE_BT is simply an integer value which identifies your request. When you receive the result Intent, the callback onActivityResult(int requestCode, int resultCode, Intent data) provides the same request code to you. In this way you can handle results from other activites
Suppose you have Activity A from where you have requested to enable bluetooth
Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
When device's bluetooth gets enable, a method onActivityResult(int requestCode, int resultCode, Intent data) will get invoked on Activity A. This method onActivityResult() is a callback method. This method will pass back the request code to the calling activity (Activity A) as soon as bluetooth gets enable. You have to override this method in your Activity A to handle results.
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data)
{
if(requestCode == REQUEST_ENABLE_BT)
Log.d("BLUETOOTH", "Successfully enabled");
}
Related
In Android 10 there apply new restrictions for apps.
We can no longer start an activity from background. While this may be fine for the majority of apps, it's a killing blow for voip-apps that need to show an incoming call after a push notification arrived.
According to this https://developer.android.com/guide/components/activities/background-starts there is a list of conditions that can be met to still allow opening an activity, but tbh I do not understand that fully (non-english-native here).
What I definitely know, is:
I do not have any running activity, task, backstack and the like
The app is NOT EVEN RUNNING
What I need to achieve:
The FCM service of the app receives a push from our server and shall present the incoming call screen (over lock screen and all - just as it did with android 9 and below)
What can I do to open an activity for an incoming voip call in android 10?
Over the lockscreen and all, just as a normal user would expect from a PHONE app.
Thanks in advance for any hints.
To open Activity over lock screen. you can use a high-notification with "full-screen intent" as CommonsWare's answer. But for more detail, you can try my solution as below code:
Create a foreground service then call buildNotification in onStartCommand method, the buildNotification method will return a notification which put into startForeground method parameter.
public class IncomingCallService extends Service {
public int onStartCommand(Intent intent, int flags, int startId) {
Notification notification = buildNotification();
startForeground(1, notification);
return START_NOT_STICKY;
}
}
In buildNotification method, we will create notification with high priority, call category and a full screen intent.
private Notification buildNotification() {
Intent fullScreenIntent = new Intent(this, IncomingCallActivity.class);
PendingIntent fullScreenPendingIntent = PendingIntent.getActivity(this, 0, fullScreenIntent, PendingIntent.FLAG_UPDATE_CURRENT);
NotificationManager notificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
NotificationCompat.Builder notificationBuilder =
new NotificationCompat.Builder(this)
.setSmallIcon(R.drawable.ic_notification_icon)
.setContentTitle("Incoming call")
.setContentText("(919) 555-1234")
.setPriority(NotificationCompat.PRIORITY_HIGH)
.setCategory(NotificationCompat.CATEGORY_CALL)
// Use a full-screen intent only for the highest-priority alerts where you
// have an associated activity that you would like to launch after the user
// interacts with the notification. Also, if your app targets Android 10
// or higher, you need to request the USE_FULL_SCREEN_INTENT permission in
// order for the platform to invoke this notification.
.setFullScreenIntent(fullScreenPendingIntent, true);
notificationBuilder.setAutoCancel(true);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
notificationManager.createNotificationChannel(new NotificationChannel("123", "123", NotificationManager.IMPORTANCE_HIGH));
notificationBuilder.setChannelId("123");
}
Notification incomingCallNotification = notificationBuilder.build();
return incomingCallNotification;
}
In onStartCommand, add a line of code to send ACTION_CLOSE_SYSTEM_DIALOGS broadcast action. This verify IMPORTANT to kick off full screen pending intent.
public int onStartCommand(Intent intent, int flags, int startId) {
Notification notification = buildNotification();
startForeground(1, notification);
sendBroadcast(new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS));
return START_NOT_STICKY;
}
Create full screen activity which you want to display over lock screen then you need to add setShowWhenLocked and setTurnScreenOn for display over lock screen. If not, your activity will be displayed behind lock screen. Below is my sample.
public class IncomingCallActivity extends AppCompatActivity {
protected void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_explore);
setShowWhenLocked(true);
setTurnScreenOn(true);
getWindow().addFlags(
WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED
| WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD
| WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON
| WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON
| WindowManager.LayoutParams.FLAG_ALLOW_LOCK_WHILE_SCREEN_ON);
}
}
Now you must start IncomingCallService when you receive a call from your logic.
public void startCallService() {
Intent intent = new Intent(context, IncomingCallService.class);
startForegroundService(intent);
}
You must declare activity, service and some permission in your manifest as below:
<uses-permission android:name="android.permission.USE_FULL_SCREEN_INTENT" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<application
...>
<activity android:name=".IncomingCallActivity" />
<service
android:name=".IncomingCallService"
android:enabled="true"
android:exported="true" />
</application>
I tested on google, samsung, vsmart phone. It work well. But for xaomi device. you need to enable some permission by flow below steps:
Long click to you app icon
Open app info
Click to "Other permission" item
Allow show on Lock screen
Now your app will work on xaomi device. If you face any problems with my solution, please leave a comment here. I will help you If I could.
Use a high-priority notification with a "full-screen intent". That will:
Invoke your "full-screen intent" if the device is locked
Otherwise, display a "heads-up" notification
Please go through my blog on how to open activity for OS 10 and also how to display heads up notification and handle clicks on the action buttons.
https://medium.com/#dcostalloyd90/show-incoming-voip-call-notification-and-open-activity-for-android-os-10-5aada2d4c1e4
Check this link this will help you
here
or
You need to ask for a permission "draw over other app" then you can make this as previous versions
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
if (!Settings.canDrawOverlays(this)) {
RequestPermission();
}
}
private void RequestPermission() {
// Check if Android P or higher
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
// Show alert dialog to the user saying a separate permission is needed
// Launch the settings activity if the user prefers
Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION,
Uri.parse("package:" + BuildConfig.APPLICATION_ID));
startActivityForResult(intent,
ACTION_MANAGE_OVERLAY_PERMISSION_REQUEST_CODE);
}
}
or You can use my this answer
https://stackoverflow.com/a/63699960/7108113
For the many threads, blogs, examples and tutorials on the topic of Broadcast Receivers and mobile data connectivity i have not seen this question asked or answered.
I believe, based on experimenting with one of my Apps, that the answer to this question is a distinct NO, that while WiFi is enabled, a Broadcast Receiver listening for Mobile Data CONNECTIVITY_CHANGE does not receive a broadcast notification when that event occurs. If i am wrong and have missed something please let me know.
My App is a home screen Widget with two classes, ActiveMobileData is the AppWidgetProvider and ConnectivityChangeReceiver is the BroadcastReceiver. The AppWidgetProvider class is my first App which i put together earlier this year mainly from code widely available in a book, on StackOverflow and on various blogs etc. There is no App just the home screen widget. It simply toggles a home screen icon between red and green to indicate the current mobile data state. It has worked perfectly for several months with about 100 users.
I decided to add the BroadcastReceiver to pick up clicks from Settings. This code is also straight forward - it determines the current state of mobile data, and uses a global boolean variable set by AppWidgetProvider to determine if the home screen icon is red or green. Then it simply ensures that the icon color matches the mobile data state.
It all works except when WiFi is enabled it does not get a notification. If there is a way around this limitation i would appreciate hearing about it.
Following is the code for the widget and then for the receiver. I left out some details to keep it somewhat brief. iconEnabled is the shared global boolean variable ...
public class ActiveMobileData extends AppWidgetProvider {
static boolean iconEnabled;
#Override
public void onReceive(Context context, Intent intent) {
if (intent.getAction() != null)
super.onReceive(context, intent);
else {
context.startService(new Intent(context, ToggleService.class));
}
}
#Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[]appWidgetIds) {
context.startService(new Intent(context, ToggleService.class));
}
public static class ToggleService extends IntentService {
public ToggleService() {
super("ActiveMobileData$ToggleService");
}
#Override
protected void onHandleIntent(Intent intent) {
ComponentName cn = new ComponentName(this, ActiveMobileData.class);
AppWidgetManager mgr = AppWidgetManager.getInstance(this);
mgr.updateAppWidget(cn, buildUpdate(this));
}
private RemoteViews buildUpdate(Context context) {
RemoteViews updateViews = new RemoteViews(context.getPackageName(), R.layout.widget);
if (!isMobileDataEnabled(getApplicationContext())) {
updateViews.setImageViewResource(R.id.mobileDataState, R.mipmap.ic_launcher_g);
enableMobileData(getApplicationContext(), true);
iconEnabled = true;
} else {
updateViews.setImageViewResource(R.id.mobileDataState, R.mipmap.ic_launcher_r);
enableMobileData(getApplicationContext(), false);
iconEnabled = false;
}
Intent i = new Intent(this, ActiveMobileData.class);
PendingIntent pi = PendingIntent.getBroadcast(context, 0, i, 0);
updateViews.setOnClickPendingIntent(R.id.mobileDataState, pi);
return updateViews;
}
public boolean isMobileDataEnabled(Context context) {
// ... the code here is the one that uses Java reflection
}
private void enableMobileData(Context context, boolean enabled) {
// ... the code here is the one that uses Java reflection
}
} // public static class ToggleService
} // public class ActiveMobileData
Following is the code for the BroadcastReceiver ...
public class ConnectivityChangeReceiver extends BroadcastReceiver {
#Override
public void onReceive (Context context, Intent intent) {
handleIntent(context);
}
protected void handleIntent(Context context) {
ComponentName cn = new ComponentName(context, ActiveMobileData.class);
AppWidgetManager mgr = AppWidgetManager.getInstance(context);
mgr.updateAppWidget(cn, buildUpdate(context));
}
private RemoteViews buildUpdate(Context context) {
RemoteViews updateViews = new RemoteViews(context.getPackageName(), R.layout.widget);
if (!ActiveMobileData.iconEnabled && isMobileDataEnabled(context)) {
ActiveMobileData.iconEnabled = true;
updateViews.setImageViewResource(R.id.mobileDataState, R.mipmap.ic_launcher_g);
Intent i = new Intent(context, ActiveMobileData.class);
PendingIntent pi = PendingIntent.getBroadcast(context, 0, i, 0);
updateViews.setOnClickPendingIntent(R.id.mobileDataState, pi);
} else
if (ActiveMobileData.iconEnabled && !isMobileDataEnabled(context)) {
ActiveMobileData.iconEnabled = false;
updateViews.setImageViewResource(R.id.mobileDataState, R.mipmap.ic_launcher_r);
Intent i = new Intent(context, ActiveMobileData.class);
PendingIntent pi = PendingIntent.getBroadcast(context, 0, i, 0);
updateViews.setOnClickPendingIntent(R.id.mobileDataState, pi);
}
return updateViews;
}
private boolean isMobileDataEnabled(Context context) {
// ... Identical code to that in the AppWidgetProvider
}
} // class ConnectivityChangeReceiver
I don't know offhand, but I can point you to the 2 best places to look.
Best bet would be to look in detail into the JavaDoc for ConnectivityManager.html#CONNECTIVITY_ACTION and then the source code for the ConnectivityManager that is online on GrepCode
In particular comments within the source code often have very informative information that doesn't exist elsewhere.
Update:
After reading the javadoc for CONNECTIVITY_ACTION again, I believe that you are correct because it say A change in network connectivity has occurred. A default connection has either been established or lost. NOTE: Default Conn. NOT 'A Conn.' So it only gets launched when the 'default' changes. So if you lose 3g/4g/etc while on WIFI then I don't think this gets launched.
However there 'is' something you 'can' do... (but only when your widget is running) (I'm actually not 100% sure a 'widget' CAN do this... b/c I generally work with teaching services/AIDL/ContentProviders/etc (aka. 'backend' stuff within the platform) But you can put a 'refresh' button on your widget that can query to GET ALL NETWORKS and then parse through all that data and display which networks 'are' active, etc.
Also there is the option. You could make pending intents for your broadcast receiver(s) (I'd recommend just 1 BR and have different payloads so you can sort them for what is being notified) then register each of those pending intents as a call back with the ConnectivityManager to notify it whenever a 'network' that 'matches' the NetworkRequest exists. This will notify you at least when they 'come alive'...
(this next idea would likely require you to make a service with a separate thread to prevent ANR)
now when they 'die'... you 'could' setup a TCP connection and see when it dies... (not 'good' but might be only 'viable' option) (and if you are are 'generous' with trying to not wake up the phone, the battery impact could be minimal)
I'd like to somehow stop Bluetooth from changing the volumes when it connects and disconnects. I just want them to stay where they are. How could I go about this? I know how to run services and I can set up listeners, but the following has no effect:
final IntentFilter bluetoothConnectedFilter = new IntentFilter( BluetoothDevice.ACTION_ACL_CONNECTED );
bluetoothConnectedReceiver = new BroadcastReceiver() {
#Override
public void onReceive( Context context, Intent intent ) {
// Change volumes programmatically (code already tested and working elsewhere)
}
};
Thanks for any help.
I'm trying to simply set a proximity later for an area an for testing, I simply added this to the onCreate method of my main activity.
public void onCreate(Bundle bndBundle) {
IntentFilter filter = new IntentFilter(WidgetService.ACTION_STOP_PROXIMITY);
registerReceiver(new ProximityIntentReceiver(), filter);
LocationManager locManager = (LocationManager) this.getSystemService(LOCATION_SERVICE);
Intent ittIntent = new Intent(this, ProximityIntentReceiver.class);
ittIntent.putExtra(WidgetService.KEY_STOP_IDENTIFIER, 1000);
PendingIntent pitIntent = PendingIntent.getBroadcast(this, 0, ittIntent, 0);
locManager.addProximityAlert(60.15769, 24.94150, 150, -1, pitIntent);
super.onCreate(bndBundle);
getActionBar().setDisplayHomeAsUpEnabled(false);
}
..and here's the simple receiver class that I'm using
public class ProximityIntentReceiver extends BroadcastReceiver {
private static final int NOTIFICATION_ID = 1000;
#Override
public void onReceive(Context context, Intent intent) {
String key = LocationManager.KEY_PROXIMITY_ENTERING;
Boolean entering = intent.getBooleanExtra(key, false);
if (entering) {
Log.d(getClass().getSimpleName(), "entering");
}
else {
Log.d(getClass().getSimpleName(), "exiting");
}
}
}
I'm testing this on my emulator and when I use the DDMS console to set the co-ordinates of the phone manually, I still don't see the log message.
My manifest file doesn't have any special code. I've added the correct permissions and have the code for a simple activity- no services or anything.
I read through a whole bunch of posts on StacKOverflow but I haven't been able to resolve the issue. Am I missing something in my snippet?
You are registering this receiver dynamically, through registerReceiver(), to have it respond to broadcasts whose action string is WidgetService.ACTION_STOP_PROXIMITY.
However, the actual broadcast you are sending is trying to use an explicit Intent, identifying your receiver class. This does not line up with the IntentFilter that you are using with registerReceiver().
Either:
Register your receiver in the manifest and get rid of registerReceiver(), in which case your explicit Intent will work, or
Use new Intent(WidgetService.ACTION_STOP_PROXIMITY) instead of new Intent(this, ProximityIntentReceiver.class), so your Intent lines up with your IntentFilter
You cannot use explicit Intent objects to send broadcasts to receivers registered via registerReceiver(). An explicit Intent will only work with a manifest-registered receiver.
make sure you type in the right coordinates. in DDMS they're reversed, longitude first, then latitude
My Application required bluetooth connectivity. And in the first phase I am trying to open up the standard Activity "Bluetooth Device Picker" to help user scan for new device or chose a device from the list.
The problem is that I am unable to get any working example for the bluetooth device picker. The task is simple. To start an Activity with Intent "android.bluetooth.devicepicker.action.LAUNCH"
And the device picker is opening without any problem.
But the device picker requires four extras and I am unable to figure out the exact parameters for two of the extras listed below.
.putExtra("android.bluetooth.devicepicker.extra.LAUNCH_PACKAGE","com.extreme.controlcenter"
.putExtra("android.bluetooth.devicepicker.extra.DEVICE_PICKER_LAUNCH_CLASS","com.extreme.controlcenter.WelcomeActivity")
What I thought the parameters should be that
*"android.bluetooth.devicepicker.extra.LAUNCH_PACKAGE"*
should have the name of my package, so I passed that only. That is "com.extreme.controlcenter"
The second should be the name of the component that must receive the broadcast that is done after a device is selected. Here I tried putting the name of the class that has the onReceive() function.
But the problem is that the onReceive() function is NOT getting called when a device is picked in device picker!
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
//Device Selected on Device Picker
if("android.bluetooth.devicepicker.action.DEVICE_SELECTED".equals(action)) {
//context.unregisterReceiver(this);
BluetoothDevice device = (BluetoothDevice) intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
Toast.makeText(context, "device" + device.getAddress(), Toast.LENGTH_SHORT).show();
String MAC = device.getAddress();
//Log.d("my", MAC);
Intent intent2 = new Intent(WelcomeActivity.this, ControlActivity.class);
intent2.putExtra(EXTRA_DEVICE_ADDRESS, MAC);
startActivity(intent2);
}
};
I have created an Intent filter and registered a receive in the onCreate() of the main Activity
// Register the BroadcastReceiver
IntentFilter filter = new IntentFilter("android.bluetooth.devicepicker.action.DEVICE_SELECTED");
registerReceiver(mReceiver, filter);
One thing is that if I don't provide those two extras, the Broadcast event is received successfully. But that code only runs on my TAB, but same is crashing in cell phone. So I think providing those two extras are mandatory.
Thanks in Advance !
"com.extreme.controlcenter.WelcomeActivity" in your EXTRAs needs to be a BroadcastReceiver class such as MyBroadcastReceiver.class.getName(). I also have it declared in the manifest inside the tags