100113e88b9dbf52fdb82c592aff1c4695e907a19Dmitri Plotnikov/* 200113e88b9dbf52fdb82c592aff1c4695e907a19Dmitri Plotnikov * Copyright (C) 2010 The Android Open Source Project 300113e88b9dbf52fdb82c592aff1c4695e907a19Dmitri Plotnikov * 400113e88b9dbf52fdb82c592aff1c4695e907a19Dmitri Plotnikov * Licensed under the Apache License, Version 2.0 (the "License"); 500113e88b9dbf52fdb82c592aff1c4695e907a19Dmitri Plotnikov * you may not use this file except in compliance with the License. 600113e88b9dbf52fdb82c592aff1c4695e907a19Dmitri Plotnikov * You may obtain a copy of the License at 700113e88b9dbf52fdb82c592aff1c4695e907a19Dmitri Plotnikov * 800113e88b9dbf52fdb82c592aff1c4695e907a19Dmitri Plotnikov * http://www.apache.org/licenses/LICENSE-2.0 900113e88b9dbf52fdb82c592aff1c4695e907a19Dmitri Plotnikov * 1000113e88b9dbf52fdb82c592aff1c4695e907a19Dmitri Plotnikov * Unless required by applicable law or agreed to in writing, software 1100113e88b9dbf52fdb82c592aff1c4695e907a19Dmitri Plotnikov * distributed under the License is distributed on an "AS IS" BASIS, 1200113e88b9dbf52fdb82c592aff1c4695e907a19Dmitri Plotnikov * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 1300113e88b9dbf52fdb82c592aff1c4695e907a19Dmitri Plotnikov * See the License for the specific language governing permissions and 1400113e88b9dbf52fdb82c592aff1c4695e907a19Dmitri Plotnikov * limitations under the License. 1500113e88b9dbf52fdb82c592aff1c4695e907a19Dmitri Plotnikov */ 1600113e88b9dbf52fdb82c592aff1c4695e907a19Dmitri Plotnikov 172ed21125970d986906ad3e55f1ab6f4d5ace364aDmitri Plotnikovpackage com.android.contacts.list; 1800113e88b9dbf52fdb82c592aff1c4695e907a19Dmitri Plotnikov 1935d928af36e091d6ed8e8c1030453deec68f793aIsaac Katzenelsonimport android.text.TextUtils; 2000113e88b9dbf52fdb82c592aff1c4695e907a19Dmitri Plotnikovimport android.widget.SectionIndexer; 2100113e88b9dbf52fdb82c592aff1c4695e907a19Dmitri Plotnikov 2200113e88b9dbf52fdb82c592aff1c4695e907a19Dmitri Plotnikovimport java.util.Arrays; 2300113e88b9dbf52fdb82c592aff1c4695e907a19Dmitri Plotnikov 2400113e88b9dbf52fdb82c592aff1c4695e907a19Dmitri Plotnikov/** 2500113e88b9dbf52fdb82c592aff1c4695e907a19Dmitri Plotnikov * A section indexer that is configured with precomputed section titles and 2600113e88b9dbf52fdb82c592aff1c4695e907a19Dmitri Plotnikov * their respective counts. 2700113e88b9dbf52fdb82c592aff1c4695e907a19Dmitri Plotnikov */ 2800113e88b9dbf52fdb82c592aff1c4695e907a19Dmitri Plotnikovpublic class ContactsSectionIndexer implements SectionIndexer { 2900113e88b9dbf52fdb82c592aff1c4695e907a19Dmitri Plotnikov 3084cac440813d6c151bf330998451ca1e3f6804b1Dave Santoro private String[] mSections; 3184cac440813d6c151bf330998451ca1e3f6804b1Dave Santoro private int[] mPositions; 3284cac440813d6c151bf330998451ca1e3f6804b1Dave Santoro private int mCount; 3335d928af36e091d6ed8e8c1030453deec68f793aIsaac Katzenelson private static final String BLANK_HEADER_STRING = " "; 3400113e88b9dbf52fdb82c592aff1c4695e907a19Dmitri Plotnikov 3500113e88b9dbf52fdb82c592aff1c4695e907a19Dmitri Plotnikov /** 3600113e88b9dbf52fdb82c592aff1c4695e907a19Dmitri Plotnikov * Constructor. 3700113e88b9dbf52fdb82c592aff1c4695e907a19Dmitri Plotnikov * 3800113e88b9dbf52fdb82c592aff1c4695e907a19Dmitri Plotnikov * @param sections a non-null array 3900113e88b9dbf52fdb82c592aff1c4695e907a19Dmitri Plotnikov * @param counts a non-null array of the same size as <code>sections</code> 4000113e88b9dbf52fdb82c592aff1c4695e907a19Dmitri Plotnikov */ 4100113e88b9dbf52fdb82c592aff1c4695e907a19Dmitri Plotnikov public ContactsSectionIndexer(String[] sections, int[] counts) { 4200113e88b9dbf52fdb82c592aff1c4695e907a19Dmitri Plotnikov if (sections == null || counts == null) { 4300113e88b9dbf52fdb82c592aff1c4695e907a19Dmitri Plotnikov throw new NullPointerException(); 4400113e88b9dbf52fdb82c592aff1c4695e907a19Dmitri Plotnikov } 4500113e88b9dbf52fdb82c592aff1c4695e907a19Dmitri Plotnikov 4600113e88b9dbf52fdb82c592aff1c4695e907a19Dmitri Plotnikov if (sections.length != counts.length) { 4700113e88b9dbf52fdb82c592aff1c4695e907a19Dmitri Plotnikov throw new IllegalArgumentException( 4800113e88b9dbf52fdb82c592aff1c4695e907a19Dmitri Plotnikov "The sections and counts arrays must have the same length"); 4900113e88b9dbf52fdb82c592aff1c4695e907a19Dmitri Plotnikov } 5000113e88b9dbf52fdb82c592aff1c4695e907a19Dmitri Plotnikov 5100113e88b9dbf52fdb82c592aff1c4695e907a19Dmitri Plotnikov // TODO process sections/counts based on current locale and/or specific section titles 5200113e88b9dbf52fdb82c592aff1c4695e907a19Dmitri Plotnikov 5300113e88b9dbf52fdb82c592aff1c4695e907a19Dmitri Plotnikov this.mSections = sections; 5400113e88b9dbf52fdb82c592aff1c4695e907a19Dmitri Plotnikov mPositions = new int[counts.length]; 5500113e88b9dbf52fdb82c592aff1c4695e907a19Dmitri Plotnikov int position = 0; 5600113e88b9dbf52fdb82c592aff1c4695e907a19Dmitri Plotnikov for (int i = 0; i < counts.length; i++) { 5735d928af36e091d6ed8e8c1030453deec68f793aIsaac Katzenelson if (TextUtils.isEmpty(mSections[i])) { 5835d928af36e091d6ed8e8c1030453deec68f793aIsaac Katzenelson mSections[i] = BLANK_HEADER_STRING; 5935d928af36e091d6ed8e8c1030453deec68f793aIsaac Katzenelson } else if (!mSections[i].equals(BLANK_HEADER_STRING)) { 6000113e88b9dbf52fdb82c592aff1c4695e907a19Dmitri Plotnikov mSections[i] = mSections[i].trim(); 6100113e88b9dbf52fdb82c592aff1c4695e907a19Dmitri Plotnikov } 6200113e88b9dbf52fdb82c592aff1c4695e907a19Dmitri Plotnikov 6300113e88b9dbf52fdb82c592aff1c4695e907a19Dmitri Plotnikov mPositions[i] = position; 6400113e88b9dbf52fdb82c592aff1c4695e907a19Dmitri Plotnikov position += counts[i]; 6500113e88b9dbf52fdb82c592aff1c4695e907a19Dmitri Plotnikov } 6600113e88b9dbf52fdb82c592aff1c4695e907a19Dmitri Plotnikov mCount = position; 6700113e88b9dbf52fdb82c592aff1c4695e907a19Dmitri Plotnikov } 6800113e88b9dbf52fdb82c592aff1c4695e907a19Dmitri Plotnikov 6900113e88b9dbf52fdb82c592aff1c4695e907a19Dmitri Plotnikov public Object[] getSections() { 7000113e88b9dbf52fdb82c592aff1c4695e907a19Dmitri Plotnikov return mSections; 7100113e88b9dbf52fdb82c592aff1c4695e907a19Dmitri Plotnikov } 7200113e88b9dbf52fdb82c592aff1c4695e907a19Dmitri Plotnikov 7300113e88b9dbf52fdb82c592aff1c4695e907a19Dmitri Plotnikov public int getPositionForSection(int section) { 7400113e88b9dbf52fdb82c592aff1c4695e907a19Dmitri Plotnikov if (section < 0 || section >= mSections.length) { 7500113e88b9dbf52fdb82c592aff1c4695e907a19Dmitri Plotnikov return -1; 7600113e88b9dbf52fdb82c592aff1c4695e907a19Dmitri Plotnikov } 7700113e88b9dbf52fdb82c592aff1c4695e907a19Dmitri Plotnikov 7800113e88b9dbf52fdb82c592aff1c4695e907a19Dmitri Plotnikov return mPositions[section]; 7900113e88b9dbf52fdb82c592aff1c4695e907a19Dmitri Plotnikov } 8000113e88b9dbf52fdb82c592aff1c4695e907a19Dmitri Plotnikov 8100113e88b9dbf52fdb82c592aff1c4695e907a19Dmitri Plotnikov public int getSectionForPosition(int position) { 8200113e88b9dbf52fdb82c592aff1c4695e907a19Dmitri Plotnikov if (position < 0 || position >= mCount) { 8300113e88b9dbf52fdb82c592aff1c4695e907a19Dmitri Plotnikov return -1; 8400113e88b9dbf52fdb82c592aff1c4695e907a19Dmitri Plotnikov } 8500113e88b9dbf52fdb82c592aff1c4695e907a19Dmitri Plotnikov 8600113e88b9dbf52fdb82c592aff1c4695e907a19Dmitri Plotnikov int index = Arrays.binarySearch(mPositions, position); 8700113e88b9dbf52fdb82c592aff1c4695e907a19Dmitri Plotnikov 8800113e88b9dbf52fdb82c592aff1c4695e907a19Dmitri Plotnikov /* 8900113e88b9dbf52fdb82c592aff1c4695e907a19Dmitri Plotnikov * Consider this example: section positions are 0, 3, 5; the supplied 9000113e88b9dbf52fdb82c592aff1c4695e907a19Dmitri Plotnikov * position is 4. The section corresponding to position 4 starts at 9100113e88b9dbf52fdb82c592aff1c4695e907a19Dmitri Plotnikov * position 3, so the expected return value is 1. Binary search will not 9200113e88b9dbf52fdb82c592aff1c4695e907a19Dmitri Plotnikov * find 4 in the array and thus will return -insertPosition-1, i.e. -3. 9300113e88b9dbf52fdb82c592aff1c4695e907a19Dmitri Plotnikov * To get from that number to the expected value of 1 we need to negate 9400113e88b9dbf52fdb82c592aff1c4695e907a19Dmitri Plotnikov * and subtract 2. 9500113e88b9dbf52fdb82c592aff1c4695e907a19Dmitri Plotnikov */ 9600113e88b9dbf52fdb82c592aff1c4695e907a19Dmitri Plotnikov return index >= 0 ? index : -index - 2; 9700113e88b9dbf52fdb82c592aff1c4695e907a19Dmitri Plotnikov } 98ead19c5eafee0ffb43b02a4ae75ac5244ad3f853Isaac Katzenelson 99ead19c5eafee0ffb43b02a4ae75ac5244ad3f853Isaac Katzenelson public void setProfileHeader(String header) { 10084cac440813d6c151bf330998451ca1e3f6804b1Dave Santoro if (mSections != null) { 10184cac440813d6c151bf330998451ca1e3f6804b1Dave Santoro // Don't do anything if the header is already set properly. 10284cac440813d6c151bf330998451ca1e3f6804b1Dave Santoro if (mSections.length > 0 && header.equals(mSections[0])) { 10384cac440813d6c151bf330998451ca1e3f6804b1Dave Santoro return; 10484cac440813d6c151bf330998451ca1e3f6804b1Dave Santoro } 10584cac440813d6c151bf330998451ca1e3f6804b1Dave Santoro 10684cac440813d6c151bf330998451ca1e3f6804b1Dave Santoro // Since the section indexer isn't aware of the profile at the top, we need to add a 10784cac440813d6c151bf330998451ca1e3f6804b1Dave Santoro // special section at the top for it and shift everything else down. 10884cac440813d6c151bf330998451ca1e3f6804b1Dave Santoro String[] tempSections = new String[mSections.length + 1]; 10984cac440813d6c151bf330998451ca1e3f6804b1Dave Santoro int[] tempPositions = new int[mPositions.length + 1]; 11084cac440813d6c151bf330998451ca1e3f6804b1Dave Santoro tempSections[0] = header; 11184cac440813d6c151bf330998451ca1e3f6804b1Dave Santoro tempPositions[0] = 0; 11284cac440813d6c151bf330998451ca1e3f6804b1Dave Santoro for (int i = 1; i <= mPositions.length; i++) { 11384cac440813d6c151bf330998451ca1e3f6804b1Dave Santoro tempSections[i] = mSections[i - 1]; 11484cac440813d6c151bf330998451ca1e3f6804b1Dave Santoro tempPositions[i] = mPositions[i - 1] + 1; 11584cac440813d6c151bf330998451ca1e3f6804b1Dave Santoro } 11684cac440813d6c151bf330998451ca1e3f6804b1Dave Santoro mSections = tempSections; 11784cac440813d6c151bf330998451ca1e3f6804b1Dave Santoro mPositions = tempPositions; 11884cac440813d6c151bf330998451ca1e3f6804b1Dave Santoro mCount++; 119ead19c5eafee0ffb43b02a4ae75ac5244ad3f853Isaac Katzenelson } 120ead19c5eafee0ffb43b02a4ae75ac5244ad3f853Isaac Katzenelson } 12100113e88b9dbf52fdb82c592aff1c4695e907a19Dmitri Plotnikov} 122