1 /* 2 * Copyright (C) 2009 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.providers.contacts; 18 19 import android.net.Uri; 20 21 import java.util.ArrayList; 22 23 /** 24 * Contacts lookup key. Used for generation and parsing of contact lookup keys as well 25 * as doing the actual lookup. 26 */ 27 public class ContactLookupKey { 28 29 public static final int LOOKUP_TYPE_SOURCE_ID = 0; 30 public static final int LOOKUP_TYPE_DISPLAY_NAME = 1; 31 public static final int LOOKUP_TYPE_RAW_CONTACT_ID = 2; 32 33 public static class LookupKeySegment implements Comparable<LookupKeySegment> { 34 public int accountHashCode; 35 public int lookupType; 36 public String rawContactId; 37 public String key; 38 public long contactId; 39 compareTo(LookupKeySegment another)40 public int compareTo(LookupKeySegment another) { 41 if (contactId > another.contactId) { 42 return -1; 43 } 44 if (contactId < another.contactId) { 45 return 1; 46 } 47 return 0; 48 } 49 } 50 51 /** 52 * Returns a short hash code that functions as an additional precaution against the exceedingly 53 * improbable collision between sync IDs in different accounts. 54 */ getAccountHashCode(String accountType, String accountName)55 public static int getAccountHashCode(String accountType, String accountName) { 56 if (accountType == null || accountName == null) { 57 return 0; 58 } 59 60 return (accountType.hashCode() ^ accountName.hashCode()) & 0xFFF; 61 } 62 appendToLookupKey(StringBuilder lookupKey, String accountType, String accountName, long rawContactId, String sourceId, String displayName)63 public static void appendToLookupKey(StringBuilder lookupKey, String accountType, 64 String accountName, long rawContactId, String sourceId, String displayName) { 65 if (displayName == null) { 66 displayName = ""; 67 } 68 69 if (lookupKey.length() != 0) { 70 lookupKey.append("."); 71 } 72 73 lookupKey.append(getAccountHashCode(accountType, accountName)); 74 if (sourceId == null) { 75 lookupKey.append('r').append(rawContactId).append('-').append( 76 NameNormalizer.normalize(displayName)); 77 } else { 78 int pos = lookupKey.length(); 79 lookupKey.append('i'); 80 if (appendEscapedSourceId(lookupKey, sourceId)) { 81 lookupKey.setCharAt(pos, 'e'); 82 } 83 } 84 } 85 appendEscapedSourceId(StringBuilder sb, String sourceId)86 private static boolean appendEscapedSourceId(StringBuilder sb, String sourceId) { 87 boolean escaped = false; 88 int start = 0; 89 while (true) { 90 int index = sourceId.indexOf('.', start); 91 if (index == -1) { 92 sb.append(sourceId, start, sourceId.length()); 93 break; 94 } 95 96 escaped = true; 97 sb.append(sourceId, start, index); 98 sb.append(".."); 99 start = index + 1; 100 } 101 return escaped; 102 } 103 parse(String lookupKey)104 public ArrayList<LookupKeySegment> parse(String lookupKey) { 105 ArrayList<LookupKeySegment> list = new ArrayList<LookupKeySegment>(); 106 107 String string = Uri.decode(lookupKey); 108 int offset = 0; 109 int length = string.length(); 110 int hashCode = 0; 111 int lookupType = -1; 112 boolean escaped = false; 113 String rawContactId = null; 114 String key; 115 116 while (offset < length) { 117 char c = 0; 118 119 // Parse account hash code 120 hashCode = 0; 121 while (offset < length) { 122 c = string.charAt(offset++); 123 if (c < '0' || c > '9') { 124 break; 125 } 126 hashCode = hashCode * 10 + (c - '0'); 127 } 128 129 // Parse segment type 130 if (c == 'i') { 131 lookupType = LOOKUP_TYPE_SOURCE_ID; 132 escaped = false; 133 } else if (c == 'e') { 134 lookupType = LOOKUP_TYPE_SOURCE_ID; 135 escaped = true; 136 } else if (c == 'n') { 137 lookupType = LOOKUP_TYPE_DISPLAY_NAME; 138 } else if (c == 'r') { 139 lookupType = LOOKUP_TYPE_RAW_CONTACT_ID; 140 } else { 141 throw new IllegalArgumentException("Invalid lookup id: " + lookupKey); 142 } 143 144 // Parse the source ID or normalized display name 145 switch (lookupType) { 146 case LOOKUP_TYPE_SOURCE_ID: { 147 if (escaped) { 148 StringBuffer sb = new StringBuffer(); 149 while (offset < length) { 150 c = string.charAt(offset++); 151 152 if (c == '.') { 153 if (offset == length) { 154 throw new IllegalArgumentException("Invalid lookup id: " + 155 lookupKey); 156 } 157 c = string.charAt(offset); 158 159 if (c == '.') { 160 sb.append('.'); 161 offset++; 162 } else { 163 break; 164 } 165 } else { 166 sb.append(c); 167 } 168 } 169 key = sb.toString(); 170 } else { 171 int start = offset; 172 while (offset < length) { 173 c = string.charAt(offset++); 174 if (c == '.') { 175 break; 176 } 177 } 178 if (offset == length) { 179 key = string.substring(start); 180 } else { 181 key = string.substring(start, offset - 1); 182 } 183 } 184 break; 185 } 186 case LOOKUP_TYPE_DISPLAY_NAME: { 187 int start = offset; 188 while (offset < length) { 189 c = string.charAt(offset++); 190 if (c == '.') { 191 break; 192 } 193 } 194 if (offset == length) { 195 key = string.substring(start); 196 } else { 197 key = string.substring(start, offset - 1); 198 } 199 break; 200 } 201 case LOOKUP_TYPE_RAW_CONTACT_ID: { 202 int dash = -1; 203 int start = offset; 204 while (offset < length) { 205 c = string.charAt(offset); 206 if (c == '-' && dash == -1) { 207 dash = offset; 208 } 209 offset++; 210 if (c == '.') { 211 break; 212 } 213 } 214 if (dash != -1) { 215 rawContactId = string.substring(start, dash); 216 start = dash + 1; 217 } 218 if (offset == length) { 219 key = string.substring(start); 220 } else { 221 key = string.substring(start, offset - 1); 222 } 223 break; 224 } 225 default: 226 // Will never happen 227 throw new IllegalStateException(); 228 } 229 230 LookupKeySegment segment = new LookupKeySegment(); 231 segment.accountHashCode = hashCode; 232 segment.lookupType = lookupType; 233 segment.rawContactId = rawContactId; 234 segment.key = key; 235 segment.contactId = -1; 236 list.add(segment); 237 } 238 239 return list; 240 } 241 } 242