Home | History | Annotate | Download | only in template
      1 /*
      2  * Copyright (C) 2017 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 
     17 package com.android.setupwizardlib.template;
     18 
     19 import android.content.Context;
     20 import android.content.res.TypedArray;
     21 import android.graphics.drawable.Drawable;
     22 import android.os.Build;
     23 import android.os.Build.VERSION_CODES;
     24 import android.support.annotation.AttrRes;
     25 import android.support.annotation.NonNull;
     26 import android.support.annotation.Nullable;
     27 import android.util.AttributeSet;
     28 import android.view.View;
     29 import android.widget.HeaderViewListAdapter;
     30 import android.widget.ListAdapter;
     31 import android.widget.ListView;
     32 
     33 import com.android.setupwizardlib.R;
     34 import com.android.setupwizardlib.TemplateLayout;
     35 import com.android.setupwizardlib.items.ItemAdapter;
     36 import com.android.setupwizardlib.items.ItemGroup;
     37 import com.android.setupwizardlib.items.ItemInflater;
     38 import com.android.setupwizardlib.util.DrawableLayoutDirectionHelper;
     39 
     40 /**
     41  * A {@link Mixin} for interacting with ListViews.
     42  */
     43 public class ListMixin implements Mixin {
     44 
     45     private TemplateLayout mTemplateLayout;
     46 
     47     @Nullable
     48     private ListView mListView;
     49 
     50     private Drawable mDivider;
     51     private Drawable mDefaultDivider;
     52 
     53     private int mDividerInsetStart;
     54     private int mDividerInsetEnd;
     55 
     56     /**
     57      * @param layout The layout this mixin belongs to.
     58      */
     59     public ListMixin(@NonNull TemplateLayout layout, @Nullable AttributeSet attrs,
     60             @AttrRes int defStyleAttr) {
     61         mTemplateLayout = layout;
     62 
     63         final Context context = layout.getContext();
     64         final TypedArray a = context.obtainStyledAttributes(
     65                 attrs, R.styleable.SuwListMixin, defStyleAttr, 0);
     66 
     67         final int entries = a.getResourceId(R.styleable.SuwListMixin_android_entries, 0);
     68         if (entries != 0) {
     69             final ItemGroup inflated =
     70                     (ItemGroup) new ItemInflater(context).inflate(entries);
     71             setAdapter(new ItemAdapter(inflated));
     72         }
     73         int dividerInset =
     74                 a.getDimensionPixelSize(R.styleable.SuwListMixin_suwDividerInset, -1);
     75         if (dividerInset != -1) {
     76             setDividerInset(dividerInset);
     77         } else {
     78             int dividerInsetStart =
     79                     a.getDimensionPixelSize(R.styleable.SuwListMixin_suwDividerInsetStart, 0);
     80             int dividerInsetEnd =
     81                     a.getDimensionPixelSize(R.styleable.SuwListMixin_suwDividerInsetEnd, 0);
     82             setDividerInsets(dividerInsetStart, dividerInsetEnd);
     83         }
     84         a.recycle();
     85     }
     86 
     87     /**
     88      * @return The list view contained in the layout, as marked by {@code @android:id/list}. This
     89      *         will return {@code null} if the list doesn't exist in the layout.
     90      */
     91     public ListView getListView() {
     92         return getListViewInternal();
     93     }
     94 
     95     // Client code can assume getListView() will not be null if they know their template contains
     96     // the list, but this mixin cannot. Any usages of getListView in this mixin needs null checks.
     97     @Nullable
     98     private ListView getListViewInternal() {
     99         if (mListView == null) {
    100             final View list = mTemplateLayout.findManagedViewById(android.R.id.list);
    101             if (list instanceof ListView) {
    102                 mListView = (ListView) list;
    103             }
    104         }
    105         return mListView;
    106     }
    107 
    108     /**
    109      * List mixin needs to update the dividers if the layout direction has changed. This method
    110      * should be called when {@link View#onLayout(boolean, int, int, int, int)} of the template
    111      * is called.
    112      */
    113     public void onLayout() {
    114         if (mDivider == null) {
    115             // Update divider in case layout direction has just been resolved
    116             updateDivider();
    117         }
    118     }
    119 
    120     /**
    121      * Gets the adapter of the list view in this layout. If the adapter is a HeaderViewListAdapter,
    122      * this method will unwrap it and return the underlying adapter.
    123      *
    124      * @return The adapter, or {@code null} if there is no list, or if the list has no adapter.
    125      */
    126     public ListAdapter getAdapter() {
    127         final ListView listView = getListViewInternal();
    128         if (listView != null) {
    129             final ListAdapter adapter = listView.getAdapter();
    130             if (adapter instanceof HeaderViewListAdapter) {
    131                 return ((HeaderViewListAdapter) adapter).getWrappedAdapter();
    132             }
    133             return adapter;
    134         }
    135         return null;
    136     }
    137 
    138     /**
    139      * Sets the adapter on the list view in this layout.
    140      */
    141     public void setAdapter(ListAdapter adapter) {
    142         final ListView listView = getListViewInternal();
    143         if (listView != null) {
    144             listView.setAdapter(adapter);
    145         }
    146     }
    147 
    148     /**
    149      * @deprecated Use {@link #setDividerInsets(int, int)} instead.
    150      */
    151     @Deprecated
    152     public void setDividerInset(int inset) {
    153         setDividerInsets(inset, 0);
    154     }
    155 
    156     /**
    157      * Sets the start inset of the divider. This will use the default divider drawable set in the
    158      * theme and apply insets to it.
    159      *
    160      * @param start The number of pixels to inset on the "start" side of the list divider. Typically
    161      *              this will be either {@code @dimen/suw_items_glif_icon_divider_inset} or
    162      *              {@code @dimen/suw_items_glif_text_divider_inset}.
    163      * @param end The number of pixels to inset on the "end" side of the list divider.
    164      */
    165     public void setDividerInsets(int start, int end) {
    166         mDividerInsetStart = start;
    167         mDividerInsetEnd = end;
    168         updateDivider();
    169     }
    170 
    171     /**
    172      * @return The number of pixels inset on the start side of the divider.
    173      * @deprecated This is the same as {@link #getDividerInsetStart()}. Use that instead.
    174      */
    175     @Deprecated
    176     public int getDividerInset() {
    177         return getDividerInsetStart();
    178     }
    179 
    180     /**
    181      * @return The number of pixels inset on the start side of the divider.
    182      */
    183     public int getDividerInsetStart() {
    184         return mDividerInsetStart;
    185     }
    186 
    187     /**
    188      * @return The number of pixels inset on the end side of the divider.
    189      */
    190     public int getDividerInsetEnd() {
    191         return mDividerInsetEnd;
    192     }
    193 
    194     private void updateDivider() {
    195         final ListView listView = getListViewInternal();
    196         if (listView == null) {
    197             return;
    198         }
    199         boolean shouldUpdate = true;
    200         if (Build.VERSION.SDK_INT >= VERSION_CODES.KITKAT) {
    201             shouldUpdate = mTemplateLayout.isLayoutDirectionResolved();
    202         }
    203         if (shouldUpdate) {
    204             if (mDefaultDivider == null) {
    205                 mDefaultDivider = listView.getDivider();
    206             }
    207             mDivider = DrawableLayoutDirectionHelper.createRelativeInsetDrawable(
    208                     mDefaultDivider,
    209                     mDividerInsetStart /* start */,
    210                     0 /* top */,
    211                     mDividerInsetEnd /* end */,
    212                     0 /* bottom */,
    213                     mTemplateLayout);
    214             listView.setDivider(mDivider);
    215         }
    216     }
    217 
    218     /**
    219      * @return The drawable used as the divider.
    220      */
    221     public Drawable getDivider() {
    222         return mDivider;
    223     }
    224 }
    225