From 109586e6eeb6be737e2de465ff22dc9488ea7d59 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=C3=81kos=20Hermann?=
Date: Fri, 16 Sep 2022 11:54:57 +0200
Subject: [PATCH 01/73] version bump
---
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 3fc93ed845..10be6c70f8 100644
--- a/apps/student/build.gradle
+++ b/apps/student/build.gradle
@@ -54,8 +54,8 @@ android {
applicationId "com.instructure.candroid"
minSdkVersion Versions.MIN_SDK
targetSdkVersion Versions.TARGET_SDK
- versionCode = 242
- versionName = '6.19.0'
+ versionCode = 243
+ versionName = '6.20.0'
vectorDrawables.useSupportLibrary = true
multiDexEnabled = true
From 9979c2f2060ae9ada53d49318bac61ed4b5283bf Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=C3=81kos=20Hermann?=
Date: Mon, 19 Sep 2022 13:48:00 +0200
Subject: [PATCH 02/73] fix color
---
.../features/shareextension/ShareExtensionActivity.kt | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/libs/pandautils/src/main/java/com/instructure/pandautils/features/shareextension/ShareExtensionActivity.kt b/libs/pandautils/src/main/java/com/instructure/pandautils/features/shareextension/ShareExtensionActivity.kt
index d1c073a9ce..eddd862fa4 100644
--- a/libs/pandautils/src/main/java/com/instructure/pandautils/features/shareextension/ShareExtensionActivity.kt
+++ b/libs/pandautils/src/main/java/com/instructure/pandautils/features/shareextension/ShareExtensionActivity.kt
@@ -226,7 +226,7 @@ abstract class ShareExtensionActivity : AppCompatActivity() {
ViewStyler.setStatusBarDark(this, color)
color
} else {
- val color = ContextCompat.getColor(this, R.color.login_studentAppTheme)
+ val color = ContextCompat.getColor(this, R.color.studentDocumentSharingColor)
ViewStyler.setStatusBarDark(this, color)
color
}
From e6caf6b2f4b071d4e55ee4782dd1913ac481b020 Mon Sep 17 00:00:00 2001
From: Kristof Deak <92309696+kdeakinstructure@users.noreply.github.com>
Date: Thu, 22 Sep 2022 14:08:26 +0200
Subject: [PATCH 03/73] [MBL-14427][Student] - Investigate collaborations e2e
failures on FTL (#1719)
* dummy commit to push branch to remote to try out on FTL.
* Investigate collaborations e2e test.
Picked up a ticket for VICE team (VICE-3157) because this test catches a screen size specific bug. (This will be breaking until they fix it)
* Put KnownBug annotation to Collaborations E2E test until it has been fixed.
---
.../student/ui/e2e/CollaborationsE2ETest.kt | 26 +++++++++----------
.../student/ui/pages/CollaborationsPage.kt | 13 +++-------
2 files changed, 16 insertions(+), 23 deletions(-)
diff --git a/apps/student/src/androidTest/java/com/instructure/student/ui/e2e/CollaborationsE2ETest.kt b/apps/student/src/androidTest/java/com/instructure/student/ui/e2e/CollaborationsE2ETest.kt
index 323fee40d5..6676a8a364 100644
--- a/apps/student/src/androidTest/java/com/instructure/student/ui/e2e/CollaborationsE2ETest.kt
+++ b/apps/student/src/androidTest/java/com/instructure/student/ui/e2e/CollaborationsE2ETest.kt
@@ -2,6 +2,7 @@ package com.instructure.student.ui.e2e
import android.util.Log
import com.instructure.canvas.espresso.E2E
+import com.instructure.canvas.espresso.KnownBug
import com.instructure.panda_annotations.FeatureCategory
import com.instructure.panda_annotations.Priority
import com.instructure.panda_annotations.TestCategory
@@ -13,6 +14,7 @@ import com.instructure.student.ui.utils.tokenLogin
import dagger.hilt.android.testing.HiltAndroidTest
import org.junit.Test
+
/**
* Very basic test to verify that the collaborations web page shows up correctly.
* We make no attempt to actually start a collaboration.
@@ -20,20 +22,16 @@ import org.junit.Test
*/
@HiltAndroidTest
class CollaborationsE2ETest: StudentTest() {
- override fun displaysPageObjects() {
- TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
- }
+ override fun displaysPageObjects() = Unit
- override fun enableAndConfigureAccessibilityChecks() {
- //We don't want to see accessibility errors on E2E tests
- }
+ override fun enableAndConfigureAccessibilityChecks() = Unit
@E2E
@Test
+ @KnownBug("https://instructure.atlassian.net/browse/VICE-3157")
@TestMetaData(Priority.MANDATORY, FeatureCategory.COLLABORATIONS, TestCategory.E2E)
fun testCollaborationsE2E() {
-
Log.d(PREPARATION_TAG,"Seeding data.")
val data = seedData(students = 1, teachers = 1, courses = 1)
val student = data.studentsList[0]
@@ -50,11 +48,13 @@ class CollaborationsE2ETest: StudentTest() {
Log.d(STEP_TAG,"Verify that various elements of the web page are present.")
CollaborationsPage.assertCurrentCollaborationsHeaderPresent()
- // For some reason, these aren't showing up when run in FTL, though they do
- // show up when run locally (same server environment in each). I'll comment
- // them out for now, with MBL-14427 being created to pursue the issue.
-// CollaborationsPage.assertStartANewCollaborationPresent()
-// CollaborationsPage.assertGoogleDocsChoicePresent()
-// CollaborationsPage.assertGoogleDocsExplanationPresent()
+ //On some screen size, this spinner does not displayed at all, instead of it,
+ //there is a button on the top-right corner with the 'Start a new Collaboration' text
+ //and clicking on it will 'expand' and display this spinner.
+ //However, there is a bug (see link in this @KnownBug annotation) which is about the button not displayed on some screen size
+ //So this test will breaks until it this ticket will be fixed.
+ CollaborationsPage.assertStartANewCollaborationPresent()
+ CollaborationsPage.assertGoogleDocsChoicePresent()
+ CollaborationsPage.assertGoogleDocsExplanationPresent()
}
}
\ No newline at end of file
diff --git a/apps/student/src/androidTest/java/com/instructure/student/ui/pages/CollaborationsPage.kt b/apps/student/src/androidTest/java/com/instructure/student/ui/pages/CollaborationsPage.kt
index 6f7528af03..ae3252b91b 100644
--- a/apps/student/src/androidTest/java/com/instructure/student/ui/pages/CollaborationsPage.kt
+++ b/apps/student/src/androidTest/java/com/instructure/student/ui/pages/CollaborationsPage.kt
@@ -16,17 +16,14 @@
*/
package com.instructure.student.ui.pages
-import androidx.test.espresso.Espresso.onView
-import androidx.test.espresso.action.ViewActions.swipeUp
-import androidx.test.espresso.matcher.ViewMatchers
-import androidx.test.espresso.matcher.ViewMatchers.*
+import androidx.test.espresso.matcher.ViewMatchers.isDisplayed
+import androidx.test.espresso.matcher.ViewMatchers.withId
import androidx.test.espresso.web.assertion.WebViewAssertions.webMatches
import androidx.test.espresso.web.sugar.Web
import androidx.test.espresso.web.webdriver.DriverAtoms
import androidx.test.espresso.web.webdriver.DriverAtoms.getText
import androidx.test.espresso.web.webdriver.Locator
import com.instructure.canvas.espresso.checkRepeat
-import com.instructure.canvas.espresso.withElementRepeat
import com.instructure.student.R
import org.hamcrest.Matchers
import org.hamcrest.Matchers.containsString
@@ -44,12 +41,8 @@ object CollaborationsPage {
}
fun assertStartANewCollaborationPresent() {
-
- // Debug maneuver to help see what was being displayed
- //onView(withId(R.id.canvasWebView)).perform(swipeUp())
-
Web.onWebView(Matchers.allOf(withId(R.id.canvasWebView), isDisplayed()))
- .withElement(DriverAtoms.findElement(Locator.TAG_NAME, "h2")) // lucky there is only one of these!
+ .withElement(DriverAtoms.findElement(Locator.TAG_NAME, "h2"))
.perform(DriverAtoms.webScrollIntoView())
.checkRepeat(webMatches(getText(), containsString("Start a New Collaboration") ), 30)
}
From 915d5f1112d26441101e564e692e6e9740b1320b Mon Sep 17 00:00:00 2001
From: Kristof Nemere <109959688+kristofnemere@users.noreply.github.com>
Date: Thu, 22 Sep 2022 14:12:02 +0200
Subject: [PATCH 04/73] [MBL-16237][Parent] Excused does not appear in the app
refs: MBL-16237
affects: Parent
release note: Fixed a bug where Excused label was missing.
---
.../lib/screens/courses/details/course_grades_screen.dart | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/apps/flutter_parent/lib/screens/courses/details/course_grades_screen.dart b/apps/flutter_parent/lib/screens/courses/details/course_grades_screen.dart
index 62d53352ee..bbe10a671c 100644
--- a/apps/flutter_parent/lib/screens/courses/details/course_grades_screen.dart
+++ b/apps/flutter_parent/lib/screens/courses/details/course_grades_screen.dart
@@ -371,7 +371,10 @@ class _AssignmentRow extends StatelessWidget {
final localizations = L10n(context);
final submission = assignment.submission(studentId);
- if (submission?.grade != null) {
+ if (submission?.excused ?? false) {
+ text = localizations.gradeFormatScoreOutOfPointsPossible(localizations.excused, points);
+ semantics = localizations.contentDescriptionScoreOutOfPointsPossible('', points);
+ } else if (submission?.grade != null) {
text = localizations.gradeFormatScoreOutOfPointsPossible(submission.grade, points);
semantics = localizations.contentDescriptionScoreOutOfPointsPossible(submission.grade, points);
} else {
From 658bf82da875e161d20bc902dac2e87f07245d03 Mon Sep 17 00:00:00 2001
From: Kristof Nemere <109959688+kristofnemere@users.noreply.github.com>
Date: Thu, 22 Sep 2022 14:39:07 +0200
Subject: [PATCH 05/73] [MBL-16257][Student][Teacher] Calendar events for
specific sections show for everyone in the app on the Syllabus
refs: MBL-16257
affects: Student
release note: Fixed a bug where events for specific sections show for everyone.
* Fixed calendar events for specific sections
* Fixed calendar events for specific sections in teacher too
* Added test cases to cover isHidden flag
---
.../student/mobius/syllabus/SyllabusPresenter.kt | 4 ++--
.../student/test/syllabus/SyllabusPresenterTest.kt | 11 +++++++++--
.../teacher/features/syllabus/SyllabusPresenter.kt | 2 +-
.../features/syllabus/SyllabusPresenterTest.kt | 8 ++++++++
4 files changed, 20 insertions(+), 5 deletions(-)
diff --git a/apps/student/src/main/java/com/instructure/student/mobius/syllabus/SyllabusPresenter.kt b/apps/student/src/main/java/com/instructure/student/mobius/syllabus/SyllabusPresenter.kt
index 7f97c7209e..ec8f72acb8 100644
--- a/apps/student/src/main/java/com/instructure/student/mobius/syllabus/SyllabusPresenter.kt
+++ b/apps/student/src/main/java/com/instructure/student/mobius/syllabus/SyllabusPresenter.kt
@@ -29,7 +29,7 @@ import com.instructure.student.mobius.syllabus.ui.EventsViewState
import com.instructure.student.mobius.syllabus.ui.ScheduleItemViewState
import com.instructure.student.mobius.syllabus.ui.SyllabusViewState
import com.instructure.student.util.toDueAtString
-import java.util.Date
+import java.util.*
object SyllabusPresenter : Presenter {
override fun present(model: SyllabusModel, context: Context): SyllabusViewState {
@@ -56,7 +56,7 @@ object SyllabusPresenter : Presenter {
eventsResult.isFail -> EventsViewState.Error
eventsResult.dataOrNull.isNullOrEmpty() -> EventsViewState.Empty
else -> {
- EventsViewState.Loaded(eventsResult.dataOrThrow.map {
+ EventsViewState.Loaded(eventsResult.dataOrThrow.filter { it.isHidden.not() }.map {
ScheduleItemViewState(
it.itemId,
it.title ?: "",
diff --git a/apps/student/src/test/java/com/instructure/student/test/syllabus/SyllabusPresenterTest.kt b/apps/student/src/test/java/com/instructure/student/test/syllabus/SyllabusPresenterTest.kt
index fa77647763..81cbf3bf85 100644
--- a/apps/student/src/test/java/com/instructure/student/test/syllabus/SyllabusPresenterTest.kt
+++ b/apps/student/src/test/java/com/instructure/student/test/syllabus/SyllabusPresenterTest.kt
@@ -39,8 +39,7 @@ import org.threeten.bp.DateTimeUtils
import org.threeten.bp.OffsetDateTime
import org.threeten.bp.ZoneId
import org.threeten.bp.format.DateTimeFormatter
-import java.util.Calendar
-import java.util.Date
+import java.util.*
@RunWith(AndroidJUnit4::class)
class SyllabusPresenterTest : Assert() {
@@ -79,6 +78,14 @@ class SyllabusPresenterTest : Assert() {
assignment = Assignment(id = 125L, submissionTypesRaw = listOf("discussion_topic")),
startAt = null,
itemType = ScheduleItem.Type.TYPE_ASSIGNMENT
+ ),
+ ScheduleItem(
+ itemId = "4",
+ title = "discussion",
+ assignment = Assignment(id = 126L, submissionTypesRaw = listOf("discussion_topic")),
+ startAt = null,
+ itemType = ScheduleItem.Type.TYPE_ASSIGNMENT,
+ isHidden = true
)
)
private lateinit var baseEventsViewState: List
diff --git a/apps/teacher/src/main/java/com/instructure/teacher/features/syllabus/SyllabusPresenter.kt b/apps/teacher/src/main/java/com/instructure/teacher/features/syllabus/SyllabusPresenter.kt
index 8c58ced7d7..19f3711028 100644
--- a/apps/teacher/src/main/java/com/instructure/teacher/features/syllabus/SyllabusPresenter.kt
+++ b/apps/teacher/src/main/java/com/instructure/teacher/features/syllabus/SyllabusPresenter.kt
@@ -67,7 +67,7 @@ class SyllabusPresenter : Presenter {
}
private fun createLoadedEvents(eventsResult: DataResult>, context: Context, color: Int): EventsViewState.Loaded {
- return EventsViewState.Loaded(eventsResult.dataOrThrow.map {
+ return EventsViewState.Loaded(eventsResult.dataOrThrow.filter { it.isHidden.not() }.map {
ScheduleItemViewState(
it.itemId,
it.title ?: "",
diff --git a/apps/teacher/src/test/java/com/instructure/teacher/features/syllabus/SyllabusPresenterTest.kt b/apps/teacher/src/test/java/com/instructure/teacher/features/syllabus/SyllabusPresenterTest.kt
index 91da53c104..9c71e19974 100644
--- a/apps/teacher/src/test/java/com/instructure/teacher/features/syllabus/SyllabusPresenterTest.kt
+++ b/apps/teacher/src/test/java/com/instructure/teacher/features/syllabus/SyllabusPresenterTest.kt
@@ -79,6 +79,14 @@ class SyllabusPresenterTest {
assignment = Assignment(id = 125L, submissionTypesRaw = listOf("discussion_topic")),
startAt = null,
itemType = ScheduleItem.Type.TYPE_ASSIGNMENT
+ ),
+ ScheduleItem(
+ itemId = "4",
+ title = "discussion",
+ assignment = Assignment(id = 126L, submissionTypesRaw = listOf("discussion_topic")),
+ startAt = null,
+ itemType = ScheduleItem.Type.TYPE_ASSIGNMENT,
+ isHidden = true
)
)
From e32bb48c3f1d58f4a3a75a65431e007ed167d17b Mon Sep 17 00:00:00 2001
From: Tamas Kozmer <72397075+tamaskozmer@users.noreply.github.com>
Date: Thu, 22 Sep 2022 14:46:05 +0200
Subject: [PATCH 06/73] [MBL-16247][Parent] App repetitively crashes on
attachment download
refs: MBL-16247
affects: Parent
release note: Fixed a crash when downloading inbox attachments.
---
.../android/app/src/main/AndroidManifest.xml | 10 ++++++++++
apps/flutter_parent/lib/main.dart | 11 +++++++++++
.../lib/utils/veneers/flutter_downloader_veneer.dart | 5 +++--
apps/flutter_parent/pubspec.lock | 2 +-
apps/flutter_parent/pubspec.yaml | 2 +-
5 files changed, 26 insertions(+), 4 deletions(-)
diff --git a/apps/flutter_parent/android/app/src/main/AndroidManifest.xml b/apps/flutter_parent/android/app/src/main/AndroidManifest.xml
index 88c5a4cae2..f4245b5921 100644
--- a/apps/flutter_parent/android/app/src/main/AndroidManifest.xml
+++ b/apps/flutter_parent/android/app/src/main/AndroidManifest.xml
@@ -89,5 +89,15 @@
+
+
+
+
diff --git a/apps/flutter_parent/lib/main.dart b/apps/flutter_parent/lib/main.dart
index 1140648ed2..102b50fc81 100644
--- a/apps/flutter_parent/lib/main.dart
+++ b/apps/flutter_parent/lib/main.dart
@@ -14,6 +14,8 @@
import 'dart:async';
import 'dart:io';
+import 'dart:isolate';
+import 'dart:ui';
import 'package:device_info/device_info.dart';
import 'package:firebase_core/firebase_core.dart';
@@ -49,6 +51,8 @@ void main() async {
]);
PandaRouter.init();
+ await FlutterDownloader.registerCallback(downloadCallback);
+
// This completer waits for the app to be built before allowing the notificationUtil to handle notifications
final Completer _appCompleter = Completer();
NotificationUtil.init(_appCompleter);
@@ -68,3 +72,10 @@ void main() async {
runApp(ParentApp(_appCompleter));
}, FirebaseCrashlytics.instance.recordError);
}
+
+@pragma('vm:entry-point')
+void downloadCallback(String id, DownloadTaskStatus status, int progress) {
+ final SendPort send =
+ IsolateNameServer.lookupPortByName('downloader_send_port');
+ send.send([id, status, progress]);
+}
diff --git a/apps/flutter_parent/lib/utils/veneers/flutter_downloader_veneer.dart b/apps/flutter_parent/lib/utils/veneers/flutter_downloader_veneer.dart
index 7933ed64db..1fdd0c3ced 100644
--- a/apps/flutter_parent/lib/utils/veneers/flutter_downloader_veneer.dart
+++ b/apps/flutter_parent/lib/utils/veneers/flutter_downloader_veneer.dart
@@ -24,7 +24,7 @@ class FlutterDownloaderVeneer {
bool showNotification = true,
bool openFileFromNotification = true,
bool requiresStorageNotLow = true,
- }) =>
+ bool saveInPublicStorage = true}) =>
FlutterDownloader.enqueue(
url: url,
savedDir: savedDir,
@@ -32,7 +32,8 @@ class FlutterDownloaderVeneer {
headers: headers,
showNotification: showNotification,
openFileFromNotification: openFileFromNotification,
- requiresStorageNotLow: requiresStorageNotLow);
+ requiresStorageNotLow: requiresStorageNotLow,
+ saveInPublicStorage: saveInPublicStorage);
Future> loadTasks() => FlutterDownloader.loadTasks();
diff --git a/apps/flutter_parent/pubspec.lock b/apps/flutter_parent/pubspec.lock
index fc0fa4e97d..5f436e3e84 100644
--- a/apps/flutter_parent/pubspec.lock
+++ b/apps/flutter_parent/pubspec.lock
@@ -425,7 +425,7 @@ packages:
name: flutter_downloader
url: "https://pub.dartlang.org"
source: hosted
- version: "1.7.1"
+ version: "1.7.4"
flutter_driver:
dependency: "direct dev"
description: flutter
diff --git a/apps/flutter_parent/pubspec.yaml b/apps/flutter_parent/pubspec.yaml
index 0b2485a37e..3e630126ce 100644
--- a/apps/flutter_parent/pubspec.yaml
+++ b/apps/flutter_parent/pubspec.yaml
@@ -58,7 +58,7 @@ dependencies:
# File handling
path_provider: ^2.0.6
- flutter_downloader: ^1.7.1
+ flutter_downloader: 1.7.4
mime: ^1.0.1
file_picker: ^4.2.0
From c4e3538d610bf1db0e66f6b6c66db92f1fd3662c Mon Sep 17 00:00:00 2001
From: Kristof Nemere <109959688+kristofnemere@users.noreply.github.com>
Date: Fri, 23 Sep 2022 11:58:53 +0200
Subject: [PATCH 07/73] [MBL-15647][Student][Teacher] Image loading library
dependency update
refs: MBL-15647
affects: Student, Teacher
release note: none
* WIP: Remove unnecessary image loading libraries
* WIP: image library dependecy update
* WIP: image library dependecy update
* WIP: image library dependency update
* Fixed comments
* minor changes
* Fixed border issue
* Fixed border issue
---
.../student/activity/NavigationActivity.kt | 18 +---
.../student/fragment/PeopleDetailsFragment.kt | 2 +-
.../student/holders/InboxMessageHolder.kt | 4 +-
.../student/holders/PeopleViewHolder.kt | 7 +-
.../student/holders/RecipientViewHolder.kt | 7 +-
.../drawer/comments/ui/views/CommentView.kt | 2 +-
.../student/view/AttachmentView.kt | 37 +++------
.../main/res/layout/dialog_file_upload.xml | 5 +-
.../layout/fragment_assignment_details.xml | 2 +-
.../layout/fragment_discussions_details.xml | 2 +-
.../res/layout/fragment_people_details.xml | 7 +-
.../src/main/res/layout/navigation_drawer.xml | 5 +-
.../src/main/res/layout/view_comment.xml | 2 +-
.../src/main/res/layout/viewholder_inbox.xml | 2 +-
.../main/res/layout/viewholder_message.xml | 2 +-
.../src/main/res/layout/viewholder_people.xml | 5 +-
.../main/res/layout/viewholder_recipient.xml | 6 +-
.../teacher/activities/InitActivity.kt | 18 +---
.../teacher/binders/MessageBinder.kt | 2 +-
.../instructure/teacher/binders/UserBinder.kt | 2 +-
.../teacher/fragments/ProfileEditFragment.kt | 28 +------
.../teacher/fragments/ProfileFragment.kt | 26 +-----
.../teacher/holders/AssigneeViewHolder.kt | 31 ++-----
.../teacher/holders/AttendanceViewHolder.kt | 2 +-
.../teacher/holders/RecipientViewHolder.kt | 7 +-
.../teacher/utils/ViewExtensions.kt | 6 +-
.../instructure/teacher/view/CommentView.kt | 2 +-
.../view/FileFolderPublishedStatusIconView.kt | 8 +-
.../view_submission_content.xml | 2 +-
.../src/main/res/layout/adapter_assignee.xml | 10 ++-
.../main/res/layout/adapter_attendance.xml | 2 +-
.../adapter_gradeable_student_submission.xml | 2 +-
.../src/main/res/layout/adapter_inbox.xml | 2 +-
.../src/main/res/layout/adapter_message.xml | 2 +-
.../adapter_speed_grader_group_member.xml | 2 +-
.../src/main/res/layout/adapter_users.xml | 2 +-
.../layout/fragment_discussions_details.xml | 2 +-
.../src/main/res/layout/fragment_profile.xml | 9 +-
.../main/res/layout/fragment_profile_edit.xml | 9 +-
.../res/layout/fragment_student_context.xml | 2 +-
.../src/main/res/layout/navigation_drawer.xml | 5 +-
.../src/main/res/layout/view_comment.xml | 2 +-
.../res/layout/view_submission_content.xml | 2 +-
.../main/res/layout/viewholder_recipient.xml | 6 +-
.../login/adapter/PreviousUsersAdapter.kt | 5 +-
.../res/layout/adapter_previous_users.xml | 8 +-
.../drawable/avatar_circular_border_thick.xml | 6 ++
.../drawable/avatar_circular_border_thin.xml | 6 ++
libs/pandares/src/main/res/values/dimens.xml | 2 +
libs/pandautils/build.gradle | 4 -
.../pandautils/utils/ProfileUtils.kt | 83 ++++---------------
.../pandautils/utils/ViewExtensions.kt | 69 ++++++++++-----
.../pandautils/views/AttachmentView.kt | 35 +++-----
.../adapter_recipient_search_result.xml | 2 +-
54 files changed, 198 insertions(+), 330 deletions(-)
create mode 100644 libs/pandares/src/main/res/drawable/avatar_circular_border_thick.xml
create mode 100644 libs/pandares/src/main/res/drawable/avatar_circular_border_thin.xml
diff --git a/apps/student/src/main/java/com/instructure/student/activity/NavigationActivity.kt b/apps/student/src/main/java/com/instructure/student/activity/NavigationActivity.kt
index ed14703185..c5311a36c8 100644
--- a/apps/student/src/main/java/com/instructure/student/activity/NavigationActivity.kt
+++ b/apps/student/src/main/java/com/instructure/student/activity/NavigationActivity.kt
@@ -419,23 +419,7 @@ class NavigationActivity : BaseRouterActivity(), Navigation, MasqueradingDialog.
if (user != null) {
navigationDrawerUserName.text = Pronouns.span(user.shortName, user.pronouns)
navigationDrawerUserEmail.text = user.primaryEmail
-
- if(ProfileUtils.shouldLoadAltAvatarImage(user.avatarUrl)) {
- val initials = ProfileUtils.getUserInitials(user.shortName ?: "")
- val color = ContextCompat.getColor(context, R.color.textDark)
- val drawable = TextDrawable.builder()
- .beginConfig()
- .height(context.resources.getDimensionPixelSize(R.dimen.profileAvatarSize))
- .width(context.resources.getDimensionPixelSize(R.dimen.profileAvatarSize))
- .toUpperCase()
- .useFont(Typeface.DEFAULT_BOLD)
- .textColor(color)
- .endConfig()
- .buildRound(initials, Color.WHITE)
- navigationDrawerProfileImage.setImageDrawable(drawable)
- } else {
- Glide.with(context).load(user.avatarUrl).into(navigationDrawerProfileImage)
- }
+ ProfileUtils.loadAvatarForUser(navigationDrawerProfileImage, user.shortName, user.avatarUrl)
}
}
diff --git a/apps/student/src/main/java/com/instructure/student/fragment/PeopleDetailsFragment.kt b/apps/student/src/main/java/com/instructure/student/fragment/PeopleDetailsFragment.kt
index 80d28715af..27921afa79 100644
--- a/apps/student/src/main/java/com/instructure/student/fragment/PeopleDetailsFragment.kt
+++ b/apps/student/src/main/java/com/instructure/student/fragment/PeopleDetailsFragment.kt
@@ -116,7 +116,7 @@ class PeopleDetailsFragment : ParentFragment(), Bookmarkable {
private fun setupUserViews() {
user?.let { u ->
- ProfileUtils.loadAvatarForUser(avatar, u)
+ ProfileUtils.loadAvatarForUser(avatar, u.name, u.avatarUrl)
userName.text = Pronouns.span(u.name, u.pronouns)
userRole.text = u.enrollments.distinctBy { it.displayType }.joinToString { it.displayType }
userBackground.setBackgroundColor(ColorKeeper.getOrGenerateColor(canvasContext))
diff --git a/apps/student/src/main/java/com/instructure/student/holders/InboxMessageHolder.kt b/apps/student/src/main/java/com/instructure/student/holders/InboxMessageHolder.kt
index 0ea1ea38e7..a4fac3b5c3 100644
--- a/apps/student/src/main/java/com/instructure/student/holders/InboxMessageHolder.kt
+++ b/apps/student/src/main/java/com/instructure/student/holders/InboxMessageHolder.kt
@@ -37,7 +37,7 @@ import com.instructure.student.interfaces.MessageAdapterCallback
import com.instructure.student.view.ViewUtils
import kotlinx.android.synthetic.main.viewholder_message.view.*
import java.text.SimpleDateFormat
-import java.util.Locale
+import java.util.*
class InboxMessageHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
@@ -52,7 +52,7 @@ class InboxMessageHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
// Set author info
if (author != null) {
authorName.text = getAuthorTitle(author.id, conversation, message)
- ProfileUtils.loadAvatarForUser(authorAvatar, author)
+ ProfileUtils.loadAvatarForUser(authorAvatar, author.name, author.avatarUrl)
authorAvatar.setupAvatarA11y(author.name)
authorAvatar.onClick { callback.onAvatarClicked(author) }
} else {
diff --git a/apps/student/src/main/java/com/instructure/student/holders/PeopleViewHolder.kt b/apps/student/src/main/java/com/instructure/student/holders/PeopleViewHolder.kt
index d9ccbff790..0d6c00c201 100644
--- a/apps/student/src/main/java/com/instructure/student/holders/PeopleViewHolder.kt
+++ b/apps/student/src/main/java/com/instructure/student/holders/PeopleViewHolder.kt
@@ -16,6 +16,7 @@
*/
package com.instructure.student.holders
+import android.content.res.ColorStateList
import android.view.View
import androidx.recyclerview.widget.RecyclerView
import com.instructure.canvasapi2.models.User
@@ -25,8 +26,8 @@ import com.instructure.pandautils.utils.ProfileUtils
import com.instructure.pandautils.utils.setGone
import com.instructure.pandautils.utils.setVisible
import com.instructure.student.R
-import com.instructure.student.util.BinderUtils
import com.instructure.student.interfaces.AdapterToFragmentCallback
+import com.instructure.student.util.BinderUtils
import kotlinx.android.synthetic.main.viewholder_people.view.*
class PeopleViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
@@ -37,11 +38,11 @@ class PeopleViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
isFirstItem: Boolean,
isLastItem: Boolean
) = with(itemView) {
- ProfileUtils.loadAvatarForUser(icon, item)
+ ProfileUtils.loadAvatarForUser(icon, item.name, item.avatarUrl, 0)
+ icon.backgroundTintList = ColorStateList.valueOf(courseColor)
itemView.setOnClickListener { adapterToFragmentCallback.onRowClicked(item, adapterPosition, true) }
- icon.borderColor = courseColor
title.text = Pronouns.span(item.name, item.pronouns)
val enrollmentIndex = item.enrollmentIndex
diff --git a/apps/student/src/main/java/com/instructure/student/holders/RecipientViewHolder.kt b/apps/student/src/main/java/com/instructure/student/holders/RecipientViewHolder.kt
index d7f66f2f65..f40440525b 100644
--- a/apps/student/src/main/java/com/instructure/student/holders/RecipientViewHolder.kt
+++ b/apps/student/src/main/java/com/instructure/student/holders/RecipientViewHolder.kt
@@ -16,9 +16,10 @@
package com.instructure.student.holders
import android.content.Context
+import android.content.res.ColorStateList
import android.graphics.Color
-import android.graphics.drawable.ColorDrawable
import android.view.View
+import androidx.core.content.ContextCompat
import androidx.recyclerview.widget.RecyclerView
import com.instructure.canvasapi2.models.Recipient
import com.instructure.canvasapi2.utils.Pronouns
@@ -47,7 +48,9 @@ class RecipientViewHolder(view: View) : RecyclerView.ViewHolder(view) {
fun setChecked(isChecked: Boolean = true) {
if (isChecked) {
setBackgroundColor(selectionColor and selectionTransparencyMask)
- avatar.setImageDrawable(ColorDrawable(selectionColor))
+ avatar.setImageDrawable(ContextCompat.getDrawable(context, R.drawable.ic_circle)?.apply {
+ mutate().setTintList(ColorStateList.valueOf(selectionColor))
+ })
checkMarkImageView.setVisible()
ColorUtils.colorIt(Color.WHITE, checkMarkImageView)
} else {
diff --git a/apps/student/src/main/java/com/instructure/student/mobius/assignmentDetails/submissionDetails/drawer/comments/ui/views/CommentView.kt b/apps/student/src/main/java/com/instructure/student/mobius/assignmentDetails/submissionDetails/drawer/comments/ui/views/CommentView.kt
index d6808caac4..bb56a568c3 100644
--- a/apps/student/src/main/java/com/instructure/student/mobius/assignmentDetails/submissionDetails/drawer/comments/ui/views/CommentView.kt
+++ b/apps/student/src/main/java/com/instructure/student/mobius/assignmentDetails/submissionDetails/drawer/comments/ui/views/CommentView.kt
@@ -57,7 +57,7 @@ class CommentView @JvmOverloads constructor(
fun setCommentBubbleColor(@ColorInt color: Int) = commentTextView.setBubbleColor(color)
fun setAvatar(avatarUrl: String?, userName: String) {
- ProfileUtils.loadAvatarForUser(avatarView, userName, avatarUrl ?: "")
+ ProfileUtils.loadAvatarForUser(avatarView, userName, avatarUrl.orEmpty())
}
var commentTextColor: Int
diff --git a/apps/student/src/main/java/com/instructure/student/view/AttachmentView.kt b/apps/student/src/main/java/com/instructure/student/view/AttachmentView.kt
index c6f0cc0063..987660b3bd 100644
--- a/apps/student/src/main/java/com/instructure/student/view/AttachmentView.kt
+++ b/apps/student/src/main/java/com/instructure/student/view/AttachmentView.kt
@@ -16,19 +16,18 @@
package com.instructure.student.view
import android.content.Context
-import android.graphics.Bitmap
-import android.graphics.Canvas
-import androidx.core.content.ContextCompat
+import android.graphics.PorterDuff
import android.view.LayoutInflater
import android.view.ViewGroup
import android.widget.FrameLayout
import android.widget.ImageView
-import com.instructure.student.R
+import androidx.core.content.ContextCompat
+import com.bumptech.glide.Glide
+import com.bumptech.glide.request.RequestOptions
import com.instructure.canvasapi2.models.Attachment
import com.instructure.canvasapi2.models.RemoteFile
import com.instructure.pandautils.utils.onClick
-import com.squareup.picasso.Picasso
-import com.squareup.picasso.Transformation
+import com.instructure.student.R
import kotlinx.android.synthetic.main.view_attachment.view.*
import java.io.File
@@ -93,27 +92,17 @@ class AttachmentView(context: Context) : FrameLayout(context) {
private fun setThumbnail(path: String?) {
if (path.isNullOrBlank()) return
val file = File(path)
- val picasso = Picasso.with(context)
- val creator = if (file.exists() && file.isFile) picasso.load(file) else picasso.load(path)
- creator.fit().centerCrop().transform(ATTACHMENT_PREVIEW_TRANSFORMER).into(previewImage)
+ Glide.with(context)
+ .load(if (file.exists() && file.isFile) file else path)
+ .apply(RequestOptions.centerCropTransform())
+ .into(previewImage)
+ previewImage.setColorFilter(
+ 0xBB9B9B9B.toInt(),
+ PorterDuff.Mode.SRC_OVER
+ )
}
companion object {
- /** Picasso transformation to apply gray overlay on thumbnail */
- @JvmField
- val ATTACHMENT_PREVIEW_TRANSFORMER: Transformation = object : Transformation {
- override fun transform(source: Bitmap?): Bitmap? {
- if (source == null) return null
- val mutableSource = source.copy(source.config, true)
- source.recycle()
- val canvas = Canvas(mutableSource)
- canvas.drawColor(0xBB9B9B9B.toInt())
- return mutableSource
- }
-
- override fun key(): String = "gray-overlay"
- }
-
fun setColorAndIcon(
context: Context,
contentType: String?,
diff --git a/apps/student/src/main/res/layout/dialog_file_upload.xml b/apps/student/src/main/res/layout/dialog_file_upload.xml
index ad2de6981d..cadcbd4893 100644
--- a/apps/student/src/main/res/layout/dialog_file_upload.xml
+++ b/apps/student/src/main/res/layout/dialog_file_upload.xml
@@ -37,12 +37,13 @@
android:gravity="center_vertical"
android:paddingBottom="16dp">
-
+ android:background="@drawable/ic_circle"
+ android:visibility="invisible" />
-
-
-
+ android:layout_marginTop="16dp"
+ android:background="@android:color/transparent" />
-
+ android:importantForAccessibility="no" />
diff --git a/apps/student/src/main/res/layout/view_comment.xml b/apps/student/src/main/res/layout/view_comment.xml
index cb38fedc87..04657f3dd4 100644
--- a/apps/student/src/main/res/layout/view_comment.xml
+++ b/apps/student/src/main/res/layout/view_comment.xml
@@ -27,7 +27,7 @@
android:layout_width="match_parent"
android:layout_height="wrap_content">
-
-
-
-
-
+ android:layout_height="40dp" />
, grantResults: IntArray) {
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 1ced17ab4f..dedf681f8f 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
@@ -16,13 +16,9 @@
*/
package com.instructure.teacher.fragments
-import android.graphics.Color
-import android.graphics.Typeface
import android.os.Bundle
-import android.util.TypedValue
import android.view.MenuItem
import android.view.View
-import com.bumptech.glide.Glide
import com.instructure.canvasapi2.utils.APIHelper
import com.instructure.canvasapi2.utils.ApiPrefs
import com.instructure.canvasapi2.utils.Pronouns
@@ -35,7 +31,6 @@ import com.instructure.teacher.R
import com.instructure.teacher.dialog.NoInternetConnectionDialog
import com.instructure.teacher.router.RouteMatcher
import com.instructure.teacher.utils.adoptToolbarStyle
-import com.instructure.teacher.utils.getColorCompat
import com.instructure.teacher.utils.setupBackButtonAsBackPressedOnly
import com.instructure.teacher.utils.setupMenu
import kotlinx.android.synthetic.main.fragment_profile.*
@@ -70,26 +65,7 @@ class ProfileFragment : BaseFragment() {
private fun setupViewableData() {
val user = ApiPrefs.user
-
- if(ProfileUtils.shouldLoadAltAvatarImage(user?.avatarUrl)) {
- val initials = ProfileUtils.getUserInitials(user?.shortName ?: "")
- val color = requireContext().getColorCompat(R.color.backgroundDark)
- val drawable = TextDrawable.builder()
- .beginConfig()
- .height(requireContext().resources.getDimensionPixelSize(R.dimen.profileAvatarSize))
- .width(requireContext().resources.getDimensionPixelSize(R.dimen.profileAvatarSize))
- .toUpperCase()
- .useFont(Typeface.DEFAULT_BOLD)
- .textColor(color)
- .endConfig()
- .buildRound(initials, Color.WHITE)
- usersAvatar.borderColor = requireContext().getColorCompat(R.color.borderDark)
- usersAvatar.borderWidth = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 6F, requireContext().resources.displayMetrics).toInt()
- usersAvatar.setImageDrawable(drawable)
- } else {
- Glide.with(requireContext()).load(user?.avatarUrl).into(usersAvatar)
- }
-
+ ProfileUtils.loadAvatarForUser(usersAvatar, user?.shortName, user?.avatarUrl, 0)
usersName.text = Pronouns.span(user?.shortName, user?.pronouns)
usersEmail.text = user?.primaryEmail
usersBio.text = user?.bio
diff --git a/apps/teacher/src/main/java/com/instructure/teacher/holders/AssigneeViewHolder.kt b/apps/teacher/src/main/java/com/instructure/teacher/holders/AssigneeViewHolder.kt
index c2f5a3eb0c..78f915bda9 100644
--- a/apps/teacher/src/main/java/com/instructure/teacher/holders/AssigneeViewHolder.kt
+++ b/apps/teacher/src/main/java/com/instructure/teacher/holders/AssigneeViewHolder.kt
@@ -15,12 +15,10 @@
*/
package com.instructure.teacher.holders
-import android.content.Context
import android.graphics.Color
-import android.graphics.Typeface
import android.graphics.drawable.ColorDrawable
-import android.util.TypedValue
import android.view.View
+import android.widget.ImageView
import android.widget.TextView
import androidx.recyclerview.widget.RecyclerView
import com.instructure.canvasapi2.models.Group
@@ -28,15 +26,12 @@ import com.instructure.canvasapi2.models.Section
import com.instructure.canvasapi2.models.User
import com.instructure.canvasapi2.utils.Pronouns
import com.instructure.pandautils.utils.ProfileUtils
-import com.instructure.pandautils.utils.TextDrawable
import com.instructure.pandautils.utils.setGone
import com.instructure.pandautils.utils.setVisible
import com.instructure.teacher.R
import com.instructure.teacher.models.AssigneeCategory
import com.instructure.teacher.models.EveryoneAssignee
import com.instructure.teacher.presenters.AssigneeListPresenter
-import com.instructure.teacher.utils.getColorCompat
-import de.hdodenhof.circleimageview.CircleImageView
import kotlinx.android.synthetic.main.adapter_assignee.view.*
import kotlinx.android.synthetic.main.adapter_assignee_header.view.*
@@ -76,7 +71,7 @@ class AssigneeItemViewHolder(view: View) : AssigneeViewHolder(view) {
if (presenter.isEveryone) {
setChecked(true)
} else {
- setItemAvatar(context, itemName, assigneeAvatarImageView)
+ setItemAvatar(itemName, assigneeAvatarImageView)
}
setOnClickListener { presenter.toggleIsEveryone(adapterPosition) }
}
@@ -97,7 +92,7 @@ class AssigneeItemViewHolder(view: View) : AssigneeViewHolder(view) {
if (item.id in presenter.selectedSections) {
setChecked(true)
} else {
- setItemAvatar(context, item.name, assigneeAvatarImageView)
+ setItemAvatar(item.name, assigneeAvatarImageView)
}
setOnClickListener { presenter.toggleSection(item.id, adapterPosition) }
}
@@ -107,30 +102,16 @@ class AssigneeItemViewHolder(view: View) : AssigneeViewHolder(view) {
if (item.id in presenter.selectedGroups) {
setChecked(true)
} else {
- setItemAvatar(context, item.name ?: "", assigneeAvatarImageView)
+ setItemAvatar(item.name.orEmpty(), assigneeAvatarImageView)
}
setOnClickListener { presenter.toggleGroup(item.id, adapterPosition) }
}
}
}
- private fun setItemAvatar(context: Context, itemName: String, circleImageView: CircleImageView) {
- val initials = ProfileUtils.getUserInitials(itemName)
- val color = context.getColorCompat(R.color.textDark)
- val drawable = TextDrawable.builder()
- .beginConfig()
- .height(context.resources.getDimensionPixelSize(com.instructure.pandautils.R.dimen.avatar_size))
- .width(context.resources.getDimensionPixelSize(com.instructure.pandautils.R.dimen.avatar_size))
- .toUpperCase()
- .useFont(Typeface.DEFAULT_BOLD)
- .textColor(color)
- .endConfig()
- .buildRound(initials, Color.TRANSPARENT)
- circleImageView.borderColor = color
- circleImageView.borderWidth = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 0.5f, context.resources.displayMetrics).toInt()
- circleImageView.setImageDrawable(drawable)
+ private fun setItemAvatar(itemName: String, imageView: ImageView) {
+ ProfileUtils.loadAvatarForUser(imageView, itemName, null, 0)
}
-
}
class AssigneeTypeViewHolder(view: View) : AssigneeViewHolder(view) {
diff --git a/apps/teacher/src/main/java/com/instructure/teacher/holders/AttendanceViewHolder.kt b/apps/teacher/src/main/java/com/instructure/teacher/holders/AttendanceViewHolder.kt
index 7919cdf299..bf7287fcba 100644
--- a/apps/teacher/src/main/java/com/instructure/teacher/holders/AttendanceViewHolder.kt
+++ b/apps/teacher/src/main/java/com/instructure/teacher/holders/AttendanceViewHolder.kt
@@ -44,7 +44,7 @@ class AttendanceViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
basicUser.name = attendance.student?.name
basicUser.pronouns = attendance.student?.pronouns
basicUser.avatarUrl = attendance.student?.avatarUrl
- ProfileUtils.loadAvatarForUser(studentAvatar, basicUser)
+ ProfileUtils.loadAvatarForUser(studentAvatar, basicUser.name, basicUser.avatarUrl)
// Set student name
userName.text = attendance.student?.let { Pronouns.span(it.name, it.pronouns) }
diff --git a/apps/teacher/src/main/java/com/instructure/teacher/holders/RecipientViewHolder.kt b/apps/teacher/src/main/java/com/instructure/teacher/holders/RecipientViewHolder.kt
index 9b14eafd8a..850960379e 100644
--- a/apps/teacher/src/main/java/com/instructure/teacher/holders/RecipientViewHolder.kt
+++ b/apps/teacher/src/main/java/com/instructure/teacher/holders/RecipientViewHolder.kt
@@ -16,9 +16,10 @@
package com.instructure.teacher.holders
import android.content.Context
+import android.content.res.ColorStateList
import android.graphics.Color
-import android.graphics.drawable.ColorDrawable
import android.view.View
+import androidx.core.content.ContextCompat
import androidx.recyclerview.widget.RecyclerView
import com.instructure.canvasapi2.models.Recipient
import com.instructure.canvasapi2.utils.Pronouns
@@ -39,7 +40,9 @@ class RecipientViewHolder(view: View) : RecyclerView.ViewHolder(view) {
fun setChecked(isChecked: Boolean = true) {
if (isChecked) {
setBackgroundColor(selectionColor and SELECTION_TRANSPARENCY_MASK)
- avatar.setImageDrawable(ColorDrawable(selectionColor))
+ avatar.setImageDrawable(ContextCompat.getDrawable(context, R.drawable.ic_circle)?.apply {
+ mutate().setTintList(ColorStateList.valueOf(selectionColor))
+ })
checkMarkImageView.setVisible()
ColorUtils.colorIt(Color.WHITE, checkMarkImageView)
} else {
diff --git a/apps/teacher/src/main/java/com/instructure/teacher/utils/ViewExtensions.kt b/apps/teacher/src/main/java/com/instructure/teacher/utils/ViewExtensions.kt
index 1a74d6109b..3d6bfbd01a 100644
--- a/apps/teacher/src/main/java/com/instructure/teacher/utils/ViewExtensions.kt
+++ b/apps/teacher/src/main/java/com/instructure/teacher/utils/ViewExtensions.kt
@@ -25,6 +25,7 @@ import android.text.SpannableString
import android.text.style.URLSpan
import android.view.MenuItem
import android.view.View
+import android.widget.ImageView
import android.widget.TextView
import androidx.annotation.DrawableRes
import androidx.annotation.MenuRes
@@ -37,12 +38,9 @@ import com.instructure.pandautils.utils.isTablet
import com.instructure.pandautils.utils.requestAccessibilityFocus
import com.instructure.teacher.R
import com.instructure.teacher.activities.InternalWebViewActivity
-import com.instructure.teacher.mobius.common.ui.MobiusView
import com.instructure.teacher.router.RouteMatcher
-import de.hdodenhof.circleimageview.CircleImageView
-
-fun CircleImageView.setAnonymousAvatar() = setImageResource(R.drawable.ic_user_avatar)
+fun ImageView.setAnonymousAvatar() = setImageResource(R.drawable.ic_user_avatar)
/**
* Loads the given resource as this Toolbar's icon, assigns it the given content description, and
diff --git a/apps/teacher/src/main/java/com/instructure/teacher/view/CommentView.kt b/apps/teacher/src/main/java/com/instructure/teacher/view/CommentView.kt
index b0cb286ae9..c7a5b7266f 100644
--- a/apps/teacher/src/main/java/com/instructure/teacher/view/CommentView.kt
+++ b/apps/teacher/src/main/java/com/instructure/teacher/view/CommentView.kt
@@ -47,7 +47,7 @@ class CommentView @JvmOverloads constructor(
fun setCommentBubbleColor(@ColorInt color: Int) = commentTextView.setBubbleColor(color)
fun setAvatar(avatarUrl: String?, userName: String) {
- ProfileUtils.loadAvatarForUser(avatarView, userName, avatarUrl ?: "")
+ ProfileUtils.loadAvatarForUser(avatarView, userName, avatarUrl.orEmpty())
}
var commentTextColor: Int
diff --git a/apps/teacher/src/main/java/com/instructure/teacher/view/FileFolderPublishedStatusIconView.kt b/apps/teacher/src/main/java/com/instructure/teacher/view/FileFolderPublishedStatusIconView.kt
index 455d445dae..4162596c1e 100644
--- a/apps/teacher/src/main/java/com/instructure/teacher/view/FileFolderPublishedStatusIconView.kt
+++ b/apps/teacher/src/main/java/com/instructure/teacher/view/FileFolderPublishedStatusIconView.kt
@@ -17,17 +17,17 @@
package com.instructure.teacher.view
import android.content.Context
-import androidx.annotation.DrawableRes
-import androidx.core.content.ContextCompat
import android.util.AttributeSet
import android.view.Gravity
import android.widget.FrameLayout
import android.widget.ImageView
+import androidx.annotation.DrawableRes
+import androidx.core.content.ContextCompat
+import com.bumptech.glide.Glide
import com.instructure.canvasapi2.models.FileFolder
import com.instructure.pandautils.utils.DP
import com.instructure.teacher.R
import com.instructure.teacher.utils.getColorCompat
-import com.squareup.picasso.Picasso
class FileFolderPublishedStatusIconView @JvmOverloads constructor(
context: Context,
@@ -108,7 +108,7 @@ class FileFolderPublishedStatusIconView @JvmOverloads constructor(
}
fun setImage(thumbnailUrl: String) {
- Picasso.with(context).load(thumbnailUrl).into(mAssignmentIcon)
+ Glide.with(context).load(thumbnailUrl).into(mAssignmentIcon)
}
fun setPublishedStatus(fileFolder: FileFolder) {
diff --git a/apps/teacher/src/main/res/layout-sw760dp/view_submission_content.xml b/apps/teacher/src/main/res/layout-sw760dp/view_submission_content.xml
index 197809466d..bae44b516c 100644
--- a/apps/teacher/src/main/res/layout-sw760dp/view_submission_content.xml
+++ b/apps/teacher/src/main/res/layout-sw760dp/view_submission_content.xml
@@ -74,7 +74,7 @@
android:gravity="center_vertical"
android:orientation="horizontal">
-
-
+ android:background="@drawable/avatar_circular_border_thin"
+ android:backgroundTint="@color/textDark"
+ android:padding="@dimen/avatar_border_width_thin" />
+ app:srcCompat="@drawable/ic_checkmark"
+ app:tint="@color/white" />
diff --git a/apps/teacher/src/main/res/layout/adapter_attendance.xml b/apps/teacher/src/main/res/layout/adapter_attendance.xml
index f8226ed635..811c150472 100644
--- a/apps/teacher/src/main/res/layout/adapter_attendance.xml
+++ b/apps/teacher/src/main/res/layout/adapter_attendance.xml
@@ -26,7 +26,7 @@
android:paddingTop="4dp"
android:paddingBottom="4dp">
-
-
-
-
-
-
-
-
+ android:background="@drawable/avatar_circular_border_thick"
+ android:backgroundTint="@color/textDark"
+ android:padding="@dimen/avatar_border_width_thick" />
-
+ android:background="@drawable/avatar_circular_border_thick"
+ android:backgroundTint="@color/textDark"
+ android:padding="@dimen/avatar_border_width_thick" />
-
-
+ android:importantForAccessibility="no" />
diff --git a/apps/teacher/src/main/res/layout/view_comment.xml b/apps/teacher/src/main/res/layout/view_comment.xml
index e98a0df40e..eae5da9432 100644
--- a/apps/teacher/src/main/res/layout/view_comment.xml
+++ b/apps/teacher/src/main/res/layout/view_comment.xml
@@ -12,7 +12,7 @@
android:layout_width="match_parent"
android:layout_height="wrap_content">
-
-
-
+ android:layout_height="40dp" />
,
@@ -63,7 +62,7 @@ class PreviousUsersAdapter(
class PreviousUserHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
fun bind(user: SignedInUser, onUserClick: () -> Unit, onUserRemove: () -> Unit) = with(itemView) {
- loadAvatarForUser(usersAvatar, user.user.name, user.user.avatarUrl)
+ ProfileUtils.loadAvatarForUser(usersAvatar, user.user.name, user.user.avatarUrl, 0)
userName.text = span(user.user.name, user.user.pronouns)
schoolDomain.text = user.domain
setOnClickListener { onUserClick() }
diff --git a/libs/login-api-2/src/main/res/layout/adapter_previous_users.xml b/libs/login-api-2/src/main/res/layout/adapter_previous_users.xml
index 6a97e13a08..e2fdbb316c 100644
--- a/libs/login-api-2/src/main/res/layout/adapter_previous_users.xml
+++ b/libs/login-api-2/src/main/res/layout/adapter_previous_users.xml
@@ -26,13 +26,15 @@
android:paddingEnd="16dp"
android:background="?android:selectableItemBackground">
-
+ android:layout_centerVertical="true"
+ android:background="@drawable/avatar_circular_border_thin"
+ android:backgroundTint="@color/backgroundMedium"
+ android:padding="@dimen/avatar_border_width_thin" />
+
+
+
+
\ No newline at end of file
diff --git a/libs/pandares/src/main/res/drawable/avatar_circular_border_thin.xml b/libs/pandares/src/main/res/drawable/avatar_circular_border_thin.xml
new file mode 100644
index 0000000000..a8e278dbf1
--- /dev/null
+++ b/libs/pandares/src/main/res/drawable/avatar_circular_border_thin.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/libs/pandares/src/main/res/values/dimens.xml b/libs/pandares/src/main/res/values/dimens.xml
index 8c64099c82..73c8d44998 100644
--- a/libs/pandares/src/main/res/values/dimens.xml
+++ b/libs/pandares/src/main/res/values/dimens.xml
@@ -19,4 +19,6 @@
127dp
195dp
60dp
+ 3dp
+ .5dp
\ No newline at end of file
diff --git a/libs/pandautils/build.gradle b/libs/pandautils/build.gradle
index 823116b65c..265c313bec 100644
--- a/libs/pandautils/build.gradle
+++ b/libs/pandautils/build.gradle
@@ -122,10 +122,6 @@ dependencies {
api Libs.ANDROID_SVG
- // TODO These should be replaced in the future with Glide.
- api 'com.squareup.picasso:picasso:2.5.2'
- api 'de.hdodenhof:circleimageview:3.0.0'
-
api Libs.EXOPLAYER
api (Libs.SCALE_IMAGE_VIEW) {
exclude group: "androidx.exifinterface"
diff --git a/libs/pandautils/src/main/java/com/instructure/pandautils/utils/ProfileUtils.kt b/libs/pandautils/src/main/java/com/instructure/pandautils/utils/ProfileUtils.kt
index 7e13316a7e..71ab242fe7 100644
--- a/libs/pandautils/src/main/java/com/instructure/pandautils/utils/ProfileUtils.kt
+++ b/libs/pandautils/src/main/java/com/instructure/pandautils/utils/ProfileUtils.kt
@@ -26,21 +26,12 @@ import android.graphics.drawable.BitmapDrawable
import android.graphics.drawable.Drawable
import android.view.View
import android.widget.ImageView
+import androidx.annotation.Dimension
import androidx.core.content.ContextCompat
-import com.bumptech.glide.Glide
-import com.bumptech.glide.load.DataSource
-import com.bumptech.glide.load.engine.GlideException
-import com.bumptech.glide.request.RequestListener
-import com.bumptech.glide.request.target.Target
-import com.instructure.canvasapi2.models.Author
import com.instructure.canvasapi2.models.BasicUser
import com.instructure.canvasapi2.models.Conversation
-import com.instructure.canvasapi2.models.User
import com.instructure.pandautils.R
-import com.squareup.picasso.Callback
-import com.squareup.picasso.Picasso
-import de.hdodenhof.circleimageview.CircleImageView
-import java.util.Locale
+import java.util.*
object ProfileUtils {
@@ -66,66 +57,22 @@ object ProfileUtils {
}
}
- fun loadAvatarForUser(avatar: CircleImageView, user: User) {
- loadAvatarForUser(avatar, user.name, user.avatarUrl)
- }
-
- fun loadAvatarForUser(avatar: CircleImageView, user: BasicUser) {
- loadAvatarForUser(avatar, user.name, user.avatarUrl)
- }
-
- fun loadAvatarForUser(avatar: CircleImageView, user: Author) {
- loadAvatarForUser(avatar, user.displayName, user.avatarImageUrl)
- }
-
- fun loadAvatarForUser(avatar: CircleImageView, name: String?, url: String?) {
- val context = avatar.context
- if (shouldLoadAltAvatarImage(url)) {
- Picasso.with(context).cancelRequest(avatar)
- avatar.setAvatarImage(context, name)
- } else {
- Picasso.with(context)
- .load(url)
- .fit()
- .placeholder(R.drawable.recipient_avatar_placeholder)
- .centerCrop()
- .into(avatar, object : Callback {
- override fun onSuccess() {}
-
- override fun onError() {
- avatar.setAvatarImage(context, name)
- }
- })
- }
- }
-
- fun loadAvatarForUser(imageView: ImageView, name: String?, url: String?) {
- val context = imageView.context
+ fun loadAvatarForUser(
+ imageView: ImageView,
+ name: String?,
+ url: String?,
+ @Dimension altAvatarBorderWidth: Int = imageView.context.resources.getDimension(R.dimen.avatar_border_width_thin).toInt()
+ ) {
if (shouldLoadAltAvatarImage(url)) {
- val avatarDrawable = createAvatarDrawable(context, name ?: "")
- imageView.setImageDrawable(avatarDrawable)
+ imageView.setImageDrawable(createAvatarDrawable(imageView.context, name.orEmpty(), altAvatarBorderWidth))
} else {
- Glide.with(imageView)
- .load(url)
- .placeholder(R.drawable.recipient_avatar_placeholder)
- .circleCrop()
- .addListener(object : RequestListener {
- override fun onLoadFailed(e: GlideException?, model: Any?, target: Target?, isFirstResource: Boolean): Boolean {
- val avatarDrawable = createAvatarDrawable(context, name ?: "")
- imageView.setImageDrawable(avatarDrawable)
- return false
- }
-
- override fun onResourceReady(resource: Drawable?, model: Any?, target: Target?, dataSource: DataSource?, isFirstResource: Boolean): Boolean {
- return false
- }
-
- })
- .into(imageView)
+ imageView.loadCircularImage(url, R.drawable.recipient_avatar_placeholder) {
+ imageView.setImageDrawable(createAvatarDrawable(imageView.context, name.orEmpty(), altAvatarBorderWidth))
+ }
}
}
- private fun createAvatarDrawable(context: Context, userName: String): Drawable {
+ private fun createAvatarDrawable(context: Context, userName: String, @Dimension borderWidth: Int): Drawable {
val initials = getUserInitials(userName)
val color = ContextCompat.getColor(context, R.color.textDark)
return TextDrawable.builder()
@@ -135,7 +82,7 @@ object ProfileUtils {
.toUpperCase()
.useFont(Typeface.DEFAULT_BOLD)
.textColor(color)
- .withBorder(context.DP(0.5f).toInt())
+ .withBorder(borderWidth)
.withBorderColor(color)
.endConfig()
.buildRound(initials, Color.WHITE)
@@ -156,7 +103,7 @@ object ProfileUtils {
* If there are more than two participants, a group avatar will be shown and accessibility will be disabled.
*/
fun configureAvatarForConversation(
- avatar: CircleImageView,
+ avatar: ImageView,
conversation: Conversation,
onClick: ((BasicUser) -> Unit)? = null
) {
diff --git a/libs/pandautils/src/main/java/com/instructure/pandautils/utils/ViewExtensions.kt b/libs/pandautils/src/main/java/com/instructure/pandautils/utils/ViewExtensions.kt
index 6dcac5a1f0..c5171d111e 100644
--- a/libs/pandautils/src/main/java/com/instructure/pandautils/utils/ViewExtensions.kt
+++ b/libs/pandautils/src/main/java/com/instructure/pandautils/utils/ViewExtensions.kt
@@ -54,10 +54,14 @@ import androidx.core.view.ViewCompat
import androidx.fragment.app.Fragment
import androidx.recyclerview.widget.RecyclerView
import com.bumptech.glide.Glide
+import com.bumptech.glide.load.DataSource
+import com.bumptech.glide.load.engine.GlideException
import com.bumptech.glide.load.engine.bitmap_recycle.BitmapPool
import com.bumptech.glide.load.resource.bitmap.CenterCrop
import com.bumptech.glide.load.resource.drawable.DrawableTransitionOptions
+import com.bumptech.glide.request.RequestListener
import com.bumptech.glide.request.RequestOptions
+import com.bumptech.glide.request.target.Target
import com.bumptech.glide.signature.ObjectKey
import com.instructure.canvasapi2.models.Attachment
import com.instructure.canvasapi2.models.Course
@@ -68,10 +72,9 @@ import com.instructure.canvasapi2.utils.tryOrNull
import com.instructure.canvasapi2.utils.weave.WeaveJob
import com.instructure.canvasapi2.utils.weave.weave
import com.instructure.pandautils.R
-import de.hdodenhof.circleimageview.CircleImageView
import kotlinx.android.synthetic.main.abc_search_view.view.*
import kotlinx.coroutines.delay
-import java.util.Locale
+import java.util.*
/** Convenience extension for setting a click listener */
@Suppress("NOTHING_TO_INLINE")
@@ -505,24 +508,6 @@ fun View.clearAvatarA11y() {
setAccessibilityDelegate(null)
}
-@JvmName("setUserAvatarImage")
-fun CircleImageView.setAvatarImage(context: Context, userName: String?) {
- val initials = ProfileUtils.getUserInitials(userName)
- val color = ContextCompat.getColor(context, R.color.textDark)
- val drawable = TextDrawable.builder()
- .beginConfig()
- .height(context.resources.getDimensionPixelSize(com.instructure.pandautils.R.dimen.avatar_size))
- .width(context.resources.getDimensionPixelSize(com.instructure.pandautils.R.dimen.avatar_size))
- .toUpperCase()
- .useFont(Typeface.DEFAULT_BOLD)
- .textColor(color)
- .endConfig()
- .buildRound(initials, Color.WHITE)
- this.borderColor = color
- this.borderWidth = context.DP(0.5f).toInt()
- this.setImageDrawable(drawable)
-}
-
/**
* Loads the given resource as this Toolbar's icon, assigns it the given content description, and
* propagates its clicks to the provided function.
@@ -771,4 +756,46 @@ fun View.fadeAnimationWithAction(action: () -> Unit) {
})
startAnimation(fadeOutAnim)
-}
\ No newline at end of file
+}
+
+/**
+ * Load model into ImageView as a circle image using Glide
+ *
+ * @param model - Any object supported by Glide (Uri, File, Bitmap, String, resource id as Int, ByteArray, and Drawable)
+ * @param placeholder - Placeholder drawable
+ * @param onFailure - Called when an exception occurs during a load
+ */
+@SuppressLint("CheckResult")
+fun ImageView.loadCircularImage(
+ model: T,
+ placeholder: Int? = null,
+ onFailure: (() -> Unit)? = null
+) {
+ Glide.with(context)
+ .asBitmap()
+ .load(model)
+ .apply { placeholder?.let { placeholder(it) } }
+ .apply(RequestOptions.circleCropTransform())
+ .addListener(object : RequestListener {
+ override fun onLoadFailed(
+ e: GlideException?,
+ model: Any?,
+ target: Target?,
+ isFirstResource: Boolean
+ ): Boolean {
+ onFailure?.invoke()
+ return true
+ }
+
+ override fun onResourceReady(
+ resource: Bitmap?,
+ model: Any?,
+ target: Target?,
+ dataSource: DataSource?,
+ isFirstResource: Boolean
+ ): Boolean {
+ return false
+ }
+ })
+ .into(this)
+}
diff --git a/libs/pandautils/src/main/java/com/instructure/pandautils/views/AttachmentView.kt b/libs/pandautils/src/main/java/com/instructure/pandautils/views/AttachmentView.kt
index 4ed424f0b6..3f04801181 100644
--- a/libs/pandautils/src/main/java/com/instructure/pandautils/views/AttachmentView.kt
+++ b/libs/pandautils/src/main/java/com/instructure/pandautils/views/AttachmentView.kt
@@ -16,19 +16,18 @@
package com.instructure.pandautils.views
import android.content.Context
-import android.graphics.Bitmap
-import android.graphics.Canvas
-import androidx.core.content.ContextCompat
+import android.graphics.PorterDuff
import android.view.LayoutInflater
import android.view.ViewGroup
import android.widget.FrameLayout
import android.widget.ImageView
+import androidx.core.content.ContextCompat
+import com.bumptech.glide.Glide
+import com.bumptech.glide.request.RequestOptions
import com.instructure.canvasapi2.models.Attachment
import com.instructure.canvasapi2.models.RemoteFile
import com.instructure.pandautils.R
import com.instructure.pandautils.utils.onClick
-import com.squareup.picasso.Picasso
-import com.squareup.picasso.Transformation
import kotlinx.android.synthetic.main.view_attachment.view.*
import java.io.File
@@ -93,27 +92,17 @@ class AttachmentView(context: Context) : FrameLayout(context) {
private fun setThumbnail(path: String?) {
if (path.isNullOrBlank()) return
val file = File(path)
- val picasso = Picasso.with(context)
- val creator = if (file.exists() && file.isFile) picasso.load(file) else picasso.load(path)
- creator.fit().centerCrop().transform(ATTACHMENT_PREVIEW_TRANSFORMER).into(previewImage)
+ Glide.with(context)
+ .load(if (file.exists() && file.isFile) file else path)
+ .apply(RequestOptions.centerCropTransform())
+ .into(previewImage)
+ previewImage.setColorFilter(
+ 0xBB9B9B9B.toInt(),
+ PorterDuff.Mode.SRC_OVER
+ )
}
companion object {
- /** Picasso transformation to apply gray overlay on thumbnail */
- @JvmField
- val ATTACHMENT_PREVIEW_TRANSFORMER: Transformation = object : Transformation {
- override fun transform(source: Bitmap?): Bitmap? {
- if (source == null) return null
- val mutableSource = source.copy(source.config, true)
- source.recycle()
- val canvas = Canvas(mutableSource)
- canvas.drawColor(0xBB9B9B9B.toInt())
- return mutableSource
- }
-
- override fun key(): String = "gray-overlay"
- }
-
fun setColorAndIcon(
context: Context,
contentType: String?,
diff --git a/libs/pandautils/src/main/res/layout/adapter_recipient_search_result.xml b/libs/pandautils/src/main/res/layout/adapter_recipient_search_result.xml
index 7224d674da..e6a5daaaec 100644
--- a/libs/pandautils/src/main/res/layout/adapter_recipient_search_result.xml
+++ b/libs/pandautils/src/main/res/layout/adapter_recipient_search_result.xml
@@ -23,7 +23,7 @@
android:paddingStart="12dp"
android:paddingEnd="12dp">
-
Date: Fri, 23 Sep 2022 16:16:13 +0200
Subject: [PATCH 08/73] [MBL-16262][Student] Comment on an announcement will
put that on top of the announcement list
refs: MBL-16262
affects: Student
release note: none
---
.../com/instructure/student/fragment/DiscussionListFragment.kt | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/apps/student/src/main/java/com/instructure/student/fragment/DiscussionListFragment.kt b/apps/student/src/main/java/com/instructure/student/fragment/DiscussionListFragment.kt
index 6ef8af3715..9a4351a229 100644
--- a/apps/student/src/main/java/com/instructure/student/fragment/DiscussionListFragment.kt
+++ b/apps/student/src/main/java/com/instructure/student/fragment/DiscussionListFragment.kt
@@ -345,7 +345,7 @@ open class DiscussionListFragment : ParentFragment(), Bookmarkable {
recyclerAdapter.addOrUpdateItem(DiscussionListRecyclerAdapter.CLOSED_FOR_COMMENTS, it)
}
else -> {
- recyclerAdapter.addOrUpdateItem(DiscussionListRecyclerAdapter.UNPINNED, it)
+ if (!isAnnouncement) recyclerAdapter.addOrUpdateItem(DiscussionListRecyclerAdapter.UNPINNED, it)
}
}
}
From 4fca23f9c6f76375414fed0b314a73baabbed82e Mon Sep 17 00:00:00 2001
From: Kristof Deak <92309696+kdeakinstructure@users.noreply.github.com>
Date: Wed, 28 Sep 2022 09:58:55 +0200
Subject: [PATCH 09/73] [MBL-16048][Teacher] - Refactor and merge Teacher
AssignmentE2E test (#1725)
Delete merged test case.
Extend AssignmentE2E test with due date, assigned to, publish,unpublish,republish, multiple dates functions.
Add some additional assertions.
Remove saveAssignment from edit functions (and so minor refactor needed on some interaction tests because of that).
refs: MBL-16048
affects: Teacher
release note: none
---
.../ui/EditAssignmentDetailsPageTest.kt | 2 +
.../teacher/ui/e2e/AssignmentE2ETest.kt | 120 ++++++++++--------
.../teacher/ui/pages/AssignmentDetailsPage.kt | 25 +++-
.../ui/pages/AssignmentDueDatesPage.kt | 20 ++-
.../ui/pages/EditAssignmentDetailsPage.kt | 27 +++-
5 files changed, 131 insertions(+), 63 deletions(-)
diff --git a/apps/teacher/src/androidTest/java/com/instructure/teacher/ui/EditAssignmentDetailsPageTest.kt b/apps/teacher/src/androidTest/java/com/instructure/teacher/ui/EditAssignmentDetailsPageTest.kt
index 775392f3f5..ce658550b7 100644
--- a/apps/teacher/src/androidTest/java/com/instructure/teacher/ui/EditAssignmentDetailsPageTest.kt
+++ b/apps/teacher/src/androidTest/java/com/instructure/teacher/ui/EditAssignmentDetailsPageTest.kt
@@ -61,6 +61,7 @@ class EditAssignmentDetailsPageTest : TeacherTest() {
editAssignmentDetailsPage.clickAssignmentNameEditText()
val newAssignmentName = randomString()
editAssignmentDetailsPage.editAssignmentName(newAssignmentName)
+ editAssignmentDetailsPage.saveAssignment()
assignmentDetailsPage.assertAssignmentNameChanged(newAssignmentName)
}
@@ -71,6 +72,7 @@ class EditAssignmentDetailsPageTest : TeacherTest() {
editAssignmentDetailsPage.clickPointsPossibleEditText()
val newPoints = randomDouble()
editAssignmentDetailsPage.editAssignmentPoints(newPoints)
+ editAssignmentDetailsPage.saveAssignment()
val stringPoints = NumberHelper.formatDecimal(newPoints, 1, true)
assignmentDetailsPage.assertAssignmentPointsChanged(stringPoints)
}
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 213ff3cccb..10442ff67b 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
@@ -23,10 +23,13 @@ import com.instructure.dataseeding.model.SubmissionType
import com.instructure.dataseeding.util.days
import com.instructure.dataseeding.util.fromNow
import com.instructure.dataseeding.util.iso8601
+import com.instructure.espresso.assertContainsText
+import com.instructure.espresso.page.onViewWithId
import com.instructure.panda_annotations.FeatureCategory
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 dagger.hilt.android.testing.HiltAndroidTest
import org.junit.Test
@@ -36,9 +39,7 @@ class AssignmentE2ETest : TeacherTest() {
override fun displaysPageObjects() = Unit
- override fun enableAndConfigureAccessibilityChecks() {
- //We don't want to see accessibility errors on E2E tests
- }
+ override fun enableAndConfigureAccessibilityChecks() = Unit
@E2E
@Test
@@ -82,6 +83,21 @@ class AssignmentE2ETest : TeacherTest() {
assignmentDetailsPage.assertNotSubmitted(3,3)
assignmentDetailsPage.assertNeedsGrading(0,3)
+ Log.d(STEP_TAG,"Publish ${assignment[0].name} assignment. Click on Save.")
+ assignmentDetailsPage.openEditPage()
+ editAssignmentDetailsPage.clickPublishSwitch()
+ editAssignmentDetailsPage.saveAssignment()
+
+ Log.d(STEP_TAG,"Refresh the page. Assert that ${assignment[0].name} assignment has been published.")
+ assignmentDetailsPage.refresh()
+ assignmentDetailsPage.waitForRender()
+ assignmentDetailsPage.assertPublishedStatus(false)
+
+ Log.d(STEP_TAG,"Open Edit Page and re-publish the assignment, then click on Save.")
+ assignmentDetailsPage.openEditPage()
+ editAssignmentDetailsPage.clickPublishSwitch()
+ editAssignmentDetailsPage.saveAssignment()
+
Log.d(PREPARATION_TAG,"Seed a submission for ${student.name} student.")
seedAssignmentSubmission(
submissionSeeds = listOf(SubmissionsApi.SubmissionSeedInfo(
@@ -126,78 +142,80 @@ class AssignmentE2ETest : TeacherTest() {
assignmentDetailsPage.assertNotSubmitted(1,3)
assignmentDetailsPage.assertNeedsGrading(1,3)
assignmentDetailsPage.assertHasGraded(1,3)
- }
-
- @E2E
- @Test
- @TestMetaData(Priority.MANDATORY, FeatureCategory.ASSIGNMENTS, TestCategory.E2E)
- fun testEditAssignmentE2E() {
-
- Log.d(PREPARATION_TAG, "Seeding data.")
- val data = seedData(teachers = 1, courses = 1, students = 1, favoriteCourses = 1)
- val teacher = data.teachersList[0]
- val course = data.coursesList[0]
- Log.d(STEP_TAG, "Login with user: ${teacher.name}, login id: ${teacher.loginId} , password: ${teacher.password}")
- tokenLogin(teacher)
- dashboardPage.waitForRender()
-
- Log.d(STEP_TAG,"Navigate to ${course.name} course's Assignments Tab and assert that there isn't any assignment displayed.")
- dashboardPage.openCourse(course.name)
- courseBrowserPage.openAssignmentsTab()
- assignmentListPage.assertDisplaysNoAssignmentsView()
-
- Log.d(PREPARATION_TAG,"Seeding assignment.")
- val assignment = seedAssignments(
- courseId = course.id,
- dueAt = 1.days.fromNow.iso8601,
- submissionTypes = listOf(SubmissionType.ONLINE_TEXT_ENTRY),
- teacherToken = teacher.token,
- pointsPossible = 15.0
- )
-
- Log.d(STEP_TAG,"Refresh Assignment List Page and assert that the previously seeded ${assignment[0].name} assignment has been displayed.")
- assignmentListPage.refresh()
- assignmentListPage.assertHasAssignment(assignment[0])
-
- Log.d(STEP_TAG,"Click on ${assignment[0].name} assignment and assert the numbers of 'Not Submitted' and 'Needs Grading' submissions.")
+ val newAssignmentName = "New Assignment Name"
+ Log.d(STEP_TAG,"Edit ${assignment[0].name} assignment's name to: $newAssignmentName.")
assignmentListPage.clickAssignment(assignment[0])
assignmentDetailsPage.waitForRender()
assignmentDetailsPage.assertAssignmentDetails(assignment[0])
- assignmentDetailsPage.assertNotSubmitted(1,1)
- assignmentDetailsPage.assertNeedsGrading(0,1)
- assignmentDetailsPage.assertSubmissionTypeOnlineTextEntry()
- val newAssignmentName = "New Assignment Name"
- Log.d(STEP_TAG,"Edit ${assignment[0].name} assignment's name to: $newAssignmentName.")
+ Log.d(STEP_TAG,"Open Edit Page again. Change the assignment's name to $newAssignmentName and it's description to 'assignment test description', then save.")
assignmentDetailsPage.openEditPage()
editAssignmentDetailsPage.clickAssignmentNameEditText()
editAssignmentDetailsPage.editAssignmentName(newAssignmentName)
+ editAssignmentDetailsPage.editDescription("assignment test description")
+ editAssignmentDetailsPage.saveAssignment()
- Log.d(STEP_TAG,"Refresh the page. Assert that the name has been changed to $newAssignmentName.")
+ Log.d(STEP_TAG,"Refresh the page. Assert that the name and description of the assignment has been changed to $newAssignmentName.")
assignmentDetailsPage.refresh()
assignmentDetailsPage.waitForRender()
assignmentDetailsPage.assertAssignmentNameChanged(newAssignmentName)
+ assignmentDetailsPage.assertDisplaysDescription("assignment test description")
Log.d(STEP_TAG,"Edit $newAssignmentName assignment's points to 20.")
assignmentDetailsPage.openEditPage()
editAssignmentDetailsPage.clickPointsPossibleEditText()
editAssignmentDetailsPage.editAssignmentPoints(20.0)
+ Log.d(STEP_TAG,"Change grade type to 'Percentage'.")
+ editAssignmentDetailsPage.clickOnDisplayGradeAsSpinner()
+ editAssignmentDetailsPage.selectGradeType("Percentage")
+
+ Log.d(STEP_TAG,"Click on the 'Due Time' section and edit the hour and minutes to 1:30 PM." +
+ "Assert that the changes has been applied on Edit Assignment Details page.")
+ editAssignmentDetailsPage.clickEditDueDate()
+ editAssignmentDetailsPage.editDate(2022,12,12)
+ editAssignmentDetailsPage.clickEditDueTime()
+ editAssignmentDetailsPage.editTime(1, 30)
+ editAssignmentDetailsPage.assertTimeChanged(1, 30, R.id.dueTime)
+
+ Log.d(STEP_TAG,"Click on 'Assigned To' spinner and select ${student.name} besides 'Everyone'." +
+ "Assert that ${student.name} and 'Everyone else' is selected as well.")
+ editAssignmentDetailsPage.editAssignees()
+ assigneeListPage.assertAssigneesSelected(listOf("Everyone"))
+ assigneeListPage.toggleAssignees(listOf(student.name))
+ val expectedAssignees = listOf(student.name, "Everyone else")
+ assigneeListPage.assertAssigneesSelected(expectedAssignees)
+
+ Log.d(STEP_TAG,"Save and close the assignee list. Assert that on the Assignment Details Page both the ${student.name} and the 'Everyone else' values are set.")
+ assigneeListPage.saveAndClose()
+ val assignText = editAssignmentDetailsPage.onViewWithId(R.id.assignTo)
+ for (assignee in expectedAssignees) assignText.assertContainsText(assignee)
+
+ Log.d(STEP_TAG,"Save the assignment.")
+ editAssignmentDetailsPage.saveAssignment()
+
Log.d(STEP_TAG,"Refresh the page. Assert that the points of $newAssignmentName assignment has been changed to 20.")
assignmentDetailsPage.refresh()
assignmentDetailsPage.waitForRender()
assignmentDetailsPage.assertAssignmentPointsChanged("20")
- Log.d(STEP_TAG,"Publish $newAssignmentName assignment. Click on Save.")
- assignmentDetailsPage.openEditPage()
- editAssignmentDetailsPage.clickPublishSwitch()
- editAssignmentDetailsPage.saveAssignment()
+ Log.d(STEP_TAG,"Assert that there are multiple due dates set, so the 'Multiple Due Dates' string is displayed on the 'Due Dates' section.")
+ assignmentDetailsPage.assertMultipleDueDates()
- Log.d(STEP_TAG,"Refresh the page. Assert that $newAssignmentName assignment has been published.")
- assignmentDetailsPage.refresh()
- assignmentDetailsPage.waitForRender()
- assignmentDetailsPage.assertPublishedStatus(false)
+ Log.d(STEP_TAG,"Open Due Dates Page and assert that there are 2 different due dates set.")
+ assignmentDetailsPage.openAllDatesPage()
+ assignmentDueDatesPage.assertDueDatesCount(2)
+
+ Log.d(STEP_TAG,"Assert that there is a due date set for '${student.name}' student especially and another one for everyone else.")
+ assignmentDueDatesPage.assertDueFor(student.name)
+ assignmentDueDatesPage.assertDueFor(R.string.everyone_else)
+
+ val dueDateForEveryoneElse = "Dec 12 at 1:30 AM"
+ val dueDateForStudentSpecially = "Dec 12 at 9:30 AM"
+ Log.d(STEP_TAG,"Assert that the there is a due date with '$dueDateForEveryoneElse' value and another one with '$dueDateForStudentSpecially'.")
+ assignmentDueDatesPage.assertDueDateTime("Due $dueDateForEveryoneElse")
+ assignmentDueDatesPage.assertDueDateTime("Due $dueDateForStudentSpecially")
}
}
\ No newline at end of file
diff --git a/apps/teacher/src/androidTest/java/com/instructure/teacher/ui/pages/AssignmentDetailsPage.kt b/apps/teacher/src/androidTest/java/com/instructure/teacher/ui/pages/AssignmentDetailsPage.kt
index beae94a041..3d68c6d2c7 100644
--- a/apps/teacher/src/androidTest/java/com/instructure/teacher/ui/pages/AssignmentDetailsPage.kt
+++ b/apps/teacher/src/androidTest/java/com/instructure/teacher/ui/pages/AssignmentDetailsPage.kt
@@ -17,12 +17,16 @@ package com.instructure.teacher.ui.pages
import androidx.test.InstrumentationRegistry
-import androidx.test.espresso.matcher.ViewMatchers.withId
+import androidx.test.espresso.web.assertion.WebViewAssertions
+import androidx.test.espresso.web.sugar.Web
+import androidx.test.espresso.web.webdriver.DriverAtoms
+import androidx.test.espresso.web.webdriver.Locator
import com.instructure.canvasapi2.models.Assignment
import com.instructure.dataseeding.model.AssignmentApiModel
import com.instructure.espresso.*
import com.instructure.espresso.page.*
import com.instructure.teacher.R
+import org.hamcrest.Matchers
@Suppress("unused")
class AssignmentDetailsPage : BasePage(pageResId = R.id.assignmentDetailsPage) {
@@ -143,6 +147,15 @@ class AssignmentDetailsPage : BasePage(pageResId = R.id.assignmentDetailsPage) {
pointsTextView.assertContainsText(newAssignmentPoints)
}
+ fun assertDisplaysDescription(text: String) {
+ descriptionWebView.assertVisible()
+ Web.onWebView().withElement(
+ DriverAtoms.findElement(
+ Locator.XPATH,
+ "//div[@id='content' and contains(text(),'$text')]"
+ )
+ ).check(WebViewAssertions.webMatches(DriverAtoms.getText(), Matchers.comparesEqualTo(text))) }
+
fun assertNeedsGrading(actual: Int = 1, outOf: Int = 1) {
val resources = InstrumentationRegistry.getTargetContext()
ungradedDonutWrapper.assertHasContentDescription(resources.getString(R.string.content_description_submission_donut_needs_grading).format(actual, outOf))
@@ -158,7 +171,11 @@ class AssignmentDetailsPage : BasePage(pageResId = R.id.assignmentDetailsPage) {
gradedDonutWrapper.assertHasContentDescription(resources.getString(R.string.content_description_submission_donut_graded).format(actual, outOf))
}
- fun scrollToSubmissionType() {
+ fun viewAllSubmission() {
+ onView(withId(R.id.viewAllSubmissions)).click()
+ }
+
+ private fun scrollToSubmissionType() {
scrollTo(R.id.submissionTypesTextView)
}
@@ -178,6 +195,10 @@ class AssignmentDetailsPage : BasePage(pageResId = R.id.assignmentDetailsPage) {
}
}
+ fun assertMultipleDueDates() {
+ onView(withId(R.id.otherDueDateTextView) + withText(R.string.multiple_due_dates)).assertDisplayed()
+ }
+
private fun assertAssignmentDetails(assignmentName: String, published: Boolean) {
assignmentNameTextView.assertHasText(assignmentName)
assertPublishedStatus(published)
diff --git a/apps/teacher/src/androidTest/java/com/instructure/teacher/ui/pages/AssignmentDueDatesPage.kt b/apps/teacher/src/androidTest/java/com/instructure/teacher/ui/pages/AssignmentDueDatesPage.kt
index 230336a2fc..11a8f521a4 100644
--- a/apps/teacher/src/androidTest/java/com/instructure/teacher/ui/pages/AssignmentDueDatesPage.kt
+++ b/apps/teacher/src/androidTest/java/com/instructure/teacher/ui/pages/AssignmentDueDatesPage.kt
@@ -16,11 +16,9 @@
package com.instructure.teacher.ui.pages
import com.instructure.espresso.*
-import com.instructure.espresso.page.BasePage
+import com.instructure.espresso.page.*
-import com.instructure.espresso.page.onViewWithId
-import com.instructure.espresso.page.onViewWithText
import com.instructure.teacher.R
@Suppress("unused")
@@ -41,6 +39,10 @@ class AssignmentDueDatesPage : BasePage(pageResId = R.id.dueDatesPage) {
onViewWithId(R.id.dueDateTextView).assertDisplayed().assertHasText(R.string.no_due_date)
}
+ fun assertDueDatesCount(expectedCount: Int) {
+ recyclerView.check(RecyclerViewItemCountAssertion(expectedCount))
+ }
+
fun assertDisplaysSingleDueDate() {
recyclerView.check(RecyclerViewItemCountAssertion(1))
assertLabelsDisplayedOnce()
@@ -49,6 +51,18 @@ class AssignmentDueDatesPage : BasePage(pageResId = R.id.dueDatesPage) {
onViewWithId(R.id.dueDateTextView).assertDisplayed().assertNotHasText(R.string.no_due_date)
}
+ fun assertDueFor(dueForString: Int) {
+ onView(withId(R.id.dueForTextView) + withText(dueForString)).assertDisplayed()
+ }
+
+ fun assertDueFor(dueForString: String) {
+ onView(withId(R.id.dueForTextView) + withText(dueForString)).assertDisplayed()
+ }
+
+ fun assertDueDateTime(dueDateString: String) {
+ onView(withId(R.id.dueDateTextView) + withText(dueDateString))
+ }
+
fun assertDisplaysAvailabilityDates() {
recyclerView.check(RecyclerViewItemCountAssertion(1))
assertLabelsDisplayedOnce()
diff --git a/apps/teacher/src/androidTest/java/com/instructure/teacher/ui/pages/EditAssignmentDetailsPage.kt b/apps/teacher/src/androidTest/java/com/instructure/teacher/ui/pages/EditAssignmentDetailsPage.kt
index cc0c5db1d7..56d6ab030e 100644
--- a/apps/teacher/src/androidTest/java/com/instructure/teacher/ui/pages/EditAssignmentDetailsPage.kt
+++ b/apps/teacher/src/androidTest/java/com/instructure/teacher/ui/pages/EditAssignmentDetailsPage.kt
@@ -16,20 +16,21 @@
package com.instructure.teacher.ui.pages
+import android.widget.DatePicker
+import android.widget.TimePicker
+import androidx.test.espresso.Espresso
import androidx.test.espresso.assertion.ViewAssertions.matches
import androidx.test.espresso.contrib.PickerActions
import androidx.test.espresso.matcher.ViewMatchers
import androidx.test.espresso.matcher.ViewMatchers.withEffectiveVisibility
-import android.widget.DatePicker
-import android.widget.TimePicker
-import androidx.test.espresso.Espresso
-import com.instructure.canvasapi2.utils.DateHelper
-import com.instructure.espresso.*
import com.instructure.canvas.espresso.has
import com.instructure.canvas.espresso.hasTextInputLayoutErrorText
import com.instructure.canvas.espresso.withIndex
+import com.instructure.canvasapi2.utils.DateHelper
+import com.instructure.espresso.*
import com.instructure.espresso.page.*
import com.instructure.teacher.R
+import com.instructure.teacher.ui.utils.TypeInRCETextEditor
import com.instructure.teacher.view.AssignmentOverrideView
import org.hamcrest.CoreMatchers.allOf
import org.hamcrest.Matchers
@@ -46,6 +47,7 @@ class EditAssignmentDetailsPage : BasePage() {
private val descriptionWebView by OnViewWithId(R.id.descriptionWebView, autoAssert = false)
private val noDescriptionTextView by OnViewWithId(R.id.noDescriptionTextView, autoAssert = false)
private val overlayContainer by OnViewWithId(R.id.overrideContainer, autoAssert = false)
+ private val contentRceView by WaitForViewWithId(R.id.rce_webView, autoAssert = false)
fun saveAssignment() {
saveButton.click()
@@ -63,14 +65,12 @@ class EditAssignmentDetailsPage : BasePage() {
fun editAssignmentName(newName: String) {
assignmentNameEditText.replaceText(newName)
Espresso.closeSoftKeyboard()
- saveAssignment()
}
fun editAssignmentPoints(newPoints: Double) {
val df = DecimalFormat("#")
pointsPossibleEditText.replaceText(df.format(newPoints))
Espresso.closeSoftKeyboard()
- saveAssignment()
}
fun editAssignees() = waitScrollClick(R.id.assignTo)
@@ -134,4 +134,17 @@ class EditAssignmentDetailsPage : BasePage() {
fun assertNoAssigneesErrorShown() {
onView(withIndex(withId(R.id.assignToTextInput), 1)).check(matches(hasTextInputLayoutErrorText(R.string.assignee_blank_error)))
}
+
+ fun clickOnDisplayGradeAsSpinner() {
+ onView(withId(R.id.displayGradeAsSpinner)).click()
+ }
+
+ fun selectGradeType(gradeType: String) {
+ onView(withText(gradeType)).click()
+ }
+
+ fun editDescription(newDescription: String) {
+ contentRceView.perform(TypeInRCETextEditor(newDescription))
+ }
+
}
From fe00b27bb74106d7efd29aa66d3f9609ea8634eb Mon Sep 17 00:00:00 2001
From: Akos Hermann <72087159+hermannakos@users.noreply.github.com>
Date: Thu, 29 Sep 2022 11:16:14 +0200
Subject: [PATCH 10/73] [Hackweek][Student] AssignmentList filters (#1724)
refs: none
affects: Student
release note: Added the option to filter assignments on the Assignment List.
test plan: On the toolbar the filter button should be visible. When setting a filter a badge should be added to the icon.
Check possible filters:
- All
- Late
- Missing
- Graded
- Upcoming
---
.../AssignmentListByDateRecyclerAdapter.kt | 21 ++-
.../AssignmentListByTypeRecyclerAdapter.kt | 21 ++-
.../AssignmentListRecyclerAdapter.kt | 26 +++-
.../fragment/AssignmentListFragment.kt | 128 +++++++++++++-----
.../main/res/menu/menu_assignment_list.xml | 24 ++++
apps/student/src/main/res/values/arrays.xml | 8 ++
libs/pandares/src/main/res/values/strings.xml | 8 ++
.../instructure/pandautils/views/EmptyView.kt | 13 +-
8 files changed, 206 insertions(+), 43 deletions(-)
create mode 100644 apps/student/src/main/res/menu/menu_assignment_list.xml
diff --git a/apps/student/src/main/java/com/instructure/student/adapter/assignment/AssignmentListByDateRecyclerAdapter.kt b/apps/student/src/main/java/com/instructure/student/adapter/assignment/AssignmentListByDateRecyclerAdapter.kt
index e05ea31f9d..51dc99259b 100644
--- a/apps/student/src/main/java/com/instructure/student/adapter/assignment/AssignmentListByDateRecyclerAdapter.kt
+++ b/apps/student/src/main/java/com/instructure/student/adapter/assignment/AssignmentListByDateRecyclerAdapter.kt
@@ -18,8 +18,11 @@
package com.instructure.student.adapter.assignment
import android.content.Context
-import com.instructure.canvasapi2.models.*
-import com.instructure.canvasapi2.utils.*
+import com.instructure.canvasapi2.models.Assignment
+import com.instructure.canvasapi2.models.AssignmentGroup
+import com.instructure.canvasapi2.models.CanvasContext
+import com.instructure.canvasapi2.utils.filterWithQuery
+import com.instructure.canvasapi2.utils.toDate
import com.instructure.pandarecycler.util.GroupSortedList
import com.instructure.pandarecycler.util.Types
import com.instructure.student.R
@@ -35,8 +38,9 @@ class AssignmentListByDateRecyclerAdapter(
context: Context,
canvasContext: CanvasContext,
adapterToAssignmentsCallback: AdapterToAssignmentsCallback,
- isTesting: Boolean = false
-) : AssignmentListRecyclerAdapter(context, canvasContext, adapterToAssignmentsCallback, isTesting) {
+ isTesting: Boolean = false,
+ filter: AssignmentListFilter = AssignmentListFilter.ALL
+) : AssignmentListRecyclerAdapter(context, canvasContext, adapterToAssignmentsCallback, isTesting, filter) {
private val overdue = AssignmentGroup(name = context.getString(R.string.overdueAssignments), position = HEADER_POSITION_OVERDUE)
private val upcoming = AssignmentGroup(name = context.getString(R.string.upcomingAssignments), position = HEADER_POSITION_UPCOMING)
@@ -69,6 +73,15 @@ class AssignmentListByDateRecyclerAdapter(
// endtodo
assignmentGroup.assignments
.filterWithQuery(searchQuery, Assignment::name)
+ .filter {
+ when (filter) {
+ AssignmentListFilter.ALL -> true
+ AssignmentListFilter.MISSING -> it.isMissing()
+ AssignmentListFilter.LATE -> it.submission?.late ?: false
+ AssignmentListFilter.GRADED -> it.submission?.isGraded ?: false
+ AssignmentListFilter.UPCOMING -> !it.isSubmitted && it.dueDate?.after(Date()) ?: false
+ }
+ }
.forEach { assignment ->
val dueAt = assignment.dueAt
val submission = assignment.submission
diff --git a/apps/student/src/main/java/com/instructure/student/adapter/assignment/AssignmentListByTypeRecyclerAdapter.kt b/apps/student/src/main/java/com/instructure/student/adapter/assignment/AssignmentListByTypeRecyclerAdapter.kt
index 569115e6c1..15f21199ee 100644
--- a/apps/student/src/main/java/com/instructure/student/adapter/assignment/AssignmentListByTypeRecyclerAdapter.kt
+++ b/apps/student/src/main/java/com/instructure/student/adapter/assignment/AssignmentListByTypeRecyclerAdapter.kt
@@ -24,17 +24,30 @@ import com.instructure.canvasapi2.utils.filterWithQuery
import com.instructure.pandarecycler.util.GroupSortedList
import com.instructure.pandarecycler.util.Types
import com.instructure.student.interfaces.AdapterToAssignmentsCallback
+import java.util.*
class AssignmentListByTypeRecyclerAdapter(
context: Context,
canvasContext: CanvasContext,
adapterToAssignmentsCallback: AdapterToAssignmentsCallback,
- isTesting: Boolean = false
-) : AssignmentListRecyclerAdapter(context, canvasContext, adapterToAssignmentsCallback, isTesting) {
+ isTesting: Boolean = false,
+ filter: AssignmentListFilter = AssignmentListFilter.ALL
+) : AssignmentListRecyclerAdapter(context, canvasContext, adapterToAssignmentsCallback, isTesting, filter) {
override fun populateData() {
- assignmentGroups.forEach { assignmentGroup ->
- val filteredAssignments = assignmentGroup.assignments.filterWithQuery(searchQuery, Assignment::name)
+ assignmentGroups
+ .forEach { assignmentGroup ->
+ val filteredAssignments = assignmentGroup.assignments
+ .filterWithQuery(searchQuery, Assignment::name)
+ .filter {
+ when (filter) {
+ AssignmentListFilter.ALL -> true
+ AssignmentListFilter.MISSING -> it.isMissing()
+ AssignmentListFilter.LATE -> it.submission?.late ?: false
+ AssignmentListFilter.GRADED -> it.submission?.isGraded ?: false
+ AssignmentListFilter.UPCOMING -> !it.isSubmitted && it.dueDate?.after(Date()) ?: false
+ }
+ }
addOrUpdateAllItems(assignmentGroup, filteredAssignments)
}
isAllPagesLoaded = true
diff --git a/apps/student/src/main/java/com/instructure/student/adapter/assignment/AssignmentListRecyclerAdapter.kt b/apps/student/src/main/java/com/instructure/student/adapter/assignment/AssignmentListRecyclerAdapter.kt
index 57620483b9..4118c9024c 100644
--- a/apps/student/src/main/java/com/instructure/student/adapter/assignment/AssignmentListRecyclerAdapter.kt
+++ b/apps/student/src/main/java/com/instructure/student/adapter/assignment/AssignmentListRecyclerAdapter.kt
@@ -23,7 +23,9 @@ import com.instructure.canvasapi2.StatusCallback
import com.instructure.canvasapi2.managers.AssignmentManager
import com.instructure.canvasapi2.managers.CourseManager
import com.instructure.canvasapi2.models.*
-import com.instructure.canvasapi2.utils.*
+import com.instructure.canvasapi2.utils.ApiType
+import com.instructure.canvasapi2.utils.LinkHeaders
+import com.instructure.canvasapi2.utils.Logger
import com.instructure.canvasapi2.utils.weave.WeaveJob
import com.instructure.canvasapi2.utils.weave.awaitApi
import com.instructure.canvasapi2.utils.weave.catch
@@ -45,7 +47,8 @@ abstract class AssignmentListRecyclerAdapter (
context: Context,
private val canvasContext: CanvasContext,
private val adapterToAssignmentsCallback: AdapterToAssignmentsCallback,
- isTesting: Boolean = false
+ isTesting: Boolean = false,
+ filter: AssignmentListFilter = AssignmentListFilter.ALL
) : ExpandableRecyclerAdapter(
context,
AssignmentGroup::class.java,
@@ -57,6 +60,16 @@ abstract class AssignmentListRecyclerAdapter (
private var apiJob: WeaveJob? = null
protected var assignmentGroups: List = emptyList()
+ var filter: AssignmentListFilter = AssignmentListFilter.ALL
+ set(value) {
+ field = value
+ if (isAllPagesLoaded) {
+ clear()
+ populateData()
+ onCallbackFinished(ApiType.CACHE)
+ }
+ }
+
var searchQuery: String = ""
set(value) {
field = value
@@ -70,6 +83,7 @@ abstract class AssignmentListRecyclerAdapter (
init {
isExpandedByDefault = true
isDisplayEmptyCell = true
+ this.filter = filter
if (!isTesting) loadData()
}
@@ -230,4 +244,12 @@ abstract class AssignmentListRecyclerAdapter (
super.cancel()
apiJob?.cancel()
}
+}
+
+enum class AssignmentListFilter {
+ ALL,
+ LATE,
+ MISSING,
+ GRADED,
+ UPCOMING
}
\ No newline at end of file
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 91edffab83..a0b0b17c4d 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
@@ -20,13 +20,17 @@ package com.instructure.student.fragment
import android.content.DialogInterface
import android.content.res.Configuration
import android.os.Bundle
+import android.os.Handler
import android.view.LayoutInflater
+import android.view.MenuItem
import android.view.View
import android.view.ViewGroup
import android.widget.AdapterView
import androidx.annotation.StringRes
import androidx.appcompat.app.AlertDialog
import com.google.android.material.appbar.AppBarLayout
+import com.google.android.material.badge.BadgeDrawable
+import com.google.android.material.badge.BadgeUtils
import com.instructure.canvasapi2.models.Assignment
import com.instructure.canvasapi2.models.CanvasContext
import com.instructure.canvasapi2.models.GradingPeriod
@@ -44,6 +48,7 @@ import com.instructure.student.R
import com.instructure.student.adapter.TermSpinnerAdapter
import com.instructure.student.adapter.assignment.AssignmentListByDateRecyclerAdapter
import com.instructure.student.adapter.assignment.AssignmentListByTypeRecyclerAdapter
+import com.instructure.student.adapter.assignment.AssignmentListFilter
import com.instructure.student.adapter.assignment.AssignmentListRecyclerAdapter
import com.instructure.student.interfaces.AdapterToAssignmentsCallback
import com.instructure.student.mobius.assignmentDetails.ui.AssignmentDetailsFragment
@@ -51,6 +56,7 @@ import com.instructure.student.router.RouteMatcher
import com.instructure.student.util.StudentPrefs
import kotlinx.android.synthetic.main.assignment_list_layout.*
+@com.google.android.material.badge.ExperimentalBadgeUtils
@ScreenView(SCREEN_VIEW_ASSIGNMENT_LIST)
@PageView(url = "{canvasContext}/assignments")
class AssignmentListFragment : ParentFragment(), Bookmarkable {
@@ -60,6 +66,11 @@ class AssignmentListFragment : ParentFragment(), Bookmarkable {
private lateinit var recyclerAdapter: AssignmentListRecyclerAdapter
private var termAdapter: TermSpinnerAdapter? = null
+ private var filterPosition = 0
+ private var filter = AssignmentListFilter.ALL
+
+ private var badgeDrawable: BadgeDrawable? = null
+
private var sortOrder: AssignmentsSortOrder
get() {
val preferenceKey = StudentPrefs.getString("sortBy_${canvasContext.contextId}", AssignmentsSortOrder.SORT_BY_TIME.preferenceKey)
@@ -91,7 +102,7 @@ class AssignmentListFragment : ParentFragment(), Bookmarkable {
}
override fun onRefreshFinished() {
- if(!isAdded) return // Refresh can finish after user has left screen, causing emptyView to be null
+ if (!isAdded) return // Refresh can finish after user has left screen, causing emptyView to be null
setRefreshing(false)
if (recyclerAdapter.size() == 0) {
setEmptyView(emptyView, R.drawable.ic_panda_space, R.string.noAssignments, R.string.noAssignmentsSubtext)
@@ -116,12 +127,12 @@ class AssignmentListFragment : ParentFragment(), Bookmarkable {
sortByButton.contentDescription = getString(sortOrder.contentDescriptionRes)
configureRecyclerView(
- view,
- requireContext(),
- recyclerAdapter,
- R.id.swipeRefreshLayout,
- R.id.emptyView,
- R.id.listView
+ view,
+ requireContext(),
+ recyclerAdapter,
+ R.id.swipeRefreshLayout,
+ R.id.emptyView,
+ R.id.listView
)
appbar.addOnOffsetChangedListener(AppBarLayout.OnOffsetChangedListener { _, i ->
@@ -136,11 +147,23 @@ class AssignmentListFragment : ParentFragment(), Bookmarkable {
setupSortByButton()
}
+ override fun onHiddenChanged(hidden: Boolean) {
+ super.onHiddenChanged(hidden)
+ if (!hidden) {
+ updateBadge()
+ }
+ }
+
+ override fun onResume() {
+ super.onResume()
+ updateBadge()
+ }
+
private fun createRecyclerAdapter(): AssignmentListRecyclerAdapter {
return if (sortOrder == AssignmentsSortOrder.SORT_BY_TIME) {
- AssignmentListByDateRecyclerAdapter(requireContext(), canvasContext, adapterToAssignmentsCallback)
+ AssignmentListByDateRecyclerAdapter(requireContext(), canvasContext, adapterToAssignmentsCallback, filter = filter)
} else {
- AssignmentListByTypeRecyclerAdapter(requireContext(), canvasContext, adapterToAssignmentsCallback)
+ AssignmentListByTypeRecyclerAdapter(requireContext(), canvasContext, adapterToAssignmentsCallback, filter = filter)
}
}
@@ -148,10 +171,10 @@ class AssignmentListFragment : ParentFragment(), Bookmarkable {
sortByButton.onClick {
val checkedItemIndex = sortOrder.index
AlertDialog.Builder(requireContext(), R.style.AccentDialogTheme)
- .setTitle(R.string.sortByDialogTitle)
- .setSingleChoiceItems(R.array.assignmentsSortByOptions, checkedItemIndex, this@AssignmentListFragment::sortOrderSelected)
- .setNegativeButton(R.string.sortByDialogCancel) { dialog, _ -> dialog.dismiss() }
- .show()
+ .setTitle(R.string.sortByDialogTitle)
+ .setSingleChoiceItems(R.array.assignmentsSortByOptions, checkedItemIndex, this@AssignmentListFragment::sortOrderSelected)
+ .setNegativeButton(R.string.sortByDialogCancel) { dialog, _ -> dialog.dismiss() }
+ .show()
}
}
@@ -169,8 +192,40 @@ class AssignmentListFragment : ParentFragment(), Bookmarkable {
}
}
+ private fun showAssignmentFilterDialog() {
+ AlertDialog.Builder(requireContext(), R.style.AccentDialogTheme)
+ .setTitle(R.string.filterAssignmentDialogTitle)
+ .setSingleChoiceItems(R.array.assignmentsFilterOptions, filterPosition, this@AssignmentListFragment::filterSelected)
+ .setNegativeButton(R.string.filterAssignmentsDialogCancel) { dialog, _ -> dialog.dismiss() }
+ .show()
+ }
+
+ private fun filterSelected(dialog: DialogInterface, index: Int) {
+ dialog.dismiss()
+ filterPosition = index
+ filter = AssignmentListFilter.values()[index]
+ recyclerAdapter.filter = filter
+ updateBadge()
+ }
+
+ private fun updateBadge() {
+ Handler().postDelayed({
+ if (badgeDrawable == null) {
+ badgeDrawable = BadgeDrawable.create(requireContext()).apply {
+ backgroundColor = ThemePrefs.accentColor
+ }
+ }
+ if (filterPosition == 0) {
+ BadgeUtils.detachBadgeDrawable(badgeDrawable, toolbar, R.id.menu_filter_assignments)
+ } else {
+ BadgeUtils.attachBadgeDrawable(badgeDrawable!!, toolbar, R.id.menu_filter_assignments)
+ }
+ }, 100)
+ }
+
+
override fun applyTheme() {
- setupToolbarMenu(toolbar)
+ setupToolbarMenu(toolbar, R.menu.menu_assignment_list)
toolbar.title = title()
toolbar.setupAsBackButton(this)
toolbar.addSearch(getString(R.string.searchAssignmentsHint)) { query ->
@@ -184,13 +239,22 @@ class AssignmentListFragment : ParentFragment(), Bookmarkable {
ViewStyler.themeToolbarColored(requireActivity(), toolbar, canvasContext)
}
+ override fun onOptionsItemSelected(item: MenuItem): Boolean {
+ when (item.itemId) {
+ R.id.menu_filter_assignments -> {
+ showAssignmentFilterDialog()
+ }
+ }
+ return super.onOptionsItemSelected(item)
+ }
+
private fun setupGradingPeriods(periods: List) {
val hasGradingPeriods = periods.isNotEmpty()
val adapter = TermSpinnerAdapter(
- requireContext(),
- android.R.layout.simple_spinner_dropdown_item,
- periods + allTermsGradingPeriod,
- hasGradingPeriods
+ requireContext(),
+ android.R.layout.simple_spinner_dropdown_item,
+ periods + allTermsGradingPeriod,
+ hasGradingPeriods
)
termSpinner.isEnabled = hasGradingPeriods
termAdapter = adapter
@@ -227,12 +291,12 @@ class AssignmentListFragment : ParentFragment(), Bookmarkable {
override fun onConfigurationChanged(newConfig: Configuration) {
super.onConfigurationChanged(newConfig)
configureRecyclerView(
- requireView(),
- requireContext(),
- recyclerAdapter,
- R.id.swipeRefreshLayout,
- R.id.emptyView,
- R.id.listView,
+ requireView(),
+ requireContext(),
+ recyclerAdapter,
+ R.id.swipeRefreshLayout,
+ R.id.emptyView,
+ R.id.listView,
R.string.noAssignments
)
if (recyclerAdapter.size() == 0) {
@@ -274,18 +338,18 @@ class AssignmentListFragment : ParentFragment(), Bookmarkable {
}
enum class AssignmentsSortOrder(
- val index: Int,
- val preferenceKey: String,
- @StringRes val buttonTextRes: Int,
- @StringRes val contentDescriptionRes: Int,
- @StringRes val orderSelectedAnnouncement: Int,
- val analyticsKey: String) {
+ val index: Int,
+ val preferenceKey: String,
+ @StringRes val buttonTextRes: Int,
+ @StringRes val contentDescriptionRes: Int,
+ @StringRes val orderSelectedAnnouncement: Int,
+ val analyticsKey: String) {
SORT_BY_TIME(0, "time", R.string.sortByTime, R.string.a11y_sortByTimeButton,
- R.string.a11y_assignmentsSortedByTime, AnalyticsEventConstants.ASSIGNMENT_LIST_SORT_BY_TIME_SELECTED),
+ R.string.a11y_assignmentsSortedByTime, AnalyticsEventConstants.ASSIGNMENT_LIST_SORT_BY_TIME_SELECTED),
SORT_BY_TYPE(1, "type", R.string.sortByType, R.string.a11y_sortByTypeButton,
- R.string.a11y_assignmentsSortedByType, AnalyticsEventConstants.ASSIGNMENT_LIST_SORT_BY_TYPE_SELECTED);
+ R.string.a11y_assignmentsSortedByType, AnalyticsEventConstants.ASSIGNMENT_LIST_SORT_BY_TYPE_SELECTED);
companion object {
fun fromPreferenceKey(key: String?): AssignmentsSortOrder {
diff --git a/apps/student/src/main/res/menu/menu_assignment_list.xml b/apps/student/src/main/res/menu/menu_assignment_list.xml
new file mode 100644
index 0000000000..514498c334
--- /dev/null
+++ b/apps/student/src/main/res/menu/menu_assignment_list.xml
@@ -0,0 +1,24 @@
+
+
+
\ No newline at end of file
diff --git a/apps/student/src/main/res/values/arrays.xml b/apps/student/src/main/res/values/arrays.xml
index f11c9955c2..264de5aee2 100644
--- a/apps/student/src/main/res/values/arrays.xml
+++ b/apps/student/src/main/res/values/arrays.xml
@@ -20,4 +20,12 @@
- @string/sortByDialogTimeOption
- @string/sortByDialogTypeOption
+
+
+ - @string/filterAssignmentAll
+ - @string/filterAssignmentLate
+ - @string/filterAssignmentMissing
+ - @string/filterAssignmentGraded
+ - @string/filterAssignmentUpcoming
+
\ No newline at end of file
diff --git a/libs/pandares/src/main/res/values/strings.xml b/libs/pandares/src/main/res/values/strings.xml
index 4e54b56730..6ab5281a96 100644
--- a/libs/pandares/src/main/res/values/strings.xml
+++ b/libs/pandares/src/main/res/values/strings.xml
@@ -1309,4 +1309,12 @@
%1$s, %2$s
Cancel Upload?
This will cancel your upload.
+ Filter assignments
+ Filter Assignments
+ Cancel
+ All
+ Late
+ Missing
+ Graded
+ Upcoming
diff --git a/libs/pandautils/src/main/java/com/instructure/pandautils/views/EmptyView.kt b/libs/pandautils/src/main/java/com/instructure/pandautils/views/EmptyView.kt
index b80cd60247..fe8900657c 100644
--- a/libs/pandautils/src/main/java/com/instructure/pandautils/views/EmptyView.kt
+++ b/libs/pandautils/src/main/java/com/instructure/pandautils/views/EmptyView.kt
@@ -30,7 +30,6 @@ import androidx.core.content.ContextCompat
import com.instructure.pandarecycler.interfaces.EmptyInterface
import com.instructure.pandautils.R
import com.instructure.pandautils.utils.isGone
-import com.instructure.pandautils.utils.isVisible
import com.instructure.pandautils.utils.setGone
import com.instructure.pandautils.utils.setVisible
import kotlinx.android.synthetic.main.empty_view.view.*
@@ -79,6 +78,18 @@ open class EmptyView @JvmOverloads constructor(
// we don't have an image for the empty state we want the title to be centered instead.
if (image.isGone) {
centerTitle()
+ } else {
+ resetTitle()
+ }
+ }
+
+ private fun resetTitle() {
+ emptyViewLayout?.let {
+ val constraintSet = ConstraintSet()
+ constraintSet.clone(it)
+ constraintSet.connect(R.id.textViews, ConstraintSet.TOP, R.id.titleTop, ConstraintSet.BOTTOM)
+ constraintSet.clear(R.id.textViews, ConstraintSet.BOTTOM)
+ constraintSet.applyTo(it)
}
}
From 4b0ed70e98608521c8daed7952f87d327b157290 Mon Sep 17 00:00:00 2001
From: Tamas Kozmer <72397075+tamaskozmer@users.noreply.github.com>
Date: Mon, 3 Oct 2022 14:04:42 +0200
Subject: [PATCH 11/73] [MBL-16269][Student][Teacher] Updated PSPDFKit (#1728)
refs: MBL-16269
affects: Student, Teacher
release note: None
* Upgrade to 8.3.0
* Updated vault commit ref.
* Removed text from stamp picker.
---
android-vault | 2 +-
apps/student/build.gradle | 5 -----
.../src/main/java/com/instructure/student/util/FileUtils.kt | 1 +
apps/student/src/main/res/values/styles.xml | 2 +-
apps/teacher/build.gradle | 5 -----
apps/teacher/src/main/res/values/styles.xml | 2 +-
buildSrc/src/main/java/GlobalDependencies.kt | 2 +-
libs/annotations/src/main/res/values-ar/strings.xml | 1 +
.../annotations/src/main/res/values-b+da+instk12/strings.xml | 1 +
.../src/main/res/values-b+en+AU+unimelb/strings.xml | 1 +
.../src/main/res/values-b+en+GB+instukhe/strings.xml | 1 +
.../annotations/src/main/res/values-b+nb+instk12/strings.xml | 1 +
.../annotations/src/main/res/values-b+sv+instk12/strings.xml | 1 +
libs/annotations/src/main/res/values-b+zh+HK/strings.xml | 1 +
libs/annotations/src/main/res/values-b+zh+Hans/strings.xml | 1 +
libs/annotations/src/main/res/values-b+zh+Hant/strings.xml | 1 +
libs/annotations/src/main/res/values-ca/strings.xml | 1 +
libs/annotations/src/main/res/values-cy/strings.xml | 1 +
libs/annotations/src/main/res/values-da/strings.xml | 1 +
libs/annotations/src/main/res/values-de/strings.xml | 1 +
libs/annotations/src/main/res/values-en-rAU/strings.xml | 1 +
libs/annotations/src/main/res/values-en-rCA/strings.xml | 1 +
libs/annotations/src/main/res/values-en-rCY/strings.xml | 1 +
libs/annotations/src/main/res/values-en-rGB/strings.xml | 1 +
libs/annotations/src/main/res/values-es-rES/strings.xml | 1 +
libs/annotations/src/main/res/values-es/strings.xml | 1 +
libs/annotations/src/main/res/values-fi/strings.xml | 1 +
libs/annotations/src/main/res/values-fr-rCA/strings.xml | 1 +
libs/annotations/src/main/res/values-fr/strings.xml | 1 +
libs/annotations/src/main/res/values-ht/strings.xml | 1 +
libs/annotations/src/main/res/values-is/strings.xml | 1 +
libs/annotations/src/main/res/values-it/strings.xml | 1 +
libs/annotations/src/main/res/values-ja/strings.xml | 1 +
libs/annotations/src/main/res/values-mi/strings.xml | 1 +
libs/annotations/src/main/res/values-nb/strings.xml | 1 +
libs/annotations/src/main/res/values-nl/strings.xml | 1 +
libs/annotations/src/main/res/values-pl/strings.xml | 1 +
libs/annotations/src/main/res/values-pt-rBR/strings.xml | 1 +
libs/annotations/src/main/res/values-pt-rPT/strings.xml | 1 +
libs/annotations/src/main/res/values-ru/strings.xml | 1 +
libs/annotations/src/main/res/values-sl/strings.xml | 1 +
libs/annotations/src/main/res/values-sv/strings.xml | 1 +
libs/annotations/src/main/res/values-th/strings.xml | 1 +
libs/annotations/src/main/res/values-vi/strings.xml | 1 +
libs/annotations/src/main/res/values-zh/strings.xml | 1 +
libs/annotations/src/main/res/values/strings.xml | 1 +
46 files changed, 44 insertions(+), 14 deletions(-)
diff --git a/android-vault b/android-vault
index eea182a4b6..ceed6c1136 160000
--- a/android-vault
+++ b/android-vault
@@ -1 +1 @@
-Subproject commit eea182a4b626678f271e42d73c100dd53a84611b
+Subproject commit ceed6c1136054f1aa424382d37185857acf19d3d
diff --git a/apps/student/build.gradle b/apps/student/build.gradle
index 10be6c70f8..7d86ad0261 100644
--- a/apps/student/build.gradle
+++ b/apps/student/build.gradle
@@ -182,11 +182,6 @@ android {
resolutionStrategy.force Libs.ANDROIDX_ANNOTATION
resolutionStrategy.force Libs.KOTLIN_COROUTINES_CORE
-
- // Some libraries want to resolve never versions of this library that requires targetSdkVersion 31.
- // Once we upgrade the targetSdkVersion this should be removed.
- resolutionStrategy.force 'androidx.core:core:1.6.0'
- resolutionStrategy.force 'androidx.core:core-ktx:1.6.0'
}
/*
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 428ccbd03d..c50a961d90 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
@@ -84,6 +84,7 @@ object FileUtils {
pspdfActivityConfiguration = PdfActivityConfiguration.Builder(context)
.scrollDirection(PageScrollDirection.HORIZONTAL)
.showThumbnailGrid()
+ .setDocumentInfoViewSeparated(false)
.setThumbnailBarMode(ThumbnailBarMode.THUMBNAIL_BAR_MODE_PINNED)
.enableDocumentEditor()
.enabledAnnotationTools(annotationCreationList)
diff --git a/apps/student/src/main/res/values/styles.xml b/apps/student/src/main/res/values/styles.xml
index 0575a7b29c..6b679b34e4 100644
--- a/apps/student/src/main/res/values/styles.xml
+++ b/apps/student/src/main/res/values/styles.xml
@@ -84,7 +84,7 @@
diff --git a/apps/teacher/build.gradle b/apps/teacher/build.gradle
index cd41f1c69c..f0723785de 100644
--- a/apps/teacher/build.gradle
+++ b/apps/teacher/build.gradle
@@ -127,11 +127,6 @@ android {
force 'android.arch.lifecycle:runtime:1.0.3'
force Libs.KOTLIN_COROUTINES_CORE
-
- // Some libraries want to resolve never versions of this library that requires targetSdkVersion 31.
- // Once we upgrade the targetSdkVersion this should be removed.
- force 'androidx.core:core:1.6.0'
- force 'androidx.core:core-ktx:1.6.0'
}
}
diff --git a/apps/teacher/src/main/res/values/styles.xml b/apps/teacher/src/main/res/values/styles.xml
index 4e5327fe3a..30e6841958 100644
--- a/apps/teacher/src/main/res/values/styles.xml
+++ b/apps/teacher/src/main/res/values/styles.xml
@@ -75,7 +75,7 @@
diff --git a/buildSrc/src/main/java/GlobalDependencies.kt b/buildSrc/src/main/java/GlobalDependencies.kt
index 3d6a0fa5b4..830779cea2 100644
--- a/buildSrc/src/main/java/GlobalDependencies.kt
+++ b/buildSrc/src/main/java/GlobalDependencies.kt
@@ -30,7 +30,7 @@ object Versions {
/* Others */
const val APOLLO = "2.5.9"
const val CRASHLYTICS = "17.2.1"
- const val PSPDFKIT = "8.1.0"
+ const val PSPDFKIT = "8.3.0"
const val PHOTO_VIEW = "2.3.0"
const val MOBIUS = "1.2.1"
const val SQLDELIGHT = "1.4.3"
diff --git a/libs/annotations/src/main/res/values-ar/strings.xml b/libs/annotations/src/main/res/values-ar/strings.xml
index 3e25e5b89e..27ab666226 100644
--- a/libs/annotations/src/main/res/values-ar/strings.xml
+++ b/libs/annotations/src/main/res/values-ar/strings.xml
@@ -37,5 +37,6 @@
جارٍ الإرسال
لون الملاحظة
تمت إزالة %1$s بواسطة %2$s
+
diff --git a/libs/annotations/src/main/res/values-b+da+instk12/strings.xml b/libs/annotations/src/main/res/values-b+da+instk12/strings.xml
index f47ffbc676..0f0e98ee0e 100644
--- a/libs/annotations/src/main/res/values-b+da+instk12/strings.xml
+++ b/libs/annotations/src/main/res/values-b+da+instk12/strings.xml
@@ -37,5 +37,6 @@
Sender
Bemærkningens farve
Fjernet %1$s af %2$s
+
diff --git a/libs/annotations/src/main/res/values-b+en+AU+unimelb/strings.xml b/libs/annotations/src/main/res/values-b+en+AU+unimelb/strings.xml
index 1f0bdefc5e..d220a46a5d 100644
--- a/libs/annotations/src/main/res/values-b+en+AU+unimelb/strings.xml
+++ b/libs/annotations/src/main/res/values-b+en+AU+unimelb/strings.xml
@@ -38,5 +38,6 @@
Note Colour
Removed %1$s by %2$s
+
diff --git a/libs/annotations/src/main/res/values-b+en+GB+instukhe/strings.xml b/libs/annotations/src/main/res/values-b+en+GB+instukhe/strings.xml
index d8596c0679..a163fff555 100644
--- a/libs/annotations/src/main/res/values-b+en+GB+instukhe/strings.xml
+++ b/libs/annotations/src/main/res/values-b+en+GB+instukhe/strings.xml
@@ -36,5 +36,6 @@
Sending
Note colour
Removed %1$s by %2$s
+
diff --git a/libs/annotations/src/main/res/values-b+nb+instk12/strings.xml b/libs/annotations/src/main/res/values-b+nb+instk12/strings.xml
index 75023d68ee..a7a39aced7 100644
--- a/libs/annotations/src/main/res/values-b+nb+instk12/strings.xml
+++ b/libs/annotations/src/main/res/values-b+nb+instk12/strings.xml
@@ -37,5 +37,6 @@
Sender
Notatfarge
Fjernet %1$s av %2$s
+
diff --git a/libs/annotations/src/main/res/values-b+sv+instk12/strings.xml b/libs/annotations/src/main/res/values-b+sv+instk12/strings.xml
index d104c77450..43adaf6db0 100644
--- a/libs/annotations/src/main/res/values-b+sv+instk12/strings.xml
+++ b/libs/annotations/src/main/res/values-b+sv+instk12/strings.xml
@@ -36,5 +36,6 @@
Skickar
Anteckningsfärg
Borttagen %1$s av %2$s
+
diff --git a/libs/annotations/src/main/res/values-b+zh+HK/strings.xml b/libs/annotations/src/main/res/values-b+zh+HK/strings.xml
index 9b8c7db567..afcd217149 100644
--- a/libs/annotations/src/main/res/values-b+zh+HK/strings.xml
+++ b/libs/annotations/src/main/res/values-b+zh+HK/strings.xml
@@ -38,5 +38,6 @@
注釋顏色
%1$s 由 %2$s移除
+
diff --git a/libs/annotations/src/main/res/values-b+zh+Hans/strings.xml b/libs/annotations/src/main/res/values-b+zh+Hans/strings.xml
index 0e33334040..a10a031b4c 100644
--- a/libs/annotations/src/main/res/values-b+zh+Hans/strings.xml
+++ b/libs/annotations/src/main/res/values-b+zh+Hans/strings.xml
@@ -38,5 +38,6 @@
注释颜色
移除%1$s者 %2$s
+
diff --git a/libs/annotations/src/main/res/values-b+zh+Hant/strings.xml b/libs/annotations/src/main/res/values-b+zh+Hant/strings.xml
index 9b8c7db567..afcd217149 100644
--- a/libs/annotations/src/main/res/values-b+zh+Hant/strings.xml
+++ b/libs/annotations/src/main/res/values-b+zh+Hant/strings.xml
@@ -38,5 +38,6 @@
注釋顏色
%1$s 由 %2$s移除
+
diff --git a/libs/annotations/src/main/res/values-ca/strings.xml b/libs/annotations/src/main/res/values-ca/strings.xml
index 977537b1e5..057c242d06 100644
--- a/libs/annotations/src/main/res/values-ca/strings.xml
+++ b/libs/annotations/src/main/res/values-ca/strings.xml
@@ -37,5 +37,6 @@
S\'està enviant
Color de la nota
Eliminat el %1$s per %2$s
+
diff --git a/libs/annotations/src/main/res/values-cy/strings.xml b/libs/annotations/src/main/res/values-cy/strings.xml
index f4861d16bf..4d9d848ff9 100644
--- a/libs/annotations/src/main/res/values-cy/strings.xml
+++ b/libs/annotations/src/main/res/values-cy/strings.xml
@@ -37,5 +37,6 @@
Wrthi’n anfon
Lliw y Nodyn
%2$s wedi tynnu %1$s
+
diff --git a/libs/annotations/src/main/res/values-da/strings.xml b/libs/annotations/src/main/res/values-da/strings.xml
index a22a87e323..0ce90ee7a3 100644
--- a/libs/annotations/src/main/res/values-da/strings.xml
+++ b/libs/annotations/src/main/res/values-da/strings.xml
@@ -38,5 +38,6 @@
Bemærkningens farve
Fjernet %1$s af %2$s
+
diff --git a/libs/annotations/src/main/res/values-de/strings.xml b/libs/annotations/src/main/res/values-de/strings.xml
index ddf968d659..4a3fedae2c 100644
--- a/libs/annotations/src/main/res/values-de/strings.xml
+++ b/libs/annotations/src/main/res/values-de/strings.xml
@@ -38,5 +38,6 @@
Hinweisfarbe
%1$s entfernt von %2$s
+
diff --git a/libs/annotations/src/main/res/values-en-rAU/strings.xml b/libs/annotations/src/main/res/values-en-rAU/strings.xml
index 1f0bdefc5e..d220a46a5d 100644
--- a/libs/annotations/src/main/res/values-en-rAU/strings.xml
+++ b/libs/annotations/src/main/res/values-en-rAU/strings.xml
@@ -38,5 +38,6 @@
Note Colour
Removed %1$s by %2$s
+
diff --git a/libs/annotations/src/main/res/values-en-rCA/strings.xml b/libs/annotations/src/main/res/values-en-rCA/strings.xml
index 44e3188fec..9fc2b43536 100644
--- a/libs/annotations/src/main/res/values-en-rCA/strings.xml
+++ b/libs/annotations/src/main/res/values-en-rCA/strings.xml
@@ -36,5 +36,6 @@
Sending
Note Color
Removed %1$s by %2$s
+
diff --git a/libs/annotations/src/main/res/values-en-rCY/strings.xml b/libs/annotations/src/main/res/values-en-rCY/strings.xml
index d8596c0679..a163fff555 100644
--- a/libs/annotations/src/main/res/values-en-rCY/strings.xml
+++ b/libs/annotations/src/main/res/values-en-rCY/strings.xml
@@ -36,5 +36,6 @@
Sending
Note colour
Removed %1$s by %2$s
+
diff --git a/libs/annotations/src/main/res/values-en-rGB/strings.xml b/libs/annotations/src/main/res/values-en-rGB/strings.xml
index bdcd9eca2e..2900a79513 100644
--- a/libs/annotations/src/main/res/values-en-rGB/strings.xml
+++ b/libs/annotations/src/main/res/values-en-rGB/strings.xml
@@ -38,5 +38,6 @@
Note colour
Removed %1$s by %2$s
+
diff --git a/libs/annotations/src/main/res/values-es-rES/strings.xml b/libs/annotations/src/main/res/values-es-rES/strings.xml
index f4025e3bb2..9389dd259f 100644
--- a/libs/annotations/src/main/res/values-es-rES/strings.xml
+++ b/libs/annotations/src/main/res/values-es-rES/strings.xml
@@ -37,5 +37,6 @@
Enviando
Color de la nota
Eliminado %1$s por %2$s
+
diff --git a/libs/annotations/src/main/res/values-es/strings.xml b/libs/annotations/src/main/res/values-es/strings.xml
index 632af30961..1f3ae8cc72 100644
--- a/libs/annotations/src/main/res/values-es/strings.xml
+++ b/libs/annotations/src/main/res/values-es/strings.xml
@@ -37,5 +37,6 @@
Enviando
Color de la nota
Eliminado el %1$s por %2$s
+
diff --git a/libs/annotations/src/main/res/values-fi/strings.xml b/libs/annotations/src/main/res/values-fi/strings.xml
index f4b993cda0..1e86351397 100644
--- a/libs/annotations/src/main/res/values-fi/strings.xml
+++ b/libs/annotations/src/main/res/values-fi/strings.xml
@@ -36,5 +36,6 @@
Lähetetään
Huomautuksen väri
Kohteen %1$s poistaja %2$s
+
diff --git a/libs/annotations/src/main/res/values-fr-rCA/strings.xml b/libs/annotations/src/main/res/values-fr-rCA/strings.xml
index 009090f78a..4fe13175fa 100644
--- a/libs/annotations/src/main/res/values-fr-rCA/strings.xml
+++ b/libs/annotations/src/main/res/values-fr-rCA/strings.xml
@@ -38,5 +38,6 @@
Couleur de note
Retiré %1$s par %2$s
+
diff --git a/libs/annotations/src/main/res/values-fr/strings.xml b/libs/annotations/src/main/res/values-fr/strings.xml
index 32f45da7da..f751bb5b31 100644
--- a/libs/annotations/src/main/res/values-fr/strings.xml
+++ b/libs/annotations/src/main/res/values-fr/strings.xml
@@ -38,5 +38,6 @@
Couleur de la note
Supprimé %1$s par %2$s
+
diff --git a/libs/annotations/src/main/res/values-ht/strings.xml b/libs/annotations/src/main/res/values-ht/strings.xml
index d60711fd1b..4b5c8ff167 100644
--- a/libs/annotations/src/main/res/values-ht/strings.xml
+++ b/libs/annotations/src/main/res/values-ht/strings.xml
@@ -38,5 +38,6 @@
Note Koulè
Elimine %1$s pa %2$s
+
diff --git a/libs/annotations/src/main/res/values-is/strings.xml b/libs/annotations/src/main/res/values-is/strings.xml
index b5531e10de..cbd9350234 100644
--- a/libs/annotations/src/main/res/values-is/strings.xml
+++ b/libs/annotations/src/main/res/values-is/strings.xml
@@ -37,5 +37,6 @@
Sendi
Litur glósu
Fjarlægt %1$s af %2$s
+
diff --git a/libs/annotations/src/main/res/values-it/strings.xml b/libs/annotations/src/main/res/values-it/strings.xml
index 6a1c5f216c..5f2fa6cab4 100644
--- a/libs/annotations/src/main/res/values-it/strings.xml
+++ b/libs/annotations/src/main/res/values-it/strings.xml
@@ -36,5 +36,6 @@
Invio in corso
Colore nota
Rimosso il %1$s da %2$s
+
diff --git a/libs/annotations/src/main/res/values-ja/strings.xml b/libs/annotations/src/main/res/values-ja/strings.xml
index ce4c759081..f3731a2b6f 100644
--- a/libs/annotations/src/main/res/values-ja/strings.xml
+++ b/libs/annotations/src/main/res/values-ja/strings.xml
@@ -38,5 +38,6 @@
ノートの色
%1$sにより除去済%2$s
+
diff --git a/libs/annotations/src/main/res/values-mi/strings.xml b/libs/annotations/src/main/res/values-mi/strings.xml
index 1567a45db4..05f1b504ef 100644
--- a/libs/annotations/src/main/res/values-mi/strings.xml
+++ b/libs/annotations/src/main/res/values-mi/strings.xml
@@ -37,5 +37,6 @@
E tuku ana
Tuhi Tae
Kua tangohia %1$s e %2$s
+
diff --git a/libs/annotations/src/main/res/values-nb/strings.xml b/libs/annotations/src/main/res/values-nb/strings.xml
index 75023d68ee..a7a39aced7 100644
--- a/libs/annotations/src/main/res/values-nb/strings.xml
+++ b/libs/annotations/src/main/res/values-nb/strings.xml
@@ -37,5 +37,6 @@
Sender
Notatfarge
Fjernet %1$s av %2$s
+
diff --git a/libs/annotations/src/main/res/values-nl/strings.xml b/libs/annotations/src/main/res/values-nl/strings.xml
index 5ed72b4e5d..6554f4e836 100644
--- a/libs/annotations/src/main/res/values-nl/strings.xml
+++ b/libs/annotations/src/main/res/values-nl/strings.xml
@@ -38,5 +38,6 @@
Kleur van opmerking
%1$s verwijderd door %2$s
+
diff --git a/libs/annotations/src/main/res/values-pl/strings.xml b/libs/annotations/src/main/res/values-pl/strings.xml
index b2bbd855d6..bc857a8d29 100644
--- a/libs/annotations/src/main/res/values-pl/strings.xml
+++ b/libs/annotations/src/main/res/values-pl/strings.xml
@@ -38,5 +38,6 @@
Kolor notatki
Usunięte %1$s przez %2$s
+
diff --git a/libs/annotations/src/main/res/values-pt-rBR/strings.xml b/libs/annotations/src/main/res/values-pt-rBR/strings.xml
index a277feb6a6..a1b7319c8c 100644
--- a/libs/annotations/src/main/res/values-pt-rBR/strings.xml
+++ b/libs/annotations/src/main/res/values-pt-rBR/strings.xml
@@ -36,5 +36,6 @@
Enviando
Cor da observação
Removido %1$s por %2$s
+
diff --git a/libs/annotations/src/main/res/values-pt-rPT/strings.xml b/libs/annotations/src/main/res/values-pt-rPT/strings.xml
index 03fbe87b99..6a5f47db1f 100644
--- a/libs/annotations/src/main/res/values-pt-rPT/strings.xml
+++ b/libs/annotations/src/main/res/values-pt-rPT/strings.xml
@@ -38,5 +38,6 @@
Observar cor
Removido %1$s por %2$s
+
diff --git a/libs/annotations/src/main/res/values-ru/strings.xml b/libs/annotations/src/main/res/values-ru/strings.xml
index 98ead02248..9fc6aac87e 100644
--- a/libs/annotations/src/main/res/values-ru/strings.xml
+++ b/libs/annotations/src/main/res/values-ru/strings.xml
@@ -38,5 +38,6 @@
Отметить цвет
%1$s удален %2$s
+
diff --git a/libs/annotations/src/main/res/values-sl/strings.xml b/libs/annotations/src/main/res/values-sl/strings.xml
index 46b285d354..68f4f28674 100644
--- a/libs/annotations/src/main/res/values-sl/strings.xml
+++ b/libs/annotations/src/main/res/values-sl/strings.xml
@@ -37,5 +37,6 @@
Pošiljanje
Barva opombe
Odstranjeno %1$s, odstranil %2$s
+
diff --git a/libs/annotations/src/main/res/values-sv/strings.xml b/libs/annotations/src/main/res/values-sv/strings.xml
index d104c77450..43adaf6db0 100644
--- a/libs/annotations/src/main/res/values-sv/strings.xml
+++ b/libs/annotations/src/main/res/values-sv/strings.xml
@@ -36,5 +36,6 @@
Skickar
Anteckningsfärg
Borttagen %1$s av %2$s
+
diff --git a/libs/annotations/src/main/res/values-th/strings.xml b/libs/annotations/src/main/res/values-th/strings.xml
index d26603b218..816c1d248d 100644
--- a/libs/annotations/src/main/res/values-th/strings.xml
+++ b/libs/annotations/src/main/res/values-th/strings.xml
@@ -37,5 +37,6 @@
กำลังส่ง
สีหมายเหตุ
ลบ %1$s โดย %2$s
+
diff --git a/libs/annotations/src/main/res/values-vi/strings.xml b/libs/annotations/src/main/res/values-vi/strings.xml
index 4daa087ba8..b9f3a5cd43 100644
--- a/libs/annotations/src/main/res/values-vi/strings.xml
+++ b/libs/annotations/src/main/res/values-vi/strings.xml
@@ -37,5 +37,6 @@
Đang Gửi
Màu Ghi Chú
Đã bị gỡ %1$s bởi %2$s
+
diff --git a/libs/annotations/src/main/res/values-zh/strings.xml b/libs/annotations/src/main/res/values-zh/strings.xml
index 0e33334040..a10a031b4c 100644
--- a/libs/annotations/src/main/res/values-zh/strings.xml
+++ b/libs/annotations/src/main/res/values-zh/strings.xml
@@ -38,5 +38,6 @@
注释颜色
移除%1$s者 %2$s
+
diff --git a/libs/annotations/src/main/res/values/strings.xml b/libs/annotations/src/main/res/values/strings.xml
index 44e3188fec..9fc2b43536 100644
--- a/libs/annotations/src/main/res/values/strings.xml
+++ b/libs/annotations/src/main/res/values/strings.xml
@@ -36,5 +36,6 @@
Sending
Note Color
Removed %1$s by %2$s
+
From 435ae20b38dcf5ecd6f073c921e98a1a87633447 Mon Sep 17 00:00:00 2001
From: Akos Hermann <72087159+hermannakos@users.noreply.github.com>
Date: Tue, 4 Oct 2022 11:05:31 +0200
Subject: [PATCH 12/73] [Student][Teacher] Fixed RestRetryInterceptor (#1733)
refs: MBL-16299
affects: Student, Teacher
release note: none
---
.../com/instructure/dataseeding/util/RestRetryInterceptor.kt | 1 +
1 file changed, 1 insertion(+)
diff --git a/automation/dataseedingapi/src/main/kotlin/com/instructure/dataseeding/util/RestRetryInterceptor.kt b/automation/dataseedingapi/src/main/kotlin/com/instructure/dataseeding/util/RestRetryInterceptor.kt
index a68d6b1ae9..c4d65eb0d7 100644
--- a/automation/dataseedingapi/src/main/kotlin/com/instructure/dataseeding/util/RestRetryInterceptor.kt
+++ b/automation/dataseedingapi/src/main/kotlin/com/instructure/dataseeding/util/RestRetryInterceptor.kt
@@ -33,6 +33,7 @@ object RestRetryInterceptor : Interceptor {
var response = chain.proceed(request)
while (response.failed && attempt <= MAX_RETRIES) {
RetryBackoff.wait(attempt)
+ response.close()
response = chain.proceed(request)
attempt += 1
}
From 8cd805bd7707c4c7affbdcb41b69e74ad9ce4666 Mon Sep 17 00:00:00 2001
From: Kristof Deak <92309696+kdeakinstructure@users.noreply.github.com>
Date: Tue, 4 Oct 2022 11:09:07 +0200
Subject: [PATCH 13/73] [MBL-16234][Student] - Share extension E2E test (#1730)
* Implement Share Extension E2E test.
Rename typo in assertAssignmentSubmissionSuccess method.
Use string resource instead of case insensitive text on Turn In and Upload buttons.
* extract share extension test filenames into variables
remove password log from all the test cases.
refs: MBL-16234
affects: Student
release note: none
---
.../student/ui/e2e/AnnouncementsE2ETest.kt | 2 +-
.../student/ui/e2e/AssignmentsE2ETest.kt | 10 +-
.../student/ui/e2e/BookmarksE2ETest.kt | 2 +-
.../student/ui/e2e/CollaborationsE2ETest.kt | 2 +-
.../student/ui/e2e/ConferencesE2ETest.kt | 2 +-
.../student/ui/e2e/DashboardE2ETest.kt | 3 +-
.../student/ui/e2e/DiscussionsE2ETest.kt | 2 +-
.../student/ui/e2e/FilesE2ETest.kt | 8 +-
.../student/ui/e2e/GradesE2ETest.kt | 2 +-
.../student/ui/e2e/InboxE2ETest.kt | 4 +-
.../student/ui/e2e/LoginE2ETest.kt | 16 +-
.../student/ui/e2e/ModulesE2ETest.kt | 8 +-
.../student/ui/e2e/NotificationsE2ETest.kt | 2 +-
.../student/ui/e2e/PagesE2ETest.kt | 2 +-
.../student/ui/e2e/PeopleE2ETest.kt | 5 +-
.../student/ui/e2e/QuizzesE2ETest.kt | 2 +-
.../student/ui/e2e/SettingsE2ETest.kt | 9 +-
.../student/ui/e2e/ShareExtensionE2ETest.kt | 231 ++++++++++++++++++
.../student/ui/e2e/SyllabusE2ETest.kt | 2 +-
.../instructure/student/ui/e2e/TodoE2ETest.kt | 2 +-
.../ShareExtensionInteractionTest.kt | 2 +-
.../student/ui/pages/FileUploadPage.kt | 7 +-
.../ui/pages/ShareExtensionStatusPage.kt | 16 +-
.../teacher/ui/e2e/AnnouncementsE2ETest.kt | 2 +-
.../teacher/ui/e2e/AssignmentE2ETest.kt | 2 +-
.../teacher/ui/e2e/CourseSettingsE2ETest.kt | 2 +-
.../teacher/ui/e2e/DashboardE2ETest.kt | 2 +-
.../teacher/ui/e2e/DiscussionsE2ETest.kt | 2 +-
.../teacher/ui/e2e/FilesE2ETest.kt | 2 +-
.../teacher/ui/e2e/InboxE2ETest.kt | 2 +-
.../teacher/ui/e2e/LoginE2ETest.kt | 10 +-
.../teacher/ui/e2e/ModulesE2ETest.kt | 8 +-
.../teacher/ui/e2e/PagesE2ETest.kt | 2 +-
.../teacher/ui/e2e/PeopleE2ETest.kt | 8 +-
.../instructure/teacher/ui/e2e/QuizE2ETest.kt | 2 +-
.../teacher/ui/e2e/SettingsE2ETest.kt | 10 +-
.../teacher/ui/e2e/SpeedGraderE2ETest.kt | 2 +-
.../teacher/ui/e2e/SyllabusE2ETest.kt | 2 +-
.../instructure/teacher/ui/e2e/TodoE2ETest.kt | 2 +-
39 files changed, 316 insertions(+), 85 deletions(-)
create mode 100644 apps/student/src/androidTest/java/com/instructure/student/ui/e2e/ShareExtensionE2ETest.kt
diff --git a/apps/student/src/androidTest/java/com/instructure/student/ui/e2e/AnnouncementsE2ETest.kt b/apps/student/src/androidTest/java/com/instructure/student/ui/e2e/AnnouncementsE2ETest.kt
index 6b03be97c9..6a980dbeaa 100644
--- a/apps/student/src/androidTest/java/com/instructure/student/ui/e2e/AnnouncementsE2ETest.kt
+++ b/apps/student/src/androidTest/java/com/instructure/student/ui/e2e/AnnouncementsE2ETest.kt
@@ -52,7 +52,7 @@ class AnnouncementsE2ETest : StudentTest() {
val announcement = data.announcementsList[0]
val secondAnnouncement = data.announcementsList[1]
- Log.d(STEP_TAG,"Login with user: ${student.name}, login id: ${student.loginId} , password: ${student.password}")
+ Log.d(STEP_TAG,"Login with user: ${student.name}, login id: ${student.loginId}.")
tokenLogin(student)
dashboardPage.waitForRender()
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 871eca2058..972fef2d13 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
@@ -77,7 +77,7 @@ class AssignmentsE2ETest: StudentTest() {
dueAt = 1.days.fromNow.iso8601
))
- Log.d(STEP_TAG, "Login with user: ${student.name}, login id: ${student.loginId} , password: ${student.password}")
+ Log.d(STEP_TAG, "Login with user: ${student.name}, login id: ${student.loginId}.")
tokenLogin(student)
dashboardPage.waitForRender()
@@ -166,7 +166,7 @@ class AssignmentsE2ETest: StudentTest() {
excused = false
)
- Log.d(STEP_TAG, "Login with user: ${student.name}, login id: ${student.loginId} , password: ${student.password}")
+ Log.d(STEP_TAG, "Login with user: ${student.name}, login id: ${student.loginId}.")
tokenLogin(student)
dashboardPage.waitForRender()
@@ -200,7 +200,7 @@ class AssignmentsE2ETest: StudentTest() {
allowedExtensions = listOf("txt", "pdf", "jpg")
))
- Log.d(STEP_TAG, "Login with user: ${student.name}, login id: ${student.loginId} , password: ${student.password}")
+ Log.d(STEP_TAG, "Login with user: ${student.name}, login id: ${student.loginId}.")
tokenLogin(student)
dashboardPage.waitForRender()
@@ -342,7 +342,7 @@ class AssignmentsE2ETest: StudentTest() {
excused = false
)
- Log.d(STEP_TAG, "Login with user: ${student.name}, login id: ${student.loginId} , password: ${student.password}")
+ Log.d(STEP_TAG, "Login with user: ${student.name}, login id: ${student.loginId}.")
tokenLogin(student)
dashboardPage.waitForRender()
@@ -389,7 +389,7 @@ class AssignmentsE2ETest: StudentTest() {
))
))
- Log.d(STEP_TAG, "Login with user: ${student.name}, login id: ${student.loginId} , password: ${student.password}")
+ Log.d(STEP_TAG, "Login with user: ${student.name}, login id: ${student.loginId}.")
tokenLogin(student)
dashboardPage.waitForRender()
diff --git a/apps/student/src/androidTest/java/com/instructure/student/ui/e2e/BookmarksE2ETest.kt b/apps/student/src/androidTest/java/com/instructure/student/ui/e2e/BookmarksE2ETest.kt
index e05d57161a..f18380ab0c 100644
--- a/apps/student/src/androidTest/java/com/instructure/student/ui/e2e/BookmarksE2ETest.kt
+++ b/apps/student/src/androidTest/java/com/instructure/student/ui/e2e/BookmarksE2ETest.kt
@@ -68,7 +68,7 @@ class BookmarksE2ETest : StudentTest() {
dueAt = 1.days.fromNow.iso8601
))
- Log.d(STEP_TAG,"Login with user: ${student.name}, login id: ${student.loginId} , password: ${student.password}")
+ Log.d(STEP_TAG,"Login with user: ${student.name}, login id: ${student.loginId}.")
tokenLogin(student)
dashboardPage.waitForRender()
diff --git a/apps/student/src/androidTest/java/com/instructure/student/ui/e2e/CollaborationsE2ETest.kt b/apps/student/src/androidTest/java/com/instructure/student/ui/e2e/CollaborationsE2ETest.kt
index 6676a8a364..c1f0c7e521 100644
--- a/apps/student/src/androidTest/java/com/instructure/student/ui/e2e/CollaborationsE2ETest.kt
+++ b/apps/student/src/androidTest/java/com/instructure/student/ui/e2e/CollaborationsE2ETest.kt
@@ -37,7 +37,7 @@ class CollaborationsE2ETest: StudentTest() {
val student = data.studentsList[0]
val course = data.coursesList[0]
- Log.d(STEP_TAG,"Login with user: ${student.name}, login id: ${student.loginId} , password: ${student.password}")
+ Log.d(STEP_TAG,"Login with user: ${student.name}, login id: ${student.loginId}.")
tokenLogin(student)
dashboardPage.waitForRender()
diff --git a/apps/student/src/androidTest/java/com/instructure/student/ui/e2e/ConferencesE2ETest.kt b/apps/student/src/androidTest/java/com/instructure/student/ui/e2e/ConferencesE2ETest.kt
index 8beae2e7d6..0e4b170ac8 100644
--- a/apps/student/src/androidTest/java/com/instructure/student/ui/e2e/ConferencesE2ETest.kt
+++ b/apps/student/src/androidTest/java/com/instructure/student/ui/e2e/ConferencesE2ETest.kt
@@ -43,7 +43,7 @@ class ConferencesE2ETest: StudentTest() {
val teacher = data.teachersList[0]
val course = data.coursesList[0]
- Log.d(STEP_TAG,"Login with user: ${student.name}, login id: ${student.loginId} , password: ${student.password}")
+ Log.d(STEP_TAG,"Login with user: ${student.name}, login id: ${student.loginId}.")
tokenLogin(student)
dashboardPage.waitForRender()
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 40acdb7f5f..ea25f2bf4c 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
@@ -28,7 +28,6 @@ import com.instructure.student.ui.utils.StudentTest
import com.instructure.student.ui.utils.seedData
import com.instructure.student.ui.utils.tokenLogin
import dagger.hilt.android.testing.HiltAndroidTest
-import junit.framework.Assert.assertEquals
import org.junit.Test
@HiltAndroidTest
@@ -64,7 +63,7 @@ class DashboardE2ETest : StudentTest() {
Log.d(PREPARATION_TAG,"Create group membership for ${student.name} student.")
GroupsApi.createGroupMembership(group.id, student.id, teacher.token)
- Log.d(STEP_TAG,"Login with user: ${student.name}, login id: ${student.loginId} , password: ${student.password}")
+ Log.d(STEP_TAG,"Login with user: ${student.name}, login id: ${student.loginId}.")
tokenLogin(student)
dashboardPage.waitForRender()
diff --git a/apps/student/src/androidTest/java/com/instructure/student/ui/e2e/DiscussionsE2ETest.kt b/apps/student/src/androidTest/java/com/instructure/student/ui/e2e/DiscussionsE2ETest.kt
index cd2238f1e5..e8f3650e4a 100644
--- a/apps/student/src/androidTest/java/com/instructure/student/ui/e2e/DiscussionsE2ETest.kt
+++ b/apps/student/src/androidTest/java/com/instructure/student/ui/e2e/DiscussionsE2ETest.kt
@@ -77,7 +77,7 @@ class DiscussionsE2ETest: StudentTest() {
token = teacher.token
)
- Log.d(STEP_TAG,"Login with user: ${student.name}, login id: ${student.loginId} , password: ${student.password}")
+ Log.d(STEP_TAG,"Login with user: ${student.name}, login id: ${student.loginId}.")
tokenLogin(student)
dashboardPage.waitForRender()
diff --git a/apps/student/src/androidTest/java/com/instructure/student/ui/e2e/FilesE2ETest.kt b/apps/student/src/androidTest/java/com/instructure/student/ui/e2e/FilesE2ETest.kt
index fc6ce70ea3..d274e63fd2 100644
--- a/apps/student/src/androidTest/java/com/instructure/student/ui/e2e/FilesE2ETest.kt
+++ b/apps/student/src/androidTest/java/com/instructure/student/ui/e2e/FilesE2ETest.kt
@@ -35,11 +35,7 @@ import com.instructure.panda_annotations.FeatureCategory
import com.instructure.panda_annotations.Priority
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.ViewUtils
-import com.instructure.student.ui.utils.seedData
-import com.instructure.student.ui.utils.tokenLogin
-import com.instructure.student.ui.utils.uploadTextFile
+import com.instructure.student.ui.utils.*
import dagger.hilt.android.testing.HiltAndroidTest
import org.junit.Test
import java.io.File
@@ -113,7 +109,7 @@ class FilesE2ETest: StudentTest() {
token = student.token
)
- Log.d(STEP_TAG,"Login with user: ${student.name}, login id: ${student.loginId} , password: ${student.password}")
+ Log.d(STEP_TAG,"Login with user: ${student.name}, login id: ${student.loginId}.")
tokenLogin(student)
dashboardPage.waitForRender()
diff --git a/apps/student/src/androidTest/java/com/instructure/student/ui/e2e/GradesE2ETest.kt b/apps/student/src/androidTest/java/com/instructure/student/ui/e2e/GradesE2ETest.kt
index 81856f5373..1511a783d3 100644
--- a/apps/student/src/androidTest/java/com/instructure/student/ui/e2e/GradesE2ETest.kt
+++ b/apps/student/src/androidTest/java/com/instructure/student/ui/e2e/GradesE2ETest.kt
@@ -85,7 +85,7 @@ class GradesE2ETest: StudentTest() {
Log.d(STEP_TAG,"Publish the previously made quiz.")
val quiz = QuizzesApi.createAndPublishQuiz(course.id, teacher.token, quizQuestions)
- Log.d(STEP_TAG,"Login with user: ${student.name}, login id: ${student.loginId} , password: ${student.password}")
+ Log.d(STEP_TAG,"Login with user: ${student.name}, login id: ${student.loginId}.")
tokenLogin(student)
dashboardPage.waitForRender()
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 95e09eecd4..d49a85c131 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
@@ -63,7 +63,7 @@ class InboxE2ETest: StudentTest() {
GroupsApi.createGroupMembership(group.id, student1.id, teacher.token)
GroupsApi.createGroupMembership(group.id, student2.id, teacher.token)
- Log.d(STEP_TAG,"Login with user: ${student1.name}, login id: ${student1.loginId} , password: ${student1.password}")
+ Log.d(STEP_TAG,"Login with user: ${student1.name}, login id: ${student1.loginId}.")
tokenLogin(student1)
dashboardPage.waitForRender()
dashboardPage.assertDisplaysCourse(course)
@@ -124,7 +124,7 @@ class InboxE2ETest: StudentTest() {
Log.d(STEP_TAG,"Log out with ${student1.name} student.")
dashboardPage.logOut()
- Log.d(STEP_TAG,"Login with user: ${student2.name}, login id: ${student2.loginId} , password: ${student2.password}")
+ Log.d(STEP_TAG,"Login with user: ${student2.name}, login id: ${student2.loginId}.")
tokenLogin(student2)
dashboardPage.waitForRender()
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 b0b9bb3748..899e9d8dc6 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
@@ -57,7 +57,7 @@ class LoginE2ETest : StudentTest() {
val student1 = data.studentsList[0]
val student2 = data.studentsList[1]
- Log.d(STEP_TAG,"Login with user: ${student1.name}, login id: ${student1.loginId} , password: ${student1.password}")
+ Log.d(STEP_TAG,"Login with user: ${student1.name}, login id: ${student1.loginId}.")
loginWithUser(student1)
Log.d(STEP_TAG,"Assert that the Dashboard Page is the landing page and it is loaded successfully.")
@@ -66,7 +66,7 @@ class LoginE2ETest : StudentTest() {
Log.d(STEP_TAG,"Log out with ${student1.name} student.")
dashboardPage.logOut()
- Log.d(STEP_TAG,"Login with user: ${student2.name}, login id: ${student2.loginId} , password: ${student2.password}")
+ Log.d(STEP_TAG,"Login with user: ${student2.name}, login id: ${student2.loginId}.")
loginWithUser(student2)
Log.d(STEP_TAG,"Assert that the Dashboard Page is the landing page and it is loaded successfully.")
@@ -78,7 +78,7 @@ class LoginE2ETest : StudentTest() {
Log.d(STEP_TAG,"Assert that the previously logins has been displayed.")
loginLandingPage.assertDisplaysPreviousLogins()
- Log.d(STEP_TAG,"Login with user: ${student1.name}, login id: ${student1.loginId} , password: ${student1.password}")
+ Log.d(STEP_TAG,"Login with user: ${student1.name}, login id: ${student1.loginId}.")
loginWithUser(student1)
Log.d(STEP_TAG,"Assert that the Dashboard Page is the landing page and it is loaded successfully.")
@@ -136,7 +136,7 @@ class LoginE2ETest : StudentTest() {
val course = data.coursesList[0]
val parent = parentData.parentsList[0] //Test with Parent user. parents don't show up in the "People" page so we can't verify their role.
- Log.d(STEP_TAG,"Login with user: ${student.name}, login id: ${student.loginId} , password: ${student.password}")
+ Log.d(STEP_TAG,"Login with user: ${student.name}, login id: ${student.loginId}.")
loginWithUser(student)
Log.d(STEP_TAG,"Validate ${student.name} user's role as a Student.")
@@ -148,7 +148,7 @@ class LoginE2ETest : StudentTest() {
Log.d(STEP_TAG,"Log out with ${student.name} student.")
dashboardPage.logOut()
- Log.d(STEP_TAG,"Login with user: ${teacher.name}, login id: ${teacher.loginId} , password: ${teacher.password}")
+ Log.d(STEP_TAG,"Login with user: ${teacher.name}, login id: ${teacher.loginId}.")
loginWithUser(teacher)
Log.d(STEP_TAG,"Validate ${teacher.name} user's role as a Teacher.")
@@ -160,7 +160,7 @@ class LoginE2ETest : StudentTest() {
Log.d(STEP_TAG,"Log out with ${teacher.name} teacher.")
dashboardPage.logOut()
- Log.d(STEP_TAG,"Login with user: ${ta.name}, login id: ${ta.loginId} , password: ${ta.password}")
+ Log.d(STEP_TAG,"Login with user: ${ta.name}, login id: ${ta.loginId}.")
loginWithUser(ta)
Log.d(STEP_TAG,"Validate ${ta.name} user's role as a TA.")
@@ -172,7 +172,7 @@ class LoginE2ETest : StudentTest() {
Log.d(STEP_TAG,"Log out with ${ta.name} teacher assistant.")
dashboardPage.logOut()
- Log.d(STEP_TAG,"Login with user: ${parent.name}, login id: ${parent.loginId} , password: ${parent.password}")
+ Log.d(STEP_TAG,"Login with user: ${parent.name}, login id: ${parent.loginId}.")
loginWithUser(parent)
Log.d(STEP_TAG,"Assert that the Dashboard Page is the landing page and it is loaded successfully.")
@@ -217,7 +217,7 @@ class LoginE2ETest : StudentTest() {
enrollmentService = enrollmentsService
)
- Log.d(STEP_TAG,"Login with user: ${student.name}, login id: ${student.loginId} , password: ${student.password}")
+ Log.d(STEP_TAG,"Login with user: ${student.name}, login id: ${student.loginId}.")
loginWithUser(student)
Log.d(STEP_TAG,"Attempt to sign into our vanity domain, and validate ${student.name} user's role as a Student.")
diff --git a/apps/student/src/androidTest/java/com/instructure/student/ui/e2e/ModulesE2ETest.kt b/apps/student/src/androidTest/java/com/instructure/student/ui/e2e/ModulesE2ETest.kt
index 03c4305a3a..e7db107328 100644
--- a/apps/student/src/androidTest/java/com/instructure/student/ui/e2e/ModulesE2ETest.kt
+++ b/apps/student/src/androidTest/java/com/instructure/student/ui/e2e/ModulesE2ETest.kt
@@ -19,11 +19,7 @@ package com.instructure.student.ui.e2e
import android.util.Log
import androidx.test.espresso.Espresso
import com.instructure.canvas.espresso.E2E
-import com.instructure.dataseeding.api.AssignmentsApi
-import com.instructure.dataseeding.api.DiscussionTopicsApi
-import com.instructure.dataseeding.api.ModulesApi
-import com.instructure.dataseeding.api.PagesApi
-import com.instructure.dataseeding.api.QuizzesApi
+import com.instructure.dataseeding.api.*
import com.instructure.dataseeding.model.ModuleItemTypes
import com.instructure.dataseeding.model.SubmissionType
import com.instructure.dataseeding.util.days
@@ -165,7 +161,7 @@ class ModulesE2ETest: StudentTest() {
contentId = discussionTopic1.id.toString()
)
- Log.d(STEP_TAG, "Login with user: ${teacher.name}, login id: ${teacher.loginId} , password: ${teacher.password}")
+ Log.d(STEP_TAG, "Login with user: ${teacher.name}, login id: ${teacher.loginId}.")
tokenLogin(student)
dashboardPage.waitForRender()
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 ab8d53bfd4..6743ac91bd 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
@@ -98,7 +98,7 @@ class NotificationsE2ETest : StudentTest() {
Log.d(PREPARATION_TAG,"Create and publish a quiz with the previously seeded questions.")
QuizzesApi.createAndPublishQuiz(course.id, teacher.token, quizQuestions)
- Log.d(STEP_TAG,"Login with user: ${student.name}, login id: ${student.loginId} , password: ${student.password}")
+ Log.d(STEP_TAG,"Login with user: ${student.name}, login id: ${student.loginId}.")
tokenLogin(student)
dashboardPage.waitForRender()
diff --git a/apps/student/src/androidTest/java/com/instructure/student/ui/e2e/PagesE2ETest.kt b/apps/student/src/androidTest/java/com/instructure/student/ui/e2e/PagesE2ETest.kt
index 0ab9529ddc..203a32fd48 100644
--- a/apps/student/src/androidTest/java/com/instructure/student/ui/e2e/PagesE2ETest.kt
+++ b/apps/student/src/androidTest/java/com/instructure/student/ui/e2e/PagesE2ETest.kt
@@ -79,7 +79,7 @@ class PagesE2ETest: StudentTest() {
body = ""
)
- Log.d(STEP_TAG,"Login with user: ${student.name}, login id: ${student.loginId} , password: ${student.password}")
+ Log.d(STEP_TAG,"Login with user: ${student.name}, login id: ${student.loginId}.")
tokenLogin(student)
dashboardPage.waitForRender()
diff --git a/apps/student/src/androidTest/java/com/instructure/student/ui/e2e/PeopleE2ETest.kt b/apps/student/src/androidTest/java/com/instructure/student/ui/e2e/PeopleE2ETest.kt
index 7d1eb54def..4451f3b26b 100644
--- a/apps/student/src/androidTest/java/com/instructure/student/ui/e2e/PeopleE2ETest.kt
+++ b/apps/student/src/androidTest/java/com/instructure/student/ui/e2e/PeopleE2ETest.kt
@@ -17,7 +17,6 @@
package com.instructure.student.ui.e2e
import android.util.Log
-import androidx.test.espresso.Espresso
import com.instructure.canvas.espresso.E2E
import com.instructure.panda_annotations.FeatureCategory
import com.instructure.panda_annotations.Priority
@@ -55,7 +54,7 @@ class PeopleE2ETest : StudentTest() {
val student1 = data.studentsList[0]
val student2 = data.studentsList[1]
- Log.d(STEP_TAG, "Login with user: ${student1.name}, login id: ${student1.loginId} , password: ${student1.password}")
+ Log.d(STEP_TAG, "Login with user: ${student1.name}, login id: ${student1.loginId}.")
tokenLogin(student1)
dashboardPage.waitForRender()
@@ -96,7 +95,7 @@ class PeopleE2ETest : StudentTest() {
Log.d(STEP_TAG,"Sign out with ${student1.name} student.")
dashboardPage.logOut()
- Log.d(STEP_TAG, "Login with user: ${student2.name}, login id: ${student2.loginId} , password: ${student2.password}")
+ Log.d(STEP_TAG, "Login with user: ${student2.name}, login id: ${student2.loginId}.")
tokenLogin(student2)
dashboardPage.waitForRender()
diff --git a/apps/student/src/androidTest/java/com/instructure/student/ui/e2e/QuizzesE2ETest.kt b/apps/student/src/androidTest/java/com/instructure/student/ui/e2e/QuizzesE2ETest.kt
index d7b9a5260b..200cf16b51 100644
--- a/apps/student/src/androidTest/java/com/instructure/student/ui/e2e/QuizzesE2ETest.kt
+++ b/apps/student/src/androidTest/java/com/instructure/student/ui/e2e/QuizzesE2ETest.kt
@@ -120,7 +120,7 @@ class QuizzesE2ETest: StudentTest() {
val quizPublished = createAndPublishQuiz(course.id, teacher.token, quizQuestions)
- Log.d(STEP_TAG, "Login with user: ${student.name}, login id: ${student.loginId} , password: ${student.password}")
+ Log.d(STEP_TAG, "Login with user: ${student.name}, login id: ${student.loginId}.")
tokenLogin(student)
dashboardPage.waitForRender()
diff --git a/apps/student/src/androidTest/java/com/instructure/student/ui/e2e/SettingsE2ETest.kt b/apps/student/src/androidTest/java/com/instructure/student/ui/e2e/SettingsE2ETest.kt
index aed91b26e3..9d1e86c595 100644
--- a/apps/student/src/androidTest/java/com/instructure/student/ui/e2e/SettingsE2ETest.kt
+++ b/apps/student/src/androidTest/java/com/instructure/student/ui/e2e/SettingsE2ETest.kt
@@ -53,7 +53,6 @@ class SettingsE2ETest : StudentTest() {
val data = seedData(students = 1, teachers = 1, courses = 1)
val student = data.studentsList[0]
- Log.d(STEP_TAG, "Login with user: ${student.name}, login id: ${student.loginId} , password: ${student.password}")
tokenLogin(student)
dashboardPage.waitForRender()
@@ -115,7 +114,7 @@ class SettingsE2ETest : StudentTest() {
val student = data.studentsList[0]
val course = data.coursesList[0]
- Log.d(STEP_TAG, "Login with user: ${student.name}, login id: ${student.loginId} , password: ${student.password}")
+ Log.d(STEP_TAG, "Login with user: ${student.name}, login id: ${student.loginId}.")
tokenLogin(student)
dashboardPage.waitForRender()
@@ -164,7 +163,7 @@ class SettingsE2ETest : StudentTest() {
val data = seedData(students = 1, teachers = 1, courses = 1)
val student = data.studentsList[0]
- Log.d(STEP_TAG, "Login with user: ${student.name}, login id: ${student.loginId} , password: ${student.password}")
+ Log.d(STEP_TAG, "Login with user: ${student.name}, login id: ${student.loginId}.")
tokenLogin(student)
dashboardPage.waitForRender()
@@ -186,7 +185,7 @@ class SettingsE2ETest : StudentTest() {
val data = seedData(students = 1, teachers = 1, courses = 1)
val student = data.studentsList[0]
- Log.d(STEP_TAG, "Login with user: ${student.name}, login id: ${student.loginId} , password: ${student.password}")
+ Log.d(STEP_TAG, "Login with user: ${student.name}, login id: ${student.loginId}.")
tokenLogin(student)
dashboardPage.waitForRender()
@@ -219,7 +218,7 @@ class SettingsE2ETest : StudentTest() {
val data = seedData(students = 1, teachers = 1, courses = 1)
val student = data.studentsList[0]
- Log.d(STEP_TAG, "Login with user: ${student.name}, login id: ${student.loginId} , password: ${student.password}")
+ Log.d(STEP_TAG, "Login with user: ${student.name}, login id: ${student.loginId}.")
tokenLogin(student)
dashboardPage.waitForRender()
diff --git a/apps/student/src/androidTest/java/com/instructure/student/ui/e2e/ShareExtensionE2ETest.kt b/apps/student/src/androidTest/java/com/instructure/student/ui/e2e/ShareExtensionE2ETest.kt
new file mode 100644
index 0000000000..80d8ae2cf3
--- /dev/null
+++ b/apps/student/src/androidTest/java/com/instructure/student/ui/e2e/ShareExtensionE2ETest.kt
@@ -0,0 +1,231 @@
+/*
+ * Copyright (C) 2022 - present Instructure, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package com.instructure.student.ui.e2e
+
+import android.content.Intent
+import android.net.Uri
+import android.util.Log
+import androidx.core.content.FileProvider
+import androidx.test.platform.app.InstrumentationRegistry
+import androidx.test.uiautomator.UiDevice
+import androidx.test.uiautomator.UiSelector
+import com.instructure.canvas.espresso.E2E
+import com.instructure.dataseeding.api.AssignmentsApi
+import com.instructure.dataseeding.model.GradingType
+import com.instructure.dataseeding.model.SubmissionType
+import com.instructure.dataseeding.util.days
+import com.instructure.dataseeding.util.fromNow
+import com.instructure.dataseeding.util.iso8601
+import com.instructure.pandautils.utils.Const
+import com.instructure.student.ui.utils.StudentTest
+import com.instructure.student.ui.utils.ViewUtils
+import com.instructure.student.ui.utils.seedData
+import com.instructure.student.ui.utils.tokenLogin
+import dagger.hilt.android.testing.HiltAndroidTest
+import org.junit.Test
+import java.io.File
+
+@HiltAndroidTest
+class ShareExtensionE2ETest: StudentTest() {
+
+ override fun displaysPageObjects() = Unit
+ override fun enableAndConfigureAccessibilityChecks() = Unit
+
+ @E2E
+ @Test
+ fun shareExtensionE2ETest() {
+
+ Log.d(PREPARATION_TAG, "Seeding data.")
+ val data = seedData(students = 1, teachers = 1, courses = 1)
+ val jpgTestFileName = "sample.jpg"
+ val pdfTestFileName = "samplepdf.pdf"
+ val uri = setupFileOnDevice(jpgTestFileName)
+ val uri2 = setupFileOnDevice(pdfTestFileName)
+ val student = data.studentsList[0]
+ val course = data.coursesList[0]
+ val teacher = data.teachersList[0]
+
+ Log.d(PREPARATION_TAG,"Seeding 'Text Entry' assignment for ${course.name} course.")
+ val testAssignmentOne = AssignmentsApi.createAssignment(
+ AssignmentsApi.CreateAssignmentRequest(
+ courseId = course.id,
+ submissionTypes = listOf(SubmissionType.ONLINE_UPLOAD),
+ gradingType = GradingType.POINTS,
+ teacherToken = teacher.token,
+ pointsPossible = 15.0,
+ dueAt = 1.days.fromNow.iso8601
+ ))
+
+ AssignmentsApi.createAssignment(
+ AssignmentsApi.CreateAssignmentRequest(
+ courseId = course.id,
+ submissionTypes = listOf(SubmissionType.ONLINE_UPLOAD),
+ gradingType = GradingType.POINTS,
+ teacherToken = teacher.token,
+ pointsPossible = 30.0,
+ dueAt = 1.days.fromNow.iso8601
+ ))
+
+ Log.d(PREPARATION_TAG, "Get the device to be able to perform app-independent actions on it.")
+ val device = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation())
+
+ Log.d(STEP_TAG, "Login with user: ${student.name}, login id: ${student.loginId}.")
+ tokenLogin(student)
+
+ Log.d(STEP_TAG, "Press 'Home' button on the device so it will take the Student application into the background.")
+ device.pressHome()
+
+ Log.d(STEP_TAG," Share the '$jpgTestFileName' and '$pdfTestFileName' files from the following uris: '$uri' and '$uri2'.")
+ shareMultipleFiles(arrayListOf(uri, uri2))
+
+ Log.d(STEP_TAG,"Click on the Canvas Student app.")
+ device.findObject(UiSelector().text("Canvas")).click()
+ device.waitForIdle()
+
+ Log.d(STEP_TAG,"Assert that the Share Extension Page is displayed and the " +
+ "'My Files' button is selected by default, and so the '${student.name}' username is displayed as well.")
+ shareExtensionTargetPage.assertPageObjects()
+ shareExtensionTargetPage.assertFilesCheckboxIsSelected()
+ shareExtensionTargetPage.assertUserName(student.name)
+
+ Log.d(STEP_TAG, "Select 'Upload as Submission' and assert that the corresponding course and assignment are displayed within the spinners.")
+ shareExtensionTargetPage.selectSubmission()
+ shareExtensionTargetPage.assertCourseSelectorDisplayedWithCourse(course.name)
+ shareExtensionTargetPage.assertAssignmentSelectorDisplayedWithAssignment(testAssignmentOne.name)
+
+ Log.d(STEP_TAG, "Click on 'Next' button.")
+ shareExtensionTargetPage.pressNext()
+
+ Log.d(STEP_TAG, "Assert that the File Upload page is displayed with the corresponding title.")
+ fileUploadPage.assertPageObjects()
+ fileUploadPage.assertDialogTitle("Submission")
+
+ Log.d(STEP_TAG, "Click on 'Turn In' button to upload both of the files.")
+ fileUploadPage.clickTurnIn()
+
+ Log.d(STEP_TAG, "Assert that the submission upload was successful.")
+ shareExtensionStatusPage.assertPageObjects()
+ shareExtensionStatusPage.assertAssignmentSubmissionSuccess()
+
+ Log.d(STEP_TAG, "Click on 'Done' button.")
+ shareExtensionStatusPage.clickOnDone()
+
+ Log.d(STEP_TAG, "Click 'Recent Apps' device button and bring Canvas Student into the foreground again." +
+ "Assert that the Dashboard Page is displayed.")
+ device.pressRecentApps()
+ device.findObject(UiSelector().descriptionContains("Canvas")).click()
+
+ Log.d(STEP_TAG, "Assert that the Dashboard Page is displayed. Select ${course.name} and navigate to Assignments Page.")
+ dashboardPage.assertPageObjects()
+ dashboardPage.selectCourse(course)
+ courseBrowserPage.selectAssignments()
+
+ Log.d(STEP_TAG, "Click on $testAssignmentOne assignment and refresh the Assignment Details Page." +
+ "Assert that the $testAssignmentOne assignment's status is 'Submitted'.")
+ assignmentListPage.clickAssignment(testAssignmentOne)
+ assignmentDetailsPage.refresh()
+ assignmentDetailsPage.assertAssignmentSubmitted()
+
+ Log.d(STEP_TAG, "Press 'Home' button on the device so it will take the Student application into the background.")
+ device.pressHome()
+
+ Log.d(STEP_TAG," Share the '$jpgTestFileName' and '$pdfTestFileName' files from the following uris: '$uri' and '$uri2'.")
+ shareMultipleFiles(arrayListOf(uri, uri2))
+
+ Log.d(STEP_TAG,"Click on the Canvas Student app.")
+ device.findObject(UiSelector().text("Canvas")).click()
+ device.waitForIdle()
+
+ Log.d(STEP_TAG,"Assert that the Share Extension Page is displayed and the " +
+ "'My Files' button is selected by default, and so the '${student.name}' username is displayed as well.")
+ shareExtensionTargetPage.assertPageObjects()
+ shareExtensionTargetPage.assertFilesCheckboxIsSelected()
+ shareExtensionTargetPage.assertUserName(student.name)
+
+ Log.d(STEP_TAG, "Press 'Next' button.")
+ shareExtensionTargetPage.pressNext()
+
+ Log.d(STEP_TAG,"Assert that the title of the File Upload Page is correct and both of the shared files are displayed.")
+ fileUploadPage.assertPageObjects()
+ fileUploadPage.assertDialogTitle("Upload To My Files")
+ fileUploadPage.assertFileDisplayed(jpgTestFileName)
+ fileUploadPage.assertFileDisplayed(pdfTestFileName)
+
+ Log.d(STEP_TAG,"Remove '$pdfTestFileName' file and assert that it's not displayed any more on the list but the other file is displayed.")
+ fileUploadPage.removeFile(pdfTestFileName)
+ fileUploadPage.assertFileNotDisplayed(pdfTestFileName)
+ fileUploadPage.assertFileDisplayed("$pdfTestFileName.jpg")
+
+ Log.d(STEP_TAG, "Click on 'Upload' button to upload the file.")
+ fileUploadPage.clickUpload()
+
+ Log.d(STEP_TAG, "Assert that the file upload (into my 'Files') was successful.")
+ shareExtensionStatusPage.assertPageObjects()
+ shareExtensionStatusPage.assertFileUploadSuccess()
+
+ Log.d(STEP_TAG, "Click on 'Done' button.")
+ shareExtensionStatusPage.clickOnDone()
+
+ Log.d(STEP_TAG, "Click 'Recent Apps' device button and bring Canvas Student into the foreground again." +
+ "Assert that the Dashboard Page is displayed.")
+ device.pressRecentApps()
+ device.findObject(UiSelector().descriptionContains("Canvas")).click()
+
+ Log.d(STEP_TAG, "Press back button to navigate back to the Dashboard Page.")
+ assignmentDetailsPage.assertPageObjects()
+ ViewUtils.pressBackButton(3)
+
+ Log.d(STEP_TAG, "Navigate to (Global) Files Page.")
+ dashboardPage.assertPageObjects()
+ Thread.sleep(4000) //Make sure that the toast message has disappeared.
+ dashboardPage.gotoGlobalFiles()
+
+ Log.d(STEP_TAG, "Assert that the 'unfiled' directory is displayed." +
+ "Click on it, and assert that the previously uploaded file ($jpgTestFileName) is displayed within the folder.")
+ fileListPage.assertItemDisplayed("unfiled")
+ fileListPage.selectItem("unfiled")
+ fileListPage.assertItemDisplayed(jpgTestFileName)
+
+ }
+
+ private fun setupFileOnDevice(fileName: String): Uri {
+ copyAssetFileToExternalCache(activityRule.activity, fileName)
+
+ val dir = activityRule.activity.externalCacheDir
+ val file = File(dir?.path, fileName)
+
+ val instrumentationContext = InstrumentationRegistry.getInstrumentation().context
+ return FileProvider.getUriForFile(
+ instrumentationContext,
+ "com.instructure.candroid" + Const.FILE_PROVIDER_AUTHORITY,
+ file
+ )
+ }
+
+ private fun shareMultipleFiles(uris: ArrayList) {
+ val intent = Intent().apply {
+ action = Intent.ACTION_SEND_MULTIPLE
+ putParcelableArrayListExtra(Intent.EXTRA_STREAM, uris)
+ type = "*/*"
+ }
+
+ val chooser = Intent.createChooser(intent, null)
+ chooser.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
+
+ InstrumentationRegistry.getInstrumentation().context.startActivity(chooser)
+ }
+}
\ No newline at end of file
diff --git a/apps/student/src/androidTest/java/com/instructure/student/ui/e2e/SyllabusE2ETest.kt b/apps/student/src/androidTest/java/com/instructure/student/ui/e2e/SyllabusE2ETest.kt
index 5be307ab37..1df4dbb0b1 100644
--- a/apps/student/src/androidTest/java/com/instructure/student/ui/e2e/SyllabusE2ETest.kt
+++ b/apps/student/src/androidTest/java/com/instructure/student/ui/e2e/SyllabusE2ETest.kt
@@ -55,7 +55,7 @@ class SyllabusE2ETest: StudentTest() {
val teacher = data.teachersList[0]
val course = data.coursesList[0]
- Log.d(STEP_TAG, "Login with user: ${student.name}, login id: ${student.loginId} , password: ${student.password}")
+ Log.d(STEP_TAG, "Login with user: ${student.name}, login id: ${student.loginId}.")
tokenLogin(student)
dashboardPage.waitForRender()
diff --git a/apps/student/src/androidTest/java/com/instructure/student/ui/e2e/TodoE2ETest.kt b/apps/student/src/androidTest/java/com/instructure/student/ui/e2e/TodoE2ETest.kt
index c0bda5daec..7e0eb1714a 100644
--- a/apps/student/src/androidTest/java/com/instructure/student/ui/e2e/TodoE2ETest.kt
+++ b/apps/student/src/androidTest/java/com/instructure/student/ui/e2e/TodoE2ETest.kt
@@ -75,7 +75,7 @@ class TodoE2ETest: StudentTest() {
dueAt = 8.days.fromNow.iso8601)
)
- Log.d(STEP_TAG, "Login with user: ${student.name}, login id: ${student.loginId} , password: ${student.password}")
+ Log.d(STEP_TAG, "Login with user: ${student.name}, login id: ${student.loginId}.")
tokenLogin(student)
dashboardPage.waitForRender()
diff --git a/apps/student/src/androidTest/java/com/instructure/student/ui/interaction/ShareExtensionInteractionTest.kt b/apps/student/src/androidTest/java/com/instructure/student/ui/interaction/ShareExtensionInteractionTest.kt
index 7bae222dd1..9386aa4ca4 100644
--- a/apps/student/src/androidTest/java/com/instructure/student/ui/interaction/ShareExtensionInteractionTest.kt
+++ b/apps/student/src/androidTest/java/com/instructure/student/ui/interaction/ShareExtensionInteractionTest.kt
@@ -230,7 +230,7 @@ class ShareExtensionInteractionTest : StudentTest() {
fileUploadPage.clickTurnIn()
shareExtensionStatusPage.assertPageObjects()
- shareExtensionStatusPage.assertAssignemntSubmissionSuccess()
+ shareExtensionStatusPage.assertAssignmentSubmissionSuccess()
}
private fun createMockData(): MockCanvas {
diff --git a/apps/student/src/androidTest/java/com/instructure/student/ui/pages/FileUploadPage.kt b/apps/student/src/androidTest/java/com/instructure/student/ui/pages/FileUploadPage.kt
index 71a5238bee..e39fa3a3cc 100644
--- a/apps/student/src/androidTest/java/com/instructure/student/ui/pages/FileUploadPage.kt
+++ b/apps/student/src/androidTest/java/com/instructure/student/ui/pages/FileUploadPage.kt
@@ -22,15 +22,12 @@ import androidx.test.espresso.assertion.ViewAssertions.doesNotExist
import androidx.test.espresso.matcher.ViewMatchers
import androidx.test.espresso.matcher.ViewMatchers.isAssignableFrom
import androidx.test.espresso.matcher.ViewMatchers.withId
-import com.instructure.canvas.espresso.containsTextCaseInsensitive
import com.instructure.espresso.OnViewWithId
import com.instructure.espresso.assertDisplayed
import com.instructure.espresso.click
import com.instructure.espresso.page.BasePage
import com.instructure.espresso.page.onViewWithText
import com.instructure.espresso.page.plus
-import com.instructure.espresso.page.waitForViewWithId
-import com.instructure.espresso.page.withAncestor
import com.instructure.espresso.page.withDescendant
import com.instructure.espresso.page.withText
import com.instructure.espresso.scrollTo
@@ -57,11 +54,11 @@ class FileUploadPage : BasePage() {
}
fun clickUpload() {
- onView(allOf(isAssignableFrom(Button::class.java),containsTextCaseInsensitive("upload"))).click()
+ onView(allOf(isAssignableFrom(Button::class.java), withText(R.string.upload))).click()
}
fun clickTurnIn() {
- onView(containsTextCaseInsensitive("turn in")).click()
+ onView(withText(R.string.turnIn)).click()
}
fun removeFile(filename: String) {
diff --git a/apps/student/src/androidTest/java/com/instructure/student/ui/pages/ShareExtensionStatusPage.kt b/apps/student/src/androidTest/java/com/instructure/student/ui/pages/ShareExtensionStatusPage.kt
index 0e94d5a7cb..83d8ba9a25 100644
--- a/apps/student/src/androidTest/java/com/instructure/student/ui/pages/ShareExtensionStatusPage.kt
+++ b/apps/student/src/androidTest/java/com/instructure/student/ui/pages/ShareExtensionStatusPage.kt
@@ -18,7 +18,10 @@ package com.instructure.student.ui.pages
import com.instructure.espresso.WaitForViewWithId
import com.instructure.espresso.assertHasText
+import com.instructure.espresso.click
import com.instructure.espresso.page.BasePage
+import com.instructure.espresso.page.onView
+import com.instructure.espresso.page.withId
import com.instructure.student.R
class ShareExtensionStatusPage : BasePage() {
@@ -27,9 +30,20 @@ class ShareExtensionStatusPage : BasePage() {
private val subtitle by WaitForViewWithId(R.id.subtitle)
private val description by WaitForViewWithId(R.id.description)
- fun assertAssignemntSubmissionSuccess() {
+ fun assertAssignmentSubmissionSuccess() {
dialogTitle.assertHasText(R.string.submission)
subtitle.assertHasText(R.string.submissionSuccessTitle)
description.assertHasText(R.string.submissionSuccessMessage)
}
+
+ fun assertFileUploadSuccess() {
+ dialogTitle.assertHasText(R.string.fileUpload)
+ subtitle.assertHasText(R.string.fileUploadSuccess)
+ description.assertHasText(R.string.filesUploadedSuccessfully)
+ }
+
+ fun clickOnDone() {
+ onView(withId(R.id.doneButton)).click()
+ }
+
}
\ No newline at end of file
diff --git a/apps/teacher/src/androidTest/java/com/instructure/teacher/ui/e2e/AnnouncementsE2ETest.kt b/apps/teacher/src/androidTest/java/com/instructure/teacher/ui/e2e/AnnouncementsE2ETest.kt
index 743e8b1ebc..b8102ee75e 100644
--- a/apps/teacher/src/androidTest/java/com/instructure/teacher/ui/e2e/AnnouncementsE2ETest.kt
+++ b/apps/teacher/src/androidTest/java/com/instructure/teacher/ui/e2e/AnnouncementsE2ETest.kt
@@ -50,7 +50,7 @@ class AnnouncementsE2ETest : TeacherTest() {
val announcement = data.announcementsList[0]
- Log.d(STEP_TAG, "Login with user: ${teacher.name}, login id: ${teacher.loginId} , password: ${teacher.password}")
+ Log.d(STEP_TAG, "Login with user: ${teacher.name}, login id: ${teacher.loginId}.")
tokenLogin(teacher)
dashboardPage.waitForRender()
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 10442ff67b..316778c991 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
@@ -53,7 +53,7 @@ class AssignmentE2ETest : TeacherTest() {
val student = data.studentsList[0]
val gradedStudent = data.studentsList[1]
- Log.d(STEP_TAG, "Login with user: ${teacher.name}, login id: ${teacher.loginId} , password: ${teacher.password}")
+ Log.d(STEP_TAG, "Login with user: ${teacher.name}, login id: ${teacher.loginId}.")
tokenLogin(teacher)
dashboardPage.waitForRender()
diff --git a/apps/teacher/src/androidTest/java/com/instructure/teacher/ui/e2e/CourseSettingsE2ETest.kt b/apps/teacher/src/androidTest/java/com/instructure/teacher/ui/e2e/CourseSettingsE2ETest.kt
index 1cfc9d2da9..14b0753b88 100644
--- a/apps/teacher/src/androidTest/java/com/instructure/teacher/ui/e2e/CourseSettingsE2ETest.kt
+++ b/apps/teacher/src/androidTest/java/com/instructure/teacher/ui/e2e/CourseSettingsE2ETest.kt
@@ -52,7 +52,7 @@ class CourseSettingsE2ETest : TeacherTest() {
val firstCourse = data.coursesList[0]
val secondCourse = data.coursesList[1]
- Log.d(STEP_TAG, "Login with user: ${teacher.name}, login id: ${teacher.loginId} , password: ${teacher.password}")
+ Log.d(STEP_TAG, "Login with user: ${teacher.name}, login id: ${teacher.loginId}.")
tokenLogin(teacher)
Log.d(STEP_TAG, "Open ${firstCourse.name} course and click on Course Settings button.")
diff --git a/apps/teacher/src/androidTest/java/com/instructure/teacher/ui/e2e/DashboardE2ETest.kt b/apps/teacher/src/androidTest/java/com/instructure/teacher/ui/e2e/DashboardE2ETest.kt
index 1d4a119183..26512953d4 100644
--- a/apps/teacher/src/androidTest/java/com/instructure/teacher/ui/e2e/DashboardE2ETest.kt
+++ b/apps/teacher/src/androidTest/java/com/instructure/teacher/ui/e2e/DashboardE2ETest.kt
@@ -48,7 +48,7 @@ class DashboardE2ETest : TeacherTest() {
val course1 = data.coursesList[0]
val course2 = data.coursesList[1]
- Log.d(STEP_TAG, "Login with user: ${teacher.name}, login id: ${teacher.loginId} , password: ${teacher.password}")
+ Log.d(STEP_TAG, "Login with user: ${teacher.name}, login id: ${teacher.loginId}.")
tokenLogin(teacher)
dashboardPage.waitForRender()
dashboardPage.assertPageObjects()
diff --git a/apps/teacher/src/androidTest/java/com/instructure/teacher/ui/e2e/DiscussionsE2ETest.kt b/apps/teacher/src/androidTest/java/com/instructure/teacher/ui/e2e/DiscussionsE2ETest.kt
index 788a7a6a80..99fb564d98 100644
--- a/apps/teacher/src/androidTest/java/com/instructure/teacher/ui/e2e/DiscussionsE2ETest.kt
+++ b/apps/teacher/src/androidTest/java/com/instructure/teacher/ui/e2e/DiscussionsE2ETest.kt
@@ -46,7 +46,7 @@ class DiscussionsE2ETest : TeacherTest() {
val course = data.coursesList[0]
val discussion = data.discussionsList[0]
- Log.d(STEP_TAG, "Login with user: ${teacher.name}, login id: ${teacher.loginId} , password: ${teacher.password}")
+ Log.d(STEP_TAG, "Login with user: ${teacher.name}, login id: ${teacher.loginId}.")
tokenLogin(teacher)
dashboardPage.waitForRender()
diff --git a/apps/teacher/src/androidTest/java/com/instructure/teacher/ui/e2e/FilesE2ETest.kt b/apps/teacher/src/androidTest/java/com/instructure/teacher/ui/e2e/FilesE2ETest.kt
index f755422921..ecd4f75249 100644
--- a/apps/teacher/src/androidTest/java/com/instructure/teacher/ui/e2e/FilesE2ETest.kt
+++ b/apps/teacher/src/androidTest/java/com/instructure/teacher/ui/e2e/FilesE2ETest.kt
@@ -116,7 +116,7 @@ class FilesE2ETest: TeacherTest() {
token = student.token
)
- Log.d(STEP_TAG, "Login with user: ${teacher.name}, login id: ${teacher.loginId} , password: ${teacher.password}")
+ Log.d(STEP_TAG, "Login with user: ${teacher.name}, login id: ${teacher.loginId}.")
tokenLogin(teacher)
dashboardPage.waitForRender()
diff --git a/apps/teacher/src/androidTest/java/com/instructure/teacher/ui/e2e/InboxE2ETest.kt b/apps/teacher/src/androidTest/java/com/instructure/teacher/ui/e2e/InboxE2ETest.kt
index a68d861ce8..3cdd76d8a9 100644
--- a/apps/teacher/src/androidTest/java/com/instructure/teacher/ui/e2e/InboxE2ETest.kt
+++ b/apps/teacher/src/androidTest/java/com/instructure/teacher/ui/e2e/InboxE2ETest.kt
@@ -42,7 +42,7 @@ class InboxE2ETest : TeacherTest() {
Log.d(PREPARATION_TAG, "Create group membership for ${student1.name} student to the group: ${group.name}.")
GroupsApi.createGroupMembership(group.id, student1.id, teacher.token)
- Log.d(STEP_TAG, "Login with user: ${teacher.name}, login id: ${teacher.loginId} , password: ${teacher.password}")
+ Log.d(STEP_TAG, "Login with user: ${teacher.name}, login id: ${teacher.loginId}.")
tokenLogin(teacher)
dashboardPage.waitForRender()
dashboardPage.assertDisplaysCourse(course)
diff --git a/apps/teacher/src/androidTest/java/com/instructure/teacher/ui/e2e/LoginE2ETest.kt b/apps/teacher/src/androidTest/java/com/instructure/teacher/ui/e2e/LoginE2ETest.kt
index 4467a12511..ba2f529c3d 100644
--- a/apps/teacher/src/androidTest/java/com/instructure/teacher/ui/e2e/LoginE2ETest.kt
+++ b/apps/teacher/src/androidTest/java/com/instructure/teacher/ui/e2e/LoginE2ETest.kt
@@ -57,7 +57,7 @@ class LoginE2ETest : TeacherTest() {
Log.d(STEP_TAG,"Click on 'Next' button on the Toolbar.")
loginFindSchoolPage.clickToolbarNextMenuItem()
- Log.d(STEP_TAG,"Login with user: ${teacher1.name}, login id: ${teacher1.loginId} , password: ${teacher1.password}")
+ Log.d(STEP_TAG,"Login with user: ${teacher1.name}, login id: ${teacher1.loginId}.")
loginSignInPage.loginAs(teacher1)
Log.d(STEP_TAG,"Assert that the Dashboard Page is the landing page and it is loaded successfully.")
@@ -78,7 +78,7 @@ class LoginE2ETest : TeacherTest() {
Log.d(STEP_TAG,"Click on 'Next' button on the Toolbar.")
loginFindSchoolPage.clickToolbarNextMenuItem()
- Log.d(STEP_TAG,"Login with user: ${teacher2.name}, login id: ${teacher2.loginId} , password: ${teacher2.password}")
+ Log.d(STEP_TAG,"Login with user: ${teacher2.name}, login id: ${teacher2.loginId}.")
loginSignInPage.loginAs(teacher2)
Log.d(STEP_TAG,"Assert that the Dashboard Page is the landing page and it is loaded successfully.")
@@ -99,7 +99,7 @@ class LoginE2ETest : TeacherTest() {
Log.d(STEP_TAG,"Click on 'Next' button on the Toolbar.")
loginFindSchoolPage.clickToolbarNextMenuItem()
- Log.d(STEP_TAG,"Login with user: ${teacher1.name}, login id: ${teacher1.loginId} , password: ${teacher1.password}")
+ Log.d(STEP_TAG,"Login with user: ${teacher1.name}, login id: ${teacher1.loginId}.")
loginSignInPage.loginAs(teacher1)
Log.d(STEP_TAG,"Assert that the Dashboard Page is the landing page and it is loaded successfully.")
@@ -142,7 +142,7 @@ class LoginE2ETest : TeacherTest() {
Log.d(STEP_TAG,"Enter domain: ${parent.domain}.")
loginFindSchoolPage.clickToolbarNextMenuItem()
- Log.d(STEP_TAG,"Login with user: ${student.name}, login id: ${student.loginId} , password: ${student.password}")
+ Log.d(STEP_TAG,"Login with user: ${student.name}, login id: ${student.loginId}.")
loginSignInPage.loginAs(student)
Log.d(STEP_TAG,"Assert that the user has been landed on 'Not a teacher?' Page.")
@@ -166,7 +166,7 @@ class LoginE2ETest : TeacherTest() {
Log.d(STEP_TAG,"Assert that the Login page has been displayed.")
loginSignInPage.assertPageObjects()
- Log.d(STEP_TAG,"Login with user: ${parent.name}, login id: ${parent.loginId} , password: ${parent.password}")
+ Log.d(STEP_TAG,"Login with user: ${parent.name}, login id: ${parent.loginId}.")
loginSignInPage.loginAs(parent)
Log.d(STEP_TAG,"Assert that the user has been landed on 'Not a teacher?' Page.")
diff --git a/apps/teacher/src/androidTest/java/com/instructure/teacher/ui/e2e/ModulesE2ETest.kt b/apps/teacher/src/androidTest/java/com/instructure/teacher/ui/e2e/ModulesE2ETest.kt
index e3e35ac233..a5861e1bbf 100644
--- a/apps/teacher/src/androidTest/java/com/instructure/teacher/ui/e2e/ModulesE2ETest.kt
+++ b/apps/teacher/src/androidTest/java/com/instructure/teacher/ui/e2e/ModulesE2ETest.kt
@@ -1,10 +1,10 @@
package com.instructure.teacher.ui.e2e
import android.util.Log
-import androidx.test.espresso.Espresso
import com.instructure.canvas.espresso.E2E
-import com.instructure.canvas.espresso.refresh
-import com.instructure.dataseeding.api.*
+import com.instructure.dataseeding.api.AssignmentsApi
+import com.instructure.dataseeding.api.ModulesApi
+import com.instructure.dataseeding.api.QuizzesApi
import com.instructure.dataseeding.model.ModuleItemTypes
import com.instructure.dataseeding.model.SubmissionType
import com.instructure.dataseeding.util.days
@@ -38,7 +38,7 @@ class ModulesE2ETest : TeacherTest() {
val teacher = data.teachersList[0]
val course = data.coursesList[0]
- Log.d(STEP_TAG, "Login with user: ${teacher.name}, login id: ${teacher.loginId} , password: ${teacher.password}")
+ Log.d(STEP_TAG, "Login with user: ${teacher.name}, login id: ${teacher.loginId}.")
tokenLogin(teacher)
dashboardPage.waitForRender()
dashboardPage.assertDisplaysCourse(course)
diff --git a/apps/teacher/src/androidTest/java/com/instructure/teacher/ui/e2e/PagesE2ETest.kt b/apps/teacher/src/androidTest/java/com/instructure/teacher/ui/e2e/PagesE2ETest.kt
index f271eb90ac..66de5ce460 100644
--- a/apps/teacher/src/androidTest/java/com/instructure/teacher/ui/e2e/PagesE2ETest.kt
+++ b/apps/teacher/src/androidTest/java/com/instructure/teacher/ui/e2e/PagesE2ETest.kt
@@ -59,7 +59,7 @@ class PagesE2ETest : TeacherTest() {
body = ""
)
- Log.d(STEP_TAG, "Login with user: ${teacher.name}, login id: ${teacher.loginId} , password: ${teacher.password}")
+ Log.d(STEP_TAG, "Login with user: ${teacher.name}, login id: ${teacher.loginId}.")
tokenLogin(teacher)
dashboardPage.waitForRender()
diff --git a/apps/teacher/src/androidTest/java/com/instructure/teacher/ui/e2e/PeopleE2ETest.kt b/apps/teacher/src/androidTest/java/com/instructure/teacher/ui/e2e/PeopleE2ETest.kt
index 162dffae72..52120c04bf 100644
--- a/apps/teacher/src/androidTest/java/com/instructure/teacher/ui/e2e/PeopleE2ETest.kt
+++ b/apps/teacher/src/androidTest/java/com/instructure/teacher/ui/e2e/PeopleE2ETest.kt
@@ -21,13 +21,13 @@ import androidx.test.espresso.Espresso
import com.instructure.canvas.espresso.E2E
import com.instructure.dataseeding.api.SubmissionsApi
import com.instructure.dataseeding.model.SubmissionType
+import com.instructure.dataseeding.util.days
+import com.instructure.dataseeding.util.fromNow
+import com.instructure.dataseeding.util.iso8601
import com.instructure.panda_annotations.FeatureCategory
import com.instructure.panda_annotations.Priority
import com.instructure.panda_annotations.TestCategory
import com.instructure.panda_annotations.TestMetaData
-import com.instructure.dataseeding.util.days
-import com.instructure.dataseeding.util.fromNow
-import com.instructure.dataseeding.util.iso8601
import com.instructure.teacher.ui.utils.*
import dagger.hilt.android.testing.HiltAndroidTest
import org.junit.Test
@@ -82,7 +82,7 @@ class PeopleE2ETest: TeacherTest() {
excused = false
)
- Log.d(STEP_TAG, "Login with user: ${teacher.name}, login id: ${teacher.loginId} , password: ${teacher.password}")
+ Log.d(STEP_TAG, "Login with user: ${teacher.name}, login id: ${teacher.loginId}.")
tokenLogin(teacher)
Log.d(STEP_TAG,"Open ${course.name} course and navigate to People Page.")
diff --git a/apps/teacher/src/androidTest/java/com/instructure/teacher/ui/e2e/QuizE2ETest.kt b/apps/teacher/src/androidTest/java/com/instructure/teacher/ui/e2e/QuizE2ETest.kt
index c4c719aa11..11cf2fe334 100644
--- a/apps/teacher/src/androidTest/java/com/instructure/teacher/ui/e2e/QuizE2ETest.kt
+++ b/apps/teacher/src/androidTest/java/com/instructure/teacher/ui/e2e/QuizE2ETest.kt
@@ -46,7 +46,7 @@ class QuizE2ETest: TeacherTest() {
val teacher = data.teachersList[0]
val course = data.coursesList[0]
- Log.d(STEP_TAG, "Login with user: ${teacher.name}, login id: ${teacher.loginId} , password: ${teacher.password}")
+ Log.d(STEP_TAG, "Login with user: ${teacher.name}, login id: ${teacher.loginId}.")
tokenLogin(teacher)
dashboardPage.waitForRender()
diff --git a/apps/teacher/src/androidTest/java/com/instructure/teacher/ui/e2e/SettingsE2ETest.kt b/apps/teacher/src/androidTest/java/com/instructure/teacher/ui/e2e/SettingsE2ETest.kt
index f64998d7ee..472f481bee 100644
--- a/apps/teacher/src/androidTest/java/com/instructure/teacher/ui/e2e/SettingsE2ETest.kt
+++ b/apps/teacher/src/androidTest/java/com/instructure/teacher/ui/e2e/SettingsE2ETest.kt
@@ -50,7 +50,7 @@ class SettingsE2ETest : TeacherTest() {
val data = seedData(students = 1, teachers = 1, courses = 1)
val teacher = data.teachersList[0]
- Log.d(STEP_TAG, "Login with user: ${teacher.name}, login id: ${teacher.loginId} , password: ${teacher.password}")
+ Log.d(STEP_TAG, "Login with user: ${teacher.name}, login id: ${teacher.loginId}.")
tokenLogin(teacher)
dashboardPage.waitForRender()
@@ -103,7 +103,7 @@ class SettingsE2ETest : TeacherTest() {
val teacher = data.teachersList[0]
val course = data.coursesList[0]
- Log.d(STEP_TAG, "Login with user: ${teacher.name}, login id: ${teacher.loginId} , password: ${teacher.password}")
+ Log.d(STEP_TAG, "Login with user: ${teacher.name}, login id: ${teacher.loginId}.")
tokenLogin(teacher)
dashboardPage.waitForRender()
@@ -152,7 +152,7 @@ class SettingsE2ETest : TeacherTest() {
val data = seedData(students = 1, teachers = 1, courses = 1)
val teacher = data.teachersList[0]
- Log.d(STEP_TAG, "Login with user: ${teacher.name}, login id: ${teacher.loginId} , password: ${teacher.password}")
+ Log.d(STEP_TAG, "Login with user: ${teacher.name}, login id: ${teacher.loginId}.")
tokenLogin(teacher)
dashboardPage.waitForRender()
@@ -174,7 +174,7 @@ class SettingsE2ETest : TeacherTest() {
val data = seedData(students = 1, teachers = 1, courses = 1)
val teacher = data.teachersList[0]
- Log.d(STEP_TAG, "Login with user: ${teacher.name}, login id: ${teacher.loginId} , password: ${teacher.password}")
+ Log.d(STEP_TAG, "Login with user: ${teacher.name}, login id: ${teacher.loginId}.")
tokenLogin(teacher)
dashboardPage.waitForRender()
@@ -199,7 +199,7 @@ class SettingsE2ETest : TeacherTest() {
val data = seedData(students = 1, teachers = 1, courses = 1)
val teacher = data.teachersList[0]
- Log.d(STEP_TAG, "Login with user: ${teacher.name}, login id: ${teacher.loginId} , password: ${teacher.password}")
+ Log.d(STEP_TAG, "Login with user: ${teacher.name}, login id: ${teacher.loginId}.")
tokenLogin(teacher)
dashboardPage.waitForRender()
diff --git a/apps/teacher/src/androidTest/java/com/instructure/teacher/ui/e2e/SpeedGraderE2ETest.kt b/apps/teacher/src/androidTest/java/com/instructure/teacher/ui/e2e/SpeedGraderE2ETest.kt
index 258a7ae1ae..908de08abd 100644
--- a/apps/teacher/src/androidTest/java/com/instructure/teacher/ui/e2e/SpeedGraderE2ETest.kt
+++ b/apps/teacher/src/androidTest/java/com/instructure/teacher/ui/e2e/SpeedGraderE2ETest.kt
@@ -101,7 +101,7 @@ class SpeedGraderE2ETest : TeacherTest() {
excused = false
)
- Log.d(STEP_TAG, "Login with user: ${teacher.name}, login id: ${teacher.loginId} , password: ${teacher.password}")
+ Log.d(STEP_TAG, "Login with user: ${teacher.name}, login id: ${teacher.loginId}.")
tokenLogin(teacher)
Log.d(STEP_TAG,"Open ${course.name} course and navigate to Assignments Page.")
diff --git a/apps/teacher/src/androidTest/java/com/instructure/teacher/ui/e2e/SyllabusE2ETest.kt b/apps/teacher/src/androidTest/java/com/instructure/teacher/ui/e2e/SyllabusE2ETest.kt
index f3122d2a5e..b3b1ad29ca 100644
--- a/apps/teacher/src/androidTest/java/com/instructure/teacher/ui/e2e/SyllabusE2ETest.kt
+++ b/apps/teacher/src/androidTest/java/com/instructure/teacher/ui/e2e/SyllabusE2ETest.kt
@@ -32,7 +32,7 @@ class SyllabusE2ETest : TeacherTest() {
val teacher = data.teachersList[0]
val course = data.coursesList[0]
- Log.d(STEP_TAG, "Login with user: ${teacher.name}, login id: ${teacher.loginId} , password: ${teacher.password}")
+ Log.d(STEP_TAG, "Login with user: ${teacher.name}, login id: ${teacher.loginId}.")
tokenLogin(teacher)
dashboardPage.waitForRender()
diff --git a/apps/teacher/src/androidTest/java/com/instructure/teacher/ui/e2e/TodoE2ETest.kt b/apps/teacher/src/androidTest/java/com/instructure/teacher/ui/e2e/TodoE2ETest.kt
index 5c41d4fecf..475ce6bf3d 100644
--- a/apps/teacher/src/androidTest/java/com/instructure/teacher/ui/e2e/TodoE2ETest.kt
+++ b/apps/teacher/src/androidTest/java/com/instructure/teacher/ui/e2e/TodoE2ETest.kt
@@ -70,7 +70,7 @@ class TodoE2ETest : TeacherTest() {
studentToken = student.token
)
- Log.d(STEP_TAG, "Login with user: ${teacher.name}, login id: ${teacher.loginId} , password: ${teacher.password}")
+ Log.d(STEP_TAG, "Login with user: ${teacher.name}, login id: ${teacher.loginId}.")
tokenLogin(teacher)
dashboardPage.waitForRender()
From 3c4839ea96084ccab282546e746216ebbe1f9d9f Mon Sep 17 00:00:00 2001
From: Kristof Nemere <109959688+kristofnemere@users.noreply.github.com>
Date: Tue, 4 Oct 2022 11:32:08 +0200
Subject: [PATCH 14/73] [MBL-16274][Student] Inconsistent reordering of
announcement list after view
refs: MBL-16274
affects: Student
release note: Fixed an announcement list reordering bug.
---
.../com/instructure/student/fragment/DiscussionListFragment.kt | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/apps/student/src/main/java/com/instructure/student/fragment/DiscussionListFragment.kt b/apps/student/src/main/java/com/instructure/student/fragment/DiscussionListFragment.kt
index 9a4351a229..b020383bba 100644
--- a/apps/student/src/main/java/com/instructure/student/fragment/DiscussionListFragment.kt
+++ b/apps/student/src/main/java/com/instructure/student/fragment/DiscussionListFragment.kt
@@ -335,6 +335,7 @@ open class DiscussionListFragment : ParentFragment(), Bookmarkable {
@Suppress("unused")
@Subscribe(threadMode = ThreadMode.MAIN, sticky = true)
fun onDiscussionTopicCountChange(event: DiscussionTopicHeaderEvent) {
+ if (isAnnouncement) return
event.get {
// Gets written over on phones - added also to {@link #onRefreshFinished()}
when {
@@ -345,7 +346,7 @@ open class DiscussionListFragment : ParentFragment(), Bookmarkable {
recyclerAdapter.addOrUpdateItem(DiscussionListRecyclerAdapter.CLOSED_FOR_COMMENTS, it)
}
else -> {
- if (!isAnnouncement) recyclerAdapter.addOrUpdateItem(DiscussionListRecyclerAdapter.UNPINNED, it)
+ recyclerAdapter.addOrUpdateItem(DiscussionListRecyclerAdapter.UNPINNED, it)
}
}
}
From 4bc36340db97aae719bc5e85c0a131db3edfad0b Mon Sep 17 00:00:00 2001
From: Kristof Nemere <109959688+kristofnemere@users.noreply.github.com>
Date: Tue, 4 Oct 2022 11:33:17 +0200
Subject: [PATCH 15/73] [MBL-15693][Student] Course announcement notifications
refs: MBL-15693
affects: Student
release note: Added notification count badge.
* Course announcement notifications
* Fixed tests
* Refresh notifications when needed
* Fixed comments
* Fixed comments
---
.../student/activity/CallbackActivity.kt | 18 +++++++++++-
.../student/activity/NavigationActivity.kt | 6 ++--
.../NotificationListRecyclerAdapter.kt | 8 +++--
.../fragment/DiscussionDetailsFragment.kt | 1 +
.../fragment/NotificationListFragment.kt | 29 +++++++++++++++++--
.../student/holders/NotificationViewHolder.kt | 10 +++----
.../NotificationAdapterToFragmentCallback.kt | 1 +
.../AssignmentDetailsPresenter.kt | 20 ++++++++++---
.../res/layout/viewholder_notification.xml | 12 ++++++--
.../canvasapi2/apis/DiscussionAPI.kt | 9 +++++-
.../instructure/canvasapi2/apis/InboxApi.kt | 10 +++----
.../canvasapi2/apis/SubmissionAPI.kt | 16 ++++++++++
.../canvasapi2/apis/UnreadCountAPI.kt | 2 +-
.../canvasapi2/managers/DiscussionManager.kt | 10 +++++++
.../canvasapi2/managers/InboxManager.kt | 6 ++--
.../canvasapi2/managers/SubmissionManager.kt | 10 +++++++
.../canvasapi2/managers/UnreadCountManager.kt | 6 ++++
.../pact/canvas/apis/InboxApiPactTests.kt | 4 +--
libs/pandares/src/main/res/values/strings.xml | 5 ++++
.../pandautils/utils/Extensions.kt | 6 +++-
20 files changed, 158 insertions(+), 31 deletions(-)
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 ff7e145dd8..ea6c6fe56c 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
@@ -38,18 +38,20 @@ import com.instructure.student.BuildConfig
import com.instructure.student.R
import com.instructure.student.flutterChannels.FlutterComm
import com.instructure.student.fragment.InboxFragment
+import com.instructure.student.fragment.NotificationListFragment
import com.instructure.student.service.StudentPageViewService
import com.instructure.student.util.StudentPrefs
import kotlinx.coroutines.Job
import retrofit2.Call
import retrofit2.Response
-abstract class CallbackActivity : ParentActivity(), InboxFragment.OnUnreadCountInvalidated {
+abstract class CallbackActivity : ParentActivity(), InboxFragment.OnUnreadCountInvalidated, NotificationListFragment.OnNotificationCountInvalidated {
private var loadInitialDataJob: Job? = null
abstract fun gotLaunchDefinitions(launchDefinitions: List?)
abstract fun updateUnreadCount(unreadCount: Int)
+ abstract fun updateNotificationCount(notificationCount: Int)
abstract fun initialCoreDataLoadingComplete()
override fun onCreate(savedInstanceState: Bundle?) {
@@ -123,6 +125,8 @@ abstract class CallbackActivity : ParentActivity(), InboxFragment.OnUnreadCountI
// get unread count of conversations
getUnreadMessageCount()
+ getUnreadNotificationCount()
+
initialCoreDataLoadingComplete()
} catch {
initialCoreDataLoadingComplete()
@@ -137,6 +141,14 @@ abstract class CallbackActivity : ParentActivity(), InboxFragment.OnUnreadCountI
}
}
+ private fun getUnreadNotificationCount() {
+ UnreadCountManager.getUnreadNotificationCount(object : StatusCallback>() {
+ override fun onResponse(data: Call>, response: Response>) {
+ updateNotificationCount(response.body()?.sumOf { it.unreadCount.orDefault() }.orDefault())
+ }
+ }, true)
+ }
+
private val themeCallback = object : StatusCallback() {
override fun onResponse(response: Response, linkHeaders: LinkHeaders, type: ApiType) {
//store the theme
@@ -201,6 +213,10 @@ abstract class CallbackActivity : ParentActivity(), InboxFragment.OnUnreadCountI
}
}
+ override fun invalidateNotificationCount() {
+ getUnreadNotificationCount()
+ }
+
/**
* This will fetch the user forcing a network request
*/
diff --git a/apps/student/src/main/java/com/instructure/student/activity/NavigationActivity.kt b/apps/student/src/main/java/com/instructure/student/activity/NavigationActivity.kt
index c5311a36c8..8d32f87378 100644
--- a/apps/student/src/main/java/com/instructure/student/activity/NavigationActivity.kt
+++ b/apps/student/src/main/java/com/instructure/student/activity/NavigationActivity.kt
@@ -23,7 +23,6 @@ import android.content.Intent
import android.content.pm.PackageManager
import android.content.res.Configuration
import android.graphics.Color
-import android.graphics.Typeface
import android.os.Bundle
import android.os.Handler
import android.view.MenuItem
@@ -46,7 +45,6 @@ import androidx.fragment.app.DialogFragment
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentManager
import com.airbnb.lottie.LottieAnimationView
-import com.bumptech.glide.Glide
import com.google.android.material.bottomnavigation.BottomNavigationView
import com.instructure.canvasapi2.CanvasRestAdapter
import com.instructure.canvasapi2.managers.CourseManager
@@ -1025,6 +1023,10 @@ class NavigationActivity : BaseRouterActivity(), Navigation, MasqueradingDialog.
updateBottomBarBadge(R.id.bottomNavigationInbox, unreadCount, R.plurals.a11y_inboxUnreadCount)
}
+ override fun updateNotificationCount(notificationCount: Int) {
+ updateBottomBarBadge(R.id.bottomNavigationNotifications, notificationCount, R.plurals.a11y_notificationsUnreadCount)
+ }
+
private fun updateBottomBarBadge(@IdRes menuItemId: Int, count: Int, @PluralsRes quantityContentDescription: Int? = null) {
if (count > 0) {
bottomBar.getOrCreateBadge(menuItemId).number = count
diff --git a/apps/student/src/main/java/com/instructure/student/adapter/NotificationListRecyclerAdapter.kt b/apps/student/src/main/java/com/instructure/student/adapter/NotificationListRecyclerAdapter.kt
index b6d5845683..ff1cf533e7 100644
--- a/apps/student/src/main/java/com/instructure/student/adapter/NotificationListRecyclerAdapter.kt
+++ b/apps/student/src/main/java/com/instructure/student/adapter/NotificationListRecyclerAdapter.kt
@@ -303,7 +303,8 @@ class NotificationListRecyclerAdapter(
notifyDataSetChanged()
}
}
- }
+ },
+ false
)
}
@@ -319,7 +320,10 @@ class NotificationListRecyclerAdapter(
streamItem.id,
object : StatusCallback() {
override fun onResponse(response: Response, linkHeaders: LinkHeaders, type: ApiType) {
- if (response.body()!!.isHidden) removeItem(streamItem)
+ if (response.body()!!.isHidden) {
+ removeItem(streamItem)
+ adapterToFragmentCallback.onItemRemoved()
+ }
}
override fun onFail(call: Call?, error: Throwable, response: Response<*>?) {
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 fdc6ba1ea4..71c4cbf429 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
@@ -405,6 +405,7 @@ class DiscussionDetailsFragment : ParentFragment(), Bookmarkable {
private fun setupHeaderWebView() {
setupWebView(discussionTopicHeaderWebView)
discussionTopicHeaderWebView.addJavascriptInterface(JSDiscussionHeaderInterface(), "accessor")
+ DiscussionManager.markDiscussionTopicRead(canvasContext, getTopicId(), object : StatusCallback() {})
}
@SuppressLint("SetJavaScriptEnabled")
diff --git a/apps/student/src/main/java/com/instructure/student/fragment/NotificationListFragment.kt b/apps/student/src/main/java/com/instructure/student/fragment/NotificationListFragment.kt
index a0fd308b16..9eeaabb39b 100644
--- a/apps/student/src/main/java/com/instructure/student/fragment/NotificationListFragment.kt
+++ b/apps/student/src/main/java/com/instructure/student/fragment/NotificationListFragment.kt
@@ -24,6 +24,7 @@ import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Toast
+import androidx.fragment.app.FragmentManager
import com.instructure.canvasapi2.models.*
import com.instructure.canvasapi2.models.StreamItem.Type.*
import com.instructure.canvasapi2.utils.ApiPrefs
@@ -47,7 +48,7 @@ import kotlinx.android.synthetic.main.panda_recycler_refresh_layout.*
@ScreenView(SCREEN_VIEW_NOTIFICATION_LIST)
@PageView
-class NotificationListFragment : ParentFragment(), Bookmarkable {
+class NotificationListFragment : ParentFragment(), Bookmarkable, FragmentManager.OnBackStackChangedListener {
@PageViewUrl
@Suppress("unused")
@@ -79,6 +80,7 @@ class NotificationListFragment : ParentFragment(), Bookmarkable {
emptyView.setGuidelines(.28f,.6f,.73f,.12f,.88f)
}
}
+ (activity as? OnNotificationCountInvalidated)?.invalidateNotificationCount()
}
override fun onShowEditView(isVisible: Boolean) {
@@ -88,12 +90,15 @@ class NotificationListFragment : ParentFragment(), Bookmarkable {
override fun onShowErrorCrouton(message: Int) {
showToast(message)
}
+
+ override fun onItemRemoved() {
+ (activity as? OnNotificationCountInvalidated)?.invalidateNotificationCount()
+ }
}
// Used to help determine if the bottom bar should be highlighted
fun isCourseOrGroup(): Boolean = canvasContext.isCourseOrGroup
-
override fun title(): String = getString(if (canvasContext.isCourse || canvasContext.isGroup) R.string.homePageIdForNotifications else R.string.notifications)
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View?
@@ -119,10 +124,25 @@ class NotificationListFragment : ParentFragment(), Bookmarkable {
cancelButton.setOnClickListener { recyclerAdapter.cancelButtonClicked() }
applyTheme()
+
+ activity?.supportFragmentManager?.addOnBackStackChangedListener(this)
+ }
+
+ private var shouldRefreshOnResume = false
+
+ override fun onBackStackChanged() {
+ if (activity?.supportFragmentManager?.fragments?.lastOrNull()?.javaClass == this.javaClass) {
+ if (shouldRefreshOnResume) {
+ swipeRefreshLayout.isRefreshing = true
+ recyclerAdapter.refresh()
+ shouldRefreshOnResume = false
+ }
+ }
}
override fun onDestroyView() {
recyclerAdapter.cancel()
+ activity?.supportFragmentManager?.removeOnBackStackChangedListener(this)
super.onDestroyView()
}
@@ -184,12 +204,17 @@ class NotificationListFragment : ParentFragment(), Bookmarkable {
return false
}
addFragmentForStreamItem(streamItem, activity as ParentActivity, false)
+ shouldRefreshOnResume = !streamItem.isReadState
return true
}
override val bookmark: Bookmarker
get() = Bookmarker(canvasContext.isCourseOrGroup, canvasContext)
+ interface OnNotificationCountInvalidated {
+ fun invalidateNotificationCount()
+ }
+
companion object {
fun addFragmentForStreamItem(streamItem: StreamItem, context: Context, fromWidget: Boolean) {
if (fromWidget) {
diff --git a/apps/student/src/main/java/com/instructure/student/holders/NotificationViewHolder.kt b/apps/student/src/main/java/com/instructure/student/holders/NotificationViewHolder.kt
index 10c978c857..17e2fa47bb 100644
--- a/apps/student/src/main/java/com/instructure/student/holders/NotificationViewHolder.kt
+++ b/apps/student/src/main/java/com/instructure/student/holders/NotificationViewHolder.kt
@@ -25,14 +25,11 @@ import androidx.core.content.ContextCompat
import androidx.recyclerview.widget.RecyclerView
import com.instructure.canvasapi2.models.CanvasContext
import com.instructure.canvasapi2.models.StreamItem
-import com.instructure.pandautils.utils.ColorKeeper
-import com.instructure.pandautils.utils.ThemePrefs
-import com.instructure.pandautils.utils.setGone
-import com.instructure.pandautils.utils.setVisible
+import com.instructure.pandautils.utils.*
import com.instructure.student.R
import com.instructure.student.adapter.NotificationListRecyclerAdapter
-import com.instructure.student.util.BinderUtils
import com.instructure.student.interfaces.NotificationAdapterToFragmentCallback
+import com.instructure.student.util.BinderUtils
import kotlinx.android.synthetic.main.viewholder_notification.view.*
class NotificationViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
@@ -155,8 +152,11 @@ class NotificationViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView)
// Read/Unread
if (item.isReadState) {
title.setTypeface(null, Typeface.NORMAL)
+ unreadMark.setInvisible()
} else {
title.setTypeface(null, Typeface.BOLD)
+ unreadMark.setVisible()
+ unreadMark.setImageDrawable(ColorUtils.colorIt(ThemePrefs.accentColor, unreadMark.drawable))
}
}
diff --git a/apps/student/src/main/java/com/instructure/student/interfaces/NotificationAdapterToFragmentCallback.kt b/apps/student/src/main/java/com/instructure/student/interfaces/NotificationAdapterToFragmentCallback.kt
index 0171efd8cf..068a2a7fbf 100644
--- a/apps/student/src/main/java/com/instructure/student/interfaces/NotificationAdapterToFragmentCallback.kt
+++ b/apps/student/src/main/java/com/instructure/student/interfaces/NotificationAdapterToFragmentCallback.kt
@@ -21,4 +21,5 @@ interface NotificationAdapterToFragmentCallback {
fun onRefreshFinished()
fun onShowEditView(isVisible: Boolean)
fun onShowErrorCrouton(message: Int)
+ fun onItemRemoved() = Unit
}
diff --git a/apps/student/src/main/java/com/instructure/student/mobius/assignmentDetails/AssignmentDetailsPresenter.kt b/apps/student/src/main/java/com/instructure/student/mobius/assignmentDetails/AssignmentDetailsPresenter.kt
index 4635932219..90f466d5a1 100644
--- a/apps/student/src/main/java/com/instructure/student/mobius/assignmentDetails/AssignmentDetailsPresenter.kt
+++ b/apps/student/src/main/java/com/instructure/student/mobius/assignmentDetails/AssignmentDetailsPresenter.kt
@@ -17,10 +17,14 @@
package com.instructure.student.mobius.assignmentDetails
import android.content.Context
-import android.view.accessibility.AccessibilityManager
import androidx.core.content.ContextCompat
+import com.instructure.canvasapi2.StatusCallback
+import com.instructure.canvasapi2.managers.SubmissionManager
import com.instructure.canvasapi2.models.*
-import com.instructure.canvasapi2.utils.*
+import com.instructure.canvasapi2.utils.DateHelper
+import com.instructure.canvasapi2.utils.NumberHelper
+import com.instructure.canvasapi2.utils.isRtl
+import com.instructure.canvasapi2.utils.isValid
import com.instructure.pandautils.utils.AssignmentUtils2
import com.instructure.student.R
import com.instructure.student.Submission
@@ -31,8 +35,7 @@ import com.instructure.student.mobius.assignmentDetails.ui.QuizDescriptionViewSt
import com.instructure.student.mobius.assignmentDetails.ui.gradeCell.GradeCellViewState
import com.instructure.student.mobius.common.ui.Presenter
import java.text.DateFormat
-import java.util.Date
-import java.util.Locale
+import java.util.*
object AssignmentDetailsPresenter : Presenter {
override fun present(model: AssignmentDetailsModel, context: Context): AssignmentDetailsViewState {
@@ -55,10 +58,19 @@ object AssignmentDetailsPresenter : Presenter() {})
+ }
+
private fun presentLoadedState(
assignment: Assignment,
quiz: Quiz?,
diff --git a/apps/student/src/main/res/layout/viewholder_notification.xml b/apps/student/src/main/res/layout/viewholder_notification.xml
index 481642070b..9cae4a96d3 100644
--- a/apps/student/src/main/res/layout/viewholder_notification.xml
+++ b/apps/student/src/main/res/layout/viewholder_notification.xml
@@ -16,12 +16,20 @@
-->
+ style="@style/AdapterItem">
+
+
diff --git a/libs/canvas-api-2/src/main/java/com/instructure/canvasapi2/apis/DiscussionAPI.kt b/libs/canvas-api-2/src/main/java/com/instructure/canvasapi2/apis/DiscussionAPI.kt
index 20f669f202..23875fb693 100644
--- a/libs/canvas-api-2/src/main/java/com/instructure/canvasapi2/apis/DiscussionAPI.kt
+++ b/libs/canvas-api-2/src/main/java/com/instructure/canvasapi2/apis/DiscussionAPI.kt
@@ -27,7 +27,6 @@ import com.instructure.canvasapi2.models.DiscussionTopicHeader
import com.instructure.canvasapi2.models.postmodels.DiscussionEntryPostBody
import com.instructure.canvasapi2.models.postmodels.DiscussionTopicPostBody
import com.instructure.canvasapi2.utils.APIHelper
-import okhttp3.MediaType
import okhttp3.MediaType.Companion.toMediaTypeOrNull
import okhttp3.MultipartBody
import okhttp3.RequestBody
@@ -88,6 +87,9 @@ object DiscussionAPI {
@POST("{contextType}/{contextId}/discussion_topics/{topicId}/entries/{entryId}/rating")
fun rateDiscussionEntry(@Path("contextType") contextType: String, @Path("contextId") contextId: Long, @Path("topicId") topicId: Long, @Path("entryId") entryId: Long, @Query("rating") rating: Int): Call
+ @PUT("{contextType}/{contextId}/discussion_topics/{topicId}/read")
+ fun markDiscussionTopicRead(@Path("contextType") contextType: String, @Path("contextId") contextId: Long, @Path("topicId") topicId: Long): Call
+
@PUT("{contextType}/{contextId}/discussion_topics/{topicId}/entries/{entryId}/read")
fun markDiscussionTopicEntryRead(@Path("contextType") contextType: String, @Path("contextId") contextId: Long, @Path("topicId") topicId: Long, @Path("entryId") entryId: Long): Call
@@ -270,6 +272,11 @@ object DiscussionAPI {
callback.addCall(adapter.build(DiscussionInterface::class.java, params).rateDiscussionEntry(contextType, canvasContext.id, topicId, entryId, rating)).enqueue(callback)
}
+ fun markDiscussionTopicRead(adapter: RestBuilder, canvasContext: CanvasContext, topicId: Long, callback: StatusCallback, params: RestParams) {
+ val contextType = CanvasContext.getApiContext(canvasContext)
+ callback.addCall(adapter.build(DiscussionInterface::class.java, params).markDiscussionTopicRead(contextType, canvasContext.id, topicId)).enqueue(callback)
+ }
+
fun markDiscussionTopicEntryRead(adapter: RestBuilder, canvasContext: CanvasContext, topicId: Long, entryId: Long, callback: StatusCallback, params: RestParams) {
val contextType = CanvasContext.getApiContext(canvasContext)
callback.addCall(adapter.build(DiscussionInterface::class.java, params).markDiscussionTopicEntryRead(contextType, canvasContext.id, topicId, entryId)).enqueue(callback)
diff --git a/libs/canvas-api-2/src/main/java/com/instructure/canvasapi2/apis/InboxApi.kt b/libs/canvas-api-2/src/main/java/com/instructure/canvasapi2/apis/InboxApi.kt
index 34eab7952f..1603303c17 100644
--- a/libs/canvas-api-2/src/main/java/com/instructure/canvasapi2/apis/InboxApi.kt
+++ b/libs/canvas-api-2/src/main/java/com/instructure/canvasapi2/apis/InboxApi.kt
@@ -66,7 +66,7 @@ object InboxApi {
@Field("bulk_message") isBulk: Int): Call>
@GET("conversations/{conversationId}?include[]=participant_avatars")
- fun getConversation(@Path("conversationId") conversationId: Long): Call
+ fun getConversation(@Path("conversationId") conversationId: Long, @Query("auto_mark_as_read") markAsRead: Boolean): Call
@PUT("conversations/{conversationId}")
fun updateConversation(@Path("conversationId") conversationId: Long, @Query("conversation[workflow_state]") workflowState: String, @Query("conversation[starred]") isStarred: Boolean?): Call
@@ -90,8 +90,8 @@ object InboxApi {
fun markConversationAsUnread(@Query("conversation_ids[]") conversationId: Long, @Query("event") conversationEvent: String): Call
}
- fun getConversation(adapter: RestBuilder, callback: StatusCallback, params: RestParams, conversationId: Long) {
- callback.addCall(adapter.build(InboxInterface::class.java, params).getConversation(conversationId)).enqueue(callback)
+ fun getConversation(adapter: RestBuilder, callback: StatusCallback, params: RestParams, conversationId: Long, markAsRead: Boolean) {
+ callback.addCall(adapter.build(InboxInterface::class.java, params).getConversation(conversationId, markAsRead)).enqueue(callback)
}
fun getConversations(scope: Scope, adapter: RestBuilder, callback: StatusCallback>, params: RestParams) {
@@ -145,7 +145,7 @@ object InboxApi {
}
@Throws(IOException::class)
- fun getConversation(adapter: RestBuilder, params: RestParams, conversationId: Long): Response {
- return adapter.build(InboxInterface::class.java, params).getConversation(conversationId).execute()
+ fun getConversation(adapter: RestBuilder, params: RestParams, conversationId: Long, markAsRead: Boolean): Response {
+ return adapter.build(InboxInterface::class.java, params).getConversation(conversationId, markAsRead).execute()
}
}
diff --git a/libs/canvas-api-2/src/main/java/com/instructure/canvasapi2/apis/SubmissionAPI.kt b/libs/canvas-api-2/src/main/java/com/instructure/canvasapi2/apis/SubmissionAPI.kt
index 6a682599db..fbc5594eec 100644
--- a/libs/canvas-api-2/src/main/java/com/instructure/canvasapi2/apis/SubmissionAPI.kt
+++ b/libs/canvas-api-2/src/main/java/com/instructure/canvasapi2/apis/SubmissionAPI.kt
@@ -131,6 +131,12 @@ object SubmissionAPI {
@GET("courses/{courseId}/assignments/{assignmentId}/submission_summary")
fun getSubmissionSummary(@Path("courseId") courseId: Long,
@Path("assignmentId") assignmentId: Long): Call
+
+ @PUT("courses/{courseId}/assignments/{assignmentId}/submissions/self/read")
+ fun markSubmissionAsRead(
+ @Path("courseId") courseId: Long,
+ @Path("assignmentId") assignmentId: Long
+ ): Call
}
fun getSingleSubmission(courseId: Long, assignmentId: Long, studentId: Long, adapter: RestBuilder, callback: StatusCallback, params: RestParams) {
@@ -212,6 +218,16 @@ object SubmissionAPI {
).enqueue(callback)
}
+ fun markSubmissionAsRead(
+ adapter: RestBuilder,
+ params: RestParams,
+ courseId: Long,
+ assignmentId: Long,
+ callback: StatusCallback
+ ) {
+ callback.addCall(adapter.build(SubmissionInterface::class.java, params).markSubmissionAsRead(courseId, assignmentId)).enqueue(callback)
+ }
+
private fun generateRubricAssessmentQueryMap(rubricAssessment: Map): Map {
val map = mutableMapOf()
for ((criterionIdKey, ratingValue) in rubricAssessment) {
diff --git a/libs/canvas-api-2/src/main/java/com/instructure/canvasapi2/apis/UnreadCountAPI.kt b/libs/canvas-api-2/src/main/java/com/instructure/canvasapi2/apis/UnreadCountAPI.kt
index 0976f05353..c7b54c8bff 100644
--- a/libs/canvas-api-2/src/main/java/com/instructure/canvasapi2/apis/UnreadCountAPI.kt
+++ b/libs/canvas-api-2/src/main/java/com/instructure/canvasapi2/apis/UnreadCountAPI.kt
@@ -16,7 +16,7 @@ internal object UnreadCountAPI {
@GET("conversations/unread_count")
fun getUnreadConversationCount(): Call
- @GET("users/self/activity_stream/summary")
+ @GET("users/self/activity_stream/summary?only_active_courses=true")
fun getNotificationsCount(): Call>
@GET("users/self/observer_alerts/unread_count")
diff --git a/libs/canvas-api-2/src/main/java/com/instructure/canvasapi2/managers/DiscussionManager.kt b/libs/canvas-api-2/src/main/java/com/instructure/canvasapi2/managers/DiscussionManager.kt
index 65810e4509..24aee6a99c 100644
--- a/libs/canvas-api-2/src/main/java/com/instructure/canvasapi2/managers/DiscussionManager.kt
+++ b/libs/canvas-api-2/src/main/java/com/instructure/canvasapi2/managers/DiscussionManager.kt
@@ -134,6 +134,16 @@ object DiscussionManager {
DiscussionAPI.rateDiscussionEntry(adapter, canvasContext, topicId, entryId, rating, callback, params)
}
+ fun markDiscussionTopicRead(
+ canvasContext: CanvasContext,
+ topicId: Long,
+ callback: StatusCallback
+ ) {
+ val adapter = RestBuilder(callback)
+ val params = RestParams()
+ DiscussionAPI.markDiscussionTopicRead(adapter, canvasContext, topicId, callback, params)
+ }
+
fun markDiscussionTopicEntryRead(
canvasContext: CanvasContext,
topicId: Long,
diff --git a/libs/canvas-api-2/src/main/java/com/instructure/canvasapi2/managers/InboxManager.kt b/libs/canvas-api-2/src/main/java/com/instructure/canvasapi2/managers/InboxManager.kt
index f48379ffc5..e8c4d2ce7d 100644
--- a/libs/canvas-api-2/src/main/java/com/instructure/canvasapi2/managers/InboxManager.kt
+++ b/libs/canvas-api-2/src/main/java/com/instructure/canvasapi2/managers/InboxManager.kt
@@ -66,10 +66,10 @@ object InboxManager {
InboxApi.getConversationsFiltered(scope, canvasContext, adapter, callback, params)
}
- fun getConversation(conversationId: Long, forceNetwork: Boolean, callback: StatusCallback) {
+ fun getConversation(conversationId: Long, forceNetwork: Boolean, callback: StatusCallback, markAsRead: Boolean = true) {
val adapter = RestBuilder(callback)
val params = RestParams(isForceReadFromNetwork = forceNetwork)
- InboxApi.getConversation(adapter, callback, params, conversationId)
+ InboxApi.getConversation(adapter, callback, params, conversationId, markAsRead)
}
fun starConversation(
@@ -142,7 +142,7 @@ object InboxManager {
val adapter = RestBuilder()
val params = RestParams(isForceReadFromNetwork = forceNetwork)
return try {
- val response = InboxApi.getConversation(adapter, params, conversationId)
+ val response = InboxApi.getConversation(adapter, params, conversationId, markAsRead = true)
if (response.isSuccessful) response.body() else null
} catch (e: IOException) {
null
diff --git a/libs/canvas-api-2/src/main/java/com/instructure/canvasapi2/managers/SubmissionManager.kt b/libs/canvas-api-2/src/main/java/com/instructure/canvasapi2/managers/SubmissionManager.kt
index f47d5144e8..6a6f115e5b 100644
--- a/libs/canvas-api-2/src/main/java/com/instructure/canvasapi2/managers/SubmissionManager.kt
+++ b/libs/canvas-api-2/src/main/java/com/instructure/canvasapi2/managers/SubmissionManager.kt
@@ -274,6 +274,16 @@ object SubmissionManager {
postStudentAnnotationSubmission(canvasContext, assignmentId, annotatableAttachmentId, it)
}
+ fun markSubmissionAsRead(
+ courseId: Long,
+ assignmentId: Long,
+ callback: StatusCallback
+ ) {
+ val adapter = RestBuilder(callback)
+ val params = RestParams(isForceReadFromNetwork = true)
+ SubmissionAPI.markSubmissionAsRead(adapter, params, courseId, assignmentId, callback)
+ }
+
private fun postStudentAnnotationSubmission(
canvasContext: CanvasContext,
assignmentId: Long,
diff --git a/libs/canvas-api-2/src/main/java/com/instructure/canvasapi2/managers/UnreadCountManager.kt b/libs/canvas-api-2/src/main/java/com/instructure/canvasapi2/managers/UnreadCountManager.kt
index 47ac3a2371..2f7f31a2ab 100644
--- a/libs/canvas-api-2/src/main/java/com/instructure/canvasapi2/managers/UnreadCountManager.kt
+++ b/libs/canvas-api-2/src/main/java/com/instructure/canvasapi2/managers/UnreadCountManager.kt
@@ -22,6 +22,7 @@ import com.instructure.canvasapi2.builders.RestBuilder
import com.instructure.canvasapi2.builders.RestParams
import com.instructure.canvasapi2.models.UnreadConversationCount
import com.instructure.canvasapi2.models.UnreadCount
+import com.instructure.canvasapi2.models.UnreadNotificationCount
object UnreadCountManager {
@@ -37,4 +38,9 @@ object UnreadCountManager {
UnreadCountAPI.getUnreadAlertCount(adapter, params, studentId, callback)
}
+ fun getUnreadNotificationCount(callback: StatusCallback>, forceNetwork: Boolean) {
+ val adapter = RestBuilder(callback)
+ val params = RestParams(isForceReadFromNetwork = forceNetwork)
+ UnreadCountAPI.getUnreadNotificationsCount(adapter, params, callback)
+ }
}
diff --git a/libs/canvas-api-2/src/test/java/com/instructure/canvasapi2/pact/canvas/apis/InboxApiPactTests.kt b/libs/canvas-api-2/src/test/java/com/instructure/canvasapi2/pact/canvas/apis/InboxApiPactTests.kt
index 0301ef4187..8be259fbe6 100644
--- a/libs/canvas-api-2/src/test/java/com/instructure/canvasapi2/pact/canvas/apis/InboxApiPactTests.kt
+++ b/libs/canvas-api-2/src/test/java/com/instructure/canvasapi2/pact/canvas/apis/InboxApiPactTests.kt
@@ -221,7 +221,7 @@ class InboxApiPactTests : ApiPactTestBase() {
// populated. Make sure that the provider_state is set up accordingly.
//
- val getOneConversationQuery = "include[]=participant_avatars"
+ val getOneConversationQuery = "include[]=participant_avatars&auto_mark_as_read=true"
val getOneConversationPath = "/api/v1/conversations/1"
val getOneConversationFieldConfig = PactConversationFieldConfig(
includeMessages = true,
@@ -255,7 +255,7 @@ class InboxApiPactTests : ApiPactTestBase() {
fun `grab a specific conversation`() {
val service = createService()
- val getConversationCall = service.getConversation(1)
+ val getConversationCall = service.getConversation(1, true)
val getConversationResult = getConversationCall.execute()
assertQueryParamsAndPath(getConversationCall, getOneConversationQuery, getOneConversationPath)
diff --git a/libs/pandares/src/main/res/values/strings.xml b/libs/pandares/src/main/res/values/strings.xml
index 6ab5281a96..4c7caea78d 100644
--- a/libs/pandares/src/main/res/values/strings.xml
+++ b/libs/pandares/src/main/res/values/strings.xml
@@ -1283,6 +1283,11 @@
- %s unread messages
+
+ - %s unread notification
+ - %s unread notifications
+
+
No annotation selected
Email Notifications
diff --git a/libs/pandautils/src/main/java/com/instructure/pandautils/utils/Extensions.kt b/libs/pandautils/src/main/java/com/instructure/pandautils/utils/Extensions.kt
index c5f286cf86..e38481a2a1 100644
--- a/libs/pandautils/src/main/java/com/instructure/pandautils/utils/Extensions.kt
+++ b/libs/pandautils/src/main/java/com/instructure/pandautils/utils/Extensions.kt
@@ -39,4 +39,8 @@ fun Long.humanReadableByteCount(): String {
fun Long?.orDefault(default: Long = 0): Long {
return this ?: default
-}
\ No newline at end of file
+}
+
+fun Int?.orDefault(default: Int = 0): Int {
+ return this ?: default
+}
From 096d624f9dd5824070038a3ba62e881147eb0429 Mon Sep 17 00:00:00 2001
From: Tamas Kozmer <72397075+tamaskozmer@users.noreply.github.com>
Date: Tue, 4 Oct 2022 12:28:42 +0200
Subject: [PATCH 16/73] [MBL-16270][All] Improve mobile login (#1727)
refs: MBL-16270
affects: All
release note: Improved the login flow by saving the last institution's login page.
* Added recent school to login page.
* Save only after login.
* Parent changes.
* Added styles for landscape layout.
* Fixed teacher and added changes according to product feedback.
* Clear login prefs in tests.
---
.../lib/l10n/app_localizations.dart | 6 +
.../lib/network/utils/api_prefs.dart | 25 ++++
.../lib/router/panda_router.dart | 5 +-
.../domain_search/domain_search_screen.dart | 9 +-
.../lib/screens/login_landing_screen.dart | 121 +++++++++++-------
.../screens/web_login/web_login_screen.dart | 14 +-
.../ui/utils/StudentActivityTestRule.kt | 2 +
.../activity/LoginLandingPageActivity.kt | 4 +-
.../ui/utils/TeacherActivityTestRule.kt | 2 +
.../activities/LoginLandingPageActivity.kt | 2 +-
.../activities/BaseLoginFindSchoolActivity.kt | 1 +
.../BaseLoginLandingPageActivity.kt | 62 +++++++--
.../activities/BaseLoginSignInActivity.kt | 3 +
.../loginapi/login/util/LoginPrefs.kt | 15 +++
.../drawable/bg_button_rounded_outline.xml | 3 +-
.../activity_login_landing_page.xml | 27 +++-
.../layout/activity_login_landing_page.xml | 31 ++++-
.../src/main/res/values/strings.xml | 1 +
.../src/main/res/values/styles.xml | 23 ++++
19 files changed, 278 insertions(+), 78 deletions(-)
create mode 100644 libs/login-api-2/src/main/java/com/instructure/loginapi/login/util/LoginPrefs.kt
diff --git a/apps/flutter_parent/lib/l10n/app_localizations.dart b/apps/flutter_parent/lib/l10n/app_localizations.dart
index 2d06702a2d..f8c753fd9d 100644
--- a/apps/flutter_parent/lib/l10n/app_localizations.dart
+++ b/apps/flutter_parent/lib/l10n/app_localizations.dart
@@ -296,6 +296,12 @@ class AppLocalizations {
desc: 'Text for the find-my-school button',
);
+ String get findAnotherSchool => Intl.message(
+ 'Find another school',
+ name: 'findAnotherSchool',
+ desc: 'Text for the find-another-school button',
+ );
+
/// Domain search screen
String get domainSearchInputHint => Intl.message(
diff --git a/apps/flutter_parent/lib/network/utils/api_prefs.dart b/apps/flutter_parent/lib/network/utils/api_prefs.dart
index 1b44ba5dc4..a4fb8d0a99 100644
--- a/apps/flutter_parent/lib/network/utils/api_prefs.dart
+++ b/apps/flutter_parent/lib/network/utils/api_prefs.dart
@@ -19,9 +19,11 @@ import 'dart:ui' as ui;
import 'package:encrypted_shared_preferences/encrypted_shared_preferences.dart';
import 'package:flutter/material.dart';
import 'package:flutter_parent/models/login.dart';
+import 'package:flutter_parent/models/school_domain.dart';
import 'package:flutter_parent/models/serializers.dart';
import 'package:flutter_parent/models/user.dart';
import 'package:flutter_parent/network/api/auth_api.dart';
+import 'package:flutter_parent/screens/web_login/web_login_screen.dart';
import 'package:flutter_parent/utils/db/calendar_filter_db.dart';
import 'package:flutter_parent/utils/db/reminder_db.dart';
import 'package:flutter_parent/utils/notification_util.dart';
@@ -29,6 +31,7 @@ import 'package:flutter_parent/utils/service_locator.dart';
import 'package:intl/intl.dart';
import 'package:package_info/package_info.dart';
import 'package:shared_preferences/shared_preferences.dart';
+import 'package:tuple/tuple.dart';
import 'dio_config.dart';
@@ -42,6 +45,8 @@ class ApiPrefs {
static const String KEY_LOGINS = 'logins';
static const String KEY_RATING_DONT_SHOW_AGAIN = 'dont_show_again';
static const String KEY_RATING_NEXT_SHOW_DATE = 'next_show_date';
+ static const String KEY_LAST_ACCOUNT = 'last_account';
+ static const String KEY_LAST_ACCOUNT_LOGIN_FLOW = 'last_account_login_flow';
static EncryptedSharedPreferences _prefs;
static PackageInfo _packageInfo;
@@ -167,6 +172,26 @@ class ApiPrefs {
return _prefs.getStringList(KEY_LOGINS)?.map((it) => deserialize(json.decode(it)))?.toList() ?? [];
}
+ static setLastAccount(SchoolDomain lastAccount, LoginFlow loginFlow) {
+ _checkInit();
+ final lastAccountJson = json.encode(serialize(lastAccount));
+ _prefs.setString(KEY_LAST_ACCOUNT, lastAccountJson);
+ _prefs.setInt(KEY_LAST_ACCOUNT_LOGIN_FLOW, loginFlow.index);
+ }
+
+ static Tuple2 getLastAccount() {
+ _checkInit();
+ if (!_prefs.containsKey(KEY_LAST_ACCOUNT)) return null;
+
+ final accountJson = _prefs.getString(KEY_LAST_ACCOUNT);
+ if (accountJson == null || accountJson.isEmpty) return null;
+
+ final lastAccount = deserialize(json.decode(accountJson));
+ final loginFlow = _prefs.containsKey(KEY_LAST_ACCOUNT_LOGIN_FLOW) ? LoginFlow.values[_prefs.getInt(KEY_LAST_ACCOUNT_LOGIN_FLOW)] : LoginFlow.normal;
+
+ return Tuple2(lastAccount, loginFlow);
+ }
+
static Future removeLogin(Login login) => removeLoginByUuid(login.uuid);
static Future removeLoginByUuid(String uuid) async {
diff --git a/apps/flutter_parent/lib/router/panda_router.dart b/apps/flutter_parent/lib/router/panda_router.dart
index 882314e745..232e20029d 100644
--- a/apps/flutter_parent/lib/router/panda_router.dart
+++ b/apps/flutter_parent/lib/router/panda_router.dart
@@ -115,10 +115,11 @@ class PandaRouter {
static String loginWeb(
String domain, {
+ String accountName = '',
String authenticationProvider = null,
LoginFlow loginFlow = LoginFlow.normal,
}) =>
- '$_loginWeb?${_RouterKeys.domain}=${Uri.encodeQueryComponent(domain)}&${_RouterKeys.authenticationProvider}=$authenticationProvider&${_RouterKeys.loginFlow}=${loginFlow.toString()}';
+ '$_loginWeb?${_RouterKeys.domain}=${Uri.encodeQueryComponent(domain)}&${_RouterKeys.accountName}=${Uri.encodeQueryComponent(accountName)}&${_RouterKeys.authenticationProvider}=$authenticationProvider&${_RouterKeys.loginFlow}=${loginFlow.toString()}';
static String notParent() => '/not_parent';
@@ -325,6 +326,7 @@ class PandaRouter {
return WebLoginScreen(
params[_RouterKeys.domain][0],
+ accountName: params[_RouterKeys.accountName][0],
authenticationProvider: authProvider,
loginFlow: loginFlow,
);
@@ -507,6 +509,7 @@ class _RouterKeys {
static final calendarView = 'view_name';
static final courseId = 'courseId';
static final domain = 'domain';
+ static final accountName = 'accountName';
static final eventId = 'eventId';
static final infoText = 'infoText';
static final isCreatingAccount = 'isCreatingAccount';
diff --git a/apps/flutter_parent/lib/screens/domain_search/domain_search_screen.dart b/apps/flutter_parent/lib/screens/domain_search/domain_search_screen.dart
index df3622532b..06a752f13a 100644
--- a/apps/flutter_parent/lib/screens/domain_search/domain_search_screen.dart
+++ b/apps/flutter_parent/lib/screens/domain_search/domain_search_screen.dart
@@ -195,8 +195,11 @@ class _DomainSearchScreenState extends State {
var item = _schoolDomains[index];
return ListTile(
title: Text(item.name),
- onTap: () => locator().pushRoute(context,
- PandaRouter.loginWeb(item.domain, authenticationProvider: item.authenticationProvider)),
+ onTap: () {
+ final accountName = (item.name == null || item.name.isEmpty) ? item.domain : item.name;
+ return locator().pushRoute(context,
+ PandaRouter.loginWeb(item.domain, accountName: accountName, authenticationProvider: item.authenticationProvider));
+ },
);
},
),
@@ -280,6 +283,6 @@ class _DomainSearchScreenState extends State {
if (regExp.hasMatch(domain)) domain = regExp.stringMatch(domain);
if (domain.startsWith('www.')) domain = domain.substring(4); // Strip off www. if they typed it
if (!domain.contains('.') || domain.endsWith('.beta')) domain += '.instructure.com';
- locator().pushRoute(context, PandaRouter.loginWeb(domain, loginFlow: widget.loginFlow));
+ locator().pushRoute(context, PandaRouter.loginWeb(domain, accountName: domain, loginFlow: widget.loginFlow));
}
}
diff --git a/apps/flutter_parent/lib/screens/login_landing_screen.dart b/apps/flutter_parent/lib/screens/login_landing_screen.dart
index b9c9d02bd6..47ca747713 100644
--- a/apps/flutter_parent/lib/screens/login_landing_screen.dart
+++ b/apps/flutter_parent/lib/screens/login_landing_screen.dart
@@ -17,6 +17,7 @@ import 'dart:math';
import 'package:flutter/material.dart';
import 'package:flutter_parent/l10n/app_localizations.dart';
import 'package:flutter_parent/models/login.dart';
+import 'package:flutter_parent/models/school_domain.dart';
import 'package:flutter_parent/network/utils/analytics.dart';
import 'package:flutter_parent/network/utils/api_prefs.dart';
import 'package:flutter_parent/router/panda_router.dart';
@@ -38,6 +39,7 @@ import 'package:flutter_parent/utils/service_locator.dart';
import 'package:flutter_parent/utils/snickers.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:flutter_svg/svg.dart';
+import 'package:tuple/tuple.dart';
class LoginLandingScreen extends StatelessWidget {
final GlobalKey _scaffoldKey = GlobalKey();
@@ -100,6 +102,7 @@ class LoginLandingScreen extends StatelessWidget {
}
Widget _body(BuildContext context) {
+ final lastLoginAccount = ApiPrefs.getLastAccount();
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
@@ -109,24 +112,23 @@ class LoginLandingScreen extends StatelessWidget {
semanticsLabel: L10n(context).canvasLogoLabel,
),
SizedBox(height: 64),
- ButtonTheme(
- minWidth: 260,
- child: RaisedButton(
- child: Padding(
- padding: const EdgeInsets.all(16.0),
- child: Text(
- L10n(context).findSchool,
- style: TextStyle(fontSize: 16),
- ),
- ),
- color: Theme.of(context).accentColor,
- textColor: Colors.white,
- shape: RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(4))),
- onPressed: () {
- onFindSchoolPressed(context);
- },
- ),
- ),
+ if (lastLoginAccount == null)
+ _filledButton(context, L10n(context).findSchool, () {
+ onFindSchoolPressed(context);
+ }),
+ if (lastLoginAccount != null)
+ _filledButton(
+ context,
+ lastLoginAccount.item1.name == null || lastLoginAccount.item1.name.isEmpty
+ ? lastLoginAccount.item1.domain
+ : lastLoginAccount.item1.name, () {
+ onSavedSchoolPressed(context, lastLoginAccount);
+ }),
+ SizedBox(height: 16),
+ if (lastLoginAccount != null)
+ _outlineButton(context, L10n(context).findAnotherSchool, () {
+ onFindSchoolPressed(context);
+ }),
SizedBox(height: 8),
if (_hasCameras()) _qrLogin(context),
],
@@ -134,6 +136,51 @@ class LoginLandingScreen extends StatelessWidget {
);
}
+ Widget _filledButton(
+ BuildContext context, String title, VoidCallback onPressed) {
+ return ButtonTheme(
+ minWidth: 260,
+ child: RaisedButton(
+ child: Padding(
+ padding: const EdgeInsets.all(16.0),
+ child: Text(
+ title,
+ style: TextStyle(fontSize: 16),
+ ),
+ ),
+ color: Theme.of(context).accentColor,
+ textColor: Colors.white,
+ shape: RoundedRectangleBorder(
+ borderRadius: BorderRadius.all(Radius.circular(4))),
+ onPressed: onPressed,
+ ),
+ );
+ }
+
+ Widget _outlineButton(
+ BuildContext context, String title, VoidCallback onPressed) {
+ return ButtonTheme(
+ minWidth: 260,
+ child: OutlinedButton(
+ child: Padding(
+ padding: const EdgeInsets.all(16.0),
+ child: Text(
+ title,
+ style: Theme.of(context).textTheme.subtitle1,
+ ),
+ ),
+ style: OutlinedButton.styleFrom(
+ shape: RoundedRectangleBorder(
+ borderRadius: BorderRadius.circular(4.0),
+ ),
+ side: BorderSide(
+ width: 1, color: ParentTheme.of(context).onSurfaceColor),
+ ),
+ onPressed: onPressed,
+ ),
+ );
+ }
+
bool _hasCameras() {
return ApiPrefs.getCameraCount() != null && ApiPrefs.getCameraCount() != 0;
}
@@ -233,36 +280,18 @@ class LoginLandingScreen extends StatelessWidget {
);
}
- Widget _helpRequestButton(BuildContext context) {
- return Semantics(
- label: L10n(context).loginHelpHint,
- child: GestureDetector(
- onTap: () {
- locator().logEvent(AnalyticsEventConstants.HELP_LOGIN);
- ErrorReportDialog.asDialog(context,
- title: L10n(context).loginHelpTitle,
- subject: L10n(context).loginHelpSubject,
- severity: ErrorReportSeverity.BLOCKING,
- includeEmail: true,
- hideSeverityPicker: true);
- },
- child: Padding(
- padding: const EdgeInsets.all(16.0),
- child: Align(
- child: Icon(
- CanvasIcons.question,
- color: ParentColors.ash,
- ),
- alignment: Alignment.topRight,
- ),
- ),
- ),
- );
- }
-
onFindSchoolPressed(BuildContext context) {
LoginFlow flow = LoginFlow.values[loginFlowIndex % LoginFlow.values.length];
- locator().pushRoute(context, PandaRouter.domainSearch(loginFlow: flow));
+ locator()
+ .pushRoute(context, PandaRouter.domainSearch(loginFlow: flow));
+ }
+
+ onSavedSchoolPressed(BuildContext context, Tuple2 lastAccount) {
+ locator().pushRoute(
+ context,
+ PandaRouter.loginWeb(lastAccount.item1.domain,
+ accountName: lastAccount.item1.name,
+ loginFlow: lastAccount.item2));
}
void _changeLoginFlow(BuildContext context) {
diff --git a/apps/flutter_parent/lib/screens/web_login/web_login_screen.dart b/apps/flutter_parent/lib/screens/web_login/web_login_screen.dart
index 5743495bc0..c63a4cd388 100644
--- a/apps/flutter_parent/lib/screens/web_login/web_login_screen.dart
+++ b/apps/flutter_parent/lib/screens/web_login/web_login_screen.dart
@@ -18,6 +18,7 @@ import 'package:device_info/device_info.dart';
import 'package:flutter/material.dart';
import 'package:flutter_parent/l10n/app_localizations.dart';
import 'package:flutter_parent/models/mobile_verify_result.dart';
+import 'package:flutter_parent/models/school_domain.dart';
import 'package:flutter_parent/network/utils/analytics.dart';
import 'package:flutter_parent/network/utils/api_prefs.dart';
import 'package:flutter_parent/router/panda_router.dart';
@@ -37,8 +38,8 @@ enum LoginFlow {
}
class WebLoginScreen extends StatefulWidget {
- WebLoginScreen(
- this.domain, {
+ WebLoginScreen(this.domain, {
+ this.accountName,
this.user,
this.pass,
this.authenticationProvider,
@@ -47,6 +48,7 @@ class WebLoginScreen extends StatefulWidget {
}) : super(key: key);
final String user;
+ final String accountName;
final String pass;
final String domain;
final String authenticationProvider;
@@ -200,7 +202,13 @@ class _WebLoginScreenState extends State {
AnalyticsEventConstants.LOGIN_SUCCESS,
extras: {AnalyticsParamConstants.DOMAIN_PARAM: result.baseUrl},
);
- locator().pushRouteAndClearStack(context, PandaRouter.rootSplash());
+ final lastAccount = new SchoolDomain((builder) =>
+ builder
+ ..domain = widget.domain
+ ..name = widget.accountName);
+ ApiPrefs.setLastAccount(lastAccount, widget.loginFlow);
+ locator().pushRouteAndClearStack(
+ context, PandaRouter.rootSplash());
}).catchError((_) {
locator().logEvent(
AnalyticsEventConstants.LOGIN_FAILURE,
diff --git a/apps/student/src/androidTest/java/com/instructure/student/ui/utils/StudentActivityTestRule.kt b/apps/student/src/androidTest/java/com/instructure/student/ui/utils/StudentActivityTestRule.kt
index bace30a97b..f14c13ddba 100644
--- a/apps/student/src/androidTest/java/com/instructure/student/ui/utils/StudentActivityTestRule.kt
+++ b/apps/student/src/androidTest/java/com/instructure/student/ui/utils/StudentActivityTestRule.kt
@@ -21,6 +21,7 @@ import android.content.Context
import com.instructure.student.util.CacheControlFlags
import com.instructure.student.util.StudentPrefs
import com.instructure.espresso.InstructureActivityTestRule
+import com.instructure.loginapi.login.util.LoginPrefs
import com.instructure.loginapi.login.util.PreviousUsersUtils
import com.instructure.pandautils.utils.PandaAppResetter
import com.instructure.pandautils.utils.ThemePrefs
@@ -32,6 +33,7 @@ class StudentActivityTestRule(activityClass: Class) : Instructu
StudentPrefs.clearPrefs()
CacheControlFlags.clearPrefs()
PreviousUsersUtils.clear(context)
+ LoginPrefs.clearPrefs()
// We need to set this true so the theme selector won't stop our tests.
ThemePrefs.themeSelectionShown = true
diff --git a/apps/student/src/main/java/com/instructure/student/activity/LoginLandingPageActivity.kt b/apps/student/src/main/java/com/instructure/student/activity/LoginLandingPageActivity.kt
index 72700c9f86..2f4ad68d00 100644
--- a/apps/student/src/main/java/com/instructure/student/activity/LoginLandingPageActivity.kt
+++ b/apps/student/src/main/java/com/instructure/student/activity/LoginLandingPageActivity.kt
@@ -63,8 +63,8 @@ class LoginLandingPageActivity : BaseLoginLandingPageActivity() {
return ContextCompat.getColor(this, R.color.login_studentAppTheme)
}
- override fun signInActivityIntent(snickerDoodle: SnickerDoodle): Intent {
- return SignInActivity.createIntent(this, AccountDomain(snickerDoodle.domain))
+ override fun signInActivityIntent(accountDomain: AccountDomain): Intent {
+ return SignInActivity.createIntent(this, accountDomain)
}
override fun loginWithQRCodeEnabled(): Boolean = true
diff --git a/apps/teacher/src/androidTest/java/com/instructure/teacher/ui/utils/TeacherActivityTestRule.kt b/apps/teacher/src/androidTest/java/com/instructure/teacher/ui/utils/TeacherActivityTestRule.kt
index 1b41aeca71..aa47deb4fc 100644
--- a/apps/teacher/src/androidTest/java/com/instructure/teacher/ui/utils/TeacherActivityTestRule.kt
+++ b/apps/teacher/src/androidTest/java/com/instructure/teacher/ui/utils/TeacherActivityTestRule.kt
@@ -19,6 +19,7 @@ package com.instructure.teacher.ui.utils
import android.app.Activity
import android.content.Context
import com.instructure.espresso.InstructureActivityTestRule
+import com.instructure.loginapi.login.util.LoginPrefs
import com.instructure.loginapi.login.util.PreviousUsersUtils
import com.instructure.pandautils.utils.PandaAppResetter
import com.instructure.pandautils.utils.ThemePrefs
@@ -30,6 +31,7 @@ class TeacherActivityTestRule(activityClass: Class) : Instructur
PandaAppResetter.reset(context)
TeacherPrefs.safeClearPrefs()
PreviousUsersUtils.clear(context)
+ LoginPrefs.clearPrefs()
// We need to set this true so the theme selector won't stop our tests.
ThemePrefs.themeSelectionShown = true
diff --git a/apps/teacher/src/main/java/com/instructure/teacher/activities/LoginLandingPageActivity.kt b/apps/teacher/src/main/java/com/instructure/teacher/activities/LoginLandingPageActivity.kt
index 2ce658f6c6..79f67d75ee 100644
--- a/apps/teacher/src/main/java/com/instructure/teacher/activities/LoginLandingPageActivity.kt
+++ b/apps/teacher/src/main/java/com/instructure/teacher/activities/LoginLandingPageActivity.kt
@@ -51,7 +51,7 @@ class LoginLandingPageActivity : BaseLoginLandingPageActivity() {
override fun themeColor(): Int = ContextCompat.getColor(this, R.color.login_teacherAppTheme)
- override fun signInActivityIntent(snickerDoodle: SnickerDoodle): Intent = SignInActivity.createIntent(this, AccountDomain(snickerDoodle.domain))
+ override fun signInActivityIntent(accountDomain: AccountDomain): Intent = SignInActivity.createIntent(this, accountDomain)
override fun startApp() {
val intent = launchApplicationMainActivityIntent()
diff --git a/libs/login-api-2/src/main/java/com/instructure/loginapi/login/activities/BaseLoginFindSchoolActivity.kt b/libs/login-api-2/src/main/java/com/instructure/loginapi/login/activities/BaseLoginFindSchoolActivity.kt
index 1de4fa365f..595f50a5bb 100644
--- a/libs/login-api-2/src/main/java/com/instructure/loginapi/login/activities/BaseLoginFindSchoolActivity.kt
+++ b/libs/login-api-2/src/main/java/com/instructure/loginapi/login/activities/BaseLoginFindSchoolActivity.kt
@@ -150,6 +150,7 @@ abstract class BaseLoginFindSchoolActivity : AppCompatActivity(), ErrorReportDia
mNextActionButton!!.isEnabled = false
mNextActionButton!!.setTextColor(ContextCompat.getColor(this@BaseLoginFindSchoolActivity, R.color.backgroundMedium))
+ domainInput.requestFocus()
domainInput.setOnEditorActionListener { _, _, _ ->
validateDomain(AccountDomain(domainInput!!.text.toString()))
true
diff --git a/libs/login-api-2/src/main/java/com/instructure/loginapi/login/activities/BaseLoginLandingPageActivity.kt b/libs/login-api-2/src/main/java/com/instructure/loginapi/login/activities/BaseLoginLandingPageActivity.kt
index 7f444b601e..c766311cd7 100644
--- a/libs/login-api-2/src/main/java/com/instructure/loginapi/login/activities/BaseLoginLandingPageActivity.kt
+++ b/libs/login-api-2/src/main/java/com/instructure/loginapi/login/activities/BaseLoginLandingPageActivity.kt
@@ -39,6 +39,7 @@ import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.google.gson.Gson
import com.instructure.canvasapi2.apis.ErrorReportAPI
+import com.instructure.canvasapi2.models.AccountDomain
import com.instructure.canvasapi2.models.ErrorReportPreFill
import com.instructure.canvasapi2.utils.APIHelper
import com.instructure.canvasapi2.utils.Analytics
@@ -57,7 +58,9 @@ import com.instructure.loginapi.login.util.Const.MOBILE_VERIFY_FLOW
import com.instructure.loginapi.login.util.Const.NORMAL_FLOW
import com.instructure.loginapi.login.util.Const.SNICKER_DOODLES
import com.instructure.loginapi.login.util.Const.URL_CANVAS_NETWORK
+import com.instructure.loginapi.login.util.LoginPrefs
import com.instructure.loginapi.login.util.PreviousUsersUtils
+import com.instructure.loginapi.login.util.SavedLoginInfo
import com.instructure.loginapi.login.viewmodel.LoginViewModel
import com.instructure.pandautils.mvvm.Event
import com.instructure.pandautils.utils.*
@@ -74,7 +77,7 @@ abstract class BaseLoginLandingPageActivity : AppCompatActivity(), ErrorReportDi
protected abstract fun beginFindSchoolFlow(): Intent
- protected abstract fun signInActivityIntent(snickerDoodle: SnickerDoodle): Intent
+ protected abstract fun signInActivityIntent(accountDomain: AccountDomain): Intent
protected abstract fun beginCanvasNetworkFlow(url: String): Intent
@@ -102,22 +105,13 @@ abstract class BaseLoginLandingPageActivity : AppCompatActivity(), ErrorReportDi
loadPreviousUsers()
setupGesture()
setupSnickerDoodles()
+ setupButtons()
}
private fun bindViews() {
// Only show the what's new text if the app supports it
changesLayout.visibility = if (appChangesLink() != null) View.VISIBLE else View.GONE
- findMySchool.onClick {
- if (APIHelper.hasNetworkConnection()) {
- val intent = beginFindSchoolFlow()
- intent.putExtra(Const.CANVAS_LOGIN, canvasLogin)
- startActivity(intent)
- } else {
- NoInternetConnectionDialog.show(supportFragmentManager)
- }
- }
-
canvasNetwork.onClick {
if (APIHelper.hasNetworkConnection()) {
val intent = beginCanvasNetworkFlow(URL_CANVAS_NETWORK)
@@ -358,7 +352,7 @@ abstract class BaseLoginLandingPageActivity : AppCompatActivity(), ErrorReportDi
drawerRecyclerView.layoutManager = LinearLayoutManager(this, RecyclerView.VERTICAL, true)
drawerRecyclerView.adapter = SnickerDoodleAdapter(snickerDoodles) { snickerDoodle ->
drawerLayout.closeDrawers()
- val intent = signInActivityIntent(snickerDoodle)
+ val intent = signInActivityIntent(AccountDomain(snickerDoodle.domain))
intent.putExtra(SNICKER_DOODLES, snickerDoodle)
startActivity(intent)
finish()
@@ -369,6 +363,50 @@ abstract class BaseLoginLandingPageActivity : AppCompatActivity(), ErrorReportDi
}
}
+ private fun setupButtons() {
+ val lastSavedLogin = LoginPrefs.lastSavedLogin
+ if (lastSavedLogin != null) {
+ openRecentSchool?.visibility = View.VISIBLE
+ findAnotherSchool?.visibility = View.VISIBLE
+ findMySchool?.visibility = View.GONE
+
+ openRecentSchool?.text = if (lastSavedLogin.accountDomain.name.isNullOrEmpty()) {
+ lastSavedLogin.accountDomain.domain
+ } else {
+ lastSavedLogin.accountDomain.name
+ }
+ openRecentSchool?.onClick { openRecentSchool(lastSavedLogin) }
+
+ findAnotherSchool?.onClick { findSchool() }
+ } else {
+ openRecentSchool?.visibility = View.GONE
+ findAnotherSchool?.visibility = View.GONE
+ findMySchool?.visibility = View.VISIBLE
+
+ findMySchool?.onClick { findSchool() }
+ }
+ }
+
+ private fun openRecentSchool(lastSavedLogin: SavedLoginInfo) {
+ if (APIHelper.hasNetworkConnection()) {
+ val intent = signInActivityIntent(lastSavedLogin.accountDomain)
+ intent.putExtra(Const.CANVAS_LOGIN, lastSavedLogin.canvasLogin)
+ startActivity(intent)
+ } else {
+ NoInternetConnectionDialog.show(supportFragmentManager)
+ }
+ }
+
+ private fun findSchool() {
+ if (APIHelper.hasNetworkConnection()) {
+ val intent = beginFindSchoolFlow()
+ intent.putExtra(Const.CANVAS_LOGIN, canvasLogin)
+ startActivity(intent)
+ } else {
+ NoInternetConnectionDialog.show(supportFragmentManager)
+ }
+ }
+
override fun onTicketPost() {
toast(R.string.errorReportThankyou)
}
diff --git a/libs/login-api-2/src/main/java/com/instructure/loginapi/login/activities/BaseLoginSignInActivity.kt b/libs/login-api-2/src/main/java/com/instructure/loginapi/login/activities/BaseLoginSignInActivity.kt
index 848211bd80..8839e4e587 100644
--- a/libs/login-api-2/src/main/java/com/instructure/loginapi/login/activities/BaseLoginSignInActivity.kt
+++ b/libs/login-api-2/src/main/java/com/instructure/loginapi/login/activities/BaseLoginSignInActivity.kt
@@ -66,7 +66,9 @@ import com.instructure.loginapi.login.util.Const.CANVAS_LOGIN_FLOW
import com.instructure.loginapi.login.util.Const.MASQUERADE_FLOW
import com.instructure.loginapi.login.util.Const.MOBILE_VERIFY_FLOW
import com.instructure.loginapi.login.util.Const.SNICKER_DOODLES
+import com.instructure.loginapi.login.util.LoginPrefs
import com.instructure.loginapi.login.util.PreviousUsersUtils.add
+import com.instructure.loginapi.login.util.SavedLoginInfo
import com.instructure.loginapi.login.viewmodel.LoginViewModel
import com.instructure.pandautils.mvvm.Event
import com.instructure.pandautils.utils.*
@@ -452,6 +454,7 @@ abstract class BaseLoginSignInActivity : AppCompatActivity(), OnAuthenticationSe
)
add(this@BaseLoginSignInActivity, user)
refreshWidgets()
+ LoginPrefs.lastSavedLogin = SavedLoginInfo(accountDomain, canvasLogin)
handleLaunchApplicationMainActivityIntent()
}
}
diff --git a/libs/login-api-2/src/main/java/com/instructure/loginapi/login/util/LoginPrefs.kt b/libs/login-api-2/src/main/java/com/instructure/loginapi/login/util/LoginPrefs.kt
new file mode 100644
index 0000000000..2a11152c7f
--- /dev/null
+++ b/libs/login-api-2/src/main/java/com/instructure/loginapi/login/util/LoginPrefs.kt
@@ -0,0 +1,15 @@
+package com.instructure.loginapi.login.util
+
+import com.instructure.canvasapi2.models.AccountDomain
+import com.instructure.canvasapi2.utils.BooleanPref
+import com.instructure.canvasapi2.utils.GsonPref
+import com.instructure.canvasapi2.utils.IntPref
+import com.instructure.canvasapi2.utils.LongPref
+import com.instructure.canvasapi2.utils.PrefManager
+import com.instructure.pandautils.dialogs.RatingDialog
+
+data class SavedLoginInfo(val accountDomain: AccountDomain, val canvasLogin: Int)
+
+object LoginPrefs : PrefManager("loginPrefs") {
+ var lastSavedLogin by GsonPref(SavedLoginInfo::class.java, null)
+}
\ No newline at end of file
diff --git a/libs/login-api-2/src/main/res/drawable/bg_button_rounded_outline.xml b/libs/login-api-2/src/main/res/drawable/bg_button_rounded_outline.xml
index 0071462ec5..bafbe7dbd1 100644
--- a/libs/login-api-2/src/main/res/drawable/bg_button_rounded_outline.xml
+++ b/libs/login-api-2/src/main/res/drawable/bg_button_rounded_outline.xml
@@ -19,8 +19,7 @@
-
-
-
+
diff --git a/libs/login-api-2/src/main/res/layout-land/activity_login_landing_page.xml b/libs/login-api-2/src/main/res/layout-land/activity_login_landing_page.xml
index d1cc7aba67..e71c00f174 100644
--- a/libs/login-api-2/src/main/res/layout-land/activity_login_landing_page.xml
+++ b/libs/login-api-2/src/main/res/layout-land/activity_login_landing_page.xml
@@ -50,13 +50,34 @@
android:layout_width="64dp"
android:layout_height="64dp"
android:importantForAccessibility="no"
+ android:layout_marginBottom="20dp"
app:srcCompat="@drawable/ic_canvas_logo"/>
+
+
+
+
+ android:textSize="14sp"
+ android:layout_marginBottom="36dp"/>
+
+
+
+
+ style="@style/LoginButtonStyle.Filled"
+ android:layout_marginHorizontal="48dp"
+ android:layout_marginBottom="36dp"
+ android:text="@string/findMySchool" />
You will stop acting as %s and return to your account.
We\'ve made a few changes.
See what\'s new
+ Find another school
diff --git a/libs/login-api-2/src/main/res/values/styles.xml b/libs/login-api-2/src/main/res/values/styles.xml
index e1fa4bba1c..47d0d83972 100644
--- a/libs/login-api-2/src/main/res/values/styles.xml
+++ b/libs/login-api-2/src/main/res/values/styles.xml
@@ -97,4 +97,27 @@
- @font/lato_font_family
+
+
+
+
+
+
+
From dcdafb22167d1a097889d9d251db78023c2d774d Mon Sep 17 00:00:00 2001
From: Kristof Nemere <109959688+kristofnemere@users.noreply.github.com>
Date: Wed, 5 Oct 2022 13:39:08 +0200
Subject: [PATCH 17/73] [MBL-16162][Student][Teacher] RCE insert image
improvements
refs: MBL-16162
affects: Student, Teacher
release note: none
* Fixed rce insert image issue
* Fixed permission issue on edit profile fragment
* Fixed comments
---
.../teacher/activities/BaseAppCompatActivity.kt | 6 +++++-
.../pandautils/utils/MediaUploadUtils.kt | 13 ++++++-------
2 files changed, 11 insertions(+), 8 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 839ae3375f..55f3eeb5f0 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
@@ -19,14 +19,18 @@ 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.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
-abstract class BaseAppCompatActivity : AppCompatActivity() {
+@Suppress("DELEGATED_MEMBER_HIDES_SUPERTYPE_OVERRIDE")
+abstract class BaseAppCompatActivity : AppCompatActivity(),
+ ActivityCompat.OnRequestPermissionsResultCallback by PermissionReceiver() {
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
diff --git a/libs/pandautils/src/main/java/com/instructure/pandautils/utils/MediaUploadUtils.kt b/libs/pandautils/src/main/java/com/instructure/pandautils/utils/MediaUploadUtils.kt
index 04734a9fb3..0e43acbe47 100644
--- a/libs/pandautils/src/main/java/com/instructure/pandautils/utils/MediaUploadUtils.kt
+++ b/libs/pandautils/src/main/java/com/instructure/pandautils/utils/MediaUploadUtils.kt
@@ -111,14 +111,13 @@ object MediaUploadUtils {
takeNewPhotoBecausePermissionsAlreadyGranted(fragment, activity)
} else {
val permissions = PermissionUtils.makeArray(PermissionUtils.WRITE_EXTERNAL_STORAGE, PermissionUtils.CAMERA)
- fragment?.requestPermissions(permissions, REQUEST_CODE_PERMISSIONS_TAKE_PHOTO)
- ?: activity.requestPermissions(permissions.toSet()) { results ->
- if (results.isNotEmpty() && results.all { it.value }) {
- takeNewPhotoBecausePermissionsAlreadyGranted(fragment, activity)
- } else {
- Toast.makeText(activity, R.string.permissionDenied, Toast.LENGTH_LONG).show()
- }
+ (fragment?.activity ?: activity).requestPermissions(permissions.toSet()) { results ->
+ if (results.isNotEmpty() && results.all { it.value }) {
+ takeNewPhotoBecausePermissionsAlreadyGranted(fragment, activity)
+ } else {
+ Toast.makeText(activity, R.string.permissionDenied, Toast.LENGTH_LONG).show()
}
+ }
}
}
From 31aef9cc3ef99dd5b404b1e75e5d24be331e72fd Mon Sep 17 00:00:00 2001
From: Kristof Deak <92309696+kdeakinstructure@users.noreply.github.com>
Date: Thu, 6 Oct 2022 11:19:08 +0200
Subject: [PATCH 18/73] [MBL-16306][Student] - Add new no assignment selected
test case to share extension interaction tests #1738
refs: MBL-16306
affects: Student
release note: none
---
android-vault | 2 +-
.../ShareExtensionInteractionTest.kt | 22 +++++++++++++++
.../ui/pages/ShareExtensionTargetPage.kt | 27 ++++++-------------
3 files changed, 31 insertions(+), 20 deletions(-)
diff --git a/android-vault b/android-vault
index ceed6c1136..eea182a4b6 160000
--- a/android-vault
+++ b/android-vault
@@ -1 +1 @@
-Subproject commit ceed6c1136054f1aa424382d37185857acf19d3d
+Subproject commit eea182a4b626678f271e42d73c100dd53a84611b
diff --git a/apps/student/src/androidTest/java/com/instructure/student/ui/interaction/ShareExtensionInteractionTest.kt b/apps/student/src/androidTest/java/com/instructure/student/ui/interaction/ShareExtensionInteractionTest.kt
index 9386aa4ca4..2e9d14f952 100644
--- a/apps/student/src/androidTest/java/com/instructure/student/ui/interaction/ShareExtensionInteractionTest.kt
+++ b/apps/student/src/androidTest/java/com/instructure/student/ui/interaction/ShareExtensionInteractionTest.kt
@@ -151,6 +151,28 @@ class ShareExtensionInteractionTest : StudentTest() {
fileUploadPage.assertFileDisplayed("sample.jpg")
}
+ @Test
+ fun shareExtensionNoAssignmentTest() {
+ val data = createMockData()
+ val student = data.students[0]
+ val uri = setupFileOnDevice("sample.jpg")
+ val device = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation())
+
+ login(student)
+ device.pressHome()
+
+ shareExternalFile(uri)
+
+ device.findObject(UiSelector().text("Canvas")).click()
+ device.waitForIdle()
+
+ shareExtensionTargetPage.selectSubmission()
+ shareExtensionTargetPage.assertNoAssignmentSelectedStringDisplayed()
+ shareExtensionTargetPage.pressNext()
+ shareExtensionTargetPage.assertPageObjects() //Make sure that we are still on the Target Page.
+ }
+
+
// Clicking spinner item not working.
@Test
@Stub
diff --git a/apps/student/src/androidTest/java/com/instructure/student/ui/pages/ShareExtensionTargetPage.kt b/apps/student/src/androidTest/java/com/instructure/student/ui/pages/ShareExtensionTargetPage.kt
index 042a4e2262..bd94dbda59 100644
--- a/apps/student/src/androidTest/java/com/instructure/student/ui/pages/ShareExtensionTargetPage.kt
+++ b/apps/student/src/androidTest/java/com/instructure/student/ui/pages/ShareExtensionTargetPage.kt
@@ -17,27 +17,11 @@
package com.instructure.student.ui.pages
import androidx.test.espresso.Espresso.onData
-import androidx.test.espresso.ViewAssertion
import androidx.test.espresso.assertion.ViewAssertions
-import androidx.test.espresso.matcher.RootMatchers.*
+import androidx.test.espresso.matcher.RootMatchers.isDialog
import androidx.test.espresso.matcher.ViewMatchers
-import com.instructure.espresso.OnViewWithId
-import com.instructure.espresso.WaitForViewWithId
-import com.instructure.espresso.WaitForViewWithStringTextIgnoreCase
-import com.instructure.espresso.WaitForViewWithText
-import com.instructure.espresso.assertDisplayed
-import com.instructure.espresso.assertHasText
-import com.instructure.espresso.assertSelected
-import com.instructure.espresso.click
-import com.instructure.espresso.page.BasePage
-import com.instructure.espresso.page.onView
-import com.instructure.espresso.page.onViewWithId
-import com.instructure.espresso.page.onViewWithSpinnerText
-import com.instructure.espresso.page.onViewWithText
-import com.instructure.espresso.page.plus
-import com.instructure.espresso.page.withAncestor
-import com.instructure.espresso.page.withId
-import com.instructure.espresso.page.withText
+import com.instructure.espresso.*
+import com.instructure.espresso.page.*
import com.instructure.student.R
import org.hamcrest.Matchers.anything
@@ -70,6 +54,11 @@ class ShareExtensionTargetPage : BasePage() {
onView(withText(assignmentName) + withAncestor(R.id.assignmentSpinner)).assertDisplayed()
}
+ fun assertNoAssignmentSelectedStringDisplayed() {
+ onViewWithId(R.id.assignmentSpinner).assertDisplayed()
+ onView(withText(R.string.noAssignmentsWithFileUpload) + withAncestor(R.id.assignmentSpinner)).assertDisplayed()
+ }
+
fun selectAssignment(assignmentName: String) {
onViewWithId(R.id.assignmentSpinner).click()
onData(anything()).inRoot(isDialog()).atPosition(1)
From 2ca53baab636ae2aeefdff31a15c843aaf90085f Mon Sep 17 00:00:00 2001
From: inst-danger
Date: Thu, 6 Oct 2022 14:03:57 +0200
Subject: [PATCH 19/73] Extract translations (#1739)
---
apps/flutter_parent/lib/l10n/res/intl_en.arb | 9 ++++++++-
apps/flutter_parent/lib/l10n/res/intl_messages.arb | 9 ++++++++-
libs/flutter_student_embed/lib/l10n/res/intl_en.arb | 2 +-
.../flutter_student_embed/lib/l10n/res/intl_messages.arb | 2 +-
4 files changed, 18 insertions(+), 4 deletions(-)
diff --git a/apps/flutter_parent/lib/l10n/res/intl_en.arb b/apps/flutter_parent/lib/l10n/res/intl_en.arb
index a7ce42583b..e2b0149f61 100644
--- a/apps/flutter_parent/lib/l10n/res/intl_en.arb
+++ b/apps/flutter_parent/lib/l10n/res/intl_en.arb
@@ -1,5 +1,5 @@
{
- "@@last_modified": "2022-01-28T12:37:40.360857",
+ "@@last_modified": "2022-10-06T10:11:41.858017",
"alertsLabel": "Alerts",
"@alertsLabel": {
"description": "The label for the Alerts tab",
@@ -259,6 +259,13 @@
"placeholders_order": [],
"placeholders": {}
},
+ "findAnotherSchool": "Find another school",
+ "@findAnotherSchool": {
+ "description": "Text for the find-another-school button",
+ "type": "text",
+ "placeholders_order": [],
+ "placeholders": {}
+ },
"domainSearchInputHint": "Enter school name or district…",
"@domainSearchInputHint": {
"description": "Input hint for the text box on the domain search screen",
diff --git a/apps/flutter_parent/lib/l10n/res/intl_messages.arb b/apps/flutter_parent/lib/l10n/res/intl_messages.arb
index a7ce42583b..e2b0149f61 100644
--- a/apps/flutter_parent/lib/l10n/res/intl_messages.arb
+++ b/apps/flutter_parent/lib/l10n/res/intl_messages.arb
@@ -1,5 +1,5 @@
{
- "@@last_modified": "2022-01-28T12:37:40.360857",
+ "@@last_modified": "2022-10-06T10:11:41.858017",
"alertsLabel": "Alerts",
"@alertsLabel": {
"description": "The label for the Alerts tab",
@@ -259,6 +259,13 @@
"placeholders_order": [],
"placeholders": {}
},
+ "findAnotherSchool": "Find another school",
+ "@findAnotherSchool": {
+ "description": "Text for the find-another-school button",
+ "type": "text",
+ "placeholders_order": [],
+ "placeholders": {}
+ },
"domainSearchInputHint": "Enter school name or district…",
"@domainSearchInputHint": {
"description": "Input hint for the text box on the domain search screen",
diff --git a/libs/flutter_student_embed/lib/l10n/res/intl_en.arb b/libs/flutter_student_embed/lib/l10n/res/intl_en.arb
index a0ec0ea1d5..c0d0de8f91 100644
--- a/libs/flutter_student_embed/lib/l10n/res/intl_en.arb
+++ b/libs/flutter_student_embed/lib/l10n/res/intl_en.arb
@@ -1,5 +1,5 @@
{
- "@@last_modified": "2022-01-28T12:37:53.041723",
+ "@@last_modified": "2022-10-06T10:11:51.373212",
"coursesLabel": "Courses",
"@coursesLabel": {
"description": "The label for the Courses tab",
diff --git a/libs/flutter_student_embed/lib/l10n/res/intl_messages.arb b/libs/flutter_student_embed/lib/l10n/res/intl_messages.arb
index a0ec0ea1d5..c0d0de8f91 100644
--- a/libs/flutter_student_embed/lib/l10n/res/intl_messages.arb
+++ b/libs/flutter_student_embed/lib/l10n/res/intl_messages.arb
@@ -1,5 +1,5 @@
{
- "@@last_modified": "2022-01-28T12:37:53.041723",
+ "@@last_modified": "2022-10-06T10:11:51.373212",
"coursesLabel": "Courses",
"@coursesLabel": {
"description": "The label for the Courses tab",
From e5c8fd441c7799b712cf0eea7cf2f26d099a9a32 Mon Sep 17 00:00:00 2001
From: kdeakinstructure
Date: Thu, 6 Oct 2022 14:12:18 +0200
Subject: [PATCH 20/73] Updated version number and version code.
---
apps/flutter_parent/pubspec.yaml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/apps/flutter_parent/pubspec.yaml b/apps/flutter_parent/pubspec.yaml
index 3e630126ce..566c85fc5f 100644
--- a/apps/flutter_parent/pubspec.yaml
+++ b/apps/flutter_parent/pubspec.yaml
@@ -25,7 +25,7 @@ description: Canvas Parent
# In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion.
# Read more about iOS versioning at
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
-version: 3.4.0+42
+version: 3.5.0+43
module:
androidX: true
From fed6e263bcb75d4b23d518914492ac4675d295d0 Mon Sep 17 00:00:00 2001
From: Kristof Deak <92309696+kdeakinstructure@users.noreply.github.com>
Date: Mon, 10 Oct 2022 12:28:24 +0200
Subject: [PATCH 21/73] [MBL-16305][Student][Teacher] - Refactor Login tests in
Student and Teacher app to handle last saved school #1741
Refactor Login tests in Student and Teacher app to handle last saved school
Also, added a new case to Student and Teacher app as well in which we directly test the newly added, last saved institute feature.
refs: MBL-16305
affects: Student
release note: none
---
.../student/ui/e2e/LoginE2ETest.kt | 95 ++++++++++----
.../student/ui/pages/LoginLandingPage.kt | 10 ++
.../student/ui/pages/LoginSignInPage.kt | 14 ++-
.../teacher/ui/e2e/LoginE2ETest.kt | 119 +++++++++---------
.../teacher/ui/pages/LoginLandingPage.kt | 12 +-
5 files changed, 159 insertions(+), 91 deletions(-)
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 899e9d8dc6..ef6654c2c3 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
@@ -57,67 +57,94 @@ class LoginE2ETest : StudentTest() {
val student1 = data.studentsList[0]
val student2 = data.studentsList[1]
- Log.d(STEP_TAG,"Login with user: ${student1.name}, login id: ${student1.loginId}.")
+ Log.d(STEP_TAG, "Login with user: ${student1.name}, login id: ${student1.loginId}.")
loginWithUser(student1)
- Log.d(STEP_TAG,"Assert that the Dashboard Page is the landing page and it is loaded successfully.")
+ Log.d(STEP_TAG, "Assert that the Dashboard Page is the landing page and it is loaded successfully.")
assertDashboardPageDisplayed(student1)
- Log.d(STEP_TAG,"Log out with ${student1.name} student.")
+ Log.d(STEP_TAG, "Log out with ${student1.name} student.")
dashboardPage.logOut()
- Log.d(STEP_TAG,"Login with user: ${student2.name}, login id: ${student2.loginId}.")
- loginWithUser(student2)
+ Log.d(STEP_TAG, "Login with user: ${student2.name}, login id: ${student2.loginId}.")
+ loginWithUser(student2, true)
- Log.d(STEP_TAG,"Assert that the Dashboard Page is the landing page and it is loaded successfully.")
+ Log.d(STEP_TAG, "Assert that the Dashboard Page is the landing page and it is loaded successfully.")
assertDashboardPageDisplayed(student2)
- Log.d(STEP_TAG,"Click on 'Change User' button on the left-side menu.")
+ Log.d(STEP_TAG, "Click on 'Change User' button on the left-side menu.")
dashboardPage.pressChangeUser()
- Log.d(STEP_TAG,"Assert that the previously logins has been displayed.")
+ Log.d(STEP_TAG, "Assert that the previously logins has been displayed.")
loginLandingPage.assertDisplaysPreviousLogins()
- Log.d(STEP_TAG,"Login with user: ${student1.name}, login id: ${student1.loginId}.")
- loginWithUser(student1)
+ Log.d(STEP_TAG, "Login with user: ${student1.name}, login id: ${student1.loginId}.")
+ loginWithUser(student1, true)
- Log.d(STEP_TAG,"Assert that the Dashboard Page is the landing page and it is loaded successfully.")
+ Log.d(STEP_TAG, "Assert that the Dashboard Page is the landing page and it is loaded successfully.")
assertDashboardPageDisplayed(student1)
- Log.d(STEP_TAG,"Click on 'Change User' button on the left-side menu.")
+ Log.d(STEP_TAG, "Click on 'Change User' button on the left-side menu.")
dashboardPage.pressChangeUser()
- Log.d(STEP_TAG,"Assert that the previously logins has been displayed. Assert that ${student1.name} and ${student2.name} students are displayed within the previous login section.")
+ Log.d(STEP_TAG, "Assert that the previously logins has been displayed. Assert that ${student1.name} and ${student2.name} students are displayed within the previous login section.")
loginLandingPage.assertDisplaysPreviousLogins()
loginLandingPage.assertPreviousLoginUserDisplayed(student1.name)
loginLandingPage.assertPreviousLoginUserDisplayed(student2.name)
- Log.d(STEP_TAG,"Remove ${student1.name} student from the previous login section.")
+ Log.d(STEP_TAG, "Remove ${student1.name} student from the previous login section.")
loginLandingPage.removeUserFromPreviousLogins(student1.name)
- Log.d(STEP_TAG,"Login with the previous user, ${student2.name}, with one click, by clicking on the user's name on the bottom.")
+ Log.d(STEP_TAG, "Login with the previous user, ${student2.name}, with one click, by clicking on the user's name on the bottom.")
loginLandingPage.loginWithPreviousUser(student2)
- Log.d(STEP_TAG,"Assert that the Dashboard Page is the landing page and it is loaded successfully.")
+ Log.d(STEP_TAG, "Assert that the Dashboard Page is the landing page and it is loaded successfully.")
assertDashboardPageDisplayed(student2)
- Log.d(STEP_TAG,"Click on 'Change User' button on the left-side menu.")
+ Log.d(STEP_TAG, "Click on 'Change User' button on the left-side menu.")
dashboardPage.pressChangeUser()
- Log.d(STEP_TAG,"Assert that the previously logins has been displayed. Assert that ${student1.name} and ${student2.name} students are displayed within the previous login section.")
+ Log.d(STEP_TAG, "Assert that the previously logins has been displayed. Assert that ${student1.name} and ${student2.name} students are displayed within the previous login section.")
loginLandingPage.assertDisplaysPreviousLogins()
loginLandingPage.assertPreviousLoginUserDisplayed(student2.name)
- Log.d(STEP_TAG,"Remove ${student2.name} student from the previous login section.")
+ Log.d(STEP_TAG, "Remove ${student2.name} student from the previous login section.")
loginLandingPage.removeUserFromPreviousLogins(student2.name)
- Log.d(STEP_TAG,"Assert that none of the students, ${student1.name} and ${student2.name} are displayed and not even the 'Previous Logins' label is displayed.")
+ Log.d(STEP_TAG, "Assert that none of the students, ${student1.name} and ${student2.name} are displayed and not even the 'Previous Logins' label is displayed.")
loginLandingPage.assertPreviousLoginUserNotExist(student1.name)
loginLandingPage.assertPreviousLoginUserNotExist(student2.name)
loginLandingPage.assertNotDisplaysPreviousLogins()
}
+ @E2E
+ @Test
+ @TestMetaData(Priority.MANDATORY, FeatureCategory.LOGIN, TestCategory.E2E)
+ fun testLoginE2EWithLastSavedSchool() {
+
+ Log.d(PREPARATION_TAG, "Seeding data.")
+ val data = seedData(students = 2, teachers = 1, courses = 1)
+ val student1 = data.studentsList[0]
+ val student2 = data.studentsList[1]
+
+ Log.d(STEP_TAG, "Login with user: ${student1.name}, login id: ${student1.loginId}.")
+ loginWithUser(student1)
+
+ Log.d(STEP_TAG, "Assert that the Dashboard Page is the landing page and it is loaded successfully.")
+ assertDashboardPageDisplayed(student1)
+
+ Log.d(STEP_TAG, "Log out with ${student1.name} student.")
+ dashboardPage.logOut()
+
+ Log.d(STEP_TAG, "Login with user: ${student2.name}, login id: ${student2.loginId}.")
+ loginWithLastSavedSchool(student2)
+
+ Log.d(STEP_TAG, "Assert that the Dashboard Page is the landing page and it is loaded successfully.")
+ assertDashboardPageDisplayed(student2)
+
+ }
+
@E2E
@Test
@TestMetaData(Priority.MANDATORY, FeatureCategory.LOGIN, TestCategory.E2E)
@@ -149,7 +176,7 @@ class LoginE2ETest : StudentTest() {
dashboardPage.logOut()
Log.d(STEP_TAG,"Login with user: ${teacher.name}, login id: ${teacher.loginId}.")
- loginWithUser(teacher)
+ loginWithUser(teacher, true)
Log.d(STEP_TAG,"Validate ${teacher.name} user's role as a Teacher.")
validateUserAndRole(teacher, course, "Teacher")
@@ -161,7 +188,7 @@ class LoginE2ETest : StudentTest() {
dashboardPage.logOut()
Log.d(STEP_TAG,"Login with user: ${ta.name}, login id: ${ta.loginId}.")
- loginWithUser(ta)
+ loginWithUser(ta, true)
Log.d(STEP_TAG,"Validate ${ta.name} user's role as a TA.")
validateUserAndRole(ta, course, "TA")
@@ -173,7 +200,7 @@ class LoginE2ETest : StudentTest() {
dashboardPage.logOut()
Log.d(STEP_TAG,"Login with user: ${parent.name}, login id: ${parent.loginId}.")
- loginWithUser(parent)
+ loginWithUser(parent, true)
Log.d(STEP_TAG,"Assert that the Dashboard Page is the landing page and it is loaded successfully.")
assertDashboardPageDisplayed(parent)
@@ -230,9 +257,16 @@ class LoginE2ETest : StudentTest() {
dashboardPage.logOut()
}
- private fun loginWithUser(user: CanvasUserApiModel) {
- Log.d(STEP_TAG,"Click 'Find My School' button.")
- loginLandingPage.clickFindMySchoolButton()
+ private fun loginWithUser(user: CanvasUserApiModel, lastSchoolSaved: Boolean = false) {
+
+ if(lastSchoolSaved) {
+ Log.d(STEP_TAG,"Click 'Find Another School' button.")
+ loginLandingPage.clickFindAnotherSchoolButton()
+ }
+ else {
+ Log.d(STEP_TAG, "Click 'Find My School' button.")
+ loginLandingPage.clickFindMySchoolButton()
+ }
Log.d(STEP_TAG,"Enter domain: ${user.domain}.")
loginFindSchoolPage.enterDomain(user.domain)
@@ -242,6 +276,15 @@ class LoginE2ETest : StudentTest() {
loginSignInPage.loginAs(user)
}
+ private fun loginWithLastSavedSchool(user: CanvasUserApiModel) {
+
+ Log.d(STEP_TAG, "Click on last saved school's button.")
+ loginLandingPage.clickOnLastSavedSchoolButton()
+
+ Log.d(STEP_TAG, "Login with ${user.name} user.")
+ loginSignInPage.loginAs(user)
+ }
+
private fun validateUserAndRole(user: CanvasUserApiModel, course: CourseApiModel, role: String) {
Log.d(STEP_TAG,"Assert that the Dashboard Page is the landing page and it is loaded successfully.")
diff --git a/apps/student/src/androidTest/java/com/instructure/student/ui/pages/LoginLandingPage.kt b/apps/student/src/androidTest/java/com/instructure/student/ui/pages/LoginLandingPage.kt
index 1fe4cc35ad..150ef99490 100644
--- a/apps/student/src/androidTest/java/com/instructure/student/ui/pages/LoginLandingPage.kt
+++ b/apps/student/src/androidTest/java/com/instructure/student/ui/pages/LoginLandingPage.kt
@@ -34,6 +34,8 @@ class LoginLandingPage : BasePage() {
private val canvasLogoImageView by OnViewWithId(R.id.canvasLogo)
private val findMySchoolButton by OnViewWithId(R.id.findMySchool)
+ private val findAnotherSchoolButton by OnViewWithId(R.id.findAnotherSchool, autoAssert = false)
+ private val lastSavedSchoolButton by OnViewWithId(R.id.openRecentSchool, autoAssert = false)
private val canvasNetworkTextView by OnViewWithId(R.id.canvasNetwork)
private val previousLoginWrapper by OnViewWithId(R.id.previousLoginWrapper, autoAssert = false)
private val previousLoginTitleText by OnViewWithId(R.id.previousLoginTitleText, autoAssert = false)
@@ -47,6 +49,14 @@ class LoginLandingPage : BasePage() {
findMySchoolButton.click()
}
+ fun clickFindAnotherSchoolButton() {
+ findAnotherSchoolButton.click()
+ }
+
+ fun clickOnLastSavedSchoolButton() {
+ lastSavedSchoolButton.click()
+ }
+
fun clickCanvasNetworkButton() {
canvasNetworkTextView.click()
}
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 9260e235c8..9cf3a9ec64 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
@@ -18,13 +18,15 @@ package com.instructure.student.ui.pages
import androidx.test.espresso.web.sugar.Web
import androidx.test.espresso.web.sugar.Web.onWebView
-import androidx.test.espresso.web.webdriver.DriverAtoms.*
+import androidx.test.espresso.web.webdriver.DriverAtoms.findElement
+import androidx.test.espresso.web.webdriver.DriverAtoms.webClick
+import androidx.test.espresso.web.webdriver.DriverAtoms.webKeys
import androidx.test.espresso.web.webdriver.Locator
import com.instructure.dataseeding.model.CanvasUserApiModel
-import com.instructure.student.R
import com.instructure.espresso.OnViewWithId
import com.instructure.espresso.assertDisplayed
import com.instructure.espresso.page.BasePage
+import com.instructure.student.R
@Suppress("unused")
class LoginSignInPage: BasePage() {
@@ -78,15 +80,15 @@ class LoginSignInPage: BasePage() {
//region UI Action Helpers
- fun enterEmail(email: String) {
+ private fun enterEmail(email: String) {
emailField().perform(webKeys(email))
}
- fun enterPassword(password: String) {
+ private fun enterPassword(password: String) {
passwordField().perform(webKeys(password))
}
- fun clickLoginButton() {
+ private fun clickLoginButton() {
loginButton().perform(webClick())
}
@@ -98,7 +100,7 @@ class LoginSignInPage: BasePage() {
loginAs(user.loginId, user.password)
}
- fun loginAs(loginId: String, password: String) {
+ private fun loginAs(loginId: String, password: String) {
enterEmail(loginId)
enterPassword(password)
clickLoginButton()
diff --git a/apps/teacher/src/androidTest/java/com/instructure/teacher/ui/e2e/LoginE2ETest.kt b/apps/teacher/src/androidTest/java/com/instructure/teacher/ui/e2e/LoginE2ETest.kt
index ba2f529c3d..828d8118c2 100644
--- a/apps/teacher/src/androidTest/java/com/instructure/teacher/ui/e2e/LoginE2ETest.kt
+++ b/apps/teacher/src/androidTest/java/com/instructure/teacher/ui/e2e/LoginE2ETest.kt
@@ -48,17 +48,7 @@ class LoginE2ETest : TeacherTest() {
val teacher2 = data.teachersList[1]
val course = data.coursesList[0]
- Log.d(STEP_TAG,"Click 'Find My School' button.")
- loginLandingPage.clickFindMySchoolButton()
-
- Log.d(STEP_TAG,"Enter domain: ${teacher1.domain}.")
- loginFindSchoolPage.enterDomain(teacher1.domain)
-
- Log.d(STEP_TAG,"Click on 'Next' button on the Toolbar.")
- loginFindSchoolPage.clickToolbarNextMenuItem()
-
- Log.d(STEP_TAG,"Login with user: ${teacher1.name}, login id: ${teacher1.loginId}.")
- loginSignInPage.loginAs(teacher1)
+ loginWithUser(teacher1)
Log.d(STEP_TAG,"Assert that the Dashboard Page is the landing page and it is loaded successfully.")
verifyDashboardPage(teacher1)
@@ -69,17 +59,7 @@ class LoginE2ETest : TeacherTest() {
Log.d(STEP_TAG,"Log out with ${teacher1.name} student.")
dashboardPage.logOut()
- Log.d(STEP_TAG,"Click 'Find My School' button.")
- loginLandingPage.clickFindMySchoolButton()
-
- Log.d(STEP_TAG,"Enter domain: ${teacher2.domain}.")
- loginFindSchoolPage.enterDomain(teacher2.domain)
-
- Log.d(STEP_TAG,"Click on 'Next' button on the Toolbar.")
- loginFindSchoolPage.clickToolbarNextMenuItem()
-
- Log.d(STEP_TAG,"Login with user: ${teacher2.name}, login id: ${teacher2.loginId}.")
- loginSignInPage.loginAs(teacher2)
+ loginWithUser(teacher2, true)
Log.d(STEP_TAG,"Assert that the Dashboard Page is the landing page and it is loaded successfully.")
verifyDashboardPage(teacher2)
@@ -90,17 +70,7 @@ class LoginE2ETest : TeacherTest() {
Log.d(STEP_TAG,"Assert that the previously logins has been displayed.")
loginLandingPage.assertDisplaysPreviousLogins()
- Log.d(STEP_TAG,"Login MANUALLY. Click 'Find My School' button.")
- loginLandingPage.clickFindMySchoolButton()
-
- Log.d(STEP_TAG,"Enter domain: ${teacher1.domain}.")
- loginFindSchoolPage.enterDomain(teacher1.domain)
-
- Log.d(STEP_TAG,"Click on 'Next' button on the Toolbar.")
- loginFindSchoolPage.clickToolbarNextMenuItem()
-
- Log.d(STEP_TAG,"Login with user: ${teacher1.name}, login id: ${teacher1.loginId}.")
- loginSignInPage.loginAs(teacher1)
+ loginWithUser(teacher1, true)
Log.d(STEP_TAG,"Assert that the Dashboard Page is the landing page and it is loaded successfully.")
verifyDashboardPage(teacher1)
@@ -133,17 +103,7 @@ class LoginE2ETest : TeacherTest() {
val student = data.studentsList[0]
val parent = parentData.parentsList[0]
- Log.d(STEP_TAG,"Click 'Find My School' button.")
- loginLandingPage.clickFindMySchoolButton()
-
- Log.d(STEP_TAG,"Enter domain: ${parent.domain}.")
- loginFindSchoolPage.enterDomain(parent.domain)
-
- Log.d(STEP_TAG,"Enter domain: ${parent.domain}.")
- loginFindSchoolPage.clickToolbarNextMenuItem()
-
- Log.d(STEP_TAG,"Login with user: ${student.name}, login id: ${student.loginId}.")
- loginSignInPage.loginAs(student)
+ loginWithUser(student)
Log.d(STEP_TAG,"Assert that the user has been landed on 'Not a teacher?' Page.")
notATeacherPage.assertPageObjects()
@@ -154,20 +114,7 @@ class LoginE2ETest : TeacherTest() {
Log.d(STEP_TAG,"Assert the Teacher app's Login Landing Page's screen is displayed.")
loginLandingPage.assertPageObjects()
- Log.d(STEP_TAG,"Click 'Find My School' button.")
- loginLandingPage.clickFindMySchoolButton()
-
- Log.d(STEP_TAG,"Enter domain: ${parent.domain}.")
- loginFindSchoolPage.enterDomain(parent.domain)
-
- Log.d(STEP_TAG,"Enter domain: ${parent.domain}.")
- loginFindSchoolPage.clickToolbarNextMenuItem()
-
- Log.d(STEP_TAG,"Assert that the Login page has been displayed.")
- loginSignInPage.assertPageObjects()
-
- Log.d(STEP_TAG,"Login with user: ${parent.name}, login id: ${parent.loginId}.")
- loginSignInPage.loginAs(parent)
+ loginWithUser(parent, true)
Log.d(STEP_TAG,"Assert that the user has been landed on 'Not a teacher?' Page.")
notATeacherPage.assertPageObjects()
@@ -179,6 +126,62 @@ class LoginE2ETest : TeacherTest() {
loginLandingPage.assertPageObjects()
}
+ @E2E
+ @Test
+ @TestMetaData(Priority.MANDATORY, FeatureCategory.LOGIN, TestCategory.E2E)
+ fun testLoginE2EWithLastSavedSchool() {
+
+ Log.d(PREPARATION_TAG, "Seeding data.")
+ val data = seedData(students = 1, teachers = 2, courses = 1)
+ val teacher1 = data.teachersList[0]
+ val teacher2 = data.teachersList[1]
+
+ Log.d(STEP_TAG, "Login with user: ${teacher1.name}, login id: ${teacher1.loginId}.")
+ loginWithUser(teacher1)
+
+ Log.d(STEP_TAG, "Assert that the Dashboard Page is the landing page and it is loaded successfully.")
+ verifyDashboardPage(teacher1)
+
+ Log.d(STEP_TAG, "Log out with ${teacher1.name} student.")
+ dashboardPage.logOut()
+
+ loginWithLastSavedSchool(teacher2)
+
+ Log.d(STEP_TAG, "Assert that the Dashboard Page is the landing page and it is loaded successfully.")
+ verifyDashboardPage(teacher2)
+
+ }
+
+ private fun loginWithUser(user: CanvasUserApiModel, lastSchoolSaved: Boolean = false) {
+
+ if(lastSchoolSaved) {
+ Log.d(STEP_TAG, "Click 'Find another school' button.")
+ loginLandingPage.clickFindAnotherSchoolButton()
+ }
+ else {
+ Log.d(STEP_TAG, "Click 'Find My School' button.")
+ loginLandingPage.clickFindMySchoolButton()
+ }
+
+ Log.d(STEP_TAG, "Enter domain: ${user.domain}.")
+ loginFindSchoolPage.enterDomain(user.domain)
+
+ Log.d(STEP_TAG, "Click on 'Next' button on the Toolbar.")
+ loginFindSchoolPage.clickToolbarNextMenuItem()
+
+ Log.d(STEP_TAG, "Login with user: ${user.name}, login id: ${user.loginId}.")
+ loginSignInPage.loginAs(user)
+ }
+
+ private fun loginWithLastSavedSchool(user: CanvasUserApiModel) {
+
+ Log.d(STEP_TAG, "Click on last saved school's button.")
+ loginLandingPage.clickOnLastSavedSchoolButton()
+
+ Log.d(STEP_TAG, "Login with ${user.name} user.")
+ loginSignInPage.loginAs(user)
+ }
+
private fun validateUserRole(user: CanvasUserApiModel, course: CourseApiModel, role: String) {
Log.d(STEP_TAG,"Navigate to 'People' Page of ${course.name} course.")
diff --git a/apps/teacher/src/androidTest/java/com/instructure/teacher/ui/pages/LoginLandingPage.kt b/apps/teacher/src/androidTest/java/com/instructure/teacher/ui/pages/LoginLandingPage.kt
index c5ff9e17a5..a991a3a6f1 100644
--- a/apps/teacher/src/androidTest/java/com/instructure/teacher/ui/pages/LoginLandingPage.kt
+++ b/apps/teacher/src/androidTest/java/com/instructure/teacher/ui/pages/LoginLandingPage.kt
@@ -13,7 +13,9 @@ import com.instructure.teacher.R
class LoginLandingPage : BasePage() {
private val canvasLogoImageView by OnViewWithId(R.id.canvasLogo)
- private val findMySchoolButton by OnViewWithId(R.id.findMySchool)
+ private val findMySchoolButton by OnViewWithId(R.id.findMySchool, autoAssert = false)
+ private val findAnotherSchoolButton by OnViewWithId(R.id.findAnotherSchool, autoAssert = false)
+ private val lastSavedSchoolButton by OnViewWithId(R.id.openRecentSchool, autoAssert = false)
private val canvasNetworkTextView by OnViewWithId(R.id.canvasNetwork)
private val previousLoginWrapper by OnViewWithId(R.id.previousLoginWrapper, autoAssert = false)
private val previousLoginTitleText by OnViewWithId(R.id.previousLoginTitleText, autoAssert = false)
@@ -26,6 +28,14 @@ class LoginLandingPage : BasePage() {
findMySchoolButton.click()
}
+ fun clickFindAnotherSchoolButton() {
+ findAnotherSchoolButton.click()
+ }
+
+ fun clickOnLastSavedSchoolButton() {
+ lastSavedSchoolButton.click()
+ }
+
fun clickCanvasNetworkButton() {
canvasNetworkTextView.click()
}
From bf4264c26398e00d78bc104b3864864937fe62d1 Mon Sep 17 00:00:00 2001
From: Kristof Nemere <109959688+kristofnemere@users.noreply.github.com>
Date: Tue, 11 Oct 2022 09:26:58 +0200
Subject: [PATCH 22/73] [MBL-15325][Student][Teacher] Users should be able to
open embedded google slides in the browser
refs: MBL-15325
affects: Student, Teacher
release note: Added option to open embedded google docs in the browser.
* Option to open embedded google slides in the browser
* Fixed comments
* Fixed comments
* Created JsGoogleDocsInterface to make onGoogleDocsButtonPressed work in Discussions too
---
.../student/fragment/PageDetailsFragment.kt | 3 +-
.../teacher/fragments/PageDetailsFragment.kt | 1 -
libs/pandares/src/main/res/values/strings.xml | 1 +
.../pandautils/utils/WebViewExtensions.kt | 35 +++++++++++++++++--
4 files changed, 34 insertions(+), 6 deletions(-)
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 f590d6eb01..d50cf6e2f6 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
@@ -205,8 +205,7 @@ class PageDetailsFragment : InternalWebviewFragment(), Bookmarkable {
// Load the html with the helper function to handle iframe cases
loadHtmlJob = canvasWebView.loadHtmlWithIframes(requireContext(), isTablet, body, ::loadPageHtml, {
- val args = LtiLaunchFragment.makeLTIBundle(
- URLDecoder.decode(it, "utf-8"), getString(R.string.utils_externalToolTitle), true)
+ val args = LtiLaunchFragment.makeLTIBundle(URLDecoder.decode(it, "utf-8"), getString(R.string.utils_externalToolTitle), true)
RouteMatcher.route(requireContext(), Route(LtiLaunchFragment::class.java, canvasContext, args))
}, page.title)
} else if (page.body == null || page.body?.endsWith("") == true) {
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 070d0bbd37..03953324f9 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
@@ -17,7 +17,6 @@
package com.instructure.teacher.fragments
-import android.graphics.Color
import android.os.Bundle
import android.webkit.WebChromeClient
import android.webkit.WebView
diff --git a/libs/pandares/src/main/res/values/strings.xml b/libs/pandares/src/main/res/values/strings.xml
index 4c7caea78d..901a254879 100644
--- a/libs/pandares/src/main/res/values/strings.xml
+++ b/libs/pandares/src/main/res/values/strings.xml
@@ -1235,6 +1235,7 @@
Draft Available
An error occurred while loading submission
Tap to see full content
+ Tap to open in external app
Important Dates
No important dates
Important Dates
diff --git a/libs/pandautils/src/main/java/com/instructure/pandautils/utils/WebViewExtensions.kt b/libs/pandautils/src/main/java/com/instructure/pandautils/utils/WebViewExtensions.kt
index 4eaaa1b859..04cf33ed06 100644
--- a/libs/pandautils/src/main/java/com/instructure/pandautils/utils/WebViewExtensions.kt
+++ b/libs/pandautils/src/main/java/com/instructure/pandautils/utils/WebViewExtensions.kt
@@ -16,7 +16,9 @@
package com.instructure.pandautils.utils
import android.content.Context
+import android.content.Intent
import android.content.res.Configuration
+import android.net.Uri
import android.webkit.JavascriptInterface
import android.webkit.WebSettings
import android.webkit.WebView
@@ -49,6 +51,7 @@ fun WebView.loadHtmlWithIframes(context: Context, isTablet: Boolean, html: Strin
if(html.contains("
"
+ val htmlButton = String.format(button, srcUrl, buttonText)
+ return iframe + htmlButton
+}
+
fun handleLTIPlaceHolders(placeHolderList: ArrayList, html: String): String {
var newHtml = html
for (holder in placeHolderList) {
@@ -146,14 +165,22 @@ suspend fun authenticateLTIUrl(ltiUrl: String): String {
data class Placeholder(val iframeHtml: String, val placeHolderHtml: String)
-class JsExternalToolInterface(val callback: (ltiUrl: String) -> Unit) {
- @Suppress("UNUSED_PARAMETER")
+@Suppress("UNUSED_PARAMETER")
+class JsExternalToolInterface(private val callback: (ltiUrl: String) -> Unit) {
@JavascriptInterface
fun onLtiToolButtonPressed(ltiUrl: String) {
callback(ltiUrl)
}
}
+@Suppress("UNUSED_PARAMETER")
+class JsGoogleDocsInterface(private val context: Context) {
+ @JavascriptInterface
+ fun onGoogleDocsButtonPressed(url: String) {
+ context.startActivity(Intent(Intent.ACTION_VIEW, Uri.parse(url)))
+ }
+}
+
fun WebView.setDarkModeSupport(webThemeDarkeningOnly: Boolean = false) {
if (WebViewFeature.isFeatureSupported(WebViewFeature.FORCE_DARK)) {
val nightModeFlags: Int = context.resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK
@@ -195,3 +222,5 @@ fun WebView.addDarkThemeToHtmlDocument() {
"""
evaluateJavascript(js, {})
}
+
+private fun isGoogleDocsUrl(url: String) = url.contains("docs.google.com")
\ No newline at end of file
From 029e121cef7e525980e8c6ddd5953d679cc2f997 Mon Sep 17 00:00:00 2001
From: Kristof Deak <92309696+kdeakinstructure@users.noreply.github.com>
Date: Tue, 11 Oct 2022 18:31:02 +0200
Subject: [PATCH 23/73] [MBL-16312][Teacher][Student] - Rename 'Rate on the App
Store' menu #1744
* Rename 'Rate on the App Store' menu in Teacher app settings page to 'Rate on the Play Store'.
Remove the rateOnTheAppStore resource from the teacher strings.xml.
Pick up a new, rateOnThePlayStore resource within the pandares strings.xml (because we store every string in the pandares).
* Put back unintentionally deleted string to ar language.
* Name back other string in portugal language.
---
apps/teacher/src/main/res/layout/fragment_settings.xml | 2 +-
apps/teacher/src/main/res/values-ar/strings.xml | 1 -
apps/teacher/src/main/res/values-b+da+instk12/strings.xml | 1 -
apps/teacher/src/main/res/values-b+en+AU+unimelb/strings.xml | 1 -
apps/teacher/src/main/res/values-b+en+GB+instukhe/strings.xml | 1 -
apps/teacher/src/main/res/values-b+nb+instk12/strings.xml | 1 -
apps/teacher/src/main/res/values-b+sv+instk12/strings.xml | 1 -
apps/teacher/src/main/res/values-b+zh+HK/strings.xml | 1 -
apps/teacher/src/main/res/values-b+zh+Hans/strings.xml | 1 -
apps/teacher/src/main/res/values-b+zh+Hant/strings.xml | 1 -
apps/teacher/src/main/res/values-ca/strings.xml | 1 -
apps/teacher/src/main/res/values-cy/strings.xml | 1 -
apps/teacher/src/main/res/values-da/strings.xml | 1 -
apps/teacher/src/main/res/values-de/strings.xml | 1 -
apps/teacher/src/main/res/values-en-rAU/strings.xml | 1 -
apps/teacher/src/main/res/values-en-rCA/strings.xml | 1 -
apps/teacher/src/main/res/values-en-rCY/strings.xml | 1 -
apps/teacher/src/main/res/values-en-rGB/strings.xml | 1 -
apps/teacher/src/main/res/values-es-rES/strings.xml | 1 -
apps/teacher/src/main/res/values-es/strings.xml | 1 -
apps/teacher/src/main/res/values-fi/strings.xml | 1 -
apps/teacher/src/main/res/values-fr-rCA/strings.xml | 1 -
apps/teacher/src/main/res/values-fr/strings.xml | 1 -
apps/teacher/src/main/res/values-ht/strings.xml | 1 -
apps/teacher/src/main/res/values-is/strings.xml | 1 -
apps/teacher/src/main/res/values-it/strings.xml | 1 -
apps/teacher/src/main/res/values-ja/strings.xml | 1 -
apps/teacher/src/main/res/values-mi/strings.xml | 1 -
apps/teacher/src/main/res/values-nb/strings.xml | 1 -
apps/teacher/src/main/res/values-nl/strings.xml | 1 -
apps/teacher/src/main/res/values-pl/strings.xml | 1 -
apps/teacher/src/main/res/values-pt-rBR/strings.xml | 1 -
apps/teacher/src/main/res/values-pt-rPT/strings.xml | 1 -
apps/teacher/src/main/res/values-ru/strings.xml | 1 -
apps/teacher/src/main/res/values-sl/strings.xml | 1 -
apps/teacher/src/main/res/values-sv/strings.xml | 1 -
apps/teacher/src/main/res/values-th/strings.xml | 1 -
apps/teacher/src/main/res/values-vi/strings.xml | 1 -
apps/teacher/src/main/res/values-zh/strings.xml | 1 -
apps/teacher/src/main/res/values/strings.xml | 1 -
libs/pandares/src/main/res/values/strings.xml | 2 +-
41 files changed, 2 insertions(+), 41 deletions(-)
diff --git a/apps/teacher/src/main/res/layout/fragment_settings.xml b/apps/teacher/src/main/res/layout/fragment_settings.xml
index 75de8dd22b..447f501f8c 100644
--- a/apps/teacher/src/main/res/layout/fragment_settings.xml
+++ b/apps/teacher/src/main/res/layout/fragment_settings.xml
@@ -124,7 +124,7 @@
android:minHeight="?android:listPreferredItemHeight"
android:paddingEnd="16dp"
android:paddingStart="16dp"
- android:text="@string/rateOnTheAppStore"
+ android:text="@string/rateOnThePlayStore"
android:textColor="@color/textDarkest"
android:textSize="16sp"/>
diff --git a/apps/teacher/src/main/res/values-ar/strings.xml b/apps/teacher/src/main/res/values-ar/strings.xml
index b8db1987d2..52d1dde9bb 100644
--- a/apps/teacher/src/main/res/values-ar/strings.xml
+++ b/apps/teacher/src/main/res/values-ar/strings.xml
@@ -763,7 +763,6 @@
تغيير المستخدم
عام
إرسال تعليقات
- التقييم على App Store
سياسة الخصوصية
اتفاقية ترخيص المستخدم
شروط الاستخدام
diff --git a/apps/teacher/src/main/res/values-b+da+instk12/strings.xml b/apps/teacher/src/main/res/values-b+da+instk12/strings.xml
index abd699dd1f..383fc23442 100644
--- a/apps/teacher/src/main/res/values-b+da+instk12/strings.xml
+++ b/apps/teacher/src/main/res/values-b+da+instk12/strings.xml
@@ -717,7 +717,6 @@
Skift bruger
Generelt
Send feedback
- Vurder i App Store
Datapolitik
EULA
Betingelser for brug
diff --git a/apps/teacher/src/main/res/values-b+en+AU+unimelb/strings.xml b/apps/teacher/src/main/res/values-b+en+AU+unimelb/strings.xml
index e7180fe4be..7eb93aea36 100644
--- a/apps/teacher/src/main/res/values-b+en+AU+unimelb/strings.xml
+++ b/apps/teacher/src/main/res/values-b+en+AU+unimelb/strings.xml
@@ -715,7 +715,6 @@
Change User
General
Send Feedback
- Rate on the App Store
Privacy Policy
EULA
Terms of Use
diff --git a/apps/teacher/src/main/res/values-b+en+GB+instukhe/strings.xml b/apps/teacher/src/main/res/values-b+en+GB+instukhe/strings.xml
index 3d43fe160d..7f50391321 100644
--- a/apps/teacher/src/main/res/values-b+en+GB+instukhe/strings.xml
+++ b/apps/teacher/src/main/res/values-b+en+GB+instukhe/strings.xml
@@ -715,7 +715,6 @@
Change user
General
Send feedback
- Rate on the App Store
Privacy Policy
EULA
Terms of Use
diff --git a/apps/teacher/src/main/res/values-b+nb+instk12/strings.xml b/apps/teacher/src/main/res/values-b+nb+instk12/strings.xml
index 0eba6238dc..e357faf202 100644
--- a/apps/teacher/src/main/res/values-b+nb+instk12/strings.xml
+++ b/apps/teacher/src/main/res/values-b+nb+instk12/strings.xml
@@ -718,7 +718,6 @@
Endre bruker
Generelt
Send tilbakemelding
- Vurder på App Store
Retningslinjer for personvern
EULA
brukervilkår
diff --git a/apps/teacher/src/main/res/values-b+sv+instk12/strings.xml b/apps/teacher/src/main/res/values-b+sv+instk12/strings.xml
index eb7a5025ae..f61e2da055 100644
--- a/apps/teacher/src/main/res/values-b+sv+instk12/strings.xml
+++ b/apps/teacher/src/main/res/values-b+sv+instk12/strings.xml
@@ -717,7 +717,6 @@
Ändra användare
Allmänt
Skicka feedback
- Bedöm på App Store
Integritetspolicy
EULA
Användningsvillkor
diff --git a/apps/teacher/src/main/res/values-b+zh+HK/strings.xml b/apps/teacher/src/main/res/values-b+zh+HK/strings.xml
index a00c82be81..9a3c6dbfba 100644
--- a/apps/teacher/src/main/res/values-b+zh+HK/strings.xml
+++ b/apps/teacher/src/main/res/values-b+zh+HK/strings.xml
@@ -703,7 +703,6 @@
變更使用者
一般
發送回饋
- 前往 App Store 評分
《隱私政策》
EULA
《使用條款》
diff --git a/apps/teacher/src/main/res/values-b+zh+Hans/strings.xml b/apps/teacher/src/main/res/values-b+zh+Hans/strings.xml
index 1e1ac223c3..487ebf0152 100644
--- a/apps/teacher/src/main/res/values-b+zh+Hans/strings.xml
+++ b/apps/teacher/src/main/res/values-b+zh+Hans/strings.xml
@@ -703,7 +703,6 @@
更改用户
一般
发送反馈
- 给应用商店评分
隐私政策
最终用户许可协议
使用条款
diff --git a/apps/teacher/src/main/res/values-b+zh+Hant/strings.xml b/apps/teacher/src/main/res/values-b+zh+Hant/strings.xml
index a00c82be81..9a3c6dbfba 100644
--- a/apps/teacher/src/main/res/values-b+zh+Hant/strings.xml
+++ b/apps/teacher/src/main/res/values-b+zh+Hant/strings.xml
@@ -703,7 +703,6 @@
變更使用者
一般
發送回饋
- 前往 App Store 評分
《隱私政策》
EULA
《使用條款》
diff --git a/apps/teacher/src/main/res/values-ca/strings.xml b/apps/teacher/src/main/res/values-ca/strings.xml
index c73fe877fa..78d844a5c3 100644
--- a/apps/teacher/src/main/res/values-ca/strings.xml
+++ b/apps/teacher/src/main/res/values-ca/strings.xml
@@ -718,7 +718,6 @@
Canvia l\'usuari
General
Envia els comentaris
- Puntuació a App Store
Política de privacitat
EULA
Condicions d\'ús
diff --git a/apps/teacher/src/main/res/values-cy/strings.xml b/apps/teacher/src/main/res/values-cy/strings.xml
index a7f86304c6..205c9f9390 100644
--- a/apps/teacher/src/main/res/values-cy/strings.xml
+++ b/apps/teacher/src/main/res/values-cy/strings.xml
@@ -715,7 +715,6 @@
Newid Defnyddiwr
Cyffredinol
Anfon Adborth
- Sgorio’r App Store
Polisi Preifatrwydd
EULA
Telerau Defnyddio
diff --git a/apps/teacher/src/main/res/values-da/strings.xml b/apps/teacher/src/main/res/values-da/strings.xml
index 8045a7237c..d7a0ebfa97 100644
--- a/apps/teacher/src/main/res/values-da/strings.xml
+++ b/apps/teacher/src/main/res/values-da/strings.xml
@@ -715,7 +715,6 @@
Skift bruger
Generelt
Send feedback
- Vurder i App Store
Datapolitik
EULA
Betingelser for brug
diff --git a/apps/teacher/src/main/res/values-de/strings.xml b/apps/teacher/src/main/res/values-de/strings.xml
index a80b3cab5c..eedeb9a24b 100644
--- a/apps/teacher/src/main/res/values-de/strings.xml
+++ b/apps/teacher/src/main/res/values-de/strings.xml
@@ -715,7 +715,6 @@
Benutzer wechseln
Allgemein
Feedback senden
- Im App Store bewerten
Datenschutzrichtlinien
EULA
Nutzungsbedingungen
diff --git a/apps/teacher/src/main/res/values-en-rAU/strings.xml b/apps/teacher/src/main/res/values-en-rAU/strings.xml
index de27af29f6..b07bb9a4e7 100644
--- a/apps/teacher/src/main/res/values-en-rAU/strings.xml
+++ b/apps/teacher/src/main/res/values-en-rAU/strings.xml
@@ -715,7 +715,6 @@
Change User
General
Send Feedback
- Rate on the App Store
Privacy Policy
EULA
Terms of Use
diff --git a/apps/teacher/src/main/res/values-en-rCA/strings.xml b/apps/teacher/src/main/res/values-en-rCA/strings.xml
index e663c539c4..47edc45449 100644
--- a/apps/teacher/src/main/res/values-en-rCA/strings.xml
+++ b/apps/teacher/src/main/res/values-en-rCA/strings.xml
@@ -718,7 +718,6 @@
Change User
General
Send Feedback
- Rate on the App Store
Privacy Policy
EULA
Terms of Use
diff --git a/apps/teacher/src/main/res/values-en-rCY/strings.xml b/apps/teacher/src/main/res/values-en-rCY/strings.xml
index 3d43fe160d..7f50391321 100644
--- a/apps/teacher/src/main/res/values-en-rCY/strings.xml
+++ b/apps/teacher/src/main/res/values-en-rCY/strings.xml
@@ -715,7 +715,6 @@
Change user
General
Send feedback
- Rate on the App Store
Privacy Policy
EULA
Terms of Use
diff --git a/apps/teacher/src/main/res/values-en-rGB/strings.xml b/apps/teacher/src/main/res/values-en-rGB/strings.xml
index edcfd0f24c..03d0abfc1d 100644
--- a/apps/teacher/src/main/res/values-en-rGB/strings.xml
+++ b/apps/teacher/src/main/res/values-en-rGB/strings.xml
@@ -715,7 +715,6 @@
Change user
General
Send feedback
- Rate on the App Store
Privacy Policy
EULA
Terms of Use
diff --git a/apps/teacher/src/main/res/values-es-rES/strings.xml b/apps/teacher/src/main/res/values-es-rES/strings.xml
index 50bfff9dbc..7c72ca5750 100644
--- a/apps/teacher/src/main/res/values-es-rES/strings.xml
+++ b/apps/teacher/src/main/res/values-es-rES/strings.xml
@@ -718,7 +718,6 @@
Cambiar usuario
General
Enviar comentarios
- Califica en la App Store
Política de privacidad
EULA
Términos de uso
diff --git a/apps/teacher/src/main/res/values-es/strings.xml b/apps/teacher/src/main/res/values-es/strings.xml
index 4c2f9a3fc3..13c0ac0e06 100644
--- a/apps/teacher/src/main/res/values-es/strings.xml
+++ b/apps/teacher/src/main/res/values-es/strings.xml
@@ -716,7 +716,6 @@
Cambiar usuario
General
Enviar los comentarios
- Califique en App Store
Política de privacidad
EULA
Términos de uso
diff --git a/apps/teacher/src/main/res/values-fi/strings.xml b/apps/teacher/src/main/res/values-fi/strings.xml
index 496f5a72c2..2e4d02210d 100644
--- a/apps/teacher/src/main/res/values-fi/strings.xml
+++ b/apps/teacher/src/main/res/values-fi/strings.xml
@@ -715,7 +715,6 @@
Change User (Vaihda käyttäjää)
Yleinen
Lähetä palautetta
- Arvostele App Storessa
Tietosuojakäytäntö
EULA
Käyttöehdot
diff --git a/apps/teacher/src/main/res/values-fr-rCA/strings.xml b/apps/teacher/src/main/res/values-fr-rCA/strings.xml
index 8e4a5feca0..ad52328543 100644
--- a/apps/teacher/src/main/res/values-fr-rCA/strings.xml
+++ b/apps/teacher/src/main/res/values-fr-rCA/strings.xml
@@ -715,7 +715,6 @@
Changer d\'utilisateur
Général
Envoyer un avis
- Noter sur l\'App Store
Politique de confidentialité
CLUF
Conditions d\'utilisation
diff --git a/apps/teacher/src/main/res/values-fr/strings.xml b/apps/teacher/src/main/res/values-fr/strings.xml
index 488d38ab0c..e50b9a211d 100644
--- a/apps/teacher/src/main/res/values-fr/strings.xml
+++ b/apps/teacher/src/main/res/values-fr/strings.xml
@@ -715,7 +715,6 @@
Changer d\'utilisateur
Général
Envoyer un avis
- Évaluez sur l’App Store
Politique de confidentialité
CGU
Conditions d\'utilisation
diff --git a/apps/teacher/src/main/res/values-ht/strings.xml b/apps/teacher/src/main/res/values-ht/strings.xml
index e0a6854a12..41838a8b74 100644
--- a/apps/teacher/src/main/res/values-ht/strings.xml
+++ b/apps/teacher/src/main/res/values-ht/strings.xml
@@ -715,7 +715,6 @@
Chanje Itilizatè
Jeneral
Voye Kòmantè
- Evalye nan App Store
Politik Konfidansyalite
EULA
Kondisyon Itilizasyon
diff --git a/apps/teacher/src/main/res/values-is/strings.xml b/apps/teacher/src/main/res/values-is/strings.xml
index 274a6471af..492f370465 100644
--- a/apps/teacher/src/main/res/values-is/strings.xml
+++ b/apps/teacher/src/main/res/values-is/strings.xml
@@ -716,7 +716,6 @@
Breyta notanda
Almennt
Senda endurgjöf
- Gefa einkunn á App Store
Persónuverndarstefna
EULA
Notandaskilmálar
diff --git a/apps/teacher/src/main/res/values-it/strings.xml b/apps/teacher/src/main/res/values-it/strings.xml
index 29cd811b7a..dece9b4d27 100644
--- a/apps/teacher/src/main/res/values-it/strings.xml
+++ b/apps/teacher/src/main/res/values-it/strings.xml
@@ -716,7 +716,6 @@
Cambia utente
Generale
Invia feedback
- Valuta su App Store
Informativa sulla privacy
EULA
Termini di utilizzo
diff --git a/apps/teacher/src/main/res/values-ja/strings.xml b/apps/teacher/src/main/res/values-ja/strings.xml
index 15921efd04..c30d6deb42 100644
--- a/apps/teacher/src/main/res/values-ja/strings.xml
+++ b/apps/teacher/src/main/res/values-ja/strings.xml
@@ -703,7 +703,6 @@
ユーザーを変更する
一般
フィードバックを送る
- App Storeでレート
プライバシーポリシー
EULA
利用規約
diff --git a/apps/teacher/src/main/res/values-mi/strings.xml b/apps/teacher/src/main/res/values-mi/strings.xml
index 0891fbac4d..67767cb55c 100644
--- a/apps/teacher/src/main/res/values-mi/strings.xml
+++ b/apps/teacher/src/main/res/values-mi/strings.xml
@@ -715,7 +715,6 @@
Huri Kaiwhakamahi
Whānui
Tuku Urupare
- Auau i runga i te Taupānga Toa
Kaupapahere Tūmataiti
EULA
Ngā ritenga whakamahi
diff --git a/apps/teacher/src/main/res/values-nb/strings.xml b/apps/teacher/src/main/res/values-nb/strings.xml
index 1fa082cfe1..8ca8dfb334 100644
--- a/apps/teacher/src/main/res/values-nb/strings.xml
+++ b/apps/teacher/src/main/res/values-nb/strings.xml
@@ -718,7 +718,6 @@
Endre bruker
Generelt
Send tilbakemelding
- Vurder på App Store
Retningslinjer for personvern
EULA
brukervilkår
diff --git a/apps/teacher/src/main/res/values-nl/strings.xml b/apps/teacher/src/main/res/values-nl/strings.xml
index 0f7734ef68..7cd51b9da2 100644
--- a/apps/teacher/src/main/res/values-nl/strings.xml
+++ b/apps/teacher/src/main/res/values-nl/strings.xml
@@ -715,7 +715,6 @@
Gebruiker wijzigen
Algemeen
Feedback versturen
- Beoordelen in de App Store
Privacy-beleid
EULA
Gebruiksvoorwaarden
diff --git a/apps/teacher/src/main/res/values-pl/strings.xml b/apps/teacher/src/main/res/values-pl/strings.xml
index 79921c3d6e..c6eb459b86 100644
--- a/apps/teacher/src/main/res/values-pl/strings.xml
+++ b/apps/teacher/src/main/res/values-pl/strings.xml
@@ -739,7 +739,6 @@
Zmień użytkownika
Ogólne
Wyślij informację zwrotną
- Pobierz w sklepie App Store
Polityka prywatności
EULA
Warunki korzystania
diff --git a/apps/teacher/src/main/res/values-pt-rBR/strings.xml b/apps/teacher/src/main/res/values-pt-rBR/strings.xml
index 49bd9f5111..5f8f84a232 100644
--- a/apps/teacher/src/main/res/values-pt-rBR/strings.xml
+++ b/apps/teacher/src/main/res/values-pt-rBR/strings.xml
@@ -716,7 +716,6 @@
Trocar Usuário
Geral
Enviar Comentários
- Avaliar na App Store
Política de privacidade
EULA
Termos de Uso
diff --git a/apps/teacher/src/main/res/values-pt-rPT/strings.xml b/apps/teacher/src/main/res/values-pt-rPT/strings.xml
index e8255c1bf1..819d955af8 100644
--- a/apps/teacher/src/main/res/values-pt-rPT/strings.xml
+++ b/apps/teacher/src/main/res/values-pt-rPT/strings.xml
@@ -715,7 +715,6 @@
Mudar utilizador
Geral
Enviar Comentários
- Taxa na App Store
Política de Privacidade
EULA
Termos de uso
diff --git a/apps/teacher/src/main/res/values-ru/strings.xml b/apps/teacher/src/main/res/values-ru/strings.xml
index 3ed833e356..aef6d8a429 100644
--- a/apps/teacher/src/main/res/values-ru/strings.xml
+++ b/apps/teacher/src/main/res/values-ru/strings.xml
@@ -739,7 +739,6 @@
Сменить пользователя
Общее
Отправить отзыв
- Оценить в App Store
Политика конфиденциальности
Лицензионный договор
Условия использования
diff --git a/apps/teacher/src/main/res/values-sl/strings.xml b/apps/teacher/src/main/res/values-sl/strings.xml
index 42d3901e5c..f5a8f16135 100644
--- a/apps/teacher/src/main/res/values-sl/strings.xml
+++ b/apps/teacher/src/main/res/values-sl/strings.xml
@@ -715,7 +715,6 @@
Spremeni uporabnika
Splošno
Pošlji povratne informacije
- Oceni v trgovini App Store
Pravilnik o zasebnosti
Licenčni pogoji za končnega uporabnika
Pogoji uporabe
diff --git a/apps/teacher/src/main/res/values-sv/strings.xml b/apps/teacher/src/main/res/values-sv/strings.xml
index bd1a7dab53..2e1d44873e 100644
--- a/apps/teacher/src/main/res/values-sv/strings.xml
+++ b/apps/teacher/src/main/res/values-sv/strings.xml
@@ -716,7 +716,6 @@
Ändra användare
Allmänt
Skicka feedback
- Bedöm på App Store
Integritetspolicy
EULA
Användningsvillkor
diff --git a/apps/teacher/src/main/res/values-th/strings.xml b/apps/teacher/src/main/res/values-th/strings.xml
index 98a1168099..7e02c5930d 100644
--- a/apps/teacher/src/main/res/values-th/strings.xml
+++ b/apps/teacher/src/main/res/values-th/strings.xml
@@ -717,7 +717,6 @@
เปลี่ยนผู้ใช้
ทั่วไป
ส่งความเห็น
- ให้คะแนนใน App Store
นโยบายความเป็นส่วนตัว
EULA
เงื่อนไขการใช้งาน
diff --git a/apps/teacher/src/main/res/values-vi/strings.xml b/apps/teacher/src/main/res/values-vi/strings.xml
index 9c37b925be..721c67f976 100644
--- a/apps/teacher/src/main/res/values-vi/strings.xml
+++ b/apps/teacher/src/main/res/values-vi/strings.xml
@@ -718,7 +718,6 @@
Thay Đổi Người Dùng
Chung
Gửi Ý Kiến Phản Hồi
- Đánh giá trên App Store
Chính Sách Quyền Riêng Tư
EULA
Điều Khoản Sử Dụng
diff --git a/apps/teacher/src/main/res/values-zh/strings.xml b/apps/teacher/src/main/res/values-zh/strings.xml
index 1e1ac223c3..487ebf0152 100644
--- a/apps/teacher/src/main/res/values-zh/strings.xml
+++ b/apps/teacher/src/main/res/values-zh/strings.xml
@@ -703,7 +703,6 @@
更改用户
一般
发送反馈
- 给应用商店评分
隐私政策
最终用户许可协议
使用条款
diff --git a/apps/teacher/src/main/res/values/strings.xml b/apps/teacher/src/main/res/values/strings.xml
index 69de0f07e9..15abd0fa7c 100644
--- a/apps/teacher/src/main/res/values/strings.xml
+++ b/apps/teacher/src/main/res/values/strings.xml
@@ -718,7 +718,6 @@
Change User
General
Send Feedback
- Rate on the App Store
Privacy Policy
EULA
Terms of Use
diff --git a/libs/pandares/src/main/res/values/strings.xml b/libs/pandares/src/main/res/values/strings.xml
index 901a254879..3a4a2a2785 100644
--- a/libs/pandares/src/main/res/values/strings.xml
+++ b/libs/pandares/src/main/res/values/strings.xml
@@ -1042,7 +1042,7 @@
Current grade
Opens in Canvas Student
Student View
-
+ Rate on the Play Store
From 924825b57471703519d9dcb0111ba3b5c1badda6 Mon Sep 17 00:00:00 2001
From: Kristof Nemere <109959688+kristofnemere@users.noreply.github.com>
Date: Wed, 12 Oct 2022 11:48:30 +0200
Subject: [PATCH 24/73] [MBL-16182][Student][Teacher] Small UI improvements
refs: MBL-16182
affects: Student, Teacher
release note: UI improvements.
* Fixed theme selector bottom sheet issue
* Fixed teacher settings issues, student rce issues
* reverted toolbar color fixes
* reverted toolbar color fixes
* reverted toolbar color fixes
* Fixed teacher settings master details issues
* Fixed comments
---
android-vault | 2 +-
.../student/activity/NavigationActivity.kt | 14 +-
.../student/activity/ParentActivity.kt | 15 +-
.../student/di/ToolbarSetupModule.kt | 35 +++
.../student/fragment/PageDetailsFragment.kt | 3 +-
.../student/router/RouteMatcher.kt | 2 +-
.../util/StudentToolbarSetupBehavior.kt | 31 +++
.../main/res/layout/fragment_edit_page.xml | 62 +++--
.../src/main/res/values-w720dp/bools.xml | 20 --
.../activities/MasterDetailActivity.kt | 17 +-
.../teacher/di/ToolbarSetupModule.kt | 35 +++
.../teacher/fragments/EmptyFragment.kt | 1 -
.../teacher/fragments/FeatureFlagsFragment.kt | 3 +-
.../teacher/fragments/SettingsFragment.kt | 30 ++-
.../utils/TeacherToolbarSetupBehavior.kt | 31 +++
.../interactions/router/RouteType.kt | 2 +-
.../EmailNotificationPreferencesFragment.kt | 18 +-
.../PushNotificationPreferencesFragment.kt | 15 +-
.../themeselector/ThemeSelectorBottomSheet.kt | 6 +-
.../fragments/RemoteConfigParamsFragment.kt | 15 +-
.../pandautils/utils/ToolbarSetupBehavior.kt | 24 ++
.../layout/bottom_sheet_theme_selector.xml | 221 +++++++++---------
22 files changed, 369 insertions(+), 233 deletions(-)
create mode 100644 apps/student/src/main/java/com/instructure/student/di/ToolbarSetupModule.kt
create mode 100644 apps/student/src/main/java/com/instructure/student/util/StudentToolbarSetupBehavior.kt
delete mode 100644 apps/student/src/main/res/values-w720dp/bools.xml
create mode 100644 apps/teacher/src/main/java/com/instructure/teacher/di/ToolbarSetupModule.kt
create mode 100644 apps/teacher/src/main/java/com/instructure/teacher/utils/TeacherToolbarSetupBehavior.kt
create mode 100644 libs/pandautils/src/main/java/com/instructure/pandautils/utils/ToolbarSetupBehavior.kt
diff --git a/android-vault b/android-vault
index eea182a4b6..ceed6c1136 160000
--- a/android-vault
+++ b/android-vault
@@ -1 +1 @@
-Subproject commit eea182a4b626678f271e42d73c100dd53a84611b
+Subproject commit ceed6c1136054f1aa424382d37185857acf19d3d
diff --git a/apps/student/src/main/java/com/instructure/student/activity/NavigationActivity.kt b/apps/student/src/main/java/com/instructure/student/activity/NavigationActivity.kt
index 8d32f87378..db9341f5c5 100644
--- a/apps/student/src/main/java/com/instructure/student/activity/NavigationActivity.kt
+++ b/apps/student/src/main/java/com/instructure/student/activity/NavigationActivity.kt
@@ -41,7 +41,6 @@ import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
import androidx.core.view.GravityCompat
import androidx.core.view.MenuItemCompat
-import androidx.fragment.app.DialogFragment
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentManager
import com.airbnb.lottie.LottieAnimationView
@@ -58,7 +57,6 @@ import com.instructure.interactions.FullScreenInteractions
import com.instructure.interactions.Navigation
import com.instructure.interactions.router.Route
import com.instructure.interactions.router.RouteContext
-import com.instructure.interactions.router.RouteType
import com.instructure.interactions.router.RouterParams
import com.instructure.loginapi.login.dialog.ErrorReportDialog
import com.instructure.loginapi.login.dialog.MasqueradingDialog
@@ -769,16 +767,10 @@ class NavigationActivity : BaseRouterActivity(), Navigation, MasqueradingDialog.
}
private fun addFragment(fragment: Fragment?, route: Route) {
- if (RouteType.DIALOG == route.routeType && fragment is DialogFragment && isTablet) {
- val ft = supportFragmentManager.beginTransaction()
- ft.addToBackStack(fragment::class.java.name)
- fragment.show(ft, fragment::class.java.name)
+ if (fragment != null && fragment::class.java.name in getBottomNavFragmentNames() && isBottomNavFragment(currentFragment)) {
+ selectBottomNavFragment(fragment::class.java)
} else {
- if (fragment != null && fragment::class.java.name in getBottomNavFragmentNames() && isBottomNavFragment(currentFragment)) {
- selectBottomNavFragment(fragment::class.java)
- } else {
- addFullScreenFragment(fragment, route.removePreviousScreen)
- }
+ addFullScreenFragment(fragment, route.removePreviousScreen)
}
}
diff --git a/apps/student/src/main/java/com/instructure/student/activity/ParentActivity.kt b/apps/student/src/main/java/com/instructure/student/activity/ParentActivity.kt
index b5b618a159..96e5d4c81a 100644
--- a/apps/student/src/main/java/com/instructure/student/activity/ParentActivity.kt
+++ b/apps/student/src/main/java/com/instructure/student/activity/ParentActivity.kt
@@ -23,22 +23,17 @@ import android.content.Intent
import android.content.IntentFilter
import android.os.Build
import android.os.Bundle
-import androidx.localbroadcastmanager.content.LocalBroadcastManager
import android.text.TextUtils
import android.util.Log
-import android.view.KeyEvent
-import android.view.MenuItem
-import android.view.MotionEvent
-import android.view.View
+import android.view.*
import android.view.View.OnTouchListener
-import android.view.ViewConfiguration
import android.widget.Toast
-
-import com.instructure.student.R
-import com.instructure.student.util.LoggingUtility
+import androidx.localbroadcastmanager.content.LocalBroadcastManager
import com.instructure.canvasapi2.utils.Logger
import com.instructure.pandautils.activities.BaseActionBarActivity
import com.instructure.pandautils.utils.Const
+import com.instructure.student.R
+import com.instructure.student.util.LoggingUtility
abstract class ParentActivity : BaseActionBarActivity(), OnTouchListener {
@@ -58,7 +53,7 @@ abstract class ParentActivity : BaseActionBarActivity(), OnTouchListener {
get() = this
val isTablet: Boolean
- get() = resources.getBoolean(R.bool.is_device_tablet)
+ get() = resources.getBoolean(R.bool.isDeviceTablet)
val isLandscape: Boolean
get() = resources.getBoolean(R.bool.isLandscape)
diff --git a/apps/student/src/main/java/com/instructure/student/di/ToolbarSetupModule.kt b/apps/student/src/main/java/com/instructure/student/di/ToolbarSetupModule.kt
new file mode 100644
index 0000000000..e40e4a82d3
--- /dev/null
+++ b/apps/student/src/main/java/com/instructure/student/di/ToolbarSetupModule.kt
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2022 - 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.di
+
+import android.app.Activity
+import com.instructure.pandautils.utils.ToolbarSetupBehavior
+import com.instructure.student.util.StudentToolbarSetupBehavior
+import dagger.Module
+import dagger.Provides
+import dagger.hilt.InstallIn
+import dagger.hilt.android.components.FragmentComponent
+
+@Module
+@InstallIn(FragmentComponent::class)
+class ToolbarSetupModule {
+ @Provides
+ fun provideToolbarSetup(activity: Activity): ToolbarSetupBehavior {
+ return StudentToolbarSetupBehavior(activity)
+ }
+}
\ No newline at end of file
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 d50cf6e2f6..9b6a0393d7 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
@@ -36,7 +36,6 @@ import com.instructure.canvasapi2.utils.weave.*
import com.instructure.interactions.bookmarks.Bookmarkable
import com.instructure.interactions.bookmarks.Bookmarker
import com.instructure.interactions.router.Route
-import com.instructure.interactions.router.RouteType
import com.instructure.interactions.router.RouterParams
import com.instructure.loginapi.login.dialog.NoInternetConnectionDialog
import com.instructure.pandautils.analytics.SCREEN_VIEW_PAGE_DETAILS
@@ -288,7 +287,7 @@ class PageDetailsFragment : InternalWebviewFragment(), Bookmarkable {
private fun openEditPage(page: Page) {
if (APIHelper.hasNetworkConnection()) {
- val route = EditPageDetailsFragment.makeRoute(canvasContext, page).apply { routeType = RouteType.DIALOG }
+ val route = EditPageDetailsFragment.makeRoute(canvasContext, page)
RouteMatcher.route(requireContext(), route)
} else {
NoInternetConnectionDialog.show(requireFragmentManager())
diff --git a/apps/student/src/main/java/com/instructure/student/router/RouteMatcher.kt b/apps/student/src/main/java/com/instructure/student/router/RouteMatcher.kt
index a0a6d15a50..a976efa9bc 100644
--- a/apps/student/src/main/java/com/instructure/student/router/RouteMatcher.kt
+++ b/apps/student/src/main/java/com/instructure/student/router/RouteMatcher.kt
@@ -317,7 +317,7 @@ object RouteMatcher : BaseRouteMatcher() {
handleMediaRoute(context, route)
} else if (route.routeContext == RouteContext.SPEED_GRADER) {
//handleSpeedGraderRoute(context, route) //Annotations for student maybe?
- } else if (context.resources.getBoolean(R.bool.is_device_tablet)) {
+ } else if (context.resources.getBoolean(R.bool.isDeviceTablet)) {
handleTabletRoute(context, route)
} else {
handleFullscreenRoute(context, route)
diff --git a/apps/student/src/main/java/com/instructure/student/util/StudentToolbarSetupBehavior.kt b/apps/student/src/main/java/com/instructure/student/util/StudentToolbarSetupBehavior.kt
new file mode 100644
index 0000000000..8e18264619
--- /dev/null
+++ b/apps/student/src/main/java/com/instructure/student/util/StudentToolbarSetupBehavior.kt
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2022 - 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.util
+
+import android.app.Activity
+import androidx.appcompat.widget.Toolbar
+import com.instructure.pandautils.utils.ToolbarSetupBehavior
+import com.instructure.pandautils.utils.ViewStyler
+import com.instructure.pandautils.utils.setupAsBackButton
+
+class StudentToolbarSetupBehavior(val activity: Activity) : ToolbarSetupBehavior {
+ override fun setupToolbar(toolbar: Toolbar) {
+ toolbar.setupAsBackButton { activity.onBackPressed() }
+ ViewStyler.themeToolbarLight(activity, toolbar)
+ }
+}
\ No newline at end of file
diff --git a/apps/student/src/main/res/layout/fragment_edit_page.xml b/apps/student/src/main/res/layout/fragment_edit_page.xml
index a2c1e3c022..88a4f2fb50 100644
--- a/apps/student/src/main/res/layout/fragment_edit_page.xml
+++ b/apps/student/src/main/res/layout/fragment_edit_page.xml
@@ -1,5 +1,4 @@
-
-
@@ -33,46 +31,38 @@
android:layout_height="24dp"
android:layout_gravity="end"
android:layout_marginEnd="16dp"
- android:visibility="gone"/>
+ android:visibility="gone" />
-
+ android:layout_height="match_parent"
+ android:layout_marginTop="8dp"
+ android:clipToPadding="false"
+ android:contentDescription="@string/scrollInstructions"
+ android:orientation="vertical"
+ android:padding="6dp">
-
-
-
-
-
-
-
+ android:layout_marginStart="10dp"
+ android:focusable="true"
+ android:text="@string/description"
+ android:textColor="@color/textDark" />
+
-
+
diff --git a/apps/student/src/main/res/values-w720dp/bools.xml b/apps/student/src/main/res/values-w720dp/bools.xml
deleted file mode 100644
index b9c3b5c78b..0000000000
--- a/apps/student/src/main/res/values-w720dp/bools.xml
+++ /dev/null
@@ -1,20 +0,0 @@
-
-
-
- true
-
\ No newline at end of file
diff --git a/apps/teacher/src/main/java/com/instructure/teacher/activities/MasterDetailActivity.kt b/apps/teacher/src/main/java/com/instructure/teacher/activities/MasterDetailActivity.kt
index 5353ea353d..78c95af187 100644
--- a/apps/teacher/src/main/java/com/instructure/teacher/activities/MasterDetailActivity.kt
+++ b/apps/teacher/src/main/java/com/instructure/teacher/activities/MasterDetailActivity.kt
@@ -22,24 +22,27 @@ import android.content.Context
import android.content.Intent
import android.os.Bundle
import android.os.Parcelable
-import androidx.percentlayout.widget.PercentLayoutHelper
-import androidx.fragment.app.Fragment
import android.view.View
import android.view.ViewTreeObserver
+import androidx.fragment.app.Fragment
+import androidx.percentlayout.widget.PercentLayoutHelper
import com.instructure.canvasapi2.StatusCallback
import com.instructure.canvasapi2.managers.CourseManager
import com.instructure.canvasapi2.models.CanvasContext
import com.instructure.canvasapi2.models.Course
import com.instructure.canvasapi2.utils.ApiType
import com.instructure.canvasapi2.utils.LinkHeaders
+import com.instructure.interactions.Identity
import com.instructure.interactions.MasterDetailInteractions
-import com.instructure.pandautils.utils.*
+import com.instructure.interactions.router.Route
+import com.instructure.pandautils.utils.ThemePrefs
+import com.instructure.pandautils.utils.color
+import com.instructure.pandautils.utils.setGone
+import com.instructure.pandautils.utils.setVisible
import com.instructure.teacher.R
import com.instructure.teacher.fragments.CourseBrowserEmptyFragment
import com.instructure.teacher.fragments.CourseBrowserFragment
import com.instructure.teacher.fragments.EmptyFragment
-import com.instructure.interactions.Identity
-import com.instructure.interactions.router.Route
import com.instructure.teacher.router.RouteMatcher
import com.instructure.teacher.router.RouteResolver
import dagger.hilt.android.AndroidEntryPoint
@@ -101,13 +104,11 @@ class MasterDetailActivity : BaseAppCompatActivity(), MasterDetailInteractions {
}
}
- if(mRoute?.canvasContext is Course) {
+ if (mRoute?.canvasContext is Course) {
val course = mRoute?.canvasContext as Course
- middleTopDivider.setBackgroundColor(course.color)
fakeToolbarMaster.setBackgroundColor(course.color)
fakeToolbarDetail.setBackgroundColor(course.color)
} else {
- middleTopDivider.setBackgroundColor(ThemePrefs.primaryColor)
fakeToolbarMaster.setBackgroundColor(ThemePrefs.primaryColor)
fakeToolbarDetail.setBackgroundColor(ThemePrefs.primaryColor)
}
diff --git a/apps/teacher/src/main/java/com/instructure/teacher/di/ToolbarSetupModule.kt b/apps/teacher/src/main/java/com/instructure/teacher/di/ToolbarSetupModule.kt
new file mode 100644
index 0000000000..cb192bf5e4
--- /dev/null
+++ b/apps/teacher/src/main/java/com/instructure/teacher/di/ToolbarSetupModule.kt
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2022 - 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.teacher.di
+
+import android.app.Activity
+import com.instructure.pandautils.utils.ToolbarSetupBehavior
+import com.instructure.teacher.utils.TeacherToolbarSetupBehavior
+import dagger.Module
+import dagger.Provides
+import dagger.hilt.InstallIn
+import dagger.hilt.android.components.FragmentComponent
+
+@Module
+@InstallIn(FragmentComponent::class)
+class ToolbarSetupModule {
+ @Provides
+ fun provideToolbarSetup(activity: Activity): ToolbarSetupBehavior {
+ return TeacherToolbarSetupBehavior(activity)
+ }
+}
\ No newline at end of file
diff --git a/apps/teacher/src/main/java/com/instructure/teacher/fragments/EmptyFragment.kt b/apps/teacher/src/main/java/com/instructure/teacher/fragments/EmptyFragment.kt
index 5ea5d5952c..a9e85c157a 100644
--- a/apps/teacher/src/main/java/com/instructure/teacher/fragments/EmptyFragment.kt
+++ b/apps/teacher/src/main/java/com/instructure/teacher/fragments/EmptyFragment.kt
@@ -15,7 +15,6 @@
*/
package com.instructure.teacher.fragments
-import android.graphics.Color
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
diff --git a/apps/teacher/src/main/java/com/instructure/teacher/fragments/FeatureFlagsFragment.kt b/apps/teacher/src/main/java/com/instructure/teacher/fragments/FeatureFlagsFragment.kt
index dacee5373c..75159136f0 100644
--- a/apps/teacher/src/main/java/com/instructure/teacher/fragments/FeatureFlagsFragment.kt
+++ b/apps/teacher/src/main/java/com/instructure/teacher/fragments/FeatureFlagsFragment.kt
@@ -23,6 +23,7 @@ import android.view.ViewGroup
import androidx.fragment.app.Fragment
import androidx.recyclerview.widget.RecyclerView
import com.instructure.canvasapi2.utils.FeatureFlagPref
+import com.instructure.pandautils.utils.isTablet
import com.instructure.pandautils.utils.setupAsBackButton
import com.instructure.teacher.R
import com.instructure.teacher.utils.FeatureFlags
@@ -38,7 +39,7 @@ class FeatureFlagsFragment : Fragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
recyclerView.adapter = FeatureFlagAdapter()
- toolbar.setupAsBackButton(this)
+ if (!isTablet) toolbar.setupAsBackButton(this)
}
}
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 2c3af578dd..6ccc99ba62 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
@@ -47,16 +47,36 @@ class SettingsFragment : BasePresenterFragment.
+ *
+ */
+
+package com.instructure.teacher.utils
+
+import android.app.Activity
+import androidx.appcompat.widget.Toolbar
+import com.instructure.pandautils.utils.ToolbarSetupBehavior
+import com.instructure.pandautils.utils.ViewStyler
+import com.instructure.pandautils.utils.setupAsBackButton
+
+class TeacherToolbarSetupBehavior(val activity: Activity) : ToolbarSetupBehavior {
+ override fun setupToolbar(toolbar: Toolbar) {
+ if (!activity.isTablet) toolbar.setupAsBackButton { activity.onBackPressed() }
+ ViewStyler.themeToolbarLight(activity, toolbar)
+ }
+}
\ No newline at end of file
diff --git a/libs/interactions/src/main/java/com/instructure/interactions/router/RouteType.kt b/libs/interactions/src/main/java/com/instructure/interactions/router/RouteType.kt
index f5cef16fee..c8438e3097 100644
--- a/libs/interactions/src/main/java/com/instructure/interactions/router/RouteType.kt
+++ b/libs/interactions/src/main/java/com/instructure/interactions/router/RouteType.kt
@@ -2,5 +2,5 @@ package com.instructure.interactions.router
/* Where the placement of the fragment belongs. Typically applied from the RouteMatcher. */
enum class RouteType {
- MASTER_DETAIL, MEDIA, FULLSCREEN, WEBVIEW, DIALOG
+ MEDIA, FULLSCREEN
}
\ No newline at end of file
diff --git a/libs/pandautils/src/main/java/com/instructure/pandautils/features/notification/preferences/EmailNotificationPreferencesFragment.kt b/libs/pandautils/src/main/java/com/instructure/pandautils/features/notification/preferences/EmailNotificationPreferencesFragment.kt
index 71b0c8cff1..cd5dd75ef6 100644
--- a/libs/pandautils/src/main/java/com/instructure/pandautils/features/notification/preferences/EmailNotificationPreferencesFragment.kt
+++ b/libs/pandautils/src/main/java/com/instructure/pandautils/features/notification/preferences/EmailNotificationPreferencesFragment.kt
@@ -28,13 +28,17 @@ import com.google.android.material.snackbar.Snackbar
import com.instructure.canvasapi2.managers.NotificationPreferencesFrequency
import com.instructure.pandautils.R
import com.instructure.pandautils.databinding.FragmentNotificationPreferencesBinding
-import com.instructure.pandautils.utils.ViewStyler
-import com.instructure.pandautils.utils.setupAsBackButton
+import com.instructure.pandautils.utils.ToolbarSetupBehavior
import dagger.hilt.android.AndroidEntryPoint
+import kotlinx.android.synthetic.main.fragment_notification_preferences.*
+import javax.inject.Inject
@AndroidEntryPoint
class EmailNotificationPreferencesFragment : Fragment() {
+ @Inject
+ lateinit var toolbarSetupBehavior: ToolbarSetupBehavior
+
private val viewModel: EmailNotificationPreferencesViewModel by viewModels()
private lateinit var binding: FragmentNotificationPreferencesBinding
@@ -50,8 +54,7 @@ class EmailNotificationPreferencesFragment : Fragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
- setupToolbar()
-
+ toolbarSetupBehavior.setupToolbar(toolbar)
viewModel.events.observe(viewLifecycleOwner) { event ->
event.getContentIfNotHandled()?.let {
handleAction(it)
@@ -71,7 +74,7 @@ class EmailNotificationPreferencesFragment : Fragment() {
val selectedIndex = NotificationPreferencesFrequency.values().indexOf(selectedFrequency)
AlertDialog.Builder(requireContext(), R.style.AccentDialogTheme)
.setTitle(R.string.selectFrequency)
- .setSingleChoiceItems(items, selectedIndex, { dialog, index -> frequencySelected(dialog, index, categoryName)})
+ .setSingleChoiceItems(items, selectedIndex) { dialog, index -> frequencySelected(dialog, index, categoryName) }
.setNegativeButton(R.string.sortByDialogCancel) { dialog, _ -> dialog.dismiss() }
.show()
}
@@ -82,11 +85,6 @@ class EmailNotificationPreferencesFragment : Fragment() {
dialog.dismiss()
}
- private fun setupToolbar() {
- binding.toolbar.setupAsBackButton { requireActivity().onBackPressed() }
- ViewStyler.themeToolbarLight(requireActivity(), binding.toolbar)
- }
-
companion object {
fun newInstance() = EmailNotificationPreferencesFragment()
}
diff --git a/libs/pandautils/src/main/java/com/instructure/pandautils/features/notification/preferences/PushNotificationPreferencesFragment.kt b/libs/pandautils/src/main/java/com/instructure/pandautils/features/notification/preferences/PushNotificationPreferencesFragment.kt
index 5a680066ae..7bc0a1a73e 100644
--- a/libs/pandautils/src/main/java/com/instructure/pandautils/features/notification/preferences/PushNotificationPreferencesFragment.kt
+++ b/libs/pandautils/src/main/java/com/instructure/pandautils/features/notification/preferences/PushNotificationPreferencesFragment.kt
@@ -28,16 +28,19 @@ import com.instructure.pandautils.R
import com.instructure.pandautils.analytics.SCREEN_VIEW_NOTIFICATION_PREFERENCES
import com.instructure.pandautils.analytics.ScreenView
import com.instructure.pandautils.databinding.FragmentNotificationPreferencesBinding
-import com.instructure.pandautils.utils.ViewStyler
-import com.instructure.pandautils.utils.setupAsBackButton
+import com.instructure.pandautils.utils.ToolbarSetupBehavior
import dagger.hilt.android.AndroidEntryPoint
import kotlinx.android.synthetic.main.fragment_notification_preferences.*
+import javax.inject.Inject
@ScreenView(SCREEN_VIEW_NOTIFICATION_PREFERENCES)
@PageView(url = "profile/communication")
@AndroidEntryPoint
class PushNotificationPreferencesFragment : Fragment() {
+ @Inject
+ lateinit var toolbarSetupBehavior: ToolbarSetupBehavior
+
private val viewModel: PushNotificationPreferencesViewModel by viewModels()
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
@@ -51,8 +54,7 @@ class PushNotificationPreferencesFragment : Fragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
- setupToolbar()
-
+ toolbarSetupBehavior.setupToolbar(toolbar)
viewModel.events.observe(viewLifecycleOwner) { event ->
event.getContentIfNotHandled()?.let {
handleAction(it)
@@ -66,11 +68,6 @@ class PushNotificationPreferencesFragment : Fragment() {
}
}
- private fun setupToolbar() {
- toolbar.setupAsBackButton { requireActivity().onBackPressed() }
- ViewStyler.themeToolbarLight(requireActivity(), toolbar)
- }
-
companion object {
fun newInstance() = PushNotificationPreferencesFragment()
}
diff --git a/libs/pandautils/src/main/java/com/instructure/pandautils/features/themeselector/ThemeSelectorBottomSheet.kt b/libs/pandautils/src/main/java/com/instructure/pandautils/features/themeselector/ThemeSelectorBottomSheet.kt
index b4f46cfcbb..727a88af0a 100644
--- a/libs/pandautils/src/main/java/com/instructure/pandautils/features/themeselector/ThemeSelectorBottomSheet.kt
+++ b/libs/pandautils/src/main/java/com/instructure/pandautils/features/themeselector/ThemeSelectorBottomSheet.kt
@@ -16,14 +16,14 @@
*/
package com.instructure.pandautils.features.themeselector
-import android.content.DialogInterface
import android.os.Bundle
-import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.appcompat.app.AppCompatDelegate
import androidx.core.widget.CompoundButtonCompat
+import com.google.android.material.bottomsheet.BottomSheetBehavior
+import com.google.android.material.bottomsheet.BottomSheetDialog
import com.google.android.material.bottomsheet.BottomSheetDialogFragment
import com.instructure.pandautils.R
import com.instructure.pandautils.utils.AppTheme
@@ -56,6 +56,8 @@ class ThemeSelectorBottomSheet : BottomSheetDialogFragment() {
}
setAppTheme(appTheme)
}
+
+ (dialog as? BottomSheetDialog)?.behavior?.state = BottomSheetBehavior.STATE_EXPANDED
}
private fun setAppTheme(appTheme: AppTheme) {
diff --git a/libs/pandautils/src/main/java/com/instructure/pandautils/fragments/RemoteConfigParamsFragment.kt b/libs/pandautils/src/main/java/com/instructure/pandautils/fragments/RemoteConfigParamsFragment.kt
index 4c8020a1d4..a3cdcb18d2 100644
--- a/libs/pandautils/src/main/java/com/instructure/pandautils/fragments/RemoteConfigParamsFragment.kt
+++ b/libs/pandautils/src/main/java/com/instructure/pandautils/fragments/RemoteConfigParamsFragment.kt
@@ -23,28 +23,31 @@ import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
-import android.widget.EditText
-import android.widget.TextView
import androidx.fragment.app.Fragment
import androidx.recyclerview.widget.RecyclerView
import com.instructure.canvasapi2.utils.RemoteConfigParam
import com.instructure.canvasapi2.utils.RemoteConfigPrefs
import com.instructure.pandautils.R
-import com.instructure.pandautils.utils.onTextChanged
-import com.instructure.pandautils.utils.setupAsBackButton
+import com.instructure.pandautils.utils.ToolbarSetupBehavior
+import dagger.hilt.android.AndroidEntryPoint
import kotlinx.android.synthetic.main.adapter_remote_config_param.view.*
import kotlinx.android.synthetic.main.fragment_remote_config_params.*
+import javax.inject.Inject
+@AndroidEntryPoint
class RemoteConfigParamsFragment : Fragment() {
+ @Inject
+ lateinit var toolbarSetupBehavior: ToolbarSetupBehavior
+
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
return inflater.inflate(R.layout.fragment_remote_config_params, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
+ toolbarSetupBehavior.setupToolbar(toolbar)
recyclerView.adapter = RemoteConfigParamAdapter()
- toolbar.setupAsBackButton(this)
}
}
@@ -91,6 +94,4 @@ private class RemoteConfigParamAdapter : RecyclerView.Adapter.
+ *
+ */
+
+package com.instructure.pandautils.utils
+
+import androidx.appcompat.widget.Toolbar
+
+interface ToolbarSetupBehavior {
+ fun setupToolbar(toolbar: Toolbar)
+}
\ No newline at end of file
diff --git a/libs/pandautils/src/main/res/layout/bottom_sheet_theme_selector.xml b/libs/pandautils/src/main/res/layout/bottom_sheet_theme_selector.xml
index 4204d580a9..e37f2861b3 100644
--- a/libs/pandautils/src/main/res/layout/bottom_sheet_theme_selector.xml
+++ b/libs/pandautils/src/main/res/layout/bottom_sheet_theme_selector.xml
@@ -14,128 +14,133 @@
~ along with this program. If not, see .
~
-->
-
+ android:layout_height="match_parent">
-
-
-
-
-
+ android:background="@drawable/bg_bottom_sheet">
-
+
-
+
-
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintHorizontal_bias="0.0"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toBottomOf="@id/titleText" />
-
+ android:layout_marginStart="16dp"
+ android:layout_marginEnd="16dp"
+ android:text="@string/themeSelectorChangeLater"
+ android:textColor="@color/textDark"
+ android:textSize="14sp"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintHorizontal_bias="0.0"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toBottomOf="@id/descriptionText" />
-
+ android:layout_marginStart="16dp"
+ android:layout_marginTop="16dp"
+ android:layout_marginEnd="16dp"
+ android:orientation="vertical"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toBottomOf="@id/changeLateText">
-
+
-
+
+
+
+
+
+
+
+
+
-
\ No newline at end of file
+
From 1a574e8ac4efb203df7e2664e0132eb14940f335 Mon Sep 17 00:00:00 2001
From: Tamas Kozmer <72397075+tamaskozmer@users.noreply.github.com>
Date: Wed, 12 Oct 2022 12:13:26 +0200
Subject: [PATCH 25/73] Update vault ref (#1745)
* Updated vault ref
* Updated vault ref
---
android-vault | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/android-vault b/android-vault
index ceed6c1136..d5fb6ca81e 160000
--- a/android-vault
+++ b/android-vault
@@ -1 +1 @@
-Subproject commit ceed6c1136054f1aa424382d37185857acf19d3d
+Subproject commit d5fb6ca81e4bdc1a8dc4f798d30a48b8e995f706
From cbbe173a188b1de09a2c208e781bb9e6b03e0de8 Mon Sep 17 00:00:00 2001
From: Kristof Nemere <109959688+kristofnemere@users.noreply.github.com>
Date: Thu, 13 Oct 2022 14:22:34 +0200
Subject: [PATCH 26/73] [MBL-16311][Student][Teacher] Toolbar theme fixes
refs: MBL-16311
affects: Student, Teacher
release note: Toolbar theme fixes.
* Fixed toolbar theme
* fixed comment
---
.../features/files/search/FileSearchFragment.kt | 15 ++++++++++++++-
.../fragment/AccountPreferencesFragment.kt | 3 +--
.../fragment/ApplicationSettingsFragment.kt | 2 +-
.../student/fragment/BookmarksFragment.kt | 2 +-
.../student/fragment/FeatureFlagsFragment.kt | 8 +++++++-
.../student/fragment/FileListFragment.kt | 4 ++--
.../student/fragment/ProfileSettingsFragment.kt | 2 +-
.../pairobserver/ui/PairObserverFragment.kt | 7 +++++++
.../settings/pairobserver/ui/PairObserverView.kt | 7 ++-----
.../student/util/StudentToolbarSetupBehavior.kt | 3 ++-
.../src/main/res/layout/fragment_file_search.xml | 1 +
.../features/files/search/FileSearchFragment.kt | 14 +++++++++++++-
.../teacher/fragments/FeatureFlagsFragment.kt | 7 +++++++
.../teacher/fragments/ProfileFragment.kt | 2 +-
.../teacher/fragments/SettingsFragment.kt | 10 +++++++---
.../teacher/utils/TeacherToolbarSetupBehavior.kt | 3 ++-
.../src/main/res/layout/fragment_file_search.xml | 1 +
.../instructure/pandautils/utils/Extensions.kt | 4 ++++
.../layout/fragment_notification_preferences.xml | 1 -
19 files changed, 74 insertions(+), 22 deletions(-)
diff --git a/apps/student/src/main/java/com/instructure/student/features/files/search/FileSearchFragment.kt b/apps/student/src/main/java/com/instructure/student/features/files/search/FileSearchFragment.kt
index c7d077f074..26fcfe8451 100644
--- a/apps/student/src/main/java/com/instructure/student/features/files/search/FileSearchFragment.kt
+++ b/apps/student/src/main/java/com/instructure/student/features/files/search/FileSearchFragment.kt
@@ -20,6 +20,7 @@ import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
+import androidx.core.graphics.ColorUtils
import androidx.recyclerview.widget.LinearLayoutManager
import com.instructure.canvasapi2.models.CanvasContext
import com.instructure.canvasapi2.models.FileFolder
@@ -33,6 +34,7 @@ import com.instructure.pandautils.utils.*
import com.instructure.student.R
import com.instructure.student.fragment.ParentFragment
import kotlinx.android.synthetic.main.fragment_file_search.*
+import com.instructure.pandautils.utils.ColorUtils as PandaColorUtils
@ScreenView(SCREEN_VIEW_FILE_SEARCH)
class FileSearchFragment : ParentFragment(), FileSearchView {
@@ -68,7 +70,7 @@ class FileSearchFragment : ParentFragment(), FileSearchView {
}
private fun setupViews() {
- ViewStyler.themeStatusBar(requireActivity())
+ themeSearchBar()
// Set up empty state
emptyPandaView.getEmptyViewImage()?.setImageResource(R.drawable.ic_panda_nofiles)
@@ -96,6 +98,17 @@ class FileSearchFragment : ParentFragment(), FileSearchView {
queryInput.postDelayed({ queryInput.showKeyboard() }, 500)
}
+ private fun themeSearchBar() {
+ val primaryColor = ColorKeeper.getOrGenerateColor(canvasContext)
+ val primaryTextColor = if (canvasContext.isUser) ThemePrefs.primaryTextColor else requireContext().getColor(R.color.white)
+ ViewStyler.setStatusBarDark(requireActivity(), primaryColor)
+ searchHeader.setBackgroundColor(primaryColor)
+ queryInput.setTextColor(primaryTextColor)
+ queryInput.setHintTextColor(ColorUtils.setAlphaComponent(primaryTextColor, 0x66))
+ PandaColorUtils.colorIt(primaryTextColor, backButton)
+ PandaColorUtils.colorIt(primaryTextColor, clearButton)
+ }
+
override fun checkIfEmpty() {
emptyPandaView.setTitleText(getString(R.string.noFilesFound))
emptyPandaView.setMessageText(getString(R.string.noItemsMatchingQuery, searchAdapter.searchQuery))
diff --git a/apps/student/src/main/java/com/instructure/student/fragment/AccountPreferencesFragment.kt b/apps/student/src/main/java/com/instructure/student/fragment/AccountPreferencesFragment.kt
index 15e0d84c0d..83996dbeeb 100644
--- a/apps/student/src/main/java/com/instructure/student/fragment/AccountPreferencesFragment.kt
+++ b/apps/student/src/main/java/com/instructure/student/fragment/AccountPreferencesFragment.kt
@@ -16,7 +16,6 @@
*/
package com.instructure.student.fragment
-import android.graphics.Color
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
@@ -73,7 +72,7 @@ class AccountPreferencesFragment : ParentFragment() {
override fun applyTheme() {
toolbar.title = title()
toolbar.setupAsBackButton(this)
- ViewStyler.themeToolbarLight(requireActivity(), toolbar)
+ ViewStyler.themeToolbarColored(requireActivity(), toolbar, ThemePrefs.primaryColor, ThemePrefs.primaryTextColor)
}
private fun setupViews() {
diff --git a/apps/student/src/main/java/com/instructure/student/fragment/ApplicationSettingsFragment.kt b/apps/student/src/main/java/com/instructure/student/fragment/ApplicationSettingsFragment.kt
index 8279d05085..ba69e1b839 100644
--- a/apps/student/src/main/java/com/instructure/student/fragment/ApplicationSettingsFragment.kt
+++ b/apps/student/src/main/java/com/instructure/student/fragment/ApplicationSettingsFragment.kt
@@ -58,7 +58,7 @@ class ApplicationSettingsFragment : ParentFragment() {
override fun applyTheme() {
toolbar.setupAsBackButton(this)
- ViewStyler.themeToolbarLight(requireActivity(), toolbar)
+ ViewStyler.themeToolbarColored(requireActivity(), toolbar, ThemePrefs.primaryColor, ThemePrefs.primaryTextColor)
}
@SuppressLint("SetTextI18n")
diff --git a/apps/student/src/main/java/com/instructure/student/fragment/BookmarksFragment.kt b/apps/student/src/main/java/com/instructure/student/fragment/BookmarksFragment.kt
index 3d63be282d..a1607dde86 100644
--- a/apps/student/src/main/java/com/instructure/student/fragment/BookmarksFragment.kt
+++ b/apps/student/src/main/java/com/instructure/student/fragment/BookmarksFragment.kt
@@ -96,7 +96,7 @@ class BookmarksFragment : ParentFragment() {
}
}
- ViewStyler.themeToolbarLight(requireActivity(), toolbar)
+ ViewStyler.themeToolbarColored(requireActivity(), toolbar, ThemePrefs.primaryColor, ThemePrefs.primaryTextColor)
}
private fun applyEmptyImage() {
diff --git a/apps/student/src/main/java/com/instructure/student/fragment/FeatureFlagsFragment.kt b/apps/student/src/main/java/com/instructure/student/fragment/FeatureFlagsFragment.kt
index e07e1111f9..e151138f48 100644
--- a/apps/student/src/main/java/com/instructure/student/fragment/FeatureFlagsFragment.kt
+++ b/apps/student/src/main/java/com/instructure/student/fragment/FeatureFlagsFragment.kt
@@ -23,6 +23,8 @@ import android.view.ViewGroup
import androidx.fragment.app.Fragment
import androidx.recyclerview.widget.RecyclerView
import com.instructure.canvasapi2.utils.FeatureFlagPref
+import com.instructure.pandautils.utils.ThemePrefs
+import com.instructure.pandautils.utils.ViewStyler
import com.instructure.pandautils.utils.setupAsBackButton
import com.instructure.student.R
import com.instructure.student.util.FeatureFlagPrefs
@@ -37,8 +39,13 @@ class FeatureFlagsFragment : Fragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
+ setupToolbar()
recyclerView.adapter = FeatureFlagAdapter()
+ }
+
+ private fun setupToolbar() {
toolbar.setupAsBackButton(this)
+ ViewStyler.themeToolbarColored(requireActivity(), toolbar, ThemePrefs.primaryColor, ThemePrefs.primaryTextColor)
}
}
@@ -64,5 +71,4 @@ private class FeatureFlagAdapter : RecyclerView.Adapter
}
}
}
-
}
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 3035098881..875a0ca333 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
@@ -239,8 +239,8 @@ class FileListFragment : ParentFragment(), Bookmarkable, FileUploadDialogParent
private fun themeToolbar() {
// We style the toolbar white for user files
if (canvasContext.type == CanvasContext.Type.USER) {
- ViewStyler.themeProgressBar(fileLoadingProgressBar, requireContext().getColor(R.color.textDarkest))
- ViewStyler.themeToolbarLight(requireActivity(), toolbar)
+ ViewStyler.themeProgressBar(fileLoadingProgressBar, ThemePrefs.primaryTextColor)
+ ViewStyler.themeToolbarColored(requireActivity(), toolbar, ThemePrefs.primaryColor, ThemePrefs.primaryTextColor)
} else {
ViewStyler.themeProgressBar(fileLoadingProgressBar, requireContext().getColor(R.color.white))
ViewStyler.themeToolbarColored(requireActivity(), toolbar, canvasContext)
diff --git a/apps/student/src/main/java/com/instructure/student/fragment/ProfileSettingsFragment.kt b/apps/student/src/main/java/com/instructure/student/fragment/ProfileSettingsFragment.kt
index f8c8973690..de8007c60e 100644
--- a/apps/student/src/main/java/com/instructure/student/fragment/ProfileSettingsFragment.kt
+++ b/apps/student/src/main/java/com/instructure/student/fragment/ProfileSettingsFragment.kt
@@ -84,7 +84,7 @@ class ProfileSettingsFragment : ParentFragment(), LoaderManager.LoaderCallbacks<
override fun applyTheme() {
toolbar.setupAsBackButton(this)
- ViewStyler.themeToolbarLight(requireActivity(), toolbar)
+ ViewStyler.themeToolbarColored(requireActivity(), toolbar, ThemePrefs.primaryColor, ThemePrefs.primaryTextColor)
}
private fun setupViews() {
diff --git a/apps/student/src/main/java/com/instructure/student/mobius/settings/pairobserver/ui/PairObserverFragment.kt b/apps/student/src/main/java/com/instructure/student/mobius/settings/pairobserver/ui/PairObserverFragment.kt
index 967f5908b8..b465f72f60 100644
--- a/apps/student/src/main/java/com/instructure/student/mobius/settings/pairobserver/ui/PairObserverFragment.kt
+++ b/apps/student/src/main/java/com/instructure/student/mobius/settings/pairobserver/ui/PairObserverFragment.kt
@@ -16,7 +16,9 @@
*/
package com.instructure.student.mobius.settings.pairobserver.ui
+import android.os.Bundle
import android.view.LayoutInflater
+import android.view.View
import android.view.ViewGroup
import com.instructure.canvasapi2.utils.ApiPrefs
import com.instructure.pandautils.analytics.SCREEN_VIEW_PAIR_OBSERVER
@@ -41,6 +43,11 @@ class PairObserverFragment : MobiusFragment) {
diff --git a/apps/student/src/main/java/com/instructure/student/util/StudentToolbarSetupBehavior.kt b/apps/student/src/main/java/com/instructure/student/util/StudentToolbarSetupBehavior.kt
index 8e18264619..83271e6472 100644
--- a/apps/student/src/main/java/com/instructure/student/util/StudentToolbarSetupBehavior.kt
+++ b/apps/student/src/main/java/com/instructure/student/util/StudentToolbarSetupBehavior.kt
@@ -19,6 +19,7 @@ package com.instructure.student.util
import android.app.Activity
import androidx.appcompat.widget.Toolbar
+import com.instructure.pandautils.utils.ThemePrefs
import com.instructure.pandautils.utils.ToolbarSetupBehavior
import com.instructure.pandautils.utils.ViewStyler
import com.instructure.pandautils.utils.setupAsBackButton
@@ -26,6 +27,6 @@ import com.instructure.pandautils.utils.setupAsBackButton
class StudentToolbarSetupBehavior(val activity: Activity) : ToolbarSetupBehavior {
override fun setupToolbar(toolbar: Toolbar) {
toolbar.setupAsBackButton { activity.onBackPressed() }
- ViewStyler.themeToolbarLight(activity, toolbar)
+ ViewStyler.themeToolbarColored(activity, toolbar, ThemePrefs.primaryColor, ThemePrefs.primaryTextColor)
}
}
\ No newline at end of file
diff --git a/apps/student/src/main/res/layout/fragment_file_search.xml b/apps/student/src/main/res/layout/fragment_file_search.xml
index f4a97f9dbb..ad83e1a176 100644
--- a/apps/student/src/main/res/layout/fragment_file_search.xml
+++ b/apps/student/src/main/res/layout/fragment_file_search.xml
@@ -21,6 +21,7 @@
android:orientation="vertical">
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 0abcce06ef..c5c2a366f2 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
@@ -17,6 +17,7 @@
package com.instructure.teacher.features.files.search
import android.view.View
+import androidx.core.graphics.ColorUtils
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.instructure.canvasapi2.models.CanvasContext
@@ -31,6 +32,7 @@ import com.instructure.teacher.R
import com.instructure.teacher.holders.FileFolderViewHolder
import com.instructure.teacher.utils.viewMedia
import kotlinx.android.synthetic.main.fragment_file_search.*
+import com.instructure.pandautils.utils.ColorUtils as PandaColorUtils
@ScreenView(SCREEN_VIEW_FILE_SEARCH)
class FileSearchFragment : BaseSyncFragment<
@@ -74,7 +76,7 @@ class FileSearchFragment : BaseSyncFragment<
}
private fun setupViews() {
- ViewStyler.themeStatusBar(requireActivity())
+ themeSearchBar()
// Set up empty state
emptyPandaView.setEmptyViewImage(requireContext().getDrawableCompat(R.drawable.ic_panda_nofiles))
@@ -98,6 +100,16 @@ class FileSearchFragment : BaseSyncFragment<
}
}
+ private fun themeSearchBar() {
+ val primaryTextColor = if (canvasContext?.isUser.orDefault()) ThemePrefs.primaryTextColor else requireContext().getColor(R.color.white)
+ ViewStyler.setStatusBarDark(requireActivity(), courseColor)
+ searchHeader.setBackgroundColor(courseColor)
+ queryInput.setTextColor(primaryTextColor)
+ queryInput.setHintTextColor(ColorUtils.setAlphaComponent(primaryTextColor, 0x66))
+ PandaColorUtils.colorIt(primaryTextColor, backButton)
+ PandaColorUtils.colorIt(primaryTextColor, clearButton)
+ }
+
override fun checkIfEmpty() {
emptyPandaView.setTitleText(getString(R.string.noFilesFound))
emptyPandaView.setMessageText(getString(R.string.noItemsMatchingQuery, presenter.searchQuery))
diff --git a/apps/teacher/src/main/java/com/instructure/teacher/fragments/FeatureFlagsFragment.kt b/apps/teacher/src/main/java/com/instructure/teacher/fragments/FeatureFlagsFragment.kt
index 75159136f0..164f275ece 100644
--- a/apps/teacher/src/main/java/com/instructure/teacher/fragments/FeatureFlagsFragment.kt
+++ b/apps/teacher/src/main/java/com/instructure/teacher/fragments/FeatureFlagsFragment.kt
@@ -23,6 +23,8 @@ import android.view.ViewGroup
import androidx.fragment.app.Fragment
import androidx.recyclerview.widget.RecyclerView
import com.instructure.canvasapi2.utils.FeatureFlagPref
+import com.instructure.pandautils.utils.ThemePrefs
+import com.instructure.pandautils.utils.ViewStyler
import com.instructure.pandautils.utils.isTablet
import com.instructure.pandautils.utils.setupAsBackButton
import com.instructure.teacher.R
@@ -38,8 +40,13 @@ class FeatureFlagsFragment : Fragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
+ setupToolbar()
recyclerView.adapter = FeatureFlagAdapter()
+ }
+
+ private fun setupToolbar() {
if (!isTablet) toolbar.setupAsBackButton(this)
+ ViewStyler.themeToolbarColored(requireActivity(), toolbar, ThemePrefs.primaryColor, ThemePrefs.primaryTextColor)
}
}
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 dedf681f8f..cdb0e8439c 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
@@ -57,7 +57,7 @@ class ProfileFragment : BaseFragment() {
private fun setupToolbar() {
toolbar.setupMenu(R.menu.menu_settings_edit, menuItemCallback)
- toolbar.setupBackButtonAsBackPressedOnly(this)
+ if (!isTablet) toolbar.setupBackButtonAsBackPressedOnly(this)
titleTextView.adoptToolbarStyle(toolbar)
ViewStyler.themeToolbarColored(requireActivity(), toolbar, ThemePrefs.primaryColor, ThemePrefs.primaryTextColor)
toolbar.requestAccessibilityFocus()
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 6ccc99ba62..4d2d4c6efd 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
@@ -46,7 +46,12 @@ class SettingsFragment : BasePresenterFragment
diff --git a/libs/pandautils/src/main/java/com/instructure/pandautils/utils/Extensions.kt b/libs/pandautils/src/main/java/com/instructure/pandautils/utils/Extensions.kt
index e38481a2a1..0f4689f547 100644
--- a/libs/pandautils/src/main/java/com/instructure/pandautils/utils/Extensions.kt
+++ b/libs/pandautils/src/main/java/com/instructure/pandautils/utils/Extensions.kt
@@ -44,3 +44,7 @@ fun Long?.orDefault(default: Long = 0): Long {
fun Int?.orDefault(default: Int = 0): Int {
return this ?: default
}
+
+fun Boolean?.orDefault(default: Boolean = false): Boolean {
+ return this ?: default
+}
diff --git a/libs/pandautils/src/main/res/layout/fragment_notification_preferences.xml b/libs/pandautils/src/main/res/layout/fragment_notification_preferences.xml
index b2937441c4..63d250b4c7 100644
--- a/libs/pandautils/src/main/res/layout/fragment_notification_preferences.xml
+++ b/libs/pandautils/src/main/res/layout/fragment_notification_preferences.xml
@@ -48,7 +48,6 @@
app:popupTheme="@style/ToolBarPopupStyle"
app:theme="@style/ToolBarStyle"
app:title="@{title}"
- app:titleTextColor="@color/textDarkest"
tools:ignore="UnusedAttribute" />
Date: Thu, 13 Oct 2022 15:53:16 +0200
Subject: [PATCH 27/73] [MBL-16263][Teacher] Count student view dummy user on
assignment details page
refs: MBL-16263
affects: Teacher
release note: Added Test Student indicator.
* Indicating assignment details user list fake student
* Indicating fake student
---
.../holders/GradeableStudentSubmissionViewHolder.kt | 7 ++++---
.../presenters/AssignmentSubmissionListPresenter.kt | 6 ++++--
.../layout/adapter_gradeable_student_submission.xml | 13 ++++++++++++-
.../java/com/instructure/canvasapi2/models/User.kt | 4 ++--
libs/pandares/src/main/res/values/strings.xml | 1 +
5 files changed, 23 insertions(+), 8 deletions(-)
diff --git a/apps/teacher/src/main/java/com/instructure/teacher/holders/GradeableStudentSubmissionViewHolder.kt b/apps/teacher/src/main/java/com/instructure/teacher/holders/GradeableStudentSubmissionViewHolder.kt
index c3b9068a2c..4481d3e985 100644
--- a/apps/teacher/src/main/java/com/instructure/teacher/holders/GradeableStudentSubmissionViewHolder.kt
+++ b/apps/teacher/src/main/java/com/instructure/teacher/holders/GradeableStudentSubmissionViewHolder.kt
@@ -39,7 +39,7 @@ import com.instructure.teacher.utils.getResForSubmission
import com.instructure.teacher.utils.iconRes
import com.instructure.teacher.utils.setAnonymousAvatar
import kotlinx.android.synthetic.main.adapter_gradeable_student_submission.view.*
-import java.util.Locale
+import java.util.*
class GradeableStudentSubmissionViewHolder(view: View) : RecyclerView.ViewHolder(view) {
@@ -55,14 +55,14 @@ class GradeableStudentSubmissionViewHolder(view: View) : RecyclerView.ViewHolder
callback: (GradeableStudentSubmission) -> Unit
) = with(itemView) {
// Set item a11y action to "view submission details"
- setAccessibilityDelegate(object : View.AccessibilityDelegate() {
+ accessibilityDelegate = object : View.AccessibilityDelegate() {
override fun onInitializeAccessibilityNodeInfo(v: View, info: AccessibilityNodeInfo) {
super.onInitializeAccessibilityNodeInfo(v, info)
val description = context.getString(R.string.a11y_viewSubmissionAction)
val customClick = AccessibilityNodeInfo.AccessibilityAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_CLICK.id, description)
info.addAction(customClick)
}
- })
+ }
hiddenIcon.setGone()
val assignee = gradeableStudentSubmission.assignee
@@ -74,6 +74,7 @@ class GradeableStudentSubmissionViewHolder(view: View) : RecyclerView.ViewHolder
assignee is StudentAssignee -> {
ProfileUtils.loadAvatarForUser(studentAvatar, assignee.student.name, assignee.student.avatarUrl)
studentName.text = Pronouns.span(assignee.student.name, assignee.student.pronouns)
+ testStudentDescription.setVisible(assignee.student.isFakeStudent)
studentAvatar.setupAvatarA11y(assignee.name)
studentAvatar.onClick {
val bundle = StudentContextFragment.makeBundle(assignee.id, courseId)
diff --git a/apps/teacher/src/main/java/com/instructure/teacher/presenters/AssignmentSubmissionListPresenter.kt b/apps/teacher/src/main/java/com/instructure/teacher/presenters/AssignmentSubmissionListPresenter.kt
index 0bba905023..3669714b44 100644
--- a/apps/teacher/src/main/java/com/instructure/teacher/presenters/AssignmentSubmissionListPresenter.kt
+++ b/apps/teacher/src/main/java/com/instructure/teacher/presenters/AssignmentSubmissionListPresenter.kt
@@ -30,7 +30,6 @@ import com.instructure.teacher.viewinterface.AssignmentSubmissionListView
import instructure.androidblueprint.SyncPresenter
import kotlinx.coroutines.Job
import java.util.*
-import kotlin.collections.ArrayList
class AssignmentSubmissionListPresenter(val mAssignment: Assignment, private var mFilter: SubmissionListFilter) : SyncPresenter(GradeableStudentSubmission::class.java) {
@@ -79,7 +78,10 @@ class AssignmentSubmissionListPresenter(val mAssignment: Assignment, private var
// Students need the enrollment info
var user = enrollmentMap[it.id]?.user
// Users can be enrolled in multiple sections, so we need to get all of them
- user = user?.copy(enrollments = user.enrollments + enrollments.filter { it.userId == user?.id })
+ user = user?.copy(
+ enrollments = user.enrollments + enrollments.filter { enrollment -> enrollment.userId == user?.id },
+ isFakeStudent = it.isFakeStudent
+ )
// Need to null out the user object to prevent infinite parcels
user?.enrollments?.forEach { it.user = null }
user
diff --git a/apps/teacher/src/main/res/layout/adapter_gradeable_student_submission.xml b/apps/teacher/src/main/res/layout/adapter_gradeable_student_submission.xml
index 14db25b15b..b99769d13e 100644
--- a/apps/teacher/src/main/res/layout/adapter_gradeable_student_submission.xml
+++ b/apps/teacher/src/main/res/layout/adapter_gradeable_student_submission.xml
@@ -28,6 +28,7 @@
android:id="@+id/studentAvatar"
android:layout_width="48dp"
android:layout_height="48dp"
+ android:layout_centerVertical="true"
android:padding="4dp"
tools:src="@mipmap/ic_launcher" />
@@ -74,12 +75,22 @@
android:textSize="16sp"
tools:text="Student Name" />
+
+
Missing
Graded
Upcoming
+ Created by Student View
From 4b92f5b1ee17483e1d376b2e96ffdf74d4604fb6 Mon Sep 17 00:00:00 2001
From: Kristof Deak <92309696+kdeakinstructure@users.noreply.github.com>
Date: Fri, 14 Oct 2022 15:31:02 +0200
Subject: [PATCH 28/73] [MBL-16211][Teacher] - Extend InboxE2E with Star,
Delete, Archive, unread features #1751
* Extend Teacher Inbox E2E test. - Star, Delete, Archive, unread features.
* Fix log typo.
refs: MBL-16211
affects: Teacher
release note: none
---
.../teacher/ui/e2e/InboxE2ETest.kt | 67 ++++++++++++++++++-
.../teacher/ui/pages/InboxMessagePage.kt | 25 +++++++
.../instructure/teacher/ui/pages/InboxPage.kt | 19 ++++--
3 files changed, 106 insertions(+), 5 deletions(-)
diff --git a/apps/teacher/src/androidTest/java/com/instructure/teacher/ui/e2e/InboxE2ETest.kt b/apps/teacher/src/androidTest/java/com/instructure/teacher/ui/e2e/InboxE2ETest.kt
index 3cdd76d8a9..e55137ffc5 100644
--- a/apps/teacher/src/androidTest/java/com/instructure/teacher/ui/e2e/InboxE2ETest.kt
+++ b/apps/teacher/src/androidTest/java/com/instructure/teacher/ui/e2e/InboxE2ETest.kt
@@ -3,6 +3,7 @@ package com.instructure.teacher.ui.e2e
import android.util.Log
import androidx.test.espresso.Espresso
import com.instructure.canvas.espresso.E2E
+import com.instructure.canvas.espresso.refresh
import com.instructure.dataseeding.api.ConversationsApi
import com.instructure.dataseeding.api.GroupsApi
import com.instructure.dataseeding.model.CanvasUserApiModel
@@ -57,9 +58,10 @@ class InboxE2ETest : TeacherTest() {
recipients = listOf(teacher.id.toString())
)
- Log.d(STEP_TAG,"Refresh the page. Assert that the previously seeded Inbox conversation is displayed.")
+ Log.d(STEP_TAG,"Refresh the page. Assert that the previously seeded Inbox conversation is displayed. Assert that the message is unread yet.")
inboxPage.refresh()
inboxPage.assertHasConversation()
+ inboxPage.assertThereIsAnUnreadMessage(true)
val replyMessage = "Hello there"
Log.d(STEP_TAG,"Click on the conversation. Write a reply with the message: '$replyMessage'.")
@@ -73,6 +75,9 @@ class InboxE2ETest : TeacherTest() {
Log.d(STEP_TAG,"Navigate back to Inbox Page.")
Espresso.pressBack()
+ Log.d(STEP_TAG, "Assert that the message is not unread anymore.")
+ inboxPage.assertThereIsAnUnreadMessage(false)
+
Log.d(STEP_TAG,"Add a new conversation message manually via UI. Click on 'New Message' ('+') button.")
inboxPage.clickAddMessageFAB()
@@ -108,6 +113,66 @@ class InboxE2ETest : TeacherTest() {
Espresso.pressBack()
inboxPage.assertHasConversation()
+ Log.d(STEP_TAG,"Filter the Inbox by selecting 'All' category from the spinner on Inbox Page.")
+ inboxPage.filterInbox("All")
+
+ Log.d(STEP_TAG,"Refresh the page. Assert that the previously seeded Inbox conversation is displayed.")
+ inboxPage.refresh()
+ inboxPage.assertHasConversation()
+
+ Log.d(STEP_TAG,"Click on the conversation. Write a reply with the message: '$replyMessage'.")
+ inboxPage.clickConversation(seedConversation[0])
+
+ Log.d(STEP_TAG, "Star the conversation and navigate back to Inbox Page.")
+ inboxMessagePage.clickOnStarConversation()
+ Espresso.pressBack()
+
+ Log.d(STEP_TAG, "Assert that the '${seedConversation[0]}' conversation has been starred.")
+ inboxPage.assertConversationStarred(student1.name + ", " + student2.name)
+
+ Log.d(STEP_TAG,"Click on the conversation. Write a reply with the message: '$replyMessage'.")
+ inboxPage.clickConversation(seedConversation[0])
+
+ Log.d(STEP_TAG, "Archive the '${seedConversation[0]}' conversation and assert that it has disappeared from the list," +
+ "because archived conversations does not displayed within the 'All' section.")
+ inboxMessagePage.openOptionMenuFor("Archive")
+ dashboardPage.assertPageObjects()
+ inboxPage.assertInboxEmpty()
+
+ Log.d(STEP_TAG,"Filter the Inbox by selecting 'Archived' category from the spinner on Inbox Page." +
+ "Assert that the previously archived conversation is displayed.")
+ inboxPage.filterInbox("Archived")
+ inboxPage.assertHasConversation()
+
+ Log.d(STEP_TAG,"Filter the Inbox by selecting 'Starred' category from the spinner on Inbox Page." +
+ "Assert that the '${seedConversation[0]}' conversation is displayed because it's still starred.")
+ inboxPage.filterInbox("Starred")
+ inboxPage.assertHasConversation()
+
+ Log.d(STEP_TAG,"Click on the conversation.")
+ inboxPage.clickConversation(seedConversation[0])
+
+ Log.d(STEP_TAG, "Remove star from the conversation and navigate back to Inbox Page.")
+ inboxMessagePage.clickOnStarConversation()
+ Espresso.pressBack()
+
+ Log.d(STEP_TAG, "Assert that the '${seedConversation[0]}' conversation is disappeared because it's not starred yet.")
+ dashboardPage.assertPageObjects()
+ inboxPage.assertInboxEmpty()
+
+ Log.d(STEP_TAG,"Filter the Inbox by selecting 'Archived' category from the spinner on Inbox Page. Assert that the '${seedConversation[0]}' conversation is displayed.")
+ inboxPage.filterInbox("Archived")
+ inboxPage.assertHasConversation()
+
+ Log.d(STEP_TAG,"Click on the conversation.")
+ inboxPage.clickConversation(seedConversation[0])
+
+ Log.d(STEP_TAG, "Delete the '${seedConversation[0]}' conversation and assert that it has disappeared from the list.")
+ inboxMessagePage.deleteConversation()
+ refresh() //This is needed because Archive list (and the other 'specific' ones) does not refreshing automatically like the 'All'.
+ inboxPage.assertPageObjects()
+ inboxPage.assertInboxEmpty()
+
}
private fun addNewMessage(course: CourseApiModel, userRecipientList: MutableList) {
diff --git a/apps/teacher/src/androidTest/java/com/instructure/teacher/ui/pages/InboxMessagePage.kt b/apps/teacher/src/androidTest/java/com/instructure/teacher/ui/pages/InboxMessagePage.kt
index 3a5f557094..80e488e201 100644
--- a/apps/teacher/src/androidTest/java/com/instructure/teacher/ui/pages/InboxMessagePage.kt
+++ b/apps/teacher/src/androidTest/java/com/instructure/teacher/ui/pages/InboxMessagePage.kt
@@ -16,9 +16,18 @@
*/
package com.instructure.teacher.ui.pages
+import androidx.appcompat.widget.AppCompatButton
+import androidx.test.espresso.Espresso
+import androidx.test.espresso.action.ViewActions
+import androidx.test.espresso.matcher.ViewMatchers
+import androidx.test.platform.app.InstrumentationRegistry
+import com.instructure.canvas.espresso.containsTextCaseInsensitive
import com.instructure.espresso.*
import com.instructure.espresso.page.BasePage
+import com.instructure.espresso.page.onView
+import com.instructure.espresso.page.withId
import com.instructure.teacher.R
+import org.hamcrest.Matchers
class InboxMessagePage: BasePage() {
@@ -48,4 +57,20 @@ class InboxMessagePage: BasePage() {
fun assertHasReply() {
messageRecyclerView.check(RecyclerViewItemCountAssertion(2))
}
+
+ fun clickOnStarConversation() {
+ onView(withId(R.id.starred)).click()
+ }
+
+ fun openOptionMenuFor(itemName: String) {
+ Espresso.openActionBarOverflowOrOptionsMenu(InstrumentationRegistry.getInstrumentation().getTargetContext());
+ Espresso.onView(ViewMatchers.withText(itemName))
+ .perform(ViewActions.click());
+ }
+
+ fun deleteConversation() {
+ openOptionMenuFor("Delete")
+ Espresso.onView(Matchers.allOf(ViewMatchers.isAssignableFrom(AppCompatButton::class.java),
+ containsTextCaseInsensitive("DELETE"))).click()
+ }
}
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 b4635351b0..b48511e498 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
@@ -1,14 +1,14 @@
package com.instructure.teacher.ui.pages
+import androidx.test.espresso.matcher.ViewMatchers
+import androidx.test.espresso.matcher.ViewMatchers.hasSibling
import com.instructure.canvasapi2.models.Conversation
import com.instructure.dataseeding.model.ConversationApiModel
import com.instructure.espresso.*
-import com.instructure.espresso.page.BasePage
-import com.instructure.espresso.page.onView
-import com.instructure.espresso.page.waitForViewWithText
-import com.instructure.espresso.page.withId
+import com.instructure.espresso.page.*
import com.instructure.teacher.R
import com.instructure.teacher.ui.utils.WaitForToolbarTitle
+import org.hamcrest.CoreMatchers.allOf
class InboxPage: BasePage() {
@@ -62,4 +62,15 @@ class InboxPage: BasePage() {
onView(withId(R.id.filterButton)).click()
waitForViewWithText(filterFor).click()
}
+
+ fun assertThereIsAnUnreadMessage(unread: Boolean) {
+ if(unread) onView(withId(R.id.unreadMark)).assertDisplayed()
+ else onView(withId(R.id.unreadMark) + ViewMatchers.withEffectiveVisibility(ViewMatchers.Visibility.GONE))
+ }
+
+ fun assertConversationStarred(recipients: String) {
+ onView(allOf(withId(R.id.star) + hasSibling(withId(R.id.userName) + withText(recipients))))
+ }
+
+
}
From 710514a85114b97c3bd4c4e7743f8dfd30e57fef Mon Sep 17 00:00:00 2001
From: Kristof Nemere <109959688+kristofnemere@users.noreply.github.com>
Date: Tue, 18 Oct 2022 12:53:03 +0200
Subject: [PATCH 29/73] [MBL-16307][Student][Teacher] Merge WebViewExtensions
and HtmlContentFormatter
refs: MBL-16307
affects: Student, Teacher
release note: none
* Using HtmlContentFormatter.kt in WebViewExtensions.kt, removed duplicated logic
* Moved route logic to LtiLaunchFragment.kt
* Fixed comment
---
.../fragment/AssignmentBasicFragment.kt | 15 +-
.../student/fragment/CalendarEventFragment.kt | 12 +-
.../fragment/DiscussionDetailsFragment.kt | 28 ++--
.../student/fragment/LtiLaunchFragment.kt | 9 +-
.../student/fragment/PageDetailsFragment.kt | 14 +-
.../ui/AssignmentDetailsView.kt | 15 +-
.../calendar/event/CalendarEventFragment.kt | 18 +--
.../fragments/AssignmentDetailsFragment.kt | 20 +--
.../fragments/DiscussionsDetailsFragment.kt | 32 ++---
.../teacher/fragments/LtiLaunchFragment.kt | 9 ++
.../teacher/fragments/PageDetailsFragment.kt | 14 +-
.../teacher/fragments/QuizDetailsFragment.kt | 20 +--
.../pandautils/binding/BindingAdapters.kt | 7 +-
.../pandautils/utils/HtmlContentFormatter.kt | 18 ++-
.../pandautils/utils/WebViewExtensions.kt | 133 +++---------------
15 files changed, 129 insertions(+), 235 deletions(-)
diff --git a/apps/student/src/main/java/com/instructure/student/fragment/AssignmentBasicFragment.kt b/apps/student/src/main/java/com/instructure/student/fragment/AssignmentBasicFragment.kt
index 8ba9ddc1e3..5e4c0a40f2 100644
--- a/apps/student/src/main/java/com/instructure/student/fragment/AssignmentBasicFragment.kt
+++ b/apps/student/src/main/java/com/instructure/student/fragment/AssignmentBasicFragment.kt
@@ -38,7 +38,6 @@ import kotlinx.coroutines.Job
import org.greenrobot.eventbus.EventBus
import org.greenrobot.eventbus.Subscribe
import org.greenrobot.eventbus.ThreadMode
-import java.net.URLDecoder
import java.util.*
@ScreenView(SCREEN_VIEW_ASSIGNMENT_BASIC)
@@ -137,17 +136,13 @@ class AssignmentBasicFragment : ParentFragment() {
description = "" + getString(R.string.noDescription) + "
"
}
- loadHtmlJob = assignmentWebView.loadHtmlWithIframes(requireContext(), isTablet, description.orEmpty(),
- ::loadDescriptionHtml, {
- val args = LtiLaunchFragment.makeLTIBundle(
- URLDecoder.decode(it, "utf-8"), getString(R.string.utils_externalToolTitle), true)
- RouteMatcher.route(requireContext(), Route(LtiLaunchFragment::class.java, canvasContext, args))
- }, assignment.name)
+ loadHtmlJob = assignmentWebView.loadHtmlWithIframes(requireContext(), description, {
+ assignmentWebView.loadHtml(it, assignment.name)
+ }, {
+ LtiLaunchFragment.routeLtiLaunchFragment(requireContext(), canvasContext, it)
+ })
}
- private fun loadDescriptionHtml(html: String, contentDescription: String?) {
- assignmentWebView.loadHtml(html, contentDescription)
- }
//endregion
//region Bus Events
diff --git a/apps/student/src/main/java/com/instructure/student/fragment/CalendarEventFragment.kt b/apps/student/src/main/java/com/instructure/student/fragment/CalendarEventFragment.kt
index fe139b7280..db168b27d9 100644
--- a/apps/student/src/main/java/com/instructure/student/fragment/CalendarEventFragment.kt
+++ b/apps/student/src/main/java/com/instructure/student/fragment/CalendarEventFragment.kt
@@ -48,7 +48,6 @@ import org.greenrobot.eventbus.EventBus
import org.greenrobot.eventbus.Subscribe
import org.greenrobot.eventbus.ThreadMode
import retrofit2.Response
-import java.net.URLDecoder
import java.util.*
@ScreenView(SCREEN_VIEW_CALENDAR_EVENT)
@@ -224,12 +223,11 @@ class CalendarEventFragment : ParentFragment() {
}
if (content?.isNotEmpty() == true) {
- loadHtmlJob = calendarEventWebView.loadHtmlWithIframes(requireContext(), isTablet, content,
- ::loadCalendarHtml, { url ->
- val args = LtiLaunchFragment.makeLTIBundle(
- URLDecoder.decode(url, "utf-8"), getString(R.string.utils_externalToolTitle), true)
- RouteMatcher.route(requireContext(), Route(LtiLaunchFragment::class.java, canvasContext, args))
- }, it.title)
+ loadHtmlJob = calendarEventWebView.loadHtmlWithIframes(requireContext(), content, { html ->
+ loadCalendarHtml(html, it.title)
+ }) { url ->
+ LtiLaunchFragment.routeLtiLaunchFragment(requireContext(), canvasContext, url)
+ }
}
}
}
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 71c4cbf429..d955faa56f 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
@@ -543,15 +543,6 @@ class DiscussionDetailsFragment : ParentFragment(), Bookmarkable {
//endregion
//region Loading
- private fun loadHTMLTopic(html: String, contentDescription: String?) {
- setupHeaderWebView()
- discussionTopicHeaderWebView.loadHtml(html, contentDescription, baseUrl = discussionTopicHeader.htmlUrl)
- }
-
- private fun loadHTMLReplies(html: String, contentDescription: String? = null) {
- discussionRepliesWebView.loadDataWithBaseURL(CanvasWebView.getReferrer(true), html, "text/html", "UTF-8", null)
- }
-
private fun populateDiscussionData(forceRefresh: Boolean = false, topLevelReplyPosted: Boolean = false) {
discussionsLoadingJob = tryWeave {
discussionProgressBar.setVisible()
@@ -709,21 +700,28 @@ class DiscussionDetailsFragment : ParentFragment(), Bookmarkable {
replyToDiscussionTopic.setVisible(discussionTopicHeader.permissions!!.reply)
replyToDiscussionTopic.onClick { showReplyView(discussionTopicHeader.id) }
- loadHeaderHtmlJob = discussionTopicHeaderWebView.loadHtmlWithIframes(requireContext(), isTablet,
- discussionTopicHeader.message.orEmpty(), ::loadHTMLTopic, null, discussionTopicHeader.title)
+ loadHeaderHtmlJob = discussionTopicHeaderWebView.loadHtmlWithIframes(requireContext(), discussionTopicHeader.message, {
+ loadHTMLTopic(it, discussionTopicHeader.title)
+ })
- attachmentIcon.setVisible(!discussionTopicHeader.attachments.isEmpty())
- attachmentIcon.onClick { _ ->
+ attachmentIcon.setVisible(discussionTopicHeader.attachments.isNotEmpty())
+ attachmentIcon.onClick {
viewAttachments(discussionTopicHeader.attachments)
}
}
+ private fun loadHTMLTopic(html: String, contentDescription: String?) {
+ setupHeaderWebView()
+ discussionTopicHeaderWebView.loadHtml(html, contentDescription, baseUrl = discussionTopicHeader.htmlUrl)
+ }
+
private fun loadDiscussionTopicViews(html: String) {
discussionRepliesWebView.setVisible()
discussionProgressBar.setGone()
- loadRepliesHtmlJob = discussionRepliesWebView.loadHtmlWithIframes(requireContext(), isTablet,
- html, ::loadHTMLReplies)
+ loadHeaderHtmlJob = discussionRepliesWebView.loadHtmlWithIframes(requireContext(), html, {
+ discussionRepliesWebView.loadDataWithBaseURL(CanvasWebView.getReferrer(true), html, "text/html", "UTF-8", null)
+ })
swipeRefreshLayout.isRefreshing = false
discussionTopicRepliesTitle.setVisible(discussionTopicHeader.shouldShowReplies)
diff --git a/apps/student/src/main/java/com/instructure/student/fragment/LtiLaunchFragment.kt b/apps/student/src/main/java/com/instructure/student/fragment/LtiLaunchFragment.kt
index 9e6cf56229..d2f7f0ada8 100644
--- a/apps/student/src/main/java/com/instructure/student/fragment/LtiLaunchFragment.kt
+++ b/apps/student/src/main/java/com/instructure/student/fragment/LtiLaunchFragment.kt
@@ -17,7 +17,7 @@
package com.instructure.student.fragment
-import android.app.Activity
+import android.content.Context
import android.net.Uri
import android.os.Bundle
import android.view.LayoutInflater
@@ -39,8 +39,10 @@ import com.instructure.pandautils.analytics.SCREEN_VIEW_LTI_LAUNCH
import com.instructure.pandautils.analytics.ScreenView
import com.instructure.pandautils.utils.*
import com.instructure.student.R
+import com.instructure.student.router.RouteMatcher
import kotlinx.android.synthetic.main.fragment_lti_launch.*
import kotlinx.coroutines.Job
+import java.net.URLDecoder
@ScreenView(SCREEN_VIEW_LTI_LAUNCH)
@PageView
@@ -216,5 +218,10 @@ class LtiLaunchFragment : ParentFragment() {
if (!validateRoute(route)) return null
return LtiLaunchFragment().withArgs(route.argsWithContext)
}
+
+ fun routeLtiLaunchFragment(context: Context, canvasContext: CanvasContext?, url: String) {
+ val args = makeLTIBundle(URLDecoder.decode(url, "utf-8"), context.getString(R.string.utils_externalToolTitle), true)
+ RouteMatcher.route(context, Route(LtiLaunchFragment::class.java, canvasContext, args))
+ }
}
}
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 9b6a0393d7..92e6cd204a 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
@@ -50,7 +50,6 @@ import kotlinx.android.synthetic.main.fragment_webview.*
import kotlinx.coroutines.Job
import org.greenrobot.eventbus.Subscribe
import retrofit2.Response
-import java.net.URLDecoder
import java.util.*
import java.util.regex.Pattern
@@ -203,10 +202,11 @@ class PageDetailsFragment : InternalWebviewFragment(), Bookmarkable {
val body = """""" + page.body.orEmpty()
// Load the html with the helper function to handle iframe cases
- loadHtmlJob = canvasWebView.loadHtmlWithIframes(requireContext(), isTablet, body, ::loadPageHtml, {
- val args = LtiLaunchFragment.makeLTIBundle(URLDecoder.decode(it, "utf-8"), getString(R.string.utils_externalToolTitle), true)
- RouteMatcher.route(requireContext(), Route(LtiLaunchFragment::class.java, canvasContext, args))
- }, page.title)
+ loadHtmlJob = canvasWebView.loadHtmlWithIframes(requireContext(), body, {
+ canvasWebView.loadHtml(it, page.title, baseUrl = page.htmlUrl)
+ }) {
+ LtiLaunchFragment.routeLtiLaunchFragment(requireContext(), canvasContext, it)
+ }
} else if (page.body == null || page.body?.endsWith("") == true) {
loadHtml(resources.getString(R.string.noPageFound), "text/html", "utf-8", null)
}
@@ -216,10 +216,6 @@ class PageDetailsFragment : InternalWebviewFragment(), Bookmarkable {
checkCanEdit()
}
- private fun loadPageHtml(html: String, contentDescription: String?) {
- canvasWebView.loadHtml(html, contentDescription, baseUrl = page.htmlUrl)
- }
-
/**
* DO NOT REMOVE
* This is a special case specific to some school districts and how they are handling iframes
diff --git a/apps/student/src/main/java/com/instructure/student/mobius/assignmentDetails/ui/AssignmentDetailsView.kt b/apps/student/src/main/java/com/instructure/student/mobius/assignmentDetails/ui/AssignmentDetailsView.kt
index 8fabfa4827..1e911f91c8 100644
--- a/apps/student/src/main/java/com/instructure/student/mobius/assignmentDetails/ui/AssignmentDetailsView.kt
+++ b/apps/student/src/main/java/com/instructure/student/mobius/assignmentDetails/ui/AssignmentDetailsView.kt
@@ -36,7 +36,6 @@ import com.instructure.canvasapi2.utils.ApiPrefs
import com.instructure.canvasapi2.utils.Pronouns
import com.instructure.canvasapi2.utils.exhaustive
import com.instructure.interactions.Navigation
-import com.instructure.interactions.router.Route
import com.instructure.pandautils.features.shareextension.ShareFileSubmissionTarget
import com.instructure.pandautils.utils.*
import com.instructure.pandautils.views.CanvasWebView
@@ -60,7 +59,6 @@ import kotlinx.android.synthetic.main.dialog_submission_picker.*
import kotlinx.android.synthetic.main.dialog_submission_picker_media.*
import kotlinx.android.synthetic.main.fragment_assignment_details.*
import kotlinx.coroutines.Job
-import java.net.URLDecoder
class AssignmentDetailsView(
val canvasContext: CanvasContext,
@@ -217,14 +215,11 @@ class AssignmentDetailsView(
submitButton.text = state.submitButtonText
if (state.visibilities.description) {
descriptionLabel.text = state.descriptionLabel
- loadHtmlJob = descriptionWebView.loadHtmlWithIframes(context, false, state.description,
- { html, contentDescription -> loadDescriptionHtml(html, contentDescription, state.htmlUrl) }, {
- val args = LtiLaunchFragment.makeLTIBundle(
- URLDecoder.decode(it, "utf-8"), context.getString(R.string.utils_externalToolTitle), true
- )
- RouteMatcher.route(context, Route(LtiLaunchFragment::class.java, canvasContext, args))
- }, state.assignmentName
- )
+ loadHtmlJob = descriptionWebView.loadHtmlWithIframes(context, state.description, {
+ loadDescriptionHtml(it, state.assignmentName, state.htmlUrl)
+ }) {
+ LtiLaunchFragment.routeLtiLaunchFragment(context, canvasContext, it)
+ }
}
if(state.visibilities.quizDetails) renderQuizDetails(state.quizDescriptionViewState!!)
if(state.visibilities.discussionTopicHeader) renderDiscussionTopicHeader(state.discussionHeaderViewState!!)
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 5a2917fb13..92d928f773 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
@@ -24,11 +24,13 @@ import androidx.core.content.ContextCompat
import com.instructure.canvasapi2.models.CanvasContext
import com.instructure.canvasapi2.models.ScheduleItem
import com.instructure.canvasapi2.utils.ApiPrefs
-import com.instructure.interactions.router.Route
import com.instructure.pandautils.analytics.SCREEN_VIEW_CALENDAR_EVENT
import com.instructure.pandautils.analytics.ScreenView
import com.instructure.pandautils.fragments.BaseFragment
-import com.instructure.pandautils.utils.*
+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.views.CanvasWebView
import com.instructure.teacher.R
import com.instructure.teacher.activities.InternalWebViewActivity
@@ -37,7 +39,6 @@ import com.instructure.teacher.router.RouteMatcher
import kotlinx.android.synthetic.main.fragment_calendar_event.*
import kotlinx.android.synthetic.main.fragment_syllabus.toolbar
import kotlinx.coroutines.Job
-import java.net.URLDecoder
@ScreenView(SCREEN_VIEW_CALENDAR_EVENT)
class CalendarEventFragment : BaseFragment() {
@@ -119,14 +120,15 @@ class CalendarEventFragment : BaseFragment() {
locationSubtitle.text = viewState.locationSubtitle
if (viewState.htmlContent.isNotEmpty()) {
- loadHtmlJob = calendarEventWebView.loadHtmlWithIframes(requireContext(), isTablet, viewState.htmlContent, ::loadCalendarHtml, { url ->
- val args = LtiLaunchFragment.makeBundle(canvasContext, URLDecoder.decode(url, "utf-8"), getString(R.string.utils_externalToolTitle), true)
- RouteMatcher.route(requireContext(), Route(LtiLaunchFragment::class.java, canvasContext, args))
- }, viewState.eventTitle)
+ loadHtmlJob = calendarEventWebView.loadHtmlWithIframes(requireContext(), viewState.htmlContent, {
+ loadCalendarHtml(it, viewState.eventTitle)
+ }) {
+ LtiLaunchFragment.routeLtiLaunchFragment(requireContext(), canvasContext, it)
+ }
}
}
- private fun loadCalendarHtml(html: String, contentDescription: String?) {
+ private fun loadCalendarHtml(html: String, contentDescription: String) {
calendarEventWebView?.setBackgroundColor(ContextCompat.getColor(requireContext(), R.color.backgroundLightest))
calendarEventWebView?.loadHtml(html, contentDescription, baseUrl = scheduleItem?.htmlUrl)
}
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 5e00f18f54..d486f79e50 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
@@ -15,7 +15,6 @@
*/
package com.instructure.teacher.fragments
-import android.graphics.Color
import android.os.Bundle
import android.webkit.WebChromeClient
import android.webkit.WebView
@@ -55,7 +54,6 @@ import kotlinx.coroutines.Job
import org.greenrobot.eventbus.EventBus
import org.greenrobot.eventbus.Subscribe
import org.greenrobot.eventbus.ThreadMode
-import java.net.URLDecoder
import java.util.*
@ScreenView(SCREEN_VIEW_ASSIGNMENT_DETAILS)
@@ -286,19 +284,11 @@ class AssignmentDetailsFragment : BasePresenterFragment<
descriptionWebView.setBackgroundResource(android.R.color.transparent)
// Load description
- loadHtmlJob = descriptionWebView.loadHtmlWithIframes(requireContext(), isTablet, description.orEmpty(),
- ::loadAssignmentHTML, {
- val args = LtiLaunchFragment.makeBundle(
- mCourse,
- URLDecoder.decode(it, "utf-8"),
- requireContext().getString(R.string.utils_externalToolTitle),
- true)
- RouteMatcher.route(requireContext(), Route(LtiLaunchFragment::class.java, mCourse, args))
- }, name)
- }
-
- private fun loadAssignmentHTML(html: String, contentDescription: String?) {
- descriptionWebView.loadHtml(html, contentDescription, baseUrl = mAssignment.htmlUrl)
+ loadHtmlJob = descriptionWebView.loadHtmlWithIframes(requireContext(), description, {
+ descriptionWebView.loadHtml(it, name, baseUrl = mAssignment.htmlUrl)
+ }) {
+ LtiLaunchFragment.routeLtiLaunchFragment(requireContext(), mCourse, it)
+ }
}
private fun configureSubmissionDonuts(assignment: Assignment): Unit = with(assignment) {
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 f2044f0971..780bdb9225 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
@@ -16,7 +16,6 @@
package com.instructure.teacher.fragments
import android.annotation.SuppressLint
-import android.graphics.Color
import android.graphics.Rect
import android.os.Bundle
import android.view.MenuItem
@@ -65,7 +64,6 @@ import kotlinx.coroutines.delay
import org.greenrobot.eventbus.EventBus
import org.greenrobot.eventbus.Subscribe
import org.greenrobot.eventbus.ThreadMode
-import java.net.URLDecoder
import java.util.*
@ScreenView(SCREEN_VIEW_DISCUSSION_DETAILS)
@@ -254,10 +252,11 @@ class DiscussionsDetailsFragment : BasePresenterFragment<
discussionRepliesWebView.setInvisible()
- repliesLoadHtmlJob = discussionRepliesWebView.loadHtmlWithIframes(this@DiscussionsDetailsFragment.requireContext(), isTablet, html, ::loadHTMLReplies, {
- val args = LtiLaunchFragment.makeBundle(canvasContext, URLDecoder.decode(it, "utf-8"), this@DiscussionsDetailsFragment.getString(R.string.utils_externalToolTitle), true)
- RouteMatcher.route(this@DiscussionsDetailsFragment.requireContext(), Route(LtiLaunchFragment::class.java, canvasContext, args))
- })
+ repliesLoadHtmlJob = discussionRepliesWebView.loadHtmlWithIframes(requireContext(), html, {
+ discussionRepliesWebView.loadDataWithBaseURL(CanvasWebView.getReferrer(true), html, "text/html", "utf-8", null)
+ }) {
+ LtiLaunchFragment.routeLtiLaunchFragment(requireContext(), canvasContext, it)
+ }
delay(300)
discussionsScrollView.post {
@@ -407,26 +406,15 @@ class DiscussionsDetailsFragment : BasePresenterFragment<
showReplyView(presenter.discussionTopicHeader.id)
}
- headerLoadHtmlJob = discussionTopicHeaderWebView.loadHtmlWithIframes(requireContext(), isTablet,
- discussionTopicHeader.message.orEmpty(), this::loadHTMLTopic, {
- val args = LtiLaunchFragment.makeBundle(
- canvasContext, URLDecoder.decode(it, "utf-8"), getString(R.string.utils_externalToolTitle), true)
- RouteMatcher.route(
- this@DiscussionsDetailsFragment.requireContext(),
- Route(LtiLaunchFragment::class.java, canvasContext, args))
- }, discussionTopicHeader.title)
+ headerLoadHtmlJob = discussionTopicHeaderWebView.loadHtmlWithIframes(requireContext(), discussionTopicHeader.message, {
+ discussionTopicHeaderWebView.loadHtml(it, discussionTopicHeader.title, baseUrl = mDiscussionTopicHeader.htmlUrl)
+ }) {
+ LtiLaunchFragment.routeLtiLaunchFragment(requireContext(), canvasContext, it)
+ }
discussionRepliesWebView.loadHtml("", "")
}
- private fun loadHTMLTopic(html: String, contentDescription: String?) {
- discussionTopicHeaderWebView.loadHtml(html, contentDescription, baseUrl = mDiscussionTopicHeader.htmlUrl)
- }
-
- private fun loadHTMLReplies(html: String, contentDescription: String? = null) {
- discussionRepliesWebView.loadDataWithBaseURL(CanvasWebView.getReferrer(true), html, "text/html", "utf-8", null)
- }
-
override fun onPause() {
super.onPause()
presenter.scrollPosition = discussionsScrollView.scrollY
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 9831a2c3f2..9ca4127c49 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
@@ -17,6 +17,7 @@
package com.instructure.teacher.fragments
import android.app.Activity
+import android.content.Context
import android.net.Uri
import android.os.Bundle
import android.os.Handler
@@ -29,13 +30,16 @@ import com.instructure.canvasapi2.models.Tab
import com.instructure.canvasapi2.utils.ApiPrefs
import com.instructure.canvasapi2.utils.validOrNull
import com.instructure.canvasapi2.utils.weave.weave
+import com.instructure.interactions.router.Route
import com.instructure.pandautils.analytics.SCREEN_VIEW_LTI_LAUNCH
import com.instructure.pandautils.analytics.ScreenView
import com.instructure.pandautils.fragments.BaseFragment
import com.instructure.pandautils.utils.*
import com.instructure.teacher.R
+import com.instructure.teacher.router.RouteMatcher
import kotlinx.android.synthetic.main.fragment_lti_launch.*
import kotlinx.coroutines.Job
+import java.net.URLDecoder
@ScreenView(SCREEN_VIEW_LTI_LAUNCH)
class LtiLaunchFragment : BaseFragment() {
@@ -176,5 +180,10 @@ class LtiLaunchFragment : BaseFragment() {
}
fun newInstance(args: Bundle) = LtiLaunchFragment().apply { arguments = args }
+
+ fun routeLtiLaunchFragment(context: Context, canvasContext: CanvasContext?, url: String) {
+ val args = makeBundle(canvasContext, URLDecoder.decode(url, "utf-8"), context.getString(R.string.utils_externalToolTitle), true)
+ RouteMatcher.route(context, Route(LtiLaunchFragment::class.java, canvasContext, args))
+ }
}
}
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 03953324f9..c822c621cb 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
@@ -53,7 +53,6 @@ import kotlinx.coroutines.Job
import org.greenrobot.eventbus.EventBus
import org.greenrobot.eventbus.Subscribe
import org.greenrobot.eventbus.ThreadMode
-import java.net.URLDecoder
@ScreenView(SCREEN_VIEW_PAGE_DETAILS)
class PageDetailsFragment : BasePresenterFragment<
@@ -173,17 +172,14 @@ class PageDetailsFragment : BasePresenterFragment<
override fun populatePageDetails(page: Page) {
mPage = page
- loadHtmlJob = canvasWebView.loadHtmlWithIframes(requireContext(), isTablet, page.body.orEmpty(), ::loadPageHtml, {
- val args = LtiLaunchFragment.makeBundle(mCanvasContext, URLDecoder.decode(it, "utf-8"), getString(R.string.utils_externalToolTitle), true)
- RouteMatcher.route(requireContext(), Route(LtiLaunchFragment::class.java, canvasContext, args))
- }, page.title)
+ loadHtmlJob = canvasWebView.loadHtmlWithIframes(requireContext(), page.body, {
+ canvasWebView.loadHtml(it, page.title, baseUrl = mPage.htmlUrl)
+ }) {
+ LtiLaunchFragment.routeLtiLaunchFragment(requireContext(), mCanvasContext, it)
+ }
setupToolbar()
}
- private fun loadPageHtml(html: String, contentDescription: String?) {
- canvasWebView.loadHtml(html, contentDescription, baseUrl = mPage.htmlUrl)
- }
-
override fun onError(stringId: Int) {
Toast.makeText(requireContext(), stringId, Toast.LENGTH_SHORT).show()
}
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 0f3c3aaeaa..1831b84e83 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
@@ -15,7 +15,6 @@
*/
package com.instructure.teacher.fragments
-import android.graphics.Color
import android.os.Bundle
import android.view.LayoutInflater
import android.webkit.WebChromeClient
@@ -56,7 +55,6 @@ import org.greenrobot.eventbus.ThreadMode
import java.io.UnsupportedEncodingException
import java.net.URI
import java.net.URL
-import java.net.URLDecoder
import java.util.*
@ScreenView(SCREEN_VIEW_EDIT_QUIZ_DETAILS)
@@ -358,7 +356,8 @@ class QuizDetailsFragment : BasePresenterFragment<
}
instructionsWebView.canvasEmbeddedWebViewCallback = object : CanvasWebView.CanvasEmbeddedWebViewCallback {
- override fun launchInternalWebViewFragment(url: String) = requireActivity().startActivity(InternalWebViewActivity.createIntent(requireActivity(), url, "", true))
+ override fun launchInternalWebViewFragment(url: String) =
+ requireActivity().startActivity(InternalWebViewActivity.createIntent(requireActivity(), url, "", true))
override fun shouldLaunchInternalWebViewFragment(url: String): Boolean = true
}
@@ -368,16 +367,11 @@ class QuizDetailsFragment : BasePresenterFragment<
instructionsWebView.setBackgroundResource(android.R.color.transparent)
// Load instructions
- loadHtmlJob = instructionsWebView.loadHtmlWithIframes(requireContext(), isTablet,
- quiz.description.orEmpty(), ::loadQuizHTML, {
- val args = LtiLaunchFragment.makeBundle(
- canvasContext, URLDecoder.decode(it, "utf-8"), getString(R.string.utils_externalToolTitle), true)
- RouteMatcher.route(requireContext(), Route(LtiLaunchFragment::class.java, canvasContext, args))
- }, quiz.title)
- }
-
- private fun loadQuizHTML(html: String, contentDescription: String?) {
- instructionsWebView.loadHtml(html, contentDescription, baseUrl = mQuiz.htmlUrl)
+ loadHtmlJob = instructionsWebView.loadHtmlWithIframes(requireContext(), quiz.description, {
+ instructionsWebView.loadHtml(it, quiz.title, baseUrl = mQuiz.htmlUrl)
+ }) {
+ LtiLaunchFragment.routeLtiLaunchFragment(requireContext(), canvasContext, it)
+ }
}
private fun setupListeners(quiz: Quiz) {
diff --git a/libs/pandautils/src/main/java/com/instructure/pandautils/binding/BindingAdapters.kt b/libs/pandautils/src/main/java/com/instructure/pandautils/binding/BindingAdapters.kt
index cc68d61c78..cb9b584383 100644
--- a/libs/pandautils/src/main/java/com/instructure/pandautils/binding/BindingAdapters.kt
+++ b/libs/pandautils/src/main/java/com/instructure/pandautils/binding/BindingAdapters.kt
@@ -25,7 +25,6 @@ import android.view.View
import android.view.ViewGroup
import android.view.accessibility.AccessibilityNodeInfo
import android.webkit.JavascriptInterface
-import android.widget.AdapterView
import android.widget.ImageView
import android.widget.Spinner
import androidx.annotation.ColorInt
@@ -109,10 +108,14 @@ private fun getOrCreateAdapter(recyclerView: RecyclerView): BindableRecyclerView
@BindingAdapter(value = ["htmlContent", "htmlTitle", "onLtiButtonPressed"], requireAll = false)
fun bindHtmlContent(webView: CanvasWebView, html: String?, title: String?, onLtiButtonPressed: OnLtiButtonPressed?) {
- webView.loadHtml(html ?: "", title ?: "")
+ webView.loadHtml(html.orEmpty(), title.orEmpty())
if (onLtiButtonPressed != null) {
webView.addJavascriptInterface(JSInterface(onLtiButtonPressed), "accessor")
}
+
+ if (HtmlContentFormatter.hasGoogleDocsUrl(html)) {
+ webView.addJavascriptInterface(JsGoogleDocsInterface(webView.context), "accessor")
+ }
}
interface OnLtiButtonPressed {
diff --git a/libs/pandautils/src/main/java/com/instructure/pandautils/utils/HtmlContentFormatter.kt b/libs/pandautils/src/main/java/com/instructure/pandautils/utils/HtmlContentFormatter.kt
index b3299edc61..fc914c0438 100644
--- a/libs/pandautils/src/main/java/com/instructure/pandautils/utils/HtmlContentFormatter.kt
+++ b/libs/pandautils/src/main/java/com/instructure/pandautils/utils/HtmlContentFormatter.kt
@@ -54,7 +54,7 @@ class HtmlContentFormatter(
// Snag that src
val srcUrl = matcher.group(1)
- if (srcUrl.contains("external_tools")) {
+ if (hasExternalTools(srcUrl)) {
// Handle the LTI case
val newIframe = externalToolIframe(srcUrl, iframe, context)
newHTML = newHTML.replace(iframe, newIframe)
@@ -70,6 +70,11 @@ class HtmlContentFormatter(
val newIframe = iframeWithLink(srcUrl, iframe, context)
newHTML = newHTML.replace(iframe, newIframe)
}
+
+ if (hasGoogleDocsUrl(srcUrl)) {
+ val newIframe = iframeWithGoogleDocsButton(srcUrl, iframe, context.getString(R.string.openLtiInExternalApp))
+ newHTML = newHTML.replace(iframe, newIframe)
+ }
}
}
@@ -118,6 +123,12 @@ class HtmlContentFormatter(
return iframe + htmlButton
}
+ private fun iframeWithGoogleDocsButton(srcUrl: String, iframe: String, buttonText: String): String {
+ val button = "%s
"
+ val htmlButton = String.format(button, srcUrl, buttonText)
+ return iframe + htmlButton
+ }
+
fun createAuthenticatedLtiUrl(html: String, authenticatedSessionUrl: String?): String {
if (authenticatedSessionUrl == null) return html
// Now we need to swap out part of the old url for this new authenticated url
@@ -134,4 +145,9 @@ class HtmlContentFormatter(
}
return newHTML
}
+
+ companion object {
+ fun hasGoogleDocsUrl(text: String?) = text?.contains("docs.google.com").orDefault()
+ fun hasExternalTools(text: String?) = text?.contains("external_tools").orDefault()
+ }
}
\ No newline at end of file
diff --git a/libs/pandautils/src/main/java/com/instructure/pandautils/utils/WebViewExtensions.kt b/libs/pandautils/src/main/java/com/instructure/pandautils/utils/WebViewExtensions.kt
index 04cf33ed06..307246bb27 100644
--- a/libs/pandautils/src/main/java/com/instructure/pandautils/utils/WebViewExtensions.kt
+++ b/libs/pandautils/src/main/java/com/instructure/pandautils/utils/WebViewExtensions.kt
@@ -26,17 +26,8 @@ import androidx.webkit.WebSettingsCompat
import androidx.webkit.WebViewFeature
import com.google.firebase.crashlytics.FirebaseCrashlytics
import com.instructure.canvasapi2.managers.OAuthManager
-import com.instructure.canvasapi2.models.AuthenticatedSession
-import com.instructure.canvasapi2.utils.Logger
-import com.instructure.canvasapi2.utils.weave.awaitApi
-import com.instructure.canvasapi2.utils.weave.catch
-import com.instructure.canvasapi2.utils.weave.tryWeave
-import com.instructure.pandautils.R
-import com.instructure.pandautils.discussions.DiscussionHtmlTemplates
-import com.instructure.pandautils.views.CanvasWebView
+import com.instructure.canvasapi2.utils.weave.weave
import kotlinx.coroutines.Job
-import java.net.URLEncoder
-import java.util.regex.Pattern
/**
* WebView helper function for handling all iframe related cases
@@ -47,105 +38,25 @@ import java.util.regex.Pattern
*
* We should now be able to call this function, preceded by a simple check for iframes, for all html webview content
*/
-fun WebView.loadHtmlWithIframes(context: Context, isTablet: Boolean, html: String, loadHtml: (newHtml: String, contentDescription: String?) -> Unit, jsCallback: ((ltiUrl: String) -> Unit)? = null, contentDescription: String? = null): Job? {
- if(html.contains("
diff --git a/apps/student/src/main/res/layout/fragment_assignment_details.xml b/apps/student/src/main/res/layout/fragment_assignment_details.xml
index 6df72977a7..76e5160ff4 100644
--- a/apps/student/src/main/res/layout/fragment_assignment_details.xml
+++ b/apps/student/src/main/res/layout/fragment_assignment_details.xml
@@ -732,17 +732,13 @@
-
+ android:background="@color/backgroundLightest"
+ android:paddingStart="8dp"
+ android:paddingEnd="8dp"/>
diff --git a/apps/student/src/main/res/layout/fragment_discussions_details.xml b/apps/student/src/main/res/layout/fragment_discussions_details.xml
index daa6a06139..51d858f941 100644
--- a/apps/student/src/main/res/layout/fragment_discussions_details.xml
+++ b/apps/student/src/main/res/layout/fragment_discussions_details.xml
@@ -317,11 +317,12 @@
-
-
diff --git a/apps/student/src/main/res/layout/fragment_module_quiz_decider.xml b/apps/student/src/main/res/layout/fragment_module_quiz_decider.xml
index c02b262549..c3b1e991de 100644
--- a/apps/student/src/main/res/layout/fragment_module_quiz_decider.xml
+++ b/apps/student/src/main/res/layout/fragment_module_quiz_decider.xml
@@ -70,10 +70,10 @@
android:layout_gravity="center_horizontal"
android:layout_marginTop="8dp"/>
-
+
.
~
-->
-
+ android:layout_height="match_parent"
+ android:fillViewport="true"
+ android:scrollbars="none">
-
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/apps/student/src/main/res/layout/fragment_text_submission.xml b/apps/student/src/main/res/layout/fragment_text_submission.xml
index 97c101a6f3..5f0b021c07 100644
--- a/apps/student/src/main/res/layout/fragment_text_submission.xml
+++ b/apps/student/src/main/res/layout/fragment_text_submission.xml
@@ -19,13 +19,21 @@
android:id="@+id/textSubmission"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:orientation="vertical">
+ android:orientation="vertical"
+ android:scrollbars="none">
-
+ android:fillViewport="true">
+
+
+
+
-
+ android:clipToPadding="false"
+ android:layout_below="@id/toolbar">
+
+
+
+
= 100) {
@@ -262,7 +262,7 @@ class AssignmentDetailsFragment : BasePresenterFragment<
}
})
- descriptionWebView.canvasWebViewClientCallback = object : CanvasWebView.CanvasWebViewClientCallback {
+ descriptionWebViewWrapper.webView.canvasWebViewClientCallback = object : CanvasWebView.CanvasWebViewClientCallback {
override fun openMediaFromWebView(mime: String, url: String, filename: String) {
RouteMatcher.openMedia(requireActivity(), url)
}
@@ -276,18 +276,14 @@ class AssignmentDetailsFragment : BasePresenterFragment<
override fun canRouteInternallyDelegate(url: String): Boolean = RouteMatcher.canRouteInternally(activity, url, ApiPrefs.domain, false)
}
- descriptionWebView.canvasEmbeddedWebViewCallback = object : CanvasWebView.CanvasEmbeddedWebViewCallback {
+ descriptionWebViewWrapper.webView.canvasEmbeddedWebViewCallback = object : CanvasWebView.CanvasEmbeddedWebViewCallback {
override fun launchInternalWebViewFragment(url: String) = requireActivity().startActivity(InternalWebViewActivity.createIntent(requireActivity(), url, "", true))
override fun shouldLaunchInternalWebViewFragment(url: String): Boolean = true
}
- //make the WebView background transparent
- descriptionWebView.setBackgroundColor(0)
- descriptionWebView.setBackgroundResource(android.R.color.transparent)
-
// Load description
- loadHtmlJob = descriptionWebView.loadHtmlWithIframes(requireContext(), description, {
- descriptionWebView.loadHtml(it, name, baseUrl = mAssignment.htmlUrl)
+ loadHtmlJob = descriptionWebViewWrapper.webView.loadHtmlWithIframes(requireContext(), description, {
+ descriptionWebViewWrapper.loadHtml(it, name, baseUrl = mAssignment.htmlUrl)
}) {
LtiLaunchFragment.routeLtiLaunchFragment(requireContext(), mCourse, it)
}
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 7807ccc30e..af92dacd34 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
@@ -250,10 +250,10 @@ class DiscussionsDetailsFragment : BasePresenterFragment<
mDiscussionEntryId)
}
- discussionRepliesWebView.setInvisible()
+ discussionRepliesWebViewWrapper.setInvisible()
- repliesLoadHtmlJob = discussionRepliesWebView.loadHtmlWithIframes(requireContext(), html, {
- discussionRepliesWebView.loadDataWithBaseURL(CanvasWebView.getReferrer(true), html, "text/html", "utf-8", null)
+ repliesLoadHtmlJob = discussionRepliesWebViewWrapper.webView.loadHtmlWithIframes(requireContext(), html, {
+ discussionRepliesWebViewWrapper.loadDataWithBaseUrl(CanvasWebView.getReferrer(true), html, "text/html", "utf-8", null)
}) {
LtiLaunchFragment.routeLtiLaunchFragment(requireContext(), canvasContext, it)
}
@@ -265,7 +265,7 @@ class DiscussionsDetailsFragment : BasePresenterFragment<
} else {
discussionsScrollView.scrollTo(0, presenter.scrollPosition)
}
- discussionRepliesWebView.setVisible()
+ discussionRepliesWebViewWrapper.setVisible()
}
} catch { Logger.e("Error loading discussion " + it.message) }
}
@@ -406,20 +406,20 @@ class DiscussionsDetailsFragment : BasePresenterFragment<
showReplyView(presenter.discussionTopicHeader.id)
}
- headerLoadHtmlJob = discussionTopicHeaderWebView.loadHtmlWithIframes(requireContext(), discussionTopicHeader.message, {
- discussionTopicHeaderWebView.loadHtml(it, discussionTopicHeader.title, baseUrl = mDiscussionTopicHeader.htmlUrl)
+ headerLoadHtmlJob = discussionTopicHeaderWebViewWrapper.webView.loadHtmlWithIframes(requireContext(), discussionTopicHeader.message, {
+ discussionTopicHeaderWebViewWrapper.loadHtml(it, discussionTopicHeader.title, baseUrl = mDiscussionTopicHeader.htmlUrl)
}) {
LtiLaunchFragment.routeLtiLaunchFragment(requireContext(), canvasContext, it)
}
- discussionRepliesWebView.loadHtml("", "")
+ discussionRepliesWebViewWrapper.loadHtml("", "")
}
override fun onPause() {
super.onPause()
presenter.scrollPosition = discussionsScrollView.scrollY
- discussionTopicHeaderWebView.onPause()
- discussionRepliesWebView.onPause()
+ discussionTopicHeaderWebViewWrapper.webView.onPause()
+ discussionRepliesWebViewWrapper.webView.onPause()
}
override fun onResume() {
@@ -445,11 +445,15 @@ class DiscussionsDetailsFragment : BasePresenterFragment<
}
}
- discussionTopicHeaderWebView.onResume()
- discussionRepliesWebView.onResume()
+ discussionTopicHeaderWebViewWrapper.webView.onResume()
+ discussionRepliesWebViewWrapper.webView.onResume()
- setupWebView(discussionTopicHeaderWebView, false)
- setupWebView(discussionRepliesWebView, true, addDarkTheme = true)
+ setupWebView(discussionTopicHeaderWebViewWrapper.webView, false)
+ setupWebView(discussionRepliesWebViewWrapper.webView, true, addDarkTheme = true)
+ discussionRepliesWebViewWrapper.onThemeChanged = { themeChanged, html ->
+ setupWebView(discussionRepliesWebViewWrapper.webView, true, addDarkTheme = !themeChanged)
+ discussionRepliesWebViewWrapper.loadDataWithBaseUrl(CanvasWebView.getReferrer(true), html, "text/html", "UTF-8", null)
+ }
}
private fun setupToolbar() {
@@ -489,7 +493,8 @@ class DiscussionsDetailsFragment : BasePresenterFragment<
@SuppressLint("SetJavaScriptEnabled")
private fun setupWebView(webView: CanvasWebView, addJSSupport: Boolean, addDarkTheme: Boolean = false) {
WebView.setWebContentsDebuggingEnabled(BuildConfig.DEBUG)
- webView.setBackgroundColor(requireContext().getColor(R.color.backgroundLightest))
+ val backgroundColorRes = if (addDarkTheme) R.color.backgroundLightest else R.color.white
+ webView.setBackgroundColor(requireContext().getColor(backgroundColorRes))
webView.settings.javaScriptEnabled = true
if(addJSSupport) webView.addJavascriptInterface(JSDiscussionInterface(), "accessor")
webView.settings.useWideViewPort = true
@@ -626,16 +631,16 @@ class DiscussionsDetailsFragment : BasePresenterFragment<
val likingSumAllyText = DiscussionEntryHtmlConverter.getLikeCountText(requireContext(), discussionEntry)
val likingColor = DiscussionUtils.getHexColorString(if (discussionEntry._hasRated) ThemePrefs.brandColor else ContextCompat.getColor(requireContext(), R.color.textDark))
requireActivity().runOnUiThread {
- discussionRepliesWebView.loadUrl("javascript:$methodName('${discussionEntry.id}')")
- discussionRepliesWebView.loadUrl("javascript:updateLikedCount('${discussionEntry.id}','$likingSum','$likingColor','$likingSumAllyText')")
+ discussionRepliesWebViewWrapper.webView.loadUrl("javascript:$methodName('${discussionEntry.id}')")
+ discussionRepliesWebViewWrapper.webView.loadUrl("javascript:updateLikedCount('${discussionEntry.id}','$likingSum','$likingColor','$likingSumAllyText')")
}
}
override fun updateDiscussionEntry(discussionEntry: DiscussionEntry) {
requireActivity().runOnUiThread {
- discussionRepliesWebView.loadUrl("javascript:updateEntry('${discussionEntry.id}', '${discussionEntry.message}')")
+ discussionRepliesWebViewWrapper.webView.loadUrl("javascript:updateEntry('${discussionEntry.id}', '${discussionEntry.message}')")
if (discussionEntry.attachments == null && discussionEntry.attachments?.size!! < 1)
- discussionRepliesWebView.loadUrl("javascript:hideAttachmentIcon('${discussionEntry.id}'")
+ discussionRepliesWebViewWrapper.webView.loadUrl("javascript:hideAttachmentIcon('${discussionEntry.id}'")
}
}
@@ -692,18 +697,18 @@ class DiscussionsDetailsFragment : BasePresenterFragment<
override fun updateDiscussionAsDeleted(discussionEntry: DiscussionEntry) {
val deletedText = DiscussionUtils.formatDeletedInfoText(requireContext(), discussionEntry)
- discussionRepliesWebView.post { discussionRepliesWebView.loadUrl(
+ discussionRepliesWebViewWrapper.post { discussionRepliesWebViewWrapper.webView.loadUrl(
"javascript:markAsDeleted" + "('" + discussionEntry.id.toString() + "','" + deletedText + "')") }
}
override fun updateDiscussionsMarkedAsReadCompleted(markedAsReadIds: List) {
markedAsReadIds.forEach {
- discussionRepliesWebView.post { discussionRepliesWebView.loadUrl("javascript:markAsRead('$it')") }
+ discussionRepliesWebViewWrapper.post { discussionRepliesWebViewWrapper.webView.loadUrl("javascript:markAsRead('$it')") }
}
}
override fun updateDiscussionsMarkedAsUnreadCompleted(markedAsUnreadId: Long) {
- discussionRepliesWebView.post { discussionRepliesWebView.loadUrl("javascript:markAsUnread('$markedAsUnreadId')") }
+ discussionRepliesWebViewWrapper.post { discussionRepliesWebViewWrapper.webView.loadUrl("javascript:markAsUnread('$markedAsUnreadId')") }
}
override fun showAnonymousDiscussionView() {
@@ -725,7 +730,7 @@ class DiscussionsDetailsFragment : BasePresenterFragment<
if (discussionsScrollView == null) return false
val scrollBounds = Rect().apply { discussionsScrollView.getDrawingRect(this) }
- val discussionRepliesHeight = discussionRepliesWebView.height
+ val discussionRepliesHeight = discussionRepliesWebViewWrapper.height
val discussionScrollViewContentHeight = discussionsScrollViewContentWrapper.height
val otherContentHeight = discussionScrollViewContentHeight - discussionRepliesHeight
val top = requireContext().DP(topOffset) + otherContentHeight
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 dab03c0206..25f3603b79 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
@@ -103,7 +103,7 @@ class PageDetailsFragment : BasePresenterFragment<
}
setupToolbar()
- canvasWebView.canvasWebViewClientCallback = object : CanvasWebView.CanvasWebViewClientCallback {
+ canvasWebViewWraper.webView.canvasWebViewClientCallback = object : CanvasWebView.CanvasWebViewClientCallback {
override fun openMediaFromWebView(mime: String, url: String, filename: String) {
RouteMatcher.openMedia(activity, url)
}
@@ -123,7 +123,7 @@ class PageDetailsFragment : BasePresenterFragment<
}
}
- canvasWebView.webChromeClient = (object : WebChromeClient() {
+ canvasWebViewWraper.webView.webChromeClient = (object : WebChromeClient() {
override fun onProgressChanged(view: WebView?, newProgress: Int) {
super.onProgressChanged(view, newProgress)
if (newProgress >= 100) {
@@ -132,12 +132,12 @@ class PageDetailsFragment : BasePresenterFragment<
}
})
- canvasWebView.canvasEmbeddedWebViewCallback = object : CanvasWebView.CanvasEmbeddedWebViewCallback {
+ canvasWebViewWraper.webView.canvasEmbeddedWebViewCallback = object : CanvasWebView.CanvasEmbeddedWebViewCallback {
override fun launchInternalWebViewFragment(url: String) = requireActivity().startActivity(InternalWebViewActivity.createIntent(requireActivity(), url, getString(R.string.utils_externalToolTitle), true))
override fun shouldLaunchInternalWebViewFragment(url: String): Boolean = !RouteMatcher.canRouteInternally(activity, url, ApiPrefs.domain, false)
}
- canvasWebView.setMediaDownloadCallback (object : CanvasWebView.MediaDownloadCallback{
+ canvasWebViewWraper.webView.setMediaDownloadCallback (object : CanvasWebView.MediaDownloadCallback{
override fun downloadMedia(mime: String?, url: String?, filename: String?) {
downloadUrl = url
downloadFileName = filename
@@ -172,8 +172,8 @@ class PageDetailsFragment : BasePresenterFragment<
override fun populatePageDetails(page: Page) {
mPage = page
- loadHtmlJob = canvasWebView.loadHtmlWithIframes(requireContext(), page.body, {
- canvasWebView.loadHtml(it, page.title, baseUrl = mPage.htmlUrl)
+ loadHtmlJob = canvasWebViewWraper.webView.loadHtmlWithIframes(requireContext(), page.body, {
+ canvasWebViewWraper.loadHtml(it, page.title, baseUrl = mPage.htmlUrl)
}) {
LtiLaunchFragment.routeLtiLaunchFragment(requireContext(), mCanvasContext, it)
}
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 29c224d4f6..ecc9cbc480 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
@@ -331,7 +331,7 @@ class QuizDetailsFragment : BasePresenterFragment<
// Show progress bar while loading description
instructionsProgressBar.announceForAccessibility(getString(R.string.loading))
instructionsProgressBar.setVisible()
- instructionsWebView.webChromeClient = object : WebChromeClient() {
+ instructionsWebViewWrapper.webView.webChromeClient = object : WebChromeClient() {
override fun onProgressChanged(view: WebView?, newProgress: Int) {
super.onProgressChanged(view, newProgress)
if (newProgress >= 100) {
@@ -340,7 +340,7 @@ class QuizDetailsFragment : BasePresenterFragment<
}
}
- instructionsWebView.canvasWebViewClientCallback = object : CanvasWebView.CanvasWebViewClientCallback {
+ instructionsWebViewWrapper.webView.canvasWebViewClientCallback = object : CanvasWebView.CanvasWebViewClientCallback {
override fun openMediaFromWebView(mime: String, url: String, filename: String) {
RouteMatcher.openMedia(requireActivity(), url)
}
@@ -355,7 +355,7 @@ class QuizDetailsFragment : BasePresenterFragment<
}
- instructionsWebView.canvasEmbeddedWebViewCallback = object : CanvasWebView.CanvasEmbeddedWebViewCallback {
+ instructionsWebViewWrapper.webView.canvasEmbeddedWebViewCallback = object : CanvasWebView.CanvasEmbeddedWebViewCallback {
override fun launchInternalWebViewFragment(url: String) =
requireActivity().startActivity(InternalWebViewActivity.createIntent(requireActivity(), url, "", true))
@@ -363,12 +363,12 @@ class QuizDetailsFragment : BasePresenterFragment<
}
//make the WebView background transparent
- instructionsWebView.setBackgroundColor(0)
- instructionsWebView.setBackgroundResource(android.R.color.transparent)
+ instructionsWebViewWrapper.setBackgroundColor(0)
+ instructionsWebViewWrapper.setBackgroundResource(android.R.color.transparent)
// Load instructions
- loadHtmlJob = instructionsWebView.loadHtmlWithIframes(requireContext(), quiz.description, {
- instructionsWebView.loadHtml(it, quiz.title, baseUrl = mQuiz.htmlUrl)
+ loadHtmlJob = instructionsWebViewWrapper.webView.loadHtmlWithIframes(requireContext(), quiz.description, {
+ instructionsWebViewWrapper.loadHtml(it, quiz.title, baseUrl = mQuiz.htmlUrl)
}) {
LtiLaunchFragment.routeLtiLaunchFragment(requireContext(), canvasContext, it)
}
diff --git a/apps/teacher/src/main/java/com/instructure/teacher/fragments/SpeedGraderTextSubmissionFragment.kt b/apps/teacher/src/main/java/com/instructure/teacher/fragments/SpeedGraderTextSubmissionFragment.kt
index e8202654bc..6208fe9c59 100644
--- a/apps/teacher/src/main/java/com/instructure/teacher/fragments/SpeedGraderTextSubmissionFragment.kt
+++ b/apps/teacher/src/main/java/com/instructure/teacher/fragments/SpeedGraderTextSubmissionFragment.kt
@@ -45,25 +45,25 @@ class SpeedGraderTextSubmissionFragment : Fragment(), SpeedGraderWebNavigator {
return inflater.inflate(R.layout.fragment_speed_grader_text_submission, container, false)
}
- override fun canGoBack() = textSubmissionWebView.canGoBack()
- override fun goBack() = textSubmissionWebView.goBack()
+ override fun canGoBack() = textSubmissionWebViewWrapper.webView.canGoBack()
+ override fun goBack() = textSubmissionWebViewWrapper.webView.goBack()
override fun onStart() {
super.onStart()
- textSubmissionWebView.webChromeClient = object : WebChromeClient() {
+ textSubmissionWebViewWrapper.webView.webChromeClient = object : WebChromeClient() {
override fun onProgressChanged(view: WebView?, newProgress: Int) {
super.onProgressChanged(view, newProgress)
if (!isAdded) return
if (newProgress >= 100) {
progressBar?.setGone()
- textSubmissionWebView?.setVisible()
+ textSubmissionWebViewWrapper?.setVisible()
} else {
progressBar.announceForAccessibility(getString(R.string.loading))
}
}
}
- textSubmissionWebView.canvasWebViewClientCallback = object : CanvasWebView.CanvasWebViewClientCallback {
+ textSubmissionWebViewWrapper.webView.canvasWebViewClientCallback = object : CanvasWebView.CanvasWebViewClientCallback {
override fun openMediaFromWebView(mime: String, url: String, filename: String) = Unit
override fun onPageStartedCallback(webView: WebView, url: String) = Unit
override fun onPageFinishedCallback(webView: WebView, url: String) = Unit
@@ -73,17 +73,17 @@ class SpeedGraderTextSubmissionFragment : Fragment(), SpeedGraderWebNavigator {
}
}
- textSubmissionWebView.canvasEmbeddedWebViewCallback = object : CanvasWebView.CanvasEmbeddedWebViewCallback {
+ textSubmissionWebViewWrapper.webView.canvasEmbeddedWebViewCallback = object : CanvasWebView.CanvasEmbeddedWebViewCallback {
override fun launchInternalWebViewFragment(url: String) = requireActivity().startActivity(InternalWebViewActivity.createIntent(requireActivity(), url, "", true))
override fun shouldLaunchInternalWebViewFragment(url: String): Boolean = true
}
- textSubmissionWebView.loadHtml(mSubmissionText, getString(R.string.a11y_submissionText))
+ textSubmissionWebViewWrapper.loadHtml(mSubmissionText, getString(R.string.a11y_submissionText))
}
override fun onStop() {
super.onStop()
- textSubmissionWebView.stopLoading()
+ textSubmissionWebViewWrapper.webView.stopLoading()
}
companion object {
diff --git a/apps/teacher/src/main/res/layout/fragment_assignment_details.xml b/apps/teacher/src/main/res/layout/fragment_assignment_details.xml
index d9e00c88e7..c259508666 100644
--- a/apps/teacher/src/main/res/layout/fragment_assignment_details.xml
+++ b/apps/teacher/src/main/res/layout/fragment_assignment_details.xml
@@ -455,13 +455,13 @@
android:visibility="gone"
tools:visibility="visible"/>
-
+ android:background="@color/backgroundLightest"
+ android:paddingStart="8dp"
+ android:paddingEnd="8dp"/>
diff --git a/apps/teacher/src/main/res/layout/fragment_calendar_event.xml b/apps/teacher/src/main/res/layout/fragment_calendar_event.xml
index 333a43507e..0e92940427 100644
--- a/apps/teacher/src/main/res/layout/fragment_calendar_event.xml
+++ b/apps/teacher/src/main/res/layout/fragment_calendar_event.xml
@@ -111,15 +111,23 @@
app:layout_constraintEnd_toEndOf="parent"
tools:text="Deak Ferenc utca 10" />
-
+ app:layout_constraintVertical_bias="0.0">
+
+
+
+
\ No newline at end of file
diff --git a/apps/teacher/src/main/res/layout/fragment_discussions_details.xml b/apps/teacher/src/main/res/layout/fragment_discussions_details.xml
index 1e04b33aff..8f0655bab8 100644
--- a/apps/teacher/src/main/res/layout/fragment_discussions_details.xml
+++ b/apps/teacher/src/main/res/layout/fragment_discussions_details.xml
@@ -416,11 +416,13 @@
-
+ android:layout_marginTop="4dp"
+ android:background="@color/backgroundLightest"
+ android:minHeight="48dp"/>
-
+ android:layout_marginTop="4dp" />
-
+ android:clipToPadding="false">
+
+
+
+
-
diff --git a/apps/teacher/src/main/res/layout/fragment_speed_grader_text_submission.xml b/apps/teacher/src/main/res/layout/fragment_speed_grader_text_submission.xml
index 22f620f849..8756c0bfad 100644
--- a/apps/teacher/src/main/res/layout/fragment_speed_grader_text_submission.xml
+++ b/apps/teacher/src/main/res/layout/fragment_speed_grader_text_submission.xml
@@ -20,11 +20,18 @@
android:layout_height="match_parent"
android:orientation="vertical">
-
+ android:fillViewport="true">
+
+
+
+
.
~
-->
-
+ android:layout_height="match_parent"
+ android:fillViewport="true"
+ android:scrollbars="none">
-
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/libs/pandares/src/main/res/drawable/bg_button_full_rounded.xml b/libs/pandares/src/main/res/drawable/bg_button_full_rounded.xml
new file mode 100644
index 0000000000..cac6de5f41
--- /dev/null
+++ b/libs/pandares/src/main/res/drawable/bg_button_full_rounded.xml
@@ -0,0 +1,24 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/libs/pandares/src/main/res/values-night/colors.xml b/libs/pandares/src/main/res/values-night/colors.xml
index 2579070035..e6bd848c2c 100644
--- a/libs/pandares/src/main/res/values-night/colors.xml
+++ b/libs/pandares/src/main/res/values-night/colors.xml
@@ -32,7 +32,7 @@
#394B58
#0D884D
#E25C1D
- #C340AA
+ #BF32A4
#C340AA
#E4263C
#C7CDD1
@@ -44,7 +44,7 @@
#0D884D
#E25C1D
#E4263C
- #127CB9
+ #008EE2
#E25C1D
#2D3B45
#394B58
diff --git a/libs/pandares/src/main/res/values/strings.xml b/libs/pandares/src/main/res/values/strings.xml
index c35182d89b..86891a0313 100644
--- a/libs/pandares/src/main/res/values/strings.xml
+++ b/libs/pandares/src/main/res/values/strings.xml
@@ -1332,4 +1332,6 @@
Subscribe to Calendar Feed
You can sync your Canvas calendar to your Google Calendar account by clicking the Subscribe button in this dialog. Then, you’ll need to go to the Google Calendar app on your device and enable sync in the settings for the new calendar.
Subscribe
+ Switch to Light Mode
+ Switch to Dark Mode
diff --git a/libs/pandautils/build.gradle b/libs/pandautils/build.gradle
index 8c3cdc2389..6d93e1c314 100644
--- a/libs/pandautils/build.gradle
+++ b/libs/pandautils/build.gradle
@@ -75,6 +75,7 @@ android {
buildFeatures {
dataBinding true
+ viewBinding true
}
}
diff --git a/libs/pandautils/src/main/java/com/instructure/pandautils/binding/BindingAdapters.kt b/libs/pandautils/src/main/java/com/instructure/pandautils/binding/BindingAdapters.kt
index a0cb399259..b46e52c11d 100644
--- a/libs/pandautils/src/main/java/com/instructure/pandautils/binding/BindingAdapters.kt
+++ b/libs/pandautils/src/main/java/com/instructure/pandautils/binding/BindingAdapters.kt
@@ -43,6 +43,7 @@ import com.instructure.pandautils.mvvm.ItemViewModel
import com.instructure.pandautils.mvvm.ViewState
import com.instructure.pandautils.utils.*
import com.instructure.pandautils.views.CanvasWebView
+import com.instructure.pandautils.views.CanvasWebViewWrapper
import com.instructure.pandautils.views.EmptyView
import java.net.URLDecoder
@@ -107,14 +108,14 @@ private fun getOrCreateAdapter(recyclerView: RecyclerView): BindableRecyclerView
}
@BindingAdapter(value = ["htmlContent", "htmlTitle", "onLtiButtonPressed"], requireAll = false)
-fun bindHtmlContent(webView: CanvasWebView, html: String?, title: String?, onLtiButtonPressed: OnLtiButtonPressed?) {
- webView.loadHtml(html.orEmpty(), title.orEmpty())
+fun bindHtmlContent(webViewWrapper: CanvasWebViewWrapper, html: String?, title: String?, onLtiButtonPressed: OnLtiButtonPressed?) {
+ webViewWrapper.loadHtml(html.orEmpty(), title.orEmpty())
if (onLtiButtonPressed != null) {
- webView.addJavascriptInterface(JSInterface(onLtiButtonPressed), "accessor")
+ webViewWrapper.webView.addJavascriptInterface(JSInterface(onLtiButtonPressed), "accessor")
}
if (HtmlContentFormatter.hasGoogleDocsUrl(html)) {
- webView.addJavascriptInterface(JsGoogleDocsInterface(webView.context), "accessor")
+ webViewWrapper.webView.addJavascriptInterface(JsGoogleDocsInterface(webViewWrapper.context), "accessor")
}
}
diff --git a/libs/pandautils/src/main/java/com/instructure/pandautils/features/elementary/homeroom/HomeroomFragment.kt b/libs/pandautils/src/main/java/com/instructure/pandautils/features/elementary/homeroom/HomeroomFragment.kt
index 392564065e..7925a456d4 100644
--- a/libs/pandautils/src/main/java/com/instructure/pandautils/features/elementary/homeroom/HomeroomFragment.kt
+++ b/libs/pandautils/src/main/java/com/instructure/pandautils/features/elementary/homeroom/HomeroomFragment.kt
@@ -17,7 +17,6 @@
package com.instructure.pandautils.features.elementary.homeroom
import android.content.res.Configuration
-import android.graphics.Color
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
@@ -25,7 +24,6 @@ import android.view.ViewGroup
import android.view.ViewTreeObserver
import android.webkit.WebView
import androidx.fragment.app.Fragment
-import androidx.fragment.app.findFragment
import androidx.fragment.app.viewModels
import androidx.lifecycle.Observer
import androidx.recyclerview.widget.GridLayoutManager
@@ -44,7 +42,6 @@ import com.instructure.pandautils.views.CanvasWebView
import com.instructure.pandautils.views.SpacesItemDecoration
import dagger.hilt.android.AndroidEntryPoint
import kotlinx.android.synthetic.main.fragment_homeroom.*
-import kotlinx.android.synthetic.main.fragment_homeroom.view.*
import kotlinx.android.synthetic.main.item_announcement.view.*
import javax.inject.Inject
@@ -130,9 +127,9 @@ class HomeroomFragment : Fragment() {
private fun setupWebViews() {
announcementsContainer.children.forEach {
- val webView = it.announcementWebView
- if (webView != null) {
- setupWebView(webView)
+ val webViewWrapper = it.announcementWebViewWrapper
+ if (webViewWrapper != null) {
+ setupWebView(webViewWrapper.webView)
}
}
}
diff --git a/libs/pandautils/src/main/java/com/instructure/pandautils/features/elementary/resources/ResourcesFragment.kt b/libs/pandautils/src/main/java/com/instructure/pandautils/features/elementary/resources/ResourcesFragment.kt
index 1d5c77ba36..3d19b8959e 100644
--- a/libs/pandautils/src/main/java/com/instructure/pandautils/features/elementary/resources/ResourcesFragment.kt
+++ b/libs/pandautils/src/main/java/com/instructure/pandautils/features/elementary/resources/ResourcesFragment.kt
@@ -17,7 +17,6 @@
package com.instructure.pandautils.features.elementary.resources
import android.content.DialogInterface
-import android.graphics.Color
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
@@ -99,9 +98,9 @@ class ResourcesFragment : Fragment() {
private fun setupWebViews() {
importantLinksContainer.children.forEach {
- val webView = it.importantLinksWebView
+ val webView = it.importantLinksWebViewWrapper
if (webView != null) {
- setupWebView(webView)
+ setupWebView(webView.webView)
}
}
}
diff --git a/libs/pandautils/src/main/java/com/instructure/pandautils/views/CanvasWebView.kt b/libs/pandautils/src/main/java/com/instructure/pandautils/views/CanvasWebView.kt
index 101293b48f..4b006a4e35 100644
--- a/libs/pandautils/src/main/java/com/instructure/pandautils/views/CanvasWebView.kt
+++ b/libs/pandautils/src/main/java/com/instructure/pandautils/views/CanvasWebView.kt
@@ -484,8 +484,14 @@ class CanvasWebView @JvmOverloads constructor(
* @param title
* @return
*/
- fun loadHtml(html: String, title: String?, backgroundColorRes: Int = R.color.backgroundLightest, baseUrl: String? = null): String {
- val result = formatHtml(html, title, backgroundColorRes)
+ fun loadHtml(html: String,
+ title: String?,
+ baseUrl: String? = null,
+ htmlFormatColors: HtmlFormatColors = HtmlFormatColors(),
+ extraFormatting: ((String) -> String)? = null
+ ): String {
+ var result = formatHtml(html, title, htmlFormatColors)
+ if (extraFormatting != null) result = extraFormatting(result)
loadDataWithBaseURL(baseUrl ?: getReferrer(true), result, "text/html", encoding, getHtmlAsUrl(result))
return result
}
@@ -493,7 +499,7 @@ class CanvasWebView @JvmOverloads constructor(
/**
* Helper function that makes html content somewhat suitable for mobile
*/
- fun formatHtml(html: String, title: String? = "", @ColorRes backgroundColorRes: Int = R.color.backgroundLightest): String {
+ fun formatHtml(html: String, title: String? = "", htmlFormatColors: HtmlFormatColors = HtmlFormatColors()): String {
var formatted = applyWorkAroundForDoubleSlashesAsUrlSource(html)
formatted = addProtocolToLinks(formatted)
formatted = checkForMathTags(formatted)
@@ -502,10 +508,10 @@ class CanvasWebView @JvmOverloads constructor(
return htmlWrapper
.replace("{\$CONTENT$}", formatted)
.replace("{\$TITLE$}", title ?: "")
- .replace("{\$BACKGROUND$}", colorResToHexString(backgroundColorRes))
- .replace("{\$COLOR$}", colorResToHexString(R.color.textDarkest))
- .replace("{\$LINK_COLOR$}", colorResToHexString(R.color.textInfo))
- .replace("{\$VISITED_LINK_COLOR\$}", colorResToHexString(R.color.textAlert))
+ .replace("{\$BACKGROUND$}", colorResToHexString(htmlFormatColors.backgroundColorRes))
+ .replace("{\$COLOR$}", colorResToHexString(htmlFormatColors.textColor))
+ .replace("{\$LINK_COLOR$}", colorResToHexString(htmlFormatColors.linkColor))
+ .replace("{\$VISITED_LINK_COLOR\$}", colorResToHexString(htmlFormatColors.visitedLinkColor))
}
private fun colorResToHexString(@ColorRes colorRes: Int): String {
@@ -789,3 +795,10 @@ class CanvasWebView @JvmOverloads constructor(
}
}
}
+
+data class HtmlFormatColors(
+ @ColorRes val backgroundColorRes: Int = R.color.backgroundLightest,
+ @ColorRes val textColor: Int = R.color.textDarkest,
+ @ColorRes val linkColor: Int = R.color.textInfo,
+ @ColorRes val visitedLinkColor: Int = R.color.textAlert
+)
diff --git a/libs/pandautils/src/main/java/com/instructure/pandautils/views/CanvasWebViewWrapper.kt b/libs/pandautils/src/main/java/com/instructure/pandautils/views/CanvasWebViewWrapper.kt
new file mode 100644
index 0000000000..47387a5de6
--- /dev/null
+++ b/libs/pandautils/src/main/java/com/instructure/pandautils/views/CanvasWebViewWrapper.kt
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 2022 - 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.views
+
+import android.content.Context
+import android.content.res.Configuration
+import android.util.AttributeSet
+import android.view.LayoutInflater
+import android.widget.LinearLayout
+import com.instructure.pandautils.R
+import com.instructure.pandautils.databinding.ViewCanvasWebViewWrapperBinding
+import com.instructure.pandautils.utils.ColorUtils
+import com.instructure.pandautils.utils.onClick
+import com.instructure.pandautils.utils.setGone
+import com.instructure.pandautils.utils.setVisible
+
+class CanvasWebViewWrapper @JvmOverloads constructor(
+ context: Context,
+ attrs: AttributeSet? = null,
+ defStyleAttr: Int = 0
+) : LinearLayout(context, attrs, defStyleAttr) {
+
+ private var html: String? = null
+ private var title: String? = null
+ private var baseUrl: String? = null
+
+ var themeSwitched = false
+ private set
+
+ private val binding: ViewCanvasWebViewWrapperBinding
+
+ val webView: CanvasWebView
+ get() = binding.contentWebView
+
+ var onThemeChanged: (Boolean, String) -> Unit = { _, html ->
+ changeContentTheme(html)
+ }
+
+ init {
+ orientation = VERTICAL
+
+ binding = ViewCanvasWebViewWrapperBinding.inflate(LayoutInflater.from(context), this)
+ binding.themeSwitchButton.onClick {
+ html?.let { html ->
+ themeSwitched = !themeSwitched
+ changeButtonTheme()
+ onThemeChanged(themeSwitched, html)
+ }
+ }
+ }
+
+ private fun changeButtonTheme() {
+ val background = if (themeSwitched) R.color.white else R.color.backgroundLightest
+ val textColor = if (themeSwitched) R.color.licorice else R.color.textDarkest
+
+ setBackgroundColor(context.getColor(background))
+ binding.themeSwitchButton.background = ColorUtils.colorIt(context.getColor(textColor), binding.themeSwitchButton.background)
+ ColorUtils.colorIt(context.getColor(textColor), binding.themeSwitchIcon)
+
+ binding.themeSwitchText.setTextColor(context.getColor(textColor))
+ binding.themeSwitchText.setText(if (themeSwitched) R.string.switchToDarkMode else R.string.switchToLightMode)
+ }
+
+ private fun changeContentTheme(html: String, extraFormatting: ((String) -> String)? = null) {
+ val background = if (themeSwitched) R.color.white else R.color.backgroundLightest
+ val textColor = if (themeSwitched) R.color.licorice else R.color.textDarkest
+ val htmlFormatColors = HtmlFormatColors(
+ backgroundColorRes = background,
+ textColor = textColor,
+ linkColor = if (themeSwitched) R.color.electric else R.color.textInfo,
+ visitedLinkColor = if (themeSwitched) R.color.barney else R.color.textAlert,
+ )
+
+ binding.contentWebView.setBackgroundColor(context.getColor(background))
+ binding.contentWebView.loadHtml(html, title, baseUrl, htmlFormatColors, extraFormatting)
+ postDelayed({
+ binding.contentWebView.clearHistory()
+ }, 100)
+ }
+
+ fun loadHtml(html: String, title: String?, baseUrl: String? = null, extraFormatting: ((String) -> String)? = null) {
+ this.html = html
+ this.title = title
+ this.baseUrl = baseUrl
+
+ initVisibility(html)
+
+ // We will change the content theme here also for pull to refresh cases.
+ changeContentTheme(html, extraFormatting)
+ }
+
+ fun loadDataWithBaseUrl(url: String?, data: String, mimeType: String?, encoding: String?, history: String?) {
+ html = data
+ initVisibility(data)
+ binding.contentWebView.loadDataWithBaseURL(url, data, mimeType, encoding, history)
+ }
+
+ private fun initVisibility(html: String) {
+ val nightModeFlags: Int = context.resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK
+ if (nightModeFlags == Configuration.UI_MODE_NIGHT_YES && html.isNotEmpty()) {
+ binding.themeSwitchButton.setVisible()
+ } else {
+ binding.themeSwitchButton.setGone()
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/libs/pandautils/src/main/res/layout/item_announcement.xml b/libs/pandautils/src/main/res/layout/item_announcement.xml
index 68349d4b19..5f8277b584 100644
--- a/libs/pandautils/src/main/res/layout/item_announcement.xml
+++ b/libs/pandautils/src/main/res/layout/item_announcement.xml
@@ -53,13 +53,13 @@
android:text="@{itemViewModel.data.title}"
tools:text="Announcement Title" />
-
diff --git a/libs/pandautils/src/main/res/layout/item_important_links.xml b/libs/pandautils/src/main/res/layout/item_important_links.xml
index fe6180121e..dc4c70e199 100644
--- a/libs/pandautils/src/main/res/layout/item_important_links.xml
+++ b/libs/pandautils/src/main/res/layout/item_important_links.xml
@@ -51,11 +51,13 @@
app:drawableTint="@color/textDarkest"
tools:text="Homeroom 1" />
-
diff --git a/libs/pandautils/src/main/res/layout/view_canvas_web_view_wrapper.xml b/libs/pandautils/src/main/res/layout/view_canvas_web_view_wrapper.xml
new file mode 100644
index 0000000000..ea1053d57e
--- /dev/null
+++ b/libs/pandautils/src/main/res/layout/view_canvas_web_view_wrapper.xml
@@ -0,0 +1,71 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
From bbd802c900f05791f8c02072456d31ecec748bd6 Mon Sep 17 00:00:00 2001
From: Akos Hermann <72087159+hermannakos@users.noreply.github.com>
Date: Fri, 18 Nov 2022 14:03:54 +0100
Subject: [PATCH 53/73] [MBL-16393][Teacher] Fixed announcement routing (#1777)
refs: MBL-16393
affects: Teacher
release note: none
test plan: See ticket.
---
.../discussion/StudentDiscussionRouter.kt | 3 ++-
.../fragment/DiscussionListFragment.kt | 2 +-
.../factory/DiscussionListPresenterFactory.kt | 4 +--
.../discussion/TeacherDiscussionRouter.kt | 9 +++----
.../fragments/DiscussionsListFragment.kt | 5 +---
.../presenters/DiscussionListPresenter.kt | 27 ++-----------------
.../discussion/router/DiscussionRouter.kt | 2 +-
.../router/DiscussionRouterFragment.kt | 13 ++++++---
.../router/DiscussionRouterViewData.kt | 3 ++-
.../router/DiscussionRouterViewModel.kt | 13 +++++----
.../router/DiscussionRouterViewModelTest.kt | 22 +++++++--------
11 files changed, 42 insertions(+), 61 deletions(-)
diff --git a/apps/student/src/main/java/com/instructure/student/features/discussion/StudentDiscussionRouter.kt b/apps/student/src/main/java/com/instructure/student/features/discussion/StudentDiscussionRouter.kt
index 827cc2ba36..47d98d8980 100644
--- a/apps/student/src/main/java/com/instructure/student/features/discussion/StudentDiscussionRouter.kt
+++ b/apps/student/src/main/java/com/instructure/student/features/discussion/StudentDiscussionRouter.kt
@@ -13,7 +13,8 @@ class StudentDiscussionRouter(private val fragmentActivity: FragmentActivity) :
override fun routeToDiscussion(
canvasContext: CanvasContext,
isRedesign: Boolean,
- discussionTopicHeader: DiscussionTopicHeader
+ discussionTopicHeader: DiscussionTopicHeader,
+ isAnnouncement: Boolean
) {
val route = when {
isRedesign -> DiscussionDetailsWebViewFragment.makeRoute(canvasContext, discussionTopicHeader)
diff --git a/apps/student/src/main/java/com/instructure/student/fragment/DiscussionListFragment.kt b/apps/student/src/main/java/com/instructure/student/fragment/DiscussionListFragment.kt
index af2e436227..6f0f7367b8 100644
--- a/apps/student/src/main/java/com/instructure/student/fragment/DiscussionListFragment.kt
+++ b/apps/student/src/main/java/com/instructure/student/fragment/DiscussionListFragment.kt
@@ -94,7 +94,7 @@ open class DiscussionListFragment : ParentFragment(), Bookmarkable {
rootView = layoutInflater.inflate(R.layout.course_discussion_topic, container, false)
recyclerAdapter = DiscussionListRecyclerAdapter(requireContext(), canvasContext, !isAnnouncement, object: DiscussionListRecyclerAdapter.AdapterToDiscussionsCallback{
override fun onRowClicked(model: DiscussionTopicHeader, position: Int, isOpenDetail: Boolean) {
- RouteMatcher.route(requireActivity(), DiscussionRouterFragment.makeRoute(canvasContext, model))
+ RouteMatcher.route(requireActivity(), DiscussionRouterFragment.makeRoute(canvasContext, model, isAnnouncement))
}
override fun onRefreshFinished() {
diff --git a/apps/teacher/src/main/java/com/instructure/teacher/factory/DiscussionListPresenterFactory.kt b/apps/teacher/src/main/java/com/instructure/teacher/factory/DiscussionListPresenterFactory.kt
index 0844b87ac0..df5fcc2877 100644
--- a/apps/teacher/src/main/java/com/instructure/teacher/factory/DiscussionListPresenterFactory.kt
+++ b/apps/teacher/src/main/java/com/instructure/teacher/factory/DiscussionListPresenterFactory.kt
@@ -21,6 +21,6 @@ import com.instructure.teacher.presenters.DiscussionListPresenter
import com.instructure.teacher.viewinterface.DiscussionListView
import instructure.androidblueprint.PresenterFactory
-class DiscussionListPresenterFactory(private var canvasContext: CanvasContext, private var isAnnouncements: Boolean, private var isRedesignEnabled: Boolean) : PresenterFactory {
- override fun create(): DiscussionListPresenter = DiscussionListPresenter(canvasContext, isAnnouncements, isRedesignEnabled)
+class DiscussionListPresenterFactory(private var canvasContext: CanvasContext, private var isAnnouncements: Boolean) : PresenterFactory {
+ override fun create(): DiscussionListPresenter = DiscussionListPresenter(canvasContext, isAnnouncements)
}
diff --git a/apps/teacher/src/main/java/com/instructure/teacher/features/discussion/TeacherDiscussionRouter.kt b/apps/teacher/src/main/java/com/instructure/teacher/features/discussion/TeacherDiscussionRouter.kt
index 16ef0bbc97..21b311f21e 100644
--- a/apps/teacher/src/main/java/com/instructure/teacher/features/discussion/TeacherDiscussionRouter.kt
+++ b/apps/teacher/src/main/java/com/instructure/teacher/features/discussion/TeacherDiscussionRouter.kt
@@ -15,16 +15,13 @@ class TeacherDiscussionRouter(private val activity: FragmentActivity) : Discussi
override fun routeToDiscussion(
canvasContext: CanvasContext,
isRedesign: Boolean,
- discussionTopicHeader: DiscussionTopicHeader
+ discussionTopicHeader: DiscussionTopicHeader,
+ isAnnouncement: Boolean
) {
val route = when {
isRedesign -> DiscussionDetailsWebViewFragment.makeRoute(canvasContext, discussionTopicHeader)
- discussionTopicHeader.announcement -> {
- val bundle = DiscussionsDetailsFragment.makeBundle(discussionTopicHeader, true)
- Route(null, DiscussionsDetailsFragment::class.java, canvasContext, bundle)
- }
else -> {
- val bundle = DiscussionsDetailsFragment.makeBundle(discussionTopicHeader)
+ val bundle = DiscussionsDetailsFragment.makeBundle(discussionTopicHeader, isAnnouncement || discussionTopicHeader.announcement)
Route(null, DiscussionsDetailsFragment::class.java, canvasContext, bundle)
}
}
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 47dacd4a93..39d41d3842 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
@@ -20,7 +20,6 @@ import android.view.View
import androidx.appcompat.app.AlertDialog
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
-import com.instructure.canvasapi2.managers.UserManager
import com.instructure.canvasapi2.models.CanvasContext
import com.instructure.canvasapi2.models.DiscussionTopicHeader
import com.instructure.canvasapi2.utils.ApiPrefs
@@ -53,8 +52,6 @@ open class DiscussionsListFragment : BaseExpandableSyncFragment<
RecyclerView.ViewHolder,
DiscussionListAdapter>(), DiscussionListView {
- val featureFlagProvider: FeatureFlagProvider = FeatureFlagProvider(UserManager, ApiPrefs)
-
protected var mCanvasContext: CanvasContext by ParcelableArg(default = CanvasContext.getGenericContext(CanvasContext.Type.COURSE, -1L, ""))
private val mLinearLayoutManager by lazy { LinearLayoutManager(requireContext()) }
@@ -67,7 +64,7 @@ open class DiscussionsListFragment : BaseExpandableSyncFragment<
override fun layoutResId(): Int = R.layout.fragment_discussion_list
override val recyclerView: RecyclerView get() = discussionRecyclerView
- override fun getPresenterFactory() = DiscussionListPresenterFactory(mCanvasContext, mIsAnnouncements, featureFlagProvider.getDiscussionRedesignFeatureFlag())
+ override fun getPresenterFactory() = DiscussionListPresenterFactory(mCanvasContext, mIsAnnouncements)
override fun onPresenterPrepared(presenter: DiscussionListPresenter) {
val emptyTitle = getString(if (mIsAnnouncements) R.string.noAnnouncements else R.string.noDiscussions)
diff --git a/apps/teacher/src/main/java/com/instructure/teacher/presenters/DiscussionListPresenter.kt b/apps/teacher/src/main/java/com/instructure/teacher/presenters/DiscussionListPresenter.kt
index 9f529e1a9b..fd14c3e727 100644
--- a/apps/teacher/src/main/java/com/instructure/teacher/presenters/DiscussionListPresenter.kt
+++ b/apps/teacher/src/main/java/com/instructure/teacher/presenters/DiscussionListPresenter.kt
@@ -19,21 +19,16 @@ package com.instructure.teacher.presenters
import com.instructure.canvasapi2.StatusCallback
import com.instructure.canvasapi2.managers.AnnouncementManager
import com.instructure.canvasapi2.managers.DiscussionManager
-import com.instructure.canvasapi2.managers.FeaturesManager
import com.instructure.canvasapi2.models.CanvasContext
import com.instructure.canvasapi2.models.DiscussionTopicHeader
-import com.instructure.canvasapi2.models.Group
import com.instructure.canvasapi2.utils.ApiType
import com.instructure.canvasapi2.utils.LinkHeaders
import com.instructure.canvasapi2.utils.filterWithQuery
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.weave.weave
import com.instructure.interactions.router.Route
import com.instructure.pandautils.features.discussion.router.DiscussionRouterFragment
-import com.instructure.pandautils.utils.isCourse
-import com.instructure.pandautils.utils.isGroup
import com.instructure.teacher.viewinterface.DiscussionListView
import instructure.androidblueprint.SyncExpandablePresenter
import kotlinx.coroutines.Job
@@ -42,17 +37,13 @@ import java.util.*
class DiscussionListPresenter(
private val canvasContext: CanvasContext,
- private val isAnnouncements: Boolean,
- private val isRedesignEnabled: Boolean
+ private val isAnnouncements: Boolean
) : SyncExpandablePresenter(
String::class.java,
DiscussionTopicHeader::class.java
) {
private var discussionsListJob: Job? = null
- private var featureFlagJob: Job? = null
-
- private var discussionRedesignEnabled: Boolean = false
private var discussions: List = emptyList()
@@ -64,18 +55,6 @@ class DiscussionListPresenter(
}
override fun loadData(forceNetwork: Boolean) {
- featureFlagJob = weave {
- if (canvasContext.isCourse) {
- val featureFlags = FeaturesManager.getEnabledFeaturesForCourseAsync(canvasContext.id, true).await().dataOrNull
- discussionRedesignEnabled = featureFlags?.contains("react_discussions_post") ?: false && isRedesignEnabled
- }
-
- if (canvasContext.isGroup) {
- val featureFlags = FeaturesManager.getEnabledFeaturesForCourseAsync((canvasContext as Group).courseId, true).await().dataOrNull
- discussionRedesignEnabled = featureFlags?.contains("react_discussions_post") ?: false && isRedesignEnabled
- }
- }
-
discussionsListJob = tryWeave {
onRefreshStarted()
discussions = awaitApi {
@@ -105,7 +84,6 @@ class DiscussionListPresenter(
override fun refresh(forceNetwork: Boolean) {
discussionsListJob?.cancel()
- featureFlagJob?.cancel()
onRefreshStarted()
clearData()
loadData(true)
@@ -114,7 +92,6 @@ class DiscussionListPresenter(
override fun onDestroyed() {
super.onDestroyed()
discussionsListJob?.cancel()
- featureFlagJob?.cancel()
}
private val mDiscussionTopicHeaderPinnedCallback = object : StatusCallback() {
@@ -226,6 +203,6 @@ class DiscussionListPresenter(
}
fun getDetailsRoute(discussionTopicHeader: DiscussionTopicHeader): Route {
- return DiscussionRouterFragment.makeRoute(canvasContext, discussionTopicHeader)
+ return DiscussionRouterFragment.makeRoute(canvasContext, discussionTopicHeader, isAnnouncements)
}
}
diff --git a/libs/pandautils/src/main/java/com/instructure/pandautils/features/discussion/router/DiscussionRouter.kt b/libs/pandautils/src/main/java/com/instructure/pandautils/features/discussion/router/DiscussionRouter.kt
index d69dd29d4b..c6023836d4 100644
--- a/libs/pandautils/src/main/java/com/instructure/pandautils/features/discussion/router/DiscussionRouter.kt
+++ b/libs/pandautils/src/main/java/com/instructure/pandautils/features/discussion/router/DiscussionRouter.kt
@@ -6,7 +6,7 @@ import com.instructure.canvasapi2.models.Group
interface DiscussionRouter {
- fun routeToDiscussion(canvasContext: CanvasContext, isRedesign: Boolean, discussionTopicHeader: DiscussionTopicHeader)
+ fun routeToDiscussion(canvasContext: CanvasContext, isRedesign: Boolean, discussionTopicHeader: DiscussionTopicHeader, isAnnouncement: Boolean)
fun routeToGroupDiscussion(group: Group, id: Long, header: DiscussionTopicHeader, isRedesign: Boolean)
}
\ No newline at end of file
diff --git a/libs/pandautils/src/main/java/com/instructure/pandautils/features/discussion/router/DiscussionRouterFragment.kt b/libs/pandautils/src/main/java/com/instructure/pandautils/features/discussion/router/DiscussionRouterFragment.kt
index dcc2bc1ba5..7c04e5e688 100644
--- a/libs/pandautils/src/main/java/com/instructure/pandautils/features/discussion/router/DiscussionRouterFragment.kt
+++ b/libs/pandautils/src/main/java/com/instructure/pandautils/features/discussion/router/DiscussionRouterFragment.kt
@@ -30,6 +30,7 @@ class DiscussionRouterFragment : Fragment() {
default = 0L,
key = DISCUSSION_TOPIC_HEADER_ID
)
+ private var isAnnouncement by BooleanArg(key = DISCUSSION_ANNOUNCEMENT, default = false)
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
@@ -43,7 +44,7 @@ class DiscussionRouterFragment : Fragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
- viewModel.route(canvasContext, discussionTopicHeader, discussionTopicHeaderId)
+ viewModel.route(canvasContext, discussionTopicHeader, discussionTopicHeaderId, isAnnouncement)
viewModel.events.observe(viewLifecycleOwner) {
it.getContentIfNotHandled()?.let {
@@ -58,7 +59,8 @@ class DiscussionRouterFragment : Fragment() {
discussionRouter.routeToDiscussion(
action.canvasContext,
action.isRedesignEnabled,
- action.discussionTopicHeader
+ action.discussionTopicHeader,
+ action.isAnnouncement
)
}
is DiscussionRouterAction.RouteToGroupDiscussion -> {
@@ -76,18 +78,21 @@ class DiscussionRouterFragment : Fragment() {
const val DISCUSSION_TOPIC_HEADER = "discussion_topic_header"
const val DISCUSSION_TOPIC_HEADER_ID = "discussion_topic_header_id"
const val DISCUSSION_TOPIC = "discussion_topic"
+ const val DISCUSSION_ANNOUNCEMENT = "isAnnouncement"
- fun makeRoute(canvasContext: CanvasContext, discussionTopicHeader: DiscussionTopicHeader): Route {
+ fun makeRoute(canvasContext: CanvasContext, discussionTopicHeader: DiscussionTopicHeader, isAnnouncement: Boolean = false): Route {
val bundle = Bundle().apply {
putParcelable(DISCUSSION_TOPIC_HEADER, discussionTopicHeader)
+ putBoolean(DISCUSSION_ANNOUNCEMENT, isAnnouncement)
}
return Route(null, DiscussionRouterFragment::class.java, canvasContext, bundle)
}
- fun makeRoute(canvasContext: CanvasContext, discussionTopicHeaderId: Long): Route {
+ fun makeRoute(canvasContext: CanvasContext, discussionTopicHeaderId: Long, isAnnouncement: Boolean = false): Route {
val bundle = Bundle().apply {
putLong(DISCUSSION_TOPIC_HEADER_ID, discussionTopicHeaderId)
+ putBoolean(DISCUSSION_ANNOUNCEMENT, isAnnouncement)
}
return Route(null, DiscussionRouterFragment::class.java, canvasContext, bundle)
diff --git a/libs/pandautils/src/main/java/com/instructure/pandautils/features/discussion/router/DiscussionRouterViewData.kt b/libs/pandautils/src/main/java/com/instructure/pandautils/features/discussion/router/DiscussionRouterViewData.kt
index bcbe1ab888..ba77273da1 100644
--- a/libs/pandautils/src/main/java/com/instructure/pandautils/features/discussion/router/DiscussionRouterViewData.kt
+++ b/libs/pandautils/src/main/java/com/instructure/pandautils/features/discussion/router/DiscussionRouterViewData.kt
@@ -8,7 +8,8 @@ sealed class DiscussionRouterAction {
data class RouteToDiscussion(
val canvasContext: CanvasContext,
val isRedesignEnabled: Boolean,
- val discussionTopicHeader: DiscussionTopicHeader
+ val discussionTopicHeader: DiscussionTopicHeader,
+ val isAnnouncement: Boolean
) : DiscussionRouterAction()
data class RouteToGroupDiscussion(
diff --git a/libs/pandautils/src/main/java/com/instructure/pandautils/features/discussion/router/DiscussionRouterViewModel.kt b/libs/pandautils/src/main/java/com/instructure/pandautils/features/discussion/router/DiscussionRouterViewModel.kt
index 06c73cc339..bcb04d563a 100644
--- a/libs/pandautils/src/main/java/com/instructure/pandautils/features/discussion/router/DiscussionRouterViewModel.kt
+++ b/libs/pandautils/src/main/java/com/instructure/pandautils/features/discussion/router/DiscussionRouterViewModel.kt
@@ -27,7 +27,8 @@ class DiscussionRouterViewModel @Inject constructor(
fun route(
canvasContext: CanvasContext,
discussionTopicHeader: DiscussionTopicHeader?,
- discussionTopicHeaderId: Long
+ discussionTopicHeaderId: Long,
+ isAnnouncement: Boolean
) {
viewModelScope.launch {
try {
@@ -47,9 +48,9 @@ class DiscussionRouterViewModel @Inject constructor(
groupDiscussionHeader,
discussionRedesignEnabled
)
- } ?: routeToDiscussion(canvasContext, header, discussionRedesignEnabled)
+ } ?: routeToDiscussion(canvasContext, header, discussionRedesignEnabled, isAnnouncement)
} else {
- routeToDiscussion(canvasContext, header, discussionRedesignEnabled)
+ routeToDiscussion(canvasContext, header, discussionRedesignEnabled, isAnnouncement)
}
} catch (e: Exception) {
@@ -80,14 +81,16 @@ class DiscussionRouterViewModel @Inject constructor(
private fun routeToDiscussion(
canvasContext: CanvasContext,
header: DiscussionTopicHeader,
- discussionRedesignEnabled: Boolean
+ discussionRedesignEnabled: Boolean,
+ isAnnouncement: Boolean
) {
_events.postValue(
Event(
DiscussionRouterAction.RouteToDiscussion(
canvasContext,
discussionRedesignEnabled,
- header
+ header,
+ isAnnouncement
)
)
)
diff --git a/libs/pandautils/src/test/java/com/instructure/pandautils/features/discussion/router/DiscussionRouterViewModelTest.kt b/libs/pandautils/src/test/java/com/instructure/pandautils/features/discussion/router/DiscussionRouterViewModelTest.kt
index bd162caaaf..3f5c6d70f7 100644
--- a/libs/pandautils/src/test/java/com/instructure/pandautils/features/discussion/router/DiscussionRouterViewModelTest.kt
+++ b/libs/pandautils/src/test/java/com/instructure/pandautils/features/discussion/router/DiscussionRouterViewModelTest.kt
@@ -62,10 +62,10 @@ class DiscussionRouterViewModelTest {
viewModel.events.observe(lifecycleOwner) {}
- viewModel.route(course, discussionTopicHeader, 1L)
+ viewModel.route(course, discussionTopicHeader, 1L, false)
assertEquals(
- DiscussionRouterAction.RouteToDiscussion(course, false, discussionTopicHeader),
+ DiscussionRouterAction.RouteToDiscussion(course, false, discussionTopicHeader, false),
viewModel.events.value?.getContentIfNotHandled()
)
}
@@ -79,10 +79,10 @@ class DiscussionRouterViewModelTest {
viewModel.events.observe(lifecycleOwner) {}
- viewModel.route(course, discussionTopicHeader, 1L)
+ viewModel.route(course, discussionTopicHeader, 1L, false)
assertEquals(
- DiscussionRouterAction.RouteToDiscussion(course, true, discussionTopicHeader),
+ DiscussionRouterAction.RouteToDiscussion(course, true, discussionTopicHeader, false),
viewModel.events.value?.getContentIfNotHandled()
)
}
@@ -99,7 +99,7 @@ class DiscussionRouterViewModelTest {
viewModel.events.observe(lifecycleOwner) {}
- viewModel.route(group, discussionTopicHeader, 1L)
+ viewModel.route(group, discussionTopicHeader, 1L, false)
assertEquals(
DiscussionRouterAction.RouteToGroupDiscussion(group, 2L, groupDiscussionTopicHeader, false),
@@ -119,7 +119,7 @@ class DiscussionRouterViewModelTest {
viewModel.events.observe(lifecycleOwner) {}
- viewModel.route(group, discussionTopicHeader, 1L)
+ viewModel.route(group, discussionTopicHeader, 1L, false)
assertEquals(
DiscussionRouterAction.RouteToGroupDiscussion(group, 2L, groupDiscussionTopicHeader, true),
@@ -136,10 +136,10 @@ class DiscussionRouterViewModelTest {
viewModel.events.observe(lifecycleOwner) {}
- viewModel.route(course, null, 1L)
+ viewModel.route(course, null, 1L, false)
assertEquals(
- DiscussionRouterAction.RouteToDiscussion(course, true, discussionTopicHeader),
+ DiscussionRouterAction.RouteToDiscussion(course, true, discussionTopicHeader, false),
viewModel.events.value?.getContentIfNotHandled()
)
}
@@ -153,10 +153,10 @@ class DiscussionRouterViewModelTest {
viewModel.events.observe(lifecycleOwner) {}
- viewModel.route(group, discussionTopicHeader, 1L)
+ viewModel.route(group, discussionTopicHeader, 1L, false)
assertEquals(
- DiscussionRouterAction.RouteToDiscussion(group, true, discussionTopicHeader),
+ DiscussionRouterAction.RouteToDiscussion(group, true, discussionTopicHeader, false),
viewModel.events.value?.getContentIfNotHandled()
)
}
@@ -169,7 +169,7 @@ class DiscussionRouterViewModelTest {
viewModel.events.observe(lifecycleOwner) {}
- viewModel.route(course, null, 1L)
+ viewModel.route(course, null, 1L, false)
assertEquals(
DiscussionRouterAction.ShowToast("Error occurred. The topic may no longer be available."),
From e6c8073f4875080ece94b1c2c06513bf42a90dc1 Mon Sep 17 00:00:00 2001
From: Akos Hermann <72087159+hermannakos@users.noreply.github.com>
Date: Wed, 23 Nov 2022 12:12:26 +0100
Subject: [PATCH 54/73] [MBL-16391][Student][Teacher] Fix Dashboard invites for
multi role #1782
refs: MBL-16391
affects: Student, Teacher
release note: Fixed a bug regarding the invitations on the Dashboard.
test plan: See ticket.
* fix tests
---
.../student/di/DashboardNotificationModule.kt | 31 +++++
.../StudentDashboardNotificationRepository.kt | 18 +++
.../StudentEditDashboardRepositoryTest.kt | 6 +-
.../teacher/di/DashboardNotificationModule.kt | 31 +++++
.../TeacherDashboardNotificationRepository.kt | 18 +++
.../DashboardNotificationRepository.kt | 39 +++++++
.../DashboardNotificationsViewModel.kt | 24 ++--
.../DashboardNotificationsViewModelTest.kt | 107 ++++++------------
8 files changed, 183 insertions(+), 91 deletions(-)
create mode 100644 apps/student/src/main/java/com/instructure/student/di/DashboardNotificationModule.kt
create mode 100644 apps/student/src/main/java/com/instructure/student/features/dashboard/notifications/StudentDashboardNotificationRepository.kt
create mode 100644 apps/teacher/src/main/java/com/instructure/teacher/di/DashboardNotificationModule.kt
create mode 100644 apps/teacher/src/main/java/com/instructure/teacher/features/dashboard/notifications/TeacherDashboardNotificationRepository.kt
create mode 100644 libs/pandautils/src/main/java/com/instructure/pandautils/features/dashboard/notifications/DashboardNotificationRepository.kt
diff --git a/apps/student/src/main/java/com/instructure/student/di/DashboardNotificationModule.kt b/apps/student/src/main/java/com/instructure/student/di/DashboardNotificationModule.kt
new file mode 100644
index 0000000000..7c1cd5f5ff
--- /dev/null
+++ b/apps/student/src/main/java/com/instructure/student/di/DashboardNotificationModule.kt
@@ -0,0 +1,31 @@
+package com.instructure.student.di
+
+import com.instructure.canvasapi2.managers.*
+import com.instructure.pandautils.features.dashboard.notifications.DashboardNotificationRepository
+import com.instructure.student.features.dashboard.notifications.StudentDashboardNotificationRepository
+import dagger.Module
+import dagger.Provides
+import dagger.hilt.InstallIn
+import dagger.hilt.android.components.ViewModelComponent
+
+@Module
+@InstallIn(ViewModelComponent::class)
+class DashboardNotificationModule {
+
+ @Provides
+ fun provideDashboardNotificationRepository(
+ courseManager: CourseManager,
+ groupManager: GroupManager,
+ enrollmentManager: EnrollmentManager,
+ conferenceManager: ConferenceManager,
+ accountNotificationManager: AccountNotificationManager,
+ ): DashboardNotificationRepository {
+ return StudentDashboardNotificationRepository(
+ courseManager,
+ groupManager,
+ enrollmentManager,
+ conferenceManager,
+ accountNotificationManager
+ )
+ }
+}
\ No newline at end of file
diff --git a/apps/student/src/main/java/com/instructure/student/features/dashboard/notifications/StudentDashboardNotificationRepository.kt b/apps/student/src/main/java/com/instructure/student/features/dashboard/notifications/StudentDashboardNotificationRepository.kt
new file mode 100644
index 0000000000..c1adff0a58
--- /dev/null
+++ b/apps/student/src/main/java/com/instructure/student/features/dashboard/notifications/StudentDashboardNotificationRepository.kt
@@ -0,0 +1,18 @@
+package com.instructure.student.features.dashboard.notifications
+
+import com.instructure.canvasapi2.managers.*
+import com.instructure.canvasapi2.models.Course
+import com.instructure.pandautils.features.dashboard.notifications.DashboardNotificationRepository
+
+class StudentDashboardNotificationRepository(
+ private val courseManager: CourseManager,
+ groupManager: GroupManager,
+ enrollmentManager: EnrollmentManager,
+ conferenceManager: ConferenceManager,
+ accountNotificationManager: AccountNotificationManager,
+) : DashboardNotificationRepository(groupManager, enrollmentManager, conferenceManager, accountNotificationManager) {
+
+ override suspend fun getCourses(forceNetwork: Boolean): List? {
+ return courseManager.getCoursesAsync(forceNetwork).await().dataOrNull?.filter { it.isStudent }
+ }
+}
\ No newline at end of file
diff --git a/apps/student/src/test/java/com/instructure/student/features/dashboard/edit/StudentEditDashboardRepositoryTest.kt b/apps/student/src/test/java/com/instructure/student/features/dashboard/edit/StudentEditDashboardRepositoryTest.kt
index 3073d91692..c3579469f2 100644
--- a/apps/student/src/test/java/com/instructure/student/features/dashboard/edit/StudentEditDashboardRepositoryTest.kt
+++ b/apps/student/src/test/java/com/instructure/student/features/dashboard/edit/StudentEditDashboardRepositoryTest.kt
@@ -51,9 +51,9 @@ class StudentEditDashboardRepositoryTest {
@Test
fun `Returns courses when fetching courses`() = runBlockingTest {
// Given
- val coursesActive = listOf(Course(id = 1L, name = "Course"))
- val coursesCompleted = listOf(Course(id = 2L, name = "Course"))
- val coursesInvitedOrPending = listOf(Course(id = 3L, name = "Course"))
+ val coursesActive = listOf(Course(id = 1L, name = "Course", enrollments = mutableListOf(Enrollment(type = Enrollment.EnrollmentType.Student))))
+ val coursesCompleted = listOf(Course(id = 2L, name = "Course", enrollments = mutableListOf(Enrollment(type = Enrollment.EnrollmentType.Student))))
+ val coursesInvitedOrPending = listOf(Course(id = 3L, name = "Course", enrollments = mutableListOf(Enrollment(type = Enrollment.EnrollmentType.Student))))
val coursesDeferred: Deferred>> = mockk()
every { courseManager.getCoursesByEnrollmentStateAsync(any(), any()) } returns coursesDeferred
diff --git a/apps/teacher/src/main/java/com/instructure/teacher/di/DashboardNotificationModule.kt b/apps/teacher/src/main/java/com/instructure/teacher/di/DashboardNotificationModule.kt
new file mode 100644
index 0000000000..156b4a3026
--- /dev/null
+++ b/apps/teacher/src/main/java/com/instructure/teacher/di/DashboardNotificationModule.kt
@@ -0,0 +1,31 @@
+package com.instructure.teacher.di
+
+import com.instructure.canvasapi2.managers.*
+import com.instructure.pandautils.features.dashboard.notifications.DashboardNotificationRepository
+import com.instructure.teacher.features.dashboard.notifications.TeacherDashboardNotificationRepository
+import dagger.Module
+import dagger.Provides
+import dagger.hilt.InstallIn
+import dagger.hilt.android.components.ViewModelComponent
+
+@Module
+@InstallIn(ViewModelComponent::class)
+class DashboardNotificationModule {
+
+ @Provides
+ fun provideDashboardNotificationRepository(
+ courseManager: CourseManager,
+ groupManager: GroupManager,
+ enrollmentManager: EnrollmentManager,
+ conferenceManager: ConferenceManager,
+ accountNotificationManager: AccountNotificationManager
+ ): DashboardNotificationRepository {
+ return TeacherDashboardNotificationRepository(
+ courseManager,
+ groupManager,
+ enrollmentManager,
+ conferenceManager,
+ accountNotificationManager
+ )
+ }
+}
\ No newline at end of file
diff --git a/apps/teacher/src/main/java/com/instructure/teacher/features/dashboard/notifications/TeacherDashboardNotificationRepository.kt b/apps/teacher/src/main/java/com/instructure/teacher/features/dashboard/notifications/TeacherDashboardNotificationRepository.kt
new file mode 100644
index 0000000000..709a5057a1
--- /dev/null
+++ b/apps/teacher/src/main/java/com/instructure/teacher/features/dashboard/notifications/TeacherDashboardNotificationRepository.kt
@@ -0,0 +1,18 @@
+package com.instructure.teacher.features.dashboard.notifications
+
+import com.instructure.canvasapi2.managers.*
+import com.instructure.canvasapi2.models.Course
+import com.instructure.pandautils.features.dashboard.notifications.DashboardNotificationRepository
+
+class TeacherDashboardNotificationRepository(
+ private val courseManager: CourseManager,
+ groupManager: GroupManager,
+ enrollmentManager: EnrollmentManager,
+ conferenceManager: ConferenceManager,
+ accountNotificationManager: AccountNotificationManager,
+) : DashboardNotificationRepository(groupManager, enrollmentManager, conferenceManager, accountNotificationManager) {
+ override suspend fun getCourses(forceNetwork: Boolean): List? {
+ return courseManager.getCoursesAsync(forceNetwork)
+ .await().dataOrNull?.filter { it.isTeacher || it.isDesigner || it.isTA }
+ }
+}
\ No newline at end of file
diff --git a/libs/pandautils/src/main/java/com/instructure/pandautils/features/dashboard/notifications/DashboardNotificationRepository.kt b/libs/pandautils/src/main/java/com/instructure/pandautils/features/dashboard/notifications/DashboardNotificationRepository.kt
new file mode 100644
index 0000000000..943c64f59d
--- /dev/null
+++ b/libs/pandautils/src/main/java/com/instructure/pandautils/features/dashboard/notifications/DashboardNotificationRepository.kt
@@ -0,0 +1,39 @@
+package com.instructure.pandautils.features.dashboard.notifications
+
+import com.instructure.canvasapi2.apis.EnrollmentAPI
+import com.instructure.canvasapi2.managers.AccountNotificationManager
+import com.instructure.canvasapi2.managers.ConferenceManager
+import com.instructure.canvasapi2.managers.EnrollmentManager
+import com.instructure.canvasapi2.managers.GroupManager
+import com.instructure.canvasapi2.models.*
+
+abstract class DashboardNotificationRepository(
+ private val groupManager: GroupManager,
+ private val enrollmentManager: EnrollmentManager,
+ private val conferenceManager: ConferenceManager,
+ private val accountNotificationManager: AccountNotificationManager,
+) {
+
+ abstract suspend fun getCourses(forceNetwork: Boolean): List?
+
+ suspend fun getGroups(forceNetwork: Boolean): List? {
+ return groupManager.getAllGroupsAsync(forceNetwork).await().dataOrNull
+ }
+
+ suspend fun getInvitations(forceNetwork: Boolean): List? {
+ return enrollmentManager.getSelfEnrollmentsAsync(
+ null,
+ listOf(EnrollmentAPI.STATE_INVITED, EnrollmentAPI.STATE_CURRENT_AND_FUTURE),
+ forceNetwork
+ ).await()
+ .dataOrNull
+ }
+
+ suspend fun getAccountNotifications(forceNetwork: Boolean): List? {
+ return accountNotificationManager.getAllAccountNotificationsAsync(forceNetwork).await().dataOrNull
+ }
+
+ suspend fun getConferences(forceNetwork: Boolean): List? {
+ return conferenceManager.getLiveConferencesAsync(forceNetwork).await().dataOrNull
+ }
+}
\ No newline at end of file
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 6c5a6cad81..e6b37229c9 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
@@ -26,7 +26,9 @@ import androidx.work.WorkInfo
import androidx.work.WorkManager
import androidx.work.await
import com.instructure.canvasapi2.apis.EnrollmentAPI
-import com.instructure.canvasapi2.managers.*
+import com.instructure.canvasapi2.managers.AccountNotificationManager
+import com.instructure.canvasapi2.managers.EnrollmentManager
+import com.instructure.canvasapi2.managers.OAuthManager
import com.instructure.canvasapi2.models.*
import com.instructure.canvasapi2.utils.ApiPrefs
import com.instructure.canvasapi2.utils.isValidTerm
@@ -53,10 +55,8 @@ import javax.inject.Inject
@HiltViewModel
class DashboardNotificationsViewModel @Inject constructor(
private val resources: Resources,
- private val courseManager: CourseManager,
- private val groupManager: GroupManager,
private val enrollmentManager: EnrollmentManager,
- private val conferenceManager: ConferenceManager,
+ private val dashboardNotificationRepository: DashboardNotificationRepository,
private val accountNotificationManager: AccountNotificationManager,
private val oauthManager: OAuthManager,
private val conferenceDashboardBlacklist: ConferenceDashboardBlacklist,
@@ -103,8 +103,8 @@ class DashboardNotificationsViewModel @Inject constructor(
val items = mutableListOf()
- val courses = courseManager.getCoursesAsync(forceNetwork).await().dataOrNull
- val groups = groupManager.getAllGroupsAsync(forceNetwork).await().dataOrNull
+ val courses = dashboardNotificationRepository.getCourses(forceNetwork)
+ val groups = dashboardNotificationRepository.getGroups(forceNetwork)
coursesMap = courses?.associateBy { it.id } ?: emptyMap()
@@ -126,8 +126,7 @@ class DashboardNotificationsViewModel @Inject constructor(
}
private suspend fun getAccountNotifications(forceNetwork: Boolean): List {
- val accountNotifications =
- accountNotificationManager.getAllAccountNotificationsAsync(forceNetwork).await().dataOrNull
+ val accountNotifications = dashboardNotificationRepository.getAccountNotifications(forceNetwork)
return createAccountNotificationViewModels(accountNotifications)
}
@@ -165,7 +164,7 @@ class DashboardNotificationsViewModel @Inject constructor(
private suspend fun getConferences(forceNetwork: Boolean): List {
val blackList = conferenceDashboardBlacklist.conferenceDashboardBlacklist
- val conferences = conferenceManager.getLiveConferencesAsync(forceNetwork).await().dataOrNull
+ val conferences = dashboardNotificationRepository.getConferences(forceNetwork)
?.filter { conference ->
// Remove blacklisted (i.e. 'dismissed') conferences
blackList.contains(conference.id.toString()).not()
@@ -196,12 +195,7 @@ class DashboardNotificationsViewModel @Inject constructor(
}
private suspend fun getInvitations(forceNetwork: Boolean): List {
- val invites = enrollmentManager.getSelfEnrollmentsAsync(
- null,
- listOf(EnrollmentAPI.STATE_INVITED, EnrollmentAPI.STATE_CURRENT_AND_FUTURE),
- forceNetwork
- ).await()
- .dataOrNull
+ val invites = dashboardNotificationRepository.getInvitations(forceNetwork)
?.filter { it.enrollmentState == EnrollmentAPI.STATE_INVITED && hasValidCourseForEnrollment(it) }
return createInvitationViewModels(invites) ?: emptyList()
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 24914afcc9..a3e4d24eb0 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
@@ -26,7 +26,9 @@ import androidx.work.WorkInfo
import androidx.work.WorkManager
import com.google.common.util.concurrent.Futures
import com.instructure.canvasapi2.apis.EnrollmentAPI
-import com.instructure.canvasapi2.managers.*
+import com.instructure.canvasapi2.managers.AccountNotificationManager
+import com.instructure.canvasapi2.managers.EnrollmentManager
+import com.instructure.canvasapi2.managers.OAuthManager
import com.instructure.canvasapi2.models.*
import com.instructure.canvasapi2.utils.ApiPrefs
import com.instructure.canvasapi2.utils.ContextKeeper
@@ -64,15 +66,13 @@ class DashboardNotificationsViewModelTest {
private val testDispatcher = TestCoroutineDispatcher()
private val resources: Resources = mockk(relaxed = true)
- private val courseManager: CourseManager = mockk(relaxed = true)
- private val groupManager: GroupManager = mockk(relaxed = true)
private val enrollmentManager: EnrollmentManager = mockk(relaxed = true)
- private val conferenceManager: ConferenceManager = mockk(relaxed = true)
private val accountNotificationManager: AccountNotificationManager = mockk(relaxed = true)
private val oauthManager: OAuthManager = mockk(relaxed = true)
private val conferenceDashboardBlacklist: ConferenceDashboardBlacklist = mockk(relaxed = true)
private val apiPrefs: ApiPrefs = mockk(relaxed = true)
private val workManager: WorkManager = mockk(relaxed = true)
+ private val dashboardNotificationRepository: DashboardNotificationRepository = mockk(relaxed = true)
private lateinit var viewModel: DashboardNotificationsViewModel
@@ -89,25 +89,15 @@ class DashboardNotificationsViewModelTest {
every { conferenceDashboardBlacklist.conferenceDashboardBlacklist } returns emptySet()
- every { enrollmentManager.getSelfEnrollmentsAsync(any(), any(), any()) } returns mockk {
- coEvery { await() } returns DataResult.Success(emptyList())
- }
+ coEvery { dashboardNotificationRepository.getInvitations(any()) } returns emptyList()
- every { conferenceManager.getLiveConferencesAsync(any()) } returns mockk {
- coEvery { await() } returns DataResult.Success(emptyList())
- }
+ coEvery { dashboardNotificationRepository.getConferences(any()) } returns emptyList()
- every { accountNotificationManager.getAllAccountNotificationsAsync(any()) } returns mockk {
- coEvery { await() } returns DataResult.Success(emptyList())
- }
+ coEvery { dashboardNotificationRepository.getAccountNotifications(any()) } returns emptyList()
- every { courseManager.getCoursesAsync(any()) } returns mockk {
- coEvery { await() } returns DataResult.Success(emptyList())
- }
+ coEvery { dashboardNotificationRepository.getCourses(any()) } returns emptyList()
- every { groupManager.getAllGroupsAsync(any()) } returns mockk {
- coEvery { await() } returns DataResult.Success(emptyList())
- }
+ coEvery { dashboardNotificationRepository.getGroups(any()) } returns emptyList()
mockkObject(FileUploadPreferences)
every { FileUploadPreferences.getRunningWorkerIds() } returns emptyList()
@@ -115,10 +105,8 @@ class DashboardNotificationsViewModelTest {
viewModel = DashboardNotificationsViewModel(
resources,
- courseManager,
- groupManager,
enrollmentManager,
- conferenceManager,
+ dashboardNotificationRepository,
accountNotificationManager,
oauthManager,
conferenceDashboardBlacklist,
@@ -184,9 +172,7 @@ class DashboardNotificationsViewModelTest {
),
)
- every { accountNotificationManager.getAllAccountNotificationsAsync(any()) } returns mockk {
- coEvery { await() } returns DataResult.Success(accountNotifications)
- }
+ coEvery { dashboardNotificationRepository.getAccountNotifications(any()) } returns accountNotifications
viewModel.loadData(true)
@@ -207,9 +193,7 @@ class DashboardNotificationsViewModelTest {
val expectedData = DashboardNotificationsActions.OpenAnnouncement("AC1 subject", "AC1 message")
- every { accountNotificationManager.getAllAccountNotificationsAsync(any()) } returns mockk {
- coEvery { await() } returns DataResult.Success(accountNotifications)
- }
+ coEvery { dashboardNotificationRepository.getAccountNotifications(any()) } returns accountNotifications
viewModel.loadData(true)
@@ -252,13 +236,9 @@ class DashboardNotificationsViewModelTest {
)
)
- every { courseManager.getCoursesAsync(any()) } returns mockk {
- coEvery { await() } returns DataResult.Success(courses)
- }
+ coEvery { dashboardNotificationRepository.getCourses(any()) } returns courses
- every { enrollmentManager.getSelfEnrollmentsAsync(any(), any(), any()) } returns mockk {
- coEvery { await() } returns DataResult.Success(enrolments)
- }
+ coEvery { dashboardNotificationRepository.getInvitations(any()) } returns enrolments
viewModel.loadData(true)
@@ -274,13 +254,9 @@ class DashboardNotificationsViewModelTest {
val enrolment = Enrollment(id = 1, courseId = 1, enrollmentState = EnrollmentAPI.STATE_INVITED)
- every { courseManager.getCoursesAsync(any()) } returns mockk {
- coEvery { await() } returns DataResult.Success(listOf(course))
- }
+ coEvery { dashboardNotificationRepository.getCourses(any()) } returns listOf(course)
- every { enrollmentManager.getSelfEnrollmentsAsync(any(), any(), any()) } returns mockk {
- coEvery { await() } returns DataResult.Success(listOf(enrolment))
- }
+ coEvery { dashboardNotificationRepository.getInvitations(any()) } returns listOf(enrolment)
every { enrollmentManager.handleInviteAsync(any(), any(), any()) } returns mockk {
coEvery { await() } returns DataResult.Success(Unit)
@@ -304,13 +280,9 @@ class DashboardNotificationsViewModelTest {
val enrolment = Enrollment(id = 1, courseId = 1, enrollmentState = EnrollmentAPI.STATE_INVITED)
- every { courseManager.getCoursesAsync(any()) } returns mockk {
- coEvery { await() } returns DataResult.Success(listOf(course))
- }
+ coEvery { dashboardNotificationRepository.getCourses(any()) } returns listOf(course)
- every { enrollmentManager.getSelfEnrollmentsAsync(any(), any(), any()) } returns mockk {
- coEvery { await() } returns DataResult.Success(listOf(enrolment))
- }
+ coEvery { dashboardNotificationRepository.getInvitations(any()) } returns listOf(enrolment)
every { enrollmentManager.handleInviteAsync(any(), any(), any()) } returns mockk {
coEvery { await() } returns DataResult.Success(Unit)
@@ -334,13 +306,9 @@ class DashboardNotificationsViewModelTest {
val enrolment = Enrollment(id = 1, courseId = 1, enrollmentState = EnrollmentAPI.STATE_INVITED)
- every { courseManager.getCoursesAsync(any()) } returns mockk {
- coEvery { await() } returns DataResult.Success(listOf(course))
- }
+ coEvery { dashboardNotificationRepository.getCourses(any()) } returns listOf(course)
- every { enrollmentManager.getSelfEnrollmentsAsync(any(), any(), any()) } returns mockk {
- coEvery { await() } returns DataResult.Success(listOf(enrolment))
- }
+ coEvery { dashboardNotificationRepository.getInvitations(any()) } returns listOf(enrolment)
every { enrollmentManager.handleInviteAsync(any(), any(), any()) } returns mockk {
coEvery { await() } returns DataResult.Fail()
@@ -364,9 +332,7 @@ class DashboardNotificationsViewModelTest {
fun `Dismissed conference is not visible`() {
val conference = Conference(id = 1, title = "Conference")
- every { conferenceManager.getLiveConferencesAsync(any()) } returns mockk {
- coEvery { await() } returns DataResult.Success(listOf(conference))
- }
+ coEvery { dashboardNotificationRepository.getConferences(any()) } returns listOf(conference)
every { conferenceDashboardBlacklist.conferenceDashboardBlacklist } returns setOf("1")
@@ -379,9 +345,7 @@ class DashboardNotificationsViewModelTest {
fun `Open conference`() {
val conference = Conference(id = 1, title = "Conference", joinUrl = "https://notAuthenticatedSession.com")
- every { conferenceManager.getLiveConferencesAsync(any()) } returns mockk {
- coEvery { await() } returns DataResult.Success(listOf(conference))
- }
+ coEvery { dashboardNotificationRepository.getConferences(any()) } returns listOf(conference)
every { oauthManager.getAuthenticatedSessionAsync(any()) } returns mockk {
coEvery { await() } returns DataResult.Success(AuthenticatedSession("https://authenticatedSession.com"))
@@ -422,13 +386,8 @@ class DashboardNotificationsViewModelTest {
ConferenceViewData(subtitle = "Invited course", conference = conferences[0]),
)
- every { conferenceManager.getLiveConferencesAsync(any()) } returns mockk {
- coEvery { await() } returns DataResult.Success(conferences)
- }
-
- every { courseManager.getCoursesAsync(any()) } returns mockk {
- coEvery { await() } returns DataResult.Success(courses)
- }
+ coEvery { dashboardNotificationRepository.getConferences(any()) } returns conferences
+ coEvery { dashboardNotificationRepository.getCourses(any()) } returns courses
viewModel.loadData(true)
@@ -481,14 +440,16 @@ class DashboardNotificationsViewModelTest {
every { FileUploadPreferences.getRunningWorkerIds() } returns listOf(workerId)
every { FileUploadPreferences.getRunningWorkersLiveData() } returns MutableLiveData(listOf(workerId))
- every { workManager.getWorkInfoById(workerId) } returns Futures.immediateFuture(WorkInfo(
- workerId,
- WorkInfo.State.RUNNING,
- Data.EMPTY,
- emptyList(),
- Data.EMPTY,
- 1
- ))
+ every { workManager.getWorkInfoById(workerId) } returns Futures.immediateFuture(
+ WorkInfo(
+ workerId,
+ WorkInfo.State.RUNNING,
+ Data.EMPTY,
+ emptyList(),
+ Data.EMPTY,
+ 1
+ )
+ )
viewModel.loadData()
assertEquals(false, viewModel.data.value?.uploadItems?.isEmpty())
From 91ef21e168c5d461d94f470ca926e61bbbc90ef4 Mon Sep 17 00:00:00 2001
From: Kristof Deak <92309696+kdeakinstructure@users.noreply.github.com>
Date: Wed, 23 Nov 2022 12:41:41 +0100
Subject: [PATCH 55/73] Fix some nightly test #1783
---
.../com/instructure/student/ui/e2e/k5/ScheduleE2ETest.kt | 7 +++++--
.../java/com/instructure/student/ui/pages/SchedulePage.kt | 2 +-
.../java/com/instructure/teacher/ui/e2e/SettingsE2ETest.kt | 3 ++-
.../teacher/ui/pages/EditAssignmentDetailsPage.kt | 2 +-
4 files changed, 9 insertions(+), 5 deletions(-)
diff --git a/apps/student/src/androidTest/java/com/instructure/student/ui/e2e/k5/ScheduleE2ETest.kt b/apps/student/src/androidTest/java/com/instructure/student/ui/e2e/k5/ScheduleE2ETest.kt
index f9e09aa62f..e99fe2334b 100644
--- a/apps/student/src/androidTest/java/com/instructure/student/ui/e2e/k5/ScheduleE2ETest.kt
+++ b/apps/student/src/androidTest/java/com/instructure/student/ui/e2e/k5/ScheduleE2ETest.kt
@@ -35,7 +35,8 @@ import com.instructure.student.ui.utils.StudentTest
import com.instructure.student.ui.utils.seedDataForK5
import com.instructure.student.ui.utils.tokenLoginElementary
import dagger.hilt.android.testing.HiltAndroidTest
-import org.junit.Test
+import org.junit.Rule
+import org.junit.rules.Timeout
import java.util.*
@HiltAndroidTest
@@ -45,8 +46,10 @@ class ScheduleE2ETest : StudentTest() {
override fun enableAndConfigureAccessibilityChecks() = Unit
+ @Rule
+ var globalTimeout: Timeout = Timeout.millis(600000) // //TODO: workaround for that sometimes this test is running infinite time because of scrollToElement does not find an element.
+
@E2E
- @Test(timeout = 600000) //TODO: workaround for that sometimes this test is running infinite time because of scrollToElement does not find an element.
@TestMetaData(Priority.MANDATORY, FeatureCategory.K5_DASHBOARD, TestCategory.E2E)
fun scheduleE2ETest() {
diff --git a/apps/student/src/androidTest/java/com/instructure/student/ui/pages/SchedulePage.kt b/apps/student/src/androidTest/java/com/instructure/student/ui/pages/SchedulePage.kt
index af737178c8..3fd513c5fb 100644
--- a/apps/student/src/androidTest/java/com/instructure/student/ui/pages/SchedulePage.kt
+++ b/apps/student/src/androidTest/java/com/instructure/student/ui/pages/SchedulePage.kt
@@ -163,7 +163,7 @@ class SchedulePage : BasePage(R.id.schedulePage) {
}
fun assertMarkedAsDoneShown() {
- onViewWithText(R.string.schedule_marked_as_done).assertDisplayed()
+ waitForViewWithText(R.string.schedule_marked_as_done).assertDisplayed()
}
fun assertMarkedAsDoneNotShown() {
diff --git a/apps/teacher/src/androidTest/java/com/instructure/teacher/ui/e2e/SettingsE2ETest.kt b/apps/teacher/src/androidTest/java/com/instructure/teacher/ui/e2e/SettingsE2ETest.kt
index 472f481bee..67b4393b2a 100644
--- a/apps/teacher/src/androidTest/java/com/instructure/teacher/ui/e2e/SettingsE2ETest.kt
+++ b/apps/teacher/src/androidTest/java/com/instructure/teacher/ui/e2e/SettingsE2ETest.kt
@@ -22,6 +22,7 @@ import androidx.test.espresso.NoMatchingViewException
import com.instructure.canvas.espresso.E2E
import com.instructure.canvasapi2.utils.RemoteConfigParam
import com.instructure.canvasapi2.utils.RemoteConfigUtils
+import com.instructure.espresso.ViewUtils
import com.instructure.panda_annotations.FeatureCategory
import com.instructure.panda_annotations.Priority
import com.instructure.panda_annotations.TestCategory
@@ -87,7 +88,7 @@ class SettingsE2ETest : TeacherTest() {
Log.d(STEP_TAG,"Edit username to 'Unsaved userName' but DO NOT CLICK ON SAVE. Navigate back to Profile Settings Page without saving.")
editProfileSettingsPage.editUserName("Unsaved userName")
- Espresso.pressBack()
+ ViewUtils.pressBackButton(2)
Log.d(STEP_TAG,"Assert that the username value remained $newUserName.")
profileSettingsPage.assertUserNameIs(newUserName)
diff --git a/apps/teacher/src/androidTest/java/com/instructure/teacher/ui/pages/EditAssignmentDetailsPage.kt b/apps/teacher/src/androidTest/java/com/instructure/teacher/ui/pages/EditAssignmentDetailsPage.kt
index 56d6ab030e..a8dbb7bba7 100644
--- a/apps/teacher/src/androidTest/java/com/instructure/teacher/ui/pages/EditAssignmentDetailsPage.kt
+++ b/apps/teacher/src/androidTest/java/com/instructure/teacher/ui/pages/EditAssignmentDetailsPage.kt
@@ -136,7 +136,7 @@ class EditAssignmentDetailsPage : BasePage() {
}
fun clickOnDisplayGradeAsSpinner() {
- onView(withId(R.id.displayGradeAsSpinner)).click()
+ onView(withId(R.id.displayGradeAsSpinner)).scrollTo().click()
}
fun selectGradeType(gradeType: String) {
From 626dca49f5581ee4c1e6f434ffa48f8f8d6a1381 Mon Sep 17 00:00:00 2001
From: Kristof Deak <92309696+kdeakinstructure@users.noreply.github.com>
Date: Thu, 24 Nov 2022 10:00:10 +0100
Subject: [PATCH 56/73] Nightly fix branch (#1785)
---
.../student/ui/e2e/ShareExtensionE2ETest.kt | 3 +--
.../instructure/student/ui/pages/LoginSignInPage.kt | 2 +-
.../instructure/teacher/ui/pages/AddMessagePage.kt | 4 +---
.../teacher/ui/pages/ChooseRecipientsPage.kt | 2 +-
.../instructure/teacher/ui/pages/InboxMessagePage.kt | 2 +-
.../com/instructure/teacher/ui/pages/InboxPage.kt | 2 +-
.../instructure/teacher/ui/pages/LoginSignInPage.kt | 7 ++++---
.../instructure/teacher/ui/pages/WebViewLoginPage.kt | 6 ++++--
.../espresso/matchers/WaitForViewMatcher.kt | 10 +++++-----
.../kotlin/com/instructure/espresso/page/BasePage.kt | 12 ++++++------
10 files changed, 25 insertions(+), 25 deletions(-)
diff --git a/apps/student/src/androidTest/java/com/instructure/student/ui/e2e/ShareExtensionE2ETest.kt b/apps/student/src/androidTest/java/com/instructure/student/ui/e2e/ShareExtensionE2ETest.kt
index 1619472d18..b529f34c06 100644
--- a/apps/student/src/androidTest/java/com/instructure/student/ui/e2e/ShareExtensionE2ETest.kt
+++ b/apps/student/src/androidTest/java/com/instructure/student/ui/e2e/ShareExtensionE2ETest.kt
@@ -113,13 +113,12 @@ class ShareExtensionE2ETest: StudentTest() {
Log.d(STEP_TAG, "Assert that the File Upload page is displayed with the corresponding title.")
fileUploadPage.assertPageObjects()
fileUploadPage.assertDialogTitle("Submission")
- device.waitForIdle()
Log.d(STEP_TAG, "Click on 'Turn In' button to upload both of the files.")
fileUploadPage.clickTurnIn()
Log.d(STEP_TAG, "Assert that the submission upload was successful.")
- shareExtensionStatusPage.assertPageObjects()
+ shareExtensionStatusPage.assertPageObjects(30)
shareExtensionStatusPage.assertAssignmentSubmissionSuccess()
Log.d(STEP_TAG, "Click on 'Done' button.")
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 9cf3a9ec64..7b1614edb1 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
@@ -66,7 +66,7 @@ class LoginSignInPage: BasePage() {
//region Assertion Helpers
- override fun assertPageObjects() {
+ override fun assertPageObjects(duration: Long) {
signInRoot.assertDisplayed()
toolbar.assertDisplayed()
diff --git a/apps/teacher/src/androidTest/java/com/instructure/teacher/ui/pages/AddMessagePage.kt b/apps/teacher/src/androidTest/java/com/instructure/teacher/ui/pages/AddMessagePage.kt
index 47dcce1596..4e9409fa35 100644
--- a/apps/teacher/src/androidTest/java/com/instructure/teacher/ui/pages/AddMessagePage.kt
+++ b/apps/teacher/src/androidTest/java/com/instructure/teacher/ui/pages/AddMessagePage.kt
@@ -16,8 +16,6 @@
*/
package com.instructure.teacher.ui.pages
-import android.util.Log
-import androidx.test.espresso.matcher.ViewMatchers
import com.google.android.material.chip.Chip
import com.instructure.canvas.espresso.typedViewCondition
import com.instructure.canvasapi2.models.Course
@@ -39,7 +37,7 @@ class AddMessagePage: BasePage() {
private val editSubjectEditText by WaitForViewWithId(R.id.editSubject)
private val addContactsButton by WaitForViewWithId(R.id.contactsImageButton)
- override fun assertPageObjects() {
+ override fun assertPageObjects(duration: Long) {
subjectTextView.assertDisplayed()
chipsInput.assertDisplayed()
}
diff --git a/apps/teacher/src/androidTest/java/com/instructure/teacher/ui/pages/ChooseRecipientsPage.kt b/apps/teacher/src/androidTest/java/com/instructure/teacher/ui/pages/ChooseRecipientsPage.kt
index 484f0b7a5d..d8f246a3a5 100644
--- a/apps/teacher/src/androidTest/java/com/instructure/teacher/ui/pages/ChooseRecipientsPage.kt
+++ b/apps/teacher/src/androidTest/java/com/instructure/teacher/ui/pages/ChooseRecipientsPage.kt
@@ -32,7 +32,7 @@ class ChooseRecipientsPage: BasePage() {
private val menuDone by WaitForViewWithId(R.id.menuDone)
private val checkBox by WaitForViewWithId(R.id.checkBox)
- override fun assertPageObjects() {
+ override fun assertPageObjects(duration: Long) {
toolbar.assertDisplayed()
recyclerView.assertDisplayed()
menuDone.assertDisplayed()
diff --git a/apps/teacher/src/androidTest/java/com/instructure/teacher/ui/pages/InboxMessagePage.kt b/apps/teacher/src/androidTest/java/com/instructure/teacher/ui/pages/InboxMessagePage.kt
index 80e488e201..3a2e5bbe23 100644
--- a/apps/teacher/src/androidTest/java/com/instructure/teacher/ui/pages/InboxMessagePage.kt
+++ b/apps/teacher/src/androidTest/java/com/instructure/teacher/ui/pages/InboxMessagePage.kt
@@ -38,7 +38,7 @@ class InboxMessagePage: BasePage() {
private val replyTextView by OnViewWithId(R.id.reply)
- override fun assertPageObjects() {
+ override fun assertPageObjects(duration: Long) {
starImageButton.assertDisplayed()
subjectTextView.assertDisplayed()
messageRecyclerView.assertDisplayed()
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 b48511e498..e74fbc6ae2 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
@@ -22,7 +22,7 @@ class InboxPage: BasePage() {
private val emptyPandaView by WaitForViewWithId(R.id.emptyPandaView)
private val filterText by OnViewWithId(R.id.filterText)
- override fun assertPageObjects() {
+ override fun assertPageObjects(duration: Long) {
toolbarTitle.assertDisplayed()
}
diff --git a/apps/teacher/src/androidTest/java/com/instructure/teacher/ui/pages/LoginSignInPage.kt b/apps/teacher/src/androidTest/java/com/instructure/teacher/ui/pages/LoginSignInPage.kt
index cf89d735c0..996d3c51b3 100644
--- a/apps/teacher/src/androidTest/java/com/instructure/teacher/ui/pages/LoginSignInPage.kt
+++ b/apps/teacher/src/androidTest/java/com/instructure/teacher/ui/pages/LoginSignInPage.kt
@@ -2,14 +2,15 @@ package com.instructure.teacher.ui.pages
import androidx.test.espresso.web.sugar.Web
import androidx.test.espresso.web.sugar.Web.onWebView
-import androidx.test.espresso.web.webdriver.DriverAtoms.*
+import androidx.test.espresso.web.webdriver.DriverAtoms.findElement
+import androidx.test.espresso.web.webdriver.DriverAtoms.webClick
+import androidx.test.espresso.web.webdriver.DriverAtoms.webKeys
import androidx.test.espresso.web.webdriver.Locator
import com.instructure.dataseeding.model.CanvasUserApiModel
import com.instructure.espresso.OnViewWithId
import com.instructure.espresso.assertDisplayed
import com.instructure.espresso.page.BasePage
import com.instructure.teacher.R
-import com.instructure.teacher.ui.utils.repeatedlyUntil
import com.instructure.teacher.ui.utils.repeatedlyUntilNot
@Suppress("unused")
@@ -50,7 +51,7 @@ class LoginSignInPage : BasePage() {
//region Assertion Helpers
- override fun assertPageObjects() {
+ override fun assertPageObjects(duration: Long) {
signInRoot.assertDisplayed()
toolbar.assertDisplayed()
diff --git a/apps/teacher/src/androidTest/java/com/instructure/teacher/ui/pages/WebViewLoginPage.kt b/apps/teacher/src/androidTest/java/com/instructure/teacher/ui/pages/WebViewLoginPage.kt
index bbc3e92ff7..e79394a719 100644
--- a/apps/teacher/src/androidTest/java/com/instructure/teacher/ui/pages/WebViewLoginPage.kt
+++ b/apps/teacher/src/androidTest/java/com/instructure/teacher/ui/pages/WebViewLoginPage.kt
@@ -18,7 +18,9 @@ package com.instructure.teacher.ui.pages
import androidx.test.espresso.web.sugar.Web
import androidx.test.espresso.web.sugar.Web.onWebView
-import androidx.test.espresso.web.webdriver.DriverAtoms.*
+import androidx.test.espresso.web.webdriver.DriverAtoms.findElement
+import androidx.test.espresso.web.webdriver.DriverAtoms.webClick
+import androidx.test.espresso.web.webdriver.DriverAtoms.webKeys
import androidx.test.espresso.web.webdriver.Locator
import com.instructure.dataseeding.model.CanvasUserApiModel
import com.instructure.espresso.page.BasePage
@@ -48,7 +50,7 @@ class WebViewLoginPage : BasePage() {
//region Assertion Helpers
- override fun assertPageObjects() {
+ override fun assertPageObjects(duration: Long) {
emailField()
passwordField()
loginButton()
diff --git a/automation/espresso/src/main/kotlin/com/instructure/espresso/matchers/WaitForViewMatcher.kt b/automation/espresso/src/main/kotlin/com/instructure/espresso/matchers/WaitForViewMatcher.kt
index 1c453ca42c..974eeba6b8 100644
--- a/automation/espresso/src/main/kotlin/com/instructure/espresso/matchers/WaitForViewMatcher.kt
+++ b/automation/espresso/src/main/kotlin/com/instructure/espresso/matchers/WaitForViewMatcher.kt
@@ -18,16 +18,16 @@
package com.instructure.espresso.matchers
+import android.view.View
import androidx.test.espresso.Espresso.onView
import androidx.test.espresso.ViewInteraction
import androidx.test.espresso.assertion.ViewAssertions.matches
import androidx.test.espresso.matcher.ViewMatchers
import androidx.test.espresso.matcher.ViewMatchers.withEffectiveVisibility
-import android.view.View
import com.instructure.espresso.EspressoLog
import org.hamcrest.Matcher
-import java.util.concurrent.TimeUnit
-import java.util.concurrent.atomic.AtomicBoolean
+import java.util.concurrent.*
+import java.util.concurrent.atomic.*
object WaitForViewMatcher {
private val log = EspressoLog(WaitForViewMatcher::class.java)
@@ -41,9 +41,9 @@ object WaitForViewMatcher {
// https://github.com/braintree/braintree_android/blob/25513d76da88fe2ce9f476c4dc51f24cf6e26104/TestUtils/src/main/java/com/braintreepayments/testutils/ui/ViewHelper.java#L30
// The viewMatcher is called on every view to determine what matches. Must be fast!
- fun waitForView(viewMatcher: Matcher): ViewInteraction {
+ fun waitForView(viewMatcher: Matcher, duration: Long = 10): ViewInteraction {
waiting.set(true)
- val waitTime = TimeUnit.SECONDS.toMillis(10)
+ val waitTime = TimeUnit.SECONDS.toMillis(duration)
val endTime = System.currentTimeMillis() + waitTime
log.i("waitForView matching...")
diff --git a/automation/espresso/src/main/kotlin/com/instructure/espresso/page/BasePage.kt b/automation/espresso/src/main/kotlin/com/instructure/espresso/page/BasePage.kt
index 212829ede6..c9f37c3705 100644
--- a/automation/espresso/src/main/kotlin/com/instructure/espresso/page/BasePage.kt
+++ b/automation/espresso/src/main/kotlin/com/instructure/espresso/page/BasePage.kt
@@ -16,19 +16,19 @@
*/
package com.instructure.espresso.page
-import com.instructure.espresso.assertDisplayed
-import com.instructure.espresso.assertVisible
import androidx.annotation.IdRes
import androidx.test.espresso.ViewInteraction
import androidx.test.espresso.matcher.ViewMatchers
+import com.instructure.espresso.assertDisplayed
+import com.instructure.espresso.assertVisible
import com.instructure.espresso.matchers.WaitForViewMatcher
import kotlin.properties.ReadOnlyProperty
import kotlin.reflect.KProperty
abstract class BasePage(@IdRes val pageResId: Int? = null) {
- open fun assertPageObjects() {
- assertProperties()
+ open fun assertPageObjects(duration: Long = 10) {
+ assertProperties(duration)
}
private val assertPropertiesInfo = arrayListOf, KProperty<*>>>()
@@ -41,8 +41,8 @@ abstract class BasePage(@IdRes val pageResId: Int? = null) {
assertPropertiesInfo += info
}
- private fun assertProperties() {
- pageResId?.let { WaitForViewMatcher.waitForView(ViewMatchers.withId(it)).assertDisplayed() }
+ private fun assertProperties(duration: Long) {
+ pageResId?.let { WaitForViewMatcher.waitForView(ViewMatchers.withId(it), duration).assertDisplayed() }
getRegisteredProperties().forEach { it.assertVisible() }
}
From b7b45ee416727ea9e2dd84bed50fd422dee33091 Mon Sep 17 00:00:00 2001
From: Kristof Deak <92309696+kdeakinstructure@users.noreply.github.com>
Date: Thu, 24 Nov 2022 16:42:22 +0100
Subject: [PATCH 57/73] [MBL-16390][Student] - Subscribe calendar added to
SettingsE2E tests (#1784)
---
.../student/ui/e2e/SettingsE2ETest.kt | 48 ++++++++++++++++---
.../ui/interaction/ModuleInteractionTest.kt | 1 +
.../student/ui/pages/CanvasWebViewPage.kt | 4 ++
.../student/ui/pages/SettingsPage.kt | 11 ++++-
.../instructure/student/ui/utils/Matchers.kt | 18 +++++--
5 files changed, 72 insertions(+), 10 deletions(-)
diff --git a/apps/student/src/androidTest/java/com/instructure/student/ui/e2e/SettingsE2ETest.kt b/apps/student/src/androidTest/java/com/instructure/student/ui/e2e/SettingsE2ETest.kt
index 9d1e86c595..195258515d 100644
--- a/apps/student/src/androidTest/java/com/instructure/student/ui/e2e/SettingsE2ETest.kt
+++ b/apps/student/src/androidTest/java/com/instructure/student/ui/e2e/SettingsE2ETest.kt
@@ -16,8 +16,11 @@
*/
package com.instructure.student.ui.e2e
+import android.content.Intent
import android.util.Log
import androidx.test.espresso.Espresso
+import androidx.test.espresso.intent.Intents
+import androidx.test.espresso.intent.Intents.intended
import com.instructure.canvas.espresso.E2E
import com.instructure.canvasapi2.utils.RemoteConfigParam
import com.instructure.canvasapi2.utils.RemoteConfigUtils
@@ -27,6 +30,7 @@ import com.instructure.panda_annotations.Priority
import com.instructure.panda_annotations.TestCategory
import com.instructure.panda_annotations.TestMetaData
import com.instructure.student.R
+import com.instructure.student.ui.utils.IntentActionMatcher
import com.instructure.student.ui.utils.StudentTest
import com.instructure.student.ui.utils.seedData
import com.instructure.student.ui.utils.tokenLogin
@@ -36,13 +40,9 @@ import org.junit.Test
@HiltAndroidTest
class SettingsE2ETest : StudentTest() {
- override fun displaysPageObjects() {
- TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
- }
+ override fun displaysPageObjects() = Unit
- override fun enableAndConfigureAccessibilityChecks() {
- //We don't want to see accessibility errors on E2E tests
- }
+ override fun enableAndConfigureAccessibilityChecks() = Unit
@E2E
@Test
@@ -53,6 +53,7 @@ class SettingsE2ETest : StudentTest() {
val data = seedData(students = 1, teachers = 1, courses = 1)
val student = data.studentsList[0]
+ Log.d(STEP_TAG, "Login with user: ${student.name}, login id: ${student.loginId}.")
tokenLogin(student)
dashboardPage.waitForRender()
@@ -258,4 +259,39 @@ class SettingsE2ETest : StudentTest() {
}
}
+
+ @E2E
+ @Test
+ @TestMetaData(Priority.COMMON, FeatureCategory.SETTINGS, TestCategory.E2E)
+ fun testSubscribeToCalendar() {
+
+ Log.d(PREPARATION_TAG, "Initialize Intents.")
+ Intents.init()
+
+ Log.d(PREPARATION_TAG, "Seeding data.")
+ val data = seedData(students = 1, teachers = 1, courses = 1)
+ val student = data.studentsList[0]
+
+ Log.d(STEP_TAG, "Login with user: ${student.name}, login id: ${student.loginId}.")
+ tokenLogin(student)
+ dashboardPage.waitForRender()
+
+ Log.d(STEP_TAG, "Navigate to User Settings Page.")
+ dashboardPage.launchSettingsPage()
+ settingsPage.assertPageObjects()
+
+ Log.d(STEP_TAG, "Click on 'Subscribe to Calendar'.")
+ settingsPage.openSubscribeToCalendar()
+
+ Log.d(STEP_TAG, "Click on the 'SUBSCRIBE' button of the pop-up dialog.")
+ settingsPage.clickOnSubscribe()
+
+ Log.d(STEP_TAG, "Assert that the proper intents has launched, so the NavigationActivity has been launched with an Intent from SettingsActivity.")
+ val calendarDataMatcherString = "https://calendar.google.com/calendar/r?cid=webcal://"
+ val intentActionMatcher = IntentActionMatcher(Intent.ACTION_VIEW, calendarDataMatcherString)
+ intended(intentActionMatcher)
+
+ Log.d(PREPARATION_TAG, "Release Intents.")
+ Intents.release()
+ }
}
\ No newline at end of file
diff --git a/apps/student/src/androidTest/java/com/instructure/student/ui/interaction/ModuleInteractionTest.kt b/apps/student/src/androidTest/java/com/instructure/student/ui/interaction/ModuleInteractionTest.kt
index 6e84a2e81e..5585a5a7ea 100644
--- a/apps/student/src/androidTest/java/com/instructure/student/ui/interaction/ModuleInteractionTest.kt
+++ b/apps/student/src/androidTest/java/com/instructure/student/ui/interaction/ModuleInteractionTest.kt
@@ -118,6 +118,7 @@ class ModuleInteractionTest : StudentTest() {
// Click the file module and verify that the file appears
modulesPage.clickModuleItem(module,fileName, R.id.openButton)
+ canvasWebViewPage.waitForWebView()
canvasWebViewPage.runTextChecks(fileCheck!!)
}
diff --git a/apps/student/src/androidTest/java/com/instructure/student/ui/pages/CanvasWebViewPage.kt b/apps/student/src/androidTest/java/com/instructure/student/ui/pages/CanvasWebViewPage.kt
index 0e3930f25b..ed7787bf7b 100644
--- a/apps/student/src/androidTest/java/com/instructure/student/ui/pages/CanvasWebViewPage.kt
+++ b/apps/student/src/androidTest/java/com/instructure/student/ui/pages/CanvasWebViewPage.kt
@@ -89,6 +89,10 @@ open class CanvasWebViewPage : BasePage(R.id.contentWebView) {
.withContextualElement(findElement(subElementType, subElementValue))
.perform(webClick())
}
+
+ fun waitForWebView() {
+ waitForView(allOf(withId(R.id.contentWebView), isDisplayed()))
+ }
}
/** data class that encapsulates info for a webview text check */
diff --git a/apps/student/src/androidTest/java/com/instructure/student/ui/pages/SettingsPage.kt b/apps/student/src/androidTest/java/com/instructure/student/ui/pages/SettingsPage.kt
index 5301e642e4..31d4060756 100644
--- a/apps/student/src/androidTest/java/com/instructure/student/ui/pages/SettingsPage.kt
+++ b/apps/student/src/androidTest/java/com/instructure/student/ui/pages/SettingsPage.kt
@@ -33,6 +33,7 @@ class SettingsPage : BasePage(R.id.settingsFragment) {
private val pairObserverLabel by OnViewWithId(R.id.pairObserver, autoAssert = false)
private val aboutLabel by OnViewWithId(R.id.about)
private val legalLabel by OnViewWithId(R.id.legal)
+ private val subscribeCalendarLabel by OnViewWithId(R.id.subscribeToCalendar, autoAssert = false)
private val remoteConfigLabel by OnViewWithId(R.id.remoteConfigParams)
private val appThemeTitle by OnViewWithId(R.id.appThemeTitle)
private val appThemeStatus by OnViewWithId(R.id.appThemeStatus)
@@ -57,13 +58,21 @@ class SettingsPage : BasePage(R.id.settingsFragment) {
profileSettingLabel.scrollTo().click()
}
+ fun openSubscribeToCalendar() {
+ subscribeCalendarLabel.scrollTo().click()
+ }
+
+ fun clickOnSubscribe() {
+ onView(withText(R.string.subscribeButton)).click()
+ }
+
fun openAppThemeSettings() {
appThemeTitle.scrollTo().click()
}
fun selectAppTheme(appTheme: String)
{
- onView(withText(appTheme) + withParent(R.id.select_dialog_listview)).click()
+ onView(withText(appTheme) + withParent(R.id.select_dialog_listview)).click()
}
fun assertAppThemeTitleTextColor(expectedTextColor: String) {
diff --git a/apps/student/src/androidTest/java/com/instructure/student/ui/utils/Matchers.kt b/apps/student/src/androidTest/java/com/instructure/student/ui/utils/Matchers.kt
index f8322cf4e4..199464a798 100644
--- a/apps/student/src/androidTest/java/com/instructure/student/ui/utils/Matchers.kt
+++ b/apps/student/src/androidTest/java/com/instructure/student/ui/utils/Matchers.kt
@@ -15,17 +15,17 @@
*/
package com.instructure.student.ui.utils
+import android.content.Intent
import android.view.View
import android.widget.TextView
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
-import androidx.test.espresso.Espresso.onView
-import androidx.test.espresso.NoMatchingViewException
import androidx.test.espresso.UiController
import androidx.test.espresso.ViewAction
import androidx.test.espresso.ViewInteraction
import androidx.test.espresso.assertion.ViewAssertions.matches
import androidx.test.espresso.matcher.BoundedMatcher
-import androidx.test.espresso.matcher.ViewMatchers.*
+import androidx.test.espresso.matcher.ViewMatchers.assertThat
+import androidx.test.espresso.matcher.ViewMatchers.isAssignableFrom
import org.hamcrest.CoreMatchers.`is`
import org.hamcrest.Description
import org.hamcrest.Matcher
@@ -44,6 +44,7 @@ fun ViewInteraction.assertLineCount(lineCount: Int) {
check(matches(matcher))
}
+
fun ViewInteraction.getView(): View {
lateinit var matchingView: View
perform(object : ViewAction {
@@ -113,3 +114,14 @@ fun ViewInteraction.assertIsRefreshing(isRefreshing: Boolean) {
}
check(matches(matcher))
}
+
+class IntentActionMatcher(private val intentType: String, private val dataMatcher: String) : TypeSafeMatcher() {
+
+ override fun describeTo(description: Description?) {
+ description?.appendText("Intent Matcher")
+ }
+
+ override fun matchesSafely(item: Intent?): Boolean {
+ return (intentType == item?.action) && (item?.dataString?.contains(dataMatcher) ?: false)
+ }
+}
From e1cad317bc0ec6640d6afe401fde0620f1f74863 Mon Sep 17 00:00:00 2001
From: Kristof Deak <92309696+kdeakinstructure@users.noreply.github.com>
Date: Sat, 26 Nov 2022 18:10:34 +0100
Subject: [PATCH 58/73] [MBL-16329][Student] - Help menu Test (#1789)
---
.../student/ui/e2e/DashboardE2ETest.kt | 30 ++++++++++++----
.../student/ui/pages/DashboardPage.kt | 5 +++
.../instructure/student/ui/pages/HelpPage.kt | 34 +++++++++++++++----
3 files changed, 57 insertions(+), 12 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 ea25f2bf4c..55cbcc5d07 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
@@ -32,13 +32,9 @@ import org.junit.Test
@HiltAndroidTest
class DashboardE2ETest : StudentTest() {
- override fun displaysPageObjects() {
- TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
- }
+ override fun displaysPageObjects() = Unit
- override fun enableAndConfigureAccessibilityChecks() {
- //We don't want to see accessibility errors on E2E tests
- }
+ override fun enableAndConfigureAccessibilityChecks() = Unit
@E2E
@Test
@@ -79,4 +75,26 @@ class DashboardE2ETest : StudentTest() {
dashboardPage.assertUnreadEmails(1)
}
+ @E2E
+ @Test
+ @TestMetaData(Priority.NICE_TO_HAVE, FeatureCategory.DASHBOARD, TestCategory.E2E)
+ fun testHelpMenuE2E() {
+ Log.d(PREPARATION_TAG,"Seeding data.")
+ val data = seedData(students = 1, courses = 1)
+ val student = data.studentsList[0]
+
+ Log.d(STEP_TAG,"Login with user: ${student.name}, login id: ${student.loginId}.")
+ tokenLogin(student)
+ dashboardPage.waitForRender()
+
+ Log.d(STEP_TAG, "Open Help Menu.")
+ dashboardPage.openHelpMenu()
+
+ Log.d(STEP_TAG, "Assert Help Menu Dialog is displayed.")
+ helpPage.assertHelpMenuDisplayed()
+
+ Log.d(STEP_TAG, "Assert that all the corresponding Help menu content are displayed.")
+ helpPage.assertHelpMenuContent()
+ }
+
}
\ No newline at end of file
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 36f5535440..d746a81aa3 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
@@ -235,6 +235,11 @@ class DashboardPage : BasePage(R.id.dashboardPage) {
onViewWithId(R.id.navigationDrawerSettings).click()
}
+ fun openHelpMenu() {
+ onView(hamburgerButtonMatcher).click()
+ onViewWithId(R.id.navigationDrawerItem_help).scrollTo().click()
+ }
+
fun selectCourse(course: CourseApiModel) {
assertDisplaysCourse(course)
onView(withText(course.name)).click()
diff --git a/apps/student/src/androidTest/java/com/instructure/student/ui/pages/HelpPage.kt b/apps/student/src/androidTest/java/com/instructure/student/ui/pages/HelpPage.kt
index 807cf33210..b9083adfe1 100644
--- a/apps/student/src/androidTest/java/com/instructure/student/ui/pages/HelpPage.kt
+++ b/apps/student/src/androidTest/java/com/instructure/student/ui/pages/HelpPage.kt
@@ -25,14 +25,10 @@ import androidx.test.espresso.matcher.ViewMatchers.withText
import com.instructure.canvas.espresso.containsTextCaseInsensitive
import com.instructure.canvas.espresso.withCustomConstraints
import com.instructure.canvasapi2.models.Course
-import com.instructure.espresso.OnViewWithStringTextIgnoreCase
-import com.instructure.espresso.OnViewWithText
-import com.instructure.espresso.assertDisplayed
-import com.instructure.espresso.click
+import com.instructure.espresso.*
import com.instructure.espresso.matchers.WaitForViewMatcher.waitForView
import com.instructure.espresso.page.BasePage
-import com.instructure.espresso.scrollTo
-import com.instructure.espresso.typeText
+import com.instructure.espresso.page.plus
import com.instructure.student.R
// This is a little hokey, as the options that appear are somewhat governed by the results of
@@ -75,4 +71,30 @@ class HelpPage : BasePage(R.id.helpDialog) {
fun submitFeature() {
submitFeatureLabel.scrollTo().click()
}
+
+ fun assertHelpMenuDisplayed() {
+ onView(withId(R.id.alertTitle) + withText(R.string.help)).assertDisplayed()
+ onView(withId(R.id.helpDialog)).assertDisplayed()
+ }
+
+ fun assertHelpMenuContent() {
+
+ onView(withId(R.id.title) + withText(R.string.searchGuides))
+ onView(withId(R.id.subtitle) + withText(R.string.searchGuidesDetails))
+
+ onView(withId(R.id.title) + withText(R.string.askInstructor))
+ onView(withId(R.id.subtitle) + withText(R.string.askInstructorDetails))
+
+ onView(withId(R.id.title) + withText(R.string.reportProblem))
+ onView(withId(R.id.subtitle) + withText(R.string.reportProblemDetails))
+
+ onView(withId(R.id.title) + withText(R.string.shareYourLove))
+ onView(withId(R.id.subtitle) + withText(R.string.shareYourLoveDetails))
+
+ onView(withId(R.id.title) + withText("Submit a Feature Idea"))
+ onView(withId(R.id.subtitle) + withText("Have an idea to improve Canvas?"))
+
+ onView(withId(R.id.title) + withText("COVID-19 Canvas Resources"))
+ onView(withId(R.id.subtitle) + withText("Tips for teaching and learning online"))
+ }
}
From ceda9765f6a781d0478e152ce5a157466b34b629 Mon Sep 17 00:00:00 2001
From: Tamas Kozmer <72397075+tamaskozmer@users.noreply.github.com>
Date: Tue, 29 Nov 2022 12:42:33 +0100
Subject: [PATCH 59/73] [MBL-16396][Student][Teacher] Heap feature flag #1787
refs: MBL-16396
affects: Student, Teacher
release note: none
---
.../instructure/student/activity/CallbackActivity.kt | 10 ++++++++++
.../com/instructure/student/tasks/StudentLogoutTask.kt | 2 ++
.../com/instructure/student/util/BaseAppManager.kt | 5 ++++-
.../instructure/teacher/activities/SplashActivity.kt | 10 ++++++++++
.../com/instructure/teacher/tasks/TeacherLogoutTask.kt | 2 ++
.../com/instructure/teacher/utils/BaseAppManager.kt | 5 ++++-
.../com/instructure/canvasapi2/apis/FeaturesAPI.kt | 2 +-
.../instructure/canvasapi2/managers/FeaturesManager.kt | 2 ++
8 files changed, 35 insertions(+), 3 deletions(-)
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 fcd567bfad..60c319f2f9 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
@@ -19,7 +19,9 @@ package com.instructure.student.activity
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
@@ -62,6 +64,8 @@ abstract class CallbackActivity : ParentActivity(), InboxFragment.OnUnreadCountI
private fun loadInitialData() {
loadInitialDataJob = tryWeave {
+ setupHeapTracking()
+
// Determine if user can masquerade
if (ApiPrefs.canBecomeUser == null) {
if (ApiPrefs.domain.startsWith("siteadmin", true)) {
@@ -136,6 +140,12 @@ abstract class CallbackActivity : ParentActivity(), InboxFragment.OnUnreadCountI
}
}
+ private suspend fun setupHeapTracking() {
+ val featureFlagsResult = FeaturesManager.getEnvironmentFeatureFlagsAsync(true).await().dataOrNull
+ val sendUsageMetrics = featureFlagsResult?.get(FeaturesManager.SEND_USAGE_METRICS) ?: false
+ Heap.setTrackingEnabled(sendUsageMetrics)
+ }
+
private suspend fun getUnreadMessageCount() {
val unreadCount = awaitApi { UnreadCountManager.getUnreadConversationCount(it, true) }
unreadCount.let {
diff --git a/apps/student/src/main/java/com/instructure/student/tasks/StudentLogoutTask.kt b/apps/student/src/main/java/com/instructure/student/tasks/StudentLogoutTask.kt
index fccf0db8f1..c24c3d17dc 100644
--- a/apps/student/src/main/java/com/instructure/student/tasks/StudentLogoutTask.kt
+++ b/apps/student/src/main/java/com/instructure/student/tasks/StudentLogoutTask.kt
@@ -20,6 +20,7 @@ import android.content.Context
import android.content.Intent
import android.net.Uri
import com.google.firebase.messaging.FirebaseMessaging
+import com.heapanalytics.android.Heap
import com.instructure.canvasapi2.utils.tryOrNull
import com.instructure.loginapi.login.tasks.LogoutTask
import com.instructure.pandautils.typeface.TypefaceBehavior
@@ -39,6 +40,7 @@ class StudentLogoutTask(
FlutterComm.reset()
StudentPrefs.safeClearPrefs()
WidgetUpdater.updateWidgets()
+ Heap.setTrackingEnabled(false)
}
override fun createLoginIntent(context: Context): Intent {
diff --git a/apps/student/src/main/java/com/instructure/student/util/BaseAppManager.kt b/apps/student/src/main/java/com/instructure/student/util/BaseAppManager.kt
index bd1f106611..aa6d1717b7 100644
--- a/apps/student/src/main/java/com/instructure/student/util/BaseAppManager.kt
+++ b/apps/student/src/main/java/com/instructure/student/util/BaseAppManager.kt
@@ -23,6 +23,7 @@ import androidx.core.content.ContextCompat
import com.google.android.play.core.missingsplits.MissingSplitsManagerFactory
import com.google.firebase.crashlytics.FirebaseCrashlytics
import com.heapanalytics.android.Heap
+import com.heapanalytics.android.config.Options
import com.instructure.canvasapi2.utils.Analytics
import com.instructure.canvasapi2.utils.AnalyticsEventConstants
import com.instructure.canvasapi2.utils.Logger
@@ -88,7 +89,9 @@ open class BaseAppManager : com.instructure.canvasapi2.AppManager(), AnalyticsEv
initFlutterEngine()
- Heap.init(this, BuildConfig.HEAP_APP_ID)
+ val options = Options()
+ options.disableTracking()
+ Heap.init(this, BuildConfig.HEAP_APP_ID, options)
}
private fun initFlutterEngine() {
diff --git a/apps/teacher/src/main/java/com/instructure/teacher/activities/SplashActivity.kt b/apps/teacher/src/main/java/com/instructure/teacher/activities/SplashActivity.kt
index c66d052fab..6137d24654 100644
--- a/apps/teacher/src/main/java/com/instructure/teacher/activities/SplashActivity.kt
+++ b/apps/teacher/src/main/java/com/instructure/teacher/activities/SplashActivity.kt
@@ -22,7 +22,9 @@ import android.graphics.drawable.ColorDrawable
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import com.google.firebase.crashlytics.FirebaseCrashlytics
+import com.heapanalytics.android.Heap
import com.instructure.canvasapi2.managers.CourseManager
+import com.instructure.canvasapi2.managers.FeaturesManager
import com.instructure.canvasapi2.managers.ThemeManager
import com.instructure.canvasapi2.managers.UserManager
import com.instructure.canvasapi2.models.*
@@ -79,6 +81,8 @@ class SplashActivity : AppCompatActivity() {
return@weave
}
+ setupHeapTracking()
+
// Determine if user is a Teacher, Ta, or Designer
// Use GlobalScope since this can continue executing after SplashActivity is destroyed
val enrollmentCheck = GlobalScope.async(start = CoroutineStart.LAZY) {
@@ -204,6 +208,12 @@ class SplashActivity : AppCompatActivity() {
return ApiPrefs.effectiveLocale != oldLocale
}
+ private suspend fun setupHeapTracking() {
+ val featureFlagsResult = FeaturesManager.getEnvironmentFeatureFlagsAsync(true).await().dataOrNull
+ val sendUsageMetrics = featureFlagsResult?.get(FeaturesManager.SEND_USAGE_METRICS) ?: false
+ Heap.setTrackingEnabled(sendUsageMetrics)
+ }
+
override fun onStop() {
super.onStop()
LoggingUtility.log(this.javaClass.simpleName + " --> On Stop")
diff --git a/apps/teacher/src/main/java/com/instructure/teacher/tasks/TeacherLogoutTask.kt b/apps/teacher/src/main/java/com/instructure/teacher/tasks/TeacherLogoutTask.kt
index d9a0469cbf..1f439f2cd2 100644
--- a/apps/teacher/src/main/java/com/instructure/teacher/tasks/TeacherLogoutTask.kt
+++ b/apps/teacher/src/main/java/com/instructure/teacher/tasks/TeacherLogoutTask.kt
@@ -20,6 +20,7 @@ import android.content.Context
import android.content.Intent
import android.net.Uri
import com.google.firebase.messaging.FirebaseMessaging
+import com.heapanalytics.android.Heap
import com.instructure.canvasapi2.utils.tryOrNull
import com.instructure.loginapi.login.tasks.LogoutTask
import com.instructure.teacher.activities.LoginActivity
@@ -29,6 +30,7 @@ class TeacherLogoutTask(type: Type, uri: Uri? = null) : LogoutTask(type, uri) {
override fun onCleanup() {
TeacherPrefs.safeClearPrefs()
+ Heap.setTrackingEnabled(false)
}
override fun createLoginIntent(context: Context): Intent {
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 ac370236b0..9cabb2a14d 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
@@ -23,6 +23,7 @@ import androidx.localbroadcastmanager.content.LocalBroadcastManager
import com.google.android.play.core.missingsplits.MissingSplitsManagerFactory
import com.google.firebase.crashlytics.FirebaseCrashlytics
import com.heapanalytics.android.Heap
+import com.heapanalytics.android.config.Options
import com.instructure.canvasapi2.utils.*
import com.instructure.loginapi.login.tasks.LogoutTask
import com.instructure.pandautils.utils.AppTheme
@@ -84,7 +85,9 @@ open class BaseAppManager : com.instructure.canvasapi2.AppManager() {
filter.addAction(Const.ACTION_MEDIA_UPLOAD_FAIL)
LocalBroadcastManager.getInstance(this).registerReceiver(mediaUploadReceiver, filter)
- Heap.init(this, BuildConfig.HEAP_APP_ID)
+ val options = Options()
+ options.disableTracking()
+ Heap.init(this, BuildConfig.HEAP_APP_ID, options)
}
override fun performLogoutOnAuthError() {
diff --git a/libs/canvas-api-2/src/main/java/com/instructure/canvasapi2/apis/FeaturesAPI.kt b/libs/canvas-api-2/src/main/java/com/instructure/canvasapi2/apis/FeaturesAPI.kt
index f016e23469..94cbd04e96 100644
--- a/libs/canvas-api-2/src/main/java/com/instructure/canvasapi2/apis/FeaturesAPI.kt
+++ b/libs/canvas-api-2/src/main/java/com/instructure/canvasapi2/apis/FeaturesAPI.kt
@@ -45,6 +45,6 @@ object FeaturesAPI {
}
fun getEnvironmentFeatureFlags(adapter: RestBuilder, callback: StatusCallback