1 /* 2 * Copyright (C) 2016 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.incallui.incall.impl; 18 19 import android.support.annotation.NonNull; 20 import android.support.v4.util.ArrayMap; 21 import android.util.ArraySet; 22 import com.android.dialer.common.Assert; 23 import com.android.incallui.incall.protocol.InCallButtonIds; 24 import com.android.incallui.incall.protocol.InCallButtonIdsExtension; 25 import com.google.auto.value.AutoValue; 26 import java.util.ArrayList; 27 import java.util.Collections; 28 import java.util.Comparator; 29 import java.util.List; 30 import java.util.Map; 31 import java.util.Map.Entry; 32 import java.util.Set; 33 import javax.annotation.concurrent.Immutable; 34 35 /** 36 * Determines logical button slot and ordering based on a provided mapping. 37 * 38 * <p>The provided mapping is declared with the following pieces of information: key, the {@link 39 * InCallButtonIds} for which the mapping applies; {@link MappingInfo#getSlot()}, the arbitrarily 40 * indexed slot into which the InCallButtonId desires to be placed; {@link 41 * MappingInfo#getSlotOrder()}, the slotOrder, used to choose the correct InCallButtonId when 42 * multiple desire to be placed in the same slot; and {@link MappingInfo#getConflictOrder()}, the 43 * conflictOrder, used to determine the overall order for InCallButtonIds that weren't chosen for 44 * their desired slot. 45 */ 46 @Immutable 47 final class MappedButtonConfig { 48 49 @NonNull private final Map<Integer, MappingInfo> mapping; 50 @NonNull private final List<Integer> orderedMappedSlots; 51 52 /** 53 * Creates this MappedButtonConfig with the given mapping of {@link InCallButtonIds} to their 54 * corresponding slots and order. 55 * 56 * @param mapping the mapping. 57 */ 58 public MappedButtonConfig(@NonNull Map<Integer, MappingInfo> mapping) { 59 this.mapping = new ArrayMap<>(); 60 this.mapping.putAll(Assert.isNotNull(mapping)); 61 this.orderedMappedSlots = findOrderedMappedSlots(); 62 } 63 64 private List<Integer> findOrderedMappedSlots() { 65 Set<Integer> slots = new ArraySet<>(); 66 for (Entry<Integer, MappingInfo> entry : mapping.entrySet()) { 67 slots.add(entry.getValue().getSlot()); 68 } 69 List<Integer> orderedSlots = new ArrayList<>(slots); 70 Collections.sort(orderedSlots); 71 return orderedSlots; 72 } 73 74 /** Returns an immutable list of the slots for which this class has button mapping. */ 75 @NonNull 76 public List<Integer> getOrderedMappedSlots() { 77 if (mapping.isEmpty()) { 78 return Collections.emptyList(); 79 } 80 return Collections.unmodifiableList(orderedMappedSlots); 81 } 82 83 /** 84 * Returns a list of {@link InCallButtonIds} that are configured to be placed in the given ui 85 * slot. The slot can be based from any index, as long as it matches the provided mapping. 86 */ 87 @NonNull 88 public List<Integer> getButtonsForSlot(int slot) { 89 List<Integer> buttons = new ArrayList<>(); 90 for (Entry<Integer, MappingInfo> entry : mapping.entrySet()) { 91 if (entry.getValue().getSlot() == slot) { 92 buttons.add(entry.getKey()); 93 } 94 } 95 return buttons; 96 } 97 98 /** 99 * Returns a {@link Comparator} capable of ordering {@link InCallButtonIds} that are configured to 100 * be placed in the same slot. InCallButtonIds are sorted based on the natural ordering of {@link 101 * MappingInfo#getSlotOrder()}. 102 * 103 * <p>Note: the returned Comparator's compare method will throw an {@link 104 * IllegalArgumentException} if called with InCallButtonIds that have no configuration or are not 105 * to be placed in the same slot. 106 */ 107 @NonNull 108 public Comparator<Integer> getSlotComparator() { 109 return new Comparator<Integer>() { 110 @Override 111 public int compare(Integer lhs, Integer rhs) { 112 MappingInfo lhsInfo = lookupMappingInfo(lhs); 113 MappingInfo rhsInfo = lookupMappingInfo(rhs); 114 if (lhsInfo.getSlot() != rhsInfo.getSlot()) { 115 throw new IllegalArgumentException("lhs and rhs don't go in the same slot"); 116 } 117 return lhsInfo.getSlotOrder() - rhsInfo.getSlotOrder(); 118 } 119 }; 120 } 121 122 /** 123 * Returns a {@link Comparator} capable of ordering {@link InCallButtonIds} by their conflict 124 * score. This comparator should be used when multiple InCallButtonIds could have been shown in 125 * the same slot. InCallButtonIds are sorted based on the natural ordering of {@link 126 * MappingInfo#getConflictOrder()}. 127 * 128 * <p>Note: the returned Comparator's compare method will throw an {@link 129 * IllegalArgumentException} if called with InCallButtonIds that have no configuration. 130 */ 131 @NonNull 132 public Comparator<Integer> getConflictComparator() { 133 return new Comparator<Integer>() { 134 @Override 135 public int compare(Integer lhs, Integer rhs) { 136 MappingInfo lhsInfo = lookupMappingInfo(lhs); 137 MappingInfo rhsInfo = lookupMappingInfo(rhs); 138 return lhsInfo.getConflictOrder() - rhsInfo.getConflictOrder(); 139 } 140 }; 141 } 142 143 @NonNull 144 private MappingInfo lookupMappingInfo(@InCallButtonIds int button) { 145 MappingInfo info = mapping.get(button); 146 if (info == null) { 147 throw new IllegalArgumentException( 148 "Unknown InCallButtonId: " + InCallButtonIdsExtension.toString(button)); 149 } 150 return info; 151 } 152 153 /** Holds information about button mapping. */ 154 @AutoValue 155 abstract static class MappingInfo { 156 157 /** The Ui slot into which a given button desires to be placed. */ 158 public abstract int getSlot(); 159 160 /** 161 * Returns an integer used to determine which button is chosen for a slot when multiple buttons 162 * desire to be placed in the same slot. Follows from the natural ordering of integers, i.e. a 163 * lower slotOrder results in the button being chosen. 164 */ 165 public abstract int getSlotOrder(); 166 167 /** 168 * Returns an integer used to determine the order in which buttons that weren't chosen for their 169 * desired slot are placed into the Ui. Follows from the natural ordering of integers, i.e. a 170 * lower conflictOrder results in the button being chosen. 171 */ 172 public abstract int getConflictOrder(); 173 174 static Builder builder(int slot) { 175 return new AutoValue_MappedButtonConfig_MappingInfo.Builder() 176 .setSlot(slot) 177 .setSlotOrder(Integer.MAX_VALUE) 178 .setConflictOrder(Integer.MAX_VALUE); 179 } 180 181 /** Class used to build instances of {@link MappingInfo}. */ 182 @AutoValue.Builder 183 abstract static class Builder { 184 public abstract Builder setSlot(int slot); 185 186 public abstract Builder setSlotOrder(int slotOrder); 187 188 public abstract Builder setConflictOrder(int conflictOrder); 189 190 public abstract MappingInfo build(); 191 } 192 } 193 } 194