Adding widgets to a launcher page without bindAppWidgetId() - android

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.

Related

Rename AppWidgetProvider without breaking existing widgets

I found recently that after I renamed my class that extends AppWidgetProvider (as part of a code tidy-up), and installed the modified app, existing widgets would break, displaying just "Problem loading widget".
Adding a fresh widget to the home screen works fine, it's just existing widgets that break. Whilst for me as developer I don't mind deleting existing widget and adding a new one, the users of my app would not take kindly to having to do this because each widget takes time to reconfigure.
So, is there a way of safely renaming an AppWidgetProvider class without breaking existing widgets? I could of course just keep the name as it is, but it gives me an uneasy feeling that I'm stuck with the (in hindsight slightly confusing) name forever.
Unfortunately, there is no way to fix it. The problem is in changed ComponentName. AppWidgetHost just trying to get your widgets from AppWidgetManager, and AppWidgetManager trying to get previous widgets by ComponentName, so after changing package name of yours AppWidgetProvider, AppWidgetHost can't restore your previous widgets, because he got saved old ComponentName. So as you can see, there is no way to change a package name of AppWidgetProvider.

WindowManager for all activities in an application

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 :)

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.

Dynamic activity registration

I've got this question regarding Android activities and AndroidManifest.xml. The question arised when I was working with Java servlets, and wanted to create a "module-based" or something similar to plugin based server. This is working fine, and I can dynamically load the servlets I want, using a code approach similar to this: Dynamically add a servlet to the servletConfig. The servlets are dynamically found and mapped during the server startup.
The question here is not how to find the proper activities, or how to create a new instance of them, or how to start them, that part I have figured out. I can iterate over my packages and find the appropriate Activity-classes, create new instances of them and add them to a list.
I've been using a interface which all the Activities must implement in order to be a valid activity. That way, I can create new parts, extentions or new features for my application, and everything just works. In my application I have a list of button that users can click on, and the button list is generated by adding them to my view using a ListView.
private void displayLoadedContent() {
View v = inflateLayout(R.layout.buttonlayout);
ListView view = (ListView) v.findViewById(R.id.list);
view.setAdapter(new ListButtonAdapter(this, content));
}
where content is declared as
List<MyActivityInterface> content;
and ListButtonAdapter extends BaseAdapter.
So no problem there. The problem is that I have to declare each and every single one of my activites in AndroidManifest.xml. That file is like a big list of possible acitivies to display, and kind of messy, so I'm not going to display the code here.
I realize that I'll have to add one activity to the manifest, but I was hoping that the one activity should be enough. My approach can in many ways be looked as a Front Controller pattern, where each of the activities is responsible for loading and displaying the acitivity they need.
I'm simply asking if it's even possible to dynamically registrer activities in the manifest (or allow them to run in another way), and if no, is there any other valid solution?
Android requires that each activity to be registered in your AndroidManifest.xml file ahead of time. When deployed, your app will live on a device as an .apk file, which can't be modified (aside from being updated/replaced with a new APK).
If I understand the question correctly, you simply don't want to have to list a large number of activities in your manifest file? Fragments don't need to be registered in the manifest file, so if your goal is to dynamically swap out UI components, that would be the answer. Fragments are available back to 1.6 via the Support library.

Last shot at undestanding what context is in Android

This will be my last try to understand what context means in Android, otherwise I will leave Android development because I don't understand why nobody can give a good answer to this. I do NOT WANT a copy paste from the Android docs that tell me that it is a interface for accessing resources. Either will I accept links to other questions because I have read them all, otherwise I wouldn't have asked.
As the documentation states it is a interface to the resources.
1st question
What is context? What does it mean that it is a interface to the resources?
2nd question
Why do we then pass this around all the time, would not every activity etc. have access to the same resources?
3rd question
Why is context needed in every friggin scenario? Such as Button myButton = new Button(this);
4th question
Yet another question about why context is passed to e.g. listadapters?
Thank you for your time:)
Would it help you to visualise a Context as a pointer to your parent object?
So, this is why in your example you create a Button with
Button myButton = new Button ( this );
The button (and Android) needs to know with which Activity it is to be associated in order to properly manage resources (as you yourself has said - don't forget, it's for Android just as much as for you or your user) and to know for example, when to trigger your onClick(). Without knowing in which Context your button exists, how does Android know whether to show it or not? How does Android know whether to send onClick() events or not? It's because it knows the context of the button.
If your buttons context is the same as the active Activity, then it's visible to the user and needs to be managed differently to an object that is not visible - for instance, the visible Activity and it's resources will be the last objects to be killed in an out-of-memory situation.
This is no different really to other operating systems and graphical toolkits, it's just different terminology.
A Context is an interface to resources, but the touchscreen is a resource, memory is a resource, the CPU is a resource - you are thinking too narrowly about what constitutes a resource (and again, it's really just semantics); resources aren't just sound files, or icons, or layouts - there are resources that Android manages too, and it needs to know the Context of your objects in order to manage those external resources properly.
Context is a means to register your objects with the system so that whenever system wants to respond to your object, it could be identified uniquely.
If you do not register your button with the system then the listener will not get the correct event and hence it will create mess and make your system crash/slow.
The concept of context is deep rooted in android. In simple terms here is my explanation:
Android is a multi-threaded platform.
There in one UI thread where everything the user sees is drawn and in the background there can be 'n' worker threads.
Every activity/service runs on its own thread.
Every activity has a local set of layouts/images/mp3's etc.
Every activity is identified in runtime by its 'Context'. Think of it as an identifier. Thus anything you might want to do within that activity you should do it with a reference of the context.
Hope this is a favorable answer for questions 1,2,3,4
1st question What is context? What
does it mean that it is a interface to
the resources?
Hmm.. Context I believe you understand that it tells you where exactly you are. As you know the concept of this pointer in Java it is same as that. The system provides each application a context. The resources needs to be mentioned that it has to be used in this context. And all these because Android is multi-threaded. It is not that you enter inside an activity and you will be dealing with just one thread.
2nd question Why do we then pass this
around all the time, would not every
activity etc. have access to the same
resources?
Well when we have one class and an inner class then if you use "this" inside the inner class then your components will take the inner class context and not the outer class context even if you meant the outer class. Here you will have to specify by saying your outer class name.this i.e you are specifying clearly that you want to use the outer class context.
Why is context needed in every friggin
scenario? Such as Button myButton = new
Button(this);
Now Button and any other widget needs to be known on which view they need to be. So by telling the context you make it clear to the widget. Say I have two classes inside a java file and now you use a button widget. How will the widget know exactly where it has to go.
I hope the answer for your forth question is also the same.

Categories

Resources