| /* |
| * Copyright (C) 2013 ZXing authors |
| * |
| * 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 com.google.zxing.client.android; |
| |
| import java.util.EnumMap; |
| import java.util.HashMap; |
| import java.util.Map; |
| import java.util.regex.Pattern; |
| |
| import android.content.Intent; |
| import android.net.Uri; |
| import android.os.Bundle; |
| import android.util.Log; |
| |
| import com.google.zxing.DecodeHintType; |
| |
| /** |
| * @author Lachezar Dobrev |
| */ |
| final class DecodeHintManager { |
| |
| private static final String TAG = DecodeHintManager.class.getSimpleName(); |
| |
| // This pattern is used in decoding integer arrays. |
| private static final Pattern COMMA = Pattern.compile(","); |
| |
| private DecodeHintManager() {} |
| |
| /** |
| * <p>Split a query string into a list of name-value pairs.</p> |
| * |
| * <p>This is an alternative to the {@link Uri#getQueryParameterNames()} and |
| * {@link Uri#getQueryParameters(String)}, which are quirky and not suitable |
| * for exist-only Uri parameters.</p> |
| * |
| * <p>This method ignores multiple parameters with the same name and returns the |
| * first one only. This is technically incorrect, but should be acceptable due |
| * to the method of processing Hints: no multiple values for a hint.</p> |
| * |
| * @param query query to split |
| * @return name-value pairs |
| */ |
| private static Map<String,String> splitQuery(String query) { |
| Map<String,String> map = new HashMap<>(); |
| int pos = 0; |
| while (pos < query.length()) { |
| if (query.charAt(pos) == '&') { |
| // Skip consecutive ampersand separators. |
| pos ++; |
| continue; |
| } |
| int amp = query.indexOf('&', pos); |
| int equ = query.indexOf('=', pos); |
| if (amp < 0) { |
| // This is the last element in the query, no more ampersand elements. |
| String name; |
| String text; |
| if (equ < 0) { |
| // No equal sign |
| name = query.substring(pos); |
| name = name.replace('+', ' '); // Preemptively decode + |
| name = Uri.decode(name); |
| text = ""; |
| } else { |
| // Split name and text. |
| name = query.substring(pos, equ); |
| name = name.replace('+', ' '); // Preemptively decode + |
| name = Uri.decode(name); |
| text = query.substring(equ + 1); |
| text = text.replace('+', ' '); // Preemptively decode + |
| text = Uri.decode(text); |
| } |
| if (!map.containsKey(name)) { |
| map.put(name, text); |
| } |
| break; |
| } |
| if (equ < 0 || equ > amp) { |
| // No equal sign until the &: this is a simple parameter with no value. |
| String name = query.substring(pos, amp); |
| name = name.replace('+', ' '); // Preemptively decode + |
| name = Uri.decode(name); |
| if (!map.containsKey(name)) { |
| map.put(name, ""); |
| } |
| pos = amp + 1; |
| continue; |
| } |
| String name = query.substring(pos, equ); |
| name = name.replace('+', ' '); // Preemptively decode + |
| name = Uri.decode(name); |
| String text = query.substring(equ+1, amp); |
| text = text.replace('+', ' '); // Preemptively decode + |
| text = Uri.decode(text); |
| if (!map.containsKey(name)) { |
| map.put(name, text); |
| } |
| pos = amp + 1; |
| } |
| return map; |
| } |
| |
| static Map<DecodeHintType,?> parseDecodeHints(Uri inputUri) { |
| String query = inputUri.getEncodedQuery(); |
| if (query == null || query.isEmpty()) { |
| return null; |
| } |
| |
| // Extract parameters |
| Map<String, String> parameters = splitQuery(query); |
| |
| Map<DecodeHintType, Object> hints = new EnumMap<>(DecodeHintType.class); |
| |
| for (DecodeHintType hintType: DecodeHintType.values()) { |
| |
| if (hintType == DecodeHintType.CHARACTER_SET || |
| hintType == DecodeHintType.NEED_RESULT_POINT_CALLBACK || |
| hintType == DecodeHintType.POSSIBLE_FORMATS) { |
| continue; // This hint is specified in another way |
| } |
| |
| String parameterName = hintType.name(); |
| String parameterText = parameters.get(parameterName); |
| if (parameterText == null) { |
| continue; |
| } |
| if (hintType.getValueType().equals(Object.class)) { |
| // This is an unspecified type of hint content. Use the value as is. |
| // TODO: Can we make a different assumption on this? |
| hints.put(hintType, parameterText); |
| continue; |
| } |
| if (hintType.getValueType().equals(Void.class)) { |
| // Void hints are just flags: use the constant specified by DecodeHintType |
| hints.put(hintType, Boolean.TRUE); |
| continue; |
| } |
| if (hintType.getValueType().equals(String.class)) { |
| // A string hint: use the decoded value. |
| hints.put(hintType, parameterText); |
| continue; |
| } |
| if (hintType.getValueType().equals(Boolean.class)) { |
| // A boolean hint: a few values for false, everything else is true. |
| // An empty parameter is simply a flag-style parameter, assuming true |
| if (parameterText.isEmpty()) { |
| hints.put(hintType, Boolean.TRUE); |
| } else if ("0".equals(parameterText) || |
| "false".equalsIgnoreCase(parameterText) || |
| "no".equalsIgnoreCase(parameterText)) { |
| hints.put(hintType, Boolean.FALSE); |
| } else { |
| hints.put(hintType, Boolean.TRUE); |
| } |
| |
| continue; |
| } |
| if (hintType.getValueType().equals(int[].class)) { |
| // An integer array. Used to specify valid lengths. |
| // Strip a trailing comma as in Java style array initialisers. |
| if (!parameterText.isEmpty() && parameterText.charAt(parameterText.length() - 1) == ',') { |
| parameterText = parameterText.substring(0, parameterText.length() - 1); |
| } |
| String[] values = COMMA.split(parameterText); |
| int[] array = new int[values.length]; |
| for (int i = 0; i < values.length; i++) { |
| try { |
| array[i] = Integer.parseInt(values[i]); |
| } catch (NumberFormatException ignored) { |
| Log.w(TAG, "Skipping array of integers hint " + hintType + " due to invalid numeric value: '" + values[i] + '\''); |
| array = null; |
| break; |
| } |
| } |
| if (array != null) { |
| hints.put(hintType, array); |
| } |
| continue; |
| } |
| Log.w(TAG, "Unsupported hint type '" + hintType + "' of type " + hintType.getValueType()); |
| } |
| |
| Log.i(TAG, "Hints from the URI: " + hints); |
| return hints; |
| } |
| |
| static Map<DecodeHintType, Object> parseDecodeHints(Intent intent) { |
| Bundle extras = intent.getExtras(); |
| if (extras == null || extras.isEmpty()) { |
| return null; |
| } |
| Map<DecodeHintType,Object> hints = new EnumMap<>(DecodeHintType.class); |
| |
| for (DecodeHintType hintType: DecodeHintType.values()) { |
| |
| if (hintType == DecodeHintType.CHARACTER_SET || |
| hintType == DecodeHintType.NEED_RESULT_POINT_CALLBACK || |
| hintType == DecodeHintType.POSSIBLE_FORMATS) { |
| continue; // This hint is specified in another way |
| } |
| |
| String hintName = hintType.name(); |
| if (extras.containsKey(hintName)) { |
| if (hintType.getValueType().equals(Void.class)) { |
| // Void hints are just flags: use the constant specified by the DecodeHintType |
| hints.put(hintType, Boolean.TRUE); |
| } else { |
| Object hintData = extras.get(hintName); |
| if (hintType.getValueType().isInstance(hintData)) { |
| hints.put(hintType, hintData); |
| } else { |
| Log.w(TAG, "Ignoring hint " + hintType + " because it is not assignable from " + hintData); |
| } |
| } |
| } |
| } |
| |
| Log.i(TAG, "Hints from the Intent: " + hints); |
| return hints; |
| } |
| |
| } |