I'm trying to create an overlay window in android (a which will float over any other app in the screen, even when my app is on background)
I followed several guides (some from SO) and here is the important code
this.sp = PreferenceManager.getDefaultSharedPreferences(context);
this.wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
this.main = (FrameLayout) LayoutInflater.from(c).inflate(R.layout.ui_floating_window, null);
int type = WindowManager.LayoutParams.TYPE_TOAST;
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O)
type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
Point p = new Point();
wm.getDefaultDisplay().getSize(p);
this.displayHeight = p.y;
this.displayWidth = p.x;
this.rationWH = this.displayWidth / (float) this.displayHeight;
WindowManager.LayoutParams params = new WindowManager.LayoutParams(
sp.getInt(Constants.DefaultSharedPreferences.FLOATING_WINDOW_WIDTH, this.displayWidth / 2),
sp.getInt(Constants.DefaultSharedPreferences.FLOATING_WINDOW_HEIGHT, this.displayHeight / 2),
sp.getInt(Constants.DefaultSharedPreferences.FLOATING_WINDOW_X, this.displayWidth / 2),
sp.getInt(Constants.DefaultSharedPreferences.FLOATING_WINDOW_Y, this.displayHeight / 2),
type,
WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL |
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH,
PixelFormat.TRANSLUCENT);
params.gravity = Gravity.START | Gravity.TOP;
params.horizontalMargin = 0;
params.verticalMargin = 0;
this.wm.addView(main, params);
i've tested on android 29 and works really fine
but on android 19 the window opens but as soon as the current app goes background the window goes either. i would like the window stayed on even after user switches the app.
This is how i get 19
this is how it works in android 29 (correct way)
https://i.imgur.com/JjMugfG.mp4
am i doing anything wrong
The problem is that the WindowManager instance from Activity is different than the instance retrieved from the application-level Context.
Let's try a test:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Context context = this; // context of the activity
WindowManager wm1 = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
WindowManager wm2 = (WindowManager) context.getApplicationContext().getSystemService(Context.WINDOW_SERVICE);
System.out.println("Are equal? " + wm1.equals(wm2));
}
The result is FALSE.
Actually, a WindowManager is responsible for managing a list of windows on the screen. In the case of Activities, when the Activity gets disappeared, all of the related windows such as your added floating view would be gone. While your app lives in the background after pressing the home button, for instance, its window manager (Application's window manager) keeps all of the managed windows. It means that your floating view has the chance of being shown until the app would be killed by the system.
So, by retrieving WindowManager instance from application-level Context and adding the view to it, the problem will be solved.
For 7 years, I work on Floating Apps (https://floatingapps.net) which is basically a set of floating mini-apps, so I know something about it :-).
I first get it working on Android 2.3.5 and progress until today.
It's a complex problem if you want to do it right across all possible devices and Android versions.
I recently published a series of articles on this topic. Check it out: https://localazy.com/blog/floating-windows-on-android-1-jetpack-compose-and-room
It goes from the main app, background service, permissions, floating windows, moving them, keyboard input to the list of tips, trick and shortcomings I learned along the way.
It should learn you how to do it right.
The window manager you are getting from an activity points out to Activity.getWindow() when adding a view, which naturally disappears if you click the back button or home button. That means you need another decor view to resolve your problem, which is calling the application context in your case, where it will let you manage the phone's window, as you want.
Check application context, and if it's not null, get the WindowManager instance, then try adding your view.
Also I'm not sure but using WindowManager.TYPE_SYSTEM_ALERT also might help.
Related
I recently decided to update my older apps and bring them a little more up to date. In doing so I noticed an odd problem when running them on my new handset (Android O / API Level 29).
My App is essentially a single Activity using OpenGL. This is basically what happens when the app is first loaded...
Create Layout, Create a Splashscreen View, create custom GLSurfaceView
Add GLSurfaceView to Layout
Add Splashscreen to Layout
Start AsyncTask to load resources and do setup ops
GLSurfaceView dismisses Splashscreen to reveal renderer...
So, it all works perfectly well on my older devices (3 x handsets, 1 x tablet all running various versions of Android no higher than Lollipop).
However, on my Android 10 handset, all I get is a blank screen while the app starts. Just to be clear, there are no errors, the app itself works perfectly fine. After the usual startup time, the blank screen is dismissed and the app continues, it's just that the 'splashscreen' has now become a "blankscreen".
But Android Studio Layout Inspector tells a different story
Indeed, if I open the Layout Inspector in Android Studio, oddly it shows exactly what I would expect... the splashscreen with its text/graphics, but not on the actual device...
Some test code
In the following test code I replaced the resource loading / setup with a simple Thread.sleep just to create a longer delay so I could more easily inspect the output in Android Studio. This does still exhibit the problem...
#Override
protected void onCreate(Bundle savedInstanceState) {
int[] deviceResolution = getDeviceResolution();
width = deviceResolution[0];
height = deviceResolution[1];
layout = new RelativeLayout(this);
splash = new Splash(getApplication(), width, height);
myGLView = new CustomGLSurfaceView(this.getApplication());
layout.addView(myGLView);
layout.addView(splash);
setContentView(layout);
loadResource = new LoadResources(newBundle);
loadResources.execute();
super.onCreate(savedInstanceState);
}
class LoadResources AsyncTask<Void, Void, Void> {
Bundle savedBundle;
LoadResources(Bundle savedBundle){
this.savedBundle=savedBundle;
}
#Override
protected void onPreExecute() {
super.onPreExecute();
}
#Override
protected Void doInBackground(Void... params) {
// Simulate some long-running task here. While this is happening, splashscreen should be visible. And on older devices, it is. Not on Android O handset (but OK in Android Studio Layout Inspector).
try {
Thread.sleep(30000);
} catch (InterruptedException e) {
e.printStackTrace();
}
// Would typically make my calls here to load resources, and complete other setup tasks here
return null;
}
#Override
protected void onPostExecute(Void result) {
super.onPostExecute(result);
}
}
** Other observations **
I know that Async is now deprecated and I'm also aware of other Splashscreen tecnhiques such as using a dedicated Activity. However due to the setup/complexity of the apps and given that it doesn't 'break' them per-se, I don't want to have to refactor the entire codebase (just yet anyway) any new apps going forward will use a different system for the splashscreen. What I'm really trying to understand here is why this now doesn't work as it used to. Is this a bug? Or a behavioural change that happened in an Android version somewhere down the line? Or maybe it's just me doing something wrong. If anyone has come across it, I'd be really interested to know of a fix/workaround.
** Other things I've tried **
In the above example, if I simply don't add 'myGLView' then the splashscreen shows, but of course the app then won't work. So I though about adding it in onPostExecute and then bringing the splashscreen view back to the front with splash.bringToFront(). This kind of works, but is messy. The splashscreen only shows for a brief second, and on devices that show it correctly there is a 'glitch' as the correctly displayed splashscreen is overlayed with the GL Renderer and then bought back to the front.
You are adding a view to an object which is not currently set in an Activity.
The better approach would be calling setContentView() first as well as the super method, then adding the multiple views in it.
So in your case, it'll be like:
super.onCreate(savedInstanceState);
setContentView(layout);
layout = new RelativeLayout(this);
int[] deviceResolution = getDeviceResolution();
width = deviceResolution[0];
height = deviceResolution[1];
layout = new RelativeLayout(this);
splash = new Splash(getApplication(), width, height);
myGLView = new CustomGLSurfaceView(this.getApplication());
layout.addView(myGLView);
layout.addView(splash);
loadResource = new LoadResources(newBundle);
loadResources.execute();
I would like my Android app to launch another app on a dedicated area of the screen, i.e. set the screen bounds within which the other app will be displayed. Below is a screenshot with an example of how this could look, where I have launched Google Maps overlaid on my app:
This is achieved by the following code:
final String packageName = "advanced.scientific.calculator.calc991.plus";
new Handler().postDelayed(() -> {
final int[] location = new int[2];
containerLayout.getLocationOnScreen(location);
final Rect appBounds = new Rect(location[0], location[1],
location[0] + containerLayout.getWidth(),
location[1] + containerLayout.getHeight());
final ActivityOptions launchOpts = ActivityOptions.makeBasic();
launchOpts.setLaunchBounds(appBounds);
final Intent intent = getPackageManager().getLaunchIntentForPackage(packageName);
intent.addFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT);
intent.addFlags(Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
startActivity(intent, launchOpts.toBundle());
}, 5000);
The above result would be satisfactory, however this is run on a Samsung Galaxy S10 device running Android 10. Now, I need to be able to achieve a similar result using Android 9. I have looked into the picture-in-picture and freeform modes, but I have not managed to use the former for this purpose, and the latter unfortunately only seems to be available from Android 10 and up. I am open to any solution for this, including creating a launcher app, etc. I am also looking to maximize compatibility with third-party apps.
What are the possible ways to achieve this on Android 9?
before anything sorry for my bad English.
i search a lot this site but not found my answer.sorry
i make a lock-screen app.(for android 4.0+)
so i make a "Service" that call a "Broadcast Receiver".
in the "Broadcast Receiver" as what happen :
if(SCREEN_OFF) else if(SCREEN_ON) else if(BOOT_COMPLETED))
i start my main activity (my main lock-screen application).
until this moment all thing work perfectly and device lock and unlock correctly.
but there is some problem.
1 : how to set my app as default lock-screen app?
2 : when device is lock if i press "home button","back button","recent button", i transfer to home screen and lock-screen go to recent like another normal application and i access to device without any security.
2-1 : as you now in android 4.0+ we can not override "Home button".
my solution for problem 2:
i search in developer.google and find below method for draw a "view" over other apps with this permission : <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
windowManager = (WindowManager) getSystemService(WINDOW_SERVICE);
WindowManager.LayoutParams params = new WindowManager.LayoutParams(
WindowManager.LayoutParams.MATCH_PARENT,
WindowManager.LayoutParams.MATCH_PARENT,
WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY,
WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN,
PixelFormat.TRANSLUCENT);
windowManager = (WindowManager) getApplicationContext().getSystemService(Context.WINDOW_SERVICE);
LayoutInflater li = (LayoutInflater) getSystemService(LAYOUT_INFLATER_SERVICE);
LinearLayout mTopView = (LinearLayout) li.inflate(R.layout.activity_main_lock, null);
windowManager.addView(mTopView, params);
but this method only draw my xml file over other apps.
i want this for my activity.
i use this FLAGs but button still working :(
getWindow().addFlags(WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD|WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED|WindowManager.LayoutParams.FLAG_FULLSCREEN);
please say , if you have any idea how to make this activity over other apps.
please help me to solve this problem
thank you very much.
The following code runs in a BroadcastReceiver and adds a transparent overlay to the system-level window manager to keep the orientation in Landscape mode. This code works fine.
When I put this code in an Activity, however, to have the Activity add the overlay instead, nothing happens. For the life of me, I cannot figure out why. I have it in a method that gets called from an OnClickListener. I know the method gets called because of a Toast message I put at the top of the method.
final View view = new View(context);
int dimension = 0;
int pixelFormat = PixelFormat.TRANSLUCENT;
final WindowManager.LayoutParams params = new WindowManager.LayoutParams(
dimension, dimension,
WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY,
WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
| WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
pixelFormat);
params.screenOrientation = ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
final WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
wm.addView(view, params);
What is wrong with this code that won't let it run in an activity?
In the activity, every instance of context is replaced with this. Could this be the issue?
The problem may very well lay in your Context.
Turns out that the context in the onReceive method in the Broadcast receiver it not the same context as the Activity. It will inflate UI elements, but inflation will be done with the default theme for the system on which you are running, not what’s defined in your application. (You have to be aware of this in case this becomes a problem).
On the other hand, when you are inside an onClickListener, is this onClickListener Anonymous or a hard reference?
E.g.: Are you doing
yourButton.setOnClickListener( new View.OnClickListener() { //code }; );
I am experience som major memory issues with my projekt. If you could throw some advice for me that would be really helpfull.. I'll try to explain my whole usage.
In the main activity, the first thing I do is setting upp the AdMob banners. Which seems to use ALOT of memory. This is my whole onCreate method:
#Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
mGameView = new GameView(this, getAssets());
ActivityManager am = ((ActivityManager)getSystemService(Activity.ACTIVITY_SERVICE));
mGameView.setMemoryLimit(am.getMemoryClass());
mLayout = new RelativeLayout(this);
mLayout.addView(mGameView);
if(mGameView.getMemoryLimit() > 40 && !mGameView.isFullVersion())
{
mAdView = new AdView(mThis, AdSize.BANNER, "xxxx");
mAdRequest = new AdRequest();
mAdView.loadAd(mAdRequest);
mAdRequest = null;
RelativeLayout.LayoutParams lp = new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.WRAP_CONTENT, RelativeLayout.LayoutParams.WRAP_CONTENT);
lp.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM);
lp.addRule(RelativeLayout.CENTER_HORIZONTAL);
mAdView.setLayoutParams(lp);
mLayout.addView(mAdView);
}
setContentView(mLayout);
hideBannerAd();
}
As I have googled alot, the AdMob code seems to leak memory so I try to keep it alive the whole lifetime of the app and only showing it on loadingscreens.
At this point I am using maybe 10-15mb memory JUST FOR THE ADS.
I have grouped all bitmaps used by Menu and Gameplay. Which means I only load the the menu bitmaps when I show the menu and then recycle and delete the reference of the old bitmaps before loading the new ones.
During the loading process I check how much percent of memory I have left and only continue to load bitmaps if the percent is less than 90%.
I calculate the memory percent by:
Debug.getMemoryInfo(memoryInfo);
percent = (float) (memoryInfo.getTotalPss() / 1024.0) / (float) mGameView.getMemoryLimit() * 100;
tge memoryLimit comes from earlier mentioned activity:
ActivityManager am = ((ActivityManager)getSystemService(Activity.ACTIVITY_SERVICE));
mGameView.setMemoryLimit(am.getMemoryClass());
As you saw in the activity I am only showing ads for devices with more than 40mb memory. But I am using about 20mb for the project WITHOUT ADS.
Another problem I have encountered is with devices that share memory with other applications. Like if their memory limit is 64 but is still using like 40-50 from elsewhere than my app. Can I somehow request that memory to be cleared?
Am I doing someting wrong because this is really getting to my nerves. I mean I have seen apps using like 2-5 ads at the same time..
Can I somehow manage this better?