package com.electronapps.LJPro;
import java.io.BufferedInputStream;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.net.URLConnection;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import org.xml.sax.Attributes;
import org.xml.sax.XMLReader;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.Typeface;
import android.graphics.BitmapFactory.Options;
import android.media.ExifInterface;
import android.os.AsyncTask;
import android.os.Parcel;
import android.os.Parcelable;
import android.text.DynamicLayout;
import android.text.Editable;
import android.text.Layout;
import android.text.ParcelableSpan;
import android.text.Selection;
import android.text.Spannable;
import android.text.SpannableString;
import android.text.Spanned;
import android.text.TextPaint;
import android.text.TextWatcher;
import android.text.method.LinkMovementMethod;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.ExtractedText;
import android.view.inputmethod.ExtractedTextRequest;
import android.view.inputmethod.InputConnection;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;
import android.widget.ToggleButton;
import android.text.style.AbsoluteSizeSpan;
import android.text.style.AlignmentSpan;
import android.text.style.BackgroundColorSpan;
import android.text.style.CharacterStyle;
import android.text.style.ForegroundColorSpan;
import android.text.style.ImageSpan;
import android.text.style.ParagraphStyle;
import android.text.style.QuoteSpan;
import android.text.style.RelativeSizeSpan;
import android.text.style.StrikethroughSpan;
import android.text.style.StyleSpan;
import android.text.style.SubscriptSpan;
import android.text.style.SuperscriptSpan;
import android.text.style.TypefaceSpan;
import android.text.style.URLSpan;
import android.text.style.UnderlineSpan;
public class RichEditText extends EditText {
private Map<Integer,Boolean> mCurrentStyles=new HashMap<Integer,Boolean>();
private Context mContext;
private Editable mEditable;
private Integer mNewStyle;
// Following ids are used for mapping to span types
//Supported "advanced" styles
public final static int STYLE_UNDERLINE=3;
public final static int STYLE_STRIKETHROUGH=4;
public final static int STYLE_BLOCKQUOTE=5;
public final static int STYLE_SUPERSCRIPT=6;
public final static int STYLE_SUBSCRIPT=7;
//generic text sizes supported
public final static int ABSOLUTESIZE_SPAN=8;
public final static int SIZE_XXSMALL=9;
public final static int SIZE_XSMALL=10;
public final static int SIZE_SMALL=11;
public final static int SIZE_MEDIUM=12;
public final static int SIZE_LARGE=13;
public final static int SIZE_XLARGE=14;
public final static int SIZE_XXLARGE=15;
private int mTextColor=0xFF000000;
private int mBgColor=0xFFFFFFFF;
public final static int IMAGE_SPAN=16;
public final static int ALIGNMENT_SPAN=17;
public final static int URL_SPAN=18;
private Paint mPaint;
public final static int RELATIVESIZE_SPAN=19;
public final static int HEADER_H1=20;
public final static int HEADER_H2=21;
public final static int HEADER_H3=22;
public final static int HEADER_H4=23;
public final static int HEADER_H5=24;
public final static int HEADER_H6=25;
public final static int TEXT_COLOR=26;
public final static int BACKGROUND_COLOR=27;
public final static int BULLET_SPAN=28;
public final static int LJCUT_SPAN=29;
private static int[] mStyles={STYLE_UNDERLINE,STYLE_STRIKETHROUGH,STYLE_BLOCKQUOTE,STYLE_SUPERSCRIPT,STYLE_SUBSCRIPT};
private static int[] mRelativeSizes={SIZE_XXSMALL,SIZE_XSMALL,SIZE_SMALL,SIZE_MEDIUM,SIZE_LARGE,SIZE_XLARGE,SIZE_XXLARGE};
private static int mOffset=3;
private static final float[] HEADER_SIZES = {
1.5f, 1.4f, 1.3f, 1.2f, 1.1f, 1f,
};
private static int[] mHeaders={HEADER_H1,HEADER_H2,HEADER_H3,HEADER_H4,HEADER_H5,HEADER_H6};
private static final int[] mOffsets={-3*mOffset,-2*mOffset,-mOffset,0,mOffset,2*mOffset,3*mOffset};
public RichEditText(Context context) {
super(context);
setupEditor(context);
}
public RichEditText(Context context, AttributeSet attrs) {
super(context, attrs);
setupEditor(context);
}
public RichEditText(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
setupEditor(context);
}
private HashMap<Integer, Button> mButtons=new HashMap<Integer,Button>();
private float mSize;
Bitmap mCutIcon;
private DynamicLayout mInternalLayout;
private float mScale;
private Bitmap mImagePlaceholder;
public void setFontSizeOffset(int offset) {
mOffset=offset;
}
private void setupEditor(Context context) {
mContext=context;
mScale = getContext().getResources().getDisplayMetrics().density;
final Resources res=mContext.getResources();
mImagePlaceholder=BitmapFactory.decodeResource(res,R.drawable.missing_image);
mPaint=new Paint();
//relaative sizes are computed based on current Text size
mSize=getTextSize();
mEditable=getEditableText();
makeSizeHashes(mSize);
addTextChangedListener(onType);
mInternalLayout=(DynamicLayout)getLayout();
}
public static HashMap<Integer, Integer> SizeMap=new HashMap<Integer,Integer>();
public static HashMap<Integer, Integer> SizeIdMap=new HashMap<Integer,Integer>();
public static HashMap<Integer, Float> RelativeSizeMap=new HashMap<Integer,Float>();
public static HashMap<Float,Integer> RelativeSizeIdMap=new HashMap<Float,Integer>();
private static void makeSizeHashes(float fSize) {
int i=0;
int size=Math.round(fSize);
while (i<mRelativeSizes.length) {
SizeMap.put(mRelativeSizes[i], size+mOffsets[i]);
SizeIdMap.put(size+mOffsets[i], mRelativeSizes[i]);
i++;
}
int j=0;
while(j<mHeaders.length) {
RelativeSizeMap.put(mHeaders[j],HEADER_SIZES[j]);
RelativeSizeIdMap.put(HEADER_SIZES[j], mHeaders[j]);
j++;
}
}
public void registerBoldButton(Button t) {
mButtons.put(Typeface.BOLD,t);
}
public void registerItalicButton(Button t) {
mButtons.put(Typeface.ITALIC,t);
}
public void registerStyleButton(Button t) {
for (int style:mStyles) {
mButtons.put(style, t);
}
}
public void registerSizeButton(Button t) {
for (int size:mRelativeSizes) {
mButtons.put(size, t);
}
}
public void registerUnderlineButton(Button t) {
mButtons.put(STYLE_UNDERLINE, t);
}
public void registerStrikethroughButton(Button t) {
mButtons.put(STYLE_STRIKETHROUGH, t);
}
public void registerQuoteButton(Button t) {
mButtons.put(STYLE_BLOCKQUOTE, t);
}
public void registerSuperscriptButton(Button t) {
mButtons.put(STYLE_SUPERSCRIPT, t);
}
public void registerSubscriptButton(Button t) {
mButtons.put(STYLE_SUBSCRIPT, t);
}
public int getColor() {
return mTextColor;
}
public void setColor(int color) {
mTextColor=color;
}
public void setBackgroundColor(int color) {
mBgColor=color;
}
public int getBackgroundColor() {
return mBgColor;
}
public boolean isStyleSet(int style) {
boolean set=mCurrentStyles.get(style)==null?false:true;
return set;
}
public void toggleStyle(int style) {
mNewStyle=style;
if(mCurrentStyles.get(style)==null) { setStyle(style);
}
else removeStyle(style);
setOrRemoveStyle();
}
private String mURL;
public void setCurrentURL(String url){
mURL=url;
}
@SuppressWarnings("unchecked")
public void toggleStyle(int style, Object extra) {
mNewStyle=style;
switch (style) {
case LJCUT_SPAN:
mCutText=(String)extra;
break;
case IMAGE_SPAN:
HashMap<String,Object> image=(HashMap<String,Object>) extra;
String src=image.get("tag").toString();
mDesiredSize=(Integer) image.get("size");
Spanned text=Html.fromHtml(src, null, tagHandler);
getEditableText().insert(getSelectionStart(), text);
break;
case URL_SPAN:
mURL=(String)extra;
}
if(mCurrentStyles.get(style)==null) { setStyle(style);
}
else removeStyle(style);
setOrRemoveStyle();
}
public void setStyle(int style) {
mCurrentStyles.put(style,true);
}
public void removeStyle(int style) {
mCurrentStyles.remove(style);
}
public Set<Integer> getCurrentStyles() {
return mCurrentStyles.keySet();
}
private String mCutText;
@SuppressWarnings("unchecked")
private void setOrRemoveStyle(){
final int style=mNewStyle==null?-1:mNewStyle;
int selectionStart =getSelectionStart();
//Class type=null;
//Object what=null;
boolean adding=isStyleSet(style);
SpanInfo si;
if (style>=SIZE_XXSMALL&&style<=SIZE_XXLARGE) {
si=new SpanInfo(style,adding,SizeMap.get(style));
}
else if (style==TEXT_COLOR) {
si=new SpanInfo(style,true,mTextColor);
}
else if (style==BACKGROUND_COLOR) {
si=new SpanInfo(style,true,mBgColor);
}
else if (style==LJCUT_SPAN) {
si=new SpanInfo(style,adding,mCutText);
}
else if (style==URL_SPAN) {
si=new SpanInfo(style,adding,mURL);
}
else {
si=new SpanInfo(style,adding);
}
int selectionEnd = getSelectionEnd();
if (selectionStart > selectionEnd){
int temp = selectionEnd;
selectionEnd = selectionStart;
selectionStart = temp;
}
if (selectionEnd > selectionStart)
{
Object[] ss = mEditable.getSpans(selectionStart, selectionEnd, si.type);
boolean exists = false;
boolean newColor=false;
for (int i = 0; i < ss.length; i++) {
si=SpanInfo.getSpanInfo(ss[i],mEditable);
if (si!=null) {
if (si.id==TEXT_COLOR||si.id==BACKGROUND_COLOR) newColor=isNewColor(si);
if ((mCurrentStyles.get(si.id)==null)||newColor)
{
mEditable.removeSpan(ss[i]);
if (selectionStart>si.start) mEditable.setSpan(getSpan(style), si.start, selectionStart, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
if (selectionEnd<si.end) mEditable.setSpan(getSpan(style), selectionEnd, si.end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
exists =true;
}
}
}
if (!exists|newColor)
mEditable.setSpan(si.what, selectionStart, selectionEnd, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
}
}
private boolean isNewColor(SpanInfo si){
boolean newColor=false;
if ((si.id==TEXT_COLOR&&si.color!=mTextColor)|(si.id==BACKGROUND_COLOR&&si.color!=mBgColor)) newColor=true;
return newColor;
}
private int mOldTextEnd=0;
private TextWatcher onType=new TextWatcher() {
public void afterTextChanged(Editable s) {
//add style as the user types based on style of prev character
int position = getSelectionStart();
if (position < 0){
position = 0;
}
if (position > 0){
int start;
int end;
int newTextEnd=s.length();
int delta=newTextEnd-mOldTextEnd;
int firstNewChar;
if (delta>0) {
firstNewChar=position-delta+1;
start=firstNewChar-2<0?0:firstNewChar-2;
end=firstNewChar-1<0?0:firstNewChar-1;
}
else {
firstNewChar=position;
start=position-1<0?0:position-1;
end=position<0?0:position;
}
mOldTextEnd=newTextEnd;
boolean deleted=false;
Object[] ss = s.getSpans(start, end, Object.class);
if (ss.length>0) {
for (int i = 0; i < ss.length; i++) {
boolean newColor=false;
SpanInfo si=SpanInfo.getSpanInfo(ss[i],mEditable);
if (si!=null) {
Object what=getSpan(si.id);
if (si.id==TEXT_COLOR||si.id==BACKGROUND_COLOR) newColor=isNewColor(si);
if (mCurrentStyles.get(si.id)!=null&&!newColor){
//we are extending this style, so it's not new
if (position>si.end) {
mCurrentStyles.put(si.id,false);
s.removeSpan(ss[i]);
s.setSpan(what, si.start, position, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
}
else {
mCurrentStyles.put(si.id,false);
s.removeSpan(ss[i]);
s.setSpan(what, si.start, si.end,Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
}
}
else {
if (position<si.end) {
s.removeSpan(ss[i]);
s.setSpan(what, si.start,firstNewChar-1<0?0:firstNewChar-1, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
s.setSpan(what,position,si.end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
}
else if (position==si.end) {
//int flags=s.getSpanFlags(ss[i]);
if ((s.getSpanFlags(ss[i])&Spanned.SPAN_COMPOSING)==0) {
deleted=true;
}
}
}
}
}
if (deleted) {
onSelectionChanged(position,position);
return;
}
for (Integer style:mCurrentStyles.keySet()) {
if(mCurrentStyles.get(style)) {
if (delta>0) {
s.setSpan(getSpan(style), firstNewChar-1,position, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
}
}
else {mCurrentStyles.put(style, true);
}
}
}
else {
for (Integer style:mCurrentStyles.keySet()){
s.setSpan(getSpan(style), firstNewChar-1,position, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
}
}
}
}
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
//unused
}
public void onTextChanged(CharSequence s, int start, int before, int count) {
//unused
}
};
private Rect sTempRect=new Rect();
private Object getSpan(int style) {
Object what;
if (style>=SIZE_XXSMALL&&style<=SIZE_XXLARGE) {
what=SpanInfo.sizeFactory(SizeMap.get(style));
}
else if (style==TEXT_COLOR) {
what=SpanInfo.colorFactory(style, mTextColor);
}
else if (style==BACKGROUND_COLOR) {
what=SpanInfo.colorFactory(style, mBgColor);
}
else if (style==LJCUT_SPAN){
what=SpanInfo.ljcutFactory(mCutText);
}
else if (style==URL_SPAN) {
what=SpanInfo.linkFactory(mURL);
}
else {
what=SpanInfo.styleFactory(style);
}
return what;
}
Rect curLine=new Rect();
Rect nextLine=new Rect();
@Override
protected void onDraw(Canvas c) {
int dtop, dbottom;
super.onDraw(c);
synchronized (sTempRect) {
if (!c.getClipBounds(sTempRect)) {
return;
}
dtop = sTempRect.top;
dbottom = sTempRect.bottom;
}
final Layout layout=getLayout();
int top = 0;
int bottom = layout.getLineTop(getLineCount());
if (dtop > top) {
top = dtop;
}
if (dbottom < bottom) {
bottom = dbottom;
}
int first = layout.getLineForVertical(top);
int last = layout.getLineForVertical(bottom);
int previousLineEnd = layout.getLineStart(first);
TextPaint p =getPaint();
CharSequence buf = mEditable;
for (int i = first; i <= last; i++) {
int start = previousLineEnd;
previousLineEnd = layout.getLineStart(i+1);
int end = getLineVisibleEnd(i, start, previousLineEnd);
Spanned spanned=(Spanned) buf;
BlockSpan[] blocks=spanned.getSpans(start,end,BlockSpan.class);
for (BlockSpan block:blocks) {
int spanStart=spanned.getSpanStart(block);
int spanEnd=spanned.getSpanEnd(block);
float x;
if (spanStart>start) {
start=spanStart;
}
if(end>spanEnd){
end=spanEnd;
}
x=layout.getPrimaryHorizontal(start);
int baseline = getLineBounds(i, curLine);
block.drawBlock(c, p,x, curLine,baseline, buf, start, end,layout);
//reset
previousLineEnd = layout.getLineStart(i+1);
end = getLineVisibleEnd(i, start, previousLineEnd);
}
}
}
public Object[] getSpansAtSelection(Class t) {
int start=getSelectionStart();
int end=getSelectionEnd();
if (end<start) {
int tmp=end;
end=start;
start=tmp;
}
Object[] spans=((Spanned)mEditable).getSpans(start, end, t);
return spans;
}
private int getLineVisibleEnd(int line, int start, int end) {
CharSequence text = mEditable;
char ch;
if (line == getLineCount() - 1) {
return end;
}
for (; end > start; end--) {
ch = text.charAt(end - 1);
if (ch == '\n') {
return end - 1;
}
if (ch != ' ' && ch != '\t') {
break;
}
}
return end;
}
@Override
protected void onSelectionChanged (int selStart, int selEnd){
super.onSelectionChanged(selStart,selEnd);
if (mEditable!=null) {
int position=selEnd;
int start=position-2<0?0:position-1;
int end=position-1<0?0:position;
final Editable s=mEditable;
Object[] ss = s.getSpans(start, end, Object.class);
for (int i:mButtons.keySet()){
Button b=mButtons.get(i);
if (b!=null) {
if (b instanceof ToggleButton) ((ToggleButton) b).setChecked(false);
else b.setPressed(false);
}
}
mCurrentStyles.clear();
if (ss.length>0) {
Boolean withinImageSpan=false;
for (int i=0;i<ss.length;i++) {
SpanInfo si=SpanInfo.getSpanInfo(ss[i],mEditable);
if (si!=null&&(mEditable.getSpanFlags(ss[i])&Spanned.SPAN_COMPOSING)==0) {
if (si.id==RichEditText.IMAGE_SPAN) withinImageSpan=true;
mCurrentStyles.put(si.id,true);
if (si.id==TEXT_COLOR) mTextColor=si.color;
if (si.id==BACKGROUND_COLOR) mBgColor=si.color;
Button b=mButtons.get(si.id);
if (b!=null) {
if (b instanceof ToggleButton) ((ToggleButton) b).setChecked(true);
else b.setPressed(true);
}
}
}
if (withinImageSpan) {
//we don't want extend an imagespan or a link wrapping an image
mCurrentStyles.remove(RichEditText.IMAGE_SPAN);
mCurrentStyles.remove(RichEditText.URL_SPAN);
}
}
}
}
private int mDesiredSize=-1;
Html.TagHandler tagHandler = new Html.TagHandler() {
public void handleTag(boolean opening, String tag,Attributes attributes, Editable output, XMLReader xmlReader) {
if (opening) {
if (tag.equalsIgnoreCase("img")) {
handleImg(attributes, output);
}
}
}
};
private void handleImg(Attributes attributes, Editable output) {
String src = attributes.getValue("src");
String h=attributes.getValue("height");
String w=attributes.getValue("width");
int width, height;
if (w!=null) {
width= Integer.parseInt(w);
}
else {
width=-1;
}
if (h!=null) {
height=Integer.parseInt(h);
}
else {
height=-1;
}
int start = output.length();
output.append("\uFFFC");
int end = output.length();
HTMLImageSpan span = new HTMLImageSpan(mImagePlaceholder,null,width,height,mDesiredSize);
if (src != null) {
(new ImageTask(span,100)).execute(src);
output.setSpan(span, start, end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
}
}
private class ImageTask extends AsyncTask<String,Void,Bitmap> {
private HTMLImageSpan mPlaceHolder;
private int mSize;
private String mSrc;
public ImageTask(HTMLImageSpan placeholder,int width) {
mPlaceHolder=placeholder;
mSize=width;
}
@Override
protected Bitmap doInBackground(String... params) {
String url=params[0].toString();
mSrc=url;
try {
URLConnection connection=new URL(url).openConnection();
InputStream stream=connection.getInputStream();
BufferedInputStream in=new BufferedInputStream(stream);
ByteArrayOutputStream out=new ByteArrayOutputStream(10240);
int read;
byte[] b=new byte[4096];
while ((read = in.read(b)) != -1) {
out.write(b, 0, read);
}
out.flush();
out.close();
in.close();
byte[] raw=out.toByteArray();
BitmapFactory.Options ops= new BitmapFactory.Options();
ops.inTempStorage = new byte[32*1024];
Bitmap scaled=BitmapFactory.decodeStream(new ByteArrayInputStream(raw), padding, ops);
return scaled;
}
catch (Throwable t) {
Log.e("NewPost", "Exception downloading image", t);
return null;
}
}
protected void onPostExecute(Bitmap bitmap) {
if(bitmap!=null) {
int width=bitmap.getWidth();
int height=bitmap.getHeight();
float scale;
int displayHeight,displayWidth;
if (width>height) {
displayWidth=mPlaceHolder.getMaxDim()==-1?width:mPlaceHolder.getMaxDim();
scale = ((float) this.mSize*mScale)/width;
float dratio=((float) bitmap.getWidth()) / displayWidth;
displayHeight=(int) (height / dratio);
}
else {
displayHeight=mPlaceHolder.getMaxDim()==-1?height:mPlaceHolder.getMaxDim();
scale = ((float) this.mSize*mScale)/height;
float dratio=((float) height / displayHeight);
displayWidth=(int) (width/ dratio);
}
Matrix matrix = new Matrix();
matrix.postScale(scale, scale);
Bitmap scaled = Bitmap.createBitmap(bitmap, 0, 0, width, height, matrix, true);
Editable editableText=getEditableText();
if (editableText != null) {
int start = editableText.getSpanStart(mPlaceHolder);
int end = editableText.getSpanEnd(mPlaceHolder);
if (start != -1 && end != -1) {
editableText.removeSpan(mPlaceHolder);
HTMLImageSpan span = new HTMLImageSpan(scaled,mSrc,displayWidth,displayHeight);
editableText.setSpan(span, start, end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
}
}
}
}
}
private static final Rect padding=new Rect(-1,-1,-1,-1);
private static final int IMAGE_THUMB=100;
HashMap<String,HTMLImageSpan> imageStubs=new HashMap<String,HTMLImageSpan>();
public void insertImageStub(String provider,String filepath, String title, int desiredSize) {
BitmapFactory.Options ops=new BitmapFactory.Options();
ops.inTempStorage = new byte[32*1024];
Matrix matrix = new Matrix();
try {
ExifInterface exif = new ExifInterface(filepath);
int orientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, 1);
switch(orientation){
case 3:
matrix.postRotate(180);
break;
case 6:
matrix.postRotate(90);
break;
case 8:
matrix.postRotate(-90);
break;
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
Bitmap bitmap=BitmapFactory.decodeFile(filepath,ops);
if(bitmap!=null) {
int width=bitmap.getWidth();
int height=bitmap.getHeight();
float scale;
int displayHeight,displayWidth;
if (width>height) {
displayWidth=desiredSize==-1||desiredSize>width?width:desiredSize;
scale = ((float) IMAGE_THUMB*mScale)/width;
float dratio=((float) bitmap.getWidth()) / displayWidth;
displayHeight=(int) (height / dratio);
}
else {
displayHeight=desiredSize==-1||desiredSize>height?height:desiredSize;
scale = ((float) IMAGE_THUMB*mScale)/height;
float dratio=((float) height / displayHeight);
displayWidth=(int) (width/ dratio);
}
matrix.postScale(scale, scale);
Bitmap scaled = Bitmap.createBitmap(bitmap, 0, 0, width, height, matrix, true);
Editable editableText=getEditableText();
if (editableText != null) {
int start=editableText.length();
editableText.append("\uFFFC");
int end = editableText.length();
if (start != -1 && end != -1) {
HTMLImageSpan span = new HTMLImageSpan(scaled,null,displayWidth,displayHeight);
span.setTitle(title);
span.setProvider(provider);
span.setUploaded(false);
imageStubs.put(filepath,span);
editableText.setSpan(span, start, end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
}
}
}
}
public void setImageDetails(String filepath,String link, String srcUrl,String title){
HTMLImageSpan span=imageStubs.get(filepath);
if (span!=null){
if (link!=null) {
final Editable text=getEditableText();
int start=text.getSpanStart(span);
int end=text.getSpanEnd(span);
text.setSpan(new URLSpan(link), start, end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
}
span.setSrc(srcUrl);
span.setTitle(title);
span.setUploaded(true);
}
imageStubs.remove(filepath);
}
public static class SavedState extends View.BaseSavedState {
CharSequence text;
int selStart;
int selEnd;
Map<Integer, Boolean> currentStyles;
SavedState(Parcelable superState) {
super(superState);
}
@Override
public void writeToParcel(Parcel out, int flags) {
super.writeToParcel(out, flags);
out.writeInt(selStart);
out.writeInt(selEnd);
for (Integer style:currentStyles.keySet()) {
out.writeInt(style);
}
out.writeInt(0);
SpanInfo.writeToParcel(text, out, flags);
}
@Override
public String toString() {
String str = "TextView.SavedState{"
+ Integer.toHexString(System.identityHashCode(this))
+ " start=" + selStart + " end=" + selEnd;
if (text != null) {
str += " text=" + text;
}
return str + "}";
}
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];
}
};
private SavedState(Parcel in) {
super(in);
selStart = in.readInt();
selEnd = in.readInt();
while(true) {
int style=in.readInt();
if (style==0) break;
currentStyles.put(style, true);
}
text = (Editable) SpanInfo.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
}
}
@Override
public Parcelable onSaveInstanceState() {
Parcelable superState = super.onSaveInstanceState();
// Save state if we are forced to
int start = 0;
int end = 0;
if (mEditable != null) {
start = getSelectionStart();
end = getSelectionEnd();
SavedState ss = new SavedState(superState);
// XXX Should also save the current scroll position!
ss.selStart = start;
ss.selEnd = end;
ss.currentStyles=mCurrentStyles;
/*
* Calling setText() strips off any ChangeWatchers;
* strip them now to avoid leaking references.
* But do it to a copy so that if there are any
* further changes to the text of this view, it
* won't get into an inconsistent state.
*/
Spannable sp = new SpannableString(mEditable);
/* for (ChangeWatcher cw :
sp.getSpans(0, sp.length(), ChangeWatcher.class)) {
sp.removeSpan(cw);
}*/
ss.text = sp;
return ss;
}
return superState;
}
@Override
public void onRestoreInstanceState(Parcelable state) {
if (!(state instanceof SavedState)) {
super.onRestoreInstanceState(state);
return;
}
SavedState ss = (SavedState)state;
super.onRestoreInstanceState(ss.getSuperState());
// XXX restore buffer type too, as well as lots of other stuff
if (ss.text != null) {
setText(ss.text);
mEditable=getEditableText();
}
mCurrentStyles=ss.currentStyles;
for (Integer style:mCurrentStyles.keySet()) {
Button b=mButtons.get(style);
if (b!=null) {
if (b instanceof ToggleButton) ((ToggleButton) b).setChecked(true);
else b.setPressed(true);
}
}
if (ss.selStart >= 0 && ss.selEnd >= 0) {
if (mEditable instanceof Spannable) {
int len = mEditable.length();
if (ss.selStart > len || ss.selEnd > len) {
String restored = "";
if (ss.text != null) {
restored = "(restored) ";
}
Log.e("RicEditText", "Saved cursor position " + ss.selStart +
"/" + ss.selEnd + " out of range for " + restored +
"text " + mEditable);
} else {
Selection.setSelection((Spannable) mEditable, ss.selStart,
ss.selEnd);
}
}
}
}
}