Monday 16 July 2018

Android displaying floating widget overlay not working

I want my my app to display a floating bubble notification as in facebook messenger. Following https://www.androidhive.info/2016/11/android-floating-widget-like-facebook-chat-head/ below is the service I wrote.

import android.annotation.SuppressLint;
import android.app.Service;
import android.content.Intent;
import android.graphics.PixelFormat;
import android.os.IBinder;
import android.util.Log;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.WindowManager;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.TextView;

public class BubbleNotifyService extends Service {

    private WindowManager windowManager;
    private View BubbleView;
    private TextView bubbleTitle, bubbleData;

    public BubbleNotifyService() {
    }

    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

    @SuppressLint({"RtlHardcoded", "InflateParams"})
    @Override
    public void onCreate() {
        super.onCreate();
        //android.os.Debug.waitForDebugger();

        setTheme(R.style.AppTheme);

        BubbleView =
                LayoutInflater.from(this).inflate(R.layout.floating_bubble, null);

        final WindowManager.LayoutParams params;

    if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
        params = new WindowManager.LayoutParams(
                WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY,
                WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
                PixelFormat.TRANSLUCENT
        );
    } else {
        params = new WindowManager.LayoutParams(
                WindowManager.LayoutParams.TYPE_PHONE,
                WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE ,
                PixelFormat.TRANSLUCENT
        );
    }

        if (BubbleView == null) {
            Log.d("Bubble", "BubbleView is null.");
        }

        params.gravity = Gravity.TOP | Gravity.LEFT;
        params.x = 0;
        params.y = 100;

        windowManager =
                (WindowManager) getSystemService(WINDOW_SERVICE);

        if (windowManager != null) {
            Log.d("Bubble", "added bubble to view.");
            windowManager.addView(BubbleView, params);
        } else {
            Log.d("Bubble", "windowManager is null");
        }

        final View collapsedView =
                BubbleView.findViewById(R.id.collapsed_view);
        final View expandedView =
                BubbleView.findViewById(R.id.expanded_view);

        expandedView.setVisibility(View.GONE);
        collapsedView.setVisibility(View.VISIBLE);


        ImageView close_collapsed = BubbleView.findViewById(R.id.collaspsed_cancel);
        ImageView close_expanded = BubbleView.findViewById(R.id.expanded_cancel);
        Button open_act_btn = BubbleView.findViewById(R.id.open_full_btn);
        bubbleTitle = BubbleView.findViewById(R.id.bubble_title);
        bubbleMeaning = BubbleView.findViewById(R.id.bubble_data);

        close_collapsed.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                stopSelf();
            }
        });

        close_expanded.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                collapsedView.setVisibility(View.VISIBLE);
                expandedView.setVisibility(View.GONE);
            }
        });

        open_act_btn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {

                //TODO new Activity
            }
        });

        BubbleView.findViewById(R.id.bubble_root).setOnTouchListener(new View.OnTouchListener() {

            private int initialX;
            private int initialY;
            private float initialTouchX;
            private float initialTouchY;

            @SuppressLint("ClickableViewAccessibility")
            @Override
            public boolean onTouch(View view, MotionEvent motionEvent) {

                switch (motionEvent.getAction()) {

                    case MotionEvent.ACTION_DOWN:
                        initialX = params.x;
                        initialY = params.y;

                        initialTouchX = motionEvent.getRawX();
                        initialTouchY = motionEvent.getRawY();
                        return true;

                    case MotionEvent.ACTION_UP:
                        int Xdiff = (int) (motionEvent.getRawX() - initialTouchX);
                        int Ydiff = (int) (motionEvent.getRawY() - initialTouchY);

                        if (Xdiff < 10 && Ydiff < 10) {
                            if (isViewCollapsed()) {
                                collapsedView.setVisibility(View.GONE);
                                expandedView.setVisibility(View.VISIBLE);
                            }
                        }
                        return true;

                    case MotionEvent.ACTION_MOVE:
                        params.x =
                                initialX + (int) (motionEvent.getRawX() - initialTouchX);
                        params.y =
                                initialY + (int) (motionEvent.getRawY() - initialTouchY);

                        windowManager.updateViewLayout(BubbleView, params);
                        return true;
                }
                return false;
            }
        });

    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        super.onStartCommand(intent, flags, startId);

        String title = intent.getStringExtra(IntentKeys.BUBBLE_DATA_TITLE);
        String data = intent.getStringExtra(IntentKeys.BUBBLE_DATA);

        bubbleTitle.setText(title);
        bubbleData.setText(data);

        return START_NOT_STICKY;
    }

    private boolean isViewCollapsed() {
        return BubbleView == null ||
                BubbleView.findViewById(R.id.collapsed_view).getVisibility() == View.VISIBLE;
    }


    @Override
    public void onDestroy() {
        super.onDestroy();

        if (BubbleView != null) {
            windowManager.removeView(BubbleView);
        }
    }
}

BubbleNotifyService is called from another service that receives notification as:

Intent intent = new Intent(context, BubbleNotifyService.class);
                intent.putExtra(IntentKeys.BUBBLE_DATA_TITLE, Results.getWord());
                intent.putExtra(IntentKeys.BUBBLE_DATA, Results.getData());
                context.startService(intent);

All data from the intent is passed to BubbleNotifyService. The service is running as a separate process as specified in manifest android:process=":BubbleResult", But the service does not display any overlay. Draw over other app permission is granted to the app.

Layout for bubble(floating_bubble.xml)

<?xml version="1.0" encoding="utf-8"?>

<FrameLayout 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:id="@+id/bubble_frame"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content">

    <android.support.constraint.ConstraintLayout
        android:id="@+id/bubble_root"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content">


        <android.support.constraint.ConstraintLayout
            android:id="@+id/collapsed_view"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:visibility="visible">

            <ImageView
                android:id="@+id/collapsed_icon"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                app:srcCompat="@mipmap/ic_launcher"
                tools:ignore="ContentDescription" />

            <ImageView
                android:id="@+id/collaspsed_cancel"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                app:layout_constraintStart_toEndOf="@+id/collapsed_icon"
                app:layout_constraintTop_toTopOf="parent"
                app:srcCompat="@drawable/ic_action_cancel"
                tools:ignore="ContentDescription" />

        </android.support.constraint.ConstraintLayout>

        <android.support.v7.widget.CardView
            android:id="@+id/expanded_view"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:visibility="visible"
            app:cardCornerRadius="5dp"
            app:cardElevation="10dp"
            app:cardUseCompatPadding="true"
            app:contentPadding="5dp">

            <android.support.constraint.ConstraintLayout
                android:layout_width="match_parent"
                android:layout_height="match_parent">

                <TextView
                    android:id="@+id/bubble_title"
                    android:layout_width="0dp"
                    android:layout_height="wrap_content"
                    android:layout_marginTop="8dp"
                    android:textSize="24sp"
                    android:textStyle="bold"
                    app:layout_constraintEnd_toStartOf="@id/expanded_cancel"
                    app:layout_constraintHorizontal_bias="0.0"
                    app:layout_constraintStart_toStartOf="parent"
                    app:layout_constraintTop_toTopOf="parent"
                    tools:text="@string/result_card_title" />

                <TextView
                    android:id="@+id/bubble_data"
                    android:layout_width="0dp"
                    android:layout_height="wrap_content"
                    android:textSize="18sp"
                    app:layout_constraintEnd_toEndOf="parent"
                    app:layout_constraintStart_toStartOf="parent"
                    app:layout_constraintTop_toBottomOf="@id/bubble_title"
                    tools:text="@string/result_card_data" />

                <ImageView
                    android:id="@+id/expanded_cancel"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    app:layout_constraintEnd_toEndOf="parent"
                    app:layout_constraintStart_toEndOf="@id/bubble_title"
                    app:srcCompat="@drawable/ic_action_cancel"
                    tools:ignore="ContentDescription" />

                <Button
                    android:id="@+id/open_full_btn"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_marginEnd="8dp"
                    android:layout_marginTop="8dp"
                    android:text="@string/result_card_see_more"
                    app:layout_constraintEnd_toEndOf="parent"
                    app:layout_constraintTop_toBottomOf="@+id/bubble_data" />


            </android.support.constraint.ConstraintLayout>
        </android.support.v7.widget.CardView>

    </android.support.constraint.ConstraintLayout>


</FrameLayout>

Please care to explain the reason for downvoting if you are downvoting this.



from Android displaying floating widget overlay not working

No comments:

Post a Comment