Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[New Designs] Update Chose websites view #630

Merged
merged 13 commits into from
Dec 5, 2023
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -95,11 +95,11 @@
</intent-filter>
</activity>
<activity
android:name=".activity.CustomWebsiteActivity"
android:name=".activity.customwebsites.CustomWebsiteActivity"
android:label="@string/Settings_Websites_CustomURL_Title"
android:exported="false"
android:parentActivityName=".activity.PreferenceActivity"
android:screenOrientation="userPortrait">
android:theme="@style/Theme.MaterialComponents.Light.DarkActionBar.App.NoActionBar">
<intent-filter>
<action android:name="${applicationId}.activity.CustomWebsiteActivity" />

Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
import androidx.core.view.ViewCompat;

import org.openobservatory.ooniprobe.R;
import org.openobservatory.ooniprobe.activity.customwebsites.CustomWebsiteActivity;
aanorbel marked this conversation as resolved.
Show resolved Hide resolved
import org.openobservatory.ooniprobe.common.PreferenceManager;
import org.openobservatory.ooniprobe.databinding.ActivityOverviewBinding;
import org.openobservatory.ooniprobe.model.database.Result;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
package org.openobservatory.ooniprobe.activity.customwebsites

import android.content.DialogInterface
import android.os.Bundle
import android.os.Parcelable
import android.util.Patterns
import android.view.Menu
import android.view.MenuInflater
import android.view.MenuItem
import androidx.activity.viewModels
import androidx.recyclerview.widget.LinearLayoutManager
import org.openobservatory.ooniprobe.R
import org.openobservatory.ooniprobe.activity.AbstractActivity
import org.openobservatory.ooniprobe.activity.RunningActivity
import org.openobservatory.ooniprobe.activity.customwebsites.adapter.CustomWebsiteRecyclerViewAdapter
import org.openobservatory.ooniprobe.activity.customwebsites.adapter.ItemChangedListener
import org.openobservatory.ooniprobe.common.PreferenceManager
import org.openobservatory.ooniprobe.databinding.ActivityCustomwebsiteBinding
import org.openobservatory.ooniprobe.fragment.ConfirmDialogFragment
import org.openobservatory.ooniprobe.model.database.Url
import org.openobservatory.ooniprobe.test.suite.WebsitesSuite
import java.io.Serializable
import javax.inject.Inject

class CustomWebsiteActivity : AbstractActivity(), ConfirmDialogFragment.OnClickListener {
aanorbel marked this conversation as resolved.
Show resolved Hide resolved
@Inject
lateinit var preferenceManager: PreferenceManager

val viewModel: CustomWebsiteViewModel by viewModels()

private lateinit var adapter: CustomWebsiteRecyclerViewAdapter
private lateinit var binding: ActivityCustomwebsiteBinding
override fun onCreate(savedInstanceState: Bundle?) {
aanorbel marked this conversation as resolved.
Show resolved Hide resolved
super.onCreate(savedInstanceState)
activityComponent.inject(this)
binding = ActivityCustomwebsiteBinding.inflate(
layoutInflater
)
setContentView(binding.root)
setSupportActionBar(binding.toolbar)
supportActionBar?.setDisplayHomeAsUpEnabled(true)
val layoutManager = LinearLayoutManager(this)
binding.urlContainer.layoutManager = layoutManager
adapter = CustomWebsiteRecyclerViewAdapter(
onItemChangedListener = object : ItemChangedListener {
override fun onItemRemoved(position: Int) {
binding.bottomBar.title = getString(
R.string.OONIRun_URLs, adapter.itemCount.toString()
)
viewModel.onItemRemoved(position)
}

override fun onItemUpdated(position: Int, item: String) {
viewModel.updateUrlAt(position, item)
}
},
)
viewModel.urls.observe(this) { urls ->
binding.bottomBar.title = getString(
R.string.OONIRun_URLs, urls.size.toString()
)
}

binding.bottomBar.setOnMenuItemClickListener { item: MenuItem? -> runTests() }
binding.add.setOnClickListener { add() }

binding.urlContainer.adapter = adapter
if (viewModel.urls.value == null) {
add()
}
}

override fun onResume() {
super.onResume()
viewModel.urls.value?.let { urls ->
adapter.submitList(urls)
binding.urlContainer.post { adapter.notifyDataSetChanged() }
}
}

/**
* This function will run the tests if the list of urls is not empty.
* If the list is empty, it will not run the tests.
* This function will also sanitize the url and remove any new lines.
* It will also check if the url is valid and not too long.
* If the url is not valid or too long, it will not be added to the tests.
*/
private fun runTests(): Boolean {
val items = viewModel.urls.value ?: listOf()
if (items.isEmpty()) {
return false
}
val urls = ArrayList<String>(items.size)
for (value in items) {
val sanitizedUrl = value.replace("\\r\\n|\\r|\\n".toRegex(), " ")
//https://support.microsoft.com/en-us/help/208427/maximum-url-length-is-2-083-characters-in-internet-explorer
if (Patterns.WEB_URL.matcher(sanitizedUrl)
.matches() && sanitizedUrl.length < 2084
) urls.add(
Url.checkExistingUrl(sanitizedUrl).toString()
)
}
val suite = WebsitesSuite()
suite.getTestList(preferenceManager)[0].inputs = urls
RunningActivity.runAsForegroundService(
this@CustomWebsiteActivity, suite.asArray(), { finish() }, preferenceManager
)
return true
}

/**
* This function will show a dialog if the user has edited the list of urls.
* If the user has edited the list of urls, it will show a dialog asking if the user wants to save the changes.
* If the user has not edited the list of urls, it will just call super.onBackPressed()
*/
override fun onBackPressed() {
val base = getString(R.string.http)
val edited = adapter.itemCount > 0 && viewModel.urls.value?.get(0) != base
if (edited) {
ConfirmDialogFragment(
title = getString(R.string.Modal_CustomURL_Title_NotSaved),
message = getString(R.string.Modal_CustomURL_NotSaved),
).show(supportFragmentManager, null)
} else {
super.onBackPressed()
}
}

override fun onSupportNavigateUp(): Boolean {
onBackPressed()
return true
}

override fun onCreateOptionsMenu(menu: Menu): Boolean {
val inflater: MenuInflater = menuInflater
inflater.inflate(R.menu.close, menu)
return true
}

override fun onOptionsItemSelected(item: MenuItem): Boolean {
return when (item.itemId) {
R.id.close_button -> {
onSupportNavigateUp()
true
}

else -> super.onOptionsItemSelected(item)
}
}

/**
* This function will add a new url to the list of urls.
* It will also scroll to the bottom of the list.
*/
fun add() {
viewModel.addUrl(getString(R.string.http))
binding.urlContainer.layoutManager?.scrollToPosition(adapter.itemCount - 1)
}

/**
* This function will be called when the user clicks on a button in the dialog.
* If the user clicks on the positive button, it will call super.onBackPressed()
*/
override fun onConfirmDialogClick(
serializable: Serializable?, parcelable: Parcelable?, buttonClicked: Int
) {
if (buttonClicked == DialogInterface.BUTTON_POSITIVE) super.onBackPressed()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package org.openobservatory.ooniprobe.activity.customwebsites

import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel

/**
* This class is used to store the data for the CustomWebsiteActivity.
* The data is stored in a ViewModel so that it can survive configuration changes (like rotation).
* This class shound not be injected to the activity using a DI framework.
aanorbel marked this conversation as resolved.
Show resolved Hide resolved
*/
class CustomWebsiteViewModel : ViewModel() {

val urls = MutableLiveData<MutableList<String>>()

/**
* This function will add a new url to the list of urls.
* If the list is null, it will create a new list.
*/
fun addUrl(url: String) {
val currentUrls = urls.value ?: ArrayList()
currentUrls.add(url)
urls.value = currentUrls
}

/**
* This function will remove a url from the list of urls.
* If the list is null, it will not do anything.
*/
fun onItemRemoved(position: Int) {
val currentList = urls.value ?: mutableListOf()
if (position < currentList.size) {
currentList.removeAt(position)
urls.value = currentList
}
}

/**
* This function will update the url at the given position.
* If the list is null, it will not do anything.
*/
fun updateUrlAt(position: Int, newUrl: String) {
val currentList = urls.value ?: mutableListOf()
if (position < currentList.size) {
currentList[position] = newUrl
urls.value = currentList
}
}

}
Loading
Loading