From 8d78f2773ac1419518c24c147f617c01c9c9833a Mon Sep 17 00:00:00 2001 From: Norbel AMBANUMBEN Date: Thu, 18 Jan 2024 12:11:23 +0100 Subject: [PATCH 1/6] fix: failing tests (#651) # Proposed Changes - Modified `CircumventionTest` to remove `riseupvpn` related tests since its status changed in #625 - Updated tests for ExpandableListView to use proper methods. --- .../ui/resultdetails/CircumventionTest.java | 37 -------- .../ui/resultdetails/PerformanceTest.java | 87 +++++++++---------- .../ui/resultdetails/WebsitesTest.java | 17 ++-- gradle.properties | 2 +- 4 files changed, 53 insertions(+), 90 deletions(-) diff --git a/app/src/androidTest/java/org/openobservatory/ooniprobe/ui/resultdetails/CircumventionTest.java b/app/src/androidTest/java/org/openobservatory/ooniprobe/ui/resultdetails/CircumventionTest.java index 233060e1b..a980b4853 100644 --- a/app/src/androidTest/java/org/openobservatory/ooniprobe/ui/resultdetails/CircumventionTest.java +++ b/app/src/androidTest/java/org/openobservatory/ooniprobe/ui/resultdetails/CircumventionTest.java @@ -113,41 +113,4 @@ public void testBlockedTor() { onView(withId(R.id.authorities)).check(matches(withText(formattedAuthorities))); assertMeasurementRuntimeAndNetwork(measurement, testResult.network); } - - @Test - public void testSuccessfulRiseUpVPN() { - // Arrange - Result testResult = ResultFactory.createAndSave(new CircumventionSuite(), 3, 0); - Measurement measurement = testResult.getMeasurement("riseupvpn"); - - // Act - launchDetails(testResult.id); - onView(withText("RiseupVPN Test")).check(matches(isDisplayed())).perform(click()); - - // Assert - assertMeasurementOutcome(true); - onView(withId(R.id.bootstrap_value)).check(matches(withText(TEST_RESULTS_AVAILABLE))); - onView(withId(R.id.openvpn_value)).check(matches(withText(TEST_RESULTS_AVAILABLE))); - onView(withId(R.id.bridges_value)).check(matches(withText(TEST_RESULTS_AVAILABLE))); - assertMeasurementRuntimeAndNetwork(measurement, testResult.network); - } - - @Test - public void testBlockedRiseUpVPN() { - // Arrange - Result testResult = ResultFactory.createAndSave(new CircumventionSuite(), 0, 3); - Measurement measurement = testResult.getMeasurement("riseupvpn"); - - // Act - launchDetails(testResult.id); - onView(withText("RiseupVPN Test")).check(matches(isDisplayed())).perform(click()); - - // Assert - assertMeasurementOutcome(false); - onView(withId(R.id.bootstrap_value)).check(matches(withText("Blocked"))); - onView(withId(R.id.openvpn_value)).check(matches(withText("1 blocked"))); - onView(withId(R.id.bridges_value)).check(matches(withText("1 blocked"))); - assertMeasurementRuntimeAndNetwork(measurement, testResult.network); - } - } diff --git a/app/src/androidTest/java/org/openobservatory/ooniprobe/ui/resultdetails/PerformanceTest.java b/app/src/androidTest/java/org/openobservatory/ooniprobe/ui/resultdetails/PerformanceTest.java index 130d8c779..4d8cb03ec 100644 --- a/app/src/androidTest/java/org/openobservatory/ooniprobe/ui/resultdetails/PerformanceTest.java +++ b/app/src/androidTest/java/org/openobservatory/ooniprobe/ui/resultdetails/PerformanceTest.java @@ -1,5 +1,18 @@ package org.openobservatory.ooniprobe.ui.resultdetails; +import static androidx.test.espresso.Espresso.onData; +import static androidx.test.espresso.Espresso.onView; +import static androidx.test.espresso.action.ViewActions.click; +import static androidx.test.espresso.action.ViewActions.swipeLeft; +import static androidx.test.espresso.assertion.ViewAssertions.matches; +import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed; +import static androidx.test.espresso.matcher.ViewMatchers.withId; +import static androidx.test.espresso.matcher.ViewMatchers.withText; +import static org.hamcrest.Matchers.allOf; +import static org.hamcrest.Matchers.anything; +import static org.hamcrest.Matchers.containsString; + +import androidx.test.espresso.assertion.ViewAssertions; import androidx.test.ext.junit.runners.AndroidJUnit4; import com.schibsted.spain.barista.rule.flaky.AllowFlaky; @@ -20,19 +33,6 @@ import org.openobservatory.ooniprobe.test.test.Ndt; import org.openobservatory.ooniprobe.utils.FormattingUtils; -import static androidx.test.espresso.Espresso.onView; -import static androidx.test.espresso.action.ViewActions.click; -import static androidx.test.espresso.action.ViewActions.swipeLeft; -import static androidx.test.espresso.assertion.ViewAssertions.matches; -import static androidx.test.espresso.contrib.RecyclerViewActions.actionOnItemAtPosition; -import static androidx.test.espresso.contrib.RecyclerViewActions.scrollToPosition; -import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed; -import static androidx.test.espresso.matcher.ViewMatchers.withId; -import static androidx.test.espresso.matcher.ViewMatchers.withText; -import static org.hamcrest.Matchers.allOf; -import static org.hamcrest.Matchers.containsString; -import static org.openobservatory.ooniprobe.ui.utils.RecyclerViewMatcher.withRecyclerView; - @RunWith(AndroidJUnit4.class) public class PerformanceTest extends MeasurementAbstractTest { @@ -112,35 +112,34 @@ public void testListOfMeasurements() { launchDetails(testResult.id); // Assert - onView(withId(R.id.recyclerView)) - .perform(scrollToPosition(0)); - - onView(withRecyclerView(R.id.recyclerView) - .atPositionOnView(0, R.id.data1)) - .check(matches(withText(download + " " + downloadUnity))); - onView(withRecyclerView(R.id.recyclerView) - .atPositionOnView(0, R.id.data2)) - .check(matches(withText(upload + " " + uploadUnity))); - - onView(withId(R.id.recyclerView)) - .perform(scrollToPosition(1)); - - onView(withRecyclerView(R.id.recyclerView) - .atPositionOnView(1, R.id.data1)) + onData(anything()) + .inAdapterView(withId(R.id.recyclerView)) + .atPosition(0) + .onChildView(withId(R.id.data1)) + .check(ViewAssertions.matches(withText(download + " " + downloadUnity))); + + onData(anything()) + .inAdapterView(withId(R.id.recyclerView)) + .atPosition(0) + .onChildView(withId(R.id.data2)) + .check(matches(withText(upload + " " + uploadUnity))); + + onData(anything()) + .inAdapterView(withId(R.id.recyclerView)) + .atPosition(1) + .onChildView(withId(R.id.data1)) .check(matches(withText(videoQuality))); - onView(withId(R.id.recyclerView)) - .perform(scrollToPosition(2)); - - onView(withRecyclerView(R.id.recyclerView) - .atPositionOnView(2, R.id.data1)) + onData(anything()) + .inAdapterView(withId(R.id.recyclerView)) + .atPosition(2) + .onChildView(withId(R.id.data1)) .check(matches(withText(notDetected))); - onView(withId(R.id.recyclerView)) - .perform(scrollToPosition(3)); - - onView(withRecyclerView(R.id.recyclerView) - .atPositionOnView(3, R.id.data1)) + onData(anything()) + .inAdapterView(withId(R.id.recyclerView)) + .atPosition(3) + .onChildView(withId(R.id.data1)) .check(matches(withText(notDetected))); } @@ -174,7 +173,7 @@ public void testNDT() { // Act launchDetails(testResult.id); - onView(withId(R.id.recyclerView)).perform(actionOnItemAtPosition(0, click())); + onData(anything()).inAdapterView(withId(R.id.recyclerView)).atPosition(0).perform(click()); // Assert onView(withText(download + downloadUnity)).check(matches(isDisplayed())); @@ -202,7 +201,7 @@ public void testStreaming() { // Act launchDetails(testResult.id); - onView(withId(R.id.recyclerView)).perform(actionOnItemAtPosition(1, click())); + onData(anything()).inAdapterView(withId(R.id.recyclerView)).atPosition(1).perform(click()); // Assert onView(withText(containsString(videoQuality))).check(matches(isDisplayed())); @@ -219,7 +218,7 @@ public void testRequestLine() { // Act launchDetails(testResult.id); - onView(withId(R.id.recyclerView)).perform(actionOnItemAtPosition(2, click())); + onData(anything()).inAdapterView(withId(R.id.recyclerView)).atPosition(2).perform(click()); // Assert onView(withText(getResourceString(R.string.TestResults_Details_Middleboxes_HTTPInvalidRequestLine_NotFound_Hero_Title))).check(matches(isDisplayed())); @@ -234,7 +233,7 @@ public void testRequestLineDetection() { // Act launchDetails(testResult.id); - onView(withId(R.id.recyclerView)).perform(actionOnItemAtPosition(2, click())); + onData(anything()).inAdapterView(withId(R.id.recyclerView)).atPosition(2).perform(click()); // Assert onView(withText(getResourceString(R.string.TestResults_Details_Middleboxes_HTTPInvalidRequestLine_Found_Hero_Title))).check(matches(isDisplayed())); @@ -249,7 +248,7 @@ public void testFieldManipulation() { // Act launchDetails(testResult.id); - onView(withId(R.id.recyclerView)).perform(actionOnItemAtPosition(3, click())); + onData(anything()).inAdapterView(withId(R.id.recyclerView)).atPosition(3).perform(click()); // Assert onView(withText(getResourceString(R.string.TestResults_Details_Middleboxes_HTTPHeaderFieldManipulation_NotFound_Hero_Title))).check(matches(isDisplayed())); @@ -264,7 +263,7 @@ public void testFieldManipulationDetection() { // Act launchDetails(testResult.id); - onView(withId(R.id.recyclerView)).perform(actionOnItemAtPosition(3, click())); + onData(anything()).inAdapterView(withId(R.id.recyclerView)).atPosition(3).perform(click()); // Assert onView(withText(getResourceString(R.string.TestResults_Details_Middleboxes_HTTPHeaderFieldManipulation_Found_Hero_Title))).check(matches(isDisplayed())); diff --git a/app/src/androidTest/java/org/openobservatory/ooniprobe/ui/resultdetails/WebsitesTest.java b/app/src/androidTest/java/org/openobservatory/ooniprobe/ui/resultdetails/WebsitesTest.java index 543b6d698..a9f82e008 100644 --- a/app/src/androidTest/java/org/openobservatory/ooniprobe/ui/resultdetails/WebsitesTest.java +++ b/app/src/androidTest/java/org/openobservatory/ooniprobe/ui/resultdetails/WebsitesTest.java @@ -18,6 +18,7 @@ import java.util.List; import java.util.concurrent.TimeUnit; +import static androidx.test.espresso.Espresso.onData; import static androidx.test.espresso.Espresso.onView; import static androidx.test.espresso.action.ViewActions.click; import static androidx.test.espresso.assertion.ViewAssertions.matches; @@ -27,6 +28,7 @@ import static androidx.test.espresso.matcher.ViewMatchers.isRoot; import static androidx.test.espresso.matcher.ViewMatchers.withId; import static androidx.test.espresso.matcher.ViewMatchers.withText; +import static org.hamcrest.Matchers.anything; import static org.hamcrest.Matchers.containsString; import static org.openobservatory.ooniprobe.ui.utils.RecyclerViewMatcher.withRecyclerView; import static org.openobservatory.ooniprobe.ui.utils.ViewMatchers.waitPartialText; @@ -64,12 +66,11 @@ public void checkListOfMeasurementsTest() { // Assert for (int i = 0; i < measurements.size(); i++) { - onView(withId(R.id.recyclerView)) - .perform(scrollToPosition(i)); - - onView(withRecyclerView(R.id.recyclerView) - .atPositionOnView(i, R.id.text)) - .check(matches(withText(containsString(measurements.get(i).getUrlString())))); + onData(anything()) + .inAdapterView(withId(R.id.recyclerView)) + .atPosition(i) + .onChildView(withId(R.id.text)) + .check(matches(withText(containsString(measurements.get(i).getUrlString())))); } } @@ -83,7 +84,7 @@ public void testSucceed() { // Act launchDetails(testResult.id); - onView(withId(R.id.recyclerView)).perform(actionOnItemAtPosition(0, click())); + onData(anything()).inAdapterView(withId(R.id.recyclerView)).atPosition(0).perform(click()); // Assert onView(withText(headerOutcome)).check(matches(isDisplayed())); @@ -99,7 +100,7 @@ public void testBlocked() { // Act launchDetails(testResult.id); - onView(withId(R.id.recyclerView)).perform(actionOnItemAtPosition(0, click())); + onData(anything()).inAdapterView(withId(R.id.recyclerView)).atPosition(0).perform(click()); // Assert onView(withText(headerOutcome)).check(matches(isDisplayed())); diff --git a/gradle.properties b/gradle.properties index a8c570082..71c004f05 100644 --- a/gradle.properties +++ b/gradle.properties @@ -11,7 +11,7 @@ android.enableJetifier=true android.nonFinalResIds=false android.nonTransitiveRClass=false android.useAndroidX=true -org.gradle.jvmargs=-Xmx1536m +org.gradle.jvmargs=-Xmx2048m # When configured, Gradle will run in incubating parallel mode. # This option should only be used with decoupled projects. More details, visit # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects From 6af25fde1c8fc407e6e52204d30b36c22dca0539 Mon Sep 17 00:00:00 2001 From: Norbel AMBANUMBEN Date: Thu, 25 Jan 2024 11:03:19 +0100 Subject: [PATCH 2/6] Releases 3.8.6 (#656) ## Proposed Changes - Measurement engine synced with OONI Probe CLI v3.20.0 - Bug fixes and Improvements --- app/build.gradle | 4 +- app/src/main/res/values-ar/strings.xml | 58 +++++++++++++------------- fastlane/changelog.sh | 5 ++- 3 files changed, 35 insertions(+), 32 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 80f7da894..7b3361f2c 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -13,8 +13,8 @@ android { applicationId 'org.openobservatory.ooniprobe' minSdk libs.versions.minSdk.get().toInteger() targetSdk libs.versions.targetSdk.get().toInteger() - versionName '3.8.5.1' - versionCode 108 + versionName '3.8.6' + versionCode 110 testInstrumentationRunner "org.openobservatory.ooniprobe.TestAndroidJUnitRunner" buildConfigField 'String', 'OONI_API_BASE_URL', '"https://api.ooni.io/"' buildConfigField 'String', 'NOTIFICATION_SERVER', '"https://countly.ooni.io"' diff --git a/app/src/main/res/values-ar/strings.xml b/app/src/main/res/values-ar/strings.xml index 997322e49..14de69c06 100644 --- a/app/src/main/res/values-ar/strings.xml +++ b/app/src/main/res/values-ar/strings.xml @@ -23,8 +23,8 @@ كل مرّّة أدير فيها OONI Probe، سيتم نشر بيانات الشبكة التي أجمعها أوتوماتيكيّاً. تحذير للزّيادة من شفافيّة الرّقابة على الانترنت، يتم النشر الأوتوماتيكي لجميع بيانات الشبكة التابعة لمستخدمي OONI Probe (إلا إذا اختاروا عدم المشاركة في ذلك من خلال الإعدادات). - الاختبارات الأوتوماتيكيّة - To measure internet censorship every day, please enable automated testing so that OONI Probe can run tests periodically.\n\nDon\'t worry, we\'ll be mindful of battery usage. \n\nYou can disable automated testing from the settings at any time. + الاختبارات التلقائية + لقياس الرقابة على الانترنت كل يوم، يُرجى تفعيل الاختبار التلقائي لكي يتمكن OONI Probe من تشغيل الاختبارات بشكل دوري.\n\nلا تقلق، سوف نضع في الحسبان استهلاك طاقة البطارية.\n\nيمكنك تعطيل الاختبار التلقائي انطلاقا من الإعدادات في أي وقت. التبليغ عن التعطيلات لتحسين خدمات OONI Probe نود تجميع تقارير حول تعطيلات في عمل التطبيق بمجهوليّة.\n\nهل تودّون بالمشاركة في إرسال التبليغات عن التعطيلات لفريق مطوّري OONI؟ ‮نعم @@ -54,7 +54,7 @@ أغلِق السجل إيقاف الاختبار جار ... يرجى الانتظار حتى الانتهاء من الاختبارات الجارية حاليّاً - Proxy in use + الوسيط يعمل انقر البطاقة للمزيد ~%1$sث اختبار حجب المواقع @@ -71,8 +71,8 @@ تحققوا من حجب [Psiphon](https://ooni.io/nettest/psiphon/) و [تور](https://ooni.io/nettest/tor/).\n\nستنشر نتائج اختبارك على [متصفح OONI](https://explorer.ooni.org/) و على [OONI API](https://api.ooni.io/). إجراء الاختبارات التجريبيّة الجديدة يمكنكم إجراء بعض الاختبارات التجريبيّة الجديدة المطوّرة من قبل فريق OONI: \n%1$s\n\nسيتم نشر نتائج اختباراتكم على كل من [مكتشف OONI](https://explorer.ooni.org) و [واجهة تطبيق OONI](https://api.ooni.io/) - The following tests will only be run as part of automated testing: - Disabled Tests + سوف تشتغل الاختبارات التالية فقط كجزء من الاختبارات التلقائية : + الاختبارات المُعطَّلة غيغابت/ثا ميغابت/ثا كيلوبت/ثا @@ -164,7 +164,7 @@ فشل باستطاعتك إجراء هذا الاختبار من جديد الرجاء المحاولة مجددًا - Learn how this test works [here](%1$s). + التعرف على كيفية عمل هذا الاختبار [هنا]([رابط]). مُتاح %1$s مُتاح. محجوب على الأرجح @@ -269,19 +269,19 @@ التغذية موافقة إلغاء - No, don\'t ask again + كلا، لا تسألني مرة أخرى حذف خطأ حاول مجددا رائع ! لا، شكراً في وقت آخر - Run anyways - Disable VPN - Always Run + شغِّل في كل الأحوال + تعطيل VPN + شغِّل دائما التطبيق غير قادر على إجراء الاختبار. من فضلك تحقق من اتصال الإنترنت. التطبيق غير قادر على تحميل قائمة المواقع. من فضلك حاول مرة أخرى. - Please wait for the current running tests to finish, before starting a new test. + يُرجى انتظار الاختبارات الحالية حتى تنتهي، قبل بدء اختبار جديد. التطبيق بحاجة الى تصريح التنبيهات. من فضلك قم بإعطاء التطبيق تصريح التنبيهات من خلال إعدادات هاتفك ثم اسمح بها في تطبيق OONI Probe. اذهب إلى الإعدادات هذه الواجهة مغلقة لحين الإنتهاء من الاختبار. @@ -290,11 +290,11 @@ بعض نتائج الاختبارات الخاصة بك لم يتمّ رفعها إلى خوادم OONI. من فضلك قم برفعها اذا كنت ترغب بالمساهمة في تجميع بيانات OONI. رفع جارٍ تحميل %1$s ... - OONI Probe cannot run automatically without battery optimization. Do you want to try again? - Please disable your VPN connection. - If you run OONI Probe with a VPN enabled, the test results may appear to come from the wrong country. Please disable your VPN connection. - Some measurements were taken over VPN. - If you upload measurements taken when VPN enabled, the test results may appear to come from the wrong country. + لا يمكن لـ OONI Probe أن يعمل تلقائيا بدون تحسينات البطارية. هل ترغب في المحاولة مرة أخرى ؟ + يُرجى تعطيل اتصالك عبر VPN. + إذا قمت بتشغيل OONI Probe عبر VPN مُفعَّل، سوف يظهر مصدر نتائج القياسات من دولة غير صحيحة. يُرجى تعطيل اتصالك عبر VPN. + لقد تم أخذ بعض القياسات عبر VPN. + إذا قمت برفع القياسات عبر اتصال VPN مُفعَّل، سوف يظهر مصدر نتائج القياسات من دولة غير صحيحة. تم التحميل بنجاح عرض سجل الإخفاق احصل.ي على تحديثات حول الرّقابة على الانترنت @@ -320,9 +320,9 @@ JSON فارغ هل تود.ين إيقاف هذا الاختبار؟ هذا سيعطّل الاختبار الجاري فوراً - هل تودّون إجراء الاختبارات أوتوماتكياً؟ - By enabling automated testing, you will contribute OONI measurements on a regular basis. - Please allow the app to run in the background. + هل تودّون إجراء الاختبارات تلقائيا؟ + عند تفعيل الاختبار التلقائي، سوف تساهم بقياسات OONI بشكل منتظم. + يُرجى السماح بتشغيل التطبيق في الخلفية. ذكرني لاحقا نسخ إلى الحافظة لم يتمّ الرفع @@ -359,13 +359,13 @@ ‮مُفعّل إجراء تنبيه عند إنتهاء الاختبار شريط الأخبار - الاختبارات الأوتوماتيكيّة - إجراء الاختبارات أوتوماتيكياً + الاختبارات التلقائية + إجراء الاختبارات تلقائيا عدد الاختبارات الأوتوماتيكية : %1$s. آخر اختبار أوتوماتيكي %1$s. فقط عند الاتصال عبر الواي الفاي فقط عند الشحن - By enabling automatic testing, OONI Probe tests will run automatically multiple times per day. Your test results will automatically get published on OONI Explorer: https://explorer.ooni.org/ \n\nImportant: If you have a VPN enabled, OONI Probe will not run tests automatically. Please turn off your VPN for automated OONI Probe testing. Learn more: https://ooni.org/support/faq/#can-i-run-ooni-probe-over-a-vpn + عند تفعيل الاختبار التلقائي، سيشغل OONI Probe الاختبارات تلقائيا عدة مرات في اليوم. سوف ينشر OONI Explorer نتائج اختباراتك تلقائيا : https://explorer.ooni.org/\n\nهام : إذا كان عندك VPN مُفعَّل، فلن يشغل OONI Probe الاختبارات تلقائيا. يُرجى إيقاف اتصالك بـ VPN لتمكين OONI Probe للقيام بالاختبار التلقائي. للتعرف على المزيد : https://ooni.org/support/faq/#can-i-run-ooni-probe-over-a-vpn المشاركة نشر النتائج أوتوماتيكيّاً رفع النتائج يدويّاً @@ -376,16 +376,16 @@ هذه المعلومات (مثلا IT لايطاليا) مطلوبة لتحدبد الدولة التي تم اجراء القياسات فيها. هل أنت متأكد أنك تريد الغاء هذا الإختيار؟ قيامك بنشر النتائج سيرفع من الشفافية حول التدخل في الشبكة ويدعم مجتمع OONI. \n\nمعلومات الشبكة (أي رقم النظام التلقائي ASN) مطلوب لتحديد مزودي خدمات الانترنت. اختيارات الاختبار - What you configure through the above test settings (e.g. disabling the WhatsApp test) will apply to tests run manually, as well as to tests run automatically (when automated testing is enabled). - Long running test - Run long running tests in foreground? + سوف تُطبَّق إعدادات الاختبار أعلاه على الاختبارات التي سوف يتم تشغيلها يدويا عندما تقوم بتهيئة تلك الإعدادات، كما ستُطبَّق أيضا على الاختبارات التي يتم تشغيلها تلقائيا (عند تفعيل الاختبار التلقائي). + اشتغال الاختبار الطويل + إطلاق اشتغال الاختبارات الطويلة في الواجهة ؟ الخصوصية أرسل تقارير حول تعطل التطبيق متقدم الثيمة المعتمة أظهر سجلات التصحيح - See recent logs - Language Setting + الاطلاع على السجلات الأخيرة + إعدادات اللغة اختر اللغة دائمًا استخدم وسيط في اتصال النطاق Domain Fronting بروكسي في خلفيّة OONI @@ -420,7 +420,7 @@ لم يتم إدخال أي روابط ابدأ أضف موقعًا - Load from template + تحميل من قالب عدد المواقع المختبرة (0 يعني الكل) اختبر واتساب اختبر تيليجرام @@ -439,7 +439,7 @@ إختبار Psiphon إختبار Tor اختبار RiseupVPN - Warn when VPN is in use + نبِّه عند استخدام VPN إرسال إيميل لفريق الدعم يرجى وصف المشكلة التي تواجهونها: من فضلك أرسل رسالة إلكترونية إلى bugs@openobservatory.org تحتوى على معلومات عن اصدار البرنامج وإصدار iOS. اضغط على \"نسخ إلى الحافظة\" فى الأسفل لنسخ عنوان بريدنا الإلكتروني. diff --git a/fastlane/changelog.sh b/fastlane/changelog.sh index 47602af25..96743ee5e 100755 --- a/fastlane/changelog.sh +++ b/fastlane/changelog.sh @@ -1,2 +1,5 @@ #usage ./changelog.sh 69 "Release notes" -for i in $(ls metadata/android); do echo "$2" > metadata/android/$i/changelogs/$1.txt; done +for i in $(ls metadata/android); do + mkdir -p metadata/android/$i/changelogs + echo "$2" > metadata/android/$i/changelogs/$1.txt +done From b432281e1c5c8f7283cc44c819ff1e494e450f9c Mon Sep 17 00:00:00 2001 From: Norbel AMBANUMBEN Date: Fri, 26 Jan 2024 16:47:09 +0100 Subject: [PATCH 3/6] fix: OONI cards not enabled by default. (#654) Fixes OONI cards not enabled by default. ## Proposed Changes - Set default value for ooni tests to enabled. --- .../ooniprobe/common/OONIDescriptor.kt | 19 +++++++++++++------ .../common/PreferenceManagerExtension.kt | 15 ++++++++++++++- 2 files changed, 27 insertions(+), 7 deletions(-) diff --git a/app/src/main/java/org/openobservatory/ooniprobe/common/OONIDescriptor.kt b/app/src/main/java/org/openobservatory/ooniprobe/common/OONIDescriptor.kt index 71c3749cc..f31c477e0 100644 --- a/app/src/main/java/org/openobservatory/ooniprobe/common/OONIDescriptor.kt +++ b/app/src/main/java/org/openobservatory/ooniprobe/common/OONIDescriptor.kt @@ -30,17 +30,24 @@ open class OONIDescriptor( ) : Serializable, BaseDescriptor(name = name, nettests = nettests) { /** - * Checks if any of the nettests are enabled based on the preferences stored in the provided [PreferenceManager]. + * This function is used to determine if the current descriptor is enabled. + * If the descriptor is [OONITests.EXPERIMENTAL], this function returns the preference value stored for the `experimental` preference key. + * If the descriptor is [OONITests.WEBSITES], this function returns true if at least one category is enabled in the preferences. + * Otherwise, it checks if any of the nettests are enabled based on the preferences stored in the provided [PreferenceManager]. * * @param preferenceManager The [PreferenceManager] instance used to resolve the status of each nettest. * @return Boolean Returns true if at least one nettest is enabled, false otherwise. */ fun isEnabled(preferenceManager: PreferenceManager): Boolean { - return nettests.any { - preferenceManager.resolveStatus( - name = it.name, - prefix = preferencePrefix(), - ) + return when (name) { + OONITests.EXPERIMENTAL.label -> preferenceManager.isExperimentalOn + OONITests.WEBSITES.label -> preferenceManager.countEnabledCategory() > 0 + else -> nettests.any { + preferenceManager.resolveStatus( + name = it.name, + prefix = preferencePrefix(), + ) + } } } diff --git a/app/src/main/java/org/openobservatory/ooniprobe/common/PreferenceManagerExtension.kt b/app/src/main/java/org/openobservatory/ooniprobe/common/PreferenceManagerExtension.kt index 8ee61072e..56965594c 100644 --- a/app/src/main/java/org/openobservatory/ooniprobe/common/PreferenceManagerExtension.kt +++ b/app/src/main/java/org/openobservatory/ooniprobe/common/PreferenceManagerExtension.kt @@ -34,7 +34,20 @@ fun PreferenceManager.resolveStatus( resolveStatus(name = name, prefix = prefix) ) } else { - return sp.getBoolean(getPreferenceKey(name = name, prefix = prefix), false) + /** + * If the preference key does not exist, we return the default value for the test. + * This is needed because ooni provided tests will not be explicitly enabled by default. + * + * However, we want to show them as enabled in the UI. + * + * Using the **[OONIDescriptor.preferencePrefix]** as an identifier, we can determine if the test is an ooni test(prefix is blank) + * and set the default value accordingly. + * + * The prefix is blank for ooni tests because they do not have a descriptor id. + * Additionally, for backward compatibility, the preference key for ooni tests + * is a lookup from **[PreferenceManager.getPreferenceKey]** with the test name. + */ + return sp.getBoolean(getPreferenceKey(name = name, prefix = prefix), prefix.isBlank()) } } From 56589224267241b2c9fc0bd3fc1fad8955e1ae42 Mon Sep 17 00:00:00 2001 From: Norbel AMBANUMBEN Date: Fri, 9 Feb 2024 14:31:44 +0100 Subject: [PATCH 4/6] Feat: Adds possibility to use PR number as version code (#652) Co-authored-by: Simone Basso --- .../workflows/firebase-app-distribution.yml | 21 +++++++++++++++---- app/build.gradle | 20 +++++++++++++----- 2 files changed, 32 insertions(+), 9 deletions(-) diff --git a/.github/workflows/firebase-app-distribution.yml b/.github/workflows/firebase-app-distribution.yml index 2090d3f74..c55cc486c 100644 --- a/.github/workflows/firebase-app-distribution.yml +++ b/.github/workflows/firebase-app-distribution.yml @@ -9,17 +9,30 @@ jobs: steps: - uses: actions/checkout@v4 - - name: set up JDK 17 + - name: Resolve PR number + uses: jwalton/gh-find-current-pr@v1 + id: findPr + with: + # Can be "open", "closed", or "all". Defaults to "open". + state: open + - name: Set up JDK 17 uses: actions/setup-java@v4 with: java-version: '17' distribution: 'temurin' - - name: build release + - name: Build `DevFullDebug` variant + if: success() && steps.findPr.outputs.number run: ./gradlew clean assembleDevFullDebug - - name: upload artifact to Firebase App Distribution + env: + PR_NUMBER: ${{ steps.findPr.outputs.pr }} + - name: Upload artifact to Firebase App Distribution uses: wzieba/Firebase-Distribution-Github-Action@v1.7.0 + id: uploadArtifact with: appId: ${{secrets.FIREBASE_APP_ID}} serviceCredentialsFileContent: ${{ secrets.CREDENTIAL_FILE_CONTENT }} groups: testers - file: app/build/outputs/apk/devFull/debug/app-dev-full-debug.apk \ No newline at end of file + file: app/build/outputs/apk/devFull/debug/app-dev-full-debug.apk + - name: Write Summary + run: | + echo "View this release in the Firebase console: ${{ steps.uploadArtifact.outputs.FIREBASE_CONSOLE_URI }}" >> $GITHUB_STEP_SUMMARY \ No newline at end of file diff --git a/app/build.gradle b/app/build.gradle index 7b3361f2c..42cbec3f3 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -55,8 +55,8 @@ android { dev { dimension 'testing' applicationIdSuffix '.dev' - versionNameSuffix "-beta.1" - versionCode versionCodeDate() + versionNameSuffix resolveVersionSuffix('beta') + versionCode resolveVersionCode() buildConfigField 'String', 'BASE_SOFTWARE_NAME', '"ooniprobe-android-dev"' resValue "string", "APP_ID", 'org.openobservatory.ooniprobe.dev' resValue "string", "APP_NAME", "OONI Dev" @@ -65,8 +65,8 @@ android { experimental { dimension 'testing' applicationIdSuffix '.experimental' - versionNameSuffix "-experimental.1" - versionCode versionCodeDate() + versionNameSuffix resolveVersionSuffix('experimental') + versionCode resolveVersionCode() buildConfigField 'String', 'BASE_SOFTWARE_NAME', '"ooniprobe-android-experimental"' resValue "string", "APP_ID", 'org.openobservatory.ooniprobe.experimental' resValue "string", "APP_NAME", "OONI Exp" @@ -173,10 +173,20 @@ dependencies { kaptAndroidTest libs.google.dagger.compiler } -static def versionCodeDate() { +static def resolveVersionCode() { + if(System.getenv("PR_NUMBER") != null) { + return System.getenv("PR_NUMBER").toInteger() + } return new Date().format("yyyyMMdd").toInteger() } +static def resolveVersionSuffix(String variant) { + if(System.getenv("PR_NUMBER") != null){ + return "-${variant}.${System.getenv("PR_NUMBER")}" + } + return "-${variant}.1" +} + if (!getGradle().getStartParameter().getTaskRequests() .toString().contains("Fdroid")){ apply plugin: 'com.google.gms.google-services' From 1658f9791a815997cd0b2942aace8e5a9c5b197d Mon Sep 17 00:00:00 2001 From: Norbel AMBANUMBEN Date: Fri, 9 Feb 2024 14:36:40 +0100 Subject: [PATCH 5/6] chore: Update android gradle plugin, preference, lifecycle process, gson, material and firebase dependencies (#658) ## Proposed Changes - Update android gradle plugin, preference, lifecycle process, gson, material and firebase dependencies. --- app/build.gradle | 2 +- gradle/libs.versions.toml | 17 ++++++++--------- gradle/wrapper/gradle-wrapper.properties | 2 +- shared-test/build.gradle | 2 +- 4 files changed, 11 insertions(+), 12 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 42cbec3f3..46fcba036 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -139,7 +139,7 @@ dependencies { fullImplementation libs.google.play.core // Dependency Injection - implementation libs.google.dagger + implementation libs.google.dagger.lib kapt libs.google.dagger.compiler // Logger diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 5f935b989..9ed68b3c1 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,5 +1,5 @@ [versions] -androidGradlePlugin = "8.1.2" +androidGradlePlugin = "8.2.2" barista = "3.9.0" countlySdk = "23.6.0" faker = "1.2.8" @@ -18,19 +18,19 @@ androidxCore = "1.5.0" androidxRunner = "1.5.2" androidxAppCompat = "1.6.1" androidxConstraintlayout = "2.1.4" -androidxLifecycleProcess = "2.5.1" -androidxPreference = "1.2.0" +androidxLifecycleProcess = "2.7.0" +androidxPreference = "1.2.1" androidxLocalbroadcastmanager = "1.1.0" androidxLegacySupportV4 = "1.0.0" androidxJunit = "1.1.5" androidxEspressoCore = "3.5.1" # Google -googleGson = "2.8.9" +googleGson = "2.10" googleGuava = "30.1.1-android" -googleMaterial = "1.6.1" +googleMaterial = "1.11.0" googleDagger = "2.45" -googleFirebaseBon = "26.3.0" +googleFirebaseBon = "32.7.1" googlePlaycore = "1.10.3" # OONI @@ -46,11 +46,10 @@ junit = "4.13.2" dbflow = "4.2.4" retrofitCore = "2.9.0" retrofitLoggingInterceptor = "4.9.1" -butterknifeCore = "10.2.3" # Firebase Services # https://firebase.google.com/support/release-notes/android -gms-googleServices = "4.3.15" +gms-googleServices = "4.4.0" [libraries] # Dependencies of the included build-logic @@ -97,7 +96,7 @@ google-gson = { module = "com.google.code.gson:gson", version.ref = "googleGson" google-guava = { module = "com.google.guava:guava", version.ref = "googleGuava" } google-material = { module = "com.google.android.material:material", version.ref = "googleMaterial" } google-dagger-compiler = { module = "com.google.dagger:dagger-compiler", version.ref = "googleDagger" } -google-dagger = { module = "com.google.dagger:dagger", version.ref = "googleDagger" } +google-dagger-lib = { module = "com.google.dagger:dagger", version.ref = "googleDagger" } google-firebase-messaging = { module = "com.google.firebase:firebase-messaging"} google-firebase-bom = { module = "com.google.firebase:firebase-bom", version.ref = "googleFirebaseBon" } diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 3a0290794..b93c46a5f 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.0-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.2-all.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/shared-test/build.gradle b/shared-test/build.gradle index 34d70a978..ed614df70 100644 --- a/shared-test/build.gradle +++ b/shared-test/build.gradle @@ -44,7 +44,7 @@ dependencies { implementation project(":app") // Dependency Injection (https://dagger.dev/) - implementation libs.google.dagger + implementation libs.google.dagger.lib kapt libs.google.dagger.compiler // Database Library (https://github.com/agrosner/DBFlow) From 14930469d5b30937259160f0cb97686b6c096f49 Mon Sep 17 00:00:00 2001 From: Norbel AMBANUMBEN Date: Sun, 11 Feb 2024 10:47:15 +0100 Subject: [PATCH 6/6] fix: https://github.com/ooni/probe/issues/2659#issuecomment-1912270537 (#660) Fixes https://github.com/ooni/probe/issues/2659#issuecomment-1912270537 ## Proposed Changes - Update `ReadMorePlugin` to take a `maxLength` parameter with default value `150`. - Update `OverviewActivity` to allow modification of all automated update items. - Update `RunTestsActivity` to save preferences only when the tests are run. --- .../ooniprobe/activity/OverviewActivity.java | 23 ++++-- .../OverviewTestsExpandableListViewAdapter.kt | 16 +++- .../activity/overview/OverviewViewModel.kt | 48 +++++++++--- .../activity/runtests/RunTestsActivity.kt | 29 +++++++ .../activity/runtests/RunTestsViewModel.kt | 35 --------- .../RunTestsExpandableListViewAdapter.kt | 13 ---- .../ooniprobe/common/OONIDescriptor.kt | 76 ++++++++++++++++++- .../common/PreferenceManagerExtension.kt | 21 +++-- .../ooniprobe/common/ReadMorePlugin.kt | 20 ++++- .../ooniprobe/common/service/ServiceUtil.java | 18 +---- .../ooniprobe/fragment/DashboardFragment.kt | 12 ++- 11 files changed, 214 insertions(+), 97 deletions(-) diff --git a/app/src/main/java/org/openobservatory/ooniprobe/activity/OverviewActivity.java b/app/src/main/java/org/openobservatory/ooniprobe/activity/OverviewActivity.java index 1ed65ca77..a71c368f8 100644 --- a/app/src/main/java/org/openobservatory/ooniprobe/activity/OverviewActivity.java +++ b/app/src/main/java/org/openobservatory/ooniprobe/activity/OverviewActivity.java @@ -3,6 +3,7 @@ import static org.openobservatory.ooniprobe.activity.overview.OverviewViewModel.SELECT_ALL; import static org.openobservatory.ooniprobe.activity.overview.OverviewViewModel.SELECT_NONE; import static org.openobservatory.ooniprobe.activity.overview.OverviewViewModel.SELECT_SOME; +import static org.openobservatory.ooniprobe.common.PreferenceManagerExtensionKt.resolveStatus; import android.content.Context; import android.content.Intent; @@ -32,6 +33,7 @@ import org.openobservatory.ooniprobe.model.database.Result; import java.util.Locale; +import java.util.Objects; import javax.inject.Inject; @@ -70,8 +72,10 @@ protected void onCreate(@Nullable Bundle savedInstanceState) { setThemeColor(ContextCompat.getColor(this, descriptor.getColor())); binding.icon.setImageResource(descriptor.getDisplayIcon(this)); binding.customUrl.setVisibility(descriptor.getName().equals(OONITests.WEBSITES.getLabel()) ? View.VISIBLE : View.GONE); - Markwon markwon = Markwon.builder(this).usePlugin(new ReadMorePlugin(getString(R.string.OONIRun_ReadMore), getString(R.string.OONIRun_ReadLess))).build(); - if (descriptor.getName().equals(OONITests.EXPERIMENTAL.name())) { + Markwon markwon = Markwon.builder(this) + .usePlugin(new ReadMorePlugin(getString(R.string.OONIRun_ReadMore), getString(R.string.OONIRun_ReadLess), 400)) + .build(); + if (Objects.equals(descriptor.getName(), OONITests.EXPERIMENTAL.name())) { markwon.setMarkdown(binding.desc, descriptor.getDescription()); if (TextUtilsCompat.getLayoutDirectionFromLocale(Locale.getDefault()) == ViewCompat.LAYOUT_DIRECTION_RTL) { binding.desc.setTextDirection(View.TEXT_DIRECTION_RTL); @@ -107,14 +111,17 @@ protected void onCreate(@Nullable Bundle savedInstanceState) { } }); - if (adapter.isSelectedAllItems()) { - binding.switchTests.setCheckedState(MaterialCheckBox.STATE_CHECKED); - } else if (adapter.isNotSelectedAnyGroupItem()) { - binding.switchTests.setCheckedState(MaterialCheckBox.STATE_UNCHECKED); + if (descriptor.getName().equals(OONITests.EXPERIMENTAL.getLabel())) { + binding.switchTests.setChecked(resolveStatus(preferenceManager, descriptor.getName(), descriptor.preferencePrefix(), true)); } else { - binding.switchTests.setCheckedState(MaterialCheckBox.STATE_INDETERMINATE); + if (adapter.isSelectedAllItems()) { + binding.switchTests.setCheckedState(MaterialCheckBox.STATE_CHECKED); + } else if (adapter.isNotSelectedAnyGroupItem()) { + binding.switchTests.setCheckedState(MaterialCheckBox.STATE_UNCHECKED); + } else { + binding.switchTests.setCheckedState(MaterialCheckBox.STATE_INDETERMINATE); + } } - binding.switchTests.setEnabled(descriptor.hasPreferencePrefix()); // Expand all groups for (int i = 0; i < adapter.getGroupCount(); i++) { binding.expandableListView.expandGroup(i); diff --git a/app/src/main/java/org/openobservatory/ooniprobe/activity/overview/OverviewTestsExpandableListViewAdapter.kt b/app/src/main/java/org/openobservatory/ooniprobe/activity/overview/OverviewTestsExpandableListViewAdapter.kt index 9346b1da6..04ff44aea 100644 --- a/app/src/main/java/org/openobservatory/ooniprobe/activity/overview/OverviewTestsExpandableListViewAdapter.kt +++ b/app/src/main/java/org/openobservatory/ooniprobe/activity/overview/OverviewTestsExpandableListViewAdapter.kt @@ -45,6 +45,7 @@ class OverviewTestsExpandableListViewAdapter( when (viewModel.descriptor.value?.name) { OONITests.EXPERIMENTAL.label -> { view.findViewById(R.id.group_name).text = groupItem.name + view.findViewById(R.id.group_icon).visibility = View.GONE } else -> { @@ -96,12 +97,21 @@ class OverviewTestsExpandableListViewAdapter( } groupCheckBox.isChecked = groupItem.selected - // Disable experimental or webconnectivity test + + /** + * Hide checkbox for experimental tests. + * Experimental tests are not configurable in the settings. + * Tests in this category are not permanent and are subject to change. + * The checkbox is hidden to prevent the user from mistakenly thinking they can be configured. + */ viewModel.descriptor.value?.run { - groupCheckBox.isEnabled = hasPreferencePrefix() + if (name == OONITests.EXPERIMENTAL.label) { + groupCheckBox.visibility = View.GONE + } else { + groupCheckBox.visibility = View.VISIBLE + } } - if (groupItem.inputs?.isNotEmpty() == true) { if (isExpanded) { groupIndicator.setImageResource(R.drawable.expand_less) diff --git a/app/src/main/java/org/openobservatory/ooniprobe/activity/overview/OverviewViewModel.kt b/app/src/main/java/org/openobservatory/ooniprobe/activity/overview/OverviewViewModel.kt index 9384e100a..7725b0496 100644 --- a/app/src/main/java/org/openobservatory/ooniprobe/activity/overview/OverviewViewModel.kt +++ b/app/src/main/java/org/openobservatory/ooniprobe/activity/overview/OverviewViewModel.kt @@ -3,8 +3,8 @@ package org.openobservatory.ooniprobe.activity.overview import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel import org.openobservatory.engine.BaseNettest -import org.openobservatory.ooniprobe.activity.runtests.RunTestsViewModel import org.openobservatory.ooniprobe.common.OONIDescriptor +import org.openobservatory.ooniprobe.common.OONITests import org.openobservatory.ooniprobe.common.PreferenceManager import org.openobservatory.ooniprobe.common.disableTest import org.openobservatory.ooniprobe.common.enableTest @@ -28,22 +28,50 @@ class OverviewViewModel() : ViewModel() { val selectedAllBtnStatus: MutableLiveData = MutableLiveData() init { - selectedAllBtnStatus.postValue(RunTestsViewModel.SELECT_SOME) + selectedAllBtnStatus.postValue(SELECT_SOME) } + + /** + * Set the status of the selected all button. + * This method will also update the preference of the tests based on the selected status. + * For experimental tests, a single button is used to enable/disable all tests. + * @param selectedStatus the status of the selected all button + */ fun setSelectedAllBtnStatus(selectedStatus: String) { selectedAllBtnStatus.postValue(selectedStatus) - when (selectedStatus) { - SELECT_ALL -> { - descriptor.value?.nettests?.forEach { - enableTest(it.name) + descriptor.value?.let { desc -> + + when (selectedStatus) { + SELECT_ALL -> { + when (desc.name) { + OONITests.EXPERIMENTAL.label -> { + enableTest(name = desc.name) + } + + else -> { + descriptor.value?.nettests?.forEach { + enableTest(name = it.name) + } + } + } } - } - SELECT_NONE -> { - descriptor.value?.nettests?.forEach { - disableTest(it.name) + SELECT_NONE -> { + when (desc.name) { + OONITests.EXPERIMENTAL.label -> { + disableTest(name = desc.name) + } + + else -> { + descriptor.value?.nettests?.forEach { + disableTest(name = it.name) + } + } + } } + + else -> {} } } } diff --git a/app/src/main/java/org/openobservatory/ooniprobe/activity/runtests/RunTestsActivity.kt b/app/src/main/java/org/openobservatory/ooniprobe/activity/runtests/RunTestsActivity.kt index ad2c69c40..fc40b4733 100644 --- a/app/src/main/java/org/openobservatory/ooniprobe/activity/runtests/RunTestsActivity.kt +++ b/app/src/main/java/org/openobservatory/ooniprobe/activity/runtests/RunTestsActivity.kt @@ -18,7 +18,10 @@ import org.openobservatory.ooniprobe.activity.runtests.adapter.RunTestsExpandabl import org.openobservatory.ooniprobe.activity.runtests.models.ChildItem import org.openobservatory.ooniprobe.activity.runtests.models.GroupItem import org.openobservatory.ooniprobe.common.OONIDescriptor +import org.openobservatory.ooniprobe.common.OONITests import org.openobservatory.ooniprobe.common.PreferenceManager +import org.openobservatory.ooniprobe.common.enableTest +import org.openobservatory.ooniprobe.common.disableTest import org.openobservatory.ooniprobe.databinding.ActivityRunTestsBinding import java.io.Serializable import javax.inject.Inject @@ -104,6 +107,7 @@ class RunTestsActivity : AbstractActivity() { private fun onMenuItemClickListener(menuItem: MenuItem): Boolean { return when (menuItem.itemId) { R.id.runButton -> { + updatePreferences() val selectedChildItems: List = getChildItemsSelectedIdList() if (selectedChildItems.isNotEmpty()) { val testSuitesToRun = getGroupItemsAtLeastOneChildEnabled().map { groupItem -> @@ -124,6 +128,31 @@ class RunTestsActivity : AbstractActivity() { } } + /** + * Update the preferences based on the selected tests. + * This method is used to update the preferences when the user has selected the tests that they want to run. + */ + private fun updatePreferences() { + for (i in 0 until adapter.groupCount) { + val group = adapter.getGroup(i) + when (group.name) { + OONITests.EXPERIMENTAL.label -> { + val testNames = OONITests.EXPERIMENTAL.nettests.map { it.name }; + when(group.nettests.filter { testNames.contains(it.name) }.map { it.selected }.all { it }) { + true -> preferenceManager.enableTest(OONITests.EXPERIMENTAL.label) + false -> preferenceManager.disableTest(OONITests.EXPERIMENTAL.label) + } + } + else -> group.nettests.forEach { nettest -> + when(nettest.selected) { + true -> preferenceManager.enableTest(nettest.name) + false -> preferenceManager.disableTest(nettest.name) + } + } + } + } + } + private fun selectAllBtnStatusObserver(selectAllBtnStatus: String?) { if (!TextUtils.isEmpty(selectAllBtnStatus)) { when (selectAllBtnStatus) { diff --git a/app/src/main/java/org/openobservatory/ooniprobe/activity/runtests/RunTestsViewModel.kt b/app/src/main/java/org/openobservatory/ooniprobe/activity/runtests/RunTestsViewModel.kt index 61ec6df97..ffb1d95f3 100644 --- a/app/src/main/java/org/openobservatory/ooniprobe/activity/runtests/RunTestsViewModel.kt +++ b/app/src/main/java/org/openobservatory/ooniprobe/activity/runtests/RunTestsViewModel.kt @@ -25,41 +25,6 @@ class RunTestsViewModel() : ViewModel() { fun setSelectedAllBtnStatus(selectedStatus: String) { selectedAllBtnStatus.postValue(selectedStatus) - when (selectedStatus) { - SELECT_ALL -> { - OONITests.INSTANT_MESSAGING.nettests.forEach { - enableTest(it.name) - } - OONITests.CIRCUMVENTION.nettests.forEach { - enableTest(it.name) - } - OONITests.PERFORMANCE.nettests.forEach { - enableTest(it.name) - } - enableTest(OONITests.EXPERIMENTAL.label) - } - - SELECT_NONE -> { - OONITests.INSTANT_MESSAGING.nettests.forEach { - disableTest(it.name) - } - OONITests.CIRCUMVENTION.nettests.forEach { - disableTest(it.name) - } - OONITests.PERFORMANCE.nettests.forEach { - disableTest(it.name) - } - disableTest(OONITests.EXPERIMENTAL.label) - } - } - } - - fun disableTest(name: String) { - preferenceManager.disableTest(name) - } - - fun enableTest(name: String) { - preferenceManager.enableTest(name) } companion object { diff --git a/app/src/main/java/org/openobservatory/ooniprobe/activity/runtests/adapter/RunTestsExpandableListViewAdapter.kt b/app/src/main/java/org/openobservatory/ooniprobe/activity/runtests/adapter/RunTestsExpandableListViewAdapter.kt index 9291be328..aa690592e 100644 --- a/app/src/main/java/org/openobservatory/ooniprobe/activity/runtests/adapter/RunTestsExpandableListViewAdapter.kt +++ b/app/src/main/java/org/openobservatory/ooniprobe/activity/runtests/adapter/RunTestsExpandableListViewAdapter.kt @@ -103,22 +103,11 @@ class RunTestsExpandableListViewAdapter( if (groupItem.selected) { if (isSelectAllChildItems(groupItem.nettests)) { groupSelectionIndicator.setImageResource(R.drawable.check_box) - // NOTE: This is the only place where OONITests.EXPERIMENTAL.label is used. - // This doesn't follow the normal rule where the component tests make up the suite. - if (groupItem.name == OONITests.EXPERIMENTAL.label) { - viewModel.enableTest(OONITests.EXPERIMENTAL.label) - } } else { groupSelectionIndicator.setImageResource(R.drawable.check_box_outline_blank) - if (groupItem.name == OONITests.EXPERIMENTAL.label) { - viewModel.disableTest(OONITests.EXPERIMENTAL.label) - } } } else { groupSelectionIndicator.setImageResource(R.drawable.check_box_outline_blank) - if (groupItem.name == OONITests.EXPERIMENTAL.label) { - viewModel.disableTest(OONITests.EXPERIMENTAL.label) - } } groupSelectionIndicator.setOnClickListener { if (groupItem.selected && isSelectAllChildItems(groupItem.nettests)) { @@ -193,7 +182,6 @@ class RunTestsExpandableListViewAdapter( setOnClickListener { if (childItem.selected) { childItem.selected = false - viewModel.disableTest(childItem.name) if (isNotSelectedAnyChildItems(groupItem.nettests)) { groupItem.selected = false } @@ -204,7 +192,6 @@ class RunTestsExpandableListViewAdapter( } } else { childItem.selected = true - viewModel.enableTest(childItem.name) groupItem.selected = true if (isSelectedAllItems(groupedListData)) { viewModel.setSelectedAllBtnStatus(SELECT_ALL) diff --git a/app/src/main/java/org/openobservatory/ooniprobe/common/OONIDescriptor.kt b/app/src/main/java/org/openobservatory/ooniprobe/common/OONIDescriptor.kt index f31c477e0..b1f827636 100644 --- a/app/src/main/java/org/openobservatory/ooniprobe/common/OONIDescriptor.kt +++ b/app/src/main/java/org/openobservatory/ooniprobe/common/OONIDescriptor.kt @@ -70,7 +70,6 @@ open class OONIDescriptor( TestGroupItem( selected = when (name) { OONITests.EXPERIMENTAL.label -> preferenceManager.isExperimentalOn - OONITests.WEBSITES.label -> preferenceManager.countEnabledCategory() > 0 else -> preferenceManager.resolveStatus( name = it.name, prefix = preferencePrefix(), @@ -345,6 +344,81 @@ enum class OONITests( } } +fun autoRunTests(context: Context, preferenceManager: PreferenceManager): List { + + return ooniDescriptors(context).filter { ooniDescriptor -> + when (ooniDescriptor.name) { + OONITests.EXPERIMENTAL.label -> preferenceManager.resolveStatus( + name = ooniDescriptor.name, + prefix = ooniDescriptor.preferencePrefix(), + autoRun = true + ) + + else -> ooniDescriptor.nettests.any { nettest -> + preferenceManager.resolveStatus( + name = nettest.name, + prefix = ooniDescriptor.preferencePrefix(), + autoRun = true + ) + } + } + }.map { ooniDescriptor -> + when (ooniDescriptor.name) { + OONITests.EXPERIMENTAL.label -> DynamicTestSuite( + name = ooniDescriptor.name, + title = ooniDescriptor.title, + shortDescription = ooniDescriptor.shortDescription, + description = ooniDescriptor.description, + icon = ooniDescriptor.getDisplayIcon(context), + icon_24 = ooniDescriptor.getDisplayIcon(context), + color = ooniDescriptor.color, + animation = ooniDescriptor.animation, + dataUsage = ooniDescriptor.dataUsage, + nettest = (ooniDescriptor.nettests).run { + this + (ooniDescriptor.longRunningTests?.filter { nettest -> + preferenceManager.resolveStatus( + name = nettest.name, + prefix = ooniDescriptor.preferencePrefix(), + autoRun = true + ) + } ?: listOf()) + } + ).apply { + autoRun = true + } + + else -> DynamicTestSuite( + name = ooniDescriptor.name, + title = ooniDescriptor.title, + shortDescription = ooniDescriptor.shortDescription, + description = ooniDescriptor.description, + icon = ooniDescriptor.getDisplayIcon(context), + icon_24 = ooniDescriptor.getDisplayIcon(context), + color = ooniDescriptor.color, + animation = ooniDescriptor.animation, + dataUsage = ooniDescriptor.dataUsage, + nettest = (ooniDescriptor.nettests).filter { nettest -> + preferenceManager.resolveStatus( + name = nettest.name, + prefix = ooniDescriptor.preferencePrefix(), + autoRun = true + ) + }.run { + this + (ooniDescriptor.longRunningTests?.filter { nettest -> + preferenceManager.resolveStatus( + name = nettest.name, + prefix = ooniDescriptor.preferencePrefix(), + autoRun = true + ) + } ?: listOf()) + } + ).apply { + autoRun = true + } + } + } +} + /** * Creates a list of [OONIDescriptor] representing the OONI tests. * diff --git a/app/src/main/java/org/openobservatory/ooniprobe/common/PreferenceManagerExtension.kt b/app/src/main/java/org/openobservatory/ooniprobe/common/PreferenceManagerExtension.kt index 56965594c..97b728642 100644 --- a/app/src/main/java/org/openobservatory/ooniprobe/common/PreferenceManagerExtension.kt +++ b/app/src/main/java/org/openobservatory/ooniprobe/common/PreferenceManagerExtension.kt @@ -8,7 +8,7 @@ private fun PreferenceManager.experimentalTestList(): MutableList { val exclusionList = mutableListOf() OONITests.EXPERIMENTAL.run { exclusionList.addAll(nettests.map { it.name }) - exclusionList.addAll(nettests.map { it.name }) + longRunningTests?.map { it.name }?.let { exclusionList.addAll(it) } } return exclusionList @@ -23,11 +23,14 @@ private fun PreferenceManager.experimentalTestList(): MutableList { fun PreferenceManager.resolveStatus( name: String, prefix: String, autoRun: Boolean = false ): Boolean { - if (name == WebConnectivity.NAME) { - return true - } else if (experimentalTestList().contains(name)) { - return isExperimentalOn + if (!autoRun) { + if (name == WebConnectivity.NAME) { + return true + } else if (experimentalTestList().contains(name)) { + return isExperimentalOn + } } + val key = getPreferenceKey(name = name, prefix = prefix, autoRun = autoRun) return if (autoRun) { sp.getBoolean( getPreferenceKey(name = name, prefix = prefix, autoRun = autoRun), @@ -73,9 +76,11 @@ private fun PreferenceManager.setValue( prefix: String, autoRun: Boolean = false, ): Boolean { - if (name == WebConnectivity.NAME || experimentalTestList().contains(name)) { + if (experimentalTestList().contains(name) && !autoRun) { return false } + val key = getPreferenceKey(name = name, prefix = prefix, autoRun = autoRun) + return with(sp.edit()) { putBoolean(getPreferenceKey(name = name, prefix = prefix, autoRun = autoRun), value) commit() @@ -135,7 +140,7 @@ fun PreferenceManager.getPreferenceKey(name: String): String { OONITests.EXPERIMENTAL.label -> r.getString(R.string.experimental) - else -> throw IllegalArgumentException("Unknown preference for: $name") + else -> name } } @@ -174,7 +179,7 @@ fun PreferenceManager.disableTest(name: String): Boolean { * @return true if the preference was successfully set, false otherwise. */ private fun PreferenceManager.setValue(name: String, value: Boolean): Boolean { - if (name == WebConnectivity.NAME || experimentalTestList().contains(name)) { + if (experimentalTestList().contains(name)) { return false } return with(sp.edit()) { diff --git a/app/src/main/java/org/openobservatory/ooniprobe/common/ReadMorePlugin.kt b/app/src/main/java/org/openobservatory/ooniprobe/common/ReadMorePlugin.kt index 9cef24f39..13ab06ffa 100644 --- a/app/src/main/java/org/openobservatory/ooniprobe/common/ReadMorePlugin.kt +++ b/app/src/main/java/org/openobservatory/ooniprobe/common/ReadMorePlugin.kt @@ -13,9 +13,22 @@ import io.noties.markwon.AbstractMarkwonPlugin * Read more plugin based on text length. * @see ReadMorePluginSample */ -class ReadMorePlugin(private val labelMore: String, private val labelLess: String) : - AbstractMarkwonPlugin() { - private val maxLength = 150 +class ReadMorePlugin private constructor( + private val labelMore: String, + private val labelLess: String, + private val maxLength: Int +) : AbstractMarkwonPlugin() { + + constructor( + labelMore: String, + labelLess: String, + maxLength: Int? + ) : this(labelMore, labelLess, maxLength ?: 150) + + constructor( + labelMore: String, + labelLess: String, + ) : this(labelMore, labelLess, 150) override fun afterSetText(textView: TextView) { val text = textView.text @@ -76,6 +89,7 @@ class ReadMorePlugin(private val labelMore: String, private val labelLess: Strin } companion object { + private fun trim(builder: SpannableStringBuilder) { // NB! tables use `\u00a0` (non breaking space) which is not reported as white-space diff --git a/app/src/main/java/org/openobservatory/ooniprobe/common/service/ServiceUtil.java b/app/src/main/java/org/openobservatory/ooniprobe/common/service/ServiceUtil.java index 2e7f8f768..caa2157d7 100644 --- a/app/src/main/java/org/openobservatory/ooniprobe/common/service/ServiceUtil.java +++ b/app/src/main/java/org/openobservatory/ooniprobe/common/service/ServiceUtil.java @@ -1,5 +1,7 @@ package org.openobservatory.ooniprobe.common.service; +import static org.openobservatory.ooniprobe.common.OONIDescriptorKt.autoRunTests; + import android.app.job.JobInfo; import android.app.job.JobScheduler; import android.content.ComponentName; @@ -75,21 +77,7 @@ public static void startRunTestServiceUnattended(Application app) { if (!d.generateAutoRunServiceSuite.shouldStart(config.isOnWiFi(), config.isCharging(), isVPNInUse)) { return; } - - AbstractSuite suite = d.generateAutoRunServiceSuite.generate(); - ArrayList testSuites = new ArrayList<>(); - testSuites.add(suite); - testSuites.addAll( - Lists.transform( - List.of(OONITests.INSTANT_MESSAGING, OONITests.CIRCUMVENTION, OONITests.PERFORMANCE, OONITests.EXPERIMENTAL), - item -> { - DynamicTestSuite testSuite = item.toOONIDescriptor(app).getTest(app); - testSuite.setAutoRun(true); - return testSuite; - } - ) - ); - ServiceUtil.startRunTestServiceCommon(app, testSuites, false, true); + ServiceUtil.startRunTestServiceCommon(app, new ArrayList<>(autoRunTests(app, d.preferenceManager)), false, true); d.generateAutoRunServiceSuite.markAsRan(); } diff --git a/app/src/main/java/org/openobservatory/ooniprobe/fragment/DashboardFragment.kt b/app/src/main/java/org/openobservatory/ooniprobe/fragment/DashboardFragment.kt index c5857f0f3..4ef973b80 100644 --- a/app/src/main/java/org/openobservatory/ooniprobe/fragment/DashboardFragment.kt +++ b/app/src/main/java/org/openobservatory/ooniprobe/fragment/DashboardFragment.kt @@ -33,9 +33,13 @@ class DashboardFragment : Fragment(), View.OnClickListener { @Inject lateinit var viewModel: DashboardViewModel + private var descriptors: ArrayList> = ArrayList() + private lateinit var binding: FragmentDashboardBinding + private lateinit var adapter: DashboardAdapter + override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, @@ -58,7 +62,8 @@ class DashboardFragment : Fragment(), View.OnClickListener { super.onViewCreated(view, savedInstanceState) viewModel.getGroupedItemList().observe(viewLifecycleOwner) { items -> binding.recycler.layoutManager = LinearLayoutManager(requireContext()) - binding.recycler.adapter = DashboardAdapter(items, this, preferenceManager) + adapter = DashboardAdapter(items, this, preferenceManager) + binding.recycler.adapter = adapter } viewModel.items.observe(viewLifecycleOwner) { items -> @@ -71,6 +76,11 @@ class DashboardFragment : Fragment(), View.OnClickListener { override fun onResume() { super.onResume() + /** + * Updates the list of tests when the user changes the default configuration + * after starting a test from [RunTestsActivity] + */ + binding.recycler.post { adapter.notifyDataSetChanged() } setLastTest() if (ReachabilityManager.isVPNinUse(this.context) && preferenceManager.isWarnVPNInUse