From cbccb8d2ccb92a76ce61766b4cd7f50b31785f6f Mon Sep 17 00:00:00 2001 From: Tamas Kozmer Date: Tue, 11 Apr 2023 12:59:32 +0200 Subject: [PATCH 001/103] Updated version --- apps/student/build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/student/build.gradle b/apps/student/build.gradle index 9ee7e0e952..3ecaad241f 100644 --- a/apps/student/build.gradle +++ b/apps/student/build.gradle @@ -50,8 +50,8 @@ android { applicationId "com.instructure.candroid" minSdkVersion Versions.MIN_SDK targetSdkVersion Versions.TARGET_SDK - versionCode = 248 - versionName = '6.22.0' + versionCode = 249 + versionName = '6.23.0' vectorDrawables.useSupportLibrary = true multiDexEnabled = true From d0f13ccb8cba0be15721492e294fb138196cefe4 Mon Sep 17 00:00:00 2001 From: Tamas Kozmer Date: Tue, 11 Apr 2023 14:04:04 +0200 Subject: [PATCH 002/103] Downgrade Firebase Crashlytics plugin version. --- buildSrc/src/main/java/GlobalDependencies.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/buildSrc/src/main/java/GlobalDependencies.kt b/buildSrc/src/main/java/GlobalDependencies.kt index 7202c63448..ba841a914f 100644 --- a/buildSrc/src/main/java/GlobalDependencies.kt +++ b/buildSrc/src/main/java/GlobalDependencies.kt @@ -160,7 +160,7 @@ object Libs { } object Plugins { - const val FIREBASE_CRASHLYTICS = "com.google.firebase:firebase-crashlytics-gradle:2.9.4" + const val FIREBASE_CRASHLYTICS = "com.google.firebase:firebase-crashlytics-gradle:2.9.2" const val ANDROID_GRADLE_TOOLS = "com.android.tools.build:gradle:${Versions.ANDROID_GRADLE_TOOLS}" const val APOLLO = "com.apollographql.apollo:apollo-gradle-plugin:${Versions.APOLLO}" const val KOTLIN = "org.jetbrains.kotlin:kotlin-gradle-plugin:${Versions.KOTLIN}" From e1abadd93e626dbcb0a8dafed02c9b203788165f Mon Sep 17 00:00:00 2001 From: Tamas Kozmer Date: Tue, 11 Apr 2023 15:16:38 +0200 Subject: [PATCH 003/103] Fixed landscape/tablet tests. --- .../student/ui/interaction/InAppUpdateInteractionTest.kt | 2 ++ .../com/instructure/canvas/espresso/mockCanvas/MockCanvas.kt | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/apps/student/src/androidTest/java/com/instructure/student/ui/interaction/InAppUpdateInteractionTest.kt b/apps/student/src/androidTest/java/com/instructure/student/ui/interaction/InAppUpdateInteractionTest.kt index 75bd92c7fb..f9d0bd8da6 100644 --- a/apps/student/src/androidTest/java/com/instructure/student/ui/interaction/InAppUpdateInteractionTest.kt +++ b/apps/student/src/androidTest/java/com/instructure/student/ui/interaction/InAppUpdateInteractionTest.kt @@ -23,6 +23,7 @@ import androidx.test.uiautomator.By import androidx.test.uiautomator.UiDevice import androidx.test.uiautomator.Until import com.google.android.play.core.appupdate.testing.FakeAppUpdateManager +import com.instructure.canvas.espresso.StubTablet import com.instructure.canvas.espresso.mockCanvas.MockCanvas import com.instructure.canvas.espresso.mockCanvas.init import com.instructure.canvasapi2.utils.toApiString @@ -268,6 +269,7 @@ class InAppUpdateInteractionTest : StudentTest() { } @Test + @StubTablet("Fails on Nexus 7 API level 26, phone version works correctly") fun flexibleUpdateCompletesIfAppRestarts() { with(appUpdateManager) { setUpdateAvailable(400) diff --git a/automation/espresso/src/main/kotlin/com/instructure/canvas/espresso/mockCanvas/MockCanvas.kt b/automation/espresso/src/main/kotlin/com/instructure/canvas/espresso/mockCanvas/MockCanvas.kt index 6bbf5d8b56..d0e4f46f42 100644 --- a/automation/espresso/src/main/kotlin/com/instructure/canvas/espresso/mockCanvas/MockCanvas.kt +++ b/automation/espresso/src/main/kotlin/com/instructure/canvas/espresso/mockCanvas/MockCanvas.kt @@ -1086,7 +1086,7 @@ fun MockCanvas.addUser(): User { val name = Randomizer.randomName() val email = Randomizer.randomEmail() val user = User( - id = users.size + 1L, + id = Random().nextLong(), name = name.fullName, shortName = name.firstName, loginId = email, From 1af36dba8b8f9cf0b6235bd8de674284c931ec6d Mon Sep 17 00:00:00 2001 From: Tamas Kozmer Date: Wed, 12 Apr 2023 07:59:10 +0200 Subject: [PATCH 004/103] Fixed Inbox E2E test. --- .../com/instructure/student/ui/e2e/InboxE2ETest.kt | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/apps/student/src/androidTest/java/com/instructure/student/ui/e2e/InboxE2ETest.kt b/apps/student/src/androidTest/java/com/instructure/student/ui/e2e/InboxE2ETest.kt index faf671fd66..f4bb8e96c4 100644 --- a/apps/student/src/androidTest/java/com/instructure/student/ui/e2e/InboxE2ETest.kt +++ b/apps/student/src/androidTest/java/com/instructure/student/ui/e2e/InboxE2ETest.kt @@ -98,7 +98,7 @@ class InboxE2ETest: StudentTest() { Log.d(STEP_TAG,"Click on 'Send' button.") newMessagePage.clickSend() - sleep(3000) // Allow time for messages to propagate + sleep(2000) // Allow time for messages to propagate Log.d(STEP_TAG,"Navigate back to Dashboard Page.") inboxPage.goToDashboard() @@ -174,6 +174,8 @@ class InboxE2ETest: StudentTest() { inboxPage.clickUnArchive() inboxPage.assertConversationNotDisplayed(seededConversation.subject) + sleep(2000) + Log.d(STEP_TAG,"Navigate to 'INBOX' scope and assert that ${seededConversation.subject} conversation is displayed.") inboxPage.filterInbox("Inbox") inboxPage.assertConversationDisplayed(seededConversation.subject) @@ -200,6 +202,8 @@ class InboxE2ETest: StudentTest() { inboxPage.assertConversationNotDisplayed(seededConversation.subject) inboxPage.assertConversationNotDisplayed(newGroupMessageSubject) + sleep(2000) + Log.d(STEP_TAG, "Navigate to 'ARCHIVED' scope and assert that both of the conversations are displayed there.") inboxPage.filterInbox("Archived") inboxPage.assertConversationDisplayed(seededConversation.subject) @@ -221,6 +225,8 @@ class InboxE2ETest: StudentTest() { inboxPage.assertConversationNotDisplayed(seededConversation.subject) inboxPage.assertConversationNotDisplayed(newGroupMessageSubject) + sleep(2000) + Log.d(STEP_TAG, "Navigate to 'ARCHIVED' scope and assert that both of the conversations are displayed there.") inboxPage.filterInbox("Archived") inboxPage.assertConversationDisplayed(seededConversation.subject) @@ -232,6 +238,8 @@ class InboxE2ETest: StudentTest() { inboxPage.assertConversationNotDisplayed(seededConversation.subject) inboxPage.assertConversationNotDisplayed(newGroupMessageSubject) + sleep(2000) + Log.d(STEP_TAG, "Navigate to 'INBOX' scope and assert that both of the conversations are displayed there.") inboxPage.filterInbox("Inbox") inboxPage.assertConversationDisplayed(seededConversation.subject) @@ -268,6 +276,8 @@ class InboxE2ETest: StudentTest() { inboxPage.assertSelectedConversationNumber("2") inboxPage.clickMarkAsUnread() + sleep(1000) + Log.d(STEP_TAG, "Navigate to 'STARRED' scope. Assert that both of the conversation are displayed in the 'STARRED' scope.") inboxPage.filterInbox("Starred") inboxPage.assertConversationDisplayed(seededConversation.subject) From 247411a27027c4f5ba3b4b5e05c2f8b70b407301 Mon Sep 17 00:00:00 2001 From: Tamas Kozmer Date: Wed, 12 Apr 2023 10:43:31 +0200 Subject: [PATCH 005/103] Fixed db migration. --- .../com/instructure/pandautils/room/AppDatabaseMigrations.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libs/pandautils/src/main/java/com/instructure/pandautils/room/AppDatabaseMigrations.kt b/libs/pandautils/src/main/java/com/instructure/pandautils/room/AppDatabaseMigrations.kt index fb5229ceda..307f84193b 100644 --- a/libs/pandautils/src/main/java/com/instructure/pandautils/room/AppDatabaseMigrations.kt +++ b/libs/pandautils/src/main/java/com/instructure/pandautils/room/AppDatabaseMigrations.kt @@ -37,7 +37,7 @@ val appDatabaseMigrations = arrayOf( }, createMigration(2, 3) { database -> - database.execSQL("CREATE TABLE IF NOT EXISTS DashboardFileUploadEntity (workerId TEXT NOT NULL, userId INTEGER NOT NULL, title TEXT, assignmentName TEXT, PRIMARY KEY(workerId))") + database.execSQL("CREATE TABLE IF NOT EXISTS DashboardFileUploadEntity (workerId TEXT NOT NULL, userId INTEGER NOT NULL, title TEXT, subtitle TEXT, PRIMARY KEY(workerId))") }, createMigration(3, 4) { database -> From 507b7215a3246b18f8c6eb44f16e7a7983467300 Mon Sep 17 00:00:00 2001 From: Tamas Kozmer Date: Wed, 12 Apr 2023 14:29:46 +0200 Subject: [PATCH 006/103] Fixed assignment list crash. --- .../com/instructure/student/fragment/AssignmentListFragment.kt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/apps/student/src/main/java/com/instructure/student/fragment/AssignmentListFragment.kt b/apps/student/src/main/java/com/instructure/student/fragment/AssignmentListFragment.kt index cede1cd23a..59d9c327e7 100644 --- a/apps/student/src/main/java/com/instructure/student/fragment/AssignmentListFragment.kt +++ b/apps/student/src/main/java/com/instructure/student/fragment/AssignmentListFragment.kt @@ -91,6 +91,7 @@ class AssignmentListFragment : ParentFragment(), Bookmarkable { private val adapterToAssignmentsCallback = object : AdapterToAssignmentsCallback { override fun assignmentLoadingFinished() { + if (view == null) return // If we only have one grading period we want to disable the spinner val termCount = termAdapter?.count ?: 0 binding.termSpinner.isEnabled = termCount > 1 @@ -99,6 +100,7 @@ class AssignmentListFragment : ParentFragment(), Bookmarkable { } override fun gradingPeriodsFetched(periods: List) { + if (view == null) return setupGradingPeriods(periods) } From d847e4ef06ba1d9ee7f8d0be781fa2386d504d21 Mon Sep 17 00:00:00 2001 From: Tamas Kozmer Date: Wed, 12 Apr 2023 16:48:40 +0200 Subject: [PATCH 007/103] Updated version. --- apps/teacher/build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/teacher/build.gradle b/apps/teacher/build.gradle index e686a38411..7c66b76d6b 100644 --- a/apps/teacher/build.gradle +++ b/apps/teacher/build.gradle @@ -41,8 +41,8 @@ android { defaultConfig { minSdkVersion Versions.MIN_SDK targetSdkVersion Versions.TARGET_SDK - versionCode = 55 - versionName = '1.22.0' + versionCode = 56 + versionName = '1.23.0' vectorDrawables.useSupportLibrary = true multiDexEnabled true testInstrumentationRunner 'com.instructure.teacher.ui.espresso.TeacherHiltTestRunner' From f2e825bd7bb947c39ba81dab56311b667bce649c Mon Sep 17 00:00:00 2001 From: Tamas Kozmer Date: Wed, 12 Apr 2023 16:51:41 +0200 Subject: [PATCH 008/103] Fixes in core codebase for issues discovered during Student release. --- .../com/instructure/student/ui/e2e/InboxE2ETest.kt | 12 +++++++++++- .../ui/interaction/InAppUpdateInteractionTest.kt | 2 ++ .../canvas/espresso/mockCanvas/MockCanvas.kt | 2 +- buildSrc/src/main/java/GlobalDependencies.kt | 2 +- .../pandautils/room/AppDatabaseMigrations.kt | 2 +- 5 files changed, 16 insertions(+), 4 deletions(-) diff --git a/apps/student/src/androidTest/java/com/instructure/student/ui/e2e/InboxE2ETest.kt b/apps/student/src/androidTest/java/com/instructure/student/ui/e2e/InboxE2ETest.kt index faf671fd66..f4bb8e96c4 100644 --- a/apps/student/src/androidTest/java/com/instructure/student/ui/e2e/InboxE2ETest.kt +++ b/apps/student/src/androidTest/java/com/instructure/student/ui/e2e/InboxE2ETest.kt @@ -98,7 +98,7 @@ class InboxE2ETest: StudentTest() { Log.d(STEP_TAG,"Click on 'Send' button.") newMessagePage.clickSend() - sleep(3000) // Allow time for messages to propagate + sleep(2000) // Allow time for messages to propagate Log.d(STEP_TAG,"Navigate back to Dashboard Page.") inboxPage.goToDashboard() @@ -174,6 +174,8 @@ class InboxE2ETest: StudentTest() { inboxPage.clickUnArchive() inboxPage.assertConversationNotDisplayed(seededConversation.subject) + sleep(2000) + Log.d(STEP_TAG,"Navigate to 'INBOX' scope and assert that ${seededConversation.subject} conversation is displayed.") inboxPage.filterInbox("Inbox") inboxPage.assertConversationDisplayed(seededConversation.subject) @@ -200,6 +202,8 @@ class InboxE2ETest: StudentTest() { inboxPage.assertConversationNotDisplayed(seededConversation.subject) inboxPage.assertConversationNotDisplayed(newGroupMessageSubject) + sleep(2000) + Log.d(STEP_TAG, "Navigate to 'ARCHIVED' scope and assert that both of the conversations are displayed there.") inboxPage.filterInbox("Archived") inboxPage.assertConversationDisplayed(seededConversation.subject) @@ -221,6 +225,8 @@ class InboxE2ETest: StudentTest() { inboxPage.assertConversationNotDisplayed(seededConversation.subject) inboxPage.assertConversationNotDisplayed(newGroupMessageSubject) + sleep(2000) + Log.d(STEP_TAG, "Navigate to 'ARCHIVED' scope and assert that both of the conversations are displayed there.") inboxPage.filterInbox("Archived") inboxPage.assertConversationDisplayed(seededConversation.subject) @@ -232,6 +238,8 @@ class InboxE2ETest: StudentTest() { inboxPage.assertConversationNotDisplayed(seededConversation.subject) inboxPage.assertConversationNotDisplayed(newGroupMessageSubject) + sleep(2000) + Log.d(STEP_TAG, "Navigate to 'INBOX' scope and assert that both of the conversations are displayed there.") inboxPage.filterInbox("Inbox") inboxPage.assertConversationDisplayed(seededConversation.subject) @@ -268,6 +276,8 @@ class InboxE2ETest: StudentTest() { inboxPage.assertSelectedConversationNumber("2") inboxPage.clickMarkAsUnread() + sleep(1000) + Log.d(STEP_TAG, "Navigate to 'STARRED' scope. Assert that both of the conversation are displayed in the 'STARRED' scope.") inboxPage.filterInbox("Starred") inboxPage.assertConversationDisplayed(seededConversation.subject) diff --git a/apps/student/src/androidTest/java/com/instructure/student/ui/interaction/InAppUpdateInteractionTest.kt b/apps/student/src/androidTest/java/com/instructure/student/ui/interaction/InAppUpdateInteractionTest.kt index 75bd92c7fb..f9d0bd8da6 100644 --- a/apps/student/src/androidTest/java/com/instructure/student/ui/interaction/InAppUpdateInteractionTest.kt +++ b/apps/student/src/androidTest/java/com/instructure/student/ui/interaction/InAppUpdateInteractionTest.kt @@ -23,6 +23,7 @@ import androidx.test.uiautomator.By import androidx.test.uiautomator.UiDevice import androidx.test.uiautomator.Until import com.google.android.play.core.appupdate.testing.FakeAppUpdateManager +import com.instructure.canvas.espresso.StubTablet import com.instructure.canvas.espresso.mockCanvas.MockCanvas import com.instructure.canvas.espresso.mockCanvas.init import com.instructure.canvasapi2.utils.toApiString @@ -268,6 +269,7 @@ class InAppUpdateInteractionTest : StudentTest() { } @Test + @StubTablet("Fails on Nexus 7 API level 26, phone version works correctly") fun flexibleUpdateCompletesIfAppRestarts() { with(appUpdateManager) { setUpdateAvailable(400) diff --git a/automation/espresso/src/main/kotlin/com/instructure/canvas/espresso/mockCanvas/MockCanvas.kt b/automation/espresso/src/main/kotlin/com/instructure/canvas/espresso/mockCanvas/MockCanvas.kt index 6bbf5d8b56..d0e4f46f42 100644 --- a/automation/espresso/src/main/kotlin/com/instructure/canvas/espresso/mockCanvas/MockCanvas.kt +++ b/automation/espresso/src/main/kotlin/com/instructure/canvas/espresso/mockCanvas/MockCanvas.kt @@ -1086,7 +1086,7 @@ fun MockCanvas.addUser(): User { val name = Randomizer.randomName() val email = Randomizer.randomEmail() val user = User( - id = users.size + 1L, + id = Random().nextLong(), name = name.fullName, shortName = name.firstName, loginId = email, diff --git a/buildSrc/src/main/java/GlobalDependencies.kt b/buildSrc/src/main/java/GlobalDependencies.kt index 7202c63448..ba841a914f 100644 --- a/buildSrc/src/main/java/GlobalDependencies.kt +++ b/buildSrc/src/main/java/GlobalDependencies.kt @@ -160,7 +160,7 @@ object Libs { } object Plugins { - const val FIREBASE_CRASHLYTICS = "com.google.firebase:firebase-crashlytics-gradle:2.9.4" + const val FIREBASE_CRASHLYTICS = "com.google.firebase:firebase-crashlytics-gradle:2.9.2" const val ANDROID_GRADLE_TOOLS = "com.android.tools.build:gradle:${Versions.ANDROID_GRADLE_TOOLS}" const val APOLLO = "com.apollographql.apollo:apollo-gradle-plugin:${Versions.APOLLO}" const val KOTLIN = "org.jetbrains.kotlin:kotlin-gradle-plugin:${Versions.KOTLIN}" diff --git a/libs/pandautils/src/main/java/com/instructure/pandautils/room/AppDatabaseMigrations.kt b/libs/pandautils/src/main/java/com/instructure/pandautils/room/AppDatabaseMigrations.kt index fb5229ceda..307f84193b 100644 --- a/libs/pandautils/src/main/java/com/instructure/pandautils/room/AppDatabaseMigrations.kt +++ b/libs/pandautils/src/main/java/com/instructure/pandautils/room/AppDatabaseMigrations.kt @@ -37,7 +37,7 @@ val appDatabaseMigrations = arrayOf( }, createMigration(2, 3) { database -> - database.execSQL("CREATE TABLE IF NOT EXISTS DashboardFileUploadEntity (workerId TEXT NOT NULL, userId INTEGER NOT NULL, title TEXT, assignmentName TEXT, PRIMARY KEY(workerId))") + database.execSQL("CREATE TABLE IF NOT EXISTS DashboardFileUploadEntity (workerId TEXT NOT NULL, userId INTEGER NOT NULL, title TEXT, subtitle TEXT, PRIMARY KEY(workerId))") }, createMigration(3, 4) { database -> From 426fa5393d9a76ab496a7bc7958fd78d1fdc1ace Mon Sep 17 00:00:00 2001 From: Tamas Kozmer Date: Fri, 14 Apr 2023 14:36:00 +0200 Subject: [PATCH 009/103] Updated version. --- apps/student/build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/student/build.gradle b/apps/student/build.gradle index 3ecaad241f..fb269772e7 100644 --- a/apps/student/build.gradle +++ b/apps/student/build.gradle @@ -50,8 +50,8 @@ android { applicationId "com.instructure.candroid" minSdkVersion Versions.MIN_SDK targetSdkVersion Versions.TARGET_SDK - versionCode = 249 - versionName = '6.23.0' + versionCode = 250 + versionName = '6.23.1' vectorDrawables.useSupportLibrary = true multiDexEnabled = true From 721e26e078fd68aaf3a3b192a156ab50993efe63 Mon Sep 17 00:00:00 2001 From: Tamas Kozmer Date: Fri, 14 Apr 2023 15:35:26 +0200 Subject: [PATCH 010/103] Fixed crash on tablet. --- .../com/instructure/teacher/fragments/ViewImageFragment.kt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/apps/teacher/src/main/java/com/instructure/teacher/fragments/ViewImageFragment.kt b/apps/teacher/src/main/java/com/instructure/teacher/fragments/ViewImageFragment.kt index 3ee57fa68b..fc10c97fcf 100644 --- a/apps/teacher/src/main/java/com/instructure/teacher/fragments/ViewImageFragment.kt +++ b/apps/teacher/src/main/java/com/instructure/teacher/fragments/ViewImageFragment.kt @@ -144,7 +144,9 @@ class ViewImageFragment : Fragment(), ShareableFile { fun colorBackground(bitmap: Bitmap) { // Generate palette asynchronously Palette.from(bitmap).generate { palette -> - palette?.let { binding.viewImageRootView.setBackgroundColor(it.getDarkMutedColor(requireContext().getColor(R.color.backgroundLightest))) } + if (view != null && palette != null) { + binding.viewImageRootView.setBackgroundColor(palette.getDarkMutedColor(requireContext().getColor(R.color.backgroundLightest))) + } } } From 466fab05b7d415d39f11ace6f26cb02fb0980c73 Mon Sep 17 00:00:00 2001 From: Tamas Kozmer <72397075+tamaskozmer@users.noreply.github.com> Date: Fri, 14 Apr 2023 16:37:48 +0200 Subject: [PATCH 011/103] [MBL-16676][Student] Fix Student crashes (#1935) refs: MBL-16676 affects: Student release note: none * Fixed discussion reply attachment crash. * Fixed InternalWebviewFragment crash. * Fixed module progression crash. * Fixed assignment details crash. * Fixed inbox conversation crash. * Fixed document scanner crash. * Fixed modules crash. --- .../AssignmentDetailsViewModel.kt | 2 +- .../student/fragment/BasicQuizViewFragment.kt | 4 +- .../CourseModuleProgressionFragment.kt | 6 +- .../fragment/InboxConversationFragment.kt | 2 +- .../fragment/InternalWebviewFragment.kt | 2 +- .../student/fragment/StudioWebViewFragment.kt | 4 +- .../content/QuizSubmissionViewFragment.kt | 6 +- .../student/view/AttachmentDogEarLayout.kt | 120 ------------------ .../src/main/res/layout/view_attachment.xml | 4 +- .../ui/imagecrop/ImageCropFragment.kt | 2 +- 10 files changed, 16 insertions(+), 136 deletions(-) delete mode 100644 apps/student/src/main/java/com/instructure/student/view/AttachmentDogEarLayout.kt diff --git a/apps/student/src/main/java/com/instructure/student/features/assignmentdetails/AssignmentDetailsViewModel.kt b/apps/student/src/main/java/com/instructure/student/features/assignmentdetails/AssignmentDetailsViewModel.kt index 079aa44a6e..7c2c0ab375 100644 --- a/apps/student/src/main/java/com/instructure/student/features/assignmentdetails/AssignmentDetailsViewModel.kt +++ b/apps/student/src/main/java/com/instructure/student/features/assignmentdetails/AssignmentDetailsViewModel.kt @@ -131,7 +131,7 @@ class AssignmentDetailsViewModel @Inject constructor( } if (isUploading && submission.errorFlag) { _data.value?.attempts = attempts?.toMutableList()?.apply { - removeFirst() + if (isNotEmpty()) removeFirst() add(0, AssignmentDetailsAttemptItemViewModel( AssignmentDetailsAttemptViewData( resources.getString(R.string.attempt, attempts.size), diff --git a/apps/student/src/main/java/com/instructure/student/fragment/BasicQuizViewFragment.kt b/apps/student/src/main/java/com/instructure/student/fragment/BasicQuizViewFragment.kt index 0a46c75d7f..d6f59bd084 100644 --- a/apps/student/src/main/java/com/instructure/student/fragment/BasicQuizViewFragment.kt +++ b/apps/student/src/main/java/com/instructure/student/fragment/BasicQuizViewFragment.kt @@ -130,12 +130,12 @@ class BasicQuizViewFragment : InternalWebviewFragment() { override fun onPageStarted(view: WebView, url: String, favicon: Bitmap?) { super.onPageStarted(view, url, favicon) - getCanvasLoading().visibility = View.VISIBLE + getCanvasLoading()?.visibility = View.VISIBLE } override fun onPageFinished(view: WebView, url: String) { super.onPageFinished(view, url) - getCanvasLoading().visibility = View.GONE + getCanvasLoading()?.visibility = View.GONE } } } diff --git a/apps/student/src/main/java/com/instructure/student/fragment/CourseModuleProgressionFragment.kt b/apps/student/src/main/java/com/instructure/student/fragment/CourseModuleProgressionFragment.kt index 737350ce00..9140c858f7 100644 --- a/apps/student/src/main/java/com/instructure/student/fragment/CourseModuleProgressionFragment.kt +++ b/apps/student/src/main/java/com/instructure/student/fragment/CourseModuleProgressionFragment.kt @@ -142,8 +142,8 @@ class CourseModuleProgressionFragment : ParentFragment(), Bookmarkable { // This function is mostly for the internal web view fragments so we can go back in the webview override fun handleBackPressed(): Boolean = with(binding) { if (items.isNotEmpty()) { - val pFrag = childFragmentManager.fragments[0] as ParentFragment - if (pFrag.handleBackPressed()) { + val pFrag = childFragmentManager.fragments.firstOrNull() as? ParentFragment + if (pFrag?.handleBackPressed() == true) { return true } } @@ -269,7 +269,7 @@ class CourseModuleProgressionFragment : ParentFragment(), Bookmarkable { private fun showFragment(item: Fragment?) { item?.let { - childFragmentManager.beginTransaction().replace(R.id.fragmentContainer, it).commit() + childFragmentManager.beginTransaction().replace(R.id.fragmentContainer, it).commitAllowingStateLoss() applyFragmentTheme(it) } } diff --git a/apps/student/src/main/java/com/instructure/student/fragment/InboxConversationFragment.kt b/apps/student/src/main/java/com/instructure/student/fragment/InboxConversationFragment.kt index f903f9db9b..8ec882d009 100644 --- a/apps/student/src/main/java/com/instructure/student/fragment/InboxConversationFragment.kt +++ b/apps/student/src/main/java/com/instructure/student/fragment/InboxConversationFragment.kt @@ -401,7 +401,7 @@ class InboxConversationFragment : ParentFragment() { } private fun refreshConversationData() { - initConversationDetails() + if (view != null) initConversationDetails() } private fun onConversationUpdated(goBack: Boolean) { diff --git a/apps/student/src/main/java/com/instructure/student/fragment/InternalWebviewFragment.kt b/apps/student/src/main/java/com/instructure/student/fragment/InternalWebviewFragment.kt index 7d8368fcf6..9e1ec9cd9f 100644 --- a/apps/student/src/main/java/com/instructure/student/fragment/InternalWebviewFragment.kt +++ b/apps/student/src/main/java/com/instructure/student/fragment/InternalWebviewFragment.kt @@ -344,7 +344,7 @@ open class InternalWebviewFragment : ParentFragment() { } else false } - fun getCanvasLoading(): ProgressBar = binding.webViewLoading + fun getCanvasLoading(): ProgressBar? = if (view != null) binding.webViewLoading else null fun getCanvasWebView(): CanvasWebView = binding.canvasWebViewWrapper.webView fun getIsUnsupportedFeature(): Boolean = isUnsupportedFeature private fun getReferer(): Map = mutableMapOf(Pair("Referer", ApiPrefs.domain)) diff --git a/apps/student/src/main/java/com/instructure/student/fragment/StudioWebViewFragment.kt b/apps/student/src/main/java/com/instructure/student/fragment/StudioWebViewFragment.kt index 4360085506..038ebdbdff 100644 --- a/apps/student/src/main/java/com/instructure/student/fragment/StudioWebViewFragment.kt +++ b/apps/student/src/main/java/com/instructure/student/fragment/StudioWebViewFragment.kt @@ -59,7 +59,7 @@ class StudioWebViewFragment : InternalWebviewFragment() { } override fun onPageFinishedCallback(webView: WebView, url: String) { - getCanvasLoading().visibility = View.GONE + getCanvasLoading()?.visibility = View.GONE // Check for a successful Studio submission if (url.contains("success/external_tool_dialog")) { @@ -68,7 +68,7 @@ class StudioWebViewFragment : InternalWebviewFragment() { } override fun onPageStartedCallback(webView: WebView, url: String) { - getCanvasLoading().visibility = View.VISIBLE + getCanvasLoading()?.visibility = View.VISIBLE } override fun canRouteInternallyDelegate(url: String): Boolean { diff --git a/apps/student/src/main/java/com/instructure/student/mobius/assignmentDetails/submissionDetails/content/QuizSubmissionViewFragment.kt b/apps/student/src/main/java/com/instructure/student/mobius/assignmentDetails/submissionDetails/content/QuizSubmissionViewFragment.kt index 44b8720c51..19b4699405 100644 --- a/apps/student/src/main/java/com/instructure/student/mobius/assignmentDetails/submissionDetails/content/QuizSubmissionViewFragment.kt +++ b/apps/student/src/main/java/com/instructure/student/mobius/assignmentDetails/submissionDetails/content/QuizSubmissionViewFragment.kt @@ -28,7 +28,7 @@ import com.instructure.student.fragment.InternalWebviewFragment @ScreenView(SCREEN_VIEW_QUIZ_SUBMISSION_VIEW) class QuizSubmissionViewFragment : InternalWebviewFragment() { override fun onActivityCreated(savedInstanceState: Bundle?) { - getCanvasLoading().setVisible() // Set visible so we can test it + getCanvasLoading()?.setVisible() // Set visible so we can test it binding.canvasWebViewWrapper.apply { webView.enableAlgorithmicDarkening() webView.setInitialScale(100) @@ -40,10 +40,10 @@ class QuizSubmissionViewFragment : InternalWebviewFragment() { // Update visibilities if (newProgress >= 100) { - getCanvasLoading().setGone() + getCanvasLoading()?.setGone() setVisible() } else { - getCanvasLoading().announceForAccessibility(getString(R.string.loading)) + getCanvasLoading()?.announceForAccessibility(getString(R.string.loading)) } } } diff --git a/apps/student/src/main/java/com/instructure/student/view/AttachmentDogEarLayout.kt b/apps/student/src/main/java/com/instructure/student/view/AttachmentDogEarLayout.kt deleted file mode 100644 index ae0e03dd3f..0000000000 --- a/apps/student/src/main/java/com/instructure/student/view/AttachmentDogEarLayout.kt +++ /dev/null @@ -1,120 +0,0 @@ -/* - * Copyright (C) 2018 - present Instructure, Inc. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, version 3 of the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package com.instructure.student.view - -import android.content.Context -import android.graphics.* -import androidx.core.content.ContextCompat -import androidx.core.view.ViewCompat -import android.util.AttributeSet -import android.view.View -import android.widget.FrameLayout -import com.instructure.student.R -import com.instructure.pandautils.utils.DP -import com.instructure.pandautils.utils.obtainFor - -class AttachmentDogEarLayout @JvmOverloads constructor( - context: Context, - attrs: AttributeSet? = null, - defStyleAttr: Int = 0 -) : FrameLayout(context, attrs, defStyleAttr) { - - private var dogEarSize = 0f - - private val rtlFlipMatrix: Matrix by lazy { - Matrix().apply { postScale(-1f, 1f, width / 2f, 0f) } - } - - private val dogEarPaint: Paint by lazy { - Paint(Paint.ANTI_ALIAS_FLAG).apply { color = ContextCompat.getColor(context, R.color.backgroundMedium) } - } - - private val dogEarShadowPaint = Paint(Paint.ANTI_ALIAS_FLAG).apply { color = 0x33000000 } - - private val clipPath: Path by lazy { - Path().apply { - moveTo(0f, 0f) - lineTo(dogEarPoint.x, 0f) - lineTo(width.toFloat(), dogEarPoint.y) - lineTo(width.toFloat(), height.toFloat()) - lineTo(0f, height.toFloat()) - close() - flipForRtlIfNecessary(this) - } - } - - private val dogEarPath: Path by lazy { - Path().apply { - moveTo(dogEarPoint.x, -1f) - lineTo(width + 1f, dogEarPoint.y) - lineTo(dogEarPoint.x, dogEarPoint.y) - close() - flipForRtlIfNecessary(this) - } - } - - private val dogEarPoint: PointF by lazy { - PointF(width - dogEarSize, dogEarSize) - } - - private val dogEarShadowPath: Path by lazy { - Path().apply { - moveTo(dogEarPoint.x, -1f) - lineTo(width + 1f, dogEarPoint.y + 1) - lineTo(dogEarPoint.x + dogEarPoint.y * DOG_EAR_SHADOW_OFFSET_MULTIPLIER_X, - dogEarPoint.y + dogEarPoint.y * DOG_EAR_SHADOW_OFFSET_MULTIPLIER_Y) - close() - flipForRtlIfNecessary(this) - } - } - - init { - // In edit mode, only the software layer type supports non-rectangular path clipping - if (isInEditMode) setLayerType(View.LAYER_TYPE_SOFTWARE, null) - - // Set defaults and get any XML attributes - dogEarSize = context.DP(DOG_EAR_DIMEN_DP) - attrs?.obtainFor(this, R.styleable.AttachmentDogEarLayout) { a, idx -> - when (idx) { - R.styleable.AttachmentDogEarLayout_adl_dogear_size -> dogEarSize = a.getDimension(idx, dogEarSize) - } - } - } - - override fun dispatchDraw(canvas: Canvas) { - // Perform clip, draw children - canvas.save() - canvas.clipPath(clipPath) - super.dispatchDraw(canvas) - canvas.restore() - - // Draw dog-ear - canvas.drawPath(dogEarShadowPath, dogEarShadowPaint) - canvas.drawPath(dogEarPath, dogEarPaint) - } - - private fun flipForRtlIfNecessary(path: Path) { - if (ViewCompat.getLayoutDirection(this) == ViewCompat.LAYOUT_DIRECTION_RTL) { - path.transform(rtlFlipMatrix) - } - } - - companion object { - private const val DOG_EAR_DIMEN_DP = 20f - private const val DOG_EAR_SHADOW_OFFSET_MULTIPLIER_X = 0.08f - private const val DOG_EAR_SHADOW_OFFSET_MULTIPLIER_Y = 0.15f - } -} diff --git a/apps/student/src/main/res/layout/view_attachment.xml b/apps/student/src/main/res/layout/view_attachment.xml index 748dad14a8..50fee7f5c3 100644 --- a/apps/student/src/main/res/layout/view_attachment.xml +++ b/apps/student/src/main/res/layout/view_attachment.xml @@ -14,7 +14,7 @@ ~ You should have received a copy of the GNU General Public License ~ along with this program. If not, see . --> - - + diff --git a/libs/DocumentScanner/src/main/java/com/zynksoftware/documentscanner/ui/imagecrop/ImageCropFragment.kt b/libs/DocumentScanner/src/main/java/com/zynksoftware/documentscanner/ui/imagecrop/ImageCropFragment.kt index ae5543c4b4..8ab1ef6633 100644 --- a/libs/DocumentScanner/src/main/java/com/zynksoftware/documentscanner/ui/imagecrop/ImageCropFragment.kt +++ b/libs/DocumentScanner/src/main/java/com/zynksoftware/documentscanner/ui/imagecrop/ImageCropFragment.kt @@ -61,7 +61,7 @@ internal class ImageCropFragment : BaseFragment(Fragme } } binding.holderImageView.post { - initializeCropping() + if (this.view != null) initializeCropping() } initListeners() From f58ee6382912fdd602cab4043a759cb1118561a9 Mon Sep 17 00:00:00 2001 From: Tamas Kozmer Date: Tue, 18 Apr 2023 13:54:12 +0200 Subject: [PATCH 012/103] Fixed smoke test findings. --- .../teacher/dialog/SectionPickerDialog.kt | 11 +++--- .../fragments/QuizPreviewWebviewFragment.kt | 2 +- .../presenters/DiscussionsReplyPresenter.kt | 3 +- .../pandautils/binding/ViewBindingDelegate.kt | 36 ++++++++++--------- 4 files changed, 27 insertions(+), 25 deletions(-) diff --git a/apps/teacher/src/main/java/com/instructure/teacher/dialog/SectionPickerDialog.kt b/apps/teacher/src/main/java/com/instructure/teacher/dialog/SectionPickerDialog.kt index 705a2a1f56..e0f335dbcf 100644 --- a/apps/teacher/src/main/java/com/instructure/teacher/dialog/SectionPickerDialog.kt +++ b/apps/teacher/src/main/java/com/instructure/teacher/dialog/SectionPickerDialog.kt @@ -122,12 +122,10 @@ class SectionRecyclerViewAdapter( private val updatedCallback: (MutableList
) -> Unit ) : RecyclerView.Adapter() { - private lateinit var binding: ViewSectionListItemBinding - override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): SectionViewHolder { - binding = ViewSectionListItemBinding.inflate(LayoutInflater.from(parent.context), parent, false) + val binding = ViewSectionListItemBinding.inflate(LayoutInflater.from(parent.context), parent, false) binding.checkbox.applyTheme(ThemePrefs.brandColor) - return SectionViewHolder(binding.root) + return SectionViewHolder(binding) } override fun getItemCount(): Int = sections.size @@ -164,7 +162,8 @@ class SectionRecyclerViewAdapter( } } - inner class SectionViewHolder(val view: View) : RecyclerView.ViewHolder(view) { + inner class SectionViewHolder(val binding: ViewSectionListItemBinding) : RecyclerView.ViewHolder(binding.root) { + fun bind(sectionName: String, checked: Boolean, callback: (Boolean) -> Unit) { binding.sectionName.text = sectionName @@ -174,7 +173,7 @@ class SectionRecyclerViewAdapter( } binding.checkbox.setOnClickListener { callback(binding.checkbox.isChecked); (it as CheckBox).toggle() } - view.setOnClickListener { binding.checkbox.performClick(); } + binding.root.setOnClickListener { binding.checkbox.performClick() } } } } diff --git a/apps/teacher/src/main/java/com/instructure/teacher/fragments/QuizPreviewWebviewFragment.kt b/apps/teacher/src/main/java/com/instructure/teacher/fragments/QuizPreviewWebviewFragment.kt index 8e14adebb4..62e77813c3 100644 --- a/apps/teacher/src/main/java/com/instructure/teacher/fragments/QuizPreviewWebviewFragment.kt +++ b/apps/teacher/src/main/java/com/instructure/teacher/fragments/QuizPreviewWebviewFragment.kt @@ -42,7 +42,7 @@ class QuizPreviewWebviewFragment : InternalWebViewFragment() { // Try to automatically tap the Preview button so we take them directly into the quiz preview Handler().postDelayed({ - requireActivity().runOnUiThread { + activity?.runOnUiThread { mClickedPreview = true val js = "javascript: { " + "document.getElementById('preview_quiz_button').click();" + diff --git a/apps/teacher/src/main/java/com/instructure/teacher/presenters/DiscussionsReplyPresenter.kt b/apps/teacher/src/main/java/com/instructure/teacher/presenters/DiscussionsReplyPresenter.kt index 6204d83421..aa115dc331 100644 --- a/apps/teacher/src/main/java/com/instructure/teacher/presenters/DiscussionsReplyPresenter.kt +++ b/apps/teacher/src/main/java/com/instructure/teacher/presenters/DiscussionsReplyPresenter.kt @@ -43,6 +43,8 @@ class DiscussionsReplyPresenter( private var postDiscussionJob: Job? = null + private var attachment: FileSubmitObject? = null + override fun loadData(forceNetwork: Boolean) {} override fun refresh(forceNetwork: Boolean) {} @@ -98,7 +100,6 @@ class DiscussionsReplyPresenter( const val REASON_MESSAGE_IN_PROGRESS = 1 const val REASON_MESSAGE_EMPTY = 2 const val REASON_MESSAGE_FAILED_TO_SEND = 3 - var attachment: FileSubmitObject? = null } override fun onDestroyed() { diff --git a/libs/pandautils/src/main/java/com/instructure/pandautils/binding/ViewBindingDelegate.kt b/libs/pandautils/src/main/java/com/instructure/pandautils/binding/ViewBindingDelegate.kt index b5d12c787a..d170dde983 100644 --- a/libs/pandautils/src/main/java/com/instructure/pandautils/binding/ViewBindingDelegate.kt +++ b/libs/pandautils/src/main/java/com/instructure/pandautils/binding/ViewBindingDelegate.kt @@ -35,26 +35,28 @@ class FragmentViewBindingDelegate( private var binding: T? = null init { - fragment.lifecycle.addObserver(object : DefaultLifecycleObserver { - val viewLifecycleOwnerLiveDataObserver = - Observer { - val viewLifecycleOwner = it ?: return@Observer + fragment.activity?.runOnUiThread { + fragment.lifecycle.addObserver(object : DefaultLifecycleObserver { + val viewLifecycleOwnerLiveDataObserver = + Observer { + val viewLifecycleOwner = it ?: return@Observer - viewLifecycleOwner.lifecycle.addObserver(object : DefaultLifecycleObserver { - override fun onDestroy(owner: LifecycleOwner) { - binding = null - } - }) - } + viewLifecycleOwner.lifecycle.addObserver(object : DefaultLifecycleObserver { + override fun onDestroy(owner: LifecycleOwner) { + binding = null + } + }) + } - override fun onCreate(owner: LifecycleOwner) { - fragment.viewLifecycleOwnerLiveData.observeForever(viewLifecycleOwnerLiveDataObserver) - } + override fun onCreate(owner: LifecycleOwner) { + fragment.viewLifecycleOwnerLiveData.observeForever(viewLifecycleOwnerLiveDataObserver) + } - override fun onDestroy(owner: LifecycleOwner) { - fragment.viewLifecycleOwnerLiveData.removeObserver(viewLifecycleOwnerLiveDataObserver) - } - }) + override fun onDestroy(owner: LifecycleOwner) { + fragment.viewLifecycleOwnerLiveData.removeObserver(viewLifecycleOwnerLiveDataObserver) + } + }) + } } override fun getValue(thisRef: Fragment, property: KProperty<*>): T { From bbcfbf2c1836c2a98aa41e7af933fd9b75598e15 Mon Sep 17 00:00:00 2001 From: Tamas Kozmer Date: Tue, 18 Apr 2023 16:24:05 +0200 Subject: [PATCH 013/103] Fixed binding delegate. --- .../com/instructure/pandautils/binding/ViewBindingDelegate.kt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/libs/pandautils/src/main/java/com/instructure/pandautils/binding/ViewBindingDelegate.kt b/libs/pandautils/src/main/java/com/instructure/pandautils/binding/ViewBindingDelegate.kt index d170dde983..f81c2f2d34 100644 --- a/libs/pandautils/src/main/java/com/instructure/pandautils/binding/ViewBindingDelegate.kt +++ b/libs/pandautils/src/main/java/com/instructure/pandautils/binding/ViewBindingDelegate.kt @@ -16,6 +16,8 @@ */ package com.instructure.pandautils.binding +import android.os.Handler +import android.os.Looper import android.view.LayoutInflater import android.view.View import androidx.appcompat.app.AppCompatActivity @@ -35,7 +37,7 @@ class FragmentViewBindingDelegate( private var binding: T? = null init { - fragment.activity?.runOnUiThread { + Handler(Looper.getMainLooper()).post { fragment.lifecycle.addObserver(object : DefaultLifecycleObserver { val viewLifecycleOwnerLiveDataObserver = Observer { From 98f935cccfa7a67e7718c4d838f16084104235af Mon Sep 17 00:00:00 2001 From: Tamas Kozmer <72397075+tamaskozmer@users.noreply.github.com> Date: Mon, 24 Apr 2023 16:46:55 +0200 Subject: [PATCH 014/103] [MBL-16634][Student] Edit Groups #1944 refs: MBL-16634 affects: Student release note: none --- .../instructure/student/adapter/DashboardRecyclerAdapter.kt | 6 ++---- .../src/main/res/layout/viewholder_edit_dashboard_group.xml | 4 ++++ 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/apps/student/src/main/java/com/instructure/student/adapter/DashboardRecyclerAdapter.kt b/apps/student/src/main/java/com/instructure/student/adapter/DashboardRecyclerAdapter.kt index 8df86cbb5f..5c5ef240c8 100644 --- a/apps/student/src/main/java/com/instructure/student/adapter/DashboardRecyclerAdapter.kt +++ b/apps/student/src/main/java/com/instructure/student/adapter/DashboardRecyclerAdapter.kt @@ -20,7 +20,6 @@ package com.instructure.student.adapter import android.app.Activity import android.view.View import androidx.recyclerview.widget.RecyclerView -import com.instructure.canvasapi2.apis.EnrollmentAPI import com.instructure.canvasapi2.managers.* import com.instructure.canvasapi2.models.* import com.instructure.canvasapi2.utils.isValidTerm @@ -30,7 +29,6 @@ import com.instructure.pandautils.utils.ColorApiHelper import com.instructure.student.flutterChannels.FlutterComm import com.instructure.student.holders.* import com.instructure.student.interfaces.CourseAdapterToFragmentCallback -import com.instructure.student.util.StudentPrefs import org.threeten.bp.OffsetDateTime import java.util.* @@ -140,8 +138,8 @@ class DashboardRecyclerAdapter( // Filter groups val allActiveGroups = groups.filter { group -> group.isActive(mCourseMap[group.courseId])} - val isAnyFavoritePresent = visibleCourses.any { it.isFavorite } || allActiveGroups.any { it.isFavorite } - val visibleGroups = if (isAnyFavoritePresent) allActiveGroups.filter { it.isFavorite } else allActiveGroups + val isAnyGroupFavorited = allActiveGroups.any { it.isFavorite } + val visibleGroups = if (isAnyGroupFavorited) allActiveGroups.filter { it.isFavorite } else allActiveGroups // Add courses addOrUpdateAllItems(ItemType.COURSE_HEADER, visibleCourses) diff --git a/libs/pandautils/src/main/res/layout/viewholder_edit_dashboard_group.xml b/libs/pandautils/src/main/res/layout/viewholder_edit_dashboard_group.xml index 207ab456c7..39331c6200 100644 --- a/libs/pandautils/src/main/res/layout/viewholder_edit_dashboard_group.xml +++ b/libs/pandautils/src/main/res/layout/viewholder_edit_dashboard_group.xml @@ -64,6 +64,8 @@ app:layout_constraintEnd_toStartOf="@+id/openButton" app:layout_constraintStart_toEndOf="@id/favoriteButton" app:layout_constraintTop_toTopOf="parent" + app:layout_constraintBottom_toTopOf="@id/subtitle" + app:layout_constraintVertical_chainStyle="packed" tools:text="Course name" /> Date: Mon, 24 Apr 2023 16:47:42 +0200 Subject: [PATCH 015/103] Update translations (#1945) --- libs/pandares/src/main/res/values-cy/strings.xml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/libs/pandares/src/main/res/values-cy/strings.xml b/libs/pandares/src/main/res/values-cy/strings.xml index bd41444ba6..a3fc35dd77 100644 --- a/libs/pandares/src/main/res/values-cy/strings.xml +++ b/libs/pandares/src/main/res/values-cy/strings.xml @@ -1296,6 +1296,8 @@ Wrthi’n llwytho i fyny i Ffeiliau Wrthi’n llwytho cyflwyniad i fyny i \"%s\" Llwytho cyflwyniad i fyny + Wedi Llwyddo i Gyflwyno + Cyflwyniad wedi methu Wrthi’n llwytho ffeiliau i fyny Canslo Cyflwyniad Bydd hyn yn canslo ac yn dileu eich cyflwyniad. From a21f3b2f7782f20154478fe2ad5003afed6f2b67 Mon Sep 17 00:00:00 2001 From: Tamas Kozmer Date: Tue, 25 Apr 2023 09:29:07 +0200 Subject: [PATCH 016/103] Updated version. --- apps/teacher/build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/teacher/build.gradle b/apps/teacher/build.gradle index 7c66b76d6b..9be4666b09 100644 --- a/apps/teacher/build.gradle +++ b/apps/teacher/build.gradle @@ -41,8 +41,8 @@ android { defaultConfig { minSdkVersion Versions.MIN_SDK targetSdkVersion Versions.TARGET_SDK - versionCode = 56 - versionName = '1.23.0' + versionCode = 57 + versionName = '1.23.1' vectorDrawables.useSupportLibrary = true multiDexEnabled true testInstrumentationRunner 'com.instructure.teacher.ui.espresso.TeacherHiltTestRunner' From ba3801b081c9518aaae0c208dc04bef1067904e0 Mon Sep 17 00:00:00 2001 From: Kristof Deak <92309696+kdeakinstructure@users.noreply.github.com> Date: Tue, 25 Apr 2023 10:22:50 +0200 Subject: [PATCH 017/103] Many nightly test fix (#1949) --- .../student/ui/e2e/InboxE2ETest.kt | 1 + .../student/ui/e2e/NotificationsE2ETest.kt | 10 ++++++-- .../ui/interaction/TodoInteractionTest.kt | 1 - .../student/ui/pages/DashboardPage.kt | 2 +- .../instructure/student/ui/pages/InboxPage.kt | 23 ++++++++++++++++--- .../instructure/student/ui/pages/TodoPage.kt | 10 ++++++-- .../instructure/teacher/ui/InboxPageTest.kt | 7 +++++- .../instructure/teacher/ui/pages/InboxPage.kt | 22 +++++++++++++++--- .../canvas/espresso/mockCanvas/MockCanvas.kt | 3 ++- 9 files changed, 65 insertions(+), 14 deletions(-) diff --git a/apps/student/src/androidTest/java/com/instructure/student/ui/e2e/InboxE2ETest.kt b/apps/student/src/androidTest/java/com/instructure/student/ui/e2e/InboxE2ETest.kt index f4bb8e96c4..6e8c9a8398 100644 --- a/apps/student/src/androidTest/java/com/instructure/student/ui/e2e/InboxE2ETest.kt +++ b/apps/student/src/androidTest/java/com/instructure/student/ui/e2e/InboxE2ETest.kt @@ -172,6 +172,7 @@ class InboxE2ETest: StudentTest() { inboxPage.selectConversation(seededConversation) inboxPage.assertSelectedConversationNumber("1") inboxPage.clickUnArchive() + inboxPage.assertInboxEmpty() inboxPage.assertConversationNotDisplayed(seededConversation.subject) sleep(2000) diff --git a/apps/student/src/androidTest/java/com/instructure/student/ui/e2e/NotificationsE2ETest.kt b/apps/student/src/androidTest/java/com/instructure/student/ui/e2e/NotificationsE2ETest.kt index 2d4077f8ae..bdeaec2ebf 100644 --- a/apps/student/src/androidTest/java/com/instructure/student/ui/e2e/NotificationsE2ETest.kt +++ b/apps/student/src/androidTest/java/com/instructure/student/ui/e2e/NotificationsE2ETest.kt @@ -22,7 +22,13 @@ import com.instructure.canvas.espresso.refresh import com.instructure.dataseeding.api.AssignmentsApi import com.instructure.dataseeding.api.QuizzesApi import com.instructure.dataseeding.api.SubmissionsApi -import com.instructure.dataseeding.model.* +import com.instructure.dataseeding.model.AssignmentApiModel +import com.instructure.dataseeding.model.CanvasUserApiModel +import com.instructure.dataseeding.model.CourseApiModel +import com.instructure.dataseeding.model.GradingType +import com.instructure.dataseeding.model.QuizAnswer +import com.instructure.dataseeding.model.QuizQuestion +import com.instructure.dataseeding.model.SubmissionType import com.instructure.dataseeding.util.days import com.instructure.dataseeding.util.fromNow import com.instructure.dataseeding.util.iso8601 @@ -104,7 +110,7 @@ class NotificationsE2ETest : StudentTest() { gradeSubmission(teacher, course, testAssignment, student) Log.d(STEP_TAG,"Refresh the Notifications Page. Assert that there is a notification about the submission grading appearing.") - sleep(5000) //Let the submission api do it's job + sleep(10000) //Let the submission api do it's job refresh() notificationPage.assertHasGrade(testAssignment.name,"13") } diff --git a/apps/student/src/androidTest/java/com/instructure/student/ui/interaction/TodoInteractionTest.kt b/apps/student/src/androidTest/java/com/instructure/student/ui/interaction/TodoInteractionTest.kt index ddc7753a77..25eab4948a 100644 --- a/apps/student/src/androidTest/java/com/instructure/student/ui/interaction/TodoInteractionTest.kt +++ b/apps/student/src/androidTest/java/com/instructure/student/ui/interaction/TodoInteractionTest.kt @@ -67,7 +67,6 @@ class TodoInteractionTest : StudentTest() { @StubLandscape("Stubbed because on lowres device in landscape mode, the space is too narrow to scroll properly. Will be refactored and running when we changed to non-lowres device on nightly runs.") @TestMetaData(Priority.IMPORTANT, FeatureCategory.TODOS, TestCategory.INTERACTION, false) fun testFilters() { - //TODO: Check and refactor (if necessary) after migrated nightly runs from lowres device to non-lowres one. val data = goToTodos(courseCount = 2, favoriteCourseCount = 1) val favoriteCourse = data.courses.values.first {course -> course.isFavorite} val notFavoriteCourse = data.courses.values.first {course -> !course.isFavorite} diff --git a/apps/student/src/androidTest/java/com/instructure/student/ui/pages/DashboardPage.kt b/apps/student/src/androidTest/java/com/instructure/student/ui/pages/DashboardPage.kt index 8bc8556ca8..1e2cb4072b 100644 --- a/apps/student/src/androidTest/java/com/instructure/student/ui/pages/DashboardPage.kt +++ b/apps/student/src/androidTest/java/com/instructure/student/ui/pages/DashboardPage.kt @@ -179,7 +179,7 @@ class DashboardPage : BasePage(R.id.dashboardPage) { fun selectCourse(course: CourseApiModel) { assertDisplaysCourse(course) - onView(withText(course.name)).click() + onView(withText(course.name) + withId(R.id.titleTextView)).click() } fun assertAnnouncementShowing(announcement: AccountNotification) { diff --git a/apps/student/src/androidTest/java/com/instructure/student/ui/pages/InboxPage.kt b/apps/student/src/androidTest/java/com/instructure/student/ui/pages/InboxPage.kt index ef23271446..223ceaa4de 100644 --- a/apps/student/src/androidTest/java/com/instructure/student/ui/pages/InboxPage.kt +++ b/apps/student/src/androidTest/java/com/instructure/student/ui/pages/InboxPage.kt @@ -32,8 +32,25 @@ import com.instructure.canvas.espresso.waitForMatcherWithRefreshes import com.instructure.canvasapi2.models.Conversation import com.instructure.canvasapi2.models.Course import com.instructure.dataseeding.model.ConversationApiModel -import com.instructure.espresso.* -import com.instructure.espresso.page.* +import com.instructure.espresso.OnViewWithId +import com.instructure.espresso.RecyclerViewItemCountGreaterThanAssertion +import com.instructure.espresso.WaitForViewWithId +import com.instructure.espresso.assertDisplayed +import com.instructure.espresso.assertVisibility +import com.instructure.espresso.click +import com.instructure.espresso.longClick +import com.instructure.espresso.page.BasePage +import com.instructure.espresso.page.onView +import com.instructure.espresso.page.plus +import com.instructure.espresso.page.waitForView +import com.instructure.espresso.page.waitForViewWithId +import com.instructure.espresso.page.waitForViewWithText +import com.instructure.espresso.page.withAncestor +import com.instructure.espresso.page.withId +import com.instructure.espresso.page.withText +import com.instructure.espresso.scrollTo +import com.instructure.espresso.swipeLeft +import com.instructure.espresso.swipeRight import com.instructure.student.R import org.hamcrest.Matchers.allOf import org.hamcrest.Matchers.not @@ -167,7 +184,7 @@ class InboxPage : BasePage(R.id.inboxPage) { } fun assertInboxEmpty() { - onView(withId(R.id.emptyInboxView)).assertDisplayed() + waitForView(withId(R.id.emptyInboxView)).assertDisplayed() } fun assertHasConversation() { diff --git a/apps/student/src/androidTest/java/com/instructure/student/ui/pages/TodoPage.kt b/apps/student/src/androidTest/java/com/instructure/student/ui/pages/TodoPage.kt index 0ba41f40ea..75064e0331 100644 --- a/apps/student/src/androidTest/java/com/instructure/student/ui/pages/TodoPage.kt +++ b/apps/student/src/androidTest/java/com/instructure/student/ui/pages/TodoPage.kt @@ -27,7 +27,13 @@ import com.instructure.dataseeding.model.QuizApiModel import com.instructure.espresso.OnViewWithId import com.instructure.espresso.assertDisplayed import com.instructure.espresso.click -import com.instructure.espresso.page.* +import com.instructure.espresso.page.BasePage +import com.instructure.espresso.page.onView +import com.instructure.espresso.page.plus +import com.instructure.espresso.page.withAncestor +import com.instructure.espresso.page.withId +import com.instructure.espresso.page.withParent +import com.instructure.espresso.page.withText import com.instructure.espresso.scrollTo import com.instructure.student.R import org.hamcrest.Matchers @@ -77,7 +83,7 @@ class TodoPage: BasePage(R.id.todoPage) { fun chooseFavoriteCourseFilter() { onView(withId(R.id.todoListFilter)).click() - onView(withText(R.string.favoritedCoursesLabel)).click() + onView(withText(R.string.favoritedCoursesLabel) + withParent(R.id.select_dialog_listview)).click() onView(allOf(isAssignableFrom(Button::class.java), withText(R.string.ok))).click() } diff --git a/apps/teacher/src/androidTest/java/com/instructure/teacher/ui/InboxPageTest.kt b/apps/teacher/src/androidTest/java/com/instructure/teacher/ui/InboxPageTest.kt index 19843d35f1..129b79248e 100644 --- a/apps/teacher/src/androidTest/java/com/instructure/teacher/ui/InboxPageTest.kt +++ b/apps/teacher/src/androidTest/java/com/instructure/teacher/ui/InboxPageTest.kt @@ -18,7 +18,11 @@ package com.instructure.teacher.ui import androidx.test.espresso.matcher.ViewMatchers -import com.instructure.canvas.espresso.mockCanvas.* +import com.instructure.canvas.espresso.mockCanvas.MockCanvas +import com.instructure.canvas.espresso.mockCanvas.addConversation +import com.instructure.canvas.espresso.mockCanvas.addConversations +import com.instructure.canvas.espresso.mockCanvas.createBasicConversation +import com.instructure.canvas.espresso.mockCanvas.init import com.instructure.canvasapi2.models.Conversation import com.instructure.canvasapi2.models.User import com.instructure.panda_annotations.FeatureCategory @@ -98,6 +102,7 @@ class InboxPageTest: TeacherTest() { inboxPage.assertConversationNotDisplayed(conversation2.subject!!) inboxPage.assertEditToolbarIs(ViewMatchers.Visibility.GONE) + inboxPage.refresh() inboxPage.filterInbox("Archived") inboxPage.assertConversationDisplayed(conversation1.subject!!) inboxPage.assertConversationDisplayed(conversation2.subject!!) diff --git a/apps/teacher/src/androidTest/java/com/instructure/teacher/ui/pages/InboxPage.kt b/apps/teacher/src/androidTest/java/com/instructure/teacher/ui/pages/InboxPage.kt index a3bd1bd3bc..7a32b23584 100644 --- a/apps/teacher/src/androidTest/java/com/instructure/teacher/ui/pages/InboxPage.kt +++ b/apps/teacher/src/androidTest/java/com/instructure/teacher/ui/pages/InboxPage.kt @@ -12,8 +12,25 @@ import com.instructure.canvas.espresso.waitForMatcherWithRefreshes import com.instructure.canvas.espresso.withCustomConstraints import com.instructure.canvasapi2.models.Conversation import com.instructure.dataseeding.model.ConversationApiModel -import com.instructure.espresso.* -import com.instructure.espresso.page.* +import com.instructure.espresso.OnViewWithId +import com.instructure.espresso.RecyclerViewItemCountGreaterThanAssertion +import com.instructure.espresso.WaitForViewWithId +import com.instructure.espresso.assertDisplayed +import com.instructure.espresso.assertVisibility +import com.instructure.espresso.click +import com.instructure.espresso.longClick +import com.instructure.espresso.page.BasePage +import com.instructure.espresso.page.onView +import com.instructure.espresso.page.plus +import com.instructure.espresso.page.waitForView +import com.instructure.espresso.page.waitForViewWithId +import com.instructure.espresso.page.waitForViewWithText +import com.instructure.espresso.page.withAncestor +import com.instructure.espresso.page.withId +import com.instructure.espresso.page.withText +import com.instructure.espresso.scrollTo +import com.instructure.espresso.swipeLeft +import com.instructure.espresso.swipeRight import com.instructure.teacher.R import com.instructure.teacher.ui.utils.WaitForToolbarTitle import org.hamcrest.Matchers @@ -69,7 +86,6 @@ class InboxPage: BasePage() { } fun filterInbox(filterFor: String) { - refresh() waitForView(withId(R.id.scopeFilterText)) onView(withId(R.id.scopeFilter)).click() waitForViewWithText(filterFor).click() diff --git a/automation/espresso/src/main/kotlin/com/instructure/canvas/espresso/mockCanvas/MockCanvas.kt b/automation/espresso/src/main/kotlin/com/instructure/canvas/espresso/mockCanvas/MockCanvas.kt index d0e4f46f42..5eef33883e 100644 --- a/automation/espresso/src/main/kotlin/com/instructure/canvas/espresso/mockCanvas/MockCanvas.kt +++ b/automation/espresso/src/main/kotlin/com/instructure/canvas/espresso/mockCanvas/MockCanvas.kt @@ -36,6 +36,7 @@ import okhttp3.mockwebserver.MockWebServer import okhttp3.mockwebserver.RecordedRequest import org.threeten.bp.OffsetDateTime import java.util.* +import kotlin.random.Random class MockCanvas { /** Fake domain */ @@ -1086,7 +1087,7 @@ fun MockCanvas.addUser(): User { val name = Randomizer.randomName() val email = Randomizer.randomEmail() val user = User( - id = Random().nextLong(), + id = Random.nextLong(), name = name.fullName, shortName = name.firstName, loginId = email, From f00a8cea0a229854ff6cc271da6c5cefbff74a2d Mon Sep 17 00:00:00 2001 From: Tamas Kozmer <72397075+tamaskozmer@users.noreply.github.com> Date: Tue, 25 Apr 2023 10:25:47 +0200 Subject: [PATCH 018/103] [MBL-16696][Teacher] Speedgrader tablet crash #1950 refs: MBL-16696 affects: Teacher release note: none --- .../instructure/pandautils/views/FloatingRecordingView.kt | 6 +++--- .../src/main/res/layout/view_floating_media_recorder.xml | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/libs/pandautils/src/main/java/com/instructure/pandautils/views/FloatingRecordingView.kt b/libs/pandautils/src/main/java/com/instructure/pandautils/views/FloatingRecordingView.kt index f2cff39422..0db877baf7 100644 --- a/libs/pandautils/src/main/java/com/instructure/pandautils/views/FloatingRecordingView.kt +++ b/libs/pandautils/src/main/java/com/instructure/pandautils/views/FloatingRecordingView.kt @@ -85,8 +85,8 @@ class FloatingRecordingView @JvmOverloads constructor( /* CameraView will throw an exception during inflation on some devices. We capture that exception here and show an error view instead when we try to record video. */ try { - videoViewBinding = ViewFloatingMediaRecorderVideoBinding.inflate(LayoutInflater.from(context), binding.dragView, false) - binding.dragView.addView(videoViewBinding?.root) + videoViewBinding = ViewFloatingMediaRecorderVideoBinding.inflate(LayoutInflater.from(context), binding.dragDetectLayout, false) + binding.dragDetectLayout.addView(videoViewBinding?.root) } catch (e: InflateException) { hasVideoError = true videoViewBinding = null @@ -147,7 +147,7 @@ class FloatingRecordingView @JvmOverloads constructor( } private fun setupFloatingAction() { - binding.dragView.setOnTouchListener(object : View.OnTouchListener { + binding.dragDetectLayout.setOnTouchListener(object : View.OnTouchListener { private var lastAction: Int = 0 var windowManager = context.getSystemService(Context.WINDOW_SERVICE) as WindowManager var display = windowManager.defaultDisplay diff --git a/libs/pandautils/src/main/res/layout/view_floating_media_recorder.xml b/libs/pandautils/src/main/res/layout/view_floating_media_recorder.xml index cafe5647cf..b6fc17a4da 100644 --- a/libs/pandautils/src/main/res/layout/view_floating_media_recorder.xml +++ b/libs/pandautils/src/main/res/layout/view_floating_media_recorder.xml @@ -20,7 +20,7 @@ android:layout_width="wrap_content"> From c17ac654b8c4757a961af8f1b62907d300afa229 Mon Sep 17 00:00:00 2001 From: Tamas Kozmer Date: Tue, 25 Apr 2023 11:29:04 +0200 Subject: [PATCH 019/103] Fixed ViewBinding errors. --- .../instructure/teacher/dialog/EditRubricCommentDialog.kt | 5 +++-- .../teacher/fragments/SpeedGraderGradeFragment.kt | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/apps/teacher/src/main/java/com/instructure/teacher/dialog/EditRubricCommentDialog.kt b/apps/teacher/src/main/java/com/instructure/teacher/dialog/EditRubricCommentDialog.kt index 2e38cf7f41..2e039e36c1 100644 --- a/apps/teacher/src/main/java/com/instructure/teacher/dialog/EditRubricCommentDialog.kt +++ b/apps/teacher/src/main/java/com/instructure/teacher/dialog/EditRubricCommentDialog.kt @@ -39,7 +39,7 @@ import org.greenrobot.eventbus.EventBus @ScreenView(SCREEN_VIEW_EDIT_RUBRIC_COMMENT) class EditRubricCommentDialog : AppCompatDialogFragment() { - private val binding by viewBinding(ViewEditGradeCommentBinding::bind) + private lateinit var binding: ViewEditGradeCommentBinding var mCriterionId by StringArg() var mStudentId by LongArg(-1L) @@ -74,7 +74,8 @@ class EditRubricCommentDialog : AppCompatDialogFragment() { } override fun onCreateDialog(savedInstanceState: Bundle?) = AppCompatDialog(requireContext(), R.style.Theme_AppCompat_DayNight_Translucent).apply { - setContentView(R.layout.view_edit_grade_comment) + binding = ViewEditGradeCommentBinding.inflate(layoutInflater, null, false) + setContentView(binding.root) // Send event bus on save, dismiss dialog. Send null if text is blank (i.e. delete comment) binding.saveCommentButton.onClick { diff --git a/apps/teacher/src/main/java/com/instructure/teacher/fragments/SpeedGraderGradeFragment.kt b/apps/teacher/src/main/java/com/instructure/teacher/fragments/SpeedGraderGradeFragment.kt index 33de592c4b..c5e32d1ad4 100644 --- a/apps/teacher/src/main/java/com/instructure/teacher/fragments/SpeedGraderGradeFragment.kt +++ b/apps/teacher/src/main/java/com/instructure/teacher/fragments/SpeedGraderGradeFragment.kt @@ -252,7 +252,7 @@ class SpeedGraderGradeFragment : BasePresenterFragment Date: Wed, 26 Apr 2023 19:52:27 +0200 Subject: [PATCH 020/103] [MBL-16670][Student] Share Extension | Route to the file location after clicking on the Successful Dashboard notification refs: MBL-16670 affects: Student release note: Dashboard upload notification improvements. * Share extension success notification routing * Share extensions navigation * minor fix * Added loading for UploadItemViewModel.kt * Added routing to my files for teacher --- .../notifications/StudentDashboardRouter.kt | 17 +++ .../notifications/TeacherDashboardRouter.kt | 12 ++ .../canvasapi2/managers/CourseManager.kt | 2 + .../DashboardNotificationsFragment.kt | 9 +- .../DashboardNotificationsViewData.kt | 8 +- .../DashboardNotificationsViewModel.kt | 62 +++++++-- .../notifications/DashboardRouter.kt | 6 + .../itemviewmodels/UploadItemViewModel.kt | 8 +- .../file/upload/worker/FileUploadWorker.kt | 14 +- .../pandautils/room/AppDatabase.kt | 2 +- .../pandautils/room/AppDatabaseMigrations.kt | 9 +- .../entities/DashboardFileUploadEntity.kt | 6 +- .../main/res/layout/item_dashboard_upload.xml | 23 +++- .../DashboardNotificationsViewModelTest.kt | 124 ++++++++++++++---- 14 files changed, 253 insertions(+), 49 deletions(-) diff --git a/apps/student/src/main/java/com/instructure/student/features/dashboard/notifications/StudentDashboardRouter.kt b/apps/student/src/main/java/com/instructure/student/features/dashboard/notifications/StudentDashboardRouter.kt index fc7e9a2955..44d5f46f5c 100644 --- a/apps/student/src/main/java/com/instructure/student/features/dashboard/notifications/StudentDashboardRouter.kt +++ b/apps/student/src/main/java/com/instructure/student/features/dashboard/notifications/StudentDashboardRouter.kt @@ -17,8 +17,11 @@ package com.instructure.student.features.dashboard.notifications import androidx.fragment.app.FragmentActivity +import com.instructure.canvasapi2.models.CanvasContext import com.instructure.pandautils.features.dashboard.notifications.DashboardRouter +import com.instructure.student.fragment.FileListFragment import com.instructure.student.fragment.InternalWebviewFragment +import com.instructure.student.mobius.assignmentDetails.submissionDetails.ui.SubmissionDetailsFragment import com.instructure.student.router.RouteMatcher class StudentDashboardRouter(private val activity: FragmentActivity) : DashboardRouter { @@ -35,4 +38,18 @@ class StudentDashboardRouter(private val activity: FragmentActivity) : Dashboard ) ) } + + override fun routeToSubmissionDetails(canvasContext: CanvasContext, assignmentId: Long, attemptId: Long) { + RouteMatcher.route( + activity, + SubmissionDetailsFragment.makeRoute(canvasContext, assignmentId, initialSelectedSubmissionAttempt = attemptId) + ) + } + + override fun routeToMyFiles(canvasContext: CanvasContext, folderId: Long) { + RouteMatcher.route( + activity, + FileListFragment.makeRoute(canvasContext, folderId) + ) + } } \ No newline at end of file diff --git a/apps/teacher/src/main/java/com/instructure/teacher/features/dashboard/notifications/TeacherDashboardRouter.kt b/apps/teacher/src/main/java/com/instructure/teacher/features/dashboard/notifications/TeacherDashboardRouter.kt index 944601cc82..e518b4414d 100644 --- a/apps/teacher/src/main/java/com/instructure/teacher/features/dashboard/notifications/TeacherDashboardRouter.kt +++ b/apps/teacher/src/main/java/com/instructure/teacher/features/dashboard/notifications/TeacherDashboardRouter.kt @@ -17,9 +17,11 @@ package com.instructure.teacher.features.dashboard.notifications import androidx.fragment.app.FragmentActivity +import com.instructure.canvasapi2.models.CanvasContext import com.instructure.interactions.router.Route import com.instructure.pandautils.features.dashboard.notifications.DashboardRouter import com.instructure.pandautils.fragments.HtmlContentFragment +import com.instructure.teacher.fragments.FileListFragment import com.instructure.teacher.router.RouteMatcher class TeacherDashboardRouter(private val activity: FragmentActivity) : DashboardRouter { @@ -28,4 +30,14 @@ class TeacherDashboardRouter(private val activity: FragmentActivity) : Dashboard val route = Route(HtmlContentFragment::class.java, null, args) RouteMatcher.route(activity, route) } + + override fun routeToSubmissionDetails(canvasContext: CanvasContext, assignmentId: Long, attemptId: Long) {} + + override fun routeToMyFiles(canvasContext: CanvasContext, folderId: Long) { + val args = FileListFragment.makeBundle(canvasContext) + RouteMatcher.route( + activity, + Route(FileListFragment::class.java, canvasContext, args) + ) + } } \ No newline at end of file diff --git a/libs/canvas-api-2/src/main/java/com/instructure/canvasapi2/managers/CourseManager.kt b/libs/canvas-api-2/src/main/java/com/instructure/canvasapi2/managers/CourseManager.kt index 7109af762c..5283bade91 100644 --- a/libs/canvas-api-2/src/main/java/com/instructure/canvasapi2/managers/CourseManager.kt +++ b/libs/canvas-api-2/src/main/java/com/instructure/canvasapi2/managers/CourseManager.kt @@ -170,6 +170,8 @@ object CourseManager { CourseAPI.getGradingPeriodsForCourse(adapter, callback, params, courseId) } + fun getCourseAsync(courseId: Long, forceNetwork: Boolean) = apiAsync { getCourse(courseId, it, forceNetwork) } + fun getCourse(courseId: Long, callback: StatusCallback, forceNetwork: Boolean) { val adapter = RestBuilder(callback) val params = RestParams(isForceReadFromNetwork = forceNetwork) diff --git a/libs/pandautils/src/main/java/com/instructure/pandautils/features/dashboard/notifications/DashboardNotificationsFragment.kt b/libs/pandautils/src/main/java/com/instructure/pandautils/features/dashboard/notifications/DashboardNotificationsFragment.kt index 21bcbe17a8..b72c16b09a 100644 --- a/libs/pandautils/src/main/java/com/instructure/pandautils/features/dashboard/notifications/DashboardNotificationsFragment.kt +++ b/libs/pandautils/src/main/java/com/instructure/pandautils/features/dashboard/notifications/DashboardNotificationsFragment.kt @@ -16,7 +16,6 @@ package com.instructure.pandautils.features.dashboard.notifications -import android.content.Intent import android.net.Uri import android.os.Bundle import android.view.LayoutInflater @@ -30,9 +29,7 @@ import androidx.fragment.app.viewModels import com.instructure.pandautils.analytics.SCREEN_VIEW_DASHBOARD_NOTIFICATIONS import com.instructure.pandautils.analytics.ScreenView import com.instructure.pandautils.databinding.FragmentDashboardNotificationsBinding -import com.instructure.pandautils.features.shareextension.ShareExtensionActivity import com.instructure.pandautils.features.shareextension.ShareExtensionRouter -import com.instructure.pandautils.utils.ColorKeeper import com.instructure.pandautils.utils.asChooserExcludingInstructure import com.instructure.pandautils.utils.backgroundColor import dagger.hilt.android.AndroidEntryPoint @@ -107,6 +104,12 @@ class DashboardNotificationsFragment : Fragment() { is DashboardNotificationsActions.OpenProgressDialog -> { startActivity(shareExtensionRouter.routeToProgressScreen(requireActivity(), action.uuid)) } + is DashboardNotificationsActions.NavigateToSubmissionDetails -> { + dashboardRouter.routeToSubmissionDetails(action.canvasContext, action.assignmentId, action.attemptId) + } + is DashboardNotificationsActions.NavigateToMyFiles -> { + dashboardRouter.routeToMyFiles(action.canvasContext, action.folderId) + } } } diff --git a/libs/pandautils/src/main/java/com/instructure/pandautils/features/dashboard/notifications/DashboardNotificationsViewData.kt b/libs/pandautils/src/main/java/com/instructure/pandautils/features/dashboard/notifications/DashboardNotificationsViewData.kt index 54a6d6933d..612ed8c6da 100644 --- a/libs/pandautils/src/main/java/com/instructure/pandautils/features/dashboard/notifications/DashboardNotificationsViewData.kt +++ b/libs/pandautils/src/main/java/com/instructure/pandautils/features/dashboard/notifications/DashboardNotificationsViewData.kt @@ -66,5 +66,11 @@ sealed class DashboardNotificationsActions { data class ShowToast(val toast: String) : DashboardNotificationsActions() data class LaunchConference(val canvasContext: CanvasContext, val url: String) : DashboardNotificationsActions() data class OpenAnnouncement(val subject: String, val message: String) : DashboardNotificationsActions() - data class OpenProgressDialog(val uuid: UUID): DashboardNotificationsActions() + data class OpenProgressDialog(val uuid: UUID) : DashboardNotificationsActions() + data class NavigateToSubmissionDetails( + val canvasContext: CanvasContext, + val assignmentId: Long, + val attemptId: Long + ) : DashboardNotificationsActions() + data class NavigateToMyFiles(val canvasContext: CanvasContext, val folderId: Long) : DashboardNotificationsActions() } diff --git a/libs/pandautils/src/main/java/com/instructure/pandautils/features/dashboard/notifications/DashboardNotificationsViewModel.kt b/libs/pandautils/src/main/java/com/instructure/pandautils/features/dashboard/notifications/DashboardNotificationsViewModel.kt index 27a3f2e3b5..0671a67699 100644 --- a/libs/pandautils/src/main/java/com/instructure/pandautils/features/dashboard/notifications/DashboardNotificationsViewModel.kt +++ b/libs/pandautils/src/main/java/com/instructure/pandautils/features/dashboard/notifications/DashboardNotificationsViewModel.kt @@ -258,16 +258,8 @@ class DashboardNotificationsViewModel @Inject constructor( workerId = workerId, workManager = workManager, data = uploadViewData, - open = { uuid -> _events.postValue(Event(DashboardNotificationsActions.OpenProgressDialog(uuid))) }, - remove = { - viewModelScope.launch { - dashboardFileUploadDao.delete(fileUploadEntity) - fileUploadInputDao.findByWorkerId(workerId.toString())?.let { - fileUploadUtilsHelper.deleteCachedFiles(it.filePaths) - fileUploadInputDao.delete(it) - } - } - } + open = { uuid -> openUploadNotification(workInfo.state, uuid, fileUploadEntity) }, + remove = { removeUploadNotification(fileUploadEntity, workerId) } ) } }.orEmpty() @@ -288,6 +280,56 @@ class DashboardNotificationsViewModel @Inject constructor( return !course.restrictEnrollmentsToCourseDate || isBeforeEndDate } + private fun openUploadNotification(state: WorkInfo.State, uuid: UUID, fileUploadEntity: DashboardFileUploadEntity) { + if (state == WorkInfo.State.SUCCEEDED) { + viewModelScope.launch { + val uploadItemViewModel = _data.value?.uploadItems?.find { it.workerId == uuid } + uploadItemViewModel?.apply { + loading = true + notifyPropertyChanged(BR.loading) + } + if (fileUploadEntity.courseId != null && fileUploadEntity.assignmentId != null && fileUploadEntity.attemptId != null) { + courseManager.getCourseAsync(fileUploadEntity.courseId, false).await().dataOrNull?.let { + dashboardFileUploadDao.delete(fileUploadEntity) + _events.postValue( + Event( + DashboardNotificationsActions.NavigateToSubmissionDetails( + it, + fileUploadEntity.assignmentId, + fileUploadEntity.attemptId + ) + ) + ) + } + } else if (fileUploadEntity.folderId != null) { + dashboardFileUploadDao.delete(fileUploadEntity) + apiPrefs.user?.let { + _events.postValue(Event(DashboardNotificationsActions.NavigateToMyFiles(it, fileUploadEntity.folderId))) + } + } else { + dashboardFileUploadDao.delete(fileUploadEntity) + _events.postValue(Event(DashboardNotificationsActions.OpenProgressDialog(uuid))) + } + uploadItemViewModel?.apply { + loading = false + notifyPropertyChanged(BR.loading) + } + } + } else { + _events.postValue(Event(DashboardNotificationsActions.OpenProgressDialog(uuid))) + } + } + + private fun removeUploadNotification(fileUploadEntity: DashboardFileUploadEntity, workerId: UUID) { + viewModelScope.launch { + dashboardFileUploadDao.delete(fileUploadEntity) + fileUploadInputDao.findByWorkerId(workerId.toString())?.let { + fileUploadUtilsHelper.deleteCachedFiles(it.filePaths) + fileUploadInputDao.delete(it) + } + } + } + private fun handleInvitation( enrollmentId: Long, courseId: Long, diff --git a/libs/pandautils/src/main/java/com/instructure/pandautils/features/dashboard/notifications/DashboardRouter.kt b/libs/pandautils/src/main/java/com/instructure/pandautils/features/dashboard/notifications/DashboardRouter.kt index f13ef49067..ddeda96a2c 100644 --- a/libs/pandautils/src/main/java/com/instructure/pandautils/features/dashboard/notifications/DashboardRouter.kt +++ b/libs/pandautils/src/main/java/com/instructure/pandautils/features/dashboard/notifications/DashboardRouter.kt @@ -16,7 +16,13 @@ package com.instructure.pandautils.features.dashboard.notifications +import com.instructure.canvasapi2.models.CanvasContext + interface DashboardRouter { fun routeToGlobalAnnouncement(subject: String, message: String) + + fun routeToSubmissionDetails(canvasContext: CanvasContext, assignmentId: Long, attemptId: Long) + + fun routeToMyFiles(canvasContext: CanvasContext, folderId: Long) } \ No newline at end of file diff --git a/libs/pandautils/src/main/java/com/instructure/pandautils/features/dashboard/notifications/itemviewmodels/UploadItemViewModel.kt b/libs/pandautils/src/main/java/com/instructure/pandautils/features/dashboard/notifications/itemviewmodels/UploadItemViewModel.kt index 6eb381848f..f0d2e65c20 100644 --- a/libs/pandautils/src/main/java/com/instructure/pandautils/features/dashboard/notifications/itemviewmodels/UploadItemViewModel.kt +++ b/libs/pandautils/src/main/java/com/instructure/pandautils/features/dashboard/notifications/itemviewmodels/UploadItemViewModel.kt @@ -31,14 +31,17 @@ import com.instructure.pandautils.mvvm.ItemViewModel import java.util.* class UploadItemViewModel( - private val workerId: UUID, + val workerId: UUID, val workManager: WorkManager, val data: UploadViewData, @get:Bindable var progress: Int = 0, val open: (UUID) -> Unit, - val remove: () -> Unit + val remove: () -> Unit, + @get:Bindable var loading: Boolean = false ) : ItemViewModel, BaseObservable() { + override val layoutId = R.layout.item_dashboard_upload + private val observer = Observer { val uploadedSize = it.progress.getLong(PROGRESS_DATA_UPLOADED_SIZE, 0L) val fullSize = it.progress.getLong(PROGRESS_DATA_FULL_SIZE, 1L) @@ -46,7 +49,6 @@ class UploadItemViewModel( progress = ((uploadedSize.toDouble() / fullSize.toDouble()) * 100.0).toInt() notifyPropertyChanged(BR.progress) } - override val layoutId = R.layout.item_dashboard_upload init { workManager.getWorkInfoByIdLiveData(workerId).observeForever(observer) diff --git a/libs/pandautils/src/main/java/com/instructure/pandautils/features/file/upload/worker/FileUploadWorker.kt b/libs/pandautils/src/main/java/com/instructure/pandautils/features/file/upload/worker/FileUploadWorker.kt index 6767b6ae82..d562086cd6 100644 --- a/libs/pandautils/src/main/java/com/instructure/pandautils/features/file/upload/worker/FileUploadWorker.kt +++ b/libs/pandautils/src/main/java/com/instructure/pandautils/features/file/upload/worker/FileUploadWorker.kt @@ -38,6 +38,7 @@ import com.instructure.pandautils.features.file.upload.FileUploadUtilsHelper import com.instructure.pandautils.room.daos.* import com.instructure.pandautils.room.entities.* import com.instructure.pandautils.utils.FileUploadUtils +import com.instructure.pandautils.utils.orDefault import com.instructure.pandautils.utils.toJson import dagger.assisted.Assisted import dagger.assisted.AssistedInject @@ -112,7 +113,7 @@ class FileUploadWorker @AssistedInject constructor( subtitle = assignmentName.ifEmpty { fileSubmitObjects.joinToString { it.name } } - insertDashboardUpload(title, subtitle) + if (shouldShowDashboardNotification(action)) insertDashboardUpload(title, subtitle) val attachments = uploadFiles(fileSubmitObjects, groupId) @@ -124,6 +125,7 @@ class FileUploadWorker @AssistedInject constructor( ACTION_ASSIGNMENT_SUBMISSION -> { submitAttachmentsToSubmission(attachmentsIds)?.let { updateSubmissionComplete(notificationId) + attemptId = it.attempt Result.success() } ?: Result.retry() } @@ -173,7 +175,7 @@ class FileUploadWorker @AssistedInject constructor( e.printStackTrace() return Result.failure(workDataBuilder.build()) } finally { - insertDashboardUpload(title, subtitle) + if (shouldShowDashboardNotification(action)) insertDashboardUpload(title, subtitle) } } @@ -184,11 +186,17 @@ class FileUploadWorker @AssistedInject constructor( workerId = id.toString(), userId = userId, title = title, - subtitle = subtitle + subtitle = subtitle, + courseId = courseId.takeIf { it != INVALID_ID }, + assignmentId = assignmentId.takeIf { it != INVALID_ID }, + folderId = if (action == ACTION_USER_FILE) parentFolderId.takeIf { it != INVALID_ID }.orDefault() else null, + attemptId = attemptId.takeIf { it != INVALID_ID } ) ) } + private fun shouldShowDashboardNotification(action: String) = action == ACTION_ASSIGNMENT_SUBMISSION || action == ACTION_USER_FILE + private suspend fun insertSubmissionComment(submissionComment: SubmissionComment): Long { val submissionCommentId = submissionCommentDao.insert( SubmissionCommentEntity(submissionComment) diff --git a/libs/pandautils/src/main/java/com/instructure/pandautils/room/AppDatabase.kt b/libs/pandautils/src/main/java/com/instructure/pandautils/room/AppDatabase.kt index a841c7f5a1..211c92dd66 100644 --- a/libs/pandautils/src/main/java/com/instructure/pandautils/room/AppDatabase.kt +++ b/libs/pandautils/src/main/java/com/instructure/pandautils/room/AppDatabase.kt @@ -15,7 +15,7 @@ import com.instructure.pandautils.room.entities.* SubmissionCommentEntity::class, PendingSubmissionCommentEntity::class, DashboardFileUploadEntity::class - ], version = 4 + ], version = 5 ) @TypeConverters(Converters::class) abstract class AppDatabase : RoomDatabase() { diff --git a/libs/pandautils/src/main/java/com/instructure/pandautils/room/AppDatabaseMigrations.kt b/libs/pandautils/src/main/java/com/instructure/pandautils/room/AppDatabaseMigrations.kt index 307f84193b..102422e5b7 100644 --- a/libs/pandautils/src/main/java/com/instructure/pandautils/room/AppDatabaseMigrations.kt +++ b/libs/pandautils/src/main/java/com/instructure/pandautils/room/AppDatabaseMigrations.kt @@ -42,7 +42,12 @@ val appDatabaseMigrations = arrayOf( createMigration(3, 4) { database -> database.execSQL("ALTER TABLE FileUploadInputEntity ADD COLUMN notificationId INTEGER") - } + }, + createMigration(4, 5) {database -> + database.execSQL("ALTER TABLE DashboardFileUploadEntity ADD COLUMN courseId INTEGER") + database.execSQL("ALTER TABLE DashboardFileUploadEntity ADD COLUMN assignmentId INTEGER") + database.execSQL("ALTER TABLE DashboardFileUploadEntity ADD COLUMN attemptId INTEGER") + database.execSQL("ALTER TABLE DashboardFileUploadEntity ADD COLUMN folderId INTEGER") + } ) - diff --git a/libs/pandautils/src/main/java/com/instructure/pandautils/room/entities/DashboardFileUploadEntity.kt b/libs/pandautils/src/main/java/com/instructure/pandautils/room/entities/DashboardFileUploadEntity.kt index eeb95097a5..47ed64d78e 100644 --- a/libs/pandautils/src/main/java/com/instructure/pandautils/room/entities/DashboardFileUploadEntity.kt +++ b/libs/pandautils/src/main/java/com/instructure/pandautils/room/entities/DashboardFileUploadEntity.kt @@ -9,5 +9,9 @@ data class DashboardFileUploadEntity( val workerId: String, val userId: Long, val title: String?, - val subtitle: String? + val subtitle: String?, + val courseId: Long?, + val assignmentId: Long?, + val attemptId: Long?, + val folderId: Long? ) diff --git a/libs/pandautils/src/main/res/layout/item_dashboard_upload.xml b/libs/pandautils/src/main/res/layout/item_dashboard_upload.xml index cce05d263a..13f1f6c94b 100644 --- a/libs/pandautils/src/main/res/layout/item_dashboard_upload.xml +++ b/libs/pandautils/src/main/res/layout/item_dashboard_upload.xml @@ -74,29 +74,35 @@ + + Date: Thu, 27 Apr 2023 15:19:02 +0200 Subject: [PATCH 021/103] Fix some nightly tests again (#1955) --- .../java/com/instructure/student/ui/pages/TodoPage.kt | 4 +--- .../java/com/instructure/teacher/ui/InboxPageTest.kt | 4 ++-- .../java/com/instructure/teacher/ui/pages/InboxPage.kt | 1 - 3 files changed, 3 insertions(+), 6 deletions(-) diff --git a/apps/student/src/androidTest/java/com/instructure/student/ui/pages/TodoPage.kt b/apps/student/src/androidTest/java/com/instructure/student/ui/pages/TodoPage.kt index 75064e0331..a189f8c8e9 100644 --- a/apps/student/src/androidTest/java/com/instructure/student/ui/pages/TodoPage.kt +++ b/apps/student/src/androidTest/java/com/instructure/student/ui/pages/TodoPage.kt @@ -16,9 +16,7 @@ */ package com.instructure.student.ui.pages -import android.widget.Button import androidx.test.espresso.assertion.ViewAssertions.doesNotExist -import androidx.test.espresso.matcher.ViewMatchers.isAssignableFrom import androidx.test.espresso.matcher.ViewMatchers.withText import com.instructure.canvasapi2.models.Assignment import com.instructure.canvasapi2.models.Quiz @@ -84,7 +82,7 @@ class TodoPage: BasePage(R.id.todoPage) { fun chooseFavoriteCourseFilter() { onView(withId(R.id.todoListFilter)).click() onView(withText(R.string.favoritedCoursesLabel) + withParent(R.id.select_dialog_listview)).click() - onView(allOf(isAssignableFrom(Button::class.java), withText(R.string.ok))).click() + onView(withText(R.string.ok)).click() } fun clearFilter() { diff --git a/apps/teacher/src/androidTest/java/com/instructure/teacher/ui/InboxPageTest.kt b/apps/teacher/src/androidTest/java/com/instructure/teacher/ui/InboxPageTest.kt index 129b79248e..2684b3a971 100644 --- a/apps/teacher/src/androidTest/java/com/instructure/teacher/ui/InboxPageTest.kt +++ b/apps/teacher/src/androidTest/java/com/instructure/teacher/ui/InboxPageTest.kt @@ -83,7 +83,7 @@ class InboxPageTest: TeacherTest() { @TestMetaData(Priority.IMPORTANT, FeatureCategory.INBOX, TestCategory.INTERACTION) fun archiveMultipleConversations() { val data = createInitialData() - data.addConversations(userId = data.teachers.first().id, messageBody = "Short body") + val conversation1 = data.addConversation( senderId = data.students.first().id, receiverIds = listOf(data.teachers.first().id), @@ -94,6 +94,7 @@ class InboxPageTest: TeacherTest() { receiverIds = listOf(data.teachers.first().id), messageBody = "Body 2", messageSubject = "Subject 2") + data.addConversations(userId = data.teachers.first().id, messageBody = "Short body") navigateToInbox(data, data.teachers.first()) inboxPage.selectConversations(listOf(conversation1.subject!!, conversation2.subject!!)) @@ -102,7 +103,6 @@ class InboxPageTest: TeacherTest() { inboxPage.assertConversationNotDisplayed(conversation2.subject!!) inboxPage.assertEditToolbarIs(ViewMatchers.Visibility.GONE) - inboxPage.refresh() inboxPage.filterInbox("Archived") inboxPage.assertConversationDisplayed(conversation1.subject!!) inboxPage.assertConversationDisplayed(conversation2.subject!!) diff --git a/apps/teacher/src/androidTest/java/com/instructure/teacher/ui/pages/InboxPage.kt b/apps/teacher/src/androidTest/java/com/instructure/teacher/ui/pages/InboxPage.kt index 7a32b23584..1317ba4bef 100644 --- a/apps/teacher/src/androidTest/java/com/instructure/teacher/ui/pages/InboxPage.kt +++ b/apps/teacher/src/androidTest/java/com/instructure/teacher/ui/pages/InboxPage.kt @@ -228,7 +228,6 @@ class InboxPage: BasePage() { } fun selectConversations(conversations: List) { - refresh() for(conversation in conversations) { selectConversation(conversation) } From 1fbaecaa84664c468657633ca22ccbef63e0ee15 Mon Sep 17 00:00:00 2001 From: Kristof Deak <92309696+kdeakinstructure@users.noreply.github.com> Date: Tue, 2 May 2023 14:16:07 +0200 Subject: [PATCH 022/103] Stub breaking testFilters Multi API level test (#1961) --- .../instructure/student/ui/interaction/TodoInteractionTest.kt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/apps/student/src/androidTest/java/com/instructure/student/ui/interaction/TodoInteractionTest.kt b/apps/student/src/androidTest/java/com/instructure/student/ui/interaction/TodoInteractionTest.kt index 25eab4948a..b7129b3e7e 100644 --- a/apps/student/src/androidTest/java/com/instructure/student/ui/interaction/TodoInteractionTest.kt +++ b/apps/student/src/androidTest/java/com/instructure/student/ui/interaction/TodoInteractionTest.kt @@ -17,6 +17,7 @@ package com.instructure.student.ui.interaction import androidx.test.espresso.Espresso import com.instructure.canvas.espresso.StubLandscape +import com.instructure.canvas.espresso.StubMultiAPILevel import com.instructure.canvas.espresso.mockCanvas.MockCanvas import com.instructure.canvas.espresso.mockCanvas.addAssignment import com.instructure.canvas.espresso.mockCanvas.addQuizToCourse @@ -65,6 +66,7 @@ class TodoInteractionTest : StudentTest() { @Test @StubLandscape("Stubbed because on lowres device in landscape mode, the space is too narrow to scroll properly. Will be refactored and running when we changed to non-lowres device on nightly runs.") + @StubMultiAPILevel("Somehow the 'OK' button within chooseFavoriteCourseFilter row is not clickable and not shown on the layout inspector as well.") @TestMetaData(Priority.IMPORTANT, FeatureCategory.TODOS, TestCategory.INTERACTION, false) fun testFilters() { val data = goToTodos(courseCount = 2, favoriteCourseCount = 1) From 84664cc761ddd3edc604edf2628ba8657437f67d Mon Sep 17 00:00:00 2001 From: Tamas Kozmer <72397075+tamaskozmer@users.noreply.github.com> Date: Tue, 2 May 2023 15:46:43 +0200 Subject: [PATCH 023/103] [MBL-16685][Teacher] Tablet: Unexpected error when trying to navigate back from inbox message attachment view #1962 refs: MBL-16685 affects: Teacher release note: none --- .../teacher/activities/InitActivity.kt | 13 +++++------ .../teacher/router/RouteMatcher.kt | 2 ++ .../teacher/utils/ModelExtensions.kt | 22 ++++++++++++++----- .../activities/BasePresenterActivity.kt | 2 +- 4 files changed, 26 insertions(+), 13 deletions(-) diff --git a/apps/teacher/src/main/java/com/instructure/teacher/activities/InitActivity.kt b/apps/teacher/src/main/java/com/instructure/teacher/activities/InitActivity.kt index da9d2116eb..d59d013855 100644 --- a/apps/teacher/src/main/java/com/instructure/teacher/activities/InitActivity.kt +++ b/apps/teacher/src/main/java/com/instructure/teacher/activities/InitActivity.kt @@ -450,6 +450,8 @@ class InitActivity : BasePresenterActivity 0) { - fm.popBackStackImmediate(fm.getBackStackEntryAt(0).id, FragmentManager.POP_BACK_STACK_INCLUSIVE) + if (clearBackStack) { + if (fm.backStackEntryCount > 0) { + fm.popBackStackImmediate(fm.getBackStackEntryAt(0).id, FragmentManager.POP_BACK_STACK_INCLUSIVE) + } } else { ft.addToBackStack(null) } @@ -574,7 +574,6 @@ class InitActivity : BasePresenterActivity { PdfFragment() val bundle = ViewPdfFragment.newInstance(url ?: "", toolbarColor, editableFile).nonNullArgs - RouteMatcher.route(context, Route(null, ViewPdfFragment::class.java, null, bundle)) + if (fullScreen) { + RouteMatcher.route(context, Route(ViewPdfFragment::class.java, null, bundle)) + } else { + RouteMatcher.route(context, Route(null, ViewPdfFragment::class.java, null, bundle)) + } } // Audio/Video contentType.startsWith("video") || contentType.startsWith("audio") -> { @@ -83,12 +87,20 @@ fun viewMedia(context: Context, filename: String, contentType: String, url: Stri contentType.startsWith("image") -> { val title = displayName ?: filename val bundle = ViewImageFragment.newInstance(title, Uri.parse(url), contentType, true, toolbarColor, editableFile).nonNullArgs - RouteMatcher.route(context, Route(null, ViewImageFragment::class.java, null, bundle)) + if (fullScreen) { + RouteMatcher.route(context, Route(ViewImageFragment::class.java, null, bundle)) + } else { + RouteMatcher.route(context, Route(null, ViewImageFragment::class.java, null, bundle)) + } } // HTML contentType == "text/html" || extension == "htm" || extension == "html" -> { val bundle = ViewHtmlFragment.makeDownloadBundle(url ?: "", filename, toolbarColor, editableFile) - RouteMatcher.route(context, Route(null, ViewHtmlFragment::class.java, null, bundle)) + if (fullScreen) { + RouteMatcher.route(context, Route(ViewHtmlFragment::class.java, null, bundle)) + } else { + RouteMatcher.route(context, Route(null, ViewHtmlFragment::class.java, null, bundle)) + } } // Multipart (Unknown) contentType == "multipart/form-data" -> { diff --git a/libs/pandautils/src/main/java/com/instructure/pandautils/activities/BasePresenterActivity.kt b/libs/pandautils/src/main/java/com/instructure/pandautils/activities/BasePresenterActivity.kt index eb0bffc4e2..5a62648333 100644 --- a/libs/pandautils/src/main/java/com/instructure/pandautils/activities/BasePresenterActivity.kt +++ b/libs/pandautils/src/main/java/com/instructure/pandautils/activities/BasePresenterActivity.kt @@ -57,7 +57,7 @@ abstract class BasePresenterActivity, VIEW> : Presen if (supportFragmentManager.backStackEntryCount > 0) { val fragments = supportFragmentManager.fragments if (fragments.isNotEmpty()) { - return fragments[supportFragmentManager.backStackEntryCount - 1] + return fragments.getOrNull(supportFragmentManager.backStackEntryCount - 1) } } else { return supportFragmentManager.fragments.lastOrNull() From 25de8b9cee3e81300ff10a5d117742fe85b8023e Mon Sep 17 00:00:00 2001 From: Kristof Deak <92309696+kdeakinstructure@users.noreply.github.com> Date: Fri, 5 May 2023 10:35:01 +0200 Subject: [PATCH 024/103] [MBL-16526][Student][Teacher] - Implement add files to comments tab test #1965 --- .../student/ui/e2e/AssignmentsE2ETest.kt | 69 ++++++++++- .../teacher/ui/e2e/AssignmentE2ETest.kt | 108 +++++++++++++++++- 2 files changed, 175 insertions(+), 2 deletions(-) diff --git a/apps/student/src/androidTest/java/com/instructure/student/ui/e2e/AssignmentsE2ETest.kt b/apps/student/src/androidTest/java/com/instructure/student/ui/e2e/AssignmentsE2ETest.kt index ef04d7437c..2d98a6a603 100644 --- a/apps/student/src/androidTest/java/com/instructure/student/ui/e2e/AssignmentsE2ETest.kt +++ b/apps/student/src/androidTest/java/com/instructure/student/ui/e2e/AssignmentsE2ETest.kt @@ -24,7 +24,14 @@ import com.instructure.canvas.espresso.E2E import com.instructure.dataseeding.api.AssignmentGroupsApi import com.instructure.dataseeding.api.AssignmentsApi import com.instructure.dataseeding.api.SubmissionsApi -import com.instructure.dataseeding.model.* +import com.instructure.dataseeding.model.AssignmentApiModel +import com.instructure.dataseeding.model.AttachmentApiModel +import com.instructure.dataseeding.model.CanvasUserApiModel +import com.instructure.dataseeding.model.CourseApiModel +import com.instructure.dataseeding.model.FileUploadType +import com.instructure.dataseeding.model.GradingType +import com.instructure.dataseeding.model.SubmissionApiModel +import com.instructure.dataseeding.model.SubmissionType import com.instructure.dataseeding.util.ago import com.instructure.dataseeding.util.days import com.instructure.dataseeding.util.fromNow @@ -448,6 +455,52 @@ class AssignmentsE2ETest: StudentTest() { submissionDetailsPage.assertAudioCommentDisplayed() } + @E2E + @Test + @TestMetaData(Priority.IMPORTANT, FeatureCategory.COMMENTS, TestCategory.E2E) + fun testAddFileCommentE2E() { + + Log.d(PREPARATION_TAG,"Seeding data.") + val data = seedData(students = 1, teachers = 1, courses = 1) + val student = data.studentsList[0] + val teacher = data.teachersList[0] + val course = data.coursesList[0] + + Log.d(PREPARATION_TAG,"Seeding assignment for ${course.name} course.") + val assignment = createAssignment(course.id, teacher, GradingType.POINTS, 15.0, 1.days.fromNow.iso8601) + + Log.d(PREPARATION_TAG,"Submit ${assignment.name} assignment for ${student.name} student.") + submitAssignment(assignment, course, student) + + Log.d(STEP_TAG, "Login with user: ${student.name}, login id: ${student.loginId}.") + tokenLogin(student) + dashboardPage.waitForRender() + + Log.d(STEP_TAG,"Seed a comment attachment upload.") + val commentUploadInfo = uploadTextFile( + assignmentId = assignment.id, + courseId = course.id, + token = student.token, + fileUploadType = FileUploadType.COMMENT_ATTACHMENT + ) + commentOnSubmission(student, course, assignment, commentUploadInfo) + + Log.d(STEP_TAG,"Select ${course.name} course and navigate to it's Assignments Page.") + dashboardPage.selectCourse(course) + courseBrowserPage.selectAssignments() + + Log.d(STEP_TAG,"Click on ${assignment.name} assignment.") + assignmentListPage.clickAssignment(assignment) + + Log.d(STEP_TAG,"Assert that ${commentUploadInfo.fileName} file is displayed as a comment by ${student.name} student.") + assignmentDetailsPage.goToSubmissionDetails() + submissionDetailsPage.openComments() + submissionDetailsPage.assertCommentAttachmentDisplayed(commentUploadInfo.fileName, student) + + Log.d(STEP_TAG,"Navigate to Submission Details Page and open Files Tab.") + submissionDetailsPage.openFiles() + } + @E2E @Test @TestMetaData(Priority.IMPORTANT, FeatureCategory.ASSIGNMENTS, TestCategory.E2E) @@ -680,4 +733,18 @@ class AssignmentsE2ETest: StudentTest() { postedGrade = postedGrade, excused = false ) + + private fun commentOnSubmission( + student: CanvasUserApiModel, + course: CourseApiModel, + assignment: AssignmentApiModel, + commentUploadInfo: AttachmentApiModel + ) { + SubmissionsApi.commentOnSubmission( + studentToken = student.token, + courseId = course.id, + assignmentId = assignment.id, + fileIds = mutableListOf(commentUploadInfo.id) + ) + } } \ No newline at end of file diff --git a/apps/teacher/src/androidTest/java/com/instructure/teacher/ui/e2e/AssignmentE2ETest.kt b/apps/teacher/src/androidTest/java/com/instructure/teacher/ui/e2e/AssignmentE2ETest.kt index 4f51cf903d..65bec5e9a1 100644 --- a/apps/teacher/src/androidTest/java/com/instructure/teacher/ui/e2e/AssignmentE2ETest.kt +++ b/apps/teacher/src/androidTest/java/com/instructure/teacher/ui/e2e/AssignmentE2ETest.kt @@ -22,8 +22,10 @@ import com.instructure.canvas.espresso.E2E import com.instructure.dataseeding.api.AssignmentsApi import com.instructure.dataseeding.api.SubmissionsApi import com.instructure.dataseeding.model.AssignmentApiModel +import com.instructure.dataseeding.model.AttachmentApiModel import com.instructure.dataseeding.model.CanvasUserApiModel import com.instructure.dataseeding.model.CourseApiModel +import com.instructure.dataseeding.model.FileUploadType import com.instructure.dataseeding.model.GradingType import com.instructure.dataseeding.model.SubmissionType import com.instructure.dataseeding.util.days @@ -36,7 +38,12 @@ import com.instructure.panda_annotations.Priority import com.instructure.panda_annotations.TestCategory import com.instructure.panda_annotations.TestMetaData import com.instructure.teacher.R -import com.instructure.teacher.ui.utils.* +import com.instructure.teacher.ui.utils.TeacherTest +import com.instructure.teacher.ui.utils.seedAssignmentSubmission +import com.instructure.teacher.ui.utils.seedAssignments +import com.instructure.teacher.ui.utils.seedData +import com.instructure.teacher.ui.utils.tokenLogin +import com.instructure.teacher.ui.utils.uploadTextFile import dagger.hilt.android.testing.HiltAndroidTest import org.junit.Test @@ -285,6 +292,61 @@ class AssignmentE2ETest : TeacherTest() { } + @E2E + @Test + @TestMetaData(Priority.IMPORTANT, FeatureCategory.COMMENTS, TestCategory.E2E) + fun testAddFileCommentE2E() { + + Log.d(PREPARATION_TAG, "Seeding data.") + val data = seedData(students = 1, teachers = 1, courses = 1) + val student = data.studentsList[0] + val teacher = data.teachersList[0] + val course = data.coursesList[0] + + Log.d(PREPARATION_TAG, "Seed a text assignment/file/submission.") + val assignment = createAssignment(course, teacher) + + Log.d(PREPARATION_TAG, "Seed a text file.") + val submissionUploadInfo = uploadTextFile( + assignmentId = assignment.id, + courseId = course.id, + token = student.token, + fileUploadType = FileUploadType.ASSIGNMENT_SUBMISSION + ) + + Log.d(PREPARATION_TAG, "Submit the ${assignment.name} assignment.") + submitCourseAssignment(course, assignment, submissionUploadInfo, student) + + Log.d(PREPARATION_TAG,"Seed a comment attachment upload.") + val commentUploadInfo = uploadTextFile( + assignmentId = assignment.id, + courseId = course.id, + token = student.token, + fileUploadType = FileUploadType.COMMENT_ATTACHMENT + ) + + commentOnSubmission(student, course, assignment, commentUploadInfo) + + Log.d(STEP_TAG, "Login with user: ${teacher.name}, login id: ${teacher.loginId}.") + tokenLogin(teacher) + dashboardPage.waitForRender() + + Log.d(STEP_TAG,"Select ${course.name} course and navigate to it's Assignments Page.") + dashboardPage.selectCourse(course) + courseBrowserPage.openAssignmentsTab() + + Log.d(STEP_TAG,"Click on ${assignment.name} assignment and navigate to Submissions Page.") + assignmentListPage.clickAssignment(assignment) + assignmentDetailsPage.openSubmissionsPage() + + Log.d(STEP_TAG,"Click on ${student.name} student's submission.") + assignmentSubmissionListPage.clickSubmission(student) + + Log.d(STEP_TAG,"Assert that ${submissionUploadInfo.fileName} file. Navigate to Comments Tab and ${commentUploadInfo.fileName} comment attachment is displayed.") + speedGraderPage.selectCommentsTab() + assignmentSubmissionListPage.assertCommentAttachmentDisplayedCommon(commentUploadInfo.fileName, student.shortName) + } + private fun gradeSubmission( teacher: CanvasUserApiModel, course: CourseApiModel, @@ -301,4 +363,48 @@ class AssignmentE2ETest : TeacherTest() { ) } + private fun createAssignment( + course: CourseApiModel, + teacher: CanvasUserApiModel + ): AssignmentApiModel { + return AssignmentsApi.createAssignment( + AssignmentsApi.CreateAssignmentRequest( + courseId = course.id, + withDescription = false, + submissionTypes = listOf(SubmissionType.ONLINE_UPLOAD), + allowedExtensions = listOf("txt"), + teacherToken = teacher.token + ) + ) + } + + private fun submitCourseAssignment( + course: CourseApiModel, + assignment: AssignmentApiModel, + submissionUploadInfo: AttachmentApiModel, + student: CanvasUserApiModel + ) { + SubmissionsApi.submitCourseAssignment( + submissionType = SubmissionType.ONLINE_UPLOAD, + courseId = course.id, + assignmentId = assignment.id, + fileIds = mutableListOf(submissionUploadInfo.id), + studentToken = student.token + ) + } + + private fun commentOnSubmission( + student: CanvasUserApiModel, + course: CourseApiModel, + assignment: AssignmentApiModel, + commentUploadInfo: AttachmentApiModel + ) { + SubmissionsApi.commentOnSubmission( + studentToken = student.token, + courseId = course.id, + assignmentId = assignment.id, + fileIds = mutableListOf(commentUploadInfo.id) + ) + } + } \ No newline at end of file From 88c76cf7887a6693cc4a3deef9acf9299ca7f4b4 Mon Sep 17 00:00:00 2001 From: Kristof Nemere <109959688+kristofnemere@users.noreply.github.com> Date: Mon, 8 May 2023 10:51:14 +0200 Subject: [PATCH 025/103] [MBL-16709][Student] Module items do not unlock until refresh refs: MBL-16709 affects: Student release note: none * Fixed a bug where module items did not unlock until refresh (and also mark as read wasn't working) * Added logic to mark next module as unlocked * Minor fix --- .../CourseModuleProgressionFragment.kt | 29 ++++++++++++++----- 1 file changed, 21 insertions(+), 8 deletions(-) diff --git a/apps/student/src/main/java/com/instructure/student/fragment/CourseModuleProgressionFragment.kt b/apps/student/src/main/java/com/instructure/student/fragment/CourseModuleProgressionFragment.kt index 9140c858f7..384487c722 100644 --- a/apps/student/src/main/java/com/instructure/student/fragment/CourseModuleProgressionFragment.kt +++ b/apps/student/src/main/java/com/instructure/student/fragment/CourseModuleProgressionFragment.kt @@ -30,6 +30,7 @@ import androidx.lifecycle.lifecycleScope import com.instructure.canvasapi2.StatusCallback import com.instructure.canvasapi2.managers.* import com.instructure.canvasapi2.models.* +import com.instructure.canvasapi2.models.ModuleObject.State import com.instructure.canvasapi2.utils.* import com.instructure.canvasapi2.utils.pageview.PageView import com.instructure.canvasapi2.utils.weave.WeaveJob @@ -296,7 +297,9 @@ class CourseModuleProgressionFragment : ParentFragment(), Bookmarkable { updatePrevNextButtons(currentPos) - val completionRequirement = getCurrentModuleItem(currentPos)!!.completionRequirement + val currentModuleItem = getCurrentModuleItem(currentPos) ?: return + + val completionRequirement = currentModuleItem.completionRequirement if (completionRequirement != null && modules[groupPos].sequentialProgress) { // Reload the sequential module object to update the subsequent items that may now be unlocked // The user has viewed the item, and may have completed the contribute/submit requirements for a @@ -304,28 +307,38 @@ class CourseModuleProgressionFragment : ParentFragment(), Bookmarkable { addLockedIconIfNeeded(modules, items, groupPos, childPos) // Mark the item as viewed - markAsRead(modules[groupPos].id, getCurrentModuleItem(currentPos)!!.id) + markAsRead(currentModuleItem.moduleId, currentModuleItem.id) } - val moduleItem = getCurrentModuleItem(currentPos) - - updateModuleMarkDoneView(moduleItem) + updateModuleMarkDoneView(currentModuleItem) } private fun markAsRead(moduleId: Long, moduleItemId: Long) { markAsReadJob = tryWeave { // mark the moduleItem as viewed if we have a valid module id and item id, // but not the files, because they need to open or download those to view them - if(moduleId != 0L && moduleItemId != 0L && getCurrentModuleItem(currentPos)!!.type != ModuleItem.Type.File.toString()) { - awaitApi { ModuleManager.markModuleItemAsRead(canvasContext, moduleId, moduleItemId, it) } + if (moduleId != 0L && moduleItemId != 0L && getCurrentModuleItem(currentPos)!!.type != ModuleItem.Type.File.toString()) { + awaitApi { ModuleManager.markModuleItemAsRead(canvasContext, moduleId, moduleItemId, it) } // Update the module item locally, needed to unlock modules as the user ViewPages through them getCurrentModuleItem(currentPos)?.completionRequirement?.completed = true setupNextModule(getModuleItemGroup(currentPos)) + // Update the module state to indicate in the list that the module is completed + val module = modules.find { it.id == moduleId } ?: return@tryWeave + val isModuleCompleted = items.flatten().filter { it.moduleId == moduleId }.all { it.completionRequirement?.completed.orDefault() } + val updatedState = if (isModuleCompleted) State.Completed.apiString else module.state + // Update the module list fragment to show that these requirements are done, - ModuleUpdatedEvent(modules[groupPos]).post() + ModuleUpdatedEvent(module.copy(state = updatedState)).post() + + // Update the state of the next module to indicate in the list that it is unlocked + modules.getOrNull(modules.indexOf(module) + 1)?.let { + if (isModuleCompleted && it.state != State.Completed.apiString) { + ModuleUpdatedEvent(it.copy(state = State.Unlocked.apiString)).post() + } + } } } catch { Logger.e("Error marking module item as read. " + it.message) From 39edb8ab34cd765db835220a77b63dc00306d5da Mon Sep 17 00:00:00 2001 From: Kristof Deak <92309696+kdeakinstructure@users.noreply.github.com> Date: Mon, 8 May 2023 20:04:12 +0200 Subject: [PATCH 026/103] Introduce ReleaseExclude annotation and mark test with it. (#1967) --- .../student/ui/e2e/NotificationsE2ETest.kt | 2 ++ .../canvas/espresso/ReleaseExclude.kt | 22 +++++++++++++++++++ 2 files changed, 24 insertions(+) create mode 100644 automation/espresso/src/main/kotlin/com/instructure/canvas/espresso/ReleaseExclude.kt diff --git a/apps/student/src/androidTest/java/com/instructure/student/ui/e2e/NotificationsE2ETest.kt b/apps/student/src/androidTest/java/com/instructure/student/ui/e2e/NotificationsE2ETest.kt index bdeaec2ebf..11e47729ec 100644 --- a/apps/student/src/androidTest/java/com/instructure/student/ui/e2e/NotificationsE2ETest.kt +++ b/apps/student/src/androidTest/java/com/instructure/student/ui/e2e/NotificationsE2ETest.kt @@ -18,6 +18,7 @@ package com.instructure.student.ui.e2e import android.util.Log import com.instructure.canvas.espresso.E2E +import com.instructure.canvas.espresso.ReleaseExclude import com.instructure.canvas.espresso.refresh import com.instructure.dataseeding.api.AssignmentsApi import com.instructure.dataseeding.api.QuizzesApi @@ -49,6 +50,7 @@ class NotificationsE2ETest : StudentTest() { override fun enableAndConfigureAccessibilityChecks() = Unit + @ReleaseExclude("The notifications API sometimes is slow and the test is breaking because the notifications aren't show up in time.") @E2E @Test @TestMetaData(Priority.MANDATORY, FeatureCategory.ASSIGNMENTS, TestCategory.E2E) diff --git a/automation/espresso/src/main/kotlin/com/instructure/canvas/espresso/ReleaseExclude.kt b/automation/espresso/src/main/kotlin/com/instructure/canvas/espresso/ReleaseExclude.kt new file mode 100644 index 0000000000..382ce737a4 --- /dev/null +++ b/automation/espresso/src/main/kotlin/com/instructure/canvas/espresso/ReleaseExclude.kt @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2023 - present Instructure, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.instructure.canvas.espresso + +// When applied to a test method, denotes that the test is stubbed out from the release process because of 3rd party flakiness or any other reason (explained in the parameter). +@Target(AnnotationTarget.FUNCTION) +@Retention(AnnotationRetention.RUNTIME) +annotation class ReleaseExclude(val excludeReason: String = "") \ No newline at end of file From 929e34390c74e141029a72067f96a80cd7825e5b Mon Sep 17 00:00:00 2001 From: Kristof Nemere <109959688+kristofnemere@users.noreply.github.com> Date: Tue, 9 May 2023 11:29:15 +0200 Subject: [PATCH 027/103] [MBL-16713][Student] Distribution score graph does not appear on app refs: MBL-16713 affects: Student release note: Added score graph to the grade cell on assignment details screen. --- .../student/binding/BindingAdapters.kt | 12 +++- .../gradecellview/GradeCellViewData.kt | 29 ++++++++- .../ui/gradeCell/GradeStatisticsView.kt | 9 ++- .../view_student_enhanced_grade_cell.xml | 61 ++++++++++++++++++- .../res/layout/view_student_grade_cell.xml | 4 +- 5 files changed, 102 insertions(+), 13 deletions(-) diff --git a/apps/student/src/main/java/com/instructure/student/binding/BindingAdapters.kt b/apps/student/src/main/java/com/instructure/student/binding/BindingAdapters.kt index 79edba18fd..4c065934d4 100644 --- a/apps/student/src/main/java/com/instructure/student/binding/BindingAdapters.kt +++ b/apps/student/src/main/java/com/instructure/student/binding/BindingAdapters.kt @@ -21,6 +21,8 @@ import androidx.databinding.BindingAdapter import com.google.android.material.tabs.TabLayout import com.instructure.student.features.elementary.course.ElementaryCourseTab import com.instructure.student.mobius.assignmentDetails.ui.gradeCell.DonutChartView +import com.instructure.student.mobius.assignmentDetails.ui.gradeCell.GradeCellViewState +import com.instructure.student.mobius.assignmentDetails.ui.gradeCell.GradeStatisticsView @BindingAdapter("tabs") fun bindCourseTabs(tabLayout: TabLayout, tabs: List?) { @@ -38,4 +40,12 @@ fun DonutChartView.setProgress(progress: Float, @ColorInt color: Int, @ColorInt setColor(color) setTrackColor(trackColor) setPercentage(progress, true) -} \ No newline at end of file +} + +@BindingAdapter("stats", "color") +fun GradeStatisticsView.setStatistics(stats: GradeCellViewState.GradeStats?, @ColorInt color: Int) { + stats?.let { + setStats(stats) + setAccentColor(color) + } +} diff --git a/apps/student/src/main/java/com/instructure/student/features/assignmentdetails/gradecellview/GradeCellViewData.kt b/apps/student/src/main/java/com/instructure/student/features/assignmentdetails/gradecellview/GradeCellViewData.kt index 60aa9a2ba8..a9d8eefaf0 100644 --- a/apps/student/src/main/java/com/instructure/student/features/assignmentdetails/gradecellview/GradeCellViewData.kt +++ b/apps/student/src/main/java/com/instructure/student/features/assignmentdetails/gradecellview/GradeCellViewData.kt @@ -9,6 +9,7 @@ import com.instructure.pandautils.utils.ThemedColor import com.instructure.pandautils.utils.getContentDescriptionForMinusGradeString import com.instructure.pandautils.utils.orDefault import com.instructure.student.R +import com.instructure.student.mobius.assignmentDetails.ui.gradeCell.GradeCellViewState data class GradeCellViewData( val courseColor: ThemedColor, @@ -22,7 +23,8 @@ data class GradeCellViewData( val gradeCellContentDescription: String = "", val outOf: String = "", val latePenalty: String = "", - val finalGrade: String = "" + val finalGrade: String = "", + val stats: GradeCellViewState.GradeStats? = null ) { val backgroundColorWithAlpha = ColorUtils.setAlphaComponent(courseColor.backgroundColor(), (.25 * 255).toInt()) @@ -143,6 +145,28 @@ data class GradeCellViewData( finalGrade = resources.getString(R.string.finalGradeFormatted, submission.grade) } + val stats = assignment.scoreStatistics?.let { stats -> + GradeCellViewState.GradeStats( + score = submission.score, + outOf = assignment.pointsPossible, + min = stats.min, + max = stats.max, + mean = stats.mean, + minText = resources.getString( + R.string.scoreStatisticsLow, + NumberHelper.formatDecimal(stats.min, 1, true) + ), + maxText = resources.getString( + R.string.scoreStatisticsHigh, + NumberHelper.formatDecimal(stats.max, 1, true) + ), + meanText = resources.getString( + R.string.scoreStatisticsMean, + NumberHelper.formatDecimal(stats.mean, 1, true) + ) + ) + } + GradeCellViewData( courseColor = courseColor, state = State.GRADED, @@ -153,7 +177,8 @@ data class GradeCellViewData( gradeCellContentDescription = gradeCellContentDescription, outOf = outOfText, latePenalty = latePenalty, - finalGrade = finalGrade + finalGrade = finalGrade, + stats = stats ) } } diff --git a/apps/student/src/main/java/com/instructure/student/mobius/assignmentDetails/ui/gradeCell/GradeStatisticsView.kt b/apps/student/src/main/java/com/instructure/student/mobius/assignmentDetails/ui/gradeCell/GradeStatisticsView.kt index d50e5ff43e..41b8aedbe4 100644 --- a/apps/student/src/main/java/com/instructure/student/mobius/assignmentDetails/ui/gradeCell/GradeStatisticsView.kt +++ b/apps/student/src/main/java/com/instructure/student/mobius/assignmentDetails/ui/gradeCell/GradeStatisticsView.kt @@ -2,7 +2,6 @@ package com.instructure.student.mobius.assignmentDetails.ui.gradeCell import android.content.Context import android.graphics.Canvas -import android.graphics.Color import android.graphics.Paint import android.util.AttributeSet import android.view.View @@ -14,7 +13,7 @@ import com.instructure.student.R class GradeStatisticsView(context: Context, attrs: AttributeSet) : View(context, attrs) { private var stats: GradeCellViewState.GradeStats? = null - private val sidePadding: Float = context.DP(16) + private val sidePadding: Float = context.DP(2) private val endMarkerHeight: Float = context.DP(16) private val minMaxHeight: Float = context.DP(16) private val scoreCircleRadius: Float = context.DP(7) @@ -22,7 +21,7 @@ class GradeStatisticsView(context: Context, attrs: AttributeSet) : View(context, private val linePaint: Paint = Paint(Paint.ANTI_ALIAS_FLAG).apply { style = Paint.Style.STROKE - strokeCap = Paint.Cap.ROUND + strokeCap = Paint.Cap.SQUARE isAntiAlias = true strokeWidth = context.DP(2) color = ContextCompat.getColor(context, R.color.backgroundMedium) @@ -30,7 +29,7 @@ class GradeStatisticsView(context: Context, attrs: AttributeSet) : View(context, private val darkLinePaint: Paint = Paint(Paint.ANTI_ALIAS_FLAG).apply { style = Paint.Style.STROKE - strokeCap = Paint.Cap.ROUND + strokeCap = Paint.Cap.SQUARE isAntiAlias = true strokeWidth = context.DP(3) color = ContextCompat.getColor(context, R.color.textDark) @@ -38,7 +37,7 @@ class GradeStatisticsView(context: Context, attrs: AttributeSet) : View(context, private val meanLinePaint: Paint = Paint(Paint.ANTI_ALIAS_FLAG).apply { style = Paint.Style.STROKE - strokeCap = Paint.Cap.ROUND + strokeCap = Paint.Cap.SQUARE isAntiAlias = true strokeWidth = context.DP(3) color = ContextCompat.getColor(context, R.color.textDarkest) diff --git a/apps/student/src/main/res/layout/view_student_enhanced_grade_cell.xml b/apps/student/src/main/res/layout/view_student_enhanced_grade_cell.xml index 83ec241625..15cf56ca1a 100644 --- a/apps/student/src/main/res/layout/view_student_enhanced_grade_cell.xml +++ b/apps/student/src/main/res/layout/view_student_enhanced_grade_cell.xml @@ -169,12 +169,13 @@ android:layout_height="130dp" android:layout_marginTop="@dimen/grade_cell_chart_top_margin" android:layout_marginEnd="12dp" - android:layout_marginBottom="20dp" + android:layout_marginBottom="8dp" android:visibility="@{viewData.state == viewData.State.GRADED ? View.VISIBLE : View.GONE}" app:color="@{viewData.showIncompleteIcon ? @color/textDark : viewData.courseColor.backgroundColor()}" - app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintBottom_toTopOf="@id/statisticsView" app:layout_constraintEnd_toStartOf="@id/guideline" app:layout_constraintTop_toBottomOf="@id/gradeLabel" + app:layout_goneMarginBottom="20dp" app:progress="@{viewData.chartPercent}" app:trackColor="@{viewData.backgroundColorWithAlpha}" /> @@ -311,6 +312,62 @@ app:layout_constraintTop_toBottomOf="@id/latePenalty" tools:text="Final Grade: 73 pts" /> + + + + + + + + \ No newline at end of file diff --git a/apps/student/src/main/res/layout/view_student_grade_cell.xml b/apps/student/src/main/res/layout/view_student_grade_cell.xml index d3fd6f2986..3f864caa2d 100644 --- a/apps/student/src/main/res/layout/view_student_grade_cell.xml +++ b/apps/student/src/main/res/layout/view_student_grade_cell.xml @@ -191,9 +191,7 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:gravity="center_horizontal" - android:orientation="horizontal" - android:paddingStart="16dp" - android:paddingEnd="16dp"> + android:orientation="horizontal"> Date: Thu, 11 May 2023 14:15:08 +0200 Subject: [PATCH 028/103] [MBL-16712][Student][Teacher] Fix new crashes (#1970) refs: MBL-16712 affects: Student, Teacher release note: none * Fixed teacher binding crashes. * Fixed theme update network errors. * Fixed assignment details crash. * Fixed ViewBinding crashes. * Fixed load media NPE in ParentFragment. --- .../student/dialog/WhatIfDialogStyled.kt | 9 ------- .../AssignmentDetailsViewModel.kt | 16 ++++++----- .../student/fragment/CourseBrowserFragment.kt | 1 + .../fragment/DiscussionDetailsFragment.kt | 4 +-- .../student/fragment/FileListFragment.kt | 2 +- .../student/fragment/GradesListFragment.kt | 1 + .../fragment/InternalWebviewFragment.kt | 2 +- .../student/fragment/ParentFragment.kt | 2 +- .../teacher/activities/InitActivity.kt | 12 ++++++--- .../adapters/StudentContextFragment.kt | 27 ++++++++++--------- .../fragments/InternalWebViewFragment.kt | 2 +- .../teacher/fragments/PageDetailsFragment.kt | 2 +- libs/pandares/src/main/res/values/strings.xml | 1 + 13 files changed, 44 insertions(+), 37 deletions(-) diff --git a/apps/student/src/main/java/com/instructure/student/dialog/WhatIfDialogStyled.kt b/apps/student/src/main/java/com/instructure/student/dialog/WhatIfDialogStyled.kt index bbbb27eb40..2ebff9c9bc 100644 --- a/apps/student/src/main/java/com/instructure/student/dialog/WhatIfDialogStyled.kt +++ b/apps/student/src/main/java/com/instructure/student/dialog/WhatIfDialogStyled.kt @@ -37,10 +37,6 @@ import kotlin.properties.Delegates @ScreenView(SCREEN_VIEW_WHAT_IF) class WhatIfDialogStyled : DialogFragment() { - init { - retainInstance = true - } - private var callback: (Double?, Double) -> Unit by Delegates.notNull() private var assignment: Assignment by ParcelableArg() private var textButtonColor: Int by IntArg() @@ -51,11 +47,6 @@ class WhatIfDialogStyled : DialogFragment() { fun onClick(assignment: Assignment, position: Int) } - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - retainInstance = true - } - override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { val builder = AlertDialog.Builder(requireContext()) .setTitle(getString(R.string.whatIfDialogText)) diff --git a/apps/student/src/main/java/com/instructure/student/features/assignmentdetails/AssignmentDetailsViewModel.kt b/apps/student/src/main/java/com/instructure/student/features/assignmentdetails/AssignmentDetailsViewModel.kt index 7c2c0ab375..f6c9d61ccd 100644 --- a/apps/student/src/main/java/com/instructure/student/features/assignmentdetails/AssignmentDetailsViewModel.kt +++ b/apps/student/src/main/java/com/instructure/student/features/assignmentdetails/AssignmentDetailsViewModel.kt @@ -220,13 +220,17 @@ class AssignmentDetailsViewModel @Inject constructor( private fun refreshAssignment() { viewModelScope.launch { - val assignmentResult = if (isObserver) { - assignmentManager.getAssignmentIncludeObserveesAsync(assignmentId, course?.id.orDefault(), true) - } else { - assignmentManager.getAssignmentWithHistoryAsync(assignmentId, course?.id.orDefault(), true) - }.await().dataOrThrow as Assignment + try { + val assignmentResult = if (isObserver) { + assignmentManager.getAssignmentIncludeObserveesAsync(assignmentId, course?.id.orDefault(), true) + } else { + assignmentManager.getAssignmentWithHistoryAsync(assignmentId, course?.id.orDefault(), true) + }.await().dataOrThrow as Assignment - _data.postValue(getViewData(assignmentResult, dbSubmission?.isDraft.orDefault())) + _data.postValue(getViewData(assignmentResult, dbSubmission?.isDraft.orDefault())) + } catch (e: Exception) { + _events.value = Event(AssignmentDetailAction.ShowToast(resources.getString(R.string.assignmentRefreshError))) + } } } diff --git a/apps/student/src/main/java/com/instructure/student/fragment/CourseBrowserFragment.kt b/apps/student/src/main/java/com/instructure/student/fragment/CourseBrowserFragment.kt index 4bd63fec45..693f665549 100644 --- a/apps/student/src/main/java/com/instructure/student/fragment/CourseBrowserFragment.kt +++ b/apps/student/src/main/java/com/instructure/student/fragment/CourseBrowserFragment.kt @@ -222,6 +222,7 @@ class CourseBrowserFragment : Fragment(), FragmentInteractions, AppBarLayout.OnO * Manages state of titles & subtitles when users scrolls */ override fun onOffsetChanged(appBarLayout: AppBarLayout?, verticalOffset: Int) { + if (view == null) return val percentage = Math.abs(verticalOffset).div(appBarLayout?.totalScrollRange?.toFloat() ?: 1F) diff --git a/apps/student/src/main/java/com/instructure/student/fragment/DiscussionDetailsFragment.kt b/apps/student/src/main/java/com/instructure/student/fragment/DiscussionDetailsFragment.kt index 67111527c7..add2fbdc0f 100644 --- a/apps/student/src/main/java/com/instructure/student/fragment/DiscussionDetailsFragment.kt +++ b/apps/student/src/main/java/com/instructure/student/fragment/DiscussionDetailsFragment.kt @@ -248,7 +248,7 @@ class DiscussionDetailsFragment : ParentFragment(), Bookmarkable { successfullyMarkedAsReadIds.forEach { binding.discussionRepliesWebViewWrapper.post { // Posting lets this escape Weave's lifecycle, so use a null-safe call on the webview here - binding.discussionRepliesWebViewWrapper.webView.loadUrl("javascript:markAsRead" + "('" + it.toString() + "')") + if (view != null) binding.discussionRepliesWebViewWrapper.webView.loadUrl("javascript:markAsRead" + "('" + it.toString() + "')") } } if (!groupDiscussion) { @@ -696,7 +696,7 @@ class DiscussionDetailsFragment : ParentFragment(), Bookmarkable { replyToDiscussionTopic.onClick { showReplyView(discussionTopicHeader.id) } loadHeaderHtmlJob = discussionTopicHeaderWebViewWrapper.webView.loadHtmlWithIframes(requireContext(), discussionTopicHeader.message, { - loadHTMLTopic(it, discussionTopicHeader.title) + if (view != null) loadHTMLTopic(it, discussionTopicHeader.title) }) attachmentIcon.setVisible(discussionTopicHeader.attachments.isNotEmpty()) diff --git a/apps/student/src/main/java/com/instructure/student/fragment/FileListFragment.kt b/apps/student/src/main/java/com/instructure/student/fragment/FileListFragment.kt index 633900e4ab..e41d5436e1 100644 --- a/apps/student/src/main/java/com/instructure/student/fragment/FileListFragment.kt +++ b/apps/student/src/main/java/com/instructure/student/fragment/FileListFragment.kt @@ -211,7 +211,7 @@ class FileListFragment : ParentFragment(), Bookmarkable, FileUploadDialogParent override fun onRefreshFinished() { setRefreshing(false) - if (recyclerAdapter?.size() == 0) { + if (recyclerAdapter?.size() == 0 && view != null) { setEmptyView(binding.emptyView, R.drawable.ic_panda_nofiles, R.string.noFiles, getNoFileSubtextId()) } } diff --git a/apps/student/src/main/java/com/instructure/student/fragment/GradesListFragment.kt b/apps/student/src/main/java/com/instructure/student/fragment/GradesListFragment.kt index 29a87d1dbd..97e68de475 100644 --- a/apps/student/src/main/java/com/instructure/student/fragment/GradesListFragment.kt +++ b/apps/student/src/main/java/com/instructure/student/fragment/GradesListFragment.kt @@ -241,6 +241,7 @@ class GradesListFragment : ParentFragment(), Bookmarkable { private val gradingPeriodsCallback = object : StatusCallback() { override fun onResponse(response: Response, linkHeaders: LinkHeaders, type: ApiType) { + if (view == null) return with(binding) { gradingPeriodsList = ArrayList() gradingPeriodsList.addAll(response.body()!!.gradingPeriodList) diff --git a/apps/student/src/main/java/com/instructure/student/fragment/InternalWebviewFragment.kt b/apps/student/src/main/java/com/instructure/student/fragment/InternalWebviewFragment.kt index 3bb125ecda..1185f145ad 100644 --- a/apps/student/src/main/java/com/instructure/student/fragment/InternalWebviewFragment.kt +++ b/apps/student/src/main/java/com/instructure/student/fragment/InternalWebviewFragment.kt @@ -408,7 +408,7 @@ open class InternalWebviewFragment : ParentFragment() { .build().toString() } - binding.canvasWebViewWrapper.webView.loadUrl(url!!, getReferer()) + if (view != null) binding.canvasWebViewWrapper.webView.loadUrl(url!!, getReferer()) } } } diff --git a/apps/student/src/main/java/com/instructure/student/fragment/ParentFragment.kt b/apps/student/src/main/java/com/instructure/student/fragment/ParentFragment.kt index f1c4c24112..f4939a341f 100644 --- a/apps/student/src/main/java/com/instructure/student/fragment/ParentFragment.kt +++ b/apps/student/src/main/java/com/instructure/student/fragment/ParentFragment.kt @@ -112,7 +112,7 @@ abstract class ParentFragment : DialogFragment(), FragmentInteractions, Navigati InternalWebviewFragment.loadInternalWebView(activity, InternalWebviewFragment.makeRoute(loadedMedia.bundle!!)) } else if (loadedMedia.intent != null && context != null) { // Show pdf with PSPDFkit - if (loadedMedia.intent!!.type!!.contains("pdf") && !loadedMedia.isUseOutsideApps) { + if (loadedMedia.intent?.type?.contains("pdf") == true && !loadedMedia.isUseOutsideApps) { val uri = loadedMedia.intent!!.data FileUtils.showPdfDocument(uri!!, loadedMedia, requireContext()) } else if (loadedMedia.intent?.type == "video/mp4") { diff --git a/apps/teacher/src/main/java/com/instructure/teacher/activities/InitActivity.kt b/apps/teacher/src/main/java/com/instructure/teacher/activities/InitActivity.kt index d59d013855..d089818bb0 100644 --- a/apps/teacher/src/main/java/com/instructure/teacher/activities/InitActivity.kt +++ b/apps/teacher/src/main/java/com/instructure/teacher/activities/InitActivity.kt @@ -22,6 +22,7 @@ import android.content.Intent import android.content.pm.PackageManager import android.content.res.Configuration import android.os.Bundle +import android.util.Log import android.view.View import android.widget.CompoundButton import android.widget.Toast @@ -87,6 +88,7 @@ import dagger.hilt.android.AndroidEntryPoint import kotlinx.coroutines.Job import kotlinx.coroutines.delay import kotlinx.coroutines.launch +import okio.IOException import org.greenrobot.eventbus.EventBus import org.greenrobot.eventbus.Subscribe import org.greenrobot.eventbus.ThreadMode @@ -200,9 +202,13 @@ class InitActivity : BasePresenterActivity { ThemeManager.getTheme(it, false) } - ThemePrefs.applyCanvasTheme(theme, this@InitActivity) - binding.bottomBar.applyTheme(ThemePrefs.brandColor, getColor(R.color.textDarkest)) + try { + val theme = awaitApi { ThemeManager.getTheme(it, false) } + ThemePrefs.applyCanvasTheme(theme, this@InitActivity) + binding.bottomBar.applyTheme(ThemePrefs.brandColor, getColor(R.color.textDarkest)) + } catch(e: IOException) { + LoggingUtility.log(e.stackTrace.toString(), Log.WARN) + } } } diff --git a/apps/teacher/src/main/java/com/instructure/teacher/adapters/StudentContextFragment.kt b/apps/teacher/src/main/java/com/instructure/teacher/adapters/StudentContextFragment.kt index 75b0303a2a..b30556b604 100644 --- a/apps/teacher/src/main/java/com/instructure/teacher/adapters/StudentContextFragment.kt +++ b/apps/teacher/src/main/java/com/instructure/teacher/adapters/StudentContextFragment.kt @@ -247,20 +247,23 @@ class StudentContextFragment : PresenterFragment 0)) { - triggered = true - presenter.loadMoreSubmissions() + override fun onScrollChanged() { + if (view == null) return // To prevent binding crashes is split view. + + with(binding) { + if (!isAdded || contentContainer.height == 0 || scrollContent.height == 0 || loadMoreContainer.height == 0) return + val threshold = scrollContent.height - loadMoreContainer.top + val bottomOffset = contentContainer.height + contentContainer.scrollY - scrollContent.bottom + if (scrollContent.height <= contentContainer.height) { + presenter.loadMoreSubmissions() + } else if (triggered && (threshold + touchSlop + bottomOffset < 0)) { + triggered = false + } else if (!triggered && (threshold + bottomOffset > 0)) { + triggered = true + presenter.loadMoreSubmissions() + } } } - } override fun addSubmissions(submissions: List, course: AsCourse, student: User) { diff --git a/apps/teacher/src/main/java/com/instructure/teacher/fragments/InternalWebViewFragment.kt b/apps/teacher/src/main/java/com/instructure/teacher/fragments/InternalWebViewFragment.kt index dfe4eb51aa..16c9963a74 100644 --- a/apps/teacher/src/main/java/com/instructure/teacher/fragments/InternalWebViewFragment.kt +++ b/apps/teacher/src/main/java/com/instructure/teacher/fragments/InternalWebViewFragment.kt @@ -83,7 +83,7 @@ open class InternalWebViewFragment : BaseFragment() { override fun onSaveInstanceState(outState: Bundle) { super.onSaveInstanceState(outState) - binding.canvasWebView?.saveState(outState) + if (view != null) binding.canvasWebView.saveState(outState) } override fun onCreateView(view: View) = Unit diff --git a/apps/teacher/src/main/java/com/instructure/teacher/fragments/PageDetailsFragment.kt b/apps/teacher/src/main/java/com/instructure/teacher/fragments/PageDetailsFragment.kt index 038cd16ebe..d3875c6c6a 100644 --- a/apps/teacher/src/main/java/com/instructure/teacher/fragments/PageDetailsFragment.kt +++ b/apps/teacher/src/main/java/com/instructure/teacher/fragments/PageDetailsFragment.kt @@ -175,7 +175,7 @@ class PageDetailsFragment : BasePresenterFragment< override fun populatePageDetails(page: Page) { mPage = page loadHtmlJob = binding.canvasWebViewWraper.webView.loadHtmlWithIframes(requireContext(), page.body, { - binding.canvasWebViewWraper.loadHtml(it, page.title, baseUrl = mPage.htmlUrl) + if (view != null) binding.canvasWebViewWraper.loadHtml(it, page.title, baseUrl = mPage.htmlUrl) }) { LtiLaunchFragment.routeLtiLaunchFragment(requireContext(), mCanvasContext, it) } diff --git a/libs/pandares/src/main/res/values/strings.xml b/libs/pandares/src/main/res/values/strings.xml index 204c5bc575..c169672ff2 100644 --- a/libs/pandares/src/main/res/values/strings.xml +++ b/libs/pandares/src/main/res/values/strings.xml @@ -1397,4 +1397,5 @@ Login ID Email Version + There was a problem reloading this assignment. Please check your connection and try again. From f54b2694a683b1d7a089201cad962ebf2cf64e5f Mon Sep 17 00:00:00 2001 From: Kristof Deak <92309696+kdeakinstructure@users.noreply.github.com> Date: Thu, 11 May 2023 16:21:45 +0200 Subject: [PATCH 029/103] Extend DashboardE2E test to test the new group favorizing/unfavorizing logics (that the groups should be displayed independently from courses) (#1971) --- .../student/ui/e2e/DashboardE2ETest.kt | 55 +++++++++++++++++++ .../student/ui/pages/DashboardPage.kt | 21 +++++-- .../student/ui/pages/EditDashboardPage.kt | 52 +++++++++++++++--- .../res/layout/fragment_edit_dashboard.xml | 1 + 4 files changed, 118 insertions(+), 11 deletions(-) diff --git a/apps/student/src/androidTest/java/com/instructure/student/ui/e2e/DashboardE2ETest.kt b/apps/student/src/androidTest/java/com/instructure/student/ui/e2e/DashboardE2ETest.kt index 5565ee3fe3..c34c0326d6 100644 --- a/apps/student/src/androidTest/java/com/instructure/student/ui/e2e/DashboardE2ETest.kt +++ b/apps/student/src/androidTest/java/com/instructure/student/ui/e2e/DashboardE2ETest.kt @@ -57,10 +57,13 @@ class DashboardE2ETest : StudentTest() { Log.d(PREPARATION_TAG,"Seed some group info.") val groupCategory = GroupsApi.createCourseGroupCategory(data.coursesList[0].id, teacher.token) + val groupCategory2 = GroupsApi.createCourseGroupCategory(data.coursesList[0].id, teacher.token) val group = GroupsApi.createGroup(groupCategory.id, teacher.token) + val group2 = GroupsApi.createGroup(groupCategory2.id, teacher.token) Log.d(PREPARATION_TAG,"Create group membership for ${student.name} student.") GroupsApi.createGroupMembership(group.id, student.id, teacher.token) + GroupsApi.createGroupMembership(group2.id, student.id, teacher.token) Log.d(STEP_TAG,"Login with user: ${student.name}, login id: ${student.loginId}.") tokenLogin(student) @@ -93,6 +96,10 @@ class DashboardE2ETest : StudentTest() { dashboardPage.assertDisplaysCourse(course) } + Log.d(STEP_TAG, "Assert that both of the groups '${group.name}' and '${group2.name}' are displayed because they are independent from the courses.") + dashboardPage.assertDisplaysGroup(group, course1) + dashboardPage.assertDisplaysGroup(group2, course1) + Log.d(STEP_TAG,"Click on 'Edit Dashboard' button. Assert that the Edit Dashboard Page is loaded.") dashboardPage.clickEditDashboard() editDashboardPage.assertPageObjects() @@ -106,6 +113,10 @@ class DashboardE2ETest : StudentTest() { dashboardPage.assertDisplaysCourse(course1) dashboardPage.assertCourseNotDisplayed(course2) + Log.d(STEP_TAG, "Assert that both of the groups '${group.name}' and '${group2.name}' are displayed because they are independent from the courses.") + dashboardPage.assertDisplaysGroup(group, course1) + dashboardPage.assertDisplaysGroup(group2, course1) + Log.d(STEP_TAG,"Opens ${course1.name} course and assert if Course Details Page has been opened. Navigate back to Dashboard Page.") dashboardPage.selectCourse(course1) courseBrowserPage.assertPageObjects() @@ -144,6 +155,9 @@ class DashboardE2ETest : StudentTest() { dashboardPage.assertPageObjects() dashboardPage.assertDisplaysCourse(newNickname) + Log.d(STEP_TAG,"Assert that ${group.name} groups is displayed and the '$newNickname' is displayed as the corresponding course name of the group.") + dashboardPage.assertDisplaysGroup(group, newNickname) + Log.d(STEP_TAG, "Click on 'Edit nickname' menu of '$newNickname' course.") dashboardPage.clickCourseOverflowMenu(newNickname, "Edit nickname") @@ -154,6 +168,9 @@ class DashboardE2ETest : StudentTest() { dashboardPage.assertPageObjects() dashboardPage.assertDisplaysCourse(course1.name) + Log.d(STEP_TAG,"Assert that ${group.name} groups is displayed and the '${data.coursesList[0]}' is displayed as the corresponding course name of the group.") + dashboardPage.assertDisplaysGroup(group, data.coursesList[0]) + Log.d(STEP_TAG, "Toggle OFF 'Show Grades' and navigate back to Dashboard Page.") leftSideNavigationDrawerPage.setShowGrades(false) @@ -168,6 +185,44 @@ class DashboardE2ETest : StudentTest() { dashboardPage.assertCourseGrade(course1.name, "N/A") dashboardPage.assertCourseGrade(course2.name, "N/A") + Log.d(STEP_TAG,"Click on 'Edit Dashboard' button.") + dashboardPage.clickEditDashboard() + editDashboardPage.assertPageObjects() + + Log.d(STEP_TAG, "Assert that the group 'mass' select button's label is 'Select All'.") + editDashboardPage.swipeUp() + editDashboardPage.assertGroupMassSelectButtonIsDisplayed(false) + + Log.d(STEP_TAG, "Favorite '${group.name}' course and navigate back to Dashboard Page.") + editDashboardPage.favoriteGroup(group.name) + Espresso.pressBack() + + Log.d(STEP_TAG,"Assert that only the favoured group, '${group.name}' is displayed." + + "Assert that the other group, '${group2.name}' is not displayed since it's not favoured.") + dashboardPage.assertDisplaysGroup(group, course1) + dashboardPage.assertGroupNotDisplayed(group2) + + Log.d(STEP_TAG,"Click on 'Edit Dashboard' button.") + dashboardPage.clickEditDashboard() + editDashboardPage.assertPageObjects() + Thread.sleep(2000) //It can be flaky without this 2 seconds + editDashboardPage.swipeUp() + + Log.d(STEP_TAG, "Assert that the group 'mass' select button's label is 'Unselect All'.") + editDashboardPage.assertGroupMassSelectButtonIsDisplayed(true) + + Log.d(STEP_TAG, "Toggle off favourite star icon of '${group.name}' group." + + "Assert that the 'mass' select button's label is 'Select All'.") + editDashboardPage.unfavoriteGroup(group.name) + editDashboardPage.assertGroupMassSelectButtonIsDisplayed(false) + + Log.d(STEP_TAG, "Navigate back to Dashboard Page.") + Espresso.pressBack() + + Log.d(STEP_TAG,"Assert that both of the groups, '${group.name}' and '${group2.name}' are displayed" + + "since if there is no group selected on the Edit Dashboard page, we are showing all of them (this is the same logics as with the courses).") + dashboardPage.assertDisplaysGroup(group, course1) + dashboardPage.assertDisplaysGroup(group2, course1) } @E2E diff --git a/apps/student/src/androidTest/java/com/instructure/student/ui/pages/DashboardPage.kt b/apps/student/src/androidTest/java/com/instructure/student/ui/pages/DashboardPage.kt index 1e2cb4072b..64e74a54d8 100644 --- a/apps/student/src/androidTest/java/com/instructure/student/ui/pages/DashboardPage.kt +++ b/apps/student/src/androidTest/java/com/instructure/student/ui/pages/DashboardPage.kt @@ -93,6 +93,10 @@ class DashboardPage : BasePage(R.id.dashboardPage) { assertDisplaysGroupCommon(group.name, course.name) } + fun assertDisplaysGroup(group: GroupApiModel, courseName: String) { + assertDisplaysGroupCommon(group.name, courseName) + } + fun assertDisplaysGroup(group: Group, course: Course) { assertDisplaysGroupCommon(group.name!!, course.name) } @@ -100,7 +104,7 @@ class DashboardPage : BasePage(R.id.dashboardPage) { private fun assertDisplaysGroupCommon(groupName: String, courseName: String) { val groupNameMatcher = allOf(withText(groupName), withId(R.id.groupNameView)) onView(groupNameMatcher).scrollTo().assertDisplayed() - val groupDescriptionMatcher = allOf(withText(courseName), withId(R.id.groupCourseView)) + val groupDescriptionMatcher = allOf(withText(courseName), withId(R.id.groupCourseView), hasSibling(groupNameMatcher)) onView(groupDescriptionMatcher).scrollTo().assertDisplayed() } @@ -245,7 +249,7 @@ class DashboardPage : BasePage(R.id.dashboardPage) { } fun clickEditDashboard() { - onView(withId(R.id.editDashboardTextView)).click() + onView(withId(R.id.editDashboardTextView)).scrollTo().click() } fun assertCourseNotDisplayed(course: CourseApiModel) { @@ -257,6 +261,15 @@ class DashboardPage : BasePage(R.id.dashboardPage) { onView(matcher).check(doesNotExist()) } + fun assertGroupNotDisplayed(group: GroupApiModel) { + val matcher = allOf( + withText(group.name), + withId(R.id.titleTextView), + withAncestor(R.id.swipeRefreshLayout) + ) + onView(matcher).check(doesNotExist()) + } + fun changeCourseNickname(changeTo: String) { onView(withId(R.id.newCourseNickname)).replaceText(changeTo) onView(withText(R.string.ok) + withAncestor(R.id.buttonPanel)).click() @@ -264,7 +277,7 @@ class DashboardPage : BasePage(R.id.dashboardPage) { fun clickCourseOverflowMenu(courseTitle: String, menuTitle: String) { val courseOverflowMatcher = withId(R.id.overflow) + withAncestor(withId(R.id.cardView) + withDescendant(withId(R.id.titleTextView) + withText(courseTitle))) - onView(courseOverflowMatcher).click() + onView(courseOverflowMatcher).scrollTo().click() waitForView(withId(R.id.title) + withText(menuTitle)).click() } @@ -272,7 +285,7 @@ class DashboardPage : BasePage(R.id.dashboardPage) { val siblingMatcher = allOf(withId(R.id.textContainer), withDescendant(withId(R.id.titleTextView) + withText(courseName))) val matcher = allOf(withId(R.id.gradeLayout), withDescendant(withId(R.id.gradeTextView) + withText(courseGrade)), hasSibling(siblingMatcher)) - onView(matcher).assertDisplayed() + onView(matcher).scrollTo().assertDisplayed() } fun assertCourseGradeNotDisplayed(courseName: String, courseGrade: String) { diff --git a/apps/student/src/androidTest/java/com/instructure/student/ui/pages/EditDashboardPage.kt b/apps/student/src/androidTest/java/com/instructure/student/ui/pages/EditDashboardPage.kt index 30adc114ed..6891b89897 100644 --- a/apps/student/src/androidTest/java/com/instructure/student/ui/pages/EditDashboardPage.kt +++ b/apps/student/src/androidTest/java/com/instructure/student/ui/pages/EditDashboardPage.kt @@ -23,7 +23,13 @@ import androidx.test.espresso.matcher.ViewMatchers.withText import com.instructure.canvasapi2.models.Course import com.instructure.espresso.assertDisplayed import com.instructure.espresso.click -import com.instructure.espresso.page.* +import com.instructure.espresso.page.BasePage +import com.instructure.espresso.page.onView +import com.instructure.espresso.page.plus +import com.instructure.espresso.page.withId +import com.instructure.espresso.page.withText +import com.instructure.espresso.scrollTo +import com.instructure.espresso.swipeUp import com.instructure.student.R import org.hamcrest.Matchers.allOf import org.hamcrest.Matchers.containsString @@ -51,8 +57,7 @@ class EditDashboardPage : BasePage(R.id.editDashboardPage) { fun unfavoriteCourse(courseName: String) { val childMatcher = withContentDescription("Remove from dashboard") val itemMatcher = allOf( - withContentDescription(containsString(", favorite")), - withContentDescription(containsString(courseName)), + withContentDescription(containsString("Course $courseName, favorite")), hasDescendant(childMatcher)) onView(withParent(itemMatcher) + childMatcher).click() @@ -65,18 +70,34 @@ class EditDashboardPage : BasePage(R.id.editDashboardPage) { fun favoriteCourse(courseName: String) { val childMatcher = withContentDescription("Add to dashboard") val itemMatcher = allOf( - withContentDescription(containsString(", not favorite")), - withContentDescription(containsString(courseName)), + withContentDescription(containsString("Course $courseName, not favorite")), hasDescendant(childMatcher)) onView(withParent(itemMatcher) + childMatcher).click() } + fun favoriteGroup(groupName: String) { + val childMatcher = withContentDescription("Add to dashboard") + val itemMatcher = allOf( + withContentDescription(containsString("Group $groupName, not favorite")), + hasDescendant(childMatcher)) + + onView(withParent(itemMatcher) + childMatcher).scrollTo().click() + } + + fun unfavoriteGroup(groupName: String) { + val childMatcher = withContentDescription("Remove from dashboard") + val itemMatcher = allOf( + withContentDescription(containsString("Group $groupName, favorite")), + hasDescendant(childMatcher)) + + onView(withParent(itemMatcher) + childMatcher).scrollTo().click() + } + fun assertCourseFavorited(course: Course) { val childMatcher = withContentDescription("Remove from dashboard") val itemMatcher = allOf( - withContentDescription(containsString(", favorite")), - withContentDescription(containsString(course.name)), + withContentDescription(containsString("Course ${course.name}, favorite")), hasDescendant(childMatcher)) onView(itemMatcher).assertDisplayed() } @@ -111,4 +132,21 @@ class EditDashboardPage : BasePage(R.id.editDashboardPage) { } } + fun assertGroupMassSelectButtonIsDisplayed(someSelected: Boolean) { + if (someSelected) { + val itemMatcher = withContentDescription("Remove all from dashboard") + val parentMatcher = allOf(hasDescendant(withText("All groups")), hasDescendant(itemMatcher)) + onView(withParent(parentMatcher) + itemMatcher).scrollTo().assertDisplayed() + } + else { + val itemMatcher = withContentDescription("Add all to dashboard") + val parentMatcher = allOf(hasDescendant(withText("All groups")), hasDescendant(itemMatcher)) + onView(withParent(parentMatcher) + itemMatcher).scrollTo().assertDisplayed() + } + } + + fun swipeUp() { + onView(withId(R.id.swipeRefreshLayout) + withParent(withId(R.id.editDashboardPage))).swipeUp() + } + } \ No newline at end of file diff --git a/libs/pandautils/src/main/res/layout/fragment_edit_dashboard.xml b/libs/pandautils/src/main/res/layout/fragment_edit_dashboard.xml index 25c3a8fcc8..d20f675003 100644 --- a/libs/pandautils/src/main/res/layout/fragment_edit_dashboard.xml +++ b/libs/pandautils/src/main/res/layout/fragment_edit_dashboard.xml @@ -45,6 +45,7 @@ Date: Fri, 12 May 2023 12:37:20 +0200 Subject: [PATCH 030/103] [MBL-16725][Student] Unable to place widgets due to light/dark theme selection #1973 refs: MBL-16725 affects: Student release note: Fixed a bug where users could not add widgets to the home screen. --- .../student/activity/StudentViewStarterActivity.kt | 2 +- .../com/instructure/student/activity/WidgetSetupActivity.kt | 2 +- .../features/documentscanning/DocumentScanningActivity.kt | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/apps/student/src/main/java/com/instructure/student/activity/StudentViewStarterActivity.kt b/apps/student/src/main/java/com/instructure/student/activity/StudentViewStarterActivity.kt index cca0c61949..135dd7252c 100644 --- a/apps/student/src/main/java/com/instructure/student/activity/StudentViewStarterActivity.kt +++ b/apps/student/src/main/java/com/instructure/student/activity/StudentViewStarterActivity.kt @@ -36,7 +36,7 @@ class StudentViewStarterActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - setContentView(R.layout.activity_student_view_starter) + setContentView(binding.root) binding.loadingView.setOverrideColor(ContextCompat.getColor(this, R.color.login_studentAppTheme)) val extras = intent.extras!! diff --git a/apps/student/src/main/java/com/instructure/student/activity/WidgetSetupActivity.kt b/apps/student/src/main/java/com/instructure/student/activity/WidgetSetupActivity.kt index 5e533ccb0d..9ae8c38975 100644 --- a/apps/student/src/main/java/com/instructure/student/activity/WidgetSetupActivity.kt +++ b/apps/student/src/main/java/com/instructure/student/activity/WidgetSetupActivity.kt @@ -39,7 +39,7 @@ class WidgetSetupActivity : AppCompatActivity() { // Sets the result canceled so if the user decides not to setup the widget it does not get added setResult(Activity.RESULT_CANCELED) - setContentView(R.layout.activity_widget_setup) + setContentView(binding.root) binding.cardDark.setOnClickListener(cardClickListener) binding.cardLight.setOnClickListener(cardClickListener) diff --git a/apps/student/src/main/java/com/instructure/student/features/documentscanning/DocumentScanningActivity.kt b/apps/student/src/main/java/com/instructure/student/features/documentscanning/DocumentScanningActivity.kt index a61b973e59..73badd284b 100644 --- a/apps/student/src/main/java/com/instructure/student/features/documentscanning/DocumentScanningActivity.kt +++ b/apps/student/src/main/java/com/instructure/student/features/documentscanning/DocumentScanningActivity.kt @@ -40,13 +40,13 @@ import java.util.* @AndroidEntryPoint class DocumentScanningActivity : ScanActivity() { - private val binding by viewBinding(ActivityDocumentScanningBinding::inflate) + private lateinit var binding: ActivityDocumentScanningBinding private val viewModel: DocumentScanningViewModel by viewModels() override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - val binding = DataBindingUtil.setContentView(this, R.layout.activity_document_scanning) + binding = DataBindingUtil.setContentView(this, R.layout.activity_document_scanning) binding.lifecycleOwner = this binding.viewModel = viewModel From f9a60e8e6ed712e571598f661a588455da948b91 Mon Sep 17 00:00:00 2001 From: Tamas Kozmer <72397075+tamaskozmer@users.noreply.github.com> Date: Tue, 16 May 2023 17:49:42 +0200 Subject: [PATCH 031/103] [MBL-16736][Student] Submission comments with comment[attempt] value set as null are missing #1977 refs: MBL-16736 affects: Student release note: Fixed a bug where some submission comments were not visible. --- .../drawer/comments/SubmissionCommentsPresenter.kt | 2 +- .../submission/SubmissionCommentsPresenterTest.kt | 12 ++++++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/apps/student/src/main/java/com/instructure/student/mobius/assignmentDetails/submissionDetails/drawer/comments/SubmissionCommentsPresenter.kt b/apps/student/src/main/java/com/instructure/student/mobius/assignmentDetails/submissionDetails/drawer/comments/SubmissionCommentsPresenter.kt index 4c32318786..18ae4f40d2 100644 --- a/apps/student/src/main/java/com/instructure/student/mobius/assignmentDetails/submissionDetails/drawer/comments/SubmissionCommentsPresenter.kt +++ b/apps/student/src/main/java/com/instructure/student/mobius/assignmentDetails/submissionDetails/drawer/comments/SubmissionCommentsPresenter.kt @@ -39,7 +39,7 @@ object SubmissionCommentsPresenter : Presenter + val comments = model.comments.filter { it.attempt == null || it.attempt == model.attemptId || !model.assignmentEnhancementsEnabled }.map { comment -> val date = comment.createdAt ?: Date(0) CommentItemState.CommentItem( id = comment.id, diff --git a/apps/student/src/test/java/com/instructure/student/test/assignment/details/submission/SubmissionCommentsPresenterTest.kt b/apps/student/src/test/java/com/instructure/student/test/assignment/details/submission/SubmissionCommentsPresenterTest.kt index 2ca6d148a6..f02c9e48f9 100644 --- a/apps/student/src/test/java/com/instructure/student/test/assignment/details/submission/SubmissionCommentsPresenterTest.kt +++ b/apps/student/src/test/java/com/instructure/student/test/assignment/details/submission/SubmissionCommentsPresenterTest.kt @@ -208,6 +208,18 @@ class SubmissionCommentsPresenterTest : Assert() { } } + @Test + fun `Always show comments when attemptId is null`() { + val comments = listOf( + submissionComment, + SubmissionComment(id=1, comment="No attempt", attempt = null) + ) + val model = baseModel.copy(comments = comments) + val actualState = SubmissionCommentsPresenter.present(model, context) + assertEquals(3, actualState.commentStates.size) + assertEquals("No attempt", (actualState.commentStates[1] as CommentItemState.CommentItem).message) + } + private fun dateFromCommentState(state: CommentItemState) : Date { return when(state) { is CommentItemState.CommentItem -> state.sortDate From 5b3b35afaaf4f1f504d158370736186a94696c15 Mon Sep 17 00:00:00 2001 From: Tamas Kozmer <72397075+tamaskozmer@users.noreply.github.com> Date: Tue, 16 May 2023 17:50:09 +0200 Subject: [PATCH 032/103] [MBL-16727][Student] 'Go To Modules' link opens a new screen which overflows the previous one (#1975) refs: MBL-16727 affects: Student release note: none * Added navigation flag for pages. * Fixed unit test. --- .../fragment/CourseModuleProgressionFragment.kt | 5 ++++- .../student/fragment/PageDetailsFragment.kt | 7 +++++-- .../instructure/student/util/LockInfoHTMLHelper.kt | 14 ++++++++------ .../com/instructure/student/util/ModuleUtility.kt | 4 ++-- .../student/test/util/ModuleUtilityTest.kt | 3 ++- 5 files changed, 21 insertions(+), 12 deletions(-) diff --git a/apps/student/src/main/java/com/instructure/student/fragment/CourseModuleProgressionFragment.kt b/apps/student/src/main/java/com/instructure/student/fragment/CourseModuleProgressionFragment.kt index 384487c722..98206784de 100644 --- a/apps/student/src/main/java/com/instructure/student/fragment/CourseModuleProgressionFragment.kt +++ b/apps/student/src/main/java/com/instructure/student/fragment/CourseModuleProgressionFragment.kt @@ -89,6 +89,7 @@ class CourseModuleProgressionFragment : ParentFragment(), Bookmarkable { private var assetId: String by StringArg(key = ASSET_ID) private var assetType: String by StringArg(key = ASSET_TYPE, default = ModuleItemAsset.MODULE_ITEM.assetType) private var route: Route by ParcelableArg(key = ROUTE) + private var navigatedFromModules: Boolean by BooleanArg(key = NAVIGATED_FROM_MODULES) // Default number will get reset private var itemsCount = 3 @@ -635,7 +636,7 @@ class CourseModuleProgressionFragment : ParentFragment(), Bookmarkable { // so we need to find the correct one overall val moduleItem = getCurrentModuleItem(position) ?: getCurrentModuleItem(0) // Default to the first item, band-aid for NPE - val fragment = ModuleUtility.getFragment(moduleItem!!, canvasContext as Course, modules[groupPos], isDiscussionRedesignEnabled) + val fragment = ModuleUtility.getFragment(moduleItem!!, canvasContext as Course, modules[groupPos], isDiscussionRedesignEnabled, navigatedFromModules) var args: Bundle? = fragment!!.arguments if (args == null) { args = Bundle() @@ -748,6 +749,7 @@ class CourseModuleProgressionFragment : ParentFragment(), Bookmarkable { private const val ASSET_ID = "asset_id" private const val ASSET_TYPE = "asset_type" private const val ROUTE = "route" + private const val NAVIGATED_FROM_MODULES = "navigated_from_modules" //we don't want to add subheaders or external tools into the list. subheaders don't do anything and we @@ -762,6 +764,7 @@ class CourseModuleProgressionFragment : ParentFragment(), Bookmarkable { return Route(null, CourseModuleProgressionFragment::class.java, canvasContext, canvasContext.makeBundle(Bundle().apply { putInt(GROUP_POSITION, groupPos) putInt(CHILD_POSITION, childPos) + putBoolean(NAVIGATED_FROM_MODULES, true) })) } diff --git a/apps/student/src/main/java/com/instructure/student/fragment/PageDetailsFragment.kt b/apps/student/src/main/java/com/instructure/student/fragment/PageDetailsFragment.kt index d38035dd5d..f938d788c6 100644 --- a/apps/student/src/main/java/com/instructure/student/fragment/PageDetailsFragment.kt +++ b/apps/student/src/main/java/com/instructure/student/fragment/PageDetailsFragment.kt @@ -61,6 +61,7 @@ class PageDetailsFragment : InternalWebviewFragment(), Bookmarkable { private var pageName: String? by NullableStringArg(key = PAGE_NAME) private var page: Page by ParcelableArg(default = Page(), key = PAGE) private var pageUrl: String? by NullableStringArg(key = PAGE_URL) + private var navigatedFromModules: Boolean by BooleanArg(key = NAVIGATED_FROM_MODULES) // Flag for the webview client to know whether or not we should clear the history private var isUpdated = false @@ -185,7 +186,7 @@ class PageDetailsFragment : InternalWebviewFragment(), Bookmarkable { setPageObject(page) if (page.lockInfo != null) { - val lockedMessage = LockInfoHTMLHelper.getLockedInfoHTML(page.lockInfo!!, requireContext(), R.string.lockedPageDesc) + val lockedMessage = LockInfoHTMLHelper.getLockedInfoHTML(page.lockInfo!!, requireContext(), R.string.lockedPageDesc, !navigatedFromModules) populateWebView(lockedMessage, getString(R.string.pages)) return } @@ -323,6 +324,7 @@ class PageDetailsFragment : InternalWebviewFragment(), Bookmarkable { const val PAGE_NAME = "pageDetailsName" const val PAGE = "pageDetails" const val PAGE_URL = "pageUrl" + const val NAVIGATED_FROM_MODULES = "navigated_from_modules" fun newInstance(route: Route): PageDetailsFragment? { return if (validRoute(route)) PageDetailsFragment().apply { @@ -345,8 +347,9 @@ class PageDetailsFragment : InternalWebviewFragment(), Bookmarkable { return Route(null, PageDetailsFragment::class.java, canvasContext, canvasContext.makeBundle(Bundle().apply { if (pageName != null) putString(PAGE_NAME, pageName) })) } - fun makeRoute(canvasContext: CanvasContext, pageName: String?, pageUrl: String?): Route { + fun makeRoute(canvasContext: CanvasContext, pageName: String?, pageUrl: String?, navigatedFromModules: Boolean): Route { return Route(null, PageDetailsFragment::class.java, canvasContext, canvasContext.makeBundle(Bundle().apply { + putBoolean(NAVIGATED_FROM_MODULES, navigatedFromModules) if (pageName != null) putString(PAGE_NAME, pageName) if (pageUrl != null) diff --git a/apps/student/src/main/java/com/instructure/student/util/LockInfoHTMLHelper.kt b/apps/student/src/main/java/com/instructure/student/util/LockInfoHTMLHelper.kt index e4696135b3..7da28a29bd 100644 --- a/apps/student/src/main/java/com/instructure/student/util/LockInfoHTMLHelper.kt +++ b/apps/student/src/main/java/com/instructure/student/util/LockInfoHTMLHelper.kt @@ -25,7 +25,7 @@ import com.instructure.student.R import java.util.* object LockInfoHTMLHelper { - fun getLockedInfoHTML(lockInfo: LockInfo, context: Context, explanationFirstLine: Int): String { + fun getLockedInfoHTML(lockInfo: LockInfo, context: Context, explanationFirstLine: Int, addModulesLink: Boolean = true): String { /* Note: if the html that this is going in isn't based on html_wrapper.html (it will have something like -- String html = CanvasAPI.getAssetsFile(getSherlockActivity(), "html_wrapper.html");) this will not look as good. The blue button will just be a link. */ @@ -55,11 +55,13 @@ object LockInfoHTMLHelper { } // Make sure we know what the protocol is (http or https) - lockInfo.contextModule?.let { module -> - // Create the url to modules for this course - val url = "$protocol://$domain/courses/${module.contextId}/modules" - // Create the button and link it to modules - lockedMessage += """
${context.resources.getString(R.string.goToModules)}
""" + if (addModulesLink) { + lockInfo.contextModule?.let { module -> + // Create the url to modules for this course + val url = "$protocol://$domain/courses/${module.contextId}/modules" + // Create the button and link it to modules + lockedMessage += """
${context.resources.getString(R.string.goToModules)}
""" + } } return lockedMessage } diff --git a/apps/student/src/main/java/com/instructure/student/util/ModuleUtility.kt b/apps/student/src/main/java/com/instructure/student/util/ModuleUtility.kt index c15b3977c8..bcb221e07d 100644 --- a/apps/student/src/main/java/com/instructure/student/util/ModuleUtility.kt +++ b/apps/student/src/main/java/com/instructure/student/util/ModuleUtility.kt @@ -37,8 +37,8 @@ import com.instructure.student.fragment.PageDetailsFragment.Companion.makeRoute import java.util.* object ModuleUtility { - fun getFragment(item: ModuleItem, course: Course, moduleObject: ModuleObject?, isDiscussionRedesignEnabled: Boolean): Fragment? = when (item.type) { - "Page" -> PageDetailsFragment.newInstance(makeRoute(course, item.title, item.pageUrl)) + fun getFragment(item: ModuleItem, course: Course, moduleObject: ModuleObject?, isDiscussionRedesignEnabled: Boolean, navigatedFromModules: Boolean): Fragment? = when (item.type) { + "Page" -> PageDetailsFragment.newInstance(makeRoute(course, item.title, item.pageUrl, navigatedFromModules)) "Assignment" -> AssignmentDetailsFragment.newInstance(makeRoute(course, getAssignmentId(item))) "Discussion" -> { if (isDiscussionRedesignEnabled) { diff --git a/apps/student/src/test/java/com/instructure/student/test/util/ModuleUtilityTest.kt b/apps/student/src/test/java/com/instructure/student/test/util/ModuleUtilityTest.kt index 5a8ada70b5..26f2f7161a 100644 --- a/apps/student/src/test/java/com/instructure/student/test/util/ModuleUtilityTest.kt +++ b/apps/student/src/test/java/com/instructure/student/test/util/ModuleUtilityTest.kt @@ -88,6 +88,7 @@ class ModuleUtilityTest : TestCase() { val expectedBundle = Bundle() expectedBundle.putParcelable(Const.CANVAS_CONTEXT, course) expectedBundle.putString(PageDetailsFragment.PAGE_NAME, "hello-world") + expectedBundle.putBoolean(PageDetailsFragment.NAVIGATED_FROM_MODULES, false) val parentFragment = callGetFragment(moduleItem, course, null) TestCase.assertNotNull(parentFragment) @@ -246,6 +247,6 @@ class ModuleUtilityTest : TestCase() { } private fun callGetFragment(moduleItem: ModuleItem, course: Course, moduleObject: ModuleObject?): Fragment? { - return ModuleUtility.getFragment(moduleItem, course, moduleObject, false) + return ModuleUtility.getFragment(moduleItem, course, moduleObject, false, false) } } From 0bdc23c1af97dd2834a79cc2cad28fcf372d6aac Mon Sep 17 00:00:00 2001 From: Tamas Kozmer <72397075+tamaskozmer@users.noreply.github.com> Date: Tue, 16 May 2023 17:51:36 +0200 Subject: [PATCH 033/103] [MBL-16735][Student] Submission view crashes on config change #1978 refs: MBL-16735 affects: Student release note: Fixed a crash on the submission details screen. --- .../AnnotationSubmissionUploadFragment.kt | 2 +- .../content/AnnotationSubmissionViewFragment.kt | 3 ++- .../content/PdfStudentSubmissionView.kt | 12 +++++++----- .../content/PdfSubmissionViewFragment.kt | 2 +- 4 files changed, 11 insertions(+), 8 deletions(-) diff --git a/apps/student/src/main/java/com/instructure/student/mobius/assignmentDetails/submission/annnotation/AnnotationSubmissionUploadFragment.kt b/apps/student/src/main/java/com/instructure/student/mobius/assignmentDetails/submission/annnotation/AnnotationSubmissionUploadFragment.kt index eb841adf7c..d3e2e49510 100644 --- a/apps/student/src/main/java/com/instructure/student/mobius/assignmentDetails/submission/annnotation/AnnotationSubmissionUploadFragment.kt +++ b/apps/student/src/main/java/com/instructure/student/mobius/assignmentDetails/submission/annnotation/AnnotationSubmissionUploadFragment.kt @@ -57,7 +57,7 @@ class AnnotationSubmissionUploadFragment : Fragment() { viewModel.pdfUrl.observe(viewLifecycleOwner, { binding.annotationSubmissionViewContainer.addView( - PdfStudentSubmissionView(requireActivity(), it, studentAnnotationSubmit = true) + PdfStudentSubmissionView(requireActivity(), it, childFragmentManager, studentAnnotationSubmit = true) ) }) diff --git a/apps/student/src/main/java/com/instructure/student/mobius/assignmentDetails/submissionDetails/content/AnnotationSubmissionViewFragment.kt b/apps/student/src/main/java/com/instructure/student/mobius/assignmentDetails/submissionDetails/content/AnnotationSubmissionViewFragment.kt index d903acb240..7f9ec7e045 100644 --- a/apps/student/src/main/java/com/instructure/student/mobius/assignmentDetails/submissionDetails/content/AnnotationSubmissionViewFragment.kt +++ b/apps/student/src/main/java/com/instructure/student/mobius/assignmentDetails/submissionDetails/content/AnnotationSubmissionViewFragment.kt @@ -52,7 +52,8 @@ class AnnotationSubmissionViewFragment : Fragment() { PdfStudentSubmissionView( requireActivity(), it, - studentAnnotationView = true + childFragmentManager, + studentAnnotationView = true, ) ) } diff --git a/apps/student/src/main/java/com/instructure/student/mobius/assignmentDetails/submissionDetails/content/PdfStudentSubmissionView.kt b/apps/student/src/main/java/com/instructure/student/mobius/assignmentDetails/submissionDetails/content/PdfStudentSubmissionView.kt index f238525403..05a4189e57 100644 --- a/apps/student/src/main/java/com/instructure/student/mobius/assignmentDetails/submissionDetails/content/PdfStudentSubmissionView.kt +++ b/apps/student/src/main/java/com/instructure/student/mobius/assignmentDetails/submissionDetails/content/PdfStudentSubmissionView.kt @@ -25,6 +25,7 @@ import android.view.LayoutInflater import android.widget.FrameLayout import android.widget.ImageView import androidx.fragment.app.Fragment +import androidx.fragment.app.FragmentManager import com.instructure.annotations.PdfSubmissionView import com.instructure.canvasapi2.managers.CanvaDocsManager import com.instructure.canvasapi2.models.ApiValues @@ -60,8 +61,9 @@ import org.greenrobot.eventbus.ThreadMode class PdfStudentSubmissionView( context: Context, private val pdfUrl: String, + private val fragmentManager: FragmentManager, private val studentAnnotationSubmit: Boolean = false, - private val studentAnnotationView: Boolean = false + private val studentAnnotationView: Boolean = false, ) : PdfSubmissionView(context, studentAnnotationView), AnnotationManager.OnAnnotationCreationModeChangeListener, AnnotationManager.OnAnnotationEditingModeChangeListener { private val binding: ViewPdfStudentSubmissionBinding @@ -124,7 +126,7 @@ class PdfStudentSubmissionView( } override fun showNoInternetDialog() { - NoInternetConnectionDialog.show(supportFragmentManager) + NoInternetConnectionDialog.show(fragmentManager) } init { @@ -178,13 +180,13 @@ class PdfStudentSubmissionView( @SuppressLint("CommitTransaction") override fun setFragment(fragment: Fragment) { - if (isAttachedToWindow) supportFragmentManager.beginTransaction().replace(binding.content.id, fragment).commitNowAllowingStateLoss() + if (isAttachedToWindow) fragmentManager.beginTransaction().replace(binding.content.id, fragment).commitNowAllowingStateLoss() } override fun removeContentFragment() { - val contentFragment = supportFragmentManager.findFragmentById(binding.content.id) + val contentFragment = fragmentManager.findFragmentById(binding.content.id) if (contentFragment != null) { - supportFragmentManager.beginTransaction().remove(contentFragment).commitAllowingStateLoss() + fragmentManager.beginTransaction().remove(contentFragment).commitAllowingStateLoss() } } diff --git a/apps/student/src/main/java/com/instructure/student/mobius/assignmentDetails/submissionDetails/content/PdfSubmissionViewFragment.kt b/apps/student/src/main/java/com/instructure/student/mobius/assignmentDetails/submissionDetails/content/PdfSubmissionViewFragment.kt index 35c8aa0183..5ad05191da 100644 --- a/apps/student/src/main/java/com/instructure/student/mobius/assignmentDetails/submissionDetails/content/PdfSubmissionViewFragment.kt +++ b/apps/student/src/main/java/com/instructure/student/mobius/assignmentDetails/submissionDetails/content/PdfSubmissionViewFragment.kt @@ -28,7 +28,7 @@ class PdfSubmissionViewFragment : Fragment() { private var pdfUrl by StringArg() override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { - return PdfStudentSubmissionView(requireContext(), pdfUrl) + return PdfStudentSubmissionView(requireContext(), pdfUrl, childFragmentManager) } companion object { From ad856d82d100c51f6d682600bdbdab73d2155c52 Mon Sep 17 00:00:00 2001 From: Kristof Nemere <109959688+kristofnemere@users.noreply.github.com> Date: Thu, 18 May 2023 07:57:13 +0200 Subject: [PATCH 034/103] Changed database package structure to match the offline branch * Changed database package structure to match the offline branch * fixed imports --- .../instructure/student/di/DatabaseModule.kt | 4 +-- .../fragment/InboxComposeMessageFragment.kt | 2 +- .../instructure/teacher/di/DatabaseModule.kt | 4 +-- .../SpeedGraderCommentsPresenterFactory.kt | 30 +++++++++---------- .../teacher/fragments/AddMessageFragment.kt | 2 +- .../fragments/SpeedGraderCommentsFragment.kt | 4 +-- .../SpeedGraderCommentsPresenter.kt | 8 ++--- .../utils/SGPendingMediaCommentReceiver.kt | 2 +- .../pandautils/di/DatabaseModule.kt | 4 +-- .../DashboardNotificationsViewModel.kt | 6 ++-- .../file/upload/FileUploadDialogViewModel.kt | 4 +-- .../file/upload/worker/FileUploadWorker.kt | 4 +-- .../ShareExtensionProgressDialogViewModel.kt | 4 +-- .../pandautils/room/MigrationUtils.kt | 29 ++++++++++++++++++ .../room/{ => appdatabase}/AppDatabase.kt | 7 +++-- .../AppDatabaseMigrations.kt | 15 ++-------- .../{ => appdatabase}/daos/AttachmentDao.kt | 4 +-- .../room/appdatabase/daos/AuthorDao.kt | 17 +++++++++++ .../daos/DashboardFileUploadDao.kt | 4 +-- .../daos/FileUploadInputDao.kt | 4 +-- .../{ => appdatabase}/daos/MediaCommentDao.kt | 4 +-- .../daos/PendingSubmissionCommentDao.kt | 6 ++-- .../daos/SubmissionCommentDao.kt | 6 ++-- .../entities/AttachmentEntity.kt | 2 +- .../entities/AuthorEntity.kt | 2 +- .../entities/DashboardFileUploadEntity.kt | 2 +- .../entities/FileUploadInputEntity.kt | 4 +-- .../entities/MediaCommentEntity.kt | 2 +- .../PendingSubmissionCommentEntity.kt | 2 +- .../entities/SubmissionCommentEntity.kt | 2 +- ...ingSubmissionCommentWithFileUploadInput.kt | 6 ++-- .../model/SubmissionCommentWithAttachments.kt | 10 +++---- .../pandautils/room/daos/AuthorDao.kt | 21 ------------- .../DashboardNotificationsViewModelTest.kt | 6 ++-- .../file/upload/FileUploadViewModelTest.kt | 2 +- .../ShareExtensionProgressViewModelTest.kt | 6 ++-- 36 files changed, 129 insertions(+), 112 deletions(-) create mode 100644 libs/pandautils/src/main/java/com/instructure/pandautils/room/MigrationUtils.kt rename libs/pandautils/src/main/java/com/instructure/pandautils/room/{ => appdatabase}/AppDatabase.kt (80%) rename libs/pandautils/src/main/java/com/instructure/pandautils/room/{ => appdatabase}/AppDatabaseMigrations.kt (80%) rename libs/pandautils/src/main/java/com/instructure/pandautils/room/{ => appdatabase}/daos/AttachmentDao.kt (80%) create mode 100644 libs/pandautils/src/main/java/com/instructure/pandautils/room/appdatabase/daos/AuthorDao.kt rename libs/pandautils/src/main/java/com/instructure/pandautils/room/{ => appdatabase}/daos/DashboardFileUploadDao.kt (80%) rename libs/pandautils/src/main/java/com/instructure/pandautils/room/{ => appdatabase}/daos/FileUploadInputDao.kt (79%) rename libs/pandautils/src/main/java/com/instructure/pandautils/room/{ => appdatabase}/daos/MediaCommentDao.kt (72%) rename libs/pandautils/src/main/java/com/instructure/pandautils/room/{ => appdatabase}/daos/PendingSubmissionCommentDao.kt (85%) rename libs/pandautils/src/main/java/com/instructure/pandautils/room/{ => appdatabase}/daos/SubmissionCommentDao.kt (66%) rename libs/pandautils/src/main/java/com/instructure/pandautils/room/{ => appdatabase}/entities/AttachmentEntity.kt (95%) rename libs/pandautils/src/main/java/com/instructure/pandautils/room/{ => appdatabase}/entities/AuthorEntity.kt (91%) rename libs/pandautils/src/main/java/com/instructure/pandautils/room/{ => appdatabase}/entities/DashboardFileUploadEntity.kt (84%) rename libs/pandautils/src/main/java/com/instructure/pandautils/room/{ => appdatabase}/entities/FileUploadInputEntity.kt (88%) rename libs/pandautils/src/main/java/com/instructure/pandautils/room/{ => appdatabase}/entities/MediaCommentEntity.kt (92%) rename libs/pandautils/src/main/java/com/instructure/pandautils/room/{ => appdatabase}/entities/PendingSubmissionCommentEntity.kt (96%) rename libs/pandautils/src/main/java/com/instructure/pandautils/room/{ => appdatabase}/entities/SubmissionCommentEntity.kt (93%) rename libs/pandautils/src/main/java/com/instructure/pandautils/room/{ => appdatabase}/model/PendingSubmissionCommentWithFileUploadInput.kt (87%) rename libs/pandautils/src/main/java/com/instructure/pandautils/room/{ => appdatabase}/model/SubmissionCommentWithAttachments.kt (76%) delete mode 100644 libs/pandautils/src/main/java/com/instructure/pandautils/room/daos/AuthorDao.kt diff --git a/apps/student/src/main/java/com/instructure/student/di/DatabaseModule.kt b/apps/student/src/main/java/com/instructure/student/di/DatabaseModule.kt index 4d90364744..2d08671dfb 100644 --- a/apps/student/src/main/java/com/instructure/student/di/DatabaseModule.kt +++ b/apps/student/src/main/java/com/instructure/student/di/DatabaseModule.kt @@ -2,8 +2,8 @@ package com.instructure.student.di import android.content.Context import androidx.room.Room -import com.instructure.pandautils.room.AppDatabase -import com.instructure.pandautils.room.appDatabaseMigrations +import com.instructure.pandautils.room.appdatabase.AppDatabase +import com.instructure.pandautils.room.appdatabase.appDatabaseMigrations import com.instructure.student.db.Db import com.instructure.student.db.StudentDb import com.instructure.student.db.getInstance diff --git a/apps/student/src/main/java/com/instructure/student/fragment/InboxComposeMessageFragment.kt b/apps/student/src/main/java/com/instructure/student/fragment/InboxComposeMessageFragment.kt index e3b128d04c..3a478db30b 100644 --- a/apps/student/src/main/java/com/instructure/student/fragment/InboxComposeMessageFragment.kt +++ b/apps/student/src/main/java/com/instructure/student/fragment/InboxComposeMessageFragment.kt @@ -40,7 +40,7 @@ import com.instructure.pandautils.analytics.ScreenView import com.instructure.pandautils.binding.viewBinding import com.instructure.pandautils.features.file.upload.FileUploadDialogFragment import com.instructure.pandautils.features.file.upload.FileUploadDialogParent -import com.instructure.pandautils.room.daos.AttachmentDao +import com.instructure.pandautils.room.appdatabase.daos.AttachmentDao import com.instructure.pandautils.utils.* import com.instructure.student.R import com.instructure.student.adapter.CanvasContextSpinnerAdapter diff --git a/apps/teacher/src/main/java/com/instructure/teacher/di/DatabaseModule.kt b/apps/teacher/src/main/java/com/instructure/teacher/di/DatabaseModule.kt index 4155b356c7..35a5330d6c 100644 --- a/apps/teacher/src/main/java/com/instructure/teacher/di/DatabaseModule.kt +++ b/apps/teacher/src/main/java/com/instructure/teacher/di/DatabaseModule.kt @@ -2,8 +2,8 @@ package com.instructure.teacher.di import android.content.Context import androidx.room.Room -import com.instructure.pandautils.room.AppDatabase -import com.instructure.pandautils.room.appDatabaseMigrations +import com.instructure.pandautils.room.appdatabase.AppDatabase +import com.instructure.pandautils.room.appdatabase.appDatabaseMigrations import dagger.Module import dagger.Provides import dagger.hilt.InstallIn diff --git a/apps/teacher/src/main/java/com/instructure/teacher/factory/SpeedGraderCommentsPresenterFactory.kt b/apps/teacher/src/main/java/com/instructure/teacher/factory/SpeedGraderCommentsPresenterFactory.kt index 3d8e6dc9a5..dd5969218c 100644 --- a/apps/teacher/src/main/java/com/instructure/teacher/factory/SpeedGraderCommentsPresenterFactory.kt +++ b/apps/teacher/src/main/java/com/instructure/teacher/factory/SpeedGraderCommentsPresenterFactory.kt @@ -19,26 +19,26 @@ package com.instructure.teacher.factory import com.instructure.canvasapi2.models.Assignee import com.instructure.canvasapi2.models.Submission import com.instructure.canvasapi2.models.SubmissionComment -import com.instructure.pandautils.room.daos.* +import com.instructure.pandautils.room.appdatabase.daos.* import com.instructure.teacher.presenters.SpeedGraderCommentsPresenter import com.instructure.teacher.viewinterface.SpeedGraderCommentsView import instructure.androidblueprint.PresenterFactory class SpeedGraderCommentsPresenterFactory( - val rawComments: java.util.ArrayList, - val submissionHistory: List, - val assignee: Assignee, - val courseId: Long, - val assignmentId: Long, - val groupMessage: Boolean, - val submissionCommentDao: SubmissionCommentDao, - val attachmentDao: AttachmentDao, - val authorDao: AuthorDao, - val mediaCommentDao: MediaCommentDao, - val pendingSubmissionCommentDao: PendingSubmissionCommentDao, - val fileUploadInputDao: FileUploadInputDao, - val selectedAttemptId: Long, - val assignmentEnhancementsEnabled: Boolean + val rawComments: java.util.ArrayList, + val submissionHistory: List, + val assignee: Assignee, + val courseId: Long, + val assignmentId: Long, + val groupMessage: Boolean, + val submissionCommentDao: SubmissionCommentDao, + val attachmentDao: AttachmentDao, + val authorDao: AuthorDao, + val mediaCommentDao: MediaCommentDao, + val pendingSubmissionCommentDao: PendingSubmissionCommentDao, + val fileUploadInputDao: FileUploadInputDao, + val selectedAttemptId: Long, + val assignmentEnhancementsEnabled: Boolean ) : PresenterFactory { override fun create() = SpeedGraderCommentsPresenter( rawComments, diff --git a/apps/teacher/src/main/java/com/instructure/teacher/fragments/AddMessageFragment.kt b/apps/teacher/src/main/java/com/instructure/teacher/fragments/AddMessageFragment.kt index c625c26264..a29ec17687 100644 --- a/apps/teacher/src/main/java/com/instructure/teacher/fragments/AddMessageFragment.kt +++ b/apps/teacher/src/main/java/com/instructure/teacher/fragments/AddMessageFragment.kt @@ -36,7 +36,7 @@ import com.instructure.pandautils.dialogs.UnsavedChangesExitDialog import com.instructure.pandautils.features.file.upload.FileUploadDialogFragment import com.instructure.pandautils.features.file.upload.FileUploadDialogParent import com.instructure.pandautils.fragments.BasePresenterFragment -import com.instructure.pandautils.room.daos.AttachmentDao +import com.instructure.pandautils.room.appdatabase.daos.AttachmentDao import com.instructure.pandautils.utils.* import com.instructure.pandautils.views.AttachmentView import com.instructure.teacher.R diff --git a/apps/teacher/src/main/java/com/instructure/teacher/fragments/SpeedGraderCommentsFragment.kt b/apps/teacher/src/main/java/com/instructure/teacher/fragments/SpeedGraderCommentsFragment.kt index 99de8ec19e..9e00e050d4 100644 --- a/apps/teacher/src/main/java/com/instructure/teacher/fragments/SpeedGraderCommentsFragment.kt +++ b/apps/teacher/src/main/java/com/instructure/teacher/fragments/SpeedGraderCommentsFragment.kt @@ -39,8 +39,8 @@ import com.instructure.pandautils.features.file.upload.FileUploadDialogFragment import com.instructure.pandautils.features.file.upload.FileUploadDialogParent import com.instructure.pandautils.features.file.upload.worker.FileUploadWorker import com.instructure.pandautils.fragments.BaseListFragment -import com.instructure.pandautils.room.daos.* -import com.instructure.pandautils.room.entities.FileUploadInputEntity +import com.instructure.pandautils.room.appdatabase.daos.* +import com.instructure.pandautils.room.appdatabase.entities.FileUploadInputEntity import com.instructure.pandautils.services.NotoriousUploadService import com.instructure.pandautils.utils.* import com.instructure.teacher.R diff --git a/apps/teacher/src/main/java/com/instructure/teacher/presenters/SpeedGraderCommentsPresenter.kt b/apps/teacher/src/main/java/com/instructure/teacher/presenters/SpeedGraderCommentsPresenter.kt index 608bc51867..71191aed4b 100644 --- a/apps/teacher/src/main/java/com/instructure/teacher/presenters/SpeedGraderCommentsPresenter.kt +++ b/apps/teacher/src/main/java/com/instructure/teacher/presenters/SpeedGraderCommentsPresenter.kt @@ -29,10 +29,10 @@ import com.instructure.canvasapi2.utils.weave.catch import com.instructure.canvasapi2.utils.weave.tryWeave import com.instructure.canvasapi2.utils.weave.weave import com.instructure.pandautils.features.file.upload.worker.FileUploadWorker -import com.instructure.pandautils.room.daos.* -import com.instructure.pandautils.room.entities.FileUploadInputEntity -import com.instructure.pandautils.room.entities.PendingSubmissionCommentEntity -import com.instructure.pandautils.room.model.SubmissionCommentWithAttachments +import com.instructure.pandautils.room.appdatabase.daos.* +import com.instructure.pandautils.room.appdatabase.entities.FileUploadInputEntity +import com.instructure.pandautils.room.appdatabase.entities.PendingSubmissionCommentEntity +import com.instructure.pandautils.room.appdatabase.model.SubmissionCommentWithAttachments import com.instructure.teacher.events.SubmissionCommentsUpdated import com.instructure.teacher.events.SubmissionUpdatedEvent import com.instructure.teacher.events.post diff --git a/apps/teacher/src/main/java/com/instructure/teacher/utils/SGPendingMediaCommentReceiver.kt b/apps/teacher/src/main/java/com/instructure/teacher/utils/SGPendingMediaCommentReceiver.kt index 3f73eba592..69fdf2bdfd 100644 --- a/apps/teacher/src/main/java/com/instructure/teacher/utils/SGPendingMediaCommentReceiver.kt +++ b/apps/teacher/src/main/java/com/instructure/teacher/utils/SGPendingMediaCommentReceiver.kt @@ -22,7 +22,7 @@ import android.content.Intent import com.instructure.canvasapi2.models.SubmissionComment import com.instructure.canvasapi2.models.postmodels.CommentSendStatus import com.instructure.canvasapi2.models.postmodels.PendingSubmissionComment -import com.instructure.pandautils.room.daos.PendingSubmissionCommentDao +import com.instructure.pandautils.room.appdatabase.daos.PendingSubmissionCommentDao import com.instructure.pandautils.utils.Const import com.instructure.teacher.events.UploadMediaCommentUpdateEvent import com.instructure.teacher.events.post diff --git a/libs/pandautils/src/main/java/com/instructure/pandautils/di/DatabaseModule.kt b/libs/pandautils/src/main/java/com/instructure/pandautils/di/DatabaseModule.kt index d4fb71153f..5d7b3807eb 100644 --- a/libs/pandautils/src/main/java/com/instructure/pandautils/di/DatabaseModule.kt +++ b/libs/pandautils/src/main/java/com/instructure/pandautils/di/DatabaseModule.kt @@ -1,7 +1,7 @@ package com.instructure.pandautils.di -import com.instructure.pandautils.room.AppDatabase -import com.instructure.pandautils.room.daos.* +import com.instructure.pandautils.room.appdatabase.AppDatabase +import com.instructure.pandautils.room.appdatabase.daos.* import dagger.Module import dagger.Provides import dagger.hilt.InstallIn diff --git a/libs/pandautils/src/main/java/com/instructure/pandautils/features/dashboard/notifications/DashboardNotificationsViewModel.kt b/libs/pandautils/src/main/java/com/instructure/pandautils/features/dashboard/notifications/DashboardNotificationsViewModel.kt index 0671a67699..73b4c351ef 100644 --- a/libs/pandautils/src/main/java/com/instructure/pandautils/features/dashboard/notifications/DashboardNotificationsViewModel.kt +++ b/libs/pandautils/src/main/java/com/instructure/pandautils/features/dashboard/notifications/DashboardNotificationsViewModel.kt @@ -41,9 +41,9 @@ import com.instructure.pandautils.models.ConferenceDashboardBlacklist import com.instructure.pandautils.mvvm.Event import com.instructure.pandautils.mvvm.ItemViewModel import com.instructure.pandautils.mvvm.ViewState -import com.instructure.pandautils.room.daos.DashboardFileUploadDao -import com.instructure.pandautils.room.daos.FileUploadInputDao -import com.instructure.pandautils.room.entities.DashboardFileUploadEntity +import com.instructure.pandautils.room.appdatabase.daos.DashboardFileUploadDao +import com.instructure.pandautils.room.appdatabase.daos.FileUploadInputDao +import com.instructure.pandautils.room.appdatabase.entities.DashboardFileUploadEntity import com.instructure.pandautils.utils.orDefault import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.delay diff --git a/libs/pandautils/src/main/java/com/instructure/pandautils/features/file/upload/FileUploadDialogViewModel.kt b/libs/pandautils/src/main/java/com/instructure/pandautils/features/file/upload/FileUploadDialogViewModel.kt index 3c5fbb4b2d..c988076ccb 100644 --- a/libs/pandautils/src/main/java/com/instructure/pandautils/features/file/upload/FileUploadDialogViewModel.kt +++ b/libs/pandautils/src/main/java/com/instructure/pandautils/features/file/upload/FileUploadDialogViewModel.kt @@ -31,8 +31,8 @@ import com.instructure.pandautils.R import com.instructure.pandautils.features.file.upload.itemviewmodels.FileItemViewModel import com.instructure.pandautils.features.file.upload.worker.FileUploadWorker import com.instructure.pandautils.mvvm.Event -import com.instructure.pandautils.room.daos.FileUploadInputDao -import com.instructure.pandautils.room.entities.FileUploadInputEntity +import com.instructure.pandautils.room.appdatabase.daos.FileUploadInputDao +import com.instructure.pandautils.room.appdatabase.entities.FileUploadInputEntity import com.instructure.pandautils.utils.humanReadableByteCount import com.instructure.pandautils.utils.orDefault import dagger.hilt.android.lifecycle.HiltViewModel diff --git a/libs/pandautils/src/main/java/com/instructure/pandautils/features/file/upload/worker/FileUploadWorker.kt b/libs/pandautils/src/main/java/com/instructure/pandautils/features/file/upload/worker/FileUploadWorker.kt index d562086cd6..4799e6bf9f 100644 --- a/libs/pandautils/src/main/java/com/instructure/pandautils/features/file/upload/worker/FileUploadWorker.kt +++ b/libs/pandautils/src/main/java/com/instructure/pandautils/features/file/upload/worker/FileUploadWorker.kt @@ -35,8 +35,8 @@ import com.instructure.canvasapi2.utils.ProgressRequestUpdateListener import com.instructure.canvasapi2.utils.weave.awaitApi import com.instructure.pandautils.R import com.instructure.pandautils.features.file.upload.FileUploadUtilsHelper -import com.instructure.pandautils.room.daos.* -import com.instructure.pandautils.room.entities.* +import com.instructure.pandautils.room.appdatabase.daos.* +import com.instructure.pandautils.room.appdatabase.entities.* import com.instructure.pandautils.utils.FileUploadUtils import com.instructure.pandautils.utils.orDefault import com.instructure.pandautils.utils.toJson diff --git a/libs/pandautils/src/main/java/com/instructure/pandautils/features/shareextension/progress/ShareExtensionProgressDialogViewModel.kt b/libs/pandautils/src/main/java/com/instructure/pandautils/features/shareextension/progress/ShareExtensionProgressDialogViewModel.kt index 7161581248..c88be6a8f3 100644 --- a/libs/pandautils/src/main/java/com/instructure/pandautils/features/shareextension/progress/ShareExtensionProgressDialogViewModel.kt +++ b/libs/pandautils/src/main/java/com/instructure/pandautils/features/shareextension/progress/ShareExtensionProgressDialogViewModel.kt @@ -18,8 +18,8 @@ import com.instructure.pandautils.features.file.upload.worker.FileUploadWorker import com.instructure.pandautils.features.shareextension.progress.itemviewmodels.FileProgressItemViewModel import com.instructure.pandautils.mvvm.Event import com.instructure.pandautils.mvvm.ViewState -import com.instructure.pandautils.room.daos.DashboardFileUploadDao -import com.instructure.pandautils.room.daos.FileUploadInputDao +import com.instructure.pandautils.room.appdatabase.daos.DashboardFileUploadDao +import com.instructure.pandautils.room.appdatabase.daos.FileUploadInputDao import com.instructure.pandautils.utils.fromJson import com.instructure.pandautils.utils.humanReadableByteCount import com.instructure.pandautils.utils.orDefault diff --git a/libs/pandautils/src/main/java/com/instructure/pandautils/room/MigrationUtils.kt b/libs/pandautils/src/main/java/com/instructure/pandautils/room/MigrationUtils.kt new file mode 100644 index 0000000000..ae5918df5b --- /dev/null +++ b/libs/pandautils/src/main/java/com/instructure/pandautils/room/MigrationUtils.kt @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2023 - present Instructure, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +package com.instructure.pandautils.room + +import androidx.room.migration.Migration +import androidx.sqlite.db.SupportSQLiteDatabase + +fun createMigration(from: Int, to: Int, migrationBlock: (SupportSQLiteDatabase) -> Unit): Migration { + return object : Migration(from, to) { + override fun migrate(database: SupportSQLiteDatabase) { + migrationBlock(database) + } + } +} \ No newline at end of file diff --git a/libs/pandautils/src/main/java/com/instructure/pandautils/room/AppDatabase.kt b/libs/pandautils/src/main/java/com/instructure/pandautils/room/appdatabase/AppDatabase.kt similarity index 80% rename from libs/pandautils/src/main/java/com/instructure/pandautils/room/AppDatabase.kt rename to libs/pandautils/src/main/java/com/instructure/pandautils/room/appdatabase/AppDatabase.kt index 211c92dd66..403fd4631e 100644 --- a/libs/pandautils/src/main/java/com/instructure/pandautils/room/AppDatabase.kt +++ b/libs/pandautils/src/main/java/com/instructure/pandautils/room/appdatabase/AppDatabase.kt @@ -1,10 +1,11 @@ -package com.instructure.pandautils.room +package com.instructure.pandautils.room.appdatabase import androidx.room.Database import androidx.room.RoomDatabase import androidx.room.TypeConverters -import com.instructure.pandautils.room.daos.* -import com.instructure.pandautils.room.entities.* +import com.instructure.pandautils.room.Converters +import com.instructure.pandautils.room.appdatabase.daos.* +import com.instructure.pandautils.room.appdatabase.entities.* @Database( entities = [ diff --git a/libs/pandautils/src/main/java/com/instructure/pandautils/room/AppDatabaseMigrations.kt b/libs/pandautils/src/main/java/com/instructure/pandautils/room/appdatabase/AppDatabaseMigrations.kt similarity index 80% rename from libs/pandautils/src/main/java/com/instructure/pandautils/room/AppDatabaseMigrations.kt rename to libs/pandautils/src/main/java/com/instructure/pandautils/room/appdatabase/AppDatabaseMigrations.kt index 102422e5b7..a99980b688 100644 --- a/libs/pandautils/src/main/java/com/instructure/pandautils/room/AppDatabaseMigrations.kt +++ b/libs/pandautils/src/main/java/com/instructure/pandautils/room/appdatabase/AppDatabaseMigrations.kt @@ -15,18 +15,9 @@ * */ -package com.instructure.pandautils.room +package com.instructure.pandautils.room.appdatabase -import androidx.room.migration.Migration -import androidx.sqlite.db.SupportSQLiteDatabase - -fun createMigration(from: Int, to: Int, migrationBlock: (SupportSQLiteDatabase) -> Unit): Migration { - return object : Migration(from, to) { - override fun migrate(database: SupportSQLiteDatabase) { - migrationBlock(database) - } - } -} +import com.instructure.pandautils.room.createMigration val appDatabaseMigrations = arrayOf( @@ -44,7 +35,7 @@ val appDatabaseMigrations = arrayOf( database.execSQL("ALTER TABLE FileUploadInputEntity ADD COLUMN notificationId INTEGER") }, - createMigration(4, 5) {database -> + createMigration(4, 5) { database -> database.execSQL("ALTER TABLE DashboardFileUploadEntity ADD COLUMN courseId INTEGER") database.execSQL("ALTER TABLE DashboardFileUploadEntity ADD COLUMN assignmentId INTEGER") database.execSQL("ALTER TABLE DashboardFileUploadEntity ADD COLUMN attemptId INTEGER") diff --git a/libs/pandautils/src/main/java/com/instructure/pandautils/room/daos/AttachmentDao.kt b/libs/pandautils/src/main/java/com/instructure/pandautils/room/appdatabase/daos/AttachmentDao.kt similarity index 80% rename from libs/pandautils/src/main/java/com/instructure/pandautils/room/daos/AttachmentDao.kt rename to libs/pandautils/src/main/java/com/instructure/pandautils/room/appdatabase/daos/AttachmentDao.kt index f36a870970..1851f4d314 100644 --- a/libs/pandautils/src/main/java/com/instructure/pandautils/room/daos/AttachmentDao.kt +++ b/libs/pandautils/src/main/java/com/instructure/pandautils/room/appdatabase/daos/AttachmentDao.kt @@ -1,7 +1,7 @@ -package com.instructure.pandautils.room.daos +package com.instructure.pandautils.room.appdatabase.daos import androidx.room.* -import com.instructure.pandautils.room.entities.AttachmentEntity +import com.instructure.pandautils.room.appdatabase.entities.AttachmentEntity @Dao interface AttachmentDao { diff --git a/libs/pandautils/src/main/java/com/instructure/pandautils/room/appdatabase/daos/AuthorDao.kt b/libs/pandautils/src/main/java/com/instructure/pandautils/room/appdatabase/daos/AuthorDao.kt new file mode 100644 index 0000000000..629f056120 --- /dev/null +++ b/libs/pandautils/src/main/java/com/instructure/pandautils/room/appdatabase/daos/AuthorDao.kt @@ -0,0 +1,17 @@ +package com.instructure.pandautils.room.appdatabase.daos + +import androidx.room.* +import com.instructure.pandautils.room.appdatabase.entities.AuthorEntity + +@Dao +interface AuthorDao { + + @Insert(onConflict = OnConflictStrategy.REPLACE) + suspend fun insert(author: AuthorEntity) + + @Delete + suspend fun delete(author: AuthorEntity) + + @Update + suspend fun update(author: AuthorEntity) +} \ No newline at end of file diff --git a/libs/pandautils/src/main/java/com/instructure/pandautils/room/daos/DashboardFileUploadDao.kt b/libs/pandautils/src/main/java/com/instructure/pandautils/room/appdatabase/daos/DashboardFileUploadDao.kt similarity index 80% rename from libs/pandautils/src/main/java/com/instructure/pandautils/room/daos/DashboardFileUploadDao.kt rename to libs/pandautils/src/main/java/com/instructure/pandautils/room/appdatabase/daos/DashboardFileUploadDao.kt index f088ab5811..acc60f7d86 100644 --- a/libs/pandautils/src/main/java/com/instructure/pandautils/room/daos/DashboardFileUploadDao.kt +++ b/libs/pandautils/src/main/java/com/instructure/pandautils/room/appdatabase/daos/DashboardFileUploadDao.kt @@ -1,8 +1,8 @@ -package com.instructure.pandautils.room.daos +package com.instructure.pandautils.room.appdatabase.daos import androidx.lifecycle.LiveData import androidx.room.* -import com.instructure.pandautils.room.entities.DashboardFileUploadEntity +import com.instructure.pandautils.room.appdatabase.entities.DashboardFileUploadEntity @Dao interface DashboardFileUploadDao { diff --git a/libs/pandautils/src/main/java/com/instructure/pandautils/room/daos/FileUploadInputDao.kt b/libs/pandautils/src/main/java/com/instructure/pandautils/room/appdatabase/daos/FileUploadInputDao.kt similarity index 79% rename from libs/pandautils/src/main/java/com/instructure/pandautils/room/daos/FileUploadInputDao.kt rename to libs/pandautils/src/main/java/com/instructure/pandautils/room/appdatabase/daos/FileUploadInputDao.kt index 4dbf2fed69..d7ef2647dd 100644 --- a/libs/pandautils/src/main/java/com/instructure/pandautils/room/daos/FileUploadInputDao.kt +++ b/libs/pandautils/src/main/java/com/instructure/pandautils/room/appdatabase/daos/FileUploadInputDao.kt @@ -1,7 +1,7 @@ -package com.instructure.pandautils.room.daos +package com.instructure.pandautils.room.appdatabase.daos import androidx.room.* -import com.instructure.pandautils.room.entities.FileUploadInputEntity +import com.instructure.pandautils.room.appdatabase.entities.FileUploadInputEntity @Dao interface FileUploadInputDao { diff --git a/libs/pandautils/src/main/java/com/instructure/pandautils/room/daos/MediaCommentDao.kt b/libs/pandautils/src/main/java/com/instructure/pandautils/room/appdatabase/daos/MediaCommentDao.kt similarity index 72% rename from libs/pandautils/src/main/java/com/instructure/pandautils/room/daos/MediaCommentDao.kt rename to libs/pandautils/src/main/java/com/instructure/pandautils/room/appdatabase/daos/MediaCommentDao.kt index 5f89c5d70b..615818d205 100644 --- a/libs/pandautils/src/main/java/com/instructure/pandautils/room/daos/MediaCommentDao.kt +++ b/libs/pandautils/src/main/java/com/instructure/pandautils/room/appdatabase/daos/MediaCommentDao.kt @@ -1,10 +1,10 @@ -package com.instructure.pandautils.room.daos +package com.instructure.pandautils.room.appdatabase.daos import androidx.room.Dao import androidx.room.Delete import androidx.room.Insert import androidx.room.Update -import com.instructure.pandautils.room.entities.MediaCommentEntity +import com.instructure.pandautils.room.appdatabase.entities.MediaCommentEntity @Dao interface MediaCommentDao { diff --git a/libs/pandautils/src/main/java/com/instructure/pandautils/room/daos/PendingSubmissionCommentDao.kt b/libs/pandautils/src/main/java/com/instructure/pandautils/room/appdatabase/daos/PendingSubmissionCommentDao.kt similarity index 85% rename from libs/pandautils/src/main/java/com/instructure/pandautils/room/daos/PendingSubmissionCommentDao.kt rename to libs/pandautils/src/main/java/com/instructure/pandautils/room/appdatabase/daos/PendingSubmissionCommentDao.kt index 3fe46f4609..dc47e5d03f 100644 --- a/libs/pandautils/src/main/java/com/instructure/pandautils/room/daos/PendingSubmissionCommentDao.kt +++ b/libs/pandautils/src/main/java/com/instructure/pandautils/room/appdatabase/daos/PendingSubmissionCommentDao.kt @@ -1,8 +1,8 @@ -package com.instructure.pandautils.room.daos +package com.instructure.pandautils.room.appdatabase.daos import androidx.room.* -import com.instructure.pandautils.room.entities.PendingSubmissionCommentEntity -import com.instructure.pandautils.room.model.PendingSubmissionCommentWithFileUploadInput +import com.instructure.pandautils.room.appdatabase.entities.PendingSubmissionCommentEntity +import com.instructure.pandautils.room.appdatabase.model.PendingSubmissionCommentWithFileUploadInput @Dao interface PendingSubmissionCommentDao { diff --git a/libs/pandautils/src/main/java/com/instructure/pandautils/room/daos/SubmissionCommentDao.kt b/libs/pandautils/src/main/java/com/instructure/pandautils/room/appdatabase/daos/SubmissionCommentDao.kt similarity index 66% rename from libs/pandautils/src/main/java/com/instructure/pandautils/room/daos/SubmissionCommentDao.kt rename to libs/pandautils/src/main/java/com/instructure/pandautils/room/appdatabase/daos/SubmissionCommentDao.kt index bcfca3d9b8..0286854878 100644 --- a/libs/pandautils/src/main/java/com/instructure/pandautils/room/daos/SubmissionCommentDao.kt +++ b/libs/pandautils/src/main/java/com/instructure/pandautils/room/appdatabase/daos/SubmissionCommentDao.kt @@ -1,8 +1,8 @@ -package com.instructure.pandautils.room.daos +package com.instructure.pandautils.room.appdatabase.daos import androidx.room.* -import com.instructure.pandautils.room.entities.SubmissionCommentEntity -import com.instructure.pandautils.room.model.SubmissionCommentWithAttachments +import com.instructure.pandautils.room.appdatabase.entities.SubmissionCommentEntity +import com.instructure.pandautils.room.appdatabase.model.SubmissionCommentWithAttachments @Dao interface SubmissionCommentDao { diff --git a/libs/pandautils/src/main/java/com/instructure/pandautils/room/entities/AttachmentEntity.kt b/libs/pandautils/src/main/java/com/instructure/pandautils/room/appdatabase/entities/AttachmentEntity.kt similarity index 95% rename from libs/pandautils/src/main/java/com/instructure/pandautils/room/entities/AttachmentEntity.kt rename to libs/pandautils/src/main/java/com/instructure/pandautils/room/appdatabase/entities/AttachmentEntity.kt index 0ff8873ad2..97ba2e503d 100644 --- a/libs/pandautils/src/main/java/com/instructure/pandautils/room/entities/AttachmentEntity.kt +++ b/libs/pandautils/src/main/java/com/instructure/pandautils/room/appdatabase/entities/AttachmentEntity.kt @@ -1,4 +1,4 @@ -package com.instructure.pandautils.room.entities +package com.instructure.pandautils.room.appdatabase.entities import androidx.room.Entity import androidx.room.PrimaryKey diff --git a/libs/pandautils/src/main/java/com/instructure/pandautils/room/entities/AuthorEntity.kt b/libs/pandautils/src/main/java/com/instructure/pandautils/room/appdatabase/entities/AuthorEntity.kt similarity index 91% rename from libs/pandautils/src/main/java/com/instructure/pandautils/room/entities/AuthorEntity.kt rename to libs/pandautils/src/main/java/com/instructure/pandautils/room/appdatabase/entities/AuthorEntity.kt index c87682cf31..81fbda4f72 100644 --- a/libs/pandautils/src/main/java/com/instructure/pandautils/room/entities/AuthorEntity.kt +++ b/libs/pandautils/src/main/java/com/instructure/pandautils/room/appdatabase/entities/AuthorEntity.kt @@ -1,4 +1,4 @@ -package com.instructure.pandautils.room.entities +package com.instructure.pandautils.room.appdatabase.entities import androidx.room.Entity import androidx.room.PrimaryKey diff --git a/libs/pandautils/src/main/java/com/instructure/pandautils/room/entities/DashboardFileUploadEntity.kt b/libs/pandautils/src/main/java/com/instructure/pandautils/room/appdatabase/entities/DashboardFileUploadEntity.kt similarity index 84% rename from libs/pandautils/src/main/java/com/instructure/pandautils/room/entities/DashboardFileUploadEntity.kt rename to libs/pandautils/src/main/java/com/instructure/pandautils/room/appdatabase/entities/DashboardFileUploadEntity.kt index 47ed64d78e..fe45f15c7c 100644 --- a/libs/pandautils/src/main/java/com/instructure/pandautils/room/entities/DashboardFileUploadEntity.kt +++ b/libs/pandautils/src/main/java/com/instructure/pandautils/room/appdatabase/entities/DashboardFileUploadEntity.kt @@ -1,4 +1,4 @@ -package com.instructure.pandautils.room.entities +package com.instructure.pandautils.room.appdatabase.entities import androidx.room.Entity import androidx.room.PrimaryKey diff --git a/libs/pandautils/src/main/java/com/instructure/pandautils/room/entities/FileUploadInputEntity.kt b/libs/pandautils/src/main/java/com/instructure/pandautils/room/appdatabase/entities/FileUploadInputEntity.kt similarity index 88% rename from libs/pandautils/src/main/java/com/instructure/pandautils/room/entities/FileUploadInputEntity.kt rename to libs/pandautils/src/main/java/com/instructure/pandautils/room/appdatabase/entities/FileUploadInputEntity.kt index 8d7ef9ec5e..d50518a568 100644 --- a/libs/pandautils/src/main/java/com/instructure/pandautils/room/entities/FileUploadInputEntity.kt +++ b/libs/pandautils/src/main/java/com/instructure/pandautils/room/appdatabase/entities/FileUploadInputEntity.kt @@ -1,8 +1,8 @@ -package com.instructure.pandautils.room.entities +package com.instructure.pandautils.room.appdatabase.entities import androidx.room.Entity import androidx.room.PrimaryKey -import java.util.Random +import java.util.* @Entity data class FileUploadInputEntity( diff --git a/libs/pandautils/src/main/java/com/instructure/pandautils/room/entities/MediaCommentEntity.kt b/libs/pandautils/src/main/java/com/instructure/pandautils/room/appdatabase/entities/MediaCommentEntity.kt similarity index 92% rename from libs/pandautils/src/main/java/com/instructure/pandautils/room/entities/MediaCommentEntity.kt rename to libs/pandautils/src/main/java/com/instructure/pandautils/room/appdatabase/entities/MediaCommentEntity.kt index 4b3a2eef91..a8a982385e 100644 --- a/libs/pandautils/src/main/java/com/instructure/pandautils/room/entities/MediaCommentEntity.kt +++ b/libs/pandautils/src/main/java/com/instructure/pandautils/room/appdatabase/entities/MediaCommentEntity.kt @@ -1,4 +1,4 @@ -package com.instructure.pandautils.room.entities +package com.instructure.pandautils.room.appdatabase.entities import androidx.room.Entity import androidx.room.PrimaryKey diff --git a/libs/pandautils/src/main/java/com/instructure/pandautils/room/entities/PendingSubmissionCommentEntity.kt b/libs/pandautils/src/main/java/com/instructure/pandautils/room/appdatabase/entities/PendingSubmissionCommentEntity.kt similarity index 96% rename from libs/pandautils/src/main/java/com/instructure/pandautils/room/entities/PendingSubmissionCommentEntity.kt rename to libs/pandautils/src/main/java/com/instructure/pandautils/room/appdatabase/entities/PendingSubmissionCommentEntity.kt index 171525bf11..31ad1b2588 100644 --- a/libs/pandautils/src/main/java/com/instructure/pandautils/room/entities/PendingSubmissionCommentEntity.kt +++ b/libs/pandautils/src/main/java/com/instructure/pandautils/room/appdatabase/entities/PendingSubmissionCommentEntity.kt @@ -1,4 +1,4 @@ -package com.instructure.pandautils.room.entities +package com.instructure.pandautils.room.appdatabase.entities import androidx.room.Entity import androidx.room.PrimaryKey diff --git a/libs/pandautils/src/main/java/com/instructure/pandautils/room/entities/SubmissionCommentEntity.kt b/libs/pandautils/src/main/java/com/instructure/pandautils/room/appdatabase/entities/SubmissionCommentEntity.kt similarity index 93% rename from libs/pandautils/src/main/java/com/instructure/pandautils/room/entities/SubmissionCommentEntity.kt rename to libs/pandautils/src/main/java/com/instructure/pandautils/room/appdatabase/entities/SubmissionCommentEntity.kt index c79957c3da..ebf984b4a5 100644 --- a/libs/pandautils/src/main/java/com/instructure/pandautils/room/entities/SubmissionCommentEntity.kt +++ b/libs/pandautils/src/main/java/com/instructure/pandautils/room/appdatabase/entities/SubmissionCommentEntity.kt @@ -1,4 +1,4 @@ -package com.instructure.pandautils.room.entities +package com.instructure.pandautils.room.appdatabase.entities import androidx.room.Entity import androidx.room.PrimaryKey diff --git a/libs/pandautils/src/main/java/com/instructure/pandautils/room/model/PendingSubmissionCommentWithFileUploadInput.kt b/libs/pandautils/src/main/java/com/instructure/pandautils/room/appdatabase/model/PendingSubmissionCommentWithFileUploadInput.kt similarity index 87% rename from libs/pandautils/src/main/java/com/instructure/pandautils/room/model/PendingSubmissionCommentWithFileUploadInput.kt rename to libs/pandautils/src/main/java/com/instructure/pandautils/room/appdatabase/model/PendingSubmissionCommentWithFileUploadInput.kt index ea636619ba..9b0c1d9018 100644 --- a/libs/pandautils/src/main/java/com/instructure/pandautils/room/model/PendingSubmissionCommentWithFileUploadInput.kt +++ b/libs/pandautils/src/main/java/com/instructure/pandautils/room/appdatabase/model/PendingSubmissionCommentWithFileUploadInput.kt @@ -1,12 +1,12 @@ -package com.instructure.pandautils.room.model +package com.instructure.pandautils.room.appdatabase.model import androidx.room.Embedded import androidx.room.Relation import com.instructure.canvasapi2.models.postmodels.CommentSendStatus import com.instructure.canvasapi2.models.postmodels.FileUploadWorkerData import com.instructure.canvasapi2.models.postmodels.PendingSubmissionComment -import com.instructure.pandautils.room.entities.FileUploadInputEntity -import com.instructure.pandautils.room.entities.PendingSubmissionCommentEntity +import com.instructure.pandautils.room.appdatabase.entities.FileUploadInputEntity +import com.instructure.pandautils.room.appdatabase.entities.PendingSubmissionCommentEntity import java.util.* data class PendingSubmissionCommentWithFileUploadInput( diff --git a/libs/pandautils/src/main/java/com/instructure/pandautils/room/model/SubmissionCommentWithAttachments.kt b/libs/pandautils/src/main/java/com/instructure/pandautils/room/appdatabase/model/SubmissionCommentWithAttachments.kt similarity index 76% rename from libs/pandautils/src/main/java/com/instructure/pandautils/room/model/SubmissionCommentWithAttachments.kt rename to libs/pandautils/src/main/java/com/instructure/pandautils/room/appdatabase/model/SubmissionCommentWithAttachments.kt index 86a979fd76..40bd4de657 100644 --- a/libs/pandautils/src/main/java/com/instructure/pandautils/room/model/SubmissionCommentWithAttachments.kt +++ b/libs/pandautils/src/main/java/com/instructure/pandautils/room/appdatabase/model/SubmissionCommentWithAttachments.kt @@ -1,12 +1,12 @@ -package com.instructure.pandautils.room.model +package com.instructure.pandautils.room.appdatabase.model import androidx.room.Embedded import androidx.room.Relation import com.instructure.canvasapi2.models.SubmissionComment -import com.instructure.pandautils.room.entities.AttachmentEntity -import com.instructure.pandautils.room.entities.AuthorEntity -import com.instructure.pandautils.room.entities.MediaCommentEntity -import com.instructure.pandautils.room.entities.SubmissionCommentEntity +import com.instructure.pandautils.room.appdatabase.entities.AttachmentEntity +import com.instructure.pandautils.room.appdatabase.entities.AuthorEntity +import com.instructure.pandautils.room.appdatabase.entities.MediaCommentEntity +import com.instructure.pandautils.room.appdatabase.entities.SubmissionCommentEntity data class SubmissionCommentWithAttachments( @Embedded diff --git a/libs/pandautils/src/main/java/com/instructure/pandautils/room/daos/AuthorDao.kt b/libs/pandautils/src/main/java/com/instructure/pandautils/room/daos/AuthorDao.kt deleted file mode 100644 index 3a2a120f2d..0000000000 --- a/libs/pandautils/src/main/java/com/instructure/pandautils/room/daos/AuthorDao.kt +++ /dev/null @@ -1,21 +0,0 @@ -package com.instructure.pandautils.room.daos - -import androidx.room.Dao -import androidx.room.Delete -import androidx.room.Insert -import androidx.room.OnConflictStrategy -import androidx.room.Update -import com.instructure.pandautils.room.entities.AuthorEntity - -@Dao -interface AuthorDao { - - @Insert(onConflict = OnConflictStrategy.REPLACE) - suspend fun insert(author: AuthorEntity) - - @Delete - suspend fun delete(author: AuthorEntity) - - @Update - suspend fun update(author: AuthorEntity) -} \ No newline at end of file diff --git a/libs/pandautils/src/test/java/com/instructure/pandautils/features/dashboard/notifications/DashboardNotificationsViewModelTest.kt b/libs/pandautils/src/test/java/com/instructure/pandautils/features/dashboard/notifications/DashboardNotificationsViewModelTest.kt index f327cc5c19..4c048de31d 100644 --- a/libs/pandautils/src/test/java/com/instructure/pandautils/features/dashboard/notifications/DashboardNotificationsViewModelTest.kt +++ b/libs/pandautils/src/test/java/com/instructure/pandautils/features/dashboard/notifications/DashboardNotificationsViewModelTest.kt @@ -38,9 +38,9 @@ import com.instructure.pandautils.features.dashboard.notifications.itemviewmodel import com.instructure.pandautils.features.file.upload.FileUploadUtilsHelper import com.instructure.pandautils.features.file.upload.worker.FileUploadWorker import com.instructure.pandautils.models.ConferenceDashboardBlacklist -import com.instructure.pandautils.room.daos.DashboardFileUploadDao -import com.instructure.pandautils.room.daos.FileUploadInputDao -import com.instructure.pandautils.room.entities.DashboardFileUploadEntity +import com.instructure.pandautils.room.appdatabase.daos.DashboardFileUploadDao +import com.instructure.pandautils.room.appdatabase.daos.FileUploadInputDao +import com.instructure.pandautils.room.appdatabase.entities.DashboardFileUploadEntity import io.mockk.coEvery import io.mockk.every import io.mockk.mockk diff --git a/libs/pandautils/src/test/java/com/instructure/pandautils/features/file/upload/FileUploadViewModelTest.kt b/libs/pandautils/src/test/java/com/instructure/pandautils/features/file/upload/FileUploadViewModelTest.kt index 5be6529542..cd1c2eabb4 100644 --- a/libs/pandautils/src/test/java/com/instructure/pandautils/features/file/upload/FileUploadViewModelTest.kt +++ b/libs/pandautils/src/test/java/com/instructure/pandautils/features/file/upload/FileUploadViewModelTest.kt @@ -27,7 +27,7 @@ import com.instructure.canvasapi2.models.Assignment import com.instructure.canvasapi2.models.Course import com.instructure.canvasapi2.models.postmodels.FileSubmitObject import com.instructure.pandautils.R -import com.instructure.pandautils.room.daos.FileUploadInputDao +import com.instructure.pandautils.room.appdatabase.daos.FileUploadInputDao import io.mockk.every import io.mockk.mockk import io.mockk.mockkStatic diff --git a/libs/pandautils/src/test/java/com/instructure/pandautils/features/shareextension/progress/ShareExtensionProgressViewModelTest.kt b/libs/pandautils/src/test/java/com/instructure/pandautils/features/shareextension/progress/ShareExtensionProgressViewModelTest.kt index f980a27023..63fb5e9cca 100644 --- a/libs/pandautils/src/test/java/com/instructure/pandautils/features/shareextension/progress/ShareExtensionProgressViewModelTest.kt +++ b/libs/pandautils/src/test/java/com/instructure/pandautils/features/shareextension/progress/ShareExtensionProgressViewModelTest.kt @@ -15,9 +15,9 @@ import com.instructure.pandautils.features.file.upload.FileUploadType import com.instructure.pandautils.features.file.upload.FileUploadUtilsHelper import com.instructure.pandautils.features.file.upload.worker.FileUploadWorker import com.instructure.pandautils.mvvm.ViewState -import com.instructure.pandautils.room.daos.DashboardFileUploadDao -import com.instructure.pandautils.room.daos.FileUploadInputDao -import com.instructure.pandautils.room.entities.FileUploadInputEntity +import com.instructure.pandautils.room.appdatabase.daos.DashboardFileUploadDao +import com.instructure.pandautils.room.appdatabase.daos.FileUploadInputDao +import com.instructure.pandautils.room.appdatabase.entities.FileUploadInputEntity import com.instructure.pandautils.utils.toJson import io.mockk.coEvery import io.mockk.every From 087921164ed346214faabf38a7de8773a68834d2 Mon Sep 17 00:00:00 2001 From: Kristof Deak <92309696+kdeakinstructure@users.noreply.github.com> Date: Thu, 18 May 2023 10:01:28 +0200 Subject: [PATCH 035/103] Implement negative login test case (invalid, and empty credentials will displayed the corresponding error message). (#1976) --- .../student/ui/e2e/LoginE2ETest.kt | 41 +++++++++++++++++++ .../student/ui/pages/LoginSignInPage.kt | 14 ++++++- 2 files changed, 54 insertions(+), 1 deletion(-) diff --git a/apps/student/src/androidTest/java/com/instructure/student/ui/e2e/LoginE2ETest.kt b/apps/student/src/androidTest/java/com/instructure/student/ui/e2e/LoginE2ETest.kt index f891bdb88b..03c3a51ddc 100644 --- a/apps/student/src/androidTest/java/com/instructure/student/ui/e2e/LoginE2ETest.kt +++ b/apps/student/src/androidTest/java/com/instructure/student/ui/e2e/LoginE2ETest.kt @@ -205,6 +205,47 @@ class LoginE2ETest : StudentTest() { leftSideNavigationDrawerPage.logout() } + @E2E + @Test + @TestMetaData(Priority.IMPORTANT, FeatureCategory.LOGIN, TestCategory.E2E) + fun testInvalidAndEmptyLoginCredentialsE2E() { + + val INVALID_USERNAME = "invalidusercred@test.com" + val INVALID_PASSWORD = "invalidpw" + val INVALID_CREDENTIALS_ERROR_MESSAGE = "Invalid username or password. Trouble logging in?" + val NO_PASSWORD_GIVEN_ERROR_MESSAGE = "No password was given" + val DOMAIN = "mobileqa.beta" + + Log.d(STEP_TAG, "Click 'Find My School' button.") + loginLandingPage.clickFindMySchoolButton() + + Log.d(STEP_TAG,"Enter domain: $DOMAIN.instructure.com.") + loginFindSchoolPage.enterDomain(DOMAIN) + + Log.d(STEP_TAG,"Click on 'Next' button on the Toolbar.") + loginFindSchoolPage.clickToolbarNextMenuItem() + + Log.d(STEP_TAG, "Try to login with invalid, non-existing credentials ($INVALID_USERNAME, $INVALID_PASSWORD)." + + "Assert that the invalid credentials error message is displayed.") + loginSignInPage.loginAs(INVALID_USERNAME, INVALID_PASSWORD) + loginSignInPage.assertLoginErrorMessage(INVALID_CREDENTIALS_ERROR_MESSAGE) + + Log.d(STEP_TAG, "Try to login with no credentials typed in either of the username and password field." + + "Assert that the no password was given error message is displayed.") + loginSignInPage.loginAs(EMPTY_STRING, EMPTY_STRING) + loginSignInPage.assertLoginErrorMessage(NO_PASSWORD_GIVEN_ERROR_MESSAGE) + + Log.d(STEP_TAG, "Try to login with leaving only the password field empty." + + "Assert that the no password was given error message is displayed.") + loginSignInPage.loginAs(INVALID_USERNAME, EMPTY_STRING) + loginSignInPage.assertLoginErrorMessage(NO_PASSWORD_GIVEN_ERROR_MESSAGE) + + Log.d(STEP_TAG, "Try to login with leaving only the username field empty." + + "Assert that the invalid credentials error message is displayed.") + loginSignInPage.loginAs(EMPTY_STRING, INVALID_PASSWORD) + loginSignInPage.assertLoginErrorMessage(INVALID_CREDENTIALS_ERROR_MESSAGE) + } + // Verify that students can sign into vanity domain @E2E @Test diff --git a/apps/student/src/androidTest/java/com/instructure/student/ui/pages/LoginSignInPage.kt b/apps/student/src/androidTest/java/com/instructure/student/ui/pages/LoginSignInPage.kt index 7b1614edb1..4173184b49 100644 --- a/apps/student/src/androidTest/java/com/instructure/student/ui/pages/LoginSignInPage.kt +++ b/apps/student/src/androidTest/java/com/instructure/student/ui/pages/LoginSignInPage.kt @@ -16,9 +16,11 @@ */ package com.instructure.student.ui.pages +import androidx.test.espresso.web.assertion.WebViewAssertions.webMatches import androidx.test.espresso.web.sugar.Web import androidx.test.espresso.web.sugar.Web.onWebView import androidx.test.espresso.web.webdriver.DriverAtoms.findElement +import androidx.test.espresso.web.webdriver.DriverAtoms.getText import androidx.test.espresso.web.webdriver.DriverAtoms.webClick import androidx.test.espresso.web.webdriver.DriverAtoms.webKeys import androidx.test.espresso.web.webdriver.Locator @@ -27,6 +29,7 @@ import com.instructure.espresso.OnViewWithId import com.instructure.espresso.assertDisplayed import com.instructure.espresso.page.BasePage import com.instructure.student.R +import org.hamcrest.CoreMatchers.containsString @Suppress("unused") class LoginSignInPage: BasePage() { @@ -36,6 +39,7 @@ class LoginSignInPage: BasePage() { private val LOGIN_BUTTON_CSS = "button[type=\"submit\"]" private val FORGOT_PASSWORD_BUTTON_CSS = "a[class=\"forgot-password flip-to-back\"]" private val AUTHORIZE_BUTTON_CSS = "button[type=\"submit\"]" + private val LOGIN_ERROR_MESSAGE_HOLDER_CSS = "div[class='error']" private val signInRoot by OnViewWithId(R.id.signInRoot, autoAssert = false) private val toolbar by OnViewWithId(R.id.toolbar, autoAssert = false) @@ -62,6 +66,10 @@ class LoginSignInPage: BasePage() { return onWebView().withElement(findElement(Locator.CSS_SELECTOR, AUTHORIZE_BUTTON_CSS)) } + private fun loginErrorMessageHolder(): Web.WebInteraction<*> { + return onWebView().withElement(findElement(Locator.CSS_SELECTOR, LOGIN_ERROR_MESSAGE_HOLDER_CSS)) + } + //endregion //region Assertion Helpers @@ -96,11 +104,15 @@ class LoginSignInPage: BasePage() { forgotPasswordButton().perform(webClick()) } + fun assertLoginErrorMessage(errorMessage: String) { + loginErrorMessageHolder().check(webMatches(getText(), containsString(errorMessage))) + } + fun loginAs(user: CanvasUserApiModel) { loginAs(user.loginId, user.password) } - private fun loginAs(loginId: String, password: String) { + fun loginAs(loginId: String, password: String) { enterEmail(loginId) enterPassword(password) clickLoginButton() From ecf67bd78300c9da7d72b18af5d7c9fcfe637670 Mon Sep 17 00:00:00 2001 From: Akos Hermann <72087159+hermannakos@users.noreply.github.com> Date: Thu, 18 May 2023 12:30:25 +0200 Subject: [PATCH 036/103] [MBL-16510][Teacher] Added Teacher Pandata (#1984) --- .../student/activity/CallbackActivity.kt | 6 +- .../fragment/EditPageDetailsFragment.kt | 2 + .../student/service/StudentPageViewService.kt | 3 +- apps/teacher/build.gradle | 2 + apps/teacher/src/main/AndroidManifest.xml | 5 + .../teacher/activities/InitActivity.kt | 15 ++ .../teacher/activities/SpeedGraderActivity.kt | 5 + .../calendar/event/CalendarEventFragment.kt | 7 +- .../files/search/FileSearchFragment.kt | 2 + .../modules/list/ui/ModuleListFragment.kt | 8 +- .../features/syllabus/ui/SyllabusFragment.kt | 2 + .../fragments/AnnouncementListFragment.kt | 6 +- .../fragments/AssignmentDetailsFragment.kt | 67 +++--- .../fragments/AssignmentListFragment.kt | 50 +++-- .../fragments/CourseBrowserFragment.kt | 48 ++-- .../fragments/CourseSettingsFragment.kt | 35 +-- .../fragments/CreateDiscussionFragment.kt | 206 +++++++++--------- .../CreateOrEditAnnouncementFragment.kt | 44 ++-- .../CreateOrEditPageDetailsFragment.kt | 60 +++-- .../teacher/fragments/DashboardFragment.kt | 2 + .../fragments/DiscussionsDetailsFragment.kt | 101 +++++---- .../fragments/DiscussionsListFragment.kt | 52 ++--- .../fragments/DiscussionsUpdateFragment.kt | 16 +- .../EditAssignmentDetailsFragment.kt | 134 ++++++------ .../teacher/fragments/FileListFragment.kt | 38 ++-- .../teacher/fragments/LtiLaunchFragment.kt | 10 + .../teacher/fragments/PageDetailsFragment.kt | 63 ++++-- .../teacher/fragments/PageListFragment.kt | 30 +-- .../teacher/fragments/PeopleListFragment.kt | 11 +- .../teacher/fragments/ProfileEditFragment.kt | 2 + .../teacher/fragments/ProfileFragment.kt | 2 + .../teacher/fragments/QuizDetailsFragment.kt | 65 +++--- .../teacher/fragments/QuizListFragment.kt | 34 +-- .../teacher/fragments/SettingsFragment.kt | 3 + .../teacher/fragments/ToDoFragment.kt | 2 + .../services/TeacherPageViewService.kt | 27 +++ .../teacher/utils/BaseAppManager.kt | 4 + .../utils/pageview/PageViewUploadService.kt | 7 +- .../fragments/BaseExpandableSyncFragment.kt | 2 - .../pandautils/fragments/BaseFragment.kt | 2 - .../fragments/BasePresenterFragment.kt | 2 - .../pandautils/fragments/BaseSyncFragment.kt | 2 - 42 files changed, 680 insertions(+), 504 deletions(-) create mode 100644 apps/teacher/src/main/java/com/instructure/teacher/services/TeacherPageViewService.kt diff --git a/apps/student/src/main/java/com/instructure/student/activity/CallbackActivity.kt b/apps/student/src/main/java/com/instructure/student/activity/CallbackActivity.kt index 5593f62be9..cdf7fd15fd 100644 --- a/apps/student/src/main/java/com/instructure/student/activity/CallbackActivity.kt +++ b/apps/student/src/main/java/com/instructure/student/activity/CallbackActivity.kt @@ -21,11 +21,7 @@ import android.os.Bundle import com.google.firebase.crashlytics.FirebaseCrashlytics import com.heapanalytics.android.Heap import com.instructure.canvasapi2.StatusCallback -import com.instructure.canvasapi2.managers.FeaturesManager -import com.instructure.canvasapi2.managers.LaunchDefinitionsManager -import com.instructure.canvasapi2.managers.ThemeManager -import com.instructure.canvasapi2.managers.UnreadCountManager -import com.instructure.canvasapi2.managers.UserManager +import com.instructure.canvasapi2.managers.* import com.instructure.canvasapi2.models.* import com.instructure.canvasapi2.utils.* import com.instructure.canvasapi2.utils.pageview.PandataInfo diff --git a/apps/student/src/main/java/com/instructure/student/fragment/EditPageDetailsFragment.kt b/apps/student/src/main/java/com/instructure/student/fragment/EditPageDetailsFragment.kt index e2a25d822e..96e685f846 100644 --- a/apps/student/src/main/java/com/instructure/student/fragment/EditPageDetailsFragment.kt +++ b/apps/student/src/main/java/com/instructure/student/fragment/EditPageDetailsFragment.kt @@ -30,6 +30,7 @@ import com.instructure.canvasapi2.models.CanvasContext import com.instructure.canvasapi2.models.Page import com.instructure.canvasapi2.models.postmodels.PagePostBody import com.instructure.canvasapi2.utils.ApiPrefs +import com.instructure.canvasapi2.utils.pageview.PageView import com.instructure.canvasapi2.utils.pageview.PageViewUrl import com.instructure.canvasapi2.utils.weave.WeaveJob import com.instructure.canvasapi2.utils.weave.awaitApi @@ -46,6 +47,7 @@ import com.instructure.student.dialog.UnsavedChangesExitDialog import com.instructure.student.events.PageUpdatedEvent import org.greenrobot.eventbus.EventBus +@PageView @ScreenView(SCREEN_VIEW_EDIT_PAGE_DETAILS) class EditPageDetailsFragment : ParentFragment() { diff --git a/apps/student/src/main/java/com/instructure/student/service/StudentPageViewService.kt b/apps/student/src/main/java/com/instructure/student/service/StudentPageViewService.kt index 6c2f881dbe..e53b079a51 100644 --- a/apps/student/src/main/java/com/instructure/student/service/StudentPageViewService.kt +++ b/apps/student/src/main/java/com/instructure/student/service/StudentPageViewService.kt @@ -17,9 +17,9 @@ package com.instructure.student.service import com.google.firebase.crashlytics.FirebaseCrashlytics -import com.instructure.student.BuildConfig import com.instructure.canvasapi2.utils.pageview.PageViewUploadService import com.instructure.canvasapi2.utils.pageview.PandataInfo +import com.instructure.student.BuildConfig /** * A [PageViewUploadService] specific to the Student application. @@ -30,7 +30,6 @@ import com.instructure.canvasapi2.utils.pageview.PandataInfo class StudentPageViewService : PageViewUploadService() { override val appKey = pandataAppKey - override fun onException(e: Throwable) = FirebaseCrashlytics.getInstance().recordException(e) diff --git a/apps/teacher/build.gradle b/apps/teacher/build.gradle index 9be4666b09..98851a2f6f 100644 --- a/apps/teacher/build.gradle +++ b/apps/teacher/build.gradle @@ -16,6 +16,7 @@ import com.instructure.android.buildtools.transform.LocaleTransformer import com.instructure.android.buildtools.transform.MasqueradeUITransformer +import com.instructure.android.buildtools.transform.PageViewTransformer import com.instructure.android.buildtools.transform.ProjectTransformer import com.instructure.android.buildtools.transform.ScreenViewTransformer @@ -189,6 +190,7 @@ android { new ProjectTransformer( android, new MasqueradeUITransformer('com.instructure.teacher.activities.LoginActivity.class'), + new PageViewTransformer(), new LocaleTransformer(project), new ScreenViewTransformer("teacher") ) diff --git a/apps/teacher/src/main/AndroidManifest.xml b/apps/teacher/src/main/AndroidManifest.xml index 6c80bd89e7..8ce6e5605d 100644 --- a/apps/teacher/src/main/AndroidManifest.xml +++ b/apps/teacher/src/main/AndroidManifest.xml @@ -228,6 +228,11 @@ + + { + PandataManager.getToken(TeacherPageViewService.pandataAppKey, it) + } + } catch (ignore: Throwable) { + Logger.w("Unable to refresh pandata info") + } + } + } + requestNotificationsPermission() } diff --git a/apps/teacher/src/main/java/com/instructure/teacher/activities/SpeedGraderActivity.kt b/apps/teacher/src/main/java/com/instructure/teacher/activities/SpeedGraderActivity.kt index 061ed894c7..e637051f83 100644 --- a/apps/teacher/src/main/java/com/instructure/teacher/activities/SpeedGraderActivity.kt +++ b/apps/teacher/src/main/java/com/instructure/teacher/activities/SpeedGraderActivity.kt @@ -36,6 +36,8 @@ import com.instructure.canvasapi2.models.GradeableStudentSubmission import com.instructure.canvasapi2.models.StudentAssignee import com.instructure.canvasapi2.utils.ApiPrefs import com.instructure.canvasapi2.utils.coerceAtLeast +import com.instructure.canvasapi2.utils.pageview.PageView +import com.instructure.canvasapi2.utils.pageview.PageViewUrlParam import com.instructure.canvasapi2.utils.rangeWithin import com.instructure.canvasapi2.utils.weave.weave import com.instructure.interactions.router.Route @@ -74,6 +76,7 @@ import org.greenrobot.eventbus.Subscribe import org.greenrobot.eventbus.ThreadMode import java.util.* +@PageView("courses/{courseId}/gradebook/speed_grader?assignment_id={assignmentId}") @ScreenView(SCREEN_VIEW_SPEED_GRADER) @AndroidEntryPoint class SpeedGraderActivity : BasePresenterActivity(), SpeedGraderView { @@ -81,7 +84,9 @@ class SpeedGraderActivity : BasePresenterActivity by lazy { intent.extras!!.getParcelableArrayList(Const.SUBMISSION) ?: arrayListOf() } diff --git a/apps/teacher/src/main/java/com/instructure/teacher/features/calendar/event/CalendarEventFragment.kt b/apps/teacher/src/main/java/com/instructure/teacher/features/calendar/event/CalendarEventFragment.kt index 2d5c65ca8e..6a3cf13b4b 100644 --- a/apps/teacher/src/main/java/com/instructure/teacher/features/calendar/event/CalendarEventFragment.kt +++ b/apps/teacher/src/main/java/com/instructure/teacher/features/calendar/event/CalendarEventFragment.kt @@ -28,10 +28,7 @@ import com.instructure.pandautils.analytics.SCREEN_VIEW_CALENDAR_EVENT import com.instructure.pandautils.analytics.ScreenView import com.instructure.pandautils.binding.viewBinding import com.instructure.pandautils.fragments.BaseFragment -import com.instructure.pandautils.utils.NullableParcelableArg -import com.instructure.pandautils.utils.ViewStyler -import com.instructure.pandautils.utils.loadHtmlWithIframes -import com.instructure.pandautils.utils.setupAsBackButton +import com.instructure.pandautils.utils.* import com.instructure.pandautils.views.CanvasWebView import com.instructure.teacher.R import com.instructure.teacher.activities.InternalWebViewActivity @@ -45,6 +42,8 @@ class CalendarEventFragment : BaseFragment() { private val binding by viewBinding(FragmentCalendarEventBinding::bind) + var canvasContext: CanvasContext? by NullableParcelableArg(key = Const.CANVAS_CONTEXT) + private val transformer = CalendarEventStateTransformer() private var scheduleItem: ScheduleItem? by NullableParcelableArg(key = SCHEDULE_ITEM) diff --git a/apps/teacher/src/main/java/com/instructure/teacher/features/files/search/FileSearchFragment.kt b/apps/teacher/src/main/java/com/instructure/teacher/features/files/search/FileSearchFragment.kt index 70b90dbbe4..bdcad88de9 100644 --- a/apps/teacher/src/main/java/com/instructure/teacher/features/files/search/FileSearchFragment.kt +++ b/apps/teacher/src/main/java/com/instructure/teacher/features/files/search/FileSearchFragment.kt @@ -45,6 +45,8 @@ class FileSearchFragment : BaseSyncFragment< private val binding by viewBinding(FragmentFileSearchBinding::bind) + var canvasContext: CanvasContext? by NullableParcelableArg(key = Const.CANVAS_CONTEXT) + private val searchAdapter by lazy { FileSearchAdapter(requireContext(), canvasContext.textAndIconColor, presenter) { val editableFile = EditableFile(it, presenter.usageRights, presenter.licenses, canvasContext.backgroundColor, presenter.canvasContext, R.drawable.ic_document) diff --git a/apps/teacher/src/main/java/com/instructure/teacher/features/modules/list/ui/ModuleListFragment.kt b/apps/teacher/src/main/java/com/instructure/teacher/features/modules/list/ui/ModuleListFragment.kt index 5ab383cdce..4c4358f49b 100644 --- a/apps/teacher/src/main/java/com/instructure/teacher/features/modules/list/ui/ModuleListFragment.kt +++ b/apps/teacher/src/main/java/com/instructure/teacher/features/modules/list/ui/ModuleListFragment.kt @@ -20,6 +20,7 @@ import android.os.Bundle import android.view.LayoutInflater import android.view.ViewGroup import com.instructure.canvasapi2.models.CanvasContext +import com.instructure.canvasapi2.utils.pageview.PageView import com.instructure.pandautils.analytics.SCREEN_VIEW_MODULE_LIST import com.instructure.pandautils.analytics.ScreenView import com.instructure.pandautils.utils.Const @@ -31,11 +32,12 @@ import com.instructure.teacher.features.modules.list.* import com.instructure.teacher.mobius.common.ui.MobiusFragment import com.instructure.teacher.mobius.common.ui.Presenter +@PageView(url = "{canvasContext}/modules") @ScreenView(SCREEN_VIEW_MODULE_LIST) class ModuleListFragment : MobiusFragment() { - val course by ParcelableArg(key = Const.COURSE) + val canvasContext by ParcelableArg(key = Const.COURSE) private val scrollToItemId by NLongArg(key = Const.MODULE_ITEM_ID) @@ -43,11 +45,11 @@ class ModuleListFragment : MobiusFragment = ModuleListPresenter - override fun makeInitModel(): ModuleListModel = ModuleListModel(course = course, scrollToItemId = scrollToItemId) + override fun makeInitModel(): ModuleListModel = ModuleListModel(course = canvasContext, scrollToItemId = scrollToItemId) override val eventSources = listOf(ModuleListEventBusSource()) diff --git a/apps/teacher/src/main/java/com/instructure/teacher/features/syllabus/ui/SyllabusFragment.kt b/apps/teacher/src/main/java/com/instructure/teacher/features/syllabus/ui/SyllabusFragment.kt index dcc81914e3..9311533f37 100644 --- a/apps/teacher/src/main/java/com/instructure/teacher/features/syllabus/ui/SyllabusFragment.kt +++ b/apps/teacher/src/main/java/com/instructure/teacher/features/syllabus/ui/SyllabusFragment.kt @@ -20,6 +20,7 @@ import android.view.LayoutInflater import android.view.ViewGroup import com.instructure.canvasapi2.models.CanvasContext import com.instructure.canvasapi2.models.Course +import com.instructure.canvasapi2.utils.pageview.PageView import com.instructure.pandautils.analytics.SCREEN_VIEW_SYLLABUS import com.instructure.pandautils.analytics.ScreenView import com.instructure.pandautils.utils.Const @@ -29,6 +30,7 @@ import com.instructure.teacher.databinding.FragmentSyllabusBinding import com.instructure.teacher.features.syllabus.* import com.instructure.teacher.mobius.common.ui.MobiusFragment +@PageView("{canvasContext}/assignments/syllabus") @ScreenView(SCREEN_VIEW_SYLLABUS) class SyllabusFragment : MobiusFragment() { diff --git a/apps/teacher/src/main/java/com/instructure/teacher/fragments/AnnouncementListFragment.kt b/apps/teacher/src/main/java/com/instructure/teacher/fragments/AnnouncementListFragment.kt index ef7fc02c14..563596b6a7 100644 --- a/apps/teacher/src/main/java/com/instructure/teacher/fragments/AnnouncementListFragment.kt +++ b/apps/teacher/src/main/java/com/instructure/teacher/fragments/AnnouncementListFragment.kt @@ -17,16 +17,18 @@ package com.instructure.teacher.fragments import com.instructure.canvasapi2.models.CanvasContext +import com.instructure.canvasapi2.utils.pageview.PageView import com.instructure.pandautils.analytics.SCREEN_VIEW_ANNOUNCEMENT_LIST import com.instructure.pandautils.analytics.ScreenView +@PageView(url = "{canvasContext}/announcements") @ScreenView(SCREEN_VIEW_ANNOUNCEMENT_LIST) class AnnouncementListFragment : DiscussionsListFragment() { companion object { fun newInstance(canvasContext: CanvasContext) = AnnouncementListFragment().apply { - mCanvasContext = canvasContext - mIsAnnouncements = true + this.canvasContext = canvasContext + isAnnouncements = true } } } diff --git a/apps/teacher/src/main/java/com/instructure/teacher/fragments/AssignmentDetailsFragment.kt b/apps/teacher/src/main/java/com/instructure/teacher/fragments/AssignmentDetailsFragment.kt index e778402c77..c984050bbc 100644 --- a/apps/teacher/src/main/java/com/instructure/teacher/fragments/AssignmentDetailsFragment.kt +++ b/apps/teacher/src/main/java/com/instructure/teacher/fragments/AssignmentDetailsFragment.kt @@ -24,6 +24,8 @@ import com.instructure.canvasapi2.models.Assignment.Companion.submissionTypeToPr import com.instructure.canvasapi2.models.CanvasContext import com.instructure.canvasapi2.models.Course import com.instructure.canvasapi2.utils.* +import com.instructure.canvasapi2.utils.pageview.PageView +import com.instructure.canvasapi2.utils.pageview.PageViewUrl import com.instructure.interactions.Identity import com.instructure.interactions.MasterDetailInteractions import com.instructure.interactions.router.Route @@ -57,6 +59,7 @@ import org.greenrobot.eventbus.Subscribe import org.greenrobot.eventbus.ThreadMode import java.util.* +@PageView @ScreenView(SCREEN_VIEW_ASSIGNMENT_DETAILS) class AssignmentDetailsFragment : BasePresenterFragment< AssignmentDetailsPresenter, @@ -64,14 +67,18 @@ class AssignmentDetailsFragment : BasePresenterFragment< private val binding by viewBinding(FragmentAssignmentDetailsBinding::bind) - private var mAssignment: Assignment by ParcelableArg(Assignment(), ASSIGNMENT) - private var mCourse: Course by ParcelableArg(Course()) - private var mAssignmentId: Long by LongArg(0L, ASSIGNMENT_ID) + private var assignment: Assignment by ParcelableArg(Assignment(), ASSIGNMENT) + private var course: Course by ParcelableArg(Course()) + private var assignmentId: Long by LongArg(0L, ASSIGNMENT_ID) - private var mNeedToForceNetwork = false + private var needToForceNetwork = false private var loadHtmlJob: Job? = null + @Suppress("unused") + @PageViewUrl + private fun makePageViewUrl() = "${ApiPrefs.fullDomain}/${course.contextId.replace("_", "s/")}/${assignment.id}" + override fun layoutResId() = R.layout.fragment_assignment_details override fun onRefreshFinished() {} @@ -88,38 +95,38 @@ class AssignmentDetailsFragment : BasePresenterFragment< override fun onReadySetGo(presenter: AssignmentDetailsPresenter) { // if we don't have an assignmentId that means we have an assignment, so we can load the data - if(mAssignmentId == 0L) { - presenter.loadData(mNeedToForceNetwork) + if(assignmentId == 0L) { + presenter.loadData(needToForceNetwork) } else { - presenter.getAssignment(mAssignmentId, mCourse) + presenter.getAssignment(assignmentId, course) } } override fun populateAssignmentDetails(assignment: Assignment) = with(binding) { - mAssignment = assignment + this@AssignmentDetailsFragment.assignment = assignment toolbar.setupMenu(R.menu.menu_edit_generic) { openEditPage(assignment) } swipeRefreshLayout.isRefreshing = false setupViews(assignment) setupListeners(assignment) - ViewStyler.themeToolbarColored(requireActivity(), toolbar, mCourse.backgroundColor, requireContext().getColor(R.color.white)) + ViewStyler.themeToolbarColored(requireActivity(), toolbar, course.backgroundColor, requireContext().getColor(R.color.white)) } - override fun getPresenterFactory() = AssignmentDetailPresenterFactory(mAssignment) + override fun getPresenterFactory() = AssignmentDetailPresenterFactory(assignment) override fun onPresenterPrepared(presenter: AssignmentDetailsPresenter) {} private fun setupToolbar() = with(binding) { toolbar.setupBackButtonWithExpandCollapseAndBack(this@AssignmentDetailsFragment) { toolbar.updateToolbarExpandCollapseIcon(this@AssignmentDetailsFragment) - ViewStyler.themeToolbarColored(requireActivity(), toolbar, mCourse.backgroundColor, requireContext().getColor(R.color.white)) + ViewStyler.themeToolbarColored(requireActivity(), toolbar, course.backgroundColor, requireContext().getColor(R.color.white)) (activity as MasterDetailInteractions).toggleExpandCollapse() } toolbar.title = getString(R.string.assignment_details) if(!isTablet) { - toolbar.subtitle = mCourse.name + toolbar.subtitle = course.name } - ViewStyler.themeToolbarColored(requireActivity(), toolbar, mCourse.backgroundColor, requireContext().getColor(R.color.white)) + ViewStyler.themeToolbarColored(requireActivity(), toolbar, course.backgroundColor, requireContext().getColor(R.color.white)) } private fun setupViews(assignment: Assignment) = with(binding) { @@ -229,19 +236,19 @@ class AssignmentDetailsFragment : BasePresenterFragment< submissionTypesArrowIcon.setVisible() submissionTypesLayout.onClickWithRequireNetwork { // If the user is a designer we don't want to let them look at LTI tools - if (mCourse.isDesigner) { + if (course.isDesigner) { toast(R.string.errorIsDesigner) return@onClickWithRequireNetwork } val ltiUrl = assignment.url.validOrNull() ?: assignment.htmlUrl if(!ltiUrl.isNullOrBlank()) { - val args = LtiLaunchFragment.makeBundle(mCourse, ltiUrl, assignment.name!!, true) - RouteMatcher.route(requireContext(), Route(LtiLaunchFragment::class.java, mCourse, args)) + val args = LtiLaunchFragment.makeBundle(course, ltiUrl, assignment.name!!, true) + RouteMatcher.route(requireContext(), Route(LtiLaunchFragment::class.java, course, args)) } } } - submissionsLayout.setVisible(!mCourse.isDesigner) + submissionsLayout.setVisible(!course.isDesigner) } @Suppress("UsePropertyAccessSyntax") @@ -281,9 +288,9 @@ class AssignmentDetailsFragment : BasePresenterFragment< // Load description loadHtmlJob = descriptionWebViewWrapper.webView.loadHtmlWithIframes(requireContext(), assignment.description, { - descriptionWebViewWrapper.loadHtml(it, assignment.name, baseUrl = mAssignment.htmlUrl) + descriptionWebViewWrapper.loadHtml(it, assignment.name, baseUrl = assignment.htmlUrl) }) { - LtiLaunchFragment.routeLtiLaunchFragment(requireContext(), mCourse, it) + LtiLaunchFragment.routeLtiLaunchFragment(requireContext(), course, it) } } @@ -361,21 +368,21 @@ class AssignmentDetailsFragment : BasePresenterFragment< private fun setupListeners(assignment: Assignment) = with(binding) { dueLayout.setOnClickListener { val args = DueDatesFragment.makeBundle(assignment) - RouteMatcher.route(requireContext(), Route(null, DueDatesFragment::class.java, mCourse, args)) + RouteMatcher.route(requireContext(), Route(null, DueDatesFragment::class.java, course, args)) } submissionsLayout.setOnClickListener { - navigateToSubmissions(mCourse, assignment, AssignmentSubmissionListPresenter.SubmissionListFilter.ALL) + navigateToSubmissions(course, assignment, AssignmentSubmissionListPresenter.SubmissionListFilter.ALL) } donutGroup.viewAllSubmissions.onClick { submissionsLayout.performClick() } // Separate click listener for a11y donutGroup.gradedWrapper.setOnClickListener { - navigateToSubmissions(mCourse, assignment, AssignmentSubmissionListPresenter.SubmissionListFilter.GRADED) + navigateToSubmissions(course, assignment, AssignmentSubmissionListPresenter.SubmissionListFilter.GRADED) } donutGroup.ungradedWrapper.setOnClickListener { - navigateToSubmissions(mCourse, assignment, AssignmentSubmissionListPresenter.SubmissionListFilter.NOT_GRADED) + navigateToSubmissions(course, assignment, AssignmentSubmissionListPresenter.SubmissionListFilter.NOT_GRADED) } donutGroup.notSubmittedWrapper.setOnClickListener { - navigateToSubmissions(mCourse, assignment, AssignmentSubmissionListPresenter.SubmissionListFilter.MISSING) + navigateToSubmissions(course, assignment, AssignmentSubmissionListPresenter.SubmissionListFilter.MISSING) } noDescriptionTextView.setOnClickListener { openEditPage(assignment) } @@ -385,7 +392,7 @@ class AssignmentDetailsFragment : BasePresenterFragment< assignment.discussionTopicHeader?.let { discussionTopicHeader -> viewDiscussionButton.setOnClickListener { - RouteMatcher.route(requireContext(), DiscussionRouterFragment.makeRoute(mCourse, discussionTopicHeader)) + RouteMatcher.route(requireContext(), DiscussionRouterFragment.makeRoute(course, discussionTopicHeader)) } } ?: viewDiscussionButton.setGone() @@ -394,7 +401,7 @@ class AssignmentDetailsFragment : BasePresenterFragment< private fun openEditPage(assignment: Assignment) { if(APIHelper.hasNetworkConnection()) { val args = EditAssignmentDetailsFragment.makeBundle(assignment, false) - RouteMatcher.route(requireContext(), Route(EditAssignmentDetailsFragment::class.java, mCourse, args)) + RouteMatcher.route(requireContext(), Route(EditAssignmentDetailsFragment::class.java, course, args)) } else { NoInternetConnectionDialog.show(requireFragmentManager()) } @@ -424,7 +431,7 @@ class AssignmentDetailsFragment : BasePresenterFragment< @Subscribe(threadMode = ThreadMode.MAIN, sticky = true) fun onAssignmentEdited(event: AssignmentUpdatedEvent) { event.once(javaClass.simpleName) { - if (it == presenter.mAssignment.id) mNeedToForceNetwork = true + if (it == presenter.mAssignment.id) needToForceNetwork = true } } @@ -440,12 +447,12 @@ class AssignmentDetailsFragment : BasePresenterFragment< @Subscribe(threadMode = ThreadMode.MAIN, sticky = true) fun onAssignmentGraded(event: AssignmentGradedEvent) { event.once(javaClass.simpleName) { - if(presenter.mAssignment.id == it) mNeedToForceNetwork = true + if(presenter.mAssignment.id == it) needToForceNetwork = true } } //Because of the presenter lifecycle using the assignment from there will result in random crashes. - override val identity: Long? get() = if(mAssignmentId != 0L) mAssignmentId else mAssignment.id + override val identity: Long? get() = if(assignmentId != 0L) assignmentId else assignment.id override val skipCheck: Boolean get() = false companion object { @@ -453,7 +460,7 @@ class AssignmentDetailsFragment : BasePresenterFragment< @JvmStatic val ASSIGNMENT_ID = "assignmentId" fun newInstance(course: Course, args: Bundle) = AssignmentDetailsFragment().withArgs(args).apply { - mCourse = course + this.course = course } fun makeBundle(assignment: Assignment): Bundle { diff --git a/apps/teacher/src/main/java/com/instructure/teacher/fragments/AssignmentListFragment.kt b/apps/teacher/src/main/java/com/instructure/teacher/fragments/AssignmentListFragment.kt index a71c9ddf7f..a20e6a382b 100644 --- a/apps/teacher/src/main/java/com/instructure/teacher/fragments/AssignmentListFragment.kt +++ b/apps/teacher/src/main/java/com/instructure/teacher/fragments/AssignmentListFragment.kt @@ -28,6 +28,7 @@ import com.instructure.canvasapi2.models.AssignmentGroup import com.instructure.canvasapi2.models.CanvasContext import com.instructure.canvasapi2.models.GradingPeriod import com.instructure.canvasapi2.utils.ApiPrefs +import com.instructure.canvasapi2.utils.pageview.PageView import com.instructure.interactions.router.Route import com.instructure.pandautils.analytics.SCREEN_VIEW_ASSIGNMENT_LIST import com.instructure.pandautils.analytics.ScreenView @@ -50,6 +51,7 @@ import org.greenrobot.eventbus.EventBus import org.greenrobot.eventbus.Subscribe import org.greenrobot.eventbus.ThreadMode +@PageView(url = "{canvasContext}/assignments") @ScreenView(SCREEN_VIEW_ASSIGNMENT_LIST) class AssignmentListFragment : BaseExpandableSyncFragment< AssignmentGroup, @@ -60,17 +62,17 @@ class AssignmentListFragment : BaseExpandableSyncFragment< private val binding by viewBinding(FragmentAssignmentListBinding::bind) - private var mCanvasContext: CanvasContext by ParcelableArg(default = CanvasContext.getGenericContext(CanvasContext.Type.COURSE, -1L, "")) - private var mPairedWithSubmissions: Boolean = false - private val mLinearLayoutManager by lazy { LinearLayoutManager(requireContext()) } + private var canvasContext: CanvasContext by ParcelableArg(default = CanvasContext.getGenericContext(CanvasContext.Type.COURSE, -1L, "")) + private var pairedWithSubmissions: Boolean = false + private val linearLayoutManager by lazy { LinearLayoutManager(requireContext()) } private lateinit var mRecyclerView: RecyclerView - private var mGradingPeriodMenu: PopupMenu? = null - private var mNeedToForceNetwork = false + private var gradingPeriodMenu: PopupMenu? = null + private var needToForceNetwork = false override fun layoutResId(): Int = R.layout.fragment_assignment_list override val recyclerView: RecyclerView get() = binding.assignmentRecyclerView - override fun getPresenterFactory() = AssignmentListPresenterFactory(mCanvasContext) + override fun getPresenterFactory() = AssignmentListPresenterFactory(canvasContext) override fun onPresenterPrepared(presenter: AssignmentListPresenter) { mRecyclerView = RecyclerViewUtils.buildRecyclerView( rootView = rootView, @@ -86,14 +88,14 @@ class AssignmentListFragment : BaseExpandableSyncFragment< } override fun onCreateView(view: View) { - mLinearLayoutManager.orientation = RecyclerView.VERTICAL + linearLayoutManager.orientation = RecyclerView.VERTICAL } override fun onReadySetGo(presenter: AssignmentListPresenter) { if (recyclerView.adapter == null) { mRecyclerView.adapter = adapter } - presenter.loadData(mNeedToForceNetwork) + presenter.loadData(needToForceNetwork) } override fun onResume() { @@ -112,24 +114,24 @@ class AssignmentListFragment : BaseExpandableSyncFragment< } override fun onPause() { - if(mGradingPeriodMenu != null) { - mGradingPeriodMenu?.dismiss() + if(gradingPeriodMenu != null) { + gradingPeriodMenu?.dismiss() } super.onPause() } override fun createAdapter(): AssignmentAdapter { - return AssignmentAdapter(requireContext(), presenter, mCanvasContext.textAndIconColor) { assignment -> - if (mPairedWithSubmissions) { + return AssignmentAdapter(requireContext(), presenter, canvasContext.textAndIconColor) { assignment -> + if (pairedWithSubmissions) { val args = AssignmentSubmissionListFragment.makeBundle(assignment) - RouteMatcher.route(requireContext(), Route(null, AssignmentSubmissionListFragment::class.java, mCanvasContext, args)) + RouteMatcher.route(requireContext(), Route(null, AssignmentSubmissionListFragment::class.java, canvasContext, args)) } else { if (assignment.submissionTypesRaw.contains(Assignment.SubmissionType.ONLINE_QUIZ.apiString)) { val args = QuizDetailsFragment.makeBundle(assignment.quizId) - RouteMatcher.route(requireContext(), Route(null, QuizDetailsFragment::class.java, mCanvasContext, args)) + RouteMatcher.route(requireContext(), Route(null, QuizDetailsFragment::class.java, canvasContext, args)) } else { val args = AssignmentDetailsFragment.makeBundle(assignment) - RouteMatcher.route(requireContext(), Route(null, AssignmentDetailsFragment::class.java, mCanvasContext, args)) + RouteMatcher.route(requireContext(), Route(null, AssignmentDetailsFragment::class.java, canvasContext, args)) } } } @@ -169,17 +171,17 @@ class AssignmentListFragment : BaseExpandableSyncFragment< //setup popup menu val menuItemView = rootView.findViewById(R.id.menu_grading_periods_filter) - mGradingPeriodMenu = PopupMenu(requireContext(), menuItemView, Gravity.TOP, 0, + gradingPeriodMenu = PopupMenu(requireContext(), menuItemView, Gravity.TOP, 0, R.style.Widget_AppCompat_PopupMenu_Overflow) - mGradingPeriodMenu?.setOnMenuItemClickListener { menuItem -> + gradingPeriodMenu?.setOnMenuItemClickListener { menuItem -> presenter.selectGradingPeriodIndex(menuItem.itemId) true } //add grading periods gradingPeriods.forEachIndexed { i, gradingPeriod -> - mGradingPeriodMenu?.menu?.add(0, i, 0, gradingPeriod.title) + gradingPeriodMenu?.menu?.add(0, i, 0, gradingPeriod.title) } //set the grading periods to the selection @@ -203,10 +205,10 @@ class AssignmentListFragment : BaseExpandableSyncFragment< private fun setupToolbar() = with(binding) { assignmentListToolbar.title = getString(R.string.assignments) - assignmentListToolbar.subtitle = mCanvasContext.name + assignmentListToolbar.subtitle = canvasContext.name assignmentListToolbar.setupBackButton(this@AssignmentListFragment) - ViewStyler.themeToolbarColored(requireActivity(), assignmentListToolbar, mCanvasContext.backgroundColor, requireContext().getColor(R.color.white)) + ViewStyler.themeToolbarColored(requireActivity(), assignmentListToolbar, canvasContext.backgroundColor, requireContext().getColor(R.color.white)) } override fun adjustGradingPeriodHeader(gradingPeriod: String, isVisible: Boolean, isFilterVisible: Boolean) { @@ -236,7 +238,7 @@ class AssignmentListFragment : BaseExpandableSyncFragment< val menuItemCallback: (MenuItem) -> Unit = { item -> when (item.itemId) { R.id.menu_grading_periods_filter -> { - mGradingPeriodMenu?.show() + gradingPeriodMenu?.show() } } } @@ -248,7 +250,7 @@ class AssignmentListFragment : BaseExpandableSyncFragment< // need to set a flag here. Because we use the event bus in the fragment instead of the presenter for unit testing purposes, // when we come back to this fragment it will go through the life cycle events again and the cached data will immediately // overwrite the data from the network if we refresh the presenter from here. - mNeedToForceNetwork = true + needToForceNetwork = true } } @@ -258,8 +260,8 @@ class AssignmentListFragment : BaseExpandableSyncFragment< @JvmStatic val PAIRED_WITH_SUBMISSIONS = "pairedWithSubmissions" fun getInstance(canvasContext: CanvasContext, args: Bundle) = AssignmentListFragment().apply { - mPairedWithSubmissions = args.getBoolean(PAIRED_WITH_SUBMISSIONS, false) - mCanvasContext = canvasContext + pairedWithSubmissions = args.getBoolean(PAIRED_WITH_SUBMISSIONS, false) + this.canvasContext = canvasContext } } } diff --git a/apps/teacher/src/main/java/com/instructure/teacher/fragments/CourseBrowserFragment.kt b/apps/teacher/src/main/java/com/instructure/teacher/fragments/CourseBrowserFragment.kt index 514c947e08..df78509560 100644 --- a/apps/teacher/src/main/java/com/instructure/teacher/fragments/CourseBrowserFragment.kt +++ b/apps/teacher/src/main/java/com/instructure/teacher/fragments/CourseBrowserFragment.kt @@ -31,6 +31,7 @@ import com.instructure.canvasapi2.utils.Analytics import com.instructure.canvasapi2.utils.AnalyticsEventConstants import com.instructure.canvasapi2.utils.ApiPrefs import com.instructure.canvasapi2.utils.isValid +import com.instructure.canvasapi2.utils.pageview.PageView import com.instructure.interactions.router.Route import com.instructure.pandautils.analytics.SCREEN_VIEW_COURSE_BROWSER import com.instructure.pandautils.analytics.ScreenView @@ -57,6 +58,7 @@ import org.greenrobot.eventbus.EventBus import org.greenrobot.eventbus.Subscribe import org.greenrobot.eventbus.ThreadMode +@PageView(url = "{canvasContext}") @ScreenView(SCREEN_VIEW_COURSE_BROWSER) class CourseBrowserFragment : BaseSyncFragment< Tab, @@ -68,13 +70,13 @@ class CourseBrowserFragment : BaseSyncFragment< private val binding by viewBinding(FragmentCourseBrowserBinding::bind) - private var mCanvasContext: CanvasContext by ParcelableArg(Course()) + private var canvasContext: CanvasContext by ParcelableArg(Course()) - private val mCourseBrowserHeader by lazy { rootView.findViewById(R.id.courseBrowserHeader) } + private val courseBrowserHeader by lazy { rootView.findViewById(R.id.courseBrowserHeader) } companion object { fun newInstance(context: CanvasContext) = CourseBrowserFragment().apply { - mCanvasContext = context + canvasContext = context } fun makeRoute(canvasContext: CanvasContext?) = Route(CourseBrowserFragment::class.java, canvasContext) @@ -86,7 +88,7 @@ class CourseBrowserFragment : BaseSyncFragment< override val recyclerView: RecyclerView get() = binding.courseBrowserRecyclerView override fun withPagination() = false - override fun getPresenterFactory() = CourseBrowserPresenterFactory(mCanvasContext) { tab, attendanceId -> + override fun getPresenterFactory() = CourseBrowserPresenterFactory(canvasContext) { tab, attendanceId -> //Filter for white-list supported features //TODO: support other things like it.isHidden when(tab.tabId) { @@ -133,15 +135,15 @@ class CourseBrowserFragment : BaseSyncFragment< presenter.loadData(false) } - override fun onResume() = with(binding) { + override fun onResume() { super.onResume() EventBus.getDefault().register(this@CourseBrowserFragment) (presenter.canvasContext as? Course)?.let { - courseImage.setCourseImage(it, it.backgroundColor, !TeacherPrefs.hideCourseColorOverlay) + binding.courseImage.setCourseImage(it, it.backgroundColor, !TeacherPrefs.hideCourseColorOverlay) } - courseBrowserTitle.text = presenter.canvasContext.name - courseBrowserSubtitle.text = (presenter.canvasContext as? Course)?.term?.name.orEmpty() - mCourseBrowserHeader.setTitleAndSubtitle(presenter.canvasContext.name.orEmpty(), (presenter.canvasContext as? Course)?.term?.name.orEmpty()) + binding.courseBrowserTitle.text = presenter.canvasContext.name + binding.courseBrowserSubtitle.text = (presenter.canvasContext as? Course)?.term?.name.orEmpty() + courseBrowserHeader.setTitleAndSubtitle(presenter.canvasContext.name.orEmpty(), (presenter.canvasContext as? Course)?.term?.name.orEmpty()) setupToolbar() if (!presenter.isEmpty) { checkIfEmpty() @@ -296,22 +298,22 @@ class CourseBrowserFragment : BaseSyncFragment< /** * Manages state of titles & subtitles when users scrolls */ - override fun onOffsetChanged(appBarLayout: AppBarLayout?, verticalOffset: Int) = with(binding) { + override fun onOffsetChanged(appBarLayout: AppBarLayout?, verticalOffset: Int) { val percentage = Math.abs(verticalOffset).div(appBarLayout?.totalScrollRange?.toFloat() ?: 1F) if(percentage <= 0.3F) { - val toolbarAnimation = ObjectAnimator.ofFloat(mCourseBrowserHeader, View.ALPHA, mCourseBrowserHeader.alpha, 0F) - val titleAnimation = ObjectAnimator.ofFloat(courseBrowserTitle, View.ALPHA, courseBrowserTitle.alpha, 1F) - val subtitleAnimation = ObjectAnimator.ofFloat(courseBrowserSubtitle, View.ALPHA, courseBrowserSubtitle.alpha, 0.8F) + val toolbarAnimation = ObjectAnimator.ofFloat(courseBrowserHeader, View.ALPHA, courseBrowserHeader.alpha, 0F) + val titleAnimation = ObjectAnimator.ofFloat(binding.courseBrowserTitle, View.ALPHA, binding.courseBrowserTitle.alpha, 1F) + val subtitleAnimation = ObjectAnimator.ofFloat(binding.courseBrowserSubtitle, View.ALPHA, binding.courseBrowserSubtitle.alpha, 0.8F) toolbarAnimation.setAutoCancel(true) titleAnimation.setAutoCancel(true) subtitleAnimation.setAutoCancel(true) - toolbarAnimation.target = mCourseBrowserHeader - titleAnimation.target = courseBrowserTitle - subtitleAnimation.target = courseBrowserSubtitle + toolbarAnimation.target = courseBrowserHeader + titleAnimation.target = binding.courseBrowserTitle + subtitleAnimation.target = binding.courseBrowserSubtitle toolbarAnimation.duration = 200 titleAnimation.duration = 320 @@ -322,17 +324,17 @@ class CourseBrowserFragment : BaseSyncFragment< subtitleAnimation.start() } else if(percentage > 0.7F) { - val toolbarAnimation = ObjectAnimator.ofFloat(mCourseBrowserHeader, View.ALPHA, mCourseBrowserHeader.alpha, 1F) - val titleAnimation = ObjectAnimator.ofFloat(courseBrowserTitle, View.ALPHA, courseBrowserTitle.alpha, 0F) - val subtitleAnimation = ObjectAnimator.ofFloat(courseBrowserSubtitle, View.ALPHA, courseBrowserSubtitle.alpha, 0F) + val toolbarAnimation = ObjectAnimator.ofFloat(courseBrowserHeader, View.ALPHA, courseBrowserHeader.alpha, 1F) + val titleAnimation = ObjectAnimator.ofFloat(binding.courseBrowserTitle, View.ALPHA, binding.courseBrowserTitle.alpha, 0F) + val subtitleAnimation = ObjectAnimator.ofFloat(binding.courseBrowserSubtitle, View.ALPHA, binding.courseBrowserSubtitle.alpha, 0F) toolbarAnimation.setAutoCancel(true) titleAnimation.setAutoCancel(true) subtitleAnimation.setAutoCancel(true) - toolbarAnimation.target = mCourseBrowserHeader - titleAnimation.target = courseBrowserTitle - subtitleAnimation.target = courseBrowserSubtitle + toolbarAnimation.target = courseBrowserHeader + titleAnimation.target = binding.courseBrowserTitle + subtitleAnimation.target = binding.courseBrowserSubtitle toolbarAnimation.duration = 200 titleAnimation.duration = 200 @@ -377,7 +379,7 @@ class CourseBrowserFragment : BaseSyncFragment< `package` = CANVAS_STUDENT_ID action = Const.INTENT_ACTION_STUDENT_VIEW putExtra(Const.TOKEN, token) - putExtra(Const.COURSE_ID, mCanvasContext.id) // Required to create/get test user + putExtra(Const.COURSE_ID, canvasContext.id) // Required to create/get test user putExtra(Const.DOMAIN, ApiPrefs.domain) putExtra(Const.CLIENT_ID, ApiPrefs.clientId) putExtra(Const.CLIENT_SECRET, ApiPrefs.clientSecret) diff --git a/apps/teacher/src/main/java/com/instructure/teacher/fragments/CourseSettingsFragment.kt b/apps/teacher/src/main/java/com/instructure/teacher/fragments/CourseSettingsFragment.kt index eedab53684..5bf2c89536 100644 --- a/apps/teacher/src/main/java/com/instructure/teacher/fragments/CourseSettingsFragment.kt +++ b/apps/teacher/src/main/java/com/instructure/teacher/fragments/CourseSettingsFragment.kt @@ -22,6 +22,8 @@ import android.os.Bundle import android.os.Parcelable import com.instructure.canvasapi2.models.Course import com.instructure.canvasapi2.utils.globalName +import com.instructure.canvasapi2.utils.pageview.PageView +import com.instructure.canvasapi2.utils.pageview.PageViewUrl import com.instructure.pandautils.analytics.SCREEN_VIEW_COURSE_SETTINGS import com.instructure.pandautils.analytics.ScreenView import com.instructure.pandautils.binding.viewBinding @@ -37,6 +39,7 @@ import com.instructure.teacher.utils.TeacherPrefs import com.instructure.teacher.utils.setupBackButton import com.instructure.teacher.viewinterface.CourseSettingsFragmentView +@PageView @ScreenView(SCREEN_VIEW_COURSE_SETTINGS) class CourseSettingsFragment : BasePresenterFragment< CourseSettingsFragmentPresenter, @@ -44,7 +47,11 @@ class CourseSettingsFragment : BasePresenterFragment< private val binding by viewBinding(FragmentCourseSettingsBinding::bind) - private var mCourse: Course by ParcelableArg(default = Course()) + private var course: Course by ParcelableArg(default = Course()) + + @Suppress("unused") + @PageViewUrl + private fun makePageViewUrl() = "courses/${course.id}/settings" private val mHomePages: Map by lazy { // Use LinkedHashMap map to keep order consistent between API levels @@ -62,16 +69,16 @@ class CourseSettingsFragment : BasePresenterFragment< override fun onReadySetGo(presenter: CourseSettingsFragmentPresenter) { setupToolbar() - binding.courseImage.setCourseImage(mCourse, mCourse.backgroundColor, !TeacherPrefs.hideCourseColorOverlay) + binding.courseImage.setCourseImage(course, course.backgroundColor, !TeacherPrefs.hideCourseColorOverlay) } override fun onViewStateRestored(savedInstanceState: Bundle?) { super.onViewStateRestored(savedInstanceState) if (savedInstanceState == null) { - updateCourseName(mCourse) - updateCourseHomePage(mCourse.homePage) + updateCourseName(course) + updateCourseHomePage(course.homePage) } else { - updateCourseName(mCourse) + updateCourseName(course) } } @@ -89,12 +96,12 @@ class CourseSettingsFragment : BasePresenterFragment< toolbar.setupBackButton(this@CourseSettingsFragment) toolbar.title = getString(R.string.course_settings) ViewStyler.themeToolbarLight(requireActivity(), toolbar) - toolbar.setSubtitleTextColor(mCourse.textAndIconColor) + toolbar.setSubtitleTextColor(course.textAndIconColor) } override fun showEditCourseNameDialog() { - val dialog: EditCourseNameDialog = EditCourseNameDialog.getInstance(requireActivity().supportFragmentManager, mCourse) { newName -> - presenter.editCourseName(newName, mCourse) + val dialog: EditCourseNameDialog = EditCourseNameDialog.getInstance(requireActivity().supportFragmentManager, course) { newName -> + presenter.editCourseName(newName, course) } dialog.show(requireActivity().supportFragmentManager, EditCourseNameDialog::class.java.simpleName) @@ -102,9 +109,9 @@ class CourseSettingsFragment : BasePresenterFragment< override fun showEditCourseHomePageDialog() { val (keys, values) = mHomePages.toList().unzip() - val selectedIdx = keys.indexOf(mCourse.homePage?.apiString) + val selectedIdx = keys.indexOf(course.homePage?.apiString) val dialog = RadioButtonDialog.getInstance(requireActivity().supportFragmentManager, getString(R.string.set_home_to), values as ArrayList, selectedIdx) { idx -> - presenter.editCourseHomePage(keys[idx], mCourse) + presenter.editCourseHomePage(keys[idx], course) } dialog.show(requireActivity().supportFragmentManager, RadioButtonDialog::class.java.simpleName) @@ -113,18 +120,18 @@ class CourseSettingsFragment : BasePresenterFragment< override fun updateCourseName(course: Course) = with(binding) { renameCourse.courseName.text = course.globalName toolbar.subtitle = course.globalName - mCourse.globalName = course.globalName + this@CourseSettingsFragment.course.globalName = course.globalName setResult() } override fun updateCourseHomePage(newHomePage: Course.HomePage?) { binding.editCourseHomepage.courseHomePage.text = mHomePages[newHomePage?.apiString] - mCourse.homePage = newHomePage + course.homePage = newHomePage setResult() } private fun setResult() { - requireActivity().setResult(Activity.RESULT_OK, Intent().apply { putExtra(Const.COURSE, mCourse as Parcelable) }) + requireActivity().setResult(Activity.RESULT_OK, Intent().apply { putExtra(Const.COURSE, course as Parcelable) }) } override fun onRefreshFinished() {} @@ -133,7 +140,7 @@ class CourseSettingsFragment : BasePresenterFragment< companion object { fun newInstance(course: Course) = CourseSettingsFragment().apply { - mCourse = course + this.course = course } } } diff --git a/apps/teacher/src/main/java/com/instructure/teacher/fragments/CreateDiscussionFragment.kt b/apps/teacher/src/main/java/com/instructure/teacher/fragments/CreateDiscussionFragment.kt index f9bfa7c447..a2199cf8e4 100644 --- a/apps/teacher/src/main/java/com/instructure/teacher/fragments/CreateDiscussionFragment.kt +++ b/apps/teacher/src/main/java/com/instructure/teacher/fragments/CreateDiscussionFragment.kt @@ -37,6 +37,7 @@ import com.instructure.canvasapi2.models.postmodels.DiscussionTopicPostBody import com.instructure.canvasapi2.models.postmodels.FileSubmitObject import com.instructure.canvasapi2.utils.NumberHelper import com.instructure.canvasapi2.utils.Pronouns +import com.instructure.canvasapi2.utils.pageview.PageView import com.instructure.canvasapi2.utils.toApiString import com.instructure.interactions.Identity import com.instructure.interactions.router.Route @@ -72,6 +73,7 @@ import org.greenrobot.eventbus.Subscribe import org.greenrobot.eventbus.ThreadMode import java.util.* +@PageView("courses/{canvasContext}/discussion_topics/new") @ScreenView(SCREEN_VIEW_CREATE_DISCUSSION) class CreateDiscussionFragment : BasePresenterFragment< CreateDiscussionPresenter, @@ -79,21 +81,21 @@ class CreateDiscussionFragment : BasePresenterFragment< private val binding by viewBinding(FragmentCreateDiscussionBinding::bind) - private var mCanvasContext: CanvasContext by ParcelableArg(Course(), CANVAS_CONTEXT) - private var mDiscussionTopicHeader: DiscussionTopicHeader? by NullableParcelableArg(null, DISCUSSION_TOPIC_HEADER) + private var canvasContext: CanvasContext by ParcelableArg(Course(), CANVAS_CONTEXT) + private var discussionTopicHeader: DiscussionTopicHeader? by NullableParcelableArg(null, DISCUSSION_TOPIC_HEADER) private val sendButton: TextView? get() = view?.findViewById(R.id.menuSaveDiscussion) private val saveButton: TextView? get() = view?.findViewById(R.id.menuSave) - private val mAttachmentButton get() = binding.toolbar.menu.findItem(R.id.menuAddAttachment) - private var mIsPublished: Boolean by BooleanArg(false) - private var mIsSubscribed: Boolean by BooleanArg(true) - private var mAllowThreaded: Boolean by BooleanArg(false) - private var mUsersMustPost: Boolean by BooleanArg(false) - private var mHasLoadedDataForEdit by BooleanArg() - private var mDisplayGradeAs: String? by NullableStringArg() - private var mDescription by NullableStringArg() - - private var mScrollToDates: Boolean by BooleanArg(false, SHOULD_SCROLL_TO_DATES) - private var mRCEHasFocus = false + private val attachmentButton get() = binding.toolbar.menu.findItem(R.id.menuAddAttachment) + private var isPublished: Boolean by BooleanArg(false) + private var isSubscribed: Boolean by BooleanArg(true) + private var allowThreaded: Boolean by BooleanArg(false) + private var usersMustPost: Boolean by BooleanArg(false) + private var hasLoadedDataForEdit by BooleanArg() + private var displayGradeAs: String? by NullableStringArg() + private var description by NullableStringArg() + + private var scrollToDates: Boolean by BooleanArg(false, SHOULD_SCROLL_TO_DATES) + private var rceHasFocus = false private var placeHolderList: ArrayList = ArrayList() @@ -102,7 +104,7 @@ class CreateDiscussionFragment : BasePresenterFragment< // We maintain a copy of the groupedDueDates to manipulate and use to display // overrides. When pushing changes, we update the original assignment object // with the changes in the copy. - private var mEditDateGroups: EditDateGroups = arrayListOf() + private var editDateGroups: EditDateGroups = arrayListOf() private val groupsMapped = hashMapOf() private val sectionsMapped = hashMapOf() private val studentsMapped = hashMapOf() @@ -110,9 +112,9 @@ class CreateDiscussionFragment : BasePresenterFragment< // Keeps track of which override we were editing so we can scroll back to it when the user returns from editing assignees private var scrollBackToOverride: AssignmentOverrideView? = null - private var mScrollHandler: Handler = Handler() + private var scrollHandler: Handler = Handler() - private var mScrollToRunnable: Runnable = Runnable { + private var scrollToRunnable: Runnable = Runnable { if(isAdded) binding.scrollView.fullScroll(ScrollView.FOCUS_DOWN) } @@ -133,7 +135,7 @@ class CreateDiscussionFragment : BasePresenterFragment< private val removeOverrideClick: (DueDateGroup) -> Unit = { callback -> // Show confirmation dialog ConfirmRemoveAssignmentOverrideDialog.show(requireActivity().supportFragmentManager) { - if (mEditDateGroups.contains(callback)) mEditDateGroups.remove(callback) + if (editDateGroups.contains(callback)) editDateGroups.remove(callback) setupOverrides() } } @@ -150,7 +152,7 @@ class CreateDiscussionFragment : BasePresenterFragment< override fun onDestroy() { super.onDestroy() - mScrollHandler.removeCallbacks(mScrollToRunnable) + scrollHandler.removeCallbacks(scrollToRunnable) } override fun onRefreshFinished() { } @@ -164,8 +166,8 @@ class CreateDiscussionFragment : BasePresenterFragment< super.onViewCreated(view, savedInstanceState) savedInstanceState?.let { @Suppress("UNCHECKED_CAST") - mEditDateGroups = (savedInstanceState.getSerializable(EDIT_DATE_GROUPS) as ArrayList) - mRCEHasFocus = savedInstanceState.getBoolean(RCE_HAS_FOCUS) + editDateGroups = (savedInstanceState.getSerializable(EDIT_DATE_GROUPS) as ArrayList) + rceHasFocus = savedInstanceState.getBoolean(RCE_HAS_FOCUS) } } @@ -186,21 +188,21 @@ class CreateDiscussionFragment : BasePresenterFragment< override fun onReadySetGo(presenter: CreateDiscussionPresenter) { // If we already have something in the edit date groups we already have the full assignment and don't need to get it again. - mDiscussionTopicHeader?.assignment?.let { + discussionTopicHeader?.assignment?.let { // Get the full assignment with overrides - if (mEditDateGroups.size == 0) presenter.getFullAssignment(it.id) + if (editDateGroups.size == 0) presenter.getFullAssignment(it.id) } setupToolbar() setupViews() - if(mRCEHasFocus) { + if(rceHasFocus) { binding.descriptionRCEView.requestEditorFocus() activity?.window?.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE) } } - override fun getPresenterFactory() = CreateDiscussionPresenterFactory(mCanvasContext, mDiscussionTopicHeader?.assignment) + override fun getPresenterFactory() = CreateDiscussionPresenterFactory(canvasContext, discussionTopicHeader?.assignment) override fun onPresenterPrepared(presenter: CreateDiscussionPresenter) { } @@ -213,14 +215,14 @@ class CreateDiscussionFragment : BasePresenterFragment< setupOverrides() - if (mScrollToDates) { - mScrollToDates = false + if (scrollToDates) { + scrollToDates = false // We came from the Dates page, scroll to the dates for editing - mScrollHandler.postDelayed(mScrollToRunnable, 300) + scrollHandler.postDelayed(scrollToRunnable, 300) } scrollBackToOverride?.let { - if (!mScrollToDates) + if (!scrollToDates) binding.scrollView.post { binding.scrollView.fullScroll(ScrollView.FOCUS_DOWN) } @@ -233,16 +235,16 @@ class CreateDiscussionFragment : BasePresenterFragment< } override fun updatedAssignment() { - mEditDateGroups.clear() + editDateGroups.clear() setupViews() } fun setupToolbar() = with(binding) { toolbar.setupCloseButton { - if(mDiscussionTopicHeader == null) { + if(discussionTopicHeader == null) { activity?.onBackPressed() } else { - if (mDiscussionTopicHeader?.message == descriptionRCEView.html) { + if (discussionTopicHeader?.message == descriptionRCEView.html) { activity?.onBackPressed() } else { UnsavedChangesExitDialog.show(requireFragmentManager()) { @@ -252,11 +254,11 @@ class CreateDiscussionFragment : BasePresenterFragment< } } - toolbar.title = if(mDiscussionTopicHeader == null) getString(R.string.createDiscussion) else getString(R.string.editDiscussion) - toolbar.setupMenu(if (mDiscussionTopicHeader == null) R.menu.create_discussion else R.menu.menu_save_generic) { menuItem -> + toolbar.title = if(discussionTopicHeader == null) getString(R.string.createDiscussion) else getString(R.string.editDiscussion) + toolbar.setupMenu(if (discussionTopicHeader == null) R.menu.create_discussion else R.menu.menu_save_generic) { menuItem -> when (menuItem.itemId) { R.id.menuSaveDiscussion, R.id.menuSave -> withRequireNetwork { saveDiscussion() } - R.id.menuAddAttachment -> if (mDiscussionTopicHeader == null) addAttachment() + R.id.menuAddAttachment -> if (discussionTopicHeader == null) addAttachment() } } @@ -272,15 +274,15 @@ class CreateDiscussionFragment : BasePresenterFragment< it.typeface = Typeface.create("sans-serif-medium", Typeface.NORMAL) } - if(CanvasWebView.containsLTI(mDescription ?: mDiscussionTopicHeader?.message ?: "", "UTF-8")) { - descriptionRCEView.setHtml(DiscussionUtils.createLTIPlaceHolders(requireContext(), mDescription ?: mDiscussionTopicHeader?.message ?: "") { _, placeholder -> + if(CanvasWebView.containsLTI(description ?: discussionTopicHeader?.message ?: "", "UTF-8")) { + descriptionRCEView.setHtml(DiscussionUtils.createLTIPlaceHolders(requireContext(), description ?: discussionTopicHeader?.message ?: "") { _, placeholder -> placeHolderList.add(placeholder) }, getString(R.string.discussion_details), getString(R.string.rce_empty_description), ThemePrefs.brandColor, ThemePrefs.textButtonColor) } else { - descriptionRCEView.setHtml(mDescription ?: mDiscussionTopicHeader?.message, + descriptionRCEView.setHtml(description ?: discussionTopicHeader?.message, getString(R.string.discussion_details), getString(R.string.rce_empty_description), ThemePrefs.brandColor, ThemePrefs.textButtonColor) @@ -289,14 +291,14 @@ class CreateDiscussionFragment : BasePresenterFragment< // When the RCE editor has focus we want the label to be darker so it matches the title's functionality descriptionRCEView.setLabel(discussionDescLabel, R.color.textDarkest, R.color.textDark) - if (!mHasLoadedDataForEdit) - mDiscussionTopicHeader?.let { + if (!hasLoadedDataForEdit) + discussionTopicHeader?.let { editDiscussionName.setText(it.title) - mIsPublished = it.published - mAllowThreaded = it.type == DiscussionTopicHeader.DiscussionType.THREADED - mUsersMustPost = it.requireInitialPost - mIsSubscribed = it.subscribed - mHasLoadedDataForEdit = true + isPublished = it.published + allowThreaded = it.type == DiscussionTopicHeader.DiscussionType.THREADED + usersMustPost = it.requireInitialPost + isSubscribed = it.subscribed + hasLoadedDataForEdit = true } ViewStyler.themeEditText(requireContext(), editDiscussionName, ThemePrefs.brandColor) @@ -309,19 +311,19 @@ class CreateDiscussionFragment : BasePresenterFragment< updateAttachmentUI() if(presenter.getAssignment() == null) { - if(mEditDateGroups.isEmpty()) { + if(editDateGroups.isEmpty()) { // If the dateGroups is empty, we want to add a due date so that we can set the available from and to fields - mEditDateGroups.clear() + editDateGroups.clear() val dueDateGroup = DueDateGroup() - if(mDiscussionTopicHeader != null) { + if(discussionTopicHeader != null) { // Populate the availability dates if we have them, the assignment is null, so this is an ungraded assignment - dueDateGroup.coreDates.lockDate = (mDiscussionTopicHeader as DiscussionTopicHeader).lockAt - dueDateGroup.coreDates.unlockDate = (mDiscussionTopicHeader as DiscussionTopicHeader).delayedPostDate + dueDateGroup.coreDates.lockDate = (discussionTopicHeader as DiscussionTopicHeader).lockAt + dueDateGroup.coreDates.unlockDate = (discussionTopicHeader as DiscussionTopicHeader).delayedPostDate } - mEditDateGroups.add(dueDateGroup) + editDateGroups.add(dueDateGroup) } //Make the graded things gone, we can't create a graded discussion @@ -333,20 +335,20 @@ class CreateDiscussionFragment : BasePresenterFragment< val pointsPossible = (presenter.getAssignment() as Assignment).pointsPossible editGradePoints.setText(NumberHelper.formatDecimal(pointsPossible, 2, true)) - if(mDisplayGradeAs == null) { - mDisplayGradeAs = (presenter.getAssignment() as Assignment).gradingType + if(displayGradeAs == null) { + displayGradeAs = (presenter.getAssignment() as Assignment).gradingType } setupDisplayGradeAs() - if (mEditDateGroups.isEmpty()) mEditDateGroups.addAll((presenter.getAssignment() as Assignment).groupedDueDates) + if (editDateGroups.isEmpty()) editDateGroups.addAll((presenter.getAssignment() as Assignment).groupedDueDates) if (groupsMapped.isEmpty() && sectionsMapped.isEmpty() && studentsMapped.isEmpty()) { presenter.getDueDateInfo((presenter.getAssignment() as Assignment).groupCategoryId) } addOverride.setOnClickListener { - mEditDateGroups.add(DueDateGroup()) + editDateGroups.add(DueDateGroup()) setupOverrides() scrollView.post { scrollView.fullScroll(ScrollView.FOCUS_DOWN) @@ -370,39 +372,39 @@ class CreateDiscussionFragment : BasePresenterFragment< // If a student has submitted something, we can't let the teacher unpublish the discussion if (presenter.getAssignment()?.unpublishable == true) { binding.publishWrapper.setGone() - mIsPublished = true + isPublished = true return } // Publish status with(binding.publishSwitch) { applyTheme() - isChecked = mIsPublished - setOnCheckedChangeListener { _, isChecked -> mIsPublished = isChecked } + isChecked = isPublished + setOnCheckedChangeListener { _, isChecked -> isPublished = isChecked } } } private fun setupSubscribeSwitch() { with(binding.subscribeSwitch) { applyTheme() - isChecked = mIsSubscribed - setOnCheckedChangeListener { _, isChecked -> mIsSubscribed = isChecked } + isChecked = isSubscribed + setOnCheckedChangeListener { _, isChecked -> isSubscribed = isChecked } } } private fun setupAllowThreadedSwitch() { with (binding.threadedSwitch) { applyTheme() - isChecked = mAllowThreaded - setOnCheckedChangeListener { _, isChecked -> mAllowThreaded = isChecked } + isChecked = allowThreaded + setOnCheckedChangeListener { _, isChecked -> allowThreaded = isChecked } } } private fun setupUsersMustPostSwitch() { with(binding.usersMustPostSwitch) { applyTheme() - isChecked = mUsersMustPost - setOnCheckedChangeListener { _, isChecked -> mUsersMustPost = isChecked } + isChecked = usersMustPost + setOnCheckedChangeListener { _, isChecked -> usersMustPost = isChecked } } } @@ -411,13 +413,13 @@ class CreateDiscussionFragment : BasePresenterFragment< if(presenter.getAssignment() == null) { // Load in overrides - mEditDateGroups.forEachIndexed { index, dueDateGroup -> + editDateGroups.forEachIndexed { index, dueDateGroup -> val assignees = ArrayList() val v = AssignmentOverrideView(requireActivity()) v.toAndFromDatesOnly() - v.setupOverride(index, dueDateGroup, mEditDateGroups.size > 1, assignees, datePickerOnClick, timePickerOnClick, { - if (mEditDateGroups.contains(it)) mEditDateGroups.remove(it) + v.setupOverride(index, dueDateGroup, editDateGroups.size > 1, assignees, datePickerOnClick, timePickerOnClick, { + if (editDateGroups.contains(it)) editDateGroups.remove(it) setupOverrides() }) { } @@ -426,11 +428,11 @@ class CreateDiscussionFragment : BasePresenterFragment< } else { // Load in overrides if(groupsMapped.isNotEmpty() || sectionsMapped.isNotEmpty() || studentsMapped.isNotEmpty()) { - mEditDateGroups.forEachIndexed { index, dueDateGroup -> + editDateGroups.forEachIndexed { index, dueDateGroup -> val assignees = ArrayList() val v = AssignmentOverrideView(requireActivity()) if (dueDateGroup.isEveryone) { - assignees += getString(if (mEditDateGroups.any { it.hasOverrideAssignees }) R.string.everyone_else else R.string.everyone) + assignees += getString(if (editDateGroups.any { it.hasOverrideAssignees }) R.string.everyone_else else R.string.everyone) } dueDateGroup.groupIds.forEach { assignees.add(groupsMapped[it]?.name!!) } @@ -439,14 +441,14 @@ class CreateDiscussionFragment : BasePresenterFragment< assignees.add(studentsMapped[it]!!.let { user -> Pronouns.span(user.name, user.pronouns) }) } - v.setupOverride(index, dueDateGroup, mEditDateGroups.size > 1, assignees, datePickerOnClick, timePickerOnClick, removeOverrideClick) { + v.setupOverride(index, dueDateGroup, editDateGroups.size > 1, assignees, datePickerOnClick, timePickerOnClick, removeOverrideClick) { val args = AssigneeListFragment.makeBundle( - mEditDateGroups, + editDateGroups, index, sectionsMapped.values.toList(), groupsMapped.values.toList(), studentsMapped.values.toList()) - RouteMatcher.route(requireContext(), Route(AssigneeListFragment::class.java, mCanvasContext, args)) + RouteMatcher.route(requireContext(), Route(AssigneeListFragment::class.java, canvasContext, args)) scrollBackToOverride = v } @@ -468,7 +470,7 @@ class CreateDiscussionFragment : BasePresenterFragment< ViewStyler.themeSpinner(requireContext(), displayGradeAsSpinner, ThemePrefs.brandColor) displayGradeAsSpinner.onItemSelectedListener = null - when(mDisplayGradeAs) { + when(displayGradeAs) { Assignment.POINTS_TYPE -> displayGradeAsSpinner.setSelection(spinnerAdapter.getPosition(getString(R.string.points))) Assignment.GPA_SCALE_TYPE -> displayGradeAsSpinner.setSelection(spinnerAdapter.getPosition(getString(R.string.gpa_scale))) Assignment.LETTER_GRADE_TYPE -> displayGradeAsSpinner.setSelection(spinnerAdapter.getPosition(getString(R.string.letter_grade))) @@ -480,11 +482,11 @@ class CreateDiscussionFragment : BasePresenterFragment< override fun onItemSelected(parent: AdapterView<*>, view: View?, position: Int, id: Long) { view ?: return when((view as TextView).text.toString()) { - getString(R.string.points) -> mDisplayGradeAs = Assignment.POINTS_TYPE - getString(R.string.gpa_scale) -> mDisplayGradeAs = Assignment.GPA_SCALE_TYPE - getString(R.string.letter_grade) -> mDisplayGradeAs = Assignment.LETTER_GRADE_TYPE - getString(R.string.complete_incomplete) -> mDisplayGradeAs = Assignment.PASS_FAIL_TYPE - getString(R.string.percentage) -> mDisplayGradeAs = Assignment.PERCENT_TYPE + getString(R.string.points) -> displayGradeAs = Assignment.POINTS_TYPE + getString(R.string.gpa_scale) -> displayGradeAs = Assignment.GPA_SCALE_TYPE + getString(R.string.letter_grade) -> displayGradeAs = Assignment.LETTER_GRADE_TYPE + getString(R.string.complete_incomplete) -> displayGradeAs = Assignment.PASS_FAIL_TYPE + getString(R.string.percentage) -> displayGradeAs = Assignment.PERCENT_TYPE } } @@ -493,14 +495,14 @@ class CreateDiscussionFragment : BasePresenterFragment< } private fun setupDelete() { - binding.deleteWrapper.setVisible(mDiscussionTopicHeader != null) + binding.deleteWrapper.setVisible(discussionTopicHeader != null) binding.deleteWrapper.onClickWithRequireNetwork { AlertDialog.Builder(requireContext()) .setTitle(R.string.discussions_delete_title) .setMessage(R.string.discussions_delete_message) .setPositiveButton(R.string.delete) { _, _ -> - if(mDiscussionTopicHeader != null) { - presenter.deleteDiscussionTopicHeader(mDiscussionTopicHeader!!.id) + if(discussionTopicHeader != null) { + presenter.deleteDiscussionTopicHeader(discussionTopicHeader!!.id) } } .setNegativeButton(R.string.cancel) { _, _ -> } @@ -526,12 +528,12 @@ class CreateDiscussionFragment : BasePresenterFragment< } // Show existing attachment (if any) - mDiscussionTopicHeader?.attachments?.firstOrNull()?.let { + discussionTopicHeader?.attachments?.firstOrNull()?.let { val attachmentView = AttachmentView(requireContext()) attachmentView.setPendingRemoteFile(it, true) { action, attachment -> if (action == AttachmentView.AttachmentAction.REMOVE) { presenter.attachmentRemoved = true - mDiscussionTopicHeader?.attachments?.remove(attachment) + discussionTopicHeader?.attachments?.remove(attachment) } } @@ -541,12 +543,12 @@ class CreateDiscussionFragment : BasePresenterFragment< private fun updateAttachmentButton(show: Boolean = true) { // Only show if (1) we're in creation mode and (2) we don't already have an attachment - mAttachmentButton?.isVisible = show && mDiscussionTopicHeader == null && presenter.attachment == null + attachmentButton?.isVisible = show && discussionTopicHeader == null && presenter.attachment == null } private fun addAttachment() { // set the description here. When we ask for permission to use the camera the app can call readySetGo and reset the description - mDescription = binding.descriptionRCEView.html + description = binding.descriptionRCEView.html val bundle = FileUploadDialogFragment.createDiscussionsBundle(ArrayList()) FileUploadDialogFragment.newInstance(bundle).show(childFragmentManager, FileUploadDialogFragment.TAG) @@ -585,7 +587,7 @@ class CreateDiscussionFragment : BasePresenterFragment< } private fun saveDiscussion() = with(binding) { - if(mDiscussionTopicHeader != null) { + if(discussionTopicHeader != null) { val postData = DiscussionTopicPostBody() // Discussion title isn't required @@ -595,27 +597,27 @@ class CreateDiscussionFragment : BasePresenterFragment< postData.title = editDiscussionName.text?.toString() ?: getString(R.string.no_title) } postData.message = handleLTIPlaceHolders(placeHolderList, descriptionRCEView.html) - postData.published = mIsPublished - postData.discussionType = if (mAllowThreaded) { + postData.published = isPublished + postData.discussionType = if (allowThreaded) { DiscussionTopicHeader.DiscussionType.THREADED.toString().lowercase(Locale.getDefault()) } else { DiscussionTopicHeader.DiscussionType.SIDE_COMMENT.toString().lowercase(Locale.getDefault()) } - postData.requireInitialPost = mUsersMustPost + postData.requireInitialPost = usersMustPost if (presenter.getAssignment() == null) { - postData.delayedPostAt = mEditDateGroups[0].coreDates.unlockDate.toApiString() - postData.lockAt = mEditDateGroups[0].coreDates.lockDate + postData.delayedPostAt = editDateGroups[0].coreDates.unlockDate.toApiString() + postData.lockAt = editDateGroups[0].coreDates.lockDate } else { val assignmentPostData = AssignmentPostBody() - assignmentPostData.gradingType = mDisplayGradeAs - assignmentPostData.setGroupedDueDates(mEditDateGroups) + assignmentPostData.gradingType = displayGradeAs + assignmentPostData.setGroupedDueDates(editDateGroups) assignmentPostData.pointsPossible = editGradePoints.text.toString().toDouble() postData.assignment = assignmentPostData } - presenter.editDiscussion((mDiscussionTopicHeader as DiscussionTopicHeader).id, postData) + presenter.editDiscussion((discussionTopicHeader as DiscussionTopicHeader).id, postData) } else { val discussionTopicHeader = DiscussionTopicHeader() @@ -626,15 +628,15 @@ class CreateDiscussionFragment : BasePresenterFragment< } discussionTopicHeader.message = descriptionRCEView.html - discussionTopicHeader.published = mIsPublished - discussionTopicHeader.subscribed = mIsSubscribed - discussionTopicHeader.type = if (mAllowThreaded) DiscussionTopicHeader.DiscussionType.THREADED else DiscussionTopicHeader.DiscussionType.SIDE_COMMENT - discussionTopicHeader.requireInitialPost = mUsersMustPost + discussionTopicHeader.published = isPublished + discussionTopicHeader.subscribed = isSubscribed + discussionTopicHeader.type = if (allowThreaded) DiscussionTopicHeader.DiscussionType.THREADED else DiscussionTopicHeader.DiscussionType.SIDE_COMMENT + discussionTopicHeader.requireInitialPost = usersMustPost // If the assignment is null, that means we're creating/editing a discussion. When we do this we initialize mEditDateGroups with an empty DueDateGroup if (presenter.getAssignment() == null) { - discussionTopicHeader.delayedPostDate = mEditDateGroups[0].coreDates.unlockDate - discussionTopicHeader.lockAt = mEditDateGroups[0].coreDates.lockDate + discussionTopicHeader.delayedPostDate = editDateGroups[0].coreDates.unlockDate + discussionTopicHeader.lockAt = editDateGroups[0].coreDates.lockDate } presenter.saveDiscussion(discussionTopicHeader) } @@ -645,9 +647,9 @@ class CreateDiscussionFragment : BasePresenterFragment< } override fun onSaveInstanceState(outState: Bundle) { - outState.putSerializable(EDIT_DATE_GROUPS, ArrayList(mEditDateGroups)) + outState.putSerializable(EDIT_DATE_GROUPS, ArrayList(editDateGroups)) outState.putBoolean(RCE_HAS_FOCUS, binding.descriptionRCEView.hasFocus()) - mDescription = binding.descriptionRCEView.html + description = binding.descriptionRCEView.html super.onSaveInstanceState(outState) } @@ -656,7 +658,7 @@ class CreateDiscussionFragment : BasePresenterFragment< fun onAssigneesChanged(event: AssigneesUpdatedEvent) { // Update grouped due dates (EditDateGroups) event.once(javaClass.simpleName) { dates -> - mEditDateGroups = dates + editDateGroups = dates setupOverrides() // Remove it so when we go to another assignment or discussion it won't show up there too EventBus.getDefault().removeStickyEvent(event) diff --git a/apps/teacher/src/main/java/com/instructure/teacher/fragments/CreateOrEditAnnouncementFragment.kt b/apps/teacher/src/main/java/com/instructure/teacher/fragments/CreateOrEditAnnouncementFragment.kt index 202176d98f..0811ef0dc7 100644 --- a/apps/teacher/src/main/java/com/instructure/teacher/fragments/CreateOrEditAnnouncementFragment.kt +++ b/apps/teacher/src/main/java/com/instructure/teacher/fragments/CreateOrEditAnnouncementFragment.kt @@ -28,6 +28,7 @@ import com.instructure.canvasapi2.models.Course import com.instructure.canvasapi2.models.DiscussionTopicHeader import com.instructure.canvasapi2.models.postmodels.FileSubmitObject import com.instructure.canvasapi2.utils.DateHelper +import com.instructure.canvasapi2.utils.pageview.PageView import com.instructure.canvasapi2.utils.parcelCopy import com.instructure.interactions.Identity import com.instructure.pandautils.analytics.SCREEN_VIEW_CREATE_OR_EDIT_ANNOUNCEMENT @@ -58,6 +59,7 @@ import org.greenrobot.eventbus.Subscribe import org.greenrobot.eventbus.ThreadMode import java.util.* +@PageView("courses/{canvasContext}/discussion_topics/new?is_announcement=true") @ScreenView(SCREEN_VIEW_CREATE_OR_EDIT_ANNOUNCEMENT) class CreateOrEditAnnouncementFragment : BasePresenterFragment(), @@ -68,24 +70,24 @@ class CreateOrEditAnnouncementFragment : private val binding by viewBinding(FragmentCreateOrEditAnnouncementBinding::bind) /* The course this announcement belongs to */ - private var mCanvasContext by ParcelableArg(Course()) + private var canvasContext by ParcelableArg(Course()) /* The announcement to be edited. This will be null if we're creating a new announcement */ - private var mEditAnnouncement by NullableParcelableArg() + private var editAnnouncement by NullableParcelableArg() /* Menu buttons. We don't cache these because the toolbar is reconstructed on configuration change. */ - private val mSaveMenuButton get() = binding.toolbar.menu.findItem(R.id.menuSaveAnnouncement) - private val mAttachmentButton get() = binding.toolbar.menu.findItem(R.id.menuAddAttachment) - private val mSaveButtonTextView: TextView? get() = view?.findViewById(R.id.menuSaveAnnouncement) + private val saveMenuButton get() = binding.toolbar.menu.findItem(R.id.menuSaveAnnouncement) + private val attachmentButton get() = binding.toolbar.menu.findItem(R.id.menuAddAttachment) + private val saveButtonTextView: TextView? get() = view?.findViewById(R.id.menuSaveAnnouncement) /* Formats for displaying the delayed post date */ - private val mDateFormat by lazy { DateHelper.fullMonthNoLeadingZeroDateFormat } - private val mTimeFormat by lazy { DateHelper.getPreferredTimeFormat(requireContext()) } + private val dateFormat by lazy { DateHelper.fullMonthNoLeadingZeroDateFormat } + private val timeFormat by lazy { DateHelper.getPreferredTimeFormat(requireContext()) } private var placeHolderList: ArrayList = ArrayList() /* The default date to show when the user enables delayed posting (the current date just before midnight) */ - private val mDefaultDate: Date + private val defaultDate: Date get() = Calendar.getInstance().apply { set(Calendar.HOUR_OF_DAY, 23) set(Calendar.MINUTE, 59) @@ -101,7 +103,7 @@ class CreateOrEditAnnouncementFragment : override fun onPresenterPrepared(presenter: CreateOrEditAnnouncementPresenter) {} override fun layoutResId(): Int = R.layout.fragment_create_or_edit_announcement - override fun getPresenterFactory() = CreateOrEditAnnouncementPresenterFactory(mCanvasContext, mEditAnnouncement?.parcelCopy()) + override fun getPresenterFactory() = CreateOrEditAnnouncementPresenterFactory(canvasContext, editAnnouncement?.parcelCopy()) override fun onStart() { super.onStart() @@ -162,12 +164,12 @@ class CreateOrEditAnnouncementFragment : ViewStyler.themeToolbarLight(requireActivity(), toolbar) ViewStyler.setToolbarElevationSmall(requireContext(), toolbar) - if (presenter.isEditing) with(mSaveMenuButton) { + if (presenter.isEditing) with(saveMenuButton) { setIcon(0) setTitle(R.string.save) } - mSaveButtonTextView?.setTextColor(ThemePrefs.textButtonColor) + saveButtonTextView?.setTextColor(ThemePrefs.textButtonColor) } private fun setupViews() { @@ -238,7 +240,7 @@ class CreateOrEditAnnouncementFragment : updatePostDate() delaySwitch.setOnCheckedChangeListener { _, isChecked -> - presenter.announcement.delayedPostDate = if (isChecked) mDefaultDate else null + presenter.announcement.delayedPostDate = if (isChecked) defaultDate else null updatePostDate() } @@ -305,8 +307,8 @@ class CreateOrEditAnnouncementFragment : postDateWrapper.setGone() } else { postDateWrapper.setVisible() - postDate.setText(mDateFormat.format(date)) - postTime.setText(mTimeFormat.format(date)) + postDate.setText(dateFormat.format(date)) + postTime.setText(timeFormat.format(date)) } } @@ -364,7 +366,7 @@ class CreateOrEditAnnouncementFragment : } override fun onSaveStarted() { - mSaveMenuButton.isVisible = false + saveMenuButton.isVisible = false updateAttachmentButton(show = false) binding.savingProgressBar.announceForAccessibility(getString(R.string.saving)) binding.savingProgressBar.setVisible() @@ -372,18 +374,18 @@ class CreateOrEditAnnouncementFragment : private fun updateAttachmentButton(show: Boolean = true) { // Only show if (1) we're in creation mode and (2) we don't already have an attachment - mAttachmentButton?.isVisible = show && !presenter.isEditing && presenter.attachment == null + attachmentButton?.isVisible = show && !presenter.isEditing && presenter.attachment == null } override fun onSaveError() { - mSaveMenuButton.isVisible = true + saveMenuButton.isVisible = true updateAttachmentButton() binding.savingProgressBar.setGone() toast(R.string.errorSavingAnnouncement) } override fun onDeleteError() { - mSaveMenuButton.isVisible = true + saveMenuButton.isVisible = true updateAttachmentButton() binding.savingProgressBar.setGone() toast(R.string.errorDeletingAnnouncement) @@ -445,13 +447,13 @@ class CreateOrEditAnnouncementFragment : fun newInstanceCreate(canvasContext: CanvasContext) = CreateOrEditAnnouncementFragment().apply { - mCanvasContext = canvasContext + this.canvasContext = canvasContext } fun newInstanceEdit(canvasContext: CanvasContext, editAnnouncement: DiscussionTopicHeader) = CreateOrEditAnnouncementFragment().apply { - mCanvasContext = canvasContext - mEditAnnouncement = editAnnouncement + this.canvasContext = canvasContext + this.editAnnouncement = editAnnouncement } } } diff --git a/apps/teacher/src/main/java/com/instructure/teacher/fragments/CreateOrEditPageDetailsFragment.kt b/apps/teacher/src/main/java/com/instructure/teacher/fragments/CreateOrEditPageDetailsFragment.kt index 72f582b5b3..d922083ded 100644 --- a/apps/teacher/src/main/java/com/instructure/teacher/fragments/CreateOrEditPageDetailsFragment.kt +++ b/apps/teacher/src/main/java/com/instructure/teacher/fragments/CreateOrEditPageDetailsFragment.kt @@ -29,11 +29,14 @@ import androidx.appcompat.app.AlertDialog import com.instructure.canvasapi2.models.CanvasContext import com.instructure.canvasapi2.models.Course import com.instructure.canvasapi2.models.Page +import com.instructure.canvasapi2.utils.ApiPrefs import com.instructure.canvasapi2.models.Page.Companion.ANYONE import com.instructure.canvasapi2.models.Page.Companion.GROUP_MEMBERS import com.instructure.canvasapi2.models.Page.Companion.STUDENTS import com.instructure.canvasapi2.models.Page.Companion.TEACHERS import com.instructure.canvasapi2.utils.isValid +import com.instructure.canvasapi2.utils.pageview.PageView +import com.instructure.canvasapi2.utils.pageview.PageViewUrl import com.instructure.canvasapi2.utils.parcelCopy import com.instructure.interactions.Identity import com.instructure.pandautils.analytics.SCREEN_VIEW_CREATE_OR_EDIT_PAGE_DETAILS @@ -53,6 +56,7 @@ import com.instructure.teacher.utils.setupMenu import com.instructure.teacher.utils.withRequireNetwork import com.instructure.teacher.viewinterface.CreateOrEditPageView +@PageView @ScreenView(SCREEN_VIEW_CREATE_OR_EDIT_PAGE_DETAILS) class CreateOrEditPageDetailsFragment : BasePresenterFragment(), @@ -62,14 +66,14 @@ class CreateOrEditPageDetailsFragment : private val binding by viewBinding(FragmentCreateOrEditPageBinding::bind) /* The course this page belongs to */ - private var mCanvasContext by ParcelableArg(Course()) + private var canvasContext by ParcelableArg(Course()) /* The page to be edited. This will be null if we're creating a new page */ - private var mPage by NullableParcelableArg() + private var page by NullableParcelableArg() /* Menu buttons. We don't cache these because the toolbar is reconstructed on configuration change. */ - private val mSaveMenuButton get() = binding.toolbar.menu.findItem(R.id.menuSavePage) - private val mSaveButtonTextView: TextView? get() = view?.findViewById(R.id.menuSavePage) + private val saveMenuButton get() = binding.toolbar.menu.findItem(R.id.menuSavePage) + private val saveButtonTextView: TextView? get() = view?.findViewById(R.id.menuSavePage) private var placeHolderList: ArrayList = ArrayList() private var forceQuit = false @@ -82,7 +86,18 @@ class CreateOrEditPageDetailsFragment : override fun onPresenterPrepared(presenter: CreateOrEditPagePresenter) {} override fun layoutResId(): Int = R.layout.fragment_create_or_edit_page - override fun getPresenterFactory() = CreateOrEditPagePresenterFactory(mCanvasContext, mPage?.parcelCopy()) + @PageViewUrl + @Suppress("unused") + private fun makePageViewUrl(): String { + val url = StringBuilder(ApiPrefs.fullDomain) + page.let { + url.append(canvasContext.toAPIString()) + if (it?.frontPage == false) url.append("/pages/${it.url}/edit") + } + return url.toString() + } + + override fun getPresenterFactory() = CreateOrEditPagePresenterFactory(canvasContext, page?.parcelCopy()) override fun onReadySetGo(presenter: CreateOrEditPagePresenter) { setupViews() @@ -101,11 +116,11 @@ class CreateOrEditPageDetailsFragment : } ViewStyler.themeToolbarLight(requireActivity(), toolbar) ViewStyler.setToolbarElevationSmall(requireContext(), toolbar) - with(mSaveMenuButton) { + with(saveMenuButton) { setIcon(0) setTitle(R.string.save) } - mSaveButtonTextView?.setTextColor(ThemePrefs.textButtonColor) + saveButtonTextView?.setTextColor(ThemePrefs.textButtonColor) } private fun shouldAllowExit() : Boolean = with(binding) { @@ -116,12 +131,11 @@ class CreateOrEditPageDetailsFragment : return true } // Check if edited page has changes - if (presenter.page.id != 0L && - (presenter.page.body.orEmpty()) == pageRCEView.html && - (mPage?.title.orEmpty()) == pageNameEditText.text.toString() && - mPage?.frontPage == frontPageSwitch.isChecked && - mPage?.published == publishSwitch.isChecked - ) { + if(presenter.page.id != 0L && + presenter.page.body.orEmpty() == pageRCEView.html && + page?.title.orEmpty() == pageNameEditText.text.toString() && + page?.frontPage == frontPageSwitch.isChecked && + page?.published == publishSwitch.isChecked) { return true } return false @@ -196,7 +210,7 @@ class CreateOrEditPageDetailsFragment : } private fun setupCanEditSpinner() = with(binding) { - val spinnerAdapter = if(mCanvasContext.type == CanvasContext.Type.GROUP) { + val spinnerAdapter = if(canvasContext.type == CanvasContext.Type.GROUP) { ArrayAdapter.createFromResource(requireContext(), R.array.canEditRolesWithGroups, R.layout.simple_spinner_item) } else { ArrayAdapter.createFromResource(requireContext(), R.array.canEditRolesNoGroups, R.layout.simple_spinner_item) @@ -236,7 +250,7 @@ class CreateOrEditPageDetailsFragment : private fun setupPublishSwitch() = with(binding) { // If it's the front page we can't unpublish it - publishWrapper.setVisible(!(mPage != null && (mPage as Page).frontPage)) + publishWrapper.setVisible(!(page != null && (page as Page).frontPage)) // Publish status publishSwitch.applyTheme() @@ -246,14 +260,14 @@ class CreateOrEditPageDetailsFragment : } private fun setupDelete() { - binding.deleteWrapper.setVisible((mPage != null && !(mPage as Page).frontPage)) + binding.deleteWrapper.setVisible((page != null && !(page as Page).frontPage)) binding.deleteWrapper.onClickWithRequireNetwork { AlertDialog.Builder(requireContext()) .setTitle(R.string.pageDeleteTitle) .setMessage(R.string.pageDeleteMessage) .setPositiveButton(R.string.delete) { _, _ -> - if(mPage != null) { - presenter.deletePage(mPage!!.url!!) + if(page != null) { + presenter.deletePage(page!!.url!!) } } .setNegativeButton(R.string.cancel) { _, _ -> } @@ -285,13 +299,13 @@ class CreateOrEditPageDetailsFragment : } override fun onSaveStarted() { - mSaveMenuButton.isVisible = false + saveMenuButton.isVisible = false binding.savingProgressBar.announceForAccessibility(getString(R.string.saving)) binding.savingProgressBar.setVisible() } override fun onSaveError() { - mSaveMenuButton.isVisible = true + saveMenuButton.isVisible = true binding.savingProgressBar.setGone() toast(R.string.errorSavingPage) } @@ -329,13 +343,13 @@ class CreateOrEditPageDetailsFragment : } fun newInstanceCreate(canvasContext: CanvasContext) = CreateOrEditPageDetailsFragment().apply { - mCanvasContext = canvasContext + this.canvasContext = canvasContext } fun newInstanceEdit(canvasContext: CanvasContext, page: Page) = CreateOrEditPageDetailsFragment().apply { - mCanvasContext = canvasContext - mPage = page + this.canvasContext = canvasContext + this.page = page } } } diff --git a/apps/teacher/src/main/java/com/instructure/teacher/fragments/DashboardFragment.kt b/apps/teacher/src/main/java/com/instructure/teacher/fragments/DashboardFragment.kt index 1459d99763..2e14c25ac3 100644 --- a/apps/teacher/src/main/java/com/instructure/teacher/fragments/DashboardFragment.kt +++ b/apps/teacher/src/main/java/com/instructure/teacher/fragments/DashboardFragment.kt @@ -26,6 +26,7 @@ import androidx.recyclerview.widget.GridLayoutManager import androidx.recyclerview.widget.RecyclerView import com.instructure.canvasapi2.models.Course import com.instructure.canvasapi2.utils.ApiPrefs +import com.instructure.canvasapi2.utils.pageview.PageView import com.instructure.pandautils.analytics.SCREEN_VIEW_DASHBOARD import com.instructure.pandautils.analytics.ScreenView import com.instructure.pandautils.binding.viewBinding @@ -54,6 +55,7 @@ import org.greenrobot.eventbus.ThreadMode private const val LIST_SPAN_COUNT = 1 +@PageView @ScreenView(SCREEN_VIEW_DASHBOARD) class DashboardFragment : BaseSyncFragment(), CoursesView { diff --git a/apps/teacher/src/main/java/com/instructure/teacher/fragments/DiscussionsDetailsFragment.kt b/apps/teacher/src/main/java/com/instructure/teacher/fragments/DiscussionsDetailsFragment.kt index 85e7d1731c..3162f8ac87 100644 --- a/apps/teacher/src/main/java/com/instructure/teacher/fragments/DiscussionsDetailsFragment.kt +++ b/apps/teacher/src/main/java/com/instructure/teacher/fragments/DiscussionsDetailsFragment.kt @@ -28,6 +28,8 @@ import androidx.appcompat.app.AlertDialog import androidx.core.content.ContextCompat import com.instructure.canvasapi2.models.* import com.instructure.canvasapi2.utils.* +import com.instructure.canvasapi2.utils.pageview.PageView +import com.instructure.canvasapi2.utils.pageview.PageViewUrlParam import com.instructure.canvasapi2.utils.weave.WeaveJob import com.instructure.canvasapi2.utils.weave.catch import com.instructure.canvasapi2.utils.weave.tryWeave @@ -44,6 +46,7 @@ import com.instructure.pandautils.discussions.DiscussionEntryHtmlConverter import com.instructure.pandautils.discussions.DiscussionUtils import com.instructure.pandautils.fragments.BasePresenterFragment import com.instructure.pandautils.utils.* +import com.instructure.pandautils.utils.Const import com.instructure.pandautils.views.CanvasWebView import com.instructure.teacher.BuildConfig import com.instructure.teacher.R @@ -66,6 +69,7 @@ import org.greenrobot.eventbus.Subscribe import org.greenrobot.eventbus.ThreadMode import java.util.* +@PageView(url = "{canvasContext}/discussion_topics/{topicId}") @ScreenView(SCREEN_VIEW_DISCUSSION_DETAILS) class DiscussionsDetailsFragment : BasePresenterFragment< DiscussionsDetailsPresenter, @@ -74,17 +78,16 @@ class DiscussionsDetailsFragment : BasePresenterFragment< private val binding by viewBinding(FragmentDiscussionsDetailsBinding::bind) //region Member Variables + private var canvasContext: CanvasContext by ParcelableArg(key = Const.CANVAS_CONTEXT) + private var discussionTopicHeader: DiscussionTopicHeader by ParcelableArg(DiscussionTopicHeader(), DISCUSSION_TOPIC_HEADER) + private var discussionTopic: DiscussionTopic by ParcelableArg(DiscussionTopic(), DISCUSSION_TOPIC) + private var discussionEntryId: Long by LongArg(0L, DISCUSSION_ENTRY_ID) + private var discussionTopicHeaderId: Long by LongArg(0L, DISCUSSION_TOPIC_HEADER_ID) + private var skipIdentityCheck: Boolean by BooleanArg(false, SKIP_IDENTITY_CHECK) + private var skipId: String by StringArg("", SKIP_ID) - private var mCanvasContext: CanvasContext by ParcelableArg(Course()) - private var mDiscussionTopicHeader: DiscussionTopicHeader by ParcelableArg(DiscussionTopicHeader(), DISCUSSION_TOPIC_HEADER) - private var mDiscussionTopic: DiscussionTopic by ParcelableArg(DiscussionTopic(), DISCUSSION_TOPIC) - private var mDiscussionEntryId: Long by LongArg(0L, DISCUSSION_ENTRY_ID) - private var mDiscussionTopicHeaderId: Long by LongArg(0L, DISCUSSION_TOPIC_HEADER_ID) - private var mSkipIdentityCheck: Boolean by BooleanArg(false, SKIP_IDENTITY_CHECK) - private var mSkipId: String by StringArg("", SKIP_ID) - - private var mIsAnnouncements: Boolean by BooleanArg(false, IS_ANNOUNCEMENT) - private var mIsNestedDetail: Boolean by BooleanArg(false, IS_NESTED_DETAIL) + private var isAnnouncements: Boolean by BooleanArg(false, IS_ANNOUNCEMENT) + private var isNestedDetail: Boolean by BooleanArg(false, IS_NESTED_DETAIL) private var repliesLoadHtmlJob: Job? = null private var headerLoadHtmlJob: Job? = null @@ -92,6 +95,10 @@ class DiscussionsDetailsFragment : BasePresenterFragment< //endregion + @Suppress("unused") + @PageViewUrlParam("topicId") + private fun getTopicId() = discussionTopicHeader.id + override fun layoutResId(): Int = R.layout.fragment_discussions_details override fun onRefreshFinished() { @@ -119,12 +126,12 @@ class DiscussionsDetailsFragment : BasePresenterFragment< headerLoadHtmlJob?.cancel() } - override val identity: Long? get() = if(mDiscussionTopicHeaderId != 0L) mDiscussionTopicHeaderId else mDiscussionTopicHeader.id - override val skipCheck: Boolean get() = mSkipIdentityCheck + override val identity: Long? get() = if(discussionTopicHeaderId != 0L) discussionTopicHeaderId else discussionTopicHeader.id + override val skipCheck: Boolean get() = skipIdentityCheck override fun getPresenterFactory() = - DiscussionsDetailsPresenterFactory(mCanvasContext, mDiscussionTopicHeader, mDiscussionTopic, - if(mSkipId.isEmpty()) DiscussionsDetailsFragment::class.java.simpleName + UUID.randomUUID().toString() else mSkipId) + DiscussionsDetailsPresenterFactory(canvasContext, discussionTopicHeader, discussionTopic, + if(skipId.isEmpty()) DiscussionsDetailsFragment::class.java.simpleName + UUID.randomUUID().toString() else skipId) override fun onPresenterPrepared(presenter: DiscussionsDetailsPresenter) {} @@ -140,24 +147,24 @@ class DiscussionsDetailsFragment : BasePresenterFragment< discussionTopicEvent.only(presenter.getSkipId()) { discussionTopic -> //A The Discussion Topic was changed in some way. Usually from a nested situation where something was added. presenter.updateDiscussionTopic(discussionTopic) - if (!mIsNestedDetail) { + if (!isNestedDetail) { EventBus.getDefault().removeStickyEvent(discussionTopicEvent) } } } else { - if (mDiscussionTopicHeaderId == 0L && presenter.discussionTopicHeader.id != 0L) { + if (discussionTopicHeaderId == 0L && presenter.discussionTopicHeader.id != 0L) { //We were given a valid DiscussionTopicHeader, no need to fetch from the API populateDiscussionTopicHeader(presenter.discussionTopicHeader, false) - } else if (mDiscussionTopicHeaderId != 0L) { + } else if (discussionTopicHeaderId != 0L) { //results of this GET will call populateDiscussionTopicHeader() - presenter.getDiscussionTopicHeader(mDiscussionTopicHeaderId) + presenter.getDiscussionTopicHeader(discussionTopicHeaderId) } } EventBus.getDefault().getStickyEvent(DiscussionTopicHeaderDeletedEvent::class.java)?.once(javaClass.simpleName + ".onResume()") { if (it == presenter.discussionTopicHeader.id) { if (activity is MasterDetailInteractions) { - (activity as MasterDetailInteractions).popFragment(mCanvasContext) + (activity as MasterDetailInteractions).popFragment(canvasContext) } else if(activity is FullScreenInteractions) { requireActivity().finish() } @@ -181,7 +188,7 @@ class DiscussionsDetailsFragment : BasePresenterFragment< } // Publish status if discussion - if(!mIsAnnouncements) { + if(!isAnnouncements) { if (discussionTopicHeader.published) { publishStatusIconView.setImageResource(R.drawable.ic_complete_solid) publishStatusIconView.setColorFilter(requireContext().getColorCompat(R.color.textSuccess)) @@ -208,7 +215,7 @@ class DiscussionsDetailsFragment : BasePresenterFragment< } loadDiscussionTopicHeader(discussionTopicHeader) - repliesBack.setVisible(mIsNestedDetail) + repliesBack.setVisible(isNestedDetail) repliesBack.onClick { requireActivity().onBackPressed() } attachmentIcon.setVisible(!discussionTopicHeader.attachments.isEmpty()) attachmentIcon.onClick { @@ -245,10 +252,10 @@ class DiscussionsDetailsFragment : BasePresenterFragment< DiscussionUtils.createDiscussionTopicHtml( requireActivity(), isTablet, - mCanvasContext, + canvasContext, discussionTopicHeader, discussionTopic.views, - mDiscussionEntryId) + discussionEntryId) } discussionRepliesWebViewWrapper.setInvisible() @@ -285,7 +292,7 @@ class DiscussionsDetailsFragment : BasePresenterFragment< NumberHelper.formatDecimal(assignment.pointsPossible, 1, true)) dueLayout.setVisible() - submissionsLayout.setVisible((mCanvasContext as? Course)?.isDesigner() == false) + submissionsLayout.setVisible((canvasContext as? Course)?.isDesigner() == false) //set these as gone and make them visible if we have data for them availabilityLayout.setGone() @@ -367,20 +374,20 @@ class DiscussionsDetailsFragment : BasePresenterFragment< private fun setupListeners() = with(binding) { dueLayout.setOnClickListener { val args = DueDatesFragment.makeBundle(presenter.discussionTopicHeader.assignment!!) - RouteMatcher.route(requireContext(), Route(null, DueDatesFragment::class.java, mCanvasContext, args)) + RouteMatcher.route(requireContext(), Route(null, DueDatesFragment::class.java, canvasContext, args)) } submissionsLayout.setOnClickListener { - navigateToSubmissions(mCanvasContext, presenter.discussionTopicHeader.assignment!!, AssignmentSubmissionListPresenter.SubmissionListFilter.ALL) + navigateToSubmissions(canvasContext, presenter.discussionTopicHeader.assignment!!, AssignmentSubmissionListPresenter.SubmissionListFilter.ALL) } binding.donutGroup.viewAllSubmissions.onClick { submissionsLayout.performClick() } // Separate click listener for a11y binding.donutGroup.gradedWrapper.setOnClickListener { - navigateToSubmissions(mCanvasContext, presenter.discussionTopicHeader.assignment!!, AssignmentSubmissionListPresenter.SubmissionListFilter.GRADED) + navigateToSubmissions(canvasContext, presenter.discussionTopicHeader.assignment!!, AssignmentSubmissionListPresenter.SubmissionListFilter.GRADED) } binding.donutGroup.ungradedWrapper.setOnClickListener { - navigateToSubmissions(mCanvasContext, presenter.discussionTopicHeader.assignment!!, AssignmentSubmissionListPresenter.SubmissionListFilter.NOT_GRADED) + navigateToSubmissions(canvasContext, presenter.discussionTopicHeader.assignment!!, AssignmentSubmissionListPresenter.SubmissionListFilter.NOT_GRADED) } binding.donutGroup.notSubmittedWrapper.setOnClickListener { - navigateToSubmissions(mCanvasContext, presenter.discussionTopicHeader.assignment!!, AssignmentSubmissionListPresenter.SubmissionListFilter.MISSING) + navigateToSubmissions(canvasContext, presenter.discussionTopicHeader.assignment!!, AssignmentSubmissionListPresenter.SubmissionListFilter.MISSING) } } @@ -394,7 +401,7 @@ class DiscussionsDetailsFragment : BasePresenterFragment< ProfileUtils.loadAvatarForUser(authorAvatar, displayName, discussionTopicHeader.author?.avatarImageUrl) authorAvatar.setupAvatarA11y(discussionTopicHeader.author?.displayName) authorAvatar.onClick { - val bundle = StudentContextFragment.makeBundle(discussionTopicHeader.author?.id ?: 0, mCanvasContext.id) + val bundle = StudentContextFragment.makeBundle(discussionTopicHeader.author?.id ?: 0, canvasContext.id) RouteMatcher.route(requireContext(), Route(StudentContextFragment::class.java, null, bundle)) } authorName?.text = discussionTopicHeader.author?.let { Pronouns.span(it.displayName, it.pronouns) } @@ -408,7 +415,7 @@ class DiscussionsDetailsFragment : BasePresenterFragment< } headerLoadHtmlJob = discussionTopicHeaderWebViewWrapper.webView.loadHtmlWithIframes(requireContext(), discussionTopicHeader.message, { - discussionTopicHeaderWebViewWrapper.loadHtml(it, discussionTopicHeader.title, baseUrl = mDiscussionTopicHeader.htmlUrl) + discussionTopicHeaderWebViewWrapper.loadHtml(it, discussionTopicHeader.title, baseUrl = this@DiscussionsDetailsFragment.discussionTopicHeader.htmlUrl) }) { LtiLaunchFragment.routeLtiLaunchFragment(requireContext(), canvasContext, it) } @@ -427,10 +434,10 @@ class DiscussionsDetailsFragment : BasePresenterFragment< super.onResume() setupToolbar() - if (isAccessibilityEnabled(requireContext()) && mDiscussionTopicHeader.htmlUrl != null) { + if (isAccessibilityEnabled(requireContext()) && discussionTopicHeader.htmlUrl != null) { alternateViewButton.visibility = View.VISIBLE alternateViewButton.setOnClickListener { - val bundle = InternalWebViewFragment.makeBundle(mDiscussionTopicHeader.htmlUrl!!, mDiscussionTopicHeader.title!!, shouldAuthenticate = true) + val bundle = InternalWebViewFragment.makeBundle(discussionTopicHeader.htmlUrl!!, discussionTopicHeader.title!!, shouldAuthenticate = true) RouteMatcher.route(requireActivity(), Route(null, InternalWebViewFragment::class.java, canvasContext, bundle)) } } @@ -460,29 +467,29 @@ class DiscussionsDetailsFragment : BasePresenterFragment< private fun setupToolbar() = with(binding) { toolbar.setupBackButtonWithExpandCollapseAndBack(this@DiscussionsDetailsFragment) { toolbar.updateToolbarExpandCollapseIcon(this@DiscussionsDetailsFragment) - ViewStyler.themeToolbarColored(requireActivity(), toolbar, mCanvasContext.backgroundColor, requireContext().getColor(R.color.white)) + ViewStyler.themeToolbarColored(requireActivity(), toolbar, canvasContext.backgroundColor, requireContext().getColor(R.color.white)) (activity as MasterDetailInteractions).toggleExpandCollapse() } toolbar.setupMenu(R.menu.menu_edit_generic, menuItemCallback) - toolbar.title = if(mIsAnnouncements) getString(R.string.announcementDetails) else getString(R.string.discussion_details) + toolbar.title = if(isAnnouncements) getString(R.string.announcementDetails) else getString(R.string.discussion_details) if(!isTablet) { - toolbar.subtitle = mCanvasContext.name + toolbar.subtitle = canvasContext.name } - ViewStyler.themeToolbarColored(requireActivity(), toolbar, mCanvasContext.backgroundColor, requireContext().getColor(R.color.white)) + ViewStyler.themeToolbarColored(requireActivity(), toolbar, canvasContext.backgroundColor, requireContext().getColor(R.color.white)) } private val menuItemCallback: (MenuItem) -> Unit = { item -> when (item.itemId) { R.id.menu_edit -> { if(APIHelper.hasNetworkConnection()) { - if(mIsAnnouncements) { + if(isAnnouncements) { val args = CreateOrEditAnnouncementFragment.newInstanceEdit(presenter.canvasContext, presenter.discussionTopicHeader).nonNullArgs RouteMatcher.route(requireContext(), Route(CreateOrEditAnnouncementFragment::class.java, null, args)) } else { // If we have an assignment, set the topic header to null to prevent cyclic reference presenter.discussionTopicHeader.assignment?.discussionTopicHeader = null val args = CreateDiscussionFragment.makeBundle(presenter.canvasContext, presenter.discussionTopicHeader) - RouteMatcher.route(requireContext(), Route(CreateDiscussionFragment::class.java, mCanvasContext, args)) + RouteMatcher.route(requireContext(), Route(CreateDiscussionFragment::class.java, canvasContext, args)) } } else { NoInternetConnectionDialog.show(requireFragmentManager()) @@ -536,7 +543,7 @@ class DiscussionsDetailsFragment : BasePresenterFragment< @JavascriptInterface fun onAvatarPressed(id: String) { presenter.findEntry(id.toLong())?.let { entry -> - val bundle = StudentContextFragment.makeBundle(entry.author!!.id, mCanvasContext.id) + val bundle = StudentContextFragment.makeBundle(entry.author!!.id, canvasContext.id) RouteMatcher.route(requireContext(), Route(StudentContextFragment::class.java, null, bundle)) } } @@ -567,7 +574,7 @@ class DiscussionsDetailsFragment : BasePresenterFragment< @JavascriptInterface fun onMoreRepliesPressed(id: String) { val args = makeBundle(presenter.discussionTopicHeader, presenter.discussionTopic, id.toLong(), presenter.getSkipId()) - RouteMatcher.route(requireContext(), Route(null, DiscussionsDetailsFragment::class.java, mCanvasContext, args)) + RouteMatcher.route(requireContext(), Route(null, DiscussionsDetailsFragment::class.java, canvasContext, args)) } @JavascriptInterface @@ -635,7 +642,7 @@ class DiscussionsDetailsFragment : BasePresenterFragment< private fun showReplyView(id: Long) { if (APIHelper.hasNetworkConnection()) { - val args = DiscussionsReplyFragment.makeBundle(presenter.discussionTopicHeader.id, id, mIsAnnouncements) + val args = DiscussionsReplyFragment.makeBundle(presenter.discussionTopicHeader.id, id, isAnnouncements) RouteMatcher.route(requireContext(), Route(DiscussionsReplyFragment::class.java, presenter.canvasContext, args)) } else { NoInternetConnectionDialog.show(requireFragmentManager()) @@ -658,7 +665,7 @@ class DiscussionsDetailsFragment : BasePresenterFragment< private fun showUpdateReplyView(id: Long) { if (APIHelper.hasNetworkConnection()) { - val args = DiscussionsUpdateFragment.makeBundle(presenter.discussionTopicHeader.id, presenter.findEntry(id), mIsAnnouncements, presenter.discussionTopic) + val args = DiscussionsUpdateFragment.makeBundle(presenter.discussionTopicHeader.id, presenter.findEntry(id), isAnnouncements, presenter.discussionTopic) RouteMatcher.route(requireContext(), Route(DiscussionsUpdateFragment::class.java, presenter.canvasContext, args)) } else { NoInternetConnectionDialog.show(requireFragmentManager()) @@ -702,11 +709,11 @@ class DiscussionsDetailsFragment : BasePresenterFragment< override fun showAnonymousDiscussionView() = with(binding) { anonymousDiscussionsNotSupported.setVisible() - openInBrowser.setVisible(mDiscussionTopicHeader.htmlUrl?.isNotEmpty() == true) + openInBrowser.setVisible(discussionTopicHeader.htmlUrl?.isNotEmpty() == true) replyToDiscussionTopic.setGone() swipeRefreshLayout.isEnabled = false openInBrowser.onClick { - mDiscussionTopicHeader.htmlUrl?.let { url -> + discussionTopicHeader.htmlUrl?.let { url -> requireContext().startActivity(InternalWebViewActivity.createIntent(requireContext(), url, "", true)) } } @@ -759,7 +766,7 @@ class DiscussionsDetailsFragment : BasePresenterFragment< event.once(javaClass.simpleName + ".onPost()") { if (it == presenter.discussionTopicHeader.id) { if(activity is MasterDetailInteractions) { - (activity as MasterDetailInteractions).popFragment(mCanvasContext) + (activity as MasterDetailInteractions).popFragment(canvasContext) } } else if(activity is FullScreenInteractions) { requireActivity().finish() @@ -822,7 +829,7 @@ class DiscussionsDetailsFragment : BasePresenterFragment< } @JvmStatic fun newInstance(canvasContext: CanvasContext, args: Bundle) = DiscussionsDetailsFragment().withArgs(args).apply { - mCanvasContext = canvasContext + this.canvasContext = canvasContext } } } diff --git a/apps/teacher/src/main/java/com/instructure/teacher/fragments/DiscussionsListFragment.kt b/apps/teacher/src/main/java/com/instructure/teacher/fragments/DiscussionsListFragment.kt index 100fee2d75..592cb9721e 100644 --- a/apps/teacher/src/main/java/com/instructure/teacher/fragments/DiscussionsListFragment.kt +++ b/apps/teacher/src/main/java/com/instructure/teacher/fragments/DiscussionsListFragment.kt @@ -23,6 +23,7 @@ import androidx.recyclerview.widget.RecyclerView import com.instructure.canvasapi2.models.CanvasContext import com.instructure.canvasapi2.models.DiscussionTopicHeader import com.instructure.canvasapi2.utils.ApiPrefs +import com.instructure.canvasapi2.utils.pageview.PageView import com.instructure.interactions.router.Route import com.instructure.pandautils.analytics.SCREEN_VIEW_DISCUSSION_LIST import com.instructure.pandautils.analytics.ScreenView @@ -44,6 +45,7 @@ import org.greenrobot.eventbus.EventBus import org.greenrobot.eventbus.Subscribe import org.greenrobot.eventbus.ThreadMode +@PageView(url = "{canvasContext}/discussion_topics") @ScreenView(SCREEN_VIEW_DISCUSSION_LIST) open class DiscussionsListFragment : BaseExpandableSyncFragment< String, @@ -55,22 +57,22 @@ open class DiscussionsListFragment : BaseExpandableSyncFragment< private val binding by viewBinding(FragmentDiscussionListBinding::bind) - protected var mCanvasContext: CanvasContext by ParcelableArg(default = CanvasContext.getGenericContext(CanvasContext.Type.COURSE, -1L, "")) + protected var canvasContext: CanvasContext by ParcelableArg(default = CanvasContext.getGenericContext(CanvasContext.Type.COURSE, -1L, "")) - private val mLinearLayoutManager by lazy { LinearLayoutManager(requireContext()) } + private val linearLayoutManager by lazy { LinearLayoutManager(requireContext()) } private lateinit var mRecyclerView: RecyclerView - private var mNeedToForceNetwork = false - private var mForceRefresh = false - protected var mIsAnnouncements by BooleanArg() + private var needToForceNetwork = false + private var forceRefresh = false + protected var isAnnouncements by BooleanArg() override fun layoutResId(): Int = R.layout.fragment_discussion_list override val recyclerView: RecyclerView get() = binding.discussionRecyclerView - override fun getPresenterFactory() = DiscussionListPresenterFactory(mCanvasContext, mIsAnnouncements) + override fun getPresenterFactory() = DiscussionListPresenterFactory(canvasContext, isAnnouncements) override fun onPresenterPrepared(presenter: DiscussionListPresenter) = with(binding) { - val emptyTitle = getString(if (mIsAnnouncements) R.string.noAnnouncements else R.string.noDiscussions) + val emptyTitle = getString(if (isAnnouncements) R.string.noAnnouncements else R.string.noDiscussions) mRecyclerView = RecyclerViewUtils.buildRecyclerView( rootView = rootView, context = requireContext(), @@ -97,17 +99,17 @@ open class DiscussionsListFragment : BaseExpandableSyncFragment< } override fun onCreateView(view: View) { - mLinearLayoutManager.orientation = RecyclerView.VERTICAL + linearLayoutManager.orientation = RecyclerView.VERTICAL } override fun onReadySetGo(presenter: DiscussionListPresenter) { mRecyclerView.adapter = adapter - if (mForceRefresh) { + if (forceRefresh) { presenter.refresh(true) - mForceRefresh = false + forceRefresh = false } else { - presenter.loadData(mNeedToForceNetwork) - mNeedToForceNetwork = false + presenter.loadData(needToForceNetwork) + needToForceNetwork = false } } @@ -127,7 +129,7 @@ open class DiscussionsListFragment : BaseExpandableSyncFragment< } override fun createAdapter(): DiscussionListAdapter { - return DiscussionListAdapter(requireContext(), presenter, mCanvasContext.textAndIconColor, mIsAnnouncements, + return DiscussionListAdapter(requireContext(), presenter, canvasContext.textAndIconColor, isAnnouncements, { discussionTopicHeader -> val route = presenter.getDetailsRoute(discussionTopicHeader) RouteMatcher.route( @@ -177,7 +179,7 @@ open class DiscussionsListFragment : BaseExpandableSyncFragment< // We don't want to leave the fab hidden if the list is empty if(presenter.isEmpty) { createNewDiscussion.show() - if (mIsAnnouncements) { + if (isAnnouncements) { emptyPandaView.setEmptyViewImage(requireContext().getDrawableCompat(R.drawable.ic_panda_noannouncements)) emptyPandaView.setMessageText(R.string.noAnnouncementsSubtext) } else { @@ -189,10 +191,10 @@ open class DiscussionsListFragment : BaseExpandableSyncFragment< } private fun setupToolbar() = with(binding) { - discussionListToolbar.title = if(mIsAnnouncements) getString(R.string.tab_announcements) else getString(R.string.tab_discussions) - discussionListToolbar.subtitle = mCanvasContext.name + discussionListToolbar.title = if(isAnnouncements) getString(R.string.tab_announcements) else getString(R.string.tab_discussions) + discussionListToolbar.subtitle = canvasContext.name discussionListToolbar.setupBackButton(this@DiscussionsListFragment) - val searchHint = getString(if (mIsAnnouncements) R.string.searchAnnouncementsHint else R.string.searchDiscussionsHint) + val searchHint = getString(if (isAnnouncements) R.string.searchAnnouncementsHint else R.string.searchDiscussionsHint) discussionListToolbar.addSearch(searchHint) { query -> if (query.isBlank()) { emptyPandaView.emptyViewText(R.string.no_items_to_display_short) @@ -201,7 +203,7 @@ open class DiscussionsListFragment : BaseExpandableSyncFragment< } presenter.searchQuery = query } - ViewStyler.themeToolbarColored(requireActivity(), discussionListToolbar, mCanvasContext.backgroundColor, requireContext().getColor(R.color.white)) + ViewStyler.themeToolbarColored(requireActivity(), discussionListToolbar, canvasContext.backgroundColor, requireContext().getColor(R.color.white)) } private fun setupViews() = with(binding) { @@ -209,11 +211,11 @@ open class DiscussionsListFragment : BaseExpandableSyncFragment< createNewDiscussion.backgroundTintList = ViewStyler.makeColorStateListForButton() createNewDiscussion.setImageDrawable(ColorUtils.colorIt(ThemePrefs.buttonTextColor, createNewDiscussion.drawable)) createNewDiscussion.onClickWithRequireNetwork { - if(mIsAnnouncements) { - val args = CreateOrEditAnnouncementFragment.newInstanceCreate(mCanvasContext).nonNullArgs + if(isAnnouncements) { + val args = CreateOrEditAnnouncementFragment.newInstanceCreate(canvasContext).nonNullArgs RouteMatcher.route(requireContext(), Route(CreateOrEditAnnouncementFragment::class.java, null, args)) } else { - val args = CreateDiscussionFragment.makeBundle(mCanvasContext) + val args = CreateDiscussionFragment.makeBundle(canvasContext) RouteMatcher.route(requireContext(), Route(CreateDiscussionFragment::class.java, null, args)) } } @@ -248,7 +250,7 @@ open class DiscussionsListFragment : BaseExpandableSyncFragment< // need to set a flag here. Because we use the event bus in the fragment instead of the presenter for unit testing purposes, // when we come back to this fragment it will go through the life cycle events again and the cached data will immediately // overwrite the data from the network if we refresh the presenter from here. - mNeedToForceNetwork = true + needToForceNetwork = true } } @@ -268,7 +270,7 @@ open class DiscussionsListFragment : BaseExpandableSyncFragment< // need to set a flag here. Because we use the event bus in the fragment instead of the presenter for unit testing purposes, // when we come back to this fragment it will go through the life cycle events again and the cached data will immediately // overwrite the data from the network if we refresh the presenter from here. - mNeedToForceNetwork = true + needToForceNetwork = true } } @@ -279,7 +281,7 @@ open class DiscussionsListFragment : BaseExpandableSyncFragment< val discussionTopicHeader = adapter.getItem(it) if(discussionTopicHeader != null) { adapter.removeItem(discussionTopicHeader, false) - mNeedToForceNetwork = true + needToForceNetwork = true } } } @@ -288,7 +290,7 @@ open class DiscussionsListFragment : BaseExpandableSyncFragment< companion object { fun newInstance(canvasContext: CanvasContext) = DiscussionsListFragment().apply { - mCanvasContext = canvasContext + this.canvasContext = canvasContext } } } diff --git a/apps/teacher/src/main/java/com/instructure/teacher/fragments/DiscussionsUpdateFragment.kt b/apps/teacher/src/main/java/com/instructure/teacher/fragments/DiscussionsUpdateFragment.kt index c9ef9fd00b..3591aaf74a 100644 --- a/apps/teacher/src/main/java/com/instructure/teacher/fragments/DiscussionsUpdateFragment.kt +++ b/apps/teacher/src/main/java/com/instructure/teacher/fragments/DiscussionsUpdateFragment.kt @@ -27,6 +27,8 @@ import com.instructure.canvasapi2.models.DiscussionEntry import com.instructure.canvasapi2.models.DiscussionTopic import com.instructure.canvasapi2.utils.APIHelper import com.instructure.canvasapi2.utils.Logger +import com.instructure.canvasapi2.utils.pageview.PageView +import com.instructure.canvasapi2.utils.pageview.PageViewUrlParam import com.instructure.pandautils.analytics.SCREEN_VIEW_DISCUSSIONS_UPDATE import com.instructure.pandautils.analytics.ScreenView import com.instructure.pandautils.binding.viewBinding @@ -49,15 +51,17 @@ import com.instructure.teacher.utils.setupCloseButton import com.instructure.teacher.utils.setupMenu import com.instructure.teacher.viewinterface.DiscussionsUpdateView +@PageView(url = "{canvasContext}/discussion_topics/{topicId}/edit") @ScreenView(SCREEN_VIEW_DISCUSSIONS_UPDATE) class DiscussionsUpdateFragment : BasePresenterFragment(), DiscussionsUpdateView { private val binding by viewBinding(FragmentDiscussionsEditBinding::bind) - private var mCanvasContext: CanvasContext by ParcelableArg(default = CanvasContext.getGenericContext(CanvasContext.Type.COURSE, -1L, "")) - private var mDiscussionTopicHeaderId: Long by LongArg(key = DISCUSSION_TOPIC_HEADER_ID, default = 0L) // The topic the discussion belongs too - private var mDiscussionEntry: DiscussionEntry by ParcelableArg(key = DISCUSSION_ENTRY, default = DiscussionEntry()) - private var mDiscussionTopic: DiscussionTopic by ParcelableArg(key = DISCUSSION_TOPIC, default = DiscussionTopic()) + private var canvasContext: CanvasContext by ParcelableArg(default = CanvasContext.getGenericContext(CanvasContext.Type.COURSE, -1L, "")) + @get:PageViewUrlParam("topicId") + private var discussionTopicHeaderId: Long by LongArg(key = DISCUSSION_TOPIC_HEADER_ID, default = 0L) // The topic the discussion belongs too + private var discussionEntry: DiscussionEntry by ParcelableArg(key = DISCUSSION_ENTRY, default = DiscussionEntry()) + private var discussionTopic: DiscussionTopic by ParcelableArg(key = DISCUSSION_TOPIC, default = DiscussionTopic()) private var placeHolderList: ArrayList = ArrayList() override fun onCreate(savedInstanceState: Bundle?) { @@ -71,7 +75,7 @@ class DiscussionsUpdateFragment : BasePresenterFragment = ArrayList() private var rceImageUploadJob: Job? = null @@ -101,19 +105,19 @@ class EditAssignmentDetailsFragment : BaseFragment() { // Keeps track of which override we were editing so we can scroll back to it when the user returns from editing assignees private var scrollBackToOverride: AssignmentOverrideView? = null - private var mDueDateApiCalls: Job? = null - private var mPutAssignmentCall: Job? = null + private var dueDateApiCalls: Job? = null + private var putAssignmentCall: Job? = null - private var mScrollHandler: Handler = Handler() + private var scrollHandler: Handler = Handler() - private var mScrollToRunnable: Runnable = Runnable { + private var scrollToRunnable: Runnable = Runnable { if(isAdded) binding.scrollView.fullScroll(ScrollView.FOCUS_DOWN) } // We maintain a copy of the groupedDueDates to manipulate and use to display // overrides. When pushing changes, we update the original assignment object // with the changes in the copy. - private var mEditDateGroups: EditDateGroups = arrayListOf() + private var editDateGroups: EditDateGroups = arrayListOf() private val datePickerOnClick: (date: Date?, (Int, Int, Int) -> Unit) -> Unit = { date, callback -> DatePickerDialogFragment.getInstance(requireActivity().supportFragmentManager, date) { year, month, dayOfMonth -> @@ -130,11 +134,15 @@ class EditAssignmentDetailsFragment : BaseFragment() { private val removeOverrideClick: (DueDateGroup) -> Unit = { callback -> // Show confirmation dialog ConfirmRemoveAssignmentOverrideDialog.show(requireActivity().supportFragmentManager) { - if (mEditDateGroups.contains(callback)) mEditDateGroups.remove(callback) + if (editDateGroups.contains(callback)) editDateGroups.remove(callback) setupOverrides() } } + @Suppress("unused") + @PageViewUrl + private fun makePageViewUrl() = "${ApiPrefs.fullDomain}/${course.contextId.replace("_", "s/")}/${assignment.id}" + override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) AppCompatDelegate.setCompatVectorFromResourcesEnabled(true) @@ -148,7 +156,7 @@ class EditAssignmentDetailsFragment : BaseFragment() { super.onViewCreated(view, savedInstanceState) savedInstanceState?.let { @Suppress("UNCHECKED_CAST") - mEditDateGroups = (savedInstanceState.getSerializable(EDIT_DATE_GROUPS) as ArrayList) + editDateGroups = (savedInstanceState.getSerializable(EDIT_DATE_GROUPS) as ArrayList) } // Hide Keyboard @@ -176,7 +184,7 @@ class EditAssignmentDetailsFragment : BaseFragment() { RequestCodes.CAMERA_PIC_REQUEST -> MediaUploadUtils.handleCameraPicResult(requireActivity(), null) else -> null }?.let { imageUri -> - rceImageUploadJob = MediaUploadUtils.uploadRceImageJob(imageUri, mCourse, requireActivity()) { imageUrl -> binding.descriptionEditor.insertImage(requireActivity(), imageUrl) } + rceImageUploadJob = MediaUploadUtils.uploadRceImageJob(imageUri, course, requireActivity()) { imageUrl -> binding.descriptionEditor.insertImage(requireActivity(), imageUrl) } } } } @@ -191,14 +199,14 @@ class EditAssignmentDetailsFragment : BaseFragment() { } @SuppressLint("ClickableViewAccessibility") - private fun setupPublishSwitch() = with(mAssignment) { + private fun setupPublishSwitch() = with(assignment) { // If a student has submitted something, we can't let the teacher unpublish the assignment // Publish status val publishSwitch = binding.publishSwitch publishSwitch.applyTheme() publishSwitch.isChecked = published - mIsPublished = published - publishSwitch.setOnCheckedChangeListener { _, isChecked -> mIsPublished = isChecked } + isPublished = published + publishSwitch.setOnCheckedChangeListener { _, isChecked -> isPublished = isChecked } if (published && !unpublishable) { publishSwitch.alpha = 0.5f publishSwitch.setOnTouchListener { _, motionEvent -> @@ -216,7 +224,7 @@ class EditAssignmentDetailsFragment : BaseFragment() { ViewStyler.themeSpinner(requireContext(), displayGradeAsSpinner, ThemePrefs.brandColor) displayGradeAsSpinner.onItemSelectedListener = null - when(mDisplayGradeAs) { + when(displayGradeAs) { POINTS_TYPE -> displayGradeAsSpinner.setSelection(spinnerAdapter.getPosition(getString(R.string.points))) GPA_SCALE_TYPE -> displayGradeAsSpinner.setSelection(spinnerAdapter.getPosition(getString(R.string.gpa_scale))) LETTER_GRADE_TYPE -> displayGradeAsSpinner.setSelection(spinnerAdapter.getPosition(getString(R.string.letter_grade))) @@ -229,12 +237,12 @@ class EditAssignmentDetailsFragment : BaseFragment() { override fun onItemSelected(parent: AdapterView<*>, view: View?, position: Int, id: Long) { if(view == null) return when((view as TextView).text.toString()) { - getString(R.string.points) -> mDisplayGradeAs = POINTS_TYPE - getString(R.string.gpa_scale) -> mDisplayGradeAs = GPA_SCALE_TYPE - getString(R.string.letter_grade) -> mDisplayGradeAs = LETTER_GRADE_TYPE - getString(R.string.complete_incomplete) -> mDisplayGradeAs = PASS_FAIL_TYPE - getString(R.string.percentage) -> mDisplayGradeAs = PERCENT_TYPE - getString(R.string.not_graded) -> mDisplayGradeAs = NOT_GRADED_TYPE + getString(R.string.points) -> displayGradeAs = POINTS_TYPE + getString(R.string.gpa_scale) -> displayGradeAs = GPA_SCALE_TYPE + getString(R.string.letter_grade) -> displayGradeAs = LETTER_GRADE_TYPE + getString(R.string.complete_incomplete) -> displayGradeAs = PASS_FAIL_TYPE + getString(R.string.percentage) -> displayGradeAs = PERCENT_TYPE + getString(R.string.not_graded) -> displayGradeAs = NOT_GRADED_TYPE } } @@ -253,7 +261,7 @@ class EditAssignmentDetailsFragment : BaseFragment() { } // Assignment name - editAssignmentName.setText(mAssignment.name) + editAssignmentName.setText(assignment.name) editAssignmentName.setOnFocusChangeListener { _, hasFocus -> if (hasFocus) descriptionEditor.hideEditorToolbar() } @@ -267,7 +275,7 @@ class EditAssignmentDetailsFragment : BaseFragment() { } } // Points possible - editGradePoints.setText(NumberHelper.formatDecimal(mAssignment.pointsPossible, 2, true)) + editGradePoints.setText(NumberHelper.formatDecimal(assignment.pointsPossible, 2, true)) // set the underline to be the brand color ViewStyler.themeEditText(requireContext(), editGradePoints, ThemePrefs.brandColor) editGradePoints.onTextChanged { @@ -279,8 +287,8 @@ class EditAssignmentDetailsFragment : BaseFragment() { } setupPublishSwitch() - if(mDisplayGradeAs == null) { - mDisplayGradeAs = mAssignment.gradingType + if(displayGradeAs == null) { + displayGradeAs = assignment.gradingType } setupDisplayGradeAs() @@ -291,14 +299,14 @@ class EditAssignmentDetailsFragment : BaseFragment() { // Description setupDescription() - if (mEditDateGroups.isEmpty()) mEditDateGroups.addAll(mAssignment.groupedDueDates) + if (editDateGroups.isEmpty()) editDateGroups.addAll(assignment.groupedDueDates) - mDueDateApiCalls = weave { + dueDateApiCalls = weave { try { if (groupsMapped.isEmpty() && sectionsMapped.isEmpty() && studentsMapped.isEmpty()) { - val sections = awaitApi> { SectionManager.getAllSectionsForCourse(mAssignment.courseId, it, false) } - val groups = if (mAssignment.groupCategoryId > 0L) awaitApi> { GroupCategoriesManager.getAllGroupsForCategory(mAssignment.groupCategoryId, it, false) } else emptyList() - val students = awaitApi> { UserManager.getAllPeopleList(mCourse, it, false) } + val sections = awaitApi> { SectionManager.getAllSectionsForCourse(assignment.courseId, it, false) } + val groups = if (assignment.groupCategoryId > 0L) awaitApi> { GroupCategoriesManager.getAllGroupsForCategory(assignment.groupCategoryId, it, false) } else emptyList() + val students = awaitApi> { UserManager.getAllPeopleList(course, it, false) } groupsMapped += groups.associateBy { it.id } sectionsMapped += sections.associateBy { it.id } studentsMapped += students.associateBy { it.id } @@ -306,14 +314,14 @@ class EditAssignmentDetailsFragment : BaseFragment() { setupAddOverrideButton() setupOverrides() - if (mScrollToDates) { - mScrollToDates = false + if (scrollToDates) { + scrollToDates = false // We came from the Dates page, scroll to the dates for editing - mScrollHandler.postDelayed(mScrollToRunnable, 300) + scrollHandler.postDelayed(scrollToRunnable, 300) } scrollBackToOverride?.let { - if (!mScrollToDates) + if (!scrollToDates) scrollView.post { scrollView.fullScroll(ScrollView.FOCUS_DOWN) } @@ -335,7 +343,7 @@ class EditAssignmentDetailsFragment : BaseFragment() { plus.setColorFilter(ThemePrefs.textButtonColor) addOverride.setOnClickListener { - mEditDateGroups.add(DueDateGroup()) + editDateGroups.add(DueDateGroup()) setupOverrides() scrollView.post { scrollView.fullScroll(ScrollView.FOCUS_DOWN) @@ -348,11 +356,11 @@ class EditAssignmentDetailsFragment : BaseFragment() { private fun setupOverrides() = with(binding) { overrideContainer.removeAllViews() // Load in overrides - mEditDateGroups.forEachIndexed { index, dueDateGroup -> + editDateGroups.forEachIndexed { index, dueDateGroup -> val assignees = ArrayList() val v = AssignmentOverrideView(requireActivity()) if (dueDateGroup.isEveryone) { - assignees += getString(if (mEditDateGroups.any { it.hasOverrideAssignees }) R.string.everyone_else else R.string.everyone) + assignees += getString(if (editDateGroups.any { it.hasOverrideAssignees }) R.string.everyone_else else R.string.everyone) } dueDateGroup.groupIds.forEach { assignees.add(groupsMapped[it]?.name!!) } dueDateGroup.sectionIds.forEach { assignees.add(sectionsMapped[it]?.name!!) } @@ -363,14 +371,14 @@ class EditAssignmentDetailsFragment : BaseFragment() { } } - v.setupOverride(index, dueDateGroup, mEditDateGroups.size > 1, assignees, datePickerOnClick, timePickerOnClick, removeOverrideClick) { + v.setupOverride(index, dueDateGroup, editDateGroups.size > 1, assignees, datePickerOnClick, timePickerOnClick, removeOverrideClick) { val args = AssigneeListFragment.makeBundle( - mEditDateGroups, + editDateGroups, index, sectionsMapped.values.toList(), groupsMapped.values.toList(), studentsMapped.values.toList()) - RouteMatcher.route(requireContext(), Route(AssigneeListFragment::class.java, mCourse, args)) + RouteMatcher.route(requireContext(), Route(AssigneeListFragment::class.java, course, args)) scrollBackToOverride = v } @@ -389,8 +397,8 @@ class EditAssignmentDetailsFragment : BaseFragment() { // Load description // If the html has a Studio LTI url, we want to authenticate so the user doesn't have to login again - if (CanvasWebView.containsLTI(mAssignment.description.orEmpty(), "UTF-8")) { - descriptionEditor.setHtml(DiscussionUtils.createLTIPlaceHolders(requireContext(), mAssignment.description ?: "") { _, placeholder -> + if (CanvasWebView.containsLTI(assignment.description.orEmpty(), "UTF-8")) { + descriptionEditor.setHtml(DiscussionUtils.createLTIPlaceHolders(requireContext(), assignment.description ?: "") { _, placeholder -> placeHolderList.add(placeholder) }, getString(R.string.assignmentDescriptionContentDescription), @@ -398,7 +406,7 @@ class EditAssignmentDetailsFragment : BaseFragment() { ThemePrefs.brandColor, ThemePrefs.textButtonColor) } else { - descriptionEditor.setHtml(mAssignment.description, + descriptionEditor.setHtml(assignment.description, getString(R.string.assignmentDescriptionContentDescription), getString(R.string.rce_empty_description), ThemePrefs.brandColor, ThemePrefs.textButtonColor) @@ -428,22 +436,22 @@ class EditAssignmentDetailsFragment : BaseFragment() { val postData = AssignmentPostBody() postData.name = editAssignmentName.text.toString() postData.pointsPossible = pointsPossible - postData.setGroupedDueDates(mEditDateGroups) + postData.setGroupedDueDates(editDateGroups) postData.description = handleLTIPlaceHolders(placeHolderList, descriptionEditor.html) postData.notifyOfUpdate = false - postData.gradingType = mDisplayGradeAs + postData.gradingType = displayGradeAs // TODO: remove this section when we support editing submission types // There is some weirdness with the API dealing with not graded stuff. When you change it from not graded you also // need to set the submission type to be something. When we implement submission type editing we won't need this here - if(mAssignment.gradingType == NOT_GRADED_TYPE && mDisplayGradeAs != NOT_GRADED_TYPE) { + if(assignment.gradingType == NOT_GRADED_TYPE && displayGradeAs != NOT_GRADED_TYPE) { val type = "none" val submissionList = listOf(type) postData.submissionTypes = submissionList } // if we want to set the type as not graded, we don't want a submission type or points possible - if(mDisplayGradeAs == NOT_GRADED_TYPE) { + if(displayGradeAs == NOT_GRADED_TYPE) { //set points to 0 if we aren't grading it postData.pointsPossible = null val type = NOT_GRADED_TYPE @@ -452,24 +460,24 @@ class EditAssignmentDetailsFragment : BaseFragment() { } // only set the published flag if we can unpublish/publish the assignment - if (mAssignment.unpublishable) postData.published = mIsPublished - else postData.published = mAssignment.published + if (assignment.unpublishable) postData.published = isPublished + else postData.published = assignment.published @Suppress("EXPERIMENTAL_FEATURE_WARNING") - mPutAssignmentCall = weave { + putAssignmentCall = weave { try { saveButton?.setGone() savingProgressBar.announceForAccessibility(getString(R.string.saving)) savingProgressBar.setVisible() - mAssignment = awaitApi { AssignmentManager.editAssignment(mAssignment.courseId, mAssignment.id, postData, it, false) } - AssignmentUpdatedEvent(mAssignment.id).post() // Post bus event + assignment = awaitApi { AssignmentManager.editAssignment(assignment.courseId, assignment.id, postData, it, false) } + AssignmentUpdatedEvent(assignment.id).post() // Post bus event toast(R.string.successfully_updated_assignment) // let the user know the assignment was saved editAssignmentName.hideKeyboard() // close the keyboard requireActivity().onBackPressed() // close this fragment } catch (e: Throwable) { saveButton?.setVisible() savingProgressBar.setGone() - if (mAssignment.inClosedGradingPeriod) { + if (assignment.inClosedGradingPeriod) { toast(R.string.error_saving_assignment_closed_grading_period) } else { toast(R.string.error_saving_assignment) @@ -483,7 +491,7 @@ class EditAssignmentDetailsFragment : BaseFragment() { fun onAssigneesChanged(event: AssigneesUpdatedEvent) { // Update grouped due dates (EditDateGroups) event.once(javaClass.simpleName) { dates -> - mEditDateGroups = dates + editDateGroups = dates setupOverrides() //remove it so when we go to another assignment it won't show up there too EventBus.getDefault().removeStickyEvent(event) @@ -491,16 +499,16 @@ class EditAssignmentDetailsFragment : BaseFragment() { } override fun onSaveInstanceState(outState: Bundle) { - outState.putSerializable(EDIT_DATE_GROUPS, ArrayList(mEditDateGroups)) + outState.putSerializable(EDIT_DATE_GROUPS, ArrayList(editDateGroups)) super.onSaveInstanceState(outState) } override fun onDestroyView() { super.onDestroyView() - mDueDateApiCalls?.cancel() - mPutAssignmentCall?.cancel() - mScrollHandler.removeCallbacks(mScrollToRunnable) - mSessionAuthJob?.cancel() + dueDateApiCalls?.cancel() + putAssignmentCall?.cancel() + scrollHandler.removeCallbacks(scrollToRunnable) + sessionAuthJob?.cancel() rceImageUploadJob?.cancel() } @@ -510,7 +518,7 @@ class EditAssignmentDetailsFragment : BaseFragment() { const val EDIT_DATE_GROUPS = "editDateGroups" fun newInstance(course: Course, args: Bundle) = EditAssignmentDetailsFragment().withArgs(args).apply { - mCourse = course + this.course = course } fun makeBundle(assignment: Assignment, scrollToDates: Boolean): Bundle { diff --git a/apps/teacher/src/main/java/com/instructure/teacher/fragments/FileListFragment.kt b/apps/teacher/src/main/java/com/instructure/teacher/fragments/FileListFragment.kt index c30f65fff1..eda09ef208 100644 --- a/apps/teacher/src/main/java/com/instructure/teacher/fragments/FileListFragment.kt +++ b/apps/teacher/src/main/java/com/instructure/teacher/fragments/FileListFragment.kt @@ -29,7 +29,10 @@ import com.instructure.canvasapi2.models.CanvasContext import com.instructure.canvasapi2.models.Course import com.instructure.canvasapi2.models.FileFolder import com.instructure.canvasapi2.utils.APIHelper +import com.instructure.canvasapi2.utils.ApiPrefs import com.instructure.canvasapi2.utils.isValid +import com.instructure.canvasapi2.utils.pageview.PageView +import com.instructure.canvasapi2.utils.pageview.PageViewUrl import com.instructure.interactions.router.Route import com.instructure.pandautils.analytics.SCREEN_VIEW_FILE_LIST import com.instructure.pandautils.analytics.ScreenView @@ -59,6 +62,7 @@ import org.greenrobot.eventbus.Subscribe import org.greenrobot.eventbus.ThreadMode import java.util.* +@PageView @ScreenView(SCREEN_VIEW_FILE_LIST) class FileListFragment : BaseSyncFragment< FileFolder, @@ -71,7 +75,7 @@ class FileListFragment : BaseSyncFragment< private lateinit var mRecyclerView: RecyclerView - private var mCanvasContext: CanvasContext by ParcelableArg(Course()) + private var canvasContext: CanvasContext by ParcelableArg(Course()) private var currentFolder: FileFolder by ParcelableArg(FileFolder()) private var fabOpen = false @@ -136,9 +140,15 @@ class FileListFragment : BaseSyncFragment< } } + @Suppress("unused") + @PageViewUrl + private fun makePageViewUrl() = + if (canvasContext.type == CanvasContext.Type.USER) "${ApiPrefs.fullDomain}/files" + else "${ApiPrefs.fullDomain}/${canvasContext.contextId.replace("_", "s/")}/files" + override fun layoutResId() = R.layout.fragment_file_list override fun onCreateView(view: View) = Unit - override fun getPresenterFactory() = FileListPresenterFactory(currentFolder, mCanvasContext) + override fun getPresenterFactory() = FileListPresenterFactory(currentFolder, canvasContext) override val recyclerView: RecyclerView get() = binding.fileListRecyclerView override fun onPresenterPrepared(presenter: FileListPresenter) { @@ -178,17 +188,17 @@ class FileListFragment : BaseSyncFragment< } override fun createAdapter(): FileListAdapter { - return FileListAdapter(requireContext(), mCanvasContext.textAndIconColor, presenter) { + return FileListAdapter(requireContext(), canvasContext.textAndIconColor, presenter) { if (it.displayName.isValid()) { // This is a file - val editableFile = EditableFile(it, presenter.usageRights, presenter.licenses, mCanvasContext.backgroundColor, presenter.mCanvasContext, R.drawable.ic_document) + val editableFile = EditableFile(it, presenter.usageRights, presenter.licenses, canvasContext.backgroundColor, presenter.mCanvasContext, R.drawable.ic_document) if (it.isHtmlFile) { /* An HTML file can reference other canvas files as resources (e.g. CSS files) and must be accessed as an authenticated preview to work correctly */ - val bundle = ViewHtmlFragment.makeAuthSessionBundle(mCanvasContext, it, it.displayName.orEmpty(), mCanvasContext.backgroundColor, editableFile) + val bundle = ViewHtmlFragment.makeAuthSessionBundle(canvasContext, it, it.displayName.orEmpty(), canvasContext.backgroundColor, editableFile) RouteMatcher.route(requireActivity(), Route(ViewHtmlFragment::class.java, null, bundle)) } else { - viewMedia(requireContext(), it.displayName.orEmpty(), it.contentType.orEmpty(), it.url, it.thumbnailUrl, it.displayName, R.drawable.ic_document, mCanvasContext.backgroundColor, editableFile) + viewMedia(requireContext(), it.displayName.orEmpty(), it.contentType.orEmpty(), it.url, it.thumbnailUrl, it.displayName, R.drawable.ic_document, canvasContext.backgroundColor, editableFile) } } else { // This is a folder @@ -213,8 +223,8 @@ class FileListFragment : BaseSyncFragment< override fun checkIfEmpty() = with(binding) { when { !presenter.currentFolder.isRoot -> emptyPandaView.setMessageText(R.string.emptyFolder) - mCanvasContext.isCourse -> emptyPandaView.setMessageText(R.string.noFilesSubtextCourse) - mCanvasContext.isGroup -> emptyPandaView.setMessageText(R.string.noFilesSubtextGroup) + canvasContext.isCourse -> emptyPandaView.setMessageText(R.string.noFilesSubtextCourse) + canvasContext.isGroup -> emptyPandaView.setMessageText(R.string.noFilesSubtextGroup) else -> emptyPandaView.setMessageText(R.string.noFilesSubtext) } emptyPandaView.setEmptyViewImage(requireContext().getDrawableCompat(R.drawable.ic_panda_nofiles)) @@ -236,7 +246,7 @@ class FileListFragment : BaseSyncFragment< addFileFab.setOnClickListener { animateFabs() handleClick(childFragmentManager) { - val bundle = FileUploadDialogFragment.createContextBundle(null, mCanvasContext, presenter.currentFolder.id) + val bundle = FileUploadDialogFragment.createContextBundle(null, canvasContext, presenter.currentFolder.id) FileUploadDialogFragment.newInstance(bundle).show(childFragmentManager, FileUploadDialogFragment.TAG) } } @@ -282,9 +292,9 @@ class FileListFragment : BaseSyncFragment< when (it.itemId) { R.id.edit -> { val bundle = EditFileFolderFragment.makeBundle(presenter.currentFolder, presenter.usageRights, presenter.licenses, presenter.mCanvasContext.id) - RouteMatcher.route(requireContext(), Route(EditFileFolderFragment::class.java, mCanvasContext, bundle)) + RouteMatcher.route(requireContext(), Route(EditFileFolderFragment::class.java, canvasContext, bundle)) } - R.id.search -> RouteMatcher.route(requireContext(), Route(FileSearchFragment::class.java, mCanvasContext, Bundle())) + R.id.search -> RouteMatcher.route(requireContext(), Route(FileSearchFragment::class.java, canvasContext, Bundle())) } } @@ -297,10 +307,10 @@ class FileListFragment : BaseSyncFragment< fileListToolbar.title = getString(R.string.sg_tab_files) } - if (mCanvasContext.isUser) { + if (canvasContext.isUser) { // User's files, no CanvasContext ViewStyler.themeToolbarColored(requireActivity(), fileListToolbar, ThemePrefs.primaryColor, ThemePrefs.primaryTextColor) - } else ViewStyler.themeToolbarColored(requireActivity(), fileListToolbar, mCanvasContext.backgroundColor, requireContext().getColor(R.color.white)) + } else ViewStyler.themeToolbarColored(requireActivity(), fileListToolbar, canvasContext.backgroundColor, requireContext().getColor(R.color.white)) } private fun animateFabs() = if (fabOpen) { @@ -362,7 +372,7 @@ class FileListFragment : BaseSyncFragment< private const val CURRENT_FOLDER = "currentFolder" fun newInstance(canvasContext: CanvasContext, args: Bundle) = FileListFragment().apply { - mCanvasContext = args.getParcelable(CANVAS_CONTEXT) ?: canvasContext + this.canvasContext = args.getParcelable(CANVAS_CONTEXT) ?: canvasContext currentFolder = args.getParcelable(CURRENT_FOLDER) ?: FileFolder(id = -1L, name = "") } diff --git a/apps/teacher/src/main/java/com/instructure/teacher/fragments/LtiLaunchFragment.kt b/apps/teacher/src/main/java/com/instructure/teacher/fragments/LtiLaunchFragment.kt index 4105c536b6..222856ccb4 100644 --- a/apps/teacher/src/main/java/com/instructure/teacher/fragments/LtiLaunchFragment.kt +++ b/apps/teacher/src/main/java/com/instructure/teacher/fragments/LtiLaunchFragment.kt @@ -28,6 +28,8 @@ import com.instructure.canvasapi2.managers.SubmissionManager import com.instructure.canvasapi2.models.CanvasContext import com.instructure.canvasapi2.models.Tab import com.instructure.canvasapi2.utils.ApiPrefs +import com.instructure.canvasapi2.utils.pageview.PageView +import com.instructure.canvasapi2.utils.pageview.PageViewUrl import com.instructure.canvasapi2.utils.validOrNull import com.instructure.canvasapi2.utils.weave.weave import com.instructure.interactions.router.Route @@ -42,11 +44,14 @@ import com.instructure.teacher.router.RouteMatcher import kotlinx.coroutines.Job import java.net.URLDecoder +@PageView @ScreenView(SCREEN_VIEW_LTI_LAUNCH) class LtiLaunchFragment : BaseFragment() { private val binding by viewBinding(FragmentLtiLaunchBinding::bind) + var canvasContext: CanvasContext? by NullableParcelableArg(key = Const.CANVAS_CONTEXT) + private var title: String? by NullableStringArg(key = Const.TITLE) private var ltiUrl: String by StringArg(key = LTI_URL) private var ltiTab: Tab? by NullableParcelableArg(key = TAB) @@ -59,6 +64,11 @@ class LtiLaunchFragment : BaseFragment() { private var ltiUrlLaunchJob: Job? = null + @Suppress("unused") + @PageViewUrl + private fun makePageViewUrl() = + ltiTab?.externalUrl ?: ApiPrefs.fullDomain + canvasContext?.toAPIString() + "/external_tools" + override fun layoutResId(): Int = R.layout.fragment_lti_launch override fun onCreate(savedInstanceState: Bundle?) { diff --git a/apps/teacher/src/main/java/com/instructure/teacher/fragments/PageDetailsFragment.kt b/apps/teacher/src/main/java/com/instructure/teacher/fragments/PageDetailsFragment.kt index d3875c6c6a..cde474614a 100644 --- a/apps/teacher/src/main/java/com/instructure/teacher/fragments/PageDetailsFragment.kt +++ b/apps/teacher/src/main/java/com/instructure/teacher/fragments/PageDetailsFragment.kt @@ -26,6 +26,8 @@ import com.instructure.canvasapi2.models.Course import com.instructure.canvasapi2.models.Page import com.instructure.canvasapi2.utils.APIHelper import com.instructure.canvasapi2.utils.ApiPrefs +import com.instructure.canvasapi2.utils.pageview.PageView +import com.instructure.canvasapi2.utils.pageview.PageViewUrl import com.instructure.interactions.FullScreenInteractions import com.instructure.interactions.Identity import com.instructure.interactions.MasterDetailInteractions @@ -55,6 +57,7 @@ import org.greenrobot.eventbus.EventBus import org.greenrobot.eventbus.Subscribe import org.greenrobot.eventbus.ThreadMode +@PageView @ScreenView(SCREEN_VIEW_PAGE_DETAILS) class PageDetailsFragment : BasePresenterFragment< PageDetailsPresenter, @@ -63,15 +66,27 @@ class PageDetailsFragment : BasePresenterFragment< private val binding by viewBinding(FragmentPageDetailsBinding::bind) - private var mCanvasContext: CanvasContext by ParcelableArg(default = Course()) - private var mPage: Page by ParcelableArg(Page(), PAGE) - private var mPageId: String by StringArg(key = PAGE_ID) + private var canvasContext: CanvasContext by ParcelableArg(default = Course()) + private var page: Page by ParcelableArg(Page(), PAGE) + private var pageId: String by StringArg(key = PAGE_ID) private var downloadUrl: String? = null var downloadFileName: String? = null private var loadHtmlJob: Job? = null + @PageViewUrl + @Suppress("unused") + private fun makePageViewUrl(): String { + val url = StringBuilder(ApiPrefs.fullDomain) + page.let { + url.append(canvasContext.toAPIString()) + if (!it.frontPage) url.append("/pages/${page.url}") + getModuleItemId()?.let { url.append("?module_item_id=$it") } + } + return url.toString() + } + override fun onStart() { super.onStart() EventBus.getDefault().register(this) @@ -96,12 +111,12 @@ class PageDetailsFragment : BasePresenterFragment< } override fun onReadySetGo(presenter: PageDetailsPresenter): Unit = with(binding) { - if (mPage.frontPage) { - presenter.getFrontPage(mCanvasContext, true) - } else if (!mPageId.isBlank()) { - presenter.getPage(mPageId, mCanvasContext, true) + if (page.frontPage) { + presenter.getFrontPage(canvasContext, true) + } else if (!pageId.isBlank()) { + presenter.getPage(pageId, canvasContext, true) } else { - presenter.getPage(mPage.url ?: "", mCanvasContext, true) + presenter.getPage(page.url ?: "", canvasContext, true) } setupToolbar() @@ -154,9 +169,9 @@ class PageDetailsFragment : BasePresenterFragment< }) EventBus.getDefault().getStickyEvent(PageDeletedEvent::class.java)?.once(javaClass.simpleName + ".onResume()") { - if (it.id == mPage.id) { + if (it.id == page.id) { if (activity is MasterDetailInteractions) { - (activity as MasterDetailInteractions).popFragment(mCanvasContext) + (activity as MasterDetailInteractions).popFragment(canvasContext) } else if (activity is FullScreenInteractions) { requireActivity().finish() } @@ -164,20 +179,20 @@ class PageDetailsFragment : BasePresenterFragment< } } - override fun getPresenterFactory() = PageDetailsPresenterFactory(mCanvasContext, mPage) + override fun getPresenterFactory() = PageDetailsPresenterFactory(canvasContext, page) override fun onPresenterPrepared(presenter: PageDetailsPresenter) = Unit override fun layoutResId() = R.layout.fragment_page_details - override val identity: Long? get() = mPage.id + override val identity: Long? get() = page.id override val skipCheck: Boolean get() = false override fun populatePageDetails(page: Page) { - mPage = page + this.page = page loadHtmlJob = binding.canvasWebViewWraper.webView.loadHtmlWithIframes(requireContext(), page.body, { - if (view != null) binding.canvasWebViewWraper.loadHtml(it, page.title, baseUrl = mPage.htmlUrl) + if (view != null) binding.canvasWebViewWraper.loadHtml(it, page.title, baseUrl = this.page.htmlUrl) }) { - LtiLaunchFragment.routeLtiLaunchFragment(requireContext(), mCanvasContext, it) + LtiLaunchFragment.routeLtiLaunchFragment(requireContext(), canvasContext, it) } setupToolbar() } @@ -187,25 +202,25 @@ class PageDetailsFragment : BasePresenterFragment< } private fun setupToolbar() = with(binding) { - toolbar.setupMenu(R.menu.menu_page_details) { openEditPage(mPage) } + toolbar.setupMenu(R.menu.menu_page_details) { openEditPage(page) } toolbar.setupBackButtonWithExpandCollapseAndBack(this@PageDetailsFragment) { toolbar.updateToolbarExpandCollapseIcon(this@PageDetailsFragment) - ViewStyler.themeToolbarColored(requireActivity(), toolbar, mCanvasContext.backgroundColor, requireContext().getColor(R.color.white)) + ViewStyler.themeToolbarColored(requireActivity(), toolbar, canvasContext.backgroundColor, requireContext().getColor(R.color.white)) (activity as MasterDetailInteractions).toggleExpandCollapse() } - toolbar.title = mPage.title + toolbar.title = page.title if (!isTablet) { - toolbar.subtitle = mCanvasContext.name + toolbar.subtitle = canvasContext.name } - ViewStyler.themeToolbarColored(requireActivity(), toolbar, mCanvasContext.backgroundColor, requireContext().getColor(R.color.white)) + ViewStyler.themeToolbarColored(requireActivity(), toolbar, canvasContext.backgroundColor, requireContext().getColor(R.color.white)) } private fun openEditPage(page: Page) { if (APIHelper.hasNetworkConnection()) { - val args = CreateOrEditPageDetailsFragment.newInstanceEdit(mCanvasContext, page).nonNullArgs - RouteMatcher.route(requireContext(), Route(CreateOrEditPageDetailsFragment::class.java, mCanvasContext, args)) + val args = CreateOrEditPageDetailsFragment.newInstanceEdit(canvasContext, page).nonNullArgs + RouteMatcher.route(requireContext(), Route(CreateOrEditPageDetailsFragment::class.java, canvasContext, args)) } else { NoInternetConnectionDialog.show(requireFragmentManager()) } @@ -227,7 +242,7 @@ class PageDetailsFragment : BasePresenterFragment< // need to set a flag here. Because we use the event bus in the fragment instead of the presenter for unit testing purposes, // when we come back to this fragment it will go through the life cycle events again and the cached data will immediately // overwrite the data from the network if we refresh the presenter from here. - mPage = it + page = it } } @@ -242,7 +257,7 @@ class PageDetailsFragment : BasePresenterFragment< fun newInstance(canvasContext: CanvasContext, args: Bundle) = PageDetailsFragment().withArgs(args).apply { - mCanvasContext = canvasContext + this.canvasContext = canvasContext } } } diff --git a/apps/teacher/src/main/java/com/instructure/teacher/fragments/PageListFragment.kt b/apps/teacher/src/main/java/com/instructure/teacher/fragments/PageListFragment.kt index bd19f66a43..9aada0d0a7 100644 --- a/apps/teacher/src/main/java/com/instructure/teacher/fragments/PageListFragment.kt +++ b/apps/teacher/src/main/java/com/instructure/teacher/fragments/PageListFragment.kt @@ -23,6 +23,7 @@ import androidx.recyclerview.widget.RecyclerView import com.instructure.canvasapi2.models.CanvasContext import com.instructure.canvasapi2.models.Page import com.instructure.canvasapi2.utils.ApiPrefs +import com.instructure.canvasapi2.utils.pageview.PageView import com.instructure.interactions.router.Route import com.instructure.pandautils.analytics.SCREEN_VIEW_PAGE_LIST import com.instructure.pandautils.analytics.ScreenView @@ -46,21 +47,22 @@ import org.greenrobot.eventbus.EventBus import org.greenrobot.eventbus.Subscribe import org.greenrobot.eventbus.ThreadMode +@PageView(url = "{canvasContext}/pages") @ScreenView(SCREEN_VIEW_PAGE_LIST) class PageListFragment : BaseSyncFragment(), PageListView { private val binding by viewBinding(FragmentPageListBinding::bind) - private var mCanvasContext: CanvasContext by ParcelableArg(default = CanvasContext.getGenericContext(CanvasContext.Type.COURSE, -1L, "")) + private var canvasContext: CanvasContext by ParcelableArg(default = CanvasContext.getGenericContext(CanvasContext.Type.COURSE, -1L, "")) - private val mLinearLayoutManager by lazy { LinearLayoutManager(requireContext()) } + private val linearLayoutManager by lazy { LinearLayoutManager(requireContext()) } private lateinit var mRecyclerView: RecyclerView - private var mNeedToForceNetwork = false + private var needToForceNetwork = false override fun layoutResId(): Int = R.layout.fragment_page_list override val recyclerView: RecyclerView get() = binding.pageRecyclerView - override fun getPresenterFactory() = PageListPresenterFactory(mCanvasContext) + override fun getPresenterFactory() = PageListPresenterFactory(canvasContext) override fun onPresenterPrepared(presenter: PageListPresenter) = with(binding) { mRecyclerView = RecyclerViewUtils.buildRecyclerView( rootView = rootView, @@ -88,14 +90,14 @@ class PageListFragment : BaseSyncFragment + return PageListAdapter(requireContext(), presenter, canvasContext.textAndIconColor) { page -> val args = PageDetailsFragment.makeBundle(page) - RouteMatcher.route(requireContext(), Route(null, PageDetailsFragment::class.java, mCanvasContext, args)) + RouteMatcher.route(requireContext(), Route(null, PageDetailsFragment::class.java, canvasContext, args)) } } @@ -149,7 +151,7 @@ class PageListFragment : BaseSyncFragment if (query.isBlank()) { @@ -159,7 +161,7 @@ class PageListFragment : BaseSyncFragment(), PeopleListView, SearchView.OnQueryTextListener { @@ -54,7 +56,8 @@ class PeopleListFragment : BaseSyncFragment? = null + private val canvasContext: CanvasContext by ParcelableArg(key = Const.CANVAS_CONTEXT) + private var canvasContextsSelected: ArrayList? = null override fun onViewCreated(view: View, savedInstanceState: Bundle?) { swipeRefreshLayoutContainerBinding = RecyclerSwipeRefreshLayoutBinding.bind(view) @@ -107,10 +110,10 @@ class PeopleListFragment : BaseSyncFragment - mCanvasContextsSelected = ArrayList() - mCanvasContextsSelected!!.addAll(canvasContexts) + canvasContextsSelected = ArrayList() + canvasContextsSelected!!.addAll(canvasContexts) - presenter.canvasContextList = mCanvasContextsSelected as ArrayList + presenter.canvasContextList = canvasContextsSelected as ArrayList setupTitle(canvasContexts) }.show(requireActivity().supportFragmentManager, PeopleListFilterDialog::class.java.simpleName) false diff --git a/apps/teacher/src/main/java/com/instructure/teacher/fragments/ProfileEditFragment.kt b/apps/teacher/src/main/java/com/instructure/teacher/fragments/ProfileEditFragment.kt index 6d903a8995..3a601f25df 100644 --- a/apps/teacher/src/main/java/com/instructure/teacher/fragments/ProfileEditFragment.kt +++ b/apps/teacher/src/main/java/com/instructure/teacher/fragments/ProfileEditFragment.kt @@ -39,6 +39,7 @@ import com.instructure.canvasapi2.utils.ApiPrefs import com.instructure.canvasapi2.utils.ApiPrefs.user import com.instructure.canvasapi2.utils.ApiType import com.instructure.canvasapi2.utils.LinkHeaders +import com.instructure.canvasapi2.utils.pageview.PageView import com.instructure.canvasapi2.utils.validOrNull import com.instructure.pandautils.analytics.SCREEN_VIEW_PROFILE_EDIT import com.instructure.pandautils.analytics.ScreenView @@ -57,6 +58,7 @@ import com.instructure.teacher.viewinterface.ProfileEditFragmentView import retrofit2.Response import java.io.File +@PageView(url = "profile/edit") @ScreenView(SCREEN_VIEW_PROFILE_EDIT) class ProfileEditFragment : BasePresenterFragment< ProfileEditFragmentPresenter, diff --git a/apps/teacher/src/main/java/com/instructure/teacher/fragments/ProfileFragment.kt b/apps/teacher/src/main/java/com/instructure/teacher/fragments/ProfileFragment.kt index a04889f83a..d96d1cefd7 100644 --- a/apps/teacher/src/main/java/com/instructure/teacher/fragments/ProfileFragment.kt +++ b/apps/teacher/src/main/java/com/instructure/teacher/fragments/ProfileFragment.kt @@ -22,6 +22,7 @@ import android.view.View import com.instructure.canvasapi2.utils.APIHelper import com.instructure.canvasapi2.utils.ApiPrefs import com.instructure.canvasapi2.utils.Pronouns +import com.instructure.canvasapi2.utils.pageview.PageView import com.instructure.interactions.router.Route import com.instructure.pandautils.analytics.SCREEN_VIEW_PROFILE import com.instructure.pandautils.analytics.ScreenView @@ -36,6 +37,7 @@ import com.instructure.teacher.utils.adoptToolbarStyle import com.instructure.teacher.utils.setupBackButtonAsBackPressedOnly import com.instructure.teacher.utils.setupMenu +@PageView(url = "profile") @ScreenView(SCREEN_VIEW_PROFILE) class ProfileFragment : BaseFragment() { diff --git a/apps/teacher/src/main/java/com/instructure/teacher/fragments/QuizDetailsFragment.kt b/apps/teacher/src/main/java/com/instructure/teacher/fragments/QuizDetailsFragment.kt index 29e23dca83..65475c47da 100644 --- a/apps/teacher/src/main/java/com/instructure/teacher/fragments/QuizDetailsFragment.kt +++ b/apps/teacher/src/main/java/com/instructure/teacher/fragments/QuizDetailsFragment.kt @@ -21,9 +21,12 @@ import android.webkit.WebChromeClient import android.webkit.WebView import android.widget.TextView import com.instructure.canvasapi2.models.Assignment +import com.instructure.canvasapi2.models.CanvasContext import com.instructure.canvasapi2.models.Course import com.instructure.canvasapi2.models.Quiz import com.instructure.canvasapi2.utils.* +import com.instructure.canvasapi2.utils.pageview.PageView +import com.instructure.canvasapi2.utils.pageview.PageViewUrl import com.instructure.interactions.Identity import com.instructure.interactions.MasterDetailInteractions import com.instructure.interactions.router.Route @@ -32,6 +35,7 @@ import com.instructure.pandautils.analytics.ScreenView import com.instructure.pandautils.binding.viewBinding import com.instructure.pandautils.fragments.BasePresenterFragment import com.instructure.pandautils.utils.* +import com.instructure.pandautils.utils.Const import com.instructure.pandautils.views.CanvasWebView import com.instructure.teacher.R import com.instructure.teacher.activities.InternalWebViewActivity @@ -56,6 +60,7 @@ import java.net.URI import java.net.URL import java.util.* +@PageView @ScreenView(SCREEN_VIEW_EDIT_QUIZ_DETAILS) class QuizDetailsFragment : BasePresenterFragment< QuizDetailsPresenter, @@ -64,17 +69,23 @@ class QuizDetailsFragment : BasePresenterFragment< private val binding by viewBinding(FragmentQuizDetailsBinding::bind) - private var mCourse: Course by ParcelableArg(default = Course()) - private var mQuizId: Long by LongArg(0L, QUIZ_ID) - private var mQuiz: Quiz by ParcelableArg(Quiz(), QUIZ) + private var canvasContext: CanvasContext? by NullableParcelableArg(key = Const.CANVAS_CONTEXT) - private var mNeedToForceNetwork = false + private var course: Course by ParcelableArg(default = Course()) + private var quizId: Long by LongArg(0L, QUIZ_ID) + private var quiz: Quiz by ParcelableArg(Quiz(), QUIZ) + + private var needToForceNetwork = false private var loadHtmlJob: Job? = null + @Suppress("unused") + @PageViewUrl + fun makePageViewUrl() = "${ApiPrefs.fullDomain}/${course.contextId.replace("_", "s/")}/quizzes/${quizId}" + override fun layoutResId(): Int = R.layout.fragment_quiz_details - override fun getPresenterFactory() = QuizDetailsPresenterFactory(mCourse, mQuiz) + override fun getPresenterFactory() = QuizDetailsPresenterFactory(course, quiz) override fun onDestroyView() { super.onDestroyView() @@ -82,12 +93,12 @@ class QuizDetailsFragment : BasePresenterFragment< } override fun onReadySetGo(presenter: QuizDetailsPresenter) { - if (mQuizId == 0L) { + if (quizId == 0L) { // No Quiz ID so we must have a quiz - presenter.loadData(mNeedToForceNetwork) + presenter.loadData(needToForceNetwork) } else { // No Quiz, we need to get it - presenter.getQuiz(mQuizId, mCourse, true) + presenter.getQuiz(quizId, course, true) } setupToolbar() @@ -102,16 +113,16 @@ class QuizDetailsFragment : BasePresenterFragment< clearListeners() } - override val identity: Long? get() = if(mQuizId != 0L) mQuizId else mQuiz.id + override val identity: Long? get() = if(quizId != 0L) quizId else quiz.id override val skipCheck: Boolean get() = false override fun populateQuizDetails(quiz: Quiz): Unit = with(binding) { - mQuiz = quiz + this@QuizDetailsFragment.quiz = quiz toolbar.setupMenu(R.menu.menu_edit_generic) { openEditPage(quiz) } swipeRefreshLayout.isRefreshing = false setupViews(quiz) setupListeners(quiz) - ViewStyler.themeToolbarColored(requireActivity(), toolbar, mCourse.backgroundColor, requireContext().getColor(R.color.white)) + ViewStyler.themeToolbarColored(requireActivity(), toolbar, course.backgroundColor, requireContext().getColor(R.color.white)) fullDateDetailsButton.setVisible(quiz._assignment != null) } @@ -119,7 +130,7 @@ class QuizDetailsFragment : BasePresenterFragment< private fun setupToolbar() = with(binding) { toolbar.setupBackButtonWithExpandCollapseAndBack(this@QuizDetailsFragment) { toolbar.updateToolbarExpandCollapseIcon(this@QuizDetailsFragment) - ViewStyler.themeToolbarColored(requireActivity(), toolbar, mCourse.backgroundColor, requireContext().getColor(R.color.white)) + ViewStyler.themeToolbarColored(requireActivity(), toolbar, course.backgroundColor, requireContext().getColor(R.color.white)) (activity as MasterDetailInteractions).toggleExpandCollapse() } @@ -127,7 +138,7 @@ class QuizDetailsFragment : BasePresenterFragment< if (!isTablet) { toolbar.subtitle = presenter.mCourse.name } - ViewStyler.themeToolbarColored(requireActivity(), toolbar, mCourse.backgroundColor, requireContext().getColor(R.color.white)) + ViewStyler.themeToolbarColored(requireActivity(), toolbar, course.backgroundColor, requireContext().getColor(R.color.white)) } private fun setupViews(quiz: Quiz) = with(binding) { @@ -218,7 +229,7 @@ class QuizDetailsFragment : BasePresenterFragment< } // If the user is a designer we don't want to show the submissions layout - submissionsLayout.setVisible(!mCourse.isDesigner) + submissionsLayout.setVisible(!course.isDesigner) if (!quiz.isGradeable) { // Quiz is not gradeable, don't show submission dials @@ -365,7 +376,7 @@ class QuizDetailsFragment : BasePresenterFragment< // Load instructions loadHtmlJob = instructionsWebViewWrapper.webView.loadHtmlWithIframes(requireContext(), quiz.description, { - instructionsWebViewWrapper.loadHtml(it, quiz.title, baseUrl = mQuiz.htmlUrl) + instructionsWebViewWrapper.loadHtml(it, quiz.title, baseUrl = this@QuizDetailsFragment.quiz.htmlUrl) }) { LtiLaunchFragment.routeLtiLaunchFragment(requireContext(), canvasContext, it) } @@ -375,22 +386,22 @@ class QuizDetailsFragment : BasePresenterFragment< dueLayout.setOnClickListener { if(quiz._assignment != null) { val args = DueDatesFragment.makeBundle(quiz._assignment!!) - RouteMatcher.route(requireContext(), Route(null, DueDatesFragment::class.java, mCourse, args)) + RouteMatcher.route(requireContext(), Route(null, DueDatesFragment::class.java, course, args)) } } submissionsLayout.setOnClickListener { - navigateToSubmissions(mCourse, quiz._assignment, SubmissionListFilter.ALL) + navigateToSubmissions(course, quiz._assignment, SubmissionListFilter.ALL) } donutGroup.viewAllSubmissions.onClick { submissionsLayout.performClick() } // Separate click listener for a11y donutGroup.gradedWrapper.setOnClickListener { - navigateToSubmissions(mCourse, quiz._assignment, SubmissionListFilter.GRADED) + navigateToSubmissions(course, quiz._assignment, SubmissionListFilter.GRADED) } donutGroup.ungradedWrapper.setOnClickListener { - navigateToSubmissions(mCourse, quiz._assignment, SubmissionListFilter.NOT_GRADED) + navigateToSubmissions(course, quiz._assignment, SubmissionListFilter.NOT_GRADED) } donutGroup.notSubmittedWrapper.setOnClickListener { - navigateToSubmissions(mCourse, quiz._assignment, SubmissionListFilter.MISSING) + navigateToSubmissions(course, quiz._assignment, SubmissionListFilter.MISSING) } noInstructionsTextView.setOnClickListener { openEditPage(quiz) @@ -399,19 +410,19 @@ class QuizDetailsFragment : BasePresenterFragment< ViewStyler.themeButton(quizPreviewButton.quizPreviewButton) quizPreviewButton.quizPreviewButton.setOnClickListener { try { - var urlStr = mQuiz.htmlUrl +"/take?preview=1&persist_headless=1" + var urlStr = this@QuizDetailsFragment.quiz.htmlUrl +"/take?preview=1&persist_headless=1" val url = URL(urlStr) val uri = URI(url.protocol, url.userInfo, url.host, url.port, url.path, url.query, url.ref) urlStr = uri.toASCIIString() val args = QuizPreviewWebviewFragment.makeBundle(urlStr, getString(R.string.quizPreview)) - RouteMatcher.route(requireContext(), Route(QuizPreviewWebviewFragment::class.java, mCourse, args)) + RouteMatcher.route(requireContext(), Route(QuizPreviewWebviewFragment::class.java, course, args)) } catch (e: UnsupportedEncodingException) {} } } private fun navigateToSubmissions(course: Course, assignment: Assignment?, filter: SubmissionListFilter) { assignment ?: return // We can't navigate to the submission list if there isn't an associated assignment - val assignmentWithAnonymousGrading = assignment.copy(anonymousGrading = mQuiz.allowAnonymousSubmissions) + val assignmentWithAnonymousGrading = assignment.copy(anonymousGrading = quiz.allowAnonymousSubmissions) val args = AssignmentSubmissionListFragment.makeBundle(assignmentWithAnonymousGrading, filter) RouteMatcher.route(requireContext(), Route(null, AssignmentSubmissionListFragment::class.java, course, args)) } @@ -428,7 +439,7 @@ class QuizDetailsFragment : BasePresenterFragment< private fun openEditPage(quiz: Quiz) { if(APIHelper.hasNetworkConnection()) { val args = EditQuizDetailsFragment.makeBundle(quiz, false) - RouteMatcher.route(requireContext(), Route(EditQuizDetailsFragment::class.java, mCourse, args)) + RouteMatcher.route(requireContext(), Route(EditQuizDetailsFragment::class.java, course, args)) } else { NoInternetConnectionDialog.show(requireFragmentManager()) } @@ -448,7 +459,7 @@ class QuizDetailsFragment : BasePresenterFragment< @Subscribe(threadMode = ThreadMode.MAIN, sticky = true) fun onQuizEdited(event: QuizUpdatedEvent) { event.once(javaClass.simpleName) { - if (it == presenter.mQuiz.id) mNeedToForceNetwork = true + if (it == presenter.mQuiz.id) needToForceNetwork = true } } @@ -456,7 +467,7 @@ class QuizDetailsFragment : BasePresenterFragment< @Subscribe(threadMode = ThreadMode.MAIN, sticky = true) fun onQuizGraded(event: QuizSubmissionGradedEvent) { event.once(javaClass.simpleName) { - if (presenter.mQuiz.assignmentId == it.assignmentId) mNeedToForceNetwork = true + if (presenter.mQuiz.assignmentId == it.assignmentId) needToForceNetwork = true } } @@ -468,6 +479,6 @@ class QuizDetailsFragment : BasePresenterFragment< fun makeBundle(quiz: Quiz): Bundle = Bundle().apply { putParcelable(QuizDetailsFragment.QUIZ, quiz) } - fun newInstance(course: Course, args: Bundle) = QuizDetailsFragment().withArgs(args).apply { mCourse = course } + fun newInstance(course: Course, args: Bundle) = QuizDetailsFragment().withArgs(args).apply { this.course = course } } } diff --git a/apps/teacher/src/main/java/com/instructure/teacher/fragments/QuizListFragment.kt b/apps/teacher/src/main/java/com/instructure/teacher/fragments/QuizListFragment.kt index e6a50ea6b1..f6ffe4d71f 100644 --- a/apps/teacher/src/main/java/com/instructure/teacher/fragments/QuizListFragment.kt +++ b/apps/teacher/src/main/java/com/instructure/teacher/fragments/QuizListFragment.kt @@ -23,6 +23,7 @@ import androidx.recyclerview.widget.RecyclerView import com.instructure.canvasapi2.models.CanvasContext import com.instructure.canvasapi2.models.Quiz import com.instructure.canvasapi2.utils.ApiPrefs +import com.instructure.canvasapi2.utils.pageview.PageView import com.instructure.interactions.router.Route import com.instructure.pandautils.analytics.SCREEN_VIEW_QUIZ_LIST import com.instructure.pandautils.analytics.ScreenView @@ -44,6 +45,7 @@ import org.greenrobot.eventbus.EventBus import org.greenrobot.eventbus.Subscribe import org.greenrobot.eventbus.ThreadMode +@PageView("{canvasContext}/quizzes") @ScreenView(SCREEN_VIEW_QUIZ_LIST) class QuizListFragment : BaseExpandableSyncFragment< String, @@ -54,18 +56,18 @@ class QuizListFragment : BaseExpandableSyncFragment< private val binding by viewBinding(FragmentQuizListBinding::bind) - private var mCanvasContext: CanvasContext by ParcelableArg(default = CanvasContext.getGenericContext(CanvasContext.Type.COURSE, -1L, "")) + private var canvasContext: CanvasContext by ParcelableArg(default = CanvasContext.getGenericContext(CanvasContext.Type.COURSE, -1L, "")) - private val mLinearLayoutManager by lazy { LinearLayoutManager(requireContext()) } + private val linearLayoutManager by lazy { LinearLayoutManager(requireContext()) } private lateinit var mRecyclerView: RecyclerView - private var mGradingPeriodMenu: PopupMenu? = null + private var gradingPeriodMenu: PopupMenu? = null - private var mNeedToForceNetwork = false + private var needToForceNetwork = false override fun layoutResId(): Int = R.layout.fragment_quiz_list override val recyclerView: RecyclerView get() = binding.quizRecyclerView - override fun getPresenterFactory() = QuizListPresenterFactory(mCanvasContext) + override fun getPresenterFactory() = QuizListPresenterFactory(canvasContext) override fun onPresenterPrepared(presenter: QuizListPresenter) { mRecyclerView = RecyclerViewUtils.buildRecyclerView( rootView = rootView, @@ -80,14 +82,14 @@ class QuizListFragment : BaseExpandableSyncFragment< } override fun onCreateView(view: View) { - mLinearLayoutManager.orientation = RecyclerView.VERTICAL + linearLayoutManager.orientation = RecyclerView.VERTICAL } override fun onReadySetGo(presenter: QuizListPresenter) { if(recyclerView.adapter == null) { mRecyclerView.adapter = adapter } - presenter.loadData(mNeedToForceNetwork) + presenter.loadData(needToForceNetwork) } override fun onResume() { @@ -106,19 +108,19 @@ class QuizListFragment : BaseExpandableSyncFragment< } override fun onPause() { - if(mGradingPeriodMenu != null) { - mGradingPeriodMenu?.dismiss() + if(gradingPeriodMenu != null) { + gradingPeriodMenu?.dismiss() } super.onPause() } override fun createAdapter(): QuizListAdapter { - return QuizListAdapter(requireContext(), presenter, mCanvasContext.textAndIconColor) { quiz -> + return QuizListAdapter(requireContext(), presenter, canvasContext.textAndIconColor) { quiz -> if (RouteMatcher.canRouteInternally(requireActivity(), quiz.htmlUrl, ApiPrefs.domain, false)) { RouteMatcher.routeUrl(requireActivity(), quiz.htmlUrl!!, ApiPrefs.domain) } else { val args = QuizDetailsFragment.makeBundle(quiz) - RouteMatcher.route(requireContext(), Route(null, QuizDetailsFragment::class.java, mCanvasContext, args)) + RouteMatcher.route(requireContext(), Route(null, QuizDetailsFragment::class.java, canvasContext, args)) } } } @@ -146,7 +148,7 @@ class QuizListFragment : BaseExpandableSyncFragment< private fun setupToolbar() = with(binding) { quizListToolbar.title = getString(R.string.tab_quizzes) - quizListToolbar.subtitle = mCanvasContext.name + quizListToolbar.subtitle = canvasContext.name quizListToolbar.setupBackButton(this@QuizListFragment) quizListToolbar.addSearch(getString(R.string.searchQuizzesHint)) { query -> if (query.isBlank()) { @@ -156,7 +158,7 @@ class QuizListFragment : BaseExpandableSyncFragment< } presenter.searchQuery = query } - ViewStyler.themeToolbarColored(requireActivity(), quizListToolbar, mCanvasContext.backgroundColor, requireContext().getColor(R.color.white)) + ViewStyler.themeToolbarColored(requireActivity(), quizListToolbar, canvasContext.backgroundColor, requireContext().getColor(R.color.white)) } override fun displayLoadingError() = toast(R.string.errorOccurred) @@ -168,14 +170,14 @@ class QuizListFragment : BaseExpandableSyncFragment< // need to set a flag here. Because we use the event bus in the fragment instead of the presenter for unit testing purposes, // when we come back to this fragment it will go through the life cycle events again and the cached data will immediately // overwrite the data from the network if we refresh the presenter from here. - mNeedToForceNetwork = true + needToForceNetwork = true } } @Suppress("unused") @Subscribe(threadMode = ThreadMode.MAIN, sticky = true) fun onQuizGraded(event: QuizSubmissionGradedEvent) { - event.once(javaClass.simpleName) { mNeedToForceNetwork = true } + event.once(javaClass.simpleName) { needToForceNetwork = true } } override fun onHandleBackPressed() = binding.quizListToolbar.closeSearch() @@ -183,7 +185,7 @@ class QuizListFragment : BaseExpandableSyncFragment< companion object { fun newInstance(canvasContext: CanvasContext) = QuizListFragment().apply { - mCanvasContext = canvasContext + this.canvasContext = canvasContext } } } diff --git a/apps/teacher/src/main/java/com/instructure/teacher/fragments/SettingsFragment.kt b/apps/teacher/src/main/java/com/instructure/teacher/fragments/SettingsFragment.kt index d2baca49ea..250435f453 100644 --- a/apps/teacher/src/main/java/com/instructure/teacher/fragments/SettingsFragment.kt +++ b/apps/teacher/src/main/java/com/instructure/teacher/fragments/SettingsFragment.kt @@ -18,6 +18,7 @@ package com.instructure.teacher.fragments import android.os.Bundle import android.view.View +import com.instructure.canvasapi2.models.CanvasContext import com.instructure.interactions.router.Route import com.instructure.pandautils.analytics.SCREEN_VIEW_SETTINGS import com.instructure.pandautils.analytics.ScreenView @@ -45,6 +46,8 @@ class SettingsFragment : BasePresenterFragment(), ToDoView { diff --git a/apps/teacher/src/main/java/com/instructure/teacher/services/TeacherPageViewService.kt b/apps/teacher/src/main/java/com/instructure/teacher/services/TeacherPageViewService.kt new file mode 100644 index 0000000000..b43aade4ae --- /dev/null +++ b/apps/teacher/src/main/java/com/instructure/teacher/services/TeacherPageViewService.kt @@ -0,0 +1,27 @@ +package com.instructure.teacher.services + +import com.google.firebase.crashlytics.FirebaseCrashlytics +import com.instructure.canvasapi2.utils.pageview.PageViewUploadService +import com.instructure.canvasapi2.utils.pageview.PandataInfo +import com.instructure.teacher.BuildConfig + +/** + * A [PageViewUploadService] specific to the Teacher application. + * + * To test this service, install the app on a device running Android 7.1+ and run the following command: + * adb shell cmd jobscheduler run -f com.instructure.teacher 188372 + */ +class TeacherPageViewService : PageViewUploadService() { + + override val appKey = pandataAppKey + + override fun onException(e: Throwable) = FirebaseCrashlytics.getInstance().recordException(e) + + + companion object { + val pandataAppKey = PandataInfo.AppKey( + "CANVAS_TEACHER_ANDROID", + "Canvas Teacher for Android - ${BuildConfig.VERSION_NAME} (${BuildConfig.VERSION_CODE})" + ) + } +} diff --git a/apps/teacher/src/main/java/com/instructure/teacher/utils/BaseAppManager.kt b/apps/teacher/src/main/java/com/instructure/teacher/utils/BaseAppManager.kt index e371038f86..f79b57adcc 100644 --- a/apps/teacher/src/main/java/com/instructure/teacher/utils/BaseAppManager.kt +++ b/apps/teacher/src/main/java/com/instructure/teacher/utils/BaseAppManager.kt @@ -27,6 +27,7 @@ import com.heapanalytics.android.Heap import com.heapanalytics.android.config.Options import com.instructure.annotations.FileCaching.FileCache import com.instructure.canvasapi2.utils.* +import com.instructure.canvasapi2.utils.pageview.PageViewUploadService import com.instructure.loginapi.login.tasks.LogoutTask import com.instructure.pandautils.utils.AppTheme import com.instructure.pandautils.utils.ColorKeeper @@ -34,6 +35,7 @@ import com.instructure.pandautils.utils.Const import com.instructure.pandautils.utils.ThemePrefs import com.instructure.teacher.BuildConfig import com.instructure.teacher.R +import com.instructure.teacher.services.TeacherPageViewService import com.instructure.teacher.tasks.TeacherLogoutTask import com.pspdfkit.PSPDFKit import com.pspdfkit.exceptions.InvalidPSPDFKitLicenseException @@ -88,6 +90,8 @@ abstract class BaseAppManager : com.instructure.canvasapi2.AppManager(), Configu val options = Options() options.disableTracking() Heap.init(this, BuildConfig.HEAP_APP_ID, options) + + PageViewUploadService.schedule(this, TeacherPageViewService::class.java) } override fun performLogoutOnAuthError() { diff --git a/libs/canvas-api-2/src/main/java/com/instructure/canvasapi2/utils/pageview/PageViewUploadService.kt b/libs/canvas-api-2/src/main/java/com/instructure/canvasapi2/utils/pageview/PageViewUploadService.kt index c941720c02..2945c2c42d 100644 --- a/libs/canvas-api-2/src/main/java/com/instructure/canvasapi2/utils/pageview/PageViewUploadService.kt +++ b/libs/canvas-api-2/src/main/java/com/instructure/canvasapi2/utils/pageview/PageViewUploadService.kt @@ -22,13 +22,15 @@ import android.app.job.JobScheduler import android.app.job.JobService import android.content.ComponentName import android.content.Context -import android.os.Build import androidx.annotation.RequiresPermission -import com.instructure.canvasapi2.utils.* +import com.instructure.canvasapi2.utils.ApiPrefs +import com.instructure.canvasapi2.utils.Logger +import com.instructure.canvasapi2.utils.isValid import com.instructure.canvasapi2.utils.weave.WeaveJob import com.instructure.canvasapi2.utils.weave.awaitApi import com.instructure.canvasapi2.utils.weave.catch import com.instructure.canvasapi2.utils.weave.tryWeave +import com.instructure.canvasapi2.utils.zonedDateTime import org.threeten.bp.format.DateTimeFormatter import java.util.* import java.util.concurrent.TimeUnit @@ -48,7 +50,6 @@ abstract class PageViewUploadService : JobService() { * which a concrete implementation of this [PageViewUploadService] is declared. */ abstract val appKey: PandataInfo.AppKey - /** * Called if an exception occurs while uploading events. It is recommended to log these exceptions in a * highly visible location (e.g. Crashlytics). diff --git a/libs/pandautils/src/main/java/com/instructure/pandautils/fragments/BaseExpandableSyncFragment.kt b/libs/pandautils/src/main/java/com/instructure/pandautils/fragments/BaseExpandableSyncFragment.kt index 0108f71fcd..9b660edb93 100644 --- a/libs/pandautils/src/main/java/com/instructure/pandautils/fragments/BaseExpandableSyncFragment.kt +++ b/libs/pandautils/src/main/java/com/instructure/pandautils/fragments/BaseExpandableSyncFragment.kt @@ -27,7 +27,6 @@ import com.instructure.canvasapi2.models.CanvasComparable import com.instructure.canvasapi2.models.CanvasContext import com.instructure.pandautils.interfaces.NavigationCallbacks import com.instructure.pandautils.utils.Const -import com.instructure.pandautils.utils.NullableParcelableArg import instructure.androidblueprint.SyncExpandableFragment import instructure.androidblueprint.SyncExpandableManager import instructure.androidblueprint.SyncExpandablePresenter @@ -41,7 +40,6 @@ abstract class BaseExpandableSyncFragment< HOLDER : RecyclerView.ViewHolder, ADAPTER : SyncExpandableRecyclerAdapter> : SyncExpandableFragment(), NavigationCallbacks { - var canvasContext: CanvasContext? by NullableParcelableArg(key = Const.CANVAS_CONTEXT) protected lateinit var rootView: View abstract fun layoutResId(): Int diff --git a/libs/pandautils/src/main/java/com/instructure/pandautils/fragments/BaseFragment.kt b/libs/pandautils/src/main/java/com/instructure/pandautils/fragments/BaseFragment.kt index a0ac2068fb..b0eb221d0d 100644 --- a/libs/pandautils/src/main/java/com/instructure/pandautils/fragments/BaseFragment.kt +++ b/libs/pandautils/src/main/java/com/instructure/pandautils/fragments/BaseFragment.kt @@ -26,10 +26,8 @@ import androidx.fragment.app.Fragment import com.instructure.canvasapi2.models.CanvasContext import com.instructure.pandautils.interfaces.NavigationCallbacks import com.instructure.pandautils.utils.Const -import com.instructure.pandautils.utils.NullableParcelableArg abstract class BaseFragment : Fragment(), NavigationCallbacks { - var canvasContext: CanvasContext? by NullableParcelableArg(key = Const.CANVAS_CONTEXT) protected lateinit var rootView: View abstract fun layoutResId(): Int diff --git a/libs/pandautils/src/main/java/com/instructure/pandautils/fragments/BasePresenterFragment.kt b/libs/pandautils/src/main/java/com/instructure/pandautils/fragments/BasePresenterFragment.kt index 8bded149b4..dbff04123f 100644 --- a/libs/pandautils/src/main/java/com/instructure/pandautils/fragments/BasePresenterFragment.kt +++ b/libs/pandautils/src/main/java/com/instructure/pandautils/fragments/BasePresenterFragment.kt @@ -25,14 +25,12 @@ import androidx.annotation.StringRes import com.instructure.canvasapi2.models.CanvasContext import com.instructure.pandautils.interfaces.NavigationCallbacks import com.instructure.pandautils.utils.Const -import com.instructure.pandautils.utils.NullableParcelableArg import instructure.androidblueprint.FragmentPresenter import instructure.androidblueprint.FragmentViewInterface import instructure.androidblueprint.PresenterFragment abstract class BasePresenterFragment, VIEW : FragmentViewInterface> : PresenterFragment(), NavigationCallbacks { - var canvasContext: CanvasContext? by NullableParcelableArg(key = Const.CANVAS_CONTEXT) protected lateinit var rootView: View abstract fun layoutResId(): Int diff --git a/libs/pandautils/src/main/java/com/instructure/pandautils/fragments/BaseSyncFragment.kt b/libs/pandautils/src/main/java/com/instructure/pandautils/fragments/BaseSyncFragment.kt index 79459332af..4d3a15983a 100644 --- a/libs/pandautils/src/main/java/com/instructure/pandautils/fragments/BaseSyncFragment.kt +++ b/libs/pandautils/src/main/java/com/instructure/pandautils/fragments/BaseSyncFragment.kt @@ -27,7 +27,6 @@ import com.instructure.canvasapi2.models.CanvasComparable import com.instructure.canvasapi2.models.CanvasContext import com.instructure.pandautils.interfaces.NavigationCallbacks import com.instructure.pandautils.utils.Const -import com.instructure.pandautils.utils.NullableParcelableArg import instructure.androidblueprint.SyncFragment import instructure.androidblueprint.SyncManager import instructure.androidblueprint.SyncPresenter @@ -40,7 +39,6 @@ abstract class BaseSyncFragment< HOLDER : RecyclerView.ViewHolder, ADAPTER : SyncRecyclerAdapter> : SyncFragment(), NavigationCallbacks { - var canvasContext: CanvasContext? by NullableParcelableArg(key = Const.CANVAS_CONTEXT) protected lateinit var rootView: View abstract fun layoutResId(): Int From de2511198a6104441d476170b03697b997151df6 Mon Sep 17 00:00:00 2001 From: inst-danger Date: Thu, 18 May 2023 13:55:34 +0200 Subject: [PATCH 037/103] Update translations --- apps/flutter_parent/lib/l10n/res/intl_ar.arb | 50 ++++++++++++++++++- apps/flutter_parent/lib/l10n/res/intl_ca.arb | 50 ++++++++++++++++++- apps/flutter_parent/lib/l10n/res/intl_cy.arb | 50 ++++++++++++++++++- apps/flutter_parent/lib/l10n/res/intl_da.arb | 50 ++++++++++++++++++- .../lib/l10n/res/intl_da_instk12.arb | 50 ++++++++++++++++++- apps/flutter_parent/lib/l10n/res/intl_de.arb | 50 ++++++++++++++++++- .../lib/l10n/res/intl_en_AU.arb | 50 ++++++++++++++++++- .../lib/l10n/res/intl_en_AU_unimelb.arb | 50 ++++++++++++++++++- .../lib/l10n/res/intl_en_CY.arb | 50 ++++++++++++++++++- .../lib/l10n/res/intl_en_GB.arb | 50 ++++++++++++++++++- .../lib/l10n/res/intl_en_GB_instukhe.arb | 50 ++++++++++++++++++- apps/flutter_parent/lib/l10n/res/intl_es.arb | 50 ++++++++++++++++++- .../lib/l10n/res/intl_es_ES.arb | 50 ++++++++++++++++++- apps/flutter_parent/lib/l10n/res/intl_fi.arb | 50 ++++++++++++++++++- apps/flutter_parent/lib/l10n/res/intl_fr.arb | 50 ++++++++++++++++++- .../lib/l10n/res/intl_fr_CA.arb | 50 ++++++++++++++++++- apps/flutter_parent/lib/l10n/res/intl_ht.arb | 50 ++++++++++++++++++- apps/flutter_parent/lib/l10n/res/intl_is.arb | 50 ++++++++++++++++++- apps/flutter_parent/lib/l10n/res/intl_it.arb | 50 ++++++++++++++++++- apps/flutter_parent/lib/l10n/res/intl_ja.arb | 50 ++++++++++++++++++- apps/flutter_parent/lib/l10n/res/intl_mi.arb | 50 ++++++++++++++++++- apps/flutter_parent/lib/l10n/res/intl_ms.arb | 50 ++++++++++++++++++- apps/flutter_parent/lib/l10n/res/intl_nb.arb | 50 ++++++++++++++++++- .../lib/l10n/res/intl_nb_instk12.arb | 50 ++++++++++++++++++- apps/flutter_parent/lib/l10n/res/intl_nl.arb | 50 ++++++++++++++++++- apps/flutter_parent/lib/l10n/res/intl_pl.arb | 50 ++++++++++++++++++- .../lib/l10n/res/intl_pt_BR.arb | 50 ++++++++++++++++++- .../lib/l10n/res/intl_pt_PT.arb | 50 ++++++++++++++++++- apps/flutter_parent/lib/l10n/res/intl_ru.arb | 50 ++++++++++++++++++- apps/flutter_parent/lib/l10n/res/intl_sl.arb | 50 ++++++++++++++++++- apps/flutter_parent/lib/l10n/res/intl_sv.arb | 50 ++++++++++++++++++- .../lib/l10n/res/intl_sv_instk12.arb | 50 ++++++++++++++++++- apps/flutter_parent/lib/l10n/res/intl_th.arb | 50 ++++++++++++++++++- apps/flutter_parent/lib/l10n/res/intl_vi.arb | 50 ++++++++++++++++++- apps/flutter_parent/lib/l10n/res/intl_zh.arb | 50 ++++++++++++++++++- .../lib/l10n/res/intl_zh_HK.arb | 50 ++++++++++++++++++- .../lib/l10n/res/intl_zh_Hans.arb | 50 ++++++++++++++++++- .../lib/l10n/res/intl_zh_Hant.arb | 50 ++++++++++++++++++- .../lib/l10n/res/intl_ar.arb | 15 +++++- .../lib/l10n/res/intl_ca.arb | 15 +++++- .../lib/l10n/res/intl_cy.arb | 15 +++++- .../lib/l10n/res/intl_da.arb | 15 +++++- .../lib/l10n/res/intl_da_instk12.arb | 15 +++++- .../lib/l10n/res/intl_de.arb | 15 +++++- .../lib/l10n/res/intl_en_AU.arb | 15 +++++- .../lib/l10n/res/intl_en_AU_unimelb.arb | 15 +++++- .../lib/l10n/res/intl_en_CY.arb | 15 +++++- .../lib/l10n/res/intl_en_GB.arb | 15 +++++- .../lib/l10n/res/intl_en_GB_instukhe.arb | 23 +++++++-- .../lib/l10n/res/intl_es.arb | 15 +++++- .../lib/l10n/res/intl_es_ES.arb | 15 +++++- .../lib/l10n/res/intl_fi.arb | 15 +++++- .../lib/l10n/res/intl_fr.arb | 15 +++++- .../lib/l10n/res/intl_fr_CA.arb | 15 +++++- .../lib/l10n/res/intl_ht.arb | 15 +++++- .../lib/l10n/res/intl_is.arb | 15 +++++- .../lib/l10n/res/intl_it.arb | 15 +++++- .../lib/l10n/res/intl_ja.arb | 15 +++++- .../lib/l10n/res/intl_mi.arb | 15 +++++- .../lib/l10n/res/intl_ms.arb | 15 +++++- .../lib/l10n/res/intl_nb.arb | 15 +++++- .../lib/l10n/res/intl_nb_instk12.arb | 15 +++++- .../lib/l10n/res/intl_nl.arb | 15 +++++- .../lib/l10n/res/intl_pl.arb | 15 +++++- .../lib/l10n/res/intl_pt_BR.arb | 15 +++++- .../lib/l10n/res/intl_pt_PT.arb | 15 +++++- .../lib/l10n/res/intl_ru.arb | 15 +++++- .../lib/l10n/res/intl_sl.arb | 15 +++++- .../lib/l10n/res/intl_sv.arb | 15 +++++- .../lib/l10n/res/intl_sv_instk12.arb | 15 +++++- .../lib/l10n/res/intl_th.arb | 15 +++++- .../lib/l10n/res/intl_vi.arb | 15 +++++- .../lib/l10n/res/intl_zh.arb | 15 +++++- .../lib/l10n/res/intl_zh_HK.arb | 15 +++++- .../lib/l10n/res/intl_zh_Hans.arb | 23 +++++++-- .../lib/l10n/res/intl_zh_Hant.arb | 23 +++++++-- .../src/main/res/values-ar/strings.xml | 17 ++++++- .../res/values-b+da+DK+instk12/strings.xml | 13 ++++- .../res/values-b+en+AU+unimelb/strings.xml | 15 +++++- .../res/values-b+en+GB+instukhe/strings.xml | 15 +++++- .../res/values-b+nb+NO+instk12/strings.xml | 13 ++++- .../res/values-b+sv+SE+instk12/strings.xml | 15 +++++- .../src/main/res/values-b+zh+HK/strings.xml | 14 +++++- .../src/main/res/values-b+zh+Hans/strings.xml | 14 +++++- .../src/main/res/values-b+zh+Hant/strings.xml | 14 +++++- .../src/main/res/values-ca/strings.xml | 15 +++++- .../src/main/res/values-cy/strings.xml | 15 +++++- .../src/main/res/values-da/strings.xml | 13 ++++- .../src/main/res/values-de/strings.xml | 15 +++++- .../src/main/res/values-en-rAU/strings.xml | 15 +++++- .../src/main/res/values-en-rCY/strings.xml | 15 +++++- .../src/main/res/values-en-rGB/strings.xml | 15 +++++- .../src/main/res/values-es-rES/strings.xml | 15 +++++- .../src/main/res/values-es/strings.xml | 13 ++++- .../src/main/res/values-fi/strings.xml | 15 +++++- .../src/main/res/values-fr-rCA/strings.xml | 15 +++++- .../src/main/res/values-fr/strings.xml | 15 +++++- .../src/main/res/values-ht/strings.xml | 13 ++++- .../src/main/res/values-is/strings.xml | 13 ++++- .../src/main/res/values-it/strings.xml | 13 ++++- .../src/main/res/values-ja/strings.xml | 14 +++++- .../src/main/res/values-mi/strings.xml | 15 +++++- .../src/main/res/values-ms/strings.xml | 13 ++++- .../src/main/res/values-nb/strings.xml | 13 ++++- .../src/main/res/values-nl/strings.xml | 15 +++++- .../src/main/res/values-pl/strings.xml | 17 ++++++- .../src/main/res/values-pt-rBR/strings.xml | 15 +++++- .../src/main/res/values-pt-rPT/strings.xml | 15 +++++- .../src/main/res/values-ru/strings.xml | 15 +++++- .../src/main/res/values-sl/strings.xml | 13 ++++- .../src/main/res/values-sv/strings.xml | 15 +++++- .../src/main/res/values-th/strings.xml | 13 ++++- .../src/main/res/values-vi/strings.xml | 13 ++++- .../src/main/res/values-zh/strings.xml | 14 +++++- 114 files changed, 2889 insertions(+), 150 deletions(-) diff --git a/apps/flutter_parent/lib/l10n/res/intl_ar.arb b/apps/flutter_parent/lib/l10n/res/intl_ar.arb index 7dfec02c20..fd7594f1fd 100644 --- a/apps/flutter_parent/lib/l10n/res/intl_ar.arb +++ b/apps/flutter_parent/lib/l10n/res/intl_ar.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2023-02-17T11:03:20.619429", + "@@last_modified": "2023-04-14T11:04:46.988317", "alertsLabel": "التنبيهات", "@alertsLabel": { "description": "The label for the Alerts tab", @@ -182,6 +182,19 @@ "points": {} } }, + "calendarDaySemanticsLabel": "{eventCount,plural, =1{{date}، {eventCount} حدث}other{{date}، {eventCount} أحداث}}", + "@calendarDaySemanticsLabel": { + "description": "Screen reader label used for calendar day, reads the date and count of events", + "type": "text", + "placeholders_order": [ + "date", + "eventCount" + ], + "placeholders": { + "date": {}, + "eventCount": {} + } + }, "No Events Today!": "لا توجد أحداث اليوم!", "@No Events Today!": { "description": "Title displayed when there are no calendar events for the current day", @@ -2694,5 +2707,40 @@ "type": "text", "placeholders_order": [], "placeholders": {} + }, + "About": "نبذة", + "@About": { + "description": "Title for about menu item in settings", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "App": "التطبيق", + "@App": { + "description": "Title for App field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "Login ID": "معرف تسجيل الدخول", + "@Login ID": { + "description": "Title for Login ID field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "Email": "البريد الإلكتروني", + "@Email": { + "description": "Title for Email field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "Version": "الإصدار", + "@Version": { + "description": "Title for Version field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} } } \ No newline at end of file diff --git a/apps/flutter_parent/lib/l10n/res/intl_ca.arb b/apps/flutter_parent/lib/l10n/res/intl_ca.arb index 16aea4a2ce..9f2971adb7 100644 --- a/apps/flutter_parent/lib/l10n/res/intl_ca.arb +++ b/apps/flutter_parent/lib/l10n/res/intl_ca.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2023-02-17T11:03:20.619429", + "@@last_modified": "2023-04-14T11:04:46.988317", "alertsLabel": "Avisos", "@alertsLabel": { "description": "The label for the Alerts tab", @@ -182,6 +182,19 @@ "points": {} } }, + "calendarDaySemanticsLabel": "{eventCount,plural, =1{{date}, {eventCount} esdeveniment}other{{date}, {eventCount} esdeveniments}}", + "@calendarDaySemanticsLabel": { + "description": "Screen reader label used for calendar day, reads the date and count of events", + "type": "text", + "placeholders_order": [ + "date", + "eventCount" + ], + "placeholders": { + "date": {}, + "eventCount": {} + } + }, "No Events Today!": "Avui no hi ha cap esdeveniment.", "@No Events Today!": { "description": "Title displayed when there are no calendar events for the current day", @@ -2694,5 +2707,40 @@ "type": "text", "placeholders_order": [], "placeholders": {} + }, + "About": "Quant a", + "@About": { + "description": "Title for about menu item in settings", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "App": "Aplicació", + "@App": { + "description": "Title for App field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "Login ID": "ID d'inici de sessió", + "@Login ID": { + "description": "Title for Login ID field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "Email": "Correu electrònic", + "@Email": { + "description": "Title for Email field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "Version": "Versió", + "@Version": { + "description": "Title for Version field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} } } \ No newline at end of file diff --git a/apps/flutter_parent/lib/l10n/res/intl_cy.arb b/apps/flutter_parent/lib/l10n/res/intl_cy.arb index 4a08e4990b..3f62546257 100644 --- a/apps/flutter_parent/lib/l10n/res/intl_cy.arb +++ b/apps/flutter_parent/lib/l10n/res/intl_cy.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2023-02-17T11:03:20.619429", + "@@last_modified": "2023-04-14T11:04:46.988317", "alertsLabel": "Negeseuon Hysbysu", "@alertsLabel": { "description": "The label for the Alerts tab", @@ -182,6 +182,19 @@ "points": {} } }, + "calendarDaySemanticsLabel": "{eventCount,plural, =1{{date}, {eventCount} digwyddiad}other{{date}, {eventCount} digwyddiad}}", + "@calendarDaySemanticsLabel": { + "description": "Screen reader label used for calendar day, reads the date and count of events", + "type": "text", + "placeholders_order": [ + "date", + "eventCount" + ], + "placeholders": { + "date": {}, + "eventCount": {} + } + }, "No Events Today!": "Dim Digwyddiadau Heddiw!", "@No Events Today!": { "description": "Title displayed when there are no calendar events for the current day", @@ -2694,5 +2707,40 @@ "type": "text", "placeholders_order": [], "placeholders": {} + }, + "About": "Ynghylch", + "@About": { + "description": "Title for about menu item in settings", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "App": "Ap", + "@App": { + "description": "Title for App field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "Login ID": "ID Mewngofnodi", + "@Login ID": { + "description": "Title for Login ID field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "Email": "E-bost", + "@Email": { + "description": "Title for Email field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "Version": "Fersiwn", + "@Version": { + "description": "Title for Version field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} } } \ No newline at end of file diff --git a/apps/flutter_parent/lib/l10n/res/intl_da.arb b/apps/flutter_parent/lib/l10n/res/intl_da.arb index a9ce16b6d5..30f0d4b2ba 100644 --- a/apps/flutter_parent/lib/l10n/res/intl_da.arb +++ b/apps/flutter_parent/lib/l10n/res/intl_da.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2023-02-17T11:03:20.619429", + "@@last_modified": "2023-04-14T11:04:46.988317", "alertsLabel": "Varslinger", "@alertsLabel": { "description": "The label for the Alerts tab", @@ -182,6 +182,19 @@ "points": {} } }, + "calendarDaySemanticsLabel": "{eventCount,plural, =1{{date}, {eventCount} begivenhed}other{{date}, {eventCount} begivenheder}}", + "@calendarDaySemanticsLabel": { + "description": "Screen reader label used for calendar day, reads the date and count of events", + "type": "text", + "placeholders_order": [ + "date", + "eventCount" + ], + "placeholders": { + "date": {}, + "eventCount": {} + } + }, "No Events Today!": "Ingen begivenheder i dag!", "@No Events Today!": { "description": "Title displayed when there are no calendar events for the current day", @@ -2694,5 +2707,40 @@ "type": "text", "placeholders_order": [], "placeholders": {} + }, + "About": "Om", + "@About": { + "description": "Title for about menu item in settings", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "App": "App", + "@App": { + "description": "Title for App field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "Login ID": "Login-id", + "@Login ID": { + "description": "Title for Login ID field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "Email": "E-mail", + "@Email": { + "description": "Title for Email field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "Version": "Version", + "@Version": { + "description": "Title for Version field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} } } \ No newline at end of file diff --git a/apps/flutter_parent/lib/l10n/res/intl_da_instk12.arb b/apps/flutter_parent/lib/l10n/res/intl_da_instk12.arb index 39dcc14673..02860edf64 100644 --- a/apps/flutter_parent/lib/l10n/res/intl_da_instk12.arb +++ b/apps/flutter_parent/lib/l10n/res/intl_da_instk12.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2023-02-17T11:03:20.619429", + "@@last_modified": "2023-04-14T11:04:46.988317", "alertsLabel": "Varslinger", "@alertsLabel": { "description": "The label for the Alerts tab", @@ -182,6 +182,19 @@ "points": {} } }, + "calendarDaySemanticsLabel": "{eventCount,plural, =1{{date}, {eventCount} begivenhed}other{{date}, {eventCount} begivenheder}}", + "@calendarDaySemanticsLabel": { + "description": "Screen reader label used for calendar day, reads the date and count of events", + "type": "text", + "placeholders_order": [ + "date", + "eventCount" + ], + "placeholders": { + "date": {}, + "eventCount": {} + } + }, "No Events Today!": "Ingen begivenheder i dag!", "@No Events Today!": { "description": "Title displayed when there are no calendar events for the current day", @@ -2694,5 +2707,40 @@ "type": "text", "placeholders_order": [], "placeholders": {} + }, + "About": "Om", + "@About": { + "description": "Title for about menu item in settings", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "App": "App", + "@App": { + "description": "Title for App field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "Login ID": "Login-id", + "@Login ID": { + "description": "Title for Login ID field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "Email": "E-mail", + "@Email": { + "description": "Title for Email field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "Version": "Version", + "@Version": { + "description": "Title for Version field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} } } \ No newline at end of file diff --git a/apps/flutter_parent/lib/l10n/res/intl_de.arb b/apps/flutter_parent/lib/l10n/res/intl_de.arb index 14657d29ee..2f61a02006 100644 --- a/apps/flutter_parent/lib/l10n/res/intl_de.arb +++ b/apps/flutter_parent/lib/l10n/res/intl_de.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2023-02-17T11:03:20.619429", + "@@last_modified": "2023-04-14T11:04:46.988317", "alertsLabel": "Benachrichtigungen", "@alertsLabel": { "description": "The label for the Alerts tab", @@ -182,6 +182,19 @@ "points": {} } }, + "calendarDaySemanticsLabel": "{eventCount,plural, =1{{date}, {eventCount} Ereignis}other{{date}, {eventCount} Ereignisse}}", + "@calendarDaySemanticsLabel": { + "description": "Screen reader label used for calendar day, reads the date and count of events", + "type": "text", + "placeholders_order": [ + "date", + "eventCount" + ], + "placeholders": { + "date": {}, + "eventCount": {} + } + }, "No Events Today!": "Heute keine Ereignisse!", "@No Events Today!": { "description": "Title displayed when there are no calendar events for the current day", @@ -2694,5 +2707,40 @@ "type": "text", "placeholders_order": [], "placeholders": {} + }, + "About": "Info zu", + "@About": { + "description": "Title for about menu item in settings", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "App": "App", + "@App": { + "description": "Title for App field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "Login ID": "Anmelde-ID", + "@Login ID": { + "description": "Title for Login ID field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "Email": "E-Mail", + "@Email": { + "description": "Title for Email field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "Version": "Version", + "@Version": { + "description": "Title for Version field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} } } \ No newline at end of file diff --git a/apps/flutter_parent/lib/l10n/res/intl_en_AU.arb b/apps/flutter_parent/lib/l10n/res/intl_en_AU.arb index 38e2f80715..b3eed19106 100644 --- a/apps/flutter_parent/lib/l10n/res/intl_en_AU.arb +++ b/apps/flutter_parent/lib/l10n/res/intl_en_AU.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2023-02-17T11:03:20.619429", + "@@last_modified": "2023-04-14T11:04:46.988317", "alertsLabel": "Alerts", "@alertsLabel": { "description": "The label for the Alerts tab", @@ -182,6 +182,19 @@ "points": {} } }, + "calendarDaySemanticsLabel": "{eventCount,plural, =1{{date}, {eventCount} event}other{{date}, {eventCount} events}}", + "@calendarDaySemanticsLabel": { + "description": "Screen reader label used for calendar day, reads the date and count of events", + "type": "text", + "placeholders_order": [ + "date", + "eventCount" + ], + "placeholders": { + "date": {}, + "eventCount": {} + } + }, "No Events Today!": "No Events Today!", "@No Events Today!": { "description": "Title displayed when there are no calendar events for the current day", @@ -2694,5 +2707,40 @@ "type": "text", "placeholders_order": [], "placeholders": {} + }, + "About": "About", + "@About": { + "description": "Title for about menu item in settings", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "App": "App", + "@App": { + "description": "Title for App field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "Login ID": "Login ID", + "@Login ID": { + "description": "Title for Login ID field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "Email": "Email", + "@Email": { + "description": "Title for Email field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "Version": "Version", + "@Version": { + "description": "Title for Version field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} } } \ No newline at end of file diff --git a/apps/flutter_parent/lib/l10n/res/intl_en_AU_unimelb.arb b/apps/flutter_parent/lib/l10n/res/intl_en_AU_unimelb.arb index b657013ab7..b2b3fbdd7d 100644 --- a/apps/flutter_parent/lib/l10n/res/intl_en_AU_unimelb.arb +++ b/apps/flutter_parent/lib/l10n/res/intl_en_AU_unimelb.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2023-02-17T11:03:20.619429", + "@@last_modified": "2023-04-14T11:04:46.988317", "alertsLabel": "Alerts", "@alertsLabel": { "description": "The label for the Alerts tab", @@ -182,6 +182,19 @@ "points": {} } }, + "calendarDaySemanticsLabel": "{eventCount,plural, =1{{date}, {eventCount} event}other{{date}, {eventCount} events}}", + "@calendarDaySemanticsLabel": { + "description": "Screen reader label used for calendar day, reads the date and count of events", + "type": "text", + "placeholders_order": [ + "date", + "eventCount" + ], + "placeholders": { + "date": {}, + "eventCount": {} + } + }, "No Events Today!": "No Events Today!", "@No Events Today!": { "description": "Title displayed when there are no calendar events for the current day", @@ -2694,5 +2707,40 @@ "type": "text", "placeholders_order": [], "placeholders": {} + }, + "About": "About", + "@About": { + "description": "Title for about menu item in settings", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "App": "App", + "@App": { + "description": "Title for App field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "Login ID": "Login ID", + "@Login ID": { + "description": "Title for Login ID field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "Email": "Email", + "@Email": { + "description": "Title for Email field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "Version": "Version", + "@Version": { + "description": "Title for Version field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} } } \ No newline at end of file diff --git a/apps/flutter_parent/lib/l10n/res/intl_en_CY.arb b/apps/flutter_parent/lib/l10n/res/intl_en_CY.arb index be1386ee8a..8fe52d88ba 100644 --- a/apps/flutter_parent/lib/l10n/res/intl_en_CY.arb +++ b/apps/flutter_parent/lib/l10n/res/intl_en_CY.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2023-02-17T11:03:20.619429", + "@@last_modified": "2023-04-14T11:04:46.988317", "alertsLabel": "Alerts", "@alertsLabel": { "description": "The label for the Alerts tab", @@ -182,6 +182,19 @@ "points": {} } }, + "calendarDaySemanticsLabel": "{eventCount,plural, =1{{date}, {eventCount} event}other{{date}, {eventCount} events}}", + "@calendarDaySemanticsLabel": { + "description": "Screen reader label used for calendar day, reads the date and count of events", + "type": "text", + "placeholders_order": [ + "date", + "eventCount" + ], + "placeholders": { + "date": {}, + "eventCount": {} + } + }, "No Events Today!": "No Events Today!", "@No Events Today!": { "description": "Title displayed when there are no calendar events for the current day", @@ -2694,5 +2707,40 @@ "type": "text", "placeholders_order": [], "placeholders": {} + }, + "About": "About", + "@About": { + "description": "Title for about menu item in settings", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "App": "App", + "@App": { + "description": "Title for App field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "Login ID": "Login ID", + "@Login ID": { + "description": "Title for Login ID field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "Email": "Email", + "@Email": { + "description": "Title for Email field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "Version": "Version", + "@Version": { + "description": "Title for Version field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} } } \ No newline at end of file diff --git a/apps/flutter_parent/lib/l10n/res/intl_en_GB.arb b/apps/flutter_parent/lib/l10n/res/intl_en_GB.arb index 20c2335f72..61612c9b10 100644 --- a/apps/flutter_parent/lib/l10n/res/intl_en_GB.arb +++ b/apps/flutter_parent/lib/l10n/res/intl_en_GB.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2023-02-17T11:03:20.619429", + "@@last_modified": "2023-04-14T11:04:46.988317", "alertsLabel": "Alerts", "@alertsLabel": { "description": "The label for the Alerts tab", @@ -182,6 +182,19 @@ "points": {} } }, + "calendarDaySemanticsLabel": "{eventCount,plural, =1{{date}, {eventCount} event}other{{date}, {eventCount} events}}", + "@calendarDaySemanticsLabel": { + "description": "Screen reader label used for calendar day, reads the date and count of events", + "type": "text", + "placeholders_order": [ + "date", + "eventCount" + ], + "placeholders": { + "date": {}, + "eventCount": {} + } + }, "No Events Today!": "No Events Today!", "@No Events Today!": { "description": "Title displayed when there are no calendar events for the current day", @@ -2694,5 +2707,40 @@ "type": "text", "placeholders_order": [], "placeholders": {} + }, + "About": "About", + "@About": { + "description": "Title for about menu item in settings", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "App": "App", + "@App": { + "description": "Title for App field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "Login ID": "Login ID", + "@Login ID": { + "description": "Title for Login ID field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "Email": "Email", + "@Email": { + "description": "Title for Email field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "Version": "Version", + "@Version": { + "description": "Title for Version field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} } } \ No newline at end of file diff --git a/apps/flutter_parent/lib/l10n/res/intl_en_GB_instukhe.arb b/apps/flutter_parent/lib/l10n/res/intl_en_GB_instukhe.arb index be1386ee8a..8fe52d88ba 100644 --- a/apps/flutter_parent/lib/l10n/res/intl_en_GB_instukhe.arb +++ b/apps/flutter_parent/lib/l10n/res/intl_en_GB_instukhe.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2023-02-17T11:03:20.619429", + "@@last_modified": "2023-04-14T11:04:46.988317", "alertsLabel": "Alerts", "@alertsLabel": { "description": "The label for the Alerts tab", @@ -182,6 +182,19 @@ "points": {} } }, + "calendarDaySemanticsLabel": "{eventCount,plural, =1{{date}, {eventCount} event}other{{date}, {eventCount} events}}", + "@calendarDaySemanticsLabel": { + "description": "Screen reader label used for calendar day, reads the date and count of events", + "type": "text", + "placeholders_order": [ + "date", + "eventCount" + ], + "placeholders": { + "date": {}, + "eventCount": {} + } + }, "No Events Today!": "No Events Today!", "@No Events Today!": { "description": "Title displayed when there are no calendar events for the current day", @@ -2694,5 +2707,40 @@ "type": "text", "placeholders_order": [], "placeholders": {} + }, + "About": "About", + "@About": { + "description": "Title for about menu item in settings", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "App": "App", + "@App": { + "description": "Title for App field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "Login ID": "Login ID", + "@Login ID": { + "description": "Title for Login ID field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "Email": "Email", + "@Email": { + "description": "Title for Email field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "Version": "Version", + "@Version": { + "description": "Title for Version field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} } } \ No newline at end of file diff --git a/apps/flutter_parent/lib/l10n/res/intl_es.arb b/apps/flutter_parent/lib/l10n/res/intl_es.arb index 2e5a216b07..b2ff089429 100644 --- a/apps/flutter_parent/lib/l10n/res/intl_es.arb +++ b/apps/flutter_parent/lib/l10n/res/intl_es.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2023-02-17T11:03:20.619429", + "@@last_modified": "2023-04-14T11:04:46.988317", "alertsLabel": "Alertas", "@alertsLabel": { "description": "The label for the Alerts tab", @@ -182,6 +182,19 @@ "points": {} } }, + "calendarDaySemanticsLabel": "{eventCount,plural, =1{{date}, {eventCount} evento}other{{date}, {eventCount} eventos}}", + "@calendarDaySemanticsLabel": { + "description": "Screen reader label used for calendar day, reads the date and count of events", + "type": "text", + "placeholders_order": [ + "date", + "eventCount" + ], + "placeholders": { + "date": {}, + "eventCount": {} + } + }, "No Events Today!": "¡No hay ningún evento hoy!", "@No Events Today!": { "description": "Title displayed when there are no calendar events for the current day", @@ -2694,5 +2707,40 @@ "type": "text", "placeholders_order": [], "placeholders": {} + }, + "About": "Acerca de", + "@About": { + "description": "Title for about menu item in settings", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "App": "Aplicación", + "@App": { + "description": "Title for App field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "Login ID": "ID de inicio de sesión", + "@Login ID": { + "description": "Title for Login ID field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "Email": "Correo electrónico", + "@Email": { + "description": "Title for Email field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "Version": "Versión", + "@Version": { + "description": "Title for Version field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} } } \ No newline at end of file diff --git a/apps/flutter_parent/lib/l10n/res/intl_es_ES.arb b/apps/flutter_parent/lib/l10n/res/intl_es_ES.arb index 2a1ba17e2f..b539d7c9e3 100644 --- a/apps/flutter_parent/lib/l10n/res/intl_es_ES.arb +++ b/apps/flutter_parent/lib/l10n/res/intl_es_ES.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2023-02-17T11:03:20.619429", + "@@last_modified": "2023-04-14T11:04:46.988317", "alertsLabel": "Alertas", "@alertsLabel": { "description": "The label for the Alerts tab", @@ -182,6 +182,19 @@ "points": {} } }, + "calendarDaySemanticsLabel": "{eventCount,plural, =1{{date}, {eventCount} evento}other{{date}, {eventCount} eventos}}", + "@calendarDaySemanticsLabel": { + "description": "Screen reader label used for calendar day, reads the date and count of events", + "type": "text", + "placeholders_order": [ + "date", + "eventCount" + ], + "placeholders": { + "date": {}, + "eventCount": {} + } + }, "No Events Today!": "No hay ningún evento hoy", "@No Events Today!": { "description": "Title displayed when there are no calendar events for the current day", @@ -2694,5 +2707,40 @@ "type": "text", "placeholders_order": [], "placeholders": {} + }, + "About": "Acerca de", + "@About": { + "description": "Title for about menu item in settings", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "App": "Aplicación", + "@App": { + "description": "Title for App field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "Login ID": "Identificación de inicio de sesión", + "@Login ID": { + "description": "Title for Login ID field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "Email": "Correo electrónico", + "@Email": { + "description": "Title for Email field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "Version": "Versión", + "@Version": { + "description": "Title for Version field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} } } \ No newline at end of file diff --git a/apps/flutter_parent/lib/l10n/res/intl_fi.arb b/apps/flutter_parent/lib/l10n/res/intl_fi.arb index b9730c06fa..aa98e93a86 100644 --- a/apps/flutter_parent/lib/l10n/res/intl_fi.arb +++ b/apps/flutter_parent/lib/l10n/res/intl_fi.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2023-02-17T11:03:20.619429", + "@@last_modified": "2023-04-14T11:04:46.988317", "alertsLabel": "Hälytykset", "@alertsLabel": { "description": "The label for the Alerts tab", @@ -182,6 +182,19 @@ "points": {} } }, + "calendarDaySemanticsLabel": "{eventCount,plural, =1{{date}, {eventCount} tapahtuma}other{{date}, {eventCount} tapahtumaa}}", + "@calendarDaySemanticsLabel": { + "description": "Screen reader label used for calendar day, reads the date and count of events", + "type": "text", + "placeholders_order": [ + "date", + "eventCount" + ], + "placeholders": { + "date": {}, + "eventCount": {} + } + }, "No Events Today!": "Ei tapahtumia tänään!", "@No Events Today!": { "description": "Title displayed when there are no calendar events for the current day", @@ -2694,5 +2707,40 @@ "type": "text", "placeholders_order": [], "placeholders": {} + }, + "About": "Tietoja", + "@About": { + "description": "Title for about menu item in settings", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "App": "Sovellus", + "@App": { + "description": "Title for App field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "Login ID": "Käyttäjätunnus", + "@Login ID": { + "description": "Title for Login ID field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "Email": "Sähköposti", + "@Email": { + "description": "Title for Email field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "Version": "Versio", + "@Version": { + "description": "Title for Version field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} } } \ No newline at end of file diff --git a/apps/flutter_parent/lib/l10n/res/intl_fr.arb b/apps/flutter_parent/lib/l10n/res/intl_fr.arb index 28463a7420..9d143b26e7 100644 --- a/apps/flutter_parent/lib/l10n/res/intl_fr.arb +++ b/apps/flutter_parent/lib/l10n/res/intl_fr.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2023-02-17T11:03:20.619429", + "@@last_modified": "2023-04-14T11:04:46.988317", "alertsLabel": "Alertes", "@alertsLabel": { "description": "The label for the Alerts tab", @@ -182,6 +182,19 @@ "points": {} } }, + "calendarDaySemanticsLabel": "{eventCount,plural, =1{{date}, {eventCount} événement}other{{date}, {eventCount} événements}}", + "@calendarDaySemanticsLabel": { + "description": "Screen reader label used for calendar day, reads the date and count of events", + "type": "text", + "placeholders_order": [ + "date", + "eventCount" + ], + "placeholders": { + "date": {}, + "eventCount": {} + } + }, "No Events Today!": "Aucun événement aujourd'hui !", "@No Events Today!": { "description": "Title displayed when there are no calendar events for the current day", @@ -2694,5 +2707,40 @@ "type": "text", "placeholders_order": [], "placeholders": {} + }, + "About": "À propos de", + "@About": { + "description": "Title for about menu item in settings", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "App": "Application", + "@App": { + "description": "Title for App field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "Login ID": "ID d’authentification", + "@Login ID": { + "description": "Title for Login ID field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "Email": "Email", + "@Email": { + "description": "Title for Email field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "Version": "Version", + "@Version": { + "description": "Title for Version field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} } } \ No newline at end of file diff --git a/apps/flutter_parent/lib/l10n/res/intl_fr_CA.arb b/apps/flutter_parent/lib/l10n/res/intl_fr_CA.arb index b266dba62e..e114361c87 100644 --- a/apps/flutter_parent/lib/l10n/res/intl_fr_CA.arb +++ b/apps/flutter_parent/lib/l10n/res/intl_fr_CA.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2023-02-17T11:03:20.619429", + "@@last_modified": "2023-04-14T11:04:46.988317", "alertsLabel": "Alertes", "@alertsLabel": { "description": "The label for the Alerts tab", @@ -182,6 +182,19 @@ "points": {} } }, + "calendarDaySemanticsLabel": "{eventCount,plural, =1{{date}, {eventCount} événement}other{{date}, {eventCount} événements}}", + "@calendarDaySemanticsLabel": { + "description": "Screen reader label used for calendar day, reads the date and count of events", + "type": "text", + "placeholders_order": [ + "date", + "eventCount" + ], + "placeholders": { + "date": {}, + "eventCount": {} + } + }, "No Events Today!": "Aucun événement d’aujourd’hui!", "@No Events Today!": { "description": "Title displayed when there are no calendar events for the current day", @@ -2694,5 +2707,40 @@ "type": "text", "placeholders_order": [], "placeholders": {} + }, + "About": "À propos", + "@About": { + "description": "Title for about menu item in settings", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "App": "Appl.", + "@App": { + "description": "Title for App field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "Login ID": "Identifiant de connexion", + "@Login ID": { + "description": "Title for Login ID field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "Email": "Adresse électronique", + "@Email": { + "description": "Title for Email field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "Version": "Version", + "@Version": { + "description": "Title for Version field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} } } \ No newline at end of file diff --git a/apps/flutter_parent/lib/l10n/res/intl_ht.arb b/apps/flutter_parent/lib/l10n/res/intl_ht.arb index 588f9e714a..b558d309f1 100644 --- a/apps/flutter_parent/lib/l10n/res/intl_ht.arb +++ b/apps/flutter_parent/lib/l10n/res/intl_ht.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2023-02-17T11:03:20.619429", + "@@last_modified": "2023-04-14T11:04:46.988317", "alertsLabel": "Alèt", "@alertsLabel": { "description": "The label for the Alerts tab", @@ -182,6 +182,19 @@ "points": {} } }, + "calendarDaySemanticsLabel": "{eventCount,plural, =1{{date}, {eventCount} evènman}other{{date}, {eventCount} evènman}}", + "@calendarDaySemanticsLabel": { + "description": "Screen reader label used for calendar day, reads the date and count of events", + "type": "text", + "placeholders_order": [ + "date", + "eventCount" + ], + "placeholders": { + "date": {}, + "eventCount": {} + } + }, "No Events Today!": "Pa gen Aktivite Jodi a!", "@No Events Today!": { "description": "Title displayed when there are no calendar events for the current day", @@ -2694,5 +2707,40 @@ "type": "text", "placeholders_order": [], "placeholders": {} + }, + "About": "Apwopo", + "@About": { + "description": "Title for about menu item in settings", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "App": "App", + "@App": { + "description": "Title for App field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "Login ID": "ID Koneksyon", + "@Login ID": { + "description": "Title for Login ID field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "Email": "Imèl", + "@Email": { + "description": "Title for Email field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "Version": "Vèsyon", + "@Version": { + "description": "Title for Version field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} } } \ No newline at end of file diff --git a/apps/flutter_parent/lib/l10n/res/intl_is.arb b/apps/flutter_parent/lib/l10n/res/intl_is.arb index 45d6d480a1..7a09f7f413 100644 --- a/apps/flutter_parent/lib/l10n/res/intl_is.arb +++ b/apps/flutter_parent/lib/l10n/res/intl_is.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2023-02-17T11:03:20.619429", + "@@last_modified": "2023-04-14T11:04:46.988317", "alertsLabel": "Viðvaranir", "@alertsLabel": { "description": "The label for the Alerts tab", @@ -182,6 +182,19 @@ "points": {} } }, + "calendarDaySemanticsLabel": "{eventCount,plural, =1{{date}, {eventCount} viðburður}other{{date}, {eventCount} viðburðir}}", + "@calendarDaySemanticsLabel": { + "description": "Screen reader label used for calendar day, reads the date and count of events", + "type": "text", + "placeholders_order": [ + "date", + "eventCount" + ], + "placeholders": { + "date": {}, + "eventCount": {} + } + }, "No Events Today!": "Engir viðburðir í dag!", "@No Events Today!": { "description": "Title displayed when there are no calendar events for the current day", @@ -2694,5 +2707,40 @@ "type": "text", "placeholders_order": [], "placeholders": {} + }, + "About": "Um", + "@About": { + "description": "Title for about menu item in settings", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "App": "Smáforrit", + "@App": { + "description": "Title for App field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "Login ID": "Innskráningarauðkenni", + "@Login ID": { + "description": "Title for Login ID field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "Email": "Tölvupóstur", + "@Email": { + "description": "Title for Email field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "Version": "Útgáfa", + "@Version": { + "description": "Title for Version field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} } } \ No newline at end of file diff --git a/apps/flutter_parent/lib/l10n/res/intl_it.arb b/apps/flutter_parent/lib/l10n/res/intl_it.arb index d099672b31..2b0d6ee265 100644 --- a/apps/flutter_parent/lib/l10n/res/intl_it.arb +++ b/apps/flutter_parent/lib/l10n/res/intl_it.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2023-02-17T11:03:20.619429", + "@@last_modified": "2023-04-14T11:04:46.988317", "alertsLabel": "Avvisi", "@alertsLabel": { "description": "The label for the Alerts tab", @@ -182,6 +182,19 @@ "points": {} } }, + "calendarDaySemanticsLabel": "{eventCount,plural, =1{{date}, {eventCount} evento}other{{date}, {eventCount} eventi}}", + "@calendarDaySemanticsLabel": { + "description": "Screen reader label used for calendar day, reads the date and count of events", + "type": "text", + "placeholders_order": [ + "date", + "eventCount" + ], + "placeholders": { + "date": {}, + "eventCount": {} + } + }, "No Events Today!": "Nessun evento oggi!", "@No Events Today!": { "description": "Title displayed when there are no calendar events for the current day", @@ -2694,5 +2707,40 @@ "type": "text", "placeholders_order": [], "placeholders": {} + }, + "About": "Informazioni", + "@About": { + "description": "Title for about menu item in settings", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "App": "App", + "@App": { + "description": "Title for App field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "Login ID": "ID di accesso", + "@Login ID": { + "description": "Title for Login ID field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "Email": "E-mail", + "@Email": { + "description": "Title for Email field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "Version": "Versione", + "@Version": { + "description": "Title for Version field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} } } \ No newline at end of file diff --git a/apps/flutter_parent/lib/l10n/res/intl_ja.arb b/apps/flutter_parent/lib/l10n/res/intl_ja.arb index a32d862c4c..9b16649315 100644 --- a/apps/flutter_parent/lib/l10n/res/intl_ja.arb +++ b/apps/flutter_parent/lib/l10n/res/intl_ja.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2023-02-17T11:03:20.619429", + "@@last_modified": "2023-04-14T11:04:46.988317", "alertsLabel": "アラート", "@alertsLabel": { "description": "The label for the Alerts tab", @@ -182,6 +182,19 @@ "points": {} } }, + "calendarDaySemanticsLabel": "{eventCount,plural, =1{{date}、{eventCount}イベント}other{{date}、{eventCount}イベント}}", + "@calendarDaySemanticsLabel": { + "description": "Screen reader label used for calendar day, reads the date and count of events", + "type": "text", + "placeholders_order": [ + "date", + "eventCount" + ], + "placeholders": { + "date": {}, + "eventCount": {} + } + }, "No Events Today!": "今日イベントはありません!", "@No Events Today!": { "description": "Title displayed when there are no calendar events for the current day", @@ -2694,5 +2707,40 @@ "type": "text", "placeholders_order": [], "placeholders": {} + }, + "About": "情報", + "@About": { + "description": "Title for about menu item in settings", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "App": "アプリ", + "@App": { + "description": "Title for App field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "Login ID": "ログイン ID", + "@Login ID": { + "description": "Title for Login ID field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "Email": "E メール", + "@Email": { + "description": "Title for Email field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "Version": "バージョン", + "@Version": { + "description": "Title for Version field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} } } \ No newline at end of file diff --git a/apps/flutter_parent/lib/l10n/res/intl_mi.arb b/apps/flutter_parent/lib/l10n/res/intl_mi.arb index ffddad5267..488c681b32 100644 --- a/apps/flutter_parent/lib/l10n/res/intl_mi.arb +++ b/apps/flutter_parent/lib/l10n/res/intl_mi.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2023-02-17T11:03:20.619429", + "@@last_modified": "2023-04-14T11:04:46.988317", "alertsLabel": "He whakamataara", "@alertsLabel": { "description": "The label for the Alerts tab", @@ -182,6 +182,19 @@ "points": {} } }, + "calendarDaySemanticsLabel": "{eventCount,plural, =1{{date}, {eventCount} takahanga}other{{date}, {eventCount} takahanga}}", + "@calendarDaySemanticsLabel": { + "description": "Screen reader label used for calendar day, reads the date and count of events", + "type": "text", + "placeholders_order": [ + "date", + "eventCount" + ], + "placeholders": { + "date": {}, + "eventCount": {} + } + }, "No Events Today!": "Kaore he tauwhāinga i tēnei rā!", "@No Events Today!": { "description": "Title displayed when there are no calendar events for the current day", @@ -2694,5 +2707,40 @@ "type": "text", "placeholders_order": [], "placeholders": {} + }, + "About": "Mō", + "@About": { + "description": "Title for about menu item in settings", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "App": "Taupānga", + "@App": { + "description": "Title for App field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "Login ID": "Takiuru ID", + "@Login ID": { + "description": "Title for Login ID field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "Email": "Īmēra", + "@Email": { + "description": "Title for Email field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "Version": "Putanga", + "@Version": { + "description": "Title for Version field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} } } \ No newline at end of file diff --git a/apps/flutter_parent/lib/l10n/res/intl_ms.arb b/apps/flutter_parent/lib/l10n/res/intl_ms.arb index 06d80ecc18..2e83b495d5 100644 --- a/apps/flutter_parent/lib/l10n/res/intl_ms.arb +++ b/apps/flutter_parent/lib/l10n/res/intl_ms.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2023-02-17T11:03:20.619429", + "@@last_modified": "2023-04-14T11:04:46.988317", "alertsLabel": "Isyarat", "@alertsLabel": { "description": "The label for the Alerts tab", @@ -182,6 +182,19 @@ "points": {} } }, + "calendarDaySemanticsLabel": "{eventCount,plural, =1{{date}, {eventCount} acara}other{{date}, {eventCount} acara}}", + "@calendarDaySemanticsLabel": { + "description": "Screen reader label used for calendar day, reads the date and count of events", + "type": "text", + "placeholders_order": [ + "date", + "eventCount" + ], + "placeholders": { + "date": {}, + "eventCount": {} + } + }, "No Events Today!": "Tiada Acara Hari Ini!", "@No Events Today!": { "description": "Title displayed when there are no calendar events for the current day", @@ -2694,5 +2707,40 @@ "type": "text", "placeholders_order": [], "placeholders": {} + }, + "About": "Perihal", + "@About": { + "description": "Title for about menu item in settings", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "App": "Apl", + "@App": { + "description": "Title for App field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "Login ID": "ID Log Masuk", + "@Login ID": { + "description": "Title for Login ID field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "Email": "E-mel", + "@Email": { + "description": "Title for Email field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "Version": "Versi", + "@Version": { + "description": "Title for Version field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} } } \ No newline at end of file diff --git a/apps/flutter_parent/lib/l10n/res/intl_nb.arb b/apps/flutter_parent/lib/l10n/res/intl_nb.arb index 50d81d9447..c272e85eba 100644 --- a/apps/flutter_parent/lib/l10n/res/intl_nb.arb +++ b/apps/flutter_parent/lib/l10n/res/intl_nb.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2023-02-17T11:03:20.619429", + "@@last_modified": "2023-04-14T11:04:46.988317", "alertsLabel": "Varsler", "@alertsLabel": { "description": "The label for the Alerts tab", @@ -182,6 +182,19 @@ "points": {} } }, + "calendarDaySemanticsLabel": "{eventCount,plural, =1{{date}, {eventCount} hendelse}other{{date}, {eventCount} hendelser}}", + "@calendarDaySemanticsLabel": { + "description": "Screen reader label used for calendar day, reads the date and count of events", + "type": "text", + "placeholders_order": [ + "date", + "eventCount" + ], + "placeholders": { + "date": {}, + "eventCount": {} + } + }, "No Events Today!": "Ingen arrangementer i dag!", "@No Events Today!": { "description": "Title displayed when there are no calendar events for the current day", @@ -2694,5 +2707,40 @@ "type": "text", "placeholders_order": [], "placeholders": {} + }, + "About": "Om", + "@About": { + "description": "Title for about menu item in settings", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "App": "App", + "@App": { + "description": "Title for App field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "Login ID": "Innloggings-ID", + "@Login ID": { + "description": "Title for Login ID field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "Email": "E-post", + "@Email": { + "description": "Title for Email field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "Version": "Versjon", + "@Version": { + "description": "Title for Version field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} } } \ No newline at end of file diff --git a/apps/flutter_parent/lib/l10n/res/intl_nb_instk12.arb b/apps/flutter_parent/lib/l10n/res/intl_nb_instk12.arb index af302f925c..75c8d09084 100644 --- a/apps/flutter_parent/lib/l10n/res/intl_nb_instk12.arb +++ b/apps/flutter_parent/lib/l10n/res/intl_nb_instk12.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2023-02-17T11:03:20.619429", + "@@last_modified": "2023-04-14T11:04:46.988317", "alertsLabel": "Varsler", "@alertsLabel": { "description": "The label for the Alerts tab", @@ -182,6 +182,19 @@ "points": {} } }, + "calendarDaySemanticsLabel": "{eventCount,plural, =1{{date}, {eventCount} hendelse}other{{date}, {eventCount} hendelser}}", + "@calendarDaySemanticsLabel": { + "description": "Screen reader label used for calendar day, reads the date and count of events", + "type": "text", + "placeholders_order": [ + "date", + "eventCount" + ], + "placeholders": { + "date": {}, + "eventCount": {} + } + }, "No Events Today!": "Ingen arrangementer i dag!", "@No Events Today!": { "description": "Title displayed when there are no calendar events for the current day", @@ -2694,5 +2707,40 @@ "type": "text", "placeholders_order": [], "placeholders": {} + }, + "About": "Om", + "@About": { + "description": "Title for about menu item in settings", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "App": "App", + "@App": { + "description": "Title for App field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "Login ID": "Innloggings-ID", + "@Login ID": { + "description": "Title for Login ID field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "Email": "E-post", + "@Email": { + "description": "Title for Email field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "Version": "Versjon", + "@Version": { + "description": "Title for Version field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} } } \ No newline at end of file diff --git a/apps/flutter_parent/lib/l10n/res/intl_nl.arb b/apps/flutter_parent/lib/l10n/res/intl_nl.arb index 22ea3b08b4..27668187aa 100644 --- a/apps/flutter_parent/lib/l10n/res/intl_nl.arb +++ b/apps/flutter_parent/lib/l10n/res/intl_nl.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2023-02-17T11:03:20.619429", + "@@last_modified": "2023-04-14T11:04:46.988317", "alertsLabel": "Waarschuwingen", "@alertsLabel": { "description": "The label for the Alerts tab", @@ -182,6 +182,19 @@ "points": {} } }, + "calendarDaySemanticsLabel": "{eventCount,plural, =1{{date}, {eventCount} gebeurtenis}other{{date}, {eventCount} gebeurtenissen}}", + "@calendarDaySemanticsLabel": { + "description": "Screen reader label used for calendar day, reads the date and count of events", + "type": "text", + "placeholders_order": [ + "date", + "eventCount" + ], + "placeholders": { + "date": {}, + "eventCount": {} + } + }, "No Events Today!": "Vandaag geen gebeurtenissen!", "@No Events Today!": { "description": "Title displayed when there are no calendar events for the current day", @@ -2694,5 +2707,40 @@ "type": "text", "placeholders_order": [], "placeholders": {} + }, + "About": "Over", + "@About": { + "description": "Title for about menu item in settings", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "App": "App", + "@App": { + "description": "Title for App field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "Login ID": "Aanmeldings-ID", + "@Login ID": { + "description": "Title for Login ID field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "Email": "E-mail", + "@Email": { + "description": "Title for Email field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "Version": "Versie", + "@Version": { + "description": "Title for Version field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} } } \ No newline at end of file diff --git a/apps/flutter_parent/lib/l10n/res/intl_pl.arb b/apps/flutter_parent/lib/l10n/res/intl_pl.arb index 98a06c5d5d..dcc306e354 100644 --- a/apps/flutter_parent/lib/l10n/res/intl_pl.arb +++ b/apps/flutter_parent/lib/l10n/res/intl_pl.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2023-02-17T11:03:20.619429", + "@@last_modified": "2023-04-14T11:04:46.988317", "alertsLabel": "Alerty", "@alertsLabel": { "description": "The label for the Alerts tab", @@ -182,6 +182,19 @@ "points": {} } }, + "calendarDaySemanticsLabel": "{eventCount,plural, =1{{date}, {eventCount} zdarzenie}other{{date}, {eventCount} zdarzenia}}", + "@calendarDaySemanticsLabel": { + "description": "Screen reader label used for calendar day, reads the date and count of events", + "type": "text", + "placeholders_order": [ + "date", + "eventCount" + ], + "placeholders": { + "date": {}, + "eventCount": {} + } + }, "No Events Today!": "Brak wydarzeń na dziś!", "@No Events Today!": { "description": "Title displayed when there are no calendar events for the current day", @@ -2694,5 +2707,40 @@ "type": "text", "placeholders_order": [], "placeholders": {} + }, + "About": "Informacje", + "@About": { + "description": "Title for about menu item in settings", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "App": "Aplikacja", + "@App": { + "description": "Title for App field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "Login ID": "Nazwa użytkownika", + "@Login ID": { + "description": "Title for Login ID field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "Email": "E-mail", + "@Email": { + "description": "Title for Email field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "Version": "Wersja", + "@Version": { + "description": "Title for Version field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} } } \ No newline at end of file diff --git a/apps/flutter_parent/lib/l10n/res/intl_pt_BR.arb b/apps/flutter_parent/lib/l10n/res/intl_pt_BR.arb index c557a68c79..58ba0f7119 100644 --- a/apps/flutter_parent/lib/l10n/res/intl_pt_BR.arb +++ b/apps/flutter_parent/lib/l10n/res/intl_pt_BR.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2023-02-17T11:03:20.619429", + "@@last_modified": "2023-04-14T11:04:46.988317", "alertsLabel": "Alertas", "@alertsLabel": { "description": "The label for the Alerts tab", @@ -182,6 +182,19 @@ "points": {} } }, + "calendarDaySemanticsLabel": "{eventCount,plural, =1{{date}, {eventCount} evento}other{{date}, {eventCount} eventos}}", + "@calendarDaySemanticsLabel": { + "description": "Screen reader label used for calendar day, reads the date and count of events", + "type": "text", + "placeholders_order": [ + "date", + "eventCount" + ], + "placeholders": { + "date": {}, + "eventCount": {} + } + }, "No Events Today!": "Nenhum evento hoje!", "@No Events Today!": { "description": "Title displayed when there are no calendar events for the current day", @@ -2694,5 +2707,40 @@ "type": "text", "placeholders_order": [], "placeholders": {} + }, + "About": "Sobre", + "@About": { + "description": "Title for about menu item in settings", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "App": "Aplicativo", + "@App": { + "description": "Title for App field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "Login ID": "ID de Login", + "@Login ID": { + "description": "Title for Login ID field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "Email": "E-mail", + "@Email": { + "description": "Title for Email field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "Version": "Versão", + "@Version": { + "description": "Title for Version field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} } } \ No newline at end of file diff --git a/apps/flutter_parent/lib/l10n/res/intl_pt_PT.arb b/apps/flutter_parent/lib/l10n/res/intl_pt_PT.arb index d33eb2b717..6505af0e5a 100644 --- a/apps/flutter_parent/lib/l10n/res/intl_pt_PT.arb +++ b/apps/flutter_parent/lib/l10n/res/intl_pt_PT.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2023-02-17T11:03:20.619429", + "@@last_modified": "2023-04-14T11:04:46.988317", "alertsLabel": "Alertas", "@alertsLabel": { "description": "The label for the Alerts tab", @@ -182,6 +182,19 @@ "points": {} } }, + "calendarDaySemanticsLabel": "{eventCount,plural, =1{{date}, {eventCount} evento}other{{date}, {eventCount} eventos}}", + "@calendarDaySemanticsLabel": { + "description": "Screen reader label used for calendar day, reads the date and count of events", + "type": "text", + "placeholders_order": [ + "date", + "eventCount" + ], + "placeholders": { + "date": {}, + "eventCount": {} + } + }, "No Events Today!": "Nenhum evento hoje!", "@No Events Today!": { "description": "Title displayed when there are no calendar events for the current day", @@ -2694,5 +2707,40 @@ "type": "text", "placeholders_order": [], "placeholders": {} + }, + "About": "Sobre", + "@About": { + "description": "Title for about menu item in settings", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "App": "Aplicação", + "@App": { + "description": "Title for App field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "Login ID": "ID de login", + "@Login ID": { + "description": "Title for Login ID field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "Email": "E-mail", + "@Email": { + "description": "Title for Email field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "Version": "Versão", + "@Version": { + "description": "Title for Version field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} } } \ No newline at end of file diff --git a/apps/flutter_parent/lib/l10n/res/intl_ru.arb b/apps/flutter_parent/lib/l10n/res/intl_ru.arb index 63e0bb16e1..1f5673f194 100644 --- a/apps/flutter_parent/lib/l10n/res/intl_ru.arb +++ b/apps/flutter_parent/lib/l10n/res/intl_ru.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2023-02-17T11:03:20.619429", + "@@last_modified": "2023-04-14T11:04:46.988317", "alertsLabel": "Предупреждения", "@alertsLabel": { "description": "The label for the Alerts tab", @@ -182,6 +182,19 @@ "points": {} } }, + "calendarDaySemanticsLabel": "{eventCount,plural, =1{{date}, {eventCount} событие}other{{date}, {eventCount} события}}", + "@calendarDaySemanticsLabel": { + "description": "Screen reader label used for calendar day, reads the date and count of events", + "type": "text", + "placeholders_order": [ + "date", + "eventCount" + ], + "placeholders": { + "date": {}, + "eventCount": {} + } + }, "No Events Today!": "На сегодня события отсутствуют!", "@No Events Today!": { "description": "Title displayed when there are no calendar events for the current day", @@ -2694,5 +2707,40 @@ "type": "text", "placeholders_order": [], "placeholders": {} + }, + "About": "О проекте", + "@About": { + "description": "Title for about menu item in settings", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "App": "Приложение", + "@App": { + "description": "Title for App field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "Login ID": "Имя пользователя", + "@Login ID": { + "description": "Title for Login ID field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "Email": "Адрес электронной почты", + "@Email": { + "description": "Title for Email field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "Version": "Версия", + "@Version": { + "description": "Title for Version field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} } } \ No newline at end of file diff --git a/apps/flutter_parent/lib/l10n/res/intl_sl.arb b/apps/flutter_parent/lib/l10n/res/intl_sl.arb index 8b1e905614..30cc7a1e70 100644 --- a/apps/flutter_parent/lib/l10n/res/intl_sl.arb +++ b/apps/flutter_parent/lib/l10n/res/intl_sl.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2023-02-17T11:03:20.619429", + "@@last_modified": "2023-04-14T11:04:46.988317", "alertsLabel": "Opozorila", "@alertsLabel": { "description": "The label for the Alerts tab", @@ -182,6 +182,19 @@ "points": {} } }, + "calendarDaySemanticsLabel": "{eventCount,plural, =1{{date}, {eventCount} dogodek}other{{date}, {eventCount} dogodkov(-a/-i)}}", + "@calendarDaySemanticsLabel": { + "description": "Screen reader label used for calendar day, reads the date and count of events", + "type": "text", + "placeholders_order": [ + "date", + "eventCount" + ], + "placeholders": { + "date": {}, + "eventCount": {} + } + }, "No Events Today!": "Danes ni dogodkov.", "@No Events Today!": { "description": "Title displayed when there are no calendar events for the current day", @@ -2694,5 +2707,40 @@ "type": "text", "placeholders_order": [], "placeholders": {} + }, + "About": "Vizitka", + "@About": { + "description": "Title for about menu item in settings", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "App": "Aplikacija", + "@App": { + "description": "Title for App field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "Login ID": "ID prijave", + "@Login ID": { + "description": "Title for Login ID field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "Email": "E-pošta", + "@Email": { + "description": "Title for Email field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "Version": "Različica", + "@Version": { + "description": "Title for Version field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} } } \ No newline at end of file diff --git a/apps/flutter_parent/lib/l10n/res/intl_sv.arb b/apps/flutter_parent/lib/l10n/res/intl_sv.arb index 56928c1f25..2520c94ae0 100644 --- a/apps/flutter_parent/lib/l10n/res/intl_sv.arb +++ b/apps/flutter_parent/lib/l10n/res/intl_sv.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2023-02-17T11:03:20.619429", + "@@last_modified": "2023-04-14T11:04:46.988317", "alertsLabel": "Notiser", "@alertsLabel": { "description": "The label for the Alerts tab", @@ -182,6 +182,19 @@ "points": {} } }, + "calendarDaySemanticsLabel": "{eventCount,plural, =1{{date}, {eventCount} händelse}other{{date}, {eventCount} händelser}}", + "@calendarDaySemanticsLabel": { + "description": "Screen reader label used for calendar day, reads the date and count of events", + "type": "text", + "placeholders_order": [ + "date", + "eventCount" + ], + "placeholders": { + "date": {}, + "eventCount": {} + } + }, "No Events Today!": "Inga händelser idag!", "@No Events Today!": { "description": "Title displayed when there are no calendar events for the current day", @@ -2694,5 +2707,40 @@ "type": "text", "placeholders_order": [], "placeholders": {} + }, + "About": "Om", + "@About": { + "description": "Title for about menu item in settings", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "App": "App", + "@App": { + "description": "Title for App field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "Login ID": "Inloggnings-ID", + "@Login ID": { + "description": "Title for Login ID field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "Email": "E-post", + "@Email": { + "description": "Title for Email field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "Version": "Version", + "@Version": { + "description": "Title for Version field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} } } \ No newline at end of file diff --git a/apps/flutter_parent/lib/l10n/res/intl_sv_instk12.arb b/apps/flutter_parent/lib/l10n/res/intl_sv_instk12.arb index 39df926726..d9196a45bb 100644 --- a/apps/flutter_parent/lib/l10n/res/intl_sv_instk12.arb +++ b/apps/flutter_parent/lib/l10n/res/intl_sv_instk12.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2023-02-17T11:03:20.619429", + "@@last_modified": "2023-04-14T11:04:46.988317", "alertsLabel": "Notiser", "@alertsLabel": { "description": "The label for the Alerts tab", @@ -182,6 +182,19 @@ "points": {} } }, + "calendarDaySemanticsLabel": "{eventCount,plural, =1{{date}, {eventCount} händelse}other{{date}, {eventCount} händelser}}", + "@calendarDaySemanticsLabel": { + "description": "Screen reader label used for calendar day, reads the date and count of events", + "type": "text", + "placeholders_order": [ + "date", + "eventCount" + ], + "placeholders": { + "date": {}, + "eventCount": {} + } + }, "No Events Today!": "Inga händelser idag!", "@No Events Today!": { "description": "Title displayed when there are no calendar events for the current day", @@ -2694,5 +2707,40 @@ "type": "text", "placeholders_order": [], "placeholders": {} + }, + "About": "Om", + "@About": { + "description": "Title for about menu item in settings", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "App": "App", + "@App": { + "description": "Title for App field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "Login ID": "Inloggnings-ID", + "@Login ID": { + "description": "Title for Login ID field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "Email": "E-post", + "@Email": { + "description": "Title for Email field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "Version": "Version", + "@Version": { + "description": "Title for Version field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} } } \ No newline at end of file diff --git a/apps/flutter_parent/lib/l10n/res/intl_th.arb b/apps/flutter_parent/lib/l10n/res/intl_th.arb index 7200a746b4..4b9c774051 100644 --- a/apps/flutter_parent/lib/l10n/res/intl_th.arb +++ b/apps/flutter_parent/lib/l10n/res/intl_th.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2023-02-17T11:03:20.619429", + "@@last_modified": "2023-04-14T11:04:46.988317", "alertsLabel": "แจ้งเตือน", "@alertsLabel": { "description": "The label for the Alerts tab", @@ -182,6 +182,19 @@ "points": {} } }, + "calendarDaySemanticsLabel": "{eventCount,plural, =1{{date}, {eventCount} กิจกรรม}other{{date}, {eventCount} กิจกรรม}}", + "@calendarDaySemanticsLabel": { + "description": "Screen reader label used for calendar day, reads the date and count of events", + "type": "text", + "placeholders_order": [ + "date", + "eventCount" + ], + "placeholders": { + "date": {}, + "eventCount": {} + } + }, "No Events Today!": "ไม่มีกิจกรรมในวันนี้!", "@No Events Today!": { "description": "Title displayed when there are no calendar events for the current day", @@ -2694,5 +2707,40 @@ "type": "text", "placeholders_order": [], "placeholders": {} + }, + "About": "เกี่ยวกับ", + "@About": { + "description": "Title for about menu item in settings", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "App": "แอพ", + "@App": { + "description": "Title for App field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "Login ID": "ID ล็อกอิน", + "@Login ID": { + "description": "Title for Login ID field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "Email": "อีเมล", + "@Email": { + "description": "Title for Email field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "Version": "เวอร์ชั่น", + "@Version": { + "description": "Title for Version field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} } } \ No newline at end of file diff --git a/apps/flutter_parent/lib/l10n/res/intl_vi.arb b/apps/flutter_parent/lib/l10n/res/intl_vi.arb index b1178043b0..466548adb1 100644 --- a/apps/flutter_parent/lib/l10n/res/intl_vi.arb +++ b/apps/flutter_parent/lib/l10n/res/intl_vi.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2023-02-17T11:03:20.619429", + "@@last_modified": "2023-04-14T11:04:46.988317", "alertsLabel": "Cảnh Báo", "@alertsLabel": { "description": "The label for the Alerts tab", @@ -182,6 +182,19 @@ "points": {} } }, + "calendarDaySemanticsLabel": "{eventCount,plural, =1{{date}, {eventCount} sự kiện}other{{date}, {eventCount} sự kiện}}", + "@calendarDaySemanticsLabel": { + "description": "Screen reader label used for calendar day, reads the date and count of events", + "type": "text", + "placeholders_order": [ + "date", + "eventCount" + ], + "placeholders": { + "date": {}, + "eventCount": {} + } + }, "No Events Today!": "Không Có Sự Kiện Hôm Nay!", "@No Events Today!": { "description": "Title displayed when there are no calendar events for the current day", @@ -2694,5 +2707,40 @@ "type": "text", "placeholders_order": [], "placeholders": {} + }, + "About": "Giới Thiệu", + "@About": { + "description": "Title for about menu item in settings", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "App": "Ứng Dụng", + "@App": { + "description": "Title for App field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "Login ID": "ID Đăng Nhập", + "@Login ID": { + "description": "Title for Login ID field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "Email": "Email", + "@Email": { + "description": "Title for Email field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "Version": "Phiên bản", + "@Version": { + "description": "Title for Version field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} } } \ No newline at end of file diff --git a/apps/flutter_parent/lib/l10n/res/intl_zh.arb b/apps/flutter_parent/lib/l10n/res/intl_zh.arb index e2c3d89c68..bcb61c5d1d 100644 --- a/apps/flutter_parent/lib/l10n/res/intl_zh.arb +++ b/apps/flutter_parent/lib/l10n/res/intl_zh.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2023-02-17T11:03:20.619429", + "@@last_modified": "2023-04-14T11:04:46.988317", "alertsLabel": "警告", "@alertsLabel": { "description": "The label for the Alerts tab", @@ -182,6 +182,19 @@ "points": {} } }, + "calendarDaySemanticsLabel": "{eventCount,plural, =1{{date}、{eventCount} 个事件}other{{date}、{eventCount} 个事件}}", + "@calendarDaySemanticsLabel": { + "description": "Screen reader label used for calendar day, reads the date and count of events", + "type": "text", + "placeholders_order": [ + "date", + "eventCount" + ], + "placeholders": { + "date": {}, + "eventCount": {} + } + }, "No Events Today!": "今天没有事件!", "@No Events Today!": { "description": "Title displayed when there are no calendar events for the current day", @@ -2694,5 +2707,40 @@ "type": "text", "placeholders_order": [], "placeholders": {} + }, + "About": "关于", + "@About": { + "description": "Title for about menu item in settings", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "App": "应用程序", + "@App": { + "description": "Title for App field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "Login ID": "登录 ID", + "@Login ID": { + "description": "Title for Login ID field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "Email": "电子邮件", + "@Email": { + "description": "Title for Email field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "Version": "版本", + "@Version": { + "description": "Title for Version field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} } } \ No newline at end of file diff --git a/apps/flutter_parent/lib/l10n/res/intl_zh_HK.arb b/apps/flutter_parent/lib/l10n/res/intl_zh_HK.arb index b2d07344b8..c19d37b2ac 100644 --- a/apps/flutter_parent/lib/l10n/res/intl_zh_HK.arb +++ b/apps/flutter_parent/lib/l10n/res/intl_zh_HK.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2023-02-17T11:03:20.619429", + "@@last_modified": "2023-04-14T11:04:46.988317", "alertsLabel": "提醒", "@alertsLabel": { "description": "The label for the Alerts tab", @@ -182,6 +182,19 @@ "points": {} } }, + "calendarDaySemanticsLabel": "{eventCount,plural, =1{{date}、{eventCount} 活動}other{{date}、{eventCount} 活動}}", + "@calendarDaySemanticsLabel": { + "description": "Screen reader label used for calendar day, reads the date and count of events", + "type": "text", + "placeholders_order": [ + "date", + "eventCount" + ], + "placeholders": { + "date": {}, + "eventCount": {} + } + }, "No Events Today!": "今天並無活動!", "@No Events Today!": { "description": "Title displayed when there are no calendar events for the current day", @@ -2694,5 +2707,40 @@ "type": "text", "placeholders_order": [], "placeholders": {} + }, + "About": "關於", + "@About": { + "description": "Title for about menu item in settings", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "App": "應用程式", + "@App": { + "description": "Title for App field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "Login ID": "登入 ID", + "@Login ID": { + "description": "Title for Login ID field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "Email": "電郵", + "@Email": { + "description": "Title for Email field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "Version": "版本", + "@Version": { + "description": "Title for Version field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} } } \ No newline at end of file diff --git a/apps/flutter_parent/lib/l10n/res/intl_zh_Hans.arb b/apps/flutter_parent/lib/l10n/res/intl_zh_Hans.arb index e2c3d89c68..bcb61c5d1d 100644 --- a/apps/flutter_parent/lib/l10n/res/intl_zh_Hans.arb +++ b/apps/flutter_parent/lib/l10n/res/intl_zh_Hans.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2023-02-17T11:03:20.619429", + "@@last_modified": "2023-04-14T11:04:46.988317", "alertsLabel": "警告", "@alertsLabel": { "description": "The label for the Alerts tab", @@ -182,6 +182,19 @@ "points": {} } }, + "calendarDaySemanticsLabel": "{eventCount,plural, =1{{date}、{eventCount} 个事件}other{{date}、{eventCount} 个事件}}", + "@calendarDaySemanticsLabel": { + "description": "Screen reader label used for calendar day, reads the date and count of events", + "type": "text", + "placeholders_order": [ + "date", + "eventCount" + ], + "placeholders": { + "date": {}, + "eventCount": {} + } + }, "No Events Today!": "今天没有事件!", "@No Events Today!": { "description": "Title displayed when there are no calendar events for the current day", @@ -2694,5 +2707,40 @@ "type": "text", "placeholders_order": [], "placeholders": {} + }, + "About": "关于", + "@About": { + "description": "Title for about menu item in settings", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "App": "应用程序", + "@App": { + "description": "Title for App field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "Login ID": "登录 ID", + "@Login ID": { + "description": "Title for Login ID field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "Email": "电子邮件", + "@Email": { + "description": "Title for Email field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "Version": "版本", + "@Version": { + "description": "Title for Version field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} } } \ No newline at end of file diff --git a/apps/flutter_parent/lib/l10n/res/intl_zh_Hant.arb b/apps/flutter_parent/lib/l10n/res/intl_zh_Hant.arb index b2d07344b8..c19d37b2ac 100644 --- a/apps/flutter_parent/lib/l10n/res/intl_zh_Hant.arb +++ b/apps/flutter_parent/lib/l10n/res/intl_zh_Hant.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2023-02-17T11:03:20.619429", + "@@last_modified": "2023-04-14T11:04:46.988317", "alertsLabel": "提醒", "@alertsLabel": { "description": "The label for the Alerts tab", @@ -182,6 +182,19 @@ "points": {} } }, + "calendarDaySemanticsLabel": "{eventCount,plural, =1{{date}、{eventCount} 活動}other{{date}、{eventCount} 活動}}", + "@calendarDaySemanticsLabel": { + "description": "Screen reader label used for calendar day, reads the date and count of events", + "type": "text", + "placeholders_order": [ + "date", + "eventCount" + ], + "placeholders": { + "date": {}, + "eventCount": {} + } + }, "No Events Today!": "今天並無活動!", "@No Events Today!": { "description": "Title displayed when there are no calendar events for the current day", @@ -2694,5 +2707,40 @@ "type": "text", "placeholders_order": [], "placeholders": {} + }, + "About": "關於", + "@About": { + "description": "Title for about menu item in settings", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "App": "應用程式", + "@App": { + "description": "Title for App field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "Login ID": "登入 ID", + "@Login ID": { + "description": "Title for Login ID field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "Email": "電郵", + "@Email": { + "description": "Title for Email field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "Version": "版本", + "@Version": { + "description": "Title for Version field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} } } \ No newline at end of file diff --git a/libs/flutter_student_embed/lib/l10n/res/intl_ar.arb b/libs/flutter_student_embed/lib/l10n/res/intl_ar.arb index 1a638c992f..df47f0ed8d 100644 --- a/libs/flutter_student_embed/lib/l10n/res/intl_ar.arb +++ b/libs/flutter_student_embed/lib/l10n/res/intl_ar.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2022-10-28T11:03:17.232435", + "@@last_modified": "2023-03-31T11:03:46.341309", "coursesLabel": "المساقات", "@coursesLabel": { "description": "The label for the Courses tab", @@ -101,6 +101,19 @@ "points": {} } }, + "calendarDaySemanticsLabel": "{eventCount,plural, =1{{date}، {eventCount} حدث}other{{date}، {eventCount} أحداث}}", + "@calendarDaySemanticsLabel": { + "description": "Screen reader label used for calendar day, reads the date and count of events", + "type": "text", + "placeholders_order": [ + "date", + "eventCount" + ], + "placeholders": { + "date": {}, + "eventCount": {} + } + }, "No Events Today!": "لا توجد أحداث اليوم!", "@No Events Today!": { "description": "Title displayed when there are no calendar events for the current day", diff --git a/libs/flutter_student_embed/lib/l10n/res/intl_ca.arb b/libs/flutter_student_embed/lib/l10n/res/intl_ca.arb index 96e5e799de..b5e7a68d69 100644 --- a/libs/flutter_student_embed/lib/l10n/res/intl_ca.arb +++ b/libs/flutter_student_embed/lib/l10n/res/intl_ca.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2022-10-28T11:03:17.232435", + "@@last_modified": "2023-03-31T11:03:46.341309", "coursesLabel": "Assignatures", "@coursesLabel": { "description": "The label for the Courses tab", @@ -101,6 +101,19 @@ "points": {} } }, + "calendarDaySemanticsLabel": "{eventCount,plural, =1{{date}, {eventCount} esdeveniment}other{{date}, {eventCount} esdeveniments}}", + "@calendarDaySemanticsLabel": { + "description": "Screen reader label used for calendar day, reads the date and count of events", + "type": "text", + "placeholders_order": [ + "date", + "eventCount" + ], + "placeholders": { + "date": {}, + "eventCount": {} + } + }, "No Events Today!": "Avui no hi ha cap esdeveniment.", "@No Events Today!": { "description": "Title displayed when there are no calendar events for the current day", diff --git a/libs/flutter_student_embed/lib/l10n/res/intl_cy.arb b/libs/flutter_student_embed/lib/l10n/res/intl_cy.arb index 5e35fe4919..a5b5d82883 100644 --- a/libs/flutter_student_embed/lib/l10n/res/intl_cy.arb +++ b/libs/flutter_student_embed/lib/l10n/res/intl_cy.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2022-10-28T11:03:17.232435", + "@@last_modified": "2023-03-31T11:03:46.341309", "coursesLabel": "Cyrsiau", "@coursesLabel": { "description": "The label for the Courses tab", @@ -101,6 +101,19 @@ "points": {} } }, + "calendarDaySemanticsLabel": "{eventCount,plural, =1{{date}, {eventCount} digwyddiad}other{{date}, {eventCount} digwyddiad}}", + "@calendarDaySemanticsLabel": { + "description": "Screen reader label used for calendar day, reads the date and count of events", + "type": "text", + "placeholders_order": [ + "date", + "eventCount" + ], + "placeholders": { + "date": {}, + "eventCount": {} + } + }, "No Events Today!": "Dim Digwyddiadau Heddiw!", "@No Events Today!": { "description": "Title displayed when there are no calendar events for the current day", diff --git a/libs/flutter_student_embed/lib/l10n/res/intl_da.arb b/libs/flutter_student_embed/lib/l10n/res/intl_da.arb index 7a65756323..ab70abf121 100644 --- a/libs/flutter_student_embed/lib/l10n/res/intl_da.arb +++ b/libs/flutter_student_embed/lib/l10n/res/intl_da.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2022-10-28T11:03:17.232435", + "@@last_modified": "2023-03-31T11:03:46.341309", "coursesLabel": "Fag", "@coursesLabel": { "description": "The label for the Courses tab", @@ -101,6 +101,19 @@ "points": {} } }, + "calendarDaySemanticsLabel": "{eventCount,plural, =1{{date}, {eventCount} begivenhed}other{{date}, {eventCount} begivenheder}}", + "@calendarDaySemanticsLabel": { + "description": "Screen reader label used for calendar day, reads the date and count of events", + "type": "text", + "placeholders_order": [ + "date", + "eventCount" + ], + "placeholders": { + "date": {}, + "eventCount": {} + } + }, "No Events Today!": "Ingen begivenheder i dag!", "@No Events Today!": { "description": "Title displayed when there are no calendar events for the current day", diff --git a/libs/flutter_student_embed/lib/l10n/res/intl_da_instk12.arb b/libs/flutter_student_embed/lib/l10n/res/intl_da_instk12.arb index d2e2f04ee4..5d2d33c3d4 100644 --- a/libs/flutter_student_embed/lib/l10n/res/intl_da_instk12.arb +++ b/libs/flutter_student_embed/lib/l10n/res/intl_da_instk12.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2022-10-28T11:03:17.232435", + "@@last_modified": "2023-03-31T11:03:46.341309", "coursesLabel": "Fag", "@coursesLabel": { "description": "The label for the Courses tab", @@ -101,6 +101,19 @@ "points": {} } }, + "calendarDaySemanticsLabel": "{eventCount,plural, =1{{date}, {eventCount} begivenhed}other{{date}, {eventCount} begivenheder}}", + "@calendarDaySemanticsLabel": { + "description": "Screen reader label used for calendar day, reads the date and count of events", + "type": "text", + "placeholders_order": [ + "date", + "eventCount" + ], + "placeholders": { + "date": {}, + "eventCount": {} + } + }, "No Events Today!": "Ingen begivenheder i dag!", "@No Events Today!": { "description": "Title displayed when there are no calendar events for the current day", diff --git a/libs/flutter_student_embed/lib/l10n/res/intl_de.arb b/libs/flutter_student_embed/lib/l10n/res/intl_de.arb index 912bd1940a..69c7097f06 100644 --- a/libs/flutter_student_embed/lib/l10n/res/intl_de.arb +++ b/libs/flutter_student_embed/lib/l10n/res/intl_de.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2022-10-28T11:03:17.232435", + "@@last_modified": "2023-03-31T11:03:46.341309", "coursesLabel": "Kurse", "@coursesLabel": { "description": "The label for the Courses tab", @@ -101,6 +101,19 @@ "points": {} } }, + "calendarDaySemanticsLabel": "{eventCount,plural, =1{{date}, {eventCount} Ereignis}other{{date}, {eventCount} Ereignisse}}", + "@calendarDaySemanticsLabel": { + "description": "Screen reader label used for calendar day, reads the date and count of events", + "type": "text", + "placeholders_order": [ + "date", + "eventCount" + ], + "placeholders": { + "date": {}, + "eventCount": {} + } + }, "No Events Today!": "Heute keine Ereignisse!", "@No Events Today!": { "description": "Title displayed when there are no calendar events for the current day", diff --git a/libs/flutter_student_embed/lib/l10n/res/intl_en_AU.arb b/libs/flutter_student_embed/lib/l10n/res/intl_en_AU.arb index 82c16296a7..0d149e4227 100644 --- a/libs/flutter_student_embed/lib/l10n/res/intl_en_AU.arb +++ b/libs/flutter_student_embed/lib/l10n/res/intl_en_AU.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2022-10-28T11:03:17.232435", + "@@last_modified": "2023-03-31T11:03:46.341309", "coursesLabel": "Courses", "@coursesLabel": { "description": "The label for the Courses tab", @@ -101,6 +101,19 @@ "points": {} } }, + "calendarDaySemanticsLabel": "{eventCount,plural, =1{{date}, {eventCount} event}other{{date}, {eventCount} events}}", + "@calendarDaySemanticsLabel": { + "description": "Screen reader label used for calendar day, reads the date and count of events", + "type": "text", + "placeholders_order": [ + "date", + "eventCount" + ], + "placeholders": { + "date": {}, + "eventCount": {} + } + }, "No Events Today!": "No Events Today!", "@No Events Today!": { "description": "Title displayed when there are no calendar events for the current day", diff --git a/libs/flutter_student_embed/lib/l10n/res/intl_en_AU_unimelb.arb b/libs/flutter_student_embed/lib/l10n/res/intl_en_AU_unimelb.arb index fb62f0b8ee..8414f8d960 100644 --- a/libs/flutter_student_embed/lib/l10n/res/intl_en_AU_unimelb.arb +++ b/libs/flutter_student_embed/lib/l10n/res/intl_en_AU_unimelb.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2022-10-28T11:03:17.232435", + "@@last_modified": "2023-03-31T11:03:46.341309", "coursesLabel": "Subjects", "@coursesLabel": { "description": "The label for the Courses tab", @@ -101,6 +101,19 @@ "points": {} } }, + "calendarDaySemanticsLabel": "{eventCount,plural, =1{{date}, {eventCount} event}other{{date}, {eventCount} events}}", + "@calendarDaySemanticsLabel": { + "description": "Screen reader label used for calendar day, reads the date and count of events", + "type": "text", + "placeholders_order": [ + "date", + "eventCount" + ], + "placeholders": { + "date": {}, + "eventCount": {} + } + }, "No Events Today!": "No Events Today!", "@No Events Today!": { "description": "Title displayed when there are no calendar events for the current day", diff --git a/libs/flutter_student_embed/lib/l10n/res/intl_en_CY.arb b/libs/flutter_student_embed/lib/l10n/res/intl_en_CY.arb index a1842afe74..6daa11570b 100644 --- a/libs/flutter_student_embed/lib/l10n/res/intl_en_CY.arb +++ b/libs/flutter_student_embed/lib/l10n/res/intl_en_CY.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2022-10-28T11:03:17.232435", + "@@last_modified": "2023-03-31T11:03:46.341309", "coursesLabel": "Modules", "@coursesLabel": { "description": "The label for the Courses tab", @@ -101,6 +101,19 @@ "points": {} } }, + "calendarDaySemanticsLabel": "{eventCount,plural, =1{{date}, {eventCount} event}other{{date}, {eventCount} events}}", + "@calendarDaySemanticsLabel": { + "description": "Screen reader label used for calendar day, reads the date and count of events", + "type": "text", + "placeholders_order": [ + "date", + "eventCount" + ], + "placeholders": { + "date": {}, + "eventCount": {} + } + }, "No Events Today!": "No Events Today!", "@No Events Today!": { "description": "Title displayed when there are no calendar events for the current day", diff --git a/libs/flutter_student_embed/lib/l10n/res/intl_en_GB.arb b/libs/flutter_student_embed/lib/l10n/res/intl_en_GB.arb index 25cd1e3c4e..1faa04e103 100644 --- a/libs/flutter_student_embed/lib/l10n/res/intl_en_GB.arb +++ b/libs/flutter_student_embed/lib/l10n/res/intl_en_GB.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2022-10-28T11:03:17.232435", + "@@last_modified": "2023-03-31T11:03:46.341309", "coursesLabel": "Courses", "@coursesLabel": { "description": "The label for the Courses tab", @@ -101,6 +101,19 @@ "points": {} } }, + "calendarDaySemanticsLabel": "{eventCount,plural, =1{{date}, {eventCount} event}other{{date}, {eventCount} events}}", + "@calendarDaySemanticsLabel": { + "description": "Screen reader label used for calendar day, reads the date and count of events", + "type": "text", + "placeholders_order": [ + "date", + "eventCount" + ], + "placeholders": { + "date": {}, + "eventCount": {} + } + }, "No Events Today!": "No Events Today!", "@No Events Today!": { "description": "Title displayed when there are no calendar events for the current day", diff --git a/libs/flutter_student_embed/lib/l10n/res/intl_en_GB_instukhe.arb b/libs/flutter_student_embed/lib/l10n/res/intl_en_GB_instukhe.arb index d3d14e047d..6daa11570b 100644 --- a/libs/flutter_student_embed/lib/l10n/res/intl_en_GB_instukhe.arb +++ b/libs/flutter_student_embed/lib/l10n/res/intl_en_GB_instukhe.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2022-01-28T12:37:53.041723", + "@@last_modified": "2023-03-31T11:03:46.341309", "coursesLabel": "Modules", "@coursesLabel": { "description": "The label for the Courses tab", @@ -101,6 +101,19 @@ "points": {} } }, + "calendarDaySemanticsLabel": "{eventCount,plural, =1{{date}, {eventCount} event}other{{date}, {eventCount} events}}", + "@calendarDaySemanticsLabel": { + "description": "Screen reader label used for calendar day, reads the date and count of events", + "type": "text", + "placeholders_order": [ + "date", + "eventCount" + ], + "placeholders": { + "date": {}, + "eventCount": {} + } + }, "No Events Today!": "No Events Today!", "@No Events Today!": { "description": "Title displayed when there are no calendar events for the current day", @@ -122,9 +135,9 @@ "placeholders_order": [], "placeholders": {} }, - "Tap to favorite the courses you want to see on the Calendar.": "Tap to favourite the modules you want to see on the Calendar.", - "@Tap to favorite the courses you want to see on the Calendar.": { - "description": "Description text on calendar filter screen.", + "Select elements to display on the calendar.": "Select elements to display on the calendar.", + "@Select elements to display on the calendar.": { + "description": "Select calendars description", "type": "text", "placeholders_order": [], "placeholders": {} @@ -444,4 +457,4 @@ "time": {} } } -} +} \ No newline at end of file diff --git a/libs/flutter_student_embed/lib/l10n/res/intl_es.arb b/libs/flutter_student_embed/lib/l10n/res/intl_es.arb index 468fba2792..456f393c50 100644 --- a/libs/flutter_student_embed/lib/l10n/res/intl_es.arb +++ b/libs/flutter_student_embed/lib/l10n/res/intl_es.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2022-10-28T11:03:17.232435", + "@@last_modified": "2023-03-31T11:03:46.341309", "coursesLabel": "Cursos", "@coursesLabel": { "description": "The label for the Courses tab", @@ -101,6 +101,19 @@ "points": {} } }, + "calendarDaySemanticsLabel": "{eventCount,plural, =1{{date}, {eventCount} evento}other{{date}, {eventCount} eventos}}", + "@calendarDaySemanticsLabel": { + "description": "Screen reader label used for calendar day, reads the date and count of events", + "type": "text", + "placeholders_order": [ + "date", + "eventCount" + ], + "placeholders": { + "date": {}, + "eventCount": {} + } + }, "No Events Today!": "¡No hay ningún evento hoy!", "@No Events Today!": { "description": "Title displayed when there are no calendar events for the current day", diff --git a/libs/flutter_student_embed/lib/l10n/res/intl_es_ES.arb b/libs/flutter_student_embed/lib/l10n/res/intl_es_ES.arb index 825ade2d33..43eef22427 100644 --- a/libs/flutter_student_embed/lib/l10n/res/intl_es_ES.arb +++ b/libs/flutter_student_embed/lib/l10n/res/intl_es_ES.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2022-10-28T11:03:17.232435", + "@@last_modified": "2023-03-31T11:03:46.341309", "coursesLabel": "Asignaturas", "@coursesLabel": { "description": "The label for the Courses tab", @@ -101,6 +101,19 @@ "points": {} } }, + "calendarDaySemanticsLabel": "{eventCount,plural, =1{{date}, {eventCount} evento}other{{date}, {eventCount} eventos}}", + "@calendarDaySemanticsLabel": { + "description": "Screen reader label used for calendar day, reads the date and count of events", + "type": "text", + "placeholders_order": [ + "date", + "eventCount" + ], + "placeholders": { + "date": {}, + "eventCount": {} + } + }, "No Events Today!": "No hay ningún evento hoy", "@No Events Today!": { "description": "Title displayed when there are no calendar events for the current day", diff --git a/libs/flutter_student_embed/lib/l10n/res/intl_fi.arb b/libs/flutter_student_embed/lib/l10n/res/intl_fi.arb index 9ad58f1a2c..01811b5e04 100644 --- a/libs/flutter_student_embed/lib/l10n/res/intl_fi.arb +++ b/libs/flutter_student_embed/lib/l10n/res/intl_fi.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2022-10-28T11:03:17.232435", + "@@last_modified": "2023-03-31T11:03:46.341309", "coursesLabel": "Kurssit", "@coursesLabel": { "description": "The label for the Courses tab", @@ -101,6 +101,19 @@ "points": {} } }, + "calendarDaySemanticsLabel": "{eventCount,plural, =1{{date}, {eventCount} tapahtuma}other{{date}, {eventCount} tapahtumaa}}", + "@calendarDaySemanticsLabel": { + "description": "Screen reader label used for calendar day, reads the date and count of events", + "type": "text", + "placeholders_order": [ + "date", + "eventCount" + ], + "placeholders": { + "date": {}, + "eventCount": {} + } + }, "No Events Today!": "Ei tapahtumia tänään!", "@No Events Today!": { "description": "Title displayed when there are no calendar events for the current day", diff --git a/libs/flutter_student_embed/lib/l10n/res/intl_fr.arb b/libs/flutter_student_embed/lib/l10n/res/intl_fr.arb index 43db096d53..c8f1da3279 100644 --- a/libs/flutter_student_embed/lib/l10n/res/intl_fr.arb +++ b/libs/flutter_student_embed/lib/l10n/res/intl_fr.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2022-10-28T11:03:17.232435", + "@@last_modified": "2023-03-31T11:03:46.341309", "coursesLabel": "Cours", "@coursesLabel": { "description": "The label for the Courses tab", @@ -101,6 +101,19 @@ "points": {} } }, + "calendarDaySemanticsLabel": "{eventCount,plural, =1{{date}, {eventCount} événement}other{{date}, {eventCount} événements}}", + "@calendarDaySemanticsLabel": { + "description": "Screen reader label used for calendar day, reads the date and count of events", + "type": "text", + "placeholders_order": [ + "date", + "eventCount" + ], + "placeholders": { + "date": {}, + "eventCount": {} + } + }, "No Events Today!": "Aucun événement aujourd'hui !", "@No Events Today!": { "description": "Title displayed when there are no calendar events for the current day", diff --git a/libs/flutter_student_embed/lib/l10n/res/intl_fr_CA.arb b/libs/flutter_student_embed/lib/l10n/res/intl_fr_CA.arb index 8077cdb498..f7caa53a07 100644 --- a/libs/flutter_student_embed/lib/l10n/res/intl_fr_CA.arb +++ b/libs/flutter_student_embed/lib/l10n/res/intl_fr_CA.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2022-10-28T11:03:17.232435", + "@@last_modified": "2023-03-31T11:03:46.341309", "coursesLabel": "Cours", "@coursesLabel": { "description": "The label for the Courses tab", @@ -101,6 +101,19 @@ "points": {} } }, + "calendarDaySemanticsLabel": "{eventCount,plural, =1{{date}, {eventCount} événement}other{{date}, {eventCount} événements}}", + "@calendarDaySemanticsLabel": { + "description": "Screen reader label used for calendar day, reads the date and count of events", + "type": "text", + "placeholders_order": [ + "date", + "eventCount" + ], + "placeholders": { + "date": {}, + "eventCount": {} + } + }, "No Events Today!": "Aucun événement d’aujourd’hui!", "@No Events Today!": { "description": "Title displayed when there are no calendar events for the current day", diff --git a/libs/flutter_student_embed/lib/l10n/res/intl_ht.arb b/libs/flutter_student_embed/lib/l10n/res/intl_ht.arb index 1a915a206e..df331ea92c 100644 --- a/libs/flutter_student_embed/lib/l10n/res/intl_ht.arb +++ b/libs/flutter_student_embed/lib/l10n/res/intl_ht.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2022-10-28T11:03:17.232435", + "@@last_modified": "2023-03-31T11:03:46.341309", "coursesLabel": "Kou", "@coursesLabel": { "description": "The label for the Courses tab", @@ -101,6 +101,19 @@ "points": {} } }, + "calendarDaySemanticsLabel": "{eventCount,plural, =1{{date}, {eventCount} evènman}other{{date}, {eventCount} evènman}}", + "@calendarDaySemanticsLabel": { + "description": "Screen reader label used for calendar day, reads the date and count of events", + "type": "text", + "placeholders_order": [ + "date", + "eventCount" + ], + "placeholders": { + "date": {}, + "eventCount": {} + } + }, "No Events Today!": "Pa gen Aktivite Jodi a!", "@No Events Today!": { "description": "Title displayed when there are no calendar events for the current day", diff --git a/libs/flutter_student_embed/lib/l10n/res/intl_is.arb b/libs/flutter_student_embed/lib/l10n/res/intl_is.arb index 6ee8a5839a..40ec80182c 100644 --- a/libs/flutter_student_embed/lib/l10n/res/intl_is.arb +++ b/libs/flutter_student_embed/lib/l10n/res/intl_is.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2022-10-28T11:03:17.232435", + "@@last_modified": "2023-03-31T11:03:46.341309", "coursesLabel": "Námskeið", "@coursesLabel": { "description": "The label for the Courses tab", @@ -101,6 +101,19 @@ "points": {} } }, + "calendarDaySemanticsLabel": "{eventCount,plural, =1{{date}, {eventCount} viðburður}other{{date}, {eventCount} viðburðir}}", + "@calendarDaySemanticsLabel": { + "description": "Screen reader label used for calendar day, reads the date and count of events", + "type": "text", + "placeholders_order": [ + "date", + "eventCount" + ], + "placeholders": { + "date": {}, + "eventCount": {} + } + }, "No Events Today!": "Engir viðburðir í dag!", "@No Events Today!": { "description": "Title displayed when there are no calendar events for the current day", diff --git a/libs/flutter_student_embed/lib/l10n/res/intl_it.arb b/libs/flutter_student_embed/lib/l10n/res/intl_it.arb index a8b6175ca3..9a6053ab56 100644 --- a/libs/flutter_student_embed/lib/l10n/res/intl_it.arb +++ b/libs/flutter_student_embed/lib/l10n/res/intl_it.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2022-10-28T11:03:17.232435", + "@@last_modified": "2023-03-31T11:03:46.341309", "coursesLabel": "Corsi", "@coursesLabel": { "description": "The label for the Courses tab", @@ -101,6 +101,19 @@ "points": {} } }, + "calendarDaySemanticsLabel": "{eventCount,plural, =1{{date}, {eventCount} evento}other{{date}, {eventCount} eventi}}", + "@calendarDaySemanticsLabel": { + "description": "Screen reader label used for calendar day, reads the date and count of events", + "type": "text", + "placeholders_order": [ + "date", + "eventCount" + ], + "placeholders": { + "date": {}, + "eventCount": {} + } + }, "No Events Today!": "Nessun evento oggi!", "@No Events Today!": { "description": "Title displayed when there are no calendar events for the current day", diff --git a/libs/flutter_student_embed/lib/l10n/res/intl_ja.arb b/libs/flutter_student_embed/lib/l10n/res/intl_ja.arb index 2484eaf737..9a29c1dad8 100644 --- a/libs/flutter_student_embed/lib/l10n/res/intl_ja.arb +++ b/libs/flutter_student_embed/lib/l10n/res/intl_ja.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2022-10-28T11:03:17.232435", + "@@last_modified": "2023-03-31T11:03:46.341309", "coursesLabel": "コース", "@coursesLabel": { "description": "The label for the Courses tab", @@ -101,6 +101,19 @@ "points": {} } }, + "calendarDaySemanticsLabel": "{eventCount,plural, =1{{date}、{eventCount}イベント}other{{date}、{eventCount}イベント}}", + "@calendarDaySemanticsLabel": { + "description": "Screen reader label used for calendar day, reads the date and count of events", + "type": "text", + "placeholders_order": [ + "date", + "eventCount" + ], + "placeholders": { + "date": {}, + "eventCount": {} + } + }, "No Events Today!": "今日イベントはありません!", "@No Events Today!": { "description": "Title displayed when there are no calendar events for the current day", diff --git a/libs/flutter_student_embed/lib/l10n/res/intl_mi.arb b/libs/flutter_student_embed/lib/l10n/res/intl_mi.arb index 68a24f7624..1ac88220fd 100644 --- a/libs/flutter_student_embed/lib/l10n/res/intl_mi.arb +++ b/libs/flutter_student_embed/lib/l10n/res/intl_mi.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2022-10-28T11:03:17.232435", + "@@last_modified": "2023-03-31T11:03:46.341309", "coursesLabel": "Ngā Akoranga", "@coursesLabel": { "description": "The label for the Courses tab", @@ -101,6 +101,19 @@ "points": {} } }, + "calendarDaySemanticsLabel": "{eventCount,plural, =1{{date}, {eventCount} takahanga}other{{date}, {eventCount} takahanga}}", + "@calendarDaySemanticsLabel": { + "description": "Screen reader label used for calendar day, reads the date and count of events", + "type": "text", + "placeholders_order": [ + "date", + "eventCount" + ], + "placeholders": { + "date": {}, + "eventCount": {} + } + }, "No Events Today!": "Kaore he tauwhāinga i tēnei rā!", "@No Events Today!": { "description": "Title displayed when there are no calendar events for the current day", diff --git a/libs/flutter_student_embed/lib/l10n/res/intl_ms.arb b/libs/flutter_student_embed/lib/l10n/res/intl_ms.arb index dc0f9b9546..5c2f9f1f1d 100644 --- a/libs/flutter_student_embed/lib/l10n/res/intl_ms.arb +++ b/libs/flutter_student_embed/lib/l10n/res/intl_ms.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2022-10-28T11:03:17.232435", + "@@last_modified": "2023-03-31T11:03:46.341309", "coursesLabel": "Kursus", "@coursesLabel": { "description": "The label for the Courses tab", @@ -101,6 +101,19 @@ "points": {} } }, + "calendarDaySemanticsLabel": "{eventCount,plural, =1{{date}, {eventCount} acara}other{{date}, {eventCount} acara}}", + "@calendarDaySemanticsLabel": { + "description": "Screen reader label used for calendar day, reads the date and count of events", + "type": "text", + "placeholders_order": [ + "date", + "eventCount" + ], + "placeholders": { + "date": {}, + "eventCount": {} + } + }, "No Events Today!": "Tiada Acara Hari Ini!", "@No Events Today!": { "description": "Title displayed when there are no calendar events for the current day", diff --git a/libs/flutter_student_embed/lib/l10n/res/intl_nb.arb b/libs/flutter_student_embed/lib/l10n/res/intl_nb.arb index c38a824e34..be1ef9e45a 100644 --- a/libs/flutter_student_embed/lib/l10n/res/intl_nb.arb +++ b/libs/flutter_student_embed/lib/l10n/res/intl_nb.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2022-10-28T11:03:17.232435", + "@@last_modified": "2023-03-31T11:03:46.341309", "coursesLabel": "Emner", "@coursesLabel": { "description": "The label for the Courses tab", @@ -101,6 +101,19 @@ "points": {} } }, + "calendarDaySemanticsLabel": "{eventCount,plural, =1{{date}, {eventCount} hendelse}other{{date}, {eventCount} hendelser}}", + "@calendarDaySemanticsLabel": { + "description": "Screen reader label used for calendar day, reads the date and count of events", + "type": "text", + "placeholders_order": [ + "date", + "eventCount" + ], + "placeholders": { + "date": {}, + "eventCount": {} + } + }, "No Events Today!": "Ingen arrangementer i dag!", "@No Events Today!": { "description": "Title displayed when there are no calendar events for the current day", diff --git a/libs/flutter_student_embed/lib/l10n/res/intl_nb_instk12.arb b/libs/flutter_student_embed/lib/l10n/res/intl_nb_instk12.arb index 2a7aa4d9a5..dfda522719 100644 --- a/libs/flutter_student_embed/lib/l10n/res/intl_nb_instk12.arb +++ b/libs/flutter_student_embed/lib/l10n/res/intl_nb_instk12.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2022-10-28T11:03:17.232435", + "@@last_modified": "2023-03-31T11:03:46.341309", "coursesLabel": "Fag", "@coursesLabel": { "description": "The label for the Courses tab", @@ -101,6 +101,19 @@ "points": {} } }, + "calendarDaySemanticsLabel": "{eventCount,plural, =1{{date}, {eventCount} hendelse}other{{date}, {eventCount} hendelser}}", + "@calendarDaySemanticsLabel": { + "description": "Screen reader label used for calendar day, reads the date and count of events", + "type": "text", + "placeholders_order": [ + "date", + "eventCount" + ], + "placeholders": { + "date": {}, + "eventCount": {} + } + }, "No Events Today!": "Ingen arrangementer i dag!", "@No Events Today!": { "description": "Title displayed when there are no calendar events for the current day", diff --git a/libs/flutter_student_embed/lib/l10n/res/intl_nl.arb b/libs/flutter_student_embed/lib/l10n/res/intl_nl.arb index e1dda6e464..30cd7251ff 100644 --- a/libs/flutter_student_embed/lib/l10n/res/intl_nl.arb +++ b/libs/flutter_student_embed/lib/l10n/res/intl_nl.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2022-10-28T11:03:17.232435", + "@@last_modified": "2023-03-31T11:03:46.341309", "coursesLabel": "Cursussen", "@coursesLabel": { "description": "The label for the Courses tab", @@ -101,6 +101,19 @@ "points": {} } }, + "calendarDaySemanticsLabel": "{eventCount,plural, =1{{date}, {eventCount} gebeurtenis}other{{date}, {eventCount} gebeurtenissen}}", + "@calendarDaySemanticsLabel": { + "description": "Screen reader label used for calendar day, reads the date and count of events", + "type": "text", + "placeholders_order": [ + "date", + "eventCount" + ], + "placeholders": { + "date": {}, + "eventCount": {} + } + }, "No Events Today!": "Vandaag geen gebeurtenissen!", "@No Events Today!": { "description": "Title displayed when there are no calendar events for the current day", diff --git a/libs/flutter_student_embed/lib/l10n/res/intl_pl.arb b/libs/flutter_student_embed/lib/l10n/res/intl_pl.arb index a471cca586..6c53e7d56a 100644 --- a/libs/flutter_student_embed/lib/l10n/res/intl_pl.arb +++ b/libs/flutter_student_embed/lib/l10n/res/intl_pl.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2022-10-28T11:03:17.232435", + "@@last_modified": "2023-03-31T11:03:46.341309", "coursesLabel": "Kursy", "@coursesLabel": { "description": "The label for the Courses tab", @@ -101,6 +101,19 @@ "points": {} } }, + "calendarDaySemanticsLabel": "{eventCount,plural, =1{{date}, {eventCount} zdarzenie}other{{date}, {eventCount} zdarzenia}}", + "@calendarDaySemanticsLabel": { + "description": "Screen reader label used for calendar day, reads the date and count of events", + "type": "text", + "placeholders_order": [ + "date", + "eventCount" + ], + "placeholders": { + "date": {}, + "eventCount": {} + } + }, "No Events Today!": "Brak wydarzeń na dziś!", "@No Events Today!": { "description": "Title displayed when there are no calendar events for the current day", diff --git a/libs/flutter_student_embed/lib/l10n/res/intl_pt_BR.arb b/libs/flutter_student_embed/lib/l10n/res/intl_pt_BR.arb index 90e37fc92e..39a2d53c87 100644 --- a/libs/flutter_student_embed/lib/l10n/res/intl_pt_BR.arb +++ b/libs/flutter_student_embed/lib/l10n/res/intl_pt_BR.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2022-10-28T11:03:17.232435", + "@@last_modified": "2023-03-31T11:03:46.341309", "coursesLabel": "Cursos", "@coursesLabel": { "description": "The label for the Courses tab", @@ -101,6 +101,19 @@ "points": {} } }, + "calendarDaySemanticsLabel": "{eventCount,plural, =1{{date}, {eventCount} evento}other{{date}, {eventCount} eventos}}", + "@calendarDaySemanticsLabel": { + "description": "Screen reader label used for calendar day, reads the date and count of events", + "type": "text", + "placeholders_order": [ + "date", + "eventCount" + ], + "placeholders": { + "date": {}, + "eventCount": {} + } + }, "No Events Today!": "Nenhum evento hoje!", "@No Events Today!": { "description": "Title displayed when there are no calendar events for the current day", diff --git a/libs/flutter_student_embed/lib/l10n/res/intl_pt_PT.arb b/libs/flutter_student_embed/lib/l10n/res/intl_pt_PT.arb index 612fb0ee44..3a16fe8130 100644 --- a/libs/flutter_student_embed/lib/l10n/res/intl_pt_PT.arb +++ b/libs/flutter_student_embed/lib/l10n/res/intl_pt_PT.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2022-10-28T11:03:17.232435", + "@@last_modified": "2023-03-31T11:03:46.341309", "coursesLabel": "Disciplinas", "@coursesLabel": { "description": "The label for the Courses tab", @@ -101,6 +101,19 @@ "points": {} } }, + "calendarDaySemanticsLabel": "{eventCount,plural, =1{{date}, {eventCount} evento}other{{date}, {eventCount} eventos}}", + "@calendarDaySemanticsLabel": { + "description": "Screen reader label used for calendar day, reads the date and count of events", + "type": "text", + "placeholders_order": [ + "date", + "eventCount" + ], + "placeholders": { + "date": {}, + "eventCount": {} + } + }, "No Events Today!": "Nenhum evento hoje!", "@No Events Today!": { "description": "Title displayed when there are no calendar events for the current day", diff --git a/libs/flutter_student_embed/lib/l10n/res/intl_ru.arb b/libs/flutter_student_embed/lib/l10n/res/intl_ru.arb index 4cb80c05d7..4b80df0a1a 100644 --- a/libs/flutter_student_embed/lib/l10n/res/intl_ru.arb +++ b/libs/flutter_student_embed/lib/l10n/res/intl_ru.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2022-10-28T11:03:17.232435", + "@@last_modified": "2023-03-31T11:03:46.341309", "coursesLabel": "Курсы", "@coursesLabel": { "description": "The label for the Courses tab", @@ -101,6 +101,19 @@ "points": {} } }, + "calendarDaySemanticsLabel": "{eventCount,plural, =1{{date}, {eventCount} событие}other{{date}, {eventCount} события}}", + "@calendarDaySemanticsLabel": { + "description": "Screen reader label used for calendar day, reads the date and count of events", + "type": "text", + "placeholders_order": [ + "date", + "eventCount" + ], + "placeholders": { + "date": {}, + "eventCount": {} + } + }, "No Events Today!": "На сегодня события отсутствуют!", "@No Events Today!": { "description": "Title displayed when there are no calendar events for the current day", diff --git a/libs/flutter_student_embed/lib/l10n/res/intl_sl.arb b/libs/flutter_student_embed/lib/l10n/res/intl_sl.arb index d4d0ffc39f..5b1bb6c4d8 100644 --- a/libs/flutter_student_embed/lib/l10n/res/intl_sl.arb +++ b/libs/flutter_student_embed/lib/l10n/res/intl_sl.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2022-10-28T11:03:17.232435", + "@@last_modified": "2023-03-31T11:03:46.341309", "coursesLabel": "Predmeti", "@coursesLabel": { "description": "The label for the Courses tab", @@ -101,6 +101,19 @@ "points": {} } }, + "calendarDaySemanticsLabel": "{eventCount,plural, =1{{date}, {eventCount} dogodek}other{{date}, {eventCount} dogodkov(-a/-i)}}", + "@calendarDaySemanticsLabel": { + "description": "Screen reader label used for calendar day, reads the date and count of events", + "type": "text", + "placeholders_order": [ + "date", + "eventCount" + ], + "placeholders": { + "date": {}, + "eventCount": {} + } + }, "No Events Today!": "Danes ni dogodkov.", "@No Events Today!": { "description": "Title displayed when there are no calendar events for the current day", diff --git a/libs/flutter_student_embed/lib/l10n/res/intl_sv.arb b/libs/flutter_student_embed/lib/l10n/res/intl_sv.arb index cc7d37ce15..2ed3fb7586 100644 --- a/libs/flutter_student_embed/lib/l10n/res/intl_sv.arb +++ b/libs/flutter_student_embed/lib/l10n/res/intl_sv.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2022-10-28T11:03:17.232435", + "@@last_modified": "2023-03-31T11:03:46.341309", "coursesLabel": "Kurser", "@coursesLabel": { "description": "The label for the Courses tab", @@ -101,6 +101,19 @@ "points": {} } }, + "calendarDaySemanticsLabel": "{eventCount,plural, =1{{date}, {eventCount} händelse}other{{date}, {eventCount} händelser}}", + "@calendarDaySemanticsLabel": { + "description": "Screen reader label used for calendar day, reads the date and count of events", + "type": "text", + "placeholders_order": [ + "date", + "eventCount" + ], + "placeholders": { + "date": {}, + "eventCount": {} + } + }, "No Events Today!": "Inga händelser idag!", "@No Events Today!": { "description": "Title displayed when there are no calendar events for the current day", diff --git a/libs/flutter_student_embed/lib/l10n/res/intl_sv_instk12.arb b/libs/flutter_student_embed/lib/l10n/res/intl_sv_instk12.arb index a6326a5d65..bfccff3ee6 100644 --- a/libs/flutter_student_embed/lib/l10n/res/intl_sv_instk12.arb +++ b/libs/flutter_student_embed/lib/l10n/res/intl_sv_instk12.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2022-10-28T11:03:17.232435", + "@@last_modified": "2023-03-31T11:03:46.341309", "coursesLabel": "Kurser", "@coursesLabel": { "description": "The label for the Courses tab", @@ -101,6 +101,19 @@ "points": {} } }, + "calendarDaySemanticsLabel": "{eventCount,plural, =1{{date}, {eventCount} händelse}other{{date}, {eventCount} händelser}}", + "@calendarDaySemanticsLabel": { + "description": "Screen reader label used for calendar day, reads the date and count of events", + "type": "text", + "placeholders_order": [ + "date", + "eventCount" + ], + "placeholders": { + "date": {}, + "eventCount": {} + } + }, "No Events Today!": "Inga händelser idag!", "@No Events Today!": { "description": "Title displayed when there are no calendar events for the current day", diff --git a/libs/flutter_student_embed/lib/l10n/res/intl_th.arb b/libs/flutter_student_embed/lib/l10n/res/intl_th.arb index 77ebf3ffdf..a30482afc3 100644 --- a/libs/flutter_student_embed/lib/l10n/res/intl_th.arb +++ b/libs/flutter_student_embed/lib/l10n/res/intl_th.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2022-10-28T11:03:17.232435", + "@@last_modified": "2023-03-31T11:03:46.341309", "coursesLabel": "บทเรียน", "@coursesLabel": { "description": "The label for the Courses tab", @@ -101,6 +101,19 @@ "points": {} } }, + "calendarDaySemanticsLabel": "{eventCount,plural, =1{{date}, {eventCount} กิจกรรม}other{{date}, {eventCount} กิจกรรม}}", + "@calendarDaySemanticsLabel": { + "description": "Screen reader label used for calendar day, reads the date and count of events", + "type": "text", + "placeholders_order": [ + "date", + "eventCount" + ], + "placeholders": { + "date": {}, + "eventCount": {} + } + }, "No Events Today!": "ไม่มีกิจกรรมในวันนี้!", "@No Events Today!": { "description": "Title displayed when there are no calendar events for the current day", diff --git a/libs/flutter_student_embed/lib/l10n/res/intl_vi.arb b/libs/flutter_student_embed/lib/l10n/res/intl_vi.arb index 01bc1ae69c..5d0e7f09c0 100644 --- a/libs/flutter_student_embed/lib/l10n/res/intl_vi.arb +++ b/libs/flutter_student_embed/lib/l10n/res/intl_vi.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2022-10-28T11:03:17.232435", + "@@last_modified": "2023-03-31T11:03:46.341309", "coursesLabel": "Khóa Học", "@coursesLabel": { "description": "The label for the Courses tab", @@ -101,6 +101,19 @@ "points": {} } }, + "calendarDaySemanticsLabel": "{eventCount,plural, =1{{date}, {eventCount} sự kiện}other{{date}, {eventCount} sự kiện}}", + "@calendarDaySemanticsLabel": { + "description": "Screen reader label used for calendar day, reads the date and count of events", + "type": "text", + "placeholders_order": [ + "date", + "eventCount" + ], + "placeholders": { + "date": {}, + "eventCount": {} + } + }, "No Events Today!": "Không Có Sự Kiện Hôm Nay!", "@No Events Today!": { "description": "Title displayed when there are no calendar events for the current day", diff --git a/libs/flutter_student_embed/lib/l10n/res/intl_zh.arb b/libs/flutter_student_embed/lib/l10n/res/intl_zh.arb index 9b40b83431..7a31fbfe80 100644 --- a/libs/flutter_student_embed/lib/l10n/res/intl_zh.arb +++ b/libs/flutter_student_embed/lib/l10n/res/intl_zh.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2022-10-28T11:03:17.232435", + "@@last_modified": "2023-03-31T11:03:46.341309", "coursesLabel": "课程", "@coursesLabel": { "description": "The label for the Courses tab", @@ -101,6 +101,19 @@ "points": {} } }, + "calendarDaySemanticsLabel": "{eventCount,plural, =1{{date}、{eventCount} 个事件}other{{date}、{eventCount} 个事件}}", + "@calendarDaySemanticsLabel": { + "description": "Screen reader label used for calendar day, reads the date and count of events", + "type": "text", + "placeholders_order": [ + "date", + "eventCount" + ], + "placeholders": { + "date": {}, + "eventCount": {} + } + }, "No Events Today!": "今天没有事件!", "@No Events Today!": { "description": "Title displayed when there are no calendar events for the current day", diff --git a/libs/flutter_student_embed/lib/l10n/res/intl_zh_HK.arb b/libs/flutter_student_embed/lib/l10n/res/intl_zh_HK.arb index a273346581..ea89647c0e 100644 --- a/libs/flutter_student_embed/lib/l10n/res/intl_zh_HK.arb +++ b/libs/flutter_student_embed/lib/l10n/res/intl_zh_HK.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2022-10-28T11:03:17.232435", + "@@last_modified": "2023-03-31T11:03:46.341309", "coursesLabel": "課程", "@coursesLabel": { "description": "The label for the Courses tab", @@ -101,6 +101,19 @@ "points": {} } }, + "calendarDaySemanticsLabel": "{eventCount,plural, =1{{date}、{eventCount} 活動}other{{date}、{eventCount} 活動}}", + "@calendarDaySemanticsLabel": { + "description": "Screen reader label used for calendar day, reads the date and count of events", + "type": "text", + "placeholders_order": [ + "date", + "eventCount" + ], + "placeholders": { + "date": {}, + "eventCount": {} + } + }, "No Events Today!": "今天並無活動!", "@No Events Today!": { "description": "Title displayed when there are no calendar events for the current day", diff --git a/libs/flutter_student_embed/lib/l10n/res/intl_zh_Hans.arb b/libs/flutter_student_embed/lib/l10n/res/intl_zh_Hans.arb index d6b8ce26b4..7a31fbfe80 100644 --- a/libs/flutter_student_embed/lib/l10n/res/intl_zh_Hans.arb +++ b/libs/flutter_student_embed/lib/l10n/res/intl_zh_Hans.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2022-01-28T12:37:53.041723", + "@@last_modified": "2023-03-31T11:03:46.341309", "coursesLabel": "课程", "@coursesLabel": { "description": "The label for the Courses tab", @@ -101,6 +101,19 @@ "points": {} } }, + "calendarDaySemanticsLabel": "{eventCount,plural, =1{{date}、{eventCount} 个事件}other{{date}、{eventCount} 个事件}}", + "@calendarDaySemanticsLabel": { + "description": "Screen reader label used for calendar day, reads the date and count of events", + "type": "text", + "placeholders_order": [ + "date", + "eventCount" + ], + "placeholders": { + "date": {}, + "eventCount": {} + } + }, "No Events Today!": "今天没有事件!", "@No Events Today!": { "description": "Title displayed when there are no calendar events for the current day", @@ -122,9 +135,9 @@ "placeholders_order": [], "placeholders": {} }, - "Tap to favorite the courses you want to see on the Calendar.": "点击以收藏您希望在日历中看到的课程。", - "@Tap to favorite the courses you want to see on the Calendar.": { - "description": "Description text on calendar filter screen.", + "Select elements to display on the calendar.": "请选择要在日历上显示的元素。", + "@Select elements to display on the calendar.": { + "description": "Select calendars description", "type": "text", "placeholders_order": [], "placeholders": {} @@ -444,4 +457,4 @@ "time": {} } } -} +} \ No newline at end of file diff --git a/libs/flutter_student_embed/lib/l10n/res/intl_zh_Hant.arb b/libs/flutter_student_embed/lib/l10n/res/intl_zh_Hant.arb index 910fe92063..ea89647c0e 100644 --- a/libs/flutter_student_embed/lib/l10n/res/intl_zh_Hant.arb +++ b/libs/flutter_student_embed/lib/l10n/res/intl_zh_Hant.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2022-01-28T12:37:53.041723", + "@@last_modified": "2023-03-31T11:03:46.341309", "coursesLabel": "課程", "@coursesLabel": { "description": "The label for the Courses tab", @@ -101,6 +101,19 @@ "points": {} } }, + "calendarDaySemanticsLabel": "{eventCount,plural, =1{{date}、{eventCount} 活動}other{{date}、{eventCount} 活動}}", + "@calendarDaySemanticsLabel": { + "description": "Screen reader label used for calendar day, reads the date and count of events", + "type": "text", + "placeholders_order": [ + "date", + "eventCount" + ], + "placeholders": { + "date": {}, + "eventCount": {} + } + }, "No Events Today!": "今天並無活動!", "@No Events Today!": { "description": "Title displayed when there are no calendar events for the current day", @@ -122,9 +135,9 @@ "placeholders_order": [], "placeholders": {} }, - "Tap to favorite the courses you want to see on the Calendar.": "點選以選擇您要在行事曆上看到的最愛課程。", - "@Tap to favorite the courses you want to see on the Calendar.": { - "description": "Description text on calendar filter screen.", + "Select elements to display on the calendar.": "選擇要在行事曆上顯示的元素。", + "@Select elements to display on the calendar.": { + "description": "Select calendars description", "type": "text", "placeholders_order": [], "placeholders": {} @@ -444,4 +457,4 @@ "time": {} } } -} +} \ No newline at end of file diff --git a/libs/pandares/src/main/res/values-ar/strings.xml b/libs/pandares/src/main/res/values-ar/strings.xml index 57545a0bc7..9d7e42efb7 100644 --- a/libs/pandares/src/main/res/values-ar/strings.xml +++ b/libs/pandares/src/main/res/values-ar/strings.xml @@ -1357,11 +1357,21 @@ إغلاق مربع حوار التقدم %1$s من %2$s جارٍ التحميل إلى الملفات + تعذر تحميل ملف أو أكثر. تحقق من اتصالك بالإنترنت وأعد محاولة الإرسال. جارٍ تحميل الإرسال إلى \"%s\" جارٍ تحميل الإرسال نجح الإرسال فشل الإرسال - جارٍ تحميل الملفات + + جارٍ تحميل الملفات + جارٍ تحميل الملف + جارٍ تحميل الملفات + جارٍ تحميل الملفات + جارٍ تحميل الملفات + جارٍ تحميل الملفات + + تم تحميل الملف بنجاح + فشل تحميل الملف إلغاء الإرسال سيؤدي هذا إلى إلغاء وحذف إرسالك. تعذر تحميل الملف @@ -1444,4 +1454,9 @@ تم إلغاء تنشيط وضع التحديد. صورة رمزية %s تراجع + التطبيق + المجال + معرف تسجيل الدخول + البريد الإلكتروني + الإصدار diff --git a/libs/pandares/src/main/res/values-b+da+DK+instk12/strings.xml b/libs/pandares/src/main/res/values-b+da+DK+instk12/strings.xml index d563f1aaa1..20884a6377 100644 --- a/libs/pandares/src/main/res/values-b+da+DK+instk12/strings.xml +++ b/libs/pandares/src/main/res/values-b+da+DK+instk12/strings.xml @@ -1294,11 +1294,17 @@ Luk statusdialog %1$s af %2$s Uploader til filer + En eller flere filer kunne ikke uploades. Kontroller din internetforbindelse, og prøv igen for at aflevere. Uploader indsendelse til \"%s\" Uploader aflevering Aflevering lykkedes Aflevering mislykkedes - Uploader filer + + Uploader fil + Uploader filer + + Filupload lykkedes + Filupload mislykkedes Annuller aflevering Dette annullerer og sletter din aflevering. Filupload mislykkedes @@ -1377,4 +1383,9 @@ Valgtilstand deaktiveret. Avatar for %s Fortryd + App + Domæne + Login-id + E-mail + Version diff --git a/libs/pandares/src/main/res/values-b+en+AU+unimelb/strings.xml b/libs/pandares/src/main/res/values-b+en+AU+unimelb/strings.xml index a5a1c17882..21dbd639d4 100644 --- a/libs/pandares/src/main/res/values-b+en+AU+unimelb/strings.xml +++ b/libs/pandares/src/main/res/values-b+en+AU+unimelb/strings.xml @@ -1294,11 +1294,17 @@ Close progress dialogue %1$s of %2$s Uploading to Files + One or more files failed to upload. Check your internet connection and retry to submit. Uploading submission to \"%s\" Uploading Submission - Submission Success + Submission Successful Submission Failed - Uploading Files + + Uploading File + Uploading Files + + File Upload Successful + File Upload Failed Cancel Submission This will cancel and delete your submission. File Upload Failed @@ -1377,4 +1383,9 @@ Selection mode deactivated. Avatar of %s Undo + App + Domain + Login ID + Email + Version diff --git a/libs/pandares/src/main/res/values-b+en+GB+instukhe/strings.xml b/libs/pandares/src/main/res/values-b+en+GB+instukhe/strings.xml index 62e5bd1896..6440be837b 100644 --- a/libs/pandares/src/main/res/values-b+en+GB+instukhe/strings.xml +++ b/libs/pandares/src/main/res/values-b+en+GB+instukhe/strings.xml @@ -1294,11 +1294,17 @@ Close progress dialogue %1$s of %2$s Uploading to Files + One or more files failed to upload. Check your internet connection and retry to submit. Uploading submission to \"%s\" Uploading submission - Submission Success + Submission Successful Submission failed - Uploading Files + + Uploading File + Uploading Files + + File Upload Successful + File Upload Failed Cancel Submission This will cancel and delete your submission. File Upload Failed @@ -1377,4 +1383,9 @@ Selection mode deactivated. Avatar of %s Undo + App + Domain + Login ID + Email + Version diff --git a/libs/pandares/src/main/res/values-b+nb+NO+instk12/strings.xml b/libs/pandares/src/main/res/values-b+nb+NO+instk12/strings.xml index 4369c6e8c7..90ed8331e9 100644 --- a/libs/pandares/src/main/res/values-b+nb+NO+instk12/strings.xml +++ b/libs/pandares/src/main/res/values-b+nb+NO+instk12/strings.xml @@ -1295,11 +1295,17 @@ Lukk fremgangsdialog %1$s av %2$s Last opp til Filer + En eller flere filer kunne ikke lastes opp. Sjekk internettforbindelsen din og prøv å lever på nytt. Laster opp innlevering til \"%s\" Laster opp innlevering Innlevering vellykket Innlevering feilet - Laste opp filer + + Laster opp fil + Laste opp filer + + Filen ble lastet opp + Filopplasting mislyktes Avbryt innlevering Dette avbryter og sletter innleveringen din. Filopplasting mislyktes @@ -1378,4 +1384,9 @@ Valgmodus deaktivert. Avatar av %s Angre + App + Domene + Innloggings-ID + E-post + Versjon diff --git a/libs/pandares/src/main/res/values-b+sv+SE+instk12/strings.xml b/libs/pandares/src/main/res/values-b+sv+SE+instk12/strings.xml index 4c73ed4bac..5cf6960010 100644 --- a/libs/pandares/src/main/res/values-b+sv+SE+instk12/strings.xml +++ b/libs/pandares/src/main/res/values-b+sv+SE+instk12/strings.xml @@ -1294,11 +1294,17 @@ Stäng förloppsdialog %1$s av %2$s Laddar upp till Filer + En eller fler filer laddades inte upp. Kontrollera din internetanslutning och försök lämna in igen. Laddar upp inlämning till \"%s\" Laddar upp inlämning - Inlämningen lyckades + Inlämningen slutfördes Inlämningen misslyckades - Laddar upp filer + + Laddar upp fil + Laddar upp filer + + Filuppladdningen slutfördes + Filuppladdningen misslyckades Avbryt inlämningen Detta avbryter och tar bort din inlämning. Filuppladdningen misslyckades @@ -1377,4 +1383,9 @@ Urvalsläget inaktiverat. Avatar för %s Ångra + App + Domän + Inloggnings-ID + E-post + Version diff --git a/libs/pandares/src/main/res/values-b+zh+HK/strings.xml b/libs/pandares/src/main/res/values-b+zh+HK/strings.xml index b4d3b8c260..25773b6b92 100644 --- a/libs/pandares/src/main/res/values-b+zh+HK/strings.xml +++ b/libs/pandares/src/main/res/values-b+zh+HK/strings.xml @@ -1278,11 +1278,16 @@ 關閉進度對話 %2$s 的 %1$s 上傳到檔案 + 上傳一個或多個檔案失敗。檢查您的網際網路連線並重試提交。 上傳提交項目到 \"%s\" 正在上傳提交項目 - 提交項目成功 + 成功提交 提交項目失敗 - 上傳檔案 + + 正在上傳檔案 + + 成功上傳檔案 + 檔案上傳失敗 取消提交項目 此動作將取消並刪除您的提交項目。 檔案上傳失敗 @@ -1360,4 +1365,9 @@ 選擇模式停用 %s 的頭像 復原 + 應用程式 + 網域 + 登入 ID + 電郵 + 版本 diff --git a/libs/pandares/src/main/res/values-b+zh+Hans/strings.xml b/libs/pandares/src/main/res/values-b+zh+Hans/strings.xml index 22bf269c32..fcc8e49b2e 100644 --- a/libs/pandares/src/main/res/values-b+zh+Hans/strings.xml +++ b/libs/pandares/src/main/res/values-b+zh+Hans/strings.xml @@ -1278,11 +1278,16 @@ 关闭进度对话框 %1$s,共%2$s 上传到文件 + 一份或多个文件上传失败。请检查网络连接,并再次尝试提交。 上传提交项到\"%s\" 正在上传提交作业 - 成功提交文件 + 提交成功 提交文件失败 - 上传文件 + + 正在上传文件 + + 文件上传成功 + 文件上传失败 取消提交 此操作将取消并删除您的提交项。 文件上传失败 @@ -1360,4 +1365,9 @@ 选择模式已被禁用。 %s头像 后退 + 应用程序 + + 登录 ID + 电子邮件 + 版本 diff --git a/libs/pandares/src/main/res/values-b+zh+Hant/strings.xml b/libs/pandares/src/main/res/values-b+zh+Hant/strings.xml index b4d3b8c260..25773b6b92 100644 --- a/libs/pandares/src/main/res/values-b+zh+Hant/strings.xml +++ b/libs/pandares/src/main/res/values-b+zh+Hant/strings.xml @@ -1278,11 +1278,16 @@ 關閉進度對話 %2$s 的 %1$s 上傳到檔案 + 上傳一個或多個檔案失敗。檢查您的網際網路連線並重試提交。 上傳提交項目到 \"%s\" 正在上傳提交項目 - 提交項目成功 + 成功提交 提交項目失敗 - 上傳檔案 + + 正在上傳檔案 + + 成功上傳檔案 + 檔案上傳失敗 取消提交項目 此動作將取消並刪除您的提交項目。 檔案上傳失敗 @@ -1360,4 +1365,9 @@ 選擇模式停用 %s 的頭像 復原 + 應用程式 + 網域 + 登入 ID + 電郵 + 版本 diff --git a/libs/pandares/src/main/res/values-ca/strings.xml b/libs/pandares/src/main/res/values-ca/strings.xml index 6e3ca1ddeb..871fa87e98 100644 --- a/libs/pandares/src/main/res/values-ca/strings.xml +++ b/libs/pandares/src/main/res/values-ca/strings.xml @@ -1295,11 +1295,17 @@ Tanqueu el quadre de diàleg de progrés %1$s de %2$s S\'està penjant als fitxers + No s\'han pogut penjar un o més fitxers. Reviseu la connexió a Internet i torneu a provar de fer l\'entrega. S\'està penjant l\'entrega a \"%s\" S\'està carregant l\'entrega - S\'ha realitzat correctament l\'entrega + S’ha fet l’entrega correctament S’ha produït un error en fer l\'entrega - S\'estan penjant els fitxers + + S\'està penjant el fitxer + S\'estan penjant els fitxers + + S’ha penjat el fitxer correctament + No s\'ha pogut penjar el fitxer Cancel·la l\'entrega Amb aquesta acció es cancel·larà i se suprimirà l\'entrega. No s\'ha pogut penjar el fitxer @@ -1378,4 +1384,9 @@ S’ha desactivat el mode de selecció. Avatar de %s Desfés + Aplicació + Domini + ID d\'inici de sessió + Correu electrònic + Versió diff --git a/libs/pandares/src/main/res/values-cy/strings.xml b/libs/pandares/src/main/res/values-cy/strings.xml index a3fc35dd77..a8368fe024 100644 --- a/libs/pandares/src/main/res/values-cy/strings.xml +++ b/libs/pandares/src/main/res/values-cy/strings.xml @@ -1294,11 +1294,17 @@ Cau’r deialog cynnydd %1$s o %2$s Wrthi’n llwytho i fyny i Ffeiliau + Wedi methu llwytho un neu ragor o ffeiliau i fyny. Gwiriwch eich cysylltiad rhyngrwyd a rhoi cynnig arall ar gyflwyno. Wrthi’n llwytho cyflwyniad i fyny i \"%s\" Llwytho cyflwyniad i fyny - Wedi Llwyddo i Gyflwyno + Wedi llwyddo i gyflwyno Cyflwyniad wedi methu - Wrthi’n llwytho ffeiliau i fyny + + Wrthi’n llwytho ffeil i fyny + Wrthi’n llwytho ffeiliau i fyny + + Wedi llwyddo i lwytho ffeil i fyny + Wedi methu llwytho ffeil i fyny Canslo Cyflwyniad Bydd hyn yn canslo ac yn dileu eich cyflwyniad. Wedi methu llwytho ffeil i fyny @@ -1377,4 +1383,9 @@ Modd dewis ddim ar waith. Afatar o %s Dad-wneud + Ap + Parth + ID Mewngofnodi + E-bost + Fersiwn diff --git a/libs/pandares/src/main/res/values-da/strings.xml b/libs/pandares/src/main/res/values-da/strings.xml index e025ef49d6..274ef8256d 100644 --- a/libs/pandares/src/main/res/values-da/strings.xml +++ b/libs/pandares/src/main/res/values-da/strings.xml @@ -1294,11 +1294,17 @@ Luk statusdialog %1$s af %2$s Uploader til filer + En eller flere filer kunne ikke uploades. Kontroller din internetforbindelse, og prøv igen for at aflevere. Uploader indsendelse til \"%s\" Uploader aflevering Aflevering lykkedes Aflevering mislykkedes - Uploader filer + + Uploader fil + Uploader filer + + Filupload lykkedes + Filupload mislykkedes Annuller aflevering Dette annullerer og sletter din aflevering. Filupload mislykkedes @@ -1377,4 +1383,9 @@ Valgtilstand deaktiveret. Avatar for %s Fortryd + App + Domæne + Login-id + E-mail + Version diff --git a/libs/pandares/src/main/res/values-de/strings.xml b/libs/pandares/src/main/res/values-de/strings.xml index b5d24c86cb..e297b35d31 100644 --- a/libs/pandares/src/main/res/values-de/strings.xml +++ b/libs/pandares/src/main/res/values-de/strings.xml @@ -1294,11 +1294,17 @@ Fortschrittsdialog schließen %1$s von %2$s Wird nach „Dateien“ hochgeladen + Eine oder mehrere Dateien wurden nicht hochgeladen. Überprüfen Sie Ihre Internetverbindung, und versuchen Sie es erneut. Abgabe wird zu \"%s\" hochgeladen Abgabe wird hochgeladen - Abgabe erfolgreichAbgabe erfolgreich + Erfolgreich gesendet Abgabe fehlgeschlagen - Dateien hochladen + + Datei wird hochgeladen + Dateien werden hochgeladen + + Hochladen der Datei erfolgreich + Hochladen der Datei fehlgeschlagen Abgabe abbrechen Dadurch wird Ihre Abgabe storniert und gelöscht. Hochladen der Datei fehlgeschlagen @@ -1377,4 +1383,9 @@ Der Auswahlmodus ist deaktiviert. Avatar von %s Rückgängig machen + App + Domäne + Anmelde-ID + E-Mail + Version diff --git a/libs/pandares/src/main/res/values-en-rAU/strings.xml b/libs/pandares/src/main/res/values-en-rAU/strings.xml index 31bc8ecea5..b07729c242 100644 --- a/libs/pandares/src/main/res/values-en-rAU/strings.xml +++ b/libs/pandares/src/main/res/values-en-rAU/strings.xml @@ -1294,11 +1294,17 @@ Close progress dialogue %1$s of %2$s Uploading to Files + One or more files failed to upload. Check your internet connection and retry to submit. Uploading submission to \"%s\" Uploading Submission - Submission Success + Submission Successful Submission Failed - Uploading Files + + Uploading File + Uploading Files + + File Upload Successful + File Upload Failed Cancel Submission This will cancel and delete your submission. File Upload Failed @@ -1377,4 +1383,9 @@ Selection mode deactivated. Avatar of %s Undo + App + Domain + Login ID + Email + Version diff --git a/libs/pandares/src/main/res/values-en-rCY/strings.xml b/libs/pandares/src/main/res/values-en-rCY/strings.xml index 62e5bd1896..6440be837b 100644 --- a/libs/pandares/src/main/res/values-en-rCY/strings.xml +++ b/libs/pandares/src/main/res/values-en-rCY/strings.xml @@ -1294,11 +1294,17 @@ Close progress dialogue %1$s of %2$s Uploading to Files + One or more files failed to upload. Check your internet connection and retry to submit. Uploading submission to \"%s\" Uploading submission - Submission Success + Submission Successful Submission failed - Uploading Files + + Uploading File + Uploading Files + + File Upload Successful + File Upload Failed Cancel Submission This will cancel and delete your submission. File Upload Failed @@ -1377,4 +1383,9 @@ Selection mode deactivated. Avatar of %s Undo + App + Domain + Login ID + Email + Version diff --git a/libs/pandares/src/main/res/values-en-rGB/strings.xml b/libs/pandares/src/main/res/values-en-rGB/strings.xml index 109a519665..4b93f190e2 100644 --- a/libs/pandares/src/main/res/values-en-rGB/strings.xml +++ b/libs/pandares/src/main/res/values-en-rGB/strings.xml @@ -1294,11 +1294,17 @@ Close progress dialogue %1$s of %2$s Uploading to Files + One or more files failed to upload. Check your internet connection and retry to submit. Uploading submission to \"%s\" Uploading submission - Submission Success + Submission Successful Submission failed - Uploading Files + + Uploading File + Uploading Files + + File Upload Successful + File Upload Failed Cancel Submission This will cancel and delete your submission. File Upload Failed @@ -1377,4 +1383,9 @@ Selection mode deactivated. Avatar of %s Undo + App + Domain + Login ID + Email + Version diff --git a/libs/pandares/src/main/res/values-es-rES/strings.xml b/libs/pandares/src/main/res/values-es-rES/strings.xml index 6eb048fcf4..bfb1606f72 100644 --- a/libs/pandares/src/main/res/values-es-rES/strings.xml +++ b/libs/pandares/src/main/res/values-es-rES/strings.xml @@ -1296,11 +1296,17 @@ Cerrar diálogo %1$s de %2$s Cargando a archivos + No se han podido cargar uno o más archivos. Comprueba tu conexión a Internet y vuelve a realizar la entrega. Cargando entrega a \"%s\" Cargando entrega - Se ha realizado la entrega + Entrega realizada correctamente No se ha podido entregar - Cargando archivos + + Cargando archivo + Cargando archivos + + Archivo cargado correctamente + Ha habido un error al cargar el archivo Cancelar entrega Esta acción cancelará y eliminará tu entrega. Ha habido un error al cargar el archivo @@ -1379,4 +1385,9 @@ Modo de selección desactivado. Avatar de %s Deshacer + Aplicación + Dominio + Identificación de inicio de sesión + Correo electrónico + Versión diff --git a/libs/pandares/src/main/res/values-es/strings.xml b/libs/pandares/src/main/res/values-es/strings.xml index 23a604c77b..88d851c809 100644 --- a/libs/pandares/src/main/res/values-es/strings.xml +++ b/libs/pandares/src/main/res/values-es/strings.xml @@ -1294,11 +1294,17 @@ Cerrar diálogo de progreso %1$s de %2$s Cargando a Archivos + No se pudieron cargar uno o más archivos. Compruebe su conexión a Internet y vuelva a entregarlos. Cargando entrega a \"%s\" Cargando la entrega Entrega exitosa No se pudo presentar - Carga de archivos + + Carga de archivo + Carga de archivos + + Carga de archivo exitosa + No se pudo cargar el archivo Cancelar entrega Esto cancelará y eliminará su entrega. No se pudo cargar el archivo @@ -1377,4 +1383,9 @@ Modo de selección desactivado. Avatar de %s Deshacer + Aplicación + Dominio + ID de inicio de sesión + Correo electrónico + Versión diff --git a/libs/pandares/src/main/res/values-fi/strings.xml b/libs/pandares/src/main/res/values-fi/strings.xml index d06ba6becf..01639c036f 100644 --- a/libs/pandares/src/main/res/values-fi/strings.xml +++ b/libs/pandares/src/main/res/values-fi/strings.xml @@ -1294,11 +1294,17 @@ Sulje edistymisruutu %1$s/%2$s Ladataan tiedostoihin + Yksi tai useampi tiedosto ei latautunut. Tarkista Internet-yhteytesi ja yritä lähettää uudelleen. Ladataan lähetystä kohteeseen \"%s\" Ladataan tehtäväpalautusta - Tehtävänpalautus onnistui + Lähetys onnistui Tehtäväpalautus epäonnistui - Ladataan tiedostoja + + Ladataan tiedostoa + Ladataan tiedostoja + + Tiedoston lataus onnistui + Tiedoston lataus epäonnistui Peruuta lähetys Tämä peruuttaa ja poistaa lähetyksesi. Tiedoston lataus epäonnistui @@ -1377,4 +1383,9 @@ Valintatilan aktivointi poistettu. Kohteen %s avatar Peruuta + Sovellus + Verkkotunnus + Käyttäjätunnus + Sähköposti + Versio diff --git a/libs/pandares/src/main/res/values-fr-rCA/strings.xml b/libs/pandares/src/main/res/values-fr-rCA/strings.xml index 1d6e83f0b5..185874579d 100644 --- a/libs/pandares/src/main/res/values-fr-rCA/strings.xml +++ b/libs/pandares/src/main/res/values-fr-rCA/strings.xml @@ -1294,11 +1294,17 @@ Fermer le dialogue de progression %1$s sur %2$s Téléversement des fichiers + Le téléversement d’un ou de plusieurs fichiers a échoué. Vérifiez votre connexion Internet et réessayez l’envoi. Téléversement de l’envoi vers \"%s\" Téléversement de l’envoi - Succès de l’envoi + Envoi réussi Échec de l’envoi - Téléversement des fichiers + + Téléversement du fichier + Téléversement des fichiers + + Le téléversement du fichier a réussi + Le téléversement du fichier a échoué Annuler l\'envoi Cela annulera et supprimera votre envoi. Le téléversement du fichier a échoué @@ -1377,4 +1383,9 @@ Mode de sélection désactivé. Avatar de %s Annuler + Appl. + Domaine + Identifiant de connexion + Adresse électronique + Version diff --git a/libs/pandares/src/main/res/values-fr/strings.xml b/libs/pandares/src/main/res/values-fr/strings.xml index 6b48292b1f..a4796e0486 100644 --- a/libs/pandares/src/main/res/values-fr/strings.xml +++ b/libs/pandares/src/main/res/values-fr/strings.xml @@ -1294,11 +1294,17 @@ Fermer la boîte de dialogue de progression %1$s sur %2$s Envoi vers les fichiers... + L’envoi d\'un ou plusieurs fichiers a échoué. Vérifiez l’état de votre connexion internet, puis réessayez. Envoi de la soumission vers \"%s\" Envoi en cours - Envoi réussi + Soumission réussie Échec de l’envoi - Envoi des fichiers... + + Envoi du fichier... + Envoi des fichiers... + + Fichier envoyé avec succès + Échec de l’envoi de fichier Annuler la soumission Ceci annulera et supprimera votre soumission. Échec de l’envoi de fichier @@ -1377,4 +1383,9 @@ Mode sélection désactivé. Avatar de %s Annuler + Application + Domaine + ID d’authentification + Email + Version diff --git a/libs/pandares/src/main/res/values-ht/strings.xml b/libs/pandares/src/main/res/values-ht/strings.xml index 1788d4dd5c..ade3eda65e 100644 --- a/libs/pandares/src/main/res/values-ht/strings.xml +++ b/libs/pandares/src/main/res/values-ht/strings.xml @@ -1294,11 +1294,17 @@ Fèmen dyalìg pwogrè %1$s de %2$s Transfere nan Fichye + Gen yonn oswa plizyè fichye pa rive transfere. Verifye koneksyon entènèt ou a epi eseye re voye yo ankò Transfere Soumisyon nan \"%s\" Chajman Soumisyon Soumisyon Reyisi Soumisyon Echwe - Ajoute Fichye + + Transfè Fichye + Ajoute Fichye + + Transfè Fichye Reyisi + Transfè Fichye Echwe Anile Soumisyon Aksyon sa a ap anile epi efase soumisyon w lan. Transfè Fichye Echwe @@ -1377,4 +1383,9 @@ Mòd seleksyon dezaktive. Avatar %s Defèt + App + Domèn + ID Koneksyon + Imèl + Vèsyon diff --git a/libs/pandares/src/main/res/values-is/strings.xml b/libs/pandares/src/main/res/values-is/strings.xml index 0466ba7789..5c57e94dcd 100644 --- a/libs/pandares/src/main/res/values-is/strings.xml +++ b/libs/pandares/src/main/res/values-is/strings.xml @@ -1294,11 +1294,17 @@ Lokaðu framvinduglugganum %1$s af %2$s Hleð upp í Skrár + Ekki tókst að hlaða upp einni eða fleiri skrám. Athugaðu nettenginguna þína og reyndu aftur að skila. Hlaða upp skilum í \"%s\" Hlaða upp skilum Skil tókust Mistókst að skila - Hleður upp skrám + + Hleður upp skrá + Hleður upp skrám + + Upphleðsla skrár tókst + Upphleðsla skrár mistókst Hætta við skil Þetta hættir við og eyðir skilunum þínum. Upphleðsla skrár mistókst @@ -1377,4 +1383,9 @@ Valhamur afvirkjaður. Gengill %s Hætta við + Smáforrit + Lén + Innskráningarauðkenni + Tölvupóstur + Útgáfa diff --git a/libs/pandares/src/main/res/values-it/strings.xml b/libs/pandares/src/main/res/values-it/strings.xml index c42a340810..01ea64df04 100644 --- a/libs/pandares/src/main/res/values-it/strings.xml +++ b/libs/pandares/src/main/res/values-it/strings.xml @@ -1294,11 +1294,17 @@ Chiudi finestra di dialogo avanzamento %1$s di %2$s Caricamento nei file + Impossibile caricare uno o più file. Controlla la tua connessione Internet e riprova a inviare. Caricamento consegna in \"%s\" Caricamento consegna Consegna eseguita correttamente Consegna non riuscita - Caricamento file + + Caricamento file + Caricamento file + + Caricamento file eseguito correttamente + Errore di caricamento file Annulla invio Questa operazione annullerà ed eliminerà il tuo invio. Errore di caricamento file @@ -1377,4 +1383,9 @@ Modalità selezione disattivata. Avatar di %s Annulla + App + Dominio + ID di accesso + E-mail + Versione diff --git a/libs/pandares/src/main/res/values-ja/strings.xml b/libs/pandares/src/main/res/values-ja/strings.xml index aa264a5b4c..943eb17052 100644 --- a/libs/pandares/src/main/res/values-ja/strings.xml +++ b/libs/pandares/src/main/res/values-ja/strings.xml @@ -1278,11 +1278,16 @@ 進捗状況ダイアログを閉じる %1$s / %2$s ファイルアップロード中 + 1つ以上のファイルのアップロードに失敗しました。インターネット接続をチェックしてから、提出を再試行してください。 提出物を\"%s\"にアップロード中 提出物をアップロードしています - 提出に成功しました + 正常に提出しました 提出に失敗しました - ファイルアップロード中 + + ファイルアップロード中 + + ファイルのアップロードに成功しました + ファイルのアップロードに失敗しました 提出をキャンセル これにより、送信がキャンセルおよび削除されます。 ファイルのアップロードに失敗しました @@ -1360,4 +1365,9 @@ 選択モードが解除されました。 %sのアバター アンドゥーする + アプリ + ドメイン + ログイン ID + E メール + バージョン diff --git a/libs/pandares/src/main/res/values-mi/strings.xml b/libs/pandares/src/main/res/values-mi/strings.xml index cdaaf4be9b..61847c0b6c 100644 --- a/libs/pandares/src/main/res/values-mi/strings.xml +++ b/libs/pandares/src/main/res/values-mi/strings.xml @@ -1294,11 +1294,17 @@ Katia te korero ahunga whakamua %1$s ō %2$s Tukuake ki nga Kōnae + Kotahi me te nui atu o ngā kōnae i hapa te tukuake. Āta titro ki to hono ipurangi ka ngana anō ki te tuku. Te tuku tukunga ki \"%s\" Tukuake ana tāpaetanga - Angitu te tukunga + I Angitu te Tukunga I rahua te tuku o te tāpaetanga - Tikiake ana ngā kōnae + + Tukuake Kōnae + Tikiake ana ngā kōnae + + I Angitu te Tukunga Kōnae + I Rahua te Tukunga Kōnae Whakakore Tāpaetanga Ka mukua me te whakakoretia to tāpaetanga. I Rahua te Tukunga Kōnae @@ -1377,4 +1383,9 @@ Kua whakakorehia te aratau whiriwhiri. Avatar o %s Wete + Taupānga + Rohe + Takiuru ID + Īmēra + Putanga diff --git a/libs/pandares/src/main/res/values-ms/strings.xml b/libs/pandares/src/main/res/values-ms/strings.xml index 6b0efe2ceb..75260c1970 100644 --- a/libs/pandares/src/main/res/values-ms/strings.xml +++ b/libs/pandares/src/main/res/values-ms/strings.xml @@ -1300,11 +1300,17 @@ Tutup dialog kemajuan %1$s daripada %2$s Memuat Naik ke Fail + Satu atau lebih fail gagal dimuat naik. Semak sambungan Internet anda dan cuba semula untuk membuat serahan. Memuat naik serahan ke \"%s\" Memuat Naik Serahan Serahan Berjaya Serahan Gagal - Memuat Naik Fail + + Memuat Naik Fail + Memuat Naik Fail + + Muat Naik Fail Berjaya + Muat Naik Fail Gagal Batal Serahan Ini akan membatalkan dan memadamkan serahan anda. Muat Naik Fail Gagal @@ -1383,4 +1389,9 @@ Mod pilihan diaktifkan. Avatar %s Buat asal + Apl + Domain + ID Log Masuk + E-mel + Versi diff --git a/libs/pandares/src/main/res/values-nb/strings.xml b/libs/pandares/src/main/res/values-nb/strings.xml index b53d07dbc6..e84ab62421 100644 --- a/libs/pandares/src/main/res/values-nb/strings.xml +++ b/libs/pandares/src/main/res/values-nb/strings.xml @@ -1295,11 +1295,17 @@ Lukk fremgangsdialog %1$s av %2$s Last opp til Filer + En eller flere filer kunne ikke lastes opp. Sjekk internettforbindelsen din og prøv å lever på nytt. Laster opp innlevering til \"%s\" Laster opp innlevering Innlevering vellykket Innlevering feilet - Laste opp filer + + Laster opp fil + Laster opp filer + + Filen ble lastet opp + Filopplasting mislyktes Avbryt innlevering Dette avbryter og sletter innleveringen din. Filopplasting mislyktes @@ -1378,4 +1384,9 @@ Valgmodus deaktivert. Avatar av %s Angre + App + Domene + Innloggings-ID + E-post + Versjon diff --git a/libs/pandares/src/main/res/values-nl/strings.xml b/libs/pandares/src/main/res/values-nl/strings.xml index 5a118ef95c..dd5325b396 100644 --- a/libs/pandares/src/main/res/values-nl/strings.xml +++ b/libs/pandares/src/main/res/values-nl/strings.xml @@ -1294,11 +1294,17 @@ Voortgangsdialoogvenster sluiten %1$s van %2$s Uploaden naar bestanden + Er kunnen een of meer bestanden niet worden geüpload. Controleer je internetverbinding en probeer opnieuw in te leveren. Inlevering uploaden naar \"%s\" Inlevering wordt geüpload - Inlevering is gelukt + Inlevering gelukt Inlevering is mislukt - Bestanden uploaden + + Bestanden worden geüpload + Bestanden uploaden + + Bestand uploaden gelukt + Bestand uploaden is mislukt Inlevering annuleren Hiermee wordt je inlevering geannuleerd en verwijderd. Bestand uploaden is mislukt @@ -1377,4 +1383,9 @@ Selectiemodus gedeactiveerd. Avatar van %s Ongedaan maken + App + Domein + Aanmeldings-ID + E-mail + Versie diff --git a/libs/pandares/src/main/res/values-pl/strings.xml b/libs/pandares/src/main/res/values-pl/strings.xml index 58f5e49d29..debd724b2b 100644 --- a/libs/pandares/src/main/res/values-pl/strings.xml +++ b/libs/pandares/src/main/res/values-pl/strings.xml @@ -1326,11 +1326,19 @@ Zamknij okno dialogowe postępów %1$s z %2$s Przesyłanie do Plików + Nie udało się przesłać co najmniej jednego pliku. Sprawdź połączenie internetowe i ponów przesyłanie. Przesyłanie materiałów do \"%s\" Trwa przesyłanie zadania - Przesłano pomyślnie + Przesyłanie zakończono pomyślnie Przesyłanie nie powiodło się - Przesyłanie plików + + Przesyłanie pliku + Przesyłanie plików + Przesyłanie plików + Przesyłanie plików + + Przesyłanie pliku zakończono pomyślnie + Niepowodzenie przesyłania pliku Anuluj przesyłkę Spowoduje to anulowanie i usunięcie przesyłki. Niepowodzenie przesyłania pliku @@ -1411,4 +1419,9 @@ Dezaktywowano tryb wyboru. Awatar %s Cofnij + Aplikacja + Domena + Nazwa użytkownika + E-mail + Wersja diff --git a/libs/pandares/src/main/res/values-pt-rBR/strings.xml b/libs/pandares/src/main/res/values-pt-rBR/strings.xml index 5b3e6023eb..939a82dfb4 100644 --- a/libs/pandares/src/main/res/values-pt-rBR/strings.xml +++ b/libs/pandares/src/main/res/values-pt-rBR/strings.xml @@ -1294,11 +1294,17 @@ Fechar caixa de diálogo de progresso %1$s de %2$s Fazendo upload para arquivos + Falha no carregamento de um ou mais arquivos. Verifique sua conexão à Internet e tente enviar novamente. Carregando envio para \"%s\" Realizando upload - Sucesso do envio + Envio bem-sucedido Falha no envio - Carregamento de arquivos + + Carregando arquivo + Carregamento de arquivos + + Carregamento de arquivo bem-sucedido + Falha no envio do arquivo Cancelar envio Isso cancelará e excluirá seu envio. Falha no envio do arquivo @@ -1377,4 +1383,9 @@ Modo de seleção desativado. Avatar de %s Desfazer + Aplicativo + Domínio + ID de Login + E-mail + Versão diff --git a/libs/pandares/src/main/res/values-pt-rPT/strings.xml b/libs/pandares/src/main/res/values-pt-rPT/strings.xml index 93978bb98a..9d5e600d24 100644 --- a/libs/pandares/src/main/res/values-pt-rPT/strings.xml +++ b/libs/pandares/src/main/res/values-pt-rPT/strings.xml @@ -1294,11 +1294,17 @@ Fechar diálogo de progresso %1$s de %2$s Carregamento para ficheiros + Um ou mais arquivos não foram carregados. Verifique sua conexão com a Internet e tente enviar novamente. A carregar o envio para \"%s\" A carregar o envio - Submissão Bem sucedida + Submissão bem sucedida Falha na submissão - Carregamento de ficheiros + + Carregamento de ficheiro + Carregamento de ficheiros + + Carregamento de ficheiros com sucesso + Carregamento de ficheiros falhado Cancelar subscrição Isto irá cancelar e eliminar a sua submissão. Carregamento de ficheiros falhado @@ -1377,4 +1383,9 @@ Modo de seleção desativado. Avatar de %s Desfazer + Aplicação + Domínio + ID de login + E-mail + Versão diff --git a/libs/pandares/src/main/res/values-ru/strings.xml b/libs/pandares/src/main/res/values-ru/strings.xml index 4c2e399840..0c7ce07b16 100644 --- a/libs/pandares/src/main/res/values-ru/strings.xml +++ b/libs/pandares/src/main/res/values-ru/strings.xml @@ -1326,11 +1326,19 @@ Закрыть диалоговое окно выполнения %1$s из %2$s Загрузка в файлы + Не удалось загрузить один или несколько файлов. Проверьте подключение к Интернету и повторите отправку. Загрузка отправки в \"%s\" Загрузка отправки Отправка выполнена Ошибка отправки - Загрузка файлов + + Загрузка файла + Загрузка файлов + Загрузка файлов + Загрузка файлов + + Файл успешно загружен + Не удалось загрузить файл Отменить отправку Данное действие отменит отправку данных и удалит отравляемые данные. Не удалось загрузить файл @@ -1411,4 +1419,9 @@ Режим выбора деактивирован. Аватар %s Отменить + Приложение + Домен + Имя пользователя + Адрес электронной почты + Версия diff --git a/libs/pandares/src/main/res/values-sl/strings.xml b/libs/pandares/src/main/res/values-sl/strings.xml index b5d462ea74..65658bce2c 100644 --- a/libs/pandares/src/main/res/values-sl/strings.xml +++ b/libs/pandares/src/main/res/values-sl/strings.xml @@ -1294,11 +1294,17 @@ Zapri pogovorno okno za napredovanje %1$s od %2$s Nalagam v datoteke + Nalaganje ene ali več datotek ni uspelo. Preverite internetno povezavo in poskusite poslati znova. Nalagam oddajo v \"%s\" Nalaganje oddaje Oddaja je uspela Oddaja ni uspela - Nalaganje datotek + + Nalagam datoteko + Nalagam datoteke + + Datoteka je uspešno naložena + Nalaganje datoteke ni uspelo Prekliči oddajo S tem boste preklicali in izbrisali oddajo. Nalaganje datoteke ni uspelo @@ -1377,4 +1383,9 @@ Način izbire je deaktiviran. Avatar osebe %s Razveljavi + Aplikacija + Domena + ID prijave + E-pošta + Različica diff --git a/libs/pandares/src/main/res/values-sv/strings.xml b/libs/pandares/src/main/res/values-sv/strings.xml index 18f4f50dcc..30e3a7d336 100644 --- a/libs/pandares/src/main/res/values-sv/strings.xml +++ b/libs/pandares/src/main/res/values-sv/strings.xml @@ -1294,11 +1294,17 @@ Stäng förloppsdialog %1$s av %2$s Laddar upp till Filer + En eller fler filer laddades inte upp. Kontrollera din internetanslutning och försök lämna in igen. Laddar upp inlämning till \"%s\" Laddar upp inlämning - Inlämningen lyckades + Inlämningen slutfördes Inlämningen misslyckades - Laddar upp filer + + Laddar upp fil + Laddar upp filer + + Filuppladdningen slutfördes + Filuppladdningen misslyckades Avbryt inlämningen Detta avbryter och tar bort din inlämning. Filuppladdningen misslyckades @@ -1377,4 +1383,9 @@ Urvalsläget inaktiverat. Avatar för %s Ångra + App + Domän + Inloggnings-ID + E-post + Version diff --git a/libs/pandares/src/main/res/values-th/strings.xml b/libs/pandares/src/main/res/values-th/strings.xml index e737332467..9951e83134 100644 --- a/libs/pandares/src/main/res/values-th/strings.xml +++ b/libs/pandares/src/main/res/values-th/strings.xml @@ -1294,11 +1294,17 @@ ปิดกล่องโต้ตอบแสดงความคืบหน้า %1$s จาก %2$s กำลังอัพโหลดไปที่ไฟล์ (Files) + ไฟล์บางส่วนไม่สามารถอัพโหลดได้ ตรวจสอบการเชื่อมต่ออินเทอร์เน็ตของคุณแล้วลองส่งใหม่อีกครั้ง กำลังอัพโหลดผลงานจัดส่งไปยัง \"%s\" กำลังอัพโหลดผลงานจัดส่ง จัดส่งเสร็จสิ้น จัดส่งล้มเหลว - กำลังอัพโหลดไฟล์ + + กำลังอัพโหลดไฟล์ + กำลังอัพโหลดไฟล์ + + อัพโหลดไฟล์เสร็จสิ้น + อัพโหลดไฟล์ล้มเหลว ยกเลิกการจัดส่ง นี่จะเป็นการยกเลิกและลบผลงานจัดส่งของคุณ อัพโหลดไฟล์ล้มเหลว @@ -1377,4 +1383,9 @@ ปิดใช้งานโหมดเลือกรายการแล้ว ภาพประจำตัวของ %s เลิกทำ + แอพ + โดเมน + ID ล็อกอิน + อีเมล + เวอร์ชั่น diff --git a/libs/pandares/src/main/res/values-vi/strings.xml b/libs/pandares/src/main/res/values-vi/strings.xml index fcbdca0946..960086917a 100644 --- a/libs/pandares/src/main/res/values-vi/strings.xml +++ b/libs/pandares/src/main/res/values-vi/strings.xml @@ -1295,11 +1295,17 @@ Đóng hộp thoại tiến trình %1$s / %2$s Đang tải lên tới Các Tập Tin + Tải lên một hoặc nhiều tập tin không thành công. Hãy kiểm tra kết nối internet của bạn rồi thử lại để nộp. Đang tải bài nộp tới \"%s\" Tải Lên Bài Nộp Nộp Thành Công Nộp Thất Bại - Đang tải Các Tập Tin + + Đang Tải Lên Tập Tin + Đang Tải Lên Các Tập Tin + + Tải Lên Tập Tin Thành Công + Tải Lên Tập Tin Không Thành Công Hủy Nộp Thao tác này sẽ hủy và xóa bài nộp của bạn. Tải Lên Các Tập Tin Không Thành Công @@ -1378,4 +1384,9 @@ Đã hủy kích hoạt chế độ lựa chọn. Hình Đại Diện của %s Hoàn tác + Ứng Dụng + Tên Miền + ID Đăng Nhập + Email + Phiên bản diff --git a/libs/pandares/src/main/res/values-zh/strings.xml b/libs/pandares/src/main/res/values-zh/strings.xml index 22bf269c32..fcc8e49b2e 100644 --- a/libs/pandares/src/main/res/values-zh/strings.xml +++ b/libs/pandares/src/main/res/values-zh/strings.xml @@ -1278,11 +1278,16 @@ 关闭进度对话框 %1$s,共%2$s 上传到文件 + 一份或多个文件上传失败。请检查网络连接,并再次尝试提交。 上传提交项到\"%s\" 正在上传提交作业 - 成功提交文件 + 提交成功 提交文件失败 - 上传文件 + + 正在上传文件 + + 文件上传成功 + 文件上传失败 取消提交 此操作将取消并删除您的提交项。 文件上传失败 @@ -1360,4 +1365,9 @@ 选择模式已被禁用。 %s头像 后退 + 应用程序 + + 登录 ID + 电子邮件 + 版本 From ed4895ac4759a0db84f51df0891513f6f7c1b640 Mon Sep 17 00:00:00 2001 From: "kristof.nemere" Date: Thu, 18 May 2023 14:00:15 +0200 Subject: [PATCH 038/103] Bumped version code and name --- apps/teacher/build.gradle | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/apps/teacher/build.gradle b/apps/teacher/build.gradle index 98851a2f6f..e6ddb681f4 100644 --- a/apps/teacher/build.gradle +++ b/apps/teacher/build.gradle @@ -14,11 +14,8 @@ * along with this program. If not, see . */ -import com.instructure.android.buildtools.transform.LocaleTransformer -import com.instructure.android.buildtools.transform.MasqueradeUITransformer -import com.instructure.android.buildtools.transform.PageViewTransformer -import com.instructure.android.buildtools.transform.ProjectTransformer -import com.instructure.android.buildtools.transform.ScreenViewTransformer + +import com.instructure.android.buildtools.transform.* apply plugin: 'com.android.application' apply plugin: 'kotlin-android' @@ -42,8 +39,8 @@ android { defaultConfig { minSdkVersion Versions.MIN_SDK targetSdkVersion Versions.TARGET_SDK - versionCode = 57 - versionName = '1.23.1' + versionCode = 58 + versionName = '1.24.0' vectorDrawables.useSupportLibrary = true multiDexEnabled true testInstrumentationRunner 'com.instructure.teacher.ui.espresso.TeacherHiltTestRunner' From 5f3c0e8a99abbf1aacbd926fa11e5f04b722c2e8 Mon Sep 17 00:00:00 2001 From: Kristof Deak <92309696+kdeakinstructure@users.noreply.github.com> Date: Mon, 22 May 2023 14:21:30 +0200 Subject: [PATCH 039/103] [MBL-16484][Student] - Remove outdated stubbed quiz assignment details interaction test --- .../AssignmentDetailsInteractionTest.kt | 45 +++++-------------- 1 file changed, 10 insertions(+), 35 deletions(-) diff --git a/apps/student/src/androidTest/java/com/instructure/student/ui/interaction/AssignmentDetailsInteractionTest.kt b/apps/student/src/androidTest/java/com/instructure/student/ui/interaction/AssignmentDetailsInteractionTest.kt index 9058403da7..c0763e0e9f 100644 --- a/apps/student/src/androidTest/java/com/instructure/student/ui/interaction/AssignmentDetailsInteractionTest.kt +++ b/apps/student/src/androidTest/java/com/instructure/student/ui/interaction/AssignmentDetailsInteractionTest.kt @@ -15,11 +15,18 @@ */ package com.instructure.student.ui.interaction -import com.instructure.canvas.espresso.Stub -import com.instructure.canvas.espresso.mockCanvas.* +import com.instructure.canvas.espresso.mockCanvas.MockCanvas +import com.instructure.canvas.espresso.mockCanvas.addAssignment +import com.instructure.canvas.espresso.mockCanvas.addAssignmentsToGroups +import com.instructure.canvas.espresso.mockCanvas.addSubmissionForAssignment +import com.instructure.canvas.espresso.mockCanvas.init import com.instructure.canvasapi2.models.Assignment import com.instructure.canvasapi2.utils.toApiString -import com.instructure.panda_annotations.* +import com.instructure.panda_annotations.FeatureCategory +import com.instructure.panda_annotations.Priority +import com.instructure.panda_annotations.SecondaryFeatureCategory +import com.instructure.panda_annotations.TestCategory +import com.instructure.panda_annotations.TestMetaData import com.instructure.student.ui.utils.StudentTest import com.instructure.student.ui.utils.routeTo import com.instructure.student.ui.utils.tokenLogin @@ -177,38 +184,6 @@ class AssignmentDetailsInteractionTest : StudentTest() { submissionDetailsPage.assertPageObjects() } - @Stub - @Test - @TestMetaData(Priority.MANDATORY, FeatureCategory.ASSIGNMENTS, TestCategory.INTERACTION, true, SecondaryFeatureCategory.ASSIGNMENT_QUIZZES) - fun testQuizzesNext_launchQuizzesNextAssignment() { - // Launch into Quizzes.Next assignment - /* First attempt based on hardcoded verifier response - val data = MockCanvas.init( - studentCount = 1, - courseCount = 1 - ) - - val course = data.courses.values.first() - val student = data.students[0] - val token = data.tokenFor(student)!! - val assignment = data.addAssignment(courseId = course.id, groupType = AssignmentGroupType.UPCOMING, submissionType = Assignment.SubmissionType.EXTERNAL_TOOL, isQuizzesNext = true) - val submission = Submission( - id = 123L, - submittedAt = Date(), - attempt = 1L, - late = false - ) - data.addSubmission(course.id, submission, assignment.id) - data.addLTITool("Quizzes 2", "https://mobiledev.instructure.com/courses/1567973/external_tools/sessionless_launch?verifier=f85d3d69189890cde2f427a8efdc0e64850d8583bf8f2e0e0fa3704782d48b5378df5d52a35a4497ec18d3b0e201b3b2cab95e1347e7c5e286ac6636bf295c6b") - tokenLogin(data.domain, token, student) - routeTo("courses/${course.id}/assignments", data.domain) - - assignmentListPage.clickAssignment(assignment) - assignmentDetailsPage.clickSubmit() - //https://mobiledev.instructure.com/api/v1/courses/1567973/external_tools/sessionless_launch?assignment_id=24378681&launch_type=assessment - */ - } - private fun goToAssignmentFromList(): MockCanvas { // Test clicking on the Submission and Rubric button to load the Submission Details Page val data = MockCanvas.init( From 99839cb7d0e63b0d912f2e5c3993226fd6c290c9 Mon Sep 17 00:00:00 2001 From: "kristof.nemere" Date: Tue, 23 May 2023 09:59:02 +0200 Subject: [PATCH 040/103] Release student 6.24.0 (251) --- apps/student/build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/student/build.gradle b/apps/student/build.gradle index fb269772e7..b32084749c 100644 --- a/apps/student/build.gradle +++ b/apps/student/build.gradle @@ -50,8 +50,8 @@ android { applicationId "com.instructure.candroid" minSdkVersion Versions.MIN_SDK targetSdkVersion Versions.TARGET_SDK - versionCode = 250 - versionName = '6.23.1' + versionCode = 251 + versionName = '6.24.0' vectorDrawables.useSupportLibrary = true multiDexEnabled = true From df409438f5d5470db7216c41a05a52a8a4d30271 Mon Sep 17 00:00:00 2001 From: Tamas Kozmer <72397075+tamaskozmer@users.noreply.github.com> Date: Tue, 23 May 2023 13:03:43 +0200 Subject: [PATCH 041/103] [MBL-16708][Student][Teacher] Setup DB test environment (#1989) refs: MBL-16708 affects: Student, Teacher release note: none * AppDatabase tests. * Added db schemas and schema import gradle task. * Basic app database migration test. * Sync db changes with offline branch and extract common entities. --- apps/student/build.gradle | 1 - .../fragment/InboxComposeMessageFragment.kt | 2 +- apps/teacher/build.gradle | 1 - .../SpeedGraderCommentsPresenterFactory.kt | 4 + .../teacher/fragments/AddMessageFragment.kt | 2 +- .../fragments/SpeedGraderCommentsFragment.kt | 4 + .../SpeedGraderCommentsPresenter.kt | 6 +- .../src/main/java/RoomSchemaArgProvider.kt | 33 ++ libs/pandautils/build.gradle | 21 +- .../3.json | 436 ++++++++++++++++ .../4.json | 442 ++++++++++++++++ .../5.json | 466 +++++++++++++++++ .../6.json | 478 ++++++++++++++++++ .../appdatabase/AppDatabaseMigrationTest.kt | 58 +++ .../appdatabase/daos/AttachmentDaoTest.kt | 83 +++ .../daos/DashboardFileUploadDaoTest.kt | 90 ++++ .../daos/FileUploadInputDaoTest.kt | 62 +++ .../daos/PendingSubmissionCommentDaoTest.kt | 126 +++++ .../daos/SubmissionCommentDaoTest.kt | 98 ++++ .../pandautils/di/DatabaseModule.kt | 4 + .../DashboardNotificationsViewModel.kt | 2 +- .../file/upload/worker/FileUploadWorker.kt | 8 + .../room/appdatabase/AppDatabase.kt | 12 +- .../room/appdatabase/AppDatabaseMigrations.kt | 7 +- .../daos/DashboardFileUploadDao.kt | 2 +- .../room/{ => common}/Converters.kt | 10 +- .../room/{ => common}/MigrationUtils.kt | 2 +- .../daos/AttachmentDao.kt | 4 +- .../{appdatabase => common}/daos/AuthorDao.kt | 4 +- .../daos/MediaCommentDao.kt | 4 +- .../daos/SubmissionCommentDao.kt | 6 +- .../entities/AttachmentEntity.kt | 5 +- .../entities/AuthorEntity.kt | 2 +- .../entities/MediaCommentEntity.kt | 2 +- .../entities/SubmissionCommentEntity.kt | 5 +- .../model/SubmissionCommentWithAttachments.kt | 10 +- .../DashboardNotificationsViewModelTest.kt | 2 +- 37 files changed, 2466 insertions(+), 38 deletions(-) create mode 100644 buildSrc/src/main/java/RoomSchemaArgProvider.kt create mode 100644 libs/pandautils/schemas/com.instructure.pandautils.room.appdatabase.AppDatabase/3.json create mode 100644 libs/pandautils/schemas/com.instructure.pandautils.room.appdatabase.AppDatabase/4.json create mode 100644 libs/pandautils/schemas/com.instructure.pandautils.room.appdatabase.AppDatabase/5.json create mode 100644 libs/pandautils/schemas/com.instructure.pandautils.room.appdatabase.AppDatabase/6.json create mode 100644 libs/pandautils/src/androidTest/java/com/instructure/pandautils/room/appdatabase/AppDatabaseMigrationTest.kt create mode 100644 libs/pandautils/src/androidTest/java/com/instructure/pandautils/room/appdatabase/daos/AttachmentDaoTest.kt create mode 100644 libs/pandautils/src/androidTest/java/com/instructure/pandautils/room/appdatabase/daos/DashboardFileUploadDaoTest.kt create mode 100644 libs/pandautils/src/androidTest/java/com/instructure/pandautils/room/appdatabase/daos/FileUploadInputDaoTest.kt create mode 100644 libs/pandautils/src/androidTest/java/com/instructure/pandautils/room/appdatabase/daos/PendingSubmissionCommentDaoTest.kt create mode 100644 libs/pandautils/src/androidTest/java/com/instructure/pandautils/room/appdatabase/daos/SubmissionCommentDaoTest.kt rename libs/pandautils/src/main/java/com/instructure/pandautils/room/{ => common}/Converters.kt (74%) rename libs/pandautils/src/main/java/com/instructure/pandautils/room/{ => common}/MigrationUtils.kt (95%) rename libs/pandautils/src/main/java/com/instructure/pandautils/room/{appdatabase => common}/daos/AttachmentDao.kt (80%) rename libs/pandautils/src/main/java/com/instructure/pandautils/room/{appdatabase => common}/daos/AuthorDao.kt (67%) rename libs/pandautils/src/main/java/com/instructure/pandautils/room/{appdatabase => common}/daos/MediaCommentDao.kt (72%) rename libs/pandautils/src/main/java/com/instructure/pandautils/room/{appdatabase => common}/daos/SubmissionCommentDao.kt (66%) rename libs/pandautils/src/main/java/com/instructure/pandautils/room/{appdatabase => common}/entities/AttachmentEntity.kt (90%) rename libs/pandautils/src/main/java/com/instructure/pandautils/room/{appdatabase => common}/entities/AuthorEntity.kt (91%) rename libs/pandautils/src/main/java/com/instructure/pandautils/room/{appdatabase => common}/entities/MediaCommentEntity.kt (92%) rename libs/pandautils/src/main/java/com/instructure/pandautils/room/{appdatabase => common}/entities/SubmissionCommentEntity.kt (86%) rename libs/pandautils/src/main/java/com/instructure/pandautils/room/{appdatabase => common}/model/SubmissionCommentWithAttachments.kt (76%) diff --git a/apps/student/build.gradle b/apps/student/build.gradle index fb269772e7..a05e86f8df 100644 --- a/apps/student/build.gradle +++ b/apps/student/build.gradle @@ -349,7 +349,6 @@ dependencies { implementation Libs.ROOM kapt Libs.ROOM_COMPILER implementation Libs.ROOM_COROUTINES - testImplementation Libs.ROOM_TEST } // Comment out this line if the reporting logic starts going wonky. diff --git a/apps/student/src/main/java/com/instructure/student/fragment/InboxComposeMessageFragment.kt b/apps/student/src/main/java/com/instructure/student/fragment/InboxComposeMessageFragment.kt index 3a478db30b..b6bb17abae 100644 --- a/apps/student/src/main/java/com/instructure/student/fragment/InboxComposeMessageFragment.kt +++ b/apps/student/src/main/java/com/instructure/student/fragment/InboxComposeMessageFragment.kt @@ -40,7 +40,7 @@ import com.instructure.pandautils.analytics.ScreenView import com.instructure.pandautils.binding.viewBinding import com.instructure.pandautils.features.file.upload.FileUploadDialogFragment import com.instructure.pandautils.features.file.upload.FileUploadDialogParent -import com.instructure.pandautils.room.appdatabase.daos.AttachmentDao +import com.instructure.pandautils.room.common.daos.AttachmentDao import com.instructure.pandautils.utils.* import com.instructure.student.R import com.instructure.student.adapter.CanvasContextSpinnerAdapter diff --git a/apps/teacher/build.gradle b/apps/teacher/build.gradle index 98851a2f6f..0d85251210 100644 --- a/apps/teacher/build.gradle +++ b/apps/teacher/build.gradle @@ -313,7 +313,6 @@ dependencies { implementation Libs.ROOM kapt Libs.ROOM_COMPILER implementation Libs.ROOM_COROUTINES - testImplementation Libs.ROOM_TEST } apply plugin: 'com.google.gms.google-services' diff --git a/apps/teacher/src/main/java/com/instructure/teacher/factory/SpeedGraderCommentsPresenterFactory.kt b/apps/teacher/src/main/java/com/instructure/teacher/factory/SpeedGraderCommentsPresenterFactory.kt index dd5969218c..3dc1a8a619 100644 --- a/apps/teacher/src/main/java/com/instructure/teacher/factory/SpeedGraderCommentsPresenterFactory.kt +++ b/apps/teacher/src/main/java/com/instructure/teacher/factory/SpeedGraderCommentsPresenterFactory.kt @@ -20,6 +20,10 @@ import com.instructure.canvasapi2.models.Assignee import com.instructure.canvasapi2.models.Submission import com.instructure.canvasapi2.models.SubmissionComment import com.instructure.pandautils.room.appdatabase.daos.* +import com.instructure.pandautils.room.common.daos.AttachmentDao +import com.instructure.pandautils.room.common.daos.AuthorDao +import com.instructure.pandautils.room.common.daos.MediaCommentDao +import com.instructure.pandautils.room.common.daos.SubmissionCommentDao import com.instructure.teacher.presenters.SpeedGraderCommentsPresenter import com.instructure.teacher.viewinterface.SpeedGraderCommentsView import instructure.androidblueprint.PresenterFactory diff --git a/apps/teacher/src/main/java/com/instructure/teacher/fragments/AddMessageFragment.kt b/apps/teacher/src/main/java/com/instructure/teacher/fragments/AddMessageFragment.kt index a29ec17687..c624e78a75 100644 --- a/apps/teacher/src/main/java/com/instructure/teacher/fragments/AddMessageFragment.kt +++ b/apps/teacher/src/main/java/com/instructure/teacher/fragments/AddMessageFragment.kt @@ -36,7 +36,7 @@ import com.instructure.pandautils.dialogs.UnsavedChangesExitDialog import com.instructure.pandautils.features.file.upload.FileUploadDialogFragment import com.instructure.pandautils.features.file.upload.FileUploadDialogParent import com.instructure.pandautils.fragments.BasePresenterFragment -import com.instructure.pandautils.room.appdatabase.daos.AttachmentDao +import com.instructure.pandautils.room.common.daos.AttachmentDao import com.instructure.pandautils.utils.* import com.instructure.pandautils.views.AttachmentView import com.instructure.teacher.R diff --git a/apps/teacher/src/main/java/com/instructure/teacher/fragments/SpeedGraderCommentsFragment.kt b/apps/teacher/src/main/java/com/instructure/teacher/fragments/SpeedGraderCommentsFragment.kt index 9e00e050d4..3faf388c36 100644 --- a/apps/teacher/src/main/java/com/instructure/teacher/fragments/SpeedGraderCommentsFragment.kt +++ b/apps/teacher/src/main/java/com/instructure/teacher/fragments/SpeedGraderCommentsFragment.kt @@ -41,6 +41,10 @@ import com.instructure.pandautils.features.file.upload.worker.FileUploadWorker import com.instructure.pandautils.fragments.BaseListFragment import com.instructure.pandautils.room.appdatabase.daos.* import com.instructure.pandautils.room.appdatabase.entities.FileUploadInputEntity +import com.instructure.pandautils.room.common.daos.AttachmentDao +import com.instructure.pandautils.room.common.daos.AuthorDao +import com.instructure.pandautils.room.common.daos.MediaCommentDao +import com.instructure.pandautils.room.common.daos.SubmissionCommentDao import com.instructure.pandautils.services.NotoriousUploadService import com.instructure.pandautils.utils.* import com.instructure.teacher.R diff --git a/apps/teacher/src/main/java/com/instructure/teacher/presenters/SpeedGraderCommentsPresenter.kt b/apps/teacher/src/main/java/com/instructure/teacher/presenters/SpeedGraderCommentsPresenter.kt index 71191aed4b..fbda9eddc2 100644 --- a/apps/teacher/src/main/java/com/instructure/teacher/presenters/SpeedGraderCommentsPresenter.kt +++ b/apps/teacher/src/main/java/com/instructure/teacher/presenters/SpeedGraderCommentsPresenter.kt @@ -32,7 +32,11 @@ import com.instructure.pandautils.features.file.upload.worker.FileUploadWorker import com.instructure.pandautils.room.appdatabase.daos.* import com.instructure.pandautils.room.appdatabase.entities.FileUploadInputEntity import com.instructure.pandautils.room.appdatabase.entities.PendingSubmissionCommentEntity -import com.instructure.pandautils.room.appdatabase.model.SubmissionCommentWithAttachments +import com.instructure.pandautils.room.common.model.SubmissionCommentWithAttachments +import com.instructure.pandautils.room.common.daos.AttachmentDao +import com.instructure.pandautils.room.common.daos.AuthorDao +import com.instructure.pandautils.room.common.daos.MediaCommentDao +import com.instructure.pandautils.room.common.daos.SubmissionCommentDao import com.instructure.teacher.events.SubmissionCommentsUpdated import com.instructure.teacher.events.SubmissionUpdatedEvent import com.instructure.teacher.events.post diff --git a/buildSrc/src/main/java/RoomSchemaArgProvider.kt b/buildSrc/src/main/java/RoomSchemaArgProvider.kt new file mode 100644 index 0000000000..6208be5ec3 --- /dev/null +++ b/buildSrc/src/main/java/RoomSchemaArgProvider.kt @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2023 - present Instructure, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +import org.gradle.api.tasks.InputDirectory +import org.gradle.api.tasks.PathSensitive +import org.gradle.api.tasks.PathSensitivity +import org.gradle.process.CommandLineArgumentProvider +import java.io.File + +class RoomSchemaArgProvider( + @get:InputDirectory + @get:PathSensitive(PathSensitivity.RELATIVE) + val schemaDir: File +) : CommandLineArgumentProvider { + + override fun asArguments(): Iterable { + return listOf("-Aroom.schemaLocation=${schemaDir.path}") + } +} \ No newline at end of file diff --git a/libs/pandautils/build.gradle b/libs/pandautils/build.gradle index 2944946864..f629e8248e 100644 --- a/libs/pandautils/build.gradle +++ b/libs/pandautils/build.gradle @@ -37,6 +37,14 @@ android { minSdkVersion Versions.MIN_SDK targetSdkVersion Versions.TARGET_SDK buildConfigField "boolean", "IS_TESTING", isTesting() + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + javaCompileOptions { + annotationProcessorOptions { + compilerArgumentProviders( + new RoomSchemaArgProvider(new File(projectDir, "schemas")) + ) + } + } } buildTypes { @@ -82,6 +90,10 @@ android { hilt { enableAggregatingTask = false } + + sourceSets { + debug.assets.srcDirs += files("$projectDir/schemas".toString()) + } } tasks.withType(Test) { @@ -180,7 +192,6 @@ dependencies { implementation Libs.ROOM kapt Libs.ROOM_COMPILER implementation Libs.ROOM_COROUTINES - testImplementation Libs.ROOM_TEST implementation Libs.FLEXBOX_LAYOUT @@ -189,4 +200,12 @@ dependencies { implementation 'com.google.guava:guava:29.0-android' kaptTest Libs.ANDROIDX_DATABINDING_COMPILER + + androidTestImplementation Libs.KOTLIN_COROUTINES_TEST + androidTestImplementation (project(':espresso')) { + exclude group: 'org.checkerframework', module: 'checker' + } + androidTestImplementation Libs.ANDROIDX_TEST_JUNIT + androidTestImplementation Libs.ANDROIDX_CORE_TESTING + androidTestImplementation Libs.ROOM_TEST } diff --git a/libs/pandautils/schemas/com.instructure.pandautils.room.appdatabase.AppDatabase/3.json b/libs/pandautils/schemas/com.instructure.pandautils.room.appdatabase.AppDatabase/3.json new file mode 100644 index 0000000000..1b3dbd3925 --- /dev/null +++ b/libs/pandautils/schemas/com.instructure.pandautils.room.appdatabase.AppDatabase/3.json @@ -0,0 +1,436 @@ +{ + "formatVersion": 1, + "database": { + "version": 3, + "identityHash": "ab4a2a7c1599b2adcf214ca77d6fec66", + "entities": [ + { + "tableName": "AttachmentEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `contentType` TEXT, `filename` TEXT, `displayName` TEXT, `url` TEXT, `thumbnailUrl` TEXT, `previewUrl` TEXT, `createdAt` INTEGER, `size` INTEGER NOT NULL, `workerId` TEXT, `submissionCommentId` INTEGER, PRIMARY KEY(`id`))", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "contentType", + "columnName": "contentType", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "filename", + "columnName": "filename", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "displayName", + "columnName": "displayName", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "url", + "columnName": "url", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "thumbnailUrl", + "columnName": "thumbnailUrl", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "previewUrl", + "columnName": "previewUrl", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "createdAt", + "columnName": "createdAt", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "size", + "columnName": "size", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "workerId", + "columnName": "workerId", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "submissionCommentId", + "columnName": "submissionCommentId", + "affinity": "INTEGER", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "id" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "AuthorEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `displayName` TEXT, `avatarImageUrl` TEXT, `htmlUrl` TEXT, `pronouns` TEXT, PRIMARY KEY(`id`))", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "displayName", + "columnName": "displayName", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "avatarImageUrl", + "columnName": "avatarImageUrl", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "htmlUrl", + "columnName": "htmlUrl", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "pronouns", + "columnName": "pronouns", + "affinity": "TEXT", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "id" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "FileUploadInputEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`workerId` TEXT NOT NULL, `courseId` INTEGER, `assignmentId` INTEGER, `quizId` INTEGER, `quizQuestionId` INTEGER, `position` INTEGER, `parentFolderId` INTEGER, `action` TEXT NOT NULL, `userId` INTEGER, `attachments` TEXT NOT NULL, `submissionId` INTEGER, `filePaths` TEXT NOT NULL, `attemptId` INTEGER, PRIMARY KEY(`workerId`))", + "fields": [ + { + "fieldPath": "workerId", + "columnName": "workerId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "courseId", + "columnName": "courseId", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "assignmentId", + "columnName": "assignmentId", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "quizId", + "columnName": "quizId", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "quizQuestionId", + "columnName": "quizQuestionId", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "position", + "columnName": "position", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "parentFolderId", + "columnName": "parentFolderId", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "action", + "columnName": "action", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "userId", + "columnName": "userId", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "attachments", + "columnName": "attachments", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "submissionId", + "columnName": "submissionId", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "filePaths", + "columnName": "filePaths", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "attemptId", + "columnName": "attemptId", + "affinity": "INTEGER", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "workerId" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "MediaCommentEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`mediaId` TEXT NOT NULL, `displayName` TEXT, `url` TEXT, `mediaType` TEXT, `contentType` TEXT, PRIMARY KEY(`mediaId`))", + "fields": [ + { + "fieldPath": "mediaId", + "columnName": "mediaId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "displayName", + "columnName": "displayName", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "url", + "columnName": "url", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "mediaType", + "columnName": "mediaType", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "contentType", + "columnName": "contentType", + "affinity": "TEXT", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "mediaId" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "SubmissionCommentEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `authorId` INTEGER NOT NULL, `authorName` TEXT, `authorPronouns` TEXT, `comment` TEXT, `createdAt` INTEGER, `mediaCommentId` TEXT, `attemptId` INTEGER, PRIMARY KEY(`id`))", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "authorId", + "columnName": "authorId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "authorName", + "columnName": "authorName", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "authorPronouns", + "columnName": "authorPronouns", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "comment", + "columnName": "comment", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "createdAt", + "columnName": "createdAt", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "mediaCommentId", + "columnName": "mediaCommentId", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "attemptId", + "columnName": "attemptId", + "affinity": "INTEGER", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "id" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "PendingSubmissionCommentEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `pageId` TEXT NOT NULL, `comment` TEXT, `date` INTEGER NOT NULL, `status` TEXT NOT NULL, `workerId` TEXT, `filePath` TEXT, `attemptId` INTEGER)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "pageId", + "columnName": "pageId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "comment", + "columnName": "comment", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "status", + "columnName": "status", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "workerId", + "columnName": "workerId", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "filePath", + "columnName": "filePath", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "attemptId", + "columnName": "attemptId", + "affinity": "INTEGER", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "DashboardFileUploadEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`workerId` TEXT NOT NULL, `userId` INTEGER NOT NULL, `title` TEXT, `subtitle` TEXT, PRIMARY KEY(`workerId`))", + "fields": [ + { + "fieldPath": "workerId", + "columnName": "workerId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "userId", + "columnName": "userId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "title", + "columnName": "title", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "subtitle", + "columnName": "subtitle", + "affinity": "TEXT", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "workerId" + ] + }, + "indices": [], + "foreignKeys": [] + } + ], + "views": [], + "setupQueries": [ + "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", + "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'ab4a2a7c1599b2adcf214ca77d6fec66')" + ] + } +} \ No newline at end of file diff --git a/libs/pandautils/schemas/com.instructure.pandautils.room.appdatabase.AppDatabase/4.json b/libs/pandautils/schemas/com.instructure.pandautils.room.appdatabase.AppDatabase/4.json new file mode 100644 index 0000000000..77f9ec534a --- /dev/null +++ b/libs/pandautils/schemas/com.instructure.pandautils.room.appdatabase.AppDatabase/4.json @@ -0,0 +1,442 @@ +{ + "formatVersion": 1, + "database": { + "version": 4, + "identityHash": "687d7d0f33a94588d5c0a3ca30356153", + "entities": [ + { + "tableName": "AttachmentEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `contentType` TEXT, `filename` TEXT, `displayName` TEXT, `url` TEXT, `thumbnailUrl` TEXT, `previewUrl` TEXT, `createdAt` INTEGER, `size` INTEGER NOT NULL, `workerId` TEXT, `submissionCommentId` INTEGER, PRIMARY KEY(`id`))", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "contentType", + "columnName": "contentType", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "filename", + "columnName": "filename", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "displayName", + "columnName": "displayName", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "url", + "columnName": "url", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "thumbnailUrl", + "columnName": "thumbnailUrl", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "previewUrl", + "columnName": "previewUrl", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "createdAt", + "columnName": "createdAt", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "size", + "columnName": "size", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "workerId", + "columnName": "workerId", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "submissionCommentId", + "columnName": "submissionCommentId", + "affinity": "INTEGER", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "id" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "AuthorEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `displayName` TEXT, `avatarImageUrl` TEXT, `htmlUrl` TEXT, `pronouns` TEXT, PRIMARY KEY(`id`))", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "displayName", + "columnName": "displayName", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "avatarImageUrl", + "columnName": "avatarImageUrl", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "htmlUrl", + "columnName": "htmlUrl", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "pronouns", + "columnName": "pronouns", + "affinity": "TEXT", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "id" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "FileUploadInputEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`workerId` TEXT NOT NULL, `courseId` INTEGER, `assignmentId` INTEGER, `quizId` INTEGER, `quizQuestionId` INTEGER, `position` INTEGER, `parentFolderId` INTEGER, `action` TEXT NOT NULL, `userId` INTEGER, `attachments` TEXT NOT NULL, `submissionId` INTEGER, `filePaths` TEXT NOT NULL, `attemptId` INTEGER, `notificationId` INTEGER, PRIMARY KEY(`workerId`))", + "fields": [ + { + "fieldPath": "workerId", + "columnName": "workerId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "courseId", + "columnName": "courseId", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "assignmentId", + "columnName": "assignmentId", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "quizId", + "columnName": "quizId", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "quizQuestionId", + "columnName": "quizQuestionId", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "position", + "columnName": "position", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "parentFolderId", + "columnName": "parentFolderId", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "action", + "columnName": "action", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "userId", + "columnName": "userId", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "attachments", + "columnName": "attachments", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "submissionId", + "columnName": "submissionId", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "filePaths", + "columnName": "filePaths", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "attemptId", + "columnName": "attemptId", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "notificationId", + "columnName": "notificationId", + "affinity": "INTEGER", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "workerId" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "MediaCommentEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`mediaId` TEXT NOT NULL, `displayName` TEXT, `url` TEXT, `mediaType` TEXT, `contentType` TEXT, PRIMARY KEY(`mediaId`))", + "fields": [ + { + "fieldPath": "mediaId", + "columnName": "mediaId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "displayName", + "columnName": "displayName", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "url", + "columnName": "url", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "mediaType", + "columnName": "mediaType", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "contentType", + "columnName": "contentType", + "affinity": "TEXT", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "mediaId" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "SubmissionCommentEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `authorId` INTEGER NOT NULL, `authorName` TEXT, `authorPronouns` TEXT, `comment` TEXT, `createdAt` INTEGER, `mediaCommentId` TEXT, `attemptId` INTEGER, PRIMARY KEY(`id`))", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "authorId", + "columnName": "authorId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "authorName", + "columnName": "authorName", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "authorPronouns", + "columnName": "authorPronouns", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "comment", + "columnName": "comment", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "createdAt", + "columnName": "createdAt", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "mediaCommentId", + "columnName": "mediaCommentId", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "attemptId", + "columnName": "attemptId", + "affinity": "INTEGER", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "id" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "PendingSubmissionCommentEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `pageId` TEXT NOT NULL, `comment` TEXT, `date` INTEGER NOT NULL, `status` TEXT NOT NULL, `workerId` TEXT, `filePath` TEXT, `attemptId` INTEGER)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "pageId", + "columnName": "pageId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "comment", + "columnName": "comment", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "status", + "columnName": "status", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "workerId", + "columnName": "workerId", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "filePath", + "columnName": "filePath", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "attemptId", + "columnName": "attemptId", + "affinity": "INTEGER", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "DashboardFileUploadEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`workerId` TEXT NOT NULL, `userId` INTEGER NOT NULL, `title` TEXT, `subtitle` TEXT, PRIMARY KEY(`workerId`))", + "fields": [ + { + "fieldPath": "workerId", + "columnName": "workerId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "userId", + "columnName": "userId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "title", + "columnName": "title", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "subtitle", + "columnName": "subtitle", + "affinity": "TEXT", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "workerId" + ] + }, + "indices": [], + "foreignKeys": [] + } + ], + "views": [], + "setupQueries": [ + "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", + "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '687d7d0f33a94588d5c0a3ca30356153')" + ] + } +} \ No newline at end of file diff --git a/libs/pandautils/schemas/com.instructure.pandautils.room.appdatabase.AppDatabase/5.json b/libs/pandautils/schemas/com.instructure.pandautils.room.appdatabase.AppDatabase/5.json new file mode 100644 index 0000000000..0047951845 --- /dev/null +++ b/libs/pandautils/schemas/com.instructure.pandautils.room.appdatabase.AppDatabase/5.json @@ -0,0 +1,466 @@ +{ + "formatVersion": 1, + "database": { + "version": 5, + "identityHash": "a12c05da6108f2f24687fabccf1e22f7", + "entities": [ + { + "tableName": "AttachmentEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `contentType` TEXT, `filename` TEXT, `displayName` TEXT, `url` TEXT, `thumbnailUrl` TEXT, `previewUrl` TEXT, `createdAt` INTEGER, `size` INTEGER NOT NULL, `workerId` TEXT, `submissionCommentId` INTEGER, PRIMARY KEY(`id`))", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "contentType", + "columnName": "contentType", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "filename", + "columnName": "filename", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "displayName", + "columnName": "displayName", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "url", + "columnName": "url", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "thumbnailUrl", + "columnName": "thumbnailUrl", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "previewUrl", + "columnName": "previewUrl", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "createdAt", + "columnName": "createdAt", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "size", + "columnName": "size", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "workerId", + "columnName": "workerId", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "submissionCommentId", + "columnName": "submissionCommentId", + "affinity": "INTEGER", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "id" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "AuthorEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `displayName` TEXT, `avatarImageUrl` TEXT, `htmlUrl` TEXT, `pronouns` TEXT, PRIMARY KEY(`id`))", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "displayName", + "columnName": "displayName", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "avatarImageUrl", + "columnName": "avatarImageUrl", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "htmlUrl", + "columnName": "htmlUrl", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "pronouns", + "columnName": "pronouns", + "affinity": "TEXT", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "id" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "FileUploadInputEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`workerId` TEXT NOT NULL, `courseId` INTEGER, `assignmentId` INTEGER, `quizId` INTEGER, `quizQuestionId` INTEGER, `position` INTEGER, `parentFolderId` INTEGER, `action` TEXT NOT NULL, `userId` INTEGER, `attachments` TEXT NOT NULL, `submissionId` INTEGER, `filePaths` TEXT NOT NULL, `attemptId` INTEGER, `notificationId` INTEGER, PRIMARY KEY(`workerId`))", + "fields": [ + { + "fieldPath": "workerId", + "columnName": "workerId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "courseId", + "columnName": "courseId", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "assignmentId", + "columnName": "assignmentId", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "quizId", + "columnName": "quizId", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "quizQuestionId", + "columnName": "quizQuestionId", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "position", + "columnName": "position", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "parentFolderId", + "columnName": "parentFolderId", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "action", + "columnName": "action", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "userId", + "columnName": "userId", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "attachments", + "columnName": "attachments", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "submissionId", + "columnName": "submissionId", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "filePaths", + "columnName": "filePaths", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "attemptId", + "columnName": "attemptId", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "notificationId", + "columnName": "notificationId", + "affinity": "INTEGER", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "workerId" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "MediaCommentEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`mediaId` TEXT NOT NULL, `displayName` TEXT, `url` TEXT, `mediaType` TEXT, `contentType` TEXT, PRIMARY KEY(`mediaId`))", + "fields": [ + { + "fieldPath": "mediaId", + "columnName": "mediaId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "displayName", + "columnName": "displayName", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "url", + "columnName": "url", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "mediaType", + "columnName": "mediaType", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "contentType", + "columnName": "contentType", + "affinity": "TEXT", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "mediaId" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "SubmissionCommentEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `authorId` INTEGER NOT NULL, `authorName` TEXT, `authorPronouns` TEXT, `comment` TEXT, `createdAt` INTEGER, `mediaCommentId` TEXT, `attemptId` INTEGER, PRIMARY KEY(`id`))", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "authorId", + "columnName": "authorId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "authorName", + "columnName": "authorName", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "authorPronouns", + "columnName": "authorPronouns", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "comment", + "columnName": "comment", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "createdAt", + "columnName": "createdAt", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "mediaCommentId", + "columnName": "mediaCommentId", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "attemptId", + "columnName": "attemptId", + "affinity": "INTEGER", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "id" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "PendingSubmissionCommentEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `pageId` TEXT NOT NULL, `comment` TEXT, `date` INTEGER NOT NULL, `status` TEXT NOT NULL, `workerId` TEXT, `filePath` TEXT, `attemptId` INTEGER)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "pageId", + "columnName": "pageId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "comment", + "columnName": "comment", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "status", + "columnName": "status", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "workerId", + "columnName": "workerId", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "filePath", + "columnName": "filePath", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "attemptId", + "columnName": "attemptId", + "affinity": "INTEGER", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "DashboardFileUploadEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`workerId` TEXT NOT NULL, `userId` INTEGER NOT NULL, `title` TEXT, `subtitle` TEXT, `courseId` INTEGER, `assignmentId` INTEGER, `attemptId` INTEGER, `folderId` INTEGER, PRIMARY KEY(`workerId`))", + "fields": [ + { + "fieldPath": "workerId", + "columnName": "workerId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "userId", + "columnName": "userId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "title", + "columnName": "title", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "subtitle", + "columnName": "subtitle", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "courseId", + "columnName": "courseId", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "assignmentId", + "columnName": "assignmentId", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "attemptId", + "columnName": "attemptId", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "folderId", + "columnName": "folderId", + "affinity": "INTEGER", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "workerId" + ] + }, + "indices": [], + "foreignKeys": [] + } + ], + "views": [], + "setupQueries": [ + "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", + "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'a12c05da6108f2f24687fabccf1e22f7')" + ] + } +} \ No newline at end of file diff --git a/libs/pandautils/schemas/com.instructure.pandautils.room.appdatabase.AppDatabase/6.json b/libs/pandautils/schemas/com.instructure.pandautils.room.appdatabase.AppDatabase/6.json new file mode 100644 index 0000000000..1832051edd --- /dev/null +++ b/libs/pandautils/schemas/com.instructure.pandautils.room.appdatabase.AppDatabase/6.json @@ -0,0 +1,478 @@ +{ + "formatVersion": 1, + "database": { + "version": 6, + "identityHash": "c843a3949b0b11ce080d316a1f324e47", + "entities": [ + { + "tableName": "AttachmentEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `contentType` TEXT, `filename` TEXT, `displayName` TEXT, `url` TEXT, `thumbnailUrl` TEXT, `previewUrl` TEXT, `createdAt` INTEGER, `size` INTEGER NOT NULL, `workerId` TEXT, `submissionCommentId` INTEGER, `submissionId` INTEGER, PRIMARY KEY(`id`))", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "contentType", + "columnName": "contentType", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "filename", + "columnName": "filename", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "displayName", + "columnName": "displayName", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "url", + "columnName": "url", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "thumbnailUrl", + "columnName": "thumbnailUrl", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "previewUrl", + "columnName": "previewUrl", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "createdAt", + "columnName": "createdAt", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "size", + "columnName": "size", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "workerId", + "columnName": "workerId", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "submissionCommentId", + "columnName": "submissionCommentId", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "submissionId", + "columnName": "submissionId", + "affinity": "INTEGER", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "id" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "AuthorEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `displayName` TEXT, `avatarImageUrl` TEXT, `htmlUrl` TEXT, `pronouns` TEXT, PRIMARY KEY(`id`))", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "displayName", + "columnName": "displayName", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "avatarImageUrl", + "columnName": "avatarImageUrl", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "htmlUrl", + "columnName": "htmlUrl", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "pronouns", + "columnName": "pronouns", + "affinity": "TEXT", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "id" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "FileUploadInputEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`workerId` TEXT NOT NULL, `courseId` INTEGER, `assignmentId` INTEGER, `quizId` INTEGER, `quizQuestionId` INTEGER, `position` INTEGER, `parentFolderId` INTEGER, `action` TEXT NOT NULL, `userId` INTEGER, `attachments` TEXT NOT NULL, `submissionId` INTEGER, `filePaths` TEXT NOT NULL, `attemptId` INTEGER, `notificationId` INTEGER, PRIMARY KEY(`workerId`))", + "fields": [ + { + "fieldPath": "workerId", + "columnName": "workerId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "courseId", + "columnName": "courseId", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "assignmentId", + "columnName": "assignmentId", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "quizId", + "columnName": "quizId", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "quizQuestionId", + "columnName": "quizQuestionId", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "position", + "columnName": "position", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "parentFolderId", + "columnName": "parentFolderId", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "action", + "columnName": "action", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "userId", + "columnName": "userId", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "attachments", + "columnName": "attachments", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "submissionId", + "columnName": "submissionId", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "filePaths", + "columnName": "filePaths", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "attemptId", + "columnName": "attemptId", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "notificationId", + "columnName": "notificationId", + "affinity": "INTEGER", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "workerId" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "MediaCommentEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`mediaId` TEXT NOT NULL, `displayName` TEXT, `url` TEXT, `mediaType` TEXT, `contentType` TEXT, PRIMARY KEY(`mediaId`))", + "fields": [ + { + "fieldPath": "mediaId", + "columnName": "mediaId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "displayName", + "columnName": "displayName", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "url", + "columnName": "url", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "mediaType", + "columnName": "mediaType", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "contentType", + "columnName": "contentType", + "affinity": "TEXT", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "mediaId" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "SubmissionCommentEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `authorId` INTEGER NOT NULL, `authorName` TEXT, `authorPronouns` TEXT, `comment` TEXT, `createdAt` INTEGER, `mediaCommentId` TEXT, `attemptId` INTEGER, `submissionId` INTEGER, PRIMARY KEY(`id`))", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "authorId", + "columnName": "authorId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "authorName", + "columnName": "authorName", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "authorPronouns", + "columnName": "authorPronouns", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "comment", + "columnName": "comment", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "createdAt", + "columnName": "createdAt", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "mediaCommentId", + "columnName": "mediaCommentId", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "attemptId", + "columnName": "attemptId", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "submissionId", + "columnName": "submissionId", + "affinity": "INTEGER", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "id" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "PendingSubmissionCommentEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `pageId` TEXT NOT NULL, `comment` TEXT, `date` INTEGER NOT NULL, `status` TEXT NOT NULL, `workerId` TEXT, `filePath` TEXT, `attemptId` INTEGER)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "pageId", + "columnName": "pageId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "comment", + "columnName": "comment", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "status", + "columnName": "status", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "workerId", + "columnName": "workerId", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "filePath", + "columnName": "filePath", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "attemptId", + "columnName": "attemptId", + "affinity": "INTEGER", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "DashboardFileUploadEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`workerId` TEXT NOT NULL, `userId` INTEGER NOT NULL, `title` TEXT, `subtitle` TEXT, `courseId` INTEGER, `assignmentId` INTEGER, `attemptId` INTEGER, `folderId` INTEGER, PRIMARY KEY(`workerId`))", + "fields": [ + { + "fieldPath": "workerId", + "columnName": "workerId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "userId", + "columnName": "userId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "title", + "columnName": "title", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "subtitle", + "columnName": "subtitle", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "courseId", + "columnName": "courseId", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "assignmentId", + "columnName": "assignmentId", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "attemptId", + "columnName": "attemptId", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "folderId", + "columnName": "folderId", + "affinity": "INTEGER", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "workerId" + ] + }, + "indices": [], + "foreignKeys": [] + } + ], + "views": [], + "setupQueries": [ + "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", + "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'c843a3949b0b11ce080d316a1f324e47')" + ] + } +} \ No newline at end of file diff --git a/libs/pandautils/src/androidTest/java/com/instructure/pandautils/room/appdatabase/AppDatabaseMigrationTest.kt b/libs/pandautils/src/androidTest/java/com/instructure/pandautils/room/appdatabase/AppDatabaseMigrationTest.kt new file mode 100644 index 0000000000..308efb812f --- /dev/null +++ b/libs/pandautils/src/androidTest/java/com/instructure/pandautils/room/appdatabase/AppDatabaseMigrationTest.kt @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2023 - present Instructure, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ +package com.instructure.pandautils.room.appdatabase + +import androidx.room.Room +import androidx.room.testing.MigrationTestHelper +import androidx.sqlite.db.framework.FrameworkSQLiteOpenHelperFactory +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.platform.app.InstrumentationRegistry +import org.junit.Rule +import org.junit.Test +import org.junit.runner.RunWith +import java.io.IOException + +private const val TEST_DB = "migration-test" +// We don't have the schemas for version 1,2 so we only start testing from version 3. +private val ALL_MIGRATIONS = appDatabaseMigrations.takeLast(appDatabaseMigrations.size - 2).toTypedArray() + +@RunWith(AndroidJUnit4::class) +class AppDatabaseMigrationTest { + + @get:Rule + val helper: MigrationTestHelper = MigrationTestHelper( + InstrumentationRegistry.getInstrumentation(), + AppDatabase::class.java.canonicalName, + FrameworkSQLiteOpenHelperFactory() + ) + + @Test + @Throws(IOException::class) + fun migrateAll() { + // Create earliest version of the database. + // We don't have the schemas for version 1,2 so we only start testing from version 3. + helper.createDatabase(TEST_DB, 3).apply { + close() + } + + // Open latest version of the database. Room validates the schema once all migrations execute. + Room.databaseBuilder(InstrumentationRegistry.getInstrumentation().targetContext, AppDatabase::class.java, TEST_DB) + .addMigrations(*ALL_MIGRATIONS).build().apply { + openHelper.writableDatabase.close() + } + } +} \ No newline at end of file diff --git a/libs/pandautils/src/androidTest/java/com/instructure/pandautils/room/appdatabase/daos/AttachmentDaoTest.kt b/libs/pandautils/src/androidTest/java/com/instructure/pandautils/room/appdatabase/daos/AttachmentDaoTest.kt new file mode 100644 index 0000000000..3f91092fce --- /dev/null +++ b/libs/pandautils/src/androidTest/java/com/instructure/pandautils/room/appdatabase/daos/AttachmentDaoTest.kt @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2023 - present Instructure, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ +package com.instructure.pandautils.room.appdatabase.daos + +import android.content.Context +import androidx.room.Room +import androidx.test.core.app.ApplicationProvider +import androidx.test.ext.junit.runners.AndroidJUnit4 +import com.instructure.pandautils.room.appdatabase.AppDatabase +import com.instructure.pandautils.room.common.daos.AttachmentDao +import com.instructure.pandautils.room.common.entities.AttachmentEntity +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.test.runTest +import org.junit.After +import org.junit.Assert +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import java.util.Date + +@ExperimentalCoroutinesApi +@RunWith(AndroidJUnit4::class) +class AttachmentDaoTest { + + private lateinit var db: AppDatabase + private lateinit var attachmentDao: AttachmentDao + + @Before + fun setUp() { + val context = ApplicationProvider.getApplicationContext() + db = Room.inMemoryDatabaseBuilder(context, AppDatabase::class.java).build() + attachmentDao = db.attachmentDao() + } + + @After + fun tearDoown() { + db.close() + } + + @Test + fun insertAndFindingByParentId() = runTest { + val attachmentEntity = AttachmentEntity(id = 1, contentType = "image/jpg", filename = "image.jpg", displayName = "File", + url = "file.com", createdAt = Date(), size = 10000, workerId = "123", submissionCommentId = 123 + ) + + val attachmentEntity2 = attachmentEntity.copy(id = 2, workerId = "124", filename = "image2.jpg") + + attachmentDao.insertAll(listOf(attachmentEntity, attachmentEntity2)) + val result = attachmentDao.findByParentId("123") + Assert.assertEquals(1, result!!.size) + Assert.assertEquals(attachmentEntity, result.first()) + } + + @Test + fun dontReturnAnyItemIfEntitiesAreDeleted() = runTest { + val attachmentEntity = AttachmentEntity(id = 1, contentType = "image/jpg", filename = "image.jpg", displayName = "File", + url = "file.com", createdAt = Date(), size = 10000, workerId = "123", submissionCommentId = 123 + ) + + val attachmentEntity2 = attachmentEntity.copy(id = 2, workerId = "124", filename = "image2.jpg") + + attachmentDao.insertAll(listOf(attachmentEntity, attachmentEntity2)) + attachmentDao.deleteAll(listOf(attachmentEntity, attachmentEntity2)) + val result = attachmentDao.findByParentId("123") + + Assert.assertEquals(0, result!!.size) + } + +} \ No newline at end of file diff --git a/libs/pandautils/src/androidTest/java/com/instructure/pandautils/room/appdatabase/daos/DashboardFileUploadDaoTest.kt b/libs/pandautils/src/androidTest/java/com/instructure/pandautils/room/appdatabase/daos/DashboardFileUploadDaoTest.kt new file mode 100644 index 0000000000..229d4b8cd5 --- /dev/null +++ b/libs/pandautils/src/androidTest/java/com/instructure/pandautils/room/appdatabase/daos/DashboardFileUploadDaoTest.kt @@ -0,0 +1,90 @@ +/* + * Copyright (C) 2023 - present Instructure, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ +package com.instructure.pandautils.room.appdatabase.daos + +import android.content.Context +import androidx.arch.core.executor.testing.InstantTaskExecutorRule +import androidx.lifecycle.Observer +import androidx.room.Room +import androidx.test.core.app.ApplicationProvider +import androidx.test.ext.junit.runners.AndroidJUnit4 +import com.instructure.pandautils.room.appdatabase.AppDatabase +import com.instructure.pandautils.room.appdatabase.entities.DashboardFileUploadEntity +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.test.runTest +import org.junit.After +import org.junit.Assert +import org.junit.Before +import org.junit.Rule +import org.junit.Test +import org.junit.runner.RunWith + +@ExperimentalCoroutinesApi +@RunWith(AndroidJUnit4::class) +class DashboardFileUploadDaoTest { + + private lateinit var db: AppDatabase + private lateinit var dashboardFileUploadDao: DashboardFileUploadDao + + @get:Rule + var instantExecutorRule = InstantTaskExecutorRule() + + @Before + fun setUp() { + val context = ApplicationProvider.getApplicationContext() + db = Room.inMemoryDatabaseBuilder(context, AppDatabase::class.java).build() + dashboardFileUploadDao = db.dashboardFileUploadDao() + } + + @After + fun tearDoown() { + db.close() + } + + @Test + fun getCorrectDataForUserId() = runTest { + val uploadForUser1 = DashboardFileUploadEntity("1", 1, title = "Upload", null, null, null, null, null) + val upload2ForUser1 = DashboardFileUploadEntity("2", 1, title = "Upload 2", null, null, null, null, null) + val uploadForUser2 = DashboardFileUploadEntity("3", 2, title = "We don't need this", null, null, null, null, null) + dashboardFileUploadDao.insert(uploadForUser1) + dashboardFileUploadDao.insert(upload2ForUser1) + dashboardFileUploadDao.insert(uploadForUser2) + + val result = dashboardFileUploadDao.getAllForUser(1) + + result.observeForever { + Assert.assertEquals(2, it.size) + Assert.assertEquals(uploadForUser1, it.get(0)) + Assert.assertEquals(upload2ForUser1, it.get(1)) + } + } + + @Test + fun deleteByWorkerIdDeletesCorrectItem() = runTest { + val uploadForUser1 = DashboardFileUploadEntity("1", 1, title = "Upload", null, null, null, null, null) + val uploadForUser2 = DashboardFileUploadEntity("3", 2, title = "We don't need this", null, null, null, null, null) + dashboardFileUploadDao.insert(uploadForUser1) + dashboardFileUploadDao.insert(uploadForUser2) + + dashboardFileUploadDao.deleteByWorkerId("1") + val result = dashboardFileUploadDao.getAllForUser(1) + + result.observeForever { + Assert.assertEquals(0, it.size) + } + } +} \ No newline at end of file diff --git a/libs/pandautils/src/androidTest/java/com/instructure/pandautils/room/appdatabase/daos/FileUploadInputDaoTest.kt b/libs/pandautils/src/androidTest/java/com/instructure/pandautils/room/appdatabase/daos/FileUploadInputDaoTest.kt new file mode 100644 index 0000000000..5b98447cdb --- /dev/null +++ b/libs/pandautils/src/androidTest/java/com/instructure/pandautils/room/appdatabase/daos/FileUploadInputDaoTest.kt @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2023 - present Instructure, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ +package com.instructure.pandautils.room.appdatabase.daos + +import android.content.Context +import androidx.room.Room +import androidx.test.core.app.ApplicationProvider +import androidx.test.ext.junit.runners.AndroidJUnit4 +import com.instructure.pandautils.room.appdatabase.AppDatabase +import com.instructure.pandautils.room.appdatabase.entities.FileUploadInputEntity +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.test.runTest +import org.junit.After +import org.junit.Assert +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith + +@ExperimentalCoroutinesApi +@RunWith(AndroidJUnit4::class) +class FileUploadInputDaoTest { + + private lateinit var db: AppDatabase + private lateinit var fileUploadInputDao: FileUploadInputDao + + @Before + fun setUp() { + val context = ApplicationProvider.getApplicationContext() + db = Room.inMemoryDatabaseBuilder(context, AppDatabase::class.java).build() + fileUploadInputDao = db.fileUploadInputDao() + } + + @After + fun tearDoown() { + db.close() + } + + @Test + fun findCorrectEntityByWorkerId() = runTest { + fileUploadInputDao.insert(FileUploadInputEntity(workerId = "2", action = "upload 2", filePaths = emptyList())) + fileUploadInputDao.insert(FileUploadInputEntity(workerId = "1", action = "upload", filePaths = emptyList())) + fileUploadInputDao.insert(FileUploadInputEntity(workerId = "3", action = "upload 3", filePaths = emptyList())) + + val result = fileUploadInputDao.findByWorkerId("1") + + Assert.assertEquals("upload", result!!.action) + } +} \ No newline at end of file diff --git a/libs/pandautils/src/androidTest/java/com/instructure/pandautils/room/appdatabase/daos/PendingSubmissionCommentDaoTest.kt b/libs/pandautils/src/androidTest/java/com/instructure/pandautils/room/appdatabase/daos/PendingSubmissionCommentDaoTest.kt new file mode 100644 index 0000000000..3efdddd201 --- /dev/null +++ b/libs/pandautils/src/androidTest/java/com/instructure/pandautils/room/appdatabase/daos/PendingSubmissionCommentDaoTest.kt @@ -0,0 +1,126 @@ +/* + * Copyright (C) 2023 - present Instructure, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ +package com.instructure.pandautils.room.appdatabase.daos + +import android.content.Context +import androidx.room.Room +import androidx.test.core.app.ApplicationProvider +import androidx.test.ext.junit.runners.AndroidJUnit4 +import com.instructure.pandautils.room.appdatabase.AppDatabase +import com.instructure.pandautils.room.appdatabase.entities.FileUploadInputEntity +import com.instructure.pandautils.room.appdatabase.entities.PendingSubmissionCommentEntity +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.test.runTest +import org.junit.After +import org.junit.Assert +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith + +@ExperimentalCoroutinesApi +@RunWith(AndroidJUnit4::class) +class PendingSubmissionCommentDaoTest { + + private lateinit var db: AppDatabase + private lateinit var pendingSubmissionCommentDao: PendingSubmissionCommentDao + + private lateinit var fileUploadInputDao: FileUploadInputDao + + @Before + fun setUp() { + val context = ApplicationProvider.getApplicationContext() + db = Room.inMemoryDatabaseBuilder(context, AppDatabase::class.java).build() + pendingSubmissionCommentDao = db.pendingSubmissionCommentDao() + fileUploadInputDao = db.fileUploadInputDao() + } + + @After + fun tearDoown() { + db.close() + } + + @Test + fun testFindCorrectItemByWorkedId() = runTest { + val itemToFind = PendingSubmissionCommentEntity(2, pageId = "12", workerId = "222") + pendingSubmissionCommentDao.insert(PendingSubmissionCommentEntity(1, pageId = "11", workerId = "111")) + pendingSubmissionCommentDao.insert(itemToFind) + pendingSubmissionCommentDao.insert(PendingSubmissionCommentEntity(3, pageId = "12", workerId = "333")) + + val result = pendingSubmissionCommentDao.findByWorkerId("222") + + Assert.assertEquals(itemToFind, result) + } + + @Test + fun testFindCorrectItemById() = runTest { + val itemToFind = PendingSubmissionCommentEntity(2, pageId = "12", workerId = "222") + pendingSubmissionCommentDao.insert(PendingSubmissionCommentEntity(1, pageId = "11", workerId = "111")) + pendingSubmissionCommentDao.insert(itemToFind) + pendingSubmissionCommentDao.insert(PendingSubmissionCommentEntity(3, pageId = "12", workerId = "333")) + + val result = pendingSubmissionCommentDao.findById(2) + + Assert.assertEquals(itemToFind, result) + } + + @Test + fun testFindCorrectItemByWorkedIdWithInputData() = runTest { + val itemToFind = PendingSubmissionCommentEntity(2, pageId = "12", workerId = "222") + pendingSubmissionCommentDao.insert(PendingSubmissionCommentEntity(1, pageId = "11", workerId = "111")) + pendingSubmissionCommentDao.insert(itemToFind) + fileUploadInputDao.insert(FileUploadInputEntity(workerId = "222", action = "File Upload", filePaths = emptyList())) + + val result = pendingSubmissionCommentDao.findByWorkerIdWithInputData("222") + + Assert.assertEquals(itemToFind, result!!.pendingSubmissionCommentEntity) + Assert.assertEquals("File Upload", result.fileUploadInput!!.action) + } + + @Test + fun testFindCorrectItemByPageIdWithInputData() = runTest { + val itemToFind = PendingSubmissionCommentEntity(2, pageId = "12", workerId = "222") + pendingSubmissionCommentDao.insert(PendingSubmissionCommentEntity(1, pageId = "11", workerId = "111")) + pendingSubmissionCommentDao.insert(itemToFind) + fileUploadInputDao.insert(FileUploadInputEntity(workerId = "222", action = "File Upload", filePaths = emptyList())) + + val result = pendingSubmissionCommentDao.findByPageId("12") + + Assert.assertEquals(1, result!!.size) + Assert.assertEquals(itemToFind, result.first().pendingSubmissionCommentEntity) + Assert.assertEquals("File Upload", result.first().fileUploadInput!!.action) + } + + @Test + fun testFindByStatusOnlyReturnsTheItemsWithCorrectStatusAndNonNullWorkerId() = runTest { + val itemToFind = PendingSubmissionCommentEntity(2, status = "progress", pageId = "12", workerId = "222") + val itemToFind2 = PendingSubmissionCommentEntity(4, status = "progress", pageId = "15", workerId = "333") + pendingSubmissionCommentDao.insert(PendingSubmissionCommentEntity(1, status = "finished", pageId = "11", workerId = "111")) + pendingSubmissionCommentDao.insert(itemToFind) + pendingSubmissionCommentDao.insert(PendingSubmissionCommentEntity(3, status = "progress", pageId = "19")) + pendingSubmissionCommentDao.insert(itemToFind2) + fileUploadInputDao.insert(FileUploadInputEntity(workerId = "222", action = "File Upload", filePaths = emptyList())) + fileUploadInputDao.insert(FileUploadInputEntity(workerId = "333", action = "File Upload 2", filePaths = emptyList())) + + val result = pendingSubmissionCommentDao.findByStatus("progress") + + Assert.assertEquals(2, result!!.size) + Assert.assertEquals(itemToFind, result.first().pendingSubmissionCommentEntity) + Assert.assertEquals(itemToFind2, result.get(1).pendingSubmissionCommentEntity) + Assert.assertEquals("File Upload", result.first().fileUploadInput!!.action) + Assert.assertEquals("File Upload 2", result.get(1).fileUploadInput!!.action) + } +} \ No newline at end of file diff --git a/libs/pandautils/src/androidTest/java/com/instructure/pandautils/room/appdatabase/daos/SubmissionCommentDaoTest.kt b/libs/pandautils/src/androidTest/java/com/instructure/pandautils/room/appdatabase/daos/SubmissionCommentDaoTest.kt new file mode 100644 index 0000000000..3f143ee8c9 --- /dev/null +++ b/libs/pandautils/src/androidTest/java/com/instructure/pandautils/room/appdatabase/daos/SubmissionCommentDaoTest.kt @@ -0,0 +1,98 @@ +/* + * Copyright (C) 2023 - present Instructure, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ +package com.instructure.pandautils.room.appdatabase.daos + +import android.content.Context +import androidx.room.Room +import androidx.test.core.app.ApplicationProvider +import androidx.test.ext.junit.runners.AndroidJUnit4 +import com.instructure.pandautils.room.appdatabase.AppDatabase +import com.instructure.pandautils.room.common.daos.AttachmentDao +import com.instructure.pandautils.room.common.daos.AuthorDao +import com.instructure.pandautils.room.common.daos.MediaCommentDao +import com.instructure.pandautils.room.common.daos.SubmissionCommentDao +import com.instructure.pandautils.room.common.entities.AttachmentEntity +import com.instructure.pandautils.room.common.entities.AuthorEntity +import com.instructure.pandautils.room.common.entities.MediaCommentEntity +import com.instructure.pandautils.room.common.entities.SubmissionCommentEntity +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.test.runTest +import org.junit.After +import org.junit.Assert +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith + +@ExperimentalCoroutinesApi +@RunWith(AndroidJUnit4::class) +class SubmissionCommentDaoTest { + + private lateinit var db: AppDatabase + private lateinit var submissionCommentDao: SubmissionCommentDao + + private lateinit var attachmentDao: AttachmentDao + private lateinit var mediaCommentDao: MediaCommentDao + private lateinit var authorDao: AuthorDao + + @Before + fun setUp() { + val context = ApplicationProvider.getApplicationContext() + db = Room.inMemoryDatabaseBuilder(context, AppDatabase::class.java).build() + submissionCommentDao = db.submissionCommentDao() + + attachmentDao = db.attachmentDao() + mediaCommentDao = db.mediaCommentDao() + authorDao = db.authorDao() + } + + @After + fun tearDoown() { + db.close() + } + + @Test + fun getSubmissionCommentWithAttachmentsById() = runTest { + // Setup all DAOs with the data needed for the query + val id = 1L + val submissionComment = SubmissionCommentEntity( + id = id, + comment = "These are the droids you are looking for", + authorId = 1, + mediaCommentId = "66" + ) + + val submissionComment2 = SubmissionCommentEntity( + id = 2, + comment = "These are not the droids you are looking for", + ) + + submissionCommentDao.insert(submissionComment) + submissionCommentDao.insert(submissionComment2) + authorDao.insert(AuthorEntity(id = 1, displayName = "Obi-Wan")) + attachmentDao.insert(AttachmentEntity(id = 5, submissionCommentId = 1, filename = "droids.mp4")) + mediaCommentDao.insert(MediaCommentEntity(mediaId = "66", displayName = "Order 66")) + + // Verify correct query + val result = submissionCommentDao.findById(id) + + Assert.assertEquals(submissionComment, result!!.submissionComment) + Assert.assertEquals(1, result.attachments!!.size) + Assert.assertEquals("droids.mp4", result.attachments!!.first().filename) + Assert.assertEquals("Obi-Wan", result.author!!.displayName) + Assert.assertEquals("Order 66", result.mediaComment!!.displayName) + } +} \ No newline at end of file diff --git a/libs/pandautils/src/main/java/com/instructure/pandautils/di/DatabaseModule.kt b/libs/pandautils/src/main/java/com/instructure/pandautils/di/DatabaseModule.kt index 5d7b3807eb..311a14c31e 100644 --- a/libs/pandautils/src/main/java/com/instructure/pandautils/di/DatabaseModule.kt +++ b/libs/pandautils/src/main/java/com/instructure/pandautils/di/DatabaseModule.kt @@ -2,6 +2,10 @@ package com.instructure.pandautils.di import com.instructure.pandautils.room.appdatabase.AppDatabase import com.instructure.pandautils.room.appdatabase.daos.* +import com.instructure.pandautils.room.common.daos.AttachmentDao +import com.instructure.pandautils.room.common.daos.AuthorDao +import com.instructure.pandautils.room.common.daos.MediaCommentDao +import com.instructure.pandautils.room.common.daos.SubmissionCommentDao import dagger.Module import dagger.Provides import dagger.hilt.InstallIn diff --git a/libs/pandautils/src/main/java/com/instructure/pandautils/features/dashboard/notifications/DashboardNotificationsViewModel.kt b/libs/pandautils/src/main/java/com/instructure/pandautils/features/dashboard/notifications/DashboardNotificationsViewModel.kt index 73b4c351ef..c8e284b1eb 100644 --- a/libs/pandautils/src/main/java/com/instructure/pandautils/features/dashboard/notifications/DashboardNotificationsViewModel.kt +++ b/libs/pandautils/src/main/java/com/instructure/pandautils/features/dashboard/notifications/DashboardNotificationsViewModel.kt @@ -92,7 +92,7 @@ class DashboardNotificationsViewModel @Inject constructor( } } - private val fileUploads = dashboardFileUploadDao.getAll(apiPrefs.user?.id.orDefault()) + private val fileUploads = dashboardFileUploadDao.getAllForUser(apiPrefs.user?.id.orDefault()) init { fileUploads.observeForever(runningWorkersObserver) diff --git a/libs/pandautils/src/main/java/com/instructure/pandautils/features/file/upload/worker/FileUploadWorker.kt b/libs/pandautils/src/main/java/com/instructure/pandautils/features/file/upload/worker/FileUploadWorker.kt index 4799e6bf9f..a0bc0b9c25 100644 --- a/libs/pandautils/src/main/java/com/instructure/pandautils/features/file/upload/worker/FileUploadWorker.kt +++ b/libs/pandautils/src/main/java/com/instructure/pandautils/features/file/upload/worker/FileUploadWorker.kt @@ -37,6 +37,14 @@ import com.instructure.pandautils.R import com.instructure.pandautils.features.file.upload.FileUploadUtilsHelper import com.instructure.pandautils.room.appdatabase.daos.* import com.instructure.pandautils.room.appdatabase.entities.* +import com.instructure.pandautils.room.common.daos.AttachmentDao +import com.instructure.pandautils.room.common.daos.AuthorDao +import com.instructure.pandautils.room.common.daos.MediaCommentDao +import com.instructure.pandautils.room.common.daos.SubmissionCommentDao +import com.instructure.pandautils.room.common.entities.AttachmentEntity +import com.instructure.pandautils.room.common.entities.AuthorEntity +import com.instructure.pandautils.room.common.entities.MediaCommentEntity +import com.instructure.pandautils.room.common.entities.SubmissionCommentEntity import com.instructure.pandautils.utils.FileUploadUtils import com.instructure.pandautils.utils.orDefault import com.instructure.pandautils.utils.toJson diff --git a/libs/pandautils/src/main/java/com/instructure/pandautils/room/appdatabase/AppDatabase.kt b/libs/pandautils/src/main/java/com/instructure/pandautils/room/appdatabase/AppDatabase.kt index 403fd4631e..243e856f65 100644 --- a/libs/pandautils/src/main/java/com/instructure/pandautils/room/appdatabase/AppDatabase.kt +++ b/libs/pandautils/src/main/java/com/instructure/pandautils/room/appdatabase/AppDatabase.kt @@ -3,9 +3,17 @@ package com.instructure.pandautils.room.appdatabase import androidx.room.Database import androidx.room.RoomDatabase import androidx.room.TypeConverters -import com.instructure.pandautils.room.Converters +import com.instructure.pandautils.room.common.Converters import com.instructure.pandautils.room.appdatabase.daos.* import com.instructure.pandautils.room.appdatabase.entities.* +import com.instructure.pandautils.room.common.daos.AttachmentDao +import com.instructure.pandautils.room.common.daos.AuthorDao +import com.instructure.pandautils.room.common.daos.MediaCommentDao +import com.instructure.pandautils.room.common.daos.SubmissionCommentDao +import com.instructure.pandautils.room.common.entities.AttachmentEntity +import com.instructure.pandautils.room.common.entities.AuthorEntity +import com.instructure.pandautils.room.common.entities.MediaCommentEntity +import com.instructure.pandautils.room.common.entities.SubmissionCommentEntity @Database( entities = [ @@ -16,7 +24,7 @@ import com.instructure.pandautils.room.appdatabase.entities.* SubmissionCommentEntity::class, PendingSubmissionCommentEntity::class, DashboardFileUploadEntity::class - ], version = 5 + ], version = 6 ) @TypeConverters(Converters::class) abstract class AppDatabase : RoomDatabase() { diff --git a/libs/pandautils/src/main/java/com/instructure/pandautils/room/appdatabase/AppDatabaseMigrations.kt b/libs/pandautils/src/main/java/com/instructure/pandautils/room/appdatabase/AppDatabaseMigrations.kt index a99980b688..fe79a15f37 100644 --- a/libs/pandautils/src/main/java/com/instructure/pandautils/room/appdatabase/AppDatabaseMigrations.kt +++ b/libs/pandautils/src/main/java/com/instructure/pandautils/room/appdatabase/AppDatabaseMigrations.kt @@ -17,7 +17,7 @@ package com.instructure.pandautils.room.appdatabase -import com.instructure.pandautils.room.createMigration +import com.instructure.pandautils.room.common.createMigration val appDatabaseMigrations = arrayOf( @@ -40,5 +40,10 @@ val appDatabaseMigrations = arrayOf( database.execSQL("ALTER TABLE DashboardFileUploadEntity ADD COLUMN assignmentId INTEGER") database.execSQL("ALTER TABLE DashboardFileUploadEntity ADD COLUMN attemptId INTEGER") database.execSQL("ALTER TABLE DashboardFileUploadEntity ADD COLUMN folderId INTEGER") + }, + + createMigration(5, 6) { database -> + database.execSQL("ALTER TABLE AttachmentEntity ADD COLUMN submissionId INTEGER") + database.execSQL("ALTER TABLE SubmissionCommentEntity ADD COLUMN submissionId INTEGER") } ) diff --git a/libs/pandautils/src/main/java/com/instructure/pandautils/room/appdatabase/daos/DashboardFileUploadDao.kt b/libs/pandautils/src/main/java/com/instructure/pandautils/room/appdatabase/daos/DashboardFileUploadDao.kt index acc60f7d86..aafac868d8 100644 --- a/libs/pandautils/src/main/java/com/instructure/pandautils/room/appdatabase/daos/DashboardFileUploadDao.kt +++ b/libs/pandautils/src/main/java/com/instructure/pandautils/room/appdatabase/daos/DashboardFileUploadDao.kt @@ -14,7 +14,7 @@ interface DashboardFileUploadDao { suspend fun delete(dashboardFileUploadEntity: DashboardFileUploadEntity) @Query("SELECT * FROM DashboardFileUploadEntity WHERE userId = :userId") - fun getAll(userId: Long): LiveData> + fun getAllForUser(userId: Long): LiveData> @Query("DELETE FROM DashboardFileUploadEntity WHERE workerId = :workerId") suspend fun deleteByWorkerId(workerId: String) diff --git a/libs/pandautils/src/main/java/com/instructure/pandautils/room/Converters.kt b/libs/pandautils/src/main/java/com/instructure/pandautils/room/common/Converters.kt similarity index 74% rename from libs/pandautils/src/main/java/com/instructure/pandautils/room/Converters.kt rename to libs/pandautils/src/main/java/com/instructure/pandautils/room/common/Converters.kt index 5b3b8e6259..061e9ddb56 100644 --- a/libs/pandautils/src/main/java/com/instructure/pandautils/room/Converters.kt +++ b/libs/pandautils/src/main/java/com/instructure/pandautils/room/common/Converters.kt @@ -1,4 +1,4 @@ -package com.instructure.pandautils.room +package com.instructure.pandautils.room.common import androidx.room.TypeConverter import java.util.* @@ -25,12 +25,12 @@ class Converters { } @TypeConverter - fun dateToLong(date: Date): Long { - return date.time + fun dateToLong(date: Date?): Long? { + return date?.time } @TypeConverter - fun longToDate(timestamp: Long): Date { - return Date(timestamp) + fun longToDate(timestamp: Long?): Date? { + return timestamp?.let { Date(it) } } } \ No newline at end of file diff --git a/libs/pandautils/src/main/java/com/instructure/pandautils/room/MigrationUtils.kt b/libs/pandautils/src/main/java/com/instructure/pandautils/room/common/MigrationUtils.kt similarity index 95% rename from libs/pandautils/src/main/java/com/instructure/pandautils/room/MigrationUtils.kt rename to libs/pandautils/src/main/java/com/instructure/pandautils/room/common/MigrationUtils.kt index ae5918df5b..4ae2fd7468 100644 --- a/libs/pandautils/src/main/java/com/instructure/pandautils/room/MigrationUtils.kt +++ b/libs/pandautils/src/main/java/com/instructure/pandautils/room/common/MigrationUtils.kt @@ -15,7 +15,7 @@ * */ -package com.instructure.pandautils.room +package com.instructure.pandautils.room.common import androidx.room.migration.Migration import androidx.sqlite.db.SupportSQLiteDatabase diff --git a/libs/pandautils/src/main/java/com/instructure/pandautils/room/appdatabase/daos/AttachmentDao.kt b/libs/pandautils/src/main/java/com/instructure/pandautils/room/common/daos/AttachmentDao.kt similarity index 80% rename from libs/pandautils/src/main/java/com/instructure/pandautils/room/appdatabase/daos/AttachmentDao.kt rename to libs/pandautils/src/main/java/com/instructure/pandautils/room/common/daos/AttachmentDao.kt index 1851f4d314..8d2942bf75 100644 --- a/libs/pandautils/src/main/java/com/instructure/pandautils/room/appdatabase/daos/AttachmentDao.kt +++ b/libs/pandautils/src/main/java/com/instructure/pandautils/room/common/daos/AttachmentDao.kt @@ -1,7 +1,7 @@ -package com.instructure.pandautils.room.appdatabase.daos +package com.instructure.pandautils.room.common.daos import androidx.room.* -import com.instructure.pandautils.room.appdatabase.entities.AttachmentEntity +import com.instructure.pandautils.room.common.entities.AttachmentEntity @Dao interface AttachmentDao { diff --git a/libs/pandautils/src/main/java/com/instructure/pandautils/room/appdatabase/daos/AuthorDao.kt b/libs/pandautils/src/main/java/com/instructure/pandautils/room/common/daos/AuthorDao.kt similarity index 67% rename from libs/pandautils/src/main/java/com/instructure/pandautils/room/appdatabase/daos/AuthorDao.kt rename to libs/pandautils/src/main/java/com/instructure/pandautils/room/common/daos/AuthorDao.kt index 629f056120..db2cd5ac8c 100644 --- a/libs/pandautils/src/main/java/com/instructure/pandautils/room/appdatabase/daos/AuthorDao.kt +++ b/libs/pandautils/src/main/java/com/instructure/pandautils/room/common/daos/AuthorDao.kt @@ -1,7 +1,7 @@ -package com.instructure.pandautils.room.appdatabase.daos +package com.instructure.pandautils.room.common.daos import androidx.room.* -import com.instructure.pandautils.room.appdatabase.entities.AuthorEntity +import com.instructure.pandautils.room.common.entities.AuthorEntity @Dao interface AuthorDao { diff --git a/libs/pandautils/src/main/java/com/instructure/pandautils/room/appdatabase/daos/MediaCommentDao.kt b/libs/pandautils/src/main/java/com/instructure/pandautils/room/common/daos/MediaCommentDao.kt similarity index 72% rename from libs/pandautils/src/main/java/com/instructure/pandautils/room/appdatabase/daos/MediaCommentDao.kt rename to libs/pandautils/src/main/java/com/instructure/pandautils/room/common/daos/MediaCommentDao.kt index 615818d205..f10912ce14 100644 --- a/libs/pandautils/src/main/java/com/instructure/pandautils/room/appdatabase/daos/MediaCommentDao.kt +++ b/libs/pandautils/src/main/java/com/instructure/pandautils/room/common/daos/MediaCommentDao.kt @@ -1,10 +1,10 @@ -package com.instructure.pandautils.room.appdatabase.daos +package com.instructure.pandautils.room.common.daos import androidx.room.Dao import androidx.room.Delete import androidx.room.Insert import androidx.room.Update -import com.instructure.pandautils.room.appdatabase.entities.MediaCommentEntity +import com.instructure.pandautils.room.common.entities.MediaCommentEntity @Dao interface MediaCommentDao { diff --git a/libs/pandautils/src/main/java/com/instructure/pandautils/room/appdatabase/daos/SubmissionCommentDao.kt b/libs/pandautils/src/main/java/com/instructure/pandautils/room/common/daos/SubmissionCommentDao.kt similarity index 66% rename from libs/pandautils/src/main/java/com/instructure/pandautils/room/appdatabase/daos/SubmissionCommentDao.kt rename to libs/pandautils/src/main/java/com/instructure/pandautils/room/common/daos/SubmissionCommentDao.kt index 0286854878..075ce58bb8 100644 --- a/libs/pandautils/src/main/java/com/instructure/pandautils/room/appdatabase/daos/SubmissionCommentDao.kt +++ b/libs/pandautils/src/main/java/com/instructure/pandautils/room/common/daos/SubmissionCommentDao.kt @@ -1,8 +1,8 @@ -package com.instructure.pandautils.room.appdatabase.daos +package com.instructure.pandautils.room.common.daos import androidx.room.* -import com.instructure.pandautils.room.appdatabase.entities.SubmissionCommentEntity -import com.instructure.pandautils.room.appdatabase.model.SubmissionCommentWithAttachments +import com.instructure.pandautils.room.common.entities.SubmissionCommentEntity +import com.instructure.pandautils.room.common.model.SubmissionCommentWithAttachments @Dao interface SubmissionCommentDao { diff --git a/libs/pandautils/src/main/java/com/instructure/pandautils/room/appdatabase/entities/AttachmentEntity.kt b/libs/pandautils/src/main/java/com/instructure/pandautils/room/common/entities/AttachmentEntity.kt similarity index 90% rename from libs/pandautils/src/main/java/com/instructure/pandautils/room/appdatabase/entities/AttachmentEntity.kt rename to libs/pandautils/src/main/java/com/instructure/pandautils/room/common/entities/AttachmentEntity.kt index 97ba2e503d..e3f7333dfa 100644 --- a/libs/pandautils/src/main/java/com/instructure/pandautils/room/appdatabase/entities/AttachmentEntity.kt +++ b/libs/pandautils/src/main/java/com/instructure/pandautils/room/common/entities/AttachmentEntity.kt @@ -1,4 +1,4 @@ -package com.instructure.pandautils.room.appdatabase.entities +package com.instructure.pandautils.room.common.entities import androidx.room.Entity import androidx.room.PrimaryKey @@ -19,7 +19,8 @@ data class AttachmentEntity( //Used for file upload result val workerId: String? = null, //Used for Submission comments - val submissionCommentId: Long? = null + val submissionCommentId: Long? = null, + val submissionId: Long? = null ) { constructor(attachment: Attachment, workerId: String? = null, submissionCommentId: Long? = null) : this( attachment.id, diff --git a/libs/pandautils/src/main/java/com/instructure/pandautils/room/appdatabase/entities/AuthorEntity.kt b/libs/pandautils/src/main/java/com/instructure/pandautils/room/common/entities/AuthorEntity.kt similarity index 91% rename from libs/pandautils/src/main/java/com/instructure/pandautils/room/appdatabase/entities/AuthorEntity.kt rename to libs/pandautils/src/main/java/com/instructure/pandautils/room/common/entities/AuthorEntity.kt index 81fbda4f72..43d0780318 100644 --- a/libs/pandautils/src/main/java/com/instructure/pandautils/room/appdatabase/entities/AuthorEntity.kt +++ b/libs/pandautils/src/main/java/com/instructure/pandautils/room/common/entities/AuthorEntity.kt @@ -1,4 +1,4 @@ -package com.instructure.pandautils.room.appdatabase.entities +package com.instructure.pandautils.room.common.entities import androidx.room.Entity import androidx.room.PrimaryKey diff --git a/libs/pandautils/src/main/java/com/instructure/pandautils/room/appdatabase/entities/MediaCommentEntity.kt b/libs/pandautils/src/main/java/com/instructure/pandautils/room/common/entities/MediaCommentEntity.kt similarity index 92% rename from libs/pandautils/src/main/java/com/instructure/pandautils/room/appdatabase/entities/MediaCommentEntity.kt rename to libs/pandautils/src/main/java/com/instructure/pandautils/room/common/entities/MediaCommentEntity.kt index a8a982385e..2a6de585ac 100644 --- a/libs/pandautils/src/main/java/com/instructure/pandautils/room/appdatabase/entities/MediaCommentEntity.kt +++ b/libs/pandautils/src/main/java/com/instructure/pandautils/room/common/entities/MediaCommentEntity.kt @@ -1,4 +1,4 @@ -package com.instructure.pandautils.room.appdatabase.entities +package com.instructure.pandautils.room.common.entities import androidx.room.Entity import androidx.room.PrimaryKey diff --git a/libs/pandautils/src/main/java/com/instructure/pandautils/room/appdatabase/entities/SubmissionCommentEntity.kt b/libs/pandautils/src/main/java/com/instructure/pandautils/room/common/entities/SubmissionCommentEntity.kt similarity index 86% rename from libs/pandautils/src/main/java/com/instructure/pandautils/room/appdatabase/entities/SubmissionCommentEntity.kt rename to libs/pandautils/src/main/java/com/instructure/pandautils/room/common/entities/SubmissionCommentEntity.kt index ebf984b4a5..6e477b4e19 100644 --- a/libs/pandautils/src/main/java/com/instructure/pandautils/room/appdatabase/entities/SubmissionCommentEntity.kt +++ b/libs/pandautils/src/main/java/com/instructure/pandautils/room/common/entities/SubmissionCommentEntity.kt @@ -1,4 +1,4 @@ -package com.instructure.pandautils.room.appdatabase.entities +package com.instructure.pandautils.room.common.entities import androidx.room.Entity import androidx.room.PrimaryKey @@ -14,7 +14,8 @@ data class SubmissionCommentEntity( val comment: String? = null, val createdAt: Date? = null, val mediaCommentId: String? = null, - val attemptId: Long? = null + val attemptId: Long? = null, + val submissionId: Long? = null ) { constructor(submissionComment: SubmissionComment): this( submissionComment.id, diff --git a/libs/pandautils/src/main/java/com/instructure/pandautils/room/appdatabase/model/SubmissionCommentWithAttachments.kt b/libs/pandautils/src/main/java/com/instructure/pandautils/room/common/model/SubmissionCommentWithAttachments.kt similarity index 76% rename from libs/pandautils/src/main/java/com/instructure/pandautils/room/appdatabase/model/SubmissionCommentWithAttachments.kt rename to libs/pandautils/src/main/java/com/instructure/pandautils/room/common/model/SubmissionCommentWithAttachments.kt index 40bd4de657..618ee58b54 100644 --- a/libs/pandautils/src/main/java/com/instructure/pandautils/room/appdatabase/model/SubmissionCommentWithAttachments.kt +++ b/libs/pandautils/src/main/java/com/instructure/pandautils/room/common/model/SubmissionCommentWithAttachments.kt @@ -1,12 +1,12 @@ -package com.instructure.pandautils.room.appdatabase.model +package com.instructure.pandautils.room.common.model import androidx.room.Embedded import androidx.room.Relation import com.instructure.canvasapi2.models.SubmissionComment -import com.instructure.pandautils.room.appdatabase.entities.AttachmentEntity -import com.instructure.pandautils.room.appdatabase.entities.AuthorEntity -import com.instructure.pandautils.room.appdatabase.entities.MediaCommentEntity -import com.instructure.pandautils.room.appdatabase.entities.SubmissionCommentEntity +import com.instructure.pandautils.room.common.entities.AttachmentEntity +import com.instructure.pandautils.room.common.entities.AuthorEntity +import com.instructure.pandautils.room.common.entities.MediaCommentEntity +import com.instructure.pandautils.room.common.entities.SubmissionCommentEntity data class SubmissionCommentWithAttachments( @Embedded diff --git a/libs/pandautils/src/test/java/com/instructure/pandautils/features/dashboard/notifications/DashboardNotificationsViewModelTest.kt b/libs/pandautils/src/test/java/com/instructure/pandautils/features/dashboard/notifications/DashboardNotificationsViewModelTest.kt index 4c048de31d..0c5ab21201 100644 --- a/libs/pandautils/src/test/java/com/instructure/pandautils/features/dashboard/notifications/DashboardNotificationsViewModelTest.kt +++ b/libs/pandautils/src/test/java/com/instructure/pandautils/features/dashboard/notifications/DashboardNotificationsViewModelTest.kt @@ -121,7 +121,7 @@ class DashboardNotificationsViewModelTest { every { apiPrefs.user } returns User(id = 1) uploadsLiveData = MutableLiveData(emptyList()) - every { dashboardFileUploadDao.getAll(1) } returns uploadsLiveData + every { dashboardFileUploadDao.getAllForUser(1) } returns uploadsLiveData viewModel = DashboardNotificationsViewModel( resources, From bb285177bd41c4beae6993ed21704a73705c1000 Mon Sep 17 00:00:00 2001 From: Kristof Nemere <109959688+kristofnemere@users.noreply.github.com> Date: Wed, 24 May 2023 10:16:54 +0200 Subject: [PATCH 042/103] [MBL-16652][Teacher] Show 'exceeded storage quota' toast when file upload storage is full refs: MBL-16652 affects: Teacher release note: Implemented storage quota limit error. --- .../activities/BaseAppCompatActivity.kt | 26 ++++++++++++++++--- 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/apps/teacher/src/main/java/com/instructure/teacher/activities/BaseAppCompatActivity.kt b/apps/teacher/src/main/java/com/instructure/teacher/activities/BaseAppCompatActivity.kt index 55f3eeb5f0..17ed4cf8c3 100644 --- a/apps/teacher/src/main/java/com/instructure/teacher/activities/BaseAppCompatActivity.kt +++ b/apps/teacher/src/main/java/com/instructure/teacher/activities/BaseAppCompatActivity.kt @@ -20,13 +20,15 @@ package com.instructure.teacher.activities import android.content.Intent import androidx.appcompat.app.AppCompatActivity import androidx.core.app.ActivityCompat -import com.instructure.pandautils.utils.ActivityResult -import com.instructure.pandautils.utils.OnActivityResults -import com.instructure.pandautils.utils.PermissionReceiver +import com.instructure.canvasapi2.models.StorageQuotaExceededError +import com.instructure.pandautils.utils.* import com.instructure.pandautils.utils.RequestCodes.CAMERA_PIC_REQUEST import com.instructure.pandautils.utils.RequestCodes.PICK_FILE_FROM_DEVICE import com.instructure.pandautils.utils.RequestCodes.PICK_IMAGE_GALLERY -import com.instructure.pandautils.utils.postSticky +import com.instructure.teacher.R +import org.greenrobot.eventbus.EventBus +import org.greenrobot.eventbus.Subscribe +import org.greenrobot.eventbus.ThreadMode @Suppress("DELEGATED_MEMBER_HIDES_SUPERTYPE_OVERRIDE") abstract class BaseAppCompatActivity : AppCompatActivity(), @@ -42,4 +44,20 @@ abstract class BaseAppCompatActivity : AppCompatActivity(), OnActivityResults(ActivityResult(requestCode, resultCode, data), null).postSticky() } } + + override fun onStart() { + super.onStart() + EventBus.getDefault().register(this) + } + + override fun onStop() { + super.onStop() + EventBus.getDefault().unregister(this) + } + + @Suppress("unused", "UNUSED_PARAMETER") + @Subscribe(threadMode = ThreadMode.MAIN) + fun onQuotaExceeded(errorCode: StorageQuotaExceededError) { + toast(R.string.fileQuotaExceeded) + } } From d5b3cd37ac5a6db091476146629a244e06a911b1 Mon Sep 17 00:00:00 2001 From: Tamas Kozmer <72397075+tamaskozmer@users.noreply.github.com> Date: Wed, 24 May 2023 15:52:44 +0200 Subject: [PATCH 043/103] [MBL-16807][Student][Teacher] Set up library integration tests on Bitrise #1995 refs: MBL-16807 affects: Student, Teacher release note: none --- libs/pandautils/build.gradle | 1 + libs/pandautils/flank.yml | 21 +++++++++++++++++++++ 2 files changed, 22 insertions(+) create mode 100644 libs/pandautils/flank.yml diff --git a/libs/pandautils/build.gradle b/libs/pandautils/build.gradle index f629e8248e..e0adde4efa 100644 --- a/libs/pandautils/build.gradle +++ b/libs/pandautils/build.gradle @@ -38,6 +38,7 @@ android { targetSdkVersion Versions.TARGET_SDK buildConfigField "boolean", "IS_TESTING", isTesting() testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + testApplicationId System.getProperty("testApplicationId", "com.instructure.pandautils.test") javaCompileOptions { annotationProcessorOptions { compilerArgumentProviders( diff --git a/libs/pandautils/flank.yml b/libs/pandautils/flank.yml new file mode 100644 index 0000000000..48bcce337e --- /dev/null +++ b/libs/pandautils/flank.yml @@ -0,0 +1,21 @@ +gcloud: + project: delta-essence-114723 + app: ./libs/pandautils/pandautils-app.apk + test: ./libs/pandautils/pandautils-test.apk + results-bucket: android-pandautils + auto-google-login: true + use-orchestrator: true + performance-metrics: false + timeout: 60m + test-targets: + - notAnnotation com.instructure.canvas.espresso.E2E, com.instructure.canvas.espresso.Stub, com.instructure.canvas.espresso.FlakyE2E, com.instructure.canvas.espresso.KnownBug + device: + - model: Nexus6P + version: 26 + locale: en_US + orientation: portrait + +flank: + testShards: 10 + testRuns: 1 + From adcd7cbf174b7b30f771099a83db8f741f828b0d Mon Sep 17 00:00:00 2001 From: Tamas Kozmer <72397075+tamaskozmer@users.noreply.github.com> Date: Wed, 24 May 2023 16:22:19 +0200 Subject: [PATCH 044/103] [MBL-16620][Student][Teacher] Update PSPDFKit (#1992) refs: MBL-16620 affects: Student, Teacher release note: Design changes on view PDF screen (Student only) * Updated dependencies. Fixed crashes after update. Fixed styles and refactored grouping rule for Student PDF view screen. * Fixed toolbar menu items. --- .../student/activity/CandroidPSPDFActivity.kt | 12 +- .../com/instructure/student/util/FileUtils.kt | 1 - .../src/main/res/menu/pspdf_activity_menu.xml | 18 +- .../src/main/res/values-night/styles.xml | 2 + apps/student/src/main/res/values/styles.xml | 4 +- buildSrc/src/main/java/GlobalDependencies.kt | 2 +- .../annotations/CanvasPdfMenuGrouping.kt | 192 ++++++++---------- 7 files changed, 100 insertions(+), 131 deletions(-) diff --git a/apps/student/src/main/java/com/instructure/student/activity/CandroidPSPDFActivity.kt b/apps/student/src/main/java/com/instructure/student/activity/CandroidPSPDFActivity.kt index 8d5f1f8e66..bee62d8765 100644 --- a/apps/student/src/main/java/com/instructure/student/activity/CandroidPSPDFActivity.kt +++ b/apps/student/src/main/java/com/instructure/student/activity/CandroidPSPDFActivity.kt @@ -95,17 +95,21 @@ class CandroidPSPDFActivity : PdfActivity(), ToolbarCoordinatorLayout.OnContextu ViewStyler.setStatusBarDark(this, color) } - override fun onPrepareOptionsMenu(menu: Menu): Boolean { - super.onPrepareOptionsMenu(menu) + override fun onCreateOptionsMenu(menu: Menu?): Boolean { + super.onCreateOptionsMenu(menu) menuInflater.inflate(R.menu.pspdf_activity_menu, menu) if (submissionTarget != null) { // If targeted for submission, change the menu item title from "Upload to Canvas" to "Submit Assignment" - val item = menu.findItem(R.id.upload_item) - item.title = getString(R.string.submitAssignment) + val item = menu?.findItem(R.id.upload_item) + item?.title = getString(R.string.submitAssignment) } return true } + override fun onGetShowAsAction(menuItemId: Int, defaultShowAsAction: Int): Int { + return if (menuItemId != R.id.upload_item) MenuItem.SHOW_AS_ACTION_ALWAYS else super.onGetShowAsAction(menuItemId, defaultShowAsAction) + } + override fun onOptionsItemSelected(item: MenuItem): Boolean { super.onOptionsItemSelected(item) return if (item.itemId == R.id.upload_item) { diff --git a/apps/student/src/main/java/com/instructure/student/util/FileUtils.kt b/apps/student/src/main/java/com/instructure/student/util/FileUtils.kt index c50a961d90..73fb0b1066 100644 --- a/apps/student/src/main/java/com/instructure/student/util/FileUtils.kt +++ b/apps/student/src/main/java/com/instructure/student/util/FileUtils.kt @@ -85,7 +85,6 @@ object FileUtils { .scrollDirection(PageScrollDirection.HORIZONTAL) .showThumbnailGrid() .setDocumentInfoViewSeparated(false) - .setThumbnailBarMode(ThumbnailBarMode.THUMBNAIL_BAR_MODE_PINNED) .enableDocumentEditor() .enabledAnnotationTools(annotationCreationList) .editableAnnotationTypes(annotationEditList) diff --git a/apps/student/src/main/res/menu/pspdf_activity_menu.xml b/apps/student/src/main/res/menu/pspdf_activity_menu.xml index fc60fbaa75..ecadf9f40f 100644 --- a/apps/student/src/main/res/menu/pspdf_activity_menu.xml +++ b/apps/student/src/main/res/menu/pspdf_activity_menu.xml @@ -1,5 +1,4 @@ - - -