├── .gitignore ├── Android屏幕共享-传输图片 └── AndroidScreenQuick │ ├── .gitignore │ ├── 1.jpg │ ├── app │ ├── .gitignore │ ├── build.gradle │ ├── proguard-rules.pro │ └── src │ │ ├── androidTest │ │ └── java │ │ │ └── com │ │ │ └── talon │ │ │ └── screen │ │ │ └── quick │ │ │ └── ExampleInstrumentedTest.java │ │ ├── main │ │ ├── AndroidManifest.xml │ │ ├── java │ │ │ └── com │ │ │ │ └── talon │ │ │ │ └── screen │ │ │ │ └── quick │ │ │ │ ├── Config.java │ │ │ │ ├── MainActivity.java │ │ │ │ ├── PlayActivity.java │ │ │ │ ├── network │ │ │ │ ├── MWebSocketClient.java │ │ │ │ └── MWebSocketServer.java │ │ │ │ └── util │ │ │ │ ├── BitmapUtils.java │ │ │ │ ├── IPUtils.java │ │ │ │ ├── LogWrapper.java │ │ │ │ ├── ScreenShotHelper.java │ │ │ │ ├── ScreenUtils.java │ │ │ │ └── ThreadPool.java │ │ └── res │ │ │ ├── drawable-v24 │ │ │ └── ic_launcher_foreground.xml │ │ │ ├── drawable │ │ │ └── ic_launcher_background.xml │ │ │ ├── layout │ │ │ ├── activity_main.xml │ │ │ └── activity_play.xml │ │ │ ├── mipmap-anydpi-v26 │ │ │ ├── ic_launcher.xml │ │ │ └── ic_launcher_round.xml │ │ │ ├── mipmap-hdpi │ │ │ ├── ic_launcher.png │ │ │ └── ic_launcher_round.png │ │ │ ├── mipmap-mdpi │ │ │ ├── ic_launcher.png │ │ │ └── ic_launcher_round.png │ │ │ ├── mipmap-xhdpi │ │ │ ├── ic_launcher.png │ │ │ └── ic_launcher_round.png │ │ │ ├── mipmap-xxhdpi │ │ │ ├── ic_launcher.png │ │ │ └── ic_launcher_round.png │ │ │ ├── mipmap-xxxhdpi │ │ │ ├── ic_launcher.png │ │ │ └── ic_launcher_round.png │ │ │ └── values │ │ │ ├── colors.xml │ │ │ ├── strings.xml │ │ │ └── styles.xml │ │ └── test │ │ └── java │ │ └── com │ │ └── talon │ │ └── screen │ │ └── quick │ │ └── ExampleUnitTest.java │ ├── build.gradle │ ├── gradle.properties │ ├── gradle │ └── wrapper │ │ ├── gradle-wrapper.jar │ │ └── gradle-wrapper.properties │ ├── gradlew │ ├── gradlew.bat │ ├── readme.md │ └── settings.gradle ├── README.md ├── 两个类实现录屏Demo └── ScreenRecorderDemo │ ├── .gitignore │ ├── app │ ├── .gitignore │ ├── build.gradle │ ├── proguard-rules.pro │ └── src │ │ ├── androidTest │ │ └── java │ │ │ └── com │ │ │ └── screen │ │ │ └── recorder │ │ │ └── demo │ │ │ └── ExampleInstrumentedTest.java │ │ ├── main │ │ ├── AndroidManifest.xml │ │ ├── java │ │ │ └── com │ │ │ │ └── screen │ │ │ │ └── recorder │ │ │ │ └── demo │ │ │ │ ├── MainActivity.java │ │ │ │ ├── service │ │ │ │ └── ScreenRecordService.java │ │ │ │ └── utils │ │ │ │ └── ScreenUtils.java │ │ └── res │ │ │ ├── drawable-v24 │ │ │ └── ic_launcher_foreground.xml │ │ │ ├── drawable │ │ │ └── ic_launcher_background.xml │ │ │ ├── layout │ │ │ └── activity_main.xml │ │ │ ├── mipmap-anydpi-v26 │ │ │ ├── ic_launcher.xml │ │ │ └── ic_launcher_round.xml │ │ │ ├── mipmap-hdpi │ │ │ ├── ic_launcher.png │ │ │ └── ic_launcher_round.png │ │ │ ├── mipmap-mdpi │ │ │ ├── ic_launcher.png │ │ │ └── ic_launcher_round.png │ │ │ ├── mipmap-xhdpi │ │ │ ├── ic_launcher.png │ │ │ └── ic_launcher_round.png │ │ │ ├── mipmap-xxhdpi │ │ │ ├── ic_launcher.png │ │ │ └── ic_launcher_round.png │ │ │ ├── mipmap-xxxhdpi │ │ │ ├── ic_launcher.png │ │ │ └── ic_launcher_round.png │ │ │ └── values │ │ │ ├── colors.xml │ │ │ ├── strings.xml │ │ │ └── styles.xml │ │ └── test │ │ └── java │ │ └── com │ │ └── screen │ │ └── recorder │ │ └── demo │ │ └── ExampleUnitTest.java │ ├── build.gradle │ ├── gradle.properties │ ├── gradle │ └── wrapper │ │ ├── gradle-wrapper.jar │ │ └── gradle-wrapper.properties │ ├── gradlew │ ├── gradlew.bat │ └── settings.gradle ├── 无声音乐mp3 └── no_notice.mp3 └── 无声音乐保活Demo └── MyApplication ├── .gitignore ├── app ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── pro │ │ └── context │ │ └── com │ │ └── myapplication │ │ └── ExampleInstrumentedTest.java │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── pro │ │ │ └── context │ │ │ └── com │ │ │ └── myapplication │ │ │ ├── MainActivity.java │ │ │ └── MusicService.java │ └── res │ │ ├── drawable-v24 │ │ └── ic_launcher_foreground.xml │ │ ├── drawable │ │ └── ic_launcher_background.xml │ │ ├── layout │ │ └── activity_main.xml │ │ ├── mipmap-anydpi-v26 │ │ ├── ic_launcher.xml │ │ └── ic_launcher_round.xml │ │ ├── mipmap-hdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-mdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxxhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── raw │ │ └── no_notice.mp3 │ │ └── values │ │ ├── colors.xml │ │ ├── strings.xml │ │ └── styles.xml │ └── test │ └── java │ └── pro │ └── context │ └── com │ └── myapplication │ └── ExampleUnitTest.java ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | # Built application files 2 | *.apk 3 | *.ap_ 4 | 5 | # Files for the ART/Dalvik VM 6 | *.dex 7 | 8 | # Java class files 9 | *.class 10 | 11 | # Generated files 12 | bin/ 13 | gen/ 14 | out/ 15 | 16 | # Gradle files 17 | .gradle/ 18 | build/ 19 | 20 | # Local configuration file (sdk path, etc) 21 | local.properties 22 | 23 | # Proguard folder generated by Eclipse 24 | proguard/ 25 | 26 | # Log Files 27 | *.log 28 | 29 | # Android Studio Navigation editor temp files 30 | .navigation/ 31 | 32 | # Android Studio captures folder 33 | captures/ 34 | 35 | # Intellij 36 | *.iml 37 | .idea/ 38 | .idea/workspace.xml 39 | 40 | # Keystore files 41 | # *.jks 42 | 43 | .DS_Store -------------------------------------------------------------------------------- /Android屏幕共享-传输图片/AndroidScreenQuick/.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/caches 5 | /.idea/libraries 6 | /.idea/modules.xml 7 | /.idea/workspace.xml 8 | /.idea/navEditor.xml 9 | /.idea/assetWizardSettings.xml 10 | .DS_Store 11 | /build 12 | /captures 13 | .externalNativeBuild 14 | .cxx 15 | -------------------------------------------------------------------------------- /Android屏幕共享-传输图片/AndroidScreenQuick/1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tangfuOK/AndroidTangFu/aa84766f0513485c7d2c348b0ab4f55ddc534054/Android屏幕共享-传输图片/AndroidScreenQuick/1.jpg -------------------------------------------------------------------------------- /Android屏幕共享-传输图片/AndroidScreenQuick/app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /Android屏幕共享-传输图片/AndroidScreenQuick/app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion 28 5 | buildToolsVersion "28.0.3" 6 | 7 | defaultConfig { 8 | applicationId "com.talon.screen.quick" 9 | minSdkVersion 21 10 | targetSdkVersion 28 11 | versionCode 1 12 | versionName "1.0" 13 | 14 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 15 | } 16 | 17 | buildTypes { 18 | release { 19 | minifyEnabled false 20 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' 21 | } 22 | } 23 | 24 | } 25 | 26 | dependencies { 27 | implementation fileTree(dir: 'libs', include: ['*.jar']) 28 | 29 | implementation 'com.android.support:appcompat-v7:28.0.0' 30 | implementation 'com.android.support.constraint:constraint-layout:1.1.3' 31 | testImplementation 'junit:junit:4.12' 32 | androidTestImplementation 'com.android.support.test:runner:1.0.2' 33 | androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2' 34 | 35 | // java websocket https://mvnrepository.com/artifact/org.java-websocket/Java-WebSocket 36 | implementation "org.java-websocket:Java-WebSocket:1.3.6" 37 | } 38 | -------------------------------------------------------------------------------- /Android屏幕共享-传输图片/AndroidScreenQuick/app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile 22 | -------------------------------------------------------------------------------- /Android屏幕共享-传输图片/AndroidScreenQuick/app/src/androidTest/java/com/talon/screen/quick/ExampleInstrumentedTest.java: -------------------------------------------------------------------------------- 1 | package com.talon.screen.quick; 2 | 3 | import android.content.Context; 4 | import android.support.test.InstrumentationRegistry; 5 | import android.support.test.runner.AndroidJUnit4; 6 | 7 | import org.junit.Test; 8 | import org.junit.runner.RunWith; 9 | 10 | import static org.junit.Assert.*; 11 | 12 | /** 13 | * Instrumented test, which will execute on an Android device. 14 | * 15 | * @see Testing documentation 16 | */ 17 | @RunWith(AndroidJUnit4.class) 18 | public class ExampleInstrumentedTest { 19 | @Test 20 | public void useAppContext() { 21 | // Context of the app under test. 22 | Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext(); 23 | 24 | assertEquals("com.talon.screen.quick", appContext.getPackageName()); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /Android屏幕共享-传输图片/AndroidScreenQuick/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /Android屏幕共享-传输图片/AndroidScreenQuick/app/src/main/java/com/talon/screen/quick/Config.java: -------------------------------------------------------------------------------- 1 | package com.talon.screen.quick; 2 | 3 | public class Config { 4 | 5 | public static float IMAGE_SCALE = 0.4f; // 设置缩放比例0.4 比较适合 (在ScreenShotHelper中设置) 6 | 7 | public static final String ANDROID_SERVER_HOST = "0.0.0.0"; 8 | public static final int ANDROID_SERVER_PORT = 8888; 9 | 10 | public static final int WINDOWS_SERVER_PORT = 9998; 11 | 12 | } 13 | -------------------------------------------------------------------------------- /Android屏幕共享-传输图片/AndroidScreenQuick/app/src/main/java/com/talon/screen/quick/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.talon.screen.quick; 2 | 3 | import android.content.Context; 4 | import android.content.DialogInterface; 5 | import android.content.Intent; 6 | import android.graphics.Bitmap; 7 | import android.media.projection.MediaProjectionManager; 8 | import android.support.v7.app.AlertDialog; 9 | import android.support.v7.app.AppCompatActivity; 10 | import android.os.Bundle; 11 | import android.text.TextUtils; 12 | import android.view.View; 13 | import android.widget.EditText; 14 | import android.widget.TextView; 15 | import android.widget.Toast; 16 | 17 | import com.talon.screen.quick.network.MWebSocketServer; 18 | import com.talon.screen.quick.util.BitmapUtils; 19 | import com.talon.screen.quick.util.IPUtils; 20 | import com.talon.screen.quick.util.LogWrapper; 21 | import com.talon.screen.quick.util.ScreenShotHelper; 22 | import com.talon.screen.quick.util.ThreadPool; 23 | 24 | /** 25 | * @author by talon, Date on 2020/6/20. 26 | * note: 主界面 27 | */ 28 | public class MainActivity extends AppCompatActivity implements ScreenShotHelper.OnScreenShotListener { 29 | 30 | private final String TAG = "btn_quick"; 31 | private static final int REQUEST_MEDIA_PROJECTION = 100; 32 | 33 | private TextView tv_ip; 34 | 35 | private MWebSocketServer webSocketServer; 36 | private boolean socketIsStarted = false; 37 | 38 | @Override 39 | protected void onCreate(Bundle savedInstanceState) { 40 | super.onCreate(savedInstanceState); 41 | setContentView(R.layout.activity_main); 42 | tv_ip = findViewById(R.id.tv_ip); 43 | 44 | webSocketServer = new MWebSocketServer(Config.ANDROID_SERVER_PORT, new MWebSocketServer.CallBack() { 45 | @Override 46 | public void onServerStatus(boolean isStarted) { 47 | socketIsStarted = isStarted; 48 | } 49 | }); 50 | webSocketServer.start(); 51 | 52 | } 53 | 54 | @Override 55 | protected void onResume() { 56 | super.onResume(); 57 | tv_ip.setText(String.format("当前IP:%s", IPUtils.getIpAddressString())); 58 | } 59 | 60 | /** 61 | * 推送端:1. 开启服务 2. 申请截图权限 3. 传输数据 62 | * 63 | * @param view 64 | */ 65 | public void StartQuick(View view) { 66 | if (!socketIsStarted) 67 | Toast.makeText(this, "socket 服务启动异常!", Toast.LENGTH_SHORT).show(); 68 | else 69 | tryStartScreenShot(); 70 | } 71 | 72 | /** 73 | * 播放端:1. 输入IP 2. 接收到数据 3. 展示 74 | * 75 | * @param view 76 | */ 77 | public void Join(View view) { 78 | showEditDialog(); 79 | } 80 | 81 | @Override 82 | protected void onActivityResult(int requestCode, int resultCode, Intent data) { 83 | if (requestCode == REQUEST_MEDIA_PROJECTION && data != null) { 84 | if (resultCode == RESULT_OK) { 85 | // 截屏的回调 86 | ScreenShotHelper screenShotHelper = new ScreenShotHelper(this, resultCode, data, this); 87 | screenShotHelper.startScreenShot(); 88 | } else if (resultCode == RESULT_CANCELED) { 89 | LogWrapper.d(TAG, "用户取消"); 90 | } 91 | } 92 | } 93 | 94 | /** 95 | * 申请截屏权限 96 | */ 97 | private void tryStartScreenShot() { 98 | MediaProjectionManager mProjectionManager = (MediaProjectionManager) getSystemService(Context.MEDIA_PROJECTION_SERVICE); 99 | if (mProjectionManager != null) { 100 | startActivityForResult(mProjectionManager.createScreenCaptureIntent(), REQUEST_MEDIA_PROJECTION); 101 | } 102 | } 103 | 104 | @Override 105 | public void onShotFinish(Bitmap bitmap) { 106 | LogWrapper.d(TAG, "bitmap:" + bitmap.getWidth()); 107 | webSocketServer.sendBytes(BitmapUtils.getByteBitmap(bitmap)); 108 | } 109 | 110 | private void showEditDialog() { 111 | final EditText editText = new EditText(this); 112 | editText.setText("192.168.2.112"); 113 | AlertDialog.Builder builder = new AlertDialog.Builder(this); 114 | builder.setTitle("Server").setIcon(android.R.drawable.ic_dialog_info).setView(editText) 115 | .setNegativeButton("Cancel", null); 116 | builder.setPositiveButton("Ok", new DialogInterface.OnClickListener() { 117 | public void onClick(DialogInterface dialog, int which) { 118 | String host = editText.getText().toString(); 119 | if (!TextUtils.isEmpty(host)) { 120 | Intent intent = new Intent(MainActivity.this, PlayActivity.class); 121 | intent.putExtra("host", host); 122 | startActivity(intent); 123 | } 124 | } 125 | }); 126 | builder.show(); 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /Android屏幕共享-传输图片/AndroidScreenQuick/app/src/main/java/com/talon/screen/quick/PlayActivity.java: -------------------------------------------------------------------------------- 1 | package com.talon.screen.quick; 2 | 3 | import android.content.Intent; 4 | import android.graphics.Bitmap; 5 | import android.os.Handler; 6 | import android.os.Looper; 7 | import android.support.v7.app.AppCompatActivity; 8 | import android.os.Bundle; 9 | import android.widget.ImageView; 10 | import android.widget.Toast; 11 | 12 | import com.talon.screen.quick.network.MWebSocketClient; 13 | import com.talon.screen.quick.util.BitmapUtils; 14 | import com.talon.screen.quick.util.LogWrapper; 15 | import com.talon.screen.quick.util.ThreadPool; 16 | 17 | import java.net.URI; 18 | import java.net.URISyntaxException; 19 | 20 | /** 21 | * @author by talon, Date on 2020/7/19. 22 | * note: 播放视频 23 | */ 24 | public class PlayActivity extends AppCompatActivity implements MWebSocketClient.CallBack { 25 | 26 | private MWebSocketClient webSocketClient; 27 | private ImageView iv_image; 28 | 29 | Handler mainHandler = new Handler(Looper.getMainLooper()); 30 | 31 | @Override 32 | protected void onCreate(Bundle savedInstanceState) { 33 | super.onCreate(savedInstanceState); 34 | setContentView(R.layout.activity_play); 35 | iv_image = findViewById(R.id.iv_image); 36 | 37 | Intent intent = getIntent(); 38 | if (intent != null) { 39 | String host = intent.getStringExtra("host"); 40 | try { 41 | URI url = new URI("ws://" + host + ":" + Config.ANDROID_SERVER_PORT); 42 | webSocketClient = new MWebSocketClient(url, this); 43 | webSocketClient.setConnectionLostTimeout(5 * 1000); 44 | boolean flag = webSocketClient.connectBlocking(); 45 | Toast.makeText(PlayActivity.this, "链接状态:" + flag, Toast.LENGTH_LONG).show(); 46 | } catch (Exception e) { 47 | e.printStackTrace(); 48 | } 49 | 50 | } 51 | } 52 | 53 | 54 | @Override 55 | public void onClientStatus(boolean isConnected) { 56 | 57 | } 58 | 59 | @Override 60 | public void onBitmapReceived(final Bitmap bitmap) { 61 | mainHandler.post(new Runnable() { 62 | @Override 63 | public void run() { 64 | iv_image.setImageBitmap(bitmap); 65 | } 66 | }); 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /Android屏幕共享-传输图片/AndroidScreenQuick/app/src/main/java/com/talon/screen/quick/network/MWebSocketClient.java: -------------------------------------------------------------------------------- 1 | package com.talon.screen.quick.network; 2 | 3 | import android.graphics.Bitmap; 4 | 5 | import com.talon.screen.quick.util.BitmapUtils; 6 | import com.talon.screen.quick.util.LogWrapper; 7 | 8 | import org.java_websocket.client.WebSocketClient; 9 | import org.java_websocket.handshake.ServerHandshake; 10 | 11 | import java.net.SocketException; 12 | import java.net.URI; 13 | import java.nio.ByteBuffer; 14 | 15 | /** 16 | * @author by Talon, Date on 2020-04-13. 17 | * note: websocket 客户端 18 | */ 19 | public class MWebSocketClient extends WebSocketClient { 20 | 21 | private final String TAG = "MWebSocketClient"; 22 | 23 | private boolean mIsConnected = false; 24 | private CallBack mCallBack; 25 | 26 | public MWebSocketClient(URI serverUri, CallBack callBack) { 27 | super(serverUri); 28 | this.mCallBack = callBack; 29 | } 30 | 31 | @Override 32 | public void onOpen(ServerHandshake handshakeData) { 33 | LogWrapper.e(TAG, "onOpen"); 34 | updateClientStatus(true); 35 | 36 | try { 37 | getSocket().setReceiveBufferSize(5 * 1024 * 1024); 38 | } catch (SocketException e) { 39 | e.printStackTrace(); 40 | } 41 | 42 | 43 | } 44 | 45 | @Override 46 | public void onMessage(String message) { 47 | 48 | } 49 | 50 | @Override 51 | public void onMessage(ByteBuffer bytes) { 52 | byte[] buf = new byte[bytes.remaining()]; 53 | bytes.get(buf); 54 | if (mCallBack != null) 55 | mCallBack.onBitmapReceived(BitmapUtils.decodeImg(buf)); 56 | } 57 | 58 | @Override 59 | public void onClose(int code, String reason, boolean remote) { 60 | updateClientStatus(false); 61 | } 62 | 63 | @Override 64 | public void onError(Exception ex) { 65 | updateClientStatus(false); 66 | } 67 | 68 | private void updateClientStatus(boolean isConnected) { 69 | 70 | mIsConnected = isConnected; 71 | LogWrapper.d(TAG, "mIsConnected:" + mIsConnected); 72 | // 回调 73 | if (mCallBack != null) 74 | mCallBack.onClientStatus(isConnected); 75 | } 76 | 77 | public boolean isConnected() { 78 | LogWrapper.d(TAG, "mIsConnected:" + mIsConnected); 79 | return mIsConnected; 80 | } 81 | 82 | public interface CallBack { 83 | void onClientStatus(boolean isConnected); 84 | 85 | void onBitmapReceived(Bitmap bitmap); 86 | } 87 | 88 | 89 | } 90 | -------------------------------------------------------------------------------- /Android屏幕共享-传输图片/AndroidScreenQuick/app/src/main/java/com/talon/screen/quick/network/MWebSocketServer.java: -------------------------------------------------------------------------------- 1 | package com.talon.screen.quick.network; 2 | 3 | 4 | import com.talon.screen.quick.util.LogWrapper; 5 | 6 | import org.java_websocket.WebSocket; 7 | import org.java_websocket.handshake.ClientHandshake; 8 | import org.java_websocket.server.WebSocketServer; 9 | 10 | import java.net.InetSocketAddress; 11 | import java.util.ArrayList; 12 | import java.util.List; 13 | 14 | /** 15 | * @author by Talon, Date on 2020-04-13. 16 | * note: websocket 服务端 17 | */ 18 | public class MWebSocketServer extends WebSocketServer { 19 | 20 | private final String TAG = "MWebSocketServer"; 21 | 22 | private boolean mIsStarted = false; 23 | private CallBack mCallBack; 24 | 25 | private List mWebSocketList; 26 | 27 | public MWebSocketServer(int port, CallBack callBack) { 28 | super(new InetSocketAddress(port)); 29 | this.mCallBack = callBack; 30 | setReuseAddr(true); 31 | setConnectionLostTimeout(5 * 1000); 32 | } 33 | 34 | @Override 35 | public void onOpen(WebSocket webSocket, ClientHandshake handshake) { 36 | LogWrapper.d(TAG, "有用户链接"); 37 | if (mWebSocketList == null) 38 | mWebSocketList = new ArrayList<>(); 39 | mWebSocketList.add(webSocket); 40 | } 41 | 42 | @Override 43 | public void onClose(WebSocket conn, int code, String reason, boolean remote) { 44 | LogWrapper.d(TAG, "有用户离开"); 45 | } 46 | 47 | @Override 48 | public void onMessage(WebSocket conn, String message) { 49 | LogWrapper.e(TAG, "接收到消息:" + message); 50 | } 51 | 52 | @Override 53 | public void onError(WebSocket conn, Exception ex) { 54 | LogWrapper.e(TAG, "发生error:" + ex.toString()); 55 | } 56 | 57 | @Override 58 | public void onStart() { 59 | updateServerStatus(true); 60 | } 61 | 62 | /** 63 | * 停止服务器 64 | */ 65 | public void socketStop() { 66 | try { 67 | super.stop(100); 68 | updateServerStatus(false); 69 | } catch (InterruptedException e) { 70 | e.printStackTrace(); 71 | } 72 | } 73 | 74 | /** 75 | * 发送二进制 76 | * 77 | * @param bytes 78 | */ 79 | public void sendBytes(byte[] bytes) { 80 | if (mWebSocketList == null) return; 81 | for (WebSocket socket : mWebSocketList) 82 | socket.send(bytes); 83 | } 84 | 85 | private void updateServerStatus(boolean isStarted) { 86 | mIsStarted = isStarted; 87 | LogWrapper.e(TAG, "mIsStarted:" + mIsStarted); 88 | // 回调 89 | if (mCallBack != null) 90 | mCallBack.onServerStatus(isStarted); 91 | } 92 | 93 | public boolean isStarted() { 94 | LogWrapper.e(TAG, "mIsStarted:" + mIsStarted); 95 | return mIsStarted; 96 | } 97 | 98 | public interface CallBack { 99 | void onServerStatus(boolean isStarted); 100 | } 101 | 102 | } 103 | -------------------------------------------------------------------------------- /Android屏幕共享-传输图片/AndroidScreenQuick/app/src/main/java/com/talon/screen/quick/util/BitmapUtils.java: -------------------------------------------------------------------------------- 1 | package com.talon.screen.quick.util; 2 | 3 | import android.graphics.Bitmap; 4 | import android.graphics.BitmapFactory; 5 | 6 | import java.io.ByteArrayInputStream; 7 | import java.io.ByteArrayOutputStream; 8 | import java.io.IOException; 9 | import java.io.InputStream; 10 | import java.lang.ref.SoftReference; 11 | 12 | public class BitmapUtils { 13 | 14 | /** 15 | * 压缩图片 (压缩后不代表实际大小,有差异) 16 | * 17 | * @param bitmap 被压缩的图片 18 | * @param sizeLimit 大小限制 单位 k 19 | * @return 压缩后的图片 20 | */ 21 | public static Bitmap compressBitmap(Bitmap bitmap, long sizeLimit) { 22 | ByteArrayOutputStream baos = new ByteArrayOutputStream(); 23 | int quality = 90; 24 | bitmap.compress(Bitmap.CompressFormat.JPEG, quality, baos); 25 | // 循环判断压缩后图片是否超过限制大小 26 | while (baos.toByteArray().length / 1024 > sizeLimit) { 27 | baos.reset(); 28 | bitmap.compress(Bitmap.CompressFormat.JPEG, quality, baos); 29 | quality -= 10; 30 | } 31 | return BitmapFactory.decodeStream(new ByteArrayInputStream(baos.toByteArray()), null, null); 32 | } 33 | 34 | 35 | /** 36 | * Bitmap 转二进制 37 | * @param bitmap 38 | * @return 39 | */ 40 | public static byte[] getByteBitmap(Bitmap bitmap) { 41 | ByteArrayOutputStream baos = new ByteArrayOutputStream(); 42 | bitmap.compress(Bitmap.CompressFormat.JPEG, 70, baos); 43 | byte[] datas = baos.toByteArray(); 44 | return datas; 45 | } 46 | 47 | /** 48 | * 二进制数组 转 Bitmap 49 | * 50 | * @param data 51 | * @return 52 | */ 53 | public static Bitmap decodeImg(byte[] data) { 54 | Bitmap bitmap = null; 55 | 56 | byte[] imgByte = null; 57 | InputStream input = null; 58 | try { 59 | if (data == null) 60 | return null; 61 | imgByte = data; 62 | BitmapFactory.Options options = new BitmapFactory.Options(); 63 | options.inSampleSize = 1; 64 | input = new ByteArrayInputStream(imgByte); 65 | SoftReference softRef = new SoftReference(BitmapFactory.decodeStream(input, null, options)); 66 | bitmap = (Bitmap) softRef.get(); 67 | } catch (Exception e) { 68 | e.printStackTrace(); 69 | } finally { 70 | if (imgByte != null) { 71 | imgByte = null; 72 | } 73 | if (input != null) { 74 | try { 75 | input.close(); 76 | } catch (IOException e) { 77 | // TODO Auto-generated catch block 78 | e.printStackTrace(); 79 | } 80 | } 81 | } 82 | return bitmap; 83 | } 84 | 85 | } 86 | -------------------------------------------------------------------------------- /Android屏幕共享-传输图片/AndroidScreenQuick/app/src/main/java/com/talon/screen/quick/util/IPUtils.java: -------------------------------------------------------------------------------- 1 | package com.talon.screen.quick.util; 2 | 3 | import java.net.Inet4Address; 4 | import java.net.InetAddress; 5 | import java.net.NetworkInterface; 6 | import java.net.SocketException; 7 | import java.util.Enumeration; 8 | 9 | /** 10 | * @author by Talon, Date on 2020/6/20. 11 | * note: 12 | */ 13 | public class IPUtils { 14 | 15 | public static String getIpAddressString() { 16 | try { 17 | for (Enumeration enNetI = NetworkInterface 18 | .getNetworkInterfaces(); enNetI.hasMoreElements(); ) { 19 | NetworkInterface netI = enNetI.nextElement(); 20 | for (Enumeration enumIpAddr = netI 21 | .getInetAddresses(); enumIpAddr.hasMoreElements(); ) { 22 | InetAddress inetAddress = enumIpAddr.nextElement(); 23 | if (inetAddress instanceof Inet4Address && !inetAddress.isLoopbackAddress()) { 24 | return inetAddress.getHostAddress(); 25 | } 26 | } 27 | } 28 | } catch (SocketException e) { 29 | e.printStackTrace(); 30 | } 31 | return "0.0.0.0"; 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /Android屏幕共享-传输图片/AndroidScreenQuick/app/src/main/java/com/talon/screen/quick/util/LogWrapper.java: -------------------------------------------------------------------------------- 1 | package com.talon.screen.quick.util; 2 | 3 | import android.util.Log; 4 | 5 | 6 | /** 7 | * Created by talon on 2019/11/19 8 | * note: 日志类 9 | */ 10 | public class LogWrapper { 11 | 12 | /** 13 | * 调试开关,无论是自动还是手动都能很好的控制调试状态 14 | */ 15 | // public static final boolean DEBUG = BuildConfig.DEBUG; 16 | public static final boolean DEBUG = true; 17 | private static final String TAG = "LogWrapper"; 18 | 19 | public static void v(String tag, String msg) { 20 | if (DEBUG) 21 | Log.v(tag, msg); 22 | } 23 | 24 | public static void d( String msg) { 25 | if (DEBUG) 26 | Log.d(TAG, msg); 27 | } 28 | 29 | public static void d(String tag, String msg) { 30 | if (DEBUG) 31 | Log.d(tag, msg); 32 | } 33 | 34 | public static void i(String tag, String msg) { 35 | if (DEBUG) 36 | Log.i(tag, msg); 37 | } 38 | 39 | public static void w(String tag, String msg) { 40 | if (DEBUG) 41 | Log.w(tag, msg); 42 | } 43 | 44 | public static void e(String tag, String msg) { 45 | if (DEBUG) 46 | Log.e(tag, msg); 47 | } 48 | 49 | public static void e(String msg) { 50 | if (DEBUG) 51 | Log.e(TAG, msg); 52 | } 53 | 54 | public static void w(String tag, String msg, Throwable ex) { 55 | if (DEBUG) 56 | Log.w(tag, msg, ex); 57 | } 58 | 59 | public static void e(String tag, String msg, Throwable ex) { 60 | if (DEBUG) 61 | Log.e(tag, msg, ex); 62 | } 63 | 64 | } 65 | -------------------------------------------------------------------------------- /Android屏幕共享-传输图片/AndroidScreenQuick/app/src/main/java/com/talon/screen/quick/util/ScreenShotHelper.java: -------------------------------------------------------------------------------- 1 | package com.talon.screen.quick.util; 2 | 3 | import android.content.Context; 4 | import android.content.Intent; 5 | import android.graphics.Bitmap; 6 | import android.hardware.display.DisplayManager; 7 | import android.hardware.display.VirtualDisplay; 8 | import android.media.Image; 9 | import android.media.ImageReader; 10 | import android.media.projection.MediaProjection; 11 | import android.media.projection.MediaProjectionManager; 12 | import com.talon.screen.quick.Config; 13 | import java.lang.ref.SoftReference; 14 | import java.nio.ByteBuffer; 15 | 16 | /** 17 | * 截屏的封装类,在onActivityResult中调用 18 | */ 19 | public class ScreenShotHelper { 20 | 21 | private final String TAG = "ScreenShotHelper"; 22 | 23 | private int mImageWidth; 24 | private int mImageHeight; 25 | private int mScreenDensity; 26 | 27 | public interface OnScreenShotListener { 28 | void onShotFinish(Bitmap bitmap); 29 | } 30 | 31 | public static ImageReader mImageReader; // 比较特殊,需要其它类持有这个对象,才不会被回收。 32 | private OnScreenShotListener mOnScreenShotListener; 33 | private MediaProjection mMediaProjection; 34 | private VirtualDisplay mVirtualDisplay; 35 | private final SoftReference mRefContext; 36 | 37 | public ScreenShotHelper(Context context, int resultCode, Intent data, OnScreenShotListener onScreenShotListener) { 38 | this.mOnScreenShotListener = onScreenShotListener; 39 | this.mRefContext = new SoftReference(context); 40 | getScreenBaseInfo(); 41 | mMediaProjection = getMediaProjectionManager().getMediaProjection(resultCode, data); 42 | // mImageReader = ImageReader.newInstance(getScreenWidth(), getScreenHeight(), PixelFormat.RGBA_8888, 1); 43 | mImageReader = ImageReader.newInstance(mImageWidth, mImageHeight, 0x01, 2); 44 | } 45 | 46 | /** 47 | * 获取屏幕相关数据 48 | */ 49 | private void getScreenBaseInfo() { 50 | mImageWidth = (int) (ScreenUtils.getScreenWidth(getContext()) * Config.IMAGE_SCALE); 51 | mImageHeight = (int) (ScreenUtils.getScreenHeight(getContext()) * Config.IMAGE_SCALE); 52 | mScreenDensity = ScreenUtils.getScreenDensityDpi(getContext()); 53 | } 54 | 55 | /** 56 | * 开始截屏 57 | */ 58 | public void startScreenShot() { 59 | createVirtualDisplay(); 60 | mImageReader.setOnImageAvailableListener(new ImageAvailableListener(), null); 61 | } 62 | 63 | 64 | private MediaProjectionManager getMediaProjectionManager() { 65 | return (MediaProjectionManager) getContext().getSystemService( 66 | Context.MEDIA_PROJECTION_SERVICE); 67 | } 68 | 69 | private void createVirtualDisplay() { 70 | mVirtualDisplay = mMediaProjection.createVirtualDisplay("screen-mirror", 71 | mImageWidth, 72 | mImageHeight, 73 | mScreenDensity, 74 | DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR, 75 | mImageReader.getSurface(), null, null 76 | ); 77 | } 78 | 79 | private Context getContext() { 80 | return mRefContext.get(); 81 | } 82 | 83 | 84 | /** 85 | * 屏幕发生变化时截取 86 | */ 87 | private class ImageAvailableListener implements ImageReader.OnImageAvailableListener { 88 | @Override 89 | public void onImageAvailable(ImageReader reader) { 90 | try (Image image = reader.acquireLatestImage()) { 91 | if (image != null) { 92 | int width = image.getWidth(); 93 | int height = image.getHeight(); 94 | final Image.Plane[] planes = image.getPlanes(); 95 | final ByteBuffer buffer = planes[0].getBuffer(); 96 | int pixelStride = planes[0].getPixelStride(); 97 | int rowStride = planes[0].getRowStride(); 98 | int rowPadding = rowStride - pixelStride * width; 99 | // Bitmap bitmap = Bitmap.createBitmap(width + rowPadding / pixelStride, height, Bitmap.Config.ARGB_8888); 100 | Bitmap bitmap = Bitmap.createBitmap(width + rowPadding / pixelStride, height, Bitmap.Config.ARGB_4444); 101 | bitmap.copyPixelsFromBuffer(buffer); 102 | bitmap = Bitmap.createBitmap(bitmap, 0, 0, width, height); 103 | 104 | // todo 可以在这里处理Bitmap 105 | bitmap = BitmapUtils.compressBitmap(bitmap, 100); 106 | 107 | if (mOnScreenShotListener != null) { 108 | if (bitmap != null) 109 | mOnScreenShotListener.onShotFinish(bitmap); 110 | } 111 | image.close(); 112 | } 113 | } catch (Exception e) { 114 | e.printStackTrace(); 115 | } 116 | } 117 | } 118 | } -------------------------------------------------------------------------------- /Android屏幕共享-传输图片/AndroidScreenQuick/app/src/main/java/com/talon/screen/quick/util/ScreenUtils.java: -------------------------------------------------------------------------------- 1 | package com.talon.screen.quick.util; 2 | 3 | import android.app.Activity; 4 | import android.content.Context; 5 | import android.view.Window; 6 | import android.view.WindowManager; 7 | 8 | /** 9 | * Created by Talon on 18/4/19. 10 | * 有关屏幕的一切操作 11 | */ 12 | 13 | public class ScreenUtils { 14 | 15 | /** 16 | * 得到设备屏幕的宽度 17 | */ 18 | public static int getScreenWidth(Context context) { 19 | return context.getResources().getDisplayMetrics().widthPixels; 20 | } 21 | 22 | /** 23 | * 得到设备屏幕的高度 24 | */ 25 | public static int getScreenHeight(Context context) { 26 | return context.getResources().getDisplayMetrics().heightPixels; 27 | } 28 | 29 | /** 30 | * 得到设备的密度 31 | */ 32 | public static float getScreenDensity(Context context) { 33 | return context.getResources().getDisplayMetrics().density; 34 | } 35 | 36 | /** 37 | * 得到设备的dpi 38 | */ 39 | public static int getScreenDensityDpi(Context context) { 40 | return context.getResources().getDisplayMetrics().densityDpi; 41 | } 42 | 43 | 44 | /** 45 | * 把密度转换为像素 46 | */ 47 | public static int dip2px(Context context, float px) { 48 | final float scale = getScreenDensity(context); 49 | return (int) (px * scale + 0.5f); 50 | } 51 | /** 52 | * 把像素转换为密度 53 | */ 54 | public static int px2dip(Context context, float pxValue) { 55 | final float scale = getScreenDensity(context); 56 | return (int) (pxValue / scale + 0.5f); 57 | } 58 | 59 | /** 60 | * 设置全屏显示 61 | * @param context 62 | */ 63 | public static void setFullScreen(Activity context){ 64 | int flag = WindowManager.LayoutParams.FLAG_FULLSCREEN; 65 | Window myWindow = context.getWindow(); 66 | myWindow.setFlags(flag, flag);// 设置为全屏 67 | } 68 | 69 | } 70 | -------------------------------------------------------------------------------- /Android屏幕共享-传输图片/AndroidScreenQuick/app/src/main/java/com/talon/screen/quick/util/ThreadPool.java: -------------------------------------------------------------------------------- 1 | package com.talon.screen.quick.util; 2 | 3 | import android.support.annotation.NonNull; 4 | 5 | import java.util.concurrent.Executor; 6 | import java.util.concurrent.ExecutorService; 7 | import java.util.concurrent.Executors; 8 | import java.util.concurrent.LinkedBlockingQueue; 9 | import java.util.concurrent.ThreadFactory; 10 | import java.util.concurrent.ThreadPoolExecutor; 11 | import java.util.concurrent.TimeUnit; 12 | 13 | /** 14 | * @author by Talon, Date on 19/1/22. 15 | * note: 线程的优化,线程池 16 | */ 17 | public class ThreadPool { 18 | 19 | private static final String TAG = "ThreadProxy"; 20 | private final Executor executor; 21 | private final ExecutorService singleThreadExecutor; 22 | 23 | static class InnerClass { 24 | static ThreadPool instance = new ThreadPool(); 25 | } 26 | 27 | private ThreadPool() { 28 | 29 | executor = new ThreadPoolExecutor(2, 8, 30 | 15, TimeUnit.SECONDS, 31 | new LinkedBlockingQueue(Integer.MAX_VALUE), new ThreadFactory() { 32 | @Override 33 | public Thread newThread(@NonNull Runnable r) { 34 | Thread thread = new Thread(r); 35 | thread.setName("ThreadProxy"); 36 | return thread; 37 | } 38 | }); 39 | singleThreadExecutor = Executors.newSingleThreadExecutor(); 40 | } 41 | 42 | public void execute(final Runnable run) { 43 | 44 | executor.execute(new Runnable() { 45 | @Override 46 | public void run() { 47 | try { 48 | run.run(); 49 | }catch (Exception e){ 50 | if(LogWrapper.DEBUG){ 51 | e.printStackTrace(); 52 | } 53 | LogWrapper.e(TAG,"failed to run task "+e.getMessage()); 54 | } 55 | } 56 | }); 57 | 58 | } 59 | 60 | public void executeInSingle(final Runnable run) { 61 | singleThreadExecutor.execute(new Runnable() { 62 | @Override 63 | public void run() { 64 | try { 65 | run.run(); 66 | }catch (Exception e){ 67 | LogWrapper.d(TAG,"failed to run task "+e.getMessage()); 68 | } 69 | } 70 | }); 71 | } 72 | 73 | public static ThreadPool getInstance() { 74 | return InnerClass.instance; 75 | } 76 | 77 | } 78 | -------------------------------------------------------------------------------- /Android屏幕共享-传输图片/AndroidScreenQuick/app/src/main/res/drawable-v24/ic_launcher_foreground.xml: -------------------------------------------------------------------------------- 1 | 7 | 8 | 9 | 15 | 18 | 21 | 22 | 23 | 24 | 30 | -------------------------------------------------------------------------------- /Android屏幕共享-传输图片/AndroidScreenQuick/app/src/main/res/drawable/ic_launcher_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 10 | 15 | 20 | 25 | 30 | 35 | 40 | 45 | 50 | 55 | 60 | 65 | 70 | 75 | 80 | 85 | 90 | 95 | 100 | 105 | 110 | 115 | 120 | 125 | 130 | 135 | 140 | 145 | 150 | 155 | 160 | 165 | 170 | 171 | -------------------------------------------------------------------------------- /Android屏幕共享-传输图片/AndroidScreenQuick/app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 10 | 16 | 17 | 18 |