WindowManager for all activities in an application - android

My end goal is to use WindowManager and add views to it that will persist across my entire app (without using TYPE_SYSTEM_OVERLAY that requires a special permission from the user in API >=23).
What I do currently is this:
Register ActivityLifecyclerCallback when that app launches
Add the views I want to persist to the windowManager and keep a list of them on the side.
Every time an activity is resumed I do this:
get the WindowManager of the current activity by calling currentActivity.getWindowManager()
reinflate and recall wm.addView(..) to all the windows that were added before.
However, no matter what I do the windows never get added when calling addView, its like the window manager just ignores that call..
I am using TYPE_APPLICATION for the windows.
I tried using currentActivity.getSystemService(WINDOW_MANAGER) instead of currentActivity.getWindowManager()
Can anybody tell why?

Contrary to all beliefs TYPE_APPLICATION_ATTACHED_WINDOW or TYPE_APPLICATION or anything that with the word APPLICATION in it (which makes sense by the way) is NOT the way to go.
I have no idea why and if a Google engineer is here to answer that would be great but the solution is to use TYPE_TOAST (?!?!?)
Also make sure you get a new WindowManager instance from the new activity when switching between activities so if you add windows or remove them it is a valid instance.
Thanks about it.. 4 hours wasted over naming conventions and lack of good documentation :)

Related

How do you pass the MethodChannel Result to a new Activity?

fellow Flutter enthusiasts, pros, and future friends. I've been really banging my head from trying to pass the MethodChannel.Result from the MainActivity to a new activity (to handle the result handler after performing some work there).
In iOS, I've stuffed the FlutterResult into a struct with other pertinent values and passed that into a new ViewController, and have been successfully running the result handler from there.
How do I go about doing so in Android? Since the result isn't serializable I can't add it as an extra in an intent.
In my MainActivity, I'm running 'setMethodCallHandler', and in the proper call.method case, I'm creating a new intent and starting it. I'm successful adding in and pulling out String values in the new activity but it's that Flutter Result that I'm needing to pass over.
What is the recommended way to achieve this? Am I going about it the wrong way?
I ask if I'm doing it incorrectly because when I finish() this new activity, there is a half-second black screen that takes over the screen when the activity is dismissed.
Not sure if it matters, but I'm writing in Kotlin. I would be happy to hear any Java recommendations though.
Thank you all in advance.
Without a little bit more context about what exactly you're doing in the new activity, it's a little bit hard to tell why you'd be getting a black screen. Theoretically, the flutter activity should still be running and should show up when you finish the new activity - unless you're doing something blocking on the UI thread.
However, for passing the result - what you want to be doing is to keep a reference to the result handler wherever you're receiving the message from flutter, and making use of android's startActivityForResult method. See the android docs on getting a result from an activity. Your 'worker' activity should do whatever it needs to do, then pass the result back by calling setResult before finish. The data you pass back must be serializable.
There is a slight added wrinkle - you're not necessarily working with your own activity here (if you're writing a plugin anyways). If that's the case, you'll need to implement ActivityResultListener and call registrar.addActivityResultListener in your plugin's registerWith function. If you're just doing this in an app, you can simply override onActivityResult - just make sure to call super or you might break other flutter plugins.
There is another possible solution to this, with various levels of hacky-ness depending on how in-depth you want to be. The solution is to simply use a Singleton or Global to store what you need between activities. For anyone out there who's reading this - I don't endorse doing this, I'm just providing an alternative. There are caveats to go with this - among them is that globals and to a lesser extent singletons are seen to be a bad idea for many reasons, among them code maintainability. If you absolutely must go down this route, I suggest using a Registry pattern rather than a simple singleton - i.e. you create a key that is serializable and store the Result in essentially a global/singleton map using the key, pass the key to the new activity, then in the new activity retrieve the value with that key (and make sure to remove the object from the map).
Note that the global/singleton/registry option won't work properly if the app is stopped by android as the activity could be recreated from its intent, but the object in memory may not persist. Then again - the flutter callback won't persist anyways so that point might be moot anyways.
If you're still seeing the black screen after finishing the new activity, that sounds more like a bug or something to do with the implementation of your new activity than a flutter problem.

How do I run an Android Dream Service as (or within) an Activity?

I have a perfectly functioning DreamService that I want to use within my main activity along with an extra button so that the user can access settings (Android TV appears to be missing the daydream settings icon as far as I can tell).
Is there an easy way this could be done that would specifically avoid duplication of code?
Sure, just take the views you created for your service and put them in an activity instead. Make sure your views can work without a direct dependency on the service.
You can invoke the DayDream like this -
Intent intentDream = new Intent(Intent.ACTION_MAIN);
intentDream.setClassName("com.android.systemui", "com.android.systemui.Somnambulator");
startActivity(intentDream);
Hope this helps.

Fragment not instantiating correctly

I'm having a problem instantiating Fragments in my program using the Support Library implementation. Here's a brief description of the task I'm attempting to perform and some of my attempts which haven't yet borne fruit:
The UI of my application is subject to change to meet user preferences. In order to do this, I'm using a Fragment for each different layout and replacing the active Fragment in the UI at a given time as per the user's instructions. Here are some ways I've tried (and failed) to do this:
I've tried adding the Fragments as non-static inner classes in my Activity. This approach worked so long as the user did not rotate the device. As soon as the user rotated the device, the application crashed (this is true for Portrait -> Landscape rotation and for Landscape -> Portrait rotation). Upon checking the issue using the emulator, I was getting an InstantiationException. I checked SO for some help, which led me to:
Implement the Fragment as a static inner class. When the Fragment initiates, it will expand its layout, and then from later in the control flow of the Activity, I can do stuff to the Fragment's subviews (in particular, add listeners to the buttons). Unfortunately this didn't work because I couldn't refer to the Fragment's subviews using [frag_name].getView().findViewById(). Something about referencing static objects in a non-static context. Once again, I checked SO, which led me to:
Implement the Fragment as a separate class altogether from the Activity. This seems to be what the Dev docs on developer.android.com recommend. Upon doing this, everything seems to compile fine, but when I try to refer to the Fragment's subviews (once again, using [frag_name].getView().findViewById()), I get a NullPointerException. When I add System.out.println() statements across my code to find out exactly what is happening, I find that the print statement inside onCreateView in the fragment is never getting fired, which implies that onCreateView is never getting triggered.
So now, I'm stuck. What am I doing wrong? The precise implementation of this isn't as important as learning something from the experience so I can get better at Android development, so if seperate classes are better than static classes or vice-versa, I don't really care which I use.
Thanks.
Figured it out. Turns out that in order to do what I wanted, I had to register the Activity as a Listener to each of the Fragments and pass "ready to enable buttons" messages back and forth between the two. To anyone using this question for further research, the guide on how to do that is located on the Android Developer guide, here: http://developer.android.com/training/basics/fragments/communicating.html

Adding widgets to a launcher page without bindAppWidgetId()

I'm trying to turn the stock ICS launcher into a standalone app. I'm nearly there - the only things not working are the search icon and dropping widgets onto the screen, which causes a crash.
The crash is because the stock launcher uses appWidgetManager.bindAppWidgetId(appWidgetId, componentName); to add widgets, which apparently only system apps have permission to do.
So my question is, what is the correct way for a non-system app to add widgets and acheive the same UI experience as the stock ICS launcher?
Timmmm,
Your issue is that you are looking to the wrong object. You can't really control the AppWidgetManager. Its not your job, its the System's. What you CAN do is control an AppWidgetHost, it just requires a few semantics. Here are the basics.
EDIT: Extra Background on the Widget Binding Process
The AppWidgetManager is a singleton object that runs when the System is started. This means that every instance of every launcher uses the same AppWidgetManager. What differentiates them is their AppWidgetHost and the RemoteViews they are currently holding. The AppWidgetManager basically keeps a list of all of the active hosts and the widgets they are holding. An AppWidgetHost is not a priveleged object. That is, any activity may have a single host. Thus, an entire application may be nothing but Widgets, if they so choose.
When you instantiate the Host, you must then add Views to it. So, basically it is a list of child Views with no mandatory parental bounds, except what your Activity gives it. First, you ask for an ID (via myHost.allocateAppWidgetId()). Then you use your Pick Widget Activity/Dialog. The Dialog returns the WidgetInfo. The View is retrieved when you ask the Host to create the View (via createView) with the WidgetInfo and the ID you asked for. It then asks the widget for its RemoteView.
Finally, you bind the widget by placing the View in your Activity as a Child. This is done via the addView() method of the ViewGroup that holds all of your Widgets.
The Process in Action (EDITED)
First, you have to make sure you have this in your android manifest:
<uses-permission android:name="android.permission.BIND_APPWIDGET" />
Next, you have to create an AppWidgetHost (I extend my own for my launcher). The key to the Host is to keep a reference to the AppWidgetManager via AppWidgetManager.getInstance();.
AppWidgetHost myHost = new AppWidgetHost(context, SOME_NUMERICAL_CONSTANT_AS_AN_ID);
Now, get your ID:
myHost.allocateAppWidgetId()
The next step is done by whatever method you use to get the widget info. Most times it is returned via an Intent through onActivityResult. Now, all you really have to do is use the appInfo and create the view. The WidgetId is normally provided by the pick widget activity result.
AppWidgetProviderInfo withWidgetInfo
= AppWidgetManager.getInstance().getAppWidgetInfo(forWidgetId);
AppWidgetHostView hostView
= myWidgetHost.createView(myContext, forWidgetId, withWidgetInfo);
hostView.setAppWidget(forWidgetId, withWidgetInfo);
Now you just bind the View as a child to whatever you want to bind it to.
myViewGroup.addView(hostView);
Of course, you always have to consider where and how to place it, etc. Also, you have to make sure that your AppWidgetHost is listening before you start adding widgets.
myHost.startListening()
To Summarize
The Widget binding process spans many methods and steps, but all occurs through the AppWidgetHost. Because Widgets are coded outside of your namespace you don't have any control except for where you put them and how you size the View. Since they are ultimately code that runs in your space but outside of your control, the AppWidgetManager acts as a neutral mediator, while the AppWidgetHost serves as the facilitator on your app's behalf. Once this is understood, your task is simple. The steps above are all the required steps for any custom launcher (including my own).
EDIT: Final Clarification
The ICS Launcher does this as well. The appWidgetManager they use is just a wrapper housing the AppWidgetHost and the calls to the AppWidgetManager. I forget that very little of this is explained on the Android Development Central website.
Hope this helps! Let me know if you need anymore details.
FuzzicalLogic
I now know the definitive answer. In Android 4.0, you can't do it. I ended up making my users pick the widget twice, which sucks, but there is no way around it.
In Android 4.1 they fixed the problem!
SDK apps can now host widgets and don't have to use the rubbish widget picker API! You can look into the Jellybean Launcher2 source code for details, but basically, when you first try to bind a widget, Android will pop up a dialog box saying "Do you want to allow this app to bind widgets", and then the user can decide to give it permission or not.
I'm not sure why they went for the modal permission-granting dialog box rather than the all-permissions-on-install model they've used for everything else, but whatever, it works!
Now we just have to wait 4 or 5 years until everyone has Android 4.1 or greater!
I just found this tutorial on how to add appwidgets to normal apps, which might help: http://coderender.blogspot.com/2012/01/hosting-android-widgets-my.html
This tutorial still uses the "AppWidget Picker" list, so it might not work for you since ICS has the widgets picker inside the app drawer itself.
Still, was worth to mention since tutorials on hosting widgets are very rare :)
Cheers,
Yuvi
Fuzzical Logic,with your code below,
AppWidgetProviderInfo withWidgetInfo
= AppWidgetManager.getInstance().getAppWidgetInfo(forWidgetId);
AppWidgetHostView hostView
= myWidgetHost.createView(myContext, forWidgetId, withWidgetInfo);
hostView.setAppWidget(forWidgetId, withWidgetInfo);
if have not the permission of bind_widget,widgethost got nothingļ¼Œcus withwidgetinfo is null,widgethost create nothing.

Configuration changed (orientation change) and destroying Activities - is this the way it's supposed to work?

I read up on how Android handles "configuration changes" - by destroying the active Activity.
I really want to know from Android Team why this is. I would appreciate an explanation on how the reasoning went, because I don't understand it. The fact that it acts in that way puts us all, as I see it, in a world of pain.
Lets assume you have a Activity which presents a number of EditText:s, checkboxes etc. If a User starts to fill that form with text/data and then changes orientation (or get a Phonecall), then all input the User made is gone. I haven't found any way to preserve state. That forces us to make extremely painful coding to not lose all data.
As I see it, you need another "non-Activity" class (or "value-holding" class perhaps) that has one field for each "form element" (EditText, checkbox etc).
For every single "form element" that exists, you then need to attach an Event like "onChanged" (or onTextChanged or something like that) that updates the corresponding field in the "value-holding" class to make sure that for every single character you type (in a EditText for example) is saved at once.
Perhaps you can use some listener (like "onDestroy" or something) and then fill the value-holding class with data.
I have also found this piece of info where they talk about using Bundle, onSaveInstanceState and onRestoreInstanceState, but that also mean that the programmer has to manually save and then later put back the values in the correct place? This approach is a bit less messier than my suggestions above, but still not very nice.
Can someone tell me that I am totally wrong and that this is not how it works and that I totally missed some vital information?
You should read the Application Fundamentals (specifically, Activity lifecycle). Since Activitys must be able to handle being killed at any time due to memory contraints, etc. it's just a cleaner way to handle rotations without adding too much complexity - instead of checking every resource for an alternate resource, re-structuring the layout, etc. you just save your essential data, kill the activity, re-create it, and load the data back in (if you're willing to deal with the extra complexity of managing this yourself, you can use onConfigurationChanged to handle the configuration change yourself.) This also encourages better practices - developers have to be prepared for their Activity to be killed for orientation change, which has the (good) consequence of being prepared for being killed off by memory contraints also.
The contents of an EditText will be saved for you automatically when rotating the screen if you put an android:id attribute on it. Similarly, if you display dialogs using Activity#showDialog, then the dialogs are reshown for you after rotating.
on why part - short answer - because you might have resources that needed to be changed as you've rotated the phone. ( Images, layout might be different, etc )
On save - you can save you stuff to bundle and read it back.
#Override
protected void onSaveInstanceState(Bundle outState) {
String story_id = "123"
outState.putString(ContentUtils.STORYID, story_id);
}
or you can use onRetainNonConfigurationInstance () as described here
http://developer.android.com/reference/android/app/Activity.html#onRetainNonConfigurationInstance()
Finally if you don't have anything you want to handle during rotation - you can ignore it
by putting this into your activity in manifest
android:configChanges="keyboardHidden|orientation"
In general, i would read trough article from url above couple of times, until lifecycle is crystal clear.
#Alex's approach above pointed me to a really, really useful solution when using fragments:
Fragments usually get recreated on configuration change. If you don't wish this to happen, use
setRetainInstance(true); in the Fragment's constructor(s)
This will cause fragments to be retained during configuration change.
http://developer.android.com/reference/android/app/Fragment.html#setRetainInstance(boolean)

Categories

Resources