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] Implement add link flow #642

Merged
merged 65 commits into from
Feb 16, 2024
Merged
Show file tree
Hide file tree
Changes from 53 commits
Commits
Show all changes
65 commits
Select commit Hold shift + click to select a range
f1392aa
Replace `HeterogeneousRecyclerAdapter` with `RecyclerView.Adapter` an…
aanorbel Aug 30, 2023
5ac4479
Update annotation processing for tests
aanorbel Aug 30, 2023
4d0a80d
Merge branch 'master' of github.com:ooni/probe-android into fix/dashb…
aanorbel Oct 18, 2023
631230a
Updated shared module
aanorbel Oct 18, 2023
1f813af
AGP upgrade
aanorbel Oct 21, 2023
9b41077
Updated roboelectric
aanorbel Oct 21, 2023
059ef51
Merge branch 'master' of github.com:ooni/probe-android into fix/dashb…
aanorbel Oct 21, 2023
66ffbc3
Upgrade gradle to `8.1.2` and add support for kotlin and `kapt`
aanorbel Oct 24, 2023
09d2685
Update from `ru.noties:markwon:2.0.1` to `io.noties.markwon:core:4.6.2`
aanorbel Oct 25, 2023
9353e64
Change dependency naming
aanorbel Oct 25, 2023
657a6c9
Merge branch 'chore/gradle-kotlin-upgrade' of github.com:ooni/probe-a…
aanorbel Oct 25, 2023
6fd4edf
Updated `OverviewActivity.` and added `ReadMorePlugin`
aanorbel Oct 25, 2023
d532158
Update overview activity to have a collapsable toolbar
aanorbel Oct 30, 2023
9e4ea1d
Updated views to match new design
aanorbel Oct 30, 2023
9bbe78d
Convert `ProgressFragment` to kotlin
aanorbel Oct 31, 2023
d1c53cd
Updated text and icon for `ProgressFragment`
aanorbel Oct 31, 2023
de8667b
Updated rules for displaying test complete notification
aanorbel Oct 31, 2023
1c9db05
Merge branch 'master' of github.com:ooni/probe-android into chore/gra…
aanorbel Oct 31, 2023
c4d1e68
Updated translations trings
aanorbel Oct 31, 2023
9309eff
Start implementation of `RunTests` flow
aanorbel Nov 2, 2023
51f350e
Updated button status
aanorbel Nov 2, 2023
1461e29
Updated suite to run from `BaseDescriptor`
aanorbel Nov 2, 2023
1c5a84a
Modified views and refactored code
aanorbel Nov 2, 2023
20a3160
Modified preferences and activity actions
aanorbel Nov 2, 2023
5fbc024
Updated rules for storing preferences
aanorbel Nov 3, 2023
3902641
Modified Menu and toolbar views for `RunTestsActivity` with proper st…
aanorbel Nov 3, 2023
2044002
Updated the drawable tint for group icon indicator
aanorbel Nov 3, 2023
30d799b
Merge branch 'master' of github.com:ooni/probe-android into issues/2589
aanorbel Nov 3, 2023
8c1ab25
Merge branch 'master' of github.com:ooni/probe-android into issues/2591
aanorbel Nov 4, 2023
2d4437d
revert changes on strings.xml
aanorbel Nov 8, 2023
c2376ec
Changed test suite item font size
aanorbel Nov 21, 2023
4b51010
Updated documentation for `RunTestsExpandableListViewAdapter`
aanorbel Nov 22, 2023
1b0a716
rename variable
aanorbel Nov 22, 2023
c5a2db4
Update app/src/main/java/org/openobservatory/ooniprobe/common/Prefere…
aanorbel Nov 22, 2023
31f8d4c
Removed comment
aanorbel Nov 22, 2023
80731aa
Merge branch 'issues/2589' of github.com:ooni/probe-android into issu…
aanorbel Nov 22, 2023
b39b639
Merge branch 'issues/2588' of github.com:ooni/probe-android into chor…
aanorbel Nov 24, 2023
c51ebb0
Merge branch 'issues/2591' of github.com:ooni/probe-android into chor…
aanorbel Nov 24, 2023
d2dd31b
Migrate dashboard and overview to descriptor format
aanorbel Nov 26, 2023
369adfa
Merge branch 'dev/design-update' of github.com:ooni/probe-android int…
aanorbel Nov 26, 2023
1b254ee
Updated functions and tests
aanorbel Nov 29, 2023
599fe32
Merge branch 'dev/design-update' of github.com:ooni/probe-android int…
aanorbel Dec 6, 2023
b13c5a0
Correct test with empty test list
aanorbel Dec 6, 2023
b7b4536
Start implementation of add link flow for ooni run v2
aanorbel Dec 11, 2023
d9ca5df
Updated layout to match design specs
aanorbel Dec 12, 2023
e1b15b8
Merge branch 'dev/design-update' of github.com:ooni/probe-android int…
aanorbel Dec 18, 2023
4e0d850
Merge branch 'dev/design-update' of github.com:ooni/probe-android int…
aanorbel Dec 18, 2023
44b4ac5
Update flow to include proper progress
aanorbel Dec 19, 2023
aa702c5
Update app/src/main/java/org/openobservatory/ooniprobe/activity/add_d…
aanorbel Dec 20, 2023
86342f1
Update app/src/main/java/org/openobservatory/ooniprobe/common/LocaleU…
aanorbel Dec 20, 2023
d3bd5d7
Update app/src/main/java/org/openobservatory/ooniprobe/activity/add_d…
aanorbel Dec 20, 2023
5a42afb
Update app/src/main/java/org/openobservatory/ooniprobe/activity/add_d…
aanorbel Dec 20, 2023
44ea86d
Updated code based on review
aanorbel Dec 20, 2023
dad3f90
Update app/src/main/java/org/openobservatory/ooniprobe/activity/MainA…
aanorbel Dec 20, 2023
788d22a
Updated code based on review
aanorbel Dec 20, 2023
b439010
Updates based on review
aanorbel Dec 21, 2023
d5c8531
Update code based on review
aanorbel Dec 21, 2023
db37b06
Merge branch 'master' of github.com:ooni/probe-android into issues/2595
aanorbel Dec 21, 2023
ae929c8
Update based on review
aanorbel Dec 22, 2023
8375440
Feat: Add database model persistence (#645)
aanorbel Jan 13, 2024
5feeaab
Feat: Add Icon support for OONI Run v2 descriptors (#649)
aanorbel Feb 13, 2024
bd68a91
Feat: Update `OverviewActivity.java` interface with missing functions…
aanorbel Feb 13, 2024
d096681
feat: Update the entry point for ooni run v2 to use load using a dif…
aanorbel Feb 13, 2024
1b2ac66
Merge branch 'dev/design-update' of github.com:ooni/probe-android int…
aanorbel Feb 15, 2024
4bf6085
Modified `AbstractDescriptor` definition
aanorbel Feb 16, 2024
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: 4 additions & 0 deletions app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,10 @@ android {
buildFeatures {
viewBinding = true
}
dataBinding {
enabled = true
enabledForTests = true
}
namespace 'org.openobservatory.ooniprobe'
}

Expand Down
403 changes: 216 additions & 187 deletions app/src/main/AndroidManifest.xml

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -0,0 +1,191 @@
package org.openobservatory.ooniprobe.activity.add_descriptor
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please, do not use _ for package names. It seems to be against some Java coding style conventions set forth by Oracle and Google, as documented at https://stackoverflow.com/a/3179221.


import android.content.Context
import android.content.Intent
import android.os.Build.VERSION
import android.os.Build.VERSION_CODES
import android.os.Bundle
import android.view.Menu
import android.view.MenuInflater
import android.view.MenuItem
import android.widget.ImageView
import android.widget.TextView
import androidx.activity.viewModels
import androidx.appcompat.widget.Toolbar
import androidx.databinding.BindingAdapter
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import com.google.android.material.checkbox.MaterialCheckBox
import io.noties.markwon.Markwon
import org.openobservatory.engine.OONIRunDescriptor
import org.openobservatory.ooniprobe.R
import org.openobservatory.ooniprobe.activity.AbstractActivity
import org.openobservatory.ooniprobe.activity.add_descriptor.adapter.AddDescriptorExpandableListAdapter
import org.openobservatory.ooniprobe.activity.add_descriptor.adapter.GroupedItem
import org.openobservatory.ooniprobe.common.PreferenceManager
import org.openobservatory.ooniprobe.common.ReadMorePlugin
import org.openobservatory.ooniprobe.common.TestDescriptorManager
import org.openobservatory.ooniprobe.databinding.ActivityAddDescriptorBinding
import javax.inject.Inject

/**
* This activity is used to add a new descriptor to the application. The activity shows the tests that are included in the descriptor.
* The user can select which tests to include, and if the descriptor should be automatically updated.
*/
class AddDescriptorActivity : AbstractActivity() {
aanorbel marked this conversation as resolved.
Show resolved Hide resolved
companion object {
private const val DESCRIPTOR = "descriptor"

/**
* This method is used to create an intent to start this activity.
* @param context is the context of the activity that calls this method
* @param descriptor is the descriptor to add
* @return an intent to start this activity
*/
@JvmStatic
fun newIntent(context: Context, descriptor: OONIRunDescriptor): Intent {
return Intent(context, AddDescriptorActivity::class.java).putExtra(
DESCRIPTOR,
descriptor
)
}

/**
* This method is used to set the text of a textview as markdown. The markdown is parsed using the markwon library.
* The textview must have the attribute app:richText set to the markdown text to parse.
* @param view is the textview that will contain the parsed text
* @param richText is the markdown text to parse
*/
@JvmStatic
@BindingAdapter(value = ["richText"])
fun setRichText(view: TextView, richText: String?) {
richText?.let { textValue ->
val r = view.context.resources
val markwon = Markwon.builder(view.context)
.usePlugin(
ReadMorePlugin(
labelMore = r.getString(R.string.OONIRun_ReadMore),
labelLess = r.getString(R.string.OONIRun_ReadLess)
)
)
.build()
markwon.setMarkdown(view, textValue)
}
}

/**
* This method is used to set the image of an imageview as a drawable resource. The drawable is set using the name of the resource.
* The imageview must have the attribute app:resource set to the name of the resource to set.
* @param imageView is the imageview that will contain the drawable resource
* @param iconName is the name of the drawable resource
*/
@JvmStatic
@BindingAdapter(value = ["resource"])
fun setImageViewResource(imageView: ImageView, iconName: String?) {
/*TODO(aanorbel): Update to parse the icon name and set the correct icon.
* Remember to ignore icons generated when generated doing this.*/
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
/*TODO(aanorbel): Update to parse the icon name and set the correct icon.
* Remember to ignore icons generated when generated doing this.*/
/* TODO(aanorbel): Update to parse the icon name and set the correct icon.
* Remember to ignore icons generated when generated doing this.*/

imageView.setImageResource(R.drawable.ooni_empty_state)
}

}

@Inject
lateinit var preferenceManager: PreferenceManager

@Inject
lateinit var descriptorManager: TestDescriptorManager

aanorbel marked this conversation as resolved.
Show resolved Hide resolved
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
activityComponent.inject(this)
val binding = ActivityAddDescriptorBinding.inflate(layoutInflater)
setContentView(binding.root)
setSupportActionBar(binding.toolbar)
supportActionBar?.setDisplayHomeAsUpEnabled(false)
supportActionBar?.setDisplayShowHomeEnabled(false)
supportActionBar?.title = "Add New Link"
val descriptorExtra = if (VERSION.SDK_INT >= VERSION_CODES.TIRAMISU) {
intent.getParcelableExtra(DESCRIPTOR, OONIRunDescriptor::class.java)
} else {
intent.getSerializableExtra(DESCRIPTOR) as OONIRunDescriptor?
}
val viewModel: AddDescriptorViewModel by viewModels {
object : ViewModelProvider.Factory {
override fun <T : ViewModel> create(modelClass: Class<T>): T {
return AddDescriptorViewModel(descriptorManager) as T
}
}
}
binding.viewmodel = viewModel
binding.lifecycleOwner = this
descriptorExtra?.let { descriptor ->
viewModel.onDescriptorChanged(descriptor)
val adapter = AddDescriptorExpandableListAdapter(
nettests = descriptor.nettests.map { nettest ->
GroupedItem(
name = nettest.name,
inputs = nettest.inputs,
selected = true
)
},
viewModel = viewModel
)
binding.expandableListView.setAdapter(adapter)
for (i in 0 until adapter.groupCount) {
binding.expandableListView.expandGroup(i)
}
val bottomBarOnMenuItemClickListener: Toolbar.OnMenuItemClickListener =
Toolbar.OnMenuItemClickListener { item ->
when (item.itemId) {
R.id.add_descriptor -> {
viewModel.onAddButtonClicked(
selectedNettest = adapter.nettests.filter { it.selected },
automatedUpdates = binding.automaticUpdatesSwitch.isChecked
)
true
}

else -> false
}
}
binding.bottomBar.setOnMenuItemClickListener(bottomBarOnMenuItemClickListener)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe split this long block of code by adding blank lines and brief comments?


viewModel.selectedAllBtnStatus.observe(this) { state ->
binding.testsCheckbox.checkedState = state;
}

// This observer is used to change the state of the "Select All" button when a checkbox is clicked.
binding.testsCheckbox.addOnCheckedStateChangedListener { checkBox, state ->
viewModel.setSelectedAllBtnStatus(state)
adapter.notifyDataSetChanged()
}

// This observer is used to finish the activity when the descriptor is added.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This kind of documentation is much appreciated! I would like to encourage you to do add more documentation like this one especially when writing new code!

viewModel.finishActivity.observe(this) { shouldFinish ->
if (shouldFinish) {
finish()
}
}
} ?: run {
finish()
}

}

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 -> {
finish()
true
}

else -> super.onOptionsItemSelected(item)
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
package org.openobservatory.ooniprobe.activity.add_descriptor

import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewmodel.ViewModelFactoryDsl
import com.google.android.material.checkbox.MaterialCheckBox
import com.google.android.material.checkbox.MaterialCheckBox.CheckedState
import org.openobservatory.engine.OONIRunDescriptor
import org.openobservatory.ooniprobe.activity.add_descriptor.adapter.GroupedItem
import org.openobservatory.ooniprobe.common.LocaleUtils
import org.openobservatory.ooniprobe.common.TestDescriptorManager
import javax.inject.Inject

/**
* ViewModel for the AddDescriptorActivity. This class is responsible for preparing and managing the data for the AddDescriptorActivity.
* It handles the communication of the Activity with the rest of the application (e.g. calling business logic classes).
*
* @property descriptorManager Instance of TestDescriptorManager which is responsible for managing the test descriptors.
* @property selectedAllBtnStatus LiveData holding the state of the "Select All" button in the UI.
* @property descriptor LiveData holding the OONIRunDescriptor object that the user is currently interacting with in the UI.
*/
class AddDescriptorViewModel constructor(
aanorbel marked this conversation as resolved.
Show resolved Hide resolved
var descriptorManager: TestDescriptorManager
) : ViewModel() {
@CheckedState
val selectedAllBtnStatus: MutableLiveData<Int> =
MutableLiveData(MaterialCheckBox.STATE_CHECKED)
var descriptor: MutableLiveData<OONIRunDescriptor> = MutableLiveData()
val finishActivity: MutableLiveData<Boolean> = MutableLiveData()

/**
* This method is called when the activity is created.
* It sets the descriptor value of this ViewModel.
* @param descriptor is the new descriptor
*/
fun onDescriptorChanged(descriptor: OONIRunDescriptor) {
aanorbel marked this conversation as resolved.
Show resolved Hide resolved
this.descriptor.value = descriptor
}

/**
* This method is used to get the name of the descriptor.
* Used by the UI during data binding.
* @return the name of the descriptor.
*/
fun getName(): String {
return descriptor.value?.let { descriptor ->
descriptor.nameIntl[LocaleUtils.sLocale.language] ?: descriptor.name
} ?: ""
}

/**
* This method is used to get the name of the descriptor.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
* This method is used to get the name of the descriptor.
* This method is used to get the description of the descriptor.

* Used by the UI during data binding.
* @return the name of the descriptor.
*/
fun getDescription(): String {
return descriptor.value?.let { descriptor ->
descriptor.descriptionIntl[LocaleUtils.sLocale.language] ?: descriptor.description
} ?: ""
}

/**
* This method is used to get the short description of the descriptor.
* Used by the UI during data binding.
* @return the short description of the descriptor.
*/
fun getShortDescription(): String {
return descriptor.value?.let { descriptor ->
descriptor.shortDescriptionIntl[LocaleUtils.sLocale.language]
?: descriptor.shortDescription
} ?: ""
}

/**
* This method is used to set the state of the "Select All" button in the UI.
* @param selectedStatus is the new state of the "Select All" button.
*/
fun setSelectedAllBtnStatus(@CheckedState selectedStatus: Int) {
selectedAllBtnStatus.postValue(selectedStatus)
}


/**
* This method is called when the "Add Link" button is clicked.
* It adds the descriptor to the descriptor manager and signals that the activity should finish.
* @param selectedNettest is the list of selected nettests.
* @param automatedUpdates is a boolean indicating whether automated updates should be enabled.
*/
fun onAddButtonClicked(selectedNettest: List<GroupedItem>, automatedUpdates: Boolean) {
descriptor.value?.let { descriptor ->
descriptorManager.addDescriptor(
descriptor = descriptor.apply {
nettests = selectedNettest.filter { it.selected }
},
automatedUpdates = automatedUpdates
).also {
finishActivity()
}
} ?: throw IllegalStateException("Descriptor is null")
}

/**
* This method is used to signal that the activity should finish.
*/
fun finishActivity() {
finishActivity.value = true
}
}
Loading
Loading