Remember about 2015 2016 something I often heard requests to make a “ball” like Facebook Messenger. Yes, that’s right, surely those who use Facebook Messenger on Android will be very familiar with a circular icon floating on the screen when receiving the message as shown below.
A lot of ideas have been raised on how to do this, of which the most common idea is to create a floating window on top of other apps through the SYSTEM_ALERT_WINDOW request. However, this is a discouraged idea, according to the Android documentation
Very few apps should use this permission; these windows are intended for system-level interaction with the user. This permission is inherently used at the system level to interact with the user. For a while, until recently, Google finally brought this cool feature to Android called Bubble. Starting with Android 10, developers have been able to experiment with this feature by enabling Settings ▸ Developer Options ▸ Bubbles. And on Android 11, it’s officially in use. Now let’s try to deploy. First of all, you can read more about Bubble here https://developer.android.com/guide/topics/ui/bubbles First you need to set targetSdkVersion to 30 (Android 11 ) in build.gradle Next, you need to build an activity so that when the user clicks on Bubble, it will expand.
1 2 3 4 5 6 7 8 | <span class="token tag"><span class="token tag"><span class="token punctuation"><</span> activity</span> <span class="token attr-name"><span class="token namespace">android:</span> name</span> <span class="token attr-value"><span class="token punctuation">=</span> <span class="token punctuation">"</span> .bubbles.BubbleActivity <span class="token punctuation">"</span></span> <span class="token attr-name"><span class="token namespace">android:</span> theme</span> <span class="token attr-value"><span class="token punctuation">=</span> <span class="token punctuation">"</span> @style/AppTheme.NoActionBar <span class="token punctuation">"</span></span> <span class="token attr-name"><span class="token namespace">android:</span> label</span> <span class="token attr-value"><span class="token punctuation">=</span> <span class="token punctuation">"</span> @string/title_activity_bubble <span class="token punctuation">"</span></span> <span class="token attr-name"><span class="token namespace">android:</span> allowEmbedded</span> <span class="token attr-value"><span class="token punctuation">=</span> <span class="token punctuation">"</span> true <span class="token punctuation">"</span></span> <span class="token attr-name"><span class="token namespace">android:</span> resizeableActivity</span> <span class="token attr-value"><span class="token punctuation">=</span> <span class="token punctuation">"</span> true <span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span> |
Note the last two lines, as in the documentation, The activity must be resizeable and embedded
Next step, please pay attention to the following line
Bubbles are built into the Notification system. They float on top of other app content and follow the user wherever they go. Bubbles can be expanded to reveal app functionality and information, and can be collapsed when not being used. Thus, Bubble is built with Notification system, which means that for Bubble to “jump”, a notification is required to fire. So in this step, we will build a notification, starting from creating a channel
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | <span class="token keyword">private</span> <span class="token keyword">void</span> <span class="token function">createNotificationChannel</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">// Create the NotificationChannel, but only on API 26+ because</span> <span class="token comment">// the NotificationChannel class is new and not in the support library</span> <span class="token class-name">CharSequence</span> name <span class="token operator">=</span> <span class="token string">"notification name"</span> <span class="token punctuation">;</span> <span class="token class-name">String</span> description <span class="token operator">=</span> <span class="token string">"notification description"</span> <span class="token punctuation">;</span> <span class="token comment">// The importance must be IMPORTANCE_HIGH to show Bubbles.</span> <span class="token keyword">int</span> importance <span class="token operator">=</span> <span class="token class-name">NotificationManager</span> <span class="token punctuation">.</span> IMPORTANCE_HIGH <span class="token punctuation">;</span> <span class="token class-name">NotificationChannel</span> channel <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">NotificationChannel</span> <span class="token punctuation">(</span> <span class="token string">"1000"</span> <span class="token punctuation">,</span> name <span class="token punctuation">,</span> importance <span class="token punctuation">)</span> <span class="token punctuation">;</span> channel <span class="token punctuation">.</span> <span class="token function">setDescription</span> <span class="token punctuation">(</span> description <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token comment">// Register the channel with the system; you can't change the importance</span> <span class="token comment">// or other notification behaviors after this</span> <span class="token class-name">NotificationManager</span> notificationManager <span class="token operator">=</span> <span class="token function">getContext</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">.</span> <span class="token function">getSystemService</span> <span class="token punctuation">(</span> <span class="token class-name">NotificationManager</span> <span class="token punctuation">.</span> <span class="token keyword">class</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> notificationManager <span class="token punctuation">.</span> <span class="token function">createNotificationChannel</span> <span class="token punctuation">(</span> channel <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token keyword">try</span> <span class="token punctuation">{</span> <span class="token function">updateShortcuts</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">catch</span> <span class="token punctuation">(</span> <span class="token class-name">IOException</span> e <span class="token punctuation">)</span> <span class="token punctuation">{</span> e <span class="token punctuation">.</span> <span class="token function">printStackTrace</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> |
However, not all notifications can show Bubble, but need to be the notification type conversation, you can read more here https://developer.android.com/guide/topics/ui/conversations#api-notifications Now We will build a shortcut for our notification (note, this is just a demo code so id, name, icon … are approximate and use hard-code, you should not use real code)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | <span class="token keyword">private</span> <span class="token keyword">void</span> <span class="token function">updateShortcuts</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token keyword">throws</span> <span class="token class-name">IOException</span> <span class="token punctuation">{</span> <span class="token comment">// Create a dynamic shortcut for each of the contacts.</span> <span class="token comment">// The same shortcut ID will be used when we show a bubble notification.</span> <span class="token class-name">HashSet</span> <span class="token generics"><span class="token punctuation"><</span> <span class="token class-name">String</span> <span class="token punctuation">></span></span> set <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">HashSet</span> <span class="token generics"><span class="token punctuation"><</span> <span class="token punctuation">></span></span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> set <span class="token punctuation">.</span> <span class="token function">add</span> <span class="token punctuation">(</span> <span class="token string">"com.example.android.bubbles.category.TEXT_SHARE_TARGET"</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token class-name">ShortcutInfo</span> shortcutInfo <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">ShortcutInfo</span> <span class="token punctuation">.</span> <span class="token class-name">Builder</span> <span class="token punctuation">(</span> <span class="token function">getContext</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">,</span> <span class="token string">"1000"</span> <span class="token punctuation">)</span> <span class="token punctuation">.</span> <span class="token function">setLocusId</span> <span class="token punctuation">(</span> <span class="token keyword">new</span> <span class="token class-name">LocusId</span> <span class="token punctuation">(</span> <span class="token string">"1000"</span> <span class="token punctuation">)</span> <span class="token punctuation">)</span> <span class="token punctuation">.</span> <span class="token function">setActivity</span> <span class="token punctuation">(</span> <span class="token keyword">new</span> <span class="token class-name">ComponentName</span> <span class="token punctuation">(</span> <span class="token function">getContext</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">,</span> <span class="token class-name">MainActivity</span> <span class="token punctuation">.</span> <span class="token keyword">class</span> <span class="token punctuation">)</span> <span class="token punctuation">)</span> <span class="token punctuation">.</span> <span class="token function">setShortLabel</span> <span class="token punctuation">(</span> <span class="token string">"contact name"</span> <span class="token punctuation">)</span> <span class="token punctuation">.</span> <span class="token function">setIcon</span> <span class="token punctuation">(</span> <span class="token class-name">Icon</span> <span class="token punctuation">.</span> <span class="token function">createWithAdaptiveBitmap</span> <span class="token punctuation">(</span> <span class="token class-name">BitmapFactory</span> <span class="token punctuation">.</span> <span class="token function">decodeStream</span> <span class="token punctuation">(</span> <span class="token function">getContext</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">.</span> <span class="token function">getResources</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">.</span> <span class="token function">getAssets</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">.</span> <span class="token keyword">open</span> <span class="token punctuation">(</span> <span class="token string">"cat.jpg"</span> <span class="token punctuation">)</span> <span class="token punctuation">)</span> <span class="token punctuation">)</span> <span class="token punctuation">)</span> <span class="token punctuation">.</span> <span class="token function">setLongLived</span> <span class="token punctuation">(</span> <span class="token boolean">true</span> <span class="token punctuation">)</span> <span class="token punctuation">.</span> <span class="token function">setCategories</span> <span class="token punctuation">(</span> set <span class="token punctuation">)</span> <span class="token punctuation">.</span> <span class="token function">setIntent</span> <span class="token punctuation">(</span> <span class="token keyword">new</span> <span class="token class-name">Intent</span> <span class="token punctuation">(</span> <span class="token function">getContext</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">,</span> <span class="token class-name">MainActivity</span> <span class="token punctuation">.</span> <span class="token keyword">class</span> <span class="token punctuation">)</span> <span class="token punctuation">.</span> <span class="token function">setAction</span> <span class="token punctuation">(</span> <span class="token class-name">Intent</span> <span class="token punctuation">.</span> ACTION_VIEW <span class="token punctuation">)</span> <span class="token punctuation">.</span> <span class="token function">setData</span> <span class="token punctuation">(</span> <span class="token class-name">Uri</span> <span class="token punctuation">.</span> <span class="token function">parse</span> <span class="token punctuation">(</span> <span class="token string">"https://android.example.com/chat/${contact.id}"</span> <span class="token punctuation">)</span> <span class="token punctuation">)</span> <span class="token punctuation">)</span> <span class="token punctuation">.</span> <span class="token function">setPerson</span> <span class="token punctuation">(</span> <span class="token keyword">new</span> <span class="token class-name">Person</span> <span class="token punctuation">.</span> <span class="token class-name">Builder</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">.</span> <span class="token function">setName</span> <span class="token punctuation">(</span> <span class="token string">"contact name"</span> <span class="token punctuation">)</span> <span class="token punctuation">.</span> <span class="token function">setIcon</span> <span class="token punctuation">(</span> <span class="token class-name">Icon</span> <span class="token punctuation">.</span> <span class="token function">createWithAdaptiveBitmap</span> <span class="token punctuation">(</span> <span class="token class-name">BitmapFactory</span> <span class="token punctuation">.</span> <span class="token function">decodeStream</span> <span class="token punctuation">(</span> <span class="token function">getContext</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">.</span> <span class="token function">getResources</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">.</span> <span class="token function">getAssets</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">.</span> <span class="token keyword">open</span> <span class="token punctuation">(</span> <span class="token string">"cat.jpg"</span> <span class="token punctuation">)</span> <span class="token punctuation">)</span> <span class="token punctuation">)</span> <span class="token punctuation">)</span> <span class="token punctuation">.</span> <span class="token function">build</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">)</span> <span class="token punctuation">.</span> <span class="token function">build</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token class-name">List</span> <span class="token generics"><span class="token punctuation"><</span> <span class="token class-name">ShortcutInfo</span> <span class="token punctuation">></span></span> shortcuts <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">ArrayList</span> <span class="token generics"><span class="token punctuation"><</span> <span class="token punctuation">></span></span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> shortcuts <span class="token punctuation">.</span> <span class="token function">add</span> <span class="token punctuation">(</span> shortcutInfo <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token punctuation">(</span> <span class="token punctuation">(</span> <span class="token class-name">ShortcutManager</span> <span class="token punctuation">)</span> <span class="token function">getContext</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">.</span> <span class="token function">getSystemService</span> <span class="token punctuation">(</span> <span class="token class-name">Context</span> <span class="token punctuation">.</span> SHORTCUT_SERVICE <span class="token punctuation">)</span> <span class="token punctuation">)</span> <span class="token punctuation">.</span> <span class="token function">addDynamicShortcuts</span> <span class="token punctuation">(</span> shortcuts <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token punctuation">}</span> |
Next, we create Bubble Intent and metadata to add to notification
1 2 3 4 5 6 7 8 9 10 11 12 | <span class="token comment">// Create bubble intent</span> <span class="token class-name">Intent</span> target <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Intent</span> <span class="token punctuation">(</span> <span class="token function">getContext</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">,</span> <span class="token class-name">BubbleActivity</span> <span class="token punctuation">.</span> <span class="token keyword">class</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token class-name">PendingIntent</span> bubbleIntent <span class="token operator">=</span> <span class="token class-name">PendingIntent</span> <span class="token punctuation">.</span> <span class="token function">getActivity</span> <span class="token punctuation">(</span> <span class="token function">getContext</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">,</span> <span class="token number">0</span> <span class="token punctuation">,</span> target <span class="token punctuation">,</span> <span class="token class-name">PendingIntent</span> <span class="token punctuation">.</span> FLAG_UPDATE_CURRENT <span class="token comment">/* flags */</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token comment">// Create bubble metadata</span> <span class="token class-name">Notification</span> <span class="token punctuation">.</span> <span class="token class-name">BubbleMetadata</span> bubbleData <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Notification</span> <span class="token punctuation">.</span> <span class="token class-name">BubbleMetadata</span> <span class="token punctuation">.</span> <span class="token class-name">Builder</span> <span class="token punctuation">(</span> bubbleIntent <span class="token punctuation">,</span> <span class="token class-name">Icon</span> <span class="token punctuation">.</span> <span class="token function">createWithAdaptiveBitmap</span> <span class="token punctuation">(</span> <span class="token class-name">BitmapFactory</span> <span class="token punctuation">.</span> <span class="token function">decodeStream</span> <span class="token punctuation">(</span> <span class="token function">getContext</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">.</span> <span class="token function">getResources</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">.</span> <span class="token function">getAssets</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">.</span> <span class="token keyword">open</span> <span class="token punctuation">(</span> <span class="token string">"cat.jpg"</span> <span class="token punctuation">)</span> <span class="token punctuation">)</span> <span class="token punctuation">)</span> <span class="token punctuation">)</span> <span class="token punctuation">.</span> <span class="token function">setDesiredHeight</span> <span class="token punctuation">(</span> <span class="token number">600</span> <span class="token punctuation">)</span> <span class="token punctuation">.</span> <span class="token function">build</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> |
Finally, create notification:
1 2 3 4 5 6 7 8 9 10 11 12 13 | <span class="token comment">// Create notification</span> <span class="token class-name">Person</span> chatPartner <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Person</span> <span class="token punctuation">.</span> <span class="token class-name">Builder</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">.</span> <span class="token function">setName</span> <span class="token punctuation">(</span> <span class="token string">"Chat partner"</span> <span class="token punctuation">)</span> <span class="token punctuation">.</span> <span class="token function">setImportant</span> <span class="token punctuation">(</span> <span class="token boolean">true</span> <span class="token punctuation">)</span> <span class="token punctuation">.</span> <span class="token function">build</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token class-name">Notification</span> <span class="token punctuation">.</span> <span class="token class-name">Builder</span> builder <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Notification</span> <span class="token punctuation">.</span> <span class="token class-name">Builder</span> <span class="token punctuation">(</span> mContext <span class="token punctuation">,</span> CHANNEL_ID <span class="token punctuation">)</span> <span class="token punctuation">.</span> <span class="token function">setContentIntent</span> <span class="token punctuation">(</span> contentIntent <span class="token punctuation">)</span> <span class="token punctuation">.</span> <span class="token function">setSmallIcon</span> <span class="token punctuation">(</span> smallIcon <span class="token punctuation">)</span> <span class="token punctuation">.</span> <span class="token function">setBubbleMetadata</span> <span class="token punctuation">(</span> bubbleData <span class="token punctuation">)</span> <span class="token punctuation">.</span> <span class="token function">addPerson</span> <span class="token punctuation">(</span> chatPartner <span class="token punctuation">)</span> <span class="token punctuation">;</span> |
At this point, you can try notifcation shot
1 2 3 4 5 | <span class="token class-name">NotificationManager</span> notificationManager <span class="token operator">=</span> <span class="token punctuation">(</span> <span class="token class-name">NotificationManager</span> <span class="token punctuation">)</span> <span class="token function">getContext</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">.</span> <span class="token function">getSystemService</span> <span class="token punctuation">(</span> <span class="token class-name">Context</span> <span class="token punctuation">.</span> NOTIFICATION_SERVICE <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token comment">// notificationId is a unique int for each notification that you must define</span> notificationManager <span class="token punctuation">.</span> <span class="token function">notify</span> <span class="token punctuation">(</span> <span class="token number">1000</span> <span class="token punctuation">,</span> builder <span class="token punctuation">.</span> <span class="token function">build</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> |
However, you will not see Bubble appear? Why? Did I follow the document correctly? That is because the code in the document does not satisfy the condition
but need to be the notification type conversation In the build notification section above, add a few lines of code to become as follows
1 2 3 4 5 6 7 8 9 10 11 12 | <span class="token class-name">Notification</span> <span class="token punctuation">.</span> <span class="token class-name">Builder</span> builder <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Notification</span> <span class="token punctuation">.</span> <span class="token class-name">Builder</span> <span class="token punctuation">(</span> mContext <span class="token punctuation">,</span> CHANNEL_ID <span class="token punctuation">)</span> <span class="token punctuation">.</span> <span class="token function">setContentIntent</span> <span class="token punctuation">(</span> contentIntent <span class="token punctuation">)</span> <span class="token punctuation">.</span> <span class="token function">setSmallIcon</span> <span class="token punctuation">(</span> smallIcon <span class="token punctuation">)</span> <span class="token punctuation">.</span> <span class="token function">setBubbleMetadata</span> <span class="token punctuation">(</span> bubbleData <span class="token punctuation">)</span> <span class="token punctuation">.</span> <span class="token function">addPerson</span> <span class="token punctuation">(</span> chatPartner <span class="token punctuation">)</span> <span class="token punctuation">.</span> <span class="token function">setCategory</span> <span class="token punctuation">(</span> <span class="token class-name">Notification</span> <span class="token punctuation">.</span> CATEGORY_MESSAGE <span class="token punctuation">)</span> <span class="token punctuation">.</span> <span class="token function">setShortcutId</span> <span class="token punctuation">(</span> <span class="token string">"1000"</span> <span class="token punctuation">)</span> <span class="token punctuation">.</span> <span class="token function">setLocusId</span> <span class="token punctuation">(</span> <span class="token keyword">new</span> <span class="token class-name">LocusId</span> <span class="token punctuation">(</span> <span class="token string">"1000"</span> <span class="token punctuation">)</span> https <span class="token operator">:</span> <span class="token operator">/</span> <span class="token operator">/</span> images <span class="token punctuation">.</span> viblo <span class="token punctuation">.</span> asia <span class="token operator">/</span> <span class="token number">64</span> x <span class="token operator">-</span> <span class="token operator">/</span> <span class="token number">9</span> cd6ae80 <span class="token operator">-</span> <span class="token number">2</span> ec5 <span class="token operator">-</span> <span class="token number">4382</span> <span class="token operator">-</span> <span class="token number">99</span> b4 <span class="token operator">-</span> <span class="token number">87e8</span> b7ca4b1e <span class="token punctuation">.</span> png <span class="token punctuation">)</span> <span class="token punctuation">.</span> <span class="token function">setStyle</span> <span class="token punctuation">(</span> <span class="token keyword">new</span> <span class="token class-name">Notification</span> <span class="token punctuation">.</span> <span class="token class-name">MessagingStyle</span> <span class="token punctuation">(</span> chatPartner <span class="token punctuation">)</span> <span class="token punctuation">.</span> <span class="token function">setGroupConversation</span> <span class="token punctuation">(</span> <span class="token boolean">false</span> <span class="token punctuation">)</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> |
That’s it, now let’s try the new feature compared to Facebook’s. Happy coding !!!