diff --git a/apps/flutter_parent/lib/l10n/res/intl_ar.arb b/apps/flutter_parent/lib/l10n/res/intl_ar.arb index 7dfec02c20..fd7594f1fd 100644 --- a/apps/flutter_parent/lib/l10n/res/intl_ar.arb +++ b/apps/flutter_parent/lib/l10n/res/intl_ar.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2023-02-17T11:03:20.619429", + "@@last_modified": "2023-04-14T11:04:46.988317", "alertsLabel": "التنبيهات", "@alertsLabel": { "description": "The label for the Alerts tab", @@ -182,6 +182,19 @@ "points": {} } }, + "calendarDaySemanticsLabel": "{eventCount,plural, =1{{date}، {eventCount} حدث}other{{date}، {eventCount} أحداث}}", + "@calendarDaySemanticsLabel": { + "description": "Screen reader label used for calendar day, reads the date and count of events", + "type": "text", + "placeholders_order": [ + "date", + "eventCount" + ], + "placeholders": { + "date": {}, + "eventCount": {} + } + }, "No Events Today!": "لا توجد أحداث اليوم!", "@No Events Today!": { "description": "Title displayed when there are no calendar events for the current day", @@ -2694,5 +2707,40 @@ "type": "text", "placeholders_order": [], "placeholders": {} + }, + "About": "نبذة", + "@About": { + "description": "Title for about menu item in settings", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "App": "التطبيق", + "@App": { + "description": "Title for App field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "Login ID": "معرف تسجيل الدخول", + "@Login ID": { + "description": "Title for Login ID field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "Email": "البريد الإلكتروني", + "@Email": { + "description": "Title for Email field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "Version": "الإصدار", + "@Version": { + "description": "Title for Version field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} } } \ No newline at end of file diff --git a/apps/flutter_parent/lib/l10n/res/intl_ca.arb b/apps/flutter_parent/lib/l10n/res/intl_ca.arb index 16aea4a2ce..9f2971adb7 100644 --- a/apps/flutter_parent/lib/l10n/res/intl_ca.arb +++ b/apps/flutter_parent/lib/l10n/res/intl_ca.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2023-02-17T11:03:20.619429", + "@@last_modified": "2023-04-14T11:04:46.988317", "alertsLabel": "Avisos", "@alertsLabel": { "description": "The label for the Alerts tab", @@ -182,6 +182,19 @@ "points": {} } }, + "calendarDaySemanticsLabel": "{eventCount,plural, =1{{date}, {eventCount} esdeveniment}other{{date}, {eventCount} esdeveniments}}", + "@calendarDaySemanticsLabel": { + "description": "Screen reader label used for calendar day, reads the date and count of events", + "type": "text", + "placeholders_order": [ + "date", + "eventCount" + ], + "placeholders": { + "date": {}, + "eventCount": {} + } + }, "No Events Today!": "Avui no hi ha cap esdeveniment.", "@No Events Today!": { "description": "Title displayed when there are no calendar events for the current day", @@ -2694,5 +2707,40 @@ "type": "text", "placeholders_order": [], "placeholders": {} + }, + "About": "Quant a", + "@About": { + "description": "Title for about menu item in settings", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "App": "Aplicació", + "@App": { + "description": "Title for App field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "Login ID": "ID d'inici de sessió", + "@Login ID": { + "description": "Title for Login ID field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "Email": "Correu electrònic", + "@Email": { + "description": "Title for Email field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "Version": "Versió", + "@Version": { + "description": "Title for Version field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} } } \ No newline at end of file diff --git a/apps/flutter_parent/lib/l10n/res/intl_cy.arb b/apps/flutter_parent/lib/l10n/res/intl_cy.arb index 4a08e4990b..3f62546257 100644 --- a/apps/flutter_parent/lib/l10n/res/intl_cy.arb +++ b/apps/flutter_parent/lib/l10n/res/intl_cy.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2023-02-17T11:03:20.619429", + "@@last_modified": "2023-04-14T11:04:46.988317", "alertsLabel": "Negeseuon Hysbysu", "@alertsLabel": { "description": "The label for the Alerts tab", @@ -182,6 +182,19 @@ "points": {} } }, + "calendarDaySemanticsLabel": "{eventCount,plural, =1{{date}, {eventCount} digwyddiad}other{{date}, {eventCount} digwyddiad}}", + "@calendarDaySemanticsLabel": { + "description": "Screen reader label used for calendar day, reads the date and count of events", + "type": "text", + "placeholders_order": [ + "date", + "eventCount" + ], + "placeholders": { + "date": {}, + "eventCount": {} + } + }, "No Events Today!": "Dim Digwyddiadau Heddiw!", "@No Events Today!": { "description": "Title displayed when there are no calendar events for the current day", @@ -2694,5 +2707,40 @@ "type": "text", "placeholders_order": [], "placeholders": {} + }, + "About": "Ynghylch", + "@About": { + "description": "Title for about menu item in settings", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "App": "Ap", + "@App": { + "description": "Title for App field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "Login ID": "ID Mewngofnodi", + "@Login ID": { + "description": "Title for Login ID field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "Email": "E-bost", + "@Email": { + "description": "Title for Email field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "Version": "Fersiwn", + "@Version": { + "description": "Title for Version field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} } } \ No newline at end of file diff --git a/apps/flutter_parent/lib/l10n/res/intl_da.arb b/apps/flutter_parent/lib/l10n/res/intl_da.arb index a9ce16b6d5..30f0d4b2ba 100644 --- a/apps/flutter_parent/lib/l10n/res/intl_da.arb +++ b/apps/flutter_parent/lib/l10n/res/intl_da.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2023-02-17T11:03:20.619429", + "@@last_modified": "2023-04-14T11:04:46.988317", "alertsLabel": "Varslinger", "@alertsLabel": { "description": "The label for the Alerts tab", @@ -182,6 +182,19 @@ "points": {} } }, + "calendarDaySemanticsLabel": "{eventCount,plural, =1{{date}, {eventCount} begivenhed}other{{date}, {eventCount} begivenheder}}", + "@calendarDaySemanticsLabel": { + "description": "Screen reader label used for calendar day, reads the date and count of events", + "type": "text", + "placeholders_order": [ + "date", + "eventCount" + ], + "placeholders": { + "date": {}, + "eventCount": {} + } + }, "No Events Today!": "Ingen begivenheder i dag!", "@No Events Today!": { "description": "Title displayed when there are no calendar events for the current day", @@ -2694,5 +2707,40 @@ "type": "text", "placeholders_order": [], "placeholders": {} + }, + "About": "Om", + "@About": { + "description": "Title for about menu item in settings", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "App": "App", + "@App": { + "description": "Title for App field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "Login ID": "Login-id", + "@Login ID": { + "description": "Title for Login ID field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "Email": "E-mail", + "@Email": { + "description": "Title for Email field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "Version": "Version", + "@Version": { + "description": "Title for Version field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} } } \ No newline at end of file diff --git a/apps/flutter_parent/lib/l10n/res/intl_da_instk12.arb b/apps/flutter_parent/lib/l10n/res/intl_da_instk12.arb index 39dcc14673..02860edf64 100644 --- a/apps/flutter_parent/lib/l10n/res/intl_da_instk12.arb +++ b/apps/flutter_parent/lib/l10n/res/intl_da_instk12.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2023-02-17T11:03:20.619429", + "@@last_modified": "2023-04-14T11:04:46.988317", "alertsLabel": "Varslinger", "@alertsLabel": { "description": "The label for the Alerts tab", @@ -182,6 +182,19 @@ "points": {} } }, + "calendarDaySemanticsLabel": "{eventCount,plural, =1{{date}, {eventCount} begivenhed}other{{date}, {eventCount} begivenheder}}", + "@calendarDaySemanticsLabel": { + "description": "Screen reader label used for calendar day, reads the date and count of events", + "type": "text", + "placeholders_order": [ + "date", + "eventCount" + ], + "placeholders": { + "date": {}, + "eventCount": {} + } + }, "No Events Today!": "Ingen begivenheder i dag!", "@No Events Today!": { "description": "Title displayed when there are no calendar events for the current day", @@ -2694,5 +2707,40 @@ "type": "text", "placeholders_order": [], "placeholders": {} + }, + "About": "Om", + "@About": { + "description": "Title for about menu item in settings", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "App": "App", + "@App": { + "description": "Title for App field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "Login ID": "Login-id", + "@Login ID": { + "description": "Title for Login ID field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "Email": "E-mail", + "@Email": { + "description": "Title for Email field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "Version": "Version", + "@Version": { + "description": "Title for Version field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} } } \ No newline at end of file diff --git a/apps/flutter_parent/lib/l10n/res/intl_de.arb b/apps/flutter_parent/lib/l10n/res/intl_de.arb index 14657d29ee..2f61a02006 100644 --- a/apps/flutter_parent/lib/l10n/res/intl_de.arb +++ b/apps/flutter_parent/lib/l10n/res/intl_de.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2023-02-17T11:03:20.619429", + "@@last_modified": "2023-04-14T11:04:46.988317", "alertsLabel": "Benachrichtigungen", "@alertsLabel": { "description": "The label for the Alerts tab", @@ -182,6 +182,19 @@ "points": {} } }, + "calendarDaySemanticsLabel": "{eventCount,plural, =1{{date}, {eventCount} Ereignis}other{{date}, {eventCount} Ereignisse}}", + "@calendarDaySemanticsLabel": { + "description": "Screen reader label used for calendar day, reads the date and count of events", + "type": "text", + "placeholders_order": [ + "date", + "eventCount" + ], + "placeholders": { + "date": {}, + "eventCount": {} + } + }, "No Events Today!": "Heute keine Ereignisse!", "@No Events Today!": { "description": "Title displayed when there are no calendar events for the current day", @@ -2694,5 +2707,40 @@ "type": "text", "placeholders_order": [], "placeholders": {} + }, + "About": "Info zu", + "@About": { + "description": "Title for about menu item in settings", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "App": "App", + "@App": { + "description": "Title for App field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "Login ID": "Anmelde-ID", + "@Login ID": { + "description": "Title for Login ID field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "Email": "E-Mail", + "@Email": { + "description": "Title for Email field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "Version": "Version", + "@Version": { + "description": "Title for Version field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} } } \ No newline at end of file diff --git a/apps/flutter_parent/lib/l10n/res/intl_en.arb b/apps/flutter_parent/lib/l10n/res/intl_en.arb index 7ce29fc9e8..9d2ca6f103 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": "2023-03-31T11:03:36.613144", + "@@last_modified": "2023-04-14T11:04:46.988317", "alertsLabel": "Alerts", "@alertsLabel": { "description": "The label for the Alerts tab", @@ -2707,5 +2707,40 @@ "type": "text", "placeholders_order": [], "placeholders": {} + }, + "About": "About", + "@About": { + "description": "Title for about menu item in settings", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "App": "App", + "@App": { + "description": "Title for App field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "Login ID": "Login ID", + "@Login ID": { + "description": "Title for Login ID field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "Email": "Email", + "@Email": { + "description": "Title for Email field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "Version": "Version", + "@Version": { + "description": "Title for Version field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} } } \ No newline at end of file diff --git a/apps/flutter_parent/lib/l10n/res/intl_en_AU.arb b/apps/flutter_parent/lib/l10n/res/intl_en_AU.arb index 38e2f80715..b3eed19106 100644 --- a/apps/flutter_parent/lib/l10n/res/intl_en_AU.arb +++ b/apps/flutter_parent/lib/l10n/res/intl_en_AU.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2023-02-17T11:03:20.619429", + "@@last_modified": "2023-04-14T11:04:46.988317", "alertsLabel": "Alerts", "@alertsLabel": { "description": "The label for the Alerts tab", @@ -182,6 +182,19 @@ "points": {} } }, + "calendarDaySemanticsLabel": "{eventCount,plural, =1{{date}, {eventCount} event}other{{date}, {eventCount} events}}", + "@calendarDaySemanticsLabel": { + "description": "Screen reader label used for calendar day, reads the date and count of events", + "type": "text", + "placeholders_order": [ + "date", + "eventCount" + ], + "placeholders": { + "date": {}, + "eventCount": {} + } + }, "No Events Today!": "No Events Today!", "@No Events Today!": { "description": "Title displayed when there are no calendar events for the current day", @@ -2694,5 +2707,40 @@ "type": "text", "placeholders_order": [], "placeholders": {} + }, + "About": "About", + "@About": { + "description": "Title for about menu item in settings", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "App": "App", + "@App": { + "description": "Title for App field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "Login ID": "Login ID", + "@Login ID": { + "description": "Title for Login ID field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "Email": "Email", + "@Email": { + "description": "Title for Email field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "Version": "Version", + "@Version": { + "description": "Title for Version field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} } } \ No newline at end of file diff --git a/apps/flutter_parent/lib/l10n/res/intl_en_AU_unimelb.arb b/apps/flutter_parent/lib/l10n/res/intl_en_AU_unimelb.arb index b657013ab7..b2b3fbdd7d 100644 --- a/apps/flutter_parent/lib/l10n/res/intl_en_AU_unimelb.arb +++ b/apps/flutter_parent/lib/l10n/res/intl_en_AU_unimelb.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2023-02-17T11:03:20.619429", + "@@last_modified": "2023-04-14T11:04:46.988317", "alertsLabel": "Alerts", "@alertsLabel": { "description": "The label for the Alerts tab", @@ -182,6 +182,19 @@ "points": {} } }, + "calendarDaySemanticsLabel": "{eventCount,plural, =1{{date}, {eventCount} event}other{{date}, {eventCount} events}}", + "@calendarDaySemanticsLabel": { + "description": "Screen reader label used for calendar day, reads the date and count of events", + "type": "text", + "placeholders_order": [ + "date", + "eventCount" + ], + "placeholders": { + "date": {}, + "eventCount": {} + } + }, "No Events Today!": "No Events Today!", "@No Events Today!": { "description": "Title displayed when there are no calendar events for the current day", @@ -2694,5 +2707,40 @@ "type": "text", "placeholders_order": [], "placeholders": {} + }, + "About": "About", + "@About": { + "description": "Title for about menu item in settings", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "App": "App", + "@App": { + "description": "Title for App field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "Login ID": "Login ID", + "@Login ID": { + "description": "Title for Login ID field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "Email": "Email", + "@Email": { + "description": "Title for Email field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "Version": "Version", + "@Version": { + "description": "Title for Version field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} } } \ No newline at end of file diff --git a/apps/flutter_parent/lib/l10n/res/intl_en_CY.arb b/apps/flutter_parent/lib/l10n/res/intl_en_CY.arb index be1386ee8a..8fe52d88ba 100644 --- a/apps/flutter_parent/lib/l10n/res/intl_en_CY.arb +++ b/apps/flutter_parent/lib/l10n/res/intl_en_CY.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2023-02-17T11:03:20.619429", + "@@last_modified": "2023-04-14T11:04:46.988317", "alertsLabel": "Alerts", "@alertsLabel": { "description": "The label for the Alerts tab", @@ -182,6 +182,19 @@ "points": {} } }, + "calendarDaySemanticsLabel": "{eventCount,plural, =1{{date}, {eventCount} event}other{{date}, {eventCount} events}}", + "@calendarDaySemanticsLabel": { + "description": "Screen reader label used for calendar day, reads the date and count of events", + "type": "text", + "placeholders_order": [ + "date", + "eventCount" + ], + "placeholders": { + "date": {}, + "eventCount": {} + } + }, "No Events Today!": "No Events Today!", "@No Events Today!": { "description": "Title displayed when there are no calendar events for the current day", @@ -2694,5 +2707,40 @@ "type": "text", "placeholders_order": [], "placeholders": {} + }, + "About": "About", + "@About": { + "description": "Title for about menu item in settings", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "App": "App", + "@App": { + "description": "Title for App field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "Login ID": "Login ID", + "@Login ID": { + "description": "Title for Login ID field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "Email": "Email", + "@Email": { + "description": "Title for Email field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "Version": "Version", + "@Version": { + "description": "Title for Version field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} } } \ No newline at end of file diff --git a/apps/flutter_parent/lib/l10n/res/intl_en_GB.arb b/apps/flutter_parent/lib/l10n/res/intl_en_GB.arb index 20c2335f72..61612c9b10 100644 --- a/apps/flutter_parent/lib/l10n/res/intl_en_GB.arb +++ b/apps/flutter_parent/lib/l10n/res/intl_en_GB.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2023-02-17T11:03:20.619429", + "@@last_modified": "2023-04-14T11:04:46.988317", "alertsLabel": "Alerts", "@alertsLabel": { "description": "The label for the Alerts tab", @@ -182,6 +182,19 @@ "points": {} } }, + "calendarDaySemanticsLabel": "{eventCount,plural, =1{{date}, {eventCount} event}other{{date}, {eventCount} events}}", + "@calendarDaySemanticsLabel": { + "description": "Screen reader label used for calendar day, reads the date and count of events", + "type": "text", + "placeholders_order": [ + "date", + "eventCount" + ], + "placeholders": { + "date": {}, + "eventCount": {} + } + }, "No Events Today!": "No Events Today!", "@No Events Today!": { "description": "Title displayed when there are no calendar events for the current day", @@ -2694,5 +2707,40 @@ "type": "text", "placeholders_order": [], "placeholders": {} + }, + "About": "About", + "@About": { + "description": "Title for about menu item in settings", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "App": "App", + "@App": { + "description": "Title for App field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "Login ID": "Login ID", + "@Login ID": { + "description": "Title for Login ID field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "Email": "Email", + "@Email": { + "description": "Title for Email field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "Version": "Version", + "@Version": { + "description": "Title for Version field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} } } \ No newline at end of file diff --git a/apps/flutter_parent/lib/l10n/res/intl_en_GB_instukhe.arb b/apps/flutter_parent/lib/l10n/res/intl_en_GB_instukhe.arb index be1386ee8a..8fe52d88ba 100644 --- a/apps/flutter_parent/lib/l10n/res/intl_en_GB_instukhe.arb +++ b/apps/flutter_parent/lib/l10n/res/intl_en_GB_instukhe.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2023-02-17T11:03:20.619429", + "@@last_modified": "2023-04-14T11:04:46.988317", "alertsLabel": "Alerts", "@alertsLabel": { "description": "The label for the Alerts tab", @@ -182,6 +182,19 @@ "points": {} } }, + "calendarDaySemanticsLabel": "{eventCount,plural, =1{{date}, {eventCount} event}other{{date}, {eventCount} events}}", + "@calendarDaySemanticsLabel": { + "description": "Screen reader label used for calendar day, reads the date and count of events", + "type": "text", + "placeholders_order": [ + "date", + "eventCount" + ], + "placeholders": { + "date": {}, + "eventCount": {} + } + }, "No Events Today!": "No Events Today!", "@No Events Today!": { "description": "Title displayed when there are no calendar events for the current day", @@ -2694,5 +2707,40 @@ "type": "text", "placeholders_order": [], "placeholders": {} + }, + "About": "About", + "@About": { + "description": "Title for about menu item in settings", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "App": "App", + "@App": { + "description": "Title for App field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "Login ID": "Login ID", + "@Login ID": { + "description": "Title for Login ID field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "Email": "Email", + "@Email": { + "description": "Title for Email field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "Version": "Version", + "@Version": { + "description": "Title for Version field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} } } \ No newline at end of file diff --git a/apps/flutter_parent/lib/l10n/res/intl_es.arb b/apps/flutter_parent/lib/l10n/res/intl_es.arb index 2e5a216b07..b2ff089429 100644 --- a/apps/flutter_parent/lib/l10n/res/intl_es.arb +++ b/apps/flutter_parent/lib/l10n/res/intl_es.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2023-02-17T11:03:20.619429", + "@@last_modified": "2023-04-14T11:04:46.988317", "alertsLabel": "Alertas", "@alertsLabel": { "description": "The label for the Alerts tab", @@ -182,6 +182,19 @@ "points": {} } }, + "calendarDaySemanticsLabel": "{eventCount,plural, =1{{date}, {eventCount} evento}other{{date}, {eventCount} eventos}}", + "@calendarDaySemanticsLabel": { + "description": "Screen reader label used for calendar day, reads the date and count of events", + "type": "text", + "placeholders_order": [ + "date", + "eventCount" + ], + "placeholders": { + "date": {}, + "eventCount": {} + } + }, "No Events Today!": "¡No hay ningún evento hoy!", "@No Events Today!": { "description": "Title displayed when there are no calendar events for the current day", @@ -2694,5 +2707,40 @@ "type": "text", "placeholders_order": [], "placeholders": {} + }, + "About": "Acerca de", + "@About": { + "description": "Title for about menu item in settings", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "App": "Aplicación", + "@App": { + "description": "Title for App field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "Login ID": "ID de inicio de sesión", + "@Login ID": { + "description": "Title for Login ID field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "Email": "Correo electrónico", + "@Email": { + "description": "Title for Email field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "Version": "Versión", + "@Version": { + "description": "Title for Version field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} } } \ No newline at end of file diff --git a/apps/flutter_parent/lib/l10n/res/intl_es_ES.arb b/apps/flutter_parent/lib/l10n/res/intl_es_ES.arb index 2a1ba17e2f..b539d7c9e3 100644 --- a/apps/flutter_parent/lib/l10n/res/intl_es_ES.arb +++ b/apps/flutter_parent/lib/l10n/res/intl_es_ES.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2023-02-17T11:03:20.619429", + "@@last_modified": "2023-04-14T11:04:46.988317", "alertsLabel": "Alertas", "@alertsLabel": { "description": "The label for the Alerts tab", @@ -182,6 +182,19 @@ "points": {} } }, + "calendarDaySemanticsLabel": "{eventCount,plural, =1{{date}, {eventCount} evento}other{{date}, {eventCount} eventos}}", + "@calendarDaySemanticsLabel": { + "description": "Screen reader label used for calendar day, reads the date and count of events", + "type": "text", + "placeholders_order": [ + "date", + "eventCount" + ], + "placeholders": { + "date": {}, + "eventCount": {} + } + }, "No Events Today!": "No hay ningún evento hoy", "@No Events Today!": { "description": "Title displayed when there are no calendar events for the current day", @@ -2694,5 +2707,40 @@ "type": "text", "placeholders_order": [], "placeholders": {} + }, + "About": "Acerca de", + "@About": { + "description": "Title for about menu item in settings", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "App": "Aplicación", + "@App": { + "description": "Title for App field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "Login ID": "Identificación de inicio de sesión", + "@Login ID": { + "description": "Title for Login ID field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "Email": "Correo electrónico", + "@Email": { + "description": "Title for Email field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "Version": "Versión", + "@Version": { + "description": "Title for Version field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} } } \ No newline at end of file diff --git a/apps/flutter_parent/lib/l10n/res/intl_fi.arb b/apps/flutter_parent/lib/l10n/res/intl_fi.arb index b9730c06fa..aa98e93a86 100644 --- a/apps/flutter_parent/lib/l10n/res/intl_fi.arb +++ b/apps/flutter_parent/lib/l10n/res/intl_fi.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2023-02-17T11:03:20.619429", + "@@last_modified": "2023-04-14T11:04:46.988317", "alertsLabel": "Hälytykset", "@alertsLabel": { "description": "The label for the Alerts tab", @@ -182,6 +182,19 @@ "points": {} } }, + "calendarDaySemanticsLabel": "{eventCount,plural, =1{{date}, {eventCount} tapahtuma}other{{date}, {eventCount} tapahtumaa}}", + "@calendarDaySemanticsLabel": { + "description": "Screen reader label used for calendar day, reads the date and count of events", + "type": "text", + "placeholders_order": [ + "date", + "eventCount" + ], + "placeholders": { + "date": {}, + "eventCount": {} + } + }, "No Events Today!": "Ei tapahtumia tänään!", "@No Events Today!": { "description": "Title displayed when there are no calendar events for the current day", @@ -2694,5 +2707,40 @@ "type": "text", "placeholders_order": [], "placeholders": {} + }, + "About": "Tietoja", + "@About": { + "description": "Title for about menu item in settings", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "App": "Sovellus", + "@App": { + "description": "Title for App field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "Login ID": "Käyttäjätunnus", + "@Login ID": { + "description": "Title for Login ID field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "Email": "Sähköposti", + "@Email": { + "description": "Title for Email field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "Version": "Versio", + "@Version": { + "description": "Title for Version field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} } } \ No newline at end of file diff --git a/apps/flutter_parent/lib/l10n/res/intl_fr.arb b/apps/flutter_parent/lib/l10n/res/intl_fr.arb index 28463a7420..9d143b26e7 100644 --- a/apps/flutter_parent/lib/l10n/res/intl_fr.arb +++ b/apps/flutter_parent/lib/l10n/res/intl_fr.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2023-02-17T11:03:20.619429", + "@@last_modified": "2023-04-14T11:04:46.988317", "alertsLabel": "Alertes", "@alertsLabel": { "description": "The label for the Alerts tab", @@ -182,6 +182,19 @@ "points": {} } }, + "calendarDaySemanticsLabel": "{eventCount,plural, =1{{date}, {eventCount} événement}other{{date}, {eventCount} événements}}", + "@calendarDaySemanticsLabel": { + "description": "Screen reader label used for calendar day, reads the date and count of events", + "type": "text", + "placeholders_order": [ + "date", + "eventCount" + ], + "placeholders": { + "date": {}, + "eventCount": {} + } + }, "No Events Today!": "Aucun événement aujourd'hui !", "@No Events Today!": { "description": "Title displayed when there are no calendar events for the current day", @@ -2694,5 +2707,40 @@ "type": "text", "placeholders_order": [], "placeholders": {} + }, + "About": "À propos de", + "@About": { + "description": "Title for about menu item in settings", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "App": "Application", + "@App": { + "description": "Title for App field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "Login ID": "ID d’authentification", + "@Login ID": { + "description": "Title for Login ID field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "Email": "Email", + "@Email": { + "description": "Title for Email field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "Version": "Version", + "@Version": { + "description": "Title for Version field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} } } \ No newline at end of file diff --git a/apps/flutter_parent/lib/l10n/res/intl_fr_CA.arb b/apps/flutter_parent/lib/l10n/res/intl_fr_CA.arb index b266dba62e..e114361c87 100644 --- a/apps/flutter_parent/lib/l10n/res/intl_fr_CA.arb +++ b/apps/flutter_parent/lib/l10n/res/intl_fr_CA.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2023-02-17T11:03:20.619429", + "@@last_modified": "2023-04-14T11:04:46.988317", "alertsLabel": "Alertes", "@alertsLabel": { "description": "The label for the Alerts tab", @@ -182,6 +182,19 @@ "points": {} } }, + "calendarDaySemanticsLabel": "{eventCount,plural, =1{{date}, {eventCount} événement}other{{date}, {eventCount} événements}}", + "@calendarDaySemanticsLabel": { + "description": "Screen reader label used for calendar day, reads the date and count of events", + "type": "text", + "placeholders_order": [ + "date", + "eventCount" + ], + "placeholders": { + "date": {}, + "eventCount": {} + } + }, "No Events Today!": "Aucun événement d’aujourd’hui!", "@No Events Today!": { "description": "Title displayed when there are no calendar events for the current day", @@ -2694,5 +2707,40 @@ "type": "text", "placeholders_order": [], "placeholders": {} + }, + "About": "À propos", + "@About": { + "description": "Title for about menu item in settings", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "App": "Appl.", + "@App": { + "description": "Title for App field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "Login ID": "Identifiant de connexion", + "@Login ID": { + "description": "Title for Login ID field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "Email": "Adresse électronique", + "@Email": { + "description": "Title for Email field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "Version": "Version", + "@Version": { + "description": "Title for Version field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} } } \ No newline at end of file diff --git a/apps/flutter_parent/lib/l10n/res/intl_ht.arb b/apps/flutter_parent/lib/l10n/res/intl_ht.arb index 588f9e714a..b558d309f1 100644 --- a/apps/flutter_parent/lib/l10n/res/intl_ht.arb +++ b/apps/flutter_parent/lib/l10n/res/intl_ht.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2023-02-17T11:03:20.619429", + "@@last_modified": "2023-04-14T11:04:46.988317", "alertsLabel": "Alèt", "@alertsLabel": { "description": "The label for the Alerts tab", @@ -182,6 +182,19 @@ "points": {} } }, + "calendarDaySemanticsLabel": "{eventCount,plural, =1{{date}, {eventCount} evènman}other{{date}, {eventCount} evènman}}", + "@calendarDaySemanticsLabel": { + "description": "Screen reader label used for calendar day, reads the date and count of events", + "type": "text", + "placeholders_order": [ + "date", + "eventCount" + ], + "placeholders": { + "date": {}, + "eventCount": {} + } + }, "No Events Today!": "Pa gen Aktivite Jodi a!", "@No Events Today!": { "description": "Title displayed when there are no calendar events for the current day", @@ -2694,5 +2707,40 @@ "type": "text", "placeholders_order": [], "placeholders": {} + }, + "About": "Apwopo", + "@About": { + "description": "Title for about menu item in settings", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "App": "App", + "@App": { + "description": "Title for App field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "Login ID": "ID Koneksyon", + "@Login ID": { + "description": "Title for Login ID field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "Email": "Imèl", + "@Email": { + "description": "Title for Email field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "Version": "Vèsyon", + "@Version": { + "description": "Title for Version field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} } } \ No newline at end of file diff --git a/apps/flutter_parent/lib/l10n/res/intl_is.arb b/apps/flutter_parent/lib/l10n/res/intl_is.arb index 45d6d480a1..7a09f7f413 100644 --- a/apps/flutter_parent/lib/l10n/res/intl_is.arb +++ b/apps/flutter_parent/lib/l10n/res/intl_is.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2023-02-17T11:03:20.619429", + "@@last_modified": "2023-04-14T11:04:46.988317", "alertsLabel": "Viðvaranir", "@alertsLabel": { "description": "The label for the Alerts tab", @@ -182,6 +182,19 @@ "points": {} } }, + "calendarDaySemanticsLabel": "{eventCount,plural, =1{{date}, {eventCount} viðburður}other{{date}, {eventCount} viðburðir}}", + "@calendarDaySemanticsLabel": { + "description": "Screen reader label used for calendar day, reads the date and count of events", + "type": "text", + "placeholders_order": [ + "date", + "eventCount" + ], + "placeholders": { + "date": {}, + "eventCount": {} + } + }, "No Events Today!": "Engir viðburðir í dag!", "@No Events Today!": { "description": "Title displayed when there are no calendar events for the current day", @@ -2694,5 +2707,40 @@ "type": "text", "placeholders_order": [], "placeholders": {} + }, + "About": "Um", + "@About": { + "description": "Title for about menu item in settings", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "App": "Smáforrit", + "@App": { + "description": "Title for App field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "Login ID": "Innskráningarauðkenni", + "@Login ID": { + "description": "Title for Login ID field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "Email": "Tölvupóstur", + "@Email": { + "description": "Title for Email field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "Version": "Útgáfa", + "@Version": { + "description": "Title for Version field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} } } \ No newline at end of file diff --git a/apps/flutter_parent/lib/l10n/res/intl_it.arb b/apps/flutter_parent/lib/l10n/res/intl_it.arb index d099672b31..2b0d6ee265 100644 --- a/apps/flutter_parent/lib/l10n/res/intl_it.arb +++ b/apps/flutter_parent/lib/l10n/res/intl_it.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2023-02-17T11:03:20.619429", + "@@last_modified": "2023-04-14T11:04:46.988317", "alertsLabel": "Avvisi", "@alertsLabel": { "description": "The label for the Alerts tab", @@ -182,6 +182,19 @@ "points": {} } }, + "calendarDaySemanticsLabel": "{eventCount,plural, =1{{date}, {eventCount} evento}other{{date}, {eventCount} eventi}}", + "@calendarDaySemanticsLabel": { + "description": "Screen reader label used for calendar day, reads the date and count of events", + "type": "text", + "placeholders_order": [ + "date", + "eventCount" + ], + "placeholders": { + "date": {}, + "eventCount": {} + } + }, "No Events Today!": "Nessun evento oggi!", "@No Events Today!": { "description": "Title displayed when there are no calendar events for the current day", @@ -2694,5 +2707,40 @@ "type": "text", "placeholders_order": [], "placeholders": {} + }, + "About": "Informazioni", + "@About": { + "description": "Title for about menu item in settings", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "App": "App", + "@App": { + "description": "Title for App field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "Login ID": "ID di accesso", + "@Login ID": { + "description": "Title for Login ID field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "Email": "E-mail", + "@Email": { + "description": "Title for Email field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "Version": "Versione", + "@Version": { + "description": "Title for Version field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} } } \ No newline at end of file diff --git a/apps/flutter_parent/lib/l10n/res/intl_ja.arb b/apps/flutter_parent/lib/l10n/res/intl_ja.arb index a32d862c4c..9b16649315 100644 --- a/apps/flutter_parent/lib/l10n/res/intl_ja.arb +++ b/apps/flutter_parent/lib/l10n/res/intl_ja.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2023-02-17T11:03:20.619429", + "@@last_modified": "2023-04-14T11:04:46.988317", "alertsLabel": "アラート", "@alertsLabel": { "description": "The label for the Alerts tab", @@ -182,6 +182,19 @@ "points": {} } }, + "calendarDaySemanticsLabel": "{eventCount,plural, =1{{date}、{eventCount}イベント}other{{date}、{eventCount}イベント}}", + "@calendarDaySemanticsLabel": { + "description": "Screen reader label used for calendar day, reads the date and count of events", + "type": "text", + "placeholders_order": [ + "date", + "eventCount" + ], + "placeholders": { + "date": {}, + "eventCount": {} + } + }, "No Events Today!": "今日イベントはありません!", "@No Events Today!": { "description": "Title displayed when there are no calendar events for the current day", @@ -2694,5 +2707,40 @@ "type": "text", "placeholders_order": [], "placeholders": {} + }, + "About": "情報", + "@About": { + "description": "Title for about menu item in settings", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "App": "アプリ", + "@App": { + "description": "Title for App field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "Login ID": "ログイン ID", + "@Login ID": { + "description": "Title for Login ID field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "Email": "E メール", + "@Email": { + "description": "Title for Email field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "Version": "バージョン", + "@Version": { + "description": "Title for Version field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} } } \ No newline at end of file diff --git a/apps/flutter_parent/lib/l10n/res/intl_messages.arb b/apps/flutter_parent/lib/l10n/res/intl_messages.arb index 7ce29fc9e8..9d2ca6f103 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": "2023-03-31T11:03:36.613144", + "@@last_modified": "2023-04-14T11:04:46.988317", "alertsLabel": "Alerts", "@alertsLabel": { "description": "The label for the Alerts tab", @@ -2707,5 +2707,40 @@ "type": "text", "placeholders_order": [], "placeholders": {} + }, + "About": "About", + "@About": { + "description": "Title for about menu item in settings", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "App": "App", + "@App": { + "description": "Title for App field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "Login ID": "Login ID", + "@Login ID": { + "description": "Title for Login ID field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "Email": "Email", + "@Email": { + "description": "Title for Email field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "Version": "Version", + "@Version": { + "description": "Title for Version field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} } } \ No newline at end of file diff --git a/apps/flutter_parent/lib/l10n/res/intl_mi.arb b/apps/flutter_parent/lib/l10n/res/intl_mi.arb index ffddad5267..488c681b32 100644 --- a/apps/flutter_parent/lib/l10n/res/intl_mi.arb +++ b/apps/flutter_parent/lib/l10n/res/intl_mi.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2023-02-17T11:03:20.619429", + "@@last_modified": "2023-04-14T11:04:46.988317", "alertsLabel": "He whakamataara", "@alertsLabel": { "description": "The label for the Alerts tab", @@ -182,6 +182,19 @@ "points": {} } }, + "calendarDaySemanticsLabel": "{eventCount,plural, =1{{date}, {eventCount} takahanga}other{{date}, {eventCount} takahanga}}", + "@calendarDaySemanticsLabel": { + "description": "Screen reader label used for calendar day, reads the date and count of events", + "type": "text", + "placeholders_order": [ + "date", + "eventCount" + ], + "placeholders": { + "date": {}, + "eventCount": {} + } + }, "No Events Today!": "Kaore he tauwhāinga i tēnei rā!", "@No Events Today!": { "description": "Title displayed when there are no calendar events for the current day", @@ -2694,5 +2707,40 @@ "type": "text", "placeholders_order": [], "placeholders": {} + }, + "About": "Mō", + "@About": { + "description": "Title for about menu item in settings", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "App": "Taupānga", + "@App": { + "description": "Title for App field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "Login ID": "Takiuru ID", + "@Login ID": { + "description": "Title for Login ID field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "Email": "Īmēra", + "@Email": { + "description": "Title for Email field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "Version": "Putanga", + "@Version": { + "description": "Title for Version field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} } } \ No newline at end of file diff --git a/apps/flutter_parent/lib/l10n/res/intl_ms.arb b/apps/flutter_parent/lib/l10n/res/intl_ms.arb index 06d80ecc18..2e83b495d5 100644 --- a/apps/flutter_parent/lib/l10n/res/intl_ms.arb +++ b/apps/flutter_parent/lib/l10n/res/intl_ms.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2023-02-17T11:03:20.619429", + "@@last_modified": "2023-04-14T11:04:46.988317", "alertsLabel": "Isyarat", "@alertsLabel": { "description": "The label for the Alerts tab", @@ -182,6 +182,19 @@ "points": {} } }, + "calendarDaySemanticsLabel": "{eventCount,plural, =1{{date}, {eventCount} acara}other{{date}, {eventCount} acara}}", + "@calendarDaySemanticsLabel": { + "description": "Screen reader label used for calendar day, reads the date and count of events", + "type": "text", + "placeholders_order": [ + "date", + "eventCount" + ], + "placeholders": { + "date": {}, + "eventCount": {} + } + }, "No Events Today!": "Tiada Acara Hari Ini!", "@No Events Today!": { "description": "Title displayed when there are no calendar events for the current day", @@ -2694,5 +2707,40 @@ "type": "text", "placeholders_order": [], "placeholders": {} + }, + "About": "Perihal", + "@About": { + "description": "Title for about menu item in settings", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "App": "Apl", + "@App": { + "description": "Title for App field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "Login ID": "ID Log Masuk", + "@Login ID": { + "description": "Title for Login ID field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "Email": "E-mel", + "@Email": { + "description": "Title for Email field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "Version": "Versi", + "@Version": { + "description": "Title for Version field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} } } \ No newline at end of file diff --git a/apps/flutter_parent/lib/l10n/res/intl_nb.arb b/apps/flutter_parent/lib/l10n/res/intl_nb.arb index 50d81d9447..c272e85eba 100644 --- a/apps/flutter_parent/lib/l10n/res/intl_nb.arb +++ b/apps/flutter_parent/lib/l10n/res/intl_nb.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2023-02-17T11:03:20.619429", + "@@last_modified": "2023-04-14T11:04:46.988317", "alertsLabel": "Varsler", "@alertsLabel": { "description": "The label for the Alerts tab", @@ -182,6 +182,19 @@ "points": {} } }, + "calendarDaySemanticsLabel": "{eventCount,plural, =1{{date}, {eventCount} hendelse}other{{date}, {eventCount} hendelser}}", + "@calendarDaySemanticsLabel": { + "description": "Screen reader label used for calendar day, reads the date and count of events", + "type": "text", + "placeholders_order": [ + "date", + "eventCount" + ], + "placeholders": { + "date": {}, + "eventCount": {} + } + }, "No Events Today!": "Ingen arrangementer i dag!", "@No Events Today!": { "description": "Title displayed when there are no calendar events for the current day", @@ -2694,5 +2707,40 @@ "type": "text", "placeholders_order": [], "placeholders": {} + }, + "About": "Om", + "@About": { + "description": "Title for about menu item in settings", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "App": "App", + "@App": { + "description": "Title for App field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "Login ID": "Innloggings-ID", + "@Login ID": { + "description": "Title for Login ID field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "Email": "E-post", + "@Email": { + "description": "Title for Email field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "Version": "Versjon", + "@Version": { + "description": "Title for Version field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} } } \ No newline at end of file diff --git a/apps/flutter_parent/lib/l10n/res/intl_nb_instk12.arb b/apps/flutter_parent/lib/l10n/res/intl_nb_instk12.arb index af302f925c..75c8d09084 100644 --- a/apps/flutter_parent/lib/l10n/res/intl_nb_instk12.arb +++ b/apps/flutter_parent/lib/l10n/res/intl_nb_instk12.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2023-02-17T11:03:20.619429", + "@@last_modified": "2023-04-14T11:04:46.988317", "alertsLabel": "Varsler", "@alertsLabel": { "description": "The label for the Alerts tab", @@ -182,6 +182,19 @@ "points": {} } }, + "calendarDaySemanticsLabel": "{eventCount,plural, =1{{date}, {eventCount} hendelse}other{{date}, {eventCount} hendelser}}", + "@calendarDaySemanticsLabel": { + "description": "Screen reader label used for calendar day, reads the date and count of events", + "type": "text", + "placeholders_order": [ + "date", + "eventCount" + ], + "placeholders": { + "date": {}, + "eventCount": {} + } + }, "No Events Today!": "Ingen arrangementer i dag!", "@No Events Today!": { "description": "Title displayed when there are no calendar events for the current day", @@ -2694,5 +2707,40 @@ "type": "text", "placeholders_order": [], "placeholders": {} + }, + "About": "Om", + "@About": { + "description": "Title for about menu item in settings", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "App": "App", + "@App": { + "description": "Title for App field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "Login ID": "Innloggings-ID", + "@Login ID": { + "description": "Title for Login ID field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "Email": "E-post", + "@Email": { + "description": "Title for Email field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "Version": "Versjon", + "@Version": { + "description": "Title for Version field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} } } \ No newline at end of file diff --git a/apps/flutter_parent/lib/l10n/res/intl_nl.arb b/apps/flutter_parent/lib/l10n/res/intl_nl.arb index 22ea3b08b4..27668187aa 100644 --- a/apps/flutter_parent/lib/l10n/res/intl_nl.arb +++ b/apps/flutter_parent/lib/l10n/res/intl_nl.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2023-02-17T11:03:20.619429", + "@@last_modified": "2023-04-14T11:04:46.988317", "alertsLabel": "Waarschuwingen", "@alertsLabel": { "description": "The label for the Alerts tab", @@ -182,6 +182,19 @@ "points": {} } }, + "calendarDaySemanticsLabel": "{eventCount,plural, =1{{date}, {eventCount} gebeurtenis}other{{date}, {eventCount} gebeurtenissen}}", + "@calendarDaySemanticsLabel": { + "description": "Screen reader label used for calendar day, reads the date and count of events", + "type": "text", + "placeholders_order": [ + "date", + "eventCount" + ], + "placeholders": { + "date": {}, + "eventCount": {} + } + }, "No Events Today!": "Vandaag geen gebeurtenissen!", "@No Events Today!": { "description": "Title displayed when there are no calendar events for the current day", @@ -2694,5 +2707,40 @@ "type": "text", "placeholders_order": [], "placeholders": {} + }, + "About": "Over", + "@About": { + "description": "Title for about menu item in settings", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "App": "App", + "@App": { + "description": "Title for App field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "Login ID": "Aanmeldings-ID", + "@Login ID": { + "description": "Title for Login ID field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "Email": "E-mail", + "@Email": { + "description": "Title for Email field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "Version": "Versie", + "@Version": { + "description": "Title for Version field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} } } \ No newline at end of file diff --git a/apps/flutter_parent/lib/l10n/res/intl_pl.arb b/apps/flutter_parent/lib/l10n/res/intl_pl.arb index 98a06c5d5d..dcc306e354 100644 --- a/apps/flutter_parent/lib/l10n/res/intl_pl.arb +++ b/apps/flutter_parent/lib/l10n/res/intl_pl.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2023-02-17T11:03:20.619429", + "@@last_modified": "2023-04-14T11:04:46.988317", "alertsLabel": "Alerty", "@alertsLabel": { "description": "The label for the Alerts tab", @@ -182,6 +182,19 @@ "points": {} } }, + "calendarDaySemanticsLabel": "{eventCount,plural, =1{{date}, {eventCount} zdarzenie}other{{date}, {eventCount} zdarzenia}}", + "@calendarDaySemanticsLabel": { + "description": "Screen reader label used for calendar day, reads the date and count of events", + "type": "text", + "placeholders_order": [ + "date", + "eventCount" + ], + "placeholders": { + "date": {}, + "eventCount": {} + } + }, "No Events Today!": "Brak wydarzeń na dziś!", "@No Events Today!": { "description": "Title displayed when there are no calendar events for the current day", @@ -2694,5 +2707,40 @@ "type": "text", "placeholders_order": [], "placeholders": {} + }, + "About": "Informacje", + "@About": { + "description": "Title for about menu item in settings", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "App": "Aplikacja", + "@App": { + "description": "Title for App field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "Login ID": "Nazwa użytkownika", + "@Login ID": { + "description": "Title for Login ID field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "Email": "E-mail", + "@Email": { + "description": "Title for Email field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "Version": "Wersja", + "@Version": { + "description": "Title for Version field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} } } \ No newline at end of file diff --git a/apps/flutter_parent/lib/l10n/res/intl_pt_BR.arb b/apps/flutter_parent/lib/l10n/res/intl_pt_BR.arb index c557a68c79..58ba0f7119 100644 --- a/apps/flutter_parent/lib/l10n/res/intl_pt_BR.arb +++ b/apps/flutter_parent/lib/l10n/res/intl_pt_BR.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2023-02-17T11:03:20.619429", + "@@last_modified": "2023-04-14T11:04:46.988317", "alertsLabel": "Alertas", "@alertsLabel": { "description": "The label for the Alerts tab", @@ -182,6 +182,19 @@ "points": {} } }, + "calendarDaySemanticsLabel": "{eventCount,plural, =1{{date}, {eventCount} evento}other{{date}, {eventCount} eventos}}", + "@calendarDaySemanticsLabel": { + "description": "Screen reader label used for calendar day, reads the date and count of events", + "type": "text", + "placeholders_order": [ + "date", + "eventCount" + ], + "placeholders": { + "date": {}, + "eventCount": {} + } + }, "No Events Today!": "Nenhum evento hoje!", "@No Events Today!": { "description": "Title displayed when there are no calendar events for the current day", @@ -2694,5 +2707,40 @@ "type": "text", "placeholders_order": [], "placeholders": {} + }, + "About": "Sobre", + "@About": { + "description": "Title for about menu item in settings", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "App": "Aplicativo", + "@App": { + "description": "Title for App field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "Login ID": "ID de Login", + "@Login ID": { + "description": "Title for Login ID field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "Email": "E-mail", + "@Email": { + "description": "Title for Email field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "Version": "Versão", + "@Version": { + "description": "Title for Version field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} } } \ No newline at end of file diff --git a/apps/flutter_parent/lib/l10n/res/intl_pt_PT.arb b/apps/flutter_parent/lib/l10n/res/intl_pt_PT.arb index d33eb2b717..6505af0e5a 100644 --- a/apps/flutter_parent/lib/l10n/res/intl_pt_PT.arb +++ b/apps/flutter_parent/lib/l10n/res/intl_pt_PT.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2023-02-17T11:03:20.619429", + "@@last_modified": "2023-04-14T11:04:46.988317", "alertsLabel": "Alertas", "@alertsLabel": { "description": "The label for the Alerts tab", @@ -182,6 +182,19 @@ "points": {} } }, + "calendarDaySemanticsLabel": "{eventCount,plural, =1{{date}, {eventCount} evento}other{{date}, {eventCount} eventos}}", + "@calendarDaySemanticsLabel": { + "description": "Screen reader label used for calendar day, reads the date and count of events", + "type": "text", + "placeholders_order": [ + "date", + "eventCount" + ], + "placeholders": { + "date": {}, + "eventCount": {} + } + }, "No Events Today!": "Nenhum evento hoje!", "@No Events Today!": { "description": "Title displayed when there are no calendar events for the current day", @@ -2694,5 +2707,40 @@ "type": "text", "placeholders_order": [], "placeholders": {} + }, + "About": "Sobre", + "@About": { + "description": "Title for about menu item in settings", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "App": "Aplicação", + "@App": { + "description": "Title for App field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "Login ID": "ID de login", + "@Login ID": { + "description": "Title for Login ID field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "Email": "E-mail", + "@Email": { + "description": "Title for Email field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "Version": "Versão", + "@Version": { + "description": "Title for Version field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} } } \ No newline at end of file diff --git a/apps/flutter_parent/lib/l10n/res/intl_ru.arb b/apps/flutter_parent/lib/l10n/res/intl_ru.arb index 63e0bb16e1..1f5673f194 100644 --- a/apps/flutter_parent/lib/l10n/res/intl_ru.arb +++ b/apps/flutter_parent/lib/l10n/res/intl_ru.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2023-02-17T11:03:20.619429", + "@@last_modified": "2023-04-14T11:04:46.988317", "alertsLabel": "Предупреждения", "@alertsLabel": { "description": "The label for the Alerts tab", @@ -182,6 +182,19 @@ "points": {} } }, + "calendarDaySemanticsLabel": "{eventCount,plural, =1{{date}, {eventCount} событие}other{{date}, {eventCount} события}}", + "@calendarDaySemanticsLabel": { + "description": "Screen reader label used for calendar day, reads the date and count of events", + "type": "text", + "placeholders_order": [ + "date", + "eventCount" + ], + "placeholders": { + "date": {}, + "eventCount": {} + } + }, "No Events Today!": "На сегодня события отсутствуют!", "@No Events Today!": { "description": "Title displayed when there are no calendar events for the current day", @@ -2694,5 +2707,40 @@ "type": "text", "placeholders_order": [], "placeholders": {} + }, + "About": "О проекте", + "@About": { + "description": "Title for about menu item in settings", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "App": "Приложение", + "@App": { + "description": "Title for App field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "Login ID": "Имя пользователя", + "@Login ID": { + "description": "Title for Login ID field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "Email": "Адрес электронной почты", + "@Email": { + "description": "Title for Email field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "Version": "Версия", + "@Version": { + "description": "Title for Version field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} } } \ No newline at end of file diff --git a/apps/flutter_parent/lib/l10n/res/intl_sl.arb b/apps/flutter_parent/lib/l10n/res/intl_sl.arb index 8b1e905614..30cc7a1e70 100644 --- a/apps/flutter_parent/lib/l10n/res/intl_sl.arb +++ b/apps/flutter_parent/lib/l10n/res/intl_sl.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2023-02-17T11:03:20.619429", + "@@last_modified": "2023-04-14T11:04:46.988317", "alertsLabel": "Opozorila", "@alertsLabel": { "description": "The label for the Alerts tab", @@ -182,6 +182,19 @@ "points": {} } }, + "calendarDaySemanticsLabel": "{eventCount,plural, =1{{date}, {eventCount} dogodek}other{{date}, {eventCount} dogodkov(-a/-i)}}", + "@calendarDaySemanticsLabel": { + "description": "Screen reader label used for calendar day, reads the date and count of events", + "type": "text", + "placeholders_order": [ + "date", + "eventCount" + ], + "placeholders": { + "date": {}, + "eventCount": {} + } + }, "No Events Today!": "Danes ni dogodkov.", "@No Events Today!": { "description": "Title displayed when there are no calendar events for the current day", @@ -2694,5 +2707,40 @@ "type": "text", "placeholders_order": [], "placeholders": {} + }, + "About": "Vizitka", + "@About": { + "description": "Title for about menu item in settings", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "App": "Aplikacija", + "@App": { + "description": "Title for App field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "Login ID": "ID prijave", + "@Login ID": { + "description": "Title for Login ID field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "Email": "E-pošta", + "@Email": { + "description": "Title for Email field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "Version": "Različica", + "@Version": { + "description": "Title for Version field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} } } \ No newline at end of file diff --git a/apps/flutter_parent/lib/l10n/res/intl_sv.arb b/apps/flutter_parent/lib/l10n/res/intl_sv.arb index 56928c1f25..2520c94ae0 100644 --- a/apps/flutter_parent/lib/l10n/res/intl_sv.arb +++ b/apps/flutter_parent/lib/l10n/res/intl_sv.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2023-02-17T11:03:20.619429", + "@@last_modified": "2023-04-14T11:04:46.988317", "alertsLabel": "Notiser", "@alertsLabel": { "description": "The label for the Alerts tab", @@ -182,6 +182,19 @@ "points": {} } }, + "calendarDaySemanticsLabel": "{eventCount,plural, =1{{date}, {eventCount} händelse}other{{date}, {eventCount} händelser}}", + "@calendarDaySemanticsLabel": { + "description": "Screen reader label used for calendar day, reads the date and count of events", + "type": "text", + "placeholders_order": [ + "date", + "eventCount" + ], + "placeholders": { + "date": {}, + "eventCount": {} + } + }, "No Events Today!": "Inga händelser idag!", "@No Events Today!": { "description": "Title displayed when there are no calendar events for the current day", @@ -2694,5 +2707,40 @@ "type": "text", "placeholders_order": [], "placeholders": {} + }, + "About": "Om", + "@About": { + "description": "Title for about menu item in settings", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "App": "App", + "@App": { + "description": "Title for App field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "Login ID": "Inloggnings-ID", + "@Login ID": { + "description": "Title for Login ID field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "Email": "E-post", + "@Email": { + "description": "Title for Email field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "Version": "Version", + "@Version": { + "description": "Title for Version field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} } } \ No newline at end of file diff --git a/apps/flutter_parent/lib/l10n/res/intl_sv_instk12.arb b/apps/flutter_parent/lib/l10n/res/intl_sv_instk12.arb index 39df926726..d9196a45bb 100644 --- a/apps/flutter_parent/lib/l10n/res/intl_sv_instk12.arb +++ b/apps/flutter_parent/lib/l10n/res/intl_sv_instk12.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2023-02-17T11:03:20.619429", + "@@last_modified": "2023-04-14T11:04:46.988317", "alertsLabel": "Notiser", "@alertsLabel": { "description": "The label for the Alerts tab", @@ -182,6 +182,19 @@ "points": {} } }, + "calendarDaySemanticsLabel": "{eventCount,plural, =1{{date}, {eventCount} händelse}other{{date}, {eventCount} händelser}}", + "@calendarDaySemanticsLabel": { + "description": "Screen reader label used for calendar day, reads the date and count of events", + "type": "text", + "placeholders_order": [ + "date", + "eventCount" + ], + "placeholders": { + "date": {}, + "eventCount": {} + } + }, "No Events Today!": "Inga händelser idag!", "@No Events Today!": { "description": "Title displayed when there are no calendar events for the current day", @@ -2694,5 +2707,40 @@ "type": "text", "placeholders_order": [], "placeholders": {} + }, + "About": "Om", + "@About": { + "description": "Title for about menu item in settings", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "App": "App", + "@App": { + "description": "Title for App field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "Login ID": "Inloggnings-ID", + "@Login ID": { + "description": "Title for Login ID field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "Email": "E-post", + "@Email": { + "description": "Title for Email field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "Version": "Version", + "@Version": { + "description": "Title for Version field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} } } \ No newline at end of file diff --git a/apps/flutter_parent/lib/l10n/res/intl_th.arb b/apps/flutter_parent/lib/l10n/res/intl_th.arb index 7200a746b4..4b9c774051 100644 --- a/apps/flutter_parent/lib/l10n/res/intl_th.arb +++ b/apps/flutter_parent/lib/l10n/res/intl_th.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2023-02-17T11:03:20.619429", + "@@last_modified": "2023-04-14T11:04:46.988317", "alertsLabel": "แจ้งเตือน", "@alertsLabel": { "description": "The label for the Alerts tab", @@ -182,6 +182,19 @@ "points": {} } }, + "calendarDaySemanticsLabel": "{eventCount,plural, =1{{date}, {eventCount} กิจกรรม}other{{date}, {eventCount} กิจกรรม}}", + "@calendarDaySemanticsLabel": { + "description": "Screen reader label used for calendar day, reads the date and count of events", + "type": "text", + "placeholders_order": [ + "date", + "eventCount" + ], + "placeholders": { + "date": {}, + "eventCount": {} + } + }, "No Events Today!": "ไม่มีกิจกรรมในวันนี้!", "@No Events Today!": { "description": "Title displayed when there are no calendar events for the current day", @@ -2694,5 +2707,40 @@ "type": "text", "placeholders_order": [], "placeholders": {} + }, + "About": "เกี่ยวกับ", + "@About": { + "description": "Title for about menu item in settings", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "App": "แอพ", + "@App": { + "description": "Title for App field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "Login ID": "ID ล็อกอิน", + "@Login ID": { + "description": "Title for Login ID field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "Email": "อีเมล", + "@Email": { + "description": "Title for Email field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "Version": "เวอร์ชั่น", + "@Version": { + "description": "Title for Version field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} } } \ No newline at end of file diff --git a/apps/flutter_parent/lib/l10n/res/intl_vi.arb b/apps/flutter_parent/lib/l10n/res/intl_vi.arb index b1178043b0..466548adb1 100644 --- a/apps/flutter_parent/lib/l10n/res/intl_vi.arb +++ b/apps/flutter_parent/lib/l10n/res/intl_vi.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2023-02-17T11:03:20.619429", + "@@last_modified": "2023-04-14T11:04:46.988317", "alertsLabel": "Cảnh Báo", "@alertsLabel": { "description": "The label for the Alerts tab", @@ -182,6 +182,19 @@ "points": {} } }, + "calendarDaySemanticsLabel": "{eventCount,plural, =1{{date}, {eventCount} sự kiện}other{{date}, {eventCount} sự kiện}}", + "@calendarDaySemanticsLabel": { + "description": "Screen reader label used for calendar day, reads the date and count of events", + "type": "text", + "placeholders_order": [ + "date", + "eventCount" + ], + "placeholders": { + "date": {}, + "eventCount": {} + } + }, "No Events Today!": "Không Có Sự Kiện Hôm Nay!", "@No Events Today!": { "description": "Title displayed when there are no calendar events for the current day", @@ -2694,5 +2707,40 @@ "type": "text", "placeholders_order": [], "placeholders": {} + }, + "About": "Giới Thiệu", + "@About": { + "description": "Title for about menu item in settings", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "App": "Ứng Dụng", + "@App": { + "description": "Title for App field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "Login ID": "ID Đăng Nhập", + "@Login ID": { + "description": "Title for Login ID field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "Email": "Email", + "@Email": { + "description": "Title for Email field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "Version": "Phiên bản", + "@Version": { + "description": "Title for Version field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} } } \ No newline at end of file diff --git a/apps/flutter_parent/lib/l10n/res/intl_zh.arb b/apps/flutter_parent/lib/l10n/res/intl_zh.arb index e2c3d89c68..bcb61c5d1d 100644 --- a/apps/flutter_parent/lib/l10n/res/intl_zh.arb +++ b/apps/flutter_parent/lib/l10n/res/intl_zh.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2023-02-17T11:03:20.619429", + "@@last_modified": "2023-04-14T11:04:46.988317", "alertsLabel": "警告", "@alertsLabel": { "description": "The label for the Alerts tab", @@ -182,6 +182,19 @@ "points": {} } }, + "calendarDaySemanticsLabel": "{eventCount,plural, =1{{date}、{eventCount} 个事件}other{{date}、{eventCount} 个事件}}", + "@calendarDaySemanticsLabel": { + "description": "Screen reader label used for calendar day, reads the date and count of events", + "type": "text", + "placeholders_order": [ + "date", + "eventCount" + ], + "placeholders": { + "date": {}, + "eventCount": {} + } + }, "No Events Today!": "今天没有事件!", "@No Events Today!": { "description": "Title displayed when there are no calendar events for the current day", @@ -2694,5 +2707,40 @@ "type": "text", "placeholders_order": [], "placeholders": {} + }, + "About": "关于", + "@About": { + "description": "Title for about menu item in settings", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "App": "应用程序", + "@App": { + "description": "Title for App field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "Login ID": "登录 ID", + "@Login ID": { + "description": "Title for Login ID field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "Email": "电子邮件", + "@Email": { + "description": "Title for Email field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "Version": "版本", + "@Version": { + "description": "Title for Version field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} } } \ No newline at end of file diff --git a/apps/flutter_parent/lib/l10n/res/intl_zh_HK.arb b/apps/flutter_parent/lib/l10n/res/intl_zh_HK.arb index b2d07344b8..c19d37b2ac 100644 --- a/apps/flutter_parent/lib/l10n/res/intl_zh_HK.arb +++ b/apps/flutter_parent/lib/l10n/res/intl_zh_HK.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2023-02-17T11:03:20.619429", + "@@last_modified": "2023-04-14T11:04:46.988317", "alertsLabel": "提醒", "@alertsLabel": { "description": "The label for the Alerts tab", @@ -182,6 +182,19 @@ "points": {} } }, + "calendarDaySemanticsLabel": "{eventCount,plural, =1{{date}、{eventCount} 活動}other{{date}、{eventCount} 活動}}", + "@calendarDaySemanticsLabel": { + "description": "Screen reader label used for calendar day, reads the date and count of events", + "type": "text", + "placeholders_order": [ + "date", + "eventCount" + ], + "placeholders": { + "date": {}, + "eventCount": {} + } + }, "No Events Today!": "今天並無活動!", "@No Events Today!": { "description": "Title displayed when there are no calendar events for the current day", @@ -2694,5 +2707,40 @@ "type": "text", "placeholders_order": [], "placeholders": {} + }, + "About": "關於", + "@About": { + "description": "Title for about menu item in settings", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "App": "應用程式", + "@App": { + "description": "Title for App field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "Login ID": "登入 ID", + "@Login ID": { + "description": "Title for Login ID field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "Email": "電郵", + "@Email": { + "description": "Title for Email field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "Version": "版本", + "@Version": { + "description": "Title for Version field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} } } \ No newline at end of file diff --git a/apps/flutter_parent/lib/l10n/res/intl_zh_Hans.arb b/apps/flutter_parent/lib/l10n/res/intl_zh_Hans.arb index e2c3d89c68..bcb61c5d1d 100644 --- a/apps/flutter_parent/lib/l10n/res/intl_zh_Hans.arb +++ b/apps/flutter_parent/lib/l10n/res/intl_zh_Hans.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2023-02-17T11:03:20.619429", + "@@last_modified": "2023-04-14T11:04:46.988317", "alertsLabel": "警告", "@alertsLabel": { "description": "The label for the Alerts tab", @@ -182,6 +182,19 @@ "points": {} } }, + "calendarDaySemanticsLabel": "{eventCount,plural, =1{{date}、{eventCount} 个事件}other{{date}、{eventCount} 个事件}}", + "@calendarDaySemanticsLabel": { + "description": "Screen reader label used for calendar day, reads the date and count of events", + "type": "text", + "placeholders_order": [ + "date", + "eventCount" + ], + "placeholders": { + "date": {}, + "eventCount": {} + } + }, "No Events Today!": "今天没有事件!", "@No Events Today!": { "description": "Title displayed when there are no calendar events for the current day", @@ -2694,5 +2707,40 @@ "type": "text", "placeholders_order": [], "placeholders": {} + }, + "About": "关于", + "@About": { + "description": "Title for about menu item in settings", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "App": "应用程序", + "@App": { + "description": "Title for App field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "Login ID": "登录 ID", + "@Login ID": { + "description": "Title for Login ID field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "Email": "电子邮件", + "@Email": { + "description": "Title for Email field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "Version": "版本", + "@Version": { + "description": "Title for Version field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} } } \ No newline at end of file diff --git a/apps/flutter_parent/lib/l10n/res/intl_zh_Hant.arb b/apps/flutter_parent/lib/l10n/res/intl_zh_Hant.arb index b2d07344b8..c19d37b2ac 100644 --- a/apps/flutter_parent/lib/l10n/res/intl_zh_Hant.arb +++ b/apps/flutter_parent/lib/l10n/res/intl_zh_Hant.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2023-02-17T11:03:20.619429", + "@@last_modified": "2023-04-14T11:04:46.988317", "alertsLabel": "提醒", "@alertsLabel": { "description": "The label for the Alerts tab", @@ -182,6 +182,19 @@ "points": {} } }, + "calendarDaySemanticsLabel": "{eventCount,plural, =1{{date}、{eventCount} 活動}other{{date}、{eventCount} 活動}}", + "@calendarDaySemanticsLabel": { + "description": "Screen reader label used for calendar day, reads the date and count of events", + "type": "text", + "placeholders_order": [ + "date", + "eventCount" + ], + "placeholders": { + "date": {}, + "eventCount": {} + } + }, "No Events Today!": "今天並無活動!", "@No Events Today!": { "description": "Title displayed when there are no calendar events for the current day", @@ -2694,5 +2707,40 @@ "type": "text", "placeholders_order": [], "placeholders": {} + }, + "About": "關於", + "@About": { + "description": "Title for about menu item in settings", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "App": "應用程式", + "@App": { + "description": "Title for App field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "Login ID": "登入 ID", + "@Login ID": { + "description": "Title for Login ID field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "Email": "電郵", + "@Email": { + "description": "Title for Email field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "Version": "版本", + "@Version": { + "description": "Title for Version field on about page", + "type": "text", + "placeholders_order": [], + "placeholders": {} } } \ No newline at end of file diff --git a/apps/flutter_parent/lib/screens/login_landing_screen.dart b/apps/flutter_parent/lib/screens/login_landing_screen.dart index d4c3f6dd36..7c66ea9cf9 100644 --- a/apps/flutter_parent/lib/screens/login_landing_screen.dart +++ b/apps/flutter_parent/lib/screens/login_landing_screen.dart @@ -203,6 +203,7 @@ class LoginLandingScreen extends StatelessWidget { child: Text( title, style: TextStyle(fontSize: 16), + overflow: TextOverflow.ellipsis, ), ), color: Theme.of(context).accentColor, diff --git a/apps/flutter_parent/pubspec.yaml b/apps/flutter_parent/pubspec.yaml index d4dc449583..021b319332 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.6.0+44 +version: 3.7.0+45 module: androidX: true diff --git a/apps/student/build.gradle b/apps/student/build.gradle index fb269772e7..b32084749c 100644 --- a/apps/student/build.gradle +++ b/apps/student/build.gradle @@ -50,8 +50,8 @@ android { applicationId "com.instructure.candroid" minSdkVersion Versions.MIN_SDK targetSdkVersion Versions.TARGET_SDK - versionCode = 250 - versionName = '6.23.1' + versionCode = 251 + versionName = '6.24.0' vectorDrawables.useSupportLibrary = true multiDexEnabled = true diff --git a/apps/student/src/androidTest/java/com/instructure/student/ui/e2e/AssignmentsE2ETest.kt b/apps/student/src/androidTest/java/com/instructure/student/ui/e2e/AssignmentsE2ETest.kt index ef04d7437c..2d98a6a603 100644 --- a/apps/student/src/androidTest/java/com/instructure/student/ui/e2e/AssignmentsE2ETest.kt +++ b/apps/student/src/androidTest/java/com/instructure/student/ui/e2e/AssignmentsE2ETest.kt @@ -24,7 +24,14 @@ import com.instructure.canvas.espresso.E2E import com.instructure.dataseeding.api.AssignmentGroupsApi import com.instructure.dataseeding.api.AssignmentsApi import com.instructure.dataseeding.api.SubmissionsApi -import com.instructure.dataseeding.model.* +import com.instructure.dataseeding.model.AssignmentApiModel +import com.instructure.dataseeding.model.AttachmentApiModel +import com.instructure.dataseeding.model.CanvasUserApiModel +import com.instructure.dataseeding.model.CourseApiModel +import com.instructure.dataseeding.model.FileUploadType +import com.instructure.dataseeding.model.GradingType +import com.instructure.dataseeding.model.SubmissionApiModel +import com.instructure.dataseeding.model.SubmissionType import com.instructure.dataseeding.util.ago import com.instructure.dataseeding.util.days import com.instructure.dataseeding.util.fromNow @@ -448,6 +455,52 @@ class AssignmentsE2ETest: StudentTest() { submissionDetailsPage.assertAudioCommentDisplayed() } + @E2E + @Test + @TestMetaData(Priority.IMPORTANT, FeatureCategory.COMMENTS, TestCategory.E2E) + fun testAddFileCommentE2E() { + + Log.d(PREPARATION_TAG,"Seeding data.") + val data = seedData(students = 1, teachers = 1, courses = 1) + val student = data.studentsList[0] + val teacher = data.teachersList[0] + val course = data.coursesList[0] + + Log.d(PREPARATION_TAG,"Seeding assignment for ${course.name} course.") + val assignment = createAssignment(course.id, teacher, GradingType.POINTS, 15.0, 1.days.fromNow.iso8601) + + Log.d(PREPARATION_TAG,"Submit ${assignment.name} assignment for ${student.name} student.") + submitAssignment(assignment, course, student) + + Log.d(STEP_TAG, "Login with user: ${student.name}, login id: ${student.loginId}.") + tokenLogin(student) + dashboardPage.waitForRender() + + Log.d(STEP_TAG,"Seed a comment attachment upload.") + val commentUploadInfo = uploadTextFile( + assignmentId = assignment.id, + courseId = course.id, + token = student.token, + fileUploadType = FileUploadType.COMMENT_ATTACHMENT + ) + commentOnSubmission(student, course, assignment, commentUploadInfo) + + Log.d(STEP_TAG,"Select ${course.name} course and navigate to it's Assignments Page.") + dashboardPage.selectCourse(course) + courseBrowserPage.selectAssignments() + + Log.d(STEP_TAG,"Click on ${assignment.name} assignment.") + assignmentListPage.clickAssignment(assignment) + + Log.d(STEP_TAG,"Assert that ${commentUploadInfo.fileName} file is displayed as a comment by ${student.name} student.") + assignmentDetailsPage.goToSubmissionDetails() + submissionDetailsPage.openComments() + submissionDetailsPage.assertCommentAttachmentDisplayed(commentUploadInfo.fileName, student) + + Log.d(STEP_TAG,"Navigate to Submission Details Page and open Files Tab.") + submissionDetailsPage.openFiles() + } + @E2E @Test @TestMetaData(Priority.IMPORTANT, FeatureCategory.ASSIGNMENTS, TestCategory.E2E) @@ -680,4 +733,18 @@ class AssignmentsE2ETest: StudentTest() { postedGrade = postedGrade, excused = false ) + + private fun commentOnSubmission( + student: CanvasUserApiModel, + course: CourseApiModel, + assignment: AssignmentApiModel, + commentUploadInfo: AttachmentApiModel + ) { + SubmissionsApi.commentOnSubmission( + studentToken = student.token, + courseId = course.id, + assignmentId = assignment.id, + fileIds = mutableListOf(commentUploadInfo.id) + ) + } } \ No newline at end of file diff --git a/apps/student/src/androidTest/java/com/instructure/student/ui/e2e/DashboardE2ETest.kt b/apps/student/src/androidTest/java/com/instructure/student/ui/e2e/DashboardE2ETest.kt index 5565ee3fe3..c34c0326d6 100644 --- a/apps/student/src/androidTest/java/com/instructure/student/ui/e2e/DashboardE2ETest.kt +++ b/apps/student/src/androidTest/java/com/instructure/student/ui/e2e/DashboardE2ETest.kt @@ -57,10 +57,13 @@ class DashboardE2ETest : StudentTest() { Log.d(PREPARATION_TAG,"Seed some group info.") val groupCategory = GroupsApi.createCourseGroupCategory(data.coursesList[0].id, teacher.token) + val groupCategory2 = GroupsApi.createCourseGroupCategory(data.coursesList[0].id, teacher.token) val group = GroupsApi.createGroup(groupCategory.id, teacher.token) + val group2 = GroupsApi.createGroup(groupCategory2.id, teacher.token) Log.d(PREPARATION_TAG,"Create group membership for ${student.name} student.") GroupsApi.createGroupMembership(group.id, student.id, teacher.token) + GroupsApi.createGroupMembership(group2.id, student.id, teacher.token) Log.d(STEP_TAG,"Login with user: ${student.name}, login id: ${student.loginId}.") tokenLogin(student) @@ -93,6 +96,10 @@ class DashboardE2ETest : StudentTest() { dashboardPage.assertDisplaysCourse(course) } + Log.d(STEP_TAG, "Assert that both of the groups '${group.name}' and '${group2.name}' are displayed because they are independent from the courses.") + dashboardPage.assertDisplaysGroup(group, course1) + dashboardPage.assertDisplaysGroup(group2, course1) + Log.d(STEP_TAG,"Click on 'Edit Dashboard' button. Assert that the Edit Dashboard Page is loaded.") dashboardPage.clickEditDashboard() editDashboardPage.assertPageObjects() @@ -106,6 +113,10 @@ class DashboardE2ETest : StudentTest() { dashboardPage.assertDisplaysCourse(course1) dashboardPage.assertCourseNotDisplayed(course2) + Log.d(STEP_TAG, "Assert that both of the groups '${group.name}' and '${group2.name}' are displayed because they are independent from the courses.") + dashboardPage.assertDisplaysGroup(group, course1) + dashboardPage.assertDisplaysGroup(group2, course1) + Log.d(STEP_TAG,"Opens ${course1.name} course and assert if Course Details Page has been opened. Navigate back to Dashboard Page.") dashboardPage.selectCourse(course1) courseBrowserPage.assertPageObjects() @@ -144,6 +155,9 @@ class DashboardE2ETest : StudentTest() { dashboardPage.assertPageObjects() dashboardPage.assertDisplaysCourse(newNickname) + Log.d(STEP_TAG,"Assert that ${group.name} groups is displayed and the '$newNickname' is displayed as the corresponding course name of the group.") + dashboardPage.assertDisplaysGroup(group, newNickname) + Log.d(STEP_TAG, "Click on 'Edit nickname' menu of '$newNickname' course.") dashboardPage.clickCourseOverflowMenu(newNickname, "Edit nickname") @@ -154,6 +168,9 @@ class DashboardE2ETest : StudentTest() { dashboardPage.assertPageObjects() dashboardPage.assertDisplaysCourse(course1.name) + Log.d(STEP_TAG,"Assert that ${group.name} groups is displayed and the '${data.coursesList[0]}' is displayed as the corresponding course name of the group.") + dashboardPage.assertDisplaysGroup(group, data.coursesList[0]) + Log.d(STEP_TAG, "Toggle OFF 'Show Grades' and navigate back to Dashboard Page.") leftSideNavigationDrawerPage.setShowGrades(false) @@ -168,6 +185,44 @@ class DashboardE2ETest : StudentTest() { dashboardPage.assertCourseGrade(course1.name, "N/A") dashboardPage.assertCourseGrade(course2.name, "N/A") + Log.d(STEP_TAG,"Click on 'Edit Dashboard' button.") + dashboardPage.clickEditDashboard() + editDashboardPage.assertPageObjects() + + Log.d(STEP_TAG, "Assert that the group 'mass' select button's label is 'Select All'.") + editDashboardPage.swipeUp() + editDashboardPage.assertGroupMassSelectButtonIsDisplayed(false) + + Log.d(STEP_TAG, "Favorite '${group.name}' course and navigate back to Dashboard Page.") + editDashboardPage.favoriteGroup(group.name) + Espresso.pressBack() + + Log.d(STEP_TAG,"Assert that only the favoured group, '${group.name}' is displayed." + + "Assert that the other group, '${group2.name}' is not displayed since it's not favoured.") + dashboardPage.assertDisplaysGroup(group, course1) + dashboardPage.assertGroupNotDisplayed(group2) + + Log.d(STEP_TAG,"Click on 'Edit Dashboard' button.") + dashboardPage.clickEditDashboard() + editDashboardPage.assertPageObjects() + Thread.sleep(2000) //It can be flaky without this 2 seconds + editDashboardPage.swipeUp() + + Log.d(STEP_TAG, "Assert that the group 'mass' select button's label is 'Unselect All'.") + editDashboardPage.assertGroupMassSelectButtonIsDisplayed(true) + + Log.d(STEP_TAG, "Toggle off favourite star icon of '${group.name}' group." + + "Assert that the 'mass' select button's label is 'Select All'.") + editDashboardPage.unfavoriteGroup(group.name) + editDashboardPage.assertGroupMassSelectButtonIsDisplayed(false) + + Log.d(STEP_TAG, "Navigate back to Dashboard Page.") + Espresso.pressBack() + + Log.d(STEP_TAG,"Assert that both of the groups, '${group.name}' and '${group2.name}' are displayed" + + "since if there is no group selected on the Edit Dashboard page, we are showing all of them (this is the same logics as with the courses).") + dashboardPage.assertDisplaysGroup(group, course1) + dashboardPage.assertDisplaysGroup(group2, course1) } @E2E diff --git a/apps/student/src/androidTest/java/com/instructure/student/ui/e2e/InboxE2ETest.kt b/apps/student/src/androidTest/java/com/instructure/student/ui/e2e/InboxE2ETest.kt index f4bb8e96c4..6e8c9a8398 100644 --- a/apps/student/src/androidTest/java/com/instructure/student/ui/e2e/InboxE2ETest.kt +++ b/apps/student/src/androidTest/java/com/instructure/student/ui/e2e/InboxE2ETest.kt @@ -172,6 +172,7 @@ class InboxE2ETest: StudentTest() { inboxPage.selectConversation(seededConversation) inboxPage.assertSelectedConversationNumber("1") inboxPage.clickUnArchive() + inboxPage.assertInboxEmpty() inboxPage.assertConversationNotDisplayed(seededConversation.subject) sleep(2000) diff --git a/apps/student/src/androidTest/java/com/instructure/student/ui/e2e/LoginE2ETest.kt b/apps/student/src/androidTest/java/com/instructure/student/ui/e2e/LoginE2ETest.kt index f891bdb88b..03c3a51ddc 100644 --- a/apps/student/src/androidTest/java/com/instructure/student/ui/e2e/LoginE2ETest.kt +++ b/apps/student/src/androidTest/java/com/instructure/student/ui/e2e/LoginE2ETest.kt @@ -205,6 +205,47 @@ class LoginE2ETest : StudentTest() { leftSideNavigationDrawerPage.logout() } + @E2E + @Test + @TestMetaData(Priority.IMPORTANT, FeatureCategory.LOGIN, TestCategory.E2E) + fun testInvalidAndEmptyLoginCredentialsE2E() { + + val INVALID_USERNAME = "invalidusercred@test.com" + val INVALID_PASSWORD = "invalidpw" + val INVALID_CREDENTIALS_ERROR_MESSAGE = "Invalid username or password. Trouble logging in?" + val NO_PASSWORD_GIVEN_ERROR_MESSAGE = "No password was given" + val DOMAIN = "mobileqa.beta" + + Log.d(STEP_TAG, "Click 'Find My School' button.") + loginLandingPage.clickFindMySchoolButton() + + Log.d(STEP_TAG,"Enter domain: $DOMAIN.instructure.com.") + loginFindSchoolPage.enterDomain(DOMAIN) + + Log.d(STEP_TAG,"Click on 'Next' button on the Toolbar.") + loginFindSchoolPage.clickToolbarNextMenuItem() + + Log.d(STEP_TAG, "Try to login with invalid, non-existing credentials ($INVALID_USERNAME, $INVALID_PASSWORD)." + + "Assert that the invalid credentials error message is displayed.") + loginSignInPage.loginAs(INVALID_USERNAME, INVALID_PASSWORD) + loginSignInPage.assertLoginErrorMessage(INVALID_CREDENTIALS_ERROR_MESSAGE) + + Log.d(STEP_TAG, "Try to login with no credentials typed in either of the username and password field." + + "Assert that the no password was given error message is displayed.") + loginSignInPage.loginAs(EMPTY_STRING, EMPTY_STRING) + loginSignInPage.assertLoginErrorMessage(NO_PASSWORD_GIVEN_ERROR_MESSAGE) + + Log.d(STEP_TAG, "Try to login with leaving only the password field empty." + + "Assert that the no password was given error message is displayed.") + loginSignInPage.loginAs(INVALID_USERNAME, EMPTY_STRING) + loginSignInPage.assertLoginErrorMessage(NO_PASSWORD_GIVEN_ERROR_MESSAGE) + + Log.d(STEP_TAG, "Try to login with leaving only the username field empty." + + "Assert that the invalid credentials error message is displayed.") + loginSignInPage.loginAs(EMPTY_STRING, INVALID_PASSWORD) + loginSignInPage.assertLoginErrorMessage(INVALID_CREDENTIALS_ERROR_MESSAGE) + } + // Verify that students can sign into vanity domain @E2E @Test diff --git a/apps/student/src/androidTest/java/com/instructure/student/ui/e2e/NotificationsE2ETest.kt b/apps/student/src/androidTest/java/com/instructure/student/ui/e2e/NotificationsE2ETest.kt index 2d4077f8ae..11e47729ec 100644 --- a/apps/student/src/androidTest/java/com/instructure/student/ui/e2e/NotificationsE2ETest.kt +++ b/apps/student/src/androidTest/java/com/instructure/student/ui/e2e/NotificationsE2ETest.kt @@ -18,11 +18,18 @@ package com.instructure.student.ui.e2e import android.util.Log import com.instructure.canvas.espresso.E2E +import com.instructure.canvas.espresso.ReleaseExclude import com.instructure.canvas.espresso.refresh import com.instructure.dataseeding.api.AssignmentsApi import com.instructure.dataseeding.api.QuizzesApi import com.instructure.dataseeding.api.SubmissionsApi -import com.instructure.dataseeding.model.* +import com.instructure.dataseeding.model.AssignmentApiModel +import com.instructure.dataseeding.model.CanvasUserApiModel +import com.instructure.dataseeding.model.CourseApiModel +import com.instructure.dataseeding.model.GradingType +import com.instructure.dataseeding.model.QuizAnswer +import com.instructure.dataseeding.model.QuizQuestion +import com.instructure.dataseeding.model.SubmissionType import com.instructure.dataseeding.util.days import com.instructure.dataseeding.util.fromNow import com.instructure.dataseeding.util.iso8601 @@ -43,6 +50,7 @@ class NotificationsE2ETest : StudentTest() { override fun enableAndConfigureAccessibilityChecks() = Unit + @ReleaseExclude("The notifications API sometimes is slow and the test is breaking because the notifications aren't show up in time.") @E2E @Test @TestMetaData(Priority.MANDATORY, FeatureCategory.ASSIGNMENTS, TestCategory.E2E) @@ -104,7 +112,7 @@ class NotificationsE2ETest : StudentTest() { gradeSubmission(teacher, course, testAssignment, student) Log.d(STEP_TAG,"Refresh the Notifications Page. Assert that there is a notification about the submission grading appearing.") - sleep(5000) //Let the submission api do it's job + sleep(10000) //Let the submission api do it's job refresh() notificationPage.assertHasGrade(testAssignment.name,"13") } diff --git a/apps/student/src/androidTest/java/com/instructure/student/ui/e2e/SettingsE2ETest.kt b/apps/student/src/androidTest/java/com/instructure/student/ui/e2e/SettingsE2ETest.kt index 38de5b1ab3..cb42b97277 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 @@ -205,6 +205,9 @@ class SettingsE2ETest : StudentTest() { Log.d(STEP_TAG,"Check that e-mail is equal to: ${student.loginId} (student's Login ID).") aboutPage.emailIs(student.loginId) + + Log.d(STEP_TAG,"Assert that the Instructure company logo has been displayed on the About page.") + aboutPage.assertInstructureLogoDisplayed() } //The remote config settings page only available on DEBUG/DEV builds. So this test is testing a non user facing feature. diff --git a/apps/student/src/androidTest/java/com/instructure/student/ui/interaction/AssignmentDetailsInteractionTest.kt b/apps/student/src/androidTest/java/com/instructure/student/ui/interaction/AssignmentDetailsInteractionTest.kt index 9058403da7..c0763e0e9f 100644 --- a/apps/student/src/androidTest/java/com/instructure/student/ui/interaction/AssignmentDetailsInteractionTest.kt +++ b/apps/student/src/androidTest/java/com/instructure/student/ui/interaction/AssignmentDetailsInteractionTest.kt @@ -15,11 +15,18 @@ */ package com.instructure.student.ui.interaction -import com.instructure.canvas.espresso.Stub -import com.instructure.canvas.espresso.mockCanvas.* +import com.instructure.canvas.espresso.mockCanvas.MockCanvas +import com.instructure.canvas.espresso.mockCanvas.addAssignment +import com.instructure.canvas.espresso.mockCanvas.addAssignmentsToGroups +import com.instructure.canvas.espresso.mockCanvas.addSubmissionForAssignment +import com.instructure.canvas.espresso.mockCanvas.init import com.instructure.canvasapi2.models.Assignment import com.instructure.canvasapi2.utils.toApiString -import com.instructure.panda_annotations.* +import com.instructure.panda_annotations.FeatureCategory +import com.instructure.panda_annotations.Priority +import com.instructure.panda_annotations.SecondaryFeatureCategory +import com.instructure.panda_annotations.TestCategory +import com.instructure.panda_annotations.TestMetaData import com.instructure.student.ui.utils.StudentTest import com.instructure.student.ui.utils.routeTo import com.instructure.student.ui.utils.tokenLogin @@ -177,38 +184,6 @@ class AssignmentDetailsInteractionTest : StudentTest() { submissionDetailsPage.assertPageObjects() } - @Stub - @Test - @TestMetaData(Priority.MANDATORY, FeatureCategory.ASSIGNMENTS, TestCategory.INTERACTION, true, SecondaryFeatureCategory.ASSIGNMENT_QUIZZES) - fun testQuizzesNext_launchQuizzesNextAssignment() { - // Launch into Quizzes.Next assignment - /* First attempt based on hardcoded verifier response - val data = MockCanvas.init( - studentCount = 1, - courseCount = 1 - ) - - val course = data.courses.values.first() - val student = data.students[0] - val token = data.tokenFor(student)!! - val assignment = data.addAssignment(courseId = course.id, groupType = AssignmentGroupType.UPCOMING, submissionType = Assignment.SubmissionType.EXTERNAL_TOOL, isQuizzesNext = true) - val submission = Submission( - id = 123L, - submittedAt = Date(), - attempt = 1L, - late = false - ) - data.addSubmission(course.id, submission, assignment.id) - data.addLTITool("Quizzes 2", "https://mobiledev.instructure.com/courses/1567973/external_tools/sessionless_launch?verifier=f85d3d69189890cde2f427a8efdc0e64850d8583bf8f2e0e0fa3704782d48b5378df5d52a35a4497ec18d3b0e201b3b2cab95e1347e7c5e286ac6636bf295c6b") - tokenLogin(data.domain, token, student) - routeTo("courses/${course.id}/assignments", data.domain) - - assignmentListPage.clickAssignment(assignment) - assignmentDetailsPage.clickSubmit() - //https://mobiledev.instructure.com/api/v1/courses/1567973/external_tools/sessionless_launch?assignment_id=24378681&launch_type=assessment - */ - } - private fun goToAssignmentFromList(): MockCanvas { // Test clicking on the Submission and Rubric button to load the Submission Details Page val data = MockCanvas.init( diff --git a/apps/student/src/androidTest/java/com/instructure/student/ui/interaction/TodoInteractionTest.kt b/apps/student/src/androidTest/java/com/instructure/student/ui/interaction/TodoInteractionTest.kt index ddc7753a77..b7129b3e7e 100644 --- a/apps/student/src/androidTest/java/com/instructure/student/ui/interaction/TodoInteractionTest.kt +++ b/apps/student/src/androidTest/java/com/instructure/student/ui/interaction/TodoInteractionTest.kt @@ -17,6 +17,7 @@ package com.instructure.student.ui.interaction import androidx.test.espresso.Espresso import com.instructure.canvas.espresso.StubLandscape +import com.instructure.canvas.espresso.StubMultiAPILevel import com.instructure.canvas.espresso.mockCanvas.MockCanvas import com.instructure.canvas.espresso.mockCanvas.addAssignment import com.instructure.canvas.espresso.mockCanvas.addQuizToCourse @@ -65,9 +66,9 @@ class TodoInteractionTest : StudentTest() { @Test @StubLandscape("Stubbed because on lowres device in landscape mode, the space is too narrow to scroll properly. Will be refactored and running when we changed to non-lowres device on nightly runs.") + @StubMultiAPILevel("Somehow the 'OK' button within chooseFavoriteCourseFilter row is not clickable and not shown on the layout inspector as well.") @TestMetaData(Priority.IMPORTANT, FeatureCategory.TODOS, TestCategory.INTERACTION, false) fun testFilters() { - //TODO: Check and refactor (if necessary) after migrated nightly runs from lowres device to non-lowres one. val data = goToTodos(courseCount = 2, favoriteCourseCount = 1) val favoriteCourse = data.courses.values.first {course -> course.isFavorite} val notFavoriteCourse = data.courses.values.first {course -> !course.isFavorite} diff --git a/apps/student/src/androidTest/java/com/instructure/student/ui/pages/AboutPage.kt b/apps/student/src/androidTest/java/com/instructure/student/ui/pages/AboutPage.kt index af6d2f458b..7218029b17 100644 --- a/apps/student/src/androidTest/java/com/instructure/student/ui/pages/AboutPage.kt +++ b/apps/student/src/androidTest/java/com/instructure/student/ui/pages/AboutPage.kt @@ -18,17 +18,13 @@ package com.instructure.student.ui.pages import androidx.test.espresso.Espresso.onView import androidx.test.espresso.matcher.ViewMatchers.withId -import androidx.test.espresso.matcher.ViewMatchers.withParent -import com.instructure.canvas.espresso.containsTextCaseInsensitive -import com.instructure.espresso.OnViewWithId import com.instructure.espresso.OnViewWithText import com.instructure.espresso.assertDisplayed -import com.instructure.espresso.click import com.instructure.espresso.page.BasePage import com.instructure.espresso.page.plus import com.instructure.espresso.page.withText +import com.instructure.espresso.scrollTo import com.instructure.student.R -import org.hamcrest.Matchers.allOf class AboutPage : BasePage(R.id.aboutPage) { @@ -47,4 +43,8 @@ class AboutPage : BasePage(R.id.aboutPage) { fun emailIs(email: String) { onView(withId(R.id.email) + withText(email)).assertDisplayed() } + + fun assertInstructureLogoDisplayed() { + onView(withId(R.id.instructureLogo)).scrollTo().assertDisplayed() + } } \ 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 8bc8556ca8..64e74a54d8 100644 --- a/apps/student/src/androidTest/java/com/instructure/student/ui/pages/DashboardPage.kt +++ b/apps/student/src/androidTest/java/com/instructure/student/ui/pages/DashboardPage.kt @@ -93,6 +93,10 @@ class DashboardPage : BasePage(R.id.dashboardPage) { assertDisplaysGroupCommon(group.name, course.name) } + fun assertDisplaysGroup(group: GroupApiModel, courseName: String) { + assertDisplaysGroupCommon(group.name, courseName) + } + fun assertDisplaysGroup(group: Group, course: Course) { assertDisplaysGroupCommon(group.name!!, course.name) } @@ -100,7 +104,7 @@ class DashboardPage : BasePage(R.id.dashboardPage) { private fun assertDisplaysGroupCommon(groupName: String, courseName: String) { val groupNameMatcher = allOf(withText(groupName), withId(R.id.groupNameView)) onView(groupNameMatcher).scrollTo().assertDisplayed() - val groupDescriptionMatcher = allOf(withText(courseName), withId(R.id.groupCourseView)) + val groupDescriptionMatcher = allOf(withText(courseName), withId(R.id.groupCourseView), hasSibling(groupNameMatcher)) onView(groupDescriptionMatcher).scrollTo().assertDisplayed() } @@ -179,7 +183,7 @@ class DashboardPage : BasePage(R.id.dashboardPage) { fun selectCourse(course: CourseApiModel) { assertDisplaysCourse(course) - onView(withText(course.name)).click() + onView(withText(course.name) + withId(R.id.titleTextView)).click() } fun assertAnnouncementShowing(announcement: AccountNotification) { @@ -245,7 +249,7 @@ class DashboardPage : BasePage(R.id.dashboardPage) { } fun clickEditDashboard() { - onView(withId(R.id.editDashboardTextView)).click() + onView(withId(R.id.editDashboardTextView)).scrollTo().click() } fun assertCourseNotDisplayed(course: CourseApiModel) { @@ -257,6 +261,15 @@ class DashboardPage : BasePage(R.id.dashboardPage) { onView(matcher).check(doesNotExist()) } + fun assertGroupNotDisplayed(group: GroupApiModel) { + val matcher = allOf( + withText(group.name), + withId(R.id.titleTextView), + withAncestor(R.id.swipeRefreshLayout) + ) + onView(matcher).check(doesNotExist()) + } + fun changeCourseNickname(changeTo: String) { onView(withId(R.id.newCourseNickname)).replaceText(changeTo) onView(withText(R.string.ok) + withAncestor(R.id.buttonPanel)).click() @@ -264,7 +277,7 @@ class DashboardPage : BasePage(R.id.dashboardPage) { fun clickCourseOverflowMenu(courseTitle: String, menuTitle: String) { val courseOverflowMatcher = withId(R.id.overflow) + withAncestor(withId(R.id.cardView) + withDescendant(withId(R.id.titleTextView) + withText(courseTitle))) - onView(courseOverflowMatcher).click() + onView(courseOverflowMatcher).scrollTo().click() waitForView(withId(R.id.title) + withText(menuTitle)).click() } @@ -272,7 +285,7 @@ class DashboardPage : BasePage(R.id.dashboardPage) { val siblingMatcher = allOf(withId(R.id.textContainer), withDescendant(withId(R.id.titleTextView) + withText(courseName))) val matcher = allOf(withId(R.id.gradeLayout), withDescendant(withId(R.id.gradeTextView) + withText(courseGrade)), hasSibling(siblingMatcher)) - onView(matcher).assertDisplayed() + onView(matcher).scrollTo().assertDisplayed() } fun assertCourseGradeNotDisplayed(courseName: String, courseGrade: String) { diff --git a/apps/student/src/androidTest/java/com/instructure/student/ui/pages/EditDashboardPage.kt b/apps/student/src/androidTest/java/com/instructure/student/ui/pages/EditDashboardPage.kt index 30adc114ed..6891b89897 100644 --- a/apps/student/src/androidTest/java/com/instructure/student/ui/pages/EditDashboardPage.kt +++ b/apps/student/src/androidTest/java/com/instructure/student/ui/pages/EditDashboardPage.kt @@ -23,7 +23,13 @@ import androidx.test.espresso.matcher.ViewMatchers.withText import com.instructure.canvasapi2.models.Course import com.instructure.espresso.assertDisplayed import com.instructure.espresso.click -import com.instructure.espresso.page.* +import com.instructure.espresso.page.BasePage +import com.instructure.espresso.page.onView +import com.instructure.espresso.page.plus +import com.instructure.espresso.page.withId +import com.instructure.espresso.page.withText +import com.instructure.espresso.scrollTo +import com.instructure.espresso.swipeUp import com.instructure.student.R import org.hamcrest.Matchers.allOf import org.hamcrest.Matchers.containsString @@ -51,8 +57,7 @@ class EditDashboardPage : BasePage(R.id.editDashboardPage) { fun unfavoriteCourse(courseName: String) { val childMatcher = withContentDescription("Remove from dashboard") val itemMatcher = allOf( - withContentDescription(containsString(", favorite")), - withContentDescription(containsString(courseName)), + withContentDescription(containsString("Course $courseName, favorite")), hasDescendant(childMatcher)) onView(withParent(itemMatcher) + childMatcher).click() @@ -65,18 +70,34 @@ class EditDashboardPage : BasePage(R.id.editDashboardPage) { fun favoriteCourse(courseName: String) { val childMatcher = withContentDescription("Add to dashboard") val itemMatcher = allOf( - withContentDescription(containsString(", not favorite")), - withContentDescription(containsString(courseName)), + withContentDescription(containsString("Course $courseName, not favorite")), hasDescendant(childMatcher)) onView(withParent(itemMatcher) + childMatcher).click() } + fun favoriteGroup(groupName: String) { + val childMatcher = withContentDescription("Add to dashboard") + val itemMatcher = allOf( + withContentDescription(containsString("Group $groupName, not favorite")), + hasDescendant(childMatcher)) + + onView(withParent(itemMatcher) + childMatcher).scrollTo().click() + } + + fun unfavoriteGroup(groupName: String) { + val childMatcher = withContentDescription("Remove from dashboard") + val itemMatcher = allOf( + withContentDescription(containsString("Group $groupName, favorite")), + hasDescendant(childMatcher)) + + onView(withParent(itemMatcher) + childMatcher).scrollTo().click() + } + fun assertCourseFavorited(course: Course) { val childMatcher = withContentDescription("Remove from dashboard") val itemMatcher = allOf( - withContentDescription(containsString(", favorite")), - withContentDescription(containsString(course.name)), + withContentDescription(containsString("Course ${course.name}, favorite")), hasDescendant(childMatcher)) onView(itemMatcher).assertDisplayed() } @@ -111,4 +132,21 @@ class EditDashboardPage : BasePage(R.id.editDashboardPage) { } } + fun assertGroupMassSelectButtonIsDisplayed(someSelected: Boolean) { + if (someSelected) { + val itemMatcher = withContentDescription("Remove all from dashboard") + val parentMatcher = allOf(hasDescendant(withText("All groups")), hasDescendant(itemMatcher)) + onView(withParent(parentMatcher) + itemMatcher).scrollTo().assertDisplayed() + } + else { + val itemMatcher = withContentDescription("Add all to dashboard") + val parentMatcher = allOf(hasDescendant(withText("All groups")), hasDescendant(itemMatcher)) + onView(withParent(parentMatcher) + itemMatcher).scrollTo().assertDisplayed() + } + } + + fun swipeUp() { + onView(withId(R.id.swipeRefreshLayout) + withParent(withId(R.id.editDashboardPage))).swipeUp() + } + } \ No newline at end of file diff --git a/apps/student/src/androidTest/java/com/instructure/student/ui/pages/InboxPage.kt b/apps/student/src/androidTest/java/com/instructure/student/ui/pages/InboxPage.kt index ef23271446..223ceaa4de 100644 --- a/apps/student/src/androidTest/java/com/instructure/student/ui/pages/InboxPage.kt +++ b/apps/student/src/androidTest/java/com/instructure/student/ui/pages/InboxPage.kt @@ -32,8 +32,25 @@ import com.instructure.canvas.espresso.waitForMatcherWithRefreshes import com.instructure.canvasapi2.models.Conversation import com.instructure.canvasapi2.models.Course import com.instructure.dataseeding.model.ConversationApiModel -import com.instructure.espresso.* -import com.instructure.espresso.page.* +import com.instructure.espresso.OnViewWithId +import com.instructure.espresso.RecyclerViewItemCountGreaterThanAssertion +import com.instructure.espresso.WaitForViewWithId +import com.instructure.espresso.assertDisplayed +import com.instructure.espresso.assertVisibility +import com.instructure.espresso.click +import com.instructure.espresso.longClick +import com.instructure.espresso.page.BasePage +import com.instructure.espresso.page.onView +import com.instructure.espresso.page.plus +import com.instructure.espresso.page.waitForView +import com.instructure.espresso.page.waitForViewWithId +import com.instructure.espresso.page.waitForViewWithText +import com.instructure.espresso.page.withAncestor +import com.instructure.espresso.page.withId +import com.instructure.espresso.page.withText +import com.instructure.espresso.scrollTo +import com.instructure.espresso.swipeLeft +import com.instructure.espresso.swipeRight import com.instructure.student.R import org.hamcrest.Matchers.allOf import org.hamcrest.Matchers.not @@ -167,7 +184,7 @@ class InboxPage : BasePage(R.id.inboxPage) { } fun assertInboxEmpty() { - onView(withId(R.id.emptyInboxView)).assertDisplayed() + waitForView(withId(R.id.emptyInboxView)).assertDisplayed() } fun assertHasConversation() { diff --git a/apps/student/src/androidTest/java/com/instructure/student/ui/pages/LoginSignInPage.kt b/apps/student/src/androidTest/java/com/instructure/student/ui/pages/LoginSignInPage.kt index 7b1614edb1..4173184b49 100644 --- a/apps/student/src/androidTest/java/com/instructure/student/ui/pages/LoginSignInPage.kt +++ b/apps/student/src/androidTest/java/com/instructure/student/ui/pages/LoginSignInPage.kt @@ -16,9 +16,11 @@ */ package com.instructure.student.ui.pages +import androidx.test.espresso.web.assertion.WebViewAssertions.webMatches import androidx.test.espresso.web.sugar.Web import androidx.test.espresso.web.sugar.Web.onWebView import androidx.test.espresso.web.webdriver.DriverAtoms.findElement +import androidx.test.espresso.web.webdriver.DriverAtoms.getText import androidx.test.espresso.web.webdriver.DriverAtoms.webClick import androidx.test.espresso.web.webdriver.DriverAtoms.webKeys import androidx.test.espresso.web.webdriver.Locator @@ -27,6 +29,7 @@ import com.instructure.espresso.OnViewWithId import com.instructure.espresso.assertDisplayed import com.instructure.espresso.page.BasePage import com.instructure.student.R +import org.hamcrest.CoreMatchers.containsString @Suppress("unused") class LoginSignInPage: BasePage() { @@ -36,6 +39,7 @@ class LoginSignInPage: BasePage() { private val LOGIN_BUTTON_CSS = "button[type=\"submit\"]" private val FORGOT_PASSWORD_BUTTON_CSS = "a[class=\"forgot-password flip-to-back\"]" private val AUTHORIZE_BUTTON_CSS = "button[type=\"submit\"]" + private val LOGIN_ERROR_MESSAGE_HOLDER_CSS = "div[class='error']" private val signInRoot by OnViewWithId(R.id.signInRoot, autoAssert = false) private val toolbar by OnViewWithId(R.id.toolbar, autoAssert = false) @@ -62,6 +66,10 @@ class LoginSignInPage: BasePage() { return onWebView().withElement(findElement(Locator.CSS_SELECTOR, AUTHORIZE_BUTTON_CSS)) } + private fun loginErrorMessageHolder(): Web.WebInteraction<*> { + return onWebView().withElement(findElement(Locator.CSS_SELECTOR, LOGIN_ERROR_MESSAGE_HOLDER_CSS)) + } + //endregion //region Assertion Helpers @@ -96,11 +104,15 @@ class LoginSignInPage: BasePage() { forgotPasswordButton().perform(webClick()) } + fun assertLoginErrorMessage(errorMessage: String) { + loginErrorMessageHolder().check(webMatches(getText(), containsString(errorMessage))) + } + fun loginAs(user: CanvasUserApiModel) { loginAs(user.loginId, user.password) } - private fun loginAs(loginId: String, password: String) { + fun loginAs(loginId: String, password: String) { enterEmail(loginId) enterPassword(password) clickLoginButton() 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 31d4060756..5de3687694 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 @@ -19,7 +19,11 @@ package com.instructure.student.ui.pages import com.instructure.espresso.OnViewWithId import com.instructure.espresso.TextViewColorAssertion import com.instructure.espresso.click -import com.instructure.espresso.page.* +import com.instructure.espresso.page.BasePage +import com.instructure.espresso.page.onView +import com.instructure.espresso.page.plus +import com.instructure.espresso.page.withParent +import com.instructure.espresso.page.withText import com.instructure.espresso.scrollTo import com.instructure.student.R @@ -39,7 +43,7 @@ class SettingsPage : BasePage(R.id.settingsFragment) { private val appThemeStatus by OnViewWithId(R.id.appThemeStatus) fun openAboutPage() { - aboutLabel.click() + aboutLabel.scrollTo().click() } fun openLegalPage() { diff --git a/apps/student/src/androidTest/java/com/instructure/student/ui/pages/TodoPage.kt b/apps/student/src/androidTest/java/com/instructure/student/ui/pages/TodoPage.kt index 0ba41f40ea..a189f8c8e9 100644 --- a/apps/student/src/androidTest/java/com/instructure/student/ui/pages/TodoPage.kt +++ b/apps/student/src/androidTest/java/com/instructure/student/ui/pages/TodoPage.kt @@ -16,9 +16,7 @@ */ package com.instructure.student.ui.pages -import android.widget.Button import androidx.test.espresso.assertion.ViewAssertions.doesNotExist -import androidx.test.espresso.matcher.ViewMatchers.isAssignableFrom import androidx.test.espresso.matcher.ViewMatchers.withText import com.instructure.canvasapi2.models.Assignment import com.instructure.canvasapi2.models.Quiz @@ -27,7 +25,13 @@ import com.instructure.dataseeding.model.QuizApiModel import com.instructure.espresso.OnViewWithId import com.instructure.espresso.assertDisplayed import com.instructure.espresso.click -import com.instructure.espresso.page.* +import com.instructure.espresso.page.BasePage +import com.instructure.espresso.page.onView +import com.instructure.espresso.page.plus +import com.instructure.espresso.page.withAncestor +import com.instructure.espresso.page.withId +import com.instructure.espresso.page.withParent +import com.instructure.espresso.page.withText import com.instructure.espresso.scrollTo import com.instructure.student.R import org.hamcrest.Matchers @@ -77,8 +81,8 @@ class TodoPage: BasePage(R.id.todoPage) { fun chooseFavoriteCourseFilter() { onView(withId(R.id.todoListFilter)).click() - onView(withText(R.string.favoritedCoursesLabel)).click() - onView(allOf(isAssignableFrom(Button::class.java), withText(R.string.ok))).click() + onView(withText(R.string.favoritedCoursesLabel) + withParent(R.id.select_dialog_listview)).click() + onView(withText(R.string.ok)).click() } fun clearFilter() { diff --git a/apps/student/src/main/java/com/instructure/student/activity/CallbackActivity.kt b/apps/student/src/main/java/com/instructure/student/activity/CallbackActivity.kt index 5593f62be9..cdf7fd15fd 100644 --- a/apps/student/src/main/java/com/instructure/student/activity/CallbackActivity.kt +++ b/apps/student/src/main/java/com/instructure/student/activity/CallbackActivity.kt @@ -21,11 +21,7 @@ import android.os.Bundle import com.google.firebase.crashlytics.FirebaseCrashlytics import com.heapanalytics.android.Heap import com.instructure.canvasapi2.StatusCallback -import com.instructure.canvasapi2.managers.FeaturesManager -import com.instructure.canvasapi2.managers.LaunchDefinitionsManager -import com.instructure.canvasapi2.managers.ThemeManager -import com.instructure.canvasapi2.managers.UnreadCountManager -import com.instructure.canvasapi2.managers.UserManager +import com.instructure.canvasapi2.managers.* import com.instructure.canvasapi2.models.* import com.instructure.canvasapi2.utils.* import com.instructure.canvasapi2.utils.pageview.PandataInfo diff --git a/apps/student/src/main/java/com/instructure/student/activity/StudentViewStarterActivity.kt b/apps/student/src/main/java/com/instructure/student/activity/StudentViewStarterActivity.kt index cca0c61949..135dd7252c 100644 --- a/apps/student/src/main/java/com/instructure/student/activity/StudentViewStarterActivity.kt +++ b/apps/student/src/main/java/com/instructure/student/activity/StudentViewStarterActivity.kt @@ -36,7 +36,7 @@ class StudentViewStarterActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - setContentView(R.layout.activity_student_view_starter) + setContentView(binding.root) binding.loadingView.setOverrideColor(ContextCompat.getColor(this, R.color.login_studentAppTheme)) val extras = intent.extras!! diff --git a/apps/student/src/main/java/com/instructure/student/activity/VideoViewActivity.kt b/apps/student/src/main/java/com/instructure/student/activity/VideoViewActivity.kt index e7afb43222..ec2c09f226 100644 --- a/apps/student/src/main/java/com/instructure/student/activity/VideoViewActivity.kt +++ b/apps/student/src/main/java/com/instructure/student/activity/VideoViewActivity.kt @@ -62,7 +62,7 @@ class VideoViewActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - setContentView(R.layout.activity_video_view) + setContentView(binding.root) binding.playerView.requestFocus() mediaDataSourceFactory = buildDataSourceFactory(true) mainHandler = Handler() diff --git a/apps/student/src/main/java/com/instructure/student/activity/WidgetSetupActivity.kt b/apps/student/src/main/java/com/instructure/student/activity/WidgetSetupActivity.kt index 5e533ccb0d..9ae8c38975 100644 --- a/apps/student/src/main/java/com/instructure/student/activity/WidgetSetupActivity.kt +++ b/apps/student/src/main/java/com/instructure/student/activity/WidgetSetupActivity.kt @@ -39,7 +39,7 @@ class WidgetSetupActivity : AppCompatActivity() { // Sets the result canceled so if the user decides not to setup the widget it does not get added setResult(Activity.RESULT_CANCELED) - setContentView(R.layout.activity_widget_setup) + setContentView(binding.root) binding.cardDark.setOnClickListener(cardClickListener) binding.cardLight.setOnClickListener(cardClickListener) diff --git a/apps/student/src/main/java/com/instructure/student/adapter/DashboardRecyclerAdapter.kt b/apps/student/src/main/java/com/instructure/student/adapter/DashboardRecyclerAdapter.kt index 8df86cbb5f..5c5ef240c8 100644 --- a/apps/student/src/main/java/com/instructure/student/adapter/DashboardRecyclerAdapter.kt +++ b/apps/student/src/main/java/com/instructure/student/adapter/DashboardRecyclerAdapter.kt @@ -20,7 +20,6 @@ package com.instructure.student.adapter import android.app.Activity import android.view.View import androidx.recyclerview.widget.RecyclerView -import com.instructure.canvasapi2.apis.EnrollmentAPI import com.instructure.canvasapi2.managers.* import com.instructure.canvasapi2.models.* import com.instructure.canvasapi2.utils.isValidTerm @@ -30,7 +29,6 @@ import com.instructure.pandautils.utils.ColorApiHelper import com.instructure.student.flutterChannels.FlutterComm import com.instructure.student.holders.* import com.instructure.student.interfaces.CourseAdapterToFragmentCallback -import com.instructure.student.util.StudentPrefs import org.threeten.bp.OffsetDateTime import java.util.* @@ -140,8 +138,8 @@ class DashboardRecyclerAdapter( // Filter groups val allActiveGroups = groups.filter { group -> group.isActive(mCourseMap[group.courseId])} - val isAnyFavoritePresent = visibleCourses.any { it.isFavorite } || allActiveGroups.any { it.isFavorite } - val visibleGroups = if (isAnyFavoritePresent) allActiveGroups.filter { it.isFavorite } else allActiveGroups + val isAnyGroupFavorited = allActiveGroups.any { it.isFavorite } + val visibleGroups = if (isAnyGroupFavorited) allActiveGroups.filter { it.isFavorite } else allActiveGroups // Add courses addOrUpdateAllItems(ItemType.COURSE_HEADER, visibleCourses) diff --git a/apps/student/src/main/java/com/instructure/student/binding/BindingAdapters.kt b/apps/student/src/main/java/com/instructure/student/binding/BindingAdapters.kt index 79edba18fd..4c065934d4 100644 --- a/apps/student/src/main/java/com/instructure/student/binding/BindingAdapters.kt +++ b/apps/student/src/main/java/com/instructure/student/binding/BindingAdapters.kt @@ -21,6 +21,8 @@ import androidx.databinding.BindingAdapter import com.google.android.material.tabs.TabLayout import com.instructure.student.features.elementary.course.ElementaryCourseTab import com.instructure.student.mobius.assignmentDetails.ui.gradeCell.DonutChartView +import com.instructure.student.mobius.assignmentDetails.ui.gradeCell.GradeCellViewState +import com.instructure.student.mobius.assignmentDetails.ui.gradeCell.GradeStatisticsView @BindingAdapter("tabs") fun bindCourseTabs(tabLayout: TabLayout, tabs: List?) { @@ -38,4 +40,12 @@ fun DonutChartView.setProgress(progress: Float, @ColorInt color: Int, @ColorInt setColor(color) setTrackColor(trackColor) setPercentage(progress, true) -} \ No newline at end of file +} + +@BindingAdapter("stats", "color") +fun GradeStatisticsView.setStatistics(stats: GradeCellViewState.GradeStats?, @ColorInt color: Int) { + stats?.let { + setStats(stats) + setAccentColor(color) + } +} diff --git a/apps/student/src/main/java/com/instructure/student/di/DatabaseModule.kt b/apps/student/src/main/java/com/instructure/student/di/DatabaseModule.kt index 4d90364744..2d08671dfb 100644 --- a/apps/student/src/main/java/com/instructure/student/di/DatabaseModule.kt +++ b/apps/student/src/main/java/com/instructure/student/di/DatabaseModule.kt @@ -2,8 +2,8 @@ package com.instructure.student.di import android.content.Context import androidx.room.Room -import com.instructure.pandautils.room.AppDatabase -import com.instructure.pandautils.room.appDatabaseMigrations +import com.instructure.pandautils.room.appdatabase.AppDatabase +import com.instructure.pandautils.room.appdatabase.appDatabaseMigrations import com.instructure.student.db.Db import com.instructure.student.db.StudentDb import com.instructure.student.db.getInstance diff --git a/apps/student/src/main/java/com/instructure/student/dialog/WhatIfDialogStyled.kt b/apps/student/src/main/java/com/instructure/student/dialog/WhatIfDialogStyled.kt index bbbb27eb40..2ebff9c9bc 100644 --- a/apps/student/src/main/java/com/instructure/student/dialog/WhatIfDialogStyled.kt +++ b/apps/student/src/main/java/com/instructure/student/dialog/WhatIfDialogStyled.kt @@ -37,10 +37,6 @@ import kotlin.properties.Delegates @ScreenView(SCREEN_VIEW_WHAT_IF) class WhatIfDialogStyled : DialogFragment() { - init { - retainInstance = true - } - private var callback: (Double?, Double) -> Unit by Delegates.notNull() private var assignment: Assignment by ParcelableArg() private var textButtonColor: Int by IntArg() @@ -51,11 +47,6 @@ class WhatIfDialogStyled : DialogFragment() { fun onClick(assignment: Assignment, position: Int) } - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - retainInstance = true - } - override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { val builder = AlertDialog.Builder(requireContext()) .setTitle(getString(R.string.whatIfDialogText)) diff --git a/apps/student/src/main/java/com/instructure/student/features/assignmentdetails/AssignmentDetailsViewModel.kt b/apps/student/src/main/java/com/instructure/student/features/assignmentdetails/AssignmentDetailsViewModel.kt index 7c2c0ab375..f6c9d61ccd 100644 --- a/apps/student/src/main/java/com/instructure/student/features/assignmentdetails/AssignmentDetailsViewModel.kt +++ b/apps/student/src/main/java/com/instructure/student/features/assignmentdetails/AssignmentDetailsViewModel.kt @@ -220,13 +220,17 @@ class AssignmentDetailsViewModel @Inject constructor( private fun refreshAssignment() { viewModelScope.launch { - val assignmentResult = if (isObserver) { - assignmentManager.getAssignmentIncludeObserveesAsync(assignmentId, course?.id.orDefault(), true) - } else { - assignmentManager.getAssignmentWithHistoryAsync(assignmentId, course?.id.orDefault(), true) - }.await().dataOrThrow as Assignment + try { + val assignmentResult = if (isObserver) { + assignmentManager.getAssignmentIncludeObserveesAsync(assignmentId, course?.id.orDefault(), true) + } else { + assignmentManager.getAssignmentWithHistoryAsync(assignmentId, course?.id.orDefault(), true) + }.await().dataOrThrow as Assignment - _data.postValue(getViewData(assignmentResult, dbSubmission?.isDraft.orDefault())) + _data.postValue(getViewData(assignmentResult, dbSubmission?.isDraft.orDefault())) + } catch (e: Exception) { + _events.value = Event(AssignmentDetailAction.ShowToast(resources.getString(R.string.assignmentRefreshError))) + } } } diff --git a/apps/student/src/main/java/com/instructure/student/features/assignmentdetails/gradecellview/GradeCellViewData.kt b/apps/student/src/main/java/com/instructure/student/features/assignmentdetails/gradecellview/GradeCellViewData.kt index 60aa9a2ba8..a9d8eefaf0 100644 --- a/apps/student/src/main/java/com/instructure/student/features/assignmentdetails/gradecellview/GradeCellViewData.kt +++ b/apps/student/src/main/java/com/instructure/student/features/assignmentdetails/gradecellview/GradeCellViewData.kt @@ -9,6 +9,7 @@ import com.instructure.pandautils.utils.ThemedColor import com.instructure.pandautils.utils.getContentDescriptionForMinusGradeString import com.instructure.pandautils.utils.orDefault import com.instructure.student.R +import com.instructure.student.mobius.assignmentDetails.ui.gradeCell.GradeCellViewState data class GradeCellViewData( val courseColor: ThemedColor, @@ -22,7 +23,8 @@ data class GradeCellViewData( val gradeCellContentDescription: String = "", val outOf: String = "", val latePenalty: String = "", - val finalGrade: String = "" + val finalGrade: String = "", + val stats: GradeCellViewState.GradeStats? = null ) { val backgroundColorWithAlpha = ColorUtils.setAlphaComponent(courseColor.backgroundColor(), (.25 * 255).toInt()) @@ -143,6 +145,28 @@ data class GradeCellViewData( finalGrade = resources.getString(R.string.finalGradeFormatted, submission.grade) } + val stats = assignment.scoreStatistics?.let { stats -> + GradeCellViewState.GradeStats( + score = submission.score, + outOf = assignment.pointsPossible, + min = stats.min, + max = stats.max, + mean = stats.mean, + minText = resources.getString( + R.string.scoreStatisticsLow, + NumberHelper.formatDecimal(stats.min, 1, true) + ), + maxText = resources.getString( + R.string.scoreStatisticsHigh, + NumberHelper.formatDecimal(stats.max, 1, true) + ), + meanText = resources.getString( + R.string.scoreStatisticsMean, + NumberHelper.formatDecimal(stats.mean, 1, true) + ) + ) + } + GradeCellViewData( courseColor = courseColor, state = State.GRADED, @@ -153,7 +177,8 @@ data class GradeCellViewData( gradeCellContentDescription = gradeCellContentDescription, outOf = outOfText, latePenalty = latePenalty, - finalGrade = finalGrade + finalGrade = finalGrade, + stats = stats ) } } diff --git a/apps/student/src/main/java/com/instructure/student/features/dashboard/notifications/StudentDashboardRouter.kt b/apps/student/src/main/java/com/instructure/student/features/dashboard/notifications/StudentDashboardRouter.kt index fc7e9a2955..44d5f46f5c 100644 --- a/apps/student/src/main/java/com/instructure/student/features/dashboard/notifications/StudentDashboardRouter.kt +++ b/apps/student/src/main/java/com/instructure/student/features/dashboard/notifications/StudentDashboardRouter.kt @@ -17,8 +17,11 @@ package com.instructure.student.features.dashboard.notifications import androidx.fragment.app.FragmentActivity +import com.instructure.canvasapi2.models.CanvasContext import com.instructure.pandautils.features.dashboard.notifications.DashboardRouter +import com.instructure.student.fragment.FileListFragment import com.instructure.student.fragment.InternalWebviewFragment +import com.instructure.student.mobius.assignmentDetails.submissionDetails.ui.SubmissionDetailsFragment import com.instructure.student.router.RouteMatcher class StudentDashboardRouter(private val activity: FragmentActivity) : DashboardRouter { @@ -35,4 +38,18 @@ class StudentDashboardRouter(private val activity: FragmentActivity) : Dashboard ) ) } + + override fun routeToSubmissionDetails(canvasContext: CanvasContext, assignmentId: Long, attemptId: Long) { + RouteMatcher.route( + activity, + SubmissionDetailsFragment.makeRoute(canvasContext, assignmentId, initialSelectedSubmissionAttempt = attemptId) + ) + } + + override fun routeToMyFiles(canvasContext: CanvasContext, folderId: Long) { + RouteMatcher.route( + activity, + FileListFragment.makeRoute(canvasContext, folderId) + ) + } } \ No newline at end of file diff --git a/apps/student/src/main/java/com/instructure/student/features/documentscanning/DocumentScanningActivity.kt b/apps/student/src/main/java/com/instructure/student/features/documentscanning/DocumentScanningActivity.kt index a61b973e59..73badd284b 100644 --- a/apps/student/src/main/java/com/instructure/student/features/documentscanning/DocumentScanningActivity.kt +++ b/apps/student/src/main/java/com/instructure/student/features/documentscanning/DocumentScanningActivity.kt @@ -40,13 +40,13 @@ import java.util.* @AndroidEntryPoint class DocumentScanningActivity : ScanActivity() { - private val binding by viewBinding(ActivityDocumentScanningBinding::inflate) + private lateinit var binding: ActivityDocumentScanningBinding private val viewModel: DocumentScanningViewModel by viewModels() override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - val binding = DataBindingUtil.setContentView(this, R.layout.activity_document_scanning) + binding = DataBindingUtil.setContentView(this, R.layout.activity_document_scanning) binding.lifecycleOwner = this binding.viewModel = viewModel diff --git a/apps/student/src/main/java/com/instructure/student/fragment/BasicQuizViewFragment.kt b/apps/student/src/main/java/com/instructure/student/fragment/BasicQuizViewFragment.kt index d6f59bd084..8ee29fb316 100644 --- a/apps/student/src/main/java/com/instructure/student/fragment/BasicQuizViewFragment.kt +++ b/apps/student/src/main/java/com/instructure/student/fragment/BasicQuizViewFragment.kt @@ -102,8 +102,8 @@ class BasicQuizViewFragment : InternalWebviewFragment() { } val uri = Uri.parse(baseURL) val host = uri.host ?: "" - getCanvasWebView().settings.javaScriptCanOpenWindowsAutomatically = true - getCanvasWebView().webViewClient = object : WebViewClient() { + getCanvasWebView()?.settings?.javaScriptCanOpenWindowsAutomatically = true + getCanvasWebView()?.webViewClient = object : WebViewClient() { override fun shouldOverrideUrlLoading(view: WebView?, request: WebResourceRequest?): Boolean = handleOverrideURlLoading(view, request?.url?.toString()) override fun shouldOverrideUrlLoading(view: WebView, url: String): Boolean = handleOverrideURlLoading(view, url) @@ -141,7 +141,7 @@ class BasicQuizViewFragment : InternalWebviewFragment() { } private fun setupFilePicker() { - getCanvasWebView().setCanvasWebChromeClientShowFilePickerCallback(object : CanvasWebView.VideoPickerCallback { + getCanvasWebView()?.setCanvasWebChromeClientShowFilePickerCallback(object : CanvasWebView.VideoPickerCallback { override fun requestStartActivityForResult(intent: Intent, requestCode: Int) { startActivityForResult(intent, requestCode) } @@ -167,13 +167,13 @@ class BasicQuizViewFragment : InternalWebviewFragment() { @Subscribe(threadMode = ThreadMode.MAIN) fun onRequestPermissionsResult(result: PermissionRequester.PermissionResult) { if (PermissionUtils.allPermissionsGrantedResultSummary(result.grantResults)) { - getCanvasWebView().clearPickerCallback() + getCanvasWebView()?.clearPickerCallback() Toast.makeText(requireContext(), R.string.pleaseTryAgain, Toast.LENGTH_SHORT).show() } } override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { - if (!getCanvasWebView().handleOnActivityResult(requestCode, resultCode, data)) { + if (getCanvasWebView()?.handleOnActivityResult(requestCode, resultCode, data) == false) { super.onActivityResult(requestCode, resultCode, data) } } @@ -213,7 +213,7 @@ class BasicQuizViewFragment : InternalWebviewFragment() { val authenticatedUrl = tryOrNull { awaitApi { OAuthManager.getAuthenticatedSession(url, it) }.sessionUrl } - getCanvasWebView().loadUrl(authenticatedUrl ?: url, APIHelper.referrer) + getCanvasWebView()?.loadUrl(authenticatedUrl ?: url, APIHelper.referrer) } } @@ -222,7 +222,7 @@ class BasicQuizViewFragment : InternalWebviewFragment() { quizDetailsJob?.cancel() } - override fun handleBackPressed() = getCanvasWebView().handleGoBack() ?: false + override fun handleBackPressed() = getCanvasWebView()?.handleGoBack() ?: false companion object { diff --git a/apps/student/src/main/java/com/instructure/student/fragment/CourseBrowserFragment.kt b/apps/student/src/main/java/com/instructure/student/fragment/CourseBrowserFragment.kt index 4bd63fec45..693f665549 100644 --- a/apps/student/src/main/java/com/instructure/student/fragment/CourseBrowserFragment.kt +++ b/apps/student/src/main/java/com/instructure/student/fragment/CourseBrowserFragment.kt @@ -222,6 +222,7 @@ class CourseBrowserFragment : Fragment(), FragmentInteractions, AppBarLayout.OnO * Manages state of titles & subtitles when users scrolls */ override fun onOffsetChanged(appBarLayout: AppBarLayout?, verticalOffset: Int) { + if (view == null) return val percentage = Math.abs(verticalOffset).div(appBarLayout?.totalScrollRange?.toFloat() ?: 1F) diff --git a/apps/student/src/main/java/com/instructure/student/fragment/CourseModuleProgressionFragment.kt b/apps/student/src/main/java/com/instructure/student/fragment/CourseModuleProgressionFragment.kt index 9140c858f7..98206784de 100644 --- a/apps/student/src/main/java/com/instructure/student/fragment/CourseModuleProgressionFragment.kt +++ b/apps/student/src/main/java/com/instructure/student/fragment/CourseModuleProgressionFragment.kt @@ -30,6 +30,7 @@ import androidx.lifecycle.lifecycleScope import com.instructure.canvasapi2.StatusCallback import com.instructure.canvasapi2.managers.* import com.instructure.canvasapi2.models.* +import com.instructure.canvasapi2.models.ModuleObject.State import com.instructure.canvasapi2.utils.* import com.instructure.canvasapi2.utils.pageview.PageView import com.instructure.canvasapi2.utils.weave.WeaveJob @@ -88,6 +89,7 @@ class CourseModuleProgressionFragment : ParentFragment(), Bookmarkable { private var assetId: String by StringArg(key = ASSET_ID) private var assetType: String by StringArg(key = ASSET_TYPE, default = ModuleItemAsset.MODULE_ITEM.assetType) private var route: Route by ParcelableArg(key = ROUTE) + private var navigatedFromModules: Boolean by BooleanArg(key = NAVIGATED_FROM_MODULES) // Default number will get reset private var itemsCount = 3 @@ -296,7 +298,9 @@ class CourseModuleProgressionFragment : ParentFragment(), Bookmarkable { updatePrevNextButtons(currentPos) - val completionRequirement = getCurrentModuleItem(currentPos)!!.completionRequirement + val currentModuleItem = getCurrentModuleItem(currentPos) ?: return + + val completionRequirement = currentModuleItem.completionRequirement if (completionRequirement != null && modules[groupPos].sequentialProgress) { // Reload the sequential module object to update the subsequent items that may now be unlocked // The user has viewed the item, and may have completed the contribute/submit requirements for a @@ -304,28 +308,38 @@ class CourseModuleProgressionFragment : ParentFragment(), Bookmarkable { addLockedIconIfNeeded(modules, items, groupPos, childPos) // Mark the item as viewed - markAsRead(modules[groupPos].id, getCurrentModuleItem(currentPos)!!.id) + markAsRead(currentModuleItem.moduleId, currentModuleItem.id) } - val moduleItem = getCurrentModuleItem(currentPos) - - updateModuleMarkDoneView(moduleItem) + updateModuleMarkDoneView(currentModuleItem) } private fun markAsRead(moduleId: Long, moduleItemId: Long) { markAsReadJob = tryWeave { // mark the moduleItem as viewed if we have a valid module id and item id, // but not the files, because they need to open or download those to view them - if(moduleId != 0L && moduleItemId != 0L && getCurrentModuleItem(currentPos)!!.type != ModuleItem.Type.File.toString()) { - awaitApi { ModuleManager.markModuleItemAsRead(canvasContext, moduleId, moduleItemId, it) } + if (moduleId != 0L && moduleItemId != 0L && getCurrentModuleItem(currentPos)!!.type != ModuleItem.Type.File.toString()) { + awaitApi { ModuleManager.markModuleItemAsRead(canvasContext, moduleId, moduleItemId, it) } // Update the module item locally, needed to unlock modules as the user ViewPages through them getCurrentModuleItem(currentPos)?.completionRequirement?.completed = true setupNextModule(getModuleItemGroup(currentPos)) + // Update the module state to indicate in the list that the module is completed + val module = modules.find { it.id == moduleId } ?: return@tryWeave + val isModuleCompleted = items.flatten().filter { it.moduleId == moduleId }.all { it.completionRequirement?.completed.orDefault() } + val updatedState = if (isModuleCompleted) State.Completed.apiString else module.state + // Update the module list fragment to show that these requirements are done, - ModuleUpdatedEvent(modules[groupPos]).post() + ModuleUpdatedEvent(module.copy(state = updatedState)).post() + + // Update the state of the next module to indicate in the list that it is unlocked + modules.getOrNull(modules.indexOf(module) + 1)?.let { + if (isModuleCompleted && it.state != State.Completed.apiString) { + ModuleUpdatedEvent(it.copy(state = State.Unlocked.apiString)).post() + } + } } } catch { Logger.e("Error marking module item as read. " + it.message) @@ -622,7 +636,7 @@ class CourseModuleProgressionFragment : ParentFragment(), Bookmarkable { // so we need to find the correct one overall val moduleItem = getCurrentModuleItem(position) ?: getCurrentModuleItem(0) // Default to the first item, band-aid for NPE - val fragment = ModuleUtility.getFragment(moduleItem!!, canvasContext as Course, modules[groupPos], isDiscussionRedesignEnabled) + val fragment = ModuleUtility.getFragment(moduleItem!!, canvasContext as Course, modules[groupPos], isDiscussionRedesignEnabled, navigatedFromModules) var args: Bundle? = fragment!!.arguments if (args == null) { args = Bundle() @@ -735,6 +749,7 @@ class CourseModuleProgressionFragment : ParentFragment(), Bookmarkable { private const val ASSET_ID = "asset_id" private const val ASSET_TYPE = "asset_type" private const val ROUTE = "route" + private const val NAVIGATED_FROM_MODULES = "navigated_from_modules" //we don't want to add subheaders or external tools into the list. subheaders don't do anything and we @@ -749,6 +764,7 @@ class CourseModuleProgressionFragment : ParentFragment(), Bookmarkable { return Route(null, CourseModuleProgressionFragment::class.java, canvasContext, canvasContext.makeBundle(Bundle().apply { putInt(GROUP_POSITION, groupPos) putInt(CHILD_POSITION, childPos) + putBoolean(NAVIGATED_FROM_MODULES, true) })) } diff --git a/apps/student/src/main/java/com/instructure/student/fragment/DiscussionDetailsFragment.kt b/apps/student/src/main/java/com/instructure/student/fragment/DiscussionDetailsFragment.kt index 67111527c7..add2fbdc0f 100644 --- a/apps/student/src/main/java/com/instructure/student/fragment/DiscussionDetailsFragment.kt +++ b/apps/student/src/main/java/com/instructure/student/fragment/DiscussionDetailsFragment.kt @@ -248,7 +248,7 @@ class DiscussionDetailsFragment : ParentFragment(), Bookmarkable { successfullyMarkedAsReadIds.forEach { binding.discussionRepliesWebViewWrapper.post { // Posting lets this escape Weave's lifecycle, so use a null-safe call on the webview here - binding.discussionRepliesWebViewWrapper.webView.loadUrl("javascript:markAsRead" + "('" + it.toString() + "')") + if (view != null) binding.discussionRepliesWebViewWrapper.webView.loadUrl("javascript:markAsRead" + "('" + it.toString() + "')") } } if (!groupDiscussion) { @@ -696,7 +696,7 @@ class DiscussionDetailsFragment : ParentFragment(), Bookmarkable { replyToDiscussionTopic.onClick { showReplyView(discussionTopicHeader.id) } loadHeaderHtmlJob = discussionTopicHeaderWebViewWrapper.webView.loadHtmlWithIframes(requireContext(), discussionTopicHeader.message, { - loadHTMLTopic(it, discussionTopicHeader.title) + if (view != null) loadHTMLTopic(it, discussionTopicHeader.title) }) attachmentIcon.setVisible(discussionTopicHeader.attachments.isNotEmpty()) diff --git a/apps/student/src/main/java/com/instructure/student/fragment/EditPageDetailsFragment.kt b/apps/student/src/main/java/com/instructure/student/fragment/EditPageDetailsFragment.kt index e2a25d822e..96e685f846 100644 --- a/apps/student/src/main/java/com/instructure/student/fragment/EditPageDetailsFragment.kt +++ b/apps/student/src/main/java/com/instructure/student/fragment/EditPageDetailsFragment.kt @@ -30,6 +30,7 @@ import com.instructure.canvasapi2.models.CanvasContext import com.instructure.canvasapi2.models.Page import com.instructure.canvasapi2.models.postmodels.PagePostBody import com.instructure.canvasapi2.utils.ApiPrefs +import com.instructure.canvasapi2.utils.pageview.PageView import com.instructure.canvasapi2.utils.pageview.PageViewUrl import com.instructure.canvasapi2.utils.weave.WeaveJob import com.instructure.canvasapi2.utils.weave.awaitApi @@ -46,6 +47,7 @@ import com.instructure.student.dialog.UnsavedChangesExitDialog import com.instructure.student.events.PageUpdatedEvent import org.greenrobot.eventbus.EventBus +@PageView @ScreenView(SCREEN_VIEW_EDIT_PAGE_DETAILS) class EditPageDetailsFragment : ParentFragment() { diff --git a/apps/student/src/main/java/com/instructure/student/fragment/FileListFragment.kt b/apps/student/src/main/java/com/instructure/student/fragment/FileListFragment.kt index 633900e4ab..e41d5436e1 100644 --- a/apps/student/src/main/java/com/instructure/student/fragment/FileListFragment.kt +++ b/apps/student/src/main/java/com/instructure/student/fragment/FileListFragment.kt @@ -211,7 +211,7 @@ class FileListFragment : ParentFragment(), Bookmarkable, FileUploadDialogParent override fun onRefreshFinished() { setRefreshing(false) - if (recyclerAdapter?.size() == 0) { + if (recyclerAdapter?.size() == 0 && view != null) { setEmptyView(binding.emptyView, R.drawable.ic_panda_nofiles, R.string.noFiles, getNoFileSubtextId()) } } diff --git a/apps/student/src/main/java/com/instructure/student/fragment/GradesListFragment.kt b/apps/student/src/main/java/com/instructure/student/fragment/GradesListFragment.kt index 29a87d1dbd..97e68de475 100644 --- a/apps/student/src/main/java/com/instructure/student/fragment/GradesListFragment.kt +++ b/apps/student/src/main/java/com/instructure/student/fragment/GradesListFragment.kt @@ -241,6 +241,7 @@ class GradesListFragment : ParentFragment(), Bookmarkable { private val gradingPeriodsCallback = object : StatusCallback() { override fun onResponse(response: Response, linkHeaders: LinkHeaders, type: ApiType) { + if (view == null) return with(binding) { gradingPeriodsList = ArrayList() gradingPeriodsList.addAll(response.body()!!.gradingPeriodList) diff --git a/apps/student/src/main/java/com/instructure/student/fragment/InboxComposeMessageFragment.kt b/apps/student/src/main/java/com/instructure/student/fragment/InboxComposeMessageFragment.kt index e3b128d04c..3a478db30b 100644 --- a/apps/student/src/main/java/com/instructure/student/fragment/InboxComposeMessageFragment.kt +++ b/apps/student/src/main/java/com/instructure/student/fragment/InboxComposeMessageFragment.kt @@ -40,7 +40,7 @@ import com.instructure.pandautils.analytics.ScreenView import com.instructure.pandautils.binding.viewBinding import com.instructure.pandautils.features.file.upload.FileUploadDialogFragment import com.instructure.pandautils.features.file.upload.FileUploadDialogParent -import com.instructure.pandautils.room.daos.AttachmentDao +import com.instructure.pandautils.room.appdatabase.daos.AttachmentDao import com.instructure.pandautils.utils.* import com.instructure.student.R import com.instructure.student.adapter.CanvasContextSpinnerAdapter diff --git a/apps/student/src/main/java/com/instructure/student/fragment/InternalWebviewFragment.kt b/apps/student/src/main/java/com/instructure/student/fragment/InternalWebviewFragment.kt index 9e1ec9cd9f..1185f145ad 100644 --- a/apps/student/src/main/java/com/instructure/student/fragment/InternalWebviewFragment.kt +++ b/apps/student/src/main/java/com/instructure/student/fragment/InternalWebviewFragment.kt @@ -345,7 +345,7 @@ open class InternalWebviewFragment : ParentFragment() { } fun getCanvasLoading(): ProgressBar? = if (view != null) binding.webViewLoading else null - fun getCanvasWebView(): CanvasWebView = binding.canvasWebViewWrapper.webView + fun getCanvasWebView(): CanvasWebView? = if (view != null) binding.canvasWebViewWrapper.webView else null fun getIsUnsupportedFeature(): Boolean = isUnsupportedFeature private fun getReferer(): Map = mutableMapOf(Pair("Referer", ApiPrefs.domain)) @@ -408,7 +408,7 @@ open class InternalWebviewFragment : ParentFragment() { .build().toString() } - binding.canvasWebViewWrapper.webView.loadUrl(url!!, getReferer()) + if (view != null) binding.canvasWebViewWrapper.webView.loadUrl(url!!, getReferer()) } } } diff --git a/apps/student/src/main/java/com/instructure/student/fragment/PageDetailsFragment.kt b/apps/student/src/main/java/com/instructure/student/fragment/PageDetailsFragment.kt index 11bb6b9a34..f938d788c6 100644 --- a/apps/student/src/main/java/com/instructure/student/fragment/PageDetailsFragment.kt +++ b/apps/student/src/main/java/com/instructure/student/fragment/PageDetailsFragment.kt @@ -61,6 +61,7 @@ class PageDetailsFragment : InternalWebviewFragment(), Bookmarkable { private var pageName: String? by NullableStringArg(key = PAGE_NAME) private var page: Page by ParcelableArg(default = Page(), key = PAGE) private var pageUrl: String? by NullableStringArg(key = PAGE_URL) + private var navigatedFromModules: Boolean by BooleanArg(key = NAVIGATED_FROM_MODULES) // Flag for the webview client to know whether or not we should clear the history private var isUpdated = false @@ -102,19 +103,19 @@ class PageDetailsFragment : InternalWebviewFragment(), Bookmarkable { override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - getCanvasWebView().canvasEmbeddedWebViewCallback = object : CanvasWebView.CanvasEmbeddedWebViewCallback { + getCanvasWebView()?.canvasEmbeddedWebViewCallback = object : CanvasWebView.CanvasEmbeddedWebViewCallback { override fun shouldLaunchInternalWebViewFragment(url: String): Boolean = true override fun launchInternalWebViewFragment(url: String) = InternalWebviewFragment.loadInternalWebView(activity, InternalWebviewFragment.makeRoute(canvasContext, url, isLTITool)) } // Add to the webview client for clearing webview history after an update to prevent going back to old data - val callback = getCanvasWebView().canvasWebViewClientCallback + val callback = getCanvasWebView()?.canvasWebViewClientCallback callback?.let { - getCanvasWebView().canvasWebViewClientCallback = object : CanvasWebView.CanvasWebViewClientCallback by it { + getCanvasWebView()?.canvasWebViewClientCallback = object : CanvasWebView.CanvasWebViewClientCallback by it { override fun onPageFinishedCallback(webView: WebView, url: String) { it.onPageFinishedCallback(webView, url) // Only clear history after an update - if (isUpdated) getCanvasWebView().clearHistory() + if (isUpdated) getCanvasWebView()?.clearHistory() } override fun openMediaFromWebView(mime: String, url: String, filename: String) { @@ -185,7 +186,7 @@ class PageDetailsFragment : InternalWebviewFragment(), Bookmarkable { setPageObject(page) if (page.lockInfo != null) { - val lockedMessage = LockInfoHTMLHelper.getLockedInfoHTML(page.lockInfo!!, requireContext(), R.string.lockedPageDesc) + val lockedMessage = LockInfoHTMLHelper.getLockedInfoHTML(page.lockInfo!!, requireContext(), R.string.lockedPageDesc, !navigatedFromModules) populateWebView(lockedMessage, getString(R.string.pages)) return } @@ -289,12 +290,16 @@ class PageDetailsFragment : InternalWebviewFragment(), Bookmarkable { } private fun checkCanEdit() = with(binding) { - if (page.editingRoles?.contains("public") == true) { - toolbar.menu?.findItem(R.id.menu_edit)?.isVisible = true - } else if (page.editingRoles?.contains("student") == true && (canvasContext as? Course)?.isStudent == true) { - toolbar.menu?.findItem(R.id.menu_edit)?.isVisible = true - } else if (page.editingRoles?.contains("teacher") == true && (canvasContext as? Course)?.isTeacher == true) { - toolbar.menu?.findItem(R.id.menu_edit)?.isVisible = true + val course = canvasContext as? Course + val editingRoles = page.editingRoles.orEmpty() + if (course?.isStudent == true) { + if (page.lockInfo == null && (editingRoles.contains("public") || editingRoles.contains("student"))) { + toolbar.menu?.findItem(R.id.menu_edit)?.isVisible = true + } + } else if (course?.isTeacher == true) { + if ((editingRoles.contains("public") || editingRoles.contains("teacher"))) { + toolbar.menu?.findItem(R.id.menu_edit)?.isVisible = true + } } } @@ -319,6 +324,7 @@ class PageDetailsFragment : InternalWebviewFragment(), Bookmarkable { const val PAGE_NAME = "pageDetailsName" const val PAGE = "pageDetails" const val PAGE_URL = "pageUrl" + const val NAVIGATED_FROM_MODULES = "navigated_from_modules" fun newInstance(route: Route): PageDetailsFragment? { return if (validRoute(route)) PageDetailsFragment().apply { @@ -341,8 +347,9 @@ class PageDetailsFragment : InternalWebviewFragment(), Bookmarkable { return Route(null, PageDetailsFragment::class.java, canvasContext, canvasContext.makeBundle(Bundle().apply { if (pageName != null) putString(PAGE_NAME, pageName) })) } - fun makeRoute(canvasContext: CanvasContext, pageName: String?, pageUrl: String?): Route { + fun makeRoute(canvasContext: CanvasContext, pageName: String?, pageUrl: String?, navigatedFromModules: Boolean): Route { return Route(null, PageDetailsFragment::class.java, canvasContext, canvasContext.makeBundle(Bundle().apply { + putBoolean(NAVIGATED_FROM_MODULES, navigatedFromModules) if (pageName != null) putString(PAGE_NAME, pageName) if (pageUrl != null) diff --git a/apps/student/src/main/java/com/instructure/student/fragment/ParentFragment.kt b/apps/student/src/main/java/com/instructure/student/fragment/ParentFragment.kt index f1c4c24112..f4939a341f 100644 --- a/apps/student/src/main/java/com/instructure/student/fragment/ParentFragment.kt +++ b/apps/student/src/main/java/com/instructure/student/fragment/ParentFragment.kt @@ -112,7 +112,7 @@ abstract class ParentFragment : DialogFragment(), FragmentInteractions, Navigati InternalWebviewFragment.loadInternalWebView(activity, InternalWebviewFragment.makeRoute(loadedMedia.bundle!!)) } else if (loadedMedia.intent != null && context != null) { // Show pdf with PSPDFkit - if (loadedMedia.intent!!.type!!.contains("pdf") && !loadedMedia.isUseOutsideApps) { + if (loadedMedia.intent?.type?.contains("pdf") == true && !loadedMedia.isUseOutsideApps) { val uri = loadedMedia.intent!!.data FileUtils.showPdfDocument(uri!!, loadedMedia, requireContext()) } else if (loadedMedia.intent?.type == "video/mp4") { diff --git a/apps/student/src/main/java/com/instructure/student/fragment/StudioWebViewFragment.kt b/apps/student/src/main/java/com/instructure/student/fragment/StudioWebViewFragment.kt index 038ebdbdff..7cd4b3bf2f 100644 --- a/apps/student/src/main/java/com/instructure/student/fragment/StudioWebViewFragment.kt +++ b/apps/student/src/main/java/com/instructure/student/fragment/StudioWebViewFragment.kt @@ -50,10 +50,10 @@ class StudioWebViewFragment : InternalWebviewFragment() { override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - getCanvasWebView().enableAlgorithmicDarkening() - getCanvasWebView().addJavascriptInterface(JSInterface(), "HtmlViewer") + getCanvasWebView()?.enableAlgorithmicDarkening() + getCanvasWebView()?.addJavascriptInterface(JSInterface(), "HtmlViewer") - getCanvasWebView().canvasWebViewClientCallback = object : CanvasWebView.CanvasWebViewClientCallback { + getCanvasWebView()?.canvasWebViewClientCallback = object : CanvasWebView.CanvasWebViewClientCallback { override fun openMediaFromWebView(mime: String, url: String, filename: String) { openMedia(mime, url, filename, canvasContext) } @@ -80,7 +80,7 @@ class StudioWebViewFragment : InternalWebviewFragment() { } } - getCanvasWebView().setCanvasWebChromeClientShowFilePickerCallback(object : CanvasWebView.VideoPickerCallback { + getCanvasWebView()?.setCanvasWebChromeClientShowFilePickerCallback(object : CanvasWebView.VideoPickerCallback { override fun requestStartActivityForResult(intent: Intent, requestCode: Int) { startActivityForResult(intent, requestCode) } @@ -99,8 +99,8 @@ class StudioWebViewFragment : InternalWebviewFragment() { override fun handleBackPressed(): Boolean { if (canGoBack()) { // This prevents a silly bug where the Studio WebView cannot go back far enough to pop its fragment - val webBackForwardList = getCanvasWebView().copyBackForwardList() - val historyUrl = webBackForwardList.getItemAtIndex(webBackForwardList.currentIndex - 1)?.url + val webBackForwardList = getCanvasWebView()?.copyBackForwardList() + val historyUrl = webBackForwardList?.getItemAtIndex(webBackForwardList.currentIndex - 1)?.url if (historyUrl != null && historyUrl.contains("external_tools/") && historyUrl.contains("resource_selection")) { navigation?.popCurrentFragment() return true @@ -116,7 +116,7 @@ class StudioWebViewFragment : InternalWebviewFragment() { override fun onRequestPermissionsResult(requestCode: Int, permissions: Array, grantResults: IntArray) { if (PermissionUtils.allPermissionsGrantedResultSummary(grantResults)) { - getCanvasWebView().clearPickerCallback() + getCanvasWebView()?.clearPickerCallback() Toast.makeText(requireContext(), R.string.pleaseTryAgain, Toast.LENGTH_SHORT).show() } super.onRequestPermissionsResult(requestCode, permissions, grantResults) @@ -143,7 +143,7 @@ class StudioWebViewFragment : InternalWebviewFragment() { } override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { - if ((getCanvasWebView().handleOnActivityResult(requestCode, resultCode, data)) != true) { + if ((getCanvasWebView()?.handleOnActivityResult(requestCode, resultCode, data)) != true) { super.onActivityResult(requestCode, resultCode, data) } } diff --git a/apps/student/src/main/java/com/instructure/student/mobius/assignmentDetails/submission/annnotation/AnnotationSubmissionUploadFragment.kt b/apps/student/src/main/java/com/instructure/student/mobius/assignmentDetails/submission/annnotation/AnnotationSubmissionUploadFragment.kt index eb841adf7c..d3e2e49510 100644 --- a/apps/student/src/main/java/com/instructure/student/mobius/assignmentDetails/submission/annnotation/AnnotationSubmissionUploadFragment.kt +++ b/apps/student/src/main/java/com/instructure/student/mobius/assignmentDetails/submission/annnotation/AnnotationSubmissionUploadFragment.kt @@ -57,7 +57,7 @@ class AnnotationSubmissionUploadFragment : Fragment() { viewModel.pdfUrl.observe(viewLifecycleOwner, { binding.annotationSubmissionViewContainer.addView( - PdfStudentSubmissionView(requireActivity(), it, studentAnnotationSubmit = true) + PdfStudentSubmissionView(requireActivity(), it, childFragmentManager, studentAnnotationSubmit = true) ) }) diff --git a/apps/student/src/main/java/com/instructure/student/mobius/assignmentDetails/submissionDetails/content/AnnotationSubmissionViewFragment.kt b/apps/student/src/main/java/com/instructure/student/mobius/assignmentDetails/submissionDetails/content/AnnotationSubmissionViewFragment.kt index d903acb240..7f9ec7e045 100644 --- a/apps/student/src/main/java/com/instructure/student/mobius/assignmentDetails/submissionDetails/content/AnnotationSubmissionViewFragment.kt +++ b/apps/student/src/main/java/com/instructure/student/mobius/assignmentDetails/submissionDetails/content/AnnotationSubmissionViewFragment.kt @@ -52,7 +52,8 @@ class AnnotationSubmissionViewFragment : Fragment() { PdfStudentSubmissionView( requireActivity(), it, - studentAnnotationView = true + childFragmentManager, + studentAnnotationView = true, ) ) } diff --git a/apps/student/src/main/java/com/instructure/student/mobius/assignmentDetails/submissionDetails/content/PdfStudentSubmissionView.kt b/apps/student/src/main/java/com/instructure/student/mobius/assignmentDetails/submissionDetails/content/PdfStudentSubmissionView.kt index f238525403..05a4189e57 100644 --- a/apps/student/src/main/java/com/instructure/student/mobius/assignmentDetails/submissionDetails/content/PdfStudentSubmissionView.kt +++ b/apps/student/src/main/java/com/instructure/student/mobius/assignmentDetails/submissionDetails/content/PdfStudentSubmissionView.kt @@ -25,6 +25,7 @@ import android.view.LayoutInflater import android.widget.FrameLayout import android.widget.ImageView import androidx.fragment.app.Fragment +import androidx.fragment.app.FragmentManager import com.instructure.annotations.PdfSubmissionView import com.instructure.canvasapi2.managers.CanvaDocsManager import com.instructure.canvasapi2.models.ApiValues @@ -60,8 +61,9 @@ import org.greenrobot.eventbus.ThreadMode class PdfStudentSubmissionView( context: Context, private val pdfUrl: String, + private val fragmentManager: FragmentManager, private val studentAnnotationSubmit: Boolean = false, - private val studentAnnotationView: Boolean = false + private val studentAnnotationView: Boolean = false, ) : PdfSubmissionView(context, studentAnnotationView), AnnotationManager.OnAnnotationCreationModeChangeListener, AnnotationManager.OnAnnotationEditingModeChangeListener { private val binding: ViewPdfStudentSubmissionBinding @@ -124,7 +126,7 @@ class PdfStudentSubmissionView( } override fun showNoInternetDialog() { - NoInternetConnectionDialog.show(supportFragmentManager) + NoInternetConnectionDialog.show(fragmentManager) } init { @@ -178,13 +180,13 @@ class PdfStudentSubmissionView( @SuppressLint("CommitTransaction") override fun setFragment(fragment: Fragment) { - if (isAttachedToWindow) supportFragmentManager.beginTransaction().replace(binding.content.id, fragment).commitNowAllowingStateLoss() + if (isAttachedToWindow) fragmentManager.beginTransaction().replace(binding.content.id, fragment).commitNowAllowingStateLoss() } override fun removeContentFragment() { - val contentFragment = supportFragmentManager.findFragmentById(binding.content.id) + val contentFragment = fragmentManager.findFragmentById(binding.content.id) if (contentFragment != null) { - supportFragmentManager.beginTransaction().remove(contentFragment).commitAllowingStateLoss() + fragmentManager.beginTransaction().remove(contentFragment).commitAllowingStateLoss() } } diff --git a/apps/student/src/main/java/com/instructure/student/mobius/assignmentDetails/submissionDetails/content/PdfSubmissionViewFragment.kt b/apps/student/src/main/java/com/instructure/student/mobius/assignmentDetails/submissionDetails/content/PdfSubmissionViewFragment.kt index 35c8aa0183..5ad05191da 100644 --- a/apps/student/src/main/java/com/instructure/student/mobius/assignmentDetails/submissionDetails/content/PdfSubmissionViewFragment.kt +++ b/apps/student/src/main/java/com/instructure/student/mobius/assignmentDetails/submissionDetails/content/PdfSubmissionViewFragment.kt @@ -28,7 +28,7 @@ class PdfSubmissionViewFragment : Fragment() { private var pdfUrl by StringArg() override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { - return PdfStudentSubmissionView(requireContext(), pdfUrl) + return PdfStudentSubmissionView(requireContext(), pdfUrl, childFragmentManager) } companion object { diff --git a/apps/student/src/main/java/com/instructure/student/mobius/assignmentDetails/submissionDetails/drawer/comments/SubmissionCommentsPresenter.kt b/apps/student/src/main/java/com/instructure/student/mobius/assignmentDetails/submissionDetails/drawer/comments/SubmissionCommentsPresenter.kt index 4c32318786..18ae4f40d2 100644 --- a/apps/student/src/main/java/com/instructure/student/mobius/assignmentDetails/submissionDetails/drawer/comments/SubmissionCommentsPresenter.kt +++ b/apps/student/src/main/java/com/instructure/student/mobius/assignmentDetails/submissionDetails/drawer/comments/SubmissionCommentsPresenter.kt @@ -39,7 +39,7 @@ object SubmissionCommentsPresenter : Presenter + val comments = model.comments.filter { it.attempt == null || it.attempt == model.attemptId || !model.assignmentEnhancementsEnabled }.map { comment -> val date = comment.createdAt ?: Date(0) CommentItemState.CommentItem( id = comment.id, diff --git a/apps/student/src/main/java/com/instructure/student/mobius/assignmentDetails/ui/gradeCell/GradeStatisticsView.kt b/apps/student/src/main/java/com/instructure/student/mobius/assignmentDetails/ui/gradeCell/GradeStatisticsView.kt index d50e5ff43e..41b8aedbe4 100644 --- a/apps/student/src/main/java/com/instructure/student/mobius/assignmentDetails/ui/gradeCell/GradeStatisticsView.kt +++ b/apps/student/src/main/java/com/instructure/student/mobius/assignmentDetails/ui/gradeCell/GradeStatisticsView.kt @@ -2,7 +2,6 @@ package com.instructure.student.mobius.assignmentDetails.ui.gradeCell import android.content.Context import android.graphics.Canvas -import android.graphics.Color import android.graphics.Paint import android.util.AttributeSet import android.view.View @@ -14,7 +13,7 @@ import com.instructure.student.R class GradeStatisticsView(context: Context, attrs: AttributeSet) : View(context, attrs) { private var stats: GradeCellViewState.GradeStats? = null - private val sidePadding: Float = context.DP(16) + private val sidePadding: Float = context.DP(2) private val endMarkerHeight: Float = context.DP(16) private val minMaxHeight: Float = context.DP(16) private val scoreCircleRadius: Float = context.DP(7) @@ -22,7 +21,7 @@ class GradeStatisticsView(context: Context, attrs: AttributeSet) : View(context, private val linePaint: Paint = Paint(Paint.ANTI_ALIAS_FLAG).apply { style = Paint.Style.STROKE - strokeCap = Paint.Cap.ROUND + strokeCap = Paint.Cap.SQUARE isAntiAlias = true strokeWidth = context.DP(2) color = ContextCompat.getColor(context, R.color.backgroundMedium) @@ -30,7 +29,7 @@ class GradeStatisticsView(context: Context, attrs: AttributeSet) : View(context, private val darkLinePaint: Paint = Paint(Paint.ANTI_ALIAS_FLAG).apply { style = Paint.Style.STROKE - strokeCap = Paint.Cap.ROUND + strokeCap = Paint.Cap.SQUARE isAntiAlias = true strokeWidth = context.DP(3) color = ContextCompat.getColor(context, R.color.textDark) @@ -38,7 +37,7 @@ class GradeStatisticsView(context: Context, attrs: AttributeSet) : View(context, private val meanLinePaint: Paint = Paint(Paint.ANTI_ALIAS_FLAG).apply { style = Paint.Style.STROKE - strokeCap = Paint.Cap.ROUND + strokeCap = Paint.Cap.SQUARE isAntiAlias = true strokeWidth = context.DP(3) color = ContextCompat.getColor(context, R.color.textDarkest) diff --git a/apps/student/src/main/java/com/instructure/student/router/RouteMatcher.kt b/apps/student/src/main/java/com/instructure/student/router/RouteMatcher.kt index 4ef1689826..cb24098b0e 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 @@ -139,6 +139,7 @@ object RouteMatcher : BaseRouteMatcher() { // Discussions routes.add(Route(courseOrGroup("/:${RouterParams.COURSE_ID}/discussion_topics"), DiscussionListFragment::class.java)) routes.add(Route(courseOrGroup("/:${RouterParams.COURSE_ID}/discussion_topics/:${RouterParams.MESSAGE_ID}"), DiscussionListFragment::class.java, CourseModuleProgressionFragment::class.java)) + routes.add(Route(courseOrGroup("/:${RouterParams.COURSE_ID}/discussion_topics/:${RouterParams.MESSAGE_ID}"), DiscussionListFragment::class.java, DiscussionRouterFragment::class.java)) // Route for bookmarking // Pages routes.add(Route(courseOrGroup("/:${RouterParams.COURSE_ID}/pages"), PageListFragment::class.java)) @@ -499,7 +500,11 @@ object RouteMatcher : BaseRouteMatcher() { fun generateUrl(type: CanvasContext.Type, masterCls: Class?, detailCls: Class?, replacementParams: HashMap?, queryParams: HashMap?): String? { val domain = ApiPrefs.fullDomain - val urlRoute = getInternalRoute(masterCls, detailCls) + + // Workaround for the discussion details because we bookmark a different class that we use for routing + val detailsClass = if (detailCls == DiscussionDetailsFragment::class.java) DiscussionRouterFragment::class.java else detailCls + + val urlRoute = getInternalRoute(masterCls, detailsClass) if(urlRoute != null) { var path = urlRoute.createUrl(replacementParams) if (path.contains(COURSE_OR_GROUP_REGEX)) { diff --git a/apps/student/src/main/java/com/instructure/student/service/StudentPageViewService.kt b/apps/student/src/main/java/com/instructure/student/service/StudentPageViewService.kt index 6c2f881dbe..e53b079a51 100644 --- a/apps/student/src/main/java/com/instructure/student/service/StudentPageViewService.kt +++ b/apps/student/src/main/java/com/instructure/student/service/StudentPageViewService.kt @@ -17,9 +17,9 @@ package com.instructure.student.service import com.google.firebase.crashlytics.FirebaseCrashlytics -import com.instructure.student.BuildConfig import com.instructure.canvasapi2.utils.pageview.PageViewUploadService import com.instructure.canvasapi2.utils.pageview.PandataInfo +import com.instructure.student.BuildConfig /** * A [PageViewUploadService] specific to the Student application. @@ -30,7 +30,6 @@ import com.instructure.canvasapi2.utils.pageview.PandataInfo class StudentPageViewService : PageViewUploadService() { override val appKey = pandataAppKey - override fun onException(e: Throwable) = FirebaseCrashlytics.getInstance().recordException(e) diff --git a/apps/student/src/main/java/com/instructure/student/util/LockInfoHTMLHelper.kt b/apps/student/src/main/java/com/instructure/student/util/LockInfoHTMLHelper.kt index e4696135b3..7da28a29bd 100644 --- a/apps/student/src/main/java/com/instructure/student/util/LockInfoHTMLHelper.kt +++ b/apps/student/src/main/java/com/instructure/student/util/LockInfoHTMLHelper.kt @@ -25,7 +25,7 @@ import com.instructure.student.R import java.util.* object LockInfoHTMLHelper { - fun getLockedInfoHTML(lockInfo: LockInfo, context: Context, explanationFirstLine: Int): String { + fun getLockedInfoHTML(lockInfo: LockInfo, context: Context, explanationFirstLine: Int, addModulesLink: Boolean = true): String { /* Note: if the html that this is going in isn't based on html_wrapper.html (it will have something like -- String html = CanvasAPI.getAssetsFile(getSherlockActivity(), "html_wrapper.html");) this will not look as good. The blue button will just be a link. */ @@ -55,11 +55,13 @@ object LockInfoHTMLHelper { } // Make sure we know what the protocol is (http or https) - lockInfo.contextModule?.let { module -> - // Create the url to modules for this course - val url = "$protocol://$domain/courses/${module.contextId}/modules" - // Create the button and link it to modules - lockedMessage += """
${context.resources.getString(R.string.goToModules)}
""" + if (addModulesLink) { + lockInfo.contextModule?.let { module -> + // Create the url to modules for this course + val url = "$protocol://$domain/courses/${module.contextId}/modules" + // Create the button and link it to modules + lockedMessage += """
${context.resources.getString(R.string.goToModules)}
""" + } } return lockedMessage } diff --git a/apps/student/src/main/java/com/instructure/student/util/ModuleUtility.kt b/apps/student/src/main/java/com/instructure/student/util/ModuleUtility.kt index c15b3977c8..bcb221e07d 100644 --- a/apps/student/src/main/java/com/instructure/student/util/ModuleUtility.kt +++ b/apps/student/src/main/java/com/instructure/student/util/ModuleUtility.kt @@ -37,8 +37,8 @@ import com.instructure.student.fragment.PageDetailsFragment.Companion.makeRoute import java.util.* object ModuleUtility { - fun getFragment(item: ModuleItem, course: Course, moduleObject: ModuleObject?, isDiscussionRedesignEnabled: Boolean): Fragment? = when (item.type) { - "Page" -> PageDetailsFragment.newInstance(makeRoute(course, item.title, item.pageUrl)) + fun getFragment(item: ModuleItem, course: Course, moduleObject: ModuleObject?, isDiscussionRedesignEnabled: Boolean, navigatedFromModules: Boolean): Fragment? = when (item.type) { + "Page" -> PageDetailsFragment.newInstance(makeRoute(course, item.title, item.pageUrl, navigatedFromModules)) "Assignment" -> AssignmentDetailsFragment.newInstance(makeRoute(course, getAssignmentId(item))) "Discussion" -> { if (isDiscussionRedesignEnabled) { diff --git a/apps/student/src/main/res/layout/view_student_enhanced_grade_cell.xml b/apps/student/src/main/res/layout/view_student_enhanced_grade_cell.xml index 83ec241625..15cf56ca1a 100644 --- a/apps/student/src/main/res/layout/view_student_enhanced_grade_cell.xml +++ b/apps/student/src/main/res/layout/view_student_enhanced_grade_cell.xml @@ -169,12 +169,13 @@ android:layout_height="130dp" android:layout_marginTop="@dimen/grade_cell_chart_top_margin" android:layout_marginEnd="12dp" - android:layout_marginBottom="20dp" + android:layout_marginBottom="8dp" android:visibility="@{viewData.state == viewData.State.GRADED ? View.VISIBLE : View.GONE}" app:color="@{viewData.showIncompleteIcon ? @color/textDark : viewData.courseColor.backgroundColor()}" - app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintBottom_toTopOf="@id/statisticsView" app:layout_constraintEnd_toStartOf="@id/guideline" app:layout_constraintTop_toBottomOf="@id/gradeLabel" + app:layout_goneMarginBottom="20dp" app:progress="@{viewData.chartPercent}" app:trackColor="@{viewData.backgroundColorWithAlpha}" /> @@ -311,6 +312,62 @@ app:layout_constraintTop_toBottomOf="@id/latePenalty" tools:text="Final Grade: 73 pts" /> + + + + + + + + \ No newline at end of file diff --git a/apps/student/src/main/res/layout/view_student_grade_cell.xml b/apps/student/src/main/res/layout/view_student_grade_cell.xml index d3fd6f2986..3f864caa2d 100644 --- a/apps/student/src/main/res/layout/view_student_grade_cell.xml +++ b/apps/student/src/main/res/layout/view_student_grade_cell.xml @@ -191,9 +191,7 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:gravity="center_horizontal" - android:orientation="horizontal" - android:paddingStart="16dp" - android:paddingEnd="16dp"> + android:orientation="horizontal"> state.sortDate diff --git a/apps/student/src/test/java/com/instructure/student/test/util/ModuleUtilityTest.kt b/apps/student/src/test/java/com/instructure/student/test/util/ModuleUtilityTest.kt index 5a8ada70b5..26f2f7161a 100644 --- a/apps/student/src/test/java/com/instructure/student/test/util/ModuleUtilityTest.kt +++ b/apps/student/src/test/java/com/instructure/student/test/util/ModuleUtilityTest.kt @@ -88,6 +88,7 @@ class ModuleUtilityTest : TestCase() { val expectedBundle = Bundle() expectedBundle.putParcelable(Const.CANVAS_CONTEXT, course) expectedBundle.putString(PageDetailsFragment.PAGE_NAME, "hello-world") + expectedBundle.putBoolean(PageDetailsFragment.NAVIGATED_FROM_MODULES, false) val parentFragment = callGetFragment(moduleItem, course, null) TestCase.assertNotNull(parentFragment) @@ -246,6 +247,6 @@ class ModuleUtilityTest : TestCase() { } private fun callGetFragment(moduleItem: ModuleItem, course: Course, moduleObject: ModuleObject?): Fragment? { - return ModuleUtility.getFragment(moduleItem, course, moduleObject, false) + return ModuleUtility.getFragment(moduleItem, course, moduleObject, false, false) } } diff --git a/apps/teacher/build.gradle b/apps/teacher/build.gradle index e686a38411..98851a2f6f 100644 --- a/apps/teacher/build.gradle +++ b/apps/teacher/build.gradle @@ -16,6 +16,7 @@ import com.instructure.android.buildtools.transform.LocaleTransformer import com.instructure.android.buildtools.transform.MasqueradeUITransformer +import com.instructure.android.buildtools.transform.PageViewTransformer import com.instructure.android.buildtools.transform.ProjectTransformer import com.instructure.android.buildtools.transform.ScreenViewTransformer @@ -41,8 +42,8 @@ android { defaultConfig { minSdkVersion Versions.MIN_SDK targetSdkVersion Versions.TARGET_SDK - versionCode = 55 - versionName = '1.22.0' + versionCode = 57 + versionName = '1.23.1' vectorDrawables.useSupportLibrary = true multiDexEnabled true testInstrumentationRunner 'com.instructure.teacher.ui.espresso.TeacherHiltTestRunner' @@ -189,6 +190,7 @@ android { new ProjectTransformer( android, new MasqueradeUITransformer('com.instructure.teacher.activities.LoginActivity.class'), + new PageViewTransformer(), new LocaleTransformer(project), new ScreenViewTransformer("teacher") ) diff --git a/apps/teacher/src/androidTest/java/com/instructure/teacher/ui/InboxPageTest.kt b/apps/teacher/src/androidTest/java/com/instructure/teacher/ui/InboxPageTest.kt index 19843d35f1..2684b3a971 100644 --- a/apps/teacher/src/androidTest/java/com/instructure/teacher/ui/InboxPageTest.kt +++ b/apps/teacher/src/androidTest/java/com/instructure/teacher/ui/InboxPageTest.kt @@ -18,7 +18,11 @@ package com.instructure.teacher.ui import androidx.test.espresso.matcher.ViewMatchers -import com.instructure.canvas.espresso.mockCanvas.* +import com.instructure.canvas.espresso.mockCanvas.MockCanvas +import com.instructure.canvas.espresso.mockCanvas.addConversation +import com.instructure.canvas.espresso.mockCanvas.addConversations +import com.instructure.canvas.espresso.mockCanvas.createBasicConversation +import com.instructure.canvas.espresso.mockCanvas.init import com.instructure.canvasapi2.models.Conversation import com.instructure.canvasapi2.models.User import com.instructure.panda_annotations.FeatureCategory @@ -79,7 +83,7 @@ class InboxPageTest: TeacherTest() { @TestMetaData(Priority.IMPORTANT, FeatureCategory.INBOX, TestCategory.INTERACTION) fun archiveMultipleConversations() { val data = createInitialData() - data.addConversations(userId = data.teachers.first().id, messageBody = "Short body") + val conversation1 = data.addConversation( senderId = data.students.first().id, receiverIds = listOf(data.teachers.first().id), @@ -90,6 +94,7 @@ class InboxPageTest: TeacherTest() { receiverIds = listOf(data.teachers.first().id), messageBody = "Body 2", messageSubject = "Subject 2") + data.addConversations(userId = data.teachers.first().id, messageBody = "Short body") navigateToInbox(data, data.teachers.first()) inboxPage.selectConversations(listOf(conversation1.subject!!, conversation2.subject!!)) diff --git a/apps/teacher/src/androidTest/java/com/instructure/teacher/ui/e2e/AssignmentE2ETest.kt b/apps/teacher/src/androidTest/java/com/instructure/teacher/ui/e2e/AssignmentE2ETest.kt index 4f51cf903d..65bec5e9a1 100644 --- a/apps/teacher/src/androidTest/java/com/instructure/teacher/ui/e2e/AssignmentE2ETest.kt +++ b/apps/teacher/src/androidTest/java/com/instructure/teacher/ui/e2e/AssignmentE2ETest.kt @@ -22,8 +22,10 @@ import com.instructure.canvas.espresso.E2E import com.instructure.dataseeding.api.AssignmentsApi import com.instructure.dataseeding.api.SubmissionsApi import com.instructure.dataseeding.model.AssignmentApiModel +import com.instructure.dataseeding.model.AttachmentApiModel import com.instructure.dataseeding.model.CanvasUserApiModel import com.instructure.dataseeding.model.CourseApiModel +import com.instructure.dataseeding.model.FileUploadType import com.instructure.dataseeding.model.GradingType import com.instructure.dataseeding.model.SubmissionType import com.instructure.dataseeding.util.days @@ -36,7 +38,12 @@ import com.instructure.panda_annotations.Priority import com.instructure.panda_annotations.TestCategory import com.instructure.panda_annotations.TestMetaData import com.instructure.teacher.R -import com.instructure.teacher.ui.utils.* +import com.instructure.teacher.ui.utils.TeacherTest +import com.instructure.teacher.ui.utils.seedAssignmentSubmission +import com.instructure.teacher.ui.utils.seedAssignments +import com.instructure.teacher.ui.utils.seedData +import com.instructure.teacher.ui.utils.tokenLogin +import com.instructure.teacher.ui.utils.uploadTextFile import dagger.hilt.android.testing.HiltAndroidTest import org.junit.Test @@ -285,6 +292,61 @@ class AssignmentE2ETest : TeacherTest() { } + @E2E + @Test + @TestMetaData(Priority.IMPORTANT, FeatureCategory.COMMENTS, TestCategory.E2E) + fun testAddFileCommentE2E() { + + Log.d(PREPARATION_TAG, "Seeding data.") + val data = seedData(students = 1, teachers = 1, courses = 1) + val student = data.studentsList[0] + val teacher = data.teachersList[0] + val course = data.coursesList[0] + + Log.d(PREPARATION_TAG, "Seed a text assignment/file/submission.") + val assignment = createAssignment(course, teacher) + + Log.d(PREPARATION_TAG, "Seed a text file.") + val submissionUploadInfo = uploadTextFile( + assignmentId = assignment.id, + courseId = course.id, + token = student.token, + fileUploadType = FileUploadType.ASSIGNMENT_SUBMISSION + ) + + Log.d(PREPARATION_TAG, "Submit the ${assignment.name} assignment.") + submitCourseAssignment(course, assignment, submissionUploadInfo, student) + + Log.d(PREPARATION_TAG,"Seed a comment attachment upload.") + val commentUploadInfo = uploadTextFile( + assignmentId = assignment.id, + courseId = course.id, + token = student.token, + fileUploadType = FileUploadType.COMMENT_ATTACHMENT + ) + + commentOnSubmission(student, course, assignment, commentUploadInfo) + + Log.d(STEP_TAG, "Login with user: ${teacher.name}, login id: ${teacher.loginId}.") + tokenLogin(teacher) + dashboardPage.waitForRender() + + Log.d(STEP_TAG,"Select ${course.name} course and navigate to it's Assignments Page.") + dashboardPage.selectCourse(course) + courseBrowserPage.openAssignmentsTab() + + Log.d(STEP_TAG,"Click on ${assignment.name} assignment and navigate to Submissions Page.") + assignmentListPage.clickAssignment(assignment) + assignmentDetailsPage.openSubmissionsPage() + + Log.d(STEP_TAG,"Click on ${student.name} student's submission.") + assignmentSubmissionListPage.clickSubmission(student) + + Log.d(STEP_TAG,"Assert that ${submissionUploadInfo.fileName} file. Navigate to Comments Tab and ${commentUploadInfo.fileName} comment attachment is displayed.") + speedGraderPage.selectCommentsTab() + assignmentSubmissionListPage.assertCommentAttachmentDisplayedCommon(commentUploadInfo.fileName, student.shortName) + } + private fun gradeSubmission( teacher: CanvasUserApiModel, course: CourseApiModel, @@ -301,4 +363,48 @@ class AssignmentE2ETest : TeacherTest() { ) } + private fun createAssignment( + course: CourseApiModel, + teacher: CanvasUserApiModel + ): AssignmentApiModel { + return AssignmentsApi.createAssignment( + AssignmentsApi.CreateAssignmentRequest( + courseId = course.id, + withDescription = false, + submissionTypes = listOf(SubmissionType.ONLINE_UPLOAD), + allowedExtensions = listOf("txt"), + teacherToken = teacher.token + ) + ) + } + + private fun submitCourseAssignment( + course: CourseApiModel, + assignment: AssignmentApiModel, + submissionUploadInfo: AttachmentApiModel, + student: CanvasUserApiModel + ) { + SubmissionsApi.submitCourseAssignment( + submissionType = SubmissionType.ONLINE_UPLOAD, + courseId = course.id, + assignmentId = assignment.id, + fileIds = mutableListOf(submissionUploadInfo.id), + studentToken = student.token + ) + } + + private fun commentOnSubmission( + student: CanvasUserApiModel, + course: CourseApiModel, + assignment: AssignmentApiModel, + commentUploadInfo: AttachmentApiModel + ) { + SubmissionsApi.commentOnSubmission( + studentToken = student.token, + courseId = course.id, + assignmentId = assignment.id, + fileIds = mutableListOf(commentUploadInfo.id) + ) + } + } \ No newline at end of file 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 72bf7ca58c..d1e7633b04 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 @@ -155,7 +155,7 @@ class DashboardE2ETest : TeacherTest() { dashboardPage.waitForRender() Log.d(STEP_TAG, "Open Help Menu.") - dashboardPage.openHelpMenu() + leftSideNavigationDrawerPage.clickHelpMenu() Log.d(STEP_TAG, "Assert Help Menu Dialog is displayed.") helpPage.assertHelpMenuDisplayed() 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 e5450a8385..afd8f76eec 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 @@ -29,7 +29,13 @@ import com.instructure.canvasapi2.utils.weave.tryWeave import com.instructure.dataseeding.api.AssignmentsApi import com.instructure.dataseeding.api.DiscussionTopicsApi import com.instructure.dataseeding.api.SubmissionsApi -import com.instructure.dataseeding.model.* +import com.instructure.dataseeding.model.AssignmentApiModel +import com.instructure.dataseeding.model.AttachmentApiModel +import com.instructure.dataseeding.model.CanvasUserApiModel +import com.instructure.dataseeding.model.CourseApiModel +import com.instructure.dataseeding.model.DiscussionApiModel +import com.instructure.dataseeding.model.FileUploadType +import com.instructure.dataseeding.model.SubmissionType import com.instructure.dataseeding.util.Randomizer import com.instructure.espresso.ViewUtils import com.instructure.panda_annotations.FeatureCategory @@ -120,8 +126,8 @@ class FilesE2ETest: TeacherTest() { Log.v(PREPARATION_TAG, "Discussion post error: $it") } - Log.d(STEP_TAG,"Navigate to 'Files' menu in user left-side menubar.") - dashboardPage.gotoGlobalFiles() + Log.d(STEP_TAG,"Navigate to 'Files' menu in user left-side menu.") + leftSideNavigationDrawerPage.clickFilesMenu() Log.d(STEP_TAG,"Assert that there is a directory called 'unfiled' is displayed.") fileListPage.assertItemDisplayed("unfiled") // Our discussion attachment goes under "unfiled" @@ -153,8 +159,8 @@ class FilesE2ETest: TeacherTest() { Log.d(STEP_TAG,"Navigate back to Dashboard Page.") ViewUtils.pressBackButton(5) - Log.d(STEP_TAG,"Navigate to 'Files' menu in user left-side menubar.") - dashboardPage.gotoGlobalFiles() + Log.d(STEP_TAG,"Navigate to 'Files' menu in user left-side menu.") + leftSideNavigationDrawerPage.clickFilesMenu() Log.d(STEP_TAG,"Assert that there is a directory called 'unfiled' is displayed.") fileListPage.assertItemDisplayed("unfiled") 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 5233e60679..640a310972 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 @@ -52,22 +52,22 @@ class LoginE2ETest : TeacherTest() { loginWithUser(teacher1) Log.d(STEP_TAG,"Assert that the Dashboard Page is the landing page and it is loaded successfully.") - verifyDashboardPage(teacher1) + assertSuccessfulLogin(teacher1) Log.d(STEP_TAG,"Validate ${teacher1.name} user's role as a Teacher.") validateUserRole(teacher1, course, "Teacher") Log.d(STEP_TAG,"Log out with ${teacher1.name} student.") - dashboardPage.logOut() + leftSideNavigationDrawerPage.logout() Log.d(STEP_TAG, "Login with user: ${teacher2.name}, login id: ${teacher2.loginId}.") loginWithUser(teacher2, true) Log.d(STEP_TAG,"Assert that the Dashboard Page is the landing page and it is loaded successfully.") - verifyDashboardPage(teacher2) + assertSuccessfulLogin(teacher2) Log.d(STEP_TAG,"Click on 'Change User' button on the left-side menu.") - dashboardPage.pressChangeUser() + leftSideNavigationDrawerPage.clickChangeUserMenu() Log.d(STEP_TAG,"Assert that the previously logins has been displayed.") loginLandingPage.assertDisplaysPreviousLogins() @@ -76,10 +76,10 @@ class LoginE2ETest : TeacherTest() { loginWithUser(teacher1, true) Log.d(STEP_TAG,"Assert that the Dashboard Page is the landing page and it is loaded successfully.") - verifyDashboardPage(teacher1) + assertSuccessfulLogin(teacher1) Log.d(STEP_TAG,"Click on 'Change User' button on the left-side menu.") - dashboardPage.pressChangeUser() + leftSideNavigationDrawerPage.clickChangeUserMenu() Log.d(STEP_TAG,"Assert that the previously logins has been displayed.") loginLandingPage.assertDisplaysPreviousLogins() @@ -88,7 +88,7 @@ class LoginE2ETest : TeacherTest() { loginLandingPage.loginWithPreviousUser(teacher2) Log.d(STEP_TAG,"Assert that the Dashboard Page is the landing page and it is loaded successfully.") - verifyDashboardPage(teacher2) + assertSuccessfulLogin(teacher2) } @E2E @@ -145,17 +145,16 @@ class LoginE2ETest : TeacherTest() { loginWithUser(teacher1) Log.d(STEP_TAG, "Assert that the Dashboard Page is the landing page and it is loaded successfully.") - verifyDashboardPage(teacher1) + assertSuccessfulLogin(teacher1) Log.d(STEP_TAG, "Log out with ${teacher1.name} student.") - dashboardPage.logOut() + leftSideNavigationDrawerPage.logout() Log.d(STEP_TAG, "Login with user: ${teacher2.name}, login id: ${teacher2.loginId}, via the last saved school's button.") loginWithLastSavedSchool(teacher2) Log.d(STEP_TAG, "Assert that the Dashboard Page is the landing page and it is loaded successfully.") - verifyDashboardPage(teacher2) - + assertSuccessfulLogin(teacher2) } private fun loginWithUser(user: CanvasUserApiModel, lastSchoolSaved: Boolean = false) { @@ -201,10 +200,10 @@ class LoginE2ETest : TeacherTest() { ViewUtils.pressBackButton(2) } - private fun verifyDashboardPage(user: CanvasUserApiModel) + private fun assertSuccessfulLogin(user: CanvasUserApiModel) { dashboardPage.waitForRender() - dashboardPage.assertUserLoggedIn(user) + leftSideNavigationDrawerPage.assertUserLoggedIn(user) dashboardPage.assertDisplaysCourses() dashboardPage.assertPageObjects() } 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 aa824c9647..ff35dc9f9d 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 @@ -53,7 +53,7 @@ class SettingsE2ETest : TeacherTest() { dashboardPage.waitForRender() Log.d(STEP_TAG, "Navigate to User Settings Page.") - dashboardPage.openUserSettingsPage() + leftSideNavigationDrawerPage.clickSettingsMenu() settingsPage.assertPageObjects() Log.d(STEP_TAG, "Open Profile Settings Page.") @@ -119,7 +119,7 @@ class SettingsE2ETest : TeacherTest() { dashboardPage.waitForRender() Log.d(STEP_TAG, "Navigate to User Settings Page.") - dashboardPage.openUserSettingsPage() + leftSideNavigationDrawerPage.clickSettingsMenu() settingsPage.assertPageObjects() Log.d(STEP_TAG,"Navigate to Settings Page and open App Theme Settings.") @@ -139,9 +139,9 @@ class SettingsE2ETest : TeacherTest() { courseBrowserPage.assertTabLabelTextColor("Announcements","#FFFFFFFF") courseBrowserPage.assertTabLabelTextColor("Assignments","#FFFFFFFF") - Log.d(STEP_TAG,"Navigate to Settins Page and open App Theme Settings again.") + Log.d(STEP_TAG,"Navigate to Settings Page and open App Theme Settings again.") Espresso.pressBack() - dashboardPage.openUserSettingsPage() + leftSideNavigationDrawerPage.clickSettingsMenu() settingsPage.openAppThemeSettings() Log.d(STEP_TAG,"Select Light App Theme and assert that the App Theme Title and Status has the proper text color (which is used in Light mode).") @@ -168,7 +168,7 @@ class SettingsE2ETest : TeacherTest() { dashboardPage.waitForRender() Log.d(STEP_TAG,"Navigate to User Settings Page.") - dashboardPage.openUserSettingsPage() + leftSideNavigationDrawerPage.clickSettingsMenu() settingsPage.assertPageObjects() Log.d(STEP_TAG,"Open Legal Page and assert that all the corresponding buttons are displayed.") @@ -176,6 +176,40 @@ class SettingsE2ETest : TeacherTest() { legalPage.assertPageObjects() } + @E2E + @Test + @TestMetaData(Priority.IMPORTANT, FeatureCategory.SETTINGS, TestCategory.E2E) + fun testAboutE2E() { + + Log.d(PREPARATION_TAG, "Seeding data.") + 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}.") + tokenLogin(teacher) + dashboardPage.waitForRender() + + Log.d(STEP_TAG, "Navigate to Settings Page on the left-side menu.") + leftSideNavigationDrawerPage.clickSettingsMenu() + settingsPage.assertPageObjects() + + Log.d(STEP_TAG, "Click on 'About' link to open About Page. Assert that About Page has opened.") + settingsPage.openAboutPage() + aboutPage.assertPageObjects() + + Log.d(STEP_TAG,"Check that domain is equal to: ${teacher.domain} (teacher's domain).") + aboutPage.domainIs(teacher.domain) + + Log.d(STEP_TAG,"Check that Login ID is equal to: ${teacher.loginId} (teacher's Login ID).") + aboutPage.loginIdIs(teacher.loginId) + + Log.d(STEP_TAG,"Check that e-mail is equal to: ${teacher.loginId} (teacher's Login ID).") + aboutPage.emailIs(teacher.loginId) + + Log.d(STEP_TAG,"Assert that the Instructure company logo has been displayed on the About page.") + aboutPage.assertInstructureLogoDisplayed() + } + @E2E @Test @TestMetaData(Priority.MANDATORY, FeatureCategory.SETTINGS, TestCategory.E2E) @@ -190,7 +224,7 @@ class SettingsE2ETest : TeacherTest() { dashboardPage.waitForRender() Log.d(STEP_TAG,"Navigate to User Settings Page.") - dashboardPage.openUserSettingsPage() + leftSideNavigationDrawerPage.clickSettingsMenu() settingsPage.assertPageObjects() Log.d(STEP_TAG,"Open Legal Page and assert that all the corresponding buttons are displayed.") @@ -215,7 +249,7 @@ class SettingsE2ETest : TeacherTest() { dashboardPage.waitForRender() Log.d(STEP_TAG,"Navigate to User Settings Page.") - dashboardPage.openUserSettingsPage() + leftSideNavigationDrawerPage.clickSettingsMenu() Log.d(PREPARATION_TAG,"Capture the initial remote config values.") val initialValues = mutableMapOf() diff --git a/apps/teacher/src/androidTest/java/com/instructure/teacher/ui/pages/AboutPage.kt b/apps/teacher/src/androidTest/java/com/instructure/teacher/ui/pages/AboutPage.kt new file mode 100644 index 0000000000..8a0827370b --- /dev/null +++ b/apps/teacher/src/androidTest/java/com/instructure/teacher/ui/pages/AboutPage.kt @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2023 - 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.teacher.ui.pages + +import androidx.test.espresso.Espresso.onView +import androidx.test.espresso.matcher.ViewMatchers.withId +import com.instructure.espresso.OnViewWithText +import com.instructure.espresso.assertDisplayed +import com.instructure.espresso.page.BasePage +import com.instructure.espresso.page.plus +import com.instructure.espresso.page.withText +import com.instructure.espresso.scrollTo +import com.instructure.teacher.R + +class AboutPage : BasePage(R.id.aboutPage) { + + private val domainLabel by OnViewWithText(R.string.domain) + private val loginIdLabel by OnViewWithText(R.string.loginId) + private val emailLabel by OnViewWithText(R.string.email) + + fun domainIs(domain: String) { + onView(withId(R.id.domain) + withText(domain)).assertDisplayed() + + } + + fun loginIdIs(loginId: String) { + onView(withId(R.id.loginId) + withText(loginId)).assertDisplayed() + } + + fun emailIs(email: String) { + onView(withId(R.id.email) + withText(email)).assertDisplayed() + } + + fun assertInstructureLogoDisplayed() { + onView(withId(R.id.instructureLogo)).scrollTo().assertDisplayed() + } +} \ No newline at end of file diff --git a/apps/teacher/src/androidTest/java/com/instructure/teacher/ui/pages/DashboardPage.kt b/apps/teacher/src/androidTest/java/com/instructure/teacher/ui/pages/DashboardPage.kt index 22b030fad1..a1aff9f7cc 100644 --- a/apps/teacher/src/androidTest/java/com/instructure/teacher/ui/pages/DashboardPage.kt +++ b/apps/teacher/src/androidTest/java/com/instructure/teacher/ui/pages/DashboardPage.kt @@ -17,19 +17,31 @@ package com.instructure.teacher.ui.pages import android.view.View -import androidx.test.espresso.Espresso import androidx.test.espresso.assertion.ViewAssertions.doesNotExist import androidx.test.espresso.assertion.ViewAssertions.matches -import androidx.test.espresso.matcher.ViewMatchers import androidx.test.espresso.matcher.ViewMatchers.isDisplayed import androidx.test.espresso.matcher.ViewMatchers.withContentDescription -import com.instructure.canvas.espresso.waitForMatcherWithSleeps import com.instructure.canvasapi2.models.Course -import com.instructure.canvasapi2.models.User -import com.instructure.dataseeding.model.CanvasUserApiModel import com.instructure.dataseeding.model.CourseApiModel -import com.instructure.espresso.* -import com.instructure.espresso.page.* +import com.instructure.espresso.OnViewWithId +import com.instructure.espresso.TextViewColorAssertion +import com.instructure.espresso.WaitForViewWithId +import com.instructure.espresso.WaitForViewWithText +import com.instructure.espresso.assertContainsText +import com.instructure.espresso.assertDisplayed +import com.instructure.espresso.assertNotDisplayed +import com.instructure.espresso.click +import com.instructure.espresso.page.BasePage +import com.instructure.espresso.page.onView +import com.instructure.espresso.page.plus +import com.instructure.espresso.page.waitForView +import com.instructure.espresso.page.withAncestor +import com.instructure.espresso.page.withDescendant +import com.instructure.espresso.page.withId +import com.instructure.espresso.page.withParent +import com.instructure.espresso.page.withText +import com.instructure.espresso.replaceText +import com.instructure.espresso.waitForCheck import com.instructure.teacher.R import org.hamcrest.CoreMatchers.allOf import org.hamcrest.Matcher @@ -136,52 +148,10 @@ class DashboardPage : BasePage() { todoTab.click() } - fun openUserSettingsPage() { - onView(hamburgerButtonMatcher).click() - onViewWithId(R.id.navigationDrawerSettings).click() - } - - fun gotoGlobalFiles() { - onView(hamburgerButtonMatcher).click() - onViewWithId(R.id.navigationDrawerItem_files).click() - } - fun assertCourseLabelTextColor(expectedTextColor: String) { onView(withId(R.id.courseLabel)).check(TextViewColorAssertion(expectedTextColor)) } - fun logOut() { - onView(hamburgerButtonMatcher).click() - onViewWithId(R.id.navigationDrawerItem_logout).scrollTo().click() - onViewWithText(android.R.string.yes).click() - // It can potentially take a long time for the sign-out to take effect, especially on - // slow FTL devices. So let's pause for a bit until we see the canvas logo. - waitForMatcherWithSleeps(ViewMatchers.withId(R.id.canvasLogo), 10000).check(matches(isDisplayed())) - } - - fun assertUserLoggedIn(user: CanvasUserApiModel) { - onView(hamburgerButtonMatcher).click() - onViewWithText(user.shortName).assertDisplayed() - Espresso.pressBack() - } - - fun assertUserLoggedIn(user: User) { - onView(hamburgerButtonMatcher).click() - onViewWithText(user.shortName!!).assertDisplayed() - Espresso.pressBack() - } - - fun assertUserLoggedIn(userName: String) { - onView(hamburgerButtonMatcher).click() - onViewWithText(userName).assertDisplayed() - Espresso.pressBack() - } - - fun pressChangeUser() { - onView(hamburgerButtonMatcher).click() - onViewWithId(R.id.navigationDrawerItem_changeUser).scrollTo().click() - } - fun selectCourse(course: CourseApiModel) { assertDisplaysCourse(course) onView(withText(course.name)).click() @@ -202,8 +172,4 @@ class DashboardPage : BasePage() { onView(withText(R.string.ok) + withAncestor(R.id.buttonPanel)).click() } - fun openHelpMenu() { - onView(hamburgerButtonMatcher).click() - onViewWithId(R.id.navigationDrawerItem_help).scrollTo().click() - } } \ No newline at end of file diff --git a/apps/teacher/src/androidTest/java/com/instructure/teacher/ui/pages/InboxPage.kt b/apps/teacher/src/androidTest/java/com/instructure/teacher/ui/pages/InboxPage.kt index a3bd1bd3bc..1317ba4bef 100644 --- a/apps/teacher/src/androidTest/java/com/instructure/teacher/ui/pages/InboxPage.kt +++ b/apps/teacher/src/androidTest/java/com/instructure/teacher/ui/pages/InboxPage.kt @@ -12,8 +12,25 @@ import com.instructure.canvas.espresso.waitForMatcherWithRefreshes import com.instructure.canvas.espresso.withCustomConstraints import com.instructure.canvasapi2.models.Conversation import com.instructure.dataseeding.model.ConversationApiModel -import com.instructure.espresso.* -import com.instructure.espresso.page.* +import com.instructure.espresso.OnViewWithId +import com.instructure.espresso.RecyclerViewItemCountGreaterThanAssertion +import com.instructure.espresso.WaitForViewWithId +import com.instructure.espresso.assertDisplayed +import com.instructure.espresso.assertVisibility +import com.instructure.espresso.click +import com.instructure.espresso.longClick +import com.instructure.espresso.page.BasePage +import com.instructure.espresso.page.onView +import com.instructure.espresso.page.plus +import com.instructure.espresso.page.waitForView +import com.instructure.espresso.page.waitForViewWithId +import com.instructure.espresso.page.waitForViewWithText +import com.instructure.espresso.page.withAncestor +import com.instructure.espresso.page.withId +import com.instructure.espresso.page.withText +import com.instructure.espresso.scrollTo +import com.instructure.espresso.swipeLeft +import com.instructure.espresso.swipeRight import com.instructure.teacher.R import com.instructure.teacher.ui.utils.WaitForToolbarTitle import org.hamcrest.Matchers @@ -69,7 +86,6 @@ class InboxPage: BasePage() { } fun filterInbox(filterFor: String) { - refresh() waitForView(withId(R.id.scopeFilterText)) onView(withId(R.id.scopeFilter)).click() waitForViewWithText(filterFor).click() @@ -212,7 +228,6 @@ class InboxPage: BasePage() { } fun selectConversations(conversations: List) { - refresh() for(conversation in conversations) { selectConversation(conversation) } diff --git a/apps/teacher/src/androidTest/java/com/instructure/teacher/ui/pages/LeftSideNavigationDrawerPage.kt b/apps/teacher/src/androidTest/java/com/instructure/teacher/ui/pages/LeftSideNavigationDrawerPage.kt new file mode 100644 index 0000000000..fdb44c0cd2 --- /dev/null +++ b/apps/teacher/src/androidTest/java/com/instructure/teacher/ui/pages/LeftSideNavigationDrawerPage.kt @@ -0,0 +1,105 @@ +package com.instructure.teacher.ui.pages + +import android.view.View +import androidx.appcompat.widget.SwitchCompat +import androidx.test.espresso.Espresso +import androidx.test.espresso.UiController +import androidx.test.espresso.ViewAction +import androidx.test.espresso.assertion.ViewAssertions.matches +import androidx.test.espresso.matcher.ViewMatchers +import com.instructure.canvas.espresso.waitForMatcherWithSleeps +import com.instructure.dataseeding.model.CanvasUserApiModel +import com.instructure.espresso.OnViewWithContentDescription +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.onView +import com.instructure.espresso.page.onViewWithId +import com.instructure.espresso.page.onViewWithText +import com.instructure.espresso.scrollTo +import com.instructure.teacher.R +import org.hamcrest.CoreMatchers +import org.hamcrest.Matcher + +class LeftSideNavigationDrawerPage: BasePage() { + + private val userName by OnViewWithId(R.id.navigationDrawerUserName) + private val userEmail by OnViewWithId(R.id.navigationDrawerUserEmail) + private val logoutButton by OnViewWithId(R.id.navigationDrawerItem_logout) + private val version by OnViewWithId(R.id.navigationDrawerVersion) + private val hamburgerButton by OnViewWithContentDescription(R.string.navigation_drawer_open) + + // Sometimes when we navigate back to the dashboard page, there can be several hamburger buttons + // in the UI stack. We want to choose the one that is displayed. + private val hamburgerButtonMatcher = CoreMatchers.allOf( + ViewMatchers.withContentDescription(R.string.navigation_drawer_open), + ViewMatchers.isDisplayed() + ) + + private fun clickMenu(menuId: Int) { + onView(hamburgerButtonMatcher).click() + onViewWithId(menuId).scrollTo().click() + } + + fun logout() { + onView(hamburgerButtonMatcher).click() + logoutButton.scrollTo().click() + onViewWithText(android.R.string.yes).click() + // It can potentially take a long time for the sign-out to take effect, especially on + // slow FTL devices. So let's pause for a bit until we see the canvas logo. + waitForMatcherWithSleeps(ViewMatchers.withId(R.id.canvasLogo), 20000).check(matches( + ViewMatchers.isDisplayed() + )) + } + + fun clickChangeUserMenu() { + clickMenu(R.id.navigationDrawerItem_changeUser) + } + + fun clickHelpMenu() { + clickMenu(R.id.navigationDrawerItem_help) + } + + fun clickFilesMenu() { + clickMenu(R.id.navigationDrawerItem_files) + } + + fun clickSettingsMenu() { + clickMenu(R.id.navigationDrawerSettings) + } + + fun setColorOverlay(colorOverlay: Boolean) { + hamburgerButton.click() + onViewWithId(R.id.navigationDrawerColorOverlaySwitch).perform(SetSwitchCompat(colorOverlay)) + Espresso.pressBack() + } + + fun assertUserLoggedIn(user: CanvasUserApiModel) { + onView(hamburgerButtonMatcher).click() + onViewWithText(user.shortName).assertDisplayed() + Espresso.pressBack() + } + + /** + * Custom ViewAction to set a SwitchCompat to the desired on/off position + * [position]: true -> "on", false -> "off" + */ + private class SetSwitchCompat(val position: Boolean) : ViewAction { + override fun getDescription(): String { + val desiredPosition = if(position) "On" else "Off" + return "Set SwitchCompat to $desiredPosition" + } + + override fun getConstraints(): Matcher { + return ViewMatchers.isAssignableFrom(SwitchCompat::class.java) + } + + override fun perform(uiController: UiController?, view: View?) { + val switch = view as SwitchCompat + if(switch != null) { + switch.isChecked = position + } + } + } +} diff --git a/apps/teacher/src/androidTest/java/com/instructure/teacher/ui/pages/SettingsPage.kt b/apps/teacher/src/androidTest/java/com/instructure/teacher/ui/pages/SettingsPage.kt index 8cb53c1ad8..025ac01352 100644 --- a/apps/teacher/src/androidTest/java/com/instructure/teacher/ui/pages/SettingsPage.kt +++ b/apps/teacher/src/androidTest/java/com/instructure/teacher/ui/pages/SettingsPage.kt @@ -22,7 +22,11 @@ import androidx.test.espresso.matcher.ViewMatchers import com.instructure.espresso.OnViewWithId import com.instructure.espresso.TextViewColorAssertion import com.instructure.espresso.click -import com.instructure.espresso.page.* +import com.instructure.espresso.page.BasePage +import com.instructure.espresso.page.onView +import com.instructure.espresso.page.plus +import com.instructure.espresso.page.withParent +import com.instructure.espresso.page.withText import com.instructure.espresso.scrollTo import com.instructure.teacher.R @@ -32,6 +36,7 @@ class SettingsPage : BasePage(R.id.settingsPage) { private val pushNotificationsLabel by OnViewWithId(R.id.notificationPreferenesButton) private val rateAppLabel by OnViewWithId(R.id.rateButton) private val legalLabel by OnViewWithId(R.id.legalButton) + private val aboutLabel by OnViewWithId(R.id.aboutButton) private val featureFlagLabel by OnViewWithId(R.id.featureFlagButton) private val remoteConfigLabel by OnViewWithId(R.id.remoteConfigButton) private val appThemeTitle by OnViewWithId(R.id.appThemeTitle) @@ -53,6 +58,10 @@ class SettingsPage : BasePage(R.id.settingsPage) { legalLabel.scrollTo().click() } + fun openAboutPage() { + aboutLabel.scrollTo().click() + } + fun openFeatureFlagsPage() { featureFlagLabel.scrollTo().click() } diff --git a/apps/teacher/src/androidTest/java/com/instructure/teacher/ui/utils/TeacherTest.kt b/apps/teacher/src/androidTest/java/com/instructure/teacher/ui/utils/TeacherTest.kt index 3b190d5ac8..d80b8e4783 100644 --- a/apps/teacher/src/androidTest/java/com/instructure/teacher/ui/utils/TeacherTest.kt +++ b/apps/teacher/src/androidTest/java/com/instructure/teacher/ui/utils/TeacherTest.kt @@ -27,15 +27,67 @@ import com.instructure.canvas.espresso.CanvasTest import com.instructure.espresso.InstructureActivityTestRule import com.instructure.teacher.BuildConfig import com.instructure.teacher.activities.LoginActivity -import com.instructure.teacher.ui.espresso.TeacherHiltTestApplication import com.instructure.teacher.ui.espresso.TeacherHiltTestApplication_Application -import com.instructure.teacher.ui.pages.* +import com.instructure.teacher.ui.pages.AboutPage +import com.instructure.teacher.ui.pages.AddMessagePage +import com.instructure.teacher.ui.pages.AnnouncementsListPage +import com.instructure.teacher.ui.pages.AssigneeListPage +import com.instructure.teacher.ui.pages.AssignmentDetailsPage +import com.instructure.teacher.ui.pages.AssignmentDueDatesPage +import com.instructure.teacher.ui.pages.AssignmentListPage +import com.instructure.teacher.ui.pages.AssignmentSubmissionListPage +import com.instructure.teacher.ui.pages.CalendarEventPage +import com.instructure.teacher.ui.pages.ChooseRecipientsPage +import com.instructure.teacher.ui.pages.CommentLibraryPage +import com.instructure.teacher.ui.pages.CourseBrowserPage +import com.instructure.teacher.ui.pages.CourseSettingsPage +import com.instructure.teacher.ui.pages.DashboardPage +import com.instructure.teacher.ui.pages.DiscussionsDetailsPage +import com.instructure.teacher.ui.pages.DiscussionsListPage +import com.instructure.teacher.ui.pages.EditAnnouncementPage +import com.instructure.teacher.ui.pages.EditAssignmentDetailsPage +import com.instructure.teacher.ui.pages.EditDashboardPage +import com.instructure.teacher.ui.pages.EditDiscussionsDetailsPage +import com.instructure.teacher.ui.pages.EditPageDetailsPage +import com.instructure.teacher.ui.pages.EditProfileSettingsPage +import com.instructure.teacher.ui.pages.EditQuizDetailsPage +import com.instructure.teacher.ui.pages.EditSyllabusPage +import com.instructure.teacher.ui.pages.FileListPage +import com.instructure.teacher.ui.pages.HelpPage +import com.instructure.teacher.ui.pages.InboxMessagePage +import com.instructure.teacher.ui.pages.InboxPage +import com.instructure.teacher.ui.pages.LeftSideNavigationDrawerPage +import com.instructure.teacher.ui.pages.LegalPage +import com.instructure.teacher.ui.pages.LoginFindSchoolPage +import com.instructure.teacher.ui.pages.LoginLandingPage +import com.instructure.teacher.ui.pages.LoginSignInPage +import com.instructure.teacher.ui.pages.ModulesPage +import com.instructure.teacher.ui.pages.NavDrawerPage +import com.instructure.teacher.ui.pages.NotATeacherPage +import com.instructure.teacher.ui.pages.PageListPage +import com.instructure.teacher.ui.pages.PeopleListPage +import com.instructure.teacher.ui.pages.PersonContextPage +import com.instructure.teacher.ui.pages.PostSettingsPage +import com.instructure.teacher.ui.pages.ProfileSettingsPage +import com.instructure.teacher.ui.pages.QuizDetailsPage +import com.instructure.teacher.ui.pages.QuizListPage +import com.instructure.teacher.ui.pages.QuizSubmissionListPage +import com.instructure.teacher.ui.pages.RemoteConfigSettingsPage +import com.instructure.teacher.ui.pages.SettingsPage +import com.instructure.teacher.ui.pages.SpeedGraderCommentsPage +import com.instructure.teacher.ui.pages.SpeedGraderFilesPage +import com.instructure.teacher.ui.pages.SpeedGraderGradePage +import com.instructure.teacher.ui.pages.SpeedGraderPage +import com.instructure.teacher.ui.pages.SpeedGraderQuizSubmissionPage +import com.instructure.teacher.ui.pages.StudentContextPage +import com.instructure.teacher.ui.pages.SyllabusPage +import com.instructure.teacher.ui.pages.TodoPage +import com.instructure.teacher.ui.pages.WebViewLoginPage import dagger.hilt.android.testing.HiltAndroidRule import instructure.rceditor.RCETextEditor import org.hamcrest.Matcher import org.junit.Before import org.junit.Rule -import java.lang.IllegalStateException import javax.inject.Inject abstract class TeacherTest : CanvasTest() { @@ -82,10 +134,12 @@ abstract class TeacherTest : CanvasTest() { val courseBrowserPage = CourseBrowserPage() val courseSettingsPage = CourseSettingsPage() val dashboardPage = DashboardPage() + val leftSideNavigationDrawerPage = LeftSideNavigationDrawerPage() val editDashboardPage = EditDashboardPage() val settingsPage = SettingsPage() val legalPage = LegalPage() val helpPage = HelpPage() + val aboutPage = AboutPage() val remoteConfigSettingsPage = RemoteConfigSettingsPage() val profileSettingsPage = ProfileSettingsPage() val editProfileSettingsPage = EditProfileSettingsPage() diff --git a/apps/teacher/src/main/AndroidManifest.xml b/apps/teacher/src/main/AndroidManifest.xml index 6c80bd89e7..8ce6e5605d 100644 --- a/apps/teacher/src/main/AndroidManifest.xml +++ b/apps/teacher/src/main/AndroidManifest.xml @@ -228,6 +228,11 @@ + + { + PandataManager.getToken(TeacherPageViewService.pandataAppKey, it) + } + } catch (ignore: Throwable) { + Logger.w("Unable to refresh pandata info") + } + } + } + requestNotificationsPermission() } @@ -200,9 +217,13 @@ class InitActivity : BasePresenterActivity { ThemeManager.getTheme(it, false) } - ThemePrefs.applyCanvasTheme(theme, this@InitActivity) - binding.bottomBar.applyTheme(ThemePrefs.brandColor, getColor(R.color.textDarkest)) + try { + val theme = awaitApi { ThemeManager.getTheme(it, false) } + ThemePrefs.applyCanvasTheme(theme, this@InitActivity) + binding.bottomBar.applyTheme(ThemePrefs.brandColor, getColor(R.color.textDarkest)) + } catch(e: IOException) { + LoggingUtility.log(e.stackTrace.toString(), Log.WARN) + } } } @@ -450,6 +471,8 @@ class InitActivity : BasePresenterActivity 0) { - fm.popBackStackImmediate(fm.getBackStackEntryAt(0).id, FragmentManager.POP_BACK_STACK_INCLUSIVE) + if (clearBackStack) { + if (fm.backStackEntryCount > 0) { + fm.popBackStackImmediate(fm.getBackStackEntryAt(0).id, FragmentManager.POP_BACK_STACK_INCLUSIVE) + } } else { ft.addToBackStack(null) } @@ -574,7 +595,6 @@ class InitActivity : BasePresenterActivity(), SpeedGraderView { @@ -81,7 +84,9 @@ class SpeedGraderActivity : BasePresenterActivity by lazy { intent.extras!!.getParcelableArrayList(Const.SUBMISSION) ?: arrayListOf() } diff --git a/apps/teacher/src/main/java/com/instructure/teacher/adapters/StudentContextFragment.kt b/apps/teacher/src/main/java/com/instructure/teacher/adapters/StudentContextFragment.kt index 75b0303a2a..b30556b604 100644 --- a/apps/teacher/src/main/java/com/instructure/teacher/adapters/StudentContextFragment.kt +++ b/apps/teacher/src/main/java/com/instructure/teacher/adapters/StudentContextFragment.kt @@ -247,20 +247,23 @@ class StudentContextFragment : PresenterFragment 0)) { - triggered = true - presenter.loadMoreSubmissions() + override fun onScrollChanged() { + if (view == null) return // To prevent binding crashes is split view. + + with(binding) { + if (!isAdded || contentContainer.height == 0 || scrollContent.height == 0 || loadMoreContainer.height == 0) return + val threshold = scrollContent.height - loadMoreContainer.top + val bottomOffset = contentContainer.height + contentContainer.scrollY - scrollContent.bottom + if (scrollContent.height <= contentContainer.height) { + presenter.loadMoreSubmissions() + } else if (triggered && (threshold + touchSlop + bottomOffset < 0)) { + triggered = false + } else if (!triggered && (threshold + bottomOffset > 0)) { + triggered = true + presenter.loadMoreSubmissions() + } } } - } override fun addSubmissions(submissions: List, course: AsCourse, student: User) { diff --git a/apps/teacher/src/main/java/com/instructure/teacher/di/DatabaseModule.kt b/apps/teacher/src/main/java/com/instructure/teacher/di/DatabaseModule.kt index 4155b356c7..35a5330d6c 100644 --- a/apps/teacher/src/main/java/com/instructure/teacher/di/DatabaseModule.kt +++ b/apps/teacher/src/main/java/com/instructure/teacher/di/DatabaseModule.kt @@ -2,8 +2,8 @@ package com.instructure.teacher.di import android.content.Context import androidx.room.Room -import com.instructure.pandautils.room.AppDatabase -import com.instructure.pandautils.room.appDatabaseMigrations +import com.instructure.pandautils.room.appdatabase.AppDatabase +import com.instructure.pandautils.room.appdatabase.appDatabaseMigrations import dagger.Module import dagger.Provides import dagger.hilt.InstallIn diff --git a/apps/teacher/src/main/java/com/instructure/teacher/dialog/EditRubricCommentDialog.kt b/apps/teacher/src/main/java/com/instructure/teacher/dialog/EditRubricCommentDialog.kt index 2e38cf7f41..2e039e36c1 100644 --- a/apps/teacher/src/main/java/com/instructure/teacher/dialog/EditRubricCommentDialog.kt +++ b/apps/teacher/src/main/java/com/instructure/teacher/dialog/EditRubricCommentDialog.kt @@ -39,7 +39,7 @@ import org.greenrobot.eventbus.EventBus @ScreenView(SCREEN_VIEW_EDIT_RUBRIC_COMMENT) class EditRubricCommentDialog : AppCompatDialogFragment() { - private val binding by viewBinding(ViewEditGradeCommentBinding::bind) + private lateinit var binding: ViewEditGradeCommentBinding var mCriterionId by StringArg() var mStudentId by LongArg(-1L) @@ -74,7 +74,8 @@ class EditRubricCommentDialog : AppCompatDialogFragment() { } override fun onCreateDialog(savedInstanceState: Bundle?) = AppCompatDialog(requireContext(), R.style.Theme_AppCompat_DayNight_Translucent).apply { - setContentView(R.layout.view_edit_grade_comment) + binding = ViewEditGradeCommentBinding.inflate(layoutInflater, null, false) + setContentView(binding.root) // Send event bus on save, dismiss dialog. Send null if text is blank (i.e. delete comment) binding.saveCommentButton.onClick { diff --git a/apps/teacher/src/main/java/com/instructure/teacher/dialog/SectionPickerDialog.kt b/apps/teacher/src/main/java/com/instructure/teacher/dialog/SectionPickerDialog.kt index 705a2a1f56..e0f335dbcf 100644 --- a/apps/teacher/src/main/java/com/instructure/teacher/dialog/SectionPickerDialog.kt +++ b/apps/teacher/src/main/java/com/instructure/teacher/dialog/SectionPickerDialog.kt @@ -122,12 +122,10 @@ class SectionRecyclerViewAdapter( private val updatedCallback: (MutableList
) -> Unit ) : RecyclerView.Adapter() { - private lateinit var binding: ViewSectionListItemBinding - override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): SectionViewHolder { - binding = ViewSectionListItemBinding.inflate(LayoutInflater.from(parent.context), parent, false) + val binding = ViewSectionListItemBinding.inflate(LayoutInflater.from(parent.context), parent, false) binding.checkbox.applyTheme(ThemePrefs.brandColor) - return SectionViewHolder(binding.root) + return SectionViewHolder(binding) } override fun getItemCount(): Int = sections.size @@ -164,7 +162,8 @@ class SectionRecyclerViewAdapter( } } - inner class SectionViewHolder(val view: View) : RecyclerView.ViewHolder(view) { + inner class SectionViewHolder(val binding: ViewSectionListItemBinding) : RecyclerView.ViewHolder(binding.root) { + fun bind(sectionName: String, checked: Boolean, callback: (Boolean) -> Unit) { binding.sectionName.text = sectionName @@ -174,7 +173,7 @@ class SectionRecyclerViewAdapter( } binding.checkbox.setOnClickListener { callback(binding.checkbox.isChecked); (it as CheckBox).toggle() } - view.setOnClickListener { binding.checkbox.performClick(); } + binding.root.setOnClickListener { binding.checkbox.performClick() } } } } diff --git a/apps/teacher/src/main/java/com/instructure/teacher/factory/SpeedGraderCommentsPresenterFactory.kt b/apps/teacher/src/main/java/com/instructure/teacher/factory/SpeedGraderCommentsPresenterFactory.kt index 3d8e6dc9a5..dd5969218c 100644 --- a/apps/teacher/src/main/java/com/instructure/teacher/factory/SpeedGraderCommentsPresenterFactory.kt +++ b/apps/teacher/src/main/java/com/instructure/teacher/factory/SpeedGraderCommentsPresenterFactory.kt @@ -19,26 +19,26 @@ package com.instructure.teacher.factory import com.instructure.canvasapi2.models.Assignee import com.instructure.canvasapi2.models.Submission import com.instructure.canvasapi2.models.SubmissionComment -import com.instructure.pandautils.room.daos.* +import com.instructure.pandautils.room.appdatabase.daos.* import com.instructure.teacher.presenters.SpeedGraderCommentsPresenter import com.instructure.teacher.viewinterface.SpeedGraderCommentsView import instructure.androidblueprint.PresenterFactory class SpeedGraderCommentsPresenterFactory( - val rawComments: java.util.ArrayList, - val submissionHistory: List, - val assignee: Assignee, - val courseId: Long, - val assignmentId: Long, - val groupMessage: Boolean, - val submissionCommentDao: SubmissionCommentDao, - val attachmentDao: AttachmentDao, - val authorDao: AuthorDao, - val mediaCommentDao: MediaCommentDao, - val pendingSubmissionCommentDao: PendingSubmissionCommentDao, - val fileUploadInputDao: FileUploadInputDao, - val selectedAttemptId: Long, - val assignmentEnhancementsEnabled: Boolean + val rawComments: java.util.ArrayList, + val submissionHistory: List, + val assignee: Assignee, + val courseId: Long, + val assignmentId: Long, + val groupMessage: Boolean, + val submissionCommentDao: SubmissionCommentDao, + val attachmentDao: AttachmentDao, + val authorDao: AuthorDao, + val mediaCommentDao: MediaCommentDao, + val pendingSubmissionCommentDao: PendingSubmissionCommentDao, + val fileUploadInputDao: FileUploadInputDao, + val selectedAttemptId: Long, + val assignmentEnhancementsEnabled: Boolean ) : PresenterFactory { override fun create() = SpeedGraderCommentsPresenter( rawComments, diff --git a/apps/teacher/src/main/java/com/instructure/teacher/features/calendar/event/CalendarEventFragment.kt b/apps/teacher/src/main/java/com/instructure/teacher/features/calendar/event/CalendarEventFragment.kt index 2d5c65ca8e..6a3cf13b4b 100644 --- a/apps/teacher/src/main/java/com/instructure/teacher/features/calendar/event/CalendarEventFragment.kt +++ b/apps/teacher/src/main/java/com/instructure/teacher/features/calendar/event/CalendarEventFragment.kt @@ -28,10 +28,7 @@ import com.instructure.pandautils.analytics.SCREEN_VIEW_CALENDAR_EVENT import com.instructure.pandautils.analytics.ScreenView import com.instructure.pandautils.binding.viewBinding import com.instructure.pandautils.fragments.BaseFragment -import com.instructure.pandautils.utils.NullableParcelableArg -import com.instructure.pandautils.utils.ViewStyler -import com.instructure.pandautils.utils.loadHtmlWithIframes -import com.instructure.pandautils.utils.setupAsBackButton +import com.instructure.pandautils.utils.* import com.instructure.pandautils.views.CanvasWebView import com.instructure.teacher.R import com.instructure.teacher.activities.InternalWebViewActivity @@ -45,6 +42,8 @@ class CalendarEventFragment : BaseFragment() { private val binding by viewBinding(FragmentCalendarEventBinding::bind) + var canvasContext: CanvasContext? by NullableParcelableArg(key = Const.CANVAS_CONTEXT) + private val transformer = CalendarEventStateTransformer() private var scheduleItem: ScheduleItem? by NullableParcelableArg(key = SCHEDULE_ITEM) diff --git a/apps/teacher/src/main/java/com/instructure/teacher/features/dashboard/notifications/TeacherDashboardRouter.kt b/apps/teacher/src/main/java/com/instructure/teacher/features/dashboard/notifications/TeacherDashboardRouter.kt index 944601cc82..e518b4414d 100644 --- a/apps/teacher/src/main/java/com/instructure/teacher/features/dashboard/notifications/TeacherDashboardRouter.kt +++ b/apps/teacher/src/main/java/com/instructure/teacher/features/dashboard/notifications/TeacherDashboardRouter.kt @@ -17,9 +17,11 @@ package com.instructure.teacher.features.dashboard.notifications import androidx.fragment.app.FragmentActivity +import com.instructure.canvasapi2.models.CanvasContext import com.instructure.interactions.router.Route import com.instructure.pandautils.features.dashboard.notifications.DashboardRouter import com.instructure.pandautils.fragments.HtmlContentFragment +import com.instructure.teacher.fragments.FileListFragment import com.instructure.teacher.router.RouteMatcher class TeacherDashboardRouter(private val activity: FragmentActivity) : DashboardRouter { @@ -28,4 +30,14 @@ class TeacherDashboardRouter(private val activity: FragmentActivity) : Dashboard val route = Route(HtmlContentFragment::class.java, null, args) RouteMatcher.route(activity, route) } + + override fun routeToSubmissionDetails(canvasContext: CanvasContext, assignmentId: Long, attemptId: Long) {} + + override fun routeToMyFiles(canvasContext: CanvasContext, folderId: Long) { + val args = FileListFragment.makeBundle(canvasContext) + RouteMatcher.route( + activity, + Route(FileListFragment::class.java, canvasContext, args) + ) + } } \ No newline at end of file diff --git a/apps/teacher/src/main/java/com/instructure/teacher/features/files/search/FileSearchFragment.kt b/apps/teacher/src/main/java/com/instructure/teacher/features/files/search/FileSearchFragment.kt index 70b90dbbe4..bdcad88de9 100644 --- a/apps/teacher/src/main/java/com/instructure/teacher/features/files/search/FileSearchFragment.kt +++ b/apps/teacher/src/main/java/com/instructure/teacher/features/files/search/FileSearchFragment.kt @@ -45,6 +45,8 @@ class FileSearchFragment : BaseSyncFragment< private val binding by viewBinding(FragmentFileSearchBinding::bind) + var canvasContext: CanvasContext? by NullableParcelableArg(key = Const.CANVAS_CONTEXT) + private val searchAdapter by lazy { FileSearchAdapter(requireContext(), canvasContext.textAndIconColor, presenter) { val editableFile = EditableFile(it, presenter.usageRights, presenter.licenses, canvasContext.backgroundColor, presenter.canvasContext, R.drawable.ic_document) diff --git a/apps/teacher/src/main/java/com/instructure/teacher/features/modules/list/ui/ModuleListFragment.kt b/apps/teacher/src/main/java/com/instructure/teacher/features/modules/list/ui/ModuleListFragment.kt index 5ab383cdce..4c4358f49b 100644 --- a/apps/teacher/src/main/java/com/instructure/teacher/features/modules/list/ui/ModuleListFragment.kt +++ b/apps/teacher/src/main/java/com/instructure/teacher/features/modules/list/ui/ModuleListFragment.kt @@ -20,6 +20,7 @@ import android.os.Bundle import android.view.LayoutInflater import android.view.ViewGroup import com.instructure.canvasapi2.models.CanvasContext +import com.instructure.canvasapi2.utils.pageview.PageView import com.instructure.pandautils.analytics.SCREEN_VIEW_MODULE_LIST import com.instructure.pandautils.analytics.ScreenView import com.instructure.pandautils.utils.Const @@ -31,11 +32,12 @@ import com.instructure.teacher.features.modules.list.* import com.instructure.teacher.mobius.common.ui.MobiusFragment import com.instructure.teacher.mobius.common.ui.Presenter +@PageView(url = "{canvasContext}/modules") @ScreenView(SCREEN_VIEW_MODULE_LIST) class ModuleListFragment : MobiusFragment() { - val course by ParcelableArg(key = Const.COURSE) + val canvasContext by ParcelableArg(key = Const.COURSE) private val scrollToItemId by NLongArg(key = Const.MODULE_ITEM_ID) @@ -43,11 +45,11 @@ class ModuleListFragment : MobiusFragment = ModuleListPresenter - override fun makeInitModel(): ModuleListModel = ModuleListModel(course = course, scrollToItemId = scrollToItemId) + override fun makeInitModel(): ModuleListModel = ModuleListModel(course = canvasContext, scrollToItemId = scrollToItemId) override val eventSources = listOf(ModuleListEventBusSource()) diff --git a/apps/teacher/src/main/java/com/instructure/teacher/features/syllabus/ui/SyllabusFragment.kt b/apps/teacher/src/main/java/com/instructure/teacher/features/syllabus/ui/SyllabusFragment.kt index dcc81914e3..9311533f37 100644 --- a/apps/teacher/src/main/java/com/instructure/teacher/features/syllabus/ui/SyllabusFragment.kt +++ b/apps/teacher/src/main/java/com/instructure/teacher/features/syllabus/ui/SyllabusFragment.kt @@ -20,6 +20,7 @@ import android.view.LayoutInflater import android.view.ViewGroup import com.instructure.canvasapi2.models.CanvasContext import com.instructure.canvasapi2.models.Course +import com.instructure.canvasapi2.utils.pageview.PageView import com.instructure.pandautils.analytics.SCREEN_VIEW_SYLLABUS import com.instructure.pandautils.analytics.ScreenView import com.instructure.pandautils.utils.Const @@ -29,6 +30,7 @@ import com.instructure.teacher.databinding.FragmentSyllabusBinding import com.instructure.teacher.features.syllabus.* import com.instructure.teacher.mobius.common.ui.MobiusFragment +@PageView("{canvasContext}/assignments/syllabus") @ScreenView(SCREEN_VIEW_SYLLABUS) class SyllabusFragment : MobiusFragment() { diff --git a/apps/teacher/src/main/java/com/instructure/teacher/fragments/AddMessageFragment.kt b/apps/teacher/src/main/java/com/instructure/teacher/fragments/AddMessageFragment.kt index c625c26264..a29ec17687 100644 --- a/apps/teacher/src/main/java/com/instructure/teacher/fragments/AddMessageFragment.kt +++ b/apps/teacher/src/main/java/com/instructure/teacher/fragments/AddMessageFragment.kt @@ -36,7 +36,7 @@ import com.instructure.pandautils.dialogs.UnsavedChangesExitDialog import com.instructure.pandautils.features.file.upload.FileUploadDialogFragment import com.instructure.pandautils.features.file.upload.FileUploadDialogParent import com.instructure.pandautils.fragments.BasePresenterFragment -import com.instructure.pandautils.room.daos.AttachmentDao +import com.instructure.pandautils.room.appdatabase.daos.AttachmentDao import com.instructure.pandautils.utils.* import com.instructure.pandautils.views.AttachmentView import com.instructure.teacher.R diff --git a/apps/teacher/src/main/java/com/instructure/teacher/fragments/AnnouncementListFragment.kt b/apps/teacher/src/main/java/com/instructure/teacher/fragments/AnnouncementListFragment.kt index ef7fc02c14..563596b6a7 100644 --- a/apps/teacher/src/main/java/com/instructure/teacher/fragments/AnnouncementListFragment.kt +++ b/apps/teacher/src/main/java/com/instructure/teacher/fragments/AnnouncementListFragment.kt @@ -17,16 +17,18 @@ package com.instructure.teacher.fragments import com.instructure.canvasapi2.models.CanvasContext +import com.instructure.canvasapi2.utils.pageview.PageView import com.instructure.pandautils.analytics.SCREEN_VIEW_ANNOUNCEMENT_LIST import com.instructure.pandautils.analytics.ScreenView +@PageView(url = "{canvasContext}/announcements") @ScreenView(SCREEN_VIEW_ANNOUNCEMENT_LIST) class AnnouncementListFragment : DiscussionsListFragment() { companion object { fun newInstance(canvasContext: CanvasContext) = AnnouncementListFragment().apply { - mCanvasContext = canvasContext - mIsAnnouncements = true + this.canvasContext = canvasContext + isAnnouncements = true } } } diff --git a/apps/teacher/src/main/java/com/instructure/teacher/fragments/AssignmentDetailsFragment.kt b/apps/teacher/src/main/java/com/instructure/teacher/fragments/AssignmentDetailsFragment.kt index e778402c77..c984050bbc 100644 --- a/apps/teacher/src/main/java/com/instructure/teacher/fragments/AssignmentDetailsFragment.kt +++ b/apps/teacher/src/main/java/com/instructure/teacher/fragments/AssignmentDetailsFragment.kt @@ -24,6 +24,8 @@ import com.instructure.canvasapi2.models.Assignment.Companion.submissionTypeToPr import com.instructure.canvasapi2.models.CanvasContext import com.instructure.canvasapi2.models.Course import com.instructure.canvasapi2.utils.* +import com.instructure.canvasapi2.utils.pageview.PageView +import com.instructure.canvasapi2.utils.pageview.PageViewUrl import com.instructure.interactions.Identity import com.instructure.interactions.MasterDetailInteractions import com.instructure.interactions.router.Route @@ -57,6 +59,7 @@ import org.greenrobot.eventbus.Subscribe import org.greenrobot.eventbus.ThreadMode import java.util.* +@PageView @ScreenView(SCREEN_VIEW_ASSIGNMENT_DETAILS) class AssignmentDetailsFragment : BasePresenterFragment< AssignmentDetailsPresenter, @@ -64,14 +67,18 @@ class AssignmentDetailsFragment : BasePresenterFragment< private val binding by viewBinding(FragmentAssignmentDetailsBinding::bind) - private var mAssignment: Assignment by ParcelableArg(Assignment(), ASSIGNMENT) - private var mCourse: Course by ParcelableArg(Course()) - private var mAssignmentId: Long by LongArg(0L, ASSIGNMENT_ID) + private var assignment: Assignment by ParcelableArg(Assignment(), ASSIGNMENT) + private var course: Course by ParcelableArg(Course()) + private var assignmentId: Long by LongArg(0L, ASSIGNMENT_ID) - private var mNeedToForceNetwork = false + private var needToForceNetwork = false private var loadHtmlJob: Job? = null + @Suppress("unused") + @PageViewUrl + private fun makePageViewUrl() = "${ApiPrefs.fullDomain}/${course.contextId.replace("_", "s/")}/${assignment.id}" + override fun layoutResId() = R.layout.fragment_assignment_details override fun onRefreshFinished() {} @@ -88,38 +95,38 @@ class AssignmentDetailsFragment : BasePresenterFragment< override fun onReadySetGo(presenter: AssignmentDetailsPresenter) { // if we don't have an assignmentId that means we have an assignment, so we can load the data - if(mAssignmentId == 0L) { - presenter.loadData(mNeedToForceNetwork) + if(assignmentId == 0L) { + presenter.loadData(needToForceNetwork) } else { - presenter.getAssignment(mAssignmentId, mCourse) + presenter.getAssignment(assignmentId, course) } } override fun populateAssignmentDetails(assignment: Assignment) = with(binding) { - mAssignment = assignment + this@AssignmentDetailsFragment.assignment = assignment toolbar.setupMenu(R.menu.menu_edit_generic) { openEditPage(assignment) } swipeRefreshLayout.isRefreshing = false setupViews(assignment) setupListeners(assignment) - ViewStyler.themeToolbarColored(requireActivity(), toolbar, mCourse.backgroundColor, requireContext().getColor(R.color.white)) + ViewStyler.themeToolbarColored(requireActivity(), toolbar, course.backgroundColor, requireContext().getColor(R.color.white)) } - override fun getPresenterFactory() = AssignmentDetailPresenterFactory(mAssignment) + override fun getPresenterFactory() = AssignmentDetailPresenterFactory(assignment) override fun onPresenterPrepared(presenter: AssignmentDetailsPresenter) {} private fun setupToolbar() = with(binding) { toolbar.setupBackButtonWithExpandCollapseAndBack(this@AssignmentDetailsFragment) { toolbar.updateToolbarExpandCollapseIcon(this@AssignmentDetailsFragment) - ViewStyler.themeToolbarColored(requireActivity(), toolbar, mCourse.backgroundColor, requireContext().getColor(R.color.white)) + ViewStyler.themeToolbarColored(requireActivity(), toolbar, course.backgroundColor, requireContext().getColor(R.color.white)) (activity as MasterDetailInteractions).toggleExpandCollapse() } toolbar.title = getString(R.string.assignment_details) if(!isTablet) { - toolbar.subtitle = mCourse.name + toolbar.subtitle = course.name } - ViewStyler.themeToolbarColored(requireActivity(), toolbar, mCourse.backgroundColor, requireContext().getColor(R.color.white)) + ViewStyler.themeToolbarColored(requireActivity(), toolbar, course.backgroundColor, requireContext().getColor(R.color.white)) } private fun setupViews(assignment: Assignment) = with(binding) { @@ -229,19 +236,19 @@ class AssignmentDetailsFragment : BasePresenterFragment< submissionTypesArrowIcon.setVisible() submissionTypesLayout.onClickWithRequireNetwork { // If the user is a designer we don't want to let them look at LTI tools - if (mCourse.isDesigner) { + if (course.isDesigner) { toast(R.string.errorIsDesigner) return@onClickWithRequireNetwork } val ltiUrl = assignment.url.validOrNull() ?: assignment.htmlUrl if(!ltiUrl.isNullOrBlank()) { - val args = LtiLaunchFragment.makeBundle(mCourse, ltiUrl, assignment.name!!, true) - RouteMatcher.route(requireContext(), Route(LtiLaunchFragment::class.java, mCourse, args)) + val args = LtiLaunchFragment.makeBundle(course, ltiUrl, assignment.name!!, true) + RouteMatcher.route(requireContext(), Route(LtiLaunchFragment::class.java, course, args)) } } } - submissionsLayout.setVisible(!mCourse.isDesigner) + submissionsLayout.setVisible(!course.isDesigner) } @Suppress("UsePropertyAccessSyntax") @@ -281,9 +288,9 @@ class AssignmentDetailsFragment : BasePresenterFragment< // Load description loadHtmlJob = descriptionWebViewWrapper.webView.loadHtmlWithIframes(requireContext(), assignment.description, { - descriptionWebViewWrapper.loadHtml(it, assignment.name, baseUrl = mAssignment.htmlUrl) + descriptionWebViewWrapper.loadHtml(it, assignment.name, baseUrl = assignment.htmlUrl) }) { - LtiLaunchFragment.routeLtiLaunchFragment(requireContext(), mCourse, it) + LtiLaunchFragment.routeLtiLaunchFragment(requireContext(), course, it) } } @@ -361,21 +368,21 @@ class AssignmentDetailsFragment : BasePresenterFragment< private fun setupListeners(assignment: Assignment) = with(binding) { dueLayout.setOnClickListener { val args = DueDatesFragment.makeBundle(assignment) - RouteMatcher.route(requireContext(), Route(null, DueDatesFragment::class.java, mCourse, args)) + RouteMatcher.route(requireContext(), Route(null, DueDatesFragment::class.java, course, args)) } submissionsLayout.setOnClickListener { - navigateToSubmissions(mCourse, assignment, AssignmentSubmissionListPresenter.SubmissionListFilter.ALL) + navigateToSubmissions(course, assignment, AssignmentSubmissionListPresenter.SubmissionListFilter.ALL) } donutGroup.viewAllSubmissions.onClick { submissionsLayout.performClick() } // Separate click listener for a11y donutGroup.gradedWrapper.setOnClickListener { - navigateToSubmissions(mCourse, assignment, AssignmentSubmissionListPresenter.SubmissionListFilter.GRADED) + navigateToSubmissions(course, assignment, AssignmentSubmissionListPresenter.SubmissionListFilter.GRADED) } donutGroup.ungradedWrapper.setOnClickListener { - navigateToSubmissions(mCourse, assignment, AssignmentSubmissionListPresenter.SubmissionListFilter.NOT_GRADED) + navigateToSubmissions(course, assignment, AssignmentSubmissionListPresenter.SubmissionListFilter.NOT_GRADED) } donutGroup.notSubmittedWrapper.setOnClickListener { - navigateToSubmissions(mCourse, assignment, AssignmentSubmissionListPresenter.SubmissionListFilter.MISSING) + navigateToSubmissions(course, assignment, AssignmentSubmissionListPresenter.SubmissionListFilter.MISSING) } noDescriptionTextView.setOnClickListener { openEditPage(assignment) } @@ -385,7 +392,7 @@ class AssignmentDetailsFragment : BasePresenterFragment< assignment.discussionTopicHeader?.let { discussionTopicHeader -> viewDiscussionButton.setOnClickListener { - RouteMatcher.route(requireContext(), DiscussionRouterFragment.makeRoute(mCourse, discussionTopicHeader)) + RouteMatcher.route(requireContext(), DiscussionRouterFragment.makeRoute(course, discussionTopicHeader)) } } ?: viewDiscussionButton.setGone() @@ -394,7 +401,7 @@ class AssignmentDetailsFragment : BasePresenterFragment< private fun openEditPage(assignment: Assignment) { if(APIHelper.hasNetworkConnection()) { val args = EditAssignmentDetailsFragment.makeBundle(assignment, false) - RouteMatcher.route(requireContext(), Route(EditAssignmentDetailsFragment::class.java, mCourse, args)) + RouteMatcher.route(requireContext(), Route(EditAssignmentDetailsFragment::class.java, course, args)) } else { NoInternetConnectionDialog.show(requireFragmentManager()) } @@ -424,7 +431,7 @@ class AssignmentDetailsFragment : BasePresenterFragment< @Subscribe(threadMode = ThreadMode.MAIN, sticky = true) fun onAssignmentEdited(event: AssignmentUpdatedEvent) { event.once(javaClass.simpleName) { - if (it == presenter.mAssignment.id) mNeedToForceNetwork = true + if (it == presenter.mAssignment.id) needToForceNetwork = true } } @@ -440,12 +447,12 @@ class AssignmentDetailsFragment : BasePresenterFragment< @Subscribe(threadMode = ThreadMode.MAIN, sticky = true) fun onAssignmentGraded(event: AssignmentGradedEvent) { event.once(javaClass.simpleName) { - if(presenter.mAssignment.id == it) mNeedToForceNetwork = true + if(presenter.mAssignment.id == it) needToForceNetwork = true } } //Because of the presenter lifecycle using the assignment from there will result in random crashes. - override val identity: Long? get() = if(mAssignmentId != 0L) mAssignmentId else mAssignment.id + override val identity: Long? get() = if(assignmentId != 0L) assignmentId else assignment.id override val skipCheck: Boolean get() = false companion object { @@ -453,7 +460,7 @@ class AssignmentDetailsFragment : BasePresenterFragment< @JvmStatic val ASSIGNMENT_ID = "assignmentId" fun newInstance(course: Course, args: Bundle) = AssignmentDetailsFragment().withArgs(args).apply { - mCourse = course + this.course = course } fun makeBundle(assignment: Assignment): Bundle { diff --git a/apps/teacher/src/main/java/com/instructure/teacher/fragments/AssignmentListFragment.kt b/apps/teacher/src/main/java/com/instructure/teacher/fragments/AssignmentListFragment.kt index a71c9ddf7f..a20e6a382b 100644 --- a/apps/teacher/src/main/java/com/instructure/teacher/fragments/AssignmentListFragment.kt +++ b/apps/teacher/src/main/java/com/instructure/teacher/fragments/AssignmentListFragment.kt @@ -28,6 +28,7 @@ import com.instructure.canvasapi2.models.AssignmentGroup import com.instructure.canvasapi2.models.CanvasContext import com.instructure.canvasapi2.models.GradingPeriod import com.instructure.canvasapi2.utils.ApiPrefs +import com.instructure.canvasapi2.utils.pageview.PageView import com.instructure.interactions.router.Route import com.instructure.pandautils.analytics.SCREEN_VIEW_ASSIGNMENT_LIST import com.instructure.pandautils.analytics.ScreenView @@ -50,6 +51,7 @@ import org.greenrobot.eventbus.EventBus import org.greenrobot.eventbus.Subscribe import org.greenrobot.eventbus.ThreadMode +@PageView(url = "{canvasContext}/assignments") @ScreenView(SCREEN_VIEW_ASSIGNMENT_LIST) class AssignmentListFragment : BaseExpandableSyncFragment< AssignmentGroup, @@ -60,17 +62,17 @@ class AssignmentListFragment : BaseExpandableSyncFragment< private val binding by viewBinding(FragmentAssignmentListBinding::bind) - private var mCanvasContext: CanvasContext by ParcelableArg(default = CanvasContext.getGenericContext(CanvasContext.Type.COURSE, -1L, "")) - private var mPairedWithSubmissions: Boolean = false - private val mLinearLayoutManager by lazy { LinearLayoutManager(requireContext()) } + private var canvasContext: CanvasContext by ParcelableArg(default = CanvasContext.getGenericContext(CanvasContext.Type.COURSE, -1L, "")) + private var pairedWithSubmissions: Boolean = false + private val linearLayoutManager by lazy { LinearLayoutManager(requireContext()) } private lateinit var mRecyclerView: RecyclerView - private var mGradingPeriodMenu: PopupMenu? = null - private var mNeedToForceNetwork = false + private var gradingPeriodMenu: PopupMenu? = null + private var needToForceNetwork = false override fun layoutResId(): Int = R.layout.fragment_assignment_list override val recyclerView: RecyclerView get() = binding.assignmentRecyclerView - override fun getPresenterFactory() = AssignmentListPresenterFactory(mCanvasContext) + override fun getPresenterFactory() = AssignmentListPresenterFactory(canvasContext) override fun onPresenterPrepared(presenter: AssignmentListPresenter) { mRecyclerView = RecyclerViewUtils.buildRecyclerView( rootView = rootView, @@ -86,14 +88,14 @@ class AssignmentListFragment : BaseExpandableSyncFragment< } override fun onCreateView(view: View) { - mLinearLayoutManager.orientation = RecyclerView.VERTICAL + linearLayoutManager.orientation = RecyclerView.VERTICAL } override fun onReadySetGo(presenter: AssignmentListPresenter) { if (recyclerView.adapter == null) { mRecyclerView.adapter = adapter } - presenter.loadData(mNeedToForceNetwork) + presenter.loadData(needToForceNetwork) } override fun onResume() { @@ -112,24 +114,24 @@ class AssignmentListFragment : BaseExpandableSyncFragment< } override fun onPause() { - if(mGradingPeriodMenu != null) { - mGradingPeriodMenu?.dismiss() + if(gradingPeriodMenu != null) { + gradingPeriodMenu?.dismiss() } super.onPause() } override fun createAdapter(): AssignmentAdapter { - return AssignmentAdapter(requireContext(), presenter, mCanvasContext.textAndIconColor) { assignment -> - if (mPairedWithSubmissions) { + return AssignmentAdapter(requireContext(), presenter, canvasContext.textAndIconColor) { assignment -> + if (pairedWithSubmissions) { val args = AssignmentSubmissionListFragment.makeBundle(assignment) - RouteMatcher.route(requireContext(), Route(null, AssignmentSubmissionListFragment::class.java, mCanvasContext, args)) + RouteMatcher.route(requireContext(), Route(null, AssignmentSubmissionListFragment::class.java, canvasContext, args)) } else { if (assignment.submissionTypesRaw.contains(Assignment.SubmissionType.ONLINE_QUIZ.apiString)) { val args = QuizDetailsFragment.makeBundle(assignment.quizId) - RouteMatcher.route(requireContext(), Route(null, QuizDetailsFragment::class.java, mCanvasContext, args)) + RouteMatcher.route(requireContext(), Route(null, QuizDetailsFragment::class.java, canvasContext, args)) } else { val args = AssignmentDetailsFragment.makeBundle(assignment) - RouteMatcher.route(requireContext(), Route(null, AssignmentDetailsFragment::class.java, mCanvasContext, args)) + RouteMatcher.route(requireContext(), Route(null, AssignmentDetailsFragment::class.java, canvasContext, args)) } } } @@ -169,17 +171,17 @@ class AssignmentListFragment : BaseExpandableSyncFragment< //setup popup menu val menuItemView = rootView.findViewById(R.id.menu_grading_periods_filter) - mGradingPeriodMenu = PopupMenu(requireContext(), menuItemView, Gravity.TOP, 0, + gradingPeriodMenu = PopupMenu(requireContext(), menuItemView, Gravity.TOP, 0, R.style.Widget_AppCompat_PopupMenu_Overflow) - mGradingPeriodMenu?.setOnMenuItemClickListener { menuItem -> + gradingPeriodMenu?.setOnMenuItemClickListener { menuItem -> presenter.selectGradingPeriodIndex(menuItem.itemId) true } //add grading periods gradingPeriods.forEachIndexed { i, gradingPeriod -> - mGradingPeriodMenu?.menu?.add(0, i, 0, gradingPeriod.title) + gradingPeriodMenu?.menu?.add(0, i, 0, gradingPeriod.title) } //set the grading periods to the selection @@ -203,10 +205,10 @@ class AssignmentListFragment : BaseExpandableSyncFragment< private fun setupToolbar() = with(binding) { assignmentListToolbar.title = getString(R.string.assignments) - assignmentListToolbar.subtitle = mCanvasContext.name + assignmentListToolbar.subtitle = canvasContext.name assignmentListToolbar.setupBackButton(this@AssignmentListFragment) - ViewStyler.themeToolbarColored(requireActivity(), assignmentListToolbar, mCanvasContext.backgroundColor, requireContext().getColor(R.color.white)) + ViewStyler.themeToolbarColored(requireActivity(), assignmentListToolbar, canvasContext.backgroundColor, requireContext().getColor(R.color.white)) } override fun adjustGradingPeriodHeader(gradingPeriod: String, isVisible: Boolean, isFilterVisible: Boolean) { @@ -236,7 +238,7 @@ class AssignmentListFragment : BaseExpandableSyncFragment< val menuItemCallback: (MenuItem) -> Unit = { item -> when (item.itemId) { R.id.menu_grading_periods_filter -> { - mGradingPeriodMenu?.show() + gradingPeriodMenu?.show() } } } @@ -248,7 +250,7 @@ class AssignmentListFragment : BaseExpandableSyncFragment< // need to set a flag here. Because we use the event bus in the fragment instead of the presenter for unit testing purposes, // when we come back to this fragment it will go through the life cycle events again and the cached data will immediately // overwrite the data from the network if we refresh the presenter from here. - mNeedToForceNetwork = true + needToForceNetwork = true } } @@ -258,8 +260,8 @@ class AssignmentListFragment : BaseExpandableSyncFragment< @JvmStatic val PAIRED_WITH_SUBMISSIONS = "pairedWithSubmissions" fun getInstance(canvasContext: CanvasContext, args: Bundle) = AssignmentListFragment().apply { - mPairedWithSubmissions = args.getBoolean(PAIRED_WITH_SUBMISSIONS, false) - mCanvasContext = canvasContext + pairedWithSubmissions = args.getBoolean(PAIRED_WITH_SUBMISSIONS, false) + this.canvasContext = canvasContext } } } diff --git a/apps/teacher/src/main/java/com/instructure/teacher/fragments/CourseBrowserFragment.kt b/apps/teacher/src/main/java/com/instructure/teacher/fragments/CourseBrowserFragment.kt index 514c947e08..df78509560 100644 --- a/apps/teacher/src/main/java/com/instructure/teacher/fragments/CourseBrowserFragment.kt +++ b/apps/teacher/src/main/java/com/instructure/teacher/fragments/CourseBrowserFragment.kt @@ -31,6 +31,7 @@ import com.instructure.canvasapi2.utils.Analytics import com.instructure.canvasapi2.utils.AnalyticsEventConstants import com.instructure.canvasapi2.utils.ApiPrefs import com.instructure.canvasapi2.utils.isValid +import com.instructure.canvasapi2.utils.pageview.PageView import com.instructure.interactions.router.Route import com.instructure.pandautils.analytics.SCREEN_VIEW_COURSE_BROWSER import com.instructure.pandautils.analytics.ScreenView @@ -57,6 +58,7 @@ import org.greenrobot.eventbus.EventBus import org.greenrobot.eventbus.Subscribe import org.greenrobot.eventbus.ThreadMode +@PageView(url = "{canvasContext}") @ScreenView(SCREEN_VIEW_COURSE_BROWSER) class CourseBrowserFragment : BaseSyncFragment< Tab, @@ -68,13 +70,13 @@ class CourseBrowserFragment : BaseSyncFragment< private val binding by viewBinding(FragmentCourseBrowserBinding::bind) - private var mCanvasContext: CanvasContext by ParcelableArg(Course()) + private var canvasContext: CanvasContext by ParcelableArg(Course()) - private val mCourseBrowserHeader by lazy { rootView.findViewById(R.id.courseBrowserHeader) } + private val courseBrowserHeader by lazy { rootView.findViewById(R.id.courseBrowserHeader) } companion object { fun newInstance(context: CanvasContext) = CourseBrowserFragment().apply { - mCanvasContext = context + canvasContext = context } fun makeRoute(canvasContext: CanvasContext?) = Route(CourseBrowserFragment::class.java, canvasContext) @@ -86,7 +88,7 @@ class CourseBrowserFragment : BaseSyncFragment< override val recyclerView: RecyclerView get() = binding.courseBrowserRecyclerView override fun withPagination() = false - override fun getPresenterFactory() = CourseBrowserPresenterFactory(mCanvasContext) { tab, attendanceId -> + override fun getPresenterFactory() = CourseBrowserPresenterFactory(canvasContext) { tab, attendanceId -> //Filter for white-list supported features //TODO: support other things like it.isHidden when(tab.tabId) { @@ -133,15 +135,15 @@ class CourseBrowserFragment : BaseSyncFragment< presenter.loadData(false) } - override fun onResume() = with(binding) { + override fun onResume() { super.onResume() EventBus.getDefault().register(this@CourseBrowserFragment) (presenter.canvasContext as? Course)?.let { - courseImage.setCourseImage(it, it.backgroundColor, !TeacherPrefs.hideCourseColorOverlay) + binding.courseImage.setCourseImage(it, it.backgroundColor, !TeacherPrefs.hideCourseColorOverlay) } - courseBrowserTitle.text = presenter.canvasContext.name - courseBrowserSubtitle.text = (presenter.canvasContext as? Course)?.term?.name.orEmpty() - mCourseBrowserHeader.setTitleAndSubtitle(presenter.canvasContext.name.orEmpty(), (presenter.canvasContext as? Course)?.term?.name.orEmpty()) + binding.courseBrowserTitle.text = presenter.canvasContext.name + binding.courseBrowserSubtitle.text = (presenter.canvasContext as? Course)?.term?.name.orEmpty() + courseBrowserHeader.setTitleAndSubtitle(presenter.canvasContext.name.orEmpty(), (presenter.canvasContext as? Course)?.term?.name.orEmpty()) setupToolbar() if (!presenter.isEmpty) { checkIfEmpty() @@ -296,22 +298,22 @@ class CourseBrowserFragment : BaseSyncFragment< /** * Manages state of titles & subtitles when users scrolls */ - override fun onOffsetChanged(appBarLayout: AppBarLayout?, verticalOffset: Int) = with(binding) { + override fun onOffsetChanged(appBarLayout: AppBarLayout?, verticalOffset: Int) { val percentage = Math.abs(verticalOffset).div(appBarLayout?.totalScrollRange?.toFloat() ?: 1F) if(percentage <= 0.3F) { - val toolbarAnimation = ObjectAnimator.ofFloat(mCourseBrowserHeader, View.ALPHA, mCourseBrowserHeader.alpha, 0F) - val titleAnimation = ObjectAnimator.ofFloat(courseBrowserTitle, View.ALPHA, courseBrowserTitle.alpha, 1F) - val subtitleAnimation = ObjectAnimator.ofFloat(courseBrowserSubtitle, View.ALPHA, courseBrowserSubtitle.alpha, 0.8F) + val toolbarAnimation = ObjectAnimator.ofFloat(courseBrowserHeader, View.ALPHA, courseBrowserHeader.alpha, 0F) + val titleAnimation = ObjectAnimator.ofFloat(binding.courseBrowserTitle, View.ALPHA, binding.courseBrowserTitle.alpha, 1F) + val subtitleAnimation = ObjectAnimator.ofFloat(binding.courseBrowserSubtitle, View.ALPHA, binding.courseBrowserSubtitle.alpha, 0.8F) toolbarAnimation.setAutoCancel(true) titleAnimation.setAutoCancel(true) subtitleAnimation.setAutoCancel(true) - toolbarAnimation.target = mCourseBrowserHeader - titleAnimation.target = courseBrowserTitle - subtitleAnimation.target = courseBrowserSubtitle + toolbarAnimation.target = courseBrowserHeader + titleAnimation.target = binding.courseBrowserTitle + subtitleAnimation.target = binding.courseBrowserSubtitle toolbarAnimation.duration = 200 titleAnimation.duration = 320 @@ -322,17 +324,17 @@ class CourseBrowserFragment : BaseSyncFragment< subtitleAnimation.start() } else if(percentage > 0.7F) { - val toolbarAnimation = ObjectAnimator.ofFloat(mCourseBrowserHeader, View.ALPHA, mCourseBrowserHeader.alpha, 1F) - val titleAnimation = ObjectAnimator.ofFloat(courseBrowserTitle, View.ALPHA, courseBrowserTitle.alpha, 0F) - val subtitleAnimation = ObjectAnimator.ofFloat(courseBrowserSubtitle, View.ALPHA, courseBrowserSubtitle.alpha, 0F) + val toolbarAnimation = ObjectAnimator.ofFloat(courseBrowserHeader, View.ALPHA, courseBrowserHeader.alpha, 1F) + val titleAnimation = ObjectAnimator.ofFloat(binding.courseBrowserTitle, View.ALPHA, binding.courseBrowserTitle.alpha, 0F) + val subtitleAnimation = ObjectAnimator.ofFloat(binding.courseBrowserSubtitle, View.ALPHA, binding.courseBrowserSubtitle.alpha, 0F) toolbarAnimation.setAutoCancel(true) titleAnimation.setAutoCancel(true) subtitleAnimation.setAutoCancel(true) - toolbarAnimation.target = mCourseBrowserHeader - titleAnimation.target = courseBrowserTitle - subtitleAnimation.target = courseBrowserSubtitle + toolbarAnimation.target = courseBrowserHeader + titleAnimation.target = binding.courseBrowserTitle + subtitleAnimation.target = binding.courseBrowserSubtitle toolbarAnimation.duration = 200 titleAnimation.duration = 200 @@ -377,7 +379,7 @@ class CourseBrowserFragment : BaseSyncFragment< `package` = CANVAS_STUDENT_ID action = Const.INTENT_ACTION_STUDENT_VIEW putExtra(Const.TOKEN, token) - putExtra(Const.COURSE_ID, mCanvasContext.id) // Required to create/get test user + putExtra(Const.COURSE_ID, canvasContext.id) // Required to create/get test user putExtra(Const.DOMAIN, ApiPrefs.domain) putExtra(Const.CLIENT_ID, ApiPrefs.clientId) putExtra(Const.CLIENT_SECRET, ApiPrefs.clientSecret) diff --git a/apps/teacher/src/main/java/com/instructure/teacher/fragments/CourseSettingsFragment.kt b/apps/teacher/src/main/java/com/instructure/teacher/fragments/CourseSettingsFragment.kt index eedab53684..5bf2c89536 100644 --- a/apps/teacher/src/main/java/com/instructure/teacher/fragments/CourseSettingsFragment.kt +++ b/apps/teacher/src/main/java/com/instructure/teacher/fragments/CourseSettingsFragment.kt @@ -22,6 +22,8 @@ import android.os.Bundle import android.os.Parcelable import com.instructure.canvasapi2.models.Course import com.instructure.canvasapi2.utils.globalName +import com.instructure.canvasapi2.utils.pageview.PageView +import com.instructure.canvasapi2.utils.pageview.PageViewUrl import com.instructure.pandautils.analytics.SCREEN_VIEW_COURSE_SETTINGS import com.instructure.pandautils.analytics.ScreenView import com.instructure.pandautils.binding.viewBinding @@ -37,6 +39,7 @@ import com.instructure.teacher.utils.TeacherPrefs import com.instructure.teacher.utils.setupBackButton import com.instructure.teacher.viewinterface.CourseSettingsFragmentView +@PageView @ScreenView(SCREEN_VIEW_COURSE_SETTINGS) class CourseSettingsFragment : BasePresenterFragment< CourseSettingsFragmentPresenter, @@ -44,7 +47,11 @@ class CourseSettingsFragment : BasePresenterFragment< private val binding by viewBinding(FragmentCourseSettingsBinding::bind) - private var mCourse: Course by ParcelableArg(default = Course()) + private var course: Course by ParcelableArg(default = Course()) + + @Suppress("unused") + @PageViewUrl + private fun makePageViewUrl() = "courses/${course.id}/settings" private val mHomePages: Map by lazy { // Use LinkedHashMap map to keep order consistent between API levels @@ -62,16 +69,16 @@ class CourseSettingsFragment : BasePresenterFragment< override fun onReadySetGo(presenter: CourseSettingsFragmentPresenter) { setupToolbar() - binding.courseImage.setCourseImage(mCourse, mCourse.backgroundColor, !TeacherPrefs.hideCourseColorOverlay) + binding.courseImage.setCourseImage(course, course.backgroundColor, !TeacherPrefs.hideCourseColorOverlay) } override fun onViewStateRestored(savedInstanceState: Bundle?) { super.onViewStateRestored(savedInstanceState) if (savedInstanceState == null) { - updateCourseName(mCourse) - updateCourseHomePage(mCourse.homePage) + updateCourseName(course) + updateCourseHomePage(course.homePage) } else { - updateCourseName(mCourse) + updateCourseName(course) } } @@ -89,12 +96,12 @@ class CourseSettingsFragment : BasePresenterFragment< toolbar.setupBackButton(this@CourseSettingsFragment) toolbar.title = getString(R.string.course_settings) ViewStyler.themeToolbarLight(requireActivity(), toolbar) - toolbar.setSubtitleTextColor(mCourse.textAndIconColor) + toolbar.setSubtitleTextColor(course.textAndIconColor) } override fun showEditCourseNameDialog() { - val dialog: EditCourseNameDialog = EditCourseNameDialog.getInstance(requireActivity().supportFragmentManager, mCourse) { newName -> - presenter.editCourseName(newName, mCourse) + val dialog: EditCourseNameDialog = EditCourseNameDialog.getInstance(requireActivity().supportFragmentManager, course) { newName -> + presenter.editCourseName(newName, course) } dialog.show(requireActivity().supportFragmentManager, EditCourseNameDialog::class.java.simpleName) @@ -102,9 +109,9 @@ class CourseSettingsFragment : BasePresenterFragment< override fun showEditCourseHomePageDialog() { val (keys, values) = mHomePages.toList().unzip() - val selectedIdx = keys.indexOf(mCourse.homePage?.apiString) + val selectedIdx = keys.indexOf(course.homePage?.apiString) val dialog = RadioButtonDialog.getInstance(requireActivity().supportFragmentManager, getString(R.string.set_home_to), values as ArrayList, selectedIdx) { idx -> - presenter.editCourseHomePage(keys[idx], mCourse) + presenter.editCourseHomePage(keys[idx], course) } dialog.show(requireActivity().supportFragmentManager, RadioButtonDialog::class.java.simpleName) @@ -113,18 +120,18 @@ class CourseSettingsFragment : BasePresenterFragment< override fun updateCourseName(course: Course) = with(binding) { renameCourse.courseName.text = course.globalName toolbar.subtitle = course.globalName - mCourse.globalName = course.globalName + this@CourseSettingsFragment.course.globalName = course.globalName setResult() } override fun updateCourseHomePage(newHomePage: Course.HomePage?) { binding.editCourseHomepage.courseHomePage.text = mHomePages[newHomePage?.apiString] - mCourse.homePage = newHomePage + course.homePage = newHomePage setResult() } private fun setResult() { - requireActivity().setResult(Activity.RESULT_OK, Intent().apply { putExtra(Const.COURSE, mCourse as Parcelable) }) + requireActivity().setResult(Activity.RESULT_OK, Intent().apply { putExtra(Const.COURSE, course as Parcelable) }) } override fun onRefreshFinished() {} @@ -133,7 +140,7 @@ class CourseSettingsFragment : BasePresenterFragment< companion object { fun newInstance(course: Course) = CourseSettingsFragment().apply { - mCourse = course + this.course = course } } } diff --git a/apps/teacher/src/main/java/com/instructure/teacher/fragments/CreateDiscussionFragment.kt b/apps/teacher/src/main/java/com/instructure/teacher/fragments/CreateDiscussionFragment.kt index f9bfa7c447..a2199cf8e4 100644 --- a/apps/teacher/src/main/java/com/instructure/teacher/fragments/CreateDiscussionFragment.kt +++ b/apps/teacher/src/main/java/com/instructure/teacher/fragments/CreateDiscussionFragment.kt @@ -37,6 +37,7 @@ import com.instructure.canvasapi2.models.postmodels.DiscussionTopicPostBody import com.instructure.canvasapi2.models.postmodels.FileSubmitObject import com.instructure.canvasapi2.utils.NumberHelper import com.instructure.canvasapi2.utils.Pronouns +import com.instructure.canvasapi2.utils.pageview.PageView import com.instructure.canvasapi2.utils.toApiString import com.instructure.interactions.Identity import com.instructure.interactions.router.Route @@ -72,6 +73,7 @@ import org.greenrobot.eventbus.Subscribe import org.greenrobot.eventbus.ThreadMode import java.util.* +@PageView("courses/{canvasContext}/discussion_topics/new") @ScreenView(SCREEN_VIEW_CREATE_DISCUSSION) class CreateDiscussionFragment : BasePresenterFragment< CreateDiscussionPresenter, @@ -79,21 +81,21 @@ class CreateDiscussionFragment : BasePresenterFragment< private val binding by viewBinding(FragmentCreateDiscussionBinding::bind) - private var mCanvasContext: CanvasContext by ParcelableArg(Course(), CANVAS_CONTEXT) - private var mDiscussionTopicHeader: DiscussionTopicHeader? by NullableParcelableArg(null, DISCUSSION_TOPIC_HEADER) + private var canvasContext: CanvasContext by ParcelableArg(Course(), CANVAS_CONTEXT) + private var discussionTopicHeader: DiscussionTopicHeader? by NullableParcelableArg(null, DISCUSSION_TOPIC_HEADER) private val sendButton: TextView? get() = view?.findViewById(R.id.menuSaveDiscussion) private val saveButton: TextView? get() = view?.findViewById(R.id.menuSave) - private val mAttachmentButton get() = binding.toolbar.menu.findItem(R.id.menuAddAttachment) - private var mIsPublished: Boolean by BooleanArg(false) - private var mIsSubscribed: Boolean by BooleanArg(true) - private var mAllowThreaded: Boolean by BooleanArg(false) - private var mUsersMustPost: Boolean by BooleanArg(false) - private var mHasLoadedDataForEdit by BooleanArg() - private var mDisplayGradeAs: String? by NullableStringArg() - private var mDescription by NullableStringArg() - - private var mScrollToDates: Boolean by BooleanArg(false, SHOULD_SCROLL_TO_DATES) - private var mRCEHasFocus = false + private val attachmentButton get() = binding.toolbar.menu.findItem(R.id.menuAddAttachment) + private var isPublished: Boolean by BooleanArg(false) + private var isSubscribed: Boolean by BooleanArg(true) + private var allowThreaded: Boolean by BooleanArg(false) + private var usersMustPost: Boolean by BooleanArg(false) + private var hasLoadedDataForEdit by BooleanArg() + private var displayGradeAs: String? by NullableStringArg() + private var description by NullableStringArg() + + private var scrollToDates: Boolean by BooleanArg(false, SHOULD_SCROLL_TO_DATES) + private var rceHasFocus = false private var placeHolderList: ArrayList = ArrayList() @@ -102,7 +104,7 @@ class CreateDiscussionFragment : BasePresenterFragment< // We maintain a copy of the groupedDueDates to manipulate and use to display // overrides. When pushing changes, we update the original assignment object // with the changes in the copy. - private var mEditDateGroups: EditDateGroups = arrayListOf() + private var editDateGroups: EditDateGroups = arrayListOf() private val groupsMapped = hashMapOf() private val sectionsMapped = hashMapOf() private val studentsMapped = hashMapOf() @@ -110,9 +112,9 @@ class CreateDiscussionFragment : BasePresenterFragment< // Keeps track of which override we were editing so we can scroll back to it when the user returns from editing assignees private var scrollBackToOverride: AssignmentOverrideView? = null - private var mScrollHandler: Handler = Handler() + private var scrollHandler: Handler = Handler() - private var mScrollToRunnable: Runnable = Runnable { + private var scrollToRunnable: Runnable = Runnable { if(isAdded) binding.scrollView.fullScroll(ScrollView.FOCUS_DOWN) } @@ -133,7 +135,7 @@ class CreateDiscussionFragment : BasePresenterFragment< private val removeOverrideClick: (DueDateGroup) -> Unit = { callback -> // Show confirmation dialog ConfirmRemoveAssignmentOverrideDialog.show(requireActivity().supportFragmentManager) { - if (mEditDateGroups.contains(callback)) mEditDateGroups.remove(callback) + if (editDateGroups.contains(callback)) editDateGroups.remove(callback) setupOverrides() } } @@ -150,7 +152,7 @@ class CreateDiscussionFragment : BasePresenterFragment< override fun onDestroy() { super.onDestroy() - mScrollHandler.removeCallbacks(mScrollToRunnable) + scrollHandler.removeCallbacks(scrollToRunnable) } override fun onRefreshFinished() { } @@ -164,8 +166,8 @@ class CreateDiscussionFragment : BasePresenterFragment< super.onViewCreated(view, savedInstanceState) savedInstanceState?.let { @Suppress("UNCHECKED_CAST") - mEditDateGroups = (savedInstanceState.getSerializable(EDIT_DATE_GROUPS) as ArrayList) - mRCEHasFocus = savedInstanceState.getBoolean(RCE_HAS_FOCUS) + editDateGroups = (savedInstanceState.getSerializable(EDIT_DATE_GROUPS) as ArrayList) + rceHasFocus = savedInstanceState.getBoolean(RCE_HAS_FOCUS) } } @@ -186,21 +188,21 @@ class CreateDiscussionFragment : BasePresenterFragment< override fun onReadySetGo(presenter: CreateDiscussionPresenter) { // If we already have something in the edit date groups we already have the full assignment and don't need to get it again. - mDiscussionTopicHeader?.assignment?.let { + discussionTopicHeader?.assignment?.let { // Get the full assignment with overrides - if (mEditDateGroups.size == 0) presenter.getFullAssignment(it.id) + if (editDateGroups.size == 0) presenter.getFullAssignment(it.id) } setupToolbar() setupViews() - if(mRCEHasFocus) { + if(rceHasFocus) { binding.descriptionRCEView.requestEditorFocus() activity?.window?.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE) } } - override fun getPresenterFactory() = CreateDiscussionPresenterFactory(mCanvasContext, mDiscussionTopicHeader?.assignment) + override fun getPresenterFactory() = CreateDiscussionPresenterFactory(canvasContext, discussionTopicHeader?.assignment) override fun onPresenterPrepared(presenter: CreateDiscussionPresenter) { } @@ -213,14 +215,14 @@ class CreateDiscussionFragment : BasePresenterFragment< setupOverrides() - if (mScrollToDates) { - mScrollToDates = false + if (scrollToDates) { + scrollToDates = false // We came from the Dates page, scroll to the dates for editing - mScrollHandler.postDelayed(mScrollToRunnable, 300) + scrollHandler.postDelayed(scrollToRunnable, 300) } scrollBackToOverride?.let { - if (!mScrollToDates) + if (!scrollToDates) binding.scrollView.post { binding.scrollView.fullScroll(ScrollView.FOCUS_DOWN) } @@ -233,16 +235,16 @@ class CreateDiscussionFragment : BasePresenterFragment< } override fun updatedAssignment() { - mEditDateGroups.clear() + editDateGroups.clear() setupViews() } fun setupToolbar() = with(binding) { toolbar.setupCloseButton { - if(mDiscussionTopicHeader == null) { + if(discussionTopicHeader == null) { activity?.onBackPressed() } else { - if (mDiscussionTopicHeader?.message == descriptionRCEView.html) { + if (discussionTopicHeader?.message == descriptionRCEView.html) { activity?.onBackPressed() } else { UnsavedChangesExitDialog.show(requireFragmentManager()) { @@ -252,11 +254,11 @@ class CreateDiscussionFragment : BasePresenterFragment< } } - toolbar.title = if(mDiscussionTopicHeader == null) getString(R.string.createDiscussion) else getString(R.string.editDiscussion) - toolbar.setupMenu(if (mDiscussionTopicHeader == null) R.menu.create_discussion else R.menu.menu_save_generic) { menuItem -> + toolbar.title = if(discussionTopicHeader == null) getString(R.string.createDiscussion) else getString(R.string.editDiscussion) + toolbar.setupMenu(if (discussionTopicHeader == null) R.menu.create_discussion else R.menu.menu_save_generic) { menuItem -> when (menuItem.itemId) { R.id.menuSaveDiscussion, R.id.menuSave -> withRequireNetwork { saveDiscussion() } - R.id.menuAddAttachment -> if (mDiscussionTopicHeader == null) addAttachment() + R.id.menuAddAttachment -> if (discussionTopicHeader == null) addAttachment() } } @@ -272,15 +274,15 @@ class CreateDiscussionFragment : BasePresenterFragment< it.typeface = Typeface.create("sans-serif-medium", Typeface.NORMAL) } - if(CanvasWebView.containsLTI(mDescription ?: mDiscussionTopicHeader?.message ?: "", "UTF-8")) { - descriptionRCEView.setHtml(DiscussionUtils.createLTIPlaceHolders(requireContext(), mDescription ?: mDiscussionTopicHeader?.message ?: "") { _, placeholder -> + if(CanvasWebView.containsLTI(description ?: discussionTopicHeader?.message ?: "", "UTF-8")) { + descriptionRCEView.setHtml(DiscussionUtils.createLTIPlaceHolders(requireContext(), description ?: discussionTopicHeader?.message ?: "") { _, placeholder -> placeHolderList.add(placeholder) }, getString(R.string.discussion_details), getString(R.string.rce_empty_description), ThemePrefs.brandColor, ThemePrefs.textButtonColor) } else { - descriptionRCEView.setHtml(mDescription ?: mDiscussionTopicHeader?.message, + descriptionRCEView.setHtml(description ?: discussionTopicHeader?.message, getString(R.string.discussion_details), getString(R.string.rce_empty_description), ThemePrefs.brandColor, ThemePrefs.textButtonColor) @@ -289,14 +291,14 @@ class CreateDiscussionFragment : BasePresenterFragment< // When the RCE editor has focus we want the label to be darker so it matches the title's functionality descriptionRCEView.setLabel(discussionDescLabel, R.color.textDarkest, R.color.textDark) - if (!mHasLoadedDataForEdit) - mDiscussionTopicHeader?.let { + if (!hasLoadedDataForEdit) + discussionTopicHeader?.let { editDiscussionName.setText(it.title) - mIsPublished = it.published - mAllowThreaded = it.type == DiscussionTopicHeader.DiscussionType.THREADED - mUsersMustPost = it.requireInitialPost - mIsSubscribed = it.subscribed - mHasLoadedDataForEdit = true + isPublished = it.published + allowThreaded = it.type == DiscussionTopicHeader.DiscussionType.THREADED + usersMustPost = it.requireInitialPost + isSubscribed = it.subscribed + hasLoadedDataForEdit = true } ViewStyler.themeEditText(requireContext(), editDiscussionName, ThemePrefs.brandColor) @@ -309,19 +311,19 @@ class CreateDiscussionFragment : BasePresenterFragment< updateAttachmentUI() if(presenter.getAssignment() == null) { - if(mEditDateGroups.isEmpty()) { + if(editDateGroups.isEmpty()) { // If the dateGroups is empty, we want to add a due date so that we can set the available from and to fields - mEditDateGroups.clear() + editDateGroups.clear() val dueDateGroup = DueDateGroup() - if(mDiscussionTopicHeader != null) { + if(discussionTopicHeader != null) { // Populate the availability dates if we have them, the assignment is null, so this is an ungraded assignment - dueDateGroup.coreDates.lockDate = (mDiscussionTopicHeader as DiscussionTopicHeader).lockAt - dueDateGroup.coreDates.unlockDate = (mDiscussionTopicHeader as DiscussionTopicHeader).delayedPostDate + dueDateGroup.coreDates.lockDate = (discussionTopicHeader as DiscussionTopicHeader).lockAt + dueDateGroup.coreDates.unlockDate = (discussionTopicHeader as DiscussionTopicHeader).delayedPostDate } - mEditDateGroups.add(dueDateGroup) + editDateGroups.add(dueDateGroup) } //Make the graded things gone, we can't create a graded discussion @@ -333,20 +335,20 @@ class CreateDiscussionFragment : BasePresenterFragment< val pointsPossible = (presenter.getAssignment() as Assignment).pointsPossible editGradePoints.setText(NumberHelper.formatDecimal(pointsPossible, 2, true)) - if(mDisplayGradeAs == null) { - mDisplayGradeAs = (presenter.getAssignment() as Assignment).gradingType + if(displayGradeAs == null) { + displayGradeAs = (presenter.getAssignment() as Assignment).gradingType } setupDisplayGradeAs() - if (mEditDateGroups.isEmpty()) mEditDateGroups.addAll((presenter.getAssignment() as Assignment).groupedDueDates) + if (editDateGroups.isEmpty()) editDateGroups.addAll((presenter.getAssignment() as Assignment).groupedDueDates) if (groupsMapped.isEmpty() && sectionsMapped.isEmpty() && studentsMapped.isEmpty()) { presenter.getDueDateInfo((presenter.getAssignment() as Assignment).groupCategoryId) } addOverride.setOnClickListener { - mEditDateGroups.add(DueDateGroup()) + editDateGroups.add(DueDateGroup()) setupOverrides() scrollView.post { scrollView.fullScroll(ScrollView.FOCUS_DOWN) @@ -370,39 +372,39 @@ class CreateDiscussionFragment : BasePresenterFragment< // If a student has submitted something, we can't let the teacher unpublish the discussion if (presenter.getAssignment()?.unpublishable == true) { binding.publishWrapper.setGone() - mIsPublished = true + isPublished = true return } // Publish status with(binding.publishSwitch) { applyTheme() - isChecked = mIsPublished - setOnCheckedChangeListener { _, isChecked -> mIsPublished = isChecked } + isChecked = isPublished + setOnCheckedChangeListener { _, isChecked -> isPublished = isChecked } } } private fun setupSubscribeSwitch() { with(binding.subscribeSwitch) { applyTheme() - isChecked = mIsSubscribed - setOnCheckedChangeListener { _, isChecked -> mIsSubscribed = isChecked } + isChecked = isSubscribed + setOnCheckedChangeListener { _, isChecked -> isSubscribed = isChecked } } } private fun setupAllowThreadedSwitch() { with (binding.threadedSwitch) { applyTheme() - isChecked = mAllowThreaded - setOnCheckedChangeListener { _, isChecked -> mAllowThreaded = isChecked } + isChecked = allowThreaded + setOnCheckedChangeListener { _, isChecked -> allowThreaded = isChecked } } } private fun setupUsersMustPostSwitch() { with(binding.usersMustPostSwitch) { applyTheme() - isChecked = mUsersMustPost - setOnCheckedChangeListener { _, isChecked -> mUsersMustPost = isChecked } + isChecked = usersMustPost + setOnCheckedChangeListener { _, isChecked -> usersMustPost = isChecked } } } @@ -411,13 +413,13 @@ class CreateDiscussionFragment : BasePresenterFragment< if(presenter.getAssignment() == null) { // Load in overrides - mEditDateGroups.forEachIndexed { index, dueDateGroup -> + editDateGroups.forEachIndexed { index, dueDateGroup -> val assignees = ArrayList() val v = AssignmentOverrideView(requireActivity()) v.toAndFromDatesOnly() - v.setupOverride(index, dueDateGroup, mEditDateGroups.size > 1, assignees, datePickerOnClick, timePickerOnClick, { - if (mEditDateGroups.contains(it)) mEditDateGroups.remove(it) + v.setupOverride(index, dueDateGroup, editDateGroups.size > 1, assignees, datePickerOnClick, timePickerOnClick, { + if (editDateGroups.contains(it)) editDateGroups.remove(it) setupOverrides() }) { } @@ -426,11 +428,11 @@ class CreateDiscussionFragment : BasePresenterFragment< } else { // Load in overrides if(groupsMapped.isNotEmpty() || sectionsMapped.isNotEmpty() || studentsMapped.isNotEmpty()) { - mEditDateGroups.forEachIndexed { index, dueDateGroup -> + editDateGroups.forEachIndexed { index, dueDateGroup -> val assignees = ArrayList() val v = AssignmentOverrideView(requireActivity()) if (dueDateGroup.isEveryone) { - assignees += getString(if (mEditDateGroups.any { it.hasOverrideAssignees }) R.string.everyone_else else R.string.everyone) + assignees += getString(if (editDateGroups.any { it.hasOverrideAssignees }) R.string.everyone_else else R.string.everyone) } dueDateGroup.groupIds.forEach { assignees.add(groupsMapped[it]?.name!!) } @@ -439,14 +441,14 @@ class CreateDiscussionFragment : BasePresenterFragment< assignees.add(studentsMapped[it]!!.let { user -> Pronouns.span(user.name, user.pronouns) }) } - v.setupOverride(index, dueDateGroup, mEditDateGroups.size > 1, assignees, datePickerOnClick, timePickerOnClick, removeOverrideClick) { + v.setupOverride(index, dueDateGroup, editDateGroups.size > 1, assignees, datePickerOnClick, timePickerOnClick, removeOverrideClick) { val args = AssigneeListFragment.makeBundle( - mEditDateGroups, + editDateGroups, index, sectionsMapped.values.toList(), groupsMapped.values.toList(), studentsMapped.values.toList()) - RouteMatcher.route(requireContext(), Route(AssigneeListFragment::class.java, mCanvasContext, args)) + RouteMatcher.route(requireContext(), Route(AssigneeListFragment::class.java, canvasContext, args)) scrollBackToOverride = v } @@ -468,7 +470,7 @@ class CreateDiscussionFragment : BasePresenterFragment< ViewStyler.themeSpinner(requireContext(), displayGradeAsSpinner, ThemePrefs.brandColor) displayGradeAsSpinner.onItemSelectedListener = null - when(mDisplayGradeAs) { + when(displayGradeAs) { Assignment.POINTS_TYPE -> displayGradeAsSpinner.setSelection(spinnerAdapter.getPosition(getString(R.string.points))) Assignment.GPA_SCALE_TYPE -> displayGradeAsSpinner.setSelection(spinnerAdapter.getPosition(getString(R.string.gpa_scale))) Assignment.LETTER_GRADE_TYPE -> displayGradeAsSpinner.setSelection(spinnerAdapter.getPosition(getString(R.string.letter_grade))) @@ -480,11 +482,11 @@ class CreateDiscussionFragment : BasePresenterFragment< override fun onItemSelected(parent: AdapterView<*>, view: View?, position: Int, id: Long) { view ?: return when((view as TextView).text.toString()) { - getString(R.string.points) -> mDisplayGradeAs = Assignment.POINTS_TYPE - getString(R.string.gpa_scale) -> mDisplayGradeAs = Assignment.GPA_SCALE_TYPE - getString(R.string.letter_grade) -> mDisplayGradeAs = Assignment.LETTER_GRADE_TYPE - getString(R.string.complete_incomplete) -> mDisplayGradeAs = Assignment.PASS_FAIL_TYPE - getString(R.string.percentage) -> mDisplayGradeAs = Assignment.PERCENT_TYPE + getString(R.string.points) -> displayGradeAs = Assignment.POINTS_TYPE + getString(R.string.gpa_scale) -> displayGradeAs = Assignment.GPA_SCALE_TYPE + getString(R.string.letter_grade) -> displayGradeAs = Assignment.LETTER_GRADE_TYPE + getString(R.string.complete_incomplete) -> displayGradeAs = Assignment.PASS_FAIL_TYPE + getString(R.string.percentage) -> displayGradeAs = Assignment.PERCENT_TYPE } } @@ -493,14 +495,14 @@ class CreateDiscussionFragment : BasePresenterFragment< } private fun setupDelete() { - binding.deleteWrapper.setVisible(mDiscussionTopicHeader != null) + binding.deleteWrapper.setVisible(discussionTopicHeader != null) binding.deleteWrapper.onClickWithRequireNetwork { AlertDialog.Builder(requireContext()) .setTitle(R.string.discussions_delete_title) .setMessage(R.string.discussions_delete_message) .setPositiveButton(R.string.delete) { _, _ -> - if(mDiscussionTopicHeader != null) { - presenter.deleteDiscussionTopicHeader(mDiscussionTopicHeader!!.id) + if(discussionTopicHeader != null) { + presenter.deleteDiscussionTopicHeader(discussionTopicHeader!!.id) } } .setNegativeButton(R.string.cancel) { _, _ -> } @@ -526,12 +528,12 @@ class CreateDiscussionFragment : BasePresenterFragment< } // Show existing attachment (if any) - mDiscussionTopicHeader?.attachments?.firstOrNull()?.let { + discussionTopicHeader?.attachments?.firstOrNull()?.let { val attachmentView = AttachmentView(requireContext()) attachmentView.setPendingRemoteFile(it, true) { action, attachment -> if (action == AttachmentView.AttachmentAction.REMOVE) { presenter.attachmentRemoved = true - mDiscussionTopicHeader?.attachments?.remove(attachment) + discussionTopicHeader?.attachments?.remove(attachment) } } @@ -541,12 +543,12 @@ class CreateDiscussionFragment : BasePresenterFragment< private fun updateAttachmentButton(show: Boolean = true) { // Only show if (1) we're in creation mode and (2) we don't already have an attachment - mAttachmentButton?.isVisible = show && mDiscussionTopicHeader == null && presenter.attachment == null + attachmentButton?.isVisible = show && discussionTopicHeader == null && presenter.attachment == null } private fun addAttachment() { // set the description here. When we ask for permission to use the camera the app can call readySetGo and reset the description - mDescription = binding.descriptionRCEView.html + description = binding.descriptionRCEView.html val bundle = FileUploadDialogFragment.createDiscussionsBundle(ArrayList()) FileUploadDialogFragment.newInstance(bundle).show(childFragmentManager, FileUploadDialogFragment.TAG) @@ -585,7 +587,7 @@ class CreateDiscussionFragment : BasePresenterFragment< } private fun saveDiscussion() = with(binding) { - if(mDiscussionTopicHeader != null) { + if(discussionTopicHeader != null) { val postData = DiscussionTopicPostBody() // Discussion title isn't required @@ -595,27 +597,27 @@ class CreateDiscussionFragment : BasePresenterFragment< postData.title = editDiscussionName.text?.toString() ?: getString(R.string.no_title) } postData.message = handleLTIPlaceHolders(placeHolderList, descriptionRCEView.html) - postData.published = mIsPublished - postData.discussionType = if (mAllowThreaded) { + postData.published = isPublished + postData.discussionType = if (allowThreaded) { DiscussionTopicHeader.DiscussionType.THREADED.toString().lowercase(Locale.getDefault()) } else { DiscussionTopicHeader.DiscussionType.SIDE_COMMENT.toString().lowercase(Locale.getDefault()) } - postData.requireInitialPost = mUsersMustPost + postData.requireInitialPost = usersMustPost if (presenter.getAssignment() == null) { - postData.delayedPostAt = mEditDateGroups[0].coreDates.unlockDate.toApiString() - postData.lockAt = mEditDateGroups[0].coreDates.lockDate + postData.delayedPostAt = editDateGroups[0].coreDates.unlockDate.toApiString() + postData.lockAt = editDateGroups[0].coreDates.lockDate } else { val assignmentPostData = AssignmentPostBody() - assignmentPostData.gradingType = mDisplayGradeAs - assignmentPostData.setGroupedDueDates(mEditDateGroups) + assignmentPostData.gradingType = displayGradeAs + assignmentPostData.setGroupedDueDates(editDateGroups) assignmentPostData.pointsPossible = editGradePoints.text.toString().toDouble() postData.assignment = assignmentPostData } - presenter.editDiscussion((mDiscussionTopicHeader as DiscussionTopicHeader).id, postData) + presenter.editDiscussion((discussionTopicHeader as DiscussionTopicHeader).id, postData) } else { val discussionTopicHeader = DiscussionTopicHeader() @@ -626,15 +628,15 @@ class CreateDiscussionFragment : BasePresenterFragment< } discussionTopicHeader.message = descriptionRCEView.html - discussionTopicHeader.published = mIsPublished - discussionTopicHeader.subscribed = mIsSubscribed - discussionTopicHeader.type = if (mAllowThreaded) DiscussionTopicHeader.DiscussionType.THREADED else DiscussionTopicHeader.DiscussionType.SIDE_COMMENT - discussionTopicHeader.requireInitialPost = mUsersMustPost + discussionTopicHeader.published = isPublished + discussionTopicHeader.subscribed = isSubscribed + discussionTopicHeader.type = if (allowThreaded) DiscussionTopicHeader.DiscussionType.THREADED else DiscussionTopicHeader.DiscussionType.SIDE_COMMENT + discussionTopicHeader.requireInitialPost = usersMustPost // If the assignment is null, that means we're creating/editing a discussion. When we do this we initialize mEditDateGroups with an empty DueDateGroup if (presenter.getAssignment() == null) { - discussionTopicHeader.delayedPostDate = mEditDateGroups[0].coreDates.unlockDate - discussionTopicHeader.lockAt = mEditDateGroups[0].coreDates.lockDate + discussionTopicHeader.delayedPostDate = editDateGroups[0].coreDates.unlockDate + discussionTopicHeader.lockAt = editDateGroups[0].coreDates.lockDate } presenter.saveDiscussion(discussionTopicHeader) } @@ -645,9 +647,9 @@ class CreateDiscussionFragment : BasePresenterFragment< } override fun onSaveInstanceState(outState: Bundle) { - outState.putSerializable(EDIT_DATE_GROUPS, ArrayList(mEditDateGroups)) + outState.putSerializable(EDIT_DATE_GROUPS, ArrayList(editDateGroups)) outState.putBoolean(RCE_HAS_FOCUS, binding.descriptionRCEView.hasFocus()) - mDescription = binding.descriptionRCEView.html + description = binding.descriptionRCEView.html super.onSaveInstanceState(outState) } @@ -656,7 +658,7 @@ class CreateDiscussionFragment : BasePresenterFragment< fun onAssigneesChanged(event: AssigneesUpdatedEvent) { // Update grouped due dates (EditDateGroups) event.once(javaClass.simpleName) { dates -> - mEditDateGroups = dates + editDateGroups = dates setupOverrides() // Remove it so when we go to another assignment or discussion it won't show up there too EventBus.getDefault().removeStickyEvent(event) diff --git a/apps/teacher/src/main/java/com/instructure/teacher/fragments/CreateOrEditAnnouncementFragment.kt b/apps/teacher/src/main/java/com/instructure/teacher/fragments/CreateOrEditAnnouncementFragment.kt index 202176d98f..0811ef0dc7 100644 --- a/apps/teacher/src/main/java/com/instructure/teacher/fragments/CreateOrEditAnnouncementFragment.kt +++ b/apps/teacher/src/main/java/com/instructure/teacher/fragments/CreateOrEditAnnouncementFragment.kt @@ -28,6 +28,7 @@ import com.instructure.canvasapi2.models.Course import com.instructure.canvasapi2.models.DiscussionTopicHeader import com.instructure.canvasapi2.models.postmodels.FileSubmitObject import com.instructure.canvasapi2.utils.DateHelper +import com.instructure.canvasapi2.utils.pageview.PageView import com.instructure.canvasapi2.utils.parcelCopy import com.instructure.interactions.Identity import com.instructure.pandautils.analytics.SCREEN_VIEW_CREATE_OR_EDIT_ANNOUNCEMENT @@ -58,6 +59,7 @@ import org.greenrobot.eventbus.Subscribe import org.greenrobot.eventbus.ThreadMode import java.util.* +@PageView("courses/{canvasContext}/discussion_topics/new?is_announcement=true") @ScreenView(SCREEN_VIEW_CREATE_OR_EDIT_ANNOUNCEMENT) class CreateOrEditAnnouncementFragment : BasePresenterFragment(), @@ -68,24 +70,24 @@ class CreateOrEditAnnouncementFragment : private val binding by viewBinding(FragmentCreateOrEditAnnouncementBinding::bind) /* The course this announcement belongs to */ - private var mCanvasContext by ParcelableArg(Course()) + private var canvasContext by ParcelableArg(Course()) /* The announcement to be edited. This will be null if we're creating a new announcement */ - private var mEditAnnouncement by NullableParcelableArg() + private var editAnnouncement by NullableParcelableArg() /* Menu buttons. We don't cache these because the toolbar is reconstructed on configuration change. */ - private val mSaveMenuButton get() = binding.toolbar.menu.findItem(R.id.menuSaveAnnouncement) - private val mAttachmentButton get() = binding.toolbar.menu.findItem(R.id.menuAddAttachment) - private val mSaveButtonTextView: TextView? get() = view?.findViewById(R.id.menuSaveAnnouncement) + private val saveMenuButton get() = binding.toolbar.menu.findItem(R.id.menuSaveAnnouncement) + private val attachmentButton get() = binding.toolbar.menu.findItem(R.id.menuAddAttachment) + private val saveButtonTextView: TextView? get() = view?.findViewById(R.id.menuSaveAnnouncement) /* Formats for displaying the delayed post date */ - private val mDateFormat by lazy { DateHelper.fullMonthNoLeadingZeroDateFormat } - private val mTimeFormat by lazy { DateHelper.getPreferredTimeFormat(requireContext()) } + private val dateFormat by lazy { DateHelper.fullMonthNoLeadingZeroDateFormat } + private val timeFormat by lazy { DateHelper.getPreferredTimeFormat(requireContext()) } private var placeHolderList: ArrayList = ArrayList() /* The default date to show when the user enables delayed posting (the current date just before midnight) */ - private val mDefaultDate: Date + private val defaultDate: Date get() = Calendar.getInstance().apply { set(Calendar.HOUR_OF_DAY, 23) set(Calendar.MINUTE, 59) @@ -101,7 +103,7 @@ class CreateOrEditAnnouncementFragment : override fun onPresenterPrepared(presenter: CreateOrEditAnnouncementPresenter) {} override fun layoutResId(): Int = R.layout.fragment_create_or_edit_announcement - override fun getPresenterFactory() = CreateOrEditAnnouncementPresenterFactory(mCanvasContext, mEditAnnouncement?.parcelCopy()) + override fun getPresenterFactory() = CreateOrEditAnnouncementPresenterFactory(canvasContext, editAnnouncement?.parcelCopy()) override fun onStart() { super.onStart() @@ -162,12 +164,12 @@ class CreateOrEditAnnouncementFragment : ViewStyler.themeToolbarLight(requireActivity(), toolbar) ViewStyler.setToolbarElevationSmall(requireContext(), toolbar) - if (presenter.isEditing) with(mSaveMenuButton) { + if (presenter.isEditing) with(saveMenuButton) { setIcon(0) setTitle(R.string.save) } - mSaveButtonTextView?.setTextColor(ThemePrefs.textButtonColor) + saveButtonTextView?.setTextColor(ThemePrefs.textButtonColor) } private fun setupViews() { @@ -238,7 +240,7 @@ class CreateOrEditAnnouncementFragment : updatePostDate() delaySwitch.setOnCheckedChangeListener { _, isChecked -> - presenter.announcement.delayedPostDate = if (isChecked) mDefaultDate else null + presenter.announcement.delayedPostDate = if (isChecked) defaultDate else null updatePostDate() } @@ -305,8 +307,8 @@ class CreateOrEditAnnouncementFragment : postDateWrapper.setGone() } else { postDateWrapper.setVisible() - postDate.setText(mDateFormat.format(date)) - postTime.setText(mTimeFormat.format(date)) + postDate.setText(dateFormat.format(date)) + postTime.setText(timeFormat.format(date)) } } @@ -364,7 +366,7 @@ class CreateOrEditAnnouncementFragment : } override fun onSaveStarted() { - mSaveMenuButton.isVisible = false + saveMenuButton.isVisible = false updateAttachmentButton(show = false) binding.savingProgressBar.announceForAccessibility(getString(R.string.saving)) binding.savingProgressBar.setVisible() @@ -372,18 +374,18 @@ class CreateOrEditAnnouncementFragment : private fun updateAttachmentButton(show: Boolean = true) { // Only show if (1) we're in creation mode and (2) we don't already have an attachment - mAttachmentButton?.isVisible = show && !presenter.isEditing && presenter.attachment == null + attachmentButton?.isVisible = show && !presenter.isEditing && presenter.attachment == null } override fun onSaveError() { - mSaveMenuButton.isVisible = true + saveMenuButton.isVisible = true updateAttachmentButton() binding.savingProgressBar.setGone() toast(R.string.errorSavingAnnouncement) } override fun onDeleteError() { - mSaveMenuButton.isVisible = true + saveMenuButton.isVisible = true updateAttachmentButton() binding.savingProgressBar.setGone() toast(R.string.errorDeletingAnnouncement) @@ -445,13 +447,13 @@ class CreateOrEditAnnouncementFragment : fun newInstanceCreate(canvasContext: CanvasContext) = CreateOrEditAnnouncementFragment().apply { - mCanvasContext = canvasContext + this.canvasContext = canvasContext } fun newInstanceEdit(canvasContext: CanvasContext, editAnnouncement: DiscussionTopicHeader) = CreateOrEditAnnouncementFragment().apply { - mCanvasContext = canvasContext - mEditAnnouncement = editAnnouncement + this.canvasContext = canvasContext + this.editAnnouncement = editAnnouncement } } } diff --git a/apps/teacher/src/main/java/com/instructure/teacher/fragments/CreateOrEditPageDetailsFragment.kt b/apps/teacher/src/main/java/com/instructure/teacher/fragments/CreateOrEditPageDetailsFragment.kt index 72f582b5b3..d922083ded 100644 --- a/apps/teacher/src/main/java/com/instructure/teacher/fragments/CreateOrEditPageDetailsFragment.kt +++ b/apps/teacher/src/main/java/com/instructure/teacher/fragments/CreateOrEditPageDetailsFragment.kt @@ -29,11 +29,14 @@ import androidx.appcompat.app.AlertDialog import com.instructure.canvasapi2.models.CanvasContext import com.instructure.canvasapi2.models.Course import com.instructure.canvasapi2.models.Page +import com.instructure.canvasapi2.utils.ApiPrefs import com.instructure.canvasapi2.models.Page.Companion.ANYONE import com.instructure.canvasapi2.models.Page.Companion.GROUP_MEMBERS import com.instructure.canvasapi2.models.Page.Companion.STUDENTS import com.instructure.canvasapi2.models.Page.Companion.TEACHERS import com.instructure.canvasapi2.utils.isValid +import com.instructure.canvasapi2.utils.pageview.PageView +import com.instructure.canvasapi2.utils.pageview.PageViewUrl import com.instructure.canvasapi2.utils.parcelCopy import com.instructure.interactions.Identity import com.instructure.pandautils.analytics.SCREEN_VIEW_CREATE_OR_EDIT_PAGE_DETAILS @@ -53,6 +56,7 @@ import com.instructure.teacher.utils.setupMenu import com.instructure.teacher.utils.withRequireNetwork import com.instructure.teacher.viewinterface.CreateOrEditPageView +@PageView @ScreenView(SCREEN_VIEW_CREATE_OR_EDIT_PAGE_DETAILS) class CreateOrEditPageDetailsFragment : BasePresenterFragment(), @@ -62,14 +66,14 @@ class CreateOrEditPageDetailsFragment : private val binding by viewBinding(FragmentCreateOrEditPageBinding::bind) /* The course this page belongs to */ - private var mCanvasContext by ParcelableArg(Course()) + private var canvasContext by ParcelableArg(Course()) /* The page to be edited. This will be null if we're creating a new page */ - private var mPage by NullableParcelableArg() + private var page by NullableParcelableArg() /* Menu buttons. We don't cache these because the toolbar is reconstructed on configuration change. */ - private val mSaveMenuButton get() = binding.toolbar.menu.findItem(R.id.menuSavePage) - private val mSaveButtonTextView: TextView? get() = view?.findViewById(R.id.menuSavePage) + private val saveMenuButton get() = binding.toolbar.menu.findItem(R.id.menuSavePage) + private val saveButtonTextView: TextView? get() = view?.findViewById(R.id.menuSavePage) private var placeHolderList: ArrayList = ArrayList() private var forceQuit = false @@ -82,7 +86,18 @@ class CreateOrEditPageDetailsFragment : override fun onPresenterPrepared(presenter: CreateOrEditPagePresenter) {} override fun layoutResId(): Int = R.layout.fragment_create_or_edit_page - override fun getPresenterFactory() = CreateOrEditPagePresenterFactory(mCanvasContext, mPage?.parcelCopy()) + @PageViewUrl + @Suppress("unused") + private fun makePageViewUrl(): String { + val url = StringBuilder(ApiPrefs.fullDomain) + page.let { + url.append(canvasContext.toAPIString()) + if (it?.frontPage == false) url.append("/pages/${it.url}/edit") + } + return url.toString() + } + + override fun getPresenterFactory() = CreateOrEditPagePresenterFactory(canvasContext, page?.parcelCopy()) override fun onReadySetGo(presenter: CreateOrEditPagePresenter) { setupViews() @@ -101,11 +116,11 @@ class CreateOrEditPageDetailsFragment : } ViewStyler.themeToolbarLight(requireActivity(), toolbar) ViewStyler.setToolbarElevationSmall(requireContext(), toolbar) - with(mSaveMenuButton) { + with(saveMenuButton) { setIcon(0) setTitle(R.string.save) } - mSaveButtonTextView?.setTextColor(ThemePrefs.textButtonColor) + saveButtonTextView?.setTextColor(ThemePrefs.textButtonColor) } private fun shouldAllowExit() : Boolean = with(binding) { @@ -116,12 +131,11 @@ class CreateOrEditPageDetailsFragment : return true } // Check if edited page has changes - if (presenter.page.id != 0L && - (presenter.page.body.orEmpty()) == pageRCEView.html && - (mPage?.title.orEmpty()) == pageNameEditText.text.toString() && - mPage?.frontPage == frontPageSwitch.isChecked && - mPage?.published == publishSwitch.isChecked - ) { + if(presenter.page.id != 0L && + presenter.page.body.orEmpty() == pageRCEView.html && + page?.title.orEmpty() == pageNameEditText.text.toString() && + page?.frontPage == frontPageSwitch.isChecked && + page?.published == publishSwitch.isChecked) { return true } return false @@ -196,7 +210,7 @@ class CreateOrEditPageDetailsFragment : } private fun setupCanEditSpinner() = with(binding) { - val spinnerAdapter = if(mCanvasContext.type == CanvasContext.Type.GROUP) { + val spinnerAdapter = if(canvasContext.type == CanvasContext.Type.GROUP) { ArrayAdapter.createFromResource(requireContext(), R.array.canEditRolesWithGroups, R.layout.simple_spinner_item) } else { ArrayAdapter.createFromResource(requireContext(), R.array.canEditRolesNoGroups, R.layout.simple_spinner_item) @@ -236,7 +250,7 @@ class CreateOrEditPageDetailsFragment : private fun setupPublishSwitch() = with(binding) { // If it's the front page we can't unpublish it - publishWrapper.setVisible(!(mPage != null && (mPage as Page).frontPage)) + publishWrapper.setVisible(!(page != null && (page as Page).frontPage)) // Publish status publishSwitch.applyTheme() @@ -246,14 +260,14 @@ class CreateOrEditPageDetailsFragment : } private fun setupDelete() { - binding.deleteWrapper.setVisible((mPage != null && !(mPage as Page).frontPage)) + binding.deleteWrapper.setVisible((page != null && !(page as Page).frontPage)) binding.deleteWrapper.onClickWithRequireNetwork { AlertDialog.Builder(requireContext()) .setTitle(R.string.pageDeleteTitle) .setMessage(R.string.pageDeleteMessage) .setPositiveButton(R.string.delete) { _, _ -> - if(mPage != null) { - presenter.deletePage(mPage!!.url!!) + if(page != null) { + presenter.deletePage(page!!.url!!) } } .setNegativeButton(R.string.cancel) { _, _ -> } @@ -285,13 +299,13 @@ class CreateOrEditPageDetailsFragment : } override fun onSaveStarted() { - mSaveMenuButton.isVisible = false + saveMenuButton.isVisible = false binding.savingProgressBar.announceForAccessibility(getString(R.string.saving)) binding.savingProgressBar.setVisible() } override fun onSaveError() { - mSaveMenuButton.isVisible = true + saveMenuButton.isVisible = true binding.savingProgressBar.setGone() toast(R.string.errorSavingPage) } @@ -329,13 +343,13 @@ class CreateOrEditPageDetailsFragment : } fun newInstanceCreate(canvasContext: CanvasContext) = CreateOrEditPageDetailsFragment().apply { - mCanvasContext = canvasContext + this.canvasContext = canvasContext } fun newInstanceEdit(canvasContext: CanvasContext, page: Page) = CreateOrEditPageDetailsFragment().apply { - mCanvasContext = canvasContext - mPage = page + this.canvasContext = canvasContext + this.page = page } } } diff --git a/apps/teacher/src/main/java/com/instructure/teacher/fragments/DashboardFragment.kt b/apps/teacher/src/main/java/com/instructure/teacher/fragments/DashboardFragment.kt index 1459d99763..2e14c25ac3 100644 --- a/apps/teacher/src/main/java/com/instructure/teacher/fragments/DashboardFragment.kt +++ b/apps/teacher/src/main/java/com/instructure/teacher/fragments/DashboardFragment.kt @@ -26,6 +26,7 @@ import androidx.recyclerview.widget.GridLayoutManager import androidx.recyclerview.widget.RecyclerView import com.instructure.canvasapi2.models.Course import com.instructure.canvasapi2.utils.ApiPrefs +import com.instructure.canvasapi2.utils.pageview.PageView import com.instructure.pandautils.analytics.SCREEN_VIEW_DASHBOARD import com.instructure.pandautils.analytics.ScreenView import com.instructure.pandautils.binding.viewBinding @@ -54,6 +55,7 @@ import org.greenrobot.eventbus.ThreadMode private const val LIST_SPAN_COUNT = 1 +@PageView @ScreenView(SCREEN_VIEW_DASHBOARD) class DashboardFragment : BaseSyncFragment(), CoursesView { diff --git a/apps/teacher/src/main/java/com/instructure/teacher/fragments/DiscussionsDetailsFragment.kt b/apps/teacher/src/main/java/com/instructure/teacher/fragments/DiscussionsDetailsFragment.kt index 85e7d1731c..3162f8ac87 100644 --- a/apps/teacher/src/main/java/com/instructure/teacher/fragments/DiscussionsDetailsFragment.kt +++ b/apps/teacher/src/main/java/com/instructure/teacher/fragments/DiscussionsDetailsFragment.kt @@ -28,6 +28,8 @@ import androidx.appcompat.app.AlertDialog import androidx.core.content.ContextCompat import com.instructure.canvasapi2.models.* import com.instructure.canvasapi2.utils.* +import com.instructure.canvasapi2.utils.pageview.PageView +import com.instructure.canvasapi2.utils.pageview.PageViewUrlParam import com.instructure.canvasapi2.utils.weave.WeaveJob import com.instructure.canvasapi2.utils.weave.catch import com.instructure.canvasapi2.utils.weave.tryWeave @@ -44,6 +46,7 @@ import com.instructure.pandautils.discussions.DiscussionEntryHtmlConverter import com.instructure.pandautils.discussions.DiscussionUtils import com.instructure.pandautils.fragments.BasePresenterFragment import com.instructure.pandautils.utils.* +import com.instructure.pandautils.utils.Const import com.instructure.pandautils.views.CanvasWebView import com.instructure.teacher.BuildConfig import com.instructure.teacher.R @@ -66,6 +69,7 @@ import org.greenrobot.eventbus.Subscribe import org.greenrobot.eventbus.ThreadMode import java.util.* +@PageView(url = "{canvasContext}/discussion_topics/{topicId}") @ScreenView(SCREEN_VIEW_DISCUSSION_DETAILS) class DiscussionsDetailsFragment : BasePresenterFragment< DiscussionsDetailsPresenter, @@ -74,17 +78,16 @@ class DiscussionsDetailsFragment : BasePresenterFragment< private val binding by viewBinding(FragmentDiscussionsDetailsBinding::bind) //region Member Variables + private var canvasContext: CanvasContext by ParcelableArg(key = Const.CANVAS_CONTEXT) + private var discussionTopicHeader: DiscussionTopicHeader by ParcelableArg(DiscussionTopicHeader(), DISCUSSION_TOPIC_HEADER) + private var discussionTopic: DiscussionTopic by ParcelableArg(DiscussionTopic(), DISCUSSION_TOPIC) + private var discussionEntryId: Long by LongArg(0L, DISCUSSION_ENTRY_ID) + private var discussionTopicHeaderId: Long by LongArg(0L, DISCUSSION_TOPIC_HEADER_ID) + private var skipIdentityCheck: Boolean by BooleanArg(false, SKIP_IDENTITY_CHECK) + private var skipId: String by StringArg("", SKIP_ID) - private var mCanvasContext: CanvasContext by ParcelableArg(Course()) - private var mDiscussionTopicHeader: DiscussionTopicHeader by ParcelableArg(DiscussionTopicHeader(), DISCUSSION_TOPIC_HEADER) - private var mDiscussionTopic: DiscussionTopic by ParcelableArg(DiscussionTopic(), DISCUSSION_TOPIC) - private var mDiscussionEntryId: Long by LongArg(0L, DISCUSSION_ENTRY_ID) - private var mDiscussionTopicHeaderId: Long by LongArg(0L, DISCUSSION_TOPIC_HEADER_ID) - private var mSkipIdentityCheck: Boolean by BooleanArg(false, SKIP_IDENTITY_CHECK) - private var mSkipId: String by StringArg("", SKIP_ID) - - private var mIsAnnouncements: Boolean by BooleanArg(false, IS_ANNOUNCEMENT) - private var mIsNestedDetail: Boolean by BooleanArg(false, IS_NESTED_DETAIL) + private var isAnnouncements: Boolean by BooleanArg(false, IS_ANNOUNCEMENT) + private var isNestedDetail: Boolean by BooleanArg(false, IS_NESTED_DETAIL) private var repliesLoadHtmlJob: Job? = null private var headerLoadHtmlJob: Job? = null @@ -92,6 +95,10 @@ class DiscussionsDetailsFragment : BasePresenterFragment< //endregion + @Suppress("unused") + @PageViewUrlParam("topicId") + private fun getTopicId() = discussionTopicHeader.id + override fun layoutResId(): Int = R.layout.fragment_discussions_details override fun onRefreshFinished() { @@ -119,12 +126,12 @@ class DiscussionsDetailsFragment : BasePresenterFragment< headerLoadHtmlJob?.cancel() } - override val identity: Long? get() = if(mDiscussionTopicHeaderId != 0L) mDiscussionTopicHeaderId else mDiscussionTopicHeader.id - override val skipCheck: Boolean get() = mSkipIdentityCheck + override val identity: Long? get() = if(discussionTopicHeaderId != 0L) discussionTopicHeaderId else discussionTopicHeader.id + override val skipCheck: Boolean get() = skipIdentityCheck override fun getPresenterFactory() = - DiscussionsDetailsPresenterFactory(mCanvasContext, mDiscussionTopicHeader, mDiscussionTopic, - if(mSkipId.isEmpty()) DiscussionsDetailsFragment::class.java.simpleName + UUID.randomUUID().toString() else mSkipId) + DiscussionsDetailsPresenterFactory(canvasContext, discussionTopicHeader, discussionTopic, + if(skipId.isEmpty()) DiscussionsDetailsFragment::class.java.simpleName + UUID.randomUUID().toString() else skipId) override fun onPresenterPrepared(presenter: DiscussionsDetailsPresenter) {} @@ -140,24 +147,24 @@ class DiscussionsDetailsFragment : BasePresenterFragment< discussionTopicEvent.only(presenter.getSkipId()) { discussionTopic -> //A The Discussion Topic was changed in some way. Usually from a nested situation where something was added. presenter.updateDiscussionTopic(discussionTopic) - if (!mIsNestedDetail) { + if (!isNestedDetail) { EventBus.getDefault().removeStickyEvent(discussionTopicEvent) } } } else { - if (mDiscussionTopicHeaderId == 0L && presenter.discussionTopicHeader.id != 0L) { + if (discussionTopicHeaderId == 0L && presenter.discussionTopicHeader.id != 0L) { //We were given a valid DiscussionTopicHeader, no need to fetch from the API populateDiscussionTopicHeader(presenter.discussionTopicHeader, false) - } else if (mDiscussionTopicHeaderId != 0L) { + } else if (discussionTopicHeaderId != 0L) { //results of this GET will call populateDiscussionTopicHeader() - presenter.getDiscussionTopicHeader(mDiscussionTopicHeaderId) + presenter.getDiscussionTopicHeader(discussionTopicHeaderId) } } EventBus.getDefault().getStickyEvent(DiscussionTopicHeaderDeletedEvent::class.java)?.once(javaClass.simpleName + ".onResume()") { if (it == presenter.discussionTopicHeader.id) { if (activity is MasterDetailInteractions) { - (activity as MasterDetailInteractions).popFragment(mCanvasContext) + (activity as MasterDetailInteractions).popFragment(canvasContext) } else if(activity is FullScreenInteractions) { requireActivity().finish() } @@ -181,7 +188,7 @@ class DiscussionsDetailsFragment : BasePresenterFragment< } // Publish status if discussion - if(!mIsAnnouncements) { + if(!isAnnouncements) { if (discussionTopicHeader.published) { publishStatusIconView.setImageResource(R.drawable.ic_complete_solid) publishStatusIconView.setColorFilter(requireContext().getColorCompat(R.color.textSuccess)) @@ -208,7 +215,7 @@ class DiscussionsDetailsFragment : BasePresenterFragment< } loadDiscussionTopicHeader(discussionTopicHeader) - repliesBack.setVisible(mIsNestedDetail) + repliesBack.setVisible(isNestedDetail) repliesBack.onClick { requireActivity().onBackPressed() } attachmentIcon.setVisible(!discussionTopicHeader.attachments.isEmpty()) attachmentIcon.onClick { @@ -245,10 +252,10 @@ class DiscussionsDetailsFragment : BasePresenterFragment< DiscussionUtils.createDiscussionTopicHtml( requireActivity(), isTablet, - mCanvasContext, + canvasContext, discussionTopicHeader, discussionTopic.views, - mDiscussionEntryId) + discussionEntryId) } discussionRepliesWebViewWrapper.setInvisible() @@ -285,7 +292,7 @@ class DiscussionsDetailsFragment : BasePresenterFragment< NumberHelper.formatDecimal(assignment.pointsPossible, 1, true)) dueLayout.setVisible() - submissionsLayout.setVisible((mCanvasContext as? Course)?.isDesigner() == false) + submissionsLayout.setVisible((canvasContext as? Course)?.isDesigner() == false) //set these as gone and make them visible if we have data for them availabilityLayout.setGone() @@ -367,20 +374,20 @@ class DiscussionsDetailsFragment : BasePresenterFragment< private fun setupListeners() = with(binding) { dueLayout.setOnClickListener { val args = DueDatesFragment.makeBundle(presenter.discussionTopicHeader.assignment!!) - RouteMatcher.route(requireContext(), Route(null, DueDatesFragment::class.java, mCanvasContext, args)) + RouteMatcher.route(requireContext(), Route(null, DueDatesFragment::class.java, canvasContext, args)) } submissionsLayout.setOnClickListener { - navigateToSubmissions(mCanvasContext, presenter.discussionTopicHeader.assignment!!, AssignmentSubmissionListPresenter.SubmissionListFilter.ALL) + navigateToSubmissions(canvasContext, presenter.discussionTopicHeader.assignment!!, AssignmentSubmissionListPresenter.SubmissionListFilter.ALL) } binding.donutGroup.viewAllSubmissions.onClick { submissionsLayout.performClick() } // Separate click listener for a11y binding.donutGroup.gradedWrapper.setOnClickListener { - navigateToSubmissions(mCanvasContext, presenter.discussionTopicHeader.assignment!!, AssignmentSubmissionListPresenter.SubmissionListFilter.GRADED) + navigateToSubmissions(canvasContext, presenter.discussionTopicHeader.assignment!!, AssignmentSubmissionListPresenter.SubmissionListFilter.GRADED) } binding.donutGroup.ungradedWrapper.setOnClickListener { - navigateToSubmissions(mCanvasContext, presenter.discussionTopicHeader.assignment!!, AssignmentSubmissionListPresenter.SubmissionListFilter.NOT_GRADED) + navigateToSubmissions(canvasContext, presenter.discussionTopicHeader.assignment!!, AssignmentSubmissionListPresenter.SubmissionListFilter.NOT_GRADED) } binding.donutGroup.notSubmittedWrapper.setOnClickListener { - navigateToSubmissions(mCanvasContext, presenter.discussionTopicHeader.assignment!!, AssignmentSubmissionListPresenter.SubmissionListFilter.MISSING) + navigateToSubmissions(canvasContext, presenter.discussionTopicHeader.assignment!!, AssignmentSubmissionListPresenter.SubmissionListFilter.MISSING) } } @@ -394,7 +401,7 @@ class DiscussionsDetailsFragment : BasePresenterFragment< ProfileUtils.loadAvatarForUser(authorAvatar, displayName, discussionTopicHeader.author?.avatarImageUrl) authorAvatar.setupAvatarA11y(discussionTopicHeader.author?.displayName) authorAvatar.onClick { - val bundle = StudentContextFragment.makeBundle(discussionTopicHeader.author?.id ?: 0, mCanvasContext.id) + val bundle = StudentContextFragment.makeBundle(discussionTopicHeader.author?.id ?: 0, canvasContext.id) RouteMatcher.route(requireContext(), Route(StudentContextFragment::class.java, null, bundle)) } authorName?.text = discussionTopicHeader.author?.let { Pronouns.span(it.displayName, it.pronouns) } @@ -408,7 +415,7 @@ class DiscussionsDetailsFragment : BasePresenterFragment< } headerLoadHtmlJob = discussionTopicHeaderWebViewWrapper.webView.loadHtmlWithIframes(requireContext(), discussionTopicHeader.message, { - discussionTopicHeaderWebViewWrapper.loadHtml(it, discussionTopicHeader.title, baseUrl = mDiscussionTopicHeader.htmlUrl) + discussionTopicHeaderWebViewWrapper.loadHtml(it, discussionTopicHeader.title, baseUrl = this@DiscussionsDetailsFragment.discussionTopicHeader.htmlUrl) }) { LtiLaunchFragment.routeLtiLaunchFragment(requireContext(), canvasContext, it) } @@ -427,10 +434,10 @@ class DiscussionsDetailsFragment : BasePresenterFragment< super.onResume() setupToolbar() - if (isAccessibilityEnabled(requireContext()) && mDiscussionTopicHeader.htmlUrl != null) { + if (isAccessibilityEnabled(requireContext()) && discussionTopicHeader.htmlUrl != null) { alternateViewButton.visibility = View.VISIBLE alternateViewButton.setOnClickListener { - val bundle = InternalWebViewFragment.makeBundle(mDiscussionTopicHeader.htmlUrl!!, mDiscussionTopicHeader.title!!, shouldAuthenticate = true) + val bundle = InternalWebViewFragment.makeBundle(discussionTopicHeader.htmlUrl!!, discussionTopicHeader.title!!, shouldAuthenticate = true) RouteMatcher.route(requireActivity(), Route(null, InternalWebViewFragment::class.java, canvasContext, bundle)) } } @@ -460,29 +467,29 @@ class DiscussionsDetailsFragment : BasePresenterFragment< private fun setupToolbar() = with(binding) { toolbar.setupBackButtonWithExpandCollapseAndBack(this@DiscussionsDetailsFragment) { toolbar.updateToolbarExpandCollapseIcon(this@DiscussionsDetailsFragment) - ViewStyler.themeToolbarColored(requireActivity(), toolbar, mCanvasContext.backgroundColor, requireContext().getColor(R.color.white)) + ViewStyler.themeToolbarColored(requireActivity(), toolbar, canvasContext.backgroundColor, requireContext().getColor(R.color.white)) (activity as MasterDetailInteractions).toggleExpandCollapse() } toolbar.setupMenu(R.menu.menu_edit_generic, menuItemCallback) - toolbar.title = if(mIsAnnouncements) getString(R.string.announcementDetails) else getString(R.string.discussion_details) + toolbar.title = if(isAnnouncements) getString(R.string.announcementDetails) else getString(R.string.discussion_details) if(!isTablet) { - toolbar.subtitle = mCanvasContext.name + toolbar.subtitle = canvasContext.name } - ViewStyler.themeToolbarColored(requireActivity(), toolbar, mCanvasContext.backgroundColor, requireContext().getColor(R.color.white)) + ViewStyler.themeToolbarColored(requireActivity(), toolbar, canvasContext.backgroundColor, requireContext().getColor(R.color.white)) } private val menuItemCallback: (MenuItem) -> Unit = { item -> when (item.itemId) { R.id.menu_edit -> { if(APIHelper.hasNetworkConnection()) { - if(mIsAnnouncements) { + if(isAnnouncements) { val args = CreateOrEditAnnouncementFragment.newInstanceEdit(presenter.canvasContext, presenter.discussionTopicHeader).nonNullArgs RouteMatcher.route(requireContext(), Route(CreateOrEditAnnouncementFragment::class.java, null, args)) } else { // If we have an assignment, set the topic header to null to prevent cyclic reference presenter.discussionTopicHeader.assignment?.discussionTopicHeader = null val args = CreateDiscussionFragment.makeBundle(presenter.canvasContext, presenter.discussionTopicHeader) - RouteMatcher.route(requireContext(), Route(CreateDiscussionFragment::class.java, mCanvasContext, args)) + RouteMatcher.route(requireContext(), Route(CreateDiscussionFragment::class.java, canvasContext, args)) } } else { NoInternetConnectionDialog.show(requireFragmentManager()) @@ -536,7 +543,7 @@ class DiscussionsDetailsFragment : BasePresenterFragment< @JavascriptInterface fun onAvatarPressed(id: String) { presenter.findEntry(id.toLong())?.let { entry -> - val bundle = StudentContextFragment.makeBundle(entry.author!!.id, mCanvasContext.id) + val bundle = StudentContextFragment.makeBundle(entry.author!!.id, canvasContext.id) RouteMatcher.route(requireContext(), Route(StudentContextFragment::class.java, null, bundle)) } } @@ -567,7 +574,7 @@ class DiscussionsDetailsFragment : BasePresenterFragment< @JavascriptInterface fun onMoreRepliesPressed(id: String) { val args = makeBundle(presenter.discussionTopicHeader, presenter.discussionTopic, id.toLong(), presenter.getSkipId()) - RouteMatcher.route(requireContext(), Route(null, DiscussionsDetailsFragment::class.java, mCanvasContext, args)) + RouteMatcher.route(requireContext(), Route(null, DiscussionsDetailsFragment::class.java, canvasContext, args)) } @JavascriptInterface @@ -635,7 +642,7 @@ class DiscussionsDetailsFragment : BasePresenterFragment< private fun showReplyView(id: Long) { if (APIHelper.hasNetworkConnection()) { - val args = DiscussionsReplyFragment.makeBundle(presenter.discussionTopicHeader.id, id, mIsAnnouncements) + val args = DiscussionsReplyFragment.makeBundle(presenter.discussionTopicHeader.id, id, isAnnouncements) RouteMatcher.route(requireContext(), Route(DiscussionsReplyFragment::class.java, presenter.canvasContext, args)) } else { NoInternetConnectionDialog.show(requireFragmentManager()) @@ -658,7 +665,7 @@ class DiscussionsDetailsFragment : BasePresenterFragment< private fun showUpdateReplyView(id: Long) { if (APIHelper.hasNetworkConnection()) { - val args = DiscussionsUpdateFragment.makeBundle(presenter.discussionTopicHeader.id, presenter.findEntry(id), mIsAnnouncements, presenter.discussionTopic) + val args = DiscussionsUpdateFragment.makeBundle(presenter.discussionTopicHeader.id, presenter.findEntry(id), isAnnouncements, presenter.discussionTopic) RouteMatcher.route(requireContext(), Route(DiscussionsUpdateFragment::class.java, presenter.canvasContext, args)) } else { NoInternetConnectionDialog.show(requireFragmentManager()) @@ -702,11 +709,11 @@ class DiscussionsDetailsFragment : BasePresenterFragment< override fun showAnonymousDiscussionView() = with(binding) { anonymousDiscussionsNotSupported.setVisible() - openInBrowser.setVisible(mDiscussionTopicHeader.htmlUrl?.isNotEmpty() == true) + openInBrowser.setVisible(discussionTopicHeader.htmlUrl?.isNotEmpty() == true) replyToDiscussionTopic.setGone() swipeRefreshLayout.isEnabled = false openInBrowser.onClick { - mDiscussionTopicHeader.htmlUrl?.let { url -> + discussionTopicHeader.htmlUrl?.let { url -> requireContext().startActivity(InternalWebViewActivity.createIntent(requireContext(), url, "", true)) } } @@ -759,7 +766,7 @@ class DiscussionsDetailsFragment : BasePresenterFragment< event.once(javaClass.simpleName + ".onPost()") { if (it == presenter.discussionTopicHeader.id) { if(activity is MasterDetailInteractions) { - (activity as MasterDetailInteractions).popFragment(mCanvasContext) + (activity as MasterDetailInteractions).popFragment(canvasContext) } } else if(activity is FullScreenInteractions) { requireActivity().finish() @@ -822,7 +829,7 @@ class DiscussionsDetailsFragment : BasePresenterFragment< } @JvmStatic fun newInstance(canvasContext: CanvasContext, args: Bundle) = DiscussionsDetailsFragment().withArgs(args).apply { - mCanvasContext = canvasContext + this.canvasContext = canvasContext } } } diff --git a/apps/teacher/src/main/java/com/instructure/teacher/fragments/DiscussionsListFragment.kt b/apps/teacher/src/main/java/com/instructure/teacher/fragments/DiscussionsListFragment.kt index 100fee2d75..592cb9721e 100644 --- a/apps/teacher/src/main/java/com/instructure/teacher/fragments/DiscussionsListFragment.kt +++ b/apps/teacher/src/main/java/com/instructure/teacher/fragments/DiscussionsListFragment.kt @@ -23,6 +23,7 @@ import androidx.recyclerview.widget.RecyclerView import com.instructure.canvasapi2.models.CanvasContext import com.instructure.canvasapi2.models.DiscussionTopicHeader import com.instructure.canvasapi2.utils.ApiPrefs +import com.instructure.canvasapi2.utils.pageview.PageView import com.instructure.interactions.router.Route import com.instructure.pandautils.analytics.SCREEN_VIEW_DISCUSSION_LIST import com.instructure.pandautils.analytics.ScreenView @@ -44,6 +45,7 @@ import org.greenrobot.eventbus.EventBus import org.greenrobot.eventbus.Subscribe import org.greenrobot.eventbus.ThreadMode +@PageView(url = "{canvasContext}/discussion_topics") @ScreenView(SCREEN_VIEW_DISCUSSION_LIST) open class DiscussionsListFragment : BaseExpandableSyncFragment< String, @@ -55,22 +57,22 @@ open class DiscussionsListFragment : BaseExpandableSyncFragment< private val binding by viewBinding(FragmentDiscussionListBinding::bind) - protected var mCanvasContext: CanvasContext by ParcelableArg(default = CanvasContext.getGenericContext(CanvasContext.Type.COURSE, -1L, "")) + protected var canvasContext: CanvasContext by ParcelableArg(default = CanvasContext.getGenericContext(CanvasContext.Type.COURSE, -1L, "")) - private val mLinearLayoutManager by lazy { LinearLayoutManager(requireContext()) } + private val linearLayoutManager by lazy { LinearLayoutManager(requireContext()) } private lateinit var mRecyclerView: RecyclerView - private var mNeedToForceNetwork = false - private var mForceRefresh = false - protected var mIsAnnouncements by BooleanArg() + private var needToForceNetwork = false + private var forceRefresh = false + protected var isAnnouncements by BooleanArg() override fun layoutResId(): Int = R.layout.fragment_discussion_list override val recyclerView: RecyclerView get() = binding.discussionRecyclerView - override fun getPresenterFactory() = DiscussionListPresenterFactory(mCanvasContext, mIsAnnouncements) + override fun getPresenterFactory() = DiscussionListPresenterFactory(canvasContext, isAnnouncements) override fun onPresenterPrepared(presenter: DiscussionListPresenter) = with(binding) { - val emptyTitle = getString(if (mIsAnnouncements) R.string.noAnnouncements else R.string.noDiscussions) + val emptyTitle = getString(if (isAnnouncements) R.string.noAnnouncements else R.string.noDiscussions) mRecyclerView = RecyclerViewUtils.buildRecyclerView( rootView = rootView, context = requireContext(), @@ -97,17 +99,17 @@ open class DiscussionsListFragment : BaseExpandableSyncFragment< } override fun onCreateView(view: View) { - mLinearLayoutManager.orientation = RecyclerView.VERTICAL + linearLayoutManager.orientation = RecyclerView.VERTICAL } override fun onReadySetGo(presenter: DiscussionListPresenter) { mRecyclerView.adapter = adapter - if (mForceRefresh) { + if (forceRefresh) { presenter.refresh(true) - mForceRefresh = false + forceRefresh = false } else { - presenter.loadData(mNeedToForceNetwork) - mNeedToForceNetwork = false + presenter.loadData(needToForceNetwork) + needToForceNetwork = false } } @@ -127,7 +129,7 @@ open class DiscussionsListFragment : BaseExpandableSyncFragment< } override fun createAdapter(): DiscussionListAdapter { - return DiscussionListAdapter(requireContext(), presenter, mCanvasContext.textAndIconColor, mIsAnnouncements, + return DiscussionListAdapter(requireContext(), presenter, canvasContext.textAndIconColor, isAnnouncements, { discussionTopicHeader -> val route = presenter.getDetailsRoute(discussionTopicHeader) RouteMatcher.route( @@ -177,7 +179,7 @@ open class DiscussionsListFragment : BaseExpandableSyncFragment< // We don't want to leave the fab hidden if the list is empty if(presenter.isEmpty) { createNewDiscussion.show() - if (mIsAnnouncements) { + if (isAnnouncements) { emptyPandaView.setEmptyViewImage(requireContext().getDrawableCompat(R.drawable.ic_panda_noannouncements)) emptyPandaView.setMessageText(R.string.noAnnouncementsSubtext) } else { @@ -189,10 +191,10 @@ open class DiscussionsListFragment : BaseExpandableSyncFragment< } private fun setupToolbar() = with(binding) { - discussionListToolbar.title = if(mIsAnnouncements) getString(R.string.tab_announcements) else getString(R.string.tab_discussions) - discussionListToolbar.subtitle = mCanvasContext.name + discussionListToolbar.title = if(isAnnouncements) getString(R.string.tab_announcements) else getString(R.string.tab_discussions) + discussionListToolbar.subtitle = canvasContext.name discussionListToolbar.setupBackButton(this@DiscussionsListFragment) - val searchHint = getString(if (mIsAnnouncements) R.string.searchAnnouncementsHint else R.string.searchDiscussionsHint) + val searchHint = getString(if (isAnnouncements) R.string.searchAnnouncementsHint else R.string.searchDiscussionsHint) discussionListToolbar.addSearch(searchHint) { query -> if (query.isBlank()) { emptyPandaView.emptyViewText(R.string.no_items_to_display_short) @@ -201,7 +203,7 @@ open class DiscussionsListFragment : BaseExpandableSyncFragment< } presenter.searchQuery = query } - ViewStyler.themeToolbarColored(requireActivity(), discussionListToolbar, mCanvasContext.backgroundColor, requireContext().getColor(R.color.white)) + ViewStyler.themeToolbarColored(requireActivity(), discussionListToolbar, canvasContext.backgroundColor, requireContext().getColor(R.color.white)) } private fun setupViews() = with(binding) { @@ -209,11 +211,11 @@ open class DiscussionsListFragment : BaseExpandableSyncFragment< createNewDiscussion.backgroundTintList = ViewStyler.makeColorStateListForButton() createNewDiscussion.setImageDrawable(ColorUtils.colorIt(ThemePrefs.buttonTextColor, createNewDiscussion.drawable)) createNewDiscussion.onClickWithRequireNetwork { - if(mIsAnnouncements) { - val args = CreateOrEditAnnouncementFragment.newInstanceCreate(mCanvasContext).nonNullArgs + if(isAnnouncements) { + val args = CreateOrEditAnnouncementFragment.newInstanceCreate(canvasContext).nonNullArgs RouteMatcher.route(requireContext(), Route(CreateOrEditAnnouncementFragment::class.java, null, args)) } else { - val args = CreateDiscussionFragment.makeBundle(mCanvasContext) + val args = CreateDiscussionFragment.makeBundle(canvasContext) RouteMatcher.route(requireContext(), Route(CreateDiscussionFragment::class.java, null, args)) } } @@ -248,7 +250,7 @@ open class DiscussionsListFragment : BaseExpandableSyncFragment< // need to set a flag here. Because we use the event bus in the fragment instead of the presenter for unit testing purposes, // when we come back to this fragment it will go through the life cycle events again and the cached data will immediately // overwrite the data from the network if we refresh the presenter from here. - mNeedToForceNetwork = true + needToForceNetwork = true } } @@ -268,7 +270,7 @@ open class DiscussionsListFragment : BaseExpandableSyncFragment< // need to set a flag here. Because we use the event bus in the fragment instead of the presenter for unit testing purposes, // when we come back to this fragment it will go through the life cycle events again and the cached data will immediately // overwrite the data from the network if we refresh the presenter from here. - mNeedToForceNetwork = true + needToForceNetwork = true } } @@ -279,7 +281,7 @@ open class DiscussionsListFragment : BaseExpandableSyncFragment< val discussionTopicHeader = adapter.getItem(it) if(discussionTopicHeader != null) { adapter.removeItem(discussionTopicHeader, false) - mNeedToForceNetwork = true + needToForceNetwork = true } } } @@ -288,7 +290,7 @@ open class DiscussionsListFragment : BaseExpandableSyncFragment< companion object { fun newInstance(canvasContext: CanvasContext) = DiscussionsListFragment().apply { - mCanvasContext = canvasContext + this.canvasContext = canvasContext } } } diff --git a/apps/teacher/src/main/java/com/instructure/teacher/fragments/DiscussionsUpdateFragment.kt b/apps/teacher/src/main/java/com/instructure/teacher/fragments/DiscussionsUpdateFragment.kt index c9ef9fd00b..3591aaf74a 100644 --- a/apps/teacher/src/main/java/com/instructure/teacher/fragments/DiscussionsUpdateFragment.kt +++ b/apps/teacher/src/main/java/com/instructure/teacher/fragments/DiscussionsUpdateFragment.kt @@ -27,6 +27,8 @@ import com.instructure.canvasapi2.models.DiscussionEntry import com.instructure.canvasapi2.models.DiscussionTopic import com.instructure.canvasapi2.utils.APIHelper import com.instructure.canvasapi2.utils.Logger +import com.instructure.canvasapi2.utils.pageview.PageView +import com.instructure.canvasapi2.utils.pageview.PageViewUrlParam import com.instructure.pandautils.analytics.SCREEN_VIEW_DISCUSSIONS_UPDATE import com.instructure.pandautils.analytics.ScreenView import com.instructure.pandautils.binding.viewBinding @@ -49,15 +51,17 @@ import com.instructure.teacher.utils.setupCloseButton import com.instructure.teacher.utils.setupMenu import com.instructure.teacher.viewinterface.DiscussionsUpdateView +@PageView(url = "{canvasContext}/discussion_topics/{topicId}/edit") @ScreenView(SCREEN_VIEW_DISCUSSIONS_UPDATE) class DiscussionsUpdateFragment : BasePresenterFragment(), DiscussionsUpdateView { private val binding by viewBinding(FragmentDiscussionsEditBinding::bind) - private var mCanvasContext: CanvasContext by ParcelableArg(default = CanvasContext.getGenericContext(CanvasContext.Type.COURSE, -1L, "")) - private var mDiscussionTopicHeaderId: Long by LongArg(key = DISCUSSION_TOPIC_HEADER_ID, default = 0L) // The topic the discussion belongs too - private var mDiscussionEntry: DiscussionEntry by ParcelableArg(key = DISCUSSION_ENTRY, default = DiscussionEntry()) - private var mDiscussionTopic: DiscussionTopic by ParcelableArg(key = DISCUSSION_TOPIC, default = DiscussionTopic()) + private var canvasContext: CanvasContext by ParcelableArg(default = CanvasContext.getGenericContext(CanvasContext.Type.COURSE, -1L, "")) + @get:PageViewUrlParam("topicId") + private var discussionTopicHeaderId: Long by LongArg(key = DISCUSSION_TOPIC_HEADER_ID, default = 0L) // The topic the discussion belongs too + private var discussionEntry: DiscussionEntry by ParcelableArg(key = DISCUSSION_ENTRY, default = DiscussionEntry()) + private var discussionTopic: DiscussionTopic by ParcelableArg(key = DISCUSSION_TOPIC, default = DiscussionTopic()) private var placeHolderList: ArrayList = ArrayList() override fun onCreate(savedInstanceState: Bundle?) { @@ -71,7 +75,7 @@ class DiscussionsUpdateFragment : BasePresenterFragment = ArrayList() private var rceImageUploadJob: Job? = null @@ -101,19 +105,19 @@ class EditAssignmentDetailsFragment : BaseFragment() { // Keeps track of which override we were editing so we can scroll back to it when the user returns from editing assignees private var scrollBackToOverride: AssignmentOverrideView? = null - private var mDueDateApiCalls: Job? = null - private var mPutAssignmentCall: Job? = null + private var dueDateApiCalls: Job? = null + private var putAssignmentCall: Job? = null - private var mScrollHandler: Handler = Handler() + private var scrollHandler: Handler = Handler() - private var mScrollToRunnable: Runnable = Runnable { + private var scrollToRunnable: Runnable = Runnable { if(isAdded) binding.scrollView.fullScroll(ScrollView.FOCUS_DOWN) } // We maintain a copy of the groupedDueDates to manipulate and use to display // overrides. When pushing changes, we update the original assignment object // with the changes in the copy. - private var mEditDateGroups: EditDateGroups = arrayListOf() + private var editDateGroups: EditDateGroups = arrayListOf() private val datePickerOnClick: (date: Date?, (Int, Int, Int) -> Unit) -> Unit = { date, callback -> DatePickerDialogFragment.getInstance(requireActivity().supportFragmentManager, date) { year, month, dayOfMonth -> @@ -130,11 +134,15 @@ class EditAssignmentDetailsFragment : BaseFragment() { private val removeOverrideClick: (DueDateGroup) -> Unit = { callback -> // Show confirmation dialog ConfirmRemoveAssignmentOverrideDialog.show(requireActivity().supportFragmentManager) { - if (mEditDateGroups.contains(callback)) mEditDateGroups.remove(callback) + if (editDateGroups.contains(callback)) editDateGroups.remove(callback) setupOverrides() } } + @Suppress("unused") + @PageViewUrl + private fun makePageViewUrl() = "${ApiPrefs.fullDomain}/${course.contextId.replace("_", "s/")}/${assignment.id}" + override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) AppCompatDelegate.setCompatVectorFromResourcesEnabled(true) @@ -148,7 +156,7 @@ class EditAssignmentDetailsFragment : BaseFragment() { super.onViewCreated(view, savedInstanceState) savedInstanceState?.let { @Suppress("UNCHECKED_CAST") - mEditDateGroups = (savedInstanceState.getSerializable(EDIT_DATE_GROUPS) as ArrayList) + editDateGroups = (savedInstanceState.getSerializable(EDIT_DATE_GROUPS) as ArrayList) } // Hide Keyboard @@ -176,7 +184,7 @@ class EditAssignmentDetailsFragment : BaseFragment() { RequestCodes.CAMERA_PIC_REQUEST -> MediaUploadUtils.handleCameraPicResult(requireActivity(), null) else -> null }?.let { imageUri -> - rceImageUploadJob = MediaUploadUtils.uploadRceImageJob(imageUri, mCourse, requireActivity()) { imageUrl -> binding.descriptionEditor.insertImage(requireActivity(), imageUrl) } + rceImageUploadJob = MediaUploadUtils.uploadRceImageJob(imageUri, course, requireActivity()) { imageUrl -> binding.descriptionEditor.insertImage(requireActivity(), imageUrl) } } } } @@ -191,14 +199,14 @@ class EditAssignmentDetailsFragment : BaseFragment() { } @SuppressLint("ClickableViewAccessibility") - private fun setupPublishSwitch() = with(mAssignment) { + private fun setupPublishSwitch() = with(assignment) { // If a student has submitted something, we can't let the teacher unpublish the assignment // Publish status val publishSwitch = binding.publishSwitch publishSwitch.applyTheme() publishSwitch.isChecked = published - mIsPublished = published - publishSwitch.setOnCheckedChangeListener { _, isChecked -> mIsPublished = isChecked } + isPublished = published + publishSwitch.setOnCheckedChangeListener { _, isChecked -> isPublished = isChecked } if (published && !unpublishable) { publishSwitch.alpha = 0.5f publishSwitch.setOnTouchListener { _, motionEvent -> @@ -216,7 +224,7 @@ class EditAssignmentDetailsFragment : BaseFragment() { ViewStyler.themeSpinner(requireContext(), displayGradeAsSpinner, ThemePrefs.brandColor) displayGradeAsSpinner.onItemSelectedListener = null - when(mDisplayGradeAs) { + when(displayGradeAs) { POINTS_TYPE -> displayGradeAsSpinner.setSelection(spinnerAdapter.getPosition(getString(R.string.points))) GPA_SCALE_TYPE -> displayGradeAsSpinner.setSelection(spinnerAdapter.getPosition(getString(R.string.gpa_scale))) LETTER_GRADE_TYPE -> displayGradeAsSpinner.setSelection(spinnerAdapter.getPosition(getString(R.string.letter_grade))) @@ -229,12 +237,12 @@ class EditAssignmentDetailsFragment : BaseFragment() { override fun onItemSelected(parent: AdapterView<*>, view: View?, position: Int, id: Long) { if(view == null) return when((view as TextView).text.toString()) { - getString(R.string.points) -> mDisplayGradeAs = POINTS_TYPE - getString(R.string.gpa_scale) -> mDisplayGradeAs = GPA_SCALE_TYPE - getString(R.string.letter_grade) -> mDisplayGradeAs = LETTER_GRADE_TYPE - getString(R.string.complete_incomplete) -> mDisplayGradeAs = PASS_FAIL_TYPE - getString(R.string.percentage) -> mDisplayGradeAs = PERCENT_TYPE - getString(R.string.not_graded) -> mDisplayGradeAs = NOT_GRADED_TYPE + getString(R.string.points) -> displayGradeAs = POINTS_TYPE + getString(R.string.gpa_scale) -> displayGradeAs = GPA_SCALE_TYPE + getString(R.string.letter_grade) -> displayGradeAs = LETTER_GRADE_TYPE + getString(R.string.complete_incomplete) -> displayGradeAs = PASS_FAIL_TYPE + getString(R.string.percentage) -> displayGradeAs = PERCENT_TYPE + getString(R.string.not_graded) -> displayGradeAs = NOT_GRADED_TYPE } } @@ -253,7 +261,7 @@ class EditAssignmentDetailsFragment : BaseFragment() { } // Assignment name - editAssignmentName.setText(mAssignment.name) + editAssignmentName.setText(assignment.name) editAssignmentName.setOnFocusChangeListener { _, hasFocus -> if (hasFocus) descriptionEditor.hideEditorToolbar() } @@ -267,7 +275,7 @@ class EditAssignmentDetailsFragment : BaseFragment() { } } // Points possible - editGradePoints.setText(NumberHelper.formatDecimal(mAssignment.pointsPossible, 2, true)) + editGradePoints.setText(NumberHelper.formatDecimal(assignment.pointsPossible, 2, true)) // set the underline to be the brand color ViewStyler.themeEditText(requireContext(), editGradePoints, ThemePrefs.brandColor) editGradePoints.onTextChanged { @@ -279,8 +287,8 @@ class EditAssignmentDetailsFragment : BaseFragment() { } setupPublishSwitch() - if(mDisplayGradeAs == null) { - mDisplayGradeAs = mAssignment.gradingType + if(displayGradeAs == null) { + displayGradeAs = assignment.gradingType } setupDisplayGradeAs() @@ -291,14 +299,14 @@ class EditAssignmentDetailsFragment : BaseFragment() { // Description setupDescription() - if (mEditDateGroups.isEmpty()) mEditDateGroups.addAll(mAssignment.groupedDueDates) + if (editDateGroups.isEmpty()) editDateGroups.addAll(assignment.groupedDueDates) - mDueDateApiCalls = weave { + dueDateApiCalls = weave { try { if (groupsMapped.isEmpty() && sectionsMapped.isEmpty() && studentsMapped.isEmpty()) { - val sections = awaitApi> { SectionManager.getAllSectionsForCourse(mAssignment.courseId, it, false) } - val groups = if (mAssignment.groupCategoryId > 0L) awaitApi> { GroupCategoriesManager.getAllGroupsForCategory(mAssignment.groupCategoryId, it, false) } else emptyList() - val students = awaitApi> { UserManager.getAllPeopleList(mCourse, it, false) } + val sections = awaitApi> { SectionManager.getAllSectionsForCourse(assignment.courseId, it, false) } + val groups = if (assignment.groupCategoryId > 0L) awaitApi> { GroupCategoriesManager.getAllGroupsForCategory(assignment.groupCategoryId, it, false) } else emptyList() + val students = awaitApi> { UserManager.getAllPeopleList(course, it, false) } groupsMapped += groups.associateBy { it.id } sectionsMapped += sections.associateBy { it.id } studentsMapped += students.associateBy { it.id } @@ -306,14 +314,14 @@ class EditAssignmentDetailsFragment : BaseFragment() { setupAddOverrideButton() setupOverrides() - if (mScrollToDates) { - mScrollToDates = false + if (scrollToDates) { + scrollToDates = false // We came from the Dates page, scroll to the dates for editing - mScrollHandler.postDelayed(mScrollToRunnable, 300) + scrollHandler.postDelayed(scrollToRunnable, 300) } scrollBackToOverride?.let { - if (!mScrollToDates) + if (!scrollToDates) scrollView.post { scrollView.fullScroll(ScrollView.FOCUS_DOWN) } @@ -335,7 +343,7 @@ class EditAssignmentDetailsFragment : BaseFragment() { plus.setColorFilter(ThemePrefs.textButtonColor) addOverride.setOnClickListener { - mEditDateGroups.add(DueDateGroup()) + editDateGroups.add(DueDateGroup()) setupOverrides() scrollView.post { scrollView.fullScroll(ScrollView.FOCUS_DOWN) @@ -348,11 +356,11 @@ class EditAssignmentDetailsFragment : BaseFragment() { private fun setupOverrides() = with(binding) { overrideContainer.removeAllViews() // Load in overrides - mEditDateGroups.forEachIndexed { index, dueDateGroup -> + editDateGroups.forEachIndexed { index, dueDateGroup -> val assignees = ArrayList() val v = AssignmentOverrideView(requireActivity()) if (dueDateGroup.isEveryone) { - assignees += getString(if (mEditDateGroups.any { it.hasOverrideAssignees }) R.string.everyone_else else R.string.everyone) + assignees += getString(if (editDateGroups.any { it.hasOverrideAssignees }) R.string.everyone_else else R.string.everyone) } dueDateGroup.groupIds.forEach { assignees.add(groupsMapped[it]?.name!!) } dueDateGroup.sectionIds.forEach { assignees.add(sectionsMapped[it]?.name!!) } @@ -363,14 +371,14 @@ class EditAssignmentDetailsFragment : BaseFragment() { } } - v.setupOverride(index, dueDateGroup, mEditDateGroups.size > 1, assignees, datePickerOnClick, timePickerOnClick, removeOverrideClick) { + v.setupOverride(index, dueDateGroup, editDateGroups.size > 1, assignees, datePickerOnClick, timePickerOnClick, removeOverrideClick) { val args = AssigneeListFragment.makeBundle( - mEditDateGroups, + editDateGroups, index, sectionsMapped.values.toList(), groupsMapped.values.toList(), studentsMapped.values.toList()) - RouteMatcher.route(requireContext(), Route(AssigneeListFragment::class.java, mCourse, args)) + RouteMatcher.route(requireContext(), Route(AssigneeListFragment::class.java, course, args)) scrollBackToOverride = v } @@ -389,8 +397,8 @@ class EditAssignmentDetailsFragment : BaseFragment() { // Load description // If the html has a Studio LTI url, we want to authenticate so the user doesn't have to login again - if (CanvasWebView.containsLTI(mAssignment.description.orEmpty(), "UTF-8")) { - descriptionEditor.setHtml(DiscussionUtils.createLTIPlaceHolders(requireContext(), mAssignment.description ?: "") { _, placeholder -> + if (CanvasWebView.containsLTI(assignment.description.orEmpty(), "UTF-8")) { + descriptionEditor.setHtml(DiscussionUtils.createLTIPlaceHolders(requireContext(), assignment.description ?: "") { _, placeholder -> placeHolderList.add(placeholder) }, getString(R.string.assignmentDescriptionContentDescription), @@ -398,7 +406,7 @@ class EditAssignmentDetailsFragment : BaseFragment() { ThemePrefs.brandColor, ThemePrefs.textButtonColor) } else { - descriptionEditor.setHtml(mAssignment.description, + descriptionEditor.setHtml(assignment.description, getString(R.string.assignmentDescriptionContentDescription), getString(R.string.rce_empty_description), ThemePrefs.brandColor, ThemePrefs.textButtonColor) @@ -428,22 +436,22 @@ class EditAssignmentDetailsFragment : BaseFragment() { val postData = AssignmentPostBody() postData.name = editAssignmentName.text.toString() postData.pointsPossible = pointsPossible - postData.setGroupedDueDates(mEditDateGroups) + postData.setGroupedDueDates(editDateGroups) postData.description = handleLTIPlaceHolders(placeHolderList, descriptionEditor.html) postData.notifyOfUpdate = false - postData.gradingType = mDisplayGradeAs + postData.gradingType = displayGradeAs // TODO: remove this section when we support editing submission types // There is some weirdness with the API dealing with not graded stuff. When you change it from not graded you also // need to set the submission type to be something. When we implement submission type editing we won't need this here - if(mAssignment.gradingType == NOT_GRADED_TYPE && mDisplayGradeAs != NOT_GRADED_TYPE) { + if(assignment.gradingType == NOT_GRADED_TYPE && displayGradeAs != NOT_GRADED_TYPE) { val type = "none" val submissionList = listOf(type) postData.submissionTypes = submissionList } // if we want to set the type as not graded, we don't want a submission type or points possible - if(mDisplayGradeAs == NOT_GRADED_TYPE) { + if(displayGradeAs == NOT_GRADED_TYPE) { //set points to 0 if we aren't grading it postData.pointsPossible = null val type = NOT_GRADED_TYPE @@ -452,24 +460,24 @@ class EditAssignmentDetailsFragment : BaseFragment() { } // only set the published flag if we can unpublish/publish the assignment - if (mAssignment.unpublishable) postData.published = mIsPublished - else postData.published = mAssignment.published + if (assignment.unpublishable) postData.published = isPublished + else postData.published = assignment.published @Suppress("EXPERIMENTAL_FEATURE_WARNING") - mPutAssignmentCall = weave { + putAssignmentCall = weave { try { saveButton?.setGone() savingProgressBar.announceForAccessibility(getString(R.string.saving)) savingProgressBar.setVisible() - mAssignment = awaitApi { AssignmentManager.editAssignment(mAssignment.courseId, mAssignment.id, postData, it, false) } - AssignmentUpdatedEvent(mAssignment.id).post() // Post bus event + assignment = awaitApi { AssignmentManager.editAssignment(assignment.courseId, assignment.id, postData, it, false) } + AssignmentUpdatedEvent(assignment.id).post() // Post bus event toast(R.string.successfully_updated_assignment) // let the user know the assignment was saved editAssignmentName.hideKeyboard() // close the keyboard requireActivity().onBackPressed() // close this fragment } catch (e: Throwable) { saveButton?.setVisible() savingProgressBar.setGone() - if (mAssignment.inClosedGradingPeriod) { + if (assignment.inClosedGradingPeriod) { toast(R.string.error_saving_assignment_closed_grading_period) } else { toast(R.string.error_saving_assignment) @@ -483,7 +491,7 @@ class EditAssignmentDetailsFragment : BaseFragment() { fun onAssigneesChanged(event: AssigneesUpdatedEvent) { // Update grouped due dates (EditDateGroups) event.once(javaClass.simpleName) { dates -> - mEditDateGroups = dates + editDateGroups = dates setupOverrides() //remove it so when we go to another assignment it won't show up there too EventBus.getDefault().removeStickyEvent(event) @@ -491,16 +499,16 @@ class EditAssignmentDetailsFragment : BaseFragment() { } override fun onSaveInstanceState(outState: Bundle) { - outState.putSerializable(EDIT_DATE_GROUPS, ArrayList(mEditDateGroups)) + outState.putSerializable(EDIT_DATE_GROUPS, ArrayList(editDateGroups)) super.onSaveInstanceState(outState) } override fun onDestroyView() { super.onDestroyView() - mDueDateApiCalls?.cancel() - mPutAssignmentCall?.cancel() - mScrollHandler.removeCallbacks(mScrollToRunnable) - mSessionAuthJob?.cancel() + dueDateApiCalls?.cancel() + putAssignmentCall?.cancel() + scrollHandler.removeCallbacks(scrollToRunnable) + sessionAuthJob?.cancel() rceImageUploadJob?.cancel() } @@ -510,7 +518,7 @@ class EditAssignmentDetailsFragment : BaseFragment() { const val EDIT_DATE_GROUPS = "editDateGroups" fun newInstance(course: Course, args: Bundle) = EditAssignmentDetailsFragment().withArgs(args).apply { - mCourse = course + this.course = course } fun makeBundle(assignment: Assignment, scrollToDates: Boolean): Bundle { diff --git a/apps/teacher/src/main/java/com/instructure/teacher/fragments/FileListFragment.kt b/apps/teacher/src/main/java/com/instructure/teacher/fragments/FileListFragment.kt index c30f65fff1..eda09ef208 100644 --- a/apps/teacher/src/main/java/com/instructure/teacher/fragments/FileListFragment.kt +++ b/apps/teacher/src/main/java/com/instructure/teacher/fragments/FileListFragment.kt @@ -29,7 +29,10 @@ import com.instructure.canvasapi2.models.CanvasContext import com.instructure.canvasapi2.models.Course import com.instructure.canvasapi2.models.FileFolder import com.instructure.canvasapi2.utils.APIHelper +import com.instructure.canvasapi2.utils.ApiPrefs import com.instructure.canvasapi2.utils.isValid +import com.instructure.canvasapi2.utils.pageview.PageView +import com.instructure.canvasapi2.utils.pageview.PageViewUrl import com.instructure.interactions.router.Route import com.instructure.pandautils.analytics.SCREEN_VIEW_FILE_LIST import com.instructure.pandautils.analytics.ScreenView @@ -59,6 +62,7 @@ import org.greenrobot.eventbus.Subscribe import org.greenrobot.eventbus.ThreadMode import java.util.* +@PageView @ScreenView(SCREEN_VIEW_FILE_LIST) class FileListFragment : BaseSyncFragment< FileFolder, @@ -71,7 +75,7 @@ class FileListFragment : BaseSyncFragment< private lateinit var mRecyclerView: RecyclerView - private var mCanvasContext: CanvasContext by ParcelableArg(Course()) + private var canvasContext: CanvasContext by ParcelableArg(Course()) private var currentFolder: FileFolder by ParcelableArg(FileFolder()) private var fabOpen = false @@ -136,9 +140,15 @@ class FileListFragment : BaseSyncFragment< } } + @Suppress("unused") + @PageViewUrl + private fun makePageViewUrl() = + if (canvasContext.type == CanvasContext.Type.USER) "${ApiPrefs.fullDomain}/files" + else "${ApiPrefs.fullDomain}/${canvasContext.contextId.replace("_", "s/")}/files" + override fun layoutResId() = R.layout.fragment_file_list override fun onCreateView(view: View) = Unit - override fun getPresenterFactory() = FileListPresenterFactory(currentFolder, mCanvasContext) + override fun getPresenterFactory() = FileListPresenterFactory(currentFolder, canvasContext) override val recyclerView: RecyclerView get() = binding.fileListRecyclerView override fun onPresenterPrepared(presenter: FileListPresenter) { @@ -178,17 +188,17 @@ class FileListFragment : BaseSyncFragment< } override fun createAdapter(): FileListAdapter { - return FileListAdapter(requireContext(), mCanvasContext.textAndIconColor, presenter) { + return FileListAdapter(requireContext(), canvasContext.textAndIconColor, presenter) { if (it.displayName.isValid()) { // This is a file - val editableFile = EditableFile(it, presenter.usageRights, presenter.licenses, mCanvasContext.backgroundColor, presenter.mCanvasContext, R.drawable.ic_document) + val editableFile = EditableFile(it, presenter.usageRights, presenter.licenses, canvasContext.backgroundColor, presenter.mCanvasContext, R.drawable.ic_document) if (it.isHtmlFile) { /* An HTML file can reference other canvas files as resources (e.g. CSS files) and must be accessed as an authenticated preview to work correctly */ - val bundle = ViewHtmlFragment.makeAuthSessionBundle(mCanvasContext, it, it.displayName.orEmpty(), mCanvasContext.backgroundColor, editableFile) + val bundle = ViewHtmlFragment.makeAuthSessionBundle(canvasContext, it, it.displayName.orEmpty(), canvasContext.backgroundColor, editableFile) RouteMatcher.route(requireActivity(), Route(ViewHtmlFragment::class.java, null, bundle)) } else { - viewMedia(requireContext(), it.displayName.orEmpty(), it.contentType.orEmpty(), it.url, it.thumbnailUrl, it.displayName, R.drawable.ic_document, mCanvasContext.backgroundColor, editableFile) + viewMedia(requireContext(), it.displayName.orEmpty(), it.contentType.orEmpty(), it.url, it.thumbnailUrl, it.displayName, R.drawable.ic_document, canvasContext.backgroundColor, editableFile) } } else { // This is a folder @@ -213,8 +223,8 @@ class FileListFragment : BaseSyncFragment< override fun checkIfEmpty() = with(binding) { when { !presenter.currentFolder.isRoot -> emptyPandaView.setMessageText(R.string.emptyFolder) - mCanvasContext.isCourse -> emptyPandaView.setMessageText(R.string.noFilesSubtextCourse) - mCanvasContext.isGroup -> emptyPandaView.setMessageText(R.string.noFilesSubtextGroup) + canvasContext.isCourse -> emptyPandaView.setMessageText(R.string.noFilesSubtextCourse) + canvasContext.isGroup -> emptyPandaView.setMessageText(R.string.noFilesSubtextGroup) else -> emptyPandaView.setMessageText(R.string.noFilesSubtext) } emptyPandaView.setEmptyViewImage(requireContext().getDrawableCompat(R.drawable.ic_panda_nofiles)) @@ -236,7 +246,7 @@ class FileListFragment : BaseSyncFragment< addFileFab.setOnClickListener { animateFabs() handleClick(childFragmentManager) { - val bundle = FileUploadDialogFragment.createContextBundle(null, mCanvasContext, presenter.currentFolder.id) + val bundle = FileUploadDialogFragment.createContextBundle(null, canvasContext, presenter.currentFolder.id) FileUploadDialogFragment.newInstance(bundle).show(childFragmentManager, FileUploadDialogFragment.TAG) } } @@ -282,9 +292,9 @@ class FileListFragment : BaseSyncFragment< when (it.itemId) { R.id.edit -> { val bundle = EditFileFolderFragment.makeBundle(presenter.currentFolder, presenter.usageRights, presenter.licenses, presenter.mCanvasContext.id) - RouteMatcher.route(requireContext(), Route(EditFileFolderFragment::class.java, mCanvasContext, bundle)) + RouteMatcher.route(requireContext(), Route(EditFileFolderFragment::class.java, canvasContext, bundle)) } - R.id.search -> RouteMatcher.route(requireContext(), Route(FileSearchFragment::class.java, mCanvasContext, Bundle())) + R.id.search -> RouteMatcher.route(requireContext(), Route(FileSearchFragment::class.java, canvasContext, Bundle())) } } @@ -297,10 +307,10 @@ class FileListFragment : BaseSyncFragment< fileListToolbar.title = getString(R.string.sg_tab_files) } - if (mCanvasContext.isUser) { + if (canvasContext.isUser) { // User's files, no CanvasContext ViewStyler.themeToolbarColored(requireActivity(), fileListToolbar, ThemePrefs.primaryColor, ThemePrefs.primaryTextColor) - } else ViewStyler.themeToolbarColored(requireActivity(), fileListToolbar, mCanvasContext.backgroundColor, requireContext().getColor(R.color.white)) + } else ViewStyler.themeToolbarColored(requireActivity(), fileListToolbar, canvasContext.backgroundColor, requireContext().getColor(R.color.white)) } private fun animateFabs() = if (fabOpen) { @@ -362,7 +372,7 @@ class FileListFragment : BaseSyncFragment< private const val CURRENT_FOLDER = "currentFolder" fun newInstance(canvasContext: CanvasContext, args: Bundle) = FileListFragment().apply { - mCanvasContext = args.getParcelable(CANVAS_CONTEXT) ?: canvasContext + this.canvasContext = args.getParcelable(CANVAS_CONTEXT) ?: canvasContext currentFolder = args.getParcelable(CURRENT_FOLDER) ?: FileFolder(id = -1L, name = "") } diff --git a/apps/teacher/src/main/java/com/instructure/teacher/fragments/InternalWebViewFragment.kt b/apps/teacher/src/main/java/com/instructure/teacher/fragments/InternalWebViewFragment.kt index dfe4eb51aa..16c9963a74 100644 --- a/apps/teacher/src/main/java/com/instructure/teacher/fragments/InternalWebViewFragment.kt +++ b/apps/teacher/src/main/java/com/instructure/teacher/fragments/InternalWebViewFragment.kt @@ -83,7 +83,7 @@ open class InternalWebViewFragment : BaseFragment() { override fun onSaveInstanceState(outState: Bundle) { super.onSaveInstanceState(outState) - binding.canvasWebView?.saveState(outState) + if (view != null) binding.canvasWebView.saveState(outState) } override fun onCreateView(view: View) = Unit diff --git a/apps/teacher/src/main/java/com/instructure/teacher/fragments/LtiLaunchFragment.kt b/apps/teacher/src/main/java/com/instructure/teacher/fragments/LtiLaunchFragment.kt index 4105c536b6..222856ccb4 100644 --- a/apps/teacher/src/main/java/com/instructure/teacher/fragments/LtiLaunchFragment.kt +++ b/apps/teacher/src/main/java/com/instructure/teacher/fragments/LtiLaunchFragment.kt @@ -28,6 +28,8 @@ import com.instructure.canvasapi2.managers.SubmissionManager import com.instructure.canvasapi2.models.CanvasContext import com.instructure.canvasapi2.models.Tab import com.instructure.canvasapi2.utils.ApiPrefs +import com.instructure.canvasapi2.utils.pageview.PageView +import com.instructure.canvasapi2.utils.pageview.PageViewUrl import com.instructure.canvasapi2.utils.validOrNull import com.instructure.canvasapi2.utils.weave.weave import com.instructure.interactions.router.Route @@ -42,11 +44,14 @@ import com.instructure.teacher.router.RouteMatcher import kotlinx.coroutines.Job import java.net.URLDecoder +@PageView @ScreenView(SCREEN_VIEW_LTI_LAUNCH) class LtiLaunchFragment : BaseFragment() { private val binding by viewBinding(FragmentLtiLaunchBinding::bind) + var canvasContext: CanvasContext? by NullableParcelableArg(key = Const.CANVAS_CONTEXT) + private var title: String? by NullableStringArg(key = Const.TITLE) private var ltiUrl: String by StringArg(key = LTI_URL) private var ltiTab: Tab? by NullableParcelableArg(key = TAB) @@ -59,6 +64,11 @@ class LtiLaunchFragment : BaseFragment() { private var ltiUrlLaunchJob: Job? = null + @Suppress("unused") + @PageViewUrl + private fun makePageViewUrl() = + ltiTab?.externalUrl ?: ApiPrefs.fullDomain + canvasContext?.toAPIString() + "/external_tools" + override fun layoutResId(): Int = R.layout.fragment_lti_launch override fun onCreate(savedInstanceState: Bundle?) { diff --git a/apps/teacher/src/main/java/com/instructure/teacher/fragments/PageDetailsFragment.kt b/apps/teacher/src/main/java/com/instructure/teacher/fragments/PageDetailsFragment.kt index 038cd16ebe..cde474614a 100644 --- a/apps/teacher/src/main/java/com/instructure/teacher/fragments/PageDetailsFragment.kt +++ b/apps/teacher/src/main/java/com/instructure/teacher/fragments/PageDetailsFragment.kt @@ -26,6 +26,8 @@ import com.instructure.canvasapi2.models.Course import com.instructure.canvasapi2.models.Page import com.instructure.canvasapi2.utils.APIHelper import com.instructure.canvasapi2.utils.ApiPrefs +import com.instructure.canvasapi2.utils.pageview.PageView +import com.instructure.canvasapi2.utils.pageview.PageViewUrl import com.instructure.interactions.FullScreenInteractions import com.instructure.interactions.Identity import com.instructure.interactions.MasterDetailInteractions @@ -55,6 +57,7 @@ import org.greenrobot.eventbus.EventBus import org.greenrobot.eventbus.Subscribe import org.greenrobot.eventbus.ThreadMode +@PageView @ScreenView(SCREEN_VIEW_PAGE_DETAILS) class PageDetailsFragment : BasePresenterFragment< PageDetailsPresenter, @@ -63,15 +66,27 @@ class PageDetailsFragment : BasePresenterFragment< private val binding by viewBinding(FragmentPageDetailsBinding::bind) - private var mCanvasContext: CanvasContext by ParcelableArg(default = Course()) - private var mPage: Page by ParcelableArg(Page(), PAGE) - private var mPageId: String by StringArg(key = PAGE_ID) + private var canvasContext: CanvasContext by ParcelableArg(default = Course()) + private var page: Page by ParcelableArg(Page(), PAGE) + private var pageId: String by StringArg(key = PAGE_ID) private var downloadUrl: String? = null var downloadFileName: String? = null private var loadHtmlJob: Job? = null + @PageViewUrl + @Suppress("unused") + private fun makePageViewUrl(): String { + val url = StringBuilder(ApiPrefs.fullDomain) + page.let { + url.append(canvasContext.toAPIString()) + if (!it.frontPage) url.append("/pages/${page.url}") + getModuleItemId()?.let { url.append("?module_item_id=$it") } + } + return url.toString() + } + override fun onStart() { super.onStart() EventBus.getDefault().register(this) @@ -96,12 +111,12 @@ class PageDetailsFragment : BasePresenterFragment< } override fun onReadySetGo(presenter: PageDetailsPresenter): Unit = with(binding) { - if (mPage.frontPage) { - presenter.getFrontPage(mCanvasContext, true) - } else if (!mPageId.isBlank()) { - presenter.getPage(mPageId, mCanvasContext, true) + if (page.frontPage) { + presenter.getFrontPage(canvasContext, true) + } else if (!pageId.isBlank()) { + presenter.getPage(pageId, canvasContext, true) } else { - presenter.getPage(mPage.url ?: "", mCanvasContext, true) + presenter.getPage(page.url ?: "", canvasContext, true) } setupToolbar() @@ -154,9 +169,9 @@ class PageDetailsFragment : BasePresenterFragment< }) EventBus.getDefault().getStickyEvent(PageDeletedEvent::class.java)?.once(javaClass.simpleName + ".onResume()") { - if (it.id == mPage.id) { + if (it.id == page.id) { if (activity is MasterDetailInteractions) { - (activity as MasterDetailInteractions).popFragment(mCanvasContext) + (activity as MasterDetailInteractions).popFragment(canvasContext) } else if (activity is FullScreenInteractions) { requireActivity().finish() } @@ -164,20 +179,20 @@ class PageDetailsFragment : BasePresenterFragment< } } - override fun getPresenterFactory() = PageDetailsPresenterFactory(mCanvasContext, mPage) + override fun getPresenterFactory() = PageDetailsPresenterFactory(canvasContext, page) override fun onPresenterPrepared(presenter: PageDetailsPresenter) = Unit override fun layoutResId() = R.layout.fragment_page_details - override val identity: Long? get() = mPage.id + override val identity: Long? get() = page.id override val skipCheck: Boolean get() = false override fun populatePageDetails(page: Page) { - mPage = page + this.page = page loadHtmlJob = binding.canvasWebViewWraper.webView.loadHtmlWithIframes(requireContext(), page.body, { - binding.canvasWebViewWraper.loadHtml(it, page.title, baseUrl = mPage.htmlUrl) + if (view != null) binding.canvasWebViewWraper.loadHtml(it, page.title, baseUrl = this.page.htmlUrl) }) { - LtiLaunchFragment.routeLtiLaunchFragment(requireContext(), mCanvasContext, it) + LtiLaunchFragment.routeLtiLaunchFragment(requireContext(), canvasContext, it) } setupToolbar() } @@ -187,25 +202,25 @@ class PageDetailsFragment : BasePresenterFragment< } private fun setupToolbar() = with(binding) { - toolbar.setupMenu(R.menu.menu_page_details) { openEditPage(mPage) } + toolbar.setupMenu(R.menu.menu_page_details) { openEditPage(page) } toolbar.setupBackButtonWithExpandCollapseAndBack(this@PageDetailsFragment) { toolbar.updateToolbarExpandCollapseIcon(this@PageDetailsFragment) - ViewStyler.themeToolbarColored(requireActivity(), toolbar, mCanvasContext.backgroundColor, requireContext().getColor(R.color.white)) + ViewStyler.themeToolbarColored(requireActivity(), toolbar, canvasContext.backgroundColor, requireContext().getColor(R.color.white)) (activity as MasterDetailInteractions).toggleExpandCollapse() } - toolbar.title = mPage.title + toolbar.title = page.title if (!isTablet) { - toolbar.subtitle = mCanvasContext.name + toolbar.subtitle = canvasContext.name } - ViewStyler.themeToolbarColored(requireActivity(), toolbar, mCanvasContext.backgroundColor, requireContext().getColor(R.color.white)) + ViewStyler.themeToolbarColored(requireActivity(), toolbar, canvasContext.backgroundColor, requireContext().getColor(R.color.white)) } private fun openEditPage(page: Page) { if (APIHelper.hasNetworkConnection()) { - val args = CreateOrEditPageDetailsFragment.newInstanceEdit(mCanvasContext, page).nonNullArgs - RouteMatcher.route(requireContext(), Route(CreateOrEditPageDetailsFragment::class.java, mCanvasContext, args)) + val args = CreateOrEditPageDetailsFragment.newInstanceEdit(canvasContext, page).nonNullArgs + RouteMatcher.route(requireContext(), Route(CreateOrEditPageDetailsFragment::class.java, canvasContext, args)) } else { NoInternetConnectionDialog.show(requireFragmentManager()) } @@ -227,7 +242,7 @@ class PageDetailsFragment : BasePresenterFragment< // need to set a flag here. Because we use the event bus in the fragment instead of the presenter for unit testing purposes, // when we come back to this fragment it will go through the life cycle events again and the cached data will immediately // overwrite the data from the network if we refresh the presenter from here. - mPage = it + page = it } } @@ -242,7 +257,7 @@ class PageDetailsFragment : BasePresenterFragment< fun newInstance(canvasContext: CanvasContext, args: Bundle) = PageDetailsFragment().withArgs(args).apply { - mCanvasContext = canvasContext + this.canvasContext = canvasContext } } } diff --git a/apps/teacher/src/main/java/com/instructure/teacher/fragments/PageListFragment.kt b/apps/teacher/src/main/java/com/instructure/teacher/fragments/PageListFragment.kt index bd19f66a43..9aada0d0a7 100644 --- a/apps/teacher/src/main/java/com/instructure/teacher/fragments/PageListFragment.kt +++ b/apps/teacher/src/main/java/com/instructure/teacher/fragments/PageListFragment.kt @@ -23,6 +23,7 @@ import androidx.recyclerview.widget.RecyclerView import com.instructure.canvasapi2.models.CanvasContext import com.instructure.canvasapi2.models.Page import com.instructure.canvasapi2.utils.ApiPrefs +import com.instructure.canvasapi2.utils.pageview.PageView import com.instructure.interactions.router.Route import com.instructure.pandautils.analytics.SCREEN_VIEW_PAGE_LIST import com.instructure.pandautils.analytics.ScreenView @@ -46,21 +47,22 @@ import org.greenrobot.eventbus.EventBus import org.greenrobot.eventbus.Subscribe import org.greenrobot.eventbus.ThreadMode +@PageView(url = "{canvasContext}/pages") @ScreenView(SCREEN_VIEW_PAGE_LIST) class PageListFragment : BaseSyncFragment(), PageListView { private val binding by viewBinding(FragmentPageListBinding::bind) - private var mCanvasContext: CanvasContext by ParcelableArg(default = CanvasContext.getGenericContext(CanvasContext.Type.COURSE, -1L, "")) + private var canvasContext: CanvasContext by ParcelableArg(default = CanvasContext.getGenericContext(CanvasContext.Type.COURSE, -1L, "")) - private val mLinearLayoutManager by lazy { LinearLayoutManager(requireContext()) } + private val linearLayoutManager by lazy { LinearLayoutManager(requireContext()) } private lateinit var mRecyclerView: RecyclerView - private var mNeedToForceNetwork = false + private var needToForceNetwork = false override fun layoutResId(): Int = R.layout.fragment_page_list override val recyclerView: RecyclerView get() = binding.pageRecyclerView - override fun getPresenterFactory() = PageListPresenterFactory(mCanvasContext) + override fun getPresenterFactory() = PageListPresenterFactory(canvasContext) override fun onPresenterPrepared(presenter: PageListPresenter) = with(binding) { mRecyclerView = RecyclerViewUtils.buildRecyclerView( rootView = rootView, @@ -88,14 +90,14 @@ class PageListFragment : BaseSyncFragment + return PageListAdapter(requireContext(), presenter, canvasContext.textAndIconColor) { page -> val args = PageDetailsFragment.makeBundle(page) - RouteMatcher.route(requireContext(), Route(null, PageDetailsFragment::class.java, mCanvasContext, args)) + RouteMatcher.route(requireContext(), Route(null, PageDetailsFragment::class.java, canvasContext, args)) } } @@ -149,7 +151,7 @@ class PageListFragment : BaseSyncFragment if (query.isBlank()) { @@ -159,7 +161,7 @@ class PageListFragment : BaseSyncFragment(), PeopleListView, SearchView.OnQueryTextListener { @@ -54,7 +56,8 @@ class PeopleListFragment : BaseSyncFragment? = null + private val canvasContext: CanvasContext by ParcelableArg(key = Const.CANVAS_CONTEXT) + private var canvasContextsSelected: ArrayList? = null override fun onViewCreated(view: View, savedInstanceState: Bundle?) { swipeRefreshLayoutContainerBinding = RecyclerSwipeRefreshLayoutBinding.bind(view) @@ -107,10 +110,10 @@ class PeopleListFragment : BaseSyncFragment - mCanvasContextsSelected = ArrayList() - mCanvasContextsSelected!!.addAll(canvasContexts) + canvasContextsSelected = ArrayList() + canvasContextsSelected!!.addAll(canvasContexts) - presenter.canvasContextList = mCanvasContextsSelected as ArrayList + presenter.canvasContextList = canvasContextsSelected as ArrayList setupTitle(canvasContexts) }.show(requireActivity().supportFragmentManager, PeopleListFilterDialog::class.java.simpleName) false diff --git a/apps/teacher/src/main/java/com/instructure/teacher/fragments/ProfileEditFragment.kt b/apps/teacher/src/main/java/com/instructure/teacher/fragments/ProfileEditFragment.kt index 6d903a8995..3a601f25df 100644 --- a/apps/teacher/src/main/java/com/instructure/teacher/fragments/ProfileEditFragment.kt +++ b/apps/teacher/src/main/java/com/instructure/teacher/fragments/ProfileEditFragment.kt @@ -39,6 +39,7 @@ import com.instructure.canvasapi2.utils.ApiPrefs import com.instructure.canvasapi2.utils.ApiPrefs.user import com.instructure.canvasapi2.utils.ApiType import com.instructure.canvasapi2.utils.LinkHeaders +import com.instructure.canvasapi2.utils.pageview.PageView import com.instructure.canvasapi2.utils.validOrNull import com.instructure.pandautils.analytics.SCREEN_VIEW_PROFILE_EDIT import com.instructure.pandautils.analytics.ScreenView @@ -57,6 +58,7 @@ import com.instructure.teacher.viewinterface.ProfileEditFragmentView import retrofit2.Response import java.io.File +@PageView(url = "profile/edit") @ScreenView(SCREEN_VIEW_PROFILE_EDIT) class ProfileEditFragment : BasePresenterFragment< ProfileEditFragmentPresenter, diff --git a/apps/teacher/src/main/java/com/instructure/teacher/fragments/ProfileFragment.kt b/apps/teacher/src/main/java/com/instructure/teacher/fragments/ProfileFragment.kt index a04889f83a..d96d1cefd7 100644 --- a/apps/teacher/src/main/java/com/instructure/teacher/fragments/ProfileFragment.kt +++ b/apps/teacher/src/main/java/com/instructure/teacher/fragments/ProfileFragment.kt @@ -22,6 +22,7 @@ import android.view.View import com.instructure.canvasapi2.utils.APIHelper import com.instructure.canvasapi2.utils.ApiPrefs import com.instructure.canvasapi2.utils.Pronouns +import com.instructure.canvasapi2.utils.pageview.PageView import com.instructure.interactions.router.Route import com.instructure.pandautils.analytics.SCREEN_VIEW_PROFILE import com.instructure.pandautils.analytics.ScreenView @@ -36,6 +37,7 @@ import com.instructure.teacher.utils.adoptToolbarStyle import com.instructure.teacher.utils.setupBackButtonAsBackPressedOnly import com.instructure.teacher.utils.setupMenu +@PageView(url = "profile") @ScreenView(SCREEN_VIEW_PROFILE) class ProfileFragment : BaseFragment() { diff --git a/apps/teacher/src/main/java/com/instructure/teacher/fragments/QuizDetailsFragment.kt b/apps/teacher/src/main/java/com/instructure/teacher/fragments/QuizDetailsFragment.kt index 29e23dca83..65475c47da 100644 --- a/apps/teacher/src/main/java/com/instructure/teacher/fragments/QuizDetailsFragment.kt +++ b/apps/teacher/src/main/java/com/instructure/teacher/fragments/QuizDetailsFragment.kt @@ -21,9 +21,12 @@ import android.webkit.WebChromeClient import android.webkit.WebView import android.widget.TextView import com.instructure.canvasapi2.models.Assignment +import com.instructure.canvasapi2.models.CanvasContext import com.instructure.canvasapi2.models.Course import com.instructure.canvasapi2.models.Quiz import com.instructure.canvasapi2.utils.* +import com.instructure.canvasapi2.utils.pageview.PageView +import com.instructure.canvasapi2.utils.pageview.PageViewUrl import com.instructure.interactions.Identity import com.instructure.interactions.MasterDetailInteractions import com.instructure.interactions.router.Route @@ -32,6 +35,7 @@ import com.instructure.pandautils.analytics.ScreenView import com.instructure.pandautils.binding.viewBinding import com.instructure.pandautils.fragments.BasePresenterFragment import com.instructure.pandautils.utils.* +import com.instructure.pandautils.utils.Const import com.instructure.pandautils.views.CanvasWebView import com.instructure.teacher.R import com.instructure.teacher.activities.InternalWebViewActivity @@ -56,6 +60,7 @@ import java.net.URI import java.net.URL import java.util.* +@PageView @ScreenView(SCREEN_VIEW_EDIT_QUIZ_DETAILS) class QuizDetailsFragment : BasePresenterFragment< QuizDetailsPresenter, @@ -64,17 +69,23 @@ class QuizDetailsFragment : BasePresenterFragment< private val binding by viewBinding(FragmentQuizDetailsBinding::bind) - private var mCourse: Course by ParcelableArg(default = Course()) - private var mQuizId: Long by LongArg(0L, QUIZ_ID) - private var mQuiz: Quiz by ParcelableArg(Quiz(), QUIZ) + private var canvasContext: CanvasContext? by NullableParcelableArg(key = Const.CANVAS_CONTEXT) - private var mNeedToForceNetwork = false + private var course: Course by ParcelableArg(default = Course()) + private var quizId: Long by LongArg(0L, QUIZ_ID) + private var quiz: Quiz by ParcelableArg(Quiz(), QUIZ) + + private var needToForceNetwork = false private var loadHtmlJob: Job? = null + @Suppress("unused") + @PageViewUrl + fun makePageViewUrl() = "${ApiPrefs.fullDomain}/${course.contextId.replace("_", "s/")}/quizzes/${quizId}" + override fun layoutResId(): Int = R.layout.fragment_quiz_details - override fun getPresenterFactory() = QuizDetailsPresenterFactory(mCourse, mQuiz) + override fun getPresenterFactory() = QuizDetailsPresenterFactory(course, quiz) override fun onDestroyView() { super.onDestroyView() @@ -82,12 +93,12 @@ class QuizDetailsFragment : BasePresenterFragment< } override fun onReadySetGo(presenter: QuizDetailsPresenter) { - if (mQuizId == 0L) { + if (quizId == 0L) { // No Quiz ID so we must have a quiz - presenter.loadData(mNeedToForceNetwork) + presenter.loadData(needToForceNetwork) } else { // No Quiz, we need to get it - presenter.getQuiz(mQuizId, mCourse, true) + presenter.getQuiz(quizId, course, true) } setupToolbar() @@ -102,16 +113,16 @@ class QuizDetailsFragment : BasePresenterFragment< clearListeners() } - override val identity: Long? get() = if(mQuizId != 0L) mQuizId else mQuiz.id + override val identity: Long? get() = if(quizId != 0L) quizId else quiz.id override val skipCheck: Boolean get() = false override fun populateQuizDetails(quiz: Quiz): Unit = with(binding) { - mQuiz = quiz + this@QuizDetailsFragment.quiz = quiz toolbar.setupMenu(R.menu.menu_edit_generic) { openEditPage(quiz) } swipeRefreshLayout.isRefreshing = false setupViews(quiz) setupListeners(quiz) - ViewStyler.themeToolbarColored(requireActivity(), toolbar, mCourse.backgroundColor, requireContext().getColor(R.color.white)) + ViewStyler.themeToolbarColored(requireActivity(), toolbar, course.backgroundColor, requireContext().getColor(R.color.white)) fullDateDetailsButton.setVisible(quiz._assignment != null) } @@ -119,7 +130,7 @@ class QuizDetailsFragment : BasePresenterFragment< private fun setupToolbar() = with(binding) { toolbar.setupBackButtonWithExpandCollapseAndBack(this@QuizDetailsFragment) { toolbar.updateToolbarExpandCollapseIcon(this@QuizDetailsFragment) - ViewStyler.themeToolbarColored(requireActivity(), toolbar, mCourse.backgroundColor, requireContext().getColor(R.color.white)) + ViewStyler.themeToolbarColored(requireActivity(), toolbar, course.backgroundColor, requireContext().getColor(R.color.white)) (activity as MasterDetailInteractions).toggleExpandCollapse() } @@ -127,7 +138,7 @@ class QuizDetailsFragment : BasePresenterFragment< if (!isTablet) { toolbar.subtitle = presenter.mCourse.name } - ViewStyler.themeToolbarColored(requireActivity(), toolbar, mCourse.backgroundColor, requireContext().getColor(R.color.white)) + ViewStyler.themeToolbarColored(requireActivity(), toolbar, course.backgroundColor, requireContext().getColor(R.color.white)) } private fun setupViews(quiz: Quiz) = with(binding) { @@ -218,7 +229,7 @@ class QuizDetailsFragment : BasePresenterFragment< } // If the user is a designer we don't want to show the submissions layout - submissionsLayout.setVisible(!mCourse.isDesigner) + submissionsLayout.setVisible(!course.isDesigner) if (!quiz.isGradeable) { // Quiz is not gradeable, don't show submission dials @@ -365,7 +376,7 @@ class QuizDetailsFragment : BasePresenterFragment< // Load instructions loadHtmlJob = instructionsWebViewWrapper.webView.loadHtmlWithIframes(requireContext(), quiz.description, { - instructionsWebViewWrapper.loadHtml(it, quiz.title, baseUrl = mQuiz.htmlUrl) + instructionsWebViewWrapper.loadHtml(it, quiz.title, baseUrl = this@QuizDetailsFragment.quiz.htmlUrl) }) { LtiLaunchFragment.routeLtiLaunchFragment(requireContext(), canvasContext, it) } @@ -375,22 +386,22 @@ class QuizDetailsFragment : BasePresenterFragment< dueLayout.setOnClickListener { if(quiz._assignment != null) { val args = DueDatesFragment.makeBundle(quiz._assignment!!) - RouteMatcher.route(requireContext(), Route(null, DueDatesFragment::class.java, mCourse, args)) + RouteMatcher.route(requireContext(), Route(null, DueDatesFragment::class.java, course, args)) } } submissionsLayout.setOnClickListener { - navigateToSubmissions(mCourse, quiz._assignment, SubmissionListFilter.ALL) + navigateToSubmissions(course, quiz._assignment, SubmissionListFilter.ALL) } donutGroup.viewAllSubmissions.onClick { submissionsLayout.performClick() } // Separate click listener for a11y donutGroup.gradedWrapper.setOnClickListener { - navigateToSubmissions(mCourse, quiz._assignment, SubmissionListFilter.GRADED) + navigateToSubmissions(course, quiz._assignment, SubmissionListFilter.GRADED) } donutGroup.ungradedWrapper.setOnClickListener { - navigateToSubmissions(mCourse, quiz._assignment, SubmissionListFilter.NOT_GRADED) + navigateToSubmissions(course, quiz._assignment, SubmissionListFilter.NOT_GRADED) } donutGroup.notSubmittedWrapper.setOnClickListener { - navigateToSubmissions(mCourse, quiz._assignment, SubmissionListFilter.MISSING) + navigateToSubmissions(course, quiz._assignment, SubmissionListFilter.MISSING) } noInstructionsTextView.setOnClickListener { openEditPage(quiz) @@ -399,19 +410,19 @@ class QuizDetailsFragment : BasePresenterFragment< ViewStyler.themeButton(quizPreviewButton.quizPreviewButton) quizPreviewButton.quizPreviewButton.setOnClickListener { try { - var urlStr = mQuiz.htmlUrl +"/take?preview=1&persist_headless=1" + var urlStr = this@QuizDetailsFragment.quiz.htmlUrl +"/take?preview=1&persist_headless=1" val url = URL(urlStr) val uri = URI(url.protocol, url.userInfo, url.host, url.port, url.path, url.query, url.ref) urlStr = uri.toASCIIString() val args = QuizPreviewWebviewFragment.makeBundle(urlStr, getString(R.string.quizPreview)) - RouteMatcher.route(requireContext(), Route(QuizPreviewWebviewFragment::class.java, mCourse, args)) + RouteMatcher.route(requireContext(), Route(QuizPreviewWebviewFragment::class.java, course, args)) } catch (e: UnsupportedEncodingException) {} } } private fun navigateToSubmissions(course: Course, assignment: Assignment?, filter: SubmissionListFilter) { assignment ?: return // We can't navigate to the submission list if there isn't an associated assignment - val assignmentWithAnonymousGrading = assignment.copy(anonymousGrading = mQuiz.allowAnonymousSubmissions) + val assignmentWithAnonymousGrading = assignment.copy(anonymousGrading = quiz.allowAnonymousSubmissions) val args = AssignmentSubmissionListFragment.makeBundle(assignmentWithAnonymousGrading, filter) RouteMatcher.route(requireContext(), Route(null, AssignmentSubmissionListFragment::class.java, course, args)) } @@ -428,7 +439,7 @@ class QuizDetailsFragment : BasePresenterFragment< private fun openEditPage(quiz: Quiz) { if(APIHelper.hasNetworkConnection()) { val args = EditQuizDetailsFragment.makeBundle(quiz, false) - RouteMatcher.route(requireContext(), Route(EditQuizDetailsFragment::class.java, mCourse, args)) + RouteMatcher.route(requireContext(), Route(EditQuizDetailsFragment::class.java, course, args)) } else { NoInternetConnectionDialog.show(requireFragmentManager()) } @@ -448,7 +459,7 @@ class QuizDetailsFragment : BasePresenterFragment< @Subscribe(threadMode = ThreadMode.MAIN, sticky = true) fun onQuizEdited(event: QuizUpdatedEvent) { event.once(javaClass.simpleName) { - if (it == presenter.mQuiz.id) mNeedToForceNetwork = true + if (it == presenter.mQuiz.id) needToForceNetwork = true } } @@ -456,7 +467,7 @@ class QuizDetailsFragment : BasePresenterFragment< @Subscribe(threadMode = ThreadMode.MAIN, sticky = true) fun onQuizGraded(event: QuizSubmissionGradedEvent) { event.once(javaClass.simpleName) { - if (presenter.mQuiz.assignmentId == it.assignmentId) mNeedToForceNetwork = true + if (presenter.mQuiz.assignmentId == it.assignmentId) needToForceNetwork = true } } @@ -468,6 +479,6 @@ class QuizDetailsFragment : BasePresenterFragment< fun makeBundle(quiz: Quiz): Bundle = Bundle().apply { putParcelable(QuizDetailsFragment.QUIZ, quiz) } - fun newInstance(course: Course, args: Bundle) = QuizDetailsFragment().withArgs(args).apply { mCourse = course } + fun newInstance(course: Course, args: Bundle) = QuizDetailsFragment().withArgs(args).apply { this.course = course } } } diff --git a/apps/teacher/src/main/java/com/instructure/teacher/fragments/QuizListFragment.kt b/apps/teacher/src/main/java/com/instructure/teacher/fragments/QuizListFragment.kt index e6a50ea6b1..f6ffe4d71f 100644 --- a/apps/teacher/src/main/java/com/instructure/teacher/fragments/QuizListFragment.kt +++ b/apps/teacher/src/main/java/com/instructure/teacher/fragments/QuizListFragment.kt @@ -23,6 +23,7 @@ import androidx.recyclerview.widget.RecyclerView import com.instructure.canvasapi2.models.CanvasContext import com.instructure.canvasapi2.models.Quiz import com.instructure.canvasapi2.utils.ApiPrefs +import com.instructure.canvasapi2.utils.pageview.PageView import com.instructure.interactions.router.Route import com.instructure.pandautils.analytics.SCREEN_VIEW_QUIZ_LIST import com.instructure.pandautils.analytics.ScreenView @@ -44,6 +45,7 @@ import org.greenrobot.eventbus.EventBus import org.greenrobot.eventbus.Subscribe import org.greenrobot.eventbus.ThreadMode +@PageView("{canvasContext}/quizzes") @ScreenView(SCREEN_VIEW_QUIZ_LIST) class QuizListFragment : BaseExpandableSyncFragment< String, @@ -54,18 +56,18 @@ class QuizListFragment : BaseExpandableSyncFragment< private val binding by viewBinding(FragmentQuizListBinding::bind) - private var mCanvasContext: CanvasContext by ParcelableArg(default = CanvasContext.getGenericContext(CanvasContext.Type.COURSE, -1L, "")) + private var canvasContext: CanvasContext by ParcelableArg(default = CanvasContext.getGenericContext(CanvasContext.Type.COURSE, -1L, "")) - private val mLinearLayoutManager by lazy { LinearLayoutManager(requireContext()) } + private val linearLayoutManager by lazy { LinearLayoutManager(requireContext()) } private lateinit var mRecyclerView: RecyclerView - private var mGradingPeriodMenu: PopupMenu? = null + private var gradingPeriodMenu: PopupMenu? = null - private var mNeedToForceNetwork = false + private var needToForceNetwork = false override fun layoutResId(): Int = R.layout.fragment_quiz_list override val recyclerView: RecyclerView get() = binding.quizRecyclerView - override fun getPresenterFactory() = QuizListPresenterFactory(mCanvasContext) + override fun getPresenterFactory() = QuizListPresenterFactory(canvasContext) override fun onPresenterPrepared(presenter: QuizListPresenter) { mRecyclerView = RecyclerViewUtils.buildRecyclerView( rootView = rootView, @@ -80,14 +82,14 @@ class QuizListFragment : BaseExpandableSyncFragment< } override fun onCreateView(view: View) { - mLinearLayoutManager.orientation = RecyclerView.VERTICAL + linearLayoutManager.orientation = RecyclerView.VERTICAL } override fun onReadySetGo(presenter: QuizListPresenter) { if(recyclerView.adapter == null) { mRecyclerView.adapter = adapter } - presenter.loadData(mNeedToForceNetwork) + presenter.loadData(needToForceNetwork) } override fun onResume() { @@ -106,19 +108,19 @@ class QuizListFragment : BaseExpandableSyncFragment< } override fun onPause() { - if(mGradingPeriodMenu != null) { - mGradingPeriodMenu?.dismiss() + if(gradingPeriodMenu != null) { + gradingPeriodMenu?.dismiss() } super.onPause() } override fun createAdapter(): QuizListAdapter { - return QuizListAdapter(requireContext(), presenter, mCanvasContext.textAndIconColor) { quiz -> + return QuizListAdapter(requireContext(), presenter, canvasContext.textAndIconColor) { quiz -> if (RouteMatcher.canRouteInternally(requireActivity(), quiz.htmlUrl, ApiPrefs.domain, false)) { RouteMatcher.routeUrl(requireActivity(), quiz.htmlUrl!!, ApiPrefs.domain) } else { val args = QuizDetailsFragment.makeBundle(quiz) - RouteMatcher.route(requireContext(), Route(null, QuizDetailsFragment::class.java, mCanvasContext, args)) + RouteMatcher.route(requireContext(), Route(null, QuizDetailsFragment::class.java, canvasContext, args)) } } } @@ -146,7 +148,7 @@ class QuizListFragment : BaseExpandableSyncFragment< private fun setupToolbar() = with(binding) { quizListToolbar.title = getString(R.string.tab_quizzes) - quizListToolbar.subtitle = mCanvasContext.name + quizListToolbar.subtitle = canvasContext.name quizListToolbar.setupBackButton(this@QuizListFragment) quizListToolbar.addSearch(getString(R.string.searchQuizzesHint)) { query -> if (query.isBlank()) { @@ -156,7 +158,7 @@ class QuizListFragment : BaseExpandableSyncFragment< } presenter.searchQuery = query } - ViewStyler.themeToolbarColored(requireActivity(), quizListToolbar, mCanvasContext.backgroundColor, requireContext().getColor(R.color.white)) + ViewStyler.themeToolbarColored(requireActivity(), quizListToolbar, canvasContext.backgroundColor, requireContext().getColor(R.color.white)) } override fun displayLoadingError() = toast(R.string.errorOccurred) @@ -168,14 +170,14 @@ class QuizListFragment : BaseExpandableSyncFragment< // need to set a flag here. Because we use the event bus in the fragment instead of the presenter for unit testing purposes, // when we come back to this fragment it will go through the life cycle events again and the cached data will immediately // overwrite the data from the network if we refresh the presenter from here. - mNeedToForceNetwork = true + needToForceNetwork = true } } @Suppress("unused") @Subscribe(threadMode = ThreadMode.MAIN, sticky = true) fun onQuizGraded(event: QuizSubmissionGradedEvent) { - event.once(javaClass.simpleName) { mNeedToForceNetwork = true } + event.once(javaClass.simpleName) { needToForceNetwork = true } } override fun onHandleBackPressed() = binding.quizListToolbar.closeSearch() @@ -183,7 +185,7 @@ class QuizListFragment : BaseExpandableSyncFragment< companion object { fun newInstance(canvasContext: CanvasContext) = QuizListFragment().apply { - mCanvasContext = canvasContext + this.canvasContext = canvasContext } } } diff --git a/apps/teacher/src/main/java/com/instructure/teacher/fragments/QuizPreviewWebviewFragment.kt b/apps/teacher/src/main/java/com/instructure/teacher/fragments/QuizPreviewWebviewFragment.kt index 8e14adebb4..62e77813c3 100644 --- a/apps/teacher/src/main/java/com/instructure/teacher/fragments/QuizPreviewWebviewFragment.kt +++ b/apps/teacher/src/main/java/com/instructure/teacher/fragments/QuizPreviewWebviewFragment.kt @@ -42,7 +42,7 @@ class QuizPreviewWebviewFragment : InternalWebViewFragment() { // Try to automatically tap the Preview button so we take them directly into the quiz preview Handler().postDelayed({ - requireActivity().runOnUiThread { + activity?.runOnUiThread { mClickedPreview = true val js = "javascript: { " + "document.getElementById('preview_quiz_button').click();" + diff --git a/apps/teacher/src/main/java/com/instructure/teacher/fragments/SettingsFragment.kt b/apps/teacher/src/main/java/com/instructure/teacher/fragments/SettingsFragment.kt index d2baca49ea..250435f453 100644 --- a/apps/teacher/src/main/java/com/instructure/teacher/fragments/SettingsFragment.kt +++ b/apps/teacher/src/main/java/com/instructure/teacher/fragments/SettingsFragment.kt @@ -18,6 +18,7 @@ package com.instructure.teacher.fragments import android.os.Bundle import android.view.View +import com.instructure.canvasapi2.models.CanvasContext import com.instructure.interactions.router.Route import com.instructure.pandautils.analytics.SCREEN_VIEW_SETTINGS import com.instructure.pandautils.analytics.ScreenView @@ -45,6 +46,8 @@ class SettingsFragment : BasePresenterFragment(), ToDoView { diff --git a/apps/teacher/src/main/java/com/instructure/teacher/fragments/ViewImageFragment.kt b/apps/teacher/src/main/java/com/instructure/teacher/fragments/ViewImageFragment.kt index 3ee57fa68b..fc10c97fcf 100644 --- a/apps/teacher/src/main/java/com/instructure/teacher/fragments/ViewImageFragment.kt +++ b/apps/teacher/src/main/java/com/instructure/teacher/fragments/ViewImageFragment.kt @@ -144,7 +144,9 @@ class ViewImageFragment : Fragment(), ShareableFile { fun colorBackground(bitmap: Bitmap) { // Generate palette asynchronously Palette.from(bitmap).generate { palette -> - palette?.let { binding.viewImageRootView.setBackgroundColor(it.getDarkMutedColor(requireContext().getColor(R.color.backgroundLightest))) } + if (view != null && palette != null) { + binding.viewImageRootView.setBackgroundColor(palette.getDarkMutedColor(requireContext().getColor(R.color.backgroundLightest))) + } } } diff --git a/apps/teacher/src/main/java/com/instructure/teacher/presenters/DiscussionsReplyPresenter.kt b/apps/teacher/src/main/java/com/instructure/teacher/presenters/DiscussionsReplyPresenter.kt index 6204d83421..aa115dc331 100644 --- a/apps/teacher/src/main/java/com/instructure/teacher/presenters/DiscussionsReplyPresenter.kt +++ b/apps/teacher/src/main/java/com/instructure/teacher/presenters/DiscussionsReplyPresenter.kt @@ -43,6 +43,8 @@ class DiscussionsReplyPresenter( private var postDiscussionJob: Job? = null + private var attachment: FileSubmitObject? = null + override fun loadData(forceNetwork: Boolean) {} override fun refresh(forceNetwork: Boolean) {} @@ -98,7 +100,6 @@ class DiscussionsReplyPresenter( const val REASON_MESSAGE_IN_PROGRESS = 1 const val REASON_MESSAGE_EMPTY = 2 const val REASON_MESSAGE_FAILED_TO_SEND = 3 - var attachment: FileSubmitObject? = null } override fun onDestroyed() { diff --git a/apps/teacher/src/main/java/com/instructure/teacher/presenters/SpeedGraderCommentsPresenter.kt b/apps/teacher/src/main/java/com/instructure/teacher/presenters/SpeedGraderCommentsPresenter.kt index 608bc51867..71191aed4b 100644 --- a/apps/teacher/src/main/java/com/instructure/teacher/presenters/SpeedGraderCommentsPresenter.kt +++ b/apps/teacher/src/main/java/com/instructure/teacher/presenters/SpeedGraderCommentsPresenter.kt @@ -29,10 +29,10 @@ import com.instructure.canvasapi2.utils.weave.catch import com.instructure.canvasapi2.utils.weave.tryWeave import com.instructure.canvasapi2.utils.weave.weave import com.instructure.pandautils.features.file.upload.worker.FileUploadWorker -import com.instructure.pandautils.room.daos.* -import com.instructure.pandautils.room.entities.FileUploadInputEntity -import com.instructure.pandautils.room.entities.PendingSubmissionCommentEntity -import com.instructure.pandautils.room.model.SubmissionCommentWithAttachments +import com.instructure.pandautils.room.appdatabase.daos.* +import com.instructure.pandautils.room.appdatabase.entities.FileUploadInputEntity +import com.instructure.pandautils.room.appdatabase.entities.PendingSubmissionCommentEntity +import com.instructure.pandautils.room.appdatabase.model.SubmissionCommentWithAttachments import com.instructure.teacher.events.SubmissionCommentsUpdated import com.instructure.teacher.events.SubmissionUpdatedEvent import com.instructure.teacher.events.post diff --git a/apps/teacher/src/main/java/com/instructure/teacher/router/RouteMatcher.kt b/apps/teacher/src/main/java/com/instructure/teacher/router/RouteMatcher.kt index 3bcea64709..9a62ea6752 100644 --- a/apps/teacher/src/main/java/com/instructure/teacher/router/RouteMatcher.kt +++ b/apps/teacher/src/main/java/com/instructure/teacher/router/RouteMatcher.kt @@ -122,6 +122,8 @@ object RouteMatcher : BaseRouteMatcher() { fullscreenFragments.add(LtiLaunchFragment::class.java) fullscreenFragments.add(SpeedGraderQuizWebViewFragment::class.java) fullscreenFragments.add(HtmlContentFragment::class.java) + fullscreenFragments.add(ViewPdfFragment::class.java) + fullscreenFragments.add(ViewHtmlFragment::class.java) // Bottom Sheet Fragments bottomSheetFragments.add(EditAssignmentDetailsFragment::class.java) diff --git a/apps/teacher/src/main/java/com/instructure/teacher/services/TeacherPageViewService.kt b/apps/teacher/src/main/java/com/instructure/teacher/services/TeacherPageViewService.kt new file mode 100644 index 0000000000..b43aade4ae --- /dev/null +++ b/apps/teacher/src/main/java/com/instructure/teacher/services/TeacherPageViewService.kt @@ -0,0 +1,27 @@ +package com.instructure.teacher.services + +import com.google.firebase.crashlytics.FirebaseCrashlytics +import com.instructure.canvasapi2.utils.pageview.PageViewUploadService +import com.instructure.canvasapi2.utils.pageview.PandataInfo +import com.instructure.teacher.BuildConfig + +/** + * A [PageViewUploadService] specific to the Teacher application. + * + * To test this service, install the app on a device running Android 7.1+ and run the following command: + * adb shell cmd jobscheduler run -f com.instructure.teacher 188372 + */ +class TeacherPageViewService : PageViewUploadService() { + + override val appKey = pandataAppKey + + override fun onException(e: Throwable) = FirebaseCrashlytics.getInstance().recordException(e) + + + companion object { + val pandataAppKey = PandataInfo.AppKey( + "CANVAS_TEACHER_ANDROID", + "Canvas Teacher for Android - ${BuildConfig.VERSION_NAME} (${BuildConfig.VERSION_CODE})" + ) + } +} diff --git a/apps/teacher/src/main/java/com/instructure/teacher/utils/BaseAppManager.kt b/apps/teacher/src/main/java/com/instructure/teacher/utils/BaseAppManager.kt index e371038f86..f79b57adcc 100644 --- a/apps/teacher/src/main/java/com/instructure/teacher/utils/BaseAppManager.kt +++ b/apps/teacher/src/main/java/com/instructure/teacher/utils/BaseAppManager.kt @@ -27,6 +27,7 @@ import com.heapanalytics.android.Heap import com.heapanalytics.android.config.Options import com.instructure.annotations.FileCaching.FileCache import com.instructure.canvasapi2.utils.* +import com.instructure.canvasapi2.utils.pageview.PageViewUploadService import com.instructure.loginapi.login.tasks.LogoutTask import com.instructure.pandautils.utils.AppTheme import com.instructure.pandautils.utils.ColorKeeper @@ -34,6 +35,7 @@ import com.instructure.pandautils.utils.Const import com.instructure.pandautils.utils.ThemePrefs import com.instructure.teacher.BuildConfig import com.instructure.teacher.R +import com.instructure.teacher.services.TeacherPageViewService import com.instructure.teacher.tasks.TeacherLogoutTask import com.pspdfkit.PSPDFKit import com.pspdfkit.exceptions.InvalidPSPDFKitLicenseException @@ -88,6 +90,8 @@ abstract class BaseAppManager : com.instructure.canvasapi2.AppManager(), Configu val options = Options() options.disableTracking() Heap.init(this, BuildConfig.HEAP_APP_ID, options) + + PageViewUploadService.schedule(this, TeacherPageViewService::class.java) } override fun performLogoutOnAuthError() { diff --git a/apps/teacher/src/main/java/com/instructure/teacher/utils/ModelExtensions.kt b/apps/teacher/src/main/java/com/instructure/teacher/utils/ModelExtensions.kt index a52ff8bbfb..915f33605f 100644 --- a/apps/teacher/src/main/java/com/instructure/teacher/utils/ModelExtensions.kt +++ b/apps/teacher/src/main/java/com/instructure/teacher/utils/ModelExtensions.kt @@ -56,7 +56,7 @@ val Attachment.isMediaSubmissionPlaceholder: Boolean get() = id == MEDIA_PLACEHO @JvmName("viewAttachment") fun Attachment.view(context: Context) { - viewMedia(context, filename!!, contentType!!, url, thumbnailUrl, displayName, iconRes) + viewMedia(context, filename!!, contentType!!, url, thumbnailUrl, displayName, iconRes, fullScreen = true) } /** @@ -65,14 +65,18 @@ fun Attachment.view(context: Context) { * file list) */ -fun viewMedia(context: Context, filename: String, contentType: String, url: String?, thumbnailUrl: String?, displayName: String?, iconRes: Int, toolbarColor: Int = 0, editableFile: EditableFile? = null) { +fun viewMedia(context: Context, filename: String, contentType: String, url: String?, thumbnailUrl: String?, displayName: String?, iconRes: Int, toolbarColor: Int = 0, editableFile: EditableFile? = null, fullScreen: Boolean = false) { val extension = filename.substringAfterLast('.') when { // PDF contentType == "application/pdf" -> { PdfFragment() val bundle = ViewPdfFragment.newInstance(url ?: "", toolbarColor, editableFile).nonNullArgs - RouteMatcher.route(context, Route(null, ViewPdfFragment::class.java, null, bundle)) + if (fullScreen) { + RouteMatcher.route(context, Route(ViewPdfFragment::class.java, null, bundle)) + } else { + RouteMatcher.route(context, Route(null, ViewPdfFragment::class.java, null, bundle)) + } } // Audio/Video contentType.startsWith("video") || contentType.startsWith("audio") -> { @@ -83,12 +87,20 @@ fun viewMedia(context: Context, filename: String, contentType: String, url: Stri contentType.startsWith("image") -> { val title = displayName ?: filename val bundle = ViewImageFragment.newInstance(title, Uri.parse(url), contentType, true, toolbarColor, editableFile).nonNullArgs - RouteMatcher.route(context, Route(null, ViewImageFragment::class.java, null, bundle)) + if (fullScreen) { + RouteMatcher.route(context, Route(ViewImageFragment::class.java, null, bundle)) + } else { + RouteMatcher.route(context, Route(null, ViewImageFragment::class.java, null, bundle)) + } } // HTML contentType == "text/html" || extension == "htm" || extension == "html" -> { val bundle = ViewHtmlFragment.makeDownloadBundle(url ?: "", filename, toolbarColor, editableFile) - RouteMatcher.route(context, Route(null, ViewHtmlFragment::class.java, null, bundle)) + if (fullScreen) { + RouteMatcher.route(context, Route(ViewHtmlFragment::class.java, null, bundle)) + } else { + RouteMatcher.route(context, Route(null, ViewHtmlFragment::class.java, null, bundle)) + } } // Multipart (Unknown) contentType == "multipart/form-data" -> { diff --git a/apps/teacher/src/main/java/com/instructure/teacher/utils/SGPendingMediaCommentReceiver.kt b/apps/teacher/src/main/java/com/instructure/teacher/utils/SGPendingMediaCommentReceiver.kt index 3f73eba592..69fdf2bdfd 100644 --- a/apps/teacher/src/main/java/com/instructure/teacher/utils/SGPendingMediaCommentReceiver.kt +++ b/apps/teacher/src/main/java/com/instructure/teacher/utils/SGPendingMediaCommentReceiver.kt @@ -22,7 +22,7 @@ import android.content.Intent import com.instructure.canvasapi2.models.SubmissionComment import com.instructure.canvasapi2.models.postmodels.CommentSendStatus import com.instructure.canvasapi2.models.postmodels.PendingSubmissionComment -import com.instructure.pandautils.room.daos.PendingSubmissionCommentDao +import com.instructure.pandautils.room.appdatabase.daos.PendingSubmissionCommentDao import com.instructure.pandautils.utils.Const import com.instructure.teacher.events.UploadMediaCommentUpdateEvent import com.instructure.teacher.events.post diff --git a/automation/espresso/src/main/kotlin/com/instructure/canvas/espresso/ReleaseExclude.kt b/automation/espresso/src/main/kotlin/com/instructure/canvas/espresso/ReleaseExclude.kt new file mode 100644 index 0000000000..382ce737a4 --- /dev/null +++ b/automation/espresso/src/main/kotlin/com/instructure/canvas/espresso/ReleaseExclude.kt @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2023 - present Instructure, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.instructure.canvas.espresso + +// When applied to a test method, denotes that the test is stubbed out from the release process because of 3rd party flakiness or any other reason (explained in the parameter). +@Target(AnnotationTarget.FUNCTION) +@Retention(AnnotationRetention.RUNTIME) +annotation class ReleaseExclude(val excludeReason: String = "") \ No newline at end of file diff --git a/automation/espresso/src/main/kotlin/com/instructure/canvas/espresso/mockCanvas/MockCanvas.kt b/automation/espresso/src/main/kotlin/com/instructure/canvas/espresso/mockCanvas/MockCanvas.kt index d0e4f46f42..5eef33883e 100644 --- a/automation/espresso/src/main/kotlin/com/instructure/canvas/espresso/mockCanvas/MockCanvas.kt +++ b/automation/espresso/src/main/kotlin/com/instructure/canvas/espresso/mockCanvas/MockCanvas.kt @@ -36,6 +36,7 @@ import okhttp3.mockwebserver.MockWebServer import okhttp3.mockwebserver.RecordedRequest import org.threeten.bp.OffsetDateTime import java.util.* +import kotlin.random.Random class MockCanvas { /** Fake domain */ @@ -1086,7 +1087,7 @@ fun MockCanvas.addUser(): User { val name = Randomizer.randomName() val email = Randomizer.randomEmail() val user = User( - id = Random().nextLong(), + id = Random.nextLong(), name = name.fullName, shortName = name.firstName, loginId = email, diff --git a/libs/canvas-api-2/src/main/java/com/instructure/canvasapi2/managers/CourseManager.kt b/libs/canvas-api-2/src/main/java/com/instructure/canvasapi2/managers/CourseManager.kt index 7109af762c..5283bade91 100644 --- a/libs/canvas-api-2/src/main/java/com/instructure/canvasapi2/managers/CourseManager.kt +++ b/libs/canvas-api-2/src/main/java/com/instructure/canvasapi2/managers/CourseManager.kt @@ -170,6 +170,8 @@ object CourseManager { CourseAPI.getGradingPeriodsForCourse(adapter, callback, params, courseId) } + fun getCourseAsync(courseId: Long, forceNetwork: Boolean) = apiAsync { getCourse(courseId, it, forceNetwork) } + fun getCourse(courseId: Long, callback: StatusCallback, forceNetwork: Boolean) { val adapter = RestBuilder(callback) val params = RestParams(isForceReadFromNetwork = forceNetwork) diff --git a/libs/canvas-api-2/src/main/java/com/instructure/canvasapi2/utils/pageview/PageViewUploadService.kt b/libs/canvas-api-2/src/main/java/com/instructure/canvasapi2/utils/pageview/PageViewUploadService.kt index c941720c02..2945c2c42d 100644 --- a/libs/canvas-api-2/src/main/java/com/instructure/canvasapi2/utils/pageview/PageViewUploadService.kt +++ b/libs/canvas-api-2/src/main/java/com/instructure/canvasapi2/utils/pageview/PageViewUploadService.kt @@ -22,13 +22,15 @@ import android.app.job.JobScheduler import android.app.job.JobService import android.content.ComponentName import android.content.Context -import android.os.Build import androidx.annotation.RequiresPermission -import com.instructure.canvasapi2.utils.* +import com.instructure.canvasapi2.utils.ApiPrefs +import com.instructure.canvasapi2.utils.Logger +import com.instructure.canvasapi2.utils.isValid import com.instructure.canvasapi2.utils.weave.WeaveJob import com.instructure.canvasapi2.utils.weave.awaitApi import com.instructure.canvasapi2.utils.weave.catch import com.instructure.canvasapi2.utils.weave.tryWeave +import com.instructure.canvasapi2.utils.zonedDateTime import org.threeten.bp.format.DateTimeFormatter import java.util.* import java.util.concurrent.TimeUnit @@ -48,7 +50,6 @@ abstract class PageViewUploadService : JobService() { * which a concrete implementation of this [PageViewUploadService] is declared. */ abstract val appKey: PandataInfo.AppKey - /** * Called if an exception occurs while uploading events. It is recommended to log these exceptions in a * highly visible location (e.g. Crashlytics). diff --git a/libs/flutter_student_embed/lib/l10n/res/intl_ar.arb b/libs/flutter_student_embed/lib/l10n/res/intl_ar.arb index 1a638c992f..df47f0ed8d 100644 --- a/libs/flutter_student_embed/lib/l10n/res/intl_ar.arb +++ b/libs/flutter_student_embed/lib/l10n/res/intl_ar.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2022-10-28T11:03:17.232435", + "@@last_modified": "2023-03-31T11:03:46.341309", "coursesLabel": "المساقات", "@coursesLabel": { "description": "The label for the Courses tab", @@ -101,6 +101,19 @@ "points": {} } }, + "calendarDaySemanticsLabel": "{eventCount,plural, =1{{date}، {eventCount} حدث}other{{date}، {eventCount} أحداث}}", + "@calendarDaySemanticsLabel": { + "description": "Screen reader label used for calendar day, reads the date and count of events", + "type": "text", + "placeholders_order": [ + "date", + "eventCount" + ], + "placeholders": { + "date": {}, + "eventCount": {} + } + }, "No Events Today!": "لا توجد أحداث اليوم!", "@No Events Today!": { "description": "Title displayed when there are no calendar events for the current day", diff --git a/libs/flutter_student_embed/lib/l10n/res/intl_ca.arb b/libs/flutter_student_embed/lib/l10n/res/intl_ca.arb index 96e5e799de..b5e7a68d69 100644 --- a/libs/flutter_student_embed/lib/l10n/res/intl_ca.arb +++ b/libs/flutter_student_embed/lib/l10n/res/intl_ca.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2022-10-28T11:03:17.232435", + "@@last_modified": "2023-03-31T11:03:46.341309", "coursesLabel": "Assignatures", "@coursesLabel": { "description": "The label for the Courses tab", @@ -101,6 +101,19 @@ "points": {} } }, + "calendarDaySemanticsLabel": "{eventCount,plural, =1{{date}, {eventCount} esdeveniment}other{{date}, {eventCount} esdeveniments}}", + "@calendarDaySemanticsLabel": { + "description": "Screen reader label used for calendar day, reads the date and count of events", + "type": "text", + "placeholders_order": [ + "date", + "eventCount" + ], + "placeholders": { + "date": {}, + "eventCount": {} + } + }, "No Events Today!": "Avui no hi ha cap esdeveniment.", "@No Events Today!": { "description": "Title displayed when there are no calendar events for the current day", diff --git a/libs/flutter_student_embed/lib/l10n/res/intl_cy.arb b/libs/flutter_student_embed/lib/l10n/res/intl_cy.arb index 5e35fe4919..a5b5d82883 100644 --- a/libs/flutter_student_embed/lib/l10n/res/intl_cy.arb +++ b/libs/flutter_student_embed/lib/l10n/res/intl_cy.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2022-10-28T11:03:17.232435", + "@@last_modified": "2023-03-31T11:03:46.341309", "coursesLabel": "Cyrsiau", "@coursesLabel": { "description": "The label for the Courses tab", @@ -101,6 +101,19 @@ "points": {} } }, + "calendarDaySemanticsLabel": "{eventCount,plural, =1{{date}, {eventCount} digwyddiad}other{{date}, {eventCount} digwyddiad}}", + "@calendarDaySemanticsLabel": { + "description": "Screen reader label used for calendar day, reads the date and count of events", + "type": "text", + "placeholders_order": [ + "date", + "eventCount" + ], + "placeholders": { + "date": {}, + "eventCount": {} + } + }, "No Events Today!": "Dim Digwyddiadau Heddiw!", "@No Events Today!": { "description": "Title displayed when there are no calendar events for the current day", diff --git a/libs/flutter_student_embed/lib/l10n/res/intl_da.arb b/libs/flutter_student_embed/lib/l10n/res/intl_da.arb index 7a65756323..ab70abf121 100644 --- a/libs/flutter_student_embed/lib/l10n/res/intl_da.arb +++ b/libs/flutter_student_embed/lib/l10n/res/intl_da.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2022-10-28T11:03:17.232435", + "@@last_modified": "2023-03-31T11:03:46.341309", "coursesLabel": "Fag", "@coursesLabel": { "description": "The label for the Courses tab", @@ -101,6 +101,19 @@ "points": {} } }, + "calendarDaySemanticsLabel": "{eventCount,plural, =1{{date}, {eventCount} begivenhed}other{{date}, {eventCount} begivenheder}}", + "@calendarDaySemanticsLabel": { + "description": "Screen reader label used for calendar day, reads the date and count of events", + "type": "text", + "placeholders_order": [ + "date", + "eventCount" + ], + "placeholders": { + "date": {}, + "eventCount": {} + } + }, "No Events Today!": "Ingen begivenheder i dag!", "@No Events Today!": { "description": "Title displayed when there are no calendar events for the current day", diff --git a/libs/flutter_student_embed/lib/l10n/res/intl_da_instk12.arb b/libs/flutter_student_embed/lib/l10n/res/intl_da_instk12.arb index d2e2f04ee4..5d2d33c3d4 100644 --- a/libs/flutter_student_embed/lib/l10n/res/intl_da_instk12.arb +++ b/libs/flutter_student_embed/lib/l10n/res/intl_da_instk12.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2022-10-28T11:03:17.232435", + "@@last_modified": "2023-03-31T11:03:46.341309", "coursesLabel": "Fag", "@coursesLabel": { "description": "The label for the Courses tab", @@ -101,6 +101,19 @@ "points": {} } }, + "calendarDaySemanticsLabel": "{eventCount,plural, =1{{date}, {eventCount} begivenhed}other{{date}, {eventCount} begivenheder}}", + "@calendarDaySemanticsLabel": { + "description": "Screen reader label used for calendar day, reads the date and count of events", + "type": "text", + "placeholders_order": [ + "date", + "eventCount" + ], + "placeholders": { + "date": {}, + "eventCount": {} + } + }, "No Events Today!": "Ingen begivenheder i dag!", "@No Events Today!": { "description": "Title displayed when there are no calendar events for the current day", diff --git a/libs/flutter_student_embed/lib/l10n/res/intl_de.arb b/libs/flutter_student_embed/lib/l10n/res/intl_de.arb index 912bd1940a..69c7097f06 100644 --- a/libs/flutter_student_embed/lib/l10n/res/intl_de.arb +++ b/libs/flutter_student_embed/lib/l10n/res/intl_de.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2022-10-28T11:03:17.232435", + "@@last_modified": "2023-03-31T11:03:46.341309", "coursesLabel": "Kurse", "@coursesLabel": { "description": "The label for the Courses tab", @@ -101,6 +101,19 @@ "points": {} } }, + "calendarDaySemanticsLabel": "{eventCount,plural, =1{{date}, {eventCount} Ereignis}other{{date}, {eventCount} Ereignisse}}", + "@calendarDaySemanticsLabel": { + "description": "Screen reader label used for calendar day, reads the date and count of events", + "type": "text", + "placeholders_order": [ + "date", + "eventCount" + ], + "placeholders": { + "date": {}, + "eventCount": {} + } + }, "No Events Today!": "Heute keine Ereignisse!", "@No Events Today!": { "description": "Title displayed when there are no calendar events for the current day", diff --git a/libs/flutter_student_embed/lib/l10n/res/intl_en.arb b/libs/flutter_student_embed/lib/l10n/res/intl_en.arb index 424941086b..428ebdf814 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": "2023-03-31T11:03:46.341309", + "@@last_modified": "2023-04-14T11:04:56.872320", "coursesLabel": "Courses", "@coursesLabel": { "description": "The label for the Courses tab", diff --git a/libs/flutter_student_embed/lib/l10n/res/intl_en_AU.arb b/libs/flutter_student_embed/lib/l10n/res/intl_en_AU.arb index 82c16296a7..0d149e4227 100644 --- a/libs/flutter_student_embed/lib/l10n/res/intl_en_AU.arb +++ b/libs/flutter_student_embed/lib/l10n/res/intl_en_AU.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2022-10-28T11:03:17.232435", + "@@last_modified": "2023-03-31T11:03:46.341309", "coursesLabel": "Courses", "@coursesLabel": { "description": "The label for the Courses tab", @@ -101,6 +101,19 @@ "points": {} } }, + "calendarDaySemanticsLabel": "{eventCount,plural, =1{{date}, {eventCount} event}other{{date}, {eventCount} events}}", + "@calendarDaySemanticsLabel": { + "description": "Screen reader label used for calendar day, reads the date and count of events", + "type": "text", + "placeholders_order": [ + "date", + "eventCount" + ], + "placeholders": { + "date": {}, + "eventCount": {} + } + }, "No Events Today!": "No Events Today!", "@No Events Today!": { "description": "Title displayed when there are no calendar events for the current day", diff --git a/libs/flutter_student_embed/lib/l10n/res/intl_en_AU_unimelb.arb b/libs/flutter_student_embed/lib/l10n/res/intl_en_AU_unimelb.arb index fb62f0b8ee..8414f8d960 100644 --- a/libs/flutter_student_embed/lib/l10n/res/intl_en_AU_unimelb.arb +++ b/libs/flutter_student_embed/lib/l10n/res/intl_en_AU_unimelb.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2022-10-28T11:03:17.232435", + "@@last_modified": "2023-03-31T11:03:46.341309", "coursesLabel": "Subjects", "@coursesLabel": { "description": "The label for the Courses tab", @@ -101,6 +101,19 @@ "points": {} } }, + "calendarDaySemanticsLabel": "{eventCount,plural, =1{{date}, {eventCount} event}other{{date}, {eventCount} events}}", + "@calendarDaySemanticsLabel": { + "description": "Screen reader label used for calendar day, reads the date and count of events", + "type": "text", + "placeholders_order": [ + "date", + "eventCount" + ], + "placeholders": { + "date": {}, + "eventCount": {} + } + }, "No Events Today!": "No Events Today!", "@No Events Today!": { "description": "Title displayed when there are no calendar events for the current day", diff --git a/libs/flutter_student_embed/lib/l10n/res/intl_en_CY.arb b/libs/flutter_student_embed/lib/l10n/res/intl_en_CY.arb index a1842afe74..6daa11570b 100644 --- a/libs/flutter_student_embed/lib/l10n/res/intl_en_CY.arb +++ b/libs/flutter_student_embed/lib/l10n/res/intl_en_CY.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2022-10-28T11:03:17.232435", + "@@last_modified": "2023-03-31T11:03:46.341309", "coursesLabel": "Modules", "@coursesLabel": { "description": "The label for the Courses tab", @@ -101,6 +101,19 @@ "points": {} } }, + "calendarDaySemanticsLabel": "{eventCount,plural, =1{{date}, {eventCount} event}other{{date}, {eventCount} events}}", + "@calendarDaySemanticsLabel": { + "description": "Screen reader label used for calendar day, reads the date and count of events", + "type": "text", + "placeholders_order": [ + "date", + "eventCount" + ], + "placeholders": { + "date": {}, + "eventCount": {} + } + }, "No Events Today!": "No Events Today!", "@No Events Today!": { "description": "Title displayed when there are no calendar events for the current day", diff --git a/libs/flutter_student_embed/lib/l10n/res/intl_en_GB.arb b/libs/flutter_student_embed/lib/l10n/res/intl_en_GB.arb index 25cd1e3c4e..1faa04e103 100644 --- a/libs/flutter_student_embed/lib/l10n/res/intl_en_GB.arb +++ b/libs/flutter_student_embed/lib/l10n/res/intl_en_GB.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2022-10-28T11:03:17.232435", + "@@last_modified": "2023-03-31T11:03:46.341309", "coursesLabel": "Courses", "@coursesLabel": { "description": "The label for the Courses tab", @@ -101,6 +101,19 @@ "points": {} } }, + "calendarDaySemanticsLabel": "{eventCount,plural, =1{{date}, {eventCount} event}other{{date}, {eventCount} events}}", + "@calendarDaySemanticsLabel": { + "description": "Screen reader label used for calendar day, reads the date and count of events", + "type": "text", + "placeholders_order": [ + "date", + "eventCount" + ], + "placeholders": { + "date": {}, + "eventCount": {} + } + }, "No Events Today!": "No Events Today!", "@No Events Today!": { "description": "Title displayed when there are no calendar events for the current day", diff --git a/libs/flutter_student_embed/lib/l10n/res/intl_en_GB_instukhe.arb b/libs/flutter_student_embed/lib/l10n/res/intl_en_GB_instukhe.arb index d3d14e047d..6daa11570b 100644 --- a/libs/flutter_student_embed/lib/l10n/res/intl_en_GB_instukhe.arb +++ b/libs/flutter_student_embed/lib/l10n/res/intl_en_GB_instukhe.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2022-01-28T12:37:53.041723", + "@@last_modified": "2023-03-31T11:03:46.341309", "coursesLabel": "Modules", "@coursesLabel": { "description": "The label for the Courses tab", @@ -101,6 +101,19 @@ "points": {} } }, + "calendarDaySemanticsLabel": "{eventCount,plural, =1{{date}, {eventCount} event}other{{date}, {eventCount} events}}", + "@calendarDaySemanticsLabel": { + "description": "Screen reader label used for calendar day, reads the date and count of events", + "type": "text", + "placeholders_order": [ + "date", + "eventCount" + ], + "placeholders": { + "date": {}, + "eventCount": {} + } + }, "No Events Today!": "No Events Today!", "@No Events Today!": { "description": "Title displayed when there are no calendar events for the current day", @@ -122,9 +135,9 @@ "placeholders_order": [], "placeholders": {} }, - "Tap to favorite the courses you want to see on the Calendar.": "Tap to favourite the modules you want to see on the Calendar.", - "@Tap to favorite the courses you want to see on the Calendar.": { - "description": "Description text on calendar filter screen.", + "Select elements to display on the calendar.": "Select elements to display on the calendar.", + "@Select elements to display on the calendar.": { + "description": "Select calendars description", "type": "text", "placeholders_order": [], "placeholders": {} @@ -444,4 +457,4 @@ "time": {} } } -} +} \ No newline at end of file diff --git a/libs/flutter_student_embed/lib/l10n/res/intl_es.arb b/libs/flutter_student_embed/lib/l10n/res/intl_es.arb index 468fba2792..456f393c50 100644 --- a/libs/flutter_student_embed/lib/l10n/res/intl_es.arb +++ b/libs/flutter_student_embed/lib/l10n/res/intl_es.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2022-10-28T11:03:17.232435", + "@@last_modified": "2023-03-31T11:03:46.341309", "coursesLabel": "Cursos", "@coursesLabel": { "description": "The label for the Courses tab", @@ -101,6 +101,19 @@ "points": {} } }, + "calendarDaySemanticsLabel": "{eventCount,plural, =1{{date}, {eventCount} evento}other{{date}, {eventCount} eventos}}", + "@calendarDaySemanticsLabel": { + "description": "Screen reader label used for calendar day, reads the date and count of events", + "type": "text", + "placeholders_order": [ + "date", + "eventCount" + ], + "placeholders": { + "date": {}, + "eventCount": {} + } + }, "No Events Today!": "¡No hay ningún evento hoy!", "@No Events Today!": { "description": "Title displayed when there are no calendar events for the current day", diff --git a/libs/flutter_student_embed/lib/l10n/res/intl_es_ES.arb b/libs/flutter_student_embed/lib/l10n/res/intl_es_ES.arb index 825ade2d33..43eef22427 100644 --- a/libs/flutter_student_embed/lib/l10n/res/intl_es_ES.arb +++ b/libs/flutter_student_embed/lib/l10n/res/intl_es_ES.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2022-10-28T11:03:17.232435", + "@@last_modified": "2023-03-31T11:03:46.341309", "coursesLabel": "Asignaturas", "@coursesLabel": { "description": "The label for the Courses tab", @@ -101,6 +101,19 @@ "points": {} } }, + "calendarDaySemanticsLabel": "{eventCount,plural, =1{{date}, {eventCount} evento}other{{date}, {eventCount} eventos}}", + "@calendarDaySemanticsLabel": { + "description": "Screen reader label used for calendar day, reads the date and count of events", + "type": "text", + "placeholders_order": [ + "date", + "eventCount" + ], + "placeholders": { + "date": {}, + "eventCount": {} + } + }, "No Events Today!": "No hay ningún evento hoy", "@No Events Today!": { "description": "Title displayed when there are no calendar events for the current day", diff --git a/libs/flutter_student_embed/lib/l10n/res/intl_fi.arb b/libs/flutter_student_embed/lib/l10n/res/intl_fi.arb index 9ad58f1a2c..01811b5e04 100644 --- a/libs/flutter_student_embed/lib/l10n/res/intl_fi.arb +++ b/libs/flutter_student_embed/lib/l10n/res/intl_fi.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2022-10-28T11:03:17.232435", + "@@last_modified": "2023-03-31T11:03:46.341309", "coursesLabel": "Kurssit", "@coursesLabel": { "description": "The label for the Courses tab", @@ -101,6 +101,19 @@ "points": {} } }, + "calendarDaySemanticsLabel": "{eventCount,plural, =1{{date}, {eventCount} tapahtuma}other{{date}, {eventCount} tapahtumaa}}", + "@calendarDaySemanticsLabel": { + "description": "Screen reader label used for calendar day, reads the date and count of events", + "type": "text", + "placeholders_order": [ + "date", + "eventCount" + ], + "placeholders": { + "date": {}, + "eventCount": {} + } + }, "No Events Today!": "Ei tapahtumia tänään!", "@No Events Today!": { "description": "Title displayed when there are no calendar events for the current day", diff --git a/libs/flutter_student_embed/lib/l10n/res/intl_fr.arb b/libs/flutter_student_embed/lib/l10n/res/intl_fr.arb index 43db096d53..c8f1da3279 100644 --- a/libs/flutter_student_embed/lib/l10n/res/intl_fr.arb +++ b/libs/flutter_student_embed/lib/l10n/res/intl_fr.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2022-10-28T11:03:17.232435", + "@@last_modified": "2023-03-31T11:03:46.341309", "coursesLabel": "Cours", "@coursesLabel": { "description": "The label for the Courses tab", @@ -101,6 +101,19 @@ "points": {} } }, + "calendarDaySemanticsLabel": "{eventCount,plural, =1{{date}, {eventCount} événement}other{{date}, {eventCount} événements}}", + "@calendarDaySemanticsLabel": { + "description": "Screen reader label used for calendar day, reads the date and count of events", + "type": "text", + "placeholders_order": [ + "date", + "eventCount" + ], + "placeholders": { + "date": {}, + "eventCount": {} + } + }, "No Events Today!": "Aucun événement aujourd'hui !", "@No Events Today!": { "description": "Title displayed when there are no calendar events for the current day", diff --git a/libs/flutter_student_embed/lib/l10n/res/intl_fr_CA.arb b/libs/flutter_student_embed/lib/l10n/res/intl_fr_CA.arb index 8077cdb498..f7caa53a07 100644 --- a/libs/flutter_student_embed/lib/l10n/res/intl_fr_CA.arb +++ b/libs/flutter_student_embed/lib/l10n/res/intl_fr_CA.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2022-10-28T11:03:17.232435", + "@@last_modified": "2023-03-31T11:03:46.341309", "coursesLabel": "Cours", "@coursesLabel": { "description": "The label for the Courses tab", @@ -101,6 +101,19 @@ "points": {} } }, + "calendarDaySemanticsLabel": "{eventCount,plural, =1{{date}, {eventCount} événement}other{{date}, {eventCount} événements}}", + "@calendarDaySemanticsLabel": { + "description": "Screen reader label used for calendar day, reads the date and count of events", + "type": "text", + "placeholders_order": [ + "date", + "eventCount" + ], + "placeholders": { + "date": {}, + "eventCount": {} + } + }, "No Events Today!": "Aucun événement d’aujourd’hui!", "@No Events Today!": { "description": "Title displayed when there are no calendar events for the current day", diff --git a/libs/flutter_student_embed/lib/l10n/res/intl_ht.arb b/libs/flutter_student_embed/lib/l10n/res/intl_ht.arb index 1a915a206e..df331ea92c 100644 --- a/libs/flutter_student_embed/lib/l10n/res/intl_ht.arb +++ b/libs/flutter_student_embed/lib/l10n/res/intl_ht.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2022-10-28T11:03:17.232435", + "@@last_modified": "2023-03-31T11:03:46.341309", "coursesLabel": "Kou", "@coursesLabel": { "description": "The label for the Courses tab", @@ -101,6 +101,19 @@ "points": {} } }, + "calendarDaySemanticsLabel": "{eventCount,plural, =1{{date}, {eventCount} evènman}other{{date}, {eventCount} evènman}}", + "@calendarDaySemanticsLabel": { + "description": "Screen reader label used for calendar day, reads the date and count of events", + "type": "text", + "placeholders_order": [ + "date", + "eventCount" + ], + "placeholders": { + "date": {}, + "eventCount": {} + } + }, "No Events Today!": "Pa gen Aktivite Jodi a!", "@No Events Today!": { "description": "Title displayed when there are no calendar events for the current day", diff --git a/libs/flutter_student_embed/lib/l10n/res/intl_is.arb b/libs/flutter_student_embed/lib/l10n/res/intl_is.arb index 6ee8a5839a..40ec80182c 100644 --- a/libs/flutter_student_embed/lib/l10n/res/intl_is.arb +++ b/libs/flutter_student_embed/lib/l10n/res/intl_is.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2022-10-28T11:03:17.232435", + "@@last_modified": "2023-03-31T11:03:46.341309", "coursesLabel": "Námskeið", "@coursesLabel": { "description": "The label for the Courses tab", @@ -101,6 +101,19 @@ "points": {} } }, + "calendarDaySemanticsLabel": "{eventCount,plural, =1{{date}, {eventCount} viðburður}other{{date}, {eventCount} viðburðir}}", + "@calendarDaySemanticsLabel": { + "description": "Screen reader label used for calendar day, reads the date and count of events", + "type": "text", + "placeholders_order": [ + "date", + "eventCount" + ], + "placeholders": { + "date": {}, + "eventCount": {} + } + }, "No Events Today!": "Engir viðburðir í dag!", "@No Events Today!": { "description": "Title displayed when there are no calendar events for the current day", diff --git a/libs/flutter_student_embed/lib/l10n/res/intl_it.arb b/libs/flutter_student_embed/lib/l10n/res/intl_it.arb index a8b6175ca3..9a6053ab56 100644 --- a/libs/flutter_student_embed/lib/l10n/res/intl_it.arb +++ b/libs/flutter_student_embed/lib/l10n/res/intl_it.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2022-10-28T11:03:17.232435", + "@@last_modified": "2023-03-31T11:03:46.341309", "coursesLabel": "Corsi", "@coursesLabel": { "description": "The label for the Courses tab", @@ -101,6 +101,19 @@ "points": {} } }, + "calendarDaySemanticsLabel": "{eventCount,plural, =1{{date}, {eventCount} evento}other{{date}, {eventCount} eventi}}", + "@calendarDaySemanticsLabel": { + "description": "Screen reader label used for calendar day, reads the date and count of events", + "type": "text", + "placeholders_order": [ + "date", + "eventCount" + ], + "placeholders": { + "date": {}, + "eventCount": {} + } + }, "No Events Today!": "Nessun evento oggi!", "@No Events Today!": { "description": "Title displayed when there are no calendar events for the current day", diff --git a/libs/flutter_student_embed/lib/l10n/res/intl_ja.arb b/libs/flutter_student_embed/lib/l10n/res/intl_ja.arb index 2484eaf737..9a29c1dad8 100644 --- a/libs/flutter_student_embed/lib/l10n/res/intl_ja.arb +++ b/libs/flutter_student_embed/lib/l10n/res/intl_ja.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2022-10-28T11:03:17.232435", + "@@last_modified": "2023-03-31T11:03:46.341309", "coursesLabel": "コース", "@coursesLabel": { "description": "The label for the Courses tab", @@ -101,6 +101,19 @@ "points": {} } }, + "calendarDaySemanticsLabel": "{eventCount,plural, =1{{date}、{eventCount}イベント}other{{date}、{eventCount}イベント}}", + "@calendarDaySemanticsLabel": { + "description": "Screen reader label used for calendar day, reads the date and count of events", + "type": "text", + "placeholders_order": [ + "date", + "eventCount" + ], + "placeholders": { + "date": {}, + "eventCount": {} + } + }, "No Events Today!": "今日イベントはありません!", "@No Events Today!": { "description": "Title displayed when there are no calendar events for the current day", diff --git a/libs/flutter_student_embed/lib/l10n/res/intl_messages.arb b/libs/flutter_student_embed/lib/l10n/res/intl_messages.arb index 424941086b..428ebdf814 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": "2023-03-31T11:03:46.341309", + "@@last_modified": "2023-04-14T11:04:56.872320", "coursesLabel": "Courses", "@coursesLabel": { "description": "The label for the Courses tab", diff --git a/libs/flutter_student_embed/lib/l10n/res/intl_mi.arb b/libs/flutter_student_embed/lib/l10n/res/intl_mi.arb index 68a24f7624..1ac88220fd 100644 --- a/libs/flutter_student_embed/lib/l10n/res/intl_mi.arb +++ b/libs/flutter_student_embed/lib/l10n/res/intl_mi.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2022-10-28T11:03:17.232435", + "@@last_modified": "2023-03-31T11:03:46.341309", "coursesLabel": "Ngā Akoranga", "@coursesLabel": { "description": "The label for the Courses tab", @@ -101,6 +101,19 @@ "points": {} } }, + "calendarDaySemanticsLabel": "{eventCount,plural, =1{{date}, {eventCount} takahanga}other{{date}, {eventCount} takahanga}}", + "@calendarDaySemanticsLabel": { + "description": "Screen reader label used for calendar day, reads the date and count of events", + "type": "text", + "placeholders_order": [ + "date", + "eventCount" + ], + "placeholders": { + "date": {}, + "eventCount": {} + } + }, "No Events Today!": "Kaore he tauwhāinga i tēnei rā!", "@No Events Today!": { "description": "Title displayed when there are no calendar events for the current day", diff --git a/libs/flutter_student_embed/lib/l10n/res/intl_ms.arb b/libs/flutter_student_embed/lib/l10n/res/intl_ms.arb index dc0f9b9546..5c2f9f1f1d 100644 --- a/libs/flutter_student_embed/lib/l10n/res/intl_ms.arb +++ b/libs/flutter_student_embed/lib/l10n/res/intl_ms.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2022-10-28T11:03:17.232435", + "@@last_modified": "2023-03-31T11:03:46.341309", "coursesLabel": "Kursus", "@coursesLabel": { "description": "The label for the Courses tab", @@ -101,6 +101,19 @@ "points": {} } }, + "calendarDaySemanticsLabel": "{eventCount,plural, =1{{date}, {eventCount} acara}other{{date}, {eventCount} acara}}", + "@calendarDaySemanticsLabel": { + "description": "Screen reader label used for calendar day, reads the date and count of events", + "type": "text", + "placeholders_order": [ + "date", + "eventCount" + ], + "placeholders": { + "date": {}, + "eventCount": {} + } + }, "No Events Today!": "Tiada Acara Hari Ini!", "@No Events Today!": { "description": "Title displayed when there are no calendar events for the current day", diff --git a/libs/flutter_student_embed/lib/l10n/res/intl_nb.arb b/libs/flutter_student_embed/lib/l10n/res/intl_nb.arb index c38a824e34..be1ef9e45a 100644 --- a/libs/flutter_student_embed/lib/l10n/res/intl_nb.arb +++ b/libs/flutter_student_embed/lib/l10n/res/intl_nb.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2022-10-28T11:03:17.232435", + "@@last_modified": "2023-03-31T11:03:46.341309", "coursesLabel": "Emner", "@coursesLabel": { "description": "The label for the Courses tab", @@ -101,6 +101,19 @@ "points": {} } }, + "calendarDaySemanticsLabel": "{eventCount,plural, =1{{date}, {eventCount} hendelse}other{{date}, {eventCount} hendelser}}", + "@calendarDaySemanticsLabel": { + "description": "Screen reader label used for calendar day, reads the date and count of events", + "type": "text", + "placeholders_order": [ + "date", + "eventCount" + ], + "placeholders": { + "date": {}, + "eventCount": {} + } + }, "No Events Today!": "Ingen arrangementer i dag!", "@No Events Today!": { "description": "Title displayed when there are no calendar events for the current day", diff --git a/libs/flutter_student_embed/lib/l10n/res/intl_nb_instk12.arb b/libs/flutter_student_embed/lib/l10n/res/intl_nb_instk12.arb index 2a7aa4d9a5..dfda522719 100644 --- a/libs/flutter_student_embed/lib/l10n/res/intl_nb_instk12.arb +++ b/libs/flutter_student_embed/lib/l10n/res/intl_nb_instk12.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2022-10-28T11:03:17.232435", + "@@last_modified": "2023-03-31T11:03:46.341309", "coursesLabel": "Fag", "@coursesLabel": { "description": "The label for the Courses tab", @@ -101,6 +101,19 @@ "points": {} } }, + "calendarDaySemanticsLabel": "{eventCount,plural, =1{{date}, {eventCount} hendelse}other{{date}, {eventCount} hendelser}}", + "@calendarDaySemanticsLabel": { + "description": "Screen reader label used for calendar day, reads the date and count of events", + "type": "text", + "placeholders_order": [ + "date", + "eventCount" + ], + "placeholders": { + "date": {}, + "eventCount": {} + } + }, "No Events Today!": "Ingen arrangementer i dag!", "@No Events Today!": { "description": "Title displayed when there are no calendar events for the current day", diff --git a/libs/flutter_student_embed/lib/l10n/res/intl_nl.arb b/libs/flutter_student_embed/lib/l10n/res/intl_nl.arb index e1dda6e464..30cd7251ff 100644 --- a/libs/flutter_student_embed/lib/l10n/res/intl_nl.arb +++ b/libs/flutter_student_embed/lib/l10n/res/intl_nl.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2022-10-28T11:03:17.232435", + "@@last_modified": "2023-03-31T11:03:46.341309", "coursesLabel": "Cursussen", "@coursesLabel": { "description": "The label for the Courses tab", @@ -101,6 +101,19 @@ "points": {} } }, + "calendarDaySemanticsLabel": "{eventCount,plural, =1{{date}, {eventCount} gebeurtenis}other{{date}, {eventCount} gebeurtenissen}}", + "@calendarDaySemanticsLabel": { + "description": "Screen reader label used for calendar day, reads the date and count of events", + "type": "text", + "placeholders_order": [ + "date", + "eventCount" + ], + "placeholders": { + "date": {}, + "eventCount": {} + } + }, "No Events Today!": "Vandaag geen gebeurtenissen!", "@No Events Today!": { "description": "Title displayed when there are no calendar events for the current day", diff --git a/libs/flutter_student_embed/lib/l10n/res/intl_pl.arb b/libs/flutter_student_embed/lib/l10n/res/intl_pl.arb index a471cca586..6c53e7d56a 100644 --- a/libs/flutter_student_embed/lib/l10n/res/intl_pl.arb +++ b/libs/flutter_student_embed/lib/l10n/res/intl_pl.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2022-10-28T11:03:17.232435", + "@@last_modified": "2023-03-31T11:03:46.341309", "coursesLabel": "Kursy", "@coursesLabel": { "description": "The label for the Courses tab", @@ -101,6 +101,19 @@ "points": {} } }, + "calendarDaySemanticsLabel": "{eventCount,plural, =1{{date}, {eventCount} zdarzenie}other{{date}, {eventCount} zdarzenia}}", + "@calendarDaySemanticsLabel": { + "description": "Screen reader label used for calendar day, reads the date and count of events", + "type": "text", + "placeholders_order": [ + "date", + "eventCount" + ], + "placeholders": { + "date": {}, + "eventCount": {} + } + }, "No Events Today!": "Brak wydarzeń na dziś!", "@No Events Today!": { "description": "Title displayed when there are no calendar events for the current day", diff --git a/libs/flutter_student_embed/lib/l10n/res/intl_pt_BR.arb b/libs/flutter_student_embed/lib/l10n/res/intl_pt_BR.arb index 90e37fc92e..39a2d53c87 100644 --- a/libs/flutter_student_embed/lib/l10n/res/intl_pt_BR.arb +++ b/libs/flutter_student_embed/lib/l10n/res/intl_pt_BR.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2022-10-28T11:03:17.232435", + "@@last_modified": "2023-03-31T11:03:46.341309", "coursesLabel": "Cursos", "@coursesLabel": { "description": "The label for the Courses tab", @@ -101,6 +101,19 @@ "points": {} } }, + "calendarDaySemanticsLabel": "{eventCount,plural, =1{{date}, {eventCount} evento}other{{date}, {eventCount} eventos}}", + "@calendarDaySemanticsLabel": { + "description": "Screen reader label used for calendar day, reads the date and count of events", + "type": "text", + "placeholders_order": [ + "date", + "eventCount" + ], + "placeholders": { + "date": {}, + "eventCount": {} + } + }, "No Events Today!": "Nenhum evento hoje!", "@No Events Today!": { "description": "Title displayed when there are no calendar events for the current day", diff --git a/libs/flutter_student_embed/lib/l10n/res/intl_pt_PT.arb b/libs/flutter_student_embed/lib/l10n/res/intl_pt_PT.arb index 612fb0ee44..3a16fe8130 100644 --- a/libs/flutter_student_embed/lib/l10n/res/intl_pt_PT.arb +++ b/libs/flutter_student_embed/lib/l10n/res/intl_pt_PT.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2022-10-28T11:03:17.232435", + "@@last_modified": "2023-03-31T11:03:46.341309", "coursesLabel": "Disciplinas", "@coursesLabel": { "description": "The label for the Courses tab", @@ -101,6 +101,19 @@ "points": {} } }, + "calendarDaySemanticsLabel": "{eventCount,plural, =1{{date}, {eventCount} evento}other{{date}, {eventCount} eventos}}", + "@calendarDaySemanticsLabel": { + "description": "Screen reader label used for calendar day, reads the date and count of events", + "type": "text", + "placeholders_order": [ + "date", + "eventCount" + ], + "placeholders": { + "date": {}, + "eventCount": {} + } + }, "No Events Today!": "Nenhum evento hoje!", "@No Events Today!": { "description": "Title displayed when there are no calendar events for the current day", diff --git a/libs/flutter_student_embed/lib/l10n/res/intl_ru.arb b/libs/flutter_student_embed/lib/l10n/res/intl_ru.arb index 4cb80c05d7..4b80df0a1a 100644 --- a/libs/flutter_student_embed/lib/l10n/res/intl_ru.arb +++ b/libs/flutter_student_embed/lib/l10n/res/intl_ru.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2022-10-28T11:03:17.232435", + "@@last_modified": "2023-03-31T11:03:46.341309", "coursesLabel": "Курсы", "@coursesLabel": { "description": "The label for the Courses tab", @@ -101,6 +101,19 @@ "points": {} } }, + "calendarDaySemanticsLabel": "{eventCount,plural, =1{{date}, {eventCount} событие}other{{date}, {eventCount} события}}", + "@calendarDaySemanticsLabel": { + "description": "Screen reader label used for calendar day, reads the date and count of events", + "type": "text", + "placeholders_order": [ + "date", + "eventCount" + ], + "placeholders": { + "date": {}, + "eventCount": {} + } + }, "No Events Today!": "На сегодня события отсутствуют!", "@No Events Today!": { "description": "Title displayed when there are no calendar events for the current day", diff --git a/libs/flutter_student_embed/lib/l10n/res/intl_sl.arb b/libs/flutter_student_embed/lib/l10n/res/intl_sl.arb index d4d0ffc39f..5b1bb6c4d8 100644 --- a/libs/flutter_student_embed/lib/l10n/res/intl_sl.arb +++ b/libs/flutter_student_embed/lib/l10n/res/intl_sl.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2022-10-28T11:03:17.232435", + "@@last_modified": "2023-03-31T11:03:46.341309", "coursesLabel": "Predmeti", "@coursesLabel": { "description": "The label for the Courses tab", @@ -101,6 +101,19 @@ "points": {} } }, + "calendarDaySemanticsLabel": "{eventCount,plural, =1{{date}, {eventCount} dogodek}other{{date}, {eventCount} dogodkov(-a/-i)}}", + "@calendarDaySemanticsLabel": { + "description": "Screen reader label used for calendar day, reads the date and count of events", + "type": "text", + "placeholders_order": [ + "date", + "eventCount" + ], + "placeholders": { + "date": {}, + "eventCount": {} + } + }, "No Events Today!": "Danes ni dogodkov.", "@No Events Today!": { "description": "Title displayed when there are no calendar events for the current day", diff --git a/libs/flutter_student_embed/lib/l10n/res/intl_sv.arb b/libs/flutter_student_embed/lib/l10n/res/intl_sv.arb index cc7d37ce15..2ed3fb7586 100644 --- a/libs/flutter_student_embed/lib/l10n/res/intl_sv.arb +++ b/libs/flutter_student_embed/lib/l10n/res/intl_sv.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2022-10-28T11:03:17.232435", + "@@last_modified": "2023-03-31T11:03:46.341309", "coursesLabel": "Kurser", "@coursesLabel": { "description": "The label for the Courses tab", @@ -101,6 +101,19 @@ "points": {} } }, + "calendarDaySemanticsLabel": "{eventCount,plural, =1{{date}, {eventCount} händelse}other{{date}, {eventCount} händelser}}", + "@calendarDaySemanticsLabel": { + "description": "Screen reader label used for calendar day, reads the date and count of events", + "type": "text", + "placeholders_order": [ + "date", + "eventCount" + ], + "placeholders": { + "date": {}, + "eventCount": {} + } + }, "No Events Today!": "Inga händelser idag!", "@No Events Today!": { "description": "Title displayed when there are no calendar events for the current day", diff --git a/libs/flutter_student_embed/lib/l10n/res/intl_sv_instk12.arb b/libs/flutter_student_embed/lib/l10n/res/intl_sv_instk12.arb index a6326a5d65..bfccff3ee6 100644 --- a/libs/flutter_student_embed/lib/l10n/res/intl_sv_instk12.arb +++ b/libs/flutter_student_embed/lib/l10n/res/intl_sv_instk12.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2022-10-28T11:03:17.232435", + "@@last_modified": "2023-03-31T11:03:46.341309", "coursesLabel": "Kurser", "@coursesLabel": { "description": "The label for the Courses tab", @@ -101,6 +101,19 @@ "points": {} } }, + "calendarDaySemanticsLabel": "{eventCount,plural, =1{{date}, {eventCount} händelse}other{{date}, {eventCount} händelser}}", + "@calendarDaySemanticsLabel": { + "description": "Screen reader label used for calendar day, reads the date and count of events", + "type": "text", + "placeholders_order": [ + "date", + "eventCount" + ], + "placeholders": { + "date": {}, + "eventCount": {} + } + }, "No Events Today!": "Inga händelser idag!", "@No Events Today!": { "description": "Title displayed when there are no calendar events for the current day", diff --git a/libs/flutter_student_embed/lib/l10n/res/intl_th.arb b/libs/flutter_student_embed/lib/l10n/res/intl_th.arb index 77ebf3ffdf..a30482afc3 100644 --- a/libs/flutter_student_embed/lib/l10n/res/intl_th.arb +++ b/libs/flutter_student_embed/lib/l10n/res/intl_th.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2022-10-28T11:03:17.232435", + "@@last_modified": "2023-03-31T11:03:46.341309", "coursesLabel": "บทเรียน", "@coursesLabel": { "description": "The label for the Courses tab", @@ -101,6 +101,19 @@ "points": {} } }, + "calendarDaySemanticsLabel": "{eventCount,plural, =1{{date}, {eventCount} กิจกรรม}other{{date}, {eventCount} กิจกรรม}}", + "@calendarDaySemanticsLabel": { + "description": "Screen reader label used for calendar day, reads the date and count of events", + "type": "text", + "placeholders_order": [ + "date", + "eventCount" + ], + "placeholders": { + "date": {}, + "eventCount": {} + } + }, "No Events Today!": "ไม่มีกิจกรรมในวันนี้!", "@No Events Today!": { "description": "Title displayed when there are no calendar events for the current day", diff --git a/libs/flutter_student_embed/lib/l10n/res/intl_vi.arb b/libs/flutter_student_embed/lib/l10n/res/intl_vi.arb index 01bc1ae69c..5d0e7f09c0 100644 --- a/libs/flutter_student_embed/lib/l10n/res/intl_vi.arb +++ b/libs/flutter_student_embed/lib/l10n/res/intl_vi.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2022-10-28T11:03:17.232435", + "@@last_modified": "2023-03-31T11:03:46.341309", "coursesLabel": "Khóa Học", "@coursesLabel": { "description": "The label for the Courses tab", @@ -101,6 +101,19 @@ "points": {} } }, + "calendarDaySemanticsLabel": "{eventCount,plural, =1{{date}, {eventCount} sự kiện}other{{date}, {eventCount} sự kiện}}", + "@calendarDaySemanticsLabel": { + "description": "Screen reader label used for calendar day, reads the date and count of events", + "type": "text", + "placeholders_order": [ + "date", + "eventCount" + ], + "placeholders": { + "date": {}, + "eventCount": {} + } + }, "No Events Today!": "Không Có Sự Kiện Hôm Nay!", "@No Events Today!": { "description": "Title displayed when there are no calendar events for the current day", diff --git a/libs/flutter_student_embed/lib/l10n/res/intl_zh.arb b/libs/flutter_student_embed/lib/l10n/res/intl_zh.arb index 9b40b83431..7a31fbfe80 100644 --- a/libs/flutter_student_embed/lib/l10n/res/intl_zh.arb +++ b/libs/flutter_student_embed/lib/l10n/res/intl_zh.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2022-10-28T11:03:17.232435", + "@@last_modified": "2023-03-31T11:03:46.341309", "coursesLabel": "课程", "@coursesLabel": { "description": "The label for the Courses tab", @@ -101,6 +101,19 @@ "points": {} } }, + "calendarDaySemanticsLabel": "{eventCount,plural, =1{{date}、{eventCount} 个事件}other{{date}、{eventCount} 个事件}}", + "@calendarDaySemanticsLabel": { + "description": "Screen reader label used for calendar day, reads the date and count of events", + "type": "text", + "placeholders_order": [ + "date", + "eventCount" + ], + "placeholders": { + "date": {}, + "eventCount": {} + } + }, "No Events Today!": "今天没有事件!", "@No Events Today!": { "description": "Title displayed when there are no calendar events for the current day", diff --git a/libs/flutter_student_embed/lib/l10n/res/intl_zh_HK.arb b/libs/flutter_student_embed/lib/l10n/res/intl_zh_HK.arb index a273346581..ea89647c0e 100644 --- a/libs/flutter_student_embed/lib/l10n/res/intl_zh_HK.arb +++ b/libs/flutter_student_embed/lib/l10n/res/intl_zh_HK.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2022-10-28T11:03:17.232435", + "@@last_modified": "2023-03-31T11:03:46.341309", "coursesLabel": "課程", "@coursesLabel": { "description": "The label for the Courses tab", @@ -101,6 +101,19 @@ "points": {} } }, + "calendarDaySemanticsLabel": "{eventCount,plural, =1{{date}、{eventCount} 活動}other{{date}、{eventCount} 活動}}", + "@calendarDaySemanticsLabel": { + "description": "Screen reader label used for calendar day, reads the date and count of events", + "type": "text", + "placeholders_order": [ + "date", + "eventCount" + ], + "placeholders": { + "date": {}, + "eventCount": {} + } + }, "No Events Today!": "今天並無活動!", "@No Events Today!": { "description": "Title displayed when there are no calendar events for the current day", diff --git a/libs/flutter_student_embed/lib/l10n/res/intl_zh_Hans.arb b/libs/flutter_student_embed/lib/l10n/res/intl_zh_Hans.arb index d6b8ce26b4..7a31fbfe80 100644 --- a/libs/flutter_student_embed/lib/l10n/res/intl_zh_Hans.arb +++ b/libs/flutter_student_embed/lib/l10n/res/intl_zh_Hans.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2022-01-28T12:37:53.041723", + "@@last_modified": "2023-03-31T11:03:46.341309", "coursesLabel": "课程", "@coursesLabel": { "description": "The label for the Courses tab", @@ -101,6 +101,19 @@ "points": {} } }, + "calendarDaySemanticsLabel": "{eventCount,plural, =1{{date}、{eventCount} 个事件}other{{date}、{eventCount} 个事件}}", + "@calendarDaySemanticsLabel": { + "description": "Screen reader label used for calendar day, reads the date and count of events", + "type": "text", + "placeholders_order": [ + "date", + "eventCount" + ], + "placeholders": { + "date": {}, + "eventCount": {} + } + }, "No Events Today!": "今天没有事件!", "@No Events Today!": { "description": "Title displayed when there are no calendar events for the current day", @@ -122,9 +135,9 @@ "placeholders_order": [], "placeholders": {} }, - "Tap to favorite the courses you want to see on the Calendar.": "点击以收藏您希望在日历中看到的课程。", - "@Tap to favorite the courses you want to see on the Calendar.": { - "description": "Description text on calendar filter screen.", + "Select elements to display on the calendar.": "请选择要在日历上显示的元素。", + "@Select elements to display on the calendar.": { + "description": "Select calendars description", "type": "text", "placeholders_order": [], "placeholders": {} @@ -444,4 +457,4 @@ "time": {} } } -} +} \ No newline at end of file diff --git a/libs/flutter_student_embed/lib/l10n/res/intl_zh_Hant.arb b/libs/flutter_student_embed/lib/l10n/res/intl_zh_Hant.arb index 910fe92063..ea89647c0e 100644 --- a/libs/flutter_student_embed/lib/l10n/res/intl_zh_Hant.arb +++ b/libs/flutter_student_embed/lib/l10n/res/intl_zh_Hant.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2022-01-28T12:37:53.041723", + "@@last_modified": "2023-03-31T11:03:46.341309", "coursesLabel": "課程", "@coursesLabel": { "description": "The label for the Courses tab", @@ -101,6 +101,19 @@ "points": {} } }, + "calendarDaySemanticsLabel": "{eventCount,plural, =1{{date}、{eventCount} 活動}other{{date}、{eventCount} 活動}}", + "@calendarDaySemanticsLabel": { + "description": "Screen reader label used for calendar day, reads the date and count of events", + "type": "text", + "placeholders_order": [ + "date", + "eventCount" + ], + "placeholders": { + "date": {}, + "eventCount": {} + } + }, "No Events Today!": "今天並無活動!", "@No Events Today!": { "description": "Title displayed when there are no calendar events for the current day", @@ -122,9 +135,9 @@ "placeholders_order": [], "placeholders": {} }, - "Tap to favorite the courses you want to see on the Calendar.": "點選以選擇您要在行事曆上看到的最愛課程。", - "@Tap to favorite the courses you want to see on the Calendar.": { - "description": "Description text on calendar filter screen.", + "Select elements to display on the calendar.": "選擇要在行事曆上顯示的元素。", + "@Select elements to display on the calendar.": { + "description": "Select calendars description", "type": "text", "placeholders_order": [], "placeholders": {} @@ -444,4 +457,4 @@ "time": {} } } -} +} \ No newline at end of file diff --git a/libs/pandares/src/main/res/values-ar/strings.xml b/libs/pandares/src/main/res/values-ar/strings.xml index 57545a0bc7..9d7e42efb7 100644 --- a/libs/pandares/src/main/res/values-ar/strings.xml +++ b/libs/pandares/src/main/res/values-ar/strings.xml @@ -1357,11 +1357,21 @@ إغلاق مربع حوار التقدم %1$s من %2$s جارٍ التحميل إلى الملفات + تعذر تحميل ملف أو أكثر. تحقق من اتصالك بالإنترنت وأعد محاولة الإرسال. جارٍ تحميل الإرسال إلى \"%s\" جارٍ تحميل الإرسال نجح الإرسال فشل الإرسال - جارٍ تحميل الملفات + + جارٍ تحميل الملفات + جارٍ تحميل الملف + جارٍ تحميل الملفات + جارٍ تحميل الملفات + جارٍ تحميل الملفات + جارٍ تحميل الملفات + + تم تحميل الملف بنجاح + فشل تحميل الملف إلغاء الإرسال سيؤدي هذا إلى إلغاء وحذف إرسالك. تعذر تحميل الملف @@ -1444,4 +1454,9 @@ تم إلغاء تنشيط وضع التحديد. صورة رمزية %s تراجع + التطبيق + المجال + معرف تسجيل الدخول + البريد الإلكتروني + الإصدار diff --git a/libs/pandares/src/main/res/values-b+da+DK+instk12/strings.xml b/libs/pandares/src/main/res/values-b+da+DK+instk12/strings.xml index d563f1aaa1..20884a6377 100644 --- a/libs/pandares/src/main/res/values-b+da+DK+instk12/strings.xml +++ b/libs/pandares/src/main/res/values-b+da+DK+instk12/strings.xml @@ -1294,11 +1294,17 @@ Luk statusdialog %1$s af %2$s Uploader til filer + En eller flere filer kunne ikke uploades. Kontroller din internetforbindelse, og prøv igen for at aflevere. Uploader indsendelse til \"%s\" Uploader aflevering Aflevering lykkedes Aflevering mislykkedes - Uploader filer + + Uploader fil + Uploader filer + + Filupload lykkedes + Filupload mislykkedes Annuller aflevering Dette annullerer og sletter din aflevering. Filupload mislykkedes @@ -1377,4 +1383,9 @@ Valgtilstand deaktiveret. Avatar for %s Fortryd + App + Domæne + Login-id + E-mail + Version diff --git a/libs/pandares/src/main/res/values-b+en+AU+unimelb/strings.xml b/libs/pandares/src/main/res/values-b+en+AU+unimelb/strings.xml index a5a1c17882..21dbd639d4 100644 --- a/libs/pandares/src/main/res/values-b+en+AU+unimelb/strings.xml +++ b/libs/pandares/src/main/res/values-b+en+AU+unimelb/strings.xml @@ -1294,11 +1294,17 @@ Close progress dialogue %1$s of %2$s Uploading to Files + One or more files failed to upload. Check your internet connection and retry to submit. Uploading submission to \"%s\" Uploading Submission - Submission Success + Submission Successful Submission Failed - Uploading Files + + Uploading File + Uploading Files + + File Upload Successful + File Upload Failed Cancel Submission This will cancel and delete your submission. File Upload Failed @@ -1377,4 +1383,9 @@ Selection mode deactivated. Avatar of %s Undo + App + Domain + Login ID + Email + Version diff --git a/libs/pandares/src/main/res/values-b+en+GB+instukhe/strings.xml b/libs/pandares/src/main/res/values-b+en+GB+instukhe/strings.xml index 62e5bd1896..6440be837b 100644 --- a/libs/pandares/src/main/res/values-b+en+GB+instukhe/strings.xml +++ b/libs/pandares/src/main/res/values-b+en+GB+instukhe/strings.xml @@ -1294,11 +1294,17 @@ Close progress dialogue %1$s of %2$s Uploading to Files + One or more files failed to upload. Check your internet connection and retry to submit. Uploading submission to \"%s\" Uploading submission - Submission Success + Submission Successful Submission failed - Uploading Files + + Uploading File + Uploading Files + + File Upload Successful + File Upload Failed Cancel Submission This will cancel and delete your submission. File Upload Failed @@ -1377,4 +1383,9 @@ Selection mode deactivated. Avatar of %s Undo + App + Domain + Login ID + Email + Version diff --git a/libs/pandares/src/main/res/values-b+nb+NO+instk12/strings.xml b/libs/pandares/src/main/res/values-b+nb+NO+instk12/strings.xml index 4369c6e8c7..90ed8331e9 100644 --- a/libs/pandares/src/main/res/values-b+nb+NO+instk12/strings.xml +++ b/libs/pandares/src/main/res/values-b+nb+NO+instk12/strings.xml @@ -1295,11 +1295,17 @@ Lukk fremgangsdialog %1$s av %2$s Last opp til Filer + En eller flere filer kunne ikke lastes opp. Sjekk internettforbindelsen din og prøv å lever på nytt. Laster opp innlevering til \"%s\" Laster opp innlevering Innlevering vellykket Innlevering feilet - Laste opp filer + + Laster opp fil + Laste opp filer + + Filen ble lastet opp + Filopplasting mislyktes Avbryt innlevering Dette avbryter og sletter innleveringen din. Filopplasting mislyktes @@ -1378,4 +1384,9 @@ Valgmodus deaktivert. Avatar av %s Angre + App + Domene + Innloggings-ID + E-post + Versjon diff --git a/libs/pandares/src/main/res/values-b+sv+SE+instk12/strings.xml b/libs/pandares/src/main/res/values-b+sv+SE+instk12/strings.xml index 4c73ed4bac..5cf6960010 100644 --- a/libs/pandares/src/main/res/values-b+sv+SE+instk12/strings.xml +++ b/libs/pandares/src/main/res/values-b+sv+SE+instk12/strings.xml @@ -1294,11 +1294,17 @@ Stäng förloppsdialog %1$s av %2$s Laddar upp till Filer + En eller fler filer laddades inte upp. Kontrollera din internetanslutning och försök lämna in igen. Laddar upp inlämning till \"%s\" Laddar upp inlämning - Inlämningen lyckades + Inlämningen slutfördes Inlämningen misslyckades - Laddar upp filer + + Laddar upp fil + Laddar upp filer + + Filuppladdningen slutfördes + Filuppladdningen misslyckades Avbryt inlämningen Detta avbryter och tar bort din inlämning. Filuppladdningen misslyckades @@ -1377,4 +1383,9 @@ Urvalsläget inaktiverat. Avatar för %s Ångra + App + Domän + Inloggnings-ID + E-post + Version diff --git a/libs/pandares/src/main/res/values-b+zh+HK/strings.xml b/libs/pandares/src/main/res/values-b+zh+HK/strings.xml index b4d3b8c260..25773b6b92 100644 --- a/libs/pandares/src/main/res/values-b+zh+HK/strings.xml +++ b/libs/pandares/src/main/res/values-b+zh+HK/strings.xml @@ -1278,11 +1278,16 @@ 關閉進度對話 %2$s 的 %1$s 上傳到檔案 + 上傳一個或多個檔案失敗。檢查您的網際網路連線並重試提交。 上傳提交項目到 \"%s\" 正在上傳提交項目 - 提交項目成功 + 成功提交 提交項目失敗 - 上傳檔案 + + 正在上傳檔案 + + 成功上傳檔案 + 檔案上傳失敗 取消提交項目 此動作將取消並刪除您的提交項目。 檔案上傳失敗 @@ -1360,4 +1365,9 @@ 選擇模式停用 %s 的頭像 復原 + 應用程式 + 網域 + 登入 ID + 電郵 + 版本 diff --git a/libs/pandares/src/main/res/values-b+zh+Hans/strings.xml b/libs/pandares/src/main/res/values-b+zh+Hans/strings.xml index 22bf269c32..fcc8e49b2e 100644 --- a/libs/pandares/src/main/res/values-b+zh+Hans/strings.xml +++ b/libs/pandares/src/main/res/values-b+zh+Hans/strings.xml @@ -1278,11 +1278,16 @@ 关闭进度对话框 %1$s,共%2$s 上传到文件 + 一份或多个文件上传失败。请检查网络连接,并再次尝试提交。 上传提交项到\"%s\" 正在上传提交作业 - 成功提交文件 + 提交成功 提交文件失败 - 上传文件 + + 正在上传文件 + + 文件上传成功 + 文件上传失败 取消提交 此操作将取消并删除您的提交项。 文件上传失败 @@ -1360,4 +1365,9 @@ 选择模式已被禁用。 %s头像 后退 + 应用程序 + + 登录 ID + 电子邮件 + 版本 diff --git a/libs/pandares/src/main/res/values-b+zh+Hant/strings.xml b/libs/pandares/src/main/res/values-b+zh+Hant/strings.xml index b4d3b8c260..25773b6b92 100644 --- a/libs/pandares/src/main/res/values-b+zh+Hant/strings.xml +++ b/libs/pandares/src/main/res/values-b+zh+Hant/strings.xml @@ -1278,11 +1278,16 @@ 關閉進度對話 %2$s 的 %1$s 上傳到檔案 + 上傳一個或多個檔案失敗。檢查您的網際網路連線並重試提交。 上傳提交項目到 \"%s\" 正在上傳提交項目 - 提交項目成功 + 成功提交 提交項目失敗 - 上傳檔案 + + 正在上傳檔案 + + 成功上傳檔案 + 檔案上傳失敗 取消提交項目 此動作將取消並刪除您的提交項目。 檔案上傳失敗 @@ -1360,4 +1365,9 @@ 選擇模式停用 %s 的頭像 復原 + 應用程式 + 網域 + 登入 ID + 電郵 + 版本 diff --git a/libs/pandares/src/main/res/values-ca/strings.xml b/libs/pandares/src/main/res/values-ca/strings.xml index 6e3ca1ddeb..871fa87e98 100644 --- a/libs/pandares/src/main/res/values-ca/strings.xml +++ b/libs/pandares/src/main/res/values-ca/strings.xml @@ -1295,11 +1295,17 @@ Tanqueu el quadre de diàleg de progrés %1$s de %2$s S\'està penjant als fitxers + No s\'han pogut penjar un o més fitxers. Reviseu la connexió a Internet i torneu a provar de fer l\'entrega. S\'està penjant l\'entrega a \"%s\" S\'està carregant l\'entrega - S\'ha realitzat correctament l\'entrega + S’ha fet l’entrega correctament S’ha produït un error en fer l\'entrega - S\'estan penjant els fitxers + + S\'està penjant el fitxer + S\'estan penjant els fitxers + + S’ha penjat el fitxer correctament + No s\'ha pogut penjar el fitxer Cancel·la l\'entrega Amb aquesta acció es cancel·larà i se suprimirà l\'entrega. No s\'ha pogut penjar el fitxer @@ -1378,4 +1384,9 @@ S’ha desactivat el mode de selecció. Avatar de %s Desfés + Aplicació + Domini + ID d\'inici de sessió + Correu electrònic + Versió diff --git a/libs/pandares/src/main/res/values-cy/strings.xml b/libs/pandares/src/main/res/values-cy/strings.xml index bd41444ba6..a8368fe024 100644 --- a/libs/pandares/src/main/res/values-cy/strings.xml +++ b/libs/pandares/src/main/res/values-cy/strings.xml @@ -1294,9 +1294,17 @@ Cau’r deialog cynnydd %1$s o %2$s Wrthi’n llwytho i fyny i Ffeiliau + Wedi methu llwytho un neu ragor o ffeiliau i fyny. Gwiriwch eich cysylltiad rhyngrwyd a rhoi cynnig arall ar gyflwyno. Wrthi’n llwytho cyflwyniad i fyny i \"%s\" Llwytho cyflwyniad i fyny - Wrthi’n llwytho ffeiliau i fyny + Wedi llwyddo i gyflwyno + Cyflwyniad wedi methu + + Wrthi’n llwytho ffeil i fyny + Wrthi’n llwytho ffeiliau i fyny + + Wedi llwyddo i lwytho ffeil i fyny + Wedi methu llwytho ffeil i fyny Canslo Cyflwyniad Bydd hyn yn canslo ac yn dileu eich cyflwyniad. Wedi methu llwytho ffeil i fyny @@ -1375,4 +1383,9 @@ Modd dewis ddim ar waith. Afatar o %s Dad-wneud + Ap + Parth + ID Mewngofnodi + E-bost + Fersiwn diff --git a/libs/pandares/src/main/res/values-da/strings.xml b/libs/pandares/src/main/res/values-da/strings.xml index e025ef49d6..274ef8256d 100644 --- a/libs/pandares/src/main/res/values-da/strings.xml +++ b/libs/pandares/src/main/res/values-da/strings.xml @@ -1294,11 +1294,17 @@ Luk statusdialog %1$s af %2$s Uploader til filer + En eller flere filer kunne ikke uploades. Kontroller din internetforbindelse, og prøv igen for at aflevere. Uploader indsendelse til \"%s\" Uploader aflevering Aflevering lykkedes Aflevering mislykkedes - Uploader filer + + Uploader fil + Uploader filer + + Filupload lykkedes + Filupload mislykkedes Annuller aflevering Dette annullerer og sletter din aflevering. Filupload mislykkedes @@ -1377,4 +1383,9 @@ Valgtilstand deaktiveret. Avatar for %s Fortryd + App + Domæne + Login-id + E-mail + Version diff --git a/libs/pandares/src/main/res/values-de/strings.xml b/libs/pandares/src/main/res/values-de/strings.xml index b5d24c86cb..e297b35d31 100644 --- a/libs/pandares/src/main/res/values-de/strings.xml +++ b/libs/pandares/src/main/res/values-de/strings.xml @@ -1294,11 +1294,17 @@ Fortschrittsdialog schließen %1$s von %2$s Wird nach „Dateien“ hochgeladen + Eine oder mehrere Dateien wurden nicht hochgeladen. Überprüfen Sie Ihre Internetverbindung, und versuchen Sie es erneut. Abgabe wird zu \"%s\" hochgeladen Abgabe wird hochgeladen - Abgabe erfolgreichAbgabe erfolgreich + Erfolgreich gesendet Abgabe fehlgeschlagen - Dateien hochladen + + Datei wird hochgeladen + Dateien werden hochgeladen + + Hochladen der Datei erfolgreich + Hochladen der Datei fehlgeschlagen Abgabe abbrechen Dadurch wird Ihre Abgabe storniert und gelöscht. Hochladen der Datei fehlgeschlagen @@ -1377,4 +1383,9 @@ Der Auswahlmodus ist deaktiviert. Avatar von %s Rückgängig machen + App + Domäne + Anmelde-ID + E-Mail + Version diff --git a/libs/pandares/src/main/res/values-en-rAU/strings.xml b/libs/pandares/src/main/res/values-en-rAU/strings.xml index 31bc8ecea5..b07729c242 100644 --- a/libs/pandares/src/main/res/values-en-rAU/strings.xml +++ b/libs/pandares/src/main/res/values-en-rAU/strings.xml @@ -1294,11 +1294,17 @@ Close progress dialogue %1$s of %2$s Uploading to Files + One or more files failed to upload. Check your internet connection and retry to submit. Uploading submission to \"%s\" Uploading Submission - Submission Success + Submission Successful Submission Failed - Uploading Files + + Uploading File + Uploading Files + + File Upload Successful + File Upload Failed Cancel Submission This will cancel and delete your submission. File Upload Failed @@ -1377,4 +1383,9 @@ Selection mode deactivated. Avatar of %s Undo + App + Domain + Login ID + Email + Version diff --git a/libs/pandares/src/main/res/values-en-rCY/strings.xml b/libs/pandares/src/main/res/values-en-rCY/strings.xml index 62e5bd1896..6440be837b 100644 --- a/libs/pandares/src/main/res/values-en-rCY/strings.xml +++ b/libs/pandares/src/main/res/values-en-rCY/strings.xml @@ -1294,11 +1294,17 @@ Close progress dialogue %1$s of %2$s Uploading to Files + One or more files failed to upload. Check your internet connection and retry to submit. Uploading submission to \"%s\" Uploading submission - Submission Success + Submission Successful Submission failed - Uploading Files + + Uploading File + Uploading Files + + File Upload Successful + File Upload Failed Cancel Submission This will cancel and delete your submission. File Upload Failed @@ -1377,4 +1383,9 @@ Selection mode deactivated. Avatar of %s Undo + App + Domain + Login ID + Email + Version diff --git a/libs/pandares/src/main/res/values-en-rGB/strings.xml b/libs/pandares/src/main/res/values-en-rGB/strings.xml index 109a519665..4b93f190e2 100644 --- a/libs/pandares/src/main/res/values-en-rGB/strings.xml +++ b/libs/pandares/src/main/res/values-en-rGB/strings.xml @@ -1294,11 +1294,17 @@ Close progress dialogue %1$s of %2$s Uploading to Files + One or more files failed to upload. Check your internet connection and retry to submit. Uploading submission to \"%s\" Uploading submission - Submission Success + Submission Successful Submission failed - Uploading Files + + Uploading File + Uploading Files + + File Upload Successful + File Upload Failed Cancel Submission This will cancel and delete your submission. File Upload Failed @@ -1377,4 +1383,9 @@ Selection mode deactivated. Avatar of %s Undo + App + Domain + Login ID + Email + Version diff --git a/libs/pandares/src/main/res/values-es-rES/strings.xml b/libs/pandares/src/main/res/values-es-rES/strings.xml index 6eb048fcf4..bfb1606f72 100644 --- a/libs/pandares/src/main/res/values-es-rES/strings.xml +++ b/libs/pandares/src/main/res/values-es-rES/strings.xml @@ -1296,11 +1296,17 @@ Cerrar diálogo %1$s de %2$s Cargando a archivos + No se han podido cargar uno o más archivos. Comprueba tu conexión a Internet y vuelve a realizar la entrega. Cargando entrega a \"%s\" Cargando entrega - Se ha realizado la entrega + Entrega realizada correctamente No se ha podido entregar - Cargando archivos + + Cargando archivo + Cargando archivos + + Archivo cargado correctamente + Ha habido un error al cargar el archivo Cancelar entrega Esta acción cancelará y eliminará tu entrega. Ha habido un error al cargar el archivo @@ -1379,4 +1385,9 @@ Modo de selección desactivado. Avatar de %s Deshacer + Aplicación + Dominio + Identificación de inicio de sesión + Correo electrónico + Versión diff --git a/libs/pandares/src/main/res/values-es/strings.xml b/libs/pandares/src/main/res/values-es/strings.xml index 23a604c77b..88d851c809 100644 --- a/libs/pandares/src/main/res/values-es/strings.xml +++ b/libs/pandares/src/main/res/values-es/strings.xml @@ -1294,11 +1294,17 @@ Cerrar diálogo de progreso %1$s de %2$s Cargando a Archivos + No se pudieron cargar uno o más archivos. Compruebe su conexión a Internet y vuelva a entregarlos. Cargando entrega a \"%s\" Cargando la entrega Entrega exitosa No se pudo presentar - Carga de archivos + + Carga de archivo + Carga de archivos + + Carga de archivo exitosa + No se pudo cargar el archivo Cancelar entrega Esto cancelará y eliminará su entrega. No se pudo cargar el archivo @@ -1377,4 +1383,9 @@ Modo de selección desactivado. Avatar de %s Deshacer + Aplicación + Dominio + ID de inicio de sesión + Correo electrónico + Versión diff --git a/libs/pandares/src/main/res/values-fi/strings.xml b/libs/pandares/src/main/res/values-fi/strings.xml index d06ba6becf..01639c036f 100644 --- a/libs/pandares/src/main/res/values-fi/strings.xml +++ b/libs/pandares/src/main/res/values-fi/strings.xml @@ -1294,11 +1294,17 @@ Sulje edistymisruutu %1$s/%2$s Ladataan tiedostoihin + Yksi tai useampi tiedosto ei latautunut. Tarkista Internet-yhteytesi ja yritä lähettää uudelleen. Ladataan lähetystä kohteeseen \"%s\" Ladataan tehtäväpalautusta - Tehtävänpalautus onnistui + Lähetys onnistui Tehtäväpalautus epäonnistui - Ladataan tiedostoja + + Ladataan tiedostoa + Ladataan tiedostoja + + Tiedoston lataus onnistui + Tiedoston lataus epäonnistui Peruuta lähetys Tämä peruuttaa ja poistaa lähetyksesi. Tiedoston lataus epäonnistui @@ -1377,4 +1383,9 @@ Valintatilan aktivointi poistettu. Kohteen %s avatar Peruuta + Sovellus + Verkkotunnus + Käyttäjätunnus + Sähköposti + Versio diff --git a/libs/pandares/src/main/res/values-fr-rCA/strings.xml b/libs/pandares/src/main/res/values-fr-rCA/strings.xml index 1d6e83f0b5..185874579d 100644 --- a/libs/pandares/src/main/res/values-fr-rCA/strings.xml +++ b/libs/pandares/src/main/res/values-fr-rCA/strings.xml @@ -1294,11 +1294,17 @@ Fermer le dialogue de progression %1$s sur %2$s Téléversement des fichiers + Le téléversement d’un ou de plusieurs fichiers a échoué. Vérifiez votre connexion Internet et réessayez l’envoi. Téléversement de l’envoi vers \"%s\" Téléversement de l’envoi - Succès de l’envoi + Envoi réussi Échec de l’envoi - Téléversement des fichiers + + Téléversement du fichier + Téléversement des fichiers + + Le téléversement du fichier a réussi + Le téléversement du fichier a échoué Annuler l\'envoi Cela annulera et supprimera votre envoi. Le téléversement du fichier a échoué @@ -1377,4 +1383,9 @@ Mode de sélection désactivé. Avatar de %s Annuler + Appl. + Domaine + Identifiant de connexion + Adresse électronique + Version diff --git a/libs/pandares/src/main/res/values-fr/strings.xml b/libs/pandares/src/main/res/values-fr/strings.xml index 6b48292b1f..a4796e0486 100644 --- a/libs/pandares/src/main/res/values-fr/strings.xml +++ b/libs/pandares/src/main/res/values-fr/strings.xml @@ -1294,11 +1294,17 @@ Fermer la boîte de dialogue de progression %1$s sur %2$s Envoi vers les fichiers... + L’envoi d\'un ou plusieurs fichiers a échoué. Vérifiez l’état de votre connexion internet, puis réessayez. Envoi de la soumission vers \"%s\" Envoi en cours - Envoi réussi + Soumission réussie Échec de l’envoi - Envoi des fichiers... + + Envoi du fichier... + Envoi des fichiers... + + Fichier envoyé avec succès + Échec de l’envoi de fichier Annuler la soumission Ceci annulera et supprimera votre soumission. Échec de l’envoi de fichier @@ -1377,4 +1383,9 @@ Mode sélection désactivé. Avatar de %s Annuler + Application + Domaine + ID d’authentification + Email + Version diff --git a/libs/pandares/src/main/res/values-ht/strings.xml b/libs/pandares/src/main/res/values-ht/strings.xml index 1788d4dd5c..ade3eda65e 100644 --- a/libs/pandares/src/main/res/values-ht/strings.xml +++ b/libs/pandares/src/main/res/values-ht/strings.xml @@ -1294,11 +1294,17 @@ Fèmen dyalìg pwogrè %1$s de %2$s Transfere nan Fichye + Gen yonn oswa plizyè fichye pa rive transfere. Verifye koneksyon entènèt ou a epi eseye re voye yo ankò Transfere Soumisyon nan \"%s\" Chajman Soumisyon Soumisyon Reyisi Soumisyon Echwe - Ajoute Fichye + + Transfè Fichye + Ajoute Fichye + + Transfè Fichye Reyisi + Transfè Fichye Echwe Anile Soumisyon Aksyon sa a ap anile epi efase soumisyon w lan. Transfè Fichye Echwe @@ -1377,4 +1383,9 @@ Mòd seleksyon dezaktive. Avatar %s Defèt + App + Domèn + ID Koneksyon + Imèl + Vèsyon diff --git a/libs/pandares/src/main/res/values-is/strings.xml b/libs/pandares/src/main/res/values-is/strings.xml index 0466ba7789..5c57e94dcd 100644 --- a/libs/pandares/src/main/res/values-is/strings.xml +++ b/libs/pandares/src/main/res/values-is/strings.xml @@ -1294,11 +1294,17 @@ Lokaðu framvinduglugganum %1$s af %2$s Hleð upp í Skrár + Ekki tókst að hlaða upp einni eða fleiri skrám. Athugaðu nettenginguna þína og reyndu aftur að skila. Hlaða upp skilum í \"%s\" Hlaða upp skilum Skil tókust Mistókst að skila - Hleður upp skrám + + Hleður upp skrá + Hleður upp skrám + + Upphleðsla skrár tókst + Upphleðsla skrár mistókst Hætta við skil Þetta hættir við og eyðir skilunum þínum. Upphleðsla skrár mistókst @@ -1377,4 +1383,9 @@ Valhamur afvirkjaður. Gengill %s Hætta við + Smáforrit + Lén + Innskráningarauðkenni + Tölvupóstur + Útgáfa diff --git a/libs/pandares/src/main/res/values-it/strings.xml b/libs/pandares/src/main/res/values-it/strings.xml index c42a340810..01ea64df04 100644 --- a/libs/pandares/src/main/res/values-it/strings.xml +++ b/libs/pandares/src/main/res/values-it/strings.xml @@ -1294,11 +1294,17 @@ Chiudi finestra di dialogo avanzamento %1$s di %2$s Caricamento nei file + Impossibile caricare uno o più file. Controlla la tua connessione Internet e riprova a inviare. Caricamento consegna in \"%s\" Caricamento consegna Consegna eseguita correttamente Consegna non riuscita - Caricamento file + + Caricamento file + Caricamento file + + Caricamento file eseguito correttamente + Errore di caricamento file Annulla invio Questa operazione annullerà ed eliminerà il tuo invio. Errore di caricamento file @@ -1377,4 +1383,9 @@ Modalità selezione disattivata. Avatar di %s Annulla + App + Dominio + ID di accesso + E-mail + Versione diff --git a/libs/pandares/src/main/res/values-ja/strings.xml b/libs/pandares/src/main/res/values-ja/strings.xml index aa264a5b4c..943eb17052 100644 --- a/libs/pandares/src/main/res/values-ja/strings.xml +++ b/libs/pandares/src/main/res/values-ja/strings.xml @@ -1278,11 +1278,16 @@ 進捗状況ダイアログを閉じる %1$s / %2$s ファイルアップロード中 + 1つ以上のファイルのアップロードに失敗しました。インターネット接続をチェックしてから、提出を再試行してください。 提出物を\"%s\"にアップロード中 提出物をアップロードしています - 提出に成功しました + 正常に提出しました 提出に失敗しました - ファイルアップロード中 + + ファイルアップロード中 + + ファイルのアップロードに成功しました + ファイルのアップロードに失敗しました 提出をキャンセル これにより、送信がキャンセルおよび削除されます。 ファイルのアップロードに失敗しました @@ -1360,4 +1365,9 @@ 選択モードが解除されました。 %sのアバター アンドゥーする + アプリ + ドメイン + ログイン ID + E メール + バージョン diff --git a/libs/pandares/src/main/res/values-mi/strings.xml b/libs/pandares/src/main/res/values-mi/strings.xml index cdaaf4be9b..61847c0b6c 100644 --- a/libs/pandares/src/main/res/values-mi/strings.xml +++ b/libs/pandares/src/main/res/values-mi/strings.xml @@ -1294,11 +1294,17 @@ Katia te korero ahunga whakamua %1$s ō %2$s Tukuake ki nga Kōnae + Kotahi me te nui atu o ngā kōnae i hapa te tukuake. Āta titro ki to hono ipurangi ka ngana anō ki te tuku. Te tuku tukunga ki \"%s\" Tukuake ana tāpaetanga - Angitu te tukunga + I Angitu te Tukunga I rahua te tuku o te tāpaetanga - Tikiake ana ngā kōnae + + Tukuake Kōnae + Tikiake ana ngā kōnae + + I Angitu te Tukunga Kōnae + I Rahua te Tukunga Kōnae Whakakore Tāpaetanga Ka mukua me te whakakoretia to tāpaetanga. I Rahua te Tukunga Kōnae @@ -1377,4 +1383,9 @@ Kua whakakorehia te aratau whiriwhiri. Avatar o %s Wete + Taupānga + Rohe + Takiuru ID + Īmēra + Putanga diff --git a/libs/pandares/src/main/res/values-ms/strings.xml b/libs/pandares/src/main/res/values-ms/strings.xml index 6b0efe2ceb..75260c1970 100644 --- a/libs/pandares/src/main/res/values-ms/strings.xml +++ b/libs/pandares/src/main/res/values-ms/strings.xml @@ -1300,11 +1300,17 @@ Tutup dialog kemajuan %1$s daripada %2$s Memuat Naik ke Fail + Satu atau lebih fail gagal dimuat naik. Semak sambungan Internet anda dan cuba semula untuk membuat serahan. Memuat naik serahan ke \"%s\" Memuat Naik Serahan Serahan Berjaya Serahan Gagal - Memuat Naik Fail + + Memuat Naik Fail + Memuat Naik Fail + + Muat Naik Fail Berjaya + Muat Naik Fail Gagal Batal Serahan Ini akan membatalkan dan memadamkan serahan anda. Muat Naik Fail Gagal @@ -1383,4 +1389,9 @@ Mod pilihan diaktifkan. Avatar %s Buat asal + Apl + Domain + ID Log Masuk + E-mel + Versi diff --git a/libs/pandares/src/main/res/values-nb/strings.xml b/libs/pandares/src/main/res/values-nb/strings.xml index b53d07dbc6..e84ab62421 100644 --- a/libs/pandares/src/main/res/values-nb/strings.xml +++ b/libs/pandares/src/main/res/values-nb/strings.xml @@ -1295,11 +1295,17 @@ Lukk fremgangsdialog %1$s av %2$s Last opp til Filer + En eller flere filer kunne ikke lastes opp. Sjekk internettforbindelsen din og prøv å lever på nytt. Laster opp innlevering til \"%s\" Laster opp innlevering Innlevering vellykket Innlevering feilet - Laste opp filer + + Laster opp fil + Laster opp filer + + Filen ble lastet opp + Filopplasting mislyktes Avbryt innlevering Dette avbryter og sletter innleveringen din. Filopplasting mislyktes @@ -1378,4 +1384,9 @@ Valgmodus deaktivert. Avatar av %s Angre + App + Domene + Innloggings-ID + E-post + Versjon diff --git a/libs/pandares/src/main/res/values-nl/strings.xml b/libs/pandares/src/main/res/values-nl/strings.xml index 5a118ef95c..dd5325b396 100644 --- a/libs/pandares/src/main/res/values-nl/strings.xml +++ b/libs/pandares/src/main/res/values-nl/strings.xml @@ -1294,11 +1294,17 @@ Voortgangsdialoogvenster sluiten %1$s van %2$s Uploaden naar bestanden + Er kunnen een of meer bestanden niet worden geüpload. Controleer je internetverbinding en probeer opnieuw in te leveren. Inlevering uploaden naar \"%s\" Inlevering wordt geüpload - Inlevering is gelukt + Inlevering gelukt Inlevering is mislukt - Bestanden uploaden + + Bestanden worden geüpload + Bestanden uploaden + + Bestand uploaden gelukt + Bestand uploaden is mislukt Inlevering annuleren Hiermee wordt je inlevering geannuleerd en verwijderd. Bestand uploaden is mislukt @@ -1377,4 +1383,9 @@ Selectiemodus gedeactiveerd. Avatar van %s Ongedaan maken + App + Domein + Aanmeldings-ID + E-mail + Versie diff --git a/libs/pandares/src/main/res/values-pl/strings.xml b/libs/pandares/src/main/res/values-pl/strings.xml index 58f5e49d29..debd724b2b 100644 --- a/libs/pandares/src/main/res/values-pl/strings.xml +++ b/libs/pandares/src/main/res/values-pl/strings.xml @@ -1326,11 +1326,19 @@ Zamknij okno dialogowe postępów %1$s z %2$s Przesyłanie do Plików + Nie udało się przesłać co najmniej jednego pliku. Sprawdź połączenie internetowe i ponów przesyłanie. Przesyłanie materiałów do \"%s\" Trwa przesyłanie zadania - Przesłano pomyślnie + Przesyłanie zakończono pomyślnie Przesyłanie nie powiodło się - Przesyłanie plików + + Przesyłanie pliku + Przesyłanie plików + Przesyłanie plików + Przesyłanie plików + + Przesyłanie pliku zakończono pomyślnie + Niepowodzenie przesyłania pliku Anuluj przesyłkę Spowoduje to anulowanie i usunięcie przesyłki. Niepowodzenie przesyłania pliku @@ -1411,4 +1419,9 @@ Dezaktywowano tryb wyboru. Awatar %s Cofnij + Aplikacja + Domena + Nazwa użytkownika + E-mail + Wersja diff --git a/libs/pandares/src/main/res/values-pt-rBR/strings.xml b/libs/pandares/src/main/res/values-pt-rBR/strings.xml index 5b3e6023eb..939a82dfb4 100644 --- a/libs/pandares/src/main/res/values-pt-rBR/strings.xml +++ b/libs/pandares/src/main/res/values-pt-rBR/strings.xml @@ -1294,11 +1294,17 @@ Fechar caixa de diálogo de progresso %1$s de %2$s Fazendo upload para arquivos + Falha no carregamento de um ou mais arquivos. Verifique sua conexão à Internet e tente enviar novamente. Carregando envio para \"%s\" Realizando upload - Sucesso do envio + Envio bem-sucedido Falha no envio - Carregamento de arquivos + + Carregando arquivo + Carregamento de arquivos + + Carregamento de arquivo bem-sucedido + Falha no envio do arquivo Cancelar envio Isso cancelará e excluirá seu envio. Falha no envio do arquivo @@ -1377,4 +1383,9 @@ Modo de seleção desativado. Avatar de %s Desfazer + Aplicativo + Domínio + ID de Login + E-mail + Versão diff --git a/libs/pandares/src/main/res/values-pt-rPT/strings.xml b/libs/pandares/src/main/res/values-pt-rPT/strings.xml index 93978bb98a..9d5e600d24 100644 --- a/libs/pandares/src/main/res/values-pt-rPT/strings.xml +++ b/libs/pandares/src/main/res/values-pt-rPT/strings.xml @@ -1294,11 +1294,17 @@ Fechar diálogo de progresso %1$s de %2$s Carregamento para ficheiros + Um ou mais arquivos não foram carregados. Verifique sua conexão com a Internet e tente enviar novamente. A carregar o envio para \"%s\" A carregar o envio - Submissão Bem sucedida + Submissão bem sucedida Falha na submissão - Carregamento de ficheiros + + Carregamento de ficheiro + Carregamento de ficheiros + + Carregamento de ficheiros com sucesso + Carregamento de ficheiros falhado Cancelar subscrição Isto irá cancelar e eliminar a sua submissão. Carregamento de ficheiros falhado @@ -1377,4 +1383,9 @@ Modo de seleção desativado. Avatar de %s Desfazer + Aplicação + Domínio + ID de login + E-mail + Versão diff --git a/libs/pandares/src/main/res/values-ru/strings.xml b/libs/pandares/src/main/res/values-ru/strings.xml index 4c2e399840..0c7ce07b16 100644 --- a/libs/pandares/src/main/res/values-ru/strings.xml +++ b/libs/pandares/src/main/res/values-ru/strings.xml @@ -1326,11 +1326,19 @@ Закрыть диалоговое окно выполнения %1$s из %2$s Загрузка в файлы + Не удалось загрузить один или несколько файлов. Проверьте подключение к Интернету и повторите отправку. Загрузка отправки в \"%s\" Загрузка отправки Отправка выполнена Ошибка отправки - Загрузка файлов + + Загрузка файла + Загрузка файлов + Загрузка файлов + Загрузка файлов + + Файл успешно загружен + Не удалось загрузить файл Отменить отправку Данное действие отменит отправку данных и удалит отравляемые данные. Не удалось загрузить файл @@ -1411,4 +1419,9 @@ Режим выбора деактивирован. Аватар %s Отменить + Приложение + Домен + Имя пользователя + Адрес электронной почты + Версия diff --git a/libs/pandares/src/main/res/values-sl/strings.xml b/libs/pandares/src/main/res/values-sl/strings.xml index b5d462ea74..65658bce2c 100644 --- a/libs/pandares/src/main/res/values-sl/strings.xml +++ b/libs/pandares/src/main/res/values-sl/strings.xml @@ -1294,11 +1294,17 @@ Zapri pogovorno okno za napredovanje %1$s od %2$s Nalagam v datoteke + Nalaganje ene ali več datotek ni uspelo. Preverite internetno povezavo in poskusite poslati znova. Nalagam oddajo v \"%s\" Nalaganje oddaje Oddaja je uspela Oddaja ni uspela - Nalaganje datotek + + Nalagam datoteko + Nalagam datoteke + + Datoteka je uspešno naložena + Nalaganje datoteke ni uspelo Prekliči oddajo S tem boste preklicali in izbrisali oddajo. Nalaganje datoteke ni uspelo @@ -1377,4 +1383,9 @@ Način izbire je deaktiviran. Avatar osebe %s Razveljavi + Aplikacija + Domena + ID prijave + E-pošta + Različica diff --git a/libs/pandares/src/main/res/values-sv/strings.xml b/libs/pandares/src/main/res/values-sv/strings.xml index 18f4f50dcc..30e3a7d336 100644 --- a/libs/pandares/src/main/res/values-sv/strings.xml +++ b/libs/pandares/src/main/res/values-sv/strings.xml @@ -1294,11 +1294,17 @@ Stäng förloppsdialog %1$s av %2$s Laddar upp till Filer + En eller fler filer laddades inte upp. Kontrollera din internetanslutning och försök lämna in igen. Laddar upp inlämning till \"%s\" Laddar upp inlämning - Inlämningen lyckades + Inlämningen slutfördes Inlämningen misslyckades - Laddar upp filer + + Laddar upp fil + Laddar upp filer + + Filuppladdningen slutfördes + Filuppladdningen misslyckades Avbryt inlämningen Detta avbryter och tar bort din inlämning. Filuppladdningen misslyckades @@ -1377,4 +1383,9 @@ Urvalsläget inaktiverat. Avatar för %s Ångra + App + Domän + Inloggnings-ID + E-post + Version diff --git a/libs/pandares/src/main/res/values-th/strings.xml b/libs/pandares/src/main/res/values-th/strings.xml index e737332467..9951e83134 100644 --- a/libs/pandares/src/main/res/values-th/strings.xml +++ b/libs/pandares/src/main/res/values-th/strings.xml @@ -1294,11 +1294,17 @@ ปิดกล่องโต้ตอบแสดงความคืบหน้า %1$s จาก %2$s กำลังอัพโหลดไปที่ไฟล์ (Files) + ไฟล์บางส่วนไม่สามารถอัพโหลดได้ ตรวจสอบการเชื่อมต่ออินเทอร์เน็ตของคุณแล้วลองส่งใหม่อีกครั้ง กำลังอัพโหลดผลงานจัดส่งไปยัง \"%s\" กำลังอัพโหลดผลงานจัดส่ง จัดส่งเสร็จสิ้น จัดส่งล้มเหลว - กำลังอัพโหลดไฟล์ + + กำลังอัพโหลดไฟล์ + กำลังอัพโหลดไฟล์ + + อัพโหลดไฟล์เสร็จสิ้น + อัพโหลดไฟล์ล้มเหลว ยกเลิกการจัดส่ง นี่จะเป็นการยกเลิกและลบผลงานจัดส่งของคุณ อัพโหลดไฟล์ล้มเหลว @@ -1377,4 +1383,9 @@ ปิดใช้งานโหมดเลือกรายการแล้ว ภาพประจำตัวของ %s เลิกทำ + แอพ + โดเมน + ID ล็อกอิน + อีเมล + เวอร์ชั่น diff --git a/libs/pandares/src/main/res/values-vi/strings.xml b/libs/pandares/src/main/res/values-vi/strings.xml index fcbdca0946..960086917a 100644 --- a/libs/pandares/src/main/res/values-vi/strings.xml +++ b/libs/pandares/src/main/res/values-vi/strings.xml @@ -1295,11 +1295,17 @@ Đóng hộp thoại tiến trình %1$s / %2$s Đang tải lên tới Các Tập Tin + Tải lên một hoặc nhiều tập tin không thành công. Hãy kiểm tra kết nối internet của bạn rồi thử lại để nộp. Đang tải bài nộp tới \"%s\" Tải Lên Bài Nộp Nộp Thành Công Nộp Thất Bại - Đang tải Các Tập Tin + + Đang Tải Lên Tập Tin + Đang Tải Lên Các Tập Tin + + Tải Lên Tập Tin Thành Công + Tải Lên Tập Tin Không Thành Công Hủy Nộp Thao tác này sẽ hủy và xóa bài nộp của bạn. Tải Lên Các Tập Tin Không Thành Công @@ -1378,4 +1384,9 @@ Đã hủy kích hoạt chế độ lựa chọn. Hình Đại Diện của %s Hoàn tác + Ứng Dụng + Tên Miền + ID Đăng Nhập + Email + Phiên bản diff --git a/libs/pandares/src/main/res/values-zh/strings.xml b/libs/pandares/src/main/res/values-zh/strings.xml index 22bf269c32..fcc8e49b2e 100644 --- a/libs/pandares/src/main/res/values-zh/strings.xml +++ b/libs/pandares/src/main/res/values-zh/strings.xml @@ -1278,11 +1278,16 @@ 关闭进度对话框 %1$s,共%2$s 上传到文件 + 一份或多个文件上传失败。请检查网络连接,并再次尝试提交。 上传提交项到\"%s\" 正在上传提交作业 - 成功提交文件 + 提交成功 提交文件失败 - 上传文件 + + 正在上传文件 + + 文件上传成功 + 文件上传失败 取消提交 此操作将取消并删除您的提交项。 文件上传失败 @@ -1360,4 +1365,9 @@ 选择模式已被禁用。 %s头像 后退 + 应用程序 + + 登录 ID + 电子邮件 + 版本 diff --git a/libs/pandares/src/main/res/values/strings.xml b/libs/pandares/src/main/res/values/strings.xml index 204c5bc575..c169672ff2 100644 --- a/libs/pandares/src/main/res/values/strings.xml +++ b/libs/pandares/src/main/res/values/strings.xml @@ -1397,4 +1397,5 @@ Login ID Email Version + There was a problem reloading this assignment. Please check your connection and try again. diff --git a/libs/pandautils/src/main/java/com/instructure/pandautils/activities/BasePresenterActivity.kt b/libs/pandautils/src/main/java/com/instructure/pandautils/activities/BasePresenterActivity.kt index eb0bffc4e2..5a62648333 100644 --- a/libs/pandautils/src/main/java/com/instructure/pandautils/activities/BasePresenterActivity.kt +++ b/libs/pandautils/src/main/java/com/instructure/pandautils/activities/BasePresenterActivity.kt @@ -57,7 +57,7 @@ abstract class BasePresenterActivity, VIEW> : Presen if (supportFragmentManager.backStackEntryCount > 0) { val fragments = supportFragmentManager.fragments if (fragments.isNotEmpty()) { - return fragments[supportFragmentManager.backStackEntryCount - 1] + return fragments.getOrNull(supportFragmentManager.backStackEntryCount - 1) } } else { return supportFragmentManager.fragments.lastOrNull() diff --git a/libs/pandautils/src/main/java/com/instructure/pandautils/binding/ViewBindingDelegate.kt b/libs/pandautils/src/main/java/com/instructure/pandautils/binding/ViewBindingDelegate.kt index b5d12c787a..f81c2f2d34 100644 --- a/libs/pandautils/src/main/java/com/instructure/pandautils/binding/ViewBindingDelegate.kt +++ b/libs/pandautils/src/main/java/com/instructure/pandautils/binding/ViewBindingDelegate.kt @@ -16,6 +16,8 @@ */ package com.instructure.pandautils.binding +import android.os.Handler +import android.os.Looper import android.view.LayoutInflater import android.view.View import androidx.appcompat.app.AppCompatActivity @@ -35,26 +37,28 @@ class FragmentViewBindingDelegate( private var binding: T? = null init { - fragment.lifecycle.addObserver(object : DefaultLifecycleObserver { - val viewLifecycleOwnerLiveDataObserver = - Observer { - val viewLifecycleOwner = it ?: return@Observer + Handler(Looper.getMainLooper()).post { + fragment.lifecycle.addObserver(object : DefaultLifecycleObserver { + val viewLifecycleOwnerLiveDataObserver = + Observer { + val viewLifecycleOwner = it ?: return@Observer - viewLifecycleOwner.lifecycle.addObserver(object : DefaultLifecycleObserver { - override fun onDestroy(owner: LifecycleOwner) { - binding = null - } - }) - } + viewLifecycleOwner.lifecycle.addObserver(object : DefaultLifecycleObserver { + override fun onDestroy(owner: LifecycleOwner) { + binding = null + } + }) + } - override fun onCreate(owner: LifecycleOwner) { - fragment.viewLifecycleOwnerLiveData.observeForever(viewLifecycleOwnerLiveDataObserver) - } + override fun onCreate(owner: LifecycleOwner) { + fragment.viewLifecycleOwnerLiveData.observeForever(viewLifecycleOwnerLiveDataObserver) + } - override fun onDestroy(owner: LifecycleOwner) { - fragment.viewLifecycleOwnerLiveData.removeObserver(viewLifecycleOwnerLiveDataObserver) - } - }) + override fun onDestroy(owner: LifecycleOwner) { + fragment.viewLifecycleOwnerLiveData.removeObserver(viewLifecycleOwnerLiveDataObserver) + } + }) + } } override fun getValue(thisRef: Fragment, property: KProperty<*>): T { diff --git a/libs/pandautils/src/main/java/com/instructure/pandautils/di/DatabaseModule.kt b/libs/pandautils/src/main/java/com/instructure/pandautils/di/DatabaseModule.kt index d4fb71153f..5d7b3807eb 100644 --- a/libs/pandautils/src/main/java/com/instructure/pandautils/di/DatabaseModule.kt +++ b/libs/pandautils/src/main/java/com/instructure/pandautils/di/DatabaseModule.kt @@ -1,7 +1,7 @@ package com.instructure.pandautils.di -import com.instructure.pandautils.room.AppDatabase -import com.instructure.pandautils.room.daos.* +import com.instructure.pandautils.room.appdatabase.AppDatabase +import com.instructure.pandautils.room.appdatabase.daos.* import dagger.Module import dagger.Provides import dagger.hilt.InstallIn diff --git a/libs/pandautils/src/main/java/com/instructure/pandautils/features/dashboard/notifications/DashboardNotificationsFragment.kt b/libs/pandautils/src/main/java/com/instructure/pandautils/features/dashboard/notifications/DashboardNotificationsFragment.kt index 21bcbe17a8..b72c16b09a 100644 --- a/libs/pandautils/src/main/java/com/instructure/pandautils/features/dashboard/notifications/DashboardNotificationsFragment.kt +++ b/libs/pandautils/src/main/java/com/instructure/pandautils/features/dashboard/notifications/DashboardNotificationsFragment.kt @@ -16,7 +16,6 @@ package com.instructure.pandautils.features.dashboard.notifications -import android.content.Intent import android.net.Uri import android.os.Bundle import android.view.LayoutInflater @@ -30,9 +29,7 @@ import androidx.fragment.app.viewModels import com.instructure.pandautils.analytics.SCREEN_VIEW_DASHBOARD_NOTIFICATIONS import com.instructure.pandautils.analytics.ScreenView import com.instructure.pandautils.databinding.FragmentDashboardNotificationsBinding -import com.instructure.pandautils.features.shareextension.ShareExtensionActivity import com.instructure.pandautils.features.shareextension.ShareExtensionRouter -import com.instructure.pandautils.utils.ColorKeeper import com.instructure.pandautils.utils.asChooserExcludingInstructure import com.instructure.pandautils.utils.backgroundColor import dagger.hilt.android.AndroidEntryPoint @@ -107,6 +104,12 @@ class DashboardNotificationsFragment : Fragment() { is DashboardNotificationsActions.OpenProgressDialog -> { startActivity(shareExtensionRouter.routeToProgressScreen(requireActivity(), action.uuid)) } + is DashboardNotificationsActions.NavigateToSubmissionDetails -> { + dashboardRouter.routeToSubmissionDetails(action.canvasContext, action.assignmentId, action.attemptId) + } + is DashboardNotificationsActions.NavigateToMyFiles -> { + dashboardRouter.routeToMyFiles(action.canvasContext, action.folderId) + } } } diff --git a/libs/pandautils/src/main/java/com/instructure/pandautils/features/dashboard/notifications/DashboardNotificationsViewData.kt b/libs/pandautils/src/main/java/com/instructure/pandautils/features/dashboard/notifications/DashboardNotificationsViewData.kt index 54a6d6933d..612ed8c6da 100644 --- a/libs/pandautils/src/main/java/com/instructure/pandautils/features/dashboard/notifications/DashboardNotificationsViewData.kt +++ b/libs/pandautils/src/main/java/com/instructure/pandautils/features/dashboard/notifications/DashboardNotificationsViewData.kt @@ -66,5 +66,11 @@ sealed class DashboardNotificationsActions { data class ShowToast(val toast: String) : DashboardNotificationsActions() data class LaunchConference(val canvasContext: CanvasContext, val url: String) : DashboardNotificationsActions() data class OpenAnnouncement(val subject: String, val message: String) : DashboardNotificationsActions() - data class OpenProgressDialog(val uuid: UUID): DashboardNotificationsActions() + data class OpenProgressDialog(val uuid: UUID) : DashboardNotificationsActions() + data class NavigateToSubmissionDetails( + val canvasContext: CanvasContext, + val assignmentId: Long, + val attemptId: Long + ) : DashboardNotificationsActions() + data class NavigateToMyFiles(val canvasContext: CanvasContext, val folderId: Long) : DashboardNotificationsActions() } diff --git a/libs/pandautils/src/main/java/com/instructure/pandautils/features/dashboard/notifications/DashboardNotificationsViewModel.kt b/libs/pandautils/src/main/java/com/instructure/pandautils/features/dashboard/notifications/DashboardNotificationsViewModel.kt index 27a3f2e3b5..73b4c351ef 100644 --- a/libs/pandautils/src/main/java/com/instructure/pandautils/features/dashboard/notifications/DashboardNotificationsViewModel.kt +++ b/libs/pandautils/src/main/java/com/instructure/pandautils/features/dashboard/notifications/DashboardNotificationsViewModel.kt @@ -41,9 +41,9 @@ import com.instructure.pandautils.models.ConferenceDashboardBlacklist import com.instructure.pandautils.mvvm.Event import com.instructure.pandautils.mvvm.ItemViewModel import com.instructure.pandautils.mvvm.ViewState -import com.instructure.pandautils.room.daos.DashboardFileUploadDao -import com.instructure.pandautils.room.daos.FileUploadInputDao -import com.instructure.pandautils.room.entities.DashboardFileUploadEntity +import com.instructure.pandautils.room.appdatabase.daos.DashboardFileUploadDao +import com.instructure.pandautils.room.appdatabase.daos.FileUploadInputDao +import com.instructure.pandautils.room.appdatabase.entities.DashboardFileUploadEntity import com.instructure.pandautils.utils.orDefault import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.delay @@ -258,16 +258,8 @@ class DashboardNotificationsViewModel @Inject constructor( workerId = workerId, workManager = workManager, data = uploadViewData, - open = { uuid -> _events.postValue(Event(DashboardNotificationsActions.OpenProgressDialog(uuid))) }, - remove = { - viewModelScope.launch { - dashboardFileUploadDao.delete(fileUploadEntity) - fileUploadInputDao.findByWorkerId(workerId.toString())?.let { - fileUploadUtilsHelper.deleteCachedFiles(it.filePaths) - fileUploadInputDao.delete(it) - } - } - } + open = { uuid -> openUploadNotification(workInfo.state, uuid, fileUploadEntity) }, + remove = { removeUploadNotification(fileUploadEntity, workerId) } ) } }.orEmpty() @@ -288,6 +280,56 @@ class DashboardNotificationsViewModel @Inject constructor( return !course.restrictEnrollmentsToCourseDate || isBeforeEndDate } + private fun openUploadNotification(state: WorkInfo.State, uuid: UUID, fileUploadEntity: DashboardFileUploadEntity) { + if (state == WorkInfo.State.SUCCEEDED) { + viewModelScope.launch { + val uploadItemViewModel = _data.value?.uploadItems?.find { it.workerId == uuid } + uploadItemViewModel?.apply { + loading = true + notifyPropertyChanged(BR.loading) + } + if (fileUploadEntity.courseId != null && fileUploadEntity.assignmentId != null && fileUploadEntity.attemptId != null) { + courseManager.getCourseAsync(fileUploadEntity.courseId, false).await().dataOrNull?.let { + dashboardFileUploadDao.delete(fileUploadEntity) + _events.postValue( + Event( + DashboardNotificationsActions.NavigateToSubmissionDetails( + it, + fileUploadEntity.assignmentId, + fileUploadEntity.attemptId + ) + ) + ) + } + } else if (fileUploadEntity.folderId != null) { + dashboardFileUploadDao.delete(fileUploadEntity) + apiPrefs.user?.let { + _events.postValue(Event(DashboardNotificationsActions.NavigateToMyFiles(it, fileUploadEntity.folderId))) + } + } else { + dashboardFileUploadDao.delete(fileUploadEntity) + _events.postValue(Event(DashboardNotificationsActions.OpenProgressDialog(uuid))) + } + uploadItemViewModel?.apply { + loading = false + notifyPropertyChanged(BR.loading) + } + } + } else { + _events.postValue(Event(DashboardNotificationsActions.OpenProgressDialog(uuid))) + } + } + + private fun removeUploadNotification(fileUploadEntity: DashboardFileUploadEntity, workerId: UUID) { + viewModelScope.launch { + dashboardFileUploadDao.delete(fileUploadEntity) + fileUploadInputDao.findByWorkerId(workerId.toString())?.let { + fileUploadUtilsHelper.deleteCachedFiles(it.filePaths) + fileUploadInputDao.delete(it) + } + } + } + private fun handleInvitation( enrollmentId: Long, courseId: Long, diff --git a/libs/pandautils/src/main/java/com/instructure/pandautils/features/dashboard/notifications/DashboardRouter.kt b/libs/pandautils/src/main/java/com/instructure/pandautils/features/dashboard/notifications/DashboardRouter.kt index f13ef49067..ddeda96a2c 100644 --- a/libs/pandautils/src/main/java/com/instructure/pandautils/features/dashboard/notifications/DashboardRouter.kt +++ b/libs/pandautils/src/main/java/com/instructure/pandautils/features/dashboard/notifications/DashboardRouter.kt @@ -16,7 +16,13 @@ package com.instructure.pandautils.features.dashboard.notifications +import com.instructure.canvasapi2.models.CanvasContext + interface DashboardRouter { fun routeToGlobalAnnouncement(subject: String, message: String) + + fun routeToSubmissionDetails(canvasContext: CanvasContext, assignmentId: Long, attemptId: Long) + + fun routeToMyFiles(canvasContext: CanvasContext, folderId: Long) } \ No newline at end of file diff --git a/libs/pandautils/src/main/java/com/instructure/pandautils/features/dashboard/notifications/itemviewmodels/UploadItemViewModel.kt b/libs/pandautils/src/main/java/com/instructure/pandautils/features/dashboard/notifications/itemviewmodels/UploadItemViewModel.kt index 6eb381848f..f0d2e65c20 100644 --- a/libs/pandautils/src/main/java/com/instructure/pandautils/features/dashboard/notifications/itemviewmodels/UploadItemViewModel.kt +++ b/libs/pandautils/src/main/java/com/instructure/pandautils/features/dashboard/notifications/itemviewmodels/UploadItemViewModel.kt @@ -31,14 +31,17 @@ import com.instructure.pandautils.mvvm.ItemViewModel import java.util.* class UploadItemViewModel( - private val workerId: UUID, + val workerId: UUID, val workManager: WorkManager, val data: UploadViewData, @get:Bindable var progress: Int = 0, val open: (UUID) -> Unit, - val remove: () -> Unit + val remove: () -> Unit, + @get:Bindable var loading: Boolean = false ) : ItemViewModel, BaseObservable() { + override val layoutId = R.layout.item_dashboard_upload + private val observer = Observer { val uploadedSize = it.progress.getLong(PROGRESS_DATA_UPLOADED_SIZE, 0L) val fullSize = it.progress.getLong(PROGRESS_DATA_FULL_SIZE, 1L) @@ -46,7 +49,6 @@ class UploadItemViewModel( progress = ((uploadedSize.toDouble() / fullSize.toDouble()) * 100.0).toInt() notifyPropertyChanged(BR.progress) } - override val layoutId = R.layout.item_dashboard_upload init { workManager.getWorkInfoByIdLiveData(workerId).observeForever(observer) diff --git a/libs/pandautils/src/main/java/com/instructure/pandautils/features/file/upload/FileUploadDialogViewModel.kt b/libs/pandautils/src/main/java/com/instructure/pandautils/features/file/upload/FileUploadDialogViewModel.kt index 3c5fbb4b2d..c988076ccb 100644 --- a/libs/pandautils/src/main/java/com/instructure/pandautils/features/file/upload/FileUploadDialogViewModel.kt +++ b/libs/pandautils/src/main/java/com/instructure/pandautils/features/file/upload/FileUploadDialogViewModel.kt @@ -31,8 +31,8 @@ import com.instructure.pandautils.R import com.instructure.pandautils.features.file.upload.itemviewmodels.FileItemViewModel import com.instructure.pandautils.features.file.upload.worker.FileUploadWorker import com.instructure.pandautils.mvvm.Event -import com.instructure.pandautils.room.daos.FileUploadInputDao -import com.instructure.pandautils.room.entities.FileUploadInputEntity +import com.instructure.pandautils.room.appdatabase.daos.FileUploadInputDao +import com.instructure.pandautils.room.appdatabase.entities.FileUploadInputEntity import com.instructure.pandautils.utils.humanReadableByteCount import com.instructure.pandautils.utils.orDefault import dagger.hilt.android.lifecycle.HiltViewModel diff --git a/libs/pandautils/src/main/java/com/instructure/pandautils/features/file/upload/worker/FileUploadWorker.kt b/libs/pandautils/src/main/java/com/instructure/pandautils/features/file/upload/worker/FileUploadWorker.kt index 6767b6ae82..4799e6bf9f 100644 --- a/libs/pandautils/src/main/java/com/instructure/pandautils/features/file/upload/worker/FileUploadWorker.kt +++ b/libs/pandautils/src/main/java/com/instructure/pandautils/features/file/upload/worker/FileUploadWorker.kt @@ -35,9 +35,10 @@ import com.instructure.canvasapi2.utils.ProgressRequestUpdateListener import com.instructure.canvasapi2.utils.weave.awaitApi import com.instructure.pandautils.R import com.instructure.pandautils.features.file.upload.FileUploadUtilsHelper -import com.instructure.pandautils.room.daos.* -import com.instructure.pandautils.room.entities.* +import com.instructure.pandautils.room.appdatabase.daos.* +import com.instructure.pandautils.room.appdatabase.entities.* import com.instructure.pandautils.utils.FileUploadUtils +import com.instructure.pandautils.utils.orDefault import com.instructure.pandautils.utils.toJson import dagger.assisted.Assisted import dagger.assisted.AssistedInject @@ -112,7 +113,7 @@ class FileUploadWorker @AssistedInject constructor( subtitle = assignmentName.ifEmpty { fileSubmitObjects.joinToString { it.name } } - insertDashboardUpload(title, subtitle) + if (shouldShowDashboardNotification(action)) insertDashboardUpload(title, subtitle) val attachments = uploadFiles(fileSubmitObjects, groupId) @@ -124,6 +125,7 @@ class FileUploadWorker @AssistedInject constructor( ACTION_ASSIGNMENT_SUBMISSION -> { submitAttachmentsToSubmission(attachmentsIds)?.let { updateSubmissionComplete(notificationId) + attemptId = it.attempt Result.success() } ?: Result.retry() } @@ -173,7 +175,7 @@ class FileUploadWorker @AssistedInject constructor( e.printStackTrace() return Result.failure(workDataBuilder.build()) } finally { - insertDashboardUpload(title, subtitle) + if (shouldShowDashboardNotification(action)) insertDashboardUpload(title, subtitle) } } @@ -184,11 +186,17 @@ class FileUploadWorker @AssistedInject constructor( workerId = id.toString(), userId = userId, title = title, - subtitle = subtitle + subtitle = subtitle, + courseId = courseId.takeIf { it != INVALID_ID }, + assignmentId = assignmentId.takeIf { it != INVALID_ID }, + folderId = if (action == ACTION_USER_FILE) parentFolderId.takeIf { it != INVALID_ID }.orDefault() else null, + attemptId = attemptId.takeIf { it != INVALID_ID } ) ) } + private fun shouldShowDashboardNotification(action: String) = action == ACTION_ASSIGNMENT_SUBMISSION || action == ACTION_USER_FILE + private suspend fun insertSubmissionComment(submissionComment: SubmissionComment): Long { val submissionCommentId = submissionCommentDao.insert( SubmissionCommentEntity(submissionComment) diff --git a/libs/pandautils/src/main/java/com/instructure/pandautils/features/shareextension/progress/ShareExtensionProgressDialogViewModel.kt b/libs/pandautils/src/main/java/com/instructure/pandautils/features/shareextension/progress/ShareExtensionProgressDialogViewModel.kt index 7161581248..c88be6a8f3 100644 --- a/libs/pandautils/src/main/java/com/instructure/pandautils/features/shareextension/progress/ShareExtensionProgressDialogViewModel.kt +++ b/libs/pandautils/src/main/java/com/instructure/pandautils/features/shareextension/progress/ShareExtensionProgressDialogViewModel.kt @@ -18,8 +18,8 @@ import com.instructure.pandautils.features.file.upload.worker.FileUploadWorker import com.instructure.pandautils.features.shareextension.progress.itemviewmodels.FileProgressItemViewModel import com.instructure.pandautils.mvvm.Event import com.instructure.pandautils.mvvm.ViewState -import com.instructure.pandautils.room.daos.DashboardFileUploadDao -import com.instructure.pandautils.room.daos.FileUploadInputDao +import com.instructure.pandautils.room.appdatabase.daos.DashboardFileUploadDao +import com.instructure.pandautils.room.appdatabase.daos.FileUploadInputDao import com.instructure.pandautils.utils.fromJson import com.instructure.pandautils.utils.humanReadableByteCount import com.instructure.pandautils.utils.orDefault diff --git a/libs/pandautils/src/main/java/com/instructure/pandautils/fragments/BaseExpandableSyncFragment.kt b/libs/pandautils/src/main/java/com/instructure/pandautils/fragments/BaseExpandableSyncFragment.kt index 0108f71fcd..9b660edb93 100644 --- a/libs/pandautils/src/main/java/com/instructure/pandautils/fragments/BaseExpandableSyncFragment.kt +++ b/libs/pandautils/src/main/java/com/instructure/pandautils/fragments/BaseExpandableSyncFragment.kt @@ -27,7 +27,6 @@ import com.instructure.canvasapi2.models.CanvasComparable import com.instructure.canvasapi2.models.CanvasContext import com.instructure.pandautils.interfaces.NavigationCallbacks import com.instructure.pandautils.utils.Const -import com.instructure.pandautils.utils.NullableParcelableArg import instructure.androidblueprint.SyncExpandableFragment import instructure.androidblueprint.SyncExpandableManager import instructure.androidblueprint.SyncExpandablePresenter @@ -41,7 +40,6 @@ abstract class BaseExpandableSyncFragment< HOLDER : RecyclerView.ViewHolder, ADAPTER : SyncExpandableRecyclerAdapter> : SyncExpandableFragment(), NavigationCallbacks { - var canvasContext: CanvasContext? by NullableParcelableArg(key = Const.CANVAS_CONTEXT) protected lateinit var rootView: View abstract fun layoutResId(): Int diff --git a/libs/pandautils/src/main/java/com/instructure/pandautils/fragments/BaseFragment.kt b/libs/pandautils/src/main/java/com/instructure/pandautils/fragments/BaseFragment.kt index a0ac2068fb..b0eb221d0d 100644 --- a/libs/pandautils/src/main/java/com/instructure/pandautils/fragments/BaseFragment.kt +++ b/libs/pandautils/src/main/java/com/instructure/pandautils/fragments/BaseFragment.kt @@ -26,10 +26,8 @@ import androidx.fragment.app.Fragment import com.instructure.canvasapi2.models.CanvasContext import com.instructure.pandautils.interfaces.NavigationCallbacks import com.instructure.pandautils.utils.Const -import com.instructure.pandautils.utils.NullableParcelableArg abstract class BaseFragment : Fragment(), NavigationCallbacks { - var canvasContext: CanvasContext? by NullableParcelableArg(key = Const.CANVAS_CONTEXT) protected lateinit var rootView: View abstract fun layoutResId(): Int diff --git a/libs/pandautils/src/main/java/com/instructure/pandautils/fragments/BasePresenterFragment.kt b/libs/pandautils/src/main/java/com/instructure/pandautils/fragments/BasePresenterFragment.kt index 8bded149b4..dbff04123f 100644 --- a/libs/pandautils/src/main/java/com/instructure/pandautils/fragments/BasePresenterFragment.kt +++ b/libs/pandautils/src/main/java/com/instructure/pandautils/fragments/BasePresenterFragment.kt @@ -25,14 +25,12 @@ import androidx.annotation.StringRes import com.instructure.canvasapi2.models.CanvasContext import com.instructure.pandautils.interfaces.NavigationCallbacks import com.instructure.pandautils.utils.Const -import com.instructure.pandautils.utils.NullableParcelableArg import instructure.androidblueprint.FragmentPresenter import instructure.androidblueprint.FragmentViewInterface import instructure.androidblueprint.PresenterFragment abstract class BasePresenterFragment, VIEW : FragmentViewInterface> : PresenterFragment(), NavigationCallbacks { - var canvasContext: CanvasContext? by NullableParcelableArg(key = Const.CANVAS_CONTEXT) protected lateinit var rootView: View abstract fun layoutResId(): Int diff --git a/libs/pandautils/src/main/java/com/instructure/pandautils/fragments/BaseSyncFragment.kt b/libs/pandautils/src/main/java/com/instructure/pandautils/fragments/BaseSyncFragment.kt index 79459332af..4d3a15983a 100644 --- a/libs/pandautils/src/main/java/com/instructure/pandautils/fragments/BaseSyncFragment.kt +++ b/libs/pandautils/src/main/java/com/instructure/pandautils/fragments/BaseSyncFragment.kt @@ -27,7 +27,6 @@ import com.instructure.canvasapi2.models.CanvasComparable import com.instructure.canvasapi2.models.CanvasContext import com.instructure.pandautils.interfaces.NavigationCallbacks import com.instructure.pandautils.utils.Const -import com.instructure.pandautils.utils.NullableParcelableArg import instructure.androidblueprint.SyncFragment import instructure.androidblueprint.SyncManager import instructure.androidblueprint.SyncPresenter @@ -40,7 +39,6 @@ abstract class BaseSyncFragment< HOLDER : RecyclerView.ViewHolder, ADAPTER : SyncRecyclerAdapter> : SyncFragment(), NavigationCallbacks { - var canvasContext: CanvasContext? by NullableParcelableArg(key = Const.CANVAS_CONTEXT) protected lateinit var rootView: View abstract fun layoutResId(): Int diff --git a/libs/pandautils/src/main/java/com/instructure/pandautils/room/MigrationUtils.kt b/libs/pandautils/src/main/java/com/instructure/pandautils/room/MigrationUtils.kt new file mode 100644 index 0000000000..ae5918df5b --- /dev/null +++ b/libs/pandautils/src/main/java/com/instructure/pandautils/room/MigrationUtils.kt @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2023 - present Instructure, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +package com.instructure.pandautils.room + +import androidx.room.migration.Migration +import androidx.sqlite.db.SupportSQLiteDatabase + +fun createMigration(from: Int, to: Int, migrationBlock: (SupportSQLiteDatabase) -> Unit): Migration { + return object : Migration(from, to) { + override fun migrate(database: SupportSQLiteDatabase) { + migrationBlock(database) + } + } +} \ No newline at end of file diff --git a/libs/pandautils/src/main/java/com/instructure/pandautils/room/AppDatabase.kt b/libs/pandautils/src/main/java/com/instructure/pandautils/room/appdatabase/AppDatabase.kt similarity index 78% rename from libs/pandautils/src/main/java/com/instructure/pandautils/room/AppDatabase.kt rename to libs/pandautils/src/main/java/com/instructure/pandautils/room/appdatabase/AppDatabase.kt index a841c7f5a1..403fd4631e 100644 --- a/libs/pandautils/src/main/java/com/instructure/pandautils/room/AppDatabase.kt +++ b/libs/pandautils/src/main/java/com/instructure/pandautils/room/appdatabase/AppDatabase.kt @@ -1,10 +1,11 @@ -package com.instructure.pandautils.room +package com.instructure.pandautils.room.appdatabase import androidx.room.Database import androidx.room.RoomDatabase import androidx.room.TypeConverters -import com.instructure.pandautils.room.daos.* -import com.instructure.pandautils.room.entities.* +import com.instructure.pandautils.room.Converters +import com.instructure.pandautils.room.appdatabase.daos.* +import com.instructure.pandautils.room.appdatabase.entities.* @Database( entities = [ @@ -15,7 +16,7 @@ import com.instructure.pandautils.room.entities.* SubmissionCommentEntity::class, PendingSubmissionCommentEntity::class, DashboardFileUploadEntity::class - ], version = 4 + ], version = 5 ) @TypeConverters(Converters::class) abstract class AppDatabase : RoomDatabase() { diff --git a/libs/pandautils/src/main/java/com/instructure/pandautils/room/AppDatabaseMigrations.kt b/libs/pandautils/src/main/java/com/instructure/pandautils/room/appdatabase/AppDatabaseMigrations.kt similarity index 72% rename from libs/pandautils/src/main/java/com/instructure/pandautils/room/AppDatabaseMigrations.kt rename to libs/pandautils/src/main/java/com/instructure/pandautils/room/appdatabase/AppDatabaseMigrations.kt index 307f84193b..a99980b688 100644 --- a/libs/pandautils/src/main/java/com/instructure/pandautils/room/AppDatabaseMigrations.kt +++ b/libs/pandautils/src/main/java/com/instructure/pandautils/room/appdatabase/AppDatabaseMigrations.kt @@ -15,18 +15,9 @@ * */ -package com.instructure.pandautils.room +package com.instructure.pandautils.room.appdatabase -import androidx.room.migration.Migration -import androidx.sqlite.db.SupportSQLiteDatabase - -fun createMigration(from: Int, to: Int, migrationBlock: (SupportSQLiteDatabase) -> Unit): Migration { - return object : Migration(from, to) { - override fun migrate(database: SupportSQLiteDatabase) { - migrationBlock(database) - } - } -} +import com.instructure.pandautils.room.createMigration val appDatabaseMigrations = arrayOf( @@ -42,7 +33,12 @@ val appDatabaseMigrations = arrayOf( createMigration(3, 4) { database -> database.execSQL("ALTER TABLE FileUploadInputEntity ADD COLUMN notificationId INTEGER") - } + }, + createMigration(4, 5) { database -> + database.execSQL("ALTER TABLE DashboardFileUploadEntity ADD COLUMN courseId INTEGER") + database.execSQL("ALTER TABLE DashboardFileUploadEntity ADD COLUMN assignmentId INTEGER") + database.execSQL("ALTER TABLE DashboardFileUploadEntity ADD COLUMN attemptId INTEGER") + database.execSQL("ALTER TABLE DashboardFileUploadEntity ADD COLUMN folderId INTEGER") + } ) - diff --git a/libs/pandautils/src/main/java/com/instructure/pandautils/room/daos/AttachmentDao.kt b/libs/pandautils/src/main/java/com/instructure/pandautils/room/appdatabase/daos/AttachmentDao.kt similarity index 80% rename from libs/pandautils/src/main/java/com/instructure/pandautils/room/daos/AttachmentDao.kt rename to libs/pandautils/src/main/java/com/instructure/pandautils/room/appdatabase/daos/AttachmentDao.kt index f36a870970..1851f4d314 100644 --- a/libs/pandautils/src/main/java/com/instructure/pandautils/room/daos/AttachmentDao.kt +++ b/libs/pandautils/src/main/java/com/instructure/pandautils/room/appdatabase/daos/AttachmentDao.kt @@ -1,7 +1,7 @@ -package com.instructure.pandautils.room.daos +package com.instructure.pandautils.room.appdatabase.daos import androidx.room.* -import com.instructure.pandautils.room.entities.AttachmentEntity +import com.instructure.pandautils.room.appdatabase.entities.AttachmentEntity @Dao interface AttachmentDao { diff --git a/libs/pandautils/src/main/java/com/instructure/pandautils/room/appdatabase/daos/AuthorDao.kt b/libs/pandautils/src/main/java/com/instructure/pandautils/room/appdatabase/daos/AuthorDao.kt new file mode 100644 index 0000000000..629f056120 --- /dev/null +++ b/libs/pandautils/src/main/java/com/instructure/pandautils/room/appdatabase/daos/AuthorDao.kt @@ -0,0 +1,17 @@ +package com.instructure.pandautils.room.appdatabase.daos + +import androidx.room.* +import com.instructure.pandautils.room.appdatabase.entities.AuthorEntity + +@Dao +interface AuthorDao { + + @Insert(onConflict = OnConflictStrategy.REPLACE) + suspend fun insert(author: AuthorEntity) + + @Delete + suspend fun delete(author: AuthorEntity) + + @Update + suspend fun update(author: AuthorEntity) +} \ No newline at end of file diff --git a/libs/pandautils/src/main/java/com/instructure/pandautils/room/daos/DashboardFileUploadDao.kt b/libs/pandautils/src/main/java/com/instructure/pandautils/room/appdatabase/daos/DashboardFileUploadDao.kt similarity index 80% rename from libs/pandautils/src/main/java/com/instructure/pandautils/room/daos/DashboardFileUploadDao.kt rename to libs/pandautils/src/main/java/com/instructure/pandautils/room/appdatabase/daos/DashboardFileUploadDao.kt index f088ab5811..acc60f7d86 100644 --- a/libs/pandautils/src/main/java/com/instructure/pandautils/room/daos/DashboardFileUploadDao.kt +++ b/libs/pandautils/src/main/java/com/instructure/pandautils/room/appdatabase/daos/DashboardFileUploadDao.kt @@ -1,8 +1,8 @@ -package com.instructure.pandautils.room.daos +package com.instructure.pandautils.room.appdatabase.daos import androidx.lifecycle.LiveData import androidx.room.* -import com.instructure.pandautils.room.entities.DashboardFileUploadEntity +import com.instructure.pandautils.room.appdatabase.entities.DashboardFileUploadEntity @Dao interface DashboardFileUploadDao { diff --git a/libs/pandautils/src/main/java/com/instructure/pandautils/room/daos/FileUploadInputDao.kt b/libs/pandautils/src/main/java/com/instructure/pandautils/room/appdatabase/daos/FileUploadInputDao.kt similarity index 79% rename from libs/pandautils/src/main/java/com/instructure/pandautils/room/daos/FileUploadInputDao.kt rename to libs/pandautils/src/main/java/com/instructure/pandautils/room/appdatabase/daos/FileUploadInputDao.kt index 4dbf2fed69..d7ef2647dd 100644 --- a/libs/pandautils/src/main/java/com/instructure/pandautils/room/daos/FileUploadInputDao.kt +++ b/libs/pandautils/src/main/java/com/instructure/pandautils/room/appdatabase/daos/FileUploadInputDao.kt @@ -1,7 +1,7 @@ -package com.instructure.pandautils.room.daos +package com.instructure.pandautils.room.appdatabase.daos import androidx.room.* -import com.instructure.pandautils.room.entities.FileUploadInputEntity +import com.instructure.pandautils.room.appdatabase.entities.FileUploadInputEntity @Dao interface FileUploadInputDao { diff --git a/libs/pandautils/src/main/java/com/instructure/pandautils/room/daos/MediaCommentDao.kt b/libs/pandautils/src/main/java/com/instructure/pandautils/room/appdatabase/daos/MediaCommentDao.kt similarity index 72% rename from libs/pandautils/src/main/java/com/instructure/pandautils/room/daos/MediaCommentDao.kt rename to libs/pandautils/src/main/java/com/instructure/pandautils/room/appdatabase/daos/MediaCommentDao.kt index 5f89c5d70b..615818d205 100644 --- a/libs/pandautils/src/main/java/com/instructure/pandautils/room/daos/MediaCommentDao.kt +++ b/libs/pandautils/src/main/java/com/instructure/pandautils/room/appdatabase/daos/MediaCommentDao.kt @@ -1,10 +1,10 @@ -package com.instructure.pandautils.room.daos +package com.instructure.pandautils.room.appdatabase.daos import androidx.room.Dao import androidx.room.Delete import androidx.room.Insert import androidx.room.Update -import com.instructure.pandautils.room.entities.MediaCommentEntity +import com.instructure.pandautils.room.appdatabase.entities.MediaCommentEntity @Dao interface MediaCommentDao { diff --git a/libs/pandautils/src/main/java/com/instructure/pandautils/room/daos/PendingSubmissionCommentDao.kt b/libs/pandautils/src/main/java/com/instructure/pandautils/room/appdatabase/daos/PendingSubmissionCommentDao.kt similarity index 85% rename from libs/pandautils/src/main/java/com/instructure/pandautils/room/daos/PendingSubmissionCommentDao.kt rename to libs/pandautils/src/main/java/com/instructure/pandautils/room/appdatabase/daos/PendingSubmissionCommentDao.kt index 3fe46f4609..dc47e5d03f 100644 --- a/libs/pandautils/src/main/java/com/instructure/pandautils/room/daos/PendingSubmissionCommentDao.kt +++ b/libs/pandautils/src/main/java/com/instructure/pandautils/room/appdatabase/daos/PendingSubmissionCommentDao.kt @@ -1,8 +1,8 @@ -package com.instructure.pandautils.room.daos +package com.instructure.pandautils.room.appdatabase.daos import androidx.room.* -import com.instructure.pandautils.room.entities.PendingSubmissionCommentEntity -import com.instructure.pandautils.room.model.PendingSubmissionCommentWithFileUploadInput +import com.instructure.pandautils.room.appdatabase.entities.PendingSubmissionCommentEntity +import com.instructure.pandautils.room.appdatabase.model.PendingSubmissionCommentWithFileUploadInput @Dao interface PendingSubmissionCommentDao { diff --git a/libs/pandautils/src/main/java/com/instructure/pandautils/room/daos/SubmissionCommentDao.kt b/libs/pandautils/src/main/java/com/instructure/pandautils/room/appdatabase/daos/SubmissionCommentDao.kt similarity index 66% rename from libs/pandautils/src/main/java/com/instructure/pandautils/room/daos/SubmissionCommentDao.kt rename to libs/pandautils/src/main/java/com/instructure/pandautils/room/appdatabase/daos/SubmissionCommentDao.kt index bcfca3d9b8..0286854878 100644 --- a/libs/pandautils/src/main/java/com/instructure/pandautils/room/daos/SubmissionCommentDao.kt +++ b/libs/pandautils/src/main/java/com/instructure/pandautils/room/appdatabase/daos/SubmissionCommentDao.kt @@ -1,8 +1,8 @@ -package com.instructure.pandautils.room.daos +package com.instructure.pandautils.room.appdatabase.daos import androidx.room.* -import com.instructure.pandautils.room.entities.SubmissionCommentEntity -import com.instructure.pandautils.room.model.SubmissionCommentWithAttachments +import com.instructure.pandautils.room.appdatabase.entities.SubmissionCommentEntity +import com.instructure.pandautils.room.appdatabase.model.SubmissionCommentWithAttachments @Dao interface SubmissionCommentDao { diff --git a/libs/pandautils/src/main/java/com/instructure/pandautils/room/entities/AttachmentEntity.kt b/libs/pandautils/src/main/java/com/instructure/pandautils/room/appdatabase/entities/AttachmentEntity.kt similarity index 95% rename from libs/pandautils/src/main/java/com/instructure/pandautils/room/entities/AttachmentEntity.kt rename to libs/pandautils/src/main/java/com/instructure/pandautils/room/appdatabase/entities/AttachmentEntity.kt index 0ff8873ad2..97ba2e503d 100644 --- a/libs/pandautils/src/main/java/com/instructure/pandautils/room/entities/AttachmentEntity.kt +++ b/libs/pandautils/src/main/java/com/instructure/pandautils/room/appdatabase/entities/AttachmentEntity.kt @@ -1,4 +1,4 @@ -package com.instructure.pandautils.room.entities +package com.instructure.pandautils.room.appdatabase.entities import androidx.room.Entity import androidx.room.PrimaryKey diff --git a/libs/pandautils/src/main/java/com/instructure/pandautils/room/entities/AuthorEntity.kt b/libs/pandautils/src/main/java/com/instructure/pandautils/room/appdatabase/entities/AuthorEntity.kt similarity index 91% rename from libs/pandautils/src/main/java/com/instructure/pandautils/room/entities/AuthorEntity.kt rename to libs/pandautils/src/main/java/com/instructure/pandautils/room/appdatabase/entities/AuthorEntity.kt index c87682cf31..81fbda4f72 100644 --- a/libs/pandautils/src/main/java/com/instructure/pandautils/room/entities/AuthorEntity.kt +++ b/libs/pandautils/src/main/java/com/instructure/pandautils/room/appdatabase/entities/AuthorEntity.kt @@ -1,4 +1,4 @@ -package com.instructure.pandautils.room.entities +package com.instructure.pandautils.room.appdatabase.entities import androidx.room.Entity import androidx.room.PrimaryKey diff --git a/libs/pandautils/src/main/java/com/instructure/pandautils/room/entities/DashboardFileUploadEntity.kt b/libs/pandautils/src/main/java/com/instructure/pandautils/room/appdatabase/entities/DashboardFileUploadEntity.kt similarity index 50% rename from libs/pandautils/src/main/java/com/instructure/pandautils/room/entities/DashboardFileUploadEntity.kt rename to libs/pandautils/src/main/java/com/instructure/pandautils/room/appdatabase/entities/DashboardFileUploadEntity.kt index eeb95097a5..fe45f15c7c 100644 --- a/libs/pandautils/src/main/java/com/instructure/pandautils/room/entities/DashboardFileUploadEntity.kt +++ b/libs/pandautils/src/main/java/com/instructure/pandautils/room/appdatabase/entities/DashboardFileUploadEntity.kt @@ -1,4 +1,4 @@ -package com.instructure.pandautils.room.entities +package com.instructure.pandautils.room.appdatabase.entities import androidx.room.Entity import androidx.room.PrimaryKey @@ -9,5 +9,9 @@ data class DashboardFileUploadEntity( val workerId: String, val userId: Long, val title: String?, - val subtitle: String? + val subtitle: String?, + val courseId: Long?, + val assignmentId: Long?, + val attemptId: Long?, + val folderId: Long? ) diff --git a/libs/pandautils/src/main/java/com/instructure/pandautils/room/entities/FileUploadInputEntity.kt b/libs/pandautils/src/main/java/com/instructure/pandautils/room/appdatabase/entities/FileUploadInputEntity.kt similarity index 88% rename from libs/pandautils/src/main/java/com/instructure/pandautils/room/entities/FileUploadInputEntity.kt rename to libs/pandautils/src/main/java/com/instructure/pandautils/room/appdatabase/entities/FileUploadInputEntity.kt index 8d7ef9ec5e..d50518a568 100644 --- a/libs/pandautils/src/main/java/com/instructure/pandautils/room/entities/FileUploadInputEntity.kt +++ b/libs/pandautils/src/main/java/com/instructure/pandautils/room/appdatabase/entities/FileUploadInputEntity.kt @@ -1,8 +1,8 @@ -package com.instructure.pandautils.room.entities +package com.instructure.pandautils.room.appdatabase.entities import androidx.room.Entity import androidx.room.PrimaryKey -import java.util.Random +import java.util.* @Entity data class FileUploadInputEntity( diff --git a/libs/pandautils/src/main/java/com/instructure/pandautils/room/entities/MediaCommentEntity.kt b/libs/pandautils/src/main/java/com/instructure/pandautils/room/appdatabase/entities/MediaCommentEntity.kt similarity index 92% rename from libs/pandautils/src/main/java/com/instructure/pandautils/room/entities/MediaCommentEntity.kt rename to libs/pandautils/src/main/java/com/instructure/pandautils/room/appdatabase/entities/MediaCommentEntity.kt index 4b3a2eef91..a8a982385e 100644 --- a/libs/pandautils/src/main/java/com/instructure/pandautils/room/entities/MediaCommentEntity.kt +++ b/libs/pandautils/src/main/java/com/instructure/pandautils/room/appdatabase/entities/MediaCommentEntity.kt @@ -1,4 +1,4 @@ -package com.instructure.pandautils.room.entities +package com.instructure.pandautils.room.appdatabase.entities import androidx.room.Entity import androidx.room.PrimaryKey diff --git a/libs/pandautils/src/main/java/com/instructure/pandautils/room/entities/PendingSubmissionCommentEntity.kt b/libs/pandautils/src/main/java/com/instructure/pandautils/room/appdatabase/entities/PendingSubmissionCommentEntity.kt similarity index 96% rename from libs/pandautils/src/main/java/com/instructure/pandautils/room/entities/PendingSubmissionCommentEntity.kt rename to libs/pandautils/src/main/java/com/instructure/pandautils/room/appdatabase/entities/PendingSubmissionCommentEntity.kt index 171525bf11..31ad1b2588 100644 --- a/libs/pandautils/src/main/java/com/instructure/pandautils/room/entities/PendingSubmissionCommentEntity.kt +++ b/libs/pandautils/src/main/java/com/instructure/pandautils/room/appdatabase/entities/PendingSubmissionCommentEntity.kt @@ -1,4 +1,4 @@ -package com.instructure.pandautils.room.entities +package com.instructure.pandautils.room.appdatabase.entities import androidx.room.Entity import androidx.room.PrimaryKey diff --git a/libs/pandautils/src/main/java/com/instructure/pandautils/room/entities/SubmissionCommentEntity.kt b/libs/pandautils/src/main/java/com/instructure/pandautils/room/appdatabase/entities/SubmissionCommentEntity.kt similarity index 93% rename from libs/pandautils/src/main/java/com/instructure/pandautils/room/entities/SubmissionCommentEntity.kt rename to libs/pandautils/src/main/java/com/instructure/pandautils/room/appdatabase/entities/SubmissionCommentEntity.kt index c79957c3da..ebf984b4a5 100644 --- a/libs/pandautils/src/main/java/com/instructure/pandautils/room/entities/SubmissionCommentEntity.kt +++ b/libs/pandautils/src/main/java/com/instructure/pandautils/room/appdatabase/entities/SubmissionCommentEntity.kt @@ -1,4 +1,4 @@ -package com.instructure.pandautils.room.entities +package com.instructure.pandautils.room.appdatabase.entities import androidx.room.Entity import androidx.room.PrimaryKey diff --git a/libs/pandautils/src/main/java/com/instructure/pandautils/room/model/PendingSubmissionCommentWithFileUploadInput.kt b/libs/pandautils/src/main/java/com/instructure/pandautils/room/appdatabase/model/PendingSubmissionCommentWithFileUploadInput.kt similarity index 87% rename from libs/pandautils/src/main/java/com/instructure/pandautils/room/model/PendingSubmissionCommentWithFileUploadInput.kt rename to libs/pandautils/src/main/java/com/instructure/pandautils/room/appdatabase/model/PendingSubmissionCommentWithFileUploadInput.kt index ea636619ba..9b0c1d9018 100644 --- a/libs/pandautils/src/main/java/com/instructure/pandautils/room/model/PendingSubmissionCommentWithFileUploadInput.kt +++ b/libs/pandautils/src/main/java/com/instructure/pandautils/room/appdatabase/model/PendingSubmissionCommentWithFileUploadInput.kt @@ -1,12 +1,12 @@ -package com.instructure.pandautils.room.model +package com.instructure.pandautils.room.appdatabase.model import androidx.room.Embedded import androidx.room.Relation import com.instructure.canvasapi2.models.postmodels.CommentSendStatus import com.instructure.canvasapi2.models.postmodels.FileUploadWorkerData import com.instructure.canvasapi2.models.postmodels.PendingSubmissionComment -import com.instructure.pandautils.room.entities.FileUploadInputEntity -import com.instructure.pandautils.room.entities.PendingSubmissionCommentEntity +import com.instructure.pandautils.room.appdatabase.entities.FileUploadInputEntity +import com.instructure.pandautils.room.appdatabase.entities.PendingSubmissionCommentEntity import java.util.* data class PendingSubmissionCommentWithFileUploadInput( diff --git a/libs/pandautils/src/main/java/com/instructure/pandautils/room/model/SubmissionCommentWithAttachments.kt b/libs/pandautils/src/main/java/com/instructure/pandautils/room/appdatabase/model/SubmissionCommentWithAttachments.kt similarity index 76% rename from libs/pandautils/src/main/java/com/instructure/pandautils/room/model/SubmissionCommentWithAttachments.kt rename to libs/pandautils/src/main/java/com/instructure/pandautils/room/appdatabase/model/SubmissionCommentWithAttachments.kt index 86a979fd76..40bd4de657 100644 --- a/libs/pandautils/src/main/java/com/instructure/pandautils/room/model/SubmissionCommentWithAttachments.kt +++ b/libs/pandautils/src/main/java/com/instructure/pandautils/room/appdatabase/model/SubmissionCommentWithAttachments.kt @@ -1,12 +1,12 @@ -package com.instructure.pandautils.room.model +package com.instructure.pandautils.room.appdatabase.model import androidx.room.Embedded import androidx.room.Relation import com.instructure.canvasapi2.models.SubmissionComment -import com.instructure.pandautils.room.entities.AttachmentEntity -import com.instructure.pandautils.room.entities.AuthorEntity -import com.instructure.pandautils.room.entities.MediaCommentEntity -import com.instructure.pandautils.room.entities.SubmissionCommentEntity +import com.instructure.pandautils.room.appdatabase.entities.AttachmentEntity +import com.instructure.pandautils.room.appdatabase.entities.AuthorEntity +import com.instructure.pandautils.room.appdatabase.entities.MediaCommentEntity +import com.instructure.pandautils.room.appdatabase.entities.SubmissionCommentEntity data class SubmissionCommentWithAttachments( @Embedded diff --git a/libs/pandautils/src/main/java/com/instructure/pandautils/room/daos/AuthorDao.kt b/libs/pandautils/src/main/java/com/instructure/pandautils/room/daos/AuthorDao.kt deleted file mode 100644 index 3a2a120f2d..0000000000 --- a/libs/pandautils/src/main/java/com/instructure/pandautils/room/daos/AuthorDao.kt +++ /dev/null @@ -1,21 +0,0 @@ -package com.instructure.pandautils.room.daos - -import androidx.room.Dao -import androidx.room.Delete -import androidx.room.Insert -import androidx.room.OnConflictStrategy -import androidx.room.Update -import com.instructure.pandautils.room.entities.AuthorEntity - -@Dao -interface AuthorDao { - - @Insert(onConflict = OnConflictStrategy.REPLACE) - suspend fun insert(author: AuthorEntity) - - @Delete - suspend fun delete(author: AuthorEntity) - - @Update - suspend fun update(author: AuthorEntity) -} \ No newline at end of file diff --git a/libs/pandautils/src/main/java/com/instructure/pandautils/views/FloatingRecordingView.kt b/libs/pandautils/src/main/java/com/instructure/pandautils/views/FloatingRecordingView.kt index f2cff39422..0db877baf7 100644 --- a/libs/pandautils/src/main/java/com/instructure/pandautils/views/FloatingRecordingView.kt +++ b/libs/pandautils/src/main/java/com/instructure/pandautils/views/FloatingRecordingView.kt @@ -85,8 +85,8 @@ class FloatingRecordingView @JvmOverloads constructor( /* CameraView will throw an exception during inflation on some devices. We capture that exception here and show an error view instead when we try to record video. */ try { - videoViewBinding = ViewFloatingMediaRecorderVideoBinding.inflate(LayoutInflater.from(context), binding.dragView, false) - binding.dragView.addView(videoViewBinding?.root) + videoViewBinding = ViewFloatingMediaRecorderVideoBinding.inflate(LayoutInflater.from(context), binding.dragDetectLayout, false) + binding.dragDetectLayout.addView(videoViewBinding?.root) } catch (e: InflateException) { hasVideoError = true videoViewBinding = null @@ -147,7 +147,7 @@ class FloatingRecordingView @JvmOverloads constructor( } private fun setupFloatingAction() { - binding.dragView.setOnTouchListener(object : View.OnTouchListener { + binding.dragDetectLayout.setOnTouchListener(object : View.OnTouchListener { private var lastAction: Int = 0 var windowManager = context.getSystemService(Context.WINDOW_SERVICE) as WindowManager var display = windowManager.defaultDisplay diff --git a/libs/pandautils/src/main/res/layout/fragment_about.xml b/libs/pandautils/src/main/res/layout/fragment_about.xml index c10013262e..5d26338930 100644 --- a/libs/pandautils/src/main/res/layout/fragment_about.xml +++ b/libs/pandautils/src/main/res/layout/fragment_about.xml @@ -135,6 +135,7 @@ tools:text="student1@gmail.com" /> + + diff --git a/libs/pandautils/src/main/res/layout/viewholder_edit_dashboard_group.xml b/libs/pandautils/src/main/res/layout/viewholder_edit_dashboard_group.xml index 207ab456c7..39331c6200 100644 --- a/libs/pandautils/src/main/res/layout/viewholder_edit_dashboard_group.xml +++ b/libs/pandautils/src/main/res/layout/viewholder_edit_dashboard_group.xml @@ -64,6 +64,8 @@ app:layout_constraintEnd_toStartOf="@+id/openButton" app:layout_constraintStart_toEndOf="@id/favoriteButton" app:layout_constraintTop_toTopOf="parent" + app:layout_constraintBottom_toTopOf="@id/subtitle" + app:layout_constraintVertical_chainStyle="packed" tools:text="Course name" />