Skip to content

Commit 8207e67

Browse files
authored
Some bug fixes (#953)
* Kotlin 1.4.0, fixes #940 * Fix multifilter bug, fixes #875 * Fix arithmetic exception, fixes #895 * Update dependencies * Demo app in Kotlin * Avoid stackoverflows on zoom/ev, fixes #856 * Remove Gemfile.lock * Try to fix build
1 parent daf7a0b commit 8207e67

File tree

24 files changed

+1040
-1744
lines changed

24 files changed

+1040
-1744
lines changed

.github/workflows/build.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ jobs:
1818
with:
1919
java-version: 1.8
2020
- name: Perform base checks
21-
run: ./gradlew demo:assembleDebug cameraview:publishToDirectory
21+
run: ./gradlew demo:assembleDebug cameraview:publishToDirectory --stacktrace
2222
ANDROID_UNIT_TESTS:
2323
name: Unit Tests
2424
runs-on: ubuntu-latest
@@ -28,7 +28,7 @@ jobs:
2828
with:
2929
java-version: 1.8
3030
- name: Execute unit tests
31-
run: ./gradlew cameraview:runUnitTests
31+
run: ./gradlew cameraview:runUnitTests --stacktrace
3232
- name: Upload unit tests artifact
3333
uses: actions/upload-artifact@v1
3434
with:

build.gradle.kts

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ buildscript {
44
extra["minSdkVersion"] = 15
55
extra["compileSdkVersion"] = 29
66
extra["targetSdkVersion"] = 29
7-
extra["kotlinVersion"] = "1.3.72"
87

98
repositories {
109
google()
@@ -13,10 +12,9 @@ buildscript {
1312
}
1413

1514
dependencies {
16-
classpath("com.android.tools.build:gradle:4.0.0")
15+
classpath("com.android.tools.build:gradle:4.0.1")
1716
classpath("com.otaliastudios.tools:publisher:0.3.3")
18-
val kotlinVersion = property("kotlinVersion") as String
19-
classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion")
17+
classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:1.4.0")
2018

2119
}
2220
}

cameraview/build.gradle.kts

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,10 @@ plugins {
99
}
1010

1111
android {
12-
setCompileSdkVersion(rootProject.property("compileSdkVersion") as Int)
12+
setCompileSdkVersion(property("compileSdkVersion") as Int)
1313
defaultConfig {
14-
setMinSdkVersion(rootProject.property("minSdkVersion") as Int)
15-
setTargetSdkVersion(rootProject.property("targetSdkVersion") as Int)
14+
setMinSdkVersion(property("minSdkVersion") as Int)
15+
setTargetSdkVersion(property("targetSdkVersion") as Int)
1616
versionCode = 1
1717
versionName = "2.6.3"
1818
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
@@ -25,20 +25,20 @@ android {
2525
}
2626

2727
dependencies {
28-
testImplementation("junit:junit:4.12")
28+
testImplementation("junit:junit:4.13")
2929
testImplementation("org.mockito:mockito-inline:2.28.2")
3030

31-
androidTestImplementation("androidx.test:runner:1.2.0")
32-
androidTestImplementation("androidx.test:rules:1.2.0")
31+
androidTestImplementation("androidx.test:runner:1.3.0")
32+
androidTestImplementation("androidx.test:rules:1.3.0")
3333
androidTestImplementation("androidx.test.ext:junit:1.1.1")
3434
androidTestImplementation("org.mockito:mockito-android:2.28.2")
3535
androidTestImplementation("androidx.test.espresso:espresso-core:3.2.0")
3636

37-
api("androidx.exifinterface:exifinterface:1.1.0")
38-
api("androidx.lifecycle:lifecycle-common:2.1.0")
39-
api("com.google.android.gms:play-services-tasks:17.0.0")
37+
api("androidx.exifinterface:exifinterface:1.2.0")
38+
api("androidx.lifecycle:lifecycle-common:2.2.0")
39+
api("com.google.android.gms:play-services-tasks:17.2.0")
4040
implementation("androidx.annotation:annotation:1.1.0")
41-
implementation("com.otaliastudios.opengl:egloo:0.5.2")
41+
implementation("com.otaliastudios.opengl:egloo:0.5.3")
4242
}
4343

4444
// Publishing
@@ -93,7 +93,7 @@ tasks.register("runAndroidTests") { // changing name? change github workflow
9393
}
9494

9595
// Merge the two with a jacoco task.
96-
jacoco { toolVersion = "0.8.1" }
96+
jacoco { toolVersion = "0.8.5" }
9797
tasks.register("computeCoverage", JacocoReport::class) {
9898
dependsOn("compileDebugSources") // Compile sources, needed below
9999
executionData.from(fileTree(coverageInputDir))

cameraview/src/main/java/com/otaliastudios/cameraview/CameraView.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -394,12 +394,10 @@ protected void onAttachedToWindow() {
394394
// attached. That's why we instantiate the preview here.
395395
doInstantiatePreview();
396396
}
397-
mOrientationHelper.enable();
398397
}
399398

400399
@Override
401400
protected void onDetachedFromWindow() {
402-
if (!mInEditor) mOrientationHelper.disable();
403401
mLastPreviewStreamSize = null;
404402
super.onDetachedFromWindow();
405403
}
@@ -732,6 +730,7 @@ private void onGesture(@NonNull GestureFinder source, @NonNull CameraOptions opt
732730
* Sets permissions flag if you want enable auto check permissions or disable it.
733731
* @param requestPermissions - true: auto check permissions enabled, false: auto check permissions disabled.
734732
*/
733+
@SuppressWarnings("unused")
735734
public void setRequestPermissions(boolean requestPermissions) {
736735
mRequestPermissions = requestPermissions;
737736
}
@@ -1810,6 +1809,7 @@ public void takeVideo(@NonNull File file, int durationMillis) {
18101809
* @param fileDescriptor a file descriptor where the video will be saved
18111810
* @param durationMillis recording max duration
18121811
*/
1812+
@SuppressWarnings("unused")
18131813
public void takeVideo(@NonNull FileDescriptor fileDescriptor, int durationMillis) {
18141814
takeVideo(null, fileDescriptor, durationMillis);
18151815
}

cameraview/src/main/java/com/otaliastudios/cameraview/engine/Camera1Engine.java

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -623,7 +623,10 @@ private boolean applyHdr(@NonNull Camera.Parameters params, @NonNull Hdr oldHdr)
623623
public void setZoom(final float zoom, @Nullable final PointF[] points, final boolean notify) {
624624
final float old = mZoomValue;
625625
mZoomValue = zoom;
626-
mZoomTask = getOrchestrator().scheduleStateful("zoom (" + zoom + ")",
626+
// Zoom requests can be high frequency (e.g. linked to touch events), so
627+
// we remove the task before scheduling to avoid stack overflows in orchestrator.
628+
getOrchestrator().remove("zoom");
629+
mZoomTask = getOrchestrator().scheduleStateful("zoom",
627630
CameraState.ENGINE,
628631
new Runnable() {
629632
@Override
@@ -655,8 +658,11 @@ public void setExposureCorrection(final float EVvalue, @NonNull final float[] bo
655658
@Nullable final PointF[] points, final boolean notify) {
656659
final float old = mExposureCorrectionValue;
657660
mExposureCorrectionValue = EVvalue;
661+
// EV requests can be high frequency (e.g. linked to touch events), so
662+
// we remove the task before scheduling to avoid stack overflows in orchestrator.
663+
getOrchestrator().remove("exposure correction");
658664
mExposureCorrectionTask = getOrchestrator().scheduleStateful(
659-
"exposure correction (" + EVvalue + ")",
665+
"exposure correction",
660666
CameraState.ENGINE,
661667
new Runnable() {
662668
@Override

cameraview/src/main/java/com/otaliastudios/cameraview/engine/Camera2Engine.java

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1246,8 +1246,11 @@ protected boolean applyHdr(@NonNull CaptureRequest.Builder builder, @NonNull Hdr
12461246
public void setZoom(final float zoom, final @Nullable PointF[] points, final boolean notify) {
12471247
final float old = mZoomValue;
12481248
mZoomValue = zoom;
1249+
// Zoom requests can be high frequency (e.g. linked to touch events), so
1250+
// we remove the task before scheduling to avoid stack overflows in orchestrator.
1251+
getOrchestrator().remove("zoom");
12491252
mZoomTask = getOrchestrator().scheduleStateful(
1250-
"zoom (" + zoom + ")",
1253+
"zoom",
12511254
CameraState.ENGINE,
12521255
new Runnable() {
12531256
@Override
@@ -1302,8 +1305,11 @@ public void setExposureCorrection(final float EVvalue,
13021305
final boolean notify) {
13031306
final float old = mExposureCorrectionValue;
13041307
mExposureCorrectionValue = EVvalue;
1308+
// EV requests can be high frequency (e.g. linked to touch events), so
1309+
// we remove the task before scheduling to avoid stack overflows in orchestrator.
1310+
getOrchestrator().remove("exposure correction");
13051311
mExposureCorrectionTask = getOrchestrator().scheduleStateful(
1306-
"exposure correction (" + EVvalue + ")",
1312+
"exposure correction",
13071313
CameraState.ENGINE,
13081314
new Runnable() {
13091315
@Override

cameraview/src/main/java/com/otaliastudios/cameraview/filter/MultiFilter.java

Lines changed: 21 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ public class MultiFilter implements Filter, OneParameterFilter, TwoParameterFilt
4848
static class State {
4949
@VisibleForTesting boolean isProgramCreated = false;
5050
@VisibleForTesting boolean isFramebufferCreated = false;
51+
private boolean sizeChanged = false;
5152
@VisibleForTesting Size size = null;
5253
private int programHandle = -1;
5354
private GlFramebuffer outputFramebuffer = null;
@@ -135,15 +136,25 @@ private void maybeDestroyProgram(@NonNull Filter filter) {
135136

136137
private void maybeCreateFramebuffer(@NonNull Filter filter, boolean isFirst, boolean isLast) {
137138
State state = states.get(filter);
139+
if (isLast) {
140+
//noinspection ConstantConditions
141+
state.sizeChanged = false;
142+
return;
143+
}
138144
//noinspection ConstantConditions
139-
if (state.isFramebufferCreated || isLast) return;
140-
state.isFramebufferCreated = true;
141-
state.outputTexture = new GlTexture(GLES20.GL_TEXTURE0,
142-
GLES20.GL_TEXTURE_2D,
143-
state.size.getWidth(),
144-
state.size.getHeight());
145-
state.outputFramebuffer = new GlFramebuffer();
146-
state.outputFramebuffer.attach(state.outputTexture);
145+
if (state.sizeChanged) {
146+
maybeDestroyFramebuffer(filter);
147+
state.sizeChanged = false;
148+
}
149+
if (!state.isFramebufferCreated) {
150+
state.isFramebufferCreated = true;
151+
state.outputTexture = new GlTexture(GLES20.GL_TEXTURE0,
152+
GLES20.GL_TEXTURE_2D,
153+
state.size.getWidth(),
154+
state.size.getHeight());
155+
state.outputFramebuffer = new GlFramebuffer();
156+
state.outputFramebuffer.attach(state.outputTexture);
157+
}
147158
}
148159

149160
private void maybeDestroyFramebuffer(@NonNull Filter filter) {
@@ -157,11 +168,13 @@ private void maybeDestroyFramebuffer(@NonNull Filter filter) {
157168
state.outputTexture = null;
158169
}
159170

171+
// Any thread...
160172
private void maybeSetSize(@NonNull Filter filter) {
161173
State state = states.get(filter);
162174
//noinspection ConstantConditions
163175
if (size != null && !size.equals(state.size)) {
164176
state.size = size;
177+
state.sizeChanged = true;
165178
filter.setSize(size.getWidth(), size.getHeight());
166179
}
167180
}

cameraview/src/main/java/com/otaliastudios/cameraview/internal/OrientationHelper.java

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77

88
import android.hardware.display.DisplayManager;
99
import android.os.Build;
10+
import android.os.Handler;
11+
import android.os.Looper;
1012
import android.view.Display;
1113
import android.view.OrientationEventListener;
1214
import android.view.Surface;
@@ -27,6 +29,7 @@ public interface Callback {
2729
void onDisplayOffsetChanged(int displayOffset, boolean willRecreate);
2830
}
2931

32+
private final Handler mHandler = new Handler(Looper.getMainLooper());
3033
private final Context mContext;
3134
private final Callback mCallback;
3235

@@ -99,16 +102,14 @@ public void onDisplayChanged(int displayId) {
99102
* Enables this listener.
100103
*/
101104
public void enable() {
102-
if (mEnabled) {
103-
//already enabled, will ignore call
104-
return;
105-
}
105+
if (mEnabled) return;
106106
mEnabled = true;
107107
mDisplayOffset = findDisplayOffset();
108108
if (Build.VERSION.SDK_INT >= 17) {
109109
DisplayManager manager = (DisplayManager)
110110
mContext.getSystemService(Context.DISPLAY_SERVICE);
111-
manager.registerDisplayListener(mDisplayOffsetListener, null);
111+
// Without the handler, this can crash if called from a thread without a looper
112+
manager.registerDisplayListener(mDisplayOffsetListener, mHandler);
112113
}
113114
mDeviceOrientationListener.enable();
114115
}

cameraview/src/main/java/com/otaliastudios/cameraview/size/AspectRatio.java

Lines changed: 10 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,8 @@ public static AspectRatio of(@NonNull Size size) {
3333
@NonNull
3434
public static AspectRatio of(int x, int y) {
3535
int gcd = gcd(x, y);
36-
x /= gcd;
37-
y /= gcd;
36+
if (gcd > 0) x /= gcd;
37+
if (gcd > 0) y /= gcd;
3838
String key = x + ":" + y;
3939
AspectRatio cached = sCache.get(key);
4040
if (cached == null) {
@@ -58,8 +58,8 @@ public static AspectRatio parse(@NonNull String string) {
5858
if (parts.length != 2) {
5959
throw new NumberFormatException("Illegal AspectRatio string. Must be x:y");
6060
}
61-
int x = Integer.valueOf(parts[0]);
62-
int y = Integer.valueOf(parts[1]);
61+
int x = Integer.parseInt(parts[0]);
62+
int y = Integer.parseInt(parts[1]);
6363
return of(x, y);
6464
}
6565

@@ -80,14 +80,11 @@ public int getY() {
8080
}
8181

8282
public boolean matches(@NonNull Size size) {
83-
int gcd = gcd(size.getWidth(), size.getHeight());
84-
int x = size.getWidth() / gcd;
85-
int y = size.getHeight() / gcd;
86-
return mX == x && mY == y;
83+
return equals(AspectRatio.of(size));
8784
}
8885

8986
public boolean matches(@NonNull Size size, float tolerance) {
90-
return Math.abs(toFloat() - (float) size.getWidth() / size.getHeight()) <= tolerance;
87+
return Math.abs(toFloat() - AspectRatio.of(size).toFloat()) <= tolerance;
9188
}
9289

9390
@Override
@@ -99,8 +96,7 @@ public boolean equals(Object o) {
9996
return true;
10097
}
10198
if (o instanceof AspectRatio) {
102-
AspectRatio ratio = (AspectRatio) o;
103-
return mX == ratio.mX && mY == ratio.mY;
99+
return toFloat() == ((AspectRatio) o).toFloat();
104100
}
105101
return false;
106102
}
@@ -117,17 +113,12 @@ public float toFloat() {
117113

118114
@Override
119115
public int hashCode() {
120-
return mY ^ ((mX << (Integer.SIZE / 2)) | (mX >>> (Integer.SIZE / 2)));
116+
return Float.floatToIntBits(toFloat());
121117
}
122118

123119
@Override
124120
public int compareTo(@NonNull AspectRatio another) {
125-
if (equals(another)) {
126-
return 0;
127-
} else if (toFloat() - another.toFloat() > 0) {
128-
return 1;
129-
}
130-
return -1;
121+
return Float.compare(toFloat(), another.toFloat());
131122
}
132123

133124
/**
@@ -140,6 +131,7 @@ public AspectRatio flip() {
140131
return AspectRatio.of(mY, mX);
141132
}
142133

134+
// Note: gcd(0,X) = gcd(X,0) = X (even for X=0)
143135
private static int gcd(int a, int b) {
144136
while (b != 0) {
145137
int c = b;

demo/build.gradle.kts

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,23 @@
11
plugins {
22
id("com.android.application")
3+
id("kotlin-android")
34
}
45

56
android {
6-
setCompileSdkVersion(rootProject.property("compileSdkVersion") as Int)
7+
setCompileSdkVersion(property("compileSdkVersion") as Int)
78
defaultConfig {
89
applicationId = "com.otaliastudios.cameraview.demo"
9-
setMinSdkVersion(rootProject.property("minSdkVersion") as Int)
10-
setTargetSdkVersion(rootProject.property("targetSdkVersion") as Int)
10+
setMinSdkVersion(property("minSdkVersion") as Int)
11+
setTargetSdkVersion(property("targetSdkVersion") as Int)
1112
versionCode = 1
1213
versionName = "1.0"
1314
vectorDrawables.useSupportLibrary = true
1415
}
16+
sourceSets["main"].java.srcDir("src/main/kotlin")
1517
}
1618

1719
dependencies {
1820
implementation(project(":cameraview"))
19-
implementation("androidx.appcompat:appcompat:1.1.0")
20-
implementation("com.google.android.material:material:1.1.0")
21+
implementation("androidx.appcompat:appcompat:1.2.0")
22+
implementation("com.google.android.material:material:1.2.0")
2123
}

0 commit comments

Comments
 (0)