Skip to content
GitLab
Menu
Projects
Groups
Snippets
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Sign in / Register
Toggle navigation
Menu
Open sidebar
e
os
BlissLauncher
Commits
10dac382
Commit
10dac382
authored
Dec 14, 2021
by
Amit Kumar
💻
Browse files
Separate folder logic from launcher activity
parent
4bf4103d
Pipeline
#151609
passed with stage
in 8 minutes and 20 seconds
Changes
20
Pipelines
1
Hide whitespace changes
Inline
Side-by-side
app/src/main/java/foundation/e/blisslauncher/core/blur/ShaderBlurDrawable.kt
View file @
10dac382
...
...
@@ -85,7 +85,7 @@ class ShaderBlurDrawable internal constructor(private val blurWallpaperProvider:
if
(
canvasOffset
>
0
)
canvas
.
translate
(-
canvasOffset
,
0f
)
}
override
fun
setAlpha
(
alpha
:
Int
)
{
blurAlpha
=
alpha
blurPaint
.
alpha
=
alpha
...
...
app/src/main/java/foundation/e/blisslauncher/core/customviews/Folder.kt
View file @
10dac382
package
foundation.e.blisslauncher.core.customviews
import
android.animation.Animator
import
android.animation.AnimatorListenerAdapter
import
android.animation.AnimatorSet
import
android.animation.ObjectAnimator
import
android.annotation.SuppressLint
import
android.content.Context
import
android.content.res.Resources
import
android.graphics.Rect
import
android.text.InputType
import
android.text.Selection
import
android.util.AttributeSet
import
android.util.Log
import
android.view.FocusFinder
import
android.view.KeyEvent
import
android.view.MotionEvent
import
android.view.View
import
android.view.ViewDebug.ExportedProperty
import
android.view.ViewGroup
import
android.view.accessibility.AccessibilityEvent
import
android.view.inputmethod.EditorInfo
import
android.widget.TextView
import
android.widget.TextView.OnEditorActionListener
import
androidx.viewpager.widget.ViewPager
import
foundation.e.blisslauncher.R
import
foundation.e.blisslauncher.core.DeviceProfile
import
foundation.e.blisslauncher.core.database.model.FolderItem
import
foundation.e.blisslauncher.core.database.model.LauncherItem
import
foundation.e.blisslauncher.features.folder.FolderAnimationManager
import
foundation.e.blisslauncher.features.folder.FolderIcon
import
foundation.e.blisslauncher.features.folder.FolderPagerAdapter
import
foundation.e.blisslauncher.features.folder.FolderViewPager
import
foundation.e.blisslauncher.features.test.Alarm
import
foundation.e.blisslauncher.features.test.IconTextView
import
foundation.e.blisslauncher.features.test.BaseDragLayer
import
foundation.e.blisslauncher.features.test.CellLayout
import
foundation.e.blisslauncher.features.test.OnAlarmListener
import
foundation.e.blisslauncher.features.test.TestActivity
import
foundation.e.blisslauncher.features.test.VariantDeviceProfile
...
...
@@ -32,15 +40,15 @@ import foundation.e.blisslauncher.features.test.dragndrop.DragLayer
import
foundation.e.blisslauncher.features.test.dragndrop.DragOptions
import
foundation.e.blisslauncher.features.test.dragndrop.DragSource
import
foundation.e.blisslauncher.features.test.dragndrop.DropTarget
import
me.relex.circleindicator.CircleIndicator
import
java.util.ArrayList
import
java.util.Collections
import
java.util.Compar
ator
import
kotlinx.android.synthetic.main.activity_test.*
import
me.relex.circleindicator.CircleIndic
ator
class
Folder
@JvmOverloads
constructor
(
context
:
Context
,
attrs
:
AttributeSet
?
=
null
)
:
AbstractFloatingView
(
context
,
attrs
),
DropTarget
,
DragController
.
DragListener
,
FolderTitleInput
.
OnBackKeyListener
,
FolderItem
.
FolderListener
,
context
:
Context
,
attrs
:
AttributeSet
?
=
null
)
:
AbstractFloatingView
(
context
,
attrs
),
DragController
.
DragListener
,
FolderTitleInput
.
OnBackKeyListener
,
FolderItem
.
FolderListener
,
View
.
OnFocusChangeListener
,
OnEditorActionListener
,
DragSource
{
/**
...
...
@@ -55,19 +63,18 @@ class Folder @JvmOverloads constructor(
private
val
sTempRect
=
Rect
()
private
val
MIN_FOLDERS_FOR_HARDWARE_OPTIMIZATION
=
10
private
var
sDefaultFolderName
:
String
?
=
null
private
val
mReorderAlarm
:
Alarm
=
Alarm
()
private
val
mOnExitAlarm
:
Alarm
=
Alarm
()
val
mItemsInReadingOrder
=
ArrayList
<
View
>()
protected
val
mL
auncher
:
TestActivity
protected
var
mD
ragController
:
DragController
?
=
null
val
l
auncher
:
TestActivity
var
d
ragController
:
DragController
?
=
null
lateinit
var
mInfo
:
FolderItem
private
va
l
mCurrentAnimator
:
AnimatorSet
?
=
null
private
va
r
mCurrentAnimator
:
AnimatorSet
?
=
null
var
mF
olderIcon
:
IconTextView
?
=
null
var
f
olderIcon
:
FolderIcon
?
=
null
lateinit
var
mContent
:
ViewPager
lateinit
var
mContent
:
Folder
ViewPager
lateinit
var
mFolderTitleInput
:
FolderTitleInput
private
lateinit
var
mPageIndicator
:
CircleIndicator
...
...
@@ -83,8 +90,8 @@ class Folder @JvmOverloads constructor(
private
var
mCurrentDragView
:
View
?
=
null
private
var
mIsExternalDrag
=
false
private
var
mDragInProgress
=
false
private
va
l
mDeleteFolderOnDropCompleted
=
false
private
va
l
mSuppressFolderDeletion
=
false
private
va
r
mDeleteFolderOnDropCompleted
=
false
private
va
r
mSuppressFolderDeletion
=
false
private
var
mItemAddedBackToSelfViaIcon
=
false
var
mFolderIconPivotX
=
0f
...
...
@@ -92,23 +99,22 @@ class Folder @JvmOverloads constructor(
private
var
mIsEditingName
=
false
@ExportedProperty
(
category
=
"launcher"
)
private
va
l
mDestroyed
=
false
private
va
r
mDestroyed
=
false
init
{
setLocaleDependentFields
(
resources
,
false
/* force */
)
mL
auncher
=
TestActivity
.
getLauncher
(
context
)
l
auncher
=
TestActivity
.
getLauncher
(
context
)
isFocusableInTouchMode
=
true
}
override
fun
onFinishInflate
()
{
super
.
onFinishInflate
()
mContent
=
findViewById
(
R
.
id
.
folder_apps
)
mContent
.
setFolder
(
this
)
mPageIndicator
=
findViewById
(
R
.
id
.
indicator
)
mFolderTitleInput
=
findViewById
(
R
.
id
.
folder_title
)
mFolderTitleInput
.
setOnBackKeyListener
(
this
)
mFolderTitleInput
.
setO
nFocusChangeListener
(
this
)
mFolderTitleInput
.
o
nFocusChangeListener
=
this
mFolderTitleInput
.
setOnEditorActionListener
(
this
)
mFolderTitleInput
.
setSelectAllOnFocus
(
true
)
mFolderTitleInput
.
inputType
=
mFolderTitleInput
.
inputType
and
...
...
@@ -124,23 +130,102 @@ class Folder @JvmOverloads constructor(
val
item
:
LauncherItem
=
tag
as
LauncherItem
mEmptyCellRank
=
item
.
cell
mCurrentDragView
=
v
mD
ragController
!!
.
addDragListener
(
this
)
mL
auncher
.
getLauncherPagedView
().
beginDragShared
(
v
,
this
,
options
)
d
ragController
!!
.
addDragListener
(
this
)
l
auncher
.
getLauncherPagedView
().
beginDragShared
(
v
,
this
,
options
)
}
return
true
}
fun
setLocaleDependentFields
(
res
:
Resources
,
force
:
Boolean
)
{
if
(
sDefaultFolderName
==
null
||
force
)
{
sDefaultFolderName
=
res
.
getString
(
R
.
string
.
untitled
)
override
fun
onControllerInterceptTouchEvent
(
ev
:
MotionEvent
?):
Boolean
{
ev
?.
let
{
if
(
it
.
action
==
MotionEvent
.
ACTION_DOWN
)
{
val
dl
:
DragLayer
=
launcher
.
dragLayer
if
(
isEditingName
())
{
if
(!
dl
.
isEventOverView
(
mFolderTitleInput
,
ev
))
{
mFolderTitleInput
.
dispatchBackKey
()
return
true
}
return
false
}
else
if
(!
dl
.
isEventOverView
(
this
,
ev
))
{
close
(
true
)
return
true
}
}
}
return
false
}
override
fun
onControllerInterceptTouchEvent
(
ev
:
MotionEvent
?):
Boolean
{
TODO
(
"Not yet implemented"
)
override
fun
handleClose
(
animate
:
Boolean
)
{
mIsOpen
=
false
if
(!
animate
&&
mCurrentAnimator
!=
null
&&
mCurrentAnimator
!!
.
isRunning
)
{
mCurrentAnimator
?.
cancel
()
}
if
(
isEditingName
())
{
mFolderTitleInput
.
dispatchBackKey
()
}
if
(
folderIcon
!=
null
)
{
// mFolderIcon.clearLeaveBehindIfExists()
}
if
(
animate
)
{
animateClosed
()
}
else
{
closeComplete
(
false
)
}
}
override
fun
handleClose
(
animate
:
Boolean
)
{
private
fun
animateClosed
()
{
val
a
=
FolderAnimationManager
(
this
,
false
/* isOpening */
).
animator
a
.
play
(
ObjectAnimator
.
ofFloat
(
launcher
.
getLauncherPagedView
(),
View
.
ALPHA
,
1f
))
.
with
(
ObjectAnimator
.
ofFloat
(
launcher
.
hotseat
,
View
.
ALPHA
,
1f
))
.
with
(
ObjectAnimator
.
ofFloat
(
launcher
.
getLauncherPagedView
().
pageIndicator
,
View
.
ALPHA
,
1f
)
)
a
.
addListener
(
object
:
AnimatorListenerAdapter
()
{
override
fun
onAnimationEnd
(
animation
:
Animator
)
{
closeComplete
(
true
)
}
})
startAnimation
(
a
)
}
private
fun
closeComplete
(
wasAnimated
:
Boolean
)
{
// TODO: Clear all active animations.
(
this
.
parent
as
DragLayer
?)
?.
removeView
(
this
)
clearFocus
()
folderIcon
?.
apply
{
launcher
.
getLauncherPagedView
().
alpha
=
1f
launcher
.
getLauncherPagedView
().
pageIndicator
.
alpha
=
1f
launcher
.
hotseat
.
alpha
=
1f
if
(
wasAnimated
)
{
if
(
this
.
hasDot
())
{
this
.
animateDotScale
(
0f
,
1f
)
}
}
}
if
(
mRearrangeOnClose
)
{
rearrangeChildren
()
mRearrangeOnClose
=
false
}
if
(
getItemCount
()
<=
1
)
{
if
(!
mDragInProgress
&&
!
mSuppressFolderDeletion
)
{
replaceFolderWithFinalItem
()
}
else
if
(
mDragInProgress
)
{
mDeleteFolderOnDropCompleted
=
true
}
}
mSuppressFolderDeletion
=
false
clearDragInfo
()
mState
=
STATE_NONE
mContent
.
currentItem
=
0
}
override
fun
isOfType
(
type
:
Int
):
Boolean
=
type
and
TYPE_FOLDER
!=
0
...
...
@@ -152,19 +237,61 @@ class Folder @JvmOverloads constructor(
mInfo
?.
setTitle
(
newTitle
)
// Update database
mL
auncher
.
getLauncherPagedView
().
updateDatabase
()
l
auncher
.
getLauncherPagedView
().
updateDatabase
()
// This ensures that focus is gained every time the field is clicked, which selects all
// the text and brings up the soft keyboard if necessary.
mFolderTitleInput
.
clearFocus
()
Selection
.
setSelection
(
mFolderTitleInput
.
getT
ext
()
,
0
,
0
)
Selection
.
setSelection
(
mFolderTitleInput
.
t
ext
,
0
,
0
)
mIsEditingName
=
false
return
true
}
override
fun
onTitleChanged
(
title
:
CharSequence
?)
{
TODO
(
"Not yet implemented"
)
// This is used so the item doesn't immediately appear in the folder when added. In one case
// 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
}
}
fun
showItem
(
info
:
LauncherItem
)
{
getViewForInfo
(
info
)
?.
apply
{
visibility
=
VISIBLE
}
}
override
fun
onAdd
(
item
:
LauncherItem
)
{
}
override
fun
onTitleChanged
(
title
:
CharSequence
?)
{}
override
fun
onRemove
(
item
:
LauncherItem
)
{
mItemsInvalidated
=
true
val
v
:
View
?
=
getViewForInfo
(
item
)
mContent
.
adapter
?.
notifyDataSetChanged
()
if
(
mState
==
STATE_ANIMATING
)
{
mRearrangeOnClose
=
true
}
else
{
rearrangeChildren
()
}
if
(
getItemCount
()
<=
1
)
{
if
(
mIsOpen
)
{
close
(
true
)
}
else
{
replaceFolderWithFinalItem
()
}
}
}
private
fun
getViewForInfo
(
item
:
LauncherItem
):
View
?
{
return
mContent
.
iterateOverItems
{
info
,
_
,
_
->
info
===
item
}
}
override
fun
onItemsChanged
(
animate
:
Boolean
)
{
updateTextViewFocus
()
}
override
fun
onDragStart
(
dragObject
:
DropTarget
.
DragObject
,
options
:
DragOptions
)
{
...
...
@@ -177,7 +304,7 @@ class Folder @JvmOverloads constructor(
mItemsInvalidated
=
true
SuppressInfoChanges
().
use
{
_
->
mInfo
.
remove
(
dragObject
.
dragInfo
as
WorkspaceItemInfo
,
dragObject
.
dragInfo
as
LauncherItem
,
true
)
}
...
...
@@ -191,7 +318,7 @@ class Folder @JvmOverloads constructor(
completeDragExit
()
}
mDragInProgress
=
false
mD
ragController
?.
removeDragListener
(
this
)
d
ragController
?.
removeDragListener
(
this
)
}
fun
isEditingName
():
Boolean
{
...
...
@@ -213,12 +340,6 @@ class Folder @JvmOverloads constructor(
return
false
}
fun
getFolderIcon
()
=
mFolderIcon
fun
setFolderIcon
(
icon
:
IconTextView
)
{
mFolderIcon
=
icon
}
override
fun
onAttachedToWindow
()
{
// requestFocus() causes the focus onto the folder itself, which doesn't cause visual
// effect but the next arrow key can start the keyboard focus inside of the folder, not
...
...
@@ -256,28 +377,135 @@ class Folder @JvmOverloads constructor(
mFolderTitleInput
.
setText
(
mInfo
.
title
)
mFolderTitleInput
.
isCursorVisible
=
false
val
mDeviceProfile
:
VariantDeviceProfile
=
mL
auncher
.
deviceProfile
mContent
?
.
adapter
=
val
mDeviceProfile
:
VariantDeviceProfile
=
l
auncher
.
deviceProfile
mContent
.
adapter
=
FolderPagerAdapter
(
context
,
mInfo
.
items
,
mDeviceProfile
)
// We use same size for height and width as we want to look it like sq
a
ure
// We use same size for height and width as we want to look it like squ
a
re
val
height
=
mDeviceProfile
.
cellHeightPx
*
3
+
resources
.
getDimensionPixelSize
(
R
.
dimen
.
folder_padding
)
mContent
?
.
layoutParams
?.
width
=
mContent
.
layoutParams
?.
width
=
mDeviceProfile
.
cellHeightPx
*
3
+
resources
.
getDimensionPixelSize
(
R
.
dimen
.
folder_padding
)
*
2
mContent
?
.
layoutParams
?.
height
=
mContent
.
layoutParams
?.
height
=
(
mDeviceProfile
.
cellHeightPx
+
mDeviceProfile
.
iconDrawablePaddingPx
*
2
)
*
3
+
resources
.
getDimensionPixelSize
(
R
.
dimen
.
folder_padding
)
*
2
mPageIndicator
.
setViewPager
(
mContent
)
// In case any children didn't come across during loading, clean up the folder accordingly
mF
olderIcon
?.
post
{
f
olderIcon
?.
post
{
if
(
getItemCount
()
<=
1
)
{
replaceFolderWithFinalItem
()
}
}
}
private
fun
startAnimation
(
a
:
AnimatorSet
)
{
if
(
mCurrentAnimator
!=
null
&&
mCurrentAnimator
!!
.
isRunning
)
{
mCurrentAnimator
?.
cancel
()
}
val
workspace
:
LauncherPagedView
=
launcher
.
getLauncherPagedView
()
val
currentCellLayout
:
CellLayout
=
workspace
.
getChildAt
(
workspace
.
currentPage
)
as
CellLayout
val
useHardware
=
shouldUseHardwareLayerForAnimation
(
currentCellLayout
)
val
wasHardwareAccelerated
:
Boolean
=
currentCellLayout
.
isHardwareLayerEnabled
()
a
.
addListener
(
object
:
AnimatorListenerAdapter
()
{
override
fun
onAnimationStart
(
animation
:
Animator
)
{
if
(
useHardware
)
{
currentCellLayout
.
enableHardwareLayer
(
true
)
}
mState
=
STATE_ANIMATING
mCurrentAnimator
=
a
}
override
fun
onAnimationEnd
(
animation
:
Animator
)
{
if
(
useHardware
)
{
currentCellLayout
.
enableHardwareLayer
(
wasHardwareAccelerated
)
}
mCurrentAnimator
=
null
}
})
a
.
start
()
}
private
fun
shouldUseHardwareLayerForAnimation
(
currentCellLayout
:
CellLayout
):
Boolean
{
var
folderCount
=
0
for
(
i
in
currentCellLayout
.
childCount
-
1
downTo
0
)
{
val
child
:
View
=
currentCellLayout
.
getChildAt
(
i
)
if
(
child
is
FolderIcon
)
++
folderCount
}
return
folderCount
>=
MIN_FOLDERS_FOR_HARDWARE_OPTIMIZATION
}
/**
* Opens the user folder described by the specified tag. The opening of the folder
* is animated relative to the specified View. If the View is null, no animation
* is played.
*/
fun
animateOpen
()
{
val
openFolder
=
getOpen
(
launcher
)
if
(
openFolder
!=
null
&&
openFolder
!==
this
)
{
// Close any open folder before opening a folder.
openFolder
.
close
(
true
)
}
mIsOpen
=
true
val
dragLayer
=
launcher
.
dragLayer
// Just verify that the folder hasn't already been added to the DragLayer.
// There was a one-off crash where the folder had a parent already.
if
(
parent
==
null
)
{
dragLayer
.
addView
(
this
,
BaseDragLayer
.
LayoutParams
(
ViewGroup
.
LayoutParams
.
MATCH_PARENT
,
ViewGroup
.
LayoutParams
.
MATCH_PARENT
))
}
else
{
Log
.
e
(
TAG
,
"Opening folder ("
+
this
+
") which already has a parent:"
+
parent
)
}
// mContent.completePendingPageChanges()
if
(!
mDragInProgress
)
{
// Open on the first page.
mContent
.
currentItem
=
0
}
// This is set to true in close(), but isn't reset to false until onDropCompleted(). This
// leads to an inconsistent state if you drag out of the folder and drag back in without
// dropping. One resulting issue is that replaceFolderWithFinalItem() can be called twice.
mDeleteFolderOnDropCompleted
=
false
// centerAboutIcon()
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
))
.
with
(
ObjectAnimator
.
ofFloat
(
launcher
.
getLauncherPagedView
().
pageIndicator
,
View
.
ALPHA
,
0f
)
)
anim
.
addListener
(
object
:
AnimatorListenerAdapter
()
{
override
fun
onAnimationStart
(
animation
:
Animator
)
{
}
override
fun
onAnimationEnd
(
animation
:
Animator
)
{
mState
=
STATE_OPEN
launcher
.
getLauncherPagedView
().
alpha
=
0f
launcher
.
hotseat
.
alpha
=
0f
launcher
.
getLauncherPagedView
().
pageIndicator
.
alpha
=
0f
}
override
fun
onAnimationCancel
(
animation
:
Animator
?)
{
launcher
.
getLauncherPagedView
().
alpha
=
1f
launcher
.
hotseat
.
alpha
=
1f
launcher
.
getLauncherPagedView
().
pageIndicator
.
alpha
=
1f
}
})
startAnimation
(
anim
)
// Make sure the folder picks up the last drag move even if the finger doesn't move.
if
(
dragController
!!
.
isDragging
)
{
dragController
!!
.
forceTouchMove
()
}
}
fun
completeDragExit
()
{
if
(
mIsOpen
)
{
close
(
true
)
...
...
@@ -308,63 +536,150 @@ class Folder @JvmOverloads constructor(
* otherwise it is ignored.
*/
fun
rearrangeChildren
(
itemCount
:
Int
)
{
val
views
:
ArrayList
<
View
>
=
getItemsInReadingOrder
()
mContent
.
arrangeChildren
(
views
,
Math
.
max
(
itemCount
,
views
.
size
))
mContent
.
adapter
?.
notifyDataSetChanged
()
mItemsInvalidated
=
true
}
fun
getItemCount
():
Int
{
return
mContent
.
getItemCount
()
}
override
fun
isDropEnabled
():
Boolean
{
TODO
(
"Not yet implemented"
)
}
override
fun
onDrop
(
dragObject
:
DropTarget
.
DragObject
?,
options
:
DragOptions
?)
{
TODO
(
"Not yet implemented"
)
}
override
fun
onDragEnter
(
dragObject
:
DropTarget
.
DragObject
?)
{
TODO
(
"Not yet implemented"
)
fun
isDestroyed
():
Boolean
{
return
mDestroyed
}
private
fun
replaceFolderWithFinalItem
()
{
// Add the last remaining child to the workspace in place of the folder
val
onCompleteRunnable
=
Runnable
{
val
itemCount
:
Int
=
mInfo
.
items
.
size
if
(
itemCount
<=
1
)
{
var
finalItem
:
LauncherItem
?
=
null
if
(
itemCount
==
1
)
{
// Move the item from the folder to the workspace, in the position of the
// folder
finalItem
=
mInfo
.
items
.
removeAt
(
0
)
finalItem
?.
apply
{
cell
=
mInfo
.
cell
screenId
=
mInfo
.
screenId
container
=
mInfo
.
container
}
}
// Remove the folder
launcher
.
getLauncherPagedView
().
removeItem
(
folderIcon
,
mInfo
/* deleteFromDb */
)
if
(
finalItem
!=
null
)
{
// We add the child after removing the folder to prevent both from existing
// at the same time in the CellLayout. We need to add the new item with
// addInScreenFromBind() to ensure that hotseat items are placed correctly.
launcher
.
getLauncherPagedView
().
bindItems
(
listOf
(
finalItem
),
true
)
}
launcher
.
getLauncherPagedView
().
updateDatabase
()
}
}
onCompleteRunnable
.
run
()
mDestroyed
=
true
}
override
fun
onDragOver
(
dragObject
:
DropTarget
.
DragObject
?)
{
TODO
(
"Not yet implemented"
)
fun
getItemCount
():
Int
{
Log
.
i
(
TAG
,
"getItemCount: "
+
mContent
.
getItemCount
()
+
" "
+
mInfo
.
items
.
size
)
return
mInfo
.
items
.
size
}
// This method keeps track of the first and last item in the folder for the purposes
// of keyboard focus
fun
updateTextViewFocus
()
{
val
firstChild
:
View
?
=
mContent
.
getFirstItem
()
val
lastChild
:
View
?
=
mContent
.
getLastItem
()
if
(
firstChild
!=
null
&&
lastChild
!=
null
)
{
mFolderTitleInput
.
nextFocusDownId
=
lastChild
.
id
mFolderTitleInput
.
nextFocusRightId
=
lastChild
.
id
mFolderTitleInput
.
nextFocusLeftId
=
lastChild
.
id
mFolderTitleInput
.
nextFocusUpId
=
lastChild
.
id
// Hitting TAB from the folder name wraps around to the first item on the current
// folder page, and hitting SHIFT+TAB from that item wraps back to the folder name.
mFolderTitleInput
.
nextFocusForwardId
=
firstChild
.
id
// When clicking off the folder when editing the name, this Folder gains focus. When
// pressing an arrow key from that state, give the focus to the first item.
this
.
nextFocusDownId
=
firstChild
.
id
this
.
nextFocusRightId
=
firstChild
.
id
this
.
nextFocusLeftId
=
firstChild
.
id
this
.
nextFocusUpId
=
firstChild
.
id
// When pressing shift+tab in the above state, give the focus to the last item.
setOnKeyListener
{
_
,
keyCode
,
event
->
val
isShiftPlusTab
=
keyCode
==
KeyEvent
.
KEYCODE_TAB
&&
event
.
hasModifiers
(
KeyEvent
.
META_SHIFT_ON
)
if
(
isShiftPlusTab
&&
this
@Folder
.
isFocused
)
{
lastChild
.
requestFocus
()
}
else
false
}
}
}