Merge https://github.com/GrapheneOS/Camera into leaf-2.0

Change-Id: I645061333390d38e34ed8f32c66a42d436307184
diff --git a/Android.bp b/Android.bp
new file mode 100644
index 0000000..0fc9a5d
--- /dev/null
+++ b/Android.bp
@@ -0,0 +1,66 @@
+/*
+ * Copyright (c) 2022 The LeafOS Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+android_app {
+    name: "GrapheneCamera",
+    platform_apis: true,
+    system_ext_specific: true,
+    overrides: ["Camera2"],
+    srcs: [
+        "app/src/aosp/java/**/*.java",
+        "app/src/main/java/**/*.java",
+        "app/src/main/java/**/*.kt",
+    ],
+    resource_dirs: [
+        "app/src/aosp/res",
+        "app/src/main/res",
+    ],
+    static_libs: [
+        "androidx.appcompat_appcompat",
+        "androidx.camera_camera-camera2",
+        "androidx.camera_camera-core",
+        "androidx.camera_camera-extensions",
+        "androidx.camera_camera-lifecycle",
+        "androidx.camera_camera-video",
+        "androidx.camera_camera-view",
+        "androidx.concurrent_concurrent-futures",
+        "androidx.databinding_viewbinding",
+        "androidx.documentfile_documentfile",
+        "androidx.exifinterface_exifinterface",
+        "androidx.lifecycle_lifecycle-livedata",
+        "androidx-constraintlayout_constraintlayout",
+        "com.google.android.material_material",
+        "com.google.zxing_core",
+        "guava",
+    ],
+    jni_libs: [
+        "libimage_processing_util_jni",
+    ],
+    optional_uses_libs: [
+        "androidx.camera.extensions.impl",
+    ],
+    kotlincflags: [
+        "-Xuse-experimental=kotlin.ExperimentalStdlibApi",
+    ],
+    manifest: "app/src/main/AndroidManifest.xml",
+    optimize: {
+        enabled: false,
+    },
+    aaptflags: [
+        "--rename-manifest-package",
+        "org.leafos.graphenecamera",
+    ],
+}
diff --git a/app/build.gradle.kts b/app/build.gradle.kts
index c8a10f1..1ea4cca 100644
--- a/app/build.gradle.kts
+++ b/app/build.gradle.kts
@@ -33,7 +33,7 @@
         }
     }
 
-    compileSdk = 32
+    compileSdk = 33
     buildToolsVersion = "33.0.0"
 
     defaultConfig {
diff --git a/app/src/aosp/java/app/grapheneos/camera/BuildConfig.java b/app/src/aosp/java/app/grapheneos/camera/BuildConfig.java
new file mode 100644
index 0000000..a604f1e
--- /dev/null
+++ b/app/src/aosp/java/app/grapheneos/camera/BuildConfig.java
@@ -0,0 +1,8 @@
+package app.grapheneos.camera;
+
+// Stub BuildConfig class to allow building in AOSP
+public class BuildConfig {
+
+    public static final boolean DEBUG = false;
+
+}
diff --git a/app/src/aosp/java/app/grapheneos/camera/databinding/ActivityMainBinding.java b/app/src/aosp/java/app/grapheneos/camera/databinding/ActivityMainBinding.java
new file mode 100644
index 0000000..87e9f06
--- /dev/null
+++ b/app/src/aosp/java/app/grapheneos/camera/databinding/ActivityMainBinding.java
@@ -0,0 +1,617 @@
+// Generated by view binder compiler. Do not edit!
+package app.grapheneos.camera.databinding;
+
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.FrameLayout;
+import android.widget.ImageButton;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.ProgressBar;
+import android.widget.RelativeLayout;
+import android.widget.TextView;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.camera.view.PreviewView;
+import androidx.constraintlayout.widget.ConstraintLayout;
+import androidx.coordinatorlayout.widget.CoordinatorLayout;
+import androidx.viewbinding.ViewBinding;
+import androidx.viewbinding.ViewBindings;
+import androidx.viewpager2.widget.ViewPager2;
+import app.grapheneos.camera.R;
+import app.grapheneos.camera.ui.BottomTabLayout;
+import app.grapheneos.camera.ui.CountDownTimerUI;
+import app.grapheneos.camera.ui.CustomGrid;
+import app.grapheneos.camera.ui.QROverlay;
+import app.grapheneos.camera.ui.QRToggle;
+import app.grapheneos.camera.ui.seekbar.ExposureBar;
+import app.grapheneos.camera.ui.seekbar.ZoomBar;
+import com.google.android.material.imageview.ShapeableImageView;
+import java.lang.NullPointerException;
+import java.lang.Override;
+import java.lang.String;
+
+public final class ActivityMainBinding implements ViewBinding {
+  @NonNull
+  private final CoordinatorLayout rootView;
+
+  @NonNull
+  public final QRToggle aztecToggle;
+
+  @NonNull
+  public final CountDownTimerUI cTimer;
+
+  @NonNull
+  public final BottomTabLayout cameraModeTabs;
+
+  @NonNull
+  public final ImageView cancelButton;
+
+  @NonNull
+  public final ImageButton captureButton;
+
+  @NonNull
+  public final ImageView captureButtonCross;
+
+  @NonNull
+  public final TextView captureButtonText;
+
+  @NonNull
+  public final ImageButton confirmButton;
+
+  @NonNull
+  public final QRToggle dataMatrixToggle;
+
+  @NonNull
+  public final ExposureBar exposureBar;
+
+  @NonNull
+  public final LinearLayout exposureBarPanel;
+
+  @NonNull
+  public final ImageView exposureNegIcon;
+
+  @NonNull
+  public final ImageView exposurePlusIcon;
+
+  @NonNull
+  public final ViewPager2 flashPager;
+
+  @NonNull
+  public final FrameLayout flipCameraCircle;
+
+  @NonNull
+  public final ImageView flipCameraIcon;
+
+  @NonNull
+  public final ImageView flipCameraIconContent;
+
+  @NonNull
+  public final ImageView focusRing;
+
+  @NonNull
+  public final LinearLayout gCircle;
+
+  @NonNull
+  public final FrameLayout gCircleFrame;
+
+  @NonNull
+  public final View gCircleLeftDash;
+
+  @NonNull
+  public final View gCircleLineX;
+
+  @NonNull
+  public final View gCircleLineZ;
+
+  @NonNull
+  public final View gCircleRightDash;
+
+  @NonNull
+  public final TextView gCircleText;
+
+  @NonNull
+  public final ShapeableImageView imagePreview;
+
+  @NonNull
+  public final FrameLayout mainFrame;
+
+  @NonNull
+  public final ImageView mainOverlay;
+
+  @NonNull
+  public final ImageView micOff;
+
+  @NonNull
+  public final ImageView moreOptions;
+
+  @NonNull
+  public final QRToggle pdf417Toggle;
+
+  @NonNull
+  public final ImageView playPreview;
+
+  @NonNull
+  public final PreviewView preview;
+
+  @NonNull
+  public final ConstraintLayout previewContainer;
+
+  @NonNull
+  public final CustomGrid previewGrid;
+
+  @NonNull
+  public final ProgressBar previewLoading;
+
+  @NonNull
+  public final QROverlay qrOverlay;
+
+  @NonNull
+  public final QRToggle qrScanToggle;
+
+  @NonNull
+  public final LinearLayout qrScanToggles;
+
+  @NonNull
+  public final ImageView retakeIcon;
+
+  @NonNull
+  public final RelativeLayout root;
+
+  @NonNull
+  public final ImageButton settingsOption;
+
+  @NonNull
+  public final ImageView thirdCircle;
+
+  @NonNull
+  public final FrameLayout thirdOption;
+
+  @NonNull
+  public final LinearLayout threeButtons;
+
+  @NonNull
+  public final TextView timer;
+
+  @NonNull
+  public final ImageView whiteOptionCircle;
+
+  @NonNull
+  public final ZoomBar zoomBar;
+
+  @NonNull
+  public final LinearLayout zoomBarPanel;
+
+  @NonNull
+  public final ImageView zoomInIcon;
+
+  @NonNull
+  public final ImageView zoomOutIcon;
+
+  private ActivityMainBinding(@NonNull CoordinatorLayout rootView, @NonNull QRToggle aztecToggle,
+      @NonNull CountDownTimerUI cTimer, @NonNull BottomTabLayout cameraModeTabs,
+      @NonNull ImageView cancelButton, @NonNull ImageButton captureButton,
+      @NonNull ImageView captureButtonCross, @NonNull TextView captureButtonText,
+      @NonNull ImageButton confirmButton, @NonNull QRToggle dataMatrixToggle,
+      @NonNull ExposureBar exposureBar, @NonNull LinearLayout exposureBarPanel,
+      @NonNull ImageView exposureNegIcon, @NonNull ImageView exposurePlusIcon,
+      @NonNull ViewPager2 flashPager, @NonNull FrameLayout flipCameraCircle,
+      @NonNull ImageView flipCameraIcon, @NonNull ImageView flipCameraIconContent,
+      @NonNull ImageView focusRing, @NonNull LinearLayout gCircle,
+      @NonNull FrameLayout gCircleFrame, @NonNull View gCircleLeftDash, @NonNull View gCircleLineX,
+      @NonNull View gCircleLineZ, @NonNull View gCircleRightDash, @NonNull TextView gCircleText,
+      @NonNull ShapeableImageView imagePreview, @NonNull FrameLayout mainFrame,
+      @NonNull ImageView mainOverlay, @NonNull ImageView micOff, @NonNull ImageView moreOptions,
+      @NonNull QRToggle pdf417Toggle, @NonNull ImageView playPreview, @NonNull PreviewView preview,
+      @NonNull ConstraintLayout previewContainer, @NonNull CustomGrid previewGrid,
+      @NonNull ProgressBar previewLoading, @NonNull QROverlay qrOverlay,
+      @NonNull QRToggle qrScanToggle, @NonNull LinearLayout qrScanToggles,
+      @NonNull ImageView retakeIcon, @NonNull RelativeLayout root,
+      @NonNull ImageButton settingsOption, @NonNull ImageView thirdCircle,
+      @NonNull FrameLayout thirdOption, @NonNull LinearLayout threeButtons, @NonNull TextView timer,
+      @NonNull ImageView whiteOptionCircle, @NonNull ZoomBar zoomBar,
+      @NonNull LinearLayout zoomBarPanel, @NonNull ImageView zoomInIcon,
+      @NonNull ImageView zoomOutIcon) {
+    this.rootView = rootView;
+    this.aztecToggle = aztecToggle;
+    this.cTimer = cTimer;
+    this.cameraModeTabs = cameraModeTabs;
+    this.cancelButton = cancelButton;
+    this.captureButton = captureButton;
+    this.captureButtonCross = captureButtonCross;
+    this.captureButtonText = captureButtonText;
+    this.confirmButton = confirmButton;
+    this.dataMatrixToggle = dataMatrixToggle;
+    this.exposureBar = exposureBar;
+    this.exposureBarPanel = exposureBarPanel;
+    this.exposureNegIcon = exposureNegIcon;
+    this.exposurePlusIcon = exposurePlusIcon;
+    this.flashPager = flashPager;
+    this.flipCameraCircle = flipCameraCircle;
+    this.flipCameraIcon = flipCameraIcon;
+    this.flipCameraIconContent = flipCameraIconContent;
+    this.focusRing = focusRing;
+    this.gCircle = gCircle;
+    this.gCircleFrame = gCircleFrame;
+    this.gCircleLeftDash = gCircleLeftDash;
+    this.gCircleLineX = gCircleLineX;
+    this.gCircleLineZ = gCircleLineZ;
+    this.gCircleRightDash = gCircleRightDash;
+    this.gCircleText = gCircleText;
+    this.imagePreview = imagePreview;
+    this.mainFrame = mainFrame;
+    this.mainOverlay = mainOverlay;
+    this.micOff = micOff;
+    this.moreOptions = moreOptions;
+    this.pdf417Toggle = pdf417Toggle;
+    this.playPreview = playPreview;
+    this.preview = preview;
+    this.previewContainer = previewContainer;
+    this.previewGrid = previewGrid;
+    this.previewLoading = previewLoading;
+    this.qrOverlay = qrOverlay;
+    this.qrScanToggle = qrScanToggle;
+    this.qrScanToggles = qrScanToggles;
+    this.retakeIcon = retakeIcon;
+    this.root = root;
+    this.settingsOption = settingsOption;
+    this.thirdCircle = thirdCircle;
+    this.thirdOption = thirdOption;
+    this.threeButtons = threeButtons;
+    this.timer = timer;
+    this.whiteOptionCircle = whiteOptionCircle;
+    this.zoomBar = zoomBar;
+    this.zoomBarPanel = zoomBarPanel;
+    this.zoomInIcon = zoomInIcon;
+    this.zoomOutIcon = zoomOutIcon;
+  }
+
+  @Override
+  @NonNull
+  public CoordinatorLayout getRoot() {
+    return rootView;
+  }
+
+  @NonNull
+  public static ActivityMainBinding inflate(@NonNull LayoutInflater inflater) {
+    return inflate(inflater, null, false);
+  }
+
+  @NonNull
+  public static ActivityMainBinding inflate(@NonNull LayoutInflater inflater,
+      @Nullable ViewGroup parent, boolean attachToParent) {
+    View root = inflater.inflate(R.layout.activity_main, parent, false);
+    if (attachToParent) {
+      parent.addView(root);
+    }
+    return bind(root);
+  }
+
+  @NonNull
+  public static ActivityMainBinding bind(@NonNull View rootView) {
+    // The body of this method is generated in a way you would not otherwise write.
+    // This is done to optimize the compiled bytecode for size and performance.
+    int id;
+    missingId: {
+      id = R.id.aztec_toggle;
+      QRToggle aztecToggle = ViewBindings.findChildViewById(rootView, id);
+      if (aztecToggle == null) {
+        break missingId;
+      }
+
+      id = R.id.c_timer;
+      CountDownTimerUI cTimer = ViewBindings.findChildViewById(rootView, id);
+      if (cTimer == null) {
+        break missingId;
+      }
+
+      id = R.id.camera_mode_tabs;
+      BottomTabLayout cameraModeTabs = ViewBindings.findChildViewById(rootView, id);
+      if (cameraModeTabs == null) {
+        break missingId;
+      }
+
+      id = R.id.cancel_button;
+      ImageView cancelButton = ViewBindings.findChildViewById(rootView, id);
+      if (cancelButton == null) {
+        break missingId;
+      }
+
+      id = R.id.capture_button;
+      ImageButton captureButton = ViewBindings.findChildViewById(rootView, id);
+      if (captureButton == null) {
+        break missingId;
+      }
+
+      id = R.id.capture_button_cross;
+      ImageView captureButtonCross = ViewBindings.findChildViewById(rootView, id);
+      if (captureButtonCross == null) {
+        break missingId;
+      }
+
+      id = R.id.capture_button_text;
+      TextView captureButtonText = ViewBindings.findChildViewById(rootView, id);
+      if (captureButtonText == null) {
+        break missingId;
+      }
+
+      id = R.id.confirm_button;
+      ImageButton confirmButton = ViewBindings.findChildViewById(rootView, id);
+      if (confirmButton == null) {
+        break missingId;
+      }
+
+      id = R.id.data_matrix_toggle;
+      QRToggle dataMatrixToggle = ViewBindings.findChildViewById(rootView, id);
+      if (dataMatrixToggle == null) {
+        break missingId;
+      }
+
+      id = R.id.exposure_bar;
+      ExposureBar exposureBar = ViewBindings.findChildViewById(rootView, id);
+      if (exposureBar == null) {
+        break missingId;
+      }
+
+      id = R.id.exposure_bar_panel;
+      LinearLayout exposureBarPanel = ViewBindings.findChildViewById(rootView, id);
+      if (exposureBarPanel == null) {
+        break missingId;
+      }
+
+      id = R.id.exposure_neg_icon;
+      ImageView exposureNegIcon = ViewBindings.findChildViewById(rootView, id);
+      if (exposureNegIcon == null) {
+        break missingId;
+      }
+
+      id = R.id.exposure_plus_icon;
+      ImageView exposurePlusIcon = ViewBindings.findChildViewById(rootView, id);
+      if (exposurePlusIcon == null) {
+        break missingId;
+      }
+
+      id = R.id.flash_pager;
+      ViewPager2 flashPager = ViewBindings.findChildViewById(rootView, id);
+      if (flashPager == null) {
+        break missingId;
+      }
+
+      id = R.id.flip_camera_circle;
+      FrameLayout flipCameraCircle = ViewBindings.findChildViewById(rootView, id);
+      if (flipCameraCircle == null) {
+        break missingId;
+      }
+
+      id = R.id.flip_camera_icon;
+      ImageView flipCameraIcon = ViewBindings.findChildViewById(rootView, id);
+      if (flipCameraIcon == null) {
+        break missingId;
+      }
+
+      id = R.id.flip_camera_icon_content;
+      ImageView flipCameraIconContent = ViewBindings.findChildViewById(rootView, id);
+      if (flipCameraIconContent == null) {
+        break missingId;
+      }
+
+      id = R.id.focusRing;
+      ImageView focusRing = ViewBindings.findChildViewById(rootView, id);
+      if (focusRing == null) {
+        break missingId;
+      }
+
+      id = R.id.g_circle;
+      LinearLayout gCircle = ViewBindings.findChildViewById(rootView, id);
+      if (gCircle == null) {
+        break missingId;
+      }
+
+      id = R.id.g_circle_frame;
+      FrameLayout gCircleFrame = ViewBindings.findChildViewById(rootView, id);
+      if (gCircleFrame == null) {
+        break missingId;
+      }
+
+      id = R.id.g_circle_left_dash;
+      View gCircleLeftDash = ViewBindings.findChildViewById(rootView, id);
+      if (gCircleLeftDash == null) {
+        break missingId;
+      }
+
+      id = R.id.g_circle_line_x;
+      View gCircleLineX = ViewBindings.findChildViewById(rootView, id);
+      if (gCircleLineX == null) {
+        break missingId;
+      }
+
+      id = R.id.g_circle_line_z;
+      View gCircleLineZ = ViewBindings.findChildViewById(rootView, id);
+      if (gCircleLineZ == null) {
+        break missingId;
+      }
+
+      id = R.id.g_circle_right_dash;
+      View gCircleRightDash = ViewBindings.findChildViewById(rootView, id);
+      if (gCircleRightDash == null) {
+        break missingId;
+      }
+
+      id = R.id.g_circle_text;
+      TextView gCircleText = ViewBindings.findChildViewById(rootView, id);
+      if (gCircleText == null) {
+        break missingId;
+      }
+
+      id = R.id.image_preview;
+      ShapeableImageView imagePreview = ViewBindings.findChildViewById(rootView, id);
+      if (imagePreview == null) {
+        break missingId;
+      }
+
+      id = R.id.main_frame;
+      FrameLayout mainFrame = ViewBindings.findChildViewById(rootView, id);
+      if (mainFrame == null) {
+        break missingId;
+      }
+
+      id = R.id.main_overlay;
+      ImageView mainOverlay = ViewBindings.findChildViewById(rootView, id);
+      if (mainOverlay == null) {
+        break missingId;
+      }
+
+      id = R.id.mic_off;
+      ImageView micOff = ViewBindings.findChildViewById(rootView, id);
+      if (micOff == null) {
+        break missingId;
+      }
+
+      id = R.id.more_options;
+      ImageView moreOptions = ViewBindings.findChildViewById(rootView, id);
+      if (moreOptions == null) {
+        break missingId;
+      }
+
+      id = R.id.pdf417_toggle;
+      QRToggle pdf417Toggle = ViewBindings.findChildViewById(rootView, id);
+      if (pdf417Toggle == null) {
+        break missingId;
+      }
+
+      id = R.id.play_preview;
+      ImageView playPreview = ViewBindings.findChildViewById(rootView, id);
+      if (playPreview == null) {
+        break missingId;
+      }
+
+      id = R.id.preview;
+      PreviewView preview = ViewBindings.findChildViewById(rootView, id);
+      if (preview == null) {
+        break missingId;
+      }
+
+      id = R.id.preview_container;
+      ConstraintLayout previewContainer = ViewBindings.findChildViewById(rootView, id);
+      if (previewContainer == null) {
+        break missingId;
+      }
+
+      id = R.id.preview_grid;
+      CustomGrid previewGrid = ViewBindings.findChildViewById(rootView, id);
+      if (previewGrid == null) {
+        break missingId;
+      }
+
+      id = R.id.preview_loading;
+      ProgressBar previewLoading = ViewBindings.findChildViewById(rootView, id);
+      if (previewLoading == null) {
+        break missingId;
+      }
+
+      id = R.id.qr_overlay;
+      QROverlay qrOverlay = ViewBindings.findChildViewById(rootView, id);
+      if (qrOverlay == null) {
+        break missingId;
+      }
+
+      id = R.id.qr_scan_toggle;
+      QRToggle qrScanToggle = ViewBindings.findChildViewById(rootView, id);
+      if (qrScanToggle == null) {
+        break missingId;
+      }
+
+      id = R.id.qr_scan_toggles;
+      LinearLayout qrScanToggles = ViewBindings.findChildViewById(rootView, id);
+      if (qrScanToggles == null) {
+        break missingId;
+      }
+
+      id = R.id.retake_icon;
+      ImageView retakeIcon = ViewBindings.findChildViewById(rootView, id);
+      if (retakeIcon == null) {
+        break missingId;
+      }
+
+      id = R.id.root;
+      RelativeLayout root = ViewBindings.findChildViewById(rootView, id);
+      if (root == null) {
+        break missingId;
+      }
+
+      id = R.id.settings_option;
+      ImageButton settingsOption = ViewBindings.findChildViewById(rootView, id);
+      if (settingsOption == null) {
+        break missingId;
+      }
+
+      id = R.id.third_circle;
+      ImageView thirdCircle = ViewBindings.findChildViewById(rootView, id);
+      if (thirdCircle == null) {
+        break missingId;
+      }
+
+      id = R.id.third_option;
+      FrameLayout thirdOption = ViewBindings.findChildViewById(rootView, id);
+      if (thirdOption == null) {
+        break missingId;
+      }
+
+      id = R.id.three_buttons;
+      LinearLayout threeButtons = ViewBindings.findChildViewById(rootView, id);
+      if (threeButtons == null) {
+        break missingId;
+      }
+
+      id = R.id.timer;
+      TextView timer = ViewBindings.findChildViewById(rootView, id);
+      if (timer == null) {
+        break missingId;
+      }
+
+      id = R.id.white_option_circle;
+      ImageView whiteOptionCircle = ViewBindings.findChildViewById(rootView, id);
+      if (whiteOptionCircle == null) {
+        break missingId;
+      }
+
+      id = R.id.zoom_bar;
+      ZoomBar zoomBar = ViewBindings.findChildViewById(rootView, id);
+      if (zoomBar == null) {
+        break missingId;
+      }
+
+      id = R.id.zoom_bar_panel;
+      LinearLayout zoomBarPanel = ViewBindings.findChildViewById(rootView, id);
+      if (zoomBarPanel == null) {
+        break missingId;
+      }
+
+      id = R.id.zoom_in_icon;
+      ImageView zoomInIcon = ViewBindings.findChildViewById(rootView, id);
+      if (zoomInIcon == null) {
+        break missingId;
+      }
+
+      id = R.id.zoom_out_icon;
+      ImageView zoomOutIcon = ViewBindings.findChildViewById(rootView, id);
+      if (zoomOutIcon == null) {
+        break missingId;
+      }
+
+      return new ActivityMainBinding((CoordinatorLayout) rootView, aztecToggle, cTimer,
+          cameraModeTabs, cancelButton, captureButton, captureButtonCross, captureButtonText,
+          confirmButton, dataMatrixToggle, exposureBar, exposureBarPanel, exposureNegIcon,
+          exposurePlusIcon, flashPager, flipCameraCircle, flipCameraIcon, flipCameraIconContent,
+          focusRing, gCircle, gCircleFrame, gCircleLeftDash, gCircleLineX, gCircleLineZ,
+          gCircleRightDash, gCircleText, imagePreview, mainFrame, mainOverlay, micOff, moreOptions,
+          pdf417Toggle, playPreview, preview, previewContainer, previewGrid, previewLoading,
+          qrOverlay, qrScanToggle, qrScanToggles, retakeIcon, root, settingsOption, thirdCircle,
+          thirdOption, threeButtons, timer, whiteOptionCircle, zoomBar, zoomBarPanel, zoomInIcon,
+          zoomOutIcon);
+    }
+    String missingId = rootView.getResources().getResourceName(id);
+    throw new NullPointerException("Missing required view with ID: ".concat(missingId));
+  }
+}
diff --git a/app/src/aosp/java/app/grapheneos/camera/databinding/GalleryBinding.java b/app/src/aosp/java/app/grapheneos/camera/databinding/GalleryBinding.java
new file mode 100644
index 0000000..016620f
--- /dev/null
+++ b/app/src/aosp/java/app/grapheneos/camera/databinding/GalleryBinding.java
@@ -0,0 +1,87 @@
+// Generated by view binder compiler. Do not edit!
+package app.grapheneos.camera.databinding;
+
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.RelativeLayout;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.viewbinding.ViewBinding;
+import androidx.viewbinding.ViewBindings;
+import androidx.viewpager2.widget.ViewPager2;
+import app.grapheneos.camera.R;
+import java.lang.NullPointerException;
+import java.lang.Override;
+import java.lang.String;
+
+public final class GalleryBinding implements ViewBinding {
+  @NonNull
+  private final RelativeLayout rootView_;
+
+  @NonNull
+  public final ViewPager2 gallerySlider;
+
+  @NonNull
+  public final GalleryPlaceholderBinding placeholderText;
+
+  @NonNull
+  public final RelativeLayout rootView;
+
+  private GalleryBinding(@NonNull RelativeLayout rootView_, @NonNull ViewPager2 gallerySlider,
+      @NonNull GalleryPlaceholderBinding placeholderText, @NonNull RelativeLayout rootView) {
+    this.rootView_ = rootView_;
+    this.gallerySlider = gallerySlider;
+    this.placeholderText = placeholderText;
+    this.rootView = rootView;
+  }
+
+  @Override
+  @NonNull
+  public RelativeLayout getRoot() {
+    return rootView_;
+  }
+
+  @NonNull
+  public static GalleryBinding inflate(@NonNull LayoutInflater inflater) {
+    return inflate(inflater, null, false);
+  }
+
+  @NonNull
+  public static GalleryBinding inflate(@NonNull LayoutInflater inflater, @Nullable ViewGroup parent,
+      boolean attachToParent) {
+    View root = inflater.inflate(R.layout.gallery, parent, false);
+    if (attachToParent) {
+      parent.addView(root);
+    }
+    return bind(root);
+  }
+
+  @NonNull
+  public static GalleryBinding bind(@NonNull View rootView) {
+    // The body of this method is generated in a way you would not otherwise write.
+    // This is done to optimize the compiled bytecode for size and performance.
+    int id;
+    missingId: {
+      id = R.id.gallery_slider;
+      ViewPager2 gallerySlider = ViewBindings.findChildViewById(rootView, id);
+      if (gallerySlider == null) {
+        break missingId;
+      }
+
+      id = R.id.placeholder_text;
+      View placeholderText = ViewBindings.findChildViewById(rootView, id);
+      if (placeholderText == null) {
+        break missingId;
+      }
+      GalleryPlaceholderBinding binding_placeholderText = GalleryPlaceholderBinding.bind(placeholderText);
+
+      RelativeLayout rootView_ = (RelativeLayout) rootView;
+
+      return new GalleryBinding((RelativeLayout) rootView, gallerySlider, binding_placeholderText,
+          rootView_);
+    }
+    String missingId = rootView.getResources().getResourceName(id);
+    throw new NullPointerException("Missing required view with ID: ".concat(missingId));
+  }
+}
diff --git a/app/src/aosp/java/app/grapheneos/camera/databinding/GalleryPlaceholderBinding.java b/app/src/aosp/java/app/grapheneos/camera/databinding/GalleryPlaceholderBinding.java
new file mode 100644
index 0000000..4183938
--- /dev/null
+++ b/app/src/aosp/java/app/grapheneos/camera/databinding/GalleryPlaceholderBinding.java
@@ -0,0 +1,52 @@
+// Generated by view binder compiler. Do not edit!
+package app.grapheneos.camera.databinding;
+
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.TextView;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.viewbinding.ViewBinding;
+import app.grapheneos.camera.R;
+import java.lang.NullPointerException;
+import java.lang.Override;
+
+public final class GalleryPlaceholderBinding implements ViewBinding {
+  @NonNull
+  private final TextView rootView;
+
+  private GalleryPlaceholderBinding(@NonNull TextView rootView) {
+    this.rootView = rootView;
+  }
+
+  @Override
+  @NonNull
+  public TextView getRoot() {
+    return rootView;
+  }
+
+  @NonNull
+  public static GalleryPlaceholderBinding inflate(@NonNull LayoutInflater inflater) {
+    return inflate(inflater, null, false);
+  }
+
+  @NonNull
+  public static GalleryPlaceholderBinding inflate(@NonNull LayoutInflater inflater,
+      @Nullable ViewGroup parent, boolean attachToParent) {
+    View root = inflater.inflate(R.layout.gallery_placeholder, parent, false);
+    if (attachToParent) {
+      parent.addView(root);
+    }
+    return bind(root);
+  }
+
+  @NonNull
+  public static GalleryPlaceholderBinding bind(@NonNull View rootView) {
+    if (rootView == null) {
+      throw new NullPointerException("rootView");
+    }
+
+    return new GalleryPlaceholderBinding((TextView) rootView);
+  }
+}
diff --git a/app/src/aosp/java/app/grapheneos/camera/databinding/GallerySlideBinding.java b/app/src/aosp/java/app/grapheneos/camera/databinding/GallerySlideBinding.java
new file mode 100644
index 0000000..92ac07a
--- /dev/null
+++ b/app/src/aosp/java/app/grapheneos/camera/databinding/GallerySlideBinding.java
@@ -0,0 +1,99 @@
+// Generated by view binder compiler. Do not edit!
+package app.grapheneos.camera.databinding;
+
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.FrameLayout;
+import android.widget.ImageView;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.viewbinding.ViewBinding;
+import androidx.viewbinding.ViewBindings;
+import app.grapheneos.camera.R;
+import app.grapheneos.camera.ui.ZoomableImageView;
+import java.lang.NullPointerException;
+import java.lang.Override;
+import java.lang.String;
+
+public final class GallerySlideBinding implements ViewBinding {
+  @NonNull
+  private final FrameLayout rootView;
+
+  @NonNull
+  public final GalleryPlaceholderBinding placeholderText;
+
+  @NonNull
+  public final ImageView playButton;
+
+  @NonNull
+  public final FrameLayout root;
+
+  @NonNull
+  public final ZoomableImageView slidePreview;
+
+  private GallerySlideBinding(@NonNull FrameLayout rootView,
+      @NonNull GalleryPlaceholderBinding placeholderText, @NonNull ImageView playButton,
+      @NonNull FrameLayout root, @NonNull ZoomableImageView slidePreview) {
+    this.rootView = rootView;
+    this.placeholderText = placeholderText;
+    this.playButton = playButton;
+    this.root = root;
+    this.slidePreview = slidePreview;
+  }
+
+  @Override
+  @NonNull
+  public FrameLayout getRoot() {
+    return rootView;
+  }
+
+  @NonNull
+  public static GallerySlideBinding inflate(@NonNull LayoutInflater inflater) {
+    return inflate(inflater, null, false);
+  }
+
+  @NonNull
+  public static GallerySlideBinding inflate(@NonNull LayoutInflater inflater,
+      @Nullable ViewGroup parent, boolean attachToParent) {
+    View root = inflater.inflate(R.layout.gallery_slide, parent, false);
+    if (attachToParent) {
+      parent.addView(root);
+    }
+    return bind(root);
+  }
+
+  @NonNull
+  public static GallerySlideBinding bind(@NonNull View rootView) {
+    // The body of this method is generated in a way you would not otherwise write.
+    // This is done to optimize the compiled bytecode for size and performance.
+    int id;
+    missingId: {
+      id = R.id.placeholder_text;
+      View placeholderText = ViewBindings.findChildViewById(rootView, id);
+      if (placeholderText == null) {
+        break missingId;
+      }
+      GalleryPlaceholderBinding binding_placeholderText = GalleryPlaceholderBinding.bind(placeholderText);
+
+      id = R.id.play_button;
+      ImageView playButton = ViewBindings.findChildViewById(rootView, id);
+      if (playButton == null) {
+        break missingId;
+      }
+
+      FrameLayout root = (FrameLayout) rootView;
+
+      id = R.id.slide_preview;
+      ZoomableImageView slidePreview = ViewBindings.findChildViewById(rootView, id);
+      if (slidePreview == null) {
+        break missingId;
+      }
+
+      return new GallerySlideBinding((FrameLayout) rootView, binding_placeholderText, playButton,
+          root, slidePreview);
+    }
+    String missingId = rootView.getResources().getResourceName(id);
+    throw new NullPointerException("Missing required view with ID: ".concat(missingId));
+  }
+}
diff --git a/app/src/aosp/java/app/grapheneos/camera/databinding/MoreSettingsBinding.java b/app/src/aosp/java/app/grapheneos/camera/databinding/MoreSettingsBinding.java
new file mode 100644
index 0000000..cf0dc70
--- /dev/null
+++ b/app/src/aosp/java/app/grapheneos/camera/databinding/MoreSettingsBinding.java
@@ -0,0 +1,486 @@
+// Generated by view binder compiler. Do not edit!
+package app.grapheneos.camera.databinding;
+
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.AutoCompleteTextView;
+import android.widget.EditText;
+import android.widget.ImageButton;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.ScrollView;
+import android.widget.TextView;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.appcompat.widget.SwitchCompat;
+import androidx.viewbinding.ViewBinding;
+import androidx.viewbinding.ViewBindings;
+import app.grapheneos.camera.R;
+import java.lang.NullPointerException;
+import java.lang.Override;
+import java.lang.String;
+
+public final class MoreSettingsBinding implements ViewBinding {
+  @NonNull
+  private final ScrollView rootView_;
+
+  @NonNull
+  public final TextView cameraSoundsDescription;
+
+  @NonNull
+  public final ImageView cameraSoundsIcon;
+
+  @NonNull
+  public final LinearLayout cameraSoundsSetting;
+
+  @NonNull
+  public final SwitchCompat cameraSoundsSwitch;
+
+  @NonNull
+  public final TextView cameraSoundsTitle;
+
+  @NonNull
+  public final LinearLayout gyroscopeSetting;
+
+  @NonNull
+  public final TextView gyroscopeSettingDescription;
+
+  @NonNull
+  public final ImageView gyroscopeSettingIcon;
+
+  @NonNull
+  public final SwitchCompat gyroscopeSettingSwitch;
+
+  @NonNull
+  public final TextView gyroscopeSettingTitle;
+
+  @NonNull
+  public final LinearLayout imageFormatSetting;
+
+  @NonNull
+  public final AutoCompleteTextView imageFormatSettingField;
+
+  @NonNull
+  public final ImageView imageFormatSettingIcon;
+
+  @NonNull
+  public final TextView imageFormatSettingTitle;
+
+  @NonNull
+  public final EditText photoQuality;
+
+  @NonNull
+  public final ImageView photoQualityIcon;
+
+  @NonNull
+  public final LinearLayout photoQualitySetting;
+
+  @NonNull
+  public final TextView photoQualitySubtitle;
+
+  @NonNull
+  public final TextView photoQualityTitle;
+
+  @NonNull
+  public final ImageButton refreshStorageLocation;
+
+  @NonNull
+  public final ImageView removeExifIcon;
+
+  @NonNull
+  public final LinearLayout removeExifSetting;
+
+  @NonNull
+  public final TextView removeExifSubtitle;
+
+  @NonNull
+  public final TextView removeExifTitle;
+
+  @NonNull
+  public final SwitchCompat removeExifToggle;
+
+  @NonNull
+  public final LinearLayout rootView;
+
+  @NonNull
+  public final ImageView saveImageAsPreviewIcon;
+
+  @NonNull
+  public final LinearLayout saveImageAsPreviewSetting;
+
+  @NonNull
+  public final TextView saveImageAsPreviewSubtitle;
+
+  @NonNull
+  public final TextView saveImageAsPreviewTitle;
+
+  @NonNull
+  public final SwitchCompat saveImageAsPreviewToggle;
+
+  @NonNull
+  public final EditText storageLocationField;
+
+  @NonNull
+  public final ImageView storageLocationIcon;
+
+  @NonNull
+  public final LinearLayout storageLocationSetting;
+
+  @NonNull
+  public final TextView storageLocationTitle;
+
+  @NonNull
+  public final LinearLayout videoFormatSetting;
+
+  @NonNull
+  public final AutoCompleteTextView videoFormatSettingField;
+
+  @NonNull
+  public final ImageView videoFormatSettingIcon;
+
+  @NonNull
+  public final TextView videoFormatSettingTitle;
+
+  private MoreSettingsBinding(@NonNull ScrollView rootView_,
+      @NonNull TextView cameraSoundsDescription, @NonNull ImageView cameraSoundsIcon,
+      @NonNull LinearLayout cameraSoundsSetting, @NonNull SwitchCompat cameraSoundsSwitch,
+      @NonNull TextView cameraSoundsTitle, @NonNull LinearLayout gyroscopeSetting,
+      @NonNull TextView gyroscopeSettingDescription, @NonNull ImageView gyroscopeSettingIcon,
+      @NonNull SwitchCompat gyroscopeSettingSwitch, @NonNull TextView gyroscopeSettingTitle,
+      @NonNull LinearLayout imageFormatSetting,
+      @NonNull AutoCompleteTextView imageFormatSettingField,
+      @NonNull ImageView imageFormatSettingIcon, @NonNull TextView imageFormatSettingTitle,
+      @NonNull EditText photoQuality, @NonNull ImageView photoQualityIcon,
+      @NonNull LinearLayout photoQualitySetting, @NonNull TextView photoQualitySubtitle,
+      @NonNull TextView photoQualityTitle, @NonNull ImageButton refreshStorageLocation,
+      @NonNull ImageView removeExifIcon, @NonNull LinearLayout removeExifSetting,
+      @NonNull TextView removeExifSubtitle, @NonNull TextView removeExifTitle,
+      @NonNull SwitchCompat removeExifToggle, @NonNull LinearLayout rootView,
+      @NonNull ImageView saveImageAsPreviewIcon, @NonNull LinearLayout saveImageAsPreviewSetting,
+      @NonNull TextView saveImageAsPreviewSubtitle, @NonNull TextView saveImageAsPreviewTitle,
+      @NonNull SwitchCompat saveImageAsPreviewToggle, @NonNull EditText storageLocationField,
+      @NonNull ImageView storageLocationIcon, @NonNull LinearLayout storageLocationSetting,
+      @NonNull TextView storageLocationTitle, @NonNull LinearLayout videoFormatSetting,
+      @NonNull AutoCompleteTextView videoFormatSettingField,
+      @NonNull ImageView videoFormatSettingIcon, @NonNull TextView videoFormatSettingTitle) {
+    this.rootView_ = rootView_;
+    this.cameraSoundsDescription = cameraSoundsDescription;
+    this.cameraSoundsIcon = cameraSoundsIcon;
+    this.cameraSoundsSetting = cameraSoundsSetting;
+    this.cameraSoundsSwitch = cameraSoundsSwitch;
+    this.cameraSoundsTitle = cameraSoundsTitle;
+    this.gyroscopeSetting = gyroscopeSetting;
+    this.gyroscopeSettingDescription = gyroscopeSettingDescription;
+    this.gyroscopeSettingIcon = gyroscopeSettingIcon;
+    this.gyroscopeSettingSwitch = gyroscopeSettingSwitch;
+    this.gyroscopeSettingTitle = gyroscopeSettingTitle;
+    this.imageFormatSetting = imageFormatSetting;
+    this.imageFormatSettingField = imageFormatSettingField;
+    this.imageFormatSettingIcon = imageFormatSettingIcon;
+    this.imageFormatSettingTitle = imageFormatSettingTitle;
+    this.photoQuality = photoQuality;
+    this.photoQualityIcon = photoQualityIcon;
+    this.photoQualitySetting = photoQualitySetting;
+    this.photoQualitySubtitle = photoQualitySubtitle;
+    this.photoQualityTitle = photoQualityTitle;
+    this.refreshStorageLocation = refreshStorageLocation;
+    this.removeExifIcon = removeExifIcon;
+    this.removeExifSetting = removeExifSetting;
+    this.removeExifSubtitle = removeExifSubtitle;
+    this.removeExifTitle = removeExifTitle;
+    this.removeExifToggle = removeExifToggle;
+    this.rootView = rootView;
+    this.saveImageAsPreviewIcon = saveImageAsPreviewIcon;
+    this.saveImageAsPreviewSetting = saveImageAsPreviewSetting;
+    this.saveImageAsPreviewSubtitle = saveImageAsPreviewSubtitle;
+    this.saveImageAsPreviewTitle = saveImageAsPreviewTitle;
+    this.saveImageAsPreviewToggle = saveImageAsPreviewToggle;
+    this.storageLocationField = storageLocationField;
+    this.storageLocationIcon = storageLocationIcon;
+    this.storageLocationSetting = storageLocationSetting;
+    this.storageLocationTitle = storageLocationTitle;
+    this.videoFormatSetting = videoFormatSetting;
+    this.videoFormatSettingField = videoFormatSettingField;
+    this.videoFormatSettingIcon = videoFormatSettingIcon;
+    this.videoFormatSettingTitle = videoFormatSettingTitle;
+  }
+
+  @Override
+  @NonNull
+  public ScrollView getRoot() {
+    return rootView_;
+  }
+
+  @NonNull
+  public static MoreSettingsBinding inflate(@NonNull LayoutInflater inflater) {
+    return inflate(inflater, null, false);
+  }
+
+  @NonNull
+  public static MoreSettingsBinding inflate(@NonNull LayoutInflater inflater,
+      @Nullable ViewGroup parent, boolean attachToParent) {
+    View root = inflater.inflate(R.layout.more_settings, parent, false);
+    if (attachToParent) {
+      parent.addView(root);
+    }
+    return bind(root);
+  }
+
+  @NonNull
+  public static MoreSettingsBinding bind(@NonNull View rootView) {
+    // The body of this method is generated in a way you would not otherwise write.
+    // This is done to optimize the compiled bytecode for size and performance.
+    int id;
+    missingId: {
+      id = R.id.camera_sounds_description;
+      TextView cameraSoundsDescription = ViewBindings.findChildViewById(rootView, id);
+      if (cameraSoundsDescription == null) {
+        break missingId;
+      }
+
+      id = R.id.camera_sounds_icon;
+      ImageView cameraSoundsIcon = ViewBindings.findChildViewById(rootView, id);
+      if (cameraSoundsIcon == null) {
+        break missingId;
+      }
+
+      id = R.id.camera_sounds_setting;
+      LinearLayout cameraSoundsSetting = ViewBindings.findChildViewById(rootView, id);
+      if (cameraSoundsSetting == null) {
+        break missingId;
+      }
+
+      id = R.id.camera_sounds_switch;
+      SwitchCompat cameraSoundsSwitch = ViewBindings.findChildViewById(rootView, id);
+      if (cameraSoundsSwitch == null) {
+        break missingId;
+      }
+
+      id = R.id.camera_sounds_title;
+      TextView cameraSoundsTitle = ViewBindings.findChildViewById(rootView, id);
+      if (cameraSoundsTitle == null) {
+        break missingId;
+      }
+
+      id = R.id.gyroscope_setting;
+      LinearLayout gyroscopeSetting = ViewBindings.findChildViewById(rootView, id);
+      if (gyroscopeSetting == null) {
+        break missingId;
+      }
+
+      id = R.id.gyroscope_setting_description;
+      TextView gyroscopeSettingDescription = ViewBindings.findChildViewById(rootView, id);
+      if (gyroscopeSettingDescription == null) {
+        break missingId;
+      }
+
+      id = R.id.gyroscope_setting_icon;
+      ImageView gyroscopeSettingIcon = ViewBindings.findChildViewById(rootView, id);
+      if (gyroscopeSettingIcon == null) {
+        break missingId;
+      }
+
+      id = R.id.gyroscope_setting_switch;
+      SwitchCompat gyroscopeSettingSwitch = ViewBindings.findChildViewById(rootView, id);
+      if (gyroscopeSettingSwitch == null) {
+        break missingId;
+      }
+
+      id = R.id.gyroscope_setting_title;
+      TextView gyroscopeSettingTitle = ViewBindings.findChildViewById(rootView, id);
+      if (gyroscopeSettingTitle == null) {
+        break missingId;
+      }
+
+      id = R.id.image_format_setting;
+      LinearLayout imageFormatSetting = ViewBindings.findChildViewById(rootView, id);
+      if (imageFormatSetting == null) {
+        break missingId;
+      }
+
+      id = R.id.image_format_setting_field;
+      AutoCompleteTextView imageFormatSettingField = ViewBindings.findChildViewById(rootView, id);
+      if (imageFormatSettingField == null) {
+        break missingId;
+      }
+
+      id = R.id.image_format_setting_icon;
+      ImageView imageFormatSettingIcon = ViewBindings.findChildViewById(rootView, id);
+      if (imageFormatSettingIcon == null) {
+        break missingId;
+      }
+
+      id = R.id.image_format_setting_title;
+      TextView imageFormatSettingTitle = ViewBindings.findChildViewById(rootView, id);
+      if (imageFormatSettingTitle == null) {
+        break missingId;
+      }
+
+      id = R.id.photo_quality;
+      EditText photoQuality = ViewBindings.findChildViewById(rootView, id);
+      if (photoQuality == null) {
+        break missingId;
+      }
+
+      id = R.id.photo_quality_icon;
+      ImageView photoQualityIcon = ViewBindings.findChildViewById(rootView, id);
+      if (photoQualityIcon == null) {
+        break missingId;
+      }
+
+      id = R.id.photo_quality_setting;
+      LinearLayout photoQualitySetting = ViewBindings.findChildViewById(rootView, id);
+      if (photoQualitySetting == null) {
+        break missingId;
+      }
+
+      id = R.id.photo_quality_subtitle;
+      TextView photoQualitySubtitle = ViewBindings.findChildViewById(rootView, id);
+      if (photoQualitySubtitle == null) {
+        break missingId;
+      }
+
+      id = R.id.photo_quality_title;
+      TextView photoQualityTitle = ViewBindings.findChildViewById(rootView, id);
+      if (photoQualityTitle == null) {
+        break missingId;
+      }
+
+      id = R.id.refresh_storage_location;
+      ImageButton refreshStorageLocation = ViewBindings.findChildViewById(rootView, id);
+      if (refreshStorageLocation == null) {
+        break missingId;
+      }
+
+      id = R.id.remove_exif_icon;
+      ImageView removeExifIcon = ViewBindings.findChildViewById(rootView, id);
+      if (removeExifIcon == null) {
+        break missingId;
+      }
+
+      id = R.id.remove_exif_setting;
+      LinearLayout removeExifSetting = ViewBindings.findChildViewById(rootView, id);
+      if (removeExifSetting == null) {
+        break missingId;
+      }
+
+      id = R.id.remove_exif_subtitle;
+      TextView removeExifSubtitle = ViewBindings.findChildViewById(rootView, id);
+      if (removeExifSubtitle == null) {
+        break missingId;
+      }
+
+      id = R.id.remove_exif_title;
+      TextView removeExifTitle = ViewBindings.findChildViewById(rootView, id);
+      if (removeExifTitle == null) {
+        break missingId;
+      }
+
+      id = R.id.remove_exif_toggle;
+      SwitchCompat removeExifToggle = ViewBindings.findChildViewById(rootView, id);
+      if (removeExifToggle == null) {
+        break missingId;
+      }
+
+      id = R.id.root_view;
+      LinearLayout rootView_ = ViewBindings.findChildViewById(rootView, id);
+      if (rootView_ == null) {
+        break missingId;
+      }
+
+      id = R.id.save_image_as_preview_icon;
+      ImageView saveImageAsPreviewIcon = ViewBindings.findChildViewById(rootView, id);
+      if (saveImageAsPreviewIcon == null) {
+        break missingId;
+      }
+
+      id = R.id.save_image_as_preview_setting;
+      LinearLayout saveImageAsPreviewSetting = ViewBindings.findChildViewById(rootView, id);
+      if (saveImageAsPreviewSetting == null) {
+        break missingId;
+      }
+
+      id = R.id.save_image_as_preview_subtitle;
+      TextView saveImageAsPreviewSubtitle = ViewBindings.findChildViewById(rootView, id);
+      if (saveImageAsPreviewSubtitle == null) {
+        break missingId;
+      }
+
+      id = R.id.save_image_as_preview_title;
+      TextView saveImageAsPreviewTitle = ViewBindings.findChildViewById(rootView, id);
+      if (saveImageAsPreviewTitle == null) {
+        break missingId;
+      }
+
+      id = R.id.save_image_as_preview_toggle;
+      SwitchCompat saveImageAsPreviewToggle = ViewBindings.findChildViewById(rootView, id);
+      if (saveImageAsPreviewToggle == null) {
+        break missingId;
+      }
+
+      id = R.id.storage_location_field;
+      EditText storageLocationField = ViewBindings.findChildViewById(rootView, id);
+      if (storageLocationField == null) {
+        break missingId;
+      }
+
+      id = R.id.storage_location_icon;
+      ImageView storageLocationIcon = ViewBindings.findChildViewById(rootView, id);
+      if (storageLocationIcon == null) {
+        break missingId;
+      }
+
+      id = R.id.storage_location_setting;
+      LinearLayout storageLocationSetting = ViewBindings.findChildViewById(rootView, id);
+      if (storageLocationSetting == null) {
+        break missingId;
+      }
+
+      id = R.id.storage_location_title;
+      TextView storageLocationTitle = ViewBindings.findChildViewById(rootView, id);
+      if (storageLocationTitle == null) {
+        break missingId;
+      }
+
+      id = R.id.video_format_setting;
+      LinearLayout videoFormatSetting = ViewBindings.findChildViewById(rootView, id);
+      if (videoFormatSetting == null) {
+        break missingId;
+      }
+
+      id = R.id.video_format_setting_field;
+      AutoCompleteTextView videoFormatSettingField = ViewBindings.findChildViewById(rootView, id);
+      if (videoFormatSettingField == null) {
+        break missingId;
+      }
+
+      id = R.id.video_format_setting_icon;
+      ImageView videoFormatSettingIcon = ViewBindings.findChildViewById(rootView, id);
+      if (videoFormatSettingIcon == null) {
+        break missingId;
+      }
+
+      id = R.id.video_format_setting_title;
+      TextView videoFormatSettingTitle = ViewBindings.findChildViewById(rootView, id);
+      if (videoFormatSettingTitle == null) {
+        break missingId;
+      }
+
+      return new MoreSettingsBinding((ScrollView) rootView, cameraSoundsDescription,
+          cameraSoundsIcon, cameraSoundsSetting, cameraSoundsSwitch, cameraSoundsTitle,
+          gyroscopeSetting, gyroscopeSettingDescription, gyroscopeSettingIcon,
+          gyroscopeSettingSwitch, gyroscopeSettingTitle, imageFormatSetting,
+          imageFormatSettingField, imageFormatSettingIcon, imageFormatSettingTitle, photoQuality,
+          photoQualityIcon, photoQualitySetting, photoQualitySubtitle, photoQualityTitle,
+          refreshStorageLocation, removeExifIcon, removeExifSetting, removeExifSubtitle,
+          removeExifTitle, removeExifToggle, rootView_, saveImageAsPreviewIcon,
+          saveImageAsPreviewSetting, saveImageAsPreviewSubtitle, saveImageAsPreviewTitle,
+          saveImageAsPreviewToggle, storageLocationField, storageLocationIcon,
+          storageLocationSetting, storageLocationTitle, videoFormatSetting, videoFormatSettingField,
+          videoFormatSettingIcon, videoFormatSettingTitle);
+    }
+    String missingId = rootView.getResources().getResourceName(id);
+    throw new NullPointerException("Missing required view with ID: ".concat(missingId));
+  }
+}
diff --git a/app/src/aosp/java/app/grapheneos/camera/databinding/ScanResultDialogBinding.java b/app/src/aosp/java/app/grapheneos/camera/databinding/ScanResultDialogBinding.java
new file mode 100644
index 0000000..c0c82ac
--- /dev/null
+++ b/app/src/aosp/java/app/grapheneos/camera/databinding/ScanResultDialogBinding.java
@@ -0,0 +1,103 @@
+// Generated by view binder compiler. Do not edit!
+package app.grapheneos.camera.databinding;
+
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ImageButton;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.viewbinding.ViewBinding;
+import androidx.viewbinding.ViewBindings;
+import app.grapheneos.camera.R;
+import com.google.android.material.tabs.TabLayout;
+import java.lang.NullPointerException;
+import java.lang.Override;
+import java.lang.String;
+
+public final class ScanResultDialogBinding implements ViewBinding {
+  @NonNull
+  private final LinearLayout rootView;
+
+  @NonNull
+  public final ImageButton copyQrText;
+
+  @NonNull
+  public final TabLayout encodingTabs;
+
+  @NonNull
+  public final TextView scanResultText;
+
+  @NonNull
+  public final ImageButton shareQrText;
+
+  private ScanResultDialogBinding(@NonNull LinearLayout rootView, @NonNull ImageButton copyQrText,
+      @NonNull TabLayout encodingTabs, @NonNull TextView scanResultText,
+      @NonNull ImageButton shareQrText) {
+    this.rootView = rootView;
+    this.copyQrText = copyQrText;
+    this.encodingTabs = encodingTabs;
+    this.scanResultText = scanResultText;
+    this.shareQrText = shareQrText;
+  }
+
+  @Override
+  @NonNull
+  public LinearLayout getRoot() {
+    return rootView;
+  }
+
+  @NonNull
+  public static ScanResultDialogBinding inflate(@NonNull LayoutInflater inflater) {
+    return inflate(inflater, null, false);
+  }
+
+  @NonNull
+  public static ScanResultDialogBinding inflate(@NonNull LayoutInflater inflater,
+      @Nullable ViewGroup parent, boolean attachToParent) {
+    View root = inflater.inflate(R.layout.scan_result_dialog, parent, false);
+    if (attachToParent) {
+      parent.addView(root);
+    }
+    return bind(root);
+  }
+
+  @NonNull
+  public static ScanResultDialogBinding bind(@NonNull View rootView) {
+    // The body of this method is generated in a way you would not otherwise write.
+    // This is done to optimize the compiled bytecode for size and performance.
+    int id;
+    missingId: {
+      id = R.id.copy_qr_text;
+      ImageButton copyQrText = ViewBindings.findChildViewById(rootView, id);
+      if (copyQrText == null) {
+        break missingId;
+      }
+
+      id = R.id.encoding_tabs;
+      TabLayout encodingTabs = ViewBindings.findChildViewById(rootView, id);
+      if (encodingTabs == null) {
+        break missingId;
+      }
+
+      id = R.id.scan_result_text;
+      TextView scanResultText = ViewBindings.findChildViewById(rootView, id);
+      if (scanResultText == null) {
+        break missingId;
+      }
+
+      id = R.id.share_qr_text;
+      ImageButton shareQrText = ViewBindings.findChildViewById(rootView, id);
+      if (shareQrText == null) {
+        break missingId;
+      }
+
+      return new ScanResultDialogBinding((LinearLayout) rootView, copyQrText, encodingTabs,
+          scanResultText, shareQrText);
+    }
+    String missingId = rootView.getResources().getResourceName(id);
+    throw new NullPointerException("Missing required view with ID: ".concat(missingId));
+  }
+}
diff --git a/app/src/aosp/java/app/grapheneos/camera/databinding/SettingsBinding.java b/app/src/aosp/java/app/grapheneos/camera/databinding/SettingsBinding.java
new file mode 100644
index 0000000..5574690
--- /dev/null
+++ b/app/src/aosp/java/app/grapheneos/camera/databinding/SettingsBinding.java
@@ -0,0 +1,341 @@
+// Generated by view binder compiler. Do not edit!
+package app.grapheneos.camera.databinding;
+
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.RadioButton;
+import android.widget.RadioGroup;
+import android.widget.RelativeLayout;
+import android.widget.ScrollView;
+import android.widget.Spinner;
+import android.widget.TextView;
+import android.widget.ToggleButton;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.appcompat.widget.SwitchCompat;
+import androidx.viewbinding.ViewBinding;
+import androidx.viewbinding.ViewBindings;
+import app.grapheneos.camera.R;
+import app.grapheneos.camera.ui.SettingsFrameLayout;
+import java.lang.NullPointerException;
+import java.lang.Override;
+import java.lang.String;
+
+public final class SettingsBinding implements ViewBinding {
+  @NonNull
+  private final SettingsFrameLayout rootView;
+
+  @NonNull
+  public final ToggleButton aspectRatioToggle;
+
+  @NonNull
+  public final View background;
+
+  @NonNull
+  public final RadioGroup cmRadioGroup;
+
+  @NonNull
+  public final LinearLayout enableEisSetting;
+
+  @NonNull
+  public final SwitchCompat enableEisSwitch;
+
+  @NonNull
+  public final ImageView flashToggleOption;
+
+  @NonNull
+  public final Spinner focusTimeoutSpinner;
+
+  @NonNull
+  public final ImageView gridToggleOption;
+
+  @NonNull
+  public final LinearLayout includeAudioSetting;
+
+  @NonNull
+  public final SwitchCompat includeAudioSwitch;
+
+  @NonNull
+  public final RadioButton latencyRadio;
+
+  @NonNull
+  public final ToggleButton locationToggle;
+
+  @NonNull
+  public final TextView moreSettings;
+
+  @NonNull
+  public final RadioButton qualityRadio;
+
+  @NonNull
+  public final SettingsFrameLayout root;
+
+  @NonNull
+  public final LinearLayout selfIlluminationSetting;
+
+  @NonNull
+  public final SwitchCompat selfIlluminationSwitch;
+
+  @NonNull
+  public final LinearLayout settingsDialog;
+
+  @NonNull
+  public final RelativeLayout settingsFrame;
+
+  @NonNull
+  public final ScrollView settingsScrollview;
+
+  @NonNull
+  public final LinearLayout settingsScrollviewContent;
+
+  @NonNull
+  public final LinearLayout timerSetting;
+
+  @NonNull
+  public final Spinner timerSpinner;
+
+  @NonNull
+  public final ToggleButton torchToggleOption;
+
+  @NonNull
+  public final LinearLayout videoQualitySetting;
+
+  @NonNull
+  public final Spinner videoQualitySpinner;
+
+  private SettingsBinding(@NonNull SettingsFrameLayout rootView,
+      @NonNull ToggleButton aspectRatioToggle, @NonNull View background,
+      @NonNull RadioGroup cmRadioGroup, @NonNull LinearLayout enableEisSetting,
+      @NonNull SwitchCompat enableEisSwitch, @NonNull ImageView flashToggleOption,
+      @NonNull Spinner focusTimeoutSpinner, @NonNull ImageView gridToggleOption,
+      @NonNull LinearLayout includeAudioSetting, @NonNull SwitchCompat includeAudioSwitch,
+      @NonNull RadioButton latencyRadio, @NonNull ToggleButton locationToggle,
+      @NonNull TextView moreSettings, @NonNull RadioButton qualityRadio,
+      @NonNull SettingsFrameLayout root, @NonNull LinearLayout selfIlluminationSetting,
+      @NonNull SwitchCompat selfIlluminationSwitch, @NonNull LinearLayout settingsDialog,
+      @NonNull RelativeLayout settingsFrame, @NonNull ScrollView settingsScrollview,
+      @NonNull LinearLayout settingsScrollviewContent, @NonNull LinearLayout timerSetting,
+      @NonNull Spinner timerSpinner, @NonNull ToggleButton torchToggleOption,
+      @NonNull LinearLayout videoQualitySetting, @NonNull Spinner videoQualitySpinner) {
+    this.rootView = rootView;
+    this.aspectRatioToggle = aspectRatioToggle;
+    this.background = background;
+    this.cmRadioGroup = cmRadioGroup;
+    this.enableEisSetting = enableEisSetting;
+    this.enableEisSwitch = enableEisSwitch;
+    this.flashToggleOption = flashToggleOption;
+    this.focusTimeoutSpinner = focusTimeoutSpinner;
+    this.gridToggleOption = gridToggleOption;
+    this.includeAudioSetting = includeAudioSetting;
+    this.includeAudioSwitch = includeAudioSwitch;
+    this.latencyRadio = latencyRadio;
+    this.locationToggle = locationToggle;
+    this.moreSettings = moreSettings;
+    this.qualityRadio = qualityRadio;
+    this.root = root;
+    this.selfIlluminationSetting = selfIlluminationSetting;
+    this.selfIlluminationSwitch = selfIlluminationSwitch;
+    this.settingsDialog = settingsDialog;
+    this.settingsFrame = settingsFrame;
+    this.settingsScrollview = settingsScrollview;
+    this.settingsScrollviewContent = settingsScrollviewContent;
+    this.timerSetting = timerSetting;
+    this.timerSpinner = timerSpinner;
+    this.torchToggleOption = torchToggleOption;
+    this.videoQualitySetting = videoQualitySetting;
+    this.videoQualitySpinner = videoQualitySpinner;
+  }
+
+  @Override
+  @NonNull
+  public SettingsFrameLayout getRoot() {
+    return rootView;
+  }
+
+  @NonNull
+  public static SettingsBinding inflate(@NonNull LayoutInflater inflater) {
+    return inflate(inflater, null, false);
+  }
+
+  @NonNull
+  public static SettingsBinding inflate(@NonNull LayoutInflater inflater,
+      @Nullable ViewGroup parent, boolean attachToParent) {
+    View root = inflater.inflate(R.layout.settings, parent, false);
+    if (attachToParent) {
+      parent.addView(root);
+    }
+    return bind(root);
+  }
+
+  @NonNull
+  public static SettingsBinding bind(@NonNull View rootView) {
+    // The body of this method is generated in a way you would not otherwise write.
+    // This is done to optimize the compiled bytecode for size and performance.
+    int id;
+    missingId: {
+      id = R.id.aspect_ratio_toggle;
+      ToggleButton aspectRatioToggle = ViewBindings.findChildViewById(rootView, id);
+      if (aspectRatioToggle == null) {
+        break missingId;
+      }
+
+      id = R.id.background;
+      View background = ViewBindings.findChildViewById(rootView, id);
+      if (background == null) {
+        break missingId;
+      }
+
+      id = R.id.cm_radio_group;
+      RadioGroup cmRadioGroup = ViewBindings.findChildViewById(rootView, id);
+      if (cmRadioGroup == null) {
+        break missingId;
+      }
+
+      id = R.id.enable_eis_setting;
+      LinearLayout enableEisSetting = ViewBindings.findChildViewById(rootView, id);
+      if (enableEisSetting == null) {
+        break missingId;
+      }
+
+      id = R.id.enable_eis_switch;
+      SwitchCompat enableEisSwitch = ViewBindings.findChildViewById(rootView, id);
+      if (enableEisSwitch == null) {
+        break missingId;
+      }
+
+      id = R.id.flash_toggle_option;
+      ImageView flashToggleOption = ViewBindings.findChildViewById(rootView, id);
+      if (flashToggleOption == null) {
+        break missingId;
+      }
+
+      id = R.id.focus_timeout_spinner;
+      Spinner focusTimeoutSpinner = ViewBindings.findChildViewById(rootView, id);
+      if (focusTimeoutSpinner == null) {
+        break missingId;
+      }
+
+      id = R.id.grid_toggle_option;
+      ImageView gridToggleOption = ViewBindings.findChildViewById(rootView, id);
+      if (gridToggleOption == null) {
+        break missingId;
+      }
+
+      id = R.id.include_audio_setting;
+      LinearLayout includeAudioSetting = ViewBindings.findChildViewById(rootView, id);
+      if (includeAudioSetting == null) {
+        break missingId;
+      }
+
+      id = R.id.include_audio_switch;
+      SwitchCompat includeAudioSwitch = ViewBindings.findChildViewById(rootView, id);
+      if (includeAudioSwitch == null) {
+        break missingId;
+      }
+
+      id = R.id.latency_radio;
+      RadioButton latencyRadio = ViewBindings.findChildViewById(rootView, id);
+      if (latencyRadio == null) {
+        break missingId;
+      }
+
+      id = R.id.location_toggle;
+      ToggleButton locationToggle = ViewBindings.findChildViewById(rootView, id);
+      if (locationToggle == null) {
+        break missingId;
+      }
+
+      id = R.id.more_settings;
+      TextView moreSettings = ViewBindings.findChildViewById(rootView, id);
+      if (moreSettings == null) {
+        break missingId;
+      }
+
+      id = R.id.quality_radio;
+      RadioButton qualityRadio = ViewBindings.findChildViewById(rootView, id);
+      if (qualityRadio == null) {
+        break missingId;
+      }
+
+      SettingsFrameLayout root = (SettingsFrameLayout) rootView;
+
+      id = R.id.self_illumination_setting;
+      LinearLayout selfIlluminationSetting = ViewBindings.findChildViewById(rootView, id);
+      if (selfIlluminationSetting == null) {
+        break missingId;
+      }
+
+      id = R.id.self_illumination_switch;
+      SwitchCompat selfIlluminationSwitch = ViewBindings.findChildViewById(rootView, id);
+      if (selfIlluminationSwitch == null) {
+        break missingId;
+      }
+
+      id = R.id.settings_dialog;
+      LinearLayout settingsDialog = ViewBindings.findChildViewById(rootView, id);
+      if (settingsDialog == null) {
+        break missingId;
+      }
+
+      id = R.id.settings_frame;
+      RelativeLayout settingsFrame = ViewBindings.findChildViewById(rootView, id);
+      if (settingsFrame == null) {
+        break missingId;
+      }
+
+      id = R.id.settings_scrollview;
+      ScrollView settingsScrollview = ViewBindings.findChildViewById(rootView, id);
+      if (settingsScrollview == null) {
+        break missingId;
+      }
+
+      id = R.id.settings_scrollview_content;
+      LinearLayout settingsScrollviewContent = ViewBindings.findChildViewById(rootView, id);
+      if (settingsScrollviewContent == null) {
+        break missingId;
+      }
+
+      id = R.id.timer_setting;
+      LinearLayout timerSetting = ViewBindings.findChildViewById(rootView, id);
+      if (timerSetting == null) {
+        break missingId;
+      }
+
+      id = R.id.timer_spinner;
+      Spinner timerSpinner = ViewBindings.findChildViewById(rootView, id);
+      if (timerSpinner == null) {
+        break missingId;
+      }
+
+      id = R.id.torch_toggle_option;
+      ToggleButton torchToggleOption = ViewBindings.findChildViewById(rootView, id);
+      if (torchToggleOption == null) {
+        break missingId;
+      }
+
+      id = R.id.video_quality_setting;
+      LinearLayout videoQualitySetting = ViewBindings.findChildViewById(rootView, id);
+      if (videoQualitySetting == null) {
+        break missingId;
+      }
+
+      id = R.id.video_quality_spinner;
+      Spinner videoQualitySpinner = ViewBindings.findChildViewById(rootView, id);
+      if (videoQualitySpinner == null) {
+        break missingId;
+      }
+
+      return new SettingsBinding((SettingsFrameLayout) rootView, aspectRatioToggle, background,
+          cmRadioGroup, enableEisSetting, enableEisSwitch, flashToggleOption, focusTimeoutSpinner,
+          gridToggleOption, includeAudioSetting, includeAudioSwitch, latencyRadio, locationToggle,
+          moreSettings, qualityRadio, root, selfIlluminationSetting, selfIlluminationSwitch,
+          settingsDialog, settingsFrame, settingsScrollview, settingsScrollviewContent,
+          timerSetting, timerSpinner, torchToggleOption, videoQualitySetting, videoQualitySpinner);
+    }
+    String missingId = rootView.getResources().getResourceName(id);
+    throw new NullPointerException("Missing required view with ID: ".concat(missingId));
+  }
+}
diff --git a/app/src/aosp/java/app/grapheneos/camera/databinding/VideoPlayerBinding.java b/app/src/aosp/java/app/grapheneos/camera/databinding/VideoPlayerBinding.java
new file mode 100644
index 0000000..106e378
--- /dev/null
+++ b/app/src/aosp/java/app/grapheneos/camera/databinding/VideoPlayerBinding.java
@@ -0,0 +1,68 @@
+// Generated by view binder compiler. Do not edit!
+package app.grapheneos.camera.databinding;
+
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.FrameLayout;
+import android.widget.VideoView;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.viewbinding.ViewBinding;
+import androidx.viewbinding.ViewBindings;
+import app.grapheneos.camera.R;
+import java.lang.NullPointerException;
+import java.lang.Override;
+import java.lang.String;
+
+public final class VideoPlayerBinding implements ViewBinding {
+  @NonNull
+  private final FrameLayout rootView;
+
+  @NonNull
+  public final VideoView videoPlayer;
+
+  private VideoPlayerBinding(@NonNull FrameLayout rootView, @NonNull VideoView videoPlayer) {
+    this.rootView = rootView;
+    this.videoPlayer = videoPlayer;
+  }
+
+  @Override
+  @NonNull
+  public FrameLayout getRoot() {
+    return rootView;
+  }
+
+  @NonNull
+  public static VideoPlayerBinding inflate(@NonNull LayoutInflater inflater) {
+    return inflate(inflater, null, false);
+  }
+
+  @NonNull
+  public static VideoPlayerBinding inflate(@NonNull LayoutInflater inflater,
+      @Nullable ViewGroup parent, boolean attachToParent) {
+    View root = inflater.inflate(R.layout.video_player, parent, false);
+    if (attachToParent) {
+      parent.addView(root);
+    }
+    return bind(root);
+  }
+
+  @NonNull
+  public static VideoPlayerBinding bind(@NonNull View rootView) {
+    // The body of this method is generated in a way you would not otherwise write.
+    // This is done to optimize the compiled bytecode for size and performance.
+    int id;
+    missingId: {
+      id = R.id.video_player;
+      VideoView videoPlayer = ViewBindings.findChildViewById(rootView, id);
+      if (videoPlayer == null) {
+        break missingId;
+      }
+
+      return new VideoPlayerBinding((FrameLayout) rootView, videoPlayer);
+    }
+    String missingId = rootView.getResources().getResourceName(id);
+    throw new NullPointerException("Missing required view with ID: ".concat(missingId));
+  }
+}
diff --git a/app/src/aosp/java/app/grapheneos/camera/databinding/ZoomBarThumbBinding.java b/app/src/aosp/java/app/grapheneos/camera/databinding/ZoomBarThumbBinding.java
new file mode 100644
index 0000000..60ab48e
--- /dev/null
+++ b/app/src/aosp/java/app/grapheneos/camera/databinding/ZoomBarThumbBinding.java
@@ -0,0 +1,68 @@
+// Generated by view binder compiler. Do not edit!
+package app.grapheneos.camera.databinding;
+
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.viewbinding.ViewBinding;
+import androidx.viewbinding.ViewBindings;
+import app.grapheneos.camera.R;
+import java.lang.NullPointerException;
+import java.lang.Override;
+import java.lang.String;
+
+public final class ZoomBarThumbBinding implements ViewBinding {
+  @NonNull
+  private final LinearLayout rootView;
+
+  @NonNull
+  public final TextView progress;
+
+  private ZoomBarThumbBinding(@NonNull LinearLayout rootView, @NonNull TextView progress) {
+    this.rootView = rootView;
+    this.progress = progress;
+  }
+
+  @Override
+  @NonNull
+  public LinearLayout getRoot() {
+    return rootView;
+  }
+
+  @NonNull
+  public static ZoomBarThumbBinding inflate(@NonNull LayoutInflater inflater) {
+    return inflate(inflater, null, false);
+  }
+
+  @NonNull
+  public static ZoomBarThumbBinding inflate(@NonNull LayoutInflater inflater,
+      @Nullable ViewGroup parent, boolean attachToParent) {
+    View root = inflater.inflate(R.layout.zoom_bar_thumb, parent, false);
+    if (attachToParent) {
+      parent.addView(root);
+    }
+    return bind(root);
+  }
+
+  @NonNull
+  public static ZoomBarThumbBinding bind(@NonNull View rootView) {
+    // The body of this method is generated in a way you would not otherwise write.
+    // This is done to optimize the compiled bytecode for size and performance.
+    int id;
+    missingId: {
+      id = R.id.progress;
+      TextView progress = ViewBindings.findChildViewById(rootView, id);
+      if (progress == null) {
+        break missingId;
+      }
+
+      return new ZoomBarThumbBinding((LinearLayout) rootView, progress);
+    }
+    String missingId = rootView.getResources().getResourceName(id);
+    throw new NullPointerException("Missing required view with ID: ".concat(missingId));
+  }
+}
diff --git a/app/src/aosp/res/values/strings.xml b/app/src/aosp/res/values/strings.xml
new file mode 100644
index 0000000..0c5a86c
--- /dev/null
+++ b/app/src/aosp/res/values/strings.xml
@@ -0,0 +1,3 @@
+<resources>
+    <string name="app_name">Camera</string>
+</resources>
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index e6754c3..a69dd11 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -28,6 +28,8 @@
         android:theme="@style/Theme.App"
         tools:ignore="UnusedAttribute">
 
+        <uses-library android:name="androidx.camera.extensions.impl"
+            android:required="false" />
 
         <!-- The main activity of the app (supports all modes) -->
         <activity
diff --git a/app/src/main/java/app/grapheneos/camera/CapturedItems.kt b/app/src/main/java/app/grapheneos/camera/CapturedItems.kt
index 01ace4e..a6f8dc9 100644
--- a/app/src/main/java/app/grapheneos/camera/CapturedItems.kt
+++ b/app/src/main/java/app/grapheneos/camera/CapturedItems.kt
@@ -69,7 +69,7 @@
             override fun createFromParcel(source: Parcel): CapturedItem {
                 val type = source.readByte().toInt()
                 val dateString = source.readString()!!
-                val uri = source.readParcelable<Uri>(null)!!
+                val uri = source.readParcelableCompat<Uri>(null)!!
                 return CapturedItem(type, dateString, uri)
             }
 
diff --git a/app/src/main/java/app/grapheneos/camera/ParcelCompat.kt b/app/src/main/java/app/grapheneos/camera/ParcelCompat.kt
new file mode 100644
index 0000000..771468c
--- /dev/null
+++ b/app/src/main/java/app/grapheneos/camera/ParcelCompat.kt
@@ -0,0 +1,15 @@
+package app.grapheneos.camera
+
+import android.os.Build
+import android.os.Bundle
+import android.os.Parcel
+import android.os.Parcelable
+
+inline internal fun <reified T : Parcelable> Parcel.readParcelableCompat(loader: ClassLoader?): T? {
+    return if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) {
+        @Suppress("DEPRECATION")
+        this.readParcelable<T>(loader)
+    } else {
+        this.readParcelable(loader, T::class.java)
+    }
+}
diff --git a/app/src/main/java/app/grapheneos/camera/ui/SettingsDialog.kt b/app/src/main/java/app/grapheneos/camera/ui/SettingsDialog.kt
index a905823..372de90 100644
--- a/app/src/main/java/app/grapheneos/camera/ui/SettingsDialog.kt
+++ b/app/src/main/java/app/grapheneos/camera/ui/SettingsDialog.kt
@@ -121,7 +121,7 @@
                     ev: MotionEvent?,
                     disallowIntercept: Boolean
                 ): Boolean {
-                    return mActivity.gestureDetectorCompat.onTouchEvent(ev)
+                    return mActivity.gestureDetectorCompat.onTouchEvent(ev!!)
                 }
 
                 override fun onTouchEvent(
diff --git a/app/src/main/java/app/grapheneos/camera/ui/ZoomableImageView.kt b/app/src/main/java/app/grapheneos/camera/ui/ZoomableImageView.kt
index 53638c9..471cadb 100644
--- a/app/src/main/java/app/grapheneos/camera/ui/ZoomableImageView.kt
+++ b/app/src/main/java/app/grapheneos/camera/ui/ZoomableImageView.kt
@@ -172,7 +172,7 @@
             return true
         }
 
-        override fun onScaleEnd(detector: ScaleGestureDetector?) {
+        override fun onScaleEnd(detector: ScaleGestureDetector) {
             super.onScaleEnd(detector)
             if (saveScale == 1f) {
                 gActivity.gallerySlider.isUserInputEnabled = true
diff --git a/app/src/main/java/app/grapheneos/camera/ui/activities/CaptureActivity.kt b/app/src/main/java/app/grapheneos/camera/ui/activities/CaptureActivity.kt
index 1b968bf..397b246 100644
--- a/app/src/main/java/app/grapheneos/camera/ui/activities/CaptureActivity.kt
+++ b/app/src/main/java/app/grapheneos/camera/ui/activities/CaptureActivity.kt
@@ -46,7 +46,7 @@
         confirmButton = findViewById(R.id.confirm_button)
 
         if (intent.extras?.containsKey(EXTRA_OUTPUT) == true) {
-            outputUri = intent.extras?.get(EXTRA_OUTPUT) as Uri
+            outputUri = intent.getParcelableExtraCompat<Uri>(EXTRA_OUTPUT) as Uri
         }
 
         // Disable capture button for a while (to avoid picture capture)
diff --git a/app/src/main/java/app/grapheneos/camera/ui/activities/InAppGallery.kt b/app/src/main/java/app/grapheneos/camera/ui/activities/InAppGallery.kt
index ba6c283..5c85b5c 100644
--- a/app/src/main/java/app/grapheneos/camera/ui/activities/InAppGallery.kt
+++ b/app/src/main/java/app/grapheneos/camera/ui/activities/InAppGallery.kt
@@ -158,7 +158,7 @@
         return true
     }
 
-    override fun onCreateOptionsMenu(menu: Menu?): Boolean {
+    override fun onCreateOptionsMenu(menu: Menu): Boolean {
         menuInflater.inflate(R.menu.gallery, menu)
         return super.onCreateOptionsMenu(menu)
     }
@@ -446,7 +446,7 @@
         gallerySlider.setPageTransformer(GSlideTransformer())
 
         val showVideosOnly = intent.getBooleanExtra(INTENT_KEY_VIDEO_ONLY_MODE, false)
-        val listOfSecureModeCapturedItems = intent.getParcelableArrayListExtra<CapturedItem>(INTENT_KEY_LIST_OF_SECURE_MODE_CAPTURED_ITEMS)
+        val listOfSecureModeCapturedItems = intent.getParcelableArrayListExtraCompat<CapturedItem>(INTENT_KEY_LIST_OF_SECURE_MODE_CAPTURED_ITEMS)
 
         asyncLoaderOfCapturedItems.execute {
             val unprocessedItems: List<CapturedItem> = try {
@@ -479,7 +479,7 @@
             mainExecutor.execute { asyncResultReady(items) }
         }
 
-        val lastCapturedItem = intent.getParcelableExtra<CapturedItem>(INTENT_KEY_LAST_CAPTURED_ITEM)
+        val lastCapturedItem = intent.getParcelableExtraCompat<CapturedItem>(INTENT_KEY_LAST_CAPTURED_ITEM)
 
         if (lastCapturedItem != null) {
             val list = ArrayList<CapturedItem>()
diff --git a/app/src/main/java/app/grapheneos/camera/ui/activities/IntentCompat.kt b/app/src/main/java/app/grapheneos/camera/ui/activities/IntentCompat.kt
new file mode 100644
index 0000000..8ee8742
--- /dev/null
+++ b/app/src/main/java/app/grapheneos/camera/ui/activities/IntentCompat.kt
@@ -0,0 +1,25 @@
+package app.grapheneos.camera.ui.activities
+
+import android.content.Intent
+import android.os.Build
+import android.os.Bundle
+import android.os.Parcel
+import android.os.Parcelable
+
+inline internal fun <reified T : Parcelable> Intent.getParcelableExtraCompat(name: String): T? {
+    return if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) {
+        @Suppress("DEPRECATION")
+        this.getParcelableExtra<T>(name)
+    } else {
+        this.getParcelableExtra(name, T::class.java)
+    }
+}
+
+inline internal fun <reified T : Parcelable> Intent.getParcelableArrayListExtraCompat(name: String): ArrayList<T>? {
+    return if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) {
+        @Suppress("DEPRECATION")
+        this.getParcelableArrayListExtra<T>(name)
+    } else {
+        this.getParcelableArrayListExtra(name, T::class.java)
+    }
+}
diff --git a/app/src/main/java/app/grapheneos/camera/ui/activities/MainActivity.kt b/app/src/main/java/app/grapheneos/camera/ui/activities/MainActivity.kt
index 16798dd..90c07f0 100644
--- a/app/src/main/java/app/grapheneos/camera/ui/activities/MainActivity.kt
+++ b/app/src/main/java/app/grapheneos/camera/ui/activities/MainActivity.kt
@@ -1306,21 +1306,21 @@
         private const val SWIPE_VELOCITY_THRESHOLD = 100
     }
 
-    override fun onDown(p0: MotionEvent?): Boolean {
+    override fun onDown(p0: MotionEvent): Boolean {
         return false
     }
 
-    override fun onShowPress(p0: MotionEvent?) {}
+    override fun onShowPress(p0: MotionEvent) {}
 
-    override fun onSingleTapUp(p0: MotionEvent?): Boolean {
+    override fun onSingleTapUp(p0: MotionEvent): Boolean {
         return false
     }
 
-    override fun onScroll(p0: MotionEvent?, p1: MotionEvent?, p2: Float, p3: Float): Boolean {
+    override fun onScroll(p0: MotionEvent, p1: MotionEvent, p2: Float, p3: Float): Boolean {
         return false
     }
 
-    override fun onLongPress(p0: MotionEvent?) {}
+    override fun onLongPress(p0: MotionEvent) {}
 
     override fun onFling(
         e1: MotionEvent, e2: MotionEvent,
@@ -1410,15 +1410,15 @@
         }
     }
 
-    override fun onSingleTapConfirmed(p0: MotionEvent?): Boolean {
+    override fun onSingleTapConfirmed(p0: MotionEvent): Boolean {
         return false
     }
 
-    override fun onDoubleTap(p0: MotionEvent?): Boolean {
+    override fun onDoubleTap(p0: MotionEvent): Boolean {
         return false
     }
 
-    override fun onDoubleTapEvent(p0: MotionEvent?): Boolean {
+    override fun onDoubleTapEvent(p0: MotionEvent): Boolean {
         return false
     }
 
diff --git a/app/src/main/java/app/grapheneos/camera/ui/activities/VideoPlayer.kt b/app/src/main/java/app/grapheneos/camera/ui/activities/VideoPlayer.kt
index 054a35d..c8546de 100644
--- a/app/src/main/java/app/grapheneos/camera/ui/activities/VideoPlayer.kt
+++ b/app/src/main/java/app/grapheneos/camera/ui/activities/VideoPlayer.kt
@@ -31,7 +31,7 @@
             throw Exception("Video Player requires videoUri")
         }
 
-        val uri = intent.extras!!.get("videoUri") as Uri
+        val uri = intent.getParcelableExtraCompat<Uri>("videoUri") as Uri
 
         val videoView = binding.videoPlayer
 
diff --git a/app/src/main/res/mipmap-anydpi/ic_launcher_round.xml b/app/src/main/res/mipmap-anydpi/ic_launcher_round.xml
index 7353dbd..515b77e 100644
--- a/app/src/main/res/mipmap-anydpi/ic_launcher_round.xml
+++ b/app/src/main/res/mipmap-anydpi/ic_launcher_round.xml
@@ -2,4 +2,5 @@
 <adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
     <background android:drawable="@color/ic_launcher_background"/>
     <foreground android:drawable="@drawable/ic_launcher_foreground"/>
-</adaptive-icon>
\ No newline at end of file
+    <monochrome android:drawable="@drawable/ic_launcher_foreground" />
+</adaptive-icon>
diff --git a/libs/Android.bp b/libs/Android.bp
new file mode 100644
index 0000000..dc2afb4
--- /dev/null
+++ b/libs/Android.bp
@@ -0,0 +1,93 @@
+/*
+ * Copyright (c) 2022 The LeafOS Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+android_library_import {
+    name: "androidx.camera_camera-camera2",
+    aars: ["androidx.camera_camera-camera2-*.aar"],
+    sdk_version: "current",
+}
+
+cc_prebuilt_library_shared {
+    name: "libimage_processing_util_jni",
+    sdk_version: "current",
+    system_ext_specific: true,
+    strip: {
+        none: true,
+    },
+    check_elf_files: false,
+    target: {
+        android_arm: {
+            srcs: ["androidx.camera_camera-core/jni/armeabi-v7a/libimage_processing_util_jni.so"],
+        },
+        android_arm64: {
+            srcs: ["androidx.camera_camera-core/jni/arm64-v8a/libimage_processing_util_jni.so"],
+        },
+        android_x86: {
+            srcs: ["androidx.camera_camera-core/jni/x86/libimage_processing_util_jni.so"],
+        },
+        android_x86_64: {
+            srcs: ["androidx.camera_camera-core/jni/x86_64/libimage_processing_util_jni.so"],
+        },
+    }
+}
+
+android_library_import {
+    name: "androidx.camera_camera-core",
+    aars: ["androidx.camera_camera-core-*.aar"],
+    sdk_version: "current",
+}
+
+android_library_import {
+    name: "androidx.camera_camera-extensions",
+    aars: ["androidx.camera_camera-extensions-*.aar"],
+    sdk_version: "current",
+}
+
+android_library_import {
+    name: "androidx.camera_camera-lifecycle",
+    aars: ["androidx.camera_camera-lifecycle-*.aar"],
+    sdk_version: "current",
+}
+
+android_library_import {
+    name: "androidx.camera_camera-video",
+    aars: ["androidx.camera_camera-video-*.aar"],
+    sdk_version: "current",
+}
+
+android_library_import {
+    name: "androidx.camera_camera-view",
+    aars: ["androidx.camera_camera-view-*.aar"],
+    sdk_version: "current",
+}
+
+java_import {
+    name: "androidx.concurrent_concurrent-futures",
+    jars: ["androidx.concurrent_concurrent-futures-*.jar"],
+    sdk_version: "current",
+}
+
+android_library_import {
+    name: "androidx.databinding_viewbinding",
+    aars: ["androidx.databinding_viewbinding-*.aar"],
+    sdk_version: "current",
+}
+
+java_import {
+    name: "com.google.zxing_core",
+    jars: ["com.google.zxing_core-*.jar"],
+    sdk_version: "current",
+}
diff --git a/libs/androidx.camera_camera-camera2-1.1.0-rc01.aar b/libs/androidx.camera_camera-camera2-1.1.0-rc01.aar
new file mode 100644
index 0000000..76a4c4d
--- /dev/null
+++ b/libs/androidx.camera_camera-camera2-1.1.0-rc01.aar
Binary files differ
diff --git a/libs/androidx.camera_camera-core-1.1.0-rc01.aar b/libs/androidx.camera_camera-core-1.1.0-rc01.aar
new file mode 100644
index 0000000..4a379a8
--- /dev/null
+++ b/libs/androidx.camera_camera-core-1.1.0-rc01.aar
Binary files differ
diff --git a/libs/androidx.camera_camera-core/jni/arm64-v8a/libimage_processing_util_jni.so b/libs/androidx.camera_camera-core/jni/arm64-v8a/libimage_processing_util_jni.so
new file mode 100644
index 0000000..ef05caf
--- /dev/null
+++ b/libs/androidx.camera_camera-core/jni/arm64-v8a/libimage_processing_util_jni.so
Binary files differ
diff --git a/libs/androidx.camera_camera-core/jni/armeabi-v7a/libimage_processing_util_jni.so b/libs/androidx.camera_camera-core/jni/armeabi-v7a/libimage_processing_util_jni.so
new file mode 100644
index 0000000..bb84dd8
--- /dev/null
+++ b/libs/androidx.camera_camera-core/jni/armeabi-v7a/libimage_processing_util_jni.so
Binary files differ
diff --git a/libs/androidx.camera_camera-core/jni/x86/libimage_processing_util_jni.so b/libs/androidx.camera_camera-core/jni/x86/libimage_processing_util_jni.so
new file mode 100644
index 0000000..be4b5b9
--- /dev/null
+++ b/libs/androidx.camera_camera-core/jni/x86/libimage_processing_util_jni.so
Binary files differ
diff --git a/libs/androidx.camera_camera-core/jni/x86_64/libimage_processing_util_jni.so b/libs/androidx.camera_camera-core/jni/x86_64/libimage_processing_util_jni.so
new file mode 100644
index 0000000..8d7303e
--- /dev/null
+++ b/libs/androidx.camera_camera-core/jni/x86_64/libimage_processing_util_jni.so
Binary files differ
diff --git a/libs/androidx.camera_camera-extensions-1.1.0-rc01.aar b/libs/androidx.camera_camera-extensions-1.1.0-rc01.aar
new file mode 100644
index 0000000..92eb71c
--- /dev/null
+++ b/libs/androidx.camera_camera-extensions-1.1.0-rc01.aar
Binary files differ
diff --git a/libs/androidx.camera_camera-lifecycle-1.1.0-rc01.aar b/libs/androidx.camera_camera-lifecycle-1.1.0-rc01.aar
new file mode 100644
index 0000000..c619f90
--- /dev/null
+++ b/libs/androidx.camera_camera-lifecycle-1.1.0-rc01.aar
Binary files differ
diff --git a/libs/androidx.camera_camera-video-1.1.0-rc01.aar b/libs/androidx.camera_camera-video-1.1.0-rc01.aar
new file mode 100644
index 0000000..188d62c
--- /dev/null
+++ b/libs/androidx.camera_camera-video-1.1.0-rc01.aar
Binary files differ
diff --git a/libs/androidx.camera_camera-view-1.1.0-rc01.aar b/libs/androidx.camera_camera-view-1.1.0-rc01.aar
new file mode 100644
index 0000000..82baacb
--- /dev/null
+++ b/libs/androidx.camera_camera-view-1.1.0-rc01.aar
Binary files differ
diff --git a/libs/androidx.concurrent_concurrent-futures-1.1.0.jar b/libs/androidx.concurrent_concurrent-futures-1.1.0.jar
new file mode 100644
index 0000000..833b095
--- /dev/null
+++ b/libs/androidx.concurrent_concurrent-futures-1.1.0.jar
Binary files differ
diff --git a/libs/androidx.databinding_viewbinding-7.4.0-alpha02.aar b/libs/androidx.databinding_viewbinding-7.4.0-alpha02.aar
new file mode 100644
index 0000000..be3cc17
--- /dev/null
+++ b/libs/androidx.databinding_viewbinding-7.4.0-alpha02.aar
Binary files differ
diff --git a/libs/com.google.zxing_core-3.5.0.jar b/libs/com.google.zxing_core-3.5.0.jar
new file mode 100644
index 0000000..7bba61d
--- /dev/null
+++ b/libs/com.google.zxing_core-3.5.0.jar
Binary files differ