Compare commits
169 Commits
developmen
...
update_flu
| Author | SHA1 | Date |
|---|---|---|
|
|
41a6d14077 | 10 months ago |
|
|
6f91040bb1 | 10 months ago |
|
|
4143a36537 | 10 months ago |
|
|
1353f48f7e | 10 months ago |
|
|
707a98cb18 | 10 months ago |
|
|
9e60920399 | 10 months ago |
|
|
ced8e7976f | 10 months ago |
|
|
ed603c4268 | 11 months ago |
|
|
248c55f75d | 11 months ago |
|
|
8f12c3da17 | 11 months ago |
|
|
6e42e50f70 | 11 months ago |
|
|
126c084ef1 | 11 months ago |
|
|
e87a0a4dd6 | 11 months ago |
|
|
4de8cb4554 | 11 months ago |
|
|
fd4dacee83 | 11 months ago |
|
|
8f05d526f6 | 11 months ago |
|
|
5ecee03052 | 11 months ago |
|
|
09480bc994 | 11 months ago |
|
|
c69ada4aa1 | 11 months ago |
|
|
5da6083c91 | 11 months ago |
|
|
48f67dc532 | 11 months ago |
|
|
ec7bd327c8 | 11 months ago |
|
|
57a542c31e | 11 months ago |
|
|
9fcef3107f | 11 months ago |
|
|
629bab2b44 | 11 months ago |
|
|
f567f191de | 11 months ago |
|
|
4bb68778dc | 11 months ago |
|
|
772ab2504b | 11 months ago |
|
|
f7ed4b6c68 | 11 months ago |
|
|
3af6248d13 | 11 months ago |
|
|
ad29dd41f4 | 11 months ago |
|
|
71a65921a3 | 11 months ago |
|
|
f5abe9a6db | 11 months ago |
|
|
c040927e67 | 11 months ago |
|
|
01a735af61 | 11 months ago |
|
|
70114b8f19 | 11 months ago |
|
|
0526896745 | 11 months ago |
|
|
1976c04e12 | 11 months ago |
|
|
7765e8ff35 | 11 months ago |
|
|
6559dd123f | 11 months ago |
|
|
76ce759e86 | 11 months ago |
|
|
7253f6c05e | 11 months ago |
|
|
2fbb25de77 | 11 months ago |
|
|
d098b419e2 | 11 months ago |
|
|
a1960c8799 | 11 months ago |
|
|
5e31c9cf98 | 11 months ago |
|
|
b727e66ac1 | 11 months ago |
|
|
6392620df7 | 11 months ago |
|
|
f3876e9ef1 | 12 months ago |
|
|
a95b5d331b | 12 months ago |
|
|
6f0133a68e | 12 months ago |
|
|
2efd1a89c7 | 12 months ago |
|
|
c8654d84f3 | 12 months ago |
|
|
58d4f4e50e | 1 year ago |
|
|
5517d81fef | 1 year ago |
|
|
6aa990d53f | 1 year ago |
|
|
074bee8c4c | 1 year ago |
|
|
fc567213ed | 1 year ago |
|
|
5f90180fd6 | 1 year ago |
|
|
d44f6f8eb0 | 1 year ago |
|
|
63c1bc4cae | 1 year ago |
|
|
4ca9ffc5c4 | 1 year ago |
|
|
6788c00ced | 1 year ago |
|
|
f1b87f2568 | 1 year ago |
|
|
726f25b91b | 1 year ago |
|
|
8bf2ca3a63 | 1 year ago |
|
|
e3ccf398bc | 1 year ago |
|
|
1fbd511161 | 1 year ago |
|
|
564e70c0f6 | 1 year ago |
|
|
204d4531a5 | 1 year ago |
|
|
415b2174e7 | 1 year ago |
|
|
53aa39cab9 | 1 year ago |
|
|
8bd73827f8 | 1 year ago |
|
|
f125efb8c9 | 1 year ago |
|
|
9e7ca2ecf8 | 1 year ago |
|
|
fe5d66f57d | 1 year ago |
|
|
b8efa77837 | 1 year ago |
|
|
7b8e0ff7bd | 1 year ago |
|
|
4b06924538 | 1 year ago |
|
|
0b89ea91b7 | 1 year ago |
|
|
df9244f343 | 1 year ago |
|
|
b8bdee99a6 | 1 year ago |
|
|
4af8a0a28f | 1 year ago |
|
|
c13d7a08a0 | 1 year ago |
|
|
9fb6997e1d | 1 year ago |
|
|
6c0debaf5e | 1 year ago |
|
|
baff15d04d | 1 year ago |
|
|
9cd330bad0 | 1 year ago |
|
|
e4b891bf68 | 1 year ago |
|
|
60bc8a39fd | 1 year ago |
|
|
d62c723685 | 2 years ago |
|
|
ee86db3778 | 2 years ago |
|
|
519b7cfc2e | 2 years ago |
|
|
0b6e2bc8e4 | 2 years ago |
|
|
ad1f1e133c | 2 years ago |
|
|
d0163d5a38 | 2 years ago |
|
|
ea547b1f71 | 2 years ago |
|
|
dd396e50f2 | 2 years ago |
|
|
c47c93b2d9 | 2 years ago |
|
|
d2c26df4d0 | 2 years ago |
|
|
79446a20e8 | 2 years ago |
|
|
83737dc5a7 | 2 years ago |
|
|
7e4792d8cd | 2 years ago |
|
|
499cdc77d4 | 2 years ago |
|
|
0f2593f56d | 2 years ago |
|
|
ea497ff8b1 | 2 years ago |
|
|
693d352669 | 2 years ago |
|
|
38d00243d8 | 2 years ago |
|
|
e1604b09f8 | 2 years ago |
|
|
3b2ee0ef65 | 2 years ago |
|
|
9e122748e9 | 2 years ago |
|
|
aee83ce930 | 2 years ago |
|
|
941134da49 | 2 years ago |
|
|
0db3617737 | 2 years ago |
|
|
6d18dbbac0 | 2 years ago |
|
|
e524151da9 | 2 years ago |
|
|
cb01308f96 | 2 years ago |
|
|
c3ff456645 | 2 years ago |
|
|
5194cab7ec | 2 years ago |
|
|
307a11c36f | 2 years ago |
|
|
7e92348cb5 | 2 years ago |
|
|
0bcea41cc5 | 2 years ago |
|
|
d4cb4d7ad3 | 2 years ago |
|
|
dc802e2bda | 2 years ago |
|
|
08c0c0abc1 | 2 years ago |
|
|
f977dc10a4 | 2 years ago |
|
|
b5851f384d | 2 years ago |
|
|
b83c48abe5 | 2 years ago |
|
|
bb86949f65 | 2 years ago |
|
|
1bc355b2c4 | 2 years ago |
|
|
ea7744254b | 2 years ago |
|
|
76f8b37d1e | 2 years ago |
|
|
597d5a3498 | 2 years ago |
|
|
435567305e | 2 years ago |
|
|
320af7d9e0 | 2 years ago |
|
|
bee21f1123 | 2 years ago |
|
|
fde016c38b | 2 years ago |
|
|
8b515ee14d | 2 years ago |
|
|
8428c2f632 | 2 years ago |
|
|
324792d957 | 2 years ago |
|
|
f0204fcbb0 | 2 years ago |
|
|
793f9ff905 | 2 years ago |
|
|
5e63db7ea6 | 2 years ago |
|
|
0d65197b2f | 2 years ago |
|
|
d2a16f5357 | 2 years ago |
|
|
e530646c28 | 2 years ago |
|
|
1a67129a85 | 2 years ago |
|
|
fc21961807 | 2 years ago |
|
|
4741a7b66d | 2 years ago |
|
|
c50100613a | 2 years ago |
|
|
2b13d43757 | 2 years ago |
|
|
0861a0c79f | 2 years ago |
|
|
90382a1b27 | 2 years ago |
|
|
ac74e1e62d | 2 years ago |
|
|
92c665197d | 2 years ago |
|
|
3b3e911af3 | 2 years ago |
|
|
73ce9d02b8 | 2 years ago |
|
|
5d4aeff350 | 2 years ago |
|
|
51a8a93de1 | 3 years ago |
|
|
b2773aa320 | 3 years ago |
|
|
705c9f6fb8 | 3 years ago |
|
|
ba5f29ee9f | 3 years ago |
|
|
fe03cc606f | 3 years ago |
|
|
89a0af1d38 | 4 years ago |
|
|
7f66670f6d | 4 years ago |
|
|
48128d4f96 | 4 years ago |
|
|
4039b3c370 | 4 years ago |
|
|
604a6bbfa9 | 4 years ago |
|
|
82c3d3454a | 4 years ago |
@ -0,0 +1,28 @@
|
||||
# This file configures the analyzer, which statically analyzes Dart code to
|
||||
# check for errors, warnings, and lints.
|
||||
#
|
||||
# The issues identified by the analyzer are surfaced in the UI of Dart-enabled
|
||||
# IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be
|
||||
# invoked from the command line by running `flutter analyze`.
|
||||
|
||||
# The following line activates a set of recommended lints for Flutter apps,
|
||||
# packages, and plugins designed to encourage good coding practices.
|
||||
include: package:flutter_lints/flutter.yaml
|
||||
|
||||
linter:
|
||||
# The lint rules applied to this project can be customized in the
|
||||
# section below to disable rules from the `package:flutter_lints/flutter.yaml`
|
||||
# included above or to enable additional rules. A list of all available lints
|
||||
# and their documentation is published at https://dart.dev/lints.
|
||||
#
|
||||
# Instead of disabling a lint rule for the entire project in the
|
||||
# section below, it can also be suppressed for a single line of code
|
||||
# or a specific dart file by using the `// ignore: name_of_lint` and
|
||||
# `// ignore_for_file: name_of_lint` syntax on the line or in the file
|
||||
# producing the lint.
|
||||
rules:
|
||||
# avoid_print: false # Uncomment to disable the `avoid_print` rule
|
||||
# prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule
|
||||
|
||||
# Additional information about this file can be found at
|
||||
# https://dart.dev/guides/language/analysis-options
|
||||
@ -0,0 +1,52 @@
|
||||
apply plugin: 'com.android.library'
|
||||
|
||||
// See: https://github.com/vanniktech/gradle-maven-publish-plugin/issues/206
|
||||
ext {
|
||||
RELEASE_REPOSITORY_URL = "https://s01.oss.sonatype.org/service/local/staging/deploy/maven2/"
|
||||
SNAPSHOT_REPOSITORY_URL = "https://s01.oss.sonatype.org/content/repositories/snapshots/"
|
||||
}
|
||||
|
||||
//apply plugin: 'com.vanniktech.maven.publish'
|
||||
|
||||
android {
|
||||
compileSdkVersion 30
|
||||
testOptions.unitTests.includeAndroidResources = true
|
||||
|
||||
defaultConfig {
|
||||
minSdkVersion 14
|
||||
targetSdkVersion 30
|
||||
versionCode 1
|
||||
versionName "3.0.0"
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
debug {
|
||||
testCoverageEnabled true
|
||||
}
|
||||
release {
|
||||
minifyEnabled false
|
||||
consumerProguardFiles 'proguard-rules.pro'
|
||||
}
|
||||
}
|
||||
|
||||
testOptions {
|
||||
unitTests {
|
||||
includeAndroidResources = true
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
dependencies {
|
||||
api "androidx.appcompat:appcompat:1.1.0"
|
||||
api "androidx.annotation:annotation:1.1.0"
|
||||
api "androidx.core:core:1.3.0"
|
||||
api "androidx.fragment:fragment:1.2.5"
|
||||
|
||||
testImplementation 'junit:junit:4.13'
|
||||
testImplementation 'com.google.truth:truth:0.42'
|
||||
testImplementation 'org.robolectric:robolectric:4.1'
|
||||
testImplementation 'androidx.test:core:1.3.0-rc01'
|
||||
testImplementation 'androidx.fragment:fragment-testing:1.2.5'
|
||||
testImplementation 'org.mockito:mockito-core:2.23.4'
|
||||
}
|
||||
@ -0,0 +1,18 @@
|
||||
GROUP=pub.devrel
|
||||
POM_ARTIFACT_ID=easypermissions
|
||||
VERSION_NAME=3.0.0
|
||||
|
||||
POM_NAME=EasyPermissions
|
||||
POM_PACKAGING=aar
|
||||
|
||||
POM_DESCRIPTION=A wrapper library for basic Android M system permissions logic
|
||||
|
||||
POM_URL=https://github.com/googlesamples/easypermissions
|
||||
POM_SCM_URL=https://github.com/googlesamples/easypermissions
|
||||
POM_SCM_CONNECTION=https://github.com/googlesamples/easypermissions.git
|
||||
|
||||
POM_LICENCE_NAME=The Apache Software License, Version 2.0
|
||||
POM_LICENCE_URL=http://www.apache.org/licenses/LICENSE-2.0.txt
|
||||
POM_LICENCE_DIST=repo
|
||||
|
||||
POM_DEVELOPER_NAME=Google
|
||||
@ -0,0 +1,3 @@
|
||||
-keepclassmembers class * {
|
||||
@pub.devrel.easypermissions.AfterPermissionGranted <methods>;
|
||||
}
|
||||
@ -0,0 +1,14 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="pub.devrel.easypermissions">
|
||||
|
||||
<application>
|
||||
<activity
|
||||
android:name="pub.devrel.easypermissions.AppSettingsDialogHolderActivity"
|
||||
android:exported="false"
|
||||
android:label=""
|
||||
android:theme="@style/EasyPermissions.Transparent"/>
|
||||
</application>
|
||||
|
||||
</manifest>
|
||||
@ -0,0 +1,29 @@
|
||||
/*
|
||||
* Copyright Google Inc. All Rights Reserved.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
package pub.devrel.easypermissions;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.METHOD)
|
||||
public @interface AfterPermissionGranted {
|
||||
|
||||
int value();
|
||||
|
||||
}
|
||||
@ -0,0 +1,356 @@
|
||||
package pub.devrel.easypermissions;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.Intent;
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.RestrictTo;
|
||||
import androidx.annotation.StringRes;
|
||||
import androidx.annotation.StyleRes;
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
import androidx.fragment.app.Fragment;
|
||||
|
||||
/**
|
||||
* Dialog to prompt the user to go to the app's settings screen and enable permissions. If the user
|
||||
* clicks 'OK' on the dialog, they are sent to the settings screen. The result is returned to the
|
||||
* Activity via {@see Activity#onActivityResult(int, int, Intent)}.
|
||||
* <p>
|
||||
* Use the {@link Builder} to create and display a dialog.
|
||||
*/
|
||||
public class AppSettingsDialog implements Parcelable {
|
||||
|
||||
private static final String TAG = "EasyPermissions";
|
||||
|
||||
public static final int DEFAULT_SETTINGS_REQ_CODE = 16061;
|
||||
|
||||
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
|
||||
public static final Parcelable.Creator<AppSettingsDialog> CREATOR = new Parcelable.Creator<AppSettingsDialog>() {
|
||||
@Override
|
||||
public AppSettingsDialog createFromParcel(Parcel in) {
|
||||
return new AppSettingsDialog(in);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AppSettingsDialog[] newArray(int size) {
|
||||
return new AppSettingsDialog[size];
|
||||
}
|
||||
};
|
||||
|
||||
static final String EXTRA_APP_SETTINGS = "extra_app_settings";
|
||||
|
||||
@StyleRes
|
||||
private final int mThemeResId;
|
||||
private final String mRationale;
|
||||
private final String mTitle;
|
||||
private final String mPositiveButtonText;
|
||||
private final String mNegativeButtonText;
|
||||
private final int mRequestCode;
|
||||
private final int mIntentFlags;
|
||||
|
||||
private Object mActivityOrFragment;
|
||||
private Context mContext;
|
||||
|
||||
private AppSettingsDialog(Parcel in) {
|
||||
mThemeResId = in.readInt();
|
||||
mRationale = in.readString();
|
||||
mTitle = in.readString();
|
||||
mPositiveButtonText = in.readString();
|
||||
mNegativeButtonText = in.readString();
|
||||
mRequestCode = in.readInt();
|
||||
mIntentFlags = in.readInt();
|
||||
}
|
||||
|
||||
private AppSettingsDialog(@NonNull final Object activityOrFragment,
|
||||
@StyleRes int themeResId,
|
||||
@Nullable String rationale,
|
||||
@Nullable String title,
|
||||
@Nullable String positiveButtonText,
|
||||
@Nullable String negativeButtonText,
|
||||
int requestCode,
|
||||
int intentFlags) {
|
||||
setActivityOrFragment(activityOrFragment);
|
||||
mThemeResId = themeResId;
|
||||
mRationale = rationale;
|
||||
mTitle = title;
|
||||
mPositiveButtonText = positiveButtonText;
|
||||
mNegativeButtonText = negativeButtonText;
|
||||
mRequestCode = requestCode;
|
||||
mIntentFlags = intentFlags;
|
||||
}
|
||||
|
||||
static AppSettingsDialog fromIntent(Intent intent, Activity activity) {
|
||||
AppSettingsDialog dialog = intent.getParcelableExtra(AppSettingsDialog.EXTRA_APP_SETTINGS);
|
||||
|
||||
// It's not clear how this could happen, but in the case that it does we should try
|
||||
// to avoid a runtime crash and just use the default dialog.
|
||||
// https://github.com/googlesamples/easypermissions/issues/278
|
||||
if (dialog == null) {
|
||||
Log.e(TAG, "Intent contains null value for EXTRA_APP_SETTINGS: "
|
||||
+ "intent=" + intent
|
||||
+ ", "
|
||||
+ "extras=" + intent.getExtras());
|
||||
|
||||
dialog = new AppSettingsDialog.Builder(activity).build();
|
||||
}
|
||||
|
||||
dialog.setActivityOrFragment(activity);
|
||||
return dialog;
|
||||
}
|
||||
|
||||
private void setActivityOrFragment(Object activityOrFragment) {
|
||||
mActivityOrFragment = activityOrFragment;
|
||||
|
||||
if (activityOrFragment instanceof Activity) {
|
||||
mContext = (Activity) activityOrFragment;
|
||||
} else if (activityOrFragment instanceof Fragment) {
|
||||
mContext = ((Fragment) activityOrFragment).getContext();
|
||||
} else {
|
||||
throw new IllegalStateException("Unknown object: " + activityOrFragment);
|
||||
}
|
||||
}
|
||||
|
||||
private void startForResult(Intent intent) {
|
||||
if (mActivityOrFragment instanceof Activity) {
|
||||
((Activity) mActivityOrFragment).startActivityForResult(intent, mRequestCode);
|
||||
} else if (mActivityOrFragment instanceof Fragment) {
|
||||
((Fragment) mActivityOrFragment).startActivityForResult(intent, mRequestCode);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Display the built dialog.
|
||||
*/
|
||||
public void show() {
|
||||
startForResult(AppSettingsDialogHolderActivity.createShowDialogIntent(mContext, this));
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the dialog. {@link #show()} is a wrapper to ensure backwards compatibility
|
||||
*/
|
||||
AlertDialog showDialog(DialogInterface.OnClickListener positiveListener,
|
||||
DialogInterface.OnClickListener negativeListener) {
|
||||
AlertDialog.Builder builder;
|
||||
if (mThemeResId != -1) {
|
||||
builder = new AlertDialog.Builder(mContext, mThemeResId);
|
||||
} else {
|
||||
builder = new AlertDialog.Builder(mContext);
|
||||
}
|
||||
return builder
|
||||
.setCancelable(false)
|
||||
.setTitle(mTitle)
|
||||
.setMessage(mRationale)
|
||||
.setPositiveButton(mPositiveButtonText, positiveListener)
|
||||
.setNegativeButton(mNegativeButtonText, negativeListener)
|
||||
.show();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeToParcel(@NonNull Parcel dest, int flags) {
|
||||
dest.writeInt(mThemeResId);
|
||||
dest.writeString(mRationale);
|
||||
dest.writeString(mTitle);
|
||||
dest.writeString(mPositiveButtonText);
|
||||
dest.writeString(mNegativeButtonText);
|
||||
dest.writeInt(mRequestCode);
|
||||
dest.writeInt(mIntentFlags);
|
||||
}
|
||||
|
||||
int getIntentFlags() {
|
||||
return mIntentFlags;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builder for an {@link AppSettingsDialog}.
|
||||
*/
|
||||
public static class Builder {
|
||||
|
||||
private final Object mActivityOrFragment;
|
||||
private final Context mContext;
|
||||
@StyleRes
|
||||
private int mThemeResId = -1;
|
||||
private String mRationale;
|
||||
private String mTitle;
|
||||
private String mPositiveButtonText;
|
||||
private String mNegativeButtonText;
|
||||
private int mRequestCode = -1;
|
||||
private boolean mOpenInNewTask = false;
|
||||
|
||||
/**
|
||||
* Create a new Builder for an {@link AppSettingsDialog}.
|
||||
*
|
||||
* @param activity the {@link Activity} in which to display the dialog.
|
||||
*/
|
||||
public Builder(@NonNull Activity activity) {
|
||||
mActivityOrFragment = activity;
|
||||
mContext = activity;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new Builder for an {@link AppSettingsDialog}.
|
||||
*
|
||||
* @param fragment the {@link Fragment} in which to display the dialog.
|
||||
*/
|
||||
public Builder(@NonNull Fragment fragment) {
|
||||
mActivityOrFragment = fragment;
|
||||
mContext = fragment.getContext();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the dialog theme.
|
||||
*/
|
||||
@NonNull
|
||||
public Builder setThemeResId(@StyleRes int themeResId) {
|
||||
mThemeResId = themeResId;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the title dialog. Default is "Permissions Required".
|
||||
*/
|
||||
@NonNull
|
||||
public Builder setTitle(@Nullable String title) {
|
||||
mTitle = title;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the title dialog. Default is "Permissions Required".
|
||||
*/
|
||||
@NonNull
|
||||
public Builder setTitle(@StringRes int title) {
|
||||
mTitle = mContext.getString(title);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the rationale dialog. Default is
|
||||
* "This app may not work correctly without the requested permissions.
|
||||
* Open the app settings screen to modify app permissions."
|
||||
*/
|
||||
@NonNull
|
||||
public Builder setRationale(@Nullable String rationale) {
|
||||
mRationale = rationale;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the rationale dialog. Default is
|
||||
* "This app may not work correctly without the requested permissions.
|
||||
* Open the app settings screen to modify app permissions."
|
||||
*/
|
||||
@NonNull
|
||||
public Builder setRationale(@StringRes int rationale) {
|
||||
mRationale = mContext.getString(rationale);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the positive button text, default is {@link android.R.string#ok}.
|
||||
*/
|
||||
@NonNull
|
||||
public Builder setPositiveButton(@Nullable String text) {
|
||||
mPositiveButtonText = text;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the positive button text, default is {@link android.R.string#ok}.
|
||||
*/
|
||||
@NonNull
|
||||
public Builder setPositiveButton(@StringRes int textId) {
|
||||
mPositiveButtonText = mContext.getString(textId);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the negative button text, default is {@link android.R.string#cancel}.
|
||||
* <p>
|
||||
* To know if a user cancelled the request, check if your permissions were given with {@link
|
||||
* EasyPermissions#hasPermissions(Context, String...)} in {@see
|
||||
* Activity#onActivityResult(int, int, Intent)}. If you still don't have the right
|
||||
* permissions, then the request was cancelled.
|
||||
*/
|
||||
@NonNull
|
||||
public Builder setNegativeButton(@Nullable String text) {
|
||||
mNegativeButtonText = text;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the negative button text, default is {@link android.R.string#cancel}.
|
||||
*/
|
||||
@NonNull
|
||||
public Builder setNegativeButton(@StringRes int textId) {
|
||||
mNegativeButtonText = mContext.getString(textId);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the request code use when launching the Settings screen for result, can be retrieved
|
||||
* in the calling Activity's {@see Activity#onActivityResult(int, int, Intent)} method.
|
||||
* Default is {@link #DEFAULT_SETTINGS_REQ_CODE}.
|
||||
*/
|
||||
@NonNull
|
||||
public Builder setRequestCode(int requestCode) {
|
||||
mRequestCode = requestCode;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set whether the settings screen should be opened in a separate task. This is achieved by
|
||||
* setting {@link android.content.Intent#FLAG_ACTIVITY_NEW_TASK#FLAG_ACTIVITY_NEW_TASK} on
|
||||
* the Intent used to open the settings screen.
|
||||
*/
|
||||
@NonNull
|
||||
public Builder setOpenInNewTask(boolean openInNewTask) {
|
||||
mOpenInNewTask = openInNewTask;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the {@link AppSettingsDialog} from the specified options. Generally followed by a
|
||||
* call to {@link AppSettingsDialog#show()}.
|
||||
*/
|
||||
@NonNull
|
||||
public AppSettingsDialog build() {
|
||||
mRationale = TextUtils.isEmpty(mRationale) ?
|
||||
mContext.getString(R.string.rationale_ask_again) : mRationale;
|
||||
mTitle = TextUtils.isEmpty(mTitle) ?
|
||||
mContext.getString(R.string.title_settings_dialog) : mTitle;
|
||||
mPositiveButtonText = TextUtils.isEmpty(mPositiveButtonText) ?
|
||||
mContext.getString(android.R.string.ok) : mPositiveButtonText;
|
||||
mNegativeButtonText = TextUtils.isEmpty(mNegativeButtonText) ?
|
||||
mContext.getString(android.R.string.cancel) : mNegativeButtonText;
|
||||
mRequestCode = mRequestCode > 0 ? mRequestCode : DEFAULT_SETTINGS_REQ_CODE;
|
||||
|
||||
int intentFlags = 0;
|
||||
if (mOpenInNewTask) {
|
||||
intentFlags |= Intent.FLAG_ACTIVITY_NEW_TASK;
|
||||
}
|
||||
|
||||
return new AppSettingsDialog(
|
||||
mActivityOrFragment,
|
||||
mThemeResId,
|
||||
mRationale,
|
||||
mTitle,
|
||||
mPositiveButtonText,
|
||||
mNegativeButtonText,
|
||||
mRequestCode,
|
||||
intentFlags);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,65 @@
|
||||
package pub.devrel.easypermissions;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.Dialog;
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.provider.Settings;
|
||||
import androidx.annotation.RestrictTo;
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
|
||||
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
|
||||
public class AppSettingsDialogHolderActivity extends AppCompatActivity implements DialogInterface.OnClickListener {
|
||||
private static final int APP_SETTINGS_RC = 7534;
|
||||
|
||||
private AlertDialog mDialog;
|
||||
private int mIntentFlags;
|
||||
|
||||
public static Intent createShowDialogIntent(Context context, AppSettingsDialog dialog) {
|
||||
Intent intent = new Intent(context, AppSettingsDialogHolderActivity.class);
|
||||
intent.putExtra(AppSettingsDialog.EXTRA_APP_SETTINGS, dialog);
|
||||
return intent;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
AppSettingsDialog appSettingsDialog = AppSettingsDialog.fromIntent(getIntent(), this);
|
||||
mIntentFlags = appSettingsDialog.getIntentFlags();
|
||||
mDialog = appSettingsDialog.showDialog(this, this);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDestroy() {
|
||||
super.onDestroy();
|
||||
if (mDialog != null && mDialog.isShowing()) {
|
||||
mDialog.dismiss();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
if (which == Dialog.BUTTON_POSITIVE) {
|
||||
Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS)
|
||||
.setData(Uri.fromParts("package", getPackageName(), null));
|
||||
intent.addFlags(mIntentFlags);
|
||||
startActivityForResult(intent, APP_SETTINGS_RC);
|
||||
} else if (which == Dialog.BUTTON_NEGATIVE) {
|
||||
setResult(Activity.RESULT_CANCELED);
|
||||
finish();
|
||||
} else {
|
||||
throw new IllegalStateException("Unknown button type: " + which);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||
super.onActivityResult(requestCode, resultCode, data);
|
||||
setResult(resultCode, data);
|
||||
finish();
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,358 @@
|
||||
/*
|
||||
* Copyright Google Inc. All Rights Reserved.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
package pub.devrel.easypermissions;
|
||||
|
||||
import android.Manifest;
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.os.Build;
|
||||
import androidx.annotation.IntRange;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Size;
|
||||
import androidx.core.app.ActivityCompat;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.core.content.ContextCompat;
|
||||
import android.util.Log;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import pub.devrel.easypermissions.helper.PermissionHelper;
|
||||
|
||||
/**
|
||||
* Utility to request and check System permissions for apps targeting Android M (API >= 23).
|
||||
*/
|
||||
public class EasyPermissions {
|
||||
|
||||
/**
|
||||
* Callback interface to receive the results of {@code EasyPermissions.requestPermissions()}
|
||||
* calls.
|
||||
*/
|
||||
public interface PermissionCallbacks extends ActivityCompat.OnRequestPermissionsResultCallback {
|
||||
|
||||
void onPermissionsGranted(int requestCode, @NonNull List<String> perms);
|
||||
|
||||
void onPermissionsDenied(int requestCode, @NonNull List<String> perms);
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback interface to receive button clicked events of the rationale dialog
|
||||
*/
|
||||
public interface RationaleCallbacks {
|
||||
void onRationaleAccepted(int requestCode);
|
||||
|
||||
void onRationaleDenied(int requestCode);
|
||||
}
|
||||
|
||||
private static final String TAG = "EasyPermissions";
|
||||
|
||||
/**
|
||||
* Check if the calling context has a set of permissions.
|
||||
*
|
||||
* @param context the calling context.
|
||||
* @param perms one ore more permissions, such as {@link Manifest.permission#CAMERA}.
|
||||
* @return true if all permissions are already granted, false if at least one permission is not
|
||||
* yet granted.
|
||||
* @see Manifest.permission
|
||||
*/
|
||||
public static boolean hasPermissions(@NonNull Context context,
|
||||
@Size(min = 1) @NonNull String... perms) {
|
||||
// Always return true for SDK < M, let the system deal with the permissions
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
|
||||
Log.w(TAG, "hasPermissions: API version < M, returning true by default");
|
||||
|
||||
// DANGER ZONE!!! Changing this will break the library.
|
||||
return true;
|
||||
}
|
||||
|
||||
// Null context may be passed if we have detected Low API (less than M) so getting
|
||||
// to this point with a null context should not be possible.
|
||||
if (context == null) {
|
||||
throw new IllegalArgumentException("Can't check permissions for null context");
|
||||
}
|
||||
|
||||
for (String perm : perms) {
|
||||
if (ContextCompat.checkSelfPermission(context, perm)
|
||||
!= PackageManager.PERMISSION_GRANTED) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Request a set of permissions, showing a rationale if the system requests it.
|
||||
*
|
||||
* @param host requesting context.
|
||||
* @param rationale a message explaining why the application needs this set of permissions;
|
||||
* will be displayed if the user rejects the request the first time.
|
||||
* @param requestCode request code to track this request, must be < 256.
|
||||
* @param perms a set of permissions to be requested.
|
||||
* @see Manifest.permission
|
||||
*/
|
||||
public static void requestPermissions(
|
||||
@NonNull Activity host, @NonNull String rationale,
|
||||
@IntRange(from = 0, to = 255) int requestCode, @Size(min = 1) @NonNull String... perms) {
|
||||
requestPermissions(
|
||||
new PermissionRequest.Builder(host, requestCode, perms)
|
||||
.setRationale(rationale)
|
||||
.build());
|
||||
}
|
||||
|
||||
/**
|
||||
* Request permissions from a Support Fragment with standard OK/Cancel buttons.
|
||||
*
|
||||
* @see #requestPermissions(Activity, String, int, String...)
|
||||
*/
|
||||
public static void requestPermissions(
|
||||
@NonNull Fragment host, @NonNull String rationale,
|
||||
@IntRange(from = 0, to = 255) int requestCode, @Size(min = 1) @NonNull String... perms) {
|
||||
requestPermissions(
|
||||
new PermissionRequest.Builder(host, requestCode, perms)
|
||||
.setRationale(rationale)
|
||||
.build());
|
||||
}
|
||||
|
||||
/**
|
||||
* Request a set of permissions.
|
||||
*
|
||||
* @param request the permission request
|
||||
* @see PermissionRequest
|
||||
*/
|
||||
public static void requestPermissions(PermissionRequest request) {
|
||||
|
||||
// Check for permissions before dispatching the request
|
||||
if (hasPermissions(request.getHelper().getContext(), request.getPerms())) {
|
||||
notifyAlreadyHasPermissions(
|
||||
request.getHelper().getHost(), request.getRequestCode(), request.getPerms());
|
||||
return;
|
||||
}
|
||||
|
||||
// Request permissions
|
||||
request.getHelper().requestPermissions(
|
||||
request.getRationale(),
|
||||
request.getPositiveButtonText(),
|
||||
request.getNegativeButtonText(),
|
||||
request.getTheme(),
|
||||
request.getRequestCode(),
|
||||
request.getPerms());
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle the result of a permission request, should be called from the calling {@link
|
||||
* Activity}'s {@link ActivityCompat.OnRequestPermissionsResultCallback#onRequestPermissionsResult(int,
|
||||
* String[], int[])} method.
|
||||
* <p>
|
||||
* If any permissions were granted or denied, the {@code object} will receive the appropriate
|
||||
* callbacks through {@link PermissionCallbacks} and methods annotated with {@link
|
||||
* AfterPermissionGranted} will be run if appropriate.
|
||||
*
|
||||
* @param requestCode requestCode argument to permission result callback.
|
||||
* @param permissions permissions argument to permission result callback.
|
||||
* @param grantResults grantResults argument to permission result callback.
|
||||
* @param receivers an array of objects that have a method annotated with {@link
|
||||
* AfterPermissionGranted} or implement {@link PermissionCallbacks}.
|
||||
*/
|
||||
public static void onRequestPermissionsResult(@IntRange(from = 0, to = 255) int requestCode,
|
||||
@NonNull String[] permissions,
|
||||
@NonNull int[] grantResults,
|
||||
@NonNull Object... receivers) {
|
||||
// Make a collection of granted and denied permissions from the request.
|
||||
List<String> granted = new ArrayList<>();
|
||||
List<String> denied = new ArrayList<>();
|
||||
for (int i = 0; i < permissions.length; i++) {
|
||||
String perm = permissions[i];
|
||||
if (grantResults[i] == PackageManager.PERMISSION_GRANTED) {
|
||||
granted.add(perm);
|
||||
} else {
|
||||
denied.add(perm);
|
||||
}
|
||||
}
|
||||
|
||||
// iterate through all receivers
|
||||
for (Object object : receivers) {
|
||||
// Report granted permissions, if any.
|
||||
if (!granted.isEmpty()) {
|
||||
if (object instanceof PermissionCallbacks) {
|
||||
((PermissionCallbacks) object).onPermissionsGranted(requestCode, granted);
|
||||
}
|
||||
}
|
||||
|
||||
// Report denied permissions, if any.
|
||||
if (!denied.isEmpty()) {
|
||||
if (object instanceof PermissionCallbacks) {
|
||||
((PermissionCallbacks) object).onPermissionsDenied(requestCode, denied);
|
||||
}
|
||||
}
|
||||
|
||||
// If 100% successful, call annotated methods
|
||||
if (!granted.isEmpty() && denied.isEmpty()) {
|
||||
runAnnotatedMethods(object, requestCode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if at least one permission in the list of denied permissions has been permanently
|
||||
* denied (user clicked "Never ask again").
|
||||
*
|
||||
* <b>Note</b>: Due to a limitation in the information provided by the Android
|
||||
* framework permissions API, this method only works after the permission
|
||||
* has been denied and your app has received the onPermissionsDenied callback.
|
||||
* Otherwise the library cannot distinguish permanent denial from the
|
||||
* "not yet denied" case.
|
||||
*
|
||||
* @param host context requesting permissions.
|
||||
* @param deniedPermissions list of denied permissions, usually from {@link
|
||||
* PermissionCallbacks#onPermissionsDenied(int, List)}
|
||||
* @return {@code true} if at least one permission in the list was permanently denied.
|
||||
*/
|
||||
public static boolean somePermissionPermanentlyDenied(@NonNull Activity host,
|
||||
@NonNull List<String> deniedPermissions) {
|
||||
return PermissionHelper.newInstance(host)
|
||||
.somePermissionPermanentlyDenied(deniedPermissions);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see #somePermissionPermanentlyDenied(Activity, List)
|
||||
*/
|
||||
public static boolean somePermissionPermanentlyDenied(@NonNull Fragment host,
|
||||
@NonNull List<String> deniedPermissions) {
|
||||
return PermissionHelper.newInstance(host)
|
||||
.somePermissionPermanentlyDenied(deniedPermissions);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a permission has been permanently denied (user clicked "Never ask again").
|
||||
*
|
||||
* @param host context requesting permissions.
|
||||
* @param deniedPermission denied permission.
|
||||
* @return {@code true} if the permissions has been permanently denied.
|
||||
*/
|
||||
public static boolean permissionPermanentlyDenied(@NonNull Activity host,
|
||||
@NonNull String deniedPermission) {
|
||||
return PermissionHelper.newInstance(host).permissionPermanentlyDenied(deniedPermission);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see #permissionPermanentlyDenied(Activity, String)
|
||||
*/
|
||||
public static boolean permissionPermanentlyDenied(@NonNull Fragment host,
|
||||
@NonNull String deniedPermission) {
|
||||
return PermissionHelper.newInstance(host).permissionPermanentlyDenied(deniedPermission);
|
||||
}
|
||||
|
||||
/**
|
||||
* See if some denied permission has been permanently denied.
|
||||
*
|
||||
* @param host requesting context.
|
||||
* @param perms array of permissions.
|
||||
* @return true if the user has previously denied any of the {@code perms} and we should show a
|
||||
* rationale, false otherwise.
|
||||
*/
|
||||
public static boolean somePermissionDenied(@NonNull Activity host,
|
||||
@NonNull String... perms) {
|
||||
return PermissionHelper.newInstance(host).somePermissionDenied(perms);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see #somePermissionDenied(Activity, String...)
|
||||
*/
|
||||
public static boolean somePermissionDenied(@NonNull Fragment host,
|
||||
@NonNull String... perms) {
|
||||
return PermissionHelper.newInstance(host).somePermissionDenied(perms);
|
||||
}
|
||||
|
||||
/**
|
||||
* Run permission callbacks on an object that requested permissions but already has them by
|
||||
* simulating {@link PackageManager#PERMISSION_GRANTED}.
|
||||
*
|
||||
* @param object the object requesting permissions.
|
||||
* @param requestCode the permission request code.
|
||||
* @param perms a list of permissions requested.
|
||||
*/
|
||||
private static void notifyAlreadyHasPermissions(@NonNull Object object,
|
||||
int requestCode,
|
||||
@NonNull String[] perms) {
|
||||
int[] grantResults = new int[perms.length];
|
||||
for (int i = 0; i < perms.length; i++) {
|
||||
grantResults[i] = PackageManager.PERMISSION_GRANTED;
|
||||
}
|
||||
|
||||
onRequestPermissionsResult(requestCode, perms, grantResults, object);
|
||||
}
|
||||
|
||||
/**
|
||||
* Find all methods annotated with {@link AfterPermissionGranted} on a given object with the
|
||||
* correct requestCode argument.
|
||||
*
|
||||
* @param object the object with annotated methods.
|
||||
* @param requestCode the requestCode passed to the annotation.
|
||||
*/
|
||||
private static void runAnnotatedMethods(@NonNull Object object, int requestCode) {
|
||||
Class clazz = object.getClass();
|
||||
if (isUsingAndroidAnnotations(object)) {
|
||||
clazz = clazz.getSuperclass();
|
||||
}
|
||||
|
||||
while (clazz != null) {
|
||||
for (Method method : clazz.getDeclaredMethods()) {
|
||||
AfterPermissionGranted ann = method.getAnnotation(AfterPermissionGranted.class);
|
||||
if (ann != null) {
|
||||
// Check for annotated methods with matching request code.
|
||||
if (ann.value() == requestCode) {
|
||||
// Method must be void so that we can invoke it
|
||||
if (method.getParameterTypes().length > 0) {
|
||||
throw new RuntimeException(
|
||||
"Cannot execute method " + method.getName() + " because it is non-void method and/or has input parameters.");
|
||||
}
|
||||
|
||||
try {
|
||||
// Make method accessible if private
|
||||
if (!method.isAccessible()) {
|
||||
method.setAccessible(true);
|
||||
}
|
||||
method.invoke(object);
|
||||
} catch (IllegalAccessException e) {
|
||||
Log.e(TAG, "runDefaultMethod:IllegalAccessException", e);
|
||||
} catch (InvocationTargetException e) {
|
||||
Log.e(TAG, "runDefaultMethod:InvocationTargetException", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
clazz = clazz.getSuperclass();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the project is using the AndroidAnnotations library.
|
||||
*/
|
||||
private static boolean isUsingAndroidAnnotations(@NonNull Object object) {
|
||||
if (!object.getClass().getSimpleName().endsWith("_")) {
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
Class clazz = Class.forName("org.androidannotations.api.view.HasViews");
|
||||
return clazz.isInstance(object);
|
||||
} catch (ClassNotFoundException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,260 @@
|
||||
package pub.devrel.easypermissions;
|
||||
|
||||
import android.app.Activity;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.RestrictTo;
|
||||
import androidx.annotation.Size;
|
||||
import androidx.annotation.StringRes;
|
||||
import androidx.annotation.StyleRes;
|
||||
import androidx.fragment.app.Fragment;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
import pub.devrel.easypermissions.helper.PermissionHelper;
|
||||
|
||||
/**
|
||||
* An immutable model object that holds all of the parameters associated with a permission request,
|
||||
* such as the permissions, request code, and rationale.
|
||||
*
|
||||
* @see EasyPermissions#requestPermissions(PermissionRequest)
|
||||
* @see PermissionRequest.Builder
|
||||
*/
|
||||
public final class PermissionRequest {
|
||||
private final PermissionHelper mHelper;
|
||||
private final String[] mPerms;
|
||||
private final int mRequestCode;
|
||||
private final String mRationale;
|
||||
private final String mPositiveButtonText;
|
||||
private final String mNegativeButtonText;
|
||||
private final int mTheme;
|
||||
|
||||
private PermissionRequest(PermissionHelper helper,
|
||||
String[] perms,
|
||||
int requestCode,
|
||||
String rationale,
|
||||
String positiveButtonText,
|
||||
String negativeButtonText,
|
||||
int theme) {
|
||||
mHelper = helper;
|
||||
mPerms = perms.clone();
|
||||
mRequestCode = requestCode;
|
||||
mRationale = rationale;
|
||||
mPositiveButtonText = positiveButtonText;
|
||||
mNegativeButtonText = negativeButtonText;
|
||||
mTheme = theme;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
|
||||
public PermissionHelper getHelper() {
|
||||
return mHelper;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public String[] getPerms() {
|
||||
return mPerms.clone();
|
||||
}
|
||||
|
||||
public int getRequestCode() {
|
||||
return mRequestCode;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public String getRationale() {
|
||||
return mRationale;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public String getPositiveButtonText() {
|
||||
return mPositiveButtonText;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public String getNegativeButtonText() {
|
||||
return mNegativeButtonText;
|
||||
}
|
||||
|
||||
@StyleRes
|
||||
public int getTheme() {
|
||||
return mTheme;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
|
||||
PermissionRequest request = (PermissionRequest) o;
|
||||
|
||||
return Arrays.equals(mPerms, request.mPerms) && mRequestCode == request.mRequestCode;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = Arrays.hashCode(mPerms);
|
||||
result = 31 * result + mRequestCode;
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "PermissionRequest{" +
|
||||
"mHelper=" + mHelper +
|
||||
", mPerms=" + Arrays.toString(mPerms) +
|
||||
", mRequestCode=" + mRequestCode +
|
||||
", mRationale='" + mRationale + '\'' +
|
||||
", mPositiveButtonText='" + mPositiveButtonText + '\'' +
|
||||
", mNegativeButtonText='" + mNegativeButtonText + '\'' +
|
||||
", mTheme=" + mTheme +
|
||||
'}';
|
||||
}
|
||||
|
||||
/**
|
||||
* Builder to build a permission request with variable options.
|
||||
*
|
||||
* @see PermissionRequest
|
||||
*/
|
||||
public static final class Builder {
|
||||
private final PermissionHelper mHelper;
|
||||
private final int mRequestCode;
|
||||
private final String[] mPerms;
|
||||
|
||||
private String mRationale;
|
||||
private String mPositiveButtonText;
|
||||
private String mNegativeButtonText;
|
||||
private int mTheme = -1;
|
||||
|
||||
/**
|
||||
* Construct a new permission request builder with a host, request code, and the requested
|
||||
* permissions.
|
||||
*
|
||||
* @param activity the permission request host
|
||||
* @param requestCode request code to track this request; must be < 256
|
||||
* @param perms the set of permissions to be requested
|
||||
*/
|
||||
public Builder(@NonNull Activity activity, int requestCode,
|
||||
@NonNull @Size(min = 1) String... perms) {
|
||||
mHelper = PermissionHelper.newInstance(activity);
|
||||
mRequestCode = requestCode;
|
||||
mPerms = perms;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see #Builder(Activity, int, String...)
|
||||
*/
|
||||
public Builder(@NonNull Fragment fragment, int requestCode,
|
||||
@NonNull @Size(min = 1) String... perms) {
|
||||
mHelper = PermissionHelper.newInstance(fragment);
|
||||
mRequestCode = requestCode;
|
||||
mPerms = perms;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the rationale to display to the user if they don't allow your permissions on the
|
||||
* first try. This rationale will be shown as long as the user has denied your permissions
|
||||
* at least once, but has not yet permanently denied your permissions. Should the user
|
||||
* permanently deny your permissions, use the {@link AppSettingsDialog} instead.
|
||||
* <p>
|
||||
* The default rationale text is {@link R.string#rationale_ask}.
|
||||
*
|
||||
* @param rationale the rationale to be displayed to the user should they deny your
|
||||
* permission at least once
|
||||
*/
|
||||
@NonNull
|
||||
public Builder setRationale(@Nullable String rationale) {
|
||||
mRationale = rationale;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param resId the string resource to be used as a rationale
|
||||
* @see #setRationale(String)
|
||||
*/
|
||||
@NonNull
|
||||
public Builder setRationale(@StringRes int resId) {
|
||||
mRationale = mHelper.getContext().getString(resId);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the positive button text for the rationale dialog should it be shown.
|
||||
* <p>
|
||||
* The default is {@link android.R.string#ok}
|
||||
*/
|
||||
@NonNull
|
||||
public Builder setPositiveButtonText(@Nullable String positiveButtonText) {
|
||||
mPositiveButtonText = positiveButtonText;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see #setPositiveButtonText(String)
|
||||
*/
|
||||
@NonNull
|
||||
public Builder setPositiveButtonText(@StringRes int resId) {
|
||||
mPositiveButtonText = mHelper.getContext().getString(resId);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the negative button text for the rationale dialog should it be shown.
|
||||
* <p>
|
||||
* The default is {@link android.R.string#cancel}
|
||||
*/
|
||||
@NonNull
|
||||
public Builder setNegativeButtonText(@Nullable String negativeButtonText) {
|
||||
mNegativeButtonText = negativeButtonText;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see #setNegativeButtonText(String)
|
||||
*/
|
||||
@NonNull
|
||||
public Builder setNegativeButtonText(@StringRes int resId) {
|
||||
mNegativeButtonText = mHelper.getContext().getString(resId);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the theme to be used for the rationale dialog should it be shown.
|
||||
*
|
||||
* @param theme a style resource
|
||||
*/
|
||||
@NonNull
|
||||
public Builder setTheme(@StyleRes int theme) {
|
||||
mTheme = theme;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the permission request.
|
||||
*
|
||||
* @return the permission request
|
||||
* @see EasyPermissions#requestPermissions(PermissionRequest)
|
||||
* @see PermissionRequest
|
||||
*/
|
||||
@NonNull
|
||||
public PermissionRequest build() {
|
||||
if (mRationale == null) {
|
||||
mRationale = mHelper.getContext().getString(R.string.rationale_ask);
|
||||
}
|
||||
if (mPositiveButtonText == null) {
|
||||
mPositiveButtonText = mHelper.getContext().getString(android.R.string.ok);
|
||||
}
|
||||
if (mNegativeButtonText == null) {
|
||||
mNegativeButtonText = mHelper.getContext().getString(android.R.string.cancel);
|
||||
}
|
||||
|
||||
return new PermissionRequest(
|
||||
mHelper,
|
||||
mPerms,
|
||||
mRequestCode,
|
||||
mRationale,
|
||||
mPositiveButtonText,
|
||||
mNegativeButtonText,
|
||||
mTheme);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,77 @@
|
||||
package pub.devrel.easypermissions;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.Dialog;
|
||||
import android.content.DialogInterface;
|
||||
import androidx.fragment.app.Fragment;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
import pub.devrel.easypermissions.helper.PermissionHelper;
|
||||
|
||||
/**
|
||||
* Click listener for either {@link RationaleDialogFragment} or {@link RationaleDialogFragmentCompat}.
|
||||
*/
|
||||
class RationaleDialogClickListener implements Dialog.OnClickListener {
|
||||
|
||||
private Object mHost;
|
||||
private RationaleDialogConfig mConfig;
|
||||
private EasyPermissions.PermissionCallbacks mCallbacks;
|
||||
private EasyPermissions.RationaleCallbacks mRationaleCallbacks;
|
||||
|
||||
RationaleDialogClickListener(RationaleDialogFragmentCompat compatDialogFragment,
|
||||
RationaleDialogConfig config,
|
||||
EasyPermissions.PermissionCallbacks callbacks,
|
||||
EasyPermissions.RationaleCallbacks rationaleCallbacks) {
|
||||
|
||||
mHost = compatDialogFragment.getParentFragment() != null
|
||||
? compatDialogFragment.getParentFragment()
|
||||
: compatDialogFragment.getActivity();
|
||||
|
||||
mConfig = config;
|
||||
mCallbacks = callbacks;
|
||||
mRationaleCallbacks = rationaleCallbacks;
|
||||
|
||||
}
|
||||
|
||||
RationaleDialogClickListener(RationaleDialogFragment dialogFragment,
|
||||
RationaleDialogConfig config,
|
||||
EasyPermissions.PermissionCallbacks callbacks,
|
||||
EasyPermissions.RationaleCallbacks dialogCallback) {
|
||||
|
||||
mHost = dialogFragment.getActivity();
|
||||
|
||||
mConfig = config;
|
||||
mCallbacks = callbacks;
|
||||
mRationaleCallbacks = dialogCallback;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
int requestCode = mConfig.requestCode;
|
||||
if (which == Dialog.BUTTON_POSITIVE) {
|
||||
String[] permissions = mConfig.permissions;
|
||||
if (mRationaleCallbacks != null) {
|
||||
mRationaleCallbacks.onRationaleAccepted(requestCode);
|
||||
}
|
||||
if (mHost instanceof Fragment) {
|
||||
PermissionHelper.newInstance((Fragment) mHost).directRequestPermissions(requestCode, permissions);
|
||||
} else if (mHost instanceof Activity) {
|
||||
PermissionHelper.newInstance((Activity) mHost).directRequestPermissions(requestCode, permissions);
|
||||
} else {
|
||||
throw new RuntimeException("Host must be an Activity or Fragment!");
|
||||
}
|
||||
} else {
|
||||
if (mRationaleCallbacks != null) {
|
||||
mRationaleCallbacks.onRationaleDenied(requestCode);
|
||||
}
|
||||
notifyPermissionDenied();
|
||||
}
|
||||
}
|
||||
|
||||
private void notifyPermissionDenied() {
|
||||
if (mCallbacks != null) {
|
||||
mCallbacks.onPermissionsDenied(mConfig.requestCode, Arrays.asList(mConfig.permissions));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,95 @@
|
||||
package pub.devrel.easypermissions;
|
||||
|
||||
import android.app.Dialog;
|
||||
import android.content.Context;
|
||||
import android.os.Bundle;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.StyleRes;
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
|
||||
/**
|
||||
* Configuration for either {@link RationaleDialogFragment} or {@link RationaleDialogFragmentCompat}.
|
||||
*/
|
||||
class RationaleDialogConfig {
|
||||
|
||||
private static final String KEY_POSITIVE_BUTTON = "positiveButton";
|
||||
private static final String KEY_NEGATIVE_BUTTON = "negativeButton";
|
||||
private static final String KEY_RATIONALE_MESSAGE = "rationaleMsg";
|
||||
private static final String KEY_THEME = "theme";
|
||||
private static final String KEY_REQUEST_CODE = "requestCode";
|
||||
private static final String KEY_PERMISSIONS = "permissions";
|
||||
|
||||
String positiveButton;
|
||||
String negativeButton;
|
||||
int theme;
|
||||
int requestCode;
|
||||
String rationaleMsg;
|
||||
String[] permissions;
|
||||
|
||||
RationaleDialogConfig(@NonNull String positiveButton,
|
||||
@NonNull String negativeButton,
|
||||
@NonNull String rationaleMsg,
|
||||
@StyleRes int theme,
|
||||
int requestCode,
|
||||
@NonNull String[] permissions) {
|
||||
|
||||
this.positiveButton = positiveButton;
|
||||
this.negativeButton = negativeButton;
|
||||
this.rationaleMsg = rationaleMsg;
|
||||
this.theme = theme;
|
||||
this.requestCode = requestCode;
|
||||
this.permissions = permissions;
|
||||
}
|
||||
|
||||
RationaleDialogConfig(Bundle bundle) {
|
||||
positiveButton = bundle.getString(KEY_POSITIVE_BUTTON);
|
||||
negativeButton = bundle.getString(KEY_NEGATIVE_BUTTON);
|
||||
rationaleMsg = bundle.getString(KEY_RATIONALE_MESSAGE);
|
||||
theme = bundle.getInt(KEY_THEME);
|
||||
requestCode = bundle.getInt(KEY_REQUEST_CODE);
|
||||
permissions = bundle.getStringArray(KEY_PERMISSIONS);
|
||||
}
|
||||
|
||||
Bundle toBundle() {
|
||||
Bundle bundle = new Bundle();
|
||||
bundle.putString(KEY_POSITIVE_BUTTON, positiveButton);
|
||||
bundle.putString(KEY_NEGATIVE_BUTTON, negativeButton);
|
||||
bundle.putString(KEY_RATIONALE_MESSAGE, rationaleMsg);
|
||||
bundle.putInt(KEY_THEME, theme);
|
||||
bundle.putInt(KEY_REQUEST_CODE, requestCode);
|
||||
bundle.putStringArray(KEY_PERMISSIONS, permissions);
|
||||
|
||||
return bundle;
|
||||
}
|
||||
|
||||
AlertDialog createSupportDialog(Context context, Dialog.OnClickListener listener) {
|
||||
AlertDialog.Builder builder;
|
||||
if (theme > 0) {
|
||||
builder = new AlertDialog.Builder(context, theme);
|
||||
} else {
|
||||
builder = new AlertDialog.Builder(context);
|
||||
}
|
||||
return builder
|
||||
.setCancelable(false)
|
||||
.setPositiveButton(positiveButton, listener)
|
||||
.setNegativeButton(negativeButton, listener)
|
||||
.setMessage(rationaleMsg)
|
||||
.create();
|
||||
}
|
||||
|
||||
android.app.AlertDialog createFrameworkDialog(Context context, Dialog.OnClickListener listener) {
|
||||
android.app.AlertDialog.Builder builder;
|
||||
if (theme > 0) {
|
||||
builder = new android.app.AlertDialog.Builder(context, theme);
|
||||
} else {
|
||||
builder = new android.app.AlertDialog.Builder(context);
|
||||
}
|
||||
return builder
|
||||
.setCancelable(false)
|
||||
.setPositiveButton(positiveButton, listener)
|
||||
.setNegativeButton(negativeButton, listener)
|
||||
.setMessage(rationaleMsg)
|
||||
.create();
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,113 @@
|
||||
package pub.devrel.easypermissions;
|
||||
|
||||
import android.app.Dialog;
|
||||
import android.app.DialogFragment;
|
||||
import android.app.FragmentManager;
|
||||
import android.content.Context;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.RestrictTo;
|
||||
import androidx.annotation.StyleRes;
|
||||
|
||||
/**
|
||||
* {@link DialogFragment} to display rationale for permission requests when the request comes from
|
||||
* a Fragment or Activity that can host a Fragment.
|
||||
*/
|
||||
@RestrictTo(RestrictTo.Scope.LIBRARY)
|
||||
public class RationaleDialogFragment extends DialogFragment {
|
||||
|
||||
public static final String TAG = "RationaleDialogFragment";
|
||||
|
||||
private EasyPermissions.PermissionCallbacks mPermissionCallbacks;
|
||||
private EasyPermissions.RationaleCallbacks mRationaleCallbacks;
|
||||
private boolean mStateSaved = false;
|
||||
|
||||
public static RationaleDialogFragment newInstance(
|
||||
@NonNull String positiveButton,
|
||||
@NonNull String negativeButton,
|
||||
@NonNull String rationaleMsg,
|
||||
@StyleRes int theme,
|
||||
int requestCode,
|
||||
@NonNull String[] permissions) {
|
||||
|
||||
// Create new Fragment
|
||||
RationaleDialogFragment dialogFragment = new RationaleDialogFragment();
|
||||
|
||||
// Initialize configuration as arguments
|
||||
RationaleDialogConfig config = new RationaleDialogConfig(
|
||||
positiveButton, negativeButton, rationaleMsg, theme, requestCode, permissions);
|
||||
dialogFragment.setArguments(config.toBundle());
|
||||
|
||||
return dialogFragment;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAttach(Context context) {
|
||||
super.onAttach(context);
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1 && getParentFragment() != null) {
|
||||
if (getParentFragment() instanceof EasyPermissions.PermissionCallbacks) {
|
||||
mPermissionCallbacks = (EasyPermissions.PermissionCallbacks) getParentFragment();
|
||||
}
|
||||
if (getParentFragment() instanceof EasyPermissions.RationaleCallbacks){
|
||||
mRationaleCallbacks = (EasyPermissions.RationaleCallbacks) getParentFragment();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (context instanceof EasyPermissions.PermissionCallbacks) {
|
||||
mPermissionCallbacks = (EasyPermissions.PermissionCallbacks) context;
|
||||
}
|
||||
|
||||
if (context instanceof EasyPermissions.RationaleCallbacks) {
|
||||
mRationaleCallbacks = (EasyPermissions.RationaleCallbacks) context;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSaveInstanceState(Bundle outState) {
|
||||
mStateSaved = true;
|
||||
super.onSaveInstanceState(outState);
|
||||
}
|
||||
|
||||
/**
|
||||
* Version of {@link #show(FragmentManager, String)} that no-ops when an IllegalStateException
|
||||
* would otherwise occur.
|
||||
*/
|
||||
public void showAllowingStateLoss(FragmentManager manager, String tag) {
|
||||
// API 26 added this convenient method
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
if (manager.isStateSaved()) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (mStateSaved) {
|
||||
return;
|
||||
}
|
||||
|
||||
show(manager, tag);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDetach() {
|
||||
super.onDetach();
|
||||
mPermissionCallbacks = null;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Dialog onCreateDialog(Bundle savedInstanceState) {
|
||||
// Rationale dialog should not be cancelable
|
||||
setCancelable(false);
|
||||
|
||||
// Get config from arguments, create click listener
|
||||
RationaleDialogConfig config = new RationaleDialogConfig(getArguments());
|
||||
RationaleDialogClickListener clickListener =
|
||||
new RationaleDialogClickListener(this, config, mPermissionCallbacks, mRationaleCallbacks);
|
||||
|
||||
// Create an AlertDialog
|
||||
return config.createFrameworkDialog(getActivity(), clickListener);
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,97 @@
|
||||
package pub.devrel.easypermissions;
|
||||
|
||||
import android.app.Dialog;
|
||||
import android.content.Context;
|
||||
import android.os.Bundle;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.RestrictTo;
|
||||
import androidx.annotation.StyleRes;
|
||||
import androidx.fragment.app.FragmentManager;
|
||||
import androidx.appcompat.app.AppCompatDialogFragment;
|
||||
|
||||
/**
|
||||
* {@link AppCompatDialogFragment} to display rationale for permission requests when the request
|
||||
* comes from a Fragment or Activity that can host a Fragment.
|
||||
*/
|
||||
@RestrictTo(RestrictTo.Scope.LIBRARY)
|
||||
public class RationaleDialogFragmentCompat extends AppCompatDialogFragment {
|
||||
|
||||
public static final String TAG = "RationaleDialogFragmentCompat";
|
||||
|
||||
private EasyPermissions.PermissionCallbacks mPermissionCallbacks;
|
||||
private EasyPermissions.RationaleCallbacks mRationaleCallbacks;
|
||||
|
||||
public static RationaleDialogFragmentCompat newInstance(
|
||||
@NonNull String rationaleMsg,
|
||||
@NonNull String positiveButton,
|
||||
@NonNull String negativeButton,
|
||||
@StyleRes int theme,
|
||||
int requestCode,
|
||||
@NonNull String[] permissions) {
|
||||
|
||||
// Create new Fragment
|
||||
RationaleDialogFragmentCompat dialogFragment = new RationaleDialogFragmentCompat();
|
||||
|
||||
// Initialize configuration as arguments
|
||||
RationaleDialogConfig config = new RationaleDialogConfig(
|
||||
positiveButton, negativeButton, rationaleMsg, theme, requestCode, permissions);
|
||||
dialogFragment.setArguments(config.toBundle());
|
||||
|
||||
return dialogFragment;
|
||||
}
|
||||
|
||||
/**
|
||||
* Version of {@link #show(FragmentManager, String)} that no-ops when an IllegalStateException
|
||||
* would otherwise occur.
|
||||
*/
|
||||
public void showAllowingStateLoss(FragmentManager manager, String tag) {
|
||||
if (manager.isStateSaved()) {
|
||||
return;
|
||||
}
|
||||
|
||||
show(manager, tag);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAttach(Context context) {
|
||||
super.onAttach(context);
|
||||
if (getParentFragment() != null) {
|
||||
if (getParentFragment() instanceof EasyPermissions.PermissionCallbacks) {
|
||||
mPermissionCallbacks = (EasyPermissions.PermissionCallbacks) getParentFragment();
|
||||
}
|
||||
if (getParentFragment() instanceof EasyPermissions.RationaleCallbacks){
|
||||
mRationaleCallbacks = (EasyPermissions.RationaleCallbacks) getParentFragment();
|
||||
}
|
||||
}
|
||||
|
||||
if (context instanceof EasyPermissions.PermissionCallbacks) {
|
||||
mPermissionCallbacks = (EasyPermissions.PermissionCallbacks) context;
|
||||
}
|
||||
|
||||
if (context instanceof EasyPermissions.RationaleCallbacks) {
|
||||
mRationaleCallbacks = (EasyPermissions.RationaleCallbacks) context;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDetach() {
|
||||
super.onDetach();
|
||||
mPermissionCallbacks = null;
|
||||
mRationaleCallbacks = null;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Dialog onCreateDialog(Bundle savedInstanceState) {
|
||||
// Rationale dialog should not be cancelable
|
||||
setCancelable(false);
|
||||
|
||||
// Get config from arguments, create click listener
|
||||
RationaleDialogConfig config = new RationaleDialogConfig(getArguments());
|
||||
RationaleDialogClickListener clickListener =
|
||||
new RationaleDialogClickListener(this, config, mPermissionCallbacks, mRationaleCallbacks);
|
||||
|
||||
// Create an AlertDialog
|
||||
return config.createSupportDialog(getContext(), clickListener);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,59 @@
|
||||
package pub.devrel.easypermissions.helper;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.Fragment;
|
||||
import android.app.FragmentManager;
|
||||
import android.content.Context;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.StyleRes;
|
||||
import androidx.core.app.ActivityCompat;
|
||||
import android.util.Log;
|
||||
|
||||
import pub.devrel.easypermissions.RationaleDialogFragment;
|
||||
|
||||
/**
|
||||
* Permissions helper for {@link Activity}.
|
||||
*/
|
||||
class ActivityPermissionHelper extends PermissionHelper<Activity> {
|
||||
private static final String TAG = "ActPermissionHelper";
|
||||
|
||||
public ActivityPermissionHelper(Activity host) {
|
||||
super(host);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void directRequestPermissions(int requestCode, @NonNull String... perms) {
|
||||
ActivityCompat.requestPermissions(getHost(), perms, requestCode);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldShowRequestPermissionRationale(@NonNull String perm) {
|
||||
return ActivityCompat.shouldShowRequestPermissionRationale(getHost(), perm);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Context getContext() {
|
||||
return getHost();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void showRequestPermissionRationale(@NonNull String rationale,
|
||||
@NonNull String positiveButton,
|
||||
@NonNull String negativeButton,
|
||||
@StyleRes int theme,
|
||||
int requestCode,
|
||||
@NonNull String... perms) {
|
||||
FragmentManager fm = getHost().getFragmentManager();
|
||||
|
||||
// Check if fragment is already showing
|
||||
Fragment fragment = fm.findFragmentByTag(RationaleDialogFragment.TAG);
|
||||
if (fragment instanceof RationaleDialogFragment) {
|
||||
Log.d(TAG, "Found existing fragment, not showing rationale.");
|
||||
return;
|
||||
}
|
||||
|
||||
RationaleDialogFragment
|
||||
.newInstance(positiveButton, negativeButton, rationale, theme, requestCode, perms)
|
||||
.showAllowingStateLoss(fm, RationaleDialogFragment.TAG);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,37 @@
|
||||
package pub.devrel.easypermissions.helper;
|
||||
|
||||
import android.content.Context;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.core.app.ActivityCompat;
|
||||
import androidx.fragment.app.FragmentManager;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
|
||||
/**
|
||||
* Permissions helper for {@link AppCompatActivity}.
|
||||
*/
|
||||
class AppCompatActivityPermissionsHelper extends BaseSupportPermissionsHelper<AppCompatActivity> {
|
||||
|
||||
public AppCompatActivityPermissionsHelper(AppCompatActivity host) {
|
||||
super(host);
|
||||
}
|
||||
|
||||
@Override
|
||||
public FragmentManager getSupportFragmentManager() {
|
||||
return getHost().getSupportFragmentManager();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void directRequestPermissions(int requestCode, @NonNull String... perms) {
|
||||
ActivityCompat.requestPermissions(getHost(), perms, requestCode);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldShowRequestPermissionRationale(@NonNull String perm) {
|
||||
return ActivityCompat.shouldShowRequestPermissionRationale(getHost(), perm);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Context getContext() {
|
||||
return getHost();
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,45 @@
|
||||
package pub.devrel.easypermissions.helper;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.StyleRes;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.fragment.app.FragmentManager;
|
||||
import android.util.Log;
|
||||
|
||||
import pub.devrel.easypermissions.RationaleDialogFragmentCompat;
|
||||
|
||||
/**
|
||||
* Implementation of {@link PermissionHelper} for Support Library host classes.
|
||||
*/
|
||||
public abstract class BaseSupportPermissionsHelper<T> extends PermissionHelper<T> {
|
||||
|
||||
private static final String TAG = "BSPermissionsHelper";
|
||||
|
||||
public BaseSupportPermissionsHelper(@NonNull T host) {
|
||||
super(host);
|
||||
}
|
||||
|
||||
public abstract FragmentManager getSupportFragmentManager();
|
||||
|
||||
@Override
|
||||
public void showRequestPermissionRationale(@NonNull String rationale,
|
||||
@NonNull String positiveButton,
|
||||
@NonNull String negativeButton,
|
||||
@StyleRes int theme,
|
||||
int requestCode,
|
||||
@NonNull String... perms) {
|
||||
|
||||
FragmentManager fm = getSupportFragmentManager();
|
||||
|
||||
// Check if fragment is already showing
|
||||
Fragment fragment = fm.findFragmentByTag(RationaleDialogFragmentCompat.TAG);
|
||||
if (fragment instanceof RationaleDialogFragmentCompat) {
|
||||
Log.d(TAG, "Found existing fragment, not showing rationale.");
|
||||
return;
|
||||
}
|
||||
|
||||
RationaleDialogFragmentCompat
|
||||
.newInstance(rationale, positiveButton, negativeButton, theme, requestCode, perms)
|
||||
.showAllowingStateLoss(fm, RationaleDialogFragmentCompat.TAG);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,47 @@
|
||||
package pub.devrel.easypermissions.helper;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.StyleRes;
|
||||
import androidx.fragment.app.Fragment;
|
||||
|
||||
/**
|
||||
* Permissions helper for apps built against API < 23, which do not need runtime permissions.
|
||||
*/
|
||||
class LowApiPermissionsHelper<T> extends PermissionHelper<T> {
|
||||
public LowApiPermissionsHelper(@NonNull T host) {
|
||||
super(host);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void directRequestPermissions(int requestCode, @NonNull String... perms) {
|
||||
throw new IllegalStateException("Should never be requesting permissions on API < 23!");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldShowRequestPermissionRationale(@NonNull String perm) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void showRequestPermissionRationale(@NonNull String rationale,
|
||||
@NonNull String positiveButton,
|
||||
@NonNull String negativeButton,
|
||||
@StyleRes int theme,
|
||||
int requestCode,
|
||||
@NonNull String... perms) {
|
||||
throw new IllegalStateException("Should never be requesting permissions on API < 23!");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Context getContext() {
|
||||
if (getHost() instanceof Activity) {
|
||||
return (Context) getHost();
|
||||
} else if (getHost() instanceof Fragment) {
|
||||
return ((Fragment) getHost()).getContext();
|
||||
} else {
|
||||
throw new IllegalStateException("Unknown host: " + getHost());
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,113 @@
|
||||
package pub.devrel.easypermissions.helper;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.os.Build;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.StyleRes;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Delegate class to make permission calls based on the 'host' (Fragment, Activity, etc).
|
||||
*/
|
||||
public abstract class PermissionHelper<T> {
|
||||
|
||||
private T mHost;
|
||||
|
||||
@NonNull
|
||||
public static PermissionHelper<? extends Activity> newInstance(Activity host) {
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
|
||||
return new LowApiPermissionsHelper<>(host);
|
||||
}
|
||||
|
||||
if (host instanceof AppCompatActivity)
|
||||
return new AppCompatActivityPermissionsHelper((AppCompatActivity) host);
|
||||
else {
|
||||
return new ActivityPermissionHelper(host);
|
||||
}
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public static PermissionHelper<Fragment> newInstance(Fragment host) {
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
|
||||
return new LowApiPermissionsHelper<>(host);
|
||||
}
|
||||
|
||||
return new SupportFragmentPermissionHelper(host);
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Public concrete methods
|
||||
// ============================================================================
|
||||
|
||||
public PermissionHelper(@NonNull T host) {
|
||||
mHost = host;
|
||||
}
|
||||
|
||||
private boolean shouldShowRationale(@NonNull String... perms) {
|
||||
for (String perm : perms) {
|
||||
if (shouldShowRequestPermissionRationale(perm)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public void requestPermissions(@NonNull String rationale,
|
||||
@NonNull String positiveButton,
|
||||
@NonNull String negativeButton,
|
||||
@StyleRes int theme,
|
||||
int requestCode,
|
||||
@NonNull String... perms) {
|
||||
if (shouldShowRationale(perms)) {
|
||||
showRequestPermissionRationale(
|
||||
rationale, positiveButton, negativeButton, theme, requestCode, perms);
|
||||
} else {
|
||||
directRequestPermissions(requestCode, perms);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean somePermissionPermanentlyDenied(@NonNull List<String> perms) {
|
||||
for (String deniedPermission : perms) {
|
||||
if (permissionPermanentlyDenied(deniedPermission)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean permissionPermanentlyDenied(@NonNull String perms) {
|
||||
return !shouldShowRequestPermissionRationale(perms);
|
||||
}
|
||||
|
||||
public boolean somePermissionDenied(@NonNull String... perms) {
|
||||
return shouldShowRationale(perms);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public T getHost() {
|
||||
return mHost;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Public abstract methods
|
||||
// ============================================================================
|
||||
|
||||
public abstract void directRequestPermissions(int requestCode, @NonNull String... perms);
|
||||
|
||||
public abstract boolean shouldShowRequestPermissionRationale(@NonNull String perm);
|
||||
|
||||
public abstract void showRequestPermissionRationale(@NonNull String rationale,
|
||||
@NonNull String positiveButton,
|
||||
@NonNull String negativeButton,
|
||||
@StyleRes int theme,
|
||||
int requestCode,
|
||||
@NonNull String... perms);
|
||||
|
||||
public abstract Context getContext();
|
||||
|
||||
}
|
||||
@ -0,0 +1,36 @@
|
||||
package pub.devrel.easypermissions.helper;
|
||||
|
||||
import android.content.Context;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.fragment.app.FragmentManager;
|
||||
|
||||
/**
|
||||
* Permissions helper for {@link Fragment} from the support library.
|
||||
*/
|
||||
class SupportFragmentPermissionHelper extends BaseSupportPermissionsHelper<Fragment> {
|
||||
|
||||
public SupportFragmentPermissionHelper(@NonNull Fragment host) {
|
||||
super(host);
|
||||
}
|
||||
|
||||
@Override
|
||||
public FragmentManager getSupportFragmentManager() {
|
||||
return getHost().getChildFragmentManager();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void directRequestPermissions(int requestCode, @NonNull String... perms) {
|
||||
getHost().requestPermissions(perms, requestCode);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldShowRequestPermissionRationale(@NonNull String perm) {
|
||||
return getHost().shouldShowRequestPermissionRationale(perm);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Context getContext() {
|
||||
return getHost().getActivity();
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,4 @@
|
||||
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
|
||||
package pub.devrel.easypermissions.helper;
|
||||
|
||||
import androidx.annotation.RestrictTo;
|
||||
@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<!-- Use system defaults for devs not using AppCompat -->
|
||||
<color name="colorPrimary">#ff212121</color>
|
||||
<color name="colorPrimaryDark">@android:color/black</color>
|
||||
<color name="colorAccent">#ff80cbc4</color>
|
||||
</resources>
|
||||
@ -0,0 +1,8 @@
|
||||
<resources>
|
||||
<string name="rationale_ask">This app may not work correctly without the requested permissions.</string>
|
||||
<string name="rationale_ask_again">
|
||||
This app may not work correctly without the requested permissions.
|
||||
Open the app settings screen to modify app permissions.
|
||||
</string>
|
||||
<string name="title_settings_dialog">Permissions Required</string>
|
||||
</resources>
|
||||
@ -0,0 +1,19 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
|
||||
<style name="EasyPermissions" parent="Theme.AppCompat.DayNight.DarkActionBar">
|
||||
<item name="colorPrimary">@color/colorPrimary</item>
|
||||
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
|
||||
<item name="colorAccent">@color/colorAccent</item>
|
||||
</style>
|
||||
|
||||
<style name="EasyPermissions.Transparent">
|
||||
<item name="android:windowIsTranslucent">true</item>
|
||||
<item name="android:windowBackground">@android:color/transparent</item>
|
||||
<item name="android:windowContentOverlay">@null</item>
|
||||
<item name="android:windowNoTitle">true</item>
|
||||
<item name="android:windowIsFloating">true</item>
|
||||
<item name="android:backgroundDimEnabled">false</item>
|
||||
</style>
|
||||
|
||||
</resources>
|
||||
@ -0,0 +1,186 @@
|
||||
package pub.devrel.easypermissions;
|
||||
|
||||
import android.app.Application;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.Intent;
|
||||
import android.widget.Button;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import org.mockito.Captor;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.Mockito;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
import org.robolectric.RobolectricTestRunner;
|
||||
import org.robolectric.annotation.Config;
|
||||
import org.robolectric.shadows.ShadowApplication;
|
||||
import org.robolectric.shadows.ShadowIntent;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
import androidx.test.core.app.ApplicationProvider;
|
||||
import pub.devrel.easypermissions.testhelper.ActivityController;
|
||||
import pub.devrel.easypermissions.testhelper.FragmentController;
|
||||
import pub.devrel.easypermissions.testhelper.TestActivity;
|
||||
import pub.devrel.easypermissions.testhelper.TestFragment;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyInt;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.robolectric.Shadows.shadowOf;
|
||||
import static pub.devrel.easypermissions.AppSettingsDialog.DEFAULT_SETTINGS_REQ_CODE;
|
||||
|
||||
@RunWith(RobolectricTestRunner.class)
|
||||
@Config(sdk = 23)
|
||||
public class AppSettingsDialogTest {
|
||||
|
||||
private static final String TITLE = "TITLE";
|
||||
private static final String RATIONALE = "RATIONALE";
|
||||
private static final String NEGATIVE = "NEGATIVE";
|
||||
private static final String POSITIVE = "POSITIVE";
|
||||
private ShadowApplication shadowApp;
|
||||
private TestActivity spyActivity;
|
||||
private TestFragment spyFragment;
|
||||
private FragmentController<TestFragment> fragmentController;
|
||||
private ActivityController<TestActivity> activityController;
|
||||
@Mock
|
||||
private DialogInterface.OnClickListener positiveListener;
|
||||
@Mock
|
||||
private DialogInterface.OnClickListener negativeListener;
|
||||
@Captor
|
||||
private ArgumentCaptor<Integer> integerCaptor;
|
||||
@Captor
|
||||
private ArgumentCaptor<Intent> intentCaptor;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
shadowApp = shadowOf((Application) ApplicationProvider.getApplicationContext());
|
||||
|
||||
activityController = new ActivityController<>(TestActivity.class);
|
||||
fragmentController = new FragmentController<>(TestFragment.class);
|
||||
|
||||
spyActivity = Mockito.spy(activityController.resume());
|
||||
spyFragment = Mockito.spy(fragmentController.resume());
|
||||
}
|
||||
|
||||
// ------ From Activity ------
|
||||
|
||||
@Test
|
||||
public void shouldShowExpectedSettingsDialog_whenBuildingFromActivity() {
|
||||
new AppSettingsDialog.Builder(spyActivity)
|
||||
.setTitle(android.R.string.dialog_alert_title)
|
||||
.setRationale(android.R.string.unknownName)
|
||||
.setPositiveButton(android.R.string.ok)
|
||||
.setNegativeButton(android.R.string.cancel)
|
||||
.setThemeResId(R.style.Theme_AppCompat)
|
||||
.build()
|
||||
.show();
|
||||
|
||||
verify(spyActivity, times(1))
|
||||
.startActivityForResult(intentCaptor.capture(), integerCaptor.capture());
|
||||
assertThat(integerCaptor.getValue()).isEqualTo(DEFAULT_SETTINGS_REQ_CODE);
|
||||
assertThat(Objects.requireNonNull(intentCaptor.getValue().getComponent()).getClassName())
|
||||
.isEqualTo(AppSettingsDialogHolderActivity.class.getName());
|
||||
|
||||
Intent startedIntent = shadowApp.getNextStartedActivity();
|
||||
ShadowIntent shadowIntent = shadowOf(startedIntent);
|
||||
assertThat(shadowIntent.getIntentClass()).isEqualTo(AppSettingsDialogHolderActivity.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldPositiveListener_whenClickingPositiveButtonFromActivity() {
|
||||
AlertDialog alertDialog = new AppSettingsDialog.Builder(spyActivity)
|
||||
.setTitle(TITLE)
|
||||
.setRationale(RATIONALE)
|
||||
.setPositiveButton(POSITIVE)
|
||||
.setNegativeButton(NEGATIVE)
|
||||
.setThemeResId(R.style.Theme_AppCompat)
|
||||
.build()
|
||||
.showDialog(positiveListener, negativeListener);
|
||||
Button positive = alertDialog.getButton(AlertDialog.BUTTON_POSITIVE);
|
||||
positive.performClick();
|
||||
|
||||
verify(positiveListener, times(1))
|
||||
.onClick(any(DialogInterface.class), anyInt());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldNegativeListener_whenClickingPositiveButtonFromActivity() {
|
||||
AlertDialog alertDialog = new AppSettingsDialog.Builder(spyActivity)
|
||||
.setTitle(TITLE)
|
||||
.setRationale(RATIONALE)
|
||||
.setPositiveButton(POSITIVE)
|
||||
.setNegativeButton(NEGATIVE)
|
||||
.setThemeResId(R.style.Theme_AppCompat)
|
||||
.build()
|
||||
.showDialog(positiveListener, negativeListener);
|
||||
Button positive = alertDialog.getButton(AlertDialog.BUTTON_NEGATIVE);
|
||||
positive.performClick();
|
||||
|
||||
verify(negativeListener, times(1))
|
||||
.onClick(any(DialogInterface.class), anyInt());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldShowExpectedSettingsDialog_whenBuildingFromSupportFragment() {
|
||||
new AppSettingsDialog.Builder(spyFragment)
|
||||
.setTitle(android.R.string.dialog_alert_title)
|
||||
.setRationale(android.R.string.unknownName)
|
||||
.setPositiveButton(android.R.string.ok)
|
||||
.setNegativeButton(android.R.string.cancel)
|
||||
.setThemeResId(R.style.Theme_AppCompat)
|
||||
.build()
|
||||
.show();
|
||||
|
||||
verify(spyFragment, times(1))
|
||||
.startActivityForResult(intentCaptor.capture(), integerCaptor.capture());
|
||||
assertThat(integerCaptor.getValue()).isEqualTo(DEFAULT_SETTINGS_REQ_CODE);
|
||||
assertThat(Objects.requireNonNull(intentCaptor.getValue().getComponent()).getClassName())
|
||||
.isEqualTo(AppSettingsDialogHolderActivity.class.getName());
|
||||
|
||||
Intent startedIntent = shadowApp.getNextStartedActivity();
|
||||
ShadowIntent shadowIntent = shadowOf(startedIntent);
|
||||
assertThat(shadowIntent.getIntentClass()).isEqualTo(AppSettingsDialogHolderActivity.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldPositiveListener_whenClickingPositiveButtonFromSupportFragment() {
|
||||
AlertDialog alertDialog = new AppSettingsDialog.Builder(spyFragment)
|
||||
.setTitle(TITLE)
|
||||
.setRationale(RATIONALE)
|
||||
.setPositiveButton(POSITIVE)
|
||||
.setNegativeButton(NEGATIVE)
|
||||
.setThemeResId(R.style.Theme_AppCompat)
|
||||
.build()
|
||||
.showDialog(positiveListener, negativeListener);
|
||||
Button positive = alertDialog.getButton(AlertDialog.BUTTON_POSITIVE);
|
||||
positive.performClick();
|
||||
|
||||
verify(positiveListener, times(1))
|
||||
.onClick(any(DialogInterface.class), anyInt());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldNegativeListener_whenClickingPositiveButtonFromSupportFragment() {
|
||||
AlertDialog alertDialog = new AppSettingsDialog.Builder(spyFragment)
|
||||
.setTitle(TITLE)
|
||||
.setRationale(RATIONALE)
|
||||
.setPositiveButton(POSITIVE)
|
||||
.setNegativeButton(NEGATIVE)
|
||||
.setThemeResId(R.style.Theme_AppCompat)
|
||||
.build()
|
||||
.showDialog(positiveListener, negativeListener);
|
||||
Button positive = alertDialog.getButton(AlertDialog.BUTTON_NEGATIVE);
|
||||
positive.performClick();
|
||||
|
||||
verify(negativeListener, times(1))
|
||||
.onClick(any(DialogInterface.class), anyInt());
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,121 @@
|
||||
package pub.devrel.easypermissions;
|
||||
|
||||
import android.Manifest;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import org.mockito.Captor;
|
||||
import org.mockito.Mockito;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
import org.robolectric.RobolectricTestRunner;
|
||||
import org.robolectric.annotation.Config;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
import androidx.test.core.app.ApplicationProvider;
|
||||
import pub.devrel.easypermissions.testhelper.ActivityController;
|
||||
import pub.devrel.easypermissions.testhelper.FragmentController;
|
||||
import pub.devrel.easypermissions.testhelper.TestActivity;
|
||||
import pub.devrel.easypermissions.testhelper.TestAppCompatActivity;
|
||||
import pub.devrel.easypermissions.testhelper.TestFragment;
|
||||
import pub.devrel.easypermissions.testhelper.TestSupportFragmentActivity;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
|
||||
/**
|
||||
* Low-API (SDK = 19) tests for {@link pub.devrel.easypermissions.EasyPermissions}.
|
||||
*/
|
||||
@RunWith(RobolectricTestRunner.class)
|
||||
@Config(sdk = 19)
|
||||
public class EasyPermissionsLowApiTest {
|
||||
|
||||
private static final String RATIONALE = "RATIONALE";
|
||||
private static final String[] ALL_PERMS = new String[]{
|
||||
Manifest.permission.READ_SMS, Manifest.permission.ACCESS_FINE_LOCATION};
|
||||
|
||||
private TestActivity spyActivity;
|
||||
private TestSupportFragmentActivity spySupportFragmentActivity;
|
||||
private TestAppCompatActivity spyAppCompatActivity;
|
||||
private TestFragment spyFragment;
|
||||
private FragmentController<TestFragment> fragmentController;
|
||||
private ActivityController<TestActivity> activityController;
|
||||
private ActivityController<TestSupportFragmentActivity> supportFragmentActivityController;
|
||||
private ActivityController<TestAppCompatActivity> appCompatActivityController;
|
||||
@Captor
|
||||
private ArgumentCaptor<Integer> integerCaptor;
|
||||
@Captor
|
||||
private ArgumentCaptor<ArrayList<String>> listCaptor;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
|
||||
activityController = new ActivityController<>(TestActivity.class);
|
||||
supportFragmentActivityController = new ActivityController<>(TestSupportFragmentActivity.class);
|
||||
appCompatActivityController = new ActivityController<>(TestAppCompatActivity.class);
|
||||
fragmentController = new FragmentController<>(TestFragment.class);
|
||||
|
||||
spyActivity = Mockito.spy(activityController.resume());
|
||||
spySupportFragmentActivity = Mockito.spy(supportFragmentActivityController.resume());
|
||||
spyAppCompatActivity = Mockito.spy(appCompatActivityController.resume());
|
||||
spyFragment = Mockito.spy(fragmentController.resume());
|
||||
}
|
||||
|
||||
// ------ General tests ------
|
||||
|
||||
@Test
|
||||
public void shouldHavePermission_whenHasPermissionsBeforeMarshmallow() {
|
||||
assertThat(EasyPermissions.hasPermissions(ApplicationProvider.getApplicationContext(),
|
||||
Manifest.permission.ACCESS_COARSE_LOCATION)).isTrue();
|
||||
}
|
||||
|
||||
// ------ From Activity ------
|
||||
|
||||
@Test
|
||||
public void shouldCallbackOnPermissionGranted_whenRequestFromActivity() {
|
||||
EasyPermissions.requestPermissions(spyActivity, RATIONALE, TestActivity.REQUEST_CODE, ALL_PERMS);
|
||||
|
||||
verify(spyActivity, times(1))
|
||||
.onPermissionsGranted(integerCaptor.capture(), listCaptor.capture());
|
||||
assertThat(integerCaptor.getValue()).isEqualTo(TestActivity.REQUEST_CODE);
|
||||
assertThat(listCaptor.getValue()).containsAllIn(ALL_PERMS);
|
||||
}
|
||||
|
||||
// ------ From Support Activity ------
|
||||
|
||||
@Test
|
||||
public void shouldCallbackOnPermissionGranted_whenRequestFromSupportFragmentActivity() {
|
||||
EasyPermissions.requestPermissions(spySupportFragmentActivity, RATIONALE, TestSupportFragmentActivity.REQUEST_CODE, ALL_PERMS);
|
||||
|
||||
verify(spySupportFragmentActivity, times(1))
|
||||
.onPermissionsGranted(integerCaptor.capture(), listCaptor.capture());
|
||||
assertThat(integerCaptor.getValue()).isEqualTo(TestSupportFragmentActivity.REQUEST_CODE);
|
||||
assertThat(listCaptor.getValue()).containsAllIn(ALL_PERMS);
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void shouldCallbackOnPermissionGranted_whenRequestFromAppCompatActivity() {
|
||||
EasyPermissions.requestPermissions(spyAppCompatActivity, RATIONALE, TestAppCompatActivity.REQUEST_CODE, ALL_PERMS);
|
||||
|
||||
verify(spyAppCompatActivity, times(1))
|
||||
.onPermissionsGranted(integerCaptor.capture(), listCaptor.capture());
|
||||
assertThat(integerCaptor.getValue()).isEqualTo(TestAppCompatActivity.REQUEST_CODE);
|
||||
assertThat(listCaptor.getValue()).containsAllIn(ALL_PERMS);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldCallbackOnPermissionGranted_whenRequestFromFragment() {
|
||||
EasyPermissions.requestPermissions(spyFragment, RATIONALE, TestFragment.REQUEST_CODE, ALL_PERMS);
|
||||
|
||||
verify(spyFragment, times(1))
|
||||
.onPermissionsGranted(integerCaptor.capture(), listCaptor.capture());
|
||||
assertThat(integerCaptor.getValue()).isEqualTo(TestFragment.REQUEST_CODE);
|
||||
assertThat(listCaptor.getValue()).containsAllIn(ALL_PERMS);
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,611 @@
|
||||
package pub.devrel.easypermissions;
|
||||
|
||||
import android.Manifest;
|
||||
import android.app.Application;
|
||||
import android.app.Dialog;
|
||||
import android.app.Fragment;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.widget.TextView;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import org.mockito.Captor;
|
||||
import org.mockito.Mockito;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
import org.robolectric.RobolectricTestRunner;
|
||||
import org.robolectric.annotation.Config;
|
||||
import org.robolectric.shadows.ShadowApplication;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
|
||||
import androidx.test.core.app.ApplicationProvider;
|
||||
import pub.devrel.easypermissions.testhelper.ActivityController;
|
||||
import pub.devrel.easypermissions.testhelper.FragmentController;
|
||||
import pub.devrel.easypermissions.testhelper.TestActivity;
|
||||
import pub.devrel.easypermissions.testhelper.TestAppCompatActivity;
|
||||
import pub.devrel.easypermissions.testhelper.TestFragment;
|
||||
import pub.devrel.easypermissions.testhelper.TestSupportFragmentActivity;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static junit.framework.Assert.fail;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyInt;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
import static org.robolectric.Shadows.shadowOf;
|
||||
|
||||
/**
|
||||
* Basic Robolectric tests for {@link pub.devrel.easypermissions.EasyPermissions}.
|
||||
*/
|
||||
@RunWith(RobolectricTestRunner.class)
|
||||
@Config(sdk = 23)
|
||||
public class EasyPermissionsTest {
|
||||
|
||||
private static final String RATIONALE = "RATIONALE";
|
||||
private static final String POSITIVE = "POSITIVE";
|
||||
private static final String NEGATIVE = "NEGATIVE";
|
||||
private static final String[] ONE_PERM = new String[]{Manifest.permission.READ_SMS};
|
||||
private static final String[] ALL_PERMS = new String[]{
|
||||
Manifest.permission.READ_SMS, Manifest.permission.ACCESS_FINE_LOCATION};
|
||||
private static final int[] SMS_DENIED_RESULT = new int[]{
|
||||
PackageManager.PERMISSION_DENIED, PackageManager.PERMISSION_GRANTED};
|
||||
|
||||
private ShadowApplication shadowApp;
|
||||
private Application app;
|
||||
private TestActivity spyActivity;
|
||||
private TestSupportFragmentActivity spySupportFragmentActivity;
|
||||
private TestAppCompatActivity spyAppCompatActivity;
|
||||
private TestFragment spyFragment;
|
||||
private FragmentController<TestFragment> fragmentController;
|
||||
private ActivityController<TestActivity> activityController;
|
||||
private ActivityController<TestSupportFragmentActivity> supportFragmentActivityController;
|
||||
private ActivityController<TestAppCompatActivity> appCompatActivityController;
|
||||
@Captor
|
||||
private ArgumentCaptor<Integer> integerCaptor;
|
||||
@Captor
|
||||
private ArgumentCaptor<ArrayList<String>> listCaptor;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
app = ApplicationProvider.getApplicationContext();
|
||||
shadowApp = shadowOf(app);
|
||||
|
||||
activityController = new ActivityController<>(TestActivity.class);
|
||||
supportFragmentActivityController = new ActivityController<>(TestSupportFragmentActivity.class);
|
||||
appCompatActivityController = new ActivityController<>(TestAppCompatActivity.class);
|
||||
fragmentController = new FragmentController<>(TestFragment.class);
|
||||
|
||||
spyActivity = Mockito.spy(activityController.resume());
|
||||
spySupportFragmentActivity = Mockito.spy(supportFragmentActivityController.resume());
|
||||
spyAppCompatActivity = Mockito.spy(appCompatActivityController.resume());
|
||||
spyFragment = Mockito.spy(fragmentController.resume());
|
||||
}
|
||||
|
||||
// ------ General tests ------
|
||||
|
||||
@Test
|
||||
public void shouldNotHavePermissions_whenNoPermissionsGranted() {
|
||||
assertThat(EasyPermissions.hasPermissions(app, ALL_PERMS)).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldNotHavePermissions_whenNotAllPermissionsGranted() {
|
||||
shadowApp.grantPermissions(ONE_PERM);
|
||||
assertThat(EasyPermissions.hasPermissions(app, ALL_PERMS)).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldHavePermissions_whenAllPermissionsGranted() {
|
||||
shadowApp.grantPermissions(ALL_PERMS);
|
||||
assertThat(EasyPermissions.hasPermissions(app, ALL_PERMS)).isTrue();
|
||||
}
|
||||
|
||||
@SuppressWarnings("ConstantConditions")
|
||||
@Test
|
||||
public void shouldThrowException_whenHasPermissionsWithNullContext() {
|
||||
try {
|
||||
EasyPermissions.hasPermissions(null, ALL_PERMS);
|
||||
fail("IllegalStateException expected because of null context.");
|
||||
} catch (IllegalArgumentException e) {
|
||||
assertThat(e).hasMessageThat()
|
||||
.isEqualTo("Can't check permissions for null context");
|
||||
}
|
||||
}
|
||||
|
||||
// ------ From Activity ------
|
||||
|
||||
@Test
|
||||
public void shouldCorrectlyCallback_whenOnRequestPermissionResultCalledFromActivity() {
|
||||
EasyPermissions.onRequestPermissionsResult(TestActivity.REQUEST_CODE, ALL_PERMS, SMS_DENIED_RESULT, spyActivity);
|
||||
|
||||
verify(spyActivity, times(1))
|
||||
.onPermissionsGranted(integerCaptor.capture(), listCaptor.capture());
|
||||
assertThat(integerCaptor.getValue()).isEqualTo(TestActivity.REQUEST_CODE);
|
||||
assertThat(listCaptor.getValue())
|
||||
.containsAllIn(new ArrayList<>(Collections.singletonList(Manifest.permission.ACCESS_FINE_LOCATION)));
|
||||
|
||||
verify(spyActivity, times(1))
|
||||
.onPermissionsDenied(integerCaptor.capture(), listCaptor.capture());
|
||||
assertThat(integerCaptor.getValue()).isEqualTo(TestActivity.REQUEST_CODE);
|
||||
assertThat(listCaptor.getValue())
|
||||
.containsAllIn(new ArrayList<>(Collections.singletonList(Manifest.permission.READ_SMS)));
|
||||
|
||||
verify(spyActivity, never()).afterPermissionGranted();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldCallbackOnPermissionGranted_whenRequestAlreadyGrantedPermissionsFromActivity() {
|
||||
grantPermissions(ALL_PERMS);
|
||||
|
||||
EasyPermissions.requestPermissions(spyActivity, RATIONALE, TestActivity.REQUEST_CODE, ALL_PERMS);
|
||||
|
||||
verify(spyActivity, times(1))
|
||||
.onPermissionsGranted(integerCaptor.capture(), listCaptor.capture());
|
||||
verify(spyActivity, never()).requestPermissions(any(String[].class), anyInt());
|
||||
assertThat(integerCaptor.getValue()).isEqualTo(TestActivity.REQUEST_CODE);
|
||||
assertThat(listCaptor.getValue()).containsAllIn(ALL_PERMS);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldCallbackAfterPermissionGranted_whenRequestAlreadyGrantedPermissionsFromActivity() {
|
||||
grantPermissions(ALL_PERMS);
|
||||
|
||||
EasyPermissions.requestPermissions(spyActivity, RATIONALE, TestActivity.REQUEST_CODE, ALL_PERMS);
|
||||
|
||||
// Called 2 times because this is a spy and library implementation invokes super classes annotated methods as well
|
||||
verify(spyActivity, times(2)).afterPermissionGranted();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldNotCallbackAfterPermissionGranted_whenRequestNotGrantedPermissionsFromActivity() {
|
||||
grantPermissions(ONE_PERM);
|
||||
|
||||
EasyPermissions.requestPermissions(spyActivity, RATIONALE, TestActivity.REQUEST_CODE, ALL_PERMS);
|
||||
|
||||
verify(spyActivity, never()).afterPermissionGranted();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldRequestPermissions_whenMissingPermissionAndNotShowRationaleFromActivity() {
|
||||
grantPermissions(ONE_PERM);
|
||||
showRationale(false, ALL_PERMS);
|
||||
|
||||
EasyPermissions.requestPermissions(spyActivity, RATIONALE, TestActivity.REQUEST_CODE, ALL_PERMS);
|
||||
|
||||
verify(spyActivity, times(1))
|
||||
.requestPermissions(ALL_PERMS, TestActivity.REQUEST_CODE);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldShowCorrectDialog_whenMissingPermissionsAndShowRationaleFromActivity() {
|
||||
grantPermissions(ONE_PERM);
|
||||
showRationale(true, ALL_PERMS);
|
||||
|
||||
EasyPermissions.requestPermissions(spyActivity, RATIONALE, TestActivity.REQUEST_CODE, ALL_PERMS);
|
||||
|
||||
Fragment dialogFragment = spyActivity.getFragmentManager()
|
||||
.findFragmentByTag(RationaleDialogFragment.TAG);
|
||||
assertThat(dialogFragment).isInstanceOf(RationaleDialogFragment.class);
|
||||
|
||||
Dialog dialog = ((RationaleDialogFragment) dialogFragment).getDialog();
|
||||
assertThatHasExpectedRationale(dialog, RATIONALE);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldShowCorrectDialogUsingRequest_whenMissingPermissionsAndShowRationaleFromActivity() {
|
||||
grantPermissions(ONE_PERM);
|
||||
showRationale(true, ALL_PERMS);
|
||||
|
||||
PermissionRequest request = new PermissionRequest.Builder(spyActivity, TestActivity.REQUEST_CODE, ALL_PERMS)
|
||||
.setPositiveButtonText(android.R.string.ok)
|
||||
.setNegativeButtonText(android.R.string.cancel)
|
||||
.setRationale(android.R.string.unknownName)
|
||||
.setTheme(R.style.Theme_AppCompat)
|
||||
.build();
|
||||
EasyPermissions.requestPermissions(request);
|
||||
|
||||
Fragment dialogFragment = spyActivity.getFragmentManager()
|
||||
.findFragmentByTag(RationaleDialogFragment.TAG);
|
||||
assertThat(dialogFragment).isInstanceOf(RationaleDialogFragment.class);
|
||||
|
||||
Dialog dialog = ((RationaleDialogFragment) dialogFragment).getDialog();
|
||||
assertThatHasExpectedButtonsAndRationale(dialog, android.R.string.unknownName,
|
||||
android.R.string.ok, android.R.string.cancel);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldHaveSomePermissionDenied_whenShowRationaleFromActivity() {
|
||||
showRationale(true, ALL_PERMS);
|
||||
|
||||
assertThat(EasyPermissions.somePermissionDenied(spyActivity, ALL_PERMS)).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldNotHaveSomePermissionDenied_whenNotShowRationaleFromActivity() {
|
||||
showRationale(false, ALL_PERMS);
|
||||
|
||||
assertThat(EasyPermissions.somePermissionDenied(spyActivity, ALL_PERMS)).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldHaveSomePermissionPermanentlyDenied_whenNotShowRationaleFromActivity() {
|
||||
showRationale(false, ALL_PERMS);
|
||||
|
||||
assertThat(EasyPermissions.somePermissionPermanentlyDenied(spyActivity, Arrays.asList(ALL_PERMS))).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldNotHaveSomePermissionPermanentlyDenied_whenShowRationaleFromActivity() {
|
||||
showRationale(true, ALL_PERMS);
|
||||
|
||||
assertThat(EasyPermissions.somePermissionPermanentlyDenied(spyActivity, Arrays.asList(ALL_PERMS))).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldHavePermissionPermanentlyDenied_whenNotShowRationaleFromActivity() {
|
||||
showRationale(false, Manifest.permission.READ_SMS);
|
||||
|
||||
assertThat(EasyPermissions.permissionPermanentlyDenied(spyActivity, Manifest.permission.READ_SMS)).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldNotHavePermissionPermanentlyDenied_whenShowRationaleFromActivity() {
|
||||
showRationale(true, Manifest.permission.READ_SMS);
|
||||
|
||||
assertThat(EasyPermissions.permissionPermanentlyDenied(spyActivity, Manifest.permission.READ_SMS)).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldCorrectlyCallback_whenOnRequestPermissionResultCalledFromAppCompatActivity() {
|
||||
EasyPermissions.onRequestPermissionsResult(TestAppCompatActivity.REQUEST_CODE, ALL_PERMS, SMS_DENIED_RESULT, spyAppCompatActivity);
|
||||
|
||||
verify(spyAppCompatActivity, times(1))
|
||||
.onPermissionsGranted(integerCaptor.capture(), listCaptor.capture());
|
||||
assertThat(integerCaptor.getValue()).isEqualTo(TestAppCompatActivity.REQUEST_CODE);
|
||||
assertThat(listCaptor.getValue())
|
||||
.containsAllIn(new ArrayList<>(Collections.singletonList(Manifest.permission.ACCESS_FINE_LOCATION)));
|
||||
|
||||
verify(spyAppCompatActivity, times(1))
|
||||
.onPermissionsDenied(integerCaptor.capture(), listCaptor.capture());
|
||||
assertThat(integerCaptor.getValue()).isEqualTo(TestAppCompatActivity.REQUEST_CODE);
|
||||
assertThat(listCaptor.getValue())
|
||||
.containsAllIn(new ArrayList<>(Collections.singletonList(Manifest.permission.READ_SMS)));
|
||||
|
||||
verify(spyAppCompatActivity, never()).afterPermissionGranted();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldCallbackOnPermissionGranted_whenRequestAlreadyGrantedPermissionsFromAppCompatActivity() {
|
||||
grantPermissions(ALL_PERMS);
|
||||
|
||||
EasyPermissions.requestPermissions(spyAppCompatActivity, RATIONALE, TestAppCompatActivity.REQUEST_CODE, ALL_PERMS);
|
||||
|
||||
verify(spyAppCompatActivity, times(1))
|
||||
.onPermissionsGranted(integerCaptor.capture(), listCaptor.capture());
|
||||
verify(spyAppCompatActivity, never()).requestPermissions(any(String[].class), anyInt());
|
||||
assertThat(integerCaptor.getValue()).isEqualTo(TestAppCompatActivity.REQUEST_CODE);
|
||||
assertThat(listCaptor.getValue()).containsAllIn(ALL_PERMS);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldCallbackAfterPermissionGranted_whenRequestAlreadyGrantedPermissionsFromAppCompatActivity() {
|
||||
grantPermissions(ALL_PERMS);
|
||||
|
||||
EasyPermissions.requestPermissions(spyAppCompatActivity, RATIONALE, TestAppCompatActivity.REQUEST_CODE, ALL_PERMS);
|
||||
|
||||
// Called 2 times because this is a spy and library implementation invokes super classes annotated methods as well
|
||||
verify(spyAppCompatActivity, times(2)).afterPermissionGranted();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldNotCallbackAfterPermissionGranted_whenRequestNotGrantedPermissionsFromAppCompatActivity() {
|
||||
grantPermissions(ONE_PERM);
|
||||
|
||||
EasyPermissions.requestPermissions(spyAppCompatActivity, RATIONALE, TestAppCompatActivity.REQUEST_CODE, ALL_PERMS);
|
||||
|
||||
verify(spyAppCompatActivity, never()).afterPermissionGranted();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldRequestPermissions_whenMissingPermissionAndNotShowRationaleFromAppCompatActivity() {
|
||||
grantPermissions(ONE_PERM);
|
||||
showRationale(false, ALL_PERMS);
|
||||
|
||||
EasyPermissions.requestPermissions(spyAppCompatActivity, RATIONALE, TestAppCompatActivity.REQUEST_CODE, ALL_PERMS);
|
||||
|
||||
verify(spyAppCompatActivity, times(1))
|
||||
.requestPermissions(ALL_PERMS, TestAppCompatActivity.REQUEST_CODE);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldShowCorrectDialog_whenMissingPermissionsAndShowRationaleFromAppCompatActivity() {
|
||||
grantPermissions(ONE_PERM);
|
||||
showRationale(true, ALL_PERMS);
|
||||
|
||||
EasyPermissions.requestPermissions(spyAppCompatActivity, RATIONALE, TestAppCompatActivity.REQUEST_CODE, ALL_PERMS);
|
||||
|
||||
androidx.fragment.app.Fragment dialogFragment = spyAppCompatActivity.getSupportFragmentManager()
|
||||
.findFragmentByTag(RationaleDialogFragmentCompat.TAG);
|
||||
assertThat(dialogFragment).isInstanceOf(RationaleDialogFragmentCompat.class);
|
||||
|
||||
Dialog dialog = ((RationaleDialogFragmentCompat) dialogFragment).getDialog();
|
||||
assertThatHasExpectedRationale(dialog, RATIONALE);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldShowCorrectDialog_whenMissingPermissionsAndShowRationaleFromSupportFragmentActivity() {
|
||||
grantPermissions(ONE_PERM);
|
||||
showRationale(true, ALL_PERMS);
|
||||
|
||||
EasyPermissions.requestPermissions(spySupportFragmentActivity, RATIONALE, TestSupportFragmentActivity.REQUEST_CODE, ALL_PERMS);
|
||||
|
||||
Fragment dialogFragment = spySupportFragmentActivity.getFragmentManager()
|
||||
.findFragmentByTag(RationaleDialogFragment.TAG);
|
||||
assertThat(dialogFragment).isInstanceOf(RationaleDialogFragment.class);
|
||||
|
||||
Dialog dialog = ((RationaleDialogFragment) dialogFragment).getDialog();
|
||||
assertThatHasExpectedRationale(dialog, RATIONALE);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldShowCorrectDialogUsingRequest_whenMissingPermissionsAndShowRationaleFromAppCompatActivity() {
|
||||
grantPermissions(ONE_PERM);
|
||||
showRationale(true, ALL_PERMS);
|
||||
|
||||
PermissionRequest request = new PermissionRequest.Builder(spyAppCompatActivity, TestAppCompatActivity.REQUEST_CODE, ALL_PERMS)
|
||||
.setPositiveButtonText(android.R.string.ok)
|
||||
.setNegativeButtonText(android.R.string.cancel)
|
||||
.setRationale(android.R.string.unknownName)
|
||||
.setTheme(R.style.Theme_AppCompat)
|
||||
.build();
|
||||
EasyPermissions.requestPermissions(request);
|
||||
|
||||
androidx.fragment.app.Fragment dialogFragment = spyAppCompatActivity.getSupportFragmentManager()
|
||||
.findFragmentByTag(RationaleDialogFragmentCompat.TAG);
|
||||
assertThat(dialogFragment).isInstanceOf(RationaleDialogFragmentCompat.class);
|
||||
|
||||
Dialog dialog = ((RationaleDialogFragmentCompat) dialogFragment).getDialog();
|
||||
assertThatHasExpectedButtonsAndRationale(dialog, android.R.string.unknownName,
|
||||
android.R.string.ok, android.R.string.cancel);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldHaveSomePermissionDenied_whenShowRationaleFromAppCompatActivity() {
|
||||
showRationale(true, ALL_PERMS);
|
||||
|
||||
assertThat(EasyPermissions.somePermissionDenied(spyAppCompatActivity, ALL_PERMS)).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldNotHaveSomePermissionDenied_whenNotShowRationaleFromAppCompatActivity() {
|
||||
showRationale(false, ALL_PERMS);
|
||||
|
||||
assertThat(EasyPermissions.somePermissionDenied(spyAppCompatActivity, ALL_PERMS)).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldHaveSomePermissionPermanentlyDenied_whenNotShowRationaleFromAppCompatActivity() {
|
||||
showRationale(false, ALL_PERMS);
|
||||
|
||||
assertThat(EasyPermissions.somePermissionPermanentlyDenied(spyAppCompatActivity, Arrays.asList(ALL_PERMS))).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldNotHaveSomePermissionPermanentlyDenied_whenShowRationaleFromAppCompatActivity() {
|
||||
showRationale(true, ALL_PERMS);
|
||||
|
||||
assertThat(EasyPermissions.somePermissionPermanentlyDenied(spyAppCompatActivity, Arrays.asList(ALL_PERMS))).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldHavePermissionPermanentlyDenied_whenNotShowRationaleFromAppCompatActivity() {
|
||||
showRationale(false, Manifest.permission.READ_SMS);
|
||||
|
||||
assertThat(EasyPermissions.permissionPermanentlyDenied(spyAppCompatActivity, Manifest.permission.READ_SMS)).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldNotHavePermissionPermanentlyDenied_whenShowRationaleFromAppCompatActivity() {
|
||||
showRationale(true, Manifest.permission.READ_SMS);
|
||||
|
||||
assertThat(EasyPermissions.permissionPermanentlyDenied(spyAppCompatActivity, Manifest.permission.READ_SMS)).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldCorrectlyCallback_whenOnRequestPermissionResultCalledFromFragment() {
|
||||
EasyPermissions.onRequestPermissionsResult(TestFragment.REQUEST_CODE, ALL_PERMS, SMS_DENIED_RESULT,
|
||||
spyFragment);
|
||||
|
||||
verify(spyFragment, times(1))
|
||||
.onPermissionsGranted(integerCaptor.capture(), listCaptor.capture());
|
||||
assertThat(integerCaptor.getValue()).isEqualTo(TestFragment.REQUEST_CODE);
|
||||
assertThat(listCaptor.getValue())
|
||||
.containsAllIn(new ArrayList<>(Collections.singletonList(Manifest.permission.ACCESS_FINE_LOCATION)));
|
||||
|
||||
verify(spyFragment, times(1))
|
||||
.onPermissionsDenied(integerCaptor.capture(), listCaptor.capture());
|
||||
assertThat(integerCaptor.getValue()).isEqualTo(TestFragment.REQUEST_CODE);
|
||||
assertThat(listCaptor.getValue())
|
||||
.containsAllIn(new ArrayList<>(Collections.singletonList(Manifest.permission.READ_SMS)));
|
||||
|
||||
verify(spyFragment, never()).afterPermissionGranted();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldCallbackOnPermissionGranted_whenRequestAlreadyGrantedPermissionsFromFragment() {
|
||||
grantPermissions(ALL_PERMS);
|
||||
|
||||
EasyPermissions.requestPermissions(spyFragment, RATIONALE,
|
||||
TestFragment.REQUEST_CODE, ALL_PERMS);
|
||||
|
||||
verify(spyFragment, times(1))
|
||||
.onPermissionsGranted(integerCaptor.capture(), listCaptor.capture());
|
||||
verify(spyFragment, never()).requestPermissions(any(String[].class), anyInt());
|
||||
assertThat(integerCaptor.getValue()).isEqualTo(TestFragment.REQUEST_CODE);
|
||||
assertThat(listCaptor.getValue()).containsAllIn(ALL_PERMS);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldCallbackAfterPermissionGranted_whenRequestAlreadyGrantedPermissionsFragment() {
|
||||
grantPermissions(ALL_PERMS);
|
||||
|
||||
EasyPermissions.requestPermissions(spyFragment, RATIONALE, TestFragment.REQUEST_CODE, ALL_PERMS);
|
||||
|
||||
// Called 2 times because this is a spy and library implementation invokes super classes annotated methods as well
|
||||
verify(spyFragment, times(2)).afterPermissionGranted();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldNotCallbackAfterPermissionGranted_whenRequestNotGrantedPermissionsFromFragment() {
|
||||
grantPermissions(ONE_PERM);
|
||||
|
||||
EasyPermissions.requestPermissions(spyFragment, RATIONALE, TestFragment.REQUEST_CODE, ALL_PERMS);
|
||||
|
||||
verify(spyFragment, never()).afterPermissionGranted();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldRequestPermissions_whenMissingPermissionsAndNotShowRationaleFromFragment() {
|
||||
grantPermissions(ONE_PERM);
|
||||
showRationale(false, ALL_PERMS);
|
||||
|
||||
EasyPermissions.requestPermissions(spyFragment, RATIONALE, TestFragment.REQUEST_CODE, ALL_PERMS);
|
||||
|
||||
verify(spyFragment, times(1))
|
||||
.requestPermissions(ALL_PERMS, TestFragment.REQUEST_CODE);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldShowCorrectDialog_whenMissingPermissionsAndShowRationaleFromFragment() {
|
||||
grantPermissions(ONE_PERM);
|
||||
showRationale(true, ALL_PERMS);
|
||||
|
||||
EasyPermissions.requestPermissions(spyFragment, RATIONALE, TestFragment.REQUEST_CODE, ALL_PERMS);
|
||||
|
||||
androidx.fragment.app.Fragment dialogFragment = spyFragment.getChildFragmentManager()
|
||||
.findFragmentByTag(RationaleDialogFragmentCompat.TAG);
|
||||
assertThat(dialogFragment).isInstanceOf(RationaleDialogFragmentCompat.class);
|
||||
|
||||
Dialog dialog = ((RationaleDialogFragmentCompat) dialogFragment).getDialog();
|
||||
assertThatHasExpectedRationale(dialog, RATIONALE);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldShowCorrectDialogUsingRequest_whenMissingPermissionsAndShowRationaleFromFragment() {
|
||||
grantPermissions(ONE_PERM);
|
||||
showRationale(true, ALL_PERMS);
|
||||
|
||||
PermissionRequest request = new PermissionRequest.Builder(spyFragment, TestFragment.REQUEST_CODE, ALL_PERMS)
|
||||
.setPositiveButtonText(POSITIVE)
|
||||
.setNegativeButtonText(NEGATIVE)
|
||||
.setRationale(RATIONALE)
|
||||
.setTheme(R.style.Theme_AppCompat)
|
||||
.build();
|
||||
EasyPermissions.requestPermissions(request);
|
||||
|
||||
androidx.fragment.app.Fragment dialogFragment = spyFragment.getChildFragmentManager()
|
||||
.findFragmentByTag(RationaleDialogFragmentCompat.TAG);
|
||||
assertThat(dialogFragment).isInstanceOf(RationaleDialogFragmentCompat.class);
|
||||
|
||||
Dialog dialog = ((RationaleDialogFragmentCompat) dialogFragment).getDialog();
|
||||
assertThatHasExpectedButtonsAndRationale(dialog, RATIONALE, POSITIVE, NEGATIVE);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldHaveSomePermissionDenied_whenShowRationaleFromFragment() {
|
||||
showRationale(true, ALL_PERMS);
|
||||
|
||||
assertThat(EasyPermissions.somePermissionDenied(spyFragment, ALL_PERMS)).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldNotHaveSomePermissionDenied_whenNotShowRationaleFromFragment() {
|
||||
showRationale(false, ALL_PERMS);
|
||||
|
||||
assertThat(EasyPermissions.somePermissionDenied(spyFragment, ALL_PERMS)).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldHaveSomePermissionPermanentlyDenied_whenNotShowRationaleFromFragment() {
|
||||
showRationale(false, ALL_PERMS);
|
||||
|
||||
assertThat(EasyPermissions.somePermissionPermanentlyDenied(spyFragment, Arrays.asList(ALL_PERMS))).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldNotHaveSomePermissionPermanentlyDenied_whenShowRationaleFromFragment() {
|
||||
showRationale(true, ALL_PERMS);
|
||||
|
||||
assertThat(EasyPermissions.somePermissionPermanentlyDenied(spyFragment, Arrays.asList(ALL_PERMS))).isFalse();
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void shouldHavePermissionPermanentlyDenied_whenNotShowRationaleFromFragment() {
|
||||
showRationale(false, Manifest.permission.READ_SMS);
|
||||
|
||||
assertThat(EasyPermissions.permissionPermanentlyDenied(spyFragment, Manifest.permission.READ_SMS)).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldNotHavePermissionPermanentlyDenied_whenShowRationaleFromFragment() {
|
||||
showRationale(true, Manifest.permission.READ_SMS);
|
||||
|
||||
assertThat(EasyPermissions.permissionPermanentlyDenied(spyFragment, Manifest.permission.READ_SMS)).isFalse();
|
||||
}
|
||||
|
||||
private void assertThatHasExpectedButtonsAndRationale(Dialog dialog, int rationale,
|
||||
int positive, int negative) {
|
||||
TextView dialogMessage = dialog.findViewById(android.R.id.message);
|
||||
assertThat(dialogMessage.getText().toString()).isEqualTo(app.getString(rationale));
|
||||
TextView positiveMessage = dialog.findViewById(android.R.id.button1);
|
||||
assertThat(positiveMessage.getText().toString()).isEqualTo(app.getString(positive));
|
||||
TextView negativeMessage = dialog.findViewById(android.R.id.button2);
|
||||
assertThat(negativeMessage.getText().toString()).isEqualTo(app.getString(negative));
|
||||
}
|
||||
|
||||
private void assertThatHasExpectedButtonsAndRationale(Dialog dialog, String rationale,
|
||||
int positive, int negative) {
|
||||
TextView dialogMessage = dialog.findViewById(android.R.id.message);
|
||||
assertThat(dialogMessage.getText().toString()).isEqualTo(rationale);
|
||||
TextView positiveMessage = dialog.findViewById(android.R.id.button1);
|
||||
assertThat(positiveMessage.getText().toString()).isEqualTo(app.getString(positive));
|
||||
TextView negativeMessage = dialog.findViewById(android.R.id.button2);
|
||||
assertThat(negativeMessage.getText().toString()).isEqualTo(app.getString(negative));
|
||||
}
|
||||
|
||||
private void assertThatHasExpectedButtonsAndRationale(Dialog dialog, String rationale,
|
||||
String positive, String negative) {
|
||||
TextView dialogMessage = dialog.findViewById(android.R.id.message);
|
||||
assertThat(dialogMessage.getText().toString()).isEqualTo(rationale);
|
||||
TextView positiveMessage = dialog.findViewById(android.R.id.button1);
|
||||
assertThat(positiveMessage.getText().toString()).isEqualTo(positive);
|
||||
TextView negativeMessage = dialog.findViewById(android.R.id.button2);
|
||||
assertThat(negativeMessage.getText().toString()).isEqualTo(negative);
|
||||
}
|
||||
|
||||
private void assertThatHasExpectedRationale(Dialog dialog, String rationale) {
|
||||
TextView dialogMessage = dialog.findViewById(android.R.id.message);
|
||||
assertThat(dialogMessage.getText().toString()).isEqualTo(rationale);
|
||||
}
|
||||
|
||||
private void grantPermissions(String[] perms) {
|
||||
shadowApp.grantPermissions(perms);
|
||||
}
|
||||
|
||||
private void showRationale(boolean show, String... perms) {
|
||||
for (String perm : perms) {
|
||||
when(spyActivity.shouldShowRequestPermissionRationale(perm)).thenReturn(show);
|
||||
when(spySupportFragmentActivity.shouldShowRequestPermissionRationale(perm)).thenReturn(show);
|
||||
when(spyAppCompatActivity.shouldShowRequestPermissionRationale(perm)).thenReturn(show);
|
||||
when(spyFragment.shouldShowRequestPermissionRationale(perm)).thenReturn(show);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,134 @@
|
||||
package pub.devrel.easypermissions;
|
||||
|
||||
import android.Manifest;
|
||||
import android.app.Activity;
|
||||
import android.app.Dialog;
|
||||
import android.content.DialogInterface;
|
||||
|
||||
import androidx.fragment.app.Fragment;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.ArgumentMatchers;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
import org.robolectric.RobolectricTestRunner;
|
||||
import org.robolectric.annotation.Config;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.anyInt;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
@RunWith(RobolectricTestRunner.class)
|
||||
@Config(sdk = 23)
|
||||
public class RationaleDialogClickListenerTest {
|
||||
|
||||
private static final int REQUEST_CODE = 5;
|
||||
private static final String[] PERMS = new String[]{
|
||||
Manifest.permission.READ_SMS, Manifest.permission.ACCESS_FINE_LOCATION};
|
||||
@Mock
|
||||
private RationaleDialogFragment dialogFragment;
|
||||
@Mock
|
||||
private RationaleDialogFragmentCompat dialogFragmentCompat;
|
||||
@Mock
|
||||
private RationaleDialogConfig dialogConfig;
|
||||
@Mock
|
||||
private EasyPermissions.PermissionCallbacks permissionCallbacks;
|
||||
@Mock
|
||||
private EasyPermissions.RationaleCallbacks rationaleCallbacks;
|
||||
@Mock
|
||||
private DialogInterface dialogInterface;
|
||||
@Mock
|
||||
private Activity activity;
|
||||
@Mock
|
||||
private Fragment fragment;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
|
||||
when(dialogFragment.getActivity()).thenReturn(activity);
|
||||
dialogConfig.requestCode = REQUEST_CODE;
|
||||
dialogConfig.permissions = PERMS;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldOnRationaleAccepted_whenPositiveButtonWithRationaleCallbacks() {
|
||||
RationaleDialogClickListener listener = new RationaleDialogClickListener(dialogFragment, dialogConfig,
|
||||
permissionCallbacks, rationaleCallbacks);
|
||||
listener.onClick(dialogInterface, Dialog.BUTTON_POSITIVE);
|
||||
|
||||
verify(rationaleCallbacks, times(1)).onRationaleAccepted(REQUEST_CODE);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldNotOnRationaleAccepted_whenPositiveButtonWithoutRationaleCallbacks() {
|
||||
RationaleDialogClickListener listener = new RationaleDialogClickListener(dialogFragment, dialogConfig,
|
||||
permissionCallbacks, null);
|
||||
listener.onClick(dialogInterface, Dialog.BUTTON_POSITIVE);
|
||||
|
||||
verify(rationaleCallbacks, never()).onRationaleAccepted(anyInt());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldRequestPermissions_whenPositiveButtonFromActivity() {
|
||||
RationaleDialogClickListener listener = new RationaleDialogClickListener(dialogFragment, dialogConfig,
|
||||
permissionCallbacks, rationaleCallbacks);
|
||||
listener.onClick(dialogInterface, Dialog.BUTTON_POSITIVE);
|
||||
|
||||
verify(activity, times(1)).requestPermissions(PERMS, REQUEST_CODE);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldRequestPermissions_whenPositiveButtonFromFragment() {
|
||||
when(dialogFragmentCompat.getParentFragment()).thenReturn(fragment);
|
||||
|
||||
RationaleDialogClickListener listener = new RationaleDialogClickListener(dialogFragmentCompat, dialogConfig,
|
||||
permissionCallbacks, rationaleCallbacks);
|
||||
listener.onClick(dialogInterface, Dialog.BUTTON_POSITIVE);
|
||||
|
||||
verify(fragment, times(1)).requestPermissions(PERMS, REQUEST_CODE);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldOnRationaleDenied_whenNegativeButtonWithRationaleCallbacks() {
|
||||
RationaleDialogClickListener listener = new RationaleDialogClickListener(dialogFragment, dialogConfig,
|
||||
permissionCallbacks, rationaleCallbacks);
|
||||
listener.onClick(dialogInterface, Dialog.BUTTON_NEGATIVE);
|
||||
|
||||
verify(rationaleCallbacks, times(1)).onRationaleDenied(REQUEST_CODE);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldNotOnRationaleDenied_whenNegativeButtonWithoutRationaleCallbacks() {
|
||||
RationaleDialogClickListener listener = new RationaleDialogClickListener(dialogFragment, dialogConfig,
|
||||
permissionCallbacks, null);
|
||||
listener.onClick(dialogInterface, Dialog.BUTTON_NEGATIVE);
|
||||
|
||||
verify(rationaleCallbacks, never()).onRationaleDenied(anyInt());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldOnPermissionsDenied_whenNegativeButtonWithPermissionCallbacks() {
|
||||
RationaleDialogClickListener listener = new RationaleDialogClickListener(dialogFragment, dialogConfig,
|
||||
permissionCallbacks, rationaleCallbacks);
|
||||
listener.onClick(dialogInterface, Dialog.BUTTON_NEGATIVE);
|
||||
|
||||
verify(permissionCallbacks, times(1))
|
||||
.onPermissionsDenied(REQUEST_CODE, Arrays.asList(PERMS));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldNotOnPermissionsDenied_whenNegativeButtonWithoutPermissionCallbacks() {
|
||||
RationaleDialogClickListener listener = new RationaleDialogClickListener(dialogFragment, dialogConfig,
|
||||
null, rationaleCallbacks);
|
||||
listener.onClick(dialogInterface, Dialog.BUTTON_NEGATIVE);
|
||||
|
||||
verify(permissionCallbacks, never()).onPermissionsDenied(anyInt(), ArgumentMatchers.<String>anyList());
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,45 @@
|
||||
package pub.devrel.easypermissions.testhelper;
|
||||
|
||||
import android.app.Activity;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.test.core.app.ActivityScenario;
|
||||
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
|
||||
/**
|
||||
* Helper class to allow starting Activity, similar to the Robolectric ActivityConroller.
|
||||
*/
|
||||
public class ActivityController<T extends Activity> {
|
||||
|
||||
private ActivityScenario<T> scenario;
|
||||
|
||||
public ActivityController(Class<T> clazz) {
|
||||
scenario = ActivityScenario.launch(clazz);
|
||||
}
|
||||
|
||||
public synchronized T resume() {
|
||||
final CompletableFuture<T> ActivityFuture = new CompletableFuture<>();
|
||||
|
||||
scenario.onActivity(new ActivityScenario.ActivityAction<T>() {
|
||||
@Override
|
||||
public void perform(@NonNull T activity) {
|
||||
ActivityFuture.complete(activity);
|
||||
}
|
||||
});
|
||||
|
||||
try {
|
||||
return ActivityFuture.get();
|
||||
} catch (InterruptedException e) {
|
||||
throw new RuntimeException(e);
|
||||
} catch (ExecutionException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public void reset() {
|
||||
scenario.recreate();
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,44 @@
|
||||
package pub.devrel.easypermissions.testhelper;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.fragment.app.testing.FragmentScenario;
|
||||
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
|
||||
/**
|
||||
* Helper class to allow starting Fragments, similar to the old SupportFragmentController.
|
||||
*/
|
||||
public class FragmentController<T extends Fragment> {
|
||||
|
||||
private FragmentScenario<T> scenario;
|
||||
|
||||
public FragmentController(Class<T> clazz) {
|
||||
scenario = FragmentScenario.launch(clazz);
|
||||
}
|
||||
|
||||
public synchronized T resume() {
|
||||
final CompletableFuture<T> fragmentFuture = new CompletableFuture<>();
|
||||
|
||||
scenario.onFragment(new FragmentScenario.FragmentAction<T>() {
|
||||
@Override
|
||||
public void perform(@NonNull T fragment) {
|
||||
fragmentFuture.complete(fragment);
|
||||
}
|
||||
});
|
||||
|
||||
try {
|
||||
return fragmentFuture.get();
|
||||
} catch (InterruptedException e) {
|
||||
throw new RuntimeException(e);
|
||||
} catch (ExecutionException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public void reset() {
|
||||
scenario.recreate();
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,40 @@
|
||||
package pub.devrel.easypermissions.testhelper;
|
||||
|
||||
import android.app.Activity;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import pub.devrel.easypermissions.AfterPermissionGranted;
|
||||
import pub.devrel.easypermissions.EasyPermissions;
|
||||
|
||||
public class TestActivity extends Activity
|
||||
implements EasyPermissions.PermissionCallbacks, EasyPermissions.RationaleCallbacks {
|
||||
|
||||
public static final int REQUEST_CODE = 1;
|
||||
|
||||
@Override
|
||||
public void onPermissionsGranted(int requestCode, @NonNull List<String> perms) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPermissionsDenied(int requestCode, @NonNull List<String> perms) {
|
||||
|
||||
}
|
||||
|
||||
@AfterPermissionGranted(REQUEST_CODE)
|
||||
public void afterPermissionGranted() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRationaleAccepted(int requestCode) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRationaleDenied(int requestCode) {
|
||||
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,50 @@
|
||||
package pub.devrel.easypermissions.testhelper;
|
||||
|
||||
import android.os.Bundle;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import pub.devrel.easypermissions.AfterPermissionGranted;
|
||||
import pub.devrel.easypermissions.EasyPermissions;
|
||||
import pub.devrel.easypermissions.R;
|
||||
|
||||
public class TestAppCompatActivity extends AppCompatActivity
|
||||
implements EasyPermissions.PermissionCallbacks, EasyPermissions.RationaleCallbacks {
|
||||
|
||||
public static final int REQUEST_CODE = 3;
|
||||
|
||||
@Override
|
||||
protected void onCreate(@Nullable Bundle savedInstanceState) {
|
||||
getTheme().applyStyle(R.style.Theme_AppCompat, true);
|
||||
super.onCreate(savedInstanceState);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPermissionsGranted(int requestCode, @NonNull List<String> perms) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPermissionsDenied(int requestCode, @NonNull List<String> perms) {
|
||||
|
||||
}
|
||||
|
||||
@AfterPermissionGranted(REQUEST_CODE)
|
||||
public void afterPermissionGranted() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRationaleAccepted(int requestCode) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRationaleDenied(int requestCode) {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,55 @@
|
||||
package pub.devrel.easypermissions.testhelper;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import pub.devrel.easypermissions.AfterPermissionGranted;
|
||||
import pub.devrel.easypermissions.EasyPermissions;
|
||||
import pub.devrel.easypermissions.R;
|
||||
|
||||
public class TestFragment extends Fragment
|
||||
implements EasyPermissions.PermissionCallbacks, EasyPermissions.RationaleCallbacks {
|
||||
|
||||
public static final int REQUEST_CODE = 4;
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public View onCreateView(@NonNull LayoutInflater inflater,
|
||||
@Nullable ViewGroup container,
|
||||
@Nullable Bundle savedInstanceState) {
|
||||
getContext().getTheme().applyStyle(R.style.Theme_AppCompat, true);
|
||||
return super.onCreateView(inflater, container, savedInstanceState);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPermissionsGranted(int requestCode, @NonNull List<String> perms) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPermissionsDenied(int requestCode, @NonNull List<String> perms) {
|
||||
|
||||
}
|
||||
|
||||
@AfterPermissionGranted(REQUEST_CODE)
|
||||
public void afterPermissionGranted() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRationaleAccepted(int requestCode) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRationaleDenied(int requestCode) {
|
||||
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,47 @@
|
||||
package pub.devrel.easypermissions.testhelper;
|
||||
|
||||
import android.os.Bundle;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.fragment.app.FragmentActivity;
|
||||
import pub.devrel.easypermissions.AfterPermissionGranted;
|
||||
import pub.devrel.easypermissions.EasyPermissions;
|
||||
|
||||
public class TestSupportFragmentActivity extends FragmentActivity
|
||||
implements EasyPermissions.PermissionCallbacks, EasyPermissions.RationaleCallbacks {
|
||||
|
||||
public static final int REQUEST_CODE = 5;
|
||||
|
||||
@Override
|
||||
protected void onCreate(@Nullable Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPermissionsGranted(int requestCode, @NonNull List<String> perms) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPermissionsDenied(int requestCode, @NonNull List<String> perms) {
|
||||
|
||||
}
|
||||
|
||||
@AfterPermissionGranted(REQUEST_CODE)
|
||||
public void afterPermissionGranted() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRationaleAccepted(int requestCode) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRationaleDenied(int requestCode) {
|
||||
|
||||
}
|
||||
}
|
||||
@ -1,5 +1,5 @@
|
||||
|
||||
android.enableR8=true
|
||||
#android.enableR8=true
|
||||
android.useAndroidX=true
|
||||
android.enableJetifier=true
|
||||
org.gradle.jvmargs=-Xmx4608m
|
||||
|
||||
@ -0,0 +1,4 @@
|
||||
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M10.6249 6.66671C10.6249 6.32153 10.3451 6.04171 9.99992 6.04171C9.65474 6.04171 9.37492 6.32153 9.37492 6.66671V9.37504H6.66659C6.32141 9.37504 6.04159 9.65486 6.04159 10C6.04159 10.3452 6.32141 10.625 6.66659 10.625H9.37492V13.3334C9.37492 13.6786 9.65474 13.9584 9.99992 13.9584C10.3451 13.9584 10.6249 13.6786 10.6249 13.3334V10.625H13.3333C13.6784 10.625 13.9583 10.3452 13.9583 10C13.9583 9.65486 13.6784 9.37504 13.3333 9.37504H10.6249V6.66671Z" fill="#2B353E"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M10.0476 1.45837H9.95227C8.12669 1.45836 6.69279 1.45835 5.57327 1.60887C4.42606 1.76311 3.51585 2.08564 2.80068 2.8008C2.08551 3.51597 1.76299 4.42618 1.60875 5.57339C1.45823 6.69291 1.45824 8.12681 1.45825 9.9524V10.0477C1.45824 11.8733 1.45823 13.3072 1.60875 14.4267C1.76299 15.5739 2.08551 16.4841 2.80068 17.1993C3.51585 17.9144 4.42606 18.237 5.57327 18.3912C6.69278 18.5417 8.12667 18.5417 9.95225 18.5417H10.0476C11.8731 18.5417 13.3071 18.5417 14.4266 18.3912C15.5738 18.237 16.484 17.9144 17.1992 17.1993C17.9143 16.4841 18.2369 15.5739 18.3911 14.4267C18.5416 13.3072 18.5416 11.8733 18.5416 10.0477V9.9524C18.5416 8.12682 18.5416 6.6929 18.3911 5.57339C18.2369 4.42618 17.9143 3.51597 17.1992 2.8008C16.484 2.08564 15.5738 1.76311 14.4266 1.60887C13.3071 1.45835 11.8732 1.45836 10.0476 1.45837ZM3.68456 3.68469C4.12877 3.24048 4.73123 2.98333 5.73983 2.84772C6.76641 2.7097 8.11627 2.70837 9.99992 2.70837C11.8836 2.70837 13.2334 2.7097 14.26 2.84772C15.2686 2.98333 15.8711 3.24048 16.3153 3.68469C16.7595 4.12889 17.0166 4.73135 17.1522 5.73995C17.2903 6.76654 17.2916 8.1164 17.2916 10C17.2916 11.8837 17.2903 13.2335 17.1522 14.2601C17.0166 15.2687 16.7595 15.8712 16.3153 16.3154C15.8711 16.7596 15.2686 17.0168 14.26 17.1524C13.2334 17.2904 11.8836 17.2917 9.99992 17.2917C8.11627 17.2917 6.76641 17.2904 5.73983 17.1524C4.73123 17.0168 4.12877 16.7596 3.68456 16.3154C3.24036 15.8712 2.9832 15.2687 2.8476 14.2601C2.70958 13.2335 2.70825 11.8837 2.70825 10C2.70825 8.1164 2.70958 6.76654 2.8476 5.73995C2.9832 4.73135 3.24036 4.12889 3.68456 3.68469Z" fill="#2B353E"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.2 KiB |
@ -0,0 +1,4 @@
|
||||
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M11.955 2.55763C8.35771 1.59711 4.67798 3.31429 3.05104 6.47547L4.24455 6.54813C4.58909 6.56911 4.85139 6.86542 4.83042 7.20996C4.80944 7.5545 4.51313 7.8168 4.16859 7.79582L2.07682 7.66848C1.87853 7.65641 1.69779 7.55079 1.58993 7.38396C1.48208 7.21713 1.45995 7.00897 1.53034 6.8232C3.14318 2.56627 7.76997 0.146382 12.2775 1.34994C17.0786 2.6319 19.936 7.5428 18.6506 12.3234C17.3654 17.1031 12.4283 19.9315 7.62802 18.6497C4.06206 17.6976 1.57008 14.7447 1.04876 11.3312C0.996642 10.99 1.23101 10.6711 1.57223 10.619C1.91345 10.5669 2.23231 10.8012 2.28443 11.1425C2.73296 14.0794 4.87811 16.6217 7.95049 17.442C12.0909 18.5476 16.339 16.1065 17.4435 11.9988C18.5478 7.89199 16.0946 3.66295 11.955 2.55763Z" fill="#2E303A"/>
|
||||
<path d="M10.6249 6.66651C10.6249 6.32133 10.3451 6.04151 9.99993 6.04151C9.65475 6.04151 9.37493 6.32133 9.37493 6.66651V9.99984C9.37493 10.1656 9.44077 10.3246 9.55798 10.4418L11.2247 12.1085C11.4687 12.3525 11.8645 12.3525 12.1085 12.1085C12.3526 11.8644 12.3526 11.4686 12.1085 11.2246L10.6249 9.74096V6.66651Z" fill="#2E303A"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.1 KiB |
@ -0,0 +1,5 @@
|
||||
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M7.91667 14.375C7.57149 14.375 7.29167 14.0951 7.29167 13.75L7.29167 8.74996C7.29167 8.40478 7.57149 8.12496 7.91667 8.12496C8.26184 8.12496 8.54167 8.40478 8.54167 8.74996L8.54167 13.75C8.54167 14.0951 8.26184 14.375 7.91667 14.375Z" fill="#D02127"/>
|
||||
<path d="M12.7083 8.74996C12.7083 8.40478 12.4285 8.12496 12.0833 8.12496C11.7382 8.12496 11.4583 8.40478 11.4583 8.74996V13.75C11.4583 14.0951 11.7382 14.375 12.0833 14.375C12.4285 14.375 12.7083 14.0951 12.7083 13.75V8.74996Z" fill="#D02127"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M10.0552 1.04163H10.0018C9.58075 1.04161 9.22356 1.04161 8.92715 1.06859C8.61261 1.09722 8.31962 1.15924 8.03631 1.30989C7.92449 1.36934 7.8178 1.43797 7.71732 1.51505C7.46273 1.71036 7.28477 1.95124 7.12826 2.22557C6.98076 2.48408 6.83263 2.80908 6.65799 3.19223L6.30879 3.95829H2.5C2.15482 3.95829 1.875 4.23812 1.875 4.58329C1.875 4.92847 2.15482 5.20829 2.5 5.20829H3.16161L3.63194 12.9992C3.6948 14.0406 3.74469 14.8669 3.84847 15.5271C3.95489 16.204 4.12601 16.7678 4.46926 17.261C4.78326 17.7122 5.18781 18.0931 5.65712 18.3793C6.17013 18.6922 6.74321 18.8291 7.42534 18.8945C8.09048 18.9583 8.91832 18.9583 9.96154 18.9583H10.0263C11.0681 18.9583 11.895 18.9583 12.5593 18.8946C13.2407 18.8293 13.8131 18.6927 14.3258 18.3804C14.7948 18.0947 15.1992 17.7145 15.5133 17.264C15.8567 16.7716 16.0283 16.2087 16.1355 15.5327C16.2401 14.8735 16.2911 14.0483 16.3554 13.0084L16.8376 5.20829H17.5C17.8452 5.20829 18.125 4.92847 18.125 4.58329C18.125 4.23812 17.8452 3.95829 17.5 3.95829H13.7714L13.3618 3.11338C13.1826 2.74382 13.0306 2.43011 12.8809 2.18069C12.722 1.91594 12.5433 1.68392 12.2918 1.4963C12.1924 1.42216 12.0871 1.35621 11.977 1.2991C11.6985 1.15458 11.4117 1.09503 11.1042 1.06752C10.8145 1.04161 10.4658 1.04162 10.0552 1.04163ZM15.5852 5.20829H13.3935C13.3847 5.20848 13.3758 5.20848 13.3669 5.20829H6.72416C6.715 5.2085 6.70582 5.2085 6.69663 5.20829H4.41389L4.87772 12.8916C4.94298 13.9725 4.98992 14.739 5.0833 15.3329C5.1751 15.9169 5.30366 16.2717 5.49526 16.547C5.71011 16.8557 5.9869 17.1163 6.30801 17.3122C6.59437 17.4868 6.95632 17.5938 7.54471 17.6502C8.14321 17.7076 8.91114 17.7083 9.99397 17.7083C11.0754 17.7083 11.8423 17.7076 12.44 17.6503C13.0277 17.594 13.3893 17.4873 13.6754 17.3129C13.9963 17.1174 14.2731 16.8573 14.488 16.5491C14.6796 16.2742 14.8085 15.9199 14.901 15.3368C14.9951 14.7437 15.043 13.9784 15.1098 12.899L15.5852 5.20829ZM12.3822 3.95829H7.68253L7.78421 3.73524C7.97304 3.32098 8.098 3.04828 8.21397 2.84502C8.32406 2.65207 8.40305 2.56445 8.47817 2.50682C8.52384 2.47178 8.57234 2.44059 8.62316 2.41356C8.70676 2.36911 8.81925 2.33358 9.04048 2.31344C9.27353 2.29222 9.5735 2.29163 10.0288 2.29163C10.4729 2.29163 10.7654 2.2922 10.9928 2.31255C11.2087 2.33185 11.319 2.36592 11.4014 2.40866C11.4514 2.43462 11.4992 2.4646 11.5444 2.4983C11.6188 2.55378 11.6976 2.63819 11.8091 2.82399C11.9267 3.01981 12.0547 3.28268 12.2485 3.68236L12.3822 3.95829Z" fill="#D02127"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 3.0 KiB |
@ -0,0 +1,4 @@
|
||||
<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M11.6701 0.495221C12.6572 -0.0918746 13.8747 -0.110273 14.8787 0.447126C15.2952 0.678342 15.675 1.06666 16.24 1.64419L16.3934 1.80091C16.9604 2.37969 17.3398 2.76705 17.5651 3.19051C18.1056 4.20643 18.088 5.43543 17.5183 6.43501C17.2808 6.85167 16.8903 7.22757 16.3071 7.78898L8.29152 15.5093C7.14082 16.618 6.43156 17.3014 5.53412 17.6519C4.63538 18.0029 3.65817 17.9772 2.07716 17.9356L1.67137 17.9249C1.43966 17.9189 1.20543 17.9129 1.01203 17.8806C0.789927 17.8436 0.52612 17.7584 0.317414 17.5215C0.110574 17.2868 0.0574955 17.0159 0.0450756 16.793C0.0340941 16.596 0.0525398 16.3599 0.0710769 16.1227L0.105174 15.685C0.21291 14.2995 0.27877 13.4525 0.613206 12.6828C0.948079 11.912 1.52015 11.2924 2.45354 10.2814L10.3439 1.73254C10.8921 1.13821 11.2606 0.73876 11.6701 0.495221ZM14.2719 1.54C13.6572 1.19868 12.9141 1.20973 12.309 1.56958C12.077 1.70753 11.8391 1.9556 11.1874 2.66174L11.1194 2.73538L15.3547 6.97069L15.3617 6.96398C16.0522 6.29893 16.296 6.05516 16.4323 5.81608C16.7896 5.18915 16.8008 4.41535 16.4615 3.77762C16.3321 3.53437 16.0952 3.28325 15.4243 2.5978C14.7524 1.91149 14.5076 1.67083 14.2719 1.54ZM3.45048 11.0443L10.2709 3.65466L14.4543 7.83799L7.5253 14.5117C6.23985 15.7498 5.71585 16.239 5.07935 16.4876C4.44424 16.7356 3.73726 16.7285 1.97083 16.6823L1.7375 16.6762C1.60457 16.6728 1.49928 16.67 1.40888 16.6652C1.36203 16.6628 1.32351 16.66 1.29159 16.6569C1.29192 16.5567 1.3021 16.415 1.31986 16.187L1.34236 15.8982C1.46224 14.3595 1.5188 13.7353 1.75968 13.1809C2.00008 12.6275 2.41374 12.1675 3.45048 11.0443Z" fill="#2B353E"/>
|
||||
<path d="M10.6668 16.7083C10.3216 16.7083 10.0418 16.9881 10.0418 17.3333C10.0418 17.6785 10.3216 17.9583 10.6668 17.9583H17.3334C17.6786 17.9583 17.9584 17.6785 17.9584 17.3333C17.9584 16.9881 17.6786 16.7083 17.3334 16.7083H10.6668Z" fill="#2B353E"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.9 KiB |
@ -0,0 +1,4 @@
|
||||
<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M11.67 0.495221C12.6572 -0.0918746 13.8747 -0.110273 14.8787 0.447126C15.2951 0.678342 15.675 1.06666 16.24 1.64419L16.3934 1.80091C16.9603 2.37969 17.3398 2.76705 17.5651 3.19051C18.1055 4.20643 18.0879 5.43543 17.5183 6.43501C17.2808 6.85167 16.8903 7.22757 16.307 7.78898L8.29149 15.5093C7.14079 16.618 6.43153 17.3014 5.53409 17.6519C4.63535 18.0029 3.65814 17.9772 2.07713 17.9356L1.67134 17.9249C1.43963 17.9189 1.2054 17.9129 1.012 17.8806C0.789896 17.8436 0.526089 17.7584 0.317383 17.5215C0.110543 17.2868 0.057465 17.0159 0.0450451 16.793C0.0340636 16.596 0.0525093 16.3599 0.0710464 16.1227L0.105143 15.685C0.212879 14.2995 0.27874 13.4525 0.613175 12.6828C0.948048 11.912 1.52012 11.2924 2.45351 10.2814L10.3439 1.73254C10.8921 1.13821 11.2605 0.73876 11.67 0.495221ZM14.2719 1.54C13.6571 1.19868 12.914 1.20973 12.309 1.56958C12.077 1.70753 11.8391 1.9556 11.1874 2.66174L11.1194 2.73538L15.3547 6.97069L15.3617 6.96398C16.0521 6.29893 16.296 6.05516 16.4323 5.81608C16.7896 5.18915 16.8008 4.41535 16.4615 3.77762C16.3321 3.53437 16.0952 3.28325 15.4242 2.5978C14.7524 1.91149 14.5076 1.67083 14.2719 1.54ZM3.45045 11.0443L10.2709 3.65466L14.4542 7.83799L7.52527 14.5117C6.23982 15.7498 5.71582 16.239 5.07932 16.4876C4.44421 16.7356 3.73723 16.7285 1.9708 16.6823L1.73747 16.6762C1.60454 16.6728 1.49925 16.67 1.40885 16.6652C1.362 16.6628 1.32348 16.66 1.29156 16.6569C1.29189 16.5567 1.30207 16.415 1.31983 16.187L1.34233 15.8982C1.46221 14.3595 1.51877 13.7353 1.75964 13.1809C2.00005 12.6275 2.41371 12.1675 3.45045 11.0443Z" fill="#2B353E"/>
|
||||
<path d="M10.6667 16.7083C10.3216 16.7083 10.0417 16.9881 10.0417 17.3333C10.0417 17.6785 10.3216 17.9583 10.6667 17.9583H17.3334C17.6786 17.9583 17.9584 17.6785 17.9584 17.3333C17.9584 16.9881 17.6786 16.7083 17.3334 16.7083H10.6667Z" fill="#2B353E"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.9 KiB |
@ -0,0 +1,3 @@
|
||||
<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M2.45441 1.79572C4.93855 0.271943 7.16707 0.879232 8.51335 1.89026C8.73484 2.05659 8.88684 2.17042 9 2.24729C9.11316 2.17042 9.26517 2.05659 9.48665 1.89026C10.8329 0.879232 13.0615 0.271943 15.5456 1.79572C17.2633 2.84936 18.2298 5.05026 17.8907 7.57902C17.5499 10.12 15.9052 12.9938 12.4222 15.5718C12.3931 15.5934 12.3642 15.6148 12.3355 15.636C11.126 16.5318 10.3251 17.125 9 17.125C7.67495 17.125 6.87396 16.5318 5.66447 15.636C5.63581 15.6148 5.60691 15.5934 5.57778 15.5718C2.09478 12.9938 0.450122 10.12 0.109332 7.57902C-0.229821 5.05026 0.736704 2.84936 2.45441 1.79572ZM8.86938 2.32783C8.8667 2.32904 8.86718 2.32865 8.87054 2.3273C8.87011 2.3275 8.86972 2.32768 8.86938 2.32783ZM9.12946 2.32729C9.13282 2.32864 9.1333 2.32904 9.13062 2.32783C9.13028 2.32768 9.12989 2.3275 9.12946 2.32729ZM7.76273 2.88979C6.76562 2.14099 5.09326 1.64347 3.10799 2.86123C1.89289 3.60658 1.0612 5.27264 1.34824 7.41286C1.63365 9.54088 3.0396 12.138 6.32144 14.5671C7.63991 15.543 8.12114 15.875 9 15.875C9.87886 15.875 10.3601 15.543 11.6786 14.5671C14.9604 12.138 16.3664 9.54088 16.6518 7.41286C16.9388 5.27264 16.1071 3.60658 14.892 2.86123C12.9067 1.64347 11.2344 2.14099 10.2373 2.88979L10.2198 2.90288C9.99452 3.0721 9.80098 3.21746 9.64616 3.31882C9.56569 3.3715 9.47659 3.42514 9.38411 3.46693C9.29635 3.50659 9.16153 3.55621 9 3.55621C8.83847 3.55621 8.70365 3.50659 8.61589 3.46693C8.52341 3.42514 8.43431 3.3715 8.35384 3.31882C8.19901 3.21745 8.00546 3.07209 7.78012 2.90285L7.76273 2.88979Z" fill="#459BF1"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.6 KiB |
@ -0,0 +1,3 @@
|
||||
<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M2.45416 1.79596C4.93831 0.272187 7.16683 0.879476 8.51311 1.89051C8.73459 2.05683 8.8866 2.17067 8.99975 2.24754C9.11292 2.17067 9.26492 2.05683 9.4864 1.89051C10.8327 0.879476 13.0612 0.272187 15.5454 1.79596C17.2631 2.8496 18.2296 5.0505 17.8904 7.57926C17.5496 10.1202 15.905 12.9941 12.422 15.5721C11.2125 16.4678 10.3248 17.1253 8.99976 17.1253C7.67471 17.1253 6.78702 16.4678 5.57754 15.5721C2.09453 12.9941 0.449878 10.1202 0.109088 7.57926C-0.230066 5.0505 0.73646 2.8496 2.45416 1.79596Z" fill="#459BF1"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 628 B |
@ -0,0 +1,5 @@
|
||||
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M9.57578 9.79268C9.5179 9.79175 9.45097 9.79172 9.36841 9.79172C9.02323 9.79172 8.74341 9.5119 8.74341 9.16672C8.74341 8.82155 9.02323 8.54172 9.36841 8.54172L9.39711 8.54172C9.56804 8.54165 9.75287 8.54158 9.90889 8.56255C10.0917 8.58712 10.3251 8.65032 10.5216 8.84682C10.7181 9.04332 10.7813 9.27681 10.8059 9.45958C10.8269 9.61559 10.8268 9.80042 10.8267 9.97135L10.8267 14.1667C10.8267 14.5119 10.5469 14.7917 10.2017 14.7917C9.85656 14.7917 9.57674 14.5119 9.57674 14.1667V10.0001C9.57674 9.91749 9.57671 9.85056 9.57578 9.79268Z" fill="#2B353E"/>
|
||||
<path d="M9.99341 5.83337C9.53317 5.83337 9.16008 6.20647 9.16008 6.66671C9.16008 7.12695 9.53317 7.50004 9.99341 7.50004H10.0009C10.4611 7.50004 10.8342 7.12695 10.8342 6.66671C10.8342 6.20647 10.4611 5.83337 10.0009 5.83337H9.99341Z" fill="#2B353E"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M9.95227 1.45837H10.0476C11.8732 1.45836 13.3071 1.45835 14.4266 1.60887C15.5738 1.76311 16.484 2.08564 17.1992 2.8008C17.9143 3.51597 18.2369 4.42618 18.3911 5.57339C18.5416 6.6929 18.5416 8.12679 18.5416 9.95237V10.0477C18.5416 11.8733 18.5416 13.3072 18.3911 14.4267C18.2369 15.5739 17.9143 16.4841 17.1992 17.1993C16.484 17.9144 15.5738 18.237 14.4266 18.3912C13.3071 18.5417 11.8732 18.5417 10.0476 18.5417H9.95228C8.1267 18.5417 6.69278 18.5417 5.57327 18.3912C4.42606 18.237 3.51585 17.9144 2.80068 17.1993C2.08551 16.4841 1.76299 15.5739 1.60875 14.4267C1.45823 13.3072 1.45824 11.8733 1.45825 10.0477V9.9524C1.45824 8.12681 1.45823 6.69291 1.60875 5.57339C1.76299 4.42618 2.08551 3.51597 2.80068 2.8008C3.51585 2.08564 4.42606 1.76311 5.57327 1.60887C6.69279 1.45835 8.12669 1.45836 9.95227 1.45837ZM5.73983 2.84772C4.73123 2.98333 4.12877 3.24048 3.68456 3.68469C3.24036 4.12889 2.9832 4.73135 2.8476 5.73995C2.70958 6.76654 2.70825 8.1164 2.70825 10C2.70825 11.8837 2.70958 13.2335 2.8476 14.2601C2.9832 15.2687 3.24036 15.8712 3.68456 16.3154C4.12877 16.7596 4.73123 17.0168 5.73983 17.1524C6.76641 17.2904 8.11627 17.2917 9.99992 17.2917C11.8836 17.2917 13.2334 17.2904 14.26 17.1524C15.2686 17.0168 15.8711 16.7596 16.3153 16.3154C16.7595 15.8712 17.0166 15.2687 17.1522 14.2601C17.2903 13.2335 17.2916 11.8837 17.2916 10C17.2916 8.1164 17.2903 6.76654 17.1522 5.73995C17.0166 4.73135 16.7595 4.12889 16.3153 3.68469C15.8711 3.24048 15.2686 2.98333 14.26 2.84772C13.2334 2.7097 11.8836 2.70837 9.99992 2.70837C8.11627 2.70837 6.76641 2.7097 5.73983 2.84772Z" fill="#2B353E"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.5 KiB |
@ -0,0 +1,8 @@
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g id="Group 8929">
|
||||
<circle id="Ellipse 149" cx="12" cy="12" r="12" transform="matrix(-1 0 0 1 24 0)" fill="white"/>
|
||||
<g id="back (1)">
|
||||
<path id="Chevron_Right" d="M16.8096 11.9647L10.965 17.8073C10.9041 17.8683 10.8319 17.9167 10.7523 17.9498C10.6727 17.9829 10.5874 17.9999 10.5012 18C10.415 18.0001 10.3297 17.9832 10.2501 17.9503C10.1704 17.9174 10.098 17.8692 10.037 17.8083C9.97603 17.7474 9.92762 17.6751 9.89456 17.5955C9.8615 17.516 9.84444 17.4307 9.84435 17.3445C9.84426 17.2583 9.86114 17.173 9.89404 17.0933C9.92693 17.0137 9.97519 16.9413 10.0361 16.8803L15.4175 11.5008L10.0367 6.12134C9.91351 5.99816 9.84431 5.83108 9.84431 5.65687C9.84431 5.48265 9.91351 5.31558 10.0367 5.19239C10.1599 5.06921 10.327 5 10.5012 5C10.6754 5 10.8425 5.06921 10.9657 5.19239L16.8102 11.0357C16.932 11.1597 17.0001 11.3265 17 11.5003C16.9999 11.6741 16.9315 11.8408 16.8096 11.9647Z" fill="#2B353E"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1011 B |
@ -0,0 +1,4 @@
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<circle cx="12" cy="12" r="12" fill="white"/>
|
||||
<path d="M7.1904 11.9647L13.035 17.8073C13.0959 17.8683 13.1681 17.9167 13.2477 17.9498C13.3273 17.9829 13.4126 17.9999 13.4988 18C13.585 18.0001 13.6703 17.9832 13.7499 17.9503C13.8296 17.9174 13.902 17.8692 13.963 17.8083C14.024 17.7474 14.0724 17.6751 14.1054 17.5955C14.1385 17.516 14.1556 17.4307 14.1557 17.3445C14.1557 17.2583 14.1389 17.173 14.106 17.0933C14.0731 17.0137 14.0248 16.9413 13.9639 16.8803L8.58255 11.5008L13.9633 6.12134C14.0865 5.99816 14.1557 5.83108 14.1557 5.65687C14.1557 5.48265 14.0865 5.31558 13.9633 5.19239C13.8401 5.06921 13.673 5 13.4988 5C13.3246 5 13.1575 5.06921 13.0343 5.19239L7.18976 11.0357C7.06803 11.1597 6.99988 11.3265 7 11.5003C7.00012 11.6741 7.0685 11.8408 7.1904 11.9647Z" fill="#2B353E"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 889 B |
@ -0,0 +1,4 @@
|
||||
<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M12.7941 6.92237C13.0273 6.66792 13.0101 6.27257 12.7557 6.03932C12.5012 5.80608 12.1059 5.82327 11.8726 6.07772L7.73038 10.5965L6.10862 8.97477C5.86454 8.73069 5.46881 8.73069 5.22473 8.97477C4.98066 9.21885 4.98066 9.61457 5.22473 9.85865L7.30807 11.942C7.42861 12.0625 7.59316 12.1286 7.76359 12.1249C7.93402 12.1212 8.09554 12.048 8.21073 11.9224L12.7941 6.92237Z" fill="#359846"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M9.04766 0.458374H8.95236C7.12678 0.458363 5.69288 0.458354 4.57336 0.608869C3.42615 0.763108 2.51594 1.08564 1.80077 1.8008C1.08561 2.51597 0.763077 3.42618 0.608838 4.57339C0.458324 5.69291 0.458333 7.12681 0.458344 8.95239V9.04769C0.458333 10.8733 0.458324 12.3072 0.608838 13.4267C0.763077 14.5739 1.08561 15.4841 1.80077 16.1993C2.51594 16.9144 3.42615 17.237 4.57336 17.3912C5.69287 17.5417 7.12676 17.5417 8.95234 17.5417H9.04765C10.8732 17.5417 12.3071 17.5417 13.4267 17.3912C14.5739 17.237 15.4841 16.9144 16.1992 16.1993C16.9144 15.4841 17.2369 14.5739 17.3912 13.4267C17.5417 12.3072 17.5417 10.8733 17.5417 9.04771V8.9524C17.5417 7.12682 17.5417 5.6929 17.3912 4.57339C17.2369 3.42618 16.9144 2.51597 16.1992 1.8008C15.4841 1.08564 14.5739 0.763108 13.4267 0.608869C12.3071 0.458354 10.8732 0.458363 9.04766 0.458374ZM2.68465 2.68468C3.12886 2.24048 3.73132 1.98333 4.73992 1.84772C5.76651 1.7097 7.11636 1.70837 9.00001 1.70837C10.8837 1.70837 12.2335 1.7097 13.2601 1.84772C14.2687 1.98333 14.8712 2.24048 15.3154 2.68468C15.7596 3.12889 16.0167 3.73135 16.1523 4.73995C16.2903 5.76654 16.2917 7.11639 16.2917 9.00004C16.2917 10.8837 16.2903 12.2335 16.1523 13.2601C16.0167 14.2687 15.7596 14.8712 15.3154 15.3154C14.8712 15.7596 14.2687 16.0168 13.2601 16.1524C12.2335 16.2904 10.8837 16.2917 9.00001 16.2917C7.11636 16.2917 5.76651 16.2904 4.73992 16.1524C3.73132 16.0168 3.12886 15.7596 2.68465 15.3154C2.24045 14.8712 1.98329 14.2687 1.84769 13.2601C1.70967 12.2335 1.70834 10.8837 1.70834 9.00004C1.70834 7.11639 1.70967 5.76654 1.84769 4.73995C1.98329 3.73135 2.24045 3.12889 2.68465 2.68468Z" fill="#359846"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.1 KiB |
@ -0,0 +1,6 @@
|
||||
<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M8.655 0.0416262H7.68289C6.05716 0.0416157 4.77679 0.0416073 3.77632 0.168644C2.75217 0.298686 1.92699 0.571419 1.27355 1.18855C0.615066 1.81045 0.319448 2.60386 0.179402 3.58764C0.0439182 4.53937 0.0439303 5.75446 0.0439456 7.28385V10.7161C0.0439303 12.2454 0.0439182 13.4605 0.179402 14.4123C0.319447 15.3961 0.615064 16.1895 1.27356 16.8114C1.927 17.4285 2.75218 17.7012 3.77632 17.8313C4.77679 17.9583 6.05717 17.9583 7.68291 17.9583H8.16895C8.51412 17.9583 8.79395 17.6785 8.79395 17.3333C8.79395 16.9881 8.51412 16.7083 8.16895 16.7083H7.72778C6.04728 16.7083 4.84633 16.7071 3.93378 16.5912C3.03571 16.4772 2.51212 16.2617 2.13181 15.9026C1.75657 15.5482 1.53532 15.0678 1.41693 14.2361C1.29543 13.3826 1.29395 12.2566 1.29395 10.6666V7.33329C1.29395 5.74327 1.29543 4.61727 1.41693 3.7638C1.53532 2.93216 1.75657 2.45173 2.13183 2.09732C2.51213 1.73814 3.03571 1.52272 3.93378 1.40869C4.84634 1.29281 6.04729 1.29163 7.72778 1.29163H8.61011C10.2906 1.29163 11.4916 1.29281 12.4041 1.40869C13.3022 1.52272 13.8258 1.73814 14.2061 2.09732C14.5813 2.45173 14.8026 2.93216 14.921 3.7638C15.0425 4.61727 15.0439 5.74327 15.0439 7.33329V8.16663C15.0439 8.5118 15.3238 8.79163 15.6689 8.79163C16.0141 8.79163 16.2939 8.5118 16.2939 8.16663V7.28387C16.294 5.75447 16.294 4.53937 16.1585 3.58764C16.0184 2.60385 15.7228 1.81045 15.0643 1.18855C14.4109 0.571419 13.5857 0.298687 12.5616 0.168644C11.5611 0.0416073 10.2807 0.0416157 8.655 0.0416262Z" fill="#449BF1"/>
|
||||
<path d="M4.83561 4.20829C4.49043 4.20829 4.21061 4.48811 4.21061 4.83329C4.21061 5.17847 4.49043 5.45829 4.83561 5.45829H11.5023C11.8475 5.45829 12.1273 5.17847 12.1273 4.83329C12.1273 4.48811 11.8475 4.20829 11.5023 4.20829H4.83561Z" fill="#449BF1"/>
|
||||
<path d="M4.21061 8.99996C4.21061 8.65478 4.49043 8.37496 4.83561 8.37496H11.5023C11.8475 8.37496 12.1273 8.65478 12.1273 8.99996C12.1273 9.34514 11.8475 9.62496 11.5023 9.62496H4.83561C4.49043 9.62496 4.21061 9.34514 4.21061 8.99996Z" fill="#449BF1"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M16.827 10.5902C16.0936 9.85877 14.9051 9.85877 14.1717 10.5902L11.1019 13.6515C10.7762 13.9762 10.5558 14.3913 10.4694 14.8428L10.0711 16.6982C9.91167 17.441 10.5742 18.0942 11.3117 17.9346L13.1822 17.5296C13.6335 17.4436 14.049 17.2243 14.3744 16.8998L17.4078 13.8178C18.14 13.0852 18.1392 11.8988 17.4056 11.1672L16.827 10.5902ZM15.0543 11.4753C15.2999 11.2304 15.6988 11.2304 15.9443 11.4753L16.5229 12.0523C16.7675 12.2962 16.7675 12.691 16.5229 12.9349L13.4899 16.0165C13.341 16.1642 13.1507 16.264 12.9436 16.3026C12.9377 16.3037 12.9317 16.3049 12.9258 16.3062L11.361 16.6449L11.6932 15.0977L11.6964 15.0816C11.735 14.8754 11.8354 14.6853 11.9845 14.5366L15.0543 11.4753Z" fill="#449BF1"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.8 KiB |
@ -0,0 +1,3 @@
|
||||
<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M16.0001 30.3333C8.084 30.3333 1.66675 23.916 1.66675 16C1.66675 8.08388 8.084 1.66663 16.0001 1.66663C23.9162 1.66663 30.3334 8.08388 30.3334 16C30.3334 23.916 23.9162 30.3333 16.0001 30.3333ZM3.66675 16C3.66675 22.8115 9.18857 28.3333 16.0001 28.3333C19.0459 28.3333 21.8338 27.2292 23.9854 25.3995L6.60053 8.01462C4.7708 10.1663 3.66675 12.9542 3.66675 16ZM25.3996 23.9853C27.2294 21.8336 28.3334 19.0457 28.3334 16C28.3334 9.18845 22.8116 3.66663 16.0001 3.66663C12.9543 3.66663 10.1664 4.77068 8.01474 6.60041L25.3996 23.9853Z" fill="#ADADAD"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 702 B |
|
After Width: | Height: | Size: 1.4 KiB |
|
After Width: | Height: | Size: 1.9 KiB |
|
After Width: | Height: | Size: 2.7 KiB |
|
After Width: | Height: | Size: 222 B |
|
After Width: | Height: | Size: 412 B |
@ -0,0 +1,22 @@
|
||||
export const icons = {
|
||||
chatSend: require('./chat-send.png'),
|
||||
defaultAvatar: require('./default-avatar.png'),
|
||||
locked: require('./locked.png'),
|
||||
more: require('./more.png'),
|
||||
mute: require('./mute.png'),
|
||||
muted: require('./muted.png'),
|
||||
shareOn: require('./share-on.png'),
|
||||
shareOff: require('./share-off.png'),
|
||||
speakerOn: require('./speaker-on.png'),
|
||||
speakerOff: require('./speaker-off.png'),
|
||||
switchCamera: require('./switch-camera.png'),
|
||||
hamburger: require('./hamburger.png'),
|
||||
questionBalloon: require('./question-ballon.png'),
|
||||
talking: require('./talking.png'),
|
||||
unmute: require('./unmute.png'),
|
||||
unlocked: require('./unlocked.png'),
|
||||
videoOn: require('./video-on.png'),
|
||||
videoOff: require('./video-off.png'),
|
||||
};
|
||||
|
||||
export type IconTypes = keyof typeof icons;
|
||||
|
After Width: | Height: | Size: 376 B |
|
After Width: | Height: | Size: 567 B |
|
After Width: | Height: | Size: 5.4 KiB |
|
After Width: | Height: | Size: 10 KiB |
|
After Width: | Height: | Size: 5.7 KiB |
|
After Width: | Height: | Size: 11 KiB |
|
After Width: | Height: | Size: 2.0 KiB |
|
After Width: | Height: | Size: 2.0 KiB |
|
After Width: | Height: | Size: 877 B |
|
After Width: | Height: | Size: 1.2 KiB |
|
After Width: | Height: | Size: 13 KiB |
|
After Width: | Height: | Size: 24 KiB |
|
After Width: | Height: | Size: 9.3 KiB |
|
After Width: | Height: | Size: 17 KiB |
|
After Width: | Height: | Size: 2.0 KiB |
|
After Width: | Height: | Size: 1.6 KiB |
|
After Width: | Height: | Size: 2.1 KiB |
|
After Width: | Height: | Size: 2.0 KiB |
|
After Width: | Height: | Size: 2.1 KiB |
|
After Width: | Height: | Size: 384 B |
|
After Width: | Height: | Size: 634 B |
|
After Width: | Height: | Size: 7.3 KiB |
|
After Width: | Height: | Size: 14 KiB |
|
After Width: | Height: | Size: 12 KiB |
|
After Width: | Height: | Size: 24 KiB |
|
After Width: | Height: | Size: 14 KiB |
|
After Width: | Height: | Size: 27 KiB |