How do you detect if an Android device has a camera? - java

I want to disable camera functionality in my app, if the device does not have a camera. However, I seem to have stumbled into a bug when doing so.
According to the official Android Developer docs, I can use hasSystemFeature() to detect device features at runtime, as shown below:
boolean hasAnyCamera = this.getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA_ANY);
Log.i(LOG_TAG, "hasAnyCamera = " + hasAnyCamera);
boolean hasBackCamera = this.getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA);
Log.i(LOG_TAG, "hasBackCamera = " + hasBackCamera);
However, I tried to create an emulator without a front-facing or back-facing camera, and it still returns true for both checks. Is there some other way that I can detect the camera in Android?
Relevant documentation:
Take Photos > Request the camera feature
PackageManager.hasSystemFeature()
PackageManager.FEATURE_CAMERA_ANY
PackageManager.FEATURE_CAMERA

After some research, it appears that this is a known bug, which has been unresolved for 10+ years now.
See: Emulator does not honour Camera support flag
It seems that the only know work-around is to detect the number of cameras using the Camera.getNumberOfCameras() method, which has been deprecated since Android 5.0.
boolean hasCamera = Camera.getNumberOfCameras() > 0;
Log.i(LOG_TAG, "hasCamera = " + hasCamera);
The above method seems to work on my emulator, despite the warning that it is deprecated.
Now according to the docs for Camera.getNumberOfCameras():
"The return value of [getNumberOfCameras()] might change dynamically if the device supports external cameras and an external camera is connected or disconnected."
So perhaps hasSystemFeature() always returns true, because the device might later have an external camera attached?

An emulator started by using a hardware configuration which enables or disables some features in runtime. It would be possible to add / change some configuration for your emulator.
I think, It is not removing camera hardware, just disables them. I created an emulator as you mentionned and there was no camera. When I changed the configuration I was able to use the camera. However, hasSystemFeature returned true for both situtation.
When you create an emulator, the settings you choose is written in hardware-qemu.ini for that AVD. You can find it in ~/.android/avd/YOUR_EMULATOR.avd/hardware-qemu.ini. You can get exact path in show on disk on Device Manager.

Related

How to use "disabledReason" in Android Management API?

So, I'm being able to disable the device using the AMAPI(Android Management API), but I want to let the user know why his device is being disabled, so I started looking for a way to show why the device was disabled and I found the "disabledReason" property, but it's not working, or I don't know where is displaying the reason that I set.
If you know where is displaying or how to display the disabledReason I will be very thankful.
device.setState("DISABLED");
UserFacingMessage reason = new UserFacingMessage();
reason.setDefaultMessage("Reason why your device was disabled...");
device.setDisabledReason(reason);
androidManagementClient
.enterprises()
.devices()
.patch(device.getName(), device).execute();
There is an issue when using the disabledReason field in AMAPI. There is no message displayed on the device indicating why the device is disabled, and this issue has not yet been resolved, but you can see it on your JSON response if you set a customized disabledReason.defaultMessage.

Play Core in app update giving UPDATE_NOT_AVAILABLE on production release

I have integrated the play core in-app update it's working fine in the testing track but when a release is published in the production track it's always giving the UPDATE_NOT_AVAILABLE flag. I think the problem might be because Timed Publishing/Publishing Overview is enabled. Is there any fix or any setting which I have to change from the play console itself? or do I have to implement something in my android end?
here is the Implemented code-
AppUpdateManager appUpdateManager = AppUpdateManagerFactory.create(context);
Task<AppUpdateInfo> appUpdateInfoTask = appUpdateManager.getAppUpdateInfo();
appUpdateInfoTask.addOnCompleteListener(listener -> {
if (listener.isSuccessful()) {
Log.d(TAG, "Update Available " + (listener.getResult().updateAvailability() == UpdateAvailability.UPDATE_AVAILABLE)); // returns false
Log.d(TAG, "Update Allowed" + listener.getResult().isUpdateTypeAllowed(AppUpdateType.IMMEDIATE)); // returns false
Log.d(TAG, "Update Availibility" + listener.getResult().updateAvailability()); // returns 1 that is UPDATE_NOT_AVAILABLE
if (listener.getResult().updateAvailability() == UpdateAvailability.UPDATE_AVAILABLE
&& listener.getResult().isUpdateTypeAllowed(AppUpdateType.IMMEDIATE)) {
try {
appUpdateManager.startUpdateFlowForResult(
listener.getResult(),
AppUpdateType.IMMEDIATE,
activity,
1001);
} catch (IntentSender.SendIntentException e) {
Log.e(TAG, "showPopup: ", e);
dialog.show();
}
} else {
Log.d(TAG, "no update: " + listener.getResult());
dialog.show();
}
} else {
Log.e(TAG, "no update: ", listener.getException());
}
});
I had a similar problem yesterday, and in my desperation put what should of been a comment about having a similar problem as an answer... #Natty showed me the error of my ways, and I felt bad, so made sure I'd come back with a better actual answer:
I discovered that the likely culprit is google play app signing. It looks like they changed things in August 2021 so as the default is to allow google to manage the app signing, which means your app is signed by a different key with each release, and thus your releases have different signatures, and it won't find the updates. The exceptions is internal app sharing.
Sadly, there appears to be no way to opt-out
You can't disable App Signing after being activated as you can read in the image below:
see this post
It get worse... because ya know google... your can't delete you app either, the only thing you can do is to
unpublish the app.
Then create a new version on the google play store. Change the applicationId to some slight variant so it counts as a different app.
When adding you first release for the new app in any track, make sure to select the appropriate option for app signing above where you drop in the app bundle
click use different key
Either use a keystore generated from android studio or make a new one. From then on google will use that same keystore for signing all future releases of the app.
I even went back and double checked this was the case for me, by checking the older version of the app and the new version on internal testing tracks. Indeed, the new version using the same app signing keystore works for in-app updates, but the older version with google app signing did not.
Just bear in mind a whole new app has to go through the review process, which can take 1-3 days for new apps (seemed to be much quicker once the initial review is done)

Missing cameras in cameraIdList

halo,
i have an issue with my app i'm trying to get all cameras of my phone.
my app detects only 2 cameras
i'm using :
val manager = context!!.getSystemService(Context.CAMERA_SERVICE) as CameraManager
var listOfCamer as = manager.cameraIdList;
When I tried openCamera (https://opencamera.sourceforge.io/help.html), it detects 4 cameras!
Or we use the same method/class
CameraManager manager = (CameraManager)context.getSystemService(Context.CAMERA_SERVICE);
try {
return manager.getCameraIdList().length;
}
am I missing some config / permissions?
(Android 10 or higher) Hide physical sub-cameras from getCameraIdList. This reduces the number of cameras that can be directly opened by apps, eliminating the need for apps to have complex camera selection logic.
I think this is the reason. Try the same with old Camera API (though it is deprecated now), perhaps, it would allow you to see all your 4 devices.

turning airplane mode on and off and how to turn on mobile data in android

I use this code for turning on airplane mode on android:
Settings.System.putInt(context.getContentResolver(), Settings.System.AIRPLANE_MODE_ON, 1);
newIntent = new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED);
newIntent.putExtra("state", true);
context.sendBroadcast(newIntent);
It works on API 16 but on API 23 doesn't work and application terminates. What should I do?
And second question is: How to turn on and off mobile data in android without root?
As per docs Settings.System.AIRPLANE_MODE_ON constant is deprecated since API Level 17 and onwards, That's why your application is crashing.
You should use Settings.Global.AIRPLANE_MODE_ON instead.

Bluetooth on Android - how to connect to the CORRECT bluetooth device?

I'm writing a program that speaks with an external accessory over rfcomm.
My problem is that I don't know what the correct way of identifying my device is.
the way I do it now is like this:
Set<BluetoothDevice> pairedDevices = mBluetoothAdapter
.getBondedDevices();
for (BluetoothDevice device : pairedDevices) {
if (device.getName().equals(MY_DEVICE_NAME)) {
this.myDevice = device;
break;
}
}
This method however relies on the name of the device which to me seems dirty and bad :)
is there a better way to do this?
I tried looking at all the methods of BluetoothDevice but none seemed to help - is the name really the best way to do it?
I saw that in some places people say that I should use UUIDs but that is used to open the socket to the device once I have it:
_socket = myDevice.createRfcommSocketToServiceRecord(MY_UUID);
is there a better way to do it?
Devices of the same kind/functionality and/or brand will usually have a similar name. For example, all RN-41 devices from Roving Networks have the following name:
FireFly-XXXX
where XXXX is the last 4 digits of the device's address. That means you can use the following to connect to any of them:
if (device.getName().startsWith("FireFly-")) {
this.myDevice = device;
break;
}
This is exactly what I do in my app and haven't found any more reliable/consistent way to do it. As a generalization, you should be able to use a regular pattern if the name in the devices you are interested in is any more complex than the example above.
You can use myDevice.getAddress() to get the bluetooth device address and compare, it will always be unique (unlike name)
You can also use BluetoothDevice.getBluetoothClass() for at narrowing down which devices might be relevant.
BluetoothClass.getMajorDeviceClass() will tell you roughly what kind of device it is - a phone, a computer, an audio or video device, or whatever.
BluetoothClass.hasService() further specifies some capabilities of the device.
Within each of the major classes, some minor classes are defined - what kind of computer / audio-video device / phone / health equipment etc. it is.
Also, on recent versions of the Android platform (API level 15+), you can query for the service records of a device, without having to connect to it. See BluetoothDevice.fetchUuidsWithSdp() and BluetoothDevice.getUuids().

Categories