mirror of
https://github.com/oxen-io/session-android.git
synced 2025-02-20 07:18:25 +00:00
fix camera ui thread hangs and view race condition
also add a background behind the camera preview surface to avoid transparency peek-through issues. Fixes #3576 Closes #3601 // FREEBIE"
This commit is contained in:
parent
72735baa11
commit
5eaaadad26
@ -6,19 +6,20 @@
|
|||||||
android:id="@+id/layout_container"
|
android:id="@+id/layout_container"
|
||||||
android:layout_width="fill_parent"
|
android:layout_width="fill_parent"
|
||||||
android:layout_height="fill_parent"
|
android:layout_height="fill_parent"
|
||||||
android:background="?conversation_background"
|
|
||||||
android:orientation="vertical">
|
android:orientation="vertical">
|
||||||
|
|
||||||
<org.thoughtcrime.securesms.components.camera.QuickAttachmentDrawer
|
<org.thoughtcrime.securesms.components.camera.QuickAttachmentDrawer
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
android:id="@+id/quick_attachment_drawer"
|
android:id="@+id/quick_attachment_drawer"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent">
|
android:layout_height="match_parent"
|
||||||
|
android:background="@color/black">
|
||||||
|
|
||||||
<RelativeLayout android:layout_width="fill_parent"
|
<RelativeLayout android:layout_width="fill_parent"
|
||||||
android:layout_height="fill_parent"
|
android:layout_height="fill_parent"
|
||||||
android:layout_weight="1"
|
android:layout_weight="1"
|
||||||
android:orientation="vertical"
|
android:orientation="vertical"
|
||||||
|
android:background="?conversation_background"
|
||||||
android:gravity="bottom">
|
android:gravity="bottom">
|
||||||
|
|
||||||
<FrameLayout android:id="@+id/fragment_content"
|
<FrameLayout android:id="@+id/fragment_content"
|
||||||
|
@ -19,6 +19,7 @@ import android.annotation.TargetApi;
|
|||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.pm.ActivityInfo;
|
import android.content.pm.ActivityInfo;
|
||||||
|
import android.graphics.Color;
|
||||||
import android.hardware.Camera;
|
import android.hardware.Camera;
|
||||||
import android.hardware.Camera.PreviewCallback;
|
import android.hardware.Camera.PreviewCallback;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
@ -29,6 +30,7 @@ import android.util.Log;
|
|||||||
import android.view.OrientationEventListener;
|
import android.view.OrientationEventListener;
|
||||||
import android.view.Surface;
|
import android.view.Surface;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
import android.widget.FrameLayout;
|
import android.widget.FrameLayout;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
@ -36,7 +38,6 @@ import java.util.concurrent.CountDownLatch;
|
|||||||
|
|
||||||
import com.commonsware.cwac.camera.CameraHost;
|
import com.commonsware.cwac.camera.CameraHost;
|
||||||
import com.commonsware.cwac.camera.CameraHost.FailureReason;
|
import com.commonsware.cwac.camera.CameraHost.FailureReason;
|
||||||
import com.commonsware.cwac.camera.PreviewStrategy;
|
|
||||||
|
|
||||||
import org.thoughtcrime.securesms.ApplicationContext;
|
import org.thoughtcrime.securesms.ApplicationContext;
|
||||||
import org.thoughtcrime.securesms.util.Util;
|
import org.thoughtcrime.securesms.util.Util;
|
||||||
@ -85,11 +86,12 @@ public class CameraView extends FrameLayout {
|
|||||||
} else {
|
} else {
|
||||||
previewStrategy = new SurfacePreviewStrategy(this);
|
previewStrategy = new SurfacePreviewStrategy(this);
|
||||||
}
|
}
|
||||||
|
addView(previewStrategy.getWidget());
|
||||||
}
|
}
|
||||||
|
|
||||||
@TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
|
@TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
|
||||||
public void onResume() {
|
public void onResume() {
|
||||||
addView(previewStrategy.getWidget());
|
Log.w(TAG, "onResume()");
|
||||||
final CameraHost host = getHost();
|
final CameraHost host = getHost();
|
||||||
submitTask(new SerializedAsyncTask<FailureReason>() {
|
submitTask(new SerializedAsyncTask<FailureReason>() {
|
||||||
@Override protected FailureReason onRunBackground() {
|
@Override protected FailureReason onRunBackground() {
|
||||||
@ -120,6 +122,8 @@ public class CameraView extends FrameLayout {
|
|||||||
synchronized (CameraView.this) {
|
synchronized (CameraView.this) {
|
||||||
CameraView.this.notifyAll();
|
CameraView.this.notifyAll();
|
||||||
}
|
}
|
||||||
|
previewCreated();
|
||||||
|
initPreview();
|
||||||
requestLayout();
|
requestLayout();
|
||||||
invalidate();
|
invalidate();
|
||||||
}
|
}
|
||||||
@ -127,7 +131,7 @@ public class CameraView extends FrameLayout {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void onPause() {
|
public void onPause() {
|
||||||
removeView(previewStrategy.getWidget());
|
Log.w(TAG, "onPause()");
|
||||||
submitTask(new SerializedAsyncTask<Void>() {
|
submitTask(new SerializedAsyncTask<Void>() {
|
||||||
@Override protected void onPreMain() {
|
@Override protected void onPreMain() {
|
||||||
cameraReady = false;
|
cameraReady = false;
|
||||||
@ -155,7 +159,6 @@ public class CameraView extends FrameLayout {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
|
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
|
||||||
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
|
|
||||||
|
|
||||||
if (getMeasuredWidth() > 0 && getMeasuredHeight() > 0 && camera != null && cameraReady) {
|
if (getMeasuredWidth() > 0 && getMeasuredHeight() > 0 && camera != null && cameraReady) {
|
||||||
Camera.Size newSize = null;
|
Camera.Size newSize = null;
|
||||||
@ -194,13 +197,14 @@ public class CameraView extends FrameLayout {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
|
||||||
}
|
}
|
||||||
|
|
||||||
// based on CameraPreview.java from ApiDemos
|
// based on CameraPreview.java from ApiDemos
|
||||||
|
|
||||||
@SuppressWarnings("SuspiciousNameCombination") @Override
|
@SuppressWarnings("SuspiciousNameCombination") @Override
|
||||||
protected void onLayout(boolean changed, int l, int t, int r, int b) {
|
protected void onLayout(boolean changed, int l, int t, int r, int b) {
|
||||||
if (changed && getChildCount() > 0) {
|
if (getChildCount() > 0) {
|
||||||
final View child = getChildAt(0);
|
final View child = getChildAt(0);
|
||||||
final int width = r - l;
|
final int width = r - l;
|
||||||
final int height = b - t;
|
final int height = b - t;
|
||||||
@ -309,6 +313,8 @@ public class CameraView extends FrameLayout {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void stopPreview() {
|
private void stopPreview() {
|
||||||
|
Log.w(TAG, "stopPreview()");
|
||||||
|
camera.startPreview();
|
||||||
inPreview = false;
|
inPreview = false;
|
||||||
getHost().autoFocusUnavailable();
|
getHost().autoFocusUnavailable();
|
||||||
camera.stopPreview();
|
camera.stopPreview();
|
||||||
@ -491,7 +497,7 @@ public class CameraView extends FrameLayout {
|
|||||||
private abstract class PostInitializationTask<Result> extends SerializedAsyncTask<Result> {
|
private abstract class PostInitializationTask<Result> extends SerializedAsyncTask<Result> {
|
||||||
@Override protected void onWait() {
|
@Override protected void onWait() {
|
||||||
synchronized (CameraView.this) {
|
synchronized (CameraView.this) {
|
||||||
while (camera == null || previewSize == null) {
|
while (camera == null || previewSize == null || !previewStrategy.isReady()) {
|
||||||
Util.wait(CameraView.this, 0);
|
Util.wait(CameraView.this, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,12 @@
|
|||||||
|
package org.thoughtcrime.securesms.components.camera;
|
||||||
|
|
||||||
|
import android.hardware.Camera;
|
||||||
|
import android.media.MediaRecorder;
|
||||||
|
import android.view.View;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
@SuppressWarnings("deprecation")
|
||||||
|
public interface PreviewStrategy extends com.commonsware.cwac.camera.PreviewStrategy {
|
||||||
|
boolean isReady();
|
||||||
|
}
|
@ -88,7 +88,7 @@ public class QuickAttachmentDrawer extends ViewGroup {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public boolean isOpen() {
|
public boolean isOpen() {
|
||||||
return getDrawerState().isVisible();
|
return drawerState.isVisible();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void close() {
|
public void close() {
|
||||||
@ -101,13 +101,13 @@ public class QuickAttachmentDrawer extends ViewGroup {
|
|||||||
|
|
||||||
public void onConfigurationChanged() {
|
public void onConfigurationChanged() {
|
||||||
int rotation = getWindowManager().getDefaultDisplay().getRotation();
|
int rotation = getWindowManager().getDefaultDisplay().getRotation();
|
||||||
Log.w(TAG, String.format("onNewOrientation(old %d, new %d)", this.rotation, rotation));
|
|
||||||
final boolean rotationChanged = this.rotation != rotation;
|
final boolean rotationChanged = this.rotation != rotation;
|
||||||
this.rotation = rotation;
|
this.rotation = rotation;
|
||||||
if (rotationChanged) {
|
if (rotationChanged) {
|
||||||
|
Log.w(TAG, String.format("onNewOrientation(old %d, new %d)", this.rotation, rotation));
|
||||||
if (isOpen()) {
|
if (isOpen()) {
|
||||||
quickCamera.onPause();
|
quickCamera.onPause();
|
||||||
setDrawerStateAndAnimate(getDrawerState());
|
setDrawerStateAndAnimate(drawerState);
|
||||||
}
|
}
|
||||||
updateControlsView();
|
updateControlsView();
|
||||||
}
|
}
|
||||||
@ -140,6 +140,7 @@ public class QuickAttachmentDrawer extends ViewGroup {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void updateHalfExpandedAnchorPoint() {
|
private void updateHalfExpandedAnchorPoint() {
|
||||||
|
Log.w(TAG, "updateHalfExpandedAnchorPoint()");
|
||||||
getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
|
getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
|
||||||
@SuppressWarnings("deprecation") @Override public void onGlobalLayout() {
|
@SuppressWarnings("deprecation") @Override public void onGlobalLayout() {
|
||||||
if(android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.JELLY_BEAN) {
|
if(android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.JELLY_BEAN) {
|
||||||
@ -151,6 +152,9 @@ public class QuickAttachmentDrawer extends ViewGroup {
|
|||||||
coverView = getChildAt(coverViewPosition);
|
coverView = getChildAt(coverViewPosition);
|
||||||
slideRange = getMeasuredHeight();
|
slideRange = getMeasuredHeight();
|
||||||
halfExpandedAnchorPoint = computeSlideOffsetFromCoverBottom(slideRange - baseHalfHeight);
|
halfExpandedAnchorPoint = computeSlideOffsetFromCoverBottom(slideRange - baseHalfHeight);
|
||||||
|
requestLayout();
|
||||||
|
invalidate();
|
||||||
|
Log.w(TAG, "updated halfExpandedAnchorPoint!");
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -246,28 +250,34 @@ public class QuickAttachmentDrawer extends ViewGroup {
|
|||||||
if (h != oldh) updateHalfExpandedAnchorPoint();
|
if (h != oldh) updateHalfExpandedAnchorPoint();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
// @Override
|
||||||
protected boolean drawChild(@NonNull Canvas canvas, @NonNull View child, long drawingTime) {
|
// protected boolean drawChild(@NonNull Canvas canvas, @NonNull View child, long drawingTime) {
|
||||||
boolean result;
|
// boolean result;
|
||||||
final int save = canvas.save(Canvas.CLIP_SAVE_FLAG);
|
// final int save = canvas.save(Canvas.CLIP_SAVE_FLAG);
|
||||||
|
//
|
||||||
canvas.getClipBounds(drawChildrenRect);
|
// canvas.getClipBounds(drawChildrenRect);
|
||||||
if (child == coverView)
|
// if (child == coverView)
|
||||||
drawChildrenRect.bottom = Math.min(drawChildrenRect.bottom, child.getBottom());
|
// drawChildrenRect.bottom = Math.min(drawChildrenRect.bottom, child.getBottom());
|
||||||
else if (coverView != null)
|
// else if (coverView != null)
|
||||||
drawChildrenRect.top = Math.max(drawChildrenRect.top, coverView.getBottom());
|
// drawChildrenRect.top = Math.max(drawChildrenRect.top, coverView.getBottom());
|
||||||
canvas.clipRect(drawChildrenRect);
|
// canvas.clipRect(drawChildrenRect);
|
||||||
result = super.drawChild(canvas, child, drawingTime);
|
// result = super.drawChild(canvas, child, drawingTime);
|
||||||
canvas.restoreToCount(save);
|
// canvas.restoreToCount(save);
|
||||||
return result;
|
// return result;
|
||||||
}
|
// }
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void computeScroll() {
|
public void computeScroll() {
|
||||||
if (dragHelper != null && dragHelper.continueSettling(true)) {
|
if (dragHelper != null && dragHelper.continueSettling(true)) {
|
||||||
ViewCompat.postInvalidateOnAnimation(this);
|
ViewCompat.postInvalidateOnAnimation(this);
|
||||||
} else if (!getDrawerState().isVisible()) {
|
}
|
||||||
|
|
||||||
|
if (slideOffset == COLLAPSED_ANCHOR_POINT && quickCamera.isStarted()) {
|
||||||
|
Log.w(TAG, "computeScroll(PAUSE)");
|
||||||
quickCamera.onPause();
|
quickCamera.onPause();
|
||||||
|
} else if (slideOffset != COLLAPSED_ANCHOR_POINT && !quickCamera.isStarted()) {
|
||||||
|
Log.w(TAG, "computeScroll(RESUME)");
|
||||||
|
quickCamera.onResume();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -282,12 +292,10 @@ public class QuickAttachmentDrawer extends ViewGroup {
|
|||||||
setDrawerState(DrawerState.FULL_EXPANDED);
|
setDrawerState(DrawerState.FULL_EXPANDED);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
quickCamera.onResume();
|
|
||||||
fullScreenButton.setImageResource(R.drawable.quick_camera_fullscreen);
|
fullScreenButton.setImageResource(R.drawable.quick_camera_fullscreen);
|
||||||
if (listener != null) listener.onAttachmentDrawerOpened();
|
if (listener != null) listener.onAttachmentDrawerOpened();
|
||||||
break;
|
break;
|
||||||
case FULL_EXPANDED:
|
case FULL_EXPANDED:
|
||||||
quickCamera.onResume();
|
|
||||||
fullScreenButton.setImageResource(isFullscreenOnly() ? R.drawable.quick_camera_hide
|
fullScreenButton.setImageResource(isFullscreenOnly() ? R.drawable.quick_camera_hide
|
||||||
: R.drawable.quick_camera_exit_fullscreen);
|
: R.drawable.quick_camera_exit_fullscreen);
|
||||||
if (listener != null) listener.onAttachmentDrawerOpened();
|
if (listener != null) listener.onAttachmentDrawerOpened();
|
||||||
@ -304,10 +312,6 @@ public class QuickAttachmentDrawer extends ViewGroup {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public DrawerState getDrawerState() {
|
|
||||||
return drawerState;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setDrawerStateAndAnimate(final DrawerState requestedDrawerState) {
|
public void setDrawerStateAndAnimate(final DrawerState requestedDrawerState) {
|
||||||
DrawerState oldDrawerState = this.drawerState;
|
DrawerState oldDrawerState = this.drawerState;
|
||||||
setDrawerState(requestedDrawerState);
|
setDrawerState(requestedDrawerState);
|
||||||
@ -487,6 +491,9 @@ public class QuickAttachmentDrawer extends ViewGroup {
|
|||||||
dragHelper.smoothSlideViewTo(quickCamera, quickCamera.getLeft(), computeCameraTopPosition(slideOffset));
|
dragHelper.smoothSlideViewTo(quickCamera, quickCamera.getLeft(), computeCameraTopPosition(slideOffset));
|
||||||
ViewCompat.postInvalidateOnAnimation(this);
|
ViewCompat.postInvalidateOnAnimation(this);
|
||||||
} else {
|
} else {
|
||||||
|
Log.w(TAG, "quick sliding to " + slideOffset);
|
||||||
|
this.slideOffset = slideOffset;
|
||||||
|
requestLayout();
|
||||||
invalidate();
|
invalidate();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -21,8 +21,6 @@ import android.view.SurfaceHolder;
|
|||||||
import android.view.SurfaceView;
|
import android.view.SurfaceView;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
|
|
||||||
import com.commonsware.cwac.camera.PreviewStrategy;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
class SurfacePreviewStrategy implements PreviewStrategy,
|
class SurfacePreviewStrategy implements PreviewStrategy,
|
||||||
@ -31,6 +29,7 @@ class SurfacePreviewStrategy implements PreviewStrategy,
|
|||||||
private final CameraView cameraView;
|
private final CameraView cameraView;
|
||||||
private SurfaceView preview=null;
|
private SurfaceView preview=null;
|
||||||
private SurfaceHolder previewHolder=null;
|
private SurfaceHolder previewHolder=null;
|
||||||
|
private boolean ready = false;
|
||||||
|
|
||||||
@SuppressWarnings("deprecation")
|
@SuppressWarnings("deprecation")
|
||||||
SurfacePreviewStrategy(CameraView cameraView) {
|
SurfacePreviewStrategy(CameraView cameraView) {
|
||||||
@ -44,14 +43,14 @@ class SurfacePreviewStrategy implements PreviewStrategy,
|
|||||||
@Override
|
@Override
|
||||||
public void surfaceCreated(SurfaceHolder holder) {
|
public void surfaceCreated(SurfaceHolder holder) {
|
||||||
Log.w(TAG, "surfaceCreated()");
|
Log.w(TAG, "surfaceCreated()");
|
||||||
cameraView.previewCreated();
|
ready = true;
|
||||||
|
synchronized (cameraView) { cameraView.notifyAll(); }
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void surfaceChanged(SurfaceHolder holder, int format,
|
public void surfaceChanged(SurfaceHolder holder, int format,
|
||||||
int width, int height) {
|
int width, int height) {
|
||||||
Log.w(TAG, "surfaceChanged()");
|
Log.w(TAG, "surfaceChanged()");
|
||||||
cameraView.initPreview();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -75,4 +74,9 @@ class SurfacePreviewStrategy implements PreviewStrategy,
|
|||||||
public View getWidget() {
|
public View getWidget() {
|
||||||
return(preview);
|
return(preview);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isReady() {
|
||||||
|
return ready;
|
||||||
|
}
|
||||||
}
|
}
|
@ -22,8 +22,6 @@ import android.util.Log;
|
|||||||
import android.view.TextureView;
|
import android.view.TextureView;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
|
|
||||||
import com.commonsware.cwac.camera.PreviewStrategy;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
@TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
|
@TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
|
||||||
@ -45,9 +43,7 @@ class TexturePreviewStrategy implements PreviewStrategy,
|
|||||||
int width, int height) {
|
int width, int height) {
|
||||||
Log.w(TAG, "onSurfaceTextureAvailable()");
|
Log.w(TAG, "onSurfaceTextureAvailable()");
|
||||||
this.surface=surface;
|
this.surface=surface;
|
||||||
|
synchronized (cameraView) { cameraView.notifyAll(); }
|
||||||
cameraView.previewCreated();
|
|
||||||
cameraView.initPreview();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -72,7 +68,6 @@ class TexturePreviewStrategy implements PreviewStrategy,
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void attach(Camera camera) throws IOException {
|
public void attach(Camera camera) throws IOException {
|
||||||
Log.w(TAG, "attach(Camera)");
|
|
||||||
camera.setPreviewTexture(surface);
|
camera.setPreviewTexture(surface);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -87,6 +82,11 @@ class TexturePreviewStrategy implements PreviewStrategy,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isReady() {
|
||||||
|
return widget.isAvailable();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public View getWidget() {
|
public View getWidget() {
|
||||||
return(widget);
|
return(widget);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user