Removing AppWidgets programmatically - android

I'm having the hardest time figuring out how to remove home screen AppWidget's programmatically (i.e. without the user actually dragging one into the trash). As an example, consider an app that can have multiple accounts, with any number of widgets for each account - once an account is removed, widget should be deleted as well.
I've tried following an obscure example from http://www.netmite.com/android/mydroid/cupcake/frameworks/base/services/java/com/android/server/AppWidgetService.java, but that doesn't seem to even trigger OnDeleted, much less remove the AppWidget from the home screen.
Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_DELETED);
intent.setComponent(info.componentName); // references AppWidgetProvider's class
intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, widgetId);
sendBroadcast(intent);
Does anyone have any advice on how this can be accomplished? An example would be the bee's knees. Thanks.

You cannot add or remove app widgets from the home screen. Only the user can do that.
Any app widgets tied to a deleted account could show a different account, or adopt some "(account deleted)" look that would trigger the user to get rid of the app widget or reconfigure it.

I'm pretty sure this should work:
int[] appWidgetIds = appWidgetManager.getAppWidgetIds(new ComponentName("com.example",
"com.example.Widget"));
AppWidgetHost host = new AppWidgetHost(ctx, 0);
host.deleteAppWidgetId(appWidgetIds[0]);

Related

Some 3rd party widgets stop updating after application upgrade

My application hosts user installed widgets, same as a launcher application.
Once I bind the widget, everything works fine. Widgets are created, updated automatically, I can click to navigate inner views.
Everything keeps working fine, until I update my application from Play store (or manually with a signed APK).
After the update, the widgets still show but they won't update anymore. Some widgets function when I click on them but the view is stuck and never gets updated until I re-create the widget (get a new ID and bind it).
I tried forcing an update using
Intent intent = new Intent();
intent.setAction(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
intent.setComponent(appWidgetInfo.provider);
intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, new int[]
{appWidgetId});
context.sendBroadcast(intent);
but that doesn't help...
I wanted to try a forced update on click but I couldn't find any way to get the widget's RemoteViews (as this is not my widget, I just host it).
RemoteViews views =
new RemoteViews(context.getPackageName(),R.layout.mywidget_layout);
Intent updateIntent = new Intent();
updateIntent.setAction(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
updateIntent.putExtra(myWidgetProvider.WIDGET_IDS_KEY, ids);
PendingIntent pendingIntent = PendingIntent.getBroadcast(
context, 0, updateIntent, PendingIntent.FLAG_UPDATE_CURRENT);
views.setOnClickPendingIntent(R.id.view_container, pendingIntent);
Also implemented an AppWidgetProvider to listen to widgets' ID changes (APPWIDGET_HOST_RESTORED) but it doesn't get called on my application update.
My next step would be to re-create all widgets after application update, but I really prefer not to do so.
Would appreciate any help!
Solved.
The last thing I wanted to do, was probably the first thing I should have tried.
I moved on to re-creating the widgets and I found that I don't have to fully re-create them, just re-call bindAppWidgetIdIfAllowed() with the same Widget ID I already have.
The method will return true if everything is still OK and if not, the widget may not be installed anymore or you need to trigger its configuration screen.
Certain Android functionality will break if the widget is installed on the SD Card. Try moving it to the device storage and re-test.
make sure you use unique keys with putExtra(MyWidgetProvider.WIDGET_ID_KEY, ids);
Do not use putExtra(AppWidgetManager.EXTRA_WIDGET_IDS, ids);

Sending Intent to Home and Widget Add

I'm trying to figure out how to send an intent to the home screen to add a Widget to it if I can. Any ideas? Here is some code I've been fooling around with to at least prompt the Add Widget selection.
AppWidgetManager mAppWidgetManager;
AppWidgetHost mAppWidgetHost;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
selectWidget();
mAppWidgetManager = AppWidgetManager.getInstance(this);
mAppWidgetHost = new AppWidgetHost(this, R.id.APPWIDGET_HOST_ID);
}
void selectWidget() {
int appWidgetId = this.mAppWidgetHost.allocateAppWidgetId();
Intent pickIntent = new Intent(AppWidgetManager.ACTION_APPWIDGET_PICK);
pickIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);
startActivityForResult(pickIntent, R.id.REQUEST_PICK_APPWIDGET);
}
Thank you to anybody who contributes.
You can send the ACTION_APPWIDGET_PICK intent to the system, but your app cannot process it, unless what you're coding is a Home screen replacement, i.e. a launcher.
Take a look at the documentation for App Widget Host, in particular the section about Host Binding. The code you're using in the selectWidget() method is the same used in the original Launcher app (under title Binding app widgets on Android 4.0 and lower). Then comes an implementation for onActivityResult, where the intent is processed. This method is what is missing in your code, but if you include it, you will end up doing all the work the Home screen app does (see addAppWidget(Intent data) next in that page).
If you continue reading the App Widget Host doc, you will see that the binding process changed on Android 4.1 and there is also a new intent for this task that requires a permission in the manifest. And to complicate things more, keep in mind #CommonsWare's comment: there are a lot of Home screen implementations, that probably do the binding process differently :(
To summarize: there's no way to get the list of app widgets and process what the user selected, neither is a way to ask the launcher app to do this for us, unfortunately. Perhaps in a future Android version, as this comment in the latest Launcher source code reveals:
/**
We will likely flesh this out later, to handle allow external apps to place widgets, but for now,
we just want to expose the action around for checking elsewhere. */

How to Launch an Android AppWidget's configuration Activity from another activity?

Well this is driving me crazy. I have developed an App-widget. Everything is working fine.
I have a configuration activity which launches every time a widget is added on the home screen and works beautiful. I save the user settings per widget id etc.
The widget has some buttons, one of them launches an activity with about information, the "About Activity".
The "About Activity" has a button which I want to use to launch the configuration activity for the widget id that launched the "About Activity". The reason I want to do that is because I want the user to be able to configure the contents of any instance of my widget without having it removed and added again (in order to launch the configuration activity).
The configuration activity needs the AppWidgetManager.EXTRA_APPWIDGET_ID in order to make the job (save the user settings for this specific widgetid) so I must somehow pass this extra when I 'm calling it from another activity. The obvious think to do is this:
startActivity(new Intent(context,act_configure.class).putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, ??? ));
Now my question is where is the widgetid? I found a million ways to get the widgetids (the array) but not a single clue on how to get the specific widgetid which launched the "About Activity"
Any help about this will make the hours I spent to find a solution, worth something. Thank you in advance.
p.s. Please forgive my English as they are not my native language...
Thanks to Cory Chaltron here is the solution to my problem.
In the widget provider onUpdate method I should create a "unique" intent to pass to the pending intent which handles the launch of the about activity. Because of the way Android compares Intents, passing the WidgetID in the extras IS NOT ENOUGH, you should also pass it as data to the intent in order to be unique. So here is the code:
Intent aboutIntent = new Intent(cx, act_about.class);
aboutIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, widgetIds[i]);
// Make this unique for this appWidgetId
aboutIntent.setData(Uri.withAppendedPath(Uri.parse("customuri://widget/id/"), String.valueOf(widgetID)));
PendingIntent aboutPendingIntent = PendingIntent.getActivity(cx, 0, aboutIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK), PendingIntent.FLAG_UPDATE_CURRENT);
views.setOnClickPendingIntent(R.id.cmdabout, aboutPendingIntent)
Although I answer my own question I am not accepting it because it is based on Cory's answer. Thank you all for the help...
How are you setting your widget views? I have an app where I iterate over the active widgets and configure set the RemoteView there. You could set your widget id in the onClick you are attaching to the "About" button.
final AppWidgetManager widgetManager = AppWidgetManager.getInstance(this);
final ComponentName widgetName = new ComponentName(this, WidgetProvider.class);
final int[] widgetIds = widgetManager.getAppWidgetIds(widgetName);
for (int widgetId : widgetIds) {
final RemoteViews remoteViews = new RemoteViews(getPackageName(), R.layout.widget);
// This is the important part :-D
remoteViews.findViewById(R.id.your_about_button).setOnClickListener(... a listener to start your about activity that puts the widget id in the extra like you suggest in your question ...);
widgetManager.updateAppWidget(widgetId, remoteViews);
}

Open an url in android browser, avoid multiple tabs

My project is mostly a web application. Now I have an android app with a home screen widget and would like to open a web page in the built-in browser on some user actions. That is easy. In MyAppWidgetProvider.onUpdate I create a new intent:
Intent intent = new Intent("android.intent.action.VIEW",
Uri.parse("http://my.example.com/"));
PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, intent, 0);
views.setOnClickPendingIntent(R.id.text, pendingIntent);
Unfortunately, a new browser window/tab is opened every time the user clicks the widget. My page contains some fancy AJAX/javascript running in the background and regularly sending http requests. So I end up having 8 tabs containing the same page and continuously sending 8-fold amount of http requests.
Is there any way, e.g. different action or additional parameters to avoid opening new tabs?
As a workaround I am now thinking about creating a new activity containing a WebView, but would like to avoid this complexity.
Yes, this is possible.
Use http://developer.android.com/reference/android/provider/Browser.html#EXTRA_APPLICATION_ID to ensure that the same browser tab is reused for your application. For example:
Context context = widget.getContext();
Intent intent = new Intent(Intent.ACTION_VIEW, uri);
intent.putExtra(Browser.EXTRA_APPLICATION_ID, context.getPackageName());
To the best of my knowledge there is no way to specify if a new tab is opened or not using the Intent. I even did some looking and could not find anyway to check which tabs were open.
Another thing you may be able to try is keeping track of if the user has recently been sent to the browser activity from your application, and check if the browser is still open, but once again I can see this going wrong in numerous ways.
Your activity idea would probably be one of the best bets in this situation, though I can understand why you would want to avoid that.

re-prompt to choose default activity

is there a way to reprompt the user to choose a default activity for an intent? For example, user selects his default home apk and I want him to reconsider his choice once again.
I know how to do that on 2.1 and before, but is there a way to do that now on 2.2?
Famous Home Switcher, which did similar thing, does not work on 2.2 anymore thanks to google team
This is how I represent the Activity selection dialog:
It start the android default ResolverActivity for "HOME" Applications.
Intent selector = new Intent("android.intent.action.MAIN");
selector.addCategory("android.intent.category.HOME");
selector.setComponent(new ComponentName("android", "com.android.internal.app.ResolverActivity"));
startActivity(selector);
The above code is working for my 2.2 enabled tablets.
When executed, it displays the "Complete Actions with:" dialog with all possible Home applications in the list.
A way to detect which is currently set by default you could ask for all preferred activities. The lists "filters" and "comps" contain the data when calling .getPreferredActivities(...).
filters - contains the intent filter data, which you could query what type of data it is.
comps - contians the component which would be called if the intent filter matches
This way you could check if your application is the current "home" application set as preferred by the user.
List<IntentFilter> filters = new ArrayList<IntentFilter>();
List<ComponentName> comps= new ArrayList<ComponentName>();
getPackageManager().getPreferredActivities(filters, comps, null);
For example, user selects his default home apk and I want him to reconsider his choice once again.
That is no longer possible, unless your app is the preferred one. Then, I think you can use clearPackagePreferredActivities() to remove yourself as the preferred choice.
In other words, you are welcome to affect your own app, but you are not welcome to affect other apps.

Categories

Resources