diff --git a/external/ColorPickerPreference/.classpath b/external/ColorPickerPreference/.classpath new file mode 100644 index 00000000..a4763d1e --- /dev/null +++ b/external/ColorPickerPreference/.classpath @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/external/ColorPickerPreference/.gitattributes b/external/ColorPickerPreference/.gitattributes new file mode 100644 index 00000000..dfe07704 --- /dev/null +++ b/external/ColorPickerPreference/.gitattributes @@ -0,0 +1,2 @@ +# Auto detect text files and perform LF normalization +* text=auto diff --git a/external/ColorPickerPreference/.gitignore b/external/ColorPickerPreference/.gitignore new file mode 100644 index 00000000..4b1a60ae --- /dev/null +++ b/external/ColorPickerPreference/.gitignore @@ -0,0 +1,4 @@ +/bin +/gen +.classpath +.project \ No newline at end of file diff --git a/external/ColorPickerPreference/.project b/external/ColorPickerPreference/.project new file mode 100644 index 00000000..b3e7744d --- /dev/null +++ b/external/ColorPickerPreference/.project @@ -0,0 +1,33 @@ + + + ColorPickerPreference + + + + + + com.android.ide.eclipse.adt.ResourceManagerBuilder + + + + + com.android.ide.eclipse.adt.PreCompilerBuilder + + + + + org.eclipse.jdt.core.javabuilder + + + + + com.android.ide.eclipse.adt.ApkBuilder + + + + + + com.android.ide.eclipse.adt.AndroidNature + org.eclipse.jdt.core.javanature + + diff --git a/external/ColorPickerPreference/AndroidManifest.xml b/external/ColorPickerPreference/AndroidManifest.xml new file mode 100644 index 00000000..e09215a9 --- /dev/null +++ b/external/ColorPickerPreference/AndroidManifest.xml @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/external/ColorPickerPreference/CHANGELOG.rst b/external/ColorPickerPreference/CHANGELOG.rst new file mode 100644 index 00000000..5342d2ac --- /dev/null +++ b/external/ColorPickerPreference/CHANGELOG.rst @@ -0,0 +1,26 @@ +================================ +ColorPickerPreference Change Log +================================ + +2011-02-11 v1.11: +---------------- +fix: color controls not visible in landscape orientation +fix: colorPickerDialog constructor was protected + +2011-01-25 v1.1: +---------------- +* new: Alpha Slider is disabled by default +* new: Alpha Slider can be enabled: + * with preference XML using attribute alphaSlider="true" + * with function setAlphaSliderEnabled(true) +* new: defaultValue in preference XML now accepts HEX color code: + * #FF00FF, rgb + * #FF00FF00, argb + +2011-01-20 v1.01: +----------------- +fix: sometimes preview color disappear + +2011-01-19 v1.0: +---------------- +release \ No newline at end of file diff --git a/external/ColorPickerPreference/LICENSE b/external/ColorPickerPreference/LICENSE new file mode 100644 index 00000000..da9cd5cb --- /dev/null +++ b/external/ColorPickerPreference/LICENSE @@ -0,0 +1,15 @@ +/* + * Copyright (C) 2011 Sergey Margaritov & Daniel Nilsson + * + * 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. + */ \ No newline at end of file diff --git a/external/ColorPickerPreference/README.rst b/external/ColorPickerPreference/README.rst new file mode 100644 index 00000000..bffa417a --- /dev/null +++ b/external/ColorPickerPreference/README.rst @@ -0,0 +1,47 @@ +===================== +ColorPickerPreference +===================== + +Generally used classes by Daniel Nilsson. +ColorPickerPreference class by Sergey Margaritov. +Packed by Sergey Margaritov. + +Features +======== + +* Color Area +* Hue Slider +* Alpha Slider (disabled by default) +* Old & New Color +* Color Preview in Preferences List + +Requirements +============ + +Tested with APIv7, but maybe will work with early versions + +Usage +===== + +You can see some tests inside + +:: + + + alphaSlider="true" + /> + +To enable Alpha Slider in your code use function: +:: + setAlphaSliderEnabled(boolean enable) + +Screens +======= + +* .. image:: https://github.com/attenzione/android-ColorPickerPreference/raw/master/screen_1.png + +* .. image:: https://github.com/attenzione/android-ColorPickerPreference/raw/master/screen_2.png \ No newline at end of file diff --git a/external/ColorPickerPreference/proguard.cfg b/external/ColorPickerPreference/proguard.cfg new file mode 100644 index 00000000..8ad7d335 --- /dev/null +++ b/external/ColorPickerPreference/proguard.cfg @@ -0,0 +1,34 @@ +-optimizationpasses 5 +-dontusemixedcaseclassnames +-dontskipnonpubliclibraryclasses +-dontpreverify +-verbose +-optimizations !code/simplification/arithmetic,!field/*,!class/merging/* + +-keep public class * extends android.app.Activity +-keep public class * extends android.app.Application +-keep public class * extends android.app.Service +-keep public class * extends android.content.BroadcastReceiver +-keep public class * extends android.content.ContentProvider +-keep public class com.android.vending.licensing.ILicensingService + +-keepclasseswithmembernames class * { + native ; +} + +-keepclasseswithmembernames class * { + public (android.content.Context, android.util.AttributeSet); +} + +-keepclasseswithmembernames class * { + public (android.content.Context, android.util.AttributeSet, int); +} + +-keepclassmembers enum * { + public static **[] values(); + public static ** valueOf(java.lang.String); +} + +-keep class * implements android.os.Parcelable { + public static final android.os.Parcelable$Creator *; +} diff --git a/external/ColorPickerPreference/project.properties b/external/ColorPickerPreference/project.properties new file mode 100644 index 00000000..616f300c --- /dev/null +++ b/external/ColorPickerPreference/project.properties @@ -0,0 +1,12 @@ +# This file is automatically generated by Android Tools. +# Do not modify this file -- YOUR CHANGES WILL BE ERASED! +# +# This file must be checked in Version Control Systems. +# +# To customize properties used by the Ant build system use, +# "ant.properties", and override values to adapt the script to your +# project structure. + +android.library=true +# Project target. +target=android-16 diff --git a/external/ColorPickerPreference/res/drawable-hdpi/icon.png b/external/ColorPickerPreference/res/drawable-hdpi/icon.png new file mode 100644 index 00000000..8074c4c5 Binary files /dev/null and b/external/ColorPickerPreference/res/drawable-hdpi/icon.png differ diff --git a/external/ColorPickerPreference/res/drawable-ldpi/icon.png b/external/ColorPickerPreference/res/drawable-ldpi/icon.png new file mode 100644 index 00000000..1095584e Binary files /dev/null and b/external/ColorPickerPreference/res/drawable-ldpi/icon.png differ diff --git a/external/ColorPickerPreference/res/drawable-mdpi/icon.png b/external/ColorPickerPreference/res/drawable-mdpi/icon.png new file mode 100644 index 00000000..a07c69fa Binary files /dev/null and b/external/ColorPickerPreference/res/drawable-mdpi/icon.png differ diff --git a/external/ColorPickerPreference/res/layout-land/dialog_color_picker.xml b/external/ColorPickerPreference/res/layout-land/dialog_color_picker.xml new file mode 100644 index 00000000..cf567016 --- /dev/null +++ b/external/ColorPickerPreference/res/layout-land/dialog_color_picker.xml @@ -0,0 +1,77 @@ + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/external/ColorPickerPreference/res/layout/dialog_color_picker.xml b/external/ColorPickerPreference/res/layout/dialog_color_picker.xml new file mode 100644 index 00000000..00ee3753 --- /dev/null +++ b/external/ColorPickerPreference/res/layout/dialog_color_picker.xml @@ -0,0 +1,78 @@ + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/external/ColorPickerPreference/res/values/integer.xml b/external/ColorPickerPreference/res/values/integer.xml new file mode 100644 index 00000000..e3626064 --- /dev/null +++ b/external/ColorPickerPreference/res/values/integer.xml @@ -0,0 +1,5 @@ + + + 0xff000000 + 0xff00ff00 + \ No newline at end of file diff --git a/external/ColorPickerPreference/res/values/strings.xml b/external/ColorPickerPreference/res/values/strings.xml new file mode 100644 index 00000000..251a66f4 --- /dev/null +++ b/external/ColorPickerPreference/res/values/strings.xml @@ -0,0 +1,21 @@ + + + + Hello World, Main! + ColorPickerPreference + + + Color Picker + Press on Color to apply + + + Category + Color 1 + black color by default, set by reference + Color 2 + not persistent color\nalpha slider added via code + Color 3 + picker with alpha slider + Color 4 + color set with HEX code in xml + diff --git a/external/ColorPickerPreference/res/xml/settings.xml b/external/ColorPickerPreference/res/xml/settings.xml new file mode 100644 index 00000000..0cd87af9 --- /dev/null +++ b/external/ColorPickerPreference/res/xml/settings.xml @@ -0,0 +1,32 @@ + + + + + + + + + \ No newline at end of file diff --git a/external/ColorPickerPreference/screen_1.png b/external/ColorPickerPreference/screen_1.png new file mode 100644 index 00000000..4798dae7 Binary files /dev/null and b/external/ColorPickerPreference/screen_1.png differ diff --git a/external/ColorPickerPreference/screen_2.png b/external/ColorPickerPreference/screen_2.png new file mode 100644 index 00000000..3bb2d671 Binary files /dev/null and b/external/ColorPickerPreference/screen_2.png differ diff --git a/external/ColorPickerPreference/src/net/margaritov/preference/colorpicker/AlphaPatternDrawable.java b/external/ColorPickerPreference/src/net/margaritov/preference/colorpicker/AlphaPatternDrawable.java new file mode 100644 index 00000000..ff9c3c84 --- /dev/null +++ b/external/ColorPickerPreference/src/net/margaritov/preference/colorpicker/AlphaPatternDrawable.java @@ -0,0 +1,128 @@ +/* + * Copyright (C) 2010 Daniel Nilsson + * + * 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 net.margaritov.preference.colorpicker; + +import android.graphics.Bitmap; +import android.graphics.Bitmap.Config; +import android.graphics.Canvas; +import android.graphics.ColorFilter; +import android.graphics.Paint; +import android.graphics.Rect; +import android.graphics.drawable.Drawable; + +/** + * This drawable that draws a simple white and gray chessboard pattern. + * It's pattern you will often see as a background behind a + * partly transparent image in many applications. + * @author Daniel Nilsson + */ +public class AlphaPatternDrawable extends Drawable { + + private int mRectangleSize = 10; + + private Paint mPaint = new Paint(); + private Paint mPaintWhite = new Paint(); + private Paint mPaintGray = new Paint(); + + private int numRectanglesHorizontal; + private int numRectanglesVertical; + + /** + * Bitmap in which the pattern will be cahched. + */ + private Bitmap mBitmap; + + public AlphaPatternDrawable(int rectangleSize) { + mRectangleSize = rectangleSize; + mPaintWhite.setColor(0xffffffff); + mPaintGray.setColor(0xffcbcbcb); + } + + @Override + public void draw(Canvas canvas) { + canvas.drawBitmap(mBitmap, null, getBounds(), mPaint); + } + + @Override + public int getOpacity() { + return 0; + } + + @Override + public void setAlpha(int alpha) { + throw new UnsupportedOperationException("Alpha is not supported by this drawwable."); + } + + @Override + public void setColorFilter(ColorFilter cf) { + throw new UnsupportedOperationException("ColorFilter is not supported by this drawwable."); + } + + @Override + protected void onBoundsChange(Rect bounds) { + super.onBoundsChange(bounds); + + int height = bounds.height(); + int width = bounds.width(); + + numRectanglesHorizontal = (int) Math.ceil((width / mRectangleSize)); + numRectanglesVertical = (int) Math.ceil(height / mRectangleSize); + + generatePatternBitmap(); + + } + + /** + * This will generate a bitmap with the pattern + * as big as the rectangle we were allow to draw on. + * We do this to chache the bitmap so we don't need to + * recreate it each time draw() is called since it + * takes a few milliseconds. + */ + private void generatePatternBitmap(){ + + if(getBounds().width() <= 0 || getBounds().height() <= 0){ + return; + } + + mBitmap = Bitmap.createBitmap(getBounds().width(), getBounds().height(), Config.ARGB_8888); + Canvas canvas = new Canvas(mBitmap); + + Rect r = new Rect(); + boolean verticalStartWhite = true; + for (int i = 0; i <= numRectanglesVertical; i++) { + + boolean isWhite = verticalStartWhite; + for (int j = 0; j <= numRectanglesHorizontal; j++) { + + r.top = i * mRectangleSize; + r.left = j * mRectangleSize; + r.bottom = r.top + mRectangleSize; + r.right = r.left + mRectangleSize; + + canvas.drawRect(r, isWhite ? mPaintWhite : mPaintGray); + + isWhite = !isWhite; + } + + verticalStartWhite = !verticalStartWhite; + + } + + } + +} \ No newline at end of file diff --git a/external/ColorPickerPreference/src/net/margaritov/preference/colorpicker/ColorPickerDialog.java b/external/ColorPickerPreference/src/net/margaritov/preference/colorpicker/ColorPickerDialog.java new file mode 100644 index 00000000..a63d48c3 --- /dev/null +++ b/external/ColorPickerPreference/src/net/margaritov/preference/colorpicker/ColorPickerDialog.java @@ -0,0 +1,142 @@ +/* + * Copyright (C) 2010 Daniel Nilsson + * + * 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 net.margaritov.preference.colorpicker; + +import android.app.Dialog; +import android.content.Context; +import android.graphics.PixelFormat; +import android.os.Bundle; +import android.view.LayoutInflater; +import android.view.View; +import android.widget.LinearLayout; + +public class ColorPickerDialog + extends + Dialog + implements + ColorPickerView.OnColorChangedListener, + View.OnClickListener { + + private ColorPickerView mColorPicker; + + private ColorPickerPanelView mOldColor; + private ColorPickerPanelView mNewColor; + + private OnColorChangedListener mListener; + + public interface OnColorChangedListener { + public void onColorChanged(int color); + } + + public ColorPickerDialog(Context context, int initialColor) { + super(context); + + init(initialColor); + } + + private void init(int color) { + // To fight color banding. + getWindow().setFormat(PixelFormat.RGBA_8888); + + setUp(color); + + } + + private void setUp(int color) { + + LayoutInflater inflater = (LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE); + + View layout = inflater.inflate(R.layout.dialog_color_picker, null); + + setContentView(layout); + + setTitle(R.string.dialog_color_picker); + + mColorPicker = (ColorPickerView) layout.findViewById(R.id.color_picker_view); + mOldColor = (ColorPickerPanelView) layout.findViewById(R.id.old_color_panel); + mNewColor = (ColorPickerPanelView) layout.findViewById(R.id.new_color_panel); + + ((LinearLayout) mOldColor.getParent()).setPadding( + Math.round(mColorPicker.getDrawingOffset()), + 0, + Math.round(mColorPicker.getDrawingOffset()), + 0 + ); + + mOldColor.setOnClickListener(this); + mNewColor.setOnClickListener(this); + mColorPicker.setOnColorChangedListener(this); + mOldColor.setColor(color); + mColorPicker.setColor(color, true); + + } + + @Override + public void onColorChanged(int color) { + + mNewColor.setColor(color); + + /* + if (mListener != null) { + mListener.onColorChanged(color); + } + */ + + } + + public void setAlphaSliderVisible(boolean visible) { + mColorPicker.setAlphaSliderVisible(visible); + } + + /** + * Set a OnColorChangedListener to get notified when the color + * selected by the user has changed. + * @param listener + */ + public void setOnColorChangedListener(OnColorChangedListener listener){ + mListener = listener; + } + + public int getColor() { + return mColorPicker.getColor(); + } + + @Override + public void onClick(View v) { + if (v.getId() == R.id.new_color_panel) { + if (mListener != null) { + mListener.onColorChanged(mNewColor.getColor()); + } + } + dismiss(); + } + + @Override + public Bundle onSaveInstanceState() { + Bundle state = super.onSaveInstanceState(); + state.putInt("old_color", mOldColor.getColor()); + state.putInt("new_color", mNewColor.getColor()); + return state; + } + + @Override + public void onRestoreInstanceState(Bundle savedInstanceState) { + super.onRestoreInstanceState(savedInstanceState); + mOldColor.setColor(savedInstanceState.getInt("old_color")); + mColorPicker.setColor(savedInstanceState.getInt("new_color"), true); + } +} diff --git a/external/ColorPickerPreference/src/net/margaritov/preference/colorpicker/ColorPickerPanelView.java b/external/ColorPickerPreference/src/net/margaritov/preference/colorpicker/ColorPickerPanelView.java new file mode 100644 index 00000000..b0003662 --- /dev/null +++ b/external/ColorPickerPreference/src/net/margaritov/preference/colorpicker/ColorPickerPanelView.java @@ -0,0 +1,171 @@ +/* + * Copyright (C) 2010 Daniel Nilsson + * + * 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 net.margaritov.preference.colorpicker; + +import android.content.Context; +import android.graphics.Canvas; +import android.graphics.Paint; +import android.graphics.RectF; +import android.util.AttributeSet; +import android.view.View; + +/** + * This class draws a panel which which will be filled with a color which can be set. + * It can be used to show the currently selected color which you will get from + * the {@link ColorPickerView}. + * @author Daniel Nilsson + * + */ +public class ColorPickerPanelView extends View { + + /** + * The width in pixels of the border + * surrounding the color panel. + */ + private final static float BORDER_WIDTH_PX = 1; + + private float mDensity = 1f; + + private int mBorderColor = 0xff6E6E6E; + private int mColor = 0xff000000; + + private Paint mBorderPaint; + private Paint mColorPaint; + + private RectF mDrawingRect; + private RectF mColorRect; + + private AlphaPatternDrawable mAlphaPattern; + + + public ColorPickerPanelView(Context context){ + this(context, null); + } + + public ColorPickerPanelView(Context context, AttributeSet attrs){ + this(context, attrs, 0); + } + + public ColorPickerPanelView(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + init(); + } + + private void init(){ + mBorderPaint = new Paint(); + mColorPaint = new Paint(); + mDensity = getContext().getResources().getDisplayMetrics().density; + } + + + @Override + protected void onDraw(Canvas canvas) { + + final RectF rect = mColorRect; + + if(BORDER_WIDTH_PX > 0){ + mBorderPaint.setColor(mBorderColor); + canvas.drawRect(mDrawingRect, mBorderPaint); + } + + if(mAlphaPattern != null){ + mAlphaPattern.draw(canvas); + } + + mColorPaint.setColor(mColor); + + canvas.drawRect(rect, mColorPaint); + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + + int width = MeasureSpec.getSize(widthMeasureSpec); + int height = MeasureSpec.getSize(heightMeasureSpec); + + setMeasuredDimension(width, height); + } + + @Override + protected void onSizeChanged(int w, int h, int oldw, int oldh) { + super.onSizeChanged(w, h, oldw, oldh); + + mDrawingRect = new RectF(); + mDrawingRect.left = getPaddingLeft(); + mDrawingRect.right = w - getPaddingRight(); + mDrawingRect.top = getPaddingTop(); + mDrawingRect.bottom = h - getPaddingBottom(); + + setUpColorRect(); + + } + + private void setUpColorRect(){ + final RectF dRect = mDrawingRect; + + float left = dRect.left + BORDER_WIDTH_PX; + float top = dRect.top + BORDER_WIDTH_PX; + float bottom = dRect.bottom - BORDER_WIDTH_PX; + float right = dRect.right - BORDER_WIDTH_PX; + + mColorRect = new RectF(left,top, right, bottom); + + mAlphaPattern = new AlphaPatternDrawable((int)(5 * mDensity)); + + mAlphaPattern.setBounds( + Math.round(mColorRect.left), + Math.round(mColorRect.top), + Math.round(mColorRect.right), + Math.round(mColorRect.bottom) + ); + + } + + /** + * Set the color that should be shown by this view. + * @param color + */ + public void setColor(int color){ + mColor = color; + invalidate(); + } + + /** + * Get the color currently show by this view. + * @return + */ + public int getColor(){ + return mColor; + } + + /** + * Set the color of the border surrounding the panel. + * @param color + */ + public void setBorderColor(int color){ + mBorderColor = color; + invalidate(); + } + + /** + * Get the color of the border surrounding the panel. + */ + public int getBorderColor(){ + return mBorderColor; + } + +} \ No newline at end of file diff --git a/external/ColorPickerPreference/src/net/margaritov/preference/colorpicker/ColorPickerPreference.java b/external/ColorPickerPreference/src/net/margaritov/preference/colorpicker/ColorPickerPreference.java new file mode 100644 index 00000000..8dded67a --- /dev/null +++ b/external/ColorPickerPreference/src/net/margaritov/preference/colorpicker/ColorPickerPreference.java @@ -0,0 +1,287 @@ +/* + * Copyright (C) 2011 Sergey Margaritov + * + * 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 net.margaritov.preference.colorpicker; + +import android.content.Context; +import android.content.res.TypedArray; +import android.graphics.Bitmap; +import android.graphics.Bitmap.Config; +import android.graphics.Color; +import android.os.Bundle; +import android.os.Parcel; +import android.os.Parcelable; +import android.preference.Preference; +import android.util.AttributeSet; +import android.view.View; +import android.widget.ImageView; +import android.widget.LinearLayout; + +/** + * A preference type that allows a user to choose a time + * @author Sergey Margaritov + */ +public class ColorPickerPreference + extends + Preference + implements + Preference.OnPreferenceClickListener, + ColorPickerDialog.OnColorChangedListener { + + View mView; + ColorPickerDialog mDialog; + private int mValue = Color.BLACK; + private float mDensity = 0; + private boolean mAlphaSliderEnabled = false; + + public ColorPickerPreference(Context context) { + super(context); + init(context, null); + } + + public ColorPickerPreference(Context context, AttributeSet attrs) { + super(context, attrs); + init(context, attrs); + } + + public ColorPickerPreference(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + init(context, attrs); + } + + @Override + protected Object onGetDefaultValue(TypedArray a, int index) { + return a.getColor(index, Color.BLACK); + } + + @Override + protected void onSetInitialValue(boolean restoreValue, Object defaultValue) { + onColorChanged(restoreValue ? getPersistedInt(mValue) : (Integer) defaultValue); + } + + private void init(Context context, AttributeSet attrs) { + mDensity = getContext().getResources().getDisplayMetrics().density; + setOnPreferenceClickListener(this); + if (attrs != null) { + mAlphaSliderEnabled = attrs.getAttributeBooleanValue(null, "alphaSlider", false); + } + } + + @Override + protected void onBindView(View view) { + super.onBindView(view); + mView = view; + setPreviewColor(); + } + + private void setPreviewColor() { + if (mView == null) return; + ImageView iView = new ImageView(getContext()); + LinearLayout widgetFrameView = ((LinearLayout)mView.findViewById(android.R.id.widget_frame)); + if (widgetFrameView == null) return; + widgetFrameView.setVisibility(View.VISIBLE); + widgetFrameView.setPadding( + widgetFrameView.getPaddingLeft(), + widgetFrameView.getPaddingTop(), + (int)(mDensity * 8), + widgetFrameView.getPaddingBottom() + ); + // remove already create preview image + int count = widgetFrameView.getChildCount(); + if (count > 0) { + widgetFrameView.removeViews(0, count); + } + widgetFrameView.addView(iView); + widgetFrameView.setMinimumWidth(0); + iView.setBackgroundDrawable(new AlphaPatternDrawable((int)(5 * mDensity))); + iView.setImageBitmap(getPreviewBitmap()); + } + + private Bitmap getPreviewBitmap() { + int d = (int) (mDensity * 31); //30dip + int color = mValue; + Bitmap bm = Bitmap.createBitmap(d, d, Config.ARGB_8888); + int w = bm.getWidth(); + int h = bm.getHeight(); + int c = color; + for (int i = 0; i < w; i++) { + for (int j = i; j < h; j++) { + c = (i <= 1 || j <= 1 || i >= w-2 || j >= h-2) ? Color.GRAY : color; + bm.setPixel(i, j, c); + if (i != j) { + bm.setPixel(j, i, c); + } + } + } + + return bm; + } + + @Override + public void onColorChanged(int color) { + if (isPersistent()) { + persistInt(color); + } + mValue = color; + setPreviewColor(); + try { + getOnPreferenceChangeListener().onPreferenceChange(this, color); + } catch (NullPointerException e) { + + } + } + + public boolean onPreferenceClick(Preference preference) { + showDialog(null); + return false; + } + + protected void showDialog(Bundle state) { + mDialog = new ColorPickerDialog(getContext(), mValue); + mDialog.setOnColorChangedListener(this); + if (mAlphaSliderEnabled) { + mDialog.setAlphaSliderVisible(true); + } + if (state != null) { + mDialog.onRestoreInstanceState(state); + } + mDialog.show(); + } + + /** + * Toggle Alpha Slider visibility (by default it's disabled) + * @param enable + */ + public void setAlphaSliderEnabled(boolean enable) { + mAlphaSliderEnabled = enable; + } + + /** + * For custom purposes. Not used by ColorPickerPreferrence + * @param color + * @author Unknown + */ + public static String convertToARGB(int color) { + String alpha = Integer.toHexString(Color.alpha(color)); + String red = Integer.toHexString(Color.red(color)); + String green = Integer.toHexString(Color.green(color)); + String blue = Integer.toHexString(Color.blue(color)); + + if (alpha.length() == 1) { + alpha = "0" + alpha; + } + + if (red.length() == 1) { + red = "0" + red; + } + + if (green.length() == 1) { + green = "0" + green; + } + + if (blue.length() == 1) { + blue = "0" + blue; + } + + return "#" + alpha + red + green + blue; + } + + /** + * For custom purposes. Not used by ColorPickerPreferrence + * @param argb + * @throws NumberFormatException + * @author Unknown + */ + public static int convertToColorInt(String argb) throws NumberFormatException { + + if (argb.startsWith("#")) { + argb = argb.replace("#", ""); + } + + int alpha = -1, red = -1, green = -1, blue = -1; + + if (argb.length() == 8) { + alpha = Integer.parseInt(argb.substring(0, 2), 16); + red = Integer.parseInt(argb.substring(2, 4), 16); + green = Integer.parseInt(argb.substring(4, 6), 16); + blue = Integer.parseInt(argb.substring(6, 8), 16); + } + else if (argb.length() == 6) { + alpha = 255; + red = Integer.parseInt(argb.substring(0, 2), 16); + green = Integer.parseInt(argb.substring(2, 4), 16); + blue = Integer.parseInt(argb.substring(4, 6), 16); + } + + return Color.argb(alpha, red, green, blue); + } + + @Override + protected Parcelable onSaveInstanceState() { + final Parcelable superState = super.onSaveInstanceState(); + if (mDialog == null || !mDialog.isShowing()) { + return superState; + } + + final SavedState myState = new SavedState(superState); + myState.dialogBundle = mDialog.onSaveInstanceState(); + return myState; + } + + @Override + protected void onRestoreInstanceState(Parcelable state) { + if (state == null || !(state instanceof SavedState)) { + // Didn't save state for us in onSaveInstanceState + super.onRestoreInstanceState(state); + return; + } + + SavedState myState = (SavedState) state; + super.onRestoreInstanceState(myState.getSuperState()); + showDialog(myState.dialogBundle); + } + + private static class SavedState extends BaseSavedState { + Bundle dialogBundle; + + public SavedState(Parcel source) { + super(source); + dialogBundle = source.readBundle(); + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + super.writeToParcel(dest, flags); + dest.writeBundle(dialogBundle); + } + + public SavedState(Parcelable superState) { + super(superState); + } + + @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]; + } + }; + } +} \ No newline at end of file diff --git a/external/ColorPickerPreference/src/net/margaritov/preference/colorpicker/ColorPickerView.java b/external/ColorPickerPreference/src/net/margaritov/preference/colorpicker/ColorPickerView.java new file mode 100644 index 00000000..c0ec0531 --- /dev/null +++ b/external/ColorPickerPreference/src/net/margaritov/preference/colorpicker/ColorPickerView.java @@ -0,0 +1,952 @@ +/* + * Copyright (C) 2010 Daniel Nilsson + * + * 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 net.margaritov.preference.colorpicker; + +import android.content.Context; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.ComposeShader; +import android.graphics.LinearGradient; +import android.graphics.Paint; +import android.graphics.Point; +import android.graphics.PorterDuff; +import android.graphics.RectF; +import android.graphics.Shader; +import android.graphics.Paint.Align; +import android.graphics.Paint.Style; +import android.graphics.Shader.TileMode; +import android.util.AttributeSet; +import android.view.MotionEvent; +import android.view.View; + +/** + * Displays a color picker to the user and allow them + * to select a color. A slider for the alpha channel is + * also available. Enable it by setting + * setAlphaSliderVisible(boolean) to true. + * @author Daniel Nilsson + */ +public class ColorPickerView extends View { + + private final static int PANEL_SAT_VAL = 0; + private final static int PANEL_HUE = 1; + private final static int PANEL_ALPHA = 2; + + /** + * The width in pixels of the border + * surrounding all color panels. + */ + private final static float BORDER_WIDTH_PX = 1; + + /** + * The width in dp of the hue panel. + */ + private float HUE_PANEL_WIDTH = 30f; + /** + * The height in dp of the alpha panel + */ + private float ALPHA_PANEL_HEIGHT = 20f; + /** + * The distance in dp between the different + * color panels. + */ + private float PANEL_SPACING = 10f; + /** + * The radius in dp of the color palette tracker circle. + */ + private float PALETTE_CIRCLE_TRACKER_RADIUS = 5f; + /** + * The dp which the tracker of the hue or alpha panel + * will extend outside of its bounds. + */ + private float RECTANGLE_TRACKER_OFFSET = 2f; + + + private float mDensity = 1f; + + private OnColorChangedListener mListener; + + private Paint mSatValPaint; + private Paint mSatValTrackerPaint; + + private Paint mHuePaint; + private Paint mHueTrackerPaint; + + private Paint mAlphaPaint; + private Paint mAlphaTextPaint; + + private Paint mBorderPaint; + + private Shader mValShader; + private Shader mSatShader; + private Shader mHueShader; + private Shader mAlphaShader; + + private int mAlpha = 0xff; + private float mHue = 360f; + private float mSat = 0f; + private float mVal = 0f; + + private String mAlphaSliderText = ""; + private int mSliderTrackerColor = 0xff1c1c1c; + private int mBorderColor = 0xff6E6E6E; + private boolean mShowAlphaPanel = false; + + /* + * To remember which panel that has the "focus" when + * processing hardware button data. + */ + private int mLastTouchedPanel = PANEL_SAT_VAL; + + /** + * Offset from the edge we must have or else + * the finger tracker will get clipped when + * it is drawn outside of the view. + */ + private float mDrawingOffset; + + + /* + * Distance form the edges of the view + * of where we are allowed to draw. + */ + private RectF mDrawingRect; + + private RectF mSatValRect; + private RectF mHueRect; + private RectF mAlphaRect; + + private AlphaPatternDrawable mAlphaPattern; + + private Point mStartTouchPoint = null; + + public interface OnColorChangedListener { + public void onColorChanged(int color); + } + + public ColorPickerView(Context context){ + this(context, null); + } + + public ColorPickerView(Context context, AttributeSet attrs) { + this(context, attrs, 0); + } + + public ColorPickerView(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + init(); + } + + private void init(){ + mDensity = getContext().getResources().getDisplayMetrics().density; + PALETTE_CIRCLE_TRACKER_RADIUS *= mDensity; + RECTANGLE_TRACKER_OFFSET *= mDensity; + HUE_PANEL_WIDTH *= mDensity; + ALPHA_PANEL_HEIGHT *= mDensity; + PANEL_SPACING = PANEL_SPACING * mDensity; + + mDrawingOffset = calculateRequiredOffset(); + + initPaintTools(); + + //Needed for receiving trackball motion events. + setFocusable(true); + setFocusableInTouchMode(true); + } + + private void initPaintTools(){ + + mSatValPaint = new Paint(); + mSatValTrackerPaint = new Paint(); + mHuePaint = new Paint(); + mHueTrackerPaint = new Paint(); + mAlphaPaint = new Paint(); + mAlphaTextPaint = new Paint(); + mBorderPaint = new Paint(); + + + mSatValTrackerPaint.setStyle(Style.STROKE); + mSatValTrackerPaint.setStrokeWidth(2f * mDensity); + mSatValTrackerPaint.setAntiAlias(true); + + mHueTrackerPaint.setColor(mSliderTrackerColor); + mHueTrackerPaint.setStyle(Style.STROKE); + mHueTrackerPaint.setStrokeWidth(2f * mDensity); + mHueTrackerPaint.setAntiAlias(true); + + mAlphaTextPaint.setColor(0xff1c1c1c); + mAlphaTextPaint.setTextSize(14f * mDensity); + mAlphaTextPaint.setAntiAlias(true); + mAlphaTextPaint.setTextAlign(Align.CENTER); + mAlphaTextPaint.setFakeBoldText(true); + + + } + + private float calculateRequiredOffset(){ + float offset = Math.max(PALETTE_CIRCLE_TRACKER_RADIUS, RECTANGLE_TRACKER_OFFSET); + offset = Math.max(offset, BORDER_WIDTH_PX * mDensity); + + return offset * 1.5f; + } + + private int[] buildHueColorArray(){ + + int[] hue = new int[361]; + + int count = 0; + for(int i = hue.length -1; i >= 0; i--, count++){ + hue[count] = Color.HSVToColor(new float[]{i, 1f, 1f}); + } + + return hue; + } + + + @Override + protected void onDraw(Canvas canvas) { + + if(mDrawingRect.width() <= 0 || mDrawingRect.height() <= 0) return; + + drawSatValPanel(canvas); + drawHuePanel(canvas); + drawAlphaPanel(canvas); + + } + + private void drawSatValPanel(Canvas canvas){ + + final RectF rect = mSatValRect; + + if(BORDER_WIDTH_PX > 0){ + mBorderPaint.setColor(mBorderColor); + canvas.drawRect(mDrawingRect.left, mDrawingRect.top, rect.right + BORDER_WIDTH_PX, rect.bottom + BORDER_WIDTH_PX, mBorderPaint); + } + + if (mValShader == null) { + mValShader = new LinearGradient(rect.left, rect.top, rect.left, rect.bottom, + 0xffffffff, 0xff000000, TileMode.CLAMP); + } + + int rgb = Color.HSVToColor(new float[]{mHue,1f,1f}); + + mSatShader = new LinearGradient(rect.left, rect.top, rect.right, rect.top, + 0xffffffff, rgb, TileMode.CLAMP); + ComposeShader mShader = new ComposeShader(mValShader, mSatShader, PorterDuff.Mode.MULTIPLY); + mSatValPaint.setShader(mShader); + + canvas.drawRect(rect, mSatValPaint); + + Point p = satValToPoint(mSat, mVal); + + mSatValTrackerPaint.setColor(0xff000000); + canvas.drawCircle(p.x, p.y, PALETTE_CIRCLE_TRACKER_RADIUS - 1f * mDensity, mSatValTrackerPaint); + + mSatValTrackerPaint.setColor(0xffdddddd); + canvas.drawCircle(p.x, p.y, PALETTE_CIRCLE_TRACKER_RADIUS, mSatValTrackerPaint); + + } + + private void drawHuePanel(Canvas canvas){ + + final RectF rect = mHueRect; + + if(BORDER_WIDTH_PX > 0){ + mBorderPaint.setColor(mBorderColor); + canvas.drawRect(rect.left - BORDER_WIDTH_PX, + rect.top - BORDER_WIDTH_PX, + rect.right + BORDER_WIDTH_PX, + rect.bottom + BORDER_WIDTH_PX, + mBorderPaint); + } + + if (mHueShader == null) { + mHueShader = new LinearGradient(rect.left, rect.top, rect.left, rect.bottom, buildHueColorArray(), null, TileMode.CLAMP); + mHuePaint.setShader(mHueShader); + } + + canvas.drawRect(rect, mHuePaint); + + float rectHeight = 4 * mDensity / 2; + + Point p = hueToPoint(mHue); + + RectF r = new RectF(); + r.left = rect.left - RECTANGLE_TRACKER_OFFSET; + r.right = rect.right + RECTANGLE_TRACKER_OFFSET; + r.top = p.y - rectHeight; + r.bottom = p.y + rectHeight; + + + canvas.drawRoundRect(r, 2, 2, mHueTrackerPaint); + + } + + private void drawAlphaPanel(Canvas canvas){ + + if(!mShowAlphaPanel || mAlphaRect == null || mAlphaPattern == null) return; + + final RectF rect = mAlphaRect; + + if(BORDER_WIDTH_PX > 0){ + mBorderPaint.setColor(mBorderColor); + canvas.drawRect(rect.left - BORDER_WIDTH_PX, + rect.top - BORDER_WIDTH_PX, + rect.right + BORDER_WIDTH_PX, + rect.bottom + BORDER_WIDTH_PX, + mBorderPaint); + } + + + mAlphaPattern.draw(canvas); + + float[] hsv = new float[]{mHue,mSat,mVal}; + int color = Color.HSVToColor(hsv); + int acolor = Color.HSVToColor(0, hsv); + + mAlphaShader = new LinearGradient(rect.left, rect.top, rect.right, rect.top, + color, acolor, TileMode.CLAMP); + + + mAlphaPaint.setShader(mAlphaShader); + + canvas.drawRect(rect, mAlphaPaint); + + if(mAlphaSliderText != null && mAlphaSliderText!= ""){ + canvas.drawText(mAlphaSliderText, rect.centerX(), rect.centerY() + 4 * mDensity, mAlphaTextPaint); + } + + float rectWidth = 4 * mDensity / 2; + + Point p = alphaToPoint(mAlpha); + + RectF r = new RectF(); + r.left = p.x - rectWidth; + r.right = p.x + rectWidth; + r.top = rect.top - RECTANGLE_TRACKER_OFFSET; + r.bottom = rect.bottom + RECTANGLE_TRACKER_OFFSET; + + canvas.drawRoundRect(r, 2, 2, mHueTrackerPaint); + + } + + + private Point hueToPoint(float hue){ + + final RectF rect = mHueRect; + final float height = rect.height(); + + Point p = new Point(); + + p.y = (int) (height - (hue * height / 360f) + rect.top); + p.x = (int) rect.left; + + return p; + } + + private Point satValToPoint(float sat, float val){ + + final RectF rect = mSatValRect; + final float height = rect.height(); + final float width = rect.width(); + + Point p = new Point(); + + p.x = (int) (sat * width + rect.left); + p.y = (int) ((1f - val) * height + rect.top); + + return p; + } + + private Point alphaToPoint(int alpha){ + + final RectF rect = mAlphaRect; + final float width = rect.width(); + + Point p = new Point(); + + p.x = (int) (width - (alpha * width / 0xff) + rect.left); + p.y = (int) rect.top; + + return p; + + } + + private float[] pointToSatVal(float x, float y){ + + final RectF rect = mSatValRect; + float[] result = new float[2]; + + float width = rect.width(); + float height = rect.height(); + + if (x < rect.left){ + x = 0f; + } + else if(x > rect.right){ + x = width; + } + else{ + x = x - rect.left; + } + + if (y < rect.top){ + y = 0f; + } + else if(y > rect.bottom){ + y = height; + } + else{ + y = y - rect.top; + } + + + result[0] = 1.f / width * x; + result[1] = 1.f - (1.f / height * y); + + return result; + } + + private float pointToHue(float y){ + + final RectF rect = mHueRect; + + float height = rect.height(); + + if (y < rect.top){ + y = 0f; + } + else if(y > rect.bottom){ + y = height; + } + else{ + y = y - rect.top; + } + + return 360f - (y * 360f / height); + } + + private int pointToAlpha(int x){ + + final RectF rect = mAlphaRect; + final int width = (int) rect.width(); + + if(x < rect.left){ + x = 0; + } + else if(x > rect.right){ + x = width; + } + else{ + x = x - (int)rect.left; + } + + return 0xff - (x * 0xff / width); + + } + + + @Override + public boolean onTrackballEvent(MotionEvent event) { + + float x = event.getX(); + float y = event.getY(); + + boolean update = false; + + + if(event.getAction() == MotionEvent.ACTION_MOVE){ + + switch(mLastTouchedPanel){ + + case PANEL_SAT_VAL: + + float sat, val; + + sat = mSat + x/50f; + val = mVal - y/50f; + + if(sat < 0f){ + sat = 0f; + } + else if(sat > 1f){ + sat = 1f; + } + + if(val < 0f){ + val = 0f; + } + else if(val > 1f){ + val = 1f; + } + + mSat = sat; + mVal = val; + + update = true; + + break; + + case PANEL_HUE: + + float hue = mHue - y * 10f; + + if(hue < 0f){ + hue = 0f; + } + else if(hue > 360f){ + hue = 360f; + } + + mHue = hue; + + update = true; + + break; + + case PANEL_ALPHA: + + if(!mShowAlphaPanel || mAlphaRect == null){ + update = false; + } + else{ + + int alpha = (int) (mAlpha - x*10); + + if(alpha < 0){ + alpha = 0; + } + else if(alpha > 0xff){ + alpha = 0xff; + } + + mAlpha = alpha; + + + update = true; + } + + break; + } + + + } + + + if(update){ + + if(mListener != null){ + mListener.onColorChanged(Color.HSVToColor(mAlpha, new float[]{mHue, mSat, mVal})); + } + + invalidate(); + return true; + } + + + return super.onTrackballEvent(event); + } + + @Override + public boolean onTouchEvent(MotionEvent event) { + + boolean update = false; + + switch(event.getAction()){ + + case MotionEvent.ACTION_DOWN: + + mStartTouchPoint = new Point((int)event.getX(), (int)event.getY()); + + update = moveTrackersIfNeeded(event); + + break; + + case MotionEvent.ACTION_MOVE: + + update = moveTrackersIfNeeded(event); + + break; + + case MotionEvent.ACTION_UP: + + mStartTouchPoint = null; + + update = moveTrackersIfNeeded(event); + + break; + + } + + if(update){ + + if(mListener != null){ + mListener.onColorChanged(Color.HSVToColor(mAlpha, new float[]{mHue, mSat, mVal})); + } + + invalidate(); + return true; + } + + + return super.onTouchEvent(event); + } + + private boolean moveTrackersIfNeeded(MotionEvent event){ + + if(mStartTouchPoint == null) return false; + + boolean update = false; + + int startX = mStartTouchPoint.x; + int startY = mStartTouchPoint.y; + + + if(mHueRect.contains(startX, startY)){ + mLastTouchedPanel = PANEL_HUE; + + mHue = pointToHue(event.getY()); + + update = true; + } + else if(mSatValRect.contains(startX, startY)){ + + mLastTouchedPanel = PANEL_SAT_VAL; + + float[] result = pointToSatVal(event.getX(), event.getY()); + + mSat = result[0]; + mVal = result[1]; + + update = true; + } + else if(mAlphaRect != null && mAlphaRect.contains(startX, startY)){ + + mLastTouchedPanel = PANEL_ALPHA; + + mAlpha = pointToAlpha((int)event.getX()); + + update = true; + } + + + return update; + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + + int width = 0; + int height = 0; + + int widthMode = MeasureSpec.getMode(widthMeasureSpec); + int heightMode = MeasureSpec.getMode(heightMeasureSpec); + + int widthAllowed = MeasureSpec.getSize(widthMeasureSpec); + int heightAllowed = MeasureSpec.getSize(heightMeasureSpec); + + widthAllowed = chooseWidth(widthMode, widthAllowed); + heightAllowed = chooseHeight(heightMode, heightAllowed); + + if(!mShowAlphaPanel){ + + height = (int) (widthAllowed - PANEL_SPACING - HUE_PANEL_WIDTH); + + //If calculated height (based on the width) is more than the allowed height. + if(height > heightAllowed || getTag().equals("landscape")) { + height = heightAllowed; + width = (int) (height + PANEL_SPACING + HUE_PANEL_WIDTH); + } + else{ + width = widthAllowed; + } + } + else{ + + width = (int) (heightAllowed - ALPHA_PANEL_HEIGHT + HUE_PANEL_WIDTH); + + if(width > widthAllowed){ + width = widthAllowed; + height = (int) (widthAllowed - HUE_PANEL_WIDTH + ALPHA_PANEL_HEIGHT); + } + else{ + height = heightAllowed; + } + + } + + setMeasuredDimension(width, height); + } + + private int chooseWidth(int mode, int size){ + if (mode == MeasureSpec.AT_MOST || mode == MeasureSpec.EXACTLY) { + return size; + } else { // (mode == MeasureSpec.UNSPECIFIED) + return getPrefferedWidth(); + } + } + + private int chooseHeight(int mode, int size){ + if (mode == MeasureSpec.AT_MOST || mode == MeasureSpec.EXACTLY) { + return size; + } else { // (mode == MeasureSpec.UNSPECIFIED) + return getPrefferedHeight(); + } + } + + private int getPrefferedWidth(){ + + int width = getPrefferedHeight(); + + if(mShowAlphaPanel){ + width -= (PANEL_SPACING + ALPHA_PANEL_HEIGHT); + } + + + return (int) (width + HUE_PANEL_WIDTH + PANEL_SPACING); + + } + + private int getPrefferedHeight(){ + + int height = (int)(200 * mDensity); + + if(mShowAlphaPanel){ + height += PANEL_SPACING + ALPHA_PANEL_HEIGHT; + } + + return height; + } + + + + @Override + protected void onSizeChanged(int w, int h, int oldw, int oldh) { + super.onSizeChanged(w, h, oldw, oldh); + + mDrawingRect = new RectF(); + mDrawingRect.left = mDrawingOffset + getPaddingLeft(); + mDrawingRect.right = w - mDrawingOffset - getPaddingRight(); + mDrawingRect.top = mDrawingOffset + getPaddingTop(); + mDrawingRect.bottom = h - mDrawingOffset - getPaddingBottom(); + + setUpSatValRect(); + setUpHueRect(); + setUpAlphaRect(); + } + + private void setUpSatValRect(){ + + final RectF dRect = mDrawingRect; + float panelSide = dRect.height() - BORDER_WIDTH_PX * 2; + + if(mShowAlphaPanel){ + panelSide -= PANEL_SPACING + ALPHA_PANEL_HEIGHT; + } + + float left = dRect.left + BORDER_WIDTH_PX; + float top = dRect.top + BORDER_WIDTH_PX; + float bottom = top + panelSide; + float right = left + panelSide; + + mSatValRect = new RectF(left,top, right, bottom); + } + + private void setUpHueRect(){ + final RectF dRect = mDrawingRect; + + float left = dRect.right - HUE_PANEL_WIDTH + BORDER_WIDTH_PX; + float top = dRect.top + BORDER_WIDTH_PX; + float bottom = dRect.bottom - BORDER_WIDTH_PX - (mShowAlphaPanel ? (PANEL_SPACING + ALPHA_PANEL_HEIGHT) : 0); + float right = dRect.right - BORDER_WIDTH_PX; + + mHueRect = new RectF(left, top, right, bottom); + } + + private void setUpAlphaRect() { + + if(!mShowAlphaPanel) return; + + final RectF dRect = mDrawingRect; + + float left = dRect.left + BORDER_WIDTH_PX; + float top = dRect.bottom - ALPHA_PANEL_HEIGHT + BORDER_WIDTH_PX; + float bottom = dRect.bottom - BORDER_WIDTH_PX; + float right = dRect.right - BORDER_WIDTH_PX; + + mAlphaRect = new RectF(left, top, right, bottom); + + mAlphaPattern = new AlphaPatternDrawable((int) (5 * mDensity)); + mAlphaPattern.setBounds( + Math.round(mAlphaRect.left), + Math.round(mAlphaRect.top), + Math.round(mAlphaRect.right), + Math.round(mAlphaRect.bottom) + ); + + } + + + /** + * Set a OnColorChangedListener to get notified when the color + * selected by the user has changed. + * @param listener + */ + public void setOnColorChangedListener(OnColorChangedListener listener){ + mListener = listener; + } + + /** + * Set the color of the border surrounding all panels. + * @param color + */ + public void setBorderColor(int color){ + mBorderColor = color; + invalidate(); + } + + /** + * Get the color of the border surrounding all panels. + */ + public int getBorderColor(){ + return mBorderColor; + } + + /** + * Get the current color this view is showing. + * @return the current color. + */ + public int getColor(){ + return Color.HSVToColor(mAlpha, new float[]{mHue,mSat,mVal}); + } + + /** + * Set the color the view should show. + * @param color The color that should be selected. + */ + public void setColor(int color){ + setColor(color, false); + } + + /** + * Set the color this view should show. + * @param color The color that should be selected. + * @param callback If you want to get a callback to + * your OnColorChangedListener. + */ + public void setColor(int color, boolean callback){ + + int alpha = Color.alpha(color); + int red = Color.red(color); + int blue = Color.blue(color); + int green = Color.green(color); + + float[] hsv = new float[3]; + + Color.RGBToHSV(red, green, blue, hsv); + + mAlpha = alpha; + mHue = hsv[0]; + mSat = hsv[1]; + mVal = hsv[2]; + + if(callback && mListener != null){ + mListener.onColorChanged(Color.HSVToColor(mAlpha, new float[]{mHue, mSat, mVal})); + } + + invalidate(); + } + + /** + * Get the drawing offset of the color picker view. + * The drawing offset is the distance from the side of + * a panel to the side of the view minus the padding. + * Useful if you want to have your own panel below showing + * the currently selected color and want to align it perfectly. + * @return The offset in pixels. + */ + public float getDrawingOffset(){ + return mDrawingOffset; + } + + /** + * Set if the user is allowed to adjust the alpha panel. Default is false. + * If it is set to false no alpha will be set. + * @param visible + */ + public void setAlphaSliderVisible(boolean visible){ + + if(mShowAlphaPanel != visible){ + mShowAlphaPanel = visible; + + /* + * Reset all shader to force a recreation. + * Otherwise they will not look right after + * the size of the view has changed. + */ + mValShader = null; + mSatShader = null; + mHueShader = null; + mAlphaShader = null;; + + requestLayout(); + } + + } + + public void setSliderTrackerColor(int color){ + mSliderTrackerColor = color; + + mHueTrackerPaint.setColor(mSliderTrackerColor); + + invalidate(); + } + + public int getSliderTrackerColor(){ + return mSliderTrackerColor; + } + + /** + * Set the text that should be shown in the + * alpha slider. Set to null to disable text. + * @param res string resource id. + */ + public void setAlphaSliderText(int res){ + String text = getContext().getString(res); + setAlphaSliderText(text); + } + + /** + * Set the text that should be shown in the + * alpha slider. Set to null to disable text. + * @param text Text that should be shown. + */ + public void setAlphaSliderText(String text){ + mAlphaSliderText = text; + invalidate(); + } + + /** + * Get the current value of the text + * that will be shown in the alpha + * slider. + * @return + */ + public String getAlphaSliderText(){ + return mAlphaSliderText; + } +} \ No newline at end of file diff --git a/external/ColorPickerPreference/src/net/margaritov/preference/colorpicker/Test.java b/external/ColorPickerPreference/src/net/margaritov/preference/colorpicker/Test.java new file mode 100644 index 00000000..e5e167d9 --- /dev/null +++ b/external/ColorPickerPreference/src/net/margaritov/preference/colorpicker/Test.java @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2011 Sergey Margaritov + * + * 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 net.margaritov.preference.colorpicker; + +import net.margaritov.preference.colorpicker.R; + +import android.os.Bundle; +import android.preference.Preference; +import android.preference.Preference.OnPreferenceChangeListener; +import android.preference.PreferenceActivity; + +public class Test extends PreferenceActivity { + /** Called when the activity is first created. */ + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + addPreferencesFromResource(R.xml.settings); + ((ColorPickerPreference)findPreference("color2")).setOnPreferenceChangeListener(new OnPreferenceChangeListener() { + + @Override + public boolean onPreferenceChange(Preference preference, Object newValue) { + preference.setSummary(ColorPickerPreference.convertToARGB(Integer.valueOf(String.valueOf(newValue)))); + return true; + } + + }); + ((ColorPickerPreference)findPreference("color2")).setAlphaSliderEnabled(true); + } +} \ No newline at end of file diff --git a/lite/.factorypath b/lite/.factorypath index 026446ed..d528d92e 100644 --- a/lite/.factorypath +++ b/lite/.factorypath @@ -1,3 +1,3 @@ - + diff --git a/lite/.project b/lite/.project index 0654bfb6..1b341fee 100644 --- a/lite/.project +++ b/lite/.project @@ -1,6 +1,6 @@ - TransdroidLite + Transdroid Core diff --git a/lite/AndroidManifest.xml b/lite/AndroidManifest.xml index 1b6f7500..e33579dd 100644 --- a/lite/AndroidManifest.xml +++ b/lite/AndroidManifest.xml @@ -1,13 +1,20 @@ + android:versionName="2.0-alpha1" > + + + + + + + + \ No newline at end of file diff --git a/lite/project.properties b/lite/project.properties index 5f7d115e..b8863ad3 100644 --- a/lite/project.properties +++ b/lite/project.properties @@ -13,3 +13,4 @@ # Project target. target=android-16 android.library.reference.1=../external/JakeWharton-ActionBarSherlock/library +android.library.reference.2=../external/ColorPickerPreference diff --git a/lite/res/drawable-hdpi/ic_priority_high.png b/lite/res/drawable-hdpi/ic_priority_high.png new file mode 100644 index 00000000..43dd253b Binary files /dev/null and b/lite/res/drawable-hdpi/ic_priority_high.png differ diff --git a/lite/res/drawable-hdpi/ic_priority_low.png b/lite/res/drawable-hdpi/ic_priority_low.png new file mode 100644 index 00000000..fa8e7bfb Binary files /dev/null and b/lite/res/drawable-hdpi/ic_priority_low.png differ diff --git a/lite/res/drawable-hdpi/ic_priority_normal.png b/lite/res/drawable-hdpi/ic_priority_normal.png new file mode 100644 index 00000000..b59edc02 Binary files /dev/null and b/lite/res/drawable-hdpi/ic_priority_normal.png differ diff --git a/lite/res/drawable-hdpi/ic_priority_off.png b/lite/res/drawable-hdpi/ic_priority_off.png new file mode 100644 index 00000000..e44ea25a Binary files /dev/null and b/lite/res/drawable-hdpi/ic_priority_off.png differ diff --git a/lite/res/layout/list_item_torrent.xml b/lite/res/layout/list_item_torrent.xml new file mode 100644 index 00000000..4041e535 --- /dev/null +++ b/lite/res/layout/list_item_torrent.xml @@ -0,0 +1,89 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/lite/res/values/attrs.xml b/lite/res/values/attrs.xml new file mode 100644 index 00000000..a19eb9b9 --- /dev/null +++ b/lite/res/values/attrs.xml @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/lite/res/values/colors.xml b/lite/res/values/colors.xml index ba2e342f..f4cdf7fa 100644 --- a/lite/res/values/colors.xml +++ b/lite/res/values/colors.xml @@ -1,4 +1,5 @@ #8acc12 + #7dbb21 diff --git a/lite/res/values/strings.xml b/lite/res/values/strings.xml index ce8d9e9b..780a7fcf 100644 --- a/lite/res/values/strings.xml +++ b/lite/res/values/strings.xml @@ -1,7 +1,7 @@ - Transdroid Lite + Transdroid Add From file @@ -21,7 +21,6 @@ Ratio Filter list Settings - Start Stop Resume @@ -41,7 +40,143 @@ SERVERS STATUS LABELS + All + Downloading + Uploading + Active + Inactive Connected, but no torrent are active on the server Select a torrent to view its details - + Servers + Add new server + Search sites + Set default site + Add web search site + RSS feeds + Add RSS feed + Other settings + + Name + Optional personal name + Direct search URL + %s will be replaced by the search query + Feed URL + Requires authentication + Opens links in the webbrowser for user login + + Server type + IP or host name + Port number + User name + Password + Deluge web password + Advanced settings + Local IP or host + When connected to the specified local network + Local network + The server\'s local (wifi) network + Folder + Usually empty + SCGI mount point + Optional settings + Finished notification + Notify when a torrent finishes + New torrent notification + Nofity when a torrent was added + Server OS + Download directory + Manually set absolute path for remote connections + Connection timeout + Number of seconds before a connection attempt is aborted + Base (S)FTP url + For example ftp://me@server/downloads/ + (S)FTP password + Use SSL + Connect using https + Custom SSL thumbprint (SHA-1) + Permit only connections to this specific certificate + Accept all SSL certificates + Allow all connections from any thumbprint + + Background notifications + Enable notifications + Enables the background service + Interval + How often to check the server + Sound + Vibrate + LED colour + If supported by your device + Support AWD notifications + Show torrent counter in ADW Launcher + + About Transdroid + Check for updates + Check transdroidorg for latest app version + Import settings + Export settings + Send error log + Get support or report a bug + About + + + BitComet + Bitflu 1.2+ + BitTorrent 6+ + Buffalo NAS -1.31 + Deluge 1.2+ + DLink Router BT + Ktorrent + qBittorrent + rTorrent + Torrentflux-b4rt + Transmission + µTorrent + Vuze + + + daemon_bitcomet + daemon_bitflu + daemon_bittorrent + daemon_buffalonas + daemon_deluge + daemon_dlinkrouterbt + daemon_ktorrent + daemon_qbittorrent + daemon_rtorrent + daemon_tfb4rt + daemon_transmission + daemon_utorrent + daemon_vuze + + + Windows + Mac + Linux + + + type_windows + type_mac + type_linux + + + 1 minute + 10 minutes + 30 minutes + 1 hour + 3 hours + 12 hours + 1 day + + + 60 + 600 + 1800 + 3600 + 10800 + 43200 + 86400 + + + \ No newline at end of file diff --git a/lite/res/xml/pref_about.xml b/lite/res/xml/pref_about.xml new file mode 100644 index 00000000..eb4dad12 --- /dev/null +++ b/lite/res/xml/pref_about.xml @@ -0,0 +1,20 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/lite/res/xml/pref_main.xml b/lite/res/xml/pref_main.xml new file mode 100644 index 00000000..19f3bf36 --- /dev/null +++ b/lite/res/xml/pref_main.xml @@ -0,0 +1,52 @@ + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/lite/res/xml/pref_notifications.xml b/lite/res/xml/pref_notifications.xml new file mode 100644 index 00000000..4a7a1a16 --- /dev/null +++ b/lite/res/xml/pref_notifications.xml @@ -0,0 +1,47 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/lite/res/xml/pref_rssfeed.xml b/lite/res/xml/pref_rssfeed.xml new file mode 100644 index 00000000..401c5aea --- /dev/null +++ b/lite/res/xml/pref_rssfeed.xml @@ -0,0 +1,21 @@ + + + + + + + + + + \ No newline at end of file diff --git a/lite/res/xml/pref_server.xml b/lite/res/xml/pref_server.xml new file mode 100644 index 00000000..9512a302 --- /dev/null +++ b/lite/res/xml/pref_server.xml @@ -0,0 +1,108 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/lite/res/xml/pref_websearch.xml b/lite/res/xml/pref_websearch.xml new file mode 100644 index 00000000..09674685 --- /dev/null +++ b/lite/res/xml/pref_websearch.xml @@ -0,0 +1,16 @@ + + + + + + + + \ No newline at end of file diff --git a/lite/src/com/actionbarsherlock/view/SherlockListView.java b/lite/src/com/actionbarsherlock/view/SherlockListView.java new file mode 100644 index 00000000..3b2df828 --- /dev/null +++ b/lite/src/com/actionbarsherlock/view/SherlockListView.java @@ -0,0 +1,337 @@ +package com.actionbarsherlock.view; + +import android.annotation.TargetApi; +import android.content.Context; +import android.util.AttributeSet; +import android.util.SparseBooleanArray; +import android.view.View; +import android.widget.AdapterView; +import android.widget.Checkable; +import android.widget.ListView; + +import com.actionbarsherlock.app.SherlockActivity; +import com.actionbarsherlock.app.SherlockFragmentActivity; +import com.actionbarsherlock.view.ActionMode; +import com.actionbarsherlock.view.Menu; +import com.actionbarsherlock.view.MenuItem; + +/** + * Provides backwards compatible multiple choice ActionMode support on Froyo+ using ActionBarSherlock. + */ +public class SherlockListView extends ListView { + // API 11+ reference, but ok because the value will be inlined. + public static final int CHOICE_MODE_MULTIPLE_MODAL_COMPAT = CHOICE_MODE_MULTIPLE_MODAL; + + /** + * Wrapper to intercept delegation of long click events, and pass to {@link #doLongPress} + */ + class OnItemLongClickListenerWrapper implements OnItemLongClickListener { + private OnItemLongClickListener wrapped; + + public void setWrapped(OnItemLongClickListener listener) { + this.wrapped = listener; + } + + @Override + public boolean onItemLongClick(AdapterView view, View child, int position, long id) { + // this would be easier if AbsListView.performLongPress wasn't package + // protected :-( + boolean handled = doLongPress(child, position, id); + if (!handled && wrapped != null) { + return wrapped.onItemLongClick(view, child, position, id); + } + return true; + } + } + + /** + * Hijack the onLongClickListener so we can intercept delegation. + */ + @Override + public void setOnItemLongClickListener(OnItemLongClickListener listener) { + if (longClickListenerWrapper == null) { + longClickListenerWrapper = new OnItemLongClickListenerWrapper(); + } + longClickListenerWrapper.setWrapped(listener); + super.setOnItemLongClickListener(longClickListenerWrapper); + } + + /** + * A MultiChoiceModeListener receives events for {@link AbsListView#CHOICE_MODE_MULTIPLE_MODAL}. It acts as the + * {@link ActionMode.Callback} for the selection mode and also receives + * {@link #onItemCheckedStateChanged(ActionMode, int, long, boolean)} events when the user selects and deselects + * list items. + */ + @SuppressWarnings("javadoc") + public interface MultiChoiceModeListenerCompat extends ActionMode.Callback { + /** + * Called when an item is checked or unchecked during selection mode. + * @param mode The {@link ActionMode} providing the selection mode + * @param position Adapter position of the item that was checked or unchecked + * @param id Adapter ID of the item that was checked or unchecked + * @param checked true if the item is now checked, false if the item is now unchecked. + */ + public void onItemCheckedStateChanged(ActionMode mode, int position, long id, boolean checked); + } + + class MultiChoiceModeWrapper implements MultiChoiceModeListenerCompat { + private MultiChoiceModeListenerCompat wrapped; + + public void setWrapped(MultiChoiceModeListenerCompat wrapped) { + this.wrapped = wrapped; + } + + @Override + public boolean onCreateActionMode(ActionMode mode, Menu menu) { + if (wrapped == null) { + return false; + } + if (wrapped.onCreateActionMode(mode, menu)) { + // Initialize checked graphic state? + setLongClickable(false); + return true; + } + return false; + } + + @Override + public boolean onPrepareActionMode(ActionMode mode, Menu menu) { + if (wrapped == null) { + return false; + } + return wrapped.onPrepareActionMode(mode, menu); + } + + @Override + public boolean onActionItemClicked(ActionMode mode, MenuItem item) { + if (wrapped == null) { + return false; + } + return wrapped.onActionItemClicked(mode, item); + } + + @Override + public void onDestroyActionMode(ActionMode mode) { + if (wrapped == null) { + return; + } + wrapped.onDestroyActionMode(mode); + actionMode = null; + + // Ending selection mode means deselecting everything. + clearChoices(); + checkedItemCount = 0; + updateOnScreenCheckedViews(); + invalidateViews(); + setLongClickable(true); + requestLayout(); + invalidate(); + } + + @Override + public void onItemCheckedStateChanged(ActionMode mode, int position, long id, boolean checked) { + if (wrapped == null) { + return; + } + wrapped.onItemCheckedStateChanged(mode, position, id, checked); + + // If there are no items selected we no longer need the selection mode. + if (checkedItemCount == 0) { + mode.finish(); + } + } + } + + private com.actionbarsherlock.view.ActionMode actionMode; + private OnItemLongClickListenerWrapper longClickListenerWrapper; + private MultiChoiceModeWrapper choiceModeListener; + private int choiceMode; + private int checkedItemCount; + + public SherlockListView(Context context) { + super(context); + init(context); + } + + public SherlockListView(Context context, AttributeSet attrs) { + super(context, attrs); + init(context); + } + + public SherlockListView(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + init(context); + } + + void init(Context context) { + if (isInEditMode()) { + // Ignore when viewing in the UI designer + return; + } + if (!(context instanceof SherlockActivity || context instanceof SherlockFragmentActivity)) { + throw new IllegalStateException( + "This view must be hosted in a SherlockActivity or SherlockFragmentActivity"); + } + setOnItemLongClickListener(null); + } + + @Override + public void setChoiceMode(int mode) { + choiceMode = mode; + if (actionMode != null) { + actionMode.finish(); + actionMode = null; + } + if (choiceMode != CHOICE_MODE_NONE) { + if (mode == CHOICE_MODE_MULTIPLE_MODAL_COMPAT) { + clearChoices(); + checkedItemCount = 0; + setLongClickable(true); + updateOnScreenCheckedViews(); + requestLayout(); + invalidate(); + mode = CHOICE_MODE_MULTIPLE; + } + super.setChoiceMode(mode); + } + } + + @Override + public int getChoiceMode() { + return choiceMode; + } + + public void setMultiChoiceModeListener(MultiChoiceModeListenerCompat listener) { + if (choiceModeListener == null) { + choiceModeListener = new MultiChoiceModeWrapper(); + } + choiceModeListener.setWrapped(listener); + } + + @Override + public boolean performItemClick(View view, int position, long id) { + boolean handled = false; + boolean dispatchItemClick = true; + boolean checkStateChanged = false; + if (choiceMode != CHOICE_MODE_NONE) { + handled = true; + if (choiceMode == CHOICE_MODE_MULTIPLE + || (choiceMode == CHOICE_MODE_MULTIPLE_MODAL_COMPAT && actionMode != null)) { + boolean newValue = !getCheckedItemPositions().get(position); + setItemChecked(position, newValue); + if (actionMode != null) { + choiceModeListener.onItemCheckedStateChanged(actionMode, position, id, newValue); + dispatchItemClick = false; + } + checkStateChanged = true; + return false; + } else if (choiceMode == CHOICE_MODE_SINGLE) { + boolean newValue = !getCheckedItemPositions().get(position); + setItemChecked(position, newValue); + checkStateChanged = true; + } + if (checkStateChanged) { + updateOnScreenCheckedViews(); + } + } + if (dispatchItemClick) { + handled |= super.performItemClick(view, position, id); + } + return handled; + } + + /** + * Perform a quick, in-place update of the checked or activated state on all visible item views. This should only be + * called when a valid choice mode is active. + *

+ * (Taken verbatim from AbsListView.java) + */ + @TargetApi(11) + private void updateOnScreenCheckedViews() { + final int firstPos = getFirstVisiblePosition(); + final int count = getChildCount(); + final boolean useActivated = getContext().getApplicationInfo().targetSdkVersion >= android.os.Build.VERSION_CODES.HONEYCOMB; + for (int i = 0; i < count; i++) { + final View child = getChildAt(i); + final int position = firstPos + i; + + if (child instanceof Checkable) { + ((Checkable) child).setChecked(getCheckedItemPositions().get(position)); + } else if (useActivated) { + child.setActivated(getCheckedItemPositions().get(position)); + } + } + } + + public ActionMode startActionMode(ActionMode.Callback callback) { + if (actionMode != null) { + return actionMode; + } + Context context = getContext(); + if (context instanceof SherlockActivity) { + actionMode = ((SherlockActivity) getContext()).startActionMode(callback); + } else if (context instanceof SherlockFragmentActivity) { + actionMode = ((SherlockFragmentActivity) context).startActionMode(callback); + } else { + throw new IllegalStateException( + "This view must be hosted in a SherlockActivity or SherlockFragmentActivity"); + } + return actionMode; + } + + boolean doLongPress(final View child, final int longPressPosition, final long longPressId) { + if (choiceMode == CHOICE_MODE_MULTIPLE_MODAL_COMPAT) { + if (actionMode == null && (actionMode = startActionMode(choiceModeListener)) != null) { + setItemChecked(longPressPosition, true); + } + return true; + } + return false; + } + + /** + * Sets the checked state of the specified position. The is only valid if the choice mode has been set to + * {@link #CHOICE_MODE_SINGLE} or {@link #CHOICE_MODE_MULTIPLE}. + * @param position The item whose checked state is to be checked + * @param value The new checked state for the item + */ + @Override + public void setItemChecked(int position, boolean value) { + if (choiceMode == CHOICE_MODE_NONE) { + return; + } + SparseBooleanArray checkStates = getCheckedItemPositions(); + + // Start selection mode if needed. We don't need to if we're unchecking + // something. + if (value && choiceMode == CHOICE_MODE_MULTIPLE_MODAL_COMPAT && actionMode == null) { + actionMode = startActionMode(choiceModeListener); + } + + if (choiceMode == CHOICE_MODE_MULTIPLE || choiceMode == CHOICE_MODE_MULTIPLE_MODAL) { + // boolean oldValue = checkStates.get(position); + checkStates.put(position, value); + if (value) { + checkedItemCount++; + } else { + checkedItemCount--; + } + if (actionMode != null) { + final long id = getAdapter().getItemId(position); + choiceModeListener.onItemCheckedStateChanged(actionMode, position, id, value); + } + } else { + if (value || isItemChecked(position)) { + checkStates.clear(); + } + // this may end up selecting the value we just cleared but this way + // we ensure length of checkStates is 1, a fact getCheckedItemPosition + // relies on + if (value) { + checkStates.put(position, true); + } + } + requestLayout(); + invalidate(); + } +} diff --git a/lite/src/fr/marvinlabs/widget/CheckableRelativeLayout.java b/lite/src/fr/marvinlabs/widget/CheckableRelativeLayout.java new file mode 100644 index 00000000..e8e92e9a --- /dev/null +++ b/lite/src/fr/marvinlabs/widget/CheckableRelativeLayout.java @@ -0,0 +1,100 @@ +package fr.marvinlabs.widget; + +import java.util.ArrayList; +import java.util.List; + +import android.content.Context; +import android.util.AttributeSet; +import android.view.View; +import android.view.ViewGroup; +import android.widget.Checkable; +import android.widget.RelativeLayout; + +/** + * Extension of a relative layout to provide a checkable behaviour + * + * @author marvinlabs + */ +public class CheckableRelativeLayout extends RelativeLayout implements Checkable { + + private boolean isChecked; + private List checkableViews; + + public CheckableRelativeLayout(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + initialise(attrs); + } + + public CheckableRelativeLayout(Context context, AttributeSet attrs) { + super(context, attrs); + initialise(attrs); + } + + public CheckableRelativeLayout(Context context, int checkableId) { + super(context); + initialise(null); + } + + /* + * @see android.widget.Checkable#isChecked() + */ + public boolean isChecked() { + return isChecked; + } + + /* + * @see android.widget.Checkable#setChecked(boolean) + */ + public void setChecked(boolean isChecked) { + this.isChecked = isChecked; + for (Checkable c : checkableViews) { + c.setChecked(isChecked); + } + } + + /* + * @see android.widget.Checkable#toggle() + */ + public void toggle() { + this.isChecked = !this.isChecked; + for (Checkable c : checkableViews) { + c.toggle(); + } + } + + @Override + protected void onFinishInflate() { + super.onFinishInflate(); + + final int childCount = this.getChildCount(); + for (int i = 0; i < childCount; ++i) { + findCheckableChildren(this.getChildAt(i)); + } + } + + /** + * Read the custom XML attributes + */ + private void initialise(AttributeSet attrs) { + this.isChecked = false; + this.checkableViews = new ArrayList(5); + } + + /** + * Add to our checkable list all the children of the view that implement the + * interface Checkable + */ + private void findCheckableChildren(View v) { + if (v instanceof Checkable) { + this.checkableViews.add((Checkable) v); + } + + if (v instanceof ViewGroup) { + final ViewGroup vg = (ViewGroup) v; + final int childCount = vg.getChildCount(); + for (int i = 0; i < childCount; ++i) { + findCheckableChildren(vg.getChildAt(i)); + } + } + } +} diff --git a/lite/src/fr/marvinlabs/widget/InertCheckBox.java b/lite/src/fr/marvinlabs/widget/InertCheckBox.java new file mode 100644 index 00000000..5dd3080b --- /dev/null +++ b/lite/src/fr/marvinlabs/widget/InertCheckBox.java @@ -0,0 +1,70 @@ +package fr.marvinlabs.widget; + +import android.content.Context; +import android.util.AttributeSet; +import android.view.KeyEvent; +import android.view.MotionEvent; +import android.widget.CheckBox; + +/** + * CheckBox that does not react to any user event in order to let the container handle them. + */ +public class InertCheckBox extends CheckBox { + + // Provide the same constructors as the superclass + public InertCheckBox(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + } + + // Provide the same constructors as the superclass + public InertCheckBox(Context context, AttributeSet attrs) { + super(context, attrs); + } + + // Provide the same constructors as the superclass + public InertCheckBox(Context context) { + super(context); + } + + @Override + public boolean onTouchEvent(MotionEvent event) { + // Make the checkbox not respond to any user event + return false; + } + + @Override + public boolean onKeyDown(int keyCode, KeyEvent event) { + // Make the checkbox not respond to any user event + return false; + } + + @Override + public boolean onKeyMultiple(int keyCode, int repeatCount, KeyEvent event) { + // Make the checkbox not respond to any user event + return false; + } + + @Override + public boolean onKeyPreIme(int keyCode, KeyEvent event) { + // Make the checkbox not respond to any user event + return false; + } + + @Override + public boolean onKeyShortcut(int keyCode, KeyEvent event) { + // Make the checkbox not respond to any user event + return false; + } + + @Override + public boolean onKeyUp(int keyCode, KeyEvent event) { + // Make the checkbox not respond to any user event + return false; + } + + @Override + public boolean onTrackballEvent(MotionEvent event) { + // Make the checkbox not respond to any user event + return false; + } +} diff --git a/lite/src/org/transdroid/lite/app/search/SearchHelper.java b/lite/src/org/transdroid/lite/app/search/SearchHelper.java new file mode 100644 index 00000000..11244a19 --- /dev/null +++ b/lite/src/org/transdroid/lite/app/search/SearchHelper.java @@ -0,0 +1,109 @@ +package org.transdroid.lite.app.search; + +import java.util.ArrayList; +import java.util.List; + +import org.androidannotations.annotations.EBean; +import org.androidannotations.annotations.EBean.Scope; +import org.androidannotations.annotations.RootContext; + +import android.content.Context; +import android.database.Cursor; +import android.net.Uri; + +@EBean(scope = Scope.Singleton) +public class SearchHelper { + + static final int CURSOR_SEARCH_ID = 0; + static final int CURSOR_SEARCH_NAME = 1; + static final int CURSOR_SEARCH_TORRENTURL = 2; + static final int CURSOR_SEARCH_DETAILSURL = 3; + static final int CURSOR_SEARCH_SIZE = 4; + static final int CURSOR_SEARCH_ADDED = 5; + static final int CURSOR_SEARCH_SEEDERS = 6; + static final int CURSOR_SEARCH_LEECHERS = 7; + + static final int CURSOR_SITE_ID = 0; + static final int CURSOR_SITE_CODE = 1; + static final int CURSOR_SITE_NAME = 2; + static final int CURSOR_SITE_RSSURL = 3; + + @RootContext + protected Context context; + + public enum SearchSortOrder { + Combined, BySeeders + } + + /** + * Return whether the Torrent Search package is installed and available to query against + * @return True if the available sites can be retrieved from the content provider, false otherwise + */ + public boolean isTorrentSearchInstalled() { + return getAvailableSites() != null; + } + + /** + * Queries the Torrent Search package for all available in-app search sites. This method is synchronous. + * @return A list of available search sites as POJOs, or null if the Torrent Search package is not installed + */ + public List getAvailableSites() { + + // Try to access the TorrentSitesProvider to retrieve all available in-app torrent search sites + Uri uri = Uri.parse("content://org.transdroid.search.torrentsitesprovider/sites"); + Cursor cursor = context.getContentResolver().query(uri, null, null, null, null); + if (cursor.moveToFirst()) { + List sites = new ArrayList(); + do { + // Read the cursor fields into the SearchSite object + sites.add(new SearchSite(cursor.getInt(CURSOR_SITE_ID), cursor.getString(CURSOR_SITE_CODE), cursor + .getString(CURSOR_SITE_NAME), cursor.getString(CURSOR_SITE_RSSURL))); + } while (cursor.moveToNext()); + cursor.close(); + return sites; + } + + // Torrent Search package is not yet installed + return null; + + } + + /** + * Queries the Torrent Search module to search for torrents on the web. This method is synchornous and should always + * be called in a background thread. + * @param query The search query to pass to the torrent site + * @param site The site to search, as retrieved from the TorrentSitesProvider, or null if the Torrent Search package + * @param sortBy.name() The sort order to request from the torrent site, if supported + * @return A list of torrent search results as POJOs, or null if the Torrent Search package is not installed + */ + public List search(String query, SearchSite site, SearchSortOrder sortBy) { + + // Try to query the TorrentSearchProvider to search for torrents on the web + Uri uri = Uri.parse("content://org.transdroid.search.torrentsearchprovider/search/" + query); + Cursor cursor; + if (site == null) { + // If no explicit site was supplied, rely on the Torrent Search package's default + cursor = context.getContentResolver().query(uri, null, null, null, sortBy.name()); + } else { + cursor = context.getContentResolver().query(uri, null, "SITE = ?", new String[] { site.getKey() }, + sortBy.name()); + } + if (cursor.moveToFirst()) { + List results = new ArrayList(); + do { + // Read the cursor fields into the SearchResult object + results.add(new SearchResult(cursor.getInt(CURSOR_SEARCH_ID), cursor.getString(CURSOR_SEARCH_NAME), + cursor.getString(CURSOR_SEARCH_TORRENTURL), cursor.getString(CURSOR_SEARCH_DETAILSURL), cursor + .getString(CURSOR_SEARCH_SIZE), cursor.getLong(CURSOR_SEARCH_ADDED), cursor + .getString(CURSOR_SEARCH_SEEDERS), cursor.getString(CURSOR_SEARCH_LEECHERS))); + } while (cursor.moveToNext()); + cursor.close(); + return results; + } + + // Torrent Search package is not yet installed + return null; + + } + +} diff --git a/lite/src/org/transdroid/lite/app/search/SearchResult.java b/lite/src/org/transdroid/lite/app/search/SearchResult.java new file mode 100644 index 00000000..77594909 --- /dev/null +++ b/lite/src/org/transdroid/lite/app/search/SearchResult.java @@ -0,0 +1,64 @@ +package org.transdroid.lite.app.search; + +import java.util.Date; + +/** + * Represents a search result as retrieved by querying the Torrent Search package. + * @author Eric Kok + */ +public class SearchResult { + + private final int id; + private final String name; + private final String torrentUrl; + private final String detailsUrl; + private final String size; + private final Date addedOn; + private final String seeders; + private final String leechers; + + public SearchResult(int id, String name, String torrentUrl, String detailsUrl, String size, long addedOnTime, + String seeders, String leechers) { + this.id = id; + this.name = name; + this.torrentUrl = torrentUrl; + this.detailsUrl = detailsUrl; + this.size = size; + this.addedOn = (addedOnTime == -1L) ? null : new Date(addedOnTime); + this.seeders = seeders; + this.leechers = leechers; + } + + public int getId() { + return id; + } + + public String getName() { + return name; + } + + public String getTorrentUrl() { + return torrentUrl; + } + + public String getDetailsUrl() { + return detailsUrl; + } + + public String getSize() { + return size; + } + + public Date getAddedOn() { + return addedOn; + } + + public String getSeeders() { + return seeders; + } + + public String getLeechers() { + return leechers; + } + +} diff --git a/lite/src/org/transdroid/lite/app/search/SearchSite.java b/lite/src/org/transdroid/lite/app/search/SearchSite.java new file mode 100644 index 00000000..2d7910df --- /dev/null +++ b/lite/src/org/transdroid/lite/app/search/SearchSite.java @@ -0,0 +1,40 @@ +package org.transdroid.lite.app.search; + +import org.transdroid.lite.gui.navigation.FilterItem; + +/** + * Represents an available torrent site that can be searched using the Torrent Search package. + * @author Eric Kok + */ +public class SearchSite implements FilterItem { + + private final int id; + private final String key; + private final String name; + private final String rssFeedUrl; + + public SearchSite(int id, String key, String name, String rssFeedUrl) { + this.id = id; + this.key = key; + this.name = name; + this.rssFeedUrl = rssFeedUrl; + } + + public int getId() { + return id; + } + + public String getKey() { + return key; + } + + @Override + public String getName() { + return name; + } + + public String getRssFeedUrl() { + return rssFeedUrl; + } + +} diff --git a/lite/src/org/transdroid/lite/app/settings/AboutSettings.java b/lite/src/org/transdroid/lite/app/settings/AboutSettings.java new file mode 100644 index 00000000..9dd745c7 --- /dev/null +++ b/lite/src/org/transdroid/lite/app/settings/AboutSettings.java @@ -0,0 +1,30 @@ +package org.transdroid.lite.app.settings; + +import org.androidannotations.annotations.EBean; +import org.androidannotations.annotations.RootContext; +import org.androidannotations.annotations.EBean.Scope; + +import android.content.Context; +import android.content.SharedPreferences; +import android.preference.PreferenceManager; + +/** + * Allows instantiation of the settings specified in R.xml.pref_about. + * @author Eric Kok + */ +@EBean(scope = Scope.Singleton) +public class AboutSettings { + + @RootContext + protected Context context; + private SharedPreferences prefs; + + protected AboutSettings(Context context) { + prefs = PreferenceManager.getDefaultSharedPreferences(context); + } + + public boolean checkForUpdates() { + return prefs.getBoolean("about_checkupdates", true); + } + +} diff --git a/lite/src/org/transdroid/lite/app/settings/ApplicationSettings.java b/lite/src/org/transdroid/lite/app/settings/ApplicationSettings.java new file mode 100644 index 00000000..75a4c09e --- /dev/null +++ b/lite/src/org/transdroid/lite/app/settings/ApplicationSettings.java @@ -0,0 +1,140 @@ +package org.transdroid.lite.app.settings; + +import java.util.ArrayList; +import java.util.List; + +import org.androidannotations.annotations.EBean; +import org.androidannotations.annotations.EBean.Scope; +import org.androidannotations.annotations.RootContext; +import org.transdroid.daemon.Daemon; +import org.transdroid.daemon.OS; + +import android.content.Context; +import android.content.SharedPreferences; +import android.preference.PreferenceManager; + +/** + * Singleton object to access all application settings, including stored servers, web search sites and RSS feeds. + * @author Eric Kok + */ +@EBean(scope = Scope.Singleton) +public class ApplicationSettings { + + @RootContext + protected Context context; + private SharedPreferences prefs; + + protected ApplicationSettings(Context context) { + prefs = PreferenceManager.getDefaultSharedPreferences(context); + } + + /** + * Returns all available user-configured servers + * @return A list of all stored server settings objects + */ + public List getServerSettings() { + List servers = new ArrayList(); + for (int i = 0; i <= getMaxServer(); i++) { + servers.add(getServerSetting(i)); + } + return servers; + } + + /** + * Returns the order number/identifying key of the last server + * @return The zero-based order number (index) of the last stored server settings + */ + public int getMaxServer() { + for (int i = 0; true; i++) { + if (prefs.getString("server_type_" + i, null) == null) + return i - 1; + } + } + + /** + * Returns the user-specified server settings for a specific server + * @param order The order number/identifying key of the settings to retrieve + * @return The server settings object, loaded from shared preferences + */ + public ServerSetting getServerSetting(int order) { + return new ServerSetting(order, prefs.getString("server_name_" + order, null), Daemon.fromCode(prefs.getString( + "server_type_" + order, null)), prefs.getString("server_address_" + order, null), prefs.getString( + "server_localaddress_" + order, null), prefs.getString("server_localnetwork_" + order, null), + prefs.getInt("server_port_" + order, -1), prefs.getBoolean("server_sslenabled_" + order, false), + prefs.getBoolean("server_ssltrustall_" + order, false), prefs.getString("server_ssltrustkey_" + order, + null), prefs.getString("server_folder_" + order, null), prefs.getBoolean("server_useauth_" + + order, true), prefs.getString("server_user_" + order, null), prefs.getString("server_pass_" + + order, null), prefs.getString("server_extrapass_" + order, null), OS.fromCode(prefs + .getString("server_os_" + order, null)), prefs.getString("server_downloaddir_" + order, null), + prefs.getString("server_ftpurl_" + order, null), prefs.getString("server_ftppass_" + order, null), + prefs.getInt("server_timeout_" + order, -1), prefs.getBoolean("server_alarmfinished_" + order, true), + prefs.getBoolean("server_alarmnew_" + order, false), false); + } + + /** + * Returns all available user-configured web-based (as opped to in-app) search sites + * @return A list of all stored web search site settings objects + */ + public List getWebsearchSettings() { + List websearches = new ArrayList(); + for (int i = 0; i <= getMaxWebsearch(); i++) { + websearches.add(getWebsearchSetting(i)); + } + return websearches; + } + + /** + * Returns the order number/identifying key of the last web search site + * @return The zero-based order number (index) of the last stored web search site + */ + public int getMaxWebsearch() { + for (int i = 0; true; i++) { + if (prefs.getString("websearch_url_" + i, null) == null) + return i - 1; + } + } + + /** + * Returns the user-specified web-based search site setting for a specific site + * @param order The order number/identifying key of the settings to retrieve + * @return The web search site settings object, loaded from shared preferences + */ + public WebsearchSetting getWebsearchSetting(int order) { + return new WebsearchSetting(order, prefs.getString("websearch_name_" + order, null), prefs.getString( + "websearch_url_" + order, null)); + } + + /** + * Returns all available user-configured RSS feeds + * @return A list of all stored RSS feed settings objects + */ + public List getRssfeedSettings() { + List rssfeeds = new ArrayList(); + for (int i = 0; i <= getMaxRssfeed(); i++) { + rssfeeds.add(getRssfeedSetting(i)); + } + return rssfeeds; + } + + /** + * Returns the order number/identifying key of the last stored RSS feed + * @return The zero-based order number (index) of the last stored RSS feed + */ + public int getMaxRssfeed() { + for (int i = 0; true; i++) { + if (prefs.getString("rssfeed_feedurl_" + i, null) == null) + return i - 1; + } + } + + /** + * Returns the user-specified RSS feed setting for a specific feed + * @param order The order number/identifying key of the settings to retrieve + * @return The RSS feed settings object, loaded from shared preferences + */ + public RssfeedSetting getRssfeedSetting(int order) { + return new RssfeedSetting(order, prefs.getString("rssfeed_name_" + order, null), prefs.getString( + "rssfeed_feedurl_" + order, null), prefs.getBoolean("rssfeed_reqauth_" + order, false)); + } + +} diff --git a/lite/src/org/transdroid/lite/app/settings/NotificationSettings.java b/lite/src/org/transdroid/lite/app/settings/NotificationSettings.java new file mode 100644 index 00000000..1a68aabd --- /dev/null +++ b/lite/src/org/transdroid/lite/app/settings/NotificationSettings.java @@ -0,0 +1,97 @@ +package org.transdroid.lite.app.settings; + +import org.androidannotations.annotations.EBean; +import org.androidannotations.annotations.EBean.Scope; +import org.androidannotations.annotations.RootContext; +import org.transdroid.core.R; + +import android.content.Context; +import android.content.SharedPreferences; +import android.net.Uri; +import android.preference.PreferenceManager; +import android.provider.Settings; + +/** + * Allows instantiation of the settings specified in R.xml.pref_notifications. + * @author Eric Kok + */ +@EBean(scope = Scope.Singleton) +public class NotificationSettings { + + @RootContext + protected Context context; + private SharedPreferences prefs; + + protected NotificationSettings(Context context) { + prefs = PreferenceManager.getDefaultSharedPreferences(context); + } + + /** + * Whether the background service is enabled, i.e. whether the user want to receive notifications + * @return True if the server should be checked for torrent status updates + */ + public boolean isEnabled() { + return prefs.getBoolean("notifications_enabled", true); + } + + private String getRawInverval() { + return prefs.getString("notifications_interval", "10800"); + } + + /** + * Returns the interval between two server checks + * @return The interval, in milliseconds + */ + public Long getInvervalInMilliseconds() { + return Long.parseLong(getRawInverval()) * 1000L; + } + + private String getRawSound() { + return prefs.getString("notifications_sound", null); + } + + /** + * Returns the sound (ring tone) to play on a new notification, or null if it should not play any + * @return Either the user-specified sound, null if the user specified 'Silent' or the system default notification sound + */ + public Uri getSound() { + String raw = getRawSound(); + if (raw == null) + return null; + if (raw.equals("")) + return Settings.System.DEFAULT_NOTIFICATION_URI; + return Uri.parse(raw); + } + + /** + * Whether the device should vibrate on a new notification + * @return + */ + public boolean shouldVibrate() { + return prefs.getBoolean("notifications_vibrate", false); + } + + private int getRawLedColour() { + return prefs.getInt("notifications_ledcolour", -1); + } + + /** + * Returns the LED colour to use on a new notification + * @return The integer value of the user-specified or default colour + */ + public int getDesiredLedColour() { + int raw = getRawLedColour(); + if (raw <= 0) + return context.getResources().getColor(R.color.ledgreen); + return raw; + } + + /** + * Whether the background service should report to ADW Launcher + * @return True if the user want Transdroid to report to ADW Launcher + */ + public boolean shouldReportToAdwLauncher() { + return prefs.getBoolean("notifications_adwnotify", false); + } + +} diff --git a/lite/src/org/transdroid/lite/app/settings/RssfeedSetting.java b/lite/src/org/transdroid/lite/app/settings/RssfeedSetting.java new file mode 100644 index 00000000..69ee3823 --- /dev/null +++ b/lite/src/org/transdroid/lite/app/settings/RssfeedSetting.java @@ -0,0 +1,67 @@ +package org.transdroid.lite.app.settings; + +import org.transdroid.lite.gui.navigation.FilterItem; + +import android.net.Uri; +import android.text.TextUtils; + +/** + * Represents a user-specified RSS feed. + * @author Eric Kok + */ +public class RssfeedSetting implements FilterItem { + + private static final String DEFAULT_NAME = "Default"; + + private final int order; + private final String name; + private final String url; + private final boolean requiresAuth; + private String lastNew; + + public RssfeedSetting(int order, String name, String baseUrl, boolean needsAuth) { + this.order = order; + this.name = name; + this.url = baseUrl; + this.requiresAuth = needsAuth; + this.lastNew = null; + } + + public int getOrder() { + return order; + } + + @Override + public String getName() { + if (!TextUtils.isEmpty(name)) + return name; + if (!TextUtils.isEmpty(url)) + return Uri.parse(url).getHost(); + return DEFAULT_NAME; + } + + public String getUrl() { + return url; + } + + public boolean requiresExternalAuthentication() { + return requiresAuth; + } + + /** + * Returns the URL of the item that was the newest last time we checked this feed + * @return The last new item's URL as URL-encoded string + */ + public String getLastNew() { + return this.lastNew; + } + + /** + * Record the URL of what is now the last item we retrieved + * @param lastNew The URL of the last new item as URL-encoded string + */ + public void setLastNew(String lastNew) { + this.lastNew = lastNew; + } + +} diff --git a/lite/src/org/transdroid/lite/app/settings/ServerSetting.java b/lite/src/org/transdroid/lite/app/settings/ServerSetting.java new file mode 100644 index 00000000..164be4ad --- /dev/null +++ b/lite/src/org/transdroid/lite/app/settings/ServerSetting.java @@ -0,0 +1,195 @@ +package org.transdroid.lite.app.settings; + +import org.transdroid.daemon.Daemon; +import org.transdroid.daemon.OS; +import org.transdroid.lite.gui.navigation.FilterItem; + +import android.text.TextUtils; + +/** + * Represents a user-configured remote server. + * @author Eric Kok + */ +public class ServerSetting implements FilterItem { + + private static final String DEFAULT_NAME = "Default"; + + private final int key; + private final String name; + private final Daemon type; + private final String address; + private final String localAddress; + private final String localNetwork; + private final int port; + private final String folder; + private final boolean useAuthentication; + private final String username; + private final String password; + private final String extraPass; + private final OS os; + private final String downloadDir; + private final String ftpUrl; + private final String ftpPassword; + private final int timeout; + private final boolean alarmOnFinishedDownload; + private final boolean alarmOnNewTorrent; + private final boolean ssl; + private final boolean sslTrustAll; + private final String sslTrustKey; + private final boolean isAutoGenerated; + + /** + * Creates a daemon settings instance, providing full connection details + * @param name A name used to identify this server to the user + * @param type The server daemon type + * @param address The server domain name or IP address + * @param localAddress The server domain or IP address when connected to the server's local network + * @param localNetwork The server's local network SSID + * @param port The port on which the server daemon is running + * @param sslTrustKey The specific key that will be accepted. + * @param folder The server folder (like a virtual sub-folder or an SCGI mount point) + * @param useAuthentication Whether to use basic authentication + * @param username The user name to provide during authentication + * @param password The password to provide during authentication + * @param extraPass The Deluge web interface password + * @param downloadDir The default download directory (which may also be used as base directory for file paths) + * @param ftpUrl The partial URL to connect to when requesting FTP-style transfers + * @param timeout The number of seconds to wait before timing out a connection attempt + * @param isAutoGenerated Whether this setting was generated rather than manually inputed by the user + */ + public ServerSetting(int key, String name, Daemon type, String address, String localAddress, String localNetwork, + int port, boolean ssl, boolean sslTrustAll, String sslTrustKey, String folder, boolean useAuthentication, + String username, String password, String extraPass, OS os, String downloadDir, String ftpUrl, + String ftpPassword, int timeout, boolean alarmOnFinishedDownload, boolean alarmOnNewTorrent, + boolean isAutoGenerated) { + this.key = key; + this.name = name; + this.type = type; + this.address = address; + this.localAddress = localAddress; + this.localNetwork = localNetwork; + this.port = port; + this.ssl = ssl; + this.sslTrustAll = sslTrustAll; + this.sslTrustKey = sslTrustKey; + this.folder = folder; + this.useAuthentication = useAuthentication; + this.username = username; + this.password = password; + this.extraPass = extraPass; + this.os = os; + this.downloadDir = downloadDir; + this.ftpUrl = ftpUrl; + this.ftpPassword = ftpPassword; + this.timeout = timeout; + this.alarmOnFinishedDownload = alarmOnFinishedDownload; + this.alarmOnNewTorrent = alarmOnNewTorrent; + this.isAutoGenerated = isAutoGenerated; + } + + @Override + public String getName() { + return (name == null || name.equals("") ? DEFAULT_NAME : name); + } + + public Daemon getType() { + return type; + } + + public String getAddress() { + return address; + } + + public String getLocalAddress() { + return localAddress; + } + + public String getLocalNetwork() { + return localNetwork; + } + + public int getPort() { + return port; + } + + public boolean getSsl() { + return ssl; + } + + public boolean getSslTrustAll() { + return sslTrustAll; + } + + public String getSslTrustKey() { + return sslTrustKey; + } + + public String getFolder() { + return folder; + } + + public boolean shouldUseAuthentication() { + return useAuthentication; + } + + public String getUsername() { + return username; + } + + public String getPassword() { + return password; + } + + public String getExtraPassword() { + return extraPass; + } + + public OS getOS() { + return os; + } + + public String getDownloadDir() { + return downloadDir; + } + + public String getFtpUrl() { + return ftpUrl; + } + + public String getFtpPassword() { + return ftpPassword; + } + + public int getTimeoutInMilliseconds() { + return timeout * 1000; + } + + public boolean shouldAlarmOnFinishedDownload() { + return alarmOnFinishedDownload; + } + + public boolean shouldAlarmOnNewTorrent() { + return alarmOnNewTorrent; + } + + public boolean isAutoGenerated() { + return isAutoGenerated; + } + + public int getOrder() { + return this.key; + } + + public String getHumanReadableIdentifier() { + if (isAutoGenerated) { + // Hide the 'implementation details'; just give the username and server + return (this.shouldUseAuthentication() && !TextUtils.isEmpty(this.getUsername()) ? this.getUsername() + "@" + : "") + getAddress(); + } + return (this.ssl ? "https://" : "http://") + + (this.shouldUseAuthentication() && !TextUtils.isEmpty(this.getUsername()) ? this.getUsername() + "@" + : "") + getAddress() + ":" + getPort() + + (Daemon.supportsCustomFolder(getType()) && getFolder() != null ? getFolder() : ""); + } + +} diff --git a/lite/src/org/transdroid/lite/app/settings/WebsearchSetting.java b/lite/src/org/transdroid/lite/app/settings/WebsearchSetting.java new file mode 100644 index 00000000..b4da7aed --- /dev/null +++ b/lite/src/org/transdroid/lite/app/settings/WebsearchSetting.java @@ -0,0 +1,48 @@ +package org.transdroid.lite.app.settings; + +import org.transdroid.lite.gui.navigation.FilterItem; + +import android.net.Uri; +import android.text.TextUtils; + +/** + * Represents a user-specified website that can be searched (by starting the browser, rather than in-app) + * @author Eric Kok + */ +public class WebsearchSetting implements FilterItem { + + private static final String DEFAULT_NAME = "Default"; + private static final String KEY_PREFIX = "websearch_"; + + private final int order; + private final String name; + private final String baseUrl; + + public WebsearchSetting(int order, String name, String baseUrl) { + this.order = order; + this.name = name; + this.baseUrl = baseUrl; + } + + public int getOrder() { + return order; + } + + @Override + public String getName() { + if (!TextUtils.isEmpty(name)) + return name; + if (!TextUtils.isEmpty(baseUrl)) + return Uri.parse(baseUrl).getHost(); + return DEFAULT_NAME; + } + + public String getBaseUrl() { + return baseUrl; + } + + public String getKey() { + return KEY_PREFIX + getOrder(); + } + +} diff --git a/lite/src/org/transdroid/lite/gui/DetailsFagment.java b/lite/src/org/transdroid/lite/gui/DetailsFagment.java index 1b3c7e10..c43b8135 100644 --- a/lite/src/org/transdroid/lite/gui/DetailsFagment.java +++ b/lite/src/org/transdroid/lite/gui/DetailsFagment.java @@ -7,7 +7,7 @@ import org.androidannotations.annotations.InstanceState; import org.androidannotations.annotations.ViewById; import org.transdroid.daemon.Torrent; import org.transdroid.daemon.TorrentDetails; -import org.transdroid.lite.R; +import org.transdroid.core.R; import android.view.View; import android.widget.TextView; diff --git a/lite/src/org/transdroid/lite/gui/TorrentsActivity.java b/lite/src/org/transdroid/lite/gui/TorrentsActivity.java index f368bc94..1af298df 100644 --- a/lite/src/org/transdroid/lite/gui/TorrentsActivity.java +++ b/lite/src/org/transdroid/lite/gui/TorrentsActivity.java @@ -4,15 +4,15 @@ import org.androidannotations.annotations.AfterViews; import org.androidannotations.annotations.Bean; import org.androidannotations.annotations.EActivity; import org.androidannotations.annotations.FragmentById; -import org.androidannotations.annotations.FragmentByTag; import org.androidannotations.annotations.ItemSelect; import org.androidannotations.annotations.OptionsMenu; import org.androidannotations.annotations.ViewById; -import org.transdroid.lite.R; +import org.transdroid.core.R; +import org.transdroid.lite.app.settings.ApplicationSettings; import org.transdroid.lite.gui.navigation.FilterAdapter; import org.transdroid.lite.gui.navigation.FilterItem; -import org.transdroid.lite.gui.navigation.FilterSeparatorView; import org.transdroid.lite.gui.navigation.NavigationHelper; +import org.transdroid.lite.gui.navigation.StatusType; import com.actionbarsherlock.app.ActionBar; import com.actionbarsherlock.app.ActionBar.OnNavigationListener; @@ -30,6 +30,10 @@ public class TorrentsActivity extends SherlockFragmentActivity implements OnNavi protected SherlockListView filtersList; protected FilterAdapter navigationListAdapter = null; protected FilterAdapter navigationSpinnerAdapter = null; + + // Settings + @Bean + protected ApplicationSettings applicationSettings; // Torrents list components @FragmentById(R.id.torrent_list) @@ -42,17 +46,23 @@ public class TorrentsActivity extends SherlockFragmentActivity implements OnNavi @AfterViews protected void init() { - // Set up navigation + // Set up navigation, with an action bar spinner and possibly (if room) with a filter list getSupportActionBar().setNavigationMode(ActionBar.NAVIGATION_MODE_LIST); getSupportActionBar().setHomeButtonEnabled(false); navigationSpinnerAdapter = new FilterAdapter(this); + // Servers are always added to the action bar spinner + navigationSpinnerAdapter.updateServers(applicationSettings.getServerSettings()); getSupportActionBar().setListNavigationCallbacks(navigationSpinnerAdapter, this); if (filtersList != null) { + // There was room for a dedicated filter list; add the status types navigationListAdapter = new FilterAdapter(this); filtersList.setAdapter(navigationListAdapter); + navigationListAdapter.updateStatusTypes(StatusType.getAllStatusTypes(this)); + } else { + // Add status types directly to the action bar spinner + navigationSpinnerAdapter.updateStatusTypes(StatusType.getAllStatusTypes(this)); } - // Load settings } @@ -73,12 +83,12 @@ public class TorrentsActivity extends SherlockFragmentActivity implements OnNavi /** * A new filter was selected; update the view over the current data - * @param selected True if - * @param item + * @param selected True if the filter item was selected, false if it was deselected + * @param item The touched filter item */ @ItemSelect(R.id.filters_list) protected void filterSelected(boolean selected, FilterItem item) { - // TODO: Update the view + // TODO: Update the torrent list view } } diff --git a/lite/src/org/transdroid/lite/gui/TorrentsFragment.java b/lite/src/org/transdroid/lite/gui/TorrentsFragment.java index f53a9e1d..5dd796bb 100644 --- a/lite/src/org/transdroid/lite/gui/TorrentsFragment.java +++ b/lite/src/org/transdroid/lite/gui/TorrentsFragment.java @@ -1,7 +1,7 @@ package org.transdroid.lite.gui; import org.androidannotations.annotations.EFragment; -import org.transdroid.lite.R; +import org.transdroid.core.R; import com.actionbarsherlock.app.SherlockFragment; diff --git a/lite/src/org/transdroid/lite/gui/lists/TorrentProgressBar.java b/lite/src/org/transdroid/lite/gui/lists/TorrentProgressBar.java new file mode 100644 index 00000000..0dbe8fc1 --- /dev/null +++ b/lite/src/org/transdroid/lite/gui/lists/TorrentProgressBar.java @@ -0,0 +1,110 @@ +package org.transdroid.lite.gui.lists; + +import org.transdroid.core.R; + +import android.content.Context; +import android.content.res.TypedArray; +import android.graphics.Canvas; +import android.graphics.Paint; +import android.graphics.RectF; +import android.util.AttributeSet; +import android.view.View; + +/** + * Draws a progress bar indicating the download progress as well as the torrent status. + * + * @author Eric Kok + */ +public class TorrentProgressBar extends View { + + private final float scale = getContext().getResources().getDisplayMetrics().density; + private final int MINIMUM_HEIGHT = (int) (2 * scale + 0.5f); + // private final int RIGHT_MARGIN = (int)(3 * scale + 0.5f); + + private int progress; + private boolean isActive; + private boolean isError; + private final Paint notdonePaint = new Paint(); + private final Paint inactiveDonePaint = new Paint(); + private final Paint inactivePaint = new Paint(); + private final Paint progressPaint = new Paint(); + private final Paint donePaint = new Paint(); + private final Paint errorPaint = new Paint(); + private final RectF fullRect = new RectF(); + private final RectF progressRect = new RectF(); + + public void setProgress(int progress) { + this.progress = progress; + this.invalidate(); + } + + public void setActive(boolean isActive) { + this.isActive = isActive; + this.invalidate(); + } + + public void setError(boolean isError) { + this.isError = isError; + this.invalidate(); + } + + public TorrentProgressBar(Context context) { + super(context); + initPaints(); + } + + public TorrentProgressBar(Context context, AttributeSet attrs) { + super(context, attrs); + initPaints(); + + // Parse any set attributes from XML + TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.TorrentProgressBar); + if (a.hasValue(R.styleable.TorrentProgressBar_progress)) { + this.progress = a.getIndex(R.styleable.TorrentProgressBar_progress); + this.isActive = a.getBoolean(R.styleable.TorrentProgressBar_isActive, false); + } + a.recycle(); + } + + private void initPaints() { + notdonePaint.setColor(0xFFEEEEEE); + inactiveDonePaint.setColor(0xFFA759D4); + inactivePaint.setColor(0xFF9E9E9E); + progressPaint.setColor(0xFF42A8FA); + donePaint.setColor(0xFF8CCF29); + errorPaint.setColor(0xFFDE3939); + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + int ws = MeasureSpec.getSize(widthMeasureSpec);// - RIGHT_MARGIN; + int hs = Math.max(getHeight(), MINIMUM_HEIGHT); + setMeasuredDimension(ws, hs); + } + + @Override + protected void onDraw(Canvas canvas) { + super.onDraw(canvas); + + int height = getHeight(); + int width = getWidth(); + fullRect.set(0, 0, width, height); + + // Error? + if (isError) { + canvas.drawRect(fullRect, errorPaint); + } else { + // Background rounded rectangle + canvas.drawRect(fullRect, notdonePaint); + + // Foreground progress indicator + if (progress > 0) { + progressRect.set(0, 0, width * ((float) progress / 100), height); + canvas.drawRect(progressRect, (isActive ? (progress == 100 ? donePaint : progressPaint) + : (progress == 100 ? inactiveDonePaint : inactivePaint))); + } + } + + } + +} diff --git a/lite/src/org/transdroid/lite/gui/navigation/FilterAdapter.java b/lite/src/org/transdroid/lite/gui/navigation/FilterAdapter.java index 0ffb0b8e..22f9dbe6 100644 --- a/lite/src/org/transdroid/lite/gui/navigation/FilterAdapter.java +++ b/lite/src/org/transdroid/lite/gui/navigation/FilterAdapter.java @@ -2,7 +2,7 @@ package org.transdroid.lite.gui.navigation; import java.util.List; -import org.transdroid.lite.R; +import org.transdroid.core.R; import android.content.Context; import android.view.View; @@ -31,7 +31,7 @@ public class FilterAdapter extends MergeAdapter { * Update the list of available servers. * @param servers The new list of available servers */ - public void updateServers(List servers) { + public void updateServers(List servers) { if (this.serverItems == null && servers != null) { addView(FilterSeparatorView_.build(context).setText(context.getString(R.string.navigation_servers)), false); this.serverItems = new FilterItemAdapter(context, servers); @@ -47,7 +47,7 @@ public class FilterAdapter extends MergeAdapter { * Update the list of available status types. * @param statusTypes The new list of available status types */ - public void updateStatusTypes(List statusTypes) { + public void updateStatusTypes(List statusTypes) { if (this.statusTypeItems == null && statusTypes != null) { addView(FilterSeparatorView_.build(context).setText(context.getString(R.string.navigation_status)), false); this.statusTypeItems = new FilterItemAdapter(context, statusTypes); @@ -63,7 +63,7 @@ public class FilterAdapter extends MergeAdapter { * Update the list of available labels. * @param labels The new list of available labels */ - public void updateLabels(List labels) { + public void updateLabels(List labels) { if (this.labelItems == null && labels != null) { addView(FilterSeparatorView_.build(context).setText(context.getString(R.string.navigation_labels)), false); this.labelItems = new FilterItemAdapter(context, labels); @@ -78,9 +78,9 @@ public class FilterAdapter extends MergeAdapter { protected class FilterItemAdapter extends BaseAdapter { private final Context context; - private List items; + private List items; - public FilterItemAdapter(Context context, List items) { + public FilterItemAdapter(Context context, List items) { this.context = context; this.items = items; } @@ -89,7 +89,7 @@ public class FilterAdapter extends MergeAdapter { * Allows updating of the full data list underlying this adapter, replacing all items * @param newItems The new list of filter items to display */ - public void update(List newItems) { + public void update(List newItems) { this.items = newItems; notifyDataSetChanged(); } diff --git a/lite/src/org/transdroid/lite/gui/navigation/FilterItemView.java b/lite/src/org/transdroid/lite/gui/navigation/FilterItemView.java index e7f6dc82..532f85f3 100644 --- a/lite/src/org/transdroid/lite/gui/navigation/FilterItemView.java +++ b/lite/src/org/transdroid/lite/gui/navigation/FilterItemView.java @@ -2,7 +2,7 @@ package org.transdroid.lite.gui.navigation; import org.androidannotations.annotations.EViewGroup; import org.androidannotations.annotations.ViewById; -import org.transdroid.lite.R; +import org.transdroid.core.R; import android.content.Context; import android.widget.LinearLayout; diff --git a/lite/src/org/transdroid/lite/gui/navigation/FilterSeparatorView.java b/lite/src/org/transdroid/lite/gui/navigation/FilterSeparatorView.java index 09368d89..ea6deaff 100644 --- a/lite/src/org/transdroid/lite/gui/navigation/FilterSeparatorView.java +++ b/lite/src/org/transdroid/lite/gui/navigation/FilterSeparatorView.java @@ -2,7 +2,7 @@ package org.transdroid.lite.gui.navigation; import org.androidannotations.annotations.EViewGroup; import org.androidannotations.annotations.ViewById; -import org.transdroid.lite.R; +import org.transdroid.core.R; import android.content.Context; import android.widget.LinearLayout; diff --git a/lite/src/org/transdroid/lite/gui/navigation/Label.java b/lite/src/org/transdroid/lite/gui/navigation/Label.java new file mode 100644 index 00000000..6da5d20a --- /dev/null +++ b/lite/src/org/transdroid/lite/gui/navigation/Label.java @@ -0,0 +1,20 @@ +package org.transdroid.lite.gui.navigation; + +/** + * Represents some label that is active or available on the server. + * @author Eric Kok + */ +public class Label implements FilterItem { + + private final String name; + + public Label(String name) { + this.name = name; + } + + @Override + public String getName() { + return this.name; + } + +} diff --git a/lite/src/org/transdroid/lite/gui/navigation/StatusType.java b/lite/src/org/transdroid/lite/gui/navigation/StatusType.java new file mode 100644 index 00000000..2aaac124 --- /dev/null +++ b/lite/src/org/transdroid/lite/gui/navigation/StatusType.java @@ -0,0 +1,75 @@ +package org.transdroid.lite.gui.navigation; + +import java.util.Arrays; +import java.util.List; + +import org.transdroid.core.R; + +import android.content.Context; + +/** + * Enumeration of all status types, which filter the list of shown torrents based on transfer activity. + * @author Eric Kok + */ +public enum StatusType { + + ShowAll { + StatusTypeFilter getFilterItem(Context context) { + return new StatusTypeFilter(context.getString(R.string.navigation_status_showall)); + } + }, + OnlyDownloading { + StatusTypeFilter getFilterItem(Context context) { + return new StatusTypeFilter(context.getString(R.string.navigation_status_onlydown)); + } + }, + OnlyUploading { + StatusTypeFilter getFilterItem(Context context) { + return new StatusTypeFilter(context.getString(R.string.navigation_status_onlyup)); + } + }, + OnlyActive { + StatusTypeFilter getFilterItem(Context context) { + return new StatusTypeFilter(context.getString(R.string.navigation_status_onlyactive)); + } + }, + OnlyInactive { + StatusTypeFilter getFilterItem(Context context) { + return new StatusTypeFilter(context.getString(R.string.navigation_status_onlyinactive)); + } + }; + + /** + * Returns a list with all status types, represented as filter item that can be shown in the GUI. + * @param context The Android UI context, to access translations + * @return A list of filter items for all available status types + */ + public static List getAllStatusTypes(Context context) { + return Arrays.asList(ShowAll.getFilterItem(context), OnlyDownloading.getFilterItem(context), + OnlyUploading.getFilterItem(context), OnlyActive.getFilterItem(context), + OnlyInactive.getFilterItem(context)); + } + + /** + * Every status type can return a filter item that represents it in the navigation + * @param context The Android UI context, to access translations + * @return A filter item object to show in the GUI + */ + abstract StatusTypeFilter getFilterItem(Context context); + + public static class StatusTypeFilter implements FilterItem { + + private final String name; + + StatusTypeFilter(String name) { + this.name = name; + } + + @Override + public String getName() { + return name; + } + + } + +} diff --git a/lite/src/org/transdroid/lite/gui/settings/MainSettingsActivity.java b/lite/src/org/transdroid/lite/gui/settings/MainSettingsActivity.java new file mode 100644 index 00000000..5240fee5 --- /dev/null +++ b/lite/src/org/transdroid/lite/gui/settings/MainSettingsActivity.java @@ -0,0 +1,164 @@ +package org.transdroid.lite.gui.settings; + +import java.util.ArrayList; +import java.util.List; + +import org.androidannotations.annotations.Bean; +import org.androidannotations.annotations.EActivity; +import org.transdroid.core.R; +import org.transdroid.lite.app.search.SearchHelper; +import org.transdroid.lite.app.search.SearchSite; +import org.transdroid.lite.app.settings.ApplicationSettings; +import org.transdroid.lite.app.settings.RssfeedSetting; +import org.transdroid.lite.app.settings.ServerSetting; +import org.transdroid.lite.app.settings.WebsearchSetting; +import org.transdroid.lite.gui.settings.RssfeedPreference.OnRssfeedClickedListener; +import org.transdroid.lite.gui.settings.ServerPreference.OnServerClickedListener; +import org.transdroid.lite.gui.settings.WebsearchPreference.OnWebsearchClickedListener; + +import android.os.Bundle; +import android.preference.ListPreference; +import android.preference.Preference; +import android.preference.Preference.OnPreferenceClickListener; + +import com.actionbarsherlock.app.SherlockPreferenceActivity; + +/** + * The main activity that provides access to all application settings. It shows the configured serves, web search sites + * and RSS feeds along with other general settings. + * @author Eric Kok + */ +@EActivity +public class MainSettingsActivity extends SherlockPreferenceActivity { + + @Bean + protected ApplicationSettings applicationSettings; + @Bean + protected SearchHelper searchHelper; + + @SuppressWarnings("deprecation") + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + // Load the preference menu and attack actions + addPreferencesFromResource(R.xml.pref_main); + findPreference("header_addserver").setOnPreferenceClickListener(onAddServer); + findPreference("header_addwebsearch").setOnPreferenceClickListener(onAddWebsearch); + findPreference("header_rssfeed").setOnPreferenceClickListener(onAddRssfeed); + findPreference("header_background").setOnPreferenceClickListener(onBackgroundSettings); + findPreference("header_system").setOnPreferenceClickListener(onSystemSettings); + + // Add existing servers + List servers = applicationSettings.getServerSettings(); + for (ServerSetting serverSetting : servers) { + getPreferenceScreen().addPreference( + new ServerPreference(this).setServerSetting(serverSetting).setOnServerClickedListener( + onServerClicked)); + } + + // Add existing websearch sites + List websearches = applicationSettings.getWebsearchSettings(); + for (WebsearchSetting websearchSetting : websearches) { + getPreferenceScreen().addPreference( + new WebsearchPreference(this).setWebsearchSetting(websearchSetting).setOnWebsearchClickedListener( + onWebsearchClicked)); + } + + // Add existing RSS feeds + List rssfeeds = applicationSettings.getRssfeedSettings(); + for (RssfeedSetting rssfeedSetting : rssfeeds) { + getPreferenceScreen().addPreference( + new RssfeedPreference(this).setRssfeedSetting(rssfeedSetting).setOnRssfeedClickedListener( + onRssfeedClicked)); + } + + // Construct list of all available search sites, in-app and web + ListPreference setSite = (ListPreference) findPreference("header_setsearchsite"); + // Retrieve the available in-app search sites (using the Torrent Search package) + List searchsites = searchHelper.getAvailableSites(); + List siteNames = new ArrayList(websearches.size() + searchsites.size()); + List siteValues = new ArrayList(websearches.size() + searchsites.size()); + for (SearchSite searchSite : searchsites) { + siteNames.add(searchSite.getName()); + siteValues.add(searchSite.getKey()); + } + for (WebsearchSetting websearch : websearches) { + siteNames.add(websearch.getName()); + siteValues.add(websearch.getKey()); + } + // Supply the Preference list names and values + setSite.setEntries(siteNames.toArray(new String[siteNames.size()])); + setSite.setEntryValues(siteValues.toArray(new String[siteValues.size()])); + + } + + @Override + public void onBuildHeaders(List

target) { + // TODO: Add two-pane support in settings + super.onBuildHeaders(target); + } + + private OnPreferenceClickListener onAddServer = new OnPreferenceClickListener() { + @Override + public boolean onPreferenceClick(Preference preference) { + ServerSettingsActivity_.intent(MainSettingsActivity.this).start(); + return true; + } + }; + + private OnPreferenceClickListener onAddWebsearch = new OnPreferenceClickListener() { + @Override + public boolean onPreferenceClick(Preference preference) { + WebsearchSettingsActivity_.intent(MainSettingsActivity.this).start(); + return true; + } + }; + + private OnPreferenceClickListener onAddRssfeed = new OnPreferenceClickListener() { + @Override + public boolean onPreferenceClick(Preference preference) { + RssfeedSettingsActivity_.intent(MainSettingsActivity.this).start(); + return true; + } + }; + + private OnPreferenceClickListener onBackgroundSettings = new OnPreferenceClickListener() { + @Override + public boolean onPreferenceClick(Preference preference) { + OtherSettingsActivity_.intent(MainSettingsActivity.this).preferencesResourceID(R.xml.pref_notifications) + .start(); + return true; + } + }; + + private OnPreferenceClickListener onSystemSettings = new OnPreferenceClickListener() { + @Override + public boolean onPreferenceClick(Preference preference) { + OtherSettingsActivity_.intent(MainSettingsActivity.this).preferencesResourceID(R.xml.pref_about).start(); + return true; + } + }; + + private OnServerClickedListener onServerClicked = new OnServerClickedListener() { + @Override + public void onServerClicked(ServerSetting serverSetting) { + ServerSettingsActivity_.intent(MainSettingsActivity.this).key(serverSetting.getOrder()).start(); + } + }; + + private OnWebsearchClickedListener onWebsearchClicked = new OnWebsearchClickedListener() { + @Override + public void onWebsearchClicked(WebsearchSetting websearchSetting) { + WebsearchSettingsActivity_.intent(MainSettingsActivity.this).key(websearchSetting.getOrder()).start(); + } + }; + + private OnRssfeedClickedListener onRssfeedClicked = new OnRssfeedClickedListener() { + @Override + public void onRssfeedClicked(RssfeedSetting rssfeedSetting) { + RssfeedSettingsActivity_.intent(MainSettingsActivity.this).key(rssfeedSetting.getOrder()).start(); + } + }; + +} diff --git a/lite/src/org/transdroid/lite/gui/settings/OtherSettingsActivity.java b/lite/src/org/transdroid/lite/gui/settings/OtherSettingsActivity.java new file mode 100644 index 00000000..5e23ee79 --- /dev/null +++ b/lite/src/org/transdroid/lite/gui/settings/OtherSettingsActivity.java @@ -0,0 +1,31 @@ +package org.transdroid.lite.gui.settings; + +import org.androidannotations.annotations.Bean; +import org.androidannotations.annotations.EActivity; +import org.androidannotations.annotations.Extra; +import org.transdroid.lite.app.settings.ApplicationSettings; + +import android.os.Bundle; + +import com.actionbarsherlock.app.SherlockPreferenceActivity; + +@EActivity +public class OtherSettingsActivity extends SherlockPreferenceActivity { + + @Extra + protected int preferencesResourceID; + + @Bean + protected ApplicationSettings applicationSettings; + + @SuppressWarnings("deprecation") + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + // Just load the preferences from XML, of which the ID is supplied as extra + addPreferencesFromResource(preferencesResourceID); + + } + +} diff --git a/lite/src/org/transdroid/lite/gui/settings/RssfeedPreference.java b/lite/src/org/transdroid/lite/gui/settings/RssfeedPreference.java new file mode 100644 index 00000000..49a81046 --- /dev/null +++ b/lite/src/org/transdroid/lite/gui/settings/RssfeedPreference.java @@ -0,0 +1,59 @@ +package org.transdroid.lite.gui.settings; + +import org.transdroid.lite.app.settings.RssfeedSetting; + +import android.content.Context; +import android.preference.Preference; + +/** + * Represents a {@link RssfeedSetting} in a preferences screen. + * @author Eric Kok + */ +public class RssfeedPreference extends Preference { + + private static final int ORDER_START = 201; + + private RssfeedSetting rssfeedSetting; + private OnRssfeedClickedListener onRssfeedClickedListener = null; + + public RssfeedPreference(Context context) { + super(context); + setOnPreferenceClickListener(onPreferenceClicked); + } + + /** + * Set the RSS feed settings object that is bound to this preference item + * @param rssfeedSetting The RSS feed settings + * @return Itself, for method chaining + */ + public RssfeedPreference setRssfeedSetting(RssfeedSetting rssfeedSetting) { + this.rssfeedSetting = rssfeedSetting; + setTitle(rssfeedSetting.getName()); + setOrder(ORDER_START + rssfeedSetting.getOrder()); + return this; + } + + /** + * Set a listener that will be notified of click events on this preference + * @param onRssfeedClickedListener The click listener to register + * @return Itself, for method chaining + */ + public RssfeedPreference setOnRssfeedClickedListener(OnRssfeedClickedListener onRssfeedClickedListener) { + this.onRssfeedClickedListener = onRssfeedClickedListener; + return this; + } + + private OnPreferenceClickListener onPreferenceClicked = new OnPreferenceClickListener() { + @Override + public boolean onPreferenceClick(Preference preference) { + if (onRssfeedClickedListener != null) + onRssfeedClickedListener.onRssfeedClicked(rssfeedSetting); + return true; + } + }; + + public interface OnRssfeedClickedListener { + public void onRssfeedClicked(RssfeedSetting rssfeedSetting); + } + +} diff --git a/lite/src/org/transdroid/lite/gui/settings/RssfeedSettingsActivity.java b/lite/src/org/transdroid/lite/gui/settings/RssfeedSettingsActivity.java new file mode 100644 index 00000000..2b83f52f --- /dev/null +++ b/lite/src/org/transdroid/lite/gui/settings/RssfeedSettingsActivity.java @@ -0,0 +1,46 @@ +package org.transdroid.lite.gui.settings; + +import org.androidannotations.annotations.Bean; +import org.androidannotations.annotations.EActivity; +import org.androidannotations.annotations.Extra; +import org.transdroid.core.R; +import org.transdroid.lite.app.settings.ApplicationSettings; + +import android.os.Bundle; + +import com.actionbarsherlock.app.SherlockPreferenceActivity; + +/** + * Activity that allows for a configuration of some RSS feed. The key can be supplied to update an + * existing RSS feed setting instead of creating a new one. + * @author Eric Kok + */ +@EActivity +public class RssfeedSettingsActivity extends SherlockPreferenceActivity { + + @Extra + protected int key = -1; + + @Bean + protected ApplicationSettings applicationSettings; + + @SuppressWarnings("deprecation") + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + // Load the raw preferences to show in this screen + addPreferencesFromResource(R.xml.pref_rssfeed); + + // Bind the preferences to the correct storage key, e.g. the first RSS feed setting stores its URL in the + // 'rssfeed_url_0' shared preferences field + if (key < 0) { + key = applicationSettings.getMaxRssfeed() + 1; + } + findPreference("rssfeed_name").setKey("rssfeed_name_" + key); + findPreference("rssfeed_url").setKey("rssfeed_url_" + key); + findPreference("rssfeed_reqauth").setKey("rssfeed_reqauth_" + key); + + } + +} diff --git a/lite/src/org/transdroid/lite/gui/settings/ServerPreference.java b/lite/src/org/transdroid/lite/gui/settings/ServerPreference.java new file mode 100644 index 00000000..802873e3 --- /dev/null +++ b/lite/src/org/transdroid/lite/gui/settings/ServerPreference.java @@ -0,0 +1,59 @@ +package org.transdroid.lite.gui.settings; + +import org.transdroid.lite.app.settings.ServerSetting; + +import android.content.Context; +import android.preference.Preference; + +/** + * Represents a {@link ServerSetting} in a preferences screen. + * @author Eric Kok + */ +public class ServerPreference extends Preference { + + private static final int ORDER_START = 1; + + private ServerSetting serverSetting; + private OnServerClickedListener onServerClickedListener = null; + + public ServerPreference(Context context) { + super(context); + setOnPreferenceClickListener(onPreferenceClicked); + } + + /** + * Set the server settings object that is bound to this preference item + * @param serverSetting The server settings + * @return Itself, for method chaining + */ + public ServerPreference setServerSetting(ServerSetting serverSetting) { + this.serverSetting = serverSetting; + setTitle(serverSetting.getHumanReadableIdentifier()); + setOrder(ORDER_START + serverSetting.getOrder()); + return this; + } + + /** + * Set a listener that will be notified of click events on this preference + * @param onServerClickedListener The click listener to register + * @return Itself, for method chaining + */ + public ServerPreference setOnServerClickedListener(OnServerClickedListener onServerClickedListener) { + this.onServerClickedListener = onServerClickedListener; + return this; + } + + private OnPreferenceClickListener onPreferenceClicked = new OnPreferenceClickListener() { + @Override + public boolean onPreferenceClick(Preference preference) { + if (onServerClickedListener != null) + onServerClickedListener.onServerClicked(serverSetting); + return true; + } + }; + + public interface OnServerClickedListener { + public void onServerClicked(ServerSetting serverSetting); + } + +} diff --git a/lite/src/org/transdroid/lite/gui/settings/ServerSettingsActivity.java b/lite/src/org/transdroid/lite/gui/settings/ServerSettingsActivity.java new file mode 100644 index 00000000..148a8b56 --- /dev/null +++ b/lite/src/org/transdroid/lite/gui/settings/ServerSettingsActivity.java @@ -0,0 +1,94 @@ +package org.transdroid.lite.gui.settings; + +import org.androidannotations.annotations.Bean; +import org.androidannotations.annotations.EActivity; +import org.androidannotations.annotations.Extra; +import org.transdroid.daemon.Daemon; +import org.transdroid.core.R; +import org.transdroid.lite.app.settings.ApplicationSettings; + +import android.content.SharedPreferences; +import android.os.Bundle; +import android.preference.Preference; +import android.preference.Preference.OnPreferenceChangeListener; +import android.preference.PreferenceManager; + +import com.actionbarsherlock.app.SherlockPreferenceActivity; + +/** + * Activity that allows for a configuration of a server. The key can be supplied to update an existing server setting + * instead of creating a new one. + * @author Eric Kok + */ +@EActivity +public class ServerSettingsActivity extends SherlockPreferenceActivity { + + @Extra + protected int key = -1; + + @Bean + protected ApplicationSettings applicationSettings; + + @SuppressWarnings("deprecation") + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + // Load the raw preferences to show in this screen + addPreferencesFromResource(R.xml.pref_server); + + // Bind the preferences to the correct storage key, e.g. the first server setting stores its address in the + // 'server_address_0' shared preferences field + if (key < 0) { + key = applicationSettings.getMaxWebsearch() + 1; + } + findPreference("server_name").setKey("server_name_" + key); + findPreference("server_type").setKey("server_type_" + key); + findPreference("server_address").setKey("server_address_" + key); + findPreference("server_port").setKey("server_port_" + key); + findPreference("server_user").setKey("server_user_" + key); + findPreference("server_pass").setKey("server_pass_" + key); + findPreference("server_extrapass").setKey("server_extrapass_" + key); + findPreference("server_localaddress").setKey("server_localaddress_" + key); + findPreference("server_localnetwork").setKey("server_localnetwork_" + key); + findPreference("server_folder").setKey("server_folder_" + key); + findPreference("server_timeout").setKey("server_timeout_" + key); + findPreference("server_alamrfinished").setKey("server_alamrfinished_" + key); + findPreference("server_alarnew").setKey("server_alarnew_" + key); + findPreference("server_os").setKey("server_os_" + key); + findPreference("server_downloaddir").setKey("server_downloaddir_" + key); + findPreference("server_ftpurl").setKey("server_ftpurl_" + key); + findPreference("server_ftppass").setKey("server_ftppass_" + key); + findPreference("server_sslenabled").setKey("server_sslenabled_" + key); + findPreference("server_ssltrustall").setKey("server_ssltrustall_" + key); + findPreference("server_ssltrustkey").setKey("server_ssltrustkey_" + key); + + // Monitor preference changes + getPreferenceScreen().setOnPreferenceChangeListener(onPreferenceChangeListener); + } + + private OnPreferenceChangeListener onPreferenceChangeListener = new OnPreferenceChangeListener() { + @Override + public boolean onPreferenceChange(Preference preference, Object newValue) { + updatePreferenceAvailability(); + return true; + } + }; + + @SuppressWarnings("deprecation") + private void updatePreferenceAvailability() { + + // Use daemon factory to see if the newly selected daemon supports the feature + SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this); + Daemon daemonType = Daemon.fromCode(prefs.getString("server_type_" + key, null)); + findPreference("server_extrapass_" + key).setEnabled(Daemon.supportsExtraPassword(daemonType)); + findPreference("server_folder_" + key).setEnabled(daemonType == null? false: Daemon.supportsCustomFolder(daemonType)); + findPreference("server_downloaddir_" + key).setEnabled(daemonType == null? false: Daemon.needsManualPathSpecified(daemonType)); + //findPreference("server_ssltrustkey_" + key).setEnabled(sslValue && !sslTAValue); + + // Adjust title texts accordingly + findPreference("server_folder_" + key).setTitle(daemonType == Daemon.rTorrent? R.string.pref_scgifolder: R.string.pref_folder); + + } + +} diff --git a/lite/src/org/transdroid/lite/gui/settings/WebsearchPreference.java b/lite/src/org/transdroid/lite/gui/settings/WebsearchPreference.java new file mode 100644 index 00000000..929f66a9 --- /dev/null +++ b/lite/src/org/transdroid/lite/gui/settings/WebsearchPreference.java @@ -0,0 +1,59 @@ +package org.transdroid.lite.gui.settings; + +import org.transdroid.lite.app.settings.WebsearchSetting; + +import android.content.Context; +import android.preference.Preference; + +/** + * Represents a {@link WebsearchSetting} in a preferences screen. + * @author Eric Kok + */ +public class WebsearchPreference extends Preference { + + private static final int ORDER_START = 102; + + private WebsearchSetting websearchSetting; + private OnWebsearchClickedListener onWebsearchClickedListener = null; + + public WebsearchPreference(Context context) { + super(context); + setOnPreferenceClickListener(onPreferenceClicked); + } + + /** + * Set the websearch settings object that is bound to this preference item + * @param websearchSetting The websearch settings + * @return Itself, for method chaining + */ + public WebsearchPreference setWebsearchSetting(WebsearchSetting websearchSetting) { + this.websearchSetting = websearchSetting; + setTitle(websearchSetting.getName()); + setOrder(ORDER_START + websearchSetting.getOrder()); + return this; + } + + /** + * Set a listener that will be notified of click events on this preference + * @param onWebsearchClickedListener The click listener to register + * @return Itself, for method chaining + */ + public WebsearchPreference setOnWebsearchClickedListener(OnWebsearchClickedListener onWebsearchClickedListener) { + this.onWebsearchClickedListener = onWebsearchClickedListener; + return this; + } + + private OnPreferenceClickListener onPreferenceClicked = new OnPreferenceClickListener() { + @Override + public boolean onPreferenceClick(Preference preference) { + if (onWebsearchClickedListener != null) + onWebsearchClickedListener.onWebsearchClicked(websearchSetting); + return true; + } + }; + + public interface OnWebsearchClickedListener { + public void onWebsearchClicked(WebsearchSetting serverSetting); + } + +} diff --git a/lite/src/org/transdroid/lite/gui/settings/WebsearchSettingsActivity.java b/lite/src/org/transdroid/lite/gui/settings/WebsearchSettingsActivity.java new file mode 100644 index 00000000..d5daae2e --- /dev/null +++ b/lite/src/org/transdroid/lite/gui/settings/WebsearchSettingsActivity.java @@ -0,0 +1,45 @@ +package org.transdroid.lite.gui.settings; + +import org.androidannotations.annotations.Bean; +import org.androidannotations.annotations.EActivity; +import org.androidannotations.annotations.Extra; +import org.transdroid.core.R; +import org.transdroid.lite.app.settings.ApplicationSettings; + +import android.os.Bundle; + +import com.actionbarsherlock.app.SherlockPreferenceActivity; + +/** + * Activity that allows for a configuration of a web search site. The key can be supplied to update an existing web + * search site setting instead of creating a new one. + * @author Eric Kok + */ +@EActivity +public class WebsearchSettingsActivity extends SherlockPreferenceActivity { + + @Extra + protected int key = -1; + + @Bean + protected ApplicationSettings applicationSettings; + + @SuppressWarnings("deprecation") + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + // Load the raw preferences to show in this screen + addPreferencesFromResource(R.xml.pref_websearch); + + // Bind the preferences to the correct storage key, e.g. the first site setting stores its URL in the + // 'websearch_baseurl_0' shared preferences field + if (key < 0) { + key = applicationSettings.getMaxWebsearch() + 1; + } + findPreference("websearch_name").setKey("websearch_name_" + key); + findPreference("websearch_baseurl").setKey("websearch_baseurl_" + key); + + } + +}