diff --git a/app/src/androidTest/java/app/opendocument/droid/test/MainActivityTests.kt b/app/src/androidTest/java/app/opendocument/droid/test/MainActivityTests.kt index d7e733d9f4dd..d78c7f24813e 100644 --- a/app/src/androidTest/java/app/opendocument/droid/test/MainActivityTests.kt +++ b/app/src/androidTest/java/app/opendocument/droid/test/MainActivityTests.kt @@ -23,6 +23,7 @@ import androidx.test.espresso.intent.VerificationModes.times import androidx.test.espresso.intent.matcher.IntentMatchers.hasAction import androidx.test.espresso.matcher.ViewMatchers.isDisplayed import androidx.test.espresso.matcher.ViewMatchers.withClassName +import androidx.test.espresso.matcher.ViewMatchers.withContentDescription import androidx.test.espresso.matcher.ViewMatchers.withId import androidx.test.espresso.matcher.ViewMatchers.withText import androidx.test.ext.junit.runners.AndroidJUnit4 @@ -124,9 +125,8 @@ class MainActivityTests { // the load itself and not just the picker round trip. waitForDocumentActions() - unfoldDocumentActions() - - onView(withText(R.string.menu_edit)).check(matches(isDisplayed())) + // nothing unfolded: Edit stands on its own where the core can write the document back + onView(withContentDescription(R.string.menu_edit)).check(matches(isDisplayed())) } @Test @@ -142,7 +142,7 @@ class MainActivityTests { unfoldDocumentActions() - onView(withText(R.string.menu_edit)).check(doesNotExist()) + onView(withContentDescription(R.string.menu_edit)).check(doesNotExist()) } @Test diff --git a/app/src/main/java/app/opendocument/droid/ui/activity/DocumentFragment.kt b/app/src/main/java/app/opendocument/droid/ui/activity/DocumentFragment.kt index 3d6b05b91fbe..05f6127d0d08 100644 --- a/app/src/main/java/app/opendocument/droid/ui/activity/DocumentFragment.kt +++ b/app/src/main/java/app/opendocument/droid/ui/activity/DocumentFragment.kt @@ -464,7 +464,7 @@ class DocumentFragment : Fragment(), DocumentLoader.Listener { // guarded like resetTabs below: a load can fail before there is a view to put right if (::actions.isInitialized) { - actions.setActions(null, emptyList()) + actions.setActions(emptyList(), emptyList()) } resetTabs() @@ -558,7 +558,6 @@ class DocumentFragment : Fragment(), DocumentLoader.Listener { R.string.menu_fullscreen, R.drawable.ic_fullscreen, ), - edit, DocumentActions.Action( DocumentActions.ACTION_TTS, R.string.menu_tts, @@ -586,11 +585,16 @@ class DocumentFragment : Fragment(), DocumentLoader.Listener { ), ) + // Edit above Search, not below it: Search is offered for every document and Edit is not, + // so this is the order that keeps the button nearest the thumb the same one throughout actions.setActions( - DocumentActions.Action( - DocumentActions.ACTION_SEARCH, - R.string.menu_search, - R.drawable.ic_search, + listOfNotNull( + edit, + DocumentActions.Action( + DocumentActions.ACTION_SEARCH, + R.string.menu_search, + R.drawable.ic_search, + ), ), unfolding, ) diff --git a/app/src/main/java/app/opendocument/droid/ui/widget/DocumentActions.kt b/app/src/main/java/app/opendocument/droid/ui/widget/DocumentActions.kt index 517984162918..3a6ab2aca7a9 100644 --- a/app/src/main/java/app/opendocument/droid/ui/widget/DocumentActions.kt +++ b/app/src/main/java/app/opendocument/droid/ui/widget/DocumentActions.kt @@ -10,12 +10,13 @@ import android.widget.ScrollView import android.widget.TextView import androidx.annotation.DrawableRes import androidx.annotation.StringRes +import androidx.appcompat.widget.TooltipCompat import app.opendocument.droid.R import com.google.android.material.floatingactionbutton.FloatingActionButton /** * What can be done with the open document, as buttons over the bottom right corner of it: one for - * the action worth its own button, and one that unfolds the rest. + * each action worth its own button, and one that unfolds the rest. * * This is what the toolbar menu used to be. A document is read with the phone in one hand, and the * top right corner of a modern screen is the one place a thumb cannot reach - so the actions sit @@ -23,7 +24,8 @@ import com.google.android.material.floatingactionbutton.FloatingActionButton * are. * * Material ships no speed dial component (the one it had was never brought over to Material 3), so - * the rows are built here from [R.layout.item_document_action]. + * the buttons are built here, from [R.layout.item_document_action_standing] and + * [R.layout.item_document_action]. */ class DocumentActions(context: Context, attributeSet: AttributeSet?) : FrameLayout(context, attributeSet) { @@ -44,7 +46,7 @@ class DocumentActions(context: Context, attributeSet: AttributeSet?) : private val buttons: LinearLayout private val rowsScroll: ScrollView private val rows: LinearLayout - private val primaryButton: FloatingActionButton + private val standingButtons: LinearLayout private val moreButton: FloatingActionButton private val basePaddingBottom: Int @@ -64,7 +66,7 @@ class DocumentActions(context: Context, attributeSet: AttributeSet?) : buttons = findViewById(R.id.document_actions_buttons) rowsScroll = findViewById(R.id.document_actions_rows_scroll) rows = findViewById(R.id.document_actions_rows) - primaryButton = findViewById(R.id.document_actions_primary) + standingButtons = findViewById(R.id.document_actions_standing) moreButton = findViewById(R.id.document_actions_more) basePaddingBottom = buttons.paddingBottom @@ -72,7 +74,7 @@ class DocumentActions(context: Context, attributeSet: AttributeSet?) : scrim.setOnClickListener { collapse() } moreButton.setOnClickListener { if (isExpanded) collapse() else expand() } - setActions(null, emptyList()) + setActions(emptyList(), emptyList()) } /** @@ -92,22 +94,20 @@ class DocumentActions(context: Context, attributeSet: AttributeSet?) : } /** - * What the document can do right now. [primary] gets a button of its own, the rest unfold out - * of the second one, the first of them closest to it - so the order is most wanted first. + * What the document can do right now. Each of [standing] keeps a button of its own whether the + * rest are folded up or not, the last of them closest to the thumb; [unfolding] comes out of + * the button below them, its first entry closest to it - so both orders are most wanted first. * - * Nothing and an empty list take the buttons away entirely, which is what a document that - * failed to load leaves behind. + * Two empty lists take the buttons away entirely, which is what a document that failed to load + * leaves behind. */ - fun setActions(primary: Action?, unfolding: List) { + fun setActions(standing: List, unfolding: List) { collapse() - if (primary == null) { - primaryButton.visibility = View.GONE - } else { - primaryButton.visibility = View.VISIBLE - primaryButton.setImageResource(primary.icon) - primaryButton.contentDescription = context.getString(primary.label) - primaryButton.setOnClickListener { listener?.onDocumentActionClicked(primary.id) } + standingButtons.removeAllViews() + + for (action in standing) { + standingButtons.addView(newStandingButton(action)) } rows.removeAllViews() @@ -121,6 +121,23 @@ class DocumentActions(context: Context, attributeSet: AttributeSet?) : moreButton.visibility = if (unfolding.isEmpty()) View.GONE else View.VISIBLE } + private fun newStandingButton(action: Action): View { + val button = + LayoutInflater.from(context) + .inflate(R.layout.item_document_action_standing, standingButtons, false) + as FloatingActionButton + + button.setImageResource(action.icon) + button.contentDescription = context.getString(action.label) + + // no label plate beside it, so the name is what a long press turns up + TooltipCompat.setTooltipText(button, context.getString(action.label)) + + button.setOnClickListener { listener?.onDocumentActionClicked(action.id) } + + return button + } + private fun newRow(action: Action): View { val row = LayoutInflater.from(context).inflate(R.layout.item_document_action, rows, false) diff --git a/app/src/main/res/layout/item_document_action_standing.xml b/app/src/main/res/layout/item_document_action_standing.xml new file mode 100644 index 000000000000..6fa160991617 --- /dev/null +++ b/app/src/main/res/layout/item_document_action_standing.xml @@ -0,0 +1,8 @@ + + + diff --git a/app/src/main/res/layout/view_document_actions.xml b/app/src/main/res/layout/view_document_actions.xml index 6e9be124c0b5..74560c38bdb2 100644 --- a/app/src/main/res/layout/view_document_actions.xml +++ b/app/src/main/res/layout/view_document_actions.xml @@ -59,13 +59,16 @@ android:orientation="vertical" /> - + + android:clipChildren="false" + android:clipToPadding="false" + android:gravity="end" + android:orientation="vertical" />