/******************************************************************************* * Copyright 2012-present Pixate, Inc. * * 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.pixate.freestyle.styling.virtualAdapters; import java.lang.reflect.Field; import java.util.ArrayList; import java.util.List; import java.util.Map; import android.annotation.TargetApi; import android.graphics.drawable.Drawable; import android.os.Build; import android.widget.CheckedTextView; import com.pixate.freestyle.styling.PXRuleSet; import com.pixate.freestyle.styling.adapters.PXStyleAdapter; import com.pixate.freestyle.styling.cache.PXStyleInfo; import com.pixate.freestyle.styling.stylers.PXStylerContext; import com.pixate.freestyle.util.MapUtil; import com.pixate.freestyle.util.PXDrawableUtil; import com.pixate.freestyle.util.PXLog; /** * A virtual adapter for styling the {@link CheckedTextView} check mark * drawable. For example: * * <pre> * #myListView checked-text-view icon:checked { * background-image: url(check-on.svg); * background-size: 42; * } * * #myListView checked-text-view icon { * background-image: url(check-off.svg); * background-size: 42; * } * </pre> * * @author Bill Dawson */ public class PXVirtualCheckedTextViewIconAdapter extends PXVirtualChildAdapter { private static PXVirtualCheckedTextViewIconAdapter instance; private static String ELEMENT_NAME = "icon"; private static String TAG = PXVirtualCheckedTextViewIconAdapter.class.getSimpleName(); // For pre-JB reflection private static String DRAWABLE_FIELD_NAME = "mCheckMarkDrawable"; private static Field drawableField; /** * Returns a singleton instance of this class. * * @return An instance of {@link PXVirtualCheckedTextViewIconAdapter} */ public static synchronized PXVirtualCheckedTextViewIconAdapter getInstance() { if (instance == null) { instance = new PXVirtualCheckedTextViewIconAdapter(); } return instance; } protected PXVirtualCheckedTextViewIconAdapter() { } /* * (non-Javadoc) * @see * com.pixate.freestyle.styling.adapters.PXStyleAdapter#getSupportedPseudoClasses * (java.lang.Object) */ @Override public List<String> getSupportedPseudoClasses(Object styleable) { return new ArrayList<String>(PXDrawableUtil.getSupportedStates().keySet()); } @Override public String getDefaultPseudoClass(Object styleable) { return PXStyleInfo.DEFAULT_STYLE; } /* * (non-Javadoc) * @see * com.pixate.freestyle.styling.adapters.PXStyleAdapter#getElementName(java * .lang.Object) */ @Override public String getElementName(Object object) { return ELEMENT_NAME; } /* * (non-Javadoc) * @see * com.pixate.freestyle.styling.adapters.PXStyleAdapter#updateStyle(java. * util.List, java.util.List) */ @Override public boolean updateStyle(List<PXRuleSet> ruleSets, List<PXStylerContext> contexts) { if (!super.updateStyle(ruleSets, contexts)) { return false; } // Style the check mark. We will construct the // drawable from the context states, and then set the constructed // drawable as the check mark using the // CheckedTextView#setCheckMarkDrawable call. Note that this is // different than the View#setBackground(Drawable) call that is handled // in the default View adapter. CheckedTextView view = (CheckedTextView) contexts.get(0).getStyleable(); Drawable currentDrawable = getCheckMarkDrawable(view); Map<int[], Drawable> existingStates = PXDrawableUtil.getExistingStates(currentDrawable); Drawable newDrawable = null; if (MapUtil.isEmpty(existingStates)) { // create a new StateListDrawable for the icon, // using the checked text view's adapter // as the source of possible drawable states. if (contexts.size() == 1) { newDrawable = contexts.get(0).getBackgroundImage(); } else { newDrawable = PXDrawableUtil.createNewStateListDrawable( PXStyleAdapter.getStyleAdapter(view), ruleSets, contexts); } } else { // create a drawable that will hold a merge of the existing states // and the new states. Use the checked text view's // adapter as the source of possible drawable states. newDrawable = PXDrawableUtil.createDrawable(PXStyleAdapter.getStyleAdapter(view), existingStates, ruleSets, contexts); } view.setCheckMarkDrawable(newDrawable); return true; } private Drawable getCheckMarkDrawable(CheckedTextView view) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { return getCheckMarkDrawableJB(view); } else { try { // Need reflection if (drawableField == null) { drawableField = CheckedTextView.class.getDeclaredField(DRAWABLE_FIELD_NAME); drawableField.setAccessible(true); } return (Drawable) drawableField.get(view); } catch (Exception e) { PXLog.e(TAG, e, "Unable to get check mark drawable using reflection"); return null; } } } @TargetApi(Build.VERSION_CODES.JELLY_BEAN) private Drawable getCheckMarkDrawableJB(CheckedTextView view) { return view.getCheckMarkDrawable(); } }