Loading app/src/main/java/foundation/e/blisslauncher/core/customviews/Folder.kt +86 −40 Original line number Diff line number Diff line Loading @@ -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>() Loading @@ -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 Loading Loading @@ -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()) { Loading @@ -152,7 +140,7 @@ class Folder @JvmOverloads constructor( return true } } } }*/ return false } Loading Loading @@ -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 Loading Loading @@ -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() Loading @@ -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 } } Loading @@ -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() Loading Loading @@ -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 Loading Loading @@ -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 = Loading Loading @@ -480,6 +467,7 @@ class Folder @JvmOverloads constructor( ViewGroup.LayoutParams.MATCH_PARENT ) ) dragController?.addDropTarget(this) } else { Log.e( TAG, Loading @@ -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)) Loading @@ -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?) { Loading @@ -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 Loading @@ -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 } Loading Loading @@ -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 Loading @@ -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() } Loading Loading @@ -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 Loading Loading @@ -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 } } app/src/main/java/foundation/e/blisslauncher/core/customviews/LauncherPagedView.java +81 −11 Original line number Diff line number Diff line Loading @@ -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); } Loading Loading @@ -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 ); Loading Loading @@ -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; } Loading @@ -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; } Loading Loading @@ -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; Loading Loading @@ -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); Loading Loading @@ -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(); Loading @@ -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"); Loading Loading @@ -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; Loading app/src/main/java/foundation/e/blisslauncher/features/folder/FolderViewPager.kt +8 −0 Original line number Diff line number Diff line Loading @@ -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 Loading Loading @@ -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() } } app/src/main/java/foundation/e/blisslauncher/features/test/dragndrop/DragController.java +5 −5 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading @@ -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. Loading Loading @@ -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; } Loading Loading
app/src/main/java/foundation/e/blisslauncher/core/customviews/Folder.kt +86 −40 Original line number Diff line number Diff line Loading @@ -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>() Loading @@ -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 Loading Loading @@ -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()) { Loading @@ -152,7 +140,7 @@ class Folder @JvmOverloads constructor( return true } } } }*/ return false } Loading Loading @@ -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 Loading Loading @@ -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() Loading @@ -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 } } Loading @@ -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() Loading Loading @@ -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 Loading Loading @@ -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 = Loading Loading @@ -480,6 +467,7 @@ class Folder @JvmOverloads constructor( ViewGroup.LayoutParams.MATCH_PARENT ) ) dragController?.addDropTarget(this) } else { Log.e( TAG, Loading @@ -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)) Loading @@ -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?) { Loading @@ -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 Loading @@ -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 } Loading Loading @@ -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 Loading @@ -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() } Loading Loading @@ -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 Loading Loading @@ -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 } }
app/src/main/java/foundation/e/blisslauncher/core/customviews/LauncherPagedView.java +81 −11 Original line number Diff line number Diff line Loading @@ -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); } Loading Loading @@ -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 ); Loading Loading @@ -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; } Loading @@ -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; } Loading Loading @@ -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; Loading Loading @@ -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); Loading Loading @@ -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(); Loading @@ -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"); Loading Loading @@ -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; Loading
app/src/main/java/foundation/e/blisslauncher/features/folder/FolderViewPager.kt +8 −0 Original line number Diff line number Diff line Loading @@ -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 Loading Loading @@ -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() } }
app/src/main/java/foundation/e/blisslauncher/features/test/dragndrop/DragController.java +5 −5 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading @@ -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. Loading Loading @@ -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; } Loading