diff options
3 files changed, 178 insertions, 88 deletions
diff --git a/packages/PrintSpooler/res/layout/print_activity_controls.xml b/packages/PrintSpooler/res/layout/print_activity_controls.xml index a87afe0a77a9..248d0c05934a 100644 --- a/packages/PrintSpooler/res/layout/print_activity_controls.xml +++ b/packages/PrintSpooler/res/layout/print_activity_controls.xml @@ -239,7 +239,8 @@ android:singleLine="true" android:ellipsize="end" android:visibility="visible" - android:inputType="textNoSuggestions"> + android:inputType="number" + android:digits="0123456789 ,-"> </com.android.printspooler.widget.CustomErrorEditText> </LinearLayout> diff --git a/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java b/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java index cde0fa3b4896..e7aebddf5aa8 100644 --- a/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java +++ b/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java @@ -62,7 +62,6 @@ import android.printservice.PrintServiceInfo; import android.provider.DocumentsContract; import android.text.Editable; import android.text.TextUtils; -import android.text.TextUtils.SimpleStringSplitter; import android.text.TextWatcher; import android.util.ArrayMap; import android.util.ArraySet; @@ -117,8 +116,6 @@ import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.Objects; -import java.util.regex.Matcher; -import java.util.regex.Pattern; public class PrintActivity extends Activity implements RemotePrintDocument.UpdateResultCallbacks, PrintErrorFragment.OnActionListener, PageAdapter.ContentCallbacks, @@ -165,22 +162,11 @@ public class PrintActivity extends Activity implements RemotePrintDocument.Updat private static final int MIN_COPIES = 1; private static final String MIN_COPIES_STRING = String.valueOf(MIN_COPIES); - private static final Pattern PATTERN_DIGITS = Pattern.compile("[\\d]+"); - - private static final Pattern PATTERN_ESCAPE_SPECIAL_CHARS = Pattern.compile( - "(?=[]\\[+&|!(){}^\"~*?:\\\\])"); - - private static final Pattern PATTERN_PAGE_RANGE = Pattern.compile( - "[\\s]*[0-9]+[\\-]?[\\s]*[0-9]*[\\s]*?(([,])" - + "[\\s]*[0-9]+[\\s]*[\\-]?[\\s]*[0-9]*[\\s]*|[\\s]*)+"); - private boolean mIsOptionsUiBound = false; private final PrinterAvailabilityDetector mPrinterAvailabilityDetector = new PrinterAvailabilityDetector(); - private final SimpleStringSplitter mStringCommaSplitter = new SimpleStringSplitter(','); - private final OnFocusChangeListener mSelectAllOnFocusListener = new SelectAllOnFocusListener(); private PrintSpoolerProvider mSpoolerProvider; @@ -1493,9 +1479,11 @@ public class PrintActivity extends Activity implements RemotePrintDocument.Updat cancelPrint(); } } else if (view == mMoreOptionsButton) { - // The selected pages is only applied once the user leaves the text field. A click - // on this button, does not count as leaving. - updateSelectedPagesFromTextField(); + if (mPageRangeEditText.getError() == null) { + // The selected pages is only applied once the user leaves the text field. A click + // on this button, does not count as leaving. + updateSelectedPagesFromTextField(); + } if (mCurrentPrinter != null) { startAdvancedPrintOptionsActivity(mCurrentPrinter); @@ -1918,42 +1906,10 @@ public class PrintActivity extends Activity implements RemotePrintDocument.Updat } if (mRangeOptionsSpinner.getSelectedItemPosition() > 0) { - List<PageRange> pageRanges = new ArrayList<>(); - mStringCommaSplitter.setString(mPageRangeEditText.getText().toString()); - - while (mStringCommaSplitter.hasNext()) { - String range = mStringCommaSplitter.next().trim(); - if (TextUtils.isEmpty(range)) { - continue; - } - final int dashIndex = range.indexOf('-'); - final int fromIndex; - final int toIndex; - - if (dashIndex > 0) { - fromIndex = Integer.parseInt(range.substring(0, dashIndex).trim()) - 1; - // It is possible that the dash is at the end since the input - // verification can has to allow the user to keep entering if - // this would lead to a valid input. So we handle this. - if (dashIndex < range.length() - 1) { - String fromString = range.substring(dashIndex + 1, range.length()).trim(); - toIndex = Integer.parseInt(fromString) - 1; - } else { - toIndex = fromIndex; - } - } else { - fromIndex = toIndex = Integer.parseInt(range) - 1; - } - - PageRange pageRange = new PageRange(Math.min(fromIndex, toIndex), - Math.max(fromIndex, toIndex)); - pageRanges.add(pageRange); - } - - PageRange[] pageRangesArray = new PageRange[pageRanges.size()]; - pageRanges.toArray(pageRangesArray); + PrintDocumentInfo info = mPrintedDocument.getDocumentInfo().info; + final int pageCount = (info != null) ? getAdjustedPageCount(info) : 0; - return PageRangeUtils.normalize(pageRangesArray); + return PageRangeUtils.parsePageRanges(mPageRangeEditText.getText(), pageCount); } return PageRange.ALL_PAGES_ARRAY; @@ -2785,7 +2741,7 @@ public class PrintActivity extends Activity implements RemotePrintDocument.Updat editText.setSelection(editText.getText().length()); } - if (view == mPageRangeEditText && !hasFocus) { + if (view == mPageRangeEditText && !hasFocus && mPageRangeEditText.getError() == null) { updateSelectedPagesFromTextField(); } } @@ -2805,18 +2761,12 @@ public class PrintActivity extends Activity implements RemotePrintDocument.Updat @Override public void afterTextChanged(Editable editable) { final boolean hadErrors = hasErrors(); - String text = editable.toString(); - if (TextUtils.isEmpty(text)) { - if (mPageRangeEditText.getError() == null) { - mPageRangeEditText.setError(""); - updateOptionsUi(); - } - return; - } + PrintDocumentInfo info = mPrintedDocument.getDocumentInfo().info; + final int pageCount = (info != null) ? getAdjustedPageCount(info) : 0; + PageRange[] ranges = PageRangeUtils.parsePageRanges(editable, pageCount); - String escapedText = PATTERN_ESCAPE_SPECIAL_CHARS.matcher(text).replaceAll("////"); - if (!PATTERN_PAGE_RANGE.matcher(escapedText).matches()) { + if (ranges.length == 0) { if (mPageRangeEditText.getError() == null) { mPageRangeEditText.setError(""); updateOptionsUi(); @@ -2824,30 +2774,6 @@ public class PrintActivity extends Activity implements RemotePrintDocument.Updat return; } - PrintDocumentInfo info = mPrintedDocument.getDocumentInfo().info; - final int pageCount = (info != null) ? getAdjustedPageCount(info) : 0; - - // The range - Matcher matcher = PATTERN_DIGITS.matcher(text); - while (matcher.find()) { - String numericString = text.substring(matcher.start(), matcher.end()).trim(); - if (TextUtils.isEmpty(numericString)) { - continue; - } - final int pageIndex = Integer.parseInt(numericString); - if (pageIndex < 1 || pageIndex > pageCount) { - if (mPageRangeEditText.getError() == null) { - mPageRangeEditText.setError(""); - updateOptionsUi(); - } - return; - } - } - - // We intentionally do not catch the case of the from page being - // greater than the to page. When computing the requested pages - // we just swap them if necessary. - if (mPageRangeEditText.getError() != null) { mPageRangeEditText.setError(null); updateOptionsUi(); diff --git a/packages/PrintSpooler/src/com/android/printspooler/util/PageRangeUtils.java b/packages/PrintSpooler/src/com/android/printspooler/util/PageRangeUtils.java index 2b317b3cbef0..7425c033a7a4 100644 --- a/packages/PrintSpooler/src/com/android/printspooler/util/PageRangeUtils.java +++ b/packages/PrintSpooler/src/com/android/printspooler/util/PageRangeUtils.java @@ -18,7 +18,9 @@ package com.android.printspooler.util; import android.print.PageRange; import android.print.PrintDocumentInfo; +import android.util.Pair; +import java.util.ArrayList; import java.util.Arrays; import java.util.Comparator; @@ -155,6 +157,167 @@ public final class PageRangeUtils { } /** + * Return the next position after {@code pos} that is not a space character. + * + * @param s The string to parse + * @param pos The starting position + * + * @return The position of the first space character + */ + private static int readWhiteSpace(CharSequence s, int pos) { + while (pos < s.length() && s.charAt(pos) == ' ') { + pos++; + } + + return pos; + } + + /** + * Read a number from a string at a certain position. + * + * @param s The string to parse + * @param pos The starting position + * + * @return The position after the number + the number read or null if the number was not found + */ + private static Pair<Integer, Integer> readNumber(CharSequence s, int pos) { + Integer result = 0; + while (pos < s.length() && s.charAt(pos) >= '0' && s.charAt(pos) <= '9') { + // Number cannot start with 0 + if (result == 0 && s.charAt(pos) == '0') { + break; + } + result = result * 10 + (s.charAt(pos) - '0'); + // Abort on overflow + if (result < 0) { + break; + } + pos++; + } + + // 0 is not a valid page number + if (result == 0) { + return new Pair<>(pos, null); + } else { + return new Pair<>(pos, result); + } + } + + /** + * Read a single character from a string at a certain position. + * + * @param s The string to parse + * @param pos The starting position + * @param expectedChar The character to read + * + * @return The position after the character + the character read or null if the character was + * not found + */ + private static Pair<Integer, Character> readChar(CharSequence s, int pos, char expectedChar) { + if (pos < s.length() && s.charAt(pos) == expectedChar) { + return new Pair<>(pos + 1, expectedChar); + } else { + return new Pair<>(pos, null); + } + } + + /** + * Read a page range character from a string at a certain position. + * + * @param s The string to parse + * @param pos The starting position + * @param maxPageNumber The highest page number to accept. + * + * @return The position after the page range + the page range read or null if the page range was + * not found + */ + private static Pair<Integer, PageRange> readRange(CharSequence s, int pos, int maxPageNumber) { + Pair<Integer, Integer> retInt; + Pair<Integer, Character> retChar; + + Character comma; + if (pos == 0) { + // When we reading the first range, we do not want to have a comma + comma = ','; + } else { + retChar = readChar(s, pos, ','); + pos = retChar.first; + comma = retChar.second; + } + + pos = readWhiteSpace(s, pos); + + retInt = readNumber(s, pos); + pos = retInt.first; + Integer start = retInt.second; + + pos = readWhiteSpace(s, pos); + + retChar = readChar(s, pos, '-'); + pos = retChar.first; + Character separator = retChar.second; + + pos = readWhiteSpace(s, pos); + + retInt = readNumber(s, pos); + pos = retInt.first; + Integer end = retInt.second; + + pos = readWhiteSpace(s, pos); + + if (comma != null && + // range, maybe unbounded + ((separator != null && (start != null || end != null)) || + // single page + (separator == null && start != null && end == null))) { + if (start == null) { + start = 1; + } + + if (end == null) { + if (separator == null) { + end = start; + } else { + end = maxPageNumber; + } + } + + if (start <= end && start >= 1 && end <= maxPageNumber) { + return new Pair<>(pos, new PageRange(start - 1, end - 1)); + } + } + + return new Pair<>(pos, null); + } + + /** + * Parse a string into an array of page ranges. + * + * @param s The string to parse + * @param maxPageNumber The highest page number to accept. + * + * @return The parsed ranges or null if the string could not be parsed. + */ + public static PageRange[] parsePageRanges(CharSequence s, int maxPageNumber) { + ArrayList<PageRange> ranges = new ArrayList<>(); + + int pos = 0; + while (pos < s.length()) { + Pair<Integer, PageRange> retRange = readRange(s, pos, maxPageNumber); + + if (retRange.second == null) { + ranges.clear(); + break; + } + + ranges.add(retRange.second); + pos = retRange.first; + } + + return PageRangeUtils.normalize(ranges.toArray(new PageRange[ranges.size()])); + } + + /** * Offsets a the start and end of page ranges with the given value. * * @param pageRanges The page ranges to offset. |