Merge branch 'master' into new-tests

pull/22/head
Mattia Iavarone 8 years ago committed by GitHub
commit e77dc26cf6
  1. 2
      README.md
  2. 2
      cameraview/build.gradle
  3. 24
      cameraview/src/androidTest/java/com/otaliastudios/cameraview/WorkerHandlerTest.java
  4. 21
      cameraview/src/main/java/com/otaliastudios/cameraview/Camera1.java
  5. 2
      cameraview/src/main/java/com/otaliastudios/cameraview/CameraController.java
  6. 2
      cameraview/src/main/java/com/otaliastudios/cameraview/CameraView.java
  7. 32
      cameraview/src/main/utils/com/otaliastudios/cameraview/WorkerHandler.java
  8. 42
      cameraview/src/main/views/com/otaliastudios/cameraview/Preview.java
  9. 10
      demo/src/main/AndroidManifest.xml

@ -10,7 +10,7 @@
CameraView is a well documented, high-level library that makes capturing pictures and videos easy, addressing most of the common issues and needs, and still leaving you with flexibility where needed. CameraView is a well documented, high-level library that makes capturing pictures and videos easy, addressing most of the common issues and needs, and still leaving you with flexibility where needed.
```groovy ```groovy
compile 'com.otaliastudios:cameraview:1.2.0' compile 'com.otaliastudios:cameraview:1.2.1'
``` ```
<p> <p>

@ -3,7 +3,7 @@ apply plugin: 'com.github.dcendents.android-maven'
apply plugin: 'com.jfrog.bintray' apply plugin: 'com.jfrog.bintray'
// Required by bintray // Required by bintray
version = '1.2.0' version = '1.2.1'
group = 'com.otaliastudios' group = 'com.otaliastudios'
//region android dependencies //region android dependencies

@ -0,0 +1,24 @@
package com.otaliastudios.cameraview;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
import org.junit.Test;
import org.junit.runner.RunWith;
import static org.junit.Assert.*;
@RunWith(AndroidJUnit4.class)
@SmallTest
public class WorkerHandlerTest {
@Test
public void testCache() {
WorkerHandler w1 = WorkerHandler.get("handler1");
WorkerHandler w1a = WorkerHandler.get("handler1");
WorkerHandler w2 = WorkerHandler.get("handler2");
assertTrue(w1 == w1a);
assertFalse(w1 == w2);
}
}

@ -450,6 +450,7 @@ class Camera1 extends CameraController {
@Override @Override
boolean shouldFlipSizes() { boolean shouldFlipSizes() {
LOG.i("shouldFlip:", "mDeviceOrientation=", mDeviceOrientation, "mSensorOffset=", mSensorOffset);
return (mDeviceOrientation + mSensorOffset) % 180 != 0; return (mDeviceOrientation + mSensorOffset) % 180 != 0;
} }
@ -510,7 +511,7 @@ class Camera1 extends CameraController {
// Choose the max size. // Choose the max size.
List<Size> captureSizes = sizesFromList(params.getSupportedPictureSizes()); List<Size> captureSizes = sizesFromList(params.getSupportedPictureSizes());
Size maxSize = Collections.max(captureSizes); Size maxSize = Collections.max(captureSizes);
LOG.i("computeCaptureSize:", "computed", maxSize, "from", captureSizes); LOG.i("computeCaptureSize:", "computed", maxSize);
return Collections.max(captureSizes); return Collections.max(captureSizes);
} else { } else {
// Choose according to developer choice in setVideoQuality. // Choose according to developer choice in setVideoQuality.
@ -768,7 +769,7 @@ class Camera1 extends CameraController {
for (Camera.Size size : sizes) { for (Camera.Size size : sizes) {
result.add(new Size(size.width, size.height)); result.add(new Size(size.width, size.height));
} }
LOG.i("sizesFromList:", result.toArray()); LOG.i("sizesFromList:", result);
return result; return result;
} }
@ -802,14 +803,22 @@ class Camera1 extends CameraController {
LOG.i("matchSize:", "found big enough and consistent:", bigEnoughAndConsistent.size()); LOG.i("matchSize:", "found big enough and consistent:", bigEnoughAndConsistent.size());
Size result; Size result;
if (biggestPossible) { if (biggestPossible) {
if (bigEnoughAndConsistent.size() > 0) return Collections.max(bigEnoughAndConsistent); if (bigEnoughAndConsistent.size() > 0) {
if (consistent.size() > 0) return Collections.max(consistent); result = Collections.max(bigEnoughAndConsistent);
} else if (consistent.size() > 0) {
result = Collections.max(consistent);
} else {
result = Collections.max(sizes); result = Collections.max(sizes);
}
} else {
if (bigEnoughAndConsistent.size() > 0) {
result = Collections.min(bigEnoughAndConsistent);
} else if (consistent.size() > 0) {
result = Collections.max(consistent);
} else { } else {
if (bigEnoughAndConsistent.size() > 0) return Collections.min(bigEnoughAndConsistent);
if (consistent.size() > 0) return Collections.max(consistent);
result = Collections.max(sizes); result = Collections.max(sizes);
} }
}
LOG.i("matchSize:", "returning result", result); LOG.i("matchSize:", "returning result", result);
return result; return result;
} }

@ -35,7 +35,7 @@ abstract class CameraController implements Preview.SurfaceCallback {
mCameraCallbacks = callback; mCameraCallbacks = callback;
mPreview = preview; mPreview = preview;
mPreview.setSurfaceCallback(this); mPreview.setSurfaceCallback(this);
mHandler = new WorkerHandler("CameraViewController"); mHandler = WorkerHandler.get("CameraViewController");
} }
//region Start&Stop //region Start&Stop

@ -117,7 +117,7 @@ public class CameraView extends FrameLayout {
mPreviewImpl = instantiatePreview(context, this); mPreviewImpl = instantiatePreview(context, this);
mCameraController = instantiateCameraController(mCameraCallbacks, mPreviewImpl); mCameraController = instantiateCameraController(mCameraCallbacks, mPreviewImpl);
mUiHandler = new Handler(Looper.getMainLooper()); mUiHandler = new Handler(Looper.getMainLooper());
mWorkerHandler = new WorkerHandler("CameraViewWorker"); mWorkerHandler = WorkerHandler.get("CameraViewWorker");
// Views // Views
mGridLinesLayout = new GridLinesLayout(context); mGridLinesLayout = new GridLinesLayout(context);

@ -3,17 +3,43 @@ package com.otaliastudios.cameraview;
import android.os.Handler; import android.os.Handler;
import android.os.HandlerThread; import android.os.HandlerThread;
import java.lang.ref.WeakReference;
import java.util.WeakHashMap;
import java.util.concurrent.ConcurrentHashMap;
/** /**
* Class holding a background handler. * Class holding a background handler.
* Using setDaemon right now but a better approach would be to have * We want them to survive configuration changes if there's still job to do.
* start() and stop() callbacks here. TODO
*/ */
class WorkerHandler { class WorkerHandler {
private final static CameraLogger LOG = CameraLogger.create(WorkerHandler.class.getSimpleName());
private final static ConcurrentHashMap<String, WeakReference<WorkerHandler>> sCache = new ConcurrentHashMap<>(4);
public static WorkerHandler get(String name) {
if (sCache.containsKey(name)) {
WorkerHandler cached = sCache.get(name).get();
if (cached != null) {
HandlerThread thread = cached.mThread;
if (thread.isAlive() && !thread.isInterrupted()) {
LOG.w("get:", "Reusing cached worker handler.", name);
return cached;
}
}
LOG.w("get:", "Thread reference died, removing.", name);
sCache.remove(name);
}
LOG.i("get:", "Creating new handler.", name);
WorkerHandler handler = new WorkerHandler(name);
sCache.put(name, new WeakReference<>(handler));
return handler;
}
private HandlerThread mThread; private HandlerThread mThread;
private Handler mHandler; private Handler mHandler;
public WorkerHandler(String name) { private WorkerHandler(String name) {
mThread = new HandlerThread(name); mThread = new HandlerThread(name);
mThread.setDaemon(true); mThread.setDaemon(true);
mThread.start(); mThread.start();

@ -9,6 +9,8 @@ import android.view.ViewGroup;
abstract class Preview { abstract class Preview {
private final static CameraLogger LOG = CameraLogger.create(Preview.class.getSimpleName());
// This is used to notify CameraImpl to recompute its camera Preview size. // This is used to notify CameraImpl to recompute its camera Preview size.
// After that, CameraView will need a new layout pass to adapt to the Preview size. // After that, CameraView will need a new layout pass to adapt to the Preview size.
interface SurfaceCallback { interface SurfaceCallback {
@ -45,6 +47,7 @@ abstract class Preview {
// This is called by the CameraImpl. // This is called by the CameraImpl.
// These must be alredy rotated, if needed, to be consistent with surface/view sizes. // These must be alredy rotated, if needed, to be consistent with surface/view sizes.
void setDesiredSize(int width, int height) { void setDesiredSize(int width, int height) {
LOG.i("setDesiredSize:", "desiredW=", width, "desiredH=", height);
this.mDesiredWidth = width; this.mDesiredWidth = width;
this.mDesiredHeight = height; this.mDesiredHeight = height;
crop(); crop();
@ -60,6 +63,7 @@ abstract class Preview {
protected final void onSurfaceAvailable(int width, int height) { protected final void onSurfaceAvailable(int width, int height) {
LOG.i("onSurfaceAvailable:", "w=", width, "h=", height);
mSurfaceWidth = width; mSurfaceWidth = width;
mSurfaceHeight = height; mSurfaceHeight = height;
crop(); crop();
@ -70,6 +74,7 @@ abstract class Preview {
// As far as I can see, these are the view/surface dimensions. // As far as I can see, these are the view/surface dimensions.
// This is called by subclasses. // This is called by subclasses.
protected final void onSurfaceSizeChanged(int width, int height) { protected final void onSurfaceSizeChanged(int width, int height) {
LOG.i("onSurfaceSizeChanged:", "w=", width, "h=", height);
if (width != mSurfaceWidth || height != mSurfaceHeight) { if (width != mSurfaceWidth || height != mSurfaceHeight) {
mSurfaceWidth = width; mSurfaceWidth = width;
mSurfaceHeight = height; mSurfaceHeight = height;
@ -87,30 +92,33 @@ abstract class Preview {
/** /**
* As far as I can see, this extends either width or height of the surface, * Here we must crop the visible part by applying a > 1 scale to one of our
* to match the desired aspect ratio. * dimensions. This way our internal aspect ratio (mSurfaceWidth / mSurfaceHeight)
* This means that the external part of the surface will be cropped by the outer view. * will match the preview size aspect ratio (mDesiredWidth / mDesiredHeight).
*
* There might still be some absolute difference (e.g. same ratio but bigger / smaller).
* However that should be already managed by the framework.
*/ */
private final void crop() { private final void crop() {
getView().post(new Runnable() { getView().post(new Runnable() {
@Override @Override
public void run() { public void run() {
if (mDesiredWidth != 0 && mDesiredHeight != 0) { if (mDesiredHeight == 0 || mDesiredWidth == 0) return;
AspectRatio aspectRatio = AspectRatio.of(mDesiredWidth, mDesiredHeight); if (mSurfaceHeight == 0 || mSurfaceWidth == 0) return;
float targetHeight = (float) mSurfaceWidth / aspectRatio.toFloat(); float scaleX = 1f, scaleY = 1f;
float scale = 1; AspectRatio current = AspectRatio.of(mSurfaceWidth, mSurfaceHeight);
if (mSurfaceHeight > 0) { AspectRatio target = AspectRatio.of(mDesiredWidth, mDesiredHeight);
scale = targetHeight / (float) mSurfaceHeight; if (current.toFloat() >= target.toFloat()) {
} // We are too short. Must increase height.
scaleY = current.toFloat() / target.toFloat();
if (scale > 1) {
getView().setScaleX(1f);
getView().setScaleY(scale);
} else { } else {
getView().setScaleX(1f / scale); // We must increase width.
getView().setScaleY(1f); scaleX = target.toFloat() / current.toFloat();
}
} }
getView().setScaleX(scaleX);
getView().setScaleY(scaleY);
LOG.i("crop:", "applied scaleX=", scaleX);
LOG.i("crop:", "applied scaleY=", scaleY);
} }
}); });
} }

@ -13,18 +13,18 @@
android:theme="@style/AppTheme"> android:theme="@style/AppTheme">
<activity <activity
android:name="com.otaliastudios.cameraview.demo.MainActivity" android:name=".MainActivity"
android:theme="@style/Theme.MainActivity"> android:theme="@style/Theme.MainActivity"
android:configChanges="orientation|screenLayout|keyboardHidden">
<intent-filter> <intent-filter>
<action android:name="android.intent.action.MAIN" /> <action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" /> <category android:name="android.intent.category.LAUNCHER" />
</intent-filter> </intent-filter>
</activity> </activity>
<activity <activity android:name=".PicturePreviewActivity" />
android:name="com.otaliastudios.cameraview.demo.PicturePreviewActivity" />
<activity android:name="com.otaliastudios.cameraview.demo.VideoPreviewActivity"/> <activity android:name=".VideoPreviewActivity"/>
</application> </application>

Loading…
Cancel
Save