Mobile App Dev 2022W: Assignment 3
Please answer all of the following questions in the supplied template. Your uploaded file should be named "comp1601-assign3-MCOname.txt" where MCOname is your MyCarletonOne username (i.e., the username you use to login to Brightspace) and it should be a UNIX text file (LF line endings).
You may use this validator page to make sure your answer file is properly named and formatted.
The questions are based on TextAnalyzer6, as shown below. There are 9 questions worth 20 points total.
Do not turn in modified applications. Instead, when answering code writing questions, include code snippets and explain how they should be used to modify the application. Use complete sentences when answering. Be sure to list collaborators and outside sources that you used.
Submit your answers via Brightspace by March 23, 2022 by 11:30 AM.
Questions
- [2] How does the syntax for dictionaries in Swift compare to mutable maps in Kotlin? How similar are they?
- [2] How can you change the code so that, when rotated, the mode is never "None" and if it was, it is changed to "Count"? Other modes should be preserved, and when the app is started it should behave as before. Explain why your change works.
- [2] How can you change the app so that when you switch to another app and then return, the result area shows "Welcome Back!"? Explain why your change works.
- [1] How can you fix the results area so it is horizontally centered, as it was before?
- [2] How can you move the menu button back to the top? Why do your changes work?
- [2] In the constraints for the menu button, there in a specifier for marginTop (line 49 of activity_main.xml). In the original layout, does this specifier affect the app's appearance? What about once the menu button is moved to the top of the screen, does it change the app's appearance?
- [2] What is the twatcher object for? What provides the same functionality in SwiftUI?
- [2] If we were to add a new entry to the analysis dictionary (mutable map) in the onResume method, would this new entry show up in the menu? Why or why not? Explain.
- [5] How could you change the program so that it no longer used the analysis dictionary?
- [1] Change it so that instead of the mutable map, it use an array of strings to generate the menu items.
- [2] Make updateAnalysis() either call the analysis functions explicitly or incorporate their code directly.
- [1] How does your code handle the "None" case? How does it compare to the original version?
- [1] Which version of the code do you like better? Why?
Code: TextAnalyzer6
MainActivity.kt
package carleton.comp1601.textanalyzer6
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.text.Editable
import android.text.TextWatcher
import android.util.Log
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
val appName = "TextAnalyzer6"
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)
Log.d(appName, "Created")
if (savedInstanceState != null) {
with(savedInstanceState) {
val savedMode = getString("analysisMode")
if (savedMode != null) {
analysisMode = savedMode
}
}
}
t = findViewById(R.id.t)
t.addTextChangedListener(twatcher)
analysisResult = findViewById(R.id.analysisResult)
updateAnalysis()
}
override fun onSaveInstanceState(outState: Bundle) {
outState.run {
putString("analysisMode", analysisMode)
}
super.onSaveInstanceState(outState)
Log.d(appName, "State saved")
}
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.menu.add("None")
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.")
}
}
}
override fun onDestroy() {
super.onDestroy()
Log.d(appName, "Destroyed")
}
override fun onResume() {
super.onResume()
Log.d(appName, "Resumed")
}
override fun onPause() {
super.onPause()
Log.d(appName, "Paused")
}
override fun onStart() {
super.onStart()
Log.d(appName, "Started")
}
override fun onStop() {
super.onStop()
Log.d(appName, "Stopped")
}
override fun onRestart() {
super.onRestart()
Log.d(appName, "Restarted")
}
}
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()
}
fun isEmpty(s: String): String {
if (s == "")
return "Yes"
else
return "No"
}
fun countPetsMentioned(s: String): String {
var count = 0
val pets = setOf("Roshi", "Tab", "Shift")
for (p in pets) {
if (s.contains(p)) {
count += 1
}
}
return count.toString()
}
val analysis: MutableMap<String, KFunction1<String, String>> = mutableMapOf(
"Count" to ::countCharacters,
"Upper Case" to ::countUpper,
"Empty" to ::isEmpty,
"Pets Mentioned" to ::countPetsMentioned
)
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">
<TextView
android:id="@+id/appTitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Text Analyzer 6"
android:textAppearance="@style/TextAppearance.AppCompat.Display3"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
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_constraintTop_toBottomOf="@+id/appTitle"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintBottom_toTopOf="@+id/analysisResult" />
<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_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/t"
app:layout_constraintBottom_toTopOf="@+id/analysisMenuButton"/>
<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_toBottomOf="@+id/analysisResult"
app:layout_constraintBottom_toBottomOf="parent"/>
</androidx.constraintlayout.widget.ConstraintLayout>
themes.xml
<resources xmlns:tools="http://schemas.android.com/tools">
<!-- Base application theme. -->
<style name="Theme.TextAnalyzer6" parent="Theme.MaterialComponents.DayNight.DarkActionBar">
<!-- Primary brand color. -->
<item name="colorPrimary">@color/purple_500</item>
<item name="colorPrimaryVariant">@color/purple_700</item>
<item name="colorOnPrimary">@color/white</item>
<!-- Secondary brand color. -->
<item name="colorSecondary">@color/teal_200</item>
<item name="colorSecondaryVariant">@color/teal_700</item>
<item name="colorOnSecondary">@color/black</item>
<!-- Status bar color. -->
<item name="android:statusBarColor" tools:targetApi="l">?attr/colorPrimaryVariant</item>
<!-- Customize your theme here. -->
<item name="windowActionBar">false</item>
<item name="windowNoTitle">true</item>
</style>
</resources>