diff --git a/.github/workflows/archive.yml b/.github/workflows/archive.yml index d41537923..b04392ac3 100644 --- a/.github/workflows/archive.yml +++ b/.github/workflows/archive.yml @@ -7,7 +7,7 @@ jobs: steps: - uses: actions/setup-java@v2 with: - java-version: '11' + java-version: '17' distribution: 'temurin' - name: checkout uses: actions/checkout@v2 @@ -16,4 +16,4 @@ jobs: uses: actions/upload-artifact@v3 with: name: dev-apk - path: app/build/outputs/apk/devFull/release \ No newline at end of file + path: app/build/outputs/apk/devFull/release diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 6ed6c7a7e..46f49645d 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -12,7 +12,7 @@ jobs: steps: - uses: actions/setup-java@v2 with: - java-version: '11' + java-version: '17' distribution: 'temurin' - name: checkout uses: actions/checkout@v2 diff --git a/.github/workflows/emulator.yml b/.github/workflows/emulator.yml index 0a599c15a..de92c9393 100644 --- a/.github/workflows/emulator.yml +++ b/.github/workflows/emulator.yml @@ -17,7 +17,7 @@ jobs: steps: - uses: actions/setup-java@v2 with: - java-version: '11' + java-version: '17' distribution: 'temurin' - name: checkout uses: actions/checkout@v2 diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index e678fedd6..f743fca58 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -7,7 +7,7 @@ jobs: steps: - uses: actions/setup-java@v2 with: - java-version: '11' + java-version: '17' distribution: 'temurin' - name: checkout uses: actions/checkout@v2 diff --git a/NOTICE b/NOTICE index 4b365e82d..5fc016898 100644 --- a/NOTICE +++ b/NOTICE @@ -19,7 +19,6 @@ Terms: https://firebase.google.com/terms/ - easypermissions - LocalhostToolkit -- butterknife - DBFlow - LocalhostToolkit - MarkdownView diff --git a/app/build.gradle b/app/build.gradle index 174fef7f1..a1a41fd8b 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -1,19 +1,24 @@ -apply plugin: 'com.android.application' +plugins { + id 'com.android.application' + id 'kotlin-android' + id 'kotlin-kapt' +} apply from: 'jacoco.gradle' android { - compileSdk 34 + compileSdk libs.versions.compileSdk.get().toInteger() ndkVersion '22.0.7026061' - defaultConfig { - applicationId 'org.openobservatory.ooniprobe' - minSdk 21 - targetSdk 34 - versionName '3.8.3' - versionCode 102 + defaultConfig { + applicationId 'org.openobservatory.ooniprobe' + minSdk libs.versions.minSdk.get().toInteger() + targetSdk libs.versions.targetSdk.get().toInteger() + versionName '3.8.5' + versionCode 107 testInstrumentationRunner "org.openobservatory.ooniprobe.TestAndroidJUnitRunner" buildConfigField 'String', 'OONI_API_BASE_URL', '"https://api.ooni.io/"' buildConfigField 'String', 'NOTIFICATION_SERVER', '"https://countly.ooni.io"' + resValue "string", "APP_ID", 'org.openobservatory.ooniprobe' resValue "string", "APP_NAME", "OONI Probe" buildConfigField 'String', 'SOFTWARE_NAME', 'BASE_SOFTWARE_NAME+IS_DEBUG' buildConfigField 'String', 'COUNTLY_KEY', '"146836f41172f9e3287cab6f2cc347de3f5ddf3b"' @@ -41,18 +46,6 @@ android { } } - // Shared test code between Unit and Instrumented tests - sourceSets { - androidTest { - java.srcDirs += "src/sharedTest/java" - resources.srcDirs += "src/sharedTest/resources" - } - test { - java.srcDirs += "src/sharedTest/java" - resources.srcDirs += "src/sharedTest/resources" - } - } - flavorDimensions 'testing', 'license' productFlavors { stable { @@ -65,6 +58,7 @@ android { versionNameSuffix "-beta.1" versionCode versionCodeDate() buildConfigField 'String', 'BASE_SOFTWARE_NAME', '"ooniprobe-android-dev"' + resValue "string", "APP_ID", 'org.openobservatory.ooniprobe.dev' resValue "string", "APP_NAME", "OONI Dev" buildConfigField 'String', 'COUNTLY_KEY', '"e6c2cfe53e85951d50567467cef3f9fa2eab32c3"' } @@ -74,6 +68,7 @@ android { versionNameSuffix "-experimental.1" versionCode versionCodeDate() buildConfigField 'String', 'BASE_SOFTWARE_NAME', '"ooniprobe-android-experimental"' + resValue "string", "APP_ID", 'org.openobservatory.ooniprobe.experimental' resValue "string", "APP_NAME", "OONI Exp" buildConfigField 'String', 'COUNTLY_KEY', '"e6c2cfe53e85951d50567467cef3f9fa2eab32c3"' } @@ -91,10 +86,12 @@ android { } } compileOptions { - sourceCompatibility JavaVersion.VERSION_1_8 - targetCompatibility JavaVersion.VERSION_1_8 + sourceCompatibility JavaVersion.VERSION_17 + targetCompatibility JavaVersion.VERSION_17 + } + kotlinOptions { + jvmTarget = JavaVersion.VERSION_17 } - buildFeatures { viewBinding = true } @@ -105,83 +102,76 @@ dependencies { implementation project(path: ':engine') // AndroidX - implementation 'androidx.appcompat:appcompat:1.6.1' - implementation 'androidx.constraintlayout:constraintlayout:2.1.4' - implementation 'androidx.lifecycle:lifecycle-process:2.5.1' - implementation 'androidx.preference:preference:1.2.0' - implementation 'com.google.android.material:material:1.6.1' - implementation 'androidx.localbroadcastmanager:localbroadcastmanager:1.1.0' - implementation 'com.google.guava:guava:30.1.1-android' - implementation 'androidx.legacy:legacy-support-v4:1.0.0' - implementation "androidx.paging:paging-runtime-ktx:3.1.1" + implementation libs.androidx.appcompat + implementation libs.androidx.constraintlayout + implementation libs.androidx.lifecycle.process + implementation libs.androidx.preference + implementation libs.androidx.localbroadcastmanager + implementation libs.androidx.legacy.support.v4 + implementation libs.androidx.paging.runtime.ktx + +// Google + implementation libs.google.material + implementation libs.google.guava + implementation libs.google.gson // Third-party - annotationProcessor 'com.github.Raizlabs.DBFlow:dbflow-processor:4.2.4' - implementation 'com.github.Raizlabs.DBFlow:dbflow-core:4.2.4' - implementation 'com.github.Raizlabs.DBFlow:dbflow:4.2.4' - - implementation 'com.squareup.retrofit2:retrofit:2.9.0' - implementation 'com.squareup.retrofit2:converter-gson:2.9.0' - implementation 'com.squareup.okhttp3:logging-interceptor:4.9.1' + kapt libs.dbflow.processor + implementation libs.dbflow.core + implementation libs.dbflow.lib + implementation libs.retrofit.lib + implementation libs.retrofit.converter.gson + implementation libs.retrofit.logging.interceptor - implementation 'com.jakewharton:butterknife:10.2.3' - annotationProcessor 'com.jakewharton:butterknife-compiler:10.2.3' - implementation 'com.github.xanscale.LocalhostToolkit:app:19.05.01' - implementation 'com.airbnb.android:lottie:3.0.7' - implementation 'com.google.code.gson:gson:2.8.9' - implementation 'ru.noties:markwon:2.0.1' - implementation 'commons-io:commons-io:2.6' - //arcview to fragment_dashboard - implementation 'com.github.florent37:shapeofview:1.3.2' + implementation libs.xanscale.localhost.toolkit + implementation libs.lottie + implementation libs.markwon.core + implementation libs.commons.io + //arcview to fragment_dashboard + implementation libs.shapeofview // Flavor - fullImplementation platform('com.google.firebase:firebase-bom:26.3.0') - fullImplementation 'com.google.firebase:firebase-messaging' - fullImplementation 'ly.count.android:sdk:21.11.0' - fullImplementation 'io.sentry:sentry-android:6.3.0' - fullImplementation 'com.google.android.play:core:1.10.3' + fullImplementation platform(libs.google.firebase.bom) + fullImplementation libs.google.firebase.messaging + fullImplementation libs.countly.sdk + fullImplementation libs.sentry.android + fullImplementation libs.google.play.core // Dependency Injection - implementation 'com.google.dagger:dagger:2.36' - annotationProcessor 'com.google.dagger:dagger-compiler:2.36' + implementation libs.google.dagger + kapt libs.google.dagger.compiler // Logger implementation project(':applogger') // Testing - // Unit Testing - testImplementation 'junit:junit:4.13.2' - testImplementation 'androidx.test:core:1.4.0' - testImplementation 'androidx.test:runner:1.4.0' - testImplementation 'androidx.test:rules:1.4.0' - testImplementation 'org.mockito:mockito-core:4.6.1' - testImplementation 'org.mockito:mockito-inline:4.6.1' - testImplementation 'org.robolectric:robolectric:4.5.1' - testImplementation 'com.github.blocoio:faker:1.2.8' - testImplementation 'org.ooni:oonimkall:2023.07.18-162729' - testAnnotationProcessor 'com.google.dagger:dagger-compiler:2.36' - - // Instrumentation Testing - androidTestImplementation 'tools.fastlane:screengrab:2.0.0' - androidTestImplementation 'com.github.blocoio:faker:1.2.8' - androidTestImplementation 'androidx.test:runner:1.4.0' - androidTestImplementation 'androidx.test:rules:1.4.0' - androidTestImplementation 'androidx.test.ext:junit:1.1.3' - androidTestImplementation 'androidx.test.espresso:espresso-intents:3.4.0' - androidTestImplementation('androidx.test.espresso:espresso-contrib:3.4.0') { - exclude group: 'com.android.support', module: 'appcompat' - exclude group: 'com.android.support', module: 'support-v4' - exclude module: 'recyclerview-v7' - } - androidTestImplementation('androidx.test.espresso:espresso-core:3.4.0') { - exclude group: 'com.android.support', module: 'appcompat' - exclude group: 'com.android.support', module: 'support-v4' - exclude module: 'recyclerview-v7' - } - androidTestImplementation('com.schibsted.spain:barista:3.9.0') - androidTestAnnotationProcessor "com.google.dagger:dagger-compiler:2.36" + // Unit Testing + testImplementation project(':shared-test') + testImplementation libs.junit4 + testImplementation libs.androidx.core + testImplementation libs.androidx.runner + testImplementation libs.androidx.rules + testImplementation libs.mockito.core + testImplementation libs.mockito.inline + testImplementation libs.robolectric + testImplementation libs.faker + testImplementation libs.ooni.oonimkall + kaptTest libs.google.dagger.compiler + + // Instrumentation Testing + androidTestImplementation project(':shared-test') + androidTestImplementation libs.fastlane.screengrab + androidTestImplementation libs.faker + androidTestImplementation libs.androidx.runner + androidTestImplementation libs.androidx.rules + androidTestImplementation libs.androidx.junit + androidTestImplementation libs.androidx.espresso.intents + androidTestImplementation libs.androidx.espresso.contrib + androidTestImplementation libs.androidx.espresso.core + androidTestImplementation libs.barista + kaptAndroidTest libs.google.dagger.compiler } static def versionCodeDate() { diff --git a/app/jacoco.gradle b/app/jacoco.gradle index 816940653..15b2b36c1 100644 --- a/app/jacoco.gradle +++ b/app/jacoco.gradle @@ -1,7 +1,7 @@ apply plugin: 'jacoco' jacoco { - toolVersion '0.8.5' + toolVersion libs.versions.jacoco.get().toString() } task jacocoAndroidTestReport(type: JacocoReport) { @@ -48,13 +48,19 @@ task jacocoAndroidTestReport(type: JacocoReport) { executionData.from += fileTree(dir: codeCoverageDataLocation, includes: ['**/*.ec']) } - reports { - html.enabled true - html.destination file("${buildDir}/reports/coverage") - xml.enabled true - xml.destination file("${buildDir}/reports/coverage.xml") - csv.enabled false - } + reports { + html { + enabled true + destination file("${buildDir}/reports/coverage") + } + xml { + enabled true + destination file("${buildDir}/reports/coverage.xml") + } + csv { + enabled false + } + } doLast { println "Wrote HTML coverage report to ${reports.html.destination}/index.html" diff --git a/app/src/dev/res/values/untranslatable.xml b/app/src/dev/res/values/untranslatable.xml deleted file mode 100644 index 0a7ae877f..000000000 --- a/app/src/dev/res/values/untranslatable.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - org.openobservatory.ooniprobe.dev.activity.InfoActivity - org.openobservatory.ooniprobe.dev.activity.ProxyActivity - org.openobservatory.ooniprobe.dev.activity.LogActivity - \ No newline at end of file diff --git a/app/src/experimental/res/values/untranslatable.xml b/app/src/experimental/res/values/untranslatable.xml deleted file mode 100644 index 75568c958..000000000 --- a/app/src/experimental/res/values/untranslatable.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - org.openobservatory.ooniprobe.experimental.activity.InfoActivity - org.openobservatory.ooniprobe.experimental.activity.ProxyActivity - org.openobservatory.ooniprobe.experimental.activity.LogActivity - \ No newline at end of file diff --git a/app/src/fdroid/res/xml/preferences_global.xml b/app/src/fdroid/res/xml/preferences_global.xml index 26eea0e6d..3cbcc5cbf 100644 --- a/app/src/fdroid/res/xml/preferences_global.xml +++ b/app/src/fdroid/res/xml/preferences_global.xml @@ -355,7 +355,9 @@ app:iconSpaceReserved="false" android:key="logs" android:title="@string/Settings_Advanced_RecentLogs"> - + - + - + diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 78c0d0b76..1b717a3e5 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -7,7 +7,7 @@ - + editTexts; private ArrayList deletes; @Inject PreferenceManager preferenceManager; + private ActivityCustomwebsiteBinding binding; @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); getActivityComponent().inject(this); - setContentView(R.layout.activity_customwebsite); - ButterKnife.bind(this); + binding = ActivityCustomwebsiteBinding.inflate(getLayoutInflater()); + setContentView(binding.getRoot()); editTexts = new ArrayList<>(); deletes = new ArrayList<>(); - bottomBar.inflateMenu(R.menu.run); - bottomBar.setOnMenuItemClickListener(item -> { + binding.bottomBar.inflateMenu(R.menu.run); + binding.bottomBar.setOnMenuItemClickListener(item -> { if (!checkPrefix()) return false; ArrayList urls = new ArrayList<>(editTexts.size()); @@ -66,6 +53,7 @@ protected void onCreate(@Nullable Bundle savedInstanceState) { RunningActivity.runAsForegroundService(CustomWebsiteActivity.this, suite.asArray(), this::finish, preferenceManager); return true; }); + binding.add.setOnClickListener(v -> add()); add(); } @@ -103,12 +91,11 @@ public boolean onSupportNavigateUp() { return true; } - @OnClick(R.id.add) void add() { - ViewGroup urlBox = (ViewGroup) getLayoutInflater().inflate(R.layout.edittext_url, urlContainer, false); + ViewGroup urlBox = (ViewGroup) getLayoutInflater().inflate(R.layout.edittext_url, binding.urlContainer, false); EditText editText = urlBox.findViewById(R.id.editText); editTexts.add(editText); - urlContainer.addView(urlBox); + binding.urlContainer.addView(urlBox); ImageButton delete = urlBox.findViewById(R.id.delete); deletes.add(delete); delete.setTag(editText); @@ -117,11 +104,11 @@ void add() { ((View) v.getParent()).setVisibility(View.GONE); editTexts.remove(tag); deletes.remove(v); - bottomBar.setTitle(getString(R.string.OONIRun_URLs, Integer.toString(editTexts.size()))); + binding.bottomBar.setTitle(getString(R.string.OONIRun_URLs, Integer.toString(editTexts.size()))); setVisibilityDelete(); }); setVisibilityDelete(); - bottomBar.setTitle(getString(R.string.OONIRun_URLs, Integer.toString(editTexts.size()))); + binding.bottomBar.setTitle(getString(R.string.OONIRun_URLs, Integer.toString(editTexts.size()))); } private void setVisibilityDelete() { diff --git a/app/src/main/java/org/openobservatory/ooniprobe/activity/InfoActivity.java b/app/src/main/java/org/openobservatory/ooniprobe/activity/InfoActivity.java index 3a75eace3..b418f26df 100644 --- a/app/src/main/java/org/openobservatory/ooniprobe/activity/InfoActivity.java +++ b/app/src/main/java/org/openobservatory/ooniprobe/activity/InfoActivity.java @@ -3,44 +3,40 @@ import android.content.Intent; import android.net.Uri; import android.os.Bundle; -import android.widget.TextView; - import androidx.annotation.Nullable; -import androidx.appcompat.widget.Toolbar; - import org.openobservatory.ooniprobe.BuildConfig; import org.openobservatory.ooniprobe.R; - -import butterknife.BindView; -import butterknife.ButterKnife; -import butterknife.OnClick; +import org.openobservatory.ooniprobe.databinding.ActivityInfoBinding; public class InfoActivity extends AbstractActivity { - @BindView(R.id.toolbar) Toolbar toolbar; - @BindView(R.id.version) TextView version; @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); - setContentView(R.layout.activity_info); - ButterKnife.bind(this); - setSupportActionBar(toolbar); + ActivityInfoBinding binding = ActivityInfoBinding.inflate(getLayoutInflater()); + setContentView(binding.getRoot()); + setSupportActionBar(binding.toolbar); getSupportActionBar().setDisplayHomeAsUpEnabled(true); - version.setText(getString(R.string.version, BuildConfig.SOFTWARE_NAME, BuildConfig.VERSION_NAME)); + binding.version.setText(getString(R.string.version, BuildConfig.SOFTWARE_NAME, BuildConfig.VERSION_NAME)); + + binding.blog.setOnClickListener(v -> onBlogClick()); + binding.reports.setOnClickListener(v -> onReportsClick()); + binding.learnMore.setOnClickListener(v -> onLearnMoreClick()); + binding.dataPolicy.setOnClickListener(v -> onDataPolicyClick()); } - @OnClick(R.id.blog) void onBlogClick() { + void onBlogClick() { startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse("https://ooni.org/blog/"))); } - @OnClick(R.id.reports) void onReportsClick() { + void onReportsClick() { startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse("https://ooni.org/reports/"))); } - @OnClick(R.id.learnMore) void onLearnMoreClick() { + void onLearnMoreClick() { startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse("https://ooni.org/"))); } - @OnClick(R.id.dataPolicy) void onDataPolicyClick() { + void onDataPolicyClick() { startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse("https://ooni.org/about/data-policy/"))); } } diff --git a/app/src/main/java/org/openobservatory/ooniprobe/activity/MainActivity.java b/app/src/main/java/org/openobservatory/ooniprobe/activity/MainActivity.java index 66650d8dc..933bfbf88 100644 --- a/app/src/main/java/org/openobservatory/ooniprobe/activity/MainActivity.java +++ b/app/src/main/java/org/openobservatory/ooniprobe/activity/MainActivity.java @@ -19,7 +19,6 @@ import androidx.appcompat.app.AppCompatDelegate; import androidx.core.content.ContextCompat; -import com.google.android.material.bottomnavigation.BottomNavigationView; import com.google.android.material.snackbar.Snackbar; import org.openobservatory.ooniprobe.R; @@ -38,8 +37,6 @@ import javax.inject.Inject; -import butterknife.BindView; -import butterknife.ButterKnife; import localhost.toolkit.app.fragment.ConfirmDialogFragment; public class MainActivity extends AbstractActivity implements ConfirmDialogFragment.OnConfirmedListener { @@ -71,7 +68,6 @@ protected void onCreate(@Nullable Bundle savedInstanceState) { finish(); } else { - ButterKnife.bind(this); binding = ActivityMainBinding.inflate(getLayoutInflater()); setContentView(binding.getRoot()); binding.bottomNavigation.setOnItemSelectedListener(item -> { diff --git a/app/src/main/java/org/openobservatory/ooniprobe/activity/MeasurementDetailActivity.java b/app/src/main/java/org/openobservatory/ooniprobe/activity/MeasurementDetailActivity.java index 1e00ceb1d..9eada3e2d 100644 --- a/app/src/main/java/org/openobservatory/ooniprobe/activity/MeasurementDetailActivity.java +++ b/app/src/main/java/org/openobservatory/ooniprobe/activity/MeasurementDetailActivity.java @@ -8,87 +8,41 @@ import android.view.Menu; import android.view.MenuItem; import android.view.View; -import android.widget.Button; -import android.widget.TextView; - import androidx.annotation.Nullable; import androidx.appcompat.app.ActionBar; -import androidx.appcompat.widget.Toolbar; -import androidx.coordinatorlayout.widget.CoordinatorLayout; import androidx.fragment.app.Fragment; - import com.google.android.material.snackbar.Snackbar; import com.google.gson.Gson; - +import localhost.toolkit.app.fragment.ConfirmDialogFragment; import org.openobservatory.ooniprobe.R; import org.openobservatory.ooniprobe.common.PreferenceManager; import org.openobservatory.ooniprobe.common.ResubmitTask; +import org.openobservatory.ooniprobe.databinding.ActivityMeasurementDetailBinding; import org.openobservatory.ooniprobe.domain.GetTestSuite; import org.openobservatory.ooniprobe.domain.MeasurementsManager; import org.openobservatory.ooniprobe.domain.callback.DomainCallback; -import org.openobservatory.ooniprobe.fragment.measurement.DashFragment; -import org.openobservatory.ooniprobe.fragment.measurement.FacebookMessengerFragment; -import org.openobservatory.ooniprobe.fragment.measurement.FailedFragment; -import org.openobservatory.ooniprobe.fragment.measurement.HeaderNdtFragment; -import org.openobservatory.ooniprobe.fragment.measurement.HeaderOutcomeFragment; -import org.openobservatory.ooniprobe.fragment.measurement.HttpHeaderFieldManipulationFragment; -import org.openobservatory.ooniprobe.fragment.measurement.HttpInvalidRequestLineFragment; -import org.openobservatory.ooniprobe.fragment.measurement.NdtFragment; -import org.openobservatory.ooniprobe.fragment.measurement.PsiphonFragment; -import org.openobservatory.ooniprobe.fragment.measurement.RiseupVPNFragment; -import org.openobservatory.ooniprobe.fragment.measurement.SignalFragment; -import org.openobservatory.ooniprobe.fragment.measurement.TelegramFragment; -import org.openobservatory.ooniprobe.fragment.measurement.TorFragment; -import org.openobservatory.ooniprobe.fragment.measurement.WebConnectivityFragment; -import org.openobservatory.ooniprobe.fragment.measurement.WhatsappFragment; +import org.openobservatory.ooniprobe.fragment.measurement.*; import org.openobservatory.ooniprobe.fragment.resultHeader.ResultHeaderDetailFragment; import org.openobservatory.ooniprobe.model.database.Measurement; import org.openobservatory.ooniprobe.model.database.Network; import org.openobservatory.ooniprobe.test.suite.PerformanceSuite; -import org.openobservatory.ooniprobe.test.test.Dash; -import org.openobservatory.ooniprobe.test.test.FacebookMessenger; -import org.openobservatory.ooniprobe.test.test.HttpHeaderFieldManipulation; -import org.openobservatory.ooniprobe.test.test.HttpInvalidRequestLine; -import org.openobservatory.ooniprobe.test.test.Ndt; -import org.openobservatory.ooniprobe.test.test.Psiphon; -import org.openobservatory.ooniprobe.test.test.RiseupVPN; -import org.openobservatory.ooniprobe.test.test.Signal; -import org.openobservatory.ooniprobe.test.test.Telegram; -import org.openobservatory.ooniprobe.test.test.Tor; -import org.openobservatory.ooniprobe.test.test.WebConnectivity; -import org.openobservatory.ooniprobe.test.test.Whatsapp; +import org.openobservatory.ooniprobe.test.test.*; +import io.noties.markwon.Markwon; +import javax.inject.Inject; import java.io.Serializable; import java.util.Collections; import java.util.Objects; -import javax.inject.Inject; - -import butterknife.BindView; -import butterknife.ButterKnife; -import butterknife.OnClick; -import localhost.toolkit.app.fragment.ConfirmDialogFragment; -import ru.noties.markwon.Markwon; - public class MeasurementDetailActivity extends AbstractActivity implements ConfirmDialogFragment.OnConfirmedListener { private static final String ID = "id"; private static final String RERUN_KEY = "rerun"; - @BindView(R.id.coordinatorLayout) - CoordinatorLayout coordinatorLayout; - @BindView(R.id.toolbar) - Toolbar toolbar; + private Boolean isInExplorer; + private Measurement measurement; + private Snackbar snackbar; - private Boolean isInExplorer; - @BindView(R.id.log) - Button log; - @BindView(R.id.explorer) - Button explorer; - @BindView(R.id.data) - Button data; - @BindView(R.id.methodology) - TextView methodology; @Inject MeasurementsManager measurementsManager; @@ -121,9 +75,9 @@ protected void onCreate(@Nullable Bundle savedInstanceState) { measurement.is_anomaly ? R.style.Theme_MaterialComponents_Light_DarkActionBar_App_NoActionBar_Failure : R.style.Theme_MaterialComponents_Light_DarkActionBar_App_NoActionBar_Success); - setContentView(R.layout.activity_measurement_detail); - ButterKnife.bind(this); - setSupportActionBar(toolbar); + ActivityMeasurementDetailBinding binding = ActivityMeasurementDetailBinding.inflate(getLayoutInflater()); + setContentView(binding.getRoot()); + setSupportActionBar(binding.toolbar); ActionBar bar = getSupportActionBar(); if (bar != null) { bar.setDisplayHomeAsUpEnabled(true); @@ -237,7 +191,7 @@ protected void onCreate(@Nullable Bundle savedInstanceState) { .replace(R.id.body, detail) .replace(R.id.head, head) .commit(); - snackbar = Snackbar.make(coordinatorLayout, R.string.Snackbar_ResultsNotUploaded_Text, Snackbar.LENGTH_INDEFINITE) + snackbar = Snackbar.make(binding.coordinatorLayout, R.string.Snackbar_ResultsNotUploaded_Text, Snackbar.LENGTH_INDEFINITE) .setAction(R.string.Snackbar_ResultsNotUploaded_Upload, v1 -> runAsyncTask()); Context c = this; isInExplorer = !measurement.hasReportFile(c); @@ -255,11 +209,16 @@ public void onError(String msg) { }); if (!measurement.hasLogFile(this)) - log.setVisibility(View.GONE); + binding.log.setVisibility(View.GONE); if (!measurementsManager.hasReportId(measurement)) - explorer.setVisibility(View.GONE); - Markwon.setMarkdown(methodology, getString(R.string.TestResults_Details_Methodology_Paragraph, getString(measurement.getTest().getUrlResId()))); + binding.explorer.setVisibility(View.GONE); + Markwon.builder(this) + .build() + .setMarkdown(binding.methodology, getString(R.string.TestResults_Details_Methodology_Paragraph, getString(measurement.getTest().getUrlResId()))); load(); + binding.log.setOnClickListener(v -> logClick()); + binding.data.setOnClickListener(v -> dataClick()); + binding.explorer.setOnClickListener(v -> explorerClick()); } private void runAsyncTask() { @@ -305,17 +264,14 @@ public boolean onOptionsItemSelected(MenuItem item) { } } - @OnClick(R.id.log) void logClick() { startActivity(TextActivity.newIntent(this, TextActivity.TYPE_LOG, measurement)); } - @OnClick(R.id.data) void dataClick() { startActivity(TextActivity.newIntent(this, TextActivity.TYPE_JSON, measurement)); } - @OnClick(R.id.explorer) void explorerClick() { startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(measurementsManager.getExplorerUrl(measurement)))); } diff --git a/app/src/main/java/org/openobservatory/ooniprobe/activity/OoniRunActivity.java b/app/src/main/java/org/openobservatory/ooniprobe/activity/OoniRunActivity.java index 89b32b674..85fd4c3d3 100644 --- a/app/src/main/java/org/openobservatory/ooniprobe/activity/OoniRunActivity.java +++ b/app/src/main/java/org/openobservatory/ooniprobe/activity/OoniRunActivity.java @@ -6,21 +6,17 @@ import android.util.Patterns; import android.view.View; import android.webkit.URLUtil; -import android.widget.Button; -import android.widget.ImageView; -import android.widget.TextView; import android.widget.Toast; -import androidx.appcompat.widget.Toolbar; import androidx.recyclerview.widget.DividerItemDecoration; import androidx.recyclerview.widget.LinearLayoutManager; -import androidx.recyclerview.widget.RecyclerView; import com.google.gson.Gson; import org.openobservatory.ooniprobe.BuildConfig; import org.openobservatory.ooniprobe.R; import org.openobservatory.ooniprobe.common.PreferenceManager; +import org.openobservatory.ooniprobe.databinding.ActivityOonirunBinding; import org.openobservatory.ooniprobe.domain.GetTestSuite; import org.openobservatory.ooniprobe.domain.VersionCompare; import org.openobservatory.ooniprobe.domain.models.Attribute; @@ -33,19 +29,11 @@ import javax.inject.Inject; -import butterknife.BindView; -import butterknife.ButterKnife; import localhost.toolkit.widget.recyclerview.HeterogeneousRecyclerAdapter; import localhost.toolkit.widget.recyclerview.HeterogeneousRecyclerItem; public class OoniRunActivity extends AbstractActivity { - @BindView(R.id.toolbar) Toolbar toolbar; - @BindView(R.id.icon) ImageView icon; - @BindView(R.id.iconBig) ImageView iconBig; - @BindView(R.id.title) TextView title; - @BindView(R.id.desc) TextView desc; - @BindView(R.id.run) Button run; - @BindView(R.id.recycler) RecyclerView recycler; + ActivityOonirunBinding binding; private ArrayList items; private HeterogeneousRecyclerAdapter adapter; @@ -65,17 +53,17 @@ public class OoniRunActivity extends AbstractActivity { protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); getActivityComponent().inject(this); - setContentView(R.layout.activity_oonirun); - ButterKnife.bind(this); - setSupportActionBar(toolbar); + binding = ActivityOonirunBinding.inflate(getLayoutInflater()); + setContentView(binding.getRoot()); + setSupportActionBar(binding.toolbar); getSupportActionBar().setDisplayHomeAsUpEnabled(true); getSupportActionBar().setDisplayShowHomeEnabled(true); LinearLayoutManager layoutManager = new LinearLayoutManager(this); - recycler.setLayoutManager(layoutManager); - recycler.addItemDecoration(new DividerItemDecoration(this, layoutManager.getOrientation())); + binding.recycler.setLayoutManager(layoutManager); + binding.recycler.addItemDecoration(new DividerItemDecoration(this, layoutManager.getOrientation())); items = new ArrayList<>(); adapter = new HeterogeneousRecyclerAdapter<>(this, items); - recycler.setAdapter(adapter); + binding.recycler.setAdapter(adapter); manageIntent(getIntent()); } @@ -120,7 +108,7 @@ private void loadScreen(String mv, String tn, String ta){ if (versionCompare.compare(version_name, mv) >= 0) { try { Attribute attribute = gson.fromJson(ta, Attribute.class); - List urls = (attribute!=null && attribute.urls != null) ? attribute.urls : Collections.emptyList(); + List urls = (attribute!=null && attribute.urls != null) ? attribute.urls : null; AbstractSuite suite = getSuite.get(tn, urls); if (suite != null) { loadSuite(suite, urls); @@ -139,34 +127,34 @@ private void loadScreen(String mv, String tn, String ta){ } private void loadOutOfDate() { - title.setText(R.string.OONIRun_OONIProbeOutOfDate); - desc.setText(R.string.OONIRun_OONIProbeNewerVersion); - run.setText(R.string.OONIRun_Update); - icon.setImageResource(R.drawable.update); - iconBig.setImageResource(R.drawable.update); - iconBig.setVisibility(View.VISIBLE); - run.setOnClickListener(v -> { + binding.title.setText(R.string.OONIRun_OONIProbeOutOfDate); + binding.desc.setText(R.string.OONIRun_OONIProbeNewerVersion); + binding.run.setText(R.string.OONIRun_Update); + binding.icon.setImageResource(R.drawable.update); + binding.iconBig.setImageResource(R.drawable.update); + binding.iconBig.setVisibility(View.VISIBLE); + binding.run.setOnClickListener(v -> { startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse("https://play.google.com/store/apps/details?id=" + getPackageName()))); finish(); }); } private void loadSuite(AbstractSuite suite, List urls) { - icon.setImageResource(suite.getIcon()); - title.setText(suite.getTestList(preferenceManager)[0].getLabelResId()); - desc.setText(getString(R.string.OONIRun_YouAreAboutToRun)); + binding.icon.setImageResource(suite.getIcon()); + binding.title.setText(suite.getTestList(preferenceManager)[0].getLabelResId()); + binding.desc.setText(getString(R.string.OONIRun_YouAreAboutToRun)); if (urls != null) { for (String url : urls) { if (URLUtil.isValidUrl(url)) items.add(new TextItem(url)); } adapter.notifyTypesChanged(); - iconBig.setVisibility(View.GONE); + binding.iconBig.setVisibility(View.GONE); } else { - iconBig.setImageResource(suite.getIcon()); - iconBig.setVisibility(View.VISIBLE); + binding.iconBig.setImageResource(suite.getIcon()); + binding.iconBig.setVisibility(View.VISIBLE); } - run.setOnClickListener(v -> { + binding.run.setOnClickListener(v -> { RunningActivity.runAsForegroundService(OoniRunActivity.this, suite.asArray(),this::finish, preferenceManager); @@ -174,12 +162,12 @@ private void loadSuite(AbstractSuite suite, List urls) { } private void loadInvalidAttributes() { - title.setText(R.string.OONIRun_InvalidParameter); - desc.setText(R.string.OONIRun_InvalidParameter_Msg); - run.setText(R.string.OONIRun_Close); - icon.setImageResource(R.drawable.question_mark); - iconBig.setImageResource(R.drawable.question_mark); - iconBig.setVisibility(View.VISIBLE); - run.setOnClickListener(v -> finish()); + binding.title.setText(R.string.OONIRun_InvalidParameter); + binding.desc.setText(R.string.OONIRun_InvalidParameter_Msg); + binding.run.setText(R.string.OONIRun_Close); + binding.icon.setImageResource(R.drawable.question_mark); + binding.iconBig.setImageResource(R.drawable.question_mark); + binding.iconBig.setVisibility(View.VISIBLE); + binding.run.setOnClickListener(v -> finish()); } } 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 415744384..be3c4dead 100644 --- a/app/src/main/java/org/openobservatory/ooniprobe/activity/OverviewActivity.java +++ b/app/src/main/java/org/openobservatory/ooniprobe/activity/OverviewActivity.java @@ -5,18 +5,14 @@ import android.os.Bundle; import android.text.format.DateUtils; import android.view.View; -import android.widget.Button; -import android.widget.ImageView; -import android.widget.TextView; import androidx.annotation.Nullable; -import androidx.appcompat.widget.Toolbar; -import androidx.core.app.ActivityCompat; import androidx.core.text.TextUtilsCompat; import androidx.core.view.ViewCompat; import org.openobservatory.ooniprobe.R; import org.openobservatory.ooniprobe.common.PreferenceManager; +import org.openobservatory.ooniprobe.databinding.ActivityOverviewBinding; import org.openobservatory.ooniprobe.model.database.Result; import org.openobservatory.ooniprobe.test.suite.AbstractSuite; import org.openobservatory.ooniprobe.test.suite.ExperimentalSuite; @@ -27,20 +23,12 @@ import javax.inject.Inject; -import butterknife.BindView; -import butterknife.ButterKnife; -import butterknife.OnClick; -import ru.noties.markwon.Markwon; +import io.noties.markwon.Markwon; public class OverviewActivity extends AbstractActivity { private static final String TEST = "test"; - @BindView(R.id.toolbar) Toolbar toolbar; - @BindView(R.id.icon) ImageView icon; - @BindView(R.id.runtime) TextView runtime; - @BindView(R.id.lastTime) TextView lastTime; - @BindView(R.id.desc) TextView desc; - @BindView(R.id.customUrl) Button customUrl; - @BindView(R.id.run) Button run; + + ActivityOverviewBinding binding; private AbstractSuite testSuite; @Inject @@ -55,41 +43,51 @@ public static Intent newIntent(Context context, AbstractSuite testSuite) { getActivityComponent().inject(this); testSuite = (AbstractSuite) getIntent().getSerializableExtra(TEST); setTheme(testSuite.getThemeLight()); - setContentView(R.layout.activity_overview); - ButterKnife.bind(this); - setSupportActionBar(toolbar); + binding = ActivityOverviewBinding.inflate(getLayoutInflater()); + setContentView(binding.getRoot()); + setSupportActionBar(binding.toolbar); getSupportActionBar().setDisplayHomeAsUpEnabled(true); setTitle(testSuite.getTitle()); - icon.setImageResource(testSuite.getIcon()); - customUrl.setVisibility(testSuite.getName().equals(WebsitesSuite.NAME) ? View.VISIBLE : View.GONE); + binding.icon.setImageResource(testSuite.getIcon()); + binding.customUrl.setVisibility(testSuite.getName().equals(WebsitesSuite.NAME) ? View.VISIBLE : View.GONE); if(testSuite.isTestEmpty(preferenceManager)){ - run.setAlpha(0.5F); - run.setEnabled(false); + binding.run.setAlpha(0.5F); + binding.run.setEnabled(false); } + Markwon markwon = Markwon.builder(this).build(); if (testSuite.getName().equals(ExperimentalSuite.NAME)) { String experimentalLinks = "\n\n* [STUN Reachability](https://github.com/ooni/spec/blob/master/nettests/ts-025-stun-reachability.md)" + "\n\n* [DNS Check](https://github.com/ooni/spec/blob/master/nettests/ts-028-dnscheck.md)" + + "\n\n* [RiseupVPN](https://ooni.org/nettest/riseupvpn/)" + + "\n\n* [ECH Check](https://github.com/ooni/spec/blob/master/nettests/ts-039-echcheck.md)" + "\n\n* [Tor Snowflake](https://ooni.org/nettest/tor-snowflake/) "+ String.format(" ( %s )",getString(R.string.Settings_TestOptions_LongRunningTest))+ "\n\n* [Vanilla Tor](https://github.com/ooni/spec/blob/master/nettests/ts-016-vanilla-tor.md) " + String.format(" ( %s )",getString(R.string.Settings_TestOptions_LongRunningTest)); - Markwon.setMarkdown(desc, getString(testSuite.getDesc1(), experimentalLinks)); + markwon.setMarkdown(binding.desc, getString(testSuite.getDesc1(), experimentalLinks)); if (TextUtilsCompat.getLayoutDirectionFromLocale(Locale.getDefault()) == ViewCompat.LAYOUT_DIRECTION_RTL) - desc.setTextDirection(View.TEXT_DIRECTION_RTL); + binding.desc.setTextDirection(View.TEXT_DIRECTION_RTL); } else - Markwon.setMarkdown(desc, getString(testSuite.getDesc1())); + markwon.setMarkdown(binding.desc, getString(testSuite.getDesc1())); Result lastResult = Result.getLastResult(testSuite.getName()); if (lastResult == null) - lastTime.setText(R.string.Dashboard_Overview_LastRun_Never); + binding.lastTime.setText(R.string.Dashboard_Overview_LastRun_Never); else - lastTime.setText(DateUtils.getRelativeTimeSpanString(lastResult.start_time.getTime())); + binding.lastTime.setText(DateUtils.getRelativeTimeSpanString(lastResult.start_time.getTime())); + + setUpOnCLickListeners(); + } + + private void setUpOnCLickListeners() { + binding.run.setOnClickListener(view -> onRunClick()); + binding.customUrl.setOnClickListener(view -> customUrlClick()); } @Override protected void onResume() { super.onResume(); testSuite.setTestList((AbstractTest[]) null); testSuite.getTestList(preferenceManager); - runtime.setText(getString(R.string.twoParam, getString(testSuite.getDataUsage()), getString(R.string.Dashboard_Card_Seconds, testSuite.getRuntime(preferenceManager).toString()))); + binding.runtime.setText(getString(R.string.twoParam, getString(testSuite.getDataUsage()), getString(R.string.Dashboard_Card_Seconds, testSuite.getRuntime(preferenceManager).toString()))); } @Override @@ -98,13 +96,13 @@ public boolean onSupportNavigateUp() { return true; } - @OnClick(R.id.run) void onRunClick() { + void onRunClick() { if(!testSuite.isTestEmpty(preferenceManager)){ RunningActivity.runAsForegroundService(this, testSuite.asArray(), this::bindTestService, preferenceManager); } } - @OnClick(R.id.customUrl) void customUrlClick() { + void customUrlClick() { startActivity(new Intent(this, CustomWebsiteActivity.class)); } } diff --git a/app/src/main/java/org/openobservatory/ooniprobe/activity/ProxyActivity.java b/app/src/main/java/org/openobservatory/ooniprobe/activity/ProxyActivity.java index 46ced49d6..fdc5bb245 100644 --- a/app/src/main/java/org/openobservatory/ooniprobe/activity/ProxyActivity.java +++ b/app/src/main/java/org/openobservatory/ooniprobe/activity/ProxyActivity.java @@ -4,29 +4,23 @@ import android.util.Log; import android.view.KeyEvent; import android.view.MenuItem; -import android.widget.RadioButton; -import android.widget.RadioGroup; -import android.widget.TextView; - import androidx.annotation.NonNull; - import com.google.android.material.textfield.TextInputLayout; import com.google.common.net.InetAddresses; import com.google.common.net.InternetDomainName; - import org.openobservatory.ooniprobe.R; import org.openobservatory.ooniprobe.common.AppLogger; import org.openobservatory.ooniprobe.common.PreferenceManager; import org.openobservatory.ooniprobe.common.ProxyProtocol; import org.openobservatory.ooniprobe.common.ProxySettings; +import org.openobservatory.ooniprobe.databinding.ActivityProxyBinding; +import io.noties.markwon.Markwon; +import javax.inject.Inject; import java.net.URISyntaxException; +import java.util.Arrays; import java.util.Objects; -import javax.inject.Inject; - -import ru.noties.markwon.Markwon; - /** * The ProxyActivity is part of the Settings. It allows users to * configure the proxy for speaking with OONI's backends. @@ -103,36 +97,8 @@ public class ProxyActivity extends AbstractActivity { // The following radio group describes the top level choice // in terms of proxying: no proxy, psiphon, or custom. - // proxyRadioGroup is the top-level radio group. - private RadioGroup proxyRadioGroup; - - // proxyNoneRB is the radio button selecting the "none" proxy. - private RadioButton proxyNoneRB; - - // proxyPsiphonRB is the radio button selecting the "psiphon" proxy. - private RadioButton proxyPsiphonRB; - - // proxyCustomRB is the radio button for the "custom" proxy. - private RadioButton proxyCustomRB; - - // The following radio group allows users to choose which specific - // custom proxy they would like to use. When writing this documentation, - // only socks5 is available but we will add more options. - - // customProxyRadioGroup allows you to choose among the different - // kinds of custom proxies that are available. - private RadioGroup customProxyRadioGroup; - - // customProxySOCKS5 selects the custom SOCKS5 proxy type. - private RadioButton customProxySOCKS5; - - // The following settings allow users to configure the custom proxy. - - // customProxyHostname is the hostname for the custom proxy. - private TextInputLayout customProxyHostname; + ActivityProxyBinding binding; - // customProxyPort is the port for the custom proxy. - private TextInputLayout customProxyPort; // settings contains a representation of the proxy settings // loaded from the preference manager. @@ -147,21 +113,15 @@ public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); getActivityComponent().inject(this); + binding = ActivityProxyBinding.inflate(getLayoutInflater()); // We draw the view and store references to objects needed // when configuring the initial view or modifying it. - setContentView(R.layout.activity_proxy); - proxyRadioGroup = findViewById(R.id.proxyRadioGroup); - proxyNoneRB = findViewById(R.id.proxyNone); - proxyPsiphonRB = findViewById(R.id.proxyPsiphon); - proxyCustomRB = findViewById(R.id.proxyCustom); - customProxyRadioGroup = findViewById(R.id.customProxyRadioGroup); - customProxySOCKS5 = findViewById(R.id.customProxySOCKS5); - customProxyHostname = findViewById(R.id.customProxyHostname); - customProxyPort = findViewById(R.id.customProxyPort); + setContentView(binding.getRoot()); // We fill the footer that helps users to understand this settings screen. - TextView proxyFooter = findViewById(R.id.proxyFooter); - Markwon.setMarkdown(proxyFooter, getString(R.string.Settings_Proxy_Footer)); + Markwon.builder(this) + .build() + .setMarkdown(binding.proxyFooter, getString(R.string.Settings_Proxy_Footer)); // We read settings and configure the initial view. loadSettingsAndConfigureInitialView(); @@ -193,11 +153,12 @@ private void configureInitialViewWithSettings(ProxySettings settings) { // Inspect the scheme and use the scheme to choose among the // top-level radio buttons describing the proxy type. if (settings.protocol == ProxyProtocol.NONE) { - proxyNoneRB.setChecked(true); + binding.proxyNone.setChecked(true); } else if (settings.protocol == ProxyProtocol.PSIPHON) { - proxyPsiphonRB.setChecked(true); - } else if (settings.protocol == ProxyProtocol.SOCKS5) { - proxyCustomRB.setChecked(true); + binding.proxyPsiphon.setChecked(true); + } else if (Arrays.asList(getResources().getStringArray(R.array.proxy_protocol_list)).contains(settings.protocol.getProtocol())) { + binding.customProxyProtocol.setText(settings.protocol.getProtocol(),false); + binding.proxyCustom.setChecked(true); } else { // TODO(bassosimone): this should also be reported as a bug. Log.w(TAG, "got an unhandled proxy scheme"); @@ -208,28 +169,28 @@ private void configureInitialViewWithSettings(ProxySettings settings) { // If the scheme is custom, then we need to enable the // part of the view related to custom proxies. customProxySetEnabled(isSchemeCustom(settings.protocol)); - customProxySOCKS5.setChecked(isSchemeCustom(settings.protocol)); // Populate all the editable fields _anyway_ so the user // has the feeling that everything was just as before + Log.d(TAG, "(from preferences) protocol: " + settings.protocol); + logger.i(TAG, "(from preferences) protocol: " + settings.protocol); Log.d(TAG, "(from preferences) hostname: " + settings.hostname); logger.i(TAG, "(from preferences) hostname: " + settings.hostname); Log.d(TAG, "(from preferences) port: " + settings.port); logger.i(TAG, "(from preferences) port: " + settings.port); - Objects.requireNonNull(customProxyHostname.getEditText()).setText(settings.hostname); - Objects.requireNonNull(customProxyPort.getEditText()).setText(settings.port); + Objects.requireNonNull(binding.customProxyHostname.getEditText()).setText(settings.hostname); + Objects.requireNonNull(binding.customProxyPort.getEditText()).setText(settings.port); // Now we need to make the top level proxy radio group interactive: when // we change what is selected, we need the view to adapt. - proxyRadioGroup.setOnCheckedChangeListener((group, checkedId) -> { + binding.proxyRadioGroup.setOnCheckedChangeListener((group, checkedId) -> { if (checkedId == R.id.proxyNone) { customProxySetEnabled(false); } else if (checkedId == R.id.proxyPsiphon) { customProxySetEnabled(false); } else if (checkedId == R.id.proxyCustom) { customProxySetEnabled(true); - customProxyRadioGroup.clearCheck(); - customProxySOCKS5.setChecked(true); + binding.customProxyRadioGroup.clearCheck(); } else { // TODO(bassosimone): this should also be reported as a bug. Log.w(TAG, "unexpected state in setOnCheckedChangeListener"); @@ -238,14 +199,14 @@ private void configureInitialViewWithSettings(ProxySettings settings) { }); // When we change the focus of text fields, clear any lingering error text. - Objects.requireNonNull(customProxyHostname.getEditText()).setOnFocusChangeListener((v, hasFocus) -> { + Objects.requireNonNull(binding.customProxyHostname.getEditText()).setOnFocusChangeListener((v, hasFocus) -> { if (!hasFocus) { - customProxyHostname.setError(null); + binding.customProxyHostname.setError(null); } }); - Objects.requireNonNull(customProxyPort.getEditText()).setOnFocusChangeListener((v, hasFocus) -> { + Objects.requireNonNull(binding.customProxyPort.getEditText()).setOnFocusChangeListener((v, hasFocus) -> { if (!hasFocus) { - customProxyHostname.setError(null); + binding.customProxyHostname.setError(null); } }); } @@ -254,7 +215,7 @@ private void configureInitialViewWithSettings(ProxySettings settings) { private boolean isSchemeCustom(ProxyProtocol protocol) { // This is where we need to extend the implementation of we add a new scheme // that will not be related to a custom proxy type. - return protocol == ProxyProtocol.SOCKS5; + return protocol == ProxyProtocol.SOCKS5 || protocol == ProxyProtocol.HTTP || protocol == ProxyProtocol.HTTPS; } // customProxyTextInputSetEnabled is a helper function that changes the @@ -268,9 +229,8 @@ private void customProxyTextInputSetEnabled(@NonNull TextInputLayout input, bool // customProxySetEnabled reacts to the enabling or disabling of the custom // proxy group and changes the view accordingly to that. private void customProxySetEnabled(boolean flag) { - customProxySOCKS5.setEnabled(flag); - customProxyTextInputSetEnabled(customProxyHostname, flag); - customProxyTextInputSetEnabled(customProxyPort, flag); + customProxyTextInputSetEnabled(binding.customProxyHostname, flag); + customProxyTextInputSetEnabled(binding.customProxyPort, flag); } // isValidHostnameOrIP validates its input as an IP address or hostname. @@ -341,14 +301,14 @@ public void onBackPressed() { logger.i(TAG, "onBackPressed: about to save proxy settings"); // Get the hostname and port for the custom proxy. - String hostname = Objects.requireNonNull(customProxyHostname.getEditText()).getText().toString(); - String port = Objects.requireNonNull(customProxyPort.getEditText()).getText().toString(); + String hostname = Objects.requireNonNull(binding.customProxyHostname.getEditText()).getText().toString(); + String port = Objects.requireNonNull(binding.customProxyPort.getEditText()).getText().toString(); settings.hostname = hostname; settings.port = port; // If no proxy is selected then just write an empty proxy // configuration into the settings and move on. - if (proxyNoneRB.isChecked()) { + if (binding.proxyNone.isChecked()) { settings.protocol = ProxyProtocol.NONE; saveSettings(); super.onBackPressed(); @@ -357,7 +317,7 @@ public void onBackPressed() { // If the psiphon proxy is checked then write back the right // proxy configuration for psiphon and move on. - if (proxyPsiphonRB.isChecked()) { + if (binding.proxyPsiphon.isChecked()) { settings.protocol = ProxyProtocol.PSIPHON; saveSettings(); super.onBackPressed(); @@ -366,26 +326,27 @@ public void onBackPressed() { // validate the hostname for the custom proxy. if (!isValidHostnameOrIP(hostname)) { - customProxyHostname.setError("not a valid hostname or IP"); + binding.customProxyHostname.setError("not a valid hostname or IP"); return; } // validate the port for the custom proxy. if (!isValidPort(port)) { - customProxyPort.setError("not a valid network port"); + binding.customProxyPort.setError("not a valid network port"); return; } - // At this point we're going to assume that this is a socks5 proxy. We will - // need to change the code in here when we add support for http proxies. - settings.protocol = ProxyProtocol.SOCKS5; + // At this point we're going to assume that this is a socks5,http,https proxy. + // ProxyProtocol.valueOf will only accept one of the values in ProxyProtocol + // as in the enum definition(uppercase). try { + settings.protocol = ProxyProtocol.valueOf(binding.customProxyProtocol.getText().toString().toUpperCase()); settings.getProxyString(); } catch (URISyntaxException e) { // okay, then, notwithstanding our efforts it still seems that we // have not obtained a valid URL, so let's not proceed. - customProxyHostname.setError("cannot construct a valid URL"); - customProxyPort.setError("cannot construct a valid URL"); + binding.customProxyHostname.setError("cannot construct a valid URL"); + binding.customProxyPort.setError("cannot construct a valid URL"); return; } diff --git a/app/src/main/java/org/openobservatory/ooniprobe/activity/ResultDetailActivity.java b/app/src/main/java/org/openobservatory/ooniprobe/activity/ResultDetailActivity.java deleted file mode 100644 index 15e334152..000000000 --- a/app/src/main/java/org/openobservatory/ooniprobe/activity/ResultDetailActivity.java +++ /dev/null @@ -1,268 +0,0 @@ -package org.openobservatory.ooniprobe.activity; - -import android.content.Context; -import android.content.DialogInterface; -import android.content.Intent; -import android.os.Bundle; -import android.view.Menu; -import android.view.MenuItem; -import android.view.View; - -import androidx.annotation.Nullable; -import androidx.appcompat.app.ActionBar; -import androidx.appcompat.widget.Toolbar; -import androidx.coordinatorlayout.widget.CoordinatorLayout; -import androidx.core.app.ActivityCompat; -import androidx.fragment.app.Fragment; -import androidx.fragment.app.FragmentActivity; -import androidx.recyclerview.widget.DividerItemDecoration; -import androidx.recyclerview.widget.LinearLayoutManager; -import androidx.recyclerview.widget.RecyclerView; -import androidx.viewpager2.adapter.FragmentStateAdapter; -import androidx.viewpager2.widget.ViewPager2; - -import com.google.android.material.snackbar.Snackbar; -import com.google.android.material.tabs.TabLayout; -import com.google.android.material.tabs.TabLayoutMediator; - -import org.openobservatory.ooniprobe.R; -import org.openobservatory.ooniprobe.common.PreferenceManager; -import org.openobservatory.ooniprobe.common.ResubmitTask; -import org.openobservatory.ooniprobe.domain.GetResults; -import org.openobservatory.ooniprobe.domain.GetTestSuite; -import org.openobservatory.ooniprobe.fragment.resultHeader.ResultHeaderDetailFragment; -import org.openobservatory.ooniprobe.fragment.resultHeader.ResultHeaderMiddleboxFragment; -import org.openobservatory.ooniprobe.fragment.resultHeader.ResultHeaderPerformanceFragment; -import org.openobservatory.ooniprobe.fragment.resultHeader.ResultHeaderTBAFragment; -import org.openobservatory.ooniprobe.item.MeasurementItem; -import org.openobservatory.ooniprobe.item.MeasurementPerfItem; -import org.openobservatory.ooniprobe.model.database.Measurement; -import org.openobservatory.ooniprobe.model.database.Network; -import org.openobservatory.ooniprobe.model.database.Result; -import org.openobservatory.ooniprobe.test.suite.CircumventionSuite; -import org.openobservatory.ooniprobe.test.suite.ExperimentalSuite; -import org.openobservatory.ooniprobe.test.suite.InstantMessagingSuite; -import org.openobservatory.ooniprobe.test.suite.MiddleBoxesSuite; -import org.openobservatory.ooniprobe.test.suite.PerformanceSuite; -import org.openobservatory.ooniprobe.test.suite.WebsitesSuite; - -import java.io.Serializable; -import java.util.ArrayList; -import java.util.List; - -import javax.inject.Inject; - -import butterknife.BindView; -import butterknife.ButterKnife; -import localhost.toolkit.app.fragment.ConfirmDialogFragment; -import localhost.toolkit.widget.recyclerview.HeterogeneousRecyclerAdapter; -import localhost.toolkit.widget.recyclerview.HeterogeneousRecyclerItem; - -public class ResultDetailActivity extends AbstractActivity implements View.OnClickListener, ConfirmDialogFragment.OnConfirmedListener { - private static final String ID = "id"; - private static final String UPLOAD_KEY = "upload"; - private static final String RERUN_KEY = "rerun"; - @BindView(R.id.coordinatorLayout) - CoordinatorLayout coordinatorLayout; - @BindView(R.id.toolbar) - Toolbar toolbar; - @BindView(R.id.tabLayout) - TabLayout tabLayout; - @BindView(R.id.pager) - ViewPager2 pager; - @BindView(R.id.recyclerView) - RecyclerView recycler; - private ArrayList items; - private HeterogeneousRecyclerAdapter adapter; - private Result result; - private Snackbar snackbar; - - @Inject - GetTestSuite getTestSuite; - - @Inject - GetResults getResults; - - @Inject - PreferenceManager preferenceManager; - - public static Intent newIntent(Context context, int id) { - return new Intent(context, ResultDetailActivity.class).putExtra(ID, id); - } - - @Override - protected void onCreate(@Nullable Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - getActivityComponent().inject(this); - result = getResults.get(getIntent().getIntExtra(ID, 0)); - assert result != null; - setTheme(result.getTestSuite().getThemeLight()); - setContentView(R.layout.activity_result_detail); - ButterKnife.bind(this); - setSupportActionBar(toolbar); - ActionBar bar = getSupportActionBar(); - if (bar != null) { - bar.setDisplayHomeAsUpEnabled(true); - bar.setTitle(result.getTestSuite().getTitle()); - } - pager.setAdapter(new ResultHeaderAdapter(this)); - new TabLayoutMediator(tabLayout, pager, (tab, position) -> - tab.setText("●") - ).attach(); - LinearLayoutManager layoutManager = new LinearLayoutManager(this); - recycler.setLayoutManager(layoutManager); - recycler.addItemDecoration(new DividerItemDecoration(this, layoutManager.getOrientation())); - result.is_viewed = true; - result.save(); - items = new ArrayList<>(); - adapter = new HeterogeneousRecyclerAdapter<>(this, items); - recycler.setAdapter(adapter); - snackbar = Snackbar.make(coordinatorLayout, R.string.Snackbar_ResultsSomeNotUploaded_Text, Snackbar.LENGTH_INDEFINITE) - .setAction(R.string.Snackbar_ResultsSomeNotUploaded_UploadAll, v1 -> runAsyncTask()); - } - - @Override - protected void onResume() { - super.onResume(); - load(); - } - - @Override - public boolean onCreateOptionsMenu(Menu menu) { - getMenuInflater().inflate(R.menu.rerun, menu); - return super.onCreateOptionsMenu(menu); - } - - @Override - public boolean onPrepareOptionsMenu(Menu menu) { - invalidateOptionsMenu(); - if (!result.test_group_name.equals(WebsitesSuite.NAME)) - menu.findItem(R.id.reRun).setVisible(false); - return super.onPrepareOptionsMenu(menu); - } - - @Override - public boolean onOptionsItemSelected(MenuItem item) { - switch (item.getItemId()) { - case R.id.reRun: - new ConfirmDialogFragment.Builder() - .withExtra(RERUN_KEY) - .withMessage(getString(R.string.Modal_ReRun_Websites_Title, String.valueOf(result.getMeasurements().size()))) - .withPositiveButton(getString(R.string.Modal_ReRun_Websites_Run)) - .build().show(getSupportFragmentManager(), null); - return true; - default: - return super.onOptionsItemSelected(item); - } - } - - private void reTestWebsites() { - RunningActivity.runAsForegroundService(this, getTestSuite.getFrom(result).asArray(),this::finish, preferenceManager); - } - - private void runAsyncTask() { - new ResubmitAsyncTask(this).execute(result.id, null); - } - - private void load() { - result = getResults.get(result.id); - assert result != null; - boolean isPerf = result.test_group_name.equals(PerformanceSuite.NAME); - items.clear(); - List measurements = result.getMeasurementsSorted(); - for (Measurement measurement : measurements) - items.add(isPerf && !measurement.is_failed ? - new MeasurementPerfItem(measurement, this) : - new MeasurementItem(measurement, this)); - adapter.notifyTypesChanged(); - if (Measurement.hasReport(this, Measurement.selectUploadableWithResultId(result.id))) - snackbar.show(); - else - snackbar.dismiss(); - } - - @Override - public void onClick(View v) { - Measurement measurement = (Measurement) v.getTag(); - if (result.test_group_name.equals(ExperimentalSuite.NAME)) - startActivity(TextActivity.newIntent(this, TextActivity.TYPE_JSON, measurement)); - else - ActivityCompat.startActivity(this, MeasurementDetailActivity.newIntent(this, measurement.id), null); - } - - @Override - public void onConfirmation(Serializable extra, int buttonClicked) { - if (buttonClicked == DialogInterface.BUTTON_POSITIVE && extra.equals(UPLOAD_KEY)) - runAsyncTask(); - else if (buttonClicked == DialogInterface.BUTTON_POSITIVE && extra.equals(RERUN_KEY)) - reTestWebsites(); - else if (buttonClicked == DialogInterface.BUTTON_NEUTRAL) - startActivity(TextActivity.newIntent(this, TextActivity.TYPE_UPLOAD_LOG, (String)extra)); - } - - private static class ResubmitAsyncTask extends ResubmitTask { - ResubmitAsyncTask(ResultDetailActivity activity) { - super(activity, activity.preferenceManager.getProxyURL()); - } - - @Override - protected void onPostExecute(Boolean result) { - super.onPostExecute(result); - if (getActivity() != null) { - getActivity().result = d.getResults.get(getActivity().result.id); - getActivity().load(); - if (!result) - new ConfirmDialogFragment.Builder() - .withExtra(UPLOAD_KEY) - .withTitle(getActivity().getString(R.string.Modal_UploadFailed_Title)) - .withMessage(getActivity().getString(R.string.Modal_UploadFailed_Paragraph, errors.toString(), totUploads.toString())) - .withPositiveButton(getActivity().getString(R.string.Modal_Retry)) - .withNeutralButton(getActivity().getString(R.string.Modal_DisplayFailureLog)) - .withExtra(String.join("\n", logger.logs)) - .build().show(getActivity().getSupportFragmentManager(), null); - } - } - } - - private class ResultHeaderAdapter extends FragmentStateAdapter { - ResultHeaderAdapter(final FragmentActivity fa) { - super(fa); - } - - @Override - public Fragment createFragment(int position) { - if (result.test_group_name.equals(ExperimentalSuite.NAME)){ - if (position == 0) - return ResultHeaderDetailFragment.newInstance(false, result.getFormattedDataUsageUp(), result.getFormattedDataUsageDown(), result.start_time, result.getRuntime(), true, null, null); - else if (position == 1) - return ResultHeaderDetailFragment.newInstance(false, null, null, null, null, null, Network.getCountry(ResultDetailActivity.this, result.network), result.network); - } - if (position == 1) - return ResultHeaderDetailFragment.newInstance(false, result.getFormattedDataUsageUp(), result.getFormattedDataUsageDown(), result.start_time, result.getRuntime(), true, null, null); - else if (position == 2) - return ResultHeaderDetailFragment.newInstance(false, null, null, null, null, null, Network.getCountry(ResultDetailActivity.this, result.network), result.network); - else switch (result.test_group_name) { - default: //Default can no longer be null, so we have to default to something... - // NOTE: Perhaps set up a test page? - case WebsitesSuite.NAME: - return ResultHeaderTBAFragment.newInstance(result); - case InstantMessagingSuite.NAME: - return ResultHeaderTBAFragment.newInstance(result); - case MiddleBoxesSuite.NAME: - return ResultHeaderMiddleboxFragment.newInstance(result.countAnomalousMeasurements() > 0); - case PerformanceSuite.NAME: - return ResultHeaderPerformanceFragment.newInstance(result); - case CircumventionSuite.NAME: - return ResultHeaderTBAFragment.newInstance(result); - } - } - - - @Override - public int getItemCount() { - if (result.test_group_name.equals(ExperimentalSuite.NAME)) - return 2; - return 3; - } - } -} diff --git a/app/src/main/java/org/openobservatory/ooniprobe/activity/ResultDetailActivity.kt b/app/src/main/java/org/openobservatory/ooniprobe/activity/ResultDetailActivity.kt new file mode 100644 index 000000000..1b4231c8a --- /dev/null +++ b/app/src/main/java/org/openobservatory/ooniprobe/activity/ResultDetailActivity.kt @@ -0,0 +1,298 @@ +package org.openobservatory.ooniprobe.activity + +import android.content.Context +import android.content.DialogInterface +import android.content.Intent +import android.os.Bundle +import android.view.Menu +import android.view.MenuItem +import android.view.View +import androidx.core.app.ActivityCompat +import androidx.fragment.app.Fragment +import androidx.fragment.app.FragmentActivity +import androidx.viewpager2.adapter.FragmentStateAdapter +import com.google.android.material.snackbar.Snackbar +import com.google.android.material.tabs.TabLayout +import com.google.android.material.tabs.TabLayoutMediator +import localhost.toolkit.app.fragment.ConfirmDialogFragment +import localhost.toolkit.app.fragment.ConfirmDialogFragment.OnConfirmedListener +import org.openobservatory.ooniprobe.R +import org.openobservatory.ooniprobe.adapters.MeasurementGroup +import org.openobservatory.ooniprobe.adapters.ResultDetailExpandableListAdapter +import org.openobservatory.ooniprobe.common.PreferenceManager +import org.openobservatory.ooniprobe.common.ResubmitTask +import org.openobservatory.ooniprobe.databinding.ActivityResultDetailBinding +import org.openobservatory.ooniprobe.domain.GetResults +import org.openobservatory.ooniprobe.domain.GetTestSuite +import org.openobservatory.ooniprobe.fragment.resultHeader.ResultHeaderDetailFragment +import org.openobservatory.ooniprobe.fragment.resultHeader.ResultHeaderMiddleboxFragment +import org.openobservatory.ooniprobe.fragment.resultHeader.ResultHeaderPerformanceFragment +import org.openobservatory.ooniprobe.fragment.resultHeader.ResultHeaderTBAFragment +import org.openobservatory.ooniprobe.model.database.Measurement +import org.openobservatory.ooniprobe.model.database.Network +import org.openobservatory.ooniprobe.model.database.Result +import org.openobservatory.ooniprobe.test.suite.* +import java.io.Serializable +import javax.inject.Inject + +class ResultDetailActivity : AbstractActivity(), View.OnClickListener, OnConfirmedListener { + + companion object { + private const val ID = "id" + private const val UPLOAD_KEY = "upload" + private const val RERUN_KEY = "rerun" + + @JvmStatic + fun newIntent(context: Context?, id: Int): Intent { + return Intent(context, ResultDetailActivity::class.java).putExtra(ID, id) + } + } + + private lateinit var result: Result + private lateinit var snackbar: Snackbar + private lateinit var binding: ActivityResultDetailBinding + + @Inject + lateinit var getTestSuite: GetTestSuite + + @Inject + lateinit var getResults: GetResults + + @Inject + lateinit var preferenceManager: PreferenceManager + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + activityComponent.inject(this) + when (val iResult = getResults[intent.getIntExtra(ID, 0)]) { + null -> { + /** + * Close the activity if the result is not found. This should never happen. + * Previous use of 'assert' closed the entire app. + */ + finish() + return + } + + else -> { + result = iResult + setTheme(result.testSuite.themeLight) + binding = ActivityResultDetailBinding.inflate(layoutInflater) + setContentView(binding.root) + setSupportActionBar(binding.toolbar) + supportActionBar?.let { actionBar -> + actionBar.setDisplayHomeAsUpEnabled(true) + actionBar.setTitle(result.testSuite.title) + } + binding.pager.apply { + setAdapter(ResultHeaderAdapter(this@ResultDetailActivity)) + TabLayoutMediator(binding.tabLayout, this) + { tab: TabLayout.Tab, _: Int -> tab.setText("●") }.attach() + } + result.apply { + is_viewed = true + save() + } + + snackbar = Snackbar.make( + binding.coordinatorLayout, + R.string.Snackbar_ResultsSomeNotUploaded_Text, + Snackbar.LENGTH_INDEFINITE + ).setAction(R.string.Snackbar_ResultsSomeNotUploaded_UploadAll) { runAsyncTask() } + load() + } + } + } + + override fun onCreateOptionsMenu(menu: Menu): Boolean { + menuInflater.inflate(R.menu.rerun, menu) + return super.onCreateOptionsMenu(menu) + } + + override fun onPrepareOptionsMenu(menu: Menu): Boolean { + invalidateOptionsMenu() + if (result.test_group_name != WebsitesSuite.NAME) menu.findItem(R.id.reRun).setVisible(false) + return super.onPrepareOptionsMenu(menu) + } + + override fun onOptionsItemSelected(item: MenuItem): Boolean { + return when (item.itemId) { + R.id.reRun -> { + ConfirmDialogFragment.Builder() + .withExtra(RERUN_KEY) + .withMessage( + getString( + R.string.Modal_ReRun_Websites_Title, + result.getMeasurements().size.toString() + ) + ) + .withPositiveButton(getString(R.string.Modal_ReRun_Websites_Run)) + .build().show(supportFragmentManager, null) + true + } + + else -> super.onOptionsItemSelected(item) + } + } + + private fun reTestWebsites() { + RunningActivity.runAsForegroundService( + this, + getTestSuite.getFrom(result).asArray(), + { finish() }, + preferenceManager + ) + } + + private fun runAsyncTask() { + ResubmitAsyncTask(this).execute(result.id, null) + } + + private fun load() { + result = getResults[result.id] + + val groupedItemList = mutableListOf() + val groupedItems = result.getMeasurementsSorted().groupBy { it.test_name } + for ((_, itemList) in groupedItems) { + if (itemList.size == 1) { + groupedItemList.add(itemList.first()) + } else { + if (groupedItems.size == 1) { + groupedItemList.addAll(itemList) + } else { + groupedItemList.add(MeasurementGroup(title = itemList.first().test.name, measurements = itemList)) + } + + } + } + + binding.recyclerView.apply { + setAdapter( + ResultDetailExpandableListAdapter(groupedItemList, this@ResultDetailActivity) + ) + } + + if (Measurement.hasReport( + this, + Measurement.selectUploadableWithResultId(result.id) + ) + ) snackbar.show() else snackbar.dismiss() + } + + override fun onClick(v: View) { + val measurement = v.tag as Measurement + if (result.test_group_name == ExperimentalSuite.NAME) startActivity( + TextActivity.newIntent( + this, + TextActivity.TYPE_JSON, + measurement + ) + ) else ActivityCompat.startActivity(this, MeasurementDetailActivity.newIntent(this, measurement.id), null) + } + + override fun onConfirmation(extra: Serializable, buttonClicked: Int) { + if (buttonClicked == DialogInterface.BUTTON_POSITIVE && extra == UPLOAD_KEY) { + runAsyncTask() + } else if (buttonClicked == DialogInterface.BUTTON_POSITIVE && extra == RERUN_KEY) { + reTestWebsites() + } else if (buttonClicked == DialogInterface.BUTTON_NEUTRAL) { + startActivity( + TextActivity.newIntent(this, TextActivity.TYPE_UPLOAD_LOG, extra as String) + ) + } + } + + private class ResubmitAsyncTask(activity: ResultDetailActivity) : + ResubmitTask(activity, activity.preferenceManager.proxyURL) { + override fun onPostExecute(result: Boolean) { + super.onPostExecute(result) + if (getActivity() != null) { + getActivity()!!.result = d.getResults[getActivity()!!.result.id] + getActivity()!!.load() + if (!result) ConfirmDialogFragment.Builder() + .withExtra(UPLOAD_KEY) + .withTitle(getActivity()!!.getString(R.string.Modal_UploadFailed_Title)) + .withMessage( + getActivity()!!.getString( + R.string.Modal_UploadFailed_Paragraph, + errors.toString(), + totUploads.toString() + ) + ) + .withPositiveButton(getActivity()!!.getString(R.string.Modal_Retry)) + .withNeutralButton(getActivity()!!.getString(R.string.Modal_DisplayFailureLog)) + .withExtra(java.lang.String.join("\n", logger.logs)) + .build().show(getActivity()!!.supportFragmentManager, null) + } + } + } + + private inner class ResultHeaderAdapter(fa: FragmentActivity) : FragmentStateAdapter(fa) { + override fun createFragment(position: Int): Fragment { + when (result.test_group_name) { + ExperimentalSuite.NAME -> { + when (position) { + 0 -> return ResultHeaderDetailFragment.newInstance( + false, + result.formattedDataUsageUp, + result.formattedDataUsageDown, + result.start_time, + result.getRuntime(), + true, + null, + null + ) + + 1 -> return ResultHeaderDetailFragment.newInstance( + false, + null, + null, + null, + null, + null, + Network.getCountry(this@ResultDetailActivity, result.network), + result.network + ) + } + } + } + return when (position) { + 1 -> ResultHeaderDetailFragment.newInstance( + false, + result.formattedDataUsageUp, + result.formattedDataUsageDown, + result.start_time, + result.getRuntime(), + true, + null, + null + ) + + 2 -> ResultHeaderDetailFragment.newInstance( + false, + null, + null, + null, + null, + null, + Network.getCountry(this@ResultDetailActivity, result.network), + result.network + ) + + else -> when (result.test_group_name) { + WebsitesSuite.NAME -> ResultHeaderTBAFragment.newInstance(result) + InstantMessagingSuite.NAME -> ResultHeaderTBAFragment.newInstance(result) + MiddleBoxesSuite.NAME -> ResultHeaderMiddleboxFragment.newInstance(result.countAnomalousMeasurements() > 0) + PerformanceSuite.NAME -> ResultHeaderPerformanceFragment.newInstance(result) + CircumventionSuite.NAME -> ResultHeaderTBAFragment.newInstance(result) + else -> ResultHeaderTBAFragment.newInstance(result) + } + } + } + + override fun getItemCount(): Int { + return if (result.test_group_name == ExperimentalSuite.NAME) 2 else 3 + } + } + +} \ No newline at end of file diff --git a/app/src/main/java/org/openobservatory/ooniprobe/activity/RunningActivity.java b/app/src/main/java/org/openobservatory/ooniprobe/activity/RunningActivity.java index d40d95dcb..c65b13df1 100644 --- a/app/src/main/java/org/openobservatory/ooniprobe/activity/RunningActivity.java +++ b/app/src/main/java/org/openobservatory/ooniprobe/activity/RunningActivity.java @@ -12,20 +12,12 @@ import android.os.Bundle; import android.view.View; import android.view.animation.Animation; -import android.widget.Button; -import android.widget.ImageButton; -import android.widget.ProgressBar; -import android.widget.RelativeLayout; -import android.widget.TextView; import android.widget.Toast; import androidx.annotation.NonNull; import androidx.annotation.Nullable; -import androidx.core.content.ContextCompat; import androidx.localbroadcastmanager.content.LocalBroadcastManager; -import com.airbnb.lottie.LottieAnimationView; - import org.openobservatory.ooniprobe.R; import org.openobservatory.ooniprobe.common.Application; import org.openobservatory.ooniprobe.common.PreferenceManager; @@ -33,6 +25,7 @@ import org.openobservatory.ooniprobe.common.TestProgressRepository; import org.openobservatory.ooniprobe.common.service.RunTestService; import org.openobservatory.ooniprobe.common.service.ServiceUtil; +import org.openobservatory.ooniprobe.databinding.ActivityRunningBinding; import org.openobservatory.ooniprobe.receiver.TestRunBroadRequestReceiver; import org.openobservatory.ooniprobe.test.suite.AbstractSuite; import org.openobservatory.ooniprobe.test.suite.ExperimentalSuite; @@ -42,36 +35,19 @@ import javax.inject.Inject; -import butterknife.BindView; -import butterknife.ButterKnife; import localhost.toolkit.app.fragment.ConfirmDialogFragment; import localhost.toolkit.app.fragment.MessageDialogFragment; /** - * Serves to display progress of {@code RunTestService} running in in the background on a screen. + * Serves to display progress of {@code RunTestService} running in the background on a screen. * * Also contains {@link #runAsForegroundService(AbstractActivity, ArrayList,OnTestServiceStartedListener) runAsForegroundService} * used to start {@code RunTestService} in the background. */ public class RunningActivity extends AbstractActivity implements ConfirmDialogFragment.OnConfirmedListener { - @BindView(R.id.running) - TextView running; - @BindView(R.id.name) - TextView name; - @BindView(R.id.log) - TextView log; - @BindView(R.id.eta) - TextView eta; - @BindView(R.id.progress) - ProgressBar progress; - @BindView(R.id.close) - ImageButton close; - @BindView(R.id.stop) - Button stop; - @BindView(R.id.animation) - LottieAnimationView animation; - @BindView(R.id.proxy_icon) - RelativeLayout proxy_icon; + + ActivityRunningBinding binding; + private TestRunBroadRequestReceiver receiver; @Inject @@ -127,7 +103,7 @@ public static void runAsForegroundService(AbstractActivity context, private static void startRunTestService(AbstractActivity context, ArrayList testSuites, OnTestServiceStartedListener onTestServiceStartedListener) { - ServiceUtil.startRunTestService(context, testSuites, true); + ServiceUtil.startRunTestServiceManual(context, testSuites, true); onTestServiceStartedListener.onTestServiceStarted(); } @@ -136,29 +112,29 @@ protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); getActivityComponent().inject(this); setTheme(R.style.Theme_MaterialComponents_NoActionBar_App); - setContentView(R.layout.activity_running); - ButterKnife.bind(this); + binding = ActivityRunningBinding.inflate(getLayoutInflater()); + setContentView(binding.getRoot()); testProgressRepository.getProgress().observe(this, progressValue -> { if (progressValue!=null) { - progress.setProgress(progressValue); + binding.progress.setProgress(progressValue); } }); testProgressRepository.getEta().observe(this,etaValue -> { if (etaValue!=null) { - eta.setText(readableTimeRemaining(etaValue)); + binding.eta.setText(readableTimeRemaining(etaValue)); } }); if (preferenceManager.getProxyURL().isEmpty()) - proxy_icon.setVisibility(View.GONE); - close.setOnClickListener(new View.OnClickListener() { + binding.proxyIcon.setVisibility(View.GONE); + binding.close.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { finish(); } }); - stop.setOnClickListener(new View.OnClickListener() { + binding.stop.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { new ConfirmDialogFragment.Builder() @@ -176,33 +152,33 @@ private void applyUIChanges(RunTestService service) { service.task.currentSuite == null || service.task.currentTest == null) { return; } - animation.setImageAssetsFolder("anim/"); - animation.setRepeatCount(Animation.INFINITE); - animation.playAnimation(); + binding.animation.setImageAssetsFolder("anim/"); + binding.animation.setRepeatCount(Animation.INFINITE); + binding.animation.playAnimation(); Integer progressLevel = testProgressRepository.getProgress().getValue(); if (progressLevel != null) { - progress.setProgress(progressLevel); + binding.progress.setProgress(progressLevel); } else { - progress.setIndeterminate(true); + binding.progress.setIndeterminate(true); } Double etaValue = testProgressRepository.getEta().getValue(); if (etaValue!=null){ - eta.setText(readableTimeRemaining(etaValue)); + binding.eta.setText(readableTimeRemaining(etaValue)); }else { - eta.setText(R.string.Dashboard_Running_CalculatingETA); + binding.eta.setText(R.string.Dashboard_Running_CalculatingETA); } if (service.task.currentSuite.getName().equals(ExperimentalSuite.NAME)) - name.setText(service.task.currentTest.getName()); + binding.name.setText(service.task.currentTest.getName()); else - name.setText(getString(service.task.currentTest.getLabelResId())); + binding.name.setText(getString(service.task.currentTest.getLabelResId())); getWindow().setBackgroundDrawableResource(service.task.currentSuite.getColor()); if (Build.VERSION.SDK_INT >= 21) { getWindow().setStatusBarColor(service.task.currentSuite.getColor()); } - animation.setAnimation(service.task.currentSuite.getAnim()); - progress.setMax(service.task.getMax(preferenceManager)); + binding.animation.setAnimation(service.task.currentSuite.getAnim()); + binding.progress.setMax(service.task.getMax(preferenceManager)); } @Override @@ -264,20 +240,20 @@ public void onStart(RunTestService service) { @Override public void onRun(String value) { - name.setText(value); + binding.name.setText(value); } @Override public void onProgress(int state, double timeLeft) { - progress.setIndeterminate(false); - progress.setProgress(state); + binding.progress.setIndeterminate(false); + binding.progress.setProgress(state); - eta.setText(readableTimeRemaining(timeLeft)); + binding.eta.setText(readableTimeRemaining(timeLeft)); } @Override public void onLog(String value) { - log.setText(value); + binding.log.setText(value); } @Override @@ -287,13 +263,13 @@ public void onError(String value) { @Override public void onUrl() { - progress.setIndeterminate(false); + binding.progress.setIndeterminate(false); } @Override public void onInterrupt() { - running.setText(getString(R.string.Dashboard_Running_Stopping_Title)); - log.setText(getString(R.string.Dashboard_Running_Stopping_Notice)); + binding.running.setText(getString(R.string.Dashboard_Running_Stopping_Title)); + binding.log.setText(getString(R.string.Dashboard_Running_Stopping_Notice)); } @Override diff --git a/app/src/main/java/org/openobservatory/ooniprobe/activity/TextActivity.java b/app/src/main/java/org/openobservatory/ooniprobe/activity/TextActivity.java index c13cc269d..94798e09a 100644 --- a/app/src/main/java/org/openobservatory/ooniprobe/activity/TextActivity.java +++ b/app/src/main/java/org/openobservatory/ooniprobe/activity/TextActivity.java @@ -7,24 +7,19 @@ import android.os.Bundle; import android.view.Menu; import android.view.MenuItem; -import android.widget.TextView; import android.widget.Toast; - import androidx.annotation.Nullable; import androidx.fragment.app.FragmentManager; - +import localhost.toolkit.app.fragment.MessageDialogFragment; import org.openobservatory.ooniprobe.R; import org.openobservatory.ooniprobe.common.ReachabilityManager; +import org.openobservatory.ooniprobe.databinding.TextBinding; import org.openobservatory.ooniprobe.domain.MeasurementsManager; import org.openobservatory.ooniprobe.domain.callback.DomainCallback; import org.openobservatory.ooniprobe.model.database.Measurement; import javax.inject.Inject; -import butterknife.BindView; -import butterknife.ButterKnife; -import localhost.toolkit.app.fragment.MessageDialogFragment; - public class TextActivity extends AbstractActivity { private Measurement measurement; private String text; @@ -34,8 +29,7 @@ public class TextActivity extends AbstractActivity { private static final String TEST = "test"; private static final String TYPE = "type"; private static final String TEXT = "text"; - @BindView(R.id.textView) - TextView textView; + private TextBinding binding; @Inject MeasurementsManager measurementsManager; @@ -52,8 +46,8 @@ public static Intent newIntent(Context context, int type, String text) { protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); getActivityComponent().inject(this); - setContentView(R.layout.text); - ButterKnife.bind(this); + binding = TextBinding.inflate(getLayoutInflater()); + setContentView(binding.getRoot()); showText(); } @@ -95,7 +89,7 @@ public void showText() { private void showLog() { try { text = measurementsManager.getReadableLog(measurement); - textView.setText(text); + binding.textView.setText(text); } catch (Exception e) { new MessageDialogFragment.Builder() .withTitle(getString(R.string.Modal_Error_LogNotFound)) @@ -107,7 +101,7 @@ private void showJson() { //Try to open file, if it doesn't exist dont show Error dialog immediately but try to download the json from internet try { text = measurementsManager.getReadableEntry(measurement); - textView.setText(text); + binding.textView.setText(text); } catch (Exception e) { e.printStackTrace(); if (ReachabilityManager.getNetworkType(this).equals(ReachabilityManager.NO_INTERNET)) { @@ -125,7 +119,7 @@ private void showJson() { public void onSuccess(String result) { runOnUiThread(() -> { text = result; - textView.setText(result); + binding.textView.setText(result); }); } @@ -140,7 +134,7 @@ public void onError(String msg) { } private void showUploadLog() { - textView.setText(text); + binding.textView.setText(text); } private void showError(String msg) { diff --git a/app/src/main/java/org/openobservatory/ooniprobe/adapters/DashboardAdapter.kt b/app/src/main/java/org/openobservatory/ooniprobe/adapters/DashboardAdapter.kt new file mode 100644 index 000000000..26baea9f4 --- /dev/null +++ b/app/src/main/java/org/openobservatory/ooniprobe/adapters/DashboardAdapter.kt @@ -0,0 +1,105 @@ +package org.openobservatory.ooniprobe.adapters + +import android.content.res.Resources +import android.graphics.PorterDuff +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.cardview.widget.CardView +import androidx.recyclerview.widget.RecyclerView +import org.openobservatory.ooniprobe.R +import org.openobservatory.ooniprobe.common.PreferenceManager +import org.openobservatory.ooniprobe.databinding.ItemSeperatorBinding +import org.openobservatory.ooniprobe.databinding.ItemTestsuiteBinding +import org.openobservatory.ooniprobe.test.suite.AbstractSuite + +class DashboardAdapter( + private val items: List, + private val onClickListener: View.OnClickListener, + private val preferenceManager: PreferenceManager, +) : RecyclerView.Adapter() { + + companion object { + private const val VIEW_TYPE_TITLE = 0 + private const val VIEW_TYPE_CARD = 1 + } + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder { + return when (viewType) { + VIEW_TYPE_TITLE -> { + CardGroupTitleViewHolder( + ItemSeperatorBinding.inflate( + LayoutInflater.from(parent.context), parent, false + ) + ) + } + + else -> { + CardViewHolder( + ItemTestsuiteBinding.inflate( + LayoutInflater.from(parent.context), parent, false + ) + ) + } + } + } + + override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { + val item = items[position] + when (holder.itemViewType) { + VIEW_TYPE_TITLE -> { + } + + VIEW_TYPE_CARD -> { + val cardHolder = holder as CardViewHolder + if (item is AbstractSuite) { + cardHolder.binding.apply { + title.setText(item.title) + desc.setText(item.cardDesc) + icon.setImageResource(item.iconGradient) + } + holder.itemView.tag = item + if (item.isTestEmpty(preferenceManager)) { + holder.setIsRecyclable(false) + holder.itemView.apply { + elevation = 0f + isClickable = false + } + val resources: Resources = holder.itemView.context.resources + (holder.itemView as CardView).setCardBackgroundColor(resources.getColor(R.color.disabled_test_background)) + holder.binding.apply { + title.setTextColor(resources.getColor(R.color.disabled_test_text)) + desc.setTextColor(resources.getColor(R.color.disabled_test_text)) + icon.setColorFilter(resources.getColor(R.color.disabled_test_text), PorterDuff.Mode.SRC_IN) + } + } else { + holder.itemView.setOnClickListener(onClickListener) + } + } + } + } + } + + override fun getItemCount(): Int { + return items.size + } + + override fun getItemViewType(position: Int): Int { + return when (items[position]) { + is String -> VIEW_TYPE_TITLE + else -> VIEW_TYPE_CARD + } + } + + /** + * ViewHolder for dashboard item group + * @param binding + */ + class CardGroupTitleViewHolder(var binding: ItemSeperatorBinding) : RecyclerView.ViewHolder(binding.root) + + /** + * ViewHolder for dashboard item + * @param binding + */ + class CardViewHolder(var binding: ItemTestsuiteBinding) : RecyclerView.ViewHolder(binding.root) +} \ No newline at end of file diff --git a/app/src/main/java/org/openobservatory/ooniprobe/adapters/ResultDetailExpandableListAdapter.kt b/app/src/main/java/org/openobservatory/ooniprobe/adapters/ResultDetailExpandableListAdapter.kt new file mode 100755 index 000000000..46fd48604 --- /dev/null +++ b/app/src/main/java/org/openobservatory/ooniprobe/adapters/ResultDetailExpandableListAdapter.kt @@ -0,0 +1,207 @@ +package org.openobservatory.ooniprobe.adapters + +import android.content.Context +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.BaseExpandableListAdapter +import android.widget.TextView +import androidx.core.content.ContextCompat +import org.openobservatory.ooniprobe.R +import org.openobservatory.ooniprobe.model.database.Measurement +import org.openobservatory.ooniprobe.test.test.* + +data class MeasurementGroup(val title: String, val measurements: List) + + +class ResultDetailExpandableListAdapter( + private val items: List, + private val onClickListener: View.OnClickListener +) : BaseExpandableListAdapter() { + + override fun getGroupCount() = items.size + + override fun getChildrenCount(listPosition: Int): Int = items[listPosition].let { + when (it) { + is MeasurementGroup -> it.measurements.size + + else -> 0 + } + } + + override fun getGroup(listPosition: Int): Any { + return when { + items[listPosition] is MeasurementGroup -> (items[listPosition] as MeasurementGroup).title + else -> items[listPosition] + } + } + + override fun getChild(listPosition: Int, expandedListPosition: Int): Measurement? = when { + items[listPosition] is MeasurementGroup -> (items[listPosition] as MeasurementGroup).measurements[expandedListPosition] + else -> null + } + + override fun getGroupId(listPosition: Int): Long = listPosition.toLong() + + override fun getChildId(listPosition: Int, expandedListPosition: Int): Long = expandedListPosition.toLong() + + override fun hasStableIds(): Boolean = false + + override fun isChildSelectable(listPosition: Int, expandedListPosition: Int): Boolean = true + + override fun getChildView( + groupPosition: Int, + childPosition: Int, + isLastChild: Boolean, + convertView: View?, + parent: ViewGroup + ): View { + val measurement = getChild(groupPosition, childPosition) + + val root = convertView ?: LayoutInflater.from(parent.context) + .inflate(R.layout.item_measurement, parent, false) + + measurement?.let { + bindMeasurement(it, root) + root.apply { + setPaddingRelative(96, 0, 0, 0) + setBackgroundColor(parent.context.resources.getColor(R.color.color_gray0)) + } + } ?: run { + root.visibility = View.GONE + } + + return root + } + + + override fun getGroupView( + groupPosition: Int, + isExpanded: Boolean, + convertView: View?, + parent: ViewGroup + ): View { + val groupItem = getGroup(groupPosition) + + val root = convertView ?: LayoutInflater.from(parent.context) + .inflate(R.layout.item_measurement, parent, false) + + when (groupItem) { + is Measurement -> bindMeasurement(groupItem, root) + + else -> { + root.findViewById(R.id.text).text = groupItem.toString() + root.findViewById(R.id.indicator).apply { + visibility = View.VISIBLE + text = "${(items[groupPosition] as MeasurementGroup).measurements.size} Inputs" + setCompoundDrawablesRelativeWithIntrinsicBounds( + null, + null, + ContextCompat.getDrawable(root.context,if (isExpanded) R.drawable.keyboard_arrow_up else R.drawable.keyboard_arrow_down)?.apply { + setTint(ContextCompat.getColor(root.context, R.color.color_black)) + }, + null + ) + } + + } + } + return root + } + + private fun bindMeasurement( + measurement: Measurement, + view: View + ) { + view.tag = measurement + view.setOnClickListener(onClickListener) + view.findViewById(R.id.text).also { textView -> + + val test: AbstractTest = measurement.getTest() + + val endDrawable: Int = when { + measurement.is_failed -> R.drawable.error_24dp + measurement.is_anomaly && measurement.isUploaded -> R.drawable.exclamation_24dp + measurement.is_anomaly -> R.drawable.exclamation_cloudoff + measurement.isUploaded -> R.drawable.tick_green_24dp + else -> R.drawable.tick_green_cloudoff + } + + if (measurement.test_name == WebConnectivity.NAME) { + if (measurement.url != null) { + textView.text = measurement.url.url + textView.setCompoundDrawablesRelativeWithIntrinsicBounds( + measurement.url.getCategoryIcon(textView.context), + 0, + endDrawable, + 0 + ) + } + } else { + when (measurement.getTest().labelResId) { + R.string.Test_Experimental_Fullname -> textView.text = measurement.getTest().name + else -> textView.setText(test.labelResId) + } + textView.setCompoundDrawablesRelativeWithIntrinsicBounds(test.iconResId, 0, endDrawable, 0) + } + } + if (arrayListOf( + Dash.NAME, + Ndt.NAME, + HttpHeaderFieldManipulation.NAME, + HttpInvalidRequestLine.NAME + ).contains(measurement.test_name) + ) { + val c: Context = view.context + view.findViewById(R.id.pref_group).visibility = View.VISIBLE + val data1: TextView = view.findViewById(R.id.data1) + val data2: TextView = view.findViewById(R.id.data2) + view.findViewById(R.id.text).setCompoundDrawablesRelativeWithIntrinsicBounds( + 0, + 0, + if (measurement.is_failed || measurement.isUploaded) 0 else R.drawable.cloudoff, + 0 + ) + when (measurement.test_name) { + Dash.NAME -> { + data1.apply { + setCompoundDrawablesRelativeWithIntrinsicBounds(R.drawable.video_quality, 0, 0, 0) + setText(measurement.getTestKeys().getVideoQuality(true)) + } + data2.visibility = View.GONE + } + + Ndt.NAME -> { + data1.apply { + setCompoundDrawablesRelativeWithIntrinsicBounds(R.drawable.download_black, 0, 0, 0) + text = c.getString( + R.string.twoParam, + measurement.getTestKeys().getDownload(c), + c.getString(measurement.getTestKeys().getDownloadUnit()) + ) + } + data2.apply { + visibility = View.VISIBLE + setCompoundDrawablesRelativeWithIntrinsicBounds(R.drawable.upload_black, 0, 0, 0) + text = c.getString( + R.string.twoParam, + measurement.getTestKeys().getUpload(c), + c.getString(measurement.getTestKeys().getUploadUnit()) + ) + } + } + + HttpHeaderFieldManipulation.NAME, HttpInvalidRequestLine.NAME -> { + data1.apply { + setCompoundDrawablesRelativeWithIntrinsicBounds(R.drawable.test_middle_boxes_small, 0, 0, 0) + (if (measurement.is_anomaly) c.getString(R.string.TestResults_Overview_MiddleBoxes_Found) else c.getString( + R.string.TestResults_Overview_MiddleBoxes_NotFound + )).also { text = it } + } + data2.visibility = View.GONE + } + } + } + } + +} diff --git a/app/src/main/java/org/openobservatory/ooniprobe/common/ProxyProtocol.java b/app/src/main/java/org/openobservatory/ooniprobe/common/ProxyProtocol.java index ed5742a2e..6bd26432e 100644 --- a/app/src/main/java/org/openobservatory/ooniprobe/common/ProxyProtocol.java +++ b/app/src/main/java/org/openobservatory/ooniprobe/common/ProxyProtocol.java @@ -4,7 +4,9 @@ public enum ProxyProtocol { NONE("none"), PSIPHON("psiphon"), - SOCKS5("socks5"); + SOCKS5("socks5"), + HTTP("http"), + HTTPS("https"); private String protocol; diff --git a/app/src/main/java/org/openobservatory/ooniprobe/common/ProxySettings.java b/app/src/main/java/org/openobservatory/ooniprobe/common/ProxySettings.java index e19867f94..65088ccc1 100644 --- a/app/src/main/java/org/openobservatory/ooniprobe/common/ProxySettings.java +++ b/app/src/main/java/org/openobservatory/ooniprobe/common/ProxySettings.java @@ -33,8 +33,10 @@ public static ProxySettings newProxySettings(PreferenceManager pm) throws Invali settings.protocol = ProxyProtocol.NONE; } else if (protocol.equals(ProxyProtocol.PSIPHON.getProtocol())) { settings.protocol = ProxyProtocol.PSIPHON; - } else if (protocol.equals(ProxyProtocol.SOCKS5.getProtocol())) { - settings.protocol = ProxyProtocol.SOCKS5; + } else if (protocol.equals(ProxyProtocol.SOCKS5.getProtocol()) || protocol.equals(ProxyProtocol.HTTP.getProtocol()) || protocol.equals(ProxyProtocol.HTTPS.getProtocol())) { + // ProxyProtocol.valueOf will only accept one of the values in ProxyProtocol + // as in the enum definition(uppercase). + settings.protocol = ProxyProtocol.valueOf(protocol.toUpperCase()); } else { // This is where we will extend the code to add support for // more proxies, e.g., HTTP proxies. @@ -72,16 +74,18 @@ private boolean isIPv6(String hostname) { /** getProxyString returns to you the proxy string you should pass to oonimkall. */ public String getProxyString() throws URISyntaxException { - if (protocol == ProxyProtocol.NONE) + if (protocol == ProxyProtocol.NONE) { return ""; - if (protocol == ProxyProtocol.PSIPHON) + } + if (protocol == ProxyProtocol.PSIPHON) { return "psiphon://"; - if (protocol == ProxyProtocol.SOCKS5) { + } + if (protocol == ProxyProtocol.SOCKS5||protocol == ProxyProtocol.HTTP||protocol == ProxyProtocol.HTTPS) { // Alright, we now need to construct a new SOCKS5 URL. We are going to defer // doing that to the Java standard library (er, the Android stdlib). - String urlStr = "socks5://" + hostname + ":" + port + "/"; + String urlStr = protocol.getProtocol()+"://" + hostname + ":" + port + "/"; if (isIPv6(hostname)) { - urlStr = "socks5://[" + hostname + "]:" + port + "/"; // IPv6 must be quoted in URLs + urlStr = protocol.getProtocol()+"://[" + hostname + "]:" + port + "/"; // IPv6 must be quoted in URLs } URI url = new URI(urlStr); return url.toASCIIString(); diff --git a/app/src/main/java/org/openobservatory/ooniprobe/common/service/RunTestJobService.java b/app/src/main/java/org/openobservatory/ooniprobe/common/service/RunTestJobService.java index 2df95f322..37fe91292 100644 --- a/app/src/main/java/org/openobservatory/ooniprobe/common/service/RunTestJobService.java +++ b/app/src/main/java/org/openobservatory/ooniprobe/common/service/RunTestJobService.java @@ -34,7 +34,7 @@ public JobTask(JobService jobService, Application app) { @Override protected JobParameters doInBackground(JobParameters... params) { - ServiceUtil.callCheckInAPI(app); + ServiceUtil.startRunTestServiceUnattended(app); return params[0]; } diff --git a/app/src/main/java/org/openobservatory/ooniprobe/common/service/RunTestService.java b/app/src/main/java/org/openobservatory/ooniprobe/common/service/RunTestService.java index a7d3e2259..d35a95798 100644 --- a/app/src/main/java/org/openobservatory/ooniprobe/common/service/RunTestService.java +++ b/app/src/main/java/org/openobservatory/ooniprobe/common/service/RunTestService.java @@ -62,6 +62,7 @@ public int onStartCommand(Intent intent, int flags, int startId) { if (testSuites == null || testSuites.size() == 0) return START_STICKY_COMPATIBILITY; boolean store_db = intent.getBooleanExtra("storeDB", true); + boolean unattended = intent.getBooleanExtra("unattended", false); Application app = ((Application) getApplication()); NotificationUtility.setChannel(getApplicationContext(), CHANNEL_ID, app.getString(R.string.Settings_AutomatedTesting_Label), false, false, false); Intent notificationIntent = new Intent(this, RunningActivity.class); @@ -79,7 +80,7 @@ public int onStartCommand(Intent intent, int flags, int startId) { .setProgress(100, 0, false) .build(); - task = (TestAsyncTask) new TestAsyncTask(app, testSuites, store_db).execute(); + task = (TestAsyncTask) new TestAsyncTask(app, testSuites, store_db, unattended).execute(); //This intent is used to manage the stop test button in the notification Intent broadcastIntent = new Intent(); broadcastIntent.setAction(RunTestService.ACTION_INTERRUPT); 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 82b1895de..1f7c0e9e7 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 @@ -68,7 +68,7 @@ public static void stopJob(Context context) { } } - public static void callCheckInAPI(Application app) { + public static void startRunTestServiceUnattended(Application app) { app.getServiceComponent().inject(d); boolean isVPNInUse = ReachabilityManager.isVPNinUse(app); @@ -79,20 +79,24 @@ public static void callCheckInAPI(Application app) { return; } - - AbstractSuite suite = d.generateAutoRunServiceSuite.generate(config); + AbstractSuite suite = d.generateAutoRunServiceSuite.generate(); ArrayList testSuites = new ArrayList<>(); testSuites.add(suite); testSuites.add(InstantMessagingSuite.initForAutoRun()); testSuites.add(CircumventionSuite.initForAutoRun()); testSuites.add(PerformanceSuite.initForAutoRun()); testSuites.add(ExperimentalSuite.initForAutoRun()); - ServiceUtil.startRunTestService(app, testSuites, false); + ServiceUtil.startRunTestServiceCommon(app, testSuites, false, true); + d.generateAutoRunServiceSuite.markAsRan(); } - public static void startRunTestService(Context context, ArrayList iTestSuites, boolean storeDB) { + public static void startRunTestServiceManual(Context context, ArrayList iTestSuites, boolean storeDB) { + startRunTestServiceCommon(context, iTestSuites, storeDB, false); + } + + private static void startRunTestServiceCommon(Context context, ArrayList iTestSuites, boolean storeDB, boolean unattended) { ArrayList testSuites = Lists.newArrayList( Iterables.filter(Iterables.filter(iTestSuites, item -> item != null), testSuite -> !testSuite.isTestEmpty(d.preferenceManager)) ); @@ -100,6 +104,7 @@ public static void startRunTestService(Context context, ArrayList Intent serviceIntent = new Intent(context, RunTestService.class); serviceIntent.putExtra("testSuites", testSuites); serviceIntent.putExtra("storeDB", storeDB); + serviceIntent.putExtra("unattended", unattended); ContextCompat.startForegroundService(context, serviceIntent); } diff --git a/app/src/main/java/org/openobservatory/ooniprobe/domain/GenerateAutoRunServiceSuite.java b/app/src/main/java/org/openobservatory/ooniprobe/domain/GenerateAutoRunServiceSuite.java index b7c4b2579..a511b847e 100644 --- a/app/src/main/java/org/openobservatory/ooniprobe/domain/GenerateAutoRunServiceSuite.java +++ b/app/src/main/java/org/openobservatory/ooniprobe/domain/GenerateAutoRunServiceSuite.java @@ -31,47 +31,17 @@ public class GenerateAutoRunServiceSuite { this.app = application; } - public AbstractSuite generate( - OONICheckInConfig config - ) { - - try { - OONISession session = EngineProvider.get().newSession( - EngineProvider.get().getDefaultSessionConfig( - app, - String.join("-",BuildConfig.SOFTWARE_NAME, AbstractTest.UNATTENDED), - BuildConfig.VERSION_NAME, - new LoggerArray(), - pm.getProxyURL() - ) - ); - OONIContext ooniContext = session.newContextWithTimeout(30); - OONICheckInResults results = session.checkIn(ooniContext, config); - - if (results.getWebConnectivity() != null) { - List inputs = new ArrayList<>(); - for (OONIURLInfo url : results.getWebConnectivity().getUrls()) { - inputs.add(url.getUrl()); - } - - markAsRan(); - - return AbstractSuite.getSuite( - app, - "web_connectivity", - inputs, - AbstractTest.AUTORUN - ); - } - - return null; - } catch (Exception e) { - e.printStackTrace(); - ThirdPartyServices.logException(e); - return null; - } + public AbstractSuite generate() { + + return AbstractSuite.getSuite( + app, + "web_connectivity", + null, + AbstractTest.AUTORUN + ); } + public boolean shouldStart(Boolean isWifi, Boolean isCharging, Boolean isVPNInUse) { if (pm.testWifiOnly() && !isWifi) return false; @@ -86,7 +56,7 @@ public boolean shouldStart(Boolean isWifi, Boolean isCharging, Boolean isVPNInUs return true; } - private void markAsRan() { + public void markAsRan() { pm.updateAutorunDate(); pm.incrementAutorun(); } diff --git a/app/src/main/java/org/openobservatory/ooniprobe/fragment/DashboardFragment.java b/app/src/main/java/org/openobservatory/ooniprobe/fragment/DashboardFragment.java deleted file mode 100644 index 0ebeb4d8d..000000000 --- a/app/src/main/java/org/openobservatory/ooniprobe/fragment/DashboardFragment.java +++ /dev/null @@ -1,149 +0,0 @@ -package org.openobservatory.ooniprobe.fragment; - -import android.content.Intent; -import android.os.Bundle; -import android.text.format.DateUtils; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.TextView; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.appcompat.app.AppCompatActivity; -import androidx.appcompat.widget.Toolbar; -import androidx.core.app.ActivityCompat; -import androidx.fragment.app.Fragment; -import androidx.recyclerview.widget.LinearLayoutManager; -import androidx.recyclerview.widget.RecyclerView; - -import org.openobservatory.ooniprobe.R; -import org.openobservatory.ooniprobe.activity.AbstractActivity; -import org.openobservatory.ooniprobe.activity.MainActivity; -import org.openobservatory.ooniprobe.activity.OverviewActivity; -import org.openobservatory.ooniprobe.activity.RunningActivity; -import org.openobservatory.ooniprobe.common.Application; -import org.openobservatory.ooniprobe.common.PreferenceManager; -import org.openobservatory.ooniprobe.common.ReachabilityManager; -import org.openobservatory.ooniprobe.common.ThirdPartyServices; -import org.openobservatory.ooniprobe.item.SeperatorItem; -import org.openobservatory.ooniprobe.item.TestsuiteItem; -import org.openobservatory.ooniprobe.model.database.Result; -import org.openobservatory.ooniprobe.test.TestAsyncTask; -import org.openobservatory.ooniprobe.test.suite.AbstractSuite; - -import java.util.ArrayList; -import java.util.Objects; - -import javax.inject.Inject; - -import butterknife.BindView; -import butterknife.ButterKnife; -import localhost.toolkit.widget.recyclerview.HeterogeneousRecyclerAdapter; -import localhost.toolkit.widget.recyclerview.HeterogeneousRecyclerItem; - -public class DashboardFragment extends Fragment implements View.OnClickListener { - @BindView(R.id.recycler) RecyclerView recycler; - @BindView(R.id.toolbar) Toolbar toolbar; - @BindView(R.id.last_tested) TextView lastTested; - @BindView(R.id.run_all) TextView runAll; - @BindView(R.id.vpn) TextView vpn; - - @Inject - PreferenceManager preferenceManager; - - private ArrayList items; - private ArrayList testSuites; - private HeterogeneousRecyclerAdapter adapter; - - @Nullable @Override public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, Bundle savedInstanceState) { - View v = inflater.inflate(R.layout.fragment_dashboard, container, false); - ButterKnife.bind(this, v); - ((Application) getActivity().getApplication()).getFragmentComponent().inject(this); - ((AppCompatActivity) getActivity()).setSupportActionBar(toolbar); - ((AppCompatActivity) getActivity()).getSupportActionBar().setTitle(null); - items = new ArrayList<>(); - testSuites = new ArrayList<>(); - adapter = new HeterogeneousRecyclerAdapter<>(getActivity(), items); - recycler.setAdapter(adapter); - recycler.setLayoutManager(new LinearLayoutManager(getActivity())); - runAll.setOnClickListener(v1 -> runAll()); - vpn.setOnClickListener(view -> ((Application) getActivity().getApplication()).openVPNSettings()); - return v; - } - - @Override public void onResume() { - super.onResume(); - items.clear(); - testSuites.clear(); - testSuites.addAll(TestAsyncTask.getSuites()); - - ArrayList emptySuites = new ArrayList<>(); - for (AbstractSuite testSuite : testSuites){ - if(testSuite.getTestList(preferenceManager).length > 0){ - items.add(new TestsuiteItem(testSuite, this, preferenceManager)); - } else { - emptySuites.add(testSuite); - } - } - - if(!emptySuites.isEmpty()){ - items.add(new SeperatorItem()); - - for(AbstractSuite emptyTest: emptySuites) - items.add(new TestsuiteItem(emptyTest, this, preferenceManager)); - } - - - - setLastTest(); - adapter.notifyTypesChanged(); - if (ReachabilityManager.isVPNinUse(this.getContext()) - && preferenceManager.isWarnVPNInUse()) - vpn.setVisibility(View.VISIBLE); - else - vpn.setVisibility(View.GONE); - } - - private void setLastTest() { - Result lastResult = Result.getLastResult(); - if (lastResult == null) - lastTested.setText(getString(R.string.Dashboard_Overview_LatestTest) - + " " + - getString(R.string.Dashboard_Overview_LastRun_Never)); - else - lastTested.setText(getString(R.string.Dashboard_Overview_LatestTest) - + " " + - DateUtils.getRelativeTimeSpanString(lastResult.start_time.getTime())); - } - - public void runAll() { - RunningActivity.runAsForegroundService((AbstractActivity) getActivity(), testSuites, this::onTestServiceStartedListener, preferenceManager); - } - - private void onTestServiceStartedListener() { - try { - ((AbstractActivity) getActivity()).bindTestService(); - } catch (Exception e) { - e.printStackTrace(); - ThirdPartyServices.logException(e); - } - } - - @Override public void onClick(View v) { - AbstractSuite testSuite = (AbstractSuite) v.getTag(); - switch (v.getId()) { - case R.id.run: - RunningActivity.runAsForegroundService( - (AbstractActivity) getActivity(), - testSuite.asArray(), - this::onTestServiceStartedListener, - preferenceManager - ); - break; - default: - ActivityCompat.startActivity(getActivity(), OverviewActivity.newIntent(getActivity(), testSuite), null); - break; - } - } -} diff --git a/app/src/main/java/org/openobservatory/ooniprobe/fragment/DashboardFragment.kt b/app/src/main/java/org/openobservatory/ooniprobe/fragment/DashboardFragment.kt new file mode 100644 index 000000000..a973b1cee --- /dev/null +++ b/app/src/main/java/org/openobservatory/ooniprobe/fragment/DashboardFragment.kt @@ -0,0 +1,120 @@ +package org.openobservatory.ooniprobe.fragment + +import android.os.Bundle +import android.text.format.DateUtils +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.appcompat.app.AppCompatActivity +import androidx.core.app.ActivityCompat +import androidx.fragment.app.Fragment +import androidx.recyclerview.widget.LinearLayoutManager +import org.openobservatory.ooniprobe.R +import org.openobservatory.ooniprobe.activity.AbstractActivity +import org.openobservatory.ooniprobe.activity.OverviewActivity +import org.openobservatory.ooniprobe.activity.RunningActivity +import org.openobservatory.ooniprobe.adapters.DashboardAdapter +import org.openobservatory.ooniprobe.common.Application +import org.openobservatory.ooniprobe.common.PreferenceManager +import org.openobservatory.ooniprobe.common.ReachabilityManager +import org.openobservatory.ooniprobe.common.ThirdPartyServices +import org.openobservatory.ooniprobe.databinding.FragmentDashboardBinding +import org.openobservatory.ooniprobe.fragment.dashboard.DashboardViewModel +import org.openobservatory.ooniprobe.model.database.Result +import org.openobservatory.ooniprobe.test.suite.AbstractSuite +import javax.inject.Inject + +class DashboardFragment : Fragment(), View.OnClickListener { + @Inject + lateinit var preferenceManager: PreferenceManager + + @Inject + lateinit var viewModel: DashboardViewModel + private var testSuites: ArrayList = ArrayList() + private lateinit var binding: FragmentDashboardBinding + + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ): View { + binding = FragmentDashboardBinding.inflate(inflater, container, false) + (requireActivity().application as Application).fragmentComponent.inject(this) + (requireActivity() as AppCompatActivity).apply { + setSupportActionBar(binding.toolbar) + supportActionBar?.title = null + } + binding.apply { + runAll.setOnClickListener { _: View? -> runAll() } + vpn.setOnClickListener { _: View? -> (requireActivity().application as Application).openVPNSettings() } + } + return binding.root + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + viewModel.getGroupedItemList().observe(viewLifecycleOwner) { items -> + binding.recycler.layoutManager = LinearLayoutManager(requireContext()) + binding.recycler.adapter = DashboardAdapter(items, this, preferenceManager) + } + + viewModel.items.observe(viewLifecycleOwner) { items -> + testSuites.apply { + clear() + addAll(items) + } + } + } + + override fun onResume() { + super.onResume() + setLastTest() + if (ReachabilityManager.isVPNinUse(this.context) + && preferenceManager.isWarnVPNInUse + ) binding.vpn.visibility = View.VISIBLE else binding.vpn.visibility = View.GONE + } + + private fun setLastTest() { + val lastResult = Result.getLastResult() + if (lastResult == null) { + (getString(R.string.Dashboard_Overview_LatestTest) + " " + getString(R.string.Dashboard_Overview_LastRun_Never)) + .also { binding.lastTested.text = it } + } else { + (getString(R.string.Dashboard_Overview_LatestTest) + " " + DateUtils.getRelativeTimeSpanString(lastResult.start_time.time)) + .also { binding.lastTested.text = it } + } + } + + private fun runAll() { + RunningActivity.runAsForegroundService( + activity as AbstractActivity?, + testSuites, + { onTestServiceStartedListener() }, + preferenceManager + ) + } + + private fun onTestServiceStartedListener() = try { + (requireActivity() as AbstractActivity).bindTestService() + } catch (e: Exception) { + e.printStackTrace() + ThirdPartyServices.logException(e) + } + + override fun onClick(v: View) { + val testSuite = v.tag as AbstractSuite + when (v.id) { + R.id.run -> RunningActivity.runAsForegroundService( + activity as AbstractActivity?, + testSuite.asArray(), { onTestServiceStartedListener() }, + preferenceManager + ) + + else -> ActivityCompat.startActivity( + requireActivity(), + OverviewActivity.newIntent(activity, testSuite), + null + ) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/org/openobservatory/ooniprobe/fragment/ProgressFragment.java b/app/src/main/java/org/openobservatory/ooniprobe/fragment/ProgressFragment.java index a4f948a15..b37840428 100644 --- a/app/src/main/java/org/openobservatory/ooniprobe/fragment/ProgressFragment.java +++ b/app/src/main/java/org/openobservatory/ooniprobe/fragment/ProgressFragment.java @@ -5,50 +5,35 @@ import android.content.Intent; import android.content.IntentFilter; import android.os.Bundle; - -import androidx.core.app.ActivityCompat; -import androidx.fragment.app.Fragment; -import androidx.localbroadcastmanager.content.LocalBroadcastManager; - import android.view.LayoutInflater; -import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; -import android.widget.FrameLayout; -import android.widget.ProgressBar; -import android.widget.TextView; - +import androidx.core.app.ActivityCompat; +import androidx.fragment.app.Fragment; +import androidx.localbroadcastmanager.content.LocalBroadcastManager; import org.openobservatory.ooniprobe.R; import org.openobservatory.ooniprobe.activity.RunningActivity; import org.openobservatory.ooniprobe.common.Application; import org.openobservatory.ooniprobe.common.PreferenceManager; import org.openobservatory.ooniprobe.common.TestProgressRepository; import org.openobservatory.ooniprobe.common.service.RunTestService; +import org.openobservatory.ooniprobe.databinding.FragmentProgressBinding; import org.openobservatory.ooniprobe.receiver.TestRunBroadRequestReceiver; import javax.inject.Inject; -import butterknife.BindView; -import butterknife.ButterKnife; - /** * Monitors and displays progress of {@link RunTestService}. */ public class ProgressFragment extends Fragment { private TestRunBroadRequestReceiver receiver; + private FragmentProgressBinding biding; + @Inject PreferenceManager preferenceManager; @Inject TestProgressRepository testProgressRepository; - @BindView(R.id.progress_layout) - FrameLayout progress_layout; - @BindView(R.id.progress) - ProgressBar progress; - @BindView(R.id.running) - TextView running; - @BindView(R.id.name) - TextView name; public ProgressFragment() { // Required empty public constructor @@ -62,24 +47,18 @@ public void onCreate(Bundle savedInstanceState) { @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - View v = inflater.inflate(R.layout.fragment_progress, container, false); - ButterKnife.bind(this, v); + biding = FragmentProgressBinding.inflate(inflater, container, false); ((Application) getActivity().getApplication()).getFragmentComponent().inject(this); - v.setOnTouchListener(new View.OnTouchListener() { - public boolean onTouch(View v, MotionEvent event) { - if(event.getAction() == MotionEvent.ACTION_DOWN){ - Intent intent = new Intent(getContext(), RunningActivity.class); - ActivityCompat.startActivity(getActivity(), intent, null); - } - return true; - } + biding.getRoot().setOnClickListener(v -> { + Intent intent = new Intent(getContext(), RunningActivity.class); + ActivityCompat.startActivity(getActivity(), intent, null); }); testProgressRepository.getProgress().observe(getViewLifecycleOwner(),progressValue -> { if (progressValue!=null) { - progress.setProgress(progressValue); + biding.progress.setProgress(progressValue); } }); - return v; + return biding.getRoot(); } @Override @@ -97,10 +76,10 @@ public void bindTestService() { if (activity!=null && ((Application)activity.getApplication()).isTestRunning()) { Intent intent = new Intent(getActivity(), RunTestService.class); getActivity().bindService(intent, receiver, Context.BIND_AUTO_CREATE); - progress_layout.setVisibility(View.VISIBLE); + biding.progressLayout.setVisibility(View.VISIBLE); } else - progress_layout.setVisibility(View.GONE); + biding.progressLayout.setVisibility(View.GONE); } private void updateUI(RunTestService service){ @@ -109,15 +88,15 @@ private void updateUI(RunTestService service){ Integer progressLevel = testProgressRepository.getProgress().getValue(); if (progressLevel != null) { - progress.setProgress(progressLevel); + biding.progress.setProgress(progressLevel); } else { - progress.setIndeterminate(true); + biding.progress.setIndeterminate(true); } if (service != null && service.task != null){ if (service.task.currentSuite != null) - progress.setMax(service.task.getMax(preferenceManager)); + biding.progress.setMax(service.task.getMax(preferenceManager)); if (service.task.currentTest != null) - name.setText(getString(service.task.currentTest.getLabelResId())); + biding.name.setText(getString(service.task.currentTest.getLabelResId())); } } } @@ -146,15 +125,15 @@ public void onStart(RunTestService service) { @Override public void onRun(String value) { - name.setText(value); + biding.name.setText(value); } @Override public void onProgress(int state, double eta) { - if (progress.isIndeterminate()) + if (biding.progress.isIndeterminate()) updateUI(receiver.service); - progress.setIndeterminate(false); - progress.setProgress(state); + biding.progress.setIndeterminate(false); + biding.progress.setProgress(state); } @Override @@ -169,17 +148,17 @@ public void onError(String value) { @Override public void onUrl() { - progress.setIndeterminate(false); + biding.progress.setIndeterminate(false); } @Override public void onInterrupt() { - running.setText(getString(R.string.Dashboard_Running_Stopping_Title)); + biding.running.setText(getString(R.string.Dashboard_Running_Stopping_Title)); } @Override public void onEnd(Context context) { - progress_layout.setVisibility(View.GONE); + biding.progressLayout.setVisibility(View.GONE); } } } \ No newline at end of file diff --git a/app/src/main/java/org/openobservatory/ooniprobe/fragment/ResultListFragment.java b/app/src/main/java/org/openobservatory/ooniprobe/fragment/ResultListFragment.java index 2cb061428..45cd07776 100644 --- a/app/src/main/java/org/openobservatory/ooniprobe/fragment/ResultListFragment.java +++ b/app/src/main/java/org/openobservatory/ooniprobe/fragment/ResultListFragment.java @@ -3,31 +3,20 @@ import android.app.ProgressDialog; import android.content.DialogInterface; import android.os.Bundle; -import android.view.LayoutInflater; -import android.view.Menu; -import android.view.MenuInflater; -import android.view.MenuItem; -import android.view.View; -import android.view.ViewGroup; -import android.widget.Spinner; -import android.widget.TextView; - +import android.view.*; +import android.widget.AdapterView; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.appcompat.app.AppCompatActivity; -import androidx.appcompat.widget.Toolbar; -import androidx.coordinatorlayout.widget.CoordinatorLayout; import androidx.core.app.ActivityCompat; import androidx.fragment.app.Fragment; import androidx.lifecycle.ViewModelProvider; import androidx.recyclerview.widget.DividerItemDecoration; import androidx.recyclerview.widget.LinearLayoutManager; -import androidx.recyclerview.widget.RecyclerView; - import com.google.android.material.snackbar.Snackbar; import com.raizlabs.android.dbflow.sql.language.Method; import com.raizlabs.android.dbflow.sql.language.SQLite; - +import localhost.toolkit.app.fragment.ConfirmDialogFragment; import org.openobservatory.ooniprobe.R; import org.openobservatory.ooniprobe.activity.AbstractActivity; import org.openobservatory.ooniprobe.activity.ResultDetailActivity; @@ -37,72 +26,62 @@ import org.openobservatory.ooniprobe.common.Application; import org.openobservatory.ooniprobe.common.PreferenceManager; import org.openobservatory.ooniprobe.common.ResubmitTask; +import org.openobservatory.ooniprobe.databinding.FragmentResultListBinding; import org.openobservatory.ooniprobe.domain.GetResults; import org.openobservatory.ooniprobe.domain.MeasurementsManager; import org.openobservatory.ooniprobe.model.database.Network; import org.openobservatory.ooniprobe.model.database.Result; import org.openobservatory.ooniprobe.model.database.Result_Table; +import javax.inject.Inject; import java.io.Serializable; import java.lang.ref.WeakReference; -import javax.inject.Inject; - -import butterknife.BindView; -import butterknife.ButterKnife; -import butterknife.OnItemSelected; -import localhost.toolkit.app.fragment.ConfirmDialogFragment; - public class ResultListFragment extends Fragment implements View.OnClickListener, View.OnLongClickListener, ConfirmDialogFragment.OnConfirmedListener { - @BindView(R.id.coordinatorLayout) - CoordinatorLayout coordinatorLayout; - @BindView(R.id.toolbar) - Toolbar toolbar; - @BindView(R.id.tests) - TextView tests; - @BindView(R.id.networks) - TextView networks; - @BindView(R.id.upload) - TextView upload; - @BindView(R.id.download) - TextView download; - @BindView(R.id.filterTests) - Spinner filterTests; - @BindView(R.id.recycler) - RecyclerView recycler; - @BindView(R.id.emptyState) - TextView emptyState; + private FragmentResultListBinding binding; + private ResultListAdapter adapter; + private boolean refresh; + private Snackbar snackbar; + @Inject MeasurementsManager measurementsManager; @Inject GetResults getResults; @Inject PreferenceManager pm; - - private ResultListAdapter mAdapter; - private boolean refresh; - private Snackbar snackbar; - private ResultListViewModel mViewModel; + private ResultListViewModel viewModel; @Nullable @Override public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, Bundle savedInstanceState) { - View v = inflater.inflate(R.layout.fragment_result_list, container, false); - ButterKnife.bind(this, v); + binding = FragmentResultListBinding.inflate(inflater, container, false); ((Application) getActivity().getApplication()).getFragmentComponent().inject(this); - ((AppCompatActivity) getActivity()).setSupportActionBar(toolbar); - // Create ViewModel - mViewModel = new ViewModelProvider(this).get(ResultListViewModel.class); + ((AppCompatActivity) getActivity()).setSupportActionBar(binding.toolbar); + + viewModel = new ViewModelProvider(this).get(ResultListViewModel.class); setHasOptionsMenu(true); getActivity().setTitle(R.string.TestResults_Overview_Title); reloadHeader(); LinearLayoutManager layoutManager = new LinearLayoutManager(getActivity()); - recycler.setLayoutManager(layoutManager); - recycler.addItemDecoration(new DividerItemDecoration(getActivity(), layoutManager.getOrientation())); - mAdapter = new ResultListAdapter(new ResultComparator(), this, this); - recycler.setAdapter(mAdapter); - snackbar = Snackbar.make(coordinatorLayout, R.string.Snackbar_ResultsSomeNotUploaded_Text, Snackbar.LENGTH_INDEFINITE) + binding.recycler.setLayoutManager(layoutManager); + binding.recycler.addItemDecoration(new DividerItemDecoration(getActivity(), layoutManager.getOrientation())); + adapter = new ResultListAdapter(new ResultComparator(), this, this); + binding.recycler.setAdapter(adapter); + + binding.filterTests.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { + @Override + public void onItemSelected(AdapterView parent, View view, int position, long id) { + queryList(); + } + + @Override + public void onNothingSelected(AdapterView parent) { + queryList(); + } + }); + + snackbar = Snackbar.make(binding.coordinatorLayout, R.string.Snackbar_ResultsSomeNotUploaded_Text, Snackbar.LENGTH_INDEFINITE) .setAction(R.string.Snackbar_ResultsSomeNotUploaded_UploadAll, v1 -> new ConfirmDialogFragment.Builder() .withExtra(R.string.Modal_ResultsNotUploaded_Title) @@ -111,14 +90,14 @@ public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup c .withPositiveButton(getString(R.string.Modal_ResultsNotUploaded_Button_Upload)) .build().show(getChildFragmentManager(), null) ); - return v; + return binding.getRoot(); } public void reloadHeader() { - tests.setText(getString(R.string.d, SQLite.selectCountOf().from(Result.class).longValue())); - networks.setText(getString(R.string.d, SQLite.selectCountOf().from(Network.class).longValue())); - upload.setText(Result.readableFileSize(SQLite.select(Method.sum(Result_Table.data_usage_up)).from(Result.class).longValue())); - download.setText(Result.readableFileSize(SQLite.select(Method.sum(Result_Table.data_usage_down)).from(Result.class).longValue())); + binding.tests.setText(getString(R.string.d, SQLite.selectCountOf().from(Result.class).longValue())); + binding.networks.setText(getString(R.string.d, SQLite.selectCountOf().from(Network.class).longValue())); + binding.upload.setText(Result.readableFileSize(SQLite.select(Method.sum(Result_Table.data_usage_up)).from(Result.class).longValue())); + binding.download.setText(Result.readableFileSize(SQLite.select(Method.sum(Result_Table.data_usage_down)).from(Result.class).longValue())); } @Override @@ -157,7 +136,6 @@ public boolean onOptionsItemSelected(MenuItem item) { } } - @OnItemSelected(R.id.filterTests) void queryList() { if (measurementsManager.hasUploadables()) { snackbar.show(); @@ -165,10 +143,10 @@ void queryList() { snackbar.dismiss(); } - String filter = getResources().getStringArray(R.array.filterTestValues)[filterTests.getSelectedItemPosition()]; - mViewModel.init(filter); - mViewModel.pagingData.observe(getViewLifecycleOwner(), resultPagingData -> { - mAdapter.submitData(getLifecycle(), resultPagingData); + String filter = getResources().getStringArray(R.array.filterTestValues)[binding.filterTests.getSelectedItemPosition()]; + viewModel.init(filter); + viewModel.pagingData.observe(getViewLifecycleOwner(), resultPagingData -> { + adapter.submitData(getLifecycle(), resultPagingData); }); } diff --git a/app/src/main/java/org/openobservatory/ooniprobe/fragment/dashboard/DashboardViewModel.kt b/app/src/main/java/org/openobservatory/ooniprobe/fragment/dashboard/DashboardViewModel.kt new file mode 100644 index 000000000..009a3cab0 --- /dev/null +++ b/app/src/main/java/org/openobservatory/ooniprobe/fragment/dashboard/DashboardViewModel.kt @@ -0,0 +1,44 @@ +package org.openobservatory.ooniprobe.fragment.dashboard + +import androidx.lifecycle.LiveData +import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.ViewModel +import org.openobservatory.ooniprobe.common.PreferenceManager +import org.openobservatory.ooniprobe.test.TestAsyncTask +import org.openobservatory.ooniprobe.test.suite.AbstractSuite +import javax.inject.Inject + +class DashboardViewModel @Inject constructor(private val preferenceManager: PreferenceManager) : ViewModel() { + private val enabledTitle: String = "Enabled" + private val groupedItemList = MutableLiveData>() + val items = MutableLiveData>(TestAsyncTask.getSuites()) + + fun getGroupedItemList(): LiveData> { + if (groupedItemList.value == null) { + fetchItemList() + } + return groupedItemList + } + + private fun fetchItemList() { + + val groupedItems = items.value!!.sortedBy { it.getTestList(preferenceManager).isEmpty() } + .groupBy { + return@groupBy if ((it.getTestList(preferenceManager).isNotEmpty())) { + enabledTitle + } else { + "" + } + } + + val groupedItemList = mutableListOf() + groupedItems.forEach { (status, itemList) -> + if (status != enabledTitle){ + groupedItemList.add(status) + } + groupedItemList.addAll(itemList) + } + + this.groupedItemList.value = groupedItemList + } +} \ No newline at end of file diff --git a/app/src/main/java/org/openobservatory/ooniprobe/fragment/measurement/DashFragment.java b/app/src/main/java/org/openobservatory/ooniprobe/fragment/measurement/DashFragment.java index d5f29b7e9..ed6d8c58d 100644 --- a/app/src/main/java/org/openobservatory/ooniprobe/fragment/measurement/DashFragment.java +++ b/app/src/main/java/org/openobservatory/ooniprobe/fragment/measurement/DashFragment.java @@ -5,22 +5,15 @@ import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; -import android.widget.TextView; - import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.fragment.app.Fragment; - import org.openobservatory.ooniprobe.R; +import org.openobservatory.ooniprobe.databinding.FragmentMeasurementDashBinding; import org.openobservatory.ooniprobe.model.database.Measurement; -import butterknife.BindView; -import butterknife.ButterKnife; - public class DashFragment extends Fragment { private static final String MEASUREMENT = "measurement"; - @BindView(R.id.medianBitrate) TextView medianBitrate; - @BindView(R.id.playoutDelay) TextView playoutDelay; public static DashFragment newInstance(Measurement measurement) { Bundle args = new Bundle(); @@ -30,14 +23,15 @@ public static DashFragment newInstance(Measurement measurement) { return fragment; } - @Nullable @Override public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, Bundle savedInstanceState) { + @Nullable + @Override + public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, Bundle savedInstanceState) { assert getArguments() != null; Measurement measurement = (Measurement) getArguments().getSerializable(MEASUREMENT); assert measurement != null; - View v = inflater.inflate(R.layout.fragment_measurement_dash, container, false); - ButterKnife.bind(this, v); - medianBitrate.setText(Html.fromHtml(getString(R.string.bigNormal, measurement.getTestKeys().getMedianBitrate(getActivity()), getString(measurement.getTestKeys().getMedianBitrateUnit())))); - playoutDelay.setText(Html.fromHtml(getString(R.string.bigNormal, measurement.getTestKeys().getPlayoutDelay(getActivity()), "s"))); - return v; + FragmentMeasurementDashBinding binding = FragmentMeasurementDashBinding.inflate(inflater,container,false); + binding.medianBitrate.setText(Html.fromHtml(getString(R.string.bigNormal, measurement.getTestKeys().getMedianBitrate(getActivity()), getString(measurement.getTestKeys().getMedianBitrateUnit())))); + binding.playoutDelay.setText(Html.fromHtml(getString(R.string.bigNormal, measurement.getTestKeys().getPlayoutDelay(getActivity()), "s"))); + return binding.getRoot(); } } diff --git a/app/src/main/java/org/openobservatory/ooniprobe/fragment/measurement/FacebookMessengerFragment.java b/app/src/main/java/org/openobservatory/ooniprobe/fragment/measurement/FacebookMessengerFragment.java index 4792e9963..b3613c949 100644 --- a/app/src/main/java/org/openobservatory/ooniprobe/fragment/measurement/FacebookMessengerFragment.java +++ b/app/src/main/java/org/openobservatory/ooniprobe/fragment/measurement/FacebookMessengerFragment.java @@ -4,25 +4,16 @@ import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; -import android.widget.TextView; - import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.core.content.ContextCompat; import androidx.fragment.app.Fragment; - import org.openobservatory.ooniprobe.R; +import org.openobservatory.ooniprobe.databinding.FragmentMeasurementFacebookmessengerBinding; import org.openobservatory.ooniprobe.model.database.Measurement; -import butterknife.BindView; -import butterknife.ButterKnife; - public class FacebookMessengerFragment extends Fragment { private static final String MEASUREMENT = "measurement"; - @BindView(R.id.tcp) TextView tcp; - @BindView(R.id.dns) TextView dns; - @BindView(R.id.desc) TextView desc; - public static FacebookMessengerFragment newInstance(Measurement measurement) { Bundle args = new Bundle(); args.putSerializable(MEASUREMENT, measurement); @@ -31,19 +22,20 @@ public static FacebookMessengerFragment newInstance(Measurement measurement) { return fragment; } - @Nullable @Override public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, Bundle savedInstanceState) { + @Nullable + @Override + public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, Bundle savedInstanceState) { assert getArguments() != null; Measurement measurement = (Measurement) getArguments().getSerializable(MEASUREMENT); assert measurement != null; - View v = inflater.inflate(R.layout.fragment_measurement_facebookmessenger, container, false); - ButterKnife.bind(this, v); - desc.setText(measurement.is_anomaly ? R.string.TestResults_Details_InstantMessaging_FacebookMessenger_LikelyBlocked_Content_Paragraph : R.string.TestResults_Details_InstantMessaging_FacebookMessenger_Reachable_Content_Paragraph); - dns.setText(measurement.getTestKeys().getFacebookMessengerDns()); + FragmentMeasurementFacebookmessengerBinding binding = FragmentMeasurementFacebookmessengerBinding.inflate(inflater,container,false); + binding.desc.setText(measurement.is_anomaly ? R.string.TestResults_Details_InstantMessaging_FacebookMessenger_LikelyBlocked_Content_Paragraph : R.string.TestResults_Details_InstantMessaging_FacebookMessenger_Reachable_Content_Paragraph); + binding.dns.setText(measurement.getTestKeys().getFacebookMessengerDns()); if (Boolean.TRUE.equals(measurement.getTestKeys().facebook_dns_blocking)) - dns.setTextColor(ContextCompat.getColor(getActivity(), R.color.color_yellow9)); - tcp.setText(measurement.getTestKeys().getFacebookMessengerTcp()); + binding.dns.setTextColor(ContextCompat.getColor(getActivity(), R.color.color_yellow9)); + binding.tcp.setText(measurement.getTestKeys().getFacebookMessengerTcp()); if (Boolean.TRUE.equals(measurement.getTestKeys().facebook_tcp_blocking)) - tcp.setTextColor(ContextCompat.getColor(getActivity(), R.color.color_yellow9)); - return v; + binding.tcp.setTextColor(ContextCompat.getColor(getActivity(), R.color.color_yellow9)); + return binding.getRoot(); } } diff --git a/app/src/main/java/org/openobservatory/ooniprobe/fragment/measurement/FailedFragment.java b/app/src/main/java/org/openobservatory/ooniprobe/fragment/measurement/FailedFragment.java index 6b04f8c92..ba9f6f98a 100644 --- a/app/src/main/java/org/openobservatory/ooniprobe/fragment/measurement/FailedFragment.java +++ b/app/src/main/java/org/openobservatory/ooniprobe/fragment/measurement/FailedFragment.java @@ -37,13 +37,15 @@ public static FailedFragment newInstance(Measurement measurement) { return fragment; } - @Nullable @Override public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, Bundle savedInstanceState) { + @Nullable + @Override + public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, Bundle savedInstanceState) { binding = FragmentMeasurementFailedBinding.inflate(inflater,container,false); binding.tryAgain.setOnClickListener(this::tryAgainClick); return binding.getRoot(); } - void tryAgainClick(View view) { + void tryAgainClick(View view) { assert getArguments() != null; Measurement failedMeasurement = (Measurement) getArguments().getSerializable(MEASUREMENT); assert failedMeasurement != null; diff --git a/app/src/main/java/org/openobservatory/ooniprobe/fragment/measurement/HeaderNdtFragment.java b/app/src/main/java/org/openobservatory/ooniprobe/fragment/measurement/HeaderNdtFragment.java index 04bab8f95..2467cf99e 100644 --- a/app/src/main/java/org/openobservatory/ooniprobe/fragment/measurement/HeaderNdtFragment.java +++ b/app/src/main/java/org/openobservatory/ooniprobe/fragment/measurement/HeaderNdtFragment.java @@ -5,24 +5,15 @@ import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; -import android.widget.TextView; - import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.fragment.app.Fragment; - import org.openobservatory.ooniprobe.R; +import org.openobservatory.ooniprobe.databinding.FragmentMeasurementHeaderNdtBinding; import org.openobservatory.ooniprobe.model.database.Measurement; -import butterknife.BindView; -import butterknife.ButterKnife; - public class HeaderNdtFragment extends Fragment { private static final String MEASUREMENT = "measurement"; - @BindView(R.id.download) TextView download; - @BindView(R.id.upload) TextView upload; - @BindView(R.id.ping) TextView ping; - @BindView(R.id.server) TextView server; public static HeaderNdtFragment newInstance(Measurement measurement) { Bundle args = new Bundle(); @@ -32,16 +23,17 @@ public static HeaderNdtFragment newInstance(Measurement measurement) { return fragment; } - @Nullable @Override public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, Bundle savedInstanceState) { + @Nullable + @Override + public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, Bundle savedInstanceState) { assert getArguments() != null; Measurement measurement = (Measurement) getArguments().getSerializable(MEASUREMENT); assert measurement != null; - View v = inflater.inflate(R.layout.fragment_measurement_header_ndt, container, false); - ButterKnife.bind(this, v); - download.setText(Html.fromHtml(getString(R.string.bigNormal, measurement.getTestKeys().getDownload(getActivity()), getString(measurement.getTestKeys().getDownloadUnit())))); - upload.setText(Html.fromHtml(getString(R.string.bigNormal, measurement.getTestKeys().getUpload(getActivity()), getString(measurement.getTestKeys().getUploadUnit())))); - ping.setText(Html.fromHtml(getString(R.string.bigNormal, measurement.getTestKeys().getPing(getActivity()), "ms"))); - server.setText(measurement.getTestKeys().getServerDetails(getActivity())); - return v; + FragmentMeasurementHeaderNdtBinding binding = FragmentMeasurementHeaderNdtBinding.inflate(inflater,container,false); + binding.download.setText(Html.fromHtml(getString(R.string.bigNormal, measurement.getTestKeys().getDownload(getActivity()), getString(measurement.getTestKeys().getDownloadUnit())))); + binding.upload.setText(Html.fromHtml(getString(R.string.bigNormal, measurement.getTestKeys().getUpload(getActivity()), getString(measurement.getTestKeys().getUploadUnit())))); + binding.ping.setText(Html.fromHtml(getString(R.string.bigNormal, measurement.getTestKeys().getPing(getActivity()), "ms"))); + binding.server.setText(measurement.getTestKeys().getServerDetails(getActivity())); + return binding.getRoot(); } } diff --git a/app/src/main/java/org/openobservatory/ooniprobe/fragment/measurement/HeaderOutcomeFragment.java b/app/src/main/java/org/openobservatory/ooniprobe/fragment/measurement/HeaderOutcomeFragment.java index 164b9361c..52af50b58 100644 --- a/app/src/main/java/org/openobservatory/ooniprobe/fragment/measurement/HeaderOutcomeFragment.java +++ b/app/src/main/java/org/openobservatory/ooniprobe/fragment/measurement/HeaderOutcomeFragment.java @@ -5,21 +5,14 @@ import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; -import android.widget.TextView; - import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.fragment.app.Fragment; - -import org.openobservatory.ooniprobe.R; - -import butterknife.BindView; -import butterknife.ButterKnife; +import org.openobservatory.ooniprobe.databinding.FragmentMeasurementHeaderOutcomeBinding; public class HeaderOutcomeFragment extends Fragment { public static final String ICON_RES = "iconRes"; private static final String DESC = "desc"; - @BindView(R.id.outcome) TextView outcome; public static HeaderOutcomeFragment newInstance(Integer iconRes, String desc) { Bundle args = new Bundle(); @@ -31,13 +24,14 @@ public static HeaderOutcomeFragment newInstance(Integer iconRes, String desc) { return fragment; } - @Nullable @Override public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { + @Nullable + @Override + public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { assert getArguments() != null; - View v = inflater.inflate(R.layout.fragment_measurement_header_outcome, container, false); - ButterKnife.bind(this, v); - outcome.setText(Html.fromHtml(getArguments().getString(DESC))); + FragmentMeasurementHeaderOutcomeBinding binding = FragmentMeasurementHeaderOutcomeBinding.inflate(inflater,container,false); + binding.outcome.setText(Html.fromHtml(getArguments().getString(DESC))); if (getArguments().containsKey(ICON_RES)) - outcome.setCompoundDrawablesRelativeWithIntrinsicBounds(0, getArguments().getInt(ICON_RES), 0, 0); - return v; + binding.outcome.setCompoundDrawablesRelativeWithIntrinsicBounds(0, getArguments().getInt(ICON_RES), 0, 0); + return binding.getRoot(); } } diff --git a/app/src/main/java/org/openobservatory/ooniprobe/fragment/measurement/HttpHeaderFieldManipulationFragment.java b/app/src/main/java/org/openobservatory/ooniprobe/fragment/measurement/HttpHeaderFieldManipulationFragment.java index 7dccf0034..aca7b2cd6 100644 --- a/app/src/main/java/org/openobservatory/ooniprobe/fragment/measurement/HttpHeaderFieldManipulationFragment.java +++ b/app/src/main/java/org/openobservatory/ooniprobe/fragment/measurement/HttpHeaderFieldManipulationFragment.java @@ -4,21 +4,15 @@ import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; -import android.widget.TextView; - import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.fragment.app.Fragment; - import org.openobservatory.ooniprobe.R; +import org.openobservatory.ooniprobe.databinding.FragmentMeasurementHttpheaderfieldmanipulationBinding; import org.openobservatory.ooniprobe.model.database.Measurement; -import butterknife.BindView; -import butterknife.ButterKnife; - public class HttpHeaderFieldManipulationFragment extends Fragment { private static final String MEASUREMENT = "measurement"; - @BindView(R.id.desc) TextView desc; public static HttpHeaderFieldManipulationFragment newInstance(Measurement measurement) { Bundle args = new Bundle(); @@ -28,13 +22,14 @@ public static HttpHeaderFieldManipulationFragment newInstance(Measurement measur return fragment; } - @Nullable @Override public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, Bundle savedInstanceState) { + @Nullable + @Override + public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, Bundle savedInstanceState) { assert getArguments() != null; Measurement measurement = (Measurement) getArguments().getSerializable(MEASUREMENT); assert measurement != null; - View v = inflater.inflate(R.layout.fragment_measurement_httpheaderfieldmanipulation, container, false); - ButterKnife.bind(this, v); - desc.setText(measurement.is_anomaly ? R.string.TestResults_Details_Middleboxes_HTTPHeaderFieldManipulation_Found_Content_Paragraph : R.string.TestResults_Details_Middleboxes_HTTPHeaderFieldManipulation_NotFound_Content_Paragraph); - return v; + FragmentMeasurementHttpheaderfieldmanipulationBinding binding = FragmentMeasurementHttpheaderfieldmanipulationBinding.inflate(inflater,container,false); + binding.desc.setText(measurement.is_anomaly ? R.string.TestResults_Details_Middleboxes_HTTPHeaderFieldManipulation_Found_Content_Paragraph : R.string.TestResults_Details_Middleboxes_HTTPHeaderFieldManipulation_NotFound_Content_Paragraph); + return binding.getRoot(); } } diff --git a/app/src/main/java/org/openobservatory/ooniprobe/fragment/measurement/HttpInvalidRequestLineFragment.java b/app/src/main/java/org/openobservatory/ooniprobe/fragment/measurement/HttpInvalidRequestLineFragment.java index 0c2135abf..aa3015b3e 100644 --- a/app/src/main/java/org/openobservatory/ooniprobe/fragment/measurement/HttpInvalidRequestLineFragment.java +++ b/app/src/main/java/org/openobservatory/ooniprobe/fragment/measurement/HttpInvalidRequestLineFragment.java @@ -4,21 +4,15 @@ import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; -import android.widget.TextView; - import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.fragment.app.Fragment; - import org.openobservatory.ooniprobe.R; +import org.openobservatory.ooniprobe.databinding.FragmentMeasurementHttpinvalidrequestlineBinding; import org.openobservatory.ooniprobe.model.database.Measurement; -import butterknife.BindView; -import butterknife.ButterKnife; - public class HttpInvalidRequestLineFragment extends Fragment { private static final String MEASUREMENT = "measurement"; - @BindView(R.id.desc) TextView desc; public static HttpInvalidRequestLineFragment newInstance(Measurement measurement) { Bundle args = new Bundle(); @@ -28,13 +22,14 @@ public static HttpInvalidRequestLineFragment newInstance(Measurement measurement return fragment; } - @Nullable @Override public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, Bundle savedInstanceState) { + @Nullable + @Override + public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, Bundle savedInstanceState) { assert getArguments() != null; Measurement measurement = (Measurement) getArguments().getSerializable(MEASUREMENT); assert measurement != null; - View v = inflater.inflate(R.layout.fragment_measurement_httpinvalidrequestline, container, false); - ButterKnife.bind(this, v); - desc.setText(measurement.is_anomaly ? R.string.TestResults_Details_Middleboxes_HTTPInvalidRequestLine_Found_Content_Paragraph : R.string.TestResults_Details_Middleboxes_HTTPInvalidRequestLine_NotFound_Content_Paragraph); - return v; + FragmentMeasurementHttpinvalidrequestlineBinding biding = FragmentMeasurementHttpinvalidrequestlineBinding.inflate(inflater,container,false); + biding.desc.setText(measurement.is_anomaly ? R.string.TestResults_Details_Middleboxes_HTTPInvalidRequestLine_Found_Content_Paragraph : R.string.TestResults_Details_Middleboxes_HTTPInvalidRequestLine_NotFound_Content_Paragraph); + return biding.getRoot(); } } diff --git a/app/src/main/java/org/openobservatory/ooniprobe/fragment/measurement/NdtFragment.java b/app/src/main/java/org/openobservatory/ooniprobe/fragment/measurement/NdtFragment.java index 16596a393..1223c562f 100644 --- a/app/src/main/java/org/openobservatory/ooniprobe/fragment/measurement/NdtFragment.java +++ b/app/src/main/java/org/openobservatory/ooniprobe/fragment/measurement/NdtFragment.java @@ -5,24 +5,15 @@ import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; -import android.widget.TextView; - import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.fragment.app.Fragment; - import org.openobservatory.ooniprobe.R; +import org.openobservatory.ooniprobe.databinding.FragmentMeasurementNdtBinding; import org.openobservatory.ooniprobe.model.database.Measurement; -import butterknife.BindView; -import butterknife.ButterKnife; - public class NdtFragment extends Fragment { private static final String MEASUREMENT = "measurement"; - @BindView(R.id.packetLoss) TextView packetLoss; - @BindView(R.id.averagePing) TextView averagePing; - @BindView(R.id.maxPing) TextView maxPing; - @BindView(R.id.mss) TextView mss; public static NdtFragment newInstance(Measurement measurement) { Bundle args = new Bundle(); @@ -36,12 +27,11 @@ public static NdtFragment newInstance(Measurement measurement) { assert getArguments() != null; Measurement measurement = (Measurement) getArguments().getSerializable(MEASUREMENT); assert measurement != null; - View v = inflater.inflate(R.layout.fragment_measurement_ndt, container, false); - ButterKnife.bind(this, v); - packetLoss.setText(Html.fromHtml(getString(R.string.bigNormal, measurement.getTestKeys().getPacketLoss(getActivity()), "%"))); - averagePing.setText(Html.fromHtml(getString(R.string.bigNormal, measurement.getTestKeys().getAveragePing(getActivity()), "ms"))); - maxPing.setText(Html.fromHtml(getString(R.string.bigNormal, measurement.getTestKeys().getMaxPing(getActivity()), "ms"))); - mss.setText(measurement.getTestKeys().getMSS(getActivity())); - return v; + FragmentMeasurementNdtBinding binding = FragmentMeasurementNdtBinding.inflate(inflater,container,false); + binding.packetLoss.setText(Html.fromHtml(getString(R.string.bigNormal, measurement.getTestKeys().getPacketLoss(getActivity()), "%"))); + binding.averagePing.setText(Html.fromHtml(getString(R.string.bigNormal, measurement.getTestKeys().getAveragePing(getActivity()), "ms"))); + binding.maxPing.setText(Html.fromHtml(getString(R.string.bigNormal, measurement.getTestKeys().getMaxPing(getActivity()), "ms"))); + binding.mss.setText(measurement.getTestKeys().getMSS(getActivity())); + return binding.getRoot(); } } diff --git a/app/src/main/java/org/openobservatory/ooniprobe/fragment/measurement/PsiphonFragment.java b/app/src/main/java/org/openobservatory/ooniprobe/fragment/measurement/PsiphonFragment.java index 33755b2fc..f1e51559a 100644 --- a/app/src/main/java/org/openobservatory/ooniprobe/fragment/measurement/PsiphonFragment.java +++ b/app/src/main/java/org/openobservatory/ooniprobe/fragment/measurement/PsiphonFragment.java @@ -4,23 +4,17 @@ import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; -import android.widget.TextView; - import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.fragment.app.Fragment; - import org.openobservatory.ooniprobe.R; +import org.openobservatory.ooniprobe.databinding.FragmentMeasurementPsiphonBinding; import org.openobservatory.ooniprobe.model.database.Measurement; -import butterknife.BindView; -import butterknife.ButterKnife; -import ru.noties.markwon.Markwon; +import io.noties.markwon.Markwon; public class PsiphonFragment extends Fragment { private static final String MEASUREMENT = "measurement"; - @BindView(R.id.bootstrap) TextView bootstrap; - @BindView(R.id.desc) TextView desc; public static PsiphonFragment newInstance(Measurement measurement) { Bundle args = new Bundle(); @@ -34,14 +28,15 @@ public static PsiphonFragment newInstance(Measurement measurement) { assert getArguments() != null; Measurement measurement = (Measurement) getArguments().getSerializable(MEASUREMENT); assert measurement != null; - View v = inflater.inflate(R.layout.fragment_measurement_psiphon, container, false); - ButterKnife.bind(this, v); - Markwon.setMarkdown(desc, + FragmentMeasurementPsiphonBinding binding = FragmentMeasurementPsiphonBinding.inflate(inflater,container,false); + Markwon.builder(getContext()) + .build() + .setMarkdown(binding.desc, measurement.is_anomaly ? getString(R.string.TestResults_Details_Circumvention_Psiphon_Blocked_Content_Paragraph) : getString(R.string.TestResults_Details_Circumvention_Psiphon_Reachable_Content_Paragraph) ); - bootstrap.setText(measurement.getTestKeys().getBootstrapTime(getActivity())); - return v; + binding.bootstrap.setText(measurement.getTestKeys().getBootstrapTime(getActivity())); + return binding.getRoot(); } } diff --git a/app/src/main/java/org/openobservatory/ooniprobe/fragment/measurement/RiseupVPNFragment.java b/app/src/main/java/org/openobservatory/ooniprobe/fragment/measurement/RiseupVPNFragment.java index c0bddd732..32401d0da 100644 --- a/app/src/main/java/org/openobservatory/ooniprobe/fragment/measurement/RiseupVPNFragment.java +++ b/app/src/main/java/org/openobservatory/ooniprobe/fragment/measurement/RiseupVPNFragment.java @@ -4,25 +4,17 @@ import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; -import android.widget.TextView; - import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.fragment.app.Fragment; - import org.openobservatory.ooniprobe.R; +import org.openobservatory.ooniprobe.databinding.FragmentMeasurementRiseupvpnBinding; import org.openobservatory.ooniprobe.model.database.Measurement; -import butterknife.BindView; -import butterknife.ButterKnife; -import ru.noties.markwon.Markwon; +import io.noties.markwon.Markwon; public class RiseupVPNFragment extends Fragment { private static final String MEASUREMENT = "measurement"; - @BindView(R.id.bootstrap_value) TextView bootstrap_value; - @BindView(R.id.openvpn_value) TextView openvpn_value; - @BindView(R.id.bridges_value) TextView bridges_value; - @BindView(R.id.desc) TextView desc; public static RiseupVPNFragment newInstance(Measurement measurement) { Bundle args = new Bundle(); @@ -36,16 +28,17 @@ public static RiseupVPNFragment newInstance(Measurement measurement) { assert getArguments() != null; Measurement measurement = (Measurement) getArguments().getSerializable(MEASUREMENT); assert measurement != null; - View v = inflater.inflate(R.layout.fragment_measurement_riseupvpn, container, false); - ButterKnife.bind(this, v); - Markwon.setMarkdown(desc, + FragmentMeasurementRiseupvpnBinding binding = FragmentMeasurementRiseupvpnBinding.inflate(inflater,container,false); + Markwon.builder(getContext()) + .build() + .setMarkdown(binding.desc, measurement.is_anomaly ? getString(R.string.TestResults_Details_Circumvention_RiseupVPN_Blocked_Content_Paragraph) : getString(R.string.TestResults_Details_Circumvention_RiseupVPN_Reachable_Content_Paragraph) ); - bootstrap_value.setText(measurement.getTestKeys().getRiseupVPNApiStatus()); - openvpn_value.setText(measurement.getTestKeys().getRiseupVPNOpenvpnGatewayStatus(getContext())); - bridges_value.setText(measurement.getTestKeys().getRiseupVPNBridgedGatewayStatus(getContext())); - return v; + binding.bootstrapValue.setText(measurement.getTestKeys().getRiseupVPNApiStatus()); + binding.openvpnValue.setText(measurement.getTestKeys().getRiseupVPNOpenvpnGatewayStatus(getContext())); + binding.bridgesValue.setText(measurement.getTestKeys().getRiseupVPNBridgedGatewayStatus(getContext())); + return binding.getRoot(); } } diff --git a/app/src/main/java/org/openobservatory/ooniprobe/fragment/measurement/SignalFragment.java b/app/src/main/java/org/openobservatory/ooniprobe/fragment/measurement/SignalFragment.java index 869345136..aa395e3d9 100644 --- a/app/src/main/java/org/openobservatory/ooniprobe/fragment/measurement/SignalFragment.java +++ b/app/src/main/java/org/openobservatory/ooniprobe/fragment/measurement/SignalFragment.java @@ -4,21 +4,15 @@ import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; -import android.widget.TextView; - import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.fragment.app.Fragment; - import org.openobservatory.ooniprobe.R; +import org.openobservatory.ooniprobe.databinding.FragmentMeasurementSignalBinding; import org.openobservatory.ooniprobe.model.database.Measurement; -import butterknife.BindView; -import butterknife.ButterKnife; - public class SignalFragment extends Fragment { private static final String MEASUREMENT = "measurement"; - @BindView(R.id.desc) TextView desc; public static SignalFragment newInstance(Measurement measurement) { Bundle args = new Bundle(); @@ -32,9 +26,8 @@ public static SignalFragment newInstance(Measurement measurement) { assert getArguments() != null; Measurement measurement = (Measurement) getArguments().getSerializable(MEASUREMENT); assert measurement != null; - View v = inflater.inflate(R.layout.fragment_measurement_signal, container, false); - ButterKnife.bind(this, v); - desc.setText(measurement.is_anomaly ? R.string.TestResults_Details_InstantMessaging_Signal_LikelyBlocked_Content_Paragraph : R.string.TestResults_Details_InstantMessaging_Signal_Reachable_Content_Paragraph); - return v; + FragmentMeasurementSignalBinding binding = FragmentMeasurementSignalBinding.inflate(inflater,container,false); + binding.desc.setText(measurement.is_anomaly ? R.string.TestResults_Details_InstantMessaging_Signal_LikelyBlocked_Content_Paragraph : R.string.TestResults_Details_InstantMessaging_Signal_Reachable_Content_Paragraph); + return binding.getRoot(); } } diff --git a/app/src/main/java/org/openobservatory/ooniprobe/fragment/measurement/TelegramFragment.java b/app/src/main/java/org/openobservatory/ooniprobe/fragment/measurement/TelegramFragment.java index da689104a..3c1e8920e 100644 --- a/app/src/main/java/org/openobservatory/ooniprobe/fragment/measurement/TelegramFragment.java +++ b/app/src/main/java/org/openobservatory/ooniprobe/fragment/measurement/TelegramFragment.java @@ -4,25 +4,17 @@ import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; -import android.widget.TextView; - import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.core.content.ContextCompat; import androidx.fragment.app.Fragment; - import org.openobservatory.ooniprobe.R; +import org.openobservatory.ooniprobe.databinding.FragmentMeasurementTelegramBinding; import org.openobservatory.ooniprobe.model.database.Measurement; import org.openobservatory.ooniprobe.model.jsonresult.TestKeys; -import butterknife.BindView; -import butterknife.ButterKnife; - public class TelegramFragment extends Fragment { private static final String MEASUREMENT = "measurement"; - @BindView(R.id.application) TextView application; - @BindView(R.id.webApp) TextView webApp; - @BindView(R.id.desc) TextView desc; public static TelegramFragment newInstance(Measurement measurement) { Bundle args = new Bundle(); @@ -32,19 +24,20 @@ public static TelegramFragment newInstance(Measurement measurement) { return fragment; } - @Nullable @Override public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, Bundle savedInstanceState) { + @Nullable + @Override + public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, Bundle savedInstanceState) { assert getArguments() != null; Measurement measurement = (Measurement) getArguments().getSerializable(MEASUREMENT); assert measurement != null; - View v = inflater.inflate(R.layout.fragment_measurement_telegram, container, false); - ButterKnife.bind(this, v); - desc.setText(measurement.is_anomaly ? R.string.TestResults_Details_InstantMessaging_Telegram_LikelyBlocked_Content_Paragraph : R.string.TestResults_Details_InstantMessaging_Telegram_Reachable_Content_Paragraph); - application.setText(measurement.getTestKeys().getTelegramEndpointStatus()); + FragmentMeasurementTelegramBinding binding = FragmentMeasurementTelegramBinding.inflate(inflater,container,false); + binding.desc.setText(measurement.is_anomaly ? R.string.TestResults_Details_InstantMessaging_Telegram_LikelyBlocked_Content_Paragraph : R.string.TestResults_Details_InstantMessaging_Telegram_Reachable_Content_Paragraph); + binding.application.setText(measurement.getTestKeys().getTelegramEndpointStatus()); if (Boolean.TRUE.equals(measurement.getTestKeys().telegram_http_blocking) || Boolean.TRUE.equals(measurement.getTestKeys().telegram_tcp_blocking)) - application.setTextColor(ContextCompat.getColor(getActivity(), R.color.color_yellow9)); - webApp.setText(measurement.getTestKeys().getTelegramWebStatus()); + binding.application.setTextColor(ContextCompat.getColor(getActivity(), R.color.color_yellow9)); + binding.webApp.setText(measurement.getTestKeys().getTelegramWebStatus()); if (TestKeys.BLOCKED.equals(measurement.getTestKeys().telegram_web_status)) - webApp.setTextColor(ContextCompat.getColor(getActivity(), R.color.color_yellow9)); - return v; + binding.webApp.setTextColor(ContextCompat.getColor(getActivity(), R.color.color_yellow9)); + return binding.getRoot(); } } diff --git a/app/src/main/java/org/openobservatory/ooniprobe/fragment/measurement/TorFragment.java b/app/src/main/java/org/openobservatory/ooniprobe/fragment/measurement/TorFragment.java index 4140994f6..d859d2eca 100644 --- a/app/src/main/java/org/openobservatory/ooniprobe/fragment/measurement/TorFragment.java +++ b/app/src/main/java/org/openobservatory/ooniprobe/fragment/measurement/TorFragment.java @@ -4,24 +4,17 @@ import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; -import android.widget.TextView; - import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.fragment.app.Fragment; - import org.openobservatory.ooniprobe.R; +import org.openobservatory.ooniprobe.databinding.FragmentMeasurementTorBinding; import org.openobservatory.ooniprobe.model.database.Measurement; -import butterknife.BindView; -import butterknife.ButterKnife; -import ru.noties.markwon.Markwon; +import io.noties.markwon.Markwon; public class TorFragment extends Fragment { private static final String MEASUREMENT = "measurement"; - @BindView(R.id.bridges) TextView bridges; - @BindView(R.id.authorities) TextView authorities; - @BindView(R.id.desc) TextView desc; public static TorFragment newInstance(Measurement measurement) { Bundle args = new Bundle(); @@ -31,19 +24,22 @@ public static TorFragment newInstance(Measurement measurement) { return fragment; } - @Nullable @Override public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, Bundle savedInstanceState) { + @Nullable + @Override + public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, Bundle savedInstanceState) { assert getArguments() != null; Measurement measurement = (Measurement) getArguments().getSerializable(MEASUREMENT); assert measurement != null; - View v = inflater.inflate(R.layout.fragment_measurement_tor, container, false); - ButterKnife.bind(this, v); - Markwon.setMarkdown(desc, + FragmentMeasurementTorBinding binding = FragmentMeasurementTorBinding.inflate(inflater,container,false); + Markwon.builder(getContext()) + .build() + .setMarkdown(binding.desc, measurement.is_anomaly ? getString(R.string.TestResults_Details_Circumvention_Tor_Blocked_Content_Paragraph) : getString(R.string.TestResults_Details_Circumvention_Tor_Reachable_Content_Paragraph) ); - bridges.setText(measurement.getTestKeys().getBridges(getActivity())); - authorities.setText(measurement.getTestKeys().getAuthorities(getActivity())); - return v; + binding.bridges.setText(measurement.getTestKeys().getBridges(getActivity())); + binding.authorities.setText(measurement.getTestKeys().getAuthorities(getActivity())); + return binding.getRoot(); } } diff --git a/app/src/main/java/org/openobservatory/ooniprobe/fragment/measurement/WebConnectivityFragment.java b/app/src/main/java/org/openobservatory/ooniprobe/fragment/measurement/WebConnectivityFragment.java index 5f0fb32cf..6951ada06 100644 --- a/app/src/main/java/org/openobservatory/ooniprobe/fragment/measurement/WebConnectivityFragment.java +++ b/app/src/main/java/org/openobservatory/ooniprobe/fragment/measurement/WebConnectivityFragment.java @@ -4,22 +4,17 @@ import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; -import android.widget.TextView; - import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.fragment.app.Fragment; - import org.openobservatory.ooniprobe.R; +import org.openobservatory.ooniprobe.databinding.FragmentMeasurementWebconnectivityBinding; import org.openobservatory.ooniprobe.model.database.Measurement; -import butterknife.BindView; -import butterknife.ButterKnife; -import ru.noties.markwon.Markwon; +import io.noties.markwon.Markwon; public class WebConnectivityFragment extends Fragment { private static final String MEASUREMENT = "measurement"; - @BindView(R.id.desc) TextView desc; public static WebConnectivityFragment newInstance(Measurement measurement) { Bundle args = new Bundle(); @@ -29,16 +24,18 @@ public static WebConnectivityFragment newInstance(Measurement measurement) { return fragment; } - @Nullable @Override public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, Bundle savedInstanceState) { + @Nullable + @Override + public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, Bundle savedInstanceState) { assert getArguments() != null; Measurement measurement = (Measurement) getArguments().getSerializable(MEASUREMENT); assert measurement != null; - View v = inflater.inflate(R.layout.fragment_measurement_webconnectivity, container, false); - ButterKnife.bind(this, v); + FragmentMeasurementWebconnectivityBinding binding = FragmentMeasurementWebconnectivityBinding.inflate(inflater,container,false); + Markwon markwon = Markwon.builder(getContext()).build(); if (measurement.is_anomaly) - Markwon.setMarkdown(desc, getString(R.string.TestResults_Details_Websites_LikelyBlocked_Content_Paragraph, measurement.url.url, getString(measurement.getTestKeys().getWebsiteBlocking()))); + markwon.setMarkdown(binding.desc, getString(R.string.TestResults_Details_Websites_LikelyBlocked_Content_Paragraph, measurement.url.url, getString(measurement.getTestKeys().getWebsiteBlocking()))); else - Markwon.setMarkdown(desc, getString(R.string.TestResults_Details_Websites_Reachable_Content_Paragraph, measurement.url.url)); - return v; + markwon.setMarkdown(binding.desc, getString(R.string.TestResults_Details_Websites_Reachable_Content_Paragraph, measurement.url.url)); + return binding.getRoot(); } } diff --git a/app/src/main/java/org/openobservatory/ooniprobe/fragment/measurement/WhatsappFragment.java b/app/src/main/java/org/openobservatory/ooniprobe/fragment/measurement/WhatsappFragment.java index 74cf5ce4d..e8e319940 100644 --- a/app/src/main/java/org/openobservatory/ooniprobe/fragment/measurement/WhatsappFragment.java +++ b/app/src/main/java/org/openobservatory/ooniprobe/fragment/measurement/WhatsappFragment.java @@ -4,26 +4,17 @@ import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; -import android.widget.TextView; - import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.core.content.ContextCompat; import androidx.fragment.app.Fragment; - import org.openobservatory.ooniprobe.R; +import org.openobservatory.ooniprobe.databinding.FragmentMeasurementWhatsappBinding; import org.openobservatory.ooniprobe.model.database.Measurement; import org.openobservatory.ooniprobe.model.jsonresult.TestKeys; -import butterknife.BindView; -import butterknife.ButterKnife; - public class WhatsappFragment extends Fragment { private static final String MEASUREMENT = "measurement"; - @BindView(R.id.desc) TextView desc; - @BindView(R.id.application) TextView application; - @BindView(R.id.webApp) TextView webApp; - @BindView(R.id.registrations) TextView registrations; public static WhatsappFragment newInstance(Measurement measurement) { Bundle args = new Bundle(); @@ -33,22 +24,23 @@ public static WhatsappFragment newInstance(Measurement measurement) { return fragment; } - @Nullable @Override public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, Bundle savedInstanceState) { + @Nullable + @Override + public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, Bundle savedInstanceState) { assert getArguments() != null; Measurement measurement = (Measurement) getArguments().getSerializable(MEASUREMENT); assert measurement != null; - View v = inflater.inflate(R.layout.fragment_measurement_whatsapp, container, false); - ButterKnife.bind(this, v); - desc.setText(measurement.is_anomaly ? R.string.TestResults_Details_InstantMessaging_WhatsApp_LikelyBlocked_Content_Paragraph : R.string.TestResults_Details_InstantMessaging_WhatsApp_Reachable_Content_Paragraph); - application.setText(measurement.getTestKeys().getWhatsappEndpointStatus()); + FragmentMeasurementWhatsappBinding binding = FragmentMeasurementWhatsappBinding.inflate(inflater,container,false); + binding.desc.setText(measurement.is_anomaly ? R.string.TestResults_Details_InstantMessaging_WhatsApp_LikelyBlocked_Content_Paragraph : R.string.TestResults_Details_InstantMessaging_WhatsApp_Reachable_Content_Paragraph); + binding.application.setText(measurement.getTestKeys().getWhatsappEndpointStatus()); if (TestKeys.BLOCKED.equals(measurement.getTestKeys().whatsapp_endpoints_status)) - application.setTextColor(ContextCompat.getColor(getActivity(), R.color.color_yellow9)); - webApp.setText(measurement.getTestKeys().getWhatsappWebStatus()); + binding.application.setTextColor(ContextCompat.getColor(getActivity(), R.color.color_yellow9)); + binding.webApp.setText(measurement.getTestKeys().getWhatsappWebStatus()); if (TestKeys.BLOCKED.equals(measurement.getTestKeys().whatsapp_web_status)) - webApp.setTextColor(ContextCompat.getColor(getActivity(), R.color.color_yellow9)); - registrations.setText(measurement.getTestKeys().getWhatsappRegistrationStatus()); + binding.webApp.setTextColor(ContextCompat.getColor(getActivity(), R.color.color_yellow9)); + binding.registrations.setText(measurement.getTestKeys().getWhatsappRegistrationStatus()); if (TestKeys.BLOCKED.equals(measurement.getTestKeys().registration_server_status)) - registrations.setTextColor(ContextCompat.getColor(getActivity(), R.color.color_yellow9)); - return v; + binding.registrations.setTextColor(ContextCompat.getColor(getActivity(), R.color.color_yellow9)); + return binding.getRoot(); } } diff --git a/app/src/main/java/org/openobservatory/ooniprobe/fragment/onboarding/Onboarding1Fragment.java b/app/src/main/java/org/openobservatory/ooniprobe/fragment/onboarding/Onboarding1Fragment.java index ddc203fbb..54382527a 100644 --- a/app/src/main/java/org/openobservatory/ooniprobe/fragment/onboarding/Onboarding1Fragment.java +++ b/app/src/main/java/org/openobservatory/ooniprobe/fragment/onboarding/Onboarding1Fragment.java @@ -4,24 +4,21 @@ import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; - import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.fragment.app.Fragment; - -import org.openobservatory.ooniprobe.R; - -import butterknife.ButterKnife; -import butterknife.OnClick; +import org.openobservatory.ooniprobe.databinding.FragmentOnboarding1Binding; public class Onboarding1Fragment extends Fragment { - @Nullable @Override public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { - View v = inflater.inflate(R.layout.fragment_onboarding_1, container, false); - ButterKnife.bind(this, v); - return v; + @Nullable + @Override + public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { + FragmentOnboarding1Binding binding = FragmentOnboarding1Binding.inflate(inflater, container, false); + binding.master.setOnClickListener(v -> masterClick()); + return binding.getRoot(); } - @OnClick(R.id.master) void masterClick() { + void masterClick() { getParentFragmentManager().beginTransaction().replace(android.R.id.content, new Onboarding2Fragment()).commit(); } } diff --git a/app/src/main/java/org/openobservatory/ooniprobe/fragment/onboarding/Onboarding2Fragment.java b/app/src/main/java/org/openobservatory/ooniprobe/fragment/onboarding/Onboarding2Fragment.java index 663da5a4c..fddbc294b 100644 --- a/app/src/main/java/org/openobservatory/ooniprobe/fragment/onboarding/Onboarding2Fragment.java +++ b/app/src/main/java/org/openobservatory/ooniprobe/fragment/onboarding/Onboarding2Fragment.java @@ -6,41 +6,36 @@ import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; -import android.widget.TextView; - import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.fragment.app.Fragment; - import org.openobservatory.ooniprobe.R; - -import butterknife.BindView; -import butterknife.ButterKnife; -import butterknife.OnClick; +import org.openobservatory.ooniprobe.databinding.FragmentOnboarding2Binding; public class Onboarding2Fragment extends Fragment implements OnboardingDialogPopquizFragment.OnboardingPopquizInterface, OnboardingDialogWarningFragment.OnboardingWarningInterface { - @BindView(R.id.bullet1) TextView bullet1; - @BindView(R.id.bullet2) TextView bullet2; - @BindView(R.id.bullet3) TextView bullet3; - @Nullable @Override public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { - View v = inflater.inflate(R.layout.fragment_onboarding_2, container, false); - ButterKnife.bind(this, v); - bullet1.setText(getString(R.string.bullet, getString(R.string.Onboarding_ThingsToKnow_Bullet_1))); - bullet2.setText(getString(R.string.bullet, getString(R.string.Onboarding_ThingsToKnow_Bullet_2))); - bullet3.setText(getString(R.string.bullet, getString(R.string.Onboarding_ThingsToKnow_Bullet_3))); - return v; + @Nullable + @Override + public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { + FragmentOnboarding2Binding binding = FragmentOnboarding2Binding.inflate(inflater, container, false); + binding.bullet1.setText(getString(R.string.bullet, getString(R.string.Onboarding_ThingsToKnow_Bullet_1))); + binding.bullet2.setText(getString(R.string.bullet, getString(R.string.Onboarding_ThingsToKnow_Bullet_2))); + binding.bullet3.setText(getString(R.string.bullet, getString(R.string.Onboarding_ThingsToKnow_Bullet_3))); + binding.master.setOnClickListener(v -> masterClick()); + binding.slave.setOnClickListener(v -> slaveClick()); + return binding.getRoot(); } - @OnClick(R.id.master) void masterClick() { + void masterClick() { OnboardingDialogPopquizFragment.newInstance(R.string.Onboarding_PopQuiz_1_Title, R.string.Onboarding_PopQuiz_1_Question).show(getChildFragmentManager(), null); } - @OnClick(R.id.slave) void slaveClick() { + void slaveClick() { startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse("https://ooni.io/about/risks/"))); } - @Override public void onPopquizResult(int questionResId, boolean positive) { + @Override + public void onPopquizResult(int questionResId, boolean positive) { if (questionResId == R.string.Onboarding_PopQuiz_1_Question) { if (positive) OnboardingDialogPopquizFragment.newInstance(R.string.Onboarding_PopQuiz_2_Title, R.string.Onboarding_PopQuiz_2_Question).show(getChildFragmentManager(), null); @@ -54,7 +49,8 @@ public class Onboarding2Fragment extends Fragment implements OnboardingDialogPop } } - @Override public void onWarningResult(int questionResId) { + @Override + public void onWarningResult(int questionResId) { if (questionResId == R.string.Onboarding_PopQuiz_1_Wrong_Paragraph) OnboardingDialogPopquizFragment.newInstance(R.string.Onboarding_PopQuiz_2_Title, R.string.Onboarding_PopQuiz_2_Question).show(getChildFragmentManager(), null); else if (questionResId == R.string.Onboarding_PopQuiz_2_Wrong_Paragraph) diff --git a/app/src/main/java/org/openobservatory/ooniprobe/fragment/onboarding/Onboarding3Fragment.java b/app/src/main/java/org/openobservatory/ooniprobe/fragment/onboarding/Onboarding3Fragment.java index cc649e834..3fe085d54 100644 --- a/app/src/main/java/org/openobservatory/ooniprobe/fragment/onboarding/Onboarding3Fragment.java +++ b/app/src/main/java/org/openobservatory/ooniprobe/fragment/onboarding/Onboarding3Fragment.java @@ -4,48 +4,44 @@ import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; -import android.widget.TextView; - import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.fragment.app.Fragment; - import org.openobservatory.ooniprobe.R; import org.openobservatory.ooniprobe.activity.MainActivity; import org.openobservatory.ooniprobe.common.Application; import org.openobservatory.ooniprobe.common.PreferenceManager; import org.openobservatory.ooniprobe.common.ThirdPartyServices; import org.openobservatory.ooniprobe.common.service.ServiceUtil; +import org.openobservatory.ooniprobe.databinding.FragmentOnboarding3Binding; +import io.noties.markwon.Markwon; import javax.inject.Inject; -import butterknife.BindView; -import butterknife.ButterKnife; -import butterknife.OnClick; -import ru.noties.markwon.Markwon; - public class Onboarding3Fragment extends Fragment { @Inject PreferenceManager preferenceManager; - @BindView(R.id.bullet1) TextView bullet1; - @BindView(R.id.bullet2) TextView bullet2; - @BindView(R.id.bullet3) TextView bullet3; - @BindView(R.id.paragraph) TextView paragraph; - - @Nullable @Override public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { + @Nullable + @Override + public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { ((Application) getActivity().getApplication()).getFragmentComponent().inject(this); - View v = inflater.inflate(R.layout.fragment_onboarding_3, container, false); - ButterKnife.bind(this, v); - bullet1.setText(getString(R.string.bullet, getString(R.string.Onboarding_DefaultSettings_Bullet_1))); - bullet2.setText(getString(R.string.bullet, getString(R.string.Onboarding_DefaultSettings_Bullet_2))); - bullet3.setText(getString(R.string.bullet, getString(R.string.Onboarding_DefaultSettings_Bullet_3))); - Markwon.setMarkdown(paragraph, getString(R.string.Onboarding_DefaultSettings_Paragraph)); - return v; + FragmentOnboarding3Binding binding = FragmentOnboarding3Binding.inflate(inflater, container, false); + binding.bullet1.setText(getString(R.string.bullet, getString(R.string.Onboarding_DefaultSettings_Bullet_1))); + binding.bullet2.setText(getString(R.string.bullet, getString(R.string.Onboarding_DefaultSettings_Bullet_2))); + binding.bullet3.setText(getString(R.string.bullet, getString(R.string.Onboarding_DefaultSettings_Bullet_3))); + Markwon.builder(getContext()) + .build() + .setMarkdown(binding.paragraph, getString(R.string.Onboarding_DefaultSettings_Paragraph)); + + binding.master.setOnClickListener(v -> masterClick()); + binding.slave.setOnClickListener(v -> slaveClick()); + + return binding.getRoot(); } - @OnClick(R.id.master) void masterClick() { + void masterClick() { preferenceManager.setShowOnboarding(false); ThirdPartyServices.reloadConsents((Application) getActivity().getApplication()); startAutoTestIfNeeded(); @@ -53,7 +49,7 @@ public class Onboarding3Fragment extends Fragment { getActivity().finish(); } - @OnClick(R.id.slave) void slaveClick() { + void slaveClick() { preferenceManager.setShowOnboarding(false); startAutoTestIfNeeded(); startActivity(MainActivity.newIntent(getActivity(), R.id.settings)); diff --git a/app/src/main/java/org/openobservatory/ooniprobe/fragment/onboarding/OnboardingAutoTestFragment.java b/app/src/main/java/org/openobservatory/ooniprobe/fragment/onboarding/OnboardingAutoTestFragment.java index 38f520820..01ebce218 100644 --- a/app/src/main/java/org/openobservatory/ooniprobe/fragment/onboarding/OnboardingAutoTestFragment.java +++ b/app/src/main/java/org/openobservatory/ooniprobe/fragment/onboarding/OnboardingAutoTestFragment.java @@ -10,42 +10,39 @@ import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; - import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.fragment.app.Fragment; - +import localhost.toolkit.app.fragment.ConfirmDialogFragment; import org.openobservatory.ooniprobe.R; import org.openobservatory.ooniprobe.common.Application; import org.openobservatory.ooniprobe.common.PreferenceManager; import org.openobservatory.ooniprobe.common.ThirdPartyServices; - -import java.io.Serializable; +import org.openobservatory.ooniprobe.databinding.FragmentOnboardingAutotestBinding; import javax.inject.Inject; - -import butterknife.ButterKnife; -import butterknife.OnClick; -import localhost.toolkit.app.fragment.ConfirmDialogFragment; +import java.io.Serializable; public class OnboardingAutoTestFragment extends Fragment implements ConfirmDialogFragment.OnConfirmedListener { @Inject PreferenceManager preferenceManager; public static final String BATTERY_DIALOG = "battery_optimization"; @Nullable - @Override public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { + @Override + public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { ((Application) getActivity().getApplication()).getFragmentComponent().inject(this); - View v = inflater.inflate(R.layout.fragment_onboarding_autotest, container, false); - ButterKnife.bind(this, v); - return v; + FragmentOnboardingAutotestBinding binding = FragmentOnboardingAutotestBinding.inflate(inflater, container, false); + binding.master.setOnClickListener(v -> masterClick()); + binding.slave.setOnClickListener(v -> slaveClick()); + return binding.getRoot(); } - @OnClick(R.id.master) void masterClick() { + void masterClick() { enableAutoTest(); } - @OnClick(R.id.slave) void slaveClick() { + void slaveClick() { next(); } diff --git a/app/src/main/java/org/openobservatory/ooniprobe/fragment/onboarding/OnboardingCrashFragment.java b/app/src/main/java/org/openobservatory/ooniprobe/fragment/onboarding/OnboardingCrashFragment.java index d3ece3295..1c4c97b94 100644 --- a/app/src/main/java/org/openobservatory/ooniprobe/fragment/onboarding/OnboardingCrashFragment.java +++ b/app/src/main/java/org/openobservatory/ooniprobe/fragment/onboarding/OnboardingCrashFragment.java @@ -4,32 +4,29 @@ import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; - import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.fragment.app.Fragment; - -import org.openobservatory.ooniprobe.R; import org.openobservatory.ooniprobe.common.Application; import org.openobservatory.ooniprobe.common.ThirdPartyServices; - -import butterknife.ButterKnife; -import butterknife.OnClick; +import org.openobservatory.ooniprobe.databinding.FragmentOnboardingCrashBinding; public class OnboardingCrashFragment extends Fragment { @Nullable - @Override public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { - View v = inflater.inflate(R.layout.fragment_onboarding_crash, container, false); - ButterKnife.bind(this, v); - return v; + @Override + public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { + FragmentOnboardingCrashBinding binding = FragmentOnboardingCrashBinding.inflate(inflater, container, false); + binding.master.setOnClickListener(v -> masterClick()); + binding.slave.setOnClickListener(v -> slaveClick()); + return binding.getRoot(); } - @OnClick(R.id.master) void masterClick() { + void masterClick() { ThirdPartyServices.acceptCrash((Application) getActivity().getApplication()); getParentFragmentManager().beginTransaction().replace(android.R.id.content, new Onboarding3Fragment()).commit(); } - @OnClick(R.id.slave) void slaveClick() { + void slaveClick() { getParentFragmentManager().beginTransaction().replace(android.R.id.content, new Onboarding3Fragment()).commit(); } diff --git a/app/src/main/java/org/openobservatory/ooniprobe/fragment/onboarding/OnboardingDialogPopquizFragment.java b/app/src/main/java/org/openobservatory/ooniprobe/fragment/onboarding/OnboardingDialogPopquizFragment.java index d1f93267a..8321f0f89 100644 --- a/app/src/main/java/org/openobservatory/ooniprobe/fragment/onboarding/OnboardingDialogPopquizFragment.java +++ b/app/src/main/java/org/openobservatory/ooniprobe/fragment/onboarding/OnboardingDialogPopquizFragment.java @@ -9,28 +9,16 @@ import android.view.View; import android.view.ViewGroup; import android.view.WindowManager; -import android.widget.LinearLayout; -import android.widget.TextView; - import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.fragment.app.DialogFragment; - -import com.airbnb.lottie.LottieAnimationView; - import org.openobservatory.ooniprobe.R; - -import butterknife.BindView; -import butterknife.ButterKnife; -import butterknife.OnClick; +import org.openobservatory.ooniprobe.databinding.FragmentOnboardingDialogPopquizBinding; public class OnboardingDialogPopquizFragment extends DialogFragment { private static final String TITLE_RES_ID = "titleResId"; private static final String QUESTION_RES_ID = "questionResId"; - @BindView(R.id.title) @Nullable TextView title; - @BindView(R.id.question) TextView question; - @BindView(R.id.dialog) LinearLayout dialog; - @BindView(R.id.animation) LottieAnimationView animation; + private FragmentOnboardingDialogPopquizBinding binding; public static OnboardingDialogPopquizFragment newInstance(int titleResId, int questionResId) { Bundle args = new Bundle(); @@ -53,42 +41,45 @@ public void onStart() { @Nullable @Override public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { assert getArguments() != null; - View v = inflater.inflate(R.layout.fragment_onboarding_dialog_popquiz, container, false); - ButterKnife.bind(this, v); - if (title != null) - title.setText(getArguments().getInt(TITLE_RES_ID)); - question.setText(getArguments().getInt(QUESTION_RES_ID)); - return v; + binding = FragmentOnboardingDialogPopquizBinding.inflate(inflater, container, false); + if (binding.title != null) + binding.title.setText(getArguments().getInt(TITLE_RES_ID)); + binding.question.setText(getArguments().getInt(QUESTION_RES_ID)); + + binding.positive.setOnClickListener(v -> positiveClick()); + binding.negative.setOnClickListener(v -> negativeClick()); + + return binding.getRoot(); } - @OnClick(R.id.positive) void positiveClick() { - animation.setBackgroundResource(R.drawable.dialog_green); - animation.setAnimation("anim/checkMark.json"); - animation.setVisibility(View.VISIBLE); - dialog.setVisibility(View.INVISIBLE); - animation.addAnimatorListener(new AnimatorListenerAdapter() { + void positiveClick() { + binding.animation.setBackgroundResource(R.drawable.dialog_green); + binding.animation.setAnimation("anim/checkMark.json"); + binding.animation.setVisibility(View.VISIBLE); + binding.dialog.setVisibility(View.INVISIBLE); + binding.animation.addAnimatorListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { dismiss(); ((OnboardingPopquizInterface) getParentFragment()).onPopquizResult(getArguments().getInt(QUESTION_RES_ID), true); } }); - animation.playAnimation(); + binding.animation.playAnimation(); } - @OnClick(R.id.negative) void negativeClick() { - animation.setBackgroundResource(R.drawable.dialog_red); - animation.setAnimation("anim/crossMark.json"); - animation.setVisibility(View.VISIBLE); - dialog.setVisibility(View.INVISIBLE); - animation.addAnimatorListener(new AnimatorListenerAdapter() { + void negativeClick() { + binding.animation.setBackgroundResource(R.drawable.dialog_red); + binding.animation.setAnimation("anim/crossMark.json"); + binding.animation.setVisibility(View.VISIBLE); + binding.dialog.setVisibility(View.INVISIBLE); + binding.animation.addAnimatorListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { dismiss(); ((OnboardingPopquizInterface) getParentFragment()).onPopquizResult(getArguments().getInt(QUESTION_RES_ID), false); } }); - animation.playAnimation(); + binding.animation.playAnimation(); } public interface OnboardingPopquizInterface { diff --git a/app/src/main/java/org/openobservatory/ooniprobe/fragment/onboarding/OnboardingDialogWarningFragment.java b/app/src/main/java/org/openobservatory/ooniprobe/fragment/onboarding/OnboardingDialogWarningFragment.java index 5cc405474..f8e6102eb 100644 --- a/app/src/main/java/org/openobservatory/ooniprobe/fragment/onboarding/OnboardingDialogWarningFragment.java +++ b/app/src/main/java/org/openobservatory/ooniprobe/fragment/onboarding/OnboardingDialogWarningFragment.java @@ -7,24 +7,13 @@ import android.view.View; import android.view.ViewGroup; import android.view.WindowManager; -import android.widget.LinearLayout; -import android.widget.TextView; - import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.fragment.app.DialogFragment; - -import org.openobservatory.ooniprobe.R; - -import butterknife.BindView; -import butterknife.ButterKnife; -import butterknife.OnClick; +import org.openobservatory.ooniprobe.databinding.FragmentOnboardingDialogWarningBinding; public class OnboardingDialogWarningFragment extends DialogFragment { private static final String QUESTION_RES_ID = "questionResId"; - @BindView(R.id.title) @Nullable TextView title; - @BindView(R.id.question) TextView question; - @BindView(R.id.dialog) LinearLayout dialog; public static OnboardingDialogWarningFragment newInstance(int questionResId) { Bundle args = new Bundle(); @@ -46,17 +35,20 @@ public void onStart() { @Nullable @Override public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { assert getArguments() != null; - View v = inflater.inflate(R.layout.fragment_onboarding_dialog_warning, container, false); - ButterKnife.bind(this, v); - question.setText(getArguments().getInt(QUESTION_RES_ID)); - return v; + FragmentOnboardingDialogWarningBinding binding = FragmentOnboardingDialogWarningBinding.inflate(inflater, container, false); + binding.question.setText(getArguments().getInt(QUESTION_RES_ID)); + + binding.positive.setOnClickListener(v -> positiveClick()); + binding.negative.setOnClickListener(v -> negativeClick()); + + return binding.getRoot(); } - @OnClick(R.id.positive) void positiveClick() { + void positiveClick() { dismiss(); } - @OnClick(R.id.negative) void negativeClick() { + void negativeClick() { dismiss(); ((OnboardingWarningInterface) getParentFragment()).onWarningResult(getArguments().getInt(QUESTION_RES_ID)); } diff --git a/app/src/main/java/org/openobservatory/ooniprobe/fragment/resultHeader/ResultHeaderDetailFragment.java b/app/src/main/java/org/openobservatory/ooniprobe/fragment/resultHeader/ResultHeaderDetailFragment.java index 45331eb23..4f9130576 100644 --- a/app/src/main/java/org/openobservatory/ooniprobe/fragment/resultHeader/ResultHeaderDetailFragment.java +++ b/app/src/main/java/org/openobservatory/ooniprobe/fragment/resultHeader/ResultHeaderDetailFragment.java @@ -5,23 +5,17 @@ import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; -import android.widget.LinearLayout; -import android.widget.TextView; - import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.appcompat.view.ContextThemeWrapper; import androidx.fragment.app.Fragment; - import org.openobservatory.ooniprobe.R; +import org.openobservatory.ooniprobe.databinding.FragmentResultHeadDetailBinding; import org.openobservatory.ooniprobe.model.database.Network; import java.util.Date; import java.util.Locale; -import butterknife.BindView; -import butterknife.ButterKnife; - public class ResultHeaderDetailFragment extends Fragment { private static final String NETWORK = "network"; private static final String COUNTRY_CODE = "country_code"; @@ -31,19 +25,6 @@ public class ResultHeaderDetailFragment extends Fragment { private static final String START_TIME = "start_time"; private static final String IS_TOTAL_RUNTIME = "isTotalRuntime"; private static final String LIGHT_THEME = "lightTheme"; - @BindView(R.id.dataUsage) LinearLayout dataUsage; - @BindView(R.id.startTimeBox) LinearLayout startTimeBox; - @BindView(R.id.runtimeBox) LinearLayout runtimeBox; - @BindView(R.id.countryBox) LinearLayout countryBox; - @BindView(R.id.networkBox) LinearLayout networkBox; - @BindView(R.id.startTime) TextView startTime; - @BindView(R.id.upload) TextView upload; - @BindView(R.id.download) TextView download; - @BindView(R.id.runtime) TextView runtime; - @BindView(R.id.runtimeLabel) TextView runtimeLabel; - @BindView(R.id.country) TextView country; - @BindView(R.id.networkName) TextView networkName; - @BindView(R.id.networkDetail) TextView networkDetail; public static ResultHeaderDetailFragment newInstance(boolean lightTheme, String data_usage_up, String data_usage_down, Date start_time, Double runtime, Boolean isTotalRuntime, String country_code, Network network) { Bundle args = new Bundle(); @@ -67,35 +48,42 @@ public static ResultHeaderDetailFragment newInstance(boolean lightTheme, String return fragment; } - @Nullable @Override public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, Bundle savedInstanceState) { + @Nullable + @Override + public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, Bundle savedInstanceState) { assert getArguments() != null; int themeResId = getArguments().getBoolean(LIGHT_THEME) ? R.style.Theme_MaterialComponents_Light_NoActionBar_App : R.style.Theme_MaterialComponents_NoActionBar_App; - View v = inflater.cloneInContext(new ContextThemeWrapper(getActivity(), themeResId)).inflate(R.layout.fragment_result_head_detail, container, false); - ButterKnife.bind(this, v); + FragmentResultHeadDetailBinding binding = FragmentResultHeadDetailBinding.inflate(inflater.cloneInContext(new ContextThemeWrapper(getActivity(), themeResId)), container, false); if (getArguments().containsKey(DATA_USAGE_DOWN) && getArguments().containsKey(DATA_USAGE_UP)) { - download.setText(getArguments().getString(DATA_USAGE_DOWN)); - upload.setText(getArguments().getString(DATA_USAGE_UP)); + binding.download.setText(getArguments().getString(DATA_USAGE_DOWN)); + binding.upload.setText(getArguments().getString(DATA_USAGE_UP)); } else - dataUsage.setVisibility(View.GONE); + binding.dataUsage.setVisibility(View.GONE); if (getArguments().containsKey(START_TIME)) - startTime.setText(DateFormat.format(DateFormat.getBestDateTimePattern(Locale.getDefault(), "yMdHm"), (Date) getArguments().getSerializable(START_TIME))); + binding.startTime.setText(DateFormat.format(DateFormat.getBestDateTimePattern(Locale.getDefault(), "yMdHm"), (Date) getArguments().getSerializable(START_TIME))); else - startTimeBox.setVisibility(View.GONE); + binding.startTimeBox.setVisibility(View.GONE); if (getArguments().containsKey(RUNTIME)) { - runtime.setText(getString(R.string.f, getArguments().getDouble(RUNTIME))); - runtimeLabel.setText(getArguments().getBoolean(IS_TOTAL_RUNTIME) ? R.string.TestResults_Summary_Hero_Runtime : R.string.TestResults_Details_Hero_Runtime); + binding.runtime.setText(getString(R.string.f, getArguments().getDouble(RUNTIME))); + binding.runtimeLabel.setText(getArguments().getBoolean(IS_TOTAL_RUNTIME) ? R.string.TestResults_Summary_Hero_Runtime : R.string.TestResults_Details_Hero_Runtime); } else - runtimeBox.setVisibility(View.GONE); + binding.runtimeBox.setVisibility(View.GONE); if (getArguments().containsKey(COUNTRY_CODE)) - country.setText(getArguments().getString(COUNTRY_CODE)); + binding.country.setText(getArguments().getString(COUNTRY_CODE)); else - countryBox.setVisibility(View.GONE); + binding.countryBox.setVisibility(View.GONE); if (getArguments().containsKey(NETWORK)) { Network n = (Network) getArguments().getSerializable(NETWORK); - networkName.setText(Network.getName(networkName.getContext(), n)); - networkDetail.setText(networkDetail.getContext().getString(R.string.twoParamWithBrackets, Network.getAsn(networkDetail.getContext(), n), Network.getLocalizedNetworkType(networkDetail.getContext(), n))); + binding.networkName.setText(Network.getName(binding.networkName.getContext(), n)); + binding.networkDetail.setText( + binding.networkDetail.getContext().getString( + R.string.twoParamWithBrackets, + Network.getAsn(binding.networkDetail.getContext(), n), + Network.getLocalizedNetworkType(binding.networkDetail.getContext(), n) + ) + ); } else - networkBox.setVisibility(View.GONE); - return v; + binding.networkBox.setVisibility(View.GONE); + return binding.getRoot(); } } diff --git a/app/src/main/java/org/openobservatory/ooniprobe/fragment/resultHeader/ResultHeaderMiddleboxFragment.java b/app/src/main/java/org/openobservatory/ooniprobe/fragment/resultHeader/ResultHeaderMiddleboxFragment.java index 3e4137ccc..c70441cb4 100644 --- a/app/src/main/java/org/openobservatory/ooniprobe/fragment/resultHeader/ResultHeaderMiddleboxFragment.java +++ b/app/src/main/java/org/openobservatory/ooniprobe/fragment/resultHeader/ResultHeaderMiddleboxFragment.java @@ -5,20 +5,15 @@ import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; -import android.widget.TextView; - import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.fragment.app.Fragment; - import org.openobservatory.ooniprobe.R; +import org.openobservatory.ooniprobe.databinding.FragmentResultHeadMiddleboxBinding; -import butterknife.BindView; -import butterknife.ButterKnife; - -@Deprecated public class ResultHeaderMiddleboxFragment extends Fragment { +@Deprecated +public class ResultHeaderMiddleboxFragment extends Fragment { private static final String ANOMALY = "anomaly"; - @BindView(R.id.text) TextView text; public static ResultHeaderMiddleboxFragment newInstance(boolean anomaly) { Bundle args = new Bundle(); @@ -28,11 +23,20 @@ public static ResultHeaderMiddleboxFragment newInstance(boolean anomaly) { return fragment; } - @Nullable @Override public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, Bundle savedInstanceState) { + @Nullable + @Override + public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, Bundle savedInstanceState) { assert getArguments() != null; - View v = inflater.inflate(R.layout.fragment_result_head_middlebox, container, false); - ButterKnife.bind(this, v); - text.setText(Html.fromHtml(v.getContext().getString(R.string.normalBold, getString(R.string.Test_Middleboxes_Fullname), getString(getArguments().getBoolean(ANOMALY) ? R.string.TestResults_Summary_Middleboxes_Hero_Found : R.string.TestResults_Summary_Middleboxes_Hero_NotFound)))); - return v; + FragmentResultHeadMiddleboxBinding binding = FragmentResultHeadMiddleboxBinding.inflate(inflater, container, false); + binding.text.setText( + Html.fromHtml( + binding.getRoot().getContext().getString( + R.string.normalBold, + getString(R.string.Test_Middleboxes_Fullname), + getString(getArguments().getBoolean(ANOMALY) ? R.string.TestResults_Summary_Middleboxes_Hero_Found : R.string.TestResults_Summary_Middleboxes_Hero_NotFound) + ) + ) + ); + return binding.getRoot(); } } diff --git a/app/src/main/java/org/openobservatory/ooniprobe/fragment/resultHeader/ResultHeaderPerformanceFragment.java b/app/src/main/java/org/openobservatory/ooniprobe/fragment/resultHeader/ResultHeaderPerformanceFragment.java index d69da9b36..8ae44721f 100644 --- a/app/src/main/java/org/openobservatory/ooniprobe/fragment/resultHeader/ResultHeaderPerformanceFragment.java +++ b/app/src/main/java/org/openobservatory/ooniprobe/fragment/resultHeader/ResultHeaderPerformanceFragment.java @@ -4,37 +4,20 @@ import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; -import android.widget.TextView; - import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.fragment.app.Fragment; - import org.openobservatory.ooniprobe.R; +import org.openobservatory.ooniprobe.databinding.FragmentResultHeadPerformanceBinding; import org.openobservatory.ooniprobe.model.database.Measurement; import org.openobservatory.ooniprobe.model.database.Result; import org.openobservatory.ooniprobe.test.test.Dash; import org.openobservatory.ooniprobe.test.test.Ndt; -import butterknife.BindView; -import butterknife.ButterKnife; - public class ResultHeaderPerformanceFragment extends Fragment { private static final String RESULT = "result"; public static final float ALPHA_DIS = 0.3f; public static final int ALPHA_ENA = 1; - @BindView(R.id.video) TextView video; - @BindView(R.id.upload) TextView upload; - @BindView(R.id.download) TextView download; - @BindView(R.id.ping) TextView ping; - @BindView(R.id.videoLabel) TextView videoLabel; - @BindView(R.id.downloadLabel) TextView downloadLabel; - @BindView(R.id.uploadLabel) TextView uploadLabel; - @BindView(R.id.pingLabel) TextView pingLabel; - @BindView(R.id.videoUnit) TextView videoUnit; - @BindView(R.id.downloadUnit) TextView downloadUnit; - @BindView(R.id.uploadUnit) TextView uploadUnit; - @BindView(R.id.pingUnit) TextView pingUnit; public static ResultHeaderPerformanceFragment newInstance(Result result) { Bundle args = new Bundle(); @@ -44,32 +27,33 @@ public static ResultHeaderPerformanceFragment newInstance(Result result) { return fragment; } - @Nullable @Override public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, Bundle savedInstanceState) { + @Nullable + @Override + public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, Bundle savedInstanceState) { assert getArguments() != null; - View v = inflater.inflate(R.layout.fragment_result_head_performance, container, false); - ButterKnife.bind(this, v); + FragmentResultHeadPerformanceBinding binding = FragmentResultHeadPerformanceBinding.inflate(inflater, container, false); Result result = (Result) getArguments().getSerializable(RESULT); assert result != null; Measurement dashM = result.getMeasurement(Dash.NAME); Measurement ndtM = result.getMeasurement(Ndt.NAME); - video.setText(dashM == null ? R.string.TestResults_NotAvailable : dashM.getTestKeys().getVideoQuality(false)); - upload.setText(ndtM == null ? getString(R.string.TestResults_NotAvailable) : ndtM.getTestKeys().getUpload(getActivity())); - uploadUnit.setText(ndtM == null ? R.string.TestResults_NotAvailable : ndtM.getTestKeys().getUploadUnit()); - download.setText(ndtM == null ? getString(R.string.TestResults_NotAvailable) : ndtM.getTestKeys().getDownload(getActivity())); - downloadUnit.setText(ndtM == null ? R.string.TestResults_NotAvailable : ndtM.getTestKeys().getDownloadUnit()); - ping.setText(ndtM == null ? getString(R.string.TestResults_NotAvailable) : ndtM.getTestKeys().getPing(getActivity())); - videoLabel.setAlpha(dashM == null ? ALPHA_DIS : ALPHA_ENA); - downloadLabel.setAlpha(ndtM == null ? ALPHA_DIS : ALPHA_ENA); - uploadLabel.setAlpha(ndtM == null ? ALPHA_DIS : ALPHA_ENA); - pingLabel.setAlpha(ndtM == null ? ALPHA_DIS : ALPHA_ENA); - video.setAlpha(dashM == null ? ALPHA_DIS : ALPHA_ENA); - download.setAlpha(ndtM == null ? ALPHA_DIS : ALPHA_ENA); - upload.setAlpha(ndtM == null ? ALPHA_DIS : ALPHA_ENA); - ping.setAlpha(ndtM == null ? ALPHA_DIS : ALPHA_ENA); - videoUnit.setAlpha(dashM == null ? ALPHA_DIS : ALPHA_ENA); - downloadUnit.setAlpha(ndtM == null ? ALPHA_DIS : ALPHA_ENA); - uploadUnit.setAlpha(ndtM == null ? ALPHA_DIS : ALPHA_ENA); - pingUnit.setAlpha(ndtM == null ? ALPHA_DIS : ALPHA_ENA); - return v; + binding.video.setText(dashM == null ? R.string.TestResults_NotAvailable : dashM.getTestKeys().getVideoQuality(false)); + binding.upload.setText(ndtM == null ? getString(R.string.TestResults_NotAvailable) : ndtM.getTestKeys().getUpload(getActivity())); + binding.uploadUnit.setText(ndtM == null ? R.string.TestResults_NotAvailable : ndtM.getTestKeys().getUploadUnit()); + binding.download.setText(ndtM == null ? getString(R.string.TestResults_NotAvailable) : ndtM.getTestKeys().getDownload(getActivity())); + binding.downloadUnit.setText(ndtM == null ? R.string.TestResults_NotAvailable : ndtM.getTestKeys().getDownloadUnit()); + binding.ping.setText(ndtM == null ? getString(R.string.TestResults_NotAvailable) : ndtM.getTestKeys().getPing(getActivity())); + binding.videoLabel.setAlpha(dashM == null ? ALPHA_DIS : ALPHA_ENA); + binding.downloadLabel.setAlpha(ndtM == null ? ALPHA_DIS : ALPHA_ENA); + binding.uploadLabel.setAlpha(ndtM == null ? ALPHA_DIS : ALPHA_ENA); + binding.pingLabel.setAlpha(ndtM == null ? ALPHA_DIS : ALPHA_ENA); + binding.video.setAlpha(dashM == null ? ALPHA_DIS : ALPHA_ENA); + binding.download.setAlpha(ndtM == null ? ALPHA_DIS : ALPHA_ENA); + binding.upload.setAlpha(ndtM == null ? ALPHA_DIS : ALPHA_ENA); + binding.ping.setAlpha(ndtM == null ? ALPHA_DIS : ALPHA_ENA); + binding.videoUnit.setAlpha(dashM == null ? ALPHA_DIS : ALPHA_ENA); + binding.downloadUnit.setAlpha(ndtM == null ? ALPHA_DIS : ALPHA_ENA); + binding.uploadUnit.setAlpha(ndtM == null ? ALPHA_DIS : ALPHA_ENA); + binding.pingUnit.setAlpha(ndtM == null ? ALPHA_DIS : ALPHA_ENA); + return binding.getRoot(); } } diff --git a/app/src/main/java/org/openobservatory/ooniprobe/fragment/resultHeader/ResultHeaderTBAFragment.java b/app/src/main/java/org/openobservatory/ooniprobe/fragment/resultHeader/ResultHeaderTBAFragment.java index b7a01a0dd..137325d2f 100644 --- a/app/src/main/java/org/openobservatory/ooniprobe/fragment/resultHeader/ResultHeaderTBAFragment.java +++ b/app/src/main/java/org/openobservatory/ooniprobe/fragment/resultHeader/ResultHeaderTBAFragment.java @@ -4,26 +4,15 @@ import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; -import android.widget.TextView; - import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.fragment.app.Fragment; - import org.openobservatory.ooniprobe.R; +import org.openobservatory.ooniprobe.databinding.FragmentResultHeadTbaBinding; import org.openobservatory.ooniprobe.model.database.Result; -import butterknife.BindView; -import butterknife.ButterKnife; - public class ResultHeaderTBAFragment extends Fragment { private static final String RESULT = "result"; - @BindView(R.id.tested) TextView tested; - @BindView(R.id.blocked) TextView blocked; - @BindView(R.id.available) TextView available; - @BindView(R.id.testedTag) TextView testedTag; - @BindView(R.id.blockedTag) TextView blockedTag; - @BindView(R.id.availableTag) TextView availableTag; public static ResultHeaderTBAFragment newInstance(Result result) { Bundle args = new Bundle(); @@ -33,21 +22,22 @@ public static ResultHeaderTBAFragment newInstance(Result result) { return fragment; } - @Nullable @Override public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, Bundle savedInstanceState) { + @Nullable + @Override + public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, Bundle savedInstanceState) { assert getArguments() != null; - View v = inflater.inflate(R.layout.fragment_result_head_tba, container, false); - ButterKnife.bind(this, v); + FragmentResultHeadTbaBinding binding = FragmentResultHeadTbaBinding.inflate(inflater, container, false); Result result = (Result) getArguments().getSerializable(RESULT); assert result != null; long testedCount = result.countTotalMeasurements(); long blockedCount = result.countAnomalousMeasurements(); long availableCount = result.countOkMeasurements(); - tested.setText(getString(R.string.d, testedCount)); - blocked.setText(getString(R.string.d, blockedCount)); - available.setText(getString(R.string.d, availableCount)); - testedTag.setText(getResources().getQuantityText(R.plurals.TestResults_Summary_Websites_Hero_Tested, (int) testedCount)); - blockedTag.setText(getResources().getQuantityText(R.plurals.TestResults_Summary_Websites_Hero_Blocked, (int) blockedCount)); - availableTag.setText(getResources().getQuantityText(R.plurals.TestResults_Summary_Websites_Hero_Reachable, (int) availableCount)); - return v; + binding.tested.setText(getString(R.string.d, testedCount)); + binding.blocked.setText(getString(R.string.d, blockedCount)); + binding.available.setText(getString(R.string.d, availableCount)); + binding.testedTag.setText(getResources().getQuantityText(R.plurals.TestResults_Summary_Websites_Hero_Tested, (int) testedCount)); + binding.blockedTag.setText(getResources().getQuantityText(R.plurals.TestResults_Summary_Websites_Hero_Blocked, (int) blockedCount)); + binding.availableTag.setText(getResources().getQuantityText(R.plurals.TestResults_Summary_Websites_Hero_Reachable, (int) availableCount)); + return binding.getRoot(); } } diff --git a/app/src/main/java/org/openobservatory/ooniprobe/item/CircumventionItem.java b/app/src/main/java/org/openobservatory/ooniprobe/item/CircumventionItem.java index dc6df6a32..43c294e5e 100644 --- a/app/src/main/java/org/openobservatory/ooniprobe/item/CircumventionItem.java +++ b/app/src/main/java/org/openobservatory/ooniprobe/item/CircumventionItem.java @@ -4,23 +4,18 @@ import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; -import android.widget.TextView; - import androidx.core.content.ContextCompat; import androidx.core.graphics.drawable.DrawableCompat; import androidx.recyclerview.widget.RecyclerView; - +import localhost.toolkit.widget.recyclerview.HeterogeneousRecyclerItem; import org.openobservatory.ooniprobe.R; +import org.openobservatory.ooniprobe.databinding.ItemCircumventionBinding; import org.openobservatory.ooniprobe.model.database.Measurement; import org.openobservatory.ooniprobe.model.database.Network; import org.openobservatory.ooniprobe.model.database.Result; import java.util.Locale; -import butterknife.BindView; -import butterknife.ButterKnife; -import localhost.toolkit.widget.recyclerview.HeterogeneousRecyclerItem; - @Deprecated public class CircumventionItem extends HeterogeneousRecyclerItem { private final View.OnClickListener onClickListener; @@ -32,39 +27,35 @@ public CircumventionItem(Result extra, View.OnClickListener onClickListener, Vie this.onLongClickListener = onLongClickListener; } - @Override public CircumventionItem.ViewHolder onCreateViewHolder(LayoutInflater layoutInflater, ViewGroup viewGroup) { - return new CircumventionItem.ViewHolder(layoutInflater.inflate(R.layout.item_circumvention, viewGroup, false)); + @Override public ViewHolder onCreateViewHolder(LayoutInflater layoutInflater, ViewGroup viewGroup) { + return new ViewHolder(ItemCircumventionBinding.inflate(layoutInflater, viewGroup, false)); } - @Override public void onBindViewHolder(CircumventionItem.ViewHolder viewHolder) { + @Override public void onBindViewHolder(ViewHolder viewHolder) { viewHolder.itemView.setTag(extra); viewHolder.itemView.setOnClickListener(onClickListener); viewHolder.itemView.setOnLongClickListener(onLongClickListener); viewHolder.itemView.setBackgroundColor(ContextCompat.getColor(viewHolder.itemView.getContext(), extra.is_viewed ? android.R.color.transparent : R.color.color_yellow0)); - viewHolder.asnName.setText(Network.toString(viewHolder.asnName.getContext(), extra.network)); - viewHolder.startTime.setText(DateFormat.format(DateFormat.getBestDateTimePattern(Locale.getDefault(), "yMdHm"), extra.start_time)); + viewHolder.binding.asnName.setText(Network.toString(viewHolder.binding.asnName.getContext(), extra.network)); + viewHolder.binding.startTime.setText(DateFormat.format(DateFormat.getBestDateTimePattern(Locale.getDefault(), "yMdHm"), extra.start_time)); Long blocked = extra.countAnomalousMeasurements(); Long available = extra.countOkMeasurements(); - viewHolder.failedMeasurements.setText(viewHolder.failedMeasurements.getContext().getResources().getQuantityString(R.plurals.TestResults_Overview_Circumvention_Blocked, blocked.intValue(), blocked.toString())); - viewHolder.okMeasurements.setText(viewHolder.failedMeasurements.getContext().getResources().getQuantityString(R.plurals.TestResults_Overview_Circumvention_Available, available.intValue(), available.toString())); - viewHolder.failedMeasurements.setTextColor(ContextCompat.getColor(viewHolder.failedMeasurements.getContext(), blocked == 0 ? R.color.color_gray9 : R.color.color_yellow9)); - DrawableCompat.setTint(DrawableCompat.wrap(viewHolder.failedMeasurements.getCompoundDrawablesRelative()[0]).mutate(), ContextCompat.getColor(viewHolder.failedMeasurements.getContext(), blocked == 0 ? R.color.color_gray9 : R.color.color_yellow9)); + viewHolder.binding.failedMeasurements.setText(viewHolder.binding.failedMeasurements.getContext().getResources().getQuantityString(R.plurals.TestResults_Overview_Circumvention_Blocked, blocked.intValue(), blocked.toString())); + viewHolder.binding.okMeasurements.setText(viewHolder.binding.failedMeasurements.getContext().getResources().getQuantityString(R.plurals.TestResults_Overview_Circumvention_Available, available.intValue(), available.toString())); + viewHolder.binding.failedMeasurements.setTextColor(ContextCompat.getColor(viewHolder.binding.failedMeasurements.getContext(), blocked == 0 ? R.color.color_gray9 : R.color.color_yellow9)); + DrawableCompat.setTint(DrawableCompat.wrap(viewHolder.binding.failedMeasurements.getCompoundDrawablesRelative()[0]).mutate(), ContextCompat.getColor(viewHolder.binding.failedMeasurements.getContext(), blocked == 0 ? R.color.color_gray9 : R.color.color_yellow9)); boolean allUploaded = true; for (Measurement m : extra.getMeasurements()) allUploaded = allUploaded && (m.isUploaded() || m.is_failed); - viewHolder.startTime.setCompoundDrawablesRelativeWithIntrinsicBounds(0, 0, allUploaded ? 0 : R.drawable.cloudoff, 0); + viewHolder.binding.startTime.setCompoundDrawablesRelativeWithIntrinsicBounds(0, 0, allUploaded ? 0 : R.drawable.cloudoff, 0); } - class ViewHolder extends RecyclerView.ViewHolder { - @BindView(R.id.asnName) - TextView asnName; - @BindView(R.id.startTime) TextView startTime; - @BindView(R.id.failedMeasurements) TextView failedMeasurements; - @BindView(R.id.okMeasurements) TextView okMeasurements; + public static class ViewHolder extends RecyclerView.ViewHolder { + ItemCircumventionBinding binding; - ViewHolder(View itemView) { - super(itemView); - ButterKnife.bind(this, itemView); + ViewHolder(ItemCircumventionBinding binding) { + super(binding.getRoot()); + this.binding = binding; } } } diff --git a/app/src/main/java/org/openobservatory/ooniprobe/item/DateItem.java b/app/src/main/java/org/openobservatory/ooniprobe/item/DateItem.java index cecfb86d5..1c8edb3a0 100644 --- a/app/src/main/java/org/openobservatory/ooniprobe/item/DateItem.java +++ b/app/src/main/java/org/openobservatory/ooniprobe/item/DateItem.java @@ -2,20 +2,16 @@ import android.text.format.DateFormat; import android.view.LayoutInflater; -import android.view.View; import android.view.ViewGroup; -import android.widget.TextView; import androidx.recyclerview.widget.RecyclerView; -import org.openobservatory.ooniprobe.R; +import org.openobservatory.ooniprobe.databinding.ItemDateBinding; import java.text.SimpleDateFormat; import java.util.Date; import java.util.Locale; -import butterknife.BindView; -import butterknife.ButterKnife; import localhost.toolkit.widget.recyclerview.HeterogeneousRecyclerItem; @Deprecated @@ -28,19 +24,19 @@ public DateItem(Date extra) { } @Override public ViewHolder onCreateViewHolder(LayoutInflater layoutInflater, ViewGroup viewGroup) { - return new ViewHolder(layoutInflater.inflate(R.layout.item_date, viewGroup, false)); + return new ViewHolder(ItemDateBinding.inflate(layoutInflater, viewGroup, false)); } @Override public void onBindViewHolder(ViewHolder viewHolder) { - viewHolder.textView.setText(SDF.format(extra)); + viewHolder.binding.textView.setText(SDF.format(extra)); } class ViewHolder extends RecyclerView.ViewHolder { - @BindView(R.id.textView) TextView textView; + ItemDateBinding binding; - ViewHolder(View itemView) { - super(itemView); - ButterKnife.bind(this, itemView); + ViewHolder(ItemDateBinding binding) { + super(binding.getRoot()); + this.binding = binding; } } } diff --git a/app/src/main/java/org/openobservatory/ooniprobe/item/ExperimentalItem.java b/app/src/main/java/org/openobservatory/ooniprobe/item/ExperimentalItem.java index 2e4b1b3d4..011c69590 100644 --- a/app/src/main/java/org/openobservatory/ooniprobe/item/ExperimentalItem.java +++ b/app/src/main/java/org/openobservatory/ooniprobe/item/ExperimentalItem.java @@ -4,22 +4,17 @@ import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; -import android.widget.TextView; - import androidx.core.content.ContextCompat; import androidx.recyclerview.widget.RecyclerView; - +import localhost.toolkit.widget.recyclerview.HeterogeneousRecyclerItem; import org.openobservatory.ooniprobe.R; +import org.openobservatory.ooniprobe.databinding.ItemExperimentalBinding; import org.openobservatory.ooniprobe.model.database.Measurement; import org.openobservatory.ooniprobe.model.database.Network; import org.openobservatory.ooniprobe.model.database.Result; import java.util.Locale; -import butterknife.BindView; -import butterknife.ButterKnife; -import localhost.toolkit.widget.recyclerview.HeterogeneousRecyclerItem; - @Deprecated public class ExperimentalItem extends HeterogeneousRecyclerItem { private final View.OnClickListener onClickListener; @@ -32,7 +27,7 @@ public ExperimentalItem(Result extra, View.OnClickListener onClickListener, View } @Override public ViewHolder onCreateViewHolder(LayoutInflater layoutInflater, ViewGroup viewGroup) { - return new ViewHolder(layoutInflater.inflate(R.layout.item_experimental, viewGroup, false)); + return new ViewHolder(ItemExperimentalBinding.inflate(layoutInflater, viewGroup, false)); } @Override public void onBindViewHolder(ViewHolder viewHolder) { @@ -40,21 +35,21 @@ public ExperimentalItem(Result extra, View.OnClickListener onClickListener, View viewHolder.itemView.setOnClickListener(onClickListener); viewHolder.itemView.setOnLongClickListener(onLongClickListener); viewHolder.itemView.setBackgroundColor(ContextCompat.getColor(viewHolder.itemView.getContext(), extra.is_viewed ? android.R.color.transparent : R.color.color_yellow0)); - viewHolder.asnName.setText(Network.toString(viewHolder.asnName.getContext(), extra.network)); - viewHolder.startTime.setText(DateFormat.format(DateFormat.getBestDateTimePattern(Locale.getDefault(), "yMdHm"), extra.start_time)); + viewHolder.binding.totalMeasurements.setText(String.format("%d measured", extra.countTotalMeasurements())); + viewHolder.binding.asnName.setText(Network.toString(viewHolder.binding.asnName.getContext(), extra.network)); + viewHolder.binding.startTime.setText(DateFormat.format(DateFormat.getBestDateTimePattern(Locale.getDefault(), "yMdHm"), extra.start_time)); boolean allUploaded = true; for (Measurement m : extra.getMeasurements()) allUploaded = allUploaded && (m.isUploaded() || m.is_failed); - viewHolder.startTime.setCompoundDrawablesRelativeWithIntrinsicBounds(0, 0, allUploaded ? 0 : R.drawable.cloudoff, 0); + viewHolder.binding.startTime.setCompoundDrawablesRelativeWithIntrinsicBounds(0, 0, allUploaded ? 0 : R.drawable.cloudoff, 0); } - class ViewHolder extends RecyclerView.ViewHolder { - @BindView(R.id.asnName) TextView asnName; - @BindView(R.id.startTime) TextView startTime; + public static class ViewHolder extends RecyclerView.ViewHolder { + ItemExperimentalBinding binding; - ViewHolder(View itemView) { - super(itemView); - ButterKnife.bind(this, itemView); + ViewHolder(ItemExperimentalBinding binding) { + super(binding.getRoot()); + this.binding = binding; } } } diff --git a/app/src/main/java/org/openobservatory/ooniprobe/item/FailedItem.java b/app/src/main/java/org/openobservatory/ooniprobe/item/FailedItem.java index 24e6a524f..ca02bc838 100644 --- a/app/src/main/java/org/openobservatory/ooniprobe/item/FailedItem.java +++ b/app/src/main/java/org/openobservatory/ooniprobe/item/FailedItem.java @@ -4,19 +4,18 @@ import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; -import android.widget.ImageView; -import android.widget.TextView; import androidx.core.content.ContextCompat; import androidx.recyclerview.widget.RecyclerView; import org.openobservatory.ooniprobe.R; +import org.openobservatory.ooniprobe.databinding.ItemFailedBinding; import org.openobservatory.ooniprobe.model.database.Result; +import java.util.Date; import java.util.Locale; +import static java.util.concurrent.TimeUnit.*; -import butterknife.BindView; -import butterknife.ButterKnife; import localhost.toolkit.widget.recyclerview.HeterogeneousRecyclerItem; @Deprecated @@ -31,7 +30,7 @@ public FailedItem(Result extra, View.OnClickListener onClickListener, View.OnLon } @Override public ViewHolder onCreateViewHolder(LayoutInflater layoutInflater, ViewGroup viewGroup) { - return new ViewHolder(layoutInflater.inflate(R.layout.item_failed, viewGroup, false)); + return new ViewHolder(ItemFailedBinding.inflate(layoutInflater, viewGroup, false)); } @Override public void onBindViewHolder(ViewHolder viewHolder) { @@ -39,25 +38,33 @@ public FailedItem(Result extra, View.OnClickListener onClickListener, View.OnLon viewHolder.itemView.setOnClickListener(onClickListener); viewHolder.itemView.setOnLongClickListener(onLongClickListener); viewHolder.itemView.setBackgroundColor(ContextCompat.getColor(viewHolder.itemView.getContext(), R.color.color_gray2)); - viewHolder.testName.setTextColor(ContextCompat.getColor(viewHolder.itemView.getContext(), R.color.color_gray6)); - viewHolder.icon.setImageResource(extra.getTestSuite().getIcon()); - viewHolder.testName.setText(extra.getTestSuite().getTitle()); + viewHolder.binding.testName.setTextColor(ContextCompat.getColor(viewHolder.itemView.getContext(), R.color.color_gray6)); + viewHolder.binding.icon.setImageResource(extra.getTestSuite().getIcon()); + viewHolder.binding.testName.setText(extra.getTestSuite().getTitle()); String failure_msg = viewHolder.itemView.getContext().getString(R.string.TestResults_Overview_Error); - if (extra.failure_msg != null) + if (extra.failure_msg != null) { failure_msg += " - " + extra.failure_msg; - viewHolder.subtitle.setText(failure_msg); - viewHolder.startTime.setText(DateFormat.format(DateFormat.getBestDateTimePattern(Locale.getDefault(), "yMdHm"), extra.start_time)); + } else { + // NOTE: If the test is running for more than 5 minutes, we assume it's stuck or failed, + // and we show the default error message. + long MAX_DURATION = MILLISECONDS.convert(5, MINUTES); + long duration = new Date().getTime() - extra.start_time.getTime(); + if (duration < MAX_DURATION) { + failure_msg = viewHolder.itemView.getContext() + .getString(R.string.Dashboard_Running_Running) + .replace(":",""); + } + } + viewHolder.binding.subtitle.setText(failure_msg); + viewHolder.binding.startTime.setText(DateFormat.format(DateFormat.getBestDateTimePattern(Locale.getDefault(), "yMdHm"), extra.start_time)); } class ViewHolder extends RecyclerView.ViewHolder { - @BindView(R.id.testName) TextView testName; - @BindView(R.id.subtitle) TextView subtitle; - @BindView(R.id.startTime) TextView startTime; - @BindView(R.id.icon) ImageView icon; - - ViewHolder(View itemView) { - super(itemView); - ButterKnife.bind(this, itemView); + ItemFailedBinding binding; + + ViewHolder(ItemFailedBinding binding) { + super(binding.getRoot()); + this.binding = binding; } } } \ No newline at end of file diff --git a/app/src/main/java/org/openobservatory/ooniprobe/item/InstantMessagingItem.java b/app/src/main/java/org/openobservatory/ooniprobe/item/InstantMessagingItem.java index 15dd4dd12..7e2bb87fe 100644 --- a/app/src/main/java/org/openobservatory/ooniprobe/item/InstantMessagingItem.java +++ b/app/src/main/java/org/openobservatory/ooniprobe/item/InstantMessagingItem.java @@ -4,23 +4,18 @@ import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; -import android.widget.TextView; - import androidx.core.content.ContextCompat; import androidx.core.graphics.drawable.DrawableCompat; import androidx.recyclerview.widget.RecyclerView; - +import localhost.toolkit.widget.recyclerview.HeterogeneousRecyclerItem; import org.openobservatory.ooniprobe.R; +import org.openobservatory.ooniprobe.databinding.ItemInstantmessagingBinding; import org.openobservatory.ooniprobe.model.database.Measurement; import org.openobservatory.ooniprobe.model.database.Network; import org.openobservatory.ooniprobe.model.database.Result; import java.util.Locale; -import butterknife.BindView; -import butterknife.ButterKnife; -import localhost.toolkit.widget.recyclerview.HeterogeneousRecyclerItem; - @Deprecated public class InstantMessagingItem extends HeterogeneousRecyclerItem { private final View.OnClickListener onClickListener; @@ -33,7 +28,7 @@ public InstantMessagingItem(Result extra, View.OnClickListener onClickListener, } @Override public ViewHolder onCreateViewHolder(LayoutInflater layoutInflater, ViewGroup viewGroup) { - return new ViewHolder(layoutInflater.inflate(R.layout.item_instantmessaging, viewGroup, false)); + return new ViewHolder(ItemInstantmessagingBinding.inflate(layoutInflater, viewGroup, false)); } @Override public void onBindViewHolder(ViewHolder viewHolder) { @@ -41,29 +36,26 @@ public InstantMessagingItem(Result extra, View.OnClickListener onClickListener, viewHolder.itemView.setOnClickListener(onClickListener); viewHolder.itemView.setOnLongClickListener(onLongClickListener); viewHolder.itemView.setBackgroundColor(ContextCompat.getColor(viewHolder.itemView.getContext(), extra.is_viewed ? android.R.color.transparent : R.color.color_yellow0)); - viewHolder.asnName.setText(Network.toString(viewHolder.asnName.getContext(), extra.network)); - viewHolder.startTime.setText(DateFormat.format(DateFormat.getBestDateTimePattern(Locale.getDefault(), "yMdHm"), extra.start_time)); + viewHolder.binding.asnName.setText(Network.toString(viewHolder.binding.asnName.getContext(), extra.network)); + viewHolder.binding.startTime.setText(DateFormat.format(DateFormat.getBestDateTimePattern(Locale.getDefault(), "yMdHm"), extra.start_time)); Long blocked = extra.countAnomalousMeasurements(); Long available = extra.countOkMeasurements(); - viewHolder.failedMeasurements.setText(viewHolder.failedMeasurements.getContext().getResources().getQuantityString(R.plurals.TestResults_Overview_InstantMessaging_Blocked, blocked.intValue(), blocked.toString())); - viewHolder.okMeasurements.setText(viewHolder.failedMeasurements.getContext().getResources().getQuantityString(R.plurals.TestResults_Overview_InstantMessaging_Available, available.intValue(), available.toString())); - viewHolder.failedMeasurements.setTextColor(ContextCompat.getColor(viewHolder.failedMeasurements.getContext(), blocked == 0 ? R.color.color_gray9 : R.color.color_yellow9)); - DrawableCompat.setTint(DrawableCompat.wrap(viewHolder.failedMeasurements.getCompoundDrawablesRelative()[0]).mutate(), ContextCompat.getColor(viewHolder.failedMeasurements.getContext(), blocked == 0 ? R.color.color_gray9 : R.color.color_yellow9)); + viewHolder.binding.failedMeasurements.setText(viewHolder.binding.failedMeasurements.getContext().getResources().getQuantityString(R.plurals.TestResults_Overview_InstantMessaging_Blocked, blocked.intValue(), blocked.toString())); + viewHolder.binding.okMeasurements.setText(viewHolder.binding.failedMeasurements.getContext().getResources().getQuantityString(R.plurals.TestResults_Overview_InstantMessaging_Available, available.intValue(), available.toString())); + viewHolder.binding.failedMeasurements.setTextColor(ContextCompat.getColor(viewHolder.binding.failedMeasurements.getContext(), blocked == 0 ? R.color.color_gray9 : R.color.color_yellow9)); + DrawableCompat.setTint(DrawableCompat.wrap(viewHolder.binding.failedMeasurements.getCompoundDrawablesRelative()[0]).mutate(), ContextCompat.getColor(viewHolder.binding.failedMeasurements.getContext(), blocked == 0 ? R.color.color_gray9 : R.color.color_yellow9)); boolean allUploaded = true; for (Measurement m : extra.getMeasurements()) allUploaded = allUploaded && (m.isUploaded() || m.is_failed); - viewHolder.startTime.setCompoundDrawablesRelativeWithIntrinsicBounds(0, 0, allUploaded ? 0 : R.drawable.cloudoff, 0); + viewHolder.binding.startTime.setCompoundDrawablesRelativeWithIntrinsicBounds(0, 0, allUploaded ? 0 : R.drawable.cloudoff, 0); } - class ViewHolder extends RecyclerView.ViewHolder { - @BindView(R.id.asnName) TextView asnName; - @BindView(R.id.startTime) TextView startTime; - @BindView(R.id.failedMeasurements) TextView failedMeasurements; - @BindView(R.id.okMeasurements) TextView okMeasurements; + public static class ViewHolder extends RecyclerView.ViewHolder { + ItemInstantmessagingBinding binding; - ViewHolder(View itemView) { - super(itemView); - ButterKnife.bind(this, itemView); + ViewHolder(ItemInstantmessagingBinding binding) { + super(binding.getRoot()); + this.binding = binding; } } } diff --git a/app/src/main/java/org/openobservatory/ooniprobe/item/MeasurementItem.java b/app/src/main/java/org/openobservatory/ooniprobe/item/MeasurementItem.java index 570444b1a..657d46c9e 100644 --- a/app/src/main/java/org/openobservatory/ooniprobe/item/MeasurementItem.java +++ b/app/src/main/java/org/openobservatory/ooniprobe/item/MeasurementItem.java @@ -11,14 +11,14 @@ import androidx.recyclerview.widget.RecyclerView; import org.openobservatory.ooniprobe.R; +import org.openobservatory.ooniprobe.databinding.ItemMeasurementBinding; import org.openobservatory.ooniprobe.model.database.Measurement; import org.openobservatory.ooniprobe.test.test.AbstractTest; import org.openobservatory.ooniprobe.test.test.WebConnectivity; -import butterknife.BindView; -import butterknife.ButterKnife; import localhost.toolkit.widget.recyclerview.HeterogeneousRecyclerItem; +@Deprecated public class MeasurementItem extends HeterogeneousRecyclerItem { private final View.OnClickListener onClickListener; @@ -28,7 +28,7 @@ public MeasurementItem(Measurement extra, View.OnClickListener onClickListener) } @Override public ViewHolder onCreateViewHolder(LayoutInflater layoutInflater, ViewGroup viewGroup) { - return new ViewHolder(layoutInflater.inflate(R.layout.item_measurement, viewGroup, false)); + return new ViewHolder(ItemMeasurementBinding.inflate(layoutInflater, viewGroup, false)); } @Override public void onBindViewHolder(ViewHolder viewHolder) { @@ -65,12 +65,12 @@ else if (extra.isUploaded()) } class ViewHolder extends RecyclerView.ViewHolder { - @BindView(R.id.text) TextView text; + TextView text; - ViewHolder(View itemView) { - super(itemView); - ButterKnife.bind(this, itemView); - text.setOnClickListener(onClickListener); + ViewHolder(ItemMeasurementBinding binding) { + super(binding.getRoot()); + this.text = binding.text; + this.text.setOnClickListener(onClickListener); } } } diff --git a/app/src/main/java/org/openobservatory/ooniprobe/item/MeasurementPerfItem.java b/app/src/main/java/org/openobservatory/ooniprobe/item/MeasurementPerfItem.java index 5cffa6903..096672eed 100644 --- a/app/src/main/java/org/openobservatory/ooniprobe/item/MeasurementPerfItem.java +++ b/app/src/main/java/org/openobservatory/ooniprobe/item/MeasurementPerfItem.java @@ -4,21 +4,19 @@ import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; -import android.widget.TextView; import androidx.recyclerview.widget.RecyclerView; import org.openobservatory.ooniprobe.R; +import org.openobservatory.ooniprobe.databinding.ItemMeasurementPerfBinding; import org.openobservatory.ooniprobe.model.database.Measurement; import org.openobservatory.ooniprobe.test.test.Dash; import org.openobservatory.ooniprobe.test.test.HttpHeaderFieldManipulation; import org.openobservatory.ooniprobe.test.test.HttpInvalidRequestLine; import org.openobservatory.ooniprobe.test.test.Ndt; -import butterknife.BindView; -import butterknife.ButterKnife; import localhost.toolkit.widget.recyclerview.HeterogeneousRecyclerItem; - +@Deprecated public class MeasurementPerfItem extends HeterogeneousRecyclerItem { private final View.OnClickListener onClickListener; @@ -28,46 +26,43 @@ public MeasurementPerfItem(Measurement extra, View.OnClickListener onClickListen } @Override public ViewHolder onCreateViewHolder(LayoutInflater layoutInflater, ViewGroup viewGroup) { - return new ViewHolder(layoutInflater.inflate(R.layout.item_measurement_perf, viewGroup, false)); + return new ViewHolder(ItemMeasurementPerfBinding.inflate(layoutInflater, viewGroup, false)); } @Override public void onBindViewHolder(ViewHolder viewHolder) { viewHolder.itemView.setTag(extra); - Context c = viewHolder.text.getContext(); + Context c = viewHolder.binding.text.getContext(); if (extra.getTest().getLabelResId() == (R.string.Test_Experimental_Fullname)) - viewHolder.text.setText(extra.getTest().getName()); + viewHolder.binding.text.setText(extra.getTest().getName()); else - viewHolder.text.setText(extra.getTest().getLabelResId()); - viewHolder.text.setCompoundDrawablesRelativeWithIntrinsicBounds(0, 0, extra.is_failed || extra.isUploaded() ? 0 : R.drawable.cloudoff, 0); + viewHolder.binding.text.setText(extra.getTest().getLabelResId()); + viewHolder.binding.text.setCompoundDrawablesRelativeWithIntrinsicBounds(0, 0, extra.is_failed || extra.isUploaded() ? 0 : R.drawable.cloudoff, 0); if (extra.test_name.equals(Dash.NAME)) { - viewHolder.data1.setCompoundDrawablesRelativeWithIntrinsicBounds(R.drawable.video_quality, 0, 0, 0); - viewHolder.data1.setText(extra.getTestKeys().getVideoQuality(true)); - viewHolder.data2.setVisibility(View.GONE); + viewHolder.binding.data1.setCompoundDrawablesRelativeWithIntrinsicBounds(R.drawable.video_quality, 0, 0, 0); + viewHolder.binding.data1.setText(extra.getTestKeys().getVideoQuality(true)); + viewHolder.binding.data2.setVisibility(View.GONE); } else if (extra.test_name.equals(Ndt.NAME)) { - viewHolder.data1.setCompoundDrawablesRelativeWithIntrinsicBounds(R.drawable.download_black, 0, 0, 0); - viewHolder.data1.setText(c.getString(R.string.twoParam, extra.getTestKeys().getDownload(c), c.getString(extra.getTestKeys().getDownloadUnit()))); - viewHolder.data2.setVisibility(View.VISIBLE); - viewHolder.data2.setCompoundDrawablesRelativeWithIntrinsicBounds(R.drawable.upload_black, 0, 0, 0); - viewHolder.data2.setText(c.getString(R.string.twoParam, extra.getTestKeys().getUpload(c), c.getString(extra.getTestKeys().getUploadUnit()))); + viewHolder.binding.data1.setCompoundDrawablesRelativeWithIntrinsicBounds(R.drawable.download_black, 0, 0, 0); + viewHolder.binding.data1.setText(c.getString(R.string.twoParam, extra.getTestKeys().getDownload(c), c.getString(extra.getTestKeys().getDownloadUnit()))); + viewHolder.binding.data2.setVisibility(View.VISIBLE); + viewHolder.binding.data2.setCompoundDrawablesRelativeWithIntrinsicBounds(R.drawable.upload_black, 0, 0, 0); + viewHolder.binding.data2.setText(c.getString(R.string.twoParam, extra.getTestKeys().getUpload(c), c.getString(extra.getTestKeys().getUploadUnit()))); } else if (extra.test_name.equals(HttpHeaderFieldManipulation.NAME) || extra.test_name.equals(HttpInvalidRequestLine.NAME)) { - viewHolder.data1.setCompoundDrawablesRelativeWithIntrinsicBounds(R.drawable.test_middle_boxes_small, 0, 0, 0); - viewHolder.data1.setText(extra.is_anomaly ? + viewHolder.binding.data1.setCompoundDrawablesRelativeWithIntrinsicBounds(R.drawable.test_middle_boxes_small, 0, 0, 0); + viewHolder.binding.data1.setText(extra.is_anomaly ? c.getString(R.string.TestResults_Overview_MiddleBoxes_Found) : c.getString(R.string.TestResults_Overview_MiddleBoxes_NotFound)); - viewHolder.data2.setVisibility(View.GONE); + viewHolder.binding.data2.setVisibility(View.GONE); } } class ViewHolder extends RecyclerView.ViewHolder { - @BindView(R.id.text) TextView text; - @BindView(R.id.data1) TextView data1; - @BindView(R.id.data2) TextView data2; - - ViewHolder(View itemView) { - super(itemView); - ButterKnife.bind(this, itemView); - itemView.setOnClickListener(onClickListener); + ItemMeasurementPerfBinding binding; + ViewHolder(ItemMeasurementPerfBinding binding) { + super(binding.getRoot()); + this.binding = binding; + binding.getRoot().setOnClickListener(onClickListener); } } } diff --git a/app/src/main/java/org/openobservatory/ooniprobe/item/MiddleboxesItem.java b/app/src/main/java/org/openobservatory/ooniprobe/item/MiddleboxesItem.java index f776347cb..df9aeefc8 100644 --- a/app/src/main/java/org/openobservatory/ooniprobe/item/MiddleboxesItem.java +++ b/app/src/main/java/org/openobservatory/ooniprobe/item/MiddleboxesItem.java @@ -4,23 +4,18 @@ import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; -import android.widget.TextView; - import androidx.core.content.ContextCompat; import androidx.core.graphics.drawable.DrawableCompat; import androidx.recyclerview.widget.RecyclerView; - +import localhost.toolkit.widget.recyclerview.HeterogeneousRecyclerItem; import org.openobservatory.ooniprobe.R; +import org.openobservatory.ooniprobe.databinding.ItemMiddleboxesBinding; import org.openobservatory.ooniprobe.model.database.Measurement; import org.openobservatory.ooniprobe.model.database.Network; import org.openobservatory.ooniprobe.model.database.Result; import java.util.Locale; -import butterknife.BindView; -import butterknife.ButterKnife; -import localhost.toolkit.widget.recyclerview.HeterogeneousRecyclerItem; - /** * @deprecated * It is not possible to run a MiddleBoxesSuite anymore @@ -38,7 +33,7 @@ public MiddleboxesItem(Result extra, View.OnClickListener onClickListener, View. } @Override public ViewHolder onCreateViewHolder(LayoutInflater layoutInflater, ViewGroup viewGroup) { - return new ViewHolder(layoutInflater.inflate(R.layout.item_middleboxes, viewGroup, false)); + return new ViewHolder(ItemMiddleboxesBinding.inflate(layoutInflater, viewGroup, false)); } @Override public void onBindViewHolder(ViewHolder viewHolder) { @@ -46,35 +41,33 @@ public MiddleboxesItem(Result extra, View.OnClickListener onClickListener, View. viewHolder.itemView.setOnClickListener(onClickListener); viewHolder.itemView.setOnLongClickListener(onLongClickListener); viewHolder.itemView.setBackgroundColor(ContextCompat.getColor(viewHolder.itemView.getContext(), extra.is_viewed ? android.R.color.transparent : R.color.color_yellow0)); - viewHolder.asnName.setText(Network.toString(viewHolder.asnName.getContext(), extra.network)); - viewHolder.startTime.setText(DateFormat.format(DateFormat.getBestDateTimePattern(Locale.getDefault(), "yMdHm"), extra.start_time)); + viewHolder.binding.asnName.setText(Network.toString(viewHolder.binding.asnName.getContext(), extra.network)); + viewHolder.binding.startTime.setText(DateFormat.format(DateFormat.getBestDateTimePattern(Locale.getDefault(), "yMdHm"), extra.start_time)); if (extra.countAnomalousMeasurements() > 0) { - viewHolder.status.setText(R.string.TestResults_Overview_MiddleBoxes_Found); - viewHolder.status.setTextColor(ContextCompat.getColor(viewHolder.status.getContext(), R.color.color_yellow9)); - DrawableCompat.setTint(DrawableCompat.wrap(viewHolder.status.getCompoundDrawablesRelative()[0]).mutate(), ContextCompat.getColor(viewHolder.status.getContext(), R.color.color_yellow9)); + viewHolder.binding.status.setText(R.string.TestResults_Overview_MiddleBoxes_Found); + viewHolder.binding.status.setTextColor(ContextCompat.getColor(viewHolder.binding.status.getContext(), R.color.color_yellow9)); + DrawableCompat.setTint(DrawableCompat.wrap(viewHolder.binding.status.getCompoundDrawablesRelative()[0]).mutate(), ContextCompat.getColor(viewHolder.binding.status.getContext(), R.color.color_yellow9)); } else if (extra.countCompletedMeasurements() == 0) { - viewHolder.status.setText(R.string.TestResults_Overview_MiddleBoxes_Failed); - viewHolder.status.setTextColor(ContextCompat.getColor(viewHolder.status.getContext(), R.color.color_gray9)); - DrawableCompat.setTint(DrawableCompat.wrap(viewHolder.status.getCompoundDrawablesRelative()[0]).mutate(), ContextCompat.getColor(viewHolder.status.getContext(), R.color.color_gray9)); + viewHolder.binding.status.setText(R.string.TestResults_Overview_MiddleBoxes_Failed); + viewHolder.binding.status.setTextColor(ContextCompat.getColor(viewHolder.binding.status.getContext(), R.color.color_gray9)); + DrawableCompat.setTint(DrawableCompat.wrap(viewHolder.binding.status.getCompoundDrawablesRelative()[0]).mutate(), ContextCompat.getColor(viewHolder.binding.status.getContext(), R.color.color_gray9)); } else { - viewHolder.status.setText(R.string.TestResults_Overview_MiddleBoxes_NotFound); - viewHolder.status.setTextColor(ContextCompat.getColor(viewHolder.status.getContext(), R.color.color_gray9)); - DrawableCompat.setTint(DrawableCompat.wrap(viewHolder.status.getCompoundDrawablesRelative()[0]).mutate(), ContextCompat.getColor(viewHolder.status.getContext(), R.color.color_gray9)); + viewHolder.binding.status.setText(R.string.TestResults_Overview_MiddleBoxes_NotFound); + viewHolder.binding.status.setTextColor(ContextCompat.getColor(viewHolder.binding.status.getContext(), R.color.color_gray9)); + DrawableCompat.setTint(DrawableCompat.wrap(viewHolder.binding.status.getCompoundDrawablesRelative()[0]).mutate(), ContextCompat.getColor(viewHolder.binding.status.getContext(), R.color.color_gray9)); } boolean allUploaded = true; for (Measurement m : extra.getMeasurements()) allUploaded = allUploaded && (m.isUploaded() || m.is_failed); - viewHolder.startTime.setCompoundDrawablesRelativeWithIntrinsicBounds(0, 0, allUploaded ? 0 : R.drawable.cloudoff, 0); + viewHolder.binding.startTime.setCompoundDrawablesRelativeWithIntrinsicBounds(0, 0, allUploaded ? 0 : R.drawable.cloudoff, 0); } - class ViewHolder extends RecyclerView.ViewHolder { - @BindView(R.id.asnName) TextView asnName; - @BindView(R.id.startTime) TextView startTime; - @BindView(R.id.status) TextView status; + public static class ViewHolder extends RecyclerView.ViewHolder { + ItemMiddleboxesBinding binding; - ViewHolder(View itemView) { - super(itemView); - ButterKnife.bind(this, itemView); + ViewHolder(ItemMiddleboxesBinding binding) { + super(binding.getRoot()); + this.binding = binding; } } } diff --git a/app/src/main/java/org/openobservatory/ooniprobe/item/PerformanceItem.java b/app/src/main/java/org/openobservatory/ooniprobe/item/PerformanceItem.java index 0ab87895e..885c144f4 100644 --- a/app/src/main/java/org/openobservatory/ooniprobe/item/PerformanceItem.java +++ b/app/src/main/java/org/openobservatory/ooniprobe/item/PerformanceItem.java @@ -5,12 +5,11 @@ import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; -import android.widget.TextView; - import androidx.core.content.ContextCompat; import androidx.recyclerview.widget.RecyclerView; - +import localhost.toolkit.widget.recyclerview.HeterogeneousRecyclerItem; import org.openobservatory.ooniprobe.R; +import org.openobservatory.ooniprobe.databinding.ItemPerformanceBinding; import org.openobservatory.ooniprobe.fragment.resultHeader.ResultHeaderPerformanceFragment; import org.openobservatory.ooniprobe.model.database.Measurement; import org.openobservatory.ooniprobe.model.database.Network; @@ -20,10 +19,6 @@ import java.util.Locale; -import butterknife.BindView; -import butterknife.ButterKnife; -import localhost.toolkit.widget.recyclerview.HeterogeneousRecyclerItem; - @Deprecated public class PerformanceItem extends HeterogeneousRecyclerItem { private final View.OnClickListener onClickListener; @@ -36,7 +31,7 @@ public PerformanceItem(Result extra, View.OnClickListener onClickListener, View. } @Override public ViewHolder onCreateViewHolder(LayoutInflater layoutInflater, ViewGroup viewGroup) { - return new ViewHolder(layoutInflater.inflate(R.layout.item_performance, viewGroup, false)); + return new ViewHolder(ItemPerformanceBinding.inflate(layoutInflater, viewGroup, false)); } @Override public void onBindViewHolder(ViewHolder viewHolder) { @@ -45,32 +40,28 @@ public PerformanceItem(Result extra, View.OnClickListener onClickListener, View. viewHolder.itemView.setOnClickListener(onClickListener); viewHolder.itemView.setOnLongClickListener(onLongClickListener); viewHolder.itemView.setBackgroundColor(ContextCompat.getColor(c, extra.is_viewed ? android.R.color.transparent : R.color.color_yellow0)); - viewHolder.asnName.setText(Network.toString(viewHolder.asnName.getContext(), extra.network)); - viewHolder.startTime.setText(DateFormat.format(DateFormat.getBestDateTimePattern(Locale.getDefault(), "yMdHm"), extra.start_time)); + viewHolder.binding.asnName.setText(Network.toString(viewHolder.binding.asnName.getContext(), extra.network)); + viewHolder.binding.startTime.setText(DateFormat.format(DateFormat.getBestDateTimePattern(Locale.getDefault(), "yMdHm"), extra.start_time)); Measurement dashM = extra.getMeasurement(Dash.NAME); Measurement ndtM = extra.getMeasurement(Ndt.NAME); - viewHolder.quality.setText(dashM == null ? R.string.TestResults_NotAvailable : dashM.getTestKeys().getVideoQuality(false)); - viewHolder.upload.setText(ndtM == null ? c.getString(R.string.TestResults_NotAvailable) : c.getString(R.string.twoParam, ndtM.getTestKeys().getUpload(c), c.getString(ndtM.getTestKeys().getUploadUnit()))); - viewHolder.download.setText(ndtM == null ? c.getString(R.string.TestResults_NotAvailable) : c.getString(R.string.twoParam, ndtM.getTestKeys().getDownload(c), c.getString(ndtM.getTestKeys().getDownloadUnit()))); - viewHolder.quality.setAlpha(dashM == null ? ResultHeaderPerformanceFragment.ALPHA_DIS : ResultHeaderPerformanceFragment.ALPHA_ENA); - viewHolder.upload.setAlpha(ndtM == null ? ResultHeaderPerformanceFragment.ALPHA_DIS : ResultHeaderPerformanceFragment.ALPHA_ENA); - viewHolder.download.setAlpha(ndtM == null ? ResultHeaderPerformanceFragment.ALPHA_DIS : ResultHeaderPerformanceFragment.ALPHA_ENA); + viewHolder.binding.quality.setText(dashM == null ? R.string.TestResults_NotAvailable : dashM.getTestKeys().getVideoQuality(false)); + viewHolder.binding.upload.setText(ndtM == null ? c.getString(R.string.TestResults_NotAvailable) : c.getString(R.string.twoParam, ndtM.getTestKeys().getUpload(c), c.getString(ndtM.getTestKeys().getUploadUnit()))); + viewHolder.binding.download.setText(ndtM == null ? c.getString(R.string.TestResults_NotAvailable) : c.getString(R.string.twoParam, ndtM.getTestKeys().getDownload(c), c.getString(ndtM.getTestKeys().getDownloadUnit()))); + viewHolder.binding.quality.setAlpha(dashM == null ? ResultHeaderPerformanceFragment.ALPHA_DIS : ResultHeaderPerformanceFragment.ALPHA_ENA); + viewHolder.binding.upload.setAlpha(ndtM == null ? ResultHeaderPerformanceFragment.ALPHA_DIS : ResultHeaderPerformanceFragment.ALPHA_ENA); + viewHolder.binding.download.setAlpha(ndtM == null ? ResultHeaderPerformanceFragment.ALPHA_DIS : ResultHeaderPerformanceFragment.ALPHA_ENA); boolean allUploaded = true; for (Measurement m : extra.getMeasurements()) allUploaded = allUploaded && (m.isUploaded() || m.is_failed); - viewHolder.startTime.setCompoundDrawablesRelativeWithIntrinsicBounds(0, 0, allUploaded ? 0 : R.drawable.cloudoff, 0); + viewHolder.binding.startTime.setCompoundDrawablesRelativeWithIntrinsicBounds(0, 0, allUploaded ? 0 : R.drawable.cloudoff, 0); } - class ViewHolder extends RecyclerView.ViewHolder { - @BindView(R.id.asnName) TextView asnName; - @BindView(R.id.startTime) TextView startTime; - @BindView(R.id.upload) TextView upload; - @BindView(R.id.download) TextView download; - @BindView(R.id.quality) TextView quality; + public static class ViewHolder extends RecyclerView.ViewHolder { + ItemPerformanceBinding binding; - ViewHolder(View itemView) { - super(itemView); - ButterKnife.bind(this, itemView); + ViewHolder(ItemPerformanceBinding binding) { + super(binding.getRoot()); + this.binding = binding; } } } diff --git a/app/src/main/java/org/openobservatory/ooniprobe/item/SeperatorItem.java b/app/src/main/java/org/openobservatory/ooniprobe/item/SeperatorItem.java index 5b50974d5..c303f2b07 100644 --- a/app/src/main/java/org/openobservatory/ooniprobe/item/SeperatorItem.java +++ b/app/src/main/java/org/openobservatory/ooniprobe/item/SeperatorItem.java @@ -1,16 +1,15 @@ package org.openobservatory.ooniprobe.item; import android.view.LayoutInflater; -import android.view.View; import android.view.ViewGroup; import androidx.recyclerview.widget.RecyclerView; -import org.openobservatory.ooniprobe.R; +import org.openobservatory.ooniprobe.databinding.ItemSeperatorBinding; -import butterknife.ButterKnife; import localhost.toolkit.widget.recyclerview.HeterogeneousRecyclerItem; +@Deprecated public class SeperatorItem extends HeterogeneousRecyclerItem { public SeperatorItem() { @@ -18,15 +17,14 @@ public SeperatorItem() { } @Override public ViewHolderImpl onCreateViewHolder(LayoutInflater layoutInflater, ViewGroup viewGroup) { - return new ViewHolderImpl(layoutInflater.inflate(R.layout.item_seperator, viewGroup, false)); + return new ViewHolderImpl(ItemSeperatorBinding.inflate(layoutInflater, viewGroup, false)); } @Override public void onBindViewHolder(ViewHolderImpl holder) {} static class ViewHolderImpl extends RecyclerView.ViewHolder { - ViewHolderImpl(View itemView) { - super(itemView); - ButterKnife.bind(this, itemView); + ViewHolderImpl(ItemSeperatorBinding binding) { + super(binding.getRoot()); } } } diff --git a/app/src/main/java/org/openobservatory/ooniprobe/item/TestsuiteItem.java b/app/src/main/java/org/openobservatory/ooniprobe/item/TestsuiteItem.java index 98fb0f70c..a334a751b 100644 --- a/app/src/main/java/org/openobservatory/ooniprobe/item/TestsuiteItem.java +++ b/app/src/main/java/org/openobservatory/ooniprobe/item/TestsuiteItem.java @@ -1,27 +1,19 @@ package org.openobservatory.ooniprobe.item; -import android.content.Context; import android.content.res.Resources; -import android.graphics.Color; import android.graphics.PorterDuff; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; -import android.widget.ImageView; -import android.widget.TextView; - import androidx.cardview.widget.CardView; -import androidx.constraintlayout.widget.ConstraintLayout; import androidx.recyclerview.widget.RecyclerView; - +import localhost.toolkit.widget.recyclerview.HeterogeneousRecyclerItem; import org.openobservatory.ooniprobe.R; import org.openobservatory.ooniprobe.common.PreferenceManager; +import org.openobservatory.ooniprobe.databinding.ItemTestsuiteBinding; import org.openobservatory.ooniprobe.test.suite.AbstractSuite; -import butterknife.BindView; -import butterknife.ButterKnife; -import localhost.toolkit.widget.recyclerview.HeterogeneousRecyclerItem; - +@Deprecated public class TestsuiteItem extends HeterogeneousRecyclerItem { private final View.OnClickListener onClickListener; private final PreferenceManager preferenceManager; @@ -33,21 +25,21 @@ public TestsuiteItem(AbstractSuite extra, View.OnClickListener onClickListener, } @Override public ViewHolderImpl onCreateViewHolder(LayoutInflater layoutInflater, ViewGroup viewGroup) { - return new ViewHolderImpl(layoutInflater.inflate(R.layout.item_testsuite, viewGroup, false)); + return new ViewHolderImpl(ItemTestsuiteBinding.inflate(layoutInflater, viewGroup, false)); } @Override public void onBindViewHolder(ViewHolderImpl holder) { - holder.title.setText(extra.getTitle()); - holder.desc.setText(extra.getCardDesc()); - holder.icon.setImageResource(extra.getIconGradient()); + holder.binding.title.setText(extra.getTitle()); + holder.binding.desc.setText(extra.getCardDesc()); + holder.binding.icon.setImageResource(extra.getIconGradient()); holder.itemView.setTag(extra); if(extra.isTestEmpty(preferenceManager)) { ((CardView)holder.itemView).setElevation(0); Resources resources = holder.itemView.getContext().getResources(); ((CardView)holder.itemView).setCardBackgroundColor(resources.getColor(R.color.disabled_test_background)); - holder.title.setTextColor(resources.getColor(R.color.disabled_test_text)); - holder.desc.setTextColor(resources.getColor(R.color.disabled_test_text)); - holder.icon.setColorFilter(resources.getColor(R.color.disabled_test_text), PorterDuff.Mode.SRC_IN); + holder.binding.title.setTextColor(resources.getColor(R.color.disabled_test_text)); + holder.binding.desc.setTextColor(resources.getColor(R.color.disabled_test_text)); + holder.binding.icon.setColorFilter(resources.getColor(R.color.disabled_test_text), PorterDuff.Mode.SRC_IN); holder.setIsRecyclable(false); holder.itemView.setClickable(false); } else { @@ -55,14 +47,12 @@ public TestsuiteItem(AbstractSuite extra, View.OnClickListener onClickListener, } } - class ViewHolderImpl extends RecyclerView.ViewHolder { - @BindView(R.id.title) TextView title; - @BindView(R.id.desc) TextView desc; - @BindView(R.id.icon) ImageView icon; + static class ViewHolderImpl extends RecyclerView.ViewHolder { + ItemTestsuiteBinding binding; - ViewHolderImpl(View itemView) { - super(itemView); - ButterKnife.bind(this, itemView); + ViewHolderImpl(ItemTestsuiteBinding binding) { + super(binding.getRoot()); + this.binding = binding; } } } diff --git a/app/src/main/java/org/openobservatory/ooniprobe/item/TextItem.java b/app/src/main/java/org/openobservatory/ooniprobe/item/TextItem.java index 9d0bdb097..f3e6349c4 100644 --- a/app/src/main/java/org/openobservatory/ooniprobe/item/TextItem.java +++ b/app/src/main/java/org/openobservatory/ooniprobe/item/TextItem.java @@ -1,16 +1,12 @@ package org.openobservatory.ooniprobe.item; import android.view.LayoutInflater; -import android.view.View; import android.view.ViewGroup; -import android.widget.TextView; import androidx.recyclerview.widget.RecyclerView; -import org.openobservatory.ooniprobe.R; +import org.openobservatory.ooniprobe.databinding.ItemTextBinding; -import butterknife.BindView; -import butterknife.ButterKnife; import localhost.toolkit.widget.recyclerview.HeterogeneousRecyclerItem; public class TextItem extends HeterogeneousRecyclerItem { @@ -19,19 +15,19 @@ public TextItem(String extra) { } @Override public ViewHolder onCreateViewHolder(LayoutInflater layoutInflater, ViewGroup viewGroup) { - return new ViewHolder(layoutInflater.inflate(R.layout.item_text, viewGroup, false)); + return new ViewHolder(ItemTextBinding.inflate(layoutInflater, viewGroup, false)); } @Override public void onBindViewHolder(ViewHolder viewHolder) { - viewHolder.textView.setText(extra); + viewHolder.binding.textView.setText(extra); } class ViewHolder extends RecyclerView.ViewHolder { - @BindView(R.id.textView) TextView textView; + ItemTextBinding binding; - ViewHolder(View itemView) { - super(itemView); - ButterKnife.bind(this, itemView); + ViewHolder(ItemTextBinding binding) { + super(binding.getRoot()); + this.binding = binding; } } } diff --git a/app/src/main/java/org/openobservatory/ooniprobe/item/WebsiteItem.java b/app/src/main/java/org/openobservatory/ooniprobe/item/WebsiteItem.java index 063c2b1c1..b61265d7a 100644 --- a/app/src/main/java/org/openobservatory/ooniprobe/item/WebsiteItem.java +++ b/app/src/main/java/org/openobservatory/ooniprobe/item/WebsiteItem.java @@ -4,23 +4,18 @@ import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; -import android.widget.TextView; - import androidx.core.content.ContextCompat; import androidx.core.graphics.drawable.DrawableCompat; import androidx.recyclerview.widget.RecyclerView; - +import localhost.toolkit.widget.recyclerview.HeterogeneousRecyclerItem; import org.openobservatory.ooniprobe.R; +import org.openobservatory.ooniprobe.databinding.ItemWebsitesBinding; import org.openobservatory.ooniprobe.model.database.Measurement; import org.openobservatory.ooniprobe.model.database.Network; import org.openobservatory.ooniprobe.model.database.Result; import java.util.Locale; -import butterknife.BindView; -import butterknife.ButterKnife; -import localhost.toolkit.widget.recyclerview.HeterogeneousRecyclerItem; - @Deprecated public class WebsiteItem extends HeterogeneousRecyclerItem { private final View.OnClickListener onClickListener; @@ -32,38 +27,37 @@ public WebsiteItem(Result extra, View.OnClickListener onClickListener, View.OnLo this.onLongClickListener = onLongClickListener; } - @Override public ViewHolder onCreateViewHolder(LayoutInflater layoutInflater, ViewGroup viewGroup) { - return new ViewHolder(layoutInflater.inflate(R.layout.item_websites, viewGroup, false)); + @Override + public ViewHolder onCreateViewHolder(LayoutInflater layoutInflater, ViewGroup viewGroup) { + return new ViewHolder(ItemWebsitesBinding.inflate(layoutInflater, viewGroup, false)); } - @Override public void onBindViewHolder(ViewHolder viewHolder) { + @Override + public void onBindViewHolder(ViewHolder viewHolder) { viewHolder.itemView.setTag(extra); viewHolder.itemView.setOnClickListener(onClickListener); viewHolder.itemView.setOnLongClickListener(onLongClickListener); viewHolder.itemView.setBackgroundColor(ContextCompat.getColor(viewHolder.itemView.getContext(), extra.is_viewed ? android.R.color.transparent : R.color.color_yellow0)); - viewHolder.asnName.setText(Network.toString(viewHolder.asnName.getContext(), extra.network)); - viewHolder.startTime.setText(DateFormat.format(DateFormat.getBestDateTimePattern(Locale.getDefault(), "yMdHm"), extra.start_time)); + viewHolder.binding.asnName.setText(Network.toString(viewHolder.binding.asnName.getContext(), extra.network)); + viewHolder.binding.startTime.setText(DateFormat.format(DateFormat.getBestDateTimePattern(Locale.getDefault(), "yMdHm"), extra.start_time)); Long blocked = extra.countAnomalousMeasurements(); Long tested = extra.countTotalMeasurements(); - viewHolder.failedMeasurements.setText(viewHolder.failedMeasurements.getContext().getResources().getQuantityString(R.plurals.TestResults_Overview_Websites_Blocked, blocked.intValue(), blocked.toString())); - viewHolder.testedMeasurements.setText(viewHolder.failedMeasurements.getContext().getResources().getQuantityString(R.plurals.TestResults_Overview_Websites_Tested, tested.intValue(), tested.toString())); - viewHolder.failedMeasurements.setTextColor(ContextCompat.getColor(viewHolder.failedMeasurements.getContext(), blocked == 0 ? R.color.color_gray9 : R.color.color_yellow9)); - DrawableCompat.setTint(DrawableCompat.wrap(viewHolder.failedMeasurements.getCompoundDrawablesRelative()[0]).mutate(), ContextCompat.getColor(viewHolder.failedMeasurements.getContext(), blocked == 0 ? R.color.color_gray9 : R.color.color_yellow9)); + viewHolder.binding.failedMeasurements.setText(viewHolder.binding.failedMeasurements.getContext().getResources().getQuantityString(R.plurals.TestResults_Overview_Websites_Blocked, blocked.intValue(), blocked.toString())); + viewHolder.binding.testedMeasurements.setText(viewHolder.binding.failedMeasurements.getContext().getResources().getQuantityString(R.plurals.TestResults_Overview_Websites_Tested, tested.intValue(), tested.toString())); + viewHolder.binding.failedMeasurements.setTextColor(ContextCompat.getColor(viewHolder.binding.failedMeasurements.getContext(), blocked == 0 ? R.color.color_gray9 : R.color.color_yellow9)); + DrawableCompat.setTint(DrawableCompat.wrap(viewHolder.binding.failedMeasurements.getCompoundDrawablesRelative()[0]).mutate(), ContextCompat.getColor(viewHolder.binding.failedMeasurements.getContext(), blocked == 0 ? R.color.color_gray9 : R.color.color_yellow9)); boolean allUploaded = true; for (Measurement m : extra.getMeasurements()) allUploaded = allUploaded && (m.isUploaded() || m.is_failed); - viewHolder.startTime.setCompoundDrawablesRelativeWithIntrinsicBounds(0, 0, allUploaded ? 0 : R.drawable.cloudoff, 0); + viewHolder.binding.startTime.setCompoundDrawablesRelativeWithIntrinsicBounds(0, 0, allUploaded ? 0 : R.drawable.cloudoff, 0); } - class ViewHolder extends RecyclerView.ViewHolder { - @BindView(R.id.asnName) TextView asnName; - @BindView(R.id.startTime) TextView startTime; - @BindView(R.id.failedMeasurements) TextView failedMeasurements; - @BindView(R.id.testedMeasurements) TextView testedMeasurements; + public static class ViewHolder extends RecyclerView.ViewHolder { + ItemWebsitesBinding binding; - ViewHolder(View itemView) { - super(itemView); - ButterKnife.bind(this, itemView); + ViewHolder(ItemWebsitesBinding binding) { + super(binding.getRoot()); + this.binding = binding; } } } \ No newline at end of file diff --git a/app/src/main/java/org/openobservatory/ooniprobe/test/TestAsyncTask.java b/app/src/main/java/org/openobservatory/ooniprobe/test/TestAsyncTask.java index 5d3e685cd..f7e642221 100644 --- a/app/src/main/java/org/openobservatory/ooniprobe/test/TestAsyncTask.java +++ b/app/src/main/java/org/openobservatory/ooniprobe/test/TestAsyncTask.java @@ -64,6 +64,7 @@ public class TestAsyncTask extends AsyncTask implements Abst private String proxy; private boolean store_db = true; + private boolean unattended; public static List getSuites() { return Arrays.asList(new WebsitesSuite(), new InstantMessagingSuite(), new CircumventionSuite(), new PerformanceSuite(), new ExperimentalSuite()); @@ -75,9 +76,10 @@ public TestAsyncTask(Application app, ArrayList testSuites) { this.proxy = app.getPreferenceManager().getProxyURL(); } - public TestAsyncTask(Application app, ArrayList testSuites, boolean store_db) { + public TestAsyncTask(Application app, ArrayList testSuites, boolean store_db, boolean unattended) { this(app, testSuites); this.store_db = store_db; + this.unattended = unattended; } private void registerConnChange() { @@ -159,8 +161,15 @@ private void runTest(AbstractTest... tests) { //This uses the wrapper private void downloadURLs() { try { - OONISession session = EngineProvider.get().newSession(EngineProvider.get().getDefaultSessionConfig( - app, BuildConfig.SOFTWARE_NAME, BuildConfig.VERSION_NAME, new LoggerArray(), proxy)); + OONISession session = EngineProvider.get().newSession( + EngineProvider.get().getDefaultSessionConfig( + app, + unattended ? String.join("-", BuildConfig.SOFTWARE_NAME, AbstractTest.UNATTENDED) : BuildConfig.SOFTWARE_NAME, + BuildConfig.VERSION_NAME, + new LoggerArray(), + proxy + ) + ); OONIContext ooniContext = session.newContextWithTimeout(30); OONICheckInConfig config = app.getOONICheckInConfig(); diff --git a/app/src/main/java/org/openobservatory/ooniprobe/test/suite/CircumventionSuite.java b/app/src/main/java/org/openobservatory/ooniprobe/test/suite/CircumventionSuite.java index 6f3928f8a..2addf9da5 100644 --- a/app/src/main/java/org/openobservatory/ooniprobe/test/suite/CircumventionSuite.java +++ b/app/src/main/java/org/openobservatory/ooniprobe/test/suite/CircumventionSuite.java @@ -43,11 +43,6 @@ public static CircumventionSuite initForAutoRun() { list.add(new Psiphon()); if (pm == null || pm.isTestTor()) list.add(new Tor()); - /* TODO (aanorbel): Riseup VPN Disabled. - The riseupvpn experiment has been quite flaky for quite some time. - To be enabled only when test is fixed or removed if deemed necessary. - if (pm == null || pm.isTestRiseupVPN()) - list.add(new RiseupVPN());*/ super.setTestList(Lists.transform(list, test -> { if (getAutoRun()) test.setOrigin(AbstractTest.AUTORUN); return test; diff --git a/app/src/main/java/org/openobservatory/ooniprobe/test/suite/ExperimentalSuite.java b/app/src/main/java/org/openobservatory/ooniprobe/test/suite/ExperimentalSuite.java index 7ad7812d9..9d7086651 100644 --- a/app/src/main/java/org/openobservatory/ooniprobe/test/suite/ExperimentalSuite.java +++ b/app/src/main/java/org/openobservatory/ooniprobe/test/suite/ExperimentalSuite.java @@ -40,12 +40,14 @@ public AbstractTest[] getTestList(@Nullable PreferenceManager pm) { if (super.getTestList(pm) == null) { ArrayList list = new ArrayList<>(); if (pm == null || pm.isExperimentalOn()){ + list.add(new Experimental("stunreachability")); + list.add(new Experimental("dnscheck")); + list.add(new Experimental("riseupvpn")); + list.add(new Experimental("echcheck")); if ((pm == null || pm.isLongRunningTestsInForeground()) || getAutoRun()){ list.add(new Experimental("torsf")); list.add(new Experimental("vanilla_tor")); - } - list.add(new Experimental("stunreachability")); - list.add(new Experimental("dnscheck")); + } } super.setTestList(Lists.transform(list, test -> { if (getAutoRun()) test.setOrigin(AbstractTest.AUTORUN); diff --git a/app/src/main/java/org/openobservatory/ooniprobe/test/test/AbstractTest.java b/app/src/main/java/org/openobservatory/ooniprobe/test/test/AbstractTest.java index 2f66abdb7..d1e964b38 100644 --- a/app/src/main/java/org/openobservatory/ooniprobe/test/test/AbstractTest.java +++ b/app/src/main/java/org/openobservatory/ooniprobe/test/test/AbstractTest.java @@ -319,8 +319,14 @@ private void setDataUsage(EventResult.Value value, Result result) { } private void setFailureMsg(EventResult.Value value, Result result) { - if (result == null) return; - result.failure_msg = value.failure; + if (result == null) { + return; + } + if (result.failure_msg == null) { + result.failure_msg = value.failure; + } else { + result.failure_msg = String.format("%s\n\n%s", result.failure_msg, value.failure); + } result.save(); } diff --git a/app/src/main/java/org/openobservatory/ooniprobe/test/test/RiseupVPN.java b/app/src/main/java/org/openobservatory/ooniprobe/test/test/RiseupVPN.java index 52601f065..2339afbbe 100644 --- a/app/src/main/java/org/openobservatory/ooniprobe/test/test/RiseupVPN.java +++ b/app/src/main/java/org/openobservatory/ooniprobe/test/test/RiseupVPN.java @@ -17,19 +17,29 @@ import org.openobservatory.ooniprobe.model.jsonresult.JsonResult; import org.openobservatory.ooniprobe.model.settings.Settings; +/** + * Represents the RiseupVPN test. + * + * @deprecated This test has been demoted to experimental in Chore: Moved riseup vpn to experimental suite and correct tests. + * This test has been moved to experimental because it causes too many false positive. + */ public class RiseupVPN extends AbstractTest { public static final String NAME = "riseupvpn"; public RiseupVPN() { - super(NAME, R.string.Test_RiseupVPN_Fullname, R.drawable.test_riseupvpn, R.string.urlTestRvpn, 15); + // NOTE: this test has been demoted to experimental (see https://github.com/ooni/probe-android/pull/632) + // and such the icon resource `R.drawable.test_riseupvpn` is not displayed anymore. + super(NAME, R.string.Test_Experimental_Fullname, 0, R.string.urlTestRvpn, 15); } - @Override public void run(Context c, PreferenceManager pm, AppLogger logger, Gson gson, Result result, int index, AbstractTest.TestCallback testCallback) { + @Override + public void run(Context c, PreferenceManager pm, AppLogger logger, Gson gson, Result result, int index, AbstractTest.TestCallback testCallback) { Settings settings = new Settings(c, pm, isAutoRun()); - run(c, pm,logger, gson, settings, result, index, testCallback); + run(c, pm, logger, gson, settings, result, index, testCallback); } - @Override public void onEntry(Context c, PreferenceManager pm, @NonNull JsonResult json, Measurement measurement) { + @Override + public void onEntry(Context c, PreferenceManager pm, @NonNull JsonResult json, Measurement measurement) { super.onEntry(c, pm, json, measurement); //When json.test_keys.transport_status is null the test is failed so the result of is_anomaly doesn't matter. if (json.test_keys == null || json.test_keys.transport_status == null) { @@ -37,8 +47,8 @@ public RiseupVPN() { return; } boolean isTransportBlocked = false; - isTransportBlocked = MapUtility.getOrDefaultCompat(json.test_keys.transport_status, "openvpn", "ok").equals(BLOCKED) || - MapUtility.getOrDefaultCompat(json.test_keys.transport_status, "obfs4", "ok").equals(BLOCKED); + isTransportBlocked = MapUtility.getOrDefaultCompat(json.test_keys.transport_status, "openvpn", "ok").equals(BLOCKED) || + MapUtility.getOrDefaultCompat(json.test_keys.transport_status, "obfs4", "ok").equals(BLOCKED); measurement.is_anomaly = !json.test_keys.ca_cert_status || json.test_keys.api_failure != null || isTransportBlocked; } diff --git a/app/src/main/res/drawable/keyboard_arrow_down.xml b/app/src/main/res/drawable/keyboard_arrow_down.xml new file mode 100644 index 000000000..9e345b86c --- /dev/null +++ b/app/src/main/res/drawable/keyboard_arrow_down.xml @@ -0,0 +1,5 @@ + + + diff --git a/app/src/main/res/drawable/keyboard_arrow_up.xml b/app/src/main/res/drawable/keyboard_arrow_up.xml new file mode 100644 index 000000000..1227182e4 --- /dev/null +++ b/app/src/main/res/drawable/keyboard_arrow_up.xml @@ -0,0 +1,5 @@ + + + diff --git a/app/src/main/res/layout/activity_proxy.xml b/app/src/main/res/layout/activity_proxy.xml index d3c565ed4..7f49e920d 100644 --- a/app/src/main/res/layout/activity_proxy.xml +++ b/app/src/main/res/layout/activity_proxy.xml @@ -49,22 +49,30 @@ android:id="@+id/customProxyRadioGroup" android:layout_width="match_parent" android:layout_height="match_parent" - android:layout_marginStart="20dp" - android:checkedButton="@id/customProxySOCKS5"> - - + android:layout_marginStart="20dp"> - + + + + android:inputType="textUri"/> @@ -93,10 +101,10 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:hint="@string/Settings_Proxy_Custom_Port" + android:textSize="12sp" android:textColorHint="@color/color_gray6" android:textColor="@android:color/black" - android:inputType="number" - /> + android:inputType="number"/> diff --git a/app/src/main/res/layout/activity_result_detail.xml b/app/src/main/res/layout/activity_result_detail.xml index c09cf62ad..ea34bc7aa 100644 --- a/app/src/main/res/layout/activity_result_detail.xml +++ b/app/src/main/res/layout/activity_result_detail.xml @@ -57,12 +57,11 @@ - - @@ -160,7 +160,7 @@ android:entries="@array/filterTests" android:paddingVertical="8dp" android:textAlignment="center" /> - + - \ No newline at end of file + diff --git a/app/src/main/res/layout/item_experimental.xml b/app/src/main/res/layout/item_experimental.xml index b2b83044a..c3eedd18a 100644 --- a/app/src/main/res/layout/item_experimental.xml +++ b/app/src/main/res/layout/item_experimental.xml @@ -52,4 +52,19 @@ android:textColor="@color/color_gray7" /> + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/item_measurement.xml b/app/src/main/res/layout/item_measurement.xml index f19216741..c27046ec2 100644 --- a/app/src/main/res/layout/item_measurement.xml +++ b/app/src/main/res/layout/item_measurement.xml @@ -1,9 +1,49 @@ - \ No newline at end of file + + + + + + + + + + + + diff --git a/app/src/main/res/values-el/strings.xml b/app/src/main/res/values-el/strings.xml index 1e604e55f..daf2564fe 100644 --- a/app/src/main/res/values-el/strings.xml +++ b/app/src/main/res/values-el/strings.xml @@ -72,7 +72,7 @@ Εκτέλεση νέων πειραματικών δοκιμών Εκτέλεσε τις παρακάτω νέες πειραματικές δοκιμές που αναπτύχθηκαν από την ομάδα του OONI:\n%1$s\n\nΤα αποτελέσματά σου θα δημοσιευθούν στο [OONI Explorer](https://explorer.ooni.org/) και στο [OONI API](https://api.ooni.io/). Οι παρακάτω δοκιμές θα εκτελούνται μόνο ως μέρος των αυτοματοποιημένων δοκιμών: - Disabled Tests + Απενεργοποιημένες δοκιμές Gbit/s Mbit/s kbit/s @@ -377,8 +377,8 @@ Δημοσιεύοντας αποτελέσματα, αυξάνεις τη διαφάνεια για τυχόν παρεμβολές στο Διαδικτύο, και υποστηρίζεις την κοινότητα του OONI.\n\nΓια την αναγνώριση των παρόχων δικτύου, απαιτούνται πληροφορίες δικτύου (όπως ο Αριθμός Αυτόνομου Συστήματος ASN). Επιλογές δοκιμής Οι παραμετροποιήσεις που κάνετε μέσω των παραπάνω ρυθμίσεων δοκιμών (π.χ. απενεργοποίηση της δοκιμής WhatsApp) θα εφαρμόζονται τόσο σε δοκιμές που εκτελούνται χειροκίνητα, όσο και σε δοκιμές που εκτελούνται αυτόματα (όταν έχετε ενεργοποιημένες τις αυτοματοποιημένες δοκιμές). - Long running test - Run long running tests in foreground? + Δοκιμή μεγάλης διάρκειας + Να εκτελούνται οι δοκιμές μεγάλης διάρκειας στο προσκήνιο; Απόρρητο Αποστολή αναφορών σφαλμάτων Ρυθμίσεις για προχωρημένους/ες diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index a7167cb8d..f2b9b157c 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -160,7 +160,7 @@ Copier l’URL de l’explorateur Partager l’URL de l’explorateur Copier dans le presse-papiers - Afficher dans l’Explorateur OONI + Dans l’Explorateur OONI Échec Vous pouvez essayer de relancer ce test Réessayer diff --git a/app/src/main/res/values-id/strings.xml b/app/src/main/res/values-id/strings.xml index 106cce24b..5c885175e 100644 --- a/app/src/main/res/values-id/strings.xml +++ b/app/src/main/res/values-id/strings.xml @@ -377,32 +377,32 @@ Dengan mempublikasikan hasil, Anda meningkatkan transpaansi intervensi jaringan dan mendukung komunitas OONI.\n\nInformasi jaringan (yaitu, Nomor Sistem Otonom) dibutuhkan untuk mengidentifikasi Penyedia Layanan Internet. Pilihan Tes 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? + Uji yang berjalan lama + Jalankan uji yang berlangsung lama di latar depan? Kerahasiaan pribadi Kirim laporan tabrakan Advanced Mode Gelap Log Debug - See recent logs + Lihat log baru-baru ini Pengaturan Bahasa Pilih Bahasa - Always use domain fronting - OONI backend proxy + Selalu memakai domain fronting + Proksi back end OONI Proksi Tidak ada Psiphon - Custom Proxy - Custom Proxy URL - Custom proxy protocol + Proxy Custom + URL Proxy Custom + Protokol Proxy Custom Koneksi Nama host Port - Credentials (optional) + Kredensial (opsional) Nama pengguna Kata sandi - Use Psiphon over custom proxy - Are you unable to use OONI Probe? Try enabling [Psiphon](https://psiphon.ca/) to circumvent potential OONI Probe blocking. Alternatively, you can use a custom proxy. + Gunakan Psiphon melalui proxy khusus + Apakah anda tidak dapat menggunakan OONI Probe? Coba nyalakan [Psiphon](https://psiphon.ca/) untuk mengelabuhi pemblokiran OONI Probe. Anda juga dapat menggunakan proxy khusus untuk mengelabuhi. Batas durasi tes Durasi tes Kategori laman yang akan dites @@ -420,7 +420,7 @@ URL belum dimasukkan Jalankan Tambah laman - Load from template + Muat dari templat Jumlah laman yang sudah dites (0 artinya semua) Tes WhatsApp Tes Telegram @@ -439,7 +439,7 @@ Tes Psiphon Tes Tor Tes RiseupVPN - Warn when VPN is in use + Peringatkan ketika VPN sedang dipakai Kirim surel ke pendukung Silakan deskripsikan masalah yang Anda alami: Silakan kirim surel ke bugs@openobservatory.org dengan informasi aplikasi dan versi iOS. Ketuk \"Salin ke clipboard\" di bawah untuk salin alamat surel kami. @@ -449,7 +449,7 @@ Penyimpanan yang digunakan Hapus Bersihkan - You are about to delete all OONI measurements from your device. If uploaded, they will still be available on [OONI Explorer](https://explorer.ooni.org) + Anda akan menghapus semua pengukuran OONI dari perangkat Anda. Bila diunggah, mereka akan masih tersedia pada [OONI Explorer](https://explorer.ooni.org) Selesai dijalani Hentikan tes Coba mirror @@ -520,7 +520,7 @@ Peralatan keamanan komputer dan berita Peralatan komunikasi individual dan grup termasuk VoIP, messaging dan webmail Pembagian video, audio dan foto - Web hosting, blogging and other online publishing + Hosting web, blogging, dan publikasi daring lain. Mesin pencari dan portal Permainan online dan platform permainan (tidak termasuk situs perjudian) Hiburan termasuk sejarah, literatur, musik, satir dan humor diff --git a/app/src/main/res/values-my/strings.xml b/app/src/main/res/values-my/strings.xml new file mode 100644 index 000000000..3fa7ffd7c --- /dev/null +++ b/app/src/main/res/values-my/strings.xml @@ -0,0 +1,533 @@ + + + OONI Probe + OONI Probe ဆိုတာ ဘာလဲ။ + သင့်အက်ပ်သည် အင်တာနက် ဆင်ဆာဖြတ်တောက်မှုကို တိုင်းတာရန်။\n\nဝဘ်ဆိုက်များနှင့် ဆိုရှယ်မီဒီယာအက်ပ်များကို ပိတ်ဆို့ နေပါသလား။ သင့်အင်တာနက် ချိတ်ဆက်မှုသည် ပုံမှန်မဟုတ်ဘဲ နှေးနေပါသလား။\n\n၄င်းအကြောင်းအရာတို့ကို သိရှိရန် OONI Probe ကိုဖွင့်ပါ။ + ရပြီ + ကြိုတင်အသိပေးသည်။ + OONI ဒေတာကို ပွင့်လင်းမြင်သာစွာ ထုတ်ဝေထားပြီး သင့်ကွန်ရက် အချက်အလက် များကိုလည်း ပါဝင်မည် ဖြစ်သည်။ + သင့် အင်တာနက် အသုံးပြုမှုကို စောင့်ကြည့်နေသူတိုင်း (အစိုးရ သို့မဟုတ် အင်တာနက် ဝန်ဆောင်မှု ပံ့ပိုးသူ) သည် OONI Probe အား သင် အသုံးပြုနေကြောင်း တွေ့မြင်ရမည်။ + တားမြစ်ထားသော ဝဘ်ဆိုဒ်များကို သင် စမ်းသပ်နိုင်သည် (သို့သော် စမ်းသပ်မည့် ဆိုဒ်များကို သင် ရွေးချယ်နိုင်သည်)။ + နားလည်သည် + ပိုမိုလေ့လာရန် + လျှပ်တပြက် ဉာဏ်စမ်း + မှန်သည် + မှားသည် + နောက်သို့ + ဆက်လုပ်မည် + မေးခွန်း ၁/၂ + တစ်စုံတစ်ယောက်သည် ကျွန်ုပ်၏ အင်တာနက်လုပ်ဆောင်ချက်ကို စောင့်ကြည့်နေပါက၊ ကျွန်ုပ်သည် OONI Probe ကို အသုံးပြုနေကြောင်း ၎င်းတို့ မြင်တွေ့ရမည် ဖြစ်သည်။ + သတိပေးချက် + OONI Probe သည် ကိုယ်ရေးလုံခြုံမှု ကိရိယာတစ်ခု မဟုတ်ပါ။ သင့် အင်တာနက် လုပ်ဆောင်ချက်များကို စောင့်ကြည့် နေသူတိုင်းသည် သင် အသုံးပြုနေသည့် ဆော့ဖ်ဝဲလ်ကို သိပါလိမ့်မည်။ + မေးခွန်း ၂/၂ + OONI Probe ကို ကျွန်ုပ်အသုံးပြုတိုင်း၊ ကျွန်ုပ် စုဆောင်းထားသော ကွန်ရက်ဒေတာကို အလိုအလျောက် ထုတ်ဝေပါမည်။ + သတိပေးချက် + အင်တာနက် ဆင်ဆာဖြတ်တောက်မှု ပွင့်လင်းမြင်သာရေး တိုးမြင့်စေရန် OONI Probe အသုံးပြုသူများ အားလုံး၏ ကွန်ရက် ဒေတာများအား အလိုအလျောက် ထုတ်ဝေပါမည် (ဆက်တင်တွင် ရွေးချယ်ထားပါက)။ + အလိုအလျောက် စမ်းသပ်ခြင်း + နေ့စဉ် အင်တာနက် ဆင်ဆာဖြတ်တောက်မှုကို တိုင်းတာရန်အတွက် OONI Probe သည် စမ်းသပ်မှုများကို အခါအားလျော်စွာ လုပ်ဆောင်နိုင်စေရန် အလိုအလျောက် စမ်းသပ်မှုကို ဖွင့်ပါ။\n\nစိတ်မပူပါနှင့်၊ ဘက်ထရီ အသုံးပြုမှုကို ကျွန်ုပ်တို့ သတိထားပါမည်။\n\nဆက်တင်များမှ အလိုအလျောက် စမ်းသပ်ခြင်းကို အချိန်မရွေး ပိတ်နိုင်သည်။ + ပျက်စီးမှု သတင်းပို့ခြင်း + OONI Probe ပိုမိုကောင်းမွန်စေရန်အတွက် အက်ပ်သည် ကောင်းမွန်စွာ အလုပ်မလုပ်သည့်အခါ အမည်မသိ ပျက်စီးမှု အစီရင်ခံစာများကို စုဆောင်းလိုပါသည်။\n\nပျက်စီးမှု အစီရင်ခံစာများကို OONI ဖွံ့ဖြိုးတိုးတက်ရေး အဖွဲ့ထံ တင်သွင်းခြင်း လုပ်ငန်းတွင် သင် ပါဝင်လိုပါသလား။ + ဟုတ်တယ် + မဟုတ်ဘူး + နဂိုမူလ ဆက်တင်များ + ကျွန်ုပ်တို့ ကောက်ယူစုဆောင်း၍ ဖြန့်ဝေသည် - + နိုင်ငံကုဒ် (ဥပမာ - အီတလီအတွက် IT) + ကွန်ရက် သတင်းအချက်အလက် (အလိုအလျောက်စနစ် နံပတ်အပါအဝင်) + စမ်းသပ်ချိန်နှင့် ရက်စွဲ + သင်၏ IP လိပ်စာ သို့မဟုတ် အခြားသော ပုဂ္ဂိုလ်ရေးအရ ခွဲခြား သိမြင်နိုင်သော အချက်အလက်များကို ထုတ်ဝေခြင်းမပြုရန် ကျွန်ုပ်တို့ အကောင်းဆုံး လုပ်ဆောင်ပါသည်။ [OONI ၏ ဒေတာမူဝါဒ](https://ooni.org/about/data-policy/) တွင် ပိုမိုလေ့လာပါ။ + OONI Probe တိုးတက်စေရေးအတွက် ကျွန်ုပ်တို့အားကူညီနိုင်ရန် \"အိုကေ\" ကို နှိပ်၍ ပျက်စီးမှု အစီရင်ခံစာများကို မျှဝေနိုင်သည်။ + သွားကြစို့ + နဂိုမူလများကို ပြောင်းမည် + ဒက်ရှ်ဘုတ် + စမ်းမည် + အဖြေမရှိ + စမ်းမည် + နောက်ဆုံး စမ်းသပ်မှု - + ခန့်မှန်းထားသည် - + ဝဘ်ဆိုဒ်များ ရွေးချယ်ရန် + စမ်းသပ်နေသည် - + ခန့်မှန်း ကျန်ရှိချိန် - + %1$s စက္ကန့် + စမ်းသပ်မှုကို ပြင်ဆင်နေသည် + ETA တွက်ချက်ခြင်း + မှတ်တမ်း ပြမည် + မှတ်တမ်း ပိတ်မည် + စမ်းသပ်မှုကို ရပ်နေသည်... + လက်ရှိ စောင့်ဆိုင်းနေသော စမ်းသပ်မှုများကို အပြီးသတ်နေသည်၊ ကျေးဇူးပြု၍ စောင့်ပါ... + အသုံးပြုနေသော ပရောက်စီ + နောက်ထပ် ရွေးချယ်စရာများအတွက် ကတ်ကို နှိပ်ပါ + ~%1$ss + ဝဘ်ဆိုဒ်များ ပိတ်ဆို့ခြင်းကို စမ်းသပ်ပါ\n + OONI ၏ [ဝဘ်ချိတ်ဆက်မှု စမ်းသပ်ခြင်း](https://ooni.org/nettest/web-connectivity/) ကို အသုံးပြု၍ ဝဘ်ဆိုဒ်များအား ပိတ်ဆို့ထားခြင်းရှိ၊ မရှိ စစ်ဆေးပါ။\n\nစမ်းမည် ခလုတ်ကို နှိပ်တိုင်း Citizen Lab ၏ [ဂလိုဘယ်](https://github.com/citizenlab/test-lists/blob/master/lists/global.csv) နှင့် [country-specific](https://github.com/citizenlab/test-lists/tree/master/lists) စမ်းသပ်မှု စာရင်းများမှ ကွဲပြားသော ဝဘ်ဆိုဒ်များကို သင် စမ်းသပ်ခြင်း ဖြစ်သည်။\n\nသင်ရွေးချယ်သည့် ဆိုဒ်များကို စမ်းသပ်ရန်၊ ဝဘ်ဆိုဒ်များ ရွေးချယ်မည် ခလုတ်ကို နှိပ်ပါ သို့မဟုတ် ဤကတ်၏ ဆက်တင်များမှ တစ်ဆင့် ဆိုဒ်များ၏ အမျိုးအစားများကို ရွေးချယ်ပါ။\n\nဤစမ်းသပ်မှုသည် ဝဘ်ဆိုက်များကို DNS ကစားခြင်း၊ TCP/IP ပိတ်ဆို့ခြင်း သို့မဟုတ် ပွင့်လင်းမြင်သာသော HTTP ပရောက်စီ ဖြင့် ပိတ်ဆို့ခြင်း ရှိ၊ မရှိ တိုင်းတာ စစ်ဆေးသည်။\nသင့် ရလဒ်များအား [OONI Explorer](https://explorer.ooni.org/world/) နှင့် [OONI API](https://api.ooni.io/) တွင် ထုတ်ပြန်ပေးပါမည်။ + OONI ၏ [ဝဘ်ချိတ်ဆက်မှုစမ်းသပ်ခြင်း](https://ooni.org/nettest/web-connectivity/) ကို အသုံးပြု၍ ဝဘ်ဆိုဒ်များအား ပိတ်ဆို့ထားခြင်းရှိ၊ မရှိ စစ်ဆေးပါ။\n\nCitizen Lab ၏ [ဂလိုဘယ်](https://github.com/citizenlab/test-lists/blob/master/lists/global.csv) နှင့် [country-specific](https://github.com/citizenlab/test-lists/tree/master/lists) စမ်းသပ်မှု စာရင်းများ အပါအဝင် ဝဘ်ဆိုဒ်များကို စမ်းသပ်ရမည်။ \n\nဤစမ်းသပ်မှုသည် ဝဘ်ဆိုက်များကို DNS ဆော့ကစားခြင်း၊ TCP/IP ပိတ်ဆို့ခြင်း သို့မဟုတ် ပွင့်လင်းမြင်သာသော HTTP ပရောက်စီ ဖြင့် ပိတ်ဆို့ခြင်း ရှိ၊မရှိ တိုင်းတာသည်။\n\nသင့်ရလဒ်များအား [OONI Explorer](https://explorer.ooni.org/) နှင့် [OONI API](https://api.ooni.io/) တွင် ထုတ်ပြန်ပေးမည်။ + သင့် ကွန်ရက်အမြန်နှုန်းနှင့် စွမ်းဆောင်ရည်ကို စမ်းသပ်ပါ + [NDT] (https://ooni.org/nettest/ndt/) စမ်းသပ်မှုကို သုံး၍ သင်၏ကွန်ရက် အမြန်နှုန်းနှင့် စွမ်းဆောင်ရည်ကို တိုင်းတာပါ။\n\n[DASH] (https://ooni.org/nettest/dash/) စမ်းသပ်မှုကို သုံး၍ ဗီဒီယိုထုတ်လွှင့်ခြင်း စွမ်းဆောင်ရည်ကို တိုင်းတာပါ။\n\nဤစစ်ဆေးမှုများသည် သင့်ကွန်ရက် အမြန်နှုန်းပေါ်မူတည်၍ ဒေတာကို အသုံးပြုကြသည်။\n\nသင့် ရလဒ်များအား [OONI Explorer](https://explorer.ooni.org/world/) နှင့် [OONI API](https://api.ooni.io/) တွင် ထုတ်ဝေပါမည်။\n\nငြင်းဆိုချက် - ဤစစ်ဆေးမှုများသည် ပြင်ပ ဆာဗာများပေါ်တွင် အားကိုးပါသည်။ ထို့ကြောင့် သင်၏ IP လိပ်စာကို စုဆောင်းမည်မဟုတ်ကြောင်း ကျွန်ုပ်တို့ အာမ မခံနိုင်ပါ။ + ဤကတ်တွင် စမ်းသပ်မှုများကို လုပ်ဆောင်ခြင်းဖြင့် သင်သည် -\n\n\n- သင့် ကွန်ရက်၏ အမြန်နှုန်းနှင့် စွမ်းဆောင်ရည်ကို တိုင်းတာခြင်း ([NDT](https://ooni.org/nettest/ndt/) စမ်းသပ်မှု)\n- ဗီဒီယိုထုတ်လွှင့်ခြင်း စွမ်းဆောင်ရည်ကို တိုင်းတာခြင်း ([DASH](https://ooni.org/nettest/dash/) စမ်းသပ်မှု)\n\n- သင့်ကွန်ရက်တွင် [middlebox နည်းပညာများ](https://ooni.org/support/glossary/#middlebox) ရှိ၊ မရှိစစ်ဆေးပါ ([HTTP Invalid Request Line](https://ooni.org/nettest/http-invalid -request-line/) နှင့် [HTTP Header Field Manipulation](https://ooni.org/nettest/http-header-field-manipulation/) စမ်းသပ်မှုများ)\n\nဤစစ်ဆေးမှုများသည် သင့်ကွန်ရက် အမြန်နှုန်းပေါ်မူတည်၍ ဒေတာကို အသုံးပြုကြသည်။ \n\nသင်၏ စမ်းသပ်မှုရလဒ်များကို [OONI Explorer](https://explorer.ooni.org/) နှင့် [OONI API](https://api.ooni.io/) တွင် ထုတ်ပြန်မည်ဖြစ်ပါသည်။\n\n**ငြင်းဆိုချက် - ** အဆိုပါ [NDT](https://ooni.org/nettest/ndt/) နှင့် \n[DASH](https://ooni.org/nettest/dash/) စမ်းသပ်မှုများကို [Measurement Lab (M-Lab)](https://www.measurementlab.net/) \nမှ ပံ့ပိုးပေးသော ပြင်ပဆာဗာများက တဆင့် ဆောင်ရွက်ခြင်း ဖြစ်သည်။ ဤ စမ်းသပ်မှုများကို ဆောင်ရွက်လျှင် OONI Probe ဆက်တင်\nနှင့် မသက်ဆိုင်ဘဲ သင့် IP လိပ်စာအား M-Lab က ကောက်ယူကာ ထုတ်ပြန်မည်။ M-Lab ၏ [ကိုယ်ရေးလုံခြုံမှု ကြေညာချက်](https://www.measurementlab.net/privacy/) မှတဆင့် ၎င်း၏ ဒေတာစီမံခန့်ခွဲမှုအကြောင်းကို ပိုမိုသိရှိနိုင်သည်။ + သင့် ကွန်ရက်ရှိ middleboxes ကို ရှာဖွေရန် + အင်တာနက် ဝန်ဆောင်မှုပေးသူများသည် ကွန်ရက် ချိတ်ဆက်ခြင်းဆိုင်ရာ ရည်ရွယ်ချက်အမျိုးမျိုးအတွက် (caching ကဲ့သို့) အတွက် ကွန်ရက်သုံးပစ္စည်းများ (middleboxes) ကို မကြာခဏ အသုံးပြုကြသည်။ တခါတရံ ဤ middleboxes ကို အင်တာနက်ဆင်ဆာဖြတ်တောက်ခြင်းနှင့်/ သို့မဟုတ် စောင့်ကြည့်ခြင်းအား အကောင်အထည်ဖော်ရန် အသုံးပြုသည်။\n\nOONI ၏ [HTTP မဆီလျော်သော တောင်းဆိုမှုလိုင်း](https://ooni.org/nettest/http-invalid-request-line/) နှင့် [HTTP Header Field Manipulation](https://ooni.org/nettest/http-header-field-manipulation/) စမ်းသပ်မှုများကို သုံး၍ သင့်ကွန်ရက်အတွင်းရှိ middleboxes ကို ရှာနိုင်သည်။ \n\nသင့်ရလဒ်များအား [OONI Explorer](https://explorer.ooni.org/world/) နှင့် [OONI API](https://api.ooni.io/) တွင် ထုတ်ပြန်ပေးပါမည်။ + လက်ငင်း စာတိုပေးပို့ခြင်း အက်ပ်များ ပိတ်ဆို့ခြင်းအား စမ်းသပ်မည် + [WhatsApp](https://ooni.org/nettest/whatsapp/)၊ [Facebook Messenger](https://ooni.org/nettest/facebook-messenger/)၊ [Telegram](https://ooni.org/nettest/telegram/) နှင့် [Signal](https://ooni.org/nettest/signal) တို့ကို ပိတ်ဆို့ထားခြင်း ရှိ/မရှိ စစ်ဆေးမည်။\n\nသင့် ရလဒ်များအား [OONI Explorer](https://explorer.ooni.org/world/) နှင့် [OONI API](https://api.ooni.io/) တွင် ထုတ်ဝေပါမည်။ + ဆင်ဆာရှောင်တိမ်း ကိရိယာများကို ပိတ်ဆို့ စမ်းသပ်မည် + [Psiphon](https://ooni.org/nettest/psiphon/)၊ [Tor](https://ooni.org/nettest/tor/) သို့မဟုတ် [RiseupVPN](https://ooni.org/nettest/riseupvpn/) တို့ကို ပိတ်ဆို့ထားခြင်း ရှိ/မရှိ စစ်ဆေးရန်\nသင့် ရလဒ်များအား [OONI Explorer](https://explorer.ooni.org/) နှင့် [OONI API](https://api.ooni.io/) တွင် ထုတ်ပြန်ပေးပါမည်။ + စမ်းသပ်မှုအသစ်များကို လုပ်ဆောင်ရန် + OONI အဖွဲ့မှ ဖန်တီးထားသော အောက်ပါ စမ်းသပ်မှုအသစ်များကို လုပ်ဆောင်မည်\n%1$s\n\nသင့်ရလဒ်များကို [OONI Explorer](https://explorer.ooni.org/) နှင့် [OONI API](https://api.ooni.io/) တွင် ထုတ်ဝေပါမည်။ + အောက်ပါ စမ်းသပ်မှုများကို အလိုအ‌လျောက် စမ်းသပ်မှု၏ အစိတ်အပိုင်း အဖြစ်သာ ဆောင်ရွက်မည် - + ပယ်ဖျက်ထားသော စမ်းသပ်မှုများ\n + ဂစ်ဂါဘစ်/စက္ကန့် + မဂ္ဂါဘစ်/စက္ကန့် + ကီလိုဘစ်/စက္ကန့် + မီလီစက္ကန့် + မရရှိနိုင်ပါ + မသိ + စမ်းသပ်မှု ရလဒ်များ + စမ်းသပ်မှု ရလဒ်များ + စမ်းသပ်မှုများ + ကွန်ရက်များ + ဒေတာအသုံးပြုမှု + စမ်းသပ်မှုများ + စမ်းသပ်မှုအားလုံး + ဝဘ်ဆိုဒ်များ + Middleboxes + စွမ်းဆောင်ရည် + ချက်ခြင်း စာတိုပေးပို့ခြင်း\n + ရှောင်တိမ်းခြင်း + စမ်းသပ် လေ့လာနေဆဲ + စမ်းသပ်မှု မရှိသေးပါ။ စမ်းသပ်မှုတစ်ခုကို လုပ်ဆောင် ကြည့်လိုက်ပါ။ + %1$s ခု ပိတ်ဆို့ထားသည် + %1$s ခု ပိတ်ဆို့ထားသည် + %1$s ကြိမ် စမ်းသပ်ခဲ့သည် + %1$s ကြိမ် စမ်းသပ်ခဲ့သည် + စစ်ဆေး တွေ့ရှိခဲ့သည် + ရှာမတွေ့ပါ + မအောင်မြင်ပါ + %1$s ခု ပိတ်ဆို့ထားသည် + %1$s ခု ပိတ်ဆို့ထားသည် + %1$s အသုံးပြုနိုင်သည် + %1$s အသုံးပြုနိုင်သည် + %1$s ခု ပိတ်ဆို့ထားသည် + %1$s ခု ပိတ်ဆို့ထားသည် + %1$s ရရှိနိုင်သည် + %1$s ရရှိနိုင်သည် + မပြည့်စုံသော ရလဒ် + ပြဿနာ + တိုင်းတာမှုတွင် ပြဿနာ ရှိသည် + ရလဒ်များကို တင်ထားခြင်း မရှိပါ + နေ့စွဲ နှင့် အချိန် + ကွန်ရက် + နိုင်ငံ + ဒေတာအသုံးပြုမှု + စုစုပေါင်း လုပ်ဆောင်ချိန် + ဝိုင်ဖိုင် + မိုဘိုင်းဒေတာ + အင်တာနက်မရှိပါ + မအောင်မြင်ပါ + စမ်းသပ်ခဲ့သည် + စမ်းသပ်ခဲ့သည် + ပိတ်ဆို့ထားသည် + ပိတ်ဆို့ထားသည် + ဝဘ်ဆိုဒ် + ဝဘ်ဆိုဒ်များ + အသုံးပြုနိုင်သည် + အသုံးပြုနိုင်သည် + ဗီဒီယို + အရည်အသွေး + အပ်လုဒ် + ဒေါင်းလုဒ်လုပ်မည်\n + ပို့ပါ + ရှာတွေ့သည် + ရှာမတွေ့ပါ + မအောင်မြင်ပါ + စမ်းသပ်ခဲ့သည် + စမ်းသပ်ခဲ့သည် + ပိတ်ဆို့ထားသည် + ပိတ်ဆို့ထားသည် + အသုံးပြုနိုင်သည် + အသုံးပြုနိုင်သည် + အက်ပ် + အက်ပ်များ + စမ်းသပ်ခဲ့သည် + စမ်းသပ်ခဲ့သည် + ပိတ်ဆို့ထားသည် + ပိတ်ဆို့ထားသည် + အလုပ်လုပ်နေသည် + အလုပ်လုပ်နေသည် + ကိရိယာ + ကိရိယာများ + လုပ်ဆောင်ချိန် + နည်းစနစ် + ကြည့်ရှုမှု မှတ်တမ်း + ဒေတာ + Explorer URL ကို ကူးမည် + Explorer URL ကို မျှဝေမည်\n + Clipboard ထဲသို့ ကူးမည် + OONI Explorer တွင် ဖော်ပြရန် + မအောင်မြင်ပါ + သင်သည် ဤစမ်းသပ်မှုကို ထပ်မံ လုပ်ဆောင်နိုင်သည် + ထပ်ကြိုးစားပါ + ဤစမ်းသပ်မှု မည်သို့ အလုပ်လုပ်ပုံကို [ဤနေရာ](%1$s) တွင် လေ့လာပါ။ + အသုံးပြုနိုင်သည် + %1$s ကို အသုံးပြုနိုင်သည်။ + ပိတ်ဆို့ခံရဖွယ် ရှိသည် + %1$s%2$s ဖြင့် ပိတ်ဆို့ထားဖွယ်ရှိသည်။\n\nမှတ်ချက် - မှားယွင်းသော အကောင်းမြင်မှုများ ဖြစ်နိုင်သည်။ [ဤနေရာတွင်](https://ooni.org/support/faq/#what-are-false-positives) ပိုမိုလေ့လာပါ။\n + ဆင်ဆာဖြတ်တောက်မှုအား ရှောင်တိမ်းခြင်း + **DNS ကစားခြင်း** + ** TCP/IP အခြေခံ ပိတ်ဆို့ခြင်း** + **HTTP ပိတ်ဆို့ခြင်း (ပိတ်ဆို့ထားသည့် စာမျက်နှာတစ်ခုကို မြင်ရနိုင်သည်)** + **HTTP ပိတ်ဆို့ခြင်း (HTTP တောင်းဆိုမှုများ မအောင်မြင်ပါ)** + မိုဘိုင်းအက်ပ် + အိုကေ + မအောင်မြင်ပါ + WhatsApp ဝဘ် + အိုကေ + မအောင်မြင်ပါ + မှတ်ပုံတင်မည်\n + အိုကေ + မအောင်မြင်ပါ + အလုပ်လုပ်နေသည် + ဤစမ်းသပ်မှုသည် WhatsApp ၏ အဆုံးမှတ်များ၊ မှတ်ပုံတင်ခြင်း ဝန်ဆောင်မှုနှင့် ဝဘ်အင်တာဖေ့စ် (web.whatsapp.com) တို့နှင့် အောင်မြင်စွာ ချိတ်ဆက် ထားသည်။ + ပိတ်ဆို့ခံရဖွယ် ရှိသည် + WhatsApp ကို ပိတ်ဆို့ထားပုံ ရသည်။ + မိုဘိုင်းဖုန်း အက်ပ်\n + အိုကေ + မအောင်မြင်ပါ + တယ်လီဂရမ် ဝဘ် + အိုကေ + မအောင်မြင်ပါ + အလုပ်လုပ်နေသည် + ဤစမ်းသပ်မှုသည် Telegram ၏ အဆုံးမှတ်များနှင့် ဝဘ်အင်တာဖေ့စ် (web.telegram.org) နှင့် အောင်မြင်စွာ ချိတ်ဆက်ထားသည်။ + ပိတ်ဆို့ခံရဖွယ် ရှိသည် + Telegram ကို ပိတ်ဆို့ထားပုံ ရသည်။ + TCP ချိတ်ဆက်မှုများ + အိုကေ + မအောင်မြင်ပါ + DNS ရှာဖွေမှုများ + အိုကေ + မအောင်မြင်ပါ + အလုပ်လုပ်နေသည် + ဤစမ်းသပ်မှုသည် Facebook ၏ endpoint များနှင့် အောင်မြင်စွာ ချိတ်ဆက်ပြီး Facebook IP လိပ်စာများအား ဖြေရှင်းခဲ့သည်။ + ပိတ်ဆို့ခံရဖွယ် ရှိသည် + Facebook Messenger ကို ပိတ်ဆို့ထားပုံ ရသည်။ + ပိတ်ဆို့ခံရဖွယ် ရှိသည် + Signal ကို ပိတ်ဆို့ထားပုံ ရသည်။ + အလုပ်လုပ်နေသည် + ဤစမ်းသပ်မှုသည် Signal ၏ endpoints နှင့် အောင်မြင်စွာ ချိတ်ဆက်ထားသည်။ + middleboxes ကို ရှာမတွေ့ပါ + ကျွန်ုပ်တို့၏ ဆာဗာများနှင့် ဆက်သွယ်သောအခါတွင် ကွန်ရက် ကွဲလွဲမှုကို မတွေ့ရှိရပါ။ + ကွန်ရက် ကစားခြင်း + ကျွန်ုပ်တို့၏ ထိန်းချုပ်မှုဆာဗာများကို ဆက်သွယ်သည့်အခါ ကွန်ရက် အသွားအလာကို ခြယ်လှယ်ထားသည်။\n\nဆိုလိုသည်မှာ ဆင်ဆာနှင့်/သို့မဟုတ် စောင့်ကြည့်ခြင်းအတွက် တာဝန်ရှိနိုင်သည့် middlebox တစ်ခု သင့်ကွန်ရက်တွင် ရှိနေနိုင်သည်ဟု ဆိုလိုသည်။ + middleboxes အား မတွေ့ရှိပါ + ကျွန်ုပ်တို့၏ ဆာဗာများနှင့် ဆက်သွယ်သောအခါတွင် ကွန်ရက် ကွဲလွဲမှုကို မတွေ့ရှိရပါ။ + ကွန်ရက်ကို လက်ဆော့ခြင်း + ကျွန်ုပ်တို့၏ ထိန်းချုပ်မှုဆာဗာများကို ဆက်သွယ်သည့်အခါ ကွန်ရက် အသွားအလာကို ခြယ်လှယ်ထားသည်။\n\nဆိုလိုသည်မှာ ဆင်ဆာနှင့်/သို့မဟုတ် စောင့်ကြည့်ခြင်းအတွက် တာဝန်ရှိနိုင်သည့် သင့် ကွန်ရက်တွင် middlebox တစ်ခု ရှိနေနိုင်သည်ဟု ဆိုလိုသည်။ + သင်ပို့ခဲ့သည် + သင် လက်ခံရရှိခဲ့သည် + အပ်လုဒ် + ဒေါင်းလုဒ်လုပ်မည် + Ping + ဆာဗာ + ပြန်ပို့မှုနှုန်း + ကောင်းစွာ အလုပ်မလုပ်ပါ + ပျမ်းမျှ Ping + Max Ping ခန့်မှန်းမှု + MSS + အချိန်ကုန်သွားသည် + ယာယီ သိမ်းစရာ မလိုဘဲ %1$s အထိ ထုတ်လွှင့် နိုင်ပါသည်။ + အလယ်အလတ် ဘစ်နှုန်း + Playout ကြန့်ကြာခြင်း + ပိတ်ဆို့ခံရဖွယ် ရှိသည် + အလုပ်လုပ်နေသည် + [Psiphon](https://psiphon.ca/) ကို ပိတ်ဆို့ ထားပုံ ရသည်။\n + ကျွန်ုပ်တို့သည် Psiphon ချိတ်ဆက်မှု တစ်ခုကို အောင်မြင်စွာ စတင်နိုင်ခဲ့သည်။ ဆိုလိုသည်မှာ [Psiphon](https://psiphon.ca/) သည် အလုပ် လုပ်သင့်သည်။ + စတင်အလုပ်လုပ်သည့် အချိန် + %1$s စက္ကန့် + ပိတ်ဆို့ခံရဖွယ် ရှိသည် + အလုပ်လုပ်နေသည် + [Tor](https://www.torproject.org/) ကို ပိတ်ဆို့ထားပုံ ရသည်။ + ကျွန်ုပ်တို့သည် နဂိုမူလ Tor bridges နှင့်/ သို့မဟုတ် Tor directory authorities ကို အောင်မြင်စွာ ချိတ်ဆက်နိုင်ခဲ့သည်။ ဆိုလိုသည်မှာ [Tor](https://www.torproject.org/) သည် သုံး၍ ရသင့်သည်။ + နဂိုမူလ Bridge များ + %1$s/%2$s အိုကေ + လုပ်ပိုင်ခွင့်ရှိသူများ လမ်းညွှန် + %1$s/%2$s အိုကေ + အမည် + လိပ်စာ + အမျိုးအစား + ချိတ်ဆက်မည် + ဒေတာဖလှယ်ခြင်း\n + ပိတ်ဆို့ခံရဖွယ် ရှိသည် + အလုပ်လုပ်နေသည် + [RiseupVPN](https://riseup.net/vpn) ကို ပိတ်ဆို့ထားပုံ ရသည်။ + ကျွန်ုပ်တို့အနေဖြင့် RiseupVPN\ ၏ bootstrap ဆာဗာနှင့် VPN gateways များကို အောင်မြင်စွာ ချိတ်ဆက်နိုင်ခဲ့သည်။ ဆိုလိုသည်မှာ [RiseupVPN](https://riseup.net/vpn) ကို သုံး၍ ရသင့်ပါသည်။ + Bootstrap ဆာဗာ + OpenVPN ချိတ်ဆက်မှုများ + ပေါင်းစပ် ချိတ်ဆက်မှုများ + ပိတ်ဆို့ထားသည် + %1$s ခု ပိတ်ဆို့ထားသည် + %1$s ခု ပိတ်ဆို့ထားသည် + အိုကေ + ဤစမ်းသပ်မှုသည် အစမ်းသဘော ဖြစ်သည်။ + သတင်း + သတင်း + အိုကေ + ဖျက်သိမ်းရန်​ + ထပ်မမေးပါနှင့် + ဖျက်မည် + ပြဿနာ + ထပ်ကြိုးစားပါ + ကောင်းပါပြီ + ရပါတယ်၊ ကျေးဇူးပါ + ယခု မဟုတ်ပါ + ဘာဖြစ်ဖြစ် စမ်းမည် + VPN ကို ပိတ်မည် + အမြဲတမ်း စမ်းမည် + စမ်းသပ်မှုကို မလုပ်ဆောင်နိုင်ပါ။ သင့် အင်တာနက် ချိတ်ဆက်မှုကို စစ်ဆေးပါ။\n + URL စာရင်းကို ဒေါင်းလုဒ်လုပ်၍ မရပါ။ ထပ်စမ်းကြည့်ပါ။ + ကျေးဇူးပြု၍ စမ်းသပ်မှုအသစ် မစတင်မီ လက်ရှိလုပ်ဆောင်နေသော စမ်းသပ်မှုများ ပြီးဆုံးရန် စောင့်ပါ။ + အသိပေးချက်ဆိုင်ရာ ခွင့်ပြုချက်များ လိုအပ်သည်။ ကျေးဇူးပြု၍ သင့်ဖုန်း၏ ဆက်တင်များထဲတွင် ၎င်းတို့ကိုဖွင့်ပြီး သင်၏ OONI Probe အက်ပ်တွင် ၎င်းတို့ကို ဖွင့်ပါ။ + ဆက်တင်များသို့ သွားမည် + စမ်းသပ်မှုတစ်ခု လုပ်ဆောင်နေချိန်တွင် ဤမျက်နှာပြင်ကို ပိတ်ထားသည်။ + တိုင်းတာမှု ဒေတာအကြမ်းကို ဒေါင်းလုဒ်လုပ်ရန် သင်သည် အင်တာနက်နှင့် ချိတ်ဆက်ထားရန် လိုအပ်သည်။ + ရလဒ်များကို အပ်လုဒ်လုပ်ခြင်း မရှိပါ + သင့်စမ်းသပ်မှု ရလဒ်အချို့ကို OONI ဆာဗာများသို့ အပ်လုဒ် လုပ်ခြင်း မရှိသေးပါ။ OONI ၏ ဒေတာအစုသို့ ထည့်ပေးလိုပါက ၎င်းတို့ကို အပ်လုဒ် လုပ်ပါ။ + အပ်လုဒ် + %1$s ကို အပ်လုဒ်လုပ်နေသည်... + ဘက်ထရီ ပိုမိုကောင်းမွန်အောင် လုပ်ဆောင်ခြင်းမရှိဘဲ OONI Probe သည် စမ်းသပ်မှုအား အလိုအလျောက် မလည်ပတ်နိုင်ပါ။ ထပ်မံ ကြိုးစားလိုပါသလား။ + သင်၏ VPN ချိတ်ဆက်မှုကို ပိတ်ပါ။ + အကယ်၍ သင်သည် VPN ကိုဖွင့်ထားလျက် OONI Probeကို စမ်းသပ်လုပ်ဆောင်ပါက၊ စမ်းသပ်မှုရလဒ်များသည် နိုင်ငံမှားယွင်းကာ ထွက်ပေါ်လာနိုင်သည်။ သင်၏ VPN ချိတ်ဆက်မှုကို ပိတ်ပါ။ + အချို့သော တိုင်းတာမှုများသည် VPN ကို ကျော်၍ လုပ်ဆောင်ခဲ့သည်။ + VPN ကို ဖွင့်ထားလျက် တိုင်းတာမှုများအား အပ်လုဒ်လုပ်ပါက၊ စမ်းသပ်မှု ရလဒ်များသည် နိုင်ငံ မှားယွင်းကာ ထွက်ပေါ်လာနိုင်သည်။ + အပ်လုဒ်လုပ်ခြင်း အောင်မြင်သည် + ပျက်ကွက်မှု မှတ်တမ်းကို ပြသရန်\n + အင်တာနက် ဆင်ဆာဖြတ်ခြင်းဆိုင်ရာ အပ်ဒိတ်များကို ရယူပါ။ + အရေးပေါ် ဆင်ဆာဖြတ်တောက်မှု ဖြစ်ရပ်များအတွင်း OONI Probe စမ်းသပ်မှုများကို လုပ်ဆောင်ရန် စိတ်ဝင်စားပါသလား။ သင့် အနီးရှိ အင်တာနက်ဆင်ဆာ ဖြတ်တောက်မှုကို ကျွန်ုပ်တို့ ကြားရသည့်အခါ အချက်အလက် သတင်း လက်ခံရရှိရန် အသိပေးချက်များကို ဖွင့်ပါ။ + စစ်ဆေးမှုများ၏ တိကျမှုကို မြှင့်တင်ရန်၊ ကျွန်ုပ်တို့သည် GPS ခွင့်ပြုချက်များ လိုအပ်ပါသည်။ OONI သည် သင့် GPS အနေအထား၏ ခန့်မှန်းခြေကိုသာ စုဆောင်း ပါမည်။ + စမ်းသပ်မှုရလဒ်အားလုံးကို ဖျက်လိုပါသလား။ + ဤစမ်းသပ်မှုကို ဖျက်လိုပါသလား။ + ကျေးဇူးပြု၍ အနည်းဆုံး စမ်းသပ်မှုတစ်ခု ဖွင့်ပါ + ဤအကွက်တွင် ကိန်းဂဏန်းများကိုသာ ထည့်သွင်းပါ။ + စမ်းသပ်မှု ပြန်လုပ်ပါ + ဤစမ်းသပ်မှု မအောင်မြင်ပါ။ စမ်းသပ်မှုကို ပြန်လည် လုပ်ဆောင်လိုပါသလား။ + သင်သည် ဝဘ်ဆိုဒ် %1$s ကို ပြန်လည် စမ်းသပ် တော့မည်။ + စမ်းမည် + ဤဝဘ်စာမျက်နှာမှ ထွက်သည့်အခါ သင့် URL များကို သိမ်းဆည်းမည် မဟုတ်ပါ။ ဤစာမျက်နှာမှ ထွက်လိုသည်မှာ သေချာပါသလား။ + Manual Upload ကို ဖွင့်မလား။ + ဤဆက်တင်သည် သင့်အား မထုတ်ဝေရသေးသော တိုင်းတာမှုများကို ကိုယ်တိုင် ပြန်တင်ခွင့် ပြုသည်။ + ဖွင့်မည် + ရပါတယ်၊ ကျေးဇူးပါ + အပ်လုဒ်လုပ်ခြင်း မအောင်မြင်ပါ + ကျွန်ုပ်တို့သည် အတိုင်းအတာများကို %1$s/%2$s အပ်လုဒ်လုပ်၍ မရပါ။ မအောင်မြင်ခြင်း မှတ်တမ်းကို OONI developer များနှင့် မျှဝေထားပါသည်။ + မှတ်တမ်းဖိုင်ကို ရှာမတွေ့ပါ + ဆီလျော်သော URL များ မတွေ့ပါ + JSON မရှိပါ + ဤစမ်းသပ်မှုကို ရပ်လိုပါသလား။ + ဤအချိန်မှစ၍ လက်ရှိစမ်းသပ်မှုကို ရပ်မည် ဖြစ်သည်။ + စမ်းသပ်မှုများကို အလိုအလျောက် လုပ်ဆောင်လိုပါသလား။ + အလိုအလျောက် စမ်းသပ်ခြင်းကို ဖွင့်ခြင်းဖြင့် သင်သည် OONI တိုင်းတာမှုများကို ပုံမှန် ပံ့ပိုးပေးမည် ဖြစ်သည်။ + ကျေးဇူးပြု၍ အက်ပ်အား နောက်တွင် ဖွင့်ခွင့် ပြုပါ။ + ငါ့ကို နောက်မှ သတိပေးပါ + Clipboard ထဲသို့ ကူးမည် + အပ်လုဒ် မလုပ်ပါ + အပ်လုဒ် + အချို့ကို အပ်လုဒ်မလုပ်ခဲ့ပါ + အားလုံးကို အပ်လုဒ်လုပ်ပါ + ဝဘ်ဆိုဒ်များ + ချက်ခြင်း မက်ဆေ့ချ် + Middleboxes + စွမ်းဆောင်ရည် + ဆင်ဆာရှောင်တိမ်းခြင်း + စမ်းသပ် လေ့လာနေဆဲ + HTTP မဆီလျော်သော တောင်းဆိုမှုလိုင်း စမ်းသပ်မှု + HTTP Header Field Manipulation စမ်းသပ်မှု + ချိတ်ဆက်နိုင်စွမ်း စမ်းသပ်မှု + NDT မြန်နှုန်းစမ်းသပ်မှု + DASH ထုတ်လွှင့် စမ်းသပ်မှု + WhatsApp စမ်းသပ်မှု + Telegram စမ်းသပ်မှု + Facebook Messenger စမ်းသပ်မှု + Psiphon စမ်းသပ်မှု + Tor စမ်းသပ်မှု + RiseupVPN စမ်းသပ်မှု + Signal စမ်းသပ်မှု + ဆက်တင်များ + စမ်းသပ်မှု ကာလအတွက် သင် သတ်မှတ်ထားသော အချိန်ပမာဏသည် အလွန် နည်းသည်။ + OONI အကြောင်း + Open Observatory of Network Interference (OONI) သည် Tor ပရောဂျက် အောက်ရှိ လွတ်လပ်ဆော့ဖ်ဝဲလ် ပရောဂျက်တစ်ခု ဖြစ်ပြီး ကမ္ဘာတစ်ဝှမ်းရှိ အင်တာနက် ဆင်ဆာဖြတ်တောက်မှုများကို ပွင့်လင်းမြင်သာမှု ရှိစေရန် ရည်ရွယ်ပါသည်။ \n\n၂၀၁၂ ခုနှစ်မှ စတင်၍ OONI ၏ ကမ္ဘာ့ အသိုင်းအဝိုင်းသည် နိုင်ငံပေါင်း ၂၀၀ ကျော်တွင် ကွန်ရက်များကို တိုင်းတာနေပါသည်။ ဤတိုင်းတာမှုအချို့သည် အင်တာနက် ဆင်ဆာ ဖြတ်တောက်ခြင်း၏ သက်သေအဖြစ် တည်ရှိနေသည်။ + ပိုမိုလေ့လာရန် + ဘလော့ဂ် + အစီရင်ခံစာများ + OONI ဒေတာမူဝါဒ + အသိပေးချက်များ + ဖွင့်ထားသည် + စမ်းသပ်မှု ပြီးပါက အသိပေးမည် + သတင်းအသစ် ရယူရန် + အလိုအလျောက် စမ်းသပ်ခြင်း + စမ်းသပ်မှုများကို အလိုအလျောက် လုပ်ဆောင်ပါ + အလိုအလျောက်စမ်းသပ်မှု အရေအတွက် - %1$s။ + နောက်ဆုံး လုပ်ဆောင်ခဲ့သည့် အလိုအလျောက် စမ်းသပ်မှု - %1$s။ + WiFi နှင့် သာ + အားသွင်းနေစဉ်သာ + အလိုအလျောက် စမ်းသပ်ခြင်းကို ဖွင့်ထားခြင်းဖြင့် OONI Probe စမ်းသပ်မှုများသည် တစ်နေ့လျှင် အကြိမ်များစွာ အလိုအလျောက် လုပ်ဆောင်မည် ဖြစ်သည်။ သင်၏ စမ်းသပ်မှုရလဒ်များကို OONI Explorer တွင် အလိုအလျောက် ထုတ်ပြန်လိမ့်မည် - https://explorer.ooni.org/ \n\nအရေးကြီးသည် - သင့်စက်တွင် VPN ဖွင့်ထားပါက OONI Probe သည် စမ်းသပ်မှုများကို အလိုအလျောက် လုပ်ဆောင်မည် မဟုတ်ပါ။ အလိုအလျောက် OONI Probe စမ်းသပ်မှုအတွက် သင့်စက် VPN ကို ပိတ်ပါ။ ပိုမိုလေ့လာရန် - https://ooni.org/support/faq/#can-i-run-ooni-probe-over-a-vpn သို့ သွားပါ + မျှဝေခြင်း + အလိုအလျောက် ထုတ်ပြန်သော ရလဒ်များ + လူကိုယ်တိုင် ရလဒ် အပ်လုဒ်လုပ်ရန် + ကွန်ရက် အချက်အလက် ထည့်သွင်းရန် + ခန့်မှန်း geo-location ထည့်သွင်းရန် + ကျွန်ုပ်၏ IP လိပ်စာ ထည့်သွင်းရန် + နိုင်ငံကုဒ် ထည့်သွင်းရန် + တိုင်းတာမှုများကို မည်သည့်နိုင်ငံမှ ကောက်ယူနေကြောင်း သိရှိရန် ဤ သတင်းအချက်အလက် (ဥပမာ - အီတလီနိုင်ငံ အတွက် အတိုက်ကောက်စာလုံး IT) လိုအပ်သည်။ ဤရွေးချယ်မှုကို ပိတ်လိုသည်မှာ သေချာသလား။ + ရလဒ်များကို ထုတ်ပြန်ခြင်းဖြင့် သင်သည် ကွန်ရက်အား နှောင့်ယှက်မှုဆိုင်ရာ ပွင့်လင်းမြင်သာမှုကို တိုးမြင့်စေလျက် OONI အသိုင်းအဝိုင်းအား ကူညီပေးသည်။\n\nအင်တာနက်ဝန်ဆောင်မှုပေးသူများအား ခွဲခြားသတ်မှတ်ရန်အတွက် ကွန်ရက်သတင်းအချက်အလက်များ (ဥပမာ - အလိုအလျောက်စနစ် နံပါတ်) လိုအပ်ပါသည်။ + စမ်းသပ်ရွေးချယ်မှုများ + အထက်ဖော်ပြပါ စမ်းသပ်မှု ဆက်တင်များမှတစ်ဆင့် သင် ပြင်ဆင်သတ်မှတ်ထားသည့် အရာ (ဥပမာ - WhatsApp စစ်ဆေးမှုအား ပိတ်ခြင်း) သည် စမ်းသပ်မှုများကို ကိုယ်တိုင် လုပ်ဆောင်သည့် အပြင် စမ်းသပ်မှုများကို အလိုအလျောက် လည်ပတ်စေသည် (အလိုအလျောက် စမ်းသပ်မှုကို ဖွင့်ထားသောအခါ) တွင် သက်ရောက်မည် ဖြစ်သည်။ + တာရှည်စမ်းသပ်မှု + ရှေ့ဘက်တွင် အချိန်ကြာမြင့်သော စမ်းသပ်မှုများကို လုပ်ဆောင်မလား။ + ကိုယ်ရေးလုံခြုံမှု + ပျက်စီးမှု အစီရင်ခံစာများ ပေးပို့မည် + အဆင့်မြင့် + အမှောင်မုဒ် + အမှားပြင်ဆင်မှု မှတ်တမ်းများ\n + လတ်တလော မှတ်တမ်းများကို ကြည့်ပါ + ဘာသာစကား ဆက်တင် + ဘာသာစကား ရွေးမည်\n + domain fronting နည်းကို အမြဲသုံးရန်\n + OONI နောက်ခံ ပရောက်စီ + ကြားခံ အင်တာနက်စနစ် + မရှိ + Psiphon + အထူး ပရောက်စီ\n + အထူး ပရောက်စီ URL + အထူး ပရောက်စီ ပရိုတိုကော\n + ချိတ်ဆက်မှု + အထိုင်နေရာ အမည် + ပေါ့တ် + အထောက်အထားများ (မထည့်လည်းရသည်) + အသုံးပြုသူအမည် + စကားဝှက် + ဖန်တီးပြုပြင်သော ပရောက်စီပေါ်တွင် Psiphon ကိုသုံးပါ + OONI Probe ကို အသုံးပြု၍ မရနိုင်လျှင် ဖြစ်နိုင်ချေရှိသော ပိတ်ဆို့မှုကို ကျော်ဖြတ်နိုင်ရန် [Psiphon](https://psiphon.ca/) ကို ဖွင့်ပါ။ တနည်းအားဖြင့် သင်သည် စိတ်ကြိုက် ပရောက်စီကို သုံးနိုင်သည်။ + စမ်းသပ်ကာလကို ကန့်သတ်မည် + စမ်းသပ်မှုကာလ + စမ်းသပ်ရန် ဝဘ်ဆိုဒ် အမျိုးအစားများ + အမျိုးအစား %1$s ခု ကို ဖွင့်ထားသည် + တည်းဖြတ်မည် + အားလုံးကို မရွေးချယ်ရန်\n + အားလုံးကို ရွေးချယ်မည်\n + သိမ်းမည်\n + မသိမ်းဆည်းရသေးသော အပြောင်းအလဲများ + သင် ဖွင့်ထားသော အမျိုးအစားများအတွက် အပြောင်းအလဲအချို့ ပြုလုပ်ထားသည်။ ၎င်းတို့ကို သင် သိမ်းဆည်းလိုသလား။ + သိမ်းမည်\n + ဖယ်ရှားမည်\n + စမ်းသပ်မည့် ဝဘ်ဆိုဒ်များ ရွေးချယ်ရန် + URL + URL များ ထည့်ထားခြင်း မရှိပါ + စမ်းမည်\n + ဝဘ်ဆိုဒ်ထည့်ရန် + တန်းပလိတ်မှ တင်ရန် + စမ်းသပ်ထားသော ဝဘ်ဆိုဒ်အရေအတွက် (0 သည် ၀က်ဘ်ဆိုဒ်အားလုံးကို ဆိုလိုသည်) + WhatsApp ကို စမ်းသပ်မည်\n + Telegram ကို စမ်းသပ်မည်\n + Facebook Messenger ကို စမ်းသပ်မည်\n + Signal ကို စမ်းသပ်မည်\n + HTTP မဆီလျော်သော တောင်းဆိုမှု လိုင်းစမ်းသပ်မှုကို လုပ်ဆောင်ရန် + HTTP Header Field Manipulation စမ်းသပ်မှုကို လုပ်ဆောင်မည် + NDT မြန်နှုန်း စမ်းသပ်မှုကို လုပ်ဆောင်မည် + အလိုအလျောက် NDT ဆာဗာ ရွေးချယ်မှု + NDT ဆာဗာ လိပ်စာ + NDT ဆာဗာ ပေါ့တ် + DASH Streaming စမ်းသပ်မှုကို လုပ်ဆောင်ရန် + အလိုအလျောက် DASH ဆာဗာ ရွေးချယ်မှု + DASH ဆာဗာ + DASH ဆာဗာပေါ့တ် + Psiphon ကို စမ်းသပ်မည်\n + Tor ကို စမ်းသပ်မည်\n + RiseupVPN ကို စမ်းသပ်မည်\n + VPN အသုံးပြုနေချိန်တွင် သတိပေးပါ + ပံ့ပိုးကူညီရန် အီးမေးလ် ပို့ပါ + သင်ကြုံတွေ့နေရသော ပြဿနာကို ကျေးဇူးပြု၍ ဖော်ပြပါ - + အက်ပ်နှင့် iOS ဗားရှင်း အချက်အလက်များဖြင့် bugs@openobservatory.org သို့ အီးမေးလ် ပို့ပါ။ အောက်တွင်ပါရှိသော \"Clipboard ထဲသို့ ကူးမည်\" ကို နှိပ်၍ ကျွန်ုပ်တို့ အီးမေးလ်လိပ်စာကို ကူးပါ။ + လက်ရှိ အက်ပ် ဘာသာစကားသည် %1$s ဖြစ်သည် + ဘာသာစကား + သိုလှောင်မှုအား အသုံးပြုမှု + အသုံးပြုထားသော သိုလှောင်မှု + ဖျက်မည် + ရှင်းမည် + သင့်စက်မှ OONI တိုင်းတာမှုအားလုံးကို သင် ဖျက်တော့မည်။ အချက်အလက်များကို အပ်လုဒ် လုပ်ထားလျှင် ၎င်းတို့ကို [OONI Explorer](https://explorer.ooni.org) တွင် ဆက်လက် ရရှိနိုင်သည်။ + ပြီးသွားပါပြီ + စမ်းသပ်မှုကို ရပ်ပါ + mirror လုပ်ကြည့်ရန် + တင်နေပါသည်... + မမျှော်လင့်ထားသော ပြဿနာ ဖြစ်ပေါ်ခဲ့သည်။ ဤစာမျက်နှာကို ပြန်လည်စတင်ပါ။ + သင်သည် OONI Probe စမ်းသပ်မှု တစ်ခုကို လုပ်ဆောင်ပါတော့မည်။ + URL %1$s ခု + စမ်းသပ်မှု အမည် + စမ်းသပ်မှု အသေးစိတ် + စမ်းမည် + နောက်ဆုံးပေါ် မဟုတ် + ဤစမ်းသပ်မှု လုပ်ဆောင်ရန် OONI Probe ဗားရှင်းအသစ် လိုသည်။ + အပ်ဒိတ်လုပ်မည် + ပိတ်မည် + မဆီလျော်သော ကန့်သတ်ချက်ဘောင် + OONI Run လင့်ခ်သည် ပုံပျက်နေသည် သို့မဟုတ် သင့်အက်ပ်သည် ခေတ်မမီတော့ပါ။ + သင်သည် ဝဘ်ဆိုဒ်များထဲမှ ကျပန်းနမူနာဝဘ်ဆိုဒ်တခုကို စမ်းသပ်မည်။ + OONI Run လင့်ခ်ကို မနှိပ်မီ စမ်းသပ်မှု အပြီးသတ်ရန် စောင့်ပါ။ + မူးယစ်ဆေးဝါးနှင့် အရက် + ဘာသာတရား + ညစ်ညမ်းရုပ်ပုံ + ဆွဲဆောင်မှုရှိသော ဝတ်စားဆင်ယင်မှု + နိုင်ငံရေး ဝေဖန်မှု + လူ့အခွင့်အရေး ကိစ္စများ + ပတ်ဝန်းကျင် + အကြမ်းဖက်ဝါဒနှင့် စစ်သွေးကြွများ + အမုန်းစကား + သတင်းမီဒီယာ + လိင်ကိစ္စ ပညာပေး\n + ပြည်သူ့ကျန်းမာရေး + လောင်းကစားခြင်း + အင်တာနက်ဆင်ဆာ ရှောင်တိမ်းသည့် ကိရိယာများ + အွန်လိုင်း ချိန်းတွေ့ခြင်း + လူမှုကွန်ရက် + LGBTQ+ + File-sharing + ဟက်ကင်းကိရိယာများ + ဆက်သွယ်ရေးကိရိယာများ + မီဒီယာ ဝေမျှခြင်း + နေရာအထိုင်ချခြင်း နှင့် ဘလော့ဂ်တွင် စာရေးသားခြင်း + ရှာဖွေရေးအင်ဂျင်များ + ဂိမ်းဆော့ခြင်း + ယဉ်ကျေးမှု + ဘောဂဗေဒ + အစိုးရ + အီလက်ထရောနစ် ကူးသန်းရောင်းဝယ်ရေး + အကြောင်းအရာကို ထိန်းချုပ်မည် + အစိုးရ အဖွဲ့အစည်းများ + အထွေထွေ အကြောင်းအရာ + မူးယစ်ဆေးဝါးနှင့် အရက်သောစာ သုံးစွဲ၊ ရောင်းချခြင်း + ပံ့ပိုးကူညီမှုနှင့် ဝေဖန်ထောက်ပြမှု အပါအဝင် ဘာသာရေးဆိုင်ရာ ကိစ္စများ + ကြည်လင်ပြတ်သားသော ညစ်ညမ်းရုပ်ပုံများ နှင့် ကြည်လင်ပြတ်သားစွာ မဖော်ပြထားသော ညစ်ညမ်းရုပ်ပုံများ + အဝတ်အစား အနည်းငယ်သာ ဝတ်ဆင်ထားသော အမျိုးသမီးများ၏ ဆွဲဆောင်မှုရှိသော ဝတ်စားဆင်ယင်မှုနှင့် သရုပ်ဖော်ပုံ + ဝေဖန်ထောက်ပြစရာ နိုင်ငံရေးအမြင်များ + လူ့အခွင့်အရေးကိစ္စများ + သဘာဝ ပတ်ဝန်းကျင်ဆိုင်ရာ ကိစ္စရပ်များအပေါ် ဆွေးနွေးချက်များ + အကြမ်းဖက်ဝါဒ၊ အကြမ်းဖက် စစ်သွေးကြွ သို့မဟုတ် ခွဲထွက်ရေး လှုပ်ရှားမှုများ + လူမျိုး၊ လိင်၊ လိင်စိတ် သို့မဟုတ် အခြားသော ဝိသေသ လက္ခဏာများအပေါ် အခြေခံ၍ သီးခြားအုပ်စုများကို နှိမ့်ချခြင်း + အဓိက သတင်းဝဘ်ဆိုဒ်များ၊ ဒေသဆိုင်ရာ သတင်းများနှင့် လွတ်လပ်သော မီဒီယာများ + သန္ဓေတားဆေး၊ STDs၊ မုဒိမ်းမှု ကာကွယ်ရေးနှင့် ကိုယ်ဝန်ဖျက်ချခြင်း အပါအဝင် လိင်ပိုင်းဆိုင်ရာ ကျန်းမာရေး ကိစ္စများ + COVID-19၊ HIV/AIDS၊ Ebola ကဲ့သို့သော ပြည်သူ့ကျန်းမာရေး ပြဿနာများ + အွန်လိုင်းလောင်းကစားနှင့် အလောင်းအစား + အမည်ဝှက်ထားခြင်း၊ ဆင်ဆာဖြတ်တောက်ခြင်း ရှောင်တိမ်းခြင်းနှင့် ကုဒ်ဝှက်ခြင်း + အွန်လိုင်းချိန်းတွေ့ ဆိုဒ်များ + အွန်လိုင်း လူမှုကွန်ရက် ကိရိယာများနှင့် ပလက်ဖောင်းများ + (ညစ်ညမ်းရုပ်ပုံစာပေ မပါဝင်ဘဲ) သက်ဆိုင်ရာ ကိစ္စများကို ဆွေးနွေးသည့် LGBTQ+ အသိုင်းအဝိုင်း + cloud-based ဖိုင်သိုလှောင်မှု၊ torrents နှင့် P2P အပါအဝင် ဖိုင်မျှဝေခြင်း\n + ကွန်ပျူတာ လုံခြုံရေး ကိရိယာများနှင့် သတင်းများ + VoIP၊ စာတို ပေးပို့ခြင်းနှင့် ဝဘ်မေးလ် အပါအဝင် တစ်ဦးချင်းနှင့် အဖွဲ့လိုက် ဆက်သွယ်ရေး ကိရိယာများ + ဗီဒီယို၊ အသံနှင့် ဓာတ်ပုံ မျှဝေခြင်း + ဝဘ်ဆိုဒ် အထိုင်နေရာ၊ ဘလော့ဂ်ရေးသားခြင်း နှင့် အခြား အွန်လိုင်းထုတ်ဝေမှု + ရှာဖွေရေးအင်ဂျင်များနှင့် ပေါ်တယ်လ်များ + အွန်လိုင်း ဂိမ်းများနှင့် ဂိမ်းပလက်ဖောင်းများ (လောင်းကစားဆိုဒ်များ မပါ)\n + သမိုင်း၊ စာပေ၊ ဂီတ၊ ရုပ်ရှင်၊ သရော်စာနှင့် ဟာသများ အပါအဝင် ဖျော်ဖြေမှု\n + အထွေထွေ စီးပွားရေးဖွံ့ဖြိုးတိုးတက်မှုနှင့် ဆင်းရဲမွဲတေမှု + စစ်တပ်အပါအဝင် အစိုးရပိုင် ဝက်ဘ်ဆိုက်များ + စီးပွားဖြစ် ဝန်ဆောင်မှုများနှင့် ထုတ်ကုန်များ + ထိန်းချုပ်ရန် အသုံးပြုသည့် နူးညံ့သိမ်မွေ့သော သို့မဟုတ် အပြစ်ကင်းသော အကြောင်းအရာ + ကုလသမဂ္ဂ အပါအဝင် အစိုးရ အဖွဲ့အစည်းများ + အမျိုးအစား မခွဲခြားရသေးသော ဆိုဒ်များ + diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml index a58a2b6af..1d493a92e 100644 --- a/app/src/main/res/values-ru/strings.xml +++ b/app/src/main/res/values-ru/strings.xml @@ -62,7 +62,7 @@ Проверьте блокировку сайтов используя OONI [Web Connectivity тест](https://ooni.org/nettest/web-connectivity/).\n\nТест использует списки сайтов от Citizen Lab: [глобальный](https://github.com/citizenlab/test-lists/blob/master/lists/global.csv) и [по странам](https://github.com/citizenlab/test-lists/tree/master/lists).\n\nТест покажет, заблокированы ли эти сайты путем вмешательства в DNS, блокировки TCP/IP или через HTTP-прокси.\n\nПолученные результаты будут опубликованы в [OONI Explorer](https://explorer.ooni.org/) и [OONI API](https://api.ooni.io/). Измерить скорость и производительность сети Скорость и производительность сети можно измерить с помощью [NDT](https://ooni.io/nettest/ndt/).\n\nСкорость видеопотока — с помощью [DASH](https://ooni.io/nettest/dash/).\n\nЭти тесты обрабатывают данные и зависят от скорости вашей сети.\n\nРезультаты будут опубликованы в [OONI Explorer](https://explorer.ooni.io/world/) и [OONI API](https://api.ooni.io/).\n\nВнимание: в этих тестах используются серверы третьих сторон. Мы не можем гарантировать конфиденциальность вашего IP-адреса. - Запустив тесты в этой карточке, вы сможете:\n\n- Измерить скорость и производительность сети ([NDT] (https://ooni.org/nettest/ndt/) тест)\n- Измерить производительность потоковой передачи видео ([DASH](https://ooni.org/nettest/dash/) тест)\n- Проверить наличие [middlebox-технологий] (https://ooni.org/support/glossary/#middlebox) в вашей сети ([HTTP Invalid Request Line](https://ooni.org/nettest/http-invalid-request-line/) и [HTTP Header Field Manipulation](https://ooni.org/nettest/http-header-field-manipulation/) тесты)\n\nЭти тесты обрабатывают данные в зависимости от скорости вашей сети.\n\nРезультаты тестов будут опубликованы на сайте [OONI Explorer] (https://explorer.ooni.org/) и [OONI API](https://api.ooni.io/).\n\n**Дисклеймер:* * [NDT](https://ooni.org/nettest/ndt/) и [DASH](https://ooni.org/nettest/dash/) тесты проводятся на сторонних серверах, предоставленных [Measurement Lab (M-Lab)](https://www.measurementlab.net). Если вы запускаете эти тесты, M-Lab соберет и опубликует ваш IP-адрес (в исследовательских целях), независимо от ваших настроек OONI Probe. Узнайте больше об управлении данными компании M-Lab в их [политике конфиденциальности](https://www.measurementlab.net/privacy/). + Запустив тесты в этой карточке, вы сможете:\n\n- Измерить скорость и производительность сети ([NDT](https://ooni.org/nettest/ndt/) тест)\n- Измерить производительность потоковой передачи видео ([DASH](https://ooni.org/nettest/dash/) тест)\n- Проверить наличие [middlebox-технологий](https://ooni.org/support/glossary/#middlebox) в вашей сети ([HTTP Invalid Request Line](https://ooni.org/nettest/http-invalid-request-line/) и [HTTP Header Field Manipulation](https://ooni.org/nettest/http-header-field-manipulation/) тесты)\n\nЭти тесты обрабатывают данные в зависимости от скорости вашей сети.\n\nРезультаты тестов будут опубликованы на сайте [OONI Explorer](https://explorer.ooni.org/) и [OONI API](https://api.ooni.io/).\n\n**Дисклеймер:** [NDT](https://ooni.org/nettest/ndt/) и [DASH](https://ooni.org/nettest/dash/) тесты проводятся на сторонних серверах, предоставленных [Measurement Lab (M-Lab)](https://www.measurementlab.net). Если вы запускаете эти тесты, M-Lab соберет и опубликует ваш IP-адрес (в исследовательских целях), независимо от ваших настроек OONI Probe. Узнайте больше об управлении данными компании M-Lab в их [политике конфиденциальности](https://www.measurementlab.net/privacy/). Определение устройства middlebox в вашей сети Интернет-провайдеры часто используют сетевые устройства (middlebox) в решении различных сетевых задач (например, для кеширования). Бывает, что middleboxes применяются для интернет-цензуры и/или слежки.\n\nПроверьте, присутствуют ли middleboxes в вашей сети используя OONI тесты [HTTP Invalid Request Line](https://ooni.org/nettest/http-invalid-request-line/) и [HTTP Header Field Manipulation](https://ooni.io/nettest/http-header-field-manipulation/).\n\nРезультаты будут опубликованы в [OONI Explorer](https://explorer.ooni.io/world/) и [OONI API](https://api.ooni.io/). Протестировать блокировку мессенджеров @@ -365,7 +365,7 @@ Последний автоматический тест: %1$s. Только на Wi-Fi Только во время зарядки - Если вы включите автоматическое тестирование в OONI Probe, тесты будут проводиться несколько раз в день. Результаты будут автоматически публиковаться в OONI Explorer:https://explorer.ooni.org/\n\nВажно: Если у вас включен VPN, OONI Probe тесты не будут производиться автоматически. Пожалуйста, выключите VPN для автоматических тестов. Узнать больше: 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 для автоматических тестов. Узнать больше: https://ooni.org/support/faq/#can-i-run-ooni-probe-over-a-vpn Обмен Автоматически публиковать результаты Загрузка результатов вручную diff --git a/app/src/main/res/values-sw/strings.xml b/app/src/main/res/values-sw/strings.xml index 2c5a9acf9..642e2a357 100644 --- a/app/src/main/res/values-sw/strings.xml +++ b/app/src/main/res/values-sw/strings.xml @@ -34,7 +34,7 @@ Msimbo wa nchi (k.m. IT ya Italia) Maelezo ya mtandao (pamoja na Nambari ya Mfumo wa Kujiendesha) Muda na tarehe ya majaribio - Tunafanya bidii kutokuchapisha anwani yako ya IP au habari zingine zozote zinayoweza kujitambulisha kibinafsi.\n\nJifunze zaidi kupitia [Sera ya Takwimu ya OONI] (https://ooni.org/about/data-policy/). + Tunafanya bidii kutokuchapisha anwani yako ya IP au habari zingine zozote zinayoweza kujitambulisha kibinafsi.\n\nJifunze zaidi kupitia [Sera ya Takwimu ya OONI](https://ooni.org/about/data-policy/). Kwa bonyeza \"Sawa\", utashiriki ripoti za ubovu wa programu ili kutusaidia kuboresha OONI Probe. Twende Badilisha chaguo-msingi @@ -58,19 +58,19 @@ Gonga kadi kwa zaidi ~%1$s Pima uzuiaji wa tovuti. - Angalia kama tovuti zimezuiwa kwa kutumia [Kipimo wa Kuunganishaa Wavuti](https://ooni.org/nettest/web-connectivity).\n\nKila unapoboyeza \"Anzisha\", unajaribu tovuti tofauti kutoka kwa Maabara ya Citizen [ya kimataifa](https://github.com/citizenlab/test-/blob/master/lists/global.csv) na [nchi maalum](https://github.com/citizenlab/test/tree/master/lists) orodha ya mtihani.\n\nIli kujaribu tovuti za chaguo lako, gusa kitufe cha Chagua tovuti au chagua makundi ya tovuti kupitia mipangilio ya kadi hii. \n\nMtihani huu unapima kama tovuti zimefungwa kwa njia ya DNS kutatiza, TCP / IP kuzuiwa au kwa mhimili wa HTTP wazi.\n\nMatokeo yako yatachapishwa kwenye [OONI Explorer](https://explorer.ooni.org/world/) na [OONI API](https://api.ooni.io/).\n\n.\n\n - Angalia ikiwa tovuti zimezuiwa kutumia OONI\'s [Mtihani wa Muunganisho wa Wavuti] (https://ooni.org/nettest/web-connectivity/).\n\nUtajaribu tovuti zilizojumuishwa katika Maabara ya Citizen [ya kimataifa] (https://github.com/citizenlab/test-lists/blob/master/lists/global.csv) na [maalum kwa nchi] (https: // github .com / Citizlab / test-lists / tree / master / lists) orodha za majaribio.\n\nJaribio hili linapima ikiwa tovuti zimezuiwa kwa njia ya kukwamisha DNS, kuzuia TCP / IP au kwa wakala wa uwazi wa HTTP.\n\nMatokeo yako yatachapishwa kwenye [OONI Explorer] (https://explorer.ooni.org/) na [OONI API] (https://api.ooni.io/). + Angalia kama tovuti zimezuiwa kwa kutumia [Kipimo wa Kuunganishaa Wavuti](https://ooni.org/nettest/web-connectivity).\n\nKila unapoboyeza \"Anzisha\", unajaribu tovuti tofauti kutoka kwa Maabara ya Citizen [ya kimataifa](https://github.com/citizenlab/test-/blob/master/lists/global.csv) na [nchi maalum](https://github.com/citizenlab/test/tree/master/lists) orodha ya mtihani.\n\nIli kujaribu tovuti za chaguo lako, gusa kitufe cha Chagua tovuti au chagua makundi ya tovuti kupitia mipangilio ya kadi hii. \n\nMtihani huu unapima kama tovuti zimefungwa kwa njia ya DNS kutatiza, TCP/IP kuzuiwa au kwa mhimili wa HTTP wazi.\n\nMatokeo yako yatachapishwa kwenye [OONI Explorer](https://explorer.ooni.org/world/) na [OONI API](https://api.ooni.io/).\n + Angalia ikiwa tovuti zimezuiwa kutumia OONI\'s [Mtihani wa Muunganisho wa Wavuti](https://ooni.org/nettest/web-connectivity/).\n\nUtajaribu tovuti zilizojumuishwa katika Maabara ya Citizen [ya kimataifa](https://github.com/citizenlab/test-lists/blob/master/lists/global.csv) na [maalum kwa nchi](https://github.com/citizenlab/test-lists/tree/master/lists) orodha za majaribio.\n\nJaribio hili linapima ikiwa tovuti zimezuiwa kwa njia ya kukwamisha DNS, kuzuia TCP/IP au kwa wakala wa uwazi wa HTTP.\n\nMatokeo yako yatachapishwa kwenye [OONI Explorer](https://explorer.ooni.org/) na [OONI API](https://api.ooni.io/). Pima kasi ya mtandao wako na utendaji - Pima kasi na utendaji kazi wa mtandao wako kwa kutumia jaribio la [NDT] (https://ooni.org/nettest/ndt/).\n\nPima utiririshaji wa video ukitumia jaribio la [DASH] (https://ooni.org/nettest/dash/).\n\nVipimo hivi hutumia data kulingana na kasi ya mtandao wako.\n\nMatokeo yako yatachapishwa kwenye [OONI Explorer] (https://explorer.ooni.org/world/) na [OONI API] (https://api.ooni.io/).\n\nKanusho: Majaribio haya yanategemea seva za mtu mwingine. Kwa hivyo hatuwezi kuhakikisha kuwa anwani yako ya IP haitakusanywa. - Kwa kufanya majaribio kwenye kadi hii, uta:\n\n- Pima kasi na utendaji wa mtandao wako ([NDT] (https://ooni.org/nettest/ndt/) mtihani)\n- Pima utiririshaji wa video ([DASH] (https://ooni.org/nettest/dash/) mtihani)\n- Angalia uwepo wa [teknolojia za kati] -request-line /) na [Udhibiti wa Shamba la kichwa cha HTTP] (https://ooni.org/nettest/http-header-field-manipulation/) vipimo)\n\nVipimo hivi hutumia data kulingana na kasi ya mtandao wako.\n\nMatokeo yako ya mtihani yatachapishwa kwenye [OONI Explorer] (https://explorer.ooni.org/) na [OONI API] (https://api.ooni.io/).\n\n** Kanusho: ** Jaribio la [NDT] (https://ooni.org/nettest/ndt/) na [DASH] (https://ooni.org/nettest/dash/) hufanywa dhidi ya seva za mtu wa tatu iliyotolewa na [Maabara ya Upimaji (M-Lab)] (https://www.measurementlab.net/). Ikiwa utafanya majaribio haya, M-Lab itakusanya na kuchapisha anwani yako ya IP (kwa sababu za utafiti), bila kujali mipangilio yako ya OONI Probe. Pata maelezo zaidi kuhusu utawala wa data wa M-Lab kupitia [taarifa ya faragha] (https://www.measurementlab.net/privacy/). + Pima kasi na utendaji kazi wa mtandao wako kwa kutumia jaribio la [NDT](https://ooni.org/nettest/ndt/).\n\nPima utiririshaji wa video ukitumia jaribio la [DASH](https://ooni.org/nettest/dash/).\n\nVipimo hivi hutumia data kulingana na kasi ya mtandao wako.\n\nMatokeo yako yatachapishwa kwenye [OONI Explorer](https://explorer.ooni.org/world/) na [OONI API](https://api.ooni.io/).\n\nKanusho: Majaribio haya yanategemea seva za mtu mwingine. Kwa hivyo hatuwezi kuhakikisha kuwa anwani yako ya IP haitakusanywa. + Kwa kufanya majaribio kwenye kadi hii, uta:\n\n- Pima kasi na utendaji wa mtandao wako ([NDT](https://ooni.org/nettest/ndt/) mtihani)\n- Pima utiririshaji wa video ([DASH](https://ooni.org/nettest/dash/) mtihani)\n- Angalia uwepo wa [teknolojia za kati] (https://ooni.org/sw/support/glossary#middlebox) na [Udhibiti wa Shamba la kichwa cha HTTP](https://ooni.org/nettest/http-header-field-manipulation/) vipimo)\n\nVipimo hivi hutumia data kulingana na kasi ya mtandao wako.\n\nMatokeo yako ya mtihani yatachapishwa kwenye [OONI Explorer](https://explorer.ooni.org/) na [OONI API](https://api.ooni.io/).\n\n**Kanusho:** Jaribio la [NDT](https://ooni.org/nettest/ndt/) na [DASH](https://ooni.org/nettest/dash/) hufanywa dhidi ya seva za mtu wa tatu iliyotolewa na [Maabara ya Upimaji (M-Lab)] (https://www.measurementlab.net/). Ikiwa utafanya majaribio haya, M-Lab itakusanya na kuchapisha anwani yako ya IP (kwa sababu za utafiti), bila kujali mipangilio yako ya OONI Probe. Pata maelezo zaidi kuhusu utawala wa data wa M-Lab kupitia [taarifa ya faragha](https://www.measurementlab.net/privacy/). Gundua visanduku vya kati kwenye mtandao wako - Watoa Huduma za Mtandao mara nyingi hutumia vifaa vya mtandao (visanduku vya kati) kwa madhumuni mbalimbali ya mitandao (kama akiba). Wakati mwingine sanduku hizi za kati hutumiwa kutekeleza udhibiti wa mtandao na / au ufuatiliaji.\n\nPata visanduku vya kati katika mtandao wako ukitumia OONI ya [Mstari wa Ombi batili wa HTTP] (https://ooni.org/nettest/http-invalid-request-line/) na [Udhibiti wa Shamba la Kichwa cha HTTP] (https://ooni.org/nettest / http-kichwa-shamba-kudanganywa /) vipimo.\n\nMatokeo yako yatachapishwa kwenye [OONI Explorer] (https://explorer.ooni.org/world/) na [OONI API] (https://api.ooni.io/) + Watoa Huduma za Mtandao mara nyingi hutumia vifaa vya mtandao (visanduku vya kati) kwa madhumuni mbalimbali ya mitandao (kama akiba). Wakati mwingine sanduku hizi za kati hutumiwa kutekeleza udhibiti wa mtandao na / au ufuatiliaji.\n\nPata visanduku vya kati katika mtandao wako ukitumia OONI ya [Mstari wa Ombi batili wa HTTP](https://ooni.org/nettest/http-invalid-request-line/) na [Udhibiti wa Shamba la Kichwa cha HTTP](https://ooni.org/nettest / http-kichwa-shamba-kudanganywa /) vipimo.\n\nMatokeo yako yatachapishwa kwenye [OONI Explorer](https://explorer.ooni.org/world/) na [OONI API](https://api.ooni.io/) Jaribu uzuiaji wa programu za ujumbe wa papo hapo - Angalia ikiwa [WhatsApp] (https://ooni.org/nettest/whatsapp/), [Facebook Messenger] (https://ooni.org/nettest/facebook-messenger/), [Telegram] (https: // ooni .org / nettest / telegram /), na [Signal] (https://ooni.org/nettest/signal) zimezuiwa.\n\nMatokeo yako yatachapishwa kwenye [OONI Explorer] (https://explorer.ooni.org/world/) na [OONI API] (https://api.ooni.io/). + Angalia ikiwa [WhatsApp](https://ooni.org/nettest/whatsapp/), [Facebook Messenger](https://ooni.org/nettest/facebook-messenger/), [Telegram](https://ooni.org/nettest/telegram/), na [Signal](https://ooni.org/nettest/signal) zimezuiwa.\n\nMatokeo yako yatachapishwa kwenye [OONI Explorer](https://explorer.ooni.org/world/) na [OONI API](https://api.ooni.io/). Jaribu kuzuia vifaa vya kuzuia uzuiaji - Angalia ikiwa [Psiphon] (https://ooni.org/nettest/psiphon/), [Tor] (https://ooni.org/nettest/tor/) au [RiseupVPN] (https://ooni.org/ nettest / riseupvpn /) zimezuiwa.\n\nMatokeo yako yatachapishwa kwenye [OONI Explorer] (https://explorer.ooni.org/) na [OONI API] (https://api.ooni.io/). + Angalia ikiwa [Psiphon](https://ooni.org/nettest/psiphon/), [Tor](https://ooni.org/nettest/tor/) au [RiseupVPN](https://ooni.org/nettest/riseupvpn/) zimezuiwa.\n\nMatokeo yako yatachapishwa kwenye [OONI Explorer](https://explorer.ooni.org/) na [OONI API](https://api.ooni.io/). Endesha majaribio mapya ya majaribio - Tumia majaribio mapya ya majaribio yaliyotengenezwa na timu ya OONI:\n{orodha ya majaribio_ya majaribio}\n\nMatokeo yako yatachapishwa kwenye [OONI Explorer] (https://explorer.ooni.org/) na [OONI API] (https://api.ooni.io/). + Tumia majaribio mapya ya majaribio yaliyotengenezwa na timu ya OONI:\n{orodha ya majaribio_ya majaribio}\n\nMatokeo yako yatachapishwa kwenye [OONI Explorer](https://explorer.ooni.org/) na [OONI API](https://api.ooni.io/). Vipimo vyafuatavyo yataendeshwa tu kama sehemu ya majaribio ya kiotomatiki: Disabled Tests Gbit / s @@ -160,7 +160,7 @@ Nakili URL ya Kichunguzi Shiriki URL ya Kichunguzi Nakili kwenye ubao wa kunakili - Onyesha katika OONI Explorer + Katika OONI Explorer Imeshindwa Unaweza kujaribu jaribio hili tena jaribu tena @@ -168,12 +168,12 @@ Inapatikana %1$s inapatikana. Inawezekana imefungwa - %1$s inawezekana imezuiwa kwa njia ya %2$s.\n\nKumbuka: Chanya za uwongo zinaweza kutokea. Jifunze zaidi [hapa] (https://ooni.org/support/faq/#what-are-false-positives). + %1$s inawezekana imezuiwa kwa njia ya %2$s.\n\nKumbuka: Chanya za uwongo zinaweza kutokea. Jifunze zaidi [hapa](https://ooni.org/support/faq/#what-are-false-positives). Udhibiti wa Udhibiti - ** Uharibifu wa DNS ** - ** Kuzuia msingi wa TCP / IP ** - ** Kuzuia HTTP (ukurasa wa kuzuia unaweza kutumiwa) ** - ** Kuzuia HTTP (maombi ya HTTP yameshindwa) ** + **Uharibifu wa DNS** + **Kuzuia msingi wa TCP/IP** + **Kuzuia HTTP (ukurasa wa kuzuia unaweza kutumiwa)** + **Kuzuia HTTP (maombi ya HTTP yameshindwa)** Programu ya Simu ya Mkononi sawa Imeshindwa @@ -236,14 +236,14 @@ Kuchelewa kwa Playout Inawezekana imefungwa Kufanya kazi - [Psiphon] (https://psiphon.ca/) inaonekana kuzuiwa. - Tuliweza kufanikiwa kufunga bootstrap muunganisho wa Psiphon. Hii inamaanisha kuwa [Psiphon] (https://psiphon.ca/) inapaswa kufanya kazi. + [Psiphon](https://psiphon.ca/) inaonekana kuzuiwa. + Tuliweza kufanikiwa kufunga bootstrap muunganisho wa Psiphon. Hii inamaanisha kuwa [Psiphon](https://psiphon.ca/) inapaswa kufanya kazi. Wakati wa Bootstrap sekunde %1$s Inawezekana imefungwa Kufanya kazi - .[Tor] (https://www.torproject.org/) inaonekana kuzuiwa - Tuliweza kufanikiwa kuunganisha kwa madaraja ya Tor ya msingi na / au mamlaka ya saraka ya Tor. Hii inamaanisha kuwa [Tor] (https://www.torproject.org/) inapaswa kufanya kazi. + .[Tor](https://www.torproject.org/) inaonekana kuzuiwa + Tuliweza kufanikiwa kuunganisha kwa madaraja ya Tor ya msingi na / au mamlaka ya saraka ya Tor. Hii inamaanisha kuwa [Tor](https://www.torproject.org/) inapaswa kufanya kazi. Madaraja chaguomsingi %1$s / %2$s sawa Mamlaka ya Saraka @@ -255,8 +255,8 @@ Kushikana mikono Inawezekana imefungwa Kufanya kazi - [KuinukaVPN] (https://riseup.net/vpn) inaonekana kuzuiwa.\n \n\n \n \n \n \n  - Tumeweza kufanikiwa kuungana na seva ya bootstrap ya RiseupVPN na milango ya VPN. Hii inamaanisha kuwa [RiseupVPN] (https://riseup.net/vpn) inapaswa kufanya kazi. + [KuinukaVPN](https://riseup.net/vpn) inaonekana kuzuiwa. + Tumeweza kufanikiwa kuungana na seva ya bootstrap ya RiseupVPN na milango ya VPN. Hii inamaanisha kuwa [RiseupVPN](https://riseup.net/vpn) inapaswa kufanya kazi. Seva ya Bootstrap Uunganisho wa OpenVPN Uunganisho uliofungwa @@ -402,7 +402,7 @@ Jina la mtumiaji Nywila Tumia Psiphon kwa ajili ya wakala maalum - Je! Hauwezi kutumia OONI Probe? Jaribu kuwezesha [Psiphon] (https://psiphon.ca/) kukwepa uwezekano wa kuzuia OONI Probe. Vinginevyo, unaweza kutumia wakala wa kawaida. + Je! Hauwezi kutumia OONI Probe? Jaribu kuwezesha [Psiphon](https://psiphon.ca/) kukwepa uwezekano wa kuzuia OONI Probe. Vinginevyo, unaweza kutumia wakala wa kawaida. Punguza muda wa kujaribu Muda wa jaribio Vikundi vya wavuti kujaribu @@ -449,7 +449,7 @@ Uhifadhi uliotumika Futa Ondoa - Uko karibu kufuta vipimo vyote vya OONI kutoka kwa kifaa chako. Ikiwa zimepakiwa, bado zitapatikana kwenye [OONI Explorer] (https://explorer.ooni.org) + Uko karibu kufuta vipimo vyote vya OONI kutoka kwa kifaa chako. Ikiwa zimepakiwa, bado zitapatikana kwenye [OONI Explorer](https://explorer.ooni.org) Imemaliza kukimbia Acha Jaribio Jaribu kioo diff --git a/app/src/main/res/values-tr/strings.xml b/app/src/main/res/values-tr/strings.xml index 0021f7bde..591a29837 100644 --- a/app/src/main/res/values-tr/strings.xml +++ b/app/src/main/res/values-tr/strings.xml @@ -2,12 +2,12 @@ OONI Probe OONI Probe nedir? - İnternet sansürünü ölçme uygulaması.\n\nWeb siteleri ve sosyal ağ uygulamaları engelleniyor mu? İnternet bağlantınız genel olarak yavaş mı?\n\nSorunu bulmak için OONI Probe uygulamasını çalıştırın! + İnternet sansürünü ölçme uygulaması.\n\nSiteler ve sosyal ağ uygulamaları engelleniyor mu? İnternet bağlantınız genel olarak yavaş mı?\n\nSorunu bulmak için OONI Probe uygulamasını çalıştırın! Anladım Hatırlatma! OONI verileri herkese açık olarak yayınlanır ve ağ bilgilerinizi de içerir. İnternet işlemlerinizi izleyenler (hükümet ya da İnternet hizmeti sağlayıcınız) OONI Probe uygulamasını çalıştırdığınızı görebilir. - Engellenmiş web sitelerini sınayabilirsiniz (ancak sınanacak siteleri seçebilirsiniz). + Engellenmiş siteleri sınayabilirsiniz (ancak sınanacak siteleri seçebilirsiniz). Anladım Ayrıntılı bilgi alın Sürpriz sınav @@ -16,15 +16,15 @@ Geri dön Devam et Soru 1/2 - İnternet işlemlerim izleniyorsa, OONI Probe uygulamasını çalıştırdığım anlaşılabilir. + İnternetim izleniyorsa, OONI Probe çalıştırdığım anlaşılabilir. Uyarı OONI Probe bir kişisel gizlilik sağlama aracı değildir. İnternet işlemlerinizi izleyenler çalıştırdığınız uygulamaları görebilir. Soru 2/2 - OONI Probe uygulamasını her çalıştırdığımda topladığım ağ verileri herkese açık olarak yayınlanır. + OONI Probe uygulamasını çalıştırdığımda topladığım veriler herkese açık olarak yayınlanır. Uyarı - İnternet sansürü şeffaflığını sağlamak için tüm OONI Probe kullanıcılarının ağ verileri otomatik olarak yayınlanır (ayarlardan devre dışı bırakılmadıkça). + İnternet sansürü şeffaflığını sağlamak için tüm OONI Probe kullanıcılarının ağ verileri otomatik olarak yayınlanır (ayarlardan kapatılmadıkça). Otomatik sınama - Lütfen İnternet sansürünün OONI Probe tarafından günlük olarak ölçülebilmesi için otomatik sınamayı etkinleştirin.\n\nEndişelenmeyin, pil kullanımına dikkat edeceğiz.\n\nOtomatik sınamayı istediğiniz zaman ayarlardan devre dışı bırakabilirsiniz. + Lütfen İnternet sansürünün OONI Probe tarafından günlük olarak ölçülebilmesi için otomatik sınamayı açın.\n\nEndişelenmeyin, pil kullanımına dikkat edeceğiz.\n\nOtomatik sınamayı istediğiniz zaman ayarlardan kapatabilirsiniz. Çökme bildirimleri OONI Probe uygulamasını iyileştirmek için, sorun çıktığında kişisel veri içermeyen anonim çökme bildirimleri almak istiyoruz.\n\nÇökme bildirimlerinin OONI geliştirme ekibine gönderilmesini ister misiniz? Evet @@ -35,7 +35,7 @@ Ağ bilgileri (otonom sistem numarası ile) Sınama tarihi ve saati IP adresinizi ya da kim olduğunuzu ortaya çıkarabilecek bilgileri yayınlamamak için elimizden geleni yapıyoruz.\n\nAyrıntılı bilgi almak için [OONI veri işleme ilkesi](https://ooni.io/about/data-policy/) bölümüne bakabilirsiniz. - OONI Probe uygulamasını geliştirmemiz için \"Tamam\" üzerine dokunarak, çökme raporunu bizimle paylaşın. + OONI Probe uygulamasını geliştirmemiz için \"Tamam\" üzerine dokunarak, çökme bildirimini bizimle paylaşın. Başlayalım Varsayılanları değiştir Pano @@ -44,7 +44,7 @@ Çalıştır Son sınama: Yaklaşık: - Web sitelerini seçin + Siteleri seçin Çalışıyor: Yaklaşık kalan süre: %1$s saniye @@ -57,22 +57,22 @@ Kullanılan vekil sunucu Ayrıntılar için karta dokunun ~%1$ss - Web sitesi engellemelerini sınayın - OONI [web bağlantısı sınaması](https://ooni.org/nettest/web-connectivity/) özelliğini kullanarak web sitelerinin engellenip engellenmediğini denetleyebilirsiniz.\n\nÇalıştır üzerine her tıkladığınızda, Citizen Lab [küresel](https://github.com/citizenlab/test-lists/blob/master/lists/global.csv) ve [ülkeye özel](https://github.com/citizenlab/test-lists/tree/master/lists) sınama listelerindeki çeşitli web siteleri denetlenir.\n\nBelirli web sitelerini sınamak için web sitelerini seçin düğmesine tıklayın ya da bu kartın ayarlarından kategori ya da siteler seçin. \n\nBu sınama web sitelerinin DNS müdahalesi, TCP/IP engelleme ya da görünmez vekil sunucu ile engellenip engellenmediğini ortaya çıkarır.\n\nSonuçlarınız [OONI Explorer](https://explorer.ooni.org/world/) ve [OONI API](https://api.ooni.io/) üzerinde yayınlanır. - OONI [web bağlantısı sınaması](https://ooni.org/nettest/web-connectivity/) özelliğini kullanarak web sitelerinin engellenip engellenmediğini denetleyebilirsiniz.\n\nSınama ile Citizen Lab [küresel](https://github.com/citizenlab/test-lists/blob/master/lists/global.csv) ve [ülkeye özel](https://github.com/citizenlab/test-lists/tree/master/lists) sınama listelerindeki web siteleri denetlenir.\n\nBu sınama web sitelerinin DNS müdahalesi, TCP/IP engelleme ya da görünmez vekil sunucu ile engellenip engellenmediğini ortaya çıkarır.\n\nSonuçlarınız [OONI Explorer](https://explorer.ooni.org/world/) ve [OONI API](https://api.ooni.io/) üzerinde yayınlanır. + Site engellemelerini sınayın + OONI [site bağlantısı sınaması](https://ooni.org/nettest/web-connectivity/) özelliğini kullanarak sitelerin engellenip engellenmediğini denetleyebilirsiniz.\n\nÇalıştır üzerine her tıkladığınızda, Citizen Lab [küresel](https://github.com/citizenlab/test-lists/blob/master/lists/global.csv) ve [ülkeye özel](https://github.com/citizenlab/test-lists/tree/master/lists) sınama listelerindeki çeşitli siteler denetlenir.\n\nBelirli siteleri sınamak için siteleri seçin düğmesine tıklayın ya da bu kartın ayarlarından kategoriyi ya da siteleri seçin. \n\nBu sınama sitelerin DNS müdahalesi, TCP/IP engelleme ya da görünmez vekil sunucu ile engellenip engellenmediğini ortaya çıkarır.\n\nSonuçlarınız [OONI Explorer](https://explorer.ooni.org/world/) ve [OONI API](https://api.ooni.io/) üzerinde yayınlanır. + OONI [site bağlantısı sınaması](https://ooni.org/nettest/web-connectivity/) özelliğini kullanarak sitelerin engellenip engellenmediğini denetleyebilirsiniz.\n\nSınama ile Citizen Lab [küresel](https://github.com/citizenlab/test-lists/blob/master/lists/global.csv) ve [ülkeye özel](https://github.com/citizenlab/test-lists/tree/master/lists) sınama listelerindeki siteler denetlenir.\n\nBu sınama sitelerin DNS müdahalesi, TCP/IP engelleme ya da görünmez vekil sunucu ile engellenip engellenmediğini ortaya çıkarır.\n\nSonuçlarınız [OONI Explorer](https://explorer.ooni.org/world/) ve [OONI API](https://api.ooni.io/) üzerinde yayınlanır. Ağınızın hızını ve başarımını ölçün [NDT](https://ooni.org/nettest/ndt/) sınaması ile ağınızın hızını ve başarımını ölçebilirsiniz.\n\n[DASH](https://ooni.org/nettest/dash/) sınaması ile görüntü akış başarımını ölçebilirsiniz.\n\nBu sınamalar sırasında ağınızın hızına göre belirlenen miktarda çeşitli veriler aktarılır.\n\nSonuçlarınız [OONI Explorer](https://explorer.ooni.org/world/) ve [OONI API](https://api.ooni.io/) üzerinde yayınlanır.\n\nBildirim: Bu sınamalar üçüncü tarafların sunucuları üzerinden yapılır. Bu nedenle IP adresinizin kaydedilmeyeceğini garanti edemeyiz. - Bu bölümdeki sınamaları yaparak şu bilgileri edinebilirsiniz:\n\n- Ağınızın hızını ve başarımını ölçebilirsiniz ([NDT](https://ooni.org/nettest/ndt/) sınaması)\n- Görüntü aktarma başarımını ölçebilirsiniz ([DASH](https://ooni.org/nettest/dash/) sınaması)\n- Ağınızdaki [ara aygıt teknolojilerini](https://ooni.org/support/glossary/#middlebox) öğrenebilirsiniz ([HTTP geçersiz istek hattı](https://ooni.org/nettest/http-invalid-request-line/) ve [HTTP üst bilgi değişikliği](https://ooni.org/nettest/http-header-field-manipulation/) sınamaları)\n\nBu sınamalar sırasında ağ hızınıza göre değişen bir miktarda veri aktarılır.\n\nSınama sonuçlarınız [OONI Explorer](https://explorer.ooni.org/) ve [OONI API](https://api.ooni.io/) üzerinde yayınlanır.\n\n**Sorumluluk reddi:** [NDT](https://ooni.org/nettest/ndt/) ve [DASH](https://ooni.org/nettest/dash/) sınamaları, [Measurement Lab (M-Lab)](https://www.measurementlab.net/) tarafından sağlanan üçüncü taraf sunucular kullanılarak gerçekleştirilir. Bu sınamaları yaptığınızda, OONI Probe ayarlarınızdan bağımsız olarak IP adresiniz M-Lab tarafından alınır ve yayınlanır (araştırma amacıyla). Verilerinizin M-Lab tarafından kullanılması hakkında ayrıntılı bilgi almak için [kişisel gizlilik duyurusuna](https://www.measurementlab.net/privacy/) bakabilirsiniz. + Bu bölümdeki sınamaları yaparak şu bilgileri edinebilirsiniz:\n\n- Ağınızın hızını ve başarımını ölçebilirsiniz ([NDT](https://ooni.org/nettest/ndt/) sınaması)\n- Görüntü aktarma başarımını ölçebilirsiniz ([DASH](https://ooni.org/nettest/dash/) sınaması)\n- Ağınızdaki [ara aygıt teknolojilerini](https://ooni.org/support/glossary/#middlebox) öğrenebilirsiniz ([HTTP geçersiz istek satırı](https://ooni.org/nettest/http-invalid-request-line/) ve [HTTP üst bilgi değişikliği](https://ooni.org/nettest/http-header-field-manipulation/) sınamaları)\n\nBu sınamalar sırasında ağ hızınıza göre değişen bir miktarda veri aktarılır.\n\nSınama sonuçlarınız [OONI Explorer](https://explorer.ooni.org/) ve [OONI API](https://api.ooni.io/) üzerinde yayınlanır.\n\n**Sorumluluk reddi:** [NDT](https://ooni.org/nettest/ndt/) ve [DASH](https://ooni.org/nettest/dash/) sınamaları, [Measurement Lab (M-Lab)](https://www.measurementlab.net/) tarafından sağlanan üçüncü taraf sunucular kullanılarak gerçekleştirilir. Bu sınamaları yaptığınızda, OONI Probe ayarlarınızdan bağımsız olarak IP adresiniz M-Lab tarafından alınır ve yayınlanır (araştırma amacıyla). Verilerinizin M-Lab tarafından kullanılması hakkında ayrıntılı bilgi almak için [kişisel gizlilik duyurusuna](https://www.measurementlab.net/privacy/) bakabilirsiniz. Ağınızdaki ara kutuları bulun - İnternet hizmeti sağlayıcıları sıklıkla bazı ağ uygulamaları için (ön bellekleme gibi) çeşitli aygıtlar (ara kutular) kullanır. Bazen bu ara kutular İnternet sansürü ve izlemesi için kullanılır.\n\nOONI [HTTP geçersiz istek hattı](https://ooni.org/nettest/http-invalid-request-line/) ve [HTTP üst bilgi alanı değişikliği](https://ooni.org/nettest/http-header-field-manipulation/) sınamalarını kullanarak ara kutuları bulabilirsiniz.\n\nSonuçlarınız [OONI Explorer](https://explorer.ooni.org/world/) ve [OONI API](https://api.ooni.io/) üzerinde yayınlanır. + İnternet hizmeti sağlayıcıları sıklıkla bazı ağ uygulamaları için (ön bellekleme gibi) çeşitli aygıtlar (ara kutular) kullanır. Bazen bu ara kutular İnternet sansürü ve izlemesi için kullanılır.\n\nOONI [HTTP geçersiz istek satırı](https://ooni.org/nettest/http-invalid-request-line/) ve [HTTP üst bilgi alanı değişikliği](https://ooni.org/nettest/http-header-field-manipulation/) sınamalarını kullanarak ara kutuları bulabilirsiniz.\n\nSonuçlarınız [OONI Explorer](https://explorer.ooni.org/world/) ve [OONI API](https://api.ooni.io/) üzerinde yayınlanır. Anlık ileti uygulamalarının engellenip engellenmediğini anlayın [WhatsApp](https://ooni.org/nettest/whatsapp/), [Facebook Messenger](https://ooni.org/nettest/facebook-messenger/), [Telegram](https://ooni.org/nettest/telegram/) ve [Signal](https://ooni.org/nettest/signal) uygulamalarının engellenip engellenmediğini kontrol edebilirsiniz.\n\nSonuçlarınız [OONI Explorer](https://explorer.ooni.org/world/) ve [OONI API](https://api.ooni.io/) üzerinde yayınlanır. Sansürü aşma araçları engellemesini sınayın [Psiphon](https://ooni.org/nettest/psiphon/) ve [Tor](https://ooni.org/nettest/tor/) ya da [RiseupVPN](https://ooni.org/nettest/riseupvpn/) engelleniyor mu öğrenin.\n\nSonuçlarınız [OONI Explorer](https://explorer.ooni.org/) ve [OONI API](https://api.ooni.io/) sayfasında yayınlanır. - Yeni deneysel sınamaları yürüt - OONI ekibi tarafından geliştirilmiş yeni deneysel sınamaları yürütün:\n%1$s\n\nSonuçlarınız [OONI Explorer](https://explorer.ooni.org/) ve [OONI API](https://api.ooni.io/) üzerinde yayınlanır. - Şu sınamalar yalnızca otomatik sınamanın bir parçası olarak yürütülecek: - Devre dışı bırakılmış sınamalar + Yeni deneysel sınamaları çalıştır + OONI ekibi tarafından geliştirilmiş yeni deneysel sınamaları çalıştırın:\n%1$s\n\nSonuçlarınız [OONI Explorer](https://explorer.ooni.org/) ve [OONI API](https://api.ooni.io/) üzerinde yayınlanır. + Şu sınamalar yalnızca otomatik sınamanın bir parçası olarak çalıştırılacak: + Kapatılmış sınamalar Gbit/s Mbit/s kbit/s @@ -86,7 +86,7 @@ Veri kullanımı Süzgeç sınamaları Tüm sınamalar - Web siteleri + Siteler Ara kutular Başarım Anlık ileti @@ -125,8 +125,8 @@ Sınanmış Engellenmiş Engellenmiş - Web sitesi - Web sitesi + Site + Site Erişilebilir Erişilebilir Görüntü @@ -160,9 +160,9 @@ Gezgin adresini kopyala Gezgin adresini paylaş Panoya kopyala - OONI gezgininde görüntüle - Başarısız - Bu sınamayı yeniden çalıştırmayı deneyebilirsiniz + OONI Explorer üzerinde görüntüle + Tamamlanamadı + Bu sınamayı yeniden çalıştırmayı deneyebilirsiniz Yeniden dene Bu sınamanın nasıl yapıldığını [buradan](%1$s) öğrenebilirsiniz. Erişilebilir @@ -184,7 +184,7 @@ Tamam Tamamlanamadı Çalışıyor - Bu sınama sırasında WhatsApp bağlantı noktaları, kayıt hizmeti ve web arayüzü (web.whatsapp.com) ile sorunsuz bağlantı kuruldu. + Bu sınama sırasında WhatsApp bağlantı noktaları, kayıt hizmeti ve internet arayüzü (web.whatsapp.com) ile sorunsuz bağlantı kuruldu. Engellenmiş olabilir WhatsApp engellenmiş gibi görünüyor. Mobil uygulama @@ -194,7 +194,7 @@ Tamam Tamamlanamadı Çalışıyor - Bu sınama sırasında Telegram bağlantı noktaları ve web arayüzü (web.telegram.org) ile sorunsuz bağlantı kuruldu. + Bu sınama sırasında Telegram bağlantı noktaları ve internet arayüzü (web.telegram.org) ile sorunsuz bağlantı kuruldu. Engellenmiş olabilir Telegram engellenmiş gibi görünüyor. TCP bağlantıları @@ -282,7 +282,7 @@ Sınama yapılamadı. Lütfen İnternet bağlantınızı denetleyin. Adres listesi indirilemedi. Lütfen yeniden deneyin. Lütfen yeni bir sınama başlatmadan önce sürmekte olan sınamaların tamamlanmasını bekleyin. - Bildirim izinleri gerekli. Lütfen telefonunuzun ayarlar bölümünden gerekli izinleri verdikten sonra OONI Probe uygulamanızdan etkinleştirin. + Bildirim izinleri gerekli. Lütfen telefonunuzun ayarlar bölümünden gerekli izinleri verdikten sonra OONI Probe uygulamanızdan açın. Ayarlar bölümüne git Bu ekran sınama sırasında kilitlidir. Ham ölçüm verilerini indirebilmek için İnternet bağlantınız olmalıdır. @@ -294,24 +294,24 @@ Lütfen VPN bağlantınızı kapatın. OONI Probe uygulamasını VPN açıkken çalıştırırsanız, sınama sonuçları yanlış ülkeden geliyormuş gibi görünebilir. Lütfen VPN bağlantınızı kapatın. Bazı ölçümler VPN üzerinden alınmıştır. - VPN etkinken alınan ölçümleri yüklerseniz, sınama sonuçları yanlış ülkeden geliyormuş gibi görünebilir. + VPN kullanılıyorken alınan ölçümleri yüklerseniz, sınama sonuçları yanlış ülkeden geliyormuş gibi görünebilir. Yüklendi Hata günlüğünü görüntüle İnternet sansürleri hakkında güncel bilgileri alın - Yeni sansür uygulamaları sırasında OONI Probe sınamaları yapmak ilginizi çeker mi? Yakınınızda bir İnternet sansürü olduğunu öğrendiğimizde size bildirmemiz için bildirimleri etkinleştirebilirsiniz. - Sınamaların ayrıntısını arttırmak için GPS izinlerine gerek duyulur. OONI yalnızca yaklaşık GPS konumunuzu kullanır. + Yeni sansür uygulamaları sırasında OONI Probe sınamaları yapmak ilginizi çeker mi? Yakınınızda bir İnternet sansürü olduğunu öğrendiğimizde size bildirmemiz için bildirimleri açabilirsiniz. + Sınamaların ayrıntısını arttırmak için konum izinlerine gerek duyulur. OONI yalnızca yaklaşık GPS konumunuzu kullanır. Tüm sınama sonuçlarını silmek ister misiniz? Bu sınamayı silmek ister misiniz? - Lütfen en az bir sınamayı etkinleştirin + Lütfen en az bir sınamayı açın Lütfen bu alana sadece sayı yazın. Sınamayı yinele Bu sınama tamamlanamadı. Yeniden sınamak ister misiniz? - %1$s web sitesini yeniden sınamak üzeresiniz. + %1$s siteyi yeniden sınamak üzeresiniz. Çalıştır Bu sayfadan ayrıldığınızda adresleriniz kaydedilmez. Bu sayfadan ayrılmak istediğinize emin misiniz? El ile yükleme yapılabilsin mi? - Bu seçenek etkinleştirildiğinde, yayınlanmamış ölçümler el ile yeniden yüklenebilir. - Etkinleştir + Bu seçenek açıldığında, yayınlanmamış ölçümler el ile yeniden yüklenebilir. + Hayır, teşekkürler Yüklenemedi %1$s/%2$s ölçüm yüklenemedi. Sorun ile ilgili günlük kayıtları OONI geliştiricileri ile paylaşıldı. @@ -320,8 +320,8 @@ JSON boş Bu sınamayı durdurmak istediğinize emin misiniz? Bu işlem şu anda yapılmakta olan sınamayı durduracak. - Sınamalar otomatik olarak yürütülsün mü? - Otomatik sınamayı etkinleştirerek, OONI ölçümlerinin düzenli olarak yapılmasına katkıda bulunacaksınız. + Sınamalar otomatik olarak çalıştırılsın mı? + Otomatik sınamayı açarak, OONI ölçümlerinin düzenli olarak yapılmasına katkıda bulunacaksınız. Lütfen uygulamanın arka planda çalışmasına izin verin. Beni hatırla Panoya kopyalandı @@ -329,15 +329,15 @@ Yükle Bazıları yüklenmemiş Tümünü yükle - Web siteleri + Siteler Anlık ileti Ara kutular Başarım Sansürü aşma Deneysel - HTTP geçersiz istek hattı sınaması + HTTP geçersiz istek satırı sınaması HTTP üst bilgi değişikliği sınaması - Web bağlantı sınaması + Site bağlantısı sınaması NDT hız sınaması DASH akış sınaması WhatsApp sınaması @@ -356,16 +356,16 @@ Raporlar OONI veri işleme ilkesi Bildirimler - Etkin + Açık Sınamanın tamamlandığı bildirilsin Haber akışı Otomatik sınama - Sınamalar otomatik olarak yapılsın + Sınamalar otomatik olarak çalıştırılsın Otomatik sınama sayısı: %1$s. Son otomatik sınama: %1$s. - Yanlız Wi-Fi kullanılırken + Yalnızca Wi-Fi kullanılırken Yalnızca şarj edilirken - Otomatik sınama etkinleştirildiğinde, OONI Prob sınamaları günde birkaç kez otomatik olarak çalışır. Sınama sonuçlarınız otomatik olarak OONI Explorer üzerinde yayınlanır: https://explorer.ooni.org/\n\nÖnemli: Etkinleştirilmiş bir VPN bağlantınız varsa, OONI Probe sınamaları otomatik olarak çalıştırmaz. Otomatik OONI Probe sınaması için lütfen VPN bağlantınızı kapatın. Ayrıntılı bilgi alın: https://ooni.org/support/faq/#can-i-run-ooni-probe-over-a-vpn + Otomatik sınama açıldığında, OONI Probe sınamaları günde birkaç kez otomatik olarak çalışır. Sınama sonuçlarınız otomatik olarak OONI Explorer üzerinde yayınlanır: https://explorer.ooni.org/\n\nÖnemli: Etkin bir VPN bağlantınız varsa, OONI Probe sınamaları otomatik olarak çalıştırmaz. Otomatik OONI Probe sınaması için lütfen VPN bağlantınızı kapatın. Ayrıntılı bilgi almak için https://ooni.org/support/faq/#can-i-run-ooni-probe-over-a-vpn adresine bakabilirsiniz Paylaşım Sonuçlar otomatik olarak yayınlansın Sonuçları el ile yükle @@ -373,14 +373,14 @@ Yaklaşık coğrafi konum katılsın IP adresim katılsın Ülke kodu katılsın - Bu bilgi ölçümlerin hangi ülkeden (Türkiye için TR gibi) yapıldığını belirlemek için istenmektedir. Bu seçeneği devre dışı bırakmak istediğinize emin misiniz? + Bu bilgi ölçümlerin hangi ülkeden (Türkiye için TR gibi) yapıldığını belirlemek için istenmektedir. Bu seçeneği kapatmak istediğinize emin misiniz? Sonuçları yayınlayarak ağ müdahalelerinin daha görünür hale gelmesine yardım ederek OONI topluluğuna destek olursunuz.\n\nAğ bilgileri (Otonom Sistem Numarası gibi) İnternet hizmeti sağlayıcılarını belirlemek için gereklidir. - Sınama ayarları - Yukarıdaki sınama ayarları ile yapılandırdığınız şey (örneğin WhatsApp sınamasını devre dışı bırakmak), el ile yürütülen sınamaların yanında otomatik olarak yürütülen sınamalara da (otomatik sınama etkinleştirilmişse) uygulanır. + Sınama seçenekleri + Yukarıdaki sınama ayarları ile yapılandırdığınız şey (örneğin WhatsApp sınamasını kapatmak), el ile çalıştırılan sınamaların yanında otomatik olarak çalıştırılan sınamalara da (otomatik sınama açılmışsa) uygulanır. Uzun süreli sınama Arka planda uzun süreli sınama yapılsın mı? Gizlilik - Çökme raporları gönderilsin + Çökme bildirimleri gönderilsin Gelişmiş Koyu kip Hata ayıklama günlükleri @@ -388,7 +388,7 @@ Dil ayarı Dil seçin Araya her zaman etki alanı eklensin (domain fronting) - OONI yönetim bölümü vekil sunucusu + OONI arka uç vekil sunucusu Vekil sunucu Yok Psiphon @@ -405,34 +405,34 @@ OONI Probe uygulamasını kullanamıyor musunuz? Olası OONI Probe engellemesini aşmak için [Psiphon](https://psiphon.ca/) kullanmayı deneyin. Alternatif olarak özel bir vekil sunucu kullanabilirsiniz. Sınama süresi sınırlansın Sınama süresi - Sınanacak web sitesi kategorileri - %1$s kategori etkin + Sınanacak site kategorileri + %1$s kategori açık Düzenle Tümünü bırak Tümünü seç Kaydet Kaydedilmemiş değişiklikler - Etkinleştirilmiş kategorilerde bazı değişiklikler yaptınız. Bunları kaydetmek ister misiniz? + Açılmış kategorilerde bazı değişiklikler yaptınız. Bunları kaydetmek ister misiniz? Kaydet Yok say - Sınanacak web sitelerini seçin + Sınanacak siteleri seçin Adres Herhangi bir adres yazılmamış Çalıştır - Web sitesi ekle + Site ekle Kalıptan yükle - Sınanan web sitesi sayısı (tümü için 0 yazın) + Sınanan site sayısı (tümü için 0 yazın) WhatsApp sınaması Telegram sınaması Facebook Messenger sınaması Signal sınaması - HTTP geçersiz istek hattı sınamasını yürüt - HTTP üst bilgi değişikliği sınamasını yürüt - NDT hız testini yürüt + HTTP geçersiz istek satırı sınamasını çalıştır + HTTP üst bilgi değişikliği sınamasını çalıştır + NDT hız testini çalıştır NDT sunucusu otomatik olarak seçilsin NDT sunucusu adresi NDT sunucusu bağlantı noktası - DASH akış sınamasını yürüt + DASH akış sınamasını çalıştır DASH sunucusu otomatik olarak seçilsin DASH sunucusu DASH sunucusu bağlantı noktası @@ -440,7 +440,7 @@ Tor sınaması RiseupVPN sınaması VPN kullanılırken uyarılsın - Destek ekibine e-posta gönderin + Destek ekibine e-posta gönder Lütfen yaşadığınız sorunu anlatın: Lütfen uygulama hakkındaki bilgiler ve iOS sürümünü yazarak bugs@openobservatory.org adresine bir e-posta gönderin. E-posta adresimizi kopyalamak için aşağıdaki \"Panoya kopyala\" seçeneğine dokunun. Geçerli uygulama dili: %1$s @@ -459,15 +459,15 @@ %1$s adres Sınama adı Sınama ayrıntıları - Yürüt + Çalıştır Güncel değil Bu sınamayı yapmak için daha yeni bir OONI Probe sürümü kullanmalısınız. Güncelle Kapat Parametre geçersiz - OONI yürütme bağlantısı bozuk ya da uygulamanız eski. - Örnek web siteleri üzerinde rastgele bir sınama yapılacak. - Lütfen OONI yürütme bağlantısına dokunmadan önce sürmekte olan sınamanın tamamlanmasını bekleyin. + OONI Run bağlantısı bozuk ya da uygulamanız eski. + Örnek siteler üzerinde rastgele bir sınama yapılacak. + Lütfen OONI Run bağlantısına dokunmadan önce sürmekte olan sınamanın tamamlanmasını bekleyin. Uyuşturucu ve alkol Din Porno @@ -482,14 +482,14 @@ Kamu sağlığı Kumar Sansürü aşma araçları - Çevrimiçi arkadaşlık + Çevrim içi arkadaşlık Sosyal ağ LGBTQ+ Dosya paylaşımı Bilgisayar korsanlığı araçları İletişim araçları Ortam paylaşma - Site barındırma ve blog yayınlama + Site barındırma ve günlük yayınlama Arama motorları Oyun Kültür @@ -511,21 +511,21 @@ Ana haber siteleri, bölgesel haber siteleri ve bağımsız medya Doğum kontrolu, cinsel yolla bulaşan hastalıklar, tecavüz önleme ve kürtaj gibi cinsel sağlık konuları COVID-19, HIV/AIDS, Ebola gibi kamu sağlığı konuları - Çevrimiçi kumar ve bahis + Çevrim içi kumar ve bahis Anonimleşme, sansürü aşma ve şifreleme - Çevrimiçi arkadaşlık siteleri - Çevrimiçi sosyal ağ araç ve platformları + Çevrim içi arkadaşlık siteleri + Çevrim içi sosyal ağ araç ve platformları LGBTQ+ ile ilgili konuları tartışma toplulukları (pornografi dışında) Bulut tabanlı dosya depolama, torrentler ve P2P gibi dosya paylaşımı Bilgisayar güvenliği araçları ve haberler - VoIP, mesajlaşma ve eposta gibilerini içeren kişisel veya toplu iletişim araçları + VoIP, ileti gönderme ve internet e-postası gibi bireysel ve grup iletişimi araçları Görüntü, ses ve fotoğraf paylaşımı - Web sitesi barındırma ve diğer çevrimiçi yayın organları + Site barındırma ve diğer çevrim içi yayın organları Arama motorları ve mecralar - Çevrimiçi oyunlar ve oyun platformları (kumar siteleri dışında) + Çevrim içi oyunlar ve oyun platformları (kumar siteleri dışında) Tarih, edebiyat, müzik, film, hiciv ve mizah gibi eğlence konuları Genel ekonomik gelişim ve güç konuları - Ordu dahil, hükümetin yönettiği web siteleri + Ordu ile birlikte, hükümetin yönettiği siteler Ticari hizmet ve ürünler Denetim için kullanılan iyi niyetli ya da bilinçsiz içerik Birleşmiş Milletler gibi hükümetlerarası kuruluşlar diff --git a/app/src/main/res/values-vi/strings.xml b/app/src/main/res/values-vi/strings.xml new file mode 100644 index 000000000..d02b4fea4 --- /dev/null +++ b/app/src/main/res/values-vi/strings.xml @@ -0,0 +1,533 @@ + + + ONNI Probe + OONI Probe là gì? + Ứng dụng này để đo lường kiểm duyệt internet\n\nCác trang web và các ứng dụng mạng xã hội có bị chặn không? Kết nối internet của bạn có bị chậm bất thường không?\n\nHãy chạy ứng dụng OONI Probe để tìm hiểu! + Hiểu rồi + Chú ý! + Dữ liệu của OONI được công bố công khai, bao gồm thông tin về mạng của bạn. + Bất cứ ai giám sát hoạt động internet của bạn (ví dụ như chính phủ hay nhà mạng) sẽ biết bạn đang chạy OONI Probe. + Bạn có thể kiểm tra các trang web bị chặn (hoặc chọn bất cứ trang nào để kiểm tra) + Tôi hiểu + Tìm hiểu thêm + Đố Nhanh + Đúng + Sai + Quay lại + Tiếp tục + Câu hỏi 1/2 + Nếu có ai đó đang giám sát hoạt động internet của tôi, họ sẽ biết tôi đang chạy ứng dụng OONI Probe. + Cảnh báo + ONNI Probe không phải công cụ có tính bảo mật cá nhân. Bất cứ ai đang giám sát hoạt động internet của bạn sẽ biết được bạn đang chạy những phần mềm nào. + Câu hỏi 2/2 + Bất cứ khi nào tôi chạy ứng dụng OONI Probe, dữ liệu mạng mà tôi thu thập được sẽ được tự động công bố. + Cảnh báo + Để tăng tính minh bạch của việc kiểm duyệt internet, dữ liệu mạng của tất cả người dùng OONI Probe sẽ tự động được công bố (trừ khi họ tắt tính năng này khi cài đặt). + Kiểm tra tự động + Để đo lường sự kiểm duyệt internet hàng ngày, vui lòng bật chế độ kiểm tra tự động để OONI Probe có thể chạy các bài kiểm tra định kỳ\n\nĐừng lo, chúng tôi sẽ lưu ý đến việc sử dụng pin.\n\nBạn có thể tắt kiểm tra tự động ở phần cài đặt bất cứ lúc nào. + Báo cáo sự cố + Để cải thiện OONI Probe, chúng tôi muốn thu thập các báo cáo sự cố ẩn danh khi ứng dụng không hoạt động bình thường.\n\nBạn có muốn chọn gửi báo cáo sự cố cho nhóm phát triển OONI không? + + Không + Thiết đặt mặc định + Chúng tôi thu thập và công bố: + Mã quốc gia (ví dụ VN cho Việt Nam) + Thông tin mạng (bao gồm Số hệ thống tự trị - ASN) + Ngày Giờ kiểm tra + Chúng tôi luôn cố gắng để không công bố địa chỉ IP của bạn hay bất kỳ thông tin nào khác có thể nhận dạng cá nhân.\n\nTìm hiểu thêm thông qua [Chính sách dữ liệu của OONI] (https://ooni.org/about/data-policy/). + Khi nhấn \"OK\", bạn đồng ý chia sẻ báo cáo sự cố để giúp chúng tôi cải thiện OONI Probe. + Bắt đầu + Thay đổi mặc định + Bảng tóm tắt + Chạy + Không có + Chạy + Lần kiểm tra chót: + Ước lượng: + Chọn trang web + Đang chạy: + Ước lượng thời gian còn lại: + %1$s giây + Chuẩn bị kiểm tra + Tính toán ETA + Hiển thị Nhật ký + Đóng Nhật ký + Dừng kiểm tra + Đang hoàn tất các bài kiểm tra dở dang, vui lòng chờ đợi.... + Proxy đang sử dụng + Chạm vào thẻ để biết thêm chi tiết + ~%1$s + Kiểm tra việc chặn các trang web + Kiểm tra xem các trang web có bị chặn không bằng cách sử dụng [Kiểm tra Kết nối Web] của OONI (https://ooni.org/nettest/web-connectivity/).\n\nMỗi khi nhấn Chạy, bạn sẽ kiểm tra các trang web khác nhau từ danh sách [toàn cầu] của Citizen Lab (https://github.com/citizenlab/test-lists/blob/master/lists/global.csv) và danh sách [quốc gia cụ thể] (https://github.com/citizenlab/test-lists/tree/master/lists).\n\nĐể chọn trang bạn muốn kiểm tra, hãy nhấn vào nút Chọn trang web hoặc chọn các danh mục trang web ở phần cài đặt.\n\nKiểm tra này cho biết các trang web có bị chặn bởi các phương thức sửa DNS trái phép, chặn TCP/IP hay bởi một proxy HTTP minh bạch không.\n\nKết quả của bạn sẽ được công bố trên [OONI Explorer] (https://explorer.ooni.org/world/) và [OONI API] (https://api.ooni.io/). + Kiểm tra xem các trang web có bị chặn không bằng cách sử dụng [Kiểm tra Kết nối Web] của OONI (https://ooni.org/nettest/web-connectivity/).\n\nBạn sẽ kiểm tra các trang web khác nhau từ danh sách [toàn cầu] của Citizen Lab (https://github.com/citizenlab/test-lists/blob/master/lists/global.csv) và danh sách [quốc gia cụ thể] (https://github.com/citizenlab/test-lists/tree/master/lists).\n\nKiểm tra này cho biết các trang web có bị chặn bởi các phương thức sửa DNS trái phép, chặn TCP / IP hay bởi một proxy HTTP minh bạch hay không.\n\nKết quả kiểm tra của bạn sẽ được công bố trên [OONI Explorer] (https://explorer.ooni.org/) và [OONI API] (https://api.ooni.io/). + Kiểm tra tốc độ và hiệu suất mạng của bạn + Đo tốc độ và hiệu suất mạng của bạn bằng cách sử dụng kiểm tra [NDT] (https://ooni.org/nettest/ndt/).\n\nĐo hiệu suất phát trực tuyến video bằng bài kiểm tra [DASH] (https://ooni.org/nettest/dash/).\n\nCác bài kiểm tra này dùng dữ liệu tùy thuộc vào tốc độ mạng của bạn.\n\nKết quả kiểm tra của bạn sẽ được công bố trên [OONI Explorer] (https://explorer.ooni.org/world/) và [OONI API] (https://api.ooni.io/).\n\nTuyên bố giới hạn trách nhiệm: Những kiểm tra này dựa trên máy chủ của bên thứ ba. Do đó, chúng tôi không thể đảm bảo rằng địa chỉ IP của bạn sẽ không bị thu thập. + Bằng cách chạy các bài kiểm tra này, bạn sẽ:\n\n- Đo tốc độ và hiệu suất mạng của bạn (kiểm tra [NDT] (https://ooni.org/nettest/ndt/))\n- Đo hiệu suất phát trực tuyến video (kiểm tra [DASH] (https://ooni.org/nettest/dash/))\n- Kiểm tra sự hiện diện của [công nghệ middlebox] (https://ooni.org/support/glossary/#middlebox) trên mạng của bạn qua các kiểm tra ([HTTP Invalid Request Line] (https://ooni.org/nettest/http-invalid -request-line /) và [HTTP Header Field Manipulation] (https://ooni.org/nettest/http-header-field-manipulation/) )\n\nCác bài kiểm tra này sử dụng dữ liệu tùy thuộc vào tốc độ mạng của bạn.\n\nKết quả kiểm tra của bạn sẽ được công bố trên [OONI Explorer] (https://explorer.ooni.org/) và [OONI API] (https://api.ooni.io/).\n\n** Tuyên bố giới hạn trách nhiệm: ** Các bài kiểm tra [NDT] (https://ooni.org/nettest/ndt/) và [DASH] (https://ooni.org/nettest/dash/) được thực hiện trên các máy chủ của bên thứ ba được cung cấp bởi [Measurement Lab (M-Lab)] (https://www.measurementlab.net/). Nếu bạn chạy các bài kiểm tra này, M-Lab sẽ thu thập và công bố địa chỉ IP của bạn (cho mục đích nghiên cứu), cho dù cài đặt OONI Probe của bạn như thế nào. Tìm hiểu thêm về quản trị dữ liệu của M-Lab thông qua [tuyên bố về quyền riêng tư] (https://www.measurementlab.net/privacy/). + Phát hiện các middlebox trong mạng của bạn + Nhà mạng thường sử dụng các thiết bị mạng (middlebox) cho các mục đích mạng khác nhau (ví dụ như bộ nhớ đệm). Đôi khi những middlebox này được sử dụng để thực hiện kiểm duyệt và/ hoặc giám sát internet.\n\nTìm các middlebox trong mạng của bạn bằng cách kiểm tra [HTTP Invalid Request Line] của OONI (https://ooni.org/nettest/http-invalid-request-line/) và kiểm tra [HTTP Header Field Manipulation] (https://ooni.org/nettest/http-header-field-manipulation/) .\n\nKết quả kiểm tra của bạn sẽ được công bố trên [OONI Explorer] (https://explorer.ooni.org/world/) và [OONI API] (https://api.ooni.io/). + Kiểm tra việc chặn các ứng dụng tin nhắn nhanh + Kiểm tra [WhatsApp] (https://ooni.org/nettest/whatsapp/), [Facebook Messenger] (https://ooni.org/nettest/facebook-messenger/), [Telegram] (https://ooni .org/nettest/telegram/) và [Signal] (https://ooni.org/nettest/signal) có bị chặn không.\n\nKết quả kiểm tra của bạn sẽ được công bố trên [OONI Explorer] (https://explorer.ooni.org/world/) và [OONI API] (https://api.ooni.io/). + Kiểm tra việc chặn các công cụ vượt thoát kiểm duyệt + Kiểm tra [Psiphon] (https://ooni.org/nettest/psiphon/), [Tor] (https://ooni.org/nettest/tor/) hay [RiseupVPN] (https://ooni.org/nettest/roseupvpn/) có bị chặn không.\n\nKết quả kiểm tra của bạn sẽ được công bố trên [OONI Explorer] (https://explorer.ooni.org/) và [OONI API] (https://api.ooni.io/). + Chạy các bài kiểm tra thử nghiệm mới + Chạy các bài kiểm tra thử nghiệm mới sau đây do nhóm OONI phát triển:\n%1$s\n\nKết quả kiểm tra của bạn sẽ được công bố trên [OONI Explorer] (https://explorer.ooni.org/) và [OONI API] (https://api.ooni.io/). + Các bài kiểm tra sau đây chỉ được chạy khi thực hiện kiểm tra tự động: + Kiểm tra bị tắt + Gbit/giây + Mbit/giây + kbit/giây + mili giây + Không có + Không rõ + Kết quả kiểm tra + Kết quả kiểm tra + Các bài kiểm tra + Mạng + Lượng dữ liệu dùng + Sàng lọc Kiểm tra + Tất cả kiểm tra + Trang web + Các middlebox + Hiệu suất + Tin Nhắn Nhanh + Vượt thoát kiểm duyệt + Thử nghiệm + Chưa có bài kiểm tra nào được chạy. Hãy chạy thử một bài kiểm tra! + %1$s đã bị chặn + %1$s đã bị chặn + %1$s đã được kiểm tra + %1$s đã được kiểm tra + Tìm thấy + Không tìm thấy + Thất bại + %1$s đã bị chặn + %1$s đã bị chặn + %1$s có thể truy cập + %1$s có thể truy cập + %1$s đã bị chặn + %1$s đã bị chặn + %1$s có sẵn + %1$s có sẵn + Kết quả chưa đầy đủ + Lỗi + Lỗi trong quá trình đo lường + Kết quả không được tải lên + Ngày & Giờ + Mạng + Quốc gia + Lượng dữ liệu dùng + Tổng cộng thời gian chạy + WiFi + Dữ liệu di động + Không có internet + Thất bại + Đã kiểm tra + Đã kiểm tra + Đã bị chặn + Đã bị chặn + Trang web + Trang web + Có thể truy cập + Có thể truy cập + Video + Chất lượng + Tải lên + Tải về + Ping + Tìm thấy + Không tìm thấy + Thất bại + Đã kiểm tra + Đã kiểm tra + Đã bị chặn + Đã bị chặn + Có thể truy cập + Có thể truy cập + Ứng dụng + Các ứng dụng + Đã kiểm tra + Đã kiểm tra + Đã bị chặn + Đã bị chặn + Đang hoạt động + Đang hoạt động + Công cụ + Công cụ + Thời gian chạy + Phương pháp + Xem Nhật ký + Dữ liệu + Sao chép URL của Explorer + Chia sẻ URL của Explorer + Sao chép vào Bảng ghi tạm + Hiển thị trong OONI Explorer + Thất bại + Bạn có thể thử chạy lại bài kiểm tra này + Thử lại + Tìm hiểu cách hoạt động của bài kiểm tra này [tại đây] (%1$s). + Có thể truy cập + %1$s có thể truy cập được + Có lẻ đã bị chặn + %1$s có lẻ bị chặn bởi %2$s.\n\nLưu ý: Kết quả này có thể không trúng (hiện tượng dương tính giả). Tìm hiểu thêm [tại đây] (https://ooni.org/support/faq/#what-are-false-pososystem) + Vượt thoát kiểm duyệt + **Sửa DNS trái phép** + ** Chặn dựa trên TCP/IP ** + ** Chặn HTTP (trang chặn được gửi ra) ** + ** Chặn HTTP (yêu cầu HTTP không thành công) ** + Ứng dụng trên điện thoại + OK + Thất bại + Trang web WhatsApp + OK + Thất bại + Đăng ký + OK + Thất bại + Đang hoạt động + Bài kiểm tra này đã kết nối thành công với các điểm cuối, dịch vụ đăng ký và giao diện web của WhatsApp (web.whatsapp.com). + Có lẻ bị chặn + WhatsApp dường như bị chặn. + Ứng dụng trên điện thoại + OK + Thất bại + Trang web Telegram + OK + Thất bại + Đang hoạt động + Bài kiểm tra này đã kết nối thành công với các điểm cuối và giao diện web của Telegram (web.telegram.org). + Có lẻ bị chặn + Telegram dường như bị chặn + Các kết nối TCP + OK + Thất bại + Tra cứu DNS + OK + Thất bại + Đang hoạt động + Bài kiểm tra này đã kết nối thành công với các điểm cuối của Facebook và lý giải đến các địa chỉ IP của Facebook. + Có lẻ bị chặn + Facebook Messenger dường như bị chặn. + Có lẻ bị chặn + Signal dường như bị chặn. + Đang hoạt động + Bài kiểm tra này đã kết nối thành công với các điểm cuối của Signal. + Không phát hiện middlebox + Không phát hiện bất thường trên mạng khi giao tiếp với máy chủ của chúng tôi. + Sửa đổi mạng trái phép + Lưu lượng mạng đã bị thay đổi trái phép khi liên lạc với các máy chủ kiểm soát của chúng tôi.\n\nĐiều này nghĩa là có thể có một middlebox trong mạng của bạn, có thể nhằm kiểm duyệt và/hoặc giám sát. + Không phát hiện middlebox + Không phát hiện bất thường trên mạng khi giao tiếp với máy chủ của chúng tôi. + Sửa đổi mạng trái phép + Lưu lượng mạng đã bị thay đổi trái phép khi liên lạc với các máy chủ kiểm soát của chúng tôi.\n\nĐiều này nghĩa là có thể có một middlebox trong mạng của bạn, có thể nhằm để kiểm duyệt và/hoặc giám sát. + Bạn đã gửi + Bạn đã nhận + Tải lên + Tải về + Ping + Máy chủ + Tỷ lệ truyền lại + Xảy ra lỗi + Ping trung bình + Ước lượng Ping tối đa + MSS + Thời gian chờ + Bạn có thể phát trực tiếp lên đến %1$s mà không bị gián đoạn + Tốc độ bit trung bình + Hoãn phát lại + Có lẻ bị chặn + Đang hoạt động + [Psiphon](https://psiphon.ca/) dường như bị chặn. + Chúng tôi đã khởi động thành công kết nối Psiphon. Điều này có nghĩa là [Psiphon] (https://psiphon.ca/) sẽ hoạt động. + Thời gian Khởi động + %1$s giây + Có lẻ bị chặn + Đang hoạt động + [Tor](https://www.torproject.org/) dường như bị chặn. + Chúng tôi đã kết nối thành công với cầu Tor mặc định và/hoặc danh bạ chính thức của Tor. Điều này có nghĩa là [Tor] (https://www.torproject.org/) sẽ hoạt động. + Cầu chuyển tiếp mặc định + %1$s/%2$s OK + Danh bạ Chính thức + %1$s/%2$s OK + Tên + Địa chỉ + Loại + Kết nối + Giao thức bắt tay + Có lẻ bị chặn + Đang hoạt động + [RiseupVPN](https://riseup.net/vpn) dường như bị chặn. + Chúng tôi đã kết nối thành công với máy chủ khởi động của RiseupVPN và các cổng VPN. Điều này có nghĩa là [RiseupVPN] (https://riseup.net/vpn) sẽ hoạt động. + Máy chủ Khởi động + Các kết nối OpenVPN + Các kết nối bắc cầu + Đã chặn + %1$s đã bị chặn + %1$s đã bị chặn + OK + Đây là bài kiểm tra thử nghiệm. + Cung cấp + Cung cấp + OK + Hủy + Không, đừng hỏi lại + Xóa + Lỗi + Thử lại + Rất tốt + Không, cảm ơn + Không phải bây giờ + Cứ chạy + Tắt VPN + Luôn chạy + Không thể chạy bài kiểm tra. Vui lòng kiểm tra lại kết nối internet của bạn. + Không thể tải danh sách URL. Vui lòng thử lại. + Vui lòng đợi các bài kiểm tra đang chạy kết thúc trước khi bắt đầu một bài kiểm tra mới. + Cần cho phép thông báo. Vui lòng bật thông báo trong Thiết Đặt của điện thoại sau đó bật thông báo trong ứng dụng OONI Probe của bạn. + Vào phần Thiết Đặt + Màn hình bị khóa khi bài kiểm tra đang chạy + Bạn cần kết nối với Internet để tải về các dữ liệu thô về đo lường + Kết quả không được tải lên + Một số kết quả kiểm tra của bạn chưa được tải lên máy chủ OONI. Nếu bạn muốn đóng góp vào bộ dữ liệu của OONI, vui lòng tải chúng lên. + Tải lên + Đang tải lên %1$s ... + OONI Probe không thể chạy tự động nếu không tối ưu hóa pin. Bạn có muốn thử lại không? + Vui lòng tắt kết nối VPN của bạn. + Nếu bạn chạy OONI Probe mà vẫn bật VPN, kết quả kiểm tra có thể đến từ quốc gia khác. Vui lòng tắt kết nối VPN của bạn. + Một số đo lường đã được thực hiện qua VPN. + Nếu bạn tải lên các đo lường được thực hiện khi bật VPN, kết quả kiểm tra có thể đến từ quốc gia khác. + Tải lên thành công + Hiển thị nhật ký lỗi + Nhận thông tin cập nhật về kiểm duyệt internet + Bạn quan tâm đến việc chạy kiểm tra OONI Probe trong các tình huống kiểm duyệt khẩn cấp? Bật thông báo để nhận tin nhắn khi chúng tôi biết có kiểm duyệt internet ở gần bạn. + Để cải thiện độ chính xác của các bài kiểm tra, chúng tôi cần có quyền truy cập GPS. OONI sẽ chỉ thu thập thông tin khái quát về vị trí GPS của bạn. + Bạn có muốn xóa tất cả các kết quả kiểm tra không? + Bạn có muốn xóa bài kiểm tra này không? + Vui lòng cho phép chạy ít nhất một bài kiểm tra + Xin chỉ điền số vào khung này. + Chạy lại bài kiểm tra + Bài kiểm tra này không thành công. Chạy lại bài kiểm tra? + Bạn chuẩn bị kiểm tra lại %1$s trang web. + Chạy + URL của bạn sẽ không được lưu khi bạn rời khỏi màn hình này. Bạn có chắc chắn muốn rời khỏi màn hình này không? + Bật cách Tự Tải Lên ? + Cài đặt này cho phép bạn tự mình tải lên các kết quả đo lường chưa được công bố. + Bật mở + Không, cảm ơn + Tải lên thất bại + Chúng tôi không thể tải lên %1$s / %2$s kết quả đo lường. Nhật ký lỗi đã được chia sẻ với các nhà phát triển OONI. + Không tìm thấy tập tin nhật ký + Không tìm thấy URL hợp lệ + JSON trống + Bạn có muốn tạm dừng bài kiểm tra này không? + Việc này sẽ làm gián đoạn bài kiểm tra từ thời điểm này. + Bạn có muốn chạy tự động các bài kiểm tra này không? + Bằng việc bật kiểm tra tự động, bạn sẽ đóng góp thường xuyên các kết quả đo lường cho OONI. + Vui lòng cho phép ứng dụng chạy trên nền sau. + Nhắc lại tôi sau + Đã sao chép vào clipboard + Chưa được tải lên + Tải lên + Một số chưa được tải lên + Tải lên tất cả + Trang web + Tin Nhắn Nhanh + Các middlebox + Hiệu suất + Vượt thoát kiểm duyệt + Thử nghiệm + Kiểm tra HTTP Invalid Request Line + Kiểm tra HTTP Header Field Manipulation + Kiểm tra Kết Nối Web + Kiểm tra tốc độ NDT + Kiểm tra phát trực tuyến DASH + Kiểm tra WhatsApp + Kiểm tra Telegram + Kiểm tra Facebook Messenger + Kiểm tra Psiphon + Kiểm tra Tor + Kiểm tra RiseupVPN + Kiểm tra Signal + Thiết đặt + Thời lượng bạn chọn cho quá trình kiểm tra quá ngắn, + Giới thiệu về OONI + Đài Quan Sát Mở về Can Thiệp Mạng (OONI) là một dự án phần mềm miễn phí thuộc Dự án Tor nhằm mục đích tăng cường tính minh bạch của việc kiểm duyệt internet trên toàn thế giới.\n\nKể từ năm 2012, cộng đồng OONI toàn cầu đã mở rộng mạng lưới tại hơn 200 quốc gia. Một số các kết quả đo lường này là bằng chứng về sự kiểm duyệt internet. + Tìm hiểu thêm + Blog + Báo cáo + Chính sách Dữ liệu của OONI + Thông báo + Bật lên + Thông báo khi bài kiểm tra hoàn tất + Bài đăng mới + Kiểm tra tự động + Chạy kiểm tra tự động + Số lượng các bài kiểm tra tự động: %1$s. + Lần kiểm tra tự động gần đây nhất: %1$s. + Chỉ khi có WiFi + Chỉ khi đang sạc + Khi bật kiểm tra tự động, các bài kiểm tra OONI Probe sẽ tự động chạy nhiều lần mỗi ngày. Kết quả kiểm tra của bạn sẽ tự động được xuất bản trên OONI Explorer: https://explorer.ooni.org/\n\nQuan trọng: Nếu bạn bật VPN, OONI Probe sẽ không tự động chạy kiểm tra. Vui lòng tắt VPN của bạn để kiểm tra OONI Probe tự động. Tìm hiểu thêm: https://ooni.org/support/faq/#can-i-run-ooni-probe-over-a-vpn + Chia sẻ + Tự động công bố các kết quả + Tự Tải Lên Kết Quả + Bao gồm thông tin mạng + Bao gồm vị trí địa lý ước lượng + Bao gồm địa chỉ IP của tôi + Bao gồm Mã quốc gia + Thông tin này (ví dụ: VN là viết tắt của Việt Nam) cần có để xác định quốc gia mà các đo lường được thu thập. Bạn có chắc chắn muốn tắt tùy chọn này không? + Bằng việc công bố kết quả, bạn đang góp phần tăng cường tính minh bạch trong can thiệp mạng và hỗ trợ cộng đồng OONI.\n\nThông tin mạng (ví dụ Số hệ thống tự trị - ASN) là bắt buộc để xác định Nhà cung cấp mạng + Tùy chọn kiểm tra + Những gì bạn cấu hình thông qua thiết đặt kiểm tra ở trên (ví dụ: tắt kiểm tra WhatsApp) sẽ áp dụng cho các bài kiểm tra chạy theo cách thủ công, cũng như các bài kiểm tra chạy tự động (khi kiểm tra tự động được bật). + Bài kiểm tra dài + Chạy bài kiểm tra dài ở nền trước? + Riêng tư + Gửi báo cáo sự cố + Nâng cao + Màn hình tối + Nhật ký gỡ lỗi + Xem nhật kí gần đây + Thiết đặt Ngôn ngữ + Chọn Ngôn Ngữ + Luôn sử dụng domain fronting + Backend proxy của OONI + Proxy + Không có + Psiphon + Proxy tùy chỉnh + URL Proxy tùy chỉnh + Giao thức proxy tùy chỉnh + Kết nối + Tên máy chủ + Cổng + Thông tin xác thực (tùy chọn) + Tên đăng nhập + Mật khẩu + Sử dụng Psiphon thay cho proxy tùy chỉnh + Bạn không thể sử dụng OONI Probe? Hãy thử bật [Psiphon] (https://psiphon.ca/) để vượt qua kiểm duyệt lên OONI Probe. Hoặc, bạn có thể sử dụng proxy tùy chỉnh. + Giới hạn thời lượng kiểm tra + Thời lượng kiểm tra + Thể loại web để kiểm tra + %1$s thể loại đã bật mở + Chỉnh sửa + Bỏ chọn tất cả + Chọn Tất cả + Lưu + Thay đổi chưa được lưu + Bạn đã thực hiện một số thay đổi đối với các thể loại đã bật mở. Bạn có muốn lưu lại không? + Lưu + Hủy bỏ + Chọn các trang web để kiểm tra + URL + Không có URL nào được nhập + Chạy + Thêm trang web + Nạp vào từ biểu mẫu + Số lượng trang web được thử nghiệm (0 có nghĩa là tất cả) + Kiểm tra WhatsApp + Kiểm tra Telegram + Kiểm tra Facebook Messenger + Kiểm tra Signal + Chạy kiểm tra HTTP Invalid Request Line + Chạy kiểm tra HTTP Header Field Manipulation + Chạy kiểm tra tốc độ NDT + Lựa chọn máy chủ NDT tự động + Địa chỉ máy chủ NDT + Cổng máy chủ NDT + Chạy kiểm tra phát trực tuyến DASH + Tự động chọn máy chủ DASH + Máy chủ DASH + Cổng máy chủ DASH + Kiểm tra Psiphon + Kiểm tra Tor + Kiểm tra RiseupVPN + Cảnh báo khi VPN được sử dụng + Gửi email đến hỗ trợ + Vui lòng mô tả vấn đề bạn đang gặp phải: + Vui lòng gửi email đến bugs@openobservatory.org kèm theo thông tin về ứng dụng và phiên bản iOS. Nhấn vào \"Sao chép vào Bảng ghi tạm\" bên dưới để sao chép địa chỉ email của chúng tôi. + Ngôn ngữ hiện tại của ứng dụng là %1$s + Ngôn ngữ + Lượng lưu trữ sử dụng + Lượng lưu trữ đã dùng + Xóa + Xoá + Bạn chuẩn bị xóa tất cả các kết quả đo lường OONI khỏi thiết bị của mình. Nếu được tải lên, những kết quả này sẽ có trên [OONI Explorer] (https://explorer.ooni.org) + Đã chạy xong + Dừng kiểm tra + Thử đối xứng + Đang nạp... + Đã xảy ra lỗi bất ngờ. Vui lòng nạp lại trang này. + Bạn chuẩn bị chạy bài kiểm tra OONI Probe. + %1$s URL + Tên bài kiểm tra + Thông tin về bài kiểm tra + Chạy + Quá hạn + Bạn cần cài đặt phiên bản mới hơn của OONI Probe để chạy bài kiểm tra này. + Cập nhật + Đóng + Thông số không hợp lệ + Liên kết chạy OONI không đúng định dạng hoặc ứng dụng của bạn chưa được cập nhật. + Bạn sẽ kiểm tra một số trang web ngẫu nhiên. + Vui lòng đợi cho bài kiểm tra hoàn thành trước khi nhấn vào liên kết chạy OONI. + Ma túy và Rượu + Tôn giáo + Nội dung khiêu dâm + Trang phục khêu gợi + Phê bình chính trị + Các vấn đề về nhân quyền + Môi trường + Chủ nghĩa khủng bố và các phần tử quá khích + Phát ngôn thù hận + Truyền thông tin tức + Giáo dục giới tính + Y tế công cộng + Cờ bạc + Công cụ vượt thoát kiểm duyệt + Hẹn hò trực tuyến + Mạng xã hội + LGBTQ+ + Chia sẻ tập tin + Công cụ lấy cắp dữ liệu + Công cụ truyền thông + Chia sẻ truyền thông + Dịch vụ lưu trữ web và viết blog + Công cụ tìm kiếm + Game + Văn hóa + Kinh tế + Chính phủ + Thương mại điện tử + Nội dung kiểm soát + Tổ chức liên chính phủ + Nội dung khác + Sử dụng và mua bán ma túy và rượu + Các vấn đề tôn giáo, cả ủng hộ và phản biện + Nội dung khiêu dâm nhẹ và mạnh bạo + Trang phục khêu gợi và hình ảnh phụ nữ mặc quần áo hở hang + Quan điểm chính trị phê phán + Các vấn đề về nhân quyền + Thảo luận về các vấn đề môi trường + Chủ nghĩa khủng bố, các phần tử bạo lực hoặc phong trào ly khai + Phê phán các nhóm về chủng tộc, giới tính, xu hướng tính dục hoặc các đặc điểm khác + Các trang web tin tức lớn, các hãng tin tức khu vực và các phương tiện truyền thông độc lập + Các vấn đề sức khỏe tình dục bao gồm tránh thai, các bệnh lây qua đường tình dục, ngăn ngừa hiếp dâm và nạo phá thai + Các vấn đề về y tế công cộng, như COVID-19, HIV/AIDS, Ebola + Cờ bạc và cá độ trực truyến + Ẩn danh, vượt thoát kiểm duyệt và mã hóa + Các trang hẹn hò trực tuyến + Các nền tảng và công cụ mạng xã hội trực tuyến + Cộng đồng LGBTQ + thảo luận về các vấn đề liên quan (trừ nội dung khiêu dâm) + Chia sẻ tập tin bao gồm dữ liệu lưu trữ điện toán đám mây, torrent và P2P + Công cụ bảo mật máy tính và tin tức + Công cụ giao tiếp cá nhân và nhóm bao gồm VoIP, nhắn tin và webmail + Chia sẻ video, âm thanh và hình ảnh + Lưu trữ web, viết blog và xuất bản trực tuyến khác + Công cụ tìm kiếm và cổng thông tin + Trò chơi trực tuyến và các nền tảng trò chơi (không bao gồm các trang web cờ bạc) + Giải trí, bao gồm lịch sử, văn học, âm nhạc, phim ảnh, châm biếm và hài + Tình hình chung về phát triển kinh tế và nghèo đói + Các trang web do chính phủ quản lý, bao gồm quân đội + Các sản phẩm và dịch vụ thương mại + Nội dung ôn hoà hoặc vô hại được sử dụng để kiểm soát + Các tổ chức liên chính phủ bao gồm Liên Hợp quốc + Các trang web chưa được phân loại + diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml index 0a100dd60..43dfbe351 100644 --- a/app/src/main/res/values-zh-rCN/strings.xml +++ b/app/src/main/res/values-zh-rCN/strings.xml @@ -58,7 +58,7 @@ 触击选项卡以查看详情 ~%1$ss 测试网站是否被屏蔽 - 使用 OONI 的[网络连接测试](https://ooni.org/nettest/web-connectivity/)检查网站是否被屏蔽。\n\n每次你点击 \"运行\",你都会从公民实验室的[全球](https://github.com/citizenlab/test-lists/blob/master/lists/global.csv)和[特定国家](https://github.com/citizenlab/test-lists/tree/master/lists)测试列表中测试不同的网站。\n\n要测试您选择的网站,请点击选择网站按钮或通过此卡的设置选择网站类别。\n\n该测试测量网站是否被 DNS 篡改、TCP/IP 封锁或透明的 HTTP 代理所屏蔽。\n\n您的结果将被发布在 [OONI Explorer](https://explorer.ooni.org/world/)和[OONI API](https://api.ooni.io/)。 + 使用 OONI 的[网页连通性测试](https://ooni.org/nettest/web-connectivity/)检查网站是否被屏蔽。\n\n每次您点击“运行”时,都会测试公民实验室的[全球](https://github.com/citizenlab/test-lists/blob/master/lists/global.csv)和[特定国家](https://github.com/citizenlab/test-lists/tree/master/lists)列表中的一些网站。\n\n如要测试您自选的网站,点击“选择网站”按钮或通过本卡片的设置选择网站类别。\n\n此测试测量网站是否遭受 DNS 篡改、TCP/IP 封锁或透明 HTTP 代理的屏蔽。\n\n您的测试结果会被发布在 [OONI Explorer](https://explorer.ooni.org/world/) 和 [OONI API](https://api.ooni.io/)。 使用 OONI 的[网络连接测试](https://ooni.org/nettest/web-connectivity/)检查网站是否被屏蔽。\n\n你将测试公民实验室的[全球](https://github.com/citizenlab/test-lists/blob/master/lists/global.csv)和[特定国家](https://github.com/citizenlab/test-lists/tree/master/lists)测试列表中的网站。\n\n这项测试测量网站是否被 DNS 篡改、TCP/IP 封锁或透明的 HTTP 代理所屏蔽。\n\n你的结果将被发布在 [OONI Explorer](https://explorer.ooni.org/)和[OONI API](https://api.ooni.io/)。 测试您的网速和性能 使用 [NDT](https://ooni.org/nettest/ndt/) 测试测量你的网络速度和性能。\n\n使用 [DASH](https://ooni.org/nettest/dash/) 测试测量视频流性能。\n\n这些测试消耗的数据取决于你的网络速度。\n\n您的结果将被发布在 [OONI Explorer](https://explorer.ooni.org/world/)和[OONI API](https://api.ooni.io/)。\n\n免责声明:这些测试依赖于第三方服务器。因此,我们不能保证你的 IP 地址不会被收集。 @@ -306,7 +306,7 @@ 此字段只能输入数字。 重新运行测试 此测试已失败。重新运行此测试? - 你正准备重新测试 %1$s 家网站。 + 您即将重新测试 %1$s 个网站。 运行 离开此屏幕将不会保存您的 URL。确定离开吗? 启用手动上传? @@ -337,7 +337,7 @@ 试验性 HTTP Invalid Request Line 测试 HTTP Header Field Manipulation 测试 - Web Connectivity 测试 + 网页连通性测试 NDT 网速测试 DASH 流测试 WhatsApp 测试 @@ -496,7 +496,7 @@ 经济 政府 电子商务 - 控制内容 + 质控内容 政府间组织 其他内容 毒品和酒精的使用和销售 @@ -527,7 +527,7 @@ 一般性经济发展与贫困 政府运营的网站,包括军方 商业服务和产品 - 用于控制的良性或无害内容 + 用于品质控制的良性或无害内容 政府间组织,例如联合国 尚未分类的网站 diff --git a/app/src/main/res/values/untraslatable.xml b/app/src/main/res/values/untraslatable.xml index 1201a1629..f3490ad0d 100644 --- a/app/src/main/res/values/untraslatable.xml +++ b/app/src/main/res/values/untraslatable.xml @@ -19,7 +19,9 @@ proxy_protocol proxy_hostname proxy_port - SOCKS5 + socks5 + http + https middle_boxes performance @@ -282,6 +284,8 @@ Kiswahili ไทย Türkçe + Tiếng Việt + မြန်မာ auto @@ -308,6 +312,13 @@ sw th tr + vi + my + + + @string/SOCKS5 + @string/HTTP + @string/HTTPS org.openobservatory.ooniprobe.activity.InfoActivity org.openobservatory.ooniprobe.activity.ProxyActivity diff --git a/app/src/main/res/xml/preferences_global.xml b/app/src/main/res/xml/preferences_global.xml index 8b93cd241..c973e7ed1 100644 --- a/app/src/main/res/xml/preferences_global.xml +++ b/app/src/main/res/xml/preferences_global.xml @@ -348,7 +348,9 @@ android:icon="@drawable/proxy" android:key="@string/ooni_backend_proxy" android:title="@string/Settings_Proxy_Label"> - + - + - + diff --git a/app/src/test/java/org/openobservatory/ooniprobe/domain/GenerateAutoRunServiceSuiteTest.java b/app/src/test/java/org/openobservatory/ooniprobe/domain/GenerateAutoRunServiceSuiteTest.java index f7c53c482..b220e7234 100644 --- a/app/src/test/java/org/openobservatory/ooniprobe/domain/GenerateAutoRunServiceSuiteTest.java +++ b/app/src/test/java/org/openobservatory/ooniprobe/domain/GenerateAutoRunServiceSuiteTest.java @@ -31,10 +31,7 @@ public class GenerateAutoRunServiceSuiteTest extends RobolectricAbstractTest { // Mocks - OONICheckInInfoWebConnectivity webConnectivityMock = mock(OONICheckInInfoWebConnectivity.class); PreferenceManager preferenceManagerMock = mock(PreferenceManager.class); - OONICheckInConfig ooniCheckConfigMock = mock(OONICheckInConfig.class); - OONICheckInResults ooniResultsMock = mock(OONICheckInResults.class); OONISession ooniSessionMock = mock(OONISession.class); // Engine && UseCase @@ -52,73 +49,19 @@ public void setUp() { } @Test - public void shouldNotStartTest() { + public void generateSuite() { // Act - AbstractSuite suite = generateSuite.generate(ooniCheckConfigMock); - - // Assert - Assert.assertNull(suite); - } - - @Test - public void generateSuite() throws Exception { - // Arrange - ArrayList suiteUrls = getTestUrls(); - - when(preferenceManagerMock.getEnabledCategoryArr()).thenReturn(new ArrayList() { - { - add("ALDR"); - add("REL"); - add("PORN"); - add("PROV"); - add("POLR"); - add("HUMR"); - add("ENV"); - } - }); - - when(webConnectivityMock.getUrls()).thenReturn(suiteUrls); - when(ooniResultsMock.getWebConnectivity()).thenReturn(webConnectivityMock); - when(ooniSessionMock.checkIn(any(), any())).thenReturn(ooniResultsMock); - - // Act - AbstractSuite suite = generateSuite.generate(ooniCheckConfigMock); + AbstractSuite suite = generateSuite.generate(); // Assert Assert.assertNotNull(suite); - verify(preferenceManagerMock, times(1)).updateAutorunDate(); - verify(preferenceManagerMock, times(1)).incrementAutorun(); Assert.assertEquals(1, suite.getTestList(preferenceManagerMock).length); AbstractTest webTest = suite.getTestList(preferenceManagerMock)[0]; Assert.assertEquals("web_connectivity", webTest.getName()); - Assert.assertEquals(suiteUrls.size(), webTest.getInputs().size()); - - for (int i = 0; i < webTest.getInputs().size(); i++) { - Assert.assertEquals( webTest.getInputs().get(i), suiteUrls.get(i).getUrl()); - } - } - - private ArrayList getTestUrls() { - Faker faker = new Faker(); - - OONIURLInfo url1 = mock(OONIURLInfo.class); - when(url1.getUrl()).thenReturn(faker.internet.url()); - - OONIURLInfo url2 = mock(OONIURLInfo.class); - when(url1.getUrl()).thenReturn(faker.internet.url()); - - OONIURLInfo url3 = mock(OONIURLInfo.class); - when(url1.getUrl()).thenReturn(faker.internet.url()); + Assert.assertNull(webTest.getInputs()); - return new ArrayList(){ - { - add(url1); - add(url2); - add(url3); - } - }; } } \ No newline at end of file diff --git a/app/src/test/java/org/openobservatory/ooniprobe/domain/GenerateTestServiceSuiteAutoRun.java b/app/src/test/java/org/openobservatory/ooniprobe/domain/GenerateTestServiceSuiteAutoRun.java deleted file mode 100644 index 705fe657e..000000000 --- a/app/src/test/java/org/openobservatory/ooniprobe/domain/GenerateTestServiceSuiteAutoRun.java +++ /dev/null @@ -1,120 +0,0 @@ -package org.openobservatory.ooniprobe.domain; - -import androidx.test.filters.SmallTest; - -import org.junit.Assert; -import org.junit.Test; -import org.openobservatory.engine.OONICheckInConfig; -import org.openobservatory.engine.OONICheckInResults; -import org.openobservatory.engine.OONICheckInResults.OONICheckInInfoWebConnectivity; -import org.openobservatory.engine.OONISession; -import org.openobservatory.engine.OONIURLInfo; -import org.openobservatory.ooniprobe.RobolectricAbstractTest; -import org.openobservatory.ooniprobe.common.PreferenceManager; -import org.openobservatory.ooniprobe.engine.TestEngineInterface; -import org.openobservatory.ooniprobe.test.EngineInterface; -import org.openobservatory.ooniprobe.test.EngineProvider; -import org.openobservatory.ooniprobe.test.suite.AbstractSuite; -import org.openobservatory.ooniprobe.test.test.AbstractTest; - -import java.util.ArrayList; - -import io.bloco.faker.Faker; - -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - -@SmallTest -public class GenerateTestServiceSuiteAutoRun extends RobolectricAbstractTest { - - // Mocks - OONICheckInInfoWebConnectivity webConnectivityMock = mock(OONICheckInInfoWebConnectivity.class); - PreferenceManager preferenceManagerMock = mock(PreferenceManager.class); - OONICheckInConfig ooniCheckConfigMock = mock(OONICheckInConfig.class); - OONICheckInResults ooniResultsMock = mock(OONICheckInResults.class); - OONISession ooniSessionMock = mock(OONISession.class); - - // Engine && UseCase - EngineInterface mockedEngine = new TestEngineInterface(ooniSessionMock); - GenerateAutoRunServiceSuite generateSuite; - - @Override - public void setUp() { - super.setUp(); - generateSuite = new GenerateAutoRunServiceSuite(a, preferenceManagerMock); - EngineProvider.engineInterface = mockedEngine; - - when(preferenceManagerMock.testWifiOnly()).thenReturn(true); - when(preferenceManagerMock.testChargingOnly()).thenReturn(true); - } - - @Test - public void shouldNotStartTest() { - // Act - AbstractSuite suite = generateSuite.generate(ooniCheckConfigMock); - - // Assert - Assert.assertNull(suite); - } - - @Test - public void generateSuite() throws Exception { - // Arrange - ArrayList suiteUrls = getTestUrls(); - - when(preferenceManagerMock.getEnabledCategoryArr()).thenReturn(new ArrayList() { - { - add("ALDR"); - add("REL"); - add("PORN"); - add("PROV"); - add("POLR"); - add("HUMR"); - add("ENV"); - } - }); - - when(webConnectivityMock.getUrls()).thenReturn(suiteUrls); - when(ooniResultsMock.getWebConnectivity()).thenReturn(webConnectivityMock); - when(ooniSessionMock.checkIn(any(), any())).thenReturn(ooniResultsMock); - - // Act - AbstractSuite suite = generateSuite.generate(ooniCheckConfigMock); - - // Assert - Assert.assertNotNull(suite); - Assert.assertEquals(1, suite.getTestList(preferenceManagerMock).length); - - AbstractTest webTest = suite.getTestList(preferenceManagerMock)[0]; - - Assert.assertEquals("web_connectivity", webTest.getName()); - Assert.assertEquals(suiteUrls.size(), webTest.getInputs().size()); - - for (int i = 0; i < webTest.getInputs().size(); i++) { - Assert.assertEquals(webTest.getInputs().get(i), suiteUrls.get(i).getUrl()); - } - } - - private ArrayList getTestUrls() { - Faker faker = new Faker(); - - OONIURLInfo url1 = mock(OONIURLInfo.class); - when(url1.getUrl()).thenReturn(faker.internet.url()); - - OONIURLInfo url2 = mock(OONIURLInfo.class); - when(url1.getUrl()).thenReturn(faker.internet.url()); - - OONIURLInfo url3 = mock(OONIURLInfo.class); - when(url1.getUrl()).thenReturn(faker.internet.url()); - - return new ArrayList() { - { - add(url1); - add(url2); - add(url3); - } - }; - } - -} \ No newline at end of file diff --git a/app/src/test/java/org/openobservatory/ooniprobe/test/TestAsyncTaskTest.java b/app/src/test/java/org/openobservatory/ooniprobe/test/TestAsyncTaskTest.java index 56816f49e..0e8c69813 100644 --- a/app/src/test/java/org/openobservatory/ooniprobe/test/TestAsyncTaskTest.java +++ b/app/src/test/java/org/openobservatory/ooniprobe/test/TestAsyncTaskTest.java @@ -161,7 +161,7 @@ public void runTest_withProgress() { AbstractTest test = mock(AbstractTest.class); when(mockedSuite.getTestList(any())).thenReturn(new AbstractTest[]{test}); - TestAsyncTask task = new TestAsyncTask(a, suiteList, false); + TestAsyncTask task = new TestAsyncTask(a, suiteList, false,false); // Act task.execute(); @@ -189,14 +189,14 @@ public void runTest_withError() { when(mockedSuite.getTestList(any())).thenReturn(new AbstractTest[]{test}); doThrow(new RuntimeException("")).when(test).run(any(), any(), any(), any(), any(), anyInt(), any()); - TestAsyncTask task = new TestAsyncTask(a, suiteList,false); + TestAsyncTask task = new TestAsyncTask(a, suiteList,false,false); // Act task.execute(); idleTaskUntilFinished(task); // Assert - verify(runService).stopSelf(); + verify(runService).stopSelf(); } @Test @@ -209,7 +209,7 @@ public void runTest_interrupt() { when(mockedSuite.getTestList(any())).thenReturn(new AbstractTest[]{test}); when(test.canInterrupt()).thenReturn(true); - TestAsyncTask task = new TestAsyncTask(a, suiteList, false); + TestAsyncTask task = new TestAsyncTask(a, suiteList, false,false); // Act task.execute(); diff --git a/app/src/test/java/org/openobservatory/ooniprobe/test/suite/CircumventionSuiteTest.java b/app/src/test/java/org/openobservatory/ooniprobe/test/suite/CircumventionSuiteTest.java index f2a5a2841..44271c373 100644 --- a/app/src/test/java/org/openobservatory/ooniprobe/test/suite/CircumventionSuiteTest.java +++ b/app/src/test/java/org/openobservatory/ooniprobe/test/suite/CircumventionSuiteTest.java @@ -4,7 +4,6 @@ import org.openobservatory.ooniprobe.common.PreferenceManager; import org.openobservatory.ooniprobe.test.test.AbstractTest; import org.openobservatory.ooniprobe.test.test.Psiphon; -import org.openobservatory.ooniprobe.test.test.RiseupVPN; import org.openobservatory.ooniprobe.test.test.Tor; import java.util.Arrays; @@ -24,7 +23,6 @@ public class CircumventionSuiteTest { public void getTestList_empty() { when(pm.isTestPsiphon()).thenReturn(false); when(pm.isTestTor()).thenReturn(false); - when(pm.isTestRiseupVPN()).thenReturn(false); AbstractTest[] tests = suite.getTestList(pm); @@ -39,11 +37,9 @@ public void getTestList_full() { List tests = Arrays.asList(suite.getTestList(pm)); - // Psiphon and Tor. Riseup VPN has been temporarily disabled. assertEquals(2, tests.size()); assertTrue(findTestClass(tests, Psiphon.class)); assertTrue(findTestClass(tests, Tor.class)); -// assertTrue(findTestClass(tests, RiseupVPN.class)); } private boolean findTestClass(List tests, Class klass) { diff --git a/app/src/test/java/org/openobservatory/ooniprobe/test/suite/ExperimentalSuiteTest.java b/app/src/test/java/org/openobservatory/ooniprobe/test/suite/ExperimentalSuiteTest.java index 3f4daac7c..c760f4d55 100644 --- a/app/src/test/java/org/openobservatory/ooniprobe/test/suite/ExperimentalSuiteTest.java +++ b/app/src/test/java/org/openobservatory/ooniprobe/test/suite/ExperimentalSuiteTest.java @@ -34,11 +34,15 @@ public void getTestList_experimental_on() { List tests = Arrays.asList(suite.getTestList(pm)); - assertEquals(2, tests.size()); + assertEquals(4, tests.size()); assertEquals(Experimental.class, tests.get(0).getClass()); assertEquals(Experimental.class, tests.get(1).getClass()); + assertEquals(Experimental.class, tests.get(2).getClass()); + assertEquals(Experimental.class, tests.get(3).getClass()); assertEquals("stunreachability", tests.get(0).getName()); assertEquals("dnscheck", tests.get(1).getName()); + assertEquals("riseupvpn", tests.get(2).getName()); + assertEquals("echcheck", tests.get(3).getName()); } @Test @@ -48,15 +52,19 @@ public void getTestList_experimental_on_autorun_on() { List tests = Arrays.asList(autoRunSuite.getTestList(pm)); - assertEquals(4, tests.size()); + assertEquals(6, tests.size()); assertEquals(Experimental.class, tests.get(0).getClass()); assertEquals(Experimental.class, tests.get(1).getClass()); assertEquals(Experimental.class, tests.get(2).getClass()); assertEquals(Experimental.class, tests.get(3).getClass()); + assertEquals(Experimental.class, tests.get(4).getClass()); + assertEquals(Experimental.class, tests.get(5).getClass()); - assertEquals("torsf", tests.get(0).getName()); - assertEquals("vanilla_tor", tests.get(1).getName()); - assertEquals("stunreachability", tests.get(2).getName()); - assertEquals("dnscheck", tests.get(3).getName()); + assertEquals("stunreachability", tests.get(0).getName()); + assertEquals("dnscheck", tests.get(1).getName()); + assertEquals("riseupvpn", tests.get(2).getName()); + assertEquals("echcheck", tests.get(3).getName()); + assertEquals("torsf", tests.get(4).getName()); + assertEquals("vanilla_tor", tests.get(5).getName()); } } diff --git a/applogger/build.gradle b/applogger/build.gradle index 2726a7647..424233927 100644 --- a/applogger/build.gradle +++ b/applogger/build.gradle @@ -4,11 +4,11 @@ plugins { } android { - compileSdk 31 + compileSdk libs.versions.compileSdk.get().toInteger() defaultConfig { - minSdk 16 - targetSdk 31 + minSdk libs.versions.minSdk.get().toInteger() + targetSdk libs.versions.targetSdk.get().toInteger() testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" consumerProguardFiles "consumer-rules.pro" @@ -32,5 +32,5 @@ android { } dependencies { - testImplementation 'junit:junit:+' + testImplementation libs.junit4 } diff --git a/build.gradle b/build.gradle index 1fcb28081..7a5b2861b 100644 --- a/build.gradle +++ b/build.gradle @@ -6,9 +6,9 @@ buildscript { maven { url "https://jitpack.io" } } dependencies { - classpath 'com.android.tools.build:gradle:7.4.1' - classpath 'com.google.gms:google-services:4.3.13' - classpath 'org.jetbrains.kotlin:kotlin-gradle-plugin:1.6.21' + classpath libs.android.gradlePlugin + classpath libs.gms.googleServices + classpath libs.kotlin.gradlePlugin } } diff --git a/engine/build.gradle b/engine/build.gradle index 93facfd52..a64cced29 100644 --- a/engine/build.gradle +++ b/engine/build.gradle @@ -1,12 +1,13 @@ -apply plugin: 'com.android.library' +plugins { + id 'com.android.library' +} android { - compileSdkVersion 30 - buildToolsVersion "30.0.3" + compileSdk libs.versions.compileSdk.get().toInteger() defaultConfig { - minSdkVersion 19 - targetSdkVersion 30 + minSdk libs.versions.minSdk.get().toInteger() + targetSdk libs.versions.targetSdk.get().toInteger() } compileOptions { @@ -32,8 +33,8 @@ android { dependencies { // For the stable and dev app flavours we're using the library // build published at Maven Central. - stableImplementation 'org.ooni:oonimkall:2023.07.18-162729' - devImplementation 'org.ooni:oonimkall:2023.07.18-162729' + stableImplementation libs.ooni.oonimkall + devImplementation libs.ooni.oonimkall // For the experimental flavour, you need to compile your own // oonimkall.aar and put it into the ../engine-experimental dir diff --git a/gradle.properties b/gradle.properties index 8de505811..a8c570082 100644 --- a/gradle.properties +++ b/gradle.properties @@ -6,7 +6,10 @@ # http://www.gradle.org/docs/current/userguide/build_environment.html # Specifies the JVM arguments used for the daemon process. # The setting is particularly useful for tweaking memory settings. +android.defaults.buildfeatures.buildconfig=true android.enableJetifier=true +android.nonFinalResIds=false +android.nonTransitiveRClass=false android.useAndroidX=true org.gradle.jvmargs=-Xmx1536m # When configured, Gradle will run in incubating parallel mode. diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml new file mode 100644 index 000000000..daf005ea9 --- /dev/null +++ b/gradle/libs.versions.toml @@ -0,0 +1,118 @@ +[versions] +androidGradlePlugin = "8.1.2" +barista = "3.9.0" +countlySdk = "23.6.0" +faker = "1.2.8" +mockitoCore = "5.3.1" +mockitoInline = "4.6.1" +pagingRuntimeKtx = "3.2.1" +robolectric = "4.10.3" +fastlaneScreengrab = "2.0.0" +sentryAndroid = "6.3.0" +xanscaleLocalhostToolkit = "19.05.01" +commonsIo = "2.6" +jacoco = "0.8.7" +kotlin = "1.8.0" + +# Android X +androidxCore = "1.5.0" +androidxRunner = "1.5.2" +androidxAppCompat = "1.6.1" +androidxConstraintlayout = "2.1.4" +androidxLifecycleProcess = "2.5.1" +androidxPreference = "1.2.0" +androidxLocalbroadcastmanager = "1.1.0" +androidxLegacySupportV4 = "1.0.0" +androidxJunit = "1.1.5" +androidxEspressoCore = "3.5.1" + +# Google +googleGson = "2.8.9" +googleGuava = "30.1.1-android" +googleMaterial = "1.6.1" +googleDagger = "2.45" +googleFirebaseBon = "26.3.0" +googlePlaycore = "1.10.3" + +# OONI +compileSdk = "34" +lottie = "3.0.7" +markwon = "4.6.2" +shapeofview = "1.3.2" +targetSdk = "33" +minSdk = "21" +oonimkall = "2023.11.21-145557" + +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" + +[libraries] +# Dependencies of the included build-logic +android-gradlePlugin = { group = "com.android.tools.build", name = "gradle", version.ref = "androidGradlePlugin" } +androidx-paging-runtime-ktx = { module = "androidx.paging:paging-runtime-ktx", version.ref = "pagingRuntimeKtx" } +barista = { module = "com.schibsted.spain:barista", version.ref = "barista" } +countly-sdk = { module = "ly.count.android:sdk", version.ref = "countlySdk" } +faker = { module = "com.github.blocoio:faker", version.ref = "faker" } +robolectric = { module = "org.robolectric:robolectric", version.ref = "robolectric" } +fastlane-screengrab = { module = "tools.fastlane:screengrab", version.ref = "fastlaneScreengrab" } +sentry-android = { module = "io.sentry:sentry-android", version.ref = "sentryAndroid" } +xanscale-localhost-toolkit = { module = "com.github.xanscale.LocalhostToolkit:app", version.ref = "xanscaleLocalhostToolkit" } +commons-io = { module = "commons-io:commons-io", version.ref = "commonsIo" } +kotlin-gradlePlugin = { group = "org.jetbrains.kotlin", name = "kotlin-gradle-plugin", version.ref = "kotlin" } + +mockito-core = { module = "org.mockito:mockito-core", version.ref = "mockitoCore" } +mockito-inline = { module = "org.mockito:mockito-inline", version.ref = "mockitoInline" } + +lottie = { module = "com.airbnb.android:lottie", version.ref = "lottie" } +markwon-core = { module = "io.noties.markwon:core", version.ref = "markwon" } +retrofit-converter-gson = { module = "com.squareup.retrofit2:converter-gson", version.ref = "retrofitCore" } +retrofit-logging-interceptor = { module = "com.squareup.okhttp3:logging-interceptor", version.ref = "retrofitLoggingInterceptor" } +retrofit-lib = { module = "com.squareup.retrofit2:retrofit", version.ref = "retrofitCore" } + +dbflow-lib = { module = "com.github.Raizlabs.DBFlow:dbflow", version.ref = "dbflow" } +dbflow-core = { module = "com.github.Raizlabs.DBFlow:dbflow-core", version.ref = "dbflow" } +dbflow-processor = { module = "com.github.Raizlabs.DBFlow:dbflow-processor", version.ref = "dbflow" } + +androidx-core = { module = "androidx.test:core", version.ref = "androidxCore" } +androidx-rules = { module = "androidx.test:rules", version.ref = "androidxCore" } +androidx-runner = { module = "androidx.test:runner", version.ref = "androidxRunner" } +androidx-appcompat = { group = "androidx.appcompat", name = "appcompat", version.ref = "androidxAppCompat" } +androidx-constraintlayout = { module = "androidx.constraintlayout:constraintlayout", version.ref = "androidxConstraintlayout" } +androidx-legacy-support-v4 = { module = "androidx.legacy:legacy-support-v4", version.ref = "androidxLegacySupportV4" } +androidx-lifecycle-process = { module = "androidx.lifecycle:lifecycle-process", version.ref = "androidxLifecycleProcess" } +androidx-localbroadcastmanager = { module = "androidx.localbroadcastmanager:localbroadcastmanager", version.ref = "androidxLocalbroadcastmanager" } +androidx-preference = { module = "androidx.preference:preference", version.ref = "androidxPreference" } +androidx-junit = { module = "androidx.test.ext:junit", version.ref = "androidxJunit" } +androidx-espresso-contrib = { module = "androidx.test.espresso:espresso-contrib", version.ref = "androidxEspressoCore" } +androidx-espresso-core = { module = "androidx.test.espresso:espresso-core", version.ref = "androidxEspressoCore" } +androidx-espresso-intents = { module = "androidx.test.espresso:espresso-intents", version.ref = "androidxEspressoCore" } + +google-play-core = { module = "com.google.android.play:core", version.ref = "googlePlaycore" } +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-firebase-messaging = { module = "com.google.firebase:firebase-messaging"} +google-firebase-bom = { module = "com.google.firebase:firebase-bom", version.ref = "googleFirebaseBon" } + +junit4 = { module = "junit:junit", version.ref = "junit" } +gms-googleServices = { module = "com.google.gms:google-services", version.ref = "gms-googleServices" } +ooni-oonimkall = { module = "org.ooni:oonimkall", version.ref = "oonimkall" } +shapeofview = { module = "com.github.florent37:shapeofview", version.ref = "shapeofview" } + +[plugins] +android-application = { id = "com.android.application", version.ref = "androidGradlePlugin" } +android-library = { id = "com.android.library", version.ref = "androidGradlePlugin" } +android-test = { id = "com.android.test", version.ref = "androidGradlePlugin" } + +kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" } + +gms-googleServices = { id = "com.google.gms.google-services", version.ref = "gms-googleServices"} diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 2ec77e51a..3a0290794 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-7.5-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.0-all.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/settings.gradle b/settings.gradle index 8a8583d06..ee1a417a5 100644 --- a/settings.gradle +++ b/settings.gradle @@ -2,3 +2,4 @@ include ':app' include ':engine' include ':engine-experimental' include ':applogger' +include ':shared-test' diff --git a/shared-test/.gitignore b/shared-test/.gitignore new file mode 100644 index 000000000..42afabfd2 --- /dev/null +++ b/shared-test/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/shared-test/build.gradle b/shared-test/build.gradle new file mode 100644 index 000000000..34d70a978 --- /dev/null +++ b/shared-test/build.gradle @@ -0,0 +1,72 @@ +plugins { + id 'com.android.library' + id 'kotlin-android' + id 'kotlin-kapt' +} + +android { + namespace 'org.openobservatory.ooniprobe.shared.test' + compileSdk libs.versions.compileSdk.get().toInteger() + + defaultConfig { + minSdk libs.versions.minSdk.get().toInteger() + } + compileOptions { + sourceCompatibility JavaVersion.VERSION_17 + targetCompatibility JavaVersion.VERSION_17 + } + kotlinOptions { + jvmTarget = JavaVersion.VERSION_17 + } + flavorDimensions = ['testing', 'license'] + productFlavors { + stable { + dimension 'testing' + } + experimental { + dimension 'testing' + } + dev { + dimension 'testing' + } + + fdroid { + dimension 'license' + } + full { + dimension 'license' + } + } +} + +dependencies { + implementation project(":engine") + implementation project(":app") + + // Dependency Injection (https://dagger.dev/) + implementation libs.google.dagger + kapt libs.google.dagger.compiler + + // Database Library (https://github.com/agrosner/DBFlow) + implementation libs.dbflow.core + implementation libs.dbflow.lib + kapt libs.dbflow.processor + + // Gson Serialization Library (https://github.com/google/gson) + implementation libs.google.gson + + implementation libs.faker + implementation libs.commons.io + + // Networking Library (https://square.github.io/retrofit/) + implementation libs.retrofit.lib + implementation libs.retrofit.converter.gson + implementation libs.retrofit.logging.interceptor + + implementation libs.androidx.appcompat + implementation libs.xanscale.localhost.toolkit + + testImplementation libs.junit4 + androidTestImplementation libs.androidx.junit + androidTestImplementation libs.androidx.espresso.core +} diff --git a/shared-test/src/main/AndroidManifest.xml b/shared-test/src/main/AndroidManifest.xml new file mode 100644 index 000000000..a5918e68a --- /dev/null +++ b/shared-test/src/main/AndroidManifest.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/app/src/sharedTest/java/org/openobservatory/ooniprobe/di/TestAppComponent.java b/shared-test/src/main/java/org/openobservatory/ooniprobe/di/TestAppComponent.java similarity index 100% rename from app/src/sharedTest/java/org/openobservatory/ooniprobe/di/TestAppComponent.java rename to shared-test/src/main/java/org/openobservatory/ooniprobe/di/TestAppComponent.java diff --git a/app/src/sharedTest/java/org/openobservatory/ooniprobe/di/TestAppModule.java b/shared-test/src/main/java/org/openobservatory/ooniprobe/di/TestAppModule.java similarity index 100% rename from app/src/sharedTest/java/org/openobservatory/ooniprobe/di/TestAppModule.java rename to shared-test/src/main/java/org/openobservatory/ooniprobe/di/TestAppModule.java diff --git a/app/src/sharedTest/java/org/openobservatory/ooniprobe/di/TestApplication.java b/shared-test/src/main/java/org/openobservatory/ooniprobe/di/TestApplication.java similarity index 100% rename from app/src/sharedTest/java/org/openobservatory/ooniprobe/di/TestApplication.java rename to shared-test/src/main/java/org/openobservatory/ooniprobe/di/TestApplication.java diff --git a/app/src/sharedTest/java/org/openobservatory/ooniprobe/factory/EventResultFactory.java b/shared-test/src/main/java/org/openobservatory/ooniprobe/factory/EventResultFactory.java similarity index 100% rename from app/src/sharedTest/java/org/openobservatory/ooniprobe/factory/EventResultFactory.java rename to shared-test/src/main/java/org/openobservatory/ooniprobe/factory/EventResultFactory.java diff --git a/app/src/sharedTest/java/org/openobservatory/ooniprobe/factory/JsonResultFactory.java b/shared-test/src/main/java/org/openobservatory/ooniprobe/factory/JsonResultFactory.java similarity index 100% rename from app/src/sharedTest/java/org/openobservatory/ooniprobe/factory/JsonResultFactory.java rename to shared-test/src/main/java/org/openobservatory/ooniprobe/factory/JsonResultFactory.java diff --git a/app/src/sharedTest/java/org/openobservatory/ooniprobe/factory/MeasurementFactory.java b/shared-test/src/main/java/org/openobservatory/ooniprobe/factory/MeasurementFactory.java similarity index 100% rename from app/src/sharedTest/java/org/openobservatory/ooniprobe/factory/MeasurementFactory.java rename to shared-test/src/main/java/org/openobservatory/ooniprobe/factory/MeasurementFactory.java diff --git a/app/src/sharedTest/java/org/openobservatory/ooniprobe/factory/NetworkFactory.java b/shared-test/src/main/java/org/openobservatory/ooniprobe/factory/NetworkFactory.java similarity index 100% rename from app/src/sharedTest/java/org/openobservatory/ooniprobe/factory/NetworkFactory.java rename to shared-test/src/main/java/org/openobservatory/ooniprobe/factory/NetworkFactory.java diff --git a/app/src/sharedTest/java/org/openobservatory/ooniprobe/factory/ResponseFactory.java b/shared-test/src/main/java/org/openobservatory/ooniprobe/factory/ResponseFactory.java similarity index 100% rename from app/src/sharedTest/java/org/openobservatory/ooniprobe/factory/ResponseFactory.java rename to shared-test/src/main/java/org/openobservatory/ooniprobe/factory/ResponseFactory.java diff --git a/app/src/sharedTest/java/org/openobservatory/ooniprobe/factory/ResultFactory.java b/shared-test/src/main/java/org/openobservatory/ooniprobe/factory/ResultFactory.java similarity index 100% rename from app/src/sharedTest/java/org/openobservatory/ooniprobe/factory/ResultFactory.java rename to shared-test/src/main/java/org/openobservatory/ooniprobe/factory/ResultFactory.java diff --git a/app/src/sharedTest/java/org/openobservatory/ooniprobe/factory/TestKeyFactory.java b/shared-test/src/main/java/org/openobservatory/ooniprobe/factory/TestKeyFactory.java similarity index 100% rename from app/src/sharedTest/java/org/openobservatory/ooniprobe/factory/TestKeyFactory.java rename to shared-test/src/main/java/org/openobservatory/ooniprobe/factory/TestKeyFactory.java diff --git a/app/src/sharedTest/java/org/openobservatory/ooniprobe/factory/UrlFactory.java b/shared-test/src/main/java/org/openobservatory/ooniprobe/factory/UrlFactory.java similarity index 100% rename from app/src/sharedTest/java/org/openobservatory/ooniprobe/factory/UrlFactory.java rename to shared-test/src/main/java/org/openobservatory/ooniprobe/factory/UrlFactory.java diff --git a/app/src/sharedTest/java/org/openobservatory/ooniprobe/utils/DatabaseUtils.java b/shared-test/src/main/java/org/openobservatory/ooniprobe/utils/DatabaseUtils.java similarity index 100% rename from app/src/sharedTest/java/org/openobservatory/ooniprobe/utils/DatabaseUtils.java rename to shared-test/src/main/java/org/openobservatory/ooniprobe/utils/DatabaseUtils.java diff --git a/app/src/sharedTest/java/org/openobservatory/ooniprobe/utils/FormattingUtils.java b/shared-test/src/main/java/org/openobservatory/ooniprobe/utils/FormattingUtils.java similarity index 100% rename from app/src/sharedTest/java/org/openobservatory/ooniprobe/utils/FormattingUtils.java rename to shared-test/src/main/java/org/openobservatory/ooniprobe/utils/FormattingUtils.java diff --git a/app/src/sharedTest/java/org/openobservatory/ooniprobe/utils/TestSuiteUtils.java b/shared-test/src/main/java/org/openobservatory/ooniprobe/utils/TestSuiteUtils.java similarity index 100% rename from app/src/sharedTest/java/org/openobservatory/ooniprobe/utils/TestSuiteUtils.java rename to shared-test/src/main/java/org/openobservatory/ooniprobe/utils/TestSuiteUtils.java diff --git a/app/src/sharedTest/java/org/openobservatory/ooniprobe/utils/models/TestSuiteMeasurements.java b/shared-test/src/main/java/org/openobservatory/ooniprobe/utils/models/TestSuiteMeasurements.java similarity index 100% rename from app/src/sharedTest/java/org/openobservatory/ooniprobe/utils/models/TestSuiteMeasurements.java rename to shared-test/src/main/java/org/openobservatory/ooniprobe/utils/models/TestSuiteMeasurements.java