I am getting an error when trying to host some widgets (in this case Gmail widget) on Android 12. I have a couple more widgets that show similar errors.
On the first glance it looks like the problem resides in widget's xml file, but everything works on previous Android versions. The Android 12 home launcher also loads widgets as expected.
Has widget hosting changed on Android 12? There are changes in widgets making, but I haven't found any changes in the hosting.
The error:
com.test.widgethosttest W/AppWidgetHostView: Error inflating RemoteViews
android.view.InflateException: Binary XML file line #19 in com.google.android.gm:layout/widget: Can't convert value at index 7 to dimension: type=0x1
Caused by: java.lang.UnsupportedOperationException: Can't convert value at index 7 to dimension: type=0x1
at android.content.res.TypedArray.getDimensionPixelSize(TypedArray.java:788)
at android.view.ViewGroup$MarginLayoutParams.<init>(ViewGroup.java:8465)
at android.widget.FrameLayout$LayoutParams.<init>(FrameLayout.java:452)
at android.appwidget.AppWidgetHostView.generateLayoutParams(AppWidgetHostView.java:463)
at android.appwidget.AppWidgetHostView.generateLayoutParams(AppWidgetHostView.java:68)
at android.view.LayoutInflater.inflate(LayoutInflater.java:673)
at android.view.LayoutInflater.inflate(LayoutInflater.java:538)
at android.widget.RemoteViews.inflateView(RemoteViews.java:5484)
at android.widget.RemoteViews.apply(RemoteViews.java:5453)
at android.appwidget.AppWidgetHostView.applyRemoteViews(AppWidgetHostView.java:572)
at android.appwidget.AppWidgetHostView.updateAppWidget(AppWidgetHostView.java:507)
at android.appwidget.AppWidgetHost.updateAppWidgetView(AppWidgetHost.java:485)
at android.appwidget.AppWidgetHost$UpdateHandler.handleMessage(AppWidgetHost.java:147)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loopOnce(Looper.java:201)
at android.os.Looper.loop(Looper.java:288)
at android.app.ActivityThread.main(ActivityThread.java:7842)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:548)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1003)
The Activity Code, found on github. It is pretty standard and works well on previous Android versions:
public class MainActivity extends AppCompatActivity {
AppWidgetManager mAppWidgetManager;
AppWidgetHost mAppWidgetHost;
ViewGroup mainlayout;
private static final int APPWIDGET_HOST_ID = 0x9345;
private static final int REQUEST_CREATE_APPWIDGET = 0x9344;
private static final int REQUEST_PICK_APPWIDGET = 0x9343;
/**
* Called on the creation of the activity.
*/
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mainlayout = (ViewGroup) findViewById(R.id.main_layout);
mAppWidgetManager = AppWidgetManager.getInstance(getApplicationContext());
mAppWidgetHost = new AppWidgetHost(getApplicationContext(), APPWIDGET_HOST_ID);
}
/**
* Launches the menu to select the widget. The selected widget will be on
* the result of the activity.
*/
void selectWidget() {
int appWidgetId = mAppWidgetHost.allocateAppWidgetId();
Intent pickIntent = new Intent(AppWidgetManager.ACTION_APPWIDGET_PICK);
pickIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);
addEmptyData(pickIntent);
startActivityForResult(pickIntent, REQUEST_PICK_APPWIDGET);
}
/**
* This avoids a bug in the com.android.settings.AppWidgetPickActivity,
* which is used to select widgets. This just adds empty extras to the
* intent, avoiding the bug.
*
* See more: http://code.google.com/p/android/issues/detail?id=4272
*/
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);
}
/**
* If the user has selected an widget, the result will be in the 'data' when
* this function is called.
*/
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, 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) {
mAppWidgetHost.deleteAppWidgetId(appWidgetId);
}
}
}
/**
* Checks if the widget needs any configuration. If it needs, launches the
* configuration activity.
*/
private void configureWidget(Intent data) {
Bundle extras = data.getExtras();
int appWidgetId = extras.getInt(AppWidgetManager.EXTRA_APPWIDGET_ID, -1);
AppWidgetProviderInfo appWidgetInfo = mAppWidgetManager.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);
}
}
/**
* Creates the widget and adds to our view layout.
*/
public void createWidget(Intent data) {
Bundle extras = data.getExtras();
int appWidgetId = extras.getInt(AppWidgetManager.EXTRA_APPWIDGET_ID, -1);
AppWidgetProviderInfo appWidgetInfo = mAppWidgetManager.getAppWidgetInfo(appWidgetId);
AppWidgetHostView hostView = mAppWidgetHost.createView(getApplicationContext(), appWidgetId, appWidgetInfo);
hostView.setAppWidget(appWidgetId, appWidgetInfo);
mainlayout.addView(hostView);
Log.i("MainActivity", "The widget size is: " + appWidgetInfo.minWidth + "*" + appWidgetInfo.minHeight);
}
/**
* Registers the AppWidgetHost to listen for updates to any widgets this app
* has.
*/
@Override
protected void onStart() {
super.onStart();
mAppWidgetHost.startListening();
}
/**
* Stop listen for updates for our widgets (saving battery).
*/
@Override
protected void onStop() {
super.onStop();
mAppWidgetHost.stopListening();
}
/**
* Removes the widget displayed by this AppWidgetHostView.
*/
public void removeWidget(AppWidgetHostView hostView) {
mAppWidgetHost.deleteAppWidgetId(hostView.getAppWidgetId());
mainlayout.removeView(hostView);
}
/**
* Handles the menu.
*/
@Override
public boolean onOptionsItemSelected(MenuItem item) {
Log.i("MainActivity", "Menu selected: " + item.getTitle() + " / " + item.getItemId() + " / " + R.id.addWidget);
switch (item.getItemId()) {
case R.id.addWidget:
selectWidget();
return true;
case R.id.removeWidget:
removeWidgetMenuSelected();
return false;
}
return super.onOptionsItemSelected(item);
}
/**
* Handle the 'Remove Widget' menu.
*/
public void removeWidgetMenuSelected() {
int childCount = mainlayout.getChildCount();
if (childCount > 1) {
View view = mainlayout.getChildAt(childCount - 1);
if (view instanceof AppWidgetHostView) {
removeWidget((AppWidgetHostView) view);
Toast.makeText(getApplicationContext(), R.string.widget_removed_popup, Toast.LENGTH_SHORT).show();
return;
}
}
Toast.makeText(getApplicationContext(), R.string.no_widgets_popup, Toast.LENGTH_SHORT).show();
}
/**
* Creates the menu with options to add and remove widgets.
*/
@Override
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.widget_menu, menu);
return true;
}
}