├── .gitignore
├── README.md
├── src
├── main
│ ├── res
│ │ ├── values
│ │ │ └── strings.xml
│ │ └── layout
│ │ │ ├── activity_maintest2.xml
│ │ │ └── activity_maintest.xml
│ ├── AndroidManifest.xml
│ └── java
│ │ └── com
│ │ └── jinwei
│ │ └── screencaputre
│ │ ├── activity
│ │ └── CapoutActivity.java
│ │ ├── TestActivity.java
│ │ └── cutout
│ │ └── ScreenService.java
├── test
│ └── java
│ │ └── com
│ │ └── jinwei
│ │ └── screencaputre
│ │ └── ExampleUnitTest.java
└── androidTest
│ └── java
│ └── com
│ └── jinwei
│ └── screencaputre
│ └── ExampleInstrumentedTest.java
├── proguard-rules.pro
└── screencaputre.iml
/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # screencaputre
2 | android 5.0 the screencaputre
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | screencaputre
4 |
5 |
6 |
--------------------------------------------------------------------------------
/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
6 |
7 |
12 |
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/src/test/java/com/jinwei/screencaputre/ExampleUnitTest.java:
--------------------------------------------------------------------------------
1 | package com.jinwei.screencaputre;
2 |
3 | import org.junit.Test;
4 |
5 | import static org.junit.Assert.*;
6 |
7 | /**
8 | * Example local unit test, which will execute on the development machine
9 | * (host).
10 | *
11 | * @see Testing documentation
12 | */
13 | public class ExampleUnitTest {
14 | @Test
15 | public void addition_isCorrect() throws Exception {
16 | assertEquals(4, 2 + 2);
17 | }
18 | }
--------------------------------------------------------------------------------
/src/main/res/layout/activity_maintest2.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
13 |
14 |
19 |
20 |
--------------------------------------------------------------------------------
/src/androidTest/java/com/jinwei/screencaputre/ExampleInstrumentedTest.java:
--------------------------------------------------------------------------------
1 | package com.jinwei.screencaputre;
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 | * Instrumentation 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() throws Exception {
21 | // Context of the app under test.
22 | Context appContext = InstrumentationRegistry.getTargetContext();
23 |
24 | assertEquals("com.jinwei.screencaputre.test", appContext.getPackageName());
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/src/main/res/layout/activity_maintest.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
13 |
19 |
24 |
25 |
--------------------------------------------------------------------------------
/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # By default, the flags in this file are appended to flags specified
3 | # in D:\android\android-sdk-windows/tools/proguard/proguard-android.txt
4 | # You can edit the include path and order by changing the proguardFiles
5 | # directive in build.gradle.
6 | #
7 | # For more details, see
8 | # http://developer.android.com/guide/developing/tools/proguard.html
9 |
10 | # Add any project specific keep options here:
11 |
12 | # If your project uses WebView with JS, uncomment the following
13 | # and specify the fully qualified class name to the JavaScript interface
14 | # class:
15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
16 | # public *;
17 | #}
18 |
19 | # Uncomment this to preserve the line number information for
20 | # debugging stack traces.
21 | #-keepattributes SourceFile,LineNumberTable
22 |
23 | # If you keep the line number information, uncomment this to
24 | # hide the original source file name.
25 | #-renamesourcefileattribute SourceFile
26 |
--------------------------------------------------------------------------------
/src/main/java/com/jinwei/screencaputre/activity/CapoutActivity.java:
--------------------------------------------------------------------------------
1 | package com.jinwei.screencaputre.activity;
2 |
3 | import android.app.Activity;
4 | import android.content.ComponentName;
5 | import android.content.Intent;
6 | import android.content.ServiceConnection;
7 | import android.graphics.Bitmap;
8 | import android.media.projection.MediaProjection;
9 | import android.media.projection.MediaProjectionManager;
10 | import android.os.Bundle;
11 | import android.os.IBinder;
12 | import android.util.DisplayMetrics;
13 | import android.view.View;
14 | import android.widget.Button;
15 | import android.widget.ImageView;
16 | import com.jinwei.screencaputre.R;
17 | import com.jinwei.screencaputre.cutout.ScreenService;
18 |
19 | /**
20 | * 项目名称:
21 | * 类描述:截屏
22 | * 创建人:JinWei
23 | * 创建时间:2017/6/5
24 | * 修改人:
25 | * 修改时间:2017/6/5
26 | * 修改备注:
27 | */
28 |
29 | public class CapoutActivity extends Activity {
30 | Button mButton;
31 | ImageView mImageView;
32 | private MediaProjectionManager projectionManager;
33 | private MediaProjection mediaProjection;
34 | private ScreenService recordService;
35 | private static int RECORD_REQUEST_CODE = 5;
36 |
37 |
38 | @Override
39 | protected void onCreate(Bundle savedInstanceState) {
40 | super.onCreate(savedInstanceState);
41 | setContentView(R.layout.activity_maintest2);
42 | projectionManager = (MediaProjectionManager) getSystemService(MEDIA_PROJECTION_SERVICE);
43 | mButton = (Button) findViewById(R.id.butview);
44 | mImageView = (ImageView) findViewById(R.id.img);
45 | Intent intent = new Intent(this, ScreenService.class);
46 | bindService(intent, mServiceConnection, BIND_AUTO_CREATE);
47 | Intent captureIntent = projectionManager.createScreenCaptureIntent();
48 | startActivityForResult(captureIntent, RECORD_REQUEST_CODE);
49 | mButton.setOnClickListener(new View.OnClickListener() {
50 | @Override
51 | public void onClick(View view) {
52 |
53 | //######## 截屏逻辑 ########
54 | Bitmap bitmap = recordService.getBitmap();
55 | mImageView.setImageBitmap(bitmap);
56 | }
57 | });
58 | }
59 |
60 |
61 | @Override
62 | protected void onActivityResult(int requestCode, int resultCode, Intent data) {
63 | if (requestCode == RECORD_REQUEST_CODE && resultCode == RESULT_OK) {
64 |
65 | //######## 截屏逻辑 ########
66 | mediaProjection = projectionManager.getMediaProjection(resultCode, data);
67 | recordService.setMediaProject(mediaProjection);
68 | recordService.initImageReader();
69 |
70 | }
71 | }
72 |
73 |
74 | @Override
75 | protected void onDestroy() {
76 | super.onDestroy();
77 | unbindService(mServiceConnection);
78 | }
79 |
80 |
81 | private ServiceConnection mServiceConnection = new ServiceConnection() {
82 | @Override
83 | public void onServiceConnected(ComponentName className, IBinder service) {
84 | DisplayMetrics metrics = new DisplayMetrics();
85 | getWindowManager().getDefaultDisplay().getMetrics(metrics);
86 | ScreenService.RecordBinder binder = (ScreenService.RecordBinder) service;
87 | recordService = binder.getRecordService();
88 | recordService.setConfig(metrics.widthPixels, metrics.heightPixels, metrics.densityDpi);
89 | mButton.setEnabled(true);
90 | mButton.setText(recordService.isRunning() ? "结束" : "开始");
91 | }
92 |
93 |
94 | @Override
95 | public void onServiceDisconnected(ComponentName arg0) {}
96 | };
97 | }
98 |
--------------------------------------------------------------------------------
/src/main/java/com/jinwei/screencaputre/TestActivity.java:
--------------------------------------------------------------------------------
1 | package com.jinwei.screencaputre;
2 |
3 | import android.app.Activity;
4 | import android.content.ComponentName;
5 | import android.content.Intent;
6 | import android.content.ServiceConnection;
7 | import android.graphics.Bitmap;
8 | import android.media.Image;
9 | import android.media.projection.MediaProjection;
10 | import android.media.projection.MediaProjectionManager;
11 | import android.os.Bundle;
12 | import android.os.IBinder;
13 | import android.util.DisplayMetrics;
14 | import android.view.View;
15 | import android.widget.Button;
16 | import android.widget.ImageView;
17 | import com.jinwei.screencaputre.activity.CapoutActivity;
18 | import com.jinwei.screencaputre.cutout.ScreenService;
19 |
20 | /**
21 | * 项目名称:
22 | * 类描述:
23 | * 创建人:JinWei
24 | * 创建时间:2017/6/5
25 | * 修改人:
26 | * 修改时间:2017/6/5
27 | * 修改备注:
28 | */
29 |
30 | public class TestActivity extends Activity {
31 | Button mButton, mCapButton;
32 | ImageView mImageView;
33 | private MediaProjectionManager projectionManager;
34 | private MediaProjection mediaProjection;
35 | private ScreenService recordService;
36 | private static int RECORD_REQUEST_CODE = 5;
37 |
38 |
39 | @Override
40 | protected void onCreate(Bundle savedInstanceState) {
41 | super.onCreate(savedInstanceState);
42 | setContentView(R.layout.activity_maintest);
43 | projectionManager = (MediaProjectionManager) getSystemService(MEDIA_PROJECTION_SERVICE);
44 | mButton = (Button) findViewById(R.id.butview);
45 | mCapButton = (Button) findViewById(R.id.capout);
46 | mImageView = (ImageView) findViewById(R.id.img);
47 | Intent intent = new Intent(this, ScreenService.class);
48 | bindService(intent, mServiceConnection, BIND_AUTO_CREATE);
49 | mButton.setOnClickListener(new View.OnClickListener() {
50 | @Override
51 | public void onClick(View view) {
52 | //######## 录屏逻辑 ########
53 | if (recordService.isRunning()) {
54 | recordService.stopRecord();
55 | mButton.setText("录屏");
56 | } else {
57 | //这里是请求录屏权限
58 | Intent captureIntent = projectionManager
59 | .createScreenCaptureIntent();
60 | startActivityForResult(captureIntent, RECORD_REQUEST_CODE);
61 | }
62 | }
63 | });
64 | mCapButton.setOnClickListener(new View.OnClickListener() {
65 | @Override
66 | public void onClick(View view) {
67 | Intent in = new Intent(TestActivity.this, CapoutActivity.class);
68 | startActivity(in);
69 | }
70 | });
71 | }
72 |
73 |
74 | @Override
75 | protected void onActivityResult(int requestCode, int resultCode, Intent data) {
76 | if (requestCode == RECORD_REQUEST_CODE && resultCode == RESULT_OK) {
77 | //######## 录屏逻辑 ########
78 | mediaProjection = projectionManager
79 | .getMediaProjection(resultCode, data);
80 | recordService.setMediaProject(mediaProjection);
81 | recordService.startRecord();
82 | mButton.setText("结束");
83 | }
84 | }
85 |
86 |
87 | @Override
88 | protected void onDestroy() {
89 | super.onDestroy();
90 | unbindService(mServiceConnection);
91 | }
92 |
93 |
94 | private ServiceConnection mServiceConnection = new ServiceConnection() {
95 | @Override
96 | public void onServiceConnected(ComponentName className, IBinder service) {
97 | DisplayMetrics metrics = new DisplayMetrics();
98 | getWindowManager().getDefaultDisplay().getMetrics(metrics);
99 | ScreenService.RecordBinder binder = (ScreenService.RecordBinder) service;
100 | recordService = binder.getRecordService();
101 | recordService
102 | .setConfig(metrics.widthPixels, metrics.heightPixels, metrics.densityDpi);
103 | mButton.setEnabled(true);
104 | mButton.setText(recordService.isRunning() ? "结束" : "开始");
105 | }
106 |
107 |
108 | @Override
109 | public void onServiceDisconnected(ComponentName arg0) {}
110 | };
111 | }
112 |
--------------------------------------------------------------------------------
/src/main/java/com/jinwei/screencaputre/cutout/ScreenService.java:
--------------------------------------------------------------------------------
1 | package com.jinwei.screencaputre.cutout;
2 |
3 | import android.app.Service;
4 | import android.content.Intent;
5 | import android.graphics.Bitmap;
6 | import android.graphics.PixelFormat;
7 | import android.hardware.display.DisplayManager;
8 | import android.hardware.display.VirtualDisplay;
9 | import android.media.Image;
10 | import android.media.ImageReader;
11 | import android.media.MediaRecorder;
12 | import android.media.projection.MediaProjection;
13 | import android.os.Binder;
14 | import android.os.Environment;
15 | import android.os.IBinder;
16 | import java.io.File;
17 | import java.io.IOException;
18 | import java.nio.ByteBuffer;
19 |
20 | /**
21 | * 项目名称:
22 | * 类描述: 录屏服务类
23 | * 创建人:JinWei
24 | * 创建时间:2017/6/7
25 | * 修改人:
26 | * 修改时间:2017/6/7
27 | * 修改备注:
28 | */
29 |
30 | public class ScreenService extends Service {
31 | private MediaRecorder mediaRecorder;
32 | private VirtualDisplay virtualDisplay;
33 | private boolean running;
34 | private int width = 720;
35 | private int height = 1080;
36 | private int dpi;
37 | private ImageReader mImageReader;
38 | private MediaProjection mediaProjection;
39 |
40 |
41 | @Override
42 | public IBinder onBind(Intent intent) {
43 | return new RecordBinder();
44 | }
45 |
46 |
47 | @Override
48 | public void onCreate() {
49 | super.onCreate();
50 | running = false;
51 | mediaRecorder = new MediaRecorder();
52 | }
53 |
54 |
55 | @Override
56 | public int onStartCommand(Intent intent, int flags, int startId) {
57 | return super.onStartCommand(intent, flags, startId);
58 | }
59 |
60 |
61 | @Override
62 | public void onDestroy() {
63 | super.onDestroy();
64 | }
65 |
66 |
67 | public void setMediaProject(MediaProjection project) {
68 | mediaProjection = project;
69 | }
70 |
71 |
72 | public boolean isRunning() {
73 | return running;
74 | }
75 |
76 |
77 | public void setConfig(int width, int height, int dpi) {
78 | this.width = width;
79 | this.height = height;
80 | this.dpi = dpi;
81 | }
82 |
83 |
84 | /**
85 | * 开始录屏
86 | *
87 | * @return true
88 | */
89 | public boolean startRecord() {
90 | if (mediaProjection == null || running) {
91 | return false;
92 | }
93 | initRecorder();
94 | createVirtualDisplay();
95 | mediaRecorder.start();
96 | running = true;
97 | return true;
98 | }
99 |
100 |
101 | /**
102 | * 结束录屏
103 | *
104 | * @return true
105 | */
106 | public boolean stopRecord() {
107 | if (!running) {
108 | return false;
109 | }
110 | running = false;
111 | mediaRecorder.stop();
112 | mediaRecorder.reset();
113 | virtualDisplay.release();
114 | mediaProjection.stop();
115 |
116 | return true;
117 | }
118 |
119 |
120 | public void setMediaProjection(MediaProjection mediaProjection) {
121 | this.mediaProjection = mediaProjection;
122 | }
123 |
124 |
125 | /**
126 | * 初始化ImageRead参数
127 | */
128 | public void initImageReader() {
129 | if (mImageReader == null) {
130 | int maxImages = 2;
131 | mImageReader = ImageReader.newInstance(width, height, PixelFormat.RGBA_8888, maxImages);
132 | createImageVirtualDisplay();
133 | }
134 | }
135 |
136 |
137 | /**
138 | * 创建一个录屏 Virtual
139 | */
140 |
141 | private void createVirtualDisplay() {
142 | virtualDisplay = mediaProjection
143 | .createVirtualDisplay("mediaprojection", width, height, dpi, DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR, mediaRecorder
144 | .getSurface(), null, null);
145 | }
146 |
147 |
148 | /**
149 | * 创建一个ImageReader Virtual
150 | */
151 | private void createImageVirtualDisplay() {
152 | virtualDisplay = mediaProjection
153 | .createVirtualDisplay("mediaprojection", width, height, dpi,
154 | DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR, mImageReader
155 | .getSurface(), null, null);
156 | }
157 |
158 |
159 | /**
160 | * 初始化保存屏幕录像的参数
161 | */
162 | private void initRecorder() {
163 | mediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
164 | mediaRecorder.setVideoSource(MediaRecorder.VideoSource.SURFACE);
165 | mediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);
166 | mediaRecorder.setOutputFile(
167 | getSavePath() + System.currentTimeMillis() + ".mp4");
168 | mediaRecorder.setVideoSize(width, height);
169 | mediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.H264);
170 | mediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);
171 | mediaRecorder.setVideoEncodingBitRate(5 * 1024 * 1024);
172 | mediaRecorder.setVideoFrameRate(30);
173 | try {
174 | mediaRecorder.prepare();
175 | } catch (IOException e) {
176 | e.printStackTrace();
177 | }
178 | }
179 |
180 |
181 | /**
182 | * 获取一个保存屏幕录像的路径
183 | *
184 | * @return path
185 | */
186 | public String getSavePath() {
187 | if (Environment.getExternalStorageState()
188 | .equals(Environment.MEDIA_MOUNTED)) {
189 | String rootDir = Environment.getExternalStorageDirectory()
190 | .getAbsolutePath() + "/" +
191 | "ScreenRecord" + "/";
192 |
193 | File file = new File(rootDir);
194 | if (!file.exists()) {
195 | if (!file.mkdirs()) {
196 | return null;
197 | }
198 | }
199 | return rootDir;
200 | } else {
201 | return null;
202 | }
203 | }
204 |
205 |
206 | /**
207 | * 请求完权限后马上获取有可能为null,可以通过判断is null来重复获取。
208 | */
209 | public Bitmap getBitmap() {
210 | Bitmap bitmap = cutoutFrame();
211 | if (bitmap == null) {
212 | getBitmap();
213 | }
214 | return bitmap;
215 | }
216 |
217 |
218 | /**
219 | * 通过底层来获取下一帧的图像
220 | *
221 | * @return bitmap
222 | */
223 | public Bitmap cutoutFrame() {
224 | Image image = mImageReader.acquireLatestImage();
225 | if (image == null) {
226 | return null;
227 | }
228 | int width = image.getWidth();
229 | int height = image.getHeight();
230 | final Image.Plane[] planes = image.getPlanes();
231 | final ByteBuffer buffer = planes[0].getBuffer();
232 | int pixelStride = planes[0].getPixelStride();
233 | int rowStride = planes[0].getRowStride();
234 | int rowPadding = rowStride - pixelStride * width;
235 | Bitmap bitmap = Bitmap.createBitmap(width +
236 | rowPadding / pixelStride, height, Bitmap.Config.ARGB_8888);
237 | bitmap.copyPixelsFromBuffer(buffer);
238 | return Bitmap.createBitmap(bitmap, 0, 0, width, height);
239 | }
240 |
241 |
242 | public class RecordBinder extends Binder {
243 | public ScreenService getRecordService() {
244 | return ScreenService.this;
245 | }
246 | }
247 | }
248 |
--------------------------------------------------------------------------------
/screencaputre.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 | generateDebugSources
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
--------------------------------------------------------------------------------