evaluator, V... values) {
+ // PropertyValuesHolder pvh = new PropertyValuesHolder(property);
+ // pvh.setObjectValues(values);
+ // pvh.setEvaluator(evaluator);
+ // return pvh;
+ //}
+
+ /**
+ * Constructs and returns a PropertyValuesHolder object with the specified property name and set
+ * of values. These values can be of any type, but the type should be consistent so that
+ * an appropriate {@link android.animation.TypeEvaluator} can be found that matches
+ * the common type.
+ * If there is only one value, it is assumed to be the end value of an animation,
+ * and an initial value will be derived, if possible, by calling a getter function
+ * on the object. Also, if any value is null, the value will be filled in when the animation
+ * starts in the same way. This mechanism of automatically getting null values only works
+ * if the PropertyValuesHolder object is used in conjunction
+ * {@link ObjectAnimator}, and with a getter function
+ * derived automatically from propertyName
, since otherwise PropertyValuesHolder has
+ * no way of determining what the value should be.
+ * @param propertyName The name of the property associated with this set of values. This
+ * can be the actual property name to be used when using a ObjectAnimator object, or
+ * just a name used to get animated values, such as if this object is used with an
+ * ValueAnimator object.
+ * @param values The set of values to animate between.
+ */
+ public static PropertyValuesHolder ofKeyframe(String propertyName, Keyframe... values) {
+ KeyframeSet keyframeSet = KeyframeSet.ofKeyframe(values);
+ if (keyframeSet instanceof IntKeyframeSet) {
+ return new IntPropertyValuesHolder(propertyName, (IntKeyframeSet) keyframeSet);
+ } else if (keyframeSet instanceof FloatKeyframeSet) {
+ return new FloatPropertyValuesHolder(propertyName, (FloatKeyframeSet) keyframeSet);
+ }
+ else {
+ PropertyValuesHolder pvh = new PropertyValuesHolder(propertyName);
+ pvh.mKeyframeSet = keyframeSet;
+ pvh.mValueType = values[0].getType();
+ return pvh;
+ }
+ }
+
+ /**
+ * Constructs and returns a PropertyValuesHolder object with the specified property and set
+ * of values. These values can be of any type, but the type should be consistent so that
+ * an appropriate {@link android.animation.TypeEvaluator} can be found that matches
+ * the common type.
+ *
If there is only one value, it is assumed to be the end value of an animation,
+ * and an initial value will be derived, if possible, by calling the property's
+ * {@link android.util.Property#get(Object)} function.
+ * Also, if any value is null, the value will be filled in when the animation
+ * starts in the same way. This mechanism of automatically getting null values only works
+ * if the PropertyValuesHolder object is used in conjunction with
+ * {@link ObjectAnimator}, since otherwise PropertyValuesHolder has
+ * no way of determining what the value should be.
+ * @param property The property associated with this set of values. Should not be null.
+ * @param values The set of values to animate between.
+ */
+ //public static PropertyValuesHolder ofKeyframe(Property property, Keyframe... values) {
+ // KeyframeSet keyframeSet = KeyframeSet.ofKeyframe(values);
+ // if (keyframeSet instanceof IntKeyframeSet) {
+ // return new IntPropertyValuesHolder(property, (IntKeyframeSet) keyframeSet);
+ // } else if (keyframeSet instanceof FloatKeyframeSet) {
+ // return new FloatPropertyValuesHolder(property, (FloatKeyframeSet) keyframeSet);
+ // }
+ // else {
+ // PropertyValuesHolder pvh = new PropertyValuesHolder(property);
+ // pvh.mKeyframeSet = keyframeSet;
+ // pvh.mValueType = ((Keyframe)values[0]).getType();
+ // return pvh;
+ // }
+ //}
+
+ /**
+ * Set the animated values for this object to this set of ints.
+ * If there is only one value, it is assumed to be the end value of an animation,
+ * and an initial value will be derived, if possible, by calling a getter function
+ * on the object. Also, if any value is null, the value will be filled in when the animation
+ * starts in the same way. This mechanism of automatically getting null values only works
+ * if the PropertyValuesHolder object is used in conjunction
+ * {@link ObjectAnimator}, and with a getter function
+ * derived automatically from propertyName
, since otherwise PropertyValuesHolder has
+ * no way of determining what the value should be.
+ *
+ * @param values One or more values that the animation will animate between.
+ */
+ public void setIntValues(int... values) {
+ mValueType = int.class;
+ mKeyframeSet = KeyframeSet.ofInt(values);
+ }
+
+ /**
+ * Set the animated values for this object to this set of floats.
+ * If there is only one value, it is assumed to be the end value of an animation,
+ * and an initial value will be derived, if possible, by calling a getter function
+ * on the object. Also, if any value is null, the value will be filled in when the animation
+ * starts in the same way. This mechanism of automatically getting null values only works
+ * if the PropertyValuesHolder object is used in conjunction
+ * {@link ObjectAnimator}, and with a getter function
+ * derived automatically from propertyName
, since otherwise PropertyValuesHolder has
+ * no way of determining what the value should be.
+ *
+ * @param values One or more values that the animation will animate between.
+ */
+ public void setFloatValues(float... values) {
+ mValueType = float.class;
+ mKeyframeSet = KeyframeSet.ofFloat(values);
+ }
+
+ /**
+ * Set the animated values for this object to this set of Keyframes.
+ *
+ * @param values One or more values that the animation will animate between.
+ */
+ public void setKeyframes(Keyframe... values) {
+ int numKeyframes = values.length;
+ Keyframe keyframes[] = new Keyframe[Math.max(numKeyframes,2)];
+ mValueType = values[0].getType();
+ for (int i = 0; i < numKeyframes; ++i) {
+ keyframes[i] = values[i];
+ }
+ mKeyframeSet = new KeyframeSet(keyframes);
+ }
+
+ /**
+ * Set the animated values for this object to this set of Objects.
+ * If there is only one value, it is assumed to be the end value of an animation,
+ * and an initial value will be derived, if possible, by calling a getter function
+ * on the object. Also, if any value is null, the value will be filled in when the animation
+ * starts in the same way. This mechanism of automatically getting null values only works
+ * if the PropertyValuesHolder object is used in conjunction
+ * {@link ObjectAnimator}, and with a getter function
+ * derived automatically from propertyName
, since otherwise PropertyValuesHolder has
+ * no way of determining what the value should be.
+ *
+ * @param values One or more values that the animation will animate between.
+ */
+ public void setObjectValues(Object... values) {
+ mValueType = values[0].getClass();
+ mKeyframeSet = KeyframeSet.ofObject(values);
+ }
+
+ /**
+ * Determine the setter or getter function using the JavaBeans convention of setFoo or
+ * getFoo for a property named 'foo'. This function figures out what the name of the
+ * function should be and uses reflection to find the Method with that name on the
+ * target object.
+ *
+ * @param targetClass The class to search for the method
+ * @param prefix "set" or "get", depending on whether we need a setter or getter.
+ * @param valueType The type of the parameter (in the case of a setter). This type
+ * is derived from the values set on this PropertyValuesHolder. This type is used as
+ * a first guess at the parameter type, but we check for methods with several different
+ * types to avoid problems with slight mis-matches between supplied values and actual
+ * value types used on the setter.
+ * @return Method the method associated with mPropertyName.
+ */
+ private Method getPropertyFunction(Class targetClass, String prefix, Class valueType) {
+ // TODO: faster implementation...
+ Method returnVal = null;
+ String methodName = getMethodName(prefix, mPropertyName);
+ Class args[] = null;
+ if (valueType == null) {
+ try {
+ returnVal = targetClass.getMethod(methodName, args);
+ } catch (NoSuchMethodException e) {
+ Log.e("PropertyValuesHolder", targetClass.getSimpleName() + " - " +
+ "Couldn't find no-arg method for property " + mPropertyName + ": " + e);
+ }
+ } else {
+ args = new Class[1];
+ Class typeVariants[];
+ if (mValueType.equals(Float.class)) {
+ typeVariants = FLOAT_VARIANTS;
+ } else if (mValueType.equals(Integer.class)) {
+ typeVariants = INTEGER_VARIANTS;
+ } else if (mValueType.equals(Double.class)) {
+ typeVariants = DOUBLE_VARIANTS;
+ } else {
+ typeVariants = new Class[1];
+ typeVariants[0] = mValueType;
+ }
+ for (Class typeVariant : typeVariants) {
+ args[0] = typeVariant;
+ try {
+ returnVal = targetClass.getMethod(methodName, args);
+ // change the value type to suit
+ mValueType = typeVariant;
+ return returnVal;
+ } catch (NoSuchMethodException e) {
+ // Swallow the error and keep trying other variants
+ }
+ }
+ // If we got here, then no appropriate function was found
+ Log.e("PropertyValuesHolder",
+ "Couldn't find " + prefix + "ter property " + mPropertyName +
+ " for " + targetClass.getSimpleName() +
+ " with value type "+ mValueType);
+ }
+
+ return returnVal;
+ }
+
+
+ /**
+ * Returns the setter or getter requested. This utility function checks whether the
+ * requested method exists in the propertyMapMap cache. If not, it calls another
+ * utility function to request the Method from the targetClass directly.
+ * @param targetClass The Class on which the requested method should exist.
+ * @param propertyMapMap The cache of setters/getters derived so far.
+ * @param prefix "set" or "get", for the setter or getter.
+ * @param valueType The type of parameter passed into the method (null for getter).
+ * @return Method the method associated with mPropertyName.
+ */
+ private Method setupSetterOrGetter(Class targetClass,
+ HashMap> propertyMapMap,
+ String prefix, Class valueType) {
+ Method setterOrGetter = null;
+ try {
+ // Have to lock property map prior to reading it, to guard against
+ // another thread putting something in there after we've checked it
+ // but before we've added an entry to it
+ mPropertyMapLock.writeLock().lock();
+ HashMap propertyMap = propertyMapMap.get(targetClass);
+ if (propertyMap != null) {
+ setterOrGetter = propertyMap.get(mPropertyName);
+ }
+ if (setterOrGetter == null) {
+ setterOrGetter = getPropertyFunction(targetClass, prefix, valueType);
+ if (propertyMap == null) {
+ propertyMap = new HashMap();
+ propertyMapMap.put(targetClass, propertyMap);
+ }
+ propertyMap.put(mPropertyName, setterOrGetter);
+ }
+ } finally {
+ mPropertyMapLock.writeLock().unlock();
+ }
+ return setterOrGetter;
+ }
+
+ /**
+ * Utility function to get the setter from targetClass
+ * @param targetClass The Class on which the requested method should exist.
+ */
+ void setupSetter(Class targetClass) {
+ mSetter = setupSetterOrGetter(targetClass, sSetterPropertyMap, "set", mValueType);
+ }
+
+ /**
+ * Utility function to get the getter from targetClass
+ */
+ private void setupGetter(Class targetClass) {
+ mGetter = setupSetterOrGetter(targetClass, sGetterPropertyMap, "get", null);
+ }
+
+ /**
+ * Internal function (called from ObjectAnimator) to set up the setter and getter
+ * prior to running the animation. If the setter has not been manually set for this
+ * object, it will be derived automatically given the property name, target object, and
+ * types of values supplied. If no getter has been set, it will be supplied iff any of the
+ * supplied values was null. If there is a null value, then the getter (supplied or derived)
+ * will be called to set those null values to the current value of the property
+ * on the target object.
+ * @param target The object on which the setter (and possibly getter) exist.
+ */
+ void setupSetterAndGetter(Object target) {
+ //if (mProperty != null) {
+ // // check to make sure that mProperty is on the class of target
+ // try {
+ // Object testValue = mProperty.get(target);
+ // for (Keyframe kf : mKeyframeSet.mKeyframes) {
+ // if (!kf.hasValue()) {
+ // kf.setValue(mProperty.get(target));
+ // }
+ // }
+ // return;
+ // } catch (ClassCastException e) {
+ // Log.e("PropertyValuesHolder","No such property (" + mProperty.getName() +
+ // ") on target object " + target + ". Trying reflection instead");
+ // mProperty = null;
+ // }
+ //}
+ Class targetClass = target.getClass();
+ if (mSetter == null) {
+ setupSetter(targetClass);
+ }
+ for (Keyframe kf : mKeyframeSet.mKeyframes) {
+ if (!kf.hasValue()) {
+ if (mGetter == null) {
+ setupGetter(targetClass);
+ }
+ try {
+ kf.setValue(mGetter.invoke(target));
+ } catch (InvocationTargetException e) {
+ Log.e("PropertyValuesHolder", e.toString());
+ } catch (IllegalAccessException e) {
+ Log.e("PropertyValuesHolder", e.toString());
+ }
+ }
+ }
+ }
+
+ /**
+ * Utility function to set the value stored in a particular Keyframe. The value used is
+ * whatever the value is for the property name specified in the keyframe on the target object.
+ *
+ * @param target The target object from which the current value should be extracted.
+ * @param kf The keyframe which holds the property name and value.
+ */
+ private void setupValue(Object target, Keyframe kf) {
+ //if (mProperty != null) {
+ // kf.setValue(mProperty.get(target));
+ //}
+ try {
+ if (mGetter == null) {
+ Class targetClass = target.getClass();
+ setupGetter(targetClass);
+ }
+ kf.setValue(mGetter.invoke(target));
+ } catch (InvocationTargetException e) {
+ Log.e("PropertyValuesHolder", e.toString());
+ } catch (IllegalAccessException e) {
+ Log.e("PropertyValuesHolder", e.toString());
+ }
+ }
+
+ /**
+ * This function is called by ObjectAnimator when setting the start values for an animation.
+ * The start values are set according to the current values in the target object. The
+ * property whose value is extracted is whatever is specified by the propertyName of this
+ * PropertyValuesHolder object.
+ *
+ * @param target The object which holds the start values that should be set.
+ */
+ void setupStartValue(Object target) {
+ setupValue(target, mKeyframeSet.mKeyframes.get(0));
+ }
+
+ /**
+ * This function is called by ObjectAnimator when setting the end values for an animation.
+ * The end values are set according to the current values in the target object. The
+ * property whose value is extracted is whatever is specified by the propertyName of this
+ * PropertyValuesHolder object.
+ *
+ * @param target The object which holds the start values that should be set.
+ */
+ void setupEndValue(Object target) {
+ setupValue(target, mKeyframeSet.mKeyframes.get(mKeyframeSet.mKeyframes.size() - 1));
+ }
+
+ @Override
+ public PropertyValuesHolder clone() {
+ try {
+ PropertyValuesHolder newPVH = (PropertyValuesHolder) super.clone();
+ newPVH.mPropertyName = mPropertyName;
+ //newPVH.mProperty = mProperty;
+ newPVH.mKeyframeSet = mKeyframeSet.clone();
+ newPVH.mEvaluator = mEvaluator;
+ return newPVH;
+ } catch (CloneNotSupportedException e) {
+ // won't reach here
+ return null;
+ }
+ }
+
+ /**
+ * Internal function to set the value on the target object, using the setter set up
+ * earlier on this PropertyValuesHolder object. This function is called by ObjectAnimator
+ * to handle turning the value calculated by ValueAnimator into a value set on the object
+ * according to the name of the property.
+ * @param target The target object on which the value is set
+ */
+ void setAnimatedValue(Object target) {
+ //if (mProperty != null) {
+ // mProperty.set(target, getAnimatedValue());
+ //}
+ if (mSetter != null) {
+ try {
+ mTmpValueArray[0] = getAnimatedValue();
+ mSetter.invoke(target, mTmpValueArray);
+ } catch (InvocationTargetException e) {
+ Log.e("PropertyValuesHolder", e.toString());
+ } catch (IllegalAccessException e) {
+ Log.e("PropertyValuesHolder", e.toString());
+ }
+ }
+ }
+
+ /**
+ * Internal function, called by ValueAnimator, to set up the TypeEvaluator that will be used
+ * to calculate animated values.
+ */
+ void init() {
+ if (mEvaluator == null) {
+ // We already handle int and float automatically, but not their Object
+ // equivalents
+ mEvaluator = (mValueType == Integer.class) ? sIntEvaluator :
+ (mValueType == Float.class) ? sFloatEvaluator :
+ null;
+ }
+ if (mEvaluator != null) {
+ // KeyframeSet knows how to evaluate the common types - only give it a custom
+ // evaluator if one has been set on this class
+ mKeyframeSet.setEvaluator(mEvaluator);
+ }
+ }
+
+ /**
+ * The TypeEvaluator will the automatically determined based on the type of values
+ * supplied to PropertyValuesHolder. The evaluator can be manually set, however, if so
+ * desired. This may be important in cases where either the type of the values supplied
+ * do not match the way that they should be interpolated between, or if the values
+ * are of a custom type or one not currently understood by the animation system. Currently,
+ * only values of type float and int (and their Object equivalents: Float
+ * and Integer) are correctly interpolated; all other types require setting a TypeEvaluator.
+ * @param evaluator
+ */
+ public void setEvaluator(TypeEvaluator evaluator) {
+ mEvaluator = evaluator;
+ mKeyframeSet.setEvaluator(evaluator);
+ }
+
+ /**
+ * Function used to calculate the value according to the evaluator set up for
+ * this PropertyValuesHolder object. This function is called by ValueAnimator.animateValue().
+ *
+ * @param fraction The elapsed, interpolated fraction of the animation.
+ */
+ void calculateValue(float fraction) {
+ mAnimatedValue = mKeyframeSet.getValue(fraction);
+ }
+
+ /**
+ * Sets the name of the property that will be animated. This name is used to derive
+ * a setter function that will be called to set animated values.
+ * For example, a property name of foo
will result
+ * in a call to the function setFoo()
on the target object. If either
+ * valueFrom
or valueTo
is null, then a getter function will
+ * also be derived and called.
+ *
+ * Note that the setter function derived from this property name
+ * must take the same parameter type as the
+ * valueFrom
and valueTo
properties, otherwise the call to
+ * the setter function will fail.
+ *
+ * @param propertyName The name of the property being animated.
+ */
+ public void setPropertyName(String propertyName) {
+ mPropertyName = propertyName;
+ }
+
+ /**
+ * Sets the property that will be animated.
+ *
+ * Note that if this PropertyValuesHolder object is used with ObjectAnimator, the property
+ * must exist on the target object specified in that ObjectAnimator.
+ *
+ * @param property The property being animated.
+ */
+ //public void setProperty(Property property) {
+ // mProperty = property;
+ //}
+
+ /**
+ * Gets the name of the property that will be animated. This name will be used to derive
+ * a setter function that will be called to set animated values.
+ * For example, a property name of foo
will result
+ * in a call to the function setFoo()
on the target object. If either
+ * valueFrom
or valueTo
is null, then a getter function will
+ * also be derived and called.
+ */
+ public String getPropertyName() {
+ return mPropertyName;
+ }
+
+ /**
+ * Internal function, called by ValueAnimator and ObjectAnimator, to retrieve the value
+ * most recently calculated in calculateValue().
+ * @return
+ */
+ Object getAnimatedValue() {
+ return mAnimatedValue;
+ }
+
+ @Override
+ public String toString() {
+ return mPropertyName + ": " + mKeyframeSet.toString();
+ }
+
+ /**
+ * Utility method to derive a setter/getter method name from a property name, where the
+ * prefix is typically "set" or "get" and the first letter of the property name is
+ * capitalized.
+ *
+ * @param prefix The precursor to the method name, before the property name begins, typically
+ * "set" or "get".
+ * @param propertyName The name of the property that represents the bulk of the method name
+ * after the prefix. The first letter of this word will be capitalized in the resulting
+ * method name.
+ * @return String the property name converted to a method name according to the conventions
+ * specified above.
+ */
+ static String getMethodName(String prefix, String propertyName) {
+ if (propertyName == null || propertyName.length() == 0) {
+ // shouldn't get here
+ return prefix;
+ }
+ char firstLetter = Character.toUpperCase(propertyName.charAt(0));
+ String theRest = propertyName.substring(1);
+ return prefix + firstLetter + theRest;
+ }
+
+ static class IntPropertyValuesHolder extends PropertyValuesHolder {
+
+ // Cache JNI functions to avoid looking them up twice
+ //private static final HashMap> sJNISetterPropertyMap =
+ // new HashMap>();
+ //int mJniSetter;
+ //private IntProperty mIntProperty;
+
+ IntKeyframeSet mIntKeyframeSet;
+ int mIntAnimatedValue;
+
+ public IntPropertyValuesHolder(String propertyName, IntKeyframeSet keyframeSet) {
+ super(propertyName);
+ mValueType = int.class;
+ mKeyframeSet = keyframeSet;
+ mIntKeyframeSet = (IntKeyframeSet) mKeyframeSet;
+ }
+
+ //public IntPropertyValuesHolder(Property property, IntKeyframeSet keyframeSet) {
+ // super(property);
+ // mValueType = int.class;
+ // mKeyframeSet = keyframeSet;
+ // mIntKeyframeSet = (IntKeyframeSet) mKeyframeSet;
+ // if (property instanceof IntProperty) {
+ // mIntProperty = (IntProperty) mProperty;
+ // }
+ //}
+
+ public IntPropertyValuesHolder(String propertyName, int... values) {
+ super(propertyName);
+ setIntValues(values);
+ }
+
+ //public IntPropertyValuesHolder(Property property, int... values) {
+ // super(property);
+ // setIntValues(values);
+ // if (property instanceof IntProperty) {
+ // mIntProperty = (IntProperty) mProperty;
+ // }
+ //}
+
+ @Override
+ public void setIntValues(int... values) {
+ super.setIntValues(values);
+ mIntKeyframeSet = (IntKeyframeSet) mKeyframeSet;
+ }
+
+ @Override
+ void calculateValue(float fraction) {
+ mIntAnimatedValue = mIntKeyframeSet.getIntValue(fraction);
+ }
+
+ @Override
+ Object getAnimatedValue() {
+ return mIntAnimatedValue;
+ }
+
+ @Override
+ public IntPropertyValuesHolder clone() {
+ IntPropertyValuesHolder newPVH = (IntPropertyValuesHolder) super.clone();
+ newPVH.mIntKeyframeSet = (IntKeyframeSet) newPVH.mKeyframeSet;
+ return newPVH;
+ }
+
+ /**
+ * Internal function to set the value on the target object, using the setter set up
+ * earlier on this PropertyValuesHolder object. This function is called by ObjectAnimator
+ * to handle turning the value calculated by ValueAnimator into a value set on the object
+ * according to the name of the property.
+ * @param target The target object on which the value is set
+ */
+ @Override
+ void setAnimatedValue(Object target) {
+ //if (mIntProperty != null) {
+ // mIntProperty.setValue(target, mIntAnimatedValue);
+ // return;
+ //}
+ //if (mProperty != null) {
+ // mProperty.set(target, mIntAnimatedValue);
+ // return;
+ //}
+ //if (mJniSetter != 0) {
+ // nCallIntMethod(target, mJniSetter, mIntAnimatedValue);
+ // return;
+ //}
+ if (mSetter != null) {
+ try {
+ mTmpValueArray[0] = mIntAnimatedValue;
+ mSetter.invoke(target, mTmpValueArray);
+ } catch (InvocationTargetException e) {
+ Log.e("PropertyValuesHolder", e.toString());
+ } catch (IllegalAccessException e) {
+ Log.e("PropertyValuesHolder", e.toString());
+ }
+ }
+ }
+
+ @Override
+ void setupSetter(Class targetClass) {
+ //if (mProperty != null) {
+ // return;
+ //}
+ // Check new static hashmap for setter method
+ //try {
+ // mPropertyMapLock.writeLock().lock();
+ // HashMap propertyMap = sJNISetterPropertyMap.get(targetClass);
+ // if (propertyMap != null) {
+ // Integer mJniSetterInteger = propertyMap.get(mPropertyName);
+ // if (mJniSetterInteger != null) {
+ // mJniSetter = mJniSetterInteger;
+ // }
+ // }
+ // if (mJniSetter == 0) {
+ // String methodName = getMethodName("set", mPropertyName);
+ // mJniSetter = nGetIntMethod(targetClass, methodName);
+ // if (mJniSetter != 0) {
+ // if (propertyMap == null) {
+ // propertyMap = new HashMap();
+ // sJNISetterPropertyMap.put(targetClass, propertyMap);
+ // }
+ // propertyMap.put(mPropertyName, mJniSetter);
+ // }
+ // }
+ //} catch (NoSuchMethodError e) {
+ // Log.d("PropertyValuesHolder",
+ // "Can't find native method using JNI, use reflection" + e);
+ //} finally {
+ // mPropertyMapLock.writeLock().unlock();
+ //}
+ //if (mJniSetter == 0) {
+ // Couldn't find method through fast JNI approach - just use reflection
+ super.setupSetter(targetClass);
+ //}
+ }
+ }
+
+ static class FloatPropertyValuesHolder extends PropertyValuesHolder {
+
+ // Cache JNI functions to avoid looking them up twice
+ //private static final HashMap> sJNISetterPropertyMap =
+ // new HashMap>();
+ //int mJniSetter;
+ //private FloatProperty mFloatProperty;
+
+ FloatKeyframeSet mFloatKeyframeSet;
+ float mFloatAnimatedValue;
+
+ public FloatPropertyValuesHolder(String propertyName, FloatKeyframeSet keyframeSet) {
+ super(propertyName);
+ mValueType = float.class;
+ mKeyframeSet = keyframeSet;
+ mFloatKeyframeSet = (FloatKeyframeSet) mKeyframeSet;
+ }
+
+ //public FloatPropertyValuesHolder(Property property, FloatKeyframeSet keyframeSet) {
+ // super(property);
+ // mValueType = float.class;
+ // mKeyframeSet = keyframeSet;
+ // mFloatKeyframeSet = (FloatKeyframeSet) mKeyframeSet;
+ // if (property instanceof FloatProperty) {
+ // mFloatProperty = (FloatProperty) mProperty;
+ // }
+ //}
+
+ public FloatPropertyValuesHolder(String propertyName, float... values) {
+ super(propertyName);
+ setFloatValues(values);
+ }
+
+ //public FloatPropertyValuesHolder(Property property, float... values) {
+ // super(property);
+ // setFloatValues(values);
+ // if (property instanceof FloatProperty) {
+ // mFloatProperty = (FloatProperty) mProperty;
+ // }
+ //}
+
+ @Override
+ public void setFloatValues(float... values) {
+ super.setFloatValues(values);
+ mFloatKeyframeSet = (FloatKeyframeSet) mKeyframeSet;
+ }
+
+ @Override
+ void calculateValue(float fraction) {
+ mFloatAnimatedValue = mFloatKeyframeSet.getFloatValue(fraction);
+ }
+
+ @Override
+ Object getAnimatedValue() {
+ return mFloatAnimatedValue;
+ }
+
+ @Override
+ public FloatPropertyValuesHolder clone() {
+ FloatPropertyValuesHolder newPVH = (FloatPropertyValuesHolder) super.clone();
+ newPVH.mFloatKeyframeSet = (FloatKeyframeSet) newPVH.mKeyframeSet;
+ return newPVH;
+ }
+
+ /**
+ * Internal function to set the value on the target object, using the setter set up
+ * earlier on this PropertyValuesHolder object. This function is called by ObjectAnimator
+ * to handle turning the value calculated by ValueAnimator into a value set on the object
+ * according to the name of the property.
+ * @param target The target object on which the value is set
+ */
+ @Override
+ void setAnimatedValue(Object target) {
+ //if (mFloatProperty != null) {
+ // mFloatProperty.setValue(target, mFloatAnimatedValue);
+ // return;
+ //}
+ //if (mProperty != null) {
+ // mProperty.set(target, mFloatAnimatedValue);
+ // return;
+ //}
+ //if (mJniSetter != 0) {
+ // nCallFloatMethod(target, mJniSetter, mFloatAnimatedValue);
+ // return;
+ //}
+ if (mSetter != null) {
+ try {
+ mTmpValueArray[0] = mFloatAnimatedValue;
+ mSetter.invoke(target, mTmpValueArray);
+ } catch (InvocationTargetException e) {
+ Log.e("PropertyValuesHolder", e.toString());
+ } catch (IllegalAccessException e) {
+ Log.e("PropertyValuesHolder", e.toString());
+ }
+ }
+ }
+
+ @Override
+ void setupSetter(Class targetClass) {
+ //if (mProperty != null) {
+ // return;
+ //}
+ // Check new static hashmap for setter method
+ //try {
+ // mPropertyMapLock.writeLock().lock();
+ // HashMap propertyMap = sJNISetterPropertyMap.get(targetClass);
+ // if (propertyMap != null) {
+ // Integer mJniSetterInteger = propertyMap.get(mPropertyName);
+ // if (mJniSetterInteger != null) {
+ // mJniSetter = mJniSetterInteger;
+ // }
+ // }
+ // if (mJniSetter == 0) {
+ // String methodName = getMethodName("set", mPropertyName);
+ // mJniSetter = nGetFloatMethod(targetClass, methodName);
+ // if (mJniSetter != 0) {
+ // if (propertyMap == null) {
+ // propertyMap = new HashMap();
+ // sJNISetterPropertyMap.put(targetClass, propertyMap);
+ // }
+ // propertyMap.put(mPropertyName, mJniSetter);
+ // }
+ // }
+ //} catch (NoSuchMethodError e) {
+ // Log.d("PropertyValuesHolder",
+ // "Can't find native method using JNI, use reflection" + e);
+ //} finally {
+ // mPropertyMapLock.writeLock().unlock();
+ //}
+ //if (mJniSetter == 0) {
+ // Couldn't find method through fast JNI approach - just use reflection
+ super.setupSetter(targetClass);
+ //}
+ }
+
+ }
+
+ //native static private int nGetIntMethod(Class targetClass, String methodName);
+ //native static private int nGetFloatMethod(Class targetClass, String methodName);
+ //native static private void nCallIntMethod(Object target, int methodID, int arg);
+ //native static private void nCallFloatMethod(Object target, int methodID, float arg);
+}
diff --git a/android-app/gen-external-apklibs/com.actionbarsherlock_actionbarsherlock_4.2.0/src/com/actionbarsherlock/internal/nineoldandroids/animation/TypeEvaluator.java b/android-app/gen-external-apklibs/com.actionbarsherlock_actionbarsherlock_4.2.0/src/com/actionbarsherlock/internal/nineoldandroids/animation/TypeEvaluator.java
new file mode 100644
index 00000000..0ea31924
--- /dev/null
+++ b/android-app/gen-external-apklibs/com.actionbarsherlock_actionbarsherlock_4.2.0/src/com/actionbarsherlock/internal/nineoldandroids/animation/TypeEvaluator.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2010 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.actionbarsherlock.internal.nineoldandroids.animation;
+
+/**
+ * Interface for use with the {@link ValueAnimator#setEvaluator(TypeEvaluator)} function. Evaluators
+ * allow developers to create animations on arbitrary property types, by allowing them to supply
+ * custom evaulators for types that are not automatically understood and used by the animation
+ * system.
+ *
+ * @see ValueAnimator#setEvaluator(TypeEvaluator)
+ */
+public interface TypeEvaluator {
+
+ /**
+ * This function returns the result of linearly interpolating the start and end values, with
+ * fraction
representing the proportion between the start and end values. The
+ * calculation is a simple parametric calculation: result = x0 + t * (v1 - v0)
,
+ * where x0
is startValue
, x1
is endValue
,
+ * and t
is fraction
.
+ *
+ * @param fraction The fraction from the starting to the ending values
+ * @param startValue The start value.
+ * @param endValue The end value.
+ * @return A linear interpolation between the start and end values, given the
+ * fraction
parameter.
+ */
+ public T evaluate(float fraction, T startValue, T endValue);
+
+}
\ No newline at end of file
diff --git a/android-app/gen-external-apklibs/com.actionbarsherlock_actionbarsherlock_4.2.0/src/com/actionbarsherlock/internal/nineoldandroids/animation/ValueAnimator.java b/android-app/gen-external-apklibs/com.actionbarsherlock_actionbarsherlock_4.2.0/src/com/actionbarsherlock/internal/nineoldandroids/animation/ValueAnimator.java
new file mode 100644
index 00000000..d8a12c68
--- /dev/null
+++ b/android-app/gen-external-apklibs/com.actionbarsherlock_actionbarsherlock_4.2.0/src/com/actionbarsherlock/internal/nineoldandroids/animation/ValueAnimator.java
@@ -0,0 +1,1265 @@
+/*
+ * Copyright (C) 2010 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.actionbarsherlock.internal.nineoldandroids.animation;
+
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.util.AndroidRuntimeException;
+import android.view.animation.AccelerateDecelerateInterpolator;
+import android.view.animation.AnimationUtils;
+import android.view.animation.Interpolator;
+import android.view.animation.LinearInterpolator;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+
+/**
+ * This class provides a simple timing engine for running animations
+ * which calculate animated values and set them on target objects.
+ *
+ * There is a single timing pulse that all animations use. It runs in a
+ * custom handler to ensure that property changes happen on the UI thread.
+ *
+ * By default, ValueAnimator uses non-linear time interpolation, via the
+ * {@link AccelerateDecelerateInterpolator} class, which accelerates into and decelerates
+ * out of an animation. This behavior can be changed by calling
+ * {@link ValueAnimator#setInterpolator(TimeInterpolator)}.
+ */
+@SuppressWarnings({"rawtypes", "unchecked"})
+public class ValueAnimator extends Animator {
+
+ /**
+ * Internal constants
+ */
+
+ /*
+ * The default amount of time in ms between animation frames
+ */
+ private static final long DEFAULT_FRAME_DELAY = 10;
+
+ /**
+ * Messages sent to timing handler: START is sent when an animation first begins, FRAME is sent
+ * by the handler to itself to process the next animation frame
+ */
+ static final int ANIMATION_START = 0;
+ static final int ANIMATION_FRAME = 1;
+
+ /**
+ * Values used with internal variable mPlayingState to indicate the current state of an
+ * animation.
+ */
+ static final int STOPPED = 0; // Not yet playing
+ static final int RUNNING = 1; // Playing normally
+ static final int SEEKED = 2; // Seeked to some time value
+
+ /**
+ * Internal variables
+ * NOTE: This object implements the clone() method, making a deep copy of any referenced
+ * objects. As other non-trivial fields are added to this class, make sure to add logic
+ * to clone() to make deep copies of them.
+ */
+
+ // The first time that the animation's animateFrame() method is called. This time is used to
+ // determine elapsed time (and therefore the elapsed fraction) in subsequent calls
+ // to animateFrame()
+ long mStartTime;
+
+ /**
+ * Set when setCurrentPlayTime() is called. If negative, animation is not currently seeked
+ * to a value.
+ */
+ long mSeekTime = -1;
+
+ // TODO: We access the following ThreadLocal variables often, some of them on every update.
+ // If ThreadLocal access is significantly expensive, we may want to put all of these
+ // fields into a structure sot hat we just access ThreadLocal once to get the reference
+ // to that structure, then access the structure directly for each field.
+
+ // The static sAnimationHandler processes the internal timing loop on which all animations
+ // are based
+ private static ThreadLocal sAnimationHandler =
+ new ThreadLocal();
+
+ // The per-thread list of all active animations
+ private static final ThreadLocal> sAnimations =
+ new ThreadLocal>() {
+ @Override
+ protected ArrayList initialValue() {
+ return new ArrayList();
+ }
+ };
+
+ // The per-thread set of animations to be started on the next animation frame
+ private static final ThreadLocal> sPendingAnimations =
+ new ThreadLocal>() {
+ @Override
+ protected ArrayList initialValue() {
+ return new ArrayList();
+ }
+ };
+
+ /**
+ * Internal per-thread collections used to avoid set collisions as animations start and end
+ * while being processed.
+ */
+ private static final ThreadLocal> sDelayedAnims =
+ new ThreadLocal>() {
+ @Override
+ protected ArrayList initialValue() {
+ return new ArrayList();
+ }
+ };
+
+ private static final ThreadLocal> sEndingAnims =
+ new ThreadLocal>() {
+ @Override
+ protected ArrayList initialValue() {
+ return new ArrayList();
+ }
+ };
+
+ private static final ThreadLocal> sReadyAnims =
+ new ThreadLocal>() {
+ @Override
+ protected ArrayList initialValue() {
+ return new ArrayList();
+ }
+ };
+
+ // The time interpolator to be used if none is set on the animation
+ private static final /*Time*/Interpolator sDefaultInterpolator =
+ new AccelerateDecelerateInterpolator();
+
+ // type evaluators for the primitive types handled by this implementation
+ //private static final TypeEvaluator sIntEvaluator = new IntEvaluator();
+ //private static final TypeEvaluator sFloatEvaluator = new FloatEvaluator();
+
+ /**
+ * Used to indicate whether the animation is currently playing in reverse. This causes the
+ * elapsed fraction to be inverted to calculate the appropriate values.
+ */
+ private boolean mPlayingBackwards = false;
+
+ /**
+ * This variable tracks the current iteration that is playing. When mCurrentIteration exceeds the
+ * repeatCount (if repeatCount!=INFINITE), the animation ends
+ */
+ private int mCurrentIteration = 0;
+
+ /**
+ * Tracks current elapsed/eased fraction, for querying in getAnimatedFraction().
+ */
+ private float mCurrentFraction = 0f;
+
+ /**
+ * Tracks whether a startDelay'd animation has begun playing through the startDelay.
+ */
+ private boolean mStartedDelay = false;
+
+ /**
+ * Tracks the time at which the animation began playing through its startDelay. This is
+ * different from the mStartTime variable, which is used to track when the animation became
+ * active (which is when the startDelay expired and the animation was added to the active
+ * animations list).
+ */
+ private long mDelayStartTime;
+
+ /**
+ * Flag that represents the current state of the animation. Used to figure out when to start
+ * an animation (if state == STOPPED). Also used to end an animation that
+ * has been cancel()'d or end()'d since the last animation frame. Possible values are
+ * STOPPED, RUNNING, SEEKED.
+ */
+ int mPlayingState = STOPPED;
+
+ /**
+ * Additional playing state to indicate whether an animator has been start()'d. There is
+ * some lag between a call to start() and the first animation frame. We should still note
+ * that the animation has been started, even if it's first animation frame has not yet
+ * happened, and reflect that state in isRunning().
+ * Note that delayed animations are different: they are not started until their first
+ * animation frame, which occurs after their delay elapses.
+ */
+ private boolean mRunning = false;
+
+ /**
+ * Additional playing state to indicate whether an animator has been start()'d, whether or
+ * not there is a nonzero startDelay.
+ */
+ private boolean mStarted = false;
+
+ /**
+ * Flag that denotes whether the animation is set up and ready to go. Used to
+ * set up animation that has not yet been started.
+ */
+ boolean mInitialized = false;
+
+ //
+ // Backing variables
+ //
+
+ // How long the animation should last in ms
+ private long mDuration = 300;
+
+ // The amount of time in ms to delay starting the animation after start() is called
+ private long mStartDelay = 0;
+
+ // The number of milliseconds between animation frames
+ private static long sFrameDelay = DEFAULT_FRAME_DELAY;
+
+ // The number of times the animation will repeat. The default is 0, which means the animation
+ // will play only once
+ private int mRepeatCount = 0;
+
+ /**
+ * The type of repetition that will occur when repeatMode is nonzero. RESTART means the
+ * animation will start from the beginning on every new cycle. REVERSE means the animation
+ * will reverse directions on each iteration.
+ */
+ private int mRepeatMode = RESTART;
+
+ /**
+ * The time interpolator to be used. The elapsed fraction of the animation will be passed
+ * through this interpolator to calculate the interpolated fraction, which is then used to
+ * calculate the animated values.
+ */
+ private /*Time*/Interpolator mInterpolator = sDefaultInterpolator;
+
+ /**
+ * The set of listeners to be sent events through the life of an animation.
+ */
+ private ArrayList mUpdateListeners = null;
+
+ /**
+ * The property/value sets being animated.
+ */
+ PropertyValuesHolder[] mValues;
+
+ /**
+ * A hashmap of the PropertyValuesHolder objects. This map is used to lookup animated values
+ * by property name during calls to getAnimatedValue(String).
+ */
+ HashMap mValuesMap;
+
+ /**
+ * Public constants
+ */
+
+ /**
+ * When the animation reaches the end and repeatCount
is INFINITE
+ * or a positive value, the animation restarts from the beginning.
+ */
+ public static final int RESTART = 1;
+ /**
+ * When the animation reaches the end and repeatCount
is INFINITE
+ * or a positive value, the animation reverses direction on every iteration.
+ */
+ public static final int REVERSE = 2;
+ /**
+ * This value used used with the {@link #setRepeatCount(int)} property to repeat
+ * the animation indefinitely.
+ */
+ public static final int INFINITE = -1;
+
+ /**
+ * Creates a new ValueAnimator object. This default constructor is primarily for
+ * use internally; the factory methods which take parameters are more generally
+ * useful.
+ */
+ public ValueAnimator() {
+ }
+
+ /**
+ * Constructs and returns a ValueAnimator that animates between int values. A single
+ * value implies that that value is the one being animated to. However, this is not typically
+ * useful in a ValueAnimator object because there is no way for the object to determine the
+ * starting value for the animation (unlike ObjectAnimator, which can derive that value
+ * from the target object and property being animated). Therefore, there should typically
+ * be two or more values.
+ *
+ * @param values A set of values that the animation will animate between over time.
+ * @return A ValueAnimator object that is set up to animate between the given values.
+ */
+ public static ValueAnimator ofInt(int... values) {
+ ValueAnimator anim = new ValueAnimator();
+ anim.setIntValues(values);
+ return anim;
+ }
+
+ /**
+ * Constructs and returns a ValueAnimator that animates between float values. A single
+ * value implies that that value is the one being animated to. However, this is not typically
+ * useful in a ValueAnimator object because there is no way for the object to determine the
+ * starting value for the animation (unlike ObjectAnimator, which can derive that value
+ * from the target object and property being animated). Therefore, there should typically
+ * be two or more values.
+ *
+ * @param values A set of values that the animation will animate between over time.
+ * @return A ValueAnimator object that is set up to animate between the given values.
+ */
+ public static ValueAnimator ofFloat(float... values) {
+ ValueAnimator anim = new ValueAnimator();
+ anim.setFloatValues(values);
+ return anim;
+ }
+
+ /**
+ * Constructs and returns a ValueAnimator that animates between the values
+ * specified in the PropertyValuesHolder objects.
+ *
+ * @param values A set of PropertyValuesHolder objects whose values will be animated
+ * between over time.
+ * @return A ValueAnimator object that is set up to animate between the given values.
+ */
+ public static ValueAnimator ofPropertyValuesHolder(PropertyValuesHolder... values) {
+ ValueAnimator anim = new ValueAnimator();
+ anim.setValues(values);
+ return anim;
+ }
+ /**
+ * Constructs and returns a ValueAnimator that animates between Object values. A single
+ * value implies that that value is the one being animated to. However, this is not typically
+ * useful in a ValueAnimator object because there is no way for the object to determine the
+ * starting value for the animation (unlike ObjectAnimator, which can derive that value
+ * from the target object and property being animated). Therefore, there should typically
+ * be two or more values.
+ *
+ * Since ValueAnimator does not know how to animate between arbitrary Objects, this
+ * factory method also takes a TypeEvaluator object that the ValueAnimator will use
+ * to perform that interpolation.
+ *
+ * @param evaluator A TypeEvaluator that will be called on each animation frame to
+ * provide the ncessry interpolation between the Object values to derive the animated
+ * value.
+ * @param values A set of values that the animation will animate between over time.
+ * @return A ValueAnimator object that is set up to animate between the given values.
+ */
+ public static ValueAnimator ofObject(TypeEvaluator evaluator, Object... values) {
+ ValueAnimator anim = new ValueAnimator();
+ anim.setObjectValues(values);
+ anim.setEvaluator(evaluator);
+ return anim;
+ }
+
+ /**
+ * Sets int values that will be animated between. A single
+ * value implies that that value is the one being animated to. However, this is not typically
+ * useful in a ValueAnimator object because there is no way for the object to determine the
+ * starting value for the animation (unlike ObjectAnimator, which can derive that value
+ * from the target object and property being animated). Therefore, there should typically
+ * be two or more values.
+ *
+ *
If there are already multiple sets of values defined for this ValueAnimator via more
+ * than one PropertyValuesHolder object, this method will set the values for the first
+ * of those objects.
+ *
+ * @param values A set of values that the animation will animate between over time.
+ */
+ public void setIntValues(int... values) {
+ if (values == null || values.length == 0) {
+ return;
+ }
+ if (mValues == null || mValues.length == 0) {
+ setValues(new PropertyValuesHolder[]{PropertyValuesHolder.ofInt("", values)});
+ } else {
+ PropertyValuesHolder valuesHolder = mValues[0];
+ valuesHolder.setIntValues(values);
+ }
+ // New property/values/target should cause re-initialization prior to starting
+ mInitialized = false;
+ }
+
+ /**
+ * Sets float values that will be animated between. A single
+ * value implies that that value is the one being animated to. However, this is not typically
+ * useful in a ValueAnimator object because there is no way for the object to determine the
+ * starting value for the animation (unlike ObjectAnimator, which can derive that value
+ * from the target object and property being animated). Therefore, there should typically
+ * be two or more values.
+ *
+ * If there are already multiple sets of values defined for this ValueAnimator via more
+ * than one PropertyValuesHolder object, this method will set the values for the first
+ * of those objects.
+ *
+ * @param values A set of values that the animation will animate between over time.
+ */
+ public void setFloatValues(float... values) {
+ if (values == null || values.length == 0) {
+ return;
+ }
+ if (mValues == null || mValues.length == 0) {
+ setValues(new PropertyValuesHolder[]{PropertyValuesHolder.ofFloat("", values)});
+ } else {
+ PropertyValuesHolder valuesHolder = mValues[0];
+ valuesHolder.setFloatValues(values);
+ }
+ // New property/values/target should cause re-initialization prior to starting
+ mInitialized = false;
+ }
+
+ /**
+ * Sets the values to animate between for this animation. A single
+ * value implies that that value is the one being animated to. However, this is not typically
+ * useful in a ValueAnimator object because there is no way for the object to determine the
+ * starting value for the animation (unlike ObjectAnimator, which can derive that value
+ * from the target object and property being animated). Therefore, there should typically
+ * be two or more values.
+ *
+ * If there are already multiple sets of values defined for this ValueAnimator via more
+ * than one PropertyValuesHolder object, this method will set the values for the first
+ * of those objects.
+ *
+ * There should be a TypeEvaluator set on the ValueAnimator that knows how to interpolate
+ * between these value objects. ValueAnimator only knows how to interpolate between the
+ * primitive types specified in the other setValues() methods.
+ *
+ * @param values The set of values to animate between.
+ */
+ public void setObjectValues(Object... values) {
+ if (values == null || values.length == 0) {
+ return;
+ }
+ if (mValues == null || mValues.length == 0) {
+ setValues(new PropertyValuesHolder[]{PropertyValuesHolder.ofObject("",
+ (TypeEvaluator)null, values)});
+ } else {
+ PropertyValuesHolder valuesHolder = mValues[0];
+ valuesHolder.setObjectValues(values);
+ }
+ // New property/values/target should cause re-initialization prior to starting
+ mInitialized = false;
+ }
+
+ /**
+ * Sets the values, per property, being animated between. This function is called internally
+ * by the constructors of ValueAnimator that take a list of values. But an ValueAnimator can
+ * be constructed without values and this method can be called to set the values manually
+ * instead.
+ *
+ * @param values The set of values, per property, being animated between.
+ */
+ public void setValues(PropertyValuesHolder... values) {
+ int numValues = values.length;
+ mValues = values;
+ mValuesMap = new HashMap(numValues);
+ for (int i = 0; i < numValues; ++i) {
+ PropertyValuesHolder valuesHolder = values[i];
+ mValuesMap.put(valuesHolder.getPropertyName(), valuesHolder);
+ }
+ // New property/values/target should cause re-initialization prior to starting
+ mInitialized = false;
+ }
+
+ /**
+ * Returns the values that this ValueAnimator animates between. These values are stored in
+ * PropertyValuesHolder objects, even if the ValueAnimator was created with a simple list
+ * of value objects instead.
+ *
+ * @return PropertyValuesHolder[] An array of PropertyValuesHolder objects which hold the
+ * values, per property, that define the animation.
+ */
+ public PropertyValuesHolder[] getValues() {
+ return mValues;
+ }
+
+ /**
+ * This function is called immediately before processing the first animation
+ * frame of an animation. If there is a nonzero startDelay
, the
+ * function is called after that delay ends.
+ * It takes care of the final initialization steps for the
+ * animation.
+ *
+ * Overrides of this method should call the superclass method to ensure
+ * that internal mechanisms for the animation are set up correctly.
+ */
+ void initAnimation() {
+ if (!mInitialized) {
+ int numValues = mValues.length;
+ for (int i = 0; i < numValues; ++i) {
+ mValues[i].init();
+ }
+ mInitialized = true;
+ }
+ }
+
+
+ /**
+ * Sets the length of the animation. The default duration is 300 milliseconds.
+ *
+ * @param duration The length of the animation, in milliseconds. This value cannot
+ * be negative.
+ * @return ValueAnimator The object called with setDuration(). This return
+ * value makes it easier to compose statements together that construct and then set the
+ * duration, as in ValueAnimator.ofInt(0, 10).setDuration(500).start()
.
+ */
+ public ValueAnimator setDuration(long duration) {
+ if (duration < 0) {
+ throw new IllegalArgumentException("Animators cannot have negative duration: " +
+ duration);
+ }
+ mDuration = duration;
+ return this;
+ }
+
+ /**
+ * Gets the length of the animation. The default duration is 300 milliseconds.
+ *
+ * @return The length of the animation, in milliseconds.
+ */
+ public long getDuration() {
+ return mDuration;
+ }
+
+ /**
+ * Sets the position of the animation to the specified point in time. This time should
+ * be between 0 and the total duration of the animation, including any repetition. If
+ * the animation has not yet been started, then it will not advance forward after it is
+ * set to this time; it will simply set the time to this value and perform any appropriate
+ * actions based on that time. If the animation is already running, then setCurrentPlayTime()
+ * will set the current playing time to this value and continue playing from that point.
+ *
+ * @param playTime The time, in milliseconds, to which the animation is advanced or rewound.
+ */
+ public void setCurrentPlayTime(long playTime) {
+ initAnimation();
+ long currentTime = AnimationUtils.currentAnimationTimeMillis();
+ if (mPlayingState != RUNNING) {
+ mSeekTime = playTime;
+ mPlayingState = SEEKED;
+ }
+ mStartTime = currentTime - playTime;
+ animationFrame(currentTime);
+ }
+
+ /**
+ * Gets the current position of the animation in time, which is equal to the current
+ * time minus the time that the animation started. An animation that is not yet started will
+ * return a value of zero.
+ *
+ * @return The current position in time of the animation.
+ */
+ public long getCurrentPlayTime() {
+ if (!mInitialized || mPlayingState == STOPPED) {
+ return 0;
+ }
+ return AnimationUtils.currentAnimationTimeMillis() - mStartTime;
+ }
+
+ /**
+ * This custom, static handler handles the timing pulse that is shared by
+ * all active animations. This approach ensures that the setting of animation
+ * values will happen on the UI thread and that all animations will share
+ * the same times for calculating their values, which makes synchronizing
+ * animations possible.
+ *
+ */
+ private static class AnimationHandler extends Handler {
+ /**
+ * There are only two messages that we care about: ANIMATION_START and
+ * ANIMATION_FRAME. The START message is sent when an animation's start()
+ * method is called. It cannot start synchronously when start() is called
+ * because the call may be on the wrong thread, and it would also not be
+ * synchronized with other animations because it would not start on a common
+ * timing pulse. So each animation sends a START message to the handler, which
+ * causes the handler to place the animation on the active animations queue and
+ * start processing frames for that animation.
+ * The FRAME message is the one that is sent over and over while there are any
+ * active animations to process.
+ */
+ @Override
+ public void handleMessage(Message msg) {
+ boolean callAgain = true;
+ ArrayList animations = sAnimations.get();
+ ArrayList delayedAnims = sDelayedAnims.get();
+ switch (msg.what) {
+ // TODO: should we avoid sending frame message when starting if we
+ // were already running?
+ case ANIMATION_START:
+ ArrayList pendingAnimations = sPendingAnimations.get();
+ if (animations.size() > 0 || delayedAnims.size() > 0) {
+ callAgain = false;
+ }
+ // pendingAnims holds any animations that have requested to be started
+ // We're going to clear sPendingAnimations, but starting animation may
+ // cause more to be added to the pending list (for example, if one animation
+ // starting triggers another starting). So we loop until sPendingAnimations
+ // is empty.
+ while (pendingAnimations.size() > 0) {
+ ArrayList pendingCopy =
+ (ArrayList) pendingAnimations.clone();
+ pendingAnimations.clear();
+ int count = pendingCopy.size();
+ for (int i = 0; i < count; ++i) {
+ ValueAnimator anim = pendingCopy.get(i);
+ // If the animation has a startDelay, place it on the delayed list
+ if (anim.mStartDelay == 0) {
+ anim.startAnimation();
+ } else {
+ delayedAnims.add(anim);
+ }
+ }
+ }
+ // fall through to process first frame of new animations
+ case ANIMATION_FRAME:
+ // currentTime holds the common time for all animations processed
+ // during this frame
+ long currentTime = AnimationUtils.currentAnimationTimeMillis();
+ ArrayList readyAnims = sReadyAnims.get();
+ ArrayList endingAnims = sEndingAnims.get();
+
+ // First, process animations currently sitting on the delayed queue, adding
+ // them to the active animations if they are ready
+ int numDelayedAnims = delayedAnims.size();
+ for (int i = 0; i < numDelayedAnims; ++i) {
+ ValueAnimator anim = delayedAnims.get(i);
+ if (anim.delayedAnimationFrame(currentTime)) {
+ readyAnims.add(anim);
+ }
+ }
+ int numReadyAnims = readyAnims.size();
+ if (numReadyAnims > 0) {
+ for (int i = 0; i < numReadyAnims; ++i) {
+ ValueAnimator anim = readyAnims.get(i);
+ anim.startAnimation();
+ anim.mRunning = true;
+ delayedAnims.remove(anim);
+ }
+ readyAnims.clear();
+ }
+
+ // Now process all active animations. The return value from animationFrame()
+ // tells the handler whether it should now be ended
+ int numAnims = animations.size();
+ int i = 0;
+ while (i < numAnims) {
+ ValueAnimator anim = animations.get(i);
+ if (anim.animationFrame(currentTime)) {
+ endingAnims.add(anim);
+ }
+ if (animations.size() == numAnims) {
+ ++i;
+ } else {
+ // An animation might be canceled or ended by client code
+ // during the animation frame. Check to see if this happened by
+ // seeing whether the current index is the same as it was before
+ // calling animationFrame(). Another approach would be to copy
+ // animations to a temporary list and process that list instead,
+ // but that entails garbage and processing overhead that would
+ // be nice to avoid.
+ --numAnims;
+ endingAnims.remove(anim);
+ }
+ }
+ if (endingAnims.size() > 0) {
+ for (i = 0; i < endingAnims.size(); ++i) {
+ endingAnims.get(i).endAnimation();
+ }
+ endingAnims.clear();
+ }
+
+ // If there are still active or delayed animations, call the handler again
+ // after the frameDelay
+ if (callAgain && (!animations.isEmpty() || !delayedAnims.isEmpty())) {
+ sendEmptyMessageDelayed(ANIMATION_FRAME, Math.max(0, sFrameDelay -
+ (AnimationUtils.currentAnimationTimeMillis() - currentTime)));
+ }
+ break;
+ }
+ }
+ }
+
+ /**
+ * The amount of time, in milliseconds, to delay starting the animation after
+ * {@link #start()} is called.
+ *
+ * @return the number of milliseconds to delay running the animation
+ */
+ public long getStartDelay() {
+ return mStartDelay;
+ }
+
+ /**
+ * The amount of time, in milliseconds, to delay starting the animation after
+ * {@link #start()} is called.
+
+ * @param startDelay The amount of the delay, in milliseconds
+ */
+ public void setStartDelay(long startDelay) {
+ this.mStartDelay = startDelay;
+ }
+
+ /**
+ * The amount of time, in milliseconds, between each frame of the animation. This is a
+ * requested time that the animation will attempt to honor, but the actual delay between
+ * frames may be different, depending on system load and capabilities. This is a static
+ * function because the same delay will be applied to all animations, since they are all
+ * run off of a single timing loop.
+ *
+ * @return the requested time between frames, in milliseconds
+ */
+ public static long getFrameDelay() {
+ return sFrameDelay;
+ }
+
+ /**
+ * The amount of time, in milliseconds, between each frame of the animation. This is a
+ * requested time that the animation will attempt to honor, but the actual delay between
+ * frames may be different, depending on system load and capabilities. This is a static
+ * function because the same delay will be applied to all animations, since they are all
+ * run off of a single timing loop.
+ *
+ * @param frameDelay the requested time between frames, in milliseconds
+ */
+ public static void setFrameDelay(long frameDelay) {
+ sFrameDelay = frameDelay;
+ }
+
+ /**
+ * The most recent value calculated by this ValueAnimator
when there is just one
+ * property being animated. This value is only sensible while the animation is running. The main
+ * purpose for this read-only property is to retrieve the value from the ValueAnimator
+ * during a call to {@link AnimatorUpdateListener#onAnimationUpdate(ValueAnimator)}, which
+ * is called during each animation frame, immediately after the value is calculated.
+ *
+ * @return animatedValue The value most recently calculated by this ValueAnimator
for
+ * the single property being animated. If there are several properties being animated
+ * (specified by several PropertyValuesHolder objects in the constructor), this function
+ * returns the animated value for the first of those objects.
+ */
+ public Object getAnimatedValue() {
+ if (mValues != null && mValues.length > 0) {
+ return mValues[0].getAnimatedValue();
+ }
+ // Shouldn't get here; should always have values unless ValueAnimator was set up wrong
+ return null;
+ }
+
+ /**
+ * The most recent value calculated by this ValueAnimator
for propertyName
.
+ * The main purpose for this read-only property is to retrieve the value from the
+ * ValueAnimator
during a call to
+ * {@link AnimatorUpdateListener#onAnimationUpdate(ValueAnimator)}, which
+ * is called during each animation frame, immediately after the value is calculated.
+ *
+ * @return animatedValue The value most recently calculated for the named property
+ * by this ValueAnimator
.
+ */
+ public Object getAnimatedValue(String propertyName) {
+ PropertyValuesHolder valuesHolder = mValuesMap.get(propertyName);
+ if (valuesHolder != null) {
+ return valuesHolder.getAnimatedValue();
+ } else {
+ // At least avoid crashing if called with bogus propertyName
+ return null;
+ }
+ }
+
+ /**
+ * Sets how many times the animation should be repeated. If the repeat
+ * count is 0, the animation is never repeated. If the repeat count is
+ * greater than 0 or {@link #INFINITE}, the repeat mode will be taken
+ * into account. The repeat count is 0 by default.
+ *
+ * @param value the number of times the animation should be repeated
+ */
+ public void setRepeatCount(int value) {
+ mRepeatCount = value;
+ }
+ /**
+ * Defines how many times the animation should repeat. The default value
+ * is 0.
+ *
+ * @return the number of times the animation should repeat, or {@link #INFINITE}
+ */
+ public int getRepeatCount() {
+ return mRepeatCount;
+ }
+
+ /**
+ * Defines what this animation should do when it reaches the end. This
+ * setting is applied only when the repeat count is either greater than
+ * 0 or {@link #INFINITE}. Defaults to {@link #RESTART}.
+ *
+ * @param value {@link #RESTART} or {@link #REVERSE}
+ */
+ public void setRepeatMode(int value) {
+ mRepeatMode = value;
+ }
+
+ /**
+ * Defines what this animation should do when it reaches the end.
+ *
+ * @return either one of {@link #REVERSE} or {@link #RESTART}
+ */
+ public int getRepeatMode() {
+ return mRepeatMode;
+ }
+
+ /**
+ * Adds a listener to the set of listeners that are sent update events through the life of
+ * an animation. This method is called on all listeners for every frame of the animation,
+ * after the values for the animation have been calculated.
+ *
+ * @param listener the listener to be added to the current set of listeners for this animation.
+ */
+ public void addUpdateListener(AnimatorUpdateListener listener) {
+ if (mUpdateListeners == null) {
+ mUpdateListeners = new ArrayList();
+ }
+ mUpdateListeners.add(listener);
+ }
+
+ /**
+ * Removes all listeners from the set listening to frame updates for this animation.
+ */
+ public void removeAllUpdateListeners() {
+ if (mUpdateListeners == null) {
+ return;
+ }
+ mUpdateListeners.clear();
+ mUpdateListeners = null;
+ }
+
+ /**
+ * Removes a listener from the set listening to frame updates for this animation.
+ *
+ * @param listener the listener to be removed from the current set of update listeners
+ * for this animation.
+ */
+ public void removeUpdateListener(AnimatorUpdateListener listener) {
+ if (mUpdateListeners == null) {
+ return;
+ }
+ mUpdateListeners.remove(listener);
+ if (mUpdateListeners.size() == 0) {
+ mUpdateListeners = null;
+ }
+ }
+
+
+ /**
+ * The time interpolator used in calculating the elapsed fraction of this animation. The
+ * interpolator determines whether the animation runs with linear or non-linear motion,
+ * such as acceleration and deceleration. The default value is
+ * {@link android.view.animation.AccelerateDecelerateInterpolator}
+ *
+ * @param value the interpolator to be used by this animation. A value of null
+ * will result in linear interpolation.
+ */
+ @Override
+ public void setInterpolator(/*Time*/Interpolator value) {
+ if (value != null) {
+ mInterpolator = value;
+ } else {
+ mInterpolator = new LinearInterpolator();
+ }
+ }
+
+ /**
+ * Returns the timing interpolator that this ValueAnimator uses.
+ *
+ * @return The timing interpolator for this ValueAnimator.
+ */
+ public /*Time*/Interpolator getInterpolator() {
+ return mInterpolator;
+ }
+
+ /**
+ * The type evaluator to be used when calculating the animated values of this animation.
+ * The system will automatically assign a float or int evaluator based on the type
+ * of startValue
and endValue
in the constructor. But if these values
+ * are not one of these primitive types, or if different evaluation is desired (such as is
+ * necessary with int values that represent colors), a custom evaluator needs to be assigned.
+ * For example, when running an animation on color values, the {@link ArgbEvaluator}
+ * should be used to get correct RGB color interpolation.
+ *
+ * If this ValueAnimator has only one set of values being animated between, this evaluator
+ * will be used for that set. If there are several sets of values being animated, which is
+ * the case if PropertyValuesHOlder objects were set on the ValueAnimator, then the evaluator
+ * is assigned just to the first PropertyValuesHolder object.
+ *
+ * @param value the evaluator to be used this animation
+ */
+ public void setEvaluator(TypeEvaluator value) {
+ if (value != null && mValues != null && mValues.length > 0) {
+ mValues[0].setEvaluator(value);
+ }
+ }
+
+ /**
+ * Start the animation playing. This version of start() takes a boolean flag that indicates
+ * whether the animation should play in reverse. The flag is usually false, but may be set
+ * to true if called from the reverse() method.
+ *
+ * The animation started by calling this method will be run on the thread that called
+ * this method. This thread should have a Looper on it (a runtime exception will be thrown if
+ * this is not the case). Also, if the animation will animate
+ * properties of objects in the view hierarchy, then the calling thread should be the UI
+ * thread for that view hierarchy.
+ *
+ * @param playBackwards Whether the ValueAnimator should start playing in reverse.
+ */
+ private void start(boolean playBackwards) {
+ if (Looper.myLooper() == null) {
+ throw new AndroidRuntimeException("Animators may only be run on Looper threads");
+ }
+ mPlayingBackwards = playBackwards;
+ mCurrentIteration = 0;
+ mPlayingState = STOPPED;
+ mStarted = true;
+ mStartedDelay = false;
+ sPendingAnimations.get().add(this);
+ if (mStartDelay == 0) {
+ // This sets the initial value of the animation, prior to actually starting it running
+ setCurrentPlayTime(getCurrentPlayTime());
+ mPlayingState = STOPPED;
+ mRunning = true;
+
+ if (mListeners != null) {
+ ArrayList tmpListeners =
+ (ArrayList) mListeners.clone();
+ int numListeners = tmpListeners.size();
+ for (int i = 0; i < numListeners; ++i) {
+ tmpListeners.get(i).onAnimationStart(this);
+ }
+ }
+ }
+ AnimationHandler animationHandler = sAnimationHandler.get();
+ if (animationHandler == null) {
+ animationHandler = new AnimationHandler();
+ sAnimationHandler.set(animationHandler);
+ }
+ animationHandler.sendEmptyMessage(ANIMATION_START);
+ }
+
+ @Override
+ public void start() {
+ start(false);
+ }
+
+ @Override
+ public void cancel() {
+ // Only cancel if the animation is actually running or has been started and is about
+ // to run
+ if (mPlayingState != STOPPED || sPendingAnimations.get().contains(this) ||
+ sDelayedAnims.get().contains(this)) {
+ // Only notify listeners if the animator has actually started
+ if (mRunning && mListeners != null) {
+ ArrayList tmpListeners =
+ (ArrayList) mListeners.clone();
+ for (AnimatorListener listener : tmpListeners) {
+ listener.onAnimationCancel(this);
+ }
+ }
+ endAnimation();
+ }
+ }
+
+ @Override
+ public void end() {
+ if (!sAnimations.get().contains(this) && !sPendingAnimations.get().contains(this)) {
+ // Special case if the animation has not yet started; get it ready for ending
+ mStartedDelay = false;
+ startAnimation();
+ } else if (!mInitialized) {
+ initAnimation();
+ }
+ // The final value set on the target varies, depending on whether the animation
+ // was supposed to repeat an odd number of times
+ if (mRepeatCount > 0 && (mRepeatCount & 0x01) == 1) {
+ animateValue(0f);
+ } else {
+ animateValue(1f);
+ }
+ endAnimation();
+ }
+
+ @Override
+ public boolean isRunning() {
+ return (mPlayingState == RUNNING || mRunning);
+ }
+
+ @Override
+ public boolean isStarted() {
+ return mStarted;
+ }
+
+ /**
+ * Plays the ValueAnimator in reverse. If the animation is already running,
+ * it will stop itself and play backwards from the point reached when reverse was called.
+ * If the animation is not currently running, then it will start from the end and
+ * play backwards. This behavior is only set for the current animation; future playing
+ * of the animation will use the default behavior of playing forward.
+ */
+ public void reverse() {
+ mPlayingBackwards = !mPlayingBackwards;
+ if (mPlayingState == RUNNING) {
+ long currentTime = AnimationUtils.currentAnimationTimeMillis();
+ long currentPlayTime = currentTime - mStartTime;
+ long timeLeft = mDuration - currentPlayTime;
+ mStartTime = currentTime - timeLeft;
+ } else {
+ start(true);
+ }
+ }
+
+ /**
+ * Called internally to end an animation by removing it from the animations list. Must be
+ * called on the UI thread.
+ */
+ private void endAnimation() {
+ sAnimations.get().remove(this);
+ sPendingAnimations.get().remove(this);
+ sDelayedAnims.get().remove(this);
+ mPlayingState = STOPPED;
+ if (mRunning && mListeners != null) {
+ ArrayList tmpListeners =
+ (ArrayList) mListeners.clone();
+ int numListeners = tmpListeners.size();
+ for (int i = 0; i < numListeners; ++i) {
+ tmpListeners.get(i).onAnimationEnd(this);
+ }
+ }
+ mRunning = false;
+ mStarted = false;
+ }
+
+ /**
+ * Called internally to start an animation by adding it to the active animations list. Must be
+ * called on the UI thread.
+ */
+ private void startAnimation() {
+ initAnimation();
+ sAnimations.get().add(this);
+ if (mStartDelay > 0 && mListeners != null) {
+ // Listeners were already notified in start() if startDelay is 0; this is
+ // just for delayed animations
+ ArrayList tmpListeners =
+ (ArrayList) mListeners.clone();
+ int numListeners = tmpListeners.size();
+ for (int i = 0; i < numListeners; ++i) {
+ tmpListeners.get(i).onAnimationStart(this);
+ }
+ }
+ }
+
+ /**
+ * Internal function called to process an animation frame on an animation that is currently
+ * sleeping through its startDelay
phase. The return value indicates whether it
+ * should be woken up and put on the active animations queue.
+ *
+ * @param currentTime The current animation time, used to calculate whether the animation
+ * has exceeded its startDelay
and should be started.
+ * @return True if the animation's startDelay
has been exceeded and the animation
+ * should be added to the set of active animations.
+ */
+ private boolean delayedAnimationFrame(long currentTime) {
+ if (!mStartedDelay) {
+ mStartedDelay = true;
+ mDelayStartTime = currentTime;
+ } else {
+ long deltaTime = currentTime - mDelayStartTime;
+ if (deltaTime > mStartDelay) {
+ // startDelay ended - start the anim and record the
+ // mStartTime appropriately
+ mStartTime = currentTime - (deltaTime - mStartDelay);
+ mPlayingState = RUNNING;
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * This internal function processes a single animation frame for a given animation. The
+ * currentTime parameter is the timing pulse sent by the handler, used to calculate the
+ * elapsed duration, and therefore
+ * the elapsed fraction, of the animation. The return value indicates whether the animation
+ * should be ended (which happens when the elapsed time of the animation exceeds the
+ * animation's duration, including the repeatCount).
+ *
+ * @param currentTime The current time, as tracked by the static timing handler
+ * @return true if the animation's duration, including any repetitions due to
+ * repeatCount
has been exceeded and the animation should be ended.
+ */
+ boolean animationFrame(long currentTime) {
+ boolean done = false;
+
+ if (mPlayingState == STOPPED) {
+ mPlayingState = RUNNING;
+ if (mSeekTime < 0) {
+ mStartTime = currentTime;
+ } else {
+ mStartTime = currentTime - mSeekTime;
+ // Now that we're playing, reset the seek time
+ mSeekTime = -1;
+ }
+ }
+ switch (mPlayingState) {
+ case RUNNING:
+ case SEEKED:
+ float fraction = mDuration > 0 ? (float)(currentTime - mStartTime) / mDuration : 1f;
+ if (fraction >= 1f) {
+ if (mCurrentIteration < mRepeatCount || mRepeatCount == INFINITE) {
+ // Time to repeat
+ if (mListeners != null) {
+ int numListeners = mListeners.size();
+ for (int i = 0; i < numListeners; ++i) {
+ mListeners.get(i).onAnimationRepeat(this);
+ }
+ }
+ if (mRepeatMode == REVERSE) {
+ mPlayingBackwards = mPlayingBackwards ? false : true;
+ }
+ mCurrentIteration += (int)fraction;
+ fraction = fraction % 1f;
+ mStartTime += mDuration;
+ } else {
+ done = true;
+ fraction = Math.min(fraction, 1.0f);
+ }
+ }
+ if (mPlayingBackwards) {
+ fraction = 1f - fraction;
+ }
+ animateValue(fraction);
+ break;
+ }
+
+ return done;
+ }
+
+ /**
+ * Returns the current animation fraction, which is the elapsed/interpolated fraction used in
+ * the most recent frame update on the animation.
+ *
+ * @return Elapsed/interpolated fraction of the animation.
+ */
+ public float getAnimatedFraction() {
+ return mCurrentFraction;
+ }
+
+ /**
+ * This method is called with the elapsed fraction of the animation during every
+ * animation frame. This function turns the elapsed fraction into an interpolated fraction
+ * and then into an animated value (from the evaluator. The function is called mostly during
+ * animation updates, but it is also called when the end()
+ * function is called, to set the final value on the property.
+ *
+ * Overrides of this method must call the superclass to perform the calculation
+ * of the animated value.
+ *
+ * @param fraction The elapsed fraction of the animation.
+ */
+ void animateValue(float fraction) {
+ fraction = mInterpolator.getInterpolation(fraction);
+ mCurrentFraction = fraction;
+ int numValues = mValues.length;
+ for (int i = 0; i < numValues; ++i) {
+ mValues[i].calculateValue(fraction);
+ }
+ if (mUpdateListeners != null) {
+ int numListeners = mUpdateListeners.size();
+ for (int i = 0; i < numListeners; ++i) {
+ mUpdateListeners.get(i).onAnimationUpdate(this);
+ }
+ }
+ }
+
+ @Override
+ public ValueAnimator clone() {
+ final ValueAnimator anim = (ValueAnimator) super.clone();
+ if (mUpdateListeners != null) {
+ ArrayList oldListeners = mUpdateListeners;
+ anim.mUpdateListeners = new ArrayList();
+ int numListeners = oldListeners.size();
+ for (int i = 0; i < numListeners; ++i) {
+ anim.mUpdateListeners.add(oldListeners.get(i));
+ }
+ }
+ anim.mSeekTime = -1;
+ anim.mPlayingBackwards = false;
+ anim.mCurrentIteration = 0;
+ anim.mInitialized = false;
+ anim.mPlayingState = STOPPED;
+ anim.mStartedDelay = false;
+ PropertyValuesHolder[] oldValues = mValues;
+ if (oldValues != null) {
+ int numValues = oldValues.length;
+ anim.mValues = new PropertyValuesHolder[numValues];
+ anim.mValuesMap = new HashMap(numValues);
+ for (int i = 0; i < numValues; ++i) {
+ PropertyValuesHolder newValuesHolder = oldValues[i].clone();
+ anim.mValues[i] = newValuesHolder;
+ anim.mValuesMap.put(newValuesHolder.getPropertyName(), newValuesHolder);
+ }
+ }
+ return anim;
+ }
+
+ /**
+ * Implementors of this interface can add themselves as update listeners
+ * to an ValueAnimator
instance to receive callbacks on every animation
+ * frame, after the current frame's values have been calculated for that
+ * ValueAnimator
.
+ */
+ public static interface AnimatorUpdateListener {
+ /**
+ * Notifies the occurrence of another frame of the animation.
+ *
+ * @param animation The animation which was repeated.
+ */
+ void onAnimationUpdate(ValueAnimator animation);
+
+ }
+
+ /**
+ * Return the number of animations currently running.
+ *
+ * Used by StrictMode internally to annotate violations. Only
+ * called on the main thread.
+ *
+ * @hide
+ */
+ public static int getCurrentAnimationsCount() {
+ return sAnimations.get().size();
+ }
+
+ /**
+ * Clear all animations on this thread, without canceling or ending them.
+ * This should be used with caution.
+ *
+ * @hide
+ */
+ public static void clearAllAnimations() {
+ sAnimations.get().clear();
+ sPendingAnimations.get().clear();
+ sDelayedAnims.get().clear();
+ }
+
+ @Override
+ public String toString() {
+ String returnVal = "ValueAnimator@" + Integer.toHexString(hashCode());
+ if (mValues != null) {
+ for (int i = 0; i < mValues.length; ++i) {
+ returnVal += "\n " + mValues[i].toString();
+ }
+ }
+ return returnVal;
+ }
+}
diff --git a/android-app/gen-external-apklibs/com.actionbarsherlock_actionbarsherlock_4.2.0/src/com/actionbarsherlock/internal/nineoldandroids/view/NineViewGroup.java b/android-app/gen-external-apklibs/com.actionbarsherlock_actionbarsherlock_4.2.0/src/com/actionbarsherlock/internal/nineoldandroids/view/NineViewGroup.java
new file mode 100644
index 00000000..7b830b9c
--- /dev/null
+++ b/android-app/gen-external-apklibs/com.actionbarsherlock_actionbarsherlock_4.2.0/src/com/actionbarsherlock/internal/nineoldandroids/view/NineViewGroup.java
@@ -0,0 +1,79 @@
+package com.actionbarsherlock.internal.nineoldandroids.view;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.ViewGroup;
+
+import com.actionbarsherlock.internal.nineoldandroids.view.animation.AnimatorProxy;
+
+public abstract class NineViewGroup extends ViewGroup {
+ private final AnimatorProxy mProxy;
+
+ public NineViewGroup(Context context) {
+ super(context);
+ mProxy = AnimatorProxy.NEEDS_PROXY ? AnimatorProxy.wrap(this) : null;
+ }
+ public NineViewGroup(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ mProxy = AnimatorProxy.NEEDS_PROXY ? AnimatorProxy.wrap(this) : null;
+ }
+ public NineViewGroup(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+ mProxy = AnimatorProxy.NEEDS_PROXY ? AnimatorProxy.wrap(this) : null;
+ }
+
+ @Override
+ public void setVisibility(int visibility) {
+ if (mProxy != null) {
+ if (visibility == GONE) {
+ clearAnimation();
+ } else if (visibility == VISIBLE) {
+ setAnimation(mProxy);
+ }
+ }
+ super.setVisibility(visibility);
+ }
+
+ public float getAlpha() {
+ if (AnimatorProxy.NEEDS_PROXY) {
+ return mProxy.getAlpha();
+ } else {
+ return super.getAlpha();
+ }
+ }
+ public void setAlpha(float alpha) {
+ if (AnimatorProxy.NEEDS_PROXY) {
+ mProxy.setAlpha(alpha);
+ } else {
+ super.setAlpha(alpha);
+ }
+ }
+ public float getTranslationX() {
+ if (AnimatorProxy.NEEDS_PROXY) {
+ return mProxy.getTranslationX();
+ } else {
+ return super.getTranslationX();
+ }
+ }
+ public void setTranslationX(float translationX) {
+ if (AnimatorProxy.NEEDS_PROXY) {
+ mProxy.setTranslationX(translationX);
+ } else {
+ super.setTranslationX(translationX);
+ }
+ }
+ public float getTranslationY() {
+ if (AnimatorProxy.NEEDS_PROXY) {
+ return mProxy.getTranslationY();
+ } else {
+ return super.getTranslationY();
+ }
+ }
+ public void setTranslationY(float translationY) {
+ if (AnimatorProxy.NEEDS_PROXY) {
+ mProxy.setTranslationY(translationY);
+ } else {
+ super.setTranslationY(translationY);
+ }
+ }
+}
diff --git a/android-app/gen-external-apklibs/com.actionbarsherlock_actionbarsherlock_4.2.0/src/com/actionbarsherlock/internal/nineoldandroids/view/animation/AnimatorProxy.java b/android-app/gen-external-apklibs/com.actionbarsherlock_actionbarsherlock_4.2.0/src/com/actionbarsherlock/internal/nineoldandroids/view/animation/AnimatorProxy.java
new file mode 100644
index 00000000..067d0494
--- /dev/null
+++ b/android-app/gen-external-apklibs/com.actionbarsherlock_actionbarsherlock_4.2.0/src/com/actionbarsherlock/internal/nineoldandroids/view/animation/AnimatorProxy.java
@@ -0,0 +1,212 @@
+package com.actionbarsherlock.internal.nineoldandroids.view.animation;
+
+import java.lang.ref.WeakReference;
+import java.util.WeakHashMap;
+import android.graphics.Matrix;
+import android.graphics.RectF;
+import android.os.Build;
+import android.util.FloatMath;
+import android.view.View;
+import android.view.animation.Animation;
+import android.view.animation.Transformation;
+
+public final class AnimatorProxy extends Animation {
+ public static final boolean NEEDS_PROXY = Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB;
+
+ private static final WeakHashMap PROXIES =
+ new WeakHashMap();
+
+ public static AnimatorProxy wrap(View view) {
+ AnimatorProxy proxy = PROXIES.get(view);
+ if (proxy == null) {
+ proxy = new AnimatorProxy(view);
+ PROXIES.put(view, proxy);
+ }
+ return proxy;
+ }
+
+ private final WeakReference mView;
+
+ private float mAlpha = 1;
+ private float mScaleX = 1;
+ private float mScaleY = 1;
+ private float mTranslationX;
+ private float mTranslationY;
+
+ private final RectF mBefore = new RectF();
+ private final RectF mAfter = new RectF();
+ private final Matrix mTempMatrix = new Matrix();
+
+ private AnimatorProxy(View view) {
+ setDuration(0); //perform transformation immediately
+ setFillAfter(true); //persist transformation beyond duration
+ view.setAnimation(this);
+ mView = new WeakReference(view);
+ }
+
+ public float getAlpha() {
+ return mAlpha;
+ }
+ public void setAlpha(float alpha) {
+ if (mAlpha != alpha) {
+ mAlpha = alpha;
+ View view = mView.get();
+ if (view != null) {
+ view.invalidate();
+ }
+ }
+ }
+ public float getScaleX() {
+ return mScaleX;
+ }
+ public void setScaleX(float scaleX) {
+ if (mScaleX != scaleX) {
+ prepareForUpdate();
+ mScaleX = scaleX;
+ invalidateAfterUpdate();
+ }
+ }
+ public float getScaleY() {
+ return mScaleY;
+ }
+ public void setScaleY(float scaleY) {
+ if (mScaleY != scaleY) {
+ prepareForUpdate();
+ mScaleY = scaleY;
+ invalidateAfterUpdate();
+ }
+ }
+ public int getScrollX() {
+ View view = mView.get();
+ if (view == null) {
+ return 0;
+ }
+ return view.getScrollX();
+ }
+ public void setScrollX(int value) {
+ View view = mView.get();
+ if (view != null) {
+ view.scrollTo(value, view.getScrollY());
+ }
+ }
+ public int getScrollY() {
+ View view = mView.get();
+ if (view == null) {
+ return 0;
+ }
+ return view.getScrollY();
+ }
+ public void setScrollY(int value) {
+ View view = mView.get();
+ if (view != null) {
+ view.scrollTo(view.getScrollY(), value);
+ }
+ }
+
+ public float getTranslationX() {
+ return mTranslationX;
+ }
+ public void setTranslationX(float translationX) {
+ if (mTranslationX != translationX) {
+ prepareForUpdate();
+ mTranslationX = translationX;
+ invalidateAfterUpdate();
+ }
+ }
+ public float getTranslationY() {
+ return mTranslationY;
+ }
+ public void setTranslationY(float translationY) {
+ if (mTranslationY != translationY) {
+ prepareForUpdate();
+ mTranslationY = translationY;
+ invalidateAfterUpdate();
+ }
+ }
+
+ private void prepareForUpdate() {
+ View view = mView.get();
+ if (view != null) {
+ computeRect(mBefore, view);
+ }
+ }
+ private void invalidateAfterUpdate() {
+ View view = mView.get();
+ if (view == null) {
+ return;
+ }
+ View parent = (View)view.getParent();
+ if (parent == null) {
+ return;
+ }
+
+ view.setAnimation(this);
+
+ final RectF after = mAfter;
+ computeRect(after, view);
+ after.union(mBefore);
+
+ parent.invalidate(
+ (int) FloatMath.floor(after.left),
+ (int) FloatMath.floor(after.top),
+ (int) FloatMath.ceil(after.right),
+ (int) FloatMath.ceil(after.bottom));
+ }
+
+ private void computeRect(final RectF r, View view) {
+ // compute current rectangle according to matrix transformation
+ final float w = view.getWidth();
+ final float h = view.getHeight();
+
+ // use a rectangle at 0,0 to make sure we don't run into issues with scaling
+ r.set(0, 0, w, h);
+
+ final Matrix m = mTempMatrix;
+ m.reset();
+ transformMatrix(m, view);
+ mTempMatrix.mapRect(r);
+
+ r.offset(view.getLeft(), view.getTop());
+
+ // Straighten coords if rotations flipped them
+ if (r.right < r.left) {
+ final float f = r.right;
+ r.right = r.left;
+ r.left = f;
+ }
+ if (r.bottom < r.top) {
+ final float f = r.top;
+ r.top = r.bottom;
+ r.bottom = f;
+ }
+ }
+
+ private void transformMatrix(Matrix m, View view) {
+ final float w = view.getWidth();
+ final float h = view.getHeight();
+
+ final float sX = mScaleX;
+ final float sY = mScaleY;
+ if ((sX != 1.0f) || (sY != 1.0f)) {
+ final float deltaSX = ((sX * w) - w) / 2f;
+ final float deltaSY = ((sY * h) - h) / 2f;
+ m.postScale(sX, sY);
+ m.postTranslate(-deltaSX, -deltaSY);
+ }
+ m.postTranslate(mTranslationX, mTranslationY);
+ }
+
+ @Override
+ protected void applyTransformation(float interpolatedTime, Transformation t) {
+ View view = mView.get();
+ if (view != null) {
+ t.setAlpha(mAlpha);
+ transformMatrix(t.getMatrix(), view);
+ }
+ }
+
+ @Override
+ public void reset() {
+ /* Do nothing. */
+ }
+}
diff --git a/android-app/gen-external-apklibs/com.actionbarsherlock_actionbarsherlock_4.2.0/src/com/actionbarsherlock/internal/nineoldandroids/widget/NineFrameLayout.java b/android-app/gen-external-apklibs/com.actionbarsherlock_actionbarsherlock_4.2.0/src/com/actionbarsherlock/internal/nineoldandroids/widget/NineFrameLayout.java
new file mode 100644
index 00000000..953e3e84
--- /dev/null
+++ b/android-app/gen-external-apklibs/com.actionbarsherlock_actionbarsherlock_4.2.0/src/com/actionbarsherlock/internal/nineoldandroids/widget/NineFrameLayout.java
@@ -0,0 +1,57 @@
+package com.actionbarsherlock.internal.nineoldandroids.widget;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.widget.FrameLayout;
+
+import com.actionbarsherlock.internal.nineoldandroids.view.animation.AnimatorProxy;
+
+public class NineFrameLayout extends FrameLayout {
+ private final AnimatorProxy mProxy;
+
+ public NineFrameLayout(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ mProxy = AnimatorProxy.NEEDS_PROXY ? AnimatorProxy.wrap(this) : null;
+ }
+
+ @Override
+ public void setVisibility(int visibility) {
+ if (mProxy != null) {
+ if (visibility == GONE) {
+ clearAnimation();
+ } else if (visibility == VISIBLE) {
+ setAnimation(mProxy);
+ }
+ }
+ super.setVisibility(visibility);
+ }
+
+ public float getAlpha() {
+ if (AnimatorProxy.NEEDS_PROXY) {
+ return mProxy.getAlpha();
+ } else {
+ return super.getAlpha();
+ }
+ }
+ public void setAlpha(float alpha) {
+ if (AnimatorProxy.NEEDS_PROXY) {
+ mProxy.setAlpha(alpha);
+ } else {
+ super.setAlpha(alpha);
+ }
+ }
+ public float getTranslationY() {
+ if (AnimatorProxy.NEEDS_PROXY) {
+ return mProxy.getTranslationY();
+ } else {
+ return super.getTranslationY();
+ }
+ }
+ public void setTranslationY(float translationY) {
+ if (AnimatorProxy.NEEDS_PROXY) {
+ mProxy.setTranslationY(translationY);
+ } else {
+ super.setTranslationY(translationY);
+ }
+ }
+}
diff --git a/android-app/gen-external-apklibs/com.actionbarsherlock_actionbarsherlock_4.2.0/src/com/actionbarsherlock/internal/nineoldandroids/widget/NineHorizontalScrollView.java b/android-app/gen-external-apklibs/com.actionbarsherlock_actionbarsherlock_4.2.0/src/com/actionbarsherlock/internal/nineoldandroids/widget/NineHorizontalScrollView.java
new file mode 100644
index 00000000..129b5aaa
--- /dev/null
+++ b/android-app/gen-external-apklibs/com.actionbarsherlock_actionbarsherlock_4.2.0/src/com/actionbarsherlock/internal/nineoldandroids/widget/NineHorizontalScrollView.java
@@ -0,0 +1,41 @@
+package com.actionbarsherlock.internal.nineoldandroids.widget;
+
+import android.content.Context;
+import android.widget.HorizontalScrollView;
+import com.actionbarsherlock.internal.nineoldandroids.view.animation.AnimatorProxy;
+
+public class NineHorizontalScrollView extends HorizontalScrollView {
+ private final AnimatorProxy mProxy;
+
+ public NineHorizontalScrollView(Context context) {
+ super(context);
+ mProxy = AnimatorProxy.NEEDS_PROXY ? AnimatorProxy.wrap(this) : null;
+ }
+
+ @Override
+ public void setVisibility(int visibility) {
+ if (mProxy != null) {
+ if (visibility == GONE) {
+ clearAnimation();
+ } else if (visibility == VISIBLE) {
+ setAnimation(mProxy);
+ }
+ }
+ super.setVisibility(visibility);
+ }
+
+ public float getAlpha() {
+ if (AnimatorProxy.NEEDS_PROXY) {
+ return mProxy.getAlpha();
+ } else {
+ return super.getAlpha();
+ }
+ }
+ public void setAlpha(float alpha) {
+ if (AnimatorProxy.NEEDS_PROXY) {
+ mProxy.setAlpha(alpha);
+ } else {
+ super.setAlpha(alpha);
+ }
+ }
+}
diff --git a/android-app/gen-external-apklibs/com.actionbarsherlock_actionbarsherlock_4.2.0/src/com/actionbarsherlock/internal/nineoldandroids/widget/NineLinearLayout.java b/android-app/gen-external-apklibs/com.actionbarsherlock_actionbarsherlock_4.2.0/src/com/actionbarsherlock/internal/nineoldandroids/widget/NineLinearLayout.java
new file mode 100644
index 00000000..1f381013
--- /dev/null
+++ b/android-app/gen-external-apklibs/com.actionbarsherlock_actionbarsherlock_4.2.0/src/com/actionbarsherlock/internal/nineoldandroids/widget/NineLinearLayout.java
@@ -0,0 +1,57 @@
+package com.actionbarsherlock.internal.nineoldandroids.widget;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.widget.LinearLayout;
+
+import com.actionbarsherlock.internal.nineoldandroids.view.animation.AnimatorProxy;
+
+public class NineLinearLayout extends LinearLayout {
+ private final AnimatorProxy mProxy;
+
+ public NineLinearLayout(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ mProxy = AnimatorProxy.NEEDS_PROXY ? AnimatorProxy.wrap(this) : null;
+ }
+
+ @Override
+ public void setVisibility(int visibility) {
+ if (mProxy != null) {
+ if (visibility == GONE) {
+ clearAnimation();
+ } else if (visibility == VISIBLE) {
+ setAnimation(mProxy);
+ }
+ }
+ super.setVisibility(visibility);
+ }
+
+ public float getAlpha() {
+ if (AnimatorProxy.NEEDS_PROXY) {
+ return mProxy.getAlpha();
+ } else {
+ return super.getAlpha();
+ }
+ }
+ public void setAlpha(float alpha) {
+ if (AnimatorProxy.NEEDS_PROXY) {
+ mProxy.setAlpha(alpha);
+ } else {
+ super.setAlpha(alpha);
+ }
+ }
+ public float getTranslationX() {
+ if (AnimatorProxy.NEEDS_PROXY) {
+ return mProxy.getTranslationX();
+ } else {
+ return super.getTranslationX();
+ }
+ }
+ public void setTranslationX(float translationX) {
+ if (AnimatorProxy.NEEDS_PROXY) {
+ mProxy.setTranslationX(translationX);
+ } else {
+ super.setTranslationX(translationX);
+ }
+ }
+}
diff --git a/android-app/gen-external-apklibs/com.actionbarsherlock_actionbarsherlock_4.2.0/src/com/actionbarsherlock/internal/view/menu/ActionMenu.java b/android-app/gen-external-apklibs/com.actionbarsherlock_actionbarsherlock_4.2.0/src/com/actionbarsherlock/internal/view/menu/ActionMenu.java
new file mode 100644
index 00000000..0354ad1a
--- /dev/null
+++ b/android-app/gen-external-apklibs/com.actionbarsherlock_actionbarsherlock_4.2.0/src/com/actionbarsherlock/internal/view/menu/ActionMenu.java
@@ -0,0 +1,264 @@
+/*
+ * Copyright (C) 2010 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.actionbarsherlock.internal.view.menu;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.view.KeyEvent;
+
+import com.actionbarsherlock.view.Menu;
+import com.actionbarsherlock.view.MenuItem;
+import com.actionbarsherlock.view.SubMenu;
+
+/**
+ * @hide
+ */
+public class ActionMenu implements Menu {
+ private Context mContext;
+
+ private boolean mIsQwerty;
+
+ private ArrayList mItems;
+
+ public ActionMenu(Context context) {
+ mContext = context;
+ mItems = new ArrayList();
+ }
+
+ public Context getContext() {
+ return mContext;
+ }
+
+ public MenuItem add(CharSequence title) {
+ return add(0, 0, 0, title);
+ }
+
+ public MenuItem add(int titleRes) {
+ return add(0, 0, 0, titleRes);
+ }
+
+ public MenuItem add(int groupId, int itemId, int order, int titleRes) {
+ return add(groupId, itemId, order, mContext.getResources().getString(titleRes));
+ }
+
+ public MenuItem add(int groupId, int itemId, int order, CharSequence title) {
+ ActionMenuItem item = new ActionMenuItem(getContext(),
+ groupId, itemId, 0, order, title);
+ mItems.add(order, item);
+ return item;
+ }
+
+ public int addIntentOptions(int groupId, int itemId, int order,
+ ComponentName caller, Intent[] specifics, Intent intent, int flags,
+ MenuItem[] outSpecificItems) {
+ PackageManager pm = mContext.getPackageManager();
+ final List lri =
+ pm.queryIntentActivityOptions(caller, specifics, intent, 0);
+ final int N = lri != null ? lri.size() : 0;
+
+ if ((flags & FLAG_APPEND_TO_GROUP) == 0) {
+ removeGroup(groupId);
+ }
+
+ for (int i=0; i= 0) {
+ outSpecificItems[ri.specificIndex] = item;
+ }
+ }
+
+ return N;
+ }
+
+ public SubMenu addSubMenu(CharSequence title) {
+ // TODO Implement submenus
+ return null;
+ }
+
+ public SubMenu addSubMenu(int titleRes) {
+ // TODO Implement submenus
+ return null;
+ }
+
+ public SubMenu addSubMenu(int groupId, int itemId, int order,
+ CharSequence title) {
+ // TODO Implement submenus
+ return null;
+ }
+
+ public SubMenu addSubMenu(int groupId, int itemId, int order, int titleRes) {
+ // TODO Implement submenus
+ return null;
+ }
+
+ public void clear() {
+ mItems.clear();
+ }
+
+ public void close() {
+ }
+
+ private int findItemIndex(int id) {
+ final ArrayList items = mItems;
+ final int itemCount = items.size();
+ for (int i = 0; i < itemCount; i++) {
+ if (items.get(i).getItemId() == id) {
+ return i;
+ }
+ }
+
+ return -1;
+ }
+
+ public MenuItem findItem(int id) {
+ return mItems.get(findItemIndex(id));
+ }
+
+ public MenuItem getItem(int index) {
+ return mItems.get(index);
+ }
+
+ public boolean hasVisibleItems() {
+ final ArrayList items = mItems;
+ final int itemCount = items.size();
+
+ for (int i = 0; i < itemCount; i++) {
+ if (items.get(i).isVisible()) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ private ActionMenuItem findItemWithShortcut(int keyCode, KeyEvent event) {
+ // TODO Make this smarter.
+ final boolean qwerty = mIsQwerty;
+ final ArrayList items = mItems;
+ final int itemCount = items.size();
+
+ for (int i = 0; i < itemCount; i++) {
+ ActionMenuItem item = items.get(i);
+ final char shortcut = qwerty ? item.getAlphabeticShortcut() :
+ item.getNumericShortcut();
+ if (keyCode == shortcut) {
+ return item;
+ }
+ }
+ return null;
+ }
+
+ public boolean isShortcutKey(int keyCode, KeyEvent event) {
+ return findItemWithShortcut(keyCode, event) != null;
+ }
+
+ public boolean performIdentifierAction(int id, int flags) {
+ final int index = findItemIndex(id);
+ if (index < 0) {
+ return false;
+ }
+
+ return mItems.get(index).invoke();
+ }
+
+ public boolean performShortcut(int keyCode, KeyEvent event, int flags) {
+ ActionMenuItem item = findItemWithShortcut(keyCode, event);
+ if (item == null) {
+ return false;
+ }
+
+ return item.invoke();
+ }
+
+ public void removeGroup(int groupId) {
+ final ArrayList items = mItems;
+ int itemCount = items.size();
+ int i = 0;
+ while (i < itemCount) {
+ if (items.get(i).getGroupId() == groupId) {
+ items.remove(i);
+ itemCount--;
+ } else {
+ i++;
+ }
+ }
+ }
+
+ public void removeItem(int id) {
+ mItems.remove(findItemIndex(id));
+ }
+
+ public void setGroupCheckable(int group, boolean checkable,
+ boolean exclusive) {
+ final ArrayList items = mItems;
+ final int itemCount = items.size();
+
+ for (int i = 0; i < itemCount; i++) {
+ ActionMenuItem item = items.get(i);
+ if (item.getGroupId() == group) {
+ item.setCheckable(checkable);
+ item.setExclusiveCheckable(exclusive);
+ }
+ }
+ }
+
+ public void setGroupEnabled(int group, boolean enabled) {
+ final ArrayList items = mItems;
+ final int itemCount = items.size();
+
+ for (int i = 0; i < itemCount; i++) {
+ ActionMenuItem item = items.get(i);
+ if (item.getGroupId() == group) {
+ item.setEnabled(enabled);
+ }
+ }
+ }
+
+ public void setGroupVisible(int group, boolean visible) {
+ final ArrayList items = mItems;
+ final int itemCount = items.size();
+
+ for (int i = 0; i < itemCount; i++) {
+ ActionMenuItem item = items.get(i);
+ if (item.getGroupId() == group) {
+ item.setVisible(visible);
+ }
+ }
+ }
+
+ public void setQwertyMode(boolean isQwerty) {
+ mIsQwerty = isQwerty;
+ }
+
+ public int size() {
+ return mItems.size();
+ }
+}
diff --git a/android-app/gen-external-apklibs/com.actionbarsherlock_actionbarsherlock_4.2.0/src/com/actionbarsherlock/internal/view/menu/ActionMenuItem.java b/android-app/gen-external-apklibs/com.actionbarsherlock_actionbarsherlock_4.2.0/src/com/actionbarsherlock/internal/view/menu/ActionMenuItem.java
new file mode 100644
index 00000000..510b9748
--- /dev/null
+++ b/android-app/gen-external-apklibs/com.actionbarsherlock_actionbarsherlock_4.2.0/src/com/actionbarsherlock/internal/view/menu/ActionMenuItem.java
@@ -0,0 +1,278 @@
+/*
+ * Copyright (C) 2010 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.actionbarsherlock.internal.view.menu;
+
+import android.content.Context;
+import android.content.Intent;
+import android.graphics.drawable.Drawable;
+import android.view.ContextMenu.ContextMenuInfo;
+import android.view.View;
+
+import com.actionbarsherlock.view.ActionProvider;
+import com.actionbarsherlock.view.MenuItem;
+import com.actionbarsherlock.view.SubMenu;
+
+/**
+ * @hide
+ */
+public class ActionMenuItem implements MenuItem {
+ private final int mId;
+ private final int mGroup;
+ //UNUSED private final int mCategoryOrder;
+ private final int mOrdering;
+
+ private CharSequence mTitle;
+ private CharSequence mTitleCondensed;
+ private Intent mIntent;
+ private char mShortcutNumericChar;
+ private char mShortcutAlphabeticChar;
+
+ private Drawable mIconDrawable;
+ //UNUSED private int mIconResId = NO_ICON;
+
+ private Context mContext;
+
+ private MenuItem.OnMenuItemClickListener mClickListener;
+
+ //UNUSED private static final int NO_ICON = 0;
+
+ private int mFlags = ENABLED;
+ private static final int CHECKABLE = 0x00000001;
+ private static final int CHECKED = 0x00000002;
+ private static final int EXCLUSIVE = 0x00000004;
+ private static final int HIDDEN = 0x00000008;
+ private static final int ENABLED = 0x00000010;
+
+ public ActionMenuItem(Context context, int group, int id, int categoryOrder, int ordering,
+ CharSequence title) {
+ mContext = context;
+ mId = id;
+ mGroup = group;
+ //UNUSED mCategoryOrder = categoryOrder;
+ mOrdering = ordering;
+ mTitle = title;
+ }
+
+ public char getAlphabeticShortcut() {
+ return mShortcutAlphabeticChar;
+ }
+
+ public int getGroupId() {
+ return mGroup;
+ }
+
+ public Drawable getIcon() {
+ return mIconDrawable;
+ }
+
+ public Intent getIntent() {
+ return mIntent;
+ }
+
+ public int getItemId() {
+ return mId;
+ }
+
+ public ContextMenuInfo getMenuInfo() {
+ return null;
+ }
+
+ public char getNumericShortcut() {
+ return mShortcutNumericChar;
+ }
+
+ public int getOrder() {
+ return mOrdering;
+ }
+
+ public SubMenu getSubMenu() {
+ return null;
+ }
+
+ public CharSequence getTitle() {
+ return mTitle;
+ }
+
+ public CharSequence getTitleCondensed() {
+ return mTitleCondensed;
+ }
+
+ public boolean hasSubMenu() {
+ return false;
+ }
+
+ public boolean isCheckable() {
+ return (mFlags & CHECKABLE) != 0;
+ }
+
+ public boolean isChecked() {
+ return (mFlags & CHECKED) != 0;
+ }
+
+ public boolean isEnabled() {
+ return (mFlags & ENABLED) != 0;
+ }
+
+ public boolean isVisible() {
+ return (mFlags & HIDDEN) == 0;
+ }
+
+ public MenuItem setAlphabeticShortcut(char alphaChar) {
+ mShortcutAlphabeticChar = alphaChar;
+ return this;
+ }
+
+ public MenuItem setCheckable(boolean checkable) {
+ mFlags = (mFlags & ~CHECKABLE) | (checkable ? CHECKABLE : 0);
+ return this;
+ }
+
+ public ActionMenuItem setExclusiveCheckable(boolean exclusive) {
+ mFlags = (mFlags & ~EXCLUSIVE) | (exclusive ? EXCLUSIVE : 0);
+ return this;
+ }
+
+ public MenuItem setChecked(boolean checked) {
+ mFlags = (mFlags & ~CHECKED) | (checked ? CHECKED : 0);
+ return this;
+ }
+
+ public MenuItem setEnabled(boolean enabled) {
+ mFlags = (mFlags & ~ENABLED) | (enabled ? ENABLED : 0);
+ return this;
+ }
+
+ public MenuItem setIcon(Drawable icon) {
+ mIconDrawable = icon;
+ //UNUSED mIconResId = NO_ICON;
+ return this;
+ }
+
+ public MenuItem setIcon(int iconRes) {
+ //UNUSED mIconResId = iconRes;
+ mIconDrawable = mContext.getResources().getDrawable(iconRes);
+ return this;
+ }
+
+ public MenuItem setIntent(Intent intent) {
+ mIntent = intent;
+ return this;
+ }
+
+ public MenuItem setNumericShortcut(char numericChar) {
+ mShortcutNumericChar = numericChar;
+ return this;
+ }
+
+ public MenuItem setOnMenuItemClickListener(OnMenuItemClickListener menuItemClickListener) {
+ mClickListener = menuItemClickListener;
+ return this;
+ }
+
+ public MenuItem setShortcut(char numericChar, char alphaChar) {
+ mShortcutNumericChar = numericChar;
+ mShortcutAlphabeticChar = alphaChar;
+ return this;
+ }
+
+ public MenuItem setTitle(CharSequence title) {
+ mTitle = title;
+ return this;
+ }
+
+ public MenuItem setTitle(int title) {
+ mTitle = mContext.getResources().getString(title);
+ return this;
+ }
+
+ public MenuItem setTitleCondensed(CharSequence title) {
+ mTitleCondensed = title;
+ return this;
+ }
+
+ public MenuItem setVisible(boolean visible) {
+ mFlags = (mFlags & HIDDEN) | (visible ? 0 : HIDDEN);
+ return this;
+ }
+
+ public boolean invoke() {
+ if (mClickListener != null && mClickListener.onMenuItemClick(this)) {
+ return true;
+ }
+
+ if (mIntent != null) {
+ mContext.startActivity(mIntent);
+ return true;
+ }
+
+ return false;
+ }
+
+ public void setShowAsAction(int show) {
+ // Do nothing. ActionMenuItems always show as action buttons.
+ }
+
+ public MenuItem setActionView(View actionView) {
+ throw new UnsupportedOperationException();
+ }
+
+ public View getActionView() {
+ return null;
+ }
+
+ @Override
+ public MenuItem setActionView(int resId) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public ActionProvider getActionProvider() {
+ return null;
+ }
+
+ @Override
+ public MenuItem setActionProvider(ActionProvider actionProvider) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public MenuItem setShowAsActionFlags(int actionEnum) {
+ setShowAsAction(actionEnum);
+ return this;
+ }
+
+ @Override
+ public boolean expandActionView() {
+ return false;
+ }
+
+ @Override
+ public boolean collapseActionView() {
+ return false;
+ }
+
+ @Override
+ public boolean isActionViewExpanded() {
+ return false;
+ }
+
+ @Override
+ public MenuItem setOnActionExpandListener(OnActionExpandListener listener) {
+ // No need to save the listener; ActionMenuItem does not support collapsing items.
+ return this;
+ }
+}
diff --git a/android-app/gen-external-apklibs/com.actionbarsherlock_actionbarsherlock_4.2.0/src/com/actionbarsherlock/internal/view/menu/ActionMenuItemView.java b/android-app/gen-external-apklibs/com.actionbarsherlock_actionbarsherlock_4.2.0/src/com/actionbarsherlock/internal/view/menu/ActionMenuItemView.java
new file mode 100644
index 00000000..dcb50f36
--- /dev/null
+++ b/android-app/gen-external-apklibs/com.actionbarsherlock_actionbarsherlock_4.2.0/src/com/actionbarsherlock/internal/view/menu/ActionMenuItemView.java
@@ -0,0 +1,295 @@
+/*
+ * Copyright (C) 2010 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.actionbarsherlock.internal.view.menu;
+
+import java.util.HashSet;
+import java.util.Set;
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
+import android.os.Build;
+import android.text.TextUtils;
+import android.util.AttributeSet;
+import android.view.Gravity;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.accessibility.AccessibilityEvent;
+import android.widget.ImageButton;
+import android.widget.LinearLayout;
+import android.widget.Toast;
+
+import com.actionbarsherlock.R;
+import com.actionbarsherlock.internal.view.View_HasStateListenerSupport;
+import com.actionbarsherlock.internal.view.View_OnAttachStateChangeListener;
+import com.actionbarsherlock.internal.widget.CapitalizingButton;
+
+import static com.actionbarsherlock.internal.ResourcesCompat.getResources_getBoolean;
+
+/**
+ * @hide
+ */
+public class ActionMenuItemView extends LinearLayout
+ implements MenuView.ItemView, View.OnClickListener, View.OnLongClickListener,
+ ActionMenuView.ActionMenuChildView, View_HasStateListenerSupport {
+ //UNUSED private static final String TAG = "ActionMenuItemView";
+
+ private MenuItemImpl mItemData;
+ private CharSequence mTitle;
+ private MenuBuilder.ItemInvoker mItemInvoker;
+
+ private ImageButton mImageButton;
+ private CapitalizingButton mTextButton;
+ private boolean mAllowTextWithIcon;
+ private boolean mExpandedFormat;
+ private int mMinWidth;
+
+ private final Set mListeners = new HashSet();
+
+ public ActionMenuItemView(Context context) {
+ this(context, null);
+ }
+
+ public ActionMenuItemView(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public ActionMenuItemView(Context context, AttributeSet attrs, int defStyle) {
+ //TODO super(context, attrs, defStyle);
+ super(context, attrs);
+ mAllowTextWithIcon = getResources_getBoolean(context,
+ R.bool.abs__config_allowActionMenuItemTextWithIcon);
+ TypedArray a = context.obtainStyledAttributes(attrs,
+ R.styleable.SherlockActionMenuItemView, 0, 0);
+ mMinWidth = a.getDimensionPixelSize(
+ R.styleable.SherlockActionMenuItemView_android_minWidth, 0);
+ a.recycle();
+ }
+
+ @Override
+ public void addOnAttachStateChangeListener(View_OnAttachStateChangeListener listener) {
+ mListeners.add(listener);
+ }
+
+ @Override
+ public void removeOnAttachStateChangeListener(View_OnAttachStateChangeListener listener) {
+ mListeners.remove(listener);
+ }
+
+ @Override
+ protected void onAttachedToWindow() {
+ super.onAttachedToWindow();
+ for (View_OnAttachStateChangeListener listener : mListeners) {
+ listener.onViewAttachedToWindow(this);
+ }
+ }
+
+ @Override
+ protected void onDetachedFromWindow() {
+ super.onDetachedFromWindow();
+ for (View_OnAttachStateChangeListener listener : mListeners) {
+ listener.onViewDetachedFromWindow(this);
+ }
+ }
+
+ @Override
+ public void onFinishInflate() {
+
+ mImageButton = (ImageButton) findViewById(R.id.abs__imageButton);
+ mTextButton = (CapitalizingButton) findViewById(R.id.abs__textButton);
+ mImageButton.setOnClickListener(this);
+ mTextButton.setOnClickListener(this);
+ mImageButton.setOnLongClickListener(this);
+ setOnClickListener(this);
+ setOnLongClickListener(this);
+ }
+
+ public MenuItemImpl getItemData() {
+ return mItemData;
+ }
+
+ public void initialize(MenuItemImpl itemData, int menuType) {
+ mItemData = itemData;
+
+ setIcon(itemData.getIcon());
+ setTitle(itemData.getTitleForItemView(this)); // Title only takes effect if there is no icon
+ setId(itemData.getItemId());
+
+ setVisibility(itemData.isVisible() ? View.VISIBLE : View.GONE);
+ setEnabled(itemData.isEnabled());
+ }
+
+ @Override
+ public void setEnabled(boolean enabled) {
+ super.setEnabled(enabled);
+ mImageButton.setEnabled(enabled);
+ mTextButton.setEnabled(enabled);
+ }
+
+ public void onClick(View v) {
+ if (mItemInvoker != null) {
+ mItemInvoker.invokeItem(mItemData);
+ }
+ }
+
+ public void setItemInvoker(MenuBuilder.ItemInvoker invoker) {
+ mItemInvoker = invoker;
+ }
+
+ public boolean prefersCondensedTitle() {
+ return true;
+ }
+
+ public void setCheckable(boolean checkable) {
+ // TODO Support checkable action items
+ }
+
+ public void setChecked(boolean checked) {
+ // TODO Support checkable action items
+ }
+
+ public void setExpandedFormat(boolean expandedFormat) {
+ if (mExpandedFormat != expandedFormat) {
+ mExpandedFormat = expandedFormat;
+ if (mItemData != null) {
+ mItemData.actionFormatChanged();
+ }
+ }
+ }
+
+ private void updateTextButtonVisibility() {
+ boolean visible = !TextUtils.isEmpty(mTextButton.getText());
+ visible &= mImageButton.getDrawable() == null ||
+ (mItemData.showsTextAsAction() && (mAllowTextWithIcon || mExpandedFormat));
+
+ mTextButton.setVisibility(visible ? VISIBLE : GONE);
+ }
+
+ public void setIcon(Drawable icon) {
+ mImageButton.setImageDrawable(icon);
+ if (icon != null) {
+ mImageButton.setVisibility(VISIBLE);
+ } else {
+ mImageButton.setVisibility(GONE);
+ }
+
+ updateTextButtonVisibility();
+ }
+
+ public boolean hasText() {
+ return mTextButton.getVisibility() != GONE;
+ }
+
+ public void setShortcut(boolean showShortcut, char shortcutKey) {
+ // Action buttons don't show text for shortcut keys.
+ }
+
+ public void setTitle(CharSequence title) {
+ mTitle = title;
+
+ mTextButton.setTextCompat(mTitle);
+
+ setContentDescription(mTitle);
+ updateTextButtonVisibility();
+ }
+
+ @Override
+ public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) {
+ onPopulateAccessibilityEvent(event);
+ return true;
+ }
+
+ @Override
+ public void onPopulateAccessibilityEvent(AccessibilityEvent event) {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
+ super.onPopulateAccessibilityEvent(event);
+ }
+ final CharSequence cdesc = getContentDescription();
+ if (!TextUtils.isEmpty(cdesc)) {
+ event.getText().add(cdesc);
+ }
+ }
+
+ @Override
+ public boolean dispatchHoverEvent(MotionEvent event) {
+ // Don't allow children to hover; we want this to be treated as a single component.
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
+ return onHoverEvent(event);
+ }
+ return false;
+ }
+
+ public boolean showsIcon() {
+ return true;
+ }
+
+ public boolean needsDividerBefore() {
+ return hasText() && mItemData.getIcon() == null;
+ }
+
+ public boolean needsDividerAfter() {
+ return hasText();
+ }
+
+ @Override
+ public boolean onLongClick(View v) {
+ if (hasText()) {
+ // Don't show the cheat sheet for items that already show text.
+ return false;
+ }
+
+ final int[] screenPos = new int[2];
+ final Rect displayFrame = new Rect();
+ getLocationOnScreen(screenPos);
+ getWindowVisibleDisplayFrame(displayFrame);
+
+ final Context context = getContext();
+ final int width = getWidth();
+ final int height = getHeight();
+ final int midy = screenPos[1] + height / 2;
+ final int screenWidth = context.getResources().getDisplayMetrics().widthPixels;
+
+ Toast cheatSheet = Toast.makeText(context, mItemData.getTitle(), Toast.LENGTH_SHORT);
+ if (midy < displayFrame.height()) {
+ // Show along the top; follow action buttons
+ cheatSheet.setGravity(Gravity.TOP | Gravity.RIGHT,
+ screenWidth - screenPos[0] - width / 2, height);
+ } else {
+ // Show along the bottom center
+ cheatSheet.setGravity(Gravity.BOTTOM | Gravity.CENTER_HORIZONTAL, 0, height);
+ }
+ cheatSheet.show();
+ return true;
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+
+ final int widthMode = MeasureSpec.getMode(widthMeasureSpec);
+ final int specSize = MeasureSpec.getSize(widthMeasureSpec);
+ final int oldMeasuredWidth = getMeasuredWidth();
+ final int targetWidth = widthMode == MeasureSpec.AT_MOST ? Math.min(specSize, mMinWidth)
+ : mMinWidth;
+
+ if (widthMode != MeasureSpec.EXACTLY && mMinWidth > 0 && oldMeasuredWidth < targetWidth) {
+ // Remeasure at exactly the minimum width.
+ super.onMeasure(MeasureSpec.makeMeasureSpec(targetWidth, MeasureSpec.EXACTLY),
+ heightMeasureSpec);
+ }
+ }
+}
diff --git a/android-app/gen-external-apklibs/com.actionbarsherlock_actionbarsherlock_4.2.0/src/com/actionbarsherlock/internal/view/menu/ActionMenuPresenter.java b/android-app/gen-external-apklibs/com.actionbarsherlock_actionbarsherlock_4.2.0/src/com/actionbarsherlock/internal/view/menu/ActionMenuPresenter.java
new file mode 100644
index 00000000..876a22c5
--- /dev/null
+++ b/android-app/gen-external-apklibs/com.actionbarsherlock_actionbarsherlock_4.2.0/src/com/actionbarsherlock/internal/view/menu/ActionMenuPresenter.java
@@ -0,0 +1,714 @@
+/*
+ * Copyright (C) 2011 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.actionbarsherlock.internal.view.menu;
+
+import static com.actionbarsherlock.internal.ResourcesCompat.getResources_getInteger;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.Set;
+import android.content.Context;
+import android.content.res.Configuration;
+import android.content.res.Resources;
+import android.os.Build;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.SparseBooleanArray;
+import android.view.SoundEffectConstants;
+import android.view.View;
+import android.view.View.MeasureSpec;
+import android.view.ViewConfiguration;
+import android.view.ViewGroup;
+import android.widget.ImageButton;
+import com.actionbarsherlock.R;
+import com.actionbarsherlock.internal.view.View_HasStateListenerSupport;
+import com.actionbarsherlock.internal.view.View_OnAttachStateChangeListener;
+import com.actionbarsherlock.internal.view.menu.ActionMenuView.ActionMenuChildView;
+import com.actionbarsherlock.view.ActionProvider;
+import com.actionbarsherlock.view.MenuItem;
+
+/**
+ * MenuPresenter for building action menus as seen in the action bar and action modes.
+ */
+public class ActionMenuPresenter extends BaseMenuPresenter
+ implements ActionProvider.SubUiVisibilityListener {
+ //UNUSED private static final String TAG = "ActionMenuPresenter";
+
+ private View mOverflowButton;
+ private boolean mReserveOverflow;
+ private boolean mReserveOverflowSet;
+ private int mWidthLimit;
+ private int mActionItemWidthLimit;
+ private int mMaxItems;
+ private boolean mMaxItemsSet;
+ private boolean mStrictWidthLimit;
+ private boolean mWidthLimitSet;
+ private boolean mExpandedActionViewsExclusive;
+
+ private int mMinCellSize;
+
+ // Group IDs that have been added as actions - used temporarily, allocated here for reuse.
+ private final SparseBooleanArray mActionButtonGroups = new SparseBooleanArray();
+
+ private View mScrapActionButtonView;
+
+ private OverflowPopup mOverflowPopup;
+ private ActionButtonSubmenu mActionButtonPopup;
+
+ private OpenOverflowRunnable mPostedOpenRunnable;
+
+ final PopupPresenterCallback mPopupPresenterCallback = new PopupPresenterCallback();
+ int mOpenSubMenuId;
+
+ public ActionMenuPresenter(Context context) {
+ super(context, R.layout.abs__action_menu_layout,
+ R.layout.abs__action_menu_item_layout);
+ }
+
+ @Override
+ public void initForMenu(Context context, MenuBuilder menu) {
+ super.initForMenu(context, menu);
+
+ final Resources res = context.getResources();
+
+ if (!mReserveOverflowSet) {
+ mReserveOverflow = reserveOverflow(mContext);
+ }
+
+ if (!mWidthLimitSet) {
+ mWidthLimit = res.getDisplayMetrics().widthPixels / 2;
+ }
+
+ // Measure for initial configuration
+ if (!mMaxItemsSet) {
+ mMaxItems = getResources_getInteger(context, R.integer.abs__max_action_buttons);
+ }
+
+ int width = mWidthLimit;
+ if (mReserveOverflow) {
+ if (mOverflowButton == null) {
+ mOverflowButton = new OverflowMenuButton(mSystemContext);
+ final int spec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
+ mOverflowButton.measure(spec, spec);
+ }
+ width -= mOverflowButton.getMeasuredWidth();
+ } else {
+ mOverflowButton = null;
+ }
+
+ mActionItemWidthLimit = width;
+
+ mMinCellSize = (int) (ActionMenuView.MIN_CELL_SIZE * res.getDisplayMetrics().density);
+
+ // Drop a scrap view as it may no longer reflect the proper context/config.
+ mScrapActionButtonView = null;
+ }
+
+ public static boolean reserveOverflow(Context context) {
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
+ return (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB);
+ } else {
+ return !HasPermanentMenuKey.get(context);
+ }
+ }
+
+ private static class HasPermanentMenuKey {
+ public static boolean get(Context context) {
+ return ViewConfiguration.get(context).hasPermanentMenuKey();
+ }
+ }
+
+ public void onConfigurationChanged(Configuration newConfig) {
+ if (!mMaxItemsSet) {
+ mMaxItems = getResources_getInteger(mContext,
+ R.integer.abs__max_action_buttons);
+ if (mMenu != null) {
+ mMenu.onItemsChanged(true);
+ }
+ }
+ }
+
+ public void setWidthLimit(int width, boolean strict) {
+ mWidthLimit = width;
+ mStrictWidthLimit = strict;
+ mWidthLimitSet = true;
+ }
+
+ public void setReserveOverflow(boolean reserveOverflow) {
+ mReserveOverflow = reserveOverflow;
+ mReserveOverflowSet = true;
+ }
+
+ public void setItemLimit(int itemCount) {
+ mMaxItems = itemCount;
+ mMaxItemsSet = true;
+ }
+
+ public void setExpandedActionViewsExclusive(boolean isExclusive) {
+ mExpandedActionViewsExclusive = isExclusive;
+ }
+
+ @Override
+ public MenuView getMenuView(ViewGroup root) {
+ MenuView result = super.getMenuView(root);
+ ((ActionMenuView) result).setPresenter(this);
+ return result;
+ }
+
+ @Override
+ public View getItemView(MenuItemImpl item, View convertView, ViewGroup parent) {
+ View actionView = item.getActionView();
+ if (actionView == null || item.hasCollapsibleActionView()) {
+ if (!(convertView instanceof ActionMenuItemView)) {
+ convertView = null;
+ }
+ actionView = super.getItemView(item, convertView, parent);
+ }
+ actionView.setVisibility(item.isActionViewExpanded() ? View.GONE : View.VISIBLE);
+
+ final ActionMenuView menuParent = (ActionMenuView) parent;
+ final ViewGroup.LayoutParams lp = actionView.getLayoutParams();
+ if (!menuParent.checkLayoutParams(lp)) {
+ actionView.setLayoutParams(menuParent.generateLayoutParams(lp));
+ }
+ return actionView;
+ }
+
+ @Override
+ public void bindItemView(MenuItemImpl item, MenuView.ItemView itemView) {
+ itemView.initialize(item, 0);
+
+ final ActionMenuView menuView = (ActionMenuView) mMenuView;
+ ActionMenuItemView actionItemView = (ActionMenuItemView) itemView;
+ actionItemView.setItemInvoker(menuView);
+ }
+
+ @Override
+ public boolean shouldIncludeItem(int childIndex, MenuItemImpl item) {
+ return item.isActionButton();
+ }
+
+ @Override
+ public void updateMenuView(boolean cleared) {
+ super.updateMenuView(cleared);
+
+ if (mMenu != null) {
+ final ArrayList actionItems = mMenu.getActionItems();
+ final int count = actionItems.size();
+ for (int i = 0; i < count; i++) {
+ final ActionProvider provider = actionItems.get(i).getActionProvider();
+ if (provider != null) {
+ provider.setSubUiVisibilityListener(this);
+ }
+ }
+ }
+
+ final ArrayList nonActionItems = mMenu != null ?
+ mMenu.getNonActionItems() : null;
+
+ boolean hasOverflow = false;
+ if (mReserveOverflow && nonActionItems != null) {
+ final int count = nonActionItems.size();
+ if (count == 1) {
+ hasOverflow = !nonActionItems.get(0).isActionViewExpanded();
+ } else {
+ hasOverflow = count > 0;
+ }
+ }
+
+ if (hasOverflow) {
+ if (mOverflowButton == null) {
+ mOverflowButton = new OverflowMenuButton(mSystemContext);
+ }
+ ViewGroup parent = (ViewGroup) mOverflowButton.getParent();
+ if (parent != mMenuView) {
+ if (parent != null) {
+ parent.removeView(mOverflowButton);
+ }
+ ActionMenuView menuView = (ActionMenuView) mMenuView;
+ menuView.addView(mOverflowButton, menuView.generateOverflowButtonLayoutParams());
+ }
+ } else if (mOverflowButton != null && mOverflowButton.getParent() == mMenuView) {
+ ((ViewGroup) mMenuView).removeView(mOverflowButton);
+ }
+
+ ((ActionMenuView) mMenuView).setOverflowReserved(mReserveOverflow);
+ }
+
+ @Override
+ public boolean filterLeftoverView(ViewGroup parent, int childIndex) {
+ if (parent.getChildAt(childIndex) == mOverflowButton) return false;
+ return super.filterLeftoverView(parent, childIndex);
+ }
+
+ public boolean onSubMenuSelected(SubMenuBuilder subMenu) {
+ if (!subMenu.hasVisibleItems()) return false;
+
+ SubMenuBuilder topSubMenu = subMenu;
+ while (topSubMenu.getParentMenu() != mMenu) {
+ topSubMenu = (SubMenuBuilder) topSubMenu.getParentMenu();
+ }
+ View anchor = findViewForItem(topSubMenu.getItem());
+ if (anchor == null) {
+ if (mOverflowButton == null) return false;
+ anchor = mOverflowButton;
+ }
+
+ mOpenSubMenuId = subMenu.getItem().getItemId();
+ mActionButtonPopup = new ActionButtonSubmenu(mContext, subMenu);
+ mActionButtonPopup.setAnchorView(anchor);
+ mActionButtonPopup.show();
+ super.onSubMenuSelected(subMenu);
+ return true;
+ }
+
+ private View findViewForItem(MenuItem item) {
+ final ViewGroup parent = (ViewGroup) mMenuView;
+ if (parent == null) return null;
+
+ final int count = parent.getChildCount();
+ for (int i = 0; i < count; i++) {
+ final View child = parent.getChildAt(i);
+ if (child instanceof MenuView.ItemView &&
+ ((MenuView.ItemView) child).getItemData() == item) {
+ return child;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Display the overflow menu if one is present.
+ * @return true if the overflow menu was shown, false otherwise.
+ */
+ public boolean showOverflowMenu() {
+ if (mReserveOverflow && !isOverflowMenuShowing() && mMenu != null && mMenuView != null &&
+ mPostedOpenRunnable == null && !mMenu.getNonActionItems().isEmpty()) {
+ OverflowPopup popup = new OverflowPopup(mContext, mMenu, mOverflowButton, true);
+ mPostedOpenRunnable = new OpenOverflowRunnable(popup);
+ // Post this for later; we might still need a layout for the anchor to be right.
+ ((View) mMenuView).post(mPostedOpenRunnable);
+
+ // ActionMenuPresenter uses null as a callback argument here
+ // to indicate overflow is opening.
+ super.onSubMenuSelected(null);
+
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Hide the overflow menu if it is currently showing.
+ *
+ * @return true if the overflow menu was hidden, false otherwise.
+ */
+ public boolean hideOverflowMenu() {
+ if (mPostedOpenRunnable != null && mMenuView != null) {
+ ((View) mMenuView).removeCallbacks(mPostedOpenRunnable);
+ mPostedOpenRunnable = null;
+ return true;
+ }
+
+ MenuPopupHelper popup = mOverflowPopup;
+ if (popup != null) {
+ popup.dismiss();
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Dismiss all popup menus - overflow and submenus.
+ * @return true if popups were dismissed, false otherwise. (This can be because none were open.)
+ */
+ public boolean dismissPopupMenus() {
+ boolean result = hideOverflowMenu();
+ result |= hideSubMenus();
+ return result;
+ }
+
+ /**
+ * Dismiss all submenu popups.
+ *
+ * @return true if popups were dismissed, false otherwise. (This can be because none were open.)
+ */
+ public boolean hideSubMenus() {
+ if (mActionButtonPopup != null) {
+ mActionButtonPopup.dismiss();
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * @return true if the overflow menu is currently showing
+ */
+ public boolean isOverflowMenuShowing() {
+ return mOverflowPopup != null && mOverflowPopup.isShowing();
+ }
+
+ /**
+ * @return true if space has been reserved in the action menu for an overflow item.
+ */
+ public boolean isOverflowReserved() {
+ return mReserveOverflow;
+ }
+
+ public boolean flagActionItems() {
+ final ArrayList visibleItems = mMenu.getVisibleItems();
+ final int itemsSize = visibleItems.size();
+ int maxActions = mMaxItems;
+ int widthLimit = mActionItemWidthLimit;
+ final int querySpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
+ final ViewGroup parent = (ViewGroup) mMenuView;
+
+ int requiredItems = 0;
+ int requestedItems = 0;
+ int firstActionWidth = 0;
+ boolean hasOverflow = false;
+ for (int i = 0; i < itemsSize; i++) {
+ MenuItemImpl item = visibleItems.get(i);
+ if (item.requiresActionButton()) {
+ requiredItems++;
+ } else if (item.requestsActionButton()) {
+ requestedItems++;
+ } else {
+ hasOverflow = true;
+ }
+ if (mExpandedActionViewsExclusive && item.isActionViewExpanded()) {
+ // Overflow everything if we have an expanded action view and we're
+ // space constrained.
+ maxActions = 0;
+ }
+ }
+
+ // Reserve a spot for the overflow item if needed.
+ if (mReserveOverflow &&
+ (hasOverflow || requiredItems + requestedItems > maxActions)) {
+ maxActions--;
+ }
+ maxActions -= requiredItems;
+
+ final SparseBooleanArray seenGroups = mActionButtonGroups;
+ seenGroups.clear();
+
+ int cellSize = 0;
+ int cellsRemaining = 0;
+ if (mStrictWidthLimit) {
+ cellsRemaining = widthLimit / mMinCellSize;
+ final int cellSizeRemaining = widthLimit % mMinCellSize;
+ cellSize = mMinCellSize + cellSizeRemaining / cellsRemaining;
+ }
+
+ // Flag as many more requested items as will fit.
+ for (int i = 0; i < itemsSize; i++) {
+ MenuItemImpl item = visibleItems.get(i);
+
+ if (item.requiresActionButton()) {
+ View v = getItemView(item, mScrapActionButtonView, parent);
+ if (mScrapActionButtonView == null) {
+ mScrapActionButtonView = v;
+ }
+ if (mStrictWidthLimit) {
+ cellsRemaining -= ActionMenuView.measureChildForCells(v,
+ cellSize, cellsRemaining, querySpec, 0);
+ } else {
+ v.measure(querySpec, querySpec);
+ }
+ final int measuredWidth = v.getMeasuredWidth();
+ widthLimit -= measuredWidth;
+ if (firstActionWidth == 0) {
+ firstActionWidth = measuredWidth;
+ }
+ final int groupId = item.getGroupId();
+ if (groupId != 0) {
+ seenGroups.put(groupId, true);
+ }
+ item.setIsActionButton(true);
+ } else if (item.requestsActionButton()) {
+ // Items in a group with other items that already have an action slot
+ // can break the max actions rule, but not the width limit.
+ final int groupId = item.getGroupId();
+ final boolean inGroup = seenGroups.get(groupId);
+ boolean isAction = (maxActions > 0 || inGroup) && widthLimit > 0 &&
+ (!mStrictWidthLimit || cellsRemaining > 0);
+
+ if (isAction) {
+ View v = getItemView(item, mScrapActionButtonView, parent);
+ if (mScrapActionButtonView == null) {
+ mScrapActionButtonView = v;
+ }
+ if (mStrictWidthLimit) {
+ final int cells = ActionMenuView.measureChildForCells(v,
+ cellSize, cellsRemaining, querySpec, 0);
+ cellsRemaining -= cells;
+ if (cells == 0) {
+ isAction = false;
+ }
+ } else {
+ v.measure(querySpec, querySpec);
+ }
+ final int measuredWidth = v.getMeasuredWidth();
+ widthLimit -= measuredWidth;
+ if (firstActionWidth == 0) {
+ firstActionWidth = measuredWidth;
+ }
+
+ if (mStrictWidthLimit) {
+ isAction &= widthLimit >= 0;
+ } else {
+ // Did this push the entire first item past the limit?
+ isAction &= widthLimit + firstActionWidth > 0;
+ }
+ }
+
+ if (isAction && groupId != 0) {
+ seenGroups.put(groupId, true);
+ } else if (inGroup) {
+ // We broke the width limit. Demote the whole group, they all overflow now.
+ seenGroups.put(groupId, false);
+ for (int j = 0; j < i; j++) {
+ MenuItemImpl areYouMyGroupie = visibleItems.get(j);
+ if (areYouMyGroupie.getGroupId() == groupId) {
+ // Give back the action slot
+ if (areYouMyGroupie.isActionButton()) maxActions++;
+ areYouMyGroupie.setIsActionButton(false);
+ }
+ }
+ }
+
+ if (isAction) maxActions--;
+
+ item.setIsActionButton(isAction);
+ }
+ }
+ return true;
+ }
+
+ @Override
+ public void onCloseMenu(MenuBuilder menu, boolean allMenusAreClosing) {
+ dismissPopupMenus();
+ super.onCloseMenu(menu, allMenusAreClosing);
+ }
+
+ @Override
+ public Parcelable onSaveInstanceState() {
+ SavedState state = new SavedState();
+ state.openSubMenuId = mOpenSubMenuId;
+ return state;
+ }
+
+ @Override
+ public void onRestoreInstanceState(Parcelable state) {
+ SavedState saved = (SavedState) state;
+ if (saved.openSubMenuId > 0) {
+ MenuItem item = mMenu.findItem(saved.openSubMenuId);
+ if (item != null) {
+ SubMenuBuilder subMenu = (SubMenuBuilder) item.getSubMenu();
+ onSubMenuSelected(subMenu);
+ }
+ }
+ }
+
+ @Override
+ public void onSubUiVisibilityChanged(boolean isVisible) {
+ if (isVisible) {
+ // Not a submenu, but treat it like one.
+ super.onSubMenuSelected(null);
+ } else {
+ mMenu.close(false);
+ }
+ }
+
+ private static class SavedState implements Parcelable {
+ public int openSubMenuId;
+
+ SavedState() {
+ }
+
+ SavedState(Parcel in) {
+ openSubMenuId = in.readInt();
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(openSubMenuId);
+ }
+
+ @SuppressWarnings("unused")
+ public static final Parcelable.Creator CREATOR
+ = new Parcelable.Creator() {
+ public SavedState createFromParcel(Parcel in) {
+ return new SavedState(in);
+ }
+
+ public SavedState[] newArray(int size) {
+ return new SavedState[size];
+ }
+ };
+ }
+
+ private class OverflowMenuButton extends ImageButton implements ActionMenuChildView, View_HasStateListenerSupport {
+ private final Set mListeners = new HashSet();
+
+ public OverflowMenuButton(Context context) {
+ super(context, null, R.attr.actionOverflowButtonStyle);
+
+ setClickable(true);
+ setFocusable(true);
+ setVisibility(VISIBLE);
+ setEnabled(true);
+ }
+
+ @Override
+ public boolean performClick() {
+ if (super.performClick()) {
+ return true;
+ }
+
+ playSoundEffect(SoundEffectConstants.CLICK);
+ showOverflowMenu();
+ return true;
+ }
+
+ public boolean needsDividerBefore() {
+ return false;
+ }
+
+ public boolean needsDividerAfter() {
+ return false;
+ }
+
+ @Override
+ protected void onAttachedToWindow() {
+ super.onAttachedToWindow();
+ for (View_OnAttachStateChangeListener listener : mListeners) {
+ listener.onViewAttachedToWindow(this);
+ }
+ }
+
+ @Override
+ protected void onDetachedFromWindow() {
+ super.onDetachedFromWindow();
+ for (View_OnAttachStateChangeListener listener : mListeners) {
+ listener.onViewDetachedFromWindow(this);
+ }
+
+ if (mOverflowPopup != null) mOverflowPopup.dismiss();
+ }
+
+ @Override
+ public void addOnAttachStateChangeListener(View_OnAttachStateChangeListener listener) {
+ mListeners.add(listener);
+ }
+
+ @Override
+ public void removeOnAttachStateChangeListener(View_OnAttachStateChangeListener listener) {
+ mListeners.remove(listener);
+ }
+ }
+
+ private class OverflowPopup extends MenuPopupHelper {
+ public OverflowPopup(Context context, MenuBuilder menu, View anchorView,
+ boolean overflowOnly) {
+ super(context, menu, anchorView, overflowOnly);
+ setCallback(mPopupPresenterCallback);
+ }
+
+ @Override
+ public void onDismiss() {
+ super.onDismiss();
+ mMenu.close();
+ mOverflowPopup = null;
+ }
+ }
+
+ private class ActionButtonSubmenu extends MenuPopupHelper {
+ //UNUSED private SubMenuBuilder mSubMenu;
+
+ public ActionButtonSubmenu(Context context, SubMenuBuilder subMenu) {
+ super(context, subMenu);
+ //UNUSED mSubMenu = subMenu;
+
+ MenuItemImpl item = (MenuItemImpl) subMenu.getItem();
+ if (!item.isActionButton()) {
+ // Give a reasonable anchor to nested submenus.
+ setAnchorView(mOverflowButton == null ? (View) mMenuView : mOverflowButton);
+ }
+
+ setCallback(mPopupPresenterCallback);
+
+ boolean preserveIconSpacing = false;
+ final int count = subMenu.size();
+ for (int i = 0; i < count; i++) {
+ MenuItem childItem = subMenu.getItem(i);
+ if (childItem.isVisible() && childItem.getIcon() != null) {
+ preserveIconSpacing = true;
+ break;
+ }
+ }
+ setForceShowIcon(preserveIconSpacing);
+ }
+
+ @Override
+ public void onDismiss() {
+ super.onDismiss();
+ mActionButtonPopup = null;
+ mOpenSubMenuId = 0;
+ }
+ }
+
+ private class PopupPresenterCallback implements MenuPresenter.Callback {
+
+ @Override
+ public boolean onOpenSubMenu(MenuBuilder subMenu) {
+ if (subMenu == null) return false;
+
+ mOpenSubMenuId = ((SubMenuBuilder) subMenu).getItem().getItemId();
+ return false;
+ }
+
+ @Override
+ public void onCloseMenu(MenuBuilder menu, boolean allMenusAreClosing) {
+ if (menu instanceof SubMenuBuilder) {
+ ((SubMenuBuilder) menu).getRootMenu().close(false);
+ }
+ }
+ }
+
+ private class OpenOverflowRunnable implements Runnable {
+ private OverflowPopup mPopup;
+
+ public OpenOverflowRunnable(OverflowPopup popup) {
+ mPopup = popup;
+ }
+
+ public void run() {
+ mMenu.changeMenuMode();
+ final View menuView = (View) mMenuView;
+ if (menuView != null && menuView.getWindowToken() != null && mPopup.tryShow()) {
+ mOverflowPopup = mPopup;
+ }
+ mPostedOpenRunnable = null;
+ }
+ }
+}
diff --git a/android-app/gen-external-apklibs/com.actionbarsherlock_actionbarsherlock_4.2.0/src/com/actionbarsherlock/internal/view/menu/ActionMenuView.java b/android-app/gen-external-apklibs/com.actionbarsherlock_actionbarsherlock_4.2.0/src/com/actionbarsherlock/internal/view/menu/ActionMenuView.java
new file mode 100644
index 00000000..0e3b1ae0
--- /dev/null
+++ b/android-app/gen-external-apklibs/com.actionbarsherlock_actionbarsherlock_4.2.0/src/com/actionbarsherlock/internal/view/menu/ActionMenuView.java
@@ -0,0 +1,575 @@
+/*
+ * Copyright (C) 2010 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.actionbarsherlock.internal.view.menu;
+
+import android.content.Context;
+import android.content.res.Configuration;
+import android.graphics.Canvas;
+import android.os.Build;
+import android.util.AttributeSet;
+import android.view.Gravity;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.accessibility.AccessibilityEvent;
+import android.widget.LinearLayout;
+import com.actionbarsherlock.internal.widget.IcsLinearLayout;
+
+/**
+ * @hide
+ */
+public class ActionMenuView extends IcsLinearLayout implements MenuBuilder.ItemInvoker, MenuView {
+ //UNUSED private static final String TAG = "ActionMenuView";
+ private static final boolean IS_FROYO = Build.VERSION.SDK_INT >= Build.VERSION_CODES.FROYO;
+
+ static final int MIN_CELL_SIZE = 56; // dips
+ static final int GENERATED_ITEM_PADDING = 4; // dips
+
+ private MenuBuilder mMenu;
+
+ private boolean mReserveOverflow;
+ private ActionMenuPresenter mPresenter;
+ private boolean mFormatItems;
+ private int mFormatItemsWidth;
+ private int mMinCellSize;
+ private int mGeneratedItemPadding;
+ //UNUSED private int mMeasuredExtraWidth;
+
+ private boolean mFirst = true;
+
+ public ActionMenuView(Context context) {
+ this(context, null);
+ }
+
+ public ActionMenuView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ setBaselineAligned(false);
+ final float density = context.getResources().getDisplayMetrics().density;
+ mMinCellSize = (int) (MIN_CELL_SIZE * density);
+ mGeneratedItemPadding = (int) (GENERATED_ITEM_PADDING * density);
+ }
+
+ public void setPresenter(ActionMenuPresenter presenter) {
+ mPresenter = presenter;
+ }
+
+ public boolean isExpandedFormat() {
+ return mFormatItems;
+ }
+
+ @Override
+ public void onConfigurationChanged(Configuration newConfig) {
+ if (IS_FROYO) {
+ super.onConfigurationChanged(newConfig);
+ }
+ mPresenter.updateMenuView(false);
+
+ if (mPresenter != null && mPresenter.isOverflowMenuShowing()) {
+ mPresenter.hideOverflowMenu();
+ mPresenter.showOverflowMenu();
+ }
+ }
+
+ @Override
+ protected void onDraw(Canvas canvas) {
+ //Need to trigger a relayout since we may have been added extremely
+ //late in the initial rendering (e.g., when contained in a ViewPager).
+ //See: https://github.com/JakeWharton/ActionBarSherlock/issues/272
+ if (!IS_FROYO && mFirst) {
+ mFirst = false;
+ requestLayout();
+ return;
+ }
+ super.onDraw(canvas);
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ // If we've been given an exact size to match, apply special formatting during layout.
+ final boolean wasFormatted = mFormatItems;
+ mFormatItems = MeasureSpec.getMode(widthMeasureSpec) == MeasureSpec.EXACTLY;
+
+ if (wasFormatted != mFormatItems) {
+ mFormatItemsWidth = 0; // Reset this when switching modes
+ }
+
+ // Special formatting can change whether items can fit as action buttons.
+ // Kick the menu and update presenters when this changes.
+ final int widthSize = MeasureSpec.getMode(widthMeasureSpec);
+ if (mFormatItems && mMenu != null && widthSize != mFormatItemsWidth) {
+ mFormatItemsWidth = widthSize;
+ mMenu.onItemsChanged(true);
+ }
+
+ if (mFormatItems) {
+ onMeasureExactFormat(widthMeasureSpec, heightMeasureSpec);
+ } else {
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+ }
+ }
+
+ private void onMeasureExactFormat(int widthMeasureSpec, int heightMeasureSpec) {
+ // We already know the width mode is EXACTLY if we're here.
+ final int heightMode = MeasureSpec.getMode(heightMeasureSpec);
+ int widthSize = MeasureSpec.getSize(widthMeasureSpec);
+ int heightSize = MeasureSpec.getSize(heightMeasureSpec);
+
+ final int widthPadding = getPaddingLeft() + getPaddingRight();
+ final int heightPadding = getPaddingTop() + getPaddingBottom();
+
+ widthSize -= widthPadding;
+
+ // Divide the view into cells.
+ final int cellCount = widthSize / mMinCellSize;
+ final int cellSizeRemaining = widthSize % mMinCellSize;
+
+ if (cellCount == 0) {
+ // Give up, nothing fits.
+ setMeasuredDimension(widthSize, 0);
+ return;
+ }
+
+ final int cellSize = mMinCellSize + cellSizeRemaining / cellCount;
+
+ int cellsRemaining = cellCount;
+ int maxChildHeight = 0;
+ int maxCellsUsed = 0;
+ int expandableItemCount = 0;
+ int visibleItemCount = 0;
+ boolean hasOverflow = false;
+
+ // This is used as a bitfield to locate the smallest items present. Assumes childCount < 64.
+ long smallestItemsAt = 0;
+
+ final int childCount = getChildCount();
+ for (int i = 0; i < childCount; i++) {
+ final View child = getChildAt(i);
+ if (child.getVisibility() == GONE) continue;
+
+ final boolean isGeneratedItem = child instanceof ActionMenuItemView;
+ visibleItemCount++;
+
+ if (isGeneratedItem) {
+ // Reset padding for generated menu item views; it may change below
+ // and views are recycled.
+ child.setPadding(mGeneratedItemPadding, 0, mGeneratedItemPadding, 0);
+ }
+
+ final LayoutParams lp = (LayoutParams) child.getLayoutParams();
+ lp.expanded = false;
+ lp.extraPixels = 0;
+ lp.cellsUsed = 0;
+ lp.expandable = false;
+ lp.leftMargin = 0;
+ lp.rightMargin = 0;
+ lp.preventEdgeOffset = isGeneratedItem && ((ActionMenuItemView) child).hasText();
+
+ // Overflow always gets 1 cell. No more, no less.
+ final int cellsAvailable = lp.isOverflowButton ? 1 : cellsRemaining;
+
+ final int cellsUsed = measureChildForCells(child, cellSize, cellsAvailable,
+ heightMeasureSpec, heightPadding);
+
+ maxCellsUsed = Math.max(maxCellsUsed, cellsUsed);
+ if (lp.expandable) expandableItemCount++;
+ if (lp.isOverflowButton) hasOverflow = true;
+
+ cellsRemaining -= cellsUsed;
+ maxChildHeight = Math.max(maxChildHeight, child.getMeasuredHeight());
+ if (cellsUsed == 1) smallestItemsAt |= (1 << i);
+ }
+
+ // When we have overflow and a single expanded (text) item, we want to try centering it
+ // visually in the available space even though overflow consumes some of it.
+ final boolean centerSingleExpandedItem = hasOverflow && visibleItemCount == 2;
+
+ // Divide space for remaining cells if we have items that can expand.
+ // Try distributing whole leftover cells to smaller items first.
+
+ boolean needsExpansion = false;
+ while (expandableItemCount > 0 && cellsRemaining > 0) {
+ int minCells = Integer.MAX_VALUE;
+ long minCellsAt = 0; // Bit locations are indices of relevant child views
+ int minCellsItemCount = 0;
+ for (int i = 0; i < childCount; i++) {
+ final View child = getChildAt(i);
+ final LayoutParams lp = (LayoutParams) child.getLayoutParams();
+
+ // Don't try to expand items that shouldn't.
+ if (!lp.expandable) continue;
+
+ // Mark indices of children that can receive an extra cell.
+ if (lp.cellsUsed < minCells) {
+ minCells = lp.cellsUsed;
+ minCellsAt = 1 << i;
+ minCellsItemCount = 1;
+ } else if (lp.cellsUsed == minCells) {
+ minCellsAt |= 1 << i;
+ minCellsItemCount++;
+ }
+ }
+
+ // Items that get expanded will always be in the set of smallest items when we're done.
+ smallestItemsAt |= minCellsAt;
+
+ if (minCellsItemCount > cellsRemaining) break; // Couldn't expand anything evenly. Stop.
+
+ // We have enough cells, all minimum size items will be incremented.
+ minCells++;
+
+ for (int i = 0; i < childCount; i++) {
+ final View child = getChildAt(i);
+ final LayoutParams lp = (LayoutParams) child.getLayoutParams();
+ if ((minCellsAt & (1 << i)) == 0) {
+ // If this item is already at our small item count, mark it for later.
+ if (lp.cellsUsed == minCells) smallestItemsAt |= 1 << i;
+ continue;
+ }
+
+ if (centerSingleExpandedItem && lp.preventEdgeOffset && cellsRemaining == 1) {
+ // Add padding to this item such that it centers.
+ child.setPadding(mGeneratedItemPadding + cellSize, 0, mGeneratedItemPadding, 0);
+ }
+ lp.cellsUsed++;
+ lp.expanded = true;
+ cellsRemaining--;
+ }
+
+ needsExpansion = true;
+ }
+
+ // Divide any space left that wouldn't divide along cell boundaries
+ // evenly among the smallest items
+
+ final boolean singleItem = !hasOverflow && visibleItemCount == 1;
+ if (cellsRemaining > 0 && smallestItemsAt != 0 &&
+ (cellsRemaining < visibleItemCount - 1 || singleItem || maxCellsUsed > 1)) {
+ float expandCount = Long.bitCount(smallestItemsAt);
+
+ if (!singleItem) {
+ // The items at the far edges may only expand by half in order to pin to either side.
+ if ((smallestItemsAt & 1) != 0) {
+ LayoutParams lp = (LayoutParams) getChildAt(0).getLayoutParams();
+ if (!lp.preventEdgeOffset) expandCount -= 0.5f;
+ }
+ if ((smallestItemsAt & (1 << (childCount - 1))) != 0) {
+ LayoutParams lp = ((LayoutParams) getChildAt(childCount - 1).getLayoutParams());
+ if (!lp.preventEdgeOffset) expandCount -= 0.5f;
+ }
+ }
+
+ final int extraPixels = expandCount > 0 ?
+ (int) (cellsRemaining * cellSize / expandCount) : 0;
+
+ for (int i = 0; i < childCount; i++) {
+ if ((smallestItemsAt & (1 << i)) == 0) continue;
+
+ final View child = getChildAt(i);
+ final LayoutParams lp = (LayoutParams) child.getLayoutParams();
+ if (child instanceof ActionMenuItemView) {
+ // If this is one of our views, expand and measure at the larger size.
+ lp.extraPixels = extraPixels;
+ lp.expanded = true;
+ if (i == 0 && !lp.preventEdgeOffset) {
+ // First item gets part of its new padding pushed out of sight.
+ // The last item will get this implicitly from layout.
+ lp.leftMargin = -extraPixels / 2;
+ }
+ needsExpansion = true;
+ } else if (lp.isOverflowButton) {
+ lp.extraPixels = extraPixels;
+ lp.expanded = true;
+ lp.rightMargin = -extraPixels / 2;
+ needsExpansion = true;
+ } else {
+ // If we don't know what it is, give it some margins instead
+ // and let it center within its space. We still want to pin
+ // against the edges.
+ if (i != 0) {
+ lp.leftMargin = extraPixels / 2;
+ }
+ if (i != childCount - 1) {
+ lp.rightMargin = extraPixels / 2;
+ }
+ }
+ }
+
+ cellsRemaining = 0;
+ }
+
+ // Remeasure any items that have had extra space allocated to them.
+ if (needsExpansion) {
+ int heightSpec = MeasureSpec.makeMeasureSpec(heightSize - heightPadding, heightMode);
+ for (int i = 0; i < childCount; i++) {
+ final View child = getChildAt(i);
+ final LayoutParams lp = (LayoutParams) child.getLayoutParams();
+
+ if (!lp.expanded) continue;
+
+ final int width = lp.cellsUsed * cellSize + lp.extraPixels;
+ child.measure(MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY), heightSpec);
+ }
+ }
+
+ if (heightMode != MeasureSpec.EXACTLY) {
+ heightSize = maxChildHeight;
+ }
+
+ setMeasuredDimension(widthSize, heightSize);
+ //UNUSED mMeasuredExtraWidth = cellsRemaining * cellSize;
+ }
+
+ /**
+ * Measure a child view to fit within cell-based formatting. The child's width
+ * will be measured to a whole multiple of cellSize.
+ *
+ * Sets the expandable and cellsUsed fields of LayoutParams.
+ *
+ * @param child Child to measure
+ * @param cellSize Size of one cell
+ * @param cellsRemaining Number of cells remaining that this view can expand to fill
+ * @param parentHeightMeasureSpec MeasureSpec used by the parent view
+ * @param parentHeightPadding Padding present in the parent view
+ * @return Number of cells this child was measured to occupy
+ */
+ static int measureChildForCells(View child, int cellSize, int cellsRemaining,
+ int parentHeightMeasureSpec, int parentHeightPadding) {
+ final LayoutParams lp = (LayoutParams) child.getLayoutParams();
+
+ final int childHeightSize = MeasureSpec.getSize(parentHeightMeasureSpec) -
+ parentHeightPadding;
+ final int childHeightMode = MeasureSpec.getMode(parentHeightMeasureSpec);
+ final int childHeightSpec = MeasureSpec.makeMeasureSpec(childHeightSize, childHeightMode);
+
+ int cellsUsed = 0;
+ if (cellsRemaining > 0) {
+ final int childWidthSpec = MeasureSpec.makeMeasureSpec(
+ cellSize * cellsRemaining, MeasureSpec.AT_MOST);
+ child.measure(childWidthSpec, childHeightSpec);
+
+ final int measuredWidth = child.getMeasuredWidth();
+ cellsUsed = measuredWidth / cellSize;
+ if (measuredWidth % cellSize != 0) cellsUsed++;
+ }
+
+ final ActionMenuItemView itemView = child instanceof ActionMenuItemView ?
+ (ActionMenuItemView) child : null;
+ final boolean expandable = !lp.isOverflowButton && itemView != null && itemView.hasText();
+ lp.expandable = expandable;
+
+ lp.cellsUsed = cellsUsed;
+ final int targetWidth = cellsUsed * cellSize;
+ child.measure(MeasureSpec.makeMeasureSpec(targetWidth, MeasureSpec.EXACTLY),
+ childHeightSpec);
+ return cellsUsed;
+ }
+
+ @Override
+ protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+ if (!mFormatItems) {
+ super.onLayout(changed, left, top, right, bottom);
+ return;
+ }
+
+ final int childCount = getChildCount();
+ final int midVertical = (top + bottom) / 2;
+ final int dividerWidth = 0;//getDividerWidth();
+ int overflowWidth = 0;
+ //UNUSED int nonOverflowWidth = 0;
+ int nonOverflowCount = 0;
+ int widthRemaining = right - left - getPaddingRight() - getPaddingLeft();
+ boolean hasOverflow = false;
+ for (int i = 0; i < childCount; i++) {
+ final View v = getChildAt(i);
+ if (v.getVisibility() == GONE) {
+ continue;
+ }
+
+ LayoutParams p = (LayoutParams) v.getLayoutParams();
+ if (p.isOverflowButton) {
+ overflowWidth = v.getMeasuredWidth();
+ if (hasDividerBeforeChildAt(i)) {
+ overflowWidth += dividerWidth;
+ }
+
+ int height = v.getMeasuredHeight();
+ int r = getWidth() - getPaddingRight() - p.rightMargin;
+ int l = r - overflowWidth;
+ int t = midVertical - (height / 2);
+ int b = t + height;
+ v.layout(l, t, r, b);
+
+ widthRemaining -= overflowWidth;
+ hasOverflow = true;
+ } else {
+ final int size = v.getMeasuredWidth() + p.leftMargin + p.rightMargin;
+ //UNUSED nonOverflowWidth += size;
+ widthRemaining -= size;
+ //if (hasDividerBeforeChildAt(i)) {
+ //UNUSED nonOverflowWidth += dividerWidth;
+ //}
+ nonOverflowCount++;
+ }
+ }
+
+ if (childCount == 1 && !hasOverflow) {
+ // Center a single child
+ final View v = getChildAt(0);
+ final int width = v.getMeasuredWidth();
+ final int height = v.getMeasuredHeight();
+ final int midHorizontal = (right - left) / 2;
+ final int l = midHorizontal - width / 2;
+ final int t = midVertical - height / 2;
+ v.layout(l, t, l + width, t + height);
+ return;
+ }
+
+ final int spacerCount = nonOverflowCount - (hasOverflow ? 0 : 1);
+ final int spacerSize = Math.max(0, spacerCount > 0 ? widthRemaining / spacerCount : 0);
+
+ int startLeft = getPaddingLeft();
+ for (int i = 0; i < childCount; i++) {
+ final View v = getChildAt(i);
+ final LayoutParams lp = (LayoutParams) v.getLayoutParams();
+ if (v.getVisibility() == GONE || lp.isOverflowButton) {
+ continue;
+ }
+
+ startLeft += lp.leftMargin;
+ int width = v.getMeasuredWidth();
+ int height = v.getMeasuredHeight();
+ int t = midVertical - height / 2;
+ v.layout(startLeft, t, startLeft + width, t + height);
+ startLeft += width + lp.rightMargin + spacerSize;
+ }
+ }
+
+ @Override
+ public void onDetachedFromWindow() {
+ super.onDetachedFromWindow();
+ mPresenter.dismissPopupMenus();
+ }
+
+ public boolean isOverflowReserved() {
+ return mReserveOverflow;
+ }
+
+ public void setOverflowReserved(boolean reserveOverflow) {
+ mReserveOverflow = reserveOverflow;
+ }
+
+ @Override
+ protected LayoutParams generateDefaultLayoutParams() {
+ LayoutParams params = new LayoutParams(LayoutParams.WRAP_CONTENT,
+ LayoutParams.WRAP_CONTENT);
+ params.gravity = Gravity.CENTER_VERTICAL;
+ return params;
+ }
+
+ @Override
+ public LayoutParams generateLayoutParams(AttributeSet attrs) {
+ return new LayoutParams(getContext(), attrs);
+ }
+
+ @Override
+ protected LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
+ if (p instanceof LayoutParams) {
+ LayoutParams result = new LayoutParams((LayoutParams) p);
+ if (result.gravity <= Gravity.NO_GRAVITY) {
+ result.gravity = Gravity.CENTER_VERTICAL;
+ }
+ return result;
+ }
+ return generateDefaultLayoutParams();
+ }
+
+ @Override
+ protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
+ return p != null && p instanceof LayoutParams;
+ }
+
+ public LayoutParams generateOverflowButtonLayoutParams() {
+ LayoutParams result = generateDefaultLayoutParams();
+ result.isOverflowButton = true;
+ return result;
+ }
+
+ public boolean invokeItem(MenuItemImpl item) {
+ return mMenu.performItemAction(item, 0);
+ }
+
+ public int getWindowAnimations() {
+ return 0;
+ }
+
+ public void initialize(MenuBuilder menu) {
+ mMenu = menu;
+ }
+
+ //@Override
+ protected boolean hasDividerBeforeChildAt(int childIndex) {
+ if (childIndex == 0) {
+ return false;
+ }
+ final View childBefore = getChildAt(childIndex - 1);
+ final View child = getChildAt(childIndex);
+ boolean result = false;
+ if (childIndex < getChildCount() && childBefore instanceof ActionMenuChildView) {
+ result |= ((ActionMenuChildView) childBefore).needsDividerAfter();
+ }
+ if (childIndex > 0 && child instanceof ActionMenuChildView) {
+ result |= ((ActionMenuChildView) child).needsDividerBefore();
+ }
+ return result;
+ }
+
+ public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) {
+ return false;
+ }
+
+ public interface ActionMenuChildView {
+ public boolean needsDividerBefore();
+ public boolean needsDividerAfter();
+ }
+
+ public static class LayoutParams extends LinearLayout.LayoutParams {
+ public boolean isOverflowButton;
+ public int cellsUsed;
+ public int extraPixels;
+ public boolean expandable;
+ public boolean preventEdgeOffset;
+
+ public boolean expanded;
+
+ public LayoutParams(Context c, AttributeSet attrs) {
+ super(c, attrs);
+ }
+
+ public LayoutParams(LayoutParams other) {
+ super((LinearLayout.LayoutParams) other);
+ isOverflowButton = other.isOverflowButton;
+ }
+
+ public LayoutParams(int width, int height) {
+ super(width, height);
+ isOverflowButton = false;
+ }
+
+ public LayoutParams(int width, int height, boolean isOverflowButton) {
+ super(width, height);
+ this.isOverflowButton = isOverflowButton;
+ }
+ }
+}
diff --git a/android-app/gen-external-apklibs/com.actionbarsherlock_actionbarsherlock_4.2.0/src/com/actionbarsherlock/internal/view/menu/BaseMenuPresenter.java b/android-app/gen-external-apklibs/com.actionbarsherlock_actionbarsherlock_4.2.0/src/com/actionbarsherlock/internal/view/menu/BaseMenuPresenter.java
new file mode 100644
index 00000000..6da26f2a
--- /dev/null
+++ b/android-app/gen-external-apklibs/com.actionbarsherlock_actionbarsherlock_4.2.0/src/com/actionbarsherlock/internal/view/menu/BaseMenuPresenter.java
@@ -0,0 +1,231 @@
+/*
+ * Copyright (C) 2011 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.actionbarsherlock.internal.view.menu;
+
+import java.util.ArrayList;
+import android.content.Context;
+import android.os.Build;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+
+/**
+ * Base class for MenuPresenters that have a consistent container view and item
+ * views. Behaves similarly to an AdapterView in that existing item views will
+ * be reused if possible when items change.
+ */
+public abstract class BaseMenuPresenter implements MenuPresenter {
+ private static final boolean IS_HONEYCOMB = Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB;
+
+ protected Context mSystemContext;
+ protected Context mContext;
+ protected MenuBuilder mMenu;
+ protected LayoutInflater mSystemInflater;
+ protected LayoutInflater mInflater;
+ private Callback mCallback;
+
+ private int mMenuLayoutRes;
+ private int mItemLayoutRes;
+
+ protected MenuView mMenuView;
+
+ private int mId;
+
+ /**
+ * Construct a new BaseMenuPresenter.
+ *
+ * @param context Context for generating system-supplied views
+ * @param menuLayoutRes Layout resource ID for the menu container view
+ * @param itemLayoutRes Layout resource ID for a single item view
+ */
+ public BaseMenuPresenter(Context context, int menuLayoutRes, int itemLayoutRes) {
+ mSystemContext = context;
+ mSystemInflater = LayoutInflater.from(context);
+ mMenuLayoutRes = menuLayoutRes;
+ mItemLayoutRes = itemLayoutRes;
+ }
+
+ @Override
+ public void initForMenu(Context context, MenuBuilder menu) {
+ mContext = context;
+ mInflater = LayoutInflater.from(mContext);
+ mMenu = menu;
+ }
+
+ @Override
+ public MenuView getMenuView(ViewGroup root) {
+ if (mMenuView == null) {
+ mMenuView = (MenuView) mSystemInflater.inflate(mMenuLayoutRes, root, false);
+ mMenuView.initialize(mMenu);
+ updateMenuView(true);
+ }
+
+ return mMenuView;
+ }
+
+ /**
+ * Reuses item views when it can
+ */
+ public void updateMenuView(boolean cleared) {
+ final ViewGroup parent = (ViewGroup) mMenuView;
+ if (parent == null) return;
+
+ int childIndex = 0;
+ if (mMenu != null) {
+ mMenu.flagActionItems();
+ ArrayList visibleItems = mMenu.getVisibleItems();
+ final int itemCount = visibleItems.size();
+ for (int i = 0; i < itemCount; i++) {
+ MenuItemImpl item = visibleItems.get(i);
+ if (shouldIncludeItem(childIndex, item)) {
+ final View convertView = parent.getChildAt(childIndex);
+ final MenuItemImpl oldItem = convertView instanceof MenuView.ItemView ?
+ ((MenuView.ItemView) convertView).getItemData() : null;
+ final View itemView = getItemView(item, convertView, parent);
+ if (item != oldItem) {
+ // Don't let old states linger with new data.
+ itemView.setPressed(false);
+ if (IS_HONEYCOMB) itemView.jumpDrawablesToCurrentState();
+ }
+ if (itemView != convertView) {
+ addItemView(itemView, childIndex);
+ }
+ childIndex++;
+ }
+ }
+ }
+
+ // Remove leftover views.
+ while (childIndex < parent.getChildCount()) {
+ if (!filterLeftoverView(parent, childIndex)) {
+ childIndex++;
+ }
+ }
+ }
+
+ /**
+ * Add an item view at the given index.
+ *
+ * @param itemView View to add
+ * @param childIndex Index within the parent to insert at
+ */
+ protected void addItemView(View itemView, int childIndex) {
+ final ViewGroup currentParent = (ViewGroup) itemView.getParent();
+ if (currentParent != null) {
+ currentParent.removeView(itemView);
+ }
+ ((ViewGroup) mMenuView).addView(itemView, childIndex);
+ }
+
+ /**
+ * Filter the child view at index and remove it if appropriate.
+ * @param parent Parent to filter from
+ * @param childIndex Index to filter
+ * @return true if the child view at index was removed
+ */
+ protected boolean filterLeftoverView(ViewGroup parent, int childIndex) {
+ parent.removeViewAt(childIndex);
+ return true;
+ }
+
+ public void setCallback(Callback cb) {
+ mCallback = cb;
+ }
+
+ /**
+ * Create a new item view that can be re-bound to other item data later.
+ *
+ * @return The new item view
+ */
+ public MenuView.ItemView createItemView(ViewGroup parent) {
+ return (MenuView.ItemView) mSystemInflater.inflate(mItemLayoutRes, parent, false);
+ }
+
+ /**
+ * Prepare an item view for use. See AdapterView for the basic idea at work here.
+ * This may require creating a new item view, but well-behaved implementations will
+ * re-use the view passed as convertView if present. The returned view will be populated
+ * with data from the item parameter.
+ *
+ * @param item Item to present
+ * @param convertView Existing view to reuse
+ * @param parent Intended parent view - use for inflation.
+ * @return View that presents the requested menu item
+ */
+ public View getItemView(MenuItemImpl item, View convertView, ViewGroup parent) {
+ MenuView.ItemView itemView;
+ if (convertView instanceof MenuView.ItemView) {
+ itemView = (MenuView.ItemView) convertView;
+ } else {
+ itemView = createItemView(parent);
+ }
+ bindItemView(item, itemView);
+ return (View) itemView;
+ }
+
+ /**
+ * Bind item data to an existing item view.
+ *
+ * @param item Item to bind
+ * @param itemView View to populate with item data
+ */
+ public abstract void bindItemView(MenuItemImpl item, MenuView.ItemView itemView);
+
+ /**
+ * Filter item by child index and item data.
+ *
+ * @param childIndex Indended presentation index of this item
+ * @param item Item to present
+ * @return true if this item should be included in this menu presentation; false otherwise
+ */
+ public boolean shouldIncludeItem(int childIndex, MenuItemImpl item) {
+ return true;
+ }
+
+ public void onCloseMenu(MenuBuilder menu, boolean allMenusAreClosing) {
+ if (mCallback != null) {
+ mCallback.onCloseMenu(menu, allMenusAreClosing);
+ }
+ }
+
+ public boolean onSubMenuSelected(SubMenuBuilder menu) {
+ if (mCallback != null) {
+ return mCallback.onOpenSubMenu(menu);
+ }
+ return false;
+ }
+
+ public boolean flagActionItems() {
+ return false;
+ }
+
+ public boolean expandItemActionView(MenuBuilder menu, MenuItemImpl item) {
+ return false;
+ }
+
+ public boolean collapseItemActionView(MenuBuilder menu, MenuItemImpl item) {
+ return false;
+ }
+
+ public int getId() {
+ return mId;
+ }
+
+ public void setId(int id) {
+ mId = id;
+ }
+}
diff --git a/android-app/gen-external-apklibs/com.actionbarsherlock_actionbarsherlock_4.2.0/src/com/actionbarsherlock/internal/view/menu/ListMenuItemView.java b/android-app/gen-external-apklibs/com.actionbarsherlock_actionbarsherlock_4.2.0/src/com/actionbarsherlock/internal/view/menu/ListMenuItemView.java
new file mode 100644
index 00000000..ac25c373
--- /dev/null
+++ b/android-app/gen-external-apklibs/com.actionbarsherlock_actionbarsherlock_4.2.0/src/com/actionbarsherlock/internal/view/menu/ListMenuItemView.java
@@ -0,0 +1,278 @@
+/*
+ * Copyright (C) 2006 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.actionbarsherlock.internal.view.menu;
+
+import com.actionbarsherlock.R;
+
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.graphics.drawable.Drawable;
+import android.util.AttributeSet;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.CheckBox;
+import android.widget.CompoundButton;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.RadioButton;
+import android.widget.TextView;
+
+/**
+ * The item view for each item in the ListView-based MenuViews.
+ */
+public class ListMenuItemView extends LinearLayout implements MenuView.ItemView {
+ private MenuItemImpl mItemData;
+
+ private ImageView mIconView;
+ private RadioButton mRadioButton;
+ private TextView mTitleView;
+ private CheckBox mCheckBox;
+ private TextView mShortcutView;
+
+ private Drawable mBackground;
+ private int mTextAppearance;
+ private Context mTextAppearanceContext;
+ private boolean mPreserveIconSpacing;
+
+ //UNUSED private int mMenuType;
+
+ private LayoutInflater mInflater;
+
+ private boolean mForceShowIcon;
+
+ final Context mContext;
+
+ public ListMenuItemView(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs);
+ mContext = context;
+
+ TypedArray a =
+ context.obtainStyledAttributes(
+ attrs, R.styleable.SherlockMenuView, defStyle, 0);
+
+ mBackground = a.getDrawable(R.styleable.SherlockMenuView_itemBackground);
+ mTextAppearance = a.getResourceId(R.styleable.
+ SherlockMenuView_itemTextAppearance, -1);
+ mPreserveIconSpacing = a.getBoolean(
+ R.styleable.SherlockMenuView_preserveIconSpacing, false);
+ mTextAppearanceContext = context;
+
+ a.recycle();
+ }
+
+ public ListMenuItemView(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ @Override
+ protected void onFinishInflate() {
+ super.onFinishInflate();
+
+ setBackgroundDrawable(mBackground);
+
+ mTitleView = (TextView) findViewById(R.id.abs__title);
+ if (mTextAppearance != -1) {
+ mTitleView.setTextAppearance(mTextAppearanceContext,
+ mTextAppearance);
+ }
+
+ mShortcutView = (TextView) findViewById(R.id.abs__shortcut);
+ }
+
+ public void initialize(MenuItemImpl itemData, int menuType) {
+ mItemData = itemData;
+ //UNUSED mMenuType = menuType;
+
+ setVisibility(itemData.isVisible() ? View.VISIBLE : View.GONE);
+
+ setTitle(itemData.getTitleForItemView(this));
+ setCheckable(itemData.isCheckable());
+ setShortcut(itemData.shouldShowShortcut(), itemData.getShortcut());
+ setIcon(itemData.getIcon());
+ setEnabled(itemData.isEnabled());
+ }
+
+ public void setForceShowIcon(boolean forceShow) {
+ mPreserveIconSpacing = mForceShowIcon = forceShow;
+ }
+
+ public void setTitle(CharSequence title) {
+ if (title != null) {
+ mTitleView.setText(title);
+
+ if (mTitleView.getVisibility() != VISIBLE) mTitleView.setVisibility(VISIBLE);
+ } else {
+ if (mTitleView.getVisibility() != GONE) mTitleView.setVisibility(GONE);
+ }
+ }
+
+ public MenuItemImpl getItemData() {
+ return mItemData;
+ }
+
+ public void setCheckable(boolean checkable) {
+
+ if (!checkable && mRadioButton == null && mCheckBox == null) {
+ return;
+ }
+
+ if (mRadioButton == null) {
+ insertRadioButton();
+ }
+ if (mCheckBox == null) {
+ insertCheckBox();
+ }
+
+ // Depending on whether its exclusive check or not, the checkbox or
+ // radio button will be the one in use (and the other will be otherCompoundButton)
+ final CompoundButton compoundButton;
+ final CompoundButton otherCompoundButton;
+
+ if (mItemData.isExclusiveCheckable()) {
+ compoundButton = mRadioButton;
+ otherCompoundButton = mCheckBox;
+ } else {
+ compoundButton = mCheckBox;
+ otherCompoundButton = mRadioButton;
+ }
+
+ if (checkable) {
+ compoundButton.setChecked(mItemData.isChecked());
+
+ final int newVisibility = checkable ? VISIBLE : GONE;
+ if (compoundButton.getVisibility() != newVisibility) {
+ compoundButton.setVisibility(newVisibility);
+ }
+
+ // Make sure the other compound button isn't visible
+ if (otherCompoundButton.getVisibility() != GONE) {
+ otherCompoundButton.setVisibility(GONE);
+ }
+ } else {
+ mCheckBox.setVisibility(GONE);
+ mRadioButton.setVisibility(GONE);
+ }
+ }
+
+ public void setChecked(boolean checked) {
+ CompoundButton compoundButton;
+
+ if (mItemData.isExclusiveCheckable()) {
+ if (mRadioButton == null) {
+ insertRadioButton();
+ }
+ compoundButton = mRadioButton;
+ } else {
+ if (mCheckBox == null) {
+ insertCheckBox();
+ }
+ compoundButton = mCheckBox;
+ }
+
+ compoundButton.setChecked(checked);
+ }
+
+ public void setShortcut(boolean showShortcut, char shortcutKey) {
+ final int newVisibility = (showShortcut && mItemData.shouldShowShortcut())
+ ? VISIBLE : GONE;
+
+ if (newVisibility == VISIBLE) {
+ mShortcutView.setText(mItemData.getShortcutLabel());
+ }
+
+ if (mShortcutView.getVisibility() != newVisibility) {
+ mShortcutView.setVisibility(newVisibility);
+ }
+ }
+
+ public void setIcon(Drawable icon) {
+ final boolean showIcon = mItemData.shouldShowIcon() || mForceShowIcon;
+ if (!showIcon && !mPreserveIconSpacing) {
+ return;
+ }
+
+ if (mIconView == null && icon == null && !mPreserveIconSpacing) {
+ return;
+ }
+
+ if (mIconView == null) {
+ insertIconView();
+ }
+
+ if (icon != null || mPreserveIconSpacing) {
+ mIconView.setImageDrawable(showIcon ? icon : null);
+
+ if (mIconView.getVisibility() != VISIBLE) {
+ mIconView.setVisibility(VISIBLE);
+ }
+ } else {
+ mIconView.setVisibility(GONE);
+ }
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ if (mIconView != null && mPreserveIconSpacing) {
+ // Enforce minimum icon spacing
+ ViewGroup.LayoutParams lp = getLayoutParams();
+ LayoutParams iconLp = (LayoutParams) mIconView.getLayoutParams();
+ if (lp.height > 0 && iconLp.width <= 0) {
+ iconLp.width = lp.height;
+ }
+ }
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+ }
+
+ private void insertIconView() {
+ LayoutInflater inflater = getInflater();
+ mIconView = (ImageView) inflater.inflate(R.layout.abs__list_menu_item_icon,
+ this, false);
+ addView(mIconView, 0);
+ }
+
+ private void insertRadioButton() {
+ LayoutInflater inflater = getInflater();
+ mRadioButton =
+ (RadioButton) inflater.inflate(R.layout.abs__list_menu_item_radio,
+ this, false);
+ addView(mRadioButton);
+ }
+
+ private void insertCheckBox() {
+ LayoutInflater inflater = getInflater();
+ mCheckBox =
+ (CheckBox) inflater.inflate(R.layout.abs__list_menu_item_checkbox,
+ this, false);
+ addView(mCheckBox);
+ }
+
+ public boolean prefersCondensedTitle() {
+ return false;
+ }
+
+ public boolean showsIcon() {
+ return mForceShowIcon;
+ }
+
+ private LayoutInflater getInflater() {
+ if (mInflater == null) {
+ mInflater = LayoutInflater.from(mContext);
+ }
+ return mInflater;
+ }
+}
diff --git a/android-app/gen-external-apklibs/com.actionbarsherlock_actionbarsherlock_4.2.0/src/com/actionbarsherlock/internal/view/menu/MenuBuilder.java b/android-app/gen-external-apklibs/com.actionbarsherlock_actionbarsherlock_4.2.0/src/com/actionbarsherlock/internal/view/menu/MenuBuilder.java
new file mode 100644
index 00000000..179b8f03
--- /dev/null
+++ b/android-app/gen-external-apklibs/com.actionbarsherlock_actionbarsherlock_4.2.0/src/com/actionbarsherlock/internal/view/menu/MenuBuilder.java
@@ -0,0 +1,1335 @@
+/*
+ * Copyright (C) 2006 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.actionbarsherlock.internal.view.menu;
+
+
+import java.lang.ref.WeakReference;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.concurrent.CopyOnWriteArrayList;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.content.res.Configuration;
+import android.content.res.Resources;
+import android.graphics.drawable.Drawable;
+import android.os.Bundle;
+import android.os.Parcelable;
+import android.util.SparseArray;
+import android.view.ContextMenu.ContextMenuInfo;
+import android.view.KeyCharacterMap;
+import android.view.KeyEvent;
+import android.view.View;
+
+import com.actionbarsherlock.R;
+import com.actionbarsherlock.view.ActionProvider;
+import com.actionbarsherlock.view.Menu;
+import com.actionbarsherlock.view.MenuItem;
+import com.actionbarsherlock.view.SubMenu;
+
+/**
+ * Implementation of the {@link android.view.Menu} interface for creating a
+ * standard menu UI.
+ */
+public class MenuBuilder implements Menu {
+ //UNUSED private static final String TAG = "MenuBuilder";
+
+ private static final String PRESENTER_KEY = "android:menu:presenters";
+ private static final String ACTION_VIEW_STATES_KEY = "android:menu:actionviewstates";
+ private static final String EXPANDED_ACTION_VIEW_ID = "android:menu:expandedactionview";
+
+ private static final int[] sCategoryToOrder = new int[] {
+ 1, /* No category */
+ 4, /* CONTAINER */
+ 5, /* SYSTEM */
+ 3, /* SECONDARY */
+ 2, /* ALTERNATIVE */
+ 0, /* SELECTED_ALTERNATIVE */
+ };
+
+ private final Context mContext;
+ private final Resources mResources;
+
+ /**
+ * Whether the shortcuts should be qwerty-accessible. Use isQwertyMode()
+ * instead of accessing this directly.
+ */
+ private boolean mQwertyMode;
+
+ /**
+ * Whether the shortcuts should be visible on menus. Use isShortcutsVisible()
+ * instead of accessing this directly.
+ */
+ private boolean mShortcutsVisible;
+
+ /**
+ * Callback that will receive the various menu-related events generated by
+ * this class. Use getCallback to get a reference to the callback.
+ */
+ private Callback mCallback;
+
+ /** Contains all of the items for this menu */
+ private ArrayList mItems;
+
+ /** Contains only the items that are currently visible. This will be created/refreshed from
+ * {@link #getVisibleItems()} */
+ private ArrayList mVisibleItems;
+ /**
+ * Whether or not the items (or any one item's shown state) has changed since it was last
+ * fetched from {@link #getVisibleItems()}
+ */
+ private boolean mIsVisibleItemsStale;
+
+ /**
+ * Contains only the items that should appear in the Action Bar, if present.
+ */
+ private ArrayList mActionItems;
+ /**
+ * Contains items that should NOT appear in the Action Bar, if present.
+ */
+ private ArrayList mNonActionItems;
+
+ /**
+ * Whether or not the items (or any one item's action state) has changed since it was
+ * last fetched.
+ */
+ private boolean mIsActionItemsStale;
+
+ /**
+ * Default value for how added items should show in the action list.
+ */
+ private int mDefaultShowAsAction = MenuItem.SHOW_AS_ACTION_NEVER;
+
+ /**
+ * Current use case is Context Menus: As Views populate the context menu, each one has
+ * extra information that should be passed along. This is the current menu info that
+ * should be set on all items added to this menu.
+ */
+ private ContextMenuInfo mCurrentMenuInfo;
+
+ /** Header title for menu types that have a header (context and submenus) */
+ CharSequence mHeaderTitle;
+ /** Header icon for menu types that have a header and support icons (context) */
+ Drawable mHeaderIcon;
+ /** Header custom view for menu types that have a header and support custom views (context) */
+ View mHeaderView;
+
+ /**
+ * Contains the state of the View hierarchy for all menu views when the menu
+ * was frozen.
+ */
+ //UNUSED private SparseArray mFrozenViewStates;
+
+ /**
+ * Prevents onItemsChanged from doing its junk, useful for batching commands
+ * that may individually call onItemsChanged.
+ */
+ private boolean mPreventDispatchingItemsChanged = false;
+ private boolean mItemsChangedWhileDispatchPrevented = false;
+
+ private boolean mOptionalIconsVisible = false;
+
+ private boolean mIsClosing = false;
+
+ private ArrayList mTempShortcutItemList = new ArrayList();
+
+ private CopyOnWriteArrayList> mPresenters =
+ new CopyOnWriteArrayList>();
+
+ /**
+ * Currently expanded menu item; must be collapsed when we clear.
+ */
+ private MenuItemImpl mExpandedItem;
+
+ /**
+ * Called by menu to notify of close and selection changes.
+ */
+ public interface Callback {
+ /**
+ * Called when a menu item is selected.
+ * @param menu The menu that is the parent of the item
+ * @param item The menu item that is selected
+ * @return whether the menu item selection was handled
+ */
+ public boolean onMenuItemSelected(MenuBuilder menu, MenuItem item);
+
+ /**
+ * Called when the mode of the menu changes (for example, from icon to expanded).
+ *
+ * @param menu the menu that has changed modes
+ */
+ public void onMenuModeChange(MenuBuilder menu);
+ }
+
+ /**
+ * Called by menu items to execute their associated action
+ */
+ public interface ItemInvoker {
+ public boolean invokeItem(MenuItemImpl item);
+ }
+
+ public MenuBuilder(Context context) {
+ mContext = context;
+ mResources = context.getResources();
+
+ mItems = new ArrayList();
+
+ mVisibleItems = new ArrayList();
+ mIsVisibleItemsStale = true;
+
+ mActionItems = new ArrayList();
+ mNonActionItems = new ArrayList();
+ mIsActionItemsStale = true;
+
+ setShortcutsVisibleInner(true);
+ }
+
+ public MenuBuilder setDefaultShowAsAction(int defaultShowAsAction) {
+ mDefaultShowAsAction = defaultShowAsAction;
+ return this;
+ }
+
+ /**
+ * Add a presenter to this menu. This will only hold a WeakReference;
+ * you do not need to explicitly remove a presenter, but you can using
+ * {@link #removeMenuPresenter(MenuPresenter)}.
+ *
+ * @param presenter The presenter to add
+ */
+ public void addMenuPresenter(MenuPresenter presenter) {
+ mPresenters.add(new WeakReference(presenter));
+ presenter.initForMenu(mContext, this);
+ mIsActionItemsStale = true;
+ }
+
+ /**
+ * Remove a presenter from this menu. That presenter will no longer
+ * receive notifications of updates to this menu's data.
+ *
+ * @param presenter The presenter to remove
+ */
+ public void removeMenuPresenter(MenuPresenter presenter) {
+ for (WeakReference ref : mPresenters) {
+ final MenuPresenter item = ref.get();
+ if (item == null || item == presenter) {
+ mPresenters.remove(ref);
+ }
+ }
+ }
+
+ private void dispatchPresenterUpdate(boolean cleared) {
+ if (mPresenters.isEmpty()) return;
+
+ stopDispatchingItemsChanged();
+ for (WeakReference ref : mPresenters) {
+ final MenuPresenter presenter = ref.get();
+ if (presenter == null) {
+ mPresenters.remove(ref);
+ } else {
+ presenter.updateMenuView(cleared);
+ }
+ }
+ startDispatchingItemsChanged();
+ }
+
+ private boolean dispatchSubMenuSelected(SubMenuBuilder subMenu) {
+ if (mPresenters.isEmpty()) return false;
+
+ boolean result = false;
+
+ for (WeakReference ref : mPresenters) {
+ final MenuPresenter presenter = ref.get();
+ if (presenter == null) {
+ mPresenters.remove(ref);
+ } else if (!result) {
+ result = presenter.onSubMenuSelected(subMenu);
+ }
+ }
+ return result;
+ }
+
+ private void dispatchSaveInstanceState(Bundle outState) {
+ if (mPresenters.isEmpty()) return;
+
+ SparseArray presenterStates = new SparseArray();
+
+ for (WeakReference ref : mPresenters) {
+ final MenuPresenter presenter = ref.get();
+ if (presenter == null) {
+ mPresenters.remove(ref);
+ } else {
+ final int id = presenter.getId();
+ if (id > 0) {
+ final Parcelable state = presenter.onSaveInstanceState();
+ if (state != null) {
+ presenterStates.put(id, state);
+ }
+ }
+ }
+ }
+
+ outState.putSparseParcelableArray(PRESENTER_KEY, presenterStates);
+ }
+
+ private void dispatchRestoreInstanceState(Bundle state) {
+ SparseArray presenterStates = state.getSparseParcelableArray(PRESENTER_KEY);
+
+ if (presenterStates == null || mPresenters.isEmpty()) return;
+
+ for (WeakReference ref : mPresenters) {
+ final MenuPresenter presenter = ref.get();
+ if (presenter == null) {
+ mPresenters.remove(ref);
+ } else {
+ final int id = presenter.getId();
+ if (id > 0) {
+ Parcelable parcel = presenterStates.get(id);
+ if (parcel != null) {
+ presenter.onRestoreInstanceState(parcel);
+ }
+ }
+ }
+ }
+ }
+
+ public void savePresenterStates(Bundle outState) {
+ dispatchSaveInstanceState(outState);
+ }
+
+ public void restorePresenterStates(Bundle state) {
+ dispatchRestoreInstanceState(state);
+ }
+
+ public void saveActionViewStates(Bundle outStates) {
+ SparseArray viewStates = null;
+
+ final int itemCount = size();
+ for (int i = 0; i < itemCount; i++) {
+ final MenuItem item = getItem(i);
+ final View v = item.getActionView();
+ if (v != null && v.getId() != View.NO_ID) {
+ if (viewStates == null) {
+ viewStates = new SparseArray();
+ }
+ v.saveHierarchyState(viewStates);
+ if (item.isActionViewExpanded()) {
+ outStates.putInt(EXPANDED_ACTION_VIEW_ID, item.getItemId());
+ }
+ }
+ if (item.hasSubMenu()) {
+ final SubMenuBuilder subMenu = (SubMenuBuilder) item.getSubMenu();
+ subMenu.saveActionViewStates(outStates);
+ }
+ }
+
+ if (viewStates != null) {
+ outStates.putSparseParcelableArray(getActionViewStatesKey(), viewStates);
+ }
+ }
+
+ public void restoreActionViewStates(Bundle states) {
+ if (states == null) {
+ return;
+ }
+
+ SparseArray viewStates = states.getSparseParcelableArray(
+ getActionViewStatesKey());
+
+ final int itemCount = size();
+ for (int i = 0; i < itemCount; i++) {
+ final MenuItem item = getItem(i);
+ final View v = item.getActionView();
+ if (v != null && v.getId() != View.NO_ID) {
+ v.restoreHierarchyState(viewStates);
+ }
+ if (item.hasSubMenu()) {
+ final SubMenuBuilder subMenu = (SubMenuBuilder) item.getSubMenu();
+ subMenu.restoreActionViewStates(states);
+ }
+ }
+
+ final int expandedId = states.getInt(EXPANDED_ACTION_VIEW_ID);
+ if (expandedId > 0) {
+ MenuItem itemToExpand = findItem(expandedId);
+ if (itemToExpand != null) {
+ itemToExpand.expandActionView();
+ }
+ }
+ }
+
+ protected String getActionViewStatesKey() {
+ return ACTION_VIEW_STATES_KEY;
+ }
+
+ public void setCallback(Callback cb) {
+ mCallback = cb;
+ }
+
+ /**
+ * Adds an item to the menu. The other add methods funnel to this.
+ */
+ private MenuItem addInternal(int group, int id, int categoryOrder, CharSequence title) {
+ final int ordering = getOrdering(categoryOrder);
+
+ final MenuItemImpl item = new MenuItemImpl(this, group, id, categoryOrder,
+ ordering, title, mDefaultShowAsAction);
+
+ if (mCurrentMenuInfo != null) {
+ // Pass along the current menu info
+ item.setMenuInfo(mCurrentMenuInfo);
+ }
+
+ mItems.add(findInsertIndex(mItems, ordering), item);
+ onItemsChanged(true);
+
+ return item;
+ }
+
+ public MenuItem add(CharSequence title) {
+ return addInternal(0, 0, 0, title);
+ }
+
+ public MenuItem add(int titleRes) {
+ return addInternal(0, 0, 0, mResources.getString(titleRes));
+ }
+
+ public MenuItem add(int group, int id, int categoryOrder, CharSequence title) {
+ return addInternal(group, id, categoryOrder, title);
+ }
+
+ public MenuItem add(int group, int id, int categoryOrder, int title) {
+ return addInternal(group, id, categoryOrder, mResources.getString(title));
+ }
+
+ public SubMenu addSubMenu(CharSequence title) {
+ return addSubMenu(0, 0, 0, title);
+ }
+
+ public SubMenu addSubMenu(int titleRes) {
+ return addSubMenu(0, 0, 0, mResources.getString(titleRes));
+ }
+
+ public SubMenu addSubMenu(int group, int id, int categoryOrder, CharSequence title) {
+ final MenuItemImpl item = (MenuItemImpl) addInternal(group, id, categoryOrder, title);
+ final SubMenuBuilder subMenu = new SubMenuBuilder(mContext, this, item);
+ item.setSubMenu(subMenu);
+
+ return subMenu;
+ }
+
+ public SubMenu addSubMenu(int group, int id, int categoryOrder, int title) {
+ return addSubMenu(group, id, categoryOrder, mResources.getString(title));
+ }
+
+ public int addIntentOptions(int group, int id, int categoryOrder, ComponentName caller,
+ Intent[] specifics, Intent intent, int flags, MenuItem[] outSpecificItems) {
+ PackageManager pm = mContext.getPackageManager();
+ final List lri =
+ pm.queryIntentActivityOptions(caller, specifics, intent, 0);
+ final int N = lri != null ? lri.size() : 0;
+
+ if ((flags & FLAG_APPEND_TO_GROUP) == 0) {
+ removeGroup(group);
+ }
+
+ for (int i=0; i= 0) {
+ outSpecificItems[ri.specificIndex] = item;
+ }
+ }
+
+ return N;
+ }
+
+ public void removeItem(int id) {
+ removeItemAtInt(findItemIndex(id), true);
+ }
+
+ public void removeGroup(int group) {
+ final int i = findGroupIndex(group);
+
+ if (i >= 0) {
+ final int maxRemovable = mItems.size() - i;
+ int numRemoved = 0;
+ while ((numRemoved++ < maxRemovable) && (mItems.get(i).getGroupId() == group)) {
+ // Don't force update for each one, this method will do it at the end
+ removeItemAtInt(i, false);
+ }
+
+ // Notify menu views
+ onItemsChanged(true);
+ }
+ }
+
+ /**
+ * Remove the item at the given index and optionally forces menu views to
+ * update.
+ *
+ * @param index The index of the item to be removed. If this index is
+ * invalid an exception is thrown.
+ * @param updateChildrenOnMenuViews Whether to force update on menu views.
+ * Please make sure you eventually call this after your batch of
+ * removals.
+ */
+ private void removeItemAtInt(int index, boolean updateChildrenOnMenuViews) {
+ if ((index < 0) || (index >= mItems.size())) return;
+
+ mItems.remove(index);
+
+ if (updateChildrenOnMenuViews) onItemsChanged(true);
+ }
+
+ public void removeItemAt(int index) {
+ removeItemAtInt(index, true);
+ }
+
+ public void clearAll() {
+ mPreventDispatchingItemsChanged = true;
+ clear();
+ clearHeader();
+ mPreventDispatchingItemsChanged = false;
+ mItemsChangedWhileDispatchPrevented = false;
+ onItemsChanged(true);
+ }
+
+ public void clear() {
+ if (mExpandedItem != null) {
+ collapseItemActionView(mExpandedItem);
+ }
+ mItems.clear();
+
+ onItemsChanged(true);
+ }
+
+ void setExclusiveItemChecked(MenuItem item) {
+ final int group = item.getGroupId();
+
+ final int N = mItems.size();
+ for (int i = 0; i < N; i++) {
+ MenuItemImpl curItem = mItems.get(i);
+ if (curItem.getGroupId() == group) {
+ if (!curItem.isExclusiveCheckable()) continue;
+ if (!curItem.isCheckable()) continue;
+
+ // Check the item meant to be checked, uncheck the others (that are in the group)
+ curItem.setCheckedInt(curItem == item);
+ }
+ }
+ }
+
+ public void setGroupCheckable(int group, boolean checkable, boolean exclusive) {
+ final int N = mItems.size();
+
+ for (int i = 0; i < N; i++) {
+ MenuItemImpl item = mItems.get(i);
+ if (item.getGroupId() == group) {
+ item.setExclusiveCheckable(exclusive);
+ item.setCheckable(checkable);
+ }
+ }
+ }
+
+ public void setGroupVisible(int group, boolean visible) {
+ final int N = mItems.size();
+
+ // We handle the notification of items being changed ourselves, so we use setVisibleInt rather
+ // than setVisible and at the end notify of items being changed
+
+ boolean changedAtLeastOneItem = false;
+ for (int i = 0; i < N; i++) {
+ MenuItemImpl item = mItems.get(i);
+ if (item.getGroupId() == group) {
+ if (item.setVisibleInt(visible)) changedAtLeastOneItem = true;
+ }
+ }
+
+ if (changedAtLeastOneItem) onItemsChanged(true);
+ }
+
+ public void setGroupEnabled(int group, boolean enabled) {
+ final int N = mItems.size();
+
+ for (int i = 0; i < N; i++) {
+ MenuItemImpl item = mItems.get(i);
+ if (item.getGroupId() == group) {
+ item.setEnabled(enabled);
+ }
+ }
+ }
+
+ public boolean hasVisibleItems() {
+ final int size = size();
+
+ for (int i = 0; i < size; i++) {
+ MenuItemImpl item = mItems.get(i);
+ if (item.isVisible()) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ public MenuItem findItem(int id) {
+ final int size = size();
+ for (int i = 0; i < size; i++) {
+ MenuItemImpl item = mItems.get(i);
+ if (item.getItemId() == id) {
+ return item;
+ } else if (item.hasSubMenu()) {
+ MenuItem possibleItem = item.getSubMenu().findItem(id);
+
+ if (possibleItem != null) {
+ return possibleItem;
+ }
+ }
+ }
+
+ return null;
+ }
+
+ public int findItemIndex(int id) {
+ final int size = size();
+
+ for (int i = 0; i < size; i++) {
+ MenuItemImpl item = mItems.get(i);
+ if (item.getItemId() == id) {
+ return i;
+ }
+ }
+
+ return -1;
+ }
+
+ public int findGroupIndex(int group) {
+ return findGroupIndex(group, 0);
+ }
+
+ public int findGroupIndex(int group, int start) {
+ final int size = size();
+
+ if (start < 0) {
+ start = 0;
+ }
+
+ for (int i = start; i < size; i++) {
+ final MenuItemImpl item = mItems.get(i);
+
+ if (item.getGroupId() == group) {
+ return i;
+ }
+ }
+
+ return -1;
+ }
+
+ public int size() {
+ return mItems.size();
+ }
+
+ /** {@inheritDoc} */
+ public MenuItem getItem(int index) {
+ return mItems.get(index);
+ }
+
+ public boolean isShortcutKey(int keyCode, KeyEvent event) {
+ return findItemWithShortcutForKey(keyCode, event) != null;
+ }
+
+ public void setQwertyMode(boolean isQwerty) {
+ mQwertyMode = isQwerty;
+
+ onItemsChanged(false);
+ }
+
+ /**
+ * Returns the ordering across all items. This will grab the category from
+ * the upper bits, find out how to order the category with respect to other
+ * categories, and combine it with the lower bits.
+ *
+ * @param categoryOrder The category order for a particular item (if it has
+ * not been or/add with a category, the default category is
+ * assumed).
+ * @return An ordering integer that can be used to order this item across
+ * all the items (even from other categories).
+ */
+ private static int getOrdering(int categoryOrder) {
+ final int index = (categoryOrder & CATEGORY_MASK) >> CATEGORY_SHIFT;
+
+ if (index < 0 || index >= sCategoryToOrder.length) {
+ throw new IllegalArgumentException("order does not contain a valid category.");
+ }
+
+ return (sCategoryToOrder[index] << CATEGORY_SHIFT) | (categoryOrder & USER_MASK);
+ }
+
+ /**
+ * @return whether the menu shortcuts are in qwerty mode or not
+ */
+ boolean isQwertyMode() {
+ return mQwertyMode;
+ }
+
+ /**
+ * Sets whether the shortcuts should be visible on menus. Devices without hardware
+ * key input will never make shortcuts visible even if this method is passed 'true'.
+ *
+ * @param shortcutsVisible Whether shortcuts should be visible (if true and a
+ * menu item does not have a shortcut defined, that item will
+ * still NOT show a shortcut)
+ */
+ public void setShortcutsVisible(boolean shortcutsVisible) {
+ if (mShortcutsVisible == shortcutsVisible) return;
+
+ setShortcutsVisibleInner(shortcutsVisible);
+ onItemsChanged(false);
+ }
+
+ private void setShortcutsVisibleInner(boolean shortcutsVisible) {
+ mShortcutsVisible = shortcutsVisible
+ && mResources.getConfiguration().keyboard != Configuration.KEYBOARD_NOKEYS
+ && mResources.getBoolean(
+ R.bool.abs__config_showMenuShortcutsWhenKeyboardPresent);
+ }
+
+ /**
+ * @return Whether shortcuts should be visible on menus.
+ */
+ public boolean isShortcutsVisible() {
+ return mShortcutsVisible;
+ }
+
+ Resources getResources() {
+ return mResources;
+ }
+
+ public Context getContext() {
+ return mContext;
+ }
+
+ boolean dispatchMenuItemSelected(MenuBuilder menu, MenuItem item) {
+ return mCallback != null && mCallback.onMenuItemSelected(menu, item);
+ }
+
+ /**
+ * Dispatch a mode change event to this menu's callback.
+ */
+ public void changeMenuMode() {
+ if (mCallback != null) {
+ mCallback.onMenuModeChange(this);
+ }
+ }
+
+ private static int findInsertIndex(ArrayList items, int ordering) {
+ for (int i = items.size() - 1; i >= 0; i--) {
+ MenuItemImpl item = items.get(i);
+ if (item.getOrdering() <= ordering) {
+ return i + 1;
+ }
+ }
+
+ return 0;
+ }
+
+ public boolean performShortcut(int keyCode, KeyEvent event, int flags) {
+ final MenuItemImpl item = findItemWithShortcutForKey(keyCode, event);
+
+ boolean handled = false;
+
+ if (item != null) {
+ handled = performItemAction(item, flags);
+ }
+
+ if ((flags & FLAG_ALWAYS_PERFORM_CLOSE) != 0) {
+ close(true);
+ }
+
+ return handled;
+ }
+
+ /*
+ * This function will return all the menu and sub-menu items that can
+ * be directly (the shortcut directly corresponds) and indirectly
+ * (the ALT-enabled char corresponds to the shortcut) associated
+ * with the keyCode.
+ */
+ @SuppressWarnings("deprecation")
+ void findItemsWithShortcutForKey(List items, int keyCode, KeyEvent event) {
+ final boolean qwerty = isQwertyMode();
+ final int metaState = event.getMetaState();
+ final KeyCharacterMap.KeyData possibleChars = new KeyCharacterMap.KeyData();
+ // Get the chars associated with the keyCode (i.e using any chording combo)
+ final boolean isKeyCodeMapped = event.getKeyData(possibleChars);
+ // The delete key is not mapped to '\b' so we treat it specially
+ if (!isKeyCodeMapped && (keyCode != KeyEvent.KEYCODE_DEL)) {
+ return;
+ }
+
+ // Look for an item whose shortcut is this key.
+ final int N = mItems.size();
+ for (int i = 0; i < N; i++) {
+ MenuItemImpl item = mItems.get(i);
+ if (item.hasSubMenu()) {
+ ((MenuBuilder)item.getSubMenu()).findItemsWithShortcutForKey(items, keyCode, event);
+ }
+ final char shortcutChar = qwerty ? item.getAlphabeticShortcut() : item.getNumericShortcut();
+ if (((metaState & (KeyEvent.META_SHIFT_ON | KeyEvent.META_SYM_ON)) == 0) &&
+ (shortcutChar != 0) &&
+ (shortcutChar == possibleChars.meta[0]
+ || shortcutChar == possibleChars.meta[2]
+ || (qwerty && shortcutChar == '\b' &&
+ keyCode == KeyEvent.KEYCODE_DEL)) &&
+ item.isEnabled()) {
+ items.add(item);
+ }
+ }
+ }
+
+ /*
+ * We want to return the menu item associated with the key, but if there is no
+ * ambiguity (i.e. there is only one menu item corresponding to the key) we want
+ * to return it even if it's not an exact match; this allow the user to
+ * _not_ use the ALT key for example, making the use of shortcuts slightly more
+ * user-friendly. An example is on the G1, '!' and '1' are on the same key, and
+ * in Gmail, Menu+1 will trigger Menu+! (the actual shortcut).
+ *
+ * On the other hand, if two (or more) shortcuts corresponds to the same key,
+ * we have to only return the exact match.
+ */
+ @SuppressWarnings("deprecation")
+ MenuItemImpl findItemWithShortcutForKey(int keyCode, KeyEvent event) {
+ // Get all items that can be associated directly or indirectly with the keyCode
+ ArrayList items = mTempShortcutItemList;
+ items.clear();
+ findItemsWithShortcutForKey(items, keyCode, event);
+
+ if (items.isEmpty()) {
+ return null;
+ }
+
+ final int metaState = event.getMetaState();
+ final KeyCharacterMap.KeyData possibleChars = new KeyCharacterMap.KeyData();
+ // Get the chars associated with the keyCode (i.e using any chording combo)
+ event.getKeyData(possibleChars);
+
+ // If we have only one element, we can safely returns it
+ final int size = items.size();
+ if (size == 1) {
+ return items.get(0);
+ }
+
+ final boolean qwerty = isQwertyMode();
+ // If we found more than one item associated with the key,
+ // we have to return the exact match
+ for (int i = 0; i < size; i++) {
+ final MenuItemImpl item = items.get(i);
+ final char shortcutChar = qwerty ? item.getAlphabeticShortcut() :
+ item.getNumericShortcut();
+ if ((shortcutChar == possibleChars.meta[0] &&
+ (metaState & KeyEvent.META_ALT_ON) == 0)
+ || (shortcutChar == possibleChars.meta[2] &&
+ (metaState & KeyEvent.META_ALT_ON) != 0)
+ || (qwerty && shortcutChar == '\b' &&
+ keyCode == KeyEvent.KEYCODE_DEL)) {
+ return item;
+ }
+ }
+ return null;
+ }
+
+ public boolean performIdentifierAction(int id, int flags) {
+ // Look for an item whose identifier is the id.
+ return performItemAction(findItem(id), flags);
+ }
+
+ public boolean performItemAction(MenuItem item, int flags) {
+ MenuItemImpl itemImpl = (MenuItemImpl) item;
+
+ if (itemImpl == null || !itemImpl.isEnabled()) {
+ return false;
+ }
+
+ boolean invoked = itemImpl.invoke();
+
+ if (itemImpl.hasCollapsibleActionView()) {
+ invoked |= itemImpl.expandActionView();
+ if (invoked) close(true);
+ } else if (item.hasSubMenu()) {
+ close(false);
+
+ final SubMenuBuilder subMenu = (SubMenuBuilder) item.getSubMenu();
+ final ActionProvider provider = item.getActionProvider();
+ if (provider != null && provider.hasSubMenu()) {
+ provider.onPrepareSubMenu(subMenu);
+ }
+ invoked |= dispatchSubMenuSelected(subMenu);
+ if (!invoked) close(true);
+ } else {
+ if ((flags & FLAG_PERFORM_NO_CLOSE) == 0) {
+ close(true);
+ }
+ }
+
+ return invoked;
+ }
+
+ /**
+ * Closes the visible menu.
+ *
+ * @param allMenusAreClosing Whether the menus are completely closing (true),
+ * or whether there is another menu coming in this menu's place
+ * (false). For example, if the menu is closing because a
+ * sub menu is about to be shown, allMenusAreClosing
+ * is false.
+ */
+ final void close(boolean allMenusAreClosing) {
+ if (mIsClosing) return;
+
+ mIsClosing = true;
+ for (WeakReference ref : mPresenters) {
+ final MenuPresenter presenter = ref.get();
+ if (presenter == null) {
+ mPresenters.remove(ref);
+ } else {
+ presenter.onCloseMenu(this, allMenusAreClosing);
+ }
+ }
+ mIsClosing = false;
+ }
+
+ /** {@inheritDoc} */
+ public void close() {
+ close(true);
+ }
+
+ /**
+ * Called when an item is added or removed.
+ *
+ * @param structureChanged true if the menu structure changed,
+ * false if only item properties changed.
+ * (Visibility is a structural property since it affects layout.)
+ */
+ void onItemsChanged(boolean structureChanged) {
+ if (!mPreventDispatchingItemsChanged) {
+ if (structureChanged) {
+ mIsVisibleItemsStale = true;
+ mIsActionItemsStale = true;
+ }
+
+ dispatchPresenterUpdate(structureChanged);
+ } else {
+ mItemsChangedWhileDispatchPrevented = true;
+ }
+ }
+
+ /**
+ * Stop dispatching item changed events to presenters until
+ * {@link #startDispatchingItemsChanged()} is called. Useful when
+ * many menu operations are going to be performed as a batch.
+ */
+ public void stopDispatchingItemsChanged() {
+ if (!mPreventDispatchingItemsChanged) {
+ mPreventDispatchingItemsChanged = true;
+ mItemsChangedWhileDispatchPrevented = false;
+ }
+ }
+
+ public void startDispatchingItemsChanged() {
+ mPreventDispatchingItemsChanged = false;
+
+ if (mItemsChangedWhileDispatchPrevented) {
+ mItemsChangedWhileDispatchPrevented = false;
+ onItemsChanged(true);
+ }
+ }
+
+ /**
+ * Called by {@link MenuItemImpl} when its visible flag is changed.
+ * @param item The item that has gone through a visibility change.
+ */
+ void onItemVisibleChanged(MenuItemImpl item) {
+ // Notify of items being changed
+ mIsVisibleItemsStale = true;
+ onItemsChanged(true);
+ }
+
+ /**
+ * Called by {@link MenuItemImpl} when its action request status is changed.
+ * @param item The item that has gone through a change in action request status.
+ */
+ void onItemActionRequestChanged(MenuItemImpl item) {
+ // Notify of items being changed
+ mIsActionItemsStale = true;
+ onItemsChanged(true);
+ }
+
+ ArrayList getVisibleItems() {
+ if (!mIsVisibleItemsStale) return mVisibleItems;
+
+ // Refresh the visible items
+ mVisibleItems.clear();
+
+ final int itemsSize = mItems.size();
+ MenuItemImpl item;
+ for (int i = 0; i < itemsSize; i++) {
+ item = mItems.get(i);
+ if (item.isVisible()) mVisibleItems.add(item);
+ }
+
+ mIsVisibleItemsStale = false;
+ mIsActionItemsStale = true;
+
+ return mVisibleItems;
+ }
+
+ /**
+ * This method determines which menu items get to be 'action items' that will appear
+ * in an action bar and which items should be 'overflow items' in a secondary menu.
+ * The rules are as follows:
+ *
+ * Items are considered for inclusion in the order specified within the menu.
+ * There is a limit of mMaxActionItems as a total count, optionally including the overflow
+ * menu button itself. This is a soft limit; if an item shares a group ID with an item
+ * previously included as an action item, the new item will stay with its group and become
+ * an action item itself even if it breaks the max item count limit. This is done to
+ * limit the conceptual complexity of the items presented within an action bar. Only a few
+ * unrelated concepts should be presented to the user in this space, and groups are treated
+ * as a single concept.
+ *
+ *
There is also a hard limit of consumed measurable space: mActionWidthLimit. This
+ * limit may be broken by a single item that exceeds the remaining space, but no further
+ * items may be added. If an item that is part of a group cannot fit within the remaining
+ * measured width, the entire group will be demoted to overflow. This is done to ensure room
+ * for navigation and other affordances in the action bar as well as reduce general UI clutter.
+ *
+ *
The space freed by demoting a full group cannot be consumed by future menu items.
+ * Once items begin to overflow, all future items become overflow items as well. This is
+ * to avoid inadvertent reordering that may break the app's intended design.
+ */
+ public void flagActionItems() {
+ if (!mIsActionItemsStale) {
+ return;
+ }
+
+ // Presenters flag action items as needed.
+ boolean flagged = false;
+ for (WeakReference ref : mPresenters) {
+ final MenuPresenter presenter = ref.get();
+ if (presenter == null) {
+ mPresenters.remove(ref);
+ } else {
+ flagged |= presenter.flagActionItems();
+ }
+ }
+
+ if (flagged) {
+ mActionItems.clear();
+ mNonActionItems.clear();
+ ArrayList visibleItems = getVisibleItems();
+ final int itemsSize = visibleItems.size();
+ for (int i = 0; i < itemsSize; i++) {
+ MenuItemImpl item = visibleItems.get(i);
+ if (item.isActionButton()) {
+ mActionItems.add(item);
+ } else {
+ mNonActionItems.add(item);
+ }
+ }
+ } else {
+ // Nobody flagged anything, everything is a non-action item.
+ // (This happens during a first pass with no action-item presenters.)
+ mActionItems.clear();
+ mNonActionItems.clear();
+ mNonActionItems.addAll(getVisibleItems());
+ }
+ mIsActionItemsStale = false;
+ }
+
+ ArrayList getActionItems() {
+ flagActionItems();
+ return mActionItems;
+ }
+
+ ArrayList getNonActionItems() {
+ flagActionItems();
+ return mNonActionItems;
+ }
+
+ public void clearHeader() {
+ mHeaderIcon = null;
+ mHeaderTitle = null;
+ mHeaderView = null;
+
+ onItemsChanged(false);
+ }
+
+ private void setHeaderInternal(final int titleRes, final CharSequence title, final int iconRes,
+ final Drawable icon, final View view) {
+ final Resources r = getResources();
+
+ if (view != null) {
+ mHeaderView = view;
+
+ // If using a custom view, then the title and icon aren't used
+ mHeaderTitle = null;
+ mHeaderIcon = null;
+ } else {
+ if (titleRes > 0) {
+ mHeaderTitle = r.getText(titleRes);
+ } else if (title != null) {
+ mHeaderTitle = title;
+ }
+
+ if (iconRes > 0) {
+ mHeaderIcon = r.getDrawable(iconRes);
+ } else if (icon != null) {
+ mHeaderIcon = icon;
+ }
+
+ // If using the title or icon, then a custom view isn't used
+ mHeaderView = null;
+ }
+
+ // Notify of change
+ onItemsChanged(false);
+ }
+
+ /**
+ * Sets the header's title. This replaces the header view. Called by the
+ * builder-style methods of subclasses.
+ *
+ * @param title The new title.
+ * @return This MenuBuilder so additional setters can be called.
+ */
+ protected MenuBuilder setHeaderTitleInt(CharSequence title) {
+ setHeaderInternal(0, title, 0, null, null);
+ return this;
+ }
+
+ /**
+ * Sets the header's title. This replaces the header view. Called by the
+ * builder-style methods of subclasses.
+ *
+ * @param titleRes The new title (as a resource ID).
+ * @return This MenuBuilder so additional setters can be called.
+ */
+ protected MenuBuilder setHeaderTitleInt(int titleRes) {
+ setHeaderInternal(titleRes, null, 0, null, null);
+ return this;
+ }
+
+ /**
+ * Sets the header's icon. This replaces the header view. Called by the
+ * builder-style methods of subclasses.
+ *
+ * @param icon The new icon.
+ * @return This MenuBuilder so additional setters can be called.
+ */
+ protected MenuBuilder setHeaderIconInt(Drawable icon) {
+ setHeaderInternal(0, null, 0, icon, null);
+ return this;
+ }
+
+ /**
+ * Sets the header's icon. This replaces the header view. Called by the
+ * builder-style methods of subclasses.
+ *
+ * @param iconRes The new icon (as a resource ID).
+ * @return This MenuBuilder so additional setters can be called.
+ */
+ protected MenuBuilder setHeaderIconInt(int iconRes) {
+ setHeaderInternal(0, null, iconRes, null, null);
+ return this;
+ }
+
+ /**
+ * Sets the header's view. This replaces the title and icon. Called by the
+ * builder-style methods of subclasses.
+ *
+ * @param view The new view.
+ * @return This MenuBuilder so additional setters can be called.
+ */
+ protected MenuBuilder setHeaderViewInt(View view) {
+ setHeaderInternal(0, null, 0, null, view);
+ return this;
+ }
+
+ public CharSequence getHeaderTitle() {
+ return mHeaderTitle;
+ }
+
+ public Drawable getHeaderIcon() {
+ return mHeaderIcon;
+ }
+
+ public View getHeaderView() {
+ return mHeaderView;
+ }
+
+ /**
+ * Gets the root menu (if this is a submenu, find its root menu).
+ * @return The root menu.
+ */
+ public MenuBuilder getRootMenu() {
+ return this;
+ }
+
+ /**
+ * Sets the current menu info that is set on all items added to this menu
+ * (until this is called again with different menu info, in which case that
+ * one will be added to all subsequent item additions).
+ *
+ * @param menuInfo The extra menu information to add.
+ */
+ public void setCurrentMenuInfo(ContextMenuInfo menuInfo) {
+ mCurrentMenuInfo = menuInfo;
+ }
+
+ void setOptionalIconsVisible(boolean visible) {
+ mOptionalIconsVisible = visible;
+ }
+
+ boolean getOptionalIconsVisible() {
+ return mOptionalIconsVisible;
+ }
+
+ public boolean expandItemActionView(MenuItemImpl item) {
+ if (mPresenters.isEmpty()) return false;
+
+ boolean expanded = false;
+
+ stopDispatchingItemsChanged();
+ for (WeakReference ref : mPresenters) {
+ final MenuPresenter presenter = ref.get();
+ if (presenter == null) {
+ mPresenters.remove(ref);
+ } else if ((expanded = presenter.expandItemActionView(this, item))) {
+ break;
+ }
+ }
+ startDispatchingItemsChanged();
+
+ if (expanded) {
+ mExpandedItem = item;
+ }
+ return expanded;
+ }
+
+ public boolean collapseItemActionView(MenuItemImpl item) {
+ if (mPresenters.isEmpty() || mExpandedItem != item) return false;
+
+ boolean collapsed = false;
+
+ stopDispatchingItemsChanged();
+ for (WeakReference ref : mPresenters) {
+ final MenuPresenter presenter = ref.get();
+ if (presenter == null) {
+ mPresenters.remove(ref);
+ } else if ((collapsed = presenter.collapseItemActionView(this, item))) {
+ break;
+ }
+ }
+ startDispatchingItemsChanged();
+
+ if (collapsed) {
+ mExpandedItem = null;
+ }
+ return collapsed;
+ }
+
+ public MenuItemImpl getExpandedItem() {
+ return mExpandedItem;
+ }
+
+ public boolean bindNativeOverflow(android.view.Menu menu, android.view.MenuItem.OnMenuItemClickListener listener, HashMap map) {
+ final List nonActionItems = getNonActionItems();
+ if (nonActionItems == null || nonActionItems.size() == 0) {
+ return false;
+ }
+
+ boolean visible = false;
+ menu.clear();
+ for (MenuItemImpl nonActionItem : nonActionItems) {
+ if (!nonActionItem.isVisible()) {
+ continue;
+ }
+ visible = true;
+
+ android.view.MenuItem nativeItem;
+ if (nonActionItem.hasSubMenu()) {
+ android.view.SubMenu nativeSub = menu.addSubMenu(nonActionItem.getGroupId(), nonActionItem.getItemId(),
+ nonActionItem.getOrder(), nonActionItem.getTitle());
+
+ SubMenuBuilder subMenu = (SubMenuBuilder)nonActionItem.getSubMenu();
+ for (MenuItemImpl subItem : subMenu.getVisibleItems()) {
+ android.view.MenuItem nativeSubItem = nativeSub.add(subItem.getGroupId(), subItem.getItemId(),
+ subItem.getOrder(), subItem.getTitle());
+
+ nativeSubItem.setIcon(subItem.getIcon());
+ nativeSubItem.setOnMenuItemClickListener(listener);
+ nativeSubItem.setEnabled(subItem.isEnabled());
+ nativeSubItem.setIntent(subItem.getIntent());
+ nativeSubItem.setNumericShortcut(subItem.getNumericShortcut());
+ nativeSubItem.setAlphabeticShortcut(subItem.getAlphabeticShortcut());
+ nativeSubItem.setTitleCondensed(subItem.getTitleCondensed());
+ nativeSubItem.setCheckable(subItem.isCheckable());
+ nativeSubItem.setChecked(subItem.isChecked());
+
+ if (subItem.isExclusiveCheckable()) {
+ nativeSub.setGroupCheckable(subItem.getGroupId(), true, true);
+ }
+
+ map.put(nativeSubItem, subItem);
+ }
+
+ nativeItem = nativeSub.getItem();
+ } else {
+ nativeItem = menu.add(nonActionItem.getGroupId(), nonActionItem.getItemId(),
+ nonActionItem.getOrder(), nonActionItem.getTitle());
+ }
+ nativeItem.setIcon(nonActionItem.getIcon());
+ nativeItem.setOnMenuItemClickListener(listener);
+ nativeItem.setEnabled(nonActionItem.isEnabled());
+ nativeItem.setIntent(nonActionItem.getIntent());
+ nativeItem.setNumericShortcut(nonActionItem.getNumericShortcut());
+ nativeItem.setAlphabeticShortcut(nonActionItem.getAlphabeticShortcut());
+ nativeItem.setTitleCondensed(nonActionItem.getTitleCondensed());
+ nativeItem.setCheckable(nonActionItem.isCheckable());
+ nativeItem.setChecked(nonActionItem.isChecked());
+
+ if (nonActionItem.isExclusiveCheckable()) {
+ menu.setGroupCheckable(nonActionItem.getGroupId(), true, true);
+ }
+
+ map.put(nativeItem, nonActionItem);
+ }
+ return visible;
+ }
+}
diff --git a/android-app/gen-external-apklibs/com.actionbarsherlock_actionbarsherlock_4.2.0/src/com/actionbarsherlock/internal/view/menu/MenuItemImpl.java b/android-app/gen-external-apklibs/com.actionbarsherlock_actionbarsherlock_4.2.0/src/com/actionbarsherlock/internal/view/menu/MenuItemImpl.java
new file mode 100644
index 00000000..f5359fb4
--- /dev/null
+++ b/android-app/gen-external-apklibs/com.actionbarsherlock_actionbarsherlock_4.2.0/src/com/actionbarsherlock/internal/view/menu/MenuItemImpl.java
@@ -0,0 +1,647 @@
+/*
+ * Copyright (C) 2006 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.actionbarsherlock.internal.view.menu;
+
+import android.content.ActivityNotFoundException;
+import android.content.Context;
+import android.content.Intent;
+import android.graphics.drawable.Drawable;
+import android.util.Log;
+import android.view.ContextMenu.ContextMenuInfo;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewDebug;
+import android.widget.LinearLayout;
+
+import com.actionbarsherlock.view.ActionProvider;
+import com.actionbarsherlock.view.MenuItem;
+import com.actionbarsherlock.view.SubMenu;
+
+/**
+ * @hide
+ */
+public final class MenuItemImpl implements MenuItem {
+ private static final String TAG = "MenuItemImpl";
+
+ private static final int SHOW_AS_ACTION_MASK = SHOW_AS_ACTION_NEVER |
+ SHOW_AS_ACTION_IF_ROOM |
+ SHOW_AS_ACTION_ALWAYS;
+
+ private final int mId;
+ private final int mGroup;
+ private final int mCategoryOrder;
+ private final int mOrdering;
+ private CharSequence mTitle;
+ private CharSequence mTitleCondensed;
+ private Intent mIntent;
+ private char mShortcutNumericChar;
+ private char mShortcutAlphabeticChar;
+
+ /** The icon's drawable which is only created as needed */
+ private Drawable mIconDrawable;
+ /**
+ * The icon's resource ID which is used to get the Drawable when it is
+ * needed (if the Drawable isn't already obtained--only one of the two is
+ * needed).
+ */
+ private int mIconResId = NO_ICON;
+
+ /** The menu to which this item belongs */
+ private MenuBuilder mMenu;
+ /** If this item should launch a sub menu, this is the sub menu to launch */
+ private SubMenuBuilder mSubMenu;
+
+ private Runnable mItemCallback;
+ private MenuItem.OnMenuItemClickListener mClickListener;
+
+ private int mFlags = ENABLED;
+ private static final int CHECKABLE = 0x00000001;
+ private static final int CHECKED = 0x00000002;
+ private static final int EXCLUSIVE = 0x00000004;
+ private static final int HIDDEN = 0x00000008;
+ private static final int ENABLED = 0x00000010;
+ private static final int IS_ACTION = 0x00000020;
+
+ private int mShowAsAction = SHOW_AS_ACTION_NEVER;
+
+ private View mActionView;
+ private ActionProvider mActionProvider;
+ private OnActionExpandListener mOnActionExpandListener;
+ private boolean mIsActionViewExpanded = false;
+
+ /** Used for the icon resource ID if this item does not have an icon */
+ static final int NO_ICON = 0;
+
+ /**
+ * Current use case is for context menu: Extra information linked to the
+ * View that added this item to the context menu.
+ */
+ private ContextMenuInfo mMenuInfo;
+
+ private static String sPrependShortcutLabel;
+ private static String sEnterShortcutLabel;
+ private static String sDeleteShortcutLabel;
+ private static String sSpaceShortcutLabel;
+
+
+ /**
+ * Instantiates this menu item.
+ *
+ * @param menu
+ * @param group Item ordering grouping control. The item will be added after
+ * all other items whose order is <= this number, and before any
+ * that are larger than it. This can also be used to define
+ * groups of items for batch state changes. Normally use 0.
+ * @param id Unique item ID. Use 0 if you do not need a unique ID.
+ * @param categoryOrder The ordering for this item.
+ * @param title The text to display for the item.
+ */
+ MenuItemImpl(MenuBuilder menu, int group, int id, int categoryOrder, int ordering,
+ CharSequence title, int showAsAction) {
+
+ /* TODO if (sPrependShortcutLabel == null) {
+ // This is instantiated from the UI thread, so no chance of sync issues
+ sPrependShortcutLabel = menu.getContext().getResources().getString(
+ com.android.internal.R.string.prepend_shortcut_label);
+ sEnterShortcutLabel = menu.getContext().getResources().getString(
+ com.android.internal.R.string.menu_enter_shortcut_label);
+ sDeleteShortcutLabel = menu.getContext().getResources().getString(
+ com.android.internal.R.string.menu_delete_shortcut_label);
+ sSpaceShortcutLabel = menu.getContext().getResources().getString(
+ com.android.internal.R.string.menu_space_shortcut_label);
+ }*/
+
+ mMenu = menu;
+ mId = id;
+ mGroup = group;
+ mCategoryOrder = categoryOrder;
+ mOrdering = ordering;
+ mTitle = title;
+ mShowAsAction = showAsAction;
+ }
+
+ /**
+ * Invokes the item by calling various listeners or callbacks.
+ *
+ * @return true if the invocation was handled, false otherwise
+ */
+ public boolean invoke() {
+ if (mClickListener != null &&
+ mClickListener.onMenuItemClick(this)) {
+ return true;
+ }
+
+ if (mMenu.dispatchMenuItemSelected(mMenu.getRootMenu(), this)) {
+ return true;
+ }
+
+ if (mItemCallback != null) {
+ mItemCallback.run();
+ return true;
+ }
+
+ if (mIntent != null) {
+ try {
+ mMenu.getContext().startActivity(mIntent);
+ return true;
+ } catch (ActivityNotFoundException e) {
+ Log.e(TAG, "Can't find activity to handle intent; ignoring", e);
+ }
+ }
+
+ if (mActionProvider != null && mActionProvider.onPerformDefaultAction()) {
+ return true;
+ }
+
+ return false;
+ }
+
+ public boolean isEnabled() {
+ return (mFlags & ENABLED) != 0;
+ }
+
+ public MenuItem setEnabled(boolean enabled) {
+ if (enabled) {
+ mFlags |= ENABLED;
+ } else {
+ mFlags &= ~ENABLED;
+ }
+
+ mMenu.onItemsChanged(false);
+
+ return this;
+ }
+
+ public int getGroupId() {
+ return mGroup;
+ }
+
+ @ViewDebug.CapturedViewProperty
+ public int getItemId() {
+ return mId;
+ }
+
+ public int getOrder() {
+ return mCategoryOrder;
+ }
+
+ public int getOrdering() {
+ return mOrdering;
+ }
+
+ public Intent getIntent() {
+ return mIntent;
+ }
+
+ public MenuItem setIntent(Intent intent) {
+ mIntent = intent;
+ return this;
+ }
+
+ Runnable getCallback() {
+ return mItemCallback;
+ }
+
+ public MenuItem setCallback(Runnable callback) {
+ mItemCallback = callback;
+ return this;
+ }
+
+ public char getAlphabeticShortcut() {
+ return mShortcutAlphabeticChar;
+ }
+
+ public MenuItem setAlphabeticShortcut(char alphaChar) {
+ if (mShortcutAlphabeticChar == alphaChar) return this;
+
+ mShortcutAlphabeticChar = Character.toLowerCase(alphaChar);
+
+ mMenu.onItemsChanged(false);
+
+ return this;
+ }
+
+ public char getNumericShortcut() {
+ return mShortcutNumericChar;
+ }
+
+ public MenuItem setNumericShortcut(char numericChar) {
+ if (mShortcutNumericChar == numericChar) return this;
+
+ mShortcutNumericChar = numericChar;
+
+ mMenu.onItemsChanged(false);
+
+ return this;
+ }
+
+ public MenuItem setShortcut(char numericChar, char alphaChar) {
+ mShortcutNumericChar = numericChar;
+ mShortcutAlphabeticChar = Character.toLowerCase(alphaChar);
+
+ mMenu.onItemsChanged(false);
+
+ return this;
+ }
+
+ /**
+ * @return The active shortcut (based on QWERTY-mode of the menu).
+ */
+ char getShortcut() {
+ return (mMenu.isQwertyMode() ? mShortcutAlphabeticChar : mShortcutNumericChar);
+ }
+
+ /**
+ * @return The label to show for the shortcut. This includes the chording
+ * key (for example 'Menu+a'). Also, any non-human readable
+ * characters should be human readable (for example 'Menu+enter').
+ */
+ String getShortcutLabel() {
+
+ char shortcut = getShortcut();
+ if (shortcut == 0) {
+ return "";
+ }
+
+ StringBuilder sb = new StringBuilder(sPrependShortcutLabel);
+ switch (shortcut) {
+
+ case '\n':
+ sb.append(sEnterShortcutLabel);
+ break;
+
+ case '\b':
+ sb.append(sDeleteShortcutLabel);
+ break;
+
+ case ' ':
+ sb.append(sSpaceShortcutLabel);
+ break;
+
+ default:
+ sb.append(shortcut);
+ break;
+ }
+
+ return sb.toString();
+ }
+
+ /**
+ * @return Whether this menu item should be showing shortcuts (depends on
+ * whether the menu should show shortcuts and whether this item has
+ * a shortcut defined)
+ */
+ boolean shouldShowShortcut() {
+ // Show shortcuts if the menu is supposed to show shortcuts AND this item has a shortcut
+ return mMenu.isShortcutsVisible() && (getShortcut() != 0);
+ }
+
+ public SubMenu getSubMenu() {
+ return mSubMenu;
+ }
+
+ public boolean hasSubMenu() {
+ return mSubMenu != null;
+ }
+
+ void setSubMenu(SubMenuBuilder subMenu) {
+ mSubMenu = subMenu;
+
+ subMenu.setHeaderTitle(getTitle());
+ }
+
+ @ViewDebug.CapturedViewProperty
+ public CharSequence getTitle() {
+ return mTitle;
+ }
+
+ /**
+ * Gets the title for a particular {@link ItemView}
+ *
+ * @param itemView The ItemView that is receiving the title
+ * @return Either the title or condensed title based on what the ItemView
+ * prefers
+ */
+ CharSequence getTitleForItemView(MenuView.ItemView itemView) {
+ return ((itemView != null) && itemView.prefersCondensedTitle())
+ ? getTitleCondensed()
+ : getTitle();
+ }
+
+ public MenuItem setTitle(CharSequence title) {
+ mTitle = title;
+
+ mMenu.onItemsChanged(false);
+
+ if (mSubMenu != null) {
+ mSubMenu.setHeaderTitle(title);
+ }
+
+ return this;
+ }
+
+ public MenuItem setTitle(int title) {
+ return setTitle(mMenu.getContext().getString(title));
+ }
+
+ public CharSequence getTitleCondensed() {
+ return mTitleCondensed != null ? mTitleCondensed : mTitle;
+ }
+
+ public MenuItem setTitleCondensed(CharSequence title) {
+ mTitleCondensed = title;
+
+ // Could use getTitle() in the loop below, but just cache what it would do here
+ if (title == null) {
+ title = mTitle;
+ }
+
+ mMenu.onItemsChanged(false);
+
+ return this;
+ }
+
+ public Drawable getIcon() {
+ if (mIconDrawable != null) {
+ return mIconDrawable;
+ }
+
+ if (mIconResId != NO_ICON) {
+ return mMenu.getResources().getDrawable(mIconResId);
+ }
+
+ return null;
+ }
+
+ public MenuItem setIcon(Drawable icon) {
+ mIconResId = NO_ICON;
+ mIconDrawable = icon;
+ mMenu.onItemsChanged(false);
+
+ return this;
+ }
+
+ public MenuItem setIcon(int iconResId) {
+ mIconDrawable = null;
+ mIconResId = iconResId;
+
+ // If we have a view, we need to push the Drawable to them
+ mMenu.onItemsChanged(false);
+
+ return this;
+ }
+
+ public boolean isCheckable() {
+ return (mFlags & CHECKABLE) == CHECKABLE;
+ }
+
+ public MenuItem setCheckable(boolean checkable) {
+ final int oldFlags = mFlags;
+ mFlags = (mFlags & ~CHECKABLE) | (checkable ? CHECKABLE : 0);
+ if (oldFlags != mFlags) {
+ mMenu.onItemsChanged(false);
+ }
+
+ return this;
+ }
+
+ public void setExclusiveCheckable(boolean exclusive) {
+ mFlags = (mFlags & ~EXCLUSIVE) | (exclusive ? EXCLUSIVE : 0);
+ }
+
+ public boolean isExclusiveCheckable() {
+ return (mFlags & EXCLUSIVE) != 0;
+ }
+
+ public boolean isChecked() {
+ return (mFlags & CHECKED) == CHECKED;
+ }
+
+ public MenuItem setChecked(boolean checked) {
+ if ((mFlags & EXCLUSIVE) != 0) {
+ // Call the method on the Menu since it knows about the others in this
+ // exclusive checkable group
+ mMenu.setExclusiveItemChecked(this);
+ } else {
+ setCheckedInt(checked);
+ }
+
+ return this;
+ }
+
+ void setCheckedInt(boolean checked) {
+ final int oldFlags = mFlags;
+ mFlags = (mFlags & ~CHECKED) | (checked ? CHECKED : 0);
+ if (oldFlags != mFlags) {
+ mMenu.onItemsChanged(false);
+ }
+ }
+
+ public boolean isVisible() {
+ return (mFlags & HIDDEN) == 0;
+ }
+
+ /**
+ * Changes the visibility of the item. This method DOES NOT notify the
+ * parent menu of a change in this item, so this should only be called from
+ * methods that will eventually trigger this change. If unsure, use {@link #setVisible(boolean)}
+ * instead.
+ *
+ * @param shown Whether to show (true) or hide (false).
+ * @return Whether the item's shown state was changed
+ */
+ boolean setVisibleInt(boolean shown) {
+ final int oldFlags = mFlags;
+ mFlags = (mFlags & ~HIDDEN) | (shown ? 0 : HIDDEN);
+ return oldFlags != mFlags;
+ }
+
+ public MenuItem setVisible(boolean shown) {
+ // Try to set the shown state to the given state. If the shown state was changed
+ // (i.e. the previous state isn't the same as given state), notify the parent menu that
+ // the shown state has changed for this item
+ if (setVisibleInt(shown)) mMenu.onItemVisibleChanged(this);
+
+ return this;
+ }
+
+ public MenuItem setOnMenuItemClickListener(MenuItem.OnMenuItemClickListener clickListener) {
+ mClickListener = clickListener;
+ return this;
+ }
+
+ @Override
+ public String toString() {
+ return mTitle.toString();
+ }
+
+ void setMenuInfo(ContextMenuInfo menuInfo) {
+ mMenuInfo = menuInfo;
+ }
+
+ public ContextMenuInfo getMenuInfo() {
+ return mMenuInfo;
+ }
+
+ public void actionFormatChanged() {
+ mMenu.onItemActionRequestChanged(this);
+ }
+
+ /**
+ * @return Whether the menu should show icons for menu items.
+ */
+ public boolean shouldShowIcon() {
+ return mMenu.getOptionalIconsVisible();
+ }
+
+ public boolean isActionButton() {
+ return (mFlags & IS_ACTION) == IS_ACTION;
+ }
+
+ public boolean requestsActionButton() {
+ return (mShowAsAction & SHOW_AS_ACTION_IF_ROOM) == SHOW_AS_ACTION_IF_ROOM;
+ }
+
+ public boolean requiresActionButton() {
+ return (mShowAsAction & SHOW_AS_ACTION_ALWAYS) == SHOW_AS_ACTION_ALWAYS;
+ }
+
+ public void setIsActionButton(boolean isActionButton) {
+ if (isActionButton) {
+ mFlags |= IS_ACTION;
+ } else {
+ mFlags &= ~IS_ACTION;
+ }
+ }
+
+ public boolean showsTextAsAction() {
+ return (mShowAsAction & SHOW_AS_ACTION_WITH_TEXT) == SHOW_AS_ACTION_WITH_TEXT;
+ }
+
+ public void setShowAsAction(int actionEnum) {
+ switch (actionEnum & SHOW_AS_ACTION_MASK) {
+ case SHOW_AS_ACTION_ALWAYS:
+ case SHOW_AS_ACTION_IF_ROOM:
+ case SHOW_AS_ACTION_NEVER:
+ // Looks good!
+ break;
+
+ default:
+ // Mutually exclusive options selected!
+ throw new IllegalArgumentException("SHOW_AS_ACTION_ALWAYS, SHOW_AS_ACTION_IF_ROOM,"
+ + " and SHOW_AS_ACTION_NEVER are mutually exclusive.");
+ }
+ mShowAsAction = actionEnum;
+ mMenu.onItemActionRequestChanged(this);
+ }
+
+ public MenuItem setActionView(View view) {
+ mActionView = view;
+ mActionProvider = null;
+ if (view != null && view.getId() == View.NO_ID && mId > 0) {
+ view.setId(mId);
+ }
+ mMenu.onItemActionRequestChanged(this);
+ return this;
+ }
+
+ public MenuItem setActionView(int resId) {
+ final Context context = mMenu.getContext();
+ final LayoutInflater inflater = LayoutInflater.from(context);
+ setActionView(inflater.inflate(resId, new LinearLayout(context), false));
+ return this;
+ }
+
+ public View getActionView() {
+ if (mActionView != null) {
+ return mActionView;
+ } else if (mActionProvider != null) {
+ mActionView = mActionProvider.onCreateActionView();
+ return mActionView;
+ } else {
+ return null;
+ }
+ }
+
+ public ActionProvider getActionProvider() {
+ return mActionProvider;
+ }
+
+ public MenuItem setActionProvider(ActionProvider actionProvider) {
+ mActionView = null;
+ mActionProvider = actionProvider;
+ mMenu.onItemsChanged(true); // Measurement can be changed
+ return this;
+ }
+
+ @Override
+ public MenuItem setShowAsActionFlags(int actionEnum) {
+ setShowAsAction(actionEnum);
+ return this;
+ }
+
+ @Override
+ public boolean expandActionView() {
+ if ((mShowAsAction & SHOW_AS_ACTION_COLLAPSE_ACTION_VIEW) == 0 || mActionView == null) {
+ return false;
+ }
+
+ if (mOnActionExpandListener == null ||
+ mOnActionExpandListener.onMenuItemActionExpand(this)) {
+ return mMenu.expandItemActionView(this);
+ }
+
+ return false;
+ }
+
+ @Override
+ public boolean collapseActionView() {
+ if ((mShowAsAction & SHOW_AS_ACTION_COLLAPSE_ACTION_VIEW) == 0) {
+ return false;
+ }
+ if (mActionView == null) {
+ // We're already collapsed if we have no action view.
+ return true;
+ }
+
+ if (mOnActionExpandListener == null ||
+ mOnActionExpandListener.onMenuItemActionCollapse(this)) {
+ return mMenu.collapseItemActionView(this);
+ }
+
+ return false;
+ }
+
+ @Override
+ public MenuItem setOnActionExpandListener(OnActionExpandListener listener) {
+ mOnActionExpandListener = listener;
+ return this;
+ }
+
+ public boolean hasCollapsibleActionView() {
+ return (mShowAsAction & SHOW_AS_ACTION_COLLAPSE_ACTION_VIEW) != 0 && mActionView != null;
+ }
+
+ public void setActionViewExpanded(boolean isExpanded) {
+ mIsActionViewExpanded = isExpanded;
+ mMenu.onItemsChanged(false);
+ }
+
+ public boolean isActionViewExpanded() {
+ return mIsActionViewExpanded;
+ }
+}
diff --git a/android-app/gen-external-apklibs/com.actionbarsherlock_actionbarsherlock_4.2.0/src/com/actionbarsherlock/internal/view/menu/MenuItemWrapper.java b/android-app/gen-external-apklibs/com.actionbarsherlock_actionbarsherlock_4.2.0/src/com/actionbarsherlock/internal/view/menu/MenuItemWrapper.java
new file mode 100644
index 00000000..aaf2997b
--- /dev/null
+++ b/android-app/gen-external-apklibs/com.actionbarsherlock_actionbarsherlock_4.2.0/src/com/actionbarsherlock/internal/view/menu/MenuItemWrapper.java
@@ -0,0 +1,310 @@
+package com.actionbarsherlock.internal.view.menu;
+
+import android.content.Intent;
+import android.graphics.drawable.Drawable;
+import android.view.ContextMenu.ContextMenuInfo;
+import android.view.View;
+import com.actionbarsherlock.internal.view.ActionProviderWrapper;
+import com.actionbarsherlock.internal.widget.CollapsibleActionViewWrapper;
+import com.actionbarsherlock.view.ActionProvider;
+import com.actionbarsherlock.view.CollapsibleActionView;
+import com.actionbarsherlock.view.MenuItem;
+import com.actionbarsherlock.view.SubMenu;
+
+public class MenuItemWrapper implements MenuItem, android.view.MenuItem.OnMenuItemClickListener {
+ private final android.view.MenuItem mNativeItem;
+ private SubMenu mSubMenu = null;
+ private OnMenuItemClickListener mMenuItemClickListener = null;
+ private OnActionExpandListener mActionExpandListener = null;
+ private android.view.MenuItem.OnActionExpandListener mNativeActionExpandListener = null;
+
+
+ public MenuItemWrapper(android.view.MenuItem nativeItem) {
+ if (nativeItem == null) {
+ throw new IllegalStateException("Wrapped menu item cannot be null.");
+ }
+ mNativeItem = nativeItem;
+ }
+
+
+ @Override
+ public int getItemId() {
+ return mNativeItem.getItemId();
+ }
+
+ @Override
+ public int getGroupId() {
+ return mNativeItem.getGroupId();
+ }
+
+ @Override
+ public int getOrder() {
+ return mNativeItem.getOrder();
+ }
+
+ @Override
+ public MenuItem setTitle(CharSequence title) {
+ mNativeItem.setTitle(title);
+ return this;
+ }
+
+ @Override
+ public MenuItem setTitle(int title) {
+ mNativeItem.setTitle(title);
+ return this;
+ }
+
+ @Override
+ public CharSequence getTitle() {
+ return mNativeItem.getTitle();
+ }
+
+ @Override
+ public MenuItem setTitleCondensed(CharSequence title) {
+ mNativeItem.setTitleCondensed(title);
+ return this;
+ }
+
+ @Override
+ public CharSequence getTitleCondensed() {
+ return mNativeItem.getTitleCondensed();
+ }
+
+ @Override
+ public MenuItem setIcon(Drawable icon) {
+ mNativeItem.setIcon(icon);
+ return this;
+ }
+
+ @Override
+ public MenuItem setIcon(int iconRes) {
+ mNativeItem.setIcon(iconRes);
+ return this;
+ }
+
+ @Override
+ public Drawable getIcon() {
+ return mNativeItem.getIcon();
+ }
+
+ @Override
+ public MenuItem setIntent(Intent intent) {
+ mNativeItem.setIntent(intent);
+ return this;
+ }
+
+ @Override
+ public Intent getIntent() {
+ return mNativeItem.getIntent();
+ }
+
+ @Override
+ public MenuItem setShortcut(char numericChar, char alphaChar) {
+ mNativeItem.setShortcut(numericChar, alphaChar);
+ return this;
+ }
+
+ @Override
+ public MenuItem setNumericShortcut(char numericChar) {
+ mNativeItem.setNumericShortcut(numericChar);
+ return this;
+ }
+
+ @Override
+ public char getNumericShortcut() {
+ return mNativeItem.getNumericShortcut();
+ }
+
+ @Override
+ public MenuItem setAlphabeticShortcut(char alphaChar) {
+ mNativeItem.setAlphabeticShortcut(alphaChar);
+ return this;
+ }
+
+ @Override
+ public char getAlphabeticShortcut() {
+ return mNativeItem.getAlphabeticShortcut();
+ }
+
+ @Override
+ public MenuItem setCheckable(boolean checkable) {
+ mNativeItem.setCheckable(checkable);
+ return this;
+ }
+
+ @Override
+ public boolean isCheckable() {
+ return mNativeItem.isCheckable();
+ }
+
+ @Override
+ public MenuItem setChecked(boolean checked) {
+ mNativeItem.setChecked(checked);
+ return this;
+ }
+
+ @Override
+ public boolean isChecked() {
+ return mNativeItem.isChecked();
+ }
+
+ @Override
+ public MenuItem setVisible(boolean visible) {
+ mNativeItem.setVisible(visible);
+ return this;
+ }
+
+ @Override
+ public boolean isVisible() {
+ return mNativeItem.isVisible();
+ }
+
+ @Override
+ public MenuItem setEnabled(boolean enabled) {
+ mNativeItem.setEnabled(enabled);
+ return this;
+ }
+
+ @Override
+ public boolean isEnabled() {
+ return mNativeItem.isEnabled();
+ }
+
+ @Override
+ public boolean hasSubMenu() {
+ return mNativeItem.hasSubMenu();
+ }
+
+ @Override
+ public SubMenu getSubMenu() {
+ if (hasSubMenu() && (mSubMenu == null)) {
+ mSubMenu = new SubMenuWrapper(mNativeItem.getSubMenu());
+ }
+ return mSubMenu;
+ }
+
+ @Override
+ public MenuItem setOnMenuItemClickListener(OnMenuItemClickListener menuItemClickListener) {
+ mMenuItemClickListener = menuItemClickListener;
+ //Register ourselves as the listener to proxy
+ mNativeItem.setOnMenuItemClickListener(this);
+ return this;
+ }
+
+ @Override
+ public boolean onMenuItemClick(android.view.MenuItem item) {
+ if (mMenuItemClickListener != null) {
+ return mMenuItemClickListener.onMenuItemClick(this);
+ }
+ return false;
+ }
+
+ @Override
+ public ContextMenuInfo getMenuInfo() {
+ return mNativeItem.getMenuInfo();
+ }
+
+ @Override
+ public void setShowAsAction(int actionEnum) {
+ mNativeItem.setShowAsAction(actionEnum);
+ }
+
+ @Override
+ public MenuItem setShowAsActionFlags(int actionEnum) {
+ mNativeItem.setShowAsActionFlags(actionEnum);
+ return this;
+ }
+
+ @Override
+ public MenuItem setActionView(View view) {
+ if (view != null && view instanceof CollapsibleActionView) {
+ view = new CollapsibleActionViewWrapper(view);
+ }
+ mNativeItem.setActionView(view);
+ return this;
+ }
+
+ @Override
+ public MenuItem setActionView(int resId) {
+ //Allow the native menu to inflate the resource
+ mNativeItem.setActionView(resId);
+ if (resId != 0) {
+ //Get newly created view
+ View view = mNativeItem.getActionView();
+ if (view instanceof CollapsibleActionView) {
+ //Wrap it and re-set it
+ mNativeItem.setActionView(new CollapsibleActionViewWrapper(view));
+ }
+ }
+ return this;
+ }
+
+ @Override
+ public View getActionView() {
+ View actionView = mNativeItem.getActionView();
+ if (actionView instanceof CollapsibleActionViewWrapper) {
+ return ((CollapsibleActionViewWrapper)actionView).unwrap();
+ }
+ return actionView;
+ }
+
+ @Override
+ public MenuItem setActionProvider(ActionProvider actionProvider) {
+ mNativeItem.setActionProvider(new ActionProviderWrapper(actionProvider));
+ return this;
+ }
+
+ @Override
+ public ActionProvider getActionProvider() {
+ android.view.ActionProvider nativeProvider = mNativeItem.getActionProvider();
+ if (nativeProvider != null && nativeProvider instanceof ActionProviderWrapper) {
+ return ((ActionProviderWrapper)nativeProvider).unwrap();
+ }
+ return null;
+ }
+
+ @Override
+ public boolean expandActionView() {
+ return mNativeItem.expandActionView();
+ }
+
+ @Override
+ public boolean collapseActionView() {
+ return mNativeItem.collapseActionView();
+ }
+
+ @Override
+ public boolean isActionViewExpanded() {
+ return mNativeItem.isActionViewExpanded();
+ }
+
+ @Override
+ public MenuItem setOnActionExpandListener(OnActionExpandListener listener) {
+ mActionExpandListener = listener;
+
+ if (mNativeActionExpandListener == null) {
+ mNativeActionExpandListener = new android.view.MenuItem.OnActionExpandListener() {
+ @Override
+ public boolean onMenuItemActionExpand(android.view.MenuItem menuItem) {
+ if (mActionExpandListener != null) {
+ return mActionExpandListener.onMenuItemActionExpand(MenuItemWrapper.this);
+ }
+ return false;
+ }
+
+ @Override
+ public boolean onMenuItemActionCollapse(android.view.MenuItem menuItem) {
+ if (mActionExpandListener != null) {
+ return mActionExpandListener.onMenuItemActionCollapse(MenuItemWrapper.this);
+ }
+ return false;
+ }
+ };
+
+ //Register our inner-class as the listener to proxy method calls
+ mNativeItem.setOnActionExpandListener(mNativeActionExpandListener);
+ }
+
+ return this;
+ }
+}
diff --git a/android-app/gen-external-apklibs/com.actionbarsherlock_actionbarsherlock_4.2.0/src/com/actionbarsherlock/internal/view/menu/MenuPopupHelper.java b/android-app/gen-external-apklibs/com.actionbarsherlock_actionbarsherlock_4.2.0/src/com/actionbarsherlock/internal/view/menu/MenuPopupHelper.java
new file mode 100644
index 00000000..f030de31
--- /dev/null
+++ b/android-app/gen-external-apklibs/com.actionbarsherlock_actionbarsherlock_4.2.0/src/com/actionbarsherlock/internal/view/menu/MenuPopupHelper.java
@@ -0,0 +1,376 @@
+/*
+ * Copyright (C) 2010 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.actionbarsherlock.internal.view.menu;
+
+import java.util.ArrayList;
+import android.content.Context;
+import android.content.res.Resources;
+import android.database.DataSetObserver;
+import android.os.Parcelable;
+import android.view.KeyEvent;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.View.MeasureSpec;
+import android.view.ViewGroup;
+import android.view.ViewTreeObserver;
+import android.widget.AdapterView;
+import android.widget.BaseAdapter;
+import android.widget.FrameLayout;
+import android.widget.ListAdapter;
+import android.widget.PopupWindow;
+import com.actionbarsherlock.R;
+import com.actionbarsherlock.internal.view.View_HasStateListenerSupport;
+import com.actionbarsherlock.internal.view.View_OnAttachStateChangeListener;
+import com.actionbarsherlock.internal.widget.IcsListPopupWindow;
+import com.actionbarsherlock.view.MenuItem;
+
+/**
+ * Presents a menu as a small, simple popup anchored to another view.
+ * @hide
+ */
+public class MenuPopupHelper implements AdapterView.OnItemClickListener, View.OnKeyListener,
+ ViewTreeObserver.OnGlobalLayoutListener, PopupWindow.OnDismissListener,
+ View_OnAttachStateChangeListener, MenuPresenter {
+ //UNUSED private static final String TAG = "MenuPopupHelper";
+
+ static final int ITEM_LAYOUT = R.layout.abs__popup_menu_item_layout;
+
+ private Context mContext;
+ private LayoutInflater mInflater;
+ private IcsListPopupWindow mPopup;
+ private MenuBuilder mMenu;
+ private int mPopupMaxWidth;
+ private View mAnchorView;
+ private boolean mOverflowOnly;
+ private ViewTreeObserver mTreeObserver;
+
+ private MenuAdapter mAdapter;
+
+ private Callback mPresenterCallback;
+
+ boolean mForceShowIcon;
+
+ private ViewGroup mMeasureParent;
+
+ public MenuPopupHelper(Context context, MenuBuilder menu) {
+ this(context, menu, null, false);
+ }
+
+ public MenuPopupHelper(Context context, MenuBuilder menu, View anchorView) {
+ this(context, menu, anchorView, false);
+ }
+
+ public MenuPopupHelper(Context context, MenuBuilder menu,
+ View anchorView, boolean overflowOnly) {
+ mContext = context;
+ mInflater = LayoutInflater.from(context);
+ mMenu = menu;
+ mOverflowOnly = overflowOnly;
+
+ final Resources res = context.getResources();
+ mPopupMaxWidth = Math.max(res.getDisplayMetrics().widthPixels / 2,
+ res.getDimensionPixelSize(R.dimen.abs__config_prefDialogWidth));
+
+ mAnchorView = anchorView;
+
+ menu.addMenuPresenter(this);
+ }
+
+ public void setAnchorView(View anchor) {
+ mAnchorView = anchor;
+ }
+
+ public void setForceShowIcon(boolean forceShow) {
+ mForceShowIcon = forceShow;
+ }
+
+ public void show() {
+ if (!tryShow()) {
+ throw new IllegalStateException("MenuPopupHelper cannot be used without an anchor");
+ }
+ }
+
+ public boolean tryShow() {
+ mPopup = new IcsListPopupWindow(mContext, null, R.attr.popupMenuStyle);
+ mPopup.setOnDismissListener(this);
+ mPopup.setOnItemClickListener(this);
+
+ mAdapter = new MenuAdapter(mMenu);
+ mPopup.setAdapter(mAdapter);
+ mPopup.setModal(true);
+
+ View anchor = mAnchorView;
+ if (anchor != null) {
+ final boolean addGlobalListener = mTreeObserver == null;
+ mTreeObserver = anchor.getViewTreeObserver(); // Refresh to latest
+ if (addGlobalListener) mTreeObserver.addOnGlobalLayoutListener(this);
+ ((View_HasStateListenerSupport)anchor).addOnAttachStateChangeListener(this);
+ mPopup.setAnchorView(anchor);
+ } else {
+ return false;
+ }
+
+ mPopup.setContentWidth(Math.min(measureContentWidth(mAdapter), mPopupMaxWidth));
+ mPopup.setInputMethodMode(PopupWindow.INPUT_METHOD_NOT_NEEDED);
+ mPopup.show();
+ mPopup.getListView().setOnKeyListener(this);
+ return true;
+ }
+
+ public void dismiss() {
+ if (isShowing()) {
+ mPopup.dismiss();
+ }
+ }
+
+ public void onDismiss() {
+ mPopup = null;
+ mMenu.close();
+ if (mTreeObserver != null) {
+ if (!mTreeObserver.isAlive()) mTreeObserver = mAnchorView.getViewTreeObserver();
+ mTreeObserver.removeGlobalOnLayoutListener(this);
+ mTreeObserver = null;
+ }
+ ((View_HasStateListenerSupport)mAnchorView).removeOnAttachStateChangeListener(this);
+ }
+
+ public boolean isShowing() {
+ return mPopup != null && mPopup.isShowing();
+ }
+
+ @Override
+ public void onItemClick(AdapterView> parent, View view, int position, long id) {
+ MenuAdapter adapter = mAdapter;
+ adapter.mAdapterMenu.performItemAction(adapter.getItem(position), 0);
+ }
+
+ public boolean onKey(View v, int keyCode, KeyEvent event) {
+ if (event.getAction() == KeyEvent.ACTION_UP && keyCode == KeyEvent.KEYCODE_MENU) {
+ dismiss();
+ return true;
+ }
+ return false;
+ }
+
+ private int measureContentWidth(ListAdapter adapter) {
+ // Menus don't tend to be long, so this is more sane than it looks.
+ int width = 0;
+ View itemView = null;
+ int itemType = 0;
+ final int widthMeasureSpec =
+ MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
+ final int heightMeasureSpec =
+ MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
+ final int count = adapter.getCount();
+ for (int i = 0; i < count; i++) {
+ final int positionType = adapter.getItemViewType(i);
+ if (positionType != itemType) {
+ itemType = positionType;
+ itemView = null;
+ }
+ if (mMeasureParent == null) {
+ mMeasureParent = new FrameLayout(mContext);
+ }
+ itemView = adapter.getView(i, itemView, mMeasureParent);
+ itemView.measure(widthMeasureSpec, heightMeasureSpec);
+ width = Math.max(width, itemView.getMeasuredWidth());
+ }
+ return width;
+ }
+
+ @Override
+ public void onGlobalLayout() {
+ if (isShowing()) {
+ final View anchor = mAnchorView;
+ if (anchor == null || !anchor.isShown()) {
+ dismiss();
+ } else if (isShowing()) {
+ // Recompute window size and position
+ mPopup.show();
+ }
+ }
+ }
+
+ @Override
+ public void onViewAttachedToWindow(View v) {
+ }
+
+ @Override
+ public void onViewDetachedFromWindow(View v) {
+ if (mTreeObserver != null) {
+ if (!mTreeObserver.isAlive()) mTreeObserver = v.getViewTreeObserver();
+ mTreeObserver.removeGlobalOnLayoutListener(this);
+ }
+ ((View_HasStateListenerSupport)v).removeOnAttachStateChangeListener(this);
+ }
+
+ @Override
+ public void initForMenu(Context context, MenuBuilder menu) {
+ // Don't need to do anything; we added as a presenter in the constructor.
+ }
+
+ @Override
+ public MenuView getMenuView(ViewGroup root) {
+ throw new UnsupportedOperationException("MenuPopupHelpers manage their own views");
+ }
+
+ @Override
+ public void updateMenuView(boolean cleared) {
+ if (mAdapter != null) mAdapter.notifyDataSetChanged();
+ }
+
+ @Override
+ public void setCallback(Callback cb) {
+ mPresenterCallback = cb;
+ }
+
+ @Override
+ public boolean onSubMenuSelected(SubMenuBuilder subMenu) {
+ if (subMenu.hasVisibleItems()) {
+ MenuPopupHelper subPopup = new MenuPopupHelper(mContext, subMenu, mAnchorView, false);
+ subPopup.setCallback(mPresenterCallback);
+
+ boolean preserveIconSpacing = false;
+ final int count = subMenu.size();
+ for (int i = 0; i < count; i++) {
+ MenuItem childItem = subMenu.getItem(i);
+ if (childItem.isVisible() && childItem.getIcon() != null) {
+ preserveIconSpacing = true;
+ break;
+ }
+ }
+ subPopup.setForceShowIcon(preserveIconSpacing);
+
+ if (subPopup.tryShow()) {
+ if (mPresenterCallback != null) {
+ mPresenterCallback.onOpenSubMenu(subMenu);
+ }
+ return true;
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public void onCloseMenu(MenuBuilder menu, boolean allMenusAreClosing) {
+ // Only care about the (sub)menu we're presenting.
+ if (menu != mMenu) return;
+
+ dismiss();
+ if (mPresenterCallback != null) {
+ mPresenterCallback.onCloseMenu(menu, allMenusAreClosing);
+ }
+ }
+
+ @Override
+ public boolean flagActionItems() {
+ return false;
+ }
+
+ public boolean expandItemActionView(MenuBuilder menu, MenuItemImpl item) {
+ return false;
+ }
+
+ public boolean collapseItemActionView(MenuBuilder menu, MenuItemImpl item) {
+ return false;
+ }
+
+ @Override
+ public int getId() {
+ return 0;
+ }
+
+ @Override
+ public Parcelable onSaveInstanceState() {
+ return null;
+ }
+
+ @Override
+ public void onRestoreInstanceState(Parcelable state) {
+ }
+
+ private class MenuAdapter extends BaseAdapter {
+ private MenuBuilder mAdapterMenu;
+ private int mExpandedIndex = -1;
+
+ public MenuAdapter(MenuBuilder menu) {
+ mAdapterMenu = menu;
+ registerDataSetObserver(new ExpandedIndexObserver());
+ findExpandedIndex();
+ }
+
+ public int getCount() {
+ ArrayList items = mOverflowOnly ?
+ mAdapterMenu.getNonActionItems() : mAdapterMenu.getVisibleItems();
+ if (mExpandedIndex < 0) {
+ return items.size();
+ }
+ return items.size() - 1;
+ }
+
+ public MenuItemImpl getItem(int position) {
+ ArrayList items = mOverflowOnly ?
+ mAdapterMenu.getNonActionItems() : mAdapterMenu.getVisibleItems();
+ if (mExpandedIndex >= 0 && position >= mExpandedIndex) {
+ position++;
+ }
+ return items.get(position);
+ }
+
+ public long getItemId(int position) {
+ // Since a menu item's ID is optional, we'll use the position as an
+ // ID for the item in the AdapterView
+ return position;
+ }
+
+ public View getView(int position, View convertView, ViewGroup parent) {
+ if (convertView == null) {
+ convertView = mInflater.inflate(ITEM_LAYOUT, parent, false);
+ }
+
+ MenuView.ItemView itemView = (MenuView.ItemView) convertView;
+ if (mForceShowIcon) {
+ ((ListMenuItemView) convertView).setForceShowIcon(true);
+ }
+ itemView.initialize(getItem(position), 0);
+ return convertView;
+ }
+
+ void findExpandedIndex() {
+ final MenuItemImpl expandedItem = mMenu.getExpandedItem();
+ if (expandedItem != null) {
+ final ArrayList items = mMenu.getNonActionItems();
+ final int count = items.size();
+ for (int i = 0; i < count; i++) {
+ final MenuItemImpl item = items.get(i);
+ if (item == expandedItem) {
+ mExpandedIndex = i;
+ return;
+ }
+ }
+ }
+ mExpandedIndex = -1;
+ }
+ }
+
+ private class ExpandedIndexObserver extends DataSetObserver {
+ @Override
+ public void onChanged() {
+ mAdapter.findExpandedIndex();
+ }
+ }
+}
diff --git a/android-app/gen-external-apklibs/com.actionbarsherlock_actionbarsherlock_4.2.0/src/com/actionbarsherlock/internal/view/menu/MenuPresenter.java b/android-app/gen-external-apklibs/com.actionbarsherlock_actionbarsherlock_4.2.0/src/com/actionbarsherlock/internal/view/menu/MenuPresenter.java
new file mode 100644
index 00000000..c3f35472
--- /dev/null
+++ b/android-app/gen-external-apklibs/com.actionbarsherlock_actionbarsherlock_4.2.0/src/com/actionbarsherlock/internal/view/menu/MenuPresenter.java
@@ -0,0 +1,148 @@
+/*
+ * Copyright (C) 2011 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.actionbarsherlock.internal.view.menu;
+
+import android.content.Context;
+import android.os.Parcelable;
+import android.view.ViewGroup;
+
+/**
+ * A MenuPresenter is responsible for building views for a Menu object.
+ * It takes over some responsibility from the old style monolithic MenuBuilder class.
+ */
+public interface MenuPresenter {
+ /**
+ * Called by menu implementation to notify another component of open/close events.
+ */
+ public interface Callback {
+ /**
+ * Called when a menu is closing.
+ * @param menu
+ * @param allMenusAreClosing
+ */
+ public void onCloseMenu(MenuBuilder menu, boolean allMenusAreClosing);
+
+ /**
+ * Called when a submenu opens. Useful for notifying the application
+ * of menu state so that it does not attempt to hide the action bar
+ * while a submenu is open or similar.
+ *
+ * @param subMenu Submenu currently being opened
+ * @return true if the Callback will handle presenting the submenu, false if
+ * the presenter should attempt to do so.
+ */
+ public boolean onOpenSubMenu(MenuBuilder subMenu);
+ }
+
+ /**
+ * Initialize this presenter for the given context and menu.
+ * This method is called by MenuBuilder when a presenter is
+ * added. See {@link MenuBuilder#addMenuPresenter(MenuPresenter)}
+ *
+ * @param context Context for this presenter; used for view creation and resource management
+ * @param menu Menu to host
+ */
+ public void initForMenu(Context context, MenuBuilder menu);
+
+ /**
+ * Retrieve a MenuView to display the menu specified in
+ * {@link #initForMenu(Context, Menu)}.
+ *
+ * @param root Intended parent of the MenuView.
+ * @return A freshly created MenuView.
+ */
+ public MenuView getMenuView(ViewGroup root);
+
+ /**
+ * Update the menu UI in response to a change. Called by
+ * MenuBuilder during the normal course of operation.
+ *
+ * @param cleared true if the menu was entirely cleared
+ */
+ public void updateMenuView(boolean cleared);
+
+ /**
+ * Set a callback object that will be notified of menu events
+ * related to this specific presentation.
+ * @param cb Callback that will be notified of future events
+ */
+ public void setCallback(Callback cb);
+
+ /**
+ * Called by Menu implementations to indicate that a submenu item
+ * has been selected. An active Callback should be notified, and
+ * if applicable the presenter should present the submenu.
+ *
+ * @param subMenu SubMenu being opened
+ * @return true if the the event was handled, false otherwise.
+ */
+ public boolean onSubMenuSelected(SubMenuBuilder subMenu);
+
+ /**
+ * Called by Menu implementations to indicate that a menu or submenu is
+ * closing. Presenter implementations should close the representation
+ * of the menu indicated as necessary and notify a registered callback.
+ *
+ * @param menu Menu or submenu that is closing.
+ * @param allMenusAreClosing True if all associated menus are closing.
+ */
+ public void onCloseMenu(MenuBuilder menu, boolean allMenusAreClosing);
+
+ /**
+ * Called by Menu implementations to flag items that will be shown as actions.
+ * @return true if this presenter changed the action status of any items.
+ */
+ public boolean flagActionItems();
+
+ /**
+ * Called when a menu item with a collapsable action view should expand its action view.
+ *
+ * @param menu Menu containing the item to be expanded
+ * @param item Item to be expanded
+ * @return true if this presenter expanded the action view, false otherwise.
+ */
+ public boolean expandItemActionView(MenuBuilder menu, MenuItemImpl item);
+
+ /**
+ * Called when a menu item with a collapsable action view should collapse its action view.
+ *
+ * @param menu Menu containing the item to be collapsed
+ * @param item Item to be collapsed
+ * @return true if this presenter collapsed the action view, false otherwise.
+ */
+ public boolean collapseItemActionView(MenuBuilder menu, MenuItemImpl item);
+
+ /**
+ * Returns an ID for determining how to save/restore instance state.
+ * @return a valid ID value.
+ */
+ public int getId();
+
+ /**
+ * Returns a Parcelable describing the current state of the presenter.
+ * It will be passed to the {@link #onRestoreInstanceState(Parcelable)}
+ * method of the presenter sharing the same ID later.
+ * @return The saved instance state
+ */
+ public Parcelable onSaveInstanceState();
+
+ /**
+ * Supplies the previously saved instance state to be restored.
+ * @param state The previously saved instance state
+ */
+ public void onRestoreInstanceState(Parcelable state);
+}
diff --git a/android-app/gen-external-apklibs/com.actionbarsherlock_actionbarsherlock_4.2.0/src/com/actionbarsherlock/internal/view/menu/MenuView.java b/android-app/gen-external-apklibs/com.actionbarsherlock_actionbarsherlock_4.2.0/src/com/actionbarsherlock/internal/view/menu/MenuView.java
new file mode 100644
index 00000000..323ba2d8
--- /dev/null
+++ b/android-app/gen-external-apklibs/com.actionbarsherlock_actionbarsherlock_4.2.0/src/com/actionbarsherlock/internal/view/menu/MenuView.java
@@ -0,0 +1,120 @@
+/*
+ * Copyright (C) 2006 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.actionbarsherlock.internal.view.menu;
+
+import android.graphics.drawable.Drawable;
+
+/**
+ * Minimal interface for a menu view. {@link #initialize(MenuBuilder)} must be called for the
+ * menu to be functional.
+ *
+ * @hide
+ */
+public interface MenuView {
+ /**
+ * Initializes the menu to the given menu. This should be called after the
+ * view is inflated.
+ *
+ * @param menu The menu that this MenuView should display.
+ */
+ public void initialize(MenuBuilder menu);
+
+ /**
+ * Returns the default animations to be used for this menu when entering/exiting.
+ * @return A resource ID for the default animations to be used for this menu.
+ */
+ public int getWindowAnimations();
+
+ /**
+ * Minimal interface for a menu item view. {@link #initialize(MenuItemImpl, int)} must be called
+ * for the item to be functional.
+ */
+ public interface ItemView {
+ /**
+ * Initializes with the provided MenuItemData. This should be called after the view is
+ * inflated.
+ * @param itemData The item that this ItemView should display.
+ * @param menuType The type of this menu, one of
+ * {@link MenuBuilder#TYPE_ICON}, {@link MenuBuilder#TYPE_EXPANDED},
+ * {@link MenuBuilder#TYPE_DIALOG}).
+ */
+ public void initialize(MenuItemImpl itemData, int menuType);
+
+ /**
+ * Gets the item data that this view is displaying.
+ * @return the item data, or null if there is not one
+ */
+ public MenuItemImpl getItemData();
+
+ /**
+ * Sets the title of the item view.
+ * @param title The title to set.
+ */
+ public void setTitle(CharSequence title);
+
+ /**
+ * Sets the enabled state of the item view.
+ * @param enabled Whether the item view should be enabled.
+ */
+ public void setEnabled(boolean enabled);
+
+ /**
+ * Displays the checkbox for the item view. This does not ensure the item view will be
+ * checked, for that use {@link #setChecked}.
+ * @param checkable Whether to display the checkbox or to hide it
+ */
+ public void setCheckable(boolean checkable);
+
+ /**
+ * Checks the checkbox for the item view. If the checkbox is hidden, it will NOT be
+ * made visible, call {@link #setCheckable(boolean)} for that.
+ * @param checked Whether the checkbox should be checked
+ */
+ public void setChecked(boolean checked);
+
+ /**
+ * Sets the shortcut for the item.
+ * @param showShortcut Whether a shortcut should be shown(if false, the value of
+ * shortcutKey should be ignored).
+ * @param shortcutKey The shortcut key that should be shown on the ItemView.
+ */
+ public void setShortcut(boolean showShortcut, char shortcutKey);
+
+ /**
+ * Set the icon of this item view.
+ * @param icon The icon of this item. null to hide the icon.
+ */
+ public void setIcon(Drawable icon);
+
+ /**
+ * Whether this item view prefers displaying the condensed title rather
+ * than the normal title. If a condensed title is not available, the
+ * normal title will be used.
+ *
+ * @return Whether this item view prefers displaying the condensed
+ * title.
+ */
+ public boolean prefersCondensedTitle();
+
+ /**
+ * Whether this item view shows an icon.
+ *
+ * @return Whether this item view shows an icon.
+ */
+ public boolean showsIcon();
+ }
+}
diff --git a/android-app/gen-external-apklibs/com.actionbarsherlock_actionbarsherlock_4.2.0/src/com/actionbarsherlock/internal/view/menu/MenuWrapper.java b/android-app/gen-external-apklibs/com.actionbarsherlock_actionbarsherlock_4.2.0/src/com/actionbarsherlock/internal/view/menu/MenuWrapper.java
new file mode 100644
index 00000000..3d4dd42f
--- /dev/null
+++ b/android-app/gen-external-apklibs/com.actionbarsherlock_actionbarsherlock_4.2.0/src/com/actionbarsherlock/internal/view/menu/MenuWrapper.java
@@ -0,0 +1,185 @@
+package com.actionbarsherlock.internal.view.menu;
+
+import java.util.WeakHashMap;
+import android.content.ComponentName;
+import android.content.Intent;
+import android.view.KeyEvent;
+import com.actionbarsherlock.view.Menu;
+import com.actionbarsherlock.view.MenuItem;
+import com.actionbarsherlock.view.SubMenu;
+
+public class MenuWrapper implements Menu {
+ private final android.view.Menu mNativeMenu;
+
+ private final WeakHashMap mNativeMap =
+ new WeakHashMap();
+
+
+ public MenuWrapper(android.view.Menu nativeMenu) {
+ mNativeMenu = nativeMenu;
+ }
+
+ public android.view.Menu unwrap() {
+ return mNativeMenu;
+ }
+
+ private MenuItem addInternal(android.view.MenuItem nativeItem) {
+ MenuItem item = new MenuItemWrapper(nativeItem);
+ mNativeMap.put(nativeItem, item);
+ return item;
+ }
+
+ @Override
+ public MenuItem add(CharSequence title) {
+ return addInternal(mNativeMenu.add(title));
+ }
+
+ @Override
+ public MenuItem add(int titleRes) {
+ return addInternal(mNativeMenu.add(titleRes));
+ }
+
+ @Override
+ public MenuItem add(int groupId, int itemId, int order, CharSequence title) {
+ return addInternal(mNativeMenu.add(groupId, itemId, order, title));
+ }
+
+ @Override
+ public MenuItem add(int groupId, int itemId, int order, int titleRes) {
+ return addInternal(mNativeMenu.add(groupId, itemId, order, titleRes));
+ }
+
+ private SubMenu addInternal(android.view.SubMenu nativeSubMenu) {
+ SubMenu subMenu = new SubMenuWrapper(nativeSubMenu);
+ android.view.MenuItem nativeItem = nativeSubMenu.getItem();
+ MenuItem item = subMenu.getItem();
+ mNativeMap.put(nativeItem, item);
+ return subMenu;
+ }
+
+ @Override
+ public SubMenu addSubMenu(CharSequence title) {
+ return addInternal(mNativeMenu.addSubMenu(title));
+ }
+
+ @Override
+ public SubMenu addSubMenu(int titleRes) {
+ return addInternal(mNativeMenu.addSubMenu(titleRes));
+ }
+
+ @Override
+ public SubMenu addSubMenu(int groupId, int itemId, int order, CharSequence title) {
+ return addInternal(mNativeMenu.addSubMenu(groupId, itemId, order, title));
+ }
+
+ @Override
+ public SubMenu addSubMenu(int groupId, int itemId, int order, int titleRes) {
+ return addInternal(mNativeMenu.addSubMenu(groupId, itemId, order, titleRes));
+ }
+
+ @Override
+ public int addIntentOptions(int groupId, int itemId, int order, ComponentName caller, Intent[] specifics, Intent intent, int flags, MenuItem[] outSpecificItems) {
+ int result;
+ if (outSpecificItems != null) {
+ android.view.MenuItem[] nativeOutItems = new android.view.MenuItem[outSpecificItems.length];
+ result = mNativeMenu.addIntentOptions(groupId, itemId, order, caller, specifics, intent, flags, nativeOutItems);
+ for (int i = 0, length = outSpecificItems.length; i < length; i++) {
+ outSpecificItems[i] = new MenuItemWrapper(nativeOutItems[i]);
+ }
+ } else {
+ result = mNativeMenu.addIntentOptions(groupId, itemId, order, caller, specifics, intent, flags, null);
+ }
+ return result;
+ }
+
+ @Override
+ public void removeItem(int id) {
+ mNativeMenu.removeItem(id);
+ }
+
+ @Override
+ public void removeGroup(int groupId) {
+ mNativeMenu.removeGroup(groupId);
+ }
+
+ @Override
+ public void clear() {
+ mNativeMap.clear();
+ mNativeMenu.clear();
+ }
+
+ @Override
+ public void setGroupCheckable(int group, boolean checkable, boolean exclusive) {
+ mNativeMenu.setGroupCheckable(group, checkable, exclusive);
+ }
+
+ @Override
+ public void setGroupVisible(int group, boolean visible) {
+ mNativeMenu.setGroupVisible(group, visible);
+ }
+
+ @Override
+ public void setGroupEnabled(int group, boolean enabled) {
+ mNativeMenu.setGroupEnabled(group, enabled);
+ }
+
+ @Override
+ public boolean hasVisibleItems() {
+ return mNativeMenu.hasVisibleItems();
+ }
+
+ @Override
+ public MenuItem findItem(int id) {
+ android.view.MenuItem nativeItem = mNativeMenu.findItem(id);
+ return findItem(nativeItem);
+ }
+
+ public MenuItem findItem(android.view.MenuItem nativeItem) {
+ if (nativeItem == null) {
+ return null;
+ }
+
+ MenuItem wrapped = mNativeMap.get(nativeItem);
+ if (wrapped != null) {
+ return wrapped;
+ }
+
+ return addInternal(nativeItem);
+ }
+
+ @Override
+ public int size() {
+ return mNativeMenu.size();
+ }
+
+ @Override
+ public MenuItem getItem(int index) {
+ android.view.MenuItem nativeItem = mNativeMenu.getItem(index);
+ return findItem(nativeItem);
+ }
+
+ @Override
+ public void close() {
+ mNativeMenu.close();
+ }
+
+ @Override
+ public boolean performShortcut(int keyCode, KeyEvent event, int flags) {
+ return mNativeMenu.performShortcut(keyCode, event, flags);
+ }
+
+ @Override
+ public boolean isShortcutKey(int keyCode, KeyEvent event) {
+ return mNativeMenu.isShortcutKey(keyCode, event);
+ }
+
+ @Override
+ public boolean performIdentifierAction(int id, int flags) {
+ return mNativeMenu.performIdentifierAction(id, flags);
+ }
+
+ @Override
+ public void setQwertyMode(boolean isQwerty) {
+ mNativeMenu.setQwertyMode(isQwerty);
+ }
+}
diff --git a/android-app/gen-external-apklibs/com.actionbarsherlock_actionbarsherlock_4.2.0/src/com/actionbarsherlock/internal/view/menu/SubMenuBuilder.java b/android-app/gen-external-apklibs/com.actionbarsherlock_actionbarsherlock_4.2.0/src/com/actionbarsherlock/internal/view/menu/SubMenuBuilder.java
new file mode 100644
index 00000000..6679cf38
--- /dev/null
+++ b/android-app/gen-external-apklibs/com.actionbarsherlock_actionbarsherlock_4.2.0/src/com/actionbarsherlock/internal/view/menu/SubMenuBuilder.java
@@ -0,0 +1,134 @@
+/*
+ * Copyright (C) 2006 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.actionbarsherlock.internal.view.menu;
+
+import android.content.Context;
+import android.graphics.drawable.Drawable;
+import android.view.View;
+
+import com.actionbarsherlock.view.Menu;
+import com.actionbarsherlock.view.MenuItem;
+import com.actionbarsherlock.view.SubMenu;
+
+/**
+ * The model for a sub menu, which is an extension of the menu. Most methods are proxied to
+ * the parent menu.
+ */
+public class SubMenuBuilder extends MenuBuilder implements SubMenu {
+ private MenuBuilder mParentMenu;
+ private MenuItemImpl mItem;
+
+ public SubMenuBuilder(Context context, MenuBuilder parentMenu, MenuItemImpl item) {
+ super(context);
+
+ mParentMenu = parentMenu;
+ mItem = item;
+ }
+
+ @Override
+ public void setQwertyMode(boolean isQwerty) {
+ mParentMenu.setQwertyMode(isQwerty);
+ }
+
+ @Override
+ public boolean isQwertyMode() {
+ return mParentMenu.isQwertyMode();
+ }
+
+ @Override
+ public void setShortcutsVisible(boolean shortcutsVisible) {
+ mParentMenu.setShortcutsVisible(shortcutsVisible);
+ }
+
+ @Override
+ public boolean isShortcutsVisible() {
+ return mParentMenu.isShortcutsVisible();
+ }
+
+ public Menu getParentMenu() {
+ return mParentMenu;
+ }
+
+ public MenuItem getItem() {
+ return mItem;
+ }
+
+ @Override
+ public void setCallback(Callback callback) {
+ mParentMenu.setCallback(callback);
+ }
+
+ @Override
+ public MenuBuilder getRootMenu() {
+ return mParentMenu;
+ }
+
+ @Override
+ boolean dispatchMenuItemSelected(MenuBuilder menu, MenuItem item) {
+ return super.dispatchMenuItemSelected(menu, item) ||
+ mParentMenu.dispatchMenuItemSelected(menu, item);
+ }
+
+ public SubMenu setIcon(Drawable icon) {
+ mItem.setIcon(icon);
+ return this;
+ }
+
+ public SubMenu setIcon(int iconRes) {
+ mItem.setIcon(iconRes);
+ return this;
+ }
+
+ public SubMenu setHeaderIcon(Drawable icon) {
+ return (SubMenu) super.setHeaderIconInt(icon);
+ }
+
+ public SubMenu setHeaderIcon(int iconRes) {
+ return (SubMenu) super.setHeaderIconInt(iconRes);
+ }
+
+ public SubMenu setHeaderTitle(CharSequence title) {
+ return (SubMenu) super.setHeaderTitleInt(title);
+ }
+
+ public SubMenu setHeaderTitle(int titleRes) {
+ return (SubMenu) super.setHeaderTitleInt(titleRes);
+ }
+
+ public SubMenu setHeaderView(View view) {
+ return (SubMenu) super.setHeaderViewInt(view);
+ }
+
+ @Override
+ public boolean expandItemActionView(MenuItemImpl item) {
+ return mParentMenu.expandItemActionView(item);
+ }
+
+ @Override
+ public boolean collapseItemActionView(MenuItemImpl item) {
+ return mParentMenu.collapseItemActionView(item);
+ }
+
+ @Override
+ public String getActionViewStatesKey() {
+ final int itemId = mItem != null ? mItem.getItemId() : 0;
+ if (itemId == 0) {
+ return null;
+ }
+ return super.getActionViewStatesKey() + ":" + itemId;
+ }
+}
diff --git a/android-app/gen-external-apklibs/com.actionbarsherlock_actionbarsherlock_4.2.0/src/com/actionbarsherlock/internal/view/menu/SubMenuWrapper.java b/android-app/gen-external-apklibs/com.actionbarsherlock_actionbarsherlock_4.2.0/src/com/actionbarsherlock/internal/view/menu/SubMenuWrapper.java
new file mode 100644
index 00000000..7d307acb
--- /dev/null
+++ b/android-app/gen-external-apklibs/com.actionbarsherlock_actionbarsherlock_4.2.0/src/com/actionbarsherlock/internal/view/menu/SubMenuWrapper.java
@@ -0,0 +1,72 @@
+package com.actionbarsherlock.internal.view.menu;
+
+import android.graphics.drawable.Drawable;
+import android.view.View;
+import com.actionbarsherlock.view.MenuItem;
+import com.actionbarsherlock.view.SubMenu;
+
+public class SubMenuWrapper extends MenuWrapper implements SubMenu {
+ private final android.view.SubMenu mNativeSubMenu;
+ private MenuItem mItem = null;
+
+ public SubMenuWrapper(android.view.SubMenu nativeSubMenu) {
+ super(nativeSubMenu);
+ mNativeSubMenu = nativeSubMenu;
+ }
+
+
+ @Override
+ public SubMenu setHeaderTitle(int titleRes) {
+ mNativeSubMenu.setHeaderTitle(titleRes);
+ return this;
+ }
+
+ @Override
+ public SubMenu setHeaderTitle(CharSequence title) {
+ mNativeSubMenu.setHeaderTitle(title);
+ return this;
+ }
+
+ @Override
+ public SubMenu setHeaderIcon(int iconRes) {
+ mNativeSubMenu.setHeaderIcon(iconRes);
+ return this;
+ }
+
+ @Override
+ public SubMenu setHeaderIcon(Drawable icon) {
+ mNativeSubMenu.setHeaderIcon(icon);
+ return this;
+ }
+
+ @Override
+ public SubMenu setHeaderView(View view) {
+ mNativeSubMenu.setHeaderView(view);
+ return this;
+ }
+
+ @Override
+ public void clearHeader() {
+ mNativeSubMenu.clearHeader();
+ }
+
+ @Override
+ public SubMenu setIcon(int iconRes) {
+ mNativeSubMenu.setIcon(iconRes);
+ return this;
+ }
+
+ @Override
+ public SubMenu setIcon(Drawable icon) {
+ mNativeSubMenu.setIcon(icon);
+ return this;
+ }
+
+ @Override
+ public MenuItem getItem() {
+ if (mItem == null) {
+ mItem = new MenuItemWrapper(mNativeSubMenu.getItem());
+ }
+ return mItem;
+ }
+}
diff --git a/android-app/gen-external-apklibs/org.solovyev.android_android-common-ads_1.0.6/gen/org/solovyev/android/ads/BuildConfig.java b/android-app/gen-external-apklibs/org.solovyev.android_android-common-ads_1.0.6/gen/org/solovyev/android/ads/BuildConfig.java
new file mode 100644
index 00000000..2cdcc5a6
--- /dev/null
+++ b/android-app/gen-external-apklibs/org.solovyev.android_android-common-ads_1.0.6/gen/org/solovyev/android/ads/BuildConfig.java
@@ -0,0 +1,6 @@
+/** Automatically generated file. DO NOT MODIFY */
+package org.solovyev.android.ads;
+
+public final class BuildConfig {
+ public final static boolean DEBUG = true;
+}
\ No newline at end of file
diff --git a/android-app/gen-external-apklibs/org.solovyev.android_android-common-ads_1.0.6/gen/org/solovyev/android/ads/Manifest.java b/android-app/gen-external-apklibs/org.solovyev.android_android-common-ads_1.0.6/gen/org/solovyev/android/ads/Manifest.java
new file mode 100644
index 00000000..9d2710a8
--- /dev/null
+++ b/android-app/gen-external-apklibs/org.solovyev.android_android-common-ads_1.0.6/gen/org/solovyev/android/ads/Manifest.java
@@ -0,0 +1,5 @@
+package org.solovyev.android.ads;
+
+/* This stub is for using by IDE only. It is NOT the Manifest class actually packed into APK */
+public final class Manifest {
+}
\ No newline at end of file
diff --git a/android-app/gen-external-apklibs/org.solovyev.android_android-common-ads_1.0.6/gen/org/solovyev/android/ads/R.java b/android-app/gen-external-apklibs/org.solovyev.android_android-common-ads_1.0.6/gen/org/solovyev/android/ads/R.java
new file mode 100644
index 00000000..510a1c46
--- /dev/null
+++ b/android-app/gen-external-apklibs/org.solovyev.android_android-common-ads_1.0.6/gen/org/solovyev/android/ads/R.java
@@ -0,0 +1,5 @@
+package org.solovyev.android.ads;
+
+/* This stub is for using by IDE only. It is NOT the R class actually packed into APK */
+public final class R {
+}
\ No newline at end of file
diff --git a/android-app/gen-external-apklibs/org.solovyev.android_android-common-all_1.0.6/gen/org/solovyev/android/all/BuildConfig.java b/android-app/gen-external-apklibs/org.solovyev.android_android-common-all_1.0.6/gen/org/solovyev/android/all/BuildConfig.java
new file mode 100644
index 00000000..c9c249b6
--- /dev/null
+++ b/android-app/gen-external-apklibs/org.solovyev.android_android-common-all_1.0.6/gen/org/solovyev/android/all/BuildConfig.java
@@ -0,0 +1,6 @@
+/** Automatically generated file. DO NOT MODIFY */
+package org.solovyev.android.all;
+
+public final class BuildConfig {
+ public final static boolean DEBUG = true;
+}
\ No newline at end of file
diff --git a/android-app/gen-external-apklibs/org.solovyev.android_android-common-all_1.0.6/gen/org/solovyev/android/all/Manifest.java b/android-app/gen-external-apklibs/org.solovyev.android_android-common-all_1.0.6/gen/org/solovyev/android/all/Manifest.java
new file mode 100644
index 00000000..07cbbca1
--- /dev/null
+++ b/android-app/gen-external-apklibs/org.solovyev.android_android-common-all_1.0.6/gen/org/solovyev/android/all/Manifest.java
@@ -0,0 +1,5 @@
+package org.solovyev.android.all;
+
+/* This stub is for using by IDE only. It is NOT the Manifest class actually packed into APK */
+public final class Manifest {
+}
\ No newline at end of file
diff --git a/android-app/gen-external-apklibs/org.solovyev.android_android-common-all_1.0.6/gen/org/solovyev/android/all/R.java b/android-app/gen-external-apklibs/org.solovyev.android_android-common-all_1.0.6/gen/org/solovyev/android/all/R.java
new file mode 100644
index 00000000..7a01793d
--- /dev/null
+++ b/android-app/gen-external-apklibs/org.solovyev.android_android-common-all_1.0.6/gen/org/solovyev/android/all/R.java
@@ -0,0 +1,5 @@
+package org.solovyev.android.all;
+
+/* This stub is for using by IDE only. It is NOT the R class actually packed into APK */
+public final class R {
+}
\ No newline at end of file
diff --git a/android-app/gen-external-apklibs/org.solovyev.android_android-common-billing_1.0.6/gen/net/robotmedia/billing/BuildConfig.java b/android-app/gen-external-apklibs/org.solovyev.android_android-common-billing_1.0.6/gen/net/robotmedia/billing/BuildConfig.java
new file mode 100644
index 00000000..f533bda0
--- /dev/null
+++ b/android-app/gen-external-apklibs/org.solovyev.android_android-common-billing_1.0.6/gen/net/robotmedia/billing/BuildConfig.java
@@ -0,0 +1,6 @@
+/** Automatically generated file. DO NOT MODIFY */
+package net.robotmedia.billing;
+
+public final class BuildConfig {
+ public final static boolean DEBUG = true;
+}
\ No newline at end of file
diff --git a/android-app/gen-external-apklibs/org.solovyev.android_android-common-billing_1.0.6/gen/net/robotmedia/billing/Manifest.java b/android-app/gen-external-apklibs/org.solovyev.android_android-common-billing_1.0.6/gen/net/robotmedia/billing/Manifest.java
new file mode 100644
index 00000000..22103563
--- /dev/null
+++ b/android-app/gen-external-apklibs/org.solovyev.android_android-common-billing_1.0.6/gen/net/robotmedia/billing/Manifest.java
@@ -0,0 +1,5 @@
+package net.robotmedia.billing;
+
+/* This stub is for using by IDE only. It is NOT the Manifest class actually packed into APK */
+public final class Manifest {
+}
\ No newline at end of file
diff --git a/android-app/gen-external-apklibs/org.solovyev.android_android-common-billing_1.0.6/gen/net/robotmedia/billing/R.java b/android-app/gen-external-apklibs/org.solovyev.android_android-common-billing_1.0.6/gen/net/robotmedia/billing/R.java
new file mode 100644
index 00000000..80008e77
--- /dev/null
+++ b/android-app/gen-external-apklibs/org.solovyev.android_android-common-billing_1.0.6/gen/net/robotmedia/billing/R.java
@@ -0,0 +1,5 @@
+package net.robotmedia.billing;
+
+/* This stub is for using by IDE only. It is NOT the R class actually packed into APK */
+public final class R {
+}
\ No newline at end of file
diff --git a/android-app/gen-external-apklibs/org.solovyev.android_android-common-core_1.0.6/gen/org/solovyev/android/core/BuildConfig.java b/android-app/gen-external-apklibs/org.solovyev.android_android-common-core_1.0.6/gen/org/solovyev/android/core/BuildConfig.java
new file mode 100644
index 00000000..57d0e993
--- /dev/null
+++ b/android-app/gen-external-apklibs/org.solovyev.android_android-common-core_1.0.6/gen/org/solovyev/android/core/BuildConfig.java
@@ -0,0 +1,6 @@
+/** Automatically generated file. DO NOT MODIFY */
+package org.solovyev.android.core;
+
+public final class BuildConfig {
+ public final static boolean DEBUG = true;
+}
\ No newline at end of file
diff --git a/android-app/gen-external-apklibs/org.solovyev.android_android-common-core_1.0.6/gen/org/solovyev/android/core/Manifest.java b/android-app/gen-external-apklibs/org.solovyev.android_android-common-core_1.0.6/gen/org/solovyev/android/core/Manifest.java
new file mode 100644
index 00000000..bf9ac9f5
--- /dev/null
+++ b/android-app/gen-external-apklibs/org.solovyev.android_android-common-core_1.0.6/gen/org/solovyev/android/core/Manifest.java
@@ -0,0 +1,5 @@
+package org.solovyev.android.core;
+
+/* This stub is for using by IDE only. It is NOT the Manifest class actually packed into APK */
+public final class Manifest {
+}
\ No newline at end of file
diff --git a/android-app/gen-external-apklibs/org.solovyev.android_android-common-core_1.0.6/gen/org/solovyev/android/core/R.java b/android-app/gen-external-apklibs/org.solovyev.android_android-common-core_1.0.6/gen/org/solovyev/android/core/R.java
new file mode 100644
index 00000000..a5563bca
--- /dev/null
+++ b/android-app/gen-external-apklibs/org.solovyev.android_android-common-core_1.0.6/gen/org/solovyev/android/core/R.java
@@ -0,0 +1,5 @@
+package org.solovyev.android.core;
+
+/* This stub is for using by IDE only. It is NOT the R class actually packed into APK */
+public final class R {
+}
\ No newline at end of file
diff --git a/android-app/gen-external-apklibs/org.solovyev.android_android-common-db_1.0.6/gen/org/solovyev/android/db/BuildConfig.java b/android-app/gen-external-apklibs/org.solovyev.android_android-common-db_1.0.6/gen/org/solovyev/android/db/BuildConfig.java
new file mode 100644
index 00000000..3acdf88d
--- /dev/null
+++ b/android-app/gen-external-apklibs/org.solovyev.android_android-common-db_1.0.6/gen/org/solovyev/android/db/BuildConfig.java
@@ -0,0 +1,6 @@
+/** Automatically generated file. DO NOT MODIFY */
+package org.solovyev.android.db;
+
+public final class BuildConfig {
+ public final static boolean DEBUG = true;
+}
\ No newline at end of file
diff --git a/android-app/gen-external-apklibs/org.solovyev.android_android-common-db_1.0.6/gen/org/solovyev/android/db/Manifest.java b/android-app/gen-external-apklibs/org.solovyev.android_android-common-db_1.0.6/gen/org/solovyev/android/db/Manifest.java
new file mode 100644
index 00000000..dfe89650
--- /dev/null
+++ b/android-app/gen-external-apklibs/org.solovyev.android_android-common-db_1.0.6/gen/org/solovyev/android/db/Manifest.java
@@ -0,0 +1,5 @@
+package org.solovyev.android.db;
+
+/* This stub is for using by IDE only. It is NOT the Manifest class actually packed into APK */
+public final class Manifest {
+}
\ No newline at end of file
diff --git a/android-app/gen-external-apklibs/org.solovyev.android_android-common-db_1.0.6/gen/org/solovyev/android/db/R.java b/android-app/gen-external-apklibs/org.solovyev.android_android-common-db_1.0.6/gen/org/solovyev/android/db/R.java
new file mode 100644
index 00000000..aefcac0c
--- /dev/null
+++ b/android-app/gen-external-apklibs/org.solovyev.android_android-common-db_1.0.6/gen/org/solovyev/android/db/R.java
@@ -0,0 +1,5 @@
+package org.solovyev.android.db;
+
+/* This stub is for using by IDE only. It is NOT the R class actually packed into APK */
+public final class R {
+}
\ No newline at end of file
diff --git a/android-app/gen-external-apklibs/org.solovyev.android_android-common-http_1.0.6/gen/org/solovyev/android/http/BuildConfig.java b/android-app/gen-external-apklibs/org.solovyev.android_android-common-http_1.0.6/gen/org/solovyev/android/http/BuildConfig.java
new file mode 100644
index 00000000..bac2bc3b
--- /dev/null
+++ b/android-app/gen-external-apklibs/org.solovyev.android_android-common-http_1.0.6/gen/org/solovyev/android/http/BuildConfig.java
@@ -0,0 +1,6 @@
+/** Automatically generated file. DO NOT MODIFY */
+package org.solovyev.android.http;
+
+public final class BuildConfig {
+ public final static boolean DEBUG = true;
+}
\ No newline at end of file
diff --git a/android-app/gen-external-apklibs/org.solovyev.android_android-common-http_1.0.6/gen/org/solovyev/android/http/Manifest.java b/android-app/gen-external-apklibs/org.solovyev.android_android-common-http_1.0.6/gen/org/solovyev/android/http/Manifest.java
new file mode 100644
index 00000000..8f7b4624
--- /dev/null
+++ b/android-app/gen-external-apklibs/org.solovyev.android_android-common-http_1.0.6/gen/org/solovyev/android/http/Manifest.java
@@ -0,0 +1,5 @@
+package org.solovyev.android.http;
+
+/* This stub is for using by IDE only. It is NOT the Manifest class actually packed into APK */
+public final class Manifest {
+}
\ No newline at end of file
diff --git a/android-app/gen-external-apklibs/org.solovyev.android_android-common-http_1.0.6/gen/org/solovyev/android/http/R.java b/android-app/gen-external-apklibs/org.solovyev.android_android-common-http_1.0.6/gen/org/solovyev/android/http/R.java
new file mode 100644
index 00000000..8f01b351
--- /dev/null
+++ b/android-app/gen-external-apklibs/org.solovyev.android_android-common-http_1.0.6/gen/org/solovyev/android/http/R.java
@@ -0,0 +1,5 @@
+package org.solovyev.android.http;
+
+/* This stub is for using by IDE only. It is NOT the R class actually packed into APK */
+public final class R {
+}
\ No newline at end of file
diff --git a/android-app/gen-external-apklibs/org.solovyev.android_android-common-keyboard_1.0.6/gen/org/solovyev/android/keyboard/BuildConfig.java b/android-app/gen-external-apklibs/org.solovyev.android_android-common-keyboard_1.0.6/gen/org/solovyev/android/keyboard/BuildConfig.java
new file mode 100644
index 00000000..8d7e42e5
--- /dev/null
+++ b/android-app/gen-external-apklibs/org.solovyev.android_android-common-keyboard_1.0.6/gen/org/solovyev/android/keyboard/BuildConfig.java
@@ -0,0 +1,6 @@
+/** Automatically generated file. DO NOT MODIFY */
+package org.solovyev.android.keyboard;
+
+public final class BuildConfig {
+ public final static boolean DEBUG = true;
+}
\ No newline at end of file
diff --git a/android-app/gen-external-apklibs/org.solovyev.android_android-common-keyboard_1.0.6/gen/org/solovyev/android/keyboard/Manifest.java b/android-app/gen-external-apklibs/org.solovyev.android_android-common-keyboard_1.0.6/gen/org/solovyev/android/keyboard/Manifest.java
new file mode 100644
index 00000000..588f9a0a
--- /dev/null
+++ b/android-app/gen-external-apklibs/org.solovyev.android_android-common-keyboard_1.0.6/gen/org/solovyev/android/keyboard/Manifest.java
@@ -0,0 +1,5 @@
+package org.solovyev.android.keyboard;
+
+/* This stub is for using by IDE only. It is NOT the Manifest class actually packed into APK */
+public final class Manifest {
+}
\ No newline at end of file
diff --git a/android-app/gen-external-apklibs/org.solovyev.android_android-common-keyboard_1.0.6/gen/org/solovyev/android/keyboard/R.java b/android-app/gen-external-apklibs/org.solovyev.android_android-common-keyboard_1.0.6/gen/org/solovyev/android/keyboard/R.java
new file mode 100644
index 00000000..4823f3eb
--- /dev/null
+++ b/android-app/gen-external-apklibs/org.solovyev.android_android-common-keyboard_1.0.6/gen/org/solovyev/android/keyboard/R.java
@@ -0,0 +1,5 @@
+package org.solovyev.android.keyboard;
+
+/* This stub is for using by IDE only. It is NOT the R class actually packed into APK */
+public final class R {
+}
\ No newline at end of file
diff --git a/android-app/gen-external-apklibs/org.solovyev.android_android-common-list_1.0.6/gen/org/solovyev/android/list/BuildConfig.java b/android-app/gen-external-apklibs/org.solovyev.android_android-common-list_1.0.6/gen/org/solovyev/android/list/BuildConfig.java
new file mode 100644
index 00000000..f7f63a43
--- /dev/null
+++ b/android-app/gen-external-apklibs/org.solovyev.android_android-common-list_1.0.6/gen/org/solovyev/android/list/BuildConfig.java
@@ -0,0 +1,6 @@
+/** Automatically generated file. DO NOT MODIFY */
+package org.solovyev.android.list;
+
+public final class BuildConfig {
+ public final static boolean DEBUG = true;
+}
\ No newline at end of file
diff --git a/android-app/gen-external-apklibs/org.solovyev.android_android-common-list_1.0.6/gen/org/solovyev/android/list/Manifest.java b/android-app/gen-external-apklibs/org.solovyev.android_android-common-list_1.0.6/gen/org/solovyev/android/list/Manifest.java
new file mode 100644
index 00000000..b6bf0da3
--- /dev/null
+++ b/android-app/gen-external-apklibs/org.solovyev.android_android-common-list_1.0.6/gen/org/solovyev/android/list/Manifest.java
@@ -0,0 +1,5 @@
+package org.solovyev.android.list;
+
+/* This stub is for using by IDE only. It is NOT the Manifest class actually packed into APK */
+public final class Manifest {
+}
\ No newline at end of file
diff --git a/android-app/gen-external-apklibs/org.solovyev.android_android-common-list_1.0.6/gen/org/solovyev/android/list/R.java b/android-app/gen-external-apklibs/org.solovyev.android_android-common-list_1.0.6/gen/org/solovyev/android/list/R.java
new file mode 100644
index 00000000..e23eb9d0
--- /dev/null
+++ b/android-app/gen-external-apklibs/org.solovyev.android_android-common-list_1.0.6/gen/org/solovyev/android/list/R.java
@@ -0,0 +1,5 @@
+package org.solovyev.android.list;
+
+/* This stub is for using by IDE only. It is NOT the R class actually packed into APK */
+public final class R {
+}
\ No newline at end of file
diff --git a/android-app/gen-external-apklibs/org.solovyev.android_android-common-menu_1.0.6/gen/org/solovyev/android/menu/BuildConfig.java b/android-app/gen-external-apklibs/org.solovyev.android_android-common-menu_1.0.6/gen/org/solovyev/android/menu/BuildConfig.java
new file mode 100644
index 00000000..506cda16
--- /dev/null
+++ b/android-app/gen-external-apklibs/org.solovyev.android_android-common-menu_1.0.6/gen/org/solovyev/android/menu/BuildConfig.java
@@ -0,0 +1,6 @@
+/** Automatically generated file. DO NOT MODIFY */
+package org.solovyev.android.menu;
+
+public final class BuildConfig {
+ public final static boolean DEBUG = true;
+}
\ No newline at end of file
diff --git a/android-app/gen-external-apklibs/org.solovyev.android_android-common-menu_1.0.6/gen/org/solovyev/android/menu/Manifest.java b/android-app/gen-external-apklibs/org.solovyev.android_android-common-menu_1.0.6/gen/org/solovyev/android/menu/Manifest.java
new file mode 100644
index 00000000..051d5f1b
--- /dev/null
+++ b/android-app/gen-external-apklibs/org.solovyev.android_android-common-menu_1.0.6/gen/org/solovyev/android/menu/Manifest.java
@@ -0,0 +1,5 @@
+package org.solovyev.android.menu;
+
+/* This stub is for using by IDE only. It is NOT the Manifest class actually packed into APK */
+public final class Manifest {
+}
\ No newline at end of file
diff --git a/android-app/gen-external-apklibs/org.solovyev.android_android-common-menu_1.0.6/gen/org/solovyev/android/menu/R.java b/android-app/gen-external-apklibs/org.solovyev.android_android-common-menu_1.0.6/gen/org/solovyev/android/menu/R.java
new file mode 100644
index 00000000..02fbd211
--- /dev/null
+++ b/android-app/gen-external-apklibs/org.solovyev.android_android-common-menu_1.0.6/gen/org/solovyev/android/menu/R.java
@@ -0,0 +1,5 @@
+package org.solovyev.android.menu;
+
+/* This stub is for using by IDE only. It is NOT the R class actually packed into APK */
+public final class R {
+}
\ No newline at end of file
diff --git a/android-app/gen-external-apklibs/org.solovyev.android_android-common-other_1.0.6/gen/org/solovyev/android/other/BuildConfig.java b/android-app/gen-external-apklibs/org.solovyev.android_android-common-other_1.0.6/gen/org/solovyev/android/other/BuildConfig.java
new file mode 100644
index 00000000..e3bddf1c
--- /dev/null
+++ b/android-app/gen-external-apklibs/org.solovyev.android_android-common-other_1.0.6/gen/org/solovyev/android/other/BuildConfig.java
@@ -0,0 +1,6 @@
+/** Automatically generated file. DO NOT MODIFY */
+package org.solovyev.android.other;
+
+public final class BuildConfig {
+ public final static boolean DEBUG = true;
+}
\ No newline at end of file
diff --git a/android-app/gen-external-apklibs/org.solovyev.android_android-common-other_1.0.6/gen/org/solovyev/android/other/Manifest.java b/android-app/gen-external-apklibs/org.solovyev.android_android-common-other_1.0.6/gen/org/solovyev/android/other/Manifest.java
new file mode 100644
index 00000000..e8d38fb1
--- /dev/null
+++ b/android-app/gen-external-apklibs/org.solovyev.android_android-common-other_1.0.6/gen/org/solovyev/android/other/Manifest.java
@@ -0,0 +1,5 @@
+package org.solovyev.android.other;
+
+/* This stub is for using by IDE only. It is NOT the Manifest class actually packed into APK */
+public final class Manifest {
+}
\ No newline at end of file
diff --git a/android-app/gen-external-apklibs/org.solovyev.android_android-common-other_1.0.6/gen/org/solovyev/android/other/R.java b/android-app/gen-external-apklibs/org.solovyev.android_android-common-other_1.0.6/gen/org/solovyev/android/other/R.java
new file mode 100644
index 00000000..d45d2276
--- /dev/null
+++ b/android-app/gen-external-apklibs/org.solovyev.android_android-common-other_1.0.6/gen/org/solovyev/android/other/R.java
@@ -0,0 +1,5 @@
+package org.solovyev.android.other;
+
+/* This stub is for using by IDE only. It is NOT the R class actually packed into APK */
+public final class R {
+}
\ No newline at end of file
diff --git a/android-app/gen-external-apklibs/org.solovyev.android_android-common-preferences_1.0.6/gen/org/solovyev/android/prefs/BuildConfig.java b/android-app/gen-external-apklibs/org.solovyev.android_android-common-preferences_1.0.6/gen/org/solovyev/android/prefs/BuildConfig.java
new file mode 100644
index 00000000..90f5ceeb
--- /dev/null
+++ b/android-app/gen-external-apklibs/org.solovyev.android_android-common-preferences_1.0.6/gen/org/solovyev/android/prefs/BuildConfig.java
@@ -0,0 +1,6 @@
+/** Automatically generated file. DO NOT MODIFY */
+package org.solovyev.android.prefs;
+
+public final class BuildConfig {
+ public final static boolean DEBUG = true;
+}
\ No newline at end of file
diff --git a/android-app/gen-external-apklibs/org.solovyev.android_android-common-preferences_1.0.6/gen/org/solovyev/android/prefs/Manifest.java b/android-app/gen-external-apklibs/org.solovyev.android_android-common-preferences_1.0.6/gen/org/solovyev/android/prefs/Manifest.java
new file mode 100644
index 00000000..8d450965
--- /dev/null
+++ b/android-app/gen-external-apklibs/org.solovyev.android_android-common-preferences_1.0.6/gen/org/solovyev/android/prefs/Manifest.java
@@ -0,0 +1,5 @@
+package org.solovyev.android.prefs;
+
+/* This stub is for using by IDE only. It is NOT the Manifest class actually packed into APK */
+public final class Manifest {
+}
\ No newline at end of file
diff --git a/android-app/gen-external-apklibs/org.solovyev.android_android-common-preferences_1.0.6/gen/org/solovyev/android/prefs/R.java b/android-app/gen-external-apklibs/org.solovyev.android_android-common-preferences_1.0.6/gen/org/solovyev/android/prefs/R.java
new file mode 100644
index 00000000..a20f3eec
--- /dev/null
+++ b/android-app/gen-external-apklibs/org.solovyev.android_android-common-preferences_1.0.6/gen/org/solovyev/android/prefs/R.java
@@ -0,0 +1,5 @@
+package org.solovyev.android.prefs;
+
+/* This stub is for using by IDE only. It is NOT the R class actually packed into APK */
+public final class R {
+}
\ No newline at end of file
diff --git a/android-app/gen-external-apklibs/org.solovyev.android_android-common-sherlock_1.0.6/gen/org/solovyev/android/sherlock/BuildConfig.java b/android-app/gen-external-apklibs/org.solovyev.android_android-common-sherlock_1.0.6/gen/org/solovyev/android/sherlock/BuildConfig.java
new file mode 100644
index 00000000..339e5e3a
--- /dev/null
+++ b/android-app/gen-external-apklibs/org.solovyev.android_android-common-sherlock_1.0.6/gen/org/solovyev/android/sherlock/BuildConfig.java
@@ -0,0 +1,6 @@
+/** Automatically generated file. DO NOT MODIFY */
+package org.solovyev.android.sherlock;
+
+public final class BuildConfig {
+ public final static boolean DEBUG = true;
+}
\ No newline at end of file
diff --git a/android-app/gen-external-apklibs/org.solovyev.android_android-common-sherlock_1.0.6/gen/org/solovyev/android/sherlock/Manifest.java b/android-app/gen-external-apklibs/org.solovyev.android_android-common-sherlock_1.0.6/gen/org/solovyev/android/sherlock/Manifest.java
new file mode 100644
index 00000000..370c9059
--- /dev/null
+++ b/android-app/gen-external-apklibs/org.solovyev.android_android-common-sherlock_1.0.6/gen/org/solovyev/android/sherlock/Manifest.java
@@ -0,0 +1,5 @@
+package org.solovyev.android.sherlock;
+
+/* This stub is for using by IDE only. It is NOT the Manifest class actually packed into APK */
+public final class Manifest {
+}
\ No newline at end of file
diff --git a/android-app/gen-external-apklibs/org.solovyev.android_android-common-sherlock_1.0.6/gen/org/solovyev/android/sherlock/R.java b/android-app/gen-external-apklibs/org.solovyev.android_android-common-sherlock_1.0.6/gen/org/solovyev/android/sherlock/R.java
new file mode 100644
index 00000000..b8722c19
--- /dev/null
+++ b/android-app/gen-external-apklibs/org.solovyev.android_android-common-sherlock_1.0.6/gen/org/solovyev/android/sherlock/R.java
@@ -0,0 +1,5 @@
+package org.solovyev.android.sherlock;
+
+/* This stub is for using by IDE only. It is NOT the R class actually packed into APK */
+public final class R {
+}
\ No newline at end of file
diff --git a/android-app/gen-external-apklibs/org.solovyev.android_android-common-view_1.0.6/gen/org/solovyev/android/view/BuildConfig.java b/android-app/gen-external-apklibs/org.solovyev.android_android-common-view_1.0.6/gen/org/solovyev/android/view/BuildConfig.java
new file mode 100644
index 00000000..4cc1a99f
--- /dev/null
+++ b/android-app/gen-external-apklibs/org.solovyev.android_android-common-view_1.0.6/gen/org/solovyev/android/view/BuildConfig.java
@@ -0,0 +1,6 @@
+/** Automatically generated file. DO NOT MODIFY */
+package org.solovyev.android.view;
+
+public final class BuildConfig {
+ public final static boolean DEBUG = true;
+}
\ No newline at end of file
diff --git a/android-app/gen-external-apklibs/org.solovyev.android_android-common-view_1.0.6/gen/org/solovyev/android/view/Manifest.java b/android-app/gen-external-apklibs/org.solovyev.android_android-common-view_1.0.6/gen/org/solovyev/android/view/Manifest.java
new file mode 100644
index 00000000..ba4012ab
--- /dev/null
+++ b/android-app/gen-external-apklibs/org.solovyev.android_android-common-view_1.0.6/gen/org/solovyev/android/view/Manifest.java
@@ -0,0 +1,5 @@
+package org.solovyev.android.view;
+
+/* This stub is for using by IDE only. It is NOT the Manifest class actually packed into APK */
+public final class Manifest {
+}
\ No newline at end of file
diff --git a/android-app/gen-external-apklibs/org.solovyev.android_android-common-view_1.0.6/gen/org/solovyev/android/view/R.java b/android-app/gen-external-apklibs/org.solovyev.android_android-common-view_1.0.6/gen/org/solovyev/android/view/R.java
new file mode 100644
index 00000000..820d2111
--- /dev/null
+++ b/android-app/gen-external-apklibs/org.solovyev.android_android-common-view_1.0.6/gen/org/solovyev/android/view/R.java
@@ -0,0 +1,5 @@
+package org.solovyev.android.view;
+
+/* This stub is for using by IDE only. It is NOT the R class actually packed into APK */
+public final class R {
+}
\ No newline at end of file
diff --git a/android-app/gen-external-apklibs/org.solovyev.android_android-common-view_1.0.6/src/org/solovyev/android/LastTabSaver.java b/android-app/gen-external-apklibs/org.solovyev.android_android-common-view_1.0.6/src/org/solovyev/android/LastTabSaver.java
new file mode 100644
index 00000000..01b48941
--- /dev/null
+++ b/android-app/gen-external-apklibs/org.solovyev.android_android-common-view_1.0.6/src/org/solovyev/android/LastTabSaver.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright (c) 2009-2012. Created by serso aka se.solovyev.
+ * For more information, please, contact se.solovyev@gmail.com
+ * or visit http://se.solovyev.org
+ */
+
+package org.solovyev.android;
+
+import android.app.TabActivity;
+import android.preference.PreferenceManager;
+import android.widget.TabHost;
+import org.jetbrains.annotations.NotNull;
+import org.solovyev.android.prefs.StringPreference;
+
+/**
+ * User: serso
+ * Date: 1/9/12
+ * Time: 6:17 PM
+ */
+
+/**
+ * This class saves the last opened tab in the tabHost to the SharedPreferences in order to reopen it after closing
+ * The tab is defined by it's tab id and tab activity where it is hold => you can use the same tab ids in different tab activities
+ */
+public class LastTabSaver implements TabHost.OnTabChangeListener {
+
+ // prefix of preference's key
+ private static final String LAST_OPENED_TAB_P_KEY = "last_opened_tab_";
+
+ // preference object
+ @NotNull
+ private final StringPreference preference;
+
+ // activity that holds tab host
+ @NotNull
+ private final TabActivity tabActivity;
+
+ /**
+ * Constructor applies saved preference value on tabHost returned by android.app.TabActivity#getTabHost() method
+ * and registers as onTabChangeListener
+ *
+ * @param tabActivity tab activity
+ * @param defaultTabId default tab (if no preference value is not defined)
+ */
+ public LastTabSaver(@NotNull TabActivity tabActivity, @NotNull String defaultTabId) {
+ this.tabActivity = tabActivity;
+ this.preference = StringPreference.newInstance(getPreferenceKey(), defaultTabId);
+
+ final TabHost tabHost = tabActivity.getTabHost();
+ tabHost.setCurrentTabByTag(this.getLastOpenedTabId());
+ tabHost.setOnTabChangedListener(this);
+ }
+
+ /**
+ * Method must be invoked on android.app.Activity#onDestroy() method of tab activity
+ */
+ public void destroy() {
+ final TabHost tabHost = tabActivity.getTabHost();
+ tabHost.setOnTabChangedListener(null);
+ }
+
+ @Override
+ public void onTabChanged(String tabId) {
+ preference.putPreference(PreferenceManager.getDefaultSharedPreferences(tabActivity), tabId);
+ }
+
+ @NotNull
+ public String getLastOpenedTabId() {
+ return preference.getPreference(PreferenceManager.getDefaultSharedPreferences(tabActivity));
+ }
+
+ @NotNull
+ private String getPreferenceKey() {
+ return LAST_OPENED_TAB_P_KEY + tabActivity.getClass().getName();
+ }
+}
diff --git a/android-app/gen-external-apklibs/org.solovyev.android_android-common-view_1.0.6/src/org/solovyev/android/history/HistoryDragProcessor.java b/android-app/gen-external-apklibs/org.solovyev.android_android-common-view_1.0.6/src/org/solovyev/android/history/HistoryDragProcessor.java
new file mode 100644
index 00000000..d9a49fd5
--- /dev/null
+++ b/android-app/gen-external-apklibs/org.solovyev.android_android-common-view_1.0.6/src/org/solovyev/android/history/HistoryDragProcessor.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2009-2011. Created by serso aka se.solovyev.
+ * For more information, please, contact se.solovyev@gmail.com
+ */
+
+package org.solovyev.android.history;
+
+import android.util.Log;
+import android.view.MotionEvent;
+import org.jetbrains.annotations.NotNull;
+import org.solovyev.android.view.drag.DragButton;
+import org.solovyev.android.view.drag.DragDirection;
+import org.solovyev.android.view.drag.SimpleOnDragListener;
+import org.solovyev.common.history.HistoryAction;
+import org.solovyev.common.history.HistoryControl;
+import org.solovyev.common.math.Point2d;
+
+/**
+* User: serso
+* Date: 9/16/11
+* Time: 11:36 PM
+*/
+public class HistoryDragProcessor implements SimpleOnDragListener.DragProcessor {
+
+ @NotNull
+ private final HistoryControl historyControl;
+
+ public HistoryDragProcessor(@NotNull HistoryControl historyControl) {
+ this.historyControl = historyControl;
+ }
+
+ @Override
+ public boolean processDragEvent(@NotNull DragDirection dragDirection, @NotNull DragButton dragButton, @NotNull Point2d startPoint2d, @NotNull MotionEvent motionEvent) {
+ boolean result = false;
+
+ Log.d(String.valueOf(dragButton.getId()), "History on drag event start: " + dragDirection);
+
+ final HistoryAction historyAction;
+ if ( dragDirection == DragDirection.up ) {
+ historyAction = HistoryAction.undo;
+ } else if ( dragDirection == DragDirection.down ) {
+ historyAction = HistoryAction.redo;
+ } else {
+ historyAction = null;
+ }
+
+ if (historyAction != null) {
+ result = true;
+ historyControl.doHistoryAction(historyAction);
+ }
+
+ return result;
+ }
+}
diff --git a/android-app/gen-external-apklibs/org.solovyev.android_android-common-view_1.0.6/src/org/solovyev/android/prefs/AbstractEnumPickerDialogPreference.java b/android-app/gen-external-apklibs/org.solovyev.android_android-common-view_1.0.6/src/org/solovyev/android/prefs/AbstractEnumPickerDialogPreference.java
new file mode 100644
index 00000000..3ed7a8ed
--- /dev/null
+++ b/android-app/gen-external-apklibs/org.solovyev.android_android-common-view_1.0.6/src/org/solovyev/android/prefs/AbstractEnumPickerDialogPreference.java
@@ -0,0 +1,39 @@
+package org.solovyev.android.prefs;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+import org.solovyev.android.Labeled;
+import org.solovyev.android.LabeledFormatter;
+import org.solovyev.android.view.ListRange;
+import org.solovyev.android.view.Picker;
+import org.solovyev.common.text.EnumMapper;
+
+import java.util.Arrays;
+
+/**
+ * User: serso
+ * Date: 8/10/12
+ * Time: 2:02 AM
+ */
+public abstract class AbstractEnumPickerDialogPreference extends AbstractPickerDialogPreference {
+
+ @NotNull
+ private final Class enumClass;
+
+ protected AbstractEnumPickerDialogPreference(Context context,
+ AttributeSet attrs,
+ @Nullable String defaultStringValue,
+ boolean needValueText,
+ @NotNull Class enumClass) {
+ super(context, attrs, defaultStringValue, needValueText, new EnumMapper(enumClass));
+ this.enumClass = enumClass;
+ }
+
+ @NotNull
+ @Override
+ protected Picker.Range createRange(@NotNull T selected) {
+ return new ListRange(Arrays.asList(enumClass.getEnumConstants()), selected, new LabeledFormatter(getContext()));
+ }
+}
diff --git a/android-app/gen-external-apklibs/org.solovyev.android_android-common-view_1.0.6/src/org/solovyev/android/prefs/AbstractPickerDialogPreference.java b/android-app/gen-external-apklibs/org.solovyev.android_android-common-view_1.0.6/src/org/solovyev/android/prefs/AbstractPickerDialogPreference.java
new file mode 100644
index 00000000..66ef429e
--- /dev/null
+++ b/android-app/gen-external-apklibs/org.solovyev.android_android-common-view_1.0.6/src/org/solovyev/android/prefs/AbstractPickerDialogPreference.java
@@ -0,0 +1,62 @@
+package org.solovyev.android.prefs;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.Gravity;
+import android.view.View;
+import android.widget.LinearLayout;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+import org.solovyev.android.view.Picker;
+import org.solovyev.common.text.Mapper;
+
+/**
+ * User: serso
+ * Date: 8/10/12
+ * Time: 1:58 AM
+ */
+public abstract class AbstractPickerDialogPreference extends AbstractDialogPreference implements Picker.OnChangedListener {
+
+
+ protected AbstractPickerDialogPreference(Context context,
+ AttributeSet attrs,
+ @Nullable String defaultStringValue,
+ boolean needValueText,
+ @NotNull Mapper mapper) {
+ super(context, attrs, defaultStringValue, needValueText, mapper);
+ }
+
+ @Override
+ protected LinearLayout.LayoutParams getParams() {
+ final LinearLayout.LayoutParams result = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT);
+
+ result.gravity = Gravity.CENTER;
+
+ return result;
+ }
+
+ @NotNull
+ @Override
+ protected View createPreferenceView(@NotNull Context context) {
+ final Picker result = new Picker(context);
+
+ result.setOnChangeListener(this);
+
+ return result;
+ }
+
+ @Override
+ protected void initPreferenceView(@NotNull View v, @Nullable T value) {
+ if (value != null) {
+ ((Picker) v).setRange(createRange(value));
+ }
+ }
+
+ @NotNull
+ protected abstract Picker.Range createRange(@NotNull T selected);
+
+ @Override
+ public void onChanged(@NotNull Picker picker, @NotNull T o) {
+ persistValue(o);
+ }
+}
diff --git a/android-app/gen-external-apklibs/org.solovyev.android_android-common-view_1.0.6/src/org/solovyev/android/prefs/DoublePickerDialogPreference.java b/android-app/gen-external-apklibs/org.solovyev.android_android-common-view_1.0.6/src/org/solovyev/android/prefs/DoublePickerDialogPreference.java
new file mode 100644
index 00000000..f8c5ee8d
--- /dev/null
+++ b/android-app/gen-external-apklibs/org.solovyev.android_android-common-view_1.0.6/src/org/solovyev/android/prefs/DoublePickerDialogPreference.java
@@ -0,0 +1,67 @@
+package org.solovyev.android.prefs;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+import org.solovyev.android.view.DoubleRange;
+import org.solovyev.android.view.NumberRange;
+import org.solovyev.common.interval.Interval;
+import org.solovyev.common.text.Formatter;
+import org.solovyev.common.text.NumberIntervalMapper;
+
+import java.text.DecimalFormat;
+
+/**
+ * User: serso
+ * Date: 8/10/12
+ * Time: 1:02 AM
+ */
+public class DoublePickerDialogPreference extends NumberPickerDialogPreference {
+
+ @NotNull
+ private static final Formatter simpleFormatter = new DoubleFormatter(2);
+
+ @NotNull
+ @Override
+ protected Double getDefaultStep() {
+ return 1d;
+ }
+
+ @NotNull
+ @Override
+ protected NumberRange createRange(@NotNull Interval boundaries, @NotNull Double step, @NotNull Double selected) {
+ return DoubleRange.newInstance(boundaries.getLeftLimit(), boundaries.getRightLimit(), step, selected, simpleFormatter);
+ }
+
+ public DoublePickerDialogPreference(Context context, AttributeSet attrs) {
+ super(context, attrs, new NumberIntervalMapper(Double.class));
+ }
+
+ private static class DoubleFormatter implements Formatter {
+
+ @NotNull
+ private final DecimalFormat df;
+
+ private DoubleFormatter(int numberOfDecimalDigits) {
+ final StringBuilder sb = new StringBuilder("#.#");
+ for (int i = 1; i < numberOfDecimalDigits; i++) {
+ sb.append("#");
+ }
+
+ this.df = new DecimalFormat(sb.toString());
+ }
+
+ @Override
+ public String formatValue(@Nullable Double value) throws IllegalArgumentException {
+
+ if (value == null) {
+ return "null";
+ } else {
+ synchronized (df) {
+ return df.format(value);
+ }
+ }
+ }
+ }
+}
diff --git a/android-app/gen-external-apklibs/org.solovyev.android_android-common-view_1.0.6/src/org/solovyev/android/prefs/FloatRangeSeekBarPreference.java b/android-app/gen-external-apklibs/org.solovyev.android_android-common-view_1.0.6/src/org/solovyev/android/prefs/FloatRangeSeekBarPreference.java
new file mode 100644
index 00000000..cfb11a47
--- /dev/null
+++ b/android-app/gen-external-apklibs/org.solovyev.android_android-common-view_1.0.6/src/org/solovyev/android/prefs/FloatRangeSeekBarPreference.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2009-2011. Created by serso aka se.solovyev.
+ * For more information, please, contact se.solovyev@gmail.com
+ * or visit http://se.solovyev.org
+ */
+
+package org.solovyev.android.prefs;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import org.jetbrains.annotations.NotNull;
+import org.solovyev.common.text.NumberIntervalMapper;
+
+/**
+ * User: serso
+ * Date: 9/21/11
+ * Time: 11:41 PM
+ */
+public class FloatRangeSeekBarPreference extends RangeSeekBarPreference {
+
+ public FloatRangeSeekBarPreference(@NotNull Context context, AttributeSet attrs) {
+ super(context, attrs, new NumberIntervalMapper(Float.class));
+ }
+
+ @NotNull
+ @Override
+ protected Float getDefaultStep() {
+ return 1f;
+ }
+
+ @NotNull
+ @Override
+ protected Float add(@NotNull Float l, @NotNull Float r) {
+ return l + r;
+ }
+}
diff --git a/android-app/gen-external-apklibs/org.solovyev.android_android-common-view_1.0.6/src/org/solovyev/android/prefs/IntegerPickerDialogPreference.java b/android-app/gen-external-apklibs/org.solovyev.android_android-common-view_1.0.6/src/org/solovyev/android/prefs/IntegerPickerDialogPreference.java
new file mode 100644
index 00000000..d7ba7f8e
--- /dev/null
+++ b/android-app/gen-external-apklibs/org.solovyev.android_android-common-view_1.0.6/src/org/solovyev/android/prefs/IntegerPickerDialogPreference.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2009-2011. Created by serso aka se.solovyev.
+ * For more information, please, contact se.solovyev@gmail.com
+ * or visit http://se.solovyev.org
+ */
+
+package org.solovyev.android.prefs;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import org.jetbrains.annotations.NotNull;
+import org.solovyev.android.view.IntegerRange;
+import org.solovyev.android.view.NumberRange;
+import org.solovyev.common.interval.Interval;
+import org.solovyev.common.text.NumberIntervalMapper;
+
+/**
+ * User: serso
+ * Date: 9/26/11
+ * Time: 10:31 PM
+ */
+public class IntegerPickerDialogPreference extends NumberPickerDialogPreference{
+
+ public IntegerPickerDialogPreference(Context context, AttributeSet attrs) {
+ super(context, attrs, new NumberIntervalMapper(Integer.class));
+ }
+
+ @NotNull
+ @Override
+ protected Integer getDefaultStep() {
+ return 1;
+ }
+
+ @NotNull
+ @Override
+ protected NumberRange createRange(@NotNull Interval boundaries, @NotNull Integer step, @NotNull Integer selected) {
+ return IntegerRange.newInstance(boundaries.getLeftLimit(), boundaries.getRightLimit(), step, selected);
+ }
+}
diff --git a/android-app/gen-external-apklibs/org.solovyev.android_android-common-view_1.0.6/src/org/solovyev/android/prefs/IntegerRangeSeekBarPreference.java b/android-app/gen-external-apklibs/org.solovyev.android_android-common-view_1.0.6/src/org/solovyev/android/prefs/IntegerRangeSeekBarPreference.java
new file mode 100644
index 00000000..9f948018
--- /dev/null
+++ b/android-app/gen-external-apklibs/org.solovyev.android_android-common-view_1.0.6/src/org/solovyev/android/prefs/IntegerRangeSeekBarPreference.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2009-2011. Created by serso aka se.solovyev.
+ * For more information, please, contact se.solovyev@gmail.com
+ * or visit http://se.solovyev.org
+ */
+
+package org.solovyev.android.prefs;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import org.jetbrains.annotations.NotNull;
+import org.solovyev.common.text.NumberIntervalMapper;
+
+/**
+ * User: serso
+ * Date: 9/19/11
+ * Time: 10:04 PM
+ */
+public class IntegerRangeSeekBarPreference extends RangeSeekBarPreference {
+
+ public IntegerRangeSeekBarPreference(@NotNull Context context, AttributeSet attrs) {
+ super(context, attrs, new NumberIntervalMapper(Integer.class));
+ }
+
+ @NotNull
+ @Override
+ protected Integer getDefaultStep() {
+ return 1;
+ }
+
+ @NotNull
+ @Override
+ protected Integer add(@NotNull Integer l, @NotNull Integer r) {
+ return l + r;
+ }
+}
diff --git a/android-app/gen-external-apklibs/org.solovyev.android_android-common-view_1.0.6/src/org/solovyev/android/prefs/MultiSelectListPreference.java b/android-app/gen-external-apklibs/org.solovyev.android_android-common-view_1.0.6/src/org/solovyev/android/prefs/MultiSelectListPreference.java
new file mode 100644
index 00000000..3180fdda
--- /dev/null
+++ b/android-app/gen-external-apklibs/org.solovyev.android_android-common-view_1.0.6/src/org/solovyev/android/prefs/MultiSelectListPreference.java
@@ -0,0 +1,196 @@
+package org.solovyev.android.prefs;
+
+
+import android.app.AlertDialog.Builder;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.res.TypedArray;
+import android.preference.ListPreference;
+import android.preference.Preference;
+import android.util.AttributeSet;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+import org.solovyev.android.view.R;
+import org.solovyev.common.text.CollectionTransformations;
+import org.solovyev.common.text.StringMapper;
+import org.solovyev.common.text.StringUtils;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * A {@link Preference} that displays a list of entries as
+ * a dialog and allows multiple selections
+ *
+ * This preference will store a string into the SharedPreferences. This string will be the values selected
+ * from the {@link #setEntryValues(CharSequence[])} array.
+ *
+ */
+public class MultiSelectListPreference extends ListPreference {
+
+ @NotNull
+ private static final String DEFAULT_SEPARATOR = ";";
+
+ @NotNull
+ private final org.solovyev.common.text.Mapper> mapper;
+
+ private boolean[] checkedIndices;
+
+ /*
+ **********************************************************************
+ *
+ * CONSTRUCTORS
+ *
+ **********************************************************************
+ */
+
+ public MultiSelectListPreference(Context context) {
+ this(context, null);
+ }
+
+ public MultiSelectListPreference(Context context, @Nullable AttributeSet attrs) {
+ super(context, attrs);
+
+ String separator = DEFAULT_SEPARATOR;
+
+ final TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.MultiSelectListPreference);
+ for (int i = 0; i < a.getIndexCount(); i++) {
+ int attr = a.getIndex(i);
+
+ final String attrValue = a.getString(attr);
+
+ if (!StringUtils.isEmpty(attrValue)) {
+ switch (attr) {
+ case R.styleable.MultiSelectListPreference_separator:
+ separator = attrValue;
+ break;
+ }
+ }
+ }
+
+ this.mapper = new Mapper(separator);
+
+ this.checkedIndices = new boolean[getEntries().length];
+ }
+
+ @Override
+ public void setEntries(@NotNull CharSequence[] entries) {
+ super.setEntries(entries);
+
+ checkedIndices = new boolean[entries.length];
+ }
+
+ @Override
+ protected void onPrepareDialogBuilder(@NotNull Builder builder) {
+ final CharSequence[] entries = getEntries();
+ final CharSequence[] entryValues = getEntryValues();
+
+ if (entries == null || entryValues == null || entries.length != entryValues.length) {
+ throw new IllegalStateException("ListPreference requires an entries array and an entryValues array which are both the same length");
+ }
+
+ restoreCheckedEntries();
+
+ builder.setMultiChoiceItems(entries, checkedIndices,
+ new DialogInterface.OnMultiChoiceClickListener() {
+ public void onClick(DialogInterface dialog, int which, boolean value) {
+ checkedIndices[which] = value;
+ }
+ });
+ }
+
+
+ private void restoreCheckedEntries() {
+ final CharSequence[] entryValues = getEntryValues();
+
+ final List values = mapper.parseValue(getValue());
+ if (values != null) {
+ for (String value : values) {
+ for (int i = 0; i < entryValues.length; i++) {
+ final CharSequence entry = entryValues[i];
+ if (entry.equals(value)) {
+ checkedIndices[i] = true;
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ @Override
+ protected void onDialogClosed(boolean positiveResult) {
+ final CharSequence[] entryValues = getEntryValues();
+ if (positiveResult && entryValues != null) {
+
+ final List checkedValues = new ArrayList();
+ for (int i = 0; i < entryValues.length; i++) {
+ if (checkedIndices[i]) {
+ checkedValues.add(entryValues[i].toString());
+ }
+ }
+
+
+ final String value = mapper.formatValue(checkedValues);
+ if (callChangeListener(value)) {
+ setValue(value);
+ }
+ }
+ }
+
+ public static class Mapper implements org.solovyev.common.text.Mapper> {
+
+ @NotNull
+ private final String separator;
+
+ public Mapper(@NotNull String separator) {
+ this.separator = separator;
+ }
+
+ @Override
+ public String formatValue(@Nullable List value) throws IllegalArgumentException {
+ return CollectionTransformations.formatValue(value, separator, StringMapper.getInstance());
+ }
+
+ @Override
+ public List parseValue(@Nullable String value) throws IllegalArgumentException {
+ return CollectionTransformations.split(value, separator, StringMapper.getInstance());
+ }
+ }
+
+ @NotNull
+ public static org.solovyev.common.text.Mapper> newListMapper(@NotNull org.solovyev.common.text.Mapper nestedMapper) {
+ return new ListMapper(DEFAULT_SEPARATOR, nestedMapper);
+ }
+
+ @NotNull
+ public static org.solovyev.common.text.Mapper> newListMapper(@NotNull org.solovyev.common.text.Mapper nestedMapper,
+ @NotNull String separator) {
+ return new ListMapper(separator, nestedMapper);
+ }
+
+
+ private static class ListMapper implements org.solovyev.common.text.Mapper> {
+
+ @NotNull
+ private final String separator;
+
+ @NotNull
+ private final org.solovyev.common.text.Mapper nestedMapper;
+
+ public ListMapper(@NotNull String separator, @NotNull org.solovyev.common.text.Mapper nestedMapper) {
+ this.separator = separator;
+ this.nestedMapper = nestedMapper;
+ }
+
+ @Override
+ public String formatValue(@Nullable List value) throws IllegalArgumentException {
+ return CollectionTransformations.formatValue(value, separator, nestedMapper);
+ }
+
+ @Override
+ public List parseValue(@Nullable String value) throws IllegalArgumentException {
+ return CollectionTransformations.split(value, separator, nestedMapper);
+ }
+ }
+}
+
diff --git a/android-app/gen-external-apklibs/org.solovyev.android_android-common-view_1.0.6/src/org/solovyev/android/prefs/NumberPickerDialogPreference.java b/android-app/gen-external-apklibs/org.solovyev.android_android-common-view_1.0.6/src/org/solovyev/android/prefs/NumberPickerDialogPreference.java
new file mode 100644
index 00000000..91721331
--- /dev/null
+++ b/android-app/gen-external-apklibs/org.solovyev.android_android-common-view_1.0.6/src/org/solovyev/android/prefs/NumberPickerDialogPreference.java
@@ -0,0 +1,52 @@
+package org.solovyev.android.prefs;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import org.jetbrains.annotations.NotNull;
+import org.solovyev.android.view.NumberRange;
+import org.solovyev.android.view.Picker;
+import org.solovyev.common.interval.Interval;
+import org.solovyev.common.text.NumberIntervalMapper;
+
+/**
+ * User: serso
+ * Date: 8/10/12
+ * Time: 12:57 AM
+ */
+public abstract class NumberPickerDialogPreference> extends AbstractPickerDialogPreference {
+
+ @NotNull
+ private final Interval boundaries;
+
+ @NotNull
+ private final N step;
+
+ protected NumberPickerDialogPreference(Context context,
+ AttributeSet attrs,
+ @NotNull NumberIntervalMapper mapper) {
+ super(context, attrs, null, false, mapper.getMapper());
+
+ //noinspection ConstantConditions
+ boundaries = mapper.parseValue(attrs.getAttributeValue(AbstractDialogPreference.localNameSpace, "boundaries"));
+
+ final String stringStep = attrs.getAttributeValue(AbstractDialogPreference.localNameSpace, "step");
+ if (stringStep == null) {
+ step = getDefaultStep();
+ } else {
+ step = mapper.getMapper().parseValue(stringStep);
+ }
+ }
+
+ @NotNull
+ protected abstract N getDefaultStep();
+
+ @NotNull
+ @Override
+ protected Picker.Range createRange(@NotNull N selected) {
+ return createRange(boundaries, step, selected);
+ }
+
+ @NotNull
+ protected abstract NumberRange createRange(@NotNull Interval boundaries, @NotNull N step, @NotNull N selected);
+
+}
diff --git a/android-app/gen-external-apklibs/org.solovyev.android_android-common-view_1.0.6/src/org/solovyev/android/prefs/RangeSeekBarPreference.java b/android-app/gen-external-apklibs/org.solovyev.android_android-common-view_1.0.6/src/org/solovyev/android/prefs/RangeSeekBarPreference.java
new file mode 100644
index 00000000..0abc8de4
--- /dev/null
+++ b/android-app/gen-external-apklibs/org.solovyev.android_android-common-view_1.0.6/src/org/solovyev/android/prefs/RangeSeekBarPreference.java
@@ -0,0 +1,92 @@
+package org.solovyev.android.prefs;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.View;
+import android.widget.LinearLayout;
+import org.jetbrains.annotations.NotNull;
+import org.solovyev.android.view.AbstractRangeSeekBar;
+import org.solovyev.android.view.NumberRangeSeekBar;
+import org.solovyev.common.interval.Interval;
+import org.solovyev.common.interval.IntervalImpl;
+import org.solovyev.common.text.NumberIntervalMapper;
+
+/**
+ * User: serso
+ * Date: 9/19/11
+ * Time: 12:27 PM
+ */
+public abstract class RangeSeekBarPreference> extends AbstractDialogPreference> implements AbstractRangeSeekBar.OnRangeSeekBarChangeListener {
+
+ @NotNull
+ private final Interval boundaries;
+
+ @NotNull
+ private final T step;
+
+ public RangeSeekBarPreference(@NotNull Context context, AttributeSet attrs, @NotNull NumberIntervalMapper mapper) {
+ super(context, attrs, null, true, mapper);
+
+ //noinspection ConstantConditions
+ boundaries = mapper.parseValue(attrs.getAttributeValue(AbstractDialogPreference.localNameSpace, "boundaries"));
+
+ final String stepValue = attrs.getAttributeValue(AbstractDialogPreference.localNameSpace, "step");
+ if ( stepValue == null ) {
+ step = getDefaultStep();
+ } else {
+ step = mapper.getMapper().parseValue(stepValue);
+ }
+
+ }
+
+ @NotNull
+ protected abstract T getDefaultStep();
+
+ @NotNull
+ protected View createPreferenceView(@NotNull Context context) {
+ int count = 0;
+ for ( T t = boundaries.getLeftLimit(); t.compareTo(boundaries.getRightLimit()) <= 0; t = add(t, step) ) {
+ count += 1;
+ }
+ final NumberRangeSeekBar result = new NumberRangeSeekBar(boundaries, count, context);
+
+ result.setNotifyWhileDragging(true);
+ result.setOnRangeSeekBarChangeListener(this);
+
+ return result;
+ }
+
+ @NotNull
+ protected abstract T add(@NotNull T l, @NotNull T r);
+
+ @Override
+ protected LinearLayout.LayoutParams getParams() {
+ return null;
+ }
+
+ @Override
+ protected void initPreferenceView(@NotNull View v, Interval value) {
+ if (value != null) {
+ ((NumberRangeSeekBar) v).setSelectedMinValue(value.getLeftLimit());
+ ((NumberRangeSeekBar) v).setSelectedMaxValue(value.getRightLimit());
+ setValueText(value);
+ }
+ }
+
+ @Override
+ public void rangeSeekBarValuesChanged(T minValue, T maxValue, boolean changeComplete) {
+ final Interval interval = IntervalImpl.newClosed(minValue, maxValue);
+
+ if (changeComplete) {
+ persistValue(interval);
+ }
+
+ setValueText(interval);
+ }
+
+ private void setValueText(@NotNull Interval interval) {
+ final String t = String.valueOf(interval);
+ final String valueText = getValueText();
+ updateValueText(valueText == null ? t : t.concat(valueText));
+ }
+}
diff --git a/android-app/gen-external-apklibs/org.solovyev.android_android-common-view_1.0.6/src/org/solovyev/android/view/AbstractAlertDialogBuilder.java b/android-app/gen-external-apklibs/org.solovyev.android_android-common-view_1.0.6/src/org/solovyev/android/view/AbstractAlertDialogBuilder.java
new file mode 100644
index 00000000..63d34a29
--- /dev/null
+++ b/android-app/gen-external-apklibs/org.solovyev.android_android-common-view_1.0.6/src/org/solovyev/android/view/AbstractAlertDialogBuilder.java
@@ -0,0 +1,25 @@
+package org.solovyev.android.view;
+
+import android.app.AlertDialog;
+import android.content.Context;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * User: serso
+ * Date: 8/2/12
+ * Time: 4:25 PM
+ */
+public abstract class AbstractAlertDialogBuilder implements DialogBuilder {
+
+ @NotNull
+ private Context context;
+
+ protected AbstractAlertDialogBuilder(@NotNull Context context) {
+ this.context = context;
+ }
+
+ @NotNull
+ protected Context getContext() {
+ return context;
+ }
+}
diff --git a/android-app/gen-external-apklibs/org.solovyev.android_android-common-view_1.0.6/src/org/solovyev/android/view/AbstractRangeSeekBar.java b/android-app/gen-external-apklibs/org.solovyev.android_android-common-view_1.0.6/src/org/solovyev/android/view/AbstractRangeSeekBar.java
new file mode 100644
index 00000000..17871f26
--- /dev/null
+++ b/android-app/gen-external-apklibs/org.solovyev.android_android-common-view_1.0.6/src/org/solovyev/android/view/AbstractRangeSeekBar.java
@@ -0,0 +1,421 @@
+package org.solovyev.android.view;
+
+/**
+ * User: serso
+ * Date: 9/19/11
+ * Time: 3:30 PM
+ */
+
+import android.content.Context;
+import android.graphics.*;
+import android.graphics.Paint.Style;
+import android.view.MotionEvent;
+import android.widget.ImageView;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+import org.solovyev.common.Converter;
+import org.solovyev.common.math.LinearNormalizer;
+import org.solovyev.common.math.Normalizer;
+
+/**
+ * Widget that lets users select a minimum and maximum value on a given numerical range.
+ * The range value types can be one of Long, Double, Integer, Float, Short, Byte or BigDecimal.
+ *
+ * @param The Number type of the range values. One of Long, Double, Integer, Float, Short, Byte or BigDecimal.
+ * @author Stephan Tittel (stephan.tittel@kom.tu-darmstadt.de)
+ */
+public abstract class AbstractRangeSeekBar extends ImageView {
+
+ @NotNull
+ private final Paint paint = new Paint();
+
+ @NotNull
+ private final ThumbContainer tc;
+
+ @NotNull
+ private final Converter toDoubleConverter;
+
+ @NotNull
+ private final Converter toTConverter;
+
+ @NotNull
+ private final T minValue, maxValue;
+
+ @NotNull
+ private final Normalizer fromValueNormalizer;
+
+ @NotNull
+ private final Normalizer fromScreenNormalizer;
+
+ private double normalizedMinValue = 0d;
+
+ private double normalizedMaxValue = 1d;
+
+ private Thumb pressedThumb = null;
+
+ private boolean notifyWhileDragging = false;
+
+
+ @Nullable
+ private OnRangeSeekBarChangeListener listener;
+
+ /**
+ * Creates a new RangeSeekBar.
+ *
+ * @param minValue The minimum value of the selectable range.
+ * @param maxValue The maximum value of the selectable range.
+ * @param steps number of steps to be used in range seek bar
+ * @param context parent context
+ * @throws IllegalArgumentException Will be thrown if min/max value types are not one of Long, Double, Integer, Float, Short, Byte or BigDecimal.
+ */
+ public AbstractRangeSeekBar(@NotNull T minValue, @NotNull T maxValue, @Nullable Integer steps, Context context) throws IllegalArgumentException {
+ super(context);
+
+ this.minValue = minValue;
+ this.maxValue = maxValue;
+
+ this.toDoubleConverter = getToDoubleConverter();
+ this.toTConverter = getToTConverter();
+
+ fromValueNormalizer = new LinearNormalizer(toDoubleConverter.convert(minValue), toDoubleConverter.convert(maxValue));
+
+ tc = new ThumbContainer();
+
+ fromScreenNormalizer = new Normalizer() {
+ @Override
+ public double normalize(double value) {
+ int width = getWidth();
+ if (width <= 2 * tc.padding) {
+ // prevent division by zero, simply return 0.
+ return 0d;
+ } else {
+ double result = (value - tc.padding) / (width - 2 * tc.padding);
+ return Math.min(1d, Math.max(0d, result));
+ }
+ }
+
+ @Override
+ public double denormalize(double value) {
+ return (float) (tc.padding + value * (getWidth() - 2 * tc.padding));
+ }
+ };
+ }
+
+ @NotNull
+ protected abstract Converter getToTConverter();
+
+ @NotNull
+ protected abstract Converter getToDoubleConverter();
+
+ public boolean isNotifyWhileDragging() {
+ return notifyWhileDragging;
+ }
+
+ /**
+ * Should the widget notify the listener callback while the user is still dragging a thumb? Default is false.
+ *
+ * @param flag
+ */
+ public void setNotifyWhileDragging(boolean flag) {
+ this.notifyWhileDragging = flag;
+ }
+
+ /**
+ * Returns the absolute minimum value of the range that has been set at construction time.
+ *
+ * @return The absolute minimum value of the range.
+ */
+ @NotNull
+ public T getMinValue() {
+ return minValue;
+ }
+
+ /**
+ * Returns the absolute maximum value of the range that has been set at construction time.
+ *
+ * @return The absolute maximum value of the range.
+ */
+ @NotNull
+ public T getMaxValue() {
+ return maxValue;
+ }
+
+ /**
+ * Returns the currently selected min value.
+ *
+ * @return The currently selected min value.
+ */
+ public T getSelectedMinValue() {
+ return denormalizeValue(normalizedMinValue);
+ }
+
+ /**
+ * Sets the currently selected minimum value. The widget will be invalidated and redrawn.
+ *
+ * @param value The Number value to set the minimum value to. Will be clamped to given absolute minimum/maximum range.
+ */
+ public void setSelectedMinValue(@NotNull T value) {
+ setNormalizedMinValue(normalizeValue(value));
+ }
+
+ /**
+ * Returns the currently selected max value.
+ *
+ * @return The currently selected max value.
+ */
+ public T getSelectedMaxValue() {
+ return denormalizeValue(normalizedMaxValue);
+ }
+
+ /**
+ * Sets the currently selected maximum value. The widget will be invalidated and redrawn.
+ *
+ * @param value The Number value to set the maximum value to. Will be clamped to given absolute minimum/maximum range.
+ */
+ public void setSelectedMaxValue(@NotNull T value) {
+ setNormalizedMaxValue(normalizeValue(value));
+ }
+
+ /**
+ * Registers given listener callback to notify about changed selected values.
+ *
+ * @param listener The listener to notify about changed selected values.
+ */
+ public void setOnRangeSeekBarChangeListener(OnRangeSeekBarChangeListener listener) {
+ this.listener = listener;
+ }
+
+ /**
+ * Handles thumb selection and movement. Notifies listener callback on certain events.
+ */
+ @Override
+ public boolean onTouchEvent(MotionEvent event) {
+ switch (event.getAction()) {
+ case MotionEvent.ACTION_DOWN:
+ pressedThumb = evalPressedThumb(event.getX());
+ invalidate();
+ break;
+ case MotionEvent.ACTION_MOVE:
+ if (pressedThumb != null) {
+
+ double value = convertToNormalizedValue(event.getX());
+
+ if (Thumb.MIN.equals(pressedThumb)) {
+ setNormalizedMinValue(value);
+ } else if (Thumb.MAX.equals(pressedThumb)) {
+ setNormalizedMaxValue(value);
+ }
+
+ if (notifyWhileDragging && listener != null) {
+ listener.rangeSeekBarValuesChanged(getSelectedMinValue(), getSelectedMaxValue(), false);
+ }
+ }
+ break;
+ case MotionEvent.ACTION_UP:
+ case MotionEvent.ACTION_CANCEL:
+ pressedThumb = null;
+ invalidate();
+ if (listener != null) {
+ listener.rangeSeekBarValuesChanged(getSelectedMinValue(), getSelectedMaxValue(), true);
+ }
+ break;
+ }
+ return true;
+ }
+
+ /**
+ * Ensures correct size of the widget.
+ */
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ int width = 200;
+ if (MeasureSpec.UNSPECIFIED != MeasureSpec.getMode(widthMeasureSpec)) {
+ width = MeasureSpec.getSize(widthMeasureSpec);
+ }
+
+ int height = tc.thumbImage.getHeight();
+ if (MeasureSpec.UNSPECIFIED != MeasureSpec.getMode(heightMeasureSpec)) {
+ height = Math.min(height, MeasureSpec.getSize(heightMeasureSpec));
+ }
+ setMeasuredDimension(width, height);
+ }
+
+ /**
+ * Draws the widget on the given canvas.
+ */
+ @Override
+ protected void onDraw(Canvas canvas) {
+ super.onDraw(canvas);
+ // draw seek bar background line
+ final RectF rect = tc.getRect();
+ paint.setStyle(Style.FILL);
+ paint.setColor(Color.GRAY);
+ canvas.drawRect(rect, paint);
+ // draw seek bar active range line
+ rect.left = convertToScreenValue(normalizedMinValue);
+ rect.right = convertToScreenValue(normalizedMaxValue);
+ // orange color
+ paint.setColor(Color.rgb(255, 165, 0));
+ canvas.drawRect(rect, paint);
+
+ // draw minimum thumb
+ drawThumb(convertToScreenValue(normalizedMinValue), Thumb.MIN == pressedThumb, canvas);
+
+ // draw maximum thumb
+ drawThumb(convertToScreenValue(normalizedMaxValue), Thumb.MAX == pressedThumb, canvas);
+ }
+
+ /**
+ * Draws the "normal" resp. "pressed" thumb image on specified x-coordinate.
+ *
+ * @param normalizedToScreenValue The x-coordinate in screen space where to draw the image.
+ * @param pressed Is the thumb currently in "pressed" state?
+ * @param canvas The canvas to draw upon.
+ */
+ private void drawThumb(float normalizedToScreenValue, boolean pressed, Canvas canvas) {
+ canvas.drawBitmap(tc.getImage(pressed), normalizedToScreenValue - tc.thumbHalfWidth, (float) ((0.5f * getHeight()) - tc.thumbHalfHeight), paint);
+ }
+
+ /**
+ * Decides which (if any) thumb is touched by the given x-coordinate.
+ *
+ * @param touchX The x-coordinate of a touch event in screen space.
+ * @return The pressed thumb or null if none has been touched.
+ */
+ private Thumb evalPressedThumb(float touchX) {
+ Thumb result = null;
+ boolean minThumbPressed = isInThumbRange(touchX, normalizedMinValue);
+ boolean maxThumbPressed = isInThumbRange(touchX, normalizedMaxValue);
+ if (minThumbPressed && maxThumbPressed) {
+ // if both thumbs are pressed (they lie on top of each other), choose the one with more room to drag. this avoids "stalling" the thumbs in a corner, not being able to drag them apart anymore.
+ result = (touchX / getWidth() > 0.5f) ? Thumb.MIN : Thumb.MAX;
+ } else if (minThumbPressed) {
+ result = Thumb.MIN;
+ } else if (maxThumbPressed) {
+ result = Thumb.MAX;
+ }
+ return result;
+ }
+
+ /**
+ * Decides if given x-coordinate in screen space needs to be interpreted as "within" the normalized thumb x-coordinate.
+ *
+ * @param touchX The x-coordinate in screen space to check.
+ * @param normalizedThumbValue The normalized x-coordinate of the thumb to check.
+ * @return true if x-coordinate is in thumb range, false otherwise.
+ */
+ private boolean isInThumbRange(float touchX, double normalizedThumbValue) {
+ return Math.abs(touchX - convertToScreenValue(normalizedThumbValue)) <= tc.thumbHalfWidth;
+ }
+
+ /**
+ * Sets normalized min value to value so that 0 <= value <= normalized max value <= 1.
+ * The View will get invalidated when calling this method.
+ *
+ * @param value The new normalized min value to set.
+ */
+ private void setNormalizedMinValue(double value) {
+ normalizedMinValue = Math.max(0d, Math.min(1d, Math.min(value, normalizedMaxValue)));
+ invalidate();
+ }
+
+ /**
+ * Sets normalized max value to value so that 0 <= normalized min value <= value <= 1.
+ * The View will get invalidated when calling this method.
+ *
+ * @param value The new normalized max value to set.
+ */
+ private void setNormalizedMaxValue(double value) {
+ normalizedMaxValue = Math.max(0d, Math.min(1d, Math.max(value, normalizedMinValue)));
+ invalidate();
+ }
+
+ /**
+ * Converts a normalized value to a Number object in the value space between absolute minimum and maximum.
+ *
+ * @param normalized
+ * @return
+ */
+ @SuppressWarnings("unchecked")
+ private T denormalizeValue(double normalized) {
+ return toTConverter.convert(fromValueNormalizer.denormalize(normalized));
+ }
+
+ /**
+ * Converts the given Number value to a normalized double.
+ *
+ * @param value The Number value to normalize.
+ * @return The normalized double.
+ */
+ private double normalizeValue(T value) {
+ return fromValueNormalizer.normalize(toDoubleConverter.convert(value));
+ }
+
+ /**
+ * Converts a normalized value into screen space.
+ *
+ * @param normalizedValue The normalized value to convert.
+ * @return The converted value in screen space.
+ */
+ private float convertToScreenValue(double normalizedValue) {
+ return (float)this.fromScreenNormalizer.denormalize(normalizedValue);
+ }
+
+ /**
+ * Converts screen space x-coordinates into normalized values.
+ *
+ * @param screenValue The x-coordinate in screen space to convert.
+ * @return The normalized value.
+ */
+ private double convertToNormalizedValue(float screenValue) {
+ return this.fromScreenNormalizer.normalize(screenValue);
+ }
+
+ /**
+ * Callback listener interface to notify about changed range values.
+ *
+ * @param The Number type the RangeSeekBar has been declared with.
+ * @author Stephan Tittel (stephan.tittel@kom.tu-darmstadt.de)
+ */
+ public interface OnRangeSeekBarChangeListener {
+
+ void rangeSeekBarValuesChanged(T minValue, T maxValue, boolean changeComplete);
+
+ }
+
+ /**
+ * Thumb constants (min and max).
+ *
+ * @author Stephan Tittel (stephan.tittel@kom.tu-darmstadt.de)
+ */
+ private static enum Thumb {
+ MIN, MAX
+ }
+
+ private class ThumbContainer {
+ @NotNull
+ private final Bitmap thumbImage = BitmapFactory.decodeResource(getResources(), R.drawable.seek_thumb_normal);
+
+ @NotNull
+ private final Bitmap thumbPressedImage = BitmapFactory.decodeResource(getResources(), R.drawable.seek_thumb_pressed);
+
+ private final float thumbWidth = thumbImage.getWidth();
+
+ private final float thumbHalfWidth = 0.5f * thumbWidth;
+
+ private final float thumbHalfHeight = 0.5f * thumbImage.getHeight();
+
+ private final float lineHeight = 0.3f * thumbHalfHeight;
+
+ private final float padding = thumbHalfWidth;
+
+ public RectF getRect() {
+ return new RectF(padding, 0.5f * (getHeight() - lineHeight), getWidth() - padding, 0.5f * (getHeight() + lineHeight));
+ }
+
+ public Bitmap getImage(boolean pressed) {
+ return pressed ? thumbPressedImage : thumbImage;
+ }
+ }
+
+}
diff --git a/android-app/gen-external-apklibs/org.solovyev.android_android-common-view_1.0.6/src/org/solovyev/android/view/ActivityViewBuilder.java b/android-app/gen-external-apklibs/org.solovyev.android_android-common-view_1.0.6/src/org/solovyev/android/view/ActivityViewBuilder.java
new file mode 100644
index 00000000..71d94fb5
--- /dev/null
+++ b/android-app/gen-external-apklibs/org.solovyev.android_android-common-view_1.0.6/src/org/solovyev/android/view/ActivityViewBuilder.java
@@ -0,0 +1,14 @@
+package org.solovyev.android.view;
+
+import android.app.Activity;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * User: serso
+ * Date: 4/19/12
+ * Time: 4:23 PM
+ */
+public interface ActivityViewBuilder {
+
+ void buildView(@NotNull Activity activity);
+}
diff --git a/android-app/gen-external-apklibs/org.solovyev.android_android-common-view_1.0.6/src/org/solovyev/android/view/AndroidViewUtils.java b/android-app/gen-external-apklibs/org.solovyev.android_android-common-view_1.0.6/src/org/solovyev/android/view/AndroidViewUtils.java
new file mode 100644
index 00000000..1ffd3fde
--- /dev/null
+++ b/android-app/gen-external-apklibs/org.solovyev.android_android-common-view_1.0.6/src/org/solovyev/android/view/AndroidViewUtils.java
@@ -0,0 +1,93 @@
+package org.solovyev.android.view;
+
+import android.graphics.Canvas;
+import android.graphics.drawable.Drawable;
+import android.view.View;
+import android.widget.Button;
+import android.widget.ImageButton;
+import android.widget.TextView;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * User: serso
+ * Date: 11/3/12
+ * Time: 10:49 PM
+ */
+public final class AndroidViewUtils {
+
+ private AndroidViewUtils() {
+ throw new AssertionError();
+ }
+
+ public static boolean drawDrawables(Canvas canvas, @NotNull TextView textView) {
+ final int compoundPaddingLeft = textView.getCompoundPaddingLeft();
+ final int compoundPaddingTop = textView.getCompoundPaddingTop();
+ final int compoundPaddingRight = textView.getCompoundPaddingRight();
+ final int compoundPaddingBottom = textView.getCompoundPaddingBottom();
+
+ final int scrollX = textView.getScrollX();
+ final int scrollY = textView.getScrollY();
+
+ final int right = textView.getRight();
+ final int left = textView.getLeft();
+ final int bottom = textView.getBottom();
+ final int top = textView.getTop();
+
+ final Drawable[] drawables = textView.getCompoundDrawables();
+ if (drawables != null) {
+
+ int vspace = bottom - top - compoundPaddingBottom - compoundPaddingTop;
+ int hspace = right - left - compoundPaddingRight - compoundPaddingLeft;
+
+ Drawable topDr = drawables[1];
+ // IMPORTANT: The coordinates computed are also used in invalidateDrawable()
+ // Make sure to update invalidateDrawable() when changing this code.
+ if (topDr != null) {
+ canvas.save();
+ canvas.translate(scrollX + compoundPaddingLeft + (hspace - topDr.getBounds().width()) / 2,
+ scrollY + textView.getPaddingTop() + vspace / 2);
+ topDr.draw(canvas);
+ canvas.restore();
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ public static void applyButtonDef(@NotNull Button button, @NotNull ButtonDef buttonDef) {
+ button.setText(buttonDef.getText());
+
+ final Integer drawableResId = buttonDef.getDrawableResId();
+ if ( drawableResId != null ) {
+ button.setPadding(0, 0, 0, 0);
+
+ final Drawable drawable = button.getContext().getResources().getDrawable(drawableResId);
+ button.setCompoundDrawablesWithIntrinsicBounds(null, drawable, null, null);
+ button.setCompoundDrawablePadding(0);
+ }
+
+ applyViewDef(button, buttonDef);
+ }
+
+ public static void applyButtonDef(@NotNull ImageButton imageButton, @NotNull ButtonDef buttonDef) {
+ final Integer drawableResId = buttonDef.getDrawableResId();
+ if ( drawableResId != null ) {
+ imageButton.setImageDrawable(imageButton.getContext().getResources().getDrawable(drawableResId));
+ }
+
+ applyViewDef(imageButton, buttonDef);
+ }
+
+ public static void applyViewDef(@NotNull View view, @NotNull ViewDef viewDef) {
+ final Integer backgroundResId = viewDef.getBackgroundResId();
+ if (backgroundResId != null) {
+ view.setBackgroundResource(backgroundResId);
+ }
+
+ final String tag = viewDef.getTag();
+ if ( tag != null ) {
+ view.setTag(tag);
+ }
+ }
+}
diff --git a/android-app/gen-external-apklibs/org.solovyev.android_android-common-view_1.0.6/src/org/solovyev/android/view/AutoResizeTextView.java b/android-app/gen-external-apklibs/org.solovyev.android_android-common-view_1.0.6/src/org/solovyev/android/view/AutoResizeTextView.java
new file mode 100644
index 00000000..f9ca5cb3
--- /dev/null
+++ b/android-app/gen-external-apklibs/org.solovyev.android_android-common-view_1.0.6/src/org/solovyev/android/view/AutoResizeTextView.java
@@ -0,0 +1,301 @@
+/*
+ * Copyright (c) 2009-2011. Created by serso aka se.solovyev.
+ * For more information, please, contact se.solovyev@gmail.com
+ * or visit http://se.solovyev.org
+ */
+
+package org.solovyev.android.view;
+
+import android.content.Context;
+import android.graphics.Canvas;
+import android.text.Editable;
+import android.text.Layout.Alignment;
+import android.text.StaticLayout;
+import android.text.TextPaint;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.widget.TextView;
+
+/**
+ * Text view that auto adjusts text size to fit within the view.
+ * If the text size equals the minimum text size and still does not
+ * fit, append with an ellipsis.
+ *
+ * @author Chase Colburn
+ * @since Apr 4, 2011
+ */
+public class AutoResizeTextView extends TextView {
+
+ // Minimum text size for this text view
+ public static final float MIN_TEXT_SIZE = 20;
+
+ private float initialTextSize = 100;
+
+ // Interface for resize notifications
+ public interface OnTextResizeListener {
+ public void onTextResize(TextView textView, float oldSize, float newSize);
+ }
+
+ // Off screen canvas for text size rendering
+ private static final Canvas textResizeCanvas = new Canvas();
+
+ // Our ellipse string
+ private static final String ellipsis = "...";
+
+ // Registered resize listener
+ private OnTextResizeListener textResizeListener;
+
+ // Flag for text and/or size changes to force a resize
+ private boolean needsResize = false;
+
+ // Lower bounds for text size
+ private float minTextSize = MIN_TEXT_SIZE;
+
+ // Text view line spacing multiplier
+ private float spacingMult = 1.0f;
+
+ // Text view additional line spacing
+ private float spacingAdd = 0.0f;
+
+ // Add ellipsis to text that overflows at the smallest text size
+ private boolean addEllipsis = true;
+
+ // Default constructor override
+ public AutoResizeTextView(Context context) {
+ super(context);
+ }
+
+ // Default constructor when inflating from XML file
+ public AutoResizeTextView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ // Default constructor override
+ public AutoResizeTextView(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+ }
+
+ /**
+ * When text changes, set the force resize flag to true and resetInterpreter the text size.
+ */
+ @Override
+ protected void onTextChanged(final CharSequence text, final int start, final int before, final int after) {
+ needsResize = true;
+ // Since this view may be reused, it is good to resetInterpreter the text size
+ }
+
+ /**
+ * If the text view size changed, set the force resize flag to true
+ */
+ @Override
+ protected void onSizeChanged(int w, int h, int oldw, int oldh) {
+ if (w != oldw || h != oldh) {
+ needsResize = true;
+ }
+ }
+
+ /**
+ * Register listener to receive resize notifications
+ *
+ * @param listener
+ */
+ public void setOnResizeListener(OnTextResizeListener listener) {
+ textResizeListener = listener;
+ }
+
+ /**
+ * Override the set text size to update our internal reference values
+ */
+ @Override
+ public void setTextSize(int unit, float size) {
+ super.setTextSize(unit, size);
+ }
+
+ /**
+ * Override the set line spacing to update our internal reference values
+ */
+ @Override
+ public void setLineSpacing(float add, float mult) {
+ super.setLineSpacing(add, mult);
+ spacingMult = mult;
+ spacingAdd = add;
+ }
+
+ /**
+ * Set the lower text size limit and invalidate the view
+ *
+ * @param minTextSize
+ */
+ public void setMinTextSize(float minTextSize) {
+ this.minTextSize = minTextSize;
+ requestLayout();
+ invalidate();
+ }
+
+ /**
+ * Return lower text size limit
+ *
+ * @return
+ */
+ public float getMinTextSize() {
+ return minTextSize;
+ }
+
+ /**
+ * Set flag to add ellipsis to text that overflows at the smallest text size
+ *
+ * @param addEllipsis
+ */
+ public void setAddEllipsis(boolean addEllipsis) {
+ this.addEllipsis = addEllipsis;
+ }
+
+ /**
+ * Return flag to add ellipsis to text that overflows at the smallest text size
+ *
+ * @return
+ */
+ public boolean getAddEllipsis() {
+ return addEllipsis;
+ }
+
+
+ /**
+ * Resize text after measuring
+ */
+ @Override
+ protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+ if (changed || needsResize) {
+ int widthLimit = (right - left) - getCompoundPaddingLeft() - getCompoundPaddingRight();
+ int heightLimit = (bottom - top) - getCompoundPaddingBottom() - getCompoundPaddingTop();
+ resizeText(widthLimit, heightLimit, getText());
+ }
+ super.onLayout(changed, left, top, right, bottom);
+ }
+
+ /**
+ * Resize the text size with default width and height
+ */
+ public void resizeText() {
+ resizeText(getText());
+ }
+
+ private void resizeText(final CharSequence text) {
+ int heightLimit = getHeight() - getPaddingBottom() - getPaddingTop();
+ int widthLimit = getWidth() - getPaddingLeft() - getPaddingRight();
+ resizeText(widthLimit, heightLimit, text);
+ }
+
+ /**
+ * Resize the text size with specified width and height
+ *
+ * @param width
+ * @param height
+ * @param text
+ */
+ private void resizeText(int width, int height, CharSequence text) {
+ Log.d(this.getClass().getName(), "Resizing: w=" + width + ", h=" + height + ", text='" + text + "'");
+
+ // Do not resize if the view does not have dimensions or there is no text
+ if (text == null || text.length() == 0 || height <= 0 || width <= 0) {
+ return;
+ }
+
+ // Get the text view's paint object
+ TextPaint textPaint = getPaint();
+
+ // Store the current text size
+ float oldTextSize = textPaint.getTextSize();
+ Log.d(this.getClass().getName(), "Old text size: " + oldTextSize);
+
+ // If there is a max text size set, use the lesser of that and the default text size
+ // todo serso: +2 is a workaround => to be checked boundary constraints
+ float newTextSize = initialTextSize + 2;
+
+ int newTextHeight;
+
+ if (text instanceof Editable) {
+ ((Editable) text).append("|");
+ }
+
+ try {
+
+ // Get the required text height
+ newTextHeight = getTextRect(text, textPaint, width, newTextSize);
+
+ logDimensions(newTextSize, newTextHeight);
+ if (newTextHeight > height) {
+ // Until we either fit within our text view or we had reached our min text size, incrementally try smaller sizes
+ while (newTextHeight > height) {
+ if (newTextSize <= minTextSize) {
+ break;
+ }
+ newTextSize = Math.max(newTextSize - 1, minTextSize);
+ newTextHeight = getTextRect(text, textPaint, width, newTextSize);
+ logDimensions(newTextSize, newTextHeight);
+ }
+ } else {
+ while (newTextHeight < height) {
+ if (newTextSize <= minTextSize) {
+ break;
+ }
+ newTextSize = Math.max(newTextSize + 1, minTextSize);
+ newTextHeight = getTextRect(text, textPaint, width, newTextSize);
+ logDimensions(newTextSize, newTextHeight);
+ }
+ }
+ } finally {
+ if (text instanceof Editable) {
+ ((Editable) text).delete(text.length() - 1, text.length());
+ }
+ }
+
+ initialTextSize = newTextSize;
+
+ // If we had reached our minimum text size and still don't fit, append an ellipsis
+ if (addEllipsis && newTextSize == minTextSize && newTextHeight > height) {
+ // Draw using a static layout
+ StaticLayout layout = new StaticLayout(text, textPaint, width, Alignment.ALIGN_NORMAL, spacingMult, spacingAdd, false);
+ layout.draw(textResizeCanvas);
+ int lastLine = layout.getLineForVertical(height) - 1;
+ int start = layout.getLineStart(lastLine);
+ int end = layout.getLineEnd(lastLine);
+ float lineWidth = layout.getLineWidth(lastLine);
+ float ellipseWidth = textPaint.measureText(ellipsis);
+
+ // Trim characters off until we have enough room to draw the ellipsis
+ while (width < lineWidth + ellipseWidth) {
+ lineWidth = textPaint.measureText(text.subSequence(start, --end + 1).toString());
+ }
+ setText(text.subSequence(0, end) + ellipsis);
+
+ }
+
+ // Some devices try to auto adjust line spacing, so force default line spacing
+ // and invalidate the layout as a side effect
+ textPaint.setTextSize(newTextSize);
+ setLineSpacing(spacingAdd, spacingMult);
+
+ // Notify the listener if registered
+ if (textResizeListener != null) {
+ textResizeListener.onTextResize(this, oldTextSize, newTextSize);
+ }
+
+ // Reset force resize flag
+ needsResize = false;
+ }
+
+ private void logDimensions(float newTextSize, int newTextHeight) {
+ Log.d(this.getClass().getName(), "Nex text size: " + newTextSize + ", new text height: " + newTextHeight);
+ }
+
+ // Set the text size of the text paint object and use a static layout to render text off screen before measuring
+ private int getTextRect(CharSequence source, TextPaint paint, int width, float textSize) {
+ // Update the text paint object
+ paint.setTextSize(textSize);
+ // Draw using a static layout
+ StaticLayout layout = new StaticLayout(source, paint, width, Alignment.ALIGN_NORMAL, spacingMult, spacingAdd, false);
+ layout.draw(textResizeCanvas);
+ return layout.getHeight();
+ }
+}
diff --git a/android-app/gen-external-apklibs/org.solovyev.android_android-common-view_1.0.6/src/org/solovyev/android/view/ButtonDef.java b/android-app/gen-external-apklibs/org.solovyev.android_android-common-view_1.0.6/src/org/solovyev/android/view/ButtonDef.java
new file mode 100644
index 00000000..3ffec7ee
--- /dev/null
+++ b/android-app/gen-external-apklibs/org.solovyev.android_android-common-view_1.0.6/src/org/solovyev/android/view/ButtonDef.java
@@ -0,0 +1,17 @@
+package org.solovyev.android.view;
+
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * User: serso
+ * Date: 11/4/12
+ * Time: 11:52 PM
+ */
+public interface ButtonDef extends ViewDef {
+
+ @Nullable
+ Integer getDrawableResId();
+
+ @Nullable
+ CharSequence getText();
+}
diff --git a/android-app/gen-external-apklibs/org.solovyev.android_android-common-view_1.0.6/src/org/solovyev/android/view/ColorButton.java b/android-app/gen-external-apklibs/org.solovyev.android_android-common-view_1.0.6/src/org/solovyev/android/view/ColorButton.java
new file mode 100644
index 00000000..cdeb541d
--- /dev/null
+++ b/android-app/gen-external-apklibs/org.solovyev.android_android-common-view_1.0.6/src/org/solovyev/android/view/ColorButton.java
@@ -0,0 +1,232 @@
+/*
+ * Copyright (C) 2008 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.
+ */
+
+/*
+ * Copyright (c) 2009-2011. Created by serso aka se.solovyev.
+ * For more information, please, contact se.solovyev@gmail.com
+ */
+
+package org.solovyev.android.view;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.content.res.TypedArray;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.Paint.Style;
+import android.os.Vibrator;
+import android.preference.PreferenceManager;
+import android.util.AttributeSet;
+import android.view.MotionEvent;
+import android.widget.Button;
+import org.jetbrains.annotations.NotNull;
+import org.solovyev.android.FontSizeAdjuster;
+import org.solovyev.common.math.Point2d;
+import org.solovyev.common.text.StringUtils;
+
+/**
+ * NOTE: copied from com.android.calculator2.ColorButton
+ */
+
+/**
+ * Button with click-animation effect.
+ */
+public class ColorButton extends Button {
+
+ private int magicFlameColour;
+ private boolean drawMagicFlame = true;
+
+ private static final int CLICK_FEEDBACK_INTERVAL = 10;
+ private static final int CLICK_FEEDBACK_DURATION = 350;
+
+ @NotNull
+ private Point2d textPosition;
+ private long animationStart;
+ private Paint feedbackPaint;
+
+ @NotNull
+ private final OnClickListenerVibrator onClickListener;
+
+ private static final float H_TEXT_POSITION_DEFAULT_VALUE = 0.5f;
+
+ private float hTextPosition = H_TEXT_POSITION_DEFAULT_VALUE;
+
+ private boolean showText = true;
+
+ public ColorButton(Context context, AttributeSet attrs) {
+ this(context, attrs, true);
+ }
+
+ public ColorButton(Context context, AttributeSet attrs, boolean init) {
+ super(context, attrs);
+
+ TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.DragButton);
+ final int N = a.getIndexCount();
+ for (int i = 0; i < N; i++) {
+ int attr = a.getIndex(i);
+
+ final String attrValue = a.getString(attr);
+
+ if (!StringUtils.isEmpty(attrValue)) {
+ switch (attr) {
+ case R.styleable.DragButton_hTextPosition:
+ this.hTextPosition = Float.valueOf(attrValue);
+ break;
+ }
+ }
+ }
+
+ if (init) {
+ init(context);
+ }
+
+ this.onClickListener = new OnClickListenerVibrator((Vibrator)context.getSystemService(Context.VIBRATOR_SERVICE), PreferenceManager.getDefaultSharedPreferences(context));
+ }
+
+ protected void init(Context context) {
+ final Resources resources = getResources();
+
+ magicFlameColour = resources.getColor(R.color.magic_flame);
+ feedbackPaint = new Paint();
+ feedbackPaint.setStyle(Style.STROKE);
+ feedbackPaint.setStrokeWidth(2);
+
+ // applying textColor property on the paint (now it is not possible to set paint color through the xml)
+ getPaint().setColor(getCurrentTextColor());
+
+ animationStart = -1;
+
+ if (context instanceof FontSizeAdjuster) {
+ ((FontSizeAdjuster) context).adjustFontSize(this);
+ }
+ }
+
+
+ @Override
+ public void onSizeChanged(int w, int h, int oldW, int oldH) {
+ measureText();
+ }
+
+ protected void measureText() {
+ Paint paint = getPaint();
+
+ if (getText() != null) {
+ textPosition = getTextPosition(paint, getText());
+ }
+ }
+
+ private Point2d getTextPosition(@NotNull Paint paint, @NotNull CharSequence text) {
+ final Point2d result = new Point2d();
+
+ result.setX(hTextPosition * getWidth() - 0.5f * paint.measureText(text.toString()));
+
+ float height = getHeight() - paint.ascent() - paint.descent();
+
+ result.setY(height / 2);
+
+ return result;
+ }
+
+ @Override
+ protected void onTextChanged(CharSequence text, int start, int before, int after) {
+ measureText();
+ }
+
+ public void drawMagicFlame(int duration, Canvas canvas) {
+ int alpha = 255 - 255 * duration / CLICK_FEEDBACK_DURATION;
+ int color = magicFlameColour | (alpha << 24);
+
+ feedbackPaint.setColor(color);
+ canvas.drawRect(1, 1, getWidth() - 1, getHeight() - 1, feedbackPaint);
+ }
+
+ @Override
+ public void onDraw(Canvas canvas) {
+ if (drawMagicFlame) {
+ if (animationStart != -1) {
+ int animDuration = (int) (System.currentTimeMillis() - animationStart);
+
+ if (animDuration >= CLICK_FEEDBACK_DURATION) {
+ animationStart = -1;
+ } else {
+ drawMagicFlame(animDuration, canvas);
+ postInvalidateDelayed(CLICK_FEEDBACK_INTERVAL);
+ }
+ }
+ }
+
+ CharSequence text = getText();
+ if ( !StringUtils.isEmpty(text) && textPosition != null) {
+ if (showText) {
+ canvas.drawText(text, 0, text.length(), textPosition.getX(), textPosition.getY(), getPaint());
+ }
+ } else {
+ AndroidViewUtils.drawDrawables(canvas, this);
+ }
+ }
+
+ public void animateClickFeedback() {
+ animationStart = System.currentTimeMillis();
+ invalidate();
+ }
+
+ @Override
+ public boolean performClick() {
+ vibrate();
+ return super.performClick();
+ }
+
+ @Override
+ public boolean performLongClick() {
+ vibrate();
+ return super.performLongClick();
+ }
+
+ private void vibrate() {
+ this.onClickListener.onClick(this);
+ }
+
+ @Override
+ public boolean onTouchEvent(MotionEvent event) {
+ boolean result = super.onTouchEvent(event);
+
+ if (this.drawMagicFlame) {
+ switch (event.getAction()) {
+ case MotionEvent.ACTION_UP:
+ animateClickFeedback();
+ break;
+ case MotionEvent.ACTION_DOWN:
+ case MotionEvent.ACTION_CANCEL:
+ invalidate();
+ break;
+ }
+ }
+
+ return result;
+ }
+
+ public void setDrawMagicFlame(boolean drawMagicFlame) {
+ this.drawMagicFlame = drawMagicFlame;
+ }
+
+ public boolean isShowText() {
+ return showText;
+ }
+
+ public void setShowText(boolean showText) {
+ this.showText = showText;
+ }
+}
diff --git a/android-app/gen-external-apklibs/org.solovyev.android_android-common-view_1.0.6/src/org/solovyev/android/view/ColorPickerDialog.java b/android-app/gen-external-apklibs/org.solovyev.android_android-common-view_1.0.6/src/org/solovyev/android/view/ColorPickerDialog.java
new file mode 100644
index 00000000..7fec311e
--- /dev/null
+++ b/android-app/gen-external-apklibs/org.solovyev.android_android-common-view_1.0.6/src/org/solovyev/android/view/ColorPickerDialog.java
@@ -0,0 +1,234 @@
+/*
+ * Copyright (C) 2007 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 org.solovyev.android.view;
+
+import android.os.Bundle;
+import android.app.Dialog;
+import android.content.Context;
+import android.graphics.*;
+import android.view.MotionEvent;
+import android.view.View;
+
+public class ColorPickerDialog extends Dialog {
+
+ public interface OnColorChangedListener {
+ void colorChanged(int color);
+ }
+
+ private OnColorChangedListener mListener;
+ private int mInitialColor;
+
+ private static class ColorPickerView extends View {
+ private Paint mPaint;
+ private Paint mCenterPaint;
+ private final int[] mColors;
+ private OnColorChangedListener mListener;
+
+ ColorPickerView(Context c, OnColorChangedListener l, int color) {
+ super(c);
+ mListener = l;
+ mColors = new int[]{
+ 0xFFFF0000, 0xFFFF00FF, 0xFF0000FF, 0xFF00FFFF, 0xFF00FF00,
+ 0xFFFFFF00, 0xFFFF0000
+ };
+ Shader s = new SweepGradient(0, 0, mColors, null);
+
+ mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
+ mPaint.setShader(s);
+ mPaint.setStyle(Paint.Style.STROKE);
+ mPaint.setStrokeWidth(32);
+
+ mCenterPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
+ mCenterPaint.setColor(color);
+ mCenterPaint.setStrokeWidth(5);
+ }
+
+ private boolean mTrackingCenter;
+ private boolean mHighlightCenter;
+
+ @Override
+ protected void onDraw(Canvas canvas) {
+ float r = CENTER_X - mPaint.getStrokeWidth() * 0.5f;
+
+ canvas.translate(CENTER_X, CENTER_X);
+
+ canvas.drawOval(new RectF(-r, -r, r, r), mPaint);
+ canvas.drawCircle(0, 0, CENTER_RADIUS, mCenterPaint);
+
+ if (mTrackingCenter) {
+ int c = mCenterPaint.getColor();
+ mCenterPaint.setStyle(Paint.Style.STROKE);
+
+ if (mHighlightCenter) {
+ mCenterPaint.setAlpha(0xFF);
+ } else {
+ mCenterPaint.setAlpha(0x80);
+ }
+ canvas.drawCircle(0, 0,
+ CENTER_RADIUS + mCenterPaint.getStrokeWidth(),
+ mCenterPaint);
+
+ mCenterPaint.setStyle(Paint.Style.FILL);
+ mCenterPaint.setColor(c);
+ }
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ setMeasuredDimension(CENTER_X * 2, CENTER_Y * 2);
+ }
+
+ private static final int CENTER_X = 100;
+ private static final int CENTER_Y = 100;
+ private static final int CENTER_RADIUS = 32;
+
+ private int floatToByte(float x) {
+ int n = java.lang.Math.round(x);
+ return n;
+ }
+
+ private int pinToByte(int n) {
+ if (n < 0) {
+ n = 0;
+ } else if (n > 255) {
+ n = 255;
+ }
+ return n;
+ }
+
+ private int ave(int s, int d, float p) {
+ return s + java.lang.Math.round(p * (d - s));
+ }
+
+ private int interpColor(int colors[], float unit) {
+ if (unit <= 0) {
+ return colors[0];
+ }
+ if (unit >= 1) {
+ return colors[colors.length - 1];
+ }
+
+ float p = unit * (colors.length - 1);
+ int i = (int) p;
+ p -= i;
+
+ // now p is just the fractional part [0...1) and i is the index
+ int c0 = colors[i];
+ int c1 = colors[i + 1];
+ int a = ave(Color.alpha(c0), Color.alpha(c1), p);
+ int r = ave(Color.red(c0), Color.red(c1), p);
+ int g = ave(Color.green(c0), Color.green(c1), p);
+ int b = ave(Color.blue(c0), Color.blue(c1), p);
+
+ return Color.argb(a, r, g, b);
+ }
+
+ private int rotateColor(int color, float rad) {
+ float deg = rad * 180 / 3.1415927f;
+ int r = Color.red(color);
+ int g = Color.green(color);
+ int b = Color.blue(color);
+
+ ColorMatrix cm = new ColorMatrix();
+ ColorMatrix tmp = new ColorMatrix();
+
+ cm.setRGB2YUV();
+ tmp.setRotate(0, deg);
+ cm.postConcat(tmp);
+ tmp.setYUV2RGB();
+ cm.postConcat(tmp);
+
+ final float[] a = cm.getArray();
+
+ int ir = floatToByte(a[0] * r + a[1] * g + a[2] * b);
+ int ig = floatToByte(a[5] * r + a[6] * g + a[7] * b);
+ int ib = floatToByte(a[10] * r + a[11] * g + a[12] * b);
+
+ return Color.argb(Color.alpha(color), pinToByte(ir),
+ pinToByte(ig), pinToByte(ib));
+ }
+
+ private static final float PI = 3.1415926f;
+
+ @Override
+ public boolean onTouchEvent(MotionEvent event) {
+ float x = event.getX() - CENTER_X;
+ float y = event.getY() - CENTER_Y;
+ boolean inCenter = java.lang.Math.sqrt(x * x + y * y) <= CENTER_RADIUS;
+
+ switch (event.getAction()) {
+ case MotionEvent.ACTION_DOWN:
+ mTrackingCenter = inCenter;
+ if (inCenter) {
+ mHighlightCenter = true;
+ invalidate();
+ break;
+ }
+ case MotionEvent.ACTION_MOVE:
+ if (mTrackingCenter) {
+ if (mHighlightCenter != inCenter) {
+ mHighlightCenter = inCenter;
+ invalidate();
+ }
+ } else {
+ float angle = (float) java.lang.Math.atan2(y, x);
+ // need to turn angle [-PI ... PI] into unit [0....1]
+ float unit = angle / (2 * PI);
+ if (unit < 0) {
+ unit += 1;
+ }
+ mCenterPaint.setColor(interpColor(mColors, unit));
+ invalidate();
+ }
+ break;
+ case MotionEvent.ACTION_UP:
+ if (mTrackingCenter) {
+ if (inCenter) {
+ mListener.colorChanged(mCenterPaint.getColor());
+ }
+ mTrackingCenter = false; // so we draw w/o halo
+ invalidate();
+ }
+ break;
+ }
+ return true;
+ }
+ }
+
+ public ColorPickerDialog(Context context,
+ OnColorChangedListener listener,
+ int initialColor) {
+ super(context);
+
+ mListener = listener;
+ mInitialColor = initialColor;
+ }
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ OnColorChangedListener l = new OnColorChangedListener() {
+ public void colorChanged(int color) {
+ mListener.colorChanged(color);
+ dismiss();
+ }
+ };
+
+ setContentView(new ColorPickerView(getContext(), l, mInitialColor));
+ setTitle("Pick a Color");
+ }
+}
\ No newline at end of file
diff --git a/android-app/gen-external-apklibs/org.solovyev.android_android-common-view_1.0.6/src/org/solovyev/android/view/ConfirmationDialogBuilder.java b/android-app/gen-external-apklibs/org.solovyev.android_android-common-view_1.0.6/src/org/solovyev/android/view/ConfirmationDialogBuilder.java
new file mode 100644
index 00000000..897289a4
--- /dev/null
+++ b/android-app/gen-external-apklibs/org.solovyev.android_android-common-view_1.0.6/src/org/solovyev/android/view/ConfirmationDialogBuilder.java
@@ -0,0 +1,104 @@
+package org.solovyev.android.view;
+
+import org.solovyev.android.ActivityDestroyerController;
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.content.Context;
+import android.content.DialogInterface;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+import org.solovyev.android.DialogOnActivityDestroyedListener;
+import org.solovyev.android.view.R;
+
+/**
+ * User: serso
+ * Date: 4/29/12
+ * Time: 1:25 PM
+ */
+public class ConfirmationDialogBuilder implements DialogBuilder {
+
+ @NotNull
+ private final Context context;
+
+ private final int messageResId;
+
+ private int titleResId = R.string.c_confirmation;
+
+ private int positiveButtonTextResId = android.R.string.ok;
+
+ private int negativeButtonTextResId = android.R.string.cancel;
+
+ @Nullable
+ private DialogInterface.OnClickListener positiveHandler;
+
+ @Nullable
+ private DialogInterface.OnClickListener negativeHandler;
+
+ public ConfirmationDialogBuilder(@NotNull Context context, int messageResId) {
+ this.context = context;
+ this.messageResId = messageResId;
+ }
+
+ @NotNull
+ public ConfirmationDialogBuilder setTitleResId(int titleResId) {
+ this.titleResId = titleResId;
+ return this;
+ }
+
+ @NotNull
+ public ConfirmationDialogBuilder setPositiveButtonTextResId(int positiveButtonTextResId) {
+ this.positiveButtonTextResId = positiveButtonTextResId;
+ return this;
+ }
+
+ @NotNull
+ public ConfirmationDialogBuilder setNegativeButtonTextResId(int negativeButtonTextResId) {
+ this.negativeButtonTextResId = negativeButtonTextResId;
+ return this;
+ }
+
+ @NotNull
+ public ConfirmationDialogBuilder setPositiveHandler(@Nullable DialogInterface.OnClickListener positiveHandler) {
+ this.positiveHandler = positiveHandler;
+ return this;
+ }
+
+ @NotNull
+ public ConfirmationDialogBuilder setNegativeHandler(@Nullable DialogInterface.OnClickListener negativeHandler) {
+ this.negativeHandler = negativeHandler;
+ return this;
+ }
+
+ @NotNull
+ @Override
+ public AlertDialog build() {
+ AlertDialog.Builder builder = new AlertDialog.Builder(context);
+
+ builder.setTitle(titleResId);
+ builder.setMessage(messageResId);
+ builder.setPositiveButton(positiveButtonTextResId, new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ if ( positiveHandler != null ) {
+ positiveHandler.onClick(dialog, which);
+ }
+ }
+ });
+
+ builder.setNegativeButton(negativeButtonTextResId, new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ if ( negativeHandler != null ) {
+ negativeHandler.onClick(dialog, which);
+ }
+ }
+ });
+
+
+ final AlertDialog result = builder.create();
+ if ( context instanceof Activity) {
+ ActivityDestroyerController.getInstance().put((Activity)context, new DialogOnActivityDestroyedListener(result));
+ }
+ return result;
+ }
+}
diff --git a/android-app/gen-external-apklibs/org.solovyev.android_android-common-view_1.0.6/src/org/solovyev/android/view/DataActivityViewBuilder.java b/android-app/gen-external-apklibs/org.solovyev.android_android-common-view_1.0.6/src/org/solovyev/android/view/DataActivityViewBuilder.java
new file mode 100644
index 00000000..b9de4257
--- /dev/null
+++ b/android-app/gen-external-apklibs/org.solovyev.android_android-common-view_1.0.6/src/org/solovyev/android/view/DataActivityViewBuilder.java
@@ -0,0 +1,13 @@
+package org.solovyev.android.view;
+
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * User: serso
+ * Date: 4/20/12
+ * Time: 12:01 AM
+ */
+public interface DataActivityViewBuilder extends ActivityViewBuilder {
+
+ void setData(@NotNull D data);
+}
diff --git a/android-app/gen-external-apklibs/org.solovyev.android_android-common-view_1.0.6/src/org/solovyev/android/view/DialogBuilder.java b/android-app/gen-external-apklibs/org.solovyev.android_android-common-view_1.0.6/src/org/solovyev/android/view/DialogBuilder.java
new file mode 100644
index 00000000..59f3b592
--- /dev/null
+++ b/android-app/gen-external-apklibs/org.solovyev.android_android-common-view_1.0.6/src/org/solovyev/android/view/DialogBuilder.java
@@ -0,0 +1,12 @@
+package org.solovyev.android.view;
+
+import android.content.DialogInterface;
+import org.solovyev.common.Builder;
+
+/**
+ * User: serso
+ * Date: 4/29/12
+ * Time: 1:24 PM
+ */
+public interface DialogBuilder extends Builder