Appwidget does not update correctly

160 Views Asked by At

I developed an appwidget, and it's a collection widget and use a Loader to retrieve the data from a database, and when a user clicks on the list item, that items details will be saved into a bundle in an intent, and the main activity will be launched with the bundled info displayed to the user.

My code works as intended; however, I am concerned with possible edge cases in how widgets actually work, initialize, and update that I have not thought of. I have discovered one edge case after thinking my code was working correctly for around 5 days now. Again, I am new to widget development, and the API for widgets took a lot for me to wrap my head around.

Issue

Everything with my appwidget seems to work correctly on my phone, but on my Tablet it works correctly AFTER opening up the app and adding a new widget to the home screen. If I don't open the app first, then nothing is displayed in my widget.

Also, if my tablet is restarted or powered on-off, then my current widgets won't display anything. Even after opening up my app, I specifically have to add a new widget for my other widgets to get updated.

For my phone, I will rarely have issues with nothing being displayed on the widget unless I add another widget. At least I can't reproduce anything and guarantee that it will not work like on the tablet.

So how can this work almost completely fine on my phone, but have issues all the time on my tablet? And is there a way to make sure my data is available whether I open on the app or not, and whether or not I restart my device?

Code

GitHub Link. Links directly to my Hub Flavor that implements the appwidget (java and res folders, and manifest file)

App Widget Provider

public class ResumeHubWidgetProvider extends AppWidgetProvider {

    private static final String LAUNCH_RESUME_ACTION = "io.github.ciscorucinski.personal.intro.hub.LAUNCH_RESUME_ACTION";

    private static Intent createIntent(Context context) {
        return new Intent(context, ResumeHubWidgetProvider.class);
    }

    private static void updateAppWidget(Context context, AppWidgetManager appWidgetManager,
                                        int appWidgetId) {

        Intent serviceIntent = MyWidgetService.createIntent(context);
        serviceIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);
        serviceIntent.setData(Uri.parse(serviceIntent.toUri(Intent.URI_INTENT_SCHEME)));

        // Construct the RemoteViews object
        RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.resume_hub_list_widget);
        views.setRemoteAdapter(R.id.widget_list, serviceIntent);

        Intent widgetProviderIntent = ResumeHubWidgetProvider.createIntent(context);
        widgetProviderIntent.setAction(ResumeHubWidgetProvider.LAUNCH_RESUME_ACTION);
        widgetProviderIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);
        serviceIntent.setData(Uri.parse(serviceIntent.toUri(Intent.URI_INTENT_SCHEME)));

        PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0, widgetProviderIntent,
                PendingIntent.FLAG_UPDATE_CURRENT);

        views.setPendingIntentTemplate(R.id.widget_list, pendingIntent);

        // 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 similar widgets active, so update all of them
        for (int appWidgetId : appWidgetIds) {
            updateAppWidget(context, appWidgetManager, appWidgetId);
        }

    }

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

        AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context);

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

            Timber.i("Intent Action is LAUNCH_RESUME_ACTION");

            // Open the Resume activity with the user selected resume info
            Bundle bundle = intent.getBundleExtra(CREATE_INTENT_BUNDLE);
            context.startActivity(ResumeActivity
                    .createIntentWithFlags(context, bundle,
                            Intent.FLAG_ACTIVITY_NEW_TASK));
        }

        int appWidgetIds[] = appWidgetManager.getAppWidgetIds(new ComponentName(context, ResumeHubWidgetProvider.class));
        appWidgetManager.notifyAppWidgetViewDataChanged(appWidgetIds, R.id.widget_list);

        super.onReceive(context, intent);

    }
}

Adapter

class HubWidgetAdapter implements RemoteViewsService.RemoteViewsFactory,
        Loader.OnLoadCompleteListener<List<Resume.People>> {

    private Context context;
    private List<Resume.People> data;
    private int appWidgetId;
    private PeopleLoader loader;

    HubWidgetAdapter(Context context, Intent intent) {
        this.context = context;
        appWidgetId = intent.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID,
                AppWidgetManager.INVALID_APPWIDGET_ID);
    }

    @Override
    public RemoteViews getViewAt(int position) {

        RemoteViews view = new RemoteViews(context.getPackageName(),
                android.R.layout.simple_list_item_1);

        Resume.People person = data.get(position);

        view.setTextViewText(android.R.id.text1, person.seeking_position());
        view.setTextColor(android.R.id.text1, Color.BLACK);

        Bundle bundle = new Bundle();

        bundle.putLong(ResumeActivity.ID, person._id());
        bundle.putString(ResumeActivity.NAME, person.name());
        bundle.putString(ResumeActivity.EMAIL, person.email());
        bundle.putString(ResumeActivity.PHONE, person.phone());
        bundle.putString(ResumeActivity.GITHUB, person.github());
        bundle.putString(ResumeActivity.LINKEDIN, person.linkedin());
        bundle.putString(ResumeActivity.SEEKING, person.seeking_position());

        Intent intent = new Intent();
        intent.putExtra(CREATE_INTENT_BUNDLE, bundle);

        Timber.i("Bundled Person%s", intent);
        view.setOnClickFillInIntent(android.R.id.text1, intent);

        return view;

    }

    @Override
    public void onLoadComplete(Loader<List<Resume.People>> loader, List<Resume.People> data) {
        this.data = data;
    }

    @Override
    public void onCreate() {
        data = new ArrayList<>();
    }

    @Override
    public void onDestroy() {

        if (loader != null) {

            loader.unregisterListener(this);
            loader.cancelLoad();
            loader.stopLoading();
            loader = null;

        }

        data = null;

    }

    @Override public void onDataSetChanged() {

        loader = new PeopleLoader(context);
        loader.registerListener(0, this);
        loader.startLoading();

    }

    @Override public int getCount() { return data.size(); }
    @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; }

}

Service

public class MyWidgetService extends RemoteViewsService {

    static Intent createIntent(Context context) {
        return new Intent(context, MyWidgetService.class);
    }

    @Override
    public RemoteViewsFactory onGetViewFactory(Intent intent) {
        return new HubWidgetAdapter(this.getApplicationContext(), intent);
    }

}

Manifest (Hub Flavor)

The Hub flavor is the only aspect of my code that has appwidgets, so I am only including the Hub Manifest file here that is merged into the main Manifest file.

<manifest xmlns:android="http://schemas.android.com/apk/res/android">
    <application>
        <activity
            android:name="io.github.ciscorucinski.personal.intro.ui.ResumeActivity"
            android:label="@string/app_name"
            android:theme="@style/AppTheme.NoActionBar"/>
        <receiver android:name="io.github.ciscorucinski.personal.intro.hub.ResumeHubWidgetProvider">
            <intent-filter>
                <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
            </intent-filter>
            <meta-data
                android:name="android.appwidget.provider"
                android:resource="@xml/resume_hub_list_widget_info" />
        </receiver>
        <service
            android:name="io.github.ciscorucinski.personal.intro.hub.MyWidgetService"
            android:exported="false"
            android:permission="android.permission.BIND_REMOTEVIEWS" />
    </application>
</manifest>
0

There are 0 best solutions below