12

I'm trying to develop an app for Android Nougat and I want to show some info/text in the status bar generating from a android service routine. So my problem is, I don't know how to show the text in the status bar.

I added a sample image to show what exactly do I mean (red circle). I know it is possible, because I saw it in a battery monitor app from play store.

Sample

I already tried NotificationCombat.Builder, but I think this is not the right way. Maybe an overlay is, but after searching I didn't find something.

Can someone show me how to do it or give me a hint, please?

Edit: Here my test code for NotificationCompat.Builder

MainActivity.java

import android.app.Notification;
import android.app.NotificationManager;
import android.os.Bundle;
import android.support.v4.app.NotificationCompat;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;

public class MainActivity extends AppCompatActivity
{
    private final int NOTIFICATION_ID = 10;

    @Override
    protected void onCreate (Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);

        NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(this);
        mBuilder.setContentTitle("Value");
        mBuilder.setContentText("123");
        mBuilder.setSmallIcon(R.mipmap.ic_launcher);
        mBuilder.setOngoing(true);
        mBuilder.setAutoCancel(false);

        //Intent resultIntent = new Intent(this, MainActivity.class);
        //PendingIntent resultPendingIntent = PendingIntent.getActivity(this, 0, resultIntent, PendingIntent.FLAG_UPDATE_CURRENT);
        //mBuilder.setContentIntent(resultPendingIntent);

        Notification notification = mBuilder.build();
        notification.flags |= Notification.FLAG_NO_CLEAR | Notification.FLAG_ONGOING_EVENT;

        NotificationManager mNotifyMgr = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
        mNotifyMgr.notify(NOTIFICATION_ID, notification);
    }
}

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:fitsSystemWindows="true"
    tools:context=".MainActivity">

    <android.support.design.widget.CoordinatorLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <android.support.design.widget.AppBarLayout android:layout_height="wrap_content"
                                                    android:layout_width="match_parent"
                                                    android:theme="@style/AppTheme.AppBarOverlay">

            <android.support.v7.widget.Toolbar android:id="@+id/toolbar"
                                               android:layout_width="match_parent"
                                               android:layout_height="wrap_content"
                                               android:background="#000000"
                                               app:popupTheme="@style/AppTheme.PopupOverlay" />

        </android.support.design.widget.AppBarLayout>

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:layout_alignParentLeft="true"
            android:layout_alignParentTop="true"
            android:orientation="vertical"
            app:layout_behavior="@string/appbar_scrolling_view_behavior"
            android:weightSum="100" >

            <TextView
                android:id="@+id/tv_value"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="Hello World!"
                app:layout_constraintBottom_toBottomOf="parent"
                app:layout_constraintLeft_toLeftOf="parent"
                app:layout_constraintRight_toRightOf="parent"
                app:layout_constraintTop_toTopOf="parent"/>

        </LinearLayout>

    </android.support.design.widget.CoordinatorLayout>

</RelativeLayout>

Result:

Result 1

Result 2

Matze
  • 171
  • 1
  • 1
  • 7
  • Possible duplicate of [Display Notification Text in Status Bar - Android](https://stackoverflow.com/questions/25734370/display-notification-text-in-status-bar-android) Hope, this is what you are looking for.. – Guruprasad J Rao Jul 28 '17 at 14:43
  • I already read and tried that but this is not what I'm searching. I don't need a notification to swype down, I need the text directly in status bar. I tried to use the setSmallIcon(int icon) from NotificationCompat.Builder, but it needs a int ressource, so no possible way to use text to bitmap/image. – Matze Jul 28 '17 at 15:46
  • @Matze has your problem solved? – Shoaib Kakal Dec 04 '20 at 05:38

3 Answers3

5

I do found a solution, the keyword is overlay with a floating window.

int statusBarHeight = 0;
int resourceId = getResources().getIdentifier("status_bar_height", "dimen", "android");
if (resourceId > 0) statusBarHeight = getResources().getDimensionPixelSize(resourceId);

final WindowManager.LayoutParams parameters = new WindowManager.LayoutParams(
        WindowManager.LayoutParams.WRAP_CONTENT,
        statusBarHeight,
        WindowManager.LayoutParams.TYPE_SYSTEM_ERROR,   // Allows the view to be on top of the StatusBar
        WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN,    // Keeps the button presses from going to the background window and Draws over status bar
        PixelFormat.TRANSLUCENT);
parameters.gravity = Gravity.TOP | Gravity.CENTER;

LinearLayout ll = new LinearLayout(this);
ll.setBackgroundColor(Color.TRANSPARENT);
LinearLayout.LayoutParams layoutParameteres = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.MATCH_PARENT);
ll.setLayoutParams(layoutParameteres);

TextView tv = new TextView(this);
ViewGroup.LayoutParams tvParameters = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.MATCH_PARENT);
tv.setLayoutParams(tvParameters);
tv.setTextColor(Color.WHITE);
tv.setGravity(Gravity.CENTER);
tv.setText("123");
ll.addView(tv);

WindowManager windowManager = (WindowManager) getSystemService(WINDOW_SERVICE);
windowManager.addView(ll, parameters);
Matze
  • 171
  • 1
  • 1
  • 7
  • 3
    I am getting this following error android.view.WindowManager$BadTokenException: Unable to add window android.view.ViewRootImpl-- permission denied for window type 2010 .. any idea why or how to solve it? – Snake Oct 29 '19 at 03:36
  • 1
    Error: permission denied for window type 2010 – Shoaib Kakal Dec 04 '20 at 05:37
4

Well, I used the way of converting text to an icon and then displaying it on the status bar. some members are trying to overlay the status bar which android does not allow(SDK>=22), I don't know if that worked for someone or not. But converting text to an icon worked for me perfectly. Tested it on Oreo

Pseudo code

  1. Convert text to Bitmap
  2. Then convert Bitmap to icon.
  3. show that icon on statusBar.

Output

enter image description here

Here is the code:

 @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        displayNotification("5F");
    }
 public void displayNotification(String text) {

        Notification.Builder builder = null;
        if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
            builder = new Notification.Builder(this, CHANNEL_ID);
        }

        //convert text to bitmap
        Bitmap bitmap = createBitmapFromString(text.trim());

        //setting bitmap to staus bar icon.
        builder.setSmallIcon(Icon.createWithBitmap(bitmap));

        builder.setContentTitle("Simple Notification");
        builder.setContentText("This is a simple notification");
        builder.setPriority(Notification.PRIORITY_MAX);

        NotificationManagerCompat notificationManagerCompat = NotificationManagerCompat.from(this);
        notificationManagerCompat.notify(NOTIFICATION_ID, builder.build());

        createNotificationChannel();
    }
  private void createNotificationChannel() {
        // Create the NotificationChannel, but only on API 26+ because
        // the NotificationChannel class is new and not in the support library
        if (VERSION.SDK_INT >= VERSION_CODES.O) {
            CharSequence name = "testing";
            String description = "i'm testing this notification";
            int importance = NotificationManager.IMPORTANCE_DEFAULT;
            NotificationChannel channel = new NotificationChannel(CHANNEL_ID, name, importance);
            channel.setDescription(description);
            // Register the channel with the system; you can't change the importance
            // or other notification behaviors after this
            NotificationManager notificationManager = getSystemService(NotificationManager.class);
            assert notificationManager != null;
            notificationManager.createNotificationChannel(channel);
        }
    }

  
  private Bitmap createBitmapFromString(String inputNumber) {

        Paint paint = new Paint();
        paint.setAntiAlias(true);
        paint.setTextSize(100);
        paint.setTextAlign(Paint.Align.CENTER);

        Rect textBounds = new Rect();
        paint.getTextBounds(inputNumber, 0, inputNumber.length(), textBounds);

        Bitmap bitmap = Bitmap.createBitmap(textBounds.width() + 10, 90,
                Bitmap.Config.ARGB_8888);

        Canvas canvas = new Canvas(bitmap);
        canvas.drawText(inputNumber, textBounds.width() / 2 + 5, 70, paint);
        return bitmap;
    }

If someone knows a better way then please do mention

Shoaib Kakal
  • 1,090
  • 2
  • 16
  • 32
  • Great idea. I have implemented this and its working. But the only problem is that I'm getting bad notification crashes a lot. I searched for it and found that builder.setSmallIcon() argument should be PNG. Do you know how to convert bitmap to PNG and pass it to builder.setSmallIcon()? – SemicolonSpace Oct 23 '21 at 08:43
  • What do I need to do for a larger text? for example, I want to print "androiddeveloper" instead of 5F – Vahit Keskin May 01 '23 at 08:04
  • @VahitKeskin you can't show that instead you should use floating widget for that. – Shoaib Kakal May 02 '23 at 09:45
  • So how can I show its floating property in statusBar. Can you show an example? @ShoaibKakal – Vahit Keskin May 03 '23 at 07:47
  • you can create a custom rectangle floating widget and show it on the top of the status bar. here's an example of a messenger floating widget https://github.com/sonusurender/Floating_Widget_Chat_Head_Demo – Shoaib Kakal May 03 '23 at 10:47
0

You can find your answer in the doc, here: https://developer.android.com/reference/android/support/v4/app/NotificationCompat.html

Edit:: Well, the answer is in the doc. However, after a good bit of research and digging, it seems as though the consensus amongst the community is that this is not possible for just any application. Only specific icons can be placed on the right side of the status bar (i.e. Clock, Weather, System info, etc...).

I'm sorry there isn't a more exciting answer, but at least you can stop stressing out about why you can't figure it out.

Edit 2:: Apparently, pre-lollipop devices had access to private apis that allowed you to work with system icons (again, think about the alarm icon). Afterward, the apis were removed. This stackoverflow post goes over the whole situation pretty extensively.

Edit 3:: If you can live with placing you icon on the left side of the status bar you can convert text to bitmap like this:

 TextView textView = new TextView(activity.getContext());
 textView.setText("Hello World");
 textView.setDrawingCacheEnabled(true);
 textView.destroyDrawingCache();
 textView.buildDrawingCache();
 Bitmap bitmap = getTransparentBitmapCopy(textView.getDrawingCache());
 private Bitmap getTransparentBitmapCopy(Bitmap source) { 
    int width = source.getWidth(); 
    int height = source.getHeight(); 
    Bitmap copy = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
    int[] pixels = new int[width * height];
    source.getPixels(pixels, 0, width, 0, 0, width, height);
    copy.setPixels(pixels, 0, width, 0, 0, width, height);
    return copy;
 }
anomeric
  • 688
  • 5
  • 17
  • Can you please be a bit specific. I tried to use NotificationCompat.Builder, but it didn't work as needed (sample picture above). – Matze Jul 28 '17 at 15:50
  • Please feel free to post your code and an image of your result. – anomeric Jul 28 '17 at 15:52
  • I edited my post, so you can see my code. Please tell me what part of the doc I missed. – Matze Jul 28 '17 at 18:21
  • How I said, NotificationCompat is not the right way, but there is a working way, demonstrated in the app Battery Monitor (GLGJing). Maybe someone can do it with an overlay layout. I don't now if it's the right way, but is ActionBarOverlayLayout a possibility? I read something about it, but not much and I don't find really helpful information in the android documentation. – Matze Jul 28 '17 at 20:45
  • The functionality that you are looking for is not possible. I'll edit my answer for further clarification. – anomeric Jul 28 '17 at 20:49