Why floating widget overlay button is not visible and code is without error?

150 Views Asked by At

I tried to make floating widget overlay button - like messenger profile photo widget, which is displayed when someone send you message. I used these files. When I run program, there is nothing invalid in code. I see app with button but there is nothing visible after click.

MainActivity.java:

package com.example.mywidget;

import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import androidx.appcompat.app.AppCompatActivity;

public class MainActivity extends Activity {

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

        startFloatingWidgetService();

    };

    private void startFloatingWidgetService() {
        Intent intent = new Intent(this, FloatingWidgetService.class);
        startService(intent);
    }
};

FloatingWidgetService.java

package com.example.mywidget; 

import android.app.Service;
import android.content.Intent;
import android.graphics.PixelFormat;
import android.os.IBinder;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.WindowManager;
import android.widget.Button;
import android.widget.LinearLayout;

public class FloatingWidgetService extends Service {

    private WindowManager mWindowManager;
    private View mFloatingWidgetView;

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

    @Override
    public void onCreate() {
        super.onCreate();
        mFloatingWidgetView = LayoutInflater.from(this).inflate(R.layout.floating_widget_layout, null);

        // Inicializace tlačítka
        Button button = mFloatingWidgetView.findViewById(R.id.floating_button);
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
              
                showTextDialog();
            }
        });

        // Inicializace WindowManager
        mWindowManager = (WindowManager) getSystemService(WINDOW_SERVICE);
        WindowManager.LayoutParams params = new WindowManager.LayoutParams(
                WindowManager.LayoutParams.WRAP_CONTENT,
                WindowManager.LayoutParams.WRAP_CONTENT,
                WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY, 
                WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
                PixelFormat.TRANSLUCENT
        );

        params.gravity = Gravity.BOTTOM | Gravity.END;
        mWindowManager.addView(mFloatingWidgetView, params);
    }

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

    private void showTextDialog() {

    }
}

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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"
    tools:context=".MainActivity">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Hello World!"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <Button
        android:id="@+id/start_button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Start Floating Widget Service" />

</androidx.constraintlayout.widget.ConstraintLayout>

floating_widget_layout.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:orientation="vertical">

    <Button
        android:id="@+id/floating_button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="dfdsfdasdfasdfAFASD"
        android:layout_gravity="end"
        android:background="@android:color/holo_red_light"/>

</LinearLayout>

AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools">

    <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>

    <application
        android:allowBackup="true"
        android:dataExtractionRules="@xml/data_extraction_rules"
        android:fullBackupContent="@xml/backup_rules"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:supportsRtl="true"
        android:theme="@style/Theme.Mywidget"
        tools:targetApi="31">
        <activity
            android:name=".MainActivity"
            android:exported="true">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>
2

There are 2 best solutions below

2
Sarmad Ali On BEST ANSWER

I have done some modification in your files. This code works fine when i tested it on my physical device, but it has one drawback that when user granted the permission of overlay it do not show floating widget but when you relaunch the app then it show the flaoting button. if you want to achieve this when user granted permission and it should show the flaoting widget immediately after it you can do it by imlementing broadcast receiver (with permission of MANAGE_OVERLAY_PERMISSION") and using the foregroundservice: however the code is this:

MainActivity.java:

public class MainActivity extends AppCompatActivity {

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

        startFloatingWidgetService();
    }
    private void startFloatingWidgetService() {
        Intent intent = new Intent(this, FloatingWidgetService.class);
        startService(intent);
    }
}

FloatingWidgetService.java

In this class the main change i did is to check if we have permission to overlay or not. if not request permission if it's already give or not needed then show widget.

public class FloatingWidgetService extends Service {


private WindowManager mWindowManager;
private View mFloatingWidgetView;
@Override
public IBinder onBind(Intent intent) {
    
    return null;
}

// Method to request the SYSTEM_ALERT_WINDOW permission
private void requestOverlayPermission() {

    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
        Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION);
        intent.setData(Uri.parse("package:" + getPackageName()));
        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        startActivity(intent);
    }
}

// Method to create the floating widget
private void createFloatingWidget() {

    // Inflate the layout for the floating widget
    mFloatingWidgetView = LayoutInflater.from(this).inflate(R.layout.floating_widget_layout,
            null);

    // Initialize the button on the floating widget
    Button button = mFloatingWidgetView.findViewById(R.id.floating_button);
    button.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            // Handle the button click event
            showTextDialog();
            // You can perform other actions here as needed
        }
    });

    // Initialize the WindowManager
    mWindowManager = (WindowManager) getSystemService(WINDOW_SERVICE);

    // Set up WindowManager.LayoutParams for the floating widget
    WindowManager.LayoutParams params = new WindowManager.LayoutParams(
            WindowManager.LayoutParams.WRAP_CONTENT,
            WindowManager.LayoutParams.WRAP_CONTENT,
            WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY,
            WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
            PixelFormat.TRANSLUCENT
    );

    // Set the gravity of the floating widget (position on the screen)
    params.gravity = Gravity.BOTTOM | Gravity.END;

    // Add the floating widget to the WindowManager
    mWindowManager.addView(mFloatingWidgetView, params);
}

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

    // Check if the permission is granted (only for API level 23 and higher)
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && !Settings.canDrawOverlays(this)) {
        // You don't have permission to draw overlays, so request it
        requestOverlayPermission();
    } else {
        // Permission is granted, proceed with creating the floating widget
        createFloatingWidget();
    }



}

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

private void showTextDialog() {

                             }
}

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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"
    tools:context=".MainActivity">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Hello World!"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <Button
        android:id="@+id/start_button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="16dp"
        android:text="@string/start_floating_widget_service"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.5"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

</androidx.constraintlayout.widget.ConstraintLayout>

floating_widget_layout.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:orientation="vertical">

    <Button
        android:id="@+id/floating_button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="end"
        android:background="@android:color/holo_red_light"
        android:text="@string/floating_widget_button"
        android:textColor="#212121" />

</LinearLayout>

AndroidManifest.xml

In your manifest you didn't added service in your manifest.

<service
            android:name=".FloatingWidgetService"
            android:enabled="true"
            android:exported="true" />

if you don't face any issue with the overlay permission then it should work by only adding service to your manifest but to be on the safe side add the code that I provided in the FloatingWidgetService class to verify the permission.

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools">

    <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />

    <application
        android:allowBackup="true"
        android:dataExtractionRules="@xml/data_extraction_rules"
        android:fullBackupContent="@xml/backup_rules"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/Theme.FloatingWidget"
        tools:targetApi="31">

        <service
            android:name=".FloatingWidgetService"
            android:enabled="true"
            android:exported="true" />

        <activity
            android:name=".MainActivity"
            android:exported="true">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>

You can access the code here : Github

let me know if it works for you or not.

2
Sarmad Ali On

Your showTextDialog() method in FloatingWidgetService.java is empty and that's why nothing appears when you call this empty method:

 private void showTextDialog() {

    }

try this to test if it is properly working or not and then replace it with your code you want:

private void showTextDialog() {
    Toast.makeText(this, "Button clicked!", Toast.LENGTH_SHORT).show();
}