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