/*
* Copyright (c) 2015 Jonas Kalderstam.
*
* 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 3 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, see <http://www.gnu.org/licenses/>.
*/
package com.nononsenseapps.notepad.ui.common;
import android.content.Context;
import android.content.res.TypedArray;
import android.text.Editable;
import android.text.Layout;
import android.text.Selection;
import android.text.Spannable;
import android.text.Spanned;
import android.text.TextWatcher;
import android.text.method.ArrowKeyMovementMethod;
import android.text.style.ClickableSpan;
import android.text.style.RelativeSizeSpan;
import android.text.style.StyleSpan;
import android.text.style.TypefaceSpan;
import android.text.util.Linkify;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.widget.EditText;
import android.widget.TextView;
import com.nononsenseapps.notepad.R;
/**
* An EditText field that highlights the first line and makes links clickable in
* the text. The text is still selectable, movable etc.
*
*/
public class StyledEditText extends EditText {
Object titleStyleSpan;
Object titleSizeSpan;
Object titleFamilySpan;
Object bodyFamilySpan;
private float mTitleRelativeSize;
private int mTitleFontFamily;
private int mBodyFontFamily;
private int mTitleFontStyle;
private boolean mLinkify;
private boolean mTitleLarger = true;
public StyledEditText(Context context, AttributeSet attrs) {
super(context, attrs);
TypedArray a = context.getTheme().obtainStyledAttributes(attrs,
R.styleable.StyledTextView, 0, 0);
try {
mTitleRelativeSize = a.getFloat(
R.styleable.StyledTextView_titleRelativeSize, 1.0f);
mTitleFontFamily = a.getInteger(
R.styleable.StyledTextView_titleFontFamily, 0);
mTitleFontStyle = a.getInteger(
R.styleable.StyledTextView_titleFontStyle, 0);
mLinkify = a.getBoolean(R.styleable.StyledTextView_linkify, false);
mBodyFontFamily = a.getInteger(
R.styleable.StyledTextView_bodyFontFamily, 0);
}
finally {
a.recycle();
}
setTitleRelativeLarger(mTitleLarger);
setTitleFontFamily(mTitleFontFamily);
setTitleFontStyle(mTitleFontStyle);
setBodyFontFamily(mBodyFontFamily);
// Style on change
addTextChangedListener(new TextWatcher() {
@Override
public void onTextChanged(CharSequence s, int start, int before,
int count) {
}
@Override
public void beforeTextChanged(CharSequence s, int start, int count,
int after) {
}
@Override
public void afterTextChanged(Editable s) {
spannify(s);
if (mLinkify) {
Linkify.addLinks(StyledEditText.this, Linkify.ALL);
// Links shouldnt steal click focus
// But text must still be selectable etc
setMovementMethod(new ArrowKeyMovementMethod());
}
}
});
}
/**
*
* @param larger true will use layout defined size, else same size
*/
public void setTitleRelativeLarger(final boolean larger) {
mTitleLarger = larger;
titleSizeSpan = new RelativeSizeSpan(larger ? mTitleRelativeSize : 1.0f);
}
/**
*
* @param family matches order defined in xml
*/
public void setTitleFontFamily(final int family) {
switch (family) {
case 1:
titleFamilySpan = new TypefaceSpan("sans-serif-condensed");
break;
case 2:
titleFamilySpan = new TypefaceSpan("sans-serif-light");
break;
case 3:
titleFamilySpan = new TypefaceSpan("sans-serif-thin");
break;
default:
titleFamilySpan = new TypefaceSpan("sans-serif");
break;
}
}
/**
*
* @param style
* matches order defined in xml
*/
public void setTitleFontStyle(final int style) {
switch (style) {
case 1:
titleStyleSpan = new StyleSpan(android.graphics.Typeface.BOLD);
break;
case 2:
titleStyleSpan = new StyleSpan(android.graphics.Typeface.ITALIC);
break;
default:
titleStyleSpan = new StyleSpan(android.graphics.Typeface.NORMAL);
break;
}
}
/**
*
* @param family matches order defined in xml
*/
public void setBodyFontFamily(final int family) {
switch (family) {
case 1:
bodyFamilySpan = new TypefaceSpan("sans-serif-condensed");
break;
case 2:
bodyFamilySpan = new TypefaceSpan("sans-serif-light");
break;
case 3:
bodyFamilySpan = new TypefaceSpan("sans-serif-thin");
break;
default:
bodyFamilySpan = new TypefaceSpan("sans-serif");
break;
}
}
/**
*
* @param size 0, 1 or 2 representing small/medium/large
*/
public void setTheTextSize(final int size) {
switch (size) {
case 0:
// small
super.setTextSize(14.0f);
break;
case 2:
// large
super.setTextSize(22.0f);
break;
case 1:
default:
// medium
super.setTextSize(18.0f);
break;
}
}
/**
*
* @param clickable if links should be clickable
*/
public void setLinkify(final boolean clickable) {
this.mLinkify = clickable;
}
private void spannify(final Spannable s) {
// Clear this first, or it will multiply!
for (RelativeSizeSpan rs : s.getSpans(0, s.length(),
RelativeSizeSpan.class)) {
s.removeSpan(rs);
}
int titleEnd = s.toString().indexOf("\n");
if (titleEnd < 0) {
titleEnd = s.toString().length();
}
if (titleEnd > 0) {
s.setSpan(titleStyleSpan, 0, titleEnd,
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
s.setSpan(titleSizeSpan, 0, titleEnd,
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
s.setSpan(titleFamilySpan, 0, titleEnd,
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
if (titleEnd < s.toString().length()) {
s.setSpan(bodyFamilySpan, titleEnd, s.toString().length(),
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
}
}
}
@Override
public boolean onTouchEvent(MotionEvent event) {
TextView widget = (TextView) this;
Object text = widget.getText();
if (text instanceof Spanned) {
Spannable buffer = (Spannable) text;
int action = event.getAction();
if (action == MotionEvent.ACTION_UP
|| action == MotionEvent.ACTION_DOWN) {
int x = (int) event.getX();
int y = (int) event.getY();
x -= widget.getTotalPaddingLeft();
y -= widget.getTotalPaddingTop();
x += widget.getScrollX();
y += widget.getScrollY();
Layout layout = widget.getLayout();
int line = layout.getLineForVertical(y);
int off = layout.getOffsetForHorizontal(line, x);
ClickableSpan[] link = buffer.getSpans(off, off,
ClickableSpan.class);
// Cant click to the right of a span, if the line ends with the span!
if (x > layout.getLineRight(line)) {
// Don't call the span
}
else if (link.length != 0) {
if (action == MotionEvent.ACTION_UP) {
link[0].onClick(widget);
}
else if (action == MotionEvent.ACTION_DOWN) {
Selection.setSelection(buffer,
buffer.getSpanStart(link[0]),
buffer.getSpanEnd(link[0]));
}
return true;
}
}
}
return super.onTouchEvent(event);
}
}