Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit f170a345 authored by Gilles Debunne's avatar Gilles Debunne
Browse files

Added drag and drop functionality in TextViews

Basic implementation. The finger is currently hiding the insertion position.
An offset has to be added in DragThumbnailBuilder to prevent this.

Change-Id: I4207c337a2a31162063fba191a5eb5d5856b24cf
parent 8e93e4f3
Loading
Loading
Loading
Loading
+95 −9
Original line number Diff line number Diff line
@@ -23,6 +23,7 @@ import org.xmlpull.v1.XmlPullParserException;

import android.R;
import android.content.ClipData;
import android.content.ClipData.Item;
import android.content.ClipboardManager;
import android.content.Context;
import android.content.pm.PackageManager;
@@ -88,6 +89,7 @@ import android.util.Log;
import android.util.TypedValue;
import android.view.ActionMode;
import android.view.ContextMenu;
import android.view.DragEvent;
import android.view.Gravity;
import android.view.HapticFeedbackConstants;
import android.view.KeyEvent;
@@ -6731,10 +6733,6 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
                    mMovement.onTakeFocus(this, (Spannable) mText, direction);
                }

                if (mSelectAllOnFocus) {
                    Selection.setSelection((Spannable) mText, 0, mText.length());
                }

                // The DecorView does not have focus when the 'Done' ExtractEditText button is
                // pressed. Since it is the ViewRoot's mView, it requests focus before
                // ExtractEditText clears focus, which gives focus to the ExtractEditText.
@@ -6753,6 +6751,11 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
                     */
                    Selection.setSelection((Spannable) mText, selStart, selEnd);
                }

                if (mSelectAllOnFocus) {
                    Selection.setSelection((Spannable) mText, 0, mText.length());
                }

                mTouchFocusSelected = true;
            }

@@ -7029,12 +7032,12 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
                    || windowParams.type > WindowManager.LayoutParams.LAST_SUB_WINDOW;
        }

        // TODO Add an extra android:cursorController flag to disable the controller?
        if (windowSupportsHandles && mCursorVisible && mLayout != null) {
        if (windowSupportsHandles && isTextEditable() && mCursorVisible && mLayout != null) {
            if (mInsertionPointCursorController == null) {
                mInsertionPointCursorController = new InsertionPointCursorController();
            }
        } else {
            hideInsertionPointCursorController();
            mInsertionPointCursorController = null;
        }

@@ -7044,7 +7047,9 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
            }
        } else {
            // Stop selection mode if the controller becomes unavailable.
            if (mSelectionModifierCursorController != null) {
                stopSelectionActionMode();
            }
            mSelectionModifierCursorController = null;
        }
    }
@@ -7053,7 +7058,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
     * @return True iff this TextView contains a text that can be edited.
     */
    private boolean isTextEditable() {
        return mText instanceof Editable && onCheckIsTextEditor();
        return mText instanceof Editable && onCheckIsTextEditor() && isEnabled();
    }

    /**
@@ -7684,6 +7689,34 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
        return packRangeInLong(min, max);
    }

    private DragThumbnailBuilder getTextThumbnailBuilder(CharSequence text) {
        TextView thumbnail = (TextView) inflate(mContext,
                com.android.internal.R.layout.text_drag_thumbnail, null);

        if (thumbnail == null) {
            throw new IllegalArgumentException("Unable to inflate text drag thumbnail");
        }

        if (text.length() > DRAG_THUMBNAIL_MAX_TEXT_LENGTH) {
            text = text.subSequence(0, DRAG_THUMBNAIL_MAX_TEXT_LENGTH);
        }
        thumbnail.setText(text);
        thumbnail.setTextColor(getTextColors());

        thumbnail.setTextAppearance(mContext, R.styleable.Theme_textAppearanceLarge);
        thumbnail.setGravity(Gravity.CENTER);

        thumbnail.setLayoutParams(new LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,
                ViewGroup.LayoutParams.WRAP_CONTENT));

        final int size = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
        thumbnail.measure(size, size);

        thumbnail.layout(0, 0, thumbnail.getMeasuredWidth(), thumbnail.getMeasuredHeight());
        thumbnail.invalidate();
        return new DragThumbnailBuilder(thumbnail);
    }

    @Override
    public boolean performLongClick() {
        if (super.performLongClick()) {
@@ -7691,6 +7724,18 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
            return true;
        }

        if (mSelectionActionMode != null && touchPositionIsInSelection()) {
            final int start = getSelectionStart();
            final int end = getSelectionEnd();
            CharSequence selectedText = mTransformed.subSequence(start, end);
            ClipData data = ClipData.newPlainText(null, null, selectedText);
            startDrag(data, getTextThumbnailBuilder(selectedText), false);
            stopSelectionActionMode();
            performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
            mEatTouchRelease = true;
            return true;
        }

        if (startSelectionActionMode()) {
            performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
            mEatTouchRelease = true;
@@ -8728,6 +8773,46 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
        return getOffsetForHorizontal(line, x);
    }

    @Override
    public boolean onDragEvent(DragEvent event) {
        switch (event.getAction()) {
            case DragEvent.ACTION_DRAG_STARTED:
                return mInsertionPointCursorController != null;

            case DragEvent.ACTION_DRAG_ENTERED:
                TextView.this.requestFocus();
                return true;

            case DragEvent.ACTION_DRAG_LOCATION: {
                final int offset = getOffset((int)event.getX(), (int)event.getY());
                Selection.setSelection((Spannable)mText, offset);
                return true;
            }

            case DragEvent.ACTION_DROP: {
                StringBuilder content = new StringBuilder("");
                ClipData clipData = event.getClipData();
                final int itemCount = clipData.getItemCount();
                for (int i=0; i < itemCount; i++) {
                    Item item = clipData.getItem(i);
                    content.append(item.coerceToText(TextView.this.mContext));
                }
                final int offset = getOffset((int) event.getX(), (int) event.getY());
                long minMax = prepareSpacesAroundPaste(offset, offset, content);
                int min = extractRangeStartFromLong(minMax);
                int max = extractRangeEndFromLong(minMax);
                Selection.setSelection((Spannable) mText, max);
                ((Editable) mText).replace(min, max, content);
                return true;
            }

            case DragEvent.ACTION_DRAG_EXITED:
            case DragEvent.ACTION_DRAG_ENDED:
            default:
                return true;
        }
    }


    @ViewDebug.ExportedProperty(category = "text")
    private CharSequence            mText;
@@ -8822,4 +8907,5 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
    private static final InputFilter[] NO_FILTERS = new InputFilter[0];
    private InputFilter[] mFilters = NO_FILTERS;
    private static final Spanned EMPTY_SPANNED = new SpannedString("");
    private static int DRAG_THUMBNAIL_MAX_TEXT_LENGTH = 20;
}
+26 −0
Original line number Diff line number Diff line
<?xml version="1.0" encoding="utf-8"?>
<!--
/* apps/common/assets/default/default/skins/StatusBar.xml
**
** Copyright 2010, The Android Open Source Project
**
** Licensed under the Apache License, Version 2.0 (the "License"); 
** you may not use this file except in compliance with the License. 
** You may obtain a copy of the License at 
**
**     http://www.apache.org/licenses/LICENSE-2.0 
**
** Unless required by applicable law or agreed to in writing, software 
** distributed under the License is distributed on an "AS IS" BASIS, 
** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
** See the License for the specific language governing permissions and 
** limitations under the License.
*/
-->
<TextView
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:textAppearance="@android:style/TextAppearance.Large"
    android:gravity="center"
    />