Question

I am using a ListView in a App Widget to show some items from a RSS feed. I want to go to a activity of my app if I click on a item in the ListView.

I am at the point that I can show a Toast with the clicked position. I want to go to. But I do not know how to start the activity.

I need to put my RSSFeed and the position inside the intent which starts the activity.

I have a WidgetProvider.java which calls a RemoteFetchService.java in the onUpdate() method. In the OnStartCommand() of this Service I start a AsyncTask and parse my RSS feed. In the OnPostExecute() I broadcast to the WidgetProvider.java that I successfully fetched the data. Then in the WidgetProvider.java I set a remote adapter via my ListProvider.java class that implents RemoteViewsFactory. So where and how can I start my activity?

WidgetProvider.java

public class WidgetProvider extends AppWidgetProvider {

    // String to be sent on Broadcast as soon as Data is Fetched
    // should be included on WidgetProvider manifest intent action
    // to be recognized by this WidgetProvider to receive broadcast
    public static final String DATA_FETCHED = "mypackage.RSS.DATA_FETCHED";
    public static final String EXTRA_LIST_VIEW_ROW_NUMBER = "mypackage.EXTRA_LIST_VIEW_ROW_NUMBER";

    /*
     * this method is called every 30 mins as specified on widgetinfo.xml this
     * method is also called on every phone reboot from this method nothing is
     * updated right now but instead RetmoteFetchService class is called this
     * service will fetch data,and send broadcast to WidgetProvider this
     * broadcast will be received by WidgetProvider onReceive which in turn
     * updates the widget
     */
    @Override
    public void onUpdate(Context ctxt, AppWidgetManager appWidgetManager,
            int[] appWidgetIds) {

        Log.d("Widget", "Hello WidgetProvider onUpdate");

        /*
         * int[] appWidgetIds holds ids of multiple instance of your widget
         * meaning you are placing more than one widgets on your homescreen
         */
        for (int i = 0; i < appWidgetIds.length; ++i) {

            Intent serviceIntent = new Intent(ctxt, RemoteFetchService.class);
            serviceIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID,
                    appWidgetIds[i]);
            ctxt.startService(serviceIntent);
        }
        super.onUpdate(ctxt, appWidgetManager, appWidgetIds);
    }

    /*
     * It receives the broadcast as per the action set on intent filters on
     * Manifest.xml once data is fetched from RemoteFetchService,it sends
     * broadcast and WidgetProvider notifies to change the data the data change
     * right now happens on ListProvider as it takes RemoteFetchService
     * listItemList as data
     */
    @SuppressWarnings("deprecation")
    @Override
    public void onReceive(Context context, Intent intent) {
        super.onReceive(context, intent);

        Log.d("Widget", "Hello WidgetProvider onReceive");

        //if RSS Feed was parsed in RemoteFetchService.java
        if (intent.getAction().equals(DATA_FETCHED)) {

            Log.d("Widget", "Data fetched in Widget Provider");

            int appWidgetId = intent.getIntExtra(
                    AppWidgetManager.EXTRA_APPWIDGET_ID,
                    AppWidgetManager.INVALID_APPWIDGET_ID);
            AppWidgetManager appWidgetManager = AppWidgetManager
                    .getInstance(context);

            // which layout to show on widget
            RemoteViews remoteViews = new RemoteViews(context.getPackageName(),
                    R.layout.widget_layout);

            // RemoteViews Service needed to provide adapter for ListView
            Intent svcIntent = new Intent(context, WidgetService.class);
            // passing app widget id to that RemoteViews Service
            svcIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, 
                    appWidgetId);
            // setting a unique Uri to the intent
            svcIntent.setData(Uri.parse(svcIntent
                    .toUri(Intent.URI_INTENT_SCHEME)));
            // setting adapter to listview of the widget
            remoteViews.setRemoteAdapter(appWidgetId, R.id.listViewWidget,
                    svcIntent);
            // setting an empty view in case of no data
            remoteViews.setEmptyView(R.id.listViewWidget, R.id.empty_view);

            // onclick item listview

            // This section makes it possible for items to have individualized
            // behavior.
            // It does this by setting up a pending intent template. Individuals
            // items of a collection
            // cannot set up their own pending intents. Instead, the collection
            // as a whole sets
            // up a pending intent template, and the individual items set a
            // fillInIntent
            // to create unique behavior on an item-by-item basis.
            Intent toastIntent = new Intent(context, WidgetProvider.class);
            // Set the action for the intent.
            // When the user touches a particular view, it will have the effect
            // of
            // broadcasting TOAST_ACTION.
            toastIntent.setAction(WidgetProvider.EXTRA_LIST_VIEW_ROW_NUMBER);
            toastIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID,
                    appWidgetId);
            intent.setData(Uri.parse(intent.toUri(Intent.URI_INTENT_SCHEME)));
            PendingIntent toastPendingIntent = PendingIntent.getBroadcast(
                    context, 0, toastIntent, PendingIntent.FLAG_UPDATE_CURRENT);
            remoteViews.setPendingIntentTemplate(R.id.listViewWidget,
                    toastPendingIntent);

            appWidgetManager.updateAppWidget(appWidgetId, remoteViews);
        }
        //if item on list was clicked
        if (intent.getAction().equals(EXTRA_LIST_VIEW_ROW_NUMBER)) {
            int appWidgetId = intent.getIntExtra(
                    AppWidgetManager.EXTRA_APPWIDGET_ID,
                    AppWidgetManager.INVALID_APPWIDGET_ID);

            AppWidgetManager appWidgetManager = AppWidgetManager
                    .getInstance(context);

            //get position on listview which was clicked
            int viewIndex = intent.getIntExtra(EXTRA_LIST_VIEW_ROW_NUMBER, 0);

            //get RSSFeed
            RSSFeed feed = ListItem.Feed;

            Toast.makeText(context, "Clicked on position :" + viewIndex,
                    Toast.LENGTH_SHORT).show();
        } else {
            Log.d("Widget", "No Data fetched in Widget Provider");
        }

    }
}

RemoteFetchService.java

public class RemoteFetchService extends Service {

    private int appWidgetId = AppWidgetManager.INVALID_APPWIDGET_ID;
    RSSFeed feed;
    public Date pDate;

    public static ArrayList<ListItem> listItemList;

    @Override
    public IBinder onBind(Intent arg0) {
        return null;
    }

    /*
     * Retrieve appwidget id from intent it is needed to update widget later
     * start Async Task
     */
    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {

        Log.d("Widget", "Hello RemoteFetchService onStartCommand");

        if (intent.hasExtra(AppWidgetManager.EXTRA_APPWIDGET_ID))
            appWidgetId = intent.getIntExtra(
                    AppWidgetManager.EXTRA_APPWIDGET_ID,
                    AppWidgetManager.INVALID_APPWIDGET_ID);

        //fetchDataFromWeb();
        new AsyncLoadXMLFeed().execute();

        return super.onStartCommand(intent, flags, startId);
    }

    /**
     * AsyncTask which parses the xml and post it
     **/
    public class AsyncLoadXMLFeed extends AsyncTask<Void, Void, RSSFeed> {

        protected RSSFeed doInBackground(Void... params) {
            try {

                Log.d("Widget", "Starte Parsing von URL in Widget");
                // Obtain feed
                DOMParser myParser = new DOMParser();
                feed = myParser.parseXml("http://www.test.de/feed");
                Log.d("Widget","ItemCount Parser in Widget: "+feed.getItemCount());

                return feed;

            } catch (Exception e) {
                Log.d("Widget","Exception beim Parsen: "+e.toString());
                return null;
            }
        }

        @Override
        protected void onPostExecute(RSSFeed parsed_feed) {
            // super.onPostExecute(result);
            Log.d("Widget", "Async Parse fertig");
            listItemList = new ArrayList<ListItem>();
            if(feed!=null){
                try {
                    int length = feed.getItemCount();
                    for (int i = 0; i < length; i++) {

                        String date = calc_date_difference(feed, i);

                        ListItem listItem = new ListItem();
                        listItem.Feed = feed;
                        listItem.heading = feed.getItem(i).getTitle();
                        listItem.pubDate = date;
                        listItem.linkUrl = feed.getItem(i).getLink();
                        Log.d("Widget Heading", feed.getItem(i).getTitle());
                        Log.d("Widget PubDate", date);
                        Log.d("Widget linkUrl", feed.getItem(i).getLink());
                        listItemList.add(listItem);
                    }

                } catch (Exception e) {
                    Log.d("Widget","Exception onPostExecute: "+e.toString());
                }
            }else{
                Log.d("Widget", "Feed in onPostExecute ist null");
            }

            //start intent and broadcast WidgetProvider, that data is fetched
            Intent widgetUpdateIntent = new Intent();
            widgetUpdateIntent.setAction(WidgetProvider.DATA_FETCHED);
            widgetUpdateIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID,
                    appWidgetId);
            sendBroadcast(widgetUpdateIntent);

            //stop service
            stopSelf();
        }
    }

    /**
     * Method to calculate the time difference
     **/
    public String calc_date_difference (RSSFeed feed, int pos){
        // calculate the time difference to the actual system time
        String pubDate = feed.getItem(pos).getDate();
        SimpleDateFormat df = new SimpleDateFormat(
                "EEE, dd MMM yyyy HH:mm:ss",Locale.ENGLISH);
        try {
            try {
                pDate = df.parse(pubDate);
            } catch (java.text.ParseException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            pubDate = "Vor "
                    + DateUtils.getDateDifference(pDate);
            return pubDate;
        } catch (ParseException e) {
            Log.e("DATE PARSING", "Error parsing date..");
            return null;
        }
    }

}

WidgetService.java

/**
 * 
 * Just consider this class as the class which tells the ListView of appwidget
 * to take what type of data. By data meaning what ListViewsFactory. To make it
 * more simple,if you have done ListView population,this class defines the
 * Adapter for the ListView. Let us name ListViewsService as WidgetService.java
 * 
 **/
public class WidgetService extends RemoteViewsService {
    /*
     * So pretty simple just defining the Adapter of the listview here Adapter
     * is ListViewsFactory
     */

    @Override
    public RemoteViewsFactory onGetViewFactory(Intent intent) {
        int appWidgetId = intent.getIntExtra(
        AppWidgetManager.EXTRA_APPWIDGET_ID,
        AppWidgetManager.INVALID_APPWIDGET_ID);
        return (new ListProvider(this.getApplicationContext(), intent));
    }
}

ListItem.java

public class ListItem {
    public String heading,pubDate,linkUrl;
    public static RSSFeed Feed;

}

ListProvider.java

/**
 * If you are familiar with Adapter of ListView,this is the same as adapter with
 * few changes
 * 
 */
public class ListProvider implements RemoteViewsFactory {

    private RemoteViews views;
    private Context ctxt = null;
    private int appWidgetId;
    private ArrayList<ListItem> listItemList = new ArrayList<ListItem>();

    public ListProvider(Context ctxt, Intent intent) {
        this.ctxt = ctxt;
        appWidgetId = intent.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID,
                AppWidgetManager.INVALID_APPWIDGET_ID);

        if (RemoteFetchService.listItemList != null)
            listItemList = (ArrayList<ListItem>) RemoteFetchService.listItemList
                    .clone();
        else
            listItemList = new ArrayList<ListItem>();
    }

    @Override
    public void onCreate() {
        // no-op
        Log.d("Widget", "Hello ListProvider onCreate");
    }

    @Override
    public void onDestroy() {
        // no-op
    }

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

    /*
     * Similar to getView of Adapter where instead of Viewwe return RemoteViews
     */
    @Override
    public RemoteViews getViewAt(int position) {

        final RemoteViews remoteView = new RemoteViews(ctxt.getPackageName(),
                R.layout.row);
        ListItem listItem = listItemList.get(position);
        remoteView.setTextViewText(R.id.heading, listItem.heading);
        remoteView.setTextViewText(R.id.pubDate, listItem.pubDate);

        //onclick item listview
        Intent fillInIntent = new Intent();
        fillInIntent.putExtra(WidgetProvider.EXTRA_LIST_VIEW_ROW_NUMBER, position);
        remoteView.setOnClickFillInIntent(R.id.heading, fillInIntent);

        return remoteView;
    }

    @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);
    }

    @Override
    public void onDataSetChanged() {
        // no-op
    }
}
Was it helpful?

Solution

If you can get the feed and position properly for

Toast.makeText(context, "Clicked on position :" + viewIndex, Toast.LENGTH_SHORT).show();

You should be able to add a line like

context.startActivity(INTENT HERE);

You may want to pass your RSS Feed as a String, or perhaps implement Parcelable.

OTHER TIPS

What you have to do is to set a PendingIntent to each list view item. So, when the user clicks the item, its pending intent will be broadcast and the activity will be started according to the extra data you filled the pending intent with. You dont even need the broadcast receiver you are using to display the toast. Just start the activity with the pending intent. To learn how to set a pending intent template to a remote listview and fill it for each list view item refer to the section "Adding behavior to individual items" in the following android doc:

https://developer.android.com/guide/topics/appwidgets/index.html

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top