From 4969bc633a8414d56a324efbf91c3e22be33fe38 Mon Sep 17 00:00:00 2001 From: tylerjroach Date: Fri, 2 Oct 2015 10:20:06 -0400 Subject: [PATCH 1/4] Added a screenshot mode. --- .../com/jakewharton/telecine/Analytics.java | 2 + .../com/jakewharton/telecine/FlashView.java | 68 +++++ .../com/jakewharton/telecine/OverlayView.java | 9 + .../telecine/RecordingSession.java | 271 +++++++++++++++++- .../jakewharton/telecine/TelecineService.java | 4 + .../ic_camera_alt_white_24dp.png | Bin 0 -> 364 bytes .../ic_camera_alt_white_24dp.png | Bin 0 -> 240 bytes .../ic_camera_alt_white_24dp.png | Bin 0 -> 446 bytes .../ic_camera_alt_white_24dp.png | Bin 0 -> 666 bytes .../ic_camera_alt_white_24dp.png | Bin 0 -> 894 bytes .../res/drawable/screenshot_background.xml | 5 + telecine/src/main/res/layout/flash_view.xml | 6 + telecine/src/main/res/layout/overlay_view.xml | 9 + telecine/src/main/res/values/dimens.xml | 2 +- telecine/src/main/res/values/strings.xml | 2 + 15 files changed, 364 insertions(+), 14 deletions(-) create mode 100644 telecine/src/main/java/com/jakewharton/telecine/FlashView.java create mode 100644 telecine/src/main/res/drawable-hdpi/ic_camera_alt_white_24dp.png create mode 100644 telecine/src/main/res/drawable-mdpi/ic_camera_alt_white_24dp.png create mode 100644 telecine/src/main/res/drawable-xhdpi/ic_camera_alt_white_24dp.png create mode 100644 telecine/src/main/res/drawable-xxhdpi/ic_camera_alt_white_24dp.png create mode 100644 telecine/src/main/res/drawable-xxxhdpi/ic_camera_alt_white_24dp.png create mode 100644 telecine/src/main/res/drawable/screenshot_background.xml create mode 100644 telecine/src/main/res/layout/flash_view.xml diff --git a/telecine/src/main/java/com/jakewharton/telecine/Analytics.java b/telecine/src/main/java/com/jakewharton/telecine/Analytics.java index bf354aa..ee0a9e3 100644 --- a/telecine/src/main/java/com/jakewharton/telecine/Analytics.java +++ b/telecine/src/main/java/com/jakewharton/telecine/Analytics.java @@ -6,6 +6,7 @@ interface Analytics { String CATEGORY_SETTINGS = "Settings"; String CATEGORY_RECORDING = "Recording"; + String CATEGORY_SCREENSHOT = "Screenshot"; String CATEGORY_SHORTCUT = "Shortcut"; String ACTION_CAPTURE_INTENT_LAUNCH = "Launch Overlay Launch"; @@ -20,6 +21,7 @@ interface Analytics { String ACTION_OVERLAY_CANCEL = "Overlay Cancel"; String ACTION_RECORDING_START = "Recording Start"; String ACTION_RECORDING_STOP = "Recording Stop"; + String ACTION_SCREENSHOT_TAKEN = "Screenshot Taken"; String ACTION_SHORTCUT_ADDED = "Shortcut Added"; String ACTION_SHORTCUT_LAUNCHED = "Shortcut Launched"; diff --git a/telecine/src/main/java/com/jakewharton/telecine/FlashView.java b/telecine/src/main/java/com/jakewharton/telecine/FlashView.java new file mode 100644 index 0000000..49d5a3b --- /dev/null +++ b/telecine/src/main/java/com/jakewharton/telecine/FlashView.java @@ -0,0 +1,68 @@ +package com.jakewharton.telecine; + +import android.annotation.SuppressLint; +import android.content.Context; +import android.view.WindowManager; +import android.view.animation.DecelerateInterpolator; +import android.widget.FrameLayout; +import butterknife.ButterKnife; +import static android.graphics.PixelFormat.TRANSLUCENT; +import static android.view.ViewGroup.LayoutParams.MATCH_PARENT; +import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR; +import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN; +import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS; +import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; +import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL; +import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_ERROR; + +@SuppressLint("ViewConstructor") // Lint, in this case, I am smarter than you. +final class FlashView extends FrameLayout { + + private FlashView flashView; + private final Listener listener; + + static FlashView create(Context context, Listener listener) { + return new FlashView(context, listener); + } + + private FlashView(Context context, Listener listener) { + super(context); + + this.listener = listener; + inflate(context, R.layout.flash_view, this); + ButterKnife.bind(this); + } + + @Override + protected void onAttachedToWindow() { + super.onAttachedToWindow(); + + animate().alpha(100) + .setDuration(200) + .withEndAction(new Runnable() { + @Override + public void run() { + listener.onFlashComplete(); + } + }) + .setInterpolator(new DecelerateInterpolator()); + } + + static WindowManager.LayoutParams createLayoutParams() { + + final WindowManager.LayoutParams params = + new WindowManager.LayoutParams(MATCH_PARENT, MATCH_PARENT, TYPE_SYSTEM_ERROR, FLAG_NOT_FOCUSABLE + | FLAG_NOT_TOUCH_MODAL + | FLAG_LAYOUT_NO_LIMITS + | FLAG_LAYOUT_INSET_DECOR + | FLAG_LAYOUT_IN_SCREEN, TRANSLUCENT); + + return params; + } + + interface Listener { + /** Called when flash animation has completed. */ + void onFlashComplete(); + } + +} diff --git a/telecine/src/main/java/com/jakewharton/telecine/OverlayView.java b/telecine/src/main/java/com/jakewharton/telecine/OverlayView.java index 4a36005..bf72ddc 100644 --- a/telecine/src/main/java/com/jakewharton/telecine/OverlayView.java +++ b/telecine/src/main/java/com/jakewharton/telecine/OverlayView.java @@ -79,6 +79,11 @@ interface Listener { /** Called when stop is clicked. This view is unusable once this callback is invoked. */ void onStop(); + + void onScreenshot(); + /** Called when screenshot is clicked. This view will hide itself completely before invoking + * this callback. It will reappear once the screenshot has been saved. + */ } @Bind(R.id.record_overlay_buttons) View buttonsView; @@ -151,6 +156,10 @@ private OverlayView(Context context, Listener listener, boolean showCountDown) { }, showCountDown ? COUNTDOWN_DELAY : NON_COUNTDOWN_DELAY); } + @OnClick(R.id.record_overlay_screenshot) void onScreenshotClicked() { + listener.onScreenshot(); + } + private void startRecording() { recordingView.setVisibility(INVISIBLE); stopView.setVisibility(VISIBLE); diff --git a/telecine/src/main/java/com/jakewharton/telecine/RecordingSession.java b/telecine/src/main/java/com/jakewharton/telecine/RecordingSession.java index b61027b..fa51569 100644 --- a/telecine/src/main/java/com/jakewharton/telecine/RecordingSession.java +++ b/telecine/src/main/java/com/jakewharton/telecine/RecordingSession.java @@ -9,8 +9,11 @@ import android.content.Intent; import android.content.res.Configuration; import android.graphics.Bitmap; +import android.graphics.PixelFormat; import android.hardware.display.VirtualDisplay; import android.media.CamcorderProfile; +import android.media.Image; +import android.media.ImageReader; import android.media.MediaMetadataRetriever; import android.media.MediaRecorder; import android.media.MediaScannerConnection; @@ -21,6 +24,7 @@ import android.os.Environment; import android.os.Handler; import android.os.Looper; +import android.provider.MediaStore; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.util.DisplayMetrics; @@ -28,7 +32,9 @@ import android.view.WindowManager; import com.google.android.gms.analytics.HitBuilders; import java.io.File; +import java.io.FileOutputStream; import java.io.IOException; +import java.nio.ByteBuffer; import java.text.DateFormat; import java.text.SimpleDateFormat; import java.util.Date; @@ -47,13 +53,15 @@ import static android.media.MediaRecorder.OutputFormat.MPEG_4; import static android.media.MediaRecorder.VideoEncoder.H264; import static android.media.MediaRecorder.VideoSource.SURFACE; +import static android.os.Environment.DIRECTORY_DCIM; import static android.os.Environment.DIRECTORY_MOVIES; final class RecordingSession { static final int NOTIFICATION_ID = 522592; private static final String DISPLAY_NAME = "telecine"; - private static final String MIME_TYPE = "video/mp4"; + private static final String MIME_TYPE_RECORDING = "video/mp4"; + private static final String MIME_TYPE_SCREENSHOT = "image/jpeg"; interface Listener { /** Invoked immediately prior to the start of recording. */ @@ -64,6 +72,9 @@ interface Listener { /** Invoked after all work for this session has completed. */ void onEnd(); + + /**Invoked immediately prior to the start of screenshot. */ + void onScreenshot(); } private final Handler mainThread = new Handler(Looper.getMainLooper()); @@ -77,16 +88,21 @@ interface Listener { private final Provider showCountDown; private final Provider videoSizePercentage; - private final File outputRoot; - private final DateFormat fileFormat = + private final File videoOutputRoot; + private final File picturesOutputRoot; + private final DateFormat videofileFormat = new SimpleDateFormat("'Telecine_'yyyy-MM-dd-HH-mm-ss'.mp4'", Locale.US); + private final DateFormat audiofileFormat = + new SimpleDateFormat("'Telecine_'yyyy-MM-dd-HH-mm-ss'.jpeg'", Locale.US); private final NotificationManager notificationManager; private final WindowManager windowManager; private final MediaProjectionManager projectionManager; private OverlayView overlayView; + private FlashView flashView; private MediaRecorder recorder; + private ImageReader imageReader; private MediaProjection projection; private VirtualDisplay display; private String outputFile; @@ -104,8 +120,10 @@ interface Listener { this.showCountDown = showCountDown; this.videoSizePercentage = videoSizePercentage; - File picturesDir = Environment.getExternalStoragePublicDirectory(DIRECTORY_MOVIES); - outputRoot = new File(picturesDir, "Telecine"); + File moviesDir = Environment.getExternalStoragePublicDirectory(DIRECTORY_MOVIES); + videoOutputRoot = new File(moviesDir, "Telecine"); + File picturesDir = Environment.getExternalStoragePublicDirectory(DIRECTORY_DCIM); + picturesOutputRoot = new File(picturesDir, "Telecine"); notificationManager = (NotificationManager) context.getSystemService(NOTIFICATION_SERVICE); windowManager = (WindowManager) context.getSystemService(WINDOW_SERVICE); @@ -127,6 +145,19 @@ public void showOverlay() { @Override public void onStop() { stopRecording(); } + + @Override public void onScreenshot() { + overlayView.animate() + .alpha(0) + .setDuration(0) + .withEndAction(new Runnable() { + @Override + public void run() { + takeScreenshot(); + } + }); + + } }; overlayView = OverlayView.create(context, overlayListener, showCountDown.get()); windowManager.addView(overlayView, OverlayView.createLayoutParams(context)); @@ -186,11 +217,27 @@ private RecordingInfo getRecordingInfo() { cameraWidth, cameraHeight, sizePercentage); } + private RecordingInfo getScreenshotInfo() { + DisplayMetrics displayMetrics = new DisplayMetrics(); + WindowManager wm = (WindowManager) context.getSystemService(WINDOW_SERVICE); + wm.getDefaultDisplay().getRealMetrics(displayMetrics); + int displayWidth = displayMetrics.widthPixels; + int displayHeight = displayMetrics.heightPixels; + int displayDensity = displayMetrics.densityDpi; + Timber.d("Display size: %s x %s @ %s", displayWidth, displayHeight, displayDensity); + + Configuration configuration = context.getResources().getConfiguration(); + boolean isLandscape = configuration.orientation == ORIENTATION_LANDSCAPE; + Timber.d("Display landscape: %s", isLandscape); + + return new RecordingInfo(displayWidth, displayHeight, displayDensity); + } + private void startRecording() { Timber.d("Starting screen recording..."); - if (!outputRoot.mkdirs()) { - Timber.e("Unable to create output directory '%s'.", outputRoot.getAbsolutePath()); + if (!videoOutputRoot.mkdirs()) { + Timber.e("Unable to create output directory '%s'.", videoOutputRoot.getAbsolutePath()); // We're probably about to crash, but at least the log will indicate as to why. } @@ -206,8 +253,8 @@ private void startRecording() { recorder.setVideoSize(recordingInfo.width, recordingInfo.height); recorder.setVideoEncodingBitRate(8 * 1000 * 1000); - String outputName = fileFormat.format(new Date()); - outputFile = new File(outputRoot, outputName).getAbsolutePath(); + String outputName = videofileFormat.format(new Date()); + outputFile = new File(videoOutputRoot, outputName).getAbsolutePath(); Timber.i("Output file '%s'.", outputFile); recorder.setOutputFile(outputFile); @@ -232,9 +279,9 @@ private void startRecording() { Timber.d("Screen recording started."); analytics.send(new HitBuilders.EventBuilder() // - .setCategory(Analytics.CATEGORY_RECORDING) - .setAction(Analytics.ACTION_RECORDING_START) - .build()); + .setCategory(Analytics.CATEGORY_RECORDING) + .setAction(Analytics.ACTION_RECORDING_START) + .build()); } private void stopRecording() { @@ -285,12 +332,144 @@ private void stopRecording() { }); } + private void takeScreenshot() { + Timber.d("Start screenshot"); + + + + if (!picturesOutputRoot.mkdirs()) { + Timber.e("Unable to create output directory '%s'.", picturesOutputRoot.getAbsolutePath()); + // We're probably about to crash, but at least the log will indicate as to why. + } + + final RecordingInfo recordingInfo = getScreenshotInfo(); + Timber.d("Screenshot: %s x %s @ %s", recordingInfo.width, recordingInfo.height, + recordingInfo.density); + + String outputName = audiofileFormat.format(new Date()); + outputFile = new File(picturesOutputRoot, outputName).getAbsolutePath(); + Timber.i("Output file '%s'.", outputFile); + + projection = projectionManager.getMediaProjection(resultCode, data); + + imageReader = ImageReader.newInstance(recordingInfo.width, recordingInfo.height, PixelFormat.RGBA_8888, 2); + Surface surface = imageReader.getSurface(); + display = + projection.createVirtualDisplay(DISPLAY_NAME, recordingInfo.width, recordingInfo.height, + recordingInfo.density, VIRTUAL_DISPLAY_FLAG_PRESENTATION, surface, null, null); + + + imageReader.setOnImageAvailableListener(new ImageReader.OnImageAvailableListener() { + @Override + public void onImageAvailable(ImageReader reader) { + Timber.d("ImageReader: Image available"); + Image image = null; + FileOutputStream fos = null; + Bitmap bitmap = null; + Bitmap croppedBitmap = null; + + try { + image = imageReader.acquireLatestImage(); + if (image != null) { + + FlashView.Listener flashViewListener = new FlashView.Listener() { + @Override + public void onFlashComplete() { + if (flashView != null) { + windowManager.removeView(flashView); + flashView = null; + } + + } + }; + flashView = FlashView.create(context, flashViewListener); + windowManager.addView(flashView, FlashView.createLayoutParams()); + + Image.Plane[] planes = image.getPlanes(); + ByteBuffer buffer = planes[0].getBuffer(); + int pixelStride = planes[0].getPixelStride(); + int rowStride = planes[0].getRowStride(); + int rowPadding = rowStride - pixelStride * recordingInfo.width; + + // create bitmap + bitmap = Bitmap.createBitmap(recordingInfo.width + rowPadding / pixelStride, recordingInfo.height, Bitmap.Config.ARGB_8888); + bitmap.copyPixelsFromBuffer(buffer); + + //Trimming the bitmap to the w/h of the screen. For some reason, image reader adds more pixels to width. + croppedBitmap = Bitmap.createBitmap(bitmap, 0, 0, recordingInfo.width, recordingInfo.height); + + bitmap.recycle(); + bitmap = null; + + // write bitmap to a file + fos = new FileOutputStream(outputFile); + croppedBitmap.compress(Bitmap.CompressFormat.JPEG, 100, fos); + + Timber.d("Screenshot taken. Notifying media scanner of new screenshot."); + MediaScannerConnection.scanFile(context, new String[]{outputFile}, null, + new MediaScannerConnection.OnScanCompletedListener() { + @Override + public void onScanCompleted(String path, final Uri uri) { + Timber.d("Media scanner completed."); + mainThread.post(new Runnable() { + @Override + public void run() { + showScreenshotNotification(uri, null); + } + }); + } + }); + } + + } catch (Exception e) { + e.printStackTrace(); + Timber.e("Error converting image to jpeg"); + } finally { + if (fos != null) { + try { + fos.close(); + } catch (IOException ioe) { + ioe.printStackTrace(); + } + } + + if (bitmap != null) { + bitmap.recycle(); + } + + if (croppedBitmap != null) { + croppedBitmap.recycle(); + } + + if (image != null) { + image.close(); + } + + imageReader.close(); + display.release(); + projection.stop(); + + overlayView.animate() + .alpha(1) + .setDuration(0); + + analytics.send(new HitBuilders.EventBuilder() // + .setCategory(Analytics.CATEGORY_SCREENSHOT) + .setAction(Analytics.ACTION_SCREENSHOT_TAKEN) + .build()); + + Timber.d("Screenshot success"); + } + } + }, null); + } + private void showNotification(final Uri uri, Bitmap bitmap) { Intent viewIntent = new Intent(ACTION_VIEW, uri); PendingIntent pendingViewIntent = PendingIntent.getActivity(context, 0, viewIntent, 0); Intent shareIntent = new Intent(ACTION_SEND); - shareIntent.setType(MIME_TYPE); + shareIntent.setType(MIME_TYPE_RECORDING); shareIntent.putExtra(Intent.EXTRA_STREAM, uri); shareIntent = Intent.createChooser(shareIntent, null); PendingIntent pendingShareIntent = PendingIntent.getActivity(context, 0, shareIntent, 0); @@ -347,6 +526,72 @@ private void showNotification(final Uri uri, Bitmap bitmap) { }.execute(); } + private void showScreenshotNotification(final Uri uri, final Bitmap bitmap) { + Intent viewIntent = new Intent(ACTION_VIEW, uri); + PendingIntent pendingViewIntent = PendingIntent.getActivity(context, 0, viewIntent, 0); + + Intent shareIntent = new Intent(ACTION_SEND); + shareIntent.setType(MIME_TYPE_SCREENSHOT); + shareIntent.putExtra(Intent.EXTRA_STREAM, uri); + shareIntent = Intent.createChooser(shareIntent, null); + PendingIntent pendingShareIntent = PendingIntent.getActivity(context, 0, shareIntent, 0); + + Intent deleteIntent = new Intent(context, DeleteRecordingBroadcastReceiver.class); + deleteIntent.setData(uri); + PendingIntent pendingDeleteIntent = PendingIntent.getBroadcast(context, 0, deleteIntent, 0); + + CharSequence title = context.getText(R.string.notification_screenshot_captured_title); + CharSequence subtitle = context.getText(R.string.notification_screenshot_captured_subtitle); + CharSequence share = context.getText(R.string.notification_captured_share); + CharSequence delete = context.getText(R.string.notification_captured_delete); + Notification.Builder builder = new Notification.Builder(context) // + .setContentTitle(title) + .setContentText(subtitle) + .setWhen(System.currentTimeMillis()) + .setShowWhen(true) + .setSmallIcon(R.drawable.ic_camera_alt_white_24dp) + .setColor(context.getResources().getColor(R.color.primary_normal)) + .setContentIntent(pendingViewIntent) + .setAutoCancel(true) + .addAction(R.drawable.ic_share_white_24dp, share, pendingShareIntent) + .addAction(R.drawable.ic_delete_white_24dp, delete, pendingDeleteIntent); + + if (bitmap != null) { + builder.setLargeIcon(createSquareBitmap(bitmap)) + .setStyle(new Notification.BigPictureStyle() // + .setBigContentTitle(title) // + .setSummaryText(subtitle) // + .bigPicture(bitmap)); + } + + notificationManager.notify(NOTIFICATION_ID, builder.build()); + + if (bitmap != null) { + listener.onEnd(); + return; + } + + new AsyncTask() { + @Override protected Bitmap doInBackground(@NonNull Void... none) { + Bitmap bitmap = null; + try { + bitmap = MediaStore.Images.Media.getBitmap(context.getContentResolver(), uri); + } catch (Exception e) { + Timber.d("Failed to create bitmap from screenshot file."); + } + return bitmap; + } + + @Override protected void onPostExecute(@Nullable Bitmap bitmap) { + if (bitmap != null) { + showScreenshotNotification(uri, bitmap); + } else { + listener.onEnd(); + } + } + }.execute(); + } + static RecordingInfo calculateRecordingInfo(int displayWidth, int displayHeight, int displayDensity, boolean isLandscapeDevice, int cameraWidth, int cameraHeight, int sizePercentage) { diff --git a/telecine/src/main/java/com/jakewharton/telecine/TelecineService.java b/telecine/src/main/java/com/jakewharton/telecine/TelecineService.java index 74243c4..62bace4 100644 --- a/telecine/src/main/java/com/jakewharton/telecine/TelecineService.java +++ b/telecine/src/main/java/com/jakewharton/telecine/TelecineService.java @@ -72,6 +72,10 @@ public static Intent newIntent(Context context, int resultCode, Intent data) { stopForeground(true /* remove notification */); } + @Override public void onScreenshot() { + + } + @Override public void onEnd() { Timber.d("Shutting down."); stopSelf(); diff --git a/telecine/src/main/res/drawable-hdpi/ic_camera_alt_white_24dp.png b/telecine/src/main/res/drawable-hdpi/ic_camera_alt_white_24dp.png new file mode 100644 index 0000000000000000000000000000000000000000..497c88ca82b139d8523f62d272569b97777cdec7 GIT binary patch literal 364 zcmV-y0h9iTP)S{uX;NkkS)#&21u+#OWT?<*8xnfup2(UyYu`|TWayHRle3^B=LUseC>Hz3 z2?03L6F2Y)Y1vJelVK0EWmn2Ql$Ly?Q}HYJ&`ey8O(uTi9vWT@jom{_aSLoF;#2pK zl4W-|8up!*EPLl3g1*=uhfkRYaWVLWw$#P9nUaw)p(W=g$}a?K8bsDKc?-L=1LM-h zsPJb*v0}uSlpQ7f(8IPT6#ZR~|J)2!gMLSvx$!&t+WdOxKlA~ZoD~H=l;~Ih0000< KMNUMnLSTYiKb+YB literal 0 HcmV?d00001 diff --git a/telecine/src/main/res/drawable-mdpi/ic_camera_alt_white_24dp.png b/telecine/src/main/res/drawable-mdpi/ic_camera_alt_white_24dp.png new file mode 100644 index 0000000000000000000000000000000000000000..e830522008b0a1b1f39fdde1156ff1bae3f955e5 GIT binary patch literal 240 zcmVHkAXPXi!ebT=lL4K1x)6Wg_MY91 zzt7rrmFO;X_oUmq$FIPH+E8b!7Su|rTTt3JU}I)<+1$1SJPXOxL=vhYuo3Q@z)==< z{b1(CNz*~H=m#TH*Ja}&F!>pnOOBdeCCh%W6|%$U`K_?;2UJ3mWl4Bp2(Ys-zHDi) q+wWiFtfaRVtkebP_bn*je*_=o)*|^)1w=Uj0000S3bFt5nS?tU*5U+ zuRv$a3fXL!qPq!nmZ-`S-6wEv!Im}!G@?dn#;*=rm4ZHT$3Hy5 zK#ky=d(UOmFsQV^o<2gJx=}eIwnA3W7xau83lN!8vECcV2)4w<^v@2~67MD11sAjh ztGUt}LA${P1=0J3E1eNGc`3L+Gf5t=WKEt5E{OV^X;qWQ4p$&rl*20-lc(iB^4yia zijQ0lF6anWaiv8;dol|xA!y6No&?ck78<-*{Fa&B9E#TSq3mYYuttQ*QZ*_=*n0M$ zV!JjJ9`z`_U5gAzIwsc`sys4xbSaS{W>lIoJ$6TM=qna%_I>f1qB5iXUVh&=cfY<* oSSx7G$evyZ)YnOH5*(G_7e~OF#v^w+0000007*qoM6N<$g3E)#tN;K2 literal 0 HcmV?d00001 diff --git a/telecine/src/main/res/drawable-xxhdpi/ic_camera_alt_white_24dp.png b/telecine/src/main/res/drawable-xxhdpi/ic_camera_alt_white_24dp.png new file mode 100644 index 0000000000000000000000000000000000000000..c8e69dcebb98d43695027fcc7e39a339c84dda51 GIT binary patch literal 666 zcmV;L0%iS)P)y zDuP#FBS9M!QcYr?x(Xj5^OKhEOrZ0;%Xt?M^PS1$oUvkJVg?bG2*VOq|FP&lUkJx< zKARBwK%20%c&{z=j$iacgSVPOZ>ZBRKY6Vs^pYxn!v)X(MaU+{KDTId#}NxSK|*6x zFv$sHenK`COmmFwEtDe)3f@BdM8ToA&@EBW@D_@SSjd>rJ=jul4hPAn$ZYd zQ(^?GM-r@1H!ZY@i{+CPWphGpzG8()mbM9@HXpGBsG>9Ms~`YLf6Qp{EBmWamEC4dDr!XN=Tg=UR*LRnyXqu_l$^Po#V$T zj`&?_>IiL%W{Dt*qP?plloe0b6+u)P>h$wELg`L#hA>QZ`WYP|H+WzkLrdxi*`3}7 zVOSR<9(Dx^Vxd?l7V1BL0REY1s|}3R@&Et;07*qoM6N<$f_%9k Avj6}9 literal 0 HcmV?d00001 diff --git a/telecine/src/main/res/drawable-xxxhdpi/ic_camera_alt_white_24dp.png b/telecine/src/main/res/drawable-xxxhdpi/ic_camera_alt_white_24dp.png new file mode 100644 index 0000000000000000000000000000000000000000..777658e95515ca47c9852d00621e2e6d45abc5c7 GIT binary patch literal 894 zcmV-^1A+XBP)zI9LJx5Cecy~F=}h8lwKtS@9n)MRxm2`AkqpItJs@p>)C(tu-V-#=CId=UD(s^ z#Zy@d`v;fOn~Tzu?OJHHNtIePI-lk`KN@Bx3G-g)`+Uz|m=FBkykXu`7K_DVu`~_~ z1Z4r$j3}CsFA0vr7p4ddb45K=_{;=>kNJmsIpZVU0%^)LLWz`4fe-nOMmgjIT>|g$ zGtFS1ZXE(|^ApYDN8W0_z#v8Txj;7;?6E~Um_R#sF$;$xalZoX{D6r(A?{D$E~c{O zM_>?haY%a$6fv1Q-U9oW%qwq!3ryzRTflV87q9|W;F{;GFv0){qSYiAAj>K*xHezl zfJJ&yHS#`7{A#YiA58NG$|F%`I5JV-9to7sbaLM;flIzZ1Lw!X3#^Fu{wYByi=R|$;R}qoTB8JI$kiVUUtqx1 zx=B!Su6`5*yii_yR9b4GhtSXus6)G54OU*Fzb<9Fuf)`*nQG!IEfB;+M}w za~X69{6e&@G0KTnj6*#FFeADVZuofoh-l4&E`cLDWuFO3wP#-rPc_|?^a;Rylnh;L z@)wd48zfOOJOnKQa*Rac43Xn8&v?osa%9ANUyhGPfeM+%FRSSkfU`OkhV}j2l~kBT z1bVSYwP7Nn+I` zNiohkFLCTaw-vAgjq*RZ@U(}??0O4qVKTS91=2W}ibKjn!0?Ng1(Y9wI8QK + + + + diff --git a/telecine/src/main/res/layout/flash_view.xml b/telecine/src/main/res/layout/flash_view.xml new file mode 100644 index 0000000..bab7bb1 --- /dev/null +++ b/telecine/src/main/res/layout/flash_view.xml @@ -0,0 +1,6 @@ + + + \ No newline at end of file diff --git a/telecine/src/main/res/layout/overlay_view.xml b/telecine/src/main/res/layout/overlay_view.xml index 07875d0..6c72c88 100644 --- a/telecine/src/main/res/layout/overlay_view.xml +++ b/telecine/src/main/res/layout/overlay_view.xml @@ -17,6 +17,15 @@ android:background="@drawable/cancel_background" android:contentDescription="@string/clear" /> + 16dp - 96dp + 144dp 25dp 24dp diff --git a/telecine/src/main/res/values/strings.xml b/telecine/src/main/res/values/strings.xml index e79d2fb..6d2243e 100644 --- a/telecine/src/main/res/values/strings.xml +++ b/telecine/src/main/res/values/strings.xml @@ -17,6 +17,8 @@ Video size Screen recording captured. Touch to view your screen recording. + Screenshot captured. + Touch to view your screenshot. Share Delete Recording screen. From ea3e1e5cf48bcdf5d467da13575133c5ba4e1544 Mon Sep 17 00:00:00 2001 From: tylerjroach Date: Fri, 2 Oct 2015 10:36:40 -0400 Subject: [PATCH 2/4] formatting fixes to match current codebase. --- .../telecine/RecordingSession.java | 55 +++++++++---------- 1 file changed, 27 insertions(+), 28 deletions(-) diff --git a/telecine/src/main/java/com/jakewharton/telecine/RecordingSession.java b/telecine/src/main/java/com/jakewharton/telecine/RecordingSession.java index fa51569..71f68eb 100644 --- a/telecine/src/main/java/com/jakewharton/telecine/RecordingSession.java +++ b/telecine/src/main/java/com/jakewharton/telecine/RecordingSession.java @@ -148,15 +148,14 @@ public void showOverlay() { @Override public void onScreenshot() { overlayView.animate() - .alpha(0) - .setDuration(0) - .withEndAction(new Runnable() { - @Override - public void run() { - takeScreenshot(); - } - }); - + .alpha(0) + .setDuration(0) + .withEndAction(new Runnable() { + @Override + public void run() { + takeScreenshot(); + } + }); } }; overlayView = OverlayView.create(context, overlayListener, showCountDown.get()); @@ -279,9 +278,9 @@ private void startRecording() { Timber.d("Screen recording started."); analytics.send(new HitBuilders.EventBuilder() // - .setCategory(Analytics.CATEGORY_RECORDING) - .setAction(Analytics.ACTION_RECORDING_START) - .build()); + .setCategory(Analytics.CATEGORY_RECORDING) + .setAction(Analytics.ACTION_RECORDING_START) + .build()); } private void stopRecording() { @@ -407,18 +406,18 @@ public void onFlashComplete() { Timber.d("Screenshot taken. Notifying media scanner of new screenshot."); MediaScannerConnection.scanFile(context, new String[]{outputFile}, null, - new MediaScannerConnection.OnScanCompletedListener() { + new MediaScannerConnection.OnScanCompletedListener() { + @Override + public void onScanCompleted(String path, final Uri uri) { + Timber.d("Media scanner completed."); + mainThread.post(new Runnable() { @Override - public void onScanCompleted(String path, final Uri uri) { - Timber.d("Media scanner completed."); - mainThread.post(new Runnable() { - @Override - public void run() { - showScreenshotNotification(uri, null); - } - }); - } - }); + public void run() { + showScreenshotNotification(uri, null); + } + }); + } + }); } } catch (Exception e) { @@ -450,13 +449,13 @@ public void run() { projection.stop(); overlayView.animate() - .alpha(1) - .setDuration(0); + .alpha(1) + .setDuration(0); analytics.send(new HitBuilders.EventBuilder() // - .setCategory(Analytics.CATEGORY_SCREENSHOT) - .setAction(Analytics.ACTION_SCREENSHOT_TAKEN) - .build()); + .setCategory(Analytics.CATEGORY_SCREENSHOT) + .setAction(Analytics.ACTION_SCREENSHOT_TAKEN) + .build()); Timber.d("Screenshot success"); } From 106402e0f5ae297b4ea26e576b3bff6917fcef9d Mon Sep 17 00:00:00 2001 From: tylerjroach Date: Fri, 2 Oct 2015 10:38:43 -0400 Subject: [PATCH 3/4] formatting fixes to match current codebase. --- .../java/com/jakewharton/telecine/RecordingSession.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/telecine/src/main/java/com/jakewharton/telecine/RecordingSession.java b/telecine/src/main/java/com/jakewharton/telecine/RecordingSession.java index 71f68eb..6b6355a 100644 --- a/telecine/src/main/java/com/jakewharton/telecine/RecordingSession.java +++ b/telecine/src/main/java/com/jakewharton/telecine/RecordingSession.java @@ -152,9 +152,9 @@ public void showOverlay() { .setDuration(0) .withEndAction(new Runnable() { @Override - public void run() { - takeScreenshot(); - } + public void run() { + takeScreenshot(); + } }); } }; From 45227413965c9c72fcdafc5f626d52db3d19dbe7 Mon Sep 17 00:00:00 2001 From: tylerjroach Date: Fri, 2 Oct 2015 11:07:49 -0400 Subject: [PATCH 4/4] formatting fixes to match current codebase. --- .../src/main/java/com/jakewharton/telecine/FlashView.java | 1 - .../java/com/jakewharton/telecine/RecordingSession.java | 8 ++++---- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/telecine/src/main/java/com/jakewharton/telecine/FlashView.java b/telecine/src/main/java/com/jakewharton/telecine/FlashView.java index 49d5a3b..5a3de30 100644 --- a/telecine/src/main/java/com/jakewharton/telecine/FlashView.java +++ b/telecine/src/main/java/com/jakewharton/telecine/FlashView.java @@ -18,7 +18,6 @@ @SuppressLint("ViewConstructor") // Lint, in this case, I am smarter than you. final class FlashView extends FrameLayout { - private FlashView flashView; private final Listener listener; static FlashView create(Context context, Listener listener) { diff --git a/telecine/src/main/java/com/jakewharton/telecine/RecordingSession.java b/telecine/src/main/java/com/jakewharton/telecine/RecordingSession.java index 6b6355a..47bf248 100644 --- a/telecine/src/main/java/com/jakewharton/telecine/RecordingSession.java +++ b/telecine/src/main/java/com/jakewharton/telecine/RecordingSession.java @@ -93,7 +93,7 @@ interface Listener { private final DateFormat videofileFormat = new SimpleDateFormat("'Telecine_'yyyy-MM-dd-HH-mm-ss'.mp4'", Locale.US); private final DateFormat audiofileFormat = - new SimpleDateFormat("'Telecine_'yyyy-MM-dd-HH-mm-ss'.jpeg'", Locale.US); + new SimpleDateFormat("'Telecine_'yyyy-MM-dd-HH-mm-ss'.jpeg'", Locale.US); private final NotificationManager notificationManager; private final WindowManager windowManager; @@ -343,7 +343,7 @@ private void takeScreenshot() { final RecordingInfo recordingInfo = getScreenshotInfo(); Timber.d("Screenshot: %s x %s @ %s", recordingInfo.width, recordingInfo.height, - recordingInfo.density); + recordingInfo.density); String outputName = audiofileFormat.format(new Date()); outputFile = new File(picturesOutputRoot, outputName).getAbsolutePath(); @@ -354,8 +354,8 @@ private void takeScreenshot() { imageReader = ImageReader.newInstance(recordingInfo.width, recordingInfo.height, PixelFormat.RGBA_8888, 2); Surface surface = imageReader.getSurface(); display = - projection.createVirtualDisplay(DISPLAY_NAME, recordingInfo.width, recordingInfo.height, - recordingInfo.density, VIRTUAL_DISPLAY_FLAG_PRESENTATION, surface, null, null); + projection.createVirtualDisplay(DISPLAY_NAME, recordingInfo.width, recordingInfo.height, + recordingInfo.density, VIRTUAL_DISPLAY_FLAG_PRESENTATION, surface, null, null); imageReader.setOnImageAvailableListener(new ImageReader.OnImageAvailableListener() {