Home | History | Annotate | Download | only in phonenumberproto
      1 /*
      2  * Copyright (C) 2018 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.dialer.phonenumberproto;
     18 
     19 import android.support.annotation.NonNull;
     20 import android.support.annotation.WorkerThread;
     21 import android.support.v4.util.ArrayMap;
     22 import android.support.v4.util.ArraySet;
     23 import com.android.dialer.DialerPhoneNumber;
     24 import com.android.dialer.common.Assert;
     25 import com.google.common.collect.ImmutableMap;
     26 import com.google.common.collect.ImmutableSet;
     27 import java.util.Map;
     28 import java.util.Set;
     29 
     30 /**
     31  * Divides a set of {@link DialerPhoneNumber DialerPhoneNumbers} according to those that are valid
     32  * according to libphonenumber, and those that are not.
     33  *
     34  * <p>Numbers with post-dial portions are always considered invalid as most systems store E164
     35  * numbers which do not support post-dial portions.
     36  */
     37 public final class PartitionedNumbers {
     38   private final ImmutableMap<String, ImmutableSet<DialerPhoneNumber>>
     39       e164NumbersToDialerPhoneNumbers;
     40   private final ImmutableMap<String, ImmutableSet<DialerPhoneNumber>>
     41       invalidNumbersToDialerPhoneNumbers;
     42 
     43   /**
     44    * Divides a set of {@link DialerPhoneNumber DialerPhoneNumbers} according to those that are valid
     45    * according to libphonenumber, and those that are not.
     46    *
     47    * <p>Numbers with post-dial portions are always considered invalid as most systems store E164
     48    * numbers which do not support post-dial portions.
     49    */
     50   @WorkerThread
     51   public PartitionedNumbers(@NonNull ImmutableSet<DialerPhoneNumber> dialerPhoneNumbers) {
     52     Assert.isWorkerThread();
     53     Map<String, Set<DialerPhoneNumber>> e164MapBuilder = new ArrayMap<>();
     54     Map<String, Set<DialerPhoneNumber>> invalidMapBuilder = new ArrayMap<>();
     55 
     56     for (DialerPhoneNumber dialerPhoneNumber : dialerPhoneNumbers) {
     57       /*
     58        * Numbers with post-dial digits are considered valid and can be converted to E164, but their
     59        * post dial digits are lost in the process. For example, the normalized version of a number
     60        * with a post-dial portion in the contacts database is stored without the post-dial portion.
     61        */
     62       if (dialerPhoneNumber.getIsValid() && dialerPhoneNumber.getPostDialPortion().isEmpty()) {
     63         String validE164 = dialerPhoneNumber.getNormalizedNumber();
     64         Set<DialerPhoneNumber> currentNumbers = e164MapBuilder.get(validE164);
     65         if (currentNumbers == null) {
     66           currentNumbers = new ArraySet<>();
     67           e164MapBuilder.put(validE164, currentNumbers);
     68         }
     69         currentNumbers.add(dialerPhoneNumber);
     70       } else {
     71         String invalidNumber = dialerPhoneNumber.getNormalizedNumber();
     72         Set<DialerPhoneNumber> currentNumbers = invalidMapBuilder.get(invalidNumber);
     73         if (currentNumbers == null) {
     74           currentNumbers = new ArraySet<>();
     75           invalidMapBuilder.put(invalidNumber, currentNumbers);
     76         }
     77         currentNumbers.add(dialerPhoneNumber);
     78       }
     79     }
     80 
     81     e164NumbersToDialerPhoneNumbers = makeImmutable(e164MapBuilder);
     82     invalidNumbersToDialerPhoneNumbers = makeImmutable(invalidMapBuilder);
     83   }
     84 
     85   /** Returns the set of invalid numbers from the original DialerPhoneNumbers */
     86   @NonNull
     87   public ImmutableSet<String> invalidNumbers() {
     88     return invalidNumbersToDialerPhoneNumbers.keySet();
     89   }
     90 
     91   /** Returns the set of valid, E164 formatted numbers from the original DialerPhoneNumbers */
     92   @NonNull
     93   public ImmutableSet<String> validE164Numbers() {
     94     return e164NumbersToDialerPhoneNumbers.keySet();
     95   }
     96 
     97   /**
     98    * Returns the corresponding set of original DialerPhoneNumbers that map to the valid E164 number
     99    * from {@link #validE164Numbers()}.
    100    *
    101    * @throws NullPointerException if there are no numbers found
    102    */
    103   @NonNull
    104   public ImmutableSet<DialerPhoneNumber> dialerPhoneNumbersForValidE164(String validE164) {
    105     return Assert.isNotNull(e164NumbersToDialerPhoneNumbers.get(validE164));
    106   }
    107 
    108   /**
    109    * Returns the corresponding set of original DialerPhoneNumbers that map to the invalid number
    110    * from {@link #invalidNumbers()}.
    111    *
    112    * @throws NullPointerException if there are no numbers found
    113    */
    114   @NonNull
    115   public ImmutableSet<DialerPhoneNumber> dialerPhoneNumbersForInvalid(String invalidNumber) {
    116     return Assert.isNotNull(invalidNumbersToDialerPhoneNumbers.get(invalidNumber));
    117   }
    118 
    119   private static <K, V> ImmutableMap<K, ImmutableSet<V>> makeImmutable(
    120       Map<K, Set<V>> mutableMapOfSet) {
    121     ImmutableMap.Builder<K, ImmutableSet<V>> mapBuilder = ImmutableMap.builder();
    122     for (Map.Entry<K, Set<V>> entry : mutableMapOfSet.entrySet()) {
    123       mapBuilder.put(entry.getKey(), ImmutableSet.copyOf(entry.getValue()));
    124     }
    125     return mapBuilder.build();
    126   }
    127 }
    128