DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Please enter at least three characters to search
Refcards Trend Reports
Events Video Library
Refcards
Trend Reports

Events

View Events Video Library

Zones

Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks

Because the DevOps movement has redefined engineering responsibilities, SREs now have to become stewards of observability strategy.

Apache Cassandra combines the benefits of major NoSQL databases to support data management needs not covered by traditional RDBMS vendors.

The software you build is only as secure as the code that powers it. Learn how malicious code creeps into your software supply chain.

Generative AI has transformed nearly every industry. How can you leverage GenAI to improve your productivity and efficiency?

Related

  • Building a Kotlin Mobile App With the Salesforce SDK
  • Supporting Offline Mode: Key Strategies and Benefits for iOS Mobile Apps
  • How Machine Learning and AI are Transforming Healthcare Diagnostics in Mobile Apps
  • Mastering Multi-Cloud and Edge Data Synchronization: A Retail Use Case With KubeMQ’s Java SDK

Trending

  • Driving DevOps With Smart, Scalable Testing
  • Prioritizing Cloud Security Risks: A Developer's Guide to Tackling Security Debt
  • Advancing Robot Vision and Control
  • AI-Driven Root Cause Analysis in SRE: Enhancing Incident Resolution
  1. DZone
  2. Data Engineering
  3. Data
  4. Building a Kotlin Mobile App With the Salesforce SDK: Editing and Creating Data

Building a Kotlin Mobile App With the Salesforce SDK: Editing and Creating Data

Here in Part Two of our three-part series, we continue to build on the fundamentals of Android development with the Salesforce Mobile SDK.

By 
Michael Bogan user avatar
Michael Bogan
DZone Core CORE ·
Mar. 04, 22 · Tutorial
Likes (2)
Comment
Save
Tweet
Share
8.8K Views

Join the DZone community and get the full member experience.

Join For Free

Here in Part Two of our three-part series, we continue to build on the fundamentals of Android development with the Salesforce Mobile SDK. In Part One, we covered project setup and creating a layout that fetches data from Salesforce, using Kotlin as our programming language of choice.

Before we continue towards developing a complete mobile synchronization strategy, we’ll first build out our mobile app to allow editing and creating data on our Salesforce org.

Editing Data

With the view established, let’s see how we can edit the data. Ideally, we want to maintain the same list format, but make it such that if a user taps on a name, they can edit it. Those changes should then be sent back to Salesforce.

To accomplish this, we need to do a few things. First, we need to override the default list behavior to make items editable. Then, after a record edit is complete, we need to make a call to the Salesforce API to update the data.

As before, let’s start with the layout. Create a file in app/res/layout called broker_item.xmland paste these lines into it:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
   xmlns:android="http://schemas.android.com/apk/res/android"
   android:orientation="vertical"
   android:layout_width="fill_parent"
   android:layout_height="fill_parent">

   <EditText android:id="@+id/broker_field"
       android:layout_width="fill_parent"
       android:layout_height="wrap_content"
       />
</LinearLayout>


Next, create a file in app/java/com.example.sfdc called BrokerListAdapter.kt, and paste this into it:

 
package com.example.sfdc

import android.content.Context;
import android.text.Editable;
import android.text.TextWatcher;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.EditText;
import com.salesforce.androidsdk.rest.BrokerItemBinding
import com.salesforce.androidsdk.rest.ApiVersionStrings
import com.salesforce.androidsdk.rest.RestClient
import com.salesforce.androidsdk.rest.RestRequest
import com.salesforce.androidsdk.rest.RestResponse

class BrokerListAdapter : ArrayAdapter<String> {
   internal var context: Context
   Private lateinit var brokerItemBinding: BrokerItemBinding
   private var client: RestClient? = null
   private val nameToId: MutableMap<String, String> = mutableMapOf()

   constructor(context: Context) : super(context, R.layout.broker_item, ArrayList<String>(0)) {
       this.context = context;
   }

   fun setClient(client: RestClient?) {
       this.client = client
   }

   fun map(name: String, id: String) {
       this.nameToId.put(name, id)
   }

   override fun getView(position: Int, convertView: View?, parent: ViewGroup): View {
       val inflater = context
           .getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater
       brokerItemBinding = BrokerItemBinding.inflate(inflater)
       val rowView: View = inflater.inflate(R.layout.broker_item, parent, false)
       val brokerField = brokerItemBinding.brokerField
       var item = getItem(position)
       var brokerId = nameToId.get(item)
       val fields: MutableMap<String, String> =  mutableMapOf()

       brokerField.setText(item)
       brokerField.addTextChangedListener(object : TextWatcher {
           override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {}
           override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {}
           override fun afterTextChanged(s: Editable) {
               item = brokerField.text.toString()

               fields.put("Name", item!!)
               val restRequest = RestRequest.getRequestForUpdate(ApiVersionStrings.getVersionNumber(context), "Broker__c", brokerId,
                   fields as Map<String, Any>?
               )
               client?.sendAsync(restRequest, object : RestClient.AsyncRequestCallback {
                   override fun onSuccess(request: RestRequest, result: RestResponse) {}

                   override fun onError(exception: Exception) {}
               })
           }
       })
       return rowView
   }
}


In the Android ecosystem, an adapter defines the functionality of a UI element. Here, we’ve created a new adapter that overrides certain behaviors of ArrayAdapter. We’ve added a function called setClient, which reuses the same Salesforce API client that we need to populate the list over in MainActivity. The map function associates a broker’s name with their custom object ID. The reason why will be apparent soon.

getView is where the bulk of the activity happens. The most important line is the one dealing with RestRequest.getRequestForUpdate. The client calls this to update Salesforce data; it requires the name of the custom object, its ID, and the value to replace (in this case, the name). This event occurs when afterTextChanged fires; that is, after the user has finished updating the broker’s name. In a real production environment, you need to check the status codes for potential errors from the API, but for the sake of brevity, we’ve omitted any type of response checking.

In MainActivity, we need to make use of this adapter. First, at the top of the class definition, change listAdapter to be a BrokerListAdapter instead of an ArrayAdapter, like so:

class MainActivity : SalesforceActivity() {

   private var client: RestClient? = null
   private var listAdapter: ArrayAdapter<String>? = null
   private lateinit var listAdapter: BrokerListAdapter
    ...


The lateinit modifier allows you to initialize a non-null type outside of the constructor. Next, replace the two onResume functions with these:

    override fun onResume() {
        // Hide everything until we are logged in
        mainViewBinding.root.visibility = View.INVISIBLE

        // Create list adapter
        listAdapter = BrokerListAdapter(this)
        mainViewBinding.brokersList.adapter = listAdapter

        super.onResume()
    }

    override fun onResume(client: RestClient) {
        // Keeping reference to rest client
        this.client = client
        listAdapter.setClient(client)

        // Show everything
        mainViewBinding.root.visibility = View.VISIBLE
        sendRequest("SELECT Name, Id FROM Broker__c")
    }


Lastly, we need to keep track of the broker’s real name along with their record Id. To do that, we can simply store it in the dictionary the BrokerListAdapter maintains. In sendRequest, replace the for loop there with this one:

 
for (i in 0..records.length() - 1) {
   listAdapter.add(records.getJSONObject(i).getString("Name"))
   listAdapter.map(records.getJSONObject(i).getString("Name"), records.getJSONObject(i).getString("Id"))
}


Go ahead and launch the app, and your list items will be editable. Make an edit, and then get ready, because things are about to get wild. Go back to your scratch org, and click on the Brokers tab in the Dreamforce app. Your edits should be reflected here on the Salesforce platform!

Adding Data

We can edit records, but what if we need to add a new broker? The format for this is pretty similar to the logic we used for fetching and editing records. Let’s quickly go through the steps.

Open up main.xml and paste these lines right before the ListView:

<LinearLayout android:orientation="horizontal"
  android:layout_width="match_parent"
  android:layout_height="wrap_content"
  android:background="?android:colorBackground"
  android:layout_marginStart="10dp"
  android:layout_marginEnd="10dp"
  android:layout_marginTop="10dp"
  android:layout_marginBottom="10dp">

  <Button
     android:id="@+id/add_broker"
     android:layout_width="0dp"
     android:layout_height="47dp"
     android:onClick="onAddBrokerClick"
     android:text="Add broker"
     android:background="?android:colorPrimary"
     android:textColor="?attr/sfColorSecondary"
     android:layout_gravity="center"
     android:layout_weight="1"
     android:layout_marginEnd="10dp"/>
</LinearLayout>


Here, we’ve added a button that will call a function called onAddBrokerClick whenever it is pressed. In MainActivity, we’ll define that method:

fun onAddBrokerClick(v: View) {
   listAdapter.add("New Broker")
   var fields: Map<String, String> = mapOf("name" to "New Broker",
                                            "Title__c" to "Junior Broker",
                                            "Phone__c" to "555-555-1234",
                                            "Mobile_Phone__c" to  "555-555-1234",
                                            "Email__c" to "todo@salesforce.com",
                                            "Picture__c" to "https://cdn.iconscout.com/icon/free/png-256/salesforce-282298.png")
   val restRequest = RestRequest.getRequestForUpsert(ApiVersionStrings.getVersionNumber(this), "Broker__c", "Id", null, fields)

   client?.sendAsync(restRequest, object : AsyncRequestCallback {
       override fun onSuccess(request: RestRequest, result: RestResponse) {}
       override fun onError(exception: Exception) {}
   })
}


Yes, that’s it! Those fields we’ve defined relate directly to the custom object. By setting Id to null, we’re telling the API that this is a new record. If you add a new row, edit it, and then head back to your scratch org, you’ll see the new data appear online.

To Be Continued…

Building on top of what we accomplished last time, in this post, we were able to edit and add broker data in our mobile app, and then see it reflected in Salesforce. Pretty neat!

However, this is only a one-way data change: from the app to the org. What about sending data from the org to the app? In addition, how can we handle network issues that are unique to mobile phones, such as connectivity issues, or multiple people changing fields at the same time? We’ll address these very real concerns in our concluding post!

mobile app Data (computing) Software development kit Kotlin (programming language)

Opinions expressed by DZone contributors are their own.

Related

  • Building a Kotlin Mobile App With the Salesforce SDK
  • Supporting Offline Mode: Key Strategies and Benefits for iOS Mobile Apps
  • How Machine Learning and AI are Transforming Healthcare Diagnostics in Mobile Apps
  • Mastering Multi-Cloud and Edge Data Synchronization: A Retail Use Case With KubeMQ’s Java SDK

Partner Resources

×

Comments
Oops! Something Went Wrong

The likes didn't load as expected. Please refresh the page and try again.

ABOUT US

  • About DZone
  • Support and feedback
  • Community research
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Core Program
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 3343 Perimeter Hill Drive
  • Suite 100
  • Nashville, TN 37211
  • support@dzone.com

Let's be friends:

Likes
There are no likes...yet! 👀
Be the first to like this post!
It looks like you're not logged in.
Sign in to see who liked this post!