Skip to content

Commit

Permalink
Add progress notifications and snakbar for opening review updates
Browse files Browse the repository at this point in the history
  • Loading branch information
aanorbel committed Jan 23, 2024
1 parent 5de1da3 commit deb078c
Show file tree
Hide file tree
Showing 5 changed files with 99 additions and 126 deletions.
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package org.openobservatory.ooniprobe.activity;

import static org.openobservatory.ooniprobe.common.service.RunTestService.CHANNEL_ID;
import static org.openobservatory.ooniprobe.common.worker.UpdateDescriptorsWorkerKt.PROGRESS;

import android.Manifest;
import android.content.Context;
Expand All @@ -17,25 +18,24 @@

import androidx.activity.result.ActivityResultLauncher;
import androidx.activity.result.contract.ActivityResultContracts;
import androidx.annotation.IdRes;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatDelegate;
import androidx.core.content.ContextCompat;
import androidx.fragment.app.Fragment;
import androidx.lifecycle.Observer;
import androidx.work.Constraints;
import androidx.work.Data;
import androidx.work.ExistingPeriodicWorkPolicy;
import androidx.work.ExistingWorkPolicy;
import androidx.work.NetworkType;
import androidx.work.OneTimeWorkRequest;
import androidx.work.PeriodicWorkRequest;
import androidx.work.WorkInfo;
import androidx.work.WorkManager;
import androidx.work.WorkRequest;

import com.google.android.material.snackbar.Snackbar;
import com.google.common.base.Optional;

import org.openobservatory.engine.OONIRunDescriptor;
import org.openobservatory.ooniprobe.R;
import org.openobservatory.ooniprobe.activity.adddescriptor.AddDescriptorActivity;
import org.openobservatory.ooniprobe.activity.reviewdescriptorupdates.ReviewDescriptorUpdatesActivity;
Expand Down Expand Up @@ -195,21 +195,65 @@ private void scheduleWorkers() {

WorkManager.getInstance(this)
.getWorkInfoByIdLiveData(manualWorkRequest.getId())
.observe(this, workInfo -> {
if (workInfo.getState().isFinished()) {
if (workInfo.getState() == WorkInfo.State.SUCCEEDED) {
System.out.println(workInfo.getOutputData());
startActivity(
ReviewDescriptorUpdatesActivity.newIntent(
this,
workInfo.getOutputData().getString(ManualUpdateDescriptorsWorker.KEY_UPDATED_DESCRIPTORS)
)
);
} else {
System.out.println("Failed");
}
}
});
.observe(this, this::onManualUpdatesFetchComplete);
}


/**
* Listens to updates from the {@link ManualUpdateDescriptorsWorker}.
* <p>
* This method is called after the {@link ManualUpdateDescriptorsWorker} is enqueued.
* The {@link ManualUpdateDescriptorsWorker} task is to fetch updates for the descriptors.
* <p>
* If the task is successful, the {@link WorkInfo} object will contain the updated descriptors.
* Otherwise, the {@link WorkInfo} object will be null.
*
* @param workInfo The {@link WorkInfo} of the task.
*/
private void onManualUpdatesFetchComplete(WorkInfo workInfo) {
if (workInfo != null) {
binding.reviewUpdateNotificationFragment.setVisibility(View.VISIBLE);
switch (workInfo.getState()) {
case SUCCEEDED -> getSupportFragmentManager()
.beginTransaction()
.add(
R.id.review_update_notification_fragment,
OONIRunDynamicProgressBar.newInstance(ProgressType.REVIEW_LINK, new OnActionListener() {
@Override
public void onActionButtonCLicked() {

startActivity(
ReviewDescriptorUpdatesActivity.newIntent(
MainActivity.this,
workInfo.getOutputData().getString(ManualUpdateDescriptorsWorker.KEY_UPDATED_DESCRIPTORS)
)
);
removeProgressFragment(R.id.review_update_notification_fragment);
}

@Override
public void onCloseButtonClicked() {
removeProgressFragment(R.id.review_update_notification_fragment);
}
}),
OONIRunDynamicProgressBar.getTAG() + "_review_update_success_notification"
).commit();
case ENQUEUED -> getSupportFragmentManager()
.beginTransaction()
.add(
R.id.review_update_notification_fragment,
OONIRunDynamicProgressBar.newInstance(ProgressType.UPDATE_LINK, null),
OONIRunDynamicProgressBar.getTAG() + "_review_update_enqueued_notification"
).commit();
case FAILED -> Snackbar.make(
binding.getRoot(),
R.string.Modal_Error,
Snackbar.LENGTH_LONG
).setAnchorView(binding.bottomNavigation).show();
default -> {
}
}
}
}

private void requestNotificationPermission() {
Expand Down Expand Up @@ -335,7 +379,7 @@ private Unit fetchDescriptorComplete(TestDescriptor descriptorResponse) {
.setAnchorView(binding.bottomNavigation) // NOTE:To avoid the `snackbar` from covering the bottom navigation.
.show();
}
removeProgressFragment();
removeProgressFragment(R.id.dynamic_progress_fragment);
return Unit.INSTANCE;

}
Expand All @@ -357,12 +401,12 @@ private void displayAddLinkProgressFragment(TaskExecutor executor) {
@Override
public void onActionButtonCLicked() {
executor.cancelTask();
removeProgressFragment();
removeProgressFragment(R.id.dynamic_progress_fragment);
}

@Override
public void onCloseButtonClicked() {
removeProgressFragment();
removeProgressFragment(R.id.dynamic_progress_fragment);
}
}),
OONIRunDynamicProgressBar.getTAG()
Expand Down Expand Up @@ -427,12 +471,12 @@ private Optional<Long> getRunId(Uri uri) {
* <p>
* This method is called when the task is completed.
*/
private void removeProgressFragment() {
Fragment fragment = getSupportFragmentManager().findFragmentByTag(OONIRunDynamicProgressBar.getTAG());
private void removeProgressFragment(@IdRes int id) {
Fragment fragment = getSupportFragmentManager().findFragmentById(id);
if (fragment != null && fragment.isAdded()) {
getSupportFragmentManager().beginTransaction().remove(fragment).commit();
}
binding.dynamicProgressFragment.setVisibility(View.GONE);
findViewById(id).setVisibility(View.GONE);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
@@ -1,19 +1,10 @@
package org.openobservatory.ooniprobe.common.worker

import android.Manifest
import android.app.NotificationChannel
import android.app.NotificationManager
import android.content.Context
import android.content.pm.PackageManager
import android.os.Build
import android.util.Log
import androidx.core.app.ActivityCompat
import androidx.core.app.NotificationCompat
import androidx.core.app.NotificationManagerCompat
import androidx.work.Data
import androidx.work.Worker
import androidx.work.WorkerParameters
import org.openobservatory.ooniprobe.R
import org.openobservatory.ooniprobe.common.Application
import org.openobservatory.ooniprobe.common.PreferenceManager
import org.openobservatory.ooniprobe.common.TestDescriptorManager
Expand All @@ -23,6 +14,7 @@ import org.openobservatory.ooniprobe.model.database.shouldUpdate
import javax.inject.Inject

var d: UpdateDescriptorsWorkerDependencies = UpdateDescriptorsWorkerDependencies()
const val PROGRESS = "PROGRESS"

class AutoUpdateDescriptorsWorker(
context: Context,
Expand All @@ -35,22 +27,11 @@ class AutoUpdateDescriptorsWorker(
return try {
Log.d(TAG, "Fetching descriptors from input")

makeStatusNotification(
applicationContext = applicationContext,
message = "Starting update for descriptors",
channelName = UPDATE_DESCRIPTOR_CHANNEL
)
val updatedDescriptors: ArrayList<TestDescriptor> = ArrayList()

for (descriptor in d.testDescriptorManager.getDescriptorWithAutoUpdateEnabled()) {
Log.d(TAG, "Fetching updates for ${descriptor.runId}")

makeStatusNotification(
applicationContext = applicationContext,
message = "Fetching updates for ${descriptor.name}",
channelName = UPDATE_DESCRIPTOR_CHANNEL
)

val updatedDescriptor: TestDescriptor =
d.testDescriptorManager.fetchDescriptorFromRunId(
descriptor.runId,
Expand All @@ -62,11 +43,6 @@ class AutoUpdateDescriptorsWorker(
updatedDescriptor.isAutoRun = descriptor.isAutoRun

Log.d(TAG, "Saving updates for ${descriptor.runId}")
makeStatusNotification(
applicationContext = applicationContext,
message = "Saving updates for ${descriptor.name}",
channelName = UPDATE_DESCRIPTOR_CHANNEL
)

updatedDescriptor.save()
updatedDescriptors.add(updatedDescriptor)
Expand All @@ -79,21 +55,12 @@ class AutoUpdateDescriptorsWorker(
(applicationContext as Application).gson.toJson(updatedDescriptors)
).build()

makeStatusNotification(
applicationContext = applicationContext,
message = "Descriptor updates complete",
channelName = UPDATE_DESCRIPTOR_CHANNEL
)
Log.e(TAG, "Descriptor updates complete")

Result.success(outputData)

} catch (exception: Exception) {
Log.e(TAG, "Error Updating")
makeStatusNotification(
applicationContext = applicationContext,
message = "Error Updating",
channelName = UPDATE_DESCRIPTOR_CHANNEL
)
exception.printStackTrace()
ThirdPartyServices.logException(exception)
Result.failure()
Expand All @@ -120,55 +87,48 @@ class ManualUpdateDescriptorsWorker(
workerParams: WorkerParameters
) : Worker(context, workerParams) {

init {
setProgressAsync(Data.Builder().putInt(PROGRESS, 0).build())
}

override fun doWork(): Result {
val app = applicationContext.applicationContext as Application
app.serviceComponent.inject(d)

return try {
Log.d(TAG, "Fetching descriptors from input")
makeStatusNotification(
applicationContext = applicationContext,
message = "Starting update for descriptors",
channelName = UPDATE_DESCRIPTOR_CHANNEL
)

val updatedDescriptors: ArrayList<TestDescriptor> = ArrayList()

for (descriptor in d.testDescriptorManager.getDescriptorWithAutoUpdateDisabled()) {
Log.d(TAG, "Fetching updates for ${descriptor.runId}")
makeStatusNotification(
applicationContext = applicationContext,
message = "Fetching updates for ${descriptor.name}",
channelName = UPDATE_DESCRIPTOR_CHANNEL
)

val updatedDescriptor: TestDescriptor =
d.testDescriptorManager.fetchDescriptorFromRunId(
descriptor.runId,
applicationContext
)
updatedDescriptors.add(updatedDescriptor)
/**
* NOTE(aanorbel): Refine this logic to only update if the descriptor has changed.
* Consider explicit version compare.
*/
if (descriptor.shouldUpdate(updatedDescriptor)) {
updatedDescriptors.add(updatedDescriptor)
}
}
val outputData = Data.Builder()
.putString(
KEY_UPDATED_DESCRIPTORS,
(applicationContext as Application).gson.toJson(updatedDescriptors)
).build()
makeStatusNotification(
applicationContext = applicationContext,
message = "fetching updates complete",
channelName = UPDATE_DESCRIPTOR_CHANNEL
)

Log.e(TAG, "fetching updates complete")

setProgressAsync(Data.Builder().putInt(PROGRESS, 100).build())
Result.success(outputData)

} catch (exception: Exception) {
Log.e(TAG, "Error Updating")
makeStatusNotification(
applicationContext = applicationContext,
message = "Error Updating",
channelName = UPDATE_DESCRIPTOR_CHANNEL
)
exception.printStackTrace()
ThirdPartyServices.logException(exception)
Result.failure()
Expand All @@ -184,6 +144,7 @@ class ManualUpdateDescriptorsWorker(

private val UPDATE_DESCRIPTOR_CHANNEL: String =
AutoUpdateDescriptorsWorker::class.java.simpleName

@JvmField
var KEY_UPDATED_DESCRIPTORS =
"${AutoUpdateDescriptorsWorker::class.java.name}.KEY_UPDATED_DESCRIPTORS"
Expand All @@ -197,48 +158,3 @@ class UpdateDescriptorsWorkerDependencies {
@Inject
lateinit var preferenceManager: PreferenceManager
}

private fun makeStatusNotification(
applicationContext: Context,
title: String = "Descriptor Update",
message: String,
channelName: String
) {

// Make a channel if necessary
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
// Create the NotificationChannel, but only on API 26+ because
// the NotificationChannel class is new and not in the support library
val importance: Int = NotificationManager.IMPORTANCE_HIGH
val channel =
NotificationChannel(channelName, "Run Descriptor Updates", importance)
channel.description =
"Shows notification related to updates being made to Run Descriptors"

// Add the channel
val notificationManager: NotificationManager = applicationContext.getSystemService(
Context.NOTIFICATION_SERVICE
) as NotificationManager
notificationManager.createNotificationChannel(channel)
}

// Create the notification
val builder: NotificationCompat.Builder =
NotificationCompat.Builder(applicationContext, channelName)
.setSmallIcon(R.drawable.notification_icon)
.setContentTitle(title)
.setContentText(message)
.setPriority(NotificationCompat.PRIORITY_LOW)
.setVibrate(LongArray(0))

// Show the notification
if (ActivityCompat.checkSelfPermission(
applicationContext,
Manifest.permission.POST_NOTIFICATIONS
) != PackageManager.PERMISSION_GRANTED
) {
return
}
NotificationManagerCompat.from(applicationContext)
.notify(System.currentTimeMillis().toInt(), builder.build())
}
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ class OONIRunDynamicProgressBar : Fragment() {
ProgressType.REVIEW_LINK -> {
binding.progressBar.visibility = View.GONE
binding.iconButton.visibility = View.VISIBLE
binding.actionButton.text = "Review"
binding.progressText.text = "Link updates ready"
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,12 @@ class TestDescriptor(
}
}

/**
* Check if the descriptor should be updated based on the creation time of the descriptor and the creation time of the translations.
*
* @param updatedDescriptor The updated descriptor
* @return True if the descriptor should be updated based on the creation time of the descriptor and the creation time of the translations.
*/
fun TestDescriptor.shouldUpdate(updatedDescriptor: TestDescriptor): Boolean {
return (updatedDescriptor.descriptorCreationTime?.after(descriptorCreationTime) ?: true
|| updatedDescriptor.translationCreationTime?.after(translationCreationTime) ?: true)
Expand Down Expand Up @@ -213,7 +219,7 @@ class ITestDescriptor(
var translationCreationTime: Date? = null,

var nettests: List<OONIRunNettest>? = emptyList()
): Serializable {
) : Serializable {
fun toTestDescriptor(): TestDescriptor {
return TestDescriptor(
runId = runId,
Expand Down
Loading

0 comments on commit deb078c

Please sign in to comment.