Loading packages/PrintSpooler/res/layout/print_activity_controls.xml +2 −1 Original line number Diff line number Diff line Loading @@ -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> Loading packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java +13 −87 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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, Loading Loading @@ -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; Loading Loading @@ -1493,9 +1479,11 @@ public class PrintActivity extends Activity implements RemotePrintDocument.Updat cancelPrint(); } } else if (view == mMoreOptionsButton) { 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); Loading Loading @@ -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; Loading Loading @@ -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(); } } Loading @@ -2805,48 +2761,18 @@ 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; } String escapedText = PATTERN_ESCAPE_SPECIAL_CHARS.matcher(text).replaceAll("////"); if (!PATTERN_PAGE_RANGE.matcher(escapedText).matches()) { 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); // 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 (ranges.length == 0) { 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); Loading packages/PrintSpooler/src/com/android/printspooler/util/PageRangeUtils.java +163 −0 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -154,6 +156,167 @@ public final class PageRangeUtils { return normalRanges; } /** * 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. * Loading Loading
packages/PrintSpooler/res/layout/print_activity_controls.xml +2 −1 Original line number Diff line number Diff line Loading @@ -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> Loading
packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java +13 −87 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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, Loading Loading @@ -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; Loading Loading @@ -1493,9 +1479,11 @@ public class PrintActivity extends Activity implements RemotePrintDocument.Updat cancelPrint(); } } else if (view == mMoreOptionsButton) { 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); Loading Loading @@ -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; Loading Loading @@ -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(); } } Loading @@ -2805,48 +2761,18 @@ 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; } String escapedText = PATTERN_ESCAPE_SPECIAL_CHARS.matcher(text).replaceAll("////"); if (!PATTERN_PAGE_RANGE.matcher(escapedText).matches()) { 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); // 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 (ranges.length == 0) { 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); Loading
packages/PrintSpooler/src/com/android/printspooler/util/PageRangeUtils.java +163 −0 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -154,6 +156,167 @@ public final class PageRangeUtils { return normalRanges; } /** * 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. * Loading