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

Commit b9d178c3 authored by Amit Kumar's avatar Amit Kumar 💻
Browse files

Recurse through open folder in all cases

parent d13159f2
Loading
Loading
Loading
Loading
Loading
+86 −40
Original line number Diff line number Diff line
@@ -50,21 +50,11 @@ class Folder @JvmOverloads constructor(
    attrs: AttributeSet? = null
) : AbstractFloatingView(context, attrs), DragController.DragListener,
    FolderTitleInput.OnBackKeyListener, FolderItem.FolderListener, View.OnFocusChangeListener,
    OnEditorActionListener, DragSource {
    OnEditorActionListener, DragSource, DropTarget {

    /**
     * Fraction of icon width which behave as scroll region.
     */
    private val ICON_OVERSCROLL_WIDTH_FACTOR = 0.45f

    private val FOLDER_NAME_ANIMATION_DURATION = 633L

    private val REORDER_DELAY = 250L
    private val ON_EXIT_CLOSE_DELAY = 400L
    private val sTempRect = Rect()
    private var mScrollAreaOffset: Int = 0
    private val MIN_FOLDERS_FOR_HARDWARE_OPTIMIZATION = 10

    private val mReorderAlarm: Alarm = Alarm()
    private val mOnExitAlarm: Alarm = Alarm()
    val mItemsInReadingOrder = ArrayList<View>()

@@ -79,8 +69,6 @@ class Folder @JvmOverloads constructor(
    lateinit var mFolderTitleInput: FolderTitleInput
    private lateinit var mPageIndicator: CircleIndicator

    // Cell ranks used for drag and drop
    var mTargetRank = 0
    var mPrevTargetRank = 0
    var mEmptyCellRank = 0

@@ -138,7 +126,7 @@ class Folder @JvmOverloads constructor(
    }

    override fun onControllerInterceptTouchEvent(ev: MotionEvent?): Boolean {
        ev?.let {
        /*ev?.let {
            if (it.action == MotionEvent.ACTION_DOWN) {
                val dl: DragLayer = launcher.dragLayer
                if (isEditingName()) {
@@ -152,7 +140,7 @@ class Folder @JvmOverloads constructor(
                    return true
                }
            }
        }
        }*/
        return false
    }

@@ -200,6 +188,7 @@ class Folder @JvmOverloads constructor(
    private fun closeComplete(wasAnimated: Boolean) {
        // TODO: Clear all active animations.
        (this.parent as DragLayer?)?.removeView(this)
        dragController?.removeDropTarget(this)
        clearFocus()
        folderIcon?.apply {
            launcher.getLauncherPagedView().alpha = 1f
@@ -235,7 +224,7 @@ class Folder @JvmOverloads constructor(
        // Convert to a string here to ensure that no other state associated with the text field
        // gets saved.
        val newTitle: String = mFolderTitleInput.text.toString()
        mInfo?.setTitle(newTitle)
        mInfo.setTitle(newTitle)

        // Update database
        launcher.getLauncherPagedView().updateDatabase()
@@ -253,14 +242,16 @@ class Folder @JvmOverloads constructor(
    // we need to create the illusion that the item isn't added back to the folder yet, to
    // to correspond to the animation of the icon back into the folder. This is
    fun hideItem(info: LauncherItem) {
        getViewForInfo(info).apply {
            visibility = INVISIBLE
        getViewForInfo(info)?.apply {
            this.clearAnimation()
            this.visibility = INVISIBLE
        }
    }

    fun showItem(info: LauncherItem) {
        getViewForInfo(info)?.apply {
            visibility = VISIBLE
            this.clearAnimation()
            this.visibility = VISIBLE
        }
    }

@@ -271,6 +262,7 @@ class Folder @JvmOverloads constructor(
    override fun onTitleChanged(title: CharSequence?) {}

    override fun onRemove(item: LauncherItem) {
        Log.d(TAG, "onRemove() called with: item = $item")
        mItemsInvalidated = true
        val v: View? = getViewForInfo(item)
        mContent.adapter?.notifyDataSetChanged()
@@ -302,15 +294,12 @@ class Folder @JvmOverloads constructor(
        if (dragObject.dragSource != this) {
            return
        }

        mContent.removeItem(mCurrentDragView)
        mCurrentDragView?.clearAnimation()
        hideItem(dragObject.dragInfo)
        if (dragObject.dragInfo is LauncherItem) {
            mItemsInvalidated = true
            SuppressInfoChanges().use { _ ->
                mInfo.remove(
                    dragObject.dragInfo as LauncherItem,
                    true
                )
                // mInfo?.remove(dragObject.dragInfo, true)
            }
        }
        mDragInProgress = true
@@ -400,8 +389,6 @@ class Folder @JvmOverloads constructor(
            }
        })
        // We use same size for height and width as we want to look it like square
        val height =
            mDeviceProfile.cellHeightPx * 3 + resources.getDimensionPixelSize(R.dimen.folder_padding)
        mContent.layoutParams?.width =
            mDeviceProfile.cellHeightPx * 3 + resources.getDimensionPixelSize(R.dimen.folder_padding) * 2
        mContent.layoutParams?.height =
@@ -480,6 +467,7 @@ class Folder @JvmOverloads constructor(
                    ViewGroup.LayoutParams.MATCH_PARENT
                )
            )
            dragController?.addDropTarget(this)
        } else {
            Log.e(
                TAG,
@@ -498,7 +486,6 @@ class Folder @JvmOverloads constructor(
        // dropping. One resulting issue is that replaceFolderWithFinalItem() can be called twice.
        mDeleteFolderOnDropCompleted = false
        // centerAboutIcon()
        Log.i(TAG, "animateOpen: " + mContent.getItemCount() + " " + mInfo.items.size)
        val anim: AnimatorSet = FolderAnimationManager(this, true /* isOpening */).animator
        anim.play(ObjectAnimator.ofFloat(launcher.getLauncherPagedView(), View.ALPHA, 0f))
            .with(ObjectAnimator.ofFloat(launcher.hotseat, View.ALPHA, 0f))
@@ -518,6 +505,7 @@ class Folder @JvmOverloads constructor(
                launcher.getLauncherPagedView().alpha = 0f
                launcher.hotseat.alpha = 0f
                launcher.getLauncherPagedView().pageIndicator.alpha = 0f
                mContent.setFocusOnFirstChild()
            }

            override fun onAnimationCancel(animation: Animator?) {
@@ -535,16 +523,20 @@ class Folder @JvmOverloads constructor(
    }

    fun completeDragExit() {
        if (mIsOpen) {
        when {
            mIsOpen -> {
                close(true)
                mRearrangeOnClose = true
        } else if (mState == STATE_ANIMATING) {
            }
            mState == STATE_ANIMATING -> {
                mRearrangeOnClose = true
        } else {
            }
            else -> {
                rearrangeChildren()
                clearDragInfo()
            }
        }
    }

    private fun clearDragInfo() {
        mCurrentDragView = null
@@ -563,7 +555,7 @@ class Folder @JvmOverloads constructor(
     * @param itemCount if greater than the total children count, empty spaces are left at the end,
     * otherwise it is ignored.
     */
    fun rearrangeChildren(itemCount: Int) {
    private fun rearrangeChildren(itemCount: Int) {
        mContent.adapter?.notifyDataSetChanged()
        mItemsInvalidated = true
    }
@@ -648,7 +640,12 @@ class Folder @JvmOverloads constructor(
            }
        } else {
            // The drag failed, we need to return the item to the folder
            mContent.adapter?.notifyDataSetChanged()
            mContent.adapter =
                FolderPagerAdapter(context, mInfo.items, launcher.deviceProfile)
            launcher.dragLayer.removeView(d?.dragView)
            d?.dragView = null
            invalidate()
            launcher.getLauncherPagedView().wobbleLayouts()
        }

        mDeleteFolderOnDropCompleted = false
@@ -656,7 +653,7 @@ class Folder @JvmOverloads constructor(
        mItemAddedBackToSelfViaIcon = false
        mCurrentDragView = null

        // Reordering may have occured, and we need to save the new item locations. We do this once
        // Reordering may have occurred, and we need to save the new item locations. We do this once
        // at the end to prevent unnecessary database operations.
        launcher.getLauncherPagedView().updateDatabase()
    }
@@ -697,6 +694,8 @@ class Folder @JvmOverloads constructor(
        const val STATE_ANIMATING = 1
        const val STATE_OPEN = 2

        private const val ON_EXIT_CLOSE_DELAY = 400L

        const val TAG = "Folder"

        private var sDefaultFolderName: String? = null
@@ -740,4 +739,51 @@ class Folder @JvmOverloads constructor(
            mInfo.removeListener(this@Folder)
        }
    }

    override fun isDropEnabled(): Boolean = mState != STATE_ANIMATING

    override fun onDrop(dragObject: DropTarget.DragObject?, options: DragOptions?) {
        // Do nothing here as we don't allow to drop icon in folder.
    }

    override fun onDragEnter(d: DropTarget.DragObject) {
        mPrevTargetRank = -1
        mOnExitAlarm.cancelAlarm()
        // Get the area offset such that the folder only closes if half the drag icon width
        // is outside the folder area
        // Get the area offset such that the folder only closes if half the drag icon width
        // is outside the folder area
        mScrollAreaOffset = d.dragView.dragRegionWidth / 2 - d.xOffset
    }

    override fun onDragOver(dragObject: DropTarget.DragObject?) {
        // Do Nothing here, we don't allow drop.
        Log.d(TAG, "onDragOver() called with: dragObject = $dragObject")
    }

    override fun onDragExit(d: DropTarget.DragObject) {
        // We only close the folder if this is a true drag exit, ie. not because
        // a drop has occurred above the folder.
        if (!d.dragComplete) {
            mOnExitAlarm.setOnAlarmListener(mOnExitAlarmListener)
            mOnExitAlarm.setAlarm(ON_EXIT_CLOSE_DELAY)
        }
    }

    override fun acceptDrop(dragObject: DropTarget.DragObject?): Boolean = false

    override fun prepareAccessibilityDrop() {
    }

    override fun getHitRectRelativeToDragLayer(outRect: Rect?) {
        launcher.dragLayer.getDescendantRectRelativeToSelf(mContent, outRect)
        // mContent.getHitRect(outRect)
        /*outRect!!.left -= mScrollAreaOffset
        outRect!!.right += mScrollAreaOffset*/
        Log.i(TAG, "getHitRectRelativeToDragLayer: " + outRect)
    }

    fun getContent(): ViewGroup {
        return mContent
    }
}
+81 −11
Original line number Diff line number Diff line
@@ -202,6 +202,10 @@ public class LauncherPagedView extends PagedView<PageIndicatorDots> implements V
     */
    public final Map<ShortcutKey, MutableInt> pinnedShortcutCounts = new HashMap<>();

    /** The value that {@link #mTransitionProgress} must be greater than for
     * {@link #transitionStateShouldAllowDrop()} to return true. */
    private static final float ALLOW_DROP_TRANSITION_PROGRESS = 0.25f;

    public LauncherPagedView(Context context, AttributeSet attributeSet) {
        this(context, attributeSet, 0);
    }
@@ -333,7 +337,8 @@ public class LauncherPagedView extends PagedView<PageIndicatorDots> implements V
            LauncherItem launcherItem = launcherItems.get(i);
            View appView;
            if (launcherItem.itemType == Constants.ITEM_TYPE_FOLDER) {
                FolderIcon folderIcon = FolderIcon.Companion.fromXml(R.layout.folder_icon,
                FolderIcon folderIcon = FolderIcon.Companion.fromXml(
                    R.layout.folder_icon,
                    getScreenWithId(launcherItem.screenId),
                    (FolderItem) launcherItem
                );
@@ -1379,11 +1384,10 @@ public class LauncherPagedView extends PagedView<PageIndicatorDots> implements V
        if (!workspaceInModalState() && !mIsSwitchingState) {
            result = super.scrollLeft();
        }
        // TODO: Fix this asap
        /*Folder openFolder = Folder.getOpen(mLauncher);
        Folder openFolder = Folder.Companion.getOpen(mLauncher);
        if (openFolder != null) {
            openFolder.completeDragExit();
        }*/
        }
        return result;
    }

@@ -1393,11 +1397,10 @@ public class LauncherPagedView extends PagedView<PageIndicatorDots> implements V
        if (!workspaceInModalState() && !mIsSwitchingState) {
            result = super.scrollRight();
        }
        // TODO: Fix this asap
        /*Folder openFolder = Folder.getOpen(mLauncher);
        Folder openFolder = Folder.Companion.getOpen(mLauncher);
        if (openFolder != null) {
            openFolder.completeDragExit();
        }*/
        }
        return result;
    }

@@ -1687,7 +1690,8 @@ public class LauncherPagedView extends PagedView<PageIndicatorDots> implements V
            final int[] touchXY = new int[]{(int) mDragViewVisualCenter[0],
                (int) mDragViewVisualCenter[1]};
            // onDropExternal(touchXY, dropTargetLayout, d);
        } else {
        }
        else {
            final View cell = mDragInfo.getCell();
            boolean droppedOnOriginalCellDuringTransition = false;
            Runnable onCompleteRunnable = null;
@@ -2211,7 +2215,7 @@ public class LauncherPagedView extends PagedView<PageIndicatorDots> implements V
        }

        // Handle the drag over
        if (mDragTargetLayout != null) {
        if (mDragTargetLayout != null && child != null) {
            // We want the point to be mapped to the dragTarget.
            if (mLauncher.isHotseatLayout(mDragTargetLayout)) {
                mapPointFromSelfToHotseatLayout(mLauncher.getHotseat(), mDragViewVisualCenter);
@@ -2301,8 +2305,46 @@ public class LauncherPagedView extends PagedView<PageIndicatorDots> implements V
    }

    @Override
    public boolean acceptDrop(DragObject dragObject) {
    public boolean acceptDrop(DragObject d) {
        CellLayout dropTargetLayout = mDropToLayout;
        if(d.dragSource != this) {
            if (dropTargetLayout == null) {
                return false;
            }
            if (!transitionStateShouldAllowDrop()) return false;

            mDragViewVisualCenter = d.getVisualCenter(mDragViewVisualCenter);

            // We want the point to be mapped to the dragTarget.
            mapPointFromDropLayout(dropTargetLayout, mDragViewVisualCenter);

            mTargetCell = findNearestArea((int) mDragViewVisualCenter[0],
                (int) mDragViewVisualCenter[1], dropTargetLayout,
                mTargetCell);
            float distance = dropTargetLayout.getDistanceFromCell(mDragViewVisualCenter[0],
                mDragViewVisualCenter[1], mTargetCell);
            if (mCreateUserFolderOnDrop && willCreateUserFolder(d.dragInfo,
                dropTargetLayout, mTargetCell, distance, true)) {
                return true;
            }

            if (mAddToExistingFolderOnDrop && willAddToExistingUserFolder(d.dragInfo,
                dropTargetLayout, mTargetCell, distance)) {
                return true;
            }

            int[] resultSpan = new int[2];
            mTargetCell = dropTargetLayout.performReorder((int) mDragViewVisualCenter[0],
                (int) mDragViewVisualCenter[1], 1, 1, 1, 1,
                null, mTargetCell, resultSpan, CellLayout.MODE_ACCEPT_DROP);
            boolean foundCell = mTargetCell[0] >= 0 && mTargetCell[1] >= 0;

            // Don't accept the drop if there's no room for the item
            if (!foundCell) {
                onNoCellFound(dropTargetLayout);
                return false;
            }
        }
        long screenId = getIdForScreen(dropTargetLayout);
        if (screenId == EXTRA_EMPTY_SCREEN_ID) {
            commitExtraEmptyScreen();
@@ -2310,6 +2352,25 @@ public class LauncherPagedView extends PagedView<PageIndicatorDots> implements V
        return true;
    }

    /**
     * Updates the point in {@param xy} to point to the co-ordinate space of {@param layout}
     * @param layout either hotseat of a page in workspace
     * @param xy the point location in workspace co-ordinate space
     */
    private void mapPointFromDropLayout(CellLayout layout, float[] xy) {
        if (mLauncher.isHotseatLayout(layout)) {
            mLauncher.getDragLayer().getDescendantCoordRelativeToSelf(this, xy, true);
            mLauncher.getDragLayer().mapCoordInSelfToDescendant(layout, xy);
        } else {
            mapPointFromSelfToChild(layout, xy);
        }
    }

    private boolean transitionStateShouldAllowDrop() {
        return (!isSwitchingState() || mTransitionProgress > ALLOW_DROP_TRANSITION_PROGRESS) &&
            workspaceIconsCanBeDragged();
    }

    @Override
    public void prepareAccessibilityDrop() {
        Log.d(TAG, "prepareAccessibilityDrop() called");
@@ -2651,9 +2712,18 @@ public class LauncherPagedView extends PagedView<PageIndicatorDots> implements V
                return;
            }
        }
        Folder folder = Folder.Companion.getOpen(mLauncher);
        if (folder != null && !folder.isDestroyed()) {
            for (int i = 0; i < folder.getContent().getChildCount(); i++) {
                GridLayout grid = (GridLayout) folder.getContent().getChildAt(i);
                if (mapOverCellLayout(recurse, grid, op)) {
                    return;
                }
            }
        }
    }

    private boolean mapOverCellLayout(boolean recurse, CellLayout layout, ItemOperator op) {
    private boolean mapOverCellLayout(boolean recurse, GridLayout layout, ItemOperator op) {
        // TODO(b/128460496) Potential race condition where layout is not yet loaded
        if (layout == null) {
            return false;
+8 −0
Original line number Diff line number Diff line
@@ -3,6 +3,7 @@ package foundation.e.blisslauncher.features.folder
import android.content.Context
import android.util.AttributeSet
import android.view.View
import android.view.ViewGroup
import android.widget.GridLayout
import androidx.core.view.get
import androidx.viewpager.widget.ViewPager
@@ -67,4 +68,11 @@ class FolderViewPager @JvmOverloads constructor(
            (getChildAt(lastPageIndex) as GridLayout).childCount + lastPageIndex * 9 // maxItems per page
        }
    }

    /**
     * Sets the focus on the first visible child.
     */
    fun setFocusOnFirstChild() {
        (getChildAt(currentItem) as ViewGroup?)?.getChildAt(0)?.requestFocus()
    }
}
+5 −5
Original line number Diff line number Diff line
@@ -16,6 +16,9 @@

package foundation.e.blisslauncher.features.test.dragndrop;

import static foundation.e.blisslauncher.features.test.LauncherState.NORMAL;
import static foundation.e.blisslauncher.features.test.anim.LauncherAnimUtils.SPRING_LOADED_EXIT_DELAY;

import android.content.ComponentName;
import android.content.res.Resources;
import android.graphics.Bitmap;
@@ -28,9 +31,6 @@ import android.view.HapticFeedbackConstants;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.View;

import java.util.ArrayList;

import foundation.e.blisslauncher.R;
import foundation.e.blisslauncher.core.database.model.ApplicationItem;
import foundation.e.blisslauncher.core.database.model.LauncherItem;
@@ -39,6 +39,7 @@ import foundation.e.blisslauncher.features.test.LauncherItemMatcher;
import foundation.e.blisslauncher.features.test.TestActivity;
import foundation.e.blisslauncher.features.test.TouchController;
import foundation.e.blisslauncher.features.test.UiThreadHelper;
import java.util.ArrayList;

/**
 * Class for initiating a drag within a view or across multiple views.
@@ -251,8 +252,7 @@ public class DragController implements DragDriver.EventListener, TouchController
        if (!accepted) {
            // If it was not accepted, cleanup the state. If it was accepted, it is the
            // responsibility of the drop target to cleanup the state.
            // mLauncher.getStateManager().goToState(NORMAL, SPRING_LOADED_EXIT_DELAY);
            //TODO: Go to normal state here.
            mLauncher.getStateManager().goToState(NORMAL, SPRING_LOADED_EXIT_DELAY);
            mDragObject.deferDragViewCleanupPostAnimation = false;
        }