Skip to content

Commit

Permalink
Start implementation of RunTests flow
Browse files Browse the repository at this point in the history
  • Loading branch information
aanorbel committed Nov 2, 2023
1 parent 1c9db05 commit 9309eff
Show file tree
Hide file tree
Showing 20 changed files with 1,364 additions and 542 deletions.
5 changes: 5 additions & 0 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,11 @@
android:name=".activity.OnboardingActivity"
android:screenOrientation="userPortrait"
android:theme="@style/Theme.MaterialComponents.NoActionBar.Onboarding" />
<activity
android:label="Run tests"
android:name=".activity.runtests.RunTestsActivity"
android:parentActivityName=".activity.MainActivity"
android:exported="false"/>
<activity
android:name=".activity.PreferenceActivity"
android:launchMode="singleTop"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
package org.openobservatory.ooniprobe.activity.runtests

import android.content.Context
import android.content.Intent
import android.os.Bundle
import android.text.TextUtils
import org.openobservatory.ooniprobe.R
import org.openobservatory.ooniprobe.activity.AbstractActivity
import org.openobservatory.ooniprobe.activity.runtests.RunTestsViewModel.Companion.SELECT_ALL
import org.openobservatory.ooniprobe.activity.runtests.adapter.RunTestsExpandableListViewAdapter
import org.openobservatory.ooniprobe.activity.runtests.models.ChildItem
import org.openobservatory.ooniprobe.activity.runtests.models.GroupItem
import org.openobservatory.ooniprobe.common.PreferenceManager
import org.openobservatory.ooniprobe.databinding.ActivityRunTestsBinding
import org.openobservatory.ooniprobe.test.suite.AbstractSuite
import java.io.Serializable
import javax.inject.Inject

class RunTestsActivity : AbstractActivity() {
lateinit var binding: ActivityRunTestsBinding
lateinit var mAdapter: RunTestsExpandableListViewAdapter

@Inject
lateinit var preferenceManager: PreferenceManager

@Inject
lateinit var viewModel: RunTestsViewModel

companion object {
const val TESTS: String = "tests"

@JvmStatic
fun newIntent(context: Context, testSuites: List<AbstractSuite>): Intent {
return Intent(context, RunTestsActivity::class.java).putExtras(Bundle().apply {
putSerializable(TESTS, testSuites as Serializable)
})
}
}

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityRunTestsBinding.inflate(layoutInflater)
setContentView(binding.getRoot())
supportActionBar?.setDisplayHomeAsUpEnabled(true)

activityComponent?.inject(this)

var testSuites: List<AbstractSuite>? = intent.extras?.getParcelableArrayList(TESTS)
testSuites?.let { ts ->
val tsGroups: List<GroupItem> = ts.map { testSuite ->
GroupItem(
selected = false,
name = testSuite.name,
nettests = testSuite.getTestList(preferenceManager).map { nettest ->
ChildItem(
selected = preferenceManager.resolveStatus(nettest.name),
name = nettest.name,
inputs = nettest.inputs
)
})
}

mAdapter = RunTestsExpandableListViewAdapter(this, tsGroups, viewModel)

binding.elv.setAdapter(mAdapter)

viewModel.selectedAllBtnStatus.observe(this) { selectAllBtnStatus ->
if (!TextUtils.isEmpty(selectAllBtnStatus)) {
if (selectAllBtnStatus == SELECT_ALL) {
binding.ckbSelectAll.isChecked = true
} else {
binding.ckbSelectAll.isChecked = false
}
mAdapter.notifyDataSetChanged()
updateStatusIndicator()
}
}
}

}

private fun updateStatusIndicator() {
binding.bottomBar.setTitle(getString(R.string.OONIRun_URLs, getChildItemsSelectedIdList().size.toString()))
}

private fun getChildItemsSelectedIdList(): List<String> {
val childItemSelectedIdList: MutableList<String> = ArrayList()
for (i in 0 until mAdapter.groupCount) {
val secondLevelItemList: List<ChildItem> = mAdapter.getGroup(i).nettests
secondLevelItemList
.asSequence()
.filter { it.selected }
.mapTo(childItemSelectedIdList) { it.name }
}
return childItemSelectedIdList
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package org.openobservatory.ooniprobe.activity.runtests

import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import org.openobservatory.ooniprobe.common.PreferenceManager
import javax.inject.Inject


class RunTestsViewModel() : ViewModel() {
lateinit var preferenceManager: PreferenceManager

@Inject
constructor(preferenceManager: PreferenceManager) : this() {
this.preferenceManager = preferenceManager
}

val selectedAllBtnStatus: MutableLiveData<String> = MutableLiveData()

init {
selectedAllBtnStatus.postValue(SELECT_SOME)
}

fun setSelectedAllBtnStatus(selectedStatus: String) {
selectedAllBtnStatus.postValue(selectedStatus)
}

companion object {
const val SELECT_ALL = "SELECT_ALL"
const val SELECT_SOME = "SELECT_SOME"
const val NOT_SELECT_ANY = "NOT_SELECT_ANY"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,232 @@
package org.openobservatory.ooniprobe.activity.runtests.adapter

import android.content.Context
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.BaseExpandableListAdapter
import android.widget.CheckBox
import android.widget.ImageView
import android.widget.TextView
import org.openobservatory.ooniprobe.R
import org.openobservatory.ooniprobe.activity.runtests.RunTestsViewModel
import org.openobservatory.ooniprobe.activity.runtests.RunTestsViewModel.Companion.NOT_SELECT_ANY
import org.openobservatory.ooniprobe.activity.runtests.RunTestsViewModel.Companion.SELECT_ALL
import org.openobservatory.ooniprobe.activity.runtests.RunTestsViewModel.Companion.SELECT_SOME
import org.openobservatory.ooniprobe.activity.runtests.models.ChildItem
import org.openobservatory.ooniprobe.activity.runtests.models.GroupItem
import org.openobservatory.ooniprobe.test.suite.AbstractSuite
import org.openobservatory.ooniprobe.test.suite.ExperimentalSuite
import org.openobservatory.ooniprobe.test.test.AbstractTest


class RunTestsExpandableListViewAdapter(
private val mContext: Context,
private val mGroupListData: List<GroupItem>,
private val mViewModel: RunTestsViewModel
) : BaseExpandableListAdapter() {
override fun getGroupCount(): Int {
return mGroupListData.size
}

override fun getChildrenCount(groupPosition: Int): Int {
return mGroupListData[groupPosition].nettests.size
}

override fun getGroup(groupPosition: Int): GroupItem {
return mGroupListData[groupPosition]
}

override fun getChild(groupPosition: Int, childPosition: Int): ChildItem {
return mGroupListData[groupPosition].nettests[childPosition]
}

override fun getGroupId(groupPosition: Int): Long {
return groupPosition.toLong()
}

override fun getChildId(groupPosition: Int, childPosition: Int): Long {
return childPosition.toLong()
}

override fun hasStableIds(): Boolean {
return false
}

override fun getGroupView(groupPosition: Int, isExpanded: Boolean, convertView: View?, parent: ViewGroup): View? {
var convertView =
convertView ?: LayoutInflater.from(mContext).inflate(R.layout.run_tests_group_list_item, parent, false)
val groupItem = getGroup(groupPosition)
val testSuite = AbstractSuite.getTestSuiteByName(groupItem.name)
convertView.findViewById<TextView>(R.id.group_name).text = parent.context.resources.getText(testSuite.title)
convertView.findViewById<ImageView>(R.id.group_icon).setImageResource(testSuite.icon)
val groupIndicator = convertView.findViewById<ImageView>(R.id.group_indicator)
val groupSelectionIndicator = convertView.findViewById<ImageView>(R.id.group_select_indicator)
val selectedAllBtnStatus = mViewModel.selectedAllBtnStatus.getValue()
if (selectedAllBtnStatus == SELECT_ALL) {
groupItem.selected = true
for (childItem in groupItem.nettests) {
childItem.selected = true
}
} else if (selectedAllBtnStatus == NOT_SELECT_ANY) {
groupItem.selected = false
for (childItem in groupItem.nettests) {
childItem.selected = false
}
}
if (groupItem.selected) {
if (isSelectAllChildItems(groupItem.nettests)) {
groupSelectionIndicator.setImageResource(R.drawable.check_box)
} else {
groupSelectionIndicator.setImageResource(R.drawable.check_box_outline_blank)
}
} else {
groupSelectionIndicator.setImageResource(R.drawable.check_box_outline_blank)
}
groupSelectionIndicator.setOnClickListener {
if (groupItem.selected && isSelectAllChildItems(groupItem.nettests)) {
groupItem.selected = false
for (childItem in groupItem.nettests) {
childItem.selected = false
}
if (isNotSelectedAnyGroupItem(mGroupListData)) {
mViewModel.setSelectedAllBtnStatus(NOT_SELECT_ANY)
} else {
mViewModel.setSelectedAllBtnStatus(SELECT_SOME)
}
} else {
groupItem.selected = true
for (childItem in groupItem.nettests) {
childItem.selected = true
}
if (isSelectedAllItems(mGroupListData)) {
mViewModel.setSelectedAllBtnStatus(SELECT_ALL)
} else {
mViewModel.setSelectedAllBtnStatus(SELECT_SOME)
}
}
notifyDataSetChanged()
}
if (isExpanded) {
groupIndicator.setImageResource(R.drawable.expand_less)
} else {
groupIndicator.setImageResource(R.drawable.expand_more)
}
return convertView
}

override fun getChildView(
groupPosition: Int,
childPosition: Int,
isLastChild: Boolean,
convertView: View?,
parent: ViewGroup
): View? {
var convertView =
convertView ?: LayoutInflater.from(mContext).inflate(R.layout.run_tests_child_list_item, parent, false)
val childItem = getChild(groupPosition, childPosition)
val groupItem = getGroup(groupPosition)
val nettest = AbstractTest.getTestByName(childItem.name)
convertView.findViewById<TextView>(R.id.child_name)?.apply {
text = when (groupItem.name) {
ExperimentalSuite.NAME -> {
childItem.name
}

else -> {
parent.context.resources.getText(nettest.labelResId)
}
}
}
convertView.findViewById<CheckBox>(R.id.child_select).apply {
isChecked = childItem.selected
setOnCheckedChangeListener { buttonView, isChecked ->

childItem.selected = isChecked
if (childItem.selected) {
if (isNotSelectedAnyChildItems(groupItem.nettests)) {
groupItem.selected = false
}
if (isNotSelectedAnyItems(mGroupListData)) {
mViewModel.setSelectedAllBtnStatus(NOT_SELECT_ANY)
} else {
mViewModel.setSelectedAllBtnStatus(SELECT_SOME)
}
} else {
groupItem.selected = true
if (isSelectedAllItems(mGroupListData)) {
mViewModel.setSelectedAllBtnStatus(SELECT_ALL)
} else {
mViewModel.setSelectedAllBtnStatus(SELECT_SOME)
}
}
notifyDataSetChanged()
}
}
return convertView
}

override fun isChildSelectable(groupPosition: Int, childPosition: Int): Boolean {
return false
}

private fun isNotSelectedAnyGroupItem(groupItemsList: List<GroupItem>): Boolean {
for (groupItem in groupItemsList) {
if (groupItem.selected) {
return false
}
}
return true
}

private fun isSelectedAllGroupItems(groupItemsList: List<GroupItem>): Boolean {
for (groupItem in groupItemsList) {
if (!groupItem.selected) {
return false
}
}
return true
}

private fun isNotSelectedAnyChildItems(childItemList: List<ChildItem>): Boolean {
for (childItem in childItemList) {
if (childItem.selected) {
return false
}
}
return true
}

private fun isSelectAllChildItems(childItemList: List<ChildItem>): Boolean {
for (childItem in childItemList) {
if (!childItem.selected) {
return false
}
}
return true
}

private fun isSelectedAllItems(groupItemList: List<GroupItem>?): Boolean {
for (groupItem in groupItemList!!) {
if (!groupItem.selected) {
return false
}
if (!isSelectAllChildItems(groupItem.nettests)) {
return false
}
}
return true
}

private fun isNotSelectedAnyItems(groupItemList: List<GroupItem>?): Boolean {
for (groupItem in groupItemList!!) {
if (groupItem.selected) {
return false
}
if (!isNotSelectedAnyChildItems(groupItem.nettests)) {
return false
}
}
return true
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package org.openobservatory.ooniprobe.activity.runtests.models

import org.openobservatory.engine.BaseDescriptor
import org.openobservatory.engine.BaseNettest

data class ChildItem(
var selected: Boolean,
var name: String,
var inputs: List<String>?
)

data class GroupItem(
var selected: Boolean,
var name: String,
var nettests: List<ChildItem>
)
Loading

0 comments on commit 9309eff

Please sign in to comment.