Access services with OAuth 2.0

Learn how to authenticate a user to access a secure ArcGIS service with OAuth 2.0.

access services with oauth 2

In this tutorial, you will build an app that uses named user login credentials to access a secure ArcGIS service using OAuth 2.0.

You can use different authentication methods to access ArcGIS location services. To implement OAuth 2.0, you can use your ArcGIS account to register an application and get a client ID, and then configure your app to redirect users to login with their credentials when the service or content is accessed. This is known as "named user" or ArcGIS identity authentication. If the app uses premium services that consume credits, the app user's account will be charged.

You will implement OAuth 2.0 so users can sign in to ArcGIS to access the ArcGIS World Traffic service.

Prerequisites

The following are required for this tutorial:

  1. An ArcGIS account to access API keys. If you don't have an account, sign up for free.
  2. Confirm that your system meets the system requirements.
  3. An IDE for Android development in Kotlin.

Steps

Configure OAuth 2.0 for your app

Use the ArcGIS Developer dashboard to create an application, generate a client ID, and define a redirect URL to access secure services.

  1. Sign in to your ArcGIS developer account. If you don't already have one, sign-up for free. You need to sign in so you can create an application and get a client ID for authentication.
  2. Click the OAuth 2.0 tab in the ribbon at the top.
  3. Click the New Application button in the upper-left of the page.
  4. In the Create New Application window, provide a Name and an optional Description for your application definition. Then click Create application. When the application is created, Client ID, Client Secret, and Temporary Token values will also be generated.
  5. Click the Add URI button at the bottom of the page to add a redirect URL.
  6. In the Add Allowed URI window, type authenticate-with-oauth://auth and click Add.
You'll use the `client ID` and `redirect URL` when implementing OAuth in your app's code.

The client ID uniquely identifies your app on the authenticating server. If the server cannot find an app with the provided client ID, it will not proceed with authentication.

The redirect URL is used to identify a response from the authenticating server when the system returns control back to your app after an OAuth 2.0 login. You can configure several redirect URLs in your application definition and can remove or edit them. It's important to make sure the redirect URL used in your app's code matches a redirect URL configured for the application.

A temporary token can be used to test access to secure resources without having to implement the full OAuth workflow.

The client secret is only needed in some OAuth workflows and will not be used in this tutorial.

Open an Android Studio project with Gradle

  1. To start this tutorial, complete the Display a map tutorial. Or download and unzip the Display a map solution in a new folder.

  2. Modify the old project for use in this new tutorial. Expand More info for instructions.

    1. On your file system, delete the .idea folder, if present, at the top level of your project.

    2. In the Android tool window, open app > res > values > strings.xml.

      In the <string name="app_name"> element, change the text content to Access services with OAuth 2.0.

      strings.xml
      Use dark colors for code blocks
      1 2 3 4 4 4 4 4 5
      Change line
      1
      2
      3
      4
      5
      6
      7
      8
      9
      <resources>
      
          <string name="app_name">Access services with OAuth 2.0</string>
      
          <string name="oauth_client_id">"YOUR_CLIENT_ID"</string>
          <string name="oauth_redirect_host">"YOUR_REDIRECT_HOST"</string>
          <string name="oauth_redirect_uri">"YOUR_REDIRECT_URI"</string>
      
      </resources>
    3. In the Android tool window, open Gradle Scripts > settings.gradle.

      Change the value of rootProject.name to "Access services with OAuth 2.0".

      settings.gradle
      Use dark colors for code blocks
      1 2 3
      Change line
      1
      2
      3
      include ':app'
      
      rootProject.name = "Access services with OAuth 2.0"
    4. Click File > Sync Project with Gradle files. Android Studio will recognize your changes and create a new .idea folder.

  3. Delete the code that sets your API key. Since your app will be using OAuth, you will not need an API Key.

    MainActivity.kt
    Use dark colors for code blocks
    60 60 60 60 60 60 60 60 60 60 60 60 60 60 60 60 60 60 60 60 60 60 60 60 60 60 60 60 60 60 60 60 60 60 60 60 60 60 60 60 60 60 60 60 60 60 60 60 60 60 60 60 60 60 60 60 60 60 60 60 61 62 63 64 65 66 67 68 69 70 71 72 73 73 73 73 73 73 73 73 73 73 73 73 73 73 73 73 73
    Remove lineRemove lineRemove lineRemove lineRemove lineRemove lineRemove lineRemove line
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    /*
     * Copyright 2020 Esri
     *
     * Licensed under the Apache License, Version 2.0 (the "License");
     * you may not use this file except in compliance with the License.
     * You may obtain a copy of the License at
     *
     *    https://www.apache.org/licenses/LICENSE-2.0
     *
     * Unless required by applicable law or agreed to in writing, software
     * distributed under the License is distributed on an "AS IS" BASIS,
     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     * See the License for the specific language governing permissions and
     * limitations under the License.
     */
    
    package com.example.app
    
    import android.os.Bundle
    import androidx.appcompat.app.AppCompatActivity
    
    import com.esri.arcgisruntime.ArcGISRuntimeEnvironment
    import com.esri.arcgisruntime.mapping.ArcGISMap
    import com.esri.arcgisruntime.mapping.BasemapStyle
    import com.esri.arcgisruntime.mapping.Viewpoint
    import com.esri.arcgisruntime.mapping.view.MapView
    
    import com.example.app.databinding.ActivityMainBinding
    
    class MainActivity : AppCompatActivity() {
    
        private val activityMainBinding by lazy {
          ActivityMainBinding.inflate(layoutInflater)
        }
    
        private val mapView: MapView by lazy {
          activityMainBinding.mapView
        }
    
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
    
            setContentView(activityMainBinding.root)
    
            setApiKeyForApp()
    
            setupMap()
    
        }
    
        override fun onPause() {
            mapView.pause()
            super.onPause()
        }
    
        override fun onResume() {
            super.onResume()
            mapView.resume()
        }
    
        override fun onDestroy() {
            mapView.dispose()
            super.onDestroy()
        }
    
        private fun setApiKeyForApp(){
            // set your API key
            // Note: it is not best practice to store API keys in source code. The API key is referenced
            // here for the convenience of this tutorial.
    
            ArcGISRuntimeEnvironment.setApiKey("YOUR_API_KEY")
    
        }
    
        // set up your map here. You will call this method from onCreate()
        private fun setupMap() {
    
            // create a map with the BasemapStyle streets
            val map = ArcGISMap(BasemapStyle.ARCGIS_TOPOGRAPHIC)
    
            // set the map to be displayed in the layout's MapView
            mapView.map = map
    
            // set the viewpoint, Viewpoint(latitude, longitude, scale)
            mapView.setViewpoint(Viewpoint(34.0270, -118.8050, 72000.0))
    
        }
    
    }
    
    MainActivity.kt
    Use dark colors for code blocks
    39 39 39 39 39 39 39 39 39 39 39 39 39 39 39 39 39 39 39 39 39 39 39 39 39 39 39 39 39 39 39 39 39 39 39 39 39 39 39 40 41 42 43 44 45 46 47 48 49 49 49 49 49 49 49 49 49 49 49 49 49 49 49 49 49 49 49 49 49 49 49 49 49 49 49 49 49 49 49 49 49 49 49 49 49 49 49 49 49
    Remove line
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    /*
     * Copyright 2020 Esri
     *
     * Licensed under the Apache License, Version 2.0 (the "License");
     * you may not use this file except in compliance with the License.
     * You may obtain a copy of the License at
     *
     *    https://www.apache.org/licenses/LICENSE-2.0
     *
     * Unless required by applicable law or agreed to in writing, software
     * distributed under the License is distributed on an "AS IS" BASIS,
     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     * See the License for the specific language governing permissions and
     * limitations under the License.
     */
    
    package com.example.app
    
    import android.os.Bundle
    import androidx.appcompat.app.AppCompatActivity
    
    import com.esri.arcgisruntime.ArcGISRuntimeEnvironment
    import com.esri.arcgisruntime.mapping.ArcGISMap
    import com.esri.arcgisruntime.mapping.BasemapStyle
    import com.esri.arcgisruntime.mapping.Viewpoint
    import com.esri.arcgisruntime.mapping.view.MapView
    
    import com.example.app.databinding.ActivityMainBinding
    
    class MainActivity : AppCompatActivity() {
    
        private val activityMainBinding by lazy {
          ActivityMainBinding.inflate(layoutInflater)
        }
    
        private val mapView: MapView by lazy {
          activityMainBinding.mapView
        }
    
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
    
            setContentView(activityMainBinding.root)
    
            setApiKeyForApp()
    
            setupMap()
    
        }
    
        override fun onPause() {
            mapView.pause()
            super.onPause()
        }
    
        override fun onResume() {
            super.onResume()
            mapView.resume()
        }
    
        override fun onDestroy() {
            mapView.dispose()
            super.onDestroy()
        }
    
        private fun setApiKeyForApp(){
            // set your API key
            // Note: it is not best practice to store API keys in source code. The API key is referenced
            // here for the convenience of this tutorial.
    
            ArcGISRuntimeEnvironment.setApiKey("YOUR_API_KEY")
    
        }
    
        // set up your map here. You will call this method from onCreate()
        private fun setupMap() {
    
            // create a map with the BasemapStyle streets
            val map = ArcGISMap(BasemapStyle.ARCGIS_TOPOGRAPHIC)
    
            // set the map to be displayed in the layout's MapView
            mapView.map = map
    
            // set the viewpoint, Viewpoint(latitude, longitude, scale)
            mapView.setViewpoint(Viewpoint(34.0270, -118.8050, 72000.0))
    
        }
    
    }
    

Prepare files before coding the app

Modify the files from the Display a map tutorial so they can be used in this tutorial: you will add imports, modify the view point's scale, define strings for OAuth client ID and redirect URI, and add an Android activity for the default OAuth intent receiver.

  1. In the Project tool window, make sure that the Android view is displayed. Open app/java/com.example.app, and click MainActivity.kt. Add the following imports, replacing those from the Display a map tutorial.

    MainActivity.kt
    Use dark colors for code blocks
                                                                                                                                    
    Change lineChange lineChange lineChange lineChange lineChange lineChange lineChange lineChange lineChange lineChange lineChange lineChange lineChange lineChange lineChange line
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    /*
     * Copyright 2020 Esri
     *
     * Licensed under the Apache License, Version 2.0 (the "License")
     * you may not use this file except in compliance with the License.
     * You may obtain a copy of the License at
     *
     *    http://www.apache.org/licenses/LICENSE-2.0
     *
     * Unless required by applicable law or agreed to in writing, software
     * distributed under the License is distributed on an "AS IS" BASIS,
     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     * See the License for the specific language governing permissions and
     * limitations under the License.
     *
     */
    
    package com.example.app
    
    import android.os.Bundle
    import android.util.Log
    import android.widget.Toast
    import androidx.appcompat.app.AppCompatActivity
    
    import com.esri.arcgisruntime.layers.ArcGISMapImageLayer
    import com.esri.arcgisruntime.mapping.ArcGISMap
    import com.esri.arcgisruntime.mapping.BasemapStyle
    import com.esri.arcgisruntime.mapping.Viewpoint
    import com.esri.arcgisruntime.mapping.view.MapView
    import com.esri.arcgisruntime.security.AuthenticationManager
    import com.esri.arcgisruntime.security.DefaultAuthenticationChallengeHandler
    import com.esri.arcgisruntime.security.OAuthConfiguration
    import com.example.app.databinding.ActivityMainBinding
    
    import java.net.MalformedURLException
    
    
    class MainActivity : AppCompatActivity() {
    
      companion object {
        private val TAG: String = MainActivity::class.java.simpleName
      }
    
      private val activityMainBinding by lazy {
        ActivityMainBinding.inflate(layoutInflater)
      }
    
      private val mapView: MapView by lazy {
        activityMainBinding.mapView
      }
    
      override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
    
        setContentView(activityMainBinding.root)
    
        setupMap()
    
      }
    
      override fun onResume() {
        super.onResume()
        mapView.resume()
      }
    
      override fun onPause() {
        mapView.pause()
        super.onPause()
      }
    
      override fun onDestroy() {
        mapView.dispose()
        super.onDestroy()
      }
    
      private fun setupMap() {
    
        try {
    
          val map = ArcGISMap(BasemapStyle.ARCGIS_TOPOGRAPHIC)
    
          mapView.map = map
    
          mapView.setViewpoint(Viewpoint(34.02700, -118.80543, 72000.0))
    
          // set up an oauth config with url to portal, a client id and a re-direct url
          // a custom client id for your app can be set on the ArcGIS for Developers dashboard under
          // Authentication --> Redirect URIs
          val oAuthConfiguration = OAuthConfiguration(
            null, getString(R.string.oauth_client_id),
            getString(R.string.oauth_redirect_uri) + "://" + getString(R.string.oauth_redirect_host)
          )
    
          // setup AuthenticationManager to handle auth challenges
          val defaultAuthenticationChallengeHandler = DefaultAuthenticationChallengeHandler(this)
    
          // use the DefaultChallengeHandler to handle authentication challenges
          AuthenticationManager.setAuthenticationChallengeHandler(
            defaultAuthenticationChallengeHandler
          )
    
          // add an OAuth configuration
          // NOTE: you must add the DefaultOAuthIntentReceiver Activity to the app's manifest to handle starting a browser
          AuthenticationManager.addOAuthConfiguration(oAuthConfiguration)
    
          val trafficLayer = ArcGISMapImageLayer("https://traffic.arcgis.com/arcgis/rest/services/World/Traffic/MapServer")
          map.operationalLayers.add(trafficLayer)
    
        }
    
        catch (e: MalformedURLException) {
          logError("Error in OAuthConfiguration URL: " + e.message)
        }
    
      }
    
      /**
       * Log an error to logcat and to the screen via Toast.
       * @param message the text to log.
       */
      private fun logError(message: String?) {
        message?.let {
          Log.e(TAG, message)
          Toast.makeText(this, message, Toast.LENGTH_LONG).show()
        }
      }
    
    }
    
  2. Change the scale of the map's initial viewpoint to 72000.0. This scale will make the secured layer visible without zooming in.

    MainActivity.kt
    Use dark colors for code blocks
                                                                                                                                    
    Change line
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    /*
     * Copyright 2020 Esri
     *
     * Licensed under the Apache License, Version 2.0 (the "License")
     * you may not use this file except in compliance with the License.
     * You may obtain a copy of the License at
     *
     *    http://www.apache.org/licenses/LICENSE-2.0
     *
     * Unless required by applicable law or agreed to in writing, software
     * distributed under the License is distributed on an "AS IS" BASIS,
     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     * See the License for the specific language governing permissions and
     * limitations under the License.
     *
     */
    
    package com.example.app
    
    import android.os.Bundle
    import android.util.Log
    import android.widget.Toast
    import androidx.appcompat.app.AppCompatActivity
    
    import com.esri.arcgisruntime.layers.ArcGISMapImageLayer
    import com.esri.arcgisruntime.mapping.ArcGISMap
    import com.esri.arcgisruntime.mapping.BasemapStyle
    import com.esri.arcgisruntime.mapping.Viewpoint
    import com.esri.arcgisruntime.mapping.view.MapView
    import com.esri.arcgisruntime.security.AuthenticationManager
    import com.esri.arcgisruntime.security.DefaultAuthenticationChallengeHandler
    import com.esri.arcgisruntime.security.OAuthConfiguration
    import com.example.app.databinding.ActivityMainBinding
    
    import java.net.MalformedURLException
    
    
    class MainActivity : AppCompatActivity() {
    
      companion object {
        private val TAG: String = MainActivity::class.java.simpleName
      }
    
      private val activityMainBinding by lazy {
        ActivityMainBinding.inflate(layoutInflater)
      }
    
      private val mapView: MapView by lazy {
        activityMainBinding.mapView
      }
    
      override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
    
        setContentView(activityMainBinding.root)
    
        setupMap()
    
      }
    
      override fun onResume() {
        super.onResume()
        mapView.resume()
      }
    
      override fun onPause() {
        mapView.pause()
        super.onPause()
      }
    
      override fun onDestroy() {
        mapView.dispose()
        super.onDestroy()
      }
    
      private fun setupMap() {
    
        try {
    
          val map = ArcGISMap(BasemapStyle.ARCGIS_TOPOGRAPHIC)
    
          mapView.map = map
    
          mapView.setViewpoint(Viewpoint(34.02700, -118.80543, 72000.0))
    
          // set up an oauth config with url to portal, a client id and a re-direct url
          // a custom client id for your app can be set on the ArcGIS for Developers dashboard under
          // Authentication --> Redirect URIs
          val oAuthConfiguration = OAuthConfiguration(
            null, getString(R.string.oauth_client_id),
            getString(R.string.oauth_redirect_uri) + "://" + getString(R.string.oauth_redirect_host)
          )
    
          // setup AuthenticationManager to handle auth challenges
          val defaultAuthenticationChallengeHandler = DefaultAuthenticationChallengeHandler(this)
    
          // use the DefaultChallengeHandler to handle authentication challenges
          AuthenticationManager.setAuthenticationChallengeHandler(
            defaultAuthenticationChallengeHandler
          )
    
          // add an OAuth configuration
          // NOTE: you must add the DefaultOAuthIntentReceiver Activity to the app's manifest to handle starting a browser
          AuthenticationManager.addOAuthConfiguration(oAuthConfiguration)
    
          val trafficLayer = ArcGISMapImageLayer("https://traffic.arcgis.com/arcgis/rest/services/World/Traffic/MapServer")
          map.operationalLayers.add(trafficLayer)
    
        }
    
        catch (e: MalformedURLException) {
          logError("Error in OAuthConfiguration URL: " + e.message)
        }
    
      }
    
      /**
       * Log an error to logcat and to the screen via Toast.
       * @param message the text to log.
       */
      private fun logError(message: String?) {
        message?.let {
          Log.e(TAG, message)
          Toast.makeText(this, message, Toast.LENGTH_LONG).show()
        }
      }
    
    }
    
  3. Open app/res/values and click strings.xml. Enter certain values that you specified while creating your OAuth configuration in Configure OAuth 2.0 for your app above.

    To find these values, log in to the Dashboard with your developer account, click the OAuth 2.0 tab, scroll to the OAuth configuration you created (It will have a name such as "Access Services OAuth2".) and click View Full Credentails.

    In string.xml define strings for the OAuth client ID, redirect host, and redirect uri. Replace the placeholder strings with values from your actual OAuth configuration. The replacement values are not quoted.

    • "YOUR_CLIENT_ID": Replace with the Client ID displayed in your dashboard.

    • "YOUR_REDIRECT_HOST": The host name portion of your redirect URI. Replace the placeholder with the name that follows //: in the Redirect URLS section in the dashboard. Above, we suggested using auth.

    • "YOUR_REDIRECT_URI": The protocol identifier of your redirect URI. Replace the placeholder with the name that precedes //: in the Redirect URLs section in the dashboard. This must be authenticate-with-oauth.

      strings.xml
      Use dark colors for code blocks
               
      Add line.Add line.Add line.
      1
      2
      3
      4
      5
      6
      7
      8
      9
      <resources>
      
          <string name="app_name">Access services with OAuth 2.0</string>
      
          <string name="oauth_client_id">"YOUR_CLIENT_ID"</string>
          <string name="oauth_redirect_host">"YOUR_REDIRECT_HOST"</string>
          <string name="oauth_redirect_uri">"YOUR_REDIRECT_URI"</string>
      
      </resources>
  4. Open app/manifests and click AndroidManifest.xml. Add an activity for the default OAuth intent receiver.

    AndroidManifest.xml
    Use dark colors for code blocks
                                                    
    Add line.Add line.Add line.Add line.Add line.Add line.Add line.Add line.Add line.Add line.Add line.Add line.Add line.Add line.Add line.Add line.
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    <?xml version="1.0" encoding="utf-8"?>
    <manifest xmlns:android="http://schemas.android.com/apk/res/android"
        package="com.example.app">
    
        <uses-permission android:name="android.permission.INTERNET" />
    
        <uses-feature
            android:glEsVersion="0x00020000"
            android:required="true" />
    
        <application
            android:allowBackup="true"
            android:icon="@mipmap/ic_launcher"
            android:label="@string/app_name"
            android:roundIcon="@mipmap/ic_launcher_round"
            android:supportsRtl="true"
            android:theme="@style/AppTheme">
            <activity
                android:exported="true"
                android:name=".MainActivity"
                android:label="@string/app_name">
                <intent-filter>
                    <action android:name="android.intent.action.MAIN" />
    
                    <category android:name="android.intent.category.LAUNCHER" />
                </intent-filter>
            </activity>
    
            <!-- You must declare this activity, an intent receiver, to display the OAuth login -->
            <activity
                android:exported="true"
                android:name="com.esri.arcgisruntime.security.DefaultOAuthIntentReceiver"
                android:launchMode="singleTask">
                <intent-filter>
                    <action android:name="android.intent.action.VIEW" />
    
                    <category android:name="android.intent.category.DEFAULT" />
                    <category android:name="android.intent.category.BROWSABLE" />
    
                    <data
                        android:host="@string/oauth_redirect_host"
                        android:scheme="authenticate-with-oauth" />
                </intent-filter>
            </activity>
    
        </application>
    
    </manifest>

Implement OAuth 2.0 authentication

ArcGIS Runtime provides an API that abstracts some of the details for OAuth 2.0 authentication in your app. You can use classes like AuthenticationManager to request, store, and manage credentials for secure resources.

Add code to set up the AuthenticationManager, which launches a browser window that challenges the user for log-in credentials.

  1. In the setupMap() function, after the line that calls mapView.setViewpoint(), create an OAuthConfiguration.

    MainActivity.kt
    Use dark colors for code blocks
                                                                                                                                    
    Add line.Add line.Add line.Add line.Add line.Add line.Add line.
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    /*
     * Copyright 2020 Esri
     *
     * Licensed under the Apache License, Version 2.0 (the "License")
     * you may not use this file except in compliance with the License.
     * You may obtain a copy of the License at
     *
     *    http://www.apache.org/licenses/LICENSE-2.0
     *
     * Unless required by applicable law or agreed to in writing, software
     * distributed under the License is distributed on an "AS IS" BASIS,
     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     * See the License for the specific language governing permissions and
     * limitations under the License.
     *
     */
    
    package com.example.app
    
    import android.os.Bundle
    import android.util.Log
    import android.widget.Toast
    import androidx.appcompat.app.AppCompatActivity
    
    import com.esri.arcgisruntime.layers.ArcGISMapImageLayer
    import com.esri.arcgisruntime.mapping.ArcGISMap
    import com.esri.arcgisruntime.mapping.BasemapStyle
    import com.esri.arcgisruntime.mapping.Viewpoint
    import com.esri.arcgisruntime.mapping.view.MapView
    import com.esri.arcgisruntime.security.AuthenticationManager
    import com.esri.arcgisruntime.security.DefaultAuthenticationChallengeHandler
    import com.esri.arcgisruntime.security.OAuthConfiguration
    import com.example.app.databinding.ActivityMainBinding
    
    import java.net.MalformedURLException
    
    
    class MainActivity : AppCompatActivity() {
    
      companion object {
        private val TAG: String = MainActivity::class.java.simpleName
      }
    
      private val activityMainBinding by lazy {
        ActivityMainBinding.inflate(layoutInflater)
      }
    
      private val mapView: MapView by lazy {
        activityMainBinding.mapView
      }
    
      override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
    
        setContentView(activityMainBinding.root)
    
        setupMap()
    
      }
    
      override fun onResume() {
        super.onResume()
        mapView.resume()
      }
    
      override fun onPause() {
        mapView.pause()
        super.onPause()
      }
    
      override fun onDestroy() {
        mapView.dispose()
        super.onDestroy()
      }
    
      private fun setupMap() {
    
        try {
    
          val map = ArcGISMap(BasemapStyle.ARCGIS_TOPOGRAPHIC)
    
          mapView.map = map
    
          mapView.setViewpoint(Viewpoint(34.02700, -118.80543, 72000.0))
    
          // set up an oauth config with url to portal, a client id and a re-direct url
          // a custom client id for your app can be set on the ArcGIS for Developers dashboard under
          // Authentication --> Redirect URIs
          val oAuthConfiguration = OAuthConfiguration(
            null, getString(R.string.oauth_client_id),
            getString(R.string.oauth_redirect_uri) + "://" + getString(R.string.oauth_redirect_host)
          )
    
          // setup AuthenticationManager to handle auth challenges
          val defaultAuthenticationChallengeHandler = DefaultAuthenticationChallengeHandler(this)
    
          // use the DefaultChallengeHandler to handle authentication challenges
          AuthenticationManager.setAuthenticationChallengeHandler(
            defaultAuthenticationChallengeHandler
          )
    
          // add an OAuth configuration
          // NOTE: you must add the DefaultOAuthIntentReceiver Activity to the app's manifest to handle starting a browser
          AuthenticationManager.addOAuthConfiguration(oAuthConfiguration)
    
          val trafficLayer = ArcGISMapImageLayer("https://traffic.arcgis.com/arcgis/rest/services/World/Traffic/MapServer")
          map.operationalLayers.add(trafficLayer)
    
        }
    
        catch (e: MalformedURLException) {
          logError("Error in OAuthConfiguration URL: " + e.message)
        }
    
      }
    
      /**
       * Log an error to logcat and to the screen via Toast.
       * @param message the text to log.
       */
      private fun logError(message: String?) {
        message?.let {
          Log.e(TAG, message)
          Toast.makeText(this, message, Toast.LENGTH_LONG).show()
        }
      }
    
    }
    

    The OAuthConfiguration constructor takes three parameters:

    • R.string.oauth_client_id: the URL for the ArcGIS Online organization that is associated with the developer account you used in Configure OAuth 2.0 for your app above. The format of the URL will be something like: https://www.myfirstname-mylastname.maps.arcgis.com.

    • "R.string.oauth_client_id": the Client ID from your OAuth 2.0 configuration.

    • "R.string.oauth_redirect_uri" + "R.string.oauth_redirect_host": the protocol identifier and host name of the redirect uri from your OAuth 2.0 configuration.

  2. Create a DefaultAuthenticationChallengeHandler and set it on the AuthenticationManager. Then add the oAuthConfiguration to the AuthenticationManager.

    MainActivity.kt
    Use dark colors for code blocks
                                                                                                                                    
    Add line.Add line.Add line.Add line.Add line.Add line.Add line.Add line.Add line.Add line.Add line.
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    /*
     * Copyright 2020 Esri
     *
     * Licensed under the Apache License, Version 2.0 (the "License")
     * you may not use this file except in compliance with the License.
     * You may obtain a copy of the License at
     *
     *    http://www.apache.org/licenses/LICENSE-2.0
     *
     * Unless required by applicable law or agreed to in writing, software
     * distributed under the License is distributed on an "AS IS" BASIS,
     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     * See the License for the specific language governing permissions and
     * limitations under the License.
     *
     */
    
    package com.example.app
    
    import android.os.Bundle
    import android.util.Log
    import android.widget.Toast
    import androidx.appcompat.app.AppCompatActivity
    
    import com.esri.arcgisruntime.layers.ArcGISMapImageLayer
    import com.esri.arcgisruntime.mapping.ArcGISMap
    import com.esri.arcgisruntime.mapping.BasemapStyle
    import com.esri.arcgisruntime.mapping.Viewpoint
    import com.esri.arcgisruntime.mapping.view.MapView
    import com.esri.arcgisruntime.security.AuthenticationManager
    import com.esri.arcgisruntime.security.DefaultAuthenticationChallengeHandler
    import com.esri.arcgisruntime.security.OAuthConfiguration
    import com.example.app.databinding.ActivityMainBinding
    
    import java.net.MalformedURLException
    
    
    class MainActivity : AppCompatActivity() {
    
      companion object {
        private val TAG: String = MainActivity::class.java.simpleName
      }
    
      private val activityMainBinding by lazy {
        ActivityMainBinding.inflate(layoutInflater)
      }
    
      private val mapView: MapView by lazy {
        activityMainBinding.mapView
      }
    
      override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
    
        setContentView(activityMainBinding.root)
    
        setupMap()
    
      }
    
      override fun onResume() {
        super.onResume()
        mapView.resume()
      }
    
      override fun onPause() {
        mapView.pause()
        super.onPause()
      }
    
      override fun onDestroy() {
        mapView.dispose()
        super.onDestroy()
      }
    
      private fun setupMap() {
    
        try {
    
          val map = ArcGISMap(BasemapStyle.ARCGIS_TOPOGRAPHIC)
    
          mapView.map = map
    
          mapView.setViewpoint(Viewpoint(34.02700, -118.80543, 72000.0))
    
          // set up an oauth config with url to portal, a client id and a re-direct url
          // a custom client id for your app can be set on the ArcGIS for Developers dashboard under
          // Authentication --> Redirect URIs
          val oAuthConfiguration = OAuthConfiguration(
            null, getString(R.string.oauth_client_id),
            getString(R.string.oauth_redirect_uri) + "://" + getString(R.string.oauth_redirect_host)
          )
    
          // setup AuthenticationManager to handle auth challenges
          val defaultAuthenticationChallengeHandler = DefaultAuthenticationChallengeHandler(this)
    
          // use the DefaultChallengeHandler to handle authentication challenges
          AuthenticationManager.setAuthenticationChallengeHandler(
            defaultAuthenticationChallengeHandler
          )
    
          // add an OAuth configuration
          // NOTE: you must add the DefaultOAuthIntentReceiver Activity to the app's manifest to handle starting a browser
          AuthenticationManager.addOAuthConfiguration(oAuthConfiguration)
    
          val trafficLayer = ArcGISMapImageLayer("https://traffic.arcgis.com/arcgis/rest/services/World/Traffic/MapServer")
          map.operationalLayers.add(trafficLayer)
    
        }
    
        catch (e: MalformedURLException) {
          logError("Error in OAuthConfiguration URL: " + e.message)
        }
    
      }
    
      /**
       * Log an error to logcat and to the screen via Toast.
       * @param message the text to log.
       */
      private fun logError(message: String?) {
        message?.let {
          Log.e(TAG, message)
          Toast.makeText(this, message, Toast.LENGTH_LONG).show()
        }
      }
    
    }
    

Add a traffic layer

You will add a layer to display the ArcGIS World Traffic service, a dynamic map service that presents historical and near real-time traffic information for different regions in the world. This service requires an ArcGIS Online organizational subscription.

ArcGIS World Traffic service data is updated every five minutes to provide traffic speed and traffic incident visualization and identification.

Traffic speeds are displayed as a percentage of free-flow speeds, which is frequently the speed limit or how fast cars tend to travel when unencumbered by other vehicles. The streets are color coded as follows:

  • Green (fast): 85 - 100% of free flow speeds
  • Yellow (moderate): 65 - 85%
  • Orange (slow); 45 - 65%
  • Red (stop and go): 0 - 45%

Finally, you will add error code that catches a malformed URL and displays a Toast with the error message.

  1. Create an ArcGISMapImageLayer to display the traffic service. Then add the layer to the map's collection of data layers (operational layers).

    MainActivity.kt
    Use dark colors for code blocks
    101 101 101 101 101 101 101 101 101 101 101 101 101 101 101 101 101 101 101 101 101 101 101 101 101 101 101 101 101 101 101 101 101 101 101 101 101 101 101 101 101 101 101 101 101 101 101 101 101 101 101 101 101 101 101 101 101 101 101 101 101 101 101 101 101 101 101 101 101 101 101 101 101 101 101 101 101 100 99 99 99 99 99 99 99 99 99 99 99 99 99 99 99 99 99 99 99 99 99 99 99 100 101 102 103 104 105 105 104 103 102 101 100 99 99 99 98 97 96 95 94 93 92 91 90 89 88 88
    Add line.Add line.
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    /*
     * Copyright 2020 Esri
     *
     * Licensed under the Apache License, Version 2.0 (the "License")
     * you may not use this file except in compliance with the License.
     * You may obtain a copy of the License at
     *
     *    http://www.apache.org/licenses/LICENSE-2.0
     *
     * Unless required by applicable law or agreed to in writing, software
     * distributed under the License is distributed on an "AS IS" BASIS,
     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     * See the License for the specific language governing permissions and
     * limitations under the License.
     *
     */
    
    package com.example.app
    
    import android.os.Bundle
    import android.util.Log
    import android.widget.Toast
    import androidx.appcompat.app.AppCompatActivity
    
    import com.esri.arcgisruntime.layers.ArcGISMapImageLayer
    import com.esri.arcgisruntime.mapping.ArcGISMap
    import com.esri.arcgisruntime.mapping.BasemapStyle
    import com.esri.arcgisruntime.mapping.Viewpoint
    import com.esri.arcgisruntime.mapping.view.MapView
    import com.esri.arcgisruntime.security.AuthenticationManager
    import com.esri.arcgisruntime.security.DefaultAuthenticationChallengeHandler
    import com.esri.arcgisruntime.security.OAuthConfiguration
    import com.example.app.databinding.ActivityMainBinding
    
    import java.net.MalformedURLException
    
    
    class MainActivity : AppCompatActivity() {
    
      companion object {
        private val TAG: String = MainActivity::class.java.simpleName
      }
    
      private val activityMainBinding by lazy {
        ActivityMainBinding.inflate(layoutInflater)
      }
    
      private val mapView: MapView by lazy {
        activityMainBinding.mapView
      }
    
      override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
    
        setContentView(activityMainBinding.root)
    
        setupMap()
    
      }
    
      override fun onResume() {
        super.onResume()
        mapView.resume()
      }
    
      override fun onPause() {
        mapView.pause()
        super.onPause()
      }
    
      override fun onDestroy() {
        mapView.dispose()
        super.onDestroy()
      }
    
      private fun setupMap() {
    
        try {
    
          val map = ArcGISMap(BasemapStyle.ARCGIS_TOPOGRAPHIC)
    
          mapView.map = map
    
          mapView.setViewpoint(Viewpoint(34.02700, -118.80543, 72000.0))
    
          // set up an oauth config with url to portal, a client id and a re-direct url
          // a custom client id for your app can be set on the ArcGIS for Developers dashboard under
          // Authentication --> Redirect URIs
          val oAuthConfiguration = OAuthConfiguration(
            null, getString(R.string.oauth_client_id),
            getString(R.string.oauth_redirect_uri) + "://" + getString(R.string.oauth_redirect_host)
          )
    
          // setup AuthenticationManager to handle auth challenges
          val defaultAuthenticationChallengeHandler = DefaultAuthenticationChallengeHandler(this)
    
          // use the DefaultChallengeHandler to handle authentication challenges
          AuthenticationManager.setAuthenticationChallengeHandler(
            defaultAuthenticationChallengeHandler
          )
    
          // add an OAuth configuration
          // NOTE: you must add the DefaultOAuthIntentReceiver Activity to the app's manifest to handle starting a browser
          AuthenticationManager.addOAuthConfiguration(oAuthConfiguration)
    
          val trafficLayer = ArcGISMapImageLayer("https://traffic.arcgis.com/arcgis/rest/services/World/Traffic/MapServer")
          map.operationalLayers.add(trafficLayer)
    
        }
    
        catch (e: MalformedURLException) {
          logError("Error in OAuthConfiguration URL: " + e.message)
        }
    
      }
    
      /**
       * Log an error to logcat and to the screen via Toast.
       * @param message the text to log.
       */
      private fun logError(message: String?) {
        message?.let {
          Log.e(TAG, message)
          Toast.makeText(this, message, Toast.LENGTH_LONG).show()
        }
      }
    
    }
    
  2. Enclose the code of setupMap() using a try clause. Follow the try with a catch clause that takes a MalformedURLException and calls logError().

    MainActivity.kt
    Use dark colors for code blocks
    75 75 75 75 75 75 75 75 75 75 75 75 75 75 75 75 75 75 75 75 75 75 75 75 75 75 75 75 75 75 75 75 75 75 75 75 75 75 75 75 75 75 75 75 75 75 75 75 75 75 75 75 75 75 75 75 75 75 75 75 75 75 75 75 75 75 75 75 75 75 75 75 75 75 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 115 114 113 112 111 110 109 108 107 106 105 104 104
    Add line.Add line.Add line.Add line.Add line.
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    /*
     * Copyright 2020 Esri
     *
     * Licensed under the Apache License, Version 2.0 (the "License")
     * you may not use this file except in compliance with the License.
     * You may obtain a copy of the License at
     *
     *    http://www.apache.org/licenses/LICENSE-2.0
     *
     * Unless required by applicable law or agreed to in writing, software
     * distributed under the License is distributed on an "AS IS" BASIS,
     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     * See the License for the specific language governing permissions and
     * limitations under the License.
     *
     */
    
    package com.example.app
    
    import android.os.Bundle
    import android.util.Log
    import android.widget.Toast
    import androidx.appcompat.app.AppCompatActivity
    
    import com.esri.arcgisruntime.layers.ArcGISMapImageLayer
    import com.esri.arcgisruntime.mapping.ArcGISMap
    import com.esri.arcgisruntime.mapping.BasemapStyle
    import com.esri.arcgisruntime.mapping.Viewpoint
    import com.esri.arcgisruntime.mapping.view.MapView
    import com.esri.arcgisruntime.security.AuthenticationManager
    import com.esri.arcgisruntime.security.DefaultAuthenticationChallengeHandler
    import com.esri.arcgisruntime.security.OAuthConfiguration
    import com.example.app.databinding.ActivityMainBinding
    
    import java.net.MalformedURLException
    
    
    class MainActivity : AppCompatActivity() {
    
      companion object {
        private val TAG: String = MainActivity::class.java.simpleName
      }
    
      private val activityMainBinding by lazy {
        ActivityMainBinding.inflate(layoutInflater)
      }
    
      private val mapView: MapView by lazy {
        activityMainBinding.mapView
      }
    
      override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
    
        setContentView(activityMainBinding.root)
    
        setupMap()
    
      }
    
      override fun onResume() {
        super.onResume()
        mapView.resume()
      }
    
      override fun onPause() {
        mapView.pause()
        super.onPause()
      }
    
      override fun onDestroy() {
        mapView.dispose()
        super.onDestroy()
      }
    
      private fun setupMap() {
    
        try {
    
          val map = ArcGISMap(BasemapStyle.ARCGIS_TOPOGRAPHIC)
    
          mapView.map = map
    
          mapView.setViewpoint(Viewpoint(34.02700, -118.80543, 72000.0))
    
          // set up an oauth config with url to portal, a client id and a re-direct url
          // a custom client id for your app can be set on the ArcGIS for Developers dashboard under
          // Authentication --> Redirect URIs
          val oAuthConfiguration = OAuthConfiguration(
            null, getString(R.string.oauth_client_id),
            getString(R.string.oauth_redirect_uri) + "://" + getString(R.string.oauth_redirect_host)
          )
    
          // setup AuthenticationManager to handle auth challenges
          val defaultAuthenticationChallengeHandler = DefaultAuthenticationChallengeHandler(this)
    
          // use the DefaultChallengeHandler to handle authentication challenges
          AuthenticationManager.setAuthenticationChallengeHandler(
            defaultAuthenticationChallengeHandler
          )
    
          // add an OAuth configuration
          // NOTE: you must add the DefaultOAuthIntentReceiver Activity to the app's manifest to handle starting a browser
          AuthenticationManager.addOAuthConfiguration(oAuthConfiguration)
    
          val trafficLayer = ArcGISMapImageLayer("https://traffic.arcgis.com/arcgis/rest/services/World/Traffic/MapServer")
          map.operationalLayers.add(trafficLayer)
    
        }
    
        catch (e: MalformedURLException) {
          logError("Error in OAuthConfiguration URL: " + e.message)
        }
    
      }
    
      /**
       * Log an error to logcat and to the screen via Toast.
       * @param message the text to log.
       */
      private fun logError(message: String?) {
        message?.let {
          Log.e(TAG, message)
          Toast.makeText(this, message, Toast.LENGTH_LONG).show()
        }
      }
    
    }
    
  3. Within the MainActivity class, create the logError() function that takes a String parameter and displays a Toast containing the message.

    MainActivity.kt
    Use dark colors for code blocks
    116 116 116 116 116 116 116 116 116 116 116 116 116 116 116 116 116 116 116 116 116 116 116 116 116 116 116 116 116 116 116 116 116 116 116 116 116 116 116 116 116 116 116 116 116 116 116 116 116 116 116 116 116 116 116 116 116 116 116 116 116 116 116 116 116 116 116 116 116 116 116 116 116 116 116 116 116 116 116 116 116 116 116 116 116 116 116 116 116 116 116 116 116 116 116 116 116 116 116 116 116 116 116 116 116 116 116 116 116 116 116 116 116 116 116 116 117 118 119 120 121 122 123 124 125 126 126 126
    Add line.Add line.Add line.Add line.Add line.Add line.Add line.Add line.Add line.Add line.
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    /*
     * Copyright 2020 Esri
     *
     * Licensed under the Apache License, Version 2.0 (the "License")
     * you may not use this file except in compliance with the License.
     * You may obtain a copy of the License at
     *
     *    http://www.apache.org/licenses/LICENSE-2.0
     *
     * Unless required by applicable law or agreed to in writing, software
     * distributed under the License is distributed on an "AS IS" BASIS,
     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     * See the License for the specific language governing permissions and
     * limitations under the License.
     *
     */
    
    package com.example.app
    
    import android.os.Bundle
    import android.util.Log
    import android.widget.Toast
    import androidx.appcompat.app.AppCompatActivity
    
    import com.esri.arcgisruntime.layers.ArcGISMapImageLayer
    import com.esri.arcgisruntime.mapping.ArcGISMap
    import com.esri.arcgisruntime.mapping.BasemapStyle
    import com.esri.arcgisruntime.mapping.Viewpoint
    import com.esri.arcgisruntime.mapping.view.MapView
    import com.esri.arcgisruntime.security.AuthenticationManager
    import com.esri.arcgisruntime.security.DefaultAuthenticationChallengeHandler
    import com.esri.arcgisruntime.security.OAuthConfiguration
    import com.example.app.databinding.ActivityMainBinding
    
    import java.net.MalformedURLException
    
    
    class MainActivity : AppCompatActivity() {
    
      companion object {
        private val TAG: String = MainActivity::class.java.simpleName
      }
    
      private val activityMainBinding by lazy {
        ActivityMainBinding.inflate(layoutInflater)
      }
    
      private val mapView: MapView by lazy {
        activityMainBinding.mapView
      }
    
      override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
    
        setContentView(activityMainBinding.root)
    
        setupMap()
    
      }
    
      override fun onResume() {
        super.onResume()
        mapView.resume()
      }
    
      override fun onPause() {
        mapView.pause()
        super.onPause()
      }
    
      override fun onDestroy() {
        mapView.dispose()
        super.onDestroy()
      }
    
      private fun setupMap() {
    
        try {
    
          val map = ArcGISMap(BasemapStyle.ARCGIS_TOPOGRAPHIC)
    
          mapView.map = map
    
          mapView.setViewpoint(Viewpoint(34.02700, -118.80543, 72000.0))
    
          // set up an oauth config with url to portal, a client id and a re-direct url
          // a custom client id for your app can be set on the ArcGIS for Developers dashboard under
          // Authentication --> Redirect URIs
          val oAuthConfiguration = OAuthConfiguration(
            null, getString(R.string.oauth_client_id),
            getString(R.string.oauth_redirect_uri) + "://" + getString(R.string.oauth_redirect_host)
          )
    
          // setup AuthenticationManager to handle auth challenges
          val defaultAuthenticationChallengeHandler = DefaultAuthenticationChallengeHandler(this)
    
          // use the DefaultChallengeHandler to handle authentication challenges
          AuthenticationManager.setAuthenticationChallengeHandler(
            defaultAuthenticationChallengeHandler
          )
    
          // add an OAuth configuration
          // NOTE: you must add the DefaultOAuthIntentReceiver Activity to the app's manifest to handle starting a browser
          AuthenticationManager.addOAuthConfiguration(oAuthConfiguration)
    
          val trafficLayer = ArcGISMapImageLayer("https://traffic.arcgis.com/arcgis/rest/services/World/Traffic/MapServer")
          map.operationalLayers.add(trafficLayer)
    
        }
    
        catch (e: MalformedURLException) {
          logError("Error in OAuthConfiguration URL: " + e.message)
        }
    
      }
    
      /**
       * Log an error to logcat and to the screen via Toast.
       * @param message the text to log.
       */
      private fun logError(message: String?) {
        message?.let {
          Log.e(TAG, message)
          Toast.makeText(this, message, Toast.LENGTH_LONG).show()
        }
      }
    
    }
    
  4. Click Run > Run > app to run the app.

You should see the map with the topographic basemap layer centered on the Santa Monica Mountains in California. You will also see the traffic layer, with its symbology of green, yellow, orange, and red roads to indicate current traffic flow. This is a secured layer, which is visible in your app because the user has entered valid ArcGIS Online username and password.

ArcGIS World Traffic service layer

Your browser is no longer supported. Please upgrade your browser for the best experience. See our browser deprecation post for more details.