I have a device of which I don't know if it has a vibrator.
Is there a way to query for the availability of the vibrator?
The Vibrator class does just that. It's hasVibrator() method returns a boolean indicating if vibrating is supported.
Get an instance of the Vibrator class which is a system service.
Query the Vibrator class using the hasVibrator() method.
String vs = Context.VIBRATOR_SERVICE;
Vibrator mVibrator = (Vibrator)getSystemService(vs);
boolean isVibrator = mVibrator.hasVibrator();
This may help for API<11:
Context.getSystemService() returns a service object or null if no service.
if ( getSystemService(VIBRATOR_SERVICE) != null ) {
//Vibrator exists
}
You need to support (at least) Android 3.0 (11 HoneyComb) before you can use hasVibrator().
Oddly, you can use vibrate() itself on any/all versions.
So the REAL question is: How do v1 - v10 detect if the device has vibrator?
(Or will nothing bad happen if you try to vibrate a device without a vibrator?)
Maybe it'll help someone.
Since I'm building a PWA, I ended up checking the screen size:
if (window.innerWidth < 600) {
window.navigator.vibrate(300);
}
Related
I've found out that Android 9 now shows info if accessibility service stopped working.
That was always a pain for developers who try to leverage accessibility API.
Accessibility looks like enabled, but service is stopped. And to get it back to work it is required to turn accessibility off and back on.
I would be glad if Google fixes that completely, but now they just show a hint that it's good to disable-enable it manually.
Not the best stuff, but at least something.
So, I've tried to find out how the system gets to know if the service is crashed. There happened to be a class called AccessibilityUtil and it contains hasServiceCrashed method.
Unfortunately, it checks a hidden field crashed from AccessibilityNodeInfo, which is not available for third-party developers (because of reflection denial) as well as on previous android versions.
So I'm wondering if there is an alternative way to get the info from the system which clarifies that my accessibility service is crashed/stopped working and user's action is required. Starting from Lollipop. Hints appreciated.
I came up with an idea to use a static boolean indicating the status of Accessibility Service and compare it with Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES. I've tested on multiple devices, haven't found any issue with this method.
1.Declare a static boolean in Accessibility Service.
private static boolean bServiceRunning = false;
2.In Accessibility Service, set the boolean value in onServiceConnected and onUnbind
#Override
protected void onServiceConnected() {
super.onServiceConnected();
bServiceRunning = true; //put this at the very beginning to reduce time gap
}
#Override
public boolean onUnbind(Intent intent) {
bServiceRunning = false;
return super.onUnbind(intent);
}
3.Create a static function in Accessibility Service
public static boolean bGetServiceStatus(){
return bServiceRunning;
}
With the boolean flag, I can know if the accessibility service is running in the desired state. When the service is being forced to stop, onUnbind will be called so the boolean value turns into false.
4.We use Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES to get accessibility service switch status
public static boolean bIsAccessibilityServiceEnabled(Context context, Class<?> accessibilityService) {
ComponentName expectedComponentName = new ComponentName(context, accessibilityService);
String strServicesSettingResult = Settings.Secure.getString(context.getContentResolver(), Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES);
if (strServicesSettingResult == null){
return false;
}
TextUtils.SimpleStringSplitter colonSplitter = new TextUtils.SimpleStringSplitter(':');
colonSplitter.setString(strServicesSettingResult);
while (colonSplitter.hasNext()) {
String strComponentName = colonSplitter.next();
ComponentName enabledService = ComponentName.unflattenFromString(strComponentName);
if (enabledService != null && enabledService.equals(expectedComponentName))
return true;
}
return false;
}
5.And this is what we want, we check with the above two methods the determine the real state of accessibility service.
public static int intIsAccessibilityServiceEnabled_WithCrashCheck(Context context, Class<?> accessibilityService){
//return 0 if Accessibility Service enabled and running
//return -1 if Accessibility Service disabled and not running
//return -2 if Accessibility Service enabled but stopped working or crashed
//first check Accessibility Service boolean
if(bGetServiceStatus()){
//service is running
return 0;
}else{
//service not running, now double check with Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES
boolean bResult = bIsAccessibilityServiceEnabled(context, accessibilityService);
if(!bResult){
//Accessibility Service is disabled
return -1;
}else{
//Accessibility Service is enabled, but service is not actually running, Accessibility Service is crashed
return -2;
}
}
}
Using "AccessibilityManager" also works the same, but I prefer a more "lightweight" version with static boolean for better performance.
Note: Using Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES without another doublecheck will cause a bug. The value is not synced. The result doesn't always represent the real service status. The following steps can create such a case :
Start an accessibility service then go to App settings force stop the App.
Now you can see the accessibility service switch is turned off. (Seems enough currently, but the next step will create a problem)
Start the accessibility service again,on Android 9.0+ devices you'll find out the switch is switched on, but the service is actually not running, and now it displays "Not
working. Tap for info."
2022/11/29 Edit: This bug is fixed according to this issue tracker. I can no longer reproduce this bug on my new Android 12 & 13 devices. However, devices with old Android firmware still has this bug. (The patch is also applied to the newest AVD images. To test this bug with AVD, now you must download old revisions of the AVD images.)
Android generally prevents apps from running if they crash repeatedly. This behavior for an accessibility service can obviously affect users who depend on the service, but since these services can effectively control the UI, having one that crashes repeatedly could also make the device unusable.
It hadn't occurred to me that anyone else would be interested in the crashed field in AccessibilityServiceInfo. I populated that field using data only available to the system unfortunately. I compare the list of services that are enabled with the list of those that are bound.
If you're interested if your service is prevented from running, you could probably do something similar by keeping track of when onBind and onUnbind is called and looking at the list of enabled services from AccessibilityManager.
I don't know if this is a solution. But I did find when it doesn't work: if I use "dumpsys accessibility", the services part is empty, looks like:
User state[attributes:{id=0,
currentUser=true,
touchExplorationEnabled=false,
displayMagnificationEnabled=false,
navBarMagnificationEnabled=false,
autoclickEnabled=false}
services:{}]
Maybe you can check if the services is empty.
I'm trying to implement haptic feedback when changing a value of a seekbar.
It works correctly on Android pre-P. On Android P it doesn't work at all.
Code:
private val vibrator = context.getSystemService(Context.VIBRATOR_SERVICE) as Vibrator?
private val effect by lazy { VibrationEffect.createOneShot(VIBRATION_DURATION, 50)}
...
fun vibrate() {
if (vibrator == null || !vibrator.hasVibrator()) {
return
}
vibrator.cancel()
vibrator.vibrate(effect)
It turns out the user has to enable Touch vibration in Settings -> Accessibility -> Vibration -> Touch vibration:
Without it, short vibration (less than 5 seconds) won't work. For me, this is not quite intuitive, so I decided to post it here
exactly what Mike said.
but for android Pie - Setting->Sounds and vibration -> System sounds and vibration -> Touch vibration
I'm developing an app where a user will not be able to use it if mock location setting is enabled using this piece of code
if (Settings.Secure.getString(context.getContentResolver(),
Settings.Secure.ALLOW_MOCK_LOCATION).equals("0"))
return false;
else
return true;
well it was working fine in most of my test devices from KitKat to Marshmallow systems, until I tried my app on this single device with Marshmallow OS, the mock setting is clearly OFF, but that code above keeps telling me that the mock setting is ON, is this a bug? or am i missing something here?
Checking out this answer from here.
boolean isMock = false;
if (android.os.Build.VERSION.SDK_INT >= 18) {
isMock = location.isFromMockProvider();
} else {
isMock = Settings.Secure.getString(context.getContentResolver(), Settings.Secure.ALLOW_MOCK_LOCATION).equals("0");
}
This might help you
According to the android developer reference: https://developer.android.com/reference/android/provider/Settings.Secure.html ALLOW_MOCK_LOCATION is now deprecated in SDK 23.
The default value you will get will always be 1 in marshmallow.
I have a problem identifying the data roaming setting in Android L. In previous versions of Android, I was able to access either Settings.Secure or Settings.Global (depending on the Android version), and get the setting.
But now, on Android L, this no longer works. Whether data roaming is on or off, the return from the Settings.Global is always 0.
Android L supports multi SIM out of the box, so, a new manager was created to handle this: SubscriptionManager. This subscription manager, handles the several settings of the several SIM cards in the form of SubInfoRecord classes. I can retrieve the settings per SIM card.
However, the dataRoaming filed inside that class is always 0 as well.
Does anyone know how can this be achieved on the new API?
My app is a system app that comes embedded in the phones from factory so, I should be able to access all the APIs available.
However, I've spent a long time looking in the source code but I found nothing. In the Settings.Global class there's no indication that that setting no longer works on Android.
Does anyone have a clue on where this setting was moved to?
Thanks in advance!
Check this DevicePolicyManager.setGlobalSetting
as from documentation this can only be called by device owner app.
Is your app is installed as device owner ?
If not you can check the following links
Create device owner without root
Create device owner with root
Do something like this
DevicePolicyManager manager = (DevicePolicyManager) getSystemService(Context.DEVICE_POLICY_SERVICE);
manager.setGlobalSetting(<Admin_Component>, Settings.Global.DATA_ROAMING, <value>);
Admin_Component: Component instance
Value: "0" for disable or "1" for enable
Since android 5.0, android supports multiple SIM cards, use the following code to check for data roaming.
public static boolean isDataRoamingEnabled(Context context) {
SubscriptionManager subMngr = (SubscriptionManager) context.getSystemService(Context.TELEPHONY_SUBSCRIPTION_SERVICE);
int id = SubscriptionManager.getDefaultDataSubscriptionId();
if (ActivityCompat.checkSelfPermission(context, Manifest.permission.READ_PHONE_STATE) != PackageManager.PERMISSION_GRANTED) {
return false;
}
SubscriptionInfo ino = subMngr.getActiveSubscriptionInfo(id);
if (ino == null)
return false;
return ino.getDataRoaming() == 1;
}
How can I check if headphones are currently plugged in. I don't want a broadcastreceiver which informs me when they have been connected to the device. I need something like:
if(/*headphone is connected*/)
...
It looks like you'll be interested in the isWiredHeadsetOn() method and isBluetoothA2dpOn() method of the AudioManager class.
However, the isWiredHeadsetOn() method is only available in Android 2.0 or later. (The isBluetoothA2dpOn() method has been available since Android 1.5.)
Use this code snippet
AudioManager am1 = (AudioManager)getSystemService(Context.AUDIO_SERVICE);
Log.i("am1.isWiredHeadsetOn()", am1.isWiredHeadsetOn()+"");
Log.i("am1.isMusicActive()", am1.isMusicActive()+"");
Log.i("am1.isSpeakerphoneOn()", am1.isSpeakerphoneOn()+"");
This seems to do the job at least on 1.6; not sure whether it's supported in later versions (a is an instance of AudioManager)
boolean headphones = (a.getRouting(a.getMode()) & AudioManager.ROUTE_HEADSET) == AudioManager.ROUTE_HEADSET;