I have an Android app with 9 buttons. This app runs on 2.36 and is the only app on the device (or at least the only app we let the user use - we ship the device with our code preinstalled as part of a suite of industrial products we sell.)
All the buttons go to the same handler and get sorted out there by their tag. The handler is specified in the XML:
<Button android:id="#+id/IdleButton"
android:layout_marginLeft="5dp"
android:background="#drawable/idle18pt_he_normal"
android:hapticFeedbackEnabled="true"
android:layout_width="92dp"
android:layout_height="92dp"
android:tag="0"
android:onClick="theButtonHandler">
</Button>
I want to enable haptic feedback, i.e., a vibration, when the user presses the button. Is there a way to do this just in the XML, or if not, is there a way to do it in my onClick() handler?
The web examples I've seen (e.g., http://androidcookbook.com/Recipe.seam?recipeId=1242 ) for haptic feedback on Android mostly seem to involve changes to the manifest, changes to the XML (you can see I've already enabled it in my XML, above) and then declaring, initializing and implementing a separate Touch handler for the button. This seems like a lot of work, especially since I have 9 buttons.
Since I already have just one onClick handler for all my buttons is there a way I can implement the haptic feedback there?
All I had to do to get a "click" sound when I tap one of my buttons was to checkmark "Audible selection" in the "Sounds" part of the phone's settings - no coding at all. Why is haptic feedback so much more complicated?
Without using the VIBRATE permission.
You can use performHapticFeedback() function of any View including Button.
For example programmatically in Kotlin:
view.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS)
This vibrates the device.
You can vibrate the device from anywhere in your Fragment or Activity, even when you don't have a View available.
In Fragment you can do:
requireView().performHapticFeedback(HapticFeedbackConstants.LONG_PRESS)
In Activity you do:
window.decorView.rootView.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS)
Create a custom VibrateButton class that inherits from Button, and add this vibration onClick. You'll still need to ask for permissions in the Manifest, so there's nothing much you can do without inheriting. This example code is taken from here, and does the vibration.
import android.os.Vibrator;
...
Vibrator v = (Vibrator) this.context.getSystemService(Context.VIBRATOR_SERVICE);
// Vibrate for 500 milliseconds
v.vibrate(500);
Note:
Don't forget to include permission in AndroidManifest.xml file:
<uses-permission android:name="android.permission.VIBRATE"/>
The reason sound can be automatically set for buttons, but vibration can't, is the same it asks for permissions to allow vibration. Vibration consumes battery (way more than a simple sound), and so it affects battery duration, which needs to be, somehow, approved by the end user. If you see, in most some there's an option to "vibrate on click" for some specific buttons (keyboard apps, mainly), but that's dependant on each app.
PS: Make sure the device can vibrate. I had some hours stupidily lost when adding vibration to a Nexus 7 2012; it hasn't got a vibration module. Also, make sure you can disable that, to keep battery up longer.
Related
Is there any way of disabling / enabling the touch events in a certain activity
without disabling certain buttons. like if the phone is "frozen" on a certain activity page for limited time?
I need that the user wont be able to go back on page, slide out or any other events.
Is it possible? (I'm using android studio, java)
Thanks in advance!
Any answer would help :)
It's possible.
Actually there is no special function to make it.
To make it, you can make a full screen button with transparent background.
And you will define a function of the button.
Of course, it will be an empty function.
Well, you can do nothing on the screen.
and then you can hide or visible the button according to your necessary.
The app I'm working on shows some sensitive information that must not be shown on the "Recent Tasks" screen when stopping the app by pressing the home button.
I'd like to blur the sensitive data in the screenshot or show the app logo instead.
I am aware of the following approaches but they don't fit my requirements:
Setting the actvitie's android:excludeFromRecents to true in the manifiest prevents the app from being shown at all in the recent tasks. This would disrupt the user experience.
Using FLAG_SECURE results in a blank card on the recents tasks screen. (How do I prevent Android taking a screenshot when my app goes to the background?) I don't like the blank screen. However, I'll stick to this solution if there is no workaround.
Overriding onCreateThumbnail seems like the ideal solution but, unfortunately, doesn't work as it's currently not invoked by the OS :( (https://code.google.com/p/android/issues/detail?id=29370)
And then there are some workarounds that I tried out but that didn't work as hoped:
Start a new activity that shows the app logo in onPause so that it's screenshot is shown instead of the actual activitie's one. But the new activity takes too long to open and it disrupts the user experience.
Set the activitie's content view to an image of the app logo in onPause. That seemed like a great solution to me. Unfortunately, the screenshot for the recent tasks screen is taken at an unspecified time. During testing the app logo quickly appears before the app is closed when pressing 'Home' but the resulting screenshot shows the activity a short time before that.
Removing the sensitive data from the widgets (e.g. textView.setText("")) has the same problem of screenshot timing just mentioned.
Any alternative ideas or solutions to the listed workarounds?
I looked into this a couple of months ago for the same purpose as you.
Unfortunately, I had to conclude that it is simply not possible. I dug through the android source code and confirmed it.
There is no callbacks or methods from android that allows you to customize it (that works anyway). Besides FLAG_SECURE, this part of the code does not accept any input or change.
OnPause and similar lifecycle methods are called too late (the screenshot is taken already). All lifecycle methods that would hint that you're about to go into the background runs too late.
The image you see in the recent tasks is an actual screenshot - and thus isn't affected by changes you do (too late) to your view. That means you can't modify your view just-in-time (like making it invisible, replacing with something else, adding SECURE_FLAG, or any other obstruction of the view). As an aside, these images can be found on an emulator at /data/system_ce/0/recent_images.
The only exception is using FLAG_SECURE, which will prevent the screenshot from being taken of your application. I experimented with setting this FLAG in onPause and removing it in onResume, however as mentioned already these lifecycle methods runs after the screenshot is taken already, and thus had absolutely no effect.
As discussed in How to change the snapshot shown by recent apps list? there used to be a callback that you could use to customize the thumbnail: onCreateThumbnail. However, this does not work and it is never called. To be clear, the callback is still there, it is simply never called by the OS. The fact that it stopped working is poorly documented, but apparently was silently deprecated/removed in 4.0.3
As for the thumbnail itself, it is a screenshot taken serverside. It is taken before onPause is called (or in fact before any callbacks indicating that your activity is about to go into the background is called).
When your app does go into the background, your actual view is animated (to get that zoom-out transition). That animation can be affected through changes you do in onPause (if you're fast enough that is) (I experimented with setting opacity to 0 on the window among other things). This will however only affect the animation. When the animation is finished, the view is replaced by the screenshot taken earlier.
Also see these questions that discuss this:
When does Android take its recent apps switcher screenshot?
Show custom application image in task manager on ICS or JB
Android never call method onCreateThumbnail
Currently (28/10/2020) is impossibile customizing app thumbnail in recent apps screen.
As explained by #Dellkan in the previous answer, the onCreateThumbnail method is not called anymore by the OS.
Unfortunately, also the suggestion to create a kind of launcher/splash screen without the FLAG_SECURE flag to let the app take a screenshot of that activity is not working, because the screenshot is taken on the activity you see and not at the launch of the app.
You cannot even customize the color of window background when using FLAG_SECURE as reported here.
How about implementing a layout overlay on top of your entire activity?
Make it transparent, it's click-through by default, so no negative impact on UX while in use.
In onPause() set a half-transparent, blurred image as the background of that layout, the data will be scrambled behind it. In onResume() change the background to fully transparent again. Voila.
It might be faster than other types of overlays. The positive side effect is, if you do the unblurring as a short animation effect when the user goes back (with a proper library that uses C++ instead of Java), it might even look cool and the users wouldnt even mind seeing it.
I haven't tried this myself, but it's something you haven't tried yet.
Since onPause is called to late, I use WindowFocusChangeListener to observe when the Fragment loses focus. At this moment we can hide all view which show sensitive data:
#Override
public void onViewCreated(#NonNull View view, #Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
view.getViewTreeObserver().addOnWindowFocusChangeListener(new ViewTreeObserver.OnWindowFocusChangeListener() {
#Override
public void onWindowFocusChanged(boolean hasFocus) {
// hide sensitive data when window moves to background (before system screenshot is captured)
myViewWithSensitiveData.setVisibility(hasFocus ? View.VISIBLE : View.INVISIBLE);
}
});
There is a way to customize it. You need your Activities with sensitive data to FLAG_SECURE in onCreate before you setContentView. Then you need an empty Activity, which renders whatever you want to have as the customized thumbnail. This usually is some sort of splash screen. This new Activity needs to be the launcher and is the only Activity not FLAG_SECURE. This Activity is launched and in onResume starts your actual Activity with the sensitive data.
Android OS will take a screenshot of that new Activity at the beginning of your App. Unfortunately the users will also see this Activity for a short moment. Since every other Activity is FLAG_SECURE, Android OS will use the only available screenshot it made at the beginning.
Was looking for a solution and found some dirty things in case you don't want to use 'FLAG_SECURE'. It doesn't give a nice picture but protects data and doesn't prevent making screenshots for the user while they are in the app.
protected void onPause () {
this.getWindow().getDecorView().getRootView().setScaleX((float)200);
this.getWindow().getDecorView().getRootView().setScaleY((float)200);
super.onPause();
}
protected void onResume () {
this.getWindow().getDecorView().getRootView().setScaleX((float)1);
this.getWindow().getDecorView().getRootView().setScaleY((float)1);
super.onResume();
}
I think this can only achieve through BroadCastReceiver but there is no receiver present. So therefore you first disable default screenshot functionality in android and then implementing your own functionality to take screenshot and before taking screenshot you should blur your secure information.
I am developing an App that has a lock screen widget where it has a play, pause, prev and next. When I connect my phone to an Android wear, it automatically shows this buttons and it works fine. I would like to add a "Like" button that would automatically be shown on the lock screen and the android wear. I know that I can create a Notification and add action for the like. But I want to know if is it possible to achieve that using only RemoteControlClient?
Have you tried using rating with the flag FLAG_KEY_MEDIA_RATING ? It seems that it's the only way to maintain generic methods and avoid having to set custom code on the wear side (or through notifications).
This flag can be set with setTransportControlFlags
Flag indicating a RemoteControlClient supports ratings. This flag must
be set in order for components that display the RemoteControlClient
information, to display ratings information, and, if ratings are
declared editable (by calling addEditableKey(int) with the
RATING_KEY_BY_USER key), it will enable the user to rate the media,
with values being received through the interface set with
setMetadataUpdateListener(OnMetadataUpdateListener).
Unfortunately, few or not example exists on the web. I discovered only one (unanswered) question relative to this on SO :
Android 4.4 KitKat Rating API
edit
It think all this stuff is now well out of date, there have been many changes to this as android has developed. I'll post an answer when I have worked it out (if noone else has).
I really just want a background app to get first crack at bluetooth messages.
end of edit
I have an android app that needs input from the user - I want to use bluetooth headset buttons (actually bluetooth watch/band buttons - but technically the same!).
I have been looking at media button examples, there are many here and elsewhere which all basically say:-
1) Put receiver/intent in manifest
2) Register receiver in main activity
3) Do processing in the receiver class
This isn't working for me (even cutting and pasting sample code), with no errors, but no events being triggered.
However I also found this:
http://developer.android.com/reference/android/bluetooth/BluetoothHeadset.html
Which is a dedicated bluetooth headset api... has it superceded the media button approach? or is it just higher in the food chain? Should this be used instead of the Media Button Event stuff - or are they just different perspectives?
If the media button stuff is the way, I can post my non-working code, but don't want to waste anyones time if its an out of date approach!
Thanks!
For examples of using bluetooth buttons - this is an excellent example...
https://code.google.com/p/media-button-router/
It detects a hit on the headset 'play' button then pops a dialog asking which app to direct the 'play' to - it plays the choices via the headset so one can be selected without viewing the screen.
I have an app which uses the ACTION_MEDIA_BUTTON intent with a BroadcastReceiver to control a music player. The user pushes a button on external hardware and it controls the in-app music player. The user can also HOLD DOWN the button and change the volume.
I recently downloaded another app which uses the headset button, and it takes over the media button intent from my app! So when this other app is open and I press the button, the other app will start running, but my app will think that the button is still pressed down so it will cycle the volume.
To summarize,
my app is open, supposed to be sole listener of media button intents
other app gets opened, it also wants to be sole listener of media button intents
button gets pressed with both apps open, control goes to other app
my app thinks the button is being held down, as it lost control as the button was pressed in down mode (I think). It then launches functions I don't want launched because it thinks the user has held down the button.
Is there any way I could make sure that while my app is open it's the sole receiver of this media button intent? Could I at least check to see if another app has taken over, so I can prevent unexpected behaviour?
Thank you for any help, I've never had apps not play nicely before!
You can alter your BroadcastReceiver's priority (make it something large, like 10000): it should then get the Intent first, and then you can pass it on to the other app.
I have a similiar issue. I believe, outside of the 'arms race' over the priorites mentioned, the only real solution is to close the other application. If you are releasing this application to other users, you could possibly give them a message telling them to close other media player apps and services.
In your manifest you can set the intent priority to the max value of an integer which is: 2147483647.
You should not however set your IntentFilter priority over 1000 as it tells you in the API docs. You can set the IntentFilter priority like so:
myIntentFilter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
I assume you should subtract one from the SYSTEM_HIGH_PRIORITY constant as the docs say the value must be less than SYSTEM_HIGH_PRIORITY.
Quote from docs about IntentFilter.setPriority(int):
Applications must use a value that is larger than SYSTEM_LOW_PRIORITY and smaller than SYSTEM_HIGH_PRIORITY.