Loading src/com/android/documentsui/queries/SearchOptionsController.kt +32 −35 Original line number Diff line number Diff line Loading @@ -36,17 +36,17 @@ import java.time.ZoneId * and converts them to a state of the dropdowns. It must be created with the view that contains * the buttons that trigger showing or hiding of the dropdowns. */ class SearchOptionsController(private val mContainer: View?) { class SearchOptionsController(private val container: View?) { // The value of currently selected options. Initialized to sensible defaults. private var mLastModifiedOption: LastModifiedOption = LastModifiedOption.ANY_TIME private var mFileTypeOption: FileTypeOption = FileTypeOption.ANY_TYPE private var mLocationOption: SearchLocationOption = SearchLocationOption.CURRENT_FOLDER private var lastModifiedOption: LastModifiedOption = LastModifiedOption.ANY_TIME private var fileTypeOption: FileTypeOption = FileTypeOption.ANY_TYPE private var locationOption: SearchLocationOption = SearchLocationOption.CURRENT_FOLDER // A single listener to query option change events. private var mOptionsListener: SearchOptionsListener? = null private var optionsListener: SearchOptionsListener? = null init { if (FlagUtils.isUseMaterial3FlagEnabled() && mContainer != null) { if (FlagUtils.isUseMaterial3FlagEnabled() && container != null) { makeTrigger( getRes(R.id.search_location_trigger), getRes(R.menu.search_location_menu), Loading @@ -67,9 +67,9 @@ class SearchOptionsController(private val mContainer: View?) { private fun getSelectedMenuOption(@MenuRes menuId: Int): Int { return when (menuId) { R.menu.search_location_menu -> mLocationOption.value R.menu.search_last_modified_menu -> mLastModifiedOption.value R.menu.search_file_type_menu -> mFileTypeOption.value R.menu.search_location_menu -> locationOption.value R.menu.search_last_modified_menu -> lastModifiedOption.value R.menu.search_file_type_menu -> fileTypeOption.value else -> throw IllegalArgumentException("Unexpected menu ID $menuId") } } Loading @@ -82,7 +82,7 @@ class SearchOptionsController(private val mContainer: View?) { @MenuRes menuId: Int, callback: (option: Int) -> Boolean ) { val trigger = mContainer?.findViewById<Chip>(triggerId) val trigger = container?.findViewById<Chip>(triggerId) trigger?.setOnClickListener { showMenu(trigger, menuId) { if (callback(it)) { Loading @@ -98,12 +98,11 @@ class SearchOptionsController(private val mContainer: View?) { */ fun onLocationSelected(locationId: Int): Boolean { val selectedOption = searchLocationOptionFor(locationId) ?: return false return if (selectedOption == mLocationOption) { false } else { mLocationOption = selectedOption true if (selectedOption == locationOption) { return false } locationOption = selectedOption return true } /** Loading @@ -112,12 +111,11 @@ class SearchOptionsController(private val mContainer: View?) { */ fun onFileTypeSelected(fileTypeId: Int): Boolean { val selectedOption = fileTypeOptionFor(fileTypeId) ?: return false return if (selectedOption == mFileTypeOption) { false } else { mFileTypeOption = selectedOption true if (selectedOption == fileTypeOption) { return false } fileTypeOption = selectedOption return true } /** Loading @@ -126,23 +124,22 @@ class SearchOptionsController(private val mContainer: View?) { */ fun onLastModifiedSelected(lastModifiedId: Int): Boolean { val selectedOption = lastModifiedOptionFor(lastModifiedId) ?: return false return if (selectedOption == mLastModifiedOption) { false } else { mLastModifiedOption = selectedOption true if (selectedOption == lastModifiedOption) { return false } lastModifiedOption = selectedOption return true } /** * Notifies an option listener about options change, if one is registered. */ fun notifyOptionsChangeListener() { mOptionsListener?.onOptionsChanged( optionsListener?.onOptionsChanged( SearchOptionsState( mFileTypeOption, mLastModifiedOption, mLocationOption fileTypeOption, lastModifiedOption, locationOption ) ) } Loading @@ -152,7 +149,7 @@ class SearchOptionsController(private val mContainer: View?) { * is set to null, that is equivalent to removing the listener. */ fun setOptionChangeListener(listener: SearchOptionsListener) { mOptionsListener = listener optionsListener = listener } /** Loading @@ -161,13 +158,13 @@ class SearchOptionsController(private val mContainer: View?) { */ private fun getLastModifiedQueryArgs(): Bundle { val bundle = Bundle() if (mLastModifiedOption != LastModifiedOption.ANY_TIME) { if (lastModifiedOption != LastModifiedOption.ANY_TIME) { bundle.putLong( DocumentsContract.QUERY_ARG_LAST_MODIFIED_AFTER, LocalDate.now() .atStartOfDay(ZoneId.systemDefault()) .toInstant() .toEpochMilli() - mLastModifiedOption.millis .toEpochMilli() - lastModifiedOption.millis ) } return bundle Loading @@ -175,7 +172,7 @@ class SearchOptionsController(private val mContainer: View?) { private fun getFileTypeQueryArgs(): Bundle { val bundle = Bundle() val mimeTypes = when (mFileTypeOption) { val mimeTypes = when (fileTypeOption) { FileTypeOption.AUDIO -> SearchChipViewManager.AUDIO_MIMETYPES FileTypeOption.DOCUMENTS -> SearchChipViewManager.DOCUMENTS_MIMETYPES FileTypeOption.IMAGES -> SearchChipViewManager.IMAGES_MIMETYPES Loading @@ -202,14 +199,14 @@ class SearchOptionsController(private val mContainer: View?) { * Sets the visibility of the search drop down options bar. */ fun setVisible(visible: Boolean) { mContainer?.visibility = if (visible) View.VISIBLE else View.GONE container?.visibility = if (visible) View.VISIBLE else View.GONE } /** * Returns whether or not this controller is visible. */ fun isVisible(): Boolean { return mContainer?.visibility == View.VISIBLE return container?.visibility == View.VISIBLE } fun showMenu(chip: Chip, @MenuRes menuRes: Int, callback: (option: Int) -> Unit) { Loading tests/unit/com/android/documentsui/queries/SearchOptionsControllerTest.kt +24 −24 Original line number Diff line number Diff line Loading @@ -24,7 +24,7 @@ import androidx.test.filters.SmallTest import androidx.test.platform.app.InstrumentationRegistry import com.android.documentsui.flags.Flags.FLAG_USE_SEARCH_V2_READ_ONLY import com.android.documentsui.rules.CheckAndForceMaterial3Flag import junit.framework.Assert.assertEquals import org.junit.Assert.assertEquals import org.junit.Before import org.junit.Rule import org.junit.Test Loading @@ -32,10 +32,10 @@ import org.junit.runner.RunWith import org.mockito.Mockito.spy class TestSearchOptionsListener() : SearchOptionsListener { var mOptionsState: SearchOptionsState? = null var optionsState: SearchOptionsState? = null override fun onOptionsChanged(options: SearchOptionsState) { mOptionsState = options optionsState = options } } Loading @@ -46,46 +46,46 @@ class SearchOptionsControllerTest { @get:Rule val checkFlags = CheckAndForceMaterial3Flag() var mContext: Context? = null var mController: SearchOptionsController? = null var mContainer: LinearLayout? = null val mOptionsListener = TestSearchOptionsListener() var context: Context? = null var controller: SearchOptionsController? = null var container: LinearLayout? = null val optionsListener = TestSearchOptionsListener() @Before fun setUp() { mContext = InstrumentationRegistry.getInstrumentation().targetContext mContainer = spy(LinearLayout(mContext)) mController = SearchOptionsController(mContainer) mController!!.setOptionChangeListener(mOptionsListener) context = InstrumentationRegistry.getInstrumentation().targetContext container = spy(LinearLayout(context)) controller = SearchOptionsController(container) controller!!.setOptionChangeListener(optionsListener) } @Test fun testOptionsUpdateWorks() { for (e in SearchLocationOption.entries) { mController!!.onLocationSelected(e.value) mController!!.notifyOptionsChangeListener() assertEquals(mOptionsListener.mOptionsState!!.location, e) controller!!.onLocationSelected(e.value) controller!!.notifyOptionsChangeListener() assertEquals(optionsListener.optionsState!!.location, e) } for (e in LastModifiedOption.entries) { mController!!.onLastModifiedSelected(e.value) mController!!.notifyOptionsChangeListener() assertEquals(mOptionsListener.mOptionsState!!.lastModified, e) controller!!.onLastModifiedSelected(e.value) controller!!.notifyOptionsChangeListener() assertEquals(optionsListener.optionsState!!.lastModified, e) } for (e in FileTypeOption.entries) { mController!!.onFileTypeSelected(e.value) mController!!.notifyOptionsChangeListener() assertEquals(mOptionsListener.mOptionsState!!.fileType, e) controller!!.onFileTypeSelected(e.value) controller!!.notifyOptionsChangeListener() assertEquals(optionsListener.optionsState!!.fileType, e) } } @Test fun testGetOptionsQueryArgs() { // Reset the options to minimum filtering state. mController!!.onLocationSelected(SearchLocationOption.EVERYWHERE.ordinal) mController!!.onLastModifiedSelected(LastModifiedOption.ANY_TIME.ordinal) mController!!.onFileTypeSelected(FileTypeOption.ANY_TYPE.ordinal) controller!!.onLocationSelected(SearchLocationOption.EVERYWHERE.ordinal) controller!!.onLastModifiedSelected(LastModifiedOption.ANY_TIME.ordinal) controller!!.onFileTypeSelected(FileTypeOption.ANY_TYPE.ordinal) val queryArgs = mController!!.getOptionsQueryArgs() val queryArgs = controller!!.getOptionsQueryArgs() // Expect no query args with the default (no limits) settings. assertEquals(queryArgs.size, 0) } Loading Loading
src/com/android/documentsui/queries/SearchOptionsController.kt +32 −35 Original line number Diff line number Diff line Loading @@ -36,17 +36,17 @@ import java.time.ZoneId * and converts them to a state of the dropdowns. It must be created with the view that contains * the buttons that trigger showing or hiding of the dropdowns. */ class SearchOptionsController(private val mContainer: View?) { class SearchOptionsController(private val container: View?) { // The value of currently selected options. Initialized to sensible defaults. private var mLastModifiedOption: LastModifiedOption = LastModifiedOption.ANY_TIME private var mFileTypeOption: FileTypeOption = FileTypeOption.ANY_TYPE private var mLocationOption: SearchLocationOption = SearchLocationOption.CURRENT_FOLDER private var lastModifiedOption: LastModifiedOption = LastModifiedOption.ANY_TIME private var fileTypeOption: FileTypeOption = FileTypeOption.ANY_TYPE private var locationOption: SearchLocationOption = SearchLocationOption.CURRENT_FOLDER // A single listener to query option change events. private var mOptionsListener: SearchOptionsListener? = null private var optionsListener: SearchOptionsListener? = null init { if (FlagUtils.isUseMaterial3FlagEnabled() && mContainer != null) { if (FlagUtils.isUseMaterial3FlagEnabled() && container != null) { makeTrigger( getRes(R.id.search_location_trigger), getRes(R.menu.search_location_menu), Loading @@ -67,9 +67,9 @@ class SearchOptionsController(private val mContainer: View?) { private fun getSelectedMenuOption(@MenuRes menuId: Int): Int { return when (menuId) { R.menu.search_location_menu -> mLocationOption.value R.menu.search_last_modified_menu -> mLastModifiedOption.value R.menu.search_file_type_menu -> mFileTypeOption.value R.menu.search_location_menu -> locationOption.value R.menu.search_last_modified_menu -> lastModifiedOption.value R.menu.search_file_type_menu -> fileTypeOption.value else -> throw IllegalArgumentException("Unexpected menu ID $menuId") } } Loading @@ -82,7 +82,7 @@ class SearchOptionsController(private val mContainer: View?) { @MenuRes menuId: Int, callback: (option: Int) -> Boolean ) { val trigger = mContainer?.findViewById<Chip>(triggerId) val trigger = container?.findViewById<Chip>(triggerId) trigger?.setOnClickListener { showMenu(trigger, menuId) { if (callback(it)) { Loading @@ -98,12 +98,11 @@ class SearchOptionsController(private val mContainer: View?) { */ fun onLocationSelected(locationId: Int): Boolean { val selectedOption = searchLocationOptionFor(locationId) ?: return false return if (selectedOption == mLocationOption) { false } else { mLocationOption = selectedOption true if (selectedOption == locationOption) { return false } locationOption = selectedOption return true } /** Loading @@ -112,12 +111,11 @@ class SearchOptionsController(private val mContainer: View?) { */ fun onFileTypeSelected(fileTypeId: Int): Boolean { val selectedOption = fileTypeOptionFor(fileTypeId) ?: return false return if (selectedOption == mFileTypeOption) { false } else { mFileTypeOption = selectedOption true if (selectedOption == fileTypeOption) { return false } fileTypeOption = selectedOption return true } /** Loading @@ -126,23 +124,22 @@ class SearchOptionsController(private val mContainer: View?) { */ fun onLastModifiedSelected(lastModifiedId: Int): Boolean { val selectedOption = lastModifiedOptionFor(lastModifiedId) ?: return false return if (selectedOption == mLastModifiedOption) { false } else { mLastModifiedOption = selectedOption true if (selectedOption == lastModifiedOption) { return false } lastModifiedOption = selectedOption return true } /** * Notifies an option listener about options change, if one is registered. */ fun notifyOptionsChangeListener() { mOptionsListener?.onOptionsChanged( optionsListener?.onOptionsChanged( SearchOptionsState( mFileTypeOption, mLastModifiedOption, mLocationOption fileTypeOption, lastModifiedOption, locationOption ) ) } Loading @@ -152,7 +149,7 @@ class SearchOptionsController(private val mContainer: View?) { * is set to null, that is equivalent to removing the listener. */ fun setOptionChangeListener(listener: SearchOptionsListener) { mOptionsListener = listener optionsListener = listener } /** Loading @@ -161,13 +158,13 @@ class SearchOptionsController(private val mContainer: View?) { */ private fun getLastModifiedQueryArgs(): Bundle { val bundle = Bundle() if (mLastModifiedOption != LastModifiedOption.ANY_TIME) { if (lastModifiedOption != LastModifiedOption.ANY_TIME) { bundle.putLong( DocumentsContract.QUERY_ARG_LAST_MODIFIED_AFTER, LocalDate.now() .atStartOfDay(ZoneId.systemDefault()) .toInstant() .toEpochMilli() - mLastModifiedOption.millis .toEpochMilli() - lastModifiedOption.millis ) } return bundle Loading @@ -175,7 +172,7 @@ class SearchOptionsController(private val mContainer: View?) { private fun getFileTypeQueryArgs(): Bundle { val bundle = Bundle() val mimeTypes = when (mFileTypeOption) { val mimeTypes = when (fileTypeOption) { FileTypeOption.AUDIO -> SearchChipViewManager.AUDIO_MIMETYPES FileTypeOption.DOCUMENTS -> SearchChipViewManager.DOCUMENTS_MIMETYPES FileTypeOption.IMAGES -> SearchChipViewManager.IMAGES_MIMETYPES Loading @@ -202,14 +199,14 @@ class SearchOptionsController(private val mContainer: View?) { * Sets the visibility of the search drop down options bar. */ fun setVisible(visible: Boolean) { mContainer?.visibility = if (visible) View.VISIBLE else View.GONE container?.visibility = if (visible) View.VISIBLE else View.GONE } /** * Returns whether or not this controller is visible. */ fun isVisible(): Boolean { return mContainer?.visibility == View.VISIBLE return container?.visibility == View.VISIBLE } fun showMenu(chip: Chip, @MenuRes menuRes: Int, callback: (option: Int) -> Unit) { Loading
tests/unit/com/android/documentsui/queries/SearchOptionsControllerTest.kt +24 −24 Original line number Diff line number Diff line Loading @@ -24,7 +24,7 @@ import androidx.test.filters.SmallTest import androidx.test.platform.app.InstrumentationRegistry import com.android.documentsui.flags.Flags.FLAG_USE_SEARCH_V2_READ_ONLY import com.android.documentsui.rules.CheckAndForceMaterial3Flag import junit.framework.Assert.assertEquals import org.junit.Assert.assertEquals import org.junit.Before import org.junit.Rule import org.junit.Test Loading @@ -32,10 +32,10 @@ import org.junit.runner.RunWith import org.mockito.Mockito.spy class TestSearchOptionsListener() : SearchOptionsListener { var mOptionsState: SearchOptionsState? = null var optionsState: SearchOptionsState? = null override fun onOptionsChanged(options: SearchOptionsState) { mOptionsState = options optionsState = options } } Loading @@ -46,46 +46,46 @@ class SearchOptionsControllerTest { @get:Rule val checkFlags = CheckAndForceMaterial3Flag() var mContext: Context? = null var mController: SearchOptionsController? = null var mContainer: LinearLayout? = null val mOptionsListener = TestSearchOptionsListener() var context: Context? = null var controller: SearchOptionsController? = null var container: LinearLayout? = null val optionsListener = TestSearchOptionsListener() @Before fun setUp() { mContext = InstrumentationRegistry.getInstrumentation().targetContext mContainer = spy(LinearLayout(mContext)) mController = SearchOptionsController(mContainer) mController!!.setOptionChangeListener(mOptionsListener) context = InstrumentationRegistry.getInstrumentation().targetContext container = spy(LinearLayout(context)) controller = SearchOptionsController(container) controller!!.setOptionChangeListener(optionsListener) } @Test fun testOptionsUpdateWorks() { for (e in SearchLocationOption.entries) { mController!!.onLocationSelected(e.value) mController!!.notifyOptionsChangeListener() assertEquals(mOptionsListener.mOptionsState!!.location, e) controller!!.onLocationSelected(e.value) controller!!.notifyOptionsChangeListener() assertEquals(optionsListener.optionsState!!.location, e) } for (e in LastModifiedOption.entries) { mController!!.onLastModifiedSelected(e.value) mController!!.notifyOptionsChangeListener() assertEquals(mOptionsListener.mOptionsState!!.lastModified, e) controller!!.onLastModifiedSelected(e.value) controller!!.notifyOptionsChangeListener() assertEquals(optionsListener.optionsState!!.lastModified, e) } for (e in FileTypeOption.entries) { mController!!.onFileTypeSelected(e.value) mController!!.notifyOptionsChangeListener() assertEquals(mOptionsListener.mOptionsState!!.fileType, e) controller!!.onFileTypeSelected(e.value) controller!!.notifyOptionsChangeListener() assertEquals(optionsListener.optionsState!!.fileType, e) } } @Test fun testGetOptionsQueryArgs() { // Reset the options to minimum filtering state. mController!!.onLocationSelected(SearchLocationOption.EVERYWHERE.ordinal) mController!!.onLastModifiedSelected(LastModifiedOption.ANY_TIME.ordinal) mController!!.onFileTypeSelected(FileTypeOption.ANY_TYPE.ordinal) controller!!.onLocationSelected(SearchLocationOption.EVERYWHERE.ordinal) controller!!.onLastModifiedSelected(LastModifiedOption.ANY_TIME.ordinal) controller!!.onFileTypeSelected(FileTypeOption.ANY_TYPE.ordinal) val queryArgs = mController!!.getOptionsQueryArgs() val queryArgs = controller!!.getOptionsQueryArgs() // Expect no query args with the default (no limits) settings. assertEquals(queryArgs.size, 0) } Loading