From a0abe8ba9f5953726344f6f9979a154875bb0b26 Mon Sep 17 00:00:00 2001 From: wadhawh <130486914+wadhawh@users.noreply.github.com> Date: Wed, 4 Dec 2024 21:30:13 +0530 Subject: [PATCH] ALS-1885 Update place popup to show new information (#96) * fix: reverse geocode and direction button should not work if only data is pulled bug fixes fix: Two bottom sheet opening fixes with code optimization refactor: Auth SDK version update with related code changes ALS-1882 * fix: App should work without no data inside custom.properties file ALS-1884 * feat: Places popup updated with new information ALS-1885 * feat: code optimize ALS-1885 --------- Co-authored-by: shah --- app/build.gradle | 2 +- .../com/aws/amazonlocation/ConstantTest.kt | 3 + .../ui/MapLoadAndPlaceSearchFlowSuite.kt | 2 + .../ui/main/SearchContactInfoPOICardTest.kt | 117 +++++++ .../data/datasource/RemoteDataSource.kt | 9 +- .../data/datasource/RemoteDataSourceImpl.kt | 26 +- .../data/repository/LocationSearchImp.kt | 7 + .../data/response/SearchSuggestionResponse.kt | 6 +- .../domain/interface/PlaceInterface.kt | 16 + .../repository/LocationSearchRepository.kt | 3 + .../domain/usecase/LocationSearchUseCase.kt | 3 + .../amazonlocation/ui/base/BaseActivity.kt | 11 +- .../amazonlocation/ui/main/MainActivity.kt | 103 ++++-- .../ui/main/explore/ExploreFragment.kt | 299 ++++++++++++++---- .../ui/main/explore/ExploreViewModel.kt | 45 +++ .../ui/main/region/RegionFragment.kt | 2 +- .../SimulationBottomSheetFragment.kt | 87 +++-- .../ui/main/simulation/SimulationUtils.kt | 30 +- .../welcome/WelcomeBottomSheetFragment.kt | 2 +- .../amazonlocation/utils/BottomSheetHelper.kt | 180 ++++++----- .../com/aws/amazonlocation/utils/Constants.kt | 21 +- .../aws/amazonlocation/utils/GeneralUtils.kt | 20 +- .../com/aws/amazonlocation/utils/MapHelper.kt | 1 + .../utils/analytics/AnalyticsUtils.kt | 8 +- .../utils/providers/LocationProvider.kt | 193 +++++------ .../utils/providers/PlacesProvider.kt | 70 ++-- .../utils/providers/RoutesProvider.kt | 2 - app/src/main/res/drawable/ic_copy.xml | 9 + app/src/main/res/drawable/ic_phone_number.xml | 9 + app/src/main/res/drawable/ic_place_link.xml | 9 + app/src/main/res/drawable/ic_timing.xml | 9 + .../layout-sw600dp/bottom_sheet_direction.xml | 217 ++++++++++++- .../bottom_sheet_direction_search.xml | 1 + .../layout-sw720dp/bottom_sheet_direction.xml | 217 ++++++++++++- .../bottom_sheet_direction_search.xml | 1 + .../res/layout/bottom_sheet_direction.xml | 217 ++++++++++++- .../layout/bottom_sheet_direction_search.xml | 1 + app/src/main/res/values-ar/string.xml | 7 + app/src/main/res/values-de/string.xml | 7 + app/src/main/res/values-es/string.xml | 7 + app/src/main/res/values-fr/string.xml | 7 + app/src/main/res/values-hi/string.xml | 7 + app/src/main/res/values-it/string.xml | 7 + app/src/main/res/values-iw/string.xml | 7 + app/src/main/res/values-ja/string.xml | 7 + app/src/main/res/values-ko/string.xml | 7 + app/src/main/res/values-pt/string.xml | 7 + app/src/main/res/values-zh-rCN/string.xml | 7 + app/src/main/res/values-zh-rTW/string.xml | 7 + app/src/main/res/values/strings.xml | 8 + .../com/aws/amazonlocation/mock/Responses.kt | 30 +- .../utils/units/UnitsGetApiKeyTest.kt | 45 +++ .../utils/units/UnitsGetRegionTest.kt | 44 +++ .../explore/ExploreVMGetPlaceDetais.kt | 110 +++++++ 54 files changed, 1863 insertions(+), 416 deletions(-) create mode 100644 app/src/androidTest/java/com/aws/amazonlocation/ui/main/SearchContactInfoPOICardTest.kt create mode 100644 app/src/main/java/com/aws/amazonlocation/domain/interface/PlaceInterface.kt create mode 100644 app/src/main/res/drawable/ic_copy.xml create mode 100644 app/src/main/res/drawable/ic_phone_number.xml create mode 100644 app/src/main/res/drawable/ic_place_link.xml create mode 100644 app/src/main/res/drawable/ic_timing.xml create mode 100644 app/src/test/java/com/aws/amazonlocation/utils/units/UnitsGetApiKeyTest.kt create mode 100644 app/src/test/java/com/aws/amazonlocation/utils/units/UnitsGetRegionTest.kt create mode 100644 app/src/test/java/com/aws/amazonlocation/viewmodel/explore/ExploreVMGetPlaceDetais.kt diff --git a/app/build.gradle b/app/build.gradle index 855326e7..b5872a98 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -145,7 +145,7 @@ dependencies { implementation "org.maplibre.gl:android-sdk:11.0.0" implementation "org.maplibre.gl:android-plugin-annotation-v9:3.0.0" - implementation("software.amazon.location:auth:0.2.5") + implementation("software.amazon.location:auth:1.1.0") implementation("com.google.android.gms:play-services-location:21.3.0") implementation "androidx.navigation:navigation-fragment-ktx:2.7.7" diff --git a/app/src/androidTest/java/com/aws/amazonlocation/ConstantTest.kt b/app/src/androidTest/java/com/aws/amazonlocation/ConstantTest.kt index 222b7f50..8eeb32e6 100644 --- a/app/src/androidTest/java/com/aws/amazonlocation/ConstantTest.kt +++ b/app/src/androidTest/java/com/aws/amazonlocation/ConstantTest.kt @@ -32,6 +32,8 @@ 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_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" const val TEST_WORD_MANLY_BEACH_SYDNEY = "manly beach sydney" const val TEST_WORD_CLOVERDALE_PERTH = "cloverdale perth" @@ -86,6 +88,7 @@ const val TEST_FAILED_COUNT_NOT_GREATER_THEN_TWO = "Test failed due to count not const val TEST_FAILED_DRIVE_OR_WALK_OR_TRUCK_OPTION_NOT_VISIBLE = "Test failed due to drive or walk or truck option not visible" const val TEST_FAILED_INVALID_ORIGIN_OR_DESTINATION_TEXT = "Test failed due to invalid origin or destination text" const val TEST_FAILED_DIRECTION_TIME_NOT_VISIBLE = "Test failed due to direction time not visible" +const val TEST_FAILED_PLACE_LINK_NOT_VISIBLE = "Test failed due to place link not visible" const val TEST_FAILED_IMAGE_NULL = "Test failed due to image null" const val TEST_FAILED_NOT_EQUAL = "Test failed due to not equal" const val TEST_FAILED_POOL_ID_NOT_BLANK = "Test failed due to pool id not blank" 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 be034d67..93297bca 100644 --- a/app/src/androidTest/java/com/aws/amazonlocation/ui/MapLoadAndPlaceSearchFlowSuite.kt +++ b/app/src/androidTest/java/com/aws/amazonlocation/ui/MapLoadAndPlaceSearchFlowSuite.kt @@ -13,6 +13,7 @@ import com.aws.amazonlocation.ui.main.ExploreFragmentSearchByCategoriesTest import com.aws.amazonlocation.ui.main.ExploreFragmentSearchCollapseTest import com.aws.amazonlocation.ui.main.ExploreFragmentSearchExistsTest import com.aws.amazonlocation.ui.main.ExploreFragmentSearchGeocodeReversedTest +import com.aws.amazonlocation.ui.main.SearchContactInfoPOICardTest import org.junit.runner.RunWith import org.junit.runners.Suite @@ -31,5 +32,6 @@ import org.junit.runners.Suite ExploreFragmentSearchCollapseTest::class, ExploreFragmentSearchExistsTest::class, ExploreFragmentSearchGeocodeReversedTest::class, + SearchContactInfoPOICardTest::class ) class MapLoadAndPlaceSearchFlowSuite diff --git a/app/src/androidTest/java/com/aws/amazonlocation/ui/main/SearchContactInfoPOICardTest.kt b/app/src/androidTest/java/com/aws/amazonlocation/ui/main/SearchContactInfoPOICardTest.kt new file mode 100644 index 00000000..d0cc83ef --- /dev/null +++ b/app/src/androidTest/java/com/aws/amazonlocation/ui/main/SearchContactInfoPOICardTest.kt @@ -0,0 +1,117 @@ +package com.aws.amazonlocation.ui.main + +import android.view.View +import androidx.appcompat.widget.AppCompatTextView +import androidx.recyclerview.widget.RecyclerView +import androidx.recyclerview.widget.RecyclerView.ViewHolder +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.contrib.RecyclerViewActions +import androidx.test.espresso.matcher.ViewMatchers.hasDescendant +import androidx.test.espresso.matcher.ViewMatchers.isDisplayed +import androidx.test.espresso.matcher.ViewMatchers.withId +import androidx.test.espresso.matcher.ViewMatchers.withText +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.DELAY_2000 +import com.aws.amazonlocation.DELAY_20000 +import com.aws.amazonlocation.R +import com.aws.amazonlocation.TEST_FAILED +import com.aws.amazonlocation.TEST_FAILED_NO_SEARCH_RESULT +import com.aws.amazonlocation.TEST_FAILED_PLACE_LINK_NOT_VISIBLE +import com.aws.amazonlocation.TEST_WORD_DOMINO_PIZZA +import com.aws.amazonlocation.TEST_WORD_DOMINO_PIZZA_VEJALPUR +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.di.AppModule +import com.aws.amazonlocation.enableGPS +import com.aws.amazonlocation.failTest +import com.google.android.material.card.MaterialCardView +import dagger.hilt.android.testing.HiltAndroidTest +import dagger.hilt.android.testing.UninstallModules +import org.junit.Assert +import org.junit.Test + +@UninstallModules(AppModule::class) +@HiltAndroidTest +class SearchContactInfoPOICardTest : BaseTestMainActivity() { + + private val uiDevice = UiDevice.getInstance(getInstrumentation()) + + @Test + fun searchContactInfoPOICardTest() { + try { + val btnContinueToApp = uiDevice.findObject(UiSelector().resourceId("${BuildConfig.APPLICATION_ID}:id/btn_continue_to_app")) + if (btnContinueToApp.exists()) { + btnContinueToApp.click() + Thread.sleep(DELAY_2000) + } + 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() + Thread.sleep(DELAY_2000) + enableGPS(ApplicationProvider.getApplicationContext()) + uiDevice.wait(Until.hasObject(By.desc(AMAZON_MAP_READY)), DELAY_15000) + Thread.sleep(DELAY_2000) + + 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_DOMINO_PIZZA_VEJALPUR)) + uiDevice.wait( + Until.hasObject(By.res("${BuildConfig.APPLICATION_ID}:id/rv_search_places_suggestion")), + DELAY_20000 + ) + getInstrumentation().waitForIdleSync() + val rvSearchPlaceSuggestion = + mActivityRule.activity.findViewById(R.id.rv_search_places_suggestion) + if (rvSearchPlaceSuggestion.adapter?.itemCount != null) { + rvSearchPlaceSuggestion.adapter?.itemCount?.let { + if (it >= 0) { + Thread.sleep(DELAY_2000) + onView(withId(R.id.rv_search_places_suggestion)) + .check(matches(hasDescendant(withText(TEST_WORD_DOMINO_PIZZA)))) + Thread.sleep(DELAY_2000) + onView(withId(R.id.rv_search_places_suggestion)) + .perform(RecyclerViewActions.actionOnItem(hasDescendant(withText(TEST_WORD_DOMINO_PIZZA)), click())) + Thread.sleep(DELAY_2000) + val btnDirection = + mActivityRule.activity.findViewById(R.id.btn_direction) + if (btnDirection.visibility == View.VISIBLE) { + uiDevice.wait( + Until.hasObject(By.res("${BuildConfig.APPLICATION_ID}:id/tv_direction_distance")), + DELAY_20000, + ) + val tvPlaceLink = + mActivityRule.activity.findViewById(R.id.tv_place_link) + Thread.sleep(DELAY_2000) + Assert.assertTrue(TEST_FAILED_PLACE_LINK_NOT_VISIBLE, tvPlaceLink.visibility == View.VISIBLE) + } else { + Assert.fail() + } + } else { + Assert.fail(TEST_FAILED_NO_SEARCH_RESULT) + } + } + } else { + Assert.fail(TEST_FAILED_NO_SEARCH_RESULT) + } + } catch (e: Exception) { + failTest(107, e) + Assert.fail(TEST_FAILED) + } + } +} diff --git a/app/src/main/java/com/aws/amazonlocation/data/datasource/RemoteDataSource.kt b/app/src/main/java/com/aws/amazonlocation/data/datasource/RemoteDataSource.kt index 79c9eabb..7e4df911 100644 --- a/app/src/main/java/com/aws/amazonlocation/data/datasource/RemoteDataSource.kt +++ b/app/src/main/java/com/aws/amazonlocation/data/datasource/RemoteDataSource.kt @@ -1,14 +1,12 @@ package com.aws.amazonlocation.data.datasource -import aws.sdk.kotlin.services.georoutes.model.CalculateRoutesResponse import aws.sdk.kotlin.services.location.model.ListGeofenceResponseEntry -import aws.sdk.kotlin.services.location.model.Step import com.aws.amazonlocation.domain.`interface`.BatchLocationUpdateInterface import com.aws.amazonlocation.domain.`interface`.DistanceInterface import com.aws.amazonlocation.domain.`interface`.GeofenceAPIInterface import com.aws.amazonlocation.domain.`interface`.LocationDeleteHistoryInterface import com.aws.amazonlocation.domain.`interface`.LocationHistoryInterface -import com.aws.amazonlocation.domain.`interface`.NavigationDataInterface +import com.aws.amazonlocation.domain.`interface`.PlaceInterface import com.aws.amazonlocation.domain.`interface`.SearchDataInterface import com.aws.amazonlocation.domain.`interface`.SearchPlaceInterface import com.aws.amazonlocation.domain.`interface`.SignInInterface @@ -102,4 +100,9 @@ interface RemoteDataSource { suspend fun refreshTokensWithOkHttp( signInInterface: SignInInterface ) + + suspend fun getPlace( + placeId: String, + placeInterface: PlaceInterface + ) } diff --git a/app/src/main/java/com/aws/amazonlocation/data/datasource/RemoteDataSourceImpl.kt b/app/src/main/java/com/aws/amazonlocation/data/datasource/RemoteDataSourceImpl.kt index 6f832845..b1b590e5 100644 --- a/app/src/main/java/com/aws/amazonlocation/data/datasource/RemoteDataSourceImpl.kt +++ b/app/src/main/java/com/aws/amazonlocation/data/datasource/RemoteDataSourceImpl.kt @@ -9,6 +9,7 @@ import com.aws.amazonlocation.domain.`interface`.DistanceInterface import com.aws.amazonlocation.domain.`interface`.GeofenceAPIInterface import com.aws.amazonlocation.domain.`interface`.LocationDeleteHistoryInterface import com.aws.amazonlocation.domain.`interface`.LocationHistoryInterface +import com.aws.amazonlocation.domain.`interface`.PlaceInterface import com.aws.amazonlocation.domain.`interface`.SearchDataInterface import com.aws.amazonlocation.domain.`interface`.SearchPlaceInterface import com.aws.amazonlocation.domain.`interface`.SignInInterface @@ -48,8 +49,8 @@ class RemoteDataSourceImpl( if (mContext.isInternetAvailable()) { val mSearchSuggestionResponse = mPlacesProvider.searchPlaceSuggestion(lat, lng, searchText, mLocationProvider.getBaseActivity(), mLocationProvider.getGeoPlacesClient()) - if (validateLatLng(searchText) != null) { - val mLatLng = validateLatLng(searchText) + if (validateLatLng(searchText.trim()) != null) { + val mLatLng = validateLatLng(searchText.trim()) if (mSearchSuggestionResponse.text == (mLatLng?.latitude.toString() + "," + mLatLng?.longitude.toString())) { searchPlace.getSearchPlaceSuggestionResponse(mSearchSuggestionResponse) } else { @@ -267,4 +268,25 @@ class RemoteDataSourceImpl( signInInterface.refreshTokensWithOkHttpFailed("failed") } } + + override suspend fun getPlace(placeId: String, placeInterface: PlaceInterface) { + if (mContext.isInternetAvailable()) { + val placeResponse = mPlacesProvider.getPlace(placeId, mLocationProvider.getBaseActivity(), mLocationProvider.getGeoPlacesClient()) + if (placeResponse != null) { + placeInterface.placeSuccess(placeResponse) + } else { + placeInterface.placeFailed(DataSourceException.Error("")) + } + } else { + placeInterface.internetConnectionError( + if (isRunningRemoteDataSourceImplTest) { + "" + } else { + mContext.resources.getString( + R.string.check_your_internet_connection_and_try_again, + ) + }, + ) + } + } } diff --git a/app/src/main/java/com/aws/amazonlocation/data/repository/LocationSearchImp.kt b/app/src/main/java/com/aws/amazonlocation/data/repository/LocationSearchImp.kt index 7cf06207..e1a93fe7 100644 --- a/app/src/main/java/com/aws/amazonlocation/data/repository/LocationSearchImp.kt +++ b/app/src/main/java/com/aws/amazonlocation/data/repository/LocationSearchImp.kt @@ -1,10 +1,13 @@ package com.aws.amazonlocation.data.repository +import aws.sdk.kotlin.services.geoplaces.GeoPlacesClient import com.aws.amazonlocation.data.datasource.RemoteDataSourceImpl import com.aws.amazonlocation.domain.`interface`.DistanceInterface +import com.aws.amazonlocation.domain.`interface`.PlaceInterface import com.aws.amazonlocation.domain.`interface`.SearchDataInterface import com.aws.amazonlocation.domain.`interface`.SearchPlaceInterface import com.aws.amazonlocation.domain.repository.LocationSearchRepository +import com.aws.amazonlocation.ui.base.BaseActivity // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. @@ -64,4 +67,8 @@ class LocationSearchImp(private val mRemoteDataSource: RemoteDataSourceImpl) : searchPlace ) } + + override suspend fun getPlace(placeId: String, placeInterface: PlaceInterface) { + mRemoteDataSource.getPlace(placeId, placeInterface) + } } diff --git a/app/src/main/java/com/aws/amazonlocation/data/response/SearchSuggestionResponse.kt b/app/src/main/java/com/aws/amazonlocation/data/response/SearchSuggestionResponse.kt index 578dc7f9..2da24ab3 100644 --- a/app/src/main/java/com/aws/amazonlocation/data/response/SearchSuggestionResponse.kt +++ b/app/src/main/java/com/aws/amazonlocation/data/response/SearchSuggestionResponse.kt @@ -1,6 +1,8 @@ package com.aws.amazonlocation.data.response import aws.sdk.kotlin.services.geoplaces.model.Address +import aws.sdk.kotlin.services.geoplaces.model.Contacts +import aws.sdk.kotlin.services.geoplaces.model.OpeningHours // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. @@ -23,5 +25,7 @@ data class SearchSuggestionData( var isPlaceIndexForPosition: Boolean = false, var amazonLocationAddress: Address? = null, var position: List ? = null, - var queryId: String ? = null + var queryId: String ? = null, + var contacts: Contacts? = null, + var openingHours: List? = null ) diff --git a/app/src/main/java/com/aws/amazonlocation/domain/interface/PlaceInterface.kt b/app/src/main/java/com/aws/amazonlocation/domain/interface/PlaceInterface.kt new file mode 100644 index 00000000..9b3d06e5 --- /dev/null +++ b/app/src/main/java/com/aws/amazonlocation/domain/interface/PlaceInterface.kt @@ -0,0 +1,16 @@ +package com.aws.amazonlocation.domain.`interface` + +import aws.sdk.kotlin.services.geoplaces.model.GetPlaceResponse +import com.aws.amazonlocation.data.common.DataSourceException + +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + +// SPDX-License-Identifier: MIT-0 +interface PlaceInterface { + + fun placeSuccess(success: GetPlaceResponse) {} + + fun placeFailed(exception: DataSourceException) {} + + fun internetConnectionError(exception: String) {} +} diff --git a/app/src/main/java/com/aws/amazonlocation/domain/repository/LocationSearchRepository.kt b/app/src/main/java/com/aws/amazonlocation/domain/repository/LocationSearchRepository.kt index 3f13083b..60860d55 100644 --- a/app/src/main/java/com/aws/amazonlocation/domain/repository/LocationSearchRepository.kt +++ b/app/src/main/java/com/aws/amazonlocation/domain/repository/LocationSearchRepository.kt @@ -4,6 +4,7 @@ import aws.sdk.kotlin.services.georoutes.model.CalculateRoutesResponse import aws.sdk.kotlin.services.location.model.Step import com.aws.amazonlocation.domain.`interface`.DistanceInterface import com.aws.amazonlocation.domain.`interface`.NavigationDataInterface +import com.aws.amazonlocation.domain.`interface`.PlaceInterface import com.aws.amazonlocation.domain.`interface`.SearchDataInterface import com.aws.amazonlocation.domain.`interface`.SearchPlaceInterface @@ -43,4 +44,6 @@ interface LocationSearchRepository { lng: Double?, searchPlace: SearchDataInterface ) + + suspend fun getPlace(placeId: String, placeInterface: PlaceInterface) } diff --git a/app/src/main/java/com/aws/amazonlocation/domain/usecase/LocationSearchUseCase.kt b/app/src/main/java/com/aws/amazonlocation/domain/usecase/LocationSearchUseCase.kt index dbf24834..c71c6ae4 100644 --- a/app/src/main/java/com/aws/amazonlocation/domain/usecase/LocationSearchUseCase.kt +++ b/app/src/main/java/com/aws/amazonlocation/domain/usecase/LocationSearchUseCase.kt @@ -4,6 +4,7 @@ import aws.sdk.kotlin.services.georoutes.model.CalculateRoutesResponse import aws.sdk.kotlin.services.location.model.Step import com.aws.amazonlocation.domain.`interface`.DistanceInterface import com.aws.amazonlocation.domain.`interface`.NavigationDataInterface +import com.aws.amazonlocation.domain.`interface`.PlaceInterface import com.aws.amazonlocation.domain.`interface`.SearchDataInterface import com.aws.amazonlocation.domain.`interface`.SearchPlaceInterface import com.aws.amazonlocation.domain.repository.LocationSearchRepository @@ -55,4 +56,6 @@ class LocationSearchUseCase @Inject constructor(private val mLocationSearchRepos lng: Double?, searchPlace: SearchDataInterface ) = mLocationSearchRepository.searPlaceIndexForPosition(lat, lng, searchPlace) + + suspend fun getPlace(placeId: String, placeInterface: PlaceInterface) = mLocationSearchRepository.getPlace(placeId, placeInterface) } diff --git a/app/src/main/java/com/aws/amazonlocation/ui/base/BaseActivity.kt b/app/src/main/java/com/aws/amazonlocation/ui/base/BaseActivity.kt index 98241437..cbb4d218 100644 --- a/app/src/main/java/com/aws/amazonlocation/ui/base/BaseActivity.kt +++ b/app/src/main/java/com/aws/amazonlocation/ui/base/BaseActivity.kt @@ -44,7 +44,6 @@ import dagger.hilt.android.AndroidEntryPoint import kotlinx.coroutines.delay import kotlinx.coroutines.launch import kotlinx.coroutines.runBlocking -import software.amazon.location.auth.AuthHelper import javax.inject.Inject // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. @@ -73,7 +72,6 @@ open class BaseActivity : AppCompatActivity() { lateinit var mLocationProvider: LocationProvider private var subTitle = "" - lateinit var authHelper: AuthHelper val mSignInViewModel: SignInViewModel by viewModels() override fun onCreate(savedInstanceState: Bundle?) { @@ -105,7 +103,6 @@ open class BaseActivity : AppCompatActivity() { mGeofenceBottomSheetHelper = GeofenceBottomSheetHelper(this@BaseActivity) mGeofenceUtils = GeofenceUtils() - authHelper = AuthHelper(applicationContext) val preference = PreferenceManager(applicationContext) mTrackingUtils = TrackingUtils(preference, this@BaseActivity, mLocationProvider) mSimulationUtils = SimulationUtils(preference, this@BaseActivity, mLocationProvider) @@ -127,8 +124,8 @@ open class BaseActivity : AppCompatActivity() { } suspend fun initMobileClient() { - mLocationProvider.initializeLocationCredentialsProvider(authHelper, this) - mLocationProvider.initPlaceRoutesClients() + mLocationProvider.initializeLocationCredentialsProvider(this) + mLocationProvider.initPlaceRoutesClients(this) } private fun locationPermissionDialog() { @@ -253,10 +250,10 @@ open class BaseActivity : AppCompatActivity() { fun restartAppWithClearData() { lifecycleScope.launch { - mLocationProvider.locationCredentialsProvider?.clear() + mLocationProvider.clearCredentials() mPreferenceManager.setDefaultConfig() delay(RESTART_DELAY) restartApplication() } } -} +} \ No newline at end of file diff --git a/app/src/main/java/com/aws/amazonlocation/ui/main/MainActivity.kt b/app/src/main/java/com/aws/amazonlocation/ui/main/MainActivity.kt index 0c92d833..16698f51 100644 --- a/app/src/main/java/com/aws/amazonlocation/ui/main/MainActivity.kt +++ b/app/src/main/java/com/aws/amazonlocation/ui/main/MainActivity.kt @@ -1,6 +1,7 @@ package com.aws.amazonlocation.ui.main import android.annotation.SuppressLint +import android.app.AlertDialog import android.app.Dialog import android.content.pm.ActivityInfo import android.content.res.Configuration @@ -14,7 +15,6 @@ import android.webkit.WebResourceRequest import android.webkit.WebView import android.webkit.WebViewClient import androidx.activity.OnBackPressedCallback -import androidx.activity.result.contract.ActivityResultContracts import androidx.constraintlayout.widget.ConstraintSet import androidx.core.content.ContextCompat import androidx.fragment.app.Fragment @@ -79,22 +79,23 @@ import com.aws.amazonlocation.utils.LANGUAGE_CODE_ARABIC import com.aws.amazonlocation.utils.LANGUAGE_CODE_HEBREW import com.aws.amazonlocation.utils.LANGUAGE_CODE_HEBREW_1 import com.aws.amazonlocation.utils.NetworkConnectivityObserveInterface -import com.aws.amazonlocation.utils.PREFS_KEY_IDENTITY_ID -import com.aws.amazonlocation.utils.PREFS_NAME_AUTH import com.aws.amazonlocation.utils.SETTING_FRAGMENT import com.aws.amazonlocation.utils.SIGN_IN import com.aws.amazonlocation.utils.SIGN_OUT import com.aws.amazonlocation.utils.Units.checkInternetConnection import com.aws.amazonlocation.utils.VERSION_FRAGMENT import com.aws.amazonlocation.utils.analytics.AnalyticsUtils +import com.aws.amazonlocation.utils.analyticsFields import com.aws.amazonlocation.utils.getLanguageCode import com.aws.amazonlocation.utils.hide import com.aws.amazonlocation.utils.hideViews import com.aws.amazonlocation.utils.invisible import com.aws.amazonlocation.utils.makeTransparentStatusBar +import com.aws.amazonlocation.utils.requiredFields import com.aws.amazonlocation.utils.setLocale import com.aws.amazonlocation.utils.show import com.aws.amazonlocation.utils.showViews +import com.aws.amazonlocation.utils.simulationFields import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.async @@ -102,7 +103,6 @@ import kotlinx.coroutines.delay import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.launch -import software.amazon.location.auth.EncryptedSharedPreferences // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. @@ -122,14 +122,7 @@ class MainActivity : private var alertDialog: Dialog? = null private var currentPage: String? = null private var connectivityObserver: ConnectivityObserveInterface? = null - private var encryptedSharedPreferences: EncryptedSharedPreferences? = null var analyticsUtils: AnalyticsUtils? = null - private val mServiceName = "geo" - - var resultLauncher = - registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { - checkMap() - } override fun onConfigurationChanged(newConfig: Configuration) { super.onConfigurationChanged(newConfig) @@ -310,13 +303,6 @@ class MainActivity : } } initClick() - if (encryptedSharedPreferences == null) { - encryptedSharedPreferences = - EncryptedSharedPreferences( - applicationContext, - PREFS_NAME_AUTH, - ).apply { initEncryptedSharedPreferences() } - } KeyBoardUtils.attachKeyboardListeners( mBinding.root, object : KeyBoardUtils.KeyBoardInterface { @@ -425,14 +411,14 @@ class MainActivity : "", ) != AuthEnum.SIGNED_IN.name ) { - mLocationProvider.locationCredentialsProvider?.clear() + mLocationProvider.clearCredentials() } mPreferenceManager.setValue( KEY_CLOUD_FORMATION_STATUS, AuthEnum.SIGNED_IN.name, ) mBottomSheetDialog?.dismiss() - async { mLocationProvider.generateNewAuthCredentials(authHelper) }.await() + async { mLocationProvider.generateNewAuthCredentials() }.await() val fragment = mNavHostFragment.childFragmentManager.fragments[0] getTokenAndAttachPolicy() val propertiesAws = @@ -659,6 +645,7 @@ class MainActivity : var mRegion = mPreferenceManager.getValue(KEY_USER_REGION, "") if (mRegion.isNullOrEmpty()) { + if (BuildConfig.DEFAULT_REGION == "null") return@launch mRegion = BuildConfig.DEFAULT_REGION } val iotClient = @@ -706,7 +693,8 @@ class MainActivity : } private fun setSimulationIotPolicy() { - val identityId = encryptedSharedPreferences?.get(PREFS_KEY_IDENTITY_ID) + val identityId = mLocationProvider.getIdentityId() + if (identityId.isNullOrEmpty()) return CoroutineScope(Dispatchers.IO).launch { val attachPolicyRequest = AttachPolicyRequest { @@ -716,7 +704,7 @@ class MainActivity : val iotClient = IotClient { - region = identityId?.split(":")?.get(0) + region = identityId.split(":")[0] credentialsProvider = createCredentialsProviderForPolicy( mLocationProvider.getCredentials(), @@ -951,7 +939,7 @@ class MainActivity : mBinding.imgAmazonLogo?.setImageResource(logoResId) } - fun setExplorer() { + private fun setExplorer() { val fragment = mNavHostFragment.childFragmentManager.fragments[0] if (fragment !is ExploreFragment) { mNavController.navigate(R.id.explore_fragment) @@ -1015,7 +1003,6 @@ class MainActivity : mTrackingUtils?.hideTrackingBottomSheet() mSimulationUtils?.showSimulationBottomSheet() if (mNavHostFragment.childFragmentManager.fragments.isNotEmpty()) { - val fragment = mNavHostFragment.childFragmentManager.fragments[0] if (fragment is ExploreFragment) { if (isTablet) { fragment.hideDirectionAndCurrentLocationIcon() @@ -1217,7 +1204,64 @@ class MainActivity : } } - fun setWelcomeToExplorer() { + fun checkPropertiesData() { + val missingRequiredFields = requiredFields.filter { it.value == "null" }.keys + val simulationMissingFields = simulationFields.filter { it.value == "null" }.keys + val analyticsMissingFields = analyticsFields.filter { it.value == "null" }.keys + + if (missingRequiredFields.isNotEmpty() || simulationMissingFields.isNotEmpty() || analyticsMissingFields.isNotEmpty()) { + val dialogMessage = buildString { + when { + missingRequiredFields.isNotEmpty() -> { + append(getString(R.string.label_required_fields_missing)) + append("\n") + missingRequiredFields.forEach { append("• $it\n") } + simulationMissingFields.forEach { append("• $it\n") } + analyticsMissingFields.forEach { append("• $it\n") } + } + simulationMissingFields.isNotEmpty() && analyticsMissingFields.isNotEmpty() -> { + append(getString(R.string.label_some_fields_missing)) + append("\n") + simulationMissingFields.forEach { append("• $it\n") } + analyticsMissingFields.forEach { append("• $it\n") } + } + simulationMissingFields.isNotEmpty() -> { + append(getString(R.string.label_simulation_fields_missing)) + append("\n") + simulationMissingFields.forEach { append("• $it\n") } + } + analyticsMissingFields.isNotEmpty() -> { + append(getString(R.string.label_analytics_fields_missing)) + append("\n") + analyticsMissingFields.forEach { append("• $it\n") } + } + } + } + + val dialogTitle = getString(R.string.title_configuration_incomplete) + val positiveButtonText = if (missingRequiredFields.isNotEmpty()) { + getString(R.string.ok) + } else { + getString(R.string.label_continue) + } + + AlertDialog + .Builder(this) + .setTitle(dialogTitle) + .setMessage(dialogMessage) + .setPositiveButton(positiveButtonText) { _, _ -> + if (missingRequiredFields.isNotEmpty()) { + finish() + } else { + setWelcomeToExplorer() + } + }.setCancelable(false).show() + } else { + setWelcomeToExplorer() + } + } + + private fun setWelcomeToExplorer() { mPreferenceManager.setValue(IS_APP_FIRST_TIME_OPENED, true) isAppNotFirstOpened = true val fragment = @@ -1249,14 +1293,15 @@ class MainActivity : } } - fun initClient(isAfterSignOut:Boolean = false){ + fun initClient(isAfterSignOut: Boolean = false) { if (!isAfterSignOut) { try { - mLocationProvider.locationCredentialsProvider?.clear() - } catch (_: Exception) { } + mLocationProvider.clearCredentials() + } catch (_: Exception) { + } } CoroutineScope(Dispatchers.IO).launch { async { initMobileClient() }.await() } } -} +} \ No newline at end of file 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 d3f24fdd..da296348 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 @@ -14,6 +14,11 @@ import android.net.Network import android.net.Uri import android.os.Bundle import android.os.SystemClock +import android.text.SpannableString +import android.text.Spanned +import android.text.TextPaint +import android.text.method.LinkMovementMethod +import android.text.style.ClickableSpan import android.util.TypedValue import android.view.LayoutInflater import android.view.View @@ -78,6 +83,7 @@ import com.aws.amazonlocation.utils.DELAY_300 import com.aws.amazonlocation.utils.DELAY_500 import com.aws.amazonlocation.utils.Distance.DISTANCE_IN_METER_10 import com.aws.amazonlocation.utils.Durations +import com.aws.amazonlocation.utils.Durations.DELAY_FOR_BOTTOM_SHEET_LOAD import com.aws.amazonlocation.utils.EventType import com.aws.amazonlocation.utils.EventType.PLACE_SEARCH import com.aws.amazonlocation.utils.EventType.ROUTE_OPTION_CHANGED @@ -97,8 +103,6 @@ import com.aws.amazonlocation.utils.LANGUAGE_CODE_HEBREW_1 import com.aws.amazonlocation.utils.MAP_STYLE_ATTRIBUTION import com.aws.amazonlocation.utils.MILES import com.aws.amazonlocation.utils.MapHelper -import com.aws.amazonlocation.utils.PREFS_KEY_IDENTITY_ID -import com.aws.amazonlocation.utils.PREFS_NAME_AUTH import com.aws.amazonlocation.utils.STRING_FORMAT import com.aws.amazonlocation.utils.SignOutInterface import com.aws.amazonlocation.utils.SimulationDialogInterface @@ -111,6 +115,7 @@ import com.aws.amazonlocation.utils.Units.isGPSEnabled import com.aws.amazonlocation.utils.Units.isMetric import com.aws.amazonlocation.utils.attributionPattern import com.aws.amazonlocation.utils.checkLocationPermission +import com.aws.amazonlocation.utils.copyTextToClipboard import com.aws.amazonlocation.utils.getLanguageCode import com.aws.amazonlocation.utils.getRegion import com.aws.amazonlocation.utils.getUserName @@ -124,6 +129,7 @@ import com.aws.amazonlocation.utils.isRunningTest3LiveLocation import com.aws.amazonlocation.utils.isRunningTestLiveLocation import com.aws.amazonlocation.utils.locationPermissionDialog import com.aws.amazonlocation.utils.show +import com.aws.amazonlocation.utils.showKeyboard import com.aws.amazonlocation.utils.showViews import com.aws.amazonlocation.utils.simulationExit import com.aws.amazonlocation.utils.textChanges @@ -162,7 +168,6 @@ import org.maplibre.android.maps.MapLibreMap import org.maplibre.android.maps.OnMapReadyCallback import org.maplibre.geojson.Point import org.maplibre.geojson.Point.fromLngLat -import software.amazon.location.auth.EncryptedSharedPreferences // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. @@ -211,7 +216,6 @@ class ExploreFragment : private var mTravelMode: String = RouteTravelMode.Car.value private var mRouteFinish: Boolean = false private var mRedirectionType: String? = null - private var encryptedSharedPreferences: EncryptedSharedPreferences?= null private var gpsActivityResult = registerForActivityResult( @@ -726,10 +730,7 @@ class ExploreFragment : collectionName: String, position1: List?, ) { - if (encryptedSharedPreferences == null) { - encryptedSharedPreferences = EncryptedSharedPreferences(requireContext(), PREFS_NAME_AUTH).apply { initEncryptedSharedPreferences() } - } - val identityId = encryptedSharedPreferences?.get(PREFS_KEY_IDENTITY_ID) + val identityId = mLocationProvider.getIdentityId() identityId?.let { mSimulationViewModel.evaluateGeofence( collectionName, @@ -1143,9 +1144,10 @@ class ExploreFragment : } } else { bottomSheetDirectionSearch.apply { - clSearchLoaderDirectionSearch.root.show() + layoutNoDataFound.root.hide() rvSearchPlacesSuggestionDirection.hide() rvSearchPlacesDirection.hide() + clSearchLoaderDirectionSearch.root.show() } } }.onSuccess { @@ -1207,6 +1209,8 @@ class ExploreFragment : bottomSheetDirection.apply { groupDistanceLoad.show() groupDistance.invisible() + btnDirection.alpha = 0.5F + btnDirection.isEnabled = false } } } else { @@ -1218,6 +1222,8 @@ class ExploreFragment : if (!mBottomSheetHelper.isDirectionSearchSheetVisible()) { bottomSheetDirection.apply { groupDistanceLoad.hide() + btnDirection.alpha = 1.0F + btnDirection.isEnabled = true groupDistance.show() tvDirectionError.hide() tvDirectionError2.hide() @@ -1515,6 +1521,8 @@ class ExploreFragment : if (!mBottomSheetHelper.isDirectionSearchSheetVisible()) { bottomSheetDirection.apply { groupDistanceLoad.hide() + btnDirection.isEnabled = true + btnDirection.alpha = 1.0F groupDistance.show() tvDirectionTime.hide() tvDirectionDot.invisible() @@ -1801,11 +1809,11 @@ class ExploreFragment : mViewModel.addressLineData.collect { handleResult -> handleResult .onLoading {} - .onSuccess { + .onSuccess { response -> if (!mBottomSheetHelper.isSearchPlaceSheetVisible() || mBottomSheetHelper.isDirectionSheetVisible()) { val searchSuggestionData = SearchSuggestionData() - if (!it.reverseGeocodeResponse?.resultItems.isNullOrEmpty()) { - it.reverseGeocodeResponse?.let { searchPlaceIndexForPositionResult -> + if (!response.reverseGeocodeResponse?.resultItems.isNullOrEmpty()) { + response.reverseGeocodeResponse?.let { searchPlaceIndexForPositionResult -> searchSuggestionData.text = searchPlaceIndexForPositionResult.resultItems?.get(0)?.title searchSuggestionData.searchText = @@ -1816,8 +1824,8 @@ class ExploreFragment : searchSuggestionData.placeId = searchPlaceIndexForPositionResult.resultItems?.get(0)?.placeId searchSuggestionData.isPlaceIndexForPosition = false - it.latitude?.let { lat-> - it.longitude?.let { lng-> + response.latitude?.let { lat-> + response.longitude?.let { lng-> searchSuggestionData.position = listOf(lng, lat) } } @@ -1826,30 +1834,30 @@ class ExploreFragment : } } else { searchSuggestionData.text = - String.format(STRING_FORMAT, it.latitude, it.longitude) + String.format(STRING_FORMAT, response.latitude, response.longitude) searchSuggestionData.searchText = - String.format(STRING_FORMAT, it.latitude, it.longitude) + String.format(STRING_FORMAT, response.latitude, response.longitude) searchSuggestionData.distance = null searchSuggestionData.isDestination = true - searchSuggestionData.placeId = "11" + searchSuggestionData.placeId = null searchSuggestionData.isPlaceIndexForPosition = false - it.latitude?.let { lat-> - it.longitude?.let { lng-> + response.latitude?.let { lat-> + response.longitude?.let { lng-> searchSuggestionData.position = listOf(lng, lat) } } val place = Address { - label = String.format(STRING_FORMAT, it.latitude, it.longitude) - addressNumber = String.format(STRING_FORMAT, it.latitude, it.longitude) - street = String.format(STRING_FORMAT, it.latitude, it.longitude) - postalCode = String.format(STRING_FORMAT, it.latitude, it.longitude) + label = String.format(STRING_FORMAT, response.latitude, response.longitude) + addressNumber = String.format(STRING_FORMAT, response.latitude, response.longitude) + street = String.format(STRING_FORMAT, response.latitude, response.longitude) + postalCode = String.format(STRING_FORMAT, response.latitude, response.longitude) } searchSuggestionData.amazonLocationAddress = place } setDirectionData(searchSuggestionData, true) return@onSuccess } - it.reverseGeocodeResponse?.let { searchPlaceIndexForPositionResult -> + response.reverseGeocodeResponse?.let { searchPlaceIndexForPositionResult -> if (searchPlaceIndexForPositionResult.resultItems?.isNotEmpty() == true) { val label = searchPlaceIndexForPositionResult.resultItems?.get(0)?.title if (label != null) { @@ -1869,6 +1877,53 @@ class ExploreFragment : } } } + + lifecycleScope.launch { + withStarted { } + mViewModel.placeData.collect { handleResult -> + handleResult + .onLoading { + if (mBottomSheetHelper.isDirectionSheetVisible()) { + mBinding.bottomSheetDirection.apply { + groupPlaceDetailsLoad.show() + viewDivider.show() + hideDirectionData() + } + } + } + .onSuccess { response -> + if (mBottomSheetHelper.isDirectionSheetVisible()) { + mViewModel.mSearchSuggestionData?.let { + it.contacts = response.contacts + it.openingHours = response.openingHours + } + mViewModel.mSearchDirectionDestinationData?.let { + it.contacts = response.contacts + it.openingHours = response.openingHours + } + if (response.contacts == null && response.openingHours == null) { + mBinding.bottomSheetDirection.apply { + viewDivider.hide() + hideDirectionData() + } + } + mBinding.bottomSheetDirection.apply { + groupPlaceDetailsLoad.hide() + } + mViewModel.mSearchSuggestionData?.let { setContactPlaceData(it) } + return@onSuccess + } + }.onError { + if (mBottomSheetHelper.isDirectionSheetVisible()) { + mBinding.bottomSheetDirection.apply { + groupPlaceDetailsLoad.hide() + viewDivider.hide() + hideDirectionData() + } + } + } + } + } } private fun showCalculateRouteAPIError(value: String) { @@ -2000,12 +2055,12 @@ class ExploreFragment : val mText = mBinding.bottomSheetSearch.edtSearchPlaces.text .toString() - if (validateLatLng(mText) != null) { - val mLatLng = validateLatLng(mText) + if (validateLatLng(mText.trim()) != null) { + val mLatLng = validateLatLng(mText.trim()) if (it.text == (mLatLng?.latitude.toString() + "," + mLatLng?.longitude.toString())) { setPlaceData(it, searchPlaceIndexText) } - } else if (!it.text.isNullOrEmpty() && it.text == mText) { + } else if (!it.text.isNullOrEmpty()) { setPlaceData(it, searchPlaceIndexText) } mBinding.apply { @@ -2046,7 +2101,7 @@ class ExploreFragment : it: SearchSuggestionResponse, searchPlaceIndexText: SearchApiEnum, ) { - val mText: String = + var mText: String = if (!isDataSearchForDestination) { mBinding.bottomSheetDirectionSearch.edtSearchDirection.text .toString() @@ -2056,6 +2111,10 @@ class ExploreFragment : .toString() .trim() } + if (validateLatLng(mText) != null) { + val mLatLng = validateLatLng(mText) + mText = mLatLng?.latitude.toString() + "," + mLatLng?.longitude.toString() + } if (!it.text.isNullOrEmpty() && it.text .toString() @@ -2111,7 +2170,7 @@ class ExploreFragment : if (mPlaceList.isNotEmpty()) { clNoInternetConnectionDirectionSearch.hide() nsDirectionSearchPlaces.show() - layoutNoDataFound.groupNoSearchFound.hide() + layoutNoDataFound.root.hide() layoutCardError.groupCardErrorNoSearchFound.hide() when (searchPlaceIndexText) { SearchApiEnum.SEARCH_PLACE_INDEX_TEXT -> { @@ -2126,7 +2185,7 @@ class ExploreFragment : } } else { hideViews(rvSearchPlacesDirection, nsDirectionSearchPlaces) - layoutNoDataFound.groupNoSearchFound.show() + layoutNoDataFound.root.show() } } } @@ -2211,7 +2270,17 @@ class ExploreFragment : private fun clickListener() { mBinding.apply { cardGeofenceMap.setOnClickListener { - (activity as MainActivity).geofenceClick() + lifecycleScope.launch { + if (mBottomSheetHelper.isDirectionSearchSheetVisible()) { + bottomSheetDirectionSearch.ivDirectionCloseDirectionSearch.performClick() + } + if (mBottomSheetHelper.isDirectionSheetVisible()) { + bottomSheetDirection.ivDirectionCloseDirection.performClick() + } + (activity as MainActivity).geofenceClick() + delay(DELAY_FOR_BOTTOM_SHEET_LOAD) + mBottomSheetHelper.hideSearchBottomSheet(true) + } } cardNavigation.setOnClickListener { @@ -2806,8 +2875,7 @@ class ExploreFragment : lifecycleScope.launch { activity?.hideKeyboard() delay(DELAY_300) - mBinding.bottomSheetSearch.clSearchLoaderSearchSheet.root - .hide() + mBinding.bottomSheetSearch.clSearchLoaderSearchSheet.root.hide() mMapHelper.addLiveLocationMarker(false) mBottomSheetHelper.hideDirectionSearchBottomSheet(this@ExploreFragment) hideDirectionBottomSheet() @@ -2816,24 +2884,28 @@ class ExploreFragment : mBinding.bottomSheetDirection.apply { btnDirection.setOnClickListener { - if (checkInternetConnection()) { - if (activity?.checkLocationPermission() == true) { - if (isGPSEnabled(requireContext())) { - if (mViewModel.mCarData?.routes?.get(0)?.legs != null) { - routeOption() - } else { - openDirectionWithError() - } - } else { - mViewModel.mCarData = null - openDirectionWithError() - } - } else { - mViewModel.mCarData = null - openDirectionWithError() - } + if (!checkInternetConnection()) return@setOnClickListener + if (cardLoaderSheet1.isVisible) return@setOnClickListener + if (activity?.checkLocationPermission() != true) { + mViewModel.mCarData = null + openDirectionWithError() + return@setOnClickListener + } + if (!isGPSEnabled(requireContext())) { + mViewModel.mCarData = null + openDirectionWithError() + return@setOnClickListener + } + if (mViewModel.mCarData?.routes?.get(0)?.legs == null) { + openDirectionWithError() + } else { + routeOption() } } + ivArrow.setOnClickListener { + tvScheduleDetails.visibility = if (tvScheduleDetails.isVisible) View.GONE else View.VISIBLE + if (ivArrow.rotation == 0F) ivArrow.rotation = 180F else ivArrow.rotation = 0F + } ivInfo.setOnClickListener { if (tvDirectionError2.isVisible) { if (tvDirectionError2.text.equals(getString(R.string.label_location_permission_denied))) { @@ -2841,6 +2913,10 @@ class ExploreFragment : } } } + ivCopyAddress.setOnClickListener { + copyTextToClipboard(requireContext(), sheetDirectionTvDirectionStreet.text.toString()) + showError(getString(R.string.label_copied_to_clipboard)) + } } bottomSheetAddGeofence.edtAddGeofenceSearch @@ -3104,6 +3180,7 @@ class ExploreFragment : mIsAvoidFerries = mPreferenceManager.getValue(KEY_AVOID_FERRIES, false) cardDirection.hide() bottomSheetDirectionSearch.clSearchLoaderDirectionSearch.root.hide() + bottomSheetDirectionSearch.layoutNoDataFound.root.hide() bottomSheetSearch.edtSearchPlaces.setText("") bottomSheetSearch.edtSearchPlaces.clearFocus() mBaseActivity?.bottomNavigationVisibility(false) @@ -3136,6 +3213,10 @@ class ExploreFragment : mPlaceList.clear() mAdapterDirection?.notifyDataSetChanged() mSearchPlacesDirectionSuggestionAdapter?.notifyDataSetChanged() + if (!edtSearchDest.hasFocus()) { + edtSearchDirection.requestFocus() + } + mBaseActivity?.showKeyboard() } mBottomSheetHelper.expandDirectionSearchSheet(this@ExploreFragment) mIsDirectionSheetHalfExpanded = false @@ -3902,12 +3983,14 @@ class ExploreFragment : tvTruckSelected, tvWalkSelected, layoutCardError.root, - layoutNoDataFound.groupNoSearchFound, + layoutNoDataFound.root, ) } mBinding.bottomSheetDirection.apply { tvDirectionError.invisible() + groupDistanceLoad.show() } + hideDirectionData() showViews( mBinding.cardMap, ) @@ -4384,6 +4467,10 @@ class ExploreFragment : edtSearchPlaces.clearFocus() edtSearchPlaces.setText("") } + mBottomSheetHelper.hideSearchBottomSheet(true) + mBottomSheetHelper.isSearchSheetOpen = false + mBaseActivity?.bottomNavigationVisibility(false) + mBottomSheetHelper.expandDirectionSheet() mViewModel.mSearchSuggestionData = data mViewModel.mSearchDirectionDestinationData = data mViewModel.mSearchDirectionDestinationData?.isDestination = true @@ -4395,6 +4482,15 @@ class ExploreFragment : tvDirectionError2.hide() val liveLocationLatLng = mMapHelper.getLiveLocation() isCalculateDriveApiError = false + if (data.placeId != null) { + data.placeId?.let { + groupPlaceDetailsLoad.show() + mViewModel.getPlaceData(it) + } + } else { + groupPlaceDetailsLoad.hide() + setContactPlaceData(data) + } mViewModel.calculateDistance( latitude = liveLocationLatLng?.latitude, longitude = liveLocationLatLng?.longitude, @@ -4436,9 +4532,6 @@ class ExploreFragment : } } notifyAdapters() - mBottomSheetHelper.hideSearchBottomSheet(true) - mBaseActivity?.bottomNavigationVisibility(false) - mBottomSheetHelper.expandDirectionSheet() } mMapHelper.clearMarker() mMapHelper.addDirectionMarker( @@ -4450,6 +4543,86 @@ class ExploreFragment : } } + private fun setContactPlaceData(data: SearchSuggestionData) { + mBinding.bottomSheetDirection.apply { + data.contacts?.let { + if (!it.phones.isNullOrEmpty()) { + tvPhone.movementMethod = LinkMovementMethod.getInstance() + showViews(tvPhone, ivPhone, viewDivider) + it.phones!!.forEachIndexed { index, phones -> + val spannableString = SpannableString(phones.value) + val color = + ContextCompat.getColor(requireContext(), R.color.color_medium_black) + spannableString.setSpan(object : ClickableSpan() { + override fun onClick(widget: View) { + startActivity( + Intent( + Intent.ACTION_DIAL, + Uri.parse("tel:${phones.value}"), + ), + ) + } + + override fun updateDrawState(ds: TextPaint) { + super.updateDrawState(ds) + ds.color = color + ds.isUnderlineText = true + } + }, 0, spannableString.length, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE) + tvPhone.append(spannableString) + if (index != it.phones!!.lastIndex) { + tvPhone.append("\n") + } + } + } + if (!it.websites.isNullOrEmpty()) { + tvPlaceLink.movementMethod = LinkMovementMethod.getInstance() + showViews(tvPlaceLink, ivPlaceLink, viewDivider) + it.websites!!.forEachIndexed { index, website -> + val spannableString = SpannableString(website.value) + val color = + ContextCompat.getColor(requireContext(), R.color.color_primary_green) + spannableString.setSpan(object : ClickableSpan() { + override fun onClick(widget: View) { + startActivity( + Intent( + Intent.ACTION_VIEW, + Uri.parse(website.value), + ), + ) + } + + override fun updateDrawState(ds: TextPaint) { + super.updateDrawState(ds) + ds.color = color + ds.isUnderlineText = false + } + }, 0, spannableString.length, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE) + + tvPlaceLink.append(spannableString) + + if (index != it.websites!!.lastIndex) { + tvPlaceLink.append("\n") + } + } + } + } + data.openingHours?.let { + if (!data.openingHours.isNullOrEmpty()) { + showViews(tvSchedule, ivSchedule, ivArrow, viewDivider) + data.openingHours!!.forEachIndexed { index, openingHour -> + openingHour.display?.forEachIndexed { displayIndex, displayText -> + tvScheduleDetails.append(displayText) + if (index != data.openingHours!!.lastIndex || displayIndex != openingHour.display!!.lastIndex) { + tvScheduleDetails.append("\n") + } + } + } + } + } + } + } + // clear markers from map and clear places list @SuppressLint("NotifyDataSetChanged") private fun notifyAdapters() { @@ -4824,6 +4997,7 @@ class ExploreFragment : MarkerEnum.DIRECTION_ICON, "", ) + hideDirectionData() mViewModel.getAddressLineFromLatLng(point.longitude, point.latitude) val isMetric = isMetric(mPreferenceManager.getValue(KEY_UNIT_SYSTEM, "")) val properties = listOf( @@ -4842,6 +5016,25 @@ class ExploreFragment : return false } + private fun hideDirectionData() { + mBinding.bottomSheetDirection.apply { + tvPhone.text = "" + tvPlaceLink.text = "" + tvScheduleDetails.text = "" + ivArrow.rotation = 0F + hideViews( + tvPhone, + ivPhone, + tvPlaceLink, + ivPlaceLink, + tvSchedule, + tvScheduleDetails, + ivSchedule, + ivArrow + ) + } + } + override fun mapLoadedSuccess() { mBinding.mapView.contentDescription = "Amazon Map Ready" mBinding.groupMapLoad.hide() diff --git a/app/src/main/java/com/aws/amazonlocation/ui/main/explore/ExploreViewModel.kt b/app/src/main/java/com/aws/amazonlocation/ui/main/explore/ExploreViewModel.kt index 1eed8749..9b317dc9 100644 --- a/app/src/main/java/com/aws/amazonlocation/ui/main/explore/ExploreViewModel.kt +++ b/app/src/main/java/com/aws/amazonlocation/ui/main/explore/ExploreViewModel.kt @@ -3,6 +3,7 @@ package com.aws.amazonlocation.ui.main.explore import android.content.Context import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope +import aws.sdk.kotlin.services.geoplaces.model.GetPlaceResponse import aws.sdk.kotlin.services.geoplaces.model.ReverseGeocodeResponse import aws.sdk.kotlin.services.georoutes.model.CalculateRoutesResponse import aws.sdk.kotlin.services.georoutes.model.RouteTravelMode @@ -19,6 +20,7 @@ import com.aws.amazonlocation.data.response.SearchResponse import com.aws.amazonlocation.data.response.SearchSuggestionData import com.aws.amazonlocation.data.response.SearchSuggestionResponse import com.aws.amazonlocation.domain.`interface`.DistanceInterface +import com.aws.amazonlocation.domain.`interface`.PlaceInterface import com.aws.amazonlocation.domain.`interface`.SearchDataInterface import com.aws.amazonlocation.domain.`interface`.SearchPlaceInterface import com.aws.amazonlocation.domain.usecase.LocationSearchUseCase @@ -94,6 +96,11 @@ class ExploreViewModel val addressLineData: Flow> = _addressLineData.receiveAsFlow() + private val _placeData = + Channel>(Channel.BUFFERED) + val placeData: Flow> = + _placeData.receiveAsFlow() + fun searchPlaceSuggestion( searchText: String, ) { @@ -459,6 +466,44 @@ class ExploreViewModel } } + fun getPlaceData( + placeId: String + ) { + _placeData.trySend(HandleResult.Loading) + viewModelScope.launch(Dispatchers.IO) { + getLocationSearchUseCase.getPlace( + placeId, + object : PlaceInterface { + override fun placeSuccess(success: GetPlaceResponse) { + _placeData.trySend( + HandleResult.Success( + success, + ), + ) + } + + override fun placeFailed(exception: DataSourceException) { + _placeData.trySend( + HandleResult.Error( + exception + ), + ) + } + + override fun internetConnectionError(exception: String) { + _placeData.trySend( + HandleResult.Error( + DataSourceException.Error( + exception, + ), + ), + ) + } + }, + ) + } + } + fun setMapListData(context: Context) { val items = arrayListOf( diff --git a/app/src/main/java/com/aws/amazonlocation/ui/main/region/RegionFragment.kt b/app/src/main/java/com/aws/amazonlocation/ui/main/region/RegionFragment.kt index e52bdced..1e6a706e 100644 --- a/app/src/main/java/com/aws/amazonlocation/ui/main/region/RegionFragment.kt +++ b/app/src/main/java/com/aws/amazonlocation/ui/main/region/RegionFragment.kt @@ -72,7 +72,7 @@ class RegionFragment : BaseFragment() { ) } adapter.notifyItemRangeChanged(0, mRegionList.size) - mLocationProvider.locationCredentialsProvider?.clear() + mLocationProvider.clearCredentials() lifecycleScope.launch { if (!isRunningTest) { delay(RESTART_DELAY) // Need delay for preference manager to set default config before restarting diff --git a/app/src/main/java/com/aws/amazonlocation/ui/main/simulation/SimulationBottomSheetFragment.kt b/app/src/main/java/com/aws/amazonlocation/ui/main/simulation/SimulationBottomSheetFragment.kt index 7e2644d8..9a65def9 100644 --- a/app/src/main/java/com/aws/amazonlocation/ui/main/simulation/SimulationBottomSheetFragment.kt +++ b/app/src/main/java/com/aws/amazonlocation/ui/main/simulation/SimulationBottomSheetFragment.kt @@ -2,6 +2,7 @@ package com.aws.amazonlocation.ui.main.simulation import android.Manifest.permission.POST_NOTIFICATIONS import android.annotation.SuppressLint +import android.app.AlertDialog import android.app.Dialog import android.content.Context import android.content.DialogInterface @@ -27,6 +28,7 @@ import com.aws.amazonlocation.utils.AnalyticsAttributeValue import com.aws.amazonlocation.utils.EventType import com.aws.amazonlocation.utils.NotificationDialogInterface import com.aws.amazonlocation.utils.notificationPermission +import com.aws.amazonlocation.utils.simulationFields import com.google.android.material.bottomsheet.BottomSheetBehavior import com.google.android.material.bottomsheet.BottomSheetDialog import com.google.android.material.bottomsheet.BottomSheetDialogFragment @@ -99,36 +101,28 @@ class SimulationBottomSheetFragment : BottomSheetDialogFragment() { private fun clickListener() { mBinding.apply { btnStartSimulation.setOnClickListener { - when { - ContextCompat.checkSelfPermission( - requireContext(), - POST_NOTIFICATIONS - ) == PackageManager.PERMISSION_GRANTED -> { - openSimulation() - } - shouldShowRequestPermissionRationale(POST_NOTIFICATIONS) -> { - if (!NotificationManagerCompat.from(requireContext()).areNotificationsEnabled()) { - requireContext().notificationPermission(object : NotificationDialogInterface { - override fun onOkClick(dialog: DialogInterface) { - openAppNotificationSettings(requireContext()) - } - - override fun onCancelClick(dialog: DialogInterface) { - openSimulation() - } - }) - } - } - else -> { - // The registered ActivityResultCallback gets the result of this request - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { - requestPermissionLauncher.launch( - POST_NOTIFICATIONS - ) - } else { - openSimulation() + val simulationMissingFields = simulationFields.filter { it.value == "null" }.keys + + if (simulationMissingFields.isNotEmpty()) { + val dialogMessage = + buildString { + append(getString(R.string.label_simulation_fields_missing)) + append("\n") + simulationMissingFields.forEach { append("• $it\n") } } - } + + val dialogTitle: String = getString(R.string.title_configuration_incomplete) + val positiveButtonText: String = getString(R.string.ok) + + AlertDialog + .Builder(activity) + .setTitle(dialogTitle) + .setMessage(dialogMessage) + .setPositiveButton(positiveButtonText) { dialog, _ -> + dialog.dismiss() + }.setCancelable(false).show() + } else { + startSimulation() } } tvMaybeLater.setOnClickListener { @@ -140,6 +134,41 @@ class SimulationBottomSheetFragment : BottomSheetDialogFragment() { } } + private fun startSimulation() { + when { + ContextCompat.checkSelfPermission( + requireContext(), + POST_NOTIFICATIONS + ) == PackageManager.PERMISSION_GRANTED -> { + openSimulation() + } + + shouldShowRequestPermissionRationale(POST_NOTIFICATIONS) -> { + if (!NotificationManagerCompat.from(requireContext()).areNotificationsEnabled()) { + requireContext().notificationPermission(object : NotificationDialogInterface { + override fun onOkClick(dialog: DialogInterface) { + openAppNotificationSettings(requireContext()) + } + + override fun onCancelClick(dialog: DialogInterface) { + openSimulation() + } + }) + } + } + + else -> { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { + requestPermissionLauncher.launch( + POST_NOTIFICATIONS + ) + } else { + openSimulation() + } + } + } + } + @SuppressLint("ObsoleteSdkInt") private fun openAppNotificationSettings(context: Context) { val intent = Intent() diff --git a/app/src/main/java/com/aws/amazonlocation/ui/main/simulation/SimulationUtils.kt b/app/src/main/java/com/aws/amazonlocation/ui/main/simulation/SimulationUtils.kt index c5aa8f7e..d8f0f092 100644 --- a/app/src/main/java/com/aws/amazonlocation/ui/main/simulation/SimulationUtils.kt +++ b/app/src/main/java/com/aws/amazonlocation/ui/main/simulation/SimulationUtils.kt @@ -50,8 +50,6 @@ import com.aws.amazonlocation.utils.MQTT_CONNECT_TIME_OUT import com.aws.amazonlocation.utils.MapCameraZoom.SIMULATION_CAMERA_ZOOM_1 import com.aws.amazonlocation.utils.MapHelper import com.aws.amazonlocation.utils.NotificationHelper -import com.aws.amazonlocation.utils.PREFS_KEY_IDENTITY_ID -import com.aws.amazonlocation.utils.PREFS_NAME_AUTH import com.aws.amazonlocation.utils.PreferenceManager import com.aws.amazonlocation.utils.SOURCE import com.aws.amazonlocation.utils.SOURCE_SIMULATION_ICON @@ -94,7 +92,6 @@ import org.maplibre.android.style.sources.GeoJsonSource import org.maplibre.geojson.LineString import org.maplibre.geojson.Point import org.maplibre.geojson.Polygon -import software.amazon.location.auth.EncryptedSharedPreferences // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. @@ -134,7 +131,6 @@ class SimulationUtils( private var mPreDrawTrackerLine = MutableList(notificationData.size) { mutableListOf() } private var busStopCounts: MutableList? = null private var notificationHelper: NotificationHelper? = null - private var encryptedSharedPreferences: EncryptedSharedPreferences?= null fun setMapBox( activity: Activity, @@ -564,14 +560,6 @@ class SimulationUtils( } }) - mFragmentActivity?.let { - if (encryptedSharedPreferences == null) { - encryptedSharedPreferences = EncryptedSharedPreferences( - it.applicationContext, - PREFS_NAME_AUTH - ).apply { initEncryptedSharedPreferences() } - } - } initClick() initAdapter() setSpinnerData() @@ -798,7 +786,7 @@ class SimulationUtils( } private fun stopMqttManager() { - val identityId = encryptedSharedPreferences?.get(PREFS_KEY_IDENTITY_ID) + val identityId = mLocationProvider.getIdentityId() mIsLocationUpdateEnable = false if (mqttClient != null) { try { @@ -823,15 +811,7 @@ class SimulationUtils( private fun startMqttManager() { if (mqttClient != null) stopMqttManager() - mActivity?.let { - if (encryptedSharedPreferences == null) { - encryptedSharedPreferences = EncryptedSharedPreferences( - it.applicationContext, - PREFS_NAME_AUTH - ).apply { initEncryptedSharedPreferences() } - } - } - val identityId = encryptedSharedPreferences?.get(PREFS_KEY_IDENTITY_ID) + val identityId = mLocationProvider.getIdentityId() val defaultIdentityPoolId: String = Units.getDefaultIdentityPoolId( mPreferenceManager?.getValue( KEY_SELECTED_REGION, @@ -839,9 +819,11 @@ class SimulationUtils( ), mPreferenceManager?.getValue(KEY_NEAREST_REGION, "") ) - + if (defaultIdentityPoolId == "null") return val credentials = createCredentialsProvider(mLocationProvider.getCredentials()) - mqttClient = AWSIotMqttClient(getSimulationWebSocketUrl(defaultIdentityPoolId), identityId, credentials, defaultIdentityPoolId.split(":")[0]) + val webSocketUrl = getSimulationWebSocketUrl(defaultIdentityPoolId) + if (webSocketUrl == "null") return + mqttClient = AWSIotMqttClient(webSocketUrl, identityId, credentials, defaultIdentityPoolId.split(":")[0]) try { mqttClient?.connect(MQTT_CONNECT_TIME_OUT, false) diff --git a/app/src/main/java/com/aws/amazonlocation/ui/main/welcome/WelcomeBottomSheetFragment.kt b/app/src/main/java/com/aws/amazonlocation/ui/main/welcome/WelcomeBottomSheetFragment.kt index 02be645b..dae0c09c 100644 --- a/app/src/main/java/com/aws/amazonlocation/ui/main/welcome/WelcomeBottomSheetFragment.kt +++ b/app/src/main/java/com/aws/amazonlocation/ui/main/welcome/WelcomeBottomSheetFragment.kt @@ -90,7 +90,7 @@ class WelcomeBottomSheetFragment : BottomSheetDialogFragment() { private fun clickListener() { mBinding.apply { btnContinueToApp.setOnClickListener { - (activity as MainActivity).setWelcomeToExplorer() + (activity as MainActivity).checkPropertiesData() dialog.dismiss() } } diff --git a/app/src/main/java/com/aws/amazonlocation/utils/BottomSheetHelper.kt b/app/src/main/java/com/aws/amazonlocation/utils/BottomSheetHelper.kt index 1f5fb01f..4e6682fb 100644 --- a/app/src/main/java/com/aws/amazonlocation/utils/BottomSheetHelper.kt +++ b/app/src/main/java/com/aws/amazonlocation/utils/BottomSheetHelper.kt @@ -50,48 +50,48 @@ class BottomSheetHelper { activity.hideKeyboard() mBottomSheetSearchPlaces.addBottomSheetCallback(object : - BottomSheetBehavior.BottomSheetCallback() { - override fun onStateChanged(bottomSheet: View, newState: Int) { - when (newState) { - BottomSheetBehavior.STATE_COLLAPSED -> { - mBaseActivity?.bottomNavigationVisibility(true) - isSearchSheetOpen = false - activity.hideKeyboard() - fragment.clearKeyboardFocus() - view.imgAmazonLogoSearchSheet.alpha = 1f - view.ivAmazonInfoSearchSheet.alpha = 1f - view.tvSearchCancel?.hide() - } - BottomSheetBehavior.STATE_EXPANDED -> { - mBaseActivity?.bottomNavigationVisibility(false) - view.imgAmazonLogoSearchSheet.alpha = 0f - view.ivAmazonInfoSearchSheet.alpha = 0f - isSearchSheetOpen = true - view.tvSearchCancel?.show() - view.edtSearchPlaces.requestFocus() - activity.showKeyboard() - } - BottomSheetBehavior.STATE_DRAGGING -> { - } - BottomSheetBehavior.STATE_HALF_EXPANDED -> { - mBaseActivity?.bottomNavigationVisibility(false) - view.imgAmazonLogoSearchSheet.alpha = 1f - view.ivAmazonInfoSearchSheet.alpha = 1f - view.tvSearchCancel?.show() - isSearchSheetOpen = true - activity.hideKeyboard() - fragment.clearKeyboardFocus() - } - BottomSheetBehavior.STATE_HIDDEN -> { - view.tvSearchCancel?.hide() - } - BottomSheetBehavior.STATE_SETTLING -> {} + BottomSheetBehavior.BottomSheetCallback() { + override fun onStateChanged(bottomSheet: View, newState: Int) { + when (newState) { + BottomSheetBehavior.STATE_COLLAPSED -> { + mBaseActivity?.bottomNavigationVisibility(true) + isSearchSheetOpen = false + activity.hideKeyboard() + fragment.clearKeyboardFocus() + view.imgAmazonLogoSearchSheet.alpha = 1f + view.ivAmazonInfoSearchSheet.alpha = 1f + view.tvSearchCancel?.hide() } + BottomSheetBehavior.STATE_EXPANDED -> { + mBaseActivity?.bottomNavigationVisibility(false) + view.imgAmazonLogoSearchSheet.alpha = 0f + view.ivAmazonInfoSearchSheet.alpha = 0f + isSearchSheetOpen = true + view.tvSearchCancel?.show() + view.edtSearchPlaces.requestFocus() + activity.showKeyboard() + } + BottomSheetBehavior.STATE_DRAGGING -> { + } + BottomSheetBehavior.STATE_HALF_EXPANDED -> { + mBaseActivity?.bottomNavigationVisibility(false) + view.imgAmazonLogoSearchSheet.alpha = 1f + view.ivAmazonInfoSearchSheet.alpha = 1f + view.tvSearchCancel?.show() + isSearchSheetOpen = true + activity.hideKeyboard() + fragment.clearKeyboardFocus() + } + BottomSheetBehavior.STATE_HIDDEN -> { + view.tvSearchCancel?.hide() + } + BottomSheetBehavior.STATE_SETTLING -> {} } + } - override fun onSlide(bottomSheet: View, slideOffset: Float) { - } - }) + override fun onSlide(bottomSheet: View, slideOffset: Float) { + } + }) } fun isSearchBottomSheetExpandedOrHalfExpand(): Boolean { @@ -150,40 +150,36 @@ class BottomSheetHelper { } } mBottomSheetDirectionsSearch.addBottomSheetCallback(object : - BottomSheetBehavior.BottomSheetCallback() { - override fun onStateChanged(bottomSheet: View, newState: Int) { - when (newState) { - BottomSheetBehavior.STATE_COLLAPSED -> { - mBaseActivity?.bottomNavigationVisibility(false) - setBottomSheetDirectionSearchData(view, mBaseActivity) - } - BottomSheetBehavior.STATE_EXPANDED -> { - mBaseActivity?.bottomNavigationVisibility(false) - view.imgAmazonLogoDirectionSearchSheet.alpha = 0f - view.ivAmazonInfoDirectionSearchSheet.alpha = 0f - if (!view.edtSearchDest.hasFocus()) { - view.edtSearchDirection.requestFocus() - } - mBaseActivity?.showKeyboard() - } - BottomSheetBehavior.STATE_DRAGGING -> { - } - BottomSheetBehavior.STATE_HALF_EXPANDED -> { - mBaseActivity?.bottomNavigationVisibility(false) - exploreFragment.changeDirectionCardMargin(175.px) - mBottomSheetDirectionsSearch.isHideable = false - setBottomSheetDirectionSearchData(view, mBaseActivity) - } - BottomSheetBehavior.STATE_HIDDEN -> { - } - BottomSheetBehavior.STATE_SETTLING -> { - } + BottomSheetBehavior.BottomSheetCallback() { + override fun onStateChanged(bottomSheet: View, newState: Int) { + when (newState) { + BottomSheetBehavior.STATE_COLLAPSED -> { + mBaseActivity?.bottomNavigationVisibility(false) + setBottomSheetDirectionSearchData(view, mBaseActivity) + } + BottomSheetBehavior.STATE_EXPANDED -> { + mBaseActivity?.bottomNavigationVisibility(false) + view.imgAmazonLogoDirectionSearchSheet.alpha = 0f + view.ivAmazonInfoDirectionSearchSheet.alpha = 0f + } + BottomSheetBehavior.STATE_DRAGGING -> { + } + BottomSheetBehavior.STATE_HALF_EXPANDED -> { + mBaseActivity?.bottomNavigationVisibility(false) + exploreFragment.changeDirectionCardMargin(175.px) + mBottomSheetDirectionsSearch.isHideable = false + setBottomSheetDirectionSearchData(view, mBaseActivity) + } + BottomSheetBehavior.STATE_HIDDEN -> { + } + BottomSheetBehavior.STATE_SETTLING -> { } } + } - override fun onSlide(bottomSheet: View, slideOffset: Float) { - } - }) + override fun onSlide(bottomSheet: View, slideOffset: Float) { + } + }) } private fun setBottomSheetDirectionSearchData( @@ -209,30 +205,30 @@ class BottomSheetHelper { view.clNavigationParent.context.resources.getDimension(R.dimen.dp_50).toInt() mNavigationBottomSheet.addBottomSheetCallback(object : - BottomSheetBehavior.BottomSheetCallback() { - override fun onStateChanged(bottomSheet: View, newState: Int) { - when (newState) { - BottomSheetBehavior.STATE_COLLAPSED -> { - view.cardNavigationLocation.alpha = 1f - } - BottomSheetBehavior.STATE_EXPANDED -> { - view.cardNavigationLocation.alpha = 0f - } - BottomSheetBehavior.STATE_DRAGGING -> { - } - BottomSheetBehavior.STATE_HALF_EXPANDED -> { - view.cardNavigationLocation.alpha = 1f - } - BottomSheetBehavior.STATE_HIDDEN -> { - } - BottomSheetBehavior.STATE_SETTLING -> { - } + BottomSheetBehavior.BottomSheetCallback() { + override fun onStateChanged(bottomSheet: View, newState: Int) { + when (newState) { + BottomSheetBehavior.STATE_COLLAPSED -> { + view.cardNavigationLocation.alpha = 1f + } + BottomSheetBehavior.STATE_EXPANDED -> { + view.cardNavigationLocation.alpha = 0f + } + BottomSheetBehavior.STATE_DRAGGING -> { + } + BottomSheetBehavior.STATE_HALF_EXPANDED -> { + view.cardNavigationLocation.alpha = 1f + } + BottomSheetBehavior.STATE_HIDDEN -> { + } + BottomSheetBehavior.STATE_SETTLING -> { } } + } - override fun onSlide(bottomSheet: View, slideOffset: Float) { - } - }) + override fun onSlide(bottomSheet: View, slideOffset: Float) { + } + }) } // set direction bottom sheet @@ -374,4 +370,4 @@ class BottomSheetHelper { mBottomSheetAttribution.state = BottomSheetBehavior.STATE_HIDDEN mBottomSheetAttribution.isFitToContents = false } -} +} \ No newline at end of file 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 ac86a009..73ed95d9 100644 --- a/app/src/main/java/com/aws/amazonlocation/utils/Constants.kt +++ b/app/src/main/java/com/aws/amazonlocation/utils/Constants.kt @@ -84,7 +84,6 @@ const val CHANNEL_NAME = "simulation Notification Channel" const val GROUP_KEY_WORK_SIMULATION = BuildConfig.APPLICATION_ID + "SIMULATION" const val STRING_REPLACE_KEY = "**" const val PREFS_NAME_AUTH = "software.amazon.location.auth" -const val PREFS_KEY_IDENTITY_ID = "identityId" const val DEFAULT_COUNTRY = "US" const val KEY_ID_TOKEN = "key_id_token" @@ -162,6 +161,7 @@ const val GEOFENCE_NAME_REG_EXP = "^[-._\\p{L}\\p{N}]+\$" const val userPoolIdPattern = "[\\w-]+_[0-9a-zA-Z]+" const val userPoolClientId = "[\\w+]+" const val regionPattern = "^[a-zA-Z-]+-\\d+$" +const val LAT_LNG_REGEX_PATTERN = "^[-+]?([1-8]?\\d(\\.\\d+)?|90(\\.0+)?),\\s*[-+]?(180(\\.0+)?|((1[0-7]\\d)|([1-9]?\\d))(\\.\\d+)?)$" const val LOCATION_AWS_PREFIX = "location.aws.com.demo." const val LOCATION_TRACKERS_PREFIX = "trackers." @@ -195,6 +195,7 @@ object Durations { const val CAMERA_RIGHT_PADDING = 180 const val DEFAULT_RADIUS = 80 const val DELAY_FOR_FRAGMENT_LOAD = 500L + const val DELAY_FOR_BOTTOM_SHEET_LOAD = 500L } object GeofenceCons { @@ -248,6 +249,24 @@ val notificationData = arrayListOf( NotificationData("Bus 05 UBC", false) ) +val requiredFields = mapOf( + "API_KEY_EU_CENTRAL" to BuildConfig.API_KEY_EU_CENTRAL, + "API_KEY_US_EAST" to BuildConfig.API_KEY_US_EAST +) + +val simulationFields = mapOf( + "DEFAULT_IDENTITY_POOL_ID" to BuildConfig.DEFAULT_IDENTITY_POOL_ID, + "DEFAULT_IDENTITY_POOL_ID_EU" to BuildConfig.DEFAULT_IDENTITY_POOL_ID_EU, + "DEFAULT_REGION" to BuildConfig.DEFAULT_REGION, + "SIMULATION_WEB_SOCKET_URL" to BuildConfig.SIMULATION_WEB_SOCKET_URL, + "SIMULATION_WEB_SOCKET_URL_EU" to BuildConfig.SIMULATION_WEB_SOCKET_URL_EU, +) + +val analyticsFields = mapOf( + "ANALYTICS_IDENTITY_POOL_ID" to BuildConfig.ANALYTICS_IDENTITY_POOL_ID, + "ANALYTICS_APP_ID" to BuildConfig.ANALYTICS_APP_ID +) + object EventType { const val SCREEN_OPEN = "SCREEN_OPEN" const val SCREEN_CLOSE = "SCREEN_CLOSE" diff --git a/app/src/main/java/com/aws/amazonlocation/utils/GeneralUtils.kt b/app/src/main/java/com/aws/amazonlocation/utils/GeneralUtils.kt index 7e8a6eb1..3e5c1ee8 100644 --- a/app/src/main/java/com/aws/amazonlocation/utils/GeneralUtils.kt +++ b/app/src/main/java/com/aws/amazonlocation/utils/GeneralUtils.kt @@ -1,6 +1,8 @@ package com.aws.amazonlocation.utils import android.app.Activity +import android.content.ClipData +import android.content.ClipboardManager import android.content.Context import android.content.Intent import android.content.res.Configuration @@ -226,19 +228,11 @@ fun Activity.hideKeyboard() { imm.hideSoftInputFromWindow(view.windowToken, 0) } -fun checkLatLngValid(latitude: Double?, longitude: Double?): Boolean { - return latitude?.toInt() in -90 until 90 && longitude?.toInt() in -180 until 180 -} - fun validateLatLng(searchText: String): LatLng? { - val pattern = Pattern.compile(LAT_LNG_REG_EXP) + val pattern = Pattern.compile(LAT_LNG_REGEX_PATTERN) return if (pattern.matcher(searchText).matches()) { val latLng = searchText.split(",").toTypedArray() - if (checkLatLngValid(latLng[0].toDouble(), latLng[1].toDouble())) { - LatLng(latLng[0].toDouble(), latLng[1].toDouble()) - } else { - null - } + LatLng(latLng[0].trim().toDouble(), latLng[1].trim().toDouble()) } else { null } @@ -611,4 +605,10 @@ fun hideKeyboard(activity: Activity, appCompatEditText: AppCompatEditText) { val imm: InputMethodManager = activity.getSystemService(Activity.INPUT_METHOD_SERVICE) as InputMethodManager imm.hideSoftInputFromWindow(appCompatEditText.windowToken, 0) +} + +fun copyTextToClipboard(context: Context, text: String) { + val clipboard = context.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager + val clip = ClipData.newPlainText("label", text) + clipboard.setPrimaryClip(clip) } \ No newline at end of file diff --git a/app/src/main/java/com/aws/amazonlocation/utils/MapHelper.kt b/app/src/main/java/com/aws/amazonlocation/utils/MapHelper.kt index 7f9583b2..bec967ce 100644 --- a/app/src/main/java/com/aws/amazonlocation/utils/MapHelper.kt +++ b/app/src/main/java/com/aws/amazonlocation/utils/MapHelper.kt @@ -175,6 +175,7 @@ class MapHelper( ), mPreferenceManager?.getValue(KEY_NEAREST_REGION, ""), ) + if (defaultIdentityPoolId == "null") return region = defaultIdentityPoolId.split(":")[0] } } diff --git a/app/src/main/java/com/aws/amazonlocation/utils/analytics/AnalyticsUtils.kt b/app/src/main/java/com/aws/amazonlocation/utils/analytics/AnalyticsUtils.kt index 7653517a..455aa00f 100644 --- a/app/src/main/java/com/aws/amazonlocation/utils/analytics/AnalyticsUtils.kt +++ b/app/src/main/java/com/aws/amazonlocation/utils/analytics/AnalyticsUtils.kt @@ -45,12 +45,12 @@ class AnalyticsUtils( suspend fun initAnalytics() { credentialProvider = mLocationProvider.getAnalyticsCredentialProvider() - credentialProvider?.let { + if (BuildConfig.ANALYTICS_IDENTITY_POOL_ID != "null" || credentialProvider != null) { val region = BuildConfig.ANALYTICS_IDENTITY_POOL_ID.split(":")[0] pinpointClient = PinpointClient { this.region = region - credentialsProvider = it + credentialsProvider = credentialProvider } if (endpointId.isEmpty()) { endpointId = UUID.randomUUID().toString() @@ -89,8 +89,9 @@ class AnalyticsUtils( event: String, properties: List> = emptyList(), ) { + if (BuildConfig.ANALYTICS_APP_ID == "null") return CoroutineScope(Dispatchers.IO).launch { - if (!mLocationProvider.isAnalyticsCredentialsValid()) { + if (!mLocationProvider.isUnAuthCredentialsValid(true)) { runBlocking { initAnalytics() } } val events: List = @@ -211,6 +212,7 @@ class AnalyticsUtils( } suspend fun startSession() { + if (BuildConfig.ANALYTICS_APP_ID == "null") return session.creationStatus = AnalyticsSessionStatus.IN_PROGRESS runBlocking { createOrUpdateEndpoint() } session.id = UUID.randomUUID().toString() diff --git a/app/src/main/java/com/aws/amazonlocation/utils/providers/LocationProvider.kt b/app/src/main/java/com/aws/amazonlocation/utils/providers/LocationProvider.kt index 081db223..242890a6 100644 --- a/app/src/main/java/com/aws/amazonlocation/utils/providers/LocationProvider.kt +++ b/app/src/main/java/com/aws/amazonlocation/utils/providers/LocationProvider.kt @@ -9,7 +9,6 @@ import aws.sdk.kotlin.services.georoutes.GeoRoutesClient import aws.sdk.kotlin.services.location.LocationClient import aws.smithy.kotlin.runtime.auth.awscredentials.Credentials import aws.smithy.kotlin.runtime.auth.awscredentials.CredentialsProvider -import aws.smithy.kotlin.runtime.net.url.Url import aws.smithy.kotlin.runtime.time.Instant import aws.smithy.kotlin.runtime.time.epochMilliseconds import aws.smithy.kotlin.runtime.time.fromEpochMilliseconds @@ -58,7 +57,6 @@ import okhttp3.OkHttpClient import okhttp3.Request import okhttp3.Response import software.amazon.location.auth.AuthHelper -import software.amazon.location.auth.LocationCredentialsProvider // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. @@ -69,7 +67,6 @@ class LocationProvider( private var mIdentityId: String? = null private var region: String? = null private var locationClient: LocationClient? = null - var locationCredentialsProvider: LocationCredentialsProvider? = null private var credentials: aws.sdk.kotlin.services.cognitoidentity.model.Credentials? = null private var mBaseActivity: BaseActivity? = null private var cognitoIdentityClient: CognitoIdentityClient? = null @@ -77,31 +74,31 @@ class LocationProvider( private var getRoutesClient: GeoRoutesClient?= null private var getPlaceClient: GeoPlacesClient?= null - fun initPlaceRoutesClients() { + suspend fun initPlaceRoutesClients(baseActivity: BaseActivity) { val mRegion = Units.getRegion(mPreferenceManager) + val apiKey = Units.getApiKey(mPreferenceManager) + if (apiKey == "null") return + val credentialProvider = + CoroutineScope(Dispatchers.Main) + .async { + AuthHelper.withApiKey(apiKey, mRegion, baseActivity.applicationContext) + }.await() if (getRoutesClient == null) { getRoutesClient = - GeoRoutesClient { - region = mRegion - credentialsProvider = createEmptyCredentialsProvider() - } + GeoRoutesClient(credentialProvider.getGeoRoutesClientConfig()) + } if (getPlaceClient== null) { getPlaceClient = - GeoPlacesClient { - region = mRegion - credentialsProvider = createEmptyCredentialsProvider() - } + GeoPlacesClient(credentialProvider.getGeoPlacesClientConfig()) } + mBaseActivity = baseActivity } - suspend fun initializeLocationCredentialsProvider( - authHelper: AuthHelper, - baseActivity: BaseActivity, - ) { + suspend fun initializeLocationCredentialsProvider(baseActivity: BaseActivity) { val mAuthStatus = mPreferenceManager.getValue(KEY_CLOUD_FORMATION_STATUS, "") if (mAuthStatus == AuthEnum.SIGNED_IN.name) { - initializeAuthLocationCredentialsProvider(authHelper, baseActivity) + initializeAuthLocationCredentialsProvider(baseActivity) } else { var defaultIdentityPoolId: String = getDefaultIdentityPoolId( @@ -117,20 +114,22 @@ class LocationProvider( "", ).toString() } + if (defaultIdentityPoolId == "null") return val defaultRegion = defaultIdentityPoolId.split(":")[0] region = defaultRegion - locationCredentialsProvider = - CoroutineScope(Dispatchers.Main) - .async { - authHelper.authenticateWithCognitoIdentityPool(defaultIdentityPoolId) - }.await() - locationClient = locationCredentialsProvider?.getLocationClient() + region?.let { + val credentialProvider = + CoroutineScope(Dispatchers.Main) + .async { + generateUnAuthCredentials(it, defaultIdentityPoolId, false) + }.await() + locationClient = credentialProvider?.let { it1 -> generateLocationClient(it, it1) } + } mBaseActivity = baseActivity } } private suspend fun initializeAuthLocationCredentialsProvider( - authHelper: AuthHelper, baseActivity: BaseActivity, ) { mBaseActivity = baseActivity @@ -145,7 +144,7 @@ class LocationProvider( expiration == 0L || isAuthTokenExpired() ) { - generateNewAuthCredentials(authHelper) + generateNewAuthCredentials() } else { region = mPreferenceManager.getValue(KEY_USER_REGION, "").toString() credentials = @@ -162,22 +161,16 @@ class LocationProvider( credentials?.sessionToken!!, credentials?.expiration?.epochMilliseconds!!, ) - locationCredentialsProvider = - CoroutineScope(Dispatchers.Main) - .async { - authHelper.authenticateWithCredentialsProvider( - region!!, - credentialsProvider, - ) - }.await() - locationClient = locationCredentialsProvider?.getLocationClient() + region?.let { + locationClient = generateLocationClient(it, credentialsProvider) + } } } catch (e: Exception) { e.printStackTrace() } } - suspend fun generateNewAuthCredentials(authHelper: AuthHelper) { + suspend fun generateNewAuthCredentials() { try { region = mPreferenceManager.getValue(KEY_USER_REGION, "").toString() cognitoIdentityClient = generateCognitoIdentityClient(region) @@ -211,33 +204,27 @@ class LocationProvider( credentials = getCredentialsResponse?.credentials if (credentials != null) { - credentials?.let { - if (it.accessKeyId == null || - it.secretKey == null || - it.sessionToken == null + credentials?.let { cred -> + if (cred.accessKeyId == null || + cred.secretKey == null || + cred.sessionToken == null ) { throw Exception("Credentials generation failed") } - mPreferenceManager.setValue(KEY_ACCESS_KEY, it.accessKeyId!!) - mPreferenceManager.setValue(KEY_SECRET_KEY, it.secretKey!!) - mPreferenceManager.setValue(KEY_SESSION_TOKEN, it.sessionToken!!) - mPreferenceManager.setValue(KEY_EXPIRATION, it.expiration?.epochMilliseconds!!) + mPreferenceManager.setValue(KEY_ACCESS_KEY, cred.accessKeyId!!) + mPreferenceManager.setValue(KEY_SECRET_KEY, cred.secretKey!!) + mPreferenceManager.setValue(KEY_SESSION_TOKEN, cred.sessionToken!!) + mPreferenceManager.setValue(KEY_EXPIRATION, cred.expiration?.epochMilliseconds!!) val credentialsProvider = createCredentialsProvider( - it.accessKeyId!!, - it.secretKey!!, - it.sessionToken!!, - it.expiration?.epochMilliseconds!!, + cred.accessKeyId!!, + cred.secretKey!!, + cred.sessionToken!!, + cred.expiration?.epochMilliseconds!!, ) - locationCredentialsProvider = - CoroutineScope(Dispatchers.Main) - .async { - authHelper.authenticateWithCredentialsProvider( - region!!, - credentialsProvider, - ) - }.await() - locationClient = locationCredentialsProvider?.getLocationClient() + region?.let { + locationClient = generateLocationClient(it, credentialsProvider) + } } } else { throw Exception("Credentials generation failed") @@ -247,30 +234,33 @@ class LocationProvider( } } suspend fun getAnalyticsCredentialProvider(): CredentialsProvider? { + if (BuildConfig.ANALYTICS_IDENTITY_POOL_ID == "null") return null val defaultIdentityPoolId = BuildConfig.ANALYTICS_IDENTITY_POOL_ID val defaultRegion = BuildConfig.ANALYTICS_IDENTITY_POOL_ID.split(":")[0] - return generateCredentials(defaultRegion, defaultIdentityPoolId) + return generateUnAuthCredentials(defaultRegion, defaultIdentityPoolId, true) } - private suspend fun generateCredentials(region: String, identityPoolId: String): CredentialsProvider? { + private suspend fun generateUnAuthCredentials(region: String, identityPoolId: String, isForAnalytics: Boolean): CredentialsProvider? { val cognitoIdentityClient = CognitoIdentityClient { this.region = region } try { - val accessKey = mPreferenceManager.getValue(KEY_ANALYTICS_ACCESS_KEY, "") - val secretKey = mPreferenceManager.getValue(KEY_ANALYTICS_SECRET_KEY, "") - val sessionToken = mPreferenceManager.getValue(KEY_ANALYTICS_SESSION_TOKEN, "") - val expiration = mPreferenceManager.getLongValue(KEY_ANALYTICS_EXPIRATION, 0L) + val accessKey = if (isForAnalytics) mPreferenceManager.getValue(KEY_ANALYTICS_ACCESS_KEY, "") else mPreferenceManager.getValue(KEY_ACCESS_KEY, "") + val secretKey = if (isForAnalytics) mPreferenceManager.getValue(KEY_ANALYTICS_SECRET_KEY, "") else mPreferenceManager.getValue(KEY_SECRET_KEY, "") + val sessionToken = if (isForAnalytics) mPreferenceManager.getValue(KEY_ANALYTICS_SESSION_TOKEN, "") else mPreferenceManager.getValue(KEY_SESSION_TOKEN, "") + val expiration = if (isForAnalytics) mPreferenceManager.getLongValue(KEY_ANALYTICS_EXPIRATION, 0L) else mPreferenceManager.getLongValue(KEY_EXPIRATION, 0L) if (accessKey.isNullOrEmpty() || secretKey.isNullOrEmpty() || sessionToken.isNullOrEmpty() || expiration == 0L || - !isAnalyticsCredentialsValid() + !isUnAuthCredentialsValid(isForAnalytics) ) { val getIdResponse = cognitoIdentityClient.getId(GetIdRequest { this.identityPoolId = identityPoolId }) val identityId = getIdResponse.identityId ?: throw Exception("Failed to get identity ID") + if (identityId.isNotEmpty()) { + if (!isForAnalytics) mPreferenceManager.setValue(KEY_IDENTITY_ID, identityId) val getCredentialsResponse = cognitoIdentityClient.getCredentialsForIdentity( GetCredentialsForIdentityRequest { @@ -283,10 +273,25 @@ class LocationProvider( if (credentials.accessKeyId == null || credentials.secretKey == null || credentials.sessionToken == null || credentials.expiration == null) throw Exception( "Credentials generation failed" ) - mPreferenceManager.setValue(KEY_ANALYTICS_ACCESS_KEY, credentials.accessKeyId!!) - mPreferenceManager.setValue(KEY_ANALYTICS_SECRET_KEY, credentials.secretKey!!) - mPreferenceManager.setValue(KEY_ANALYTICS_SESSION_TOKEN, credentials.sessionToken!!) - mPreferenceManager.setValue(KEY_ANALYTICS_EXPIRATION, credentials.expiration!!.epochMilliseconds) + + if (isForAnalytics) { + mPreferenceManager.setValue(KEY_ANALYTICS_ACCESS_KEY, credentials.accessKeyId!!) + mPreferenceManager.setValue(KEY_ANALYTICS_SECRET_KEY, credentials.secretKey!!) + mPreferenceManager.setValue(KEY_ANALYTICS_SESSION_TOKEN, credentials.sessionToken!!) + mPreferenceManager.setValue(KEY_ANALYTICS_EXPIRATION, credentials.expiration!!.epochMilliseconds) + } else { + this.credentials = + aws.sdk.kotlin.services.cognitoidentity.model.Credentials { + this.accessKeyId = credentials.accessKeyId + this.secretKey = credentials.secretKey + this.sessionToken = credentials.sessionToken + this.expiration = credentials.expiration + } + mPreferenceManager.setValue(KEY_ACCESS_KEY, credentials.accessKeyId!!) + mPreferenceManager.setValue(KEY_SECRET_KEY, credentials.secretKey!!) + mPreferenceManager.setValue(KEY_SESSION_TOKEN, credentials.sessionToken!!) + mPreferenceManager.setValue(KEY_EXPIRATION, credentials.expiration?.epochMilliseconds!!) + } return createCredentialsProvider( credentials.accessKeyId!!, credentials.secretKey!!, @@ -297,6 +302,15 @@ class LocationProvider( return null } } else { + if (!isForAnalytics) { + credentials = + aws.sdk.kotlin.services.cognitoidentity.model.Credentials { + this.accessKeyId = accessKey + this.secretKey = secretKey + this.sessionToken = sessionToken + this.expiration = Instant.fromEpochMilliseconds(expiration) + } + } return createCredentialsProvider( accessKey, secretKey, @@ -309,8 +323,8 @@ class LocationProvider( } } - fun isAnalyticsCredentialsValid(): Boolean { - val expirationTimeMillis = mPreferenceManager.getLongValue(KEY_ANALYTICS_EXPIRATION, 0L) + fun isUnAuthCredentialsValid(isForAnalytics: Boolean): Boolean { + val expirationTimeMillis = if (isForAnalytics) mPreferenceManager.getLongValue(KEY_ANALYTICS_EXPIRATION, 0L) else mPreferenceManager.getLongValue(KEY_EXPIRATION, 0L) if (expirationTimeMillis == 0L) return false val currentTimeMillis = Instant.now().epochMilliseconds return currentTimeMillis < expirationTimeMillis @@ -334,16 +348,9 @@ class LocationProvider( ) fun getCredentials(): aws.sdk.kotlin.services.cognitoidentity.model.Credentials? { - val mAuthStatus = mPreferenceManager.getValue(KEY_CLOUD_FORMATION_STATUS, "") - return if (mAuthStatus == AuthEnum.SIGNED_IN.name) { - credentials - } else { - locationCredentialsProvider?.getCredentialsProvider() - } + return credentials } - fun getRegion(): String? = region - fun checkClientInitialize(): Boolean = locationClient != null fun checkSessionValid(activity: BaseActivity) { @@ -353,17 +360,23 @@ class LocationProvider( activity.refreshToken() } } else { - locationCredentialsProvider?.let { - if (!it.isCredentialsValid()) { - CoroutineScope(Dispatchers.IO).launch { - async { it.verifyAndRefreshCredentials() }.await() - locationClient = locationCredentialsProvider?.getLocationClient() - } + if (!isUnAuthCredentialsValid(false)) { + CoroutineScope(Dispatchers.IO).launch { + async { initializeLocationCredentialsProvider(activity) }.await() } } } } + private fun generateLocationClient( + region: String, + credentialsProvider: CredentialsProvider, + ): LocationClient { + return LocationClient { + this.region = region + this.credentialsProvider = credentialsProvider + } + } fun isAuthTokenExpired(): Boolean { val expiresIn = mPreferenceManager.getLongValue(KEY_AUTH_EXPIRES_IN, 0L) val authFetchTime = mPreferenceManager.getLongValue(KEY_AUTH_FETCH_TIME, 0L) @@ -453,12 +466,10 @@ class LocationProvider( fun getBaseActivity(): BaseActivity? = mBaseActivity - private fun createEmptyCredentialsProvider(): CredentialsProvider = - StaticCredentialsProvider( - Credentials.invoke( - accessKeyId = "", - secretAccessKey = "", - sessionToken = null, - ), - ) + fun clearCredentials() { + mPreferenceManager.removeValue(KEY_ACCESS_KEY) + mPreferenceManager.removeValue(KEY_SECRET_KEY) + mPreferenceManager.removeValue(KEY_SESSION_TOKEN) + mPreferenceManager.removeValue(KEY_EXPIRATION) + } } \ No newline at end of file diff --git a/app/src/main/java/com/aws/amazonlocation/utils/providers/PlacesProvider.kt b/app/src/main/java/com/aws/amazonlocation/utils/providers/PlacesProvider.kt index e6adce9c..06160367 100644 --- a/app/src/main/java/com/aws/amazonlocation/utils/providers/PlacesProvider.kt +++ b/app/src/main/java/com/aws/amazonlocation/utils/providers/PlacesProvider.kt @@ -3,6 +3,7 @@ package com.aws.amazonlocation.utils.providers import android.location.Location import aws.sdk.kotlin.services.geoplaces.GeoPlacesClient import aws.sdk.kotlin.services.geoplaces.model.Address +import aws.sdk.kotlin.services.geoplaces.model.GetPlaceAdditionalFeature import aws.sdk.kotlin.services.geoplaces.model.ReverseGeocodeRequest import aws.sdk.kotlin.services.geoplaces.model.ReverseGeocodeResponse import aws.sdk.kotlin.services.geoplaces.model.SearchTextRequest @@ -42,7 +43,6 @@ class PlacesProvider( try { val request = SuggestRequest { - key = getApiKey(mPreferenceManager) this.queryText = text this.language = getLanguageCode() this.maxResults = SEARCH_MAX_SUGGESTION_RESULT @@ -70,9 +70,10 @@ class PlacesProvider( var isLatLng = false var suggestResponse: SuggestResponse? = null var reverseGeocodeResponse: ReverseGeocodeResponse? = null - if (validateLatLng(searchText) != null) { + val response: SearchSuggestionResponse? + if (validateLatLng(searchText.trim()) != null) { isLatLng = true - val mLatLng = validateLatLng(searchText) + val mLatLng = validateLatLng(searchText.trim()) reverseGeocodeResponse = searchPlaceIndexForPosition( lng = mLatLng?.longitude, @@ -80,6 +81,11 @@ class PlacesProvider( mBaseActivity, getPlaceClient, ) + response = + SearchSuggestionResponse( + text = mLatLng?.latitude.toString() + "," + mLatLng?.longitude.toString(), + maxResults = reverseGeocodeResponse?.resultItems?.size + ) } else { suggestResponse = searchPlaceIndexForSuggestions( @@ -89,16 +95,14 @@ class PlacesProvider( mBaseActivity, getPlaceClient, ) + response = + SearchSuggestionResponse( + text = searchText, + maxResults = suggestResponse?.resultItems?.size, + ) } val mList = ArrayList() - val response = - SearchSuggestionResponse( - text = searchText, - maxResults = - reverseGeocodeResponse?.resultItems?.size - ?: suggestResponse?.resultItems?.size, - ) if (isLatLng) { addMarkerBasedOnLatLng(response, searchText, mList) } @@ -106,22 +110,13 @@ class PlacesProvider( reverseGeocodeResponse?.resultItems?.forEach { val mSearchSuggestionData: SearchSuggestionData = if (it.placeId.isNotEmpty()) { - val getSearchResult = getPlace(it.placeId, mBaseActivity, getPlaceClient) - val position = getSearchResult?.position SearchSuggestionData( placeId = it.placeId, - text = getSearchResult?.address?.label, - amazonLocationAddress = getSearchResult?.address, - distance = - position?.let { doubles -> - getDistance( - liveLocation, - doubles[1], - doubles[0], - ) - }, + text = it.address?.label, + amazonLocationAddress = it.address, + distance = it.distance.toDouble(), position = - position?.let { doubles -> + it.position?.let { doubles -> listOf( doubles[0], doubles[1], @@ -136,24 +131,14 @@ class PlacesProvider( suggestResponse?.resultItems?.forEach { val mSearchSuggestionData: SearchSuggestionData = if (!it.place?.placeId.isNullOrEmpty()) { - val getSearchResult = - getPlace(it.place?.placeId!!, mBaseActivity, getPlaceClient) - val position = getSearchResult?.position SearchSuggestionData( placeId = it.place!!.placeId, searchText = searchText, - text = getSearchResult?.address?.label, - amazonLocationAddress = getSearchResult?.address, - distance = - position?.let { doubles -> - getDistance( - liveLocation, - doubles[1], - doubles[0], - ) - }, + text = it.place?.address?.label, + amazonLocationAddress = it.place?.address, + distance = it.place?.distance?.toDouble(), position = - position?.let { doubles -> + it.place?.position?.let { doubles -> listOf( doubles[0], doubles[1], @@ -180,7 +165,7 @@ class PlacesProvider( searchText: String, mList: ArrayList, ) { - val mLatLng = validateLatLng(searchText) + val mLatLng = validateLatLng(searchText.trim()) if (mLatLng != null) { val place = Address { @@ -211,7 +196,6 @@ class PlacesProvider( val liveLocation = mMapHelper.getLiveLocation() val request = SearchTextRequest { - this.key = getApiKey(mPreferenceManager) if (mText != null) this.queryText = mText if (queryId != null) this.queryId = queryId if (mText != null) this.language = getLanguageCode() @@ -238,12 +222,13 @@ class PlacesProvider( ) val mList = ArrayList() - if (validateLatLng(text!!) != null && response?.resultItems?.isEmpty()!!) { + if (validateLatLng(text!!.trim()) != null && response?.resultItems?.isEmpty()!!) { addMarkerBasedOnLatLng(searchSuggestionResponse, text, mList) } else { response?.resultItems?.forEach { result -> val placeData = SearchSuggestionData( + placeId = result.placeId, searchText = text, amazonLocationAddress = result.address, text = result.title, @@ -252,7 +237,7 @@ class PlacesProvider( result.position?.get(0) ?: 0.0, result.position?.get(1) ?: 0.0, ), - distance = + distance = getDistance( liveLocation, result.position @@ -279,6 +264,7 @@ class PlacesProvider( try { val request = aws.sdk.kotlin.services.geoplaces.model.GetPlaceRequest { + this.additionalFeatures = listOf(GetPlaceAdditionalFeature.Contact) this.key = getApiKey(mPreferenceManager) this.placeId = placeId this.language = getLanguageCode() @@ -301,7 +287,6 @@ class PlacesProvider( try { val request = ReverseGeocodeRequest { - this.key = getApiKey(mPreferenceManager) this.language = getLanguageCode() this.queryPosition = listOfNotNull(lng, lat) this.maxResults = SEARCH_MAX_SUGGESTION_RESULT @@ -326,7 +311,6 @@ class PlacesProvider( try { val request = ReverseGeocodeRequest { - this.key = getApiKey(mPreferenceManager) this.language = getLanguageCode() this.queryPosition = listOfNotNull(lng, lat) this.maxResults = 1 diff --git a/app/src/main/java/com/aws/amazonlocation/utils/providers/RoutesProvider.kt b/app/src/main/java/com/aws/amazonlocation/utils/providers/RoutesProvider.kt index 561661ec..c7f068b2 100644 --- a/app/src/main/java/com/aws/amazonlocation/utils/providers/RoutesProvider.kt +++ b/app/src/main/java/com/aws/amazonlocation/utils/providers/RoutesProvider.kt @@ -12,7 +12,6 @@ import aws.sdk.kotlin.services.georoutes.model.RouteTravelStepType import com.aws.amazonlocation.ui.base.BaseActivity import com.aws.amazonlocation.utils.KEY_UNIT_SYSTEM import com.aws.amazonlocation.utils.PreferenceManager -import com.aws.amazonlocation.utils.Units.getApiKey import com.aws.amazonlocation.utils.Units.isMetric import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext @@ -43,7 +42,6 @@ class RoutesProvider( } val request = CalculateRoutesRequest { - key = getApiKey(mPreferenceManager) origin = listOfNotNull(lngDeparture, latDeparture) destination = listOfNotNull(lngDestination, latDestination) avoid = RouteAvoidanceOptions { diff --git a/app/src/main/res/drawable/ic_copy.xml b/app/src/main/res/drawable/ic_copy.xml new file mode 100644 index 00000000..24078a25 --- /dev/null +++ b/app/src/main/res/drawable/ic_copy.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_phone_number.xml b/app/src/main/res/drawable/ic_phone_number.xml new file mode 100644 index 00000000..103b88e3 --- /dev/null +++ b/app/src/main/res/drawable/ic_phone_number.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_place_link.xml b/app/src/main/res/drawable/ic_place_link.xml new file mode 100644 index 00000000..9df4002e --- /dev/null +++ b/app/src/main/res/drawable/ic_place_link.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_timing.xml b/app/src/main/res/drawable/ic_timing.xml new file mode 100644 index 00000000..f9a93757 --- /dev/null +++ b/app/src/main/res/drawable/ic_timing.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/layout-sw600dp/bottom_sheet_direction.xml b/app/src/main/res/layout-sw600dp/bottom_sheet_direction.xml index c7fde108..403de10a 100644 --- a/app/src/main/res/layout-sw600dp/bottom_sheet_direction.xml +++ b/app/src/main/res/layout-sw600dp/bottom_sheet_direction.xml @@ -69,11 +69,22 @@ android:gravity="start" android:textDirection="locale" android:maxLines="2" - app:layout_constraintEnd_toStartOf="@id/iv_direction_close_direction" + android:layout_marginEnd="@dimen/dp_8" + app:layout_constraintEnd_toStartOf="@id/iv_copy_address" app:layout_constraintStart_toStartOf="@id/direction_sheet_gl_direction_start" app:layout_constraintTop_toBottomOf="@id/tv_direction_address" tools:text="24 Heath Hill Road South,London,RG45" /> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + app:layout_constraintTop_toBottomOf="@id/top_barrier"> - + + \ No newline at end of file diff --git a/app/src/main/res/layout-sw600dp/bottom_sheet_direction_search.xml b/app/src/main/res/layout-sw600dp/bottom_sheet_direction_search.xml index 2445b7e6..83998635 100644 --- a/app/src/main/res/layout-sw600dp/bottom_sheet_direction_search.xml +++ b/app/src/main/res/layout-sw600dp/bottom_sheet_direction_search.xml @@ -1365,6 +1365,7 @@ android:id="@+id/layout_no_data_found" layout="@layout/layout_no_data_found" android:layout_width="match_parent" + android:visibility="gone" android:layout_height="@dimen/dp_0" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" diff --git a/app/src/main/res/layout-sw720dp/bottom_sheet_direction.xml b/app/src/main/res/layout-sw720dp/bottom_sheet_direction.xml index c7fde108..403de10a 100644 --- a/app/src/main/res/layout-sw720dp/bottom_sheet_direction.xml +++ b/app/src/main/res/layout-sw720dp/bottom_sheet_direction.xml @@ -69,11 +69,22 @@ android:gravity="start" android:textDirection="locale" android:maxLines="2" - app:layout_constraintEnd_toStartOf="@id/iv_direction_close_direction" + android:layout_marginEnd="@dimen/dp_8" + app:layout_constraintEnd_toStartOf="@id/iv_copy_address" app:layout_constraintStart_toStartOf="@id/direction_sheet_gl_direction_start" app:layout_constraintTop_toBottomOf="@id/tv_direction_address" tools:text="24 Heath Hill Road South,London,RG45" /> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + app:layout_constraintTop_toBottomOf="@id/top_barrier"> - + + \ No newline at end of file diff --git a/app/src/main/res/layout-sw720dp/bottom_sheet_direction_search.xml b/app/src/main/res/layout-sw720dp/bottom_sheet_direction_search.xml index 4c3e256a..914b6220 100644 --- a/app/src/main/res/layout-sw720dp/bottom_sheet_direction_search.xml +++ b/app/src/main/res/layout-sw720dp/bottom_sheet_direction_search.xml @@ -1367,6 +1367,7 @@ layout="@layout/layout_no_data_found" android:layout_width="match_parent" android:layout_height="@dimen/dp_0" + android:visibility="gone" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@id/card_map_option" /> diff --git a/app/src/main/res/layout/bottom_sheet_direction.xml b/app/src/main/res/layout/bottom_sheet_direction.xml index 0feebad0..93cf055c 100644 --- a/app/src/main/res/layout/bottom_sheet_direction.xml +++ b/app/src/main/res/layout/bottom_sheet_direction.xml @@ -89,11 +89,22 @@ android:textDirection="locale" android:lines="2" android:maxLines="2" - app:layout_constraintEnd_toStartOf="@id/iv_direction_close_direction" + android:layout_marginEnd="@dimen/dp_8" + app:layout_constraintEnd_toStartOf="@id/iv_copy_address" app:layout_constraintStart_toStartOf="@id/direction_sheet_gl_direction_start" app:layout_constraintTop_toBottomOf="@id/tv_direction_address" tools:text="24 Heath Hill Road South,London,RG45" /> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + app:layout_constraintTop_toBottomOf="@id/top_barrier"> - + + \ No newline at end of file diff --git a/app/src/main/res/layout/bottom_sheet_direction_search.xml b/app/src/main/res/layout/bottom_sheet_direction_search.xml index 88db351b..f545766e 100644 --- a/app/src/main/res/layout/bottom_sheet_direction_search.xml +++ b/app/src/main/res/layout/bottom_sheet_direction_search.xml @@ -1348,6 +1348,7 @@ android:id="@+id/layout_no_data_found" layout="@layout/layout_no_data_found" android:layout_width="match_parent" + android:visibility="gone" android:layout_height="@dimen/dp_0" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" diff --git a/app/src/main/res/values-ar/string.xml b/app/src/main/res/values-ar/string.xml index 4c74b053..de14b2f8 100644 --- a/app/src/main/res/values-ar/string.xml +++ b/app/src/main/res/values-ar/string.xml @@ -254,4 +254,11 @@ تنزانيا منظر في بحيرة مالاوي الأوروغواي منظر في رينكون دي أرتيجاس فيتنام منظر في ال باراسيل الجزر و سبراتلي الجزر + مطلوب التكوين مفاتيح يكونوا في عداد المفقودين، و ال تطبيق غير قادر وظيفة بدون هم. ال التالي مفاتيح يكونوا مفقود:\n + بعض التكوين مفاتيح يكونوا مفقود. خريطة، ابحث، الأماكن، و المسار يكونوا متاح، لكن محاكاة و تحليلات يكونوا معاق حتى ال التالي مفاتيح يكونوا وأضاف:\n + بعض التكوين مفاتيح يكونوا مفقود. خريطة، ابحث، الأماكن، و المسار يكونوا متاح، لكن محاكاة هو معاق حتى ال التالي مفاتيح يكونوا وأضاف:\n + تحليلات تتبع هو معاق بسبب ال التالي التكوين مفاتيح يكونوا مفقود:\n + التكوين غير مكتمل + جدول + تم نسخها إلى الحافظة \ No newline at end of file diff --git a/app/src/main/res/values-de/string.xml b/app/src/main/res/values-de/string.xml index 4afa43c0..34b897d4 100644 --- a/app/src/main/res/values-de/string.xml +++ b/app/src/main/res/values-de/string.xml @@ -254,4 +254,11 @@ Tansania\'s anzeigen auf See Malawi Uruguays anzeigen auf Rincon de Artigas Vietnams anzeigen auf die Paracel Inseln und Spratli Inseln + Erforderlich Aufbau Schlüssel sind fehlt, und die Anwendung kann nicht wirken ohne sie. Das folgend Schlüssel sind fehlt:\n + Einige Aufbau Schlüssel sind fehlt. Landkarte, Suche, Orte, und Route sind verfügbar, aber Simulation und Analytik sind Behinderte bis die folgend Schlüssel sind hinzugefügt:\n + Einige Aufbau Schlüssel sind fehlt. Landkarte, Suche, Orte, und Route sind verfügbar, aber Simulation ist Behinderte bis die folgend Schlüssel sind hinzugefügt:\n + Analytik Verfolgung ist Behinderte da die folgend Aufbau Schlüssel sind fehlt:\n + Konfiguration Unvollständig + Zeitplan + Kopiert zu Zwischenablage \ No newline at end of file diff --git a/app/src/main/res/values-es/string.xml b/app/src/main/res/values-es/string.xml index e9d62123..562ccd43 100644 --- a/app/src/main/res/values-es/string.xml +++ b/app/src/main/res/values-es/string.xml @@ -254,4 +254,11 @@ Tanzania\'s vista en lago Malawi Uruguay\'s vista en Rincón de Artigas Vietnam vista en la Paracelso Islas y Spratly Islas + Necesario configuración llaves somos desaparecido, y la aplicación no poder función sin ellos. El siguiendo llaves somos falta:\n + Algunos configuración llaves somos desaparecido. Mapa, Buscar, Lugares, y Ruta somos disponible, pero Simulación y Analítica somos inhabilitado hasta la siguiendo llaves somos agregado:\n + Algunos configuración llaves somos desaparecido. Mapa, Buscar, Lugares, y Ruta somos disponible, pero Simulación es inhabilitado hasta la siguiendo llaves somos agregado:\n + Analítica rastreo es inhabilitado porque la siguiendo configuración llaves somos falta:\n + Configuración Incompleto + Programar + Copiado a portapapeles \ No newline at end of file diff --git a/app/src/main/res/values-fr/string.xml b/app/src/main/res/values-fr/string.xml index 4a62f2e4..54c5bf1f 100644 --- a/app/src/main/res/values-fr/string.xml +++ b/app/src/main/res/values-fr/string.xml @@ -254,4 +254,11 @@ Tanzanie voir sur Lac Malawi Uruguay voir sur Rincón de Artigas Vietnam voir sur le Paracelle Îles et Spratly Îles + Nécessaire configuration clés sont disparu, et le candidature ne peut pas fonction sans eux. Le suivant clés sont manquant :\n + Certains configuration clés sont disparu. Carte, Rechercher, Des lieux, et Parcours sont disponible, mais Simulation et Analytique sont handicapé jusqu\'à le suivant clés sont ajouté :\n + Certains configuration clés sont disparu. Carte, Rechercher, Des lieux, et Parcours sont disponible, mais Simulation est handicapé jusqu\'à le suivant clés sont ajouté :\n + Analytique suivi est handicapé parce que le suivant configuration clés sont manquant :\n + Configuration Incomplet + Calendrier + Copié pour presse-papier \ No newline at end of file diff --git a/app/src/main/res/values-hi/string.xml b/app/src/main/res/values-hi/string.xml index abdea279..6be3992a 100644 --- a/app/src/main/res/values-hi/string.xml +++ b/app/src/main/res/values-hi/string.xml @@ -254,4 +254,11 @@ तंज़ानिया\'s राय के ऊपर झील मलावी उरुग्वे\'s राय के ऊपर रिनकोन डी अर्टिगास वियतनाम का राय के ऊपर यह पैरासेल द्वीप समूह और स्प्रैटली द्वीप समूह + ज़रूरी कॉन्फ़िगरेशन चांबियाँ हैं गुम, और यह आवेदन नहीं कर सकता समारोह बगैर उन्हें। द निम्नलिखित चांबियाँ हैं अनुपलब्ध:\n + कुछ कॉन्फ़िगरेशन चांबियाँ हैं लापता। नक्शा, खोजें, जगहें, और रूट हैं उपलब्ध, परंतु सिमुलेशन और एनालिटिक्स हैं दिव्यांग जब तक यह निम्नलिखित चांबियाँ हैं जोड़ा गया:\n + कुछ कॉन्फ़िगरेशन चांबियाँ हैं लापता। नक्शा, खोजें, जगहें, और रूट हैं उपलब्ध, परंतु सिमुलेशन है दिव्यांग जब तक यह निम्नलिखित चांबियाँ हैं जोड़ा गया:\n + एनालिटिक्स नज़र रखना है दिव्यांग इसलिये यह निम्नलिखित कॉन्फ़िगरेशन चांबियाँ हैं अनुपलब्ध:\n + कॉन्फ़िगरेशन अधूरा + शेड्यूल + कॉपी किया गया को क्लिपबोर्ड \ No newline at end of file diff --git a/app/src/main/res/values-it/string.xml b/app/src/main/res/values-it/string.xml index 02019c8c..a671702a 100644 --- a/app/src/main/res/values-it/string.xml +++ b/app/src/main/res/values-it/string.xml @@ -254,4 +254,11 @@ della Tanzania vista sul Lago Malawi Uruguay\'s vista sul Rincon de Artigas Vietnam\'s vista sul lo Paracel Isole e Spratly Isole + Richiesto configurazione chiavi siamo mancante, e lo candidatura Non posso funzione senza loro. Le seguenti chiavi siamo mancante:\n + Alcuni configurazione chiavi siamo mancante. Mappa, Cerca, Luoghi, e Percorso siamo disponibile, tranne Simulazione e analitica siamo disabili fino a lo seguenti chiavi siamo aggiunto:\n + Alcuni configurazione chiavi siamo mancante. Mappa, Cerca, Luoghi, e Percorso siamo disponibile, tranne Simulazione è disabili fino a lo seguenti chiavi siamo aggiunto:\n + analitica tracciamento è disabili perché lo seguenti configurazione chiavi siamo mancante:\n + Configurazione Incompleto + Pianificazione + Copiato a appunti \ No newline at end of file diff --git a/app/src/main/res/values-iw/string.xml b/app/src/main/res/values-iw/string.xml index 31c8de0d..b0cd2b4b 100644 --- a/app/src/main/res/values-iw/string.xml +++ b/app/src/main/res/values-iw/string.xml @@ -254,4 +254,11 @@ טנזניה מבט על אגם מלאווי אורוגוואי\ של מבט על רינקון דה ארטיגאס וייטנאם\ מבט על ה פאראסל איים ו ספראטלי איים + חובה תצורה מפתחות מהווים חסר, ו ה יישום לא יכול תפקוד בלי אותם. ה הבא מפתחות מהווים חסר:\ n + חלק תצורה מפתחות מהווים חסר. מפה, חיפוש, מקומות, ו מסלול מהווים זמין, אולם סימולציה ו ניתוח מהווים מושבת עד ה הבא מפתחות מהווים הוסיף:\ n + חלק תצורה מפתחות מהווים חסר. מפה, חיפוש, מקומות, ו מסלול מהווים זמין, אולם סימולציה הווה מושבת עד ה הבא מפתחות מהווים הוסיף:\ n + ניתוח מעקב הווה מושבת בגלל ה הבא תצורה מפתחות מהווים חסר:\ n + תצורה לא שלם + לוח זמנים + הועתק כדי הלוח \ No newline at end of file diff --git a/app/src/main/res/values-ja/string.xml b/app/src/main/res/values-ja/string.xml index 5f80f25f..3674cd5d 100644 --- a/app/src/main/res/values-ja/string.xml +++ b/app/src/main/res/values-ja/string.xml @@ -254,4 +254,11 @@ タンザニア 見る オン 湖 マラウイ ウルグアイ\ 見る オン リンコン de アルティガス ベトナムの 見る オン その パラセル 島々 そして スプラトリー 島々 + 必須 設定 キー です 行方不明、 そして その 応用 できない 関数 なし それら。 ザの 以下 キー です 見つからない:\n + 一部 設定 キー です 行方不明。 マップ、 検索、 場所、 そして ルート です 利用可能、 しかし シミュレーション そして 分析 です 無効になっています まで その 以下 キー です 追加されました:\n + 一部 設定 キー です 行方不明。 マップ、 検索、 場所、 そして ルート です 利用可能、 しかし シミュレーション です 無効になっています まで その 以下 キー です 追加されました:\n + 分析 追跡 です 無効になっています なぜなら その 以下 設定 キー です 見つからない:\n + コンフィギュレーション 不完全 + スケジュール + コピーされました に クリップボード \ No newline at end of file diff --git a/app/src/main/res/values-ko/string.xml b/app/src/main/res/values-ko/string.xml index 674236e9..3d04308e 100644 --- a/app/src/main/res/values-ko/string.xml +++ b/app/src/main/res/values-ko/string.xml @@ -254,4 +254,11 @@ 탄자니아 전망 ...에 호수 말라위 우루과이 전망 ...에 린콘 de 아르티가스 베트남 전망 ...에 그 파라셀 섬 과 스프래틀리 섬 + 필수 구성 키를 누릅니다 입니다 실종, 과 그 응용 프로그램 할 수 없다 기능 없이 그들을. 더 다음의 키를 누릅니다 입니다 누락:\n + 일부 구성 키를 누릅니다 입니다 누락되었습니다. 지도, 검색, 장소, 과 노선 입니다 사용 가능, 하지만 시뮬레이션 과 해석학 입니다 비활성화 ...까지 그 다음의 키를 누릅니다 입니다 추가됨:\n + 일부 구성 키를 누릅니다 입니다 누락되었습니다. 지도, 검색, 장소, 과 노선 입니다 사용 가능, 하지만 시뮬레이션 입니다 비활성화 ...까지 그 다음의 키를 누릅니다 입니다 추가됨:\n + 해석학 추적 입니다 비활성화 왜냐하면 그 다음의 구성 키를 누릅니다 입니다 누락:\n + 구성 불완전 + 스케줄 + 복사됨 에 클립보드 \ No newline at end of file diff --git a/app/src/main/res/values-pt/string.xml b/app/src/main/res/values-pt/string.xml index 3ef5a51d..9de24215 100644 --- a/app/src/main/res/values-pt/string.xml +++ b/app/src/main/res/values-pt/string.xml @@ -254,4 +254,11 @@ Tanzânia\'s olhar em Lago Malawi Uruguai\'s olhar em Rincon de Artigas Vietnã\'s olhar em a Paracel Ilhas e Spratly Ilhas + Obrigatório configuração chaves está desaparecido, e a aplicativo não pode função sem eles. O segue chaves está ausente:\n + Alguns configuração chaves está desaparecido. Mapa, Pesquisar, Lugares, e Rota está disponível, mas Simulação e Análise está deficiente até a segue chaves está adicionado:\n + Alguns configuração chaves está desaparecido. Mapa, Pesquisar, Lugares, e Rota está disponível, mas Simulação é deficiente até a segue chaves está adicionado:\n + Análise rastreamento é deficiente porque a segue configuração chaves está ausente:\n + Configuração Incompleto + Cronograma + Copiado para prancheta \ No newline at end of file diff --git a/app/src/main/res/values-zh-rCN/string.xml b/app/src/main/res/values-zh-rCN/string.xml index 2af7679e..f5125af5 100644 --- a/app/src/main/res/values-zh-rCN/string.xml +++ b/app/src/main/res/values-zh-rCN/string.xml @@ -254,4 +254,11 @@ 坦桑尼亚的 观点 上 湖 马拉维 乌拉圭 观点 上 林康 de 阿蒂加斯 越南 观点 上 这 帕拉塞尔 岛屿 和 南沙群岛 岛屿 + 必填项 配置 钥匙 是 失踪, 和 这 应用程序 不能 功能 没有 他们。 这个 以下 钥匙 是 缺失:\n + 一些 配置 钥匙 是 失踪。 地图, 搜索, 地方, 和 路线 是 可用, 但是 模拟 和 分析 是 残疾的 直到 这 以下 钥匙 是 已添加:\n + 一些 配置 钥匙 是 失踪。 地图, 搜索, 地方, 和 路线 是 可用, 但是 模拟 是 残疾的 直到 这 以下 钥匙 是 已添加:\n + 分析 追踪 是 残疾的 因为 这 以下 配置 钥匙 是 缺失:\n + 配置 不完整 + 日程安排 + 已复制 到 剪贴板 \ No newline at end of file diff --git a/app/src/main/res/values-zh-rTW/string.xml b/app/src/main/res/values-zh-rTW/string.xml index c4a9fc5a..a59e0c0e 100644 --- a/app/src/main/res/values-zh-rTW/string.xml +++ b/app/src/main/res/values-zh-rTW/string.xml @@ -254,4 +254,11 @@ 坦桑尼亞\ 的 檢視 上 湖 馬拉威 烏拉圭\ 的 檢視 上 林康 下 阿蒂加斯 越南\ 的 檢視 上 該 帕拉塞爾 島嶼 和 普拉特利 島嶼 + 必須 配置 按鍵 是 遺失, 和 該 應用程式 不能 功能 無 他們。 The 以下 按鍵 是 缺少:\n + 一些 配置 按鍵 是 遺失。 地圖, 搜索, 地點, 和 路線 是 可用, 但 模擬 和 分析 是 殘疾人 直到 該 以下 按鍵 是 添加:\n + 一些 配置 按鍵 是 遺失。 地圖, 搜索, 地點, 和 路線 是 可用, 但 模擬 是 殘疾人 直到 該 以下 按鍵 是 添加:\n + 分析 追蹤 是 殘疾人 因為 該 以下 配置 按鍵 是 缺少:\n + 配置 不完整 + 時間表 + 已複製 至 剪貼板 \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 5674a083..239595f7 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -300,4 +300,12 @@ TZA URY VNM + + Required configuration keys are missing, and the application cannot function without them. The following keys are missing:\n + Some configuration keys are missing. Map, Search, Places, and Route are available, but Simulation and Analytics are disabled until the following keys are added:\n + Some configuration keys are missing. Map, Search, Places, and Route are available, but Simulation is disabled until the following keys are added:\n + Analytics tracking is disabled because the following configuration keys are missing:\n + Configuration Incomplete + Schedule + Copied to clipboard \ No newline at end of file diff --git a/app/src/test/java/com/aws/amazonlocation/mock/Responses.kt b/app/src/test/java/com/aws/amazonlocation/mock/Responses.kt index 5bdb2804..f81feab0 100644 --- a/app/src/test/java/com/aws/amazonlocation/mock/Responses.kt +++ b/app/src/test/java/com/aws/amazonlocation/mock/Responses.kt @@ -1,7 +1,10 @@ package com.aws.amazonlocation.mock import aws.sdk.kotlin.services.geoplaces.model.Address +import aws.sdk.kotlin.services.geoplaces.model.ContactDetails +import aws.sdk.kotlin.services.geoplaces.model.Contacts import aws.sdk.kotlin.services.geoplaces.model.Country +import aws.sdk.kotlin.services.geoplaces.model.GetPlaceResponse import aws.sdk.kotlin.services.geoplaces.model.PlaceType import aws.sdk.kotlin.services.geoplaces.model.Region import aws.sdk.kotlin.services.geoplaces.model.ReverseGeocodeResponse @@ -515,19 +518,20 @@ object Responses { country = "IND", ) - val RESPONSE_NAVIGATION_DATA_CAR_STEP_2 = - NavigationData( - duration = 16.70485124, - distance = 0.060054637848645454, - startLat = 72.83317999485907, - startLng = 18.92106998837811, - endLat = 72.83338298024672, - endLng = 18.921575535610945, - destinationAddress = "Jokim Alva Chowk, Colaba, Mumbai, Maharashtra, 400005, IND", - region = "Maharashtra", - subRegion = "Mumbai", - country = "IND", - ) + val GET_PLACE_RESPONSE = GetPlaceResponse { + placeId = "test" + placeType = PlaceType.Block + pricingBucket = "test" + title = "place" + contacts = Contacts { + phones = listOf( + ContactDetails { + label = "test" + value = "6666444422" + } + ) + } + } val RESPONSE_GEOFENCE_LIST = GeofenceData( diff --git a/app/src/test/java/com/aws/amazonlocation/utils/units/UnitsGetApiKeyTest.kt b/app/src/test/java/com/aws/amazonlocation/utils/units/UnitsGetApiKeyTest.kt new file mode 100644 index 00000000..7d736ead --- /dev/null +++ b/app/src/test/java/com/aws/amazonlocation/utils/units/UnitsGetApiKeyTest.kt @@ -0,0 +1,45 @@ +package com.aws.amazonlocation.utils.units + +import com.aws.amazonlocation.BaseTest +import com.aws.amazonlocation.BuildConfig +import com.aws.amazonlocation.mock.* +import com.aws.amazonlocation.utils.KEY_NEAREST_REGION +import com.aws.amazonlocation.utils.KEY_SELECTED_REGION +import com.aws.amazonlocation.utils.PreferenceManager +import com.aws.amazonlocation.utils.Units +import com.aws.amazonlocation.utils.regionDisplayName +import com.aws.amazonlocation.utils.regionList +import org.junit.Assert +import org.junit.Test +import org.junit.runner.RunWith +import org.robolectric.RobolectricTestRunner +import org.robolectric.RuntimeEnvironment + +@RunWith(RobolectricTestRunner::class) +class UnitsGetApiKeyTest : BaseTest() { + private val context = RuntimeEnvironment.getApplication().applicationContext + + private val preferenceManager = PreferenceManager(context) + @Test + fun getApiKeySuccess() { + var result = Units.getApiKey(preferenceManager) + Assert.assertTrue(TEST_FAILED_DUE_TO_INCORRECT_DATA, result == BuildConfig.API_KEY_US_EAST) + result = Units.getApiKey(null) + Assert.assertTrue(TEST_FAILED_DUE_TO_INCORRECT_DATA, result == BuildConfig.API_KEY_US_EAST) + preferenceManager.setValue(KEY_NEAREST_REGION, regionList[0]) + result = Units.getApiKey(preferenceManager) + Assert.assertTrue(TEST_FAILED_DUE_TO_INCORRECT_DATA, result == BuildConfig.API_KEY_US_EAST) + preferenceManager.setValue(KEY_NEAREST_REGION, regionList[1]) + result = Units.getApiKey(preferenceManager) + Assert.assertTrue(TEST_FAILED_DUE_TO_INCORRECT_DATA, result == BuildConfig.API_KEY_EU_CENTRAL) + preferenceManager.setValue(KEY_SELECTED_REGION, regionDisplayName[1]) + result = Units.getApiKey(preferenceManager) + Assert.assertTrue(TEST_FAILED_DUE_TO_INCORRECT_DATA, result == BuildConfig.API_KEY_EU_CENTRAL) + preferenceManager.setValue(KEY_SELECTED_REGION, regionDisplayName[2]) + result = Units.getApiKey(preferenceManager) + Assert.assertTrue(TEST_FAILED_DUE_TO_INCORRECT_DATA, result == BuildConfig.API_KEY_US_EAST) + preferenceManager.setValue(KEY_SELECTED_REGION, "test") + result = Units.getApiKey(preferenceManager) + Assert.assertTrue(TEST_FAILED_DUE_TO_INCORRECT_DATA, result == BuildConfig.API_KEY_US_EAST) + } +} diff --git a/app/src/test/java/com/aws/amazonlocation/utils/units/UnitsGetRegionTest.kt b/app/src/test/java/com/aws/amazonlocation/utils/units/UnitsGetRegionTest.kt new file mode 100644 index 00000000..501f9711 --- /dev/null +++ b/app/src/test/java/com/aws/amazonlocation/utils/units/UnitsGetRegionTest.kt @@ -0,0 +1,44 @@ +package com.aws.amazonlocation.utils.units + +import com.aws.amazonlocation.BaseTest +import com.aws.amazonlocation.mock.* +import com.aws.amazonlocation.utils.KEY_NEAREST_REGION +import com.aws.amazonlocation.utils.KEY_SELECTED_REGION +import com.aws.amazonlocation.utils.PreferenceManager +import com.aws.amazonlocation.utils.Units +import com.aws.amazonlocation.utils.regionDisplayName +import com.aws.amazonlocation.utils.regionList +import org.junit.Assert +import org.junit.Test +import org.junit.runner.RunWith +import org.robolectric.RobolectricTestRunner +import org.robolectric.RuntimeEnvironment + +@RunWith(RobolectricTestRunner::class) +class UnitsGetRegionTest : BaseTest() { + private val context = RuntimeEnvironment.getApplication().applicationContext + + private val preferenceManager = PreferenceManager(context) + @Test + fun getRegionSuccess() { + var result = Units.getRegion(preferenceManager) + Assert.assertTrue(TEST_FAILED_DUE_TO_INCORRECT_DATA, result == regionList[0]) + result = Units.getRegion(null) + Assert.assertTrue(TEST_FAILED_DUE_TO_INCORRECT_DATA, result == regionList[0]) + preferenceManager.setValue(KEY_NEAREST_REGION, regionList[0]) + result = Units.getRegion(preferenceManager) + Assert.assertTrue(TEST_FAILED_DUE_TO_INCORRECT_DATA, result == regionList[0]) + preferenceManager.setValue(KEY_NEAREST_REGION, regionList[1]) + result = Units.getRegion(preferenceManager) + Assert.assertTrue(TEST_FAILED_DUE_TO_INCORRECT_DATA, result == regionList[1]) + preferenceManager.setValue(KEY_SELECTED_REGION, regionDisplayName[1]) + result = Units.getRegion(preferenceManager) + Assert.assertTrue(TEST_FAILED_DUE_TO_INCORRECT_DATA, result == regionList[1]) + preferenceManager.setValue(KEY_SELECTED_REGION, regionDisplayName[2]) + result = Units.getRegion(preferenceManager) + Assert.assertTrue(TEST_FAILED_DUE_TO_INCORRECT_DATA, result == regionList[0]) + preferenceManager.setValue(KEY_SELECTED_REGION, "test") + result = Units.getRegion(preferenceManager) + Assert.assertTrue(TEST_FAILED_DUE_TO_INCORRECT_DATA, result == regionList[0]) + } +} diff --git a/app/src/test/java/com/aws/amazonlocation/viewmodel/explore/ExploreVMGetPlaceDetais.kt b/app/src/test/java/com/aws/amazonlocation/viewmodel/explore/ExploreVMGetPlaceDetais.kt new file mode 100644 index 00000000..2539ade2 --- /dev/null +++ b/app/src/test/java/com/aws/amazonlocation/viewmodel/explore/ExploreVMGetPlaceDetais.kt @@ -0,0 +1,110 @@ +package com.aws.amazonlocation.viewmodel.explore + +import androidx.arch.core.executor.testing.InstantTaskExecutorRule +import app.cash.turbine.test +import com.aws.amazonlocation.BaseTest +import com.aws.amazonlocation.data.common.DataSourceException +import com.aws.amazonlocation.data.common.HandleResult +import com.aws.amazonlocation.data.datasource.RemoteDataSourceImpl +import com.aws.amazonlocation.data.repository.LocationSearchImp +import com.aws.amazonlocation.domain.`interface`.PlaceInterface +import com.aws.amazonlocation.domain.usecase.LocationSearchUseCase +import com.aws.amazonlocation.mock.* +import com.aws.amazonlocation.ui.main.explore.ExploreViewModel +import kotlinx.coroutines.test.runTest +import org.junit.Assert +import org.junit.Rule +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.Mock +import org.mockito.Mockito +import org.mockito.kotlin.any +import org.mockito.kotlin.anyOrNull +import org.robolectric.RobolectricTestRunner + +@RunWith(RobolectricTestRunner::class) +class ExploreVMGetPlaceDetais : BaseTest() { + + @get:Rule + val instantExecutorRule = InstantTaskExecutorRule() + + @Mock + private lateinit var mRemoteDataSourceImpl: RemoteDataSourceImpl + + private lateinit var locationSearchImp: LocationSearchImp + + private lateinit var locationSearchUseCase: LocationSearchUseCase + + private lateinit var mExploreVM: ExploreViewModel + + override fun setUp() { + super.setUp() + + locationSearchImp = LocationSearchImp(mRemoteDataSourceImpl) + locationSearchUseCase = LocationSearchUseCase(locationSearchImp) + mExploreVM = ExploreViewModel(locationSearchUseCase) + } + + @Test + fun getPlaceSuccess() = runTest { + Mockito.`when`(mRemoteDataSourceImpl.getPlace(anyOrNull(), any())) + .thenAnswer { + val callback: PlaceInterface = it.arguments[1] as PlaceInterface + Responses.GET_PLACE_RESPONSE.let { it1 -> + callback.placeSuccess( + it1 + ) + } + } + + mExploreVM.placeData.test { + mExploreVM.getPlaceData("test") + Assert.assertTrue(TEST_FAILED_DUE_TO_STATE_NOT_LOADING, awaitItem() is HandleResult.Loading) + + val result = awaitItem() + Assert.assertTrue(TEST_FAILED_DUE_TO_STATE_NOT_SUCCESS, result is HandleResult.Success) + val data = (result as HandleResult.Success).response + Assert.assertTrue(TEST_FAILED_DUE_TO_INCORRECT_DATA, data.contacts?.phones?.get(0)?.value == "6666444422") + cancelAndIgnoreRemainingEvents() + } + } + + @Test + fun getPlaceError() = runTest { + Mockito.`when`(mRemoteDataSourceImpl.getPlace(anyOrNull(), any())) + .thenAnswer { + val callback: PlaceInterface = it.arguments[1] as PlaceInterface + callback.placeFailed(DataSourceException.Error("")) + } + + mExploreVM.placeData.test { + mExploreVM.getPlaceData("test") + Assert.assertTrue(TEST_FAILED_DUE_TO_STATE_NOT_LOADING, awaitItem() is HandleResult.Loading) + val result = awaitItem() + Assert.assertTrue(TEST_FAILED_DUE_TO_STATE_NOT_SUCCESS, result is HandleResult.Error) + val data = (result as HandleResult.Error).exception + Assert.assertTrue(TEST_FAILED_DUE_TO_DATA_NOT_EMPTY, data.messageResource == "") + cancelAndIgnoreRemainingEvents() + } + } + + @Test + fun getPlaceInternetError() = runTest { + Mockito.`when`(mRemoteDataSourceImpl.getPlace(anyOrNull(), any())) + .thenAnswer { + val callback: PlaceInterface = it.arguments[1] as PlaceInterface + callback.internetConnectionError(NO_INTERNET_ERROR) + } + + mExploreVM.placeData.test { + mExploreVM.getPlaceData("test") + Assert.assertTrue(TEST_FAILED_DUE_TO_STATE_NOT_LOADING, awaitItem() is HandleResult.Loading) + + val result = awaitItem() + Assert.assertTrue(TEST_FAILED_DUE_TO_STATE_NOT_ERROR, result is HandleResult.Error) + val error = (result as HandleResult.Error).exception.messageResource + Assert.assertTrue(TEST_FAILED_DUE_TO_INCORRECT_NO_INTERNET_ERROR, error == NO_INTERNET_ERROR) + cancelAndIgnoreRemainingEvents() + } + } +}