diff --git a/briar-android/src/main/java/org/briarproject/briar/android/activity/RequestCodes.java b/briar-android/src/main/java/org/briarproject/briar/android/activity/RequestCodes.java index 15f6211070af9e2eee9022789ebc185642d6e1ae..131b2921b8b139be92b3535d478debb6db742f36 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/activity/RequestCodes.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/activity/RequestCodes.java @@ -10,5 +10,6 @@ public interface RequestCodes { int REQUEST_WRITE_BLOG_POST = 6; int REQUEST_SHARE_BLOG = 7; int REQUEST_RINGTONE = 8; + int REQUEST_PERMISSION_CAMERA = 9; } diff --git a/briar-android/src/main/java/org/briarproject/briar/android/keyagreement/KeyAgreementActivity.java b/briar-android/src/main/java/org/briarproject/briar/android/keyagreement/KeyAgreementActivity.java index b66577b67c170419bb618cd22364e597f1a0a6d3..48e09c3681bf7316677be5599fcb41094797973b 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/keyagreement/KeyAgreementActivity.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/keyagreement/KeyAgreementActivity.java @@ -1,6 +1,15 @@ package org.briarproject.briar.android.keyagreement; +import android.Manifest; +import android.Manifest.permission; +import android.content.DialogInterface; +import android.content.DialogInterface.OnClickListener; +import android.content.pm.PackageManager; import android.os.Bundle; +import android.support.annotation.UiThread; +import android.support.v4.app.ActivityCompat; +import android.support.v4.content.ContextCompat; +import android.support.v7.app.AlertDialog.Builder; import android.support.v7.widget.Toolbar; import android.view.MenuItem; import android.widget.Toast; @@ -19,19 +28,24 @@ import org.briarproject.bramble.api.keyagreement.event.KeyAgreementFinishedEvent import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault; import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault; import org.briarproject.briar.R; +import org.briarproject.briar.R.string; +import org.briarproject.briar.R.style; import org.briarproject.briar.android.activity.ActivityComponent; import org.briarproject.briar.android.activity.BriarActivity; import org.briarproject.briar.android.fragment.BaseFragment; import org.briarproject.briar.android.fragment.BaseFragment.BaseFragmentListener; import org.briarproject.briar.android.keyagreement.IntroFragment.IntroScreenSeenListener; +import org.briarproject.briar.android.util.UiUtils; import java.util.logging.Logger; import javax.annotation.Nullable; import javax.inject.Inject; +import static android.support.v4.content.PermissionChecker.PERMISSION_GRANTED; import static android.widget.Toast.LENGTH_LONG; import static java.util.logging.Level.WARNING; +import static org.briarproject.briar.android.activity.RequestCodes.REQUEST_PERMISSION_CAMERA; @MethodsNotNullByDefault @ParametersNotNullByDefault @@ -51,6 +65,8 @@ public class KeyAgreementActivity extends BriarActivity implements @Inject volatile IdentityManager identityManager; + private boolean continueClicked, gotCameraPermission; + @Override public void injectActivity(ActivityComponent component) { component.inject(this); @@ -96,10 +112,26 @@ public class KeyAgreementActivity extends BriarActivity implements } } + @Override + protected void onPostResume() { + super.onPostResume(); + //Workaround for https://code.google.com/p/android/issues/detail?id=190966 + if (continueClicked && gotCameraPermission) { + showQrCodeFragment(); + } + } + @Override public void showNextScreen() { // FIXME #824 // showNextFragment(ShowQrCodeFragment.newInstance()); + continueClicked = true; + if (checkPermissions()) { + showQrCodeFragment(); + } + } + + private void showQrCodeFragment() { BaseFragment f = ShowQrCodeFragment.newInstance(); getSupportFragmentManager().beginTransaction() .replace(R.id.fragmentContainer, f, f.getUniqueTag()) @@ -107,6 +139,76 @@ public class KeyAgreementActivity extends BriarActivity implements .commit(); } + private boolean checkPermissions() { + if (ContextCompat.checkSelfPermission(this, permission.CAMERA) != + PackageManager.PERMISSION_GRANTED) { + // Should we show an explanation? + if (ActivityCompat.shouldShowRequestPermissionRationale(this, + permission.CAMERA)) { + OnClickListener proceedListener = new OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + requestPermission(); + } + }; + Builder builder = new Builder(this, style.BriarDialogTheme); + builder.setTitle(string.permission_camera_title); + builder.setMessage(string.permission_camera_request_text); + builder.setNeutralButton(string.continue_button, + proceedListener); + builder.show(); + } else { + requestPermission(); + } + return false; + } else { + return true; + } + } + + private void requestPermission() { + ActivityCompat.requestPermissions(this, + new String[] {permission.CAMERA}, + REQUEST_PERMISSION_CAMERA); + } + + @Override + @UiThread + public void onRequestPermissionsResult(int requestCode, + String permissions[], int[] grantResults) { + if (requestCode == REQUEST_PERMISSION_CAMERA) { + // If request is cancelled, the result arrays are empty. + if (grantResults.length > 0 && + grantResults[0] == PERMISSION_GRANTED) { + gotCameraPermission = true; + } else { + if (!ActivityCompat.shouldShowRequestPermissionRationale(this, + permission.CAMERA)) { + Builder builder = new Builder(this, style.BriarDialogTheme); + builder.setTitle(string.permission_camera_title); + builder.setMessage(string.permission_camera_perm_denied); + builder.setPositiveButton(string.open_settings, + UiUtils.getGoToSettingsListener( + this)); + builder.setNegativeButton(string.cancel, + new OnClickListener() { + @Override + public void onClick( + DialogInterface dialog, + int which) { + supportFinishAfterTransition(); + } + }); + builder.show(); + } else { + Toast.makeText(this, string.permission_camera_denied_toast, + LENGTH_LONG).show(); + supportFinishAfterTransition(); + } + } + } + } + @Override public void eventOccurred(Event e) { if (e instanceof KeyAgreementFinishedEvent) { @@ -189,5 +291,4 @@ public class KeyAgreementActivity extends BriarActivity implements } }); } - } diff --git a/briar-android/src/main/java/org/briarproject/briar/android/keyagreement/ShowQrCodeFragment.java b/briar-android/src/main/java/org/briarproject/briar/android/keyagreement/ShowQrCodeFragment.java index 7a57a601b55f5efee400587dd605ea5485e9a1fe..93dd2914466f6f05e843c902f26e55eaf3aa48c1 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/keyagreement/ShowQrCodeFragment.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/keyagreement/ShowQrCodeFragment.java @@ -114,6 +114,7 @@ public class ShowQrCodeFragment extends BaseEventFragment public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { + return inflater.inflate(R.layout.fragment_keyagreement_qr, container, false); } @@ -143,13 +144,11 @@ public class ShowQrCodeFragment extends BaseEventFragment @Override public void onStart() { super.onStart(); - try { cameraView.start(); } catch (CameraException e) { logCameraExceptionAndFinish(e); } - // Listen for changes to the Bluetooth state IntentFilter filter = new IntentFilter(); filter.addAction(ACTION_STATE_CHANGED); @@ -157,7 +156,8 @@ public class ShowQrCodeFragment extends BaseEventFragment getActivity().registerReceiver(receiver, filter); // Enable BT adapter if it is not already on. - final BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); + final BluetoothAdapter adapter = + BluetoothAdapter.getDefaultAdapter(); if (adapter != null && !adapter.isEnabled()) { waitingForBluetooth = true; androidExecutor.runOnBackgroundThread(new Runnable() { diff --git a/briar-android/src/main/java/org/briarproject/briar/android/util/UiUtils.java b/briar-android/src/main/java/org/briarproject/briar/android/util/UiUtils.java index 4543867fa94c69dcee899a6714a18d52d3dd5377..74f182cbcbb3ca6ac9b636517c754e66fbc056ed 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/util/UiUtils.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/util/UiUtils.java @@ -1,6 +1,11 @@ package org.briarproject.briar.android.util; import android.content.Context; + +import android.content.DialogInterface; +import android.content.DialogInterface.OnClickListener; +import android.content.Intent; +import android.net.Uri; import android.support.design.widget.TextInputLayout; import android.support.v4.app.FragmentManager; import android.support.v4.content.ContextCompat; @@ -18,12 +23,14 @@ import android.view.View; import android.widget.TextView; import org.briarproject.bramble.api.contact.ContactId; +import org.briarproject.briar.BuildConfig; import org.briarproject.briar.R; import org.briarproject.briar.android.view.ArticleMovementMethod; import org.briarproject.briar.android.widget.LinkDialogFragment; import javax.annotation.Nullable; +import static android.content.Intent.*; import static android.text.format.DateUtils.DAY_IN_MILLIS; import static android.text.format.DateUtils.FORMAT_ABBREV_MONTH; import static android.text.format.DateUtils.FORMAT_ABBREV_RELATIVE; @@ -31,6 +38,7 @@ import static android.text.format.DateUtils.FORMAT_ABBREV_TIME; import static android.text.format.DateUtils.FORMAT_SHOW_DATE; import static android.text.format.DateUtils.MINUTE_IN_MILLIS; import static android.text.format.DateUtils.WEEK_IN_MILLIS; +import static org.briarproject.briar.BuildConfig.*; import static org.briarproject.briar.android.BriarApplication.EXPIRY_DATE; public class UiUtils { @@ -126,5 +134,19 @@ public class UiUtils { public static String getBulbTransitionName(ContactId c) { return "bulb" + c.getInt(); } - + + public static OnClickListener getGoToSettingsListener( + final Context context) { + return new OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + Intent i = new Intent(); + i.setAction("android.settings.APPLICATION_DETAILS_SETTINGS"); + i.addCategory(CATEGORY_DEFAULT); + i.setData(Uri.parse("package:" + APPLICATION_ID)); + i.addFlags(FLAG_ACTIVITY_NEW_TASK); + context.startActivity(i); + } + }; + } } diff --git a/briar-android/src/main/res/values/strings.xml b/briar-android/src/main/res/values/strings.xml index d2dabb83f775f4c675334800f692913ea74b2ef4..7e85ec103411f08063ecb59ed24aca395f7f2620 100644 --- a/briar-android/src/main/res/values/strings.xml +++ b/briar-android/src/main/res/values/strings.xml @@ -381,4 +381,11 @@ <string name="screen_filter_title">Screen overlay detected</string> <string name="screen_filter_body">Another app is drawing on top of Briar. To protect your security, Briar will not respond to touches when another app is drawing on top.\n\nTry turning off the following apps when using Briar:\n\n%1$s</string> + <!-- Permission Requests and Doze Mode --> + <string name="permission_camera_title">Camera permission</string> + <string name="permission_camera_request_text">To scan the QR code, Briar needs access to the camera.</string> + <string name="permission_camera_perm_denied">You have denied access to the camera, but adding contacts requires using the camera. Please consider granting access.</string> + <string name="permission_camera_denied_toast">Camera permission was not granted</string> + <string name="open_settings">Open Settings</string> + </resources>