diff options
author | 2025-03-20 18:48:06 -0700 | |
---|---|---|
committer | 2025-03-20 18:48:06 -0700 | |
commit | 3fea3c4cdb50094a4d37d0f5d1ac1749ae866b6a (patch) | |
tree | 7bf4691aea7cbb6ff5e750863a315bc27fcaf187 | |
parent | bde3eb49cbf4b0522d2ab28cab84650065a8f47d (diff) | |
parent | 4332f355c63fbfc4bc7c859ce8969b1960dc39dd (diff) |
Merge "Add the job progress panel" into main
12 files changed, 215 insertions, 15 deletions
diff --git a/proguard.flags b/proguard.flags index 76449d4e9..34071fa6d 100644 --- a/proguard.flags +++ b/proguard.flags @@ -106,6 +106,7 @@ int dir_menu_view_in_owner; int drawer_layout; int inspector_details_view; + int job_progress_panel_title; int option_menu_create_dir; int option_menu_debug; int option_menu_extract_all; diff --git a/res/flag(com.android.documentsui.flags.use_material3)/layout/job_progress_panel.xml b/res/flag(com.android.documentsui.flags.use_material3)/layout/job_progress_panel.xml new file mode 100644 index 000000000..17f6aa6fc --- /dev/null +++ b/res/flag(com.android.documentsui.flags.use_material3)/layout/job_progress_panel.xml @@ -0,0 +1,43 @@ +<?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. +--> + +<FrameLayout + xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="wrap_content"> + <com.google.android.material.card.MaterialCardView + style="@style/JobProgressPanelStyle" + android:layout_width="match_parent" + android:layout_height="wrap_content"> + <LinearLayout + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="vertical"> + <TextView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:id="@+id/job_progress_panel_title" + android:text="@string/job_progress_panel_title" + android:textAppearance="@style/JobProgressPanelHeaderText" + android:layout_margin="@dimen/job_progress_panel_header_margin" /> + <androidx.recyclerview.widget.RecyclerView + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:id="@+id/job_progress_list" /> + </LinearLayout> + </com.google.android.material.card.MaterialCardView> +</FrameLayout> diff --git a/res/flag(com.android.documentsui.flags.use_material3)/values/dimens.xml b/res/flag(com.android.documentsui.flags.use_material3)/values/dimens.xml index f6cff73b4..fa9e436ab 100644 --- a/res/flag(com.android.documentsui.flags.use_material3)/values/dimens.xml +++ b/res/flag(com.android.documentsui.flags.use_material3)/values/dimens.xml @@ -248,4 +248,8 @@ <dimen name="focus_ring_gap">5dp</dimen> <dimen name="hover_overlay_alpha">0.08</dimen> <dimen name="ripple_overlay_alpha">0.10</dimen> + + <dimen name="job_progress_panel_width">360dp</dimen> + <dimen name="job_progress_panel_margin">@dimen/space_small_1</dimen> + <dimen name="job_progress_panel_header_margin">@dimen/space_small_1</dimen> </resources> diff --git a/res/flag(com.android.documentsui.flags.use_material3)/values/styles.xml b/res/flag(com.android.documentsui.flags.use_material3)/values/styles.xml index 6819c12a3..481cd0be8 100644 --- a/res/flag(com.android.documentsui.flags.use_material3)/values/styles.xml +++ b/res/flag(com.android.documentsui.flags.use_material3)/values/styles.xml @@ -190,4 +190,11 @@ <item name="android:textColor">@color/nav_rail_item_text_color</item> <item name="android:textAppearance">@style/NavRailItemTextAppearance</item> </style> + + <style name="JobProgressPanelStyle" parent="@style/Widget.Material3.CardView.Elevated"> + <item name="android:layout_marginStart">@dimen/job_progress_panel_margin</item> + <item name="android:layout_marginBottom">@dimen/job_progress_panel_margin</item> + <item name="cardElevation">1dp</item> + <item name="cardBackgroundColor">?attr/colorSurfaceDim</item> + </style> </resources> diff --git a/res/flag(com.android.documentsui.flags.use_material3)/values/styles_text.xml b/res/flag(com.android.documentsui.flags.use_material3)/values/styles_text.xml index 2f400ea24..57dbb1212 100644 --- a/res/flag(com.android.documentsui.flags.use_material3)/values/styles_text.xml +++ b/res/flag(com.android.documentsui.flags.use_material3)/values/styles_text.xml @@ -159,4 +159,8 @@ <item name="fontFamily">@string/config_fontFamily</item> </style> + <style name="JobProgressPanelHeaderText" parent="@style/TextAppearance.Material3.TitleMedium"> + <item name="fontFamily">@string/config_fontFamilyMedium</item> + </style> + </resources> diff --git a/res/values/strings.xml b/res/values/strings.xml index f8ade4c47..c3f11bab4 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -429,6 +429,7 @@ =1 {Zipping <xliff:g id="filename" example="foobar.txt">{filename}</xliff:g>} other {Zipping # files} }</string> + <string name="job_progress_panel_title" translatable="false">File Progress</string> <!-- Text in an alert dialog asking user to grant app access to a given directory in an external storage volume --> <string name="open_external_dialog_request">Grant <xliff:g id="appName" example="System Settings"><b>^1</b></xliff:g> diff --git a/src/com/android/documentsui/JobPanelController.kt b/src/com/android/documentsui/JobPanelController.kt index b3b5f1cfd..a8ab8b0a4 100644 --- a/src/com/android/documentsui/JobPanelController.kt +++ b/src/com/android/documentsui/JobPanelController.kt @@ -20,7 +20,10 @@ import android.content.Context import android.content.Intent import android.content.IntentFilter import android.util.Log +import android.view.LayoutInflater import android.view.MenuItem +import android.view.ViewGroup +import android.widget.PopupWindow import android.widget.ProgressBar import com.android.documentsui.base.Menus import com.android.documentsui.services.FileOperationService @@ -77,8 +80,29 @@ class JobPanelController(private val mContext: Context) : BroadcastReceiver() { /** * Sets the menu item controlled by this class. The item's actionView must be a [ProgressBar]. */ + @Suppress("ktlint:standard:comment-wrapping") fun setMenuItem(menuItem: MenuItem) { - (menuItem.actionView as ProgressBar).max = MAX_PROGRESS + val progressIcon = menuItem.actionView as ProgressBar + progressIcon.max = MAX_PROGRESS + progressIcon.setOnClickListener { view -> + val panel = LayoutInflater.from(mContext).inflate( + R.layout.job_progress_panel, + /* root= */ null + ) + val popupWidth = mContext.resources.getDimension(R.dimen.job_progress_panel_width) + + mContext.resources.getDimension(R.dimen.job_progress_panel_margin) + val popup = PopupWindow( + /* contentView= */ panel, + /* width= */ popupWidth.toInt(), + /* height= */ ViewGroup.LayoutParams.WRAP_CONTENT, + /* focusable= */ true + ) + popup.showAsDropDown( + /* anchor= */ view, + /* xoff= */ view.width - popupWidth.toInt(), + /* yoff= */ 0 + ) + } mMenuItem = menuItem updateMenuItem(animate = false) } diff --git a/src/com/android/documentsui/files/FilesActivity.java b/src/com/android/documentsui/files/FilesActivity.java index cb8708f0b..955a94d93 100644 --- a/src/com/android/documentsui/files/FilesActivity.java +++ b/src/com/android/documentsui/files/FilesActivity.java @@ -19,6 +19,7 @@ package com.android.documentsui.files; import static com.android.documentsui.OperationDialogFragment.DIALOG_TYPE_UNKNOWN; import static com.android.documentsui.base.SharedMinimal.DEBUG; import static com.android.documentsui.util.FlagUtils.isUseMaterial3FlagEnabled; +import static com.android.documentsui.util.FlagUtils.isVisualSignalsFlagEnabled; import static com.android.documentsui.util.FlagUtils.isZipNgFlagEnabled; import android.app.ActivityManager.TaskDescription; @@ -134,7 +135,7 @@ public class FilesActivity extends BaseActivity implements AbstractActionHandler return clipper.hasItemsToPaste(); } }, - getApplicationContext(), + isVisualSignalsFlagEnabled() ? this : getApplicationContext(), mInjector.selectionMgr, mProviders::getApplicationName, mInjector.getModel()::getItemUri, diff --git a/tests/Android.bp b/tests/Android.bp index 41ccc1ab1..95418bdde 100644 --- a/tests/Android.bp +++ b/tests/Android.bp @@ -70,6 +70,7 @@ android_library { srcs: [ "common/**/*.java", + "common/**/*.kt", "unit/**/*.java", "unit/**/*.kt", ], @@ -94,6 +95,7 @@ android_library { srcs: [ "common/**/*.java", + "common/**/*.kt", "functional/**/*.java", "functional/**/*.kt", "unit/**/*.java", diff --git a/tests/common/com/android/documentsui/testing/MutableJobProgress.kt b/tests/common/com/android/documentsui/testing/MutableJobProgress.kt new file mode 100644 index 000000000..b2dd595f9 --- /dev/null +++ b/tests/common/com/android/documentsui/testing/MutableJobProgress.kt @@ -0,0 +1,32 @@ +/* + * 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.testing + +import com.android.documentsui.services.Job +import com.android.documentsui.services.JobProgress + +data class MutableJobProgress( + var id: String, + @Job.State var state: Int, + var msg: String?, + var hasFailures: Boolean, + var currentBytes: Long = -1, + var requiredBytes: Long = -1, + var msRemaining: Long = -1, +) { + fun toJobProgress() = + JobProgress(id, state, msg, hasFailures, currentBytes, requiredBytes, msRemaining) +} diff --git a/tests/functional/com/android/documentsui/JobPanelUiTest.kt b/tests/functional/com/android/documentsui/JobPanelUiTest.kt new file mode 100644 index 000000000..5b28b1f5d --- /dev/null +++ b/tests/functional/com/android/documentsui/JobPanelUiTest.kt @@ -0,0 +1,93 @@ +/* + * 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.Intent +import android.platform.test.annotations.RequiresFlagsEnabled +import android.platform.test.flag.junit.CheckFlagsRule +import android.platform.test.flag.junit.DeviceFlagsValueProvider +import androidx.test.espresso.Espresso.onView +import androidx.test.espresso.action.ViewActions.click +import androidx.test.espresso.assertion.ViewAssertions.doesNotExist +import androidx.test.espresso.assertion.ViewAssertions.matches +import androidx.test.espresso.matcher.ViewMatchers.isDisplayed +import androidx.test.espresso.matcher.ViewMatchers.withId +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.platform.app.InstrumentationRegistry +import com.android.documentsui.files.FilesActivity +import com.android.documentsui.flags.Flags.FLAG_USE_MATERIAL3 +import com.android.documentsui.flags.Flags.FLAG_VISUAL_SIGNALS_RO +import com.android.documentsui.services.FileOperationService.ACTION_PROGRESS +import com.android.documentsui.services.FileOperationService.EXTRA_PROGRESS +import com.android.documentsui.services.Job +import com.android.documentsui.services.JobProgress +import com.android.documentsui.testing.MutableJobProgress +import org.junit.After +import org.junit.Before +import org.junit.Rule +import org.junit.Test +import org.junit.runner.RunWith + +@RequiresFlagsEnabled(FLAG_USE_MATERIAL3, FLAG_VISUAL_SIGNALS_RO) +@RunWith(AndroidJUnit4::class) +class JobPanelUiTest : ActivityTestJunit4<FilesActivity>() { + @get:Rule + val mCheckFlagsRule: CheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule() + + private var mLastId = 0L + + private fun sendProgress(progresses: ArrayList<JobProgress>, id: Long = mLastId++) { + val context = InstrumentationRegistry.getInstrumentation().targetContext + var intent = Intent(ACTION_PROGRESS).apply { + `package` = context.packageName + putExtra("id", id) + putParcelableArrayListExtra(EXTRA_PROGRESS, progresses) + } + context.sendBroadcast(intent) + } + + @Before + override fun setUp() { + super.setUp() + } + + @After + override fun tearDown() { + super.tearDown() + } + + @Test + fun testJobPanelAppearsOnClick() { + onView(withId(R.id.option_menu_job_progress)).check(doesNotExist()) + onView(withId(R.id.job_progress_panel_title)).check(doesNotExist()) + + val progress = MutableJobProgress( + id = "jobId1", + state = Job.STATE_SET_UP, + msg = "Job started", + hasFailures = false, + currentBytes = 4, + requiredBytes = 10, + msRemaining = -1 + ) + 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())) + } +} diff --git a/tests/unit/com/android/documentsui/JobPanelControllerTest.kt b/tests/unit/com/android/documentsui/JobPanelControllerTest.kt index be0c9adbd..3e510edd9 100644 --- a/tests/unit/com/android/documentsui/JobPanelControllerTest.kt +++ b/tests/unit/com/android/documentsui/JobPanelControllerTest.kt @@ -30,6 +30,7 @@ import com.android.documentsui.services.FileOperationService.ACTION_PROGRESS import com.android.documentsui.services.FileOperationService.EXTRA_PROGRESS import com.android.documentsui.services.Job import com.android.documentsui.services.JobProgress +import com.android.documentsui.testing.MutableJobProgress import junit.framework.Assert.assertEquals import junit.framework.Assert.assertFalse import junit.framework.Assert.assertTrue @@ -38,19 +39,6 @@ import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith -private data class MutableJobProgress( - var id: String, - @Job.State var state: Int, - var msg: String?, - var hasFailures: Boolean, - var currentBytes: Long = -1, - var requiredBytes: Long = -1, - var msRemaining: Long = -1, -) { - fun toJobProgress() = - JobProgress(id, state, msg, hasFailures, currentBytes, requiredBytes, msRemaining) -} - @SmallTest @RequiresFlagsEnabled(FLAG_USE_MATERIAL3, FLAG_VISUAL_SIGNALS_RO) @RunWith(AndroidJUnit4::class) |