I am using a webrtc sample Code to stream from my Android device to a Webpage.
The sample Code does not have the function to Switch the camera. I tried to solve it but I failed. The sample uses a VideoCapturerAndroid class all of the Suggestions I found switching the camera used a different type.
The main part of the sample Looks like this:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_video_chat);
ButterKnife.bind(this);
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
Bundle extras = getIntent().getExtras();
if (extras == null || !extras.containsKey(Constants.USER_NAME)) {
Intent intent = new Intent(this, MainActivity.class);
startActivity(intent);
Toast.makeText(this, "Need to pass username to VideoChatActivity in intent extras (Constants.USER_NAME).", Toast.LENGTH_SHORT).show();
finish();
return;
}
this.username = extras.getString(Constants.USER_NAME, "");
this.mCallStatus = (TextView) findViewById(R.id.call_status);
// First, we initiate the PeerConnectionFactory with our application context and some options.
PeerConnectionFactory.initializeAndroidGlobals(
this, // Context
true, // Audio Enabled
true, // Video Enabled
true, // Hardware Acceleration Enabled
null); // Render EGL Context
pcFactory = new PeerConnectionFactory();
this.pnRTCClient = new PnRTCClient(Constants.PUB_KEY, Constants.SUB_KEY, this.username);
List<PeerConnection.IceServer> servers = getXirSysIceServers();
if (!servers.isEmpty()) {
this.pnRTCClient.setSignalParams(new de.kevingleason.pnwebrtc.PnSignalingParams());
}
backFacingCam = VideoCapturerAndroid.getNameOfBackFacingDevice();
frontFacingCam = VideoCapturerAndroid.getNameOfFrontFacingDevice();
// Creates a VideoCapturerAndroid instance for the device name
//VideoCapturer capturer = VideoCapturerAndroid.create(frontFacingCam);
capturer = VideoCapturerAndroid.create(facingCam);
// First create a Video Source, then we can make a Video Track
localVideoSource = pcFactory.createVideoSource(capturer, this.pnRTCClient.videoConstraints());
localVideoTrack = pcFactory.createVideoTrack(VIDEO_TRACK_ID, localVideoSource);
// First we create an AudioSource then we can create our AudioTrack
AudioSource audioSource = pcFactory.createAudioSource(this.pnRTCClient.audioConstraints());
AudioTrack localAudioTrack = pcFactory.createAudioTrack(AUDIO_TRACK_ID, audioSource);
// To create our VideoRenderer, we can use the included VideoRendererGui for simplicity
// First we need to set the GLSurfaceView that it should render to
this.videoView = (GLSurfaceView) findViewById(R.id.gl_surface);
// Then we set that view, and pass a Runnable to run once the surface is ready
VideoRendererGui.setView(videoView, null);
// Now that VideoRendererGui is ready, we can get our VideoRenderer.
// IN THIS ORDER. Effects which is on top or bottom
remoteRender = VideoRendererGui.create(0, 0, 100, 100, VideoRendererGui.ScalingType.SCALE_ASPECT_FILL, false);
localRender = VideoRendererGui.create(0, 0, 100, 100, VideoRendererGui.ScalingType.SCALE_ASPECT_FILL, true);
// We start out with an empty MediaStream object, created with help from our PeerConnectionFactory
// Note that LOCAL_MEDIA_STREAM_ID can be any string
MediaStream mediaStream = pcFactory.createLocalMediaStream(LOCAL_MEDIA_STREAM_ID);
// Now we can add our tracks.
mediaStream.addTrack(localVideoTrack);
mediaStream.addTrack(localAudioTrack);
// First attach the RTC Listener so that callback events will be triggered
this.pnRTCClient.attachRTCListener(new DemoRTCListener());
// Then attach your local media stream to the PnRTCClient.
// This will trigger the onLocalStream callback.
this.pnRTCClient.attachLocalMediaStream(mediaStream);
this.pnRTCClient.listenOn(username);
this.pnRTCClient.setMaxConnections(1);
....
}
Currently I am hardcoding which camera shall be used:
backFacingCam = VideoCapturerAndroid.getNameOfBackFacingDevice();
frontFacingCam = VideoCapturerAndroid.getNameOfFrontFacingDevice();
This is my button which shall Switch the camera:
#OnClick(R.id.switchCameraBtn)
public void switchCameraBtn(View view) {
Log.e("Test", "switch camera button clicked");
this.mCallStatus = (TextView) findViewById(R.id.call_status);
}
I also tried to restart the activity and give a Parameter which tells one that the other camera shall be used, but I would like to Keep the stream fluent and not to restart the activity.
You are using a very old implementation of WebRTC in android. VideoRendererGui is removed from new WebRTC library. I strongly suggest you to always use the newest version possible of google WebRTC from here which is at the time of writing is 1.0.22512
compile 'org.webrtc:google-webrtc:1.0.22512'
You can check the Android implementation of the newest library from official WebRTC Chromium project site here. Check out the other classes too.
With the new library, you should create a VideoCapturer in the following way.
private void createVideoCapturer() {
VideoCapturer videoCapturer;
if (Camera2Enumerator.isSupported(this)) {
videoCapturer = createCameraCapturer(new Camera2Enumerator(this));
} else {
videoCapturer = createCameraCapturer(new Camera1Enumerator(false));
}
}
createCameraCapturer() Method :
private VideoCapturer createCameraCapturer(CameraEnumerator enumerator) {
final String[] deviceNames = enumerator.getDeviceNames();
// First, try to find front facing camera
for (String deviceName : deviceNames) {
if (enumerator.isFrontFacing(deviceName)) {
VideoCapturer videoCapturer = enumerator.createCapturer(deviceName, null);
if (videoCapturer != null) {
return videoCapturer;
}
}
}
// Front facing camera not found, try something else
for (String deviceName : deviceNames) {
if (!enumerator.isFrontFacing(deviceName)) {
CameraVideoCapturer videoCapturer = enumerator.createCapturer(deviceName, null);
if (videoCapturer != null) {
return videoCapturer;
}
}
}
return null;
}
and call switchCamera() method from your class whenever you want to switch between front and back camera.
private void switchCamera() {
if (videoCapturer != null) {
if (videoCapturer instanceof CameraVideoCapturer) {
CameraVideoCapturer cameraVideoCapturer = (CameraVideoCapturer) videoCapturer;
cameraVideoCapturer.switchCamera(null);
} else {
// Will not switch camera, video capturer is not a camera
}
}
}
Related
I'm trying to handle the event when a user presses "ok" or "cancel" on the automatic permission dialog presented when I connect a "known" USB device to the android phone.
I'm using the android.usb.host library and can send and receive between the android phone and the device. Futhermore do I handle the "USB_DEVICE_ATTACHED" and "USB_DEVICE_DETACHED" using a BroadcastReceiver without any problems.
I want to enable a sort of "autoconnect" feature and therefore I need to know when the user has pressed "ok" in the automatically displayed permission dialog, but I can't find anything online at all. All I find is "bypass dialog", but this is not what I want or need.
When I connect the usb device to the android phone, a permission dialog is automatically displayed because I use the "device_filter.xml" solution from androids documentation which can be seen here Android Usb Docs.
This is how I handle the USB_DEVICE_ATTATCHED and USB_DEVICE_DETACHED events:
public NativeUsbService(ReactApplicationContext reactContext) {
...
// register device attached/detached event listeners
IntentFilter filter = new IntentFilter();
filter.addAction(UsbManager.ACTION_USB_DEVICE_ATTACHED);
filter.addAction(UsbManager.ACTION_USB_DEVICE_DETACHED);
reactContext.registerReceiver(usbReceiver, filter);
...
}
And then the Broadcast Receiver:
private final BroadcastReceiver usbReceiver = new BroadcastReceiver() {
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (action.equals(UsbManager.ACTION_USB_DEVICE_ATTACHED)) {
synchronized (this) {
UsbDevice device = (UsbDevice) intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
if(device != null){
usbDevice = device;
} else {
Log.d(TAG, "onReceive: DEVICE WAS ATTACHED AND WAS NULL :(");
}
}
} else if (action.equals(UsbManager.ACTION_USB_DEVICE_DETACHED)) {
Log.d(TAG, "onReceive: Device was detached!");
if(connection != null) {
connection.releaseInterface(usbDeviceInterface);
connection.close();
}
connection = null;
usbDevice = null;
endpointIn = null;
endpointOut = null;
}
}
};
I have tried multiple different approaches, but nothing has worked.
I have tried getting the user response in from the intent, like with a manual permission request like below:
private final BroadcastReceiver usbReceiver = new BroadcastReceiver() {
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (action.equals(UsbManager.ACTION_USB_DEVICE_ATTACHED)) {
synchronized (this) {
UsbDevice device = (UsbDevice) intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
if(device != null){
usbDevice = device;
// THIS DOES NOT WORK ↓↓↓
if(intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false)) {
// The code never gets here...
}
} else {
Log.d(TAG, "onReceive: DEVICE WAS ATTACHED AND WAS NULL :(");
sendEvent("onDeviceAttached", false);
}
}
} else if (action.equals(UsbManager.ACTION_USB_DEVICE_DETACHED)) {
if(connection != null) {
connection.releaseInterface(usbDeviceInterface);
connection.close();
}
connection = null;
usbDevice = null;
endpointIn = null;
endpointOut = null;
}
}
};
I have also tried by adding a usb permission listener to the broadcast receiver by first adding the action name to my class variables:
private static final String ACTION_USB_PERMISSION = "com.android.example.USB_PERMISSION";
Then adding the action to my intent filter like so:
public NativeUsbService(ReactApplicationContext reactContext) {
// register device attached/detached event listeners
IntentFilter filter = new IntentFilter();
filter.addAction(UsbManager.ACTION_USB_DEVICE_ATTACHED);
filter.addAction(UsbManager.ACTION_USB_DEVICE_DETACHED);
filter.addAction(ACTION_USB_PERMISSION); // added action to my intent filter
reactContext.registerReceiver(usbReceiver, filter);
}
And finally reacting to the action like so:
private final BroadcastReceiver usbReceiver = new BroadcastReceiver() {
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (action.equals(UsbManager.ACTION_USB_DEVICE_ATTACHED)) {
synchronized (this) {
UsbDevice device = (UsbDevice) intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
if(device != null){
usbDevice = device;
}
}
} else if (action.equals(UsbManager.ACTION_USB_DEVICE_DETACHED)) {
Log.d(TAG, "onReceive: Device was detached!");
if(connection != null) {
connection.releaseInterface(usbDeviceInterface);
connection.close();
}
connection = null;
usbDevice = null;
endpointIn = null;
endpointOut = null;
sendEvent("onDeviceDetached", true);
}
else if (action.equals(ACTION_USB_PERMISSION)) {
Log.d(TAG, "onReceive: ACTION_USB_PERMISSION");
if(intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false)) {
Log.d(TAG, "onReceive: EXTRA_PERMISSION_GRANTED = true");
} else Log.d(TAG, "onReceive: EXTRA_PERMISSION_GRANTED = false");
}
}
};
Please make me aware of any missing information.
Any help is greatly appreciated.
Answering my own question in case someone else is facing the same issue.
I though about manually requesting the permission again, after permission was granted, since it is possible to handle this manual permission request when user presses an option in the dialog. I discarded this idea, not because it wouldn't work, but because I saw it as unecessary for the user to also have to press another dialog after the initial (automatic dialog).
I must add that I have not implemented this solution, so I do not know with certainty that it would prompt the user again, but I have had trouble with the manual permission request previously. If you want to try this approach the method belongs to the UsbManager class and is invoke like so usbManger.requestPermission(usbDevice).
I ended up with a solution where I start a thread which runs a loop calling usbManager.hasPermission(usbDevice) until it has permission and then emits an event (emitting this event is my use case, implement it how you like).
The solution can be seen here:
import android.hardware.usb.UsbDevice;
import android.hardware.usb.UsbDeviceConnection;
import android.hardware.usb.UsbManager;
import com.facebook.react.bridge.ReactApplicationContext;
...
private static volatile boolean permissionThreadShouldStop = false;
private static Thread activePermissionThread = null;
...
public static void usbPermissionEventEmitter(ReactApplicationContext reactContext, UsbManager usbManager, UsbDevice usbDevice) {
if((activePermissionThread != null && activePermissionThread.isAlive())) {
activePermissionThread.interrupt();
}
permissionThreadShouldStop = false;
activePermissionThread = new Thread(new Runnable() {
#Override
public void run() {
while (!usbManager.hasPermission(usbDevice) && !permissionThreadShouldStop) {
try {
Thread.sleep(30);
} catch (InterruptedException e) {
e.printStackTrace();
break;
}
}
if(usbManager.hasPermission(usbDevice)) {
sendEvent(reactContext, "onUsbPermissionGranted", true);
}
}
});
activePermissionThread.start();
}
The ReactApplicationContext can be swapped with the normal android context. But this is for a react native module, so I use the reactContext.
I hope this will be helpful for someone, because i'm honestly really surpriced how scarse the android documentation is in regards to implementing Usb functionality using the android.hardware.usb library.
Also in general when searching for information online, I have often found myself lost since there is very little information on this subject.
I am a complete novice in Java and Android. I am trying to create a test app to listen for BLE and BT devices nearby. I have another device where I wrote some logic to broadcast its BLE beacons. I verified it using a playstore app. Now I am trying to write my own app on Android.
I have been reading the Android developer pages for guidance. I have literally followed every step of the following pages
https://developer.android.com/guide/topics/connectivity/bluetooth/setup
https://developer.android.com/guide/topics/connectivity/bluetooth/permissions
https://developer.android.com/guide/topics/connectivity/bluetooth/find-bluetooth-devices
https://developer.android.com/guide/topics/connectivity/bluetooth/find-ble-devices
Also, Note that I have used BARE MINIMUM CODE from the Android Developers page So here is what I have done.
1. First off I have added my permissions under AndroidManifest
Note1 : I am deploying this app to My phone running Android 11
Note2 : All this code is written inside MainActivity. I have not created any other activity class
<uses-permission android:name="android.permission.BLUETOOTH" android:maxSdkVersion="30" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" android:maxSdkVersion="30" />
<uses-permission android:name="android.permission.BLUETOOTH_SCAN" />
<uses-permission android:name="android.permission.BLUETOOTH_ADVERTISE" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
2. Next I check if my BT is enabled.
if (bluetoothAdapter == null) {
blefinder.append("\nDEVICE DOES NOT SUPPORT BLUETOOTH");
}
else {
blefinder.append("\nDEVICE SUPPORTS BLUETOOTH");
}
I get the success message that BT is of course enabled
3. Next I check if my device supports BLE
if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) {
blefinder.append("\nBLE NOT SUPPORTED ON THIS DEVICE : ");
finish();
}
else{
blefinder.append("\nBLE IS SUPPORTED ON THIS DEVICE : ");
}
I get the message that BLE is supported
4. Next I list my already paired/bonded devices
For this I call ListPairedAndBondedDevices(); in onCreate() itself right after the above steps. Function Definition Below.
private void ListPairedAndBondedDevices(){
#SuppressLint("MissingPermission") Set<BluetoothDevice> pairedDevices = bluetoothAdapter.getBondedDevices();
if (pairedDevices.size() > 0) {
// There are paired devices. Get the name and address of each paired device.
blefinder.append("\nPAIRED/BONDED DEVICES");
for (BluetoothDevice device : pairedDevices) {
blefinder.append("\n" + device.getName() + " | " + device.getAddress());
}
}
}
This also works like a charm and prints out my paired devices. The next 2 parts is where I face the problem.
5. The Problem Step | Part 1:
Here I register a Broadcast receiver to discover all BT devices in the vicinity. I've unbonded my BT headphones and kept it in pairing mode to verify this.
ListPairedAndBondedDevices(); // From previous code snippet
IntentFilter filter = new IntentFilter(BluetoothDevice.ACTION_FOUND); // New code statement
registerReceiver(BTReceiver, filter);// New code statement
Broadcast Receiver implementation
private final BroadcastReceiver BTReceiver = new BroadcastReceiver() {
#SuppressLint("MissingPermission")
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (BluetoothDevice.ACTION_FOUND.equals(action)) {
// Discovery has found a device. Get the BluetoothDevice
// object and its info from the Intent.
BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
blefinder.append("\n" + device.getName() + " | " + device.getAddress());
}
}
};
So This part didn't Work :(
If you see above, I am registering the BTReceiver in onCreate right after listing the already paired devices (by calling ListPairedAndBondedDevices()).
When I ran the debugger, this broadcast receiver never gets called.
6. The Problem Step | Part 2:
Right after this I try to scan for BLE Devices as well by callin scanLeDevice()
ListPairedAndBondedDevices(); // From previous snippet
IntentFilter filter = new IntentFilter(BluetoothDevice.ACTION_FOUND); // From previous snippet
registerReceiver(BTReceiver, filter);// From previous snippet
scanLeDevice(); // ---------------->>> CALLING THIS FUNCTION TO SCAN FOR BLE DEVICES
Implementation of scanLeDevice()
private void scanLeDevice() {
if (!scanning) {
// Stops scanning after a predefined scan period.
handler.postDelayed(new Runnable() {
#Override
public void run() {
scanning = false;
bluetoothLeScanner.stopScan(leScanCallback);
blefinder.append("\nSTOPPING BLE SCAN... TIMEOUT REACHED");
}
}, SCAN_PERIOD);
scanning = true;
bluetoothLeScanner.startScan(leScanCallback);
} else {
scanning = false;
bluetoothLeScanner.stopScan(leScanCallback);
blefinder.append("\nSTOPPING BLE SCAN");
}
}
Unfortunately this also fails. The debugger tells me that this part of the code is getting called.
And after 30 seconds of SCAN_PERIOD (The TIMEOUT that I've set), I get the message that the scanning has stopped (STOPPING BLE SCAN)
Now I have implemented the leScanCallback as well (i.e the Device Scan Callback)
private ScanCallback leScanCallback =
new ScanCallback() {
#Override
public void onScanResult(int callbackType, ScanResult result) {
super.onScanResult(callbackType, result);
blefinder.append("SOMETHING GOT SCANNED?");
blefinder.append("\n"+result.getDevice().toString());
// leDeviceListAdapter.addDevice(result.getDevice());
// leDeviceListAdapter.notifyDataSetChanged();
}
};
Notice that I am not using a ListAdapter since I have no idea about that concept. Hence for starters I am just trying to dump the results in a TextView represented by blefinder . This blefinder prints all the other texts so there is nothing wrong with that TextView variable. When I ran using the, debugger, it is not entering into the leScanCallback piece of code definition at all, even after 30 seconds, after scanLeDevice() function is executed.
I am a little lost here. Is there something I may be missing or doing wrong. It is supposed to be a simple, list the ble/bt devices around my vicinity.
I am happy to share any further information if I have missed. Just let me know in the comments.
Assuming you've done with the permissions that I've mentioned in the comments, we can implement a clean bluetooth LE scanner object and then use it in the UI.
First we implement a result consumer interface in order to deliver the results to the consumers which call the BleScanner.scan() method.
public interface ScanResultConsumer {
public void onDeviceFound(BluetoothDevice device, byte[] scanRecord, int rssi);
public void onScanningStarted();
public void onScanningStopped();
}
Now we need to implement the scanner object that manages the scanning events:
public class BleScanner {
private static final String TAG = BleScanner.class.getSimpleName();
private BluetoothLeScanner leScanner = null;
private BluetoothAdapter bleAdapter = null;
private Handler uiHandler = new Handler(Looper.getMainLooper);
private ScanResultConsumer scanResultConsumer;
private boolean scanning = false;
private final ArrayList<BluetoothDevice> foundDeviceList = new ArrayList<>();
public BleScanner(Context context) {
final BluetoothManager bluetoothManager = (BluetoothManager)
context.getSystemService(Context.BLUETOOTH_SERVICE);
bleAdapter = bluetoothManager.getAdapter();
if(bleAdapter == null) {
Log.d(TAG, "No bluetooth hardware.");
}
else if(!bleAdapter.isEnabled()){
Log.d(TAG, "Blutooth is off.");
}
}
public void scan(ScanResultConsumer scanResultConsumer, long scanTime){
foundDeviceList.clear();
if (scanning){
Log.d(TAG, "Already scanning.");
return;
}
Log.d(TAG, "Scanning...");
if(leScanner == null){
leScanner = bleAdapter.getBluetoothLeScanner();
}
if(scanTimeMs > 0) {
uiHandler.postDelayed(()-> {
if (scanning) {
Log.d(TAG, "Scanning is stopping.");
if(leScanner != null)
leScanner.stopScan(scanCallBack);
else
Log.d(TAG,"Scanner null");
setScanning(false);
}
}, scanTimeMs);
}
this.scanResultConsumer = scanResultConsumer;
leScanner.startScan(scanCallBack);
setScanning(true);
}
private final ScanCallback scanCallBack = new ScanCallback() {
#Override
public void onScanResult(int callbackType, ScanResult result) {
super.onScanResult(callbackType, result);
if (!scanning){
return;
}
if(foundDeviceList.contains(result.getDevice())) {
// This device has already been found
return;
}
// New device found, add it to the list in order to prevent duplications
foundDeviceList.add(result.getDevice());
if(scanResultConsumer != null) {
uiHandler.post(() -> {
scanResultConsumer.onDeviceFound(result.getDevice(),
result.getScanRecord().getBytes(), result.getRssi());
});
}
}
};
public boolean isScanning(){
return scanning;
}
void setScanning(boolean scanning){
this.scanning = scanning;
uiHandler.post(() -> {
if(scanResultConsumer == null) return;
if(!scanning){
scanResultConsumer.onScanningStopped();
// Nullify the consumer in order to prevent UI crashes
scanResultConsumer = null;
} else{
scanResultConsumer.onScanningStarted();
}
});
}
}
Finally we can use this clean implementation in anywhere we need. But do note that a context must be provided in order to create a BleScanner object.
public class MainActivity extends AppCompatActivity {
private BleScanner bleScanner;
private Button buttonScan
// Other codes...
#Override
public void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Other codes...
bleScanner = new BleScanner(getApplicationContext());
// Other codes...
// For example if you want to start scanning on a button press
// Let's say you have a button called buttonScan and initiated it
buttonScan = findViewById(R.id.scan_button);
buttonScan.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
bleScanner.scan(new ScanResultConsumer {
#Override
public void onDeviceFound(BluetoothDevice device, byte[] scanRecord, int rssi) {
// TODO Here you have a newly found device, do something.
}
#Override
q public void onScanningStarted() {
// TODO Scanning has just started, you may want to make some UI changes.
}
#Override
public void onScanningStopped() {
// TODO Scanning has just stopped, you may want to make some UI changes.
}
});
}
});
}
}
Note: I written this code in a plain editor not in Android Studio. So there may be some errors, let me know if any.
First you should check if your app was granted the location permission(s) in the Settings app > Apps <your_app> > permissions. Some permissions (like ACCESS_*_LOCATION and BLUETOOTH_ADMIN) need to be requested at runtime and granted by the user through a popup. Normally you should get a SecurityException or a logcat warning when trying to execute code requiring permissions which your app doesn't have, but it's not uncommon for android to skip over error handling.
Consider using this method to start the scan in order check its result code for potential additional info about what is (not) going on.
You might also get some clues by logging all actions received in BTReceiver.onReceive(), not just action found.
Lastly check if the location settings on your device to ensure that bluetooth scanning is turned on (Settings app > location > wifi and bluetooth scanning )
I'm making an android application which uses it's own Camera activity. The images are then stored on the local SQLite database by using an IntentService.
Problem now is: the IntentService doesn't get started after the picture is taken. When using this IntentService on other occations it works perfect so I am at a loss on the why it is not working...
Below is the code used in my CameraActivity to take a picture and start the IntentService.
mPicture = getPictureCallback();
mRaw = getRawCallback();
mShutter = getShutterCallback();
mCamera.takePicture(mShutter, mRaw, mPicture);
private PictureCallback getPictureCallback() {
PictureCallback picture = new PictureCallback() {
#Override
public void onPictureTaken(byte[] data, Camera camera) {
Log.d(LOG_TAG, "Picture taken, starting service");
Intent databaseIntent = new Intent(MyCameraActivity.this, DBHandlerService.class);
databaseIntent.putExtra("image", data);
databaseIntent.putExtra("action", "save");
databaseIntent.putExtra(AppConstant.RESULT_RECEIVER, mReceiver);
startService(databaseIntent);
mPreview.refreshCamera(mCamera);
}
};
return picture;
}
The onHandleEvent:
#Override
protected void onHandleIntent(Intent intent) {
Log.d(LOG_TAG, "onHandle started");
Database db = getDB();
action = intent.getStringExtra("action");
Bundle resultbundle = new Bundle();
Log.d(LOG_TAG, "onHandleIntent action : " + action);
final ResultReceiver reciever = intent
.getParcelableExtra(AppConstant.RESULT_RECEIVER);
reciever.send(STATUS_RUNNING, Bundle.EMPTY);
//Different actions depending on the "action" string
if(action.equals("xxxxx"){
//perform action
} else {
// inform activity the requested action does not exist. When
// STATUS_ERROR is received they must check if errorcode is not
// null.
resultbundle.putString("errormsg", "No actions undertaken");
resultbundle.putInt("errorcode", 9999);
reciever.send(STATUS_ERROR, resultbundle);
}
}
I also use this IntentService to get all the pictures from the database. Since the DB stays empty no images are returned but every log gets printed.
If i try it to save the image the logs inside the IntentService are nevr printed.
If more code is needed, let me know.
Thanks in advance!
Thomas
On my on create function I give the location an ID. However my issue is when I go to add a photo to that page before I've saved it. I need to save the photo with the same ID as the page. So I create the ID on the on create then when i add a picture and come back as it's loaded the other intent it runs the code again and creates a new ID. How can I pass the ID through the photo and back.
The variable checkStationObjectID is set at the top of the file.
Heres my on create:
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_add_asset);
Bundle extras = getIntent().getExtras();
Log.e("CamDebug", "Loading extras");
if (extras != null) {
PreviousStationObjectID = extras.getString("StationObjectID");
editMode = extras.getBoolean("editMode");
editAreaObjectID = extras.getString("editAreaObjectID");
}
if(editMode){
checkStationObjectID = editAreaObjectID;
Log.e("CHECK ID - Edit mode", checkStationObjectID);
} else {
if(checkStationObjectID == null){
checkStationObjectID = NewAssetStationObjectID;
Log.e("CHECK ID = NEW ID", checkStationObjectID);
}
}
//Other items taken out
}
I launch the camera with:
public void startCamera()
{
File photo = null;
Intent intent = new Intent("android.media.action.IMAGE_CAPTURE");
timestamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
if (android.os.Environment.getExternalStorageState().equals(android.os.Environment.MEDIA_MOUNTED))
{
photo = new File(Global.Pictures + timestamp +".png");
}
else
{
photo = new File(getCacheDir(), FOLDER_NAME+File.separator+timestamp+".png");
}
if (photo != null)
{
intent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(photo));
selectedImageUri = Uri.fromFile(photo);
startActivityForResult(intent, CAPTURE_IMAGE_CALLBACK);
}
}
onCreate is called whenever your application is started. If you are calling another app like a camera, then the OS can kill your own Activity at any time while the camera Activity is running. This could be to recover memory or resources that the running app needs. Then when you exit the camera the OS will restart your Activity, which will call onCreate again.
I would suggest that you save the ID that you have generated into SharedPreferences just before you start the camera app. Then check for a saved ID in onActivityResult.
If you don't need to persist it you can create a Singleton object to act as a session and store the variable in there.. But I don't get why onCreate is executed again.. I thought the sub camera activity should return in onActivityResult? I pardon me if I misunderstood.
Or you can pass the var to the camera activity and pass it back when you are done with it.
I have a class, which creates conncection to NFC and two activites. Both of them creates an object of that class so they can connect to NFC.
Earlier it worked somehow but now I've got problem - my application doesn't do anything onNewIntent, even on the first activity. Instead of it, I can see "New tag collected" from build-in app called "Tags" (Nexus S).
What should I do?
class:
public NFCForegroundUtil(Activity activity)
{
super();
this.activity = activity;
mAdapter = NfcAdapter.getDefaultAdapter(activity
.getApplicationContext());
mPendingIntent = PendingIntent.getActivity(activity, 0, new Intent(
activity, activity.getClass())
.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP), 0);
IntentFilter ndef = new IntentFilter(NfcAdapter.ACTION_TAG_DISCOVERED);
IntentFilter ndef2 = new IntentFilter(NfcAdapter.ACTION_NDEF_DISCOVERED);
IntentFilter ndef3 = new IntentFilter(NfcAdapter.ACTION_TECH_DISCOVERED);
try
{
ndef2.addDataType("*/*");
}
catch (MalformedMimeTypeException e)
{
throw new RuntimeException("fail", e);
}
mFilters = new IntentFilter[] {ndef, ndef2, ndef3 };
mTechLists = new String[][] { new String[] {
// android.nfc.tech.NfcV.class.getName(),
android.nfc.tech.NfcA.class.getName(),
android.nfc.tech.IsoDep.class.getName() } };
mAdapter.enableForegroundDispatch(this, mPendingIntent, mFilters, mTechLists);
}
activity 1:
#Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
nfcForegroundUtil = new NFCForegroundUtil(this);
}
#Override
protected void onNewIntent(Intent intent)
{
super.onNewIntent(intent);
Intent i = new Intent(this, NfcDisplayLabelActivity2.class);
startActivity(i);
}
Go to settings -> Apps -> All -> Tags(in my case) -> disable
I had a similar problem when trying to open my app from an NFC tag. I had registered an intentfilter in my AndroidManifest.xml for the scheme "magicnfc" and yet it opened the Android OS Tags app instead of mine.
I discovered that the NFC intent (TECH_DISCOVERED in my case) had higher priority than a generic scheme-based intent filter. Because the Tags app registered TECH_DISCOVERED, it was getting opened instead of mine.
Luckily, apps can register for NDEF_DISCOVERED (a higher priority filter) and get opened instead of the Tags app.
That made my app open when I tapped the tag.
More info is here:
http://developer.android.com/guide/topics/connectivity/nfc/nfc.html
But I found that I had to override the function onNewIntent, with code like this:
if (NfcAdapter.ACTION_NDEF_DISCOVERED.equals(getIntent().getAction())) {
String uri = intent.getDataString();
Parcelable[] rawMsgs = intent.getParcelableArrayExtra(NfcAdapter.EXTRA_NDEF_MESSAGES);
if (rawMsgs != null) {
msgs = new NdefMessage[rawMsgs.length];
for (int i = 0; i < rawMsgs.length; i++) {
msgs[i] = (NdefMessage) rawMsgs[i];
}
}
}
For me, all I needed was:
String uri = intent.getDataString();
Good luck!
You can listen for all tags activated using the ACTION_TAG_DISCOVERED intent, rather than filtering for a specific one with the following code:
public NFCForegroundUtil(Activity activity)
{
super();
this.activity = activity;
mAdapter = NfcAdapter.getDefaultAdapter(activity
.getApplicationContext());
mPendingIntent = PendingIntent.getActivity(activity, 0, new Intent(
activity, activity.getClass())
.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP),
0);
// See below
mAdapter.enableForegroundDispatch(this, mPendingIntent, null, null);
}
From the NFCAdapter Documentation :
If you pass null for both the filters and techLists parameters that acts a wild card and will cause the foreground activity to receive all tags via the ACTION_TAG_DISCOVERED intent.
Your problem is when you initialise the intent i onNewIntent
The class should be itself, not the second class.
The right code should be :
#Override
protected void onNewIntent(Intent intent)
{
super.onNewIntent(intent);
Intent i = new Intent(this, NfcDisplayLabelActivity1.class);
startActivity(i);
}
I was seeing "New tag collected" from build-in app called "Tags" because my application didn't work properly.
When it works ok, it has higher priority than "Tags" and phone reads tags from my application. But when it works unproperly and phone collect a tag, "Tags" application is activated and "Tags" application talks to my device.
After repairing code, my app has higher priority and phone reads tags using my application.