package carbon.beta; import android.content.Context; import android.os.Build; import android.os.Parcel; import android.os.Parcelable; import android.support.annotation.NonNull; import android.support.v7.view.ContextThemeWrapper; import android.util.AttributeSet; import android.util.TypedValue; import android.view.ActionMode; import android.view.ContextMenu; import android.view.Display; import android.view.Menu; import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; import android.view.WindowManager; import android.widget.PopupWindow; import java.lang.reflect.Field; import carbon.R; import carbon.internal.EditTextMenu; import carbon.widget.EditText; public class MarshmallowEditText extends EditText { private static final int ID_CUT = android.R.id.cut; private static final int ID_COPY = android.R.id.copy; private static final int ID_PASTE = android.R.id.paste; private static final int ID_COPY_URL = android.R.id.copyUrl; private static final int ID_SELECT_ALL = android.R.id.selectAll; public MarshmallowEditText(Context context) { super(context); initActionModeCallback(); } public MarshmallowEditText(Context context, AttributeSet attrs) { super(context, attrs); initActionModeCallback(); } public MarshmallowEditText(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); initActionModeCallback(); } public MarshmallowEditText(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); initActionModeCallback(); } @Override public boolean onTextContextMenuItem(int id) { boolean result = super.onTextContextMenuItem(id); if (id == ID_SELECT_ALL) showContextMenu(); return result; } // ------------------------------- // popup // ------------------------------- EditTextMenu popupMenu; private boolean isShowingPopup = false; WindowManager brokenWindowManager = new WindowManager() { @Override public Display getDefaultDisplay() { return null; } @Override public void removeViewImmediate(View view) { } @Override public void addView(View view, ViewGroup.LayoutParams params) { final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params; view.setLayoutParams(wparams); } @Override public void updateViewLayout(View view, ViewGroup.LayoutParams params) { final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params; view.setLayoutParams(wparams); } @Override public void removeView(View view) { } }; @Override public int getSelectionStart() { try { if (brokenWindowManager != null) { Field ccmf = android.widget.TextView.class.getDeclaredField("mCursorControllerMenu"); ccmf.setAccessible(true); Object ccm = ccmf.get(this); { Field pwf = ccm.getClass().getDeclaredField("mPopupWindow"); pwf.setAccessible(true); PopupWindow pw = (PopupWindow) pwf.get(ccm); Field wmf = pw.getClass().getDeclaredField("mWindowManager"); wmf.setAccessible(true); wmf.set(pw, brokenWindowManager); } { Field pwf = ccm.getClass().getDeclaredField("mPopupWindowArrowDown"); pwf.setAccessible(true); PopupWindow pw = (PopupWindow) pwf.get(ccm); Field wmf = pw.getClass().getDeclaredField("mWindowManager"); wmf.setAccessible(true); wmf.set(pw, brokenWindowManager); } { Field pwf = ccm.getClass().getDeclaredField("mPopupWindowArrowUp"); pwf.setAccessible(true); PopupWindow pw = (PopupWindow) pwf.get(ccm); Field wmf = pw.getClass().getDeclaredField("mWindowManager"); wmf.setAccessible(true); wmf.set(pw, brokenWindowManager); } brokenWindowManager = null; } } catch (Exception e) { } return super.getSelectionStart(); } @Override public ActionMode startActionMode(final ActionMode.Callback callback) { ActionMode.Callback c = new ActionMode.Callback() { public boolean onPrepareActionMode(ActionMode mode, Menu menu) { return callback.onPrepareActionMode(mode, menu); //createMenu(menu); } public void onDestroyActionMode(ActionMode mode) { popupMenu.dismiss(); callback.onDestroyActionMode(mode); } public boolean onCreateActionMode(ActionMode mode, Menu menu) { return callback.onCreateActionMode(mode, menu); } public boolean onActionItemClicked(ActionMode mode, MenuItem item) { return callback.onActionItemClicked(mode, item); } }; return super.startActionMode(c); } @Override public ActionMode startActionMode(final ActionMode.Callback callback, int type) { ActionMode.Callback c = new ActionMode.Callback() { public boolean onPrepareActionMode(ActionMode mode, Menu menu) { prepareMenu(); return true; } public void onDestroyActionMode(ActionMode mode) { callback.onDestroyActionMode(mode); } public boolean onCreateActionMode(ActionMode mode, Menu menu) { // mode.setCustomView(new View(getContext())); // callback.onCreateActionMode(mode, menu); callback.onCreateActionMode(mode, menu); createMenu(menu); menu.clear(); return true;// } public boolean onActionItemClicked(ActionMode mode, MenuItem item) { return callback.onActionItemClicked(mode, item); } }; return super.startActionMode(c, type); } private void initActionModeCallback() { if (android.os.Build.VERSION.SDK_INT >= 11) { setCustomSelectionActionModeCallback(new ActionMode.Callback() { public boolean onPrepareActionMode(ActionMode mode, Menu menu) { return false; } public void onDestroyActionMode(ActionMode mode) { } public boolean onCreateActionMode(ActionMode mode, Menu menu) { createMenu(menu); return true; } public boolean onActionItemClicked(ActionMode mode, MenuItem item) { return false; } }); } if (Build.VERSION.SDK_INT >= 23) { setCustomInsertionActionModeCallback(new ActionMode.Callback() { @Override public boolean onCreateActionMode(ActionMode mode, Menu menu) { createMenu(menu); return true; } @Override public boolean onPrepareActionMode(ActionMode mode, Menu menu) { return false; } @Override public boolean onActionItemClicked(ActionMode mode, MenuItem item) { return false; } @Override public void onDestroyActionMode(ActionMode mode) { } }); } } @Override protected void onCreateContextMenu(ContextMenu menu) { super.onCreateContextMenu(menu); //createMenu(menu); } private void prepareMenu() { if (popupMenu.hasVisibleItems()) { popupMenu.show(this); isShowingPopup = true; } } private void createMenu(Menu menu) { TypedValue outValue = new TypedValue(); getContext().getTheme().resolveAttribute(R.attr.carbon_editMenuTheme, outValue, true); int theme = outValue.resourceId; Context themedContext = new ContextThemeWrapper(getContext(), theme); popupMenu = new EditTextMenu(themedContext); popupMenu.setOnDismissListener(new PopupWindow.OnDismissListener() { @Override public void onDismiss() { isShowingPopup = false; } }); popupMenu.initCopy(menu.findItem(ID_COPY)); popupMenu.initCut(menu.findItem(ID_CUT)); popupMenu.initPaste(menu.findItem(ID_PASTE)); popupMenu.initSelectAll(menu.findItem(ID_SELECT_ALL)); //menu.clear(); /*try { mIgnoreActionUpEventField.set(editor, true); } catch (IllegalAccessException e) { e.printStackTrace(); }*/ } @Override protected boolean setFrame(int l, int t, int r, int b) { boolean result = super.setFrame(l, t, r, b); if (popupMenu != null) popupMenu.update(); return result; } @Override protected void onAttachedToWindow() { super.onAttachedToWindow(); if (isShowingPopup) popupMenu.showImmediate(this); } @Override protected void onDetachedFromWindow() { super.onDetachedFromWindow(); if (isShowingPopup) popupMenu.dismissImmediate(); } @Override public Parcelable onSaveInstanceState() { //begin boilerplate code that allows parent classes to save state Parcelable superState = super.onSaveInstanceState(); SavedState ss = new SavedState(superState); //end ss.isShowingPopup = this.isShowingPopup ? 1 : 0; return ss; } @Override public void onRestoreInstanceState(Parcelable state) { //begin boilerplate code so parent classes can restore state if (!(state instanceof SavedState)) { super.onRestoreInstanceState(state); return; } SavedState ss = (SavedState) state; super.onRestoreInstanceState(ss.getSuperState()); //end this.isShowingPopup = ss.isShowingPopup > 0; } static class SavedState implements Parcelable { public static final SavedState EMPTY_STATE = new SavedState() { }; int isShowingPopup; Parcelable superState; SavedState() { superState = null; } SavedState(Parcelable superState) { this.superState = superState != EMPTY_STATE ? superState : null; } private SavedState(Parcel in) { Parcelable superState = in.readParcelable(EditText.class.getClassLoader()); this.superState = superState != null ? superState : EMPTY_STATE; this.isShowingPopup = in.readInt(); } @Override public int describeContents() { return 0; } @Override public void writeToParcel(@NonNull Parcel out, int flags) { out.writeParcelable(superState, flags); out.writeInt(this.isShowingPopup); } public Parcelable getSuperState() { return superState; } //required field that makes Parcelables from a Parcel public static final Parcelable.Creator<SavedState> CREATOR = new Parcelable.Creator<SavedState>() { public SavedState createFromParcel(Parcel in) { return new SavedState(in); } public SavedState[] newArray(int size) { return new SavedState[size]; } }; } }