[go: nahoru, domu]

[Text Selection] Get Selected Text.

-- Add getText to Selectable.
-- Add getSelectedText to SelectionManager.

Bug: 139311166
Test: ./gradlew test
Test: ./gradlew buildOnServer
Test: ./gradlew :ui:ui-framework:connectedAndroidTest
Change-Id: I5fbabc57c11227cf30b61a9aadaa4d08193d2da3
diff --git a/ui/ui-framework/src/main/java/androidx/ui/core/selection/SelectionManager.kt b/ui/ui-framework/src/main/java/androidx/ui/core/selection/SelectionManager.kt
index 8e0403b..cd65aae 100644
--- a/ui/ui-framework/src/main/java/androidx/ui/core/selection/SelectionManager.kt
+++ b/ui/ui-framework/src/main/java/androidx/ui/core/selection/SelectionManager.kt
@@ -21,6 +21,9 @@
 import androidx.ui.core.gesture.LongPressDragObserver
 import androidx.ui.core.hapticfeedback.HapticFeedback
 import androidx.ui.core.hapticfeedback.HapticFeedbackType
+import androidx.ui.text.AnnotatedString
+import androidx.ui.text.length
+import androidx.ui.text.subSequence
 import androidx.ui.unit.PxPosition
 import androidx.ui.unit.px
 
@@ -112,6 +115,32 @@
         return newSelection
     }
 
+    internal fun getSelectedText(): AnnotatedString? {
+        val selectables = selectionRegistrar.sort(containerLayoutCoordinates)
+        var selectedText: AnnotatedString? = null
+
+        selection?.let {
+            for (handler in selectables) {
+                // Continue if the current selectable is before the selection starts.
+                if (handler != it.start.selectable && handler != it.end.selectable &&
+                    selectedText == null
+                ) continue
+
+                val currentSelectedText = getCurrentSelectedText(
+                    selectable = handler,
+                    selection = it
+                )
+                selectedText = selectedText?.plus(currentSelectedText) ?: currentSelectedText
+
+                // Break if the current selectable is the last selected selectable.
+                if (handler == it.end.selectable && !it.handlesCrossed ||
+                    handler == it.start.selectable && it.handlesCrossed
+                ) break
+            }
+        }
+        return selectedText
+    }
+
     // This is for PressGestureDetector to cancel the selection.
     fun onRelease() {
         // Call mergeSelections with an out of boundary input to inform all text widgets to
@@ -263,3 +292,46 @@
 private fun merge(lhs: Selection?, rhs: Selection?): Selection? {
     return lhs?.merge(rhs) ?: rhs
 }
+
+private fun getCurrentSelectedText(
+    selectable: Selectable,
+    selection: Selection
+): AnnotatedString {
+    val currentText = selectable.getText()
+
+    return if (
+        selectable != selection.start.selectable &&
+        selectable != selection.end.selectable
+    ) {
+        // Select the full text content if the current selectable is between the
+        // start and the end selectables.
+        currentText
+    } else if (
+        selectable == selection.start.selectable &&
+        selectable == selection.end.selectable
+    ) {
+        // Select partial text content if the current selectable is the start and
+        // the end selectable.
+        if (selection.handlesCrossed) {
+            currentText.subSequence(selection.end.offset, selection.start.offset)
+        } else {
+            currentText.subSequence(selection.start.offset, selection.end.offset)
+        }
+    } else if (selectable == selection.start.selectable) {
+        // Select partial text content if the current selectable is the start
+        // selectable.
+        if (selection.handlesCrossed) {
+            currentText.subSequence(0, selection.start.offset)
+        } else {
+            currentText.subSequence(selection.start.offset, currentText.length)
+        }
+    } else {
+        // Selectable partial text content if the current selectable is the end
+        // selectable.
+        if (selection.handlesCrossed) {
+            currentText.subSequence(selection.end.offset, currentText.length)
+        } else {
+            currentText.subSequence(0, selection.end.offset)
+        }
+    }
+}