Pit and summary in the development of Android widget

Time:2021-7-28

@

summary

Official reference

Build an App Widget

design sketch

Put a rendering. This is the desktop app folder I play
Pit and summary in the development of Android widget

AndroidManifest.xml

Receiver

Remember not to make mistakes in the letters inside. It’s best to copy and paste and then modify the corresponding custom places. One letter mistake made me vomit blood all day

Service

If list items such as listview and GridView are used in the widget, you need to use remoteviewsservice and provide a remoteviewsfactory instance to fill in the data instead of the adapter when binding data
Again, there must be no wrong letters, especially the permission, otherwise the data cannot be bound

Options

res/xml/

widget_desktop_options.xml

Common parameters

Size
  • The size is finally displayed as cell data, but is defined as DP

  • Cell number conversion basic formula size = 70 x cells – 30

  • For example: 1 grid = 70 x 1 – 30 = 40dp

  • When defining the minimum size, it is best not to exceed 4 cells, that is, 250dp

updatePeriodMillis

Update time in milliseconds, that is, how often the onupdate() method is called

initialLayout

Load layout file

Using App Widgets with Collections

List items used in widgets

Official reference

Google Develper

AppWidgetProvider

public class DesktopWidget extends AppWidgetProvider {

    public String ACTION_START_ACTIVITY = "STARTACTIVITYACTION";

    public DesktopWidget() {
        super();
    }

    //Called when a broadcast is received
    //Broadcast will be sent when onupdate, onenabled, etc
    @Override
    public void onReceive(Context context, Intent intent) {
        super.onReceive(context, intent);
        pub.log("onReceive:" + intent.getAction());
        if (intent.getAction().equals(ACTION_START_ACTIVITY)) {
            String strPackage = intent.getStringExtra("app_package");
            Intent appIntent = context.getPackageManager().getLaunchIntentForPackage(strPackage);
            pub.log(appIntent.getPackage());
            if (appIntent != null) {
                appIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                pub.log("start activity");
                context.startActivity(appIntent);
                pub.log("finish");
            }
        }
    }

    //Called when added or updated
    @Override
    public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
        super.onUpdate(context, appWidgetManager, appWidgetIds);

        // There may be multiple widgets active, so update all of them
        for (int appWidgetId : appWidgetIds) {
            updateAppWidget(context, appWidgetManager, appWidgetId);
        }
    }
    private void updateAppWidget(Context context, AppWidgetManager appWidgetManager, int appWidgetId) {
        pub.log("onUpdate:" + appWidgetId);
        // Construct the RemoteViews object
        RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.widget_desktop);
        //Set textview value on widget
        //views.setTextViewText(R.id.tv_widget_desktop_title, "ZGTools");
        //Setremoteadapter sets adapters with complex layouts such as listivew and GridView
        //The first parameter is the ID of the GridView of the widget to be bound, and the second parameter is the intent of the remoteviews service
        Intent intent = new Intent(context, DesktopViewsService.class);
        intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);
        intent.setData(Uri.parse(intent.toUri(Intent.URI_INTENT_SCHEME)));
        views.setRemoteAdapter(R.id.gv_app_group, intent);

        //Fixed point event of binding item
        //The activity mode test was not successful. Use broadcast mode to send and execute events
        //This cannot be an empty intent, otherwise the broadcast cannot be sent
        //Define the intent event template, which is equivalent to public settings, and then set the specific extra value in fillintent
        Intent templateIntent = new Intent(context, DesktopWidget.class);
        templateIntent.setAction(ACTION_START_ACTIVITY);
        templateIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);
        templateIntent.setData(Uri.parse(intent.toUri(Intent.URI_INTENT_SCHEME)));
        PendingIntent pendingIntent = PendingIntent.getBroadcast(context, appWidgetId, templateIntent, PendingIntent.FLAG_UPDATE_CURRENT);
        views.setPendingIntentTemplate(R.id.gv_app_group, pendingIntent);

        // Instruct the widget manager to update the widget
        appWidgetManager.updateAppWidget(appWidgetId, views);
    }


    //Called when the size is added or changed. In this method, some controls can be selectively displayed or hidden according to the interface size
    @Override
    public void onAppWidgetOptionsChanged(Context context, AppWidgetManager appWidgetManager, int appWidgetId, Bundle newOptions) {
        super.onAppWidgetOptionsChanged(context, appWidgetManager, appWidgetId, newOptions);
        updateAppWidget(context, appWidgetManager, appWidgetId);
    }
    @Override
    public void onEnabled(Context context) {
        super.onEnabled(context);
        // Enter relevant functionality for when the first widget is created
    }
    @Override
    public void onDisabled(Context context) {
        super.onDisabled(context);
        // Enter relevant functionality for when the last widget is disabled
    }
}

RemoteViewsService

public class DesktopViewsService extends RemoteViewsService {
    @Override
    public RemoteViewsFactory onGetViewFactory(Intent intent) {
        return new DesktopViewsFactory(this, intent);
    }
}

RemoteViewsFactory

The type is baseadapter. The key point is oncreate() to load data, and getviewat() method to return layout binding data

public class DesktopViewsFactory implements RemoteViewsService.RemoteViewsFactory {
    private Context mContext;
    private int mAppWidgetId;
    private List lstApps = new ArrayList();

    public DesktopViewsFactory(Context context, Intent intent){
        this.mContext = context;
        this.mAppWidgetId = intent.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, AppWidgetManager.INVALID_APPWIDGET_ID);
    }

    @Override
    public void onCreate() {
        DesktopDbHelper dbHelper = new DesktopDbHelper(mContext);
        SQLiteDatabase db = dbHelper.getReadableDatabase();
        String strSql = "Select * from tb_app";
        try {
            Cursor cursor = db.rawQuery(strSql, null);
            if (cursor != null && cursor.getCount() > 0) {
            	lstApps.clear();
                while (cursor.moveToNext()) {
                    byte[] blob = cursor.getBlob(cursor.getColumnIndex("app_icon"));
                    Bitmap bmpIcon = BitmapFactory.decodeByteArray(blob, 0, blob.length);
                    String strLabel = cursor.getString(cursor.getColumnIndex("app_label"));
                    String strPackage = cursor.getString(cursor.getColumnIndex("app_package"));
                    lstApps.add(new AppPackage(strLabel, strPackage, bmpIcon));
                }
            }
            if (cursor != null) {
                cursor.close();
            }
        } catch (Exception e) {
            db.close();
            e.printStackTrace();
        }
        db.close();
    }

    @Override
    public void onDataSetChanged() {
    	//Here I added a call to reload the data to update the list content of the widget
		onCreate();
	}
    @Override
    public void onDestroy() {}
    @Override
    public int getCount() {return 0;}

    @Override
    public RemoteViews getViewAt(int i) {
        if (i < 0 || i >= lstApps.size()) return null;
        //Construct a remote views item based on the app widget item XML file
        RemoteViews views = new RemoteViews(mContext.getPackageName(), R.layout.widget_desktop_item);
        views.setTextViewText(R.id.tv_widget_desktop_label, lstApps.get(i).getAppLabel());
        views.setImageViewBitmap(R.id.iv_widget_desktop_icon, lstApps.get(i).getAppIcon());

        Intent intent = new Intent();
        Bundle bundle = new Bundle();
        bundle.putString("app_package", lstApps.get(i).getAppPackage());
        intent.putExtras(bundle);
        views.setOnClickFillInIntent(R.id.iv_widget_desktop_icon, intent);
        views.setOnClickFillInIntent(R.id.tv_widget_desktop_label, intent);

        return views;
    }

    @Override
    public RemoteViews getLoadingView() { return null;}
    @Override
    public int getViewTypeCount() {return 0;}
    @Override
    public long getItemId(int i) { return 0;}
    @Override
    public boolean hasStableIds() { return false; }
}

DesktopActivity

//Update widget layout
    private void updateWidget(){
        AppWidgetManager widgetManager = AppWidgetManager.getInstance(this);
        ComponentName componentName = new ComponentName(this, DesktopWidget.class);
        int[] ids = widgetManager.getAppWidgetIds(componentName);
        widgetManager.notifyAppWidgetViewDataChanged(ids, R.id.gv_app_group);
    }