Skip to content

Commit

Permalink
Updated activity to retain state on configuration change
Browse files Browse the repository at this point in the history
  • Loading branch information
aanorbel committed Dec 2, 2023
1 parent 3918adb commit c3740dc
Show file tree
Hide file tree
Showing 8 changed files with 150 additions and 112 deletions.
2 changes: 1 addition & 1 deletion app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@
</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"
Expand Down
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;
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
@@ -1,4 +1,4 @@
package org.openobservatory.ooniprobe.activity
package org.openobservatory.ooniprobe.activity.customwebsites

import android.content.DialogInterface
import android.os.Bundle
Expand All @@ -8,13 +8,16 @@ import android.view.Menu
import android.view.MenuInflater
import android.view.MenuItem
import android.view.View
import androidx.activity.viewModels
import androidx.recyclerview.widget.LinearLayoutManager
import org.openobservatory.ooniprobe.fragment.ConfirmDialogFragment
import org.openobservatory.ooniprobe.R
import org.openobservatory.ooniprobe.adapters.CustomWebsiteRecyclerViewAdapter
import org.openobservatory.ooniprobe.adapters.ItemRemovedListener
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.ItemRemovedListener
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
Expand All @@ -23,6 +26,9 @@ import javax.inject.Inject
class CustomWebsiteActivity : AbstractActivity(), ConfirmDialogFragment.OnClickListener {
@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?) {
Expand All @@ -36,40 +42,63 @@ class CustomWebsiteActivity : AbstractActivity(), ConfirmDialogFragment.OnClickL
supportActionBar?.setDisplayHomeAsUpEnabled(true)
val layoutManager = LinearLayoutManager(this)
binding.urlContainer.layoutManager = layoutManager
adapter = CustomWebsiteRecyclerViewAdapter(object : ItemRemovedListener {
override fun onItemRemoved(position: Int) {
binding.bottomBar.title = getString(
R.string.OONIRun_URLs, adapter.itemCount.toString()
)
}
})
binding.bottomBar.setOnMenuItemClickListener { item: MenuItem? ->
val items = adapter.getItems()
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
adapter = CustomWebsiteRecyclerViewAdapter(
onItemRemovedListener = object : ItemRemovedListener {
override fun onItemRemoved(position: Int) {
binding.bottomBar.title = getString(
R.string.OONIRun_URLs, adapter.itemCount.toString()
)
viewModel.onItemRemoved(position)
}
},
viewModel = viewModel
)
viewModel.urls.observe(this) { urls ->
binding.bottomBar.title = getString(
R.string.OONIRun_URLs, urls.size.toString()
)
true
}

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

binding.urlContainer.adapter = adapter
add()
// TODO(aanorbel): Fix: Configuration change triggers loss of data.
if (viewModel.urls.value == null) {
add()
}
}

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

private fun runTests(): Boolean {
val items = viewModel.urls.value ?: listOf()
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
}

override fun onBackPressed() {
val base = getString(R.string.http)
val edited = adapter.itemCount > 0 && adapter.getItems()[0] != base
val edited = adapter.itemCount > 0 && adapter.items[0] != base
if (edited) {
ConfirmDialogFragment(
title = "Are you sure?",
Expand Down Expand Up @@ -103,11 +132,7 @@ class CustomWebsiteActivity : AbstractActivity(), ConfirmDialogFragment.OnClickL
}

fun add() {
adapter.addAll(listOf(getString(R.string.http)))
binding.bottomBar.title = getString(
R.string.OONIRun_URLs, adapter.itemCount.toString()
)
adapter.notifyDataSetChanged()
viewModel.addUrl(getString(R.string.http))
scrollToBottom()
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package org.openobservatory.ooniprobe.activity.customwebsites

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


class CustomWebsiteViewModel : ViewModel() {

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

fun addUrl(url: String) {
val currentUrls = urls.value ?: ArrayList()
currentUrls.add(url)
urls.value = currentUrls
}

fun onItemRemoved(position: Int) {
val currentList = urls.value ?: mutableListOf()
if (position < currentList.size) {
currentList.removeAt(position)
urls.value = currentList
}
}

fun updateUrlAt(position: Int, newUrl: String) {
val currentList = urls.value ?: mutableListOf()
if (position < currentList.size) {
currentList[position] = newUrl
urls.value = currentList
}
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package org.openobservatory.ooniprobe.activity.customwebsites.adapter

import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.core.widget.addTextChangedListener
import androidx.recyclerview.widget.RecyclerView
import org.openobservatory.ooniprobe.activity.customwebsites.CustomWebsiteViewModel
import org.openobservatory.ooniprobe.databinding.EdittextUrlBinding


class CustomWebsiteRecyclerViewAdapter(
private val onItemRemovedListener: ItemRemovedListener,
private val viewModel: CustomWebsiteViewModel,
var items: MutableList<String> = mutableListOf()
) : RecyclerView.Adapter<CustomWebsiteRecyclerViewAdapter.ViewHolder>() {

override fun onCreateViewHolder(
parent: ViewGroup, viewType: Int
): ViewHolder {
return ViewHolder(
EdittextUrlBinding.inflate(
LayoutInflater.from(parent.context),
parent,
false
)
)
}

override fun onBindViewHolder(
holder: ViewHolder, position: Int
) {
holder.binding.editText.setText(items[position])
holder.binding.delete.visibility = if (items.size > 1) {
View.VISIBLE
} else {
View.INVISIBLE
}
holder.binding.delete.setOnClickListener {
onItemRemovedListener.onItemRemoved(holder.adapterPosition)
}
holder.binding.editText.addTextChangedListener {
viewModel.updateUrlAt(position, it.toString())
}
}

override fun getItemCount(): Int = items.size

class ViewHolder(val binding: EdittextUrlBinding) : RecyclerView.ViewHolder(binding.root)
}

interface ItemRemovedListener {
fun onItemRemoved(position: Int)
}

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package org.openobservatory.ooniprobe.di;


import org.openobservatory.ooniprobe.activity.CustomWebsiteActivity;
import org.openobservatory.ooniprobe.activity.customwebsites.CustomWebsiteActivity;
import org.openobservatory.ooniprobe.activity.LogActivity;
import org.openobservatory.ooniprobe.activity.MainActivity;
import org.openobservatory.ooniprobe.activity.MeasurementDetailActivity;
Expand Down
2 changes: 1 addition & 1 deletion app/src/main/res/layout/activity_customwebsite.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".activity.CustomWebsiteActivity">
tools:context=".activity.customwebsites.CustomWebsiteActivity">
<com.google.android.material.appbar.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
Expand Down

0 comments on commit c3740dc

Please sign in to comment.