How to draw an overlay from a service - java

My app is supposed to draw an overlay (blue light filter) at a certain time. Now I have a method called
private void drawOverlay()
Which under normal circumstances works perfectly fine. However, I want to use this method from a service, that runs in the background and is already running, so that I can draw the overlay at a certain time. In some cases this works, but sometimes I get a the following error.:-
java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()
I understand this has something to do with the UI thread, and I am supposed to use a Looper and a handler and I have read the documentation, but I cannot fully work out how I am supposed to do this.
Can someone please clarify this.
private void drawOverlay() {
log("drawing overlay");
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
log("overlay >m checking permission");
wm = (WindowManager) getSystemService(WINDOW_SERVICE);
if (permissionGranted) {
log("permission granted");
LayoutInflater inflater = (LayoutInflater) this.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
view = inflater.inflate(R.layout.screen_dimmer, null);
WindowManager.LayoutParams params = new WindowManager.LayoutParams(
WindowManager.LayoutParams.MATCH_PARENT,
WindowManager.LayoutParams.MATCH_PARENT,
WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY,
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
| WindowManager.LayoutParams.FLAG_FULLSCREEN
| WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED
| WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS
| WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION,
PixelFormat.TRANSLUCENT);
wm = (WindowManager) getSystemService(WINDOW_SERVICE);
wm.addView(view, params);
overlayDrawn = true;
} else {
log("no permission");
}
} else {
log("overlay drawing no permission needed (<m)");
wm = (WindowManager) getSystemService(WINDOW_SERVICE);
if (permissionGranted) {
LayoutInflater inflater = (LayoutInflater) this.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
view = inflater.inflate(R.layout.screen_dimmer, null);
WindowManager.LayoutParams params = new WindowManager.LayoutParams(
WindowManager.LayoutParams.MATCH_PARENT,
WindowManager.LayoutParams.MATCH_PARENT,
WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY,
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
| WindowManager.LayoutParams.FLAG_FULLSCREEN
| WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED
| WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS
| WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION,
PixelFormat.TRANSLUCENT);
wm = (WindowManager) getSystemService(WINDOW_SERVICE);
wm.addView(view, params);
}
}
}
Before the service is started the app checks if it has permission to draw an overlay, that is where the permissionGranted boolean comes from, this is only needed if the SDK version is higher than M.

Related

Show dialog over another app without SYSTEM_ALERT_WINDOW permission

Right now, I'm using the following permission to show a dialog over another app :
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
Dialog :
dialogBuilder = new AlertDialog.Builder(activity);
LayoutInflater inflater2 = (LayoutInflater) AppController.getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View dialogView = inflater2.inflate(R.layout.dialog_view, null);
dialogBuilder.setView(dialogView);
dialog = dialogBuilder.create();
dialog.setCancelable(true);
final WindowManager.LayoutParams layoutParams = new WindowManager.LayoutParams();
layoutParams.width = WindowManager.LayoutParams.MATCH_PARENT;
layoutParams.height = WindowManager.LayoutParams.WRAP_CONTENT;
final Window dialogWindow = dialog.getWindow();
if (dialogWindow != null) {
dialogWindow.setAttributes(layoutParams);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
dialogWindow.setType(WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY);
} else {
dialogWindow.setType(WindowManager.LayoutParams.TYPE_PHONE);
}
}
dialog.show();
Snaptube app, shows a dialog over the YouTube app without asking for any permission :
How I can achieve that?

Activity not attached to windows manager

I have a floating widget (that draws over all the apps) that on long click (mouse hold) will show a relative layout (remove_relativelayout) from the bottom so that the user can close the widget and the service will be destroyed.
On most Samsung phones I get this crash:
Fatal Exception: java.lang.IllegalArgumentException: View=android.widget.RelativeLayout{762db5 G.E...... ......ID 0,0-160,160 #7f09022d app:id/remove_relativelayout} not attached to window manager
at android.view.WindowManagerGlobal.findViewLocked(WindowManagerGlobal.java:569)
at android.view.WindowManagerGlobal.updateViewLayout(WindowManagerGlobal.java:463)
at android.view.WindowManagerImpl.updateViewLayout(WindowManagerImpl.java:101)
at com.xxxx.dev.floatingwidget.services.FloatingWidgetServices.lambda$widgetLongClick$0(FloatingWidgetServices.java:20)
at com.xxxxx.dev.floatingwidget.services.FloatingWidgetServices.lambda$widgetLongClick$0$FloatingWidgetServices(FloatingWidgetServices.java)
at com.xxxxxx.dev.floatingwidget.services.-$$Lambda$FloatingWidgetServices$DhV3rKQxnQ9oEEnb70PNAJKR8NQ.onAnimationUpdate(-.java:4)
at android.animation.ValueAnimator.animateValue(ValueAnimator.java:1558)
at android.animation.ValueAnimator.animateBasedOnTime(ValueAnimator.java:1349)
at android.animation.ValueAnimator.doAnimationFrame(ValueAnimator.java:1481)
at android.animation.AnimationHandler.doAnimationFrame(AnimationHandler.java:146)
at android.animation.AnimationHandler.access$100(AnimationHandler.java:37)
at android.animation.AnimationHandler$1.doFrame(AnimationHandler.java:54)
at android.view.Choreographer$CallbackRecord.run(Choreographer.java:994)
at android.view.Choreographer.doCallbacks(Choreographer.java:794)
at android.view.Choreographer.doFrame(Choreographer.java:725)
at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:981)
at android.os.Handler.handleCallback(Handler.java:883)
at android.os.Handler.dispatchMessage(Handler.java:100)
at android.os.Looper.loop(Looper.java:237)
at android.app.ActivityThread.main(ActivityThread.java:7857)
at java.lang.reflect.Method.invoke(Method.java)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1068)
Here is on start:
WindowManager.LayoutParams paramRemove = new WindowManager.LayoutParams(
WindowManager.LayoutParams.WRAP_CONTENT,
WindowManager.LayoutParams.WRAP_CONTENT,
windowType,
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH |
WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS,
PixelFormat.TRANSLUCENT);
paramRemove.gravity = Gravity.TOP | Gravity.LEFT;
removeView.setVisibility(View.GONE);
removeImg = removeView.findViewById(R.id.remove_img);
windowManager.addView(removeView, paramRemove);
I think its coming from this function but I'm not sure:
private void widgetLongClick() {
GlobalFunctions.printLn(">>>>>>>>>>>>Into FloatingWidgetService.chathead_longclick() ");
WindowManager.LayoutParams param_remove = (WindowManager.LayoutParams) removeView.getLayoutParams();
int x_cord_remove = (szWindow.x - removeView.getWidth()) / 2;
int y_cord_remove = szWindow.y - (removeView.getHeight() + getStatusBarHeight());
param_remove.x = x_cord_remove;
param_remove.y = y_cord_remove;
ValueAnimator va = ValueAnimator.ofFloat(szWindow.y, y_cord_remove);
int mDuration = 100;
if(removeView.isAttachedToWindow()){
if(param_remove != null){
if(windowManager !=null){
va.setDuration(mDuration);
va.addUpdateListener(animation -> {
param_remove.y = Math.round((Float) animation.getAnimatedValue());
windowManager.updateViewLayout(removeView, param_remove);
});
va.start();
}
}
}
}
I would recommend you add this removable layout into your XML file and have it be View.GONE until you require it, this way you don't have to be worried about adding it programmatically and settings the correct layout parameters (this is probably what's failing in your current code).
Once you receive the longClick on the other View, you can toggle the removable layout to be View.VISIBLE and perhaps even add combine that with a quick animation to make it seem more 'friendly' to the user.
I find the solution for my problem,
the problem was that I'm closing the activity before the view is loaded,
so what I did is to wait for 1 sec before closing the activity,
using simply:
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
//close the activity after 1 sec
}
},1000);

How to open new video in locked screen

I have a video watch app. I want to watch video in locked screen, and it is working. but some Api version i can watch but can't change video, can't open in rotated screen in some Api version. My code is belove
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O_MR1) {
setShowWhenLocked(true);
setTurnScreenOn(true);
KeyguardManager keyguardManager = (KeyguardManager)getSystemService(Context.KEYGUARD_SERVICE);
keyguardManager.requestDismissKeyguard(this, null);
}
WindowManager wm= (WindowManager) getSystemService(WINDOW_SERVICE);
Display dp= wm.getDefaultDisplay();
if (dp.getHeight()<dp.getWidth())
{
if (Build.VERSION.SDK_INT < 16) {
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN);
}
else {
View decorView = getWindow().getDecorView();
int uiOptions = View.SYSTEM_UI_FLAG_FULLSCREEN;
decorView.setSystemUiVisibility(uiOptions);
}
setContentView(R.layout.activity_watch_landscape);
}
else {
setContentView(R.layout.activity_watch);}
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON|
WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD|
WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED|
WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON);
}
When ı delete this code line, problem solved
KeyguardManager keyguardManager = (KeyguardManager)getSystemService(Context.KEYGUARD_SERVICE);
keyguardManager.requestDismissKeyguard(this, null);

How to show dialog in service on android

In my application I want to use service and in this service I should show layout.
I want when click on button show dialog, I write below codes!
But after click on button, show this dialog back of layout and not show any dialog!
My service codes :
public class FloatingLayoutService extends Service implements StepperFormListener {
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
windowManager = (WindowManager) getSystemService(WINDOW_SERVICE);
//Inflate the layout using LayoutInflater
layoutInflater = (LayoutInflater) getSystemService(LAYOUT_INFLATER_SERVICE);
floatingLayout = (ConstraintLayout) layoutInflater.inflate(R.layout.service_floating_layout, null);
floatingLay_root = floatingLayout.findViewById(R.id.floatingLay_root);
floatingLay_main = floatingLayout.findViewById(R.id.floatingLay_main);
floatingLay_emptyImg = floatingLayout.findViewById(R.id.floatingLay_emptyImg);
...
//Set layout params to display the controls over any screen.
int LAYOUT_FLAG;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
LAYOUT_FLAG = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
} else {
LAYOUT_FLAG = WindowManager.LayoutParams.TYPE_PHONE;
}
final WindowManager.LayoutParams params = new WindowManager.LayoutParams(
WindowManager.LayoutParams.MATCH_PARENT,
WindowManager.LayoutParams.MATCH_PARENT,
LAYOUT_FLAG,
0,
PixelFormat.TRANSLUCENT);
// From API26, TYPE_PHONE deprecated. Use TYPE_APPLICATION_OVERLAY for O
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O)
params.type = WindowManager.LayoutParams.TYPE_PHONE;
//Initial position of the floating controls
params.gravity = Gravity.BOTTOM | Gravity.START;
params.x = 0;
params.y = 0;
//Add the controls view to windowManager
windowManager.addView(floatingLayout, params);
//Task page btn
floatingLay_infoContentNextPage.setOnClickListener(v -> {
AlertDialog alertDialog = new AlertDialog.Builder(this)
.setTitle("Title")
.setMessage("Are you sure?")
.create();
alertDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
alertDialog.show();
});
return START_STICKY;
}
How can I show dialog, front of layout ?
You can make an activity without a layout just to show the Alert dialog.
public class MyAlertDialog extends Activity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
new AlertDialog.Builder(this)
.setTitle("Title")
.setMessage("Message")
...
...
.show()
}
}
Remember to finish the activity.
You can set the title and message dynamically by passing them through intent.
Service
Intent intent = new Intent(this, MyAlertDialog.class);
intent.putExtra("titleKey", "title");
intent.putExtra("messageKey", "messge");
startActivity(intent);
And inside the activity, read it as:
getIntent().getStringExtra("titleKey")
getIntent().getStringExtra("messageKey")

WebView scrolling doesn't work in WindowManager

Code snippet:
WindowManager windowManager = (WindowManager) getSystemService(WINDOW_SERVICE);
WindowManager.LayoutParams params = new WindowManager.LayoutParams(
WindowManager.LayoutParams.MATCH_PARENT,
WindowManager.LayoutParams.MATCH_PARENT,
WindowManager.LayoutParams.TYPE_PHONE,
WindowManager.LayoutParams.FLAG_FULLSCREEN,
PixelFormat.TRANSLUCENT);
windowManager.addView(webView, params);
It works fine: browser displays, links clicking works, but web page scrolling doesn't work.
What could be the reason?
Thanks.
Following Zoom Control properties should work.
WebSettings l_webviewSetting = webview.getSettings();
l_webviewSetting.setAllowFileAccess(true);
l_webviewSetting.setAppCacheEnabled(true);
l_webviewSetting.setBlockNetworkImage(false);
l_webviewSetting.setBlockNetworkLoads(false);
l_webviewSetting.setBuiltInZoomControls(true);
l_webviewSetting.setLoadsImagesAutomatically(true);
l_webviewSetting.setJavaScriptCanOpenWindowsAutomatically(true);
l_webviewSetting.setJavaScriptEnabled(true);
l_webviewSetting.setUseWideViewPort(true);
l_webviewSetting.setLoadWithOverviewMode(true);
l_webviewSetting.setDomStorageEnabled(true);
l_webviewSetting.getBuiltInZoomControls();

Categories

Resources