1d349adb3941c88ae27ec451acd19641ba03205dfMaurice Lam/*
2d349adb3941c88ae27ec451acd19641ba03205dfMaurice Lam * Copyright (C) 2017 The Android Open Source Project
3d349adb3941c88ae27ec451acd19641ba03205dfMaurice Lam *
4d349adb3941c88ae27ec451acd19641ba03205dfMaurice Lam * Licensed under the Apache License, Version 2.0 (the "License");
5d349adb3941c88ae27ec451acd19641ba03205dfMaurice Lam * you may not use this file except in compliance with the License.
6d349adb3941c88ae27ec451acd19641ba03205dfMaurice Lam * You may obtain a copy of the License at
7d349adb3941c88ae27ec451acd19641ba03205dfMaurice Lam *
8d349adb3941c88ae27ec451acd19641ba03205dfMaurice Lam *      http://www.apache.org/licenses/LICENSE-2.0
9d349adb3941c88ae27ec451acd19641ba03205dfMaurice Lam *
10d349adb3941c88ae27ec451acd19641ba03205dfMaurice Lam * Unless required by applicable law or agreed to in writing, software
11d349adb3941c88ae27ec451acd19641ba03205dfMaurice Lam * distributed under the License is distributed on an "AS IS" BASIS,
12d349adb3941c88ae27ec451acd19641ba03205dfMaurice Lam * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13d349adb3941c88ae27ec451acd19641ba03205dfMaurice Lam * See the License for the specific language governing permissions and
14d349adb3941c88ae27ec451acd19641ba03205dfMaurice Lam * limitations under the License.
15d349adb3941c88ae27ec451acd19641ba03205dfMaurice Lam */
16d349adb3941c88ae27ec451acd19641ba03205dfMaurice Lam
17d349adb3941c88ae27ec451acd19641ba03205dfMaurice Lampackage com.android.setupwizardlib.template;
18d349adb3941c88ae27ec451acd19641ba03205dfMaurice Lam
19d349adb3941c88ae27ec451acd19641ba03205dfMaurice Lamimport android.content.Context;
20d349adb3941c88ae27ec451acd19641ba03205dfMaurice Lamimport android.content.res.TypedArray;
21d349adb3941c88ae27ec451acd19641ba03205dfMaurice Lamimport android.graphics.drawable.Drawable;
22d349adb3941c88ae27ec451acd19641ba03205dfMaurice Lamimport android.os.Build;
23d349adb3941c88ae27ec451acd19641ba03205dfMaurice Lamimport android.os.Build.VERSION_CODES;
24d349adb3941c88ae27ec451acd19641ba03205dfMaurice Lamimport android.support.annotation.AttrRes;
25d349adb3941c88ae27ec451acd19641ba03205dfMaurice Lamimport android.support.annotation.NonNull;
26d349adb3941c88ae27ec451acd19641ba03205dfMaurice Lamimport android.support.annotation.Nullable;
27d349adb3941c88ae27ec451acd19641ba03205dfMaurice Lamimport android.util.AttributeSet;
28d349adb3941c88ae27ec451acd19641ba03205dfMaurice Lamimport android.view.View;
29d349adb3941c88ae27ec451acd19641ba03205dfMaurice Lamimport android.widget.HeaderViewListAdapter;
30d349adb3941c88ae27ec451acd19641ba03205dfMaurice Lamimport android.widget.ListAdapter;
31d349adb3941c88ae27ec451acd19641ba03205dfMaurice Lamimport android.widget.ListView;
32d349adb3941c88ae27ec451acd19641ba03205dfMaurice Lam
33d349adb3941c88ae27ec451acd19641ba03205dfMaurice Lamimport com.android.setupwizardlib.R;
34d349adb3941c88ae27ec451acd19641ba03205dfMaurice Lamimport com.android.setupwizardlib.TemplateLayout;
35d349adb3941c88ae27ec451acd19641ba03205dfMaurice Lamimport com.android.setupwizardlib.items.ItemAdapter;
36d349adb3941c88ae27ec451acd19641ba03205dfMaurice Lamimport com.android.setupwizardlib.items.ItemGroup;
37d349adb3941c88ae27ec451acd19641ba03205dfMaurice Lamimport com.android.setupwizardlib.items.ItemInflater;
38d349adb3941c88ae27ec451acd19641ba03205dfMaurice Lamimport com.android.setupwizardlib.util.DrawableLayoutDirectionHelper;
39d349adb3941c88ae27ec451acd19641ba03205dfMaurice Lam
40d349adb3941c88ae27ec451acd19641ba03205dfMaurice Lam/**
41d349adb3941c88ae27ec451acd19641ba03205dfMaurice Lam * A {@link Mixin} for interacting with ListViews.
42d349adb3941c88ae27ec451acd19641ba03205dfMaurice Lam */
43d349adb3941c88ae27ec451acd19641ba03205dfMaurice Lampublic class ListMixin implements Mixin {
44d349adb3941c88ae27ec451acd19641ba03205dfMaurice Lam
45d349adb3941c88ae27ec451acd19641ba03205dfMaurice Lam    private TemplateLayout mTemplateLayout;
46d349adb3941c88ae27ec451acd19641ba03205dfMaurice Lam
47d349adb3941c88ae27ec451acd19641ba03205dfMaurice Lam    @Nullable
48d349adb3941c88ae27ec451acd19641ba03205dfMaurice Lam    private ListView mListView;
49d349adb3941c88ae27ec451acd19641ba03205dfMaurice Lam
50d349adb3941c88ae27ec451acd19641ba03205dfMaurice Lam    private Drawable mDivider;
51d349adb3941c88ae27ec451acd19641ba03205dfMaurice Lam    private Drawable mDefaultDivider;
526011b35deae30839cd454e0ec5866487e90860f5Maurice Lam
536011b35deae30839cd454e0ec5866487e90860f5Maurice Lam    private int mDividerInsetStart;
546011b35deae30839cd454e0ec5866487e90860f5Maurice Lam    private int mDividerInsetEnd;
55d349adb3941c88ae27ec451acd19641ba03205dfMaurice Lam
56d349adb3941c88ae27ec451acd19641ba03205dfMaurice Lam    /**
57d349adb3941c88ae27ec451acd19641ba03205dfMaurice Lam     * @param layout The layout this mixin belongs to.
58d349adb3941c88ae27ec451acd19641ba03205dfMaurice Lam     */
59d349adb3941c88ae27ec451acd19641ba03205dfMaurice Lam    public ListMixin(@NonNull TemplateLayout layout, @Nullable AttributeSet attrs,
60d349adb3941c88ae27ec451acd19641ba03205dfMaurice Lam            @AttrRes int defStyleAttr) {
61d349adb3941c88ae27ec451acd19641ba03205dfMaurice Lam        mTemplateLayout = layout;
62d349adb3941c88ae27ec451acd19641ba03205dfMaurice Lam
63d349adb3941c88ae27ec451acd19641ba03205dfMaurice Lam        final Context context = layout.getContext();
64d349adb3941c88ae27ec451acd19641ba03205dfMaurice Lam        final TypedArray a = context.obtainStyledAttributes(
65d349adb3941c88ae27ec451acd19641ba03205dfMaurice Lam                attrs, R.styleable.SuwListMixin, defStyleAttr, 0);
66d349adb3941c88ae27ec451acd19641ba03205dfMaurice Lam
67d349adb3941c88ae27ec451acd19641ba03205dfMaurice Lam        final int entries = a.getResourceId(R.styleable.SuwListMixin_android_entries, 0);
68d349adb3941c88ae27ec451acd19641ba03205dfMaurice Lam        if (entries != 0) {
69d349adb3941c88ae27ec451acd19641ba03205dfMaurice Lam            final ItemGroup inflated =
70d349adb3941c88ae27ec451acd19641ba03205dfMaurice Lam                    (ItemGroup) new ItemInflater(context).inflate(entries);
71d349adb3941c88ae27ec451acd19641ba03205dfMaurice Lam            setAdapter(new ItemAdapter(inflated));
72d349adb3941c88ae27ec451acd19641ba03205dfMaurice Lam        }
73d349adb3941c88ae27ec451acd19641ba03205dfMaurice Lam        int dividerInset =
746011b35deae30839cd454e0ec5866487e90860f5Maurice Lam                a.getDimensionPixelSize(R.styleable.SuwListMixin_suwDividerInset, -1);
756011b35deae30839cd454e0ec5866487e90860f5Maurice Lam        if (dividerInset != -1) {
766011b35deae30839cd454e0ec5866487e90860f5Maurice Lam            setDividerInset(dividerInset);
776011b35deae30839cd454e0ec5866487e90860f5Maurice Lam        } else {
786011b35deae30839cd454e0ec5866487e90860f5Maurice Lam            int dividerInsetStart =
796011b35deae30839cd454e0ec5866487e90860f5Maurice Lam                    a.getDimensionPixelSize(R.styleable.SuwListMixin_suwDividerInsetStart, 0);
806011b35deae30839cd454e0ec5866487e90860f5Maurice Lam            int dividerInsetEnd =
816011b35deae30839cd454e0ec5866487e90860f5Maurice Lam                    a.getDimensionPixelSize(R.styleable.SuwListMixin_suwDividerInsetEnd, 0);
826011b35deae30839cd454e0ec5866487e90860f5Maurice Lam            setDividerInsets(dividerInsetStart, dividerInsetEnd);
836011b35deae30839cd454e0ec5866487e90860f5Maurice Lam        }
84d349adb3941c88ae27ec451acd19641ba03205dfMaurice Lam        a.recycle();
85d349adb3941c88ae27ec451acd19641ba03205dfMaurice Lam    }
86d349adb3941c88ae27ec451acd19641ba03205dfMaurice Lam
87d349adb3941c88ae27ec451acd19641ba03205dfMaurice Lam    /**
88d349adb3941c88ae27ec451acd19641ba03205dfMaurice Lam     * @return The list view contained in the layout, as marked by {@code @android:id/list}. This
89d349adb3941c88ae27ec451acd19641ba03205dfMaurice Lam     *         will return {@code null} if the list doesn't exist in the layout.
90d349adb3941c88ae27ec451acd19641ba03205dfMaurice Lam     */
91d349adb3941c88ae27ec451acd19641ba03205dfMaurice Lam    public ListView getListView() {
92d349adb3941c88ae27ec451acd19641ba03205dfMaurice Lam        return getListViewInternal();
93d349adb3941c88ae27ec451acd19641ba03205dfMaurice Lam    }
94d349adb3941c88ae27ec451acd19641ba03205dfMaurice Lam
95d349adb3941c88ae27ec451acd19641ba03205dfMaurice Lam    // Client code can assume getListView() will not be null if they know their template contains
96d349adb3941c88ae27ec451acd19641ba03205dfMaurice Lam    // the list, but this mixin cannot. Any usages of getListView in this mixin needs null checks.
97d349adb3941c88ae27ec451acd19641ba03205dfMaurice Lam    @Nullable
98d349adb3941c88ae27ec451acd19641ba03205dfMaurice Lam    private ListView getListViewInternal() {
99d349adb3941c88ae27ec451acd19641ba03205dfMaurice Lam        if (mListView == null) {
100d349adb3941c88ae27ec451acd19641ba03205dfMaurice Lam            final View list = mTemplateLayout.findManagedViewById(android.R.id.list);
101d349adb3941c88ae27ec451acd19641ba03205dfMaurice Lam            if (list instanceof ListView) {
102d349adb3941c88ae27ec451acd19641ba03205dfMaurice Lam                mListView = (ListView) list;
103d349adb3941c88ae27ec451acd19641ba03205dfMaurice Lam            }
104d349adb3941c88ae27ec451acd19641ba03205dfMaurice Lam        }
105d349adb3941c88ae27ec451acd19641ba03205dfMaurice Lam        return mListView;
106d349adb3941c88ae27ec451acd19641ba03205dfMaurice Lam    }
107d349adb3941c88ae27ec451acd19641ba03205dfMaurice Lam
108d349adb3941c88ae27ec451acd19641ba03205dfMaurice Lam    /**
109d349adb3941c88ae27ec451acd19641ba03205dfMaurice Lam     * List mixin needs to update the dividers if the layout direction has changed. This method
110d349adb3941c88ae27ec451acd19641ba03205dfMaurice Lam     * should be called when {@link View#onLayout(boolean, int, int, int, int)} of the template
111d349adb3941c88ae27ec451acd19641ba03205dfMaurice Lam     * is called.
112d349adb3941c88ae27ec451acd19641ba03205dfMaurice Lam     */
113d349adb3941c88ae27ec451acd19641ba03205dfMaurice Lam    public void onLayout() {
114d349adb3941c88ae27ec451acd19641ba03205dfMaurice Lam        if (mDivider == null) {
115d349adb3941c88ae27ec451acd19641ba03205dfMaurice Lam            // Update divider in case layout direction has just been resolved
116d349adb3941c88ae27ec451acd19641ba03205dfMaurice Lam            updateDivider();
117d349adb3941c88ae27ec451acd19641ba03205dfMaurice Lam        }
118d349adb3941c88ae27ec451acd19641ba03205dfMaurice Lam    }
119d349adb3941c88ae27ec451acd19641ba03205dfMaurice Lam
120d349adb3941c88ae27ec451acd19641ba03205dfMaurice Lam    /**
121d349adb3941c88ae27ec451acd19641ba03205dfMaurice Lam     * Gets the adapter of the list view in this layout. If the adapter is a HeaderViewListAdapter,
122d349adb3941c88ae27ec451acd19641ba03205dfMaurice Lam     * this method will unwrap it and return the underlying adapter.
123d349adb3941c88ae27ec451acd19641ba03205dfMaurice Lam     *
124d349adb3941c88ae27ec451acd19641ba03205dfMaurice Lam     * @return The adapter, or {@code null} if there is no list, or if the list has no adapter.
125d349adb3941c88ae27ec451acd19641ba03205dfMaurice Lam     */
126d349adb3941c88ae27ec451acd19641ba03205dfMaurice Lam    public ListAdapter getAdapter() {
127d349adb3941c88ae27ec451acd19641ba03205dfMaurice Lam        final ListView listView = getListViewInternal();
128d349adb3941c88ae27ec451acd19641ba03205dfMaurice Lam        if (listView != null) {
129d349adb3941c88ae27ec451acd19641ba03205dfMaurice Lam            final ListAdapter adapter = listView.getAdapter();
130d349adb3941c88ae27ec451acd19641ba03205dfMaurice Lam            if (adapter instanceof HeaderViewListAdapter) {
131d349adb3941c88ae27ec451acd19641ba03205dfMaurice Lam                return ((HeaderViewListAdapter) adapter).getWrappedAdapter();
132d349adb3941c88ae27ec451acd19641ba03205dfMaurice Lam            }
133d349adb3941c88ae27ec451acd19641ba03205dfMaurice Lam            return adapter;
134d349adb3941c88ae27ec451acd19641ba03205dfMaurice Lam        }
135d349adb3941c88ae27ec451acd19641ba03205dfMaurice Lam        return null;
136d349adb3941c88ae27ec451acd19641ba03205dfMaurice Lam    }
137d349adb3941c88ae27ec451acd19641ba03205dfMaurice Lam
138d349adb3941c88ae27ec451acd19641ba03205dfMaurice Lam    /**
139d349adb3941c88ae27ec451acd19641ba03205dfMaurice Lam     * Sets the adapter on the list view in this layout.
140d349adb3941c88ae27ec451acd19641ba03205dfMaurice Lam     */
141d349adb3941c88ae27ec451acd19641ba03205dfMaurice Lam    public void setAdapter(ListAdapter adapter) {
142d349adb3941c88ae27ec451acd19641ba03205dfMaurice Lam        final ListView listView = getListViewInternal();
143d349adb3941c88ae27ec451acd19641ba03205dfMaurice Lam        if (listView != null) {
144d349adb3941c88ae27ec451acd19641ba03205dfMaurice Lam            listView.setAdapter(adapter);
145d349adb3941c88ae27ec451acd19641ba03205dfMaurice Lam        }
146d349adb3941c88ae27ec451acd19641ba03205dfMaurice Lam    }
147d349adb3941c88ae27ec451acd19641ba03205dfMaurice Lam
148d349adb3941c88ae27ec451acd19641ba03205dfMaurice Lam    /**
1496011b35deae30839cd454e0ec5866487e90860f5Maurice Lam     * @deprecated Use {@link #setDividerInsets(int, int)} instead.
1506011b35deae30839cd454e0ec5866487e90860f5Maurice Lam     */
1516011b35deae30839cd454e0ec5866487e90860f5Maurice Lam    @Deprecated
1526011b35deae30839cd454e0ec5866487e90860f5Maurice Lam    public void setDividerInset(int inset) {
1536011b35deae30839cd454e0ec5866487e90860f5Maurice Lam        setDividerInsets(inset, 0);
1546011b35deae30839cd454e0ec5866487e90860f5Maurice Lam    }
1556011b35deae30839cd454e0ec5866487e90860f5Maurice Lam
1566011b35deae30839cd454e0ec5866487e90860f5Maurice Lam    /**
157d349adb3941c88ae27ec451acd19641ba03205dfMaurice Lam     * Sets the start inset of the divider. This will use the default divider drawable set in the
1586011b35deae30839cd454e0ec5866487e90860f5Maurice Lam     * theme and apply insets to it.
159d349adb3941c88ae27ec451acd19641ba03205dfMaurice Lam     *
1606011b35deae30839cd454e0ec5866487e90860f5Maurice Lam     * @param start The number of pixels to inset on the "start" side of the list divider. Typically
161d349adb3941c88ae27ec451acd19641ba03205dfMaurice Lam     *              this will be either {@code @dimen/suw_items_glif_icon_divider_inset} or
162d349adb3941c88ae27ec451acd19641ba03205dfMaurice Lam     *              {@code @dimen/suw_items_glif_text_divider_inset}.
1636011b35deae30839cd454e0ec5866487e90860f5Maurice Lam     * @param end The number of pixels to inset on the "end" side of the list divider.
164d349adb3941c88ae27ec451acd19641ba03205dfMaurice Lam     */
1656011b35deae30839cd454e0ec5866487e90860f5Maurice Lam    public void setDividerInsets(int start, int end) {
1666011b35deae30839cd454e0ec5866487e90860f5Maurice Lam        mDividerInsetStart = start;
1676011b35deae30839cd454e0ec5866487e90860f5Maurice Lam        mDividerInsetEnd = end;
168d349adb3941c88ae27ec451acd19641ba03205dfMaurice Lam        updateDivider();
169d349adb3941c88ae27ec451acd19641ba03205dfMaurice Lam    }
170d349adb3941c88ae27ec451acd19641ba03205dfMaurice Lam
171d349adb3941c88ae27ec451acd19641ba03205dfMaurice Lam    /**
172d349adb3941c88ae27ec451acd19641ba03205dfMaurice Lam     * @return The number of pixels inset on the start side of the divider.
1736011b35deae30839cd454e0ec5866487e90860f5Maurice Lam     * @deprecated This is the same as {@link #getDividerInsetStart()}. Use that instead.
174d349adb3941c88ae27ec451acd19641ba03205dfMaurice Lam     */
1756011b35deae30839cd454e0ec5866487e90860f5Maurice Lam    @Deprecated
176d349adb3941c88ae27ec451acd19641ba03205dfMaurice Lam    public int getDividerInset() {
1776011b35deae30839cd454e0ec5866487e90860f5Maurice Lam        return getDividerInsetStart();
1786011b35deae30839cd454e0ec5866487e90860f5Maurice Lam    }
1796011b35deae30839cd454e0ec5866487e90860f5Maurice Lam
1806011b35deae30839cd454e0ec5866487e90860f5Maurice Lam    /**
1816011b35deae30839cd454e0ec5866487e90860f5Maurice Lam     * @return The number of pixels inset on the start side of the divider.
1826011b35deae30839cd454e0ec5866487e90860f5Maurice Lam     */
1836011b35deae30839cd454e0ec5866487e90860f5Maurice Lam    public int getDividerInsetStart() {
1846011b35deae30839cd454e0ec5866487e90860f5Maurice Lam        return mDividerInsetStart;
1856011b35deae30839cd454e0ec5866487e90860f5Maurice Lam    }
1866011b35deae30839cd454e0ec5866487e90860f5Maurice Lam
1876011b35deae30839cd454e0ec5866487e90860f5Maurice Lam    /**
1886011b35deae30839cd454e0ec5866487e90860f5Maurice Lam     * @return The number of pixels inset on the end side of the divider.
1896011b35deae30839cd454e0ec5866487e90860f5Maurice Lam     */
1906011b35deae30839cd454e0ec5866487e90860f5Maurice Lam    public int getDividerInsetEnd() {
1916011b35deae30839cd454e0ec5866487e90860f5Maurice Lam        return mDividerInsetEnd;
192d349adb3941c88ae27ec451acd19641ba03205dfMaurice Lam    }
193d349adb3941c88ae27ec451acd19641ba03205dfMaurice Lam
194d349adb3941c88ae27ec451acd19641ba03205dfMaurice Lam    private void updateDivider() {
195d349adb3941c88ae27ec451acd19641ba03205dfMaurice Lam        final ListView listView = getListViewInternal();
196d349adb3941c88ae27ec451acd19641ba03205dfMaurice Lam        if (listView == null) {
197d349adb3941c88ae27ec451acd19641ba03205dfMaurice Lam            return;
198d349adb3941c88ae27ec451acd19641ba03205dfMaurice Lam        }
199d349adb3941c88ae27ec451acd19641ba03205dfMaurice Lam        boolean shouldUpdate = true;
200d349adb3941c88ae27ec451acd19641ba03205dfMaurice Lam        if (Build.VERSION.SDK_INT >= VERSION_CODES.KITKAT) {
201d349adb3941c88ae27ec451acd19641ba03205dfMaurice Lam            shouldUpdate = mTemplateLayout.isLayoutDirectionResolved();
202d349adb3941c88ae27ec451acd19641ba03205dfMaurice Lam        }
203d349adb3941c88ae27ec451acd19641ba03205dfMaurice Lam        if (shouldUpdate) {
204d349adb3941c88ae27ec451acd19641ba03205dfMaurice Lam            if (mDefaultDivider == null) {
205d349adb3941c88ae27ec451acd19641ba03205dfMaurice Lam                mDefaultDivider = listView.getDivider();
206d349adb3941c88ae27ec451acd19641ba03205dfMaurice Lam            }
207d349adb3941c88ae27ec451acd19641ba03205dfMaurice Lam            mDivider = DrawableLayoutDirectionHelper.createRelativeInsetDrawable(
208d349adb3941c88ae27ec451acd19641ba03205dfMaurice Lam                    mDefaultDivider,
2096011b35deae30839cd454e0ec5866487e90860f5Maurice Lam                    mDividerInsetStart /* start */,
210d349adb3941c88ae27ec451acd19641ba03205dfMaurice Lam                    0 /* top */,
2116011b35deae30839cd454e0ec5866487e90860f5Maurice Lam                    mDividerInsetEnd /* end */,
212d349adb3941c88ae27ec451acd19641ba03205dfMaurice Lam                    0 /* bottom */,
213d349adb3941c88ae27ec451acd19641ba03205dfMaurice Lam                    mTemplateLayout);
214d349adb3941c88ae27ec451acd19641ba03205dfMaurice Lam            listView.setDivider(mDivider);
215d349adb3941c88ae27ec451acd19641ba03205dfMaurice Lam        }
216d349adb3941c88ae27ec451acd19641ba03205dfMaurice Lam    }
217d349adb3941c88ae27ec451acd19641ba03205dfMaurice Lam
218d349adb3941c88ae27ec451acd19641ba03205dfMaurice Lam    /**
219d349adb3941c88ae27ec451acd19641ba03205dfMaurice Lam     * @return The drawable used as the divider.
220d349adb3941c88ae27ec451acd19641ba03205dfMaurice Lam     */
221d349adb3941c88ae27ec451acd19641ba03205dfMaurice Lam    public Drawable getDivider() {
222d349adb3941c88ae27ec451acd19641ba03205dfMaurice Lam        return mDivider;
223d349adb3941c88ae27ec451acd19641ba03205dfMaurice Lam    }
224d349adb3941c88ae27ec451acd19641ba03205dfMaurice Lam}
225