POST
|
Hi @g3oapp, Similar to iOS SDK, our Android SDK also provides a "PopupView" and "PopupViewModel" through ToolKit to help simplify your popup workflows. The ToolKit for Android including documents and examples can be found in github. The links are listed below. ToolKit: https://developers.arcgis.com/android/toolkit/ Popup toolkit: https://github.com/Esri/arcgis-runtime-toolkit-android/tree/master/arcgis-android-toolkit/src/main/java/com/esri/arcgisruntime/toolkit/popup Doc of Popup toolkit: https://github.com/Esri/arcgis-runtime-toolkit-android/tree/master/Documentation/Popup Example: https://github.com/Esri/arcgis-runtime-toolkit-android/tree/master/toolkit-test-app/src/main/java/com/esri/arcgisruntime/toolkit/test/popup
... View more
08-31-2021
04:51 PM
|
1
|
0
|
1351
|
POST
|
Hello, Thank you for bringing this to our notice. It is a known issue in our SDK when the network security config contains one or more domain-config elements, the platform certificate verification fails with CertificateException and causes the map fails to load. We will address this issue. At the meantime, if you remove the domain-config elements from the network security config, the map loads. If your intention of using the domain-config elements is to trust self-signed certificates you can use this API AuthenticationManager.setSelfSignedCertificateListener instead of the domain-config elements. The following code snippet shows an example. AuthenticationManager.setSelfSignedCertificateListener(SelfSignedCertificateListener { chains, s ->
try {
chains[0].checkValidity()
if (chains[0].issuerDN.name == "xxx") {
return@SelfSignedCertificateListener SelfSignedResponse(true, true)
}
} catch (cee: CertificateExpiredException) {
} catch (cnyve: CertificateNotYetValidException) {
}
null
})
... View more
04-28-2021
04:16 PM
|
0
|
0
|
797
|
POST
|
Hi Aaron, Thanks for reporting the issue. When the server context passed as a parameter to the constructor of CredentialCacheEntry does not have the proper pattern, our SDK will thrown an IllegalArgumentException. However this exception should not cause a crash when setting the persistence. It is a bug. The workaround is to pass in a server context with the right pattern. e.g. the server context of service "https://sampleserver6.arcgisonline.com/arcgis/rest/services/Sync/SaveTheBaySync_secured/MapServer/0" is "sampleserver6.arcgisonline.com/arcgis". One important thing I want to point out is that the example provided by us has not fully utilized the power of SharedPreferencesCredentialPersistence. The following are some Kotlin code snippets of a simpler example. The workflow of this example is: 1. Launch the activity 2. Init_persistence 3. Add_layer, you will be prompted for username/password to access the feature layer 4. Close & relaunch the activity 5. Init_persistence 6. Check_persistence, the feature layer should be displayed without any prompt for credentials Hope this help. Regards, class MainActivity : AppCompatActivity() {
private val TAG = "PersistenceTest"
private val URL_SEPARATOR = "/"
private lateinit var credentialsStore : SharedPreferencesCredentialPersistence
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
AuthenticationManager.setAuthenticationChallengeHandler(
DefaultAuthenticationChallengeHandler(this)
)
val map = ArcGISMap(Basemap.createLightGrayCanvas())
mapView.map = map
}
override fun onCreateOptionsMenu(menu: Menu?): Boolean {
menuInflater.inflate(R.menu.main_activity, menu)
return super.onCreateOptionsMenu(menu)
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
when (item.itemId) {
R.id.check_persistence -> {
displayLayer(getCredentialsFromPersistence("https://sampleserver6.arcgisonline.com/arcgis/rest/services/Sync/SaveTheBaySync_secured/MapServer"))
}
R.id.init_persistence ->
initPersistence()
R.id.add_layer ->
displayLayer(null)
R.id.cleanup -> {
AuthenticationManager.CredentialCache.clear()
mapView.map.operationalLayers.clear()
}
else -> return super.onOptionsItemSelected(item)
}
return true
}
private fun getCredentialsFromPersistence(url: String) : Credential? {
if (credentialsStore.credentials != null) {
Log.i(TAG, "Retrieving credentials from shared persistence ...")
for (entry in credentialsStore.credentials) {
val serverContext = getServerContext(url)
if (entry.serverContext.contentEquals(serverContext)) {
return entry.credential
}
}
}
return null
}
private fun initPersistence() {
val store = AuthenticationManager.CredentialCache.getPersistence()
Log.i(
TAG,
"initPersistence - Current credential cache - \n ${AuthenticationManager.CredentialCache.toJson()}"
)
if ( store == null) {
Log.i(TAG, "Current credential cache persistence is null. Setting a new one ...")
credentialsStore = SharedPreferencesCredentialPersistence(this)
AuthenticationManager.CredentialCache.setPersistence(credentialsStore)
} else {
credentialsStore = store as SharedPreferencesCredentialPersistence
}
}
private fun getServerContext(urlStr: String) : String {
val url = URL(urlStr)
var ret = url.path.toLowerCase()
try {
val pieces = ret.split(URL_SEPARATOR)
ret = url.host.toLowerCase()
return if (pieces.size < 3) {
pieces.joinToString(URL_SEPARATOR, ret)
} else {
if (pieces[1].isNotEmpty() && !pieces[1].startsWith(URL_SEPARATOR)) {
ret = ret.plus(URL_SEPARATOR)
}
ret.plus(pieces[1])
}
} catch (e: java.lang.Exception) {
e.printStackTrace()
}
return ret
}
private fun displayLayer(credential: Credential?) {
Log.i(
TAG,
"displayLayer - Current credential cache - \n ${AuthenticationManager.CredentialCache.toJson()}"
)
val mapImageLayer = ArcGISMapImageLayer(
"https://sampleserver6.arcgisonline.com/arcgis/rest/services/Sync/SaveTheBaySync_secured/MapServer"
)
if (credential != null) {
mapImageLayer.credential = credential
}
mapView.map.operationalLayers.add(mapImageLayer)
mapImageLayer.addDoneLoadingListener {
Log.i(TAG, "displayLayer - load status = ${mapImageLayer.loadStatus}")
}
}
override fun onResume() {
super.onResume()
mapView.resume()
}
override fun onPause() {
super.onPause()
mapView.pause()
}
override fun onDestroy() {
super.onDestroy()
mapView.dispose()
}
}
... View more
01-07-2021
05:47 PM
|
0
|
0
|
1108
|
POST
|
Hi Aaron, Sorry for missing your posts. Have been busy with upcoming release. I will try out the example and let you know if there is a solution and if it is a bug. Regards!
... View more
12-29-2020
06:15 PM
|
0
|
1
|
1128
|
POST
|
Hi Aaron, It is indeed a flaw if the CredentialCache which stores credentials in-memory retains credentials information after an app is closed or forced closed. In our testing the CredentialCache was cleared out when an app was closed and reopened again. We may miss some workflow though. So, could you share your workflow or maybe some code snippets with us so that we can investigate. We are ware of the security risks and concerns of storing credentials in plain texts. We introduced a new class called SharedPreferencesCredentialPersistence which encrypts and stores credentials in SharedPreferences file in 100.9.0. We also plan to provide a default implementation to persist credential cache to a secured storage which should be encrypted in the future. Thanks,
... View more
11-13-2020
04:41 PM
|
0
|
6
|
1282
|
POST
|
Hi Aaron, Could you check if the feature layer fLayer was loaded or was displayed in a MapView before checking the credential cache? Though mAgencyCredentials was set on the service feature table fsTable, if it was not used to load the table or layer successfully, it won't be added to the credential cache. As a side note that the DefaultAuthenticationChallengeHandler also handle secured services including non-federated services. If you are already aware of it or you have special logic which requires a custom challenge handler, please ignore this side note. Regards,
... View more
10-08-2020
05:27 PM
|
0
|
1
|
911
|
POST
|
Hi Eliasz, Thank you for your feedback. We would like to help and understand why token-based authentication didn't work for you. Could you share more info about your server such as version and if it is federated with a server? And could you share some code snippets? Regards,
... View more
06-05-2020
06:06 PM
|
0
|
0
|
1193
|
POST
|
Hello, Not like our 10.x SDK, the third parameter of constructor UserCredential(username, password, referer) in 100.7.0 specifies a referer. The 100.x SDK will figure out the url of generateToken endpoint for you. There is no need to specify it. Could you try to use the other constructor constructor UserCredential(username, password) which takes two parameters? I also want to know what request configuration you set on the following line. Request configuration will impact the request sent to load the layer. dynamicLayer.setRequestConfiguration(requestConfiguration); Generally, setting a credential on a layer of feature table should work in our 100.x and 10.x Android SDKs. But the patterns and workflows do have some changes since 100.1.0 release. For more info on how to work with security with 100.x SDK please check out the following guide doc and samples. Authentication Manger Token Authentication sample Authentication with OAuth IWA Authentication sample Regards,
... View more
05-06-2020
03:29 PM
|
2
|
3
|
1193
|
POST
|
Hi Xinfei, 从程序看你的应用一次就查询feature layer 中所有的记录并遍历它们。我们sdk的ArcGISFeature实行数个事件包括DoneLoading 和 LoadStatusChanged,每个事件都需要一个weak global reference以便之后可以调用它的Callback。由于Android系统只允许在同一时间生成一定数量的weak global reference,当一次查询多个记录比如30000个就会超过系统限制的数量,导致溢出,从而崩溃。 我们建议你一次不要查询过多的记录,你也可以尝试调用ArcGISFeature.close() 去释放你不需要的features占用的资源。关于ArcGISFeature.close() 可以参见这里 Based on your code snippet, you queried all the features in your feature layer which contains about 30000 features in a single queryFeaturesAsync call then iterated through the query result. In our Runtime for Android SDK, an ArcGISFeature implements several events such as DoneLoading and LoadStatusChanged. Each of these events requires the allocation of a weak global reference object to allow a callback to be invoked later. As Android system only allows for a limited number of weak global references to be allocated at a time, querying a large amount of features, e.g. 30000, with a single queryFeatures call will exceed the limit and end up with the weak global reference table overflow. The recommendation would be to limit the number of features queried and held in memory at a time. One more thing you can try is to call ArcGISFeature.close() on the features you don’t need to release the underlying resources. For more info about this method can be found at here.
... View more
03-04-2020
03:49 PM
|
0
|
0
|
601
|
DOC
|
Hi Marin, App login has not been supported by our Android SDK yet. It is still in our to-do list. But I logged an issue to provide and share a workaround with our users at our earliest availability. Thanks for checking with us. Regards, Xueming
... View more
02-13-2020
12:21 PM
|
0
|
0
|
2036
|
POST
|
I just tried on Pixel 3a emulator. The street vector basemap was displayed. Is there a proxy or firewall in your environment?
... View more
01-09-2020
12:36 PM
|
0
|
2
|
2394
|
POST
|
Hristijan, I tied your workflow on my Nexus 6 which is on 7.1.1. The street vector basemap displayed properly. No exception was thrown. Generally, SSLHandshakeException is thrown when accessing a server with self-signed certificate which is not trusted by your app/device. Is the street vector basemap the only layer added to your map? What device did you use? thanks.
... View more
01-09-2020
11:50 AM
|
0
|
4
|
2394
|
POST
|
Hi Aaron, The DefaultAuthenticationChallengeHandler is a class that implements AuthenticationChallengeHandler interface. You can extend it. The following is an example of extending the DefaultAuthenticationChallengeHandler: private class MyDefaultChallengeHandler extends DefaultAuthenticationChallengeHandler {
public MyDefaultChallengeHandler(Activity context) {
super(context);
}
@Override
public AuthenticationChallengeResponse handleChallenge(AuthenticationChallenge challenge) {
if (challenge.getRemoteResource() instanceof Portal) {
// let DefaultAuthenticationChallengeHandler handle this challenge
return super.handleChallenge(challenge);
} else {
// your logic to handle this challenge
......
}
}
}} The add the following line to the onCreate() of your activity instead of implementing AuthenticationChallengeHandler: AuthenticationManager.setAuthenticationChallengeHandler(
new MyDefaultChallengeHandler(this)); Thanks.
... View more
10-07-2019
03:26 PM
|
1
|
2
|
1663
|
POST
|
Hi Aaron, Firing multiple challenges for layers hosted on the same server is known issue in our API and has been planned to be fixed in a future release. Sorry for the inconvenient. To workaround this issue and also take advantage of the DefaultAuthenticationChallengeHandler, your custom challenge handler can extend the DefaultAuthenticationChallengeHandler. In handleChallenge() method of your custom challenge handler, you can return super.handleChallenge() where you want to let the DefaultAuthenticationChallengeHandler to take care of the challenges. For other challenges that you want to take care by yourself use your own logic like what you did in the code that you shared in this thread. Thanks.
... View more
10-07-2019
01:02 PM
|
0
|
0
|
1663
|
POST
|
Hi Simon, Sorry for lacking of appropriate documentation concerning dealing with authentication errors. We have created issues and will endeavor to improve our documentation in future releases. Starting from 100.0, errors thrown from our SDK are wrapped in a class called ArcGISRuntimeException. More specific information of where the error occurred and what caused it are provided via getErrorDomain(), getErrorCode() and getCause(). In regard to obtaining detailed error message when an authentication fails, you can work with ArcGISRuntimeException in the similar way as EsriSecurityException as shown in the following code snippet: Throwable cause = error.getCause();
if (cause == null) {
Log.i(tag, "error without a cause - code: " + error.getErrorCode() + "; " + error.getMessage());
} else {
if (cause instanceof JsonEmbeddedException) {
JsonEmbeddedException jsonError = (JsonEmbeddedException) cause;
Log.i(tag, "error with cause: JsonEmbeddedException - code: " + jsonError.getCode()
+ " message: " + jsonError.getMessage());
} else {
Log.i(tag, "error with cause: " + cause.getClass().getSimpleName() + " - code: " + error.getErrorCode()
+ " message: " + cause.getMessage());
}
} The followings are some example printout from the above code snippet in some error cases: 1. Passing wrong credential to a token-based portal/layer error with cause: JsonEmbeddedException - code: 400 message: Unable to generate token. 2. Passing wrong credential to aN IWA portal/layer error with cause: HttpResponseException - code: 22 message: status code: 401, reason phrase: Unauthorized: Access is denied due to invalid credentials. Please share more info regarding your workflow if you still have problem in handling error message. Thanks,
... View more
09-23-2019
12:35 PM
|
1
|
0
|
494
|
Title | Kudos | Posted |
---|---|---|
1 | 08-31-2021 04:51 PM | |
1 | 09-23-2019 12:35 PM | |
1 | 10-07-2019 03:26 PM | |
2 | 05-06-2020 03:29 PM | |
1 | 08-21-2019 03:19 PM |
Online Status |
Offline
|
Date Last Visited |
02-22-2024
02:26 AM
|