Loading src/com/android/documentsui/JobPanelController.kt +8 −2 Original line number Diff line number Diff line Loading @@ -40,6 +40,7 @@ import androidx.recyclerview.widget.RecyclerView import com.android.documentsui.base.Menus import com.android.documentsui.services.FileOperationService import com.android.documentsui.services.FileOperationService.EXTRA_PROGRESS import com.android.documentsui.services.FileOperations import com.android.documentsui.services.Job import com.android.documentsui.services.JobProgress import com.android.documentsui.util.FormatUtils Loading Loading @@ -129,6 +130,7 @@ class JobPanelController(private val activityContext: Context) : BroadcastReceiv cancelButton.isVisible = expanded && !jobProgress.isFinal showInFolderButton.isVisible = expanded && jobProgress.isFinal dismissButton.isVisible = expanded && jobProgress.isFinal cancelButton.setOnClickListener { FileOperations.cancel(context, jobProgress.id) } dismissButton.setOnClickListener { controller.dismissProgress(jobProgress.id) } } Loading Loading @@ -367,8 +369,12 @@ class JobPanelController(private val activityContext: Context) : BroadcastReceiv for (jobProgress in progresses) { Log.d(TAG, "Received $jobProgress") currentJobs.merge(jobProgress.id, ProgressViewModel(jobProgress)) { old, new -> ProgressViewModel(new.jobProgress, old.expanded) if (jobProgress.state == Job.STATE_CANCELED) { currentJobs.remove(jobProgress.id) } else { currentJobs.merge(jobProgress.id, ProgressViewModel(jobProgress)) { old, new -> ProgressViewModel(new.jobProgress, old.expanded) } } } for ((jobProgress, _) in currentJobs.values) { Loading src/com/android/documentsui/services/FileOperations.java +11 −7 Original line number Diff line number Diff line Loading @@ -17,18 +17,18 @@ package com.android.documentsui.services; import static android.os.SystemClock.elapsedRealtime; import static com.android.documentsui.base.SharedMinimal.DEBUG; import static com.android.documentsui.services.FileOperationService.EXTRA_CANCEL; import static com.android.documentsui.services.FileOperationService.EXTRA_JOB_ID; import static com.android.documentsui.services.FileOperationService.EXTRA_OPERATION; import androidx.annotation.IntDef; import android.app.Activity; import android.content.Context; import android.content.Intent; import androidx.annotation.VisibleForTesting; import android.util.Log; import androidx.annotation.IntDef; import com.android.documentsui.services.FileOperationService.OpType; import java.lang.annotation.Retention; Loading Loading @@ -74,17 +74,21 @@ public final class FileOperations { return newJobId; } @VisibleForTesting public static void cancel(Activity activity, String jobId) { /** * Tries to cancel a given operation by its job id. * @param context * @param jobId The job to cancel. */ public static void cancel(Context context, String jobId) { if (DEBUG) { Log.d(TAG, "Attempting to canceling operation: " + jobId); } Intent intent = new Intent(activity, FileOperationService.class); Intent intent = new Intent(context, FileOperationService.class); intent.putExtra(EXTRA_CANCEL, true); intent.putExtra(EXTRA_JOB_ID, jobId); activity.startService(intent); context.startService(intent); } /** Loading tests/functional/com/android/documentsui/JobPanelUiTest.kt +59 −8 Original line number Diff line number Diff line Loading @@ -21,6 +21,7 @@ import android.platform.test.flag.junit.CheckFlagsRule import android.platform.test.flag.junit.DeviceFlagsValueProvider import android.view.View import android.widget.ProgressBar import androidx.test.espresso.Espresso import androidx.test.espresso.Espresso.onView import androidx.test.espresso.action.ViewActions.click import androidx.test.espresso.assertion.ViewAssertions.doesNotExist Loading Loading @@ -84,6 +85,13 @@ class JobPanelUiTest : ActivityTestJunit4<FilesActivity>() { context.sendBroadcast(intent) } private fun openPanel() { onView(withId(R.id.option_menu_job_progress)) .check(matches(isDisplayed())) .perform(click()) onView(withId(R.id.job_progress_panel_title)).check(matches(isDisplayed())) } @Test fun testInProgressItems() { onView(withId(R.id.option_menu_job_progress)).check(doesNotExist()) Loading @@ -101,10 +109,7 @@ class JobPanelUiTest : ActivityTestJunit4<FilesActivity>() { ) sendProgress(arrayListOf(progress.toJobProgress())) onView(withId(R.id.option_menu_job_progress)) .check(matches(isDisplayed())) .perform(click()) onView(withId(R.id.job_progress_panel_title)).check(matches(isDisplayed())) openPanel() val expectedPrimaryStatus = "4 B of 10 B" val expectedSecondaryStatus = "10 seconds left" Loading Loading @@ -144,10 +149,7 @@ class JobPanelUiTest : ActivityTestJunit4<FilesActivity>() { ) sendProgress(arrayListOf(progress1.toJobProgress(), progress2.toJobProgress())) onView(withId(R.id.option_menu_job_progress)) .check(matches(isDisplayed())) .perform(click()) onView(withId(R.id.job_progress_panel_title)).check(matches(isDisplayed())) openPanel() onView(withChild(withText(progress1.msg))) .check(selectedDescendantsMatch( Loading Loading @@ -185,4 +187,53 @@ class JobPanelUiTest : ActivityTestJunit4<FilesActivity>() { onView(withId(R.id.option_menu_job_progress)).check(doesNotExist()) onView(withId(R.id.job_progress_panel_title)).check(doesNotExist()) } @Test fun testJobCanceled() { val progress1 = MutableJobProgress( id = "jobId1", operationType = FileOperationService.OPERATION_COPY, state = Job.STATE_SET_UP, msg = "Job1", hasFailures = false, currentBytes = 10, requiredBytes = 20, msRemaining = 1000, ) val progress2 = MutableJobProgress( id = "jobId2", operationType = FileOperationService.OPERATION_EXTRACT, state = Job.STATE_CREATED, msg = "Job2", hasFailures = false, ) sendProgress(arrayListOf(progress1.toJobProgress(), progress2.toJobProgress())) // Overall progress should be 25%. onView(withId(R.id.option_menu_job_progress)).check(matches(withProgress(25))) openPanel() // Check both items are displayed. onView(withText(progress1.msg)).check(matches(isDisplayed())) onView(withText(progress2.msg)).check(matches(isDisplayed())) // Cancel the first job. Only the second item should be displayed. progress1.state = Job.STATE_CANCELED sendProgress(arrayListOf(progress1.toJobProgress(), progress2.toJobProgress())) onView(withText(progress1.msg)).check(doesNotExist()) onView(withText(progress2.msg)).check(matches(isDisplayed())) // Overall progress should be 0% as the first job doesn't count. We need to close the popup // panel first in order to check the menu item behind. Espresso.pressBack() onView(withId(R.id.option_menu_job_progress)).check(matches(withProgress(0))) openPanel() // Cancel the second job. The panel should disappear. progress2.state = Job.STATE_CANCELED sendProgress(arrayListOf(progress2.toJobProgress())) onView(withId(R.id.option_menu_job_progress)).check(doesNotExist()) onView(withId(R.id.job_progress_panel_title)).check(doesNotExist()) } } Loading
src/com/android/documentsui/JobPanelController.kt +8 −2 Original line number Diff line number Diff line Loading @@ -40,6 +40,7 @@ import androidx.recyclerview.widget.RecyclerView import com.android.documentsui.base.Menus import com.android.documentsui.services.FileOperationService import com.android.documentsui.services.FileOperationService.EXTRA_PROGRESS import com.android.documentsui.services.FileOperations import com.android.documentsui.services.Job import com.android.documentsui.services.JobProgress import com.android.documentsui.util.FormatUtils Loading Loading @@ -129,6 +130,7 @@ class JobPanelController(private val activityContext: Context) : BroadcastReceiv cancelButton.isVisible = expanded && !jobProgress.isFinal showInFolderButton.isVisible = expanded && jobProgress.isFinal dismissButton.isVisible = expanded && jobProgress.isFinal cancelButton.setOnClickListener { FileOperations.cancel(context, jobProgress.id) } dismissButton.setOnClickListener { controller.dismissProgress(jobProgress.id) } } Loading Loading @@ -367,8 +369,12 @@ class JobPanelController(private val activityContext: Context) : BroadcastReceiv for (jobProgress in progresses) { Log.d(TAG, "Received $jobProgress") currentJobs.merge(jobProgress.id, ProgressViewModel(jobProgress)) { old, new -> ProgressViewModel(new.jobProgress, old.expanded) if (jobProgress.state == Job.STATE_CANCELED) { currentJobs.remove(jobProgress.id) } else { currentJobs.merge(jobProgress.id, ProgressViewModel(jobProgress)) { old, new -> ProgressViewModel(new.jobProgress, old.expanded) } } } for ((jobProgress, _) in currentJobs.values) { Loading
src/com/android/documentsui/services/FileOperations.java +11 −7 Original line number Diff line number Diff line Loading @@ -17,18 +17,18 @@ package com.android.documentsui.services; import static android.os.SystemClock.elapsedRealtime; import static com.android.documentsui.base.SharedMinimal.DEBUG; import static com.android.documentsui.services.FileOperationService.EXTRA_CANCEL; import static com.android.documentsui.services.FileOperationService.EXTRA_JOB_ID; import static com.android.documentsui.services.FileOperationService.EXTRA_OPERATION; import androidx.annotation.IntDef; import android.app.Activity; import android.content.Context; import android.content.Intent; import androidx.annotation.VisibleForTesting; import android.util.Log; import androidx.annotation.IntDef; import com.android.documentsui.services.FileOperationService.OpType; import java.lang.annotation.Retention; Loading Loading @@ -74,17 +74,21 @@ public final class FileOperations { return newJobId; } @VisibleForTesting public static void cancel(Activity activity, String jobId) { /** * Tries to cancel a given operation by its job id. * @param context * @param jobId The job to cancel. */ public static void cancel(Context context, String jobId) { if (DEBUG) { Log.d(TAG, "Attempting to canceling operation: " + jobId); } Intent intent = new Intent(activity, FileOperationService.class); Intent intent = new Intent(context, FileOperationService.class); intent.putExtra(EXTRA_CANCEL, true); intent.putExtra(EXTRA_JOB_ID, jobId); activity.startService(intent); context.startService(intent); } /** Loading
tests/functional/com/android/documentsui/JobPanelUiTest.kt +59 −8 Original line number Diff line number Diff line Loading @@ -21,6 +21,7 @@ import android.platform.test.flag.junit.CheckFlagsRule import android.platform.test.flag.junit.DeviceFlagsValueProvider import android.view.View import android.widget.ProgressBar import androidx.test.espresso.Espresso import androidx.test.espresso.Espresso.onView import androidx.test.espresso.action.ViewActions.click import androidx.test.espresso.assertion.ViewAssertions.doesNotExist Loading Loading @@ -84,6 +85,13 @@ class JobPanelUiTest : ActivityTestJunit4<FilesActivity>() { context.sendBroadcast(intent) } private fun openPanel() { onView(withId(R.id.option_menu_job_progress)) .check(matches(isDisplayed())) .perform(click()) onView(withId(R.id.job_progress_panel_title)).check(matches(isDisplayed())) } @Test fun testInProgressItems() { onView(withId(R.id.option_menu_job_progress)).check(doesNotExist()) Loading @@ -101,10 +109,7 @@ class JobPanelUiTest : ActivityTestJunit4<FilesActivity>() { ) sendProgress(arrayListOf(progress.toJobProgress())) onView(withId(R.id.option_menu_job_progress)) .check(matches(isDisplayed())) .perform(click()) onView(withId(R.id.job_progress_panel_title)).check(matches(isDisplayed())) openPanel() val expectedPrimaryStatus = "4 B of 10 B" val expectedSecondaryStatus = "10 seconds left" Loading Loading @@ -144,10 +149,7 @@ class JobPanelUiTest : ActivityTestJunit4<FilesActivity>() { ) sendProgress(arrayListOf(progress1.toJobProgress(), progress2.toJobProgress())) onView(withId(R.id.option_menu_job_progress)) .check(matches(isDisplayed())) .perform(click()) onView(withId(R.id.job_progress_panel_title)).check(matches(isDisplayed())) openPanel() onView(withChild(withText(progress1.msg))) .check(selectedDescendantsMatch( Loading Loading @@ -185,4 +187,53 @@ class JobPanelUiTest : ActivityTestJunit4<FilesActivity>() { onView(withId(R.id.option_menu_job_progress)).check(doesNotExist()) onView(withId(R.id.job_progress_panel_title)).check(doesNotExist()) } @Test fun testJobCanceled() { val progress1 = MutableJobProgress( id = "jobId1", operationType = FileOperationService.OPERATION_COPY, state = Job.STATE_SET_UP, msg = "Job1", hasFailures = false, currentBytes = 10, requiredBytes = 20, msRemaining = 1000, ) val progress2 = MutableJobProgress( id = "jobId2", operationType = FileOperationService.OPERATION_EXTRACT, state = Job.STATE_CREATED, msg = "Job2", hasFailures = false, ) sendProgress(arrayListOf(progress1.toJobProgress(), progress2.toJobProgress())) // Overall progress should be 25%. onView(withId(R.id.option_menu_job_progress)).check(matches(withProgress(25))) openPanel() // Check both items are displayed. onView(withText(progress1.msg)).check(matches(isDisplayed())) onView(withText(progress2.msg)).check(matches(isDisplayed())) // Cancel the first job. Only the second item should be displayed. progress1.state = Job.STATE_CANCELED sendProgress(arrayListOf(progress1.toJobProgress(), progress2.toJobProgress())) onView(withText(progress1.msg)).check(doesNotExist()) onView(withText(progress2.msg)).check(matches(isDisplayed())) // Overall progress should be 0% as the first job doesn't count. We need to close the popup // panel first in order to check the menu item behind. Espresso.pressBack() onView(withId(R.id.option_menu_job_progress)).check(matches(withProgress(0))) openPanel() // Cancel the second job. The panel should disappear. progress2.state = Job.STATE_CANCELED sendProgress(arrayListOf(progress2.toJobProgress())) onView(withId(R.id.option_menu_job_progress)).check(doesNotExist()) onView(withId(R.id.job_progress_panel_title)).check(doesNotExist()) } }