Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to create a notification with a custom view , but with an native look & feel?

Background

I need to make a custom, big-style notification that has about 3 images on the bottom, and a title of and image and 2 textviews on the top.

Something like that:

enter image description here

The upper area needs to look exactly like the native notification, to make it have a nice transition between the two.

The bottom area is dynamic in terms of which photos to put there (using bitmaps).

The problem

Once I use a custom view, it actually holds the entire layout, including the upper area.

What I've tried

I've thought of 2 solutions :

  1. create a bitmap that holds the 3 images inside it, and then use BigPictureStyle for it. Problem is that it might crop the content of the image in some cases, and the spacing between the images won't be shown well if you go to landscape (and I've checked : you can't set multiple layouts, for example one for landscape and one for portrait).

In order to create the bitmap, I could use something like this :

    final int widthScreen = getResources().getDisplayMetrics().widthPixels;
    final int width = Math.min(widthScreen, (int) convertDpToPixels(this, 256));
    final Bitmap outputBitmap = Bitmap.createBitmap(width, width / 3, Config.ARGB_8888);
    final Canvas canvas = new Canvas(outputBitmap);
    final int[] imagesResIds = { R.drawable.pic1, R.drawable.pic2, R.drawable.pic3 };
    final Paint paint = new Paint();
    for (int i = 0; i < 3; ++i) {
        final Bitmap b = BitmapFactory.decodeResource(getResources(), imagesResIds[i]);
        canvas.drawBitmap(b, null, new Rect(i * (width / 3), 0, (i + 1) * (width / 3) - 1, width / 3 - 1), paint);
    }
    return outputBitmap;

For some reason, it doesn't show the images well, but you get the idea...

I've also tried to inflate the views on #2 , and draw to a bitmap. It works, but on some devices and configurations (even landscape) it doesn't work well .

  1. Use RemoteViews, and then set the images on the ImageViews. The problem is that I need to mimic the top area to look native, but even if I find it, there is no guarantee that it will look well on all devices and Android versions (right?) . Plus I will probably need to get the native notification layout of each Android version. It could also be nice to use a horizontal listView, but it doesn't exist.

Here's a sample layout of the bottom area of the notification:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="128dp"
    android:gravity="center_horizontal"
    android:orientation="horizontal" >

    <ImageView
        android:layout_width="0px"
        android:layout_height="wrap_content"
        android:layout_weight="1" />

    <ImageView
        android:layout_width="128dp"
        android:layout_height="match_parent"
        android:scaleType="centerCrop"
        android:src="@drawable/pic1" />

    <ImageView
        android:layout_width="0px"
        android:layout_height="wrap_content"
        android:layout_weight="1" />

    <ImageView
        android:layout_width="128dp"
        android:layout_height="match_parent"
        android:scaleType="centerCrop"
        android:src="@drawable/pic2" />

    <ImageView
        android:layout_width="0px"
        android:layout_height="wrap_content"
        android:layout_weight="1" />

    <ImageView
        android:layout_width="128dp"
        android:layout_height="match_parent"
        android:scaleType="centerCrop"
        android:src="@drawable/pic3" />

    <ImageView
        android:layout_width="0px"
        android:layout_height="wrap_content"
        android:layout_weight="1" />

</LinearLayout>

What I've found

The documentation about using style on custom layouts is pretty vague, telling to just be careful and try it out on multiple Android devices:

Caution: When you use a custom notification layout, take special care to ensure that your custom layout works with different device orientations and resolutions. While this advice applies to all View layouts, it's especially important for notifications because the space in the notification drawer is very restricted. Don't make your custom layout too complex, and be sure to test it in various configurations.

Same goes about styling texts:

Always use style resources for the text of a custom notification. The background color of the notification can vary across different devices and versions, and using style resources helps you account for this. Starting in Android 2.3, the system defined a style for the standard notification layout text. If you use the same style in applications that target Android 2.3 or higher, you'll ensure that your text is visible against the display background.

They don't tell what to use, which style or color or anything...

The question

Is it possible to create such a notification? How should I handle it?

Do you have any other idea of how this can be done?

Is it also possible to set a different layout using qualifiers (I didn't succeed doing it) ?

Also, maybe the most important question: how do I use the default style of the notifications on custom views? I'm talking not only about the title and subtitle textViews (which probably use "android:TextAppearance.StatusBar.EventContent.Title" and "android:TextAppearance.StatusBar.EventContent" styles) , but for any part of the notifications: the icons the text on the right, the actions,...


EDIT: after searching for a while about styles of the notification, I've found this in "styles.xml" inside the source code of Android:

<style name="TextAppearance.StatusBar.Icon">
</style>
<!-- Notification content styles -->
<style name="TextAppearance.StatusBar.EventContent">
    <item name="textColor">#999999</item>
    <item name="textSize">@dimen/notification_text_size</item>
</style>
<style name="TextAppearance.StatusBar.EventContent.Title">
    <item name="textColor">#ffffff</item>
    <item name="fontFamily">sans-serif-light</item>
    <item name="textSize">@dimen/notification_title_text_size</item>
    <item name="textStyle">bold</item>
</style>
<style name="TextAppearance.StatusBar.EventContent.Line2">
    <item name="textSize">@dimen/notification_subtext_size</item>
</style>
<style name="TextAppearance.StatusBar.EventContent.Info">
    <item name="textSize">@dimen/notification_subtext_size</item>
    <item name="textColor">#999999</item>
</style>
<style name="TextAppearance.StatusBar.EventContent.Time">
    <item name="textSize">@dimen/notification_subtext_size</item>
    <item name="textColor">#999999</item>
</style>
<style name="TextAppearance.StatusBar.EventContent.Emphasis">
    <item name="textColor">#CCCCCC</item>
</style>

It's weird that there is a style named "TextAppearance.StatusBar.Icon", as it's for an icon, yet it's a textAppearance. Plus it's empty...

Anyway, after that, I've found the next layout file, called "notification_intruder_content.xml", but I'm not sure it's ok to copy it as things might change between OSs (and even between different roms) :

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="vertical"
    android:padding="4dp"
    >
    <ImageView android:id="@+id/icon"
        android:layout_width="32dp"
        android:layout_height="32dp"
        android:scaleType="center"
        android:padding="4dp"
        />
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="40dp"
        android:layout_marginStart="40dp"
        android:orientation="vertical"
        >
        <TextView android:id="@+id/title"
            android:textAppearance="@style/TextAppearance.StatusBar.EventContent.Title"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:singleLine="true"
            android:ellipsize="marquee"
            android:fadingEdge="horizontal"
            />
        <TextView android:id="@+id/text"
            android:textAppearance="@style/TextAppearance.StatusBar.EventContent"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:layout_marginTop="-4dp"
            android:singleLine="true"
            android:ellipsize="marquee"
            android:fadingEdge="horizontal"
            />
    </LinearLayout>
    <LinearLayout
        android:id="@+id/actions"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="48dp"
        android:orientation="horizontal"
        android:visibility="gone"
        >
        <Button
            style="?android:attr/buttonBarButtonStyle"
            android:textAppearance="@style/TextAppearance.StatusBar.EventContent.Title"
            android:id="@+id/action0"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:visibility="gone"
            />
        <Button
            style="?android:attr/buttonBarButtonStyle"
            android:textAppearance="@style/TextAppearance.StatusBar.EventContent.Title"
            android:id="@+id/action1"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:visibility="gone"
            />
        <Button
            style="?android:attr/buttonBarButtonStyle"
            android:textAppearance="@style/TextAppearance.StatusBar.EventContent.Title"
            android:id="@+id/action2"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:visibility="gone"
            />
    </LinearLayout>
</FrameLayout>

I've tried to use it (just changing the ids, and set the texts and the image), but it doesn't look well at all :

enter image description here

Not only that, but when touching it, I don't see the effect of touching normal notifications, so the background might be wrong too.


EDIT: sadly it was decided to use the custom-view solution (#2), but I can't find enough information about how to make everything look native, so I had to do some "reverse-engineering" by both looking at Android's code and how the notifications looks like.

If anyone has a full information of how to make it look well across all Android versions (and roms), please let me know.

like image 408
android developer Avatar asked Nov 02 '14 13:11

android developer


1 Answers

I didn't get Exact output, try this

Create Notification:

public void CustomNotification() {
        //dynamic layer
        LinearLayout ll = new LinearLayout(this);
        ll.setOrientation(LinearLayout.HORIZONTAL);

        LayoutParams llLP = new LayoutParams(
                LinearLayout.LayoutParams.FILL_PARENT,
                LinearLayout.LayoutParams.WRAP_CONTENT);
        ll.setLayoutParams(llLP);
        ImageView imageView = new ImageView(this);
        LayoutParams lp = new LayoutParams(LayoutParams.WRAP_CONTENT,
                LayoutParams.WRAP_CONTENT);
        imageView.setLayoutParams(lp);
        imageView.setImageResource(R.drawable.v1);

        ll.addView(imageView);
        ImageView imageView1 = new ImageView(this);

        imageView1.setLayoutParams(lp);
        imageView1.setImageResource(R.drawable.v2);

        ll.addView(imageView1);
        //create a dynamic layout to bitmap
        Bitmap bp = convertViewToDrawable(ll);
        NotificationCompat.Builder builder = new NotificationCompat.Builder(
                this).setSmallIcon(R.drawable.ic_launcher)
                .setTicker("Dummy App ").setAutoCancel(true)
                .setContentIntent(null);
        NotificationCompat.BigPictureStyle bigPicStyle = new NotificationCompat.BigPictureStyle();
        bigPicStyle.bigPicture(bp);
        bigPicStyle.setBigContentTitle("My Title");
        builder.setStyle(bigPicStyle);

        NotificationManager notificationmanager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
        notificationmanager.notify(100, builder.build());

    }

Creating view to bitmap

public static Bitmap convertViewToDrawable(View view) {
        int spec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
        view.measure(spec, spec);
        view.layout(0, 0, view.getMeasuredWidth(), view.getMeasuredHeight());
        Bitmap b = Bitmap.createBitmap(view.getMeasuredWidth(),
                view.getMeasuredHeight(), Bitmap.Config.ARGB_8888);
        Canvas c = new Canvas(b);
        c.translate(-view.getScrollX(), -view.getScrollY());

        view.draw(c);
        view.setDrawingCacheEnabled(true);
        Bitmap cacheBmp = view.getDrawingCache();
        Bitmap viewBmp = cacheBmp.copy(Bitmap.Config.ARGB_8888, true);
        view.destroyDrawingCache();
        return viewBmp;

    }

Output:

output

like image 108
appukrb Avatar answered Nov 06 '22 00:11

appukrb