24
24
import androidx .core .content .ContextCompat ;
25
25
26
26
import java .util .ArrayList ;
27
+ import java .util .Arrays ;
27
28
import java .util .HashMap ;
28
29
import java .util .HashSet ;
29
30
import java .util .List ;
@@ -165,9 +166,38 @@ public boolean onRequestPermissionsResult(
165
166
return false ;
166
167
}
167
168
169
+ // Calendar permissions are split between READ and WRITE in Android, and split between READ
170
+ // and FULL ACCESS in the plugin. We need special logic for this translation.
171
+ final List <String > permissionList = Arrays .asList (permissions );
172
+ final int calendarReadIndex = permissionList .indexOf (Manifest .permission .READ_CALENDAR );
173
+ final int calendarWriteIndex = permissionList .indexOf (Manifest .permission .WRITE_CALENDAR );
174
+ // READ -> READ.
175
+ if (calendarReadIndex >= 0 ) {
176
+ final int readGrantResult = grantResults [calendarReadIndex ];
177
+ final @ PermissionConstants .PermissionStatus int readStatus =
178
+ PermissionUtils .toPermissionStatus (this .activity , Manifest .permission .READ_CALENDAR , readGrantResult );
179
+ requestResults .put (PermissionConstants .PERMISSION_GROUP_CALENDAR_READ_ONLY , readStatus );
180
+
181
+ // READ + WRITE -> FULL ACCESS.
182
+ if (calendarWriteIndex >= 0 ) {
183
+ final int writeGrantResult = grantResults [calendarWriteIndex ];
184
+ final @ PermissionConstants .PermissionStatus int writeStatus =
185
+ PermissionUtils .toPermissionStatus (this .activity , Manifest .permission .WRITE_CALENDAR , writeGrantResult );
186
+ final @ PermissionConstants .PermissionStatus int fullAccessStatus = strictestStatus (readStatus , writeStatus );
187
+ requestResults .put (PermissionConstants .PERMISSION_GROUP_CALENDAR_FULL_ACCESS , fullAccessStatus );
188
+ // Support deprecated CALENDAR permission.
189
+ requestResults .put (PermissionConstants .PERMISSION_GROUP_CALENDAR , fullAccessStatus );
190
+ }
191
+ }
192
+
168
193
for (int i = 0 ; i < permissions .length ; i ++) {
169
194
final String permissionName = permissions [i ];
170
195
196
+ // READ_CALENDAR and WRITE_CALENDAR permission results have already been handled.
197
+ if (permissionName .equals (Manifest .permission .READ_CALENDAR ) || permissionName .equals (Manifest .permission .WRITE_CALENDAR )) {
198
+ continue ;
199
+ }
200
+
171
201
@ PermissionConstants .PermissionGroup final int permission =
172
202
PermissionUtils .parseManifestName (permissionName );
173
203
@@ -378,6 +408,17 @@ void requestPermissions(
378
408
launchSpecialPermission (
379
409
Settings .ACTION_REQUEST_SCHEDULE_EXACT_ALARM ,
380
410
PermissionConstants .PERMISSION_CODE_SCHEDULE_EXACT_ALARM );
411
+ } else if (permission == PermissionConstants .PERMISSION_GROUP_CALENDAR_FULL_ACCESS || permission == PermissionConstants .PERMISSION_GROUP_CALENDAR ) {
412
+ // Deny CALENDAR_FULL_ACCESS permission if manifest is not listing both read- and write permissions.
413
+ // Otherwise, we will only ask for READ permission and think full access is granted.
414
+ final boolean isValidManifest = isValidManifestForCalendarFullAccess ();
415
+ if (isValidManifest ) {
416
+ permissionsToRequest .add (Manifest .permission .READ_CALENDAR );
417
+ permissionsToRequest .add (Manifest .permission .WRITE_CALENDAR );
418
+ pendingRequestCount += 2 ;
419
+ } else {
420
+ requestResults .put (permission , PermissionConstants .PERMISSION_STATUS_DENIED );
421
+ }
381
422
} else {
382
423
permissionsToRequest .addAll (names );
383
424
pendingRequestCount += names .size ();
@@ -418,6 +459,13 @@ private int determinePermissionStatus(final @PermissionConstants.PermissionGroup
418
459
}
419
460
}
420
461
462
+ // Inspect the manifest for CALENDAR_FULL_ACCESS, as a misconfigured manifest will give a false positive if READ access has been provided.
463
+ if (permission == PermissionConstants .PERMISSION_GROUP_CALENDAR_FULL_ACCESS || permission == PermissionConstants .PERMISSION_GROUP_CALENDAR ) {
464
+ boolean isValidManifest = isValidManifestForCalendarFullAccess ();
465
+ if (!isValidManifest )
466
+ return PermissionConstants .PERMISSION_STATUS_DENIED ;
467
+ }
468
+
421
469
final List <String > names = PermissionUtils .getManifestNames (context , permission );
422
470
423
471
if (names == null ) {
@@ -451,24 +499,19 @@ private int determinePermissionStatus(final @PermissionConstants.PermissionGroup
451
499
: PermissionConstants .PERMISSION_STATUS_DENIED ;
452
500
}
453
501
454
- final boolean targetsMOrHigher = context .getApplicationInfo ().targetSdkVersion >= Build .VERSION_CODES .M ;
502
+ final boolean requiresExplicitPermission = context .getApplicationInfo ().targetSdkVersion >= Build .VERSION_CODES .M ;
503
+
504
+ if (requiresExplicitPermission ) {
505
+ final Set <@ PermissionConstants .PermissionStatus Integer > permissionStatuses = new HashSet <>();
455
506
456
- Set <@ PermissionConstants .PermissionStatus Integer > permissionStatuses = new HashSet <>();
457
- for (String name : names ) {
458
- // Only handle them if the client app actually targets a API level greater than M.
459
- if (targetsMOrHigher ) {
507
+ for (String name : names ) {
460
508
if (permission == PermissionConstants .PERMISSION_GROUP_IGNORE_BATTERY_OPTIMIZATIONS ) {
461
509
String packageName = context .getPackageName ();
462
510
PowerManager pm = (PowerManager ) context .getSystemService (Context .POWER_SERVICE );
463
- // PowerManager.isIgnoringBatteryOptimizations has been included in Android M first.
464
- if (Build .VERSION .SDK_INT >= Build .VERSION_CODES .M ) {
465
- if (pm != null && pm .isIgnoringBatteryOptimizations (packageName )) {
466
- permissionStatuses .add (PermissionConstants .PERMISSION_STATUS_GRANTED );
467
- } else {
468
- permissionStatuses .add (PermissionConstants .PERMISSION_STATUS_DENIED );
469
- }
511
+ if (pm != null && pm .isIgnoringBatteryOptimizations (packageName )) {
512
+ permissionStatuses .add (PermissionConstants .PERMISSION_STATUS_GRANTED );
470
513
} else {
471
- permissionStatuses .add (PermissionConstants .PERMISSION_STATUS_RESTRICTED );
514
+ permissionStatuses .add (PermissionConstants .PERMISSION_STATUS_DENIED );
472
515
}
473
516
} else if (permission == PermissionConstants .PERMISSION_GROUP_MANAGE_EXTERNAL_STORAGE ) {
474
517
if (Build .VERSION .SDK_INT < Build .VERSION_CODES .R ) {
@@ -480,12 +523,10 @@ private int determinePermissionStatus(final @PermissionConstants.PermissionGroup
480
523
: PermissionConstants .PERMISSION_STATUS_DENIED ;
481
524
permissionStatuses .add (status );
482
525
} else if (permission == PermissionConstants .PERMISSION_GROUP_SYSTEM_ALERT_WINDOW ) {
483
- if (Build .VERSION .SDK_INT >= Build .VERSION_CODES .M ) {
484
- int status = Settings .canDrawOverlays (context )
485
- ? PermissionConstants .PERMISSION_STATUS_GRANTED
486
- : PermissionConstants .PERMISSION_STATUS_DENIED ;
487
- permissionStatuses .add (status );
488
- }
526
+ int status = Settings .canDrawOverlays (context )
527
+ ? PermissionConstants .PERMISSION_STATUS_GRANTED
528
+ : PermissionConstants .PERMISSION_STATUS_DENIED ;
529
+ permissionStatuses .add (status );
489
530
} else if (permission == PermissionConstants .PERMISSION_GROUP_REQUEST_INSTALL_PACKAGES ) {
490
531
if (Build .VERSION .SDK_INT >= Build .VERSION_CODES .O ) {
491
532
int status = context .getPackageManager ().canRequestPackageInstalls ()
@@ -494,13 +535,11 @@ private int determinePermissionStatus(final @PermissionConstants.PermissionGroup
494
535
permissionStatuses .add (status );
495
536
}
496
537
} else if (permission == PermissionConstants .PERMISSION_GROUP_ACCESS_NOTIFICATION_POLICY ) {
497
- if (Build .VERSION .SDK_INT >= Build .VERSION_CODES .M ) {
498
- NotificationManager notificationManager = (NotificationManager ) context .getSystemService (Application .NOTIFICATION_SERVICE );
499
- int status = notificationManager .isNotificationPolicyAccessGranted ()
500
- ? PermissionConstants .PERMISSION_STATUS_GRANTED
501
- : PermissionConstants .PERMISSION_STATUS_DENIED ;
502
- permissionStatuses .add (status );
503
- }
538
+ NotificationManager notificationManager = (NotificationManager ) context .getSystemService (Application .NOTIFICATION_SERVICE );
539
+ int status = notificationManager .isNotificationPolicyAccessGranted ()
540
+ ? PermissionConstants .PERMISSION_STATUS_GRANTED
541
+ : PermissionConstants .PERMISSION_STATUS_DENIED ;
542
+ permissionStatuses .add (status );
504
543
} else if (permission == PermissionConstants .PERMISSION_GROUP_SCHEDULE_EXACT_ALARM ) {
505
544
if (Build .VERSION .SDK_INT >= Build .VERSION_CODES .S ) {
506
545
AlarmManager alarmManager = (AlarmManager ) context .getSystemService (Context .ALARM_SERVICE );
@@ -518,11 +557,11 @@ private int determinePermissionStatus(final @PermissionConstants.PermissionGroup
518
557
}
519
558
}
520
559
}
560
+ if (!permissionStatuses .isEmpty ()) {
561
+ return strictestStatus (permissionStatuses );
562
+ }
521
563
}
522
564
523
- if (!permissionStatuses .isEmpty ()) {
524
- return strictestStatus (permissionStatuses );
525
- }
526
565
return PermissionConstants .PERMISSION_STATUS_GRANTED ;
527
566
}
528
567
@@ -609,4 +648,23 @@ private int checkBluetoothPermissionStatus() {
609
648
}
610
649
return PermissionConstants .PERMISSION_STATUS_GRANTED ;
611
650
}
651
+
652
+ /**
653
+ * Checks if the manifest contains both {@link Manifest.permission#READ_CALENDAR} and
654
+ * {@link Manifest.permission#WRITE_CALENDAR} permission declarations.
655
+ */
656
+ private boolean isValidManifestForCalendarFullAccess () {
657
+ List <String > names = PermissionUtils .getManifestNames (context , PermissionConstants .PERMISSION_GROUP_CALENDAR_FULL_ACCESS );
658
+ final boolean readInManifest = names != null && names .contains (Manifest .permission .READ_CALENDAR );
659
+ final boolean writeInManifest = names != null && names .contains (Manifest .permission .WRITE_CALENDAR );
660
+ final boolean validManifest = readInManifest && writeInManifest ;
661
+ if (!validManifest ) {
662
+ if (!readInManifest )
663
+ Log .d (PermissionConstants .LOG_TAG , Manifest .permission .READ_CALENDAR + " missing in manifest" );
664
+ if (!writeInManifest )
665
+ Log .d (PermissionConstants .LOG_TAG , Manifest .permission .WRITE_CALENDAR + " missing in manifest" );
666
+ return false ;
667
+ }
668
+ return true ;
669
+ }
612
670
}
0 commit comments