diff --git a/app/src/androidTest/java/com/aws/amazonlocation/ConstantTest.kt b/app/src/androidTest/java/com/aws/amazonlocation/ConstantTest.kt index d34eba4a..2202e6ef 100644 --- a/app/src/androidTest/java/com/aws/amazonlocation/ConstantTest.kt +++ b/app/src/androidTest/java/com/aws/amazonlocation/ConstantTest.kt @@ -32,6 +32,7 @@ const val TEST_WORD_RIO_TINTO = "Rio Tinto" const val TEST_WORD_SHYAMAL = "Shyamal Cross" const val TEST_WORD_SCHOOL = "School" const val TEST_WORD_SHYAMAL_CROSS_ROAD = "Shyamal Cross Road" +const val TEST_WORD_TALWALKERS_SHYAMAL_CROSS_ROAD = "Talwalkers Shyamal Cross Road" const val TEST_WORD_DOMINO_PIZZA_VEJALPUR = "dominos Jivraj Park Cross Road, Vejalpur Police Chowky, Vejalpur" const val TEST_WORD_DOMINO_PIZZA = "Domino's" const val TEST_WORD_AUBURN_SYDNEY = "auburn sydney" @@ -78,6 +79,7 @@ const val NESTED_SCROLL_ERROR = "Nested scroll error" const val TEST_FAILED_SEARCH_FIELD_NOT_VISIBLE = "Test failed due to search field not visible" const val TEST_FAILED_EXIT_BUTTON_NOT_VISIBLE = "Test failed due to exit button not visible" const val TEST_FAILED_DISTANCE_OR_TIME_EMPTY = "Test failed due to distance or time empty" +const val TEST_FAILED_DISTANCE_EMPTY = "Test failed due to distance is empty" const val TEST_FAILED_ZOOM_LEVEL_NOT_CHANGED = "Test failed due to zoom level not changed" const val TEST_FAILED_INVALID_IDENTITY_POOL_ID = "Test failed due to invalid identity pool id" const val TEST_FAILED_LOCATION_COMPONENT_NOT_ACTIVATED_OR_ENABLED = "Test failed due to location component not activated or enabled" diff --git a/app/src/androidTest/java/com/aws/amazonlocation/ui/MapLoadAndPlaceSearchFlowSuite.kt b/app/src/androidTest/java/com/aws/amazonlocation/ui/MapLoadAndPlaceSearchFlowSuite.kt index 93297bca..2e48bdc4 100644 --- a/app/src/androidTest/java/com/aws/amazonlocation/ui/MapLoadAndPlaceSearchFlowSuite.kt +++ b/app/src/androidTest/java/com/aws/amazonlocation/ui/MapLoadAndPlaceSearchFlowSuite.kt @@ -11,6 +11,7 @@ import com.aws.amazonlocation.ui.main.ExploreFragmentMapZoomInOutTest import com.aws.amazonlocation.ui.main.ExploreFragmentMaxZoomInOutTest import com.aws.amazonlocation.ui.main.ExploreFragmentSearchByCategoriesTest import com.aws.amazonlocation.ui.main.ExploreFragmentSearchCollapseTest +import com.aws.amazonlocation.ui.main.ExploreFragmentSearchDistanceTest import com.aws.amazonlocation.ui.main.ExploreFragmentSearchExistsTest import com.aws.amazonlocation.ui.main.ExploreFragmentSearchGeocodeReversedTest import com.aws.amazonlocation.ui.main.SearchContactInfoPOICardTest @@ -32,6 +33,7 @@ import org.junit.runners.Suite ExploreFragmentSearchCollapseTest::class, ExploreFragmentSearchExistsTest::class, ExploreFragmentSearchGeocodeReversedTest::class, + ExploreFragmentSearchDistanceTest::class, SearchContactInfoPOICardTest::class ) class MapLoadAndPlaceSearchFlowSuite diff --git a/app/src/androidTest/java/com/aws/amazonlocation/ui/main/ExploreFragmentSearchDistanceTest.kt b/app/src/androidTest/java/com/aws/amazonlocation/ui/main/ExploreFragmentSearchDistanceTest.kt new file mode 100644 index 00000000..d7475156 --- /dev/null +++ b/app/src/androidTest/java/com/aws/amazonlocation/ui/main/ExploreFragmentSearchDistanceTest.kt @@ -0,0 +1,83 @@ +package com.aws.amazonlocation.ui.main + +import androidx.test.core.app.ApplicationProvider +import androidx.test.espresso.Espresso.onView +import androidx.test.espresso.action.ViewActions.click +import androidx.test.espresso.action.ViewActions.replaceText +import androidx.test.espresso.assertion.ViewAssertions.matches +import androidx.test.espresso.matcher.ViewMatchers.hasMinimumChildCount +import androidx.test.espresso.matcher.ViewMatchers.isDisplayed +import androidx.test.espresso.matcher.ViewMatchers.withId +import androidx.test.platform.app.InstrumentationRegistry.getInstrumentation +import androidx.test.uiautomator.By +import androidx.test.uiautomator.UiDevice +import androidx.test.uiautomator.UiSelector +import androidx.test.uiautomator.Until +import com.aws.amazonlocation.ALLOW +import com.aws.amazonlocation.AMAZON_MAP_READY +import com.aws.amazonlocation.BaseTestMainActivity +import com.aws.amazonlocation.BuildConfig +import com.aws.amazonlocation.DELAY_15000 +import com.aws.amazonlocation.R +import com.aws.amazonlocation.TEST_FAILED +import com.aws.amazonlocation.TEST_FAILED_DISTANCE_EMPTY +import com.aws.amazonlocation.TEST_WORD_TALWALKERS_SHYAMAL_CROSS_ROAD +import com.aws.amazonlocation.WHILE_USING_THE_APP +import com.aws.amazonlocation.WHILE_USING_THE_APP_ALLOW +import com.aws.amazonlocation.WHILE_USING_THE_APP_CAPS +import com.aws.amazonlocation.actions.swipeLeft +import com.aws.amazonlocation.di.AppModule +import com.aws.amazonlocation.enableGPS +import com.aws.amazonlocation.failTest +import com.aws.amazonlocation.waitForView +import dagger.hilt.android.testing.HiltAndroidTest +import dagger.hilt.android.testing.UninstallModules +import org.hamcrest.CoreMatchers.allOf +import org.junit.Assert +import org.junit.Test + +@UninstallModules(AppModule::class) +@HiltAndroidTest +class ExploreFragmentSearchDistanceTest : BaseTestMainActivity() { + private val uiDevice = UiDevice.getInstance(getInstrumentation()) + + @Test + fun testSearchDistanceTest() { + try { + val btnContinueToApp = + uiDevice.findObject(UiSelector().resourceId("${BuildConfig.APPLICATION_ID}:id/btn_continue_to_app")) + if (btnContinueToApp.exists()) { + btnContinueToApp.click() + } + uiDevice.findObject(By.text(WHILE_USING_THE_APP))?.click() + uiDevice.findObject(By.text(WHILE_USING_THE_APP_CAPS))?.click() + uiDevice.findObject(By.text(WHILE_USING_THE_APP_ALLOW))?.click() + uiDevice.findObject(By.text(ALLOW))?.click() + enableGPS(ApplicationProvider.getApplicationContext()) + uiDevice.wait(Until.hasObject(By.desc(AMAZON_MAP_READY)), DELAY_15000) + + onView(withId(R.id.mapView)).perform(swipeLeft()) + + val edtSearch = + onView(withId(R.id.edt_search_places)).check(matches(isDisplayed())) + + edtSearch.perform(click()) + onView(withId(R.id.edt_search_places)).perform( + replaceText( + TEST_WORD_TALWALKERS_SHYAMAL_CROSS_ROAD, + ), + ) + waitForView(allOf(withId(R.id.rv_search_places_suggestion), isDisplayed(), hasMinimumChildCount(1))) + + val tvDistance = uiDevice.findObject(UiSelector().resourceId("${BuildConfig.APPLICATION_ID}:id/tv_distance")) + val distanceValue = tvDistance.text.split(" ")[0].toDouble() + Assert.assertTrue( + TEST_FAILED_DISTANCE_EMPTY, + distanceValue > 0.0 + ) + } catch (e: Exception) { + failTest(71, e) + Assert.fail(TEST_FAILED) + } + } +} diff --git a/app/src/main/java/com/aws/amazonlocation/ui/main/explore/ExploreFragment.kt b/app/src/main/java/com/aws/amazonlocation/ui/main/explore/ExploreFragment.kt index 9e2b0ac3..34f301ef 100644 --- a/app/src/main/java/com/aws/amazonlocation/ui/main/explore/ExploreFragment.kt +++ b/app/src/main/java/com/aws/amazonlocation/ui/main/explore/ExploreFragment.kt @@ -2009,8 +2009,7 @@ class ExploreFragment : cardView: MaterialCardView, cl: ConstraintLayout, ) { - if (source == resources.getString(R.string.label_my_location) - ) { + if (source == resources.getString(R.string.label_my_location)) { mBinding.bottomSheetDirectionSearch.apply { when (cardView) { cardWalkGo -> { @@ -4150,6 +4149,7 @@ class ExploreFragment : SearchPlacesAdapter( mPlaceList, mPreferenceManager, + true, object : SearchPlacesAdapter.SearchPlaceInterface { override fun placeClick(position: Int) { if (checkInternetConnection()) { @@ -4175,6 +4175,7 @@ class ExploreFragment : SearchPlacesSuggestionAdapter( mPlaceList, mPreferenceManager, + true, object : SearchPlacesSuggestionAdapter.SearchPlaceSuggestionInterface { override fun suggestedPlaceClick(position: Int) { if (checkInternetConnection()) { @@ -4196,9 +4197,9 @@ class ExploreFragment : mIsDirectionDataSet = false } if (mPlaceList[position].placeId.isNullOrEmpty() && !mPlaceList[position].queryId.isNullOrEmpty()) { - mPlaceList[position].text?.let { + mPlaceList[position].queryId?.let { mViewModel.searchPlaceIndexForText( - it, + queryId = it ) } } else { @@ -4480,6 +4481,7 @@ class ExploreFragment : SearchPlacesAdapter( mPlaceList, mPreferenceManager, + false, object : SearchPlacesAdapter.SearchPlaceInterface { override fun placeClick(position: Int) { if (checkInternetConnection()) { @@ -4503,6 +4505,7 @@ class ExploreFragment : SearchPlacesSuggestionAdapter( mPlaceList, mPreferenceManager, + false, object : SearchPlacesSuggestionAdapter.SearchPlaceSuggestionInterface { override fun suggestedPlaceClick(position: Int) { if (checkInternetConnection()) { diff --git a/app/src/main/java/com/aws/amazonlocation/ui/main/explore/SearchPlacesAdapter.kt b/app/src/main/java/com/aws/amazonlocation/ui/main/explore/SearchPlacesAdapter.kt index a102487b..18a7ee35 100644 --- a/app/src/main/java/com/aws/amazonlocation/ui/main/explore/SearchPlacesAdapter.kt +++ b/app/src/main/java/com/aws/amazonlocation/ui/main/explore/SearchPlacesAdapter.kt @@ -5,6 +5,7 @@ import android.view.ViewGroup import androidx.recyclerview.widget.RecyclerView import com.aws.amazonlocation.R import com.aws.amazonlocation.data.response.SearchSuggestionData +import com.aws.amazonlocation.databinding.ItemSearchDirectionsBinding import com.aws.amazonlocation.databinding.ItemSearchPlacesBinding import com.aws.amazonlocation.utils.KEY_UNIT_SYSTEM import com.aws.amazonlocation.utils.PreferenceManager @@ -19,9 +20,15 @@ import com.aws.amazonlocation.utils.show class SearchPlacesAdapter( private val mSearchPlaceList: ArrayList, private val preferenceManager: PreferenceManager?, + private var isForDirections: Boolean, var mSearchPlaceInterface: SearchPlaceInterface, ) : - RecyclerView.Adapter() { + RecyclerView.Adapter() { + + companion object { + private const val TYPE_PLACE = 0 + private const val TYPE_DIRECTIONS = 1 + } inner class SearchPlaceVH(private val binding: ItemSearchPlacesBinding) : RecyclerView.ViewHolder(binding.root) { @@ -64,14 +71,65 @@ class SearchPlacesAdapter( } } - override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): SearchPlaceVH { - return SearchPlaceVH( - ItemSearchPlacesBinding.inflate(LayoutInflater.from(parent.context), parent, false), - ) + inner class SearchDirectionsVH(private val binding: ItemSearchDirectionsBinding) : + RecyclerView.ViewHolder(binding.root) { + fun bind(data: SearchSuggestionData) { + binding.apply { + tvPlaceName.text = data.text + binding.apply { + if (data.isPlaceIndexForPosition || data.amazonLocationAddress?.label.isNullOrEmpty()) { + tvPlaceName.text = data.text + tvDescription.hide() + } else { + data.amazonLocationAddress?.label?.split(",")?.let { parts -> + tvPlaceName.text = parts.getOrNull(0) ?: data.text + tvDescription.text = parts.drop(1).joinToString(",").trim() + tvDescription.show() + } + } + + when { + data.placeId.isNullOrEmpty() -> ivSearchLocation.setImageResource(R.drawable.icon_search) + !data.placeId.isNullOrEmpty() -> ivSearchLocation.setImageResource(R.drawable.ic_map_pin) + } + clMain.setOnClickListener { + mSearchPlaceInterface.placeClick(adapterPosition) + } + } + } + } + } + + override fun getItemViewType(position: Int): Int { + return if (isForDirections) TYPE_DIRECTIONS else TYPE_PLACE } - override fun onBindViewHolder(holder: SearchPlaceVH, position: Int) { - holder.bind(mSearchPlaceList[position]) + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder { + return when (viewType) { + TYPE_PLACE -> SearchPlaceVH( + ItemSearchPlacesBinding.inflate( + LayoutInflater.from(parent.context), + parent, + false + ) + ) + TYPE_DIRECTIONS -> SearchDirectionsVH( + ItemSearchDirectionsBinding.inflate( + LayoutInflater.from(parent.context), + parent, + false + ) + ) + else -> throw IllegalArgumentException("Invalid view type") + } + } + + override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { + val data = mSearchPlaceList[position] + when (holder) { + is SearchPlaceVH -> holder.bind(data) + is SearchDirectionsVH -> holder.bind(data) + } } override fun getItemCount() = mSearchPlaceList.size diff --git a/app/src/main/java/com/aws/amazonlocation/ui/main/explore/SearchPlacesSuggestionAdapter.kt b/app/src/main/java/com/aws/amazonlocation/ui/main/explore/SearchPlacesSuggestionAdapter.kt index aa11481a..ae29a7ff 100644 --- a/app/src/main/java/com/aws/amazonlocation/ui/main/explore/SearchPlacesSuggestionAdapter.kt +++ b/app/src/main/java/com/aws/amazonlocation/ui/main/explore/SearchPlacesSuggestionAdapter.kt @@ -5,6 +5,7 @@ import android.view.ViewGroup import androidx.recyclerview.widget.RecyclerView import com.aws.amazonlocation.R import com.aws.amazonlocation.data.response.SearchSuggestionData +import com.aws.amazonlocation.databinding.ItemSearchDirectionsBinding import com.aws.amazonlocation.databinding.ItemSearchPlacesSuggestionBinding import com.aws.amazonlocation.utils.KEY_UNIT_SYSTEM import com.aws.amazonlocation.utils.PreferenceManager @@ -18,9 +19,14 @@ import com.aws.amazonlocation.utils.show class SearchPlacesSuggestionAdapter( private val mSearchPlaceList: ArrayList, private val preferenceManager: PreferenceManager?, + private var isForDirections: Boolean, var mSearchPlaceSuggestionInterface: SearchPlaceSuggestionInterface, -) : - RecyclerView.Adapter() { +) : RecyclerView.Adapter() { + + companion object { + private const val TYPE_PLACE = 0 + private const val TYPE_DIRECTIONS = 1 + } inner class SearchPlaceVH(private val binding: ItemSearchPlacesSuggestionBinding) : RecyclerView.ViewHolder(binding.root) { @@ -69,18 +75,65 @@ class SearchPlacesSuggestionAdapter( } } - override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): SearchPlaceVH { - return SearchPlaceVH( - ItemSearchPlacesSuggestionBinding.inflate( - LayoutInflater.from(parent.context), - parent, - false, - ), - ) + inner class SearchDirectionsVH(private val binding: ItemSearchDirectionsBinding) : + RecyclerView.ViewHolder(binding.root) { + fun bind(data: SearchSuggestionData) { + binding.apply { + tvPlaceName.text = data.text + binding.apply { + if (data.isPlaceIndexForPosition || data.amazonLocationAddress?.label.isNullOrEmpty()) { + tvPlaceName.text = data.text + tvDescription.hide() + } else { + data.amazonLocationAddress?.label?.split(",")?.let { parts -> + tvPlaceName.text = parts.getOrNull(0) ?: data.text + tvDescription.text = parts.drop(1).joinToString(",").trim() + tvDescription.show() + } + } + + when { + data.placeId.isNullOrEmpty() -> ivSearchLocation.setImageResource(R.drawable.icon_search) + !data.placeId.isNullOrEmpty() -> ivSearchLocation.setImageResource(R.drawable.ic_map_pin) + } + clMain.setOnClickListener { + mSearchPlaceSuggestionInterface.suggestedPlaceClick(adapterPosition) + } + } + } + } + } + + override fun getItemViewType(position: Int): Int { + return if (isForDirections) TYPE_DIRECTIONS else TYPE_PLACE } - override fun onBindViewHolder(holder: SearchPlaceVH, position: Int) { - holder.bind(mSearchPlaceList[position]) + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder { + return when (viewType) { + TYPE_PLACE -> SearchPlaceVH( + ItemSearchPlacesSuggestionBinding.inflate( + LayoutInflater.from(parent.context), + parent, + false + ) + ) + TYPE_DIRECTIONS -> SearchDirectionsVH( + ItemSearchDirectionsBinding.inflate( + LayoutInflater.from(parent.context), + parent, + false + ) + ) + else -> throw IllegalArgumentException("Invalid view type") + } + } + + override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { + val data = mSearchPlaceList[position] + when (holder) { + is SearchPlaceVH -> holder.bind(data) + is SearchDirectionsVH -> holder.bind(data) + } } override fun getItemCount() = mSearchPlaceList.size diff --git a/app/src/main/java/com/aws/amazonlocation/ui/main/geofence/GeofenceUtils.kt b/app/src/main/java/com/aws/amazonlocation/ui/main/geofence/GeofenceUtils.kt index 3e132b23..718cf1df 100644 --- a/app/src/main/java/com/aws/amazonlocation/ui/main/geofence/GeofenceUtils.kt +++ b/app/src/main/java/com/aws/amazonlocation/ui/main/geofence/GeofenceUtils.kt @@ -807,6 +807,7 @@ class GeofenceUtils { mGeofenceSearchSuggestionAdapter = SearchPlacesSuggestionAdapter( mPlaceList, preferenceManager, + true, object : SearchPlacesSuggestionAdapter.SearchPlaceSuggestionInterface { override fun suggestedPlaceClick(position: Int) { if (checkInternetConnection()) { @@ -881,6 +882,7 @@ class GeofenceUtils { mSearchPlacesAdapter = SearchPlacesAdapter( mPlaceList, preferenceManager, + true, object : SearchPlacesAdapter.SearchPlaceInterface { override fun placeClick(position: Int) { if (checkInternetConnection()) { diff --git a/app/src/main/java/com/aws/amazonlocation/utils/Constants.kt b/app/src/main/java/com/aws/amazonlocation/utils/Constants.kt index b89630c0..6a70f220 100644 --- a/app/src/main/java/com/aws/amazonlocation/utils/Constants.kt +++ b/app/src/main/java/com/aws/amazonlocation/utils/Constants.kt @@ -12,7 +12,7 @@ const val KEY_BOARD_HEIGHT = 500 const val KEY_USER_DETAILS = "user_details" const val KEY_LOCATION_PERMISSION = "location_permission" const val SEARCH_MAX_RESULT = 15 -const val SEARCH_MAX_SUGGESTION_RESULT = 5 +const val SEARCH_MAX_SUGGESTION_RESULT = 10 const val KEY_CODE = "code" const val SIGN_OUT = "signout" const val SIGN_IN = "signin" diff --git a/app/src/main/res/layout/item_search_directions.xml b/app/src/main/res/layout/item_search_directions.xml new file mode 100644 index 00000000..bda7b48a --- /dev/null +++ b/app/src/main/res/layout/item_search_directions.xml @@ -0,0 +1,54 @@ + + + + + + + + + + + + \ No newline at end of file