11package com .otaliastudios .cameraview .internal ;
22
3+ import android .app .Activity ;
34import android .content .Context ;
5+ import android .content .res .Configuration ;
46import android .hardware .SensorManager ;
57import androidx .annotation .NonNull ;
68import androidx .annotation .VisibleForTesting ;
911import android .os .Build ;
1012import android .os .Handler ;
1113import android .os .Looper ;
14+ import android .view .View ;
1215import android .view .Display ;
1316import android .view .OrientationEventListener ;
1417import android .view .Surface ;
1821 * Helps with keeping track of both device orientation (which changes when device is rotated)
1922 * and the display offset (which depends on the activity orientation wrt the device default
2023 * orientation).
24+ *
25+ * Note: any change in the display offset should restart the camera engine, because it reads
26+ * from the angles container at startup and computes size based on that. This is tricky because
27+ * activity behavior can differ:
28+ *
29+ * - if activity is locked to some orientation, {@link #mDisplayOffset} won't change, and
30+ * {@link View#onConfigurationChanged(Configuration)} won't be called.
31+ * The library will work fine.
32+ *
33+ * - if the activity is unlocked and does NOT handle orientation changes with android:configChanges,
34+ * the actual behavior differs depending on the rotation.
35+ * - the configuration callback is never called, of course.
36+ * - for 90°/-90° rotations, the activity is recreated. Sometime you get {@link #mDisplayOffset}
37+ * callback before destruction, sometimes you don't - in any case it's going to recreate.
38+ * - for 180°/-180°, the activity is NOT recreated! But we can rely on {@link #mDisplayOffset}
39+ * changing with a 180 delta and restart the engine.
40+ *
41+ * - lastly, if the activity is unlocked and DOES handle orientation changes with android:configChanges,
42+ * as it will often be the case in a modern Compose app,
43+ * - you always get the {@link #mDisplayOffset} callback
44+ * - for 90°/-90° rotations, the view also gets the configuration changed callback.
45+ * - for 180°/-180°, the view won't get it because configuration only cares about portrait vs. landscape.
46+ *
47+ * In practice, since we don't control the activity and we can't easily inspect the configChanges
48+ * flags at runtime, a good solution is to always restart when the display offset changes. We might
49+ * do useless restarts in one rare scenario (unlocked, no android:configChanges, 90° rotation,
50+ * display offset callback received before destruction) but that's acceptable.
51+ *
52+ * Tried to avoid that by looking at {@link Activity#isChangingConfigurations()}, but it's always
53+ * false by the time the display offset callback is invoked.
2154 */
2255public class OrientationHelper {
2356
@@ -26,7 +59,7 @@ public class OrientationHelper {
2659 */
2760 public interface Callback {
2861 void onDeviceOrientationChanged (int deviceOrientation );
29- void onDisplayOffsetChanged (int displayOffset , boolean willRecreate );
62+ void onDisplayOffsetChanged ();
3063 }
3164
3265 private final Handler mHandler = new Handler (Looper .getMainLooper ());
@@ -87,9 +120,7 @@ public void onDisplayChanged(int displayId) {
87120 int newDisplayOffset = findDisplayOffset ();
88121 if (newDisplayOffset != oldDisplayOffset ) {
89122 mDisplayOffset = newDisplayOffset ;
90- // With 180 degrees flips, the activity is not recreated.
91- boolean willRecreate = Math .abs (newDisplayOffset - oldDisplayOffset ) != 180 ;
92- mCallback .onDisplayOffsetChanged (newDisplayOffset , willRecreate );
123+ mCallback .onDisplayOffsetChanged ();
93124 }
94125 }
95126 };
0 commit comments