From 4c25c1038f3b7560cd387a50d943b20732331215 Mon Sep 17 00:00:00 2001 From: Eric Kok Date: Mon, 20 Feb 2012 15:41:54 +0100 Subject: [PATCH] Added alarm notification LED colour including costumizable colour setting (issue 384). (Also some small crash fixes.) --- README | 3 + .../res/layout-land/dialog_color_picker.xml | 76 ++ android/res/layout/dialog_color_picker.xml | 77 ++ android/res/values/strings.xml | 5 + .../colorpicker/AlphaPatternDrawable.java | 128 +++ .../colorpicker/ColorPickerDialog.java | 129 +++ .../colorpicker/ColorPickerPanelView.java | 171 ++++ .../colorpicker/ColorPickerPreference.java | 246 +++++ .../colorpicker/ColorPickerView.java | 952 ++++++++++++++++++ .../org/transdroid/gui/DetailsFragment.java | 5 + .../org/transdroid/gui/TaskResultHandler.java | 5 + .../org/transdroid/gui/TorrentsFragment.java | 8 +- .../transdroid/preferences/Preferences.java | 12 +- .../preferences/PreferencesAlarm.java | 19 +- .../org/transdroid/service/AlarmService.java | 11 +- .../org/transdroid/service/AlarmSettings.java | 10 +- lib/.classpath | 2 +- .../transdroid/daemon/IDaemonCallback.java | 2 + lib/src/org/transdroid/daemon/TaskQueue.java | 8 +- 19 files changed, 1852 insertions(+), 17 deletions(-) create mode 100644 android/res/layout-land/dialog_color_picker.xml create mode 100644 android/res/layout/dialog_color_picker.xml create mode 100644 android/src/net/margaritov/preference/colorpicker/AlphaPatternDrawable.java create mode 100644 android/src/net/margaritov/preference/colorpicker/ColorPickerDialog.java create mode 100644 android/src/net/margaritov/preference/colorpicker/ColorPickerPanelView.java create mode 100644 android/src/net/margaritov/preference/colorpicker/ColorPickerPreference.java create mode 100644 android/src/net/margaritov/preference/colorpicker/ColorPickerView.java diff --git a/README b/README index 6c9f6040..2f989bf7 100644 --- a/README +++ b/README @@ -34,4 +34,7 @@ Some code/libraries are used in the project: android-xmlrpc pskink et al. (Apache License, Version 2.0) http://code.google.com/p/android-xmlrpc/ + android-ColorPickerPreference + Daniel Nilsson and Sergey Margaritov (Apache License, Version 2.0) + https://github.com/attenzione/android-ColorPickerPreference diff --git a/android/res/layout-land/dialog_color_picker.xml b/android/res/layout-land/dialog_color_picker.xml new file mode 100644 index 00000000..e326d7e7 --- /dev/null +++ b/android/res/layout-land/dialog_color_picker.xml @@ -0,0 +1,76 @@ + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/res/layout/dialog_color_picker.xml b/android/res/layout/dialog_color_picker.xml new file mode 100644 index 00000000..8ba75f2a --- /dev/null +++ b/android/res/layout/dialog_color_picker.xml @@ -0,0 +1,77 @@ + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/res/values/strings.xml b/android/res/values/strings.xml index 034f64de..c25dcf0c 100644 --- a/android/res/values/strings.xml +++ b/android/res/values/strings.xml @@ -216,6 +216,8 @@ Notification sound to play with alarm Enable vibration Vibrate on alarm +Notification LED colour +If supported by your device Check RSS feeds Alarm when new torrents are available Enable ADW notifications @@ -324,6 +326,9 @@ Loading RSS feed Connected, but the RSS feed is empty +Color Picker +Press on Color to apply + Error during communication with server Error building request Error parsing of server response (please check your settings) diff --git a/android/src/net/margaritov/preference/colorpicker/AlphaPatternDrawable.java b/android/src/net/margaritov/preference/colorpicker/AlphaPatternDrawable.java new file mode 100644 index 00000000..60947a4a --- /dev/null +++ b/android/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/android/src/net/margaritov/preference/colorpicker/ColorPickerDialog.java b/android/src/net/margaritov/preference/colorpicker/ColorPickerDialog.java new file mode 100644 index 00000000..63840bca --- /dev/null +++ b/android/src/net/margaritov/preference/colorpicker/ColorPickerDialog.java @@ -0,0 +1,129 @@ +/* + * 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 org.transdroid.R; + +import android.app.Dialog; +import android.content.Context; +import android.graphics.PixelFormat; +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 branding. + 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(); + } + +} diff --git a/android/src/net/margaritov/preference/colorpicker/ColorPickerPanelView.java b/android/src/net/margaritov/preference/colorpicker/ColorPickerPanelView.java new file mode 100644 index 00000000..4011b5ff --- /dev/null +++ b/android/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/android/src/net/margaritov/preference/colorpicker/ColorPickerPreference.java b/android/src/net/margaritov/preference/colorpicker/ColorPickerPreference.java new file mode 100644 index 00000000..1b2d831e --- /dev/null +++ b/android/src/net/margaritov/preference/colorpicker/ColorPickerPreference.java @@ -0,0 +1,246 @@ +/* + * 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.graphics.Bitmap; +import android.graphics.Color; +import android.graphics.Bitmap.Config; +import android.preference.Preference; +import android.util.AttributeSet; +import android.util.Log; +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; + int mDefaultValue = Color.BLACK; + private int mValue = Color.BLACK; + private float mDensity = 0; + private boolean mAlphaSliderEnabled = false; + + private static final String androidns = "http://schemas.android.com/apk/res/android"; + + 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 void onSetInitialValue(boolean restoreValue, Object defaultValue) { + onColorChanged(restoreValue ? getValue() : (Integer) defaultValue); + } + + private void init(Context context, AttributeSet attrs) { + mDensity = getContext().getResources().getDisplayMetrics().density; + setOnPreferenceClickListener(this); + if (attrs != null) { + String defaultValue = attrs.getAttributeValue(androidns, "defaultValue"); + if (defaultValue.startsWith("#")) { + try { + mDefaultValue = convertToColorInt(defaultValue); + } catch (NumberFormatException e) { + Log.e("ColorPickerPreference", "Wrong color: " + defaultValue); + mDefaultValue = convertToColorInt("#FF000000"); + } + } else { + int resourceId = attrs.getAttributeResourceValue(androidns, "defaultValue", 0); + if (resourceId != 0) { + mDefaultValue = context.getResources().getInteger(resourceId); + } + } + mAlphaSliderEnabled = attrs.getAttributeBooleanValue(null, "alphaSlider", false); + } + mValue = mDefaultValue; + } + + @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); + iView.setBackgroundDrawable(new AlphaPatternDrawable((int)(5 * mDensity))); + iView.setImageBitmap(getPreviewBitmap()); + } + + private Bitmap getPreviewBitmap() { + int d = (int) (mDensity * 31); //30dip + int color = getValue(); + 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; + } + + public int getValue() { + try { + if (isPersistent()) { + mValue = getPersistedInt(mDefaultValue); + } + } catch (ClassCastException e) { + mValue = mDefaultValue; + } + + return mValue; + } + + @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) { + ColorPickerDialog picker = new ColorPickerDialog(getContext(), getValue()); + picker.setOnColorChangedListener(this); + if (mAlphaSliderEnabled) { + picker.setAlphaSliderVisible(true); + } + picker.show(); + + return false; + } + + /** + * 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); + } + +} \ No newline at end of file diff --git a/android/src/net/margaritov/preference/colorpicker/ColorPickerView.java b/android/src/net/margaritov/preference/colorpicker/ColorPickerView.java new file mode 100644 index 00000000..32e81dbd --- /dev/null +++ b/android/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/android/src/org/transdroid/gui/DetailsFragment.java b/android/src/org/transdroid/gui/DetailsFragment.java index 8a89c84e..2e150034 100644 --- a/android/src/org/transdroid/gui/DetailsFragment.java +++ b/android/src/org/transdroid/gui/DetailsFragment.java @@ -529,6 +529,11 @@ public class DetailsFragment extends Fragment implements IDaemonCallback, OnSele * } } */ + @Override + public boolean isAttached() { + return getActivity() != null; + } + @Override public void onQueueEmpty() { // No active task: turn off status indicator diff --git a/android/src/org/transdroid/gui/TaskResultHandler.java b/android/src/org/transdroid/gui/TaskResultHandler.java index 4f676002..f8c55906 100644 --- a/android/src/org/transdroid/gui/TaskResultHandler.java +++ b/android/src/org/transdroid/gui/TaskResultHandler.java @@ -90,5 +90,10 @@ public class TaskResultHandler extends Handler implements IDaemonCallback { msg.obj = result; sendMessage(msg); } + + @Override + public boolean isAttached() { + return callback.isAttached(); + } } diff --git a/android/src/org/transdroid/gui/TorrentsFragment.java b/android/src/org/transdroid/gui/TorrentsFragment.java index f551bb59..4afa19c0 100644 --- a/android/src/org/transdroid/gui/TorrentsFragment.java +++ b/android/src/org/transdroid/gui/TorrentsFragment.java @@ -1593,6 +1593,11 @@ public class TorrentsFragment extends Fragment implements IDaemonCallback, OnTou return ""; } + @Override + public boolean isAttached() { + return getActivity() != null; + } + @Override public void onTaskFailure(DaemonTaskFailureResult result) { @@ -1970,7 +1975,8 @@ public class TorrentsFragment extends Fragment implements IDaemonCallback, OnTou private void setProgressBar(boolean b) { inProgress = b; - getSupportActivity().invalidateOptionsMenu(); + if (getSupportActivity() != null) + getSupportActivity().invalidateOptionsMenu(); } protected View findViewById(int id) { diff --git a/android/src/org/transdroid/preferences/Preferences.java b/android/src/org/transdroid/preferences/Preferences.java index b21adc85..a2f94a0e 100644 --- a/android/src/org/transdroid/preferences/Preferences.java +++ b/android/src/org/transdroid/preferences/Preferences.java @@ -127,12 +127,13 @@ public class Preferences { public static final String KEY_PREF_ALARMPLAYSOUND = "transdroid_alarm_playsound"; public static final String KEY_PREF_ALARMSOUNDURI = "transdroid_alarm_sounduri"; public static final String KEY_PREF_ALARMVIBRATE = "transdroid_alarm_vibrate"; - public static final String KEY_PREF_ADWNOTIFY = "transdroid_alarm_adwnotify"; - public static final String KEY_PREF_ADWONLYDL = "transdroid_alarm_adwonlydl"; + public static final String KEY_PREF_ALARMCOLOUR = "transdroid_alarm_colour"; + public static final String KEY_PREF_ADWNOTIFY = "transdroid_alarm_adwnotify"; + public static final String KEY_PREF_ADWONLYDL = "transdroid_alarm_adwonlydl"; - public static final String KEY_WIDGET_DAEMON = "transdroid_widget_daemon"; - public static final String KEY_WIDGET_REFRESH = "transdroid_widget_refresh"; - public static final String KEY_WIDGET_LAYOUT = "transdroid_widget_layout"; + public static final String KEY_WIDGET_DAEMON = "transdroid_widget_daemon"; + public static final String KEY_WIDGET_REFRESH = "transdroid_widget_refresh"; + public static final String KEY_WIDGET_LAYOUT = "transdroid_widget_layout"; /** * Determines the order number of the last used daemon settings object @@ -918,6 +919,7 @@ public class Preferences { prefs.getBoolean(KEY_PREF_ALARMPLAYSOUND, false), prefs.getString(KEY_PREF_ALARMSOUNDURI, null), prefs.getBoolean(KEY_PREF_ALARMVIBRATE, false), + prefs.getInt(KEY_PREF_ALARMCOLOUR, 0xff7dbb21), prefs.getBoolean(KEY_PREF_ADWNOTIFY, false), prefs.getBoolean(KEY_PREF_ADWONLYDL, false)); } diff --git a/android/src/org/transdroid/preferences/PreferencesAlarm.java b/android/src/org/transdroid/preferences/PreferencesAlarm.java index 3ed4826f..5429cafe 100644 --- a/android/src/org/transdroid/preferences/PreferencesAlarm.java +++ b/android/src/org/transdroid/preferences/PreferencesAlarm.java @@ -17,6 +17,8 @@ */ package org.transdroid.preferences; +import net.margaritov.preference.colorpicker.ColorPickerPreference; + import org.transdroid.R; import org.transdroid.service.BootReceiver; @@ -39,6 +41,7 @@ public class PreferencesAlarm extends PreferenceActivity { private TransdroidCheckBoxPreference alarmPlaySound; private TransdroidNotificationListPreference alarmSoundURI; private TransdroidCheckBoxPreference alarmVibrate; + private ColorPickerPreference alarmColour; private TransdroidCheckBoxPreference adwNotify; private TransdroidCheckBoxPreference adwOnlyDl; @@ -78,14 +81,14 @@ public class PreferencesAlarm extends PreferenceActivity { checkRssFeeds.setKey(Preferences.KEY_PREF_CHECKRSSFEEDS); checkRssFeeds.setEnabled(isEnabled); getPreferenceScreen().addItemFromInflater(checkRssFeeds); - // alarm play sound + // Alarm play sound alarmPlaySound = new TransdroidCheckBoxPreference(this); alarmPlaySound.setTitle(R.string.pref_alarmplaysound); alarmPlaySound.setSummary(R.string.pref_alarmplaysound_info); alarmPlaySound.setKey(Preferences.KEY_PREF_ALARMPLAYSOUND); alarmPlaySound.setEnabled(isEnabled); getPreferenceScreen().addItemFromInflater(alarmPlaySound); - // alarm sound URI + // Alarm sound URI alarmSoundURI = new TransdroidNotificationListPreference(this); alarmSoundURI.setTitle(R.string.pref_alarmsounduri); alarmSoundURI.setSummary(R.string.pref_alarmsounduri_info); @@ -95,13 +98,22 @@ public class PreferencesAlarm extends PreferenceActivity { alarmSoundURI.setShowSilent(false); alarmSoundURI.setEnabled(isEnabled); getPreferenceScreen().addItemFromInflater(alarmSoundURI); - // vibrate + // Vibrate alarmVibrate = new TransdroidCheckBoxPreference(this); alarmVibrate.setTitle(R.string.pref_alarmvibrate); alarmVibrate.setSummary(R.string.pref_alarmvibrate_info); alarmVibrate.setKey(Preferences.KEY_PREF_ALARMVIBRATE); alarmVibrate.setEnabled(isEnabled); getPreferenceScreen().addItemFromInflater(alarmVibrate); + // Notification LED colour + alarmColour = new ColorPickerPreference(this); + alarmColour.setTitle(R.string.pref_alarmcolour); + alarmColour.setSummary(R.string.pref_alarmcolour_info); + alarmColour.setKey(Preferences.KEY_PREF_ALARMCOLOUR); + alarmColour.setAlphaSliderEnabled(false); + alarmColour.setDefaultValue(0xff7dbb21); + alarmColour.setEnabled(isEnabled); + getPreferenceScreen().addItemFromInflater(alarmColour); // Enable ADW notifications adwNotify = new TransdroidCheckBoxPreference(this); adwNotify.setTitle(R.string.pref_adwnotify); @@ -153,6 +165,7 @@ public class PreferencesAlarm extends PreferenceActivity { alarmPlaySound.setEnabled(isEnabled); alarmSoundURI.setEnabled(isEnabled); alarmVibrate.setEnabled(isEnabled); + alarmColour.setEnabled(isEnabled); adwNotify.setEnabled(isEnabled); adwOnlyDl.setEnabled(isEnabled && isAdwEnabled); } diff --git a/android/src/org/transdroid/service/AlarmService.java b/android/src/org/transdroid/service/AlarmService.java index eba9daf4..47454235 100644 --- a/android/src/org/transdroid/service/AlarmService.java +++ b/android/src/org/transdroid/service/AlarmService.java @@ -314,15 +314,22 @@ public class AlarmService extends IntentService { notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); } - //if sound enabled add to notification + // If sound enabled add to notification if (settings.getAlarmPlaySound() && settings.getAlarmSoundURI() != null) { newNotification.sound = Uri.parse(settings.getAlarmSoundURI()); } - //if vibration enabled add to notification + // If vibration enabled add to notification if (settings.getAlarmVibrate()) { newNotification.defaults = Notification.DEFAULT_VIBRATE; } + + // Add coloured light; defaults to 0xff7dbb21 + newNotification.ledARGB = settings.getAlarmColour(); + newNotification.ledOnMS = 600; + newNotification.ledOffMS = 1000; + newNotification.flags |= Notification.FLAG_SHOW_LIGHTS; + // Send notification notificationManager.notify(notifyID, newNotification); diff --git a/android/src/org/transdroid/service/AlarmSettings.java b/android/src/org/transdroid/service/AlarmSettings.java index 3357ebd7..a10059a0 100644 --- a/android/src/org/transdroid/service/AlarmSettings.java +++ b/android/src/org/transdroid/service/AlarmSettings.java @@ -31,16 +31,18 @@ public class AlarmSettings{ private boolean alarmPlaySound; private String alarmSoundURI; private boolean alarmVibrate; + private int alarmColour; private boolean adwNotify; private boolean adwOnlyDl; - public AlarmSettings(boolean enableAlarm, int alarmInterval, boolean checkRssFeeds, boolean alarmPlaySound, String alarmSoundURI, boolean alarmVibrate, boolean adwNotify, boolean adwOnlyDl) { + public AlarmSettings(boolean enableAlarm, int alarmInterval, boolean checkRssFeeds, boolean alarmPlaySound, String alarmSoundURI, boolean alarmVibrate, int alarmColour, boolean adwNotify, boolean adwOnlyDl) { this.enableAlarm = enableAlarm; this.alarmInterval = alarmInterval; this.checkRssFeeds = checkRssFeeds; this.alarmPlaySound = alarmPlaySound; this.alarmSoundURI = alarmSoundURI; this.alarmVibrate = alarmVibrate; + this.alarmColour = alarmColour; this.adwNotify = adwNotify; this.adwOnlyDl = adwOnlyDl; } @@ -68,11 +70,15 @@ public class AlarmSettings{ public String getAlarmSoundURI() { return alarmSoundURI; } - + public boolean getAlarmVibrate() { return alarmVibrate; } + public int getAlarmColour() { + return alarmColour; + } + public boolean showAdwNotifications() { return adwNotify; } diff --git a/lib/.classpath b/lib/.classpath index e7acd261..f2d4ba8a 100644 --- a/lib/.classpath +++ b/lib/.classpath @@ -2,6 +2,6 @@ - + diff --git a/lib/src/org/transdroid/daemon/IDaemonCallback.java b/lib/src/org/transdroid/daemon/IDaemonCallback.java index e57de8e6..8c76f967 100644 --- a/lib/src/org/transdroid/daemon/IDaemonCallback.java +++ b/lib/src/org/transdroid/daemon/IDaemonCallback.java @@ -38,5 +38,7 @@ public interface IDaemonCallback { void onTaskFailure(DaemonTaskFailureResult result); void onTaskSuccess(DaemonTaskSuccessResult result); + + boolean isAttached(); } diff --git a/lib/src/org/transdroid/daemon/TaskQueue.java b/lib/src/org/transdroid/daemon/TaskQueue.java index 602252c8..dd16189d 100644 --- a/lib/src/org/transdroid/daemon/TaskQueue.java +++ b/lib/src/org/transdroid/daemon/TaskQueue.java @@ -156,17 +156,19 @@ public class TaskQueue implements Runnable { return; } - callback.onQueuedTaskStarted(task); + if (callback.isAttached()) + callback.onQueuedTaskStarted(task); // Ask the daemon adapter to perform the task (which does it synchronously) DLog.d(LOG_NAME, "Starting task: " + task.toString()); DaemonTaskResult result = task.execute(); - callback.onQueuedTaskFinished(task); + if (callback.isAttached()) + callback.onQueuedTaskFinished(task); // Return the result (to the UI thread) DLog.d(LOG_NAME, "Task result: " + (result == null? "null": result.toString())); - if (result != null && !this.paused) { + if (result != null && !this.paused && callback.isAttached()) { if (result.wasSuccessful()) { callback.onTaskSuccess((DaemonTaskSuccessResult) result); } else {