Home | History | Annotate | Download | only in util
      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.tuner.util;
     18 
     19 import android.content.Context;
     20 import android.location.Address;
     21 import android.support.annotation.NonNull;
     22 import android.support.annotation.Nullable;
     23 import android.text.TextUtils;
     24 import android.util.Log;
     25 import com.android.tv.tuner.TunerPreferences;
     26 import com.android.tv.util.LocationUtils;
     27 
     28 import java.io.IOException;
     29 import java.util.HashMap;
     30 import java.util.Locale;
     31 import java.util.Map;
     32 import java.util.regex.Pattern;
     33 
     34 /**
     35  * A utility class to update, get, and set the last known postal or zip code.
     36  */
     37 public class PostalCodeUtils {
     38     private static final String TAG = "PostalCodeUtils";
     39 
     40     // Postcode formats, where A signifies a letter and 9 a digit:
     41     // US zip code format: 99999
     42     private static final String POSTCODE_REGEX_US = "^(\\d{5})";
     43     // UK postcode district formats: A9, A99, AA9, AA99
     44     // Full UK postcode format: Postcode District + space + 9AA
     45     // Should be able to handle both postcode district and full postcode
     46     private static final String POSTCODE_REGEX_GB =
     47             "^([A-Z][A-Z]?[0-9][0-9A-Z]?)( ?[0-9][A-Z]{2})?$";
     48     private static final String POSTCODE_REGEX_GB_GIR = "^GIR( ?0AA)?$"; // special UK postcode
     49 
     50     private static final Map<String, Pattern> REGION_PATTERN = new HashMap<>();
     51     private static final Map<String, Integer> REGION_MAX_LENGTH = new HashMap<>();
     52 
     53     static {
     54         REGION_PATTERN.put(Locale.US.getCountry(), Pattern.compile(POSTCODE_REGEX_US));
     55         REGION_PATTERN.put(
     56                 Locale.UK.getCountry(),
     57                 Pattern.compile(POSTCODE_REGEX_GB + "|" + POSTCODE_REGEX_GB_GIR));
     58         REGION_MAX_LENGTH.put(Locale.US.getCountry(), 5);
     59         REGION_MAX_LENGTH.put(Locale.UK.getCountry(), 8);
     60     }
     61 
     62     // The longest postcode number is 10-character-long.
     63     // Use a larger number to accommodate future changes.
     64     private static final int DEFAULT_MAX_LENGTH = 16;
     65 
     66     /** Returns {@code true} if postal code has been changed */
     67     public static boolean updatePostalCode(Context context)
     68             throws IOException, SecurityException, NoPostalCodeException {
     69         String postalCode = getPostalCode(context);
     70         String lastPostalCode = getLastPostalCode(context);
     71         if (TextUtils.isEmpty(postalCode)) {
     72             if (TextUtils.isEmpty(lastPostalCode)) {
     73                 throw new NoPostalCodeException();
     74             }
     75         } else if (!TextUtils.equals(postalCode, lastPostalCode)) {
     76             setLastPostalCode(context, postalCode);
     77             return true;
     78         }
     79         return false;
     80     }
     81 
     82     /**
     83      * Gets the last stored postal or zip code, which might be decided by {@link LocationUtils} or
     84      * input by users.
     85      */
     86     public static String getLastPostalCode(Context context) {
     87         return TunerPreferences.getLastPostalCode(context);
     88     }
     89 
     90     /**
     91      * Sets the last stored postal or zip code. This method will overwrite the value written by
     92      * calling {@link #updatePostalCode(Context)}.
     93      */
     94     public static void setLastPostalCode(Context context, String postalCode) {
     95         Log.i(TAG, "Set Postal Code:" + postalCode);
     96         TunerPreferences.setLastPostalCode(context, postalCode);
     97     }
     98 
     99     @Nullable
    100     private static String getPostalCode(Context context) throws IOException, SecurityException {
    101         Address address = LocationUtils.getCurrentAddress(context);
    102         if (address != null) {
    103             Log.i(TAG, "Current country and postal code is " + address.getCountryName() + ", "
    104                     + address.getPostalCode());
    105             return address.getPostalCode();
    106         }
    107         return null;
    108     }
    109 
    110     /** An {@link java.lang.Exception} class to notify no valid postal or zip code is available. */
    111     public static class NoPostalCodeException extends Exception {
    112         public NoPostalCodeException() {
    113         }
    114     }
    115 
    116     /**
    117      * Checks whether a postcode matches the format of the specific region.
    118      *
    119      * @return {@code false} if the region is supported and the postcode doesn't match; {@code true}
    120      *     otherwise
    121      */
    122     public static boolean matches(@NonNull CharSequence postcode, @NonNull String region) {
    123         Pattern pattern = REGION_PATTERN.get(region.toUpperCase());
    124         return pattern == null || pattern.matcher(postcode).matches();
    125     }
    126 
    127     /**
    128      * Gets the largest possible postcode length in the region.
    129      *
    130      * @return maximum postcode length if the region is supported; {@link #DEFAULT_MAX_LENGTH}
    131      *     otherwise
    132      */
    133     public static int getRegionMaxLength(Context context) {
    134         Integer maxLength =
    135                 REGION_MAX_LENGTH.get(LocationUtils.getCurrentCountry(context).toUpperCase());
    136         return maxLength == null ? DEFAULT_MAX_LENGTH : maxLength;
    137     }
    138 }