Implement Ctrl+B/I/U shorcut
These shortcut works as follows: - If the selected region is empty, do nothing. - If the selected region has at least one character that is not styled, e.g. bold, apply bold style. - If the selected region is fully styled, e.g. bold, remove bold style from the region. The default TextView/EditText supports styled editing, but it is used commonly as plain text fields, e.g. email address in the form, etc. So, not to style the text in such places, disable styled shortcut by default and let developers to enable it when necessary. Bug: 240628246 Test: TextViewStyleShortcutTest Change-Id: Id0f1cd85eb198553a852c5c2b628f5d03a2741e1
This commit is contained in:
@@ -636,6 +636,7 @@ package android {
|
||||
field public static final int ellipsize = 16842923; // 0x10100ab
|
||||
field public static final int ems = 16843096; // 0x1010158
|
||||
field public static final int enableOnBackInvokedCallback = 16844396; // 0x101066c
|
||||
field public static final int enableTextStylingShortcuts;
|
||||
field public static final int enableVrMode = 16844069; // 0x1010525
|
||||
field public static final int enabled = 16842766; // 0x101000e
|
||||
field public static final int end = 16843996; // 0x10104dc
|
||||
@@ -2113,6 +2114,7 @@ package android {
|
||||
field public static final int addToDictionary = 16908330; // 0x102002a
|
||||
field public static final int autofill = 16908355; // 0x1020043
|
||||
field public static final int background = 16908288; // 0x1020000
|
||||
field public static final int bold;
|
||||
field public static final int button1 = 16908313; // 0x1020019
|
||||
field public static final int button2 = 16908314; // 0x102001a
|
||||
field public static final int button3 = 16908315; // 0x102001b
|
||||
@@ -2138,6 +2140,7 @@ package android {
|
||||
field public static final int inputExtractAccessories = 16908378; // 0x102005a
|
||||
field public static final int inputExtractAction = 16908377; // 0x1020059
|
||||
field public static final int inputExtractEditText = 16908325; // 0x1020025
|
||||
field public static final int italic;
|
||||
field @Deprecated public static final int keyboardView = 16908326; // 0x1020026
|
||||
field public static final int list = 16908298; // 0x102000a
|
||||
field public static final int list_container = 16908351; // 0x102003f
|
||||
@@ -2169,6 +2172,7 @@ package android {
|
||||
field public static final int textAssist = 16908353; // 0x1020041
|
||||
field public static final int title = 16908310; // 0x1020016
|
||||
field public static final int toggle = 16908311; // 0x1020017
|
||||
field public static final int underline;
|
||||
field public static final int undo = 16908338; // 0x1020032
|
||||
field public static final int widget_frame = 16908312; // 0x1020018
|
||||
}
|
||||
@@ -55989,9 +55993,11 @@ package android.widget {
|
||||
ctor public EditText(android.content.Context, android.util.AttributeSet, int, int);
|
||||
method public void extendSelection(int);
|
||||
method public android.text.Editable getText();
|
||||
method public boolean isStyleShortcutEnabled();
|
||||
method public void selectAll();
|
||||
method public void setSelection(int, int);
|
||||
method public void setSelection(int);
|
||||
method public void setStyleShortcutsEnabled(boolean);
|
||||
}
|
||||
|
||||
public interface ExpandableListAdapter {
|
||||
|
||||
351
core/java/android/text/style/SpanUtils.java
Normal file
351
core/java/android/text/style/SpanUtils.java
Normal file
@@ -0,0 +1,351 @@
|
||||
/*
|
||||
* Copyright (C) 2022 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.
|
||||
*/
|
||||
|
||||
package android.text.style;
|
||||
|
||||
import android.annotation.IntRange;
|
||||
import android.annotation.NonNull;
|
||||
import android.graphics.Typeface;
|
||||
import android.text.Spannable;
|
||||
import android.text.Spanned;
|
||||
import android.util.LongArray;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
public class SpanUtils {
|
||||
private SpanUtils() {} // Do not instantiate
|
||||
|
||||
/**
|
||||
* Toggle the bold state of the given range.
|
||||
*
|
||||
* If there is at least one character is not bold in the given range, make the entire region to
|
||||
* be bold. If all characters of the given range is already bolded, this method removes bold
|
||||
* style from the given selection.
|
||||
*
|
||||
* @param spannable a spannable string
|
||||
* @param min minimum inclusive index of the selection.
|
||||
* @param max maximum exclusive index of the selection.
|
||||
* @return true if the selected region is toggled.
|
||||
*/
|
||||
public static boolean toggleBold(@NonNull Spannable spannable,
|
||||
@IntRange(from = 0) int min, @IntRange(from = 0) int max) {
|
||||
|
||||
if (min == max) {
|
||||
return false;
|
||||
}
|
||||
|
||||
final StyleSpan[] boldSpans = spannable.getSpans(min, max, StyleSpan.class);
|
||||
final ArrayList<StyleSpan> filteredBoldSpans = new ArrayList<>();
|
||||
for (StyleSpan span : boldSpans) {
|
||||
if ((span.getStyle() & Typeface.BOLD) == Typeface.BOLD) {
|
||||
filteredBoldSpans.add(span);
|
||||
}
|
||||
}
|
||||
|
||||
if (!isCovered(spannable, filteredBoldSpans, min, max)) {
|
||||
// At least one character doesn't have bold style. Making given region bold.
|
||||
spannable.setSpan(
|
||||
new StyleSpan(Typeface.BOLD), min, max, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Span covers the entire selection. Removing spans from tha region.
|
||||
for (int si = 0; si < filteredBoldSpans.size(); ++si) {
|
||||
final StyleSpan span = filteredBoldSpans.get(si);
|
||||
final int start = spannable.getSpanStart(span);
|
||||
final int end = spannable.getSpanEnd(span);
|
||||
final int flag = spannable.getSpanFlags(span);
|
||||
|
||||
// If BOLD_ITALIC style is attached, need to set ITALIC span to the subtracted range.
|
||||
final boolean needItalicSpan = (span.getStyle() & Typeface.ITALIC) == Typeface.ITALIC;
|
||||
|
||||
if (start < min) {
|
||||
if (end > max) {
|
||||
// selection: ------------|===================|----------------
|
||||
// span: <-------------------------------->
|
||||
// result: <-------> <---->
|
||||
spannable.setSpan(span, start, min, flag);
|
||||
spannable.setSpan(new StyleSpan(span.getStyle()), max, end, flag);
|
||||
if (needItalicSpan) {
|
||||
spannable.setSpan(new StyleSpan(Typeface.ITALIC), min, max, flag);
|
||||
}
|
||||
} else {
|
||||
// selection: ------------|===================|----------------
|
||||
// span: <----------->
|
||||
// result: <------->
|
||||
spannable.setSpan(span, start, min, flag);
|
||||
if (needItalicSpan) {
|
||||
spannable.setSpan(new StyleSpan(Typeface.ITALIC), min, end, flag);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (end > max) {
|
||||
// selection: ------------|===================|----------------
|
||||
// span: <------------------------>
|
||||
// result: <------------>
|
||||
spannable.setSpan(span, max, end, flag);
|
||||
if (needItalicSpan) {
|
||||
spannable.setSpan(new StyleSpan(Typeface.ITALIC), max, end, flag);
|
||||
}
|
||||
} else {
|
||||
// selection: ------------|===================|----------------
|
||||
// span: <----------->
|
||||
// result:
|
||||
spannable.removeSpan(span);
|
||||
if (needItalicSpan) {
|
||||
spannable.setSpan(new StyleSpan(Typeface.ITALIC), start, end, flag);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Toggle the italic state of the given range.
|
||||
*
|
||||
* If there is at least one character is not italic in the given range, make the entire region
|
||||
* to be italic. If all characters of the given range is already italic, this method removes
|
||||
* italic style from the given selection.
|
||||
*
|
||||
* @param spannable a spannable string
|
||||
* @param min minimum inclusive index of the selection.
|
||||
* @param max maximum exclusive index of the selection.
|
||||
* @return true if the selected region is toggled.
|
||||
*/
|
||||
public static boolean toggleItalic(@NonNull Spannable spannable,
|
||||
@IntRange(from = 0) int min, @IntRange(from = 0) int max) {
|
||||
|
||||
if (min == max) {
|
||||
return false;
|
||||
}
|
||||
|
||||
final StyleSpan[] boldSpans = spannable.getSpans(min, max, StyleSpan.class);
|
||||
final ArrayList<StyleSpan> filteredBoldSpans = new ArrayList<>();
|
||||
for (StyleSpan span : boldSpans) {
|
||||
if ((span.getStyle() & Typeface.ITALIC) == Typeface.ITALIC) {
|
||||
filteredBoldSpans.add(span);
|
||||
}
|
||||
}
|
||||
|
||||
if (!isCovered(spannable, filteredBoldSpans, min, max)) {
|
||||
// At least one character doesn't have italic style. Making given region italic.
|
||||
spannable.setSpan(
|
||||
new StyleSpan(Typeface.ITALIC), min, max, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Span covers the entire selection. Removing spans from tha region.
|
||||
for (int si = 0; si < filteredBoldSpans.size(); ++si) {
|
||||
final StyleSpan span = filteredBoldSpans.get(si);
|
||||
final int start = spannable.getSpanStart(span);
|
||||
final int end = spannable.getSpanEnd(span);
|
||||
final int flag = spannable.getSpanFlags(span);
|
||||
|
||||
// If BOLD_ITALIC style is attached, need to set BOLD span to the subtracted range.
|
||||
final boolean needBoldSpan = (span.getStyle() & Typeface.BOLD) == Typeface.BOLD;
|
||||
|
||||
if (start < min) {
|
||||
if (end > max) {
|
||||
// selection: ------------|===================|----------------
|
||||
// span: <-------------------------------->
|
||||
// result: <-------> <---->
|
||||
spannable.setSpan(span, start, min, flag);
|
||||
spannable.setSpan(new StyleSpan(span.getStyle()), max, end, flag);
|
||||
if (needBoldSpan) {
|
||||
spannable.setSpan(new StyleSpan(Typeface.BOLD), min, max, flag);
|
||||
}
|
||||
} else {
|
||||
// selection: ------------|===================|----------------
|
||||
// span: <----------->
|
||||
// result: <------->
|
||||
spannable.setSpan(span, start, min, flag);
|
||||
if (needBoldSpan) {
|
||||
spannable.setSpan(new StyleSpan(Typeface.BOLD), min, end, flag);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (end > max) {
|
||||
// selection: ------------|===================|----------------
|
||||
// span: <------------------------>
|
||||
// result: <------------>
|
||||
spannable.setSpan(span, max, end, flag);
|
||||
if (needBoldSpan) {
|
||||
spannable.setSpan(new StyleSpan(Typeface.BOLD), max, end, flag);
|
||||
}
|
||||
} else {
|
||||
// selection: ------------|===================|----------------
|
||||
// span: <----------->
|
||||
// result:
|
||||
spannable.removeSpan(span);
|
||||
if (needBoldSpan) {
|
||||
spannable.setSpan(new StyleSpan(Typeface.BOLD), start, end, flag);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Toggle the underline state of the given range.
|
||||
*
|
||||
* If there is at least one character is not underlined in the given range, make the entire
|
||||
* region to underlined. If all characters of the given range is already underlined, this
|
||||
* method removes underline from the given selection.
|
||||
*
|
||||
* @param spannable a spannable string
|
||||
* @param min minimum inclusive index of the selection.
|
||||
* @param max maximum exclusive index of the selection.
|
||||
* @return true if the selected region is toggled.
|
||||
*/
|
||||
public static boolean toggleUnderline(@NonNull Spannable spannable,
|
||||
@IntRange(from = 0) int min, @IntRange(from = 0) int max) {
|
||||
|
||||
if (min == max) {
|
||||
return false;
|
||||
}
|
||||
|
||||
final List<UnderlineSpan> spans =
|
||||
Arrays.asList(spannable.getSpans(min, max, UnderlineSpan.class));
|
||||
|
||||
if (!isCovered(spannable, spans, min, max)) {
|
||||
// At least one character doesn't have underline style. Making given region underline.
|
||||
spannable.setSpan(new UnderlineSpan(), min, max, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
|
||||
return true;
|
||||
}
|
||||
// Span covers the entire selection. Removing spans from tha region.
|
||||
for (int si = 0; si < spans.size(); ++si) {
|
||||
final UnderlineSpan span = spans.get(si);
|
||||
final int start = spannable.getSpanStart(span);
|
||||
final int end = spannable.getSpanEnd(span);
|
||||
final int flag = spannable.getSpanFlags(span);
|
||||
|
||||
if (start < min) {
|
||||
if (end > max) {
|
||||
// selection: ------------|===================|----------------
|
||||
// span: <-------------------------------->
|
||||
// result: <-------> <---->
|
||||
spannable.setSpan(span, start, min, flag);
|
||||
spannable.setSpan(new UnderlineSpan(), max, end, flag);
|
||||
} else {
|
||||
// selection: ------------|===================|----------------
|
||||
// span: <----------->
|
||||
// result: <------->
|
||||
spannable.setSpan(span, start, min, flag);
|
||||
}
|
||||
} else {
|
||||
if (end > max) {
|
||||
// selection: ------------|===================|----------------
|
||||
// span: <------------------------>
|
||||
// result: <------------>
|
||||
spannable.setSpan(span, max, end, flag);
|
||||
} else {
|
||||
// selection: ------------|===================|----------------
|
||||
// span: <----------->
|
||||
// result:
|
||||
spannable.removeSpan(span);
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private static long pack(int from, int to) {
|
||||
return ((long) from) << 32 | (long) to;
|
||||
}
|
||||
|
||||
private static int min(long packed) {
|
||||
return (int) (packed >> 32);
|
||||
}
|
||||
|
||||
private static int max(long packed) {
|
||||
return (int) (packed & 0xFFFFFFFFL);
|
||||
}
|
||||
|
||||
private static boolean hasIntersection(int aMin, int aMax, int bMin, int bMax) {
|
||||
return aMin < bMax && bMin < aMax;
|
||||
}
|
||||
|
||||
private static long intersection(int aMin, int aMax, int bMin, int bMax) {
|
||||
return pack(Math.max(aMin, bMin), Math.min(aMax, bMax));
|
||||
}
|
||||
|
||||
private static <T> boolean isCovered(@NonNull Spannable spannable, @NonNull List<T> spans,
|
||||
@IntRange(from = 0) int min, @IntRange(from = 0) int max) {
|
||||
|
||||
if (min == max) {
|
||||
return false;
|
||||
}
|
||||
|
||||
LongArray uncoveredRanges = new LongArray();
|
||||
LongArray nextUncoveredRanges = new LongArray();
|
||||
|
||||
uncoveredRanges.add(pack(min, max));
|
||||
|
||||
for (int si = 0; si < spans.size(); ++si) {
|
||||
final T span = spans.get(si);
|
||||
final int start = spannable.getSpanStart(span);
|
||||
final int end = spannable.getSpanEnd(span);
|
||||
|
||||
for (int i = 0; i < uncoveredRanges.size(); ++i) {
|
||||
final long packed = uncoveredRanges.get(i);
|
||||
final int uncoveredStart = min(packed);
|
||||
final int uncoveredEnd = max(packed);
|
||||
|
||||
if (!hasIntersection(start, end, uncoveredStart, uncoveredEnd)) {
|
||||
// This span doesn't affect this uncovered range. Try next span.
|
||||
nextUncoveredRanges.add(packed);
|
||||
} else {
|
||||
// This span has an intersection with uncovered range. Update the uncovered
|
||||
// range.
|
||||
long intersectionPack = intersection(start, end, uncoveredStart, uncoveredEnd);
|
||||
int intersectStart = min(intersectionPack);
|
||||
int intersectEnd = max(intersectionPack);
|
||||
|
||||
// Uncovered Range : ----------|=======================|-------------
|
||||
// Intersection : <---------->
|
||||
// Remaining uncovered ranges: ----------|=====|----------|======|-------------
|
||||
if (uncoveredStart != intersectStart) {
|
||||
// There is still uncovered area on the left.
|
||||
nextUncoveredRanges.add(pack(uncoveredStart, intersectStart));
|
||||
}
|
||||
if (intersectEnd != uncoveredEnd) {
|
||||
// There is still uncovered area on the right.
|
||||
nextUncoveredRanges.add(pack(intersectEnd, uncoveredEnd));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (nextUncoveredRanges.size() == 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Swap the uncoveredRanges and nextUncoveredRanges and clear the next one.
|
||||
final LongArray tmp = nextUncoveredRanges;
|
||||
nextUncoveredRanges = uncoveredRanges;
|
||||
uncoveredRanges = tmp;
|
||||
nextUncoveredRanges.clear();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -17,13 +17,17 @@
|
||||
package android.widget;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.Resources;
|
||||
import android.content.res.TypedArray;
|
||||
import android.text.Editable;
|
||||
import android.text.Selection;
|
||||
import android.text.Spannable;
|
||||
import android.text.TextUtils;
|
||||
import android.text.method.ArrowKeyMovementMethod;
|
||||
import android.text.method.MovementMethod;
|
||||
import android.text.style.SpanUtils;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.KeyEvent;
|
||||
|
||||
/*
|
||||
* This is supposed to be a *very* thin veneer over TextView.
|
||||
@@ -69,8 +73,18 @@ import android.util.AttributeSet;
|
||||
* See {@link android.R.styleable#EditText EditText Attributes},
|
||||
* {@link android.R.styleable#TextView TextView Attributes},
|
||||
* {@link android.R.styleable#View View Attributes}
|
||||
*
|
||||
* @attr ref android.R.styleable#EditText_enableTextStylingShortcuts
|
||||
*/
|
||||
public class EditText extends TextView {
|
||||
|
||||
// True if the style shortcut is enabled.
|
||||
private boolean mStyleShortcutsEnabled = false;
|
||||
|
||||
private static final int ID_BOLD = android.R.id.bold;
|
||||
private static final int ID_ITALIC = android.R.id.italic;
|
||||
private static final int ID_UNDERLINE = android.R.id.underline;
|
||||
|
||||
public EditText(Context context) {
|
||||
this(context, null);
|
||||
}
|
||||
@@ -85,6 +99,20 @@ public class EditText extends TextView {
|
||||
|
||||
public EditText(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
|
||||
super(context, attrs, defStyleAttr, defStyleRes);
|
||||
|
||||
final Resources.Theme theme = context.getTheme();
|
||||
final TypedArray a = theme.obtainStyledAttributes(attrs,
|
||||
com.android.internal.R.styleable.EditText, defStyleAttr, defStyleRes);
|
||||
|
||||
final int n = a.getIndexCount();
|
||||
for (int i = 0; i < n; ++i) {
|
||||
int attr = a.getIndex(i);
|
||||
switch (attr) {
|
||||
case com.android.internal.R.styleable.EditText_enableTextStylingShortcuts:
|
||||
mStyleShortcutsEnabled = a.getBoolean(attr, false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -178,4 +206,77 @@ public class EditText extends TextView {
|
||||
protected boolean supportsAutoSizeText() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onKeyShortcut(int keyCode, KeyEvent event) {
|
||||
if (event.hasModifiers(KeyEvent.META_CTRL_ON)) {
|
||||
// Handle Ctrl-only shortcuts.
|
||||
switch (keyCode) {
|
||||
case KeyEvent.KEYCODE_B:
|
||||
if (mStyleShortcutsEnabled && hasSelection()) {
|
||||
return onTextContextMenuItem(ID_BOLD);
|
||||
}
|
||||
break;
|
||||
case KeyEvent.KEYCODE_I:
|
||||
if (mStyleShortcutsEnabled && hasSelection()) {
|
||||
return onTextContextMenuItem(ID_ITALIC);
|
||||
}
|
||||
break;
|
||||
case KeyEvent.KEYCODE_U:
|
||||
if (mStyleShortcutsEnabled && hasSelection()) {
|
||||
return onTextContextMenuItem(ID_UNDERLINE);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
return super.onKeyShortcut(keyCode, event);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onTextContextMenuItem(int id) {
|
||||
// TODO: Move to switch-case once the resource ID is finalized.
|
||||
if (id == ID_BOLD || id == ID_ITALIC || id == ID_UNDERLINE) {
|
||||
return performStylingAction(id);
|
||||
}
|
||||
return super.onTextContextMenuItem(id);
|
||||
}
|
||||
|
||||
private boolean performStylingAction(int actionId) {
|
||||
final int selectionStart = getSelectionStart();
|
||||
final int selectionEnd = getSelectionEnd();
|
||||
if (selectionStart < 0 || selectionEnd < 0) {
|
||||
return false; // There is no selection.
|
||||
}
|
||||
int min = Math.min(selectionStart, selectionEnd);
|
||||
int max = Math.max(selectionStart, selectionEnd);
|
||||
|
||||
|
||||
Spannable spannable = getText();
|
||||
if (actionId == ID_BOLD) {
|
||||
return SpanUtils.toggleBold(spannable, min, max);
|
||||
} else if (actionId == ID_ITALIC) {
|
||||
return SpanUtils.toggleItalic(spannable, min, max);
|
||||
} else if (actionId == ID_UNDERLINE) {
|
||||
return SpanUtils.toggleUnderline(spannable, min, max);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enables styls shortcuts, e.g. Ctrl+B for making text bold.
|
||||
*
|
||||
* @param enabled true for enabled, false for disabled.
|
||||
*/
|
||||
public void setStyleShortcutsEnabled(boolean enabled) {
|
||||
mStyleShortcutsEnabled = enabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if style shortcut is enabled, otherwise returns false.
|
||||
* @return true if style shortcut is enabled, otherwise returns false.
|
||||
*/
|
||||
public boolean isStyleShortcutEnabled() {
|
||||
return mStyleShortcutsEnabled;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4509,6 +4509,8 @@
|
||||
</attr>
|
||||
</declare-styleable>
|
||||
<declare-styleable name="EditText">
|
||||
<!-- Enables styling shortcuts, e.g. Ctrl+B for bold. This is off by default. -->
|
||||
<attr name="enableTextStylingShortcuts" format="boolean" />
|
||||
</declare-styleable>
|
||||
<declare-styleable name="FastScroll">
|
||||
<!-- Drawable used for the scroll bar thumb. -->
|
||||
|
||||
@@ -70,6 +70,12 @@
|
||||
<item type="id" name="cut" />
|
||||
<item type="id" name="copy" />
|
||||
<item type="id" name="paste" />
|
||||
<!-- Editor action that makes selected text bold. -->
|
||||
<item type="id" name="bold" />
|
||||
<!-- Editor action that makes selected text italic. -->
|
||||
<item type="id" name="italic" />
|
||||
<!-- Editor action that makes selected text underline. -->
|
||||
<item type="id" name="underline" />
|
||||
<item type="id" name="copyUrl" />
|
||||
<item type="id" name="selectTextMode" />
|
||||
<item type="id" name="switchInputMethod" />
|
||||
|
||||
@@ -115,9 +115,13 @@
|
||||
<public name="handwritingBoundsOffsetRight" />
|
||||
<public name="handwritingBoundsOffsetBottom" />
|
||||
<public name="accessibilityDataPrivate" />
|
||||
<public name="enableTextStylingShortcuts" />
|
||||
</staging-public-group>
|
||||
|
||||
<staging-public-group type="id" first-id="0x01cd0000">
|
||||
<public name="bold" />
|
||||
<public name="italic" />
|
||||
<public name="underline" />
|
||||
</staging-public-group>
|
||||
|
||||
<staging-public-group type="style" first-id="0x01cc0000">
|
||||
|
||||
Reference in New Issue
Block a user