Android Open Source - DominionPicker Multi Select Image Preference






From Project

Back to project page DominionPicker.

License

The source code is released under:

MIT License

If you think the Android project DominionPicker listed in this page is inappropriate, such as containing malicious code/tools or violating the copyright, please email info at java2s dot com, thanks.

Java Source Code

/* Copyright (c) 2014 Mark Christopher Lauman
 * /*from  w w w. j  a va2  s .c  om*/
 * 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 ca.marklauman.tools;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;

import ca.marklauman.dominionpicker.R;
import android.app.AlertDialog.Builder;
import android.content.Context;
import android.content.SharedPreferences;
import android.content.res.TypedArray;
import android.graphics.Color;
import android.preference.ListPreference;
import android.support.annotation.NonNull;
import android.text.TextUtils.TruncateAt;
import android.util.AttributeSet;
import android.view.View;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.ListView;
import android.widget.TextView;

/** An implementation of MultiSelectImagePreference
 *  for Android 2 with icons included beside
 *  the list items.
 *  @author Mark Lauman & Krzysztof Suszynski.
 *  This is a heavily modified version of
 *  Krzysztof Suszynski's MultiSelectListPreference:
 *  https://gist.github.com/cardil/4754571 . */
public class MultiSelectImagePreference extends ListPreference {
  
  /** The view used by this preference for items in
   *  the popup list.                            */
  public static final int LIST_ITEM_VIEW = R.layout.list_item_check;
  
  /** Separator between list entries */
    private static final String SEPARATOR = "\u0001\u0007\u001D\u0007\u0001";
    /** Adapter used to track selections */
    private ArrayCheckAdapter<CharSequence> adapt;
    /** Icon resources used by the adapter */
    private int[] icons = null;
    /** If this preference is inverted, it saves the
     *  items that are NOT selected */
    private boolean inverted = false;

    
    public MultiSelectImagePreference(Context context, AttributeSet attributeSet) {
        super(context, attributeSet);
        
        // load inversion settings
        isInverted(attributeSet);
        
        // setup the adapter
        CharSequence[] entries = getEntries();
        CharSequence[] entryValues = getEntryValues();
        if (entries == null || entryValues == null
                || entries.length != entryValues.length) {
            throw new IllegalStateException(
                    "MultiSelectImagePreference requires an entries array and an entryValues "
                            + "array which are both the same length");
        }
        adapt = new ArrayCheckAdapter<>(getContext(),
                              LIST_ITEM_VIEW,
                              entries);
        adapt.setChoiceMode(ArrayCheckAdapter.CHOICE_MODE_MULTIPLE);
        adapt.setIcons(getEntryIcons(attributeSet));
        
        // setup the summary
        CharSequence[] values = getValues(getValue());
        ArrayList<String> listVals = new ArrayList<>(values.length);
        for(CharSequence val : values)
          listVals.add("" + val);
        setSummary(prepareSummary(listVals));
    }
    
    
    /** Load the icons used for the list entries
     *  into {@link #icons}. After this is called,
     *  {@link #getEntryIcons()} will return the same
     *  as this function - and run faster. Before it
     *  is called, {@link #getEntryIcons()} always
     *  returns {@code null}.
     *  @param attrs The attributes provided
     *  to this {@code MultiSelectImagePreference}.
     *  The icons are retrieved from this parameter.
     *  @return The resource ids of each icon in the
     *  list. These icons appear in the order of
     *  the entries themselves.                   */
    private int[] getEntryIcons(AttributeSet attrs) {
      icons = null;
      if(attrs == null) return null;
      int list_id = attrs.getAttributeResourceValue(null, "icons", -1);
      if(list_id < 0) return icons;
      
      TypedArray ta = getContext().getResources()
                    .obtainTypedArray(list_id);
      if(ta == null) return icons;
      icons = new int[ta.length()];
      for(int i=0; i<ta.length(); i++) {
        icons[i] = ta.getResourceId(i, -1);
      }
      ta.recycle();
      return icons;
    }
    
    
    /** Get the icons used for the list entries.
     *  @return The resource ids of each icon in the
     *  list. These icons appear in the order of
     *  the entries themselves.                   */
    public int[] getEntryIcons() {
      return icons;
    }
    
    /** <p>Check if this preference is inverted in its
     *  xml description. Inverted preferences save
     *  items that are NOT selected.</p>
     *  <p>After this is called once
     *  {@link #isInverted()} returns the same thing
     *  and is faster. Before this is called,
     *  {@link #isInverted()} returns {@code null}.</p>
     *  @param attrs The attributes provided
     *  to this {@code MultiSelectImagePreference}.
     *  The setting is retrieved from this parameter.
     *  @return {@code true} if this is inverted */
    private boolean isInverted(AttributeSet attrs) {
      inverted = attrs.getAttributeBooleanValue(null, "inverted", false);
      return inverted;
    }
    
    /** Check if this preference is inverted.
     *  Inverted preferences save items that are
     *  NOT selected.
     *  @return {@code true} if this is inverted */
    public boolean isInverted() {
      return inverted;
    }
    
    
    @Override
    protected void onPrepareDialogBuilder(@NonNull Builder builder) {
        restoreCheckedEntries();
        
        ListView list = new ListView(getContext());
        list.setBackgroundColor(Color.WHITE);
        list.setAdapter(adapt);
        OnItemClickListener listener = new OnItemClickListener() {
      public void onItemClick (AdapterView<?> parent, View view, int position, long id) {
        adapt.toggleItem(position);
      }
        };
        list.setOnItemClickListener(listener);
        builder.setView(list);
    }
    

    private void restoreCheckedEntries() {
        // get preference state
        CharSequence[] saved = getValues(getValue());
        
        if (saved == null || saved.length == 0) {
          if(inverted) adapt.selectAll();
          else adapt.deselectAll();
          return;
        }
        
        List<CharSequence> savedList = Arrays.asList(saved);
        CharSequence[] values = getEntryValues();
        ArrayList<Integer> selections = new ArrayList<>(savedList.size());
        if(inverted) {
          for (int i = 0; i < values.length; i++) {
              if(!savedList.contains(values[i]))
                  selections.add(i);
            }
        } else {
          for (int i = 0; i < values.length; i++) {
              if(savedList.contains(values[i]))
                  selections.add(i);
            }
        }
        adapt.setSelections(selections);
    }

    @Override
    protected void onDialogClosed(boolean positiveResult) {
      if(!positiveResult) return;
      
      CharSequence[] entryValues = getEntryValues();
      Integer[] select;
      if(inverted) select = adapt.getDeselections();
      else select = adapt.getSelections();
        List<String> values = new ArrayList<>();
        if (select != null) {
          for(int id : select)
            values.add("" + entryValues[id]);
            String value = join(values, SEPARATOR);
            setSummary(prepareSummary(values));
            setValueAndEvent(value);
        }
    }
    
    
    private void setValueAndEvent(String value) {
        if (callChangeListener(getValues(value))) {
            setValue(value);
        }
    }
    
    private CharSequence prepareSummary(List<String> joined) {
        List<String> titles = new ArrayList<>();
        CharSequence[] entryTitle = getEntries();
        CharSequence[] entryValues = getEntryValues();
        int ix = 0;
        if(inverted) {
          for (CharSequence value : entryValues) {
                if (!joined.contains(value + "")) {
                    titles.add((String) entryTitle[ix]);
                }
                ix += 1;
            }
        } else {
          for (CharSequence value : entryValues) {
                if (joined.contains(value + "")) {
                    titles.add((String) entryTitle[ix]);
                }
                ix += 1;
            }
        }
        return join(titles, ", ");
    }

    @Override
    protected Object onGetDefaultValue(@NonNull TypedArray typedArray, int index) {
        return typedArray.getTextArray(index);
    }

    @Override
    protected void onSetInitialValue(boolean restoreValue,
            Object rawDefaultValue) {
        String value;
        CharSequence[] defaultValue;
        if (rawDefaultValue == null) {
            defaultValue = new CharSequence[0];
        } else {
            defaultValue = (CharSequence[]) rawDefaultValue;
        }
        List<CharSequence> joined = Arrays.asList(defaultValue);
        String joinedDefaultValue = join(joined, SEPARATOR);
        if (restoreValue) {
            value = getPersistedString(joinedDefaultValue);
        } else {
            value = joinedDefaultValue;
        }

        setSummary(prepareSummary(Arrays.asList(getValues(value))));
        setValueAndEvent(value);
    }

    /** Joins array of object to single string by separator
     *  Credits to kurellajunior on this post
     *  http://snippets.dzone.com/posts/show/91
     *   @param iterable any kind of iterable
     *   ex.: <code>["a", "b", "c"]</code>
     *  @param separator separetes entries
     *  ex.: <code>","</code>
     *  @return joined string
     *  ex.: <code>"a,b,c"</code>                  */
    protected static String join(Iterable<?> iterable, String separator) {
        Iterator<?> oIter;
        if (iterable == null || (!(oIter = iterable.iterator()).hasNext()))
            return "";
        StringBuilder oBuilder = new StringBuilder(String.valueOf(oIter.next()));
        while (oIter.hasNext())
            oBuilder.append(separator).append(oIter.next());
        return oBuilder.toString();
    }
    
    @Override
    protected void onBindView(@NonNull View view) {
      super.onBindView(view);
      TextView summary = (TextView) view.findViewById(android.R.id.summary);
      if(summary != null) {
        summary.setEllipsize(TruncateAt.END);
        summary.setLines(2);
      }
    }
    
    
    /** Save a value to memory using the
     *  {@code MultiSelectImagePreference} format.
     *  @param prefs The preferences to save the
     *  values into.
     *  @param key The key associated with this
     *  preference value.
     *  @param values The values you wish placed
     *  there.                                */
    public static void saveValue(SharedPreferences prefs, String key, Collection<? extends String> values) {
      prefs.edit()
         .putString(key, join(values, SEPARATOR))
         .commit();
    }
    
    /** Extract the values stored by a
     *  {@code MultiSelectImagePreference}.
     *  (Retrieve the values as a String and pass
     *  them to this)
     *  @param val The value stored in the preferences
     *  @return The values stored inside that.      */
    public static String[] getValues(CharSequence val) {
        if (val == null || "".equals(val)) {
            return new String[0];
        } else {
            return ((String) val).split(SEPARATOR);
        }
    }
}




Java Source Code List

ca.marklauman.dominionpicker.CardAdapter.java
ca.marklauman.dominionpicker.CardList.java
ca.marklauman.dominionpicker.MainActivity.java
ca.marklauman.dominionpicker.MarketActivity.java
ca.marklauman.dominionpicker.SupplyActivity.java
ca.marklauman.dominionpicker.SupplyShuffler.java
ca.marklauman.dominionpicker.Supply.java
ca.marklauman.dominionpicker.settings.SettingsActivity.java
ca.marklauman.dominionpicker.settings.SettingsFragment.java
ca.marklauman.tools.ArrayCheckAdapter.java
ca.marklauman.tools.CursorSelAdapter.java
ca.marklauman.tools.MultiSelectImagePreference.java