After connectivityManager.getActiveNetworkInfo() got deprecated in API 29, it was quite a struggle to put together a network monitoring function that would also distinguish metered (cell) vs WiFi connection.
The problems I faced were 2:
connectivityManager.isActiveNetworkMetered() -- when both WiFi and Mobile data were on, it returned true (at least on my emulator, and I couldn't check on a device). When only WiFi was on, it returned false, as expected.
connectivityManager.isDefaultNetworkActive() -- it would always be active even if WiFi and Mobile were off.
The only way I found to work around this is via (1) onCapabilitiesChanged() and (2) getActiveNetwork()==null. The code is pasted below.
Do you know if there are other ways to do it? The whole implementation seems cumbersome, and the internet is full of deprecated examples.
Thanks
private static void monitorNetworks(){
NetworkRequest.Builder builder = new NetworkRequest.Builder();
connectivityManager.registerNetworkCallback(
builder.build(),
ncb = new ConnectivityManager.NetworkCallback() {
#Override
public void onAvailable(Network network) {
scanAndSend();
}
#Override
public void onLost(Network network) {
scanAndSend();
}
#Override
public void onUnavailable(){
scanAndSend();
}
#Override
public void onCapabilitiesChanged (Network network,
NetworkCapabilities networkCapabilities){
boolean metered = !networkCapabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED);
Log.d(TAG, "NET_CAPABILITY_NOT_METERED: " +
String.valueOf(networkCapabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED)));
sendMetered(metered);
}
}
);
}
private static void scanAndSend(){
boolean is_connected = false;
if(connectivityManager.getActiveNetwork()==null) is_connected = false;
else is_connected = true;
sendConnectivityIntent(IS_NETWORK_AVAILABLE, is_connected);
}
private static void sendMetered(boolean metered) {
sendIntent(IS_NETWORK_METERED, metered);
}
private static void sendIntent(String val_name, boolean val) {
Intent intent = new Intent();
intent.setAction(CONNECTIVITY_ACTION);
intent.putExtra(val_name, val);
context.sendBroadcast(intent);
}
Related
I'm new to Android Studio and I'm using the functionality of Local Only Hotspot to turn on and off programmatically the hotspot (found this two post for reference: How to turn on/off wifi hotspot programmatically in Android 8.0 (Oreo),How to turn on Wifi-Hotspot programmatically on Android >= 7.1 (including sharing the internet access)? .
private void turnOnHotspot() {
wifiManager.startLocalOnlyHotspot(new
WifiManager.LocalOnlyHotspotCallback()
{
#Override
public void onStarted(WifiManager.LocalOnlyHotspotReservation reservation) {
super.onStarted(reservation);
hotspotReservation = reservation;
String key = hotspotReservation.getWifiConfiguration().preSharedKey;
String ussid = hotspotReservation.getWifiConfiguration().SSID;
System.out.println("KEY: "+ key);
System.out.println("USSID: "+ ussid);
currentConfig = hotspotReservation.getWifiConfiguration();
System.out.println("STARTED THE HOTSPOT");
}
#Override
public void onStopped() {
super.onStopped();
System.out.println("STOPPED THE HOTSPOT");
}
#Override
public void onFailed(int reason) {
super.onFailed(reason);
System.out.println("FAILED THE HOTSPOT");
}
}, new Handler());
}
private void turnOffHotspot() {
active = false;
if (hotspotReservation != null) {
hotspotReservation.close();
System.out.println("CLOSE HOTSPOT");
}
}
But from what I gather from other older posts and documentation, this method gives a local network without internet access and a random SSID and Password that cannot be personalised.
I need to connect only one device to this hotspot to share the mobile data (to have internet access), but I didn't find anything that could have help me. Is there another alternative?
With my current configuration, other android device gets to read the set UUID just fine but when it comes to iOS, my colleague is getting variants of UUID from this advertisement/GATT broadcast. Do I really need to broadcast GATT for iOS to discover me?
Starting the advertisement:
#ReactMethod
private void advertise(Callback advCallBack) {
...
private static UUID myUUID = UUID.fromString("A85A30E5-93F3-42AE-86EB-33BFD8133597");
AdvertiseSettings settings = new AdvertiseSettings.Builder()
.setAdvertiseMode(AdvertiseSettings.ADVERTISE_MODE_BALANCED)
.setTxPowerLevel(AdvertiseSettings.ADVERTISE_TX_POWER_HIGH).setConnectable(false).build();
ParcelUuid pUuid = new ParcelUuid(myUUID);
AdvertiseData data = new AdvertiseData.Builder().setIncludeDeviceName(false).addServiceUuid(pUuid)
.setIncludeTxPowerLevel(true).build();
AdvertiseCallback advertisingCallback = new AdvertiseCallback() {
#Override
public void onStartSuccess(AdvertiseSettings settingsInEffect) {
super.onStartSuccess(settingsInEffect);
}
#Override
public void onStartFailure(int errorCode) {
super.onStartFailure(errorCode);
}
};
advertiser.startAdvertising(settings, data, advertisingCallback);
}
Starting the GATT Server:
#ReactMethod
private void startServer(Callback srvCallBack) {
mBluetoothGattServer = mBluetoothManager.openGattServer(getReactApplicationContext(), mGattServerCallback);
if (mBluetoothGattServer == null) {
srvCallBack.invoke(false);
return;
}
mBluetoothGattServer.addService(new BluetoothGattService(myUUID, BluetoothGattService.SERVICE_TYPE_PRIMARY));
srvCallBack.invoke(true);
}
private BluetoothGattServerCallback mGattServerCallback = new BluetoothGattServerCallback() {
#Override
public void onConnectionStateChange(BluetoothDevice device, int status, int newState) {
if (newState == BluetoothProfile.STATE_CONNECTED) {
return;
} else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
return;
}
}
};
We don't need to connect to each other, we just need to scan for its existence and maybe pass bits of information which I can't do right now from android because just including the device name is giving me an Error code 1 which basically means that the payload is bigger than the allowed 31 bytes for the advertisement packet. Any advise?
I solved the problem by including a serviceData on the advertising data.
AdvertiseData advertiseData = new AdvertiseData.Builder().setIncludeDeviceName(true)
.addServiceUuid(new ParcelUuid(SERVICE_UUID)).addServiceData(new ParcelUuid(SERVICE_UUID), serviceData)
.setIncludeTxPowerLevel(true).build();
So, I am developing a Java BLE Android Module using Eclipse (to code the module and Appcelerator (to make the Android App). I am trying to receive data from a BLE Device and show it on an Android Phone. I can scan the device and connect to it.
But I really, really can't receive any data from it.
I have tried at least 10 different stuff but...
The main problem is that I don't know the BLE API very well, and I am a little noobie in Java. Can anyone please help a poor soul to actually read the data from the device?
The main problem is setting the Bluetooth Characteristic UUID (which I have). I just don't know how to do it...
Bellow are the codes for the Module...
public class AndroidbleModule extends KrollModule {
public static final String LCAT = "BLE";
private BluetoothManager btManager;
private BluetoothAdapter btAdapter;
private BluetoothDevice btDevice;
private TiApplication appContext;
private Activity activity;
private KrollFunction onFound;
private KrollFunction onConnections;
private BluetoothLeScanner btScanner;
private UUID uuid;
BluetoothGatt bluetoothGatt;
BluetoothGattCharacteristic btChar;
BluetoothGattCallbackHandler btData;
KrollDict kd;
Boolean isConnected = false;
BluetoothGatt connectedGatt;
private ScanCallback scanCallback = new ScanCallback() {
#Override
public void onScanResult(int callbackType, ScanResult result) {
BluetoothDevice device = result.getDevice();
if (device != null) {
BluetoothDeviceProxy btDeviceProxy = new
BluetoothDeviceProxy(device);
if (device.getName() != null) {
Log.d(LCAT, "Found: " + device.getName() + " " +
device.getAddress());
ArrayList<String> ids = new ArrayList<String>();
if (device.getUuids() != null) {
for (ParcelUuid id1 : device.getUuids()) {
ids.add(id1.toString());
}
}
btDevice = device;
kd = new KrollDict();
kd.put("name", btDevice.getName());
kd.put("macaddress", btDevice.getAddress());
fireEvent("nb_DevicesFound", kd);
btScanner.stopScan(scanCallback);
}
}
}
};
#Kroll.method
public boolean connect()
{
try {
bluetoothGatt = btDevice.connectGatt(appContext, true,
new BluetoothGattCallbackHandler(AndroidbleModule.this));
if (bluetoothGatt != null) {
System.out.println("***** ***** Connected to: =====>>>>> " + btDevice.getAddress() + " " + btDevice.getName());
this.fireEvent("nb_onConnect",null);
isConnected = true;
bluetoothGatt = connectedGatt;
}
} catch (Exception e) {
isConnected = false;
this.fireEvent("nb_NoConnection", null);
}
return true;
};
#Kroll.method
public void readData()
{
System.out.println("WHAT THE HELL DO I DO????");
}
}
public final class BluetoothGattCallbackHandler extends
BluetoothGattCallback {
private static final String LCAT = AndroidbleModule.LCAT;
private KrollProxy proxy;
private static final String UUID_SERVICE_TS002004 = "6E400001-B5A3-F393-E0A9-E50E24DCCA9E";
private static final String UUID_CHARACTERISTIC_WRITE_TS002004 = "6E400002-B5A3-F393-E0A9-E50E24DCCA9E";
private static final String UUID_CHARACTERISTIC_NOTIFY_TS002004 = "6E400003-B5A3-F393-E0A9-E50E24DCCA9E";
BluetoothGattCharacteristic btCharacteristic;
public BluetoothGattCallbackHandler(KrollProxy proxy) {
super();
this.proxy = proxy;
}
#Override
public void onConnectionStateChange(final BluetoothGatt gatt,
final int status, final int newState) {
KrollDict kd = new KrollDict();
kd.put("newState", newState);
kd.put("status", status);
if (proxy.hasListeners("didConnectionStateChange")) {
proxy.fireEvent("didConnectionStateChange", kd);
}
gatt.discoverServices();
}
#Override
public void onServicesDiscovered(BluetoothGatt gatt, int status) {
super.onServicesDiscovered(gatt, status);
Log.i(LCAT,"onServicesDiscovered");
if (status != BluetoothGatt.GATT_SUCCESS) return;
btCharacteristic =
gatt.getService(UUID.fromString(UUID_SERVICE_TS002004)).getCharacteristic(UUID.fromString(UUID_CHARACTERISTIC_NOTIFY_TS002004));
gatt.setCharacteristicNotification(btCharacteristic,true);
BluetoothGattDescriptor descriptor = btCharacteristic.getDescriptor(UUID.fromString(UUID_CHARACTERISTIC_WRITE_TS002004));
descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
gatt.writeDescriptor(descriptor);
}
#Override
public void onCharacteristicChanged(BluetoothGatt gatt,
final BluetoothGattCharacteristic characteristic) {
byte[] data = characteristic.getValue();
Log.i(LCAT, "Char changed " + data.toString());
for (BluetoothGattDescriptor descriptor :
characteristic.getDescriptors()) {
descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE;
}
}
#Override
public void onCharacteristicRead(BluetoothGatt gatt,
BluetoothGattCharacteristic characteristic, int status) {
super.onCharacteristicRead(gatt, characteristic, status);
Log.i(LCAT,"onCharacteristicRead");
}
#Override
public void onDescriptorRead(BluetoothGatt gatt,
BluetoothGattDescriptor descriptor, int status) {
super.onDescriptorRead(gatt, descriptor, status);
Log.i(LCAT,"onDescriptorRead");
}
}
I expect to some good soul that will go to Heaven to have mercy on me and help me get those sweet bytes of data.
One of the things that is missing in your code, is to Set Notifications, so the Central can listen to the Peripheral's response.
Try something like this:
public void setNotifications() {
BluetoothGattService service = bluetoothGatt.getService(SERVICE_UUID);
BluetoothGattCharacteristic characteristic = service.getCharacteristic(CHARACTERISTIC_UUID);
BluetoothGattDescriptor descriptor = characteristic.getDescriptor(DESCRIPTOR_UUID);
descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
characteristic.addDescriptor(descriptor);
characteristic.setWriteType(BluetoothGattCharacteristic.WRITE_TYPE_NO_RESPONSE);
bluetoothGatt.writeDescriptor(descriptor);
bluetoothGatt.setCharacteristicNotification(characteristic, true);
}
After that, you can send commands to the device and hear it's return.
public void returnData(String data) {
BluetoothGattService service = bluetoothGatt.getService(SERVICE_UUID);
BluetoothGattCharacteristic characteristic = service.getCharacteristic(CHARACTERISTIC_UUID);
String dataString = data;
dataString.getBytes();
writeCharacteristic.setValue(dataString);
writeCharacteristic.setWriteType(BluetoothGattCharacteristic.WRITE_TYPE_NO_RESPONSE);
bluetoothGatt.writeCharacteristic(writeCharacteristic);
}
First of all, read the bluetooth overview. Check if bluetooth permissions have been added to the project.
One thing that is wrong here is that isConnected=true is set too early, because you can consider that you are connected after ble services has been discovered and (status == BluetoothGatt.GATT_SUCCESS). Otherwise, you can't read and write characteristics.
A good starting point can be this repo from google. It is old project and I'm not sure if you have to update some dependency to make it compile, but nothing important.
Establish a connection and start reading bytes is easy, but if you want establish a reliable connection with ble device, due to android fragmentation and manufacturerers that don't follow the bluetooth specs, it can be super difficult, almost imposible to make a bluetooth low energy that works perfect for all devices.
Once you have started to read some bytes, I suggest this video to learn some important tricks about. If you want go deeper, read carefully this fantastic resource about ble.
Once you start to get desperate with ble, probably it will be a good moment to read this list of known issues
Finally, you will discover that the best thing that you can do with ble low energy in android is use open source libraries like the Nordic semiconductor ble library or RxAndroid Ble.
But before use a ble library, it is a good practice understand what is doing the library and understand why you need it.
EDIT: I have never used appcelerator, but here you have a bluetooth module for appcelerator titanium.
You have the wrong uuid for the Client Characteristic Configuration Descriptor. It should be 00002902-0000-1000-8000-00805f9b34fb but you have written 6E400002-B5A3-F393-E0A9-E50E24DCCA9E.
I'm trying to create a native extension which can receive broadcasts, sent from a native android am as intent broadcasts.
The sending part works, I've tested this with a native app that has a broadcast receiver, but I cant get it to work in the native extension.
Here's what I have so far:
Here the java side of the ANE
public class ReceiverPhidget extends BroadcastReceiver {
private FREContext mFREContext;
public ReceiverPhidget(FREContext mFREContext) {
this.mFREContext = mFREContext;
}
#Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (action.equals(IntentsKeys.INTENT_PHIDGET_CONNECTED)){
//Send listener in ANE project with message that phidget connected (not must)
System.out.println("Phidget connected");
mFREContext.dispatchStatusEventAsync("Yes", Keys.KEY_CONNECTED);
} else
if (action.equals(IntentsKeys.INTENT_PHIDGET_DISCONNECTED)){
//Send listener in ANE project with message that phidget disconnected (not must)
System.out.println("Phidget disconnected");
mFREContext.dispatchStatusEventAsync("Yes", Keys.KEY_DISCONNECTED);
} else
if (action.equals(IntentsKeys.INTENT_PHIDGET_GAIN_TAG)){
//Send listener with data in ANE project with message that phidget gain receive
String message = intent.getStringExtra(IntentsKeys.INTENT_PHIDGET_EXTRA_DATA);
System.out.println("Phidget gain message: " + message);
Log.d("TAG FOUND", message);
mFREContext.dispatchStatusEventAsync(message, Keys.KEY_TAG_GAIN);
}
}
public static IntentFilter getIntentFilter(){
final IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(IntentsKeys.INTENT_PHIDGET_CONNECTED);
intentFilter.addAction(IntentsKeys.INTENT_PHIDGET_DISCONNECTED);
intentFilter.addAction(IntentsKeys.INTENT_PHIDGET_GAIN_TAG);
return intentFilter;
}
}
And the FREExtension
public class ReceiverExtension implements FREExtension {
private ReceiverPhidget mReceiverPhidget;
private ReceiverExtensionContext mContext;
#Override
public void initialize() {
mReceiverPhidget = new ReceiverPhidget(mContext);
mContext.getActivity().registerReceiver(mReceiverPhidget, ReceiverPhidget.getIntentFilter());
}
#Override
public FREContext createContext(String s) {
return mContext = new ReceiverExtensionContext();
}
#Override
public void dispose() {
mContext.getActivity().unregisterReceiver(mReceiverPhidget);
}
}
And here is the flash library side of the ANE
package nl.mediaheads.anetest.extension {
import flash.events.EventDispatcher;
import flash.events.StatusEvent;
import flash.external.ExtensionContext;
public class RFIDController extends EventDispatcher {
private var extContext:ExtensionContext;
private var channel:int;
private var scannedChannelList:Vector.<int>;
public function RFIDController() {
extContext = ExtensionContext.createExtensionContext(
"nl.mediaheads.anetest.exntension.RFIDController", "");
extContext.addEventListener(StatusEvent.STATUS, onStatus);
}
private function onStatus(event:StatusEvent):void {
if (event.level == EventKeys.KEY_TAG_GAIN) {
dispatchEvent (new TagEvent(TagEvent.TAG_GAINED, event.code) );
}
}
}
}
And here is my test mobile project class to test the ANE
package
{
import flash.display.Sprite;
import flash.display.StageAlign;
import flash.display.StageScaleMode;
import flash.events.Event;
import flash.text.TextField;
import nl.mediaheads.anetest.extension.RFIDController;
[SWF(width="1280", height="800", frameRate="60", backgroundColor="#ffffff")]
public class AneTestApp extends Sprite
{
private var tf:TextField;
private var rc:RFIDController;
public function AneTestApp()
{
super();
// support autoOrients
stage.align = StageAlign.TOP_LEFT;
stage.scaleMode = StageScaleMode.NO_SCALE;
stage.color = 0xFFFFFF;
addEventListener(Event.ADDED_TO_STAGE, onAdded);
}
private function onAdded(event:Event):void {
//
tf = new TextField();
tf.width = 200;
tf.height = 50;
tf.x = 10;
tf.y = 64;
tf.mouseEnabled = false;
tf.background = true;
tf.backgroundColor = 0xF50000;
addChild(tf);
rc = new RFIDController();
tf.text = "test 1";
this.addEventListener( TagEvent.TAG_GAINED , onTagAdded);
tf.text = "test 2";
//
}
private function onTagAdded(event:TagEvent):void
{
tf.text = event.params;
}
}
}
I have signed the ANE accordingly, I also signed the test app it's self.
I have a Log.d in the java part of the ANE which should pop up on log cat but it doesn't, also the textfield just becomes blank as soon as I initialized the RFIDController even without added the event listener.
If you need any more code or information to help me solve this problem feel free to ask.
I could really use some help because I'm completely lost, I've followed multiple tutorials and guide on how to do this, I should have done everything correctly, but I clearly have not.
UPDATE: 1
The extension xml
<extension xmlns="http://ns.adobe.com/air/extension/3.5">
<id>nl.mediaheads.anetest.exntension.RFIDController</id>
<versionNumber>0.0.1</versionNumber>
<platforms>
<platform name="Android-ARM">
<applicationDeployment>
<nativeLibrary>AneTest.jar</nativeLibrary>
<initializer>nl.mediaheads.anetest.ReceiverExtension</initializer>
<finalizer>nl.mediaheads.anetest.ReceiverExtension</finalizer>
</applicationDeployment>
</platform>
</platforms>
</extension>
UPDATE 2:
I fixed it, it was an context issue together with that flash somehow clean my custom event so I used status event to parse from the flash side of the ANE to the air application itself.
Currently you are creating your receiver at the initialisation point of the extension which will most likely be called before the context creation, so your context may be null at that point and causing your errors.
Try moving the creation of your ReceiverPhidget to the constructor of your ReceiverExtensionContext. Something like the following (I haven't tested this):
public class ReceiverExtensionContext extends FREContext
{
private ReceiverPhidget mReceiverPhidget;
public ReceiverExtensionContext()
{
mReceiverPhidget = new ReceiverPhidget( this );
getActivity().registerReceiver( mReceiverPhidget, ReceiverPhidget.getIntentFilter() );
}
#Override
public Map<String, FREFunction> getFunctions()
{
Map<String, FREFunction> functionMap = new HashMap<String, FREFunction>();
return functionMap;
}
#Override
public void dispose()
{
getActivity().unregisterReceiver( mReceiverPhidget );
}
}
I am developing an application which first discover the peers in range and then connect with all of them one by one my function look like this:
void connectTo(WifiP2pDevice device) {
WifiP2pConfig config = new WifiP2pConfig();
config.deviceAddress = device.deviceAddress;
config.groupOwnerIntent=15;
wifiP2pManager.connect(wifiDirectChannel, config, actionListener);
wifiP2pManager.createGroup(wifiDirectChannel, actionListener);
}
But I don't know the difference between the connect and createGroup function of Wifip2pManager class. What's the core difference between them, Please help!
I know I am late to answer but I am sure it would help others. There is no need to createGroup, you simply need to call connect method in this way:
void connectTo(WifiP2pDevice device) {
WifiP2pConfig wifiP2pConfig = new WifiP2pConfig();
wifiP2pConfig.deviceAddress = device.deviceAddress;
wifiP2pConfig.groupOwnerIntent = 0;
wifiP2pConfig.wps.setup = WpsInfo.PBC;
if (wifiP2pManager != null) {
wifiP2pManager.connect(mChannel, wifiP2pConfig,
new ActionListener() {
#Override
public void onSuccess() {
// WiFiDirectBroadcastReceiver will notify us.
// Ignore for now.
Utility.showToast(
WifiP2PConnectionActivity.this,
Constants.CONNECTED);
}
#Override
public void onFailure(int reason) {
Utility.showToast(
WifiP2PConnectionActivity.this,
getErrorMessage(reason));
}
});
}
It will get connected now.
wifiP2pConfig.groupOwnerIntent = 0; is set to zero so that you allow other device to become owner and your own device as client everytime. groupOwnerIntent prioritices our own device priority to be lesser of becoming groupOwner. Rest is upto you how you want your device to behave.