diff --git a/domain/src/main/java/org/oppia/android/domain/profile/ProfileManagementController.kt b/domain/src/main/java/org/oppia/android/domain/profile/ProfileManagementController.kt index a94c037166e..6f50444ec88 100644 --- a/domain/src/main/java/org/oppia/android/domain/profile/ProfileManagementController.kt +++ b/domain/src/main/java/org/oppia/android/domain/profile/ProfileManagementController.kt @@ -95,6 +95,9 @@ class ProfileManagementController @Inject constructor( /** Indicates that the given profileId is not associated with an admin. */ class ProfileNotAdminException(msg: String) : Exception(msg) + /** Indicates that the Profile already has admin. */ + class ProfileAlreadyHasAdminException(msg: String) : Exception(msg) + /** Indicates that the there is not device settings currently. */ class DeviceSettingsNotFoundException(msg: String) : Exception(msg) @@ -112,7 +115,8 @@ class ProfileManagementController @Inject constructor( FAILED_TO_GENERATE_GRAVATAR, FAILED_TO_DELETE_DIR, PROFILE_NOT_FOUND, - PROFILE_NOT_ADMIN + PROFILE_NOT_ADMIN, + PROFILE_ALREADY_HAS_ADMIN } // TODO(#272): Remove init block when storeDataAsync is fixed @@ -199,6 +203,12 @@ class ProfileManagementController @Inject constructor( if (!isNameUnique(name, it)) { return@storeDataWithCustomChannelAsync Pair(it, ProfileActionStatus.PROFILE_NAME_NOT_UNIQUE) } + if (isAdmin && alreadyHasAdmin(it)) { + return@storeDataWithCustomChannelAsync Pair( + it, + ProfileActionStatus.PROFILE_ALREADY_HAS_ADMIN + ) + } val nextProfileId = it.nextProfileId val profileDir = directoryManagementUtil.getOrCreateDir(nextProfileId.toString()) @@ -662,6 +672,11 @@ class ProfileManagementController @Inject constructor( "ProfileId ${profileId?.internalId} does not match an existing admin" ) ) + ProfileActionStatus.PROFILE_ALREADY_HAS_ADMIN -> AsyncResult.failed( + ProfileAlreadyHasAdminException( + "Profile cannot be an admin" + ) + ) } } @@ -675,6 +690,15 @@ class ProfileManagementController @Inject constructor( return true } + private fun alreadyHasAdmin(profileDatabase: ProfileDatabase): Boolean { + profileDatabase.profilesMap.values.forEach { + if (it.isAdmin) { + return true + } + } + return false + } + private fun saveImageToInternalStorage(avatarImagePath: Uri, profileDir: File): String? { val bitmap = MediaStore.Images.Media.getBitmap(context.contentResolver, avatarImagePath) val fileName = avatarImagePath.pathSegments.last() diff --git a/domain/src/test/java/org/oppia/android/domain/profile/ProfileManagementControllerTest.kt b/domain/src/test/java/org/oppia/android/domain/profile/ProfileManagementControllerTest.kt index 51eef4ffcaa..2cc711db970 100644 --- a/domain/src/test/java/org/oppia/android/domain/profile/ProfileManagementControllerTest.kt +++ b/domain/src/test/java/org/oppia/android/domain/profile/ProfileManagementControllerTest.kt @@ -729,6 +729,33 @@ class ProfileManagementControllerTest { assertThat(wasProfileEverAdded).isTrue() } + @Test + fun testAddAdminProfile_addAnotherAdminProfile_checkSecondAdminProfileWasNotAdded() { + profileManagementController.addProfile( + name = "Rohit", + pin = "12345", + avatarImagePath = null, + allowDownloadAccess = true, + colorRgb = -10710042, + isAdmin = true + ).toLiveData().observeForever(mockUpdateResultObserver) + testCoroutineDispatchers.runCurrent() + + profileManagementController.addProfile( + name = "Ben", + pin = "12345", + avatarImagePath = null, + allowDownloadAccess = true, + colorRgb = -10710042, + isAdmin = true + ).toLiveData().observeForever(mockUpdateResultObserver) + testCoroutineDispatchers.runCurrent() + + verifyUpdateFailed() + assertThat(updateResultCaptor.value.getErrorOrNull()).hasMessageThat() + .contains("Profile cannot be an admin") + } + @Test fun testDeviceSettings_addAdminProfile_getDefaultDeviceSettings_isSuccessful() { profileManagementController.addProfile(