1 /*
2  * Copyright (C) 2016 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.internal.widget;
17 
18 import android.annotation.IdRes;
19 import android.content.Context;
20 import android.util.AttributeSet;
21 import android.view.View;
22 import android.view.ViewGroup;
23 import android.widget.AdapterView;
24 import android.widget.ListAdapter;
25 import android.widget.ListView;
26 import android.widget.HeaderViewListAdapter;
27 
28 import java.util.ArrayList;
29 import java.util.function.Predicate;
30 
31 public class WatchHeaderListView extends ListView {
32     private View mTopPanel;
33 
WatchHeaderListView(Context context, AttributeSet attrs)34     public WatchHeaderListView(Context context, AttributeSet attrs) {
35         super(context, attrs);
36     }
37 
WatchHeaderListView(Context context, AttributeSet attrs, int defStyleAttr)38     public WatchHeaderListView(Context context, AttributeSet attrs, int defStyleAttr) {
39         super(context, attrs, defStyleAttr);
40     }
41 
WatchHeaderListView( Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes)42     public WatchHeaderListView(
43             Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
44         super(context, attrs, defStyleAttr, defStyleRes);
45     }
46 
47     @Override
wrapHeaderListAdapterInternal( ArrayList<ListView.FixedViewInfo> headerViewInfos, ArrayList<ListView.FixedViewInfo> footerViewInfos, ListAdapter adapter)48     protected HeaderViewListAdapter wrapHeaderListAdapterInternal(
49             ArrayList<ListView.FixedViewInfo> headerViewInfos,
50             ArrayList<ListView.FixedViewInfo> footerViewInfos,
51             ListAdapter adapter) {
52         return new WatchHeaderListAdapter(headerViewInfos, footerViewInfos, adapter);
53     }
54 
55     @Override
addView(View child, ViewGroup.LayoutParams params)56     public void addView(View child, ViewGroup.LayoutParams params) {
57         if (mTopPanel == null) {
58             setTopPanel(child);
59         } else {
60             throw new IllegalStateException("WatchHeaderListView can host only one header");
61         }
62     }
63 
setTopPanel(View v)64     public void setTopPanel(View v) {
65         mTopPanel = v;
66         wrapAdapterIfNecessary();
67     }
68 
69     @Override
setAdapter(ListAdapter adapter)70     public void setAdapter(ListAdapter adapter) {
71         super.setAdapter(adapter);
72         wrapAdapterIfNecessary();
73     }
74 
75     @Override
findViewTraversal(@dRes int id)76     protected View findViewTraversal(@IdRes int id) {
77         View v = super.findViewTraversal(id);
78         if (v == null && mTopPanel != null && !mTopPanel.isRootNamespace()) {
79             return mTopPanel.findViewById(id);
80         }
81         return v;
82     }
83 
84     @Override
findViewWithTagTraversal(Object tag)85     protected View findViewWithTagTraversal(Object tag) {
86         View v = super.findViewWithTagTraversal(tag);
87         if (v == null && mTopPanel != null && !mTopPanel.isRootNamespace()) {
88             return mTopPanel.findViewWithTag(tag);
89         }
90         return v;
91     }
92 
93     @Override
findViewByPredicateTraversal( Predicate<View> predicate, View childToSkip)94     protected <T extends View> T findViewByPredicateTraversal(
95             Predicate<View> predicate, View childToSkip) {
96         View v = super.findViewByPredicateTraversal(predicate, childToSkip);
97         if (v == null && mTopPanel != null && mTopPanel != childToSkip
98                 && !mTopPanel.isRootNamespace()) {
99             return (T) mTopPanel.findViewByPredicate(predicate);
100         }
101         return (T) v;
102     }
103 
104     @Override
getHeaderViewsCount()105     public int getHeaderViewsCount() {
106         return mTopPanel == null ? super.getHeaderViewsCount()
107                 : super.getHeaderViewsCount() + (mTopPanel.getVisibility() == GONE ? 0 : 1);
108     }
109 
wrapAdapterIfNecessary()110     private void wrapAdapterIfNecessary() {
111         ListAdapter adapter = getAdapter();
112         if (adapter != null && mTopPanel != null) {
113             if (!(adapter instanceof WatchHeaderListAdapter)) {
114                 wrapHeaderListAdapterInternal();
115             }
116 
117             ((WatchHeaderListAdapter) getAdapter()).setTopPanel(mTopPanel);
118             dispatchDataSetObserverOnChangedInternal();
119         }
120     }
121 
122     private static class WatchHeaderListAdapter extends HeaderViewListAdapter {
123         private View mTopPanel;
124 
WatchHeaderListAdapter( ArrayList<ListView.FixedViewInfo> headerViewInfos, ArrayList<ListView.FixedViewInfo> footerViewInfos, ListAdapter adapter)125         public WatchHeaderListAdapter(
126                 ArrayList<ListView.FixedViewInfo> headerViewInfos,
127                 ArrayList<ListView.FixedViewInfo> footerViewInfos,
128                 ListAdapter adapter) {
129             super(headerViewInfos, footerViewInfos, adapter);
130         }
131 
setTopPanel(View v)132         public void setTopPanel(View v) {
133             mTopPanel = v;
134         }
135 
getTopPanelCount()136         private int getTopPanelCount() {
137             return (mTopPanel == null || mTopPanel.getVisibility() == GONE) ? 0 : 1;
138         }
139 
140         @Override
getCount()141         public int getCount() {
142             return super.getCount() + getTopPanelCount();
143         }
144 
145         @Override
areAllItemsEnabled()146         public boolean areAllItemsEnabled() {
147             return getTopPanelCount() == 0 && super.areAllItemsEnabled();
148         }
149 
150         @Override
isEnabled(int position)151         public boolean isEnabled(int position) {
152             int topPanelCount = getTopPanelCount();
153             return position < topPanelCount ? false : super.isEnabled(position - topPanelCount);
154         }
155 
156         @Override
getItem(int position)157         public Object getItem(int position) {
158             int topPanelCount = getTopPanelCount();
159             return position < topPanelCount ? null : super.getItem(position - topPanelCount);
160         }
161 
162         @Override
getItemId(int position)163         public long getItemId(int position) {
164             int numHeaders = getHeadersCount() + getTopPanelCount();
165             if (getWrappedAdapter() != null && position >= numHeaders) {
166                 int adjPosition = position - numHeaders;
167                 int adapterCount = getWrappedAdapter().getCount();
168                 if (adjPosition < adapterCount) {
169                     return getWrappedAdapter().getItemId(adjPosition);
170                 }
171             }
172             return -1;
173         }
174 
175         @Override
getView(int position, View convertView, ViewGroup parent)176         public View getView(int position, View convertView, ViewGroup parent) {
177             int topPanelCount = getTopPanelCount();
178             return position < topPanelCount
179                     ? mTopPanel : super.getView(position - topPanelCount, convertView, parent);
180         }
181 
182         @Override
getItemViewType(int position)183         public int getItemViewType(int position) {
184             int numHeaders = getHeadersCount() + getTopPanelCount();
185             if (getWrappedAdapter() != null && position >= numHeaders) {
186                 int adjPosition = position - numHeaders;
187                 int adapterCount = getWrappedAdapter().getCount();
188                 if (adjPosition < adapterCount) {
189                     return getWrappedAdapter().getItemViewType(adjPosition);
190                 }
191             }
192 
193             return AdapterView.ITEM_VIEW_TYPE_HEADER_OR_FOOTER;
194         }
195     }
196 }
197