Skip to content

Commit

Permalink
Release Student 6.11.0 (226)
Browse files Browse the repository at this point in the history
  • Loading branch information
hermannakos authored Mar 12, 2021
1 parent 870eeda commit 4d8e05f
Show file tree
Hide file tree
Showing 91 changed files with 1,222 additions and 516 deletions.
4 changes: 2 additions & 2 deletions apps/student/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -57,8 +57,8 @@ android {
applicationId "com.instructure.candroid"
minSdkVersion Versions.MIN_SDK
targetSdkVersion Versions.TARGET_SDK
versionCode = 225
versionName = '6.10.0'
versionCode = 226
versionName = '6.11.0'

vectorDrawables.useSupportLibrary = true
multiDexEnabled = true
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,26 @@ class AssignmentListInteractionTest : StudentTest() {
assignmentListPage.assertHasAssignment(assignment)
}

@Test
@TestMetaData(Priority.P1, FeatureCategory.ASSIGNMENTS, TestCategory.INTERACTION)
fun sortAssignmentsByTimeByDefault() {
val assignment = getToAssignmentsPage()[0]
assignmentListPage.assertHasAssignment(assignment)
assignmentListPage.assertSortByButtonShowsSortByTime()
assignmentListPage.assertFindsUndatedAssignmentLabel()
}

@Test
@TestMetaData(Priority.P1, FeatureCategory.ASSIGNMENTS, TestCategory.INTERACTION)
fun sortAssignmentsByTypeWhenTypeIsSelectedInTheDialog() {
val assignment = getToAssignmentsPage()[0]

assignmentListPage.selectSortByType()

assignmentListPage.assertHasAssignment(assignment)
assignmentListPage.assertSortByButtonShowsSortByType()
}

private fun getToAssignmentsPage(assignmentCount: Int = 1): List<Assignment> {
val data = MockCanvas.init(
courseCount = 1,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,25 +19,16 @@ package com.instructure.student.ui.pages
import android.view.View
import androidx.test.espresso.Espresso.onView
import androidx.test.espresso.assertion.ViewAssertions.doesNotExist
import androidx.test.espresso.matcher.ViewMatchers.isDisplayed
import androidx.test.espresso.matcher.ViewMatchers.withChild
import androidx.test.espresso.matcher.ViewMatchers.withId
import androidx.test.espresso.matcher.ViewMatchers.withParent
import androidx.test.espresso.matcher.ViewMatchers.withText
import com.instructure.canvasapi2.models.Assignment
import androidx.test.espresso.assertion.ViewAssertions.matches
import androidx.test.espresso.matcher.ViewMatchers.*
import com.instructure.canvas.espresso.scrollRecyclerView
import com.instructure.canvas.espresso.waitForMatcherWithRefreshes
import com.instructure.canvasapi2.models.Assignment
import com.instructure.dataseeding.model.AssignmentApiModel
import com.instructure.dataseeding.model.QuizApiModel
import com.instructure.espresso.OnViewWithId
import com.instructure.espresso.WaitForViewWithId
import com.instructure.espresso.WaitForViewWithText
import com.instructure.espresso.assertDisplayed
import com.instructure.espresso.click
import com.instructure.espresso.*
import com.instructure.espresso.page.BasePage
import com.instructure.espresso.page.waitForViewWithText
import com.instructure.espresso.scrollTo
import com.instructure.espresso.swipeDown
import com.instructure.student.R
import org.hamcrest.Matcher
import org.hamcrest.Matchers.allOf
Expand All @@ -46,13 +37,13 @@ import org.hamcrest.Matchers.containsString
class AssignmentListPage : BasePage(pageResId = R.id.assignmentListPage) {

private val assignmentListToolbar by OnViewWithId(R.id.toolbar)
private val gradingPeriodHeader by WaitForViewWithId(R.id.termSpinnerLayout)
private val sortByButton by OnViewWithId(R.id.sortByButton)
private val sortByTextView by OnViewWithId(R.id.sortByTextView)

// Only displayed when assignment list is empty
private val emptyView by WaitForViewWithId(R.id.emptyView, autoAssert = false)

// Only displayed when there are grading periods
private val gradingPeriodHeader by WaitForViewWithId(R.id.termSpinnerLayout, autoAssert = false)

// Only displayed when there are no assignments
private val emptyText by WaitForViewWithText(R.string.noItemsToDisplayShort, autoAssert = false)

Expand Down Expand Up @@ -140,4 +131,21 @@ class AssignmentListPage : BasePage(pageResId = R.id.assignmentListPage) {
fun assertHasGradingPeriods() {
gradingPeriodHeader.assertDisplayed()
}

fun assertSortByButtonShowsSortByTime() {
sortByTextView.check(matches(withText(R.string.sortByTime)))
}

fun assertSortByButtonShowsSortByType() {
sortByTextView.check(matches(withText(R.string.sortByType)))
}

fun assertFindsUndatedAssignmentLabel() {
onView(withText(R.string.undatedAssignments)).assertVisible()
}

fun selectSortByType() {
sortByButton.click()
onView(withText(R.string.sortByDialogTypeOption)).click()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -55,49 +55,49 @@ abstract class StudentTest : CanvasTest() {
/**
* Required for auto complete of page objects within tests
*/
val allCoursesPage = AllCoursesPage()
val annotationCommentListPage = AnnotationCommentListPage()
val assignmentDetailsPage = AssignmentDetailsPage()
val assignmentListPage = AssignmentListPage()
val bookmarkPage = BookmarkPage()
val calendarEventPage = CalendarEventPage()
val calendarPage = CalendarPage()
val canvasWebViewPage = CanvasWebViewPage()
val courseBrowserPage = CourseBrowserPage()
val courseGradesPage = CourseGradesPage()
val dashboardPage = DashboardPage()
val allCoursesPage = AllCoursesPage()
val discussionDetailsPage = DiscussionDetailsPage()
val discussionListPage = DiscussionListPage()
val editFavoritesPage = EditFavoritesPage()
val calendarPage = CalendarPage()
val todoPage = TodoPage()
val inboxPage = InboxPage()
val fileListPage = FileListPage()
val fileUploadPage = FileUploadPage()
val helpPage = HelpPage()
val inboxConversationPage = InboxConversationPage()
val newMessagePage = NewMessagePage()
val settingsPage = SettingsPage()
val pairObserverPage = PairObserverPage()
val inboxPage = InboxPage()
val legalPage = LegalPage()
val helpPage = HelpPage()
val loginFindSchoolPage = LoginFindSchoolPage()
val loginLandingPage = LoginLandingPage()
val loginSignInPage = LoginSignInPage()
val qrLoginPage = QRLoginPage()
val courseBrowserPage = CourseBrowserPage()
val assignmentDetailsPage = AssignmentDetailsPage()
val submissionDetailsPage = SubmissionDetailsPage()
val peopleListPage = PeopleListPage()
val personDetailsPage = PersonDetailsPage()
val moduleProgressionPage = ModuleProgressionPage()
val modulesPage = ModulesPage()
val syllabusPage = SyllabusPage()
val fileListPage = FileListPage()
val discussionListPage = DiscussionListPage()
val discussionDetailsPage = DiscussionDetailsPage()
val newMessagePage = NewMessagePage()
val notificationPage = NotificationPage()
val pageListPage = PageListPage()
val quizListPage = QuizListPage()
val urlSubmissionUploadPage = UrlSubmissionUploadPage()
val courseGradesPage = CourseGradesPage()
val moduleProgressionPage = ModuleProgressionPage()
val canvasWebViewPage = CanvasWebViewPage()
val fileUploadPage = FileUploadPage()
val annotationCommentListPage = AnnotationCommentListPage()
val pairObserverPage = PairObserverPage()
val pandaAvatarPage = PandaAvatarPage()
val peopleListPage = PeopleListPage()
val personDetailsPage = PersonDetailsPage()
val pickerSubmissionUploadPage = PickerSubmissionUploadPage()
val remoteConfigSettingsPage = RemoteConfigSettingsPage()
val profileSettingsPage = ProfileSettingsPage()
val calendarEventPage = CalendarEventPage()
val qrLoginPage = QRLoginPage()
val quizListPage = QuizListPage()
val quizTakingPage = QuizTakingPage()
val pandaAvatarPage = PandaAvatarPage()
val notificationPage = NotificationPage()
val bookmarkPage = BookmarkPage()
val remoteConfigSettingsPage = RemoteConfigSettingsPage()
val settingsPage = SettingsPage()
val submissionDetailsPage = SubmissionDetailsPage()
val syllabusPage = SyllabusPage()
val todoPage = TodoPage()
val urlSubmissionUploadPage = UrlSubmissionUploadPage()

// A no-op interaction to afford us an easy, harmless way to get a11y checking to trigger.
fun meaninglessSwipe() {
Expand Down
1 change: 0 additions & 1 deletion apps/student/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,6 @@
android:hardwareAccelerated="true"
android:supportsRtl="true"
android:largeHeap="true"
android:usesCleartextTraffic="true"
android:requestLegacyExternalStorage="true"
android:networkSecurityConfig="@xml/network_security_config"
tools:replace="android:supportsRtl"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ import com.instructure.canvasapi2.models.CanvasContext
import com.instructure.canvasapi2.models.Course
import com.instructure.canvasapi2.models.StorageQuotaExceededError
import com.instructure.canvasapi2.utils.ApiPrefs
import com.instructure.canvasapi2.utils.isNotDeleted
import com.instructure.canvasapi2.utils.weave.awaitApi
import com.instructure.canvasapi2.utils.weave.catch
import com.instructure.canvasapi2.utils.weave.tryWeave
Expand Down Expand Up @@ -131,6 +132,7 @@ class ShareFileUploadActivity : AppCompatActivity(), ShareFileDestinationDialog.
private fun getCourses() {
loadCoursesJob = tryWeave {
val courses = awaitApi<List<Course>> { CourseManager.getCourses(true, it) }
.filter { it.isNotDeleted() }
if (courses.isNotEmpty()) {
this@ShareFileUploadActivity.courses = ArrayList(courses)
if (uploadFileSourceFragment == null) showDestinationDialog()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,17 +20,18 @@ package com.instructure.student.adapter
import android.app.Activity
import android.view.View
import android.widget.Toast
import com.instructure.student.R
import com.instructure.student.holders.CourseViewHolder
import com.instructure.student.interfaces.CourseAdapterToFragmentCallback
import com.instructure.canvasapi2.managers.CourseManager
import com.instructure.canvasapi2.models.Course
import com.instructure.canvasapi2.utils.APIHelper
import com.instructure.canvasapi2.utils.isInvited
import com.instructure.canvasapi2.utils.isNotDeleted
import com.instructure.canvasapi2.utils.weave.WeaveJob
import com.instructure.canvasapi2.utils.weave.awaitApi
import com.instructure.canvasapi2.utils.weave.catch
import com.instructure.canvasapi2.utils.weave.tryWeave
import com.instructure.student.R
import com.instructure.student.holders.CourseViewHolder
import com.instructure.student.interfaces.CourseAdapterToFragmentCallback


class AllCoursesRecyclerAdapter(
Expand All @@ -57,7 +58,7 @@ class AllCoursesRecyclerAdapter(
mApiCall?.cancel()
mApiCall = tryWeave {
val courses = awaitApi<List<Course>> { CourseManager.getCourses(isRefresh, it) }
.filter { !it.accessRestrictedByDate && !it.isInvited() }
.filter { !it.accessRestrictedByDate && !it.isInvited() && it.isNotDeleted() }
addAll(courses)
notifyDataSetChanged()
isAllPagesLoaded = true
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,7 @@ class DashboardRecyclerAdapter(

// Get enrollment invites
val invites = awaitApi<List<Enrollment>> {
EnrollmentManager.getSelfEnrollments(null, listOf(EnrollmentAPI.STATE_INVITED), isRefresh, it)
EnrollmentManager.getSelfEnrollments(null, listOf(EnrollmentAPI.STATE_INVITED, EnrollmentAPI.STATE_CURRENT_AND_FUTURE), isRefresh, it)
}

// Map not null is needed because the dashboard api can return unpublished courses
Expand Down Expand Up @@ -205,10 +205,7 @@ class DashboardRecyclerAdapter(
addOrUpdateAllItems(ItemType.ANNOUNCEMENT_HEADER, announcements)

// Add course invites
val validInvites = invites.filter {
mCourseMap[it.courseId]?.let { course ->
course.isValidTerm() && !course.accessRestrictedByDate && isEnrollmentBetweenCourseDatesOrNotRestricted(course) } ?: false
}
val validInvites = invites.filter { it.enrollmentState == EnrollmentAPI.STATE_INVITED && hasValidCourseForEnrollment(it) }

addOrUpdateAllItems(ItemType.INVITATION_HEADER, validInvites)

Expand All @@ -222,13 +219,20 @@ class DashboardRecyclerAdapter(
}
}

private fun isEnrollmentBetweenCourseDatesOrNotRestricted(course: Course): Boolean {
val now = OffsetDateTime.now()
val startDate = OffsetDateTime.parse(course.startAt).withOffsetSameInstant(OffsetDateTime.now().offset)
val endDate = OffsetDateTime.parse(course.endAt).withOffsetSameInstant(OffsetDateTime.now().offset)
private fun hasValidCourseForEnrollment(enrollment: Enrollment): Boolean {
return mCourseMap[enrollment.courseId]?.let { course ->
course.isValidTerm() && !course.accessRestrictedByDate && isEnrollmentBeforeEndDateOrNotRestricted(course)
} ?: false
}

private fun isEnrollmentBeforeEndDateOrNotRestricted(course: Course): Boolean {
val isBeforeEndDate = course.endAt?.let {
val now = OffsetDateTime.now()
val endDate = OffsetDateTime.parse(it).withOffsetSameInstant(OffsetDateTime.now().offset)
now.isBefore(endDate)
} ?: true // Case when the course has no end date

val isBetweenCourseDates = now.isAfter(startDate) && now.isBefore(endDate)
return !course.restrictEnrollmentsToCourseDate || isBetweenCourseDates
return !course.restrictEnrollmentsToCourseDate || isBeforeEndDate
}

override fun itemLayoutResId(viewType: Int) = when (ItemType.values()[viewType]) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ import com.instructure.canvasapi2.models.CanvasComparable
import com.instructure.canvasapi2.models.Course
import com.instructure.canvasapi2.models.Group
import com.instructure.canvasapi2.utils.isInvited
import com.instructure.canvasapi2.utils.isValidTerm
import com.instructure.canvasapi2.utils.isNotDeleted
import com.instructure.canvasapi2.utils.weave.WeaveJob
import com.instructure.canvasapi2.utils.weave.awaitApis
import com.instructure.canvasapi2.utils.weave.catch
Expand Down Expand Up @@ -129,7 +129,7 @@ class EditFavoritesRecyclerAdapter(
val (rawCourses, rawGroups) = awaitApis<List<Course>,List<Group>>(
{ CourseManager.getCourses(true, it) },
{ GroupManager.getAllGroups(it,true)})
val validCourses = rawCourses.filter { !it.accessRestrictedByDate && !it.isInvited() }
val validCourses = rawCourses.filter { !it.accessRestrictedByDate && !it.isInvited() && it.isNotDeleted() }
addOrUpdateAllItems(ItemType.COURSE_HEADER,validCourses)
val courseMap = rawCourses.associateBy { it.id }
val groups = rawGroups.filter { group -> group.isActive(courseMap[group.courseId]) }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,17 +57,18 @@ import retrofit2.Response
import java.util.*

open class ModuleListRecyclerAdapter(
val courseContext: CanvasContext,
private val courseContext: CanvasContext,
context: Context,
val adapterToFragmentCallback: ModuleAdapterToFragmentCallback?
private var shouldExhaustPagination: Boolean,
private val adapterToFragmentCallback: ModuleAdapterToFragmentCallback?
) : ExpandableRecyclerAdapter<ModuleObject, ModuleItem, RecyclerView.ViewHolder>(context, ModuleObject::class.java, ModuleItem::class.java) {

private val mModuleItemCallbacks = HashMap<Long, ModuleItemCallback>()
private var mModuleObjectCallback: StatusCallback<List<ModuleObject>>? = null
private var checkCourseTabsJob: Job? = null

/* For testing purposes only */
protected constructor(context: Context) : this(CanvasContext.defaultCanvasContext(), context, null) // Callback not needed for testing, cast to null
protected constructor(context: Context) : this(CanvasContext.defaultCanvasContext(), context, false,null) // Callback not needed for testing, cast to null

init {
viewHolderHeaderClicked = object : ViewHolderHeaderClicked<ModuleObject> {
Expand All @@ -83,7 +84,7 @@ open class ModuleListRecyclerAdapter(

}
isExpandedByDefault = false
// isDisplayEmptyCell = true TODO - make this work with scroll to functionality
isDisplayEmptyCell = true
if (adapterToFragmentCallback != null) loadData() // Callback is null when testing
}

Expand Down Expand Up @@ -136,6 +137,7 @@ open class ModuleListRecyclerAdapter(
}

override fun refresh() {
shouldExhaustPagination = false
mModuleItemCallbacks.clear()
checkCourseTabsJob?.cancel()
collapseAll()
Expand Down Expand Up @@ -339,8 +341,8 @@ open class ModuleListRecyclerAdapter(
ModuleManager.getFirstPageModuleItems(courseContext, it.id, getModuleItemsCallback(it, true), true)
}
}
if(!this.moreCallsExist()) {
// Wait until we are done exhausting pagination
if(!shouldExhaustPagination || !this.moreCallsExist()) {
// If we should exhaust pagination wait until we are done exhausting pagination
adapterToFragmentCallback?.onRefreshFinished()
}
}
Expand All @@ -358,7 +360,11 @@ open class ModuleListRecyclerAdapter(

// We only want to show modules if its a course nav option OR set to as the homepage
if (tabs.find { it.tabId == "modules" } != null || (courseContext as Course).homePage?.apiString == "modules") {
ModuleManager.getAllModuleObjets(courseContext, mModuleObjectCallback!!, true)
if (shouldExhaustPagination) {
ModuleManager.getAllModuleObjets(courseContext, mModuleObjectCallback!!, true)
} else {
ModuleManager.getFirstPageModuleObjects(courseContext, mModuleObjectCallback!!, true)
}
} else {
adapterToFragmentCallback?.onRefreshFinished(true)
}
Expand All @@ -367,6 +373,10 @@ open class ModuleListRecyclerAdapter(
}
}

override fun loadNextPage(nextURL: String) {
ModuleManager.getNextPageModuleObjects(nextURL, mModuleObjectCallback!!, true)
}

// endregion

// region Module binder Helpers
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ import com.instructure.canvasapi2.utils.ApiPrefs.user
import com.instructure.canvasapi2.utils.ApiType
import com.instructure.canvasapi2.utils.DateHelper
import com.instructure.canvasapi2.utils.LinkHeaders
import com.instructure.canvasapi2.utils.isNotDeleted
import com.instructure.pandarecycler.util.GroupSortedList.GroupComparatorCallback
import com.instructure.pandarecycler.util.GroupSortedList.ItemComparatorCallback
import com.instructure.pandarecycler.util.Types
Expand Down Expand Up @@ -156,7 +157,8 @@ class NotificationListRecyclerAdapter(

coursesCallback = object : StatusCallback<List<Course>>() {
override fun onResponse(response: Response<List<Course>>, linkHeaders: LinkHeaders, type: ApiType) {
courseMap = createCourseMap(response.body())
val courses = response.body()?.filter { it.isNotDeleted() }
courseMap = createCourseMap(courses)
populateActivityStreamAdapter()
}
}
Expand Down
Loading

0 comments on commit 4d8e05f

Please sign in to comment.