1
0
Fork 0
mirror of https://github.com/geometer/FBReaderJ.git synced 2025-10-04 10:19:33 +02:00

AmbilWarna library + new color picker + style editing

Conflicts:
	AndroidManifest.xml
This commit is contained in:
Nikolay Pultsin 2013-06-09 00:45:53 +01:00
parent aeed5ae6a2
commit c57eeca23f
62 changed files with 1222 additions and 249 deletions

View file

@ -112,7 +112,8 @@
<meta-data android:name="android.app.searchable" android:resource="@xml/searchable"/>
</activity>
<activity android:name="org.geometerplus.android.fbreader.CancelActivity" android:theme="@style/FBReader.Dialog" android:configChanges="orientation|keyboardHidden|screenSize"/>
<activity android:name="org.geometerplus.android.fbreader.StyleListActivity" android:theme="@style/FBReader.Dialog" android:configChanges="orientation|keyboardHidden|screenSize"/>
<activity android:name="org.geometerplus.android.fbreader.style.StyleListActivity" android:theme="@style/FBReader.Dialog" android:configChanges="orientation|keyboardHidden|screenSize"/>
<activity android:name="org.geometerplus.android.fbreader.style.EditStyleActivity" android:theme="@style/FBReader.Dialog" android:configChanges="orientation|keyboardHidden|screenSize"/>
<activity android:name="org.geometerplus.android.fbreader.image.ImageViewActivity" android:process=":imageView" android:theme="@style/FBReader.Activity" android:configChanges="orientation|keyboardHidden|screenSize"/>
<service android:name="org.geometerplus.android.fbreader.libraryService.LibraryService" android:launchMode="singleTask" android:process=":libraryService">
<intent-filter>

View file

@ -112,7 +112,8 @@
<meta-data android:name="android.app.searchable" android:resource="@xml/searchable"/>
</activity>
<activity android:name="org.geometerplus.android.fbreader.CancelActivity" android:theme="@style/FBReader.Dialog" android:configChanges="orientation|keyboardHidden|screenSize"/>
<activity android:name="org.geometerplus.android.fbreader.StyleListActivity" android:theme="@style/FBReader.Dialog" android:configChanges="orientation|keyboardHidden|screenSize"/>
<activity android:name="org.geometerplus.android.fbreader.style.StyleListActivity" android:theme="@style/FBReader.Dialog" android:configChanges="orientation|keyboardHidden|screenSize"/>
<activity android:name="org.geometerplus.android.fbreader.style.EditStyleActivity" android:theme="@style/FBReader.Dialog" android:configChanges="orientation|keyboardHidden|screenSize"/>
<activity android:name="org.geometerplus.android.fbreader.image.ImageViewActivity" android:process=":imageView" android:theme="@style/FBReader.Activity" android:configChanges="orientation|keyboardHidden|screenSize"/>
<service android:name="org.geometerplus.android.fbreader.libraryService.LibraryService" android:launchMode="singleTask" android:process=":libraryService">
<intent-filter>

View file

@ -1,4 +1,5 @@
===== 1.8.2 (May ??, 2013) =====
===== 1.8.2 (Jun ??, 2013) =====
* New color picker dialog (AmbilWarna library http://code.google.com/p/android-color-picker/ is used)
* Updated Spanish loading (by Diego Bernardi, sponsored by Panacea Supplies)
* Updated Czech localization (by Marek Pavelka)
* Updated Dutch localization (by Frank Fesevur)

View file

@ -221,10 +221,19 @@
<node name="forward" value="Forward"/>
<node name="close" value="Close FBReader"/>
</node>
<node name="style" value="Style %s"/>
<node name="highlightingStyleMenu">
<node name="style" value="Style %s"/>
<node name="editStyle" value="Edit style…"/>
<node name="deleteBookmark" value="Delete bookmark"/>
</node>
<node name="editStyle">
<node name="name" value="Style name"/>
<node name="invisible" value="Invisible">
<node name="summaryOn" value="Do not highlight bookmarks"/>
<node name="summaryOff" value="Highlight bookmarks"/>
</node>
<node name="bgColor" value="Background color"/>
</node>
<node name="selection" value="Selection">
<node name="copyToClipboard" value="Copy to clipboard"/>
<node name="openInDictionary" value="Open in dictionary"/>

View file

@ -11,3 +11,4 @@ java.encoding=utf-8
# proguard.config=proguard.cfg
# Project target.
target=android-14
android.library.reference.1=third-party/AmbilWarna

View file

@ -0,0 +1,33 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:minHeight="?android:attr/listPreferredItemHeight"
android:paddingRight="10dip"
android:paddingTop="10dip"
android:paddingBottom="10dip"
android:orientation="horizontal"
>
<yuku.ambilwarna.widget.AmbilWarnaPrefWidgetView
android:id="@+id/color_preference_widget"
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_gravity="center_vertical|left"
android:focusable="false"
android:clickable="false"
/>
<TextView
android:id="@+id/color_preference_title"
android:layout_width="wrap_content"
android:layout_height="fill_parent"
android:gravity="center_vertical|left"
android:layout_marginLeft="13dip"
android:singleLine="true"
android:textAppearance="?android:attr/textAppearanceMedium"
/>
<View
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_weight="3"
/>
</LinearLayout>

View file

@ -9,12 +9,13 @@
android:paddingBottom="10dip"
android:orientation="horizontal"
>
<ImageView
<yuku.ambilwarna.widget.AmbilWarnaPrefWidgetView
android:id="@+id/style_item_color"
android:layout_width="wrap_content"
android:layout_height="fill_parent"
android:minHeight="?android:attr/listPreferredItemHeight"
android:minWidth="?android:attr/listPreferredItemHeight"
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_gravity="center_vertical|left"
android:focusable="false"
android:clickable="false"
/>
<TextView
android:id="@+id/style_item_title"
@ -28,5 +29,13 @@
<View
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_weight="3"
/>
<Button
android:layout_width="wrap_content"
android:layout_height="fill_parent"
android:id="@+id/style_item_edit_button"
android:focusable="false"
android:focusableInTouchMode="false"
/>
</LinearLayout>

View file

@ -77,7 +77,7 @@ class ProcessHyperlinkAction extends FBAndroidAction {
intent.setData(Uri.parse(url));
intent.putExtra(
ImageViewActivity.BACKGROUND_COLOR_KEY,
Reader.ImageViewBackgroundOption.getValue().getIntValue()
Reader.ImageViewBackgroundOption.getValue().intValue()
);
OrientationUtil.startActivity(BaseActivity, intent);
} catch (Exception e) {

View file

@ -27,6 +27,7 @@ import org.geometerplus.fbreader.book.Bookmark;
import org.geometerplus.fbreader.book.SerializerUtil;
import org.geometerplus.fbreader.fbreader.FBReaderApp;
import org.geometerplus.android.fbreader.style.StyleListActivity;
import org.geometerplus.android.util.UIUtil;
public class SelectionBookmarkAction extends FBAndroidAction {

View file

@ -65,7 +65,7 @@ public class ImageViewActivity extends Activity {
final Intent intent = getIntent();
myBgColor = new ZLColor(
intent.getIntExtra(BACKGROUND_COLOR_KEY, new ZLColor(127, 127, 127).getIntValue())
intent.getIntExtra(BACKGROUND_COLOR_KEY, new ZLColor(127, 127, 127).intValue())
);
final Uri uri = intent.getData();

View file

@ -416,7 +416,7 @@ public class BookCollectionShadow extends AbstractBookCollection implements Serv
}
}
public HighlightingStyle getHighlightingStyle(int styleId) {
public synchronized HighlightingStyle getHighlightingStyle(int styleId) {
if (myInterface == null) {
return null;
}
@ -427,7 +427,7 @@ public class BookCollectionShadow extends AbstractBookCollection implements Serv
}
}
public List<HighlightingStyle> highlightingStyles() {
public synchronized List<HighlightingStyle> highlightingStyles() {
if (myInterface == null) {
return Collections.emptyList();
}
@ -438,6 +438,16 @@ public class BookCollectionShadow extends AbstractBookCollection implements Serv
}
}
public synchronized void saveHighlightingStyle(HighlightingStyle style) {
if (myInterface != null) {
try {
myInterface.saveHighlightingStyle(SerializerUtil.serialize(style));
} catch (RemoteException e) {
// ignore
}
}
}
// method from ServiceConnection interface
public synchronized void onServiceConnected(ComponentName name, IBinder service) {
myInterface = LibraryInterface.Stub.asInterface(service);

View file

@ -47,4 +47,5 @@ interface LibraryInterface {
String getHighlightingStyle(in int styleId);
List<String> highlightingStyles();
void saveHighlightingStyle(in String style);
}

View file

@ -269,6 +269,10 @@ public class LibraryService extends Service {
public List<String> highlightingStyles() {
return SerializerUtil.serializeStyleList(myCollection.highlightingStyles());
}
public void saveHighlightingStyle(String style) {
myCollection.saveHighlightingStyle(SerializerUtil.deserializeStyle(style));
}
}
private volatile LibraryImplementation myLibrary;

View file

@ -28,10 +28,11 @@ import android.database.sqlite.SQLiteStatement;
import android.database.SQLException;
import android.database.Cursor;
import org.geometerplus.zlibrary.core.config.ZLConfig;
import org.geometerplus.zlibrary.core.filesystem.ZLFile;
import org.geometerplus.zlibrary.core.options.ZLStringOption;
import org.geometerplus.zlibrary.core.options.ZLIntegerOption;
import org.geometerplus.zlibrary.core.config.ZLConfig;
import org.geometerplus.zlibrary.core.util.ZLColor;
import org.geometerplus.zlibrary.text.view.ZLTextPosition;
import org.geometerplus.zlibrary.text.view.ZLTextFixedPosition;
@ -883,15 +884,34 @@ final class SQLiteBooksDatabase extends BooksDatabase {
@Override
protected List<HighlightingStyle> loadStyles() {
final LinkedList<HighlightingStyle> list = new LinkedList<HighlightingStyle>();
final String sql = "SELECT style_id,bg_color FROM HighlightingStyle";
final String sql = "SELECT style_id,name,bg_color FROM HighlightingStyle";
final Cursor cursor = myDatabase.rawQuery(sql, null);
while (cursor.moveToNext()) {
list.add(createStyle((int)cursor.getLong(0), (int)cursor.getLong(1)));
list.add(createStyle(
(int)cursor.getLong(0),
cursor.getString(1),
(int)cursor.getLong(2)
));
}
cursor.close();
return list;
}
private SQLiteStatement myInsertStyleStatement;
protected void saveStyle(HighlightingStyle style) {
if (myInsertStyleStatement == null) {
myInsertStyleStatement = myDatabase.compileStatement(
"INSERT OR REPLACE INTO HighlightingStyle (style_id,name,bg_color) VALUES (?,?,?)"
);
}
myInsertStyleStatement.bindLong(1, style.Id);
final String name = style.getName();
myInsertStyleStatement.bindString(2, name != null ? name : "");
final ZLColor bgColor = style.getBackgroundColor();
myInsertStyleStatement.bindLong(3, bgColor != null ? bgColor.intValue() : -1);
myInsertStyleStatement.executeInsert();
}
private SQLiteStatement myInsertBookmarkStatement;
private SQLiteStatement myUpdateBookmarkStatement;
@Override
@ -1410,7 +1430,7 @@ final class SQLiteBooksDatabase extends BooksDatabase {
myDatabase.execSQL(
"CREATE TABLE IF NOT EXISTS HighlightingStyle(" +
"style_id INTEGER PRIMARY KEY," +
"name TEXT," +
"name TEXT NOT NULL," +
"bg_color INTEGER NOT NULL)");
myDatabase.execSQL("ALTER TABLE Bookmarks ADD COLUMN style_id INTEGER NOT NULL REFERENCES HighlightingStyle(style_id) DEFAULT 1");
myDatabase.execSQL("UPDATE Bookmarks SET end_paragraph = LENGTH(bookmark_text)");

View file

@ -0,0 +1,72 @@
/*
* Copyright (C) 2010-2013 Geometer Plus <contact@geometerplus.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
package org.geometerplus.android.fbreader.preferences;
import android.content.Context;
import android.preference.Preference;
import android.view.View;
import android.widget.TextView;
import yuku.ambilwarna.AmbilWarnaDialog;
import org.geometerplus.zlibrary.core.util.ZLColor;
import org.geometerplus.zlibrary.ui.android.R;
import org.geometerplus.zlibrary.ui.android.util.ZLAndroidColorUtil;
public abstract class ColorPreference extends Preference {
protected ColorPreference(Context context) {
super(context);
setWidgetLayoutResource(R.layout.color_preference);
}
public abstract String getTitle();
protected abstract ZLColor getSavedColor();
protected abstract void saveColor(ZLColor color);
@Override
protected void onBindView(View view) {
super.onBindView(view);
((TextView)view.findViewById(R.id.color_preference_title)).setText(getTitle());
final ZLColor color = getSavedColor();
view.findViewById(R.id.color_preference_widget).setBackgroundColor(
color != null ? ZLAndroidColorUtil.rgb(color) : 0
);
}
@Override
protected void onClick() {
new AmbilWarnaDialog(getContext(), ZLAndroidColorUtil.rgb(getSavedColor()), new AmbilWarnaDialog.OnAmbilWarnaListener() {
@Override
public void onOk(AmbilWarnaDialog dialog, int color) {
if (!callChangeListener(color)) {
return;
}
saveColor(new ZLColor(color));
notifyChanged();
}
@Override
public void onCancel(AmbilWarnaDialog dialog) {
}
}).show();
}
}

View file

@ -24,8 +24,8 @@ import android.preference.CheckBoxPreference;
import org.geometerplus.zlibrary.core.resources.ZLResource;
abstract class ZLCheckBoxPreference extends CheckBoxPreference {
ZLCheckBoxPreference(Context context, ZLResource rootResource, String resourceKey) {
public abstract class ZLCheckBoxPreference extends CheckBoxPreference {
protected ZLCheckBoxPreference(Context context, ZLResource rootResource, String resourceKey) {
super(context);
ZLResource resource = rootResource.getResource(resourceKey);

View file

@ -20,12 +20,6 @@
package org.geometerplus.android.fbreader.preferences;
import android.content.Context;
import android.content.DialogInterface;
import android.graphics.*;
import android.graphics.drawable.*;
import android.preference.DialogPreference;
import android.view.View;
import android.widget.SeekBar;
import org.geometerplus.zlibrary.core.util.ZLColor;
import org.geometerplus.zlibrary.core.options.ZLColorOption;
@ -34,184 +28,27 @@ import org.geometerplus.zlibrary.core.resources.ZLResource;
import org.geometerplus.zlibrary.ui.android.R;
import org.geometerplus.zlibrary.ui.android.util.ZLAndroidColorUtil;
class ZLColorPreference extends DialogPreference {
class ZLColorPreference extends ColorPreference {
private final ZLColorOption myOption;
private SeekBar myRedSlider;
private SeekBar myGreenSlider;
private SeekBar myBlueSlider;
private final GradientDrawable myPreviewDrawable = new GradientDrawable();
private final String myTitle;
ZLColorPreference(Context context, ZLResource resource, String resourceKey, ZLColorOption option) {
super(context, null);
super(context);
myOption = option;
final String title = resource.getResource(resourceKey).getValue();
setTitle(title);
setDialogTitle(title);
setDialogLayoutResource(R.layout.color_dialog);
setWidgetLayoutResource(R.layout.color_preference);
final ZLResource buttonResource = ZLResource.resource("dialog").getResource("button");
setPositiveButtonText(buttonResource.getResource("ok").getValue());
setNegativeButtonText(buttonResource.getResource("cancel").getValue());
myTitle = resource.getResource(resourceKey).getValue();
}
private SeekBar createSlider(View view, int id, int value, String resourceKey) {
final SeekBar slider = (SeekBar)view.findViewById(id);
slider.setProgressDrawable(new SeekBarDrawable(
slider.getProgressDrawable(),
ZLResource.resource("color").getResource(resourceKey).getValue(),
slider
));
slider.setProgress(value);
return slider;
public String getTitle() {
return myTitle;
}
@Override
protected void onBindDialogView(View view) {
final ZLColor color = myOption.getValue();
myRedSlider = createSlider(view, R.id.color_red, color.Red, "red");
myGreenSlider = createSlider(view, R.id.color_green, color.Green, "green");
myBlueSlider = createSlider(view, R.id.color_blue, color.Blue, "blue");
final View colorBox = view.findViewById(R.id.color_box);
colorBox.setBackgroundDrawable(myPreviewDrawable);
myPreviewDrawable.setCornerRadius(7);
myPreviewDrawable.setColor(ZLAndroidColorUtil.rgb(color));
final SeekBar.OnSeekBarChangeListener listener = new SeekBar.OnSeekBarChangeListener() {
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
myPreviewDrawable.setColor(Color.rgb(
myRedSlider.getProgress(),
myGreenSlider.getProgress(),
myBlueSlider.getProgress()
));
myPreviewDrawable.invalidateSelf();
}
public void onStartTrackingTouch(SeekBar seekBar) {
}
public void onStopTrackingTouch(SeekBar seekBar) {
myPreviewDrawable.setColor(Color.rgb(
myRedSlider.getProgress(),
myGreenSlider.getProgress(),
myBlueSlider.getProgress()
));
myPreviewDrawable.invalidateSelf();
}
};
myRedSlider.setOnSeekBarChangeListener(listener);
myGreenSlider.setOnSeekBarChangeListener(listener);
myBlueSlider.setOnSeekBarChangeListener(listener);
super.onBindDialogView(view);
protected ZLColor getSavedColor() {
return myOption.getValue();
}
@Override
public void onClick(DialogInterface dialog, int which) {
if (which == DialogInterface.BUTTON_POSITIVE) {
myOption.setValue(new ZLColor(
myRedSlider.getProgress(),
myGreenSlider.getProgress(),
myBlueSlider.getProgress()
));
}
}
/*
@Override
protected void onBindView(View view) {
final ImageView colorView = (ImageView)view.findViewById(R.id.color_preference_color);
//colorView.setImageResource(R.drawable.fbreader);
final Drawable drawable = new ColorDrawable(0x00FF00);
colorView.setImageDrawable(drawable);
super.onBindView(view);
}
*/
static class SeekBarDrawable extends Drawable {
private final SeekBar mySlider;
private final Drawable myBase;
private final String myText;
private final Paint myPaint;
private final Paint myOutlinePaint;
private boolean myLabelOnRight;
public SeekBarDrawable(Drawable base, String text, SeekBar slider) {
mySlider = slider;
myBase = base;
myText = text;
myPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
myPaint.setTypeface(Typeface.DEFAULT_BOLD);
myPaint.setColor(Color.BLACK);
myPaint.setAlpha(255);
myOutlinePaint = new Paint(myPaint);
myOutlinePaint.setStyle(Paint.Style.STROKE);
myOutlinePaint.setStrokeWidth(3);
myOutlinePaint.setColor(0xFFAAAAAA);
myLabelOnRight = mySlider.getProgress() < 128;
}
@Override
protected void onBoundsChange(Rect bounds) {
myBase.setBounds(bounds);
}
@Override
protected boolean onStateChange(int[] state) {
invalidateSelf();
return false;
}
@Override
public boolean isStateful() {
return true;
}
@Override
protected boolean onLevelChange(int level) {
if (level < 4000) {
myLabelOnRight = true;
} else if (level > 6000) {
myLabelOnRight = false;
}
return myBase.setLevel(level);
}
@Override
public void draw(Canvas canvas) {
myBase.draw(canvas);
final Rect bounds = getBounds();
final int textSize = bounds.height() * 2 / 3;
myPaint.setTextSize(textSize);
myOutlinePaint.setTextSize(textSize);
final Rect textBounds = new Rect();
myPaint.getTextBounds("a", 0, 1, textBounds);
final String text = myText + ": " + mySlider.getProgress();
final float textWidth = myOutlinePaint.measureText(text);
final float x = myLabelOnRight ? bounds.width() - textWidth - 6 : 6;
final float y = bounds.height() / 2 + textBounds.height();
canvas.drawText(text, x, y, myOutlinePaint);
canvas.drawText(text, x, y, myPaint);
}
@Override
public int getOpacity() {
return PixelFormat.TRANSLUCENT;
}
@Override
public void setAlpha(int alpha) {
}
@Override
public void setColorFilter(ColorFilter cf) {
}
protected void saveColor(ZLColor color) {
myOption.setValue(color);
}
}

View file

@ -24,10 +24,10 @@ import android.preference.EditTextPreference;
import org.geometerplus.zlibrary.core.resources.ZLResource;
abstract class ZLStringPreference extends EditTextPreference {
public abstract class ZLStringPreference extends EditTextPreference {
private String myValue;
ZLStringPreference(Context context, ZLResource rootResource, String resourceKey) {
protected ZLStringPreference(Context context, ZLResource rootResource, String resourceKey) {
super(context);
ZLResource resource = rootResource.getResource(resourceKey);

View file

@ -0,0 +1,137 @@
/*
* Copyright (C) 2010-2013 Geometer Plus <contact@geometerplus.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
package org.geometerplus.android.fbreader.style;
import android.content.Context;
import android.os.Bundle;
import android.preference.PreferenceActivity;
import android.preference.PreferenceScreen;
import android.view.Window;
import org.geometerplus.zlibrary.core.resources.ZLResource;
import org.geometerplus.zlibrary.core.util.ZLColor;
import org.geometerplus.fbreader.book.HighlightingStyle;
import org.geometerplus.android.fbreader.libraryService.BookCollectionShadow;
import org.geometerplus.android.fbreader.preferences.*;
public class EditStyleActivity extends PreferenceActivity {
static final String STYLE_ID_KEY = "style.id";
private final ZLResource myRootResource = ZLResource.resource("editStyle");
private final BookCollectionShadow myCollection = new BookCollectionShadow();
private HighlightingStyle myStyle;
private BgColorPreference myBgColorPreference;
@Override
protected void onCreate(Bundle bundle) {
requestWindowFeature(Window.FEATURE_NO_TITLE);
super.onCreate(bundle);
Thread.setDefaultUncaughtExceptionHandler(new org.geometerplus.zlibrary.ui.android.library.UncaughtExceptionHandler(this));
final PreferenceScreen screen = getPreferenceManager().createPreferenceScreen(this);
setPreferenceScreen(screen);
myCollection.bindToService(this, new Runnable() {
public void run() {
myStyle = myCollection.getHighlightingStyle(getIntent().getIntExtra(STYLE_ID_KEY, -1));
if (myStyle == null) {
finish();
return;
}
screen.addPreference(new NamePreference());
screen.addPreference(new InvisiblePreference());
myBgColorPreference = new BgColorPreference();
screen.addPreference(myBgColorPreference);
}
});
}
@Override
protected void onDestroy() {
myCollection.unbind();
super.onDestroy();
}
private class NamePreference extends ZLStringPreference {
NamePreference() {
super(EditStyleActivity.this, myRootResource, "name");
super.setValue(myStyle.getName());
}
@Override
protected void setValue(String value) {
super.setValue(value);
myStyle.setName(value);
myCollection.saveHighlightingStyle(myStyle);
}
}
private class InvisiblePreference extends ZLCheckBoxPreference {
private ZLColor mySavedBgColor;
InvisiblePreference() {
super(EditStyleActivity.this, myRootResource, "invisible");
setChecked(myStyle.getBackgroundColor() == null);
}
@Override
protected void onClick() {
super.onClick();
if (isChecked()) {
mySavedBgColor = myStyle.getBackgroundColor();
myStyle.setBackgroundColor(null);
myBgColorPreference.setEnabled(false);
} else {
myStyle.setBackgroundColor(
mySavedBgColor != null ? mySavedBgColor : new ZLColor(127, 127, 127)
);
myBgColorPreference.setEnabled(true);
}
myCollection.saveHighlightingStyle(myStyle);
}
}
private class BgColorPreference extends ColorPreference {
BgColorPreference() {
super(EditStyleActivity.this);
setEnabled(getSavedColor() != null);
}
@Override
public String getTitle() {
return myRootResource.getResource("bgColor").getValue();
}
@Override
protected ZLColor getSavedColor() {
return myStyle.getBackgroundColor();
}
@Override
protected void saveColor(ZLColor color) {
myStyle.setBackgroundColor(color);
myCollection.saveHighlightingStyle(myStyle);
}
}
}

View file

@ -17,7 +17,7 @@
* 02110-1301, USA.
*/
package org.geometerplus.android.fbreader;
package org.geometerplus.android.fbreader.style;
import java.util.ArrayList;
import java.util.List;
@ -30,16 +30,20 @@ import android.graphics.drawable.ColorDrawable;
import android.widget.*;
import android.view.*;
import yuku.ambilwarna.widget.AmbilWarnaPrefWidgetView;
import org.geometerplus.zlibrary.core.resources.ZLResource;
import org.geometerplus.zlibrary.core.util.ZLColor;
import org.geometerplus.zlibrary.ui.android.R;
import org.geometerplus.zlibrary.ui.android.util.ZLAndroidColorUtil;
import org.geometerplus.fbreader.book.*;
import org.geometerplus.android.fbreader.FBReader;
import org.geometerplus.android.fbreader.libraryService.BookCollectionShadow;
public class StyleListActivity extends ListActivity {
static final String EXISTING_BOOKMARK_KEY = "existing.bookmark";
public class StyleListActivity extends ListActivity implements IBookCollection.Listener {
public static final String EXISTING_BOOKMARK_KEY = "existing.bookmark";
private final BookCollectionShadow myCollection = new BookCollectionShadow();
private boolean myExistingBookmark;
@ -72,6 +76,7 @@ public class StyleListActivity extends ListActivity {
final ActionListAdapter adapter = new ActionListAdapter(styles);
setListAdapter(adapter);
getListView().setOnItemClickListener(adapter);
myCollection.addListener(StyleListActivity.this);
}
});
}
@ -82,6 +87,17 @@ public class StyleListActivity extends ListActivity {
super.onDestroy();
}
// method from IBookCollection.Listener
public void onBookEvent(BookEvent event, Book book) {
if (event == BookEvent.BookmarkStyleChanged) {
((ActionListAdapter)getListAdapter()).setStyleList(myCollection.highlightingStyles());
}
}
// method from IBookCollection.Listener
public void onBuildEvent(IBookCollection.Status status) {
}
private class ActionListAdapter extends BaseAdapter implements AdapterView.OnItemClickListener {
private final List<HighlightingStyle> myStyles;
@ -89,11 +105,17 @@ public class StyleListActivity extends ListActivity {
myStyles = new ArrayList<HighlightingStyle>(styles);
}
public final int getCount() {
public synchronized void setStyleList(List<HighlightingStyle> styles) {
myStyles.clear();
myStyles.addAll(styles);
notifyDataSetChanged();
}
public final synchronized int getCount() {
return myExistingBookmark ? myStyles.size() + 1 : myStyles.size();
}
public final HighlightingStyle getItem(int position) {
public final synchronized HighlightingStyle getItem(int position) {
return position < myStyles.size() ? myStyles.get(position) : null;
}
@ -101,27 +123,54 @@ public class StyleListActivity extends ListActivity {
return position;
}
public View getView(int position, View convertView, final ViewGroup parent) {
public final synchronized View getView(int position, View convertView, final ViewGroup parent) {
final View view = convertView != null
? convertView
: LayoutInflater.from(parent.getContext()).inflate(R.layout.style_item, parent, false);
final HighlightingStyle style = getItem(position);
final ImageView colorView = (ImageView)view.findViewById(R.id.style_item_color);
final AmbilWarnaPrefWidgetView colorView = (AmbilWarnaPrefWidgetView)view.findViewById(R.id.style_item_color);
final TextView titleView = (TextView)view.findViewById(R.id.style_item_title);
final Button button = (Button)view.findViewById(R.id.style_item_edit_button);
final ZLResource resource = ZLResource.resource("highlightingStyleMenu");
if (style != null) {
colorView.setVisibility(View.VISIBLE);
colorView.setImageDrawable(new ColorDrawable(ZLAndroidColorUtil.rgb(style.BackgroundColor)));
titleView.setText(
ZLResource.resource("highlightingStyleMenu")
String name = style.getName();
if (name == null || "".equals(name)) {
name = resource
.getResource("style").getValue()
.replace("%s", String.valueOf(style.Id))
);
.replace("%s", String.valueOf(style.Id));
}
final ZLColor color = style.getBackgroundColor();
final int rgb = color != null ? ZLAndroidColorUtil.rgb(color) : -1;
colorView.setVisibility(View.VISIBLE);
if (rgb != -1) {
colorView.showCross(false);
colorView.setBackgroundColor(rgb);
} else {
colorView.showCross(true);
colorView.setBackgroundColor(0);
}
titleView.setText(name);
button.setVisibility(View.VISIBLE);
button.setText(resource.getResource("editStyle").getValue());
button.setOnClickListener(new Button.OnClickListener() {
@Override
public void onClick(View view) {
startActivity(
new Intent(StyleListActivity.this, EditStyleActivity.class)
.putExtra(EditStyleActivity.STYLE_ID_KEY, style.Id)
);
}
});
} else {
colorView.setVisibility(View.GONE);
button.setVisibility(View.GONE);
titleView.setText(
ZLResource.resource("highlightingStyleMenu")
resource
.getResource("deleteBookmark").getValue()
);
}
@ -129,7 +178,7 @@ public class StyleListActivity extends ListActivity {
return view;
}
public final void onItemClick(AdapterView<?> parent, View view, int position, long id) {
public final synchronized void onItemClick(AdapterView<?> parent, View view, int position, long id) {
final HighlightingStyle style = getItem(position);
myCollection.bindToService(StyleListActivity.this, new Runnable() {
public void run() {

View file

@ -674,4 +674,10 @@ public class BookCollection extends AbstractBookCollection {
initStylesTable();
return new ArrayList<HighlightingStyle>(myStyles.values());
}
public void saveHighlightingStyle(HighlightingStyle style) {
myStyles.put(style.Id, style);
myDatabase.saveStyle(style);
fireBookEvent(BookEvent.BookmarkStyleChanged, null);
}
}

View file

@ -23,5 +23,6 @@ public enum BookEvent {
Added,
Updated,
Removed,
BookmarksUpdated
BookmarksUpdated,
BookmarkStyleChanged,
}

View file

@ -111,10 +111,11 @@ public abstract class BooksDatabase {
protected abstract long saveBookmark(Bookmark bookmark);
protected abstract void deleteBookmark(Bookmark bookmark);
protected HighlightingStyle createStyle(int id, int color) {
return new HighlightingStyle(id, new ZLColor(color));
protected HighlightingStyle createStyle(int id, String name, int color) {
return new HighlightingStyle(id, name, color != -1 ? new ZLColor(color) : null);
}
protected abstract List<HighlightingStyle> loadStyles();
protected abstract void saveStyle(HighlightingStyle style);
protected abstract ZLTextPosition getStoredPosition(long bookId);
protected abstract void storePosition(long bookId, ZLTextPosition position);

View file

@ -22,14 +22,41 @@ package org.geometerplus.fbreader.book;
import java.util.Map;
import java.util.HashMap;
import org.geometerplus.zlibrary.core.resources.ZLResource;
import org.geometerplus.zlibrary.core.util.ZLColor;
public class HighlightingStyle {
public final int Id;
public final ZLColor BackgroundColor;
HighlightingStyle(int id, ZLColor bgColor) {
private String myName;
private ZLColor myBackgroundColor;
HighlightingStyle(int id, String name, ZLColor bgColor) {
Id = id;
BackgroundColor = bgColor;
myName = name;
myBackgroundColor = bgColor;
}
private String defaultName() {
return ZLResource.resource("style").getValue().replace("%s", String.valueOf(Id));
}
public String getName() {
if (myName == null || "".equals(myName)) {
return defaultName();
}
return myName;
}
public void setName(String name) {
myName = defaultName().equals(name) ? "" : name;
}
public ZLColor getBackgroundColor() {
return myBackgroundColor;
}
public void setBackgroundColor(ZLColor bgColor) {
myBackgroundColor = bgColor;
}
}

View file

@ -85,4 +85,5 @@ public interface IBookCollection {
HighlightingStyle getHighlightingStyle(int styleId);
List<HighlightingStyle> highlightingStyles();
void saveHighlightingStyle(HighlightingStyle style);
}

View file

@ -309,13 +309,12 @@ class XMLSerializer extends AbstractSerializer {
@Override
public String serialize(HighlightingStyle style) {
final StringBuilder buffer = new StringBuilder();
appendTag(buffer, "style", false,
"id", String.valueOf(style.Id)
final ZLColor bgColor = style.getBackgroundColor();
appendTag(buffer, "style", true,
"id", String.valueOf(style.Id),
"name", style.getName(),
"bg-color", bgColor != null ? String.valueOf(bgColor.intValue()) : "-1"
);
appendTag(buffer, "bg-color", true,
"value", String.valueOf(style.BackgroundColor.getIntValue())
);
closeTag(buffer, "style");
return buffer.toString();
}
@ -961,9 +960,6 @@ class XMLSerializer extends AbstractSerializer {
private static final class StyleDeserializer extends DefaultHandler {
private HighlightingStyle myStyle;
private int myId = -1;
private int myColor;
public HighlightingStyle getStyle() {
return myStyle;
}
@ -971,29 +967,22 @@ class XMLSerializer extends AbstractSerializer {
@Override
public void startDocument() {
myStyle = null;
myId = -1;
}
@Override
public void endDocument() {
if (myId != -1) {
myStyle = new HighlightingStyle(myId, new ZLColor(myColor));
}
}
@Override
public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
if ("style".equals(localName)) {
try {
myId = Integer.parseInt(attributes.getValue("id"));
final int id = Integer.parseInt(attributes.getValue("id"));
if (id != -1) {
final int rgb = Integer.parseInt(attributes.getValue("bg-color"));
final ZLColor color = rgb != -1 ? new ZLColor(rgb) : null;
myStyle = new HighlightingStyle(
id, attributes.getValue("name"), color
);
}
} catch (Exception e) {
throw new SAXException("XML parsing error", e);
}
} else if ("bg-color".equals(localName)) {
try {
myColor = Integer.parseInt(attributes.getValue("value"));
} catch (Exception e) {
throw new SAXException("XML parsing error", e);
// ignore
}
}
}

View file

@ -51,6 +51,6 @@ public final class BookmarkHighlighting extends ZLTextSimpleHighlighting {
@Override
public ZLColor getBackgroundColor() {
final HighlightingStyle bmStyle = Collection.getHighlightingStyle(Bookmark.getStyleId());
return bmStyle != null ? bmStyle.BackgroundColor : new ZLColor(255, 255, 255);
return bmStyle != null ? bmStyle.getBackgroundColor() : null;
}
}

View file

@ -118,8 +118,9 @@ public final class FBReaderApp extends ZLApplication {
collection.addListener(new IBookCollection.Listener() {
public void onBookEvent(BookEvent event, Book book) {
switch (event) {
case BookmarkStyleChanged:
case BookmarksUpdated:
if (Model != null && book.equals(Model.Book)) {
if (Model != null && (book == null || book.equals(Model.Book))) {
if (BookTextView.getModel() != null) {
setBookmarkHighlightings(BookTextView, null);
}

View file

@ -37,7 +37,7 @@ public final class ZLColorOption extends ZLOption {
if (value != null) {
try {
int intValue = Integer.parseInt(value);
if (myValue.getIntValue() != intValue) {
if (myValue.intValue() != intValue) {
myValue = new ZLColor(intValue);
}
} catch (NumberFormatException e) {
@ -61,7 +61,7 @@ public final class ZLColorOption extends ZLOption {
if (colorValue.equals(myDefaultValue)) {
unsetConfigValue();
} else {
setConfigValue("" + colorValue.getIntValue());
setConfigValue(String.valueOf(colorValue.intValue()));
}
}
}

View file

@ -40,7 +40,7 @@ public final class ZLColor {
Blue = (short)(intValue & 0xFF);
}
public int getIntValue() {
public int intValue() {
return (Red << 16) + (Green << 8) + Blue;
}
@ -60,7 +60,7 @@ public final class ZLColor {
@Override
public int hashCode() {
return getIntValue();
return intValue();
}
@Override

View file

@ -837,6 +837,10 @@ public abstract class ZLTextView extends ZLTextViewBase {
final ZLTextElementArea fromArea = page.TextElementMap.get(from);
final ZLTextElementArea toArea = page.TextElementMap.get(to - 1);
for (ZLTextHighlighting h : hilites) {
final ZLColor bgColor = h.getBackgroundColor();
if (bgColor == null) {
continue;
}
final ZLTextElementArea selectionStartArea = h.getStartArea(page);
if (selectionStartArea == null || selectionStartArea.compareTo(toArea) > 0) {
continue;
@ -859,7 +863,7 @@ public abstract class ZLTextView extends ZLTextViewBase {
} else {
right = selectionEndArea.XEnd;
}
getContext().setFillColor(h.getBackgroundColor());
getContext().setFillColor(bgColor);
getContext().fillRectangle(left, top, right, bottom);
}
}
@ -1587,7 +1591,7 @@ public abstract class ZLTextView extends ZLTextViewBase {
}
synchronized (myHighlightings) {
for (ZLTextHighlighting h : myHighlightings) {
if (h.intersects(region)) {
if (h.getBackgroundColor() != null && h.intersects(region)) {
return h;
}
}

30
third-party/AmbilWarna/AmbilWarna.iml vendored Normal file
View file

@ -0,0 +1,30 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="JAVA_MODULE" version="4">
<component name="EclipseModuleManager" forced_jdk="true">
<conelement value="com.android.ide.eclipse.adt.DEPENDENCIES" />
<src_description expected_position="1">
<src_folder value="file://$MODULE_DIR$/src" expected_position="0" />
<src_folder value="file://$MODULE_DIR$/gen" expected_position="1" />
<src_folder value="com.android.ide.eclipse.adt.ANDROID_FRAMEWORK" expected_position="2" />
<src_folder value="com.android.ide.eclipse.adt.LIBRARIES" expected_position="3" />
</src_description>
</component>
<component name="FacetManager">
<facet type="android" name="Android">
<configuration>
<option name="LIBRARY_PROJECT" value="true" />
</configuration>
</facet>
</component>
<component name="NewModuleRootManager" inherit-compiler-output="false">
<output url="file://$MODULE_DIR$/bin/classes" />
<exclude-output />
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/gen" isTestSource="false" />
</content>
<orderEntry type="jdk" jdkName="Android 4.2.2 Platform" jdkType="Android SDK" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>

View file

@ -0,0 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest
xmlns:android="http://schemas.android.com/apk/res/android"
package="yuku.ambilwarna"
android:versionCode="1"
android:versionName="1.0">
<application>
</application>
<uses-sdk
android:minSdkVersion="3" />
</manifest>

92
third-party/AmbilWarna/build.xml vendored Normal file
View file

@ -0,0 +1,92 @@
<?xml version="1.0" encoding="UTF-8"?>
<project name="AmbilWarna" default="help">
<!-- The local.properties file is created and updated by the 'android' tool.
It contains the path to the SDK. It should *NOT* be checked into
Version Control Systems. -->
<property file="local.properties" />
<!-- The ant.properties file can be created by you. It is only edited by the
'android' tool to add properties to it.
This is the place to change some Ant specific build properties.
Here are some properties you may want to change/update:
source.dir
The name of the source directory. Default is 'src'.
out.dir
The name of the output directory. Default is 'bin'.
For other overridable properties, look at the beginning of the rules
files in the SDK, at tools/ant/build.xml
Properties related to the SDK location or the project target should
be updated using the 'android' tool with the 'update' action.
This file is an integral part of the build system for your
application and should be checked into Version Control Systems.
-->
<property file="ant.properties" />
<!-- if sdk.dir was not set from one of the property file, then
get it from the ANDROID_HOME env var.
This must be done before we load project.properties since
the proguard config can use sdk.dir -->
<property environment="env" />
<condition property="sdk.dir" value="${env.ANDROID_HOME}">
<isset property="env.ANDROID_HOME" />
</condition>
<!-- The project.properties file is created and updated by the 'android'
tool, as well as ADT.
This contains project specific properties such as project target, and library
dependencies. Lower level build properties are stored in ant.properties
(or in .classpath for Eclipse projects).
This file is an integral part of the build system for your
application and should be checked into Version Control Systems. -->
<loadproperties srcFile="project.properties" />
<!-- quick check on sdk.dir -->
<fail
message="sdk.dir is missing. Make sure to generate local.properties using 'android update project' or to inject it through the ANDROID_HOME environment variable."
unless="sdk.dir"
/>
<!--
Import per project custom build rules if present at the root of the project.
This is the place to put custom intermediary targets such as:
-pre-build
-pre-compile
-post-compile (This is typically used for code obfuscation.
Compiled code location: ${out.classes.absolute.dir}
If this is not done in place, override ${out.dex.input.absolute.dir})
-post-package
-post-build
-pre-clean
-->
<import file="custom_rules.xml" optional="true" />
<!-- Import the actual build file.
To customize existing targets, there are two options:
- Customize only one target:
- copy/paste the target into this file, *before* the
<import> task.
- customize it to your needs.
- Customize the whole content of build.xml
- copy/paste the content of the rules files (minus the top node)
into this file, replacing the <import> task.
- customize to your needs.
***********************
****** IMPORTANT ******
***********************
In all cases you must update the value of version-tag below to read 'custom' instead of an integer,
in order to avoid having your file be overridden by tools such as "android update project"
-->
<!-- version-tag: 1 -->
<import file="${sdk.dir}/tools/ant/build.xml" />
</project>

View file

@ -0,0 +1,14 @@
# 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
# Indicates whether an apk should be generated for each density.
split.density=false
# Project target.
target=android-11

Binary file not shown.

After

Width:  |  Height:  |  Size: 1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 666 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 983 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 748 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 751 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 480 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 607 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 782 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 889 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 814 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 579 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 455 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 816 B

View file

@ -0,0 +1,85 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/ambilwarna_viewContainer"
android:paddingTop="@dimen/ambilwarna_spacer"
android:paddingRight="@dimen/ambilwarna_spacer"
android:paddingBottom="0dp"
android:paddingLeft="@dimen/ambilwarna_spacer"
android:clipToPadding="false"
android:layout_gravity="center"
>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/ambilwarna_state"
android:orientation="vertical"
android:paddingRight="@dimen/ambilwarna_spacer"
android:gravity="center"
android:layout_centerVertical="true"
>
<View
android:layout_width="60dp"
android:layout_height="30dp"
android:id="@+id/ambilwarna_warnaLama"
android:background="#faa"
/>
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/ambilwarna_arrow_down"
android:paddingTop="@dimen/ambilwarna_spacer"
android:paddingBottom="@dimen/ambilwarna_spacer"
/>
<View
android:layout_width="60dp"
android:layout_height="30dp"
android:id="@+id/ambilwarna_warnaBaru"
android:background="#aaf"
/>
</LinearLayout>
<yuku.ambilwarna.AmbilWarnaKotak
android:id="@+id/ambilwarna_viewSatBri"
android:layout_width="@dimen/ambilwarna_hsvWidth"
android:layout_height="@dimen/ambilwarna_hsvHeight"
android:layout_toRightOf="@id/ambilwarna_state"
android:layerType="software"
/>
<!-- needed because i can't get parent keeping its bottom layout
and its wrap_content height. This view will serve as a bottom spacer. -->
<View
android:layout_width="@dimen/ambilwarna_spacer"
android:layout_height="@dimen/ambilwarna_spacer"
android:layout_below="@id/ambilwarna_viewSatBri"
/>
<ImageView
android:id="@+id/ambilwarna_viewHue"
android:layout_width="@dimen/ambilwarna_hueWidth"
android:layout_height="@dimen/ambilwarna_hsvHeight"
android:layout_toRightOf="@id/ambilwarna_viewSatBri"
android:layout_marginLeft="@dimen/ambilwarna_spacer"
android:src="@drawable/ambilwarna_hue"
android:scaleType="fitXY"
/>
<ImageView
android:id="@+id/ambilwarna_cursor"
android:layout_width="9dp"
android:layout_height="9dp"
android:src="@drawable/ambilwarna_cursor"
android:scaleType="matrix"
/>
<ImageView
android:id="@+id/ambilwarna_target"
android:layout_width="15dp"
android:layout_height="15dp"
android:src="@drawable/ambilwarna_target"
android:scaleType="matrix"
/>
</RelativeLayout>

View file

@ -0,0 +1,78 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/ambilwarna_dialogView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center" >
<RelativeLayout
android:id="@+id/ambilwarna_viewContainer"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:clipToPadding="false"
android:paddingBottom="@dimen/ambilwarna_spacer"
android:paddingLeft="@dimen/ambilwarna_spacer"
android:paddingRight="@dimen/ambilwarna_spacer"
android:paddingTop="@dimen/ambilwarna_spacer" android:layout_gravity="center">
<yuku.ambilwarna.AmbilWarnaKotak
android:id="@+id/ambilwarna_viewSatBri"
android:layout_width="@dimen/ambilwarna_hsvWidth"
android:layout_height="@dimen/ambilwarna_hsvHeight"
android:layerType="software" />
<ImageView
android:id="@+id/ambilwarna_viewHue"
android:layout_width="@dimen/ambilwarna_hueWidth"
android:layout_height="@dimen/ambilwarna_hsvHeight"
android:layout_marginLeft="@dimen/ambilwarna_spacer"
android:layout_toRightOf="@id/ambilwarna_viewSatBri"
android:scaleType="fitXY"
android:src="@drawable/ambilwarna_hue" />
<ImageView
android:id="@+id/ambilwarna_cursor"
android:layout_width="9dp"
android:layout_height="9dp"
android:scaleType="matrix"
android:src="@drawable/ambilwarna_cursor" />
<ImageView
android:id="@+id/ambilwarna_target"
android:layout_width="15dp"
android:layout_height="15dp"
android:scaleType="matrix"
android:src="@drawable/ambilwarna_target" />
<LinearLayout
android:id="@+id/ambilwarna_state"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/ambilwarna_viewSatBri"
android:layout_centerHorizontal="true"
android:layout_marginTop="@dimen/ambilwarna_spacer"
android:gravity="center"
android:orientation="horizontal" >
<View
android:id="@+id/ambilwarna_warnaLama"
android:layout_width="60dp"
android:layout_height="30dp"
android:background="#faa" />
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingLeft="@dimen/ambilwarna_spacer"
android:paddingRight="@dimen/ambilwarna_spacer"
android:src="@drawable/ambilwarna_arrow_right" />
<View
android:id="@+id/ambilwarna_warnaBaru"
android:layout_width="60dp"
android:layout_height="30dp"
android:background="#aaf" />
</LinearLayout>
</RelativeLayout>
</FrameLayout>

View file

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<yuku.ambilwarna.widget.AmbilWarnaPrefWidgetView
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/ambilwarna_pref_widget_kotak"
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_gravity="center_vertical"
android:layout_marginRight="6dp"
android:focusable="false"
android:clickable="false" />

View file

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<dimen name="ambilwarna_hsvWidth">240dp</dimen>
<dimen name="ambilwarna_hsvHeight">120dp</dimen>
</resources>

View file

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<dimen name="ambilwarna_hsvWidth">240dp</dimen>
<dimen name="ambilwarna_hsvHeight">240dp</dimen>
</resources>

View file

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<dimen name="ambilwarna_hsvHeight">240dp</dimen>
<dimen name="ambilwarna_hsvWidth">240dp</dimen>
<dimen name="ambilwarna_hueWidth">30dp</dimen>
<dimen name="ambilwarna_spacer">8dp</dimen>
</resources>

View file

@ -0,0 +1,198 @@
package yuku.ambilwarna;
import android.app.*;
import android.content.*;
import android.content.DialogInterface.OnCancelListener;
import android.graphics.*;
import android.view.*;
import android.widget.*;
public class AmbilWarnaDialog {
public interface OnAmbilWarnaListener {
void onCancel(AmbilWarnaDialog dialog);
void onOk(AmbilWarnaDialog dialog, int color);
}
final AlertDialog dialog;
final OnAmbilWarnaListener listener;
final View viewHue;
final AmbilWarnaKotak viewSatVal;
final ImageView viewCursor;
final View viewOldColor;
final View viewNewColor;
final ImageView viewTarget;
final ViewGroup viewContainer;
final float[] currentColorHsv = new float[3];
/**
* create an AmbilWarnaDialog. call this only from OnCreateDialog() or from a background thread.
*
* @param context
* current context
* @param color
* current color
* @param listener
* an OnAmbilWarnaListener, allowing you to get back error or
*/
public AmbilWarnaDialog(final Context context, int color, OnAmbilWarnaListener listener) {
this.listener = listener;
Color.colorToHSV(color, currentColorHsv);
final View view = LayoutInflater.from(context).inflate(R.layout.ambilwarna_dialog, null);
viewHue = view.findViewById(R.id.ambilwarna_viewHue);
viewSatVal = (AmbilWarnaKotak) view.findViewById(R.id.ambilwarna_viewSatBri);
viewCursor = (ImageView) view.findViewById(R.id.ambilwarna_cursor);
viewOldColor = view.findViewById(R.id.ambilwarna_warnaLama);
viewNewColor = view.findViewById(R.id.ambilwarna_warnaBaru);
viewTarget = (ImageView) view.findViewById(R.id.ambilwarna_target);
viewContainer = (ViewGroup) view.findViewById(R.id.ambilwarna_viewContainer);
viewSatVal.setHue(getHue());
viewOldColor.setBackgroundColor(color);
viewNewColor.setBackgroundColor(color);
viewHue.setOnTouchListener(new View.OnTouchListener() {
@Override public boolean onTouch(View v, MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_MOVE
|| event.getAction() == MotionEvent.ACTION_DOWN
|| event.getAction() == MotionEvent.ACTION_UP) {
float y = event.getY();
if (y < 0.f) y = 0.f;
if (y > viewHue.getMeasuredHeight()) y = viewHue.getMeasuredHeight() - 0.001f; // to avoid looping from end to start.
float hue = 360.f - 360.f / viewHue.getMeasuredHeight() * y;
if (hue == 360.f) hue = 0.f;
setHue(hue);
// update view
viewSatVal.setHue(getHue());
moveCursor();
viewNewColor.setBackgroundColor(getColor());
return true;
}
return false;
}
});
viewSatVal.setOnTouchListener(new View.OnTouchListener() {
@Override public boolean onTouch(View v, MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_MOVE
|| event.getAction() == MotionEvent.ACTION_DOWN
|| event.getAction() == MotionEvent.ACTION_UP) {
float x = event.getX(); // touch event are in dp units.
float y = event.getY();
if (x < 0.f) x = 0.f;
if (x > viewSatVal.getMeasuredWidth()) x = viewSatVal.getMeasuredWidth();
if (y < 0.f) y = 0.f;
if (y > viewSatVal.getMeasuredHeight()) y = viewSatVal.getMeasuredHeight();
setSat(1.f / viewSatVal.getMeasuredWidth() * x);
setVal(1.f - (1.f / viewSatVal.getMeasuredHeight() * y));
// update view
moveTarget();
viewNewColor.setBackgroundColor(getColor());
return true;
}
return false;
}
});
dialog = new AlertDialog.Builder(context)
.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
@Override public void onClick(DialogInterface dialog, int which) {
if (AmbilWarnaDialog.this.listener != null) {
AmbilWarnaDialog.this.listener.onOk(AmbilWarnaDialog.this, getColor());
}
}
})
.setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() {
@Override public void onClick(DialogInterface dialog, int which) {
if (AmbilWarnaDialog.this.listener != null) {
AmbilWarnaDialog.this.listener.onCancel(AmbilWarnaDialog.this);
}
}
})
.setOnCancelListener(new OnCancelListener() {
// if back button is used, call back our listener.
@Override public void onCancel(DialogInterface paramDialogInterface) {
if (AmbilWarnaDialog.this.listener != null) {
AmbilWarnaDialog.this.listener.onCancel(AmbilWarnaDialog.this);
}
}
})
.create();
// kill all padding from the dialog window
dialog.setView(view, 0, 0, 0, 0);
// move cursor & target on first draw
ViewTreeObserver vto = view.getViewTreeObserver();
vto.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
@Override public void onGlobalLayout() {
moveCursor();
moveTarget();
view.getViewTreeObserver().removeGlobalOnLayoutListener(this);
}
});
}
protected void moveCursor() {
float y = viewHue.getMeasuredHeight() - (getHue() * viewHue.getMeasuredHeight() / 360.f);
if (y == viewHue.getMeasuredHeight()) y = 0.f;
RelativeLayout.LayoutParams layoutParams = (RelativeLayout.LayoutParams) viewCursor.getLayoutParams();
layoutParams.leftMargin = (int) (viewHue.getLeft() - Math.floor(viewCursor.getMeasuredWidth() / 2) - viewContainer.getPaddingLeft());
;
layoutParams.topMargin = (int) (viewHue.getTop() + y - Math.floor(viewCursor.getMeasuredHeight() / 2) - viewContainer.getPaddingTop());
;
viewCursor.setLayoutParams(layoutParams);
}
protected void moveTarget() {
float x = getSat() * viewSatVal.getMeasuredWidth();
float y = (1.f - getVal()) * viewSatVal.getMeasuredHeight();
RelativeLayout.LayoutParams layoutParams = (RelativeLayout.LayoutParams) viewTarget.getLayoutParams();
layoutParams.leftMargin = (int) (viewSatVal.getLeft() + x - Math.floor(viewTarget.getMeasuredWidth() / 2) - viewContainer.getPaddingLeft());
layoutParams.topMargin = (int) (viewSatVal.getTop() + y - Math.floor(viewTarget.getMeasuredHeight() / 2) - viewContainer.getPaddingTop());
viewTarget.setLayoutParams(layoutParams);
}
private int getColor() {
return Color.HSVToColor(currentColorHsv);
}
private float getHue() {
return currentColorHsv[0];
}
private float getSat() {
return currentColorHsv[1];
}
private float getVal() {
return currentColorHsv[2];
}
private void setHue(float hue) {
currentColorHsv[0] = hue;
}
private void setSat(float sat) {
currentColorHsv[1] = sat;
}
private void setVal(float val) {
currentColorHsv[2] = val;
}
public void show() {
dialog.show();
}
public AlertDialog getDialog() {
return dialog;
}
}

View file

@ -0,0 +1,46 @@
package yuku.ambilwarna;
import android.annotation.SuppressLint;
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.PorterDuff;
import android.graphics.Shader;
import android.graphics.Shader.TileMode;
import android.util.AttributeSet;
import android.view.View;
public class AmbilWarnaKotak extends View {
Paint paint;
Shader luar;
final float[] color = { 1.f, 1.f, 1.f };
public AmbilWarnaKotak(Context context, AttributeSet attrs) {
super(context, attrs);
}
public AmbilWarnaKotak(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
@SuppressLint("DrawAllocation") @Override protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (paint == null) {
paint = new Paint();
luar = new LinearGradient(0.f, 0.f, 0.f, this.getMeasuredHeight(), 0xffffffff, 0xff000000, TileMode.CLAMP);
}
int rgb = Color.HSVToColor(color);
Shader dalam = new LinearGradient(0.f, 0.f, this.getMeasuredWidth(), 0.f, 0xffffffff, rgb, TileMode.CLAMP);
ComposeShader shader = new ComposeShader(luar, dalam, PorterDuff.Mode.MULTIPLY);
paint.setShader(shader);
canvas.drawRect(0.f, 0.f, this.getMeasuredWidth(), this.getMeasuredHeight(), paint);
}
void setHue(float hue) {
color[0] = hue;
invalidate();
}
}

View file

@ -0,0 +1,45 @@
package yuku.ambilwarna.widget;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Paint.Style;
import android.util.AttributeSet;
import android.util.FloatMath;
import android.view.View;
public class AmbilWarnaPrefWidgetView extends View {
Paint paint;
float rectSize;
float strokeWidth;
boolean drawCross;
public AmbilWarnaPrefWidgetView(Context context, AttributeSet attrs) {
super(context, attrs);
float density = context.getResources().getDisplayMetrics().density;
rectSize = FloatMath.floor(24.f * density + 0.5f);
strokeWidth = FloatMath.floor(1.f * density + 0.5f);
paint = new Paint();
paint.setColor(0xffffffff);
paint.setStyle(Style.STROKE);
paint.setStrokeWidth(strokeWidth);
}
public void showCross(boolean show) {
drawCross = show;
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawRect(strokeWidth, strokeWidth, rectSize - strokeWidth, rectSize - strokeWidth, paint);
if (drawCross) {
canvas.drawLine(strokeWidth, strokeWidth, rectSize - strokeWidth, rectSize - strokeWidth, paint);
canvas.drawLine(strokeWidth, rectSize - strokeWidth, rectSize - strokeWidth, strokeWidth, paint);
}
}
}

View file

@ -0,0 +1,129 @@
package yuku.ambilwarna.widget;
import android.content.Context;
import android.content.res.TypedArray;
import android.os.Parcel;
import android.os.Parcelable;
import android.preference.Preference;
import android.util.AttributeSet;
import android.view.View;
import yuku.ambilwarna.AmbilWarnaDialog;
import yuku.ambilwarna.R;
public class AmbilWarnaPreference extends Preference {
int value;
public AmbilWarnaPreference(Context context, AttributeSet attrs) {
super(context, attrs);
setWidgetLayoutResource(R.layout.ambilwarna_pref_widget);
}
@Override protected void onBindView(View view) {
super.onBindView(view);
// Set our custom views inside the layout
final View kotak = view.findViewById(R.id.ambilwarna_pref_widget_kotak);
if (kotak != null) {
kotak.setBackgroundColor(value);
}
}
@Override protected void onClick() {
new AmbilWarnaDialog(getContext(), value, new AmbilWarnaDialog.OnAmbilWarnaListener() {
@Override public void onOk(AmbilWarnaDialog dialog, int color) {
if (!callChangeListener(color)) return; // They don't want the value to be set
value = color;
persistInt(value);
notifyChanged();
}
@Override public void onCancel(AmbilWarnaDialog dialog) {
// nothing to do
}
}).show();
}
public void forceSetValue(int value) {
this.value = value;
persistInt(value);
notifyChanged();
}
@Override protected Object onGetDefaultValue(TypedArray a, int index) {
// This preference type's value type is Integer, so we read the default value from the attributes as an Integer.
return a.getInteger(index, 0);
}
@Override protected void onSetInitialValue(boolean restoreValue, Object defaultValue) {
if (restoreValue) { // Restore state
value = getPersistedInt(value);
} else { // Set state
int value = (Integer) defaultValue;
this.value = value;
persistInt(value);
}
}
/*
* Suppose a client uses this preference type without persisting. We
* must save the instance state so it is able to, for example, survive
* orientation changes.
*/
@Override protected Parcelable onSaveInstanceState() {
final Parcelable superState = super.onSaveInstanceState();
if (isPersistent()) return superState; // No need to save instance state since it's persistent
final SavedState myState = new SavedState(superState);
myState.value = value;
return myState;
}
@Override protected void onRestoreInstanceState(Parcelable state) {
if (!state.getClass().equals(SavedState.class)) {
// Didn't save state for us in onSaveInstanceState
super.onRestoreInstanceState(state);
return;
}
// Restore the instance state
SavedState myState = (SavedState) state;
super.onRestoreInstanceState(myState.getSuperState());
this.value = myState.value;
notifyChanged();
}
/**
* SavedState, a subclass of {@link BaseSavedState}, will store the state
* of MyPreference, a subclass of Preference.
* <p>
* It is important to always call through to super methods.
*/
private static class SavedState extends BaseSavedState {
int value;
public SavedState(Parcel source) {
super(source);
value = source.readInt();
}
@Override public void writeToParcel(Parcel dest, int flags) {
super.writeToParcel(dest, flags);
dest.writeInt(value);
}
public SavedState(Parcelable superState) {
super(superState);
}
@SuppressWarnings("unused") public static final Parcelable.Creator<SavedState> CREATOR = new Parcelable.Creator<SavedState>() {
public SavedState createFromParcel(Parcel in) {
return new SavedState(in);
}
public SavedState[] newArray(int size) {
return new SavedState[size];
}
};
}
}