Can someone tell me how to check that my widget have been placed on the homescreen?
I have some code in my app that should run only if the widget is placed on the homescreen.
Just saying, but...
int ids[] = AppWidgetManager.getInstance(this).getAppWidgetIds(new ComponentName(this,MyAppWidgetProvider.class));
Toast.makeText(this, "Number of widgets: "+ids.length, Toast.LENGTH_LONG).show();
You need to store that information yourself. I usually use the application preferences, but you could use anything. Generally widgets use services to communicate, so your code that does stuff is likely in a service, but using the preference allows any portion of your app to access this.
In your widget class that extends AppWidgetProvider the onEnabled is called when the widget is put on a homescreen and the onDeleted is (usually) called when it's removed. onDisabled is called when all copies are removed.
So in the code of your widget provider:
#Override
public void onEnabled(Context context) {
super.onEnabled(context);
setWidgetActive(true);
context.startService(new Intent(appContext, WidgetUpdateService.class));
}
#Override
public void onDisabled(Context context) {
Context appContext = context.getApplicationContext();
setWidgetActive(false);
context.stopService(new Intent(appContext, WidgetUpdateService.class));
super.onDisabled(context);
}
private void setWidgetActive(boolean active){
Context appContext = context.getApplicationContext();
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(appContext);
SharedPreferences.Editor edit = prefs.edit();
edit.putBoolean(Constants.WIDGET_ACTIVE, active);
edit.commit();
}
Elsewhere in code, you would check to see if the widget is active by:
public boolean isWidgetActive(Context context){
Context appContext = context.getApplicationContext();
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
return prefs.getBoolean(Constants.WIDGET_ACTIVE, false);
}
I know it's an old question, but looking at this today I saw that there are a couple of problems with the accepted answer from #larsona1:
if the user cleared the shared preferences - there's still widget, but the app won't know about it.
if the user regret between "add widget" and before pressing "ok" - onEnabled will be called anyway, and a widget will be registered in the home screen even though there is no widget, and no way to remove it later. (it may be a bug in ADT home launcher).
I found a solution to the first problem.
No shared preferences are needed at all, since it's unreliable anyway. It has to be checked in runtime.
// in some class you define a static variable, say in S.java
static boolean sWidgetMayExist = true;
In your widget provider:
// MyAppWidgetProvider.java
// to respond to runtime changes, when widgets are added and removed
#Override
public void onEnabled(Context context) {
super.onEnabled(context);
S.sWidgetMayExist = true;
}
#Override
public void onDisabled(Context context) {
super.onDisabled(context);
S.sWidgetMayExist = true;
}
And, in your service code add this:
AppWidgetManager manager = null;
RemoteViews views = null;
ComponentName widgetComponent = null;
// ..and in your update thread
if (!S.sWidgetMayExist) { return; }
if (manager == null || widgetComponent == null) {
widgetComponent = new ComponentName(c,
MyAppWidgetProvider.class);
manager = AppWidgetManager.getInstance(c);
}
if (manager.getAppWidgetIds(widgetComponent) == null) {
S.sWidgetMayExist = false;
}
#Waza_Be is right as looking at the "AppWidgetIds" list to know the number of active widgets (those installed on your homescreen) is the correct way to know this information.
However, keep in mind that you SHOULD don't have to look at this by yourself.
Check the official Android documentation for best practice about widgets :
https://developer.android.com/guide/topics/appwidgets/index.html#AppWidgetProvider
The right approach is to override only the onUpdate() method and iterate through the list of "active" widgets :
public class ExampleAppWidgetProvider extends AppWidgetProvider {
public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
final int N = appWidgetIds.length;
// Perform this loop procedure for each App Widget that belongs to this provider
for (int i=0; i<N; i++) {
int appWidgetId = appWidgetIds[i];
// Create an Intent to launch ExampleActivity
Intent intent = new Intent(context, ExampleActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, intent, 0);
// Get the layout for the App Widget and attach an on-click listener
// to the button
RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.appwidget_provider_layout);
views.setOnClickPendingIntent(R.id.button, pendingIntent);
// Tell the AppWidgetManager to perform an update on the current app widget
appWidgetManager.updateAppWidget(appWidgetId, views);
}
}
}
And as your own widget provider overrides AppWidgetProvider, you will NOT go into the onUpdate() method if you have no widgets active on the home screen!
See the onReceive() code of Android AppWidgetProvider that checks already for you that "appWidgetIds.length > 0":
public void onReceive(Context context, Intent intent) {
// Protect against rogue update broadcasts (not really a security issue,
// just filter bad broacasts out so subclasses are less likely to crash).
String action = intent.getAction();
if (AppWidgetManager.ACTION_APPWIDGET_UPDATE.equals(action)) {
Bundle extras = intent.getExtras();
if (extras != null) {
int[] appWidgetIds = extras.getIntArray(AppWidgetManager.EXTRA_APPWIDGET_IDS);
if (appWidgetIds != null && appWidgetIds.length > 0) {
this.onUpdate(context, AppWidgetManager.getInstance(context), appWidgetIds);
}
}
}
(...)
}
What about the following:
boolean widgetExists(Context context, int appWidgetId) {
AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context);
AppWidgetProviderInfo info = appWidgetManager.getAppWidgetInfo(appWidgetId);
return (info != null);
}
From the docs for appWidgetManager.getAppWidgetInfo():
If the appWidgetId has not been bound to a provider yet, or you don't have access to that appWidgetId, null is returned.
Related
I have a widget for my app. On widget Creation when android triggers onEnabled I'm checking if a user meets a certain requirement then the user can go ahead and create a widget. But I need to stop widget creation if they don't meet a certain requirement. I can't figure out how to cancel widget creation dynamically. Here's what I was trying which didn't work.
RemoteViews views;
void updateAppWidget(Context context, AppWidgetManager appWidgetManager,
int appWidgetId) {
// Construct the RemoteViews object
views = new RemoteViews(context.getPackageName(), R.layout.lock_widget);
views.setOnClickPendingIntent(R.id.lock_widget, getPendingSelfIntent(context, LOCK));
// Instruct the widget manager to update the widget
appWidgetManager.updateAppWidget(appWidgetId, views);
}
#Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
// There may be multiple widgets active, so update all of them
for (int appWidgetId : appWidgetIds) {
updateAppWidget(context, appWidgetManager, appWidgetId);
}
}
#Override
public void onEnabled(Context context) {
// Enter relevant functionality for when the first widget is created
// Query User pro status when creating widget
Log.d("Widget", "OnEnabled Fired");
views.removeAllViews(views.getLayoutId());
//views.
}
views.removeAllViews(views.getLayoutId()); doesn't seem to work.
Is there even a way to this. The workaround that I'm using is checking certain requirement check in onUpdate.
The best solution in your case, enable widget component, if user have all "certain requirement".
So i assume a following case. User uses your app, and in some cases (you enable widget component, if user lose his "certain requirement" just disable widget component, so there is no widget in widget picker). You can easily achieve this through PackageManager.
Updated: When user have a pro status, just enable widget, if not - just disable
public static void setComponentState(Context context, boolean enabled) {
int flag = (enabled ?
PackageManager.COMPONENT_ENABLED_STATE_ENABLED :
PackageManager.COMPONENT_ENABLED_STATE_DISABLED);
ComponentName component = new ComponentName(context, YourAppWidgetProvider.class); // or make component via package name and class name (check Component constructors)
PackageManager pm = context.getApplicationContext().getPackageManager();
pm.setComponentEnabledSetting(component, flag, PackageManager.DONT_KILL_APP);
}
Good Day! I want to add text from inside application to widget, i have a main activity and it has list view and lots of text contents and it has a button to add the text to widget via shared preference, it's works fine when i close the widget and recreate it only, otherwise it's not automatically refresh.if anyone know; how to solve this please help me. here i attached the widget code below.
public class WidgetMaster extends AppWidgetProvider {
static void updateAppWidget(Context context, AppWidgetManager appWidgetManager,
int appWidgetId) {
Intent intentHome = new Intent(context, MainActivity.class);
PendingIntent pendingIntentHome = PendingIntent.getActivity(context, 0, intentHome, 0);
RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.widget_master);
views.setOnClickPendingIntent(R.id.wid_home, pendingIntentHome);
final SharedPreferences sharedPreferencestoWi = context.getSharedPreferences(String.valueOf(R.string.addTextToWidgetPref), MODE_PRIVATE);
String forWidget = sharedPreferencestoWi.getString("textToWidget", "");
String dum = "add from reading";
if(forWidget.equals("")){
views.setTextViewText(R.id.dum_appwidget_text, dum);
appWidgetManager.updateAppWidget(appWidgetId, views);
} else {
views.setTextViewText(R.id.appwidget_text, forWidget);
views.setViewVisibility(R.id.appwidget_text, 0);
appWidgetManager.updateAppWidget(appWidgetId, views);
}
}
#Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
// There may be multiple widgets active, so update all of them
for (int appWidgetId : appWidgetIds) {
updateAppWidget(context, appWidgetManager, appWidgetId);
}
}
#Override
public void onEnabled(Context context) {
// Enter relevant functionality for when the first widget is created
}
#Override
public void onDisabled(Context context) {
// Enter relevant functionality for when the last widget is disabled
}
}
How do you update the widget from mainActivity?
In the widget configuration file you can specify a fixed update interval. The smallest update interval is 1800000 milliseconds (30 minutes).
But its better to update widget programmatically, to do this You should send a broadCast to update widget and use a
method like alarmManager or Handler for a repeating task for example you can use the following broadCast to update widget form mainActivity:
Intent intent = new Intent(this, WidgetMaster.class);
intent.setAction(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
int[] ids = AppWidgetManager.getInstance(getApplication())
.getAppWidgetIds(new ComponentName(getApplication(),WidgetMaster.class));
intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, ids);
sendBroadcast(intent);
I am using AlarmManager to update my widgets. And I want to stop it if there is no widget on homescreen. But I am facing a problem with detecting if there is no widget on home screen.
As whenever I try to get the AppWidgetIds using this way:
AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context);
int[] appWidgetIDs = appWidgetManager
.getAppWidgetIds(new ComponentName(context, Widget.class));
I get the a length of appWidgetIDs while actually there is no widget on homescreen. Why?
Therefore, I would like to know if there is a way to detect that a widget id is exists on homescreen.
Thank you upfront.
Congratulations, you've encountered phantom appwidgets. It appears to be documented on the Android issue tracker. They usually occur when the configuration activity for an appwidget is canceled, though it seems to be through improper implementation of the configuration activity; developers neglect to include the appwidget ID as an extra when setting the activity result to RESULT_CANCELED. (even Google's ApiDemos sample application neglects to do this!)
The proper implementation is like this:
public class AppWidgetConfigActivity extends Activity {
private int appWidgetId;
private Intent resultValue;
protected void onCreate(bundle saved) {
super.onCreate(saved);
// get the appwidget id from the intent
Intent intent = getIntent();
appWidgetId = intent.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID,
AppWidgetManager.INVALID_APPWIDGET_ID);
// make the result intent and set the result to canceled
resultValue = new Intent();
resultValue.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);
setResult(RESULT_CANCELED, resultValue);
// if we weren't started properly, finish here
if (appwidgetId == AppWidgetManager.INVALID_APPWIDGET_ID) {
finish();
}
/* ... */
}
/* ... */
private void finishConfigure() {
/* finish configuring appwidget ... */
setResult(RESULT_OK, resultValue);
}
}
Thus far I know of no way to detect the presence of a phantom appwidget without doing your own bookkeeping. I suggest storing a SharedPreferences value indicating that the configuration activity was not canceled and then querying this value in your other code. You can also use this information to "delete" a phantom widget if you come across one. In your appwidget configuration activity:
private void finishConfigure() {
/* finish configuring appwidget ... */
setResult(RESULT_OK, resultValue);
String key = String.format("appwidget%d_configured", appwidgetId);
SharedPreferences prefs = getSharedPreferences("widget_prefs", 0);
prefs.edit().putBoolean(key, true).commit;
}
Then you can check that you have at least one non-phantom appwidget like so:
AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context);
AppWidgetHost appWidgetHost = new AppWidgetHost(context, 1); // for removing phantoms
SharedPreferences prefs = getSharedPreferences("widget_prefs", 0);
boolean hasWidget = false;
int[] appWidgetIDs = appWidgetManager.getAppWidgetIds(new ComponentName(context, Widget.class));
for (int i = 0; i < appWidgetIDs.length; i++) {
int id = appWidgetIDs[i];
String key = String.format("appwidget%d_configured", id);
if (prefs.getBoolean(key, false)) {
hasWidget = true;
} else {
// delete the phantom appwidget
appWidgetHost.deleteAppWidgetId(id);
}
}
if (hasWidget) {
// proceed
} else {
// turn off alarms
}
I've got a widget which will update itself whenever there's a configuration change (such as screen orientation), and whenever the phone is unlocked. This process involves setting onClick handlers for the buttons on my widget. This works well, however I have found that there's a usage case which causes my app to not respond to onClick events. This particular case is whenever the launcher restarts itself.
Is there a way to detect when a launcher restarts, so I can update my widget manually? Or is there another way to ensure onClick handlers are not lost?
Turns out I was spamming new RemoteViews() when I should have just called it once to produce the view, and then referred to that one instance when required. In my solution, I have a class variable which stores this single RemoteView instance, and a getter to access it.
Proposal by #Glitch might not work for certain cases, especially app widget with ListView. This is because ListView will get very slow (Try to scroll the ListView) after appWidgetManager.notifyAppWidgetViewDataChanged(appWidgetId, list_id) had been called several time.
My guess is, the single RemoteView instance will keep all its executed instruction in a list. Over the time, the instruction list will grow. Every time appWidgetManager.notifyAppWidgetViewDataChanged(appWidgetId, list_id), the large instruction list will be executed all over again.
My proposed solution is as follow. However, I believe it will only work on certain device, as not all devices will receive same broadcast message during launcher restarting.
#SuppressLint("NewApi")
#Override
public void onReceive(Context context, Intent intent) {
final String action = intent.getAction();
if (action.equals("com.sec.android.widgetapp.APPWIDGET_RESIZE")) {
// http://stackoverflow.com/questions/17396045/how-to-catch-widget-size-changes-on-devices-where-onappwidgetoptionschanged-not
handleTouchWiz(context, intent);
// Possible launcher restart.
handleLauncherRestart(context, intent);
} else if (action.equals("android.appwidget.action.APPWIDGET_UPDATE_OPTIONS")) {
// Possible launcher restart.
handleLauncherRestart(context, intent);
}
super.onReceive(context, intent);
}
private void handleLauncherRestart(Context context, Intent intent) {
AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context);
int appWidgetId = intent.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID,
AppWidgetManager.INVALID_APPWIDGET_ID);
updateAppWidget(context, appWidgetManager, appWidgetId);
}
private void handleTouchWiz(Context context, Intent intent) {
AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context);
int appWidgetId = intent.getIntExtra("widgetId", 0);
int widgetSpanX = intent.getIntExtra("widgetspanx", 0);
int widgetSpanY = intent.getIntExtra("widgetspany", 0);
if (appWidgetId > 0 && widgetSpanX > 0 && widgetSpanY > 0) {
Bundle newOptions = new Bundle();
// We have to convert these numbers for future use
// http://stackoverflow.com/questions/10008521/appwidget-size-calculation
if (android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
newOptions.putInt(AppWidgetManager.OPTION_APPWIDGET_MIN_HEIGHT, widgetSpanY * 74 - 2);
newOptions.putInt(AppWidgetManager.OPTION_APPWIDGET_MIN_WIDTH, widgetSpanX * 74 - 2);
} else {
newOptions.putInt(AppWidgetManager.OPTION_APPWIDGET_MIN_HEIGHT, widgetSpanY * 70 - 30);
newOptions.putInt(AppWidgetManager.OPTION_APPWIDGET_MIN_WIDTH, widgetSpanX * 70 - 30);
}
onAppWidgetOptionsChanged(context, appWidgetManager, appWidgetId, newOptions);
}
}
I've got a problem when updating my widget manually via AppWidgetManager.updateAppWidget.
Platform is Android 2.2.
Here's the code:
I declared the widget additionally to an existing Activity in the Manifest:
<receiver android:name=".Widget" android:label="#string/app_name">
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
</intent-filter>
<meta-data android:name="android.appwidget.provider" android:resource="#xml/widget" />
</receiver>
The widget-class was declared in Widget.java:
public class Widget extends AppWidgetProvider {
#Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
int use_static_ip;
RemoteViews remoteViews;
try {
use_static_ip = Settings.System.getInt(context.getContentResolver(), Settings.System.WIFI_USE_STATIC_IP);
if (use_static_ip == 0) { //DHCP
remoteViews = new RemoteViews(context.getPackageName(), R.layout.widget_dhcp);
} else { //static IP
remoteViews = new RemoteViews(context.getPackageName(), R.layout.widget_static);
}
Intent call_activity = new Intent(context, StaticIPToggle.class);
PendingIntent pending_call_activity = PendingIntent.getActivity(context, 0, call_activity, 0);
remoteViews.setOnClickPendingIntent(R.id.widget_icon, pending_call_activity);
appWidgetManager.updateAppWidget(appWidgetIds, remoteViews);
} catch (SettingNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
In the existing activity I added some lines to manually update the widget:
AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(getApplicationContext());
ComponentName componentName = new ComponentName(getApplicationContext(), Widget.class);
int[] ids = appWidgetManager.getAppWidgetIds(componentName);
Intent update_widget = new Intent();
update_widget.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, ids);
update_widget.setAction(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
getApplicationContext().sendBroadcast(update_widget);
But now I have this bug:
If the widget is onClicked, it shows the energy-settings-widget for a short period (~0,5sec-1sec) before the desired widget is shown.
Here a screenshot of this issue (sequence from left to right): http://i.stack.imgur.com/rHkjw.jpg
First there's the widget as in the left picture, then changes to the middle picture and either stays there or changes to the right picture after ~0,5-1sec.
Unfortunately I can't see any mistake in the code, do you?
Hopefully you can help me to fix this problem ;)
Thanks in advance,
regards, Oli
I had exactly the same problem. I solved it by manually updating the widget NOT through the broadcast, but by directly calling AppWidgetManager.updateAppWidget(ComponentName, RemoteViews) instead, from an AsyncTask.
Try this inside of your Widget class:
/**
* Asynchronously builds views and updates the widget.
*/
private static class UpdateWidgetTask extends AsyncTask<Void, Void, RemoteViews> {
private AppWidgetManager mAppWidgetManager;
private Context mContext;
private UpdateWidgetTask(AppWidgetManager appWidgetManager, Context context) {
mAppWidgetManager = appWidgetManager;
mContext = context;
}
#Override
protected RemoteViews doInBackground(Void... params) {
DebugLog.d(TAG, "Asynchronously updating widget.");
RemoteViews views = buildUpdate(mContext);
return views;
}
#Override
protected void onPostExecute(RemoteViews views) {
ComponentName thisWidget = new ComponentName(mContext, Widget.class);
mAppWidgetManager.updateAppWidget(thisWidget, views);
}
}
/**
* Asynchronously updates all widgets of this type. This is the fastest way to update the widget.
*
* #param context The context.
*/
public static void updateWidget(Context context) {
new UpdateWidgetTask(AppWidgetManager.getInstance(context), context).execute();
}
Then update by calling Widget.updateWidget(this) from your activity.
I used the same programatic way to manually update the widget as you did (From Is it possible to throw an intent for APPWIDGET_UPDATE programmatically?) and found this to be the exact cause of the issue.
This still happens on 4.2 and 2.3, Official and Unofficial ROMS (Touchwiz goes absolutely wild when this happens).
The only way I could fix this was to remove the whole update involving the broadcast and to update the widget remotely by sending a regular Broadcast to the Widget Provider and calling
ComponentName thisWidget = new ComponentName(context,AvailabilityWidgetProvider.class);
AppWidgetManager.getInstance(context).updateAppWidget(thisWidget,views);
from within onReceive
(This works for me as the my widgets are identical)
This way, the issue seemed to not occur and I could update the widget remotely.
Old question, I know, but deserves an answer as this was all I could find on this weird issue.