Paul
Paul

Reputation: 4430

Android Widget multiple instances of the same widget conflicts

I'm having the problem that when I create two instances of the same widget, they conflict.

When I refresh, so when I call

if (WIDGET_LIST_REFRESH.equals (intent.getAction ())) {

only last widget is updated.

The problem I think is also that WidgetConfigureActivity.loadTitlePref, change value in the first widget when I create the second instance of the widget.

ListWidgetProvider:

    import android.app.PendingIntent;
    import android.appwidget.AppWidgetManager;
    import android.appwidget.AppWidgetProvider;
    import android.content.Context;
    import android.content.Intent;
    import android.net.Uri;
    import android.util.Log;
    import android.widget.RemoteViews;
    import android.widget.Toast;

    import com.widgettwitter.AppIntent;
    import com.widgettwitter.R;

    /**
     * Implementation of App Widget functionality.
     * App Widget Configuration implemented in {@link WidgetConfigureActivity WidgetConfigureActivity}
     */
    public class ListWidgetProvider extends AppWidgetProvider {
        public static final int OPEN_CLICK_TYPE = 1;
        private static final String TAG = ListWidgetProvider.class.getName();
        private static final String WIDGET_APP_CONFIG = "com.widgettwitter.WIDGET_CONFIG";

        public static String WIDGET_TWITTER_NEW = "com.widgettwitter.WIDGET_NEW";
        public static String WIDGET_LIST_REFRESH = "com.widgettwitter.WIDGET_REFRESH";
        private static String WIDGET_LIST_CLEAR = "com.widgettwitter.WIDGET_CLEAR";
        static Context contextApp;
        static int appWidgetIdApp;

        @Override
        public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
            Log.v("Class:" + TAG, "onUpdate:" + appWidgetIds.length);
            for (int appWidgetId : appWidgetIds)
                updateAppWidget(context, appWidgetManager, appWidgetId);
            super.onUpdate(context, appWidgetManager, appWidgetIds);
        }

        static void updateAppWidget(Context context, AppWidgetManager appWidgetManager, int appWidgetId) {
            contextApp = context;
            appWidgetIdApp = appWidgetId;

            CharSequence widgetText = WidgetConfigureActivity.loadTitlePref(context, appWidgetId);
            Log.v("Class:" + TAG, "updateAppWidget:" + String.valueOf(widgetText));

            RemoteViews remoteView = new RemoteViews(context.getPackageName(), R.layout.widget);
            remoteView.setTextViewText(R.id.hashtag, widgetText);

            Intent intent = new Intent(context, ListWidgetService.class);
            intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);
            intent.setData(Uri.parse(intent.toUri(Intent.URI_INTENT_SCHEME)));//problem
            remoteView.setRemoteAdapter(R.id.widgetListViewTweets, intent);
            remoteView.setEmptyView(R.id.widgetListViewTweets, R.id.empty_view_tweets);

            final Intent newIntent = new Intent(context, ListWidgetProvider.class);
            newIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);
            newIntent.setAction(ListWidgetProvider.WIDGET_TWITTER_NEW);
            PendingIntent newPendingIntent = PendingIntent.getBroadcast(context, appWidgetId, newIntent, PendingIntent.FLAG_UPDATE_CURRENT);
            remoteView.setOnClickPendingIntent(R.id.add, newPendingIntent);

            final Intent configIntent = new Intent(context, ListWidgetProvider.class);
            configIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);
            configIntent.setAction(ListWidgetProvider.WIDGET_APP_CONFIG);
            PendingIntent configPendingIntent = PendingIntent.getBroadcast(context, appWidgetId, configIntent, PendingIntent.FLAG_UPDATE_CURRENT);
            remoteView.setOnClickPendingIntent(R.id.hashtag, configPendingIntent);

            final Intent refreshIntent = new Intent(context, ListWidgetProvider.class);
            refreshIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);
            refreshIntent.setAction(ListWidgetProvider.WIDGET_LIST_REFRESH);
            PendingIntent refreshPendingIntent = PendingIntent.getBroadcast(context, appWidgetId, refreshIntent, PendingIntent.FLAG_UPDATE_CURRENT);
            remoteView.setOnClickPendingIntent(R.id.refresh, refreshPendingIntent);

            final Intent clearIntent = new Intent(context, ListWidgetProvider.class);
            clearIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);
            clearIntent.setAction(ListWidgetProvider.WIDGET_LIST_CLEAR);
            PendingIntent clearPendingIntent = PendingIntent.getBroadcast(context, appWidgetId, clearIntent, PendingIntent.FLAG_UPDATE_CURRENT);
            remoteView.setOnClickPendingIntent(R.id.clear, clearPendingIntent);

            final Intent onClickIntent = new Intent(context, ListWidgetProvider.class);
            onClickIntent.setAction(AppIntent.ACTION_CLICK_LIST_WIDGET);
            onClickIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);
            onClickIntent.setData(Uri.parse(onClickIntent.toUri(Intent.URI_INTENT_SCHEME)));
            final PendingIntent onClickPendingIntent = PendingIntent.getBroadcast(context, appWidgetId, onClickIntent, PendingIntent.FLAG_UPDATE_CURRENT);
            remoteView.setPendingIntentTemplate(R.id.widgetListViewTweets, onClickPendingIntent);

            appWidgetManager.notifyAppWidgetViewDataChanged(appWidgetId, R.id.widgetListViewTweets);
            appWidgetManager.updateAppWidget(appWidgetId, remoteView);
        }

        @Override
        public void onDeleted(Context context, int[] appWidgetIds) {
            for (int appWidgetId : appWidgetIds)
                WidgetConfigureActivity.deleteTitlePref(context, appWidgetId);
        }

        @Override
        public void onEnabled(Context context) {
        }

        @Override
        public void onDisabled(Context context) {
        }

        @Override
        public void onReceive(Context context, Intent intent) {
            super.onReceive(context, intent);

            String widgetText = WidgetConfigureActivity.loadTitlePref(context, appWidgetIdApp);
            Log.v("Class:" + TAG, "onReceive:" + intent.getAction() + " widgetText:" + widgetText);

            if (WIDGET_LIST_REFRESH.equals(intent.getAction())) {
                Toast.makeText(contextApp, "Refresh:" + widgetText, Toast.LENGTH_SHORT).show();
                ListWidgetViewsFactory.onRefresh();
            } else if (WIDGET_APP_CONFIG.equals(intent.getAction())) {
                Intent appIntent = new Intent(context, WidgetConfigureActivity.class);
                appIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                context.startActivity(appIntent);
            } else if (WIDGET_LIST_CLEAR.equals(intent.getAction())) {
                ListWidgetViewsFactory.onClear();
            } else if (WIDGET_TWITTER_NEW.equals(intent.getAction())) {
                String text = widgetText.replaceAll("#", "");
                Intent appIntent = new Intent(Intent.ACTION_VIEW, Uri.parse("https://twitter.com/intent/tweet?text=%23" + text + " "));
                context.startActivity(appIntent);
            } else if (AppIntent.ACTION_CLICK_LIST_WIDGET.equals(intent.getAction())) {
                switch (intent.getIntExtra(AppIntent.EXTRA_CLICK_TYPE, -1)) {
                    case OPEN_CLICK_TYPE:
                        String id = intent.getStringExtra(AppIntent.EXTRA_ID);
                        String user = intent.getStringExtra(AppIntent.EXTRA_USERNAME);
                        user = user.replaceAll("@", "");

                        Log.v("Class:" + TAG, "ID:" + id + ",User:" + user);
                        Intent webIntent = new Intent(Intent.ACTION_VIEW, Uri.parse("https://twitter.com/" + user + "/status/" + id));
                        context.startActivity(webIntent);
                        break;
                }
            }
        }

    }

ListWidgetViewsFactory:

import android.appwidget.AppWidgetManager;
import android.content.Context;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.graphics.Rect;
import android.os.AsyncTask;
import android.util.Log;
import android.view.View;
import android.widget.RemoteViews;
import android.widget.RemoteViewsService;

import com.widgettwitter.AppIntent;
import com.widgettwitter.R;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.select.Elements;

import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;

class ListItem {
    public String id, ch, fullname, username, timestamp, text, photo;
}

public class ListWidgetViewsFactory implements RemoteViewsService.RemoteViewsFactory {
    private static final String TAG = ListWidgetViewsFactory.class.getName();

    private static Context contextApp = null;
    private static int appWidgetIdApp;
    private static AppWidgetManager appWidgetManager = null;
    private static RemoteViews remoteViewWidget = null;

    public static ArrayList<ListItem> listItemList = new ArrayList<ListItem>();
    public static String ultimeTwitter = "";

    private static String url;
    static String widgetText;
    static Boolean tag = false;

    public ListWidgetViewsFactory(Context context, Intent intent) {
        contextApp = context;
        appWidgetIdApp = intent.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, AppWidgetManager.INVALID_APPWIDGET_ID);
        widgetText = WidgetConfigureActivity.loadTitlePref(context, appWidgetIdApp);
        Log.v("Class:" + TAG, widgetText);

        String query = String.valueOf(widgetText);
        if (query.contains("#")) {
            widgetText = query.replaceAll("#", "");
            url = "https://twitter.com/search?f=tweets&q=%23" + widgetText + "&lang=it";
            tag = true;
        } else {
            url = "https://twitter.com/search?f=tweets&q=" + widgetText + "&lang=it";
            tag = false;
        }
        appWidgetManager = AppWidgetManager.getInstance(context);
        remoteViewWidget = new RemoteViews(context.getPackageName(), R.layout.widget);
    }

    @Override
    public void onCreate() {
        Log.v("Class:" + TAG, "onCreate:" + widgetText);
        remoteViewWidget.setTextViewText(R.id.hashtag, "Init");
        remoteViewWidget.setViewVisibility(R.id.progressBar, View.VISIBLE);
        appWidgetManager.updateAppWidget(appWidgetIdApp, remoteViewWidget);

        new Html().execute();
    }

    @Override
    public void onDataSetChanged() {
        Log.v("Class:" + TAG, "onDataSetChanged:");
    }

    public static void onClear(){
        listItemList.clear();
        ultimeTwitter = "";
        appWidgetManager.notifyAppWidgetViewDataChanged(appWidgetIdApp, R.id.widgetListViewTweets);
    }


    public static void onRefresh(){
        remoteViewWidget.setTextViewText(R.id.hashtag, "#Refresh");
        remoteViewWidget.setViewVisibility(R.id.progressBar, View.VISIBLE);
        appWidgetManager.updateAppWidget(appWidgetIdApp, remoteViewWidget);

        new Html().execute();
    }

    @Override
    public void onDestroy() {
        if (listItemList != null) listItemList.clear();
    }

    @Override
    public int getCount() {
        return listItemList.size();
    }

    @Override
    public int getViewTypeCount() {
        return 1;
    }

    @Override
    public boolean hasStableIds() {
        return true;
    }

    @Override
    public RemoteViews getLoadingView() {
        return null;
    }

    @Override
    public long getItemId(int position) {
        return position;
    }

    @Override
    public RemoteViews getViewAt(int position) {
        Log.v("Class:" + TAG, "getViewAt:" + position);
        final RemoteViews remoteView = new RemoteViews(contextApp.getPackageName(), R.layout.list_row);
        ListItem listItem = listItemList.get(position);
        URL url = null;
        try {
            url = new URL(listItem.ch);
        } catch (MalformedURLException e) {
            e.printStackTrace();
        }
        Bitmap bmp = null;
        try {
            bmp = BitmapFactory.decodeStream(url.openConnection().getInputStream());
        } catch (IOException e) {
            e.printStackTrace();
        }
        bmp = getCroppedBitmap(bmp);
        remoteView.setImageViewBitmap(R.id.ch, bmp);
        remoteView.setTextViewText(R.id.name, listItem.fullname);
        remoteView.setTextViewText(R.id.username, listItem.username);
        remoteView.setTextViewText(R.id.timestamp, listItem.timestamp);
        remoteView.setTextViewText(R.id.text, listItem.text);

        /*if (listItem.photo != "") {
            try {
                url = new URL(listItem.photo);
            } catch (MalformedURLException e) {
                e.printStackTrace();
            }
            bmp = null;
            try {
                bmp = BitmapFactory.decodeStream(url.openConnection().getInputStream());
            } catch (IOException e) {
                e.printStackTrace();
            }
            remoteView.setViewVisibility(R.id.photo, View.VISIBLE);
            remoteView.setImageViewBitmap(R.id.photo, bmp);
        }*/

        final Intent openIntent = new Intent();
        openIntent.putExtra(AppIntent.EXTRA_CLICK_TYPE, ListWidgetProvider.OPEN_CLICK_TYPE);
        openIntent.putExtra(AppIntent.EXTRA_ID, listItem.id);
        openIntent.putExtra(AppIntent.EXTRA_USERNAME, listItem.username);
        remoteView.setOnClickFillInIntent(R.id.widgetItemContainer, openIntent);

        return remoteView;
    }

    public Bitmap getCroppedBitmap(Bitmap bitmap) {
        Bitmap output = Bitmap.createBitmap(bitmap.getWidth(),
                bitmap.getHeight(), Bitmap.Config.ARGB_8888);
        Canvas canvas = new Canvas(output);

        final int color = 0xff424242;
        final Paint paint = new Paint();
        final Rect rect = new Rect(0, 0, bitmap.getWidth(), bitmap.getHeight());

        paint.setAntiAlias(true);
        canvas.drawARGB(0, 0, 0, 0);
        paint.setColor(color);
        // canvas.drawRoundRect(rectF, roundPx, roundPx, paint);
        canvas.drawCircle(bitmap.getWidth() / 2, bitmap.getHeight() / 2,
                bitmap.getWidth() / 2, paint);
        paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
        canvas.drawBitmap(bitmap, rect, rect, paint);
        //Bitmap _bmp = Bitmap.createScaledBitmap(output, 60, 60, false);
        //return _bmp;
        return output;
    }

    private static class Html extends AsyncTask<Void, Void, Void> {
        private JSONArray array = new JSONArray();

        @Override
        protected void onPreExecute() {
            super.onPreExecute();
        }

        @Override
        protected Void doInBackground(Void... params) {

            try {
                // Connect to the web site
                Document doc = Jsoup.connect(url).get();
                // Using Elements to get the Meta data
                Elements mElementDataSize = doc.select("div[class=content]");
                // Locate the content attribute
                int mElementSize = mElementDataSize.size();

                for (int i = 0; i < mElementSize; i++) {
                    JSONObject obj = new JSONObject();

                    Elements mElementId = doc.select("li[class=js-stream-item stream-item stream-item]").eq(i);
                    String mId = mElementId.attr("data-item-id");

                    Elements mElementAvatar = doc.select("img[class=avatar js-action-profile-avatar]").eq(i);
                    String mAvatar = mElementAvatar.attr("src");

                    Elements mElementFullName = doc.select("strong[class=fullname show-popup-with-id u-textTruncate]").eq(i);
                    String mFullName = mElementFullName.text();

                    Elements mElementUsername = doc.select("span[class=username u-dir u-textTruncate]").eq(i);
                    String mUsername = mElementUsername.text();

                    Elements mElementTimestamp = doc.select("span[class=_timestamp js-short-timestamp js-relative-timestamp]").eq(i);
                    String mTimestamp = mElementTimestamp.text();

                    Elements mElementText = doc.select("p[class=TweetTextSize  js-tweet-text tweet-text]").eq(i);
                    String mText = mElementText.text();

                    Elements mElementPhoto = doc.select("div[class=AdaptiveMedia-photoContainer js-adaptive-photo]").eq(i);
                    String mPhoto = mElementPhoto.attr("data-image-url");

                    try {
                        obj.put("id", mId);
                        obj.put("avatar", mAvatar);
                        obj.put("fullname", i + ") " + mFullName);
                        obj.put("username", mUsername);
                        obj.put("timestamp", mTimestamp);
                        obj.put("text", mText);
                        obj.put("photo", mPhoto);
                        array.put(obj);
                    } catch (JSONException e) {
                        e.printStackTrace();
                    }

                }
            } catch (IOException e) {
                e.printStackTrace();
            }
            return null;
        }

        @Override
        protected void onPostExecute(Void result) {
            populateListItem(array);
        }

        private void populateListItem(JSONArray array) {
            int id = array.length() - 1;
            if (!ultimeTwitter.equals("")) {
                for (int i = array.length() - 1; i >= 0; i--) {
                    try {
                        JSONObject o = array.getJSONObject(i);
                        if (o.getString("id").equals(ultimeTwitter)) {
                            id = i - 1;
                        }
                    } catch (JSONException e) {
                        e.printStackTrace();
                    }
                }
            }

            for (; id >= 0; id--) {
                ListItem listItem = new ListItem();
                try {
                    JSONObject o = array.getJSONObject(id);
                    Log.v("Class:" + TAG, "populateListItem" + String.valueOf(array.getJSONObject(id)));
                    ultimeTwitter = o.getString("id");
                    listItem.id = o.getString("id");
                    listItem.fullname = o.getString("fullname");
                    listItem.username = o.getString("username");
                    listItem.timestamp = "· " + o.getString("timestamp");
                    listItem.text = o.getString("text");
                    listItem.ch = o.getString("avatar");
                    listItem.photo = o.getString("photo");
                } catch (JSONException e) {
                    e.printStackTrace();
                }
                listItemList.add(listItem);
            }

            String str = tag ? "#" : "";
            remoteViewWidget.setTextViewText(R.id.hashtag, str + String.valueOf(widgetText)+" ("+listItemList.size()+")");
            remoteViewWidget.setViewVisibility(R.id.progressBar, View.INVISIBLE);
            appWidgetManager.updateAppWidget(appWidgetIdApp, remoteViewWidget);
            appWidgetManager.notifyAppWidgetViewDataChanged(appWidgetIdApp, R.id.widgetListViewTweets);
            Log.v("Class:" + TAG, "TweetProvider:onPostExecute");
        }
    }

}

Upvotes: 2

Views: 757

Answers (1)

HeyAlex
HeyAlex

Reputation: 1746

You're saving your app widget id in static variable, so after making two widgets quickly, in ListWidgetProvider static appWidgetId will have value of last widget, that user take a place on Launcher. The problem is that static variable will have a same value across all instances of class. So what do we have: after executing two asynctasks, it will update last widget, because asynctask of first widget still executing, and will update with widget app if of second widget. To make it works, make it non-static field and pass app widget id in ListWidgetViewsFactory through ListWidgetService. Yes, you need to remove all static methods that uses that static variable, you can make same things by ListWidgetService that will trigger ListWidgetViewsFactory with some action like REFRESH, CLEAR.

Also as i check your code, you can remove your asynctask, and update you widget by appWidgetManager.notifyAppWidgetViewDataChanged(appWidgetIds, R.id.widgetListViewTweets) and in onDataSetChanged() make you requests through in-net and populate your widget. The idea is that onDataSetChanged() is working on binder thread (non a main thread), so it will be safe to use it like that. Also check this callback map of , actually if you will log current thread in RemoteViewFactory callbacks, u will see that only onCreate() is on main thread

enter image description here

Upvotes: 1

Related Questions