1 /* 2 * Copyright (C) 2017 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.tv.common.util; 18 19 import android.content.Context; 20 import android.support.annotation.NonNull; 21 import android.text.TextUtils; 22 import android.util.Log; 23 24 import com.android.tv.common.CommonPreferences; 25 26 import java.io.IOException; 27 import java.util.HashMap; 28 import java.util.Locale; 29 import java.util.Map; 30 import java.util.regex.Pattern; 31 32 /** A utility class to update, get, and set the last known postal or zip code. */ 33 public class PostalCodeUtils { 34 private static final String TAG = "PostalCodeUtils"; 35 36 // Postcode formats, where A signifies a letter and 9 a digit: 37 // US zip code format: 99999 38 private static final String POSTCODE_REGEX_US = "^(\\d{5})"; 39 // UK postcode district formats: A9, A99, AA9, AA99 40 // Full UK postcode format: Postcode District + space + 9AA 41 // Should be able to handle both postcode district and full postcode 42 private static final String POSTCODE_REGEX_GB = 43 "^([A-Z][A-Z]?[0-9][0-9A-Z]?)( ?[0-9][A-Z]{2})?$"; 44 private static final String POSTCODE_REGEX_GB_GIR = "^GIR( ?0AA)?$"; // special UK postcode 45 46 private static final Map<String, Pattern> REGION_PATTERN = new HashMap<>(); 47 private static final Map<String, Integer> REGION_MAX_LENGTH = new HashMap<>(); 48 49 static { Locale.US.getCountry()50 REGION_PATTERN.put(Locale.US.getCountry(), Pattern.compile(POSTCODE_REGEX_US)); 51 REGION_PATTERN.put( Locale.UK.getCountry()52 Locale.UK.getCountry(), 53 Pattern.compile(POSTCODE_REGEX_GB + "|" + POSTCODE_REGEX_GB_GIR)); Locale.US.getCountry()54 REGION_MAX_LENGTH.put(Locale.US.getCountry(), 5); Locale.UK.getCountry()55 REGION_MAX_LENGTH.put(Locale.UK.getCountry(), 8); 56 } 57 58 // The longest postcode number is 10-character-long. 59 // Use a larger number to accommodate future changes. 60 private static final int DEFAULT_MAX_LENGTH = 16; 61 62 /** Returns {@code true} if postal code has been changed */ updatePostalCode(Context context)63 public static boolean updatePostalCode(Context context) 64 throws IOException, SecurityException, NoPostalCodeException { 65 String postalCode = LocationUtils.getCurrentPostalCode(context); 66 String lastPostalCode = getLastPostalCode(context); 67 if (TextUtils.isEmpty(postalCode)) { 68 if (TextUtils.isEmpty(lastPostalCode)) { 69 throw new NoPostalCodeException(); 70 } 71 } else if (!TextUtils.equals(postalCode, lastPostalCode)) { 72 setLastPostalCode(context, postalCode); 73 return true; 74 } 75 return false; 76 } 77 78 /** 79 * Gets the last stored postal or zip code, which might be decided by {@link LocationUtils} or 80 * input by users. 81 */ getLastPostalCode(Context context)82 public static String getLastPostalCode(Context context) { 83 return CommonPreferences.getLastPostalCode(context); 84 } 85 86 /** 87 * Sets the last stored postal or zip code. This method will overwrite the value written by 88 * calling {@link #updatePostalCode(Context)}. 89 */ setLastPostalCode(Context context, String postalCode)90 public static void setLastPostalCode(Context context, String postalCode) { 91 Log.i(TAG, "Set Postal Code:" + postalCode); 92 CommonPreferences.setLastPostalCode(context, postalCode); 93 } 94 95 /** An {@link java.lang.Exception} class to notify no valid postal or zip code is available. */ 96 public static class NoPostalCodeException extends Exception { NoPostalCodeException()97 public NoPostalCodeException() {} 98 } 99 100 /** 101 * Checks whether a postcode matches the format of the specific region. 102 * 103 * @return {@code false} if the region is supported and the postcode doesn't match; {@code true} 104 * otherwise 105 */ matches(@onNull CharSequence postcode, @NonNull String region)106 public static boolean matches(@NonNull CharSequence postcode, @NonNull String region) { 107 Pattern pattern = REGION_PATTERN.get(region.toUpperCase()); 108 return pattern == null || pattern.matcher(postcode).matches(); 109 } 110 111 /** 112 * Gets the largest possible postcode length in the region. 113 * 114 * @return maximum postcode length if the region is supported; {@link #DEFAULT_MAX_LENGTH} 115 * otherwise 116 */ getRegionMaxLength(Context context)117 public static int getRegionMaxLength(Context context) { 118 Integer maxLength = 119 REGION_MAX_LENGTH.get(LocationUtils.getCurrentCountry(context).toUpperCase()); 120 return maxLength == null ? DEFAULT_MAX_LENGTH : maxLength; 121 } 122 } 123