目录

Android强制横屏

目录

Android强制横屏

https://i-operation.csdnimg.cn/images/cf31225e169b4512917b2e77694eb0a2.pngAndroid强制横屏实现方案

原理是在其他应用上方创建一个超级小的layout来带动它横屏

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" />
    <uses-permission android:name="android.permission.FOREGROUND_SERVICE"
        tools:ignore="ForegroundServicesPolicy" />
    <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.Intop">
        <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>
        <service
            android:name=".OverlayService"
            android:enabled="true"
            android:exported="false" />
        <receiver
            android:name=".StopServiceReceiver"
            android:enabled="true"
            android:exported="false" />
    </application>

</manifest>

MainActivity.java

package com.acplt.intop;

import androidx.appcompat.app.AppCompatActivity;
import androidx.core.content.ContextCompat;

import android.content.Intent;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.provider.Settings;
import android.widget.Toast;
import android.Manifest;

public class MainActivity extends AppCompatActivity {

    private static final int REQUEST_CODE_OVERLAY_PERMISSION = 100;

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

        // 一启动就检查并请求权限
        checkOverlayPermission();
    }

    @Override
    protected void onResume() {
        super.onResume();
        // 从设置页面返回时,再次检查
        if (Settings.canDrawOverlays(this)) {
            startOverlayService();
            finish(); // 权限到手,启动服务后直接关闭Activity
        }
    }

    private void checkOverlayPermission() {
        if (Settings.canDrawOverlays(this)) {
            // 已经有权限,直接启动服务
            startOverlayService();
            finish();
        } else {
            // 没有权限,引导用户去开启
            requestOverlayPermission();
        }
    }

    private void requestOverlayPermission() {
        Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION,
                Uri.parse("package:" + getPackageName()));
        startActivityForResult(intent, REQUEST_CODE_OVERLAY_PERMISSION);
    }

    private void startOverlayService() {
        // 启动服务
        Intent serviceIntent = new Intent(this, OverlayService.class);
        ContextCompat.startForegroundService(this, serviceIntent);
        Toast.makeText(this, "强制横屏已开启", Toast.LENGTH_SHORT).show();
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        if (requestCode == REQUEST_CODE_OVERLAY_PERMISSION) {
            // 不管用户是否授权,onResume里都会再次检查,所以这里不需要额外处理
        }
    }
}

OverlayService.java

package com.acplt.intop;

import android.annotation.SuppressLint;
import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.PendingIntent;
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.View;
import android.view.WindowManager;
import android.widget.Toast;

import androidx.core.app.NotificationCompat;

public class OverlayService extends Service {

    private View overlayView;
    private WindowManager windowManager;

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

    @SuppressLint("ForegroundServiceType")
    @Override
    public void onCreate() {
        super.onCreate();
        createOverlayView();
        startForeground(1, createNotification()); // 必须将服务设为前台服务
    }

    private void createOverlayView() {
        // 1. 获取 WindowManager
        windowManager = (WindowManager) getSystemService(WINDOW_SERVICE);

        // 2. 设置 WindowManager.LayoutParams 参数
        WindowManager.LayoutParams params = new WindowManager.LayoutParams(
                1, // 宽度:1像素,几乎看不见
                1, // 高度:1像素,几乎看不见
                // 系统窗口类型,关键!
                Build.VERSION.SDK_INT >= Build.VERSION_CODES.O ?
                        WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY :
                        WindowManager.LayoutParams.TYPE_SYSTEM_ALERT,
                // 标志:不可聚焦、不可触摸、保持在锁屏之上
                WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
                        | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE
                        | WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED,
                PixelFormat.TRANSLUCENT // 透明
        );

        // 3. 设置窗口位置和重力
        params.gravity = Gravity.START | Gravity.TOP; // 左上角

        // 4. !!!最关键的参数:强制此窗口为横屏!!!
        params.screenOrientation = android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;

        // 5. 创建一个极小的View
        overlayView = LayoutInflater.from(this).inflate(R.layout.overlay_layout, null);

        // 6. 将View添加到WindowManager
        try {
            windowManager.addView(overlayView, params);
        } catch (Exception e) {
            Toast.makeText(this, "无法创建悬浮窗,请确保权限已开启", Toast.LENGTH_LONG).show();
            stopSelf();
        }
    }

    private Notification createNotification() {
        // 创建停止服务的Intent
        Intent stopIntent = new Intent(this, StopServiceReceiver.class);
        PendingIntent stopPendingIntent = PendingIntent.getBroadcast(
                this,
                0,
                stopIntent,
                PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE
        );

        // 创建一个前台服务通知
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            // 创建通知渠道(Android 8.0+要求)
            NotificationChannel channel = new NotificationChannel(
                    "landscape_channel",
                    "强制横屏服务",
                    NotificationManager.IMPORTANCE_LOW
            );
            NotificationManager manager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
            manager.createNotificationChannel(channel);
        }

        NotificationCompat.Builder builder = new NotificationCompat.Builder(this, "landscape_channel")
                .setContentTitle("强制横屏服务运行中")
                .setContentText("点击停止服务")
                .setSmallIcon(R.mipmap.ic_launcher) // 确保有这个资源
                .setPriority(NotificationCompat.PRIORITY_LOW)
                .setContentIntent(stopPendingIntent) // 设置点击事件
                .setAutoCancel(true); // 点击后自动取消通知

        return builder.build();
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        // 服务被销毁时,移除悬浮窗
        if (overlayView != null && windowManager != null) {
            windowManager.removeView(overlayView);
        }
        Toast.makeText(this, "强制横屏已关闭", Toast.LENGTH_SHORT).show();
    }
}

StopServiceReceiver.java

package com.acplt.intop;

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.widget.Toast;

public class StopServiceReceiver extends BroadcastReceiver {

    @Override
    public void onReceive(Context context, Intent intent) {
        // 停止 OverlayService
        Intent serviceIntent = new Intent(context, OverlayService.class);
        context.stopService(serviceIntent);

        // 可选:显示提示消息
        Toast.makeText(context, "强制横屏已关闭", Toast.LENGTH_SHORT).show();
    }
}

overlay_layout.xml

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

    <!-- 这个布局基本是空的,只是作为一个占位符 -->

</LinearLayout>