summaryrefslogtreecommitdiff
path: root/java
diff options
context:
space:
mode:
Diffstat (limited to 'java')
-rw-r--r--java/src/com/android/intentresolver/v2/MultiProfilePagerAdapter.java51
-rw-r--r--java/src/com/android/intentresolver/v2/emptystate/EmptyStateUiHelper.java118
-rw-r--r--java/tests/src/com/android/intentresolver/v2/emptystate/EmptyStateUiHelperTest.kt88
3 files changed, 181 insertions, 76 deletions
diff --git a/java/src/com/android/intentresolver/v2/MultiProfilePagerAdapter.java b/java/src/com/android/intentresolver/v2/MultiProfilePagerAdapter.java
index ad9614b9..391cce7a 100644
--- a/java/src/com/android/intentresolver/v2/MultiProfilePagerAdapter.java
+++ b/java/src/com/android/intentresolver/v2/MultiProfilePagerAdapter.java
@@ -21,8 +21,6 @@ import android.os.Trace;
import android.os.UserHandle;
import android.view.View;
import android.view.ViewGroup;
-import android.widget.Button;
-import android.widget.TextView;
import androidx.viewpager.widget.PagerAdapter;
import androidx.viewpager.widget.ViewPager;
@@ -414,6 +412,8 @@ public class MultiProfilePagerAdapter<
* The intention is to prevent the user from having to turn
* the work profile on if there will not be any apps resolved
* anyway.
+ *
+ * TODO: move this comment to the place where we configure our composite provider.
*/
public void showEmptyResolverListEmptyState(ListAdapterT listAdapter) {
final EmptyState emptyState = mEmptyStateProvider.getEmptyState(listAdapter);
@@ -449,48 +449,13 @@ public class MultiProfilePagerAdapter<
}
}
- protected void showEmptyState(
+ private void showEmptyState(
ListAdapterT activeListAdapter,
EmptyState emptyState,
View.OnClickListener buttonOnClick) {
ProfileDescriptor<PageViewT, SinglePageAdapterT> descriptor = getItem(
userHandleToPageIndex(activeListAdapter.getUserHandle()));
- descriptor.mRootView.findViewById(
- com.android.internal.R.id.resolver_list).setVisibility(View.GONE);
- descriptor.mEmptyStateUi.resetViewVisibilities();
- descriptor.setupContainerPadding();
-
- ViewGroup emptyStateView = descriptor.getEmptyStateView();
-
-
- TextView titleView = emptyStateView.findViewById(
- com.android.internal.R.id.resolver_empty_state_title);
- String title = emptyState.getTitle();
- if (title != null) {
- titleView.setVisibility(View.VISIBLE);
- titleView.setText(title);
- } else {
- titleView.setVisibility(View.GONE);
- }
-
- TextView subtitleView = emptyStateView.findViewById(
- com.android.internal.R.id.resolver_empty_state_subtitle);
- String subtitle = emptyState.getSubtitle();
- if (subtitle != null) {
- subtitleView.setVisibility(View.VISIBLE);
- subtitleView.setText(subtitle);
- } else {
- subtitleView.setVisibility(View.GONE);
- }
-
- View defaultEmptyText = emptyStateView.findViewById(com.android.internal.R.id.empty);
- defaultEmptyText.setVisibility(emptyState.useDefaultEmptyView() ? View.VISIBLE : View.GONE);
-
- Button button = emptyStateView.findViewById(
- com.android.internal.R.id.resolver_empty_state_button);
- button.setVisibility(buttonOnClick != null ? View.VISIBLE : View.GONE);
- button.setOnClickListener(buttonOnClick);
-
+ descriptor.mEmptyStateUi.showEmptyState(emptyState, buttonOnClick);
activeListAdapter.markTabLoaded();
}
@@ -505,8 +470,6 @@ public class MultiProfilePagerAdapter<
public void showListView(ListAdapterT activeListAdapter) {
ProfileDescriptor<PageViewT, SinglePageAdapterT> descriptor = getItem(
userHandleToPageIndex(activeListAdapter.getUserHandle()));
- descriptor.mRootView.findViewById(
- com.android.internal.R.id.resolver_list).setVisibility(View.VISIBLE);
descriptor.mEmptyStateUi.hide();
}
@@ -538,8 +501,10 @@ public class MultiProfilePagerAdapter<
mAdapter = adapter;
mEmptyStateView = rootView.findViewById(com.android.internal.R.id.resolver_empty_state);
mView = (PageViewT) rootView.findViewById(com.android.internal.R.id.resolver_list);
- mEmptyStateUi =
- new EmptyStateUiHelper(rootView, containerBottomPaddingOverrideSupplier);
+ mEmptyStateUi = new EmptyStateUiHelper(
+ rootView,
+ com.android.internal.R.id.resolver_list,
+ containerBottomPaddingOverrideSupplier);
}
protected ViewGroup getEmptyStateView() {
diff --git a/java/src/com/android/intentresolver/v2/emptystate/EmptyStateUiHelper.java b/java/src/com/android/intentresolver/v2/emptystate/EmptyStateUiHelper.java
index fc852f5c..2f1e1b59 100644
--- a/java/src/com/android/intentresolver/v2/emptystate/EmptyStateUiHelper.java
+++ b/java/src/com/android/intentresolver/v2/emptystate/EmptyStateUiHelper.java
@@ -17,6 +17,11 @@ package com.android.intentresolver.v2.emptystate;
import android.view.View;
import android.view.ViewGroup;
+import android.widget.Button;
+import android.widget.TextView;
+
+import com.android.intentresolver.emptystate.EmptyState;
+import com.android.internal.annotations.VisibleForTesting;
import java.util.Optional;
import java.util.function.Supplier;
@@ -26,58 +31,111 @@ import java.util.function.Supplier;
* some empty-state status.
*/
public class EmptyStateUiHelper {
- private final View mEmptyStateView;
private final Supplier<Optional<Integer>> mContainerBottomPaddingOverrideSupplier;
+ private final View mEmptyStateView;
+ private final View mListView;
+ private final View mEmptyStateContainerView;
+ private final TextView mEmptyStateTitleView;
+ private final TextView mEmptyStateSubtitleView;
+ private final Button mEmptyStateButtonView;
+ private final View mEmptyStateProgressView;
+ private final View mEmptyStateEmptyView;
public EmptyStateUiHelper(
ViewGroup rootView,
+ int listViewResourceId,
Supplier<Optional<Integer>> containerBottomPaddingOverrideSupplier) {
+ mContainerBottomPaddingOverrideSupplier = containerBottomPaddingOverrideSupplier;
mEmptyStateView =
rootView.requireViewById(com.android.internal.R.id.resolver_empty_state);
- mContainerBottomPaddingOverrideSupplier = containerBottomPaddingOverrideSupplier;
+ mListView = rootView.requireViewById(listViewResourceId);
+ mEmptyStateContainerView = mEmptyStateView.requireViewById(
+ com.android.internal.R.id.resolver_empty_state_container);
+ mEmptyStateTitleView = mEmptyStateView.requireViewById(
+ com.android.internal.R.id.resolver_empty_state_title);
+ mEmptyStateSubtitleView = mEmptyStateView.requireViewById(
+ com.android.internal.R.id.resolver_empty_state_subtitle);
+ mEmptyStateButtonView = mEmptyStateView.requireViewById(
+ com.android.internal.R.id.resolver_empty_state_button);
+ mEmptyStateProgressView = mEmptyStateView.requireViewById(
+ com.android.internal.R.id.resolver_empty_state_progress);
+ mEmptyStateEmptyView = mEmptyStateView.requireViewById(com.android.internal.R.id.empty);
+ }
+
+ /**
+ * Display the described empty state.
+ * @param emptyState the data describing the cause of this empty-state condition.
+ * @param buttonOnClick handler for a button that the user might be able to use to circumvent
+ * the empty-state condition. If null, no button will be displayed.
+ */
+ public void showEmptyState(EmptyState emptyState, View.OnClickListener buttonOnClick) {
+ resetViewVisibilities();
+ setupContainerPadding();
+
+ String title = emptyState.getTitle();
+ if (title != null) {
+ mEmptyStateTitleView.setVisibility(View.VISIBLE);
+ mEmptyStateTitleView.setText(title);
+ } else {
+ mEmptyStateTitleView.setVisibility(View.GONE);
+ }
+
+ String subtitle = emptyState.getSubtitle();
+ if (subtitle != null) {
+ mEmptyStateSubtitleView.setVisibility(View.VISIBLE);
+ mEmptyStateSubtitleView.setText(subtitle);
+ } else {
+ mEmptyStateSubtitleView.setVisibility(View.GONE);
+ }
+
+ mEmptyStateEmptyView.setVisibility(
+ emptyState.useDefaultEmptyView() ? View.VISIBLE : View.GONE);
+ // TODO: The EmptyState API says that if `useDefaultEmptyView()` is true, we'll ignore the
+ // state's specified title/subtitle; where (if anywhere) is that implemented?
+
+ mEmptyStateButtonView.setVisibility(buttonOnClick != null ? View.VISIBLE : View.GONE);
+ mEmptyStateButtonView.setOnClickListener(buttonOnClick);
+
+ // Don't show the main list view when we're showing an empty state.
+ mListView.setVisibility(View.GONE);
}
/** Sets up the padding of the view containing the empty state screens. */
public void setupContainerPadding() {
- View container = mEmptyStateView.requireViewById(
- com.android.internal.R.id.resolver_empty_state_container);
Optional<Integer> bottomPaddingOverride = mContainerBottomPaddingOverrideSupplier.get();
bottomPaddingOverride.ifPresent(paddingBottom ->
- container.setPadding(
- container.getPaddingLeft(),
- container.getPaddingTop(),
- container.getPaddingRight(),
+ mEmptyStateContainerView.setPadding(
+ mEmptyStateContainerView.getPaddingLeft(),
+ mEmptyStateContainerView.getPaddingTop(),
+ mEmptyStateContainerView.getPaddingRight(),
paddingBottom));
}
- public void resetViewVisibilities() {
- mEmptyStateView.requireViewById(com.android.internal.R.id.resolver_empty_state_title)
- .setVisibility(View.VISIBLE);
- mEmptyStateView.requireViewById(com.android.internal.R.id.resolver_empty_state_subtitle)
- .setVisibility(View.VISIBLE);
- mEmptyStateView.requireViewById(com.android.internal.R.id.resolver_empty_state_button)
- .setVisibility(View.INVISIBLE);
- mEmptyStateView.requireViewById(com.android.internal.R.id.resolver_empty_state_progress)
- .setVisibility(View.GONE);
- mEmptyStateView.requireViewById(com.android.internal.R.id.empty)
- .setVisibility(View.GONE);
- mEmptyStateView.setVisibility(View.VISIBLE);
- }
-
public void showSpinner() {
- mEmptyStateView.requireViewById(com.android.internal.R.id.resolver_empty_state_title)
- .setVisibility(View.INVISIBLE);
+ mEmptyStateTitleView.setVisibility(View.INVISIBLE);
// TODO: subtitle?
- mEmptyStateView.requireViewById(com.android.internal.R.id.resolver_empty_state_button)
- .setVisibility(View.INVISIBLE);
- mEmptyStateView.requireViewById(com.android.internal.R.id.resolver_empty_state_progress)
- .setVisibility(View.VISIBLE);
- mEmptyStateView.requireViewById(com.android.internal.R.id.empty)
- .setVisibility(View.GONE);
+ mEmptyStateButtonView.setVisibility(View.INVISIBLE);
+ mEmptyStateProgressView.setVisibility(View.VISIBLE);
+ mEmptyStateEmptyView.setVisibility(View.GONE);
}
public void hide() {
mEmptyStateView.setVisibility(View.GONE);
+ mListView.setVisibility(View.VISIBLE);
+ }
+
+ // TODO: this is exposed for testing so we can thoroughly prepare initial conditions that let us
+ // observe the resulting change. In reality it's only invoked as part of `showEmptyState()` and
+ // we could consider setting up narrower "realistic" preconditions to make assertions about the
+ // higher-level operation.
+ @VisibleForTesting
+ void resetViewVisibilities() {
+ mEmptyStateTitleView.setVisibility(View.VISIBLE);
+ mEmptyStateSubtitleView.setVisibility(View.VISIBLE);
+ mEmptyStateButtonView.setVisibility(View.INVISIBLE);
+ mEmptyStateProgressView.setVisibility(View.GONE);
+ mEmptyStateEmptyView.setVisibility(View.GONE);
+ mEmptyStateView.setVisibility(View.VISIBLE);
}
}
diff --git a/java/tests/src/com/android/intentresolver/v2/emptystate/EmptyStateUiHelperTest.kt b/java/tests/src/com/android/intentresolver/v2/emptystate/EmptyStateUiHelperTest.kt
index 27ed7e38..696dd1fd 100644
--- a/java/tests/src/com/android/intentresolver/v2/emptystate/EmptyStateUiHelperTest.kt
+++ b/java/tests/src/com/android/intentresolver/v2/emptystate/EmptyStateUiHelperTest.kt
@@ -20,12 +20,18 @@ import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.FrameLayout
+import android.widget.TextView
import androidx.test.platform.app.InstrumentationRegistry
+import com.android.intentresolver.any
+import com.android.intentresolver.emptystate.EmptyState
+import com.android.intentresolver.mock
import com.google.common.truth.Truth.assertThat
import java.util.Optional
import java.util.function.Supplier
import org.junit.Before
import org.junit.Test
+import org.mockito.Mockito.never
+import org.mockito.Mockito.verify
class EmptyStateUiHelperTest {
private val context = InstrumentationRegistry.getInstrumentation().getContext()
@@ -37,8 +43,9 @@ class EmptyStateUiHelperTest {
}
lateinit var rootContainer: ViewGroup
- lateinit var emptyStateTitleView: View
- lateinit var emptyStateSubtitleView: View
+ lateinit var mainListView: View // Visible when no empty state is showing.
+ lateinit var emptyStateTitleView: TextView
+ lateinit var emptyStateSubtitleView: TextView
lateinit var emptyStateButtonView: View
lateinit var emptyStateProgressView: View
lateinit var emptyStateDefaultTextView: View
@@ -55,6 +62,7 @@ class EmptyStateUiHelperTest {
rootContainer,
true
)
+ mainListView = rootContainer.requireViewById(com.android.internal.R.id.resolver_list)
emptyStateRootView =
rootContainer.requireViewById(com.android.internal.R.id.resolver_empty_state)
emptyStateTitleView =
@@ -68,7 +76,12 @@ class EmptyStateUiHelperTest {
emptyStateDefaultTextView = rootContainer.requireViewById(com.android.internal.R.id.empty)
emptyStateContainerView =
rootContainer.requireViewById(com.android.internal.R.id.resolver_empty_state_container)
- emptyStateUiHelper = EmptyStateUiHelper(rootContainer, containerPaddingSupplier)
+ emptyStateUiHelper =
+ EmptyStateUiHelper(
+ rootContainer,
+ com.android.internal.R.id.resolver_list,
+ containerPaddingSupplier
+ )
}
@Test
@@ -112,10 +125,12 @@ class EmptyStateUiHelperTest {
@Test
fun testHide() {
emptyStateRootView.visibility = View.VISIBLE
+ mainListView.visibility = View.GONE
emptyStateUiHelper.hide()
assertThat(emptyStateRootView.visibility).isEqualTo(View.GONE)
+ assertThat(mainListView.visibility).isEqualTo(View.VISIBLE)
}
@Test
@@ -143,4 +158,71 @@ class EmptyStateUiHelperTest {
assertThat(emptyStateContainerView.paddingRight).isEqualTo(3)
assertThat(emptyStateContainerView.paddingBottom).isEqualTo(42)
}
+
+ @Test
+ fun testShowEmptyState_noOnClickHandler() {
+ mainListView.visibility = View.VISIBLE
+
+ // Note: an `EmptyState.ClickListener` isn't invoked directly by the UI helper; it has to be
+ // built into the "on-click handler" that's injected to implement the button-press. We won't
+ // display the button without a click "handler," even if it *does* have a `ClickListener`.
+ val clickListener = mock<EmptyState.ClickListener>()
+
+ val emptyState =
+ object : EmptyState {
+ override fun getTitle() = "Test title"
+ override fun getSubtitle() = "Test subtitle"
+
+ override fun getButtonClickListener() = clickListener
+ }
+ emptyStateUiHelper.showEmptyState(emptyState, null)
+
+ assertThat(mainListView.visibility).isEqualTo(View.GONE)
+ assertThat(emptyStateRootView.visibility).isEqualTo(View.VISIBLE)
+ assertThat(emptyStateTitleView.visibility).isEqualTo(View.VISIBLE)
+ assertThat(emptyStateSubtitleView.visibility).isEqualTo(View.VISIBLE)
+ assertThat(emptyStateButtonView.visibility).isEqualTo(View.GONE)
+ assertThat(emptyStateProgressView.visibility).isEqualTo(View.GONE)
+ assertThat(emptyStateDefaultTextView.visibility).isEqualTo(View.GONE)
+
+ assertThat(emptyStateTitleView.text).isEqualTo("Test title")
+ assertThat(emptyStateSubtitleView.text).isEqualTo("Test subtitle")
+
+ verify(clickListener, never()).onClick(any())
+ }
+
+ @Test
+ fun testShowEmptyState_withOnClickHandlerAndClickListener() {
+ mainListView.visibility = View.VISIBLE
+
+ val clickListener = mock<EmptyState.ClickListener>()
+ val onClickHandler = mock<View.OnClickListener>()
+
+ val emptyState =
+ object : EmptyState {
+ override fun getTitle() = "Test title"
+ override fun getSubtitle() = "Test subtitle"
+
+ override fun getButtonClickListener() = clickListener
+ }
+ emptyStateUiHelper.showEmptyState(emptyState, onClickHandler)
+
+ assertThat(mainListView.visibility).isEqualTo(View.GONE)
+ assertThat(emptyStateRootView.visibility).isEqualTo(View.VISIBLE)
+ assertThat(emptyStateTitleView.visibility).isEqualTo(View.VISIBLE)
+ assertThat(emptyStateSubtitleView.visibility).isEqualTo(View.VISIBLE)
+ assertThat(emptyStateButtonView.visibility).isEqualTo(View.VISIBLE) // Now shown.
+ assertThat(emptyStateProgressView.visibility).isEqualTo(View.GONE)
+ assertThat(emptyStateDefaultTextView.visibility).isEqualTo(View.GONE)
+
+ assertThat(emptyStateTitleView.text).isEqualTo("Test title")
+ assertThat(emptyStateSubtitleView.text).isEqualTo("Test subtitle")
+
+ emptyStateButtonView.performClick()
+
+ verify(onClickHandler).onClick(emptyStateButtonView)
+ // The test didn't explicitly configure its `OnClickListener` to relay the click event on
+ // to the `EmptyState.ClickListener`, so it still won't have fired here.
+ verify(clickListener, never()).onClick(any())
+ }
}