diff --git a/.classpath b/.classpath
deleted file mode 100644
index aa361afe..00000000
--- a/.classpath
+++ /dev/null
@@ -1,13 +0,0 @@
-
-
+
+
It simulate HTML behaviour <img src="" widh="100" />
- * - * - *It also allow set related view which will be used as reference for size measure.
- * - * @author Tomáš Procházka <tomas.prochazka@gmail.com> - * @version $Revision: 0$ ($Date: 6.6.2011 18:16:52$) - */ -public class EnlargingImageView extends android.widget.ImageView { - - private int mDrawableWidth; - private int mDrawableHeight; - private boolean mAdjustViewBoundsL; - private int mMaxWidthL = Integer.MAX_VALUE; - private int mMaxHeightL = Integer.MAX_VALUE; - private View relatedView; - - public EnlargingImageView(Context context, AttributeSet attrs, int defStyle) { - super(context, attrs, defStyle); - - // hack for acces some private field of parent :-( - Field f; - try { - f = android.widget.ImageView.class.getDeclaredField("mAdjustViewBounds"); - f.setAccessible(true); - setAdjustViewBounds((Boolean) f.get(this)); - - f = android.widget.ImageView.class.getDeclaredField("mMaxWidth"); - f.setAccessible(true); - setMaxWidth((Integer) f.get(this)); - - f = android.widget.ImageView.class.getDeclaredField("mMaxHeight"); - f.setAccessible(true); - setMaxHeight((Integer) f.get(this)); - } catch (Exception e) { - e.printStackTrace(); - } - } - - public EnlargingImageView(Context context, AttributeSet attrs) { - this(context, attrs, 0); - } - - public EnlargingImageView(Context context) { - super(context); - } - - public void setAdjustViewBounds(boolean adjustViewBounds) { - super.setAdjustViewBounds(adjustViewBounds); - mAdjustViewBoundsL = adjustViewBounds; - } - - public void setMaxWidth(int maxWidth) { - super.setMaxWidth(maxWidth); - mMaxWidthL = maxWidth; - } - - public void setMaxHeight(int maxHeight) { - super.setMaxHeight(maxHeight); - mMaxHeightL = maxHeight; - } - - @Override - protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { - super.onMeasure(widthMeasureSpec, heightMeasureSpec); - - if (getDrawable() == null) { - setMeasuredDimension(0, 0); - return; - } - - mDrawableWidth = getDrawable().getIntrinsicWidth(); - mDrawableHeight = getDrawable().getIntrinsicHeight(); - - int w = 0; - int h = 0; - - // Desired aspect ratio of the view's contents (not including padding) - float desiredAspect = 0.0f; - - // We are allowed to change the view's width - boolean resizeWidth = false; - - // We are allowed to change the view's height - boolean resizeHeight = false; - - if (mDrawableWidth > 0) { - w = mDrawableWidth; - h = mDrawableHeight; - if (w <= 0) w = 1; - if (h <= 0) h = 1; - - // We are supposed to adjust view bounds to match the aspect - // ratio of our drawable. See if that is possible. - if (mAdjustViewBoundsL) { - - int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec); - int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec); - - resizeWidth = widthSpecMode != MeasureSpec.EXACTLY; - resizeHeight = heightSpecMode != MeasureSpec.EXACTLY; - - desiredAspect = (float) w / (float) h; - } - } - - int pleft = getPaddingLeft(); - int pright = getPaddingRight(); - int ptop = getPaddingTop(); - int pbottom = getPaddingBottom(); - - int widthSize; - int heightSize; - - if (resizeWidth || resizeHeight) { - /* If we get here, it means we want to resize to match the - drawables aspect ratio, and we have the freedom to change at - least one dimension. - */ - - // Get the max possible width given our constraints - widthSize = resolveAdjustedSize(w + pleft + pright, - mMaxWidthL, widthMeasureSpec); - - // Get the max possible height given our constraints - heightSize = resolveAdjustedSize(h + ptop + pbottom, - mMaxHeightL, heightMeasureSpec); - - if (desiredAspect != 0.0f) { - // See what our actual aspect ratio is - float actualAspect = (float) (widthSize - pleft - pright) / - (heightSize - ptop - pbottom); - - if (Math.abs(actualAspect - desiredAspect) > 0.0000001) { - - // Try adjusting width to be proportional to height - if (resizeWidth) { - int newWidth = (int) (desiredAspect * (heightSize - ptop - pbottom)) + pleft + pright; - if (/*newWidth <= widthSize &&*/newWidth > 0) { - widthSize = Math.min(newWidth, mMaxWidthL); - heightSize = (int) ((widthSize - pleft - pright) / desiredAspect) + ptop + pbottom; - } - } - - // Try adjusting height to be proportional to width - if (resizeHeight) { - int newHeight = (int) ((widthSize - pleft - pright) / desiredAspect) + ptop + pbottom; - if (/*newHeight <= heightSize && */newHeight > 0) { - heightSize = Math.min(newHeight, mMaxHeightL); - widthSize = (int) (desiredAspect * (heightSize - ptop - pbottom)) + pleft + pright; - } - } - } - } - } else { - /* We are either don't want to preserve the drawables aspect ratio, - or we are not allowed to change view dimensions. Just measure in - the normal way. - */ - w += pleft + pright; - h += ptop + pbottom; - - w = Math.max(w, getSuggestedMinimumWidth()); - h = Math.max(h, getSuggestedMinimumHeight()); - - widthSize = resolveSize(w, widthMeasureSpec); - heightSize = resolveSize(h, heightMeasureSpec); - } - - //Log.d(Constants.LOGTAG, mDrawableWidth + ":" + mDrawableHeight + " to " + widthSize + ":" + heightSize); - - setMeasuredDimension(widthSize, heightSize); - - if (relatedView != null) { - //Log.i(Constants.LOGTAG, getTag() + " onMeasure:" + widthSize + ", " + heightSize + " update size of related view!"); - relatedView.getLayoutParams().width = widthSize; - relatedView.getLayoutParams().height = heightSize; - } - - } - - @Override - protected void onLayout(boolean changed, int left, int top, int right, int bottom) { - super.onLayout(changed, left, top, right, bottom); - //Log.d(Constants.LOGTAG, getTag() + " onLayout:" + left + ", " + top + ", " + right + ", " + bottom); - } - - /** - * Experimental. This view will be set to the same size as this image. - */ - public void setRelatedView(View view) { - relatedView = view; - } - - @Override - protected void onSizeChanged(int w, int h, int oldw, int oldh) { - super.onSizeChanged(w, h, oldw, oldh); - //Log.d(Constants.LOGTAG, getTag() + " onSizeChanged:" + w + ", " + h + ", " + oldw + ", " + oldh); - } - - private int resolveAdjustedSize(int desiredSize, int maxSize, int measureSpec) { - int result = desiredSize; - int specMode = MeasureSpec.getMode(measureSpec); - int specSize = MeasureSpec.getSize(measureSpec); - switch (specMode) { - case MeasureSpec.UNSPECIFIED: - /* Parent says we can be as big as we want. Just don't be larger - than max size imposed on ourselves. - */ - result = Math.min(desiredSize, maxSize); - break; - case MeasureSpec.AT_MOST: - // Parent says we can be as big as we want, up to specSize. - // Don't be larger than specSize, and don't be larger than - // the max size imposed on ourselves. - result = Math.min(Math.min(desiredSize, specSize), maxSize); - break; - case MeasureSpec.EXACTLY: - // No choice. Do what we are told. - result = specSize; - break; - } - return result; - } +package org.fox.ttrss.util; + +/* + * Copyright (C) 2013 Tomáš Procházka + * + * 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. + */ + +import java.lang.reflect.Field; + +import android.content.Context; +import android.util.AttributeSet; +import android.view.View; + +/** + * Special version of ImageView which allow enlarge width of image if android:adjustViewBounds is true. + * + *It simulate HTML behaviour <img src="" widh="100" />
+ * + * + *It also allow set related view which will be used as reference for size measure.
+ * + * @author Tomáš Procházka <tomas.prochazka@gmail.com> + * @version $Revision: 0$ ($Date: 6.6.2011 18:16:52$) + */ +public class EnlargingImageView extends android.widget.ImageView { + + private int mDrawableWidth; + private int mDrawableHeight; + private boolean mAdjustViewBoundsL; + private int mMaxWidthL = Integer.MAX_VALUE; + private int mMaxHeightL = Integer.MAX_VALUE; + private View relatedView; + + public EnlargingImageView(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + + // hack for acces some private field of parent :-( + Field f; + try { + f = android.widget.ImageView.class.getDeclaredField("mAdjustViewBounds"); + f.setAccessible(true); + setAdjustViewBounds((Boolean) f.get(this)); + + f = android.widget.ImageView.class.getDeclaredField("mMaxWidth"); + f.setAccessible(true); + setMaxWidth((Integer) f.get(this)); + + f = android.widget.ImageView.class.getDeclaredField("mMaxHeight"); + f.setAccessible(true); + setMaxHeight((Integer) f.get(this)); + } catch (Exception e) { + e.printStackTrace(); + } + } + + public EnlargingImageView(Context context, AttributeSet attrs) { + this(context, attrs, 0); + } + + public EnlargingImageView(Context context) { + super(context); + } + + public void setAdjustViewBounds(boolean adjustViewBounds) { + super.setAdjustViewBounds(adjustViewBounds); + mAdjustViewBoundsL = adjustViewBounds; + } + + public void setMaxWidth(int maxWidth) { + super.setMaxWidth(maxWidth); + mMaxWidthL = maxWidth; + } + + public void setMaxHeight(int maxHeight) { + super.setMaxHeight(maxHeight); + mMaxHeightL = maxHeight; + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + super.onMeasure(widthMeasureSpec, heightMeasureSpec); + + if (getDrawable() == null) { + setMeasuredDimension(0, 0); + return; + } + + mDrawableWidth = getDrawable().getIntrinsicWidth(); + mDrawableHeight = getDrawable().getIntrinsicHeight(); + + int w = 0; + int h = 0; + + // Desired aspect ratio of the view's contents (not including padding) + float desiredAspect = 0.0f; + + // We are allowed to change the view's width + boolean resizeWidth = false; + + // We are allowed to change the view's height + boolean resizeHeight = false; + + if (mDrawableWidth > 0) { + w = mDrawableWidth; + h = mDrawableHeight; + if (w <= 0) w = 1; + if (h <= 0) h = 1; + + // We are supposed to adjust view bounds to match the aspect + // ratio of our drawable. See if that is possible. + if (mAdjustViewBoundsL) { + + int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec); + int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec); + + resizeWidth = widthSpecMode != MeasureSpec.EXACTLY; + resizeHeight = heightSpecMode != MeasureSpec.EXACTLY; + + desiredAspect = (float) w / (float) h; + } + } + + int pleft = getPaddingLeft(); + int pright = getPaddingRight(); + int ptop = getPaddingTop(); + int pbottom = getPaddingBottom(); + + int widthSize; + int heightSize; + + if (resizeWidth || resizeHeight) { + /* If we get here, it means we want to resize to match the + drawables aspect ratio, and we have the freedom to change at + least one dimension. + */ + + // Get the max possible width given our constraints + widthSize = resolveAdjustedSize(w + pleft + pright, + mMaxWidthL, widthMeasureSpec); + + // Get the max possible height given our constraints + heightSize = resolveAdjustedSize(h + ptop + pbottom, + mMaxHeightL, heightMeasureSpec); + + if (desiredAspect != 0.0f) { + // See what our actual aspect ratio is + float actualAspect = (float) (widthSize - pleft - pright) / + (heightSize - ptop - pbottom); + + if (Math.abs(actualAspect - desiredAspect) > 0.0000001) { + + // Try adjusting width to be proportional to height + if (resizeWidth) { + int newWidth = (int) (desiredAspect * (heightSize - ptop - pbottom)) + pleft + pright; + if (/*newWidth <= widthSize &&*/newWidth > 0) { + widthSize = Math.min(newWidth, mMaxWidthL); + heightSize = (int) ((widthSize - pleft - pright) / desiredAspect) + ptop + pbottom; + } + } + + // Try adjusting height to be proportional to width + if (resizeHeight) { + int newHeight = (int) ((widthSize - pleft - pright) / desiredAspect) + ptop + pbottom; + if (/*newHeight <= heightSize && */newHeight > 0) { + heightSize = Math.min(newHeight, mMaxHeightL); + widthSize = (int) (desiredAspect * (heightSize - ptop - pbottom)) + pleft + pright; + } + } + } + } + } else { + /* We are either don't want to preserve the drawables aspect ratio, + or we are not allowed to change view dimensions. Just measure in + the normal way. + */ + w += pleft + pright; + h += ptop + pbottom; + + w = Math.max(w, getSuggestedMinimumWidth()); + h = Math.max(h, getSuggestedMinimumHeight()); + + widthSize = resolveSize(w, widthMeasureSpec); + heightSize = resolveSize(h, heightMeasureSpec); + } + + //Log.d(Constants.LOGTAG, mDrawableWidth + ":" + mDrawableHeight + " to " + widthSize + ":" + heightSize); + + setMeasuredDimension(widthSize, heightSize); + + if (relatedView != null) { + //Log.i(Constants.LOGTAG, getTag() + " onMeasure:" + widthSize + ", " + heightSize + " update size of related view!"); + relatedView.getLayoutParams().width = widthSize; + relatedView.getLayoutParams().height = heightSize; + } + + } + + @Override + protected void onLayout(boolean changed, int left, int top, int right, int bottom) { + super.onLayout(changed, left, top, right, bottom); + //Log.d(Constants.LOGTAG, getTag() + " onLayout:" + left + ", " + top + ", " + right + ", " + bottom); + } + + /** + * Experimental. This view will be set to the same size as this image. + */ + public void setRelatedView(View view) { + relatedView = view; + } + + @Override + protected void onSizeChanged(int w, int h, int oldw, int oldh) { + super.onSizeChanged(w, h, oldw, oldh); + //Log.d(Constants.LOGTAG, getTag() + " onSizeChanged:" + w + ", " + h + ", " + oldw + ", " + oldh); + } + + private int resolveAdjustedSize(int desiredSize, int maxSize, int measureSpec) { + int result = desiredSize; + int specMode = MeasureSpec.getMode(measureSpec); + int specSize = MeasureSpec.getSize(measureSpec); + switch (specMode) { + case MeasureSpec.UNSPECIFIED: + /* Parent says we can be as big as we want. Just don't be larger + than max size imposed on ourselves. + */ + result = Math.min(desiredSize, maxSize); + break; + case MeasureSpec.AT_MOST: + // Parent says we can be as big as we want, up to specSize. + // Don't be larger than specSize, and don't be larger than + // the max size imposed on ourselves. + result = Math.min(Math.min(desiredSize, specSize), maxSize); + break; + case MeasureSpec.EXACTLY: + // No choice. Do what we are told. + result = specSize; + break; + } + return result; + } } \ No newline at end of file diff --git a/src/org/fox/ttrss/util/FontSizeDialogPreference.java b/orgfoxttrss/src/main/java/org/fox/ttrss/util/FontSizeDialogPreference.java similarity index 96% rename from src/org/fox/ttrss/util/FontSizeDialogPreference.java rename to orgfoxttrss/src/main/java/org/fox/ttrss/util/FontSizeDialogPreference.java index a4220fd1..ec7af2e5 100644 --- a/src/org/fox/ttrss/util/FontSizeDialogPreference.java +++ b/orgfoxttrss/src/main/java/org/fox/ttrss/util/FontSizeDialogPreference.java @@ -1,224 +1,224 @@ -package org.fox.ttrss.util; - -// http://www.lukehorvat.com/blog/android-seekbardialogpreference/ - -import org.fox.ttrss.R; - -import android.content.Context; -import android.content.res.TypedArray; -import android.os.Parcel; -import android.os.Parcelable; -import android.preference.DialogPreference; -import android.util.AttributeSet; -import android.util.TypedValue; -import android.view.View; -import android.widget.SeekBar; -import android.widget.SeekBar.OnSeekBarChangeListener; -import android.widget.TextView; - -/** - * A {@link DialogPreference} that provides a user with the means to select an - * integer from a {@link SeekBar}, and persist it. - * - * @author lukehorvat - * - */ -public class FontSizeDialogPreference extends DialogPreference { - private static final int DEFAULT_MIN_PROGRESS = 9; - private static final int DEFAULT_MAX_PROGRESS = 24; - private static final String DEFAULT_PROGRESS = "0"; - - private int mMinProgress = DEFAULT_MIN_PROGRESS; - private int mMaxProgress = DEFAULT_MAX_PROGRESS; - private int mProgress; - private CharSequence mProgressTextSuffix; - private TextView mProgressText; - private SeekBar mSeekBar; - - public FontSizeDialogPreference(Context context) { - this(context, null); - } - - public FontSizeDialogPreference(Context context, AttributeSet attrs) { - super(context, attrs); - - setProgressTextSuffix(" " + context.getString(R.string.font_size_dialog_suffix)); - - // set layout - setDialogLayoutResource(R.layout.select_font_size_dialog); - setPositiveButtonText(android.R.string.ok); - setNegativeButtonText(android.R.string.cancel); - setDialogIcon(null); - } - - @Override - protected void onSetInitialValue(boolean restore, Object defaultValue) { - setProgress(restore ? Integer.valueOf(getPersistedString(DEFAULT_PROGRESS)) - : Integer.valueOf((String)defaultValue)); - } - - @Override - protected Object onGetDefaultValue(TypedArray a, int index) { - return a.getString(index); - } - - @Override - protected void onBindDialogView(View view) { - super.onBindDialogView(view); - - mProgressText = (TextView) view.findViewById(R.id.text_progress); - - mSeekBar = (SeekBar) view.findViewById(R.id.seek_bar); - mSeekBar.setOnSeekBarChangeListener(new OnSeekBarChangeListener() { - @Override - public void onStopTrackingTouch(SeekBar seekBar) { - } - - @Override - public void onStartTrackingTouch(SeekBar seekBar) { - } - - @Override - public void onProgressChanged(SeekBar seekBar, int progress, - boolean fromUser) { - // update text that displays the current SeekBar progress value - // note: this does not persist the progress value. that is only - // ever done in setProgress() - String progressStr = String.valueOf(progress + mMinProgress); - mProgressText.setText(mProgressTextSuffix == null ? progressStr - : progressStr.concat(mProgressTextSuffix.toString())); - mProgressText.setTextSize(TypedValue.COMPLEX_UNIT_SP, progress + mMinProgress); - } - }); - - mSeekBar.setMax(mMaxProgress - mMinProgress); - mSeekBar.setProgress(mProgress - mMinProgress); - } - - public int getMinProgress() { - return mMinProgress; - } - - public void setMinProgress(int minProgress) { - mMinProgress = minProgress; - setProgress(Math.max(mProgress, mMinProgress)); - } - - public int getMaxProgress() { - return mMaxProgress; - } - - public void setMaxProgress(int maxProgress) { - mMaxProgress = maxProgress; - setProgress(Math.min(mProgress, mMaxProgress)); - } - - public int getProgress() { - return mProgress; - } - - public void setProgress(int progress) { - progress = Math.max(Math.min(progress, mMaxProgress), mMinProgress); - - if (progress != mProgress) { - mProgress = progress; - persistString(String.valueOf(progress)); - notifyChanged(); - } - } - - public CharSequence getProgressTextSuffix() { - return mProgressTextSuffix; - } - - public void setProgressTextSuffix(CharSequence progressTextSuffix) { - mProgressTextSuffix = progressTextSuffix; - } - - @Override - protected void onDialogClosed(boolean positiveResult) { - super.onDialogClosed(positiveResult); - - // when the user selects "OK", persist the new value - if (positiveResult) { - int seekBarProgress = mSeekBar.getProgress() + mMinProgress; - if (callChangeListener(seekBarProgress)) { - setProgress(seekBarProgress); - } - } - } - - @Override - protected Parcelable onSaveInstanceState() { - // save the instance state so that it will survive screen orientation - // changes and other events that may temporarily destroy it - final Parcelable superState = super.onSaveInstanceState(); - - // set the state's value with the class member that holds current - // setting value - final SavedState myState = new SavedState(superState); - myState.minProgress = getMinProgress(); - myState.maxProgress = getMaxProgress(); - myState.progress = getProgress(); - - return myState; - } - - @Override - protected void onRestoreInstanceState(Parcelable state) { - // check whether we saved the state in onSaveInstanceState() - if (state == null || !state.getClass().equals(SavedState.class)) { - // didn't save the state, so call superclass - super.onRestoreInstanceState(state); - return; - } - - // restore the state - SavedState myState = (SavedState) state; - setMinProgress(myState.minProgress); - setMaxProgress(myState.maxProgress); - setProgress(myState.progress); - - super.onRestoreInstanceState(myState.getSuperState()); - } - - private static class SavedState extends BaseSavedState { - int minProgress; - int maxProgress; - int progress; - - public SavedState(Parcelable superState) { - super(superState); - } - - public SavedState(Parcel source) { - super(source); - - minProgress = source.readInt(); - maxProgress = source.readInt(); - progress = source.readInt(); - } - - @Override - public void writeToParcel(Parcel dest, int flags) { - super.writeToParcel(dest, flags); - - dest.writeInt(minProgress); - dest.writeInt(maxProgress); - dest.writeInt(progress); - } - - @SuppressWarnings("unused") - public static final Parcelable.Creator
+ * This class cannot be instantiated.
+ */
+public final class BreadCrumber
+{
+ /**
+ * Static helper method to generate bread crumbs. Bread crumb strings will be properly formatted for the
+ * current language, including right-to-left languages, as long as the proper
+ * {@link com.twofortyfouram.locale.platform.R.string#twofortyfouram_locale_breadcrumb_format} string
+ * resources have been created.
+ *
+ * @param context {@code Context} for loading platform resources. Cannot be null.
+ * @param intent {@code Intent} to extract the bread crumb from.
+ * @param currentCrumb The last element of the bread crumb path.
+ * @return {@code String} presentation of the bread crumb. If the intent parameter is null, then this
+ * method returns currentCrumb. If currentCrumb is null, then this method returns the empty string
+ * "". If intent contains a private Serializable instances as an extra, then this method returns
+ * the empty string "".
+ * @throws IllegalArgumentException if {@code context} is null.
+ */
+ public static CharSequence generateBreadcrumb(final Context context, final Intent intent,
+ final String currentCrumb)
+ {
+ if (null == context)
+ {
+ throw new IllegalArgumentException("context cannot be null"); //$NON-NLS-1$
+ }
+
+ try
+ {
+ if (null == currentCrumb)
+ {
+ Log.w(Constants.LOG_TAG, "currentCrumb cannot be null"); //$NON-NLS-1$
+ return ""; //$NON-NLS-1$
+ }
+ if (null == intent)
+ {
+ Log.w(Constants.LOG_TAG, "intent cannot be null"); //$NON-NLS-1$
+ return currentCrumb;
+ }
+
+ /*
+ * Note: this is vulnerable to a private serializable attack, but the try-catch will solve that.
+ */
+ final String breadcrumbString = intent.getStringExtra(com.twofortyfouram.locale.Intent.EXTRA_STRING_BREADCRUMB);
+ if (null != breadcrumbString)
+ {
+ return context.getString(R.string.twofortyfouram_locale_breadcrumb_format, breadcrumbString, context.getString(R.string.twofortyfouram_locale_breadcrumb_separator), currentCrumb);
+ }
+ return currentCrumb;
+ }
+ catch (final Exception e)
+ {
+ Log.e(Constants.LOG_TAG, "Encountered error generating breadcrumb", e); //$NON-NLS-1$
+ return ""; //$NON-NLS-1$
+ }
+ }
+
+ /**
+ * Private constructor prevents instantiation.
+ *
+ * @throws UnsupportedOperationException because this class cannot be instantiated.
+ */
+ private BreadCrumber()
+ {
+ throw new UnsupportedOperationException("This class is non-instantiable"); //$NON-NLS-1$
+ }
+}
\ No newline at end of file
diff --git a/taskerlocaleapi/src/main/java/com/twofortyfouram/locale/Constants.java b/taskerlocaleapi/src/main/java/com/twofortyfouram/locale/Constants.java
new file mode 100644
index 00000000..7e22c518
--- /dev/null
+++ b/taskerlocaleapi/src/main/java/com/twofortyfouram/locale/Constants.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2013 two forty four a.m. LLC
+ * If a condition returns UNKNOWN, then Locale will use the last known return value on a best-effort
+ * basis. Best-effort means that Locale may not persist known values forever (e.g. last known values could
+ * hypothetically be cleared after a device reboot or a restart of the Locale process. If
+ * there is no last known return value, then unknown is treated as not satisfied (false).
+ *
+ * The purpose of an UNKNOWN result is to allow a plug-in condition more than 10 seconds to process a
+ * requery. A {@code BroadcastReceiver} must return within 10 seconds, otherwise it will be killed by
+ * Android. A plug-in that needs more than 10 seconds might initially return
+ * {@link #RESULT_CONDITION_UNKNOWN}, subsequently request a requery, and then return either
+ * {@link #RESULT_CONDITION_SATISFIED} or {@link #RESULT_CONDITION_UNSATISFIED}.
+ *
+ * @see Intent#ACTION_QUERY_CONDITION
+ */
+ public static final int RESULT_CONDITION_UNKNOWN = 18;
+
+ /**
+ * {@code Intent} action {@code String} broadcast by Locale to create or edit a plug-in setting. When
+ * Locale broadcasts this {@code Intent}, it will be sent directly to the package and class of the
+ * plug-in's {@code Activity}. The {@code Intent} may contain a {@link #EXTRA_BUNDLE} that was previously
+ * set by the {@code Activity} result of {@link #ACTION_EDIT_SETTING}.
+ *
+ * There SHOULD be only one {@code Activity} per APK that implements this {@code Intent}. If a single APK
+ * wishes to export multiple plug-ins, it MAY implement multiple Activity instances that implement this
+ * {@code Intent}, however there must only be a single {@link #ACTION_FIRE_SETTING} receiver. In this
+ * scenario, it is the responsibility of the Activities to store enough data in {@link #EXTRA_BUNDLE} to
+ * allow this receiver to disambiguate which "plug-in" is being fired. To avoid user confusion, it is
+ * recommended that only a single plug-in be implemented per APK.
+ *
+ * @see Intent#EXTRA_BUNDLE
+ * @see Intent#EXTRA_STRING_BREADCRUMB
+ */
+ public static final String ACTION_EDIT_SETTING = "com.twofortyfouram.locale.intent.action.EDIT_SETTING"; //$NON-NLS-1$
+
+ /**
+ * {@code Intent} action {@code String} broadcast by Locale to fire a plug-in setting. When Locale
+ * broadcasts this {@code Intent}, it will be sent directly to the package and class of the plug-in's
+ * {@code BroadcastReceiver}. The {@code Intent} will contain a {@link #EXTRA_BUNDLE} that was previously
+ * set by the {@code Activity} result of {@link #ACTION_EDIT_SETTING}.
+ *
+ * There MUST be only one {@code BroadcastReceiver} per APK that implements this {@code Intent}.
+ *
+ * @see Intent#EXTRA_BUNDLE
+ */
+ public static final String ACTION_FIRE_SETTING = "com.twofortyfouram.locale.intent.action.FIRE_SETTING"; //$NON-NLS-1$
+
+ /**
+ * {@code Intent} action {@code String} broadcast by Locale to create or edit a plug-in condition. When
+ * Locale broadcasts this {@code Intent}, it will be sent directly to the package and class of the
+ * plug-in's {@code Activity}. The {@code Intent} may contain a store-and-forward {@link #EXTRA_BUNDLE}
+ * that was previously set by the {@code Activity} result of {@link #ACTION_EDIT_CONDITION}.
+ *
+ * There SHOULD be only one {@code Activity} per APK that implements this {@code Intent}. If a single APK
+ * wishes to export multiple plug-ins, it MAY implement multiple Activity instances that implement this
+ * {@code Intent}, however there must only be a single {@link #ACTION_QUERY_CONDITION} receiver. In this
+ * scenario, it is the responsibility of the Activities to store enough data in {@link #EXTRA_BUNDLE} to
+ * allow this receiver to disambiguate which "plug-in" is being queried. To avoid user confusion, it is
+ * recommended that only a single plug-in be implemented per APK.
+ *
+ * @see Intent#EXTRA_BUNDLE
+ * @see Intent#EXTRA_STRING_BREADCRUMB
+ */
+ public static final String ACTION_EDIT_CONDITION = "com.twofortyfouram.locale.intent.action.EDIT_CONDITION"; //$NON-NLS-1$
+
+ /**
+ * Ordered {@code Intent} action {@code String} broadcast by Locale to query a plug-in condition. When
+ * Locale broadcasts this {@code Intent}, it will be sent directly to the package and class of the
+ * plug-in's {@code BroadcastReceiver}. The {@code Intent} will contain a {@link #EXTRA_BUNDLE} that was
+ * previously set by the {@code Activity} result of {@link #ACTION_EDIT_CONDITION}.
+ *
+ * Since this is an ordered broadcast, the receiver is expected to set an appropriate result code from
+ * {@link #RESULT_CONDITION_SATISFIED}, {@link #RESULT_CONDITION_UNSATISFIED}, and
+ * {@link #RESULT_CONDITION_UNKNOWN}.
+ *
+ * There MUST be only one {@code BroadcastReceiver} per APK that implements this {@code Intent}.
+ *
+ * @see Intent#EXTRA_BUNDLE
+ * @see Intent#RESULT_CONDITION_SATISFIED
+ * @see Intent#RESULT_CONDITION_UNSATISFIED
+ * @see Intent#RESULT_CONDITION_UNKNOWN
+ */
+ public static final String ACTION_QUERY_CONDITION = "com.twofortyfouram.locale.intent.action.QUERY_CONDITION"; //$NON-NLS-1$
+
+ /**
+ * {@code Intent} action {@code String} to notify Locale that a plug-in condition is requesting that
+ * Locale query it via {@link #ACTION_QUERY_CONDITION}. This merely serves as a hint to Locale that a
+ * condition wants to be queried. There is no guarantee as to when or if the plug-in will be queried after
+ * this {@code Intent} is broadcast. If Locale does not respond to the plug-in condition after a
+ * {@link #ACTION_REQUEST_QUERY} Intent is sent, the plug-in SHOULD shut itself down and stop requesting
+ * requeries. A lack of response from Locale indicates that Locale is not currently interested in this
+ * plug-in. When Locale becomes interested in the plug-in again, Locale will send
+ * {@link #ACTION_QUERY_CONDITION}.
+ *
+ * The extra {@link #EXTRA_ACTIVITY} MUST be included, otherwise Locale will ignore this {@code Intent}.
+ *
+ * Plug-in conditions SHOULD NOT use this unless there is some sort of asynchronous event that has
+ * occurred, such as a broadcast {@code Intent} being received by the plug-in. Plug-ins SHOULD NOT
+ * periodically request a requery as a way of implementing polling behavior.
+ *
+ * @see Intent#EXTRA_ACTIVITY
+ */
+ public static final String ACTION_REQUEST_QUERY = "com.twofortyfouram.locale.intent.action.REQUEST_QUERY"; //$NON-NLS-1$
+
+ /**
+ * Type: {@code String}.
+ *
+ * Maps to a {@code String} that represents the {@code Activity} bread crumb path.
+ *
+ * @see BreadCrumber
+ */
+ public static final String EXTRA_STRING_BREADCRUMB = "com.twofortyfouram.locale.intent.extra.BREADCRUMB"; //$NON-NLS-1$
+
+ /**
+ * Type: {@code String}.
+ *
+ * Maps to a {@code String} that represents a blurb. This is returned as an {@code Activity} result extra
+ * from {@link #ACTION_EDIT_CONDITION} or {@link #ACTION_EDIT_SETTING}.
+ *
+ * The blurb is a concise description displayed to the user of what the plug-in is configured to do.
+ */
+ public static final String EXTRA_STRING_BLURB = "com.twofortyfouram.locale.intent.extra.BLURB"; //$NON-NLS-1$
+
+ /**
+ * Type: {@code Bundle}.
+ *
+ * Maps to a {@code Bundle} that contains all of a plug-in's extras.
+ *
+ * Plug-ins MUST NOT store {@link Parcelable} objects in this {@code Bundle}, because {@code Parcelable}
+ * is not a long-term storage format. Also, plug-ins MUST NOT store any serializable object that is not
+ * exposed by the Android SDK.
+ *
+ * The maximum size of a Bundle that can be sent across process boundaries is on the order of 500
+ * kilobytes (base-10), while Locale further limits plug-in Bundles to about 100 kilobytes (base-10).
+ * Although the maximum size is about 100 kilobytes, plug-ins SHOULD keep Bundles much smaller for
+ * performance and memory usage reasons.
+ */
+ public static final String EXTRA_BUNDLE = "com.twofortyfouram.locale.intent.extra.BUNDLE"; //$NON-NLS-1$
+
+ /**
+ * Type: {@code String}.
+ *
+ * Maps to a {@code String} that represents the name of a plug-in's {@code Activity}.
+ *
+ * @see Intent#ACTION_REQUEST_QUERY
+ */
+ public static final String EXTRA_ACTIVITY = "com.twofortyfouram.locale.intent.extra.ACTIVITY"; //$NON-NLS-1$
+}
\ No newline at end of file
diff --git a/taskerlocaleapi/src/main/java/com/twofortyfouram/locale/PackageUtilities.java b/taskerlocaleapi/src/main/java/com/twofortyfouram/locale/PackageUtilities.java
new file mode 100644
index 00000000..a4b623ce
--- /dev/null
+++ b/taskerlocaleapi/src/main/java/com/twofortyfouram/locale/PackageUtilities.java
@@ -0,0 +1,123 @@
+/*
+ * Copyright 2013 two forty four a.m. LLC
+ * Note: A TOCTOU error exists, due to the fact that the package could be uninstalled at any time.
+ *
+ * Note: If there are multiple hosts, this method will return one of them. The interface of this method
+ * makes no guarantee which host will returned, nor whether that host will be consistently returned.
+ *
+ * @param manager an instance of {@code PackageManager}. Cannot be null.
+ * @param packageHint hint as to which package should take precedence. This parameter may be null.
+ * @return {@code String} package name of a host for the Locale Developer Platform, such as
+ * "com.twofortyfouram.locale". If no such package is found, returns null.
+ */
+ public static String getCompatiblePackage(final PackageManager manager, final String packageHint)
+ {
+ /*
+ * The interface for this method makes no guarantees as to which host will be returned. However the
+ * implementation is more predictable.
+ */
+
+ final List