9

My initial goal is to add the Google Search widget to the linear layout of the activity. I need to include it just like it appears and works in the Launcher (this is why I need to be able to add the widget).


I would like to add widgets to my activity whithout having to launch the widget picker activity. I tried to:

1. directly specify an integer id (I always get inflate errors)

2. get the id like this:

  ComponentName cn = new ComponentName(getBaseContext(), "com.android.quicksearchbox.SearchWidgetProvider");
  int[] ids = AppWidgetManager.getInstance(getApplicationContext()).getAppWidgetIds (cn);

(the array is always empty)

None of these works.

After this I have this code, to use the ID (it works if I get the ID from the widget picker activity):

    AppWidgetProviderInfo withWidgetInfo = AppWidgetManager.getInstance(getApplicationContext()).getAppWidgetInfo(appWidgetId);
    AppWidgetHostView hostView = myWidgetHost.createView(getBaseContext(), appWidgetId, withWidgetInfo);
    hostView.setAppWidget(appWidgetId, withWidgetInfo);
    LinearLayout ll = (LinearLayout) findViewById(R.id.ll);
    ll.addView(hostView);

How should I do it? Thank you!

stormofwar
  • 551
  • 5
  • 9

2 Answers2

19

Try this:

// APPWIDGET_HOST_ID is any number you like
appWidgetManager = AppWidgetManager.getInstance(this);
appWidgetHost = new AppWidgetHost(this, APPWIDGET_HOST_ID);
AppWidgetProviderInfo newAppWidgetProviderInfo = new AppWidgetProviderInfo();

// Get an id
int appWidgetId = appWidgetHost.allocateAppWidgetId();

// Get the list of installed widgets
List<AppWidgetProviderInfo> appWidgetInfos = new ArrayList<AppWidgetProviderInfo>();
appWidgetInfos = appWidgetManager.getInstalledProviders();

for(int j = 0; j < appWidgetInfos.size(); j++)
{
    if (appWidgetInfos.get(j).provider.getPackageName().equals("com.android.quicksearchbox") && appWidgetInfos.get(j).provider.getClassName().equals("com.android.quicksearchbox.SearchWidgetProvider"))
    {
        // Get the full info of the required widget
        newAppWidgetProviderInfo = appWidgetInfos.get(j);
        break;
    }
 }

// Create Widget
AppWidgetHostView hostView = appWidgetHost.createView(this, appWidgetId, newAppWidgetProviderInfo);
hostView.setAppWidget(appWidgetId, newAppWidgetProviderInfo);

// Add it to your layout
LinearLayout ll = (LinearLayout) findViewById(R.id.ll);
ll.addView(hostView);

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
  • 1
    Above code just show the widget, but it can't responed to user's click without appWidgetManager.bindAppWidgetId(); – herbertD Jan 17 '13 at 09:16
  • But `appWidgetManager.bindAppWidgetId();` can be executed by a system app only, right? Even I'm looking for a work around for that, can you help? – Kamran Ahmed Aug 02 '13 at 06:48
  • you give LinearLayout and say "Add it to your layout" but i want to add it on home screen of mobile so how can i do it – Bhanu Sharma Jul 07 '15 at 13:27
  • The 'LinearLayout ll' is the layout on your "home screen of mobile" that you want to add the widget in. – Kamran Ahmed Jul 07 '15 at 13:37
  • 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:29
  • 1
    @KamranAhmed if I do it this way (I'm trying to add calendar to my screen) I can only see the container of that widget, without any data inside it. Also it's not clickable, actually it won't open the app. Any ideas why? Thank you – joe Jul 31 '17 at 14:43
  • @KamranAhmed i created digital clock that is updating using Alarm Manager and it is working well if i added it to Home Screen App........ But when i added this digital clock to my host as you told .. my clock added but after adding to host , clock is not updating by AlarmManager... – Imran Khan Saifi Dec 08 '17 at 12:19
  • @ImranKhanSaifi Did you try the solution I mentioned in the edit? That should fix it. – Kamran Ahmed Dec 08 '17 at 16:00
  • @KamranAhmed, Yes i tried but it is not updating. i found that widgetManager.bindAppWidgetIdIfAllowed(widgetId, mWidgetInfo, opts); method giving false... may be thats why it is not updating. – Imran Khan Saifi Dec 09 '17 at 05:22
  • @KamranAhmed, i also tried another widget with button action and it is working well(button action is working) after adding this widget to my host using your solution mentioned in the edit even in this case also bindAppWidgetIdIfAllowed method return false but still button action is working.. But when add my Clock widget i, clock not updating by AlarmManager even i used your solution – Imran Khan Saifi Dec 09 '17 at 05:32
6

Well, I managed to make the code that works (except it should and cannot work that easily - details are provide bellow).

The code that should work is:

        //create ComponentName for accesing the widget provider
        ComponentName cn = new ComponentName("com.android.quicksearchbox", "com.android.quicksearchbox.SearchWidgetProvider");
        //ComponentName cn = new ComponentName("com.android.music", "com.android.music.MediaAppWidgetProvider");

        //get appWidgetManager instance
        AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(getBaseContext());
        //get list of the providers - .getAppWidgetIds (cn) does seem to be unrelated to widget hosting and more related to widget development
        final List<AppWidgetProviderInfo> infos = appWidgetManager.getInstalledProviders();

        //get AppWidgetProviderInfo
        AppWidgetProviderInfo appWidgetInfo = null;
        //just in case you want to see all package and class names of installed widget providers, this code is useful
        for (final AppWidgetProviderInfo info : infos) {
            Log.v("AD3", info.provider.getPackageName() + " / "
                    + info.provider.getClassName());
        }
        //iterate through all infos, trying to find the desired one
        for (final AppWidgetProviderInfo info : infos) {
            if (info.provider.getClassName().equals(cn.getClassName()) && info.provider.getPackageName().equals(cn.getPackageName())) {
                //we found it
                appWidgetInfo = info;
                break;
            }
        }
        if (appWidgetInfo == null)
            return; //stop here

        //allocate the hosted widget id
        int appWidgetId = myWidgetHost.allocateAppWidgetId();

        //bind the id and the componentname - here's the problem!!!
        appWidgetManager.bindAppWidgetId(appWidgetId, cn); 

        //creat the host view
        AppWidgetHostView hostView = myWidgetHost.createView(getBaseContext(), appWidgetId, appWidgetInfo);
        //set the desired widget
        hostView.setAppWidget(appWidgetId, appWidgetInfo);

        //add the new host view to your activity's GUI
        LinearLayout ll = (LinearLayout) findViewById(R.id.ll);
        ll.addView(hostView);

Well, the problem is "appWidgetManager.bindAppWidgetId(appWidgetId, cn); " - this requires the permission BIND_APPWIDGET - in the manifest. - even if we put, it still won't work. I read it may be reserved for system applications

However, even after adding the permission and putting the apk in the system/app folder, it still does not work (it does not make it a system app).

Regarding this problem I found this answer here:

This is deliberately not available to applications. If you want to add a widget, you need to launch the selector UI for the user to pick the widget which will then take care of the bind. Widgets can expose a lot of private data of all types, so it is not safe to allow an application to arbitrarily bind to them without the user implicitly approving (through the selection UI).

Dianne ... Android framework engineer

However, I am uncertain but, maybe this means what we could achieve the desired functionality if we could sign the apk with the image developer's key? However, this is off the question's topic. But if you happen to be able to explain to me how to efficiently host AppWidgets, please do and have your answer accepted by me (launcher applications in the market can do it, so it must be possible).

stormofwar
  • 551
  • 5
  • 9