summaryrefslogtreecommitdiff
path: root/src/com
diff options
context:
space:
mode:
author Austin Tankiang <austinct@google.com> 2025-03-17 22:01:15 -0700
committer Android (Google) Code Review <android-gerrit@google.com> 2025-03-17 22:01:15 -0700
commit91bc3ee29efaba6a55bb1eb8e5cc6c214cde7ca0 (patch)
treec86525d4857ac85ab19f052f2875a424a0c974d5 /src/com
parent8f03bda5e1e45faa4c76a6f43da3fe7d91ab6283 (diff)
parent60ab4dfb4e8814d1da61ba450cd228b4771b84bf (diff)
Merge "Add job progress icon to the app bar" into main
Diffstat (limited to 'src/com')
-rw-r--r--src/com/android/documentsui/JobPanelController.kt126
-rw-r--r--src/com/android/documentsui/MenuManager.java9
-rw-r--r--src/com/android/documentsui/NavigationViewManager.java4
-rw-r--r--src/com/android/documentsui/files/MenuManager.java18
-rw-r--r--src/com/android/documentsui/services/FileOperationService.java10
-rw-r--r--src/com/android/documentsui/services/Job.java10
6 files changed, 169 insertions, 8 deletions
diff --git a/src/com/android/documentsui/JobPanelController.kt b/src/com/android/documentsui/JobPanelController.kt
new file mode 100644
index 000000000..b3b5f1cfd
--- /dev/null
+++ b/src/com/android/documentsui/JobPanelController.kt
@@ -0,0 +1,126 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.documentsui
+
+import android.content.BroadcastReceiver
+import android.content.Context
+import android.content.Intent
+import android.content.IntentFilter
+import android.util.Log
+import android.view.MenuItem
+import android.widget.ProgressBar
+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.Job
+import com.android.documentsui.services.JobProgress
+
+/**
+ * JobPanelController is responsible for receiving broadcast updates from the [FileOperationService]
+ * and updating a given menu item to reflect the current progress.
+ */
+class JobPanelController(private val mContext: Context) : BroadcastReceiver() {
+ companion object {
+ private const val TAG = "JobPanelController"
+ private const val MAX_PROGRESS = 100
+ }
+
+ private enum class State {
+ INVISIBLE, INDETERMINATE, VISIBLE
+ }
+
+ /** The current state of the menu progress item. */
+ private var mState = State.INVISIBLE
+
+ /** The total progress from 0 to MAX_PROGRESS. */
+ private var mTotalProgress = 0
+
+ /** List of jobs currently tracked by this class. */
+ private val mCurrentJobs = LinkedHashMap<String, JobProgress>()
+
+ /** Current menu item being controlled by this class. */
+ private var mMenuItem: MenuItem? = null
+
+ init {
+ val filter = IntentFilter(FileOperationService.ACTION_PROGRESS)
+ mContext.registerReceiver(this, filter, Context.RECEIVER_NOT_EXPORTED)
+ }
+
+ private fun updateMenuItem(animate: Boolean) {
+ mMenuItem?.let {
+ Menus.setEnabledAndVisible(it, mState != State.INVISIBLE)
+ val icon = it.actionView as ProgressBar
+ when (mState) {
+ State.INDETERMINATE -> icon.isIndeterminate = true
+ State.VISIBLE -> icon.apply {
+ isIndeterminate = false
+ setProgress(mTotalProgress, animate)
+ }
+ State.INVISIBLE -> {}
+ }
+ }
+ }
+
+ /**
+ * Sets the menu item controlled by this class. The item's actionView must be a [ProgressBar].
+ */
+ fun setMenuItem(menuItem: MenuItem) {
+ (menuItem.actionView as ProgressBar).max = MAX_PROGRESS
+ mMenuItem = menuItem
+ updateMenuItem(animate = false)
+ }
+
+ override fun onReceive(context: Context?, intent: Intent) {
+ val progresses = intent.getParcelableArrayListExtra<JobProgress>(
+ EXTRA_PROGRESS,
+ JobProgress::class.java
+ )
+ updateProgress(progresses!!)
+ }
+
+ private fun updateProgress(progresses: List<JobProgress>) {
+ var requiredBytes = 0L
+ var currentBytes = 0L
+ var allFinished = true
+
+ for (jobProgress in progresses) {
+ Log.d(TAG, "Received $jobProgress")
+ mCurrentJobs.put(jobProgress.id, jobProgress)
+ }
+ for (jobProgress in mCurrentJobs.values) {
+ if (jobProgress.state != Job.STATE_COMPLETED) {
+ allFinished = false
+ }
+ if (jobProgress.requiredBytes != -1L && jobProgress.currentBytes != -1L) {
+ requiredBytes += jobProgress.requiredBytes
+ currentBytes += jobProgress.currentBytes
+ }
+ }
+
+ if (mCurrentJobs.isEmpty()) {
+ mState = State.INVISIBLE
+ } else if (requiredBytes != 0L) {
+ mState = State.VISIBLE
+ mTotalProgress = (MAX_PROGRESS * currentBytes / requiredBytes).toInt()
+ } else if (allFinished) {
+ mState = State.VISIBLE
+ mTotalProgress = MAX_PROGRESS
+ } else {
+ mState = State.INDETERMINATE
+ }
+ updateMenuItem(animate = true)
+ }
+}
diff --git a/src/com/android/documentsui/MenuManager.java b/src/com/android/documentsui/MenuManager.java
index 888a45996..a46659c77 100644
--- a/src/com/android/documentsui/MenuManager.java
+++ b/src/com/android/documentsui/MenuManager.java
@@ -282,6 +282,15 @@ public abstract class MenuManager {
public abstract void updateKeyboardShortcutsMenu(
List<KeyboardShortcutGroup> data, IntFunction<String> stringSupplier);
+ /**
+ * Called on option menu creation to instantiate the job progress item if applicable.
+ *
+ * @param menu The option menu created.
+ */
+ public void instantiateJobProgress(Menu menu) {
+ // This icon is not shown in the picker.
+ }
+
protected void updateModePicker(MenuItem grid, MenuItem list) {
// The order of enabling disabling menu item in wrong order removed accessibility focus.
if (mState.derivedMode != State.MODE_LIST) {
diff --git a/src/com/android/documentsui/NavigationViewManager.java b/src/com/android/documentsui/NavigationViewManager.java
index 12afbd69b..bbae22d42 100644
--- a/src/com/android/documentsui/NavigationViewManager.java
+++ b/src/com/android/documentsui/NavigationViewManager.java
@@ -18,6 +18,7 @@ package com.android.documentsui;
import static com.android.documentsui.base.SharedMinimal.VERBOSE;
import static com.android.documentsui.util.FlagUtils.isUseMaterial3FlagEnabled;
+import static com.android.documentsui.util.FlagUtils.isVisualSignalsFlagEnabled;
import android.content.res.Resources;
import android.content.res.TypedArray;
@@ -409,6 +410,9 @@ public class NavigationViewManager extends SelectionTracker.SelectionObserver<St
mActivity.getResources().getBoolean(R.bool.full_bar_search_view);
boolean showSearchBar = mActivity.getResources().getBoolean(R.bool.show_search_bar);
mInjector.searchManager.install(mToolbar.getMenu(), fullBarSearch, showSearchBar);
+ if (isVisualSignalsFlagEnabled()) {
+ mInjector.menuManager.instantiateJobProgress(mToolbar.getMenu());
+ }
}
mInjector.menuManager.updateOptionMenu(mToolbar.getMenu());
mInjector.searchManager.showMenu(mState.stack);
diff --git a/src/com/android/documentsui/files/MenuManager.java b/src/com/android/documentsui/files/MenuManager.java
index 7dc6b57d6..2c85dcb01 100644
--- a/src/com/android/documentsui/files/MenuManager.java
+++ b/src/com/android/documentsui/files/MenuManager.java
@@ -17,6 +17,7 @@
package com.android.documentsui.files;
import static com.android.documentsui.util.FlagUtils.isDesktopFileHandlingFlagEnabled;
+import static com.android.documentsui.util.FlagUtils.isVisualSignalsFlagEnabled;
import android.content.Context;
import android.content.res.Resources;
@@ -30,9 +31,11 @@ import android.view.MenuItem;
import android.view.View;
import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import androidx.recyclerview.selection.SelectionTracker;
+import com.android.documentsui.JobPanelController;
import com.android.documentsui.R;
import com.android.documentsui.base.DocumentInfo;
import com.android.documentsui.base.Features;
@@ -56,6 +59,7 @@ public final class MenuManager extends com.android.documentsui.MenuManager {
private final SelectionTracker<String> mSelectionManager;
private final Lookup<String, Uri> mUriLookup;
private final LookupApplicationName mAppNameLookup;
+ @Nullable private final JobPanelController mJobPanelController;
public MenuManager(
Features features,
@@ -75,6 +79,12 @@ public final class MenuManager extends com.android.documentsui.MenuManager {
mSelectionManager = selectionManager;
mAppNameLookup = appNameLookup;
mUriLookup = uriLookup;
+
+ if (isVisualSignalsFlagEnabled()) {
+ mJobPanelController = new JobPanelController(context);
+ } else {
+ mJobPanelController = null;
+ }
}
@Override
@@ -142,6 +152,14 @@ public final class MenuManager extends com.android.documentsui.MenuManager {
}
@Override
+ public void instantiateJobProgress(Menu menu) {
+ if (mJobPanelController == null) {
+ return;
+ }
+ mJobPanelController.setMenuItem(menu.findItem(R.id.option_menu_job_progress));
+ }
+
+ @Override
protected void updateSettings(MenuItem settings, RootInfo root) {
Menus.setEnabledAndVisible(settings, root.hasSettings());
}
diff --git a/src/com/android/documentsui/services/FileOperationService.java b/src/com/android/documentsui/services/FileOperationService.java
index dcb2c1db4..14b87ef5b 100644
--- a/src/com/android/documentsui/services/FileOperationService.java
+++ b/src/com/android/documentsui/services/FileOperationService.java
@@ -68,6 +68,9 @@ public class FileOperationService extends Service implements Job.Listener {
public static final String EXTRA_OPERATION = "com.android.documentsui.OPERATION";
public static final String EXTRA_CANCEL = "com.android.documentsui.CANCEL";
+ public static final String ACTION_PROGRESS = "com.android.documentsui.action.PROGRESS";
+ public static final String EXTRA_PROGRESS = "com.android.documentsui.PROGRESS";
+
@IntDef({
OPERATION_UNKNOWN,
OPERATION_COPY,
@@ -580,6 +583,7 @@ public class FileOperationService extends Service implements Job.Listener {
private final class GlobalJobMonitor implements Runnable {
private static final long PROGRESS_INTERVAL_MILLIS = 500L;
private boolean mRunning = false;
+ private long mLastId = 0;
private void start() {
if (!mRunning) {
@@ -602,9 +606,9 @@ public class FileOperationService extends Service implements Job.Listener {
}
Intent intent = new Intent();
intent.setPackage(getPackageName());
- intent.setAction("com.android.documentsui.PROGRESS");
- intent.putExtra("id", 0);
- intent.putParcelableArrayListExtra("progress", progress);
+ intent.setAction(ACTION_PROGRESS);
+ intent.putExtra("id", mLastId++);
+ intent.putParcelableArrayListExtra(EXTRA_PROGRESS, progress);
sendBroadcast(intent);
}
diff --git a/src/com/android/documentsui/services/Job.java b/src/com/android/documentsui/services/Job.java
index 0f432cc19..95ad8b335 100644
--- a/src/com/android/documentsui/services/Job.java
+++ b/src/com/android/documentsui/services/Job.java
@@ -76,11 +76,11 @@ abstract public class Job implements Runnable {
@Retention(RetentionPolicy.SOURCE)
@IntDef({STATE_CREATED, STATE_STARTED, STATE_SET_UP, STATE_COMPLETED, STATE_CANCELED})
- @interface State {}
- static final int STATE_CREATED = 0;
- static final int STATE_STARTED = 1;
- static final int STATE_SET_UP = 2;
- static final int STATE_COMPLETED = 3;
+ public @interface State {}
+ public static final int STATE_CREATED = 0;
+ public static final int STATE_STARTED = 1;
+ public static final int STATE_SET_UP = 2;
+ public static final int STATE_COMPLETED = 3;
/**
* A job is in canceled state as long as {@link #cancel()} is called on it, even after it is
* completed.