Day81 of #100DaysOfCode

Kushagra Kesav
5 min readApr 28, 2022

--

Day81 of #100DaysOfCode

Hii folks 🙌

Today I will be continuing the same pathway in which we will implement send order feature.

Unit 3: Navigation

Pathway 3: Advanced navigation app Examples

https://developer.android.com/courses/android-basics-kotlin/course

Send the order

We will implement the toast message which will appear when we tap on the send order button on the SummaryFragment,

It would be a more useful experience if the order could be sent out from the app. That way, the user can share the cupcake order information with an email app on the device, allowing the order to be emailed to the cupcake shop.

To implement this feature, we’ll be using these strings that are already in our strings.xml file.

<string name="new_cupcake_order">New Cupcake Order</string>
<string name="order_details">Quantity: %1$s cupcakes \n Flavor: %2$s \nPickup date: %3$s \n Total: %4$s \n\n Thank you!</string>

order_details is a string resource with 4 different format arguments in it, which are placeholders for the actual quantity of cupcakes, desired flavor, desired pickup date, and total price. The arguments are numbered from 1 to 4 with the syntax %1 to %4. The type of argument is also specified ($s means a string is expected here).

Quantity: 12 cupcakes
Flavor: Chocolate
Pickup date: Sat Dec 12
Total: $24.00
Thank you!

<string name="subtotal_price">Subtotal %s</string>

<string name="total_price">Total %s</string>

where %s is a placeholder for the formatted price string.

  • In SummaryFragment.kt modify the sendOrder() method. Remove the existing Toast message.
fun sendOrder() {

}
  • Within the sendOrder() method, construct the order summary text. Create the formatted order_details string by getting the order quantity, flavor, date, and price from the shared view model.
val orderSummary = getString(
R.string.order_details,
sharedViewModel.quantity.value.toString(),
sharedViewModel.flavor.value.toString(),
sharedViewModel.date.value.toString(),
sharedViewModel.price.value.toString()
)
  • Still within the sendOrder() method, create an implicit intent for sharing the order to another app. See the documentation for how to create an email intent. Specify Intent.ACTION_SEND for the intent action, set type to "text/plain" and include intent extras for the email subject (Intent.EXTRA_SUBJECT) and email body (Intent.EXTRA_TEXT). Import android.content.Intent if needed.
val intent = Intent(Intent.ACTION_SEND)
.setType("text/plain")
.putExtra(Intent.EXTRA_SUBJECT, getString(R.string.new_cupcake_order))
.putExtra(Intent.EXTRA_TEXT, orderSummary)
  • Since this is an implicit intent, we don’t need to know ahead of time which specific component or app will handle this intent. The user will decide which app they want to use to fulfill the intent. However, before launching an activity with this intent, check to see if there’s an app that could even handle it. This check will prevent the Cupcake app from crashing if there’s no app to handle the intent, making our code safer.
if (activity?.packageManager?.resolveActivity(intent, 0) != null) {
startActivity(intent)
}

Perform this check by accessing the PackageManager, which has information about what app packages are installed on the device. The PackageManager can be accessed via the fragment's activity, as long as the activity and packageManager are not null. Call the PackageManager's resolveActivity() method with the intent we created. If the result is not null, then it is safe to call startActivity() with our intent.

In testing different scenarios, we may notice a bug if we only have 1 cupcake. The order summary says 1 cupcakes, but in English, this is grammatically incorrect.

Instead, it should say 1 cupcake (no plural). If we want to choose whether the word cupcake or cupcakes is used based on the quantity value, then we can use something called quantity strings in Android. By declaring a plurals resource, we can specify different string resources to use based on what the quantity is, for example in the singular or plural case.

  • Add a cupcakes plurals resource in our strings.xml file.
<plurals name="cupcakes">
<item quantity="one">%d cupcake</item>
<item quantity="other">%d cupcakes</item>
</plurals>

In the singular case (quantity="one"), the singular string will be used. In all other cases (quantity="other"), the plural string will be used. Note that instead of %s which expects a string argument, %d expects an integer argument, which we will pass in when we format the string.

In our Kotlin code, calling:

getQuantityString(R.plurals.cupcakes, 1, 1) returns the string 1 cupcake

getQuantityString(R.plurals.cupcakes, 6, 6) returns the string 6 cupcakes

getQuantityString(R.plurals.cupcakes, 0, 0) returns the string 0 cupcakes

Note: When calling getQuantityString(), we need to pass in the quantity twice because the first quantity parameter is used to select the correct plural string. The second quantity parameter is used in the %d placeholder of the actual string resource.

  • Before going to our Kotlin code, update the order_details string resource in strings.xml so that the plural version of cupcakes is no longer hardcoded into it.
<string name="order_details">Quantity: %1$s \n Flavor: %2$s \nPickup date: %3$s \n
Total: %4$s \n\n Thank you!</string>
  • In the SummaryFragment class, update our sendOrder() method to use the new quantity string. It would be easiest to first figure out the quantity from the view model and store that in a variable. Since quantity in the view model is of type LiveData<Int>, it's possible that sharedViewModel.quantity.value is null. If it is null, then use 0 as the default value for numberOfCupcakes.

Add this as the first line of code in our sendOrder() method.

val numberOfCupcakes = sharedViewModel.quantity.value ?: 0

The elvis operator (?:) means that if the expression on the left is not null, then use it. Otherwise if the expression on the left is null, then use the expression to the right of the elvis operator (which is 0 in this case).

  • Then format the order_details string as we did before. Instead of passing in numberOfCupcakes as the quantity argument directly, create the formatted cupcakes string with resources.getQuantityString(R.plurals.cupcakes, numberOfCupcakes, numberOfCupcakes).

The full sendOrder() method should look like the following:

fun sendOrder() {
val numberOfCupcakes = sharedViewModel.quantity.value ?: 0
val orderSummary = getString(
R.string.order_details,
resources.getQuantityString(R.plurals.cupcakes, numberOfCupcakes, numberOfCupcakes),
sharedViewModel.flavor.value.toString(),
sharedViewModel.date.value.toString(),
sharedViewModel.price.value.toString()
)
val intent = Intent(Intent.ACTION_SEND)
.setType("text/plain")
.putExtra(Intent.EXTRA_SUBJECT, getString(R.string.new_cupcake_order))
.putExtra(Intent.EXTRA_TEXT, orderSummary)

if (activity?.packageManager?.resolveActivity(intent, 0) != null) {
startActivity(intent)
}
}
  • Run and test our code. Check that the order summary in the email body shows 1 cupcake vs. 6 cupcakes or 12 cupcakes.

That is all for Day81 ✅

Thanks for reading, See you tomorrow!

--

--

Kushagra Kesav
Kushagra Kesav

Written by Kushagra Kesav

Developer Relations | Communities | Software Engineering | https://www.linkedin.com/in/kushagrakesav

No responses yet