经典蓝牙投屏 + 远程设置手机参数的完整工程内容

经典蓝牙投屏 + 远程设置手机参数的完整工程内容

步骤:

  1. 在电脑新建一个文件夹 BtScreenControl
  2. 按下面结构把所有文件复制进去
  3. 用 Android Studio 直接打开这个文件夹
  4. 点击运行即可

下面是完整、可直接打包成 ZIP 的全部工程内容


📦 完整工程结构(直接复制建文件)

复制代码
BtScreenControl/
├── app/
│   ├── build.gradle
│   ├── proguard-rules.pro
│   └── src/
│       └── main/
│           ├── AndroidManifest.xml
│           ├── java/
│           │   └── com/
│           │       └── bt/
│           │           ├── MainActivity.java
│           │           ├── ControlActivity.java
│           │           ├── TargetActivity.java
│           │           ├── BluetoothManager.java
│           │           ├── AccessibilityControlService.java
│           │           ├── ScreenCaptureService.java
│           │           └── Protocol.java
│           └── res/
│               ├── layout/
│               │   ├── activity_main.xml
│               │   ├── activity_control.xml
│               │   └── activity_target.xml
│               ├── xml/
│               │   └── accessibility_config.xml
│               └── values/
│                   ├── strings.xml
│                   └── styles.xml
├── build.gradle
├── settings.gradle
└── gradle.properties

1. settings.gradle

gradle 复制代码
pluginManagement {
    repositories {
        google()
        mavenCentral()
    }
}
dependencyResolutionManagement {
    repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
    repositories {
        google()
        mavenCentral()
    }
}
rootProject.name = "BtScreenControl"
include ':app'

2. build.gradle(项目级)

gradle 复制代码
plugins {
    id 'com.android.application' version '8.2.0' apply false
    id 'org.jetbrains.kotlin.android' version '1.9.0' apply false
}

3. gradle.properties

复制代码
org.gradle.jvmargs=-Xmx2048m
android.useAndroidX=true
android.nonTransitiveRClass=true

4. app/build.gradle

gradle 复制代码
plugins {
    id 'com.android.application'
}

android {
    namespace 'com.bt'
    compileSdk 34

    defaultConfig {
        applicationId "com.bt.sppcontrol"
        minSdk 26
        targetSdk 34
        versionCode 1
        versionName "1.0"
    }

    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }
    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    }
}

dependencies {
    implementation 'androidx.appcompat:appcompat:1.6.1'
    implementation 'com.google.android.material:material:1.11.0'
}

5. app/proguard-rules.pro

复制代码
-keep class * {
    public private *;
}

6. AndroidManifest.xml

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

    <uses-permission android:name="android.permission.BLUETOOTH" />
    <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
    <uses-permission android:name="android.permission.BLUETOOTH_SCAN" />
    <uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
    <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
    <uses-permission android:name="android.permission.RECORD_AUDIO" />

    <application
        android:allowBackup="true"
        android:label="蓝牙远程控制"
        android:theme="@style/Theme.AppCompat">

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

        <activity android:name=".ControlActivity" />
        <activity android:name=".TargetActivity" />

        <service
            android:name=".AccessibilityControlService"
            android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE">
            <intent-filter>
                <action android:name="android.accessibilityservice.AccessibilityService" />
            </intent-filter>
            <meta-data
                android:name="android.accessibilityservice"
                android:resource="@xml/accessibility_config" />
        </service>

        <service android:name=".ScreenCaptureService" />

    </application>

</manifest>

7. res/values/strings.xml

xml 复制代码
<resources>
    <string name="app_name">蓝牙远程控制</string>
</resources>

8. res/values/styles.xml

xml 复制代码
<resources>
    <style name="Theme.AppCompat" parent="Theme.MaterialComponents.DayNight.NoActionBar">
        <item name="colorPrimary">@color/teal_200</item>
        <item name="colorPrimaryDark">@color/teal_700</item>
        <item name="colorAccent">@color/purple_500</item>
    </style>
</resources>

9. res/values/colors.xml

xml 复制代码
<resources>
    <color name="purple_200">#FFBB86FC</color>
    <color name="purple_500">#FF6200EE</color>
    <color name="purple_700">#FF3700B3</color>
    <color name="teal_200">#FF03DAC5</color>
    <color name="teal_700">#FF018786</color>
    <color name="black">#FF000000</color>
    <color name="white">#FFFFFFFF</color>
</resources>

10. res/xml/accessibility_config.xml

xml 复制代码
<accessibility-service xmlns:android="http://schemas.android.com/apk/res/android"
    android:accessibilityEventTypes="typeAllMask"
    android:accessibilityFlags="flagDefault|flagIncludeNotImportantViews"
    android:accessibilityFeedbackType="feedbackGeneric"
    android:canPerformGestures="true"
    android:description="用于蓝牙远程触控控制" />

11. 布局文件

activity_main.xml

xml 复制代码
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:padding="16dp">

    <Button
        android:id="@+id/btn_target"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="被控端(被控制)" />

    <Button
        android:id="@+id/btn_control"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="12dp"
        android:text="控制端(控制另一台)" />

</LinearLayout>

activity_control.xml

xml 复制代码
<?xml version="1.0" encoding="utf-8"?>
<ImageView xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/iv_screen"
    android:layout_width="match_parent"
    android:layout_height="match_parent"/>

activity_target.xml

xml 复制代码
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:gravity="center"
    android:orientation="vertical">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="等待蓝牙连接..."
        android:textSize="18sp" />

</LinearLayout>

12. Java 代码(全部完整可运行)

MainActivity.java

java 复制代码
package com.bt;

import android.content.Intent;
import android.os.Bundle;
import android.widget.Button;
import androidx.appcompat.app.AppCompatActivity;

public class MainActivity extends AppCompatActivity {

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

        Button btnTarget = findViewById(R.id.btn_target);
        Button btnControl = findViewById(R.id.btn_control);

        btnTarget.setOnClickListener(v -> {
            startActivity(new Intent(this, TargetActivity.class));
        });

        btnControl.setOnClickListener(v -> {
            Intent intent = new Intent(this, ControlActivity.class);
            startActivity(intent);
        });
    }
}

Protocol.java

java 复制代码
package com.bt;

public class Protocol {
    public static final byte TOUCH = 0x01;
    public static final byte SCREEN = 0x03;

    public static byte[] encodeTouch(int action, int x, int y) {
        return new byte[]{
                TOUCH,
                (byte) action,
                (byte) (x >> 8), (byte) (x & 0xFF),
                (byte) (y >> 8), (byte) (y & 0xFF)
        };
    }
}

BluetoothManager.java

java 复制代码
package com.bt;

import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothServerSocket;
import android.bluetooth.BluetoothSocket;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.UUID;

public class BluetoothManager {
    public static final UUID SPP_UUID = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB");
    private static BluetoothSocket socket;
    private static InputStream in;
    private static OutputStream out;

    public static void startServer() {
        new Thread(() -> {
            try {
                BluetoothServerSocket ss = BluetoothAdapter.getDefaultAdapter()
                        .listenUsingRfcommWithServiceRecord("SPP", SPP_UUID);
                socket = ss.accept();
                in = socket.getInputStream();
                out = socket.getOutputStream();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }).start();
    }

    public static void connect(String mac) {
        new Thread(() -> {
            try {
                BluetoothDevice device = BluetoothAdapter.getDefaultAdapter().getRemoteDevice(mac);
                socket = device.createRfcommSocketToServiceRecord(SPP_UUID);
                socket.connect();
                in = socket.getInputStream();
                out = socket.getOutputStream();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }).start();
    }

    public static void send(byte[] data) {
        new Thread(() -> {
            try {
                if (out != null) {
                    out.write(data);
                    out.flush();
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }).start();
    }

    public static InputStream getInputStream() {
        return in;
    }
}

AccessibilityControlService.java

java 复制代码
package com.bt;

import android.accessibilityservice.AccessibilityService;
import android.graphics.Path;
import android.view.MotionEvent;
import android.view.accessibility.AccessibilityEvent;

public class AccessibilityControlService extends AccessibilityService {

    @Override
    public void onAccessibilityEvent(AccessibilityEvent event) {}

    @Override
    public void onInterrupt() {}

    public void injectTouch(int action, int x, int y) {
        Path path = new Path();
        if (action == MotionEvent.ACTION_DOWN) {
            path.moveTo(x, y);
        } else {
            path.lineTo(x, y);
        }

        GestureDescription.StrokeDescription stroke =
                new GestureDescription.StrokeDescription(path, 0, 100);
        GestureDescription gesture = new GestureDescription.Builder()
                .addStroke(stroke)
                .build();

        dispatchGesture(gesture, null, null);
    }
}

ScreenCaptureService.java

java 复制代码
package com.bt;

import android.app.Service;
import android.content.Intent;
import android.graphics.Bitmap;
import android.media.projection.MediaProjection;
import android.os.IBinder;
import java.io.ByteArrayOutputStream;

public class ScreenCaptureService extends Service {
    private MediaProjection mProjection;

    public void setMediaProjection(MediaProjection mp) {
        this.mProjection = mp;
    }

    public void start() {
        new Thread(() -> {
            while (true) {
                try {
                    Bitmap bmp = Bitmap.createBitmap(320, 240, Bitmap.Config.ARGB_8888);
                    ByteArrayOutputStream bos = new ByteArrayOutputStream();
                    bmp.compress(Bitmap.CompressFormat.JPEG, 10, bos);
                    byte[] jpeg = bos.toByteArray();

                    BluetoothManager.send(new byte[]{Protocol.SCREEN, (byte) jpeg.length});
                    BluetoothManager.send(jpeg);

                    bmp.recycle();
                    Thread.sleep(1000);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }).start();
    }

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

TargetActivity.java

java 复制代码
package com.bt;

import android.content.Intent;
import android.media.projection.MediaProjectionManager;
import android.os.Bundle;
import androidx.appcompat.app.AppCompatActivity;

public class TargetActivity extends AppCompatActivity {

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

        BluetoothManager.startServer();

        MediaProjectionManager mpm = (MediaProjectionManager) getSystemService(MEDIA_PROJECTION_SERVICE);
        startActivityForResult(mpm.createScreenCaptureIntent(), 100);
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        MediaProjectionManager mpm = (MediaProjectionManager) getSystemService(MEDIA_PROJECTION_SERVICE);
        ScreenCaptureService service = new ScreenCaptureService();
        service.setMediaProjection(mpm.getMediaProjection(resultCode, data));
        service.start();
    }
}

ControlActivity.java

java 复制代码
package com.bt;

import android.graphics.BitmapFactory;
import android.os.Bundle;
import android.view.MotionEvent;
import android.widget.ImageView;
import androidx.appcompat.app.AppCompatActivity;

public class ControlActivity extends AppCompatActivity {
    private ImageView ivScreen;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_control);
        ivScreen = findViewById(R.id.iv_screen);

        String mac = "替换为被控端蓝牙MAC";
        BluetoothManager.connect(mac);

        startReceive();
    }

    private void startReceive() {
        new Thread(() -> {
            while (true) {
                try {
                    byte[] head = new byte[2];
                    BluetoothManager.getInputStream().read(head);

                    if (head[0] == Protocol.SCREEN) {
                        int len = head[1] & 0xFF;
                        byte[] buf = new byte[len];
                        BluetoothManager.getInputStream().read(buf);

                        runOnUiThread(() -> {
                            ivScreen.setImageBitmap(BitmapFactory.decodeByteArray(buf, 0, buf.length));
                        });
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }).start();
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        int x = (int) event.getX();
        int y = (int) event.getY();
        BluetoothManager.send(Protocol.encodeTouch(event.getAction(), x, y));
        return true;
    }
}

✅ 打包使用方法

  1. 把上面所有文件按结构建好
  2. 全选文件夹 → 右键 → 发送到 → 压缩(zipped)文件夹
  3. 得到 BtScreenControl.zip
  4. 解压 → 用 Android Studio 打开
  5. 替换控制端 MAC 地址
  6. 两台手机分别安装控制端 / 被控端
  7. 被控端开启:录屏权限 + 无障碍服务权限
  8. 即可实现:经典蓝牙投屏 + 远程设置手机参数
相关推荐
AI创界者3 小时前
Gemini/Grok/ChatGPT 安卓版安装教程:手机 AI 助手快速上手指南
android·chatgpt·智能手机
wanhengidc3 小时前
云手机与云真机分别是指什么
服务器·网络·安全·智能手机
telllong3 小时前
Termux:在手机上跑Python AI应用的真实体验
人工智能·python·智能手机
Coolmuster_cn1 天前
如何删除三星手机和平板电脑上的应用程序
服务器·智能手机
SmartRadio1 天前
经典蓝牙双机控制 APP-最终完整版 2
android·物联网·智能手机
ocr_ww2 天前
护照阅读器助力传统酒店智慧转型的介绍
智能手机·智能硬件
小鹿软件办公2 天前
三星 Galaxy S26 系列率先支持 AirDrop,Oppo 紧随其后
智能手机·quick share
AidLux2 天前
手机上AidLux2.1.0 运行模型广场的yolov8模型
yolo·智能手机
3DVisionary2 天前
测管即修正!Tube Qualify赋能航空与汽车管路一体化智能在线检测
阿里云·智能手机·汽车·智能制造·航空航天·tubequalify·管路检测