MOBILE APPLICATION DEVELOPMENT (MAD): LAB 6 Lab 06 Activities Activity 1: In this task, your goal is to build a straightforward Android application with two activities. The primary objective is to create a login system. The first activity will serve as a login screen, prompting users to input their username and password. A "Login" or "Sign In" button should accompany these fields. When users submit their credentials, the app will verify them against a predefined record in an SQLite database. If the credentials match, users will be taken to the second activity. If not, an error message in red text will inform them of the issue. In the second activity, you will create a greeting screen, featuring a simple TextView with a message like "Hello, [username]," where "[username]" will display the actual username entered during login or retrieved from the database. Moreover, your app should remember the user's login state. If a user successfully logs in, the app should remember this and not require them to log in again when they close and reopen the app. Additionally, implement a "Log Out" or "Sign Out" button in the second activity. When tapped, this button should return the user to the login screen (Activity 1). If the user closes and reopens the app, they should see the login screen to proceed to the second activity once more. Kotlin Codes: (Main Activity) package com.example.myapplication import android.content.Context import android.content.Intent import android.content.SharedPreferences import androidx.appcompat.app.AppCompatActivity import android.os.Bundle import android.widget.Button import android.widget.EditText import android.widget.TextView import androidx.core.content.ContextCompat class MainActivity : AppCompatActivity() { private lateinit var sharedPreferences: SharedPreferences override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) sharedPreferences = getSharedPreferences("MyPrefs", Context.MODE_PRIVATE) val val val val usernameEditText = findViewById<EditText>(R.id.usernameEditText) passwordEditText = findViewById<EditText>(R.id.passwordEditText) loginButton = findViewById<Button>(R.id.loginButton) errorMessage = findViewById<TextView>(R.id.errorTextView) loginButton.setOnClickListener { val username = usernameEditText.text.toString() val password = passwordEditText.text.toString() if (isValidCredentials(username, password)) { saveLoginState(true) val intent = Intent(this, GreetingActivity::class.java) intent.putExtra("username", username) startActivity(intent) finish() } else { errorMessage.apply { text = "Invalid username or password" setTextColor(ContextCompat.getColor(this@MainActivity, R.color.errorRed)) visibility = TextView.VISIBLE } } } } private fun isValidCredentials(username: String, password: String): Boolean { return (username == "admin" && password == "password") } private fun saveLoginState(isLoggedIn: Boolean) { val editor = sharedPreferences.edit() editor.putBoolean("isLoggedIn", isLoggedIn) editor.apply() } } (Greeting) package com.example.myapplication import import import import import import import import android.content.Context android.content.Intent android.content.SharedPreferences android.os.Bundle android.widget.Button android.widget.EditText android.widget.TextView androidx.appcompat.app.AppCompatActivity class GreetingActivity : AppCompatActivity() { private lateinit var sharedPreferences: SharedPreferences override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_greeting) sharedPreferences = getSharedPreferences("MyPrefs", Context.MODE_PRIVATE) val logoutButton : Button = findViewById(R.id.logoutButton) val greetingTextView : TextView = findViewById(R.id.greetingTextView) val username = intent.getStringExtra("username") greetingTextView.text = "Hello, $username!" logoutButton.setOnClickListener { // Clear login state sharedPreferences.edit().putBoolean("isLoggedIn", false).apply() // Navigate back to login activity val intent = Intent(this, MainActivity::class.java) startActivity(intent) finish() } } } XML Layout codes: 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"> <EditText android:id="@+id/name" android:layout_width="wrap_content" android:layout_height="wrap_content" android:hint="Enter Username" android:ems="10" android:inputType="textPersonName" tools:layout_editor_absoluteX="56dp" tools:layout_editor_absoluteY="262dp" /> <EditText android:id="@+id/password" android:layout_width="wrap_content" android:layout_height="wrap_content" android:ems="10" android:hint="Enter Password" android:inputType="textPassword" tools:layout_editor_absoluteX="56dp" tools:layout_editor_absoluteY="307dp" /> <Button android:id="@+id/button" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Login" tools:layout_editor_absoluteX="127dp" tools:layout_editor_absoluteY="352dp" /> <ImageButton android:id="@+id/imageButton2" android:layout_width="wrap_content" android:layout_height="wrap_content" app:srcCompat="@drawable/img_1" tools:layout_editor_absoluteX="16dp" tools:layout_editor_absoluteY="16dp" /> </androidx.constraintlayout.widget.ConstraintLayout> (Greeting xml) <?xml version="1.0" encoding="utf-8"?> <LinearLayout 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" android:orientation="vertical" android:padding="16dp" tools:context=".GreetingActivity"> <TextView android:id="@+id/greetingTextView" android:layout_width="wrap_content" android:layout_height="wrap_content" android:textSize="20sp" /> <Button android:id="@+id/logoutButton" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Log Out" /> </LinearLayout> Activity 2: In this task, you will be creating a straightforward To-Do app. The main screen of the app will display a title at the top, saying "To-Do's," followed by an empty list (a ListView below the TextView). This list will show all your To-Do items, which will be stored in an SQLite database. Initially, the list and database will be empty. To add a To-Do item, you should add a "+" icon in the app's Toolbar. When you tap this icon, a dialog box should pop up, presenting you with an editable text field to type your new To-Do. Two buttons, labeled "Add" and "Cancel," should be available in this dialog. If you click "Cancel," nothing should change, and the dialog box should disappear. But if you select "Add," your new To-Do should be saved in the database, and the dialog should close. The list should then refresh to include both your existing To-Dos (if any) and the newly added one. For editing an existing To-Do, you should long-press on the item you wish to modify. A context menu should appear with an "Edit" option. When you choose this option, another dialog box should appear with an editable text field containing the text of the selected To-Do. Similar to the previous dialog, there should be two buttons, "Save" and "Cancel." If you click "Cancel," nothing should change, and the dialog box should close. However, selecting the "Save" button should update the To-Do in the database, close the dialog, and refresh the list to show/load the updated To-Do's from the database (even the edited To-Do in its new/edited form). Xml: <?xml version="1.0" encoding="utf-8"?> <LinearLayout 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" android:orientation="vertical" tools:context=".MainActivity"> <androidx.appcompat.widget.Toolbar android:id="@+id/toolbar" android:layout_width="match_parent" android:layout_height="?attr/actionBarSize" android:background="?attr/colorPrimary" android:theme="?attr/actionBarTheme" /> <ListView android:id="@+id/listView" android:layout_width="match_parent" android:layout_height="0dp" android:layout_weight="1" /> <com.google.android.material.floatingactionbutton.FloatingActionButton android:id="@+id/fab" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="end" android:layout_margin="16dp" android:src="@android:drawable/ic_input_add" /> </LinearLayout> Kotlin package com.example.myapplication import android.os.Bundle import android.widget.EditText import android.widget.ListView import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AppCompatActivity import com.google.android.material.floatingactionbutton.FloatingActionButton class MainActivity : AppCompatActivity() { private lateinit var dbHelper: DatabaseHelper private lateinit var todoAdapter: TodoAdapter override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) dbHelper = DatabaseHelper(this) todoAdapter = TodoAdapter(this, dbHelper.getAllTodos()) val listView = findViewById<ListView>(R.id.listView) val fab = findViewById<FloatingActionButton>(R.id.fab) listView.adapter = todoAdapter fab.setOnClickListener { showAddTodoDialog() } listView.setOnItemLongClickListener { _, _, position, _ -> showEditTodoDialog(position) true } } private fun showAddTodoDialog() { val editText = EditText(this) val dialog = AlertDialog.Builder(this) .setTitle("Add New Todo") .setView(editText) .setPositiveButton("Add") { _, _ -> val todoText = editText.text.toString() if (todoText.isNotEmpty()) { dbHelper.addTodoItem(todoText) refreshTodoList() } } .setNegativeButton("Cancel", null) .create() dialog.show() } private fun showEditTodoDialog(position: Int) { val selectedTodo = todoAdapter.getItem(position) val editText = EditText(this) if (selectedTodo != null) { editText.setText(selectedTodo.todoText) } val dialog = AlertDialog.Builder(this) .setTitle("Edit Todo") .setView(editText) .setPositiveButton("Save") { _, _ -> val updatedTodoText = editText.text.toString() if (updatedTodoText.isNotEmpty()) { if (selectedTodo != null) { dbHelper.updateTodoItem(selectedTodo.id, updatedTodoText) } refreshTodoList() } } .setNegativeButton("Cancel", null) .create() dialog.show() } private fun refreshTodoList() { val newTodos = dbHelper.getAllTodos() todoAdapter.updateList(newTodos) } } package com.example.myapplication import import import import import import import android.annotation.SuppressLint android.app.Activity android.content.Intent android.os.Bundle android.widget.Button android.widget.EditText androidx.appcompat.app.AppCompatActivity class AddToDoActivity : AppCompatActivity() { @SuppressLint("MissingInflatedId") override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_add_to_do) val addTodoButton: Button = findViewById(R.id.addTodoButton) val cancelButton: Button = findViewById(R.id.cancelButton) val todoEditText: EditText = findViewById(R.id.todoEditText) addTodoButton.setOnClickListener { val todoText = todoEditText.text.toString() val resultIntent = Intent() resultIntent.putExtra("newTodo", todoText) setResult(Activity.RESULT_OK, resultIntent) finish() } cancelButton.setOnClickListener { finish() } } } package com.example.myapplication // DatabaseHelper.kt import android.annotation.SuppressLint import android.content.ContentValues import android.content.Context import android.database.Cursor import android.database.SQLException import android.database.sqlite.SQLiteDatabase import android.database.sqlite.SQLiteOpenHelper class DatabaseHelper(context: Context) : SQLiteOpenHelper(context, DATABASE_NAME, null, DATABASE_VERSION) { companion object { private const val private const val private const val private const val private const val } DATABASE_NAME = "todo_database" DATABASE_VERSION = 1 TABLE_TODO = "todo" KEY_ID = "id" KEY_TEXT = "todo_text" override fun onCreate(db: SQLiteDatabase) { val CREATE_TODO_TABLE = ("CREATE TABLE $TABLE_TODO($KEY_ID INTEGER PRIMARY KEY, $KEY_TEXT TEXT)") db.execSQL(CREATE_TODO_TABLE) } override fun onUpgrade(db: SQLiteDatabase, oldVersion: Int, newVersion: Int) { db.execSQL("DROP TABLE IF EXISTS $TABLE_TODO") onCreate(db) } fun addTodoItem(todoText: String) { val values = ContentValues() values.put(KEY_TEXT, todoText) val db = this.writableDatabase db.insert(TABLE_TODO, null, values) db.close() } fun updateTodoItem(id: Int, updatedText: String) { val values = ContentValues() values.put(KEY_TEXT, updatedText) val db = this.writableDatabase db.update(TABLE_TODO, values, "$KEY_ID=?", arrayOf(id.toString())) db.close() } @SuppressLint("Range") fun getAllTodos(): List<TodoItem> { val todoList = mutableListOf<TodoItem>() val query = "SELECT * FROM $TABLE_TODO" val db = this.readableDatabase try { val cursor: Cursor = db.rawQuery(query, null) if (cursor.moveToFirst()) { do { val id = cursor.getInt(cursor.getColumnIndex(KEY_ID)) val text = cursor.getString(cursor.getColumnIndex(KEY_TEXT)) val todoItem = TodoItem(id, text) todoList.add(todoItem) } while (cursor.moveToNext()) } } catch (e: SQLException) { e.printStackTrace() } db.close() return todoList } } // TodoItem.kt data class TodoItem(val id: Int, val todoText: String) package com.example.myapplication import android.content.Context import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import android.widget.ArrayAdapter import android.widget.TextView class TodoAdapter(context: Context, private var todoList: List<TodoItem>) : ArrayAdapter<TodoItem>(context, 0, todoList) { override fun getView(position: Int, convertView: View?, parent: ViewGroup): View { var convertView = convertView if (convertView == null) { convertView = LayoutInflater.from(context).inflate(android.R.layout.simple_list_item_1, parent, false) } val todoItem = getItem(position) val textView = convertView!!.findViewById<TextView>(android.R.id.text1) textView.text = todoItem?.todoText return convertView } fun updateList(newList: List<TodoItem>) { todoList.run { clear() addAll(newList) } } }