In this tutorial we will make a “Notes App” using SQLite and Kotlin.
- It will contain following features.
- Enter Data
- Retrieve Data in ListView
- Update/Edit Data
- Delete Data
- Search Data
- Copy Data
- Share Data
Step 01: Create a new Project or open new project
Step 02: Create layout resource file under res>layout folder
Step 03: Create new “Android Resource Directory” by clicking “res>New>Android Resource Directory”, choose menu from Resource type
Step 04: Create menu_main.xml by clicking “menu>New>Menu resource file”
Step 05: Create empty Activity name it as “AddNoteActivity.kt”
Step 06: Create Class Note.kt
Step 07: Create Class DbManager.kt
Step 08: Source Code
build.gradle(module:App)
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
android {
compileSdkVersion 27
defaultConfig {
applicationId "com.jigopost.notesapp"
minSdkVersion 16
targetSdkVersion 27
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation "org.jetbrains.kotlin:kotlin-stdlib-jre7:$kotlin_version"
implementation 'com.android.support:appcompat-v7:27.1.1'
implementation 'com.android.support:cardview-v7:27.1.1'
implementation 'com.android.support.constraint:constraint-layout:1.1.0'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'com.android.support.test:runner:1.0.2'
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
}
menu_main.xml
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="@+id/action_settings"
android:title="settings" />
<item
android:id="@+id/app_bar_search"
android:icon="@drawable/ic_action_search"
android:title="Search"
app:actionViewClass="android.support.v7.widget.SearchView"
app:showAsAction="always" />
<item
android:id="@+id/addNote"
android:icon="@drawable/ic_action_add"
android:title="Add Nore"
app:showAsAction="always" />
</menu>
colors.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="colorPrimary">#0488d1</color>
<color name="colorPrimaryDark">#0477bd</color>
<color name="colorAccent">#0488d1</color>
<color name="gray">#e8e8e8</color>
<color name="white">#fff</color>
<color name="black">#000</color>
</resources>
row.xml
<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:cardBackgroundColor="@color/white"
app:cardCornerRadius="3dp"
app:cardElevation="3dp"
app:cardUseCompatPadding="true">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="5dp"
android:gravity="end|bottom"
android:orientation="vertical">
<TextView
android:id="@+id/titleTv"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Title"
android:textColor="@color/colorPrimary"
android:textSize="22sp"
android:textStyle="bold" />
<TextView
android:id="@+id/descTv"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="There may be a very long description of the note"
android:textSize="18sp" />
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:layout_marginBottom="3dp"
android:layout_marginTop="2dp"
android:background="@color/colorPrimaryDark" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="end"
android:orientation="horizontal">
<ImageButton
android:id="@+id/deleteBtn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="5dp"
android:layout_marginRight="5dp"
android:background="@null"
android:src="@drawable/ic_action_delete" />
<ImageButton
android:id="@+id/editBtn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="5dp"
android:layout_marginRight="5dp"
android:background="@null"
android:src="@drawable/ic_action_edit" />
<ImageButton
android:id="@+id/copyBtn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="5dp"
android:layout_marginRight="5dp"
android:background="@null"
android:src="@drawable/ic_action_copy" />
<ImageButton
android:id="@+id/shareBtn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="5dp"
android:layout_marginRight="5dp"
android:background="@null"
android:src="@drawable/ic_action_share" />
</LinearLayout>
</LinearLayout>
</android.support.v7.widget.CardView>
Note.kt
package com.jigopost.notesapp
class Note(nodeID: Int, nodeName: String, nodeDes: String) {
var nodeID: Int? = nodeID
var nodeName: String? = nodeName
var nodeDes: String? = nodeDes
}
DbManager.kt
package com.jigopost.notesapp
import android.app.DownloadManager
import android.content.ContentValues
import android.content.Context
import android.database.Cursor
import android.database.sqlite.SQLiteDatabase
import android.database.sqlite.SQLiteOpenHelper
import android.database.sqlite.SQLiteQueryBuilder
import android.widget.Toast
class DbManager {
//database name
var dbName = "MyNotes"
//table name
var dbTable = "Notes"
//columns
var colID = "ID"
var colTitle = "Title"
var colDes = "Description"
//database version
var dbVersion = 1
//CREATE TABLE IF NOT EXISTS MyNotes (ID INTEGER PRIMARY KEY,title TEXT, Description TEXT);"
val sqlCreateTable = "CREATE TABLE IF NOT EXISTS " + dbTable + " (" + colID + " INTEGER PRIMARY KEY," + colTitle + " TEXT, " + colDes + " TEXT);"
var sqlDB: SQLiteDatabase? = null
constructor(context: Context) {
var db = DatabaseHelperNotes(context)
sqlDB = db.writableDatabase
}
inner class DatabaseHelperNotes : SQLiteOpenHelper {
var context: Context? = null
constructor(context: Context) : super(context, dbName, null, dbVersion) {
this.context = context
}
override fun onCreate(db: SQLiteDatabase?) {
db!!.execSQL(sqlCreateTable)
Toast.makeText(this.context, "database created...", Toast.LENGTH_SHORT).show()
}
override fun onUpgrade(db: SQLiteDatabase?, oldVersion: Int, newVersion: Int) {
db!!.execSQL("Drop table if Exists" + dbTable)
}
}
fun insert(values: ContentValues): Long {
val ID = sqlDB!!.insert(dbTable, "", values)
return ID
}
fun Query(projection: Array<String>, selection: String, selectionArgs: Array<String>, sorOrder: String): Cursor {
val qb = SQLiteQueryBuilder();
qb.tables = dbTable
val cursor = qb.query(sqlDB, projection, selection, selectionArgs, null, null, sorOrder)
return cursor
}
fun delete(selection: String, selectionArgs: Array<String>): Int {
val count = sqlDB!!.delete(dbTable, selection, selectionArgs)
return count
}
fun update(values: ContentValues, selection: String, selectionArgs: Array<String>): Int {
val count = sqlDB!!.update(dbTable, values, selection, selectionArgs)
return count
}
}
activity_main.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:background="@color/gray"
android:orientation="vertical"
tools:context=".MainActivity">
<!--Display list of notes from SQLite/database-->
<ListView
android:id="@+id/notesLv"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:divider="@null"
android:dividerHeight="1dp" />
</LinearLayout>
MainActivity.kt
package com.jigopost.notesapp
import android.app.SearchManager
import android.content.Context
import android.content.Intent
import android.support.v7.app.AppCompatActivity
import android.os.Bundle
import android.support.v7.widget.SearchView
import android.text.ClipboardManager
import android.view.Menu
import android.view.MenuItem
import android.view.View
import android.view.ViewGroup
import android.widget.BaseAdapter
import android.widget.Toast
import kotlinx.android.synthetic.main.activity_main.*
import kotlinx.android.synthetic.main.row.*
import kotlinx.android.synthetic.main.row.view.*
class MainActivity : AppCompatActivity() {
var listNotes = ArrayList<Note>()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
//Load from DB
LoadQuery("%")
}
override fun onResume() {
super.onResume()
LoadQuery("%")
}
private fun LoadQuery(title: String) {
var dbManager = DbManager(this)
val projections = arrayOf("ID", "Title", "Description")
val selectionArgs = arrayOf(title)
val cursor = dbManager.Query(projections, "Title like ?", selectionArgs, "Title")
listNotes.clear()
if (cursor.moveToFirst()) {
do {
val ID = cursor.getInt(cursor.getColumnIndex("ID"))
val Title = cursor.getString(cursor.getColumnIndex("Title"))
val Description = cursor.getString(cursor.getColumnIndex("Description"))
listNotes.add(Note(ID, Title, Description))
} while (cursor.moveToNext())
}
//adapter
var myNotesAdapter = MyNotesAdapter(this, listNotes)
//set adapter
notesLv.adapter = myNotesAdapter
//get total number of tasks from ListView
val total = notesLv.count
//actionbar
val mActionBar = supportActionBar
if (mActionBar != null) {
//set to actionbar as subtitle of actionbar
mActionBar.subtitle = "You have $total note(s) in list..."
}
}
override fun onCreateOptionsMenu(menu: Menu?): Boolean {
menuInflater.inflate(R.menu.main_menu, menu)
//searchView
val sv: SearchView = menu!!.findItem(R.id.app_bar_search).actionView as SearchView
val sm = getSystemService(Context.SEARCH_SERVICE) as SearchManager
sv.setSearchableInfo(sm.getSearchableInfo(componentName))
sv.setOnQueryTextListener(object : SearchView.OnQueryTextListener {
override fun onQueryTextSubmit(query: String?): Boolean {
LoadQuery("%" + query + "%")
return false
}
override fun onQueryTextChange(newText: String?): Boolean {
LoadQuery("%" + newText + "%")
return false
}
});
return super.onCreateOptionsMenu(menu)
}
override fun onOptionsItemSelected(item: MenuItem?): Boolean {
if (item != null) {
when (item.itemId) {
R.id.addNote -> {
startActivity(Intent(this, AddNoteActivity::class.java))
}
R.id.action_settings -> {
Toast.makeText(this, "Settings", Toast.LENGTH_SHORT).show()
}
}
}
return super.onOptionsItemSelected(item)
}
inner class MyNotesAdapter : BaseAdapter {
var listNotesAdapter = ArrayList<Note>()
var context: Context? = null
constructor(context: Context, listNotesAdapter: ArrayList<Note>) : super() {
this.listNotesAdapter = listNotesAdapter
this.context = context
}
override fun getView(position: Int, convertView: View?, parent: ViewGroup?): View {
//inflate layout row.xml
var myView = layoutInflater.inflate(R.layout.row, null)
val myNote = listNotesAdapter[position]
myView.titleTv.text = myNote.nodeName
myView.descTv.text = myNote.nodeDes
//delete button click
myView.deleteBtn.setOnClickListener {
var dbManager = DbManager(this.context!!)
val selectionArgs = arrayOf(myNote.nodeID.toString())
dbManager.delete("ID=?", selectionArgs)
LoadQuery("%")
}
//edit//update button click
myView.editBtn.setOnClickListener {
GoToUpdateFun(myNote)
}
//copy btn click
myView.copyBtn.setOnClickListener {
//get title
val title = myView.titleTv.text.toString()
//get description
val desc = myView.descTv.text.toString()
//concatinate
val s = title + "\n" + desc
val cb = getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
cb.text = s // add to clipboard
Toast.makeText(this@MainActivity, "Copied...", Toast.LENGTH_SHORT).show()
}
//share btn click
myView.shareBtn.setOnClickListener {
//get title
val title = myView.titleTv.text.toString()
//get description
val desc = myView.descTv.text.toString()
//concatenate
val s = title + "\n" + desc
//share intent
val shareIntent = Intent()
shareIntent.action = Intent.ACTION_SEND
shareIntent.type = "text/plain"
shareIntent.putExtra(Intent.EXTRA_TEXT, s)
startActivity(Intent.createChooser(shareIntent, s))
}
return myView
}
override fun getItem(position: Int): Any {
return listNotesAdapter[position]
}
override fun getItemId(position: Int): Long {
return position.toLong()
}
override fun getCount(): Int {
return listNotesAdapter.size
}
}
private fun GoToUpdateFun(myNote: Note) {
var intent = Intent(this, AddNoteActivity::class.java)
intent.putExtra("ID", myNote.nodeID) //put id
intent.putExtra("name", myNote.nodeName) //ut name
intent.putExtra("des", myNote.nodeDes) //put description
startActivity(intent) //start activity
}
}
activity_add_note.xml
<?xml version="1.0" encoding="utf-8"?>
<ScrollView 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=".AddNoteActivity">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<android.support.v7.widget.CardView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="5dp"
app:cardBackgroundColor="@color/white"
app:cardCornerRadius="3dp"
app:cardElevation="3dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<EditText
android:id="@+id/titleEt"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@null"
android:hint="Enter Title"
android:padding="10dp"
android:singleLine="true"
android:textStyle="bold" />
<EditText
android:id="@+id/descEt"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@null"
android:gravity="top"
android:hint="Enter description..."
android:minHeight="100dp"
android:padding="10dp" />
</LinearLayout>
</android.support.v7.widget.CardView>
<Button
android:id="@+id/addBtn"
style="@style/Base.Widget.AppCompat.Button.Colored"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="end"
android:layout_marginEnd="5dp"
android:layout_marginRight="5dp"
android:onClick="addFunc"
android:text="Add" />
</LinearLayout>
</ScrollView>
AddNoteActivity.kt
package com.jigopost.notesapp
import android.content.ContentValues
import android.support.v7.app.AppCompatActivity
import android.os.Bundle
import android.view.View
import android.widget.Toast
import kotlinx.android.synthetic.main.activity_add_note.*
class AddNoteActivity : AppCompatActivity() {
val dbTable = "Notes"
var id = 0
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_add_note)
try {
val bundle:Bundle = intent.extras
id = bundle.getInt("ID", 0)
if (id!=0){
titleEt.setText(bundle.getString("name"))
descEt.setText(bundle.getString("des"))
}
}catch (ex:Exception){}
}
fun addFunc(view:View){
var dbManager = DbManager(this)
var values = ContentValues()
values.put("Title", titleEt.text.toString())
values.put("Description", descEt.text.toString())
if (id ==0){
val ID = dbManager.insert(values)
if (ID>0){
Toast.makeText(this, "Note is added", Toast.LENGTH_SHORT).show()
finish()
}
else{
Toast.makeText(this, "Error adding note...", Toast.LENGTH_SHORT).show()
}
}
else{
var selectionArgs = arrayOf(id.toString())
val ID = dbManager.update(values, "ID=?", selectionArgs)
if (ID>0){
Toast.makeText(this, "Note is added", Toast.LENGTH_SHORT).show()
finish()
}
else{
Toast.makeText(this, "Error adding note...", Toast.LENGTH_SHORT).show()
}
}
}
}
Step 09: Run Project