/* * 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.graphics.Rect import android.text.format.Formatter import android.view.Gravity import android.view.LayoutInflater import android.view.MenuItem import android.view.View import android.view.ViewGroup import android.widget.Button import android.widget.ImageView import android.widget.PopupWindow import android.widget.ProgressBar import android.widget.TextView import androidx.annotation.VisibleForTesting import androidx.core.content.IntentCompat import androidx.core.view.isGone import androidx.core.view.isVisible import androidx.lifecycle.DefaultLifecycleObserver import androidx.lifecycle.Lifecycle import androidx.lifecycle.LifecycleOwner import androidx.lifecycle.lifecycleScope import androidx.lifecycle.repeatOnLifecycle import androidx.recyclerview.widget.DiffUtil import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.ListAdapter import androidx.recyclerview.widget.RecyclerView import com.android.documentsui.JobPanelViewModel.MenuIconState import com.android.documentsui.JobPanelViewModel.ProgressViewModel import com.android.documentsui.base.Menus import com.android.documentsui.services.FileOperationService import com.android.documentsui.services.FileOperations import com.android.documentsui.services.Job import com.android.documentsui.services.JobProgress import com.android.documentsui.util.FormatUtils import com.android.documentsui.util.Material3Config.Companion.getRes import com.google.android.material.button.MaterialButton import com.google.android.material.card.MaterialCardView import com.google.android.material.progressindicator.LinearProgressIndicator import com.google.android.material.shape.ShapeAppearanceModel import kotlinx.coroutines.coroutineScope import kotlinx.coroutines.launch /** * Adds a gap between items in a vertical Recycler View. */ private class VerticalMarginItemDecoration( private val marginSize: Int ) : RecyclerView.ItemDecoration() { override fun getItemOffsets( outRect: Rect, view: View, parent: RecyclerView, state: RecyclerView.State ) { if (parent.getChildAdapterPosition(view) > 0) { outRect.top = marginSize } } } /** * JobPanelController is responsible for receiving broadcast updates from the [FileOperationService] * and updating a given menu item to reflect the current progress. * * @param activityContext Context used to receive broadcasts and access resources for UI. * @param viewModel View model containing the state of the job panel. */ class JobPanelController( private val activityContext: Context, private val actions: ActionHandler, private val viewModel: JobPanelViewModel, ) : BroadcastReceiver(), DefaultLifecycleObserver { companion object { private const val TAG = "JobPanelController" private const val MAX_PROGRESS = 100 } private class ProgressItemHolder( private val controller: JobPanelController, private val cardView: View, ) : RecyclerView.ViewHolder(cardView) { private val context = cardView.context // Header elements. private val titleView = cardView.findViewById( getRes(R.id.job_progress_item_title) ) private val toggleExpandButton = cardView.findViewById(getRes(R.id.job_progress_item_expand)) // Body elements. private val progressView = cardView.findViewById(getRes(R.id.job_progress_item_progress)) private val primaryStatusView = cardView.findViewById(getRes(R.id.job_progress_item_primary_status)) private val statusSeparator = cardView.findViewById(getRes(R.id.job_progress_item_status_separator)) private val secondaryStatusView = cardView.findViewById(getRes(R.id.job_progress_item_secondary_status)) // Buttons private val cancelButton = cardView.findViewById