1/*
2 * Copyright (C) 2015 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
17package com.android.setupwizardlib;
18
19import android.content.Context;
20import android.content.res.TypedArray;
21import android.graphics.drawable.Drawable;
22import android.os.Build;
23import android.support.v7.widget.LinearLayoutManager;
24import android.support.v7.widget.RecyclerView;
25import android.util.AttributeSet;
26import android.util.Log;
27import android.view.LayoutInflater;
28import android.view.View;
29import android.view.ViewGroup;
30import android.widget.TextView;
31
32import com.android.setupwizardlib.items.ItemGroup;
33import com.android.setupwizardlib.items.ItemInflater;
34import com.android.setupwizardlib.items.RecyclerItemAdapter;
35import com.android.setupwizardlib.util.DrawableLayoutDirectionHelper;
36import com.android.setupwizardlib.util.RecyclerViewRequireScrollHelper;
37import com.android.setupwizardlib.view.HeaderRecyclerView;
38import com.android.setupwizardlib.view.NavigationBar;
39
40/**
41 * A setup wizard layout for use with {@link android.support.v7.widget.RecyclerView}.
42 * {@code android:entries} can also be used to specify an
43 * {@link com.android.setupwizardlib.items.ItemHierarchy} to be used with this layout in XML.
44 *
45 * @see SetupWizardItemsLayout
46 */
47public class SetupWizardRecyclerLayout extends SetupWizardLayout {
48
49    private static final String TAG = "RecyclerLayout";
50
51    private RecyclerView.Adapter mAdapter;
52    private RecyclerView mRecyclerView;
53    private View mHeader;
54
55    private DividerItemDecoration mDividerDecoration;
56    private Drawable mDefaultDivider;
57    private Drawable mDivider;
58    private int mDividerInset;
59
60    public SetupWizardRecyclerLayout(Context context) {
61        this(context, 0, 0);
62    }
63
64    public SetupWizardRecyclerLayout(Context context, int template, int containerId) {
65        super(context, template, containerId);
66        init(context, null, 0);
67    }
68
69    public SetupWizardRecyclerLayout(Context context, AttributeSet attrs) {
70        super(context, attrs);
71        init(context, attrs, 0);
72    }
73
74    public SetupWizardRecyclerLayout(Context context, AttributeSet attrs, int defStyleAttr) {
75        super(context, attrs, defStyleAttr);
76        init(context, attrs, defStyleAttr);
77    }
78
79    private void init(Context context, AttributeSet attrs, int defStyleAttr) {
80        final TypedArray a = context.obtainStyledAttributes(attrs,
81                R.styleable.SuwSetupWizardRecyclerItemsLayout, defStyleAttr, 0);
82        final int xml = a.getResourceId(
83                R.styleable.SuwSetupWizardRecyclerItemsLayout_android_entries, 0);
84        if (xml != 0) {
85            final ItemGroup inflated = (ItemGroup) new ItemInflater(context).inflate(xml);
86            mAdapter = new RecyclerItemAdapter(inflated);
87            mAdapter.setHasStableIds(a.getBoolean(
88                    R.styleable.SuwSetupWizardRecyclerItemsLayout_suwHasStableIds, false));
89            setAdapter(mAdapter);
90        }
91        int dividerInset = a.getDimensionPixelSize(
92                R.styleable.SuwSetupWizardRecyclerItemsLayout_suwDividerInset, 0);
93        if (dividerInset == 0) {
94            dividerInset = getResources()
95                    .getDimensionPixelSize(R.dimen.suw_items_icon_divider_inset);
96        }
97        setDividerInset(dividerInset);
98        a.recycle();
99    }
100
101    @Override
102    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
103        super.onLayout(changed, left, top, right, bottom);
104        if (mDivider == null) {
105            // Update divider in case layout direction has just been resolved
106            updateDivider();
107        }
108    }
109
110    public RecyclerView.Adapter getAdapter() {
111        return mAdapter;
112    }
113
114    public void setAdapter(RecyclerView.Adapter adapter) {
115        mAdapter = adapter;
116        getRecyclerView().setAdapter(adapter);
117    }
118
119    public RecyclerView getRecyclerView() {
120        return mRecyclerView;
121    }
122
123    @Override
124    protected ViewGroup findContainer(int containerId) {
125        if (containerId == 0) {
126            containerId = R.id.suw_recycler_view;
127        }
128        return super.findContainer(containerId);
129    }
130
131    @Override
132    protected void onTemplateInflated() {
133        initRecyclerView((RecyclerView) findViewById(R.id.suw_recycler_view));
134    }
135
136    protected void initRecyclerView(RecyclerView recyclerView) {
137        mRecyclerView = recyclerView;
138        mRecyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
139        if (mRecyclerView instanceof HeaderRecyclerView) {
140            mHeader = ((HeaderRecyclerView) mRecyclerView).getHeader();
141        }
142        mDividerDecoration = DividerItemDecoration.getDefault(getContext());
143        mRecyclerView.addItemDecoration(mDividerDecoration);
144    }
145
146    @Override
147    protected View onInflateTemplate(LayoutInflater inflater, int template) {
148        if (template == 0) {
149            template = R.layout.suw_recycler_template;
150        }
151        return super.onInflateTemplate(inflater, template);
152    }
153
154    @Override
155    protected View findManagedViewById(int id) {
156        if (mHeader != null) {
157            final View view = mHeader.findViewById(id);
158            if (view != null) {
159                return view;
160            }
161        }
162        return super.findViewById(id);
163    }
164
165    @Override
166    public void requireScrollToBottom() {
167        final NavigationBar navigationBar = getNavigationBar();
168        final RecyclerView recyclerView = getRecyclerView();
169        if (navigationBar != null && recyclerView != null) {
170            RecyclerViewRequireScrollHelper.requireScroll(navigationBar, recyclerView);
171        } else {
172            Log.e(TAG, "Both suw_layout_navigation_bar and suw_recycler_view must exist in"
173                    + " the template to require scrolling.");
174        }
175    }
176
177    /**
178     * Sets the start inset of the divider. This will use the default divider drawable set in the
179     * theme and inset it {@code inset} pixels to the right (or left in RTL layouts).
180     *
181     * @param inset The number of pixels to inset on the "start" side of the list divider. Typically
182     *              this will be either {@code @dimen/suw_items_icon_divider_inset} or
183     *              {@code @dimen/suw_items_text_divider_inset}.
184     */
185    public void setDividerInset(int inset) {
186        mDividerInset = inset;
187        updateDivider();
188    }
189
190    public int getDividerInset() {
191        return mDividerInset;
192    }
193
194    private void updateDivider() {
195        boolean shouldUpdate = true;
196        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
197            shouldUpdate = isLayoutDirectionResolved();
198        }
199        if (shouldUpdate) {
200            if (mDefaultDivider == null) {
201                mDefaultDivider = mDividerDecoration.getDivider();
202            }
203            mDivider = DrawableLayoutDirectionHelper.createRelativeInsetDrawable(mDefaultDivider,
204                    mDividerInset /* start */, 0 /* top */, 0 /* end */, 0 /* bottom */, this);
205            mDividerDecoration.setDivider(mDivider);
206        }
207    }
208
209    public Drawable getDivider() {
210        return mDivider;
211    }
212}
213