Skip to content

Commit

Permalink
Updated code based on review
Browse files Browse the repository at this point in the history
  • Loading branch information
aanorbel committed Dec 20, 2023
1 parent 5a42afb commit 44ea86d
Show file tree
Hide file tree
Showing 10 changed files with 439 additions and 246 deletions.
378 changes: 189 additions & 189 deletions app/src/main/AndroidManifest.xml

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
import android.provider.Settings;

import android.view.View;

import androidx.activity.result.ActivityResultLauncher;
import androidx.activity.result.contract.ActivityResultContracts;
import androidx.annotation.Nullable;
Expand All @@ -23,6 +24,7 @@

import com.google.android.material.snackbar.Snackbar;

import org.openobservatory.engine.OONIRunDescriptor;
import org.openobservatory.ooniprobe.R;
import org.openobservatory.ooniprobe.activity.add_descriptor.AddDescriptorActivity;
import org.openobservatory.ooniprobe.common.*;
Expand All @@ -40,6 +42,7 @@

import javax.inject.Inject;

import kotlin.Unit;
import localhost.toolkit.app.fragment.ConfirmDialogFragment;

public class MainActivity extends AbstractActivity implements ConfirmDialogFragment.OnConfirmedListener {
Expand Down Expand Up @@ -187,66 +190,34 @@ private void requestNotificationPermission() {
@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
// Check if we are starting the activity from a link [Intent.ACTION_VIEW].
if (Intent.ACTION_VIEW.equals(intent.getAction())) {
Uri uri = intent.getData();
if (uri == null) return;
// If the intent does not contain a link, do nothing.
if (uri == null) {
return;
}

String host = uri.getHost();
long runId = 0;
long runId = getRunId(uri);

try {
if ("runv2".equals(host)) {
runId = Long.parseLong(uri.getPathSegments().get(0));
} else if ("run.test.ooni.org".equals(host)) {
runId = Long.parseLong(uri.getPathSegments().get(1));
}
} catch (Exception e) {
e.printStackTrace();
// If the intent contains a link, but the `link_id` is zero, or the link is not supported, do nothing.
if (runId == 0) {
return;
}
if (runId == 0) return;

TaskExecutor executor = new TaskExecutor();

binding.dynamicProgressFragment.setVisibility(View.VISIBLE);
getSupportFragmentManager().beginTransaction()
.add(
R.id.dynamic_progress_fragment,
DynamicProgressFragment.newInstance(ProgressType.ADD_LINK, new OnActionListener() {
@Override
public void onActionButtonCLicked() {
executor.cancelTask();
removeProgressFragment();
}

@Override
public void onIconButtonClicked() {
removeProgressFragment();
}
}),
DynamicProgressFragment.getTAG()
).commit();
long finalRunId = runId;
executor.executeTask(
() -> {
try {
return descriptorManager.fetchDescriptorFromRunId(finalRunId, this);
} catch (Exception exception) {
exception.printStackTrace();
ThirdPartyServices.logException(exception);
return null;
}
},
descriptorResponse -> {
if (descriptorResponse != null) {
startActivity(AddDescriptorActivity.newIntent(this, descriptorResponse));
} else {
// TODO(aanorbel): Provide a better error message.
Snackbar.make(binding.getRoot(), R.string.Modal_Error, Snackbar.LENGTH_LONG)
.setAnchorView(binding.bottomNavigation) // NOTE:To avoid the `snackbar` from covering the bottom navigation.
.show();
}
removeProgressFragment();
return null;
});
displayAddLinkProgressFragment(executor);

executor.executeTask(() -> {
try {
return descriptorManager.fetchDescriptorFromRunId(runId, this);
} catch (Exception exception) {
exception.printStackTrace();
ThirdPartyServices.logException(exception);
return null;
}
}, this::fetchDescriptorComplete);
} else {
if (intent.getExtras() != null) {
if (intent.getExtras().containsKey(RES_ITEM))
Expand All @@ -263,6 +234,112 @@ else if (intent.getExtras().containsKey(NOTIFICATION_DIALOG)) {
}
}

/**
* The task to fetch the descriptor from the link is completed.
* <p>
* This method is called when the `fetchDescriptorFromRunId` task is completed.
* The `descriptorResponse` is the result of the task.
* If the task is successful, the `descriptorResponse` is the descriptor.
* Otherwise, the `descriptorResponse` is null.
* <p>
* If the `descriptorResponse` is not null, start the `AddDescriptorActivity`.
* Otherwise, show an error message.
*
* @param descriptorResponse The result of the task.
* @return null.
*/
private Unit fetchDescriptorComplete(OONIRunDescriptor descriptorResponse) {
if (descriptorResponse != null) {
startActivity(AddDescriptorActivity.newIntent(this, descriptorResponse));
} else {
// TODO(aanorbel): Provide a better error message.
Snackbar.make(binding.getRoot(), R.string.Modal_Error, Snackbar.LENGTH_LONG)
.setAnchorView(binding.bottomNavigation) // NOTE:To avoid the `snackbar` from covering the bottom navigation.
.show();
}
removeProgressFragment();
return Unit.INSTANCE;

}

/**
* Display the progress fragment.
* <p>
* The progress fragment is used to display the progress of the task.
* e.g. Fetching the descriptor from the link.
*
* @param executor The executor that will be used to execute the task.
*/
private void displayAddLinkProgressFragment(TaskExecutor executor) {
binding.dynamicProgressFragment.setVisibility(View.VISIBLE);
getSupportFragmentManager().beginTransaction()
.add(
R.id.dynamic_progress_fragment,
DynamicProgressFragment.newInstance(ProgressType.ADD_LINK, new OnActionListener() {
@Override
public void onActionButtonCLicked() {
executor.cancelTask();
removeProgressFragment();
}

@Override
public void onCloseButtonClicked() {
removeProgressFragment();
}
}),
DynamicProgressFragment.getTAG()
).commit();
}

/**
* Get the run id from the intent.
* The run id can be in two different formats.
* <p>
* 1. ooni://runv2/<link_id>
* 2. https://run.test.ooni.org/v2/<link_id>
* The run id is the `link_id` in the link.
* If the intent contains a link, but the `link_id` is not a number, return zero.
* If the intent contains a link, but it is not a supported link, return zero.
*
* @param uri The intent data.
* @return The run id if the intent contains a link with a valid `link_id`.
*/
private long getRunId(Uri uri) {
String host = uri.getHost();
long runId = 0;

try {
if ("runv2".equals(host)) {
/**
* The run id is the first segment of the path.
* Launched when `Open Link in OONI Probe` is clicked.
* e.g. ooni://runv2/<link_id>
*/
runId = Long.parseLong(uri.getPathSegments().get(0));
} else if ("run.test.ooni.org".equals(host)) {
/**
* The run id is the second segment of the path.
* Launched when the system recognizes this app can open this link
* and launches the app when a link is clicked.
* e.g. https://run.test.ooni.org/v2/<link_id>
*/
runId = Long.parseLong(uri.getPathSegments().get(1));
} else {
// If the intent contains a link, but it is not a supported link, return zero.
return 0;
}
} catch (Exception e) {
// If the intent contains a link, but the `link_id` is not a number.
e.printStackTrace();
}
return runId;
}

/**
* Remove the progress fragment.
* <p>
* This method is called when the task is completed.
*/
private void removeProgressFragment() {
Fragment fragment = getSupportFragmentManager().findFragmentByTag(DynamicProgressFragment.getTAG());
if (fragment != null && fragment.isAdded()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,20 @@ 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() {
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(
Expand Down Expand Up @@ -144,11 +154,13 @@ class AddDescriptorActivity : AbstractActivity() {
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.
viewModel.finishActivity.observe(this) { shouldFinish ->
if (shouldFinish) {
finish()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,14 @@ 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(
var descriptorManager: TestDescriptorManager
) : ViewModel() {
Expand All @@ -19,33 +27,65 @@ class AddDescriptorViewModel constructor(
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) {
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.
* 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(
Expand All @@ -59,7 +99,10 @@ class AddDescriptorViewModel constructor(
} ?: throw IllegalStateException("Descriptor is null")
}

/**
* This method is used to signal that the activity should finish.
*/
fun finishActivity() {
finishActivity.value = true
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,22 @@ import org.openobservatory.ooniprobe.R
import org.openobservatory.ooniprobe.activity.add_descriptor.AddDescriptorViewModel
import org.openobservatory.ooniprobe.test.test.AbstractTest


/**
* An extension of [OONIRunNettest] class
* used to track the selected state of nettests in the [ExpandableListView].
*/
class GroupedItem(
override var name: String,
override var inputs: List<String>?,
var selected: Boolean = false
) : OONIRunNettest(name = name, inputs = inputs)

/**
* Adapter class for the [ExpandableListView] in [AddDescriptorActivity].
* @param nettests List of GroupedItem objects.
* @param viewModel AddDescriptorViewModel object.
*/
class AddDescriptorExpandableListAdapter(
val nettests: List<GroupedItem>,
val viewModel: AddDescriptorViewModel
Expand Down
Loading

0 comments on commit 44ea86d

Please sign in to comment.