Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 91bc3ee2 authored by Austin Tankiang's avatar Austin Tankiang Committed by Android (Google) Code Review
Browse files

Merge "Add job progress icon to the app bar" into main

parents 8f03bda5 60ab4dfb
Loading
Loading
Loading
Loading
+25 −0
Original line number Diff line number Diff line
<?xml version="1.0" encoding="utf-8"?>
<!--
    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.
-->

<com.google.android.material.progressindicator.CircularProgressIndicator
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    style="@style/JobProgressToolbarIndicatorStyle"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:clickable="true"
    android:focusable="true" />
+6 −0
Original line number Diff line number Diff line
@@ -33,6 +33,12 @@
        android:visible="false"
        app:showAsAction="always|collapseActionView"
        app:actionViewClass="androidx.appcompat.widget.SearchView"/>
    <item
        android:id="@+id/option_menu_job_progress"
        android:enabled="false"
        android:visible="false"
        app:actionLayout="@layout/job_progress_toolbar_item"
        app:showAsAction="always" />
    <item
        android:id="@+id/sub_menu_grid"
        android:title="@string/menu_grid"
+1 −0
Original line number Diff line number Diff line
@@ -226,6 +226,7 @@
    <!-- Main margin is set by main_container_padding_start for the menu button, here is for
    the space between the button the text/title. -->
    <dimen name="search_bar_text_margin_start">@dimen/space_extra_small_6</dimen>
    <dimen name="job_progress_toolbar_indicator_size">24dp</dimen>
    <!-- The main margin is controlled above on paddingStart, zeroing toolbar_content_insets to
         avoid pushing the title or button further. -->
    <dimen name="toolbar_content_inset_start">0dp</dimen>
+5 −0
Original line number Diff line number Diff line
@@ -34,6 +34,11 @@
        <item name="android:maxHeight">@dimen/icon_size_headline_large</item>
    </style>

    <style name="JobProgressToolbarIndicatorStyle" parent="@style/Widget.Material3.CircularProgressIndicator.ExtraSmall">
        <item name="indicatorSize">@dimen/job_progress_toolbar_indicator_size</item>
        <item name="indicatorInset">@dimen/space_extra_small_6</item>
    </style>

    <style name="ToolbarStyles" parent="@style/Widget.Material3.Toolbar">
        <item name="android:paddingStart">@dimen/toolbar_padding_start</item>
        <item name="android:paddingEnd">@dimen/toolbar_padding_end</item>
+126 −0
Original line number Diff line number Diff line
/*
 * 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)
    }
}
Loading