diff options
Diffstat (limited to 'libs/editor/WordPressEditor/src/main/java/org/wordpress/android/editor/ImageSettingsDialogFragment.java')
-rw-r--r-- | libs/editor/WordPressEditor/src/main/java/org/wordpress/android/editor/ImageSettingsDialogFragment.java | 431 |
1 files changed, 431 insertions, 0 deletions
diff --git a/libs/editor/WordPressEditor/src/main/java/org/wordpress/android/editor/ImageSettingsDialogFragment.java b/libs/editor/WordPressEditor/src/main/java/org/wordpress/android/editor/ImageSettingsDialogFragment.java new file mode 100644 index 000000000..70a995b01 --- /dev/null +++ b/libs/editor/WordPressEditor/src/main/java/org/wordpress/android/editor/ImageSettingsDialogFragment.java @@ -0,0 +1,431 @@ +package org.wordpress.android.editor; + +import android.app.DialogFragment; +import android.content.Context; +import android.content.DialogInterface; +import android.content.Intent; +import android.content.res.Configuration; +import android.net.Uri; +import android.os.Bundle; +import android.support.v7.app.ActionBar; +import android.support.v7.app.AlertDialog; +import android.support.v7.app.AppCompatActivity; +import android.view.KeyEvent; +import android.view.LayoutInflater; +import android.view.Menu; +import android.view.MenuInflater; +import android.view.MenuItem; +import android.view.View; +import android.view.ViewGroup; +import android.view.inputmethod.InputMethodManager; +import android.widget.CheckBox; +import android.widget.EditText; +import android.widget.ImageView; +import android.widget.SeekBar; +import android.widget.Spinner; +import android.widget.TextView; + +import org.json.JSONException; +import org.json.JSONObject; +import org.wordpress.android.util.AppLog; +import org.wordpress.android.util.MediaUtils; +import org.wordpress.android.util.ToastUtils; + +import java.util.Arrays; +import java.util.Map; + +/** + * A full-screen DialogFragment with image settings. + * + * Modifies the action bar - host activity must call {@link ImageSettingsDialogFragment#dismissFragment()} + * when the fragment is dismissed to restore it. + */ +public class ImageSettingsDialogFragment extends DialogFragment { + public static final int IMAGE_SETTINGS_DIALOG_REQUEST_CODE = 5; + public static final String IMAGE_SETTINGS_DIALOG_TAG = "image-settings"; + + private JSONObject mImageMeta; + private int mMaxImageWidth; + + private EditText mTitleText; + private EditText mCaptionText; + private EditText mAltText; + private Spinner mAlignmentSpinner; + private String[] mAlignmentKeyArray; + private EditText mLinkTo; + private EditText mWidthText; + private CheckBox mFeaturedCheckBox; + + private boolean mIsFeatured; + + private Map<String, String> mHttpHeaders; + + private CharSequence mPreviousActionBarTitle; + private boolean mPreviousHomeAsUpEnabled; + private View mPreviousCustomView; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setHasOptionsMenu(true); + + ActionBar actionBar = getActionBar(); + if (actionBar == null) { + return; + } + + actionBar.show(); + + mPreviousActionBarTitle = actionBar.getTitle(); + mPreviousCustomView = actionBar.getCustomView(); + + final int displayOptions = actionBar.getDisplayOptions(); + mPreviousHomeAsUpEnabled = (displayOptions & ActionBar.DISPLAY_HOME_AS_UP) != 0; + + actionBar.setTitle(R.string.image_settings); + actionBar.setDisplayHomeAsUpEnabled(true); + if (getResources().getBoolean(R.bool.show_extra_side_padding)) { + actionBar.setHomeAsUpIndicator(R.drawable.ic_close_padded); + } else { + actionBar.setHomeAsUpIndicator(R.drawable.ic_close_white_24dp); + } + + // Show custom view with padded Save button + actionBar.setDisplayShowCustomEnabled(true); + actionBar.setCustomView(R.layout.image_settings_formatbar); + + actionBar.getCustomView().findViewById(R.id.menu_save).setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + mImageMeta = extractMetaDataFromFields(mImageMeta); + + String imageRemoteId = ""; + try { + imageRemoteId = mImageMeta.getString("attachment_id"); + } catch (JSONException e) { + AppLog.e(AppLog.T.EDITOR, "Unable to retrieve featured image id from meta data"); + } + + Intent intent = new Intent(); + intent.putExtra("imageMeta", mImageMeta.toString()); + + mIsFeatured = mFeaturedCheckBox.isChecked(); + intent.putExtra("isFeatured", mIsFeatured); + + if (!imageRemoteId.isEmpty()) { + intent.putExtra("imageRemoteId", Integer.parseInt(imageRemoteId)); + } + + getTargetFragment().onActivityResult(getTargetRequestCode(), getTargetRequestCode(), intent); + + restorePreviousActionBar(); + getFragmentManager().popBackStack(); + ToastUtils.showToast(getActivity(), R.string.image_settings_save_toast); + } + }); + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + View view = inflater.inflate(R.layout.dialog_image_options, container, false); + + ImageView thumbnailImage = (ImageView) view.findViewById(R.id.image_thumbnail); + TextView filenameLabel = (TextView) view.findViewById(R.id.image_filename); + mTitleText = (EditText) view.findViewById(R.id.image_title); + mCaptionText = (EditText) view.findViewById(R.id.image_caption); + mAltText = (EditText) view.findViewById(R.id.image_alt_text); + mAlignmentSpinner = (Spinner) view.findViewById(R.id.alignment_spinner); + mLinkTo = (EditText) view.findViewById(R.id.image_link_to); + SeekBar widthSeekBar = (SeekBar) view.findViewById(R.id.image_width_seekbar); + mWidthText = (EditText) view.findViewById(R.id.image_width_text); + mFeaturedCheckBox = (CheckBox) view.findViewById(R.id.featuredImage); + + // Populate the dialog with existing values + Bundle bundle = getArguments(); + if (bundle != null) { + try { + mImageMeta = new JSONObject(bundle.getString("imageMeta")); + + mHttpHeaders = (Map) bundle.getSerializable("headerMap"); + + final String imageSrc = mImageMeta.getString("src"); + final String imageFilename = imageSrc.substring(imageSrc.lastIndexOf("/") + 1); + + loadThumbnail(imageSrc, thumbnailImage); + filenameLabel.setText(imageFilename); + + mTitleText.setText(mImageMeta.getString("title")); + mCaptionText.setText(mImageMeta.getString("caption")); + mAltText.setText(mImageMeta.getString("alt")); + + String alignment = mImageMeta.getString("align"); + mAlignmentKeyArray = getResources().getStringArray(R.array.alignment_key_array); + int alignmentIndex = Arrays.asList(mAlignmentKeyArray).indexOf(alignment); + mAlignmentSpinner.setSelection(alignmentIndex == -1 ? 0 : alignmentIndex); + + mLinkTo.setText(mImageMeta.getString("linkUrl")); + + mMaxImageWidth = MediaUtils.getMaximumImageWidth(mImageMeta.getInt("naturalWidth"), + bundle.getString("maxWidth")); + + setupWidthSeekBar(widthSeekBar, mWidthText, mImageMeta.getInt("width")); + + boolean featuredImageSupported = bundle.getBoolean("featuredImageSupported"); + if (featuredImageSupported) { + mFeaturedCheckBox.setVisibility(View.VISIBLE); + mIsFeatured = bundle.getBoolean("isFeatured", false); + mFeaturedCheckBox.setChecked(mIsFeatured); + } + } catch (JSONException e1) { + AppLog.d(AppLog.T.EDITOR, "Missing JSON properties"); + } + } + + mTitleText.requestFocus(); + + return view; + } + + @Override + public void onConfigurationChanged(Configuration newConfig) { + super.onConfigurationChanged(newConfig); + ActionBar actionBar = getActionBar(); + if (actionBar != null) { + actionBar.show(); + } + } + + @Override + public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { + if (menu != null) { + menu.clear(); + } + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + int id = item.getItemId(); + + if (id == android.R.id.home) { + dismissFragment(); + return true; + } + return super.onOptionsItemSelected(item); + } + + private ActionBar getActionBar() { + if (getActivity() instanceof AppCompatActivity) { + return ((AppCompatActivity) getActivity()).getSupportActionBar(); + } else { + return null; + } + } + + /** + * To be called when the fragment is being dismissed, either by ActionBar navigation or by pressing back in the + * navigation bar. + * Displays a confirmation dialog if there are unsaved changes, otherwise undoes the fragment's modifications to + * the ActionBar and restores the last visible fragment. + */ + public void dismissFragment() { + try { + JSONObject newImageMeta = extractMetaDataFromFields(new JSONObject()); + + for (int i = 0; i < newImageMeta.names().length(); i++) { + String name = newImageMeta.names().getString(i); + if (!newImageMeta.getString(name).equals(mImageMeta.getString(name))) { + showDiscardChangesDialog(); + return; + } + } + + if (mFeaturedCheckBox.isChecked() != mIsFeatured) { + // Featured image status has changed + showDiscardChangesDialog(); + return; + } + } catch (JSONException e) { + AppLog.d(AppLog.T.EDITOR, "Unable to update JSON array"); + } + + getTargetFragment().onActivityResult(getTargetRequestCode(), getTargetRequestCode(), null); + restorePreviousActionBar(); + getFragmentManager().popBackStack(); + } + + private void restorePreviousActionBar() { + ActionBar actionBar = getActionBar(); + if (actionBar != null) { + actionBar.setTitle(mPreviousActionBarTitle); + actionBar.setHomeAsUpIndicator(null); + actionBar.setDisplayHomeAsUpEnabled(mPreviousHomeAsUpEnabled); + + actionBar.setCustomView(mPreviousCustomView); + if (mPreviousCustomView == null) { + actionBar.setDisplayShowCustomEnabled(false); + } + } + } + + /** + * Displays a dialog asking the user to confirm that they want to exit, discarding unsaved changes. + */ + private void showDiscardChangesDialog() { + AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()); + builder.setTitle(getString(R.string.image_settings_dismiss_dialog_title)); + builder.setPositiveButton(getString(R.string.discard), new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int id) { + getTargetFragment().onActivityResult(getTargetRequestCode(), getTargetRequestCode(), null); + restorePreviousActionBar(); + getFragmentManager().popBackStack(); + } + }); + + builder.setNegativeButton(getString(R.string.cancel), new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int id) { + dialog.dismiss(); + } + }); + + AlertDialog dialog = builder.create(); + dialog.show(); + } + + /** + * Extracts the meta data from the dialog fields and updates the entries in the given JSONObject. + */ + private JSONObject extractMetaDataFromFields(JSONObject metaData) { + try { + metaData.put("title", mTitleText.getText().toString()); + metaData.put("caption", mCaptionText.getText().toString()); + metaData.put("alt", mAltText.getText().toString()); + if (mAlignmentSpinner.getSelectedItemPosition() < mAlignmentKeyArray.length) { + metaData.put("align", mAlignmentKeyArray[mAlignmentSpinner.getSelectedItemPosition()]); + } + metaData.put("linkUrl", mLinkTo.getText().toString()); + + int newWidth = getEditTextIntegerClamped(mWidthText, 10, mMaxImageWidth); + metaData.put("width", newWidth); + metaData.put("height", getRelativeHeightFromWidth(newWidth)); + } catch (JSONException e) { + AppLog.d(AppLog.T.EDITOR, "Unable to build JSON object from new meta data"); + } + + return metaData; + } + + private void loadThumbnail(final String src, final ImageView thumbnailImage) { + Thread thread = new Thread(new Runnable() { + @Override + public void run() { + if (isAdded()) { + final Uri localUri = Utils.downloadExternalMedia(getActivity(), Uri.parse(src), mHttpHeaders); + + if (getActivity() != null) { + getActivity().runOnUiThread(new Runnable() { + @Override + public void run() { + thumbnailImage.setImageURI(localUri); + } + }); + } + } + } + }); + + thread.start(); + } + + /** + * Initialize the image width SeekBar and accompanying EditText + */ + private void setupWidthSeekBar(final SeekBar widthSeekBar, final EditText widthText, int imageWidth) { + widthSeekBar.setMax(mMaxImageWidth / 10); + + if (imageWidth != 0) { + widthSeekBar.setProgress(imageWidth / 10); + widthText.setText(String.valueOf(imageWidth) + "px"); + } + + widthSeekBar.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; + } + widthText.setText(progress * 10 + "px"); + } + }); + + widthText.setOnFocusChangeListener(new View.OnFocusChangeListener() { + @Override + public void onFocusChange(View v, boolean hasFocus) { + if (hasFocus) { + widthText.setText(""); + } + } + }); + + widthText.setOnEditorActionListener(new TextView.OnEditorActionListener() { + @Override + public boolean onEditorAction(TextView v, int actionId, KeyEvent event) { + int width = getEditTextIntegerClamped(widthText, 10, mMaxImageWidth); + widthSeekBar.setProgress(width / 10); + widthText.setSelection((String.valueOf(width).length())); + + InputMethodManager imm = (InputMethodManager) getActivity() + .getSystemService(Context.INPUT_METHOD_SERVICE); + imm.hideSoftInputFromWindow(widthText.getWindowToken(), + InputMethodManager.RESULT_UNCHANGED_SHOWN); + + return true; + } + }); + } + + /** + * Return the integer value of the width EditText, adjusted to be within the given min and max, and stripped of the + * 'px' units + */ + private int getEditTextIntegerClamped(EditText editText, int minWidth, int maxWidth) { + int width = 10; + + try { + if (editText.getText() != null) + width = Integer.parseInt(editText.getText().toString().replace("px", "")); + } catch (NumberFormatException e) { + AppLog.e(AppLog.T.EDITOR, e); + } + + width = Math.min(maxWidth, Math.max(width, minWidth)); + + return width; + } + + /** + * Given the new width, return the proportionally adjusted height, given the dimensions of the original image + */ + private int getRelativeHeightFromWidth(int width) { + int height = 0; + + try { + int naturalHeight = mImageMeta.getInt("naturalHeight"); + int naturalWidth = mImageMeta.getInt("naturalWidth"); + + float ratio = (float) naturalHeight / naturalWidth; + height = (int) (ratio * width); + } catch (JSONException e) { + AppLog.d(AppLog.T.EDITOR, "JSON object missing naturalHeight or naturalWidth property"); + } + + return height; + } +} |