1 /* 2 * Copyright (C) 2023 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 package com.android.intentresolver.emptystate; 17 18 import android.view.View; 19 import android.view.ViewGroup; 20 import android.widget.Button; 21 import android.widget.TextView; 22 23 import java.util.Optional; 24 import java.util.function.Supplier; 25 26 /** 27 * Helper for building `MultiProfilePagerAdapter` tab UIs for profile tabs that are "blocked" by 28 * some empty-state status. 29 */ 30 public class EmptyStateUiHelper { 31 private final Supplier<Optional<Integer>> mContainerBottomPaddingOverrideSupplier; 32 private final View mEmptyStateView; 33 private final View mListView; 34 private final View mEmptyStateContainerView; 35 private final TextView mEmptyStateTitleView; 36 private final TextView mEmptyStateSubtitleView; 37 private final Button mEmptyStateButtonView; 38 private final View mEmptyStateProgressView; 39 private final View mEmptyStateEmptyView; 40 EmptyStateUiHelper( ViewGroup rootView, int listViewResourceId, Supplier<Optional<Integer>> containerBottomPaddingOverrideSupplier)41 public EmptyStateUiHelper( 42 ViewGroup rootView, 43 int listViewResourceId, 44 Supplier<Optional<Integer>> containerBottomPaddingOverrideSupplier) { 45 mContainerBottomPaddingOverrideSupplier = containerBottomPaddingOverrideSupplier; 46 mEmptyStateView = 47 rootView.requireViewById(com.android.internal.R.id.resolver_empty_state); 48 mListView = rootView.requireViewById(listViewResourceId); 49 mEmptyStateContainerView = mEmptyStateView.requireViewById( 50 com.android.internal.R.id.resolver_empty_state_container); 51 mEmptyStateTitleView = mEmptyStateView.requireViewById( 52 com.android.internal.R.id.resolver_empty_state_title); 53 mEmptyStateSubtitleView = mEmptyStateView.requireViewById( 54 com.android.internal.R.id.resolver_empty_state_subtitle); 55 mEmptyStateButtonView = mEmptyStateView.requireViewById( 56 com.android.internal.R.id.resolver_empty_state_button); 57 mEmptyStateProgressView = mEmptyStateView.requireViewById( 58 com.android.internal.R.id.resolver_empty_state_progress); 59 mEmptyStateEmptyView = mEmptyStateView.requireViewById(com.android.internal.R.id.empty); 60 } 61 62 /** 63 * Display the described empty state. 64 * @param emptyState the data describing the cause of this empty-state condition. 65 * @param buttonOnClick handler for a button that the user might be able to use to circumvent 66 * the empty-state condition. If null, no button will be displayed. 67 */ showEmptyState(EmptyState emptyState, View.OnClickListener buttonOnClick)68 public void showEmptyState(EmptyState emptyState, View.OnClickListener buttonOnClick) { 69 resetViewVisibilities(); 70 setupContainerPadding(); 71 72 String title = emptyState.getTitle(); 73 if (title != null) { 74 mEmptyStateTitleView.setVisibility(View.VISIBLE); 75 mEmptyStateTitleView.setText(title); 76 } else { 77 mEmptyStateTitleView.setVisibility(View.GONE); 78 } 79 80 String subtitle = emptyState.getSubtitle(); 81 if (subtitle != null) { 82 mEmptyStateSubtitleView.setVisibility(View.VISIBLE); 83 mEmptyStateSubtitleView.setText(subtitle); 84 } else { 85 mEmptyStateSubtitleView.setVisibility(View.GONE); 86 } 87 88 mEmptyStateEmptyView.setVisibility( 89 emptyState.useDefaultEmptyView() ? View.VISIBLE : View.GONE); 90 // TODO: The EmptyState API says that if `useDefaultEmptyView()` is true, we'll ignore the 91 // state's specified title/subtitle; where (if anywhere) is that implemented? 92 93 mEmptyStateButtonView.setVisibility(buttonOnClick != null ? View.VISIBLE : View.GONE); 94 mEmptyStateButtonView.setOnClickListener(buttonOnClick); 95 96 // Don't show the main list view when we're showing an empty state. 97 mListView.setVisibility(View.GONE); 98 } 99 100 /** Sets up the padding of the view containing the empty state screens. */ setupContainerPadding()101 public void setupContainerPadding() { 102 Optional<Integer> bottomPaddingOverride = mContainerBottomPaddingOverrideSupplier.get(); 103 bottomPaddingOverride.ifPresent(paddingBottom -> 104 mEmptyStateContainerView.setPadding( 105 mEmptyStateContainerView.getPaddingLeft(), 106 mEmptyStateContainerView.getPaddingTop(), 107 mEmptyStateContainerView.getPaddingRight(), 108 paddingBottom)); 109 } 110 showSpinner()111 public void showSpinner() { 112 mEmptyStateTitleView.setVisibility(View.INVISIBLE); 113 // TODO: subtitle? 114 mEmptyStateButtonView.setVisibility(View.INVISIBLE); 115 mEmptyStateProgressView.setVisibility(View.VISIBLE); 116 mEmptyStateEmptyView.setVisibility(View.GONE); 117 } 118 hide()119 public void hide() { 120 mEmptyStateView.setVisibility(View.GONE); 121 mListView.setVisibility(View.VISIBLE); 122 } 123 124 // TODO: this is exposed for testing so we can thoroughly prepare initial conditions that let us 125 // observe the resulting change. In reality it's only invoked as part of `showEmptyState()` and 126 // we could consider setting up narrower "realistic" preconditions to make assertions about the 127 // higher-level operation. resetViewVisibilities()128 public void resetViewVisibilities() { 129 mEmptyStateTitleView.setVisibility(View.VISIBLE); 130 mEmptyStateSubtitleView.setVisibility(View.VISIBLE); 131 mEmptyStateButtonView.setVisibility(View.INVISIBLE); 132 mEmptyStateProgressView.setVisibility(View.GONE); 133 mEmptyStateEmptyView.setVisibility(View.GONE); 134 mEmptyStateView.setVisibility(View.VISIBLE); 135 } 136 } 137