diff --git a/.idea/caches/build_file_checksums.ser b/.idea/caches/build_file_checksums.ser index 753c055..0868703 100644 Binary files a/.idea/caches/build_file_checksums.ser and b/.idea/caches/build_file_checksums.ser differ diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..94a25f7 --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/app/build.gradle b/app/build.gradle index 3681459..393523c 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -1,11 +1,11 @@ apply plugin: 'com.android.application' android { - compileSdkVersion 28 + compileSdkVersion 27 defaultConfig { applicationId "com.deltaforce.siliconcupcake.x11screenrecorder" minSdkVersion 21 - targetSdkVersion 28 + targetSdkVersion 27 versionCode 1 versionName "1.0" testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" @@ -20,9 +20,10 @@ android { dependencies { implementation fileTree(dir: 'libs', include: ['*.jar']) - implementation 'com.android.support:appcompat-v7:28.0.0-beta01' + implementation 'com.android.support:appcompat-v7:27.1.1' implementation 'com.android.support.constraint:constraint-layout:1.1.2' testImplementation 'junit:junit:4.12' androidTestImplementation 'com.android.support.test:runner:1.0.2' + implementation 'com.android.support:design:27.1.1' androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2' } diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index ab170d1..4646ef3 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -1,6 +1,10 @@ + + + + = Build.VERSION_CODES.M && context != null && permissions != null) { + for (String permission : permissions) { + if (ActivityCompat.checkSelfPermission(context, permission) != PackageManager.PERMISSION_GRANTED) { + return false; + } + } + } + return true; + } +} diff --git a/app/src/main/java/com/deltaforce/siliconcupcake/x11screenrecorder/MainActivity.java b/app/src/main/java/com/deltaforce/siliconcupcake/x11screenrecorder/MainActivity.java index 2fa7fc1..4cd69c9 100644 --- a/app/src/main/java/com/deltaforce/siliconcupcake/x11screenrecorder/MainActivity.java +++ b/app/src/main/java/com/deltaforce/siliconcupcake/x11screenrecorder/MainActivity.java @@ -1,13 +1,267 @@ package com.deltaforce.siliconcupcake.x11screenrecorder; +import android.Manifest; +import android.content.Context; +import android.content.Intent; +import android.content.SharedPreferences; +import android.content.pm.PackageManager; +import android.hardware.display.DisplayManager; +import android.hardware.display.VirtualDisplay; +import android.media.MediaRecorder; +import android.media.projection.MediaProjection; +import android.media.projection.MediaProjectionManager; +import android.net.Uri; +import android.os.Environment; +import android.provider.Settings; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.support.design.widget.Snackbar; +import android.support.v4.app.ActivityCompat; +import android.support.v4.content.ContextCompat; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; +import android.util.DisplayMetrics; +import android.util.Log; +import android.util.SparseIntArray; +import android.view.Surface; +import android.view.View; +import android.widget.Toast; +import android.widget.ToggleButton; + +import java.io.IOException; public class MainActivity extends AppCompatActivity { + private static final String TAG = "MainActivity"; + private static final int REQUEST_CODE = 1000; + public ToggleButton toggleButton; + public boolean isRecording = false; + private int mScreenDensity; + private MediaProjectionManager mProjectionManager;//manages MediaProjection objects; + private static final int DISPLAY_WIDTH = 720; + private static final int DISPLAY_HEIGHT = 1280; + private MediaProjection mMediaProjection;//token grants ability to capture screen content; + private VirtualDisplay mVirtualDisplay; + private MediaProjectionCallback mMediaProjectionCallback; + public MediaRecorder mMediaRecorder; + private static final SparseIntArray ORIENTATIONS = new SparseIntArray(); + private static final int REQUEST_PERMISSION_KEY = 1; + private int counter = 0; + + static { + ORIENTATIONS.append(Surface.ROTATION_0, 90); + ORIENTATIONS.append(Surface.ROTATION_90, 0); + ORIENTATIONS.append(Surface.ROTATION_180, 270); + ORIENTATIONS.append(Surface.ROTATION_270, 180); + } + @Override - protected void onCreate(Bundle savedInstanceState) { + protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); + SharedPreferences prefs = getSharedPreferences("MyPref", MODE_PRIVATE); + counter = prefs.getInt("counter" , 0); + final String[] PERMISSIONS = { + Manifest.permission.WRITE_EXTERNAL_STORAGE, + Manifest.permission.READ_EXTERNAL_STORAGE, + Manifest.permission.RECORD_AUDIO + }; + Log.d("write permission", PERMISSIONS[0]); + Log.d("read permission", PERMISSIONS[1]); + if (!Function.hasPermissions(MainActivity.this, PERMISSIONS)) { + ActivityCompat.requestPermissions(MainActivity.this, PERMISSIONS, REQUEST_PERMISSION_KEY); + } + + DisplayMetrics metrics = new DisplayMetrics(); + getWindowManager().getDefaultDisplay().getMetrics(metrics); + mScreenDensity = metrics.densityDpi; + mMediaRecorder = new MediaRecorder(); + mProjectionManager = (MediaProjectionManager) getSystemService(Context.MEDIA_PROJECTION_SERVICE); + toggleButton = (ToggleButton) findViewById(R.id.toggleButton); + toggleButton.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + + + onToggleScreenShare(); + + + } + }); + + + } + + public void onToggleScreenShare() { + + + if (!isRecording) { + initRecorder(); + shareScreen(); + } else { + mMediaRecorder.stop(); + mMediaRecorder.reset(); + stopScreenSharing(); + } + + + } + + + private void initRecorder() { + try { + mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC); + mMediaRecorder.setVideoSource(MediaRecorder.VideoSource.SURFACE); + mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4); //THREE_GPP + mMediaRecorder.setOutputFile(Environment + .getExternalStoragePublicDirectory(Environment + .DIRECTORY_DOWNLOADS) + "/video"+String.valueOf(counter)+".mp4"); + counter++; + SharedPreferences pref = getApplicationContext().getSharedPreferences("MyPref", MODE_PRIVATE); + SharedPreferences.Editor editor = pref.edit(); + editor.putInt("counter" , counter); + editor.commit(); + mMediaRecorder.setVideoSize(DISPLAY_WIDTH, DISPLAY_HEIGHT); + mMediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.H264); + mMediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB); + mMediaRecorder.setVideoEncodingBitRate(512 * 1000); + mMediaRecorder.setVideoFrameRate(16); // 30 + mMediaRecorder.setVideoEncodingBitRate(3000000); + int rotation = getWindowManager().getDefaultDisplay().getRotation(); + int orientation = ORIENTATIONS.get(rotation + 90); + mMediaRecorder.setOrientationHint(orientation); + mMediaRecorder.prepare(); + } catch (IOException e) { + e.printStackTrace(); + } + } + + private void shareScreen() { + if (mMediaProjection == null) { + startActivityForResult(mProjectionManager.createScreenCaptureIntent(), REQUEST_CODE);//permission to record screen! + return; + } + mVirtualDisplay = createVirtualDisplay(); + mMediaRecorder.start(); + isRecording = true; + } + + @Override + public void onActivityResult(int requestCode, int resultCode, Intent data) { + if (requestCode != REQUEST_CODE) { + Log.e(TAG, "Unknown request code: " + requestCode); + return; + } + if (resultCode != RESULT_OK) { + Toast.makeText(this, "Screen Cast Permission Denied", Toast.LENGTH_SHORT).show(); + isRecording = false; + toggleButton.setChecked(false); + mMediaRecorder = new MediaRecorder(); + return; + } + Log.d("REQUEST_CODE", String.valueOf(REQUEST_CODE)); + mMediaProjectionCallback = new MediaProjectionCallback(); + mMediaProjection = mProjectionManager.getMediaProjection(resultCode, data); + mMediaProjection.registerCallback(mMediaProjectionCallback, null); + mVirtualDisplay = createVirtualDisplay(); + mMediaRecorder.start(); + isRecording = true; + toggleButton.setChecked(true); + } + + private VirtualDisplay createVirtualDisplay() { + return mMediaProjection.createVirtualDisplay("MainActivity", DISPLAY_WIDTH, DISPLAY_HEIGHT, mScreenDensity, + DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR, mMediaRecorder.getSurface(), null, null); + } + + + private void stopScreenSharing() { + if (mVirtualDisplay == null) { + return; + } + mVirtualDisplay.release(); + destroyMediaProjection(); + isRecording = false; + toggleButton.setChecked(false); + } + + private void destroyMediaProjection() { + if (mMediaProjection != null) { + mMediaProjection.unregisterCallback(mMediaProjectionCallback); + mMediaProjection.stop(); + mMediaProjection = null; + } + Log.i(TAG, "MediaProjection Stopped"); + } + + private class MediaProjectionCallback extends MediaProjection.Callback { + @Override + public void onStop() { + if (isRecording) { + isRecording = false; + toggleButton.setChecked(false); + mMediaRecorder.stop(); + mMediaRecorder.reset(); + } + mMediaProjection = null; + stopScreenSharing(); + } + } + + @Override + public void onDestroy() { + super.onDestroy(); + destroyMediaProjection(); + } + + @Override + public void onBackPressed() { + if (isRecording) { + Snackbar.make(findViewById(android.R.id.content), "Wanna Stop recording and exit?", + Snackbar.LENGTH_INDEFINITE).setAction("Stop", + new View.OnClickListener() { + @Override + public void onClick(View v) { + mMediaRecorder.stop(); + mMediaRecorder.reset(); + Log.v(TAG, "Stopping Recording"); + stopScreenSharing(); + finish(); + } + }).show(); + } else { + finish(); + } + } + + @Override + public void onRequestPermissionsResult(int requestCode, @NonNull String permissions[], @NonNull int[] grantResults) { + switch (requestCode) { + case REQUEST_PERMISSION_KEY: { + if ((grantResults.length > 0) && (grantResults[0] + grantResults[1]) == PackageManager.PERMISSION_GRANTED) { + Toast.makeText(this, "Screen Cast Permission Granted", Toast.LENGTH_SHORT).show(); + + } else { + isRecording = false; + toggleButton.setChecked(false); + Snackbar.make(findViewById(android.R.id.content), "Please enable Microphone and Storage permissions.", + Snackbar.LENGTH_INDEFINITE).setAction("ENABLE", + new View.OnClickListener() { + @Override + public void onClick(View v) { + Intent intent = new Intent(); + intent.setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS); + intent.addCategory(Intent.CATEGORY_DEFAULT); + intent.setData(Uri.parse("package:" + getPackageName())); + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + intent.addFlags(Intent.FLAG_ACTIVITY_NO_HISTORY); + intent.addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS); + startActivity(intent); + } + }).show(); + } + return; + } + } } } diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index 84f1951..eddd65d 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -1,18 +1,33 @@ - - - \ No newline at end of file + + + + + + + \ No newline at end of file