I have the following working code that is able to scan and connect to a BLE device:
import android.Manifest;
import android.app.AlertDialog;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothGatt;
import android.bluetooth.BluetoothGattCallback;
import android.bluetooth.BluetoothGattCharacteristic;
import android.bluetooth.BluetoothGattService;
import android.bluetooth.BluetoothManager;
import android.bluetooth.le.BluetoothLeScanner;
import android.bluetooth.le.ScanCallback;
import android.bluetooth.le.ScanResult;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Handler;
import android.support.v7.app.AppCompatActivity;
import com.google.android.gms.appindexing.Action;
import com.google.android.gms.appindexing.AppIndex;
import com.google.android.gms.common.api.GoogleApiClient;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class MainActivity2 extends AppCompatActivity {
private final static String TAG = MainActivity2.class.getSimpleName();
BluetoothManager btManager;
BluetoothAdapter btAdapter;
BluetoothLeScanner btScanner;
private final static int REQUEST_ENABLE_BT = 1;
private static final int PERMISSION_REQUEST_COARSE_LOCATION = 1;
Boolean btScanning = false;
int deviceIndex = 0;
ArrayList<BluetoothDevice> devicesDiscovered = new ArrayList<BluetoothDevice>();
BluetoothGatt bluetoothGatt;
public final static String ACTION_GATT_CONNECTED =
"com.example.bluetooth.le.ACTION_GATT_CONNECTED";
public final static String ACTION_GATT_DISCONNECTED =
"com.example.bluetooth.le.ACTION_GATT_DISCONNECTED";
public final static String ACTION_GATT_SERVICES_DISCOVERED =
"com.example.bluetooth.le.ACTION_GATT_SERVICES_DISCOVERED";
public final static String ACTION_DATA_AVAILABLE =
"com.example.bluetooth.le.ACTION_DATA_AVAILABLE";
public final static String EXTRA_DATA =
"com.example.bluetooth.le.EXTRA_DATA";
public Map<String, String> uuids = new HashMap<String, String>();
// Stops scanning after 5 seconds.
private Handler mHandler = new Handler();
private static final long SCAN_PERIOD = 5000;
/**
* ATTENTION: This was auto-generated to implement the App Indexing API.
* See https://g.co/AppIndexing/AndroidStudio for more information.
*/
private GoogleApiClient client;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
btManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
btAdapter = btManager.getAdapter();
btScanner = btAdapter.getBluetoothLeScanner();
if (btAdapter != null && !btAdapter.isEnabled()) {
Intent enableIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(enableIntent, REQUEST_ENABLE_BT);
}
// Make sure we have access coarse location enabled, if not, prompt the user to enable it
if (this.checkSelfPermission(Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
final AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle("This app needs location access");
builder.setMessage("Please grant location access so this app can detect peripherals.");
builder.setPositiveButton(android.R.string.ok, null);
builder.setOnDismissListener(new DialogInterface.OnDismissListener() {
#Override
public void onDismiss(DialogInterface dialog) {
requestPermissions(new String[]{Manifest.permission.ACCESS_COARSE_LOCATION}, PERMISSION_REQUEST_COARSE_LOCATION);
}
});
builder.show();
}
client = new GoogleApiClient.Builder(this).addApi(AppIndex.API).build();
}
// Device scan callback.
private ScanCallback leScanCallback = new ScanCallback() {
#Override
public void onScanResult(int callbackType, ScanResult result) {
devicesDiscovered.add(result.getDevice());
deviceIndex++;
}
};
// Device connect call back
private final BluetoothGattCallback btleGattCallback = new BluetoothGattCallback() {
#Override
public void onCharacteristicChanged(BluetoothGatt gatt, final BluetoothGattCharacteristic characteristic) {
}
#Override
public void onConnectionStateChange(final BluetoothGatt gatt, final int status, final int newState) {
}
#Override
public void onServicesDiscovered(final BluetoothGatt gatt, final int status) {
displayGattServices(bluetoothGatt.getServices());
}
#Override
// Result of a characteristic read operation
public void onCharacteristicRead(BluetoothGatt gatt,
BluetoothGattCharacteristic characteristic,
int status) {
if (status == BluetoothGatt.GATT_SUCCESS) {
broadcastUpdate(ACTION_DATA_AVAILABLE, characteristic);
}
}
#Override
public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
// >> DUNNO WHAT TO DO HERE <<
}
};
private void broadcastUpdate(final String action,
final BluetoothGattCharacteristic characteristic) {
System.out.println(characteristic.getUuid());
}
#Override
public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) {
switch (requestCode) {
case PERMISSION_REQUEST_COARSE_LOCATION: {
if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
System.out.println("coarse location permission granted");
} else {
final AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle("Functionality limited");
builder.setMessage("Since location access has not been granted, this app will not be able to discover beacons when in the background.");
builder.setPositiveButton(android.R.string.ok, null);
builder.setOnDismissListener(new DialogInterface.OnDismissListener() {
#Override
public void onDismiss(DialogInterface dialog) {
}
});
builder.show();
}
return;
}
}
}
public void startScanning() {
System.out.println("start scanning");
btScanning = true;
deviceIndex = 0;
devicesDiscovered.clear();
AsyncTask.execute(new Runnable() {
#Override
public void run() {
btScanner.startScan(leScanCallback);
}
});
mHandler.postDelayed(new Runnable() {
#Override
public void run() {
stopScanning();
}
}, SCAN_PERIOD);
}
public void stopScanning() {
System.out.println("stopping scanning");
btScanning = false;
AsyncTask.execute(new Runnable() {
#Override
public void run() {
btScanner.stopScan(leScanCallback);
}
});
}
public void connectToDeviceSelected() {
int deviceSelected = Integer.parseInt("0" /* MY DEVICE INDEX TAKEN FROM AN EDITTEXT*/);
bluetoothGatt = devicesDiscovered.get(deviceSelected).connectGatt(this, false, btleGattCallback);
}
public void disconnectDeviceSelected() {
bluetoothGatt.disconnect();
}
private void displayGattServices(List<BluetoothGattService> gattServices) {
if (gattServices == null) return;
// Loops through available GATT Services.
for (BluetoothGattService gattService : gattServices) {
new ArrayList<HashMap<String, String>>();
List<BluetoothGattCharacteristic> gattCharacteristics =
gattService.getCharacteristics();
// Loops through available Characteristics.
for (BluetoothGattCharacteristic gattCharacteristic :
gattCharacteristics) {
// SHOWS AVAILABLES
}
}
}
#Override
public void onStart() {
super.onStart();
client.connect();
Action viewAction = Action.newAction(
Action.TYPE_VIEW, // TODO: choose an action type.
"Main Page", // TODO: Define a title for the content shown.
// TODO: If you have web page content that matches this app activity's content,
// make sure this auto-generated web page URL is correct.
// Otherwise, set the URL to null.
Uri.parse("http://host/path"),
// TODO: Make sure this auto-generated app URL is correct.
Uri.parse("android-app://com.example.joelwasserman.androidbleconnectexample/http/host/path")
);
AppIndex.AppIndexApi.start(client, viewAction);
}
#Override
public void onStop() {
super.onStop();
Action viewAction = Action.newAction(
Action.TYPE_VIEW, // TODO: choose an action type.
"Main Page", // TODO: Define a title for the content shown.
// TODO: If you have web page content that matches this app activity's content,
// make sure this auto-generated web page URL is correct.
// Otherwise, set the URL to null.
Uri.parse("http://host/path"),
// TODO: Make sure this auto-generated app URL is correct.
Uri.parse("android-app://com.example.joelwasserman.androidbleconnectexample/http/host/path")
);
AppIndex.AppIndexApi.end(client, viewAction);
client.disconnect();
}
}
Now, once connected, I want to send data (in byte[] format) to the BLE device... How can I reach that goal? Thanks in advance
Obs.: I'm targetting APIs >= 21
Check out an example with writing a new value in characteristic:
public static final byte NO_ALERT = 0x00; //your byte data
public final static UUID IMMEDIATE_ALERT_UUID =
UUID.fromString("00001802-0000-1000-8000-00805f9b34fb"); //UUID of gatt service
public final static UUID IMMEDIATE_ALERT_LEVEL_UUID =
UUID.fromString("00002a06-0000-1000-8000-00805f9b34fb"); //UUID of characteristic
#Override
public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
byte[] data;
byte b = NO_ALERT;
data = new byte[]{b};
BluetoothGattService service = gatt.getService(IMMEDIATE_ALERT_UUID);
if (service != null) {
BluetoothGattCharacteristic characteristic = service.getCharacteristic(IMMEDIATE_ALERT_LEVEL_UUID);
characteristic.setValue(data);
gatt.writeCharacteristic(characteristic);
}
}
Related
I'm trying to get several characteritics from a BLE device (an Arduino).
My activity manages an automatic connection to the device address and it works without problems.
package com.example.flyin_glass;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import android.Manifest;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.*;
import android.bluetooth.le.*;
import android.content.pm.PackageManager;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.util.Log;
import android.content.Context;
import android.widget.TextView;
import android.widget.Toast;
import java.util.UUID;
public class MainActivity extends AppCompatActivity {
BluetoothAdapter mBluetoothAdapter;
BluetoothDevice device;
private boolean mScanning;
BluetoothGatt gatt;
BluetoothGattService commService;
BluetoothGattCharacteristic characteristic;
TextView display;
public int i = 0;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
display = (TextView) findViewById(R.id.TV_STATUS);
MyThread myThread = new MyThread();
myThread.start();
}
public class MyThread extends Thread {
public void run() {
mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
if (ActivityCompat.checkSelfPermission(getApplicationContext(), Manifest.permission.BLUETOOTH_SCAN) == PackageManager.PERMISSION_GRANTED) {
return;
}
//mBluetoothAdapter.startLeScan(mLeScanCallback);
device = mBluetoothAdapter.getRemoteDevice("4F:B8:A6:D3:F8:37");
device.connectGatt(getApplicationContext(), true, bluetoothGattCallback);
//gatt.discoverServices();
//commService = gatt.getService(UUID.fromString("00000000-0000-1000-8000-00805f9b34fb"));
//characteristic = commService.getCharacteristic(UUID.fromString("0001"));
// boolean ret = gatt.readCharacteristic(characteristic);
//final byte[] data = characteristic.getValue();
while (true) {
try {
Thread.sleep(1000); //refresh ?
/*runOnUiThread(new Runnable() {
public void run() {
display.setText("testBLE");
}
});*/
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
private BluetoothAdapter.LeScanCallback mLeScanCallback = new BluetoothAdapter.LeScanCallback() {
#Override
public void onLeScan(final BluetoothDevice device, int rssi, byte[] scanRecord) {
if (ActivityCompat.checkSelfPermission(getApplicationContext(), Manifest.permission.BLUETOOTH_CONNECT) == PackageManager.PERMISSION_GRANTED) {
return;
}
if (device.getName() == null) {
runOnUiThread(new Runnable() {
public void run() {
display.setText("Nothing");
}
});
} else {
runOnUiThread(new Runnable() {
public void run() {
if (ActivityCompat.checkSelfPermission(getApplicationContext(), Manifest.permission.BLUETOOTH_CONNECT) == PackageManager.PERMISSION_GRANTED) {
return;
}
//display.setText(device.getName());
//display.setText(device.getAddress());
display.setText(String.valueOf(rssi));
}
});
}
}
};
private final BluetoothGattCallback bluetoothGattCallback = new BluetoothGattCallback() {
#Override
public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
if (newState == BluetoothProfile.STATE_CONNECTED) {
// successfully connected to the GATT Server
runOnUiThread(new Runnable() {
public void run() {
display.setText("OK");
}
});
}
else if
(newState == BluetoothProfile.STATE_DISCONNECTED)
{
runOnUiThread(new Runnable()
{
public void run()
{
display.setText("NOK");
}
});
}
}
};
}
So it works until the line :
device.connectGatt(getApplicationContext(), true, bluetoothGattCallback);
The callBack returns OK, which means I am connected automaticaly to the device.
But when I add the next line for discover services, the application crashes.
gatt.discoverServices();
I do not know why. Have you some ideas ? I just would like be able to read and display some characteristics of my device.
Thank you in advance.
I want make some project where Android can scan nearby Beacon/BLE and send it using MQTT. But I want the service to work in the background if the service work in the foreground it will interrupt the scanning process when screen is off.
This is my code for scanning:
package com.example.mqtt_active;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
import android.Manifest;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.os.Handler;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;
import org.eclipse.paho.android.service.MqttAndroidClient;
import org.eclipse.paho.client.mqttv3.IMqttActionListener;
import org.eclipse.paho.client.mqttv3.IMqttToken;
import org.eclipse.paho.client.mqttv3.MqttException;
import java.nio.charset.StandardCharsets;
import android.util.Log;
public class MainActivity extends AppCompatActivity {
private Button turnon, changeLayout;
MqttAndroidClient client;
private boolean state=false;
private BluetoothAdapter bluetoothAdapter;
public static final int REQUEST_ACCESS_COARSE_LOCATION = 1;
public static final int REQUEST_ENABLE_BLUETOOTH = 11;
public static String mqtt_server,mqtt_port,mqtt_id;
private TextView textView;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getSupportActionBar().hide();
setContentView(R.layout.activity_main);
Log.d("Logger", "On Create Android");
turnon = findViewById(R.id.turnon);
changeLayout = findViewById(R.id.mqttSet);
bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
textView = findViewById(R.id.textView4);
textView.setText("id "+mqtt_id+" port "+mqtt_port+" server "+mqtt_server);
client = new MqttAndroidClient(this.getApplicationContext(), "tcp://"+mqtt_server+":"+mqtt_port,mqtt_id);
final Handler handler = new Handler();
handler.postDelayed(new Runnable() {
#Override
public void run() {
stateCheck();
Log.d("Logger", "State Check");
handler.postDelayed(this, 1000);
}
}, 1000);
// final Handler handlerStop = new Handler();
// handlerStop.postDelayed(new Runnable() {
// #Override
// public void run() {
// bluetoothAdapter.cancelDiscovery();
// Log.d("Logger", "Cancel Dsicovery");
// handlerStop.postDelayed(this, 2000);
// }
// }, 2000);
turnon.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
if (!state){
turnon.setText("Turn Off");
Log.d("Logger", "Turn On State");
// if (bluetoothAdapter!=null & bluetoothAdapter.isEnabled()) {
// if(checkCoarsePermission()){
// bluetoothAdapter.startDiscovery();
// }
// }
if(mqtt_server!=null||mqtt_id!=null||mqtt_port!=null){
try {
Log.d("Logger", "Try ");
IMqttToken token = client.connect();
token.setActionCallback(new IMqttActionListener() {
#Override
public void onSuccess(IMqttToken asyncActionToken) {
Log.d("Logger", "Connect MQTT");
Toast.makeText(MainActivity.this,"connected!!",Toast.LENGTH_LONG).show();
}
#Override
public void onFailure(IMqttToken asyncActionToken, Throwable exception) {
Log.d("Logger", "Connect Failed");
Toast.makeText(MainActivity.this,"connection failed!!",Toast.LENGTH_LONG).show();
}
});
} catch (MqttException e) {
e.printStackTrace();
Log.d("Logger", "error"+e);
}}
state = true;
}else{
turnon.setText("Turn On");
state = false;
// bluetoothAdapter.cancelDiscovery();
}
}
});
changeLayout.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
startActivity(new Intent(MainActivity.this,MqttActivity.class));
}
});
}
public void stateCheck(){
if (state){
if (bluetoothAdapter!=null & bluetoothAdapter.isEnabled()) {
if(checkCoarsePermission()){
Log.d("Logger", "Discover");
bluetoothAdapter.startDiscovery();
}
}
}
// else {
// Log.d("Logger", "Cancel");
// bluetoothAdapter.cancelDiscovery();
// }
}
private boolean checkCoarsePermission(){
if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION)
!= PackageManager.PERMISSION_GRANTED){
ActivityCompat.requestPermissions(this, new String[] {Manifest.permission.ACCESS_COARSE_LOCATION},
REQUEST_ACCESS_COARSE_LOCATION);
return false;
}else {
return true;
}
}
#Override
protected void onResume() {
super.onResume();
registerReceiver(devicesFoundReceiver, new IntentFilter(BluetoothDevice.ACTION_FOUND));
registerReceiver(devicesFoundReceiver, new IntentFilter(BluetoothAdapter.ACTION_DISCOVERY_STARTED));
registerReceiver(devicesFoundReceiver, new IntentFilter(BluetoothAdapter.ACTION_DISCOVERY_FINISHED));
}
#Override
protected void onPause() {
super.onPause();
unregisterReceiver(devicesFoundReceiver);
}
private final BroadcastReceiver devicesFoundReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
String action= intent.getAction();
if(BluetoothDevice.ACTION_FOUND.equals(action)){
BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
int rssi = intent.getShortExtra(BluetoothDevice.EXTRA_RSSI,Short.MIN_VALUE);
String RSSI = String.valueOf(rssi);
Toast.makeText(context.getApplicationContext(),"rssi "+RSSI+" "+device.getAddress(),Toast.LENGTH_SHORT).show();
Log.d("Logger", "Recive data "+device.getAddress());
if(mqtt_server!=null||mqtt_id!=null||mqtt_port!=null){
try {
Log.d("Logger", "Sending data");
String payload = "rssi:"+RSSI+"mac:"+device.getAddress();
client.publish("test",payload.getBytes(),0,false);
} catch ( MqttException e) {
e.printStackTrace();
Log.d("Logger", "Error Sending "+e);
}}
}else if(BluetoothAdapter.ACTION_DISCOVERY_FINISHED.equals(action)){
}else if(BluetoothAdapter.ACTION_DISCOVERY_STARTED.equals(action)){
}
}
};
#Override
public void onRequestPermissionsResult(int requestCode, #NonNull String[] permissions, #NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
switch (requestCode){
case REQUEST_ACCESS_COARSE_LOCATION:
if(grantResults.length>0 && grantResults[0] == PackageManager.PERMISSION_GRANTED){
Toast.makeText(this,"ALLOWED", Toast.LENGTH_SHORT).show();
}else {
Toast.makeText(this,"Forbidden",Toast.LENGTH_SHORT).show();
} break;
}
}
}
App Flow:
Insert MQTT server, port, id, topic.
Turn on the proccess.
Android scan BLE/Beacon
Android sending MAC/RSSI to MQTT
I hope someone can help to guide me, on how to make the application run in the background?
I'm a beginner, and I don't understand how to implement background service in my application. Please help me!
You need to implement a foreground service that will handle your ble scanning and MQTT logic.
See this article with an overview of how to do it. Depending on your build/target SDK, the implementation will vary.
This is such a basic issue that I am not sure what I could possibly be doing wrong. Sinch is not starting for me and I don't know why. I don't have enough experience with Sinch to diagnose why a basic command is not doing what it is supposed to do. Here's what I have:
I am trying to start and making the call from the Calling.java class. The code is as follows:
import android.content.Intent;
import android.os.Bundle;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.TextView;
import com.sinch.android.rtc.calling.Call;
import com.squareup.picasso.Picasso;
import static android.content.Intent.FLAG_ACTIVITY_PREVIOUS_IS_TOP;
public class Calling extends CallActivity {
private String calleeID;
private TextView serviceName;
Bundle callDetails;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_calling);
callDetails = getIntent().getBundleExtra("callDetails");
//Setup end button
Button endCallButton = findViewById(R.id.endcall);
endCallButton.setOnClickListener(v -> endCall());
}
private void endCall() {
if (getSinchServiceInterface() != null) {
getSinchServiceInterface().stopClient();
}
finishActivity(FLAG_ACTIVITY_PREVIOUS_IS_TOP);
finish();
}
// invoked when the connection with SinchServer is established
#Override
protected void onServiceConnected() {
//Setup Calling Screen
ImageView avatar = findViewById(R.id.dialingAvatar);
Picasso.get().load(callDetails.getString("Logo")).into(avatar);
TextView midScreenName = findViewById(R.id.memberName);
midScreenName.setText(callDetails.getString("Name"));
serviceName = findViewById(R.id.serviceName);
serviceName.setText(callDetails.getString("Service"));
TextView ratings = findViewById(R.id.rating);
ratings.setText(callDetails.getString("Rating") + " ★");
//Get CallerID and CalleeID
calleeID = callDetails.getString("CalleeID");
//Start sinch Service
if(!getSinchServiceInterface().isStarted()){
getSinchServiceInterface().startClient(callDetails.getString("CallerID"));
Call call = getSinchServiceInterface().callUserVideo(calleeID);
Intent callServiceScreen = new Intent(this, ServiceCallActivity.class);
callDetails.putString(SinchService.CALL_ID, call.getCallId());
callServiceScreen.putExtra("Call Details", callDetails);
startActivity(callServiceScreen);
}
}
#Override
public void onDestroy() {
if (getSinchServiceInterface() != null) {
getSinchServiceInterface().stopClient();
}
super.onDestroy();
}
}
I am coming to Calling.java from Precall.java the code for that is:
import android.content.Intent;
import android.os.AsyncTask;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.TextView;
import androidx.appcompat.app.AppCompatActivity;
import com.sinch.android.rtc.SinchError;
import com.squareup.picasso.Picasso;
import org.json.JSONObject;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
public class precall extends CallActivity implements SinchService.StartFailedListener {
private Bundle memberDetails;
private String url;
private Button cancel;
private Button call;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_precall);
//url
url = apiCallPoints.userInfo;
//Set Member Text
memberDetails = getIntent().getBundleExtra("Member");
//Populate screen
ImageView avatar = findViewById(R.id.avatar);
Picasso.get().load(memberDetails.getString("Logo")).into(avatar);
TextView memberName = findViewById(R.id.membername);
memberName.setText(memberDetails.getString("Name"));
TextView rating = findViewById(R.id.rating);
rating.setText(memberDetails.getString("Rating") + " ★");
TextView serviceName = findViewById(R.id.servicename);
serviceName.setText(memberDetails.getString("Service"));
TextView overview = findViewById(R.id.overview);
overview.setText(memberDetails.getString("Overview"));
//Add button clicks
cancel = findViewById(R.id.cancel_button);
cancel.setOnClickListener(view -> finish());
cancel.setEnabled(false);
call = findViewById(R.id.yes_button);
call.setOnClickListener(view -> {
goToCalling();
});
call.setEnabled(false);
setHomeBar();
}
//this method is invoked when the connection is established with the SinchService
#Override
protected void onServiceConnected() {
call.setEnabled(true);
cancel.setEnabled(true);
getSinchServiceInterface().setStartListener(this);
}
#Override
protected void onPause() {
super.onPause();
}
#Override
public void onStartFailed(SinchError error) {
}
//Invoked when just after the service is connected with Sinch
#Override
public void onStarted() {
}
private void goToCalling() {
//Async search
CallBackendSync callBackendSync = new CallBackendSync();
Object [] params = {url, memberDetails};
callBackendSync.execute(params);
}
private void setHomeBar() {
final Button home = findViewById(R.id.home_button);
home.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
// Code here executes on main thread after user presses button
startActivity(new Intent(precall.this, SecondActivity.class));
}
});
final Button favourites = findViewById(R.id.star_button);
favourites.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
// Code here executes on main thread after user presses button
startActivity(new Intent(precall.this, Favourite_Page.class));
}
});
final Button profile_page = findViewById(R.id.person_button);
profile_page.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
// Code here executes on main thread after user presses button
startActivity(new Intent(getApplicationContext(), Profile.class));
}
});
final Button notifications = findViewById(R.id.notification_button);
notifications.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
// Code here executes on main thread after user presses button
startActivity(new Intent(precall.this, Notification_Page.class));
}
});
final Button service = findViewById(R.id.service_button);
service.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
// Code here executes on main thread after user presses button
startActivity(new Intent(precall.this, services.class));
}
});
}
class CallBackendSync extends AsyncTask {
OkHttpClient client = new OkHttpClient();
#Override
protected Object doInBackground(Object [] objects) {
String url = (String) objects[0];
Bundle memberDetails = (Bundle) objects[1];
//Get access token from shared preference
isLoggedIn loggedIn = new isLoggedIn(getApplicationContext());
String token = loggedIn.getToken();
if(token != null){
//Create request
Request request = new Request.Builder()
.url(url)
.addHeader("Authorization", "Bearer " + token)
.addHeader("Accept", "application/json")
.build();
try {
Response response = client.newCall(request).execute();
JSONObject results = new JSONObject(response.body().string());
String UserID = results.getString("UserId");
memberDetails.putString("CallerID", UserID);
Intent callIntent = new Intent(precall.this, Calling.class);
callIntent.putExtra("callDetails", memberDetails);
startActivity(callIntent);
return results;
}catch (Exception e){
e.printStackTrace();
}
} else {
startActivity(new Intent(precall.this, Login_page.class));
}
return null;
}
protected void onPostExecute(String s){
super.onPostExecute(s);
}
}
}
The failure is happening in SinchService.java
import com.sinch.android.rtc.AudioController;
import com.sinch.android.rtc.ClientRegistration;
import com.sinch.android.rtc.Sinch;
import com.sinch.android.rtc.SinchClient;
import com.sinch.android.rtc.SinchClientListener;
import com.sinch.android.rtc.SinchError;
import com.sinch.android.rtc.video.VideoController;
import com.sinch.android.rtc.calling.Call;
import com.sinch.android.rtc.calling.CallClient;
import com.sinch.android.rtc.calling.CallClientListener;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.os.Binder;
import android.os.IBinder;
import android.util.Log;
public class SinchService extends Service {
private static final String APP_KEY = "is correct";
private static final String APP_SECRET = "is correct";
//private static final String ENVIRONMENT = "clientapi.sinch.com";
private static final String ENVIRONMENT = "sandbox.sinch.com";
public static final String CALL_ID = "CALL_ID";
static final String TAG = SinchService.class.getSimpleName();
private SinchServiceInterface mSinchServiceInterface = new SinchServiceInterface();
private SinchClient mSinchClient = null;
private String mUserId = "";
private StartFailedListener mListener;
#Override
public void onCreate() {
super.onCreate();
}
#Override
public void onDestroy() {
if(mSinchClient != null){
mSinchClient.terminate();
}
super.onDestroy();
}
private void start(String userName) {
mUserId = userName;
mSinchClient = Sinch.getSinchClientBuilder().context(getApplicationContext())
.applicationKey(APP_KEY)
.applicationSecret(APP_SECRET)
.environmentHost(ENVIRONMENT)
.userId(userName)
.enableVideoCalls(true)
.build();
mSinchClient.setSupportCalling(true);
mSinchClient.startListeningOnActiveConnection();
mSinchClient.addSinchClientListener(new MySinchClientListener());
mSinchClient.getCallClient().addCallClientListener(new SinchCallClientListener());
mSinchClient.checkManifest();
mSinchClient.start();
System.out.println("Is started: " + mSinchClient.isStarted());
}
private void stop() {
if(mSinchClient != null){
mSinchClient.terminate();
}
}
private boolean isStarted() {
if(mSinchClient != null){
return mSinchClient.isStarted();
} else {
return false;
}
}
#Override
public IBinder onBind(Intent intent) {
return mSinchServiceInterface;
}
public class SinchServiceInterface extends Binder {
public Call callUserVideo(String userId) {
return mSinchClient.getCallClient().callUserVideo(userId);
}
public String getUserName() {
return mUserId;
}
public boolean isStarted() {
return SinchService.this.isStarted();
}
public void startClient(String userName) {
start(userName);
}
public void stopClient() {
stop();
}
public void setStartListener(StartFailedListener listener) {
mListener = listener;
}
public Call getCall(String callId) {
return mSinchClient.getCallClient().getCall(callId);
}
public VideoController getVideoController() {
return mSinchClient.getVideoController();
}
public AudioController getAudioController() {
return mSinchClient.getAudioController();
}
}
public interface StartFailedListener {
void onStartFailed(SinchError error);
void onStarted();
}
private class MySinchClientListener implements SinchClientListener {
#Override
public void onClientFailed(SinchClient client, SinchError error) {
if (mListener != null) {
mListener.onStartFailed(error);
}
mSinchClient.terminate();
mSinchClient = null;
}
#Override
public void onClientStarted(SinchClient client) {
Log.d(TAG, "SinchClient started");
if (mListener != null) {
mListener.onStarted();
}
}
#Override
public void onClientStopped(SinchClient client) {
Log.d(TAG, "SinchClient stopped");
}
#Override
public void onLogMessage(int level, String area, String message) {
switch (level) {
case Log.DEBUG:
Log.d(area, message);
break;
case Log.ERROR:
Log.e(area, message);
break;
case Log.INFO:
Log.i(area, message);
break;
case Log.VERBOSE:
Log.v(area, message);
break;
case Log.WARN:
Log.w(area, message);
break;
}
}
#Override
public void onRegistrationCredentialsRequired(SinchClient client,
ClientRegistration clientRegistration) {
}
}
private class SinchCallClientListener implements CallClientListener {
#Override
public void onIncomingCall(CallClient callClient, Call call) {
Log.d(TAG, "Incoming call");
Intent intent = new Intent(SinchService.this, Calling.class);
intent.putExtra(CALL_ID, call.getCallId());
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
SinchService.this.startActivity(intent);
}
}
}
And the base activity is CallActivity.java
import android.content.Intent;
import android.os.Bundle;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.TextView;
import com.sinch.android.rtc.calling.Call;
import com.squareup.picasso.Picasso;
import static android.content.Intent.FLAG_ACTIVITY_PREVIOUS_IS_TOP;
public class Calling extends CallActivity {
private String calleeID;
private TextView serviceName;
Bundle callDetails;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_calling);
callDetails = getIntent().getBundleExtra("callDetails");
//Setup end button
Button endCallButton = findViewById(R.id.endcall);
endCallButton.setOnClickListener(v -> endCall());
}
private void endCall() {
if (getSinchServiceInterface() != null) {
getSinchServiceInterface().stopClient();
}
finishActivity(FLAG_ACTIVITY_PREVIOUS_IS_TOP);
finish();
}
// invoked when the connection with SinchServer is established
#Override
protected void onServiceConnected() {
//Setup Calling Screen
ImageView avatar = findViewById(R.id.dialingAvatar);
Picasso.get().load(callDetails.getString("Logo")).into(avatar);
TextView midScreenName = findViewById(R.id.memberName);
midScreenName.setText(callDetails.getString("Name"));
serviceName = findViewById(R.id.serviceName);
serviceName.setText(callDetails.getString("Service"));
TextView ratings = findViewById(R.id.rating);
ratings.setText(callDetails.getString("Rating") + " ★");
//Get CallerID and CalleeID
calleeID = callDetails.getString("CalleeID");
//Start sinch Service
if(!getSinchServiceInterface().isStarted()){
getSinchServiceInterface().startClient(callDetails.getString("CallerID"));
Call call = getSinchServiceInterface().callUserVideo(calleeID);
Intent callServiceScreen = new Intent(this, ServiceCallActivity.class);
callDetails.putString(SinchService.CALL_ID, call.getCallId());
callServiceScreen.putExtra("Call Details", callDetails);
startActivity(callServiceScreen);
}
}
#Override
public void onDestroy() {
if (getSinchServiceInterface() != null) {
getSinchServiceInterface().stopClient();
}
super.onDestroy();
}
}
I have been banging my head against this but I cannot figure out what's wrong. I sure it's something stupid and obvious that I can't see because I am too close to the code. But any ideas you guys have would be very helpful!
It looks like you are not waiting for it to start before you try to make a call. We recommend to start the service when the app starts. If you dont do that you need to wait or the onStarted event in the service for fire
I am working on a project that has a class for GPS
I know that to call a class I have to use instant of the class
something like this
GPS insgps = new GPS();
if (insgps .canGetLocation())
{/* Do Something */}
but when I try to use this code I get error
GPS has private access in 'com.myapp.locationapp.app.GPS'
I dont know why and how to fix that?
here is the class I use
import android.Manifest;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.os.Bundle;
import android.provider.Settings;
import android.support.v4.app.ActivityCompat;
public final class GPS implements LocationListener, ActivityCompat.OnRequestPermissionsResultCallback {
private static GPS _instance = new GPS();
private static Activity _activity;
private static boolean _isGPSEnabled = false;
private static boolean _isNetworkEnabled = false;
private static boolean _canGetLocation = false;
private static boolean _isPermissionEnabled = false;
private Location _location;
private double _latitude;
private double _longitude;
private static final long MIN_DISTANCE_CHANGE_FOR_UPDATES = 1; // 10 meters
private static final long MIN_TIME_BW_UPDATES = 1; // 1 minute
private static LocationManager _locationManager;
private LocationPermissionResponseListener _locationPermissionListener;
public static final int LOCATION_REQUEST_CODE = 200;
private GPS() {}
public static GPS sharedInstance(Activity activity) {
_activity = activity;
_locationManager = (LocationManager) _activity.getSystemService(Context.LOCATION_SERVICE);
_isGPSEnabled = _locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER);
_isNetworkEnabled = _locationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER);
if (!_isGPSEnabled && !_isNetworkEnabled) {
_canGetLocation = false;
} else {
_canGetLocation = true;
}
if (ActivityCompat.checkSelfPermission(_activity, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
_isPermissionEnabled = false;
} else {
_isPermissionEnabled = true;
}
return _instance;
}
public Location getLastKnownLocation() {
if (ActivityCompat.checkSelfPermission(_activity, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
_isPermissionEnabled = false;
} else {
if (_canGetLocation) {
if (_isNetworkEnabled) {
_locationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, MIN_TIME_BW_UPDATES, MIN_DISTANCE_CHANGE_FOR_UPDATES, this);
if (_locationManager != null) {
_location = _locationManager.getLastKnownLocation(LocationManager.NETWORK_PROVIDER);
if (_location != null) {
_latitude = _location.getLatitude();
_longitude = _location.getLongitude();
}
}
}
if (_isGPSEnabled) {
if (_location == null) {
_locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, MIN_TIME_BW_UPDATES, MIN_DISTANCE_CHANGE_FOR_UPDATES, this);
if (_locationManager != null) {
_location = _locationManager.getLastKnownLocation(LocationManager.GPS_PROVIDER);
if (_location != null) {
_latitude = _location.getLatitude();
_longitude = _location.getLongitude();
}
}
}
}
}
}
return _location;
}
public void stopUsingGPS() {
if (_locationManager != null) {
if (ActivityCompat.checkSelfPermission(_activity, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED) {
_locationManager.removeUpdates(GPS.this);
}
}
}
public double getLatitude() {
if (_locationManager != null) {
_latitude = _location.getLatitude();
}
return _latitude;
}
public double getLongitude() {
if (_locationManager != null) {
_longitude = _location.getLongitude();
}
return _longitude;
}
public boolean canGetLocation() {
return _canGetLocation;
}
public boolean isPermissionEnabled() {
return _isPermissionEnabled;
}
public void showSettingsAlert() {
AlertDialog.Builder alertDialog = new AlertDialog.Builder(_activity);
alertDialog.setTitle("GPS Settings");
alertDialog.setMessage("GPS is not enabled. Do you want to go to settings menu ?");
alertDialog.setPositiveButton("Settings",
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
Intent intent = new Intent(
Settings.ACTION_LOCATION_SOURCE_SETTINGS);
_activity.startActivity(intent);
}
});
alertDialog.setNegativeButton("Cancel",
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
dialog.cancel();
}
});
alertDialog.show();
}
public void requestLocationPermission(LocationPermissionResponseListener listener) {
_locationPermissionListener = listener;
ActivityCompat.requestPermissions(_activity, new String[]{Manifest.permission.ACCESS_FINE_LOCATION}, LOCATION_REQUEST_CODE);
}
#Override
public void onLocationChanged(Location location) {
this._location = location;
}
#Override
public void onStatusChanged(String provider, int status, Bundle extras) {
}
#Override
public void onProviderDisabled(String provider) {
}
#Override
public void onProviderEnabled(String provider) {
}
#Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
switch (requestCode) {
case GPS.LOCATION_REQUEST_CODE: {
_locationPermissionListener.onResponse(grantResults[0] == PackageManager.PERMISSION_GRANTED);
}
}
}
public static interface LocationPermissionResponseListener {
public void onResponse(Boolean permissionGranted);
}
}
From the looks of it, GPS is a singleton class.
There is a private static field _instance of type GPS and there is also a static method called sharedInstance(Activity). These are features of a singleton.
A singleton class basically means that there will only be one instance of the class at runtime. In this case, it is _instance. It is not allowed to create other instances of GPS. That is why the constructor is marked private, making you unable to access it.
Because if this, you should not create a new instance of GPS. You should instead access the only one instance by calling the method sharedInstance.
GPS insgps = GPS.sharedInstance(anActivity);
If you're writing this code in a subclass of Activity, replace anActivity above with this. If you're writing this code in some other class, get an instance of Activity and replace anActivity with it.
Your constructor is private:
private GPS() {}
This would need to be public
public GPS() {}
Saying that however, it looks like your class is using a shared instance function so you'd likely want:
GPS insgps = GPS.sharedInstance([myactivitycontext]);
package fr.pchab.AndroidRTC;
import android.app.Activity;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.res.Configuration;
import android.graphics.Point;
import android.os.Bundle;
import android.view.Window;
import android.widget.Toast;
import org.json.JSONException;
import org.webrtc.MediaStream;
import org.webrtc.PeerConnectionFactory;
import org.webrtc.VideoRenderer;
import java.util.List;
public class RTCActivity extends Activity implements WebRtcClient.RTCListener{
private final static int VIDEO_CALL_SENT = 666;
private VideoStreamsView vsv;
private WebRtcClient client;
private String mSocketAddress;
private String callerId;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
mSocketAddress = "http://" + getResources().getString(R.string.host);
mSocketAddress += (":"+getResources().getString(R.string.port)+"/");
PeerConnectionFactory.initializeAndroidGlobals(this);
// Camera display view
Point displaySize = new Point();
getWindowManager().getDefaultDisplay().getSize(displaySize);
vsv = new VideoStreamsView(this, displaySize);
client = new WebRtcClient(this, mSocketAddress);
final Intent intent = getIntent();
final String action = intent.getAction();
if (Intent.ACTION_VIEW.equals(action)) {
final List<String> segments = intent.getData().getPathSegments();
callerId = segments.get(0);
}
}
public void onConfigurationChanged(Configuration newConfig)
{
super.onConfigurationChanged(newConfig);
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
}
#Override
public void onPause() {
super.onPause();
vsv.onPause();
}
#Override
public void onResume() {
super.onResume();
vsv.onResume();
}
#Override
public void onCallReady(String callId) {
if(callerId != null) {
try {
answer(callerId);
} catch (JSONException e) {
e.printStackTrace();
}
} else {
call(callId);
}
}
public void answer(String callerId) throws JSONException {
client.sendMessage(callerId, "init", null);
startCam();
}
public void call(String callId) {
Intent msg = new Intent(Intent.ACTION_SEND);
msg.putExtra(Intent.EXTRA_TEXT, mSocketAddress + callId);
msg.setType("text/plain");
startActivityForResult(Intent.createChooser(msg, "Call someone :"), VIDEO_CALL_SENT);
}
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if(requestCode == VIDEO_CALL_SENT) {
startCam();
}
}
public void startCam() {
setContentView(vsv);
// Camera settings
client.setCamera("front", "640", "480");
client.start("android_test", true);
}
#Override
public void onStatusChanged(final String newStatus) {
runOnUiThread(new Runnable() {
#Override
public void run() {
Toast.makeText(getApplicationContext(), newStatus, Toast.LENGTH_SHORT).show();
}
});
}
#Override
public void onLocalStream(MediaStream localStream) {
localStream.videoTracks.get(0).addRenderer(new VideoRenderer(new VideoCallbacks(vsv, 0)));
}
#Override
public void onAddRemoteStream(MediaStream remoteStream, int endPoint) {
remoteStream.videoTracks.get(0).addRenderer(new VideoRenderer(new VideoCallbacks(vsv, endPoint)));
vsv.shouldDraw[endPoint] = true;
}
#Override
public void onRemoveRemoteStream(MediaStream remoteStream, int endPoint) {
remoteStream.videoTracks.get(0).dispose();
vsv.shouldDraw[endPoint] = false;
}
// Implementation detail: bridge the VideoRenderer.Callbacks interface to the
// VideoStreamsView implementation.
private class VideoCallbacks implements VideoRenderer.Callbacks {
private final VideoStreamsView view;
private final int stream;
public VideoCallbacks(VideoStreamsView view, int stream) {
this.view = view;
this.stream = stream;
}
#Override
public void setSize(final int width, final int height) {
view.queueEvent(new Runnable() {
public void run() {
view.setSize(stream, width, height);
}
});
}
#Override
public void renderFrame(VideoRenderer.I420Frame frame) {
view.queueFrame(stream, frame);
}
}
}
I want to use webrtc in my android app.
https://github.com/pchab/AndroidRTC
after import project from this site video calling or voice calling or chatting is not working.
how can i use websocket library send data and receive for video or voice chatting?
can we interface WEBRTC API with webview.
or how can we do voice chatting and display view in chrom in any control of android.
now i am using webrtc in a native app but it is not working.
if u have any other code or project for webrtc in android than send me link.
I want to use webrtc in my android app.
https://github.com/pchab/AndroidRTC
after import project from this site video calling or voice calling or chatting is not working.
how can i use websocket library send data and receive for video or voice chatting?
can we interface WEBRTC API with webview.
To get this application working you need a server-side which will do the signaling job. If you don't want to develope yourself you can install the node.js server with its desktop client from the same project (just follow How To instructions from AndroidRTC and the Install instructions from ProjectRTC):
https://github.com/pchab/ProjectRTC
Hope it helps.