Android - click simulation from service [duplicate] - android

I'm basically trying to get all touch event data from something like a system overlay, move my sprites around based on this touch data, then allow the OS/homescreen/browser to act upon the data as it should(or vice versa). I have found similar questions, but nothing that leads me anywhere I haven't already been:
Getting the View that is receiving all the touch events
(Implemented, with results below)
Creating a system overlay window (always on top)
What I can do:
I can EITHER grab ALL the touch events and act upon them by moving my sprites and not allow the OS/homescreen/browser to see any of them, OR ELSE I can allow the touch events to pass through and only get a “TOUCH_OUTSIDE” for my app to act upon.
My unattained goal:
I CAN NOT for the life of me figure out a way around getting BOTH to work with the data. The only methods I can think of, that I can't get implemented are:
Intercepting the data in my APP and passing it onto OS/homescreen/browser to work with
Allowing the OS/homescreen/browser to get the data first, and then getting a callback with information somehow
Allowing the OS/homescreen/browser to get the data, act on the data, and the poll them for what their scroll/location values are so as to act upon it in my APP.
I fear that this just isn't possible, I think I read somewhere in some documentation that I can't find now:
“It's all or nothing, either your view gets all the events, or none of them”
(To avoid confusion, I don't mean I have two views. I mean I have one view controlled via activity/service overlaying the OS/homescreen/browser. Like a pane of glass if you will.)
Thank you for any helpful information you can offer, it's very much appreciated!
[UPDATE]
Posted my own documentation on the matter below, so as to not be confusing.

Found this documentation that pretty much states that it's not possible to do both:
Android : Multi touch and TYPE_SYSTEM_OVERLAY
They discuss workarounds but I don't think any of them will actually work for exactly what I'm trying to do. Both given the events to the underlying app, and being able to snoop them to act upon them for myself.
To create an overlay view, when setting up the LayoutParams you need
to set the type to TYPE_SYSTEM_OVERLAY and use the flag
FLAG_WATCH_OUTSIDE_TOUCH. This presents a problem because as the
Android documentation states: "you will not receive the full
down/move/up gesture, only the location of the first down as an
ACTION_OUTSIDE." In order to receive the full array of touch events
you need to use the TYPE_SYSTEM_ALERT type, but this causes the
overlay to take over the screen and stop interaction with other
elements.
Anyone wants to disagree I'd love to hear good news :-D

You can use the GestureOverlayView, if you want to hide the lines it draws you can set the color to Transparent #00000000 so it doesn't show up, and then you can capture all touches, and gestures.
http://developer.android.com/resources/articles/gestures.html

I was looking for the same thing.
This flag does the trick for me FLAG_NOT_TOUCH_MODAL
Works on Android 7 and 8 setup as below.
The only action I've implemented so far is a touch to close the overlay window.
I also used code from here Example System Overlay Code on Github which was needed to get the events.
By the way Google Maps does a really nice job with this on Android 8. You can drag their overlay window around, resize it or close it. And all other apps work fine while it's up.
var type = 0
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O)
{
type = WindowManager.LayoutParams.TYPE_SYSTEM_ALERT
}
else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
{
type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY
}
var flags = FLAG_NOT_TOUCH_MODAL
mOverlayLayoutParams = WindowManager.LayoutParams(WindowManager.LayoutParams.WRAP_CONTENT, WindowManager.LayoutParams.WRAP_CONTENT, type, flags, PixelFormat.TRANSLUCENT)

This is not the optimal solution but it works. Restart the service toggling the flag not touchable. put boolean extra to Intent
which is used to startservice to determine previous state toggle value. A better implementation would be to fire the intent on
touch when the window is listening and after a fixed period when not listening.
public class bleh extends Service {
public void onCteqwer(int i) {
Context context; Class <bleh> context1 = bleh.class;
WindowManager.LayoutParams params = null;
WindowManager mang = (WindowManager) getSystemService(WINDOW_SERVICE);
//check previous state of service
if(i==0)
params = new WindowManager.LayoutParams(arg0,arg1,arg2,arg3,arg4,arg5);
if(i==1)
params = new WindowManager.LayoutParams(
WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE,arg0,arg1,arg2,arg3,arg4);
LayoutInflater inflater = (LayoutInflater) getSystemService(LAYOUT_INFLATER_SERVICE);
View mViw = inflater.inflate(arg, null);
mViw.setOnTouchListener(new OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
return true;
}
});
mang.addView(mViw, params);
Intent z = new Intent(context, context1);
if(i==0)
z.putExtra("name", 1);
if(i==1)
z.putExtra("name", 0);
stopSelf();
startService(z);
}
#Override
public IBinder onBind(Intent arg0) {
return null;
}
#Override
public void onStart(Intent intent, int startId) {
super.onStart(intent, startId);
Bundle i=intent.getExtras();
int userName = 0;
if (i != null)
{
userName = i.getInt("name");
onCteqwer(userName);
}
}
}

Related

Why does ACTION_OUTSIDE return 0 everytime on KitKat 4.4.2?

I have implemented a window with size 1 and want to catch ACTION_OUTSIDE event.
mWindowManager = (WindowManager) getSystemService(WINDOW_SERVICE);
WindowManager.LayoutParams mParams = new WindowManager.LayoutParams(1,1,
WindowManager.LayoutParams.TYPE_PHONE,
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE|
WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL|
WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH,
PixelFormat.TRANSLUCENT);
I get the trigger and I get the ACTION_OUTSIDE event, but when reading event.getRawX() and event.getRawY() they both return 0 every time.
I tested the same thing with Android 2.3.6 and it worked. And I can't find anything that is deprecated.
Is it an Android problem or does anyone know a solution?
Thx
Tniederm, I answered a similar question here for reference, but I'll rehash it here with some minor edits:
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. You can try to look for older versions of the InputDispatcher to find out when exactly this feature was introduced - I haven't looked myself.
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+

Android: Using WebView outside an Activity context

I am trying to achieve Web Scraping through a background IntentService that periodically scrape a website without a view displaying on the users phone.
Since I have to do call some javascript on the loaded page I cannot use any HttpGet's etc.
I therefore have to use a WebView instance which can only run on an UI thread.
Any attempts to start an Activity that use a WebView results in a View coming into the phones foreground (as per Android's design of Activities)
Any attempts to use a WebView outside of an Activity context resulted in error pointing to the fact that you cannot use WebView on a non-UI thread.
For various complexity reasons I cannot consider using libraries such as Rhino for UI-less web scraping.
Is there any way of working around this problem?
You can display a webview from a service. Code below creates a window which your service has access to. The window isn't visible because the size is 0 by 0.
public class ServiceWithWebView extends Service {
#Override
public void onCreate() {
super.onCreate();
WindowManager windowManager = (WindowManager) getSystemService(WINDOW_SERVICE);
params = new WindowManager.LayoutParams(WindowManager.LayoutParams.WRAP_CONTENT, WindowManager.LayoutParams.WRAP_CONTENT, WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY, WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE, PixelFormat.TRANSLUCENT);
params.gravity = Gravity.TOP | Gravity.LEFT;
params.x = 0;
params.y = 0;
params.width = 0;
params.height = 0;
LinearLayout view = new LinearLayout(this);
view.setLayoutParams(new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.MATCH_PARENT, RelativeLayout.LayoutParams.MATCH_PARENT));
WebView wv = new WebView(this);
wv.setLayoutParams(new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.MATCH_PARENT));
view.addView(wv);
wv.loadUrl("http://google.com");
windowManager.addView(view, params);
}
}
Also this will require the android.permission.SYSTEM_ALERT_WINDOW permission.
Correct me if I am wrong but the correct answer to this question is that there is NO possible way to use a WebView in the background while the user is doing other things on the phone without interrupting the user by means of an Activity.
I have applied both Randy and Code_Yoga's suggestions: Using an activity with "Theme.NoDisplay" to launch a background service with a WebView to do some work. However even though no view is visible the switching to that activity for that second to start the services interrupts the user (ex. like pausing a running game that was being played).
Totally disastrous news for my app so I am still hoping someone will give me a way to use a WebView that does not need an Activity (or a substitute for a WebView that can accomplish the same)
You can use this to hide the Activity
<activity android:name="MyActivity"
android:label="#string/app_name"
android:theme="#android:style/Theme.NoDisplay">
Doing this will prevent the app from showing any Activity.
And then you can do your stuff in the Activity.
the solution was like this, but with Looper.getMainLooper() :
https://github.com/JonasCz/save-for-offline/blob/master/app/src/main/java/jonas/tool/saveForOffline/ScreenshotService.java
#Override
public void onCreate() {
super.onCreate();
//HandlerThread thread = new HandlerThread("ScreenshotService", Process.THREAD_PRIORITY_BACKGROUND);
//thread.start();
//mServiceHandler = new ServiceHandler(thread.getLooper()); // not working
mServiceHandler = new ServiceHandler(Looper.getMainLooper()); // working
}
with help of #JonasCz : https://stackoverflow.com/a/28234761/466363
I used the following code to get round this problem:
Handler handler = new Handler(Looper.getMainLooper());
try
{
handler.post(
new Runnable()
{
#Override
public void run()
{
ProcessRequest(); // Where this method runs the code you're needing
}
}
);
} catch (Exception e)
{
e.printStackTrace();
}
A WebView cannot exist outside of an Activity or Fragment due to it being a UI.
However, this means that an Activity is only needed to create the WebView, not handle all its requests.
If you create the invisible WebView in your main activity and have it accessible from a static context, you should be able to perform tasks in the view in the background from anywhere, since I believe all of WebView's IO is done asynchronously.
To take away the ick of that global access, you could always launch a Service with a reference to the WebView to do the work you need.
or a substitute for a WebView that can accomplish the same <=== if you do not wish to show the loaded info on UI, maybe you can try to use HTTP to call the url directly, and process on the returned response from HTTP
Why don't you create a Backend Service that does the scraping for you?
And then you just poll results from a RESTful Webservice or even use a messaging middleware (e.g. ZeroMQ).
Maybe more elegant if it fits your use case: let the Scraping Service send your App Push Messages via GCM :)
I am not sure if this is a silver bullet to the given problem.
As per #Pierre's accepted answer (sounds correct to me)
there is NO possible way to use a WebView in the background while the
user is doing other things on the phone without interrupting the user
by means of an Activity.
Thus, I believe there must be some architectural/flow/strategy changes that must be done in order to solve this problem.
Proposed Solution #1: Instead of getting a push notification from the server and run a background job and followed by running some JS code or WebView. Instead, Whenever user launch the application one should query the backend server to know whether there is any need to perform any scraping or not. And on the basis of backend input android client can run JS code or WebView and pass the result back to the server.
I haven't tried this solution. But hope it is feasible.
This will also solve the following problem stated in the comments:
Reason for this is because the backend will get detected as a bot scraping from the same IP and get blocked (in addition to backend resources needed to do a lot of scraping on different pages).
Data might be unavailable for some time (until some user scrape it for you). But surely we can provide a better user experience to the end users using this strategy.
I know it'a been a year and a half, but I'm now facing the same issue. I solved it eventually by running my Javascript code inside a Node engine that is running inside my Android App. It's called JXCore. You can take a look. Also, take a look at this sample that runs Javascript without a WebView. I really would like to know what did you end up using?

clear missed calls and clear notification from android bar

Using this code I managed to mark all missed calls as read:
ContentValues values = new ContentValues();
values.put(Calls.NEW, 0);
if (android.os.Build.VERSION.SDK_INT >= 14) {
values.put(Calls.IS_READ, 1);
}
StringBuilder where = new StringBuilder();
where.append(Calls.NEW);
where.append(" = 1 AND ");
where.append(Calls.TYPE);
where.append(" = ?");
context.getContentResolver().update(Calls.CONTENT_URI, values, where.toString(),
new String[]{ Integer.toString(Calls.MISSED_TYPE) });
but in the android notification bar I still have a flag with missed calls. How can I also clear the notification bar for calls in android?
How can I also clear the notification bar for calls in android?
You don't. That Notification is put there by another app, and you have no means of controlling whether that Notification is displayed, short of building a ROM mod that changes the behavior of that other app.
UPDATE: Since this answer was originally written, NotificationListenerService was added and can clear notifications, but only on Android 4.3+.
The only "legal" but extremely ugly and usually useless way to achieve what you want is to show Call Log to user. And I mean literally show (becomes visual, gets focus). In case you want to do this, here's how:
public static boolean showCallLog(Context context)
{
try
{
Intent showCallLog = new Intent();
showCallLog.setAction(Intent.ACTION_VIEW);
showCallLog.setType(android.provider.CallLog.Calls.CONTENT_TYPE);
context.startActivity(showCallLog);
return true;
}
catch (Exception e)
{
Log.d("Couldn't show call log.", e.getMessage());
}
return false;
}
The reason behind this mess is the fact that apps authoritatively responsible for call logging and notifying users about missed calls (stock phone apps) use cached values. Why? Because of overall performance. You need to somehow notify those apps that Call Log has changed (seen means changed, as well) and that it should update it. It would be nice if all such apps on all devices would receive a broadcast in order to refresh, but as far as I know, it's not the case.
I hope someone will find a better way (without interrupting the user) to force refresh on stock phone apps.

Android 4.2 ACTION_OUTSIDE MotionEvent X and Y return 0 outside of own application

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+

Android overlay to grab ALL touch, and pass them on?

I'm basically trying to get all touch event data from something like a system overlay, move my sprites around based on this touch data, then allow the OS/homescreen/browser to act upon the data as it should(or vice versa). I have found similar questions, but nothing that leads me anywhere I haven't already been:
Getting the View that is receiving all the touch events
(Implemented, with results below)
Creating a system overlay window (always on top)
What I can do:
I can EITHER grab ALL the touch events and act upon them by moving my sprites and not allow the OS/homescreen/browser to see any of them, OR ELSE I can allow the touch events to pass through and only get a “TOUCH_OUTSIDE” for my app to act upon.
My unattained goal:
I CAN NOT for the life of me figure out a way around getting BOTH to work with the data. The only methods I can think of, that I can't get implemented are:
Intercepting the data in my APP and passing it onto OS/homescreen/browser to work with
Allowing the OS/homescreen/browser to get the data first, and then getting a callback with information somehow
Allowing the OS/homescreen/browser to get the data, act on the data, and the poll them for what their scroll/location values are so as to act upon it in my APP.
I fear that this just isn't possible, I think I read somewhere in some documentation that I can't find now:
“It's all or nothing, either your view gets all the events, or none of them”
(To avoid confusion, I don't mean I have two views. I mean I have one view controlled via activity/service overlaying the OS/homescreen/browser. Like a pane of glass if you will.)
Thank you for any helpful information you can offer, it's very much appreciated!
[UPDATE]
Posted my own documentation on the matter below, so as to not be confusing.
Found this documentation that pretty much states that it's not possible to do both:
Android : Multi touch and TYPE_SYSTEM_OVERLAY
They discuss workarounds but I don't think any of them will actually work for exactly what I'm trying to do. Both given the events to the underlying app, and being able to snoop them to act upon them for myself.
To create an overlay view, when setting up the LayoutParams you need
to set the type to TYPE_SYSTEM_OVERLAY and use the flag
FLAG_WATCH_OUTSIDE_TOUCH. This presents a problem because as the
Android documentation states: "you will not receive the full
down/move/up gesture, only the location of the first down as an
ACTION_OUTSIDE." In order to receive the full array of touch events
you need to use the TYPE_SYSTEM_ALERT type, but this causes the
overlay to take over the screen and stop interaction with other
elements.
Anyone wants to disagree I'd love to hear good news :-D
You can use the GestureOverlayView, if you want to hide the lines it draws you can set the color to Transparent #00000000 so it doesn't show up, and then you can capture all touches, and gestures.
http://developer.android.com/resources/articles/gestures.html
I was looking for the same thing.
This flag does the trick for me FLAG_NOT_TOUCH_MODAL
Works on Android 7 and 8 setup as below.
The only action I've implemented so far is a touch to close the overlay window.
I also used code from here Example System Overlay Code on Github which was needed to get the events.
By the way Google Maps does a really nice job with this on Android 8. You can drag their overlay window around, resize it or close it. And all other apps work fine while it's up.
var type = 0
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O)
{
type = WindowManager.LayoutParams.TYPE_SYSTEM_ALERT
}
else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
{
type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY
}
var flags = FLAG_NOT_TOUCH_MODAL
mOverlayLayoutParams = WindowManager.LayoutParams(WindowManager.LayoutParams.WRAP_CONTENT, WindowManager.LayoutParams.WRAP_CONTENT, type, flags, PixelFormat.TRANSLUCENT)
This is not the optimal solution but it works. Restart the service toggling the flag not touchable. put boolean extra to Intent
which is used to startservice to determine previous state toggle value. A better implementation would be to fire the intent on
touch when the window is listening and after a fixed period when not listening.
public class bleh extends Service {
public void onCteqwer(int i) {
Context context; Class <bleh> context1 = bleh.class;
WindowManager.LayoutParams params = null;
WindowManager mang = (WindowManager) getSystemService(WINDOW_SERVICE);
//check previous state of service
if(i==0)
params = new WindowManager.LayoutParams(arg0,arg1,arg2,arg3,arg4,arg5);
if(i==1)
params = new WindowManager.LayoutParams(
WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE,arg0,arg1,arg2,arg3,arg4);
LayoutInflater inflater = (LayoutInflater) getSystemService(LAYOUT_INFLATER_SERVICE);
View mViw = inflater.inflate(arg, null);
mViw.setOnTouchListener(new OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
return true;
}
});
mang.addView(mViw, params);
Intent z = new Intent(context, context1);
if(i==0)
z.putExtra("name", 1);
if(i==1)
z.putExtra("name", 0);
stopSelf();
startService(z);
}
#Override
public IBinder onBind(Intent arg0) {
return null;
}
#Override
public void onStart(Intent intent, int startId) {
super.onStart(intent, startId);
Bundle i=intent.getExtras();
int userName = 0;
if (i != null)
{
userName = i.getInt("name");
onCteqwer(userName);
}
}
}

Categories

Resources