content: /*
   * 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;
    }
  }
  