web-dev-qa-db-ja.com

Androidフローティングウィジェットオーバーレイの表示が機能しない

Facebookメッセンジャーのようにアプリにフローティングバブル通知を表示させたい。次の https://www.androidhive.info/2016/11/Android-floating-widget-like-facebook-chat-head/ 以下は私が書いたサービスです。

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は、次のように通知を受け取る別のサービスから呼び出されます。

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);

インテントからのすべてのデータはBubbleNotifyServiceに渡されます。マニフェストAndroid:process=":BubbleResult"で指定されているように、サービスは別のプロセスとして実行されていますが、サービスはオーバーレイを表示しません。他のアプリの描画権限がアプリに付与されます。

バブルのレイアウト(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>
7
Pushkar

あなたのコードと言及されたブログ投稿のコードの両方をテストしました。問題は、レイアウトでConstraintLayoutCardViewを使用したことが原因のようです。

ウィジェットの動作 の場合、これらのビュータイプはサポートされていません。ビューをシステムウィンドウに直接追加すると、RemoteViewsのように動作します。 ConstraintLayoutCardViewを変更するとすぐに、レイアウトが表示され、操作可能になります。

3
theJango

これが私の作業コードです。フローティングビューを表示する必要がある場合は、このサービスを開始します。

FloatingService.class

import Android.app.Service;
import Android.content.Intent;
import Android.graphics.PixelFormat;
import Android.os.Build;
import Android.os.IBinder;
import Android.view.Gravity;
import Android.view.LayoutInflater;
import Android.view.MotionEvent;
import Android.view.View;
import Android.view.WindowManager;
import Android.widget.ImageView;
import Android.widget.Toast;

public class FloatingService extends Service {

    private WindowManager mWindowManager;
    private View mFloatingView;

    public FloatingService() {
    }

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

    @Override
    public void onCreate() {
        super.onCreate();
        //Inflate the floating view layout we created
        mFloatingView = LayoutInflater.from(this).inflate(R.layout.layout_floating_widget, null);
        int LAYOUT_FLAG;
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            LAYOUT_FLAG = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
        } else {
            LAYOUT_FLAG = WindowManager.LayoutParams.TYPE_PHONE;
        }
        //Add the view to the window.
        final WindowManager.LayoutParams params = new WindowManager.LayoutParams(
                WindowManager.LayoutParams.WRAP_CONTENT,
                WindowManager.LayoutParams.WRAP_CONTENT,
                LAYOUT_FLAG,
                WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
                PixelFormat.TRANSLUCENT);

        //Specify the view position
        params.gravity = Gravity.TOP | Gravity.LEFT;        //Initially view will be added to top-left corner
        params.x = 0;
        params.y = 100;

        //Add the view to the window
        mWindowManager = (WindowManager) getSystemService(WINDOW_SERVICE);
        mWindowManager.addView(mFloatingView, params);

        //The root element of the collapsed view layout
        final View collapsedView = mFloatingView.findViewById(R.id.collapse_view);
        //The root element of the expanded view layout
        final View expandedView = mFloatingView.findViewById(R.id.expanded_container);


        //Set the close button
        ImageView closeButtonCollapsed = (ImageView) mFloatingView.findViewById(R.id.close_btn);
        closeButtonCollapsed.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                //close the service and remove the from from the window
                stopSelf();
            }
        });

        //Set the view while floating view is expanded.
        //Set the play button.
        ImageView playButton = (ImageView) mFloatingView.findViewById(R.id.play_btn);
        playButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Toast.makeText(FloatingService.this, "Playing the song.", Toast.LENGTH_LONG).show();
            }
        });

        //Set the next button.
        ImageView nextButton = (ImageView) mFloatingView.findViewById(R.id.next_btn);
        nextButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Toast.makeText(FloatingService.this, "Playing next song.", Toast.LENGTH_LONG).show();
            }
        });

        //Set the pause button.
        ImageView prevButton = (ImageView) mFloatingView.findViewById(R.id.prev_btn);
        prevButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Toast.makeText(FloatingService.this, "Playing previous song.", Toast.LENGTH_LONG).show();
            }
        });

        //Set the close button
        ImageView closeButton = (ImageView) mFloatingView.findViewById(R.id.close_button);
        closeButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                collapsedView.setVisibility(View.VISIBLE);
                expandedView.setVisibility(View.GONE);
            }
        });

        //Open the application on thi button click
        ImageView openButton = (ImageView) mFloatingView.findViewById(R.id.open_button);
        openButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                //Open the application  click.
                Intent intent = new Intent(FloatingService.this, MainActivity.class);
                intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                startActivity(intent);

                //close the service and remove view from the view hierarchy
                stopSelf();
            }
        });

        //Drag and move floating view using user's touch action.
        mFloatingView.findViewById(R.id.root_container).setOnTouchListener(new View.OnTouchListener() {
            private int initialX;
            private int initialY;
            private float initialTouchX;
            private float initialTouchY;

            @Override
            public boolean onTouch(View v, MotionEvent event) {
                switch (event.getAction()) {
                    case MotionEvent.ACTION_DOWN:

                        //remember the initial position.
                        initialX = params.x;
                        initialY = params.y;

                        //get the touch location
                        initialTouchX = event.getRawX();
                        initialTouchY = event.getRawY();
                        return true;
                    case MotionEvent.ACTION_UP:
                        int Xdiff = (int) (event.getRawX() - initialTouchX);
                        int Ydiff = (int) (event.getRawY() - initialTouchY);

                        //The check for Xdiff <10 && YDiff< 10 because sometime elements moves a little while clicking.
                        //So that is click event.
                        if (Xdiff < 10 && Ydiff < 10) {
                            if (isViewCollapsed()) {
                                //When user clicks on the image view of the collapsed layout,
                                //visibility of the collapsed layout will be changed to "View.GONE"
                                //and expanded view will become visible.
                                collapsedView.setVisibility(View.GONE);
                                expandedView.setVisibility(View.VISIBLE);
                            }
                        }
                        return true;
                    case MotionEvent.ACTION_MOVE:
                        //Calculate the X and Y coordinates of the view.
                        params.x = initialX + (int) (event.getRawX() - initialTouchX);
                        params.y = initialY + (int) (event.getRawY() - initialTouchY);

                        //Update the layout with new X & Y coordinate
                        mWindowManager.updateViewLayout(mFloatingView, params);
                        return true;
                }
                return false;
            }
        });
    }

    /**
     * Detect if the floating view is collapsed or expanded.
     *
     * @return true if the floating view is collapsed.
     */
    private boolean isViewCollapsed() {
        return mFloatingView == null || mFloatingView.findViewById(R.id.collapse_view).getVisibility() == View.VISIBLE;
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        if (mFloatingView != null) mWindowManager.removeView(mFloatingView);
    }
}

それから加えて

  <service
            Android:name="yourpakcage.FloatingService"
            Android:enabled="true"
            Android:exported="false"/>

layout_floating_widget.xml

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
    xmlns:Android="http://schemas.Android.com/apk/res/Android"
    xmlns:tools="http://schemas.Android.com/tools"
    Android:layout_width="wrap_content"
    Android:layout_height="wrap_content">

    <!--Root container-->
    <RelativeLayout
        Android:id="@+id/root_container"
        Android:layout_width="wrap_content"
        Android:layout_height="wrap_content"
        tools:ignore="UselessParent">

        <!--View while view is collapsed-->
        <RelativeLayout
            Android:id="@+id/collapse_view"
            Android:layout_width="wrap_content"
            Android:visibility="visible"
            Android:layout_height="wrap_content"
            Android:orientation="vertical">

            <!--Icon of floating widget -->
            <ImageView
                Android:id="@+id/collapsed_iv"
                Android:layout_width="60dp"
                Android:layout_height="60dp"
                Android:layout_marginTop="8dp"
                Android:src="@drawable/ic_Android_circle"
                tools:ignore="ContentDescription"/>

            <!--Close button-->
            <ImageView
                Android:id="@+id/close_btn"
                Android:layout_width="20dp"
                Android:layout_height="20dp"
                Android:layout_marginLeft="40dp"
                Android:src="@drawable/ic_close"
                tools:ignore="ContentDescription"/>
        </RelativeLayout>

        <!--View while view is expanded-->
        <LinearLayout
            Android:id="@+id/expanded_container"
            Android:layout_width="wrap_content"
            Android:layout_height="wrap_content"
            Android:background="#F8BBD0"
            Android:visibility="gone"
            Android:orientation="horizontal"
            Android:padding="8dp">

            <!--Album image for the song currently playing.-->
            <ImageView
                Android:layout_width="80dp"
                Android:layout_height="80dp"
                Android:src="@drawable/music_player"
                tools:ignore="ContentDescription"/>

            <!--Previous button-->
            <ImageView
                Android:id="@+id/prev_btn"
                Android:layout_width="30dp"
                Android:layout_height="30dp"
                Android:layout_gravity="center_vertical"
                Android:layout_marginLeft="20dp"
                Android:src="@mipmap/ic_previous"
                tools:ignore="ContentDescription"/>

            <!--Play button-->
            <ImageView
                Android:id="@+id/play_btn"
                Android:layout_width="50dp"
                Android:layout_height="50dp"
                Android:layout_gravity="center_vertical"
                Android:layout_marginLeft="10dp"
                Android:src="@mipmap/ic_play"
                tools:ignore="ContentDescription"/>

            <!--Next button-->
            <ImageView
                Android:id="@+id/next_btn"
                Android:layout_width="30dp"
                Android:layout_height="30dp"
                Android:layout_gravity="center_vertical"
                Android:layout_marginLeft="10dp"
                Android:src="@mipmap/ic_play_next"
                tools:ignore="ContentDescription"/>

            <RelativeLayout
                Android:layout_width="wrap_content"
                Android:layout_height="match_parent"
                Android:orientation="vertical">

                <ImageView
                    Android:id="@+id/close_button"
                    Android:layout_width="20dp"
                    Android:layout_height="20dp"
                    Android:src="@drawable/ic_close"/>

                <ImageView
                    Android:id="@+id/open_button"
                    Android:layout_width="20dp"
                    Android:layout_height="20dp"
                    Android:layout_alignParentBottom="true"
                    Android:src="@drawable/ic_open"/>
            </RelativeLayout>
        </LinearLayout>
    </RelativeLayout>
</FrameLayout>

ダミーのドローアブルと文字列を追加してください。

フローティングビューを表示します。

startService(new Intent(passContext, FloatingViewService.class));
1
Khemraj