Mobile App Dev 2022W: Tutorial 6
In this tutorial you will be playing with textanalyzer-5, which implements the same text analyzer functionality as Tutorial 5's textanalyzer-3. This version, however, is an Android app.
Getting Started
- Install Android Studio. While you can run the version for MacOS, you should probably download and run it on the fastest computer you have access to.
- Create a new Phone & Tablet project, selecting the "Empty Activity" template. Select "Kotlin" as the language and API 21, Android 5.0 as the minimum SDK.
- Replace the contents of MainActivity.kt (under app->java) with the version below. Be sure to copy the raw source rather than the syntax highlighted version below. Preserve the first line that says "package", however, as that depends on the precise name you gave your application.
- Replace the contents of activity_main.xml (under app->res->layout) with the version below. Select the "Code" view (rather than "Design") so you can see the raw XML. You may have save the file below locally as your browser may try to interpret the XML.
- Add the following lines to themes.xml (in app->res->values->themes), just below the last item:
<item name="windowActionBar">false</item> <item name="windowNoTitle">true</item>
- Create a new Android simulator using the Device Manager. We suggest a Pixel 3a running API 31, but other devices should work fine.
- Save the project and run it!
Questions
- What is the Kotlin equivalent of let?
- How does Kotlin handle variables that could be null? How does this compare to Swift's handling of nil?
- How similar are Kotlin and Swift function definitions? What about their declaration of dictionaries and sets?
- What is the :: for? Is something similar used in Swift?
- How is the layout XML connected with Kotlin code? Specifically, what are the two ways they are integrated in this program? Howe do they compare with how Swift code and Storyboard layouts are connected on iOS?
- What is the role of the twatcher object? What is the equivalent in iOS Storyboard programs? What about SwiftUI?
Tasks
- Add the Empty and Pets Mentioned actions as they were in textanalyzer-2.
- Add a menu item "None" that sets the analysis mode to "None". When selected, the result area should show "Please choose an analysis mode." (This message should appear without you changing any part of the code except for the part determining the contents of the menu.)
- Make the title of the app "Text Analyzer 5" and make the text non-bold (while keeping the same font size).
- Put the results area in the center of the screen and the text input area just below it (thus swapping the position of the two). Make sure your layout works with different phone sizes and when the phone is rotated.
- Change the button and status bar background color of the app from purple to anything else.
textanalyzer-5 Code
MainActivity.kt
package carleton.comp1601.textanalyzer5
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.text.Editable
import android.text.TextWatcher
import android.view.MenuItem
import android.view.View
import android.widget.Button
import android.widget.EditText
import android.widget.TextView
import androidx.appcompat.widget.PopupMenu
import kotlin.reflect.KFunction1
class MainActivity : AppCompatActivity() {
private lateinit var t: EditText
private lateinit var analysisResult: TextView
var analysisMode = "None"
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
t = findViewById(R.id.t)
t.addTextChangedListener(twatcher)
analysisResult = findViewById(R.id.analysisResult)
updateAnalysis()
}
fun onMenuItemClick(choice: MenuItem): Boolean {
analysisMode = choice.getTitle() as String
updateAnalysis()
return true
}
// https://stackoverflow.com/questions/15580111/
fun showMenu(v: View) {
val menu = PopupMenu(this, v)
for (s in analysis.keys) {
menu.menu.add(s)
}
menu.setOnMenuItemClickListener(::onMenuItemClick)
menu.show()
}
private val twatcher = object : TextWatcher {
override fun afterTextChanged(s: Editable) {
updateAnalysis()
}
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {
}
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
}
}
fun updateAnalysis() {
val inputText: Editable? = t.text
if (inputText != null) {
val analysisFunc: KFunction1<String, String>? = analysis[analysisMode]
if (analysisFunc != null) {
val r = analysisFunc(inputText.toString())
analysisResult.setText(analysisMode + ": " + r)
} else {
analysisResult.setText("Please choose an analysis mode.")
}
}
}
}
fun countUpper(s: String): String {
var count = 0
val upperCase = setOf("A","B","C","D","E","F","G","H","I","J","K","L","M",
"N","O","P","Q","R","S","T","U","V","W","X","Y","Z")
for (c in s) {
if (upperCase.contains(c.toString())) {
count += 1
}
}
return count.toString()
}
fun countCharacters(s: String): String {
val charcount = s.length
return charcount.toString()
}
val analysis: MutableMap<String, KFunction1<String, String>> = mutableMapOf(
"Count" to ::countCharacters,
"Upper Case" to ::countUpper
)
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<Button
android:id="@+id/analysisMenuButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="26dp"
android:onClick="showMenu"
android:text="Analysis Mode"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/appTitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Text Analyzer"
android:textAppearance="@style/TextAppearance.AppCompat.Display2"
android:textStyle="bold"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/analysisMenuButton"
app:layout_constraintBottom_toTopOf="@id/t"
/>
<EditText
android:id="@+id/t"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:ems="20"
android:hint="Enter Text"
android:inputType="text"
android:minHeight="48dp"
android:textAppearance="@style/TextAppearance.AppCompat.Body2"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/analysisResult"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="32dp"
android:text="Result Area"
android:textAppearance="@style/TextAppearance.AppCompat.Medium"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/t"
app:layout_constraintBottom_toBottomOf="parent"/>
</androidx.constraintlayout.widget.ConstraintLayout>