Skip to content

Instantly share code, notes, and snippets.

@decodeandroid
Last active December 18, 2024 18:36
Show Gist options
  • Save decodeandroid/918cab90be073e3dc7044f1a731996bf to your computer and use it in GitHub Desktop.
Save decodeandroid/918cab90be073e3dc7044f1a731996bf to your computer and use it in GitHub Desktop.
Content Resolver and Content Provider in Android
<permission
android:name="com.example.learngit.ACCESS_USERS"
android:protectionLevel="normal" />
<provider
android:name=".phase2.MyContentProvider"
android:authorities="com.example.learngit"
android:enabled="true"
android:exported="true"
android:permission="com.example.learngit.ACCESS_USERS"/>
<queries>
<package android:name="com.example.provider"/>
</queries>
import android.content.ContentProvider
import android.content.ContentUris
import android.content.ContentValues
import android.content.Context
import android.content.UriMatcher
import android.database.Cursor
import android.database.sqlite.SQLiteDatabase
import android.database.sqlite.SQLiteException
import android.database.sqlite.SQLiteOpenHelper
import android.database.sqlite.SQLiteQueryBuilder
import android.net.Uri
class MyContentProvider : ContentProvider() {
companion object {
// defining authority so that other application can access it
private const val PROVIDER_NAME = "com.example.learngit"
private const val PATH="users"
// parsing the content URI
val CONTENT_URI: Uri = Uri.parse("content://$PROVIDER_NAME/$PATH")
const val USERS_URI = 1
const val USER_ID_URI = 2
private val values: HashMap<String, String>? = null
// declaring name of the database
const val DATABASE_NAME = "UserDB"
// declaring table name of the database
const val TABLE_NAME = "Users"
// declaring version of the database
const val DATABASE_VERSION = 1
// sql query to create the table
const val CREATE_DB_TABLE =
("CREATE TABLE $TABLE_NAME (id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL);")
// to match the content URI every time user access table under content provider
private val uriMatcher = UriMatcher(UriMatcher.NO_MATCH).apply {
// to access whole table
addURI(PROVIDER_NAME, "users", USERS_URI)
// to access a particular row of the table
addURI(PROVIDER_NAME, "users/#", USER_ID_URI)
}
}
override fun getType(uri: Uri): String = when (uriMatcher.match(uri)) {
USERS_URI -> "vnd.android.cursor.dir/vnd.com.example.learngit.users"
USER_ID_URI -> "vnd.android.cursor.item/vnd.com.example.learngit.users"
else -> throw IllegalArgumentException("Unknown URI: $uri")
}
// creating the database
override fun onCreate(): Boolean {
val context = context
val dbHelper = DatabaseHelper(context)
db = dbHelper.writableDatabase
return if (db != null) {
true
} else false
}
override fun query(
uri: Uri, projection: Array<String>?, selection: String?,
selectionArgs: Array<String>?, sortOrder: String?
): Cursor? {
val qb = SQLiteQueryBuilder() // Query builder to construct the query
qb.tables = TABLE_NAME // Set the table name to query
when (uriMatcher.match(uri)) {
USERS_URI -> {
// Query the whole table
qb.projectionMap = values
}
USER_ID_URI -> {
// Query a single row; filter the row using the ID from the URI
qb.projectionMap = values
val id = uri.lastPathSegment // Extract the ID from the URI
qb.appendWhere("id = $id") // Add WHERE clause to match the ID
}
else -> throw IllegalArgumentException("Unknown URI: $uri")
}
// Set the default sort order if none is provided
val finalSortOrder = sortOrder ?: "id"
// Execute the query on the database
val cursor = qb.query(
db, // Database reference
projection, // Columns to retrieve
selection, // WHERE clause passed from the caller
selectionArgs, // Arguments for the WHERE clause
null, // GROUP BY
null, // HAVING
finalSortOrder // SORT BY
)
// Notify observers of any changes in the queried URI
cursor.setNotificationUri(context!!.contentResolver, uri)
return cursor
}
// adding data to the database
override fun insert(uri: Uri, values: ContentValues?): Uri {
val rowID = db!!.insert(TABLE_NAME, "", values)
if (rowID > 0) {
val _uri = ContentUris.withAppendedId(CONTENT_URI, rowID)
context!!.contentResolver.notifyChange(_uri, null)
return _uri
}
throw SQLiteException("Failed to add a record into $uri")
}
override fun update(
uri: Uri, values: ContentValues?, selection: String?,
selectionArgs: Array<String>?
): Int {
val count: Int = when (uriMatcher.match(uri)) {
USERS_URI -> db!!.update(TABLE_NAME, values, selection, selectionArgs)
else -> throw IllegalArgumentException("Unknown URI $uri")
}
context!!.contentResolver.notifyChange(uri, null)
return count
}
override fun delete(
uri: Uri,
selection: String?,
selectionArgs: Array<String>?
): Int {
val count: Int = when (uriMatcher.match(uri)) {
USERS_URI -> db!!.delete(TABLE_NAME, selection, selectionArgs)
else -> throw IllegalArgumentException("Unknown URI $uri")
}
context!!.contentResolver.notifyChange(uri, null)
return count
}
// creating object of database
// to perform query
private var db: SQLiteDatabase? = null
// creating a database
private class DatabaseHelper // defining a constructor
(context: Context?) : SQLiteOpenHelper(
context,
DATABASE_NAME,
null,
DATABASE_VERSION
) {
// creating a table in the database
override fun onCreate(db: SQLiteDatabase) {
db.execSQL(CREATE_DB_TABLE)
}
override fun onUpgrade(
db: SQLiteDatabase,
oldVersion: Int,
newVersion: Int
) {
// sql query to drop a table
// having similar name
db.execSQL("DROP TABLE IF EXISTS $TABLE_NAME")
onCreate(db)
}
}
}
import android.content.ContentResolver
import android.content.ContentValues
import android.net.Uri
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Delete
import androidx.compose.material.icons.filled.Edit
import androidx.compose.material3.Button
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.Text
import androidx.compose.material3.TextField
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateListOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
@Composable
fun UserListScreen(contentResolver: ContentResolver) {
// defining authority so that other application can access it
val PROVIDER_NAME = "com.example.learngit"
// parsing the content URI
val CONTENT_URI = Uri.parse("content://$PROVIDER_NAME/users")
// State to hold list of users
val users = remember { mutableStateListOf<Map<String, String>>() }
// States for input fields
var name by remember { mutableStateOf("") }
var userIdToUpdate by remember { mutableStateOf<Int?>(null) }
// Function to fetch all users
fun fetchUsers() {
val cursor = contentResolver.query(
CONTENT_URI,
null, // Projection
null, // Selection
null, // Selection args
null // Sort order
)
users.clear()
cursor?.use {
while (it.moveToNext()) {
val id = it.getInt(it.getColumnIndexOrThrow("id"))
val name = it.getString(it.getColumnIndexOrThrow("name"))
users.add(mapOf("id" to id.toString(), "name" to name))
}
}
}
// Insert or Update function
fun saveUser() {
if (name.isNotBlank()) {
val values = ContentValues().apply {
put("name", name)
}
if (userIdToUpdate != null) {
val selection = "id = ?"
val selectionArgs = arrayOf(userIdToUpdate.toString())
contentResolver.update(
CONTENT_URI,
values, selection, selectionArgs
)
userIdToUpdate = null
} else {
contentResolver.insert(CONTENT_URI, values)
}
name = ""
fetchUsers()
}
}
// Delete function
fun deleteUser(id: Int) {
val selection = "id = ?"
val selectionArgs = arrayOf(id.toString())
contentResolver.delete(CONTENT_URI, selection, selectionArgs)
fetchUsers()
}
// Initial data fetch
LaunchedEffect(Unit) {
fetchUsers()
}
// UI
Column(modifier = Modifier.fillMaxSize().padding(16.dp)) {
// Input fields
TextField(
value = name,
onValueChange = { name = it },
label = { Text("Name") },
modifier = Modifier.fillMaxWidth()
)
Spacer(modifier = Modifier.height(8.dp))
Button(
onClick = { saveUser() },
modifier = Modifier.fillMaxWidth()
) {
Text(if (userIdToUpdate != null) "Update User" else "Add User")
}
Spacer(modifier = Modifier.height(16.dp))
// User list
LazyColumn {
items(users) { user ->
Row(
modifier = Modifier
.fillMaxWidth()
.padding(vertical = 8.dp),
horizontalArrangement = Arrangement.SpaceBetween
) {
Column {
Text("ID: ${user["id"]}")
Text("Name: ${user["name"]}")
}
Row {
IconButton(onClick = {
name = user["name"].orEmpty()
userIdToUpdate = user["id"]?.toInt()
}) {
Icon(
imageVector = Icons.Default.Edit,
contentDescription = "Edit"
)
}
IconButton(onClick = {
deleteUser(user["id"]!!.toInt())
}) {
Icon(
imageVector = Icons.Default.Delete,
contentDescription = "Delete"
)
}
}
}
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment