📜 ⬆️ ⬇️

Widget with unread message counter

A frequently encountered task is to create a widget with a counter for unread messages / calls, etc. However, Android does not have a standard class for creating such widgets.

image

How to create such a widget?

')
On how to create widgets and how they work can be found in this and this article, so we will not dwell on the details of creating widgets as such.

The main task that we need to perform is to create a widget that is as close as possible to standard icons and widgets of standard applications.

Attempts to find a ready-made solution have come to nothing. Therefore, I had to take APK Manager in my hands (thanks to the article on reverse engineering ) and the Google Reader for Android application - I hoped that their widget looked standard. It turned out that this is not so:

image

A bit like, of course, but not quite what is needed. I had to finish:

<?xml version="1.0" encoding="UTF-8"?> <LinearLayout android:orientation="vertical" android:id="@+id/widget" android:background="@drawable/shortcut_selector" android:focusable="true" android:clickable="true" android:layout_width="74.0dip" android:layout_height="82.0dip" xmlns:android="http://schemas.android.com/apk/res/android"> <FrameLayout android:layout_width="fill_parent" android:layout_height="0.0dip" android:layout_weight="1.0" android:paddingTop="3dip"> <FrameLayout android:layout_gravity="center" android:layout_width="wrap_content" android:layout_height="wrap_content"> <ImageView android:id="@+id/icon" android:layout_width="wrap_content" android:layout_height="wrap_content" android:src="@drawable/icon" android:scaleType="center" /> <TextView android:textSize="14.0dip" android:textStyle="bold" android:textColor="#ffffffff" android:gravity="center" android:layout_gravity="bottom|right|center" android:id="@+id/widget_counter" android:background="@android:drawable/ic_notification_overlay" android:layout_width="24dip" android:layout_height="24dip" android:singleLine="true" android:shadowColor="#ff000000" android:shadowRadius="1.0"/> </FrameLayout> </FrameLayout> <TextView android:textSize="13.0dip" android:textColor="#ffffffff" android:ellipsize="marquee" android:gravity="center_horizontal" android:layout_gravity="center_horizontal" android:id="@android:id/text1" android:background="@drawable/appwidget_text_background" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="1dip" android:paddingBottom="1dip" android:singleLine="true" android:shadowColor="#ff000000" android:shadowRadius="2.0" android:layout_weight="0.0" android:text="@string/app_name" /> </LinearLayout> 


The height of the widget - 82dip - more than recommended . But otherwise, our widget will be less than the standard icon.

The above @ drawable / shortcut_selector is the xml file that describes how the widget will respond to clicks / selections:

 <?xml version="1.0" encoding="UTF-8"?> <selector xmlns:android="http://schemas.android.com/apk/res/android"> <item android:state_pressed="true" android:drawable="@drawable/pressed_application_background" /> <item android:state_focused="true" android:state_window_focused="true" android:drawable="@drawable/focused_application_background" /> <item android:state_focused="true" android:state_window_focused="false" android:drawable="@android:color/transparent" /> </selector> 

Here we are faced with the first problem - pressed_application_background and focused_application_background - the background image. But different phones have different backgrounds. For example, in standard Android - the background is orange, on HTC - green. The solution would be to access the standard Android’s resources — thanks to the Android R Drawables site, you can even find the id of these resources:
  1. pressed_application_background_static
  2. focused_application_background_static

But these resources are not public and cannot be used in your application. You can try to read the value of android.os.Build and determine the type of OS. But I decided that it was not so important for the widget, so I left an orange background (by the way, the Google Reader widget also did this).

The second problem is similar to the first. The background of the circle with the number of unread messages on the standard Android is red, on HTC it is green. But here you can refer to the standard resource - @android: drawable / ic_notification_overlay . This is not a circle, but it looks very similar (the image will be slightly lower).

The background text highlighting is taken from the Google Reader widget without changes ( appwidget_text_background.xml file):

 <?xml version="1.0" encoding="UTF-8"?> <shape android:shape="rectangle" xmlns:android="http://schemas.android.com/apk/res/android"> <solid android:color="#b2191919" /> <padding android:left="5.0dip" android:top="1.0dip" android:right="5.0dip" android:bottom="1.0dip" /> <corners android:radius="8.0dip" /> </shape> 


In my application (I think, as in other applications with such a counter) there is no need to periodically update the value of the counter. Therefore, updatePeriodMillis = “0” and its update occurs only in two cases:
  1. when an update occurs in the database;
  2. when the application becomes invisible ( onStop () ).

The following code is used to update the widget:

 AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(this); int[] a = appWidgetManager.getAppWidgetIds(new ComponentName(this.getPackageName(), "WidgetProvider")); List<AppWidgetProviderInfo> b = appWidgetManager.getInstalledProviders(); for (AppWidgetProviderInfo i : b) { if (i.provider.getPackageName().equals(this.getPackageName())) { a = appWidgetManager.getAppWidgetIds(i.provider); new WidgetProvider().onUpdate(this, appWidgetManager, a); } } 


If the number of unread messages is zero, then the circle with the number is hidden:
 if (unreadRecordsCount == 0) { views.setViewVisibility(R.id.widget_counter, View.INVISIBLE); views.setTextViewText(R.id.widget_counter, ""); } else { views.setTextViewText(R.id.widget_counter, Long.toString(unreadRecordsCount)); views.setViewVisibility(R.id.widget_counter, View.VISIBLE); } 


Here's what happened in the end:

image

It looks imperfect, but in my opinion it is very similar. The emulator also looks very similar to the original.

Source: https://habr.com/ru/post/117997/


All Articles