diff --git a/app/src/main/res/layout-land/coming_soon_topic_view.xml b/app/src/main/res/layout-land/coming_soon_topic_view.xml deleted file mode 100644 index 6bac60e0d09..00000000000 --- a/app/src/main/res/layout-land/coming_soon_topic_view.xml +++ /dev/null @@ -1,102 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/app/src/main/res/layout-land/profile_chooser_fragment.xml b/app/src/main/res/layout-land/profile_chooser_fragment.xml index 595e923267a..3d9a06b1bc4 100644 --- a/app/src/main/res/layout-land/profile_chooser_fragment.xml +++ b/app/src/main/res/layout-land/profile_chooser_fragment.xml @@ -53,7 +53,7 @@ android:id="@+id/profile_select_text" style="@style/Heading1" android:layout_marginStart="36dp" - android:layout_marginTop="64dp" + android:layout_marginTop="@dimen/profile_chooser_fragment_profile_select_text_margin_top" android:layout_marginEnd="36dp" android:text="@string/profile_chooser_select" android:textColor="@color/white" @@ -65,7 +65,7 @@ android:layout_width="match_parent" android:layout_height="match_parent" android:layout_marginStart="16dp" - android:layout_marginTop="12dp" + android:layout_marginTop="@dimen/profile_chooser_fragment_profile_recycler_view_margin_top" android:layout_marginEnd="16dp" android:clipToPadding="false" android:fadingEdge="horizontal" diff --git a/app/src/main/res/layout-land/profile_chooser_profile_view.xml b/app/src/main/res/layout-land/profile_chooser_profile_view.xml index daad93ec975..a884798d94f 100644 --- a/app/src/main/res/layout-land/profile_chooser_profile_view.xml +++ b/app/src/main/res/layout-land/profile_chooser_profile_view.xml @@ -89,6 +89,7 @@ android:id="@+id/add_profile_divider_view" android:layout_width="1dp" android:layout_height="match_parent" + android:layout_marginTop="@dimen/profile_chooser_profile_view_view_margin_top" android:layout_gravity="bottom" android:background="@color/oppiaProfileChooserDivider" android:visibility="@{hasProfileEverBeenAddedValue ? View.GONE : View.VISIBLE}" diff --git a/app/src/main/res/layout-land/promoted_story_card.xml b/app/src/main/res/layout-land/promoted_story_card.xml deleted file mode 100755 index 4b9951015cf..00000000000 --- a/app/src/main/res/layout-land/promoted_story_card.xml +++ /dev/null @@ -1,103 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/app/src/main/res/layout-sw600dp-land/coming_soon_topic_view.xml b/app/src/main/res/layout-sw600dp-land/coming_soon_topic_view.xml deleted file mode 100644 index 6bac60e0d09..00000000000 --- a/app/src/main/res/layout-sw600dp-land/coming_soon_topic_view.xml +++ /dev/null @@ -1,102 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/app/src/main/res/layout-sw600dp-land/promoted_story_card.xml b/app/src/main/res/layout-sw600dp-land/promoted_story_card.xml deleted file mode 100644 index f8ff4daf685..00000000000 --- a/app/src/main/res/layout-sw600dp-land/promoted_story_card.xml +++ /dev/null @@ -1,102 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/app/src/main/res/layout-sw600dp-port/coming_soon_topic_view.xml b/app/src/main/res/layout-sw600dp-port/coming_soon_topic_view.xml deleted file mode 100644 index ec35dab92ff..00000000000 --- a/app/src/main/res/layout-sw600dp-port/coming_soon_topic_view.xml +++ /dev/null @@ -1,102 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/app/src/main/res/layout-sw600dp-port/promoted_story_card.xml b/app/src/main/res/layout-sw600dp-port/promoted_story_card.xml deleted file mode 100644 index 34780cdd4a4..00000000000 --- a/app/src/main/res/layout-sw600dp-port/promoted_story_card.xml +++ /dev/null @@ -1,102 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/app/src/main/res/layout/coming_soon_topic_view.xml b/app/src/main/res/layout/coming_soon_topic_view.xml index cfc347ce6ed..5e1535834a7 100644 --- a/app/src/main/res/layout/coming_soon_topic_view.xml +++ b/app/src/main/res/layout/coming_soon_topic_view.xml @@ -13,11 +13,11 @@ android:id="@+id/topic_container" android:layout_width="144dp" android:layout_height="wrap_content" - app:layoutMarginEnd="@{viewModel.endMargin}" - app:cardCornerRadius="4dp"> + app:cardCornerRadius="4dp" + app:layoutMarginEnd="@{viewModel.endMargin}"> diff --git a/app/src/main/res/layout/profile_chooser_fragment.xml b/app/src/main/res/layout/profile_chooser_fragment.xml index 1c8f504dfbc..bba2aeb6c10 100644 --- a/app/src/main/res/layout/profile_chooser_fragment.xml +++ b/app/src/main/res/layout/profile_chooser_fragment.xml @@ -53,7 +53,7 @@ android:id="@+id/profile_select_text" style="@style/Heading1" android:layout_marginStart="36dp" - android:layout_marginTop="64dp" + android:layout_marginTop="@dimen/profile_chooser_fragment_profile_select_text_margin_top" android:layout_marginEnd="36dp" android:text="@string/profile_chooser_select" android:textColor="@color/white" @@ -65,7 +65,7 @@ android:layout_width="match_parent" android:layout_height="match_parent" android:layout_marginStart="32dp" - android:layout_marginTop="36dp" + android:layout_marginTop="@dimen/profile_chooser_fragment_profile_recycler_view_margin_top" android:layout_marginEnd="32dp" android:clipToPadding="false" android:fadingEdge="horizontal" diff --git a/app/src/main/res/layout/profile_chooser_profile_view.xml b/app/src/main/res/layout/profile_chooser_profile_view.xml index 3b4abd6dba5..6d5d24a109f 100644 --- a/app/src/main/res/layout/profile_chooser_profile_view.xml +++ b/app/src/main/res/layout/profile_chooser_profile_view.xml @@ -96,7 +96,7 @@ android:id="@+id/add_profile_divider_view" android:layout_width="match_parent" android:layout_height="1dp" - android:layout_marginTop="60dp" + android:layout_marginTop="@dimen/profile_chooser_profile_view_view_margin_top" android:background="@color/oppiaProfileChooserDivider" android:visibility="@{hasProfileEverBeenAddedValue ? View.GONE : View.VISIBLE}" app:layout_constraintEnd_toEndOf="parent" diff --git a/app/src/main/res/layout/promoted_story_card.xml b/app/src/main/res/layout/promoted_story_card.xml index b0d418b0917..9239643c554 100755 --- a/app/src/main/res/layout/promoted_story_card.xml +++ b/app/src/main/res/layout/promoted_story_card.xml @@ -12,9 +12,9 @@ + android:textSize="@dimen/promoted_story_card_text_size" /> أعلى قائمة التنقل @@ -65,7 +66,7 @@ الأسئلة المتكررة الأسئلة المتكررة التحقق من رقم التعريف الشخصي - مقدمة عن أوبيا + مقدمة الأسئلة الأكثر تكرارا معلومات الدروس @@ -185,7 +186,7 @@ الحقول المعلمة بـ* مطلوبة. صورة الملف الشخصي الحالية. تعديل صورة الملف الشخصي - مرحبًا بكم في أوبيا! + مرحبًا بكم في %s تعلّم أي شيءٍ تريده بطريقة فعّالة وممتعة. أضف مستخدمين إلى حسابك. شارك الخبرة وأنشئ حتى 10 ملفات شخصية. @@ -207,7 +208,7 @@ إغلاق تم تغيير رقم التعريف الشخصي بنجاح نسيت رقم التعريف الشخصي؟ - لإعادة ضبط رقم التعريف الشخصي الخاص بك، برجاء حذف تطبيق أوبيا وإعادة تثبيته مرة أخرى.\n\nبرجاء العلم أنه في حالة عدم اتصال الجهاز بالإنترنت قد تفقد تقدم المستخدم في عدة حسابات. + لإعادة ضبط رقم التعريف الشخصي PIN الخاص بك، إحذف تطبيق %s وأعد تثبيته مرة أخرى.\n\nضع في اعتبارك أنه إذا لم يكن الجهاز متصلاً بالإنترنت ، فقد تفقد تقدم المستخدم في حسابات متعددة. الذهاب إلى متجر بلاي (Play Store). إظهار/إخفاء أيقونة كلمة السر أيقونة كلمة المرور ظاهرة. @@ -363,6 +364,7 @@ تمييز القصص كمكتملة تمييز المواضيع كمكتملة عرض سجلات الأحداث (Event Logs) + فرض نوع الشبكة تعديل تقدم الدرس تمييز الفصول كمكتملة تمييز القصص كمكتملة @@ -375,8 +377,40 @@ تعطيل التطبيق الكل اكتمل التحديد + تم تحديد الشبكة + افتراضي + الويفي + شبكة الهاتف + لا يوجد شبكة مكتبات برمجية (Third-party Dependencies) إصدار %s رخص حقوق النسخ عارض رخصة حقوق النسخ + انتقل مرة أخرى إلى %s + قائمة تبعيات الطرف الثالث + قائمة تراخيص حقوق النشر + استئناف الدرس + تابع + ابدأ من جديد + صباح الخير + مساء الخير + مساء الخير، + كيف يمكنني إنشاء ملف تعريف(حساب) جديد؟ + كيف يمكنني حذف ملف التعريف(حساب)؟ + كيف يمكنني تغيير بريدي الإلكتروني / رقم هاتفي؟ + ما هي %s؟ + من هو المشرف؟ + لماذا لا يتم تحميل مشغل الاستكشاف؟ + لماذا لا يتم تشغيل الصوت الخاص بي؟ + كيف يمكنني تنزيل موضوع؟ + لا أجد سؤالي هنا. ماذا الان؟ + <p>إذا كانت هذه هي المرة الأولى التي تنشئ فيها ملفًا شخصيًا وليس لديك رقم تعريف شخصي: </p> \n<p> 1. من منتقي الملف الشخصي ، اضغط على\n<strong> قم بإعداد ملفات تعريف متعددة</strong>\n</p>\n<p> 2. قم بإنشاء رقم تعريف شخصي و\n<strong>احفظ</strong>\n</p> \n<p> 3. املأ جميع البيانات للملف الشخصي.</p> \n<ol> \n<li>(اختياري) قم بتحميل صورة.</li> \n<li>إدخال اسم.</li> \n<li>(اختياري) قم بتعيين رقم تعريف شخصي مكون من 3 أرقام.</li> \n</ol> \n<p> 4. اضغط\n<strong>إنشاء</strong> . تمت إضافة هذا الملف الشخصي إلى منتقي ملف التعريف الخاص بك!\n<br/> \n<br/> إذا قمت بإنشاء ملف تعريف من قبل ولديك رقم تعريف شخصي:\n</p>\n<p> 1. من منتقي الملف الشخصي ، اضغط على\n<strong>إضافة الملف الشخصي</strong>\n</p> \n<p> 2. أدخل رقم التعريف الشخصي الخاص بك وانقر فوق\n<strong>إرسال</strong>\n</p> \n<p>3. املأ جميع الحقول للملف الشخصي.</p> \n<ol> \n<li>(اختياري) قم بتحميل صورة.</li> \n<li>إدخال اسم.</li> \n<li>(اختياري) قم بتعيين رقم تعريف شخصي مكون من 3 أرقام.</li> \n</ol> \n<p> 4. اضغط\n<strong>إنشاء</strong> . تمت إضافة هذا الملف الشخصي إلى منتقي ملف التعريف الخاص بك!\n<br/><br/> ملاحظة: فقط ال\n<u>مدير</u> قادر على إدارة الملفات الشخصية.\n</p> + <p>بمجرد حذف ملف التعريف:</p>\n<p><br></p> \n<p>\n<li>لا يمكن استعادة ملف التعريف.</li>\n</p>\n<p><li>سيتم حذف معلومات الملف الشخصي مثل الاسم والصور والتقدم بشكل دائم.</li></p>\n<p><br></p>\n<p>لحذف ملف تعريف (باستثناء<u>المسؤول</u></p>\n<p>1. من الصفحة الرئيسية للمسؤول ، اضغط على زر القائمة أعلى اليسار.</p>\n<p>2. اضغط على<strong>ضوابط المسؤول</strong></p>\n<p>3. اضغط على<strong>تحرير ملفات التعريف</strong></p>\n<p>4. اضغط على الملف الشخصي الذي ترغب في حذفه.</p>\n<p>5. في الجزء السفلي من الشاشة ، انقر فوق<strong>حذف الملف الشخصي</strong></p>\n<p>6. اضغط<strong>حذف</strong>لتأكيد الحذف.</p>\n<p><br></p>\n<p>ملاحظة:<u>المسؤول</u>فقط هو القادر على إدارة الملفات الشخصية.</p> + <p>لتغيير بريدك الإلكتروني / رقم هاتفك:</p> <p>1. من الصفحة الرئيسية للمشرف ، اضغط على زر القائمة أعلى اليسار.</p> <p>2. اضغط على <strong> عناصر تحكم المسؤول </ strong>.</p> <p>3. اضغط على <strong> تعديل الحساب </ strong>.</p> <p><br></p> <p>إذا كنت تريد تغيير بريدك الإلكتروني:</p> <p>4. أدخل بريدك الإلكتروني الجديد وانقر على <strong> حفظ </ strong>.</p> <p>5. يتم إرسال رابط التأكيد لتأكيد بريدك الإلكتروني الجديد. ستنتهي صلاحية الرابط بعد 24 ساعة ويجب النقر عليه لربطه بحسابك.</p> <p><br></p> <p>في حالة تغيير رقم هاتفك: </ p> <p> 4. أدخل رقم هاتفك الجديد وانقر على <strong> تحقق </ strong>.</p> <p>5. يتم إرسال رمز لتأكيد رقمك الجديد. ستنتهي صلاحية الرمز بعد 5 دقائق ويجب إدخاله في الشاشة الجديدة لربطه بحسابك.</p> + <p>%1$s \n<i>\"أو-بي-يا\"</i>(فنلندية) - \"للتعلم\"</p>\n<p><br></p><p>%1$sمهمتنا هي مساعدة أي شخص على تعلم أي شيء يريده بطريقة فعالة وممتعة.</p><p><br></p><p>من خلال إنشاء مجموعة من الدروس المجانية عالية الجودة والفعالة بشكل واضح بمساعدة معلمين من جميع أنحاء العالم ، تهدف %1$s إلى تزويد الطلاب بتعليم جيد - بغض النظر عن مكان وجودهم أو الموارد التقليدية التي يمكنهم الوصول إليها.</p><p><br></p><p>كطالب ، يمكنك أن تبدأ مغامرتك التعليمية من خلال تصفح الموضوعات المدرجة في الصفحة الرئيسية!</p> + <p>المشرف هو المستخدم الرئيسي الذي يدير ملفات التعريف والإعدادات لكل ملف تعريف على حسابه. هم على الأرجح والدك أو معلمك أو وصي عليك الذي أنشأ هذا الملف الشخصي لك.</p><p><br></p><p>يمكن للمسؤولين إدارة الملفات الشخصية وتعيين أرقام التعريف الشخصية وتغيير الإعدادات الأخرى ضمن حساباتهم. بناءً على ملف التعريف الخاص بك ، قد تكون أذونات المسؤول مطلوبة لبعض الميزات مثل تنزيل الموضوعات وتغيير رقم التعريف الشخصي وغير ذلك.</p><p><br></p><p>لمعرفة من هو المسؤول لديك ، انتقل إلى منتقي الملف الشخصي. الملف الشخصي الأول المدرج ولديه \"المسؤول\" مكتوب باسمه هو المسؤول.</p> + <p>إذا لم يتم تحميل مشغل الاستكشاف</p><p><br></p><p>تحقق لمعرفة ما إذا كان التطبيق محدثًا أم لا:</p><p> <ol> <li> انتقل إلى متجر Play وتأكد من تحديث التطبيق إلى أحدث إصدار </li> </ol> </p><p><br></p><p>تحقق من اتصالك بالإنترنت:</p><p> <li> إذا كان اتصالك بالإنترنت بطيئًا ، فحاول إعادة الاتصال بشبكة Wi-Fi أو الاتصال بشبكة أخرى. </li> </p><p><br></p><p>اطلب من المشرف التحقق من أجهزتهم واتصال الإنترنت:</p><p> <li> اطلب من المشرف استكشاف الأخطاء وإصلاحها باستخدام الخطوات المذكورة أعلاه </li> </p><p><br></p><p>أخبرنا إذا كنت لا تزال تواجه مشكلات في التحميل:</p><p> <li> أبلغ عن مشكلة عن طريق الاتصال بنا على admin@oppia.org. </li> </p> + <p>إذا لم يتم تشغيل الصوت الخاص بك</p><p><br></p>\n<p>تحقق لمعرفة ما إذا كان التطبيق محدثًا أم لا:</p>\n<p> <li>انتقل إلى متجر Play وتأكد من تحديث التطبيق إلى أحدث إصدار</li> </p><p><br></p>\n<p>تحقق من اتصالك بالإنترنت:</p><p> <li>إذا كان اتصالك بالإنترنت بطيئًا ، فحاول إعادة الاتصال بشبكة Wi-Fi أو الاتصال بشبكة أخرى. قد يتسبب الإنترنت البطيء في تحميل الصوت بشكل غير منتظم ، مما يجعل من الصعب تشغيله.</li> </p><p><br></p>\n<p>اطلب من المسؤول التحقق من أجهزتهم واتصال الإنترنت:</p><p> \n<li>اطلب من المسؤول استكشاف الأخطاء وإصلاحها باستخدام الخطوات المذكورة أعلاه</li> </p><p><br></p>\n<p>أخبرنا إذا كنت لا تزال تواجه مشكلات في التحميل:</p><p> <li>أبلغ عن مشكلة عن طريق الاتصال بنا على admin@oppia.org.</li></p> + <p>لتنزيل استكشاف:</p>\n<p>1. من الصفحة الرئيسية ، انقر فوق موضوع أو استكشاف.</p>\n<p>2. من صفحة الموضوع هذه ، انقر على علامة التبويب <strong> معلومات </ strong>.</p>\n<p>3. اضغط على <strong> تنزيل الموضوع </ strong>.</p>\n<p>4. اعتمادًا على إعدادات التطبيق ، قد تحتاج إلى موافقة المسؤول أو اتصال Wifi ثابتًا لإكمال التنزيل. إذا لزم الأمر ، بمجرد استيفاء هذه المتطلبات ، يتم تنزيل الموضوع على الجهاز ويمكن استخدامه في وضع عدم الاتصال بواسطة جميع ملفات التعريف. <p> + <p> إذا لم تتمكن من العثور على سؤالك أو كنت ترغب في الإبلاغ عن خطأ ، فاتصل بنا على admin@oppia.org. </p> diff --git a/app/src/main/res/values-hdpi/dimens.xml b/app/src/main/res/values-hdpi/dimens.xml new file mode 100644 index 00000000000..bf52594b9cd --- /dev/null +++ b/app/src/main/res/values-hdpi/dimens.xml @@ -0,0 +1,9 @@ + + + + 32dp + 18dp + 32dp + 32dp + + diff --git a/app/src/main/res/values-land-hdpi/dimens.xml b/app/src/main/res/values-land-hdpi/dimens.xml new file mode 100644 index 00000000000..686aa480cdc --- /dev/null +++ b/app/src/main/res/values-land-hdpi/dimens.xml @@ -0,0 +1,9 @@ + + + + 20dp + 8dp + 32dp + 32dp + + diff --git a/app/src/main/res/values-land-ldpi/dimens.xml b/app/src/main/res/values-land-ldpi/dimens.xml new file mode 100644 index 00000000000..6a718617118 --- /dev/null +++ b/app/src/main/res/values-land-ldpi/dimens.xml @@ -0,0 +1,8 @@ + + + + 16dp + 8dp + 32dp + 32dp + diff --git a/app/src/main/res/values-land-mdpi/dimens.xml b/app/src/main/res/values-land-mdpi/dimens.xml new file mode 100644 index 00000000000..71dd70dee47 --- /dev/null +++ b/app/src/main/res/values-land-mdpi/dimens.xml @@ -0,0 +1,8 @@ + + + + 64dp + 32dp + 32dp + 32dp + diff --git a/app/src/main/res/values-land/dimens.xml b/app/src/main/res/values-land/dimens.xml index 4b51ff326bb..cbb81f2c947 100644 --- a/app/src/main/res/values-land/dimens.xml +++ b/app/src/main/res/values-land/dimens.xml @@ -260,6 +260,11 @@ 76dp 12dp + + 64dp + 12dp + 32dp + 72dp 72dp @@ -297,4 +302,9 @@ 40dp 96dp + + 8dp + 8dp + 14sp + 8dp diff --git a/app/src/main/res/values-ldpi/dimens.xml b/app/src/main/res/values-ldpi/dimens.xml new file mode 100644 index 00000000000..0d5895656e6 --- /dev/null +++ b/app/src/main/res/values-ldpi/dimens.xml @@ -0,0 +1,8 @@ + + + + 32dp + 12dp + 32dp + 32dp + diff --git a/app/src/main/res/values-mdpi/dimens.xml b/app/src/main/res/values-mdpi/dimens.xml new file mode 100644 index 00000000000..21e07668b22 --- /dev/null +++ b/app/src/main/res/values-mdpi/dimens.xml @@ -0,0 +1,8 @@ + + + + 64dp + 36dp + 60dp + 60dp + diff --git a/app/src/main/res/values-pt-rBR/strings.xml b/app/src/main/res/values-pt-rBR/strings.xml index 221312ff17e..33eda2d12fa 100644 --- a/app/src/main/res/values-pt-rBR/strings.xml +++ b/app/src/main/res/values-pt-rBR/strings.xml @@ -1,6 +1,7 @@ @@ -9,6 +10,7 @@ Opções Meus Downloads Ajuda + Reprodutor de Exploração Ajuda Trocar Perfil Opções de Desenvolvedor @@ -65,7 +67,7 @@ Perguntas Frequentes FAQs (Perguntas Frequentes) Verificação de PIN - Introdução à Oppia + Introdução Perguntas Frequentes (FAQs) Info Lições @@ -131,6 +133,7 @@ %d GB Correto! Tópico: %s + %1$s em %2$s 1 Capítulo\n \n %d Capítulos\n @@ -186,6 +189,9 @@ Autorize para adicionar perfis Adicionar Perfil Adicionar Perfil + Nome* + PIN de 3 Dígitos* + Confirmar PIN de 3 Dígitos* Permitir Acesso a Download O usuário pode baixar e excluir conteúdo sem o PIN do administrador. Criar @@ -201,7 +207,7 @@ Os campos marcados com * são obrigatórios. Foto de perfil atual Editar foto de perfil - Bem-vindo à Oppia! + Bem-vindo à %s! Aprenda o que você quiser de uma forma eficaz e divertida. Adicione usuários à sua conta. Compartilhe a experiência e crie até 10 perfis. @@ -212,6 +218,7 @@ Pular Próximo Começar + Slide %d de %d Olá, %s! Por favor, insira o PIN do Administrador. Por favor, insira seu PIN. @@ -222,7 +229,7 @@ Fechar A alteração do PIN foi bem-sucedida Esqueceu o PIN? - Para redefinir o seu PIN, desinstale a Oppia e reinstale-a depois.\n\nLembre-se de que, se o dispositivo não estiver online, você pode perder o progresso do usuário em várias contas. + Para redefinir seu PIN, desinstale %s e reinstale-o.\n\nLembre-se de que, se o dispositivo não estiver online, você pode perder o progresso do usuário em várias contas. Ir para a Play Store Mostrar/Esconder ícone da senha Ícone de mostrar a senha @@ -234,6 +241,7 @@ Cancelar Enviar PIN do administrador incorreto. Por favor, tente novamente. + Novo PIN de %1$s Insira um Novo Pin Meus Downloads Downloads @@ -269,6 +277,7 @@ Seu PIN deve ter 3 dígitos. Seu PIN deve ter 5 dígitos. Criar um PIN de 3 Dígitos + *Requerido Botão de Voltar Próximo Geral @@ -397,14 +406,35 @@ Wifi Celular Sem conexão - Dependências de Terceiros + Dependências de terceiros versão %s Licença de Direitos Autorais Visualizador de Licença de Direitos Autorais Voltar para %s lista de dependências de terceiros lista de licenças de direitos autorais - Retomar Lição + Retomar lição Continuar Recomeçar + Bom dia, + Boa tarde, + Boa noite, + Como posso criar um novo perfil? + Como posso deletar um perfil? + Como posso alterar meu e-mail/número de telefone? + O que é %s? + Quem é um administrador? + Por que a exploração não está carregando? + Por que meu áudio não está tocando? + Como faço o download de um tópico? + Não consigo encontrar minha pergunta aqui. E agora? + <p>Se é a sua primeira vez criando um perfil e você não tem um PIN:</p> <p> 1. No Seletor de Perfil, toque em <strong>Configurar Múltiplos Perfis</strong>. </p> <p> 2. Crie um PIN e <strong>Salvar</strong>. </p> <p> 3. Preencha todos os campos do perfil. </p> <ol> <li> (Opcional) Carregar uma foto. </li> <li> Insira um nome. </li> <li> (Opcional) Atribua um PIN de 3 dígitos. </li> </ol> <p> 4. Toque em <strong>Criar</strong>. Este perfil está adicionado ao seu Seletor de Perfil! <br/> <br/> Se você já criou um perfil antes e tem um PIN: </p> <p> 1. No Seletor de Perfil, toque em <strong>Adicionar Perfil</strong>. </p> <p> 2. Digite seu PIN e toque em <strong>Enviar</strong>. </p> <p> 3. Preencha todos os campos do perfil. </p> <ol> <li> (Opcional) Carregar uma foto. </li> <li> Insira um nome. </li> <li> (Opcional) Atribua um PIN de 3 dígitos. </li> </ol> <p> 4. Toque em <strong>Criar</strong>. Este perfil está adicionado ao seu Seletor de Perfil! <br/> <br/> Nota: Apenas o <u>Administrador</u> pode gerenciar perfis.</p> + <p>Depois que um perfil é deletado:</p> <p><br></p> <p> <li> O perfil não pode ser recuperado. </li> </p> <p> <li> As informações do perfil, como nome, fotos e progresso, serão excluídas permanentemente. </li> </p> <p><br></p> <p>Para deletar um perfil(excluindo o do <u>Administrador</u>):</p> <p>1. Na página inicial do administrador, toque no botão de menu no canto superior esquerdo.</p> <p>2. Toque em <strong>Controles do Administrador</strong>.</p> <p>3. Toque em <strong>Editar Perfis</strong>.</p> <p>4. Toque no perfil que deseja excluir.</p> <p>5. Na parte inferior da tela, toque em <strong>Exclusão de Perfil</strong>.</p> <p>6. Toque em <strong>Deletar</strong> para confirmar a exclusão.</p><p><br></p><p>Nota: Apenas o <u>Administrador</u> pode gerenciar perfis.</p> + <p>Para alterar seu e-mail/número de telefone:</p> <p>1. Na página inicial do administrador, toque no botão de menu no canto superior esquerdo.</p> <p>2. Toque em <strong>Controles do Administrador</strong>.</p> <p>3. Toque em <strong>Editar Conta</strong>.</p> <p><br></p> <p>Se você deseja alterar seu e-mail:</p> <p>4. Digite seu novo e-mail e toque em <strong>Salvar</strong>.</p> <p>5. Um link de confirmação será enviado para confirmar seu novo e-mail. O link irá expirar após 24 horas e deve ser clicado para ser associado à sua conta. </p> <p><br></p> <p>Se mudar seu número de telefone:</p> <p>4. Digite seu novo número de telefone e toque em <strong>Verificar</strong>.</p> <p>5. Um código será enviado para confirmar seu novo número. O código expira após 5 minutos e deve ser inserido na nova tela para ser associado à sua conta.</p> + <p>%1$s <i>\"O-pee-yah\"</i> (Finnish) - \"aprender\"</p><p><br></p><p>%1$s tem a missão de ajudar qualquer pessoa a aprender o que quiser de uma forma eficaz e agradável.</p><p><br></p><p>Ao criar um conjunto de aulas gratuitas, de alta qualidade e comprovadamente eficazes com a ajuda de educadores de todo o mundo, %1$s visa proporcionar aos alunos uma educação de qualidade - independentemente de onde estejam ou a quais recursos tradicionais tenham acesso.</p><p><br></p><p>Como estudante, você pode começar sua aventura de aprendizado navegando pelos tópicos listados na página inicial!</p> + <p>Um administrador é o usuário principal que gerencia perfis e configurações para cada perfil em sua conta. Provavelmente, eles são seus pais, professores ou responsáveis ​​que criaram este perfil para você.</p><p><br></p><p>Os administradores podem gerenciar perfis, atribuir PINs e alterar outras configurações em suas contas. Dependendo do seu perfil, as permissões de administrador podem ser necessárias para determinados recursos, como download de tópicos, alteração do PIN e muito mais. </p><p><br></p><p>Para ver quem é o seu administrador, vá para o Seletor de perfil. O primeiro perfil listado e com \"Administrador\" escrito em seu nome é o Administrador. </p> + <p>Se a exploração não estiver carregando</p><p><br></p><p>Verifique se o aplicativo está atualizado:</p><p> <ol> <li> Acesse a Play Store e certifique-se de que o aplicativo esteja atualizado com a versão mais recente </li> </ol> </p><p><br></p><p>Verifique sua conexão com a internet:</p><p> <li> Se sua conexão com a Internet estiver lenta, tente se reconectar à rede Wi-Fi ou conectar-se a uma rede diferente. </li> </p><p><br></p><p>Peça ao administrador para verificar o dispositivo e a conexão com a Internet:</p><p> <li> Peça ao administrador para solucionar o problema usando as etapas acima </li> </p><p><br></p><p>Informe-nos se você ainda tiver problemas com o carregamento:</p><p> <li> Relate um problema entrando em contato conosco em admin@oppia.org. </li> </p> + <p>Se o seu áudio não estiver tocando</p><p><br></p><p>Verifique se o aplicativo está atualizado:</p><p> <li> Acesse a Play Store e certifique-se de que o aplicativo esteja atualizado com a versão mais recente </li> </p><p><br></p><p>Verifique sua conexão com a internet:</p><p> <li> Se sua conexão com a Internet estiver lenta, tente se reconectar à rede Wi-Fi ou conectar-se a uma rede diferente. A Internet lenta pode fazer com que o áudio carregue irregularmente, dificultando a reprodução. </li> </p><p><br></p><p>Peça ao administrador para verificar o dispositivo e a conexão com a Internet:</p><p> <li> Peça ao administrador para solucionar o problema usando as etapas acima</li> </p><p><br></p><p>Informe-nos se você ainda tiver problemas com o carregamento:</p><p> <li> Relate um problema entrando em contato conosco em admin@oppia.org. </li></p> + <p>Para baixar uma Exploração</p><p>1. Na página inicial, toque em um Tópico ou Exploração.</p><p>2. Na Página do Tópico, toque em <strong>Info</strong> aba.</p><p>3. Toque em <strong>Baixar Tópico</strong>. </p><p>4. Dependendo das configurações do aplicativo, você pode precisar da aprovação do Administrador ou de uma conexão Wifi estável para concluir o download. Se necessário, uma vez que esses requisitos sejam satisfeitos, o Tópico foi baixado para o dispositivo e pode ser usado offline por todos os perfis. <p> + <p>Se você não consegue encontrar sua pergunta ou gostaria de relatar um problema, entre em contato conosco em admin@oppia.org.</p> diff --git a/app/src/main/res/values-sw600dp-land/dimens.xml b/app/src/main/res/values-sw600dp-land/dimens.xml index f15655f2f9f..6fdbe35a1e9 100644 --- a/app/src/main/res/values-sw600dp-land/dimens.xml +++ b/app/src/main/res/values-sw600dp-land/dimens.xml @@ -322,4 +322,9 @@ 48dp 100dp + + 0dp + 32dp + 16sp + 4dp diff --git a/app/src/main/res/values-sw600dp-port/dimens.xml b/app/src/main/res/values-sw600dp-port/dimens.xml index d2f9881d062..428084dfa53 100644 --- a/app/src/main/res/values-sw600dp-port/dimens.xml +++ b/app/src/main/res/values-sw600dp-port/dimens.xml @@ -324,5 +324,9 @@ 48dp 100dp - + + 8dp + 24dp + 16sp + 4dp diff --git a/app/src/main/res/values-xxhdpi/dimens.xml b/app/src/main/res/values-xxhdpi/dimens.xml new file mode 100644 index 00000000000..21e07668b22 --- /dev/null +++ b/app/src/main/res/values-xxhdpi/dimens.xml @@ -0,0 +1,8 @@ + + + + 64dp + 36dp + 60dp + 60dp + diff --git a/app/src/main/res/values/dimens.xml b/app/src/main/res/values/dimens.xml index 0be5019b680..94dad7a33c2 100644 --- a/app/src/main/res/values/dimens.xml +++ b/app/src/main/res/values/dimens.xml @@ -136,7 +136,7 @@ 24dp 24dp 24dp - 84dp + 60dp 24dp 24dp 24dp @@ -444,6 +444,11 @@ 32dp 12dp + + 64dp + 36dp + 60dp + 0dp 0dp @@ -485,4 +490,10 @@ 36dp 148dp + + 8dp + 8dp + 280dp + 16sp + 8dp diff --git a/app/src/sharedTest/java/org/oppia/android/app/topic/practice/TopicPracticeFragmentTest.kt b/app/src/sharedTest/java/org/oppia/android/app/topic/practice/TopicPracticeFragmentTest.kt index 7e9128034f4..3abbf827b28 100644 --- a/app/src/sharedTest/java/org/oppia/android/app/topic/practice/TopicPracticeFragmentTest.kt +++ b/app/src/sharedTest/java/org/oppia/android/app/topic/practice/TopicPracticeFragmentTest.kt @@ -4,6 +4,7 @@ import android.app.Application import androidx.appcompat.app.AppCompatActivity import androidx.recyclerview.widget.RecyclerView import androidx.test.core.app.ActivityScenario +import androidx.test.core.app.ActivityScenario.launch import androidx.test.core.app.ApplicationProvider import androidx.test.espresso.Espresso.onView import androidx.test.espresso.action.ViewActions.click @@ -26,7 +27,6 @@ import org.hamcrest.Matchers.allOf import org.hamcrest.Matchers.not import org.junit.After import org.junit.Before -import org.junit.Ignore import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith @@ -244,15 +244,16 @@ class TopicPracticeFragmentTest { } @Test - @Ignore("Flaky test") // TODO(#3413): Test is failing unexpectedly. fun testTopicPracticeFragment_loadFragment_selectSubtopics_clickStartButton_skillListTransferSuccessfully() { // ktlint-disable max-line-length - launchTopicActivityIntent(internalProfileId, FRACTIONS_TOPIC_ID) - clickPracticeTab() - clickPracticeItem(position = 1, targetViewId = R.id.subtopic_check_box) - scrollToPosition(position = 5) - clickPracticeItem(position = 5, targetViewId = R.id.topic_practice_start_button) - intended(hasComponent(QuestionPlayerActivity::class.java.name)) - intended(hasExtra(QuestionPlayerActivity.getIntentKey(), skillIdList)) + testCoroutineDispatchers.unregisterIdlingResource() + launchTopicActivityIntent(internalProfileId, FRACTIONS_TOPIC_ID).use { + clickPracticeTab() + clickPracticeItem(position = 1, targetViewId = R.id.subtopic_check_box) + scrollToPosition(position = 5) + clickPracticeItem(position = 5, targetViewId = R.id.topic_practice_start_button) + intended(hasComponent(QuestionPlayerActivity::class.java.name)) + intended(hasExtra(QuestionPlayerActivity.getIntentKey(), skillIdList)) + } } @Test diff --git a/scripts/assets/filename_pattern_validation_checks.textproto b/scripts/assets/filename_pattern_validation_checks.textproto index fc6cad22b44..9307ed6f21d 100644 --- a/scripts/assets/filename_pattern_validation_checks.textproto +++ b/scripts/assets/filename_pattern_validation_checks.textproto @@ -1,4 +1,8 @@ filename_checks { prohibited_filename_regex: "^((?!(app|testing)).)+/src/main/.+?Activity.kt" - failure_message: "Activities cannot be placed outside the app or testing module" + failure_message: "Activities cannot be placed outside the app or testing module." +} +filename_checks { + prohibited_filename_regex: "^.+?/res/([^/]+/){2,}[^/]+$" + failure_message: "Only one level of subdirectories under res/ should be maintained (further subdirectories aren't supported by the project configuration)." } diff --git a/scripts/src/java/org/oppia/android/scripts/regex/RegexPatternValidationCheck.kt b/scripts/src/java/org/oppia/android/scripts/regex/RegexPatternValidationCheck.kt index c6ba0af0f16..464251b2689 100644 --- a/scripts/src/java/org/oppia/android/scripts/regex/RegexPatternValidationCheck.kt +++ b/scripts/src/java/org/oppia/android/scripts/regex/RegexPatternValidationCheck.kt @@ -32,24 +32,25 @@ fun main(vararg args: String) { // Check if the repo has any filename failure. val hasFilenameCheckFailure = retrieveFilenameChecks() - .fold(initial = false) { isFailing, filenameCheck -> - val checkFailed = checkProhibitedFileNamePattern( + .fold(initial = false) { hasFailingFile, filenameCheck -> + val fileFails = checkProhibitedFileNamePattern( repoRoot, searchFiles, filenameCheck, ) - isFailing || checkFailed + return@fold hasFailingFile || fileFails } // Check if the repo has any file content failure. - val hasFileContentCheckFailure = retrieveFileContentChecks() - .fold(initial = false) { isFailing, fileContentCheck -> - val checkFailed = checkProhibitedContent( + val contentChecks = retrieveFileContentChecks().map { MatchableFileContentCheck.createFrom(it) } + val hasFileContentCheckFailure = + searchFiles.fold(initial = false) { hasFailingFile, searchFile -> + val fileFails = checkProhibitedContent( repoRoot, - searchFiles, - fileContentCheck + searchFile, + contentChecks ) - isFailing || checkFailed + return@fold hasFailingFile || fileFails } if (hasFilenameCheckFailure || hasFileContentCheckFailure) { @@ -138,43 +139,33 @@ private fun checkProhibitedFileNamePattern( * Checks for a prohibited file content. * * @param repoRoot the root directory of the repo - * @param searchFiles a list of all the files which needs to be checked - * @param fileContentCheck proto object of FileContentCheck + * @param searchFile the file to check for prohibited content + * @param fileContentChecks contents to check for validity * @return whether the file content pattern is correct or not */ private fun checkProhibitedContent( repoRoot: File, - searchFiles: List, - fileContentCheck: FileContentCheck + searchFile: File, + fileContentChecks: Iterable ): Boolean { - val filePathRegex = fileContentCheck.filePathRegex.toRegex() - val prohibitedContentRegex = fileContentCheck.prohibitedContentRegex.toRegex() - - val matchedFiles = searchFiles.filter { file -> - val fileRelativePath = file.toRelativeString(repoRoot) - val isFileExactExemption = fileRelativePath in fileContentCheck.exemptedFileNameList - val isFileMatchedExemption = fileContentCheck.exemptedFilePatternsList.any { pattern -> - pattern.toRegex().matches(fileRelativePath) - } - // TODO: add tests. - val isExempted = isFileExactExemption || isFileMatchedExemption - return@filter if (!isExempted && filePathRegex.matches(fileRelativePath)) { - file.useLines { lines -> - lines.foldIndexed(initial = false) { lineIndex, isFailing, lineContent -> - val matches = prohibitedContentRegex.containsMatchIn(lineContent) - if (matches) { - logProhibitedContentFailure( - lineIndex + 1, // Increment by 1 since line numbers begin at 1 rather than 0. - fileContentCheck.failureMessage, - fileRelativePath - ) - } - isFailing || matches + val lines = searchFile.readLines() + return fileContentChecks.fold(initial = false) { hasFailingFile, fileContentCheck -> + val fileRelativePath = searchFile.toRelativeString(repoRoot) + val fileFails = if (fileContentCheck.isFileAffectedByCheck(fileRelativePath)) { + val affectedLines = fileContentCheck.computeAffectedLines(lines) + if (affectedLines.isNotEmpty()) { + affectedLines.forEach { lineIndex -> + logProhibitedContentFailure( + lineIndex + 1, // Increment by 1 since line numbers begin at 1 rather than 0. + fileContentCheck.failureMessage, + fileRelativePath + ) } } + affectedLines.isNotEmpty() } else false + return@fold hasFailingFile || fileFails } - return matchedFiles.isNotEmpty() } /** @@ -213,3 +204,46 @@ private fun logProhibitedContentFailure( val failureMessage = "$filePath:$lineNumber: $errorToShow" println(failureMessage) } + +/** A matchable version of [FileContentCheck]. */ +private data class MatchableFileContentCheck( + val filePathRegex: Regex, + val prohibitedContentRegex: Regex, + val failureMessage: String, + val exemptedFileNames: List, + val exemptedFilePatterns: List +) { + /** + * Returns whether the relative file given by the specified path should be affected by this check + * (i.e. that it matches the inclusion pattern and is not explicitly or implicitly excluded). + */ + fun isFileAffectedByCheck(relativePath: String): Boolean = + filePathRegex.matches(relativePath) && !isFileExempted(relativePath) + + /** + * Returns the list of line indexes which contain prohibited content per this check (given an + * iterable of lines). Note that the returned indexes are based on the iteration order of the + * provided iterable. + */ + fun computeAffectedLines(lines: Iterable): List { + return lines.withIndex().filter { (_, line) -> + prohibitedContentRegex.containsMatchIn(line) + }.map { (index, _) -> index } + } + + private fun isFileExempted(relativePath: String): Boolean = + relativePath in exemptedFileNames || exemptedFilePatterns.any { it.matches(relativePath) } + + companion object { + /** Returns a new [MatchableFileContentCheck] based on the specified [FileContentCheck]. */ + fun createFrom(fileContentCheck: FileContentCheck): MatchableFileContentCheck { + return MatchableFileContentCheck( + filePathRegex = fileContentCheck.filePathRegex.toRegex(), + prohibitedContentRegex = fileContentCheck.prohibitedContentRegex.toRegex(), + failureMessage = fileContentCheck.failureMessage, + exemptedFileNames = fileContentCheck.exemptedFileNameList, + exemptedFilePatterns = fileContentCheck.exemptedFilePatternsList.map { it.toRegex() } + ) + } + } +} diff --git a/scripts/src/javatests/org/oppia/android/scripts/regex/RegexPatternValidationCheckTest.kt b/scripts/src/javatests/org/oppia/android/scripts/regex/RegexPatternValidationCheckTest.kt index e6f8c23870a..675ebe06f53 100644 --- a/scripts/src/javatests/org/oppia/android/scripts/regex/RegexPatternValidationCheckTest.kt +++ b/scripts/src/javatests/org/oppia/android/scripts/regex/RegexPatternValidationCheckTest.kt @@ -17,13 +17,18 @@ class RegexPatternValidationCheckTest { private val originalOut: PrintStream = System.out private val REGEX_CHECK_PASSED_OUTPUT_INDICATOR: String = "REGEX PATTERN CHECKS PASSED" private val REGEX_CHECK_FAILED_OUTPUT_INDICATOR: String = "REGEX PATTERN CHECKS FAILED" + private val activitiesPlacementErrorMessage = + "Activities cannot be placed outside the app or testing module." + private val nestedResourceSubdirectoryErrorMessage = + "Only one level of subdirectories under res/ should be maintained (further subdirectories " + + "aren't supported by the project configuration)." private val supportLibraryUsageErrorMessage = "AndroidX should be used instead of the support library" private val coroutineWorkerUsageErrorMessage = "For stable tests, prefer using ListenableWorker with an Oppia-managed dispatcher." private val settableFutureUsageErrorMessage = - "SettableFuture should only be used in pre-approved locations since it's easy to potentially" + - " mess up & lead to a hanging ListenableFuture." + "SettableFuture should only be used in pre-approved locations since it's easy to potentially " + + "mess up & lead to a hanging ListenableFuture." private val androidGravityLeftErrorMessage = "Use android:gravity=\"start\", instead, for proper RTL support" private val androidGravityRightErrorMessage = @@ -47,8 +52,8 @@ class RegexPatternValidationCheckTest { private val androidTouchAnchorSideRightErrorMessage = "Use motion:touchAnchorSide=\"end\", instead, for proper RTL support" private val oppiaCantBeTranslatedErrorMessage = - "Oppia should never used directly in a string (since it shouldn't be translated). Instead," + - " use a parameter & insert the string retrieved from app_name." + "Oppia should never used directly in a string (since it shouldn't be translated). Instead, " + + "use a parameter & insert the string retrieved from app_name." private val untranslatableStringsGoInSpecificFileErrorMessage = "Untranslatable strings should go in untranslated_strings.xml, instead." private val translatableStringsGoInMainFileErrorMessage = @@ -104,8 +109,58 @@ class RegexPatternValidationCheckTest { assertThat(exception).hasMessageThat().contains(REGEX_CHECK_FAILED_OUTPUT_INDICATOR) assertThat(outContent.toString().trim()).isEqualTo( """ - File name/path violation: Activities cannot be placed outside the app or testing module + File name/path violation: $activitiesPlacementErrorMessage - data/src/main/TestActivity.kt + + $wikiReferenceNote + """.trimIndent() + ) + } + + @Test + fun testFileNamePattern_appResources_stringsFile_fileNamePatternIsCorrect() { + tempFolder.newFolder("testfiles", "app", "src", "main", "res", "values") + tempFolder.newFile("testfiles/app/src/main/res/values/strings.xml") + + runScript() + + assertThat(outContent.toString().trim()).isEqualTo(REGEX_CHECK_PASSED_OUTPUT_INDICATOR) + } + + @Test + fun testFileNamePattern_appResources_subValuesDir_stringsFile_fileNamePatternIsNotCorrect() { + tempFolder.newFolder("testfiles", "app", "src", "main", "res", "values", "subdir") + tempFolder.newFile("testfiles/app/src/main/res/values/subdir/strings.xml") + + val exception = assertThrows(Exception::class) { + runScript() + } + + assertThat(exception).hasMessageThat().contains(REGEX_CHECK_FAILED_OUTPUT_INDICATOR) + assertThat(outContent.toString().trim()).isEqualTo( + """ + File name/path violation: $nestedResourceSubdirectoryErrorMessage + - app/src/main/res/values/subdir/strings.xml + + $wikiReferenceNote + """.trimIndent() + ) + } + + @Test + fun testFileNamePattern_domainResources_subValuesDir_stringsFile_fileNamePatternIsNotCorrect() { + tempFolder.newFolder("testfiles", "domain", "src", "main", "res", "drawable", "subdir") + tempFolder.newFile("testfiles/domain/src/main/res/drawable/subdir/example.png") + + val exception = assertThrows(Exception::class) { + runScript() + } + + assertThat(exception).hasMessageThat().contains(REGEX_CHECK_FAILED_OUTPUT_INDICATOR) + assertThat(outContent.toString().trim()).isEqualTo( + """ + File name/path violation: $nestedResourceSubdirectoryErrorMessage + - domain/src/main/res/drawable/subdir/example.png $wikiReferenceNote """.trimIndent() @@ -828,6 +883,30 @@ class RegexPatternValidationCheckTest { assertThat(outContent.toString().trim()).isEqualTo(REGEX_CHECK_PASSED_OUTPUT_INDICATOR) } + @Test + fun testFileContent_translatableString_inPrimaryStringsFile_fileContentIsCorrect() { + val prohibitedContent = "Translatable" + tempFolder.newFolder("testfiles", "app", "src", "main", "res", "values") + val stringFilePath = "app/src/main/res/values/strings.xml" + tempFolder.newFile("testfiles/$stringFilePath").writeText(prohibitedContent) + + runScript() + + assertThat(outContent.toString().trim()).isEqualTo(REGEX_CHECK_PASSED_OUTPUT_INDICATOR) + } + + @Test + fun testFileContent_translatableString_inTranslatedPrimaryStringsFile_fileContentIsCorrect() { + val prohibitedContent = "Translatable" + tempFolder.newFolder("testfiles", "app", "src", "main", "res", "values-ar") + val stringFilePath = "app/src/main/res/values-ar/strings.xml" + tempFolder.newFile("testfiles/$stringFilePath").writeText(prohibitedContent) + + runScript() + + assertThat(outContent.toString().trim()).isEqualTo(REGEX_CHECK_PASSED_OUTPUT_INDICATOR) + } + @Test fun testFilenameAndContent_useProhibitedFileName_useProhibitedFileContent_multipleFailures() { tempFolder.newFolder("testfiles", "data", "src", "main") @@ -842,10 +921,10 @@ class RegexPatternValidationCheckTest { assertThat(exception).hasMessageThat().contains(REGEX_CHECK_FAILED_OUTPUT_INDICATOR) assertThat(outContent.toString().trim()).isEqualTo( """ - File name/path violation: Activities cannot be placed outside the app or testing module + File name/path violation: $activitiesPlacementErrorMessage - data/src/main/TestActivity.kt - data/src/main/TestActivity.kt:1: AndroidX should be used instead of the support library + data/src/main/TestActivity.kt:1: $supportLibraryUsageErrorMessage $wikiReferenceNote """.trimIndent() ) diff --git a/utility/src/main/java/org/oppia/android/util/parser/html/CustomHtmlContentHandler.kt b/utility/src/main/java/org/oppia/android/util/parser/html/CustomHtmlContentHandler.kt index bd115719c1a..c19f35f61f3 100644 --- a/utility/src/main/java/org/oppia/android/util/parser/html/CustomHtmlContentHandler.kt +++ b/utility/src/main/java/org/oppia/android/util/parser/html/CustomHtmlContentHandler.kt @@ -113,8 +113,6 @@ class CustomHtmlContentHandler private constructor( customTagHandlers.getValue(tag).handleClosingTag(output) customTagHandlers.getValue(tag) .handleTag(attributes, openTagIndex, output.length, output, imageRetriever) - customTagHandlers.getValue(tag) - .handleContentDescription(attributes, openTagIndex, output.length, output) } } } @@ -165,21 +163,6 @@ class CustomHtmlContentHandler private constructor( * @param output the destination [Editable] to which spans can be added */ fun handleClosingTag(output: Editable) {} - - /** - * Called when a custom tag is encountered. This is always called after the closing tag. - * - * @param attributes the tag's attributes - * @param openIndex the index in the output [Editable] at which this tag begins - * @param closeIndex the index in the output [Editable] at which this tag ends - * @param output the destination [Editable] to which spans can be added - */ - fun handleContentDescription( - attributes: Attributes, - openIndex: Int, - closeIndex: Int, - output: Editable - ) {} } /** diff --git a/utility/src/main/java/org/oppia/android/util/parser/html/ImageTagHandler.kt b/utility/src/main/java/org/oppia/android/util/parser/html/ImageTagHandler.kt index 4581c2587ad..5d55fcf4ed7 100644 --- a/utility/src/main/java/org/oppia/android/util/parser/html/ImageTagHandler.kt +++ b/utility/src/main/java/org/oppia/android/util/parser/html/ImageTagHandler.kt @@ -2,7 +2,6 @@ package org.oppia.android.util.parser.html import android.text.Editable import android.text.Spannable -import android.text.SpannableStringBuilder import android.text.style.ImageSpan import org.oppia.android.util.logging.ConsoleLogger import org.xml.sax.Attributes @@ -10,7 +9,6 @@ import org.xml.sax.Attributes /** The custom tag corresponding to [ImageTagHandler]. */ const val CUSTOM_IMG_TAG = "oppia-noninteractive-image" private const val CUSTOM_IMG_FILE_PATH_ATTRIBUTE = "filepath-with-value" -private const val CUSTOM_IMG_ALT_TEXT_ATTRIBUTE = "alt-with-value" /** * A custom tag handler for supporting custom Oppia images parsed with [CustomHtmlContentHandler]. @@ -45,25 +43,6 @@ class ImageTagHandler( endIndex, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE ) - } else consoleLogger.e("ImageTagHandler", "Failed to parse $CUSTOM_IMG_FILE_PATH_ATTRIBUTE") - } - - override fun handleContentDescription( - attributes: Attributes, - openIndex: Int, - closeIndex: Int, - output: Editable - ) { - val contentDescription = attributes.getJsonStringValue(CUSTOM_IMG_ALT_TEXT_ATTRIBUTE) - if (contentDescription != null) { - val spannableBuilder = SpannableStringBuilder(contentDescription) - spannableBuilder.setSpan( - contentDescription, - /* start= */ 0, - /* end= */ contentDescription.length, - Spannable.SPAN_INCLUSIVE_EXCLUSIVE - ) - output.replace(openIndex, closeIndex, spannableBuilder) - } else consoleLogger.e("ImageTagHandler", "Failed to parse $CUSTOM_IMG_ALT_TEXT_ATTRIBUTE") + } else consoleLogger.e("ImageTagHandler", "Failed to parse image tag") } } diff --git a/utility/src/test/java/org/oppia/android/util/parser/html/ImageTagHandlerTest.kt b/utility/src/test/java/org/oppia/android/util/parser/html/ImageTagHandlerTest.kt index 9dd8e10aeb1..352fad75028 100644 --- a/utility/src/test/java/org/oppia/android/util/parser/html/ImageTagHandlerTest.kt +++ b/utility/src/test/java/org/oppia/android/util/parser/html/ImageTagHandlerTest.kt @@ -50,10 +50,6 @@ private const val IMAGE_TAG_WITHOUT_FILEPATH_MARKUP = "" -private const val IMAGE_TAG_WITHOUT_ALT_VALUE_MARKUP = - "" - /** Tests for [ImageTagHandler]. */ @RunWith(AndroidJUnit4::class) @LooperMode(LooperMode.Mode.PAUSED) @@ -113,7 +109,7 @@ class ImageTagHandlerTest { fun testParseHtml_withImageCardMarkup_hasNoReadableText() { val parsedHtml = CustomHtmlContentHandler.fromHtml( - html = IMAGE_TAG_WITHOUT_ALT_VALUE_MARKUP, + html = IMAGE_TAG_MARKUP_1, imageRetriever = mockImageRetriever, customTagHandlers = tagHandlersWithImageTagSupport ) @@ -124,22 +120,6 @@ class ImageTagHandlerTest { assertThat(parsedHtmlStr.first().isObjectReplacementCharacter()).isTrue() } - @Test - fun testParseHtml_withImageCardMarkup_missingAltValue_hasReadableText() { - val parsedHtml = - CustomHtmlContentHandler.fromHtml( - html = IMAGE_TAG_MARKUP_1, - imageRetriever = mockImageRetriever, - customTagHandlers = tagHandlersWithImageTagSupport - ) - - // The image only adds a control character, so there aren't any human-readable characters. - val parsedHtmlStr = parsedHtml.toString() - val parsedContentDescription = "alt text 1" - assertThat(parsedHtmlStr).hasLength(parsedContentDescription.length) - assertThat(parsedHtmlStr.first().isObjectReplacementCharacter()).isFalse() - } - @Test fun testParseHtml_withImageCardMarkup_missingFilename_doesNotIncludeImageSpan() { val parsedHtml =