Android floating window keyboard not showing outside window - java

I'm having a problem with focus when using a floating window. My current code is:
WindowManager windowManager = (WindowManager) getSystemService(WINDOW_SERVICE);
WindowManager.LayoutParams parameters = new WindowManager.LayoutParams(
200, 200, WindowManager.LayoutParams.TYPE_PHONE,
WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH,
PixelFormat.TRANSLUCENT);
parameters.gravity = Gravity.CENTER;
RelativeLayout layoutView = new RelativeLayout(this);
...
windowManager.addView(layoutView, parameters);
The problem is that with this code only the floating window is focusable. I've tried changing the flags, but always either only the floating window, or the background application is focusable (so a keyboard will appear when I click an EditText.
I want both the floating window and the background application be able to show a softkeyboard on an EditText click.

You can try like below:
this.setOnTouchListener { v, event ->
if (event.action == MotionEvent.ACTION_OUTSIDE) {
updateLayoutParamFlag(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE)
}
return#setOnTouchListener false
}
mEditText.setOnTouchListener { v, event ->
if (event.action == MotionEvent.ACTION_DOWN) {
updateLayoutParamFlag(WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL or
WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH)
}
return#setOnTouchListener false
}
fun updateLayoutParamFlag(flags: Int) {
if (mlayoutParams.flags != flags) {
mlayoutParams.flags = flags
mWindowManager.updateViewLayout(this, mlayoutParams)
}
}

Nevermind. I used a library called StandOut (http://pingpongboss.github.io/StandOut/). It somehow manages to do the above thing. I looked into the source code, but I couldn't really figure out what it was doing to make this work, so I just rewrote part of my project to make it compatible with this library

Related

Keyboard visibility listener when activity is not resizable

I have an Activity where there are 2 EditText. I have applied android:windowSoftInputMode="adjustNothing". When the user clicks on the EditText, the soft keyboard opens to enter some value in it. I want to set some other View's visibility to Gone when the soft keyboard opens and also when the soft keyboard closes from the EditText on the back button press. Then I want to set some other View's visibility to Visible.
I have tried serveral solution but they are not working due to adjustNothing applied on the Activity.
I have also used following keyboard visibilty watcher library but it's also not working due to adjustNothing.
implementation 'net.yslibrary.keyboardvisibilityevent:keyboardvisibilityevent:3.0.0-RC2'
You can try to set a global layout listener to the root view of your Activity to detect changes in the height of the view. When the height of the view decreases, you can assume that the keyboard has opened. When the height of the view increases, you can assume that the keyboard has closed.
val rootView = findViewById<View>(android.R.id.content)
rootView.viewTreeObserver.addOnGlobalLayoutListener(object :
ViewTreeObserver.OnGlobalLayoutListener {
override fun onGlobalLayout() {
val heightDiff = rootView.rootView.height - rootView.height
if (heightDiff > dpToPx(this#YourActivity, 200f)) { // if more than 200 dp, it's probably a keyboard...
// do something here
} else {
// do something here
}
}
})
fun dpToPx(context: Context, dp: Float): Int {
return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, context.resources.displayMetrics).toInt()
}

How do you get an Android accessibility service to draw overlays over the entire screen?

There's a popular app called Twilight: Blue light filter that draws a red overlay over the entire screen, including both the navigation bar and the status bar. It does this using an accessibility service. How? I've been trying to replicate it for days but I can't get it right. This is what I have inside my own accessibility service:
private void drawTheOverlay(Drawable drawable) {
WindowManager windowManager = (WindowManager) this.getSystemService(Service.WINDOW_SERVICE);
View view = new LinearLayout(this);
view.setClickable(false);
view.setFocusable(false);
view.setFocusableInTouchMode(false);
view.setLongClickable(false);
view.setKeepScreenOn(false);
Display display = windowManager.getDefaultDisplay();
Point realScreenSize = new Point();
display.getRealSize(realScreenSize);
WindowManager.LayoutParams layoutParams = new WindowManager.LayoutParams();
layoutParams.height = realScreenSize.y;
layoutParams.width = realScreenSize.x;
layoutParams.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
| WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE
| WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS
| WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
layoutParams.format = PixelFormat.TRANSLUCENT;
layoutParams.windowAnimations = android.R.style.Animation_Toast;
layoutParams.type = WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY;
view.setBackground(drawable);
windowManager.addView(view, layoutParams);
view.setVisibility(View.VISIBLE);
}
The problem with the above code is that the overlay gets vertically offset by some unknown amount. This is how it looks like on my own device, a Razer Phone 2, and as you can see the dark overlay is pushed upwards by a bit:
I've also tried it on a Galaxy S10 and it's the same issue but in the opposite direction. Meaning the overlay is pushed downwards instead of upwards.
How does the Twilight app do it?

How to forward click event to Launcher app?

I have a widget placed programatically on the Home screen, and I made it possible for the user to drag it to the place he wishes. I have added an OnTouchListener to the view to get motion events (so that I can remember view position on screen for next time), and it fires wherever I click on the Home screen, even when this is outside of my view.
What I would like to achieve is to forward the MotionEvent.ACTION_UP to the Launcher app so that, if user clicked on an app icon, that app will be launched. Or alternatively, NOT to receive this event when clicked outside of my view. Here's what I have done:
LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
inflatedViewWidget = inflater.inflate(R.layout.mtc_appwidget, null);
WindowManager.LayoutParams params = new WindowManager.LayoutParams(
WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY,
0,
PixelFormat.TRANSLUCENT);
final WindowManager wm = (WindowManager) context.getSystemService(WINDOW_SERVICE);
wm.addView(inflatedViewWidget, params);
OnTouchListener:
inflatedViewWidget.setClickable(true);
inflatedViewWidget.setOnTouchListener(new View.OnTouchListener() {
public boolean onTouch(View v, MotionEvent event) {
int action = event.getAction();
switch (action) {
case MotionEvent.ACTION_UP:
//how to forward event to Launcher app?
break;
AFAIK, doing a click in another app is not allowed, that would allow overlay apps to make you click on a pay button for example, you would need root or debugging capabilities for that.
So the trick is to play with the window size.
Your widget's window size and location should always match the clickable area.
You can do this with the updateViewLayout method on the WindowManager.
A good example can be found here:
https://medium.com/#kevalpatel2106/create-chat-heads-like-facebook-messenger-32f7f1a62064

Method to capture the whole Screen from a Floating Window Application

I'm trying to do implement a floating widget button(like the Facebook Messenger) which is displayed over the other app. The button should be able to interfere with the displayed view of the other app and capture the whole smartphone screen underneath.
The floating button is already implemented and works fine on every app. Getting the root view and printing out the bitmap of this floating view will just return the floating button image. I think the floating window cant detect the view of the apps underneath.
One possible solution could be to start the other application as a separate activity and get its view, but I am not sure how to do that. Maybe there is some easier way to achieve this?
//Creating the floating window and set its Layout parameters
mFloatViewLayoutParams = new WindowManager.LayoutParams();
mFloatViewLayoutParams.format = PixelFormat.TRANSLUCENT;
mFloatViewLayoutParams.flags = WindowManager.LayoutParams.FORMAT_CHANGED;
mFloatViewLayoutParams.type = Build.VERSION.SDK_INT >= Build.VERSION_CODES.O
? WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY
: WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
mFloatViewLayoutParams.gravity = Gravity.START;
mFloatViewLayoutParams.width = WindowManager.LayoutParams.WRAP_CONTENT;
mFloatViewLayoutParams.height = WindowManager.LayoutParams.WRAP_CONTENT;
// adding the floatview to the WindowManager
LayoutInflater inflater = LayoutInflater.from(activity);
mFloatView = inflater.inflate(R.layout.float_view_layout, null);
mWindowManager.addView(mFloatView, mFloatViewLayoutParams);
...
// some clickListener to start the screenshot
ImageButton imageButton = mFloatView.findViewById(R.id.imageButton);
imageButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(final View v) {
mActivity.runOnUiThread(new Runnable() {
#Override
public void run() {
// here I want to get the view of the whole screen to
screenshot it
}
});
}
});
What I don't want: I don't want to screenshot the view of my main activity which starts the floating button.
I am really out of ideas about how to handle this challenge. Hopefully, someone has a guess!
Cheers!

How to draw a view on top of everything?

I want to make an app that can create notification on the screen on top of anything that is currently being displayed. Something like the Go SMS message popup or something like the ChatHead in the following picture:
It would be even better if it is possible to draw it dynamically including touch events.What is the conventional or standard way to do this?
Example:
Like an Icon that can be clicked or dragged no matter whether you are on home screen or app drawer or other apps.Pay attention to the circular icons near the edges of the screen in the picture posted. You can drag them anywhere in any app.
What you are looking for is System Alert Window.
There's a library called StandOut! which will assist you in creating such apps.
Here is how things like Toast and dialog windows work:
In the case where just adding or bringing to front does not work, say when you are having a service add its own view to another client activity or application (FaceUnlock does this), or you cannot depend on hierarchies, you need to use the window manager and a window token to do the trick. You can then create layouts and take advantage of animations and hardware acceleration as before.
WindowManager windowManager = (WindowManager) getBaseContext().getSystemService(Context.WINDOW_SERVICE);
WindowManager.LayoutParams layoutParams = new WindowManager.LayoutParams(WindowManager.LayoutParams.FIRST_SUB_WINDOW);
layoutParams.width = 300;
layoutParams.height = 300;
layoutParams.format = PixelFormat.RGBA_8888;
layoutParams.flags =
WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
| WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
layoutParams.token = getWindow().getDecorView().getRootView().getWindowToken();
//Feel free to inflate here
mTestView = new View(this);
mTestView.setBackgroundColor(Color.RED);
//Must wire up back button, otherwise it's not sent to our activity
mTestView.setOnKeyListener(new View.OnKeyListener() {
#Override
public boolean onKey(View v, int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_BACK) {
onBackPressed();
}
return true;
}
});
windowManager.addView(mTestView, layoutParams);
Then be sure to remove the view onDestroy (or onPause) or you will crash
if (mTestView != null) {
WindowManager windowManager = (WindowManager) getBaseContext().getSystemService(Context.WINDOW_SERVICE);
if (mTestView.isShown()) {
windowManager.removeViewImmediate(mTestView);
}
}
You don't need a new activity to do this. All you need to do is to add another view into your existing activity and bring it to the front, and draw/write the things that you want into that view.
If you want to do special things with this extra view, you could create your own view class
class DrawOnTop extends View {
public DrawOnTop(Context activity) {
super(activity);
}
#Override
protected void onDraw(Canvas canvas) {
// put your drawing commands here
}
}
and then you do something like
DrawOnTop mDraw = new DrawOnTop(this);
addContentView(mDraw, new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));
mDraw.bringToFront();
Then to force it to draw, you need to use mDraw.invalidate();
You could have the parent of your whole layout as RelativeLayout. The first child being the "root" of your main layout. Anything after that can be considered an overlay which is placeable to your whims.
Example:
<RelativeLayout>
<LinearLayout>
... Main Layout here ...
</LinearLayout>
<TextView left="20dip" top="20dip" text="Overlay" alpha="0.7" />
</RelativeLayout>
The best way is to start a service with your application.
Create an ImageView.
Set the LayoutParams of the Image View.
Add the view along with the params to the window manager when the service is created.
ALL SET
Your Image sticks to your window (At any screen over all apps), till you application is closed.
You can even add onclicklisteners and ontouchlisteners to the imageview.
Eg. OnClick listeners to perform some actions and Ontouchlisteners move the image along the screen.

Categories