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