onDataSetChanged is not called in RemoteViewsFactory after notifyAppWidgetViewDataChanged is called - android

I'm trying to implement android app with widget, but found strange behavior on nexus 5 devices (i have two - one with lollipop, one with marshmallow). If I call AppWidgetManager.notifyAppWidgetViewDataChanged, widget's ListView content is refreshed on other devices (Nexus 3, ASUS K010), but on these two nothing happens. Couldn't find any info about this.
So here's my code: MainActivity.java.
public class MainActivity extends AppCompatActivity {
public static final String PREFS_TAG = "StringList";
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
View v = findViewById(R.id.fill);
v.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
fillWidget();
}
});
v= findViewById(R.id.clear);
v.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
clearWidget();
}
});
}
private void fillWidget(){
SharedPreferences preferences = getSharedPreferences(WidgetRemoteService.class.getSimpleName(), Context.MODE_PRIVATE);
SharedPreferences.Editor editor = preferences.edit();
Set<String> strings = new HashSet<>();
strings.add("111111111111111");
strings.add("222222222222222");
strings.add("333333333333333");
strings.add("444444444444444");
strings.add("555555555555555");
strings.add("666666666666666");
strings.add("777777777777777");
strings.add("888888888888888");
strings.add("999999999999999");
editor.putStringSet(PREFS_TAG, strings);
editor.commit();
AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(this);
ComponentName componentName = new ComponentName(getApplicationContext(),WidgetProvider.class);
int[] appWidgetIds = appWidgetManager.getAppWidgetIds(componentName);
appWidgetManager.notifyAppWidgetViewDataChanged(appWidgetIds, R.id.listView);
}
private void clearWidget(){
SharedPreferences preferences = getSharedPreferences(WidgetRemoteService.class.getSimpleName(), Context.MODE_PRIVATE);
SharedPreferences.Editor editor = preferences.edit();
editor.remove(PREFS_TAG);
editor.commit();
AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(this);
ComponentName componentName = new ComponentName(getApplicationContext(),WidgetProvider.class);
int[] appWidgetIds = appWidgetManager.getAppWidgetIds(componentName);
appWidgetManager.notifyAppWidgetViewDataChanged(appWidgetIds, R.id.listView);
}
}
WidgetProvider.java
public class WidgetProvider extends AppWidgetProvider {
private static final String TAG = WidgetProvider.class.getSimpleName();
#Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
super.onUpdate(context, appWidgetManager, appWidgetIds);
for (int appWidgetId : appWidgetIds) {
appWidgetManager.updateAppWidget(appWidgetId,createRemoteViews(context, appWidgetId));
}
}
public static RemoteViews createRemoteViews(Context context, int appWidgetId){
Log.e(TAG, "Creating remote views");
Intent intent = new Intent(context, WidgetRemoteService.class);
// Add the app widget ID to the intent extras.
Log.e(TAG, "Creating remote views for widget #"+appWidgetId);
intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);
intent.setData(Uri.parse(intent.toUri(Intent.URI_INTENT_SCHEME)));
RemoteViews result = new RemoteViews(WidgetProvider.class.getPackage().getName(),R.layout.widget_layout);
result.setRemoteAdapter(R.id.listView,intent);
return result;
}
}
WidgetRemoteService.java
public class WidgetRemoteService extends RemoteViewsService {
private static final String TAG = WidgetRemoteService.class.getSimpleName();
#Override
public RemoteViewsFactory onGetViewFactory(Intent intent) {
int appWidgetID = intent.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID,
AppWidgetManager.INVALID_APPWIDGET_ID);
return new WidgetViewsFactory(getApplicationContext(), appWidgetID);
}
public static class WidgetViewsFactory implements RemoteViewsService.RemoteViewsFactory{
private Context context;
private String[] strings;
private int appWidgetId;
public WidgetViewsFactory(Context context, int appWidgetId){
Log.e(TAG, "Factory created");
this.context = context;
this.appWidgetId = appWidgetId;
}
#Override
public void onCreate() {
}
#Override
public void onDataSetChanged() {
Log.e(TAG, "Dataset changed");
SharedPreferences preferences = context.getSharedPreferences(WidgetRemoteService.class.getSimpleName(), Context.MODE_PRIVATE);
Set<String> stringSet = preferences.getStringSet(MainActivity.PREFS_TAG, new HashSet<String>());
strings = stringSet.toArray(new String[]{});
}
#Override
public void onDestroy() {
}
#Override
public int getCount() {
if (strings==null)
return 0;
else
return strings.length;
}
#Override
public RemoteViews getViewAt(int position) {
RemoteViews result = new RemoteViews(getClass().getPackage().getName(),R.layout.widget_item);
result.setTextViewText(R.id.textTitle,"This is title");
result.setTextViewText(R.id.textContent,strings[position]);
return result;
}
#Override
public RemoteViews getLoadingView() {
return null;
}
#Override
public int getViewTypeCount() {
return 1;
}
#Override
public long getItemId(int position) {
return 0;
}
#Override
public boolean hasStableIds() {
return false;
}
}
}
Here I try to update widget from MainActivity, but somehow it doesn't work on my nexus 5 devices - according to logs onDataSetChanged() is never called on this devices, but on others it works. Maybe I misunderstood something in guides, and it wasn't supposed to work, and works on other devices only because of some bug, which was fixed in later android versions?

try add
android:permission="android.permission.BIND_REMOTEVIEWS"
to RemoteViewsService in your AndroidManifest.xml, I was facing this problem too today.

I had a breakpoint set, wondering why onDataSetChanged wasn't getting called. It turns out that it got called, but only after I backgrounded my app and the widget was visible!

Related

Replace RemoteViewsFactory on receiving a broadcast in widget provider

I have an app with a widget showing a listview. I want to update the list based on the recipe that user opens in the app.
To do that first I send a broadcast with an extra integer when activity is open.
Then in widget provider I am setting remote adapter with an intent containing integer received from broadcast.
Now I would expect new RemoteViewsFactory to be created each time so I can extract an integer from an intent and load different list based on this number.
The problem is this only happens when an app is first open, every other time only onDataSetChanged() in MyWidgetRemoteViewsFactory is called so I cannot get the recipe number to update data correctly. The list in widget never gets updated.
How to force widget to recreate RemoteViewsFactory? Based on the other topic on stackoverflow I have tried passing null in appWidgetManager.updateAppWidget(appWidgetId1, null) - this didn't work.
MainListActivity.java - send a broadcast
#Override
public void onItemClickListener(int itemID) {
Intent recipeIntent = new Intent(this, BakingWidgetProvider.class);
recipeIntent.setAction(BakingWidgetProvider.UPDATE_WIDGET_RECIPE);
recipeIntent.putExtra(StepsListActivity.EXTRA_RECIPE_ID, itemID);
sendBroadcast(recipeIntent);
Intent intent = new Intent(MainListActivity.this, StepsListActivity.class);
intent.putExtra(StepsListActivity.EXTRA_RECIPE_ID, itemID);
startActivity(intent);
}
BakingWidgetProvider.java - Receive broadcast
#Override
public void onReceive(Context context, Intent intent) {
AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context);
if (intent.getAction().equals(UPDATE_WIDGET_RECIPE)) {
RemoteViews remoteViews = new RemoteViews(context.getPackageName(), R.layout.baking_widget);
int appWidgetId = intent.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, AppWidgetManager.INVALID_APPWIDGET_ID);
int viewIndex = intent.getIntExtra(StepsListActivity.EXTRA_RECIPE_ID, 0);
int[] appWidgetIds = appWidgetManager.getAppWidgetIds(new ComponentName(context, BakingWidgetProvider.class));
for (int appWidgetId1 : appWidgetIds) {
//trying to pass null to clear the data ?
appWidgetManager.updateAppWidget(appWidgetId1, null);
}
Intent intent2 = new Intent(context, MyWidgetRemoteViewsService.class);
Bundle bundle = new Bundle();
bundle.putInt(StepsListActivity.EXTRA_RECIPE_ID,viewIndex );
intent2.putExtras(bundle);
remoteViews.setRemoteAdapter(R.id.list_view, intent2);
appWidgetManager.notifyAppWidgetViewDataChanged(appWidgetIds, R.id.list_view);
//empty view
remoteViews.setEmptyView(R.id.list_view, R.id.empty_view);
for (int appWidgetId1 : appWidgetIds) {
appWidgetManager.updateAppWidget(appWidgetId1, remoteViews);
}
}
super.onReceive(context, intent);
}
MyWidgetRemoteViewsService.java - get an integer from an intent to update data properly
public class MyWidgetRemoteViewsService extends RemoteViewsService {
private static final String MyOnClick = "myOnClickTag";
#Override
public RemoteViewsFactory onGetViewFactory(Intent intent) {
return new MyWidgetRemoteViewsFactory(this.getApplicationContext(), intent);
}
class MyWidgetRemoteViewsFactory implements RemoteViewsService.RemoteViewsFactory {
List<Recipe> recipeList;
private Context mContext;
private int mRecipeOpen;
public MyWidgetRemoteViewsFactory(Context context, Intent intent) {
mRecipeOpen = intent.getExtras().getInt(StepsListActivity.EXTRA_RECIPE_ID);
mContext = context;
}
#Override
public void onCreate() {
}
#Override
public void onDataSetChanged() {
recipeList = AppDatabase.getInstance(getApplicationContext()).recipeDao().getAll();
}
#Override
public void onDestroy() {
}
#Override
public int getCount() {
return recipeList.get(mRecipeOpen).ingredients.size();
}
#Override
public RemoteViews getViewAt(int position) {
RemoteViews rv = new RemoteViews(mContext.getPackageName(), R.layout.widget_list_item);
rv.setTextViewText(R.id.quantity, recipeList.get(mRecipeOpen).ingredients.get(position).getQuantity());
rv.setTextViewText(R.id.measurement, recipeList.get(mRecipeOpen).ingredients.get(position).getMeasure());
rv.setTextViewText(R.id.ingredient, recipeList.get(mRecipeOpen).ingredients.get(position).getIngredient());
return rv;
}
#Override
public RemoteViews getLoadingView() {
return null;
}
#Override
public int getViewTypeCount() {
return 1;
}
#Override
public long getItemId(int position) {
return position;
}
#Override
public boolean hasStableIds() {
return true;
}
}
}
In the end I had to change my approach.
I have placed MyWidgetRemoteViewsFactory in a separate class and extended BroadcastReceiver. Here in onReceive method I can retrieve the broadcast with the required integer that is sent from BakingWidgetProvider.

Same widgets with different ListView data

I have a simple AppWidget that has ListView.
The widget have simple config Activity, where you select which set of data you want to display. That info (int id) is stored into SharedPreferences and later get from SP in RemoteViewsFactory.
All is working perfectly, until I add another Widget to the screen. The new one doesn't load different data if selected, but rather takes the one from previous Widget.
With little debugging I think that my onUpdate method is't being called, but I cannot find the problem here.
WidgetConfig.java
public class IngredientsWidgetConfigureActivity extends Activity implements RecipesAdapter.RecipesAdapterOnClickHandler {
int mAppWidgetId = AppWidgetManager.INVALID_APPWIDGET_ID;
RecipesAdapter mAdapter;
public IngredientsWidgetConfigureActivity(){
super();
}
//save recipeId into SharedPrefs
static void saveSelectedRecipe (Context context, int appWidgetId, int selectedRecipe){
SharedPreferences.Editor prefs = context.getSharedPreferences(AppConstants.PREFS_NAME, 0).edit();
prefs.putInt(AppConstants.PREF_PREFIX_KEY + appWidgetId, selectedRecipe);
prefs.apply();
}
#Override
protected void onCreate(#Nullable Bundle icicle) {
super.onCreate(icicle);
setResult(RESULT_CANCELED);
setContentView(R.layout.ingredients_widget_configure);
//setup RecyclerView
...
Intent intent = getIntent();
Bundle extras = intent.getExtras();
if (extras != null){
mAppWidgetId = extras.getInt(AppWidgetManager.EXTRA_APPWIDGET_ID, AppWidgetManager.INVALID_APPWIDGET_ID);
}
if (mAppWidgetId == AppWidgetManager.INVALID_APPWIDGET_ID){
finish();
return;
}
}
#Override
public void onClick(Recipe recipe) {
final Context context = IngredientsWidgetConfigureActivity.this;
// When the button is clicked, store the recipe Id
int selectedRecipe = recipe.id;
saveSelectedRecipe(context, mAppWidgetId, selectedRecipe);
// It is the responsibility of the configuration activity to update the app widget
AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context);
IngredientsWidget.updateAppWidget(context, appWidgetManager, mAppWidgetId);
// Make sure we pass back the original appWidgetId
Intent resultValue = new Intent();
resultValue.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, mAppWidgetId);
setResult(RESULT_OK, resultValue);
finish();
}
WidgetProvider
....
static void updateAppWidget(final Context context, AppWidgetManager appWidgetManager,
int appWidgetId) {
RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.ingredients_widget_layout);
Intent intent = new Intent(context, WidgetRemoteViewsService.class);
intent.putExtra(AppConstants.APP_WIDGET_ID_KEY, appWidgetId);
views.setRemoteAdapter(R.id.widget_list_view, intent);
//TODO: recipes are not changing in different widgets
// 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);
}
}
And finally my
RemoteViewsFactory.java
public WidgetRemoteViewsFactory (Context context, Intent intent){
mContext = context;
appWidgetId = intent.getIntExtra(AppConstants.APP_WIDGET_ID_KEY, 1);
}
#Override
public void onCreate() {
}
#Override
public void onDataSetChanged() {
final long identityToken = Binder.clearCallingIdentity();
//get id from SharedPref
RecipeDatabase mDb = RecipeDatabase.getInstance(mContext);
SharedPreferences prefs = mContext.getSharedPreferences(AppConstants.PREFS_NAME, 0);
recipeId = prefs.getInt(AppConstants.PREF_PREFIX_KEY + appWidgetId, 9999);
recipe = mDb.recipeDAO().loadRecipe(recipeId);
Binder.restoreCallingIdentity(identityToken);
}
#Override
public void onDestroy() {
}
#Override
public int getCount() {
if (recipe == null) {
return 0;
} else {
return recipe.ingredients.size();
}
}
#Override
public RemoteViews getViewAt(int position) {
if (position > recipe.ingredients.size()){
return null;
}
RemoteViews remoteViews = new RemoteViews(mContext.getPackageName(), R.layout.widget_list_item);
remoteViews.setTextViewText(R.id.widget_text, recipe.ingredients.get(position).getIngredient());
return remoteViews;
}
#Override
public RemoteViews getLoadingView() {
return null;
}
#Override
public int getViewTypeCount() {
return 1;
}
#Override
public long getItemId(int position) {
return position;
}
#Override
public boolean hasStableIds() {
return true;
}
}
Solved...
I had to put new data into the Intent in updateAppWidget so the Factory loads new instance instead of reusing new one.
So I am sending new random value data with every widget, so it updates properly.
updateAppWidget snippet:
//send intent and data to Adapter
Intent intent = new Intent(context, WidgetRemoteViewsService.class);
intent.putExtra(AppConstants.APP_WIDGET_ID_KEY, appWidgetId);
//set random data to initialize new Factory
Random rnd = new Random();
intent.setData(Uri.fromParts("content", String.valueOf(rnd.nextInt()), null));
views.setRemoteAdapter(R.id.widget_list_view, intent);
Thanks to #TheFedex87

How to get Click event on List Item of Home Screen widget in android [duplicate]

This question already has answers here:
Android start activity when pressing widget ListView item
(2 answers)
how to attach a onClick Listener to a listview item on an app widget
(3 answers)
listview in app widget android
(1 answer)
Android clickable Widget with ListView not clickable on ListItems
(1 answer)
Closed 5 years ago.
Issue
I have widget on homescreen with Listview which dynamic data item, now
can I get click event on listview item click.
As I can get click of another button on widget but still not success on list item click.
SimpleWidgetProvider.Java
public class SimpleWidgetProvider extends AppWidgetProvider {
public static String UPDATE_ACTION = "ActionUpdateSinglenoteWidget";
public static final String TOAST_ACTION = "com.varshaaweblabs.estateblock.TOAST_ACTION";
public static final String EXTRA_ITEM = "com.varshaaweblabs.estateblock.EXTRA_ITEM";
private static final String MyButton1 = "mybutton1";
private static final String MyButton2 = "mybutton2";
static RemoteViews remoteViews;
public static SharedPreferences.Editor editor;
public static SharedPreferences pref;
public Call<Search_Response> registerResponseCall;
ArrayList<Listing> listItemList = new ArrayList<>();
Gson gson1 = new Gson();
public static GoogleMap mMap;
ArrayList<String> jarray = new ArrayList<>();
public Criteria criteria = new Criteria();
public void updateAppWidget(final Context context, final AppWidgetManager appWidgetManager,
final int appWidgetId) {
remoteViews = new RemoteViews(context.getPackageName(), R.layout.widget_layout);
pref = context.getSharedPreferences("estateblock", MODE_PRIVATE);
editor = pref.edit();
getData(context, MyButton1);
remoteViews.setInt(R.id.btn_current, "setBackgroundColor", Color.MAGENTA);
remoteViews.setInt(R.id.btn_home, "setBackgroundColor", Color.LTGRAY);
remoteViews.setInt(R.id.btn_seeall, "setBackgroundColor", Color.MAGENTA);
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
// Set up the collection
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
setRemoteAdapter(context, remoteViews);
} else {
setRemoteAdapterV11(context, remoteViews);
}
// Instruct the widget manager to update the widget
appWidgetManager.updateAppWidget(appWidgetId, remoteViews);
remoteViews.setOnClickPendingIntent(R.id.btn_current, getPendingSelfIntent(context, MyButton1));
remoteViews.setOnClickPendingIntent(R.id.btn_home, getPendingSelfIntent(context, MyButton2));
remoteViews.setViewVisibility(R.id.progress, View.GONE);
remoteViews.setViewVisibility(R.id.widget_list, View.VISIBLE);
pushWidgetUpdate(context, remoteViews);
notifyUpdate(context, remoteViews);
}
}, 5000);
}
#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);
}
super.onUpdate(context, appWidgetManager, appWidgetIds);
}
#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
}
/**
* Sets the remote adapter used to fill in the list items
*
* #param views RemoteViews to set the RemoteAdapter
*/
#TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
private static void setRemoteAdapter(Context context, #NonNull final RemoteViews views) {
views.setRemoteAdapter(R.id.widget_list,
new Intent(context, WidgetService.class));
}
/**
* Sets the remote adapter used to fill in the list items
*
* #param views RemoteViews to set the RemoteAdapter
*/
#SuppressWarnings("deprecation")
private static void setRemoteAdapterV11(Context context, #NonNull final RemoteViews views) {
views.setRemoteAdapter(0, R.id.widget_list,
new Intent(context, WidgetService.class));
}
protected PendingIntent getPendingSelfIntent(Context context, String action) {
Intent intent = new Intent(context, getClass());
intent.setAction(action);
return PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
}
public static void pushWidgetUpdate(Context context, RemoteViews remoteViews) {
ComponentName myWidget = new ComponentName(context, SimpleWidgetProvider.class);
AppWidgetManager manager = AppWidgetManager.getInstance(context);
manager.updateAppWidget(myWidget, remoteViews);
}
public void notifyUpdate(Context context, RemoteViews remoteViews) {
AppWidgetManager manager = AppWidgetManager.getInstance(context);
int appWidgetIds[] = manager.getAppWidgetIds(
new ComponentName(context, SimpleWidgetProvider.class));
manager.notifyAppWidgetViewDataChanged(appWidgetIds, R.id.widget_list);
hideProgress(remoteViews, context);
}
#Override
public void onReceive(Context context, Intent intent) {
super.onReceive(context, intent);
String action = intent.getAction();
AppWidgetManager mgr = AppWidgetManager.getInstance(context);
if (intent.getAction().equals(TOAST_ACTION)) {
int appWidgetId = intent.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID,
AppWidgetManager.INVALID_APPWIDGET_ID);
int viewIndex = intent.getIntExtra(EXTRA_ITEM, 0);
Toast.makeText(context, "Touched view " + viewIndex, Toast.LENGTH_SHORT).show();
}
if (MyButton1.equals(intent.getAction())) {
pref = context.getSharedPreferences("estateblock", MODE_PRIVATE);
editor = pref.edit();
listItemList.clear();
updateData(context, MyButton1);
Toast.makeText(context, "Current Criteria", Toast.LENGTH_SHORT).show();
}
if (MyButton2.equals(intent.getAction())) {
pref = context.getSharedPreferences("estateblock", MODE_PRIVATE);
editor = pref.edit();
listItemList.clear();
updateData(context, MyButton2);
}
}
private void updateData(final Context context, String myButton) {
if (myButton.equalsIgnoreCase("mybutton1")) {
final RemoteViews remoteViews = new RemoteViews(context.getPackageName(), R.layout.widget_layout);
showProgress(remoteViews, context);
remoteViews.setInt(R.id.btn_home, "setBackgroundColor", Color.LTGRAY);
remoteViews.setInt(R.id.btn_current, "setBackgroundColor", Color.MAGENTA);
Intent configIntent = new Intent(context, HomeActivity.class);
configIntent.putExtra("widget", "Current");
configIntent.setAction("current");
PendingIntent configPendingIntent = PendingIntent.getActivity(context, 0, configIntent, PendingIntent.FLAG_UPDATE_CURRENT);
remoteViews.setOnClickPendingIntent(R.id.btn_seeall, configPendingIntent);
Toast.makeText(context, "Current", Toast.LENGTH_SHORT).show();
pushWidgetUpdate(context.getApplicationContext(), remoteViews);
getData(context, MyButton1);
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
notifyUpdate(context.getApplicationContext(), remoteViews);
}
}, 3500);
}
if (myButton.equalsIgnoreCase("mybutton2")) {
final RemoteViews remoteViews = new RemoteViews(context.getPackageName(), R.layout.widget_layout);
showProgress(remoteViews, context);
remoteViews.setInt(R.id.btn_home, "setBackgroundColor", Color.MAGENTA);
remoteViews.setInt(R.id.btn_current, "setBackgroundColor", Color.LTGRAY);
Intent configIntent1 = new Intent(context, HomeActivity.class);
configIntent1.putExtra("widget", "Home");
configIntent1.setAction("Home");
PendingIntent configPendingIntent1 = PendingIntent.getActivity(context, 1, configIntent1, PendingIntent.FLAG_UPDATE_CURRENT);
remoteViews.setOnClickPendingIntent(R.id.btn_seeall, configPendingIntent1);
Toast.makeText(context, "All Homes", Toast.LENGTH_SHORT).show();
pushWidgetUpdate(context.getApplicationContext(), remoteViews);
getData(context, MyButton2);
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
notifyUpdate(context.getApplicationContext(), remoteViews);
}
}, 3500);
}
}
}
ListProvider.java
public class ListProvider implements RemoteViewsService.RemoteViewsFactory {
private Context context = null;
private int appWidgetId;
List<Listing> list = new ArrayList<>();
List<String> mCollection = new ArrayList<>();
public static List<Listing> listItemList = new ArrayList<>();
SharedPreferences.Editor editor;
SharedPreferences pref;
Gson gson = new Gson();
public ListProvider(Context context, Intent intent) {
this.context = context;
pref = context.getSharedPreferences(Utility.APP_NAME, MODE_PRIVATE);
editor = pref.edit();
Log.e("Constructor", "Called");
populateListItem();
}
public void populateListItem() {
listItemList.clear();
String arrayListString = pref.getString("widget", "");
Type type = new TypeToken<ArrayList<Listing>>() {
}.getType();
listItemList = gson.fromJson(arrayListString, type);
}
#Override
public int getCount() {
return listItemList.size();
}
#Override
public long getItemId(int position) {
return position;
}
#Override
public RemoteViews getViewAt(final int position) {
final RemoteViews remoteView = new RemoteViews(
context.getPackageName(), R.layout.grid_view);
Bitmap image = null;
try {
URL url = new URL(listItemList.get(position).getPrimaryPhoto().getUrl().getThumb());
image = BitmapFactory.decodeStream(url.openConnection().getInputStream());
} catch (IOException e) {
Log.e("Image_load", e.getMessage());
}
remoteView.setImageViewBitmap(R.id.widget_item_app_icon, image);
remoteView.setTextViewText(R.id.heading, listItemList.get(position).getPriceForMap());
remoteView.setTextViewText(R.id.address, listItemList.get(position).getArea());
Intent intent = new Intent(context, Propertyfulldetailactivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, intent, 0);
intent.putExtra(Utility.LISTING_KEY, listItemList.get(position).getListingKey());
Bundle args = new Bundle();
args.putSerializable("ARRAYLIST", (Serializable) listItemList.get(position));
intent.putExtra("BUNDLE", args);
remoteView.setOnClickPendingIntent(R.id.widget_item_app_icon, pendingIntent);
return remoteView;
}
#Override
public RemoteViews getLoadingView() {
return null;
}
#Override
public int getViewTypeCount() {
return 1;
}
#Override
public boolean hasStableIds() {
return true;
}
#Override
public void onCreate() {
Log.e("onCreate", "Called");
populateListItem();
}
#Override
public void onDataSetChanged() {
populateListItem();
}
#Override
public void onDestroy() {
}
}
So please refer my code and try to help me out with this issue.
This is very simple to add a item click listener in list-view. You just take a look in Android List-view link.

notifyappwidgetviewdatachanged not working on a listview on my widget

I tried lots of solutions but after weeks i have not been able to solve this issue: why "notifyappwidgetviewdatachanged" doesn't work? how can i update a listview placed on my widget? Where am i wrong?
Here are my classes.
Widget Provider:
public class Widget_Provider extends AppWidgetProvider
{
public static final String ACTION_MOSTRAORARI = "fACTION_TOAST";
public static final String EXTRA_STRING = "EXTRA_STRING";
#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)
{
RemoteViews views = updateWidgetListView(context, appWidgetId);
final Intent onItemClick = new Intent(context, Widget_Provider.class);
onItemClick.setAction(ACTION_MOSTRAORARI);
onItemClick.setData(Uri.parse(onItemClick.toUri(Intent.URI_INTENT_SCHEME)));
final PendingIntent onClickPendingIntent = PendingIntent.getBroadcast(context, 0, onItemClick, PendingIntent.FLAG_UPDATE_CURRENT);
views.setPendingIntentTemplate(R.id.myStopList, onClickPendingIntent);
appWidgetManager.updateAppWidget(appWidgetId, views);
}
super.onUpdate(context, appWidgetManager, appWidgetIds);
}
public RemoteViews updateWidgetListView(Context context, int appWidgetId)
{
RemoteViews remoteViews = new RemoteViews(context.getPackageName(), R.layout.widget);
Intent svcIntent = new Intent(context, Widget_Service.class);
svcIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);
svcIntent.setData(Uri.parse(svcIntent.toUri(Intent.URI_INTENT_SCHEME)));
remoteViews.setRemoteAdapter(R.id.myStopList, svcIntent);
return remoteViews;
}
#Override
public void onReceive(Context context, Intent intent)
{
if (intent.getAction().equals(ACTION_MOSTRAORARI)) {
if (MainUtils.isNewtworkAvailable(context))
{
String item = intent.getExtras().getString(EXTRA_STRING);
Intent intentOrari = new Intent(context, Diag_MostraOrari.class);
intentOrari.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
context.startActivity(intentOrari);
}
}
super.onReceive(context, intent);
}
#Override
public void onDeleted(Context context, int[] appWidgetIds) {}
#Override
public void onEnabled(Context context) {}
#Override
public void onDisabled(Context context) {}
}
Widget Service:
public class Widget_Service extends RemoteViewsService
{
#Override
public RemoteViewsFactory onGetViewFactory(Intent intent)
{
Map<String, ArrayList<String>> map = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
/* ---
* Here i fetch data from a local db and store it on "map"
*/ ---
return (new Widget_ListProvider(this.getApplicationContext(), intent, map));
}
}
ListProvider:
public class Widget_ListProvider implements RemoteViewsFactory
{
private Map<String, ArrayList<String>> map = new HashMap<>();
private ArrayList<ListItem_Widget> listItemList = new ArrayList<>();
private Context context = null;
private int appWidgetId;
public Widget_ListProvider(Context context, Intent intent, Map<String, ArrayList<String>> map)
{
this.map = map;
this.context = context;
appWidgetId = intent.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, AppWidgetManager.INVALID_APPWIDGET_ID);
populateListItem();
}
//This function populate the arraylist "listItemList" by the data stored on "map"
private void populateListItem() { [...] }
#Override
public int getCount() {
return listItemList.size();
}
#Override
public long getItemId(int position) {
return position;
}
#Override
public RemoteViews getViewAt(int position)
{
final RemoteViews remoteView = new RemoteViews(context.getPackageName(), R.layout.listitem_widget);
ListItem_Widget listItem = listItemList.get(position);
remoteView.setTextViewText(R.id.heading, listItem.heading);
remoteView.setTextViewText(R.id.content, listItem.content);
final Intent fillInIntent = new Intent();
fillInIntent.setAction(Widget.ACTION_MOSTRAORARI);
final Bundle bundle = new Bundle();
bundle.putString(Widget.EXTRA_STRING, listItem.heading);
fillInIntent.putExtras(bundle);
remoteView.setOnClickFillInIntent(R.id.listrow, fillInIntent);
return remoteView;
}
#Override
public RemoteViews getLoadingView() {
return null;
}
#Override
public int getViewTypeCount() {
return 1;
}
#Override
public boolean hasStableIds() {
return true;
}
#Override
public void onCreate() {}
#Override
public void onDataSetChanged() {}
#Override
public void onDestroy() {}
}
The xml of my listview custom item contains just two textviews: "heading" and "content".
Where am i wrong? why when i call "notifyappwidgetviewdatachanged" from another activity nothing happens?
[EDIT]
That's the activity where i need to update my widget.
public class Diag_Line extends AppCompatActivity
{
//[...]
#Override
protected void onCreate(Bundle savedInstanceState)
{
//[...]
AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(getApplicationContext());
ComponentName thisAppWidget = new ComponentName(getApplicationContext().getPackageName(), Widget.class.getName());
int[] appWidgetIds = appWidgetManager.getAppWidgetIds(thisAppWidget);
appWidgetManager.notifyAppWidgetViewDataChanged(appWidgetIds, R.id.myStopList);
//"myStopList" is the id of my listview inside the widget xml.
//[...]
}
}
Well, the problem is a quite obvious: In your RemoteViewsFactory you have an empty method onDataSetChanged(). But when you're triggering notifyAppWidgetViewDataChanged(), you're getting callback in onDataSetChanged(). For better understanding check this pic.
If Diag_Line is a Configuration Activity just make something like this:
appWidgetManager.updateAppWidget(appWidgetIds, views); // views is a RemoteViews that you need to build
appWidgetManager.notifyAppWidgetViewDataChanged(appWidgetIds, R.id.myStopList);
Also check official documentation here
UPDATE
Some hints for you. If you're working with widget, make all fetching data and fillings RemoteViews on background (IntentService is a nice approach). So just make IntentService and make logic for widget here. Don't forget to make him foreground (only for Android O), if you're triggering your updates by broadcasts. Also you can check library that makes all for you.
In order for notifyAppWidgetViewDataChanged() to work properly
we need to retreive the data in onDataSetChanged() method of RemoteViewSourceFactory
like this:
override fun onDataSetChanged() {
listOfArticles = newsDatabaseRepo.getSynchronouslySavedNews() as ArrayList<Articles>
}
as suggested in here
And have a method to refresh the widget if a change occured:
private fun refreshWidget() {
val appWidgetManager =
AppWidgetManager.getInstance(applicationContext)
val thisAppWidget = ComponentName(
applicationContext.packageName,
MyAppWidgetProvider::class.java.name
)
val appWidgetIds = appWidgetManager.getAppWidgetIds(thisAppWidget)
appWidgetManager.notifyAppWidgetViewDataChanged(appWidgetIds, R.id.stack_view)
}
After calling the above method and once I come out of my App I am able to notice the changes in My Widget
So in order to check if onDataSetChanged is called after notifyAppWidgetViewDataChanged()
I kept a debug point and onDataSetChanged() was called and the list was updated as suggested in here here
Had a similar problem updating my widget, it was calling onDataSetChanged but not refreshing the layout. So in my case I had to set updateAppWidget to null before updating with remoteviews like this:
appWidgetManager.updateAppWidget(appWidgetId, null);
appWidgetManager.updateAppWidget(appWidgetId, remoteViews);
Looks like there is some sort of caching happening here.

How to automatically update widget in home screen

I have completed one task application in android.And now i create one widget for this application.In my widget i have display list of task for today,which is working correctly.My problem is when i go to my application and add some task in today and then back to the home screen the widget having only old data instead of new data and no modification...please any one help me....
My RemoteFactory class:
public class TaskItemStatus implements RemoteViewsService.RemoteViewsFactory {
Context context;
int appWidgetId;
String statusRemainingTask="false";
String[] items;
private final String TAG = "CalendarViewSample:"
+ this.getClass().getName();
public TaskItemStatus(Context context, Intent intent) {
this.context = context;
appWidgetId = intent.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID,
AppWidgetManager.INVALID_APPWIDGET_ID);
getData();
}
#SuppressLint("SimpleDateFormat")
private void getData() {
List<String> listTask=new ArrayList<String>();
Taskdatabase objTaskDb = new Taskdatabase(this.context);
objTaskDb.Open();
Calendar calendarToday = Calendar.getInstance();
SimpleDateFormat simpledateFormat = new SimpleDateFormat("dd-MM-yyyy");
String dateToday = simpledateFormat.format(calendarToday.getTime());
listTask.addAll(objTaskDb.fetchTodayRemainTask(dateToday, statusRemainingTask));
Log.i(TAG,"ListTask:"+listTask.toString());
items=new String[listTask.size()];
items=listTask.toArray(items);
}
#Override
public int getCount() {
return (items.length);
}
#Override
public long getItemId(int position) {
return position;
}
#Override
public RemoteViews getLoadingView() {
return null;
}
#Override
public RemoteViews getViewAt(int position) {
RemoteViews row = new RemoteViews(context.getPackageName(),
R.layout.widgetrow);
row.setTextViewText(android.R.id.text1, items[position]);
Intent i = new Intent();
//Bundle extras = new Bundle();
//extras.putString(WidgetTaskSchedular.EXTRA_WORD, items[position]);
//i.putExtras(extras);
row.setOnClickFillInIntent(android.R.id.text1, i);
return (row);
}
#Override
public int getViewTypeCount() {
return 1;
}
#Override
public boolean hasStableIds() {
return (true);
}
#Override
public void onCreate() {
}
#Override
public void onDataSetChanged() {
}
#Override
public void onDestroy() {
}
}
WidgetProvider:
public class WidgetTaskSchedular extends AppWidgetProvider {
static int ID;
static final int[] sameid=new int[1];
public static String EXTRA_WORD=
"com.capsone.testing.calendar.WORD";
#Override
public void onReceive(Context context, Intent intent) {
super.onReceive(context, intent);
if(intent.getAction().equals("update_widget"))
{
Log.i(TAG,"AppWidgetIds:"+ID);
for(int i=0;i<1;i++)
{
sameid[i]=ID;
Log.i(TAG,"SameId:"+sameid[i]);
onUpdate(context, AppWidgetManager.getInstance(context),sameid);
}
}
}
#SuppressWarnings("deprecation")
#Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager,
int[] appWidgetIds) {
super.onUpdate(context, appWidgetManager, appWidgetIds);
ID=appWidgetIds[i];
for (int i=0; i<appWidgetIds.length; i++) {
Log.i("Widget","WidgetId:"+appWidgetIds.length);
Intent intentWidgetService=new Intent(context, WidgetService.class);
intentWidgetService.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetIds[i]);
intentWidgetService.setData(Uri.parse(intentWidgetService.toUri(Intent.URI_INTENT_SCHEME)));
RemoteViews remoteView=new RemoteViews(context.getPackageName(),
R.layout.widgetlayout);
remoteView.setRemoteAdapter(appWidgetIds[i], R.id.listWidget,
intentWidgetService);
Intent clickIntent=new Intent(context, ActionBarActivity.class);
PendingIntent clickPendingIntent=PendingIntent
.getActivity(context, 0,
clickIntent,
PendingIntent.FLAG_UPDATE_CURRENT);
remoteView.setPendingIntentTemplate(R.id.listWidget, clickPendingIntent);
ComponentName component=new ComponentName(context,WidgetTaskSchedular.class);
appWidgetManager.updateAppWidget(component, remoteView);
}
}
}
AppWidgetProvider class:
ComponentName component = new ComponentName(context, WidgetTaskSchedular.class);
appWidgetManager.notifyAppWidgetViewDataChanged(appWidgetIds[i], R.id.listWidget);
appWidgetManager.updateAppWidget(component, remoteView);
RemoteView class:
#Override
public void onDataSetChanged() {
// Just copy and paste you getdata() coding here
}
One way to achieve this is when you save the new task in your application just send a broadcast with a custom intent to indicate the change in the underlying database.
Add a receiver to this broadcast in your WidgetTaskSchedular class and in on receive method call the onUpdate method to re-populate data in the widget. Somewhat like this:
public void onReceive(Context context, Intent intent) {
System.out.println("On receive function");
if (intent.getAction().equals("com.android.myapp.myBroadcast")) {
System.out.println("There is an update from app ");
//re populate data or in onUpdate
onUpdate(context, AppWidgetManager.getInstance(context), IDs);
}
super.onReceive(context, intent);
}
PS:Save Ids as a static field or something.I took in the IDs in a static array of integers which I populatedin the onUpdate method,you can also try replacing that part with the following code:
RemoteViews remoteViews = new RemoteViews(context.getPackageName(),
R.layout.widget);
// Update - here like for example as below
remoteViews.setTextViewText(R.id.yourTextID, "My updated text");
// Trigger widget layout update
AppWidgetManager.getInstance(context).updateAppWidget(
new ComponentName(context, Widget.class), remoteViews);

Categories

Resources