@Test public void testUnescapeInvalidFileName() { assertThat(Util.unescapeFileName("%a")).isNull(); assertThat(Util.unescapeFileName("%xyz")).isNull(); }
public static @Nullable String unescapeFileName(String fileName) { int length = fileName.length(); int percentCharacterCount = 0; for (int i = 0; i < length; i++) { if (fileName.charAt(i) == '%') { percentCharacterCount++; } } if (percentCharacterCount == 0) { return fileName; } int expectedLength = length - percentCharacterCount * 2; StringBuilder builder = new StringBuilder(expectedLength); Matcher matcher = ESCAPED_CHARACTER_PATTERN.matcher(fileName); int startOfNotEscaped = 0; while (percentCharacterCount > 0 && matcher.find()) { char unescapedCharacter = (char) Integer.parseInt(matcher.group(1), 16); builder.append(fileName, startOfNotEscaped, matcher.start()).append(unescapedCharacter); startOfNotEscaped = matcher.end(); percentCharacterCount--; } if (startOfNotEscaped < length) { builder.append(fileName, startOfNotEscaped, length); } if (builder.length() != expectedLength) { return null; } return builder.toString(); }
Util { public static @Nullable String unescapeFileName(String fileName) { int length = fileName.length(); int percentCharacterCount = 0; for (int i = 0; i < length; i++) { if (fileName.charAt(i) == '%') { percentCharacterCount++; } } if (percentCharacterCount == 0) { return fileName; } int expectedLength = length - percentCharacterCount * 2; StringBuilder builder = new StringBuilder(expectedLength); Matcher matcher = ESCAPED_CHARACTER_PATTERN.matcher(fileName); int startOfNotEscaped = 0; while (percentCharacterCount > 0 && matcher.find()) { char unescapedCharacter = (char) Integer.parseInt(matcher.group(1), 16); builder.append(fileName, startOfNotEscaped, matcher.start()).append(unescapedCharacter); startOfNotEscaped = matcher.end(); percentCharacterCount--; } if (startOfNotEscaped < length) { builder.append(fileName, startOfNotEscaped, length); } if (builder.length() != expectedLength) { return null; } return builder.toString(); } }
Util { public static @Nullable String unescapeFileName(String fileName) { int length = fileName.length(); int percentCharacterCount = 0; for (int i = 0; i < length; i++) { if (fileName.charAt(i) == '%') { percentCharacterCount++; } } if (percentCharacterCount == 0) { return fileName; } int expectedLength = length - percentCharacterCount * 2; StringBuilder builder = new StringBuilder(expectedLength); Matcher matcher = ESCAPED_CHARACTER_PATTERN.matcher(fileName); int startOfNotEscaped = 0; while (percentCharacterCount > 0 && matcher.find()) { char unescapedCharacter = (char) Integer.parseInt(matcher.group(1), 16); builder.append(fileName, startOfNotEscaped, matcher.start()).append(unescapedCharacter); startOfNotEscaped = matcher.end(); percentCharacterCount--; } if (startOfNotEscaped < length) { builder.append(fileName, startOfNotEscaped, length); } if (builder.length() != expectedLength) { return null; } return builder.toString(); } private  Util(); }
Util { public static @Nullable String unescapeFileName(String fileName) { int length = fileName.length(); int percentCharacterCount = 0; for (int i = 0; i < length; i++) { if (fileName.charAt(i) == '%') { percentCharacterCount++; } } if (percentCharacterCount == 0) { return fileName; } int expectedLength = length - percentCharacterCount * 2; StringBuilder builder = new StringBuilder(expectedLength); Matcher matcher = ESCAPED_CHARACTER_PATTERN.matcher(fileName); int startOfNotEscaped = 0; while (percentCharacterCount > 0 && matcher.find()) { char unescapedCharacter = (char) Integer.parseInt(matcher.group(1), 16); builder.append(fileName, startOfNotEscaped, matcher.start()).append(unescapedCharacter); startOfNotEscaped = matcher.end(); percentCharacterCount--; } if (startOfNotEscaped < length) { builder.append(fileName, startOfNotEscaped, length); } if (builder.length() != expectedLength) { return null; } return builder.toString(); } private  Util(); static byte[] toByteArray(InputStream inputStream); @Nullable static ComponentName startForegroundService(Context context, Intent intent); @TargetApi(23) static boolean maybeRequestReadExternalStoragePermission(Activity activity, Uri... uris); @TargetApi(24) static boolean checkCleartextTrafficPermitted(Uri... uris); static boolean isLocalFileUri(Uri uri); static boolean areEqual(@Nullable Object o1, @Nullable Object o2); static boolean contains(Object[] items, Object item); static void removeRange(List<T> list, int fromIndex, int toIndex); @SuppressWarnings({"contracts.postcondition.not.satisfied", "return.type.incompatible"}) @EnsuresNonNull("#1") static T castNonNull(@Nullable T value); @SuppressWarnings({"contracts.postcondition.not.satisfied", "return.type.incompatible"}) @EnsuresNonNull("#1") static T[] castNonNullTypeArray(@NullableType T[] value); @SuppressWarnings({"nullness:argument.type.incompatible", "nullness:return.type.incompatible"}) static T[] nullSafeArrayCopy(T[] input, int length); @SuppressWarnings({"nullness:assignment.type.incompatible"}) static T[] nullSafeArrayConcatenation(T[] first, T[] second); static Handler createHandler(Handler.@UnknownInitialization Callback callback); @SuppressWarnings({"nullness:argument.type.incompatible", "nullness:return.type.incompatible"}) static Handler createHandler(
      Looper looper, Handler.@UnknownInitialization Callback callback); static Looper getLooper(); static ExecutorService newSingleThreadExecutor(final String threadName); static void closeQuietly(@Nullable DataSource dataSource); static void closeQuietly(@Nullable Closeable closeable); static boolean readBoolean(Parcel parcel); static void writeBoolean(Parcel parcel, boolean value); static @PolyNull String normalizeLanguageCode(@PolyNull String language); static String fromUtf8Bytes(byte[] bytes); static String fromUtf8Bytes(byte[] bytes, int offset, int length); static byte[] getUtf8Bytes(String value); static String[] split(String value, String regex); static String[] splitAtFirst(String value, String regex); static boolean isLinebreak(int c); static @PolyNull String toLowerInvariant(@PolyNull String text); static @PolyNull String toUpperInvariant(@PolyNull String text); static String formatInvariant(String format, Object... args); static int ceilDivide(int numerator, int denominator); static long ceilDivide(long numerator, long denominator); static int constrainValue(int value, int min, int max); static long constrainValue(long value, long min, long max); static float constrainValue(float value, float min, float max); static long addWithOverflowDefault(long x, long y, long overflowResult); static long subtractWithOverflowDefault(long x, long y, long overflowResult); static int binarySearchFloor(int[] array, int value, boolean inclusive,
      boolean stayInBounds); static int binarySearchFloor(long[] array, long value, boolean inclusive,
      boolean stayInBounds); static int binarySearchFloor(
      List<? extends Comparable<? super T>> list,
      T value,
      boolean inclusive,
      boolean stayInBounds); static int binarySearchCeil(
      int[] array, int value, boolean inclusive, boolean stayInBounds); static int binarySearchCeil(
      long[] array, long value, boolean inclusive, boolean stayInBounds); static int binarySearchCeil(
      List<? extends Comparable<? super T>> list,
      T value,
      boolean inclusive,
      boolean stayInBounds); static int compareLong(long left, long right); static long parseXsDuration(String value); static long parseXsDateTime(String value); static long scaleLargeTimestamp(long timestamp, long multiplier, long divisor); static long[] scaleLargeTimestamps(List<Long> timestamps, long multiplier, long divisor); static void scaleLargeTimestampsInPlace(long[] timestamps, long multiplier, long divisor); static long getMediaDurationForPlayoutDuration(long playoutDuration, float speed); static long getPlayoutDurationForMediaDuration(long mediaDuration, float speed); static long resolveSeekPositionUs(
      long positionUs, SeekParameters seekParameters, long firstSyncUs, long secondSyncUs); static int @PolyNull [] toArray(@PolyNull List<Integer> list); static int getIntegerCodeForString(String string); static byte[] getBytesFromHexString(String hexString); static String getCommaDelimitedSimpleClassNames(Object[] objects); static String getUserAgent(Context context, String applicationName); static @Nullable String getCodecsOfType(String codecs, int trackType); static String[] splitCodecs(String codecs); @C.PcmEncoding static int getPcmEncoding(int bitDepth); static boolean isEncodingLinearPcm(@C.Encoding int encoding); static boolean isEncodingHighResolutionIntegerPcm(@C.PcmEncoding int encoding); static int getAudioTrackChannelConfig(int channelCount); static int getPcmFrameSize(@C.PcmEncoding int pcmEncoding, int channelCount); @C.AudioUsage static int getAudioUsageForStreamType(@C.StreamType int streamType); @C.AudioContentType static int getAudioContentTypeForStreamType(@C.StreamType int streamType); @C.StreamType static int getStreamTypeForAudioUsage(@C.AudioUsage int usage); static @Nullable UUID getDrmUuid(String drmScheme); @C.ContentType static int inferContentType(Uri uri, String overrideExtension); @C.ContentType static int inferContentType(Uri uri); @C.ContentType static int inferContentType(String fileName); static String getStringForTime(StringBuilder builder, Formatter formatter, long timeMs); static int getDefaultBufferSize(int trackType); static String escapeFileName(String fileName); static @Nullable String unescapeFileName(String fileName); static void sneakyThrow(Throwable t); static void recursiveDelete(File fileOrDirectory); static File createTempDirectory(Context context, String prefix); static File createTempFile(Context context, String prefix); static int crc(byte[] bytes, int start, int end, int initialValue); @C.NetworkType static int getNetworkType(Context context); static String getCountryCode(@Nullable Context context); static String[] getSystemLanguageCodes(); static boolean inflate(
      ParsableByteArray input, ParsableByteArray output, @Nullable Inflater inflater); static boolean isTv(Context context); static Point getPhysicalDisplaySize(Context context); static Point getPhysicalDisplaySize(Context context, Display display); static RendererCapabilities[] getRendererCapabilities(
      RenderersFactory renderersFactory,
      @Nullable DrmSessionManager<FrameworkMediaCrypto> drmSessionManager); }
Util { public static @Nullable String unescapeFileName(String fileName) { int length = fileName.length(); int percentCharacterCount = 0; for (int i = 0; i < length; i++) { if (fileName.charAt(i) == '%') { percentCharacterCount++; } } if (percentCharacterCount == 0) { return fileName; } int expectedLength = length - percentCharacterCount * 2; StringBuilder builder = new StringBuilder(expectedLength); Matcher matcher = ESCAPED_CHARACTER_PATTERN.matcher(fileName); int startOfNotEscaped = 0; while (percentCharacterCount > 0 && matcher.find()) { char unescapedCharacter = (char) Integer.parseInt(matcher.group(1), 16); builder.append(fileName, startOfNotEscaped, matcher.start()).append(unescapedCharacter); startOfNotEscaped = matcher.end(); percentCharacterCount--; } if (startOfNotEscaped < length) { builder.append(fileName, startOfNotEscaped, length); } if (builder.length() != expectedLength) { return null; } return builder.toString(); } private  Util(); static byte[] toByteArray(InputStream inputStream); @Nullable static ComponentName startForegroundService(Context context, Intent intent); @TargetApi(23) static boolean maybeRequestReadExternalStoragePermission(Activity activity, Uri... uris); @TargetApi(24) static boolean checkCleartextTrafficPermitted(Uri... uris); static boolean isLocalFileUri(Uri uri); static boolean areEqual(@Nullable Object o1, @Nullable Object o2); static boolean contains(Object[] items, Object item); static void removeRange(List<T> list, int fromIndex, int toIndex); @SuppressWarnings({"contracts.postcondition.not.satisfied", "return.type.incompatible"}) @EnsuresNonNull("#1") static T castNonNull(@Nullable T value); @SuppressWarnings({"contracts.postcondition.not.satisfied", "return.type.incompatible"}) @EnsuresNonNull("#1") static T[] castNonNullTypeArray(@NullableType T[] value); @SuppressWarnings({"nullness:argument.type.incompatible", "nullness:return.type.incompatible"}) static T[] nullSafeArrayCopy(T[] input, int length); @SuppressWarnings({"nullness:assignment.type.incompatible"}) static T[] nullSafeArrayConcatenation(T[] first, T[] second); static Handler createHandler(Handler.@UnknownInitialization Callback callback); @SuppressWarnings({"nullness:argument.type.incompatible", "nullness:return.type.incompatible"}) static Handler createHandler(
      Looper looper, Handler.@UnknownInitialization Callback callback); static Looper getLooper(); static ExecutorService newSingleThreadExecutor(final String threadName); static void closeQuietly(@Nullable DataSource dataSource); static void closeQuietly(@Nullable Closeable closeable); static boolean readBoolean(Parcel parcel); static void writeBoolean(Parcel parcel, boolean value); static @PolyNull String normalizeLanguageCode(@PolyNull String language); static String fromUtf8Bytes(byte[] bytes); static String fromUtf8Bytes(byte[] bytes, int offset, int length); static byte[] getUtf8Bytes(String value); static String[] split(String value, String regex); static String[] splitAtFirst(String value, String regex); static boolean isLinebreak(int c); static @PolyNull String toLowerInvariant(@PolyNull String text); static @PolyNull String toUpperInvariant(@PolyNull String text); static String formatInvariant(String format, Object... args); static int ceilDivide(int numerator, int denominator); static long ceilDivide(long numerator, long denominator); static int constrainValue(int value, int min, int max); static long constrainValue(long value, long min, long max); static float constrainValue(float value, float min, float max); static long addWithOverflowDefault(long x, long y, long overflowResult); static long subtractWithOverflowDefault(long x, long y, long overflowResult); static int binarySearchFloor(int[] array, int value, boolean inclusive,
      boolean stayInBounds); static int binarySearchFloor(long[] array, long value, boolean inclusive,
      boolean stayInBounds); static int binarySearchFloor(
      List<? extends Comparable<? super T>> list,
      T value,
      boolean inclusive,
      boolean stayInBounds); static int binarySearchCeil(
      int[] array, int value, boolean inclusive, boolean stayInBounds); static int binarySearchCeil(
      long[] array, long value, boolean inclusive, boolean stayInBounds); static int binarySearchCeil(
      List<? extends Comparable<? super T>> list,
      T value,
      boolean inclusive,
      boolean stayInBounds); static int compareLong(long left, long right); static long parseXsDuration(String value); static long parseXsDateTime(String value); static long scaleLargeTimestamp(long timestamp, long multiplier, long divisor); static long[] scaleLargeTimestamps(List<Long> timestamps, long multiplier, long divisor); static void scaleLargeTimestampsInPlace(long[] timestamps, long multiplier, long divisor); static long getMediaDurationForPlayoutDuration(long playoutDuration, float speed); static long getPlayoutDurationForMediaDuration(long mediaDuration, float speed); static long resolveSeekPositionUs(
      long positionUs, SeekParameters seekParameters, long firstSyncUs, long secondSyncUs); static int @PolyNull [] toArray(@PolyNull List<Integer> list); static int getIntegerCodeForString(String string); static byte[] getBytesFromHexString(String hexString); static String getCommaDelimitedSimpleClassNames(Object[] objects); static String getUserAgent(Context context, String applicationName); static @Nullable String getCodecsOfType(String codecs, int trackType); static String[] splitCodecs(String codecs); @C.PcmEncoding static int getPcmEncoding(int bitDepth); static boolean isEncodingLinearPcm(@C.Encoding int encoding); static boolean isEncodingHighResolutionIntegerPcm(@C.PcmEncoding int encoding); static int getAudioTrackChannelConfig(int channelCount); static int getPcmFrameSize(@C.PcmEncoding int pcmEncoding, int channelCount); @C.AudioUsage static int getAudioUsageForStreamType(@C.StreamType int streamType); @C.AudioContentType static int getAudioContentTypeForStreamType(@C.StreamType int streamType); @C.StreamType static int getStreamTypeForAudioUsage(@C.AudioUsage int usage); static @Nullable UUID getDrmUuid(String drmScheme); @C.ContentType static int inferContentType(Uri uri, String overrideExtension); @C.ContentType static int inferContentType(Uri uri); @C.ContentType static int inferContentType(String fileName); static String getStringForTime(StringBuilder builder, Formatter formatter, long timeMs); static int getDefaultBufferSize(int trackType); static String escapeFileName(String fileName); static @Nullable String unescapeFileName(String fileName); static void sneakyThrow(Throwable t); static void recursiveDelete(File fileOrDirectory); static File createTempDirectory(Context context, String prefix); static File createTempFile(Context context, String prefix); static int crc(byte[] bytes, int start, int end, int initialValue); @C.NetworkType static int getNetworkType(Context context); static String getCountryCode(@Nullable Context context); static String[] getSystemLanguageCodes(); static boolean inflate(
      ParsableByteArray input, ParsableByteArray output, @Nullable Inflater inflater); static boolean isTv(Context context); static Point getPhysicalDisplaySize(Context context); static Point getPhysicalDisplaySize(Context context, Display display); static RendererCapabilities[] getRendererCapabilities(
      RenderersFactory renderersFactory,
      @Nullable DrmSessionManager<FrameworkMediaCrypto> drmSessionManager); static final int SDK_INT; static final String DEVICE; static final String MANUFACTURER; static final String MODEL; static final String DEVICE_DEBUG_INFO; static final byte[] EMPTY_BYTE_ARRAY; }