Last active
December 18, 2024 18:36
-
-
Save decodeandroid/918cab90be073e3dc7044f1a731996bf to your computer and use it in GitHub Desktop.
Content Resolver and Content Provider in Android
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<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"/> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<queries> | |
<package android:name="com.example.provider"/> | |
</queries> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) | |
} | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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