11

I am making a launcher for android, and I'm stuck at the widget part. I've been searching for about an hour and a half on the internet trying to figure out how can I host widgets in my application, but no luck.

I have went through some stock launchers and ADW launcher codes but both just have miles of code and this is the first time I'm making a launcher.

Could someone please guide me through how can I add widgets into my launcher? Or at least post any links/tutorials? Please explain it since this is my first time.

gung - Reinstate Monica
  • 11,583
  • 7
  • 60
  • 79
user1446632
  • 417
  • 2
  • 9
  • 24
  • 1
    Here is a good explanation along with a full working project: http://leonardofischer.com/hosting-android-widgets-my-appwidgethost-tutorial/ Source: http://leonardofischer.com/wp-content/uploads/2012/01/WidgetHostExample.zip Just need to apply this fix: http://selvaline.blogspot.it/2015/10/hosting-widgets-and-regurally-update.html – Pascal Jan 28 '16 at 15:31

2 Answers2

16

Try this:

final int APPWIDGET_HOST_ID = 2048;
final int REQUEST_PICK_APPWIDGET = 0;
final int REQUEST_CREATE_APPWIDGET = 5;

AppWidgetManager appWidgetManager;
AppWidgetHost appWidgetHost;

// Let user pick a widget from the list of intalled AppWidgets
public void selectWidget()
{
    int appWidgetId = this.appWidgetHost.allocateAppWidgetId();
    Intent pickIntent = new Intent(AppWidgetManager.ACTION_APPWIDGET_PICK);
    pickIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);
    addEmptyData(pickIntent);
    startActivityForResult(pickIntent, REQUEST_PICK_APPWIDGET);
}

// For some reason you have to add this empty data, else it won't work
public void addEmptyData(Intent pickIntent)
{
    ArrayList<AppWidgetProviderInfo> customInfo = 
        new ArrayList<AppWidgetProviderInfo>();
    pickIntent.putParcelableArrayListExtra(
        AppWidgetManager.EXTRA_CUSTOM_INFO, customInfo);
    ArrayList<Bundle> customExtras = new ArrayList<Bundle>();
    pickIntent.putParcelableArrayListExtra(
        AppWidgetManager.EXTRA_CUSTOM_EXTRAS, customExtras);
};

@Override
protected void onActivityResult(int requestCode, int resultCode, 
                                Intent data) {
    if (resultCode == RESULT_OK ) {
        if (requestCode == REQUEST_PICK_APPWIDGET) {
            configureWidget(data);
        }
        else if (requestCode == REQUEST_CREATE_APPWIDGET) {
            createWidget(data);
        }
    }
    else if (resultCode == RESULT_CANCELED && data != null) {
        int appWidgetId = 
            data.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, -1);
        if (appWidgetId != -1) {
            appWidgetHost.deleteAppWidgetId(appWidgetId);
        }
    }
}

// Show configuration activity of the widget picked by the user
private void configureWidget(Intent data) {
    Bundle extras = data.getExtras();
    int appWidgetId = extras.getInt(AppWidgetManager.EXTRA_APPWIDGET_ID, -1);
    AppWidgetProviderInfo appWidgetInfo = 
            appWidgetManager.getAppWidgetInfo(appWidgetId);
    if (appWidgetInfo.configure != null) {
        Intent intent = 
            new Intent(AppWidgetManager.ACTION_APPWIDGET_CONFIGURE);
        intent.setComponent(appWidgetInfo.configure);
        intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);
        startActivityForResult(intent, REQUEST_CREATE_APPWIDGET);
    } else {
        createWidget(data);
    }
}

// Get an instance of the selected widget as a AppWidgetHostView
public void createWidget(Intent data) {
    Bundle extras = data.getExtras();
    int appWidgetId = extras.getInt(AppWidgetManager.EXTRA_APPWIDGET_ID, -1);
    AppWidgetProviderInfo appWidgetInfo = appWidgetManager.getAppWidgetInfo(appWidgetId);

    AppWidgetHostView hostView = appWidgetHost.createView(this, appWidgetId, appWidgetInfo);
    hostView.setAppWidget(appWidgetId, appWidgetInfo);
    // Add  it on the layout you want
    myLayout.addView(hostView);
}

// Call this when you want to remove one from your layout
public void removeWidget(AppWidgetHostView hostView) {
    appWidgetHost.deleteAppWidgetId(hostView.getAppWidgetId());

    // Remove from your layout
    myLayout.removeView(hostView);
}

@Override
protected void onStart() {
    super.onStart();
    appWidgetManager = AppWidgetManager.getInstance(this);
    appWidgetHost = new AppWidgetHost(this, APPWIDGET_HOST_ID);

    // Start listening to pending intents from the widgets
    appWidgetHost.startListening();
}

@Override
protected void onStop() {
    super.onStop();
    appWidgetHost.stopListening();
}

All you've got to do is call the method selectWidget() whenever you want to show a list of AppWidgets installed on the device to pick from.

This code is a bit of a modified version of the stock Android launcher.

EDIT:

To bind widget IDs, i.e. make widgets update and respond to user interaction, refer the solution here: Widgets don't respond when re-added through code

Kamran Ahmed
  • 7,661
  • 4
  • 30
  • 55
  • This is a plug and play code. If you have any doubts, you can ask. – Kamran Ahmed Jul 14 '12 at 04:19
  • Run this in debug mode and try to find out the values you are getting in "resultCode" and "requestCode". Try changing the values of "REQUEST_PICK_APPWIDGET", "REQUEST_CREATE_APPWIDGET" and "RESULT_OK" accordingly. – Kamran Ahmed Jul 15 '12 at 04:40
  • @Kamran Thanks, works great. Any idea why it doesn't show all widgets shown when I use the launcher widget picker? – Lior Iluz Jun 08 '13 at 07:55
  • Shouldn't be so... I hope you're not referring to the widgets which appear only in a particular launcher like "Go SMS" and similar stuff, are you? – Kamran Ahmed Aug 02 '13 at 06:45
  • I'm almost embarassed posting this, but if anyone else has trouble figuring out how to get a handle on your Layout, you need to add an attribute 'android:id="@+id/mainLayout"' to the LinearLayout in the XML, at which point you can do LinearLayout myLayout = (LinearLayout)findViewById(R.id.mainLayout) in the code. – Chris Rae Dec 10 '14 at 19:56
  • I have done hosting widgets more or less like this, but i`m facing an issue: whenever my app pauses and resumes, widgets are not updating properly (for instance the power-control widget doesnt show the current brightness after resuming and changing the brightness again). Doing what the author of this answer posted in onStop and onStart is not sufficient. Can someone help? – treesoft Jun 04 '15 at 14:50
  • In case anyone is still wondering how to bind widget IDs without system permissions refer the answer here: https://stackoverflow.com/a/44351320/1468093 – Kamran Ahmed Jun 04 '17 at 06:30
0

I know I am a bit late but things have changed and the code provided by Leonardo Garcia Fischer doesn't work anymore.

Here is the updated version:

final int appWidgetHostId = 23456;
final int REQUEST_BIND_APPWIDGET = 12345;
AppWidgetHost widgetHost;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    widgetHost = new AppWidgetHost(getApplicationContext(), appWidgetHostId);
    widgetHost.startListening();
}

@Override
protected void onDestroy() {
    widgetHost.stopListening();
    super.onDestroy();
}

void configureWidget(int appWidgetId) {
    widgetHost.startAppWidgetConfigureActivityForResult(this, appWidgetId, 0, 0, null);
}

AppWidgetProviderInfo getWidgetProviderInfo(String packageName, int widgetPosition) {
    AppWidgetManager manager = AppWidgetManager.getInstance(this);
    List<AppWidgetProviderInfo> infos = manager.getInstalledProvidersForPackage(packageName, Process.myUserHandle());
    return infos.get(widgetPosition);
}

AppWidgetHostView getWidgetHostView(AppWidgetProviderInfo info) {
    int appWidgetId = widgetHost.allocateAppWidgetId();
    AppWidgetManager manager = AppWidgetManager.getInstance(getApplicationContext());

    boolean canBindWidgets = manager.bindAppWidgetIdIfAllowed(appWidgetId, info.provider);
    if(!canBindWidgets) {
        Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_BIND);
        intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);
        intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_PROVIDER, info.provider);
        startActivityForResult(intent, REQUEST_BIND_APPWIDGET);
    }

    AppWidgetHostView widgetHostView = widgetHost.createView(getApplicationContext(), appWidgetId, info);
    
    if(info.configure != null) {
        configureWidget(appWidgetId);
    }

    return widgetHostView;
}

Now, if you want to create an app widget, first you call getWidgetProviderInfo() by specifying which package you want to get your app widgets from and which widget you want (because there may be more than one). Then, you pass the result to getWidgetHostView which will create the widget and launch the configure activity if there is one. Then, you can use the AppWidgetHostView it returns and put it in a linear layout for example. Here is a more complete example:

void createMyWidget() {
    String packageName = com.xxx.xxx;
    AppWidgetProviderInfo widgetInfo = getWidgetProviderInfo(packageName, 0);
    AppWidgetHostView widgetHostView = getWidgetHostView(widgetInfo);
    //Add the widgetHostView to your container.
}
    

Finally, if you want to reconfigure your app widget, you can do so by calling configureWidget and passing its app widget id as a parameter. It will relauch the configure activity (if there is one; that's why you should verify if it exists first by looking at the widget's provider info, just like in the getWidgetHostView function).