Compare commits

...

98 Commits

Author SHA1 Message Date
Sultan khan 415b2174e7 update. 1 year ago
Sultan khan 53aa39cab9 Updated 1 year ago
Sultan khan f125efb8c9 frequency changes 1 year ago
Sultan khan 9e7ca2ecf8 update assessment changes. 1 year ago
Sultan khan fe5d66f57d ECG order number updated. 1 year ago
Sultan khan b8efa77837 Merge branch 'update_flutter_3.16.0' of http://34.17.75.184/Haroon6138/doctor_app_flutter into update_flutter_3.16.0
# Conflicts:
#	lib/utils/exception_report.dart
1 year ago
Sultan khan 7b8e0ff7bd data type changes 1 year ago
taha.alam 4b06924538 WD:date time added to the document reference in collection 1 year ago
taha.alam 0b89ea91b7 WD: firebase firestore added and url and response mapping added 1 year ago
Sultan khan df9244f343 Merge branch 'update_flutter_3.16.0' of http://34.17.75.184/Haroon6138/doctor_app_flutter into update_flutter_3.16.0 1 year ago
Sultan khan b8bdee99a6 referral click fixed, 1 year ago
haroon amjad 4af8a0a28f update to stores version ID 9.1 1 year ago
Sultan khan c13d7a08a0 logout issue fixed. 1 year ago
haroon amjad 9fb6997e1d updates & fixes 1 year ago
haroon amjad 6c0debaf5e fixes 1 year ago
Sultan khan baff15d04d fingerprint login issue fixed. 1 year ago
Sultan khan 9cd330bad0 Merge branch 'update_flutter_3.16.0' of http://34.17.52.79/Haroon6138/doctor_app_flutter into update_flutter_3.16.0 1 year ago
Sultan khan e4b891bf68 Merge branch 'development-3.3' of http://34.17.52.79/Haroon6138/doctor_app_flutter into update_flutter_3.16.0
# Conflicts:
#	ios/Runner.xcodeproj/project.pbxproj
#	lib/core/model/SOAP/assessment/patch_assessment_req_model.dart
#	lib/core/service/patient_medical_file/soap/SOAP_service.dart
#	lib/core/viewModel/SOAP_view_model.dart
#	lib/core/viewModel/dashboard_view_model.dart
#	lib/core/viewModel/prescription/prescription_view_model.dart
#	lib/core/viewModel/procedure_View_model.dart
#	lib/screens/doctor_schedule/doctor_schedule.dart
#	lib/screens/home/home_screen.dart
#	lib/screens/patients/insurance_approvals_details.dart
#	lib/screens/patients/profile/lab_result/labs_home_page.dart
#	lib/screens/patients/profile/soap_update/assessment/add_assessment_details.dart
#	lib/screens/patients/profile/soap_update/assessment/update_assessment_page.dart
#	lib/screens/prescription/add_prescription/add_drug/add_drug_widget.dart
#	lib/screens/prescription/add_prescription/prescription_form_widget.dart
#	lib/screens/prescription/new_prescriptions_page.dart
#	lib/screens/procedures/add_procedure_page.dart
#	lib/screens/procedures/procedure_screen.dart
#	lib/utils/translations_delegate_base_utils.dart
#	lib/widgets/shared/speech-text-popup.dart
#	pubspec.lock
#	pubspec.yaml
1 year ago
Sultan khan 60bc8a39fd Merge branch 'update_flutter_3.16.0' of http://34.17.52.79/Haroon6138/doctor_app_flutter into update_flutter_3.16.0 1 year ago
haroon amjad d62c723685 Biometric login fixed 2 years ago
haroon amjad ee86db3778 Radiology Critical finding CR Implemented 2 years ago
haroon amjad 519b7cfc2e Update to stores for ICD 10 code changes & other improvements 2 years ago
haroon amjad 0b6e2bc8e4 updates 2 years ago
haroon amjad ad1f1e133c updates 2 years ago
Sultan khan d0163d5a38 no message 2 years ago
Sultan khan ea547b1f71 ICD10 CR changes 2 years ago
Sultan khan dd396e50f2 bug fixes for latest version 2 years ago
Aamir Muhammad c47c93b2d9 Android Native Change For Call 2 years ago
Aamir Muhammad d2c26df4d0 Android Native Change For Call 2 years ago
Aamir Muhammad 79446a20e8 Android Native Change For Call 2 years ago
Aamir Muhammad 83737dc5a7 Null Safety Fixes & Overrides 2 years ago
Aamir Muhammad 7e4792d8cd Merge remote-tracking branch 'origin/update_flutter_3.16.0' into update_flutter_3.16.0 2 years ago
Aamir Muhammad 499cdc77d4 Null Safety Fixes & Overrides 2 years ago
Sultan khan 0f2593f56d Merge branch 'update_flutter_3.16.0' of http://34.17.52.79/Haroon6138/doctor_app_flutter into update_flutter_3.16.0
# Conflicts:
#	lib/screens/patients/profile/profile_screen/profile_gird_for_search.dart
2 years ago
Aamir Muhammad ea497ff8b1 Null Safety Fixes & Overrides 2 years ago
Aamir Muhammad 693d352669 Null Safety Fixes 2 years ago
Sultan khan 38d00243d8 updates 2 years ago
Aamir Muhammad e1604b09f8 Null Safety Fixes 2 years ago
Aamir Muhammad 3b2ee0ef65 Null Safety Fixes 2 years ago
Sultan khan 9e122748e9 updated 2 years ago
Sultan khan aee83ce930 spacing added 2 years ago
Aamir Muhammad 941134da49 Null Safety 2 years ago
Aamir Muhammad 0db3617737 Null Safety 2 years ago
Aamir Muhammad 6d18dbbac0 Null Safety 2 years ago
Aamir Muhammad e524151da9 Null Safety 2 years ago
Aamir Muhammad cb01308f96 Null Safety 2 years ago
Aamir Muhammad c3ff456645 Null Safety 2 years ago
Aamir Muhammad 5194cab7ec Merge remote-tracking branch 'origin/development-3.3' into update_flutter_3.16.0
# Conflicts:
#	lib/core/model/dashboard/doctor_schedule.dart
#	lib/screens/doctor_schedule/doctor_schedule.dart
2 years ago
Aamir Muhammad 307a11c36f Null Safety 2 years ago
Aamir Muhammad 7e92348cb5 Null Safety 2 years ago
Aamir Muhammad 0bcea41cc5 Null Safety 2 years ago
Aamir Muhammad d4cb4d7ad3 Null Safety 2 years ago
Aamir Muhammad dc802e2bda Merge remote-tracking branch 'origin/development-3.3' into update_flutter_3.16.0
# Conflicts:
#	android/app/build.gradle
#	android/app/src/main/kotlin/com/hmg/hmgDr/Service/BaseMovingFloatingWidget.kt
#	android/app/src/main/kotlin/com/hmg/hmgDr/ui/fragment/VideoCallFragment.kt
#	android/build.gradle
#	android/gradle/wrapper/gradle-wrapper.properties
#	ios/Runner.xcodeproj/project.pbxproj
#	ios/Runner/Info.plist
#	lib/client/base_app_client.dart
#	lib/core/model/ER_sign_in/doctor_ER_sign_assessment_req_model.dart
#	lib/core/model/SOAP/assessment/post_assessment_request_model.dart
#	lib/core/model/admissionRequest/admission-request.dart
#	lib/core/model/auth/check_activation_code_for_doctor_app_response_model.dart
#	lib/core/model/doctor/clinic_model.dart
#	lib/core/model/hospitals/get_hospitals_request_model.dart
#	lib/core/model/labs/patient_lab_orders.dart
#	lib/core/model/patient/get_doctor_by_clinic_id_request.dart
#	lib/core/model/patient/vital_sign/patient-vital-sign-data.dart
#	lib/core/model/pharmacy-intervention-model/intervention_medication_history_res_model.dart
#	lib/core/model/radiology/final_radiology.dart
#	lib/core/service/AnalyticsService.dart
#	lib/core/service/VideoCallService.dart
#	lib/core/service/authentication_service.dart
#	lib/core/service/hospitals/hospitals_service.dart
#	lib/core/service/patient_medical_file/ER_signin/ER_signin_service.dart
#	lib/core/service/patient_medical_file/lab_order/labs_service.dart
#	lib/core/service/patient_medical_file/radiology/radiology_service.dart
#	lib/core/service/pending_order_service.dart
#	lib/core/viewModel/ER_sign_in/ER_sign_in_view_model.dart
#	lib/core/viewModel/SOAP_view_model.dart
#	lib/core/viewModel/authentication_view_model.dart
#	lib/core/viewModel/dashboard_view_model.dart
#	lib/core/viewModel/doctor_replay_view_model.dart
#	lib/core/viewModel/patient-ucaf-viewmodel.dart
#	lib/core/viewModel/procedure_View_model.dart
#	lib/core/viewModel/radiology_view_model.dart
#	lib/screens/ER_singin/ER_singin_screen.dart
#	lib/screens/auth/login_screen.dart
#	lib/screens/auth/verification_methods_screen.dart
#	lib/screens/live_care/video_call.dart
#	lib/screens/medical-file/medical_file_details.dart
#	lib/screens/medicine/medicine_search_screen.dart
#	lib/screens/patients/DischargedPatientPage.dart
#	lib/screens/patients/ECGPage.dart
#	lib/screens/patients/out_patient/out_patient_screen.dart
#	lib/screens/patients/profile/admission_request/admission_request_first_screen.dart
#	lib/screens/patients/profile/diabetic_chart/line_chart_for_diabetic.dart
#	lib/screens/patients/profile/lab_result/history/line_chart_curved_lab_history.dart
#	lib/screens/patients/profile/lab_result/laboratory_result/laboratory_result_widget.dart
#	lib/screens/patients/profile/notes/note/progress_note_screen.dart
#	lib/screens/patients/profile/notes/note/update_note.dart
#	lib/screens/patients/profile/operation_report/update_operation_report.dart
#	lib/screens/patients/profile/profile_screen/patient_profile_screen.dart
#	lib/screens/patients/profile/profile_screen/profile_gird_for_other.dart
#	lib/screens/patients/profile/radiology/radiology_details_page.dart
#	lib/screens/patients/profile/radiology/radiology_home_page.dart
#	lib/screens/patients/profile/referral/refer_details/referred_patient_detail_in-paint.dart
#	lib/screens/patients/profile/soap_update/assessment/add_assessment_details.dart
#	lib/screens/patients/profile/soap_update/assessment/update_assessment_page.dart
#	lib/screens/patients/profile/soap_update/objective/update_objective_page.dart
#	lib/screens/patients/profile/soap_update/plan/update_plan_page.dart
#	lib/screens/patients/profile/soap_update/soap_utils.dart
#	lib/screens/patients/profile/soap_update/subjective/allergies/add_allergies.dart
#	lib/screens/patients/profile/soap_update/subjective/allergies/master_key_checkbox_search_allergies_widget.dart
#	lib/screens/patients/profile/soap_update/subjective/medication/add_medication.dart
#	lib/screens/patients/profile/soap_update/subjective/update_subjective_page.dart
#	lib/screens/patients/profile/vital_sign/line_chart_curved.dart
#	lib/screens/patients/profile/vital_sign/line_chart_curved_blood_pressure.dart
#	lib/screens/patients/profile/vital_sign/vital_sign_details_screen.dart
#	lib/screens/patients/profile/vital_sign/vital_sign_item_details_screen.dart
#	lib/screens/prescription/add_prescription/add_drug/drug_to_drug.dart
#	lib/screens/prescription/add_prescription/prescription_form_widget.dart
#	lib/screens/prescription/old_prescriptions_page.dart
#	lib/screens/prescription/prescription_checkout_screen.dart
#	lib/screens/procedures/add_procedure_page.dart
#	lib/screens/procedures/entity_list_checkbox_search_widget.dart
#	lib/screens/reschedule_leaves/reschedule_leave.dart
#	lib/utils/translations_delegate_base_utils.dart
#	lib/utils/utils.dart
#	lib/widgets/patients/profile/app_bar/patient-profile-app-bar.dart
#	lib/widgets/patients/profile/app_bar/patient-profile-header-new-design-app-bar.dart
#	lib/widgets/shared/app_scaffold_widget.dart
#	lib/widgets/shared/app_texts_widget.dart
#	lib/widgets/shared/dialogs/dailog-list-select.dart
#	lib/widgets/shared/dialogs/master_key_dailog.dart
#	lib/widgets/shared/dialogs/search-drugs-dailog-list.dart
#	lib/widgets/shared/text_fields/TextFields.dart
#	lib/widgets/shared/text_fields/html_rich_editor.dart
#	pubspec.lock
#	pubspec.yaml
2 years ago
Sultan khan 08c0c0abc1 doctor rota 2 years ago
Aamir Muhammad f977dc10a4 Fixes 2 years ago
Sultan khan b5851f384d DOCTOR ROTA AND LAB RAD CHANGES 2 years ago
Aamir Muhammad b83c48abe5 Updates 2 years ago
Aamir Muhammad bb86949f65 Null Safety Updates 3.16 2 years ago
Aamir Muhammad 1bc355b2c4 Null Safety Updates 3.16 2 years ago
Aamir Muhammad ea7744254b Null Safety Update 2 years ago
Aamir Muhammad 76f8b37d1e Null Safety Update 2 years ago
Aamir Muhammad 597d5a3498 Null Safety Update 2 years ago
Aamir Muhammad 435567305e Pubspec & Models Null Safety Update 2 years ago
Sultan khan 320af7d9e0 patch allergies updated. 2 years ago
Sultan khan bee21f1123 scroll added on add assessment 2 years ago
haroon amjad fde016c38b updates 2 years ago
Sultan khan 8b515ee14d progress not updated 2 years ago
Sultan khan 8428c2f632 Merge branch 'development-3.3' of http://34.17.52.79/Haroon6138/doctor_app_flutter into development-3.3
# Conflicts:
#	lib/screens/patients/profile/soap_update/assessment/add_assessment_details.dart
2 years ago
Sultan khan 324792d957 post assessment fix 2 years ago
haroon amjad f0204fcbb0 updates & fixes 2 years ago
haroon amjad 793f9ff905 add assessment updated 2 years ago
Sultan khan 5e63db7ea6 bug fixes and updates 2 years ago
haroon amjad 0d65197b2f updates & fixes 2 years ago
haroon amjad d2a16f5357 updates & fixes 2 years ago
haroon amjad e530646c28 VidaPlus changes for radiology 2 years ago
haroon amjad 1a67129a85 radiology fixes 2 years ago
haroon amjad fc21961807 updates & fixes 2 years ago
haroon amjad 4741a7b66d podfile 2 years ago
haroon amjad c50100613a updates 2 years ago
Sultan khan 2b13d43757 bug fixes 2 years ago
haroon amjad 0861a0c79f updates 2 years ago
haroon amjad 90382a1b27 Create Episode fixes 2 years ago
haroon amjad ac74e1e62d updates 2 years ago
haroon amjad 92c665197d SetupID made dynamic 2 years ago
haroon amjad 3b3e911af3 Search medicine bug fixed 2 years ago
haroon amjad 73ce9d02b8 chart fixes 2 years ago
haroon amjad 5d4aeff350 Updates & fixes 2 years ago
Sultan khan 51a8a93de1 pharmacy intervention null issue 3 years ago
Sultan khan b2773aa320 ios fixed for speech to text 3 years ago
mirza.shafique 705c9f6fb8 android fixes 3 years ago
Sultan khan ba5f29ee9f updated code for v.3.3 3 years ago
Elham Ali fe03cc606f Merge branch 'development' into 'master'
Development

See merge request Cloud_Solution/doctor_app_flutter!1000
3 years ago
Elham Ali 89a0af1d38 Merge branch 'development' into 'master'
fix the remark validation, make the bottomSheet show after loading and edit...

See merge request Cloud_Solution/doctor_app_flutter!998
4 years ago
Elham Ali 7f66670f6d Merge branch 'development' into 'master'
Update to v2.8

See merge request Cloud_Solution/doctor_app_flutter!975
4 years ago
Elham Ali 48128d4f96 Merge branch 'development' into 'master'
Revert "Merge branch 'hussam_flutter_2' into 'development'"

See merge request Cloud_Solution/doctor_app_flutter!940
4 years ago
Mohammad Aljammal 4039b3c370 Merge branch 'development' into 'master'
Release 6.4

See merge request Cloud_Solution/doctor_app_flutter!853
4 years ago
Mohammad Aljammal 604a6bbfa9 Merge branch 'development' into 'master'
Development

See merge request Cloud_Solution/doctor_app_flutter!776
4 years ago
Mohammad Aljammal 82c3d3454a Merge branch 'development' into 'master'
New Services Live care Video call and others

See merge request Cloud_Solution/doctor_app_flutter!768
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

@ -26,11 +26,11 @@ apply plugin: 'kotlin-android'
apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
android {
compileSdkVersion 30
compileSdkVersion 34
signingConfigs {
release {
storeFile file('/Users/nextwo/Desktop/Elham/keys/doctor app key')
storeFile file('/Users/mohamedmekawy/Documents/Work/DoctorApp/android/doctor_app_key')
storePassword 'Hmgdoctor1234'
keyAlias 'hmgdoctor'
keyPassword 'Hmgdoctor1234'
@ -49,7 +49,7 @@ android {
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
applicationId "com.hmg.hmgDr"
minSdkVersion 21
targetSdkVersion 30
targetSdkVersion 34
versionCode flutterVersionCode.toInteger()
versionName flutterVersionName
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
@ -59,7 +59,7 @@ android {
buildTypes {
release {
// TODO: Add your own signing config for the release build.
signingConfig signingConfigs.release
signingConfig signingConfigs.debug
}
debug {
@ -103,14 +103,17 @@ dependencies {
//openTok
implementation 'com.opentok.android:opentok-android-sdk:2.20.1'
//permissions
implementation 'pub.devrel:easypermissions:0.4.0'
// implementation 'pub.devrel:easypermissions:0.4.0'
implementation project(':easypermissions')
//retrofit
implementation 'com.squareup.retrofit2:retrofit:2.9.0'
implementation 'com.squareup.okhttp3:okhttp:4.9.0'
implementation 'com.squareup.retrofit2:converter-gson:2.6.2'
implementation 'com.squareup.okhttp3:logging-interceptor:4.9.1'
implementation 'com.squareup.retrofit2:adapter-rxjava:2.1.0'
implementation 'com.android.support:multidex:1.0.3'
implementation 'com.google.firebase:firebase-analytics:17.4.1'
}
apply plugin: 'com.google.gms.google-services'

@ -39,8 +39,10 @@
android:name=".MainActivity"
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
android:hardwareAccelerated="true"
android:exported="true"
android:launchMode="singleTop"
android:theme="@style/LaunchTheme"
android:windowSoftInputMode="adjustResize">
<intent-filter>
<action android:name="android.intent.action.MAIN" />

@ -17,8 +17,8 @@ import android.widget.Toast
import androidx.annotation.NonNull
import com.google.gson.GsonBuilder
import com.hmg.hmgDr.Service.VideoStreamFloatingWidgetService
import com.hmg.hmgDr.model.GetSessionStatusModel
import com.hmg.hmgDr.model.SessionStatusModel
import com.hmg.hmgDr.Model.GetSessionStatusModel
import com.hmg.hmgDr.Model.SessionStatusModel
import com.hmg.hmgDr.ui.VideoCallResponseListener
import io.flutter.embedding.android.FlutterFragmentActivity
import io.flutter.embedding.engine.FlutterEngine

@ -133,10 +133,10 @@ abstract class BaseMovingFloatingWidget : Service() {
return true
}
override fun onDoubleTap(e: MotionEvent?): Boolean {
miniCircleDoubleTap()
return super.onDoubleTap(e)
}
// override fun onDoubleTap(e: MotionEvent?): Boolean {
// miniCircleDoubleTap()
// return super.onDoubleTap(e)
// }
}

@ -1,8 +1,8 @@
package com.hmg.hmgDr.Service;
import com.hmg.hmgDr.model.ChangeCallStatusRequestModel;
import com.hmg.hmgDr.model.GetSessionStatusModel;
import com.hmg.hmgDr.model.SessionStatusModel;
import com.hmg.hmgDr.Model.ChangeCallStatusRequestModel;
import com.hmg.hmgDr.Model.GetSessionStatusModel;
import com.hmg.hmgDr.Model.SessionStatusModel;
import retrofit2.Call;

@ -17,6 +17,9 @@ import androidx.core.app.NotificationCompat
import androidx.core.app.NotificationManagerCompat
import androidx.core.content.ContextCompat
import androidx.core.view.GestureDetectorCompat
import com.hmg.hmgDr.Model.ChangeCallStatusRequestModel
import com.hmg.hmgDr.Model.GetSessionStatusModel
import com.hmg.hmgDr.Model.SessionStatusModel
import com.hmg.hmgDr.R
import com.hmg.hmgDr.model.*
import com.hmg.hmgDr.ui.VideoCallContract
@ -993,7 +996,7 @@ class VideoStreamFloatingWidgetService : BaseMovingFloatingWidget(), Session.Ses
Intent(this, VideoStreamFloatingWidgetService::class.java)
.let { notificationIntent ->
notificationIntent.action = ACTION_MINIMIZE_CALL
PendingIntent.getService(this, 0, notificationIntent, 0)
PendingIntent.getService(this, 0, notificationIntent, PendingIntent.FLAG_MUTABLE )
}
// 4. Create additional Actions (Intents) for the Notification.
@ -1003,7 +1006,7 @@ class VideoStreamFloatingWidgetService : BaseMovingFloatingWidget(), Session.Ses
action = ACTION_END_CALL
}
.let { notificationIntent ->
PendingIntent.getService(this, 0, notificationIntent, 0)
PendingIntent.getService(this, 0, notificationIntent, PendingIntent.FLAG_MUTABLE)
}
val endCallAction = NotificationCompat.Action.Builder(
R.drawable.ic_end_call,

@ -1,4 +1,4 @@
package com.hmg.hmgDr.model;
package com.hmg.hmgDr.Model;
import android.os.Parcel;

@ -1,4 +1,4 @@
package com.hmg.hmgDr.model;
package com.hmg.hmgDr.Model;
import android.os.Parcel;
import android.os.Parcelable;

@ -1,4 +1,4 @@
package com.hmg.hmgDr.model;
package com.hmg.hmgDr.Model;
import android.os.Parcel;
import android.os.Parcelable;

@ -1,8 +1,8 @@
package com.hmg.hmgDr.ui;
import com.hmg.hmgDr.model.ChangeCallStatusRequestModel;
import com.hmg.hmgDr.model.GetSessionStatusModel;
import com.hmg.hmgDr.model.SessionStatusModel;
import com.hmg.hmgDr.Model.ChangeCallStatusRequestModel;
import com.hmg.hmgDr.Model.GetSessionStatusModel;
import com.hmg.hmgDr.Model.SessionStatusModel;
public interface VideoCallContract {

@ -1,8 +1,8 @@
package com.hmg.hmgDr.ui;
import com.hmg.hmgDr.model.ChangeCallStatusRequestModel;
import com.hmg.hmgDr.model.GetSessionStatusModel;
import com.hmg.hmgDr.model.SessionStatusModel;
import com.hmg.hmgDr.Model.ChangeCallStatusRequestModel;
import com.hmg.hmgDr.Model.GetSessionStatusModel;
import com.hmg.hmgDr.Model.SessionStatusModel;
import com.hmg.hmgDr.Service.AppRetrofit;
import com.hmg.hmgDr.Service.SessionStatusAPI;

@ -20,9 +20,9 @@ import androidx.constraintlayout.widget.ConstraintLayout
import androidx.core.content.ContextCompat
import androidx.core.view.GestureDetectorCompat
import androidx.fragment.app.DialogFragment
import com.hmg.hmgDr.model.ChangeCallStatusRequestModel
import com.hmg.hmgDr.model.GetSessionStatusModel
import com.hmg.hmgDr.model.SessionStatusModel
import com.hmg.hmgDr.Model.ChangeCallStatusRequestModel
import com.hmg.hmgDr.Model.GetSessionStatusModel
import com.hmg.hmgDr.Model.SessionStatusModel
import com.hmg.hmgDr.R
import com.hmg.hmgDr.ui.VideoCallContract.VideoCallPresenter
import com.hmg.hmgDr.ui.VideoCallContract.VideoCallView
@ -143,8 +143,9 @@ class VideoCallFragment : DialogFragment(), PermissionCallbacks, Session.Session
return R.style.dialogTheme
}
@SuppressLint("UseRequireInsteadOfGet")
override fun onCreateDialog(@Nullable savedInstanceState: Bundle?): Dialog {// FullScreenVideoTheme
val layoutInflater = activity!!.layoutInflater
val layoutInflater = requireActivity().layoutInflater
this.parentView = onCreateView(layoutInflater, null)
val alertDialogBuilder: AlertDialog.Builder =
@ -230,7 +231,7 @@ class VideoCallFragment : DialogFragment(), PermissionCallbacks, Session.Session
handleDragDialog()
mDetector = GestureDetectorCompat(
context,
requireContext(),
MyGestureListener({ showControlPanelTemporarily() }, { miniCircleDoubleTap() })
)
@ -772,16 +773,16 @@ class VideoCallFragment : DialogFragment(), PermissionCallbacks, Session.Session
val mCallBtnLayoutParam: ConstraintLayout.LayoutParams =
mCallBtn.layoutParams as ConstraintLayout.LayoutParams
val iconSize: Int = context!!.resources.getDimension(R.dimen.video_icon_size).toInt()
val iconSize: Int = requireContext().resources.getDimension(R.dimen.video_icon_size).toInt()
val iconSizeSmall: Int =
context!!.resources.getDimension(R.dimen.video_icon_size_small).toInt()
requireContext().resources.getDimension(R.dimen.video_icon_size_small).toInt()
val localPreviewMargin: Int =
context!!.resources.getDimension(R.dimen.local_preview_margin_top).toInt()
requireContext().resources.getDimension(R.dimen.local_preview_margin_top).toInt()
val localPreviewWidth: Int =
context!!.resources.getDimension(R.dimen.local_preview_width).toInt()
requireContext().resources.getDimension(R.dimen.local_preview_width).toInt()
val localPreviewHeight: Int =
context!!.resources.getDimension(R.dimen.local_preview_height).toInt()
requireContext().resources.getDimension(R.dimen.local_preview_height).toInt()
// val localPreviewIconSize: Int = context!!.resources.getDimension(R.dimen.local_back_icon_size).toInt()
// val localPreviewMarginSmall : Int = context!!.resources.getDimension(R.dimen.local_preview_margin_small).toInt()
// val localPreviewWidthSmall : Int = context!!.resources.getDimension(R.dimen.local_preview_width_small).toInt()
@ -792,9 +793,9 @@ class VideoCallFragment : DialogFragment(), PermissionCallbacks, Session.Session
mPublisherViewContainer.layoutParams as RelativeLayout.LayoutParams
val remotePreviewIconSize: Int =
context!!.resources.getDimension(R.dimen.remote_back_icon_size).toInt()
requireContext().resources.getDimension(R.dimen.remote_back_icon_size).toInt()
val remotePreviewIconSizeSmall: Int =
context!!.resources.getDimension(R.dimen.remote_back_icon_size_small).toInt()
requireContext().resources.getDimension(R.dimen.remote_back_icon_size_small).toInt()
val remotePreviewLayoutParam: FrameLayout.LayoutParams =
mSubscriberViewIcon.layoutParams as FrameLayout.LayoutParams
@ -884,17 +885,17 @@ class VideoCallFragment : DialogFragment(), PermissionCallbacks, Session.Session
controlPanel.layoutParams as ConstraintLayout.LayoutParams
val layoutNameHeight: Int =
context!!.resources.getDimension(R.dimen.layout_name_height).toInt()
requireContext().resources.getDimension(R.dimen.layout_name_height).toInt()
val layoutMiniHeight: Int =
context!!.resources.getDimension(R.dimen.layout_mini_height).toInt()
val panelHeight: Int = context!!.resources.getDimension(R.dimen.layout_panel_height).toInt()
requireContext().resources.getDimension(R.dimen.layout_mini_height).toInt()
val panelHeight: Int = requireContext().resources.getDimension(R.dimen.layout_panel_height).toInt()
val panelHeightSmall: Int =
context!!.resources.getDimension(R.dimen.layout_panel_height_small).toInt()
requireContext().resources.getDimension(R.dimen.layout_panel_height_small).toInt()
val panelPadding: Int =
context!!.resources.getDimension(R.dimen.padding_space_big).toInt()
requireContext().resources.getDimension(R.dimen.padding_space_big).toInt()
val panelPaddingMedium: Int =
context!!.resources.getDimension(R.dimen.padding_space_medium).toInt()
requireContext().resources.getDimension(R.dimen.padding_space_medium).toInt()
val temp = getStatusBarHeight() / 2
@ -1152,10 +1153,10 @@ class VideoCallFragment : DialogFragment(), PermissionCallbacks, Session.Session
return true
}
override fun onDoubleTap(e: MotionEvent?): Boolean {
miniCircleDoubleTap()
return super.onDoubleTap(e)
}
// override fun onDoubleTap(e: MotionEvent?): Boolean {
// miniCircleDoubleTap()
// return super.onDoubleTap(e)
// }
}

@ -1,12 +1,12 @@
buildscript {
ext.kotlin_version = '1.3.50'
ext.kotlin_version = '2.0.0'
repositories {
google()
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:7.0.3'
classpath 'com.android.tools.build:gradle:7.1.3'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
classpath 'com.google.gms:google-services:4.3.3'
}
@ -31,6 +31,6 @@ subprojects {
project.evaluationDependsOn(':app')
}
task clean(type: Delete) {
tasks.register("clean", Delete) {
delete rootProject.buildDir
}

@ -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 &gt;= 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 &lt; 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 &lt; 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

@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-7.0.2-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-7.6-all.zip

@ -1,4 +1,4 @@
include ':app'
include ':app', ':easypermissions'
def flutterProjectRoot = rootProject.projectDir.parentFile.toPath()

@ -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

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 222 B

Binary file not shown.

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;

Binary file not shown.

After

Width:  |  Height:  |  Size: 376 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 567 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 877 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 384 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 634 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

@ -21,6 +21,6 @@
<key>CFBundleVersion</key>
<string>1.0</string>
<key>MinimumOSVersion</key>
<string>9.0</string>
<string>13.0</string>
</dict>
</plist>

@ -1,5 +1,5 @@
# Uncomment this line to define a global platform for your project
platform :ios, '11.0'
platform :ios, '13.0'
# CocoaPods analytics sends network stats synchronously affecting flutter build latency.
ENV['COCOAPODS_DISABLE_STATS'] = 'true'
@ -38,10 +38,24 @@ target 'Runner' do
# Plugin Pods
flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__))
pod 'DKImagePickerController/PhotoGallery', :git => 'https://github.com/miguelpruivo/DKImagePickerController.git'
end
post_install do |installer|
installer.pods_project.targets.each do |target|
flutter_additional_ios_build_settings(target)
target.build_configurations.each do |build_configuration|
build_configuration.build_settings['GCC_PREPROCESSOR_DEFINITIONS'] ||= [
'$(inherited)',
## dart: [PermissionGroup.location, PermissionGroup.locationAlways, PermissionGroup.locationWhenInUse]
'PERMISSION_CAMERA=1',
'PERMISSION_MICROPHONE=1',
]
build_configuration.build_settings['EXCLUDED_ARCHS[sdk=iphonesimulator*]'] = 'arm64 i386'
build_configuration.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] = '13.0'
if build_configuration.build_settings['WRAPPER_EXTENSION'] == 'bundle'
build_configuration.build_settings['DEVELOPMENT_TEAM'] = '3A359E86ZF'
end
end
end
end

@ -3,7 +3,7 @@
archiveVersion = 1;
classes = {
};
objectVersion = 50;
objectVersion = 54;
objects = {
/* Begin PBXBuildFile section */
@ -14,8 +14,8 @@
30F70E6C266F56FD005D8F8E /* MainAppViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30F70E6B266F56FD005D8F8E /* MainAppViewController.swift */; };
30F70E6F266F6509005D8F8E /* VideoCallRequestParameters.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30F70E6E266F6509005D8F8E /* VideoCallRequestParameters.swift */; };
3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; };
3D7F2AC9B3EBF568D59B943B /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 104DCFE405BA413255D4BDD9 /* Pods_Runner.framework */; };
74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; };
7645621A848A5D0B29B97C46 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E4E9718EA62661304F55A32D /* Pods_Runner.framework */; };
97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; };
97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; };
97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; };
@ -37,13 +37,10 @@
/* End PBXCopyFilesBuildPhase section */
/* Begin PBXFileReference section */
0762701A9540BA69842BCA9C /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = "<group>"; };
104DCFE405BA413255D4BDD9 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; };
1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = "<group>"; };
1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = "<group>"; };
29211CD725C165D600DD740D /* RunnerRelease.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = RunnerRelease.entitlements; sourceTree = "<group>"; };
29211E4125C172B700DD740D /* GoogleService-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "GoogleService-Info.plist"; sourceTree = "<group>"; };
2ADF20AFB51DAEC9BED1884D /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = "<group>"; };
300790F9266FB14B0052174C /* VCEmbeder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VCEmbeder.swift; sourceTree = "<group>"; };
300790FB26710CAB0052174C /* Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Extensions.swift; sourceTree = "<group>"; };
30F70E6B266F56FD005D8F8E /* MainAppViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainAppViewController.swift; sourceTree = "<group>"; };
@ -61,7 +58,10 @@
97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
9CE61EBC24AB366E008D68DD /* VideoCallViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VideoCallViewController.swift; sourceTree = "<group>"; };
9CE61ECC24ADBB4C008D68DD /* ICallProtocoll.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ICallProtocoll.swift; sourceTree = "<group>"; };
E5CA53445955DBB6BB6D164C /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = "<group>"; };
C55D4AE245B71956447BA22F /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = "<group>"; };
C8801C5E6B82B6CB497CA5C7 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = "<group>"; };
E333B59AA5538E0304102FD2 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = "<group>"; };
E4E9718EA62661304F55A32D /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
@ -69,7 +69,7 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
3D7F2AC9B3EBF568D59B943B /* Pods_Runner.framework in Frameworks */,
7645621A848A5D0B29B97C46 /* Pods_Runner.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@ -104,9 +104,9 @@
7D66D387293CE5376A07EC5F /* Pods */ = {
isa = PBXGroup;
children = (
2ADF20AFB51DAEC9BED1884D /* Pods-Runner.debug.xcconfig */,
E5CA53445955DBB6BB6D164C /* Pods-Runner.release.xcconfig */,
0762701A9540BA69842BCA9C /* Pods-Runner.profile.xcconfig */,
C55D4AE245B71956447BA22F /* Pods-Runner.debug.xcconfig */,
C8801C5E6B82B6CB497CA5C7 /* Pods-Runner.release.xcconfig */,
E333B59AA5538E0304102FD2 /* Pods-Runner.profile.xcconfig */,
);
path = Pods;
sourceTree = "<group>";
@ -129,7 +129,7 @@
97C146F01CF9000F007C117D /* Runner */,
97C146EF1CF9000F007C117D /* Products */,
7D66D387293CE5376A07EC5F /* Pods */,
C1938275850F66EAA5D7AC0F /* Frameworks */,
A9562A4C43D78E443FA8BFD1 /* Frameworks */,
);
sourceTree = "<group>";
};
@ -171,10 +171,10 @@
name = "Supporting Files";
sourceTree = "<group>";
};
C1938275850F66EAA5D7AC0F /* Frameworks */ = {
A9562A4C43D78E443FA8BFD1 /* Frameworks */ = {
isa = PBXGroup;
children = (
104DCFE405BA413255D4BDD9 /* Pods_Runner.framework */,
E4E9718EA62661304F55A32D /* Pods_Runner.framework */,
);
name = Frameworks;
sourceTree = "<group>";
@ -186,14 +186,15 @@
isa = PBXNativeTarget;
buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */;
buildPhases = (
C79BE4619064E868C0DFBD12 /* [CP] Check Pods Manifest.lock */,
4CBD0CB2C106C3E696B88FDB /* [CP] Check Pods Manifest.lock */,
9740EEB61CF901F6004384FC /* Run Script */,
97C146EA1CF9000F007C117D /* Sources */,
97C146EB1CF9000F007C117D /* Frameworks */,
97C146EC1CF9000F007C117D /* Resources */,
9705A1C41CF9048500538489 /* Embed Frameworks */,
3B06AD1E1E4923F5004D2608 /* Thin Binary */,
3DBDEDAE0EB70F8613EE7963 /* [CP] Embed Pods Frameworks */,
C21500C474806845DCE7C12A /* [CP] Embed Pods Frameworks */,
294E280337160E3088FD37C0 /* [CP] Copy Pods Resources */,
);
buildRules = (
);
@ -210,11 +211,13 @@
97C146E61CF9000F007C117D /* Project object */ = {
isa = PBXProject;
attributes = {
LastUpgradeCheck = 1300;
BuildIndependentTargetsInParallel = YES;
LastUpgradeCheck = 1500;
ORGANIZATIONNAME = "The Chromium Authors";
TargetAttributes = {
97C146ED1CF9000F007C117D = {
CreatedOnToolsVersion = 7.3.1;
DevelopmentTeam = 3A359E86ZF;
LastSwiftMigration = 1100;
};
};
@ -253,12 +256,32 @@
/* End PBXResourcesBuildPhase section */
/* Begin PBXShellScriptBuildPhase section */
294E280337160E3088FD37C0 /* [CP] Copy Pods Resources */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources.sh",
"${PODS_CONFIGURATION_BUILD_DIR}/permission_handler_apple/permission_handler_apple_privacy.bundle",
);
name = "[CP] Copy Pods Resources";
outputPaths = (
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/permission_handler_apple_privacy.bundle",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources.sh\"\n";
showEnvVarsInLog = 0;
};
3B06AD1E1E4923F5004D2608 /* Thin Binary */ = {
isa = PBXShellScriptBuildPhase;
alwaysOutOfDate = 1;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
"${TARGET_BUILD_DIR}/${INFOPLIST_PATH}",
);
name = "Thin Binary";
outputPaths = (
@ -267,7 +290,44 @@
shellPath = /bin/sh;
shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin";
};
3DBDEDAE0EB70F8613EE7963 /* [CP] Embed Pods Frameworks */ = {
4CBD0CB2C106C3E696B88FDB /* [CP] Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
);
inputPaths = (
"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
"${PODS_ROOT}/Manifest.lock",
);
name = "[CP] Check Pods Manifest.lock";
outputFileListPaths = (
);
outputPaths = (
"$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
showEnvVarsInLog = 0;
};
9740EEB61CF901F6004384FC /* Run Script */ = {
isa = PBXShellScriptBuildPhase;
alwaysOutOfDate = 1;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
);
name = "Run Script";
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build";
};
C21500C474806845DCE7C12A /* [CP] Embed Pods Frameworks */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
@ -278,9 +338,8 @@
"${BUILT_PRODUCTS_DIR}/Alamofire/Alamofire.framework",
"${BUILT_PRODUCTS_DIR}/DKImagePickerController/DKImagePickerController.framework",
"${BUILT_PRODUCTS_DIR}/DKPhotoGallery/DKPhotoGallery.framework",
"${BUILT_PRODUCTS_DIR}/FMDB/FMDB.framework",
"${BUILT_PRODUCTS_DIR}/FirebaseCore/FirebaseCore.framework",
"${BUILT_PRODUCTS_DIR}/FirebaseCoreDiagnostics/FirebaseCoreDiagnostics.framework",
"${BUILT_PRODUCTS_DIR}/FirebaseCoreInternal/FirebaseCoreInternal.framework",
"${BUILT_PRODUCTS_DIR}/FirebaseInstallations/FirebaseInstallations.framework",
"${BUILT_PRODUCTS_DIR}/FirebaseMessaging/FirebaseMessaging.framework",
"${BUILT_PRODUCTS_DIR}/GoogleDataTransport/GoogleDataTransport.framework",
@ -301,18 +360,14 @@
"${BUILT_PRODUCTS_DIR}/flutter_inappwebview/flutter_inappwebview.framework",
"${BUILT_PRODUCTS_DIR}/flutter_keyboard_visibility/flutter_keyboard_visibility.framework",
"${BUILT_PRODUCTS_DIR}/fluttertoast/fluttertoast.framework",
"${BUILT_PRODUCTS_DIR}/hexcolor/hexcolor.framework",
"${BUILT_PRODUCTS_DIR}/local_auth/local_auth.framework",
"${BUILT_PRODUCTS_DIR}/local_auth_ios/local_auth_ios.framework",
"${BUILT_PRODUCTS_DIR}/maps_launcher/maps_launcher.framework",
"${BUILT_PRODUCTS_DIR}/nanopb/nanopb.framework",
"${BUILT_PRODUCTS_DIR}/path_provider_ios/path_provider_ios.framework",
"${BUILT_PRODUCTS_DIR}/shared_preferences_ios/shared_preferences_ios.framework",
"${BUILT_PRODUCTS_DIR}/path_provider_foundation/path_provider_foundation.framework",
"${BUILT_PRODUCTS_DIR}/shared_preferences_foundation/shared_preferences_foundation.framework",
"${BUILT_PRODUCTS_DIR}/speech_to_text/speech_to_text.framework",
"${BUILT_PRODUCTS_DIR}/sqflite/sqflite.framework",
"${BUILT_PRODUCTS_DIR}/url_launcher_ios/url_launcher_ios.framework",
"${BUILT_PRODUCTS_DIR}/video_player_avfoundation/video_player_avfoundation.framework",
"${BUILT_PRODUCTS_DIR}/wakelock/wakelock.framework",
"${BUILT_PRODUCTS_DIR}/webview_flutter_wkwebview/webview_flutter_wkwebview.framework",
);
name = "[CP] Embed Pods Frameworks";
outputPaths = (
@ -320,9 +375,8 @@
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Alamofire.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/DKImagePickerController.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/DKPhotoGallery.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/FMDB.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/FirebaseCore.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/FirebaseCoreDiagnostics.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/FirebaseCoreInternal.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/FirebaseInstallations.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/FirebaseMessaging.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/GoogleDataTransport.framework",
@ -343,60 +397,20 @@
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/flutter_inappwebview.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/flutter_keyboard_visibility.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/fluttertoast.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/hexcolor.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/local_auth.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/local_auth_ios.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/maps_launcher.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/nanopb.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/path_provider_ios.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/shared_preferences_ios.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/path_provider_foundation.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/shared_preferences_foundation.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/speech_to_text.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/sqflite.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/url_launcher_ios.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/video_player_avfoundation.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/wakelock.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/webview_flutter_wkwebview.framework",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n";
showEnvVarsInLog = 0;
};
9740EEB61CF901F6004384FC /* Run Script */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
);
name = "Run Script";
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build";
};
C79BE4619064E868C0DFBD12 /* [CP] Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
);
inputPaths = (
"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
"${PODS_ROOT}/Manifest.lock",
);
name = "[CP] Check Pods Manifest.lock";
outputFileListPaths = (
);
outputPaths = (
"$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
showEnvVarsInLog = 0;
};
/* End PBXShellScriptBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
@ -460,6 +474,7 @@
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
@ -470,6 +485,7 @@
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_USER_SCRIPT_SANDBOXING = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_NO_COMMON_BLOCKS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
@ -478,7 +494,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
IPHONEOS_DEPLOYMENT_TARGET = 12.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
SUPPORTED_PLATFORMS = iphoneos;
@ -489,19 +505,19 @@
};
249021D4217E4FDB00AE95B9 /* Profile */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
baseConfigurationReference = E333B59AA5538E0304102FD2 /* Pods-Runner.profile.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CURRENT_PROJECT_VERSION = 4;
DEVELOPMENT_TEAM = "";
DEVELOPMENT_TEAM = 3A359E86ZF;
ENABLE_BITCODE = NO;
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
"$(PROJECT_DIR)/Flutter",
);
INFOPLIST_FILE = Runner/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
@ -510,7 +526,7 @@
"$(inherited)",
"$(PROJECT_DIR)/Flutter",
);
MARKETING_VERSION = 1.3.1;
MARKETING_VERSION = 1.3.99;
PRODUCT_BUNDLE_IDENTIFIER = com.hmg.hmgDr;
PRODUCT_NAME = Runner;
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
@ -521,6 +537,7 @@
};
97C147031CF9000F007C117D /* Debug */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 9740EEB31CF90195004384FC /* Generated.xcconfig */;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
@ -542,6 +559,7 @@
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
@ -552,6 +570,7 @@
DEBUG_INFORMATION_FORMAT = dwarf;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES;
ENABLE_USER_SCRIPT_SANDBOXING = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_DYNAMIC_NO_PIC = NO;
GCC_NO_COMMON_BLOCKS = YES;
@ -566,7 +585,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
IPHONEOS_DEPLOYMENT_TARGET = 12.0;
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos;
@ -597,6 +616,7 @@
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
@ -607,6 +627,7 @@
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_USER_SCRIPT_SANDBOXING = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_NO_COMMON_BLOCKS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
@ -615,10 +636,11 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
IPHONEOS_DEPLOYMENT_TARGET = 12.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
SUPPORTED_PLATFORMS = iphoneos;
SWIFT_COMPILATION_MODE = wholemodule;
SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
TARGETED_DEVICE_FAMILY = "1,2";
VALIDATE_PRODUCT = YES;
@ -627,19 +649,19 @@
};
97C147061CF9000F007C117D /* Debug */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */;
baseConfigurationReference = C55D4AE245B71956447BA22F /* Pods-Runner.debug.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CURRENT_PROJECT_VERSION = 4;
DEVELOPMENT_TEAM = "";
DEVELOPMENT_TEAM = 3A359E86ZF;
ENABLE_BITCODE = NO;
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
"$(PROJECT_DIR)/Flutter",
);
INFOPLIST_FILE = Runner/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
@ -648,7 +670,7 @@
"$(inherited)",
"$(PROJECT_DIR)/Flutter",
);
MARKETING_VERSION = 1.3.1;
MARKETING_VERSION = 1.3.99;
PRODUCT_BUNDLE_IDENTIFIER = com.hmg.hmgDr;
PRODUCT_NAME = Runner;
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
@ -660,20 +682,20 @@
};
97C147071CF9000F007C117D /* Release */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
baseConfigurationReference = C8801C5E6B82B6CB497CA5C7 /* Pods-Runner.release.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = Runner/RunnerRelease.entitlements;
CURRENT_PROJECT_VERSION = 4;
DEVELOPMENT_TEAM = "";
DEVELOPMENT_TEAM = 3A359E86ZF;
ENABLE_BITCODE = NO;
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
"$(PROJECT_DIR)/Flutter",
);
INFOPLIST_FILE = Runner/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
@ -682,7 +704,7 @@
"$(inherited)",
"$(PROJECT_DIR)/Flutter",
);
MARKETING_VERSION = 1.3.1;
MARKETING_VERSION = 1.3.99;
PRODUCT_BUNDLE_IDENTIFIER = com.hmg.hmgDr;
PRODUCT_NAME = Runner;
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
@ -702,7 +724,7 @@
249021D3217E4FDB00AE95B9 /* Profile */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
defaultConfigurationName = Debug;
};
97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = {
isa = XCConfigurationList;
@ -712,7 +734,7 @@
249021D4217E4FDB00AE95B9 /* Profile */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
defaultConfigurationName = Debug;
};
/* End XCConfigurationList section */
};

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>PreviewsEnabled</key>
<false/>
</dict>
</plist>

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1300"
LastUpgradeVersion = "1500"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
@ -40,7 +40,7 @@
</Testables>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
buildConfiguration = "Release"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>PreviewsEnabled</key>
<false/>
</dict>
</plist>

@ -65,5 +65,9 @@
</array>
<key>UIViewControllerBasedStatusBarAppearance</key>
<false/>
<key>CADisableMinimumFrameDurationOnPhone</key>
<true/>
<key>UIApplicationSupportsIndirectInputEvents</key>
<true/>
</dict>
</plist>

@ -0,0 +1,12 @@
import Flutter
import UIKit
import XCTest
class RunnerTests: XCTestCase {
func testExample() {
// If you add code to the Runner application, consider adding tests here.
// See https://developer.apple.com/documentation/xctest for more information about using XCTest.
}
}

@ -0,0 +1,4 @@
storePassword=HmGsa123
keyPassword=HmGsa123
keyAlias=hmgdoctor
storeFile=doctor_app_key

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save