I'm trying to create an App that sends a data text via USB Serial from a Android to a computer and receive. I'm using a Sample from Android called AdbTestActivity to try to connect with my device. I am able to find the device but when I try to open the device using UsbDeviceConnection connection = mManager.openDevice(device); It returned me response 'null'
AdbTestActivity.java
package com.android.adb;
import android.app.Activity;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.graphics.Rect;
import android.hardware.usb.UsbDevice;
import android.hardware.usb.UsbDeviceConnection;
import android.hardware.usb.UsbInterface;
import android.hardware.usb.UsbManager;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
import android.widget.TextView;
/* Main activity for the adb test program */
public class AdbTestActivity extends Activity {
private static final String TAG = "AdbTestActivity";
private TextView mLog;
private UsbManager mManager;
private UsbDevice mDevice;
private UsbDeviceConnection mDeviceConnection;
private UsbInterface mInterface;
private AdbDevice mAdbDevice;
private static final int MESSAGE_LOG = 1;
private static final int MESSAGE_DEVICE_ONLINE = 2;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.adb);
mLog = (TextView)findViewById(R.id.log);
mManager = (UsbManager)getSystemService(Context.USB_SERVICE);
// check for existing devices
for (UsbDevice device : mManager.getDeviceList().values()) {
UsbInterface intf = findAdbInterface(device);
if (setAdbInterface(device, intf)) {
break;
}
}
// listen for new devices
IntentFilter filter = new IntentFilter();
filter.addAction(UsbManager.ACTION_USB_DEVICE_ATTACHED);
filter.addAction(UsbManager.ACTION_USB_DEVICE_DETACHED);
registerReceiver(mUsbReceiver, filter);
}
#Override
public void onDestroy() {
unregisterReceiver(mUsbReceiver);
setAdbInterface(null, null);
super.onDestroy();
}
public void log(String s) {
Message m = Message.obtain(mHandler, MESSAGE_LOG);
m.obj = s;
mHandler.sendMessage(m);
}
private void appendLog(String text) {
Rect r = new Rect();
mLog.getDrawingRect(r);
int maxLines = r.height() / mLog.getLineHeight() - 1;
text = mLog.getText() + "\n" + text;
// see how many lines we have
int index = text.lastIndexOf('\n');
int count = 0;
while (index > 0 && count <= maxLines) {
count++;
index = text.lastIndexOf('\n', index - 1);
}
// truncate to maxLines
if (index > 0) {
text = text.substring(index + 1);
}
mLog.setText(text);
}
public void deviceOnline(AdbDevice device) {
Message m = Message.obtain(mHandler, MESSAGE_DEVICE_ONLINE);
m.obj = device;
mHandler.sendMessage(m);
}
private void handleDeviceOnline(AdbDevice device) {
log("device online: " + device.getSerial());
device.openSocket("shell:exec logcat");
}
// Sets the current USB device and interface
private boolean setAdbInterface(UsbDevice device, UsbInterface intf) {
if (mDeviceConnection != null) {
if (mInterface != null) {
mDeviceConnection.releaseInterface(mInterface);
mInterface = null;
}
mDeviceConnection.close();
mDevice = null;
mDeviceConnection = null;
}
if (device != null && intf != null) {
//Here returns null connection
UsbDeviceConnection connection = mManager.openDevice(device);
if (connection != null) {
log("open succeeded");
if (connection.claimInterface(intf, false)) {
log("claim interface succeeded");
mDevice = device;
mDeviceConnection = connection;
mInterface = intf;
mAdbDevice = new AdbDevice(this, mDeviceConnection, intf);
log("call start");
mAdbDevice.start();
return true;
} else {
log("claim interface failed");
connection.close();
}
} else {
log("open failed");
}
}
if (mDeviceConnection == null && mAdbDevice != null) {
mAdbDevice.stop();
mAdbDevice = null;
}
return false;
}
// searches for an adb interface on the given USB device
static private UsbInterface findAdbInterface(UsbDevice device) {
Log.d(TAG, "findAdbInterface " + device);
int count = device.getInterfaceCount();
for (int i = 0; i < count; i++) {
UsbInterface intf = device.getInterface(i);
if (intf.getInterfaceClass() == 255 && intf.getInterfaceSubclass() == 66 &&
intf.getInterfaceProtocol() == 1) {
return intf;
}
if (intf.getInterfaceClass() == 2 && intf.getInterfaceSubclass() == 2 &&
intf.getInterfaceProtocol() == 1) {
return intf;
}
}
return null;
}
BroadcastReceiver mUsbReceiver = new BroadcastReceiver() {
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (UsbManager.ACTION_USB_DEVICE_ATTACHED.equals(action)) {
UsbDevice device = (UsbDevice)intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
UsbInterface intf = findAdbInterface(device);
if (intf != null) {
log("Found adb interface " + intf);
setAdbInterface(device, intf);
}
} else if (UsbManager.ACTION_USB_DEVICE_DETACHED.equals(action)) {
UsbDevice device = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
String deviceName = device.getDeviceName();
if (mDevice != null && mDevice.equals(deviceName)) {
log("adb interface removed");
setAdbInterface(null, null);
}
}
}
};
Handler mHandler = new Handler() {
#Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MESSAGE_LOG:
appendLog((String)msg.obj);
break;
case MESSAGE_DEVICE_ONLINE:
handleDeviceOnline((AdbDevice)msg.obj);
break;
}
}
};
}
I also tried to insert my vendor and the product in the device_filter.xml, but it didn't work.
<resources>
<usb-device class="255" subclass="66" protocol="0" />
<usb-device vendor-id="5401" product-id="32" />
</resources>
Manifest
...
<intent-filter>
<action android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED" />
</intent-filter>
<meta-data android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED"
android:resource="#xml/device_filter" />
You should ask before permission to open usb device, like that:
final String usbPermissionName = "com.android.example.USB_PERMISSION";
BroadcastReceiver usbPermissionReceiver = new BroadcastReceiver() {
public void onReceive(Context context, Intent intent) {
if (usbPermissionName.equals(intent.getAction())) {
UsbDevice device = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
if(intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false)) {
synchronized (this) {
mManager.openDevice(device);
}
}
}
}
};
registerReceiver(usbPermissionReceiver, new IntentFilter(usbPermissionName));
PendingIntent permissionPendingIntent = PendingIntent.getBroadcast(instance, 0, new Intent(usbPermissionName), 0);
usbManager.requestPermission(usbDevice, permissionPendingIntent);
Related
My goal is to record audio from a Bluetooth Microphone while in Background Mode.
Expected result:
The A2DPService records audio while in the background mode.
Actual result:
The A2DPService can not record audio after in the background mode after 2 minutes. The audio file will be empty.
How to reproduce the issue:
use the #ReactMethod connectDevice(address: string).
went to background mode, wait until 2 minutes.
use the #ReactMethod startBluetoothSco() to record the audio
use the #ReactMethod stopBluetoothSco() to stop the recorder.
play the audio file. the audio file will not run as there is no input from the Bluetooth Microphone. Getting the app to Foreground Mode fixed the issue, but the app is intended to be used most of the time while in Background Mode.
A2DPService.java
package com.satpam.RNBluetoothNM.A2DP;
import android.bluetooth.BluetoothA2dp;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothProfile;
import android.content.Context;
import android.media.AudioManager;
import com.facebook.react.bridge.Promise;
import com.facebook.react.bridge.ReactApplicationContext;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class A2DPService {
private BluetoothDevice mBluetoothDevice;
private BluetoothA2dp mBluetoothA2dp;
private AudioManager mAudioManager;
public A2DPService(ReactApplicationContext reactContext) {
mAudioManager = (AudioManager) reactContext.getSystemService(Context.AUDIO_SERVICE);
BluetoothAdapter
.getDefaultAdapter()
.getProfileProxy(reactContext, new BluetoothProfile.ServiceListener() {
#Override
public void onServiceConnected(int i, BluetoothProfile bluetoothProfile) {
if (i == BluetoothProfile.A2DP) {
mBluetoothA2dp = (BluetoothA2dp) bluetoothProfile;
}
}
#Override
public void onServiceDisconnected(int i) {
if (i == BluetoothProfile.A2DP) {
mBluetoothA2dp = null;
}
}
}, BluetoothProfile.A2DP);
}
public boolean createBond(BluetoothDevice bluetoothDevice) {
mBluetoothDevice = bluetoothDevice;
return mBluetoothDevice.createBond();
}
public boolean connectA2DP(BluetoothDevice bluetoothDevice) {
if (mBluetoothDevice == null) {
return false;
} else {
try {
Method method = BluetoothA2dp.class.getMethod("connect", BluetoothDevice.class);
method.invoke(mBluetoothA2dp, bluetoothDevice);
return true;
} catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
return false;
}
}
}
public int getConnectedState() {
return mBluetoothA2dp.getConnectionState(mBluetoothDevice);
}
public boolean startBluetoothSco() {
if (getConnectedState() != BluetoothProfile.STATE_CONNECTED) {
return false;
} else {
mAudioManager.setBluetoothScoOn(true);
mAudioManager.startBluetoothSco();
return true;
}
}
public boolean stopBluetoothSco() {
if (getConnectedState() != BluetoothProfile.STATE_CONNECTED) {
return false;
} else {
mAudioManager.setBluetoothScoOn(false);
mAudioManager.stopBluetoothSco();
return true;
}
}
}
RNBluetoothNM.java
package com.satpam.RNBluetoothNM;
import android.app.Activity;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothProfile;
import android.content.Intent;
import android.util.Log;
import androidx.annotation.NonNull;
import com.facebook.react.bridge.ActivityEventListener;
import com.facebook.react.bridge.Arguments;
import com.facebook.react.bridge.BaseActivityEventListener;
import com.facebook.react.bridge.Promise;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;
import com.facebook.react.bridge.WritableArray;
import com.facebook.react.bridge.WritableMap;
import com.facebook.react.modules.core.DeviceEventManagerModule;
import com.satpam.RNBluetoothNM.A2DP.A2DPService;
import com.satpam.RNBluetoothNM.SPP.SPPService;
import java.util.Set;
public class RNBluetoothNMPackage extends ReactContextBaseJavaModule {
#NonNull
#Override
public String getName() {
return "RNBluetoothNM";
}
private static final int getBondedDeviceRequestCode = 0;
private Promise mPromise;
private final ActivityEventListener activityListener = new BaseActivityEventListener() {
#Override
public void onActivityResult(Activity activity, int requestCode, int resultCode, Intent data) {
super.onActivityResult(activity, requestCode, resultCode, data);
if (requestCode == getBondedDeviceRequestCode && resultCode == activity.RESULT_OK) {
getBondedDevices(mPromise);
mPromise = null;
}
}
};
public RNBluetoothNMPackage(ReactApplicationContext reactContext) {
super(reactContext);
// Add a listener for `onActivityResult`
reactContext.addActivityEventListener(activityListener);
}
private void _requestEnableBluetooth(Promise promise) {
Intent enableAdapter = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
Activity activity = getCurrentActivity();
// Store the promise to resolve/reject when onActivityResult returns value;
mPromise = promise;
activity.startActivityForResult(enableAdapter, getBondedDeviceRequestCode);
}
#ReactMethod
public void getBondedDevices(final Promise promise) {
BluetoothAdapter bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
if (bluetoothAdapter == null) {
promise.reject("getBondedDevices", "BluetoothAdapter is not supported");
} else {
if (!bluetoothAdapter.isEnabled()) {
_requestEnableBluetooth(promise);
}
Set<BluetoothDevice> devices = bluetoothAdapter.getBondedDevices();
if (devices.size() > 0) {
WritableArray array = Arguments.createArray();
for (BluetoothDevice device : devices) {
WritableMap writableMap = Arguments.createMap();
writableMap.putString("name", device.getName());
writableMap.putString("address", device.getAddress());
writableMap.putInt("bondState", device.getBondState());
array.pushMap(writableMap);
}
promise.resolve(array);
}
}
}
private A2DPService a2DPService;
private SPPService sppService;
#ReactMethod
public void connectDevice(String address, final Promise promise) {
BluetoothAdapter bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
if (bluetoothAdapter == null) {
promise.reject("connectDevice", "BluetoothAdapter is not supported");
} else {
if (!bluetoothAdapter.isEnabled()) {
_requestEnableBluetooth(promise);
}
BluetoothDevice bluetoothDevice = bluetoothAdapter.getRemoteDevice(address);
a2DPService = new A2DPService(getReactApplicationContext());
boolean a2dpIsBonded = a2DPService.createBond(bluetoothDevice);
if (a2dpIsBonded == false) {
promise.reject("A2DPService", "A2DP failed to bond");
} else {
boolean a2dpIsConnected = a2DPService.connectA2DP(bluetoothDevice);
if (a2dpIsConnected == false) {
promise.reject("A2DP", "A2DP failed to connect");
} else {
sppService = new SPPService(getReactApplicationContext());
boolean sppIsConnected = sppService.connectSPP(bluetoothDevice);
if (sppIsConnected == false) {
promise.reject("SPPService", "A2DP failed to bond");
} else {
promise.resolve(true);
Thread listener = new Thread(sppService);
listener.start();
}
}
}
}
}
#ReactMethod
public void startBluetoothSco(final Promise promise) {
if (a2DPService == null) {
promise.reject("startBluetoothSco", "A2DPService is null");
} else {
boolean isBluetoothScoStarted = a2DPService.startBluetoothSco();
if (isBluetoothScoStarted == false) {
promise.reject("startBluetoothSco", "A2DPService is not connected");
} else {
promise.resolve(true);
}
}
}
#ReactMethod
public void stopBluetoothSco(final Promise promise) {
if (a2DPService == null) {
promise.reject("stopBluetoothSco", "A2DPService is null");
} else {
boolean isBluetoothScoStopped = a2DPService.stopBluetoothSco();
if (isBluetoothScoStopped == false) {
promise.reject("stopBluetoothSco", "A2DPService is not connected");
} else {
promise.resolve(true);
}
}
}
}
You're using a background service. There's a limited amount of time those are allowed to run. Use a foreground service, which has no time limitations. This doesn't require your app to be in front, it just causes a notification to appear saying you're running.
The only things you need to change is to start the service with startForegroundService() and to call startForegound in the service itself.
I'm building video chat application using webrtc. when i started video call some error is coming. how to resolve that issue.
E/RoomRTCClient: Room connection error: HTTP POST to https://appr.tc/join/E52U8KA error: Unacceptable certificate: CN=COMODO RSA Certification Authority, O=COMODO CA Limited, L=Salford, ST=Greater Manchester, C=GB.
E/WSRTCClient: HTTP POST to https://appr.tc/join/E52U8KA error: Unacceptable certificate: CN=COMODO RSA Certification Authority, O=COMODO CA Limited, L=Salford, ST=Greater Manchester, C=GB
import android.app.Activity;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.net.Uri;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.ImageButton;
import android.widget.TextView;
import android.widget.Toast;
import androidx.annotation.UiThread;
import com.skillatwill.skillatwill.R;
import org.appspot.apprtc.AppRTCClient;
import org.appspot.apprtc.PeerConnectionClient;
import org.appspot.apprtc.WebSocketRTCClient;
import org.webrtc.Camera2Enumerator;
import org.webrtc.CameraEnumerator;
import org.webrtc.IceCandidate;
import org.webrtc.Logging;
import org.webrtc.RendererCommon.ScalingType;
import org.webrtc.SessionDescription;
import org.webrtc.StatsReport;
import org.webrtc.SurfaceViewRenderer;
import org.webrtc.VideoCapturer;
import org.webrtc.VideoFrame;
import org.webrtc.VideoRenderer;
import org.webrtc.VideoSink;
import java.security.SecureRandom;
import java.util.ArrayList;
import java.util.List;
/**
* Activity for peer connection call setup, call waiting
* and call view.
*/
public class CallActivity extends Activity implements AppRTCClient.SignalingEvents,
PeerConnectionClient.PeerConnectionEvents {
private static final String TAG = "CallActivity";
private static final String APPRTC_URL = "https://appr.tc";
private static final String UPPER_ALPHA_DIGITS = "ACEFGHJKLMNPQRUVWXY123456789";
// Peer connection statistics callback period in ms.
private static final int STAT_CALLBACK_PERIOD = 1000;
private final ProxyRenderer remoteProxyRenderer = new ProxyRenderer();
private final ProxyVideoSink localProxyVideoSink = new ProxyVideoSink();
private final List<VideoRenderer.Callbacks> remoteRenderers = new ArrayList<>();
private PeerConnectionClient peerConnectionClient = null;
private AppRTCClient appRtcClient;
private AppRTCClient.SignalingParameters signalingParameters;
private SurfaceViewRenderer pipRenderer;
private SurfaceViewRenderer fullscreenRenderer;
private Toast logToast;
private boolean activityRunning;
private AppRTCClient.RoomConnectionParameters roomConnectionParameters;
private PeerConnectionClient.PeerConnectionParameters peerConnectionParameters;
private boolean iceConnected;
private boolean isError;
private long callStartedTimeMs = 0;
private boolean micEnabled = true;
private boolean isSwappedFeeds;
// Control buttons for limited UI
private ImageButton disconnectButton;
private ImageButton cameraSwitchButton;
private ImageButton toggleMuteButton;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_call);
iceConnected = false;
signalingParameters = null;
// Create UI controls.
pipRenderer = findViewById(R.id.pip_video_view);
fullscreenRenderer = findViewById(R.id.fullscreen_video_view);
disconnectButton = findViewById(R.id.button_call_disconnect);
cameraSwitchButton = findViewById(R.id.button_call_switch_camera);
toggleMuteButton = findViewById(R.id.button_call_toggle_mic);
// Add buttons click events.
disconnectButton.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
onCallHangUp();
}
});
cameraSwitchButton.setOnClickListener(new View.OnClickListener() {
public void onClick(View view) {
onCameraSwitch();
}
});
toggleMuteButton.setOnClickListener(new View.OnClickListener() {
public void onClick(View view) {
boolean enabled = onToggleMic();
toggleMuteButton.setAlpha(enabled ? 1.0f : 0.3f);
}
});
// Swap feeds on pip view click.
pipRenderer.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
setSwappedFeeds(!isSwappedFeeds);
}
});
remoteRenderers.add(remoteProxyRenderer);
// Create peer connection client.
peerConnectionClient = new PeerConnectionClient();
// Create video renderers.
pipRenderer.init(peerConnectionClient.getRenderContext(), null);
pipRenderer.setScalingType(ScalingType.SCALE_ASPECT_FIT);
fullscreenRenderer.init(peerConnectionClient.getRenderContext(), null);
fullscreenRenderer.setScalingType(ScalingType.SCALE_ASPECT_FILL);
pipRenderer.setZOrderMediaOverlay(true);
pipRenderer.setEnableHardwareScaler(true /* enabled */);
fullscreenRenderer.setEnableHardwareScaler(true /* enabled */);
// Start with local feed in fullscreen and swap it to the pip when the call is connected.
setSwappedFeeds(true /* isSwappedFeeds */);
// Generate a random room ID with 7 uppercase letters and digits
String randomRoomID = randomString(7, UPPER_ALPHA_DIGITS);
// Show the random room ID so that another client can join from https://appr.tc
TextView roomIdTextView = findViewById(R.id.roomID);
roomIdTextView.setText(getString(R.string.room_id_caption) + randomRoomID);
Log.d(TAG, getString(R.string.room_id_caption) + randomRoomID);
// Connect video call to the random room
connectVideoCall(randomRoomID);
}
// Create a random string
private String randomString(int length, String characterSet) {
StringBuilder sb = new StringBuilder(); //consider using StringBuffer if needed
for (int i = 0; i < length; i++) {
int randomInt = new SecureRandom().nextInt(characterSet.length());
sb.append(characterSet.substring(randomInt, randomInt + 1));
}
return sb.toString();
}
// Join video call with randomly generated roomId
private void connectVideoCall(String roomId) {
Uri roomUri = Uri.parse(APPRTC_URL);
int videoWidth = 0;
int videoHeight = 0;
peerConnectionParameters =
new PeerConnectionClient.PeerConnectionParameters(true,
false,
false,
videoWidth,
videoHeight,
0,
Integer.parseInt(getString(R.string.pref_maxvideobitratevalue_default)),
getString(R.string.pref_videocodec_default),
true,
false,
Integer.parseInt(getString(R.string.pref_startaudiobitratevalue_default)),
getString(R.string.pref_audiocodec_default),
false,
false,
false,
false,
false,
false,
false,
false,
null);
// Create connection client. Use the standard WebSocketRTCClient.
// DirectRTCClient could be used for point-to-point connection
appRtcClient = new WebSocketRTCClient(this);
// Create connection parameters.
roomConnectionParameters =
new AppRTCClient.RoomConnectionParameters(
roomUri.toString(),
roomId,
false,
null);
peerConnectionClient.createPeerConnectionFactory(
getApplicationContext(), peerConnectionParameters, CallActivity.this);
startCall();
}
public void onCallHangUp() {
disconnect();
}
public void onCameraSwitch() {
if (peerConnectionClient != null) {
peerConnectionClient.switchCamera();
}
}
public boolean onToggleMic() {
if (peerConnectionClient != null) {
micEnabled = !micEnabled;
peerConnectionClient.setAudioEnabled(micEnabled);
}
return micEnabled;
}
private void startCall() {
if (appRtcClient == null) {
Log.e(TAG, "AppRTC client is not allocated for a call.");
return;
}
callStartedTimeMs = System.currentTimeMillis();
// Start room connection.
logAndToast(getString(R.string.connecting_to, roomConnectionParameters.roomUrl));
appRtcClient.connectToRoom(roomConnectionParameters);
}
#UiThread
private void callConnected() {
final long delta = System.currentTimeMillis() - callStartedTimeMs;
Log.i(TAG, "Call connected: delay=" + delta + "ms");
if (peerConnectionClient == null || isError) {
Log.w(TAG, "Call is connected in closed or error state");
return;
}
// Enable statistics callback.
peerConnectionClient.enableStatsEvents(true, STAT_CALLBACK_PERIOD);
setSwappedFeeds(false /* isSwappedFeeds */);
}
// Disconnect from remote resources, dispose of local resources, and exit.
private void disconnect() {
activityRunning = false;
remoteProxyRenderer.setTarget(null);
localProxyVideoSink.setTarget(null);
if (appRtcClient != null) {
appRtcClient.disconnectFromRoom();
appRtcClient = null;
}
if (pipRenderer != null) {
pipRenderer.release();
pipRenderer = null;
}
if (fullscreenRenderer != null) {
fullscreenRenderer.release();
fullscreenRenderer = null;
}
if (peerConnectionClient != null) {
peerConnectionClient.close();
peerConnectionClient = null;
}
if (iceConnected && !isError) {
setResult(RESULT_OK);
} else {
setResult(RESULT_CANCELED);
}
finish();
}
private void disconnectWithErrorMessage(final String errorMessage) {
if (!activityRunning) {
Log.e(TAG, "Critical error: " + errorMessage);
disconnect();
} else {
new AlertDialog.Builder(this)
.setTitle(getText(R.string.channel_error_title))
.setMessage(errorMessage)
.setCancelable(false)
.setNeutralButton(R.string.ok,
new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int id) {
dialog.cancel();
disconnect();
}
})
.create()
.show();
}
}
// Log |msg| and Toast about it.
private void logAndToast(String msg) {
Log.d(TAG, msg);
if (logToast != null) {
logToast.cancel();
}
logToast = Toast.makeText(this, msg, Toast.LENGTH_SHORT);
logToast.show();
}
private void reportError(final String description) {
runOnUiThread(new Runnable() {
#Override
public void run() {
if (!isError) {
isError = true;
disconnectWithErrorMessage(description);
}
}
});
}
// Create VideoCapturer
private VideoCapturer createVideoCapturer() {
final VideoCapturer videoCapturer;
Logging.d(TAG, "Creating capturer using camera2 API.");
videoCapturer = createCameraCapturer(new Camera2Enumerator(this));
if (videoCapturer == null) {
reportError("Failed to open camera");
return null;
}
return videoCapturer;
}
// Create VideoCapturer from camera
private VideoCapturer createCameraCapturer(CameraEnumerator enumerator) {
final String[] deviceNames = enumerator.getDeviceNames();
// First, try to find front facing camera
Logging.d(TAG, "Looking for front facing cameras.");
for (String deviceName : deviceNames) {
if (enumerator.isFrontFacing(deviceName)) {
Logging.d(TAG, "Creating front facing camera capturer.");
VideoCapturer videoCapturer = enumerator.createCapturer(deviceName, null);
if (videoCapturer != null) {
return videoCapturer;
}
}
}
// Front facing camera not found, try something else
Logging.d(TAG, "Looking for other cameras.");
for (String deviceName : deviceNames) {
if (!enumerator.isFrontFacing(deviceName)) {
Logging.d(TAG, "Creating other camera capturer.");
VideoCapturer videoCapturer = enumerator.createCapturer(deviceName, null);
if (videoCapturer != null) {
return videoCapturer;
}
}
}
return null;
}
private void setSwappedFeeds(boolean isSwappedFeeds) {
Logging.d(TAG, "setSwappedFeeds: " + isSwappedFeeds);
this.isSwappedFeeds = isSwappedFeeds;
localProxyVideoSink.setTarget(isSwappedFeeds ? fullscreenRenderer : pipRenderer);
remoteProxyRenderer.setTarget(isSwappedFeeds ? pipRenderer : fullscreenRenderer);
fullscreenRenderer.setMirror(isSwappedFeeds);
pipRenderer.setMirror(!isSwappedFeeds);
}
// -----Implementation of AppRTCClient.AppRTCSignalingEvents ---------------
// All callbacks are invoked from websocket signaling looper thread and
// are routed to UI thread.
private void onConnectedToRoomInternal(final AppRTCClient.SignalingParameters params) {
final long delta = System.currentTimeMillis() - callStartedTimeMs;
signalingParameters = params;
logAndToast("Creating peer connection, delay=" + delta + "ms");
VideoCapturer videoCapturer = null;
if (peerConnectionParameters.videoCallEnabled) {
videoCapturer = createVideoCapturer();
}
peerConnectionClient.createPeerConnection(
localProxyVideoSink, remoteRenderers, videoCapturer, signalingParameters);
if (signalingParameters.initiator) {
logAndToast("Creating OFFER...");
// Create offer. Offer SDP will be sent to answering client in
// PeerConnectionEvents.onLocalDescription event.
peerConnectionClient.createOffer();
} else {
if (params.offerSdp != null) {
peerConnectionClient.setRemoteDescription(params.offerSdp);
logAndToast("Creating ANSWER...");
// Create answer. Answer SDP will be sent to offering client in
// PeerConnectionEvents.onLocalDescription event.
peerConnectionClient.createAnswer();
}
if (params.iceCandidates != null) {
// Add remote ICE candidates from room.
for (IceCandidate iceCandidate : params.iceCandidates) {
peerConnectionClient.addRemoteIceCandidate(iceCandidate);
}
}
}
}
#Override
public void onConnectedToRoom(final AppRTCClient.SignalingParameters params) {
runOnUiThread(new Runnable() {
#Override
public void run() {
onConnectedToRoomInternal(params);
}
});
}
#Override
public void onRemoteDescription(final SessionDescription sdp) {
final long delta = System.currentTimeMillis() - callStartedTimeMs;
runOnUiThread(new Runnable() {
#Override
public void run() {
if (peerConnectionClient == null) {
Log.e(TAG, "Received remote SDP for non-initilized peer connection.");
return;
}
logAndToast("Received remote " + sdp.type + ", delay=" + delta + "ms");
peerConnectionClient.setRemoteDescription(sdp);
if (!signalingParameters.initiator) {
logAndToast("Creating ANSWER...");
// Create answer. Answer SDP will be sent to offering client in
// PeerConnectionEvents.onLocalDescription event.
peerConnectionClient.createAnswer();
}
}
});
}
#Override
public void onRemoteIceCandidate(final IceCandidate candidate) {
runOnUiThread(new Runnable() {
#Override
public void run() {
if (peerConnectionClient == null) {
Log.e(TAG, "Received ICE candidate for a non-initialized peer connection.");
return;
}
peerConnectionClient.addRemoteIceCandidate(candidate);
}
});
}
#Override
public void onRemoteIceCandidatesRemoved(final IceCandidate[] candidates) {
runOnUiThread(new Runnable() {
#Override
public void run() {
if (peerConnectionClient == null) {
Log.e(TAG, "Received ICE candidate removals for a non-initialized peer connection.");
return;
}
peerConnectionClient.removeRemoteIceCandidates(candidates);
}
});
}
#Override
public void onChannelClose() {
runOnUiThread(new Runnable() {
#Override
public void run() {
logAndToast("Remote end hung up; dropping PeerConnection");
disconnect();
}
});
}
#Override
public void onChannelError(final String description) {
reportError(description);
}
// -----Implementation of PeerConnectionClient.PeerConnectionEvents.---------
// Send local peer connection SDP and ICE candidates to remote party.
// All callbacks are invoked from peer connection client looper thread and
// are routed to UI thread.
#Override
public void onLocalDescription(final SessionDescription sdp) {
final long delta = System.currentTimeMillis() - callStartedTimeMs;
runOnUiThread(new Runnable() {
#Override
public void run() {
if (appRtcClient != null) {
logAndToast("Sending " + sdp.type + ", delay=" + delta + "ms");
if (signalingParameters.initiator) {
appRtcClient.sendOfferSdp(sdp);
} else {
appRtcClient.sendAnswerSdp(sdp);
}
}
if (peerConnectionParameters.videoMaxBitrate > 0) {
Log.d(TAG, "Set video maximum bitrate: " + peerConnectionParameters.videoMaxBitrate);
peerConnectionClient.setVideoMaxBitrate(peerConnectionParameters.videoMaxBitrate);
}
}
});
}
#Override
public void onIceCandidate(final IceCandidate candidate) {
runOnUiThread(new Runnable() {
#Override
public void run() {
if (appRtcClient != null) {
appRtcClient.sendLocalIceCandidate(candidate);
}
}
});
}
#Override
public void onIceCandidatesRemoved(final IceCandidate[] candidates) {
runOnUiThread(new Runnable() {
#Override
public void run() {
if (appRtcClient != null) {
appRtcClient.sendLocalIceCandidateRemovals(candidates);
}
}
});
}
#Override
public void onIceConnected() {
final long delta = System.currentTimeMillis() - callStartedTimeMs;
runOnUiThread(new Runnable() {
#Override
public void run() {
logAndToast("ICE connected, delay=" + delta + "ms");
iceConnected = true;
callConnected();
}
});
}
#Override
public void onIceDisconnected() {
runOnUiThread(new Runnable() {
#Override
public void run() {
logAndToast("ICE disconnected");
iceConnected = false;
disconnect();
}
});
}
#Override
public void onPeerConnectionClosed() {
}
#Override
public void onPeerConnectionStatsReady(final StatsReport[] reports) {
}
#Override
public void onPeerConnectionError(final String description) {
reportError(description);
}
// Activity interfaces
#Override
public void onStop() {
super.onStop();
activityRunning = false;
if (peerConnectionClient != null) {
peerConnectionClient.stopVideoSource();
}
}
#Override
public void onStart() {
super.onStart();
activityRunning = true;
// Video is not paused for screencapture. See onPause.
if (peerConnectionClient != null) {
peerConnectionClient.startVideoSource();
}
}
#Override
protected void onDestroy() {
Thread.setDefaultUncaughtExceptionHandler(null);
disconnect();
if (logToast != null) {
logToast.cancel();
}
activityRunning = false;
super.onDestroy();
}
private static class ProxyRenderer implements VideoRenderer.Callbacks {
private VideoRenderer.Callbacks target;
#Override
synchronized public void renderFrame(VideoRenderer.I420Frame frame) {
if (target == null) {
Logging.d(TAG, "Dropping frame in proxy because target is null.");
VideoRenderer.renderFrameDone(frame);
return;
}
target.renderFrame(frame);
}
synchronized public void setTarget(VideoRenderer.Callbacks target) {
this.target = target;
}
}
private static class ProxyVideoSink implements VideoSink {
private VideoSink target;
#Override
synchronized public void onFrame(VideoFrame frame) {
if (target == null) {
Logging.d(TAG, "Dropping frame in proxy because target is null.");
return;
}
target.onFrame(frame);
}
synchronized public void setTarget(VideoSink target) {
this.target = target;
}
}
}
You are using a demo signaling server operated by an entity (Google) that does not consider this demo signaling server a production system. The certificate expired a week ago.
Run your own server, instructions can be found here: https://github.com/webrtc/apprtc
I'm using the Google-API to get the count of unread E-Mails and some Events from the Google-Calender. I used the Quick-Start Sample Code from Google and it worked fine(asks for scope-permissions if not set and so on). So I formed it to a Service and a Auth-Activity for the user to change their Accounts. My problem is that the User don't get asked for Authorization anymore. The App still uses the right Account and if I select an Account which is authorized for my App it still work but if I select a new Account no Authorization-form is being shown.
Google AuthUtil Error:
09-19 13:13:54.114: D/DEBUG_TEST(7072): ERROR: UserRecoverableErrorNeedPermission
Here is my Activity that changes the Account. Every time a user selects an Google-Account the Static-Variable "gAccountDaten"(GoogleAccountCredential Object) got updated.
import android.accounts.AccountManager;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.util.Log;
import android.view.View;
import android.widget.ArrayAdapter;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.Toast;
import com.google.api.client.googleapis.extensions.android.gms.auth.GoogleAccountCredential;
import com.google.api.client.util.ExponentialBackOff;
import com.google.api.services.calendar.CalendarScopes;
import com.google.api.services.gmail.GmailScopes;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import pub.devrel.easypermissions.AfterPermissionGranted;
import pub.devrel.easypermissions.EasyPermissions;
public class GoogleAuthActivity extends Activity implements EasyPermissions.PermissionCallbacks {
static GoogleAccountCredential gAccountDaten;
static final int REQUEST_GOOGLE_PLAY_SERVICES = 1002;
static final int REQUEST_ACCOUNT_PICKER = 1000;
static final int REQUEST_AUTHORIZATION = 1001;
static final int REQUEST_PERMISSION_GET_ACCOUNTS = 1003;
private static final String PREF_ACCOUNT_NAME = "accountName";
private static final String[] SCOPES = { GmailScopes.GMAIL_LABELS, GmailScopes.GMAIL_READONLY , GmailScopes.MAIL_GOOGLE_COM, CalendarScopes.CALENDAR_READONLY};
private Boolean verbunden = false;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.googleauthactivitylayout);
gAccountDaten = GoogleAccountCredential.usingOAuth2(getApplicationContext(), Arrays.asList(SCOPES)).setBackOff(new ExponentialBackOff());
chooseAccount();
}
private String getGoogleAccountNameSettings(){
String toReturn = getPreferences(Context.MODE_PRIVATE)
.getString(PREF_ACCOUNT_NAME, null);
Log.d("ACCOUNT_DEBUG", "FUNC: getGoogleAccountNameSettings --> " + toReturn);
return toReturn;
}
private boolean setGoogleAccountNameSettings(String accountName){
try{
SharedPreferences settings = getPreferences(Context.MODE_PRIVATE);
SharedPreferences.Editor editor = settings.edit();
editor.putString(PREF_ACCOUNT_NAME, accountName);
editor.apply();
Log.d("ACCOUNT_DEBUG", "FUNC: setGoogleAccountNameSettings --> " + accountName);
}catch (Exception exc){
Log.e("ACCOUNT_DEBUG", "FUNC: setGoogleAccountNameSettings --> ERROR");
return false;
}finally {
Log.e("ACCOUNT_DEBUG", "FUNC: setGoogleAccountNameSettings --> ERROR");
return true;
}
}
private boolean removeGoogleAccountNameSettings(){
try{
SharedPreferences settings = getPreferences(Context.MODE_PRIVATE);
SharedPreferences.Editor editor = settings.edit();
editor.remove(PREF_ACCOUNT_NAME);
editor.apply();
GoogleService.EMAIL_COUNT = 0;
GoogleService.lstKalender.clear();
Log.d("ACCOUNT_DEBUG", "FUNC: removeGoogleAccountNameSettings --> " + "rennt");
}catch (Exception exc){
Log.e("ACCOUNT_DEBUG", "FUNC: removeGoogleAccountNameSettings --> ERROR");
return false;
}finally {
Log.e("ACCOUNT_DEBUG", "FUNC: removeGoogleAccountNameSettings --> ERROR");
return true;
}
}
#AfterPermissionGranted(REQUEST_PERMISSION_GET_ACCOUNTS)
private void chooseAccount( ) {
removeGoogleAccountNameSettings();
if (EasyPermissions.hasPermissions(
this, android.Manifest.permission.GET_ACCOUNTS)) {
String accountName = getGoogleAccountNameSettings();
if (accountName != null) {
gAccountDaten.setSelectedAccountName(accountName);
this.startService(new Intent(this, GoogleService.class).setFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK));
Log.d("AUTH_DEBUG", "Already authed" + " | " + gAccountDaten.getScope());
//finish();
} else {
// Start a dialog from which the user can choose an account
startActivityForResult(
gAccountDaten.newChooseAccountIntent(),
REQUEST_ACCOUNT_PICKER);
}
} else {
// Request the GET_ACCOUNTS permission via a user dialog
EasyPermissions.requestPermissions(
this,
"This app needs to access your Google account .",
REQUEST_PERMISSION_GET_ACCOUNTS,
android.Manifest.permission.GET_ACCOUNTS);
}
}
#Override
protected void onActivityResult(
int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
switch(requestCode) {
case REQUEST_GOOGLE_PLAY_SERVICES:
if (resultCode != RESULT_OK) {
//mOutputText.setText(
// "This app requires Google Play Services. Please install " +
// "Google Play Services on your device and relaunch this app.");
} else {
//getResultsFromApi();
}
break;
case REQUEST_ACCOUNT_PICKER:
if (resultCode == RESULT_OK && data != null && data.getExtras() != null) {
String accountName =
data.getStringExtra(AccountManager.KEY_ACCOUNT_NAME);
if (accountName != null) {
setGoogleAccountNameSettings(accountName);
gAccountDaten.setSelectedAccountName(accountName);
Log.d("AUTH_DEBUG", "Authed after Picker -->" + accountName + " | " + gAccountDaten.getScope());
this.startService(new Intent(this, GoogleService.class).setFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK));
finish();
}
}
break;
case REQUEST_AUTHORIZATION:
if (resultCode == RESULT_OK) {
Log.d("AUTH_DEBUG", "started aufter authorization -->" + resultCode + " | " + gAccountDaten.getScope());
this.startService(new Intent(this, GoogleService.class).setFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK));
finish();
}
break;
}
}
/////PERMISSION CALLBACKS
#Override
public void onRequestPermissionsResult(int requestCode,
#NonNull String[] permissions,
#NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
EasyPermissions.onRequestPermissionsResult(
requestCode, permissions, grantResults, this);
}
/**
* Callback for when a permission is granted using the EasyPermissions
* library.
* #param requestCode The request code associated with the requested
* permission
* #param list The requested permission list. Never null.
*/
#Override
public void onPermissionsGranted(int requestCode, List<String> list) {
// Do nothing.
}
/**
* Callback for when a permission is denied using the EasyPermissions
* library.
* #param requestCode The request code associated with the requested
* permission
* #param list The requested permission list. Never null.
*/
#Override
public void onPermissionsDenied(int requestCode, List<String> list) {
Log.d("DEBUG_TEST", "NO PERM");
}
}
And here is my Google-Service Code. It will gather the information from the google api every 60 seconds and write it to static variables.
import android.*;
import android.app.Dialog;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.os.AsyncTask;
import android.os.Handler;
import android.os.IBinder;
import android.preference.PreferenceManager;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.util.Log;
import android.widget.RemoteViews;
import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.GoogleApiAvailability;
import com.google.api.client.extensions.android.http.AndroidHttp;
import com.google.api.client.googleapis.extensions.android.gms.auth.GoogleAccountCredential;
import com.google.api.client.googleapis.extensions.android.gms.auth.GooglePlayServicesAvailabilityIOException;
import com.google.api.client.googleapis.extensions.android.gms.auth.UserRecoverableAuthIOException;
import com.google.api.client.http.HttpTransport;
import com.google.api.client.json.JsonFactory;
import com.google.api.client.json.jackson2.JacksonFactory;
import com.google.api.client.util.ExponentialBackOff;
import com.google.api.services.calendar.Calendar;
import com.google.api.services.calendar.CalendarScopes;
import com.google.api.services.calendar.model.CalendarList;
import com.google.api.services.calendar.model.CalendarListEntry;
import com.google.api.services.calendar.model.Event;
import com.google.api.services.calendar.model.EventDateTime;
import com.google.api.services.calendar.model.Events;
import com.google.api.services.gmail.GmailScopes;
import com.google.api.services.gmail.model.Label;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import pub.devrel.easypermissions.AfterPermissionGranted;
import pub.devrel.easypermissions.EasyPermissions;
public class GoogleService extends Service implements EasyPermissions.PermissionCallbacks{
GoogleAccountCredential gAccountDaten;
static final int REQUEST_ACCOUNT_PICKER = 1000;
static final int REQUEST_AUTHORIZATION = 1001;
static final int REQUEST_GOOGLE_PLAY_SERVICES = 1002;
static final int REQUEST_PERMISSION_GET_ACCOUNTS = 1003;
private static final String PREF_ACCOUNT_NAME = "accountName";
static String testj = "";
static ArrayList<Kalender> lstKalender = new ArrayList<>();
static int EMAIL_COUNT = 0;
//GOOGLE BERECHTIGUNGEN
private static final String[] SCOPES = { GmailScopes.GMAIL_LABELS, GmailScopes.GMAIL_READONLY , GmailScopes.MAIL_GOOGLE_COM, CalendarScopes.CALENDAR_READONLY};
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.d("DEBUG_TEST", "onStartCommand");
gAccountDaten = GoogleAuthActivity.gAccountDaten;
if(gAccountDaten == null){
Log.d("DEBUG_TEST", "gAccountDaten == NULL!");
return 0;
}
getResultsFromApi();
final Handler handler = new Handler();
Runnable runnable = new Runnable() {
#Override
public void run() {
getResultsFromApi();
handler.postDelayed(this, 60000);
}
};
handler.postDelayed(runnable, 60000);
return START_STICKY;
}
#Nullable
#Override
public IBinder onBind(Intent intent) {
return null;
}
#Override
public void onPermissionsGranted(int requestCode, List<String> perms) {
}
#Override
public void onPermissionsDenied(int requestCode, List<String> perms) {
}
#Override
public void onRequestPermissionsResult(int requestCode, #NonNull String[] permissions, #NonNull int[] grantResults) {
}
//HELFER FUNKTIONEN
private void getResultsFromApi() {
if (! isGooglePlayServicesAvailable()) {
acquireGooglePlayServices();
} else if (gAccountDaten.getSelectedAccountName() == null) {
Log.d("DEBUG_TEST", "ChooseAccount");
} else if (! isDeviceOnline()) {
//Gerät Offline
} else {
Log.d("DEBUG_TEST", "MakeRequestTask -->" + gAccountDaten.getSelectedAccountName());
new MakeRequestTask(gAccountDaten).execute();
}
}
//CHECK: GERÄT ONLINE ?
private boolean isDeviceOnline() {
ConnectivityManager connMgr =
(ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo networkInfo = connMgr.getActiveNetworkInfo();
return (networkInfo != null && networkInfo.isConnected());
}
//CHECK: GOOGLE SERVICE ONLINE ?
private boolean isGooglePlayServicesAvailable() {
GoogleApiAvailability apiAvailability =
GoogleApiAvailability.getInstance();
final int connectionStatusCode =
apiAvailability.isGooglePlayServicesAvailable(this);
return connectionStatusCode == ConnectionResult.SUCCESS;
}
private void acquireGooglePlayServices() {
GoogleApiAvailability apiAvailability =
GoogleApiAvailability.getInstance();
final int connectionStatusCode =
apiAvailability.isGooglePlayServicesAvailable(this);
}
/////////////////////////MAKE REQUEST KLASSE////////////////////////////////////
private class MakeRequestTask extends AsyncTask<Void, Void, List<String>> {
private com.google.api.services.gmail.Gmail mService = null;
private com.google.api.services.calendar.Calendar mServiceKalender = null;
private Exception mLastError = null;
public MakeRequestTask(GoogleAccountCredential credential) {
HttpTransport transport = AndroidHttp.newCompatibleTransport();
JsonFactory jsonFactory = JacksonFactory.getDefaultInstance();
HttpTransport transport2 = AndroidHttp.newCompatibleTransport();
JsonFactory jsonFactory2 = JacksonFactory.getDefaultInstance();
mService = new com.google.api.services.gmail.Gmail.Builder(
transport, jsonFactory, credential)
.setApplicationName("Gmail API Android Quickstart")
.build();
mServiceKalender = new com.google.api.services.calendar.Calendar.Builder(
transport2, jsonFactory2, credential)
.setApplicationName("Google Calendar API Android Quickstart")
.build();
Log.d("DEBUG_TEST", "mService und mServiceKalender Objecte instanzieren --> " + credential.getSelectedAccountName());
}
/**
* Background task to call Gmail API.
* #param params no parameters needed for this task.
*/
#Override
protected List<String> doInBackground(Void... params) {
try {
return getDataFromApi();
} catch (Exception e) {
mLastError = e;
cancel(true);
return null;
}
}
/**
* Fetch a list of Gmail labels attached to the specified account.
* #return List of Strings labels.
* #throws IOException
*/
private List<String> getDataFromApi() throws IOException {
// Get the labels in the user's account.
String user = "me";
List<String> labels = new ArrayList<String>();
Label label = mService.users().labels().get(user, "INBOX").execute();
Log.d("DEBUG_TEST", "getDataFromApi() --> " + user );
//EMAIL_COUNT = label.getMessagesUnread();
EMAIL_COUNT = label.getMessagesTotal();
labels.add("E-Mails: " + EMAIL_COUNT);
lstKalender.clear();
String pageToken = null;
do {
CalendarList calendarList = mServiceKalender.calendarList().list().setPageToken(pageToken).execute();
List<CalendarListEntry> items = calendarList.getItems();
for (CalendarListEntry calendarListEntry : items) {
String KALENDERNAME = calendarListEntry.getSummary();
String KALENDER_ID = calendarListEntry.getId();
Kalender neuerKalender = new Kalender();
neuerKalender.setKalenderName(KALENDERNAME);
neuerKalender.setKalenderId(KALENDER_ID);
Log.d("KALENDER_DEBUG", "Kalender: " + KALENDERNAME + "wurde angelegt! ID: " + KALENDER_ID);
String pageToken2 = null;
do {
Events events = mServiceKalender.events().list(KALENDER_ID).setPageToken(pageToken2).execute();
List<Event> items2 = events.getItems();
for (Event event : items2) {
String calName = event.getSummary();
EventDateTime calStart = event.getStart();
EventDateTime calEnde = event.getEnd();
String calLocation = event.getLocation();
String calID = event.getId();
if(neuerKalender.addEvent(calName,calStart,calEnde,calLocation,calID)){
//Log.d("KALENDER_DEBUG", "Event: " + calName + " wurde zum Kalender " + KALENDERNAME + " hinzugefügt!");
}
}
pageToken2 = events.getNextPageToken();
} while (pageToken2 != null);
lstKalender.add(neuerKalender);
Log.d("KALENDER_DEBUG", "Kalender: " + neuerKalender.getKalenderName() + " durchgelaufen und zur Globalen Liste hinzugefügt!");
}
pageToken = calendarList.getNextPageToken();
} while (pageToken != null);
DaWidgetProvider.updateEventList(getBaseContext());
UpdateService updateService = new UpdateService();
updateService.buildUpdate(getBaseContext());
DaWidgetProvider.updateAllWidgets(getBaseContext());
return labels;
}
#Override
protected void onPreExecute() {
}
#Override
protected void onPostExecute(List<String> output) {
if (output == null || output.size() == 0) {
// mOutputText.setText("No results returned.");
} else {
//output.add(0, "Data retrieved using the Gmail API:");
//mOutputText.setText(TextUtils.join("\n", output));
//mProgress.dismiss();
Context mContext = getApplicationContext();
}
}
#Override
protected void onCancelled() {
if (mLastError != null) {
if (mLastError instanceof GooglePlayServicesAvailabilityIOException) {
Log.d("DEBUG_TEST", "ERROR: " + mLastError.getMessage());
} else if (mLastError instanceof UserRecoverableAuthIOException) {
Log.d("DEBUG_TEST", "ERROR: UserRecoverableError" + mLastError.getMessage());
} else {
Log.d("DEBUG_TEST", "ERROR: " + mLastError.getMessage());
}
} else {
Log.d("DEBUG_TEST", "abgebrochen");
}
}
}
}
Thanks,
J. Doe ;)
Nice that seems to be the problem. But ive got a problem to fix it, because my Google-Service and my Google-Account Picker Activity are differented. I tried to pass the mLastError to my Activity in an Intent and start the Authentication-Request there but i got the following Error:
09-19 13:44:18.508: E/AndroidRuntime(7959): java.lang.RuntimeException: Parcelable encountered IOException writing serializable object (name = com.google.api.client.googleapis.extensions.android.gms.auth.UserRecoverableAuthIOException)
And my Code changes in Google-Service and GoogleAuthActivity:
Google-Service:
protected void onCancelled() {
if (mLastError != null) {
if (mLastError instanceof GooglePlayServicesAvailabilityIOException) {
Log.d("DEBUG_TEST", "ERROR: " + mLastError.getMessage());
} else if (mLastError instanceof UserRecoverableAuthIOException) {
Log.d("DEBUG_TEST", "ERROR: UserRecoverableError" + mLastError.getCause().getMessage());
//KEINE BERECHTIGUNG GESETZT
[CODE CHANGES]
Intent startIntent = new Intent(getApplicationContext(), GoogleAuthActivity.class);
startIntent.putExtra("auththis", mLastError);
startIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(startIntent);
[/CODE CHANGES]
} else {
Log.d("DEBUG_TEST", "ERROR: " + mLastError.getMessage());
}
} else {
Log.d("DEBUG_TEST", "abgebrochen");
}
}
GoogleAuthActivity:
//EXTRA DATA???
if(getIntent().hasExtra("auththis")){
Intent edIntent = getIntent();
Bundle extras = edIntent.getExtras();
UserRecoverableAuthIOException mLastError = null;
mLastError = (UserRecoverableAuthIOException) extras.get("auththis");
startActivityForResult(
mLastError.getIntent(),
GoogleAuthActivity.REQUEST_AUTHORIZATION);
}else{
chooseAccount();
}
Im following the tutorial of In-app Billing from the following link:
Android Studio Google Play In-app Billing Tutorial.
Im implementing this logic in a Contact Adapter class which extends Base Adapter. In tutorial it is implemented in a class which extends Activity.
Error comes on onActivityResult(). I read several questions on this and I understand this method should be written in class which extends Activity but in my case the scenario is different.
Is there any way to solve this without writing onActivityResult method in MainActivity class.. and if not what should I do?
Heres ContactAdapter.java
import android.app.Activity;
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.CheckBox;
import android.widget.CompoundButton;
import android.widget.TextView;
import android.widget.Toast;
import com.neirx.myco.smsproject.util.IabHelper;
import com.neirx.myco.smsproject.util.IabResult;
import com.neirx.myco.smsproject.util.Inventory;
import com.neirx.myco.smsproject.util.Purchase;
import java.util.List;
public class ContactAdapter extends BaseAdapter {
private static final java.lang.String CLASS_NAME = "<ContactAdapter> ";
Context context;
List<Contact> objects;
LayoutInflater lInflater;
MainActivity activity;
static final String ITEM_SKU = "android.test.purchased";
IabHelper mHelper;
int count = 0;
int get_limit;
private int limit_counter = 0;
private int max_limit = 2;
boolean testbool = true;
public ContactAdapter(Context context, List<Contact> contact) {
this.context = context;
objects = contact;
lInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
activity = (MainActivity) context;
}
#Override
public int getCount() {
int selectedCount = 0;
int nonSelectedCount = 0;
int size = 0;
for(Contact contact : objects){
if(contact.isChecked()) selectedCount++;
else nonSelectedCount++;
}
if(activity.isShowSelected()) size += selectedCount;
if(activity.isShowNonSelected()) size += nonSelectedCount;
return size;
}
#Override
public Object getItem(int position) {
return objects.get(position);
}
#Override
public long getItemId(int position) {
return position;
}
public void buyClick() {
mHelper.launchPurchaseFlow(activity, ITEM_SKU, 10001, mPurchaseFinishedListener, "mypurchasetoken");
}
#Override
protected void onActivityResult(int requestCode, int resultCode,Intent data)
{
if (!mHelper.handleActivityResult(requestCode,resultCode, data)) {
super.onActivityResult(requestCode, resultCode, data);
}
}
IabHelper.OnIabPurchaseFinishedListener mPurchaseFinishedListener = new IabHelper.OnIabPurchaseFinishedListener() {
public void onIabPurchaseFinished(IabResult result,Purchase purchase)
{
if (result.isFailure()) {
// Handle error
Log.d("----FAILURE 1---", "FAIL 1");
return;
}
else if (purchase.getSku().equals(ITEM_SKU)) {
consumeItem();
//buyButton.setEnabled(false);
}
}
};
public void consumeItem() {
mHelper.queryInventoryAsync(mReceivedInventoryListener);
}
IabHelper.QueryInventoryFinishedListener mReceivedInventoryListener = new IabHelper.QueryInventoryFinishedListener() {
public void onQueryInventoryFinished(IabResult result,Inventory inventory) {
if (result.isFailure()) {
// Handle failure
Log.d("----FAILURE 2---", "FAIL 2");
} else {
mHelper.consumeAsync(inventory.getPurchase(ITEM_SKU),mConsumeFinishedListener);
}
}
};
IabHelper.OnConsumeFinishedListener mConsumeFinishedListener = new IabHelper.OnConsumeFinishedListener() {
public void onConsumeFinished(Purchase purchase,IabResult result) {
if (result.isSuccess()) {
//clickButton.setEnabled(true);
Log.d("----Success ----", "Success");
} else {
// handle error
Log.d("----FAILURE 3---", "FAIL 3");
}
}
};
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View view = convertView;
String base64EncodedPublicKey = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAxW650UixX2dLFECVdOpTh5OpBTqHwsznQAKd/cVcqKhrXROy4+Gj6B7M6wbkhTaloNSzTOf+nw9t1LZZ19Vlr6kcwmtxP+V/HOFwjf/NB69StOONogXtGKDyRrxtVaPM5es3yGy/aP/LXWfTLFQYJvur4AePonuRXz33iufBq5ITDQJ0+0D/o/mGtadJv0ZMsP9LV/qrMqruoqpSdaIiw5TGXdzYlJTuoP3GwS9kRyZKDeG/70KZ28W/ZclVWAdnZ7aCeDURYDV3a4pmGp5/cIvKwbex6Y7KbQYENX5ObSgNoFHLdyPTdkYaeuU9O6pet2TjGUCKr8n4M5KUMZVm8QIDAQAB";
mHelper = new IabHelper(context, base64EncodedPublicKey);
mHelper.startSetup(new IabHelper.OnIabSetupFinishedListener() {
public void onIabSetupFinished(IabResult result)
{
if (!result.isSuccess()) {
Log.d("---MY TAG---", "In-app Billing setup failed: " +
result);
} else {
Log.d("---MY TAG---", "In-app Billing is set up OK");
}
}
});
SharedPreferences limit_pref = context.getSharedPreferences(Statical.PREF_LIMIT, Context.MODE_PRIVATE);
get_limit = limit_pref.getInt("LIMIT_KEY",0);
//Log.d("----- STORED VALUE-----", "Value is: "+get_limit);
Log.d("----- Max VALUE-----", "Value is: "+max_limit);
if (view == null) {
view = lInflater.inflate(R.layout.view_contact, parent, false);//create view file
}
if (position == 0) {
count = 0;
}
if (!activity.isShowSelected()) {
while (objects.get(position + count).isChecked()) {
count++;
}
}
if (!activity.isShowNonSelected()) {
while (!objects.get(position + count).isChecked()) {
count++;
}
}
final Contact contact = objects.get(position + count);
String contactFirstName = contact.getFirstName();
String contactSecondName = contact.getSecondName();
String contactMiddleName = contact.getMiddleName();
String[] contactNumbers = contact.getNumbers();
TextView tvName = (TextView) view.findViewById(R.id.tvName);
TextView tvNumber = (TextView) view.findViewById(R.id.tvNumber);
final CheckBox checkBox = (CheckBox) view.findViewById(R.id.checkBox);
if(get_limit == max_limit)
{
//checkBox.setChecked(contact.isChecked());
//testbool = false;
limit_counter = get_limit;
}
else if (get_limit == 1)
{
limit_counter = 1;
}
String fullName;
switch (MainActivity.sortMode) {
case Contact.COMPARE_SECOND_NAME:
fullName = getFullName(contactSecondName, contactFirstName);
break;
case Contact.COMPARE_MIDDLE_NAME:
fullName = getFullName(contactMiddleName, contactFirstName, contactSecondName);
break;
default:
fullName = getFullName(contactFirstName, contactSecondName);
break;
}
tvName.setText(fullName);
StringBuilder sbNumber = new StringBuilder();
for (int i = 0; i < contactNumbers.length; i++) {
sbNumber.append(contactNumbers[i]);
if (i < contactNumbers.length - 1) {
sbNumber.append(", ");
}
}
tvNumber.setText(sbNumber);
if (testbool) {
//Log.d("Check Boolean Tag 2 ", "testbool: "+testbool);
checkBox.setChecked(contact.isChecked());
}
checkBox.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
SharedPreferences selected = context.getSharedPreferences(Statical.CONTACTS_SELECTED, Context.MODE_PRIVATE);
SharedPreferences.Editor editSelected = selected.edit();
SharedPreferences limit_pref = context.getSharedPreferences(Statical.PREF_LIMIT, Context.MODE_PRIVATE);
SharedPreferences.Editor edit_limit_pref = limit_pref.edit();
int k = 0;
if(limit_counter == max_limit)
{
checkBox.setChecked(false);
//Toast.makeText(context,"Limit reached !! ", Toast.LENGTH_SHORT).show();
Log.d("-------LIMIT REACH-----", "value: "+limit_counter);
edit_limit_pref.putInt("LIMIT_KEY",limit_counter);
showAlertDialog();
}
if (contact.isChecked() && limit_counter <= max_limit && limit_counter >= 0) {
limit_counter = limit_counter - 1;
editSelected.putBoolean(contact.getContactId(), false);
edit_limit_pref.putInt("LIMIT_KEY",limit_counter);
contact.setChecked(false);
Log.d("-------UN CHECKED-----", "Un Checked value: "+limit_counter);
k = -1;
}
else if(!contact.isChecked() && limit_counter < max_limit){
limit_counter = limit_counter + 1;
editSelected.putBoolean(contact.getContactId(), true);
edit_limit_pref.putInt("LIMIT_KEY",limit_counter);
contact.setChecked(true);
Log.d("------- CHECKED -----", "Checked value: "+limit_counter);
k = 1;
}
editSelected.apply();
edit_limit_pref.apply();
activity.updateCount(k);
}
});
return view;
}
public void showAlertDialog()
{
Log.d("-------VALUE-----", "value: "+limit_counter);
AlertDialog.Builder alertDialog = new AlertDialog.Builder(context);
alertDialog.setTitle("Limit Reached!");
alertDialog.setMessage("Buy Pro Version");
alertDialog.setIcon(R.drawable.action_bar_logo);
alertDialog.setPositiveButton("Ok", new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
buyClick();
Log.d("-------OK PRESSED -----", "value: " + limit_counter);
dialog.cancel();
}
});
alertDialog.setNegativeButton("Later", new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
Log.d("----LATER PRESSED -----", "value: " + limit_counter);
dialog.cancel();
}
});
alertDialog.show();
}
private String getFullName(String first, String second, String third) {
StringBuilder sbName = new StringBuilder();
if (!first.isEmpty()) {
sbName.append(first).append(" ");
}
if (!second.isEmpty()) {
sbName.append(second).append(" ");
}
if (!third.isEmpty()) {
sbName.append(third);
}
return sbName.toString();
}
private String getFullName(String first, String second) {
StringBuilder sbName = new StringBuilder();
if (!first.isEmpty()) {
sbName.append(first).append(" ");
}
if (!second.isEmpty()) {
sbName.append(second);
}
return sbName.toString();
}
}
I recommend you to create an activity just to process this payment and then you can back to your normal flow.
I've got a problem with this tutorial from the Book "Getting Started with Bluetooth Low Energy".
I only want to scan for BLE devices and post the results via logcat. But anytime I do start the scan, the app shut down and give me a NullPointerException (See Logcat below). For this example I'm using the BleWrapper Class, but I also try to set up BLE Scan without it in another app. But it is always the same error... NullPointerException...
I also tried out 2 different demo apps, import them into my Android studio(v1.1) and they perform well. But if I try to write the same code in its own activity it will crash with this error.
I guess it's something wrong with the callback, or the scanning-method, maybe I forget some reference?
Here is my main activity:
package com.example.oliver.blebuch;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.content.Intent;
import android.support.v7.app.ActionBarActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.widget.Toast;
public class MainActivity extends ActionBarActivity {
private BleWrapper mBleWrapper = null;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Log.d("tag", "Test");
mBleWrapper = new BleWrapper(this, new BleWrapperUiCallbacks.Null()
{
#Override
public void uiDeviceFound(final BluetoothDevice device,
final int rssi,
final byte[] scanRecord)
{
Log.d("tag", "uiDeviceFound: "+device.getName()+", "+rssi+", "+scanRecord.toString());
}
});
if(mBleWrapper.checkBleHardwareAvailable() == false)
{
Toast.makeText(this,"No BLE-compatible hardware detected",
Toast.LENGTH_SHORT).show();
finish();
}
}
protected void onResume(){
super.onResume();
//check for Bluetooth enabled on each resume
if (mBleWrapper.isBtEnabled() == false)
{
Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivity(enableBtIntent);
finish();
}
}
#Override
protected void onPause(){
super.onPause();
mBleWrapper.disconnect();
mBleWrapper.close();
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.menu_main, menu);
return true;
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId())
{
case R.id.action_scan:
mBleWrapper.startScanning();
break;
case R.id.action_stop:
mBleWrapper.stopScanning();
break;
default:
break;
}
return super.onOptionsItemSelected(item);
}
}
Logcat:
03-26 10:28:49.986 26288-26288/com.example.oliver.blebuch D/OpenGLRenderer﹕ Enabling debug mode 0
03-26 10:28:53.236 26288-26288/com.example.oliver.blebuch W/dalvikvm﹕ method Landroid/support/v7/internal/widget/ListViewCompat;.lookForSelectablePosition incorrectly overrides package-private method with same name in Landroid/widget/ListView;
03-26 10:28:53.266 26288-26288/com.example.oliver.blebuch D/AbsListView﹕ Get MotionRecognitionManager
03-26 10:28:54.366 26288-26288/com.example.oliver.blebuch D/AndroidRuntime﹕ Shutting down VM
03-26 10:28:54.366 26288-26288/com.example.oliver.blebuch W/dalvikvm﹕ threadid=1: thread exiting with uncaught exception (group=0x41783da0)
03-26 10:28:54.376 26288-26288/com.example.oliver.blebuch E/AndroidRuntime﹕ FATAL EXCEPTION: main
Process: com.example.oliver.blebuch, PID: 26288
java.lang.NullPointerException
at com.example.oliver.blebuch.BleWrapper.startScanning(BleWrapper.java:79)
at com.example.oliver.blebuch.MainActivity.onOptionsItemSelected(MainActivity.java:78)
at android.app.Activity.onMenuItemSelected(Activity.java:2708)
at android.support.v4.app.FragmentActivity.onMenuItemSelected(FragmentActivity.java:350)
at android.support.v7.app.ActionBarActivity.onMenuItemSelected(ActionBarActivity.java:155)
at android.support.v7.app.ActionBarActivityDelegate$1.onMenuItemSelected(ActionBarActivityDelegate.java:74)
at android.support.v7.app.ActionBarActivityDelegateBase.onMenuItemSelected(ActionBarActivityDelegateBase.java:556)
at android.support.v7.internal.view.menu.MenuBuilder.dispatchMenuItemSelected(MenuBuilder.java:802)
at android.support.v7.internal.view.menu.MenuItemImpl.invoke(MenuItemImpl.java:153)
at android.support.v7.internal.view.menu.MenuBuilder.performItemAction(MenuBuilder.java:949)
at android.support.v7.internal.view.menu.MenuBuilder.performItemAction(MenuBuilder.java:939)
at android.support.v7.internal.view.menu.MenuPopupHelper.onItemClick(MenuPopupHelper.java:187)
at android.widget.AdapterView.performItemClick(AdapterView.java:308)
at android.widget.AbsListView.performItemClick(AbsListView.java:1495)
at android.widget.AbsListView$PerformClick.run(AbsListView.java:3453)
at android.widget.AbsListView$3.run(AbsListView.java:4816)
at android.os.Handler.handleCallback(Handler.java:733)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:136)
at android.app.ActivityThread.main(ActivityThread.java:5479)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:515)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1283)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1099)
at dalvik.system.NativeStart.main(Native Method)
BLE Wrapper Class:
package com.example.oliver.blebuch;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
import java.util.Locale;
import java.util.UUID;
import android.app.Activity;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothGatt;
import android.bluetooth.BluetoothGattCallback;
import android.bluetooth.BluetoothGattCharacteristic;
import android.bluetooth.BluetoothGattDescriptor;
import android.bluetooth.BluetoothGattService;
import android.bluetooth.BluetoothManager;
import android.bluetooth.BluetoothProfile;
import android.content.Context;
import android.content.pm.PackageManager;
import android.os.Handler;
import android.util.Log;
public class BleWrapper {
/* defines (in milliseconds) how often RSSI should be updated */
private static final int RSSI_UPDATE_TIME_INTERVAL = 1500; // 1.5 seconds
/* callback object through which we are returning results to the caller */
private BleWrapperUiCallbacks mUiCallback = null;
/* define NULL object for UI callbacks */
private static final BleWrapperUiCallbacks NULL_CALLBACK = new BleWrapperUiCallbacks.Null();
/* creates BleWrapper object, set its parent activity and callback object */
public BleWrapper(Activity parent, BleWrapperUiCallbacks callback) {
this.mParent = parent;
mUiCallback = callback;
if(mUiCallback == null) mUiCallback = NULL_CALLBACK;
}
public BluetoothManager getManager() { return mBluetoothManager; }
public BluetoothAdapter getAdapter() { return mBluetoothAdapter; }
public BluetoothDevice getDevice() { return mBluetoothDevice; }
public BluetoothGatt getGatt() { return mBluetoothGatt; }
public BluetoothGattService getCachedService() { return mBluetoothSelectedService; }
public List<BluetoothGattService> getCachedServices() { return mBluetoothGattServices; }
public boolean isConnected() { return mConnected; }
/* run test and check if this device has BT and BLE hardware available */
public boolean checkBleHardwareAvailable() {
// First check general Bluetooth Hardware:
// get BluetoothManager...
final BluetoothManager manager = (BluetoothManager) mParent.getSystemService(Context.BLUETOOTH_SERVICE);
if(manager == null) return false;
// .. and then get adapter from manager
final BluetoothAdapter adapter = manager.getAdapter();
if(adapter == null) return false;
// and then check if BT LE is also available
boolean hasBle = mParent.getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE);
return hasBle;
}
/* before any action check if BT is turned ON and enabled for us
* call this in onResume to be always sure that BT is ON when Your
* application is put into the foreground */
public boolean isBtEnabled() {
final BluetoothManager manager = (BluetoothManager) mParent.getSystemService(Context.BLUETOOTH_SERVICE);
if(manager == null) return false;
final BluetoothAdapter adapter = manager.getAdapter();
if(adapter == null) return false;
return adapter.isEnabled();
}
/* start scanning for BT LE devices around */
public void startScanning() {
mBluetoothAdapter.startLeScan(mDeviceFoundCallback);
}
/* stops current scanning */
public void stopScanning() {
mBluetoothAdapter.stopLeScan(mDeviceFoundCallback);
}
/* initialize BLE and get BT Manager & Adapter */
public boolean initialize() {
if (mBluetoothManager == null) {
mBluetoothManager = (BluetoothManager) mParent.getSystemService(Context.BLUETOOTH_SERVICE);
if (mBluetoothManager == null) {
return false;
}
}
if(mBluetoothAdapter == null) mBluetoothAdapter = mBluetoothManager.getAdapter();
if (mBluetoothAdapter == null) {
return false;
}
return true;
}
/* connect to the device with specified address */
public boolean connect(final String deviceAddress) {
if (mBluetoothAdapter == null || deviceAddress == null) return false;
mDeviceAddress = deviceAddress;
// check if we need to connect from scratch or just reconnect to previous device
if(mBluetoothGatt != null && mBluetoothGatt.getDevice().getAddress().equals(deviceAddress)) {
// just reconnect
return mBluetoothGatt.connect();
}
else {
// connect from scratch
// get BluetoothDevice object for specified address
mBluetoothDevice = mBluetoothAdapter.getRemoteDevice(mDeviceAddress);
if (mBluetoothDevice == null) {
// we got wrong address - that device is not available!
return false;
}
// connect with remote device
mBluetoothGatt = mBluetoothDevice.connectGatt(mParent, false, mBleCallback);
}
return true;
}
/* disconnect the device. It is still possible to reconnect to it later with this Gatt client */
public void disconnect() {
if(mBluetoothGatt != null) mBluetoothGatt.disconnect();
mUiCallback.uiDeviceDisconnected(mBluetoothGatt, mBluetoothDevice);
}
/* close GATT client completely */
public void close() {
if(mBluetoothGatt != null) mBluetoothGatt.close();
mBluetoothGatt = null;
}
/* request new RSSi value for the connection*/
public void readPeriodicalyRssiValue(final boolean repeat) {
mTimerEnabled = repeat;
// check if we should stop checking RSSI value
if(mConnected == false || mBluetoothGatt == null || mTimerEnabled == false) {
mTimerEnabled = false;
return;
}
mTimerHandler.postDelayed(new Runnable() {
#Override
public void run() {
if(mBluetoothGatt == null ||
mBluetoothAdapter == null ||
mConnected == false)
{
mTimerEnabled = false;
return;
}
// request RSSI value
mBluetoothGatt.readRemoteRssi();
// add call it once more in the future
readPeriodicalyRssiValue(mTimerEnabled);
}
}, RSSI_UPDATE_TIME_INTERVAL);
}
/* starts monitoring RSSI value */
public void startMonitoringRssiValue() {
readPeriodicalyRssiValue(true);
}
/* stops monitoring of RSSI value */
public void stopMonitoringRssiValue() {
readPeriodicalyRssiValue(false);
}
/* request to discover all services available on the remote devices
* results are delivered through callback object */
public void startServicesDiscovery() {
if(mBluetoothGatt != null) mBluetoothGatt.discoverServices();
}
/* gets services and calls UI callback to handle them
* before calling getServices() make sure service discovery is finished! */
public void getSupportedServices() {
if(mBluetoothGattServices != null && mBluetoothGattServices.size() > 0) mBluetoothGattServices.clear();
// keep reference to all services in local array:
if(mBluetoothGatt != null) mBluetoothGattServices = mBluetoothGatt.getServices();
mUiCallback.uiAvailableServices(mBluetoothGatt, mBluetoothDevice, mBluetoothGattServices);
}
/* get all characteristic for particular service and pass them to the UI callback */
public void getCharacteristicsForService(final BluetoothGattService service) {
if(service == null) return;
List<BluetoothGattCharacteristic> chars = null;
chars = service.getCharacteristics();
mUiCallback.uiCharacteristicForService(mBluetoothGatt, mBluetoothDevice, service, chars);
// keep reference to the last selected service
mBluetoothSelectedService = service;
}
/* request to fetch newest value stored on the remote device for particular characteristic */
public void requestCharacteristicValue(BluetoothGattCharacteristic ch) {
if (mBluetoothAdapter == null || mBluetoothGatt == null) return;
mBluetoothGatt.readCharacteristic(ch);
// new value available will be notified in Callback Object
}
/* get characteristic's value (and parse it for some types of characteristics)
* before calling this You should always update the value by calling requestCharacteristicValue() */
public void getCharacteristicValue(BluetoothGattCharacteristic ch) {
if (mBluetoothAdapter == null || mBluetoothGatt == null || ch == null) return;
byte[] rawValue = ch.getValue();
String strValue = null;
int intValue = 0;
// lets read and do real parsing of some characteristic to get meaningful value from it
UUID uuid = ch.getUuid();
if(uuid.equals(BleDefinedUUIDs.Characteristic.HEART_RATE_MEASUREMENT)) { // heart rate
// follow https://developer.bluetooth.org/gatt/characteristics/Pages/CharacteristicViewer.aspx?u=org.bluetooth.characteristic.heart_rate_measurement.xml
// first check format used by the device - it is specified in bit 0 and tells us if we should ask for index 1 (and uint8) or index 2 (and uint16)
int index = ((rawValue[0] & 0x01) == 1) ? 2 : 1;
// also we need to define format
int format = (index == 1) ? BluetoothGattCharacteristic.FORMAT_UINT8 : BluetoothGattCharacteristic.FORMAT_UINT16;
// now we have everything, get the value
intValue = ch.getIntValue(format, index);
strValue = intValue + " bpm"; // it is always in bpm units
}
else if (uuid.equals(BleDefinedUUIDs.Characteristic.HEART_RATE_MEASUREMENT) || // manufacturer name string
uuid.equals(BleDefinedUUIDs.Characteristic.MODEL_NUMBER_STRING) || // model number string)
uuid.equals(BleDefinedUUIDs.Characteristic.FIRMWARE_REVISION_STRING)) // firmware revision string
{
// follow https://developer.bluetooth.org/gatt/characteristics/Pages/CharacteristicViewer.aspx?u=org.bluetooth.characteristic.manufacturer_name_string.xml etc.
// string value are usually simple utf8s string at index 0
strValue = ch.getStringValue(0);
}
else if(uuid.equals(BleDefinedUUIDs.Characteristic.APPEARANCE)) { // appearance
// follow: https://developer.bluetooth.org/gatt/characteristics/Pages/CharacteristicViewer.aspx?u=org.bluetooth.characteristic.gap.appearance.xml
intValue = ((int)rawValue[1]) << 8;
intValue += rawValue[0];
strValue = BleNamesResolver.resolveAppearance(intValue);
}
else if(uuid.equals(BleDefinedUUIDs.Characteristic.BODY_SENSOR_LOCATION)) { // body sensor location
// follow: https://developer.bluetooth.org/gatt/characteristics/Pages/CharacteristicViewer.aspx?u=org.bluetooth.characteristic.body_sensor_location.xml
intValue = rawValue[0];
strValue = BleNamesResolver.resolveHeartRateSensorLocation(intValue);
}
else if(uuid.equals(BleDefinedUUIDs.Characteristic.BATTERY_LEVEL)) { // battery level
// follow: https://developer.bluetooth.org/gatt/characteristics/Pages/CharacteristicViewer.aspx?u=org.bluetooth.characteristic.battery_level.xml
intValue = rawValue[0];
strValue = "" + intValue + "% battery level";
}
else {
// not known type of characteristic, so we need to handle this in "general" way
// get first four bytes and transform it to integer
intValue = 0;
if(rawValue.length > 0) intValue = (int)rawValue[0];
if(rawValue.length > 1) intValue = intValue + ((int)rawValue[1] << 8);
if(rawValue.length > 2) intValue = intValue + ((int)rawValue[2] << 8);
if(rawValue.length > 3) intValue = intValue + ((int)rawValue[3] << 8);
if (rawValue.length > 0) {
final StringBuilder stringBuilder = new StringBuilder(rawValue.length);
for(byte byteChar : rawValue) {
stringBuilder.append(String.format("%c", byteChar));
}
strValue = stringBuilder.toString();
}
}
String timestamp = new SimpleDateFormat("yyyy.MM.dd HH:mm:ss.SSS").format(new Date());
mUiCallback.uiNewValueForCharacteristic(mBluetoothGatt,
mBluetoothDevice,
mBluetoothSelectedService,
ch,
strValue,
intValue,
rawValue,
timestamp);
}
/* reads and return what what FORMAT is indicated by characteristic's properties
* seems that value makes no sense in most cases */
public int getValueFormat(BluetoothGattCharacteristic ch) {
int properties = ch.getProperties();
if((BluetoothGattCharacteristic.FORMAT_FLOAT & properties) != 0) return BluetoothGattCharacteristic.FORMAT_FLOAT;
if((BluetoothGattCharacteristic.FORMAT_SFLOAT & properties) != 0) return BluetoothGattCharacteristic.FORMAT_SFLOAT;
if((BluetoothGattCharacteristic.FORMAT_SINT16 & properties) != 0) return BluetoothGattCharacteristic.FORMAT_SINT16;
if((BluetoothGattCharacteristic.FORMAT_SINT32 & properties) != 0) return BluetoothGattCharacteristic.FORMAT_SINT32;
if((BluetoothGattCharacteristic.FORMAT_SINT8 & properties) != 0) return BluetoothGattCharacteristic.FORMAT_SINT8;
if((BluetoothGattCharacteristic.FORMAT_UINT16 & properties) != 0) return BluetoothGattCharacteristic.FORMAT_UINT16;
if((BluetoothGattCharacteristic.FORMAT_UINT32 & properties) != 0) return BluetoothGattCharacteristic.FORMAT_UINT32;
if((BluetoothGattCharacteristic.FORMAT_UINT8 & properties) != 0) return BluetoothGattCharacteristic.FORMAT_UINT8;
return 0;
}
/* set new value for particular characteristic */
public void writeDataToCharacteristic(final BluetoothGattCharacteristic ch, final byte[] dataToWrite) {
if (mBluetoothAdapter == null || mBluetoothGatt == null || ch == null) return;
// first set it locally....
ch.setValue(dataToWrite);
// ... and then "commit" changes to the peripheral
mBluetoothGatt.writeCharacteristic(ch);
}
/* enables/disables notification for characteristic */
public void setNotificationForCharacteristic(BluetoothGattCharacteristic ch, boolean enabled) {
if (mBluetoothAdapter == null || mBluetoothGatt == null) return;
boolean success = mBluetoothGatt.setCharacteristicNotification(ch, enabled);
if(!success) {
Log.e("------", "Seting proper notification status for characteristic failed!");
}
// This is also sometimes required (e.g. for heart rate monitors) to enable notifications/indications
// see: https://developer.bluetooth.org/gatt/descriptors/Pages/DescriptorViewer.aspx?u=org.bluetooth.descriptor.gatt.client_characteristic_configuration.xml
BluetoothGattDescriptor descriptor = ch.getDescriptor(UUID.fromString("00002902-0000-1000-8000-00805f9b34fb"));
if(descriptor != null) {
byte[] val = enabled ? BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE : BluetoothGattDescriptor.DISABLE_NOTIFICATION_VALUE;
descriptor.setValue(val);
mBluetoothGatt.writeDescriptor(descriptor);
}
}
/* defines callback for scanning results */
private BluetoothAdapter.LeScanCallback mDeviceFoundCallback = new BluetoothAdapter.LeScanCallback() {
#Override
public void onLeScan(final BluetoothDevice device, final int rssi, final byte[] scanRecord) {
mUiCallback.uiDeviceFound(device, rssi, scanRecord);
}
};
/* callbacks called for any action on particular Ble Device */
private final BluetoothGattCallback mBleCallback = new BluetoothGattCallback() {
#Override
public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
if (newState == BluetoothProfile.STATE_CONNECTED) {
mConnected = true;
mUiCallback.uiDeviceConnected(mBluetoothGatt, mBluetoothDevice);
// now we can start talking with the device, e.g.
mBluetoothGatt.readRemoteRssi();
// response will be delivered to callback object!
// in our case we would also like automatically to call for services discovery
startServicesDiscovery();
// and we also want to get RSSI value to be updated periodically
startMonitoringRssiValue();
}
else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
mConnected = false;
mUiCallback.uiDeviceDisconnected(mBluetoothGatt, mBluetoothDevice);
}
}
#Override
public void onServicesDiscovered(BluetoothGatt gatt, int status) {
if (status == BluetoothGatt.GATT_SUCCESS) {
// now, when services discovery is finished, we can call getServices() for Gatt
getSupportedServices();
}
}
#Override
public void onCharacteristicRead(BluetoothGatt gatt,
BluetoothGattCharacteristic characteristic,
int status)
{
// we got response regarding our request to fetch characteristic value
if (status == BluetoothGatt.GATT_SUCCESS) {
// and it success, so we can get the value
getCharacteristicValue(characteristic);
}
}
#Override
public void onCharacteristicChanged(BluetoothGatt gatt,
BluetoothGattCharacteristic characteristic)
{
// characteristic's value was updated due to enabled notification, lets get this value
// the value itself will be reported to the UI inside getCharacteristicValue
getCharacteristicValue(characteristic);
// also, notify UI that notification are enabled for particular characteristic
mUiCallback.uiGotNotification(mBluetoothGatt, mBluetoothDevice, mBluetoothSelectedService, characteristic);
}
#Override
public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
String deviceName = gatt.getDevice().getName();
String serviceName = BleNamesResolver.resolveServiceName(characteristic.getService().getUuid().toString().toLowerCase(Locale.getDefault()));
String charName = BleNamesResolver.resolveCharacteristicName(characteristic.getUuid().toString().toLowerCase(Locale.getDefault()));
String description = "Device: " + deviceName + " Service: " + serviceName + " Characteristic: " + charName;
// we got response regarding our request to write new value to the characteristic
// let see if it failed or not
if(status == BluetoothGatt.GATT_SUCCESS) {
mUiCallback.uiSuccessfulWrite(mBluetoothGatt, mBluetoothDevice, mBluetoothSelectedService, characteristic, description);
}
else {
mUiCallback.uiFailedWrite(mBluetoothGatt, mBluetoothDevice, mBluetoothSelectedService, characteristic, description + " STATUS = " + status);
}
};
#Override
public void onReadRemoteRssi(BluetoothGatt gatt, int rssi, int status) {
if(status == BluetoothGatt.GATT_SUCCESS) {
// we got new value of RSSI of the connection, pass it to the UI
mUiCallback.uiNewRssiAvailable(mBluetoothGatt, mBluetoothDevice, rssi);
}
};
};
private Activity mParent = null;
private boolean mConnected = false;
private String mDeviceAddress = "";
private BluetoothManager mBluetoothManager = null;
private BluetoothAdapter mBluetoothAdapter = null;
private BluetoothDevice mBluetoothDevice = null;
private BluetoothGatt mBluetoothGatt = null;
private BluetoothGattService mBluetoothSelectedService = null;
private List<BluetoothGattService> mBluetoothGattServices = null;
private Handler mTimerHandler = new Handler();
private boolean mTimerEnabled = false;
}
replace your startScanning method of BleWrapper class to below code and it should work.
public void startScanning() {
if(mBluetoothAdapter == null)
mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
mBluetoothAdapter.startLeScan(mDeviceFoundCallback);
}
mBluetoothAdapter is null and therefore the startLeScan method call fails
Hope this helps :)