├── .gitignore
├── .idea
├── .name
├── compiler.xml
├── copyright
│ └── profiles_settings.xml
├── gradle.xml
├── misc.xml
├── modules.xml
├── runConfigurations.xml
└── vcs.xml
├── PresentationOnVirtualDisplay.iml
├── README.md
├── app
├── .gitignore
├── app.iml
├── build.gradle
├── proguard-rules.pro
└── src
│ ├── androidTest
│ └── java
│ │ └── com
│ │ └── andronblog
│ │ └── presentationonvirtualdisplay
│ │ └── ApplicationTest.java
│ └── main
│ ├── AndroidManifest.xml
│ ├── java
│ └── com
│ │ └── andronblog
│ │ └── presentationonvirtualdisplay
│ │ ├── CameraView.java
│ │ ├── DemoPresentation.java
│ │ ├── MainActivity.java
│ │ └── RecorderHelper.java
│ └── res
│ ├── anim
│ └── rotator.xml
│ ├── layout
│ ├── activity_main.xml
│ └── my_presentation.xml
│ ├── menu
│ └── menu_main.xml
│ ├── mipmap-hdpi
│ └── ic_launcher.png
│ ├── mipmap-mdpi
│ └── ic_launcher.png
│ ├── mipmap-xhdpi
│ └── ic_launcher.png
│ ├── mipmap-xxhdpi
│ └── ic_launcher.png
│ ├── values-v21
│ └── styles.xml
│ ├── values-w820dp
│ └── dimens.xml
│ └── values
│ ├── dimens.xml
│ ├── strings.xml
│ └── styles.xml
├── build.gradle
├── gradle.properties
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
└── settings.gradle
/.gitignore:
--------------------------------------------------------------------------------
1 | .gradle
2 | /local.properties
3 | /.idea/workspace.xml
4 | /.idea/libraries
5 | .DS_Store
6 | /build
7 | /captures
8 |
--------------------------------------------------------------------------------
/.idea/.name:
--------------------------------------------------------------------------------
1 | PresentationOnVirtualDisplay
--------------------------------------------------------------------------------
/.idea/compiler.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/.idea/copyright/profiles_settings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/.idea/gradle.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
17 |
18 |
--------------------------------------------------------------------------------
/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
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 | 1.7
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
--------------------------------------------------------------------------------
/.idea/modules.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/.idea/runConfigurations.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/PresentationOnVirtualDisplay.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # PresentationOnVirtualDisplay
2 |
3 | PresentationOnVirtualDisplay is a small android application demonstrating:
4 |
5 | * How to show Presentation on android Virtual Display. Git tag: l01_virtualdisplay. [More details](http://www.andronblog.com/dev/how-to-show-presentation-on-android-virtual-display/)
6 |
7 | * How to capture android Presentation by MediaRecorder. Git tag: l02_mediarecorder. [More details](http://www.andronblog.com/dev/how-to-capture-android-presentation-by-mediarecorder/)
8 |
9 | By [Andrei Mandychev](http://www.andronblog.com/dev/)
10 |
--------------------------------------------------------------------------------
/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/app/app.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 | generateDebugAndroidTestSources
19 | generateDebugSources
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 |
--------------------------------------------------------------------------------
/app/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.application'
2 |
3 | android {
4 | compileSdkVersion 23
5 | buildToolsVersion "23.0.0"
6 |
7 | defaultConfig {
8 | applicationId "com.andronblog.presentationonvirtualdisplay"
9 | minSdkVersion 21
10 | targetSdkVersion 23
11 | versionCode 1
12 | versionName "1.0"
13 | }
14 | buildTypes {
15 | release {
16 | minifyEnabled false
17 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
18 | }
19 | }
20 | }
21 |
22 | dependencies {
23 | compile fileTree(dir: 'libs', include: ['*.jar'])
24 | compile 'com.android.support:support-v4:23.0.0'
25 | }
26 |
--------------------------------------------------------------------------------
/app/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 /home/mandrew/android-sdks/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 |
--------------------------------------------------------------------------------
/app/src/androidTest/java/com/andronblog/presentationonvirtualdisplay/ApplicationTest.java:
--------------------------------------------------------------------------------
1 | package com.andronblog.presentationonvirtualdisplay;
2 |
3 | import android.app.Application;
4 | import android.test.ApplicationTestCase;
5 |
6 | /**
7 | * Testing Fundamentals
8 | */
9 | public class ApplicationTest extends ApplicationTestCase {
10 | public ApplicationTest() {
11 | super(Application.class);
12 | }
13 | }
--------------------------------------------------------------------------------
/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
8 |
13 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/app/src/main/java/com/andronblog/presentationonvirtualdisplay/CameraView.java:
--------------------------------------------------------------------------------
1 | package com.andronblog.presentationonvirtualdisplay;
2 |
3 | import android.app.Activity;
4 | import android.app.Presentation;
5 | import android.content.Context;
6 | import android.content.pm.PackageManager;
7 | import android.hardware.Camera;
8 | import android.util.AttributeSet;
9 | import android.util.Log;
10 | import android.view.Display;
11 | import android.view.Surface;
12 | import android.view.SurfaceHolder;
13 | import android.view.SurfaceView;
14 |
15 | import java.io.IOException;
16 |
17 | public class CameraView extends SurfaceView implements SurfaceHolder.Callback {
18 |
19 | private static final String TAG = "CameraView";
20 | private static final int CAMERA_ID = Camera.CameraInfo.CAMERA_FACING_FRONT;
21 |
22 | private Context mContext;
23 | private SurfaceHolder mHolder;
24 | private Camera mCamera;
25 |
26 | public CameraView(Context context) {
27 | super(context);
28 | initCameraView(context);
29 | }
30 |
31 | public CameraView(Context context, AttributeSet attrs) {
32 | super(context, attrs);
33 | initCameraView(context);
34 | }
35 |
36 | public CameraView(Context context, AttributeSet attrs, int defStyle) {
37 | super(context, attrs, defStyle);
38 | initCameraView(context);
39 | }
40 |
41 | private void initCameraView(Context context) {
42 | mContext = context;
43 | // TODO check that camera exist
44 | mCamera = getCameraInstance();
45 | // Install a SurfaceHolder.Callback so we get notified when the
46 | // underlying surface is created and destroyed.
47 | mHolder = getHolder();
48 | mHolder.addCallback(this);
49 | }
50 |
51 | public void setCameraDisplayOrientation(int displayOrientation) {
52 |
53 | if (mCamera == null)
54 | return;
55 |
56 | android.hardware.Camera.CameraInfo info = new android.hardware.Camera.CameraInfo();
57 | android.hardware.Camera.getCameraInfo(CAMERA_ID, info);
58 | int degrees = 0;
59 | switch (displayOrientation) {
60 | case Surface.ROTATION_0: degrees = 0; break;
61 | case Surface.ROTATION_90: degrees = 90; break;
62 | case Surface.ROTATION_180: degrees = 180; break;
63 | case Surface.ROTATION_270: degrees = 270; break;
64 | }
65 |
66 | int result;
67 | if (info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
68 | result = (info.orientation + degrees) % 360;
69 | result = (360 - result) % 360; // compensate the mirror
70 | } else { // back-facing
71 | result = (info.orientation - degrees + 360) % 360;
72 | }
73 | mCamera.setDisplayOrientation(result);
74 | }
75 |
76 | public void surfaceCreated(SurfaceHolder holder) {
77 | // The Surface has been created, now tell the camera where to draw the preview.
78 | try {
79 | mCamera.setPreviewDisplay(holder);
80 | mCamera.startPreview();
81 | } catch (IOException e) {
82 | Log.d(TAG, "Error setting camera preview: " + e.getMessage());
83 | }
84 | }
85 |
86 | public void surfaceDestroyed(SurfaceHolder holder) {
87 | if (mCamera != null) {
88 | mCamera.release();
89 | }
90 | }
91 |
92 | public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
93 | // If your preview can change or rotate, take care of those events here.
94 | // Make sure to stop the preview before resizing or reformatting it.
95 |
96 | if (mHolder.getSurface() == null){
97 | // preview surface does not exist
98 | return;
99 | }
100 |
101 | // stop preview before making changes
102 | try {
103 | mCamera.stopPreview();
104 | } catch (Exception e){
105 | // ignore: tried to stop a non-existent preview
106 | }
107 |
108 | // set preview size and make any resize, rotate or
109 | // reformatting changes here
110 |
111 | // start preview with new settings
112 | try {
113 | mCamera.setPreviewDisplay(mHolder);
114 | mCamera.startPreview();
115 |
116 | } catch (Exception e){
117 | Log.d(TAG, "Error starting camera preview: " + e.getMessage());
118 | }
119 | }
120 |
121 | private boolean checkCameraHardware(Context context) {
122 | if (context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA)){
123 | // this device has a camera
124 | return true;
125 | } else {
126 | // no camera on this device
127 | return false;
128 | }
129 | }
130 |
131 | public static Camera getCameraInstance(){
132 | Camera c = null;
133 | try {
134 | c = Camera.open(CAMERA_ID);
135 | }
136 | catch (Exception e){
137 | // Camera is not available (in use or does not exist)
138 | }
139 | return c; // returns null if camera is unavailable
140 | }
141 |
142 |
143 | }
144 |
--------------------------------------------------------------------------------
/app/src/main/java/com/andronblog/presentationonvirtualdisplay/DemoPresentation.java:
--------------------------------------------------------------------------------
1 | package com.andronblog.presentationonvirtualdisplay;
2 |
3 | import android.app.Activity;
4 | import android.app.Presentation;
5 | import android.content.Context;
6 | import android.content.DialogInterface;
7 | import android.os.Bundle;
8 | import android.os.Handler;
9 | import android.util.Log;
10 | import android.view.Display;
11 | import android.view.Surface;
12 | import android.view.animation.Animation;
13 | import android.view.animation.AnimationUtils;
14 | import android.widget.TextView;
15 |
16 | public class DemoPresentation extends Presentation {
17 |
18 | private static final String TAG = "DemoPresentation";
19 |
20 | private int mDefaultDisplayOrientation = Surface.ROTATION_0;
21 | private Handler mTimerHandler;
22 |
23 | public DemoPresentation(Context context, Display display) {
24 | super(context, display);
25 | mTimerHandler = new Handler();
26 | if (context instanceof Activity) {
27 | Activity activity = (Activity) context;
28 | display = activity.getWindowManager().getDefaultDisplay();
29 | mDefaultDisplayOrientation = display.getRotation();
30 | }
31 | }
32 |
33 | @Override
34 | protected void onCreate(Bundle savedInstanceState) {
35 | super.onCreate(savedInstanceState);
36 | Log.i(TAG, "onCreate");
37 | setContentView(R.layout.my_presentation);
38 |
39 | TextView tv = (TextView) findViewById(R.id.textView);
40 | Animation myRotation = AnimationUtils.loadAnimation(getContext(), R.anim.rotator);
41 | tv.startAnimation(myRotation);
42 |
43 | CameraView cameraView = (CameraView) findViewById(R.id.cameraView);
44 | cameraView.setCameraDisplayOrientation(mDefaultDisplayOrientation);
45 |
46 | final TextView timeTextView = (TextView) findViewById(R.id.tv_time);
47 | final long startTime = System.currentTimeMillis();
48 | final Runnable timerRunnable = new Runnable() {
49 | @Override
50 | public void run() {
51 | long millis = System.currentTimeMillis() - startTime;
52 | int seconds = (int) (millis / 1000);
53 | int minutes = seconds / 60;
54 | seconds = seconds % 60;
55 | timeTextView.setText(String.format("%d:%02d", minutes, seconds));
56 | mTimerHandler.postDelayed(this, 500);
57 | }
58 | };
59 | mTimerHandler.postDelayed(timerRunnable, 0);
60 |
61 | setOnDismissListener(new OnDismissListener() {
62 | @Override
63 | public void onDismiss(DialogInterface dialogInterface) {
64 | Log.i(TAG, "onDismiss");
65 | mTimerHandler.removeCallbacks(timerRunnable);
66 | }
67 | });
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/app/src/main/java/com/andronblog/presentationonvirtualdisplay/MainActivity.java:
--------------------------------------------------------------------------------
1 | package com.andronblog.presentationonvirtualdisplay;
2 |
3 | import android.Manifest;
4 | import android.app.Activity;
5 | import android.app.Presentation;
6 | import android.content.Context;
7 | import android.content.Intent;
8 | import android.content.pm.PackageManager;
9 | import android.graphics.Point;
10 | import android.hardware.display.DisplayManager;
11 | import android.hardware.display.VirtualDisplay;
12 | import android.media.MediaPlayer;
13 | import android.media.MediaRecorder;
14 | import android.media.MediaRouter;
15 | import android.media.projection.MediaProjection;
16 | import android.media.projection.MediaProjectionManager;
17 | import android.net.Uri;
18 | import android.os.Build;
19 | import android.os.Bundle;
20 | import android.os.Environment;
21 | import android.support.v4.app.ActivityCompat;
22 | import android.support.v4.content.ContextCompat;
23 | import android.util.DisplayMetrics;
24 | import android.util.Log;
25 | import android.util.Size;
26 | import android.view.Display;
27 | import android.view.Gravity;
28 | import android.view.Menu;
29 | import android.view.MenuItem;
30 | import android.view.Surface;
31 | import android.view.SurfaceView;
32 | import android.view.View;
33 | import android.view.WindowManager;
34 | import android.view.animation.Animation;
35 | import android.view.animation.AnimationUtils;
36 | import android.widget.Button;
37 | import android.widget.TextView;
38 | import android.widget.Toast;
39 |
40 | import java.io.IOException;
41 |
42 | public class MainActivity extends Activity {
43 |
44 | private static final String TAG = "MainActivity";
45 |
46 | private static final int SCREEN_CAPTURE_PERMISSION_CODE = 1;
47 | private static final int EXTERNAL_STORAGE_PERMISSION_CODE = 2;
48 |
49 | private static final int FRAMERATE = 30;
50 | private static final String FILENAME = Environment.getExternalStorageDirectory().getPath()+"/presentation.mp4";
51 |
52 | private int mWidth;
53 | private int mHeight;
54 | private DisplayMetrics mMetrics = new DisplayMetrics();
55 |
56 | private DisplayManager mDisplayManager;
57 | private VirtualDisplay mVirtualDisplay;
58 | private MediaRecorder mMediaRecorder;
59 |
60 | private int mResultCode;
61 | private Intent mResultData;
62 |
63 | private MediaProjectionManager mProjectionManager;
64 | private MediaProjection mProjection;
65 | private MediaProjection.Callback mProjectionCallback;
66 |
67 | private MediaPlayer mMediaPlayer;
68 | private SurfaceView mSurfaceView;
69 |
70 | private Surface mSurface;
71 | private Button mButtonCreate;
72 | private Button mButtonDestroy;
73 | private Button mButtonPlayVideo;
74 | private Button mButtonStopVideo;
75 |
76 | @Override
77 | protected void onCreate(Bundle savedInstanceState) {
78 | super.onCreate(savedInstanceState);
79 | setContentView(R.layout.activity_main);
80 |
81 | mSurfaceView = (SurfaceView) findViewById(R.id.surfaceView);
82 | mSurface = mSurfaceView.getHolder().getSurface();
83 |
84 | // Obtain display metrics of current display to know its density (dpi)
85 | WindowManager wm = (WindowManager) getSystemService(Context.WINDOW_SERVICE);
86 | Display display = wm.getDefaultDisplay();
87 | display.getMetrics(mMetrics);
88 | // Initialize resolution of virtual display in pixels to show
89 | // the surface view on full screen
90 | mWidth = mSurfaceView.getLayoutParams().width;
91 | mHeight = mSurfaceView.getLayoutParams().height;
92 |
93 | mDisplayManager = (DisplayManager) getSystemService(Context.DISPLAY_SERVICE);
94 | mDisplayManager.registerDisplayListener(mDisplayListener, null);
95 | mProjectionManager = (MediaProjectionManager) getSystemService(Context.MEDIA_PROJECTION_SERVICE);
96 | mMediaRecorder = new MediaRecorder();
97 |
98 | mButtonCreate = (Button) findViewById(R.id.btn_create_virtual_display);
99 | mButtonCreate.setEnabled(false);
100 | mButtonCreate.setOnClickListener(new View.OnClickListener() {
101 | @Override
102 | public void onClick(View view) {
103 | startScreenCapture();
104 | }
105 | });
106 |
107 | mButtonDestroy = (Button) findViewById(R.id.btn_destroy_virtual_display);
108 | mButtonDestroy.setEnabled(false);
109 | mButtonDestroy.setOnClickListener(new View.OnClickListener() {
110 | @Override
111 | public void onClick(View view) {
112 | stopScreenCapture();
113 | }
114 | });
115 |
116 | mButtonPlayVideo = (Button) findViewById(R.id.btn_play);
117 | mButtonPlayVideo.setEnabled(false);
118 | mButtonPlayVideo.setOnClickListener(new View.OnClickListener() {
119 | @Override
120 | public void onClick(View view) {
121 | if (mMediaPlayer == null) {
122 | Uri uri = Uri.parse(FILENAME);
123 | mMediaPlayer = MediaPlayer.create(MainActivity.this, uri, mSurfaceView.getHolder());
124 | } else {
125 | try {
126 | mMediaPlayer.prepare();
127 | } catch (IOException e) {
128 | e.printStackTrace();
129 | return;
130 | }
131 | }
132 | mMediaPlayer.start();
133 | mButtonCreate.setEnabled(false);
134 | mButtonDestroy.setEnabled(false);
135 | mButtonPlayVideo.setEnabled(false);
136 | mButtonStopVideo.setEnabled(true);
137 | }
138 | });
139 |
140 | mButtonStopVideo = (Button) findViewById(R.id.btn_stop);
141 | mButtonStopVideo.setEnabled(false);
142 | mButtonStopVideo.setOnClickListener(new View.OnClickListener() {
143 | @Override
144 | public void onClick(View view) {
145 | mMediaPlayer.stop();
146 | mButtonCreate.setEnabled(true);
147 | mButtonDestroy.setEnabled(false);
148 | mButtonPlayVideo.setEnabled(true);
149 | mButtonStopVideo.setEnabled(false);
150 | }
151 | });
152 |
153 | // Check if we have write permission
154 | int permission = ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE);
155 | if (permission != PackageManager.PERMISSION_GRANTED) {
156 | Log.w(TAG, "Write permissions is not granted");
157 | // Request permissions
158 | ActivityCompat.requestPermissions(this,
159 | new String[] {Manifest.permission.WRITE_EXTERNAL_STORAGE},
160 | EXTERNAL_STORAGE_PERMISSION_CODE);
161 | } else {
162 | Log.i(TAG, "Write permission is granted!");
163 | mButtonCreate.setEnabled(true);
164 | }
165 | }
166 |
167 | @Override
168 | public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) {
169 | switch(requestCode) {
170 | case EXTERNAL_STORAGE_PERMISSION_CODE: {
171 | // If request is cancelled, the result arrays are empty.
172 | if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
173 | Log.i(TAG, "Write permission is granted!");
174 | mButtonCreate.setEnabled(true);
175 | } else {
176 | Toast.makeText(this, "Write permission is not granted", Toast.LENGTH_LONG).show();
177 | }
178 | return;
179 | }
180 | }
181 | }
182 |
183 | @Override
184 | protected void onStart() {
185 | super.onStart();
186 | Log.d(TAG, "onStart");
187 | }
188 |
189 | @Override
190 | protected void onResume() {
191 | super.onResume();
192 | Log.d(TAG, "onResume");
193 | }
194 |
195 | @Override
196 | protected void onPause() {
197 | super.onPause();
198 | Log.d(TAG, "onPause");
199 | destroyVirtualDisplay();
200 | if (mMediaPlayer != null) {
201 | mMediaPlayer.release();
202 | mMediaPlayer = null;
203 | mButtonPlayVideo.setEnabled(false);
204 | mButtonStopVideo.setEnabled(false);
205 | }
206 | }
207 |
208 | @Override
209 | protected void onStop() {
210 | super.onStop();
211 | Log.d(TAG, "onStop");
212 | }
213 |
214 | @Override
215 | protected void onDestroy() {
216 | super.onDestroy();
217 | Log.d(TAG, "onDestroy");
218 | mDisplayManager.unregisterDisplayListener(mDisplayListener);
219 | if (mProjection != null) {
220 | Log.i(TAG, "Stop media projection");
221 | mProjection.unregisterCallback(mProjectionCallback);
222 | mProjection.stop();
223 | mProjection = null;
224 | }
225 | mMediaRecorder.release();
226 | }
227 |
228 | private void startScreenCapture() {
229 | if (mProjection != null) {
230 | // start virtual display
231 | Log.i(TAG, "The media projection is already gotten");
232 | createVirtualDisplay();
233 | } else if (mResultCode != 0 && mResultData != null) {
234 | // get media projection
235 | Log.i(TAG, "Get media projection with the existing permission");
236 | mProjection = getProjection();
237 | createVirtualDisplay();
238 | } else {
239 | Log.i(TAG, "Request the permission for media projection");
240 | startActivityForResult(mProjectionManager.createScreenCaptureIntent(), SCREEN_CAPTURE_PERMISSION_CODE);
241 | }
242 | }
243 |
244 | private void stopScreenCapture() {
245 | destroyVirtualDisplay();
246 | }
247 |
248 | @Override
249 | public void onActivityResult(int requestCode, int resultCode, Intent data) {
250 | mResultCode = resultCode;
251 | mResultData = data;
252 | if (requestCode != SCREEN_CAPTURE_PERMISSION_CODE) {
253 | Toast.makeText(this, "Unknown request code: " + requestCode, Toast.LENGTH_SHORT).show();
254 | return;
255 | }
256 | if (resultCode != RESULT_OK) {
257 | Toast.makeText(this, "Screen Cast Permission Denied", Toast.LENGTH_SHORT).show();
258 | return;
259 | }
260 | Log.i(TAG, "Get media projection with the new permission");
261 | mProjection = getProjection();
262 | createVirtualDisplay();
263 | }
264 |
265 | private MediaProjection getProjection() {
266 | MediaProjection projection = mProjectionManager.getMediaProjection(mResultCode, mResultData);
267 | // Add a callback to be informed if the projection
268 | // will be stopped from the status bar.
269 | mProjectionCallback = new MediaProjection.Callback() {
270 | @Override
271 | public void onStop() {
272 | Log.d(TAG, "MediaProjection.Callback onStop obj:" + toString());
273 | destroyVirtualDisplay();
274 | mProjection = null;
275 | }
276 | };
277 | projection.registerCallback(mProjectionCallback, null);
278 | return projection;
279 | }
280 |
281 | private void createVirtualDisplay() {
282 | if (mProjection != null && mVirtualDisplay == null) {
283 | Log.d(TAG, "createVirtualDisplay WxH (px): " + mWidth + "x" + mHeight +
284 | ", dpi: " + mMetrics.densityDpi);
285 | if (!prepareMediaRecorder(mWidth, mHeight, FRAMERATE, FILENAME)) {
286 | Toast.makeText(this, "Can't prepare MediaRecorder", Toast.LENGTH_LONG).show();
287 | return;
288 | }
289 | int flags = DisplayManager.VIRTUAL_DISPLAY_FLAG_PRESENTATION;
290 | //flags |= DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC;
291 | mVirtualDisplay = mProjection.createVirtualDisplay("MyVirtualDisplay",
292 | mWidth, mHeight, mMetrics.densityDpi, flags, mMediaRecorder.getSurface(),
293 | null /*Callbacks*/, null /*Handler*/);
294 | mButtonCreate.setEnabled(false);
295 | mButtonDestroy.setEnabled(true);
296 | mButtonPlayVideo.setEnabled(false);
297 | // Release the previous instance of media player before recording new data into the same file.
298 | if (mMediaPlayer != null) {
299 | mMediaPlayer.release();
300 | mMediaPlayer = null;
301 | }
302 | // Start recording the content of MediaRecorder surface rendering by VirtualDisplay
303 | // into file.
304 | mMediaRecorder.start();
305 | }
306 | }
307 |
308 | private void destroyVirtualDisplay() {
309 | Log.d(TAG, "destroyVirtualDisplay");
310 | if (mVirtualDisplay != null) {
311 | Log.d(TAG, "destroyVirtualDisplay release");
312 | mVirtualDisplay.release();
313 | mVirtualDisplay = null;
314 | mMediaRecorder.stop();
315 | }
316 | mButtonDestroy.setEnabled(false);
317 | mButtonCreate.setEnabled(true);
318 | mButtonPlayVideo.setEnabled(true);
319 | }
320 |
321 | private boolean prepareMediaRecorder(int width, int height, int framerate, String filename) {
322 | Size sz = new Size(width, height);
323 | boolean supported = RecorderHelper.isSupportedByAVCEncoder(sz, framerate);
324 | if (!supported) {
325 | Log.e(TAG, "The combination of video size and framerate is not supported by MediaCodec");
326 | return false;
327 | }
328 |
329 | mMediaRecorder.setVideoSource(MediaRecorder.VideoSource.SURFACE);
330 | mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4);
331 | mMediaRecorder.setVideoEncodingBitRate(RecorderHelper.getVideoBitRate(sz));
332 | mMediaRecorder.setVideoFrameRate(framerate);
333 | mMediaRecorder.setVideoSize(sz.getWidth(), sz.getHeight());
334 | mMediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.H264);
335 | mMediaRecorder.setOutputFile(filename);
336 | try {
337 | mMediaRecorder.prepare();
338 | } catch (IOException e) {
339 | e.printStackTrace();
340 | Log.e(TAG, "Prepare MediaRecorder is failed");
341 | return false;
342 | }
343 |
344 | return true;
345 | }
346 |
347 | private final DisplayManager.DisplayListener mDisplayListener = new DisplayManager.DisplayListener() {
348 |
349 | private boolean mNewDisplayAdded = false;
350 | private int mCurrentDisplayId = -1;
351 | private DemoPresentation mPresentation;
352 |
353 | @Override
354 | public void onDisplayAdded(int i) {
355 | Log.d(TAG, "onDisplayAdded id=" + i);
356 | if (!mNewDisplayAdded && mCurrentDisplayId == -1) {
357 | mNewDisplayAdded = true;
358 | mCurrentDisplayId = i;
359 | }
360 | }
361 |
362 | @Override
363 | public void onDisplayRemoved(int i) {
364 | Log.d(TAG, "onDisplayRemoved id=" + i);
365 | if (mCurrentDisplayId == i) {
366 | mNewDisplayAdded = false;
367 | mCurrentDisplayId = -1;
368 | if (mPresentation != null) {
369 | mPresentation.dismiss();
370 | mPresentation = null;
371 | }
372 | }
373 | }
374 |
375 | @Override
376 | public void onDisplayChanged(int i) {
377 | Log.d(TAG, "onDisplayChanged id=" + i);
378 | if (mCurrentDisplayId == i) {
379 | if (mNewDisplayAdded) {
380 | // create a presentation
381 | mNewDisplayAdded = false;
382 | Display display = mDisplayManager.getDisplay(i);
383 | mPresentation = new DemoPresentation(MainActivity.this, display);
384 | mPresentation.show();
385 | }
386 | }
387 | }
388 | };
389 | }
390 |
--------------------------------------------------------------------------------
/app/src/main/java/com/andronblog/presentationonvirtualdisplay/RecorderHelper.java:
--------------------------------------------------------------------------------
1 | package com.andronblog.presentationonvirtualdisplay;
2 |
3 | import android.media.MediaCodecInfo;
4 | import android.media.MediaCodecList;
5 | import android.util.Log;
6 | import android.util.Size;
7 |
8 | public class RecorderHelper {
9 |
10 | private final static String TAG = "MediaRecorderHelper";
11 | private final static boolean VERBOSE = true;
12 |
13 | private static final int BIT_RATE_1080P = 16000000;
14 | private static final int BIT_RATE_MIN = 64000;
15 | private static final int BIT_RATE_MAX = 40000000;
16 |
17 | /**
18 | * Calculate a video bit rate based on the size. The bit rate is scaled
19 | * based on ratio of video size to 1080p size.
20 | */
21 | public static int getVideoBitRate(Size sz) {
22 | int rate = BIT_RATE_1080P;
23 | float scaleFactor = sz.getHeight() * sz.getWidth() / (float)(1920 * 1080);
24 | rate = (int)(rate * scaleFactor);
25 |
26 | // Clamp to the MIN, MAX range.
27 | return Math.max(BIT_RATE_MIN, Math.min(BIT_RATE_MAX, rate));
28 | }
29 |
30 | /**
31 | * Check if encoder can support this size and frame rate combination by querying
32 | * MediaCodec capability. Check is based on size and frame rate. Ignore the bit rate
33 | * as the bit rates targeted in this test are well below the bit rate max value specified
34 | * by AVC specification for certain level.
35 | */
36 | public static boolean isSupportedByAVCEncoder(Size sz, int frameRate) {
37 | String mimeType = "video/avc";
38 | MediaCodecInfo codecInfo = getEncoderInfo(mimeType);
39 | if (codecInfo == null) {
40 | return false;
41 | }
42 | MediaCodecInfo.CodecCapabilities cap = codecInfo.getCapabilitiesForType(mimeType);
43 | if (cap == null) {
44 | return false;
45 | }
46 |
47 | int highestLevel = 0;
48 | for (MediaCodecInfo.CodecProfileLevel lvl : cap.profileLevels) {
49 | if (lvl.level > highestLevel) {
50 | highestLevel = lvl.level;
51 | }
52 | }
53 |
54 | if(VERBOSE) {
55 | Log.v(TAG, "The highest level supported by encoder is: " + highestLevel);
56 | }
57 |
58 | // Put bitRate here for future use.
59 | int maxW, maxH, bitRate;
60 | // Max encoding speed.
61 | int maxMacroblocksPerSecond = 0;
62 | switch(highestLevel) {
63 | case MediaCodecInfo.CodecProfileLevel.AVCLevel21:
64 | maxW = 352;
65 | maxH = 576;
66 | bitRate = 4000000;
67 | maxMacroblocksPerSecond = 19800;
68 | break;
69 | case MediaCodecInfo.CodecProfileLevel.AVCLevel22:
70 | maxW = 720;
71 | maxH = 480;
72 | bitRate = 4000000;
73 | maxMacroblocksPerSecond = 20250;
74 | break;
75 | case MediaCodecInfo.CodecProfileLevel.AVCLevel3:
76 | maxW = 720;
77 | maxH = 480;
78 | bitRate = 10000000;
79 | maxMacroblocksPerSecond = 40500;
80 | break;
81 | case MediaCodecInfo.CodecProfileLevel.AVCLevel31:
82 | maxW = 1280;
83 | maxH = 720;
84 | bitRate = 14000000;
85 | maxMacroblocksPerSecond = 108000;
86 | break;
87 | case MediaCodecInfo.CodecProfileLevel.AVCLevel32:
88 | maxW = 1280;
89 | maxH = 720;
90 | bitRate = 20000000;
91 | maxMacroblocksPerSecond = 216000;
92 | break;
93 | case MediaCodecInfo.CodecProfileLevel.AVCLevel4:
94 | maxW = 1920;
95 | maxH = 1088; // It should be 1088 in terms of AVC capability.
96 | bitRate = 20000000;
97 | maxMacroblocksPerSecond = 245760;
98 | break;
99 | case MediaCodecInfo.CodecProfileLevel.AVCLevel41:
100 | maxW = 1920;
101 | maxH = 1088; // It should be 1088 in terms of AVC capability.
102 | bitRate = 50000000;
103 | maxMacroblocksPerSecond = 245760;
104 | break;
105 | case MediaCodecInfo.CodecProfileLevel.AVCLevel42:
106 | maxW = 2048;
107 | maxH = 1088; // It should be 1088 in terms of AVC capability.
108 | bitRate = 50000000;
109 | maxMacroblocksPerSecond = 522240;
110 | break;
111 | case MediaCodecInfo.CodecProfileLevel.AVCLevel5:
112 | maxW = 3672;
113 | maxH = 1536;
114 | bitRate = 135000000;
115 | maxMacroblocksPerSecond = 589824;
116 | break;
117 | case MediaCodecInfo.CodecProfileLevel.AVCLevel51:
118 | default:
119 | maxW = 4096;
120 | maxH = 2304;
121 | bitRate = 240000000;
122 | maxMacroblocksPerSecond = 983040;
123 | break;
124 | }
125 |
126 | // Check size limit.
127 | if (sz.getWidth() > maxW || sz.getHeight() > maxH) {
128 | Log.i(TAG, "Requested resolution " + sz.toString() + " exceeds (" +
129 | maxW + "," + maxH + ")");
130 | return false;
131 | }
132 |
133 | // Check frame rate limit.
134 | Size sizeInMb = new Size((sz.getWidth() + 15) / 16, (sz.getHeight() + 15) / 16);
135 | int maxFps = maxMacroblocksPerSecond / (sizeInMb.getWidth() * sizeInMb.getHeight());
136 | if (frameRate > maxFps) {
137 | Log.i(TAG, "Requested frame rate " + frameRate + " exceeds " + maxFps);
138 | return false;
139 | }
140 |
141 | return true;
142 | }
143 |
144 |
145 | private static MediaCodecInfo getEncoderInfo(String mimeType) {
146 | int numCodecs = MediaCodecList.getCodecCount();
147 | for (int i = 0; i < numCodecs; i++) {
148 | MediaCodecInfo codecInfo = MediaCodecList.getCodecInfoAt(i);
149 |
150 | if (!codecInfo.isEncoder()) {
151 | continue;
152 | }
153 |
154 | String[] types = codecInfo.getSupportedTypes();
155 | for (int j = 0; j < types.length; j++) {
156 | if (types[j].equalsIgnoreCase(mimeType)) {
157 | return codecInfo;
158 | }
159 | }
160 | }
161 | return null;
162 | }
163 | }
164 |
--------------------------------------------------------------------------------
/app/src/main/res/anim/rotator.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
13 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
6 |
7 |
13 |
14 |
15 |
22 |
23 |
30 |
31 |
38 |
39 |
40 |
41 |
48 |
49 |
50 |
58 |
59 |
66 |
67 |
74 |
75 |
76 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/my_presentation.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
13 |
14 |
23 |
24 |
33 |
34 |
--------------------------------------------------------------------------------
/app/src/main/res/menu/menu_main.xml:
--------------------------------------------------------------------------------
1 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/andronmobi/PresentationOnVirtualDisplay/22fd06e49553592ae2db46430d4b36f497b098bd/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/andronmobi/PresentationOnVirtualDisplay/22fd06e49553592ae2db46430d4b36f497b098bd/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/andronmobi/PresentationOnVirtualDisplay/22fd06e49553592ae2db46430d4b36f497b098bd/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/andronmobi/PresentationOnVirtualDisplay/22fd06e49553592ae2db46430d4b36f497b098bd/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/values-v21/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/values-w820dp/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 64dp
6 |
7 |
--------------------------------------------------------------------------------
/app/src/main/res/values/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 16dp
4 | 16dp
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | PresentationOnVirtualDisplay
3 |
4 | Settings
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/build.gradle:
--------------------------------------------------------------------------------
1 | // Top-level build file where you can add configuration options common to all sub-projects/modules.
2 |
3 | buildscript {
4 | repositories {
5 | jcenter()
6 | }
7 | dependencies {
8 | classpath 'com.android.tools.build:gradle:1.3.0'
9 |
10 | // NOTE: Do not place your application dependencies here; they belong
11 | // in the individual module build.gradle files
12 | }
13 | }
14 |
15 | allprojects {
16 | repositories {
17 | jcenter()
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 |
3 | # IDE (e.g. Android Studio) users:
4 | # Gradle settings configured through the IDE *will override*
5 | # any settings specified in this file.
6 |
7 | # For more details on how to configure your build environment visit
8 | # http://www.gradle.org/docs/current/userguide/build_environment.html
9 |
10 | # Specifies the JVM arguments used for the daemon process.
11 | # The setting is particularly useful for tweaking memory settings.
12 | # Default value: -Xmx10248m -XX:MaxPermSize=256m
13 | # org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
14 |
15 | # When configured, Gradle will run in incubating parallel mode.
16 | # This option should only be used with decoupled projects. More details, visit
17 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
18 | # org.gradle.parallel=true
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/andronmobi/PresentationOnVirtualDisplay/22fd06e49553592ae2db46430d4b36f497b098bd/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Wed Nov 04 14:32:23 CET 2015
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-2.4-all.zip
7 |
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | ##############################################################################
4 | ##
5 | ## Gradle start up script for UN*X
6 | ##
7 | ##############################################################################
8 |
9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
10 | DEFAULT_JVM_OPTS=""
11 |
12 | APP_NAME="Gradle"
13 | APP_BASE_NAME=`basename "$0"`
14 |
15 | # Use the maximum available, or set MAX_FD != -1 to use that value.
16 | MAX_FD="maximum"
17 |
18 | warn ( ) {
19 | echo "$*"
20 | }
21 |
22 | die ( ) {
23 | echo
24 | echo "$*"
25 | echo
26 | exit 1
27 | }
28 |
29 | # OS specific support (must be 'true' or 'false').
30 | cygwin=false
31 | msys=false
32 | darwin=false
33 | case "`uname`" in
34 | CYGWIN* )
35 | cygwin=true
36 | ;;
37 | Darwin* )
38 | darwin=true
39 | ;;
40 | MINGW* )
41 | msys=true
42 | ;;
43 | esac
44 |
45 | # For Cygwin, ensure paths are in UNIX format before anything is touched.
46 | if $cygwin ; then
47 | [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
48 | fi
49 |
50 | # Attempt to set APP_HOME
51 | # Resolve links: $0 may be a link
52 | PRG="$0"
53 | # Need this for relative symlinks.
54 | while [ -h "$PRG" ] ; do
55 | ls=`ls -ld "$PRG"`
56 | link=`expr "$ls" : '.*-> \(.*\)$'`
57 | if expr "$link" : '/.*' > /dev/null; then
58 | PRG="$link"
59 | else
60 | PRG=`dirname "$PRG"`"/$link"
61 | fi
62 | done
63 | SAVED="`pwd`"
64 | cd "`dirname \"$PRG\"`/" >&-
65 | APP_HOME="`pwd -P`"
66 | cd "$SAVED" >&-
67 |
68 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
69 |
70 | # Determine the Java command to use to start the JVM.
71 | if [ -n "$JAVA_HOME" ] ; then
72 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
73 | # IBM's JDK on AIX uses strange locations for the executables
74 | JAVACMD="$JAVA_HOME/jre/sh/java"
75 | else
76 | JAVACMD="$JAVA_HOME/bin/java"
77 | fi
78 | if [ ! -x "$JAVACMD" ] ; then
79 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
80 |
81 | Please set the JAVA_HOME variable in your environment to match the
82 | location of your Java installation."
83 | fi
84 | else
85 | JAVACMD="java"
86 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
87 |
88 | Please set the JAVA_HOME variable in your environment to match the
89 | location of your Java installation."
90 | fi
91 |
92 | # Increase the maximum file descriptors if we can.
93 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
94 | MAX_FD_LIMIT=`ulimit -H -n`
95 | if [ $? -eq 0 ] ; then
96 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
97 | MAX_FD="$MAX_FD_LIMIT"
98 | fi
99 | ulimit -n $MAX_FD
100 | if [ $? -ne 0 ] ; then
101 | warn "Could not set maximum file descriptor limit: $MAX_FD"
102 | fi
103 | else
104 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
105 | fi
106 | fi
107 |
108 | # For Darwin, add options to specify how the application appears in the dock
109 | if $darwin; then
110 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
111 | fi
112 |
113 | # For Cygwin, switch paths to Windows format before running java
114 | if $cygwin ; then
115 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
116 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
117 |
118 | # We build the pattern for arguments to be converted via cygpath
119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
120 | SEP=""
121 | for dir in $ROOTDIRSRAW ; do
122 | ROOTDIRS="$ROOTDIRS$SEP$dir"
123 | SEP="|"
124 | done
125 | OURCYGPATTERN="(^($ROOTDIRS))"
126 | # Add a user-defined pattern to the cygpath arguments
127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
129 | fi
130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
131 | i=0
132 | for arg in "$@" ; do
133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
135 |
136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
138 | else
139 | eval `echo args$i`="\"$arg\""
140 | fi
141 | i=$((i+1))
142 | done
143 | case $i in
144 | (0) set -- ;;
145 | (1) set -- "$args0" ;;
146 | (2) set -- "$args0" "$args1" ;;
147 | (3) set -- "$args0" "$args1" "$args2" ;;
148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
154 | esac
155 | fi
156 |
157 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
158 | function splitJvmOpts() {
159 | JVM_OPTS=("$@")
160 | }
161 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
162 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
163 |
164 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
165 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @if "%DEBUG%" == "" @echo off
2 | @rem ##########################################################################
3 | @rem
4 | @rem Gradle startup script for Windows
5 | @rem
6 | @rem ##########################################################################
7 |
8 | @rem Set local scope for the variables with windows NT shell
9 | if "%OS%"=="Windows_NT" setlocal
10 |
11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
12 | set DEFAULT_JVM_OPTS=
13 |
14 | set DIRNAME=%~dp0
15 | if "%DIRNAME%" == "" set DIRNAME=.
16 | set APP_BASE_NAME=%~n0
17 | set APP_HOME=%DIRNAME%
18 |
19 | @rem Find java.exe
20 | if defined JAVA_HOME goto findJavaFromJavaHome
21 |
22 | set JAVA_EXE=java.exe
23 | %JAVA_EXE% -version >NUL 2>&1
24 | if "%ERRORLEVEL%" == "0" goto init
25 |
26 | echo.
27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28 | echo.
29 | echo Please set the JAVA_HOME variable in your environment to match the
30 | echo location of your Java installation.
31 |
32 | goto fail
33 |
34 | :findJavaFromJavaHome
35 | set JAVA_HOME=%JAVA_HOME:"=%
36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37 |
38 | if exist "%JAVA_EXE%" goto init
39 |
40 | echo.
41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42 | echo.
43 | echo Please set the JAVA_HOME variable in your environment to match the
44 | echo location of your Java installation.
45 |
46 | goto fail
47 |
48 | :init
49 | @rem Get command-line arguments, handling Windowz variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 | if "%@eval[2+2]" == "4" goto 4NT_args
53 |
54 | :win9xME_args
55 | @rem Slurp the command line arguments.
56 | set CMD_LINE_ARGS=
57 | set _SKIP=2
58 |
59 | :win9xME_args_slurp
60 | if "x%~1" == "x" goto execute
61 |
62 | set CMD_LINE_ARGS=%*
63 | goto execute
64 |
65 | :4NT_args
66 | @rem Get arguments from the 4NT Shell from JP Software
67 | set CMD_LINE_ARGS=%$
68 |
69 | :execute
70 | @rem Setup the command line
71 |
72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
73 |
74 | @rem Execute Gradle
75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
76 |
77 | :end
78 | @rem End local scope for the variables with windows NT shell
79 | if "%ERRORLEVEL%"=="0" goto mainEnd
80 |
81 | :fail
82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
83 | rem the _cmd.exe /c_ return code!
84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
85 | exit /b 1
86 |
87 | :mainEnd
88 | if "%OS%"=="Windows_NT" endlocal
89 |
90 | :omega
91 |
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app'
2 |
--------------------------------------------------------------------------------