/* * Copyright (C) 2017 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.setupwizardlib.template; import android.content.Context; import android.content.res.TypedArray; import android.graphics.drawable.Drawable; import android.os.Build; import android.os.Build.VERSION_CODES; import androidx.annotation.AttrRes; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import android.util.AttributeSet; import android.view.View; import android.widget.HeaderViewListAdapter; import android.widget.ListAdapter; import android.widget.ListView; import com.android.setupwizardlib.R; import com.android.setupwizardlib.TemplateLayout; import com.android.setupwizardlib.items.ItemAdapter; import com.android.setupwizardlib.items.ItemGroup; import com.android.setupwizardlib.items.ItemInflater; import com.android.setupwizardlib.util.DrawableLayoutDirectionHelper; /** A {@link Mixin} for interacting with ListViews. */ public class ListMixin implements Mixin { private final TemplateLayout templateLayout; @Nullable private ListView listView; private Drawable divider; private Drawable defaultDivider; private int dividerInsetStart; private int dividerInsetEnd; /** @param layout The layout this mixin belongs to. */ public ListMixin( @NonNull TemplateLayout layout, @Nullable AttributeSet attrs, @AttrRes int defStyleAttr) { templateLayout = layout; final Context context = layout.getContext(); final TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.SuwListMixin, defStyleAttr, 0); final int entries = a.getResourceId(R.styleable.SuwListMixin_android_entries, 0); if (entries != 0) { final ItemGroup inflated = (ItemGroup) new ItemInflater(context).inflate(entries); setAdapter(new ItemAdapter(inflated)); } int dividerInset = a.getDimensionPixelSize(R.styleable.SuwListMixin_suwDividerInset, -1); if (dividerInset != -1) { setDividerInset(dividerInset); } else { int dividerInsetStart = a.getDimensionPixelSize(R.styleable.SuwListMixin_suwDividerInsetStart, 0); int dividerInsetEnd = a.getDimensionPixelSize(R.styleable.SuwListMixin_suwDividerInsetEnd, 0); setDividerInsets(dividerInsetStart, dividerInsetEnd); } a.recycle(); } /** * @return The list view contained in the layout, as marked by {@code @android:id/list}. This will * return {@code null} if the list doesn't exist in the layout. */ public ListView getListView() { return getListViewInternal(); } // Client code can assume getListView() will not be null if they know their template contains // the list, but this mixin cannot. Any usages of getListView in this mixin needs null checks. @Nullable private ListView getListViewInternal() { if (listView == null) { final View list = templateLayout.findManagedViewById(android.R.id.list); if (list instanceof ListView) { listView = (ListView) list; } } return listView; } /** * List mixin needs to update the dividers if the layout direction has changed. This method should * be called when {@link View#onLayout(boolean, int, int, int, int)} of the template is called. */ public void onLayout() { if (divider == null) { // Update divider in case layout direction has just been resolved updateDivider(); } } /** * Gets the adapter of the list view in this layout. If the adapter is a HeaderViewListAdapter, * this method will unwrap it and return the underlying adapter. * * @return The adapter, or {@code null} if there is no list, or if the list has no adapter. */ public ListAdapter getAdapter() { final ListView listView = getListViewInternal(); if (listView != null) { final ListAdapter adapter = listView.getAdapter(); if (adapter instanceof HeaderViewListAdapter) { return ((HeaderViewListAdapter) adapter).getWrappedAdapter(); } return adapter; } return null; } /** Sets the adapter on the list view in this layout. */ public void setAdapter(ListAdapter adapter) { final ListView listView = getListViewInternal(); if (listView != null) { listView.setAdapter(adapter); } } /** @deprecated Use {@link #setDividerInsets(int, int)} instead. */ @Deprecated public void setDividerInset(int inset) { setDividerInsets(inset, 0); } /** * Sets the start inset of the divider. This will use the default divider drawable set in the * theme and apply insets to it. * * @param start The number of pixels to inset on the "start" side of the list divider. Typically * this will be either {@code @dimen/suw_items_glif_icon_divider_inset} or * {@code @dimen/suw_items_glif_text_divider_inset}. * @param end The number of pixels to inset on the "end" side of the list divider. */ public void setDividerInsets(int start, int end) { dividerInsetStart = start; dividerInsetEnd = end; updateDivider(); } /** * @return The number of pixels inset on the start side of the divider. * @deprecated This is the same as {@link #getDividerInsetStart()}. Use that instead. */ @Deprecated public int getDividerInset() { return getDividerInsetStart(); } /** @return The number of pixels inset on the start side of the divider. */ public int getDividerInsetStart() { return dividerInsetStart; } /** @return The number of pixels inset on the end side of the divider. */ public int getDividerInsetEnd() { return dividerInsetEnd; } private void updateDivider() { final ListView listView = getListViewInternal(); if (listView == null) { return; } boolean shouldUpdate = true; if (Build.VERSION.SDK_INT >= VERSION_CODES.KITKAT) { shouldUpdate = templateLayout.isLayoutDirectionResolved(); } if (shouldUpdate) { if (defaultDivider == null) { defaultDivider = listView.getDivider(); } divider = DrawableLayoutDirectionHelper.createRelativeInsetDrawable( defaultDivider, dividerInsetStart /* start */, 0 /* top */, dividerInsetEnd /* end */, 0 /* bottom */, templateLayout); listView.setDivider(divider); } } /** @return The drawable used as the divider. */ public Drawable getDivider() { return divider; } }