1d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu/* 2d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu * Copyright (C) 2013 The Android Open Source Project 3d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu * 4d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu * Licensed under the Apache License, Version 2.0 (the "License"); 5d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu * you may not use this file except in compliance with the License. 6d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu * You may obtain a copy of the License at 7d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu * 8d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu * http://www.apache.org/licenses/LICENSE-2.0 9d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu * 10d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu * Unless required by applicable law or agreed to in writing, software 11d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu * distributed under the License is distributed on an "AS IS" BASIS, 12d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu * See the License for the specific language governing permissions and 14d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu * limitations under the License. 15d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu */ 16d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu 17d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescupackage com.android.gallery3d.ingest; 18d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu 19d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescuimport android.mtp.MtpConstants; 20d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescuimport android.mtp.MtpDevice; 21d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescuimport android.mtp.MtpObjectInfo; 22d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu 23d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescuimport java.util.ArrayList; 24d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescuimport java.util.Arrays; 25d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescuimport java.util.Collection; 26d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescuimport java.util.Collections; 27d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescuimport java.util.Comparator; 28d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescuimport java.util.HashMap; 29baa68886db66f4beb6a07e4441b122975b778f1cBobby Georgescuimport java.util.HashSet; 30d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescuimport java.util.List; 31d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescuimport java.util.Map; 32baa68886db66f4beb6a07e4441b122975b778f1cBobby Georgescuimport java.util.Set; 33d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescuimport java.util.Stack; 34d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu 35d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu/** 36d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu * MTP objects in the index are organized into "buckets," or groupings. 37d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu * At present, these buckets are based on the date an item was created. 38d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu * 39d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu * When the index is created, the buckets are sorted in their natural 40d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu * order, and the items within the buckets sorted by the date they are taken. 41d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu * 42d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu * The index enables the access of items and bucket labels as one unified list. 43d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu * For example, let's say we have the following data in the index: 44d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu * [Bucket A]: [photo 1], [photo 2] 45d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu * [Bucket B]: [photo 3] 46d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu * 47d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu * Then the items can be thought of as being organized as a 5 element list: 48d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu * [Bucket A], [photo 1], [photo 2], [Bucket B], [photo 3] 49d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu * 50d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu * The data can also be accessed in descending order, in which case the list 51d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu * would be a bit different from simply reversing the ascending list, since the 52d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu * bucket labels need to always be at the beginning: 53d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu * [Bucket B], [photo 3], [Bucket A], [photo 2], [photo 1] 54d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu * 55d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu * The index enables all the following operations in constant time, both for 56d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu * ascending and descending views of the data: 57d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu * - get/getAscending/getDescending: get an item at a specified list position 58d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu * - size: get the total number of items (bucket labels and MTP objects) 59d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu * - getFirstPositionForBucketNumber 60d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu * - getBucketNumberForPosition 61d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu * - isFirstInBucket 62d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu * 63d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu * See the comments in buildLookupIndex for implementation notes. 64d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu */ 65d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescupublic class MtpDeviceIndex { 66d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu 67baa68886db66f4beb6a07e4441b122975b778f1cBobby Georgescu public static final int FORMAT_MOV = 0x300D; // For some reason this is not in MtpConstants 68baa68886db66f4beb6a07e4441b122975b778f1cBobby Georgescu 69baa68886db66f4beb6a07e4441b122975b778f1cBobby Georgescu public static final Set<Integer> SUPPORTED_IMAGE_FORMATS; 70baa68886db66f4beb6a07e4441b122975b778f1cBobby Georgescu public static final Set<Integer> SUPPORTED_VIDEO_FORMATS; 71baa68886db66f4beb6a07e4441b122975b778f1cBobby Georgescu 72baa68886db66f4beb6a07e4441b122975b778f1cBobby Georgescu static { 73baa68886db66f4beb6a07e4441b122975b778f1cBobby Georgescu SUPPORTED_IMAGE_FORMATS = new HashSet<Integer>(); 74baa68886db66f4beb6a07e4441b122975b778f1cBobby Georgescu SUPPORTED_IMAGE_FORMATS.add(MtpConstants.FORMAT_JFIF); 75baa68886db66f4beb6a07e4441b122975b778f1cBobby Georgescu SUPPORTED_IMAGE_FORMATS.add(MtpConstants.FORMAT_EXIF_JPEG); 76baa68886db66f4beb6a07e4441b122975b778f1cBobby Georgescu SUPPORTED_IMAGE_FORMATS.add(MtpConstants.FORMAT_PNG); 77baa68886db66f4beb6a07e4441b122975b778f1cBobby Georgescu SUPPORTED_IMAGE_FORMATS.add(MtpConstants.FORMAT_GIF); 78baa68886db66f4beb6a07e4441b122975b778f1cBobby Georgescu SUPPORTED_IMAGE_FORMATS.add(MtpConstants.FORMAT_BMP); 79baa68886db66f4beb6a07e4441b122975b778f1cBobby Georgescu 80baa68886db66f4beb6a07e4441b122975b778f1cBobby Georgescu SUPPORTED_VIDEO_FORMATS = new HashSet<Integer>(); 81baa68886db66f4beb6a07e4441b122975b778f1cBobby Georgescu SUPPORTED_VIDEO_FORMATS.add(MtpConstants.FORMAT_3GP_CONTAINER); 82baa68886db66f4beb6a07e4441b122975b778f1cBobby Georgescu SUPPORTED_VIDEO_FORMATS.add(MtpConstants.FORMAT_AVI); 83baa68886db66f4beb6a07e4441b122975b778f1cBobby Georgescu SUPPORTED_VIDEO_FORMATS.add(MtpConstants.FORMAT_MP4_CONTAINER); 84baa68886db66f4beb6a07e4441b122975b778f1cBobby Georgescu SUPPORTED_VIDEO_FORMATS.add(MtpConstants.FORMAT_MPEG); 85baa68886db66f4beb6a07e4441b122975b778f1cBobby Georgescu // TODO: add FORMAT_MOV once Media Scanner supports .mov files 86baa68886db66f4beb6a07e4441b122975b778f1cBobby Georgescu } 87baa68886db66f4beb6a07e4441b122975b778f1cBobby Georgescu 88d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu @Override 89d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu public int hashCode() { 90d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu final int prime = 31; 91d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu int result = 1; 92d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu result = prime * result + ((mDevice == null) ? 0 : mDevice.getDeviceId()); 93d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu result = prime * result + mGeneration; 94d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu return result; 95d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu } 96d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu 97d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu public interface ProgressListener { 98d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu public void onObjectIndexed(MtpObjectInfo object, int numVisited); 99d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu 100d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu public void onSorting(); 101d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu 102d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu public void onIndexFinish(); 103d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu } 104d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu 105d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu public enum SortOrder { 106d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu Ascending, Descending 107d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu } 108d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu 109d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu private MtpDevice mDevice; 110d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu private int[] mUnifiedLookupIndex; 111d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu private MtpObjectInfo[] mMtpObjects; 112d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu private DateBucket[] mBuckets; 113d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu private int mGeneration = 0; 114d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu 115d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu public enum Progress { 116d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu Uninitialized, Initialized, Pending, Started, Sorting, Finished 117d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu } 118d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu 119d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu private Progress mProgress = Progress.Uninitialized; 120d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu private ProgressListener mProgressListener; 121d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu 122d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu private static final MtpDeviceIndex sInstance = new MtpDeviceIndex(); 123eebf9e81673150a6c4fec89b64e3cdb595377c83Bobby Georgescu private static final MtpObjectTimestampComparator sMtpObjectComparator = 124eebf9e81673150a6c4fec89b64e3cdb595377c83Bobby Georgescu new MtpObjectTimestampComparator(); 125d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu 126d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu public static MtpDeviceIndex getInstance() { 127d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu return sInstance; 128d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu } 129d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu 130d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu private MtpDeviceIndex() { 131d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu } 132d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu 133d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu synchronized public MtpDevice getDevice() { 134d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu return mDevice; 135d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu } 136d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu 137d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu /** 138d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu * Sets the MtpDevice that should be indexed and initializes state, but does 139d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu * not kick off the actual indexing task, which is instead done by using 140d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu * {@link #getIndexRunnable()} 141d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu * 142d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu * @param device The MtpDevice that should be indexed 143d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu */ 144d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu synchronized public void setDevice(MtpDevice device) { 145d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu if (device == mDevice) return; 146d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu mDevice = device; 147d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu resetState(); 148d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu } 149d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu 150d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu /** 151d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu * Provides a Runnable for the indexing task assuming the state has already 152d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu * been correctly initialized (by calling {@link #setDevice(MtpDevice)}) and 153d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu * has not already been run. 154d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu * 155d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu * @return Runnable for the main indexing task 156d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu */ 157d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu synchronized public Runnable getIndexRunnable() { 158d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu if (mProgress != Progress.Initialized) return null; 159d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu mProgress = Progress.Pending; 160eebf9e81673150a6c4fec89b64e3cdb595377c83Bobby Georgescu return new IndexRunnable(mDevice); 161d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu } 162d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu 1636516d271e11f9ae9928be3bb70c99531a842c577Bobby Georgescu synchronized public boolean indexReady() { 1646516d271e11f9ae9928be3bb70c99531a842c577Bobby Georgescu return mProgress == Progress.Finished; 1656516d271e11f9ae9928be3bb70c99531a842c577Bobby Georgescu } 1666516d271e11f9ae9928be3bb70c99531a842c577Bobby Georgescu 167d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu synchronized public Progress getProgress() { 168d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu return mProgress; 169d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu } 170d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu 171d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu /** 172d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu * @param listener Listener to change to 173d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu * @return Progress at the time the listener was added (useful for 174d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu * configuring initial UI state) 175d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu */ 176d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu synchronized public Progress setProgressListener(ProgressListener listener) { 177d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu mProgressListener = listener; 178d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu return mProgress; 179d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu } 180d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu 181d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu /** 182d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu * Make the listener null if it matches the argument 183d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu * 184d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu * @param listener Listener to unset, if currently registered 185d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu */ 186d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu synchronized public void unsetProgressListener(ProgressListener listener) { 187d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu if (mProgressListener == listener) 188d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu mProgressListener = null; 189d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu } 190d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu 191d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu /** 192d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu * @return The total number of elements in the index (labels and items) 193d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu */ 194d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu public int size() { 195d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu return mProgress == Progress.Finished ? mUnifiedLookupIndex.length : 0; 196d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu } 197d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu 198d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu /** 199d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu * @param position Index of item to fetch, where 0 is the first item in the 200d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu * specified order 201d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu * @param order 202d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu * @return the bucket label or MtpObjectInfo at the specified position and 203d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu * order 204d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu */ 205d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu public Object get(int position, SortOrder order) { 206c8a9e86919dca8938948f97efc3dcbe143e806bfBobby Georgescu if (mProgress != Progress.Finished) return null; 207d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu if(order == SortOrder.Ascending) { 208c8a9e86919dca8938948f97efc3dcbe143e806bfBobby Georgescu DateBucket bucket = mBuckets[mUnifiedLookupIndex[position]]; 209c8a9e86919dca8938948f97efc3dcbe143e806bfBobby Georgescu if (bucket.unifiedStartIndex == position) { 210c8a9e86919dca8938948f97efc3dcbe143e806bfBobby Georgescu return bucket.bucket; 211c8a9e86919dca8938948f97efc3dcbe143e806bfBobby Georgescu } else { 212c8a9e86919dca8938948f97efc3dcbe143e806bfBobby Georgescu return mMtpObjects[bucket.itemsStartIndex + position - 1 213c8a9e86919dca8938948f97efc3dcbe143e806bfBobby Georgescu - bucket.unifiedStartIndex]; 214c8a9e86919dca8938948f97efc3dcbe143e806bfBobby Georgescu } 215d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu } else { 216c8a9e86919dca8938948f97efc3dcbe143e806bfBobby Georgescu int zeroIndex = mUnifiedLookupIndex.length - 1 - position; 217c8a9e86919dca8938948f97efc3dcbe143e806bfBobby Georgescu DateBucket bucket = mBuckets[mUnifiedLookupIndex[zeroIndex]]; 218c8a9e86919dca8938948f97efc3dcbe143e806bfBobby Georgescu if (bucket.unifiedEndIndex == zeroIndex) { 219c8a9e86919dca8938948f97efc3dcbe143e806bfBobby Georgescu return bucket.bucket; 220c8a9e86919dca8938948f97efc3dcbe143e806bfBobby Georgescu } else { 221c8a9e86919dca8938948f97efc3dcbe143e806bfBobby Georgescu return mMtpObjects[bucket.itemsStartIndex + zeroIndex 222c8a9e86919dca8938948f97efc3dcbe143e806bfBobby Georgescu - bucket.unifiedStartIndex]; 223c8a9e86919dca8938948f97efc3dcbe143e806bfBobby Georgescu } 224d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu } 225d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu } 226d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu 227d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu /** 228c8a9e86919dca8938948f97efc3dcbe143e806bfBobby Georgescu * @param position Index of item to fetch from a view of the data that doesn't 229c8a9e86919dca8938948f97efc3dcbe143e806bfBobby Georgescu * include labels and is in the specified order 230c8a9e86919dca8938948f97efc3dcbe143e806bfBobby Georgescu * @return position-th item in specified order, when not including labels 231d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu */ 232c8a9e86919dca8938948f97efc3dcbe143e806bfBobby Georgescu public MtpObjectInfo getWithoutLabels(int position, SortOrder order) { 233d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu if (mProgress != Progress.Finished) return null; 234c8a9e86919dca8938948f97efc3dcbe143e806bfBobby Georgescu if (order == SortOrder.Ascending) { 235c8a9e86919dca8938948f97efc3dcbe143e806bfBobby Georgescu return mMtpObjects[position]; 236d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu } else { 237c8a9e86919dca8938948f97efc3dcbe143e806bfBobby Georgescu return mMtpObjects[mMtpObjects.length - 1 - position]; 238d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu } 239d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu } 240d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu 241d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu /** 242c8a9e86919dca8938948f97efc3dcbe143e806bfBobby Georgescu * Although this is O(log(number of buckets)), and thus should not be used 243c8a9e86919dca8938948f97efc3dcbe143e806bfBobby Georgescu * in hotspots, even if the attached device has items for every day for 244c8a9e86919dca8938948f97efc3dcbe143e806bfBobby Georgescu * a five-year timeframe, it would still only take 11 iterations at most, 245c8a9e86919dca8938948f97efc3dcbe143e806bfBobby Georgescu * so shouldn't be a huge issue. 246c8a9e86919dca8938948f97efc3dcbe143e806bfBobby Georgescu * @param position Index of item to map from a view of the data that doesn't 247c8a9e86919dca8938948f97efc3dcbe143e806bfBobby Georgescu * include labels and is in the specified order 248c8a9e86919dca8938948f97efc3dcbe143e806bfBobby Georgescu * @param order 249c8a9e86919dca8938948f97efc3dcbe143e806bfBobby Georgescu * @return position in a view of the data that does include labels 250d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu */ 251c8a9e86919dca8938948f97efc3dcbe143e806bfBobby Georgescu public int getPositionFromPositionWithoutLabels(int position, SortOrder order) { 252c8a9e86919dca8938948f97efc3dcbe143e806bfBobby Georgescu if (mProgress != Progress.Finished) return -1; 253c8a9e86919dca8938948f97efc3dcbe143e806bfBobby Georgescu if (order == SortOrder.Descending) { 254c8a9e86919dca8938948f97efc3dcbe143e806bfBobby Georgescu position = mMtpObjects.length - 1 - position; 255c8a9e86919dca8938948f97efc3dcbe143e806bfBobby Georgescu } 256c8a9e86919dca8938948f97efc3dcbe143e806bfBobby Georgescu int bucketNumber = 0; 257c8a9e86919dca8938948f97efc3dcbe143e806bfBobby Georgescu int iMin = 0; 258c8a9e86919dca8938948f97efc3dcbe143e806bfBobby Georgescu int iMax = mBuckets.length - 1; 259c8a9e86919dca8938948f97efc3dcbe143e806bfBobby Georgescu while (iMax >= iMin) { 260c8a9e86919dca8938948f97efc3dcbe143e806bfBobby Georgescu int iMid = (iMax + iMin) / 2; 261c8a9e86919dca8938948f97efc3dcbe143e806bfBobby Georgescu if (mBuckets[iMid].itemsStartIndex + mBuckets[iMid].numItems <= position) { 262c8a9e86919dca8938948f97efc3dcbe143e806bfBobby Georgescu iMin = iMid + 1; 263c8a9e86919dca8938948f97efc3dcbe143e806bfBobby Georgescu } else if (mBuckets[iMid].itemsStartIndex > position) { 264c8a9e86919dca8938948f97efc3dcbe143e806bfBobby Georgescu iMax = iMid - 1; 265c8a9e86919dca8938948f97efc3dcbe143e806bfBobby Georgescu } else { 266c8a9e86919dca8938948f97efc3dcbe143e806bfBobby Georgescu bucketNumber = iMid; 267c8a9e86919dca8938948f97efc3dcbe143e806bfBobby Georgescu break; 268c8a9e86919dca8938948f97efc3dcbe143e806bfBobby Georgescu } 269d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu } 27096e6ddb24dd149aa991ef007a394ea9592066d14John Hoford if (mBuckets.length == 0 || mUnifiedLookupIndex.length == 0) { 27196e6ddb24dd149aa991ef007a394ea9592066d14John Hoford return -1; 27296e6ddb24dd149aa991ef007a394ea9592066d14John Hoford } 273c8a9e86919dca8938948f97efc3dcbe143e806bfBobby Georgescu int mappedPos = mBuckets[bucketNumber].unifiedStartIndex 274c8a9e86919dca8938948f97efc3dcbe143e806bfBobby Georgescu + position - mBuckets[bucketNumber].itemsStartIndex; 275c8a9e86919dca8938948f97efc3dcbe143e806bfBobby Georgescu if (order == SortOrder.Descending) { 276c8a9e86919dca8938948f97efc3dcbe143e806bfBobby Georgescu mappedPos = mUnifiedLookupIndex.length - 1 - mappedPos; 277c8a9e86919dca8938948f97efc3dcbe143e806bfBobby Georgescu } 278c8a9e86919dca8938948f97efc3dcbe143e806bfBobby Georgescu return mappedPos; 279d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu } 280d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu 281c8a9e86919dca8938948f97efc3dcbe143e806bfBobby Georgescu public int getPositionWithoutLabelsFromPosition(int position, SortOrder order) { 282c8a9e86919dca8938948f97efc3dcbe143e806bfBobby Georgescu if (mProgress != Progress.Finished) return -1; 283c8a9e86919dca8938948f97efc3dcbe143e806bfBobby Georgescu if(order == SortOrder.Ascending) { 284c8a9e86919dca8938948f97efc3dcbe143e806bfBobby Georgescu DateBucket bucket = mBuckets[mUnifiedLookupIndex[position]]; 285c8a9e86919dca8938948f97efc3dcbe143e806bfBobby Georgescu if (bucket.unifiedStartIndex == position) position++; 286c8a9e86919dca8938948f97efc3dcbe143e806bfBobby Georgescu return bucket.itemsStartIndex + position - 1 - bucket.unifiedStartIndex; 287d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu } else { 288c8a9e86919dca8938948f97efc3dcbe143e806bfBobby Georgescu int zeroIndex = mUnifiedLookupIndex.length - 1 - position; 28996e6ddb24dd149aa991ef007a394ea9592066d14John Hoford if (mBuckets.length == 0 || mUnifiedLookupIndex.length == 0) { 29096e6ddb24dd149aa991ef007a394ea9592066d14John Hoford return -1; 29196e6ddb24dd149aa991ef007a394ea9592066d14John Hoford } 292c8a9e86919dca8938948f97efc3dcbe143e806bfBobby Georgescu DateBucket bucket = mBuckets[mUnifiedLookupIndex[zeroIndex]]; 293c8a9e86919dca8938948f97efc3dcbe143e806bfBobby Georgescu if (bucket.unifiedEndIndex == zeroIndex) zeroIndex--; 294c8a9e86919dca8938948f97efc3dcbe143e806bfBobby Georgescu return mMtpObjects.length - 1 - bucket.itemsStartIndex 295c8a9e86919dca8938948f97efc3dcbe143e806bfBobby Georgescu - zeroIndex + bucket.unifiedStartIndex; 296d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu } 297d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu } 298d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu 299d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu /** 300d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu * @return The number of MTP items in the index (without labels) 301d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu */ 302d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu public int sizeWithoutLabels() { 303d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu return mProgress == Progress.Finished ? mMtpObjects.length : 0; 304d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu } 305d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu 306d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu public int getFirstPositionForBucketNumber(int bucketNumber, SortOrder order) { 307d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu if (order == SortOrder.Ascending) { 308d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu return mBuckets[bucketNumber].unifiedStartIndex; 309d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu } else { 310d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu return mUnifiedLookupIndex.length - mBuckets[mBuckets.length - 1 - bucketNumber].unifiedEndIndex - 1; 311d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu } 312d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu } 313d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu 314d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu public int getBucketNumberForPosition(int position, SortOrder order) { 315d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu if (order == SortOrder.Ascending) { 316d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu return mUnifiedLookupIndex[position]; 317d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu } else { 318d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu return mBuckets.length - 1 - mUnifiedLookupIndex[mUnifiedLookupIndex.length - 1 - position]; 319d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu } 320d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu } 321d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu 322d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu public boolean isFirstInBucket(int position, SortOrder order) { 323d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu if (order == SortOrder.Ascending) { 324d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu return mBuckets[mUnifiedLookupIndex[position]].unifiedStartIndex == position; 325d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu } else { 326d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu position = mUnifiedLookupIndex.length - 1 - position; 327d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu return mBuckets[mUnifiedLookupIndex[position]].unifiedEndIndex == position; 328d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu } 329d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu } 330d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu 331d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu private Object[] mCachedReverseBuckets; 332d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu 333d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu public Object[] getBuckets(SortOrder order) { 334d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu if (mBuckets == null) return null; 335d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu if (order == SortOrder.Ascending) { 336d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu return mBuckets; 337d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu } else { 338d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu if (mCachedReverseBuckets == null) { 339d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu computeReversedBuckets(); 340d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu } 341d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu return mCachedReverseBuckets; 342d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu } 343d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu } 344d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu 345d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu /* 346d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu * See the comments for buildLookupIndex for notes on the specific fields of 347d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu * this class. 348d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu */ 349d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu private class DateBucket implements Comparable<DateBucket> { 350d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu SimpleDate bucket; 351d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu List<MtpObjectInfo> tempElementsList = new ArrayList<MtpObjectInfo>(); 352d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu int unifiedStartIndex; 353d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu int unifiedEndIndex; 354d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu int itemsStartIndex; 355c8a9e86919dca8938948f97efc3dcbe143e806bfBobby Georgescu int numItems; 356d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu 357d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu public DateBucket(SimpleDate bucket) { 358d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu this.bucket = bucket; 359d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu } 360d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu 361d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu public DateBucket(SimpleDate bucket, MtpObjectInfo firstElement) { 362d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu this(bucket); 363d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu tempElementsList.add(firstElement); 364d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu } 365d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu 366d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu void sortElements(Comparator<MtpObjectInfo> comparator) { 367d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu Collections.sort(tempElementsList, comparator); 368d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu } 369d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu 370d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu @Override 371d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu public String toString() { 372d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu return bucket.toString(); 373d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu } 374d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu 375d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu @Override 376d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu public int hashCode() { 377d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu return bucket.hashCode(); 378d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu } 379d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu 380d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu @Override 381d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu public boolean equals(Object obj) { 382d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu if (this == obj) return true; 383d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu if (obj == null) return false; 384d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu if (!(obj instanceof DateBucket)) return false; 385d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu DateBucket other = (DateBucket) obj; 386d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu if (bucket == null) { 387d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu if (other.bucket != null) return false; 388d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu } else if (!bucket.equals(other.bucket)) { 389d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu return false; 390d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu } 391d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu return true; 392d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu } 393d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu 394d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu @Override 395d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu public int compareTo(DateBucket another) { 396d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu return this.bucket.compareTo(another.bucket); 397d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu } 398d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu } 399d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu 400d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu /** 401d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu * Comparator to sort MtpObjectInfo objects by date created. 402d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu */ 403d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu private static class MtpObjectTimestampComparator implements Comparator<MtpObjectInfo> { 404d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu @Override 405d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu public int compare(MtpObjectInfo o1, MtpObjectInfo o2) { 406d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu long diff = o1.getDateCreated() - o2.getDateCreated(); 407d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu if (diff < 0) { 408d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu return -1; 409d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu } else if (diff == 0) { 410d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu return 0; 411d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu } else { 412d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu return 1; 413d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu } 414d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu } 415d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu } 416d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu 417d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu private void resetState() { 418d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu mGeneration++; 419d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu mUnifiedLookupIndex = null; 420d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu mMtpObjects = null; 421d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu mBuckets = null; 422d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu mCachedReverseBuckets = null; 423d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu mProgress = (mDevice == null) ? Progress.Uninitialized : Progress.Initialized; 424d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu } 425d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu 426eebf9e81673150a6c4fec89b64e3cdb595377c83Bobby Georgescu 427eebf9e81673150a6c4fec89b64e3cdb595377c83Bobby Georgescu private class IndexRunnable implements Runnable { 428eebf9e81673150a6c4fec89b64e3cdb595377c83Bobby Georgescu private int[] mUnifiedLookupIndex; 429eebf9e81673150a6c4fec89b64e3cdb595377c83Bobby Georgescu private MtpObjectInfo[] mMtpObjects; 430eebf9e81673150a6c4fec89b64e3cdb595377c83Bobby Georgescu private DateBucket[] mBuckets; 431eebf9e81673150a6c4fec89b64e3cdb595377c83Bobby Georgescu private Map<SimpleDate, DateBucket> mBucketsTemp; 432eebf9e81673150a6c4fec89b64e3cdb595377c83Bobby Georgescu private MtpDevice mDevice; 433eebf9e81673150a6c4fec89b64e3cdb595377c83Bobby Georgescu private int mNumObjects = 0; 434eebf9e81673150a6c4fec89b64e3cdb595377c83Bobby Georgescu 435eebf9e81673150a6c4fec89b64e3cdb595377c83Bobby Georgescu private class IndexingException extends Exception {}; 436eebf9e81673150a6c4fec89b64e3cdb595377c83Bobby Georgescu 437eebf9e81673150a6c4fec89b64e3cdb595377c83Bobby Georgescu public IndexRunnable(MtpDevice device) { 438eebf9e81673150a6c4fec89b64e3cdb595377c83Bobby Georgescu mDevice = device; 439eebf9e81673150a6c4fec89b64e3cdb595377c83Bobby Georgescu } 440eebf9e81673150a6c4fec89b64e3cdb595377c83Bobby Georgescu 441eebf9e81673150a6c4fec89b64e3cdb595377c83Bobby Georgescu /* 442eebf9e81673150a6c4fec89b64e3cdb595377c83Bobby Georgescu * Implementation note: this is the way the index supports a lot of its operations in 443eebf9e81673150a6c4fec89b64e3cdb595377c83Bobby Georgescu * constant time and respecting the need to have bucket names always come before items 444eebf9e81673150a6c4fec89b64e3cdb595377c83Bobby Georgescu * in that bucket when accessing the list sequentially, both in ascending and descending 445eebf9e81673150a6c4fec89b64e3cdb595377c83Bobby Georgescu * orders. 446eebf9e81673150a6c4fec89b64e3cdb595377c83Bobby Georgescu * 447eebf9e81673150a6c4fec89b64e3cdb595377c83Bobby Georgescu * Let's say the data we have in the index is the following: 448eebf9e81673150a6c4fec89b64e3cdb595377c83Bobby Georgescu * [Bucket A]: [photo 1], [photo 2] 449eebf9e81673150a6c4fec89b64e3cdb595377c83Bobby Georgescu * [Bucket B]: [photo 3] 450eebf9e81673150a6c4fec89b64e3cdb595377c83Bobby Georgescu * 451eebf9e81673150a6c4fec89b64e3cdb595377c83Bobby Georgescu * In this case, the lookup index array would be 452eebf9e81673150a6c4fec89b64e3cdb595377c83Bobby Georgescu * [0, 0, 0, 1, 1] 453eebf9e81673150a6c4fec89b64e3cdb595377c83Bobby Georgescu * 454eebf9e81673150a6c4fec89b64e3cdb595377c83Bobby Georgescu * Now, whether we access the list in ascending or descending order, we know which bucket 455eebf9e81673150a6c4fec89b64e3cdb595377c83Bobby Georgescu * to look in (0 corresponds to A and 1 to B), and can return the bucket label as the first 456eebf9e81673150a6c4fec89b64e3cdb595377c83Bobby Georgescu * item in a bucket as needed. The individual IndexBUckets have a startIndex and endIndex 457eebf9e81673150a6c4fec89b64e3cdb595377c83Bobby Georgescu * that correspond to indices in this lookup index array, allowing us to calculate the 458eebf9e81673150a6c4fec89b64e3cdb595377c83Bobby Georgescu * offset of the specific item we want from within a specific bucket. 459eebf9e81673150a6c4fec89b64e3cdb595377c83Bobby Georgescu */ 460eebf9e81673150a6c4fec89b64e3cdb595377c83Bobby Georgescu private void buildLookupIndex() { 461eebf9e81673150a6c4fec89b64e3cdb595377c83Bobby Georgescu int numBuckets = mBuckets.length; 462eebf9e81673150a6c4fec89b64e3cdb595377c83Bobby Georgescu mUnifiedLookupIndex = new int[mNumObjects + numBuckets]; 463eebf9e81673150a6c4fec89b64e3cdb595377c83Bobby Georgescu int currentUnifiedIndexEntry = 0; 464eebf9e81673150a6c4fec89b64e3cdb595377c83Bobby Georgescu int nextUnifiedEntry; 465eebf9e81673150a6c4fec89b64e3cdb595377c83Bobby Georgescu 466eebf9e81673150a6c4fec89b64e3cdb595377c83Bobby Georgescu mMtpObjects = new MtpObjectInfo[mNumObjects]; 467eebf9e81673150a6c4fec89b64e3cdb595377c83Bobby Georgescu int currentItemsEntry = 0; 468eebf9e81673150a6c4fec89b64e3cdb595377c83Bobby Georgescu for (int i = 0; i < numBuckets; i++) { 469eebf9e81673150a6c4fec89b64e3cdb595377c83Bobby Georgescu DateBucket bucket = mBuckets[i]; 470eebf9e81673150a6c4fec89b64e3cdb595377c83Bobby Georgescu nextUnifiedEntry = currentUnifiedIndexEntry + bucket.tempElementsList.size() + 1; 471eebf9e81673150a6c4fec89b64e3cdb595377c83Bobby Georgescu Arrays.fill(mUnifiedLookupIndex, currentUnifiedIndexEntry, nextUnifiedEntry, i); 472eebf9e81673150a6c4fec89b64e3cdb595377c83Bobby Georgescu bucket.unifiedStartIndex = currentUnifiedIndexEntry; 473eebf9e81673150a6c4fec89b64e3cdb595377c83Bobby Georgescu bucket.unifiedEndIndex = nextUnifiedEntry - 1; 474eebf9e81673150a6c4fec89b64e3cdb595377c83Bobby Georgescu currentUnifiedIndexEntry = nextUnifiedEntry; 475eebf9e81673150a6c4fec89b64e3cdb595377c83Bobby Georgescu 476eebf9e81673150a6c4fec89b64e3cdb595377c83Bobby Georgescu bucket.itemsStartIndex = currentItemsEntry; 477c8a9e86919dca8938948f97efc3dcbe143e806bfBobby Georgescu bucket.numItems = bucket.tempElementsList.size(); 478c8a9e86919dca8938948f97efc3dcbe143e806bfBobby Georgescu for (int j = 0; j < bucket.numItems; j++) { 479eebf9e81673150a6c4fec89b64e3cdb595377c83Bobby Georgescu mMtpObjects[currentItemsEntry] = bucket.tempElementsList.get(j); 480eebf9e81673150a6c4fec89b64e3cdb595377c83Bobby Georgescu currentItemsEntry++; 481eebf9e81673150a6c4fec89b64e3cdb595377c83Bobby Georgescu } 482eebf9e81673150a6c4fec89b64e3cdb595377c83Bobby Georgescu bucket.tempElementsList = null; 483d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu } 484d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu } 485d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu 486eebf9e81673150a6c4fec89b64e3cdb595377c83Bobby Georgescu private void copyResults() { 487eebf9e81673150a6c4fec89b64e3cdb595377c83Bobby Georgescu MtpDeviceIndex.this.mUnifiedLookupIndex = mUnifiedLookupIndex; 488eebf9e81673150a6c4fec89b64e3cdb595377c83Bobby Georgescu MtpDeviceIndex.this.mMtpObjects = mMtpObjects; 489eebf9e81673150a6c4fec89b64e3cdb595377c83Bobby Georgescu MtpDeviceIndex.this.mBuckets = mBuckets; 490eebf9e81673150a6c4fec89b64e3cdb595377c83Bobby Georgescu mUnifiedLookupIndex = null; 491eebf9e81673150a6c4fec89b64e3cdb595377c83Bobby Georgescu mMtpObjects = null; 492eebf9e81673150a6c4fec89b64e3cdb595377c83Bobby Georgescu mBuckets = null; 493d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu } 494eebf9e81673150a6c4fec89b64e3cdb595377c83Bobby Georgescu 495eebf9e81673150a6c4fec89b64e3cdb595377c83Bobby Georgescu @Override 496eebf9e81673150a6c4fec89b64e3cdb595377c83Bobby Georgescu public void run() { 497eebf9e81673150a6c4fec89b64e3cdb595377c83Bobby Georgescu try { 498eebf9e81673150a6c4fec89b64e3cdb595377c83Bobby Georgescu indexDevice(); 499eebf9e81673150a6c4fec89b64e3cdb595377c83Bobby Georgescu } catch (IndexingException e) { 500eebf9e81673150a6c4fec89b64e3cdb595377c83Bobby Georgescu synchronized (MtpDeviceIndex.this) { 501eebf9e81673150a6c4fec89b64e3cdb595377c83Bobby Georgescu resetState(); 502eebf9e81673150a6c4fec89b64e3cdb595377c83Bobby Georgescu if (mProgressListener != null) { 503eebf9e81673150a6c4fec89b64e3cdb595377c83Bobby Georgescu mProgressListener.onIndexFinish(); 504d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu } 505d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu } 506d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu } 507d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu } 508eebf9e81673150a6c4fec89b64e3cdb595377c83Bobby Georgescu 509eebf9e81673150a6c4fec89b64e3cdb595377c83Bobby Georgescu private void indexDevice() throws IndexingException { 510eebf9e81673150a6c4fec89b64e3cdb595377c83Bobby Georgescu synchronized (MtpDeviceIndex.this) { 511eebf9e81673150a6c4fec89b64e3cdb595377c83Bobby Georgescu mProgress = Progress.Started; 512d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu } 513eebf9e81673150a6c4fec89b64e3cdb595377c83Bobby Georgescu mBucketsTemp = new HashMap<SimpleDate, DateBucket>(); 514eebf9e81673150a6c4fec89b64e3cdb595377c83Bobby Georgescu for (int storageId : mDevice.getStorageIds()) { 515eebf9e81673150a6c4fec89b64e3cdb595377c83Bobby Georgescu if (mDevice != getDevice()) throw new IndexingException(); 516eebf9e81673150a6c4fec89b64e3cdb595377c83Bobby Georgescu Stack<Integer> pendingDirectories = new Stack<Integer>(); 517eebf9e81673150a6c4fec89b64e3cdb595377c83Bobby Georgescu pendingDirectories.add(0xFFFFFFFF); // start at the root of the device 518eebf9e81673150a6c4fec89b64e3cdb595377c83Bobby Georgescu while (!pendingDirectories.isEmpty()) { 519eebf9e81673150a6c4fec89b64e3cdb595377c83Bobby Georgescu if (mDevice != getDevice()) throw new IndexingException(); 520eebf9e81673150a6c4fec89b64e3cdb595377c83Bobby Georgescu int dirHandle = pendingDirectories.pop(); 521eebf9e81673150a6c4fec89b64e3cdb595377c83Bobby Georgescu for (int objectHandle : mDevice.getObjectHandles(storageId, 0, dirHandle)) { 522eebf9e81673150a6c4fec89b64e3cdb595377c83Bobby Georgescu MtpObjectInfo objectInfo = mDevice.getObjectInfo(objectHandle); 523eebf9e81673150a6c4fec89b64e3cdb595377c83Bobby Georgescu if (objectInfo == null) throw new IndexingException(); 524baa68886db66f4beb6a07e4441b122975b778f1cBobby Georgescu int format = objectInfo.getFormat(); 525baa68886db66f4beb6a07e4441b122975b778f1cBobby Georgescu if (format == MtpConstants.FORMAT_ASSOCIATION) { 526baa68886db66f4beb6a07e4441b122975b778f1cBobby Georgescu pendingDirectories.add(objectHandle); 527baa68886db66f4beb6a07e4441b122975b778f1cBobby Georgescu } else if (SUPPORTED_IMAGE_FORMATS.contains(format) 528baa68886db66f4beb6a07e4441b122975b778f1cBobby Georgescu || SUPPORTED_VIDEO_FORMATS.contains(format)) { 529baa68886db66f4beb6a07e4441b122975b778f1cBobby Georgescu addObject(objectInfo); 530eebf9e81673150a6c4fec89b64e3cdb595377c83Bobby Georgescu } 531eebf9e81673150a6c4fec89b64e3cdb595377c83Bobby Georgescu } 532eebf9e81673150a6c4fec89b64e3cdb595377c83Bobby Georgescu } 533d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu } 534eebf9e81673150a6c4fec89b64e3cdb595377c83Bobby Georgescu Collection<DateBucket> values = mBucketsTemp.values(); 535eebf9e81673150a6c4fec89b64e3cdb595377c83Bobby Georgescu mBucketsTemp = null; 536eebf9e81673150a6c4fec89b64e3cdb595377c83Bobby Georgescu mBuckets = values.toArray(new DateBucket[values.size()]); 537eebf9e81673150a6c4fec89b64e3cdb595377c83Bobby Georgescu values = null; 538eebf9e81673150a6c4fec89b64e3cdb595377c83Bobby Georgescu synchronized (MtpDeviceIndex.this) { 539eebf9e81673150a6c4fec89b64e3cdb595377c83Bobby Georgescu mProgress = Progress.Sorting; 540eebf9e81673150a6c4fec89b64e3cdb595377c83Bobby Georgescu if (mProgressListener != null) { 541eebf9e81673150a6c4fec89b64e3cdb595377c83Bobby Georgescu mProgressListener.onSorting(); 542eebf9e81673150a6c4fec89b64e3cdb595377c83Bobby Georgescu } 543eebf9e81673150a6c4fec89b64e3cdb595377c83Bobby Georgescu } 544eebf9e81673150a6c4fec89b64e3cdb595377c83Bobby Georgescu sortAll(); 545eebf9e81673150a6c4fec89b64e3cdb595377c83Bobby Georgescu buildLookupIndex(); 546eebf9e81673150a6c4fec89b64e3cdb595377c83Bobby Georgescu synchronized (MtpDeviceIndex.this) { 547eebf9e81673150a6c4fec89b64e3cdb595377c83Bobby Georgescu if (mDevice != getDevice()) throw new IndexingException(); 548eebf9e81673150a6c4fec89b64e3cdb595377c83Bobby Georgescu copyResults(); 549eebf9e81673150a6c4fec89b64e3cdb595377c83Bobby Georgescu 550eebf9e81673150a6c4fec89b64e3cdb595377c83Bobby Georgescu /* 551eebf9e81673150a6c4fec89b64e3cdb595377c83Bobby Georgescu * In order for getBuckets to operate in constant time for descending 552eebf9e81673150a6c4fec89b64e3cdb595377c83Bobby Georgescu * order, we must precompute a reversed array of the buckets, mainly 553eebf9e81673150a6c4fec89b64e3cdb595377c83Bobby Georgescu * because the android.widget.SectionIndexer interface which adapters 554eebf9e81673150a6c4fec89b64e3cdb595377c83Bobby Georgescu * that call getBuckets implement depends on section numbers to be 555eebf9e81673150a6c4fec89b64e3cdb595377c83Bobby Georgescu * ascending relative to the scroll position, so we must have this for 556eebf9e81673150a6c4fec89b64e3cdb595377c83Bobby Georgescu * descending order or the scrollbar goes crazy. 557eebf9e81673150a6c4fec89b64e3cdb595377c83Bobby Georgescu */ 558eebf9e81673150a6c4fec89b64e3cdb595377c83Bobby Georgescu computeReversedBuckets(); 559d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu 560eebf9e81673150a6c4fec89b64e3cdb595377c83Bobby Georgescu mProgress = Progress.Finished; 561eebf9e81673150a6c4fec89b64e3cdb595377c83Bobby Georgescu if (mProgressListener != null) { 562eebf9e81673150a6c4fec89b64e3cdb595377c83Bobby Georgescu mProgressListener.onIndexFinish(); 563eebf9e81673150a6c4fec89b64e3cdb595377c83Bobby Georgescu } 564eebf9e81673150a6c4fec89b64e3cdb595377c83Bobby Georgescu } 565d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu } 566eebf9e81673150a6c4fec89b64e3cdb595377c83Bobby Georgescu 567eebf9e81673150a6c4fec89b64e3cdb595377c83Bobby Georgescu private SimpleDate mDateInstance = new SimpleDate(); 568eebf9e81673150a6c4fec89b64e3cdb595377c83Bobby Georgescu 569eebf9e81673150a6c4fec89b64e3cdb595377c83Bobby Georgescu private void addObject(MtpObjectInfo objectInfo) { 570eebf9e81673150a6c4fec89b64e3cdb595377c83Bobby Georgescu mNumObjects++; 571eebf9e81673150a6c4fec89b64e3cdb595377c83Bobby Georgescu mDateInstance.setTimestamp(objectInfo.getDateCreated()); 572eebf9e81673150a6c4fec89b64e3cdb595377c83Bobby Georgescu DateBucket bucket = mBucketsTemp.get(mDateInstance); 573eebf9e81673150a6c4fec89b64e3cdb595377c83Bobby Georgescu if (bucket == null) { 574eebf9e81673150a6c4fec89b64e3cdb595377c83Bobby Georgescu bucket = new DateBucket(mDateInstance, objectInfo); 575eebf9e81673150a6c4fec89b64e3cdb595377c83Bobby Georgescu mBucketsTemp.put(mDateInstance, bucket); 576eebf9e81673150a6c4fec89b64e3cdb595377c83Bobby Georgescu mDateInstance = new SimpleDate(); // only create new date 577eebf9e81673150a6c4fec89b64e3cdb595377c83Bobby Georgescu // objects when they are used 578eebf9e81673150a6c4fec89b64e3cdb595377c83Bobby Georgescu return; 579eebf9e81673150a6c4fec89b64e3cdb595377c83Bobby Georgescu } else { 580eebf9e81673150a6c4fec89b64e3cdb595377c83Bobby Georgescu bucket.tempElementsList.add(objectInfo); 581eebf9e81673150a6c4fec89b64e3cdb595377c83Bobby Georgescu } 582eebf9e81673150a6c4fec89b64e3cdb595377c83Bobby Georgescu if (mProgressListener != null) { 583eebf9e81673150a6c4fec89b64e3cdb595377c83Bobby Georgescu mProgressListener.onObjectIndexed(objectInfo, mNumObjects); 584eebf9e81673150a6c4fec89b64e3cdb595377c83Bobby Georgescu } 585d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu } 586d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu 587eebf9e81673150a6c4fec89b64e3cdb595377c83Bobby Georgescu private void sortAll() { 588eebf9e81673150a6c4fec89b64e3cdb595377c83Bobby Georgescu Arrays.sort(mBuckets); 589eebf9e81673150a6c4fec89b64e3cdb595377c83Bobby Georgescu for (DateBucket bucket : mBuckets) { 590eebf9e81673150a6c4fec89b64e3cdb595377c83Bobby Georgescu bucket.sortElements(sMtpObjectComparator); 591eebf9e81673150a6c4fec89b64e3cdb595377c83Bobby Georgescu } 592d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu } 593d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu 594d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu } 595d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu 596d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu private void computeReversedBuckets() { 597d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu mCachedReverseBuckets = new Object[mBuckets.length]; 598d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu for (int i = 0; i < mCachedReverseBuckets.length; i++) { 599d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu mCachedReverseBuckets[i] = mBuckets[mBuckets.length - 1 - i]; 600d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu } 601d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu } 602d3aac52ffb88ced53413d5eef29c641dd6982267Bobby Georgescu} 603