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 17 package com.android.quicksearchbox.ui 18 19 import android.database.DataSetObserver 20 import android.util.Log 21 import android.view.View.OnFocusChangeListener 22 import com.android.quicksearchbox.SuggestionCursor 23 import com.android.quicksearchbox.SuggestionPosition 24 import com.android.quicksearchbox.Suggestions 25 26 /** 27 * A [SuggestionsListAdapter] that doesn't expose the new suggestions until there are some results 28 * to show. 29 */ 30 class DelayingSuggestionsAdapter<A>(private val mDelayedAdapter: SuggestionsAdapterBase<A>) : 31 SuggestionsAdapter<A> { 32 private var mPendingDataSetObserver: DataSetObserver? = null 33 private var mPendingSuggestions: Suggestions? = null closenull34 fun close() { 35 setPendingSuggestions(null) 36 mDelayedAdapter.close() 37 } 38 39 /** Gets whether the given suggestions are non-empty for the selected source. */ shouldPublishnull40 private fun shouldPublish(suggestions: Suggestions?): Boolean { 41 if (suggestions!!.isDone) return true 42 val cursor: SuggestionCursor? = suggestions.getResult() 43 return cursor != null && cursor.count > 0 44 } 45 setPendingSuggestionsnull46 private fun setPendingSuggestions(suggestions: Suggestions?) { 47 if (mPendingSuggestions === suggestions) { 48 return 49 } 50 if (mDelayedAdapter.isClosed) { 51 suggestions?.release() 52 return 53 } 54 if (mPendingDataSetObserver == null) { 55 mPendingDataSetObserver = PendingSuggestionsObserver() 56 } 57 if (mPendingSuggestions != null) { 58 mPendingSuggestions!!.unregisterDataSetObserver(mPendingDataSetObserver) 59 // Close old suggestions, but only if they are not also the current 60 // suggestions. 61 if (mPendingSuggestions !== this.suggestions) { 62 mPendingSuggestions!!.release() 63 } 64 } 65 mPendingSuggestions = suggestions 66 if (mPendingSuggestions != null) { 67 mPendingSuggestions!!.registerDataSetObserver(mPendingDataSetObserver) 68 } 69 } 70 onPendingSuggestionsChangednull71 protected fun onPendingSuggestionsChanged() { 72 if (DBG) Log.d(TAG, "onPendingSuggestionsChanged(), mPendingSuggestions=" + mPendingSuggestions) 73 if (shouldPublish(mPendingSuggestions)) { 74 if (DBG) Log.d(TAG, "Suggestions now available, publishing: $mPendingSuggestions") 75 mDelayedAdapter.suggestions = mPendingSuggestions 76 // The suggestions are no longer pending. 77 setPendingSuggestions(null) 78 } 79 } 80 81 private inner class PendingSuggestionsObserver : DataSetObserver() { 82 @Override onChangednull83 override fun onChanged() { 84 onPendingSuggestionsChanged() 85 } 86 } 87 88 @get:Override 89 override val listAdapter: A 90 get() = mDelayedAdapter.listAdapter 91 val currentPromotedSuggestions: SuggestionCursor? 92 get() = mDelayedAdapter.currentSuggestions 93 94 // Clear any old pending suggestions. 95 @get:Override 96 @set:Override 97 override var suggestions: Suggestions? 98 get() = mDelayedAdapter.suggestions 99 set(suggestions) { 100 if (suggestions == null) { 101 mDelayedAdapter.suggestions = null 102 setPendingSuggestions(null) 103 return 104 } 105 if (shouldPublish(suggestions)) { 106 if (DBG) Log.d(TAG, "Publishing suggestions immediately: $suggestions") 107 mDelayedAdapter.suggestions = suggestions 108 // Clear any old pending suggestions. 109 setPendingSuggestions(null) 110 } else { 111 if (DBG) Log.d(TAG, "Delaying suggestions publishing: $suggestions") 112 setPendingSuggestions(suggestions) 113 } 114 } 115 116 @Override getSuggestionnull117 override fun getSuggestion(suggestionId: Long): SuggestionPosition? { 118 return mDelayedAdapter.getSuggestion(suggestionId) 119 } 120 121 @Override onSuggestionClickednull122 override fun onSuggestionClicked(suggestionId: Long) { 123 mDelayedAdapter.onSuggestionClicked(suggestionId) 124 } 125 126 @Override onSuggestionQueryRefineClickednull127 override fun onSuggestionQueryRefineClicked(suggestionId: Long) { 128 mDelayedAdapter.onSuggestionQueryRefineClicked(suggestionId) 129 } 130 131 @Override setOnFocusChangeListenernull132 override fun setOnFocusChangeListener(l: OnFocusChangeListener?) { 133 mDelayedAdapter.setOnFocusChangeListener(l) 134 } 135 136 @Override setSuggestionClickListenernull137 override fun setSuggestionClickListener(listener: SuggestionClickListener?) { 138 mDelayedAdapter.setSuggestionClickListener(listener) 139 } 140 141 @get:Override 142 override val isEmpty: Boolean 143 get() = mDelayedAdapter.isEmpty 144 145 companion object { 146 private const val DBG = false 147 private const val TAG = "QSB.DelayingSuggestionsAdapter" 148 } 149 } 150