Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 6c2bed24 authored by Philip P. Moltmann's avatar Philip P. Moltmann Committed by Android (Google) Code Review
Browse files

Merge "Print Activity: Use a single (re-implemented) parsing logig for page ranges." into nyc-dev

parents 6cfecf4d c6c319e9
Loading
Loading
Loading
Loading
+2 −1
Original line number Diff line number Diff line
@@ -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>
+13 −87
Original line number Diff line number Diff line
@@ -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) {
                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,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);
+163 −0
Original line number Diff line number Diff line
@@ -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;

@@ -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.
     *