Skip to content

Commit

Permalink
[New Designs] Update OONI Run v1 UI to v2 (#626)
Browse files Browse the repository at this point in the history
Fixes  ooni/probe#2596

## Proposed Changes

  - Update designs for Run v1 components to match new designs
  - Add `SrtingListRecyclerViewAdapter` to display List items
  - Convert `OoniRunActivity` to Kotlin
  
| Light | Dark|
|--|--|
|.|.|

|![web_connectivity_light](https://github.com/ooni/probe-android/assets/17911892/6f4b5896-c85a-4cd9-8fb2-0f6eb693f38e)|![web_connectivity_dark](https://github.com/ooni/probe-android/assets/17911892/6a30f612-1b42-4e43-b8e5-0f4a16edf86b)|

|![web_connectivity_light_long_list](https://github.com/ooni/probe-android/assets/17911892/9d871137-fd3b-4b23-9c3a-700d30d43f6b)|
![web_connectivity_dark_long_list](https://github.com/ooni/probe-android/assets/17911892/82c0d8fa-c0b6-4ed1-83ef-607c4bf952c4)|

|![web_connectivity_light_short_list](https://github.com/ooni/probe-android/assets/17911892/42e96ad5-318b-4220-beae-d5d8932da985)|![web_connectivity_dark_short_list](https://github.com/ooni/probe-android/assets/17911892/7aafa54c-5498-40d2-8cbb-f00173e001ee)|

|![http_header_field_manipulation_light](https://github.com/ooni/probe-android/assets/17911892/c79e07be-9044-4cf9-9272-b4be0a6f6d4f)|![http_header_field_manipulation_dark](https://github.com/ooni/probe-android/assets/17911892/d4cb96ed-c6a5-4e55-9a9b-7f47059fde3f)|

|![invalid_light](https://github.com/ooni/probe-android/assets/17911892/05516448-7094-484d-9ccb-f118f489e546)|![invalid_dark](https://github.com/ooni/probe-android/assets/17911892/bc5351fa-770f-43d8-aa65-5265ba4c1cad)|

|![outdated_light](https://github.com/ooni/probe-android/assets/17911892/e475971e-23c0-4f37-9ee1-598ce91bb611)|![outdated_dark](https://github.com/ooni/probe-android/assets/17911892/a94e5849-3eeb-4ad1-a393-4591d5d0f824)|

|![whatsapp_light](https://github.com/ooni/probe-android/assets/17911892/3f9b5760-a259-4838-a28c-1d8db1e8ceed)|![whatsapp_dark](https://github.com/ooni/probe-android/assets/17911892/2313ca90-b5ab-4bb1-ac13-7cc763bc06eb)?
  • Loading branch information
aanorbel authored Nov 24, 2023
1 parent 7deb8bf commit 997840c
Show file tree
Hide file tree
Showing 8 changed files with 341 additions and 245 deletions.
2 changes: 1 addition & 1 deletion app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@
</activity>
<activity
android:name=".activity.OoniRunActivity"
android:label="@string/APP_NAME"
android:label=""
android:exported="true"
android:launchMode="singleTop"
android:parentActivityName=".activity.MainActivity"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,173 +0,0 @@
package org.openobservatory.ooniprobe.activity;

import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.util.Patterns;
import android.view.View;
import android.webkit.URLUtil;
import android.widget.Toast;

import androidx.recyclerview.widget.DividerItemDecoration;
import androidx.recyclerview.widget.LinearLayoutManager;

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;
import org.openobservatory.ooniprobe.item.TextItem;
import org.openobservatory.ooniprobe.test.suite.AbstractSuite;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import javax.inject.Inject;

import localhost.toolkit.widget.recyclerview.HeterogeneousRecyclerAdapter;
import localhost.toolkit.widget.recyclerview.HeterogeneousRecyclerItem;

public class OoniRunActivity extends AbstractActivity {
ActivityOonirunBinding binding;
private ArrayList<HeterogeneousRecyclerItem> items;
private HeterogeneousRecyclerAdapter<HeterogeneousRecyclerItem> adapter;

@Inject
PreferenceManager preferenceManager;

@Inject
VersionCompare versionCompare;

@Inject
GetTestSuite getSuite;

@Inject
Gson gson;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getActivityComponent().inject(this);
binding = ActivityOonirunBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot());
setSupportActionBar(binding.toolbar);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
getSupportActionBar().setDisplayShowHomeEnabled(true);
LinearLayoutManager layoutManager = new LinearLayoutManager(this);
binding.recycler.setLayoutManager(layoutManager);
binding.recycler.addItemDecoration(new DividerItemDecoration(this, layoutManager.getOrientation()));
items = new ArrayList<>();
adapter = new HeterogeneousRecyclerAdapter<>(this, items);
binding.recycler.setAdapter(adapter);
manageIntent(getIntent());
}

@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
manageIntent(intent);
}

private void manageIntent(Intent intent) {
if (isTestRunning()) {
Toast.makeText(this, getString(R.string.OONIRun_TestRunningError), Toast.LENGTH_LONG).show();
finish();
}
else if (Intent.ACTION_VIEW.equals(intent.getAction())) {
Uri uri = intent.getData();
String mv = uri == null ? null : uri.getQueryParameter("mv");
String tn = uri == null ? null : uri.getQueryParameter("tn");
String ta = uri == null ? null : uri.getQueryParameter("ta");
loadScreen(mv, tn, ta);
}
else if (Intent.ACTION_SEND.equals(intent.getAction())) {
String url = intent.getStringExtra(Intent.EXTRA_TEXT);
if (url != null && Patterns.WEB_URL.matcher(url).matches()) {
List<String> urls = Collections.singletonList(url);
AbstractSuite suite = getSuite.get("web_connectivity", urls);
if (suite != null) {
loadSuite(suite, urls);
} else {
loadInvalidAttributes();
}
} else {
loadInvalidAttributes();
}
}
}

private void loadScreen(String mv, String tn, String ta){
String[] split = BuildConfig.VERSION_NAME.split("-");
String version_name = split[0];
if (mv != null && tn != null) {
if (versionCompare.compare(version_name, mv) >= 0) {
try {
Attribute attribute = gson.fromJson(ta, Attribute.class);
List<String> urls = (attribute!=null && attribute.urls != null) ? attribute.urls : null;
AbstractSuite suite = getSuite.get(tn, urls);
if (suite != null) {
loadSuite(suite, urls);
} else {
loadInvalidAttributes();
}
} catch (Exception e) {
loadInvalidAttributes();
}
} else {
loadOutOfDate();
}
} else {
loadInvalidAttributes();
}
}

private void loadOutOfDate() {
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<String> urls) {
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();
binding.iconBig.setVisibility(View.GONE);
} else {
binding.iconBig.setImageResource(suite.getIcon());
binding.iconBig.setVisibility(View.VISIBLE);
}
binding.run.setOnClickListener(v -> {

RunningActivity.runAsForegroundService(OoniRunActivity.this, suite.asArray(),this::finish, preferenceManager);

});
}

private void loadInvalidAttributes() {
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());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,188 @@
package org.openobservatory.ooniprobe.activity

import android.content.Intent
import android.content.res.ColorStateList
import android.graphics.Color
import android.net.Uri
import android.os.Bundle
import android.util.Patterns
import android.webkit.URLUtil
import android.widget.Toast
import androidx.core.graphics.ColorUtils
import androidx.recyclerview.widget.LinearLayoutManager
import com.google.gson.Gson
import org.openobservatory.ooniprobe.BuildConfig
import org.openobservatory.ooniprobe.R
import org.openobservatory.ooniprobe.adapters.StringListRecyclerViewAdapter
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
import org.openobservatory.ooniprobe.test.suite.AbstractSuite
import javax.inject.Inject

class OoniRunActivity : AbstractActivity() {
lateinit var binding: ActivityOonirunBinding

@Inject
lateinit var preferenceManager: PreferenceManager

@Inject
lateinit var versionCompare: VersionCompare

@Inject
lateinit var getSuite: GetTestSuite

@Inject
lateinit var gson: Gson
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
activityComponent.inject(this)
binding = ActivityOonirunBinding.inflate(
layoutInflater
)
setContentView(binding.root)
setSupportActionBar(binding.toolbar)
supportActionBar?.apply {
setDisplayHomeAsUpEnabled(true)
setDisplayShowHomeEnabled(true)
}
manageIntent(intent)
}

override fun onNewIntent(intent: Intent) {
super.onNewIntent(intent)
manageIntent(intent)
}

private fun manageIntent(intent: Intent) {
if (isTestRunning) {
Toast.makeText(this, getString(R.string.OONIRun_TestRunningError), Toast.LENGTH_LONG)
.show()
finish()
} else if (Intent.ACTION_VIEW == intent.action) {
val uri = intent.data
val mv = uri?.getQueryParameter("mv")
val tn = uri?.getQueryParameter("tn")
val ta = uri?.getQueryParameter("ta")
loadScreen(mv, tn, ta)
} else if (Intent.ACTION_SEND == intent.action) {
val url = intent.getStringExtra(Intent.EXTRA_TEXT)
if (url != null && Patterns.WEB_URL.matcher(url).matches()) {
val urls = listOf(url)
val suite = getSuite["web_connectivity", urls]
if (suite != null) {
loadSuite(suite, urls)
} else {
loadInvalidAttributes()
}
} else {
loadInvalidAttributes()
}
}
}

private fun loadScreen(mv: String?, tn: String?, ta: String?) {
val split = BuildConfig.VERSION_NAME.split("-".toRegex()).dropLastWhile { it.isEmpty() }
.toTypedArray()
val versionName = split.first()
if (mv != null && tn != null) {
if (versionCompare.compare(versionName, mv) >= 0) {
try {
val attribute = gson.fromJson(ta, Attribute::class.java)
val urls = attribute?.urls
val suite = getSuite[tn, urls]
if (suite != null) {
loadSuite(suite, urls)
} else {
loadInvalidAttributes()
}
} catch (e: Exception) {
loadInvalidAttributes()
}
} else {
loadOutOfDate()
}
} else {
loadInvalidAttributes()
}
}

private fun loadOutOfDate() {
setThemeColor(resources.getColor(R.color.color_gray4))
setTextColor(Color.BLACK)
binding.title.setText(R.string.OONIRun_OONIProbeOutOfDate)
binding.desc.setText(R.string.OONIRun_OONIProbeNewerVersion)
binding.icon.setImageResource(R.drawable.update)
binding.run.apply {
setText(R.string.OONIRun_Update)
setOnClickListener {
startActivity(
Intent(
Intent.ACTION_VIEW,
Uri.parse("https://play.google.com/store/apps/details?id=$packageName")
)
)
finish()
}
}
}

private fun loadSuite(suite: AbstractSuite, urls: List<String>?) {
val items = ArrayList<String>()
binding.icon.setImageResource(suite.icon)
binding.title.setText(suite.getTestList(preferenceManager).first().labelResId)
binding.desc.text = getString(R.string.OONIRun_YouAreAboutToRun)
urls?.let { urls ->
urls.filterTo(items) { URLUtil.isValidUrl(it) }
binding.recycler.apply {
layoutManager = LinearLayoutManager(this@OoniRunActivity)
adapter = StringListRecyclerViewAdapter(items)
}
}
setThemeColor(resources.getColor(suite.color))
binding.run.setOnClickListener {
RunningActivity.runAsForegroundService(
this@OoniRunActivity, suite.asArray(), { finish() }, preferenceManager
)
}
}

private fun setThemeColor(color: Int) {
val window = window
window.statusBarColor = color
binding.appbarLayout.setBackgroundColor(color)
when {
ColorUtils.calculateLuminance(color) > 0.5 -> {
setTextColor(Color.WHITE)
}

else -> {
setTextColor(Color.WHITE)
}
}
}

private fun setTextColor(color: Int) {
binding.title.setTextColor(color)
binding.icon.setColorFilter(color)
binding.desc.setTextColor(color)
binding.run.apply {
setTextColor(color)
strokeColor = ColorStateList.valueOf(color)
}
}

private fun loadInvalidAttributes() {
setThemeColor(resources.getColor(R.color.color_gray4))
setTextColor(Color.BLACK)
binding.title.setText(R.string.OONIRun_InvalidParameter)
binding.desc.setText(R.string.OONIRun_InvalidParameter_Msg)
binding.icon.setImageResource(R.drawable.question_mark)
binding.run.apply {
setText(R.string.OONIRun_Close)
setOnClickListener { finish() }
}
}
}
Loading

0 comments on commit 997840c

Please sign in to comment.