You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Java中实现SYSTEM_ALERT_WINDOW应用时长提醒悬浮弹窗

Step-by-Step Guide to Build a Custom Floating Popup for App Usage Limits

Hey there! I see you've already tackled the SYSTEM_ALERT_WINDOW permission part (quick note: I spotted a tiny logic flip in your onCreate method—right now you're requesting permission only if you already have it; I'll fix that at the end!). Let's walk through building the floating popup and usage monitoring logic step by step, perfect for a new Android dev.

1. Create the Floating Popup Layout

First, make a custom layout for your popup. Create a new XML file in res/layout called layout_floating_popup.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:background="#ffffff"
    android:orientation="vertical"
    android:padding="16dp"
    android:elevation="8dp"
    android:layout_gravity="center">

    <TextView
        android:id="@+id/popup_title"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Time's Up!"
        android:textSize="18sp"
        android:textStyle="bold"/>

    <TextView
        android:id="@+id/popup_message"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="You've been using this app for X minutes. Take a break!"
        android:layout_marginTop="8dp"/>

    <Button
        android:id="@+id/btn_close"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Got it"
        android:layout_marginTop="16dp"
        android:layout_gravity="end"/>

</LinearLayout>

2. Build a Service to Manage the Floating Popup

Floating windows need to run independently of your Activity, so we'll use a Service to handle showing/hiding the popup. Create a new class FloatingPopupService.java:

public class FloatingPopupService extends Service {
    private WindowManager windowManager;
    private View floatingView;

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

    @Override
    public void onCreate() {
        super.onCreate();
        // Initialize WindowManager
        windowManager = (WindowManager) getSystemService(WINDOW_SERVICE);

        // Inflate the popup layout
        floatingView = LayoutInflater.from(this).inflate(R.layout.layout_floating_popup, null);

        // Configure WindowManager LayoutParams
        WindowManager.LayoutParams params = new WindowManager.LayoutParams(
                WindowManager.LayoutParams.WRAP_CONTENT,
                WindowManager.LayoutParams.WRAP_CONTENT,
                // For Android 8.0+ use TYPE_APPLICATION_OVERLAY
                Build.VERSION.SDK_INT >= Build.VERSION_CODES.O ?
                        WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY :
                        WindowManager.LayoutParams.TYPE_PHONE,
                // Flags to make the popup non-focusable (so user can interact with apps behind it)
                WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
                PixelFormat.TRANSLUCENT
        );

        // Set popup position (center screen here)
        params.gravity = Gravity.CENTER;

        // Add the view to WindowManager
        windowManager.addView(floatingView, params);

        // Handle close button click
        Button closeBtn = floatingView.findViewById(R.id.btn_close);
        closeBtn.setOnClickListener(v -> {
            // Remove the popup and stop the service
            stopSelf();
        });
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        // Clean up the view when service stops
        if (floatingView != null) {
            windowManager.removeView(floatingView);
        }
    }
}

Don't forget to add this service to your AndroidManifest.xml:

<service
    android:name=".FloatingPopupService"
    android:exported="false"/>

3. Implement App Usage Monitoring

To track continuous app usage, we'll use UsageStatsManager. First, add the required permission to your manifest:

<uses-permission android:name="android.permission.PACKAGE_USAGE_STATS"
    tools:ignore="ProtectedPermissions"/>

3.1 Request Usage Stats Permission

Add this method to your ExcessiveUsageActivity to request the usage stats permission:

private void requestUsageStatsPermission() {
    Intent intent = new Intent(Settings.ACTION_USAGE_ACCESS_SETTINGS);
    startActivity(intent);
}

3.2 Monitor App Usage Duration

Add a helper method to check if the user has been using the target app for X minutes. We'll use a Handler to check periodically:

private Handler usageMonitorHandler = new Handler();
private Runnable usageMonitorRunnable = new Runnable() {
    @Override
    public void run() {
        String targetPackage = "com.target.app.package"; // Replace with your target app's package
        long thresholdMillis = 10 * 60 * 1000; // 10 minutes (adjust as needed)

        if (isAppUsedContinuously(targetPackage, thresholdMillis)) {
            // Show the floating popup
            startService(new Intent(ExcessiveUsageActivity.this, FloatingPopupService.class));
            // Stop monitoring once popup is shown (or adjust to repeat)
            usageMonitorHandler.removeCallbacks(this);
        } else {
            // Check again in 1 minute
            usageMonitorHandler.postDelayed(this, 60 * 1000);
        }
    }
};

private boolean isAppUsedContinuously(String targetPackage, long thresholdMillis) {
    UsageStatsManager usageStatsManager = (UsageStatsManager) getSystemService(USAGE_STATS_SERVICE);
    long endTime = System.currentTimeMillis();
    long startTime = endTime - thresholdMillis;

    // Get usage stats for the time range
    List<UsageStats> usageStatsList = usageStatsManager.queryUsageStats(
            UsageStatsManager.INTERVAL_DAILY,
            startTime,
            endTime
    );

    if (usageStatsList != null) {
        for (UsageStats stats : usageStatsList) {
            if (stats.getPackageName().equals(targetPackage)) {
                // Check if total time in foreground meets the threshold
                return stats.getTotalTimeInForeground() >= thresholdMillis;
            }
        }
    }
    return false;
}

4. Fix Your Existing Permission Logic

You had a small logic error in your onCreate method—you were requesting overlay permission only if you already had it. Let's fix that:

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_excessiveusageactivity);
    result = findViewById(R.id.result); // Move this here to avoid null pointer

    // Request overlay permission if not granted
    if (!Settings.canDrawOverlays(this)) {
        Intent requestOverlayPermissionIntent = new Intent(
                Settings.ACTION_MANAGE_OVERLAY_PERMISSION,
                Uri.parse("package:" + getPackageName())
        );
        startActivityForResult(requestOverlayPermissionIntent, REQUEST_OVERLAY_PERMISSION);
        result.setText("Requesting overlay permission...");
    } else {
        // Check if we have usage stats permission
        if (!hasUsageStatsPermission()) {
            requestUsageStatsPermission();
            result.setText("Requesting usage stats permission...");
        } else {
            // Start monitoring app usage
            usageMonitorHandler.post(usageMonitorRunnable);
            result.setText("Monitoring app usage...");
        }
    }
}

// Helper method to check if usage stats permission is granted
private boolean hasUsageStatsPermission() {
    AppOpsManager appOps = (AppOpsManager) getSystemService(Context.APP_OPS_SERVICE);
    int mode = appOps.checkOpNoThrow(
            AppOpsManager.OPSTR_GET_USAGE_STATS,
            android.os.Process.myUid(),
            getPackageName()
    );
    return mode == AppOpsManager.MODE_ALLOWED;
}

5. Test and Polish

  • Test Permissions: Make sure you grant both overlay and usage stats permissions when prompted.
  • Adjust Threshold: Change thresholdMillis to your desired duration (e.g., 30 minutes = 30 * 60 * 1000).
  • Foreground Service: For Android 8.0+, if your service runs in the background for long periods, you'll need to make it a Foreground Service with a notification to avoid being killed.
  • Popup Customization: Tweak the layout_floating_popup.xml to match your app's design (colors, icons, etc.).

内容的提问来源于stack exchange,提问作者confusedcoder

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.14 09:14:13