<commit_before>/*
 * Copyright 2017 <NAME>
 *
 * 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 me.piruin.spinney;

import android.app.Dialog;
import android.content.Context;
import android.content.DialogInterface;
import android.graphics.Canvas;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v7.app.AlertDialog;
import android.support.v7.widget.AppCompatEditText;
import android.util.AttributeSet;
import java.util.List;

/**
 * Replacement of vanilla Spinner with Super-power
 *
 * @param <T> Type of Selectable choice to use with Spinney
 */
public class Spinney<T> extends AppCompatEditText {

  static ItemPresenter defaultItemPresenter = new ItemPresenter() {
    @Override public String getLabelOf(Object item, int position) {
      return item.toString();
    }
  };
  private final CharSequence hint;
  /** Dialog object to show selectable item of Spinney can be Searchable or normal List Dialog */
  private Dialog dialog;
  /** OnItemSelectedListener set by Library user */
  private OnItemSelectedListener<T> itemSelectedListener;
  /** Internal OnItemSelectedListener use when filterBy() was called */
  private OnItemSelectedListener<T> _itemSelectedListener;
  private ItemPresenter itemPresenter = defaultItemPresenter;
  private SpinneyAdapter<T> adapter;
  private T selectedItem;

  public Spinney(Context context) { this(context, null); }

  public Spinney(Context context, AttributeSet attrs) {
    this(context, attrs, android.R.attr.editTextStyle);
  }

  public Spinney(Context context, AttributeSet attrs, int defStyleAttr) {
    super(context, attrs, defStyleAttr);

    /*
      Save hint at constructor because, after this getHint() will return null
      when use Spinney as child of Support's TextInputLayout.
     */
    hint = getHint();
  }

  /**
   * replace default global ItemPresenter this should be set at Application.onCreate()
   *
   * @param defaultItemDisplayer to present selected object on spinney view
   */
  public static void setDefaultItemPresenter(@NonNull ItemPresenter defaultItemDisplayer) {
    Spinney.defaultItemPresenter = defaultItemDisplayer;
  }

  /**
   * <pre>
   * Use this when number of items is more than user can scan by their eye.
   *
   * This method use inpurt list of item to create SpinneyAdapter if want to custom.
   * See setSearchableAdapter(SpinneyAdpter)
   * </pre>
   *
   * @param items list of item use
   */
  public void setSearchableItem(@NonNull final List<T> items) {
    setSearchableAdapter(new SpinneyAdapter<>(getContext(), items, itemPresenter));
  }

  /**
   * Call this when build-in SpinneyAdapter not enough for you requirement
   *
   * @param adapter spinneyAdapter to use with SearchableListDialog
   */
  public void setSearchableAdapter(@NonNull final SpinneyAdapter<T> adapter) {
    this.adapter = adapter;

    SearchableListDialog searchableListDialog = new SearchableListDialog(getContext());
    searchableListDialog.setAdapter(adapter);
    searchableListDialog.setHint(hint);
    searchableListDialog.setOnItemSelectedListener(
      new SearchableListDialog.OnItemSelectedListener<T>() {

        @Override
        public boolean onItemSelected(Object item, int position) {
          whenItemSelected((T) item, position);
          return true;
        }
      });
    dialog = searchableListDialog;
  }

  private void whenItemSelected(@Nullable T item, int selectedIndex) {
    this.selectedItem = item;
    if (item == null) {
      setText(null);
    } else {
      setText(itemPresenter.getLabelOf(item, selectedIndex));

      if (_itemSelectedListener != null)
        _itemSelectedListener.onItemSelected(Spinney.this, item, selectedIndex);
      if (itemSelectedListener != null)
        itemSelectedListener.onItemSelected(Spinney.this, item, selectedIndex);
    }
  }

  /**
   * Just set List of item on Dialog! Don't worry with Adapter Spinney will handler with it
   *
   * @param items list of item use
   */
  public void setItems(@NonNull final List<T> items) {
    adapter = new SpinneyAdapter<>(getContext(), items, itemPresenter);
    AlertDialog.Builder builder = new AlertDialog.Builder(getContext());
    builder.setTitle(getHint() != null ? getHint() : hint);
    builder.setAdapter(adapter,
      new DialogInterface.OnClickListener() {
        @Override public void onClick(DialogInterface dialogInterface, int selectedIndex) {
          T selectedItem = (T) adapter.getItem(selectedIndex);
          whenItemSelected(selectedItem, adapter.findPositionOf(selectedItem));
        }
      });
    dialog = builder.create();
  }

  public final <K> void filterBy(Spinney<K> parent, final Condition<T, K> filter) {
    parent._itemSelectedListener = new OnItemSelectedListener<K>() {

      @Override public void onItemSelected(Spinney view, K parentSelectedItem, int position) {
        adapter.updateCondition(parentSelectedItem, filter);
        T selectedItem = getSelectedItem();
        if (selectedItem != null && !adapter.isFilteredListContain(selectedItem)) {
          whenItemSelected(null, -1);
        }
      }
    };
  }

  /** @return selected item, this may be null */
  @Nullable public final T getSelectedItem() { return selectedItem; }

  @Override public final boolean performClick() {
    dialog.show();
    return true;
  }

  @Override protected final void onDraw(Canvas canvas) {
    super.onDraw(canvas);
    setFocusable(false);
    setClickable(true);
  }

  /**
   * Must call after adapter or item have already set
   *
   * @param item to set as selected item
   * @throws IllegalArgumentException if not found item in adapter of spinney
   */
  public final void setSelectedItem(T item) {<commit_msg>Add clearSelection()<commit_after>/*
 * Copyright 2017 <NAME>
 *
 * 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 me.piruin.spinney;

import android.app.Dialog;
import android.content.Context;
import android.content.DialogInterface;
import android.graphics.Canvas;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v7.app.AlertDialog;
import android.support.v7.widget.AppCompatEditText;
import android.util.AttributeSet;
import java.util.List;

/**
 * Replacement of vanilla Spinner with Super-power
 *
 * @param <T> Type of Selectable choice to use with Spinney
 */
public class Spinney<T> extends AppCompatEditText {

  static ItemPresenter defaultItemPresenter = new ItemPresenter() {
    @Override public String getLabelOf(Object item, int position) {
      return item.toString();
    }
  };
  private final CharSequence hint;
  /** Dialog object to show selectable item of Spinney can be Searchable or normal List Dialog */
  private Dialog dialog;
  /** OnItemSelectedListener set by Library user */
  private OnItemSelectedListener<T> itemSelectedListener;
  /** Internal OnItemSelectedListener use when filterBy() was called */
  private OnItemSelectedListener<T> _itemSelectedListener;
  private ItemPresenter itemPresenter = defaultItemPresenter;
  private SpinneyAdapter<T> adapter;
  private T selectedItem;

  public Spinney(Context context) { this(context, null); }

  public Spinney(Context context, AttributeSet attrs) {
    this(context, attrs, android.R.attr.editTextStyle);
  }

  public Spinney(Context context, AttributeSet attrs, int defStyleAttr) {
    super(context, attrs, defStyleAttr);

    /*
      Save hint at constructor because, after this getHint() will return null
      when use Spinney as child of Support's TextInputLayout.
     */
    hint = getHint();
  }

  /**
   * replace default global ItemPresenter this should be set at Application.onCreate()
   *
   * @param defaultItemDisplayer to present selected object on spinney view
   */
  public static void setDefaultItemPresenter(@NonNull ItemPresenter defaultItemDisplayer) {
    Spinney.defaultItemPresenter = defaultItemDisplayer;
  }

  /**
   * <pre>
   * Use this when number of items is more than user can scan by their eye.
   *
   * This method use inpurt list of item to create SpinneyAdapter if want to custom.
   * See setSearchableAdapter(SpinneyAdpter)
   * </pre>
   *
   * @param items list of item use
   */
  public void setSearchableItem(@NonNull final List<T> items) {
    setSearchableAdapter(new SpinneyAdapter<>(getContext(), items, itemPresenter));
  }

  /**
   * Call this when build-in SpinneyAdapter not enough for you requirement
   *
   * @param adapter spinneyAdapter to use with SearchableListDialog
   */
  public void setSearchableAdapter(@NonNull final SpinneyAdapter<T> adapter) {
    this.adapter = adapter;

    SearchableListDialog searchableListDialog = new SearchableListDialog(getContext());
    searchableListDialog.setAdapter(adapter);
    searchableListDialog.setHint(hint);
    searchableListDialog.setOnItemSelectedListener(
      new SearchableListDialog.OnItemSelectedListener<T>() {

        @Override
        public boolean onItemSelected(Object item, int position) {
          whenItemSelected((T) item, position);
          return true;
        }
      });
    dialog = searchableListDialog;
  }

  private void whenItemSelected(@Nullable T item, int selectedIndex) {
    this.selectedItem = item;
    if (item == null) {
      setText(null);
    } else {
      setText(itemPresenter.getLabelOf(item, selectedIndex));

      if (_itemSelectedListener != null)
        _itemSelectedListener.onItemSelected(Spinney.this, item, selectedIndex);
      if (itemSelectedListener != null)
        itemSelectedListener.onItemSelected(Spinney.this, item, selectedIndex);
    }
  }

  /**
   * Just set List of item on Dialog! Don't worry with Adapter Spinney will handler with it
   *
   * @param items list of item use
   */
  public void setItems(@NonNull final List<T> items) {
    adapter = new SpinneyAdapter<>(getContext(), items, itemPresenter);
    AlertDialog.Builder builder = new AlertDialog.Builder(getContext());
    builder.setTitle(getHint() != null ? getHint() : hint);
    builder.setAdapter(adapter,
      new DialogInterface.OnClickListener() {
        @Override public void onClick(DialogInterface dialogInterface, int selectedIndex) {
          T selectedItem = (T) adapter.getItem(selectedIndex);
          whenItemSelected(selectedItem, adapter.findPositionOf(selectedItem));
        }
      });
    dialog = builder.create();
  }

  public final <K> void filterBy(Spinney<K> parent, final Condition<T, K> filter) {
    parent._itemSelectedListener = new OnItemSelectedListener<K>() {

      @Override public void onItemSelected(Spinney view, K parentSelectedItem, int position) {
        adapter.updateCondition(parentSelectedItem, filter);
        T selectedItem = getSelectedItem();
        if (selectedItem != null && !adapter.isFilteredListContain(selectedItem)) {
          whenItemSelected(null, -1);
        }
      }
    };
  }

  /** @return selected item, this may be null */
  @Nullable public final T getSelectedItem() { return selectedItem; }

  @Override public final boolean performClick() {
    dialog.show();
    return true;
  }

  @Override protected final void onDraw(Canvas canvas) {
    super.onDraw(canvas);
    setFocusable(false);
    setClickable(true);
  }

  public void clearSelection() {
    setText(null);
    selectedItem = null;
  }

  /**
   * Must call after adapter or item have already set
   *
   * @param item to set as selected item
   * @throws IllegalArgumentException if not found item in adapter of spinney
   */
  public final void setSelectedItem(T item) {
{'fraction_non_alphanumeric': 0.08851313804874177, 'fraction_numerical': 0.0017464475668810034, 'mean_word_length': 3.743222891566265, 'pattern_counts': {'":': 0, '<': 39, '<?xml version=': 0, '>': 39, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4934766', 'n_tokens_mistral': 3434, 'n_tokens_neox': 3357, 'n_words': 1085}