Android widget development
There are already enough articles on Habr on how to develop a "hello world" widget for Android devices. You can read more about this on the net, including on the Google site for developers , StackOverflow and other resources. It would seem that everything has been chewed in detail, there are hundreds of examples - why write another article when there is enough information around?
However, when we started developing the widget, we had to spend several weeks to figure out the nuances and implement the project in the way we originally conceived it.
We hope our experience will help save time on the implementation of your widget.
Android Stuido was chosen for development . The product is still very crude, not all developers are ready to switch to it, but the excellent work of Preview and the wide capabilities of the Gradle build system prevail over all the shortcomings. Therefore, we ventured to try, and, as it turned out, not in vain.
For testing, in addition to direct debugging on a test smartphone, we also used software emulators. Using the standard is rather problematic, various high-performance emulators Android-x86 , AndroVM , Genymotion , Manymo and others were considered. In the end, we chose Genymotion - it bribed with its ease of installation and speed of Android-x86, detailed instructions for setting up and installing- required for testing on devices with Android 4.0 and below.
These emulators work fine under various OSs: Linux, Mac, Windows, developers have different preferences, and it’s wrong to persuade them, so cross-platform tools help out.
Also, these programs help with automated testing: tests are written using the Android Instrumentation Framework, JUnit, Robotium. More on this in the next article, which we will publish in the near future :)
So, we want the user to see the search form, the button for voice queries, and with an increase in the available size of the widget, announcements of current news.

According to Google Play, about 4,500 types of various devices with Android support are registered in the world.

In addition to screen resolution, these devices can differ in diagonals and pixel density per unit area (ppi). Fortunately, the task can be simplified and use device-independent pixels - dp to determine the size of widget elements. Most smartphones use a 4x4 grid, for 7-inch tablets the grid can be 6x6, and even the size of the cell itself depends on the launcher and Android API versions. In the table we presented the resulting dimensions in dp for various devices:
You can start with the formulas:
for APIs younger than 14, size = (74 x number of cells) - 2
for the latest versions size = (70 x number of cells) - 30
If during testing you encounter problems on a specific device, for example, when changing orientation of the screen, it’s easier to add a separate layout or specify the desired size in dimensions.xml than try to adjust the parameters. Even at the design stage, pay attention to reusable elements, so that when developing, put them in separate layouts, and use Include to insert them into the desired location. In our application, this technology was used for news, and for implementing the shadow of some elements of home_news_row.xml:
home_news_item.xml:
We get the data for the widget from the server in JSON - everything is quite simple and described in detail in the documentation. If the widget is set to the minimum size (search bar and voice request icon), then we do not request up-to-date news announcements, and when the widget is enlarged, we first check to see if there are cached current news, then we take the available data, thereby saving traffic and battery.
After analyzing the current distribution of Android versions, we found out that version 2.2 is still relevant and needs to be supported. Unfortunately, support for resizing the widget is available only from version 3.0, so for older versions we will make a static version of the expanded widget. The share of devices of versions 3.x is currently insignificant, and we decided to respond to widget resizing starting from Android 4.1 using the onAppWidgetOptionsChanged method. But not everything is smooth with him: it does not work in some modified firmware. I had to look for an alternative solution, and it was found: we used the com.sec.android.widgetapp.APPWIDGET_RESIZE event in the onReceive () method:
When installing the widget on the home screen, the user can select color and transparency in the settings. There is nothing complicated in this implementation, but there is one caveat: the level of transparency must be added to the selected color. For example, this is how it is implemented with us:
The resulting color with the transparency level is applied to the widget element. In our case, we just set setBackgroundColor () on LinearLayout.
There are also situations when in landscape mode the cell size of the widget is smaller than in portrait mode, and therefore the text of a given length no longer fits. You can try to reduce the size of the text, but on devices with low resolution it becomes unreadable. In this regard, when changing the orientation, we simply reduce the number of displayed lines text.setMaxLines (2) in the landscape mode layout, and leave the font size unchanged:
The last property adds an ellipsis at the end of the line.
In order to make our widget easier to find in the list of installed ones, we need the final touch: preparing preview images, or previewImage. You can try to reproduce the final widget in a graphical editor, but we used the Widget Preview application .
Here is the result:

Download the final application here .
Thanks for attention!
However, when we started developing the widget, we had to spend several weeks to figure out the nuances and implement the project in the way we originally conceived it.
We hope our experience will help save time on the implementation of your widget.
Training
Android Stuido was chosen for development . The product is still very crude, not all developers are ready to switch to it, but the excellent work of Preview and the wide capabilities of the Gradle build system prevail over all the shortcomings. Therefore, we ventured to try, and, as it turned out, not in vain.
For testing, in addition to direct debugging on a test smartphone, we also used software emulators. Using the standard is rather problematic, various high-performance emulators Android-x86 , AndroVM , Genymotion , Manymo and others were considered. In the end, we chose Genymotion - it bribed with its ease of installation and speed of Android-x86, detailed instructions for setting up and installing- required for testing on devices with Android 4.0 and below.
These emulators work fine under various OSs: Linux, Mac, Windows, developers have different preferences, and it’s wrong to persuade them, so cross-platform tools help out.
Also, these programs help with automated testing: tests are written using the Android Instrumentation Framework, JUnit, Robotium. More on this in the next article, which we will publish in the near future :)
Design
So, we want the user to see the search form, the button for voice queries, and with an increase in the available size of the widget, announcements of current news.

According to Google Play, about 4,500 types of various devices with Android support are registered in the world.

In addition to screen resolution, these devices can differ in diagonals and pixel density per unit area (ppi). Fortunately, the task can be simplified and use device-independent pixels - dp to determine the size of widget elements. Most smartphones use a 4x4 grid, for 7-inch tablets the grid can be 6x6, and even the size of the cell itself depends on the launcher and Android API versions. In the table we presented the resulting dimensions in dp for various devices:
Samsung GT-i9000 | Nexus 4 | Samsung Tab | Nexus 7 | |
---|---|---|---|---|
1 x 1 | 64 x 58 | 64 x 58 | 74 x 74 | 80 x 71 |
2 x 2 | 144 x 132 | 152 x 132 | 148 x 148 | 176 x 159 |
4 x 3 | 304 x 206 | 328 x 206 | 296 x 222 | 368 x 247 |
You can start with the formulas:
for APIs younger than 14, size = (74 x number of cells) - 2
for the latest versions size = (70 x number of cells) - 30
If during testing you encounter problems on a specific device, for example, when changing orientation of the screen, it’s easier to add a separate layout or specify the desired size in dimensions.xml than try to adjust the parameters. Even at the design stage, pay attention to reusable elements, so that when developing, put them in separate layouts, and use Include to insert them into the desired location. In our application, this technology was used for news, and for implementing the shadow of some elements of home_news_row.xml:
home_news_item.xml:
Implementation
We get the data for the widget from the server in JSON - everything is quite simple and described in detail in the documentation. If the widget is set to the minimum size (search bar and voice request icon), then we do not request up-to-date news announcements, and when the widget is enlarged, we first check to see if there are cached current news, then we take the available data, thereby saving traffic and battery.
After analyzing the current distribution of Android versions, we found out that version 2.2 is still relevant and needs to be supported. Unfortunately, support for resizing the widget is available only from version 3.0, so for older versions we will make a static version of the expanded widget. The share of devices of versions 3.x is currently insignificant, and we decided to respond to widget resizing starting from Android 4.1 using the onAppWidgetOptionsChanged method. But not everything is smooth with him: it does not work in some modified firmware. I had to look for an alternative solution, and it was found: we used the com.sec.android.widgetapp.APPWIDGET_RESIZE event in the onReceive () method:
public void onReceive(Context context, Intent intent) {
if (intent.getAction().contentEquals("com.sec.android.widgetapp.APPWIDGET_RESIZE")) {
handleResize(context, intent);
}
super.onReceive(context, intent);
}
private void handleResize(Context context, Intent intent) {
AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context);
Bundle newOptions = getOptions(context, intent);
int appWidgetId = intent.getIntExtra("widgetId", 0);
if (!newOptions.isEmpty()) {
onAppWidgetOptionsChanged(context, appWidgetManager, appWidgetId, newOptions);
}
}
public Bundle getOptions(Context context, Intent intent) {
Bundle newOptions = new Bundle();
int appWidgetId = intent.getIntExtra("widgetId", 0);
int widgetSpanX = intent.getIntExtra("widgetspanx", 0);
int widgetSpanY = intent.getIntExtra("widgetspany", 0);
if(appWidgetId > 0 && widgetSpanX > 0 && widgetSpanY > 0) {
newOptions.putInt(AppWidgetManager.OPTION_APPWIDGET_MIN_HEIGHT, widgetSpanY * 74);
newOptions.putInt(AppWidgetManager.OPTION_APPWIDGET_MIN_WIDTH, widgetSpanX * 74);
}
return newOptions;
}
@Override
public void onAppWidgetOptionsChanged(Context context, AppWidgetManager appWidgetManager, int appWidgetId, Bundle newOptions) {
Log.d("height", newOptions.getInt(AppWidgetManager.OPTION_APPWIDGET_MIN_HEIGHT));
Log.d("width", newOptions.getInt(AppWidgetManager.OPTION_APPWIDGET_MIN_WIDTH));
}
When installing the widget on the home screen, the user can select color and transparency in the settings. There is nothing complicated in this implementation, but there is one caveat: the level of transparency must be added to the selected color. For example, this is how it is implemented with us:
int color = Color.parseColor(“#FFFFFF”);
int transparent =150;
color = Color.argb(transparent, Color.red(color), Color.green(color), Color.blue(color));
The resulting color with the transparency level is applied to the widget element. In our case, we just set setBackgroundColor () on LinearLayout.
There are also situations when in landscape mode the cell size of the widget is smaller than in portrait mode, and therefore the text of a given length no longer fits. You can try to reduce the size of the text, but on devices with low resolution it becomes unreadable. In this regard, when changing the orientation, we simply reduce the number of displayed lines text.setMaxLines (2) in the landscape mode layout, and leave the font size unchanged:
android:maxLines="3"
android:ellipsize="end"
The last property adds an ellipsis at the end of the line.
In order to make our widget easier to find in the list of installed ones, we need the final touch: preparing preview images, or previewImage. You can try to reproduce the final widget in a graphical editor, but we used the Widget Preview application .
Here is the result:

Download the final application here .
Thanks for attention!