Deal with inactive wi-fi networks - android

Suppose phone finds open wi-fi network and connects to it. But wi-fi network is "inactive", i.e. when you open browser you see prompt for credentials. I have many apps on my phone(for example web-browser), which fail to work in such cases.
I want to send data using mobile network, but system still tries to use wi-fi.
NetworkInfo.isAvailable() and NetworkInfo.isConnected() still return true for the described wi-fi networks.
Any solution?

I've been having the same problem, and what I found is there is no such possibility via the Android SDK, you have to write your own way to do it.
It depends what do you want to do with the network and what do you mean by 'inactive' - you can be connected to a router, which doesn't have connection to the Internet, and there is no Android method to check such situation. As MrMichael wrote, ping is one way to check it, but in that case positive test gives you just info about ping - the network can have some heavy firewall which allows you to send pings, but i. e. will not let HTTP request through.
In that case, you have to write your own custom test for your own needs, alas - that's what I did. I just implemented simple ping-like protocol (I try to connect my socket on proper IP/port and send short message waiting for short answer). Only that gives me 100% warranty that the connection I want can be established.

As far as I am aware there is no way to force the use of a data connection over wifi (perhaps something that you shouldn't do without user interaction anyway).
I have the same requirements in many of my applications, I want to know if they have a viable network connection whilst the splash screen is loading (for example I show a read only version of the app if not).
The way that I get around this is to fire a simple service call to my server called isAlive which just returns a true imedialtly. This not only tells me that I am able to see my service, it also confirms that my server is on-line (no issues at my end). In the case that I do not get a response back in a timely fashion I inform the user that they have no network connection and "Please ensure you have a valid data/wifi connection before continuing". I then take the isConnected property for the wifi and modify this message to say "Your current Wireless connection does not internet access" or something similar.
Not quite the answer you were hoping for but maybe a possibility?

Just a suggestion: You may try using requestRouteToHost(). But first search SO for problems in using this method.
Also you'll need the CHANGE_NETWORK_STATE permission.

Try this....
I needed to do some custom work..but got it up and running...
My code switches from Wifi to Mobile network when its off.
And I am using the TimeService at port 37 to know that the Internet is DEAD while the wifi connection is still ON
//////////////////////////Edited//////////////////////////////////
Now i am putting here a complete working code i made. Please pardon me as the DRY (Don't Repeat Yourself Principle ) has been abused here. So please refactor the code and convert the duplicate codes into method , ie into a single sensible place, when using in production network
/////---------------------------Intial Available Network Checking
private boolean checkConnection(){
boolean connected = false;
ConnectivityManager cm = (ConnectivityManager)getSystemService(Context.CONNECTIVITY_SERVICE);
if (cm != null) {
NetworkInfo[] netInfo = cm.getAllNetworkInfo();
for (NetworkInfo ni : netInfo) {
if ((ni.getTypeName().equalsIgnoreCase("WIFI")
|| ni.getTypeName().equalsIgnoreCase("MOBILE"))
& ni.isConnected() & ni.isAvailable()) {
connected = true;
}
}
}
return connected;
}
/////---------------------------Intial Available Network Checking
/////-------------------------------Check for the working Internet Connection
public boolean inetAddr(){
boolean x1 = false;
try {
Socket s = new Socket("utcnist.colorado.edu", 37);
InputStream i = s.getInputStream();
Scanner scan = new Scanner(i);
while(scan.hasNextLine()){
System.out.println(scan.nextLine());
x1 = true;
}
} catch (Exception e) {
x1 = false;
}
return x1;
}
/////-------------------------------Check for the working Internet Connection
////-------------------------------Check Mobile Conectivity Again
public boolean mobileConnect(){
boolean conn = false;
ConnectivityManager cm = (ConnectivityManager)getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo activeNet = cm.getNetworkInfo(ConnectivityManager.TYPE_MOBILE);
if(activeNet != null){
conn = true;
}else{
conn = false;
}
return conn;
}
////------------------------------Check Mobile Conectivity Again
Here i am using the Above Methods....
try{
if (!checkConnection()){
AlertDialog.Builder myAlertDialog = new AlertDialog.Builder(YumZingSplashActivity.this);
myAlertDialog.setTitle("--- Connectivity Check ---");
myAlertDialog.setMessage("No Internet Connectivity");
myAlertDialog.setPositiveButton("OK", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface arg0, int arg1) {
YumZingSplashActivity.this.finish();
//splashHandler.removeCallbacks(launcherRunnable);
}});
System.out.println("No Internet Connectivity");
myAlertDialog.show();
}
else{
if(inetAddr()){
aphandle = APIHandling.getInstance();
aphandle.xmlCreateSession();
System.out.println("Net Connectivity is Present");
DURATION = Integer.valueOf(getString(R.string.splash_duration));
splashHandler = new Handler();
// ================ Main Code of the Application
launcherRunnable = new Runnable() {
public void run() {
Intent i = new Intent(YumZingSplashActivity.this, YumZingTabHostActivity.class);
i.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivity(i);
YumZingSplashActivity.this.finish();
}
};
if (DEBUG)
{
splashHandler.post(launcherRunnable);
}
else{
splashHandler.postDelayed(launcherRunnable, DURATION);
}
}
else{
if(mobileConnect()){
if(inetAddr()){
aphandle = APIHandling.getInstance();
aphandle.xmlCreateSession();
System.out.println("Net Connectivity is Present");
DURATION = Integer.valueOf(getString(R.string.splash_duration));
splashHandler = new Handler();
// ================ Main Code of the Application
launcherRunnable = new Runnable() {
public void run() {
Intent i = new Intent(YumZingSplashActivity.this, YumZingTabHostActivity.class);
i.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivity(i);
YumZingSplashActivity.this.finish();
}
};
if (DEBUG)
{
splashHandler.post(launcherRunnable);
}
else{
splashHandler.postDelayed(launcherRunnable, DURATION);
}
}else{
AlertDialog.Builder myAlertDialog = new AlertDialog.Builder(YumZingSplashActivity.this);
myAlertDialog.setTitle("--- Connectivity Check ---");
myAlertDialog.setMessage("No Internet Connectivity");
myAlertDialog.setPositiveButton("OK", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface arg0, int arg1) {
YumZingSplashActivity.this.finish();
//splashHandler.removeCallbacks(launcherRunnable);
}});
System.out.println("No Internet Connectivity");
myAlertDialog.show();
}
}else{
AlertDialog.Builder myAlertDialog = new AlertDialog.Builder(YumZingSplashActivity.this);
myAlertDialog.setTitle("--- Connectivity Check ---");
myAlertDialog.setMessage("No Internet Connectivity");
myAlertDialog.setPositiveButton("OK", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface arg0, int arg1) {
YumZingSplashActivity.this.finish();
//splashHandler.removeCallbacks(launcherRunnable);
}});
System.out.println("No Internet Connectivity");
myAlertDialog.show();
}
}
}
//setContentView(R.layout.yumzing_splash_layout);
} catch(Exception ex){
System.out.println("Leak ko catch");
}
}

You could connect to the wifi network, try connect to any page in background and verify if there is any redirection. If so, it is very likely to be the credential pages. In fact when I was trying to find how to implement the solution I have just described, I found it to be described in HttpURLConnection class documentation at Android developers site. There you can read:
Handling Network Sign-On
Some Wi-Fi networks block Internet access until the user clicks through a sign-on page. Such sign-on pages are typically presented by using HTTP redirects. You can use getURL() to test if your connection has been unexpectedly redirected. This check is not valid until after the response headers have been received, which you can trigger by calling getHeaderFields() or getInputStream(). For example, to check that a response was not redirected to an unexpected host:
HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();
try {
InputStream in = new BufferedInputStream(urlConnection.getInputStream());
if (!url.getHost().equals(urlConnection.getURL().getHost())) {
// we were redirected! Kick the user out to the browser to sign on?
...
} finally {
urlConnection.disconnect();
}
}

Try
InetAddress.getByName(host).isReachable(timeOut)

Related

Internet check, where to place when using MVP, RX and Retrofit

I have went through this and this post. So I really agree with the second post that presenter should not be aware of android specific thing. So what I am thinking is putting internet check in service layer.
I am using Rx Java for making network calls, so I can either place the network check before making a service call, so this way I need to manually throw and IOException because I need to show an error page on view when network is not available, the other option is I create my own error class for no internet
Observable<PaginationResponse<Notification>> response = Observable.create(new Observable.OnSubscribe<PaginationResponse<Notification>>() {
#Override
public void call(Subscriber<? super PaginationResponse<Notification>> subscriber) {
if (isNetworkConnected()) {
Call<List<Notification>> call = mService.getNotifications();
try {
Response<List<Notification>> response = call.execute();
processPaginationResponse(subscriber, response);
} catch (IOException e) {
e.printStackTrace();
subscriber.onError(e);
}
} else {
//This is I am adding manually
subscriber.onError(new IOException);
}
subscriber.onCompleted();
}
});
The other way I though of is adding interceptor to OkHttpClient and set it to retrofit
OkHttpClient.Builder builder = new OkHttpClient().newBuilder();
builder.addInterceptor(new Interceptor() {
#Override
public Response intercept(Chain chain) throws IOException {
if (!isNetworkConnected()) {
throw new IOException();
}
final Request.Builder builder = chain.request().newBuilder();
Request request = builder.build();
return chain.proceed(request);
}
});
Now the 2nd approach is more scalable, but I am not sure it will be efficient as I would be unnecessarily calling service method and call.execute() method.
Any suggestion which way should be used?
Also my parameter for judging the way is
Efficiency
Scalability
Generic : I want this same logic can be used across apps who are following the similar architecture where MVP and Repository/DataProvider (May give data from network/db)
Other suggestions are also welcome, if you people are already using any other way.
First we create a utility for checking internet connection, there are two ways we can create this utility, one where the utility emits the status only once, which looks like this,
public class InternetConnection {
public static Observable<Boolean> isInternetOn(Context context) {
ConnectivityManager connectivityManager
= (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo activeNetworkInfo = connectivityManager.getActiveNetworkInfo();
return Observable.just(activeNetworkInfo != null && activeNetworkInfo.isConnected());
}
}
Other way of creating this utility is, where the utility keeps emitting the connection status if it changes, which looks like this,
public class InternetConnection {
public Observable<Boolean> isInternetOn(Context context) {
final IntentFilter filter = new IntentFilter();
filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
return Observable.create(new Observable.OnSubscribe<Boolean>() {
#Override
public void call(final Subscriber<? super Boolean> subscriber) {
final BroadcastReceiver receiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
ConnectivityManager cm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo netInfo = cm.getActiveNetworkInfo();
subscriber.onNext(netInfo != null && netInfo.isConnected());
}
};
context.registerReceiver(receiver, filter);
subscriber.add(unsubscribeInUiThread(() -> context.unregisterReceiver(receiver)));
}
}).defaultIfEmpty(false);
}
private Subscription unsubscribeInUiThread(final Action0 unsubscribe) {
return Subscriptions.create(() -> {
if (Looper.getMainLooper() == Looper.myLooper()) {
unsubscribe.call();
} else {
final Scheduler.Worker inner = AndroidSchedulers.mainThread().createWorker();
inner.schedule(() -> {
unsubscribe.call();
inner.unsubscribe();
});
}
});
}
}
Next, in your dataSource or Presenter use switchMap or flatMap to check for internet connection before doing any network operation which looks like this,
private Observable<List<GitHubUser>> getGitHubUsersFromRetrofit() {
return isInternetOn(context)
.filter(connectionStatus -> connectionStatus)
.switchMap(connectionStatus -> gitHubApiInterface.getGitHubUsersList()
.map(gitHubUserList -> {
gitHubUserDao.storeOrUpdateGitHubUserList(gitHubUserList);
return gitHubUserList;
}));
}
Note that, we are using switchMap instead of flatMap. why switchMap? because, we have 2 data stream here, first is internet connection and second is Retrofit. first we will take connection status value (true/false), if we have active connection, we will create a new Retrofit stream and return start getting results, down the line if we the status of the connection changes, switchMap will first stop the existing Retrofit connection and then decide if we need to start a new one or ignore it.
EDIT:
This is one of the sample, which might give better clarity https://github.com/viraj49/Realm_android-injection-rx-test/blob/master/app-safeIntegration/src/main/java/tank/viraj/realm/dataSource/GitHubUserListDataSource.java
EDIT2:
So you mean switch map will try it itself once internet is back?
Yes and No, let's first see the difference between flatMap & switchMap. Let's say we have an editText and we search some info from network based on what user types, every time user adds a new character we have to make a new query (which can be reduced with debounce), now with so many network calls only the latest results are useful, with flatMap we will receive all the results from all the calls we made to the network, with switchMap on the other hand, the moment we make a query, all previous calls are discarded.
Now the solution here is made of 2 parts,
We need an Observable that keeps emitting current state of Network, the first InternetConnection above sends the status once and calls onComplete(), but the second one has a Broadcast receiver and it will keep sending onNext() when network status changes. IF you need to make a reactive solution go for case-2
Let's say you choose InternetConnection case-2, in this case use switchMap(), cause when network status changes, we need to stop Retrofit from whatever it is doing and then based on the status of network either make a new call or don't make a call.
How do I let my view know that the error is internet one also will this be scalable because I need to do with every network call, any suggestions regarding writing a wrapper?
Writing a wrapper would be excellent choice, you can create your own custom response which can take multiple entries from a set of possible responses e.g. SUCCESS_INTERNET, SUCCESS_LOGIN, ERROR_INVALID_ID
EDIT3: Please find an updated InternetConnectionUtil here https://github.com/viraj49/Realm_android-injection-rx-test/blob/master/app-safeIntegration/src/main/java/tank/viraj/realm/util/InternetConnection.java
More detail on the same topic is here: https://medium.com/#Viraj.Tank/android-mvp-that-survives-view-life-cycle-configuration-internet-changes-part-2-6b1e2b5c5294
EDIT4: I have recently created an Internet utility using Android Architecture Components - LiveData, you can find full source code here,
https://github.com/viraj49/Internet-Utitliy-using-AAC-LiveData
A detailed description of the code is here,
https://medium.com/#Viraj.Tank/internet-utility-using-android-architecture-components-livedata-e828a0fcd3db

How can I check socket.io connect or disconnect on android?

I connect to the node server with socketio.SocketIO running as a service.And, When Service restarts,opens socket.io without socket.io closure.That's a problem.
A device making multiple socketIO connection on the server side.So the server is swelling..
! I am using gottox/socketio-java-client on android.
Check Socket is connected or not using socket.isConnected().
This will return true if socket is connected
Its just an idea so i don't know the limitations. pls let me know.
You can ping the server to check if the connection is alive.
In android
socket.emit("ping","ping");
socket.on("pong",pingHandler); //EmitterListener
private Emitter.Listener pingHandler=new Emitter.Listener(){
#Override
public void call(final Object... args) {
Log.d("SocketStatus","Connection is active");
});
}
and make the server return response for the ping
socket.on("ping",function(data){
socket.emit("pong","pong"); //from your server ex.Node.js
});
You can check the socket.connected property:
var socket = io.connect();
console.log('Connected status before onConnect', socket.socket.connected);
socket.on('connect', function() {
console.log('Connected status onConnect', socket.socket.connected);
});
It's updated dynamically, if the connection is lost it'll be set to false until the client picks up the connection again. So easy to check for with setInterval or something like that.
Another solution would be to catch disconnect events and track the status yourself.
The following is an expansion/modification of Rafique Mohammed answer above. The correct way is to try to reconnect on client side.
Internet drops (server cannot communicate disconnection to client). Server crashes (server may/may not be able to tell client. Server Restart (server can tell but that just extra work). After reconnection you will also like to rejoin the room for seamless communication
public void connectAfterDisconnectSocket(String senderActivity) {
new Timer().scheduleAtFixedRate(new TimerTask() {
#Override
public void run() {
boolean isConnected = false;
isConnected = mSocket != null && mSocket.connected();
if (!isConnected) {
SocketIOClient socketIOClient = new SocketIOClient();
socketIOClient.connectToSocketIO();
if (senderActivity.equals("A")) {
A.joinChatRoom(room);
}
if (senderActivity.equals("B")) {
B.joinChatRoom(room);
}
}
}
}, 0, 1000); //put here time 1000 milliseconds=1 second
}

Start activity from another activity?

I want in my application this functionality:
When i start my app
-check if there is interne access : if yes {
start LogInActivity { if login is succesfull
dialog:"synced!" for 3second
else
dialog:"no synced!" for 3second
}
}
dialog:"no synced!"
startMainActivity
I want the first activity just to performs checks.Not to be visible and if there is internet
then forward to login Activity else login to Main activity
This is because i want my app to be used without interner.But for the logged users it will download from web service some information to be stored in Shared Preferences.Any help?
The best way is to create a small function which checks both for wifi and mobile net as follows :-
/**
* Function to check whether internet connection is available or not
*
* #return true - if net is available
*/
public boolean haveNetworkConnection() {
mHaveConnectedWifi = false;
mHaveConnectedMobile = false;
mConnectivityManager = (ConnectivityManager) mContext
.getSystemService(Context.CONNECTIVITY_SERVICE);
mNetworkInfo = mConnectivityManager.getAllNetworkInfo();
for (NetworkInfo mNetInfo : mNetworkInfo) {
if (mNetInfo.getTypeName().equalsIgnoreCase("WIFI"))
if (mNetInfo.isConnected())
mHaveConnectedWifi = true;
if (mNetInfo.getTypeName().equalsIgnoreCase("MOBILE"))
if (mNetInfo.isConnected())
mHaveConnectedMobile = true;
}
return mHaveConnectedWifi || mHaveConnectedMobile;
}
Now in your code just do :-
if(haveNetworkConnection){
// do something
}else{
// no internet
}
The advantage is that we are checking for both wifi and mobile net...
Hope the explanation was useful....
Just create a third activity that starts first and on the onCreate of that activity you run your code and call other activity.
Also you could show a splash screen on this third activity, while you decide which activity to show.
You'll want to either use startActivity() or startActivityForResult().
The advantage of startActivityForResult() is that you can receive data back from the activity you started, upon its completion.
To launch your other activity:
if(connection/login fails){
Intent loginfailed = new Intent(MainActivity.this, loginfailedactivity.class);
startActivity(loginfailed);
}
else {.....}
Here is what I did to make sure there was a wifi connection:
private void checkWifiConnection(String menuUrl){
ConnectivityManager connManager = (ConnectivityManager) getSystemService(CONNECTIVITY_SERVICE);
NetworkInfo mWifi = connManager.getNetworkInfo(ConnectivityManager.TYPE_WIFI);
if(mWifi.isConnected()){
LoadJSON lJs = new LoadJSON();
lJs.execute(menuUrl);
} else {
AlertDialog.Builder ab = new AlertDialog.Builder(context);
ab.setCancelable(true);
ab.setTitle("No Connection");
ab.setMessage("Your device is currently not connected to the Internet, please check your connection and launch the app again.");
ab.setInverseBackgroundForced(true);
ab.setPositiveButton("Okay", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
dialog.dismiss();
MainActivity.this.finish();
}
});
AlertDialog alert = ab.create();
alert.show();
}
You could insert an Intent call in the if statement that launches your Activity

Android to detect possibilities of no data connection

I have an application which needs to communicate with the server at some random interval through GPRS or EDGE.. But there are few possibilities at which internet cannot be accessed by the application when the user is in call or deactivated etc. At these time i have two scenario's recoverable and non recoverable.
Recoverable scenarios
On phone call ( User will hang up and data connection will be active
again)
No Signal (Sometimes signal may drop and the phone will get
signal again)
Non Recoverable Scenarios
Flight mode
Deactivating Data Connection
When its recoverable i can try again for the connection after some defined interval. And during non recoverable i have to alert user. For instance if the user deactivates data connection or enables flight mode i have to alert the user.
EDIT:I can able to detect flight mode through one of the intents. I couldn't able to find for others.
The below code return if valid connections are available
public boolean isConnectionsAvailable() {
boolean lRet = false;
try{
ConnectivityManager conMgr = (ConnectivityManager)getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo info= conMgr.getActiveNetworkInfo();
if(info != null && info.isConnected()) {
lRet = true ;
}else{
lRet = false ;
}
}catch (Exception e) {
Log.d("Connection Error", e.toString());
lRet = false ;
}
return lRet;
}
After this, if you have low signal strength then you make a HTTP request by setting relevant time out to it. If timeout happened give relevant alert msg to user as below
public void serverCall(String pURL){
if (isConnectionsAvailable()){
// Call server by setting proper timeout
}
}
Edit:
To check the Airplane mode status:
private static boolean isAirplaneModeOn(Context context) {
return Settings.System.getInt(context.getContentResolver(),
Settings.System.AIRPLANE_MODE_ON, 0) != 0;
}
You could try surrounding your method with try and catch. If the method fails because it cannot connect to your server for whatever reason you could call postDelayed from a Handler and re-run your method again in a pre-determined length of time.
Handler mHandler = new Handler();
Runnable yourMethodRunnable = new Runnable(){
#Override
public void run(){
yourMethod();
}
};
private void yourMethod(){
try{
// talk to server
} catch (InCallException e) {
mHandler.postDelayed(yourMethodRunnable, delay)
} catch (NoSignalException e) {
// etc...
} catch (OtherException e) {
// etc...
}
}
The exceptions are just examples, and likely don't exist, get the exceptions that you want to catch either from the Android Developer Docs, or by looking at the output from LogCat when you re-enact each time that the connection to the server would fail.

How to programmatically pair a bluetooth device on Android

For my application I'm trying to programmatically pair a bluetooth device. I'm able to show the pairing dialog for the device I want to pair and I can enter a pincode. When I press "Pair" the dialog is removed and nothing happens.
I only need to support devices with Android 2.0 and newer.
Currently I am using the following code to start the pairing progress:
public void pairDevice(BluetoothDevice device) {
String ACTION_PAIRING_REQUEST = "android.bluetooth.device.action.PAIRING_REQUEST";
Intent intent = new Intent(ACTION_PAIRING_REQUEST);
String EXTRA_DEVICE = "android.bluetooth.device.extra.DEVICE";
intent.putExtra(EXTRA_DEVICE, device);
String EXTRA_PAIRING_VARIANT = "android.bluetooth.device.extra.PAIRING_VARIANT";
int PAIRING_VARIANT_PIN = 0;
intent.putExtra(EXTRA_PAIRING_VARIANT, PAIRING_VARIANT_PIN);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(intent);
}
Before starting a pairing request I stop scanning for new devices.
My application has the following bluetooth permissions:
android.permission.BLUETOOTH_ADMIN
android.permission.BLUETOOTH
I managed to auto request a pairing procedure with keyboard featured devices through an app working as a service checking the presence of a specific kind of device and a modified version of the Settings app.
I have to say that I was working on a custom device running Android 4.0.3 without external controls (no back/Home/confirm buttons): pairing a controller on boot complete without any interaction until PIN request was mandatory.
First I created a service starting an activity on boot (with android.intent.action.BOOT_COMPLETED and android.permission.RECEIVE_BOOT_COMPLETED) that checks periodically the presence of a 1344 class device (a keyboard, the only way to input data on request) on the onReceive callback:
public void onReceive(Context context, Intent intent)
...
BluetoothDevice dev = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
...
if(dev.getBluetoothClass().getDeviceClass() == 1344){...}
Once filtered I choose the first keyboard available and then I pass the BT address to the Settings app:
Intent btSettingsIntent = new Intent(Settings.ACTION_BLUETOOTH_SETTINGS);
btSettingsIntent.putExtra("btcontroller", dev.getAddress());
startActivityForResult(btSettingsIntent, 1);
The tricky part was looking for the best position to call the pairing process. Using only the
intent.putExtra(BluetoothDevice.EXTRA_PAIRING_VARIANT, PAIRING_VARIANT_PIN);
led me to a paring dialog that once closed left me with the device paired, but unusable.
Digging into the classes of com.Android.settings.Bluetooth I found my way through the
createDevicePreference(CachedBluetoothDevice cachedDevice)
in the DeviceListPreferenceFragment.
From there I did compare my previously selected BT address with those available coming up and once successfully matched I call
cachedDevice.startPairing();
I know, it's tricky and requires access to the Android source code, but in a custom environment it works.
I hope this could be helpful.
It's my answer:
in onCreate() write this:
registerReceiver(incomingPairRequestReceiver, new IntentFilter(BluetoothDevice.ACTION_PAIRING_REQUEST));
then create variable
private final BroadcastReceiver incomingPairRequestReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (BluetoothDevice.ACTION_PAIRING_REQUEST.equals(action)) {
BluetoothDevice dev = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
//pair from device: dev.getName()
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
dev.setPairingConfirmation(true);
//successfull pairing
} else {
//impossible to automatically perform pairing,
//your Android version is below KITKAT
}
}
}
};
Unfortunately, I think the best that you are going to get is opening up Settings/Wireless & networks/Bluetooth Settings for the user like so:
Intent intent = new Intent(Settings.ACTION_BLUETOOTH_SETTINGS);
startActivityForResult(intent, REQUEST_PAIR_DEVICE);
Using reflection you can call the method createBond from the BluetoothDevice class.
See this post: How to unpair or delete paired bluetooth device programmatically on android?
There is also a solution for unpair.
Reflection is DODGY, different manufacturers can change these underlying methods as they wish! I have tested many different apps on our 10 devices here and these reflection method only works fully on roughly 75% of devices. If you want an app that works for everyone be very careful when using reflection - try some cloud testing to test your app on 100+ devices and check the failure rate.
In this case reflection is not needed at all since API 19 (KitKat 4.4)
BluetoothDevice has new method CreateBond.
private void pairDevice(BluetoothDevice device) {
device.createBond();
}
developer.android.com/reference/android/bluetooth/BluetoothDevice.html
May be you need to startActivityForResult instead of only startActivity?
Other option is to look into the BluetoothChat application sample and start an RFComm connection socket, as soon as you start the socket a pairing request will automatically appear without needing to send a separate intent for pairing. This way you won't need to handle pairing.
http://developer.android.com/resources/samples/BluetoothChat/index.html
I am using this class to do connection between my client smartphone and the server device:
private class ConnectThread extends Thread
{
private final BluetoothSocket mmSocket;
private final UUID WELL_KNOWN_UUID = UUID.fromString("00001101-0000-1000-8000-00805f9b34fb");
public ConnectThread(BluetoothDevice device)
{
// Use a temporary object that is later assigned to mmSocket,because
// mmSocket is final
BluetoothSocket tmp = null;
// Get a BluetoothSocket to connect with the given BluetoothDevice
try
{
tmp = device.createRfcommSocketToServiceRecord(WELL_KNOWN_UUID);
//This is the trick
Method m = device.getClass().getMethod("createRfcommSocket", new Class[] { int.class });
tmp = (BluetoothSocket) m.invoke(device, 1);
} catch (Exception e)
{
e.printStackTrace();
}
mmSocket = tmp;
}
public void run()
{
DebugLog.i(TAG, "Trying to connect...");
// Cancel discovery because it will slow down the connection
mBluetoothAdapter.cancelDiscovery();
try
{
// Connect the device through the socket. This will block
// until it succeeds or throws an exception
mmSocket.connect();
DebugLog.i(TAG, "Connection stablished");
} catch (IOException connectException)
{
// Unable to connect; close the socket and get out
DebugLog.e(TAG, "Fail to connect!", connectException);
try
{
mmSocket.close();
} catch (IOException closeException)
{
DebugLog.e(TAG, "Fail to close connection", closeException);
}
return;
}
}
/** Will cancel an in-progress connection, and close the socket */
public void cancel()
{
try
{
mmSocket.close();
} catch (IOException e)
{
}
}
}
First, get the BluetoothDevice object that you want to connect (listing paired devices or discoverying devices). Then do:
ConnectThread ct = new ConnectThread(device);
ct.start();
Because connect() is a blocking call, this connection procedure should always be performed in a thread separate from the main activity thread. See Android Developers for more detailed info.
I've found that using different values for PAIRING_VARIANT_PIN result in different pairing UI behaviours.
See this page:
http://code.google.com/p/backport-android-bluetooth/source/browse/trunk/backport-android-bluetooth201/src/backport/android/bluetooth/BluetoothDevice.java?spec=svn67&r=67
I suspect the problem you're having is that both devices are Bluetooth 2.1, in which case a pairing request should result in a 6 digit passkey being displayed on both devices.
The best result I was able to achieve was using PAIRING_VARIANT_PIN = 0. When prompted by my application, I entered pin 1234 and a 6 digit passkey appeared on my target device. The pairing UI finished and that was that.
Either you need to find out how to initiate a Bluetooth 2.1 pairing request, using some other pairing variant or pairing variant pin. Or, you're not catching the result of the activity that's running properly.
Given the amount of time I've been trying to do this, I've decided that my end users will just have to pair using the android settings before using my application.
This is how I get it:
Bluetooth device = mBtAdapter.getRemoteDevice(address);
//address:11:23:FF:cc:22
Method m = device.getClass()
.getMethod("createBond", (Class[]) null);
m.invoke(device, (Object[]) null); // send pairing dialog request
After pairing//
connectDevice(address);
in addition to my comment, by the way, even if these ACTION types did exist, that's not how you use them. here's an example:
Intent intent = new Intent(BluetoothDevice.ACTION_PAIRING_REQUEST);
intent.putExtra(EXTRA_DEVICE, device);
int PAIRING_VARIANT_PIN = 272;
intent.putExtra(BluetoothDevice.EXTRA_PAIRING_VARIANT, PAIRING_VARIANT_PIN);
sendBroadcast(intent);

Categories

Resources