1 /* 2 * Copyright (C) 2022 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 package com.android.quicksearchbox 17 18 import android.database.DataSetObservable 19 import android.database.DataSetObserver 20 import com.google.common.annotations.VisibleForTesting 21 import kotlin.collections.ArrayList 22 import kotlin.collections.HashSet 23 24 /** A SuggestionCursor that is backed by a list of Suggestions. */ 25 open class ListSuggestionCursor(userQuery: String?, capacity: Int) : 26 AbstractSuggestionCursorWrapper(userQuery!!) { 27 private val mDataSetObservable: DataSetObservable = DataSetObservable() 28 29 private val mSuggestions: ArrayList<Entry> 30 31 private var mExtraColumns: HashSet<String>? = null 32 33 override var position = 0 34 35 constructor(userQuery: String?) : this(userQuery, DEFAULT_CAPACITY) 36 37 @VisibleForTesting 38 constructor( 39 userQuery: String?, 40 vararg suggestions: Suggestion? 41 ) : this(userQuery, suggestions.size) { 42 for (suggestion in suggestions) { 43 add(suggestion!!) 44 } 45 } 46 47 /** 48 * Adds a suggestion from another suggestion cursor. 49 * 50 * @return `true` if the suggestion was added. 51 */ addnull52 open fun add(suggestion: Suggestion): Boolean { 53 mSuggestions.add(Entry(suggestion)) 54 return true 55 } 56 closenull57 override fun close() { 58 mSuggestions.clear() 59 } 60 moveTonull61 override fun moveTo(pos: Int) { 62 position = pos 63 } 64 moveToNextnull65 override fun moveToNext(): Boolean { 66 val size: Int = mSuggestions.size 67 if (position >= size) { 68 // Already past the end 69 return false 70 } 71 position++ 72 return position < size 73 } 74 removeRownull75 fun removeRow() { 76 mSuggestions.removeAt(position) 77 } 78 replaceRownull79 fun replaceRow(suggestion: Suggestion) { 80 mSuggestions.set(position, Entry(suggestion)) 81 } 82 83 override val count: Int 84 get() = mSuggestions.size 85 86 @Override currentnull87 override fun current(): Suggestion { 88 return mSuggestions.get(position).get() 89 } 90 91 @Override toStringnull92 override fun toString(): String { 93 return this::class.simpleName.toString() + "{[" + userQuery + "] " + mSuggestions + "}" 94 } 95 96 /** 97 * Register an observer that is called when changes happen to this data set. 98 * 99 * @param observer gets notified when the data set changes. 100 */ registerDataSetObservernull101 override fun registerDataSetObserver(observer: DataSetObserver?) { 102 mDataSetObservable.registerObserver(observer) 103 } 104 105 /** 106 * Unregister an observer that has previously been registered with [.registerDataSetObserver] 107 * 108 * @param observer the observer to unregister. 109 */ unregisterDataSetObservernull110 override fun unregisterDataSetObserver(observer: DataSetObserver?) { 111 mDataSetObservable.unregisterObserver(observer) 112 } 113 notifyDataSetChangednull114 protected fun notifyDataSetChanged() { 115 mDataSetObservable.notifyChanged() 116 } 117 118 // override with caching to avoid re-parsing the extras 119 @get:Override 120 override val extras: SuggestionExtras? 121 // override with caching to avoid re-parsing the extras 122 get() = mSuggestions.get(position).getExtras() 123 124 override val extraColumns: Collection<String>? 125 get() { 126 if (mExtraColumns == null) { 127 mExtraColumns = HashSet<String>() 128 for (e in mSuggestions) { 129 val extras: SuggestionExtras? = e.getExtras() 130 val extraColumns: Collection<String>? = 131 if (extras == null) null else extras.extraColumnNames 132 if (extraColumns != null) { 133 for (column in extras!!.extraColumnNames) { 134 mExtraColumns?.add(column) 135 } 136 } 137 } 138 } 139 return if (mExtraColumns!!.isEmpty()) null else mExtraColumns 140 } 141 142 /** This class exists purely to cache the suggestion extras. */ 143 private class Entry(private val mSuggestion: Suggestion) { 144 private var mExtras: SuggestionExtras? = null getnull145 fun get(): Suggestion { 146 return mSuggestion 147 } 148 getExtrasnull149 fun getExtras(): SuggestionExtras? { 150 if (mExtras == null) { 151 mExtras = mSuggestion.extras 152 } 153 return mExtras 154 } 155 } 156 157 companion object { 158 private const val DEFAULT_CAPACITY = 16 159 } 160 161 init { 162 mSuggestions = ArrayList<Entry>(capacity) 163 } 164 } 165