blob: 75febb3fa6c920b9d7266ff442ae8edf5fdfa81f [file] [log] [blame]
package org.wordpress.android.editor;
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.res.Configuration;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Typeface;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Parcelable;
import android.support.v7.app.ActionBar;
import android.support.v7.app.AppCompatActivity;
import android.text.Editable;
import android.text.Layout;
import android.text.Selection;
import android.text.Spannable;
import android.text.Spanned;
import android.text.TextUtils;
import android.text.TextWatcher;
import android.text.method.ArrowKeyMovementMethod;
import android.text.style.AlignmentSpan;
import android.text.style.QuoteSpan;
import android.text.style.StrikethroughSpan;
import android.text.style.StyleSpan;
import android.text.style.URLSpan;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewTreeObserver;
import android.view.WindowManager;
import android.view.animation.AlphaAnimation;
import android.view.animation.Animation;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputMethodManager;
import android.webkit.URLUtil;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.CompoundButton;
import android.widget.EditText;
import android.widget.LinearLayout;
import android.widget.SeekBar;
import android.widget.Spinner;
import android.widget.TextView;
import android.widget.ToggleButton;
import com.android.volley.VolleyError;
import com.android.volley.toolbox.ImageLoader;
import org.wordpress.android.editor.legacy.EditLinkActivity;
import org.wordpress.android.editor.legacy.WPEditImageSpan;
import org.wordpress.android.util.AppLog;
import org.wordpress.android.util.AppLog.T;
import org.wordpress.android.util.DisplayUtils;
import org.wordpress.android.util.ImageUtils;
import org.wordpress.android.util.MediaUtils;
import org.wordpress.android.util.ToastUtils;
import org.wordpress.android.util.ToastUtils.Duration;
import org.wordpress.android.util.helpers.MediaFile;
import org.wordpress.android.util.helpers.MediaGallery;
import org.wordpress.android.util.helpers.MediaGalleryImageSpan;
import org.wordpress.android.util.helpers.WPImageSpan;
import org.wordpress.android.util.helpers.WPUnderlineSpan;
import org.wordpress.android.util.widgets.WPEditText;
import java.util.Locale;
public class LegacyEditorFragment extends EditorFragmentAbstract implements TextWatcher,
WPEditText.OnSelectionChangedListener, View.OnTouchListener {
public static final int ACTIVITY_REQUEST_CODE_CREATE_LINK = 4;
public static final String ACTION_MEDIA_GALLERY_TOUCHED = "MEDIA_GALLERY_TOUCHED";
public static final String EXTRA_MEDIA_GALLERY = "EXTRA_MEDIA_GALLERY";
private static final int MIN_THUMBNAIL_WIDTH = 200;
private static final int CONTENT_ANIMATION_DURATION = 250;
private static final String KEY_IMAGE_SPANS = "image-spans";
private static final String KEY_START = "start";
private static final String KEY_END = "end";
private static final String KEY_CONTENT = "content";
private static final String TAG_FORMAT_BAR_BUTTON_STRONG = "strong";
private static final String TAG_FORMAT_BAR_BUTTON_EM = "em";
private static final String TAG_FORMAT_BAR_BUTTON_UNDERLINE = "u";
private static final String TAG_FORMAT_BAR_BUTTON_STRIKE = "strike";
private static final String TAG_FORMAT_BAR_BUTTON_QUOTE = "blockquote";
private View mRootView;
private WPEditText mContentEditText;
private EditText mTitleEditText;
private ToggleButton mBoldToggleButton, mEmToggleButton, mBquoteToggleButton;
private ToggleButton mUnderlineToggleButton, mStrikeToggleButton;
private LinearLayout mFormatBar, mPostContentLinearLayout, mPostSettingsLinearLayout;
private Button mAddPictureButton;
private boolean mIsBackspace;
private boolean mScrollDetected;
private boolean mIsLocalDraft;
private int mStyleStart, mSelectionStart, mSelectionEnd, mFullViewBottom;
private int mLastPosition = -1;
private CharSequence mTitle;
private CharSequence mContent;
private float mLastYPos = 0;
@Override
public boolean onBackPressed() {
// leave full screen mode back button is pressed
if (getActionBar() != null && !getActionBar().isShowing()) {
setContentEditingModeVisible(false);
return true;
}
return false;
}
@Override
public CharSequence getTitle() {
if (mTitleEditText != null) {
return mTitleEditText.getText().toString();
}
return mTitle;
}
@Override
public CharSequence getContent() {
if (mContentEditText != null) {
return mContentEditText.getText().toString();
}
return mContent;
}
@Override
public void setTitle(CharSequence text) {
mTitle = text;
if (mTitleEditText != null) {
mTitleEditText.setText(text);
} else {
// TODO
}
}
@Override
public void setContent(CharSequence text) {
mContent = text;
if (mContentEditText != null) {
mContentEditText.setText(text);
mContentEditText.setSelection(mSelectionStart, mSelectionEnd);
} else {
// TODO
}
}
@Override
public Spanned getSpannedContent() {
return mContentEditText.getText();
}
public void setLocalDraft(boolean isLocalDraft) {
mIsLocalDraft = isLocalDraft;
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
final ViewGroup rootView = (ViewGroup) inflater.inflate(R.layout.fragment_edit_post_content, container, false);
mFormatBar = (LinearLayout) rootView.findViewById(R.id.format_bar);
mTitleEditText = (EditText) rootView.findViewById(R.id.post_title);
mTitleEditText.setText(mTitle);
mTitleEditText.setOnEditorActionListener(new TextView.OnEditorActionListener() {
@Override
public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
// Go to full screen editor when 'next' button is tapped on soft keyboard
ActionBar actionBar = getActionBar();
if (actionId == EditorInfo.IME_ACTION_NEXT && actionBar != null && actionBar.isShowing()) {
setContentEditingModeVisible(true);
}
return false;
}
});
mContentEditText = (WPEditText) rootView.findViewById(R.id.post_content);
mContentEditText.setText(mContent);
mPostContentLinearLayout = (LinearLayout) rootView.findViewById(R.id.post_content_wrapper);
mPostSettingsLinearLayout = (LinearLayout) rootView.findViewById(R.id.post_settings_wrapper);
Button postSettingsButton = (Button) rootView.findViewById(R.id.post_settings_button);
postSettingsButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mEditorFragmentListener.onSettingsClicked();
}
});
mBoldToggleButton = (ToggleButton) rootView.findViewById(R.id.bold);
mEmToggleButton = (ToggleButton) rootView.findViewById(R.id.em);
mBquoteToggleButton = (ToggleButton) rootView.findViewById(R.id.bquote);
mUnderlineToggleButton = (ToggleButton) rootView.findViewById(R.id.underline);
mStrikeToggleButton = (ToggleButton) rootView.findViewById(R.id.strike);
mAddPictureButton = (Button) rootView.findViewById(R.id.addPictureButton);
Button linkButton = (Button) rootView.findViewById(R.id.link);
Button moreButton = (Button) rootView.findViewById(R.id.more);
registerForContextMenu(mAddPictureButton);
mContentEditText = (WPEditText) rootView.findViewById(R.id.post_content);
mContentEditText.setOnSelectionChangedListener(this);
mContentEditText.setOnTouchListener(this);
mContentEditText.addTextChangedListener(this);
mContentEditText.setOnEditTextImeBackListener(new WPEditText.EditTextImeBackListener() {
@Override
public void onImeBack(WPEditText ctrl, String text) {
// Go back to regular editor if IME keyboard is dismissed
// Bottom comparison is there to ensure that the keyboard is actually showing
ActionBar actionBar = getActionBar();
if (mRootView.getBottom() < mFullViewBottom && actionBar != null && !actionBar.isShowing()) {
setContentEditingModeVisible(false);
}
}
});
mAddPictureButton.setOnClickListener(mFormatBarButtonClickListener);
mBoldToggleButton.setOnClickListener(mFormatBarButtonClickListener);
linkButton.setOnClickListener(mFormatBarButtonClickListener);
mEmToggleButton.setOnClickListener(mFormatBarButtonClickListener);
mUnderlineToggleButton.setOnClickListener(mFormatBarButtonClickListener);
mStrikeToggleButton.setOnClickListener(mFormatBarButtonClickListener);
mBquoteToggleButton.setOnClickListener(mFormatBarButtonClickListener);
moreButton.setOnClickListener(mFormatBarButtonClickListener);
mEditorFragmentListener.onEditorFragmentInitialized();
if (savedInstanceState != null) {
Parcelable[] spans = savedInstanceState.getParcelableArray(KEY_IMAGE_SPANS);
mContent = savedInstanceState.getString(KEY_CONTENT, "");
mContentEditText.setText(mContent);
mContentEditText.setSelection(savedInstanceState.getInt(KEY_START, 0),
savedInstanceState.getInt(KEY_END, 0));
if (spans != null && spans.length > 0) {
for (Parcelable s : spans) {
WPImageSpan editSpan = (WPImageSpan)s;
addMediaFile(editSpan.getMediaFile(), editSpan.getMediaFile().getFilePath(),
mImageLoader, editSpan.getStartPosition(), editSpan.getEndPosition());
}
}
}
return rootView;
}
@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
mRootView = view;
mRootView.getViewTreeObserver().addOnGlobalLayoutListener(mGlobalLayoutListener);
}
private ViewTreeObserver.OnGlobalLayoutListener mGlobalLayoutListener = new ViewTreeObserver.OnGlobalLayoutListener() {
public void onGlobalLayout() {
mRootView.getViewTreeObserver().removeGlobalOnLayoutListener(this);
mFullViewBottom = mRootView.getBottom();
}
};
private ActionBar getActionBar() {
if (!isAdded()) {
return null;
}
if (getActivity() instanceof AppCompatActivity) {
return ((AppCompatActivity) getActivity()).getSupportActionBar();
} else {
return null;
}
}
public void setContentEditingModeVisible(boolean isVisible) {
if (!isAdded()) {
return;
}
ActionBar actionBar = getActionBar();
if (isVisible) {
Animation fadeAnimation = new AlphaAnimation(1, 0);
fadeAnimation.setDuration(CONTENT_ANIMATION_DURATION);
fadeAnimation.setAnimationListener(new Animation.AnimationListener() {
@Override
public void onAnimationStart(Animation animation) {
mTitleEditText.setVisibility(View.GONE);
}
@Override
public void onAnimationEnd(Animation animation) {
mPostSettingsLinearLayout.setVisibility(View.GONE);
mFormatBar.setVisibility(View.VISIBLE);
}
@Override
public void onAnimationRepeat(Animation animation) {
}
});
mPostContentLinearLayout.startAnimation(fadeAnimation);
if (actionBar != null) {
actionBar.hide();
}
} else {
mTitleEditText.setVisibility(View.VISIBLE);
mFormatBar.setVisibility(View.GONE);
Animation fadeAnimation = new AlphaAnimation(0, 1);
fadeAnimation.setDuration(CONTENT_ANIMATION_DURATION);
fadeAnimation.setAnimationListener(new Animation.AnimationListener() {
@Override
public void onAnimationStart(Animation animation) {
}
@Override
public void onAnimationEnd(Animation animation) {
mPostSettingsLinearLayout.setVisibility(View.VISIBLE);
}
@Override
public void onAnimationRepeat(Animation animation) {
}
});
mPostContentLinearLayout.startAnimation(fadeAnimation);
getActivity().invalidateOptionsMenu();
if (actionBar != null) {
actionBar.show();
}
}
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == LegacyEditorFragment.ACTIVITY_REQUEST_CODE_CREATE_LINK && data != null) {
Bundle extras = data.getExtras();
if (extras == null) {
return;
}
String linkURL = extras.getString("linkURL");
String linkText = extras.getString("linkText");
createLinkFromSelection(linkURL, linkText);
}
}
public boolean hasEmptyContentFields() {
return TextUtils.isEmpty(mTitleEditText.getText()) && TextUtils.isEmpty(mContentEditText.getText());
}
@Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
mFullViewBottom = mRootView.getBottom();
}
private void createLinkFromSelection(String linkURL, String linkText) {
try {
if (linkURL != null && !linkURL.equals("http://") && !linkURL.equals("")) {
if (mSelectionStart > mSelectionEnd) {
int temp = mSelectionEnd;
mSelectionEnd = mSelectionStart;
mSelectionStart = temp;
}
Editable editable = mContentEditText.getText();
if (editable == null) {
return;
}
if (mIsLocalDraft) {
if (linkText == null) {
if (mSelectionStart < mSelectionEnd) {
editable.delete(mSelectionStart, mSelectionEnd);
}
editable.insert(mSelectionStart, linkURL);
editable.setSpan(new URLSpan(linkURL), mSelectionStart, mSelectionStart + linkURL.length(),
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
mContentEditText.setSelection(mSelectionStart + linkURL.length());
} else {
if (mSelectionStart < mSelectionEnd) {
editable.delete(mSelectionStart, mSelectionEnd);
}
editable.insert(mSelectionStart, linkText);
editable.setSpan(new URLSpan(linkURL), mSelectionStart, mSelectionStart + linkText.length(),
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
mContentEditText.setSelection(mSelectionStart + linkText.length());
}
} else {
if (linkText == null) {
if (mSelectionStart < mSelectionEnd) {
editable.delete(mSelectionStart, mSelectionEnd);
}
String urlHTML = "<a href=\"" + linkURL + "\">" + linkURL + "</a>";
editable.insert(mSelectionStart, urlHTML);
mContentEditText.setSelection(mSelectionStart + urlHTML.length());
} else {
if (mSelectionStart < mSelectionEnd) {
editable.delete(mSelectionStart, mSelectionEnd);
}
String urlHTML = "<a href=\"" + linkURL + "\">" + linkText + "</a>";
editable.insert(mSelectionStart, urlHTML);
mContentEditText.setSelection(mSelectionStart + urlHTML.length());
}
}
}
} catch (RuntimeException e) {
AppLog.e(T.POSTS, e);
}
}
/**
* Formatting bar
*/
private View.OnClickListener mFormatBarButtonClickListener = new View.OnClickListener() {
@Override
public void onClick(View v) {
int id = v.getId();
if (id == R.id.bold) {
mEditorFragmentListener.onTrackableEvent(TrackableEvent.BOLD_BUTTON_TAPPED);
onFormatButtonClick(mBoldToggleButton, TAG_FORMAT_BAR_BUTTON_STRONG);
} else if (id == R.id.em) {
mEditorFragmentListener.onTrackableEvent(TrackableEvent.ITALIC_BUTTON_TAPPED);
onFormatButtonClick(mEmToggleButton, TAG_FORMAT_BAR_BUTTON_EM);
} else if (id == R.id.underline) {
mEditorFragmentListener.onTrackableEvent(TrackableEvent.UNDERLINE_BUTTON_TAPPED);
onFormatButtonClick(mUnderlineToggleButton, TAG_FORMAT_BAR_BUTTON_UNDERLINE);
} else if (id == R.id.strike) {
mEditorFragmentListener.onTrackableEvent(TrackableEvent.STRIKETHROUGH_BUTTON_TAPPED);
onFormatButtonClick(mStrikeToggleButton, TAG_FORMAT_BAR_BUTTON_STRIKE);
} else if (id == R.id.bquote) {
mEditorFragmentListener.onTrackableEvent(TrackableEvent.BLOCKQUOTE_BUTTON_TAPPED);
onFormatButtonClick(mBquoteToggleButton, TAG_FORMAT_BAR_BUTTON_QUOTE);
} else if (id == R.id.more) {
mEditorFragmentListener.onTrackableEvent(TrackableEvent.MORE_BUTTON_TAPPED);
mSelectionEnd = mContentEditText.getSelectionEnd();
Editable str = mContentEditText.getText();
if (str != null) {
if (mSelectionEnd > str.length())
mSelectionEnd = str.length();
str.insert(mSelectionEnd, "\n<!--more-->\n");
}
} else if (id == R.id.link) {
mEditorFragmentListener.onTrackableEvent(TrackableEvent.LINK_BUTTON_TAPPED);
mSelectionStart = mContentEditText.getSelectionStart();
mStyleStart = mSelectionStart;
mSelectionEnd = mContentEditText.getSelectionEnd();
if (mSelectionStart > mSelectionEnd) {
int temp = mSelectionEnd;
mSelectionEnd = mSelectionStart;
mSelectionStart = temp;
}
Intent i = new Intent(getActivity(), EditLinkActivity.class);
if (mSelectionEnd > mSelectionStart) {
if (mContentEditText.getText() != null) {
String selectedText = mContentEditText.getText().subSequence(mSelectionStart, mSelectionEnd).toString();
i.putExtra("selectedText", selectedText);
}
}
startActivityForResult(i, ACTIVITY_REQUEST_CODE_CREATE_LINK);
} else if (id == R.id.addPictureButton) {
mEditorFragmentListener.onTrackableEvent(TrackableEvent.MEDIA_BUTTON_TAPPED);
mEditorFragmentListener.onAddMediaClicked();
if (isAdded()) {
getActivity().openContextMenu(mAddPictureButton);
}
}
}
};
private WPEditImageSpan createWPEditImageSpanLocal(Context context, MediaFile mediaFile) {
if (context == null || mediaFile == null || mediaFile.getFilePath() == null) {
return null;
}
Uri imageUri = Uri.parse(mediaFile.getFilePath());
Bitmap thumbnailBitmap;
if (MediaUtils.isVideo(imageUri.toString())) {
thumbnailBitmap = BitmapFactory.decodeResource(context.getResources(), R.drawable.media_movieclip);
} else {
thumbnailBitmap = ImageUtils.getWPImageSpanThumbnailFromFilePath(context, imageUri.getEncodedPath(),
ImageUtils.getMaximumThumbnailWidthForEditor(context));
if (thumbnailBitmap == null) {
// Use a placeholder in case thumbnail can't be decoded (OOM for instance)
thumbnailBitmap = BitmapFactory.decodeResource(context.getResources(),
R.drawable.legacy_dashicon_format_image_big_grey);
}
}
WPEditImageSpan imageSpan = new WPEditImageSpan(context, thumbnailBitmap, imageUri);
mediaFile.setWidth(MediaUtils.getMaximumImageWidth(context, imageUri, mBlogSettingMaxImageWidth));
imageSpan.setMediaFile(mediaFile);
return imageSpan;
}
private WPEditImageSpan createWPEditImageSpanRemote(Context context, MediaFile mediaFile) {
if (context == null || mediaFile == null || mediaFile.getFileURL() == null) {
return null;
}
int drawable = mediaFile.isVideo() ? R.drawable.media_movieclip : R.drawable.legacy_dashicon_format_image_big_grey;
Uri uri = Uri.parse(mediaFile.getFileURL());
WPEditImageSpan imageSpan = new WPEditImageSpan(context, drawable, uri);
imageSpan.setMediaFile(mediaFile);
return imageSpan;
}
private WPEditImageSpan createWPEditImageSpan(Context context, MediaFile mediaFile) {
if (!URLUtil.isNetworkUrl(mediaFile.getFileURL())) {
return createWPEditImageSpanLocal(context, mediaFile);
} else {
return createWPEditImageSpanRemote(context, mediaFile);
}
}
/**
* Applies formatting to selected text, or marks the entry for a new text style
* at the current cursor position
* @param toggleButton button from formatting bar
* @param tag HTML tag name for text style
*/
private void onFormatButtonClick(ToggleButton toggleButton, String tag) {
Spannable s = mContentEditText.getText();
if (s == null)
return;
int selectionStart = mContentEditText.getSelectionStart();
mStyleStart = selectionStart;
int selectionEnd = mContentEditText.getSelectionEnd();
if (selectionStart > selectionEnd) {
int temp = selectionEnd;
selectionEnd = selectionStart;
selectionStart = temp;
}
Class styleClass = null;
if (tag.equals(TAG_FORMAT_BAR_BUTTON_STRONG) || tag.equals(TAG_FORMAT_BAR_BUTTON_EM))
styleClass = StyleSpan.class;
else if (tag.equals(TAG_FORMAT_BAR_BUTTON_UNDERLINE))
styleClass = WPUnderlineSpan.class;
else if (tag.equals(TAG_FORMAT_BAR_BUTTON_STRIKE))
styleClass = StrikethroughSpan.class;
else if (tag.equals(TAG_FORMAT_BAR_BUTTON_QUOTE))
styleClass = QuoteSpan.class;
if (styleClass == null)
return;
Object[] allSpans = s.getSpans(selectionStart, selectionEnd, styleClass);
boolean textIsSelected = selectionEnd > selectionStart;
if (mIsLocalDraft) {
// Local drafts can use the rich text editor. Yay!
boolean shouldAddSpan = true;
for (Object span : allSpans) {
if (span instanceof StyleSpan) {
StyleSpan styleSpan = (StyleSpan)span;
if ((styleSpan.getStyle() == Typeface.BOLD && !tag.equals(TAG_FORMAT_BAR_BUTTON_STRONG))
|| (styleSpan.getStyle() == Typeface.ITALIC && !tag.equals(TAG_FORMAT_BAR_BUTTON_EM))) {
continue;
}
}
if (!toggleButton.isChecked() && textIsSelected) {
// If span exists and text is selected, remove the span
s.removeSpan(span);
shouldAddSpan = false;
break;
} else if (!toggleButton.isChecked()) {
// Remove span at cursor point if button isn't checked
Object[] spans = s.getSpans(mStyleStart - 1, mStyleStart, styleClass);
for (Object removeSpan : spans) {
selectionStart = s.getSpanStart(removeSpan);
selectionEnd = s.getSpanEnd(removeSpan);
s.removeSpan(removeSpan);
}
}
}
if (shouldAddSpan) {
if (tag.equals(TAG_FORMAT_BAR_BUTTON_STRONG)) {
s.setSpan(new StyleSpan(android.graphics.Typeface.BOLD), selectionStart, selectionEnd, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
} else if (tag.equals(TAG_FORMAT_BAR_BUTTON_EM)) {
s.setSpan(new StyleSpan(android.graphics.Typeface.ITALIC), selectionStart, selectionEnd, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
} else {
try {
s.setSpan(styleClass.newInstance(), selectionStart, selectionEnd, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
} catch (java.lang.InstantiationException e) {
AppLog.e(T.POSTS, e);
} catch (IllegalAccessException e) {
AppLog.e(T.POSTS, e);
}
}
}
} else {
// Add HTML tags when editing an existing post
String startTag = "<" + tag + ">";
String endTag = "</" + tag + ">";
Editable content = mContentEditText.getText();
if (textIsSelected) {
content.insert(selectionStart, startTag);
content.insert(selectionEnd + startTag.length(), endTag);
toggleButton.setChecked(false);
mContentEditText.setSelection(selectionEnd + startTag.length() + endTag.length());
} else if (toggleButton.isChecked()) {
content.insert(selectionStart, startTag);
mContentEditText.setSelection(selectionEnd + startTag.length());
} else if (!toggleButton.isChecked()) {
content.insert(selectionEnd, endTag);
mContentEditText.setSelection(selectionEnd + endTag.length());
}
}
}
/**
* Rich Text Editor
*/
public void showImageSettings(final View alertView, final EditText titleText,
final EditText caption, final EditText imageWidthText,
final CheckBox featuredCheckBox, final CheckBox featuredInPostCheckBox,
final int maxWidth, final Spinner alignmentSpinner, final WPImageSpan imageSpan) {
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
builder.setTitle(getString(R.string.image_settings));
builder.setView(alertView);
builder.setPositiveButton(getString(android.R.string.ok), new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int whichButton) {
String title = (titleText.getText() != null) ? titleText.getText().toString() : "";
MediaFile mediaFile = imageSpan.getMediaFile();
if (mediaFile == null) {
return;
}
mediaFile.setTitle(title);
mediaFile.setHorizontalAlignment(alignmentSpinner.getSelectedItemPosition());
mediaFile.setWidth(getEditTextIntegerClamped(imageWidthText, 10, maxWidth));
String captionText = (caption.getText() != null) ? caption.getText().toString() : "";
mediaFile.setCaption(captionText);
mediaFile.setFeatured(featuredCheckBox.isChecked());
if (featuredCheckBox.isChecked()) {
// remove featured flag from all other images
Spannable contentSpannable = mContentEditText.getText();
WPImageSpan[] imageSpans =
contentSpannable.getSpans(0, contentSpannable.length(), WPImageSpan.class);
if (imageSpans.length > 1) {
for (WPImageSpan postImageSpan : imageSpans) {
if (postImageSpan != imageSpan) {
MediaFile postMediaFile = postImageSpan.getMediaFile();
postMediaFile.setFeatured(false);
postMediaFile.setFeaturedInPost(false);
// TODO: remove this
mEditorFragmentListener.saveMediaFile(postMediaFile);
}
}
}
}
mediaFile.setFeaturedInPost(featuredInPostCheckBox.isChecked());
// TODO: remove this
mEditorFragmentListener.saveMediaFile(mediaFile);
}
});
builder.setNegativeButton(getString(android.R.string.cancel), new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int whichButton) {
dialog.dismiss();
}
});
AlertDialog alertDialog = builder.create();
alertDialog.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN);
alertDialog.show();
}
@Override
public boolean onTouch(View v, MotionEvent event) {
float pos = event.getY();
if (event.getAction() == 0)
mLastYPos = pos;
if (event.getAction() > 1) {
int scrollThreshold = DisplayUtils.dpToPx(getActivity(), 2);
if (((mLastYPos - pos) > scrollThreshold) || ((pos - mLastYPos) > scrollThreshold))
mScrollDetected = true;
}
mLastYPos = pos;
if (event.getAction() == MotionEvent.ACTION_UP) {
ActionBar actionBar = getActionBar();
if (actionBar != null && actionBar.isShowing()) {
setContentEditingModeVisible(true);
return false;
}
}
if (event.getAction() == MotionEvent.ACTION_UP && !mScrollDetected) {
Layout layout = ((TextView) v).getLayout();
int x = (int) event.getX();
int y = (int) event.getY();
x += v.getScrollX();
y += v.getScrollY();
if (layout != null) {
int line = layout.getLineForVertical(y);
int charPosition = layout.getOffsetForHorizontal(line, x);
Spannable spannable = mContentEditText.getText();
if (spannable == null) {
return false;
}
// check if image span was tapped
WPImageSpan[] imageSpans = spannable.getSpans(charPosition, charPosition, WPImageSpan.class);
if (imageSpans.length != 0) {
final WPImageSpan imageSpan = imageSpans[0];
MediaFile mediaFile = imageSpan.getMediaFile();
if (mediaFile == null)
return false;
if (!mediaFile.isVideo()) {
LayoutInflater factory = LayoutInflater.from(getActivity());
final View alertView = factory.inflate(R.layout.alert_image_options, null);
if (alertView == null)
return false;
final EditText imageWidthText = (EditText) alertView.findViewById(R.id.imageWidthText);
final EditText titleText = (EditText) alertView.findViewById(R.id.title);
final EditText caption = (EditText) alertView.findViewById(R.id.caption);
final CheckBox featuredCheckBox = (CheckBox) alertView.findViewById(R.id.featuredImage);
final CheckBox featuredInPostCheckBox = (CheckBox) alertView.findViewById(R.id.featuredInPost);
// show featured image checkboxes if supported
if (mFeaturedImageSupported) {
featuredCheckBox.setVisibility(View.VISIBLE);
featuredInPostCheckBox.setVisibility(View.VISIBLE);
}
featuredCheckBox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
if (isChecked) {
featuredInPostCheckBox.setVisibility(View.VISIBLE);
} else {
featuredInPostCheckBox.setVisibility(View.GONE);
}
}
});
final SeekBar seekBar = (SeekBar) alertView.findViewById(R.id.imageWidth);
final Spinner alignmentSpinner = (Spinner) alertView.findViewById(R.id.alignment_spinner);
ArrayAdapter<CharSequence> adapter =
ArrayAdapter.createFromResource(getActivity(), R.array.alignment_array,
android.R.layout.simple_spinner_item);
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
alignmentSpinner.setAdapter(adapter);
seekBar.setProgress(mediaFile.getWidth());
titleText.setText(mediaFile.getTitle());
caption.setText(mediaFile.getCaption());
featuredCheckBox.setChecked(mediaFile.isFeatured());
if (mediaFile.isFeatured()) {
featuredInPostCheckBox.setVisibility(View.VISIBLE);
} else {
featuredInPostCheckBox.setVisibility(View.GONE);
}
featuredInPostCheckBox.setChecked(mediaFile.isFeaturedInPost());
alignmentSpinner.setSelection(mediaFile.getHorizontalAlignment(), true);
final int maxWidth = MediaUtils.getMaximumImageWidth(getActivity(),
imageSpan.getImageSource(), mBlogSettingMaxImageWidth);
seekBar.setMax(maxWidth / 10);
imageWidthText.setText(String.format(Locale.US, "%dpx", maxWidth));
if (mediaFile.getWidth() != 0) {
seekBar.setProgress(mediaFile.getWidth() / 10);
}
seekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
@Override
public void onStopTrackingTouch(SeekBar seekBar) {
}
@Override
public void onStartTrackingTouch(SeekBar seekBar) {
}
@Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
if (progress == 0) {
progress = 1;
}
imageWidthText.setText(String.format(Locale.US, "%dpx", progress * 10));
}
});
imageWidthText.setOnFocusChangeListener(new View.OnFocusChangeListener() {
@Override
public void onFocusChange(View v, boolean hasFocus) {
if (hasFocus) {
imageWidthText.setText("");
}
}
});
imageWidthText.setOnEditorActionListener(new TextView.OnEditorActionListener() {
@Override
public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
int width = getEditTextIntegerClamped(imageWidthText, 10, maxWidth);
seekBar.setProgress(width / 10);
imageWidthText.setSelection((String.valueOf(width).length()));
InputMethodManager imm = (InputMethodManager) getActivity()
.getSystemService(Context.INPUT_METHOD_SERVICE);
imm.hideSoftInputFromWindow(imageWidthText.getWindowToken(),
InputMethodManager.RESULT_UNCHANGED_SHOWN);
return true;
}
});
showImageSettings(alertView, titleText, caption, imageWidthText, featuredCheckBox,
featuredInPostCheckBox, maxWidth, alignmentSpinner, imageSpan);
mScrollDetected = false;
return true;
}
} else {
mContentEditText.setMovementMethod(ArrowKeyMovementMethod.getInstance());
int selectionStart = mContentEditText.getSelectionStart();
if (selectionStart >= 0 && mContentEditText.getSelectionEnd() >= selectionStart)
mContentEditText.setSelection(selectionStart, mContentEditText.getSelectionEnd());
}
// get media gallery spans
MediaGalleryImageSpan[] gallerySpans = spannable.getSpans(charPosition, charPosition, MediaGalleryImageSpan.class);
if (gallerySpans.length > 0) {
final MediaGalleryImageSpan gallerySpan = gallerySpans[0];
Intent intent = new Intent(ACTION_MEDIA_GALLERY_TOUCHED);
intent.putExtra(EXTRA_MEDIA_GALLERY, gallerySpan.getMediaGallery());
getActivity().sendBroadcast(intent);
}
}
} else if (event.getAction() == 1) {
mScrollDetected = false;
}
return false;
}
@Override
public void afterTextChanged(Editable s) {
int position = Selection.getSelectionStart(mContentEditText.getText());
if ((mIsBackspace && position != 1) || mLastPosition == position || !mIsLocalDraft)
return;
if (position < 0) {
position = 0;
}
mLastPosition = position;
if (position > 0) {
if (mStyleStart > position) {
mStyleStart = position - 1;
}
boolean shouldBold = mBoldToggleButton.isChecked();
boolean shouldEm = mEmToggleButton.isChecked();
boolean shouldUnderline = mUnderlineToggleButton.isChecked();
boolean shouldStrike = mStrikeToggleButton.isChecked();
boolean shouldQuote = mBquoteToggleButton.isChecked();
Object[] allSpans = s.getSpans(mStyleStart, position, Object.class);
for (Object span : allSpans) {
if (span instanceof StyleSpan) {
StyleSpan styleSpan = (StyleSpan) span;
if (styleSpan.getStyle() == Typeface.BOLD)
shouldBold = false;
else if (styleSpan.getStyle() == Typeface.ITALIC)
shouldEm = false;
} else if (span instanceof WPUnderlineSpan) {
shouldUnderline = false;
} else if (span instanceof StrikethroughSpan) {
shouldStrike = false;
} else if (span instanceof QuoteSpan) {
shouldQuote = false;
}
}
if (shouldBold)
s.setSpan(new StyleSpan(android.graphics.Typeface.BOLD), mStyleStart, position, Spannable.SPAN_INCLUSIVE_INCLUSIVE);
if (shouldEm)
s.setSpan(new StyleSpan(android.graphics.Typeface.ITALIC), mStyleStart, position, Spannable.SPAN_INCLUSIVE_INCLUSIVE);
if (shouldUnderline)
s.setSpan(new WPUnderlineSpan(), mStyleStart, position, Spannable.SPAN_INCLUSIVE_INCLUSIVE);
if (shouldStrike)
s.setSpan(new StrikethroughSpan(), mStyleStart, position, Spannable.SPAN_INCLUSIVE_INCLUSIVE);
if (shouldQuote)
s.setSpan(new QuoteSpan(), mStyleStart, position, Spannable.SPAN_INCLUSIVE_INCLUSIVE);
}
}
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
mIsBackspace = (count - after == 1) || (s.length() == 0);
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
}
@Override
public void onSelectionChanged() {
if (!mIsLocalDraft) {
return;
}
final Spannable s = mContentEditText.getText();
if (s == null)
return;
// set toggle buttons if cursor is inside of a matching span
mStyleStart = mContentEditText.getSelectionStart();
Object[] spans = s.getSpans(mContentEditText.getSelectionStart(), mContentEditText.getSelectionStart(), Object.class);
mBoldToggleButton.setChecked(false);
mEmToggleButton.setChecked(false);
mBquoteToggleButton.setChecked(false);
mUnderlineToggleButton.setChecked(false);
mStrikeToggleButton.setChecked(false);
for (Object span : spans) {
if (span instanceof StyleSpan) {
StyleSpan ss = (StyleSpan) span;
if (ss.getStyle() == android.graphics.Typeface.BOLD) {
mBoldToggleButton.setChecked(true);
}
if (ss.getStyle() == android.graphics.Typeface.ITALIC) {
mEmToggleButton.setChecked(true);
}
}
if (span instanceof QuoteSpan) {
mBquoteToggleButton.setChecked(true);
}
if (span instanceof WPUnderlineSpan) {
mUnderlineToggleButton.setChecked(true);
}
if (span instanceof StrikethroughSpan) {
mStrikeToggleButton.setChecked(true);
}
}
}
private int getEditTextIntegerClamped(EditText editText, int min, int max) {
int width = 10;
try {
if (editText.getText() != null)
width = Integer.parseInt(editText.getText().toString().replace("px", ""));
} catch (NumberFormatException e) {
AppLog.e(T.POSTS, e);
}
width = Math.min(max, Math.max(width, min));
return width;
}
private void loadWPImageSpanThumbnail(MediaFile mediaFile, String imageURL, ImageLoader imageLoader) {
if (mediaFile == null || imageURL == null) {
return;
}
final String mediaId = mediaFile.getMediaId();
if (mediaId == null) {
return;
}
final int maxThumbWidth = ImageUtils.getMaximumThumbnailWidthForEditor(getActivity());
imageLoader.get(imageURL, new ImageLoader.ImageListener() {
@Override
public void onErrorResponse(VolleyError arg0) {
}
@Override
public void onResponse(ImageLoader.ImageContainer container, boolean arg1) {
Bitmap downloadedBitmap = container.getBitmap();
if (downloadedBitmap == null) {
// no bitmap downloaded from the server.
return;
}
if (downloadedBitmap.getWidth() < MIN_THUMBNAIL_WIDTH) {
// Picture is too small. Show the placeholder in this case.
return;
}
Bitmap resizedBitmap;
// resize the downloaded bitmap
resizedBitmap = ImageUtils.getScaledBitmapAtLongestSide(downloadedBitmap, maxThumbWidth);
if (resizedBitmap == null) {
return;
}
final EditText editText = mContentEditText;
Editable s = editText.getText();
if (s == null) {
return;
}
WPImageSpan[] spans = s.getSpans(0, s.length(), WPImageSpan.class);
if (spans.length != 0 && getActivity() != null) {
for (WPImageSpan is : spans) {
MediaFile mediaFile = is.getMediaFile();
if (mediaFile == null) {
continue;
}
if (mediaId.equals(mediaFile.getMediaId()) && !is.isNetworkImageLoaded()) {
// replace the existing span with a new one with the correct image, re-add
// it to the same position.
int spanStart = is.getStartPosition();
int spanEnd = is.getEndPosition();
WPEditImageSpan imageSpan = new WPEditImageSpan(getActivity(), resizedBitmap,
is.getImageSource());
imageSpan.setMediaFile(is.getMediaFile());
imageSpan.setNetworkImageLoaded(true);
imageSpan.setPosition(spanStart, spanEnd);
s.removeSpan(is);
s.setSpan(imageSpan, spanStart, spanEnd + 1, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
break;
}
}
}
}
}, 0, 0);
}
@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
WPImageSpan[] spans = mContentEditText.getText().getSpans(0, mContentEditText.getText().length(), WPEditImageSpan.class);
if (spans != null && spans.length > 0) {
outState.putParcelableArray(KEY_IMAGE_SPANS, spans);
}
outState.putInt(KEY_START, mContentEditText.getSelectionStart());
outState.putInt(KEY_END, mContentEditText.getSelectionEnd());
outState.putString(KEY_CONTENT, mContentEditText.getText().toString());
}
private class AddMediaFileTask extends AsyncTask<Void, Void, WPEditImageSpan> {
private MediaFile mMediaFile;
private String mImageUrl;
private ImageLoader mImageLoader;
private int mStart;
private int mEnd;
public AddMediaFileTask(MediaFile mediaFile, String imageUrl, ImageLoader imageLoader, int start, int end) {
mMediaFile = mediaFile;
mImageUrl = imageUrl;
mImageLoader = imageLoader;
mStart = start;
mEnd = end;
}
protected WPEditImageSpan doInBackground(Void... voids) {
mMediaFile.setFileURL(mImageUrl);
mMediaFile.setFilePath(mImageUrl);
WPEditImageSpan imageSpan = createWPEditImageSpan(getActivity(), mMediaFile);
mEditorFragmentListener.saveMediaFile(mMediaFile);
return imageSpan;
}
protected void onPostExecute(WPEditImageSpan imageSpan) {
if (imageSpan == null) {
if (isAdded()) {
ToastUtils.showToast(getActivity(), R.string.alert_error_adding_media, Duration.LONG);
}
return ;
}
// Insert the WPImageSpan in the content field
int selectionStart = mStart;
int selectionEnd = mEnd;
if (selectionStart > selectionEnd) {
int temp = selectionEnd;
selectionEnd = selectionStart;
selectionStart = temp;
}
imageSpan.setPosition(selectionStart, selectionEnd);
int line, column = 0;
if (mContentEditText.getLayout() != null) {
line = mContentEditText.getLayout().getLineForOffset(selectionStart);
column = selectionStart - mContentEditText.getLayout().getLineStart(line);
}
Editable s = mContentEditText.getText();
if (s == null) {
return;
}
WPImageSpan[] imageSpans = s.getSpans(selectionStart, selectionEnd, WPImageSpan.class);
if (imageSpans.length != 0) {
// insert a few line breaks if the cursor is already on an image
s.insert(selectionEnd, "\n\n");
selectionStart = selectionStart + 2;
selectionEnd = selectionEnd + 2;
} else if (column != 0) {
// insert one line break if the cursor is not at the first column
s.insert(selectionEnd, "\n");
selectionStart = selectionStart + 1;
selectionEnd = selectionEnd + 1;
}
s.insert(selectionStart, " ");
s.setSpan(imageSpan, selectionStart, selectionEnd + 1, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
AlignmentSpan.Standard as = new AlignmentSpan.Standard(Layout.Alignment.ALIGN_CENTER);
s.setSpan(as, selectionStart, selectionEnd + 1, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
s.insert(selectionEnd + 1, "\n\n");
// Fetch and replace the WPImageSpan if it's a remote media
if (mImageLoader != null && URLUtil.isNetworkUrl(mImageUrl)) {
loadWPImageSpanThumbnail(mMediaFile, mImageUrl, mImageLoader);
}
}
}
public void addMediaFile(final MediaFile mediaFile, final String imageUrl, final ImageLoader imageLoader,
final int start, final int end) {
AddMediaFileTask addMediaFileTask = new AddMediaFileTask(mediaFile, imageUrl, imageLoader, start, end);
addMediaFileTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
}
@Override
public void appendMediaFile(final MediaFile mediaFile, final String imageUrl, final ImageLoader imageLoader) {
addMediaFile(mediaFile, imageUrl, imageLoader, mContentEditText.getSelectionStart(), mContentEditText.getSelectionEnd());
}
@Override
public void appendGallery(MediaGallery mediaGallery) {
Editable editableText = mContentEditText.getText();
if (editableText == null) {
return;
}
int selectionStart = mContentEditText.getSelectionStart();
int selectionEnd = mContentEditText.getSelectionEnd();
if (selectionStart > selectionEnd) {
int temp = selectionEnd;
selectionEnd = selectionStart;
selectionStart = temp;
}
int line, column = 0;
if (mContentEditText.getLayout() != null) {
line = mContentEditText.getLayout().getLineForOffset(selectionStart);
column = mContentEditText.getSelectionStart() - mContentEditText.getLayout().getLineStart(line);
}
if (column != 0) {
// insert one line break if the cursor is not at the first column
editableText.insert(selectionEnd, "\n");
selectionStart = selectionStart + 1;
selectionEnd = selectionEnd + 1;
}
editableText.insert(selectionStart, " ");
MediaGalleryImageSpan is = new MediaGalleryImageSpan(getActivity(), mediaGallery,
R.drawable.legacy_icon_mediagallery_placeholder);
editableText.setSpan(is, selectionStart, selectionEnd + 1, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
AlignmentSpan.Standard as = new AlignmentSpan.Standard(Layout.Alignment.ALIGN_CENTER);
editableText.setSpan(as, selectionStart, selectionEnd + 1, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
editableText.insert(selectionEnd + 1, "\n\n");
}
@Override
public void setUrlForVideoPressId(String videoPressId, String url, String posterUrl) {
}
@Override
public boolean isUploadingMedia() {
return false;
}
@Override
public boolean hasFailedMediaUploads() {
return false;
}
@Override
public void removeAllFailedMediaUploads() {}
@Override
public void setTitlePlaceholder(CharSequence text) {
}
@Override
public void setContentPlaceholder(CharSequence text) {
}
@Override
public boolean isActionInProgress() {
return false;
}
}