Skip to content

Commit

Permalink
New fragment for handling camera permission
Browse files Browse the repository at this point in the history
  • Loading branch information
DavidGillsjo committed Sep 29, 2020
1 parent 64d89bf commit 671d7f0
Show file tree
Hide file tree
Showing 11 changed files with 179 additions and 93 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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.
* <p>
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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
Expand All @@ -166,27 +168,93 @@ 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();
}

@Override
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);
}
Expand Down Expand Up @@ -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;
}
}

Expand All @@ -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();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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();
}
});
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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);
}

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -1,32 +1,17 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/camera_capture"
android:layout_height="wrap_content"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1"
tools:context="se.lth.math.videoimucapture.CameraCaptureFragment">

<com.google.android.material.appbar.AppBarLayout
android:id="@+id/appBarLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintTop_toTopOf="parent">

<com.google.android.material.appbar.MaterialToolbar
android:id="@+id/topAppBar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
app:menu="@menu/camera"
app:title="@string/camera_page_title" />

</com.google.android.material.appbar.AppBarLayout>

<se.lth.math.videoimucapture.AspectFrameLayout
android:id="@+id/cameraPreview_afl"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_constraintTop_toBottomOf="@+id/appBarLayout">
android:layout_height="match_parent">

<se.lth.math.videoimucapture.SampleGLView
android:id="@+id/cameraPreview_surfaceView"
Expand All @@ -43,9 +28,9 @@
android:layout_marginStart="16dp"
android:layout_marginTop="16dp"
android:text="[?]"
android:textAppearance="?android:attr/textAppearanceMedium"
android:textAppearance="@style/TextAppearance.MyApp.Overlay"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/appBarLayout" />
app:layout_constraintTop_toTopOf="parent" />

<com.google.android.material.textview.MaterialTextView
android:id="@+id/captureResult_text"
Expand All @@ -54,6 +39,7 @@
android:layout_marginStart="16dp"
android:layout_marginTop="16dp"
android:text="focal length : exposure time"
android:textAppearance="@style/TextAppearance.MyApp.Overlay"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/cameraParams_text" />

Expand All @@ -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" />

<com.google.android.material.floatingactionbutton.FloatingActionButton
Expand Down
25 changes: 25 additions & 0 deletions android_app/app/src/main/res/layout/capture_toolbar_fragment.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/main_fragment"
android:layout_width="match_parent"
android:layout_height="wrap_content"
tools:context="se.lth.math.videoimucapture.CameraCaptureFragment">

<com.google.android.material.appbar.AppBarLayout
android:id="@+id/appBarLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintTop_toTopOf="parent">

<com.google.android.material.appbar.MaterialToolbar
android:id="@+id/topAppBar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
app:menu="@menu/camera"
app:title="@string/camera_page_title" />

</com.google.android.material.appbar.AppBarLayout>

</androidx.constraintlayout.widget.ConstraintLayout>
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
<FrameLayout
<LinearLayout
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/main_content"
android:layout_height="match_parent"
android:layout_width="match_parent" />
android:layout_width="match_parent"
android:orientation="vertical" />
Loading

0 comments on commit 671d7f0

Please sign in to comment.