From 671d7f00128a868c3f5d5981b7ee34f135eaf296 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Gillsj=C3=B6?= Date: Tue, 29 Sep 2020 13:15:55 +0200 Subject: [PATCH] New fragment for handling camera permission --- .../CameraCaptureActivity.java | 109 +++++++++++++----- .../CameraCaptureFragment.java | 2 +- .../math/videoimucapture/CaptureSettings.java | 2 +- .../videoimucapture/PermissionHelper.java | 43 +------ ...amera_capture.xml => capture_fragment.xml} | 32 ++--- .../res/layout/capture_toolbar_fragment.xml | 25 ++++ .../{activity_main.xml => main_activity.xml} | 5 +- .../main/res/layout/permission_fragment.xml | 45 ++++++++ ...ngs_activity.xml => settings_fragment.xml} | 0 .../app/src/main/res/values/strings.xml | 3 + .../app/src/main/res/values/styles.xml | 6 +- 11 files changed, 179 insertions(+), 93 deletions(-) rename android_app/app/src/main/res/layout/{activity_camera_capture.xml => capture_fragment.xml} (71%) create mode 100644 android_app/app/src/main/res/layout/capture_toolbar_fragment.xml rename android_app/app/src/main/res/layout/{activity_main.xml => main_activity.xml} (68%) create mode 100644 android_app/app/src/main/res/layout/permission_fragment.xml rename android_app/app/src/main/res/layout/{settings_activity.xml => settings_fragment.xml} (100%) diff --git a/android_app/app/src/main/java/se/lth/math/videoimucapture/CameraCaptureActivity.java b/android_app/app/src/main/java/se/lth/math/videoimucapture/CameraCaptureActivity.java index edcf4d1..b023c0a 100644 --- a/android_app/app/src/main/java/se/lth/math/videoimucapture/CameraCaptureActivity.java +++ b/android_app/app/src/main/java/se/lth/math/videoimucapture/CameraCaptureActivity.java @@ -22,21 +22,20 @@ import android.os.Bundle; import android.os.Handler; import android.os.Message; -import androidx.annotation.NonNull; import androidx.appcompat.app.AppCompatActivity; import android.util.Log; import android.util.Size; +import android.view.LayoutInflater; import android.view.MenuItem; -import android.widget.Toast; +import android.view.View; +import android.view.ViewGroup; +import com.google.android.material.appbar.MaterialToolbar; import com.google.firebase.analytics.FirebaseAnalytics; -import java.io.File; import java.lang.ref.WeakReference; -import static androidx.core.content.FileProvider.getUriForFile; - /** * Shows the camera preview on screen while simultaneously recording it to a .mp4 file. *

@@ -113,11 +112,14 @@ */ public class CameraCaptureActivity extends AppCompatActivity { public static final String TAG = "VIMUC-Main"; + public static final String FRAGMENT_TOOLBAR_TAG = "CaptureToolbar"; + public static final String FRAGMENT_MAIN_TAG = "CaptureWindow"; private static final boolean VERBOSE = false; private Camera2Proxy mCamera2Proxy = null; private CameraHandler mCameraHandler; - private CameraCaptureFragment mCameraCaptureFragment; + private CameraCaptureFragment mCameraCaptureFragment = null; + private PermissionRationaleFragment mPermissionFragment = null; private CameraSettingsManager mCameraSettingsManager; private FirebaseAnalytics mFirebaseAnalytics; @@ -153,7 +155,7 @@ public Camera2Proxy getmCamera2Proxy() { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - setContentView(R.layout.activity_main); + setContentView(R.layout.main_activity); // Define a handler that receives camera-control messages from other threads. All calls // to Camera must be made on the same thread. Note we create this before the renderer @@ -166,20 +168,64 @@ protected void onCreate(Bundle savedInstanceState) { // Obtain the FirebaseAnalytics instance. mFirebaseAnalytics = FirebaseAnalytics.getInstance(this); - if ((savedInstanceState == null) && PermissionHelper.hasCameraPermission(this)) { - createCameraCaptureFragment(); - } else { - PermissionHelper.requestCameraPermission(this, false); + if (savedInstanceState == null) { + ToolBarFragment fragment = new ToolBarFragment(); + getSupportFragmentManager() + .beginTransaction() + .add(R.id.main_content, fragment, FRAGMENT_TOOLBAR_TAG) + .commit(); + if (PermissionHelper.hasCameraPermission(this)) { + createCameraCaptureFragment(); + } } - Log.d(TAG, "onCreate complete: " + this); } + public static class ToolBarFragment extends Fragment { + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + return inflater.inflate(R.layout.capture_toolbar_fragment, container, false); + } + } + + public static class PermissionRationaleFragment extends Fragment { + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + View rootView = inflater.inflate(R.layout.permission_fragment, container, false); + + rootView.findViewById(R.id.grant_permission_button).setOnClickListener(this::onGrantClick); + rootView.findViewById(R.id.exit_permission_button).setOnClickListener(this::onExitClick); + return rootView; + } + + private void onGrantClick(View unused) { + PermissionHelper.requestCameraPermission(getActivity()); + } + + private void onExitClick(View unused) { + getActivity().finish(); + } + } + private void createCameraCaptureFragment() { + MaterialToolbar bar = (MaterialToolbar) findViewById(R.id.topAppBar); + bar.getMenu().findItem(R.id.settings).setEnabled(true); CameraCaptureFragment fragment = new CameraCaptureFragment(); getSupportFragmentManager() .beginTransaction() - .add(R.id.main_content, fragment) + .add(R.id.main_content, fragment, FRAGMENT_MAIN_TAG) + .commit(); + } + + private void createPermissionFragment() { + MaterialToolbar bar = (MaterialToolbar) findViewById(R.id.topAppBar); + bar.getMenu().findItem(R.id.settings).setEnabled(false); + PermissionRationaleFragment fragment = new PermissionRationaleFragment(); + getSupportFragmentManager() + .beginTransaction() + .add(R.id.main_content, fragment, FRAGMENT_MAIN_TAG) .commit(); } @@ -187,6 +233,28 @@ private void createCameraCaptureFragment() { protected void onResume() { super.onResume(); + /*This block is only used if we did not have camera permission in onCreate + If we don't have permission and haven't asked the user before, simply ask. + If we asked previously but got denied, show permission fragment with information. + If user accepts, the onResume will be called again (app pause during prompt) + and we need to create the capture fragment. + */ + if (!PermissionHelper.hasCameraPermission(this)) { + // We don't have permission to use camera + if (PermissionHelper.mustShowRationale(this)) { + createPermissionFragment(); + } + else { + PermissionHelper.requestCameraPermission(this); + } + } else if (mCameraCaptureFragment == null) { + // We now have permission, but have no capture fragment yet. + if (mPermissionFragment != null) { + getSupportFragmentManager().beginTransaction().remove(mPermissionFragment).commit(); + } + createCameraCaptureFragment(); + } + mImuManager.register(); Log.d(TAG, "onResume complete: " + this); } @@ -230,6 +298,8 @@ protected void onDestroy() { public void onAttachFragment(Fragment fragment) { if (fragment instanceof CameraCaptureFragment) { mCameraCaptureFragment = (CameraCaptureFragment) fragment; + } else if (fragment instanceof PermissionRationaleFragment) { + mPermissionFragment = (PermissionRationaleFragment) fragment; } } @@ -255,19 +325,6 @@ public void displayInfo(MenuItem unused) { newFragment.show(getSupportFragmentManager(), "info"); } - @Override - public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { - super.onRequestPermissionsResult(requestCode, permissions, grantResults); - if (!PermissionHelper.hasCameraPermission(this)) { - Toast.makeText(this, - "Camera permission is needed to run this application", Toast.LENGTH_LONG).show(); - PermissionHelper.launchPermissionSettings(this); - finish(); - } else { - // Now have permission, start fragment - createCameraCaptureFragment(); - } - } public String getResultRoot() { return getExternalFilesDir(null).getAbsolutePath(); diff --git a/android_app/app/src/main/java/se/lth/math/videoimucapture/CameraCaptureFragment.java b/android_app/app/src/main/java/se/lth/math/videoimucapture/CameraCaptureFragment.java index 4cc638d..6539ef4 100644 --- a/android_app/app/src/main/java/se/lth/math/videoimucapture/CameraCaptureFragment.java +++ b/android_app/app/src/main/java/se/lth/math/videoimucapture/CameraCaptureFragment.java @@ -102,7 +102,7 @@ public View onCreateView(@NonNull LayoutInflater inflater, @Nullable Bundle savedInstanceState) { Log.d(TAG, "onCreateView: " + this); View rootView = inflater.inflate( - R.layout.activity_camera_capture, + R.layout.capture_fragment, container, false); diff --git a/android_app/app/src/main/java/se/lth/math/videoimucapture/CaptureSettings.java b/android_app/app/src/main/java/se/lth/math/videoimucapture/CaptureSettings.java index fa77b31..0f4cdb8 100644 --- a/android_app/app/src/main/java/se/lth/math/videoimucapture/CaptureSettings.java +++ b/android_app/app/src/main/java/se/lth/math/videoimucapture/CaptureSettings.java @@ -24,7 +24,7 @@ public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceStat @Override public void onClick(View v) { // back button pressed - getFragmentManager().popBackStackImmediate(); + getActivity().getSupportFragmentManager().popBackStackImmediate(); } }); } diff --git a/android_app/app/src/main/java/se/lth/math/videoimucapture/PermissionHelper.java b/android_app/app/src/main/java/se/lth/math/videoimucapture/PermissionHelper.java index a87d249..7a35f1d 100644 --- a/android_app/app/src/main/java/se/lth/math/videoimucapture/PermissionHelper.java +++ b/android_app/app/src/main/java/se/lth/math/videoimucapture/PermissionHelper.java @@ -25,7 +25,6 @@ import android.provider.Settings; import androidx.core.app.ActivityCompat; import androidx.core.content.ContextCompat; -import android.widget.Toast; /** * Helper class for handling dangerous permissions for Android API level >= 23 which @@ -39,45 +38,13 @@ public static boolean hasCameraPermission(Activity activity) { Manifest.permission.CAMERA) == PackageManager.PERMISSION_GRANTED; } - public static boolean hasWriteStoragePermission(Activity activity) { - return ContextCompat.checkSelfPermission(activity, - Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED; + public static boolean mustShowRationale(Activity activity) { + return ActivityCompat.shouldShowRequestPermissionRationale(activity, Manifest.permission.CAMERA); } - public static void requestCameraPermission(Activity activity, boolean requestWritePermission) { - - boolean showRationale = ActivityCompat.shouldShowRequestPermissionRationale(activity, - Manifest.permission.CAMERA) || (requestWritePermission && - ActivityCompat.shouldShowRequestPermissionRationale(activity, - Manifest.permission.WRITE_EXTERNAL_STORAGE)); - if (showRationale) { - Toast.makeText(activity, - "Camera permission is needed to run this application", Toast.LENGTH_LONG).show(); - } else { - - // No explanation needed, we can request the permission. - - String permissions[] = requestWritePermission ? new String[]{Manifest.permission.CAMERA, - Manifest.permission.WRITE_EXTERNAL_STORAGE} : new String[]{Manifest.permission.CAMERA}; - ActivityCompat.requestPermissions(activity, permissions, RC_PERMISSION_REQUEST); - } - } - - public static void requestWriteStoragePermission(Activity activity) { - boolean showRationale = ActivityCompat.shouldShowRequestPermissionRationale(activity, - Manifest.permission.WRITE_EXTERNAL_STORAGE); - if (showRationale) { - Toast.makeText(activity, - "Writing to external storage permission is needed to run this application", - Toast.LENGTH_LONG).show(); - } else { - - // No explanation needed, we can request the permission. - - String permissions[] = new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}; - - ActivityCompat.requestPermissions(activity, permissions, RC_PERMISSION_REQUEST); - } + public static void requestCameraPermission(Activity activity) { + String permissions[] = new String[]{Manifest.permission.CAMERA}; + ActivityCompat.requestPermissions(activity, permissions, RC_PERMISSION_REQUEST); } /** diff --git a/android_app/app/src/main/res/layout/activity_camera_capture.xml b/android_app/app/src/main/res/layout/capture_fragment.xml similarity index 71% rename from android_app/app/src/main/res/layout/activity_camera_capture.xml rename to android_app/app/src/main/res/layout/capture_fragment.xml index 659285f..42b9056 100644 --- a/android_app/app/src/main/res/layout/activity_camera_capture.xml +++ b/android_app/app/src/main/res/layout/capture_fragment.xml @@ -1,32 +1,17 @@ - - - - - - - + android:layout_height="match_parent"> + app:layout_constraintTop_toTopOf="parent" /> @@ -69,7 +55,7 @@ app:backgroundTint="@null" app:fabSize="mini" app:layout_constraintEnd_toEndOf="parent" - app:layout_constraintTop_toBottomOf="@+id/appBarLayout" + app:layout_constraintTop_toTopOf="parent" app:tint="@null" /> + + + + + + + + + \ No newline at end of file diff --git a/android_app/app/src/main/res/layout/activity_main.xml b/android_app/app/src/main/res/layout/main_activity.xml similarity index 68% rename from android_app/app/src/main/res/layout/activity_main.xml rename to android_app/app/src/main/res/layout/main_activity.xml index d23e809..5df54b4 100644 --- a/android_app/app/src/main/res/layout/activity_main.xml +++ b/android_app/app/src/main/res/layout/main_activity.xml @@ -1,6 +1,7 @@ - \ No newline at end of file + android:layout_width="match_parent" + android:orientation="vertical" /> \ No newline at end of file diff --git a/android_app/app/src/main/res/layout/permission_fragment.xml b/android_app/app/src/main/res/layout/permission_fragment.xml new file mode 100644 index 0000000..c4b6bce --- /dev/null +++ b/android_app/app/src/main/res/layout/permission_fragment.xml @@ -0,0 +1,45 @@ + + + + + + + + + + \ No newline at end of file diff --git a/android_app/app/src/main/res/layout/settings_activity.xml b/android_app/app/src/main/res/layout/settings_fragment.xml similarity index 100% rename from android_app/app/src/main/res/layout/settings_activity.xml rename to android_app/app/src/main/res/layout/settings_fragment.xml diff --git a/android_app/app/src/main/res/values/strings.xml b/android_app/app/src/main/res/values/strings.xml index 5925c89..b644800 100644 --- a/android_app/app/src/main/res/values/strings.xml +++ b/android_app/app/src/main/res/values/strings.xml @@ -19,4 +19,7 @@ Optical Image Stabilization (OIS) is enabled, you need to compensate for this with the OIS sample data during 3D reconstruction. Optical Image Stabilization (OIS) is enabled but OIS data is not stored, which means that the saved camera parameters are not valid. Please enable OIS data in settings. Digital Video Stabilization is enabled, the stored camera parameters will not be correct. + This app requires permission to use the camera.\n\nIt will be useless otherwise. + Grant Permission + Keep it useless \ No newline at end of file diff --git a/android_app/app/src/main/res/values/styles.xml b/android_app/app/src/main/res/values/styles.xml index 7fa8381..aee2f56 100644 --- a/android_app/app/src/main/res/values/styles.xml +++ b/android_app/app/src/main/res/values/styles.xml @@ -4,13 +4,15 @@ @color/colorPrimaryVariant @color/colorSecondary @color/colorSecondaryVariant--> - @style/Theme.MyApp.PreferenceThemeOverlay + + \ No newline at end of file