package kr.kdev.dg1s.biowiki.ui.posts; import android.app.Activity; 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.Build; import android.os.Build.VERSION_CODES; import android.os.Bundle; import android.os.Environment; import android.text.Editable; import android.text.Layout; import android.text.Selection; import android.text.Spannable; import android.text.SpannableStringBuilder; import android.text.TextUtils; import android.text.TextWatcher; import android.text.method.ArrowKeyMovementMethod; import android.text.style.AlignmentSpan; import android.text.style.CharacterStyle; import android.text.style.QuoteSpan; import android.text.style.StrikethroughSpan; import android.text.style.StyleSpan; import android.text.style.URLSpan; import android.view.ContextMenu; import android.view.KeyEvent; import android.view.LayoutInflater; import android.view.MenuItem; 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.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.Toast; import android.widget.ToggleButton; import com.actionbarsherlock.app.ActionBar; import com.actionbarsherlock.app.SherlockFragment; import com.android.volley.VolleyError; import com.android.volley.toolbox.ImageLoader; import org.wordpress.passcodelock.AppLockManager; import org.xmlrpc.android.ApiHelper; import java.io.File; import java.io.Serializable; import java.util.ArrayList; import java.util.List; import java.util.Vector; import kr.kdev.dg1s.biowiki.BioWiki; import kr.kdev.dg1s.biowiki.Constants; import kr.kdev.dg1s.biowiki.R; import kr.kdev.dg1s.biowiki.models.Blog; import kr.kdev.dg1s.biowiki.models.MediaFile; import kr.kdev.dg1s.biowiki.models.MediaGallery; import kr.kdev.dg1s.biowiki.models.Post; import kr.kdev.dg1s.biowiki.ui.media.MediaGalleryActivity; import kr.kdev.dg1s.biowiki.ui.media.MediaGalleryPickerActivity; import kr.kdev.dg1s.biowiki.util.AppLog; import kr.kdev.dg1s.biowiki.util.BWEditText; import kr.kdev.dg1s.biowiki.util.BWHtml; import kr.kdev.dg1s.biowiki.util.BWImageSpan; import kr.kdev.dg1s.biowiki.util.BWMobileStatsUtil; import kr.kdev.dg1s.biowiki.util.BWUnderlineSpan; import kr.kdev.dg1s.biowiki.util.DeviceUtils; import kr.kdev.dg1s.biowiki.util.DisplayUtils; import kr.kdev.dg1s.biowiki.util.ImageHelper; import kr.kdev.dg1s.biowiki.util.MediaGalleryImageSpan; import kr.kdev.dg1s.biowiki.util.MediaUtils; import kr.kdev.dg1s.biowiki.util.StringUtils; public class EditPostContentFragment extends SherlockFragment implements TextWatcher, BWEditText.OnSelectionChangedListener, View.OnTouchListener { public static final String NEW_MEDIA_GALLERY = "NEW_MEDIA_GALLERY"; public static final String NEW_MEDIA_GALLERY_EXTRA_IDS = "NEW_MEDIA_GALLERY_EXTRA_IDS"; public static final String NEW_MEDIA_POST = "NEW_MEDIA_POST"; public static final String NEW_MEDIA_POST_EXTRA = "NEW_MEDIA_POST_ID"; private static final int ACTIVITY_REQUEST_CODE_CREATE_LINK = 4; 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 static final int CONTENT_ANIMATION_DURATION = 250; EditPostActivity mActivity; private View mRootView; private BWEditText mContentEditText; private Button mAddPictureButton; private EditText mTitleEditText; private ToggleButton mBoldToggleButton, mEmToggleButton, mBquoteToggleButton; private ToggleButton mUnderlineToggleButton, mStrikeToggleButton; private LinearLayout mFormatBar, mPostContentLinearLayout, mPostSettingsLinearLayout; private boolean mIsBackspace; private boolean mScrollDetected; private String mMediaCapturePath = ""; private int mStyleStart, mSelectionStart, mSelectionEnd, mFullViewBottom; /** * Formatting bar */ private View.OnClickListener mFormatBarButtonClickListener = new View.OnClickListener() { @Override public void onClick(View v) { int id = v.getId(); if (id == R.id.bold) { onFormatButtonClick(mBoldToggleButton, TAG_FORMAT_BAR_BUTTON_STRONG); trackFormatButtonClick(BWMobileStatsUtil.StatsPropertyPostDetailClickedKeyboardToolbarBoldButton); } else if (id == R.id.em) { onFormatButtonClick(mEmToggleButton, TAG_FORMAT_BAR_BUTTON_EM); trackFormatButtonClick(BWMobileStatsUtil.StatsPropertyPostDetailClickedKeyboardToolbarItalicButton); } else if (id == R.id.underline) { onFormatButtonClick(mUnderlineToggleButton, TAG_FORMAT_BAR_BUTTON_UNDERLINE); trackFormatButtonClick(BWMobileStatsUtil.StatsPropertyPostDetailClickedKeyboardToolbarUnderlineButton); } else if (id == R.id.strike) { onFormatButtonClick(mStrikeToggleButton, TAG_FORMAT_BAR_BUTTON_STRIKE); trackFormatButtonClick(BWMobileStatsUtil.StatsPropertyPostDetailClickedKeyboardToolbarDelButton); } else if (id == R.id.bquote) { onFormatButtonClick(mBquoteToggleButton, TAG_FORMAT_BAR_BUTTON_QUOTE); trackFormatButtonClick(BWMobileStatsUtil.StatsPropertyPostDetailClickedKeyboardToolbarBlockquoteButton); } else if (id == R.id.more) { mSelectionEnd = mContentEditText.getSelectionEnd(); Editable str = mContentEditText.getText(); if (str != null) { if (mSelectionEnd > str.length()) mSelectionEnd = str.length(); str.insert(mSelectionEnd, "\n<!--more-->\n"); } trackFormatButtonClick(BWMobileStatsUtil.StatsPropertyPostDetailClickedKeyboardToolbarMoreButton); } else if (id == R.id.link) { 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); } } trackFormatButtonClick(BWMobileStatsUtil.StatsPropertyPostDetailClickedKeyboardToolbarLinkButton); startActivityForResult(i, ACTIVITY_REQUEST_CODE_CREATE_LINK); } else if (id == R.id.addPictureButton) { mAddPictureButton.performLongClick(); trackFormatButtonClick(BWMobileStatsUtil.StatsPropertyPostDetailClickedKeyboardToolbarPictureButton); } } }; private int mLastPosition = -1, mQuickMediaType = -1; private float mLastYPos = 0; private ViewTreeObserver.OnGlobalLayoutListener mGlobalLayoutListener = new ViewTreeObserver.OnGlobalLayoutListener() { public void onGlobalLayout() { mRootView.getViewTreeObserver().removeGlobalOnLayoutListener(this); mFullViewBottom = mRootView.getBottom(); } }; @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { mActivity = (EditPostActivity) getActivity(); 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.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 if (actionId == EditorInfo.IME_ACTION_NEXT && mActivity.getSupportActionBar().isShowing()) { setContentEditingModeVisible(true); } return false; } }); mContentEditText = (BWEditText) rootView.findViewById(R.id.post_content); if (Build.VERSION.SDK_INT <= VERSION_CODES.GINGERBREAD_MR1) { mContentEditText.setBackgroundResource(android.R.drawable.editbox_background_normal); LinearLayout.LayoutParams layoutParams = (LinearLayout.LayoutParams) mContentEditText.getLayoutParams(); int sideMargin = mActivity.getResources().getDimensionPixelSize( R.dimen.post_editor_content_side_margin_gingerbread); layoutParams.setMargins(sideMargin, layoutParams.topMargin, sideMargin, layoutParams.bottomMargin); mContentEditText.setLayoutParams(layoutParams); } 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) { mActivity.showPostSettings(); } }); 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.setOnSelectionChangedListener(this); mContentEditText.setOnTouchListener(this); mContentEditText.addTextChangedListener(this); mContentEditText.setOnEditTextImeBackListener(new BWEditText.EditTextImeBackListener() { @Override public void onImeBack(BWEditText 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 if (mRootView.getBottom() < mFullViewBottom && !mActivity.getSupportActionBar().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); Post post = mActivity.getPost(); if (post != null) { if (!TextUtils.isEmpty(post.getContent())) { if (post.isLocalDraft()) mContentEditText.setText(BWHtml.fromHtml(post.getContent().replaceAll("\uFFFC", ""), mActivity, post)); else mContentEditText.setText(post.getContent().replaceAll("\uFFFC", "")); } if (!TextUtils.isEmpty(post.getTitle())) { mTitleEditText.setText(post.getTitle()); } postSettingsButton.setText(post.isPage() ? R.string.page_settings : R.string.post_settings); } // Check for Android share action String action = mActivity.getIntent().getAction(); if (mActivity.getIntent().getExtras() != null) mQuickMediaType = mActivity.getIntent().getExtras().getInt("quick-media", -1); if (Intent.ACTION_SEND.equals(action) || Intent.ACTION_SEND_MULTIPLE.equals(action)) setPostContentFromShareAction(); else if (NEW_MEDIA_GALLERY.equals(action)) prepareMediaGallery(); else if (NEW_MEDIA_POST.equals(action)) prepareMediaPost(); else if (mQuickMediaType >= 0) { // User selected a 'Quick (media type)' option in the menu drawer if (mQuickMediaType == Constants.QUICK_POST_PHOTO_CAMERA) launchCamera(); else if (mQuickMediaType == Constants.QUICK_POST_PHOTO_LIBRARY) launchPictureLibrary(); else if (mQuickMediaType == Constants.QUICK_POST_VIDEO_CAMERA) launchVideoCamera(); else if (mQuickMediaType == Constants.QUICK_POST_VIDEO_LIBRARY) launchVideoLibrary(); if (post != null) { if (mQuickMediaType == Constants.QUICK_POST_PHOTO_CAMERA || mQuickMediaType == Constants.QUICK_POST_PHOTO_LIBRARY) post.setQuickPostType(Post.QUICK_MEDIA_TYPE_PHOTO); else if (mQuickMediaType == Constants.QUICK_POST_VIDEO_CAMERA || mQuickMediaType == Constants.QUICK_POST_VIDEO_LIBRARY) post.setQuickPostType(Post.QUICK_MEDIA_TYPE_VIDEO); } } View editbutton = rootView.findViewById(R.id.post_settings_button); editbutton.performClick(); return rootView; } @Override public void onViewCreated(View view, Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); mRootView = view; mRootView.getViewTreeObserver().addOnGlobalLayoutListener(mGlobalLayoutListener); } public void setContentEditingModeVisible(boolean isVisible) { if (mActivity == null) return; ActionBar actionBar = mActivity.getSupportActionBar(); 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); 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); mActivity.supportInvalidateOptionsMenu(); actionBar.show(); } } public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) { menu.add(0, 0, 0, getResources().getText(R.string.select_photo)); if (DeviceUtils.getInstance().hasCamera(getActivity())) { menu.add(0, 1, 0, getResources().getText(R.string.media_add_popup_capture_photo)); } menu.add(0, 2, 0, getResources().getText(R.string.select_video)); if (DeviceUtils.getInstance().hasCamera(getActivity())) { menu.add(0, 3, 0, getResources().getText(R.string.media_add_popup_capture_video)); } menu.add(0, 4, 0, getResources().getText(R.string.media_add_new_media_gallery)); menu.add(0, 5, 0, getResources().getText(R.string.select_from_media_library)); } @Override public void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); if (data != null || ((requestCode == MediaUtils.RequestCode.ACTIVITY_REQUEST_CODE_TAKE_PHOTO || requestCode == MediaUtils.RequestCode.ACTIVITY_REQUEST_CODE_TAKE_VIDEO))) { Bundle extras; switch (requestCode) { case MediaGalleryActivity.REQUEST_CODE: if (resultCode == Activity.RESULT_OK) { handleMediaGalleryResult(data); } break; case MediaGalleryPickerActivity.REQUEST_CODE: if (resultCode == Activity.RESULT_OK) { handleMediaGalleryPickerResult(data); } break; case MediaUtils.RequestCode.ACTIVITY_REQUEST_CODE_PICTURE_LIBRARY: Uri imageUri = data.getData(); fetchMedia(imageUri); break; case MediaUtils.RequestCode.ACTIVITY_REQUEST_CODE_TAKE_PHOTO: if (resultCode == Activity.RESULT_OK) { try { File f = new File(mMediaCapturePath); Uri capturedImageUri = Uri.fromFile(f); if (!addMedia(capturedImageUri, null)) Toast.makeText(getActivity(), getResources().getText(R.string.gallery_error), Toast.LENGTH_SHORT).show(); getActivity().sendBroadcast(new Intent(Intent.ACTION_MEDIA_MOUNTED, Uri.parse("file://" + Environment.getExternalStorageDirectory()))); } catch (RuntimeException e) { AppLog.e(AppLog.T.POSTS, e); } catch (OutOfMemoryError e) { AppLog.e(AppLog.T.POSTS, e); } } else if (mActivity != null && mQuickMediaType > -1 && TextUtils.isEmpty(mContentEditText.getText())) { // Quick Photo was cancelled, delete post and finish activity BioWiki.wpDB.deletePost(mActivity.getPost()); mActivity.finish(); } break; case MediaUtils.RequestCode.ACTIVITY_REQUEST_CODE_VIDEO_LIBRARY: Uri videoUri = data.getData(); fetchMedia(videoUri); break; case MediaUtils.RequestCode.ACTIVITY_REQUEST_CODE_TAKE_VIDEO: if (resultCode == Activity.RESULT_OK) { Uri capturedVideoUri = MediaUtils.getLastRecordedVideoUri(getActivity()); if (!addMedia(capturedVideoUri, null)) Toast.makeText(getActivity(), getResources().getText(R.string.gallery_error), Toast.LENGTH_SHORT).show(); } else if (mActivity != null && mQuickMediaType > -1 && TextUtils.isEmpty(mContentEditText.getText())) { // Quick Photo was cancelled, delete post and finish activity BioWiki.wpDB.deletePost(mActivity.getPost()); mActivity.finish(); } break; case ACTIVITY_REQUEST_CODE_CREATE_LINK: try { extras = data.getExtras(); if (extras == null) return; String linkURL = extras.getString("linkURL"); if (linkURL != null && !linkURL.equals("http://") && !linkURL.equals("")) { if (mSelectionStart > mSelectionEnd) { int temp = mSelectionEnd; mSelectionEnd = mSelectionStart; mSelectionStart = temp; } Editable str = mContentEditText.getText(); if (str == null) return; if (mActivity.getPost().isLocalDraft()) { if (extras.getString("linkText") == null) { if (mSelectionStart < mSelectionEnd) str.delete(mSelectionStart, mSelectionEnd); str.insert(mSelectionStart, linkURL); str.setSpan(new URLSpan(linkURL), mSelectionStart, mSelectionStart + linkURL.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); mContentEditText.setSelection(mSelectionStart + linkURL.length()); } else { String linkText = extras.getString("linkText"); if (linkText == null) return; if (mSelectionStart < mSelectionEnd) str.delete(mSelectionStart, mSelectionEnd); str.insert(mSelectionStart, linkText); str.setSpan(new URLSpan(linkURL), mSelectionStart, mSelectionStart + linkText.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); mContentEditText.setSelection(mSelectionStart + linkText.length()); } } else { if (extras.getString("linkText") == null) { if (mSelectionStart < mSelectionEnd) str.delete(mSelectionStart, mSelectionEnd); String urlHTML = "<a href=\"" + linkURL + "\">" + linkURL + "</a>"; str.insert(mSelectionStart, urlHTML); mContentEditText.setSelection(mSelectionStart + urlHTML.length()); } else { String linkText = extras.getString("linkText"); if (mSelectionStart < mSelectionEnd) str.delete(mSelectionStart, mSelectionEnd); String urlHTML = "<a href=\"" + linkURL + "\">" + linkText + "</a>"; str.insert(mSelectionStart, urlHTML); mContentEditText.setSelection(mSelectionStart + urlHTML.length()); } } } } catch (RuntimeException e) { AppLog.e(AppLog.T.POSTS, e); } break; } } } @Override public boolean onContextItemSelected(MenuItem item) { switch (item.getItemId()) { case 0: launchPictureLibrary(); return true; case 1: launchCamera(); return true; case 2: launchVideoLibrary(); return true; case 3: launchVideoCamera(); return true; case 4: startMediaGalleryActivity(null); return true; case 5: startMediaGalleryAddActivity(); return true; } return false; } protected boolean hasActivity() { return (getActivity() != null && !isRemoving()); } protected void setPostContentFromShareAction() { Intent intent = mActivity.getIntent(); // Check for shared text String text = intent.getStringExtra(Intent.EXTRA_TEXT); String title = intent.getStringExtra(Intent.EXTRA_SUBJECT); if (text != null) { if (title != null) { mTitleEditText.setText(title); } if (text.contains("youtube_gdata")) { // Just use the URL for YouTube links for oEmbed support mContentEditText.setText(text); } else { // add link tag around URLs, trac #64 text = text.replaceAll("((http|https|ftp|mailto):\\S+)", "<a href=\"$1\">$1</a>"); mContentEditText.setText(BWHtml.fromHtml(StringUtils.addPTags(text), getActivity(), mActivity.getPost())); } } // Check for shared media if (intent.hasExtra(Intent.EXTRA_STREAM)) { String action = intent.getAction(); String type = intent.getType(); ArrayList<Uri> sharedUris; if (Intent.ACTION_SEND_MULTIPLE.equals(action)) { sharedUris = intent.getParcelableArrayListExtra((Intent.EXTRA_STREAM)); } else { // For a single media share, we only allow images and video types if (type != null && (type.startsWith("image") || type.startsWith("video"))) { sharedUris = new ArrayList<Uri>(); sharedUris.add((Uri) intent.getParcelableExtra(Intent.EXTRA_STREAM)); } else { return; } } if (sharedUris != null) { List<Serializable> params = new Vector<Serializable>(); params.add(sharedUris); params.add(type); new processAttachmentsTask().execute(params); } } } public void savePostContent(boolean isAutoSave) { Post post = mActivity.getPost(); if (post == null || mContentEditText.getText() == null) return; String title = (mTitleEditText.getText() != null) ? mTitleEditText.getText().toString() : ""; String content; Editable postContentEditable; try { postContentEditable = new SpannableStringBuilder(mContentEditText.getText()); } catch (IndexOutOfBoundsException e) { // A core android bug might cause an out of bounds exception, if so we'll just use the current editable // See https://code.google.com/p/android/issues/detail?id=5164 postContentEditable = mContentEditText.getText(); } if (postContentEditable == null) return; if (post.isLocalDraft()) { if (android.os.Build.VERSION.SDK_INT >= 14) { // remove suggestion spans, they cause craziness in BWHtml.toHTML(). CharacterStyle[] characterStyles = postContentEditable.getSpans(0, postContentEditable.length(), CharacterStyle.class); for (CharacterStyle characterStyle : characterStyles) { if (characterStyle.getClass().getName().equals("android.text.style.SuggestionSpan")) postContentEditable.removeSpan(characterStyle); } } content = BWHtml.toHtml(postContentEditable); // replace duplicate <p> tags so there's not duplicates, trac #86 content = content.replace("<p><p>", "<p>"); content = content.replace("</p></p>", "</p>"); content = content.replace("<br><br>", "<br>"); // sometimes the editor creates extra tags content = content.replace("</strong><strong>", "").replace("</em><em>", "").replace("</u><u>", "") .replace("</strike><strike>", "").replace("</blockquote><blockquote>", ""); } else { //content = (mContentEditText.getText() != null) ? mContentEditText.getText().toString() : ""; BWImageSpan[] imageSpans = postContentEditable.getSpans(0, postContentEditable.length(), BWImageSpan.class); if (imageSpans.length != 0) { for (BWImageSpan wpIS : imageSpans) { //images += wpIS.getImageSource().toString() + ","; MediaFile mediaFile = wpIS.getMediaFile(); if (mediaFile == null) continue; if (mediaFile.getMediaId() != null) { updateMediaFileOnServer(wpIS); } else { mediaFile.setFileName(wpIS.getImageSource().toString()); mediaFile.setFilePath(wpIS.getImageSource().toString()); mediaFile.save(); } int tagStart = postContentEditable.getSpanStart(wpIS); if (!isAutoSave) { postContentEditable.removeSpan(wpIS); // network image has a mediaId if (mediaFile.getMediaId() != null && mediaFile.getMediaId().length() > 0) { postContentEditable.insert(tagStart, BWHtml.getContent(wpIS)); } else { // local image for upload postContentEditable.insert(tagStart, "<img android-uri=\"" + wpIS.getImageSource().toString() + "\" />"); } } } } content = postContentEditable.toString(); } if (!isAutoSave) { // Add gallery shortcode MediaGalleryImageSpan[] gallerySpans = postContentEditable.getSpans(0, postContentEditable.length(), MediaGalleryImageSpan.class); for (MediaGalleryImageSpan gallerySpan : gallerySpans) { int start = postContentEditable.getSpanStart(gallerySpan); postContentEditable.removeSpan(gallerySpan); postContentEditable.insert(start, BWHtml.getGalleryShortcode(gallerySpan)); } } String moreTag = "<!--more-->"; post.setTitle(title); // split up the post content if there's a more tag if (post.isLocalDraft() && content.contains(moreTag)) { post.setDescription(content.substring(0, content.indexOf(moreTag))); post.setMoreText(content.substring(content.indexOf(moreTag) + moreTag.length(), content.length())); } else { post.setDescription(content); post.setMoreText(""); } if (!post.isLocalDraft()) post.setLocalChange(true); BioWiki.wpDB.updatePost(post); } 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 launchPictureLibrary() { MediaUtils.launchPictureLibrary(this); AppLockManager.getInstance().setExtendedTimeout(); } private void launchCamera() { MediaUtils.launchCamera(this, new MediaUtils.LaunchCameraCallback() { @Override public void onMediaCapturePathReady(String mediaCapturePath) { mMediaCapturePath = mediaCapturePath; AppLockManager.getInstance().setExtendedTimeout(); } }); } private void launchVideoLibrary() { MediaUtils.launchVideoLibrary(this); AppLockManager.getInstance().setExtendedTimeout(); } private void launchVideoCamera() { MediaUtils.launchVideoCamera(this); AppLockManager.getInstance().setExtendedTimeout(); } private void fetchMedia(Uri mediaUri) { if (!MediaUtils.isInMediaStore(mediaUri)) { // Create an AsyncTask to download the file if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) new DownloadMediaTask().executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, mediaUri); else new DownloadMediaTask().execute(mediaUri); } else { // It is a regular local image file if (!addMedia(mediaUri, null)) Toast.makeText(getActivity(), getResources().getText(R.string.gallery_error), Toast.LENGTH_SHORT).show(); } } private void prepareMediaGallery() { MediaGallery mediaGallery = new MediaGallery(); mediaGallery.setIds(getActivity().getIntent().getStringArrayListExtra(NEW_MEDIA_GALLERY_EXTRA_IDS)); startMediaGalleryActivity(mediaGallery); } private void prepareMediaPost() { String mediaId = getActivity().getIntent().getStringExtra(NEW_MEDIA_POST_EXTRA); addExistingMediaToEditor(mediaId); } private void addExistingMediaToEditor(String mediaId) { if (BioWiki.getCurrentBlog() == null) return; String blogId = String.valueOf(BioWiki.getCurrentBlog().getLocalTableBlogId()); BWImageSpan imageSpan = MediaUtils.prepareWPImageSpan(getActivity(), blogId, mediaId); if (imageSpan == null) return; // based on addMedia() 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); } Editable s = mContentEditText.getText(); if (s != null) { BWImageSpan[] gallerySpans = s.getSpans(selectionStart, selectionEnd, BWImageSpan.class); if (gallerySpans.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"); } // load image from server loadWPImageSpanThumbnail(imageSpan); } private void updateMediaFileOnServer(BWImageSpan wpIS) { Blog currentBlog = BioWiki.getCurrentBlog(); if (currentBlog == null || wpIS == null) return; MediaFile mf = wpIS.getMediaFile(); final String mediaId = mf.getMediaId(); final String title = mf.getTitle(); final String description = mf.getDescription(); final String caption = mf.getCaption(); ApiHelper.EditMediaItemTask task = new ApiHelper.EditMediaItemTask(mf.getMediaId(), mf.getTitle(), mf.getDescription(), mf.getCaption(), new ApiHelper.GenericCallback() { @Override public void onSuccess() { if (BioWiki.getCurrentBlog() == null) { return; } String localBlogTableIndex = String.valueOf(BioWiki.getCurrentBlog().getLocalTableBlogId()); BioWiki.wpDB.updateMediaFile(localBlogTableIndex, mediaId, title, description, caption); } @Override public void onFailure(ApiHelper.ErrorType errorType, String errorMessage, Throwable throwable) { if (hasActivity()) { Toast.makeText(getActivity(), R.string.media_edit_failure, Toast.LENGTH_LONG).show(); } } } ); List<Object> apiArgs = new ArrayList<Object>(); apiArgs.add(currentBlog); task.execute(apiArgs); } /** * Loads the thumbnail url in the imagespan from a server * */ private void loadWPImageSpanThumbnail(BWImageSpan imageSpan) { final int maxPictureWidthForContentEditor = 400; final int minPictureWidthForContentEditor = 200; MediaFile mediaFile = imageSpan.getMediaFile(); if (mediaFile == null) return; final String mediaId = mediaFile.getMediaId(); if (mediaId == null) return; String imageURL; if (BioWiki.getCurrentBlog() != null && BioWiki.getCurrentBlog().isPhotonCapable()) { String photonUrl = imageSpan.getImageSource().toString(); imageURL = StringUtils.getPhotonUrl(photonUrl, maxPictureWidthForContentEditor); } else { // Not a Jetpack or wpcom blog // imageURL = mediaFile.getThumbnailURL(); //do not use fileURL here since downloading picture // of big dimensions can result in OOM Exception imageURL = mediaFile.getFileURL() != null ? mediaFile.getFileURL() : mediaFile.getThumbnailURL(); } if (imageURL == null) return; BioWiki.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() < minPictureWidthForContentEditor) { //Picture is too small. Show the placeholder in this case. return; } Bitmap resizedBitmap; if (downloadedBitmap.getWidth() <= maxPictureWidthForContentEditor) { //bitmap is already small in size, do not resize. resizedBitmap = downloadedBitmap; } else { //resize the downloaded bitmap try { ImageHelper ih = new ImageHelper(); resizedBitmap = ih.getThumbnailForWPImageSpan(downloadedBitmap, 400); } catch (OutOfMemoryError er) { BWMobileStatsUtil.trackEventForSelfHostedAndWPCom(BWMobileStatsUtil.StatsEventMediaOutOfMemory); return; } } if (resizedBitmap == null) return; Editable s = mContentEditText.getText(); if (s == null) return; BWImageSpan[] spans = s.getSpans(0, s.length(), BWImageSpan.class); if (spans.length != 0) { for (BWImageSpan is : spans) { MediaFile mediaFile = is.getMediaFile(); if (mediaFile == null) continue; if (mediaId.equals(mediaFile.getMediaId()) && !is.isNetworkImageLoaded() && hasActivity()) { // replace the existing span with a new one with the correct image, re-add it to the same position. int spanStart = s.getSpanStart(is); int spanEnd = s.getSpanEnd(is); BWImageSpan imageSpan = new BWImageSpan(getActivity(), resizedBitmap, is.getImageSource()); imageSpan.setMediaFile(is.getMediaFile()); imageSpan.setNetworkImageLoaded(true); s.removeSpan(is); s.setSpan(imageSpan, spanStart, spanEnd, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); break; } } } } }, 0, 0); } private void startMediaGalleryActivity(MediaGallery mediaGallery) { Intent intent = new Intent(getActivity(), MediaGalleryActivity.class); intent.putExtra(MediaGalleryActivity.PARAMS_MEDIA_GALLERY, mediaGallery); if (mediaGallery == null) intent.putExtra(MediaGalleryActivity.PARAMS_LAUNCH_PICKER, true); startActivityForResult(intent, MediaGalleryActivity.REQUEST_CODE); } private void startMediaGalleryAddActivity() { Intent intent = new Intent(getActivity(), MediaGalleryPickerActivity.class); intent.putExtra(MediaGalleryPickerActivity.PARAM_SELECT_ONE_ITEM, true); startActivityForResult(intent, MediaGalleryPickerActivity.REQUEST_CODE); } private void handleMediaGalleryPickerResult(Intent data) { ArrayList<String> ids = data.getStringArrayListExtra(MediaGalleryPickerActivity.RESULT_IDS); if (ids == null || ids.size() == 0) return; String mediaId = ids.get(0); addExistingMediaToEditor(mediaId); } private void handleMediaGalleryResult(Intent data) { MediaGallery gallery = (MediaGallery) data.getSerializableExtra(MediaGalleryActivity.RESULT_MEDIA_GALLERY); // if blank gallery returned, don't add to span if (gallery == null || gallery.getIds().size() == 0) 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); } Editable s = mContentEditText.getText(); if (s == null) return; MediaGalleryImageSpan[] gallerySpans = s.getSpans(selectionStart, selectionEnd, MediaGalleryImageSpan.class); if (gallerySpans.length != 0) { for (MediaGalleryImageSpan gallerySpan : gallerySpans) { if (gallerySpan.getMediaGallery().getUniqueId() == gallery.getUniqueId()) { // replace the existing span with a new gallery, re-add it to the same position. gallerySpan.setMediaGallery(gallery); int spanStart = s.getSpanStart(gallerySpan); int spanEnd = s.getSpanEnd(gallerySpan); s.setSpan(gallerySpan, spanStart, spanEnd, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); } } return; } 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, " "); MediaGalleryImageSpan is = new MediaGalleryImageSpan(getActivity(), gallery); s.setSpan(is, 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"); } private boolean addMedia(Uri imageUri, SpannableStringBuilder ssb) { if (ssb != null && !MediaUtils.isInMediaStore(imageUri)) imageUri = MediaUtils.downloadExternalMedia(getActivity(), imageUri); if (imageUri == null) { return false; } Bitmap thumbnailBitmap; String mediaTitle; if (imageUri.toString().contains("video") && !MediaUtils.isInMediaStore(imageUri)) { thumbnailBitmap = BitmapFactory.decodeResource(getActivity().getResources(), R.drawable.media_movieclip); mediaTitle = getResources().getString(R.string.video); } else { ImageHelper ih = new ImageHelper(); thumbnailBitmap = ih.getThumbnailForWPImageSpan(getActivity(), imageUri.getEncodedPath()); if (thumbnailBitmap == null) return false; mediaTitle = ih.getTitleForWPImageSpan(getActivity(), imageUri.getEncodedPath()); } BWImageSpan is = new BWImageSpan(getActivity(), thumbnailBitmap, imageUri); MediaFile mediaFile = is.getMediaFile(); mediaFile.setPostID(mActivity.getPost().getLocalTablePostId()); mediaFile.setTitle(mediaTitle); mediaFile.setFilePath(is.getImageSource().toString()); MediaUtils.setWPImageSpanWidth(getActivity(), imageUri, is); if (imageUri.getEncodedPath() != null) mediaFile.setVideo(imageUri.getEncodedPath().contains("video")); mediaFile.save(); if (ssb != null) { ssb.append(" "); ssb.setSpan(is, ssb.length() - 1, ssb.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); AlignmentSpan.Standard as = new AlignmentSpan.Standard(Layout.Alignment.ALIGN_CENTER); ssb.setSpan(as, ssb.length() - 1, ssb.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); ssb.append("\n"); } else { int selectionStart = mContentEditText.getSelectionStart(); mStyleStart = selectionStart; int selectionEnd = mContentEditText.getSelectionEnd(); if (selectionStart > selectionEnd) { int temp = selectionEnd; selectionEnd = selectionStart; selectionStart = temp; } Editable s = mContentEditText.getText(); if (s == null) return false; int line, column = 0; if (mContentEditText.getLayout() != null) { line = mContentEditText.getLayout().getLineForOffset(selectionStart); column = mContentEditText.getSelectionStart() - mContentEditText.getLayout().getLineStart(line); } BWImageSpan[] image_spans = s.getSpans(selectionStart, selectionEnd, BWImageSpan.class); if (image_spans.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(is, 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"); } // Show the soft keyboard after adding media if (mActivity != null && !mActivity.getSupportActionBar().isShowing()) ((InputMethodManager) mActivity.getSystemService(Context.INPUT_METHOD_SERVICE)).toggleSoftInput(InputMethodManager.SHOW_FORCED, InputMethodManager.HIDE_IMPLICIT_ONLY); return true; } public void trackFormatButtonClick(String statPropertyName) { BWMobileStatsUtil.flagProperty(mActivity.getStatEventEditorClosed(), statPropertyName); } /** * 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 = BWUnderlineSpan.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 (mActivity.getPost().isLocalDraft()) { // 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(AppLog.T.POSTS, e); } catch (IllegalAccessException e) { AppLog.e(AppLog.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 */ @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) { if (mActivity != null && mActivity.getSupportActionBar().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 s = mContentEditText.getText(); if (s == null) return false; // check if image span was tapped BWImageSpan[] image_spans = s.getSpans(charPosition, charPosition, BWImageSpan.class); if (image_spans.length != 0) { final BWImageSpan span = image_spans[0]; MediaFile mediaFile = span.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 theme support it if (BioWiki.getCurrentBlog().isFeaturedImageCapable()) { 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); imageWidthText.setText(String.valueOf(mediaFile.getWidth()) + "px"); 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.getMinimumImageWidth(getActivity(), span.getImageSource()); seekBar.setMax(maxWidth / 10); 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(progress * 10 + "px"); } }); 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; } }); AlertDialog ad = new AlertDialog.Builder(getActivity()).setTitle(getString(R.string.image_settings)) .setView(alertView).setPositiveButton(getString(R.string.ok), new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int whichButton) { String title = (titleText.getText() != null) ? titleText.getText().toString() : ""; MediaFile mediaFile = span.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(); BWImageSpan[] postImageSpans = contentSpannable.getSpans(0, contentSpannable.length(), BWImageSpan.class); if (postImageSpans.length > 1) { for (BWImageSpan postImageSpan : postImageSpans) { if (postImageSpan != span) { MediaFile postMediaFile = postImageSpan.getMediaFile(); postMediaFile.setFeatured(false); postMediaFile.setFeaturedInPost(false); postMediaFile.save(); } } } } mediaFile.setFeaturedInPost(featuredInPostCheckBox.isChecked()); mediaFile.save(); } }).setNegativeButton(getString(R.string.cancel), new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int whichButton) { dialog.dismiss(); } }).create(); ad.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN); ad.show(); 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 = s.getSpans(charPosition, charPosition, MediaGalleryImageSpan.class); if (gallerySpans.length > 0) { final MediaGalleryImageSpan gallerySpan = gallerySpans[0]; startMediaGalleryActivity(gallerySpan.getMediaGallery()); } } } 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 || !mActivity.getPost().isLocalDraft()) 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 BWUnderlineSpan) { 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 BWUnderlineSpan(), 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 (mActivity.getPost() == null || !mActivity.getPost().isLocalDraft()) 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 BWUnderlineSpan) { 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(AppLog.T.POSTS, e); } width = Math.min(max, Math.max(width, min)); return width; } /** * Media */ private class processAttachmentsTask extends AsyncTask<List<?>, Void, SpannableStringBuilder> { protected void onPreExecute() { Toast.makeText(getActivity(), R.string.loading, Toast.LENGTH_SHORT).show(); } @Override protected SpannableStringBuilder doInBackground(List<?>... args) { ArrayList<?> multi_stream = (ArrayList<?>) args[0].get(0); String type = (String) args[0].get(1); SpannableStringBuilder ssb = new SpannableStringBuilder(); for (Object streamUri : multi_stream) { if (streamUri instanceof Uri) { Uri imageUri = (Uri) streamUri; if (type != null) { addMedia(imageUri, ssb); } } } return ssb; } protected void onPostExecute(SpannableStringBuilder ssb) { if (!hasActivity()) { return; } if (ssb != null && ssb.length() > 0) { Editable postContentEditable = mContentEditText.getText(); if (postContentEditable != null) { postContentEditable.insert(0, ssb); } } else { Toast.makeText(getActivity(), getResources().getText(R.string.gallery_error), Toast.LENGTH_SHORT) .show(); } } } private class DownloadMediaTask extends AsyncTask<Uri, Integer, Uri> { @Override protected Uri doInBackground(Uri... uris) { Uri imageUri = uris[0]; return MediaUtils.downloadExternalMedia(getActivity(), imageUri); } @Override protected void onPreExecute() { Toast.makeText(getActivity(), R.string.download, Toast.LENGTH_SHORT).show(); } protected void onPostExecute(Uri newUri) { if (!hasActivity()) { return; } if (newUri != null) { addMedia(newUri, null); } else { Toast.makeText(getActivity(), getString(R.string.error_downloading_image), Toast.LENGTH_SHORT).show(); } } } }