001/* 002 * Copyright 2015 The AppAuth for Android Authors. All Rights Reserved. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 005 * in compliance with the License. You may obtain a copy of the License at 006 * 007 * http://www.apache.org/licenses/LICENSE-2.0 008 * 009 * Unless required by applicable law or agreed to in writing, software distributed under the 010 * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 011 * express or implied. See the License for the specific language governing permissions and 012 * limitations under the License. 013 */ 014 015package net.openid.appauth.internal; 016 017import android.net.Uri; 018import android.os.Bundle; 019import android.text.TextUtils; 020import androidx.annotation.NonNull; 021import androidx.annotation.Nullable; 022import androidx.browser.customtabs.CustomTabsService; 023import androidx.core.util.Pair; 024 025import net.openid.appauth.Preconditions; 026 027import java.io.UnsupportedEncodingException; 028import java.net.URLDecoder; 029import java.net.URLEncoder; 030import java.util.ArrayList; 031import java.util.Collections; 032import java.util.HashMap; 033import java.util.List; 034import java.util.Map; 035 036/** 037 * Utility methods for extracting parameters from Uri objects. 038 */ 039public final class UriUtil { 040 041 private UriUtil() { 042 throw new IllegalStateException("This type is not intended to be instantiated"); 043 } 044 045 public static Uri parseUriIfAvailable(@Nullable String uri) { 046 if (uri == null) { 047 return null; 048 } 049 050 return Uri.parse(uri); 051 } 052 053 public static void appendQueryParameterIfNotNull( 054 @NonNull Uri.Builder uriBuilder, 055 @NonNull String paramName, 056 @Nullable Object value) { 057 if (value == null) { 058 return; 059 } 060 061 String valueStr = value.toString(); 062 if (valueStr == null) { 063 return; 064 } 065 066 uriBuilder.appendQueryParameter(paramName, value.toString()); 067 } 068 069 public static Long getLongQueryParameter(@NonNull Uri uri, @NonNull String param) { 070 String valueStr = uri.getQueryParameter(param); 071 if (valueStr != null) { 072 return Long.parseLong(valueStr); 073 } 074 return null; 075 } 076 077 public static List<Bundle> toCustomTabUriBundle(Uri[] uris, int startIndex) { 078 Preconditions.checkArgument(startIndex >= 0, "startIndex must be positive"); 079 if (uris == null || uris.length <= startIndex) { 080 return Collections.emptyList(); 081 } 082 083 List<Bundle> uriBundles = new ArrayList<>(uris.length - startIndex); 084 for (int i = startIndex; i < uris.length; i++) { 085 if (uris[i] == null) { 086 Logger.warn("Null URI in possibleUris list - ignoring"); 087 continue; 088 } 089 090 Bundle uriBundle = new Bundle(); 091 uriBundle.putParcelable(CustomTabsService.KEY_URL, uris[i]); 092 uriBundles.add(uriBundle); 093 } 094 095 return uriBundles; 096 } 097 098 @NonNull 099 public static String formUrlEncode(@Nullable Map<String, String> parameters) { 100 if (parameters == null) { 101 return ""; 102 } 103 104 List<String> queryParts = new ArrayList<>(); 105 for (Map.Entry<String, String> param : parameters.entrySet()) { 106 queryParts.add(param.getKey() + "=" + formUrlEncodeValue(param.getValue())); 107 } 108 return TextUtils.join("&", queryParts); 109 } 110 111 @NonNull 112 public static String formUrlEncodeValue(@NonNull String value) { 113 Preconditions.checkNotNull(value); 114 try { 115 return URLEncoder.encode(value, "utf-8"); 116 } catch (UnsupportedEncodingException ex) { 117 // utf-8 should always be supported 118 throw new IllegalStateException("Unable to encode using UTF-8"); 119 } 120 } 121 122 public static List<Pair<String, String>> formUrlDecode(String encoded) { 123 if (TextUtils.isEmpty(encoded)) { 124 return Collections.emptyList(); 125 } 126 127 String[] parts = encoded.split("&"); 128 List<Pair<String, String>> params = new ArrayList<>(); 129 130 for (String part : parts) { 131 String[] paramAndValue = part.split("="); 132 String param = paramAndValue[0]; 133 String encodedValue = paramAndValue[1]; 134 135 try { 136 params.add(Pair.create(param, URLDecoder.decode(encodedValue, "utf-8"))); 137 } catch (UnsupportedEncodingException ex) { 138 Logger.error("Unable to decode parameter, ignoring", ex); 139 } 140 } 141 142 return params; 143 } 144 145 public static Map<String, String> formUrlDecodeUnique(String encoded) { 146 List<Pair<String, String>> params = UriUtil.formUrlDecode(encoded); 147 Map<String, String> uniqueParams = new HashMap<>(); 148 149 for (Pair<String, String> param : params) { 150 uniqueParams.put(param.first, param.second); 151 } 152 153 return uniqueParams; 154 } 155}