プロジェクト

全般

プロフィール

« | » 

リビジョン 0406a46c

高徹 高橋 徹 さんが5年以上前に追加

refs #166 Add Content Provider

差分を表示:

learn/android/TempRecorderClassicKt/.idea/vcs.xml
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$/../../.." vcs="Git" />
</component>
</project>
learn/android/TempRecorderClassicKt/app/src/main/AndroidManifest.xml
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<provider
android:name=".TempProvider"
android:authorities="com.torutk.temprecorder.kt.provider"
android:enabled="true"
android:exported="true"></provider>
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
learn/android/TempRecorderClassicKt/app/src/main/java/com/torutk/temprecorder/kt/MainActivity.kt
package com.torutk.temprecorder.kt
import android.content.ContentValues
import android.database.ContentObserver
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.os.Handler
import kotlinx.android.synthetic.main.activity_main.*
import java.time.LocalDateTime
import java.time.format.DateTimeFormatter
val DATE_TIME_VIEW_FORMATTER = DateTimeFormatter.ofPattern("MM.dd HH:mm")
class MainActivity : AppCompatActivity() {
lateinit var tempAdapter: TempAdapter
lateinit var tempProviderObserver: ContentObserver
private var measuredAt: LocalDateTime = LocalDateTime.now() // 検温日時
set(dateTime: LocalDateTime) {
field = dateTime
measuredAtTextView.text = field.format(DATE_TIME_VIEW_FORMATTER)
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
dec10MinButton.setOnClickListener { measuredAt = measuredAt.minusMinutes(10) }
inc10MinButton.setOnClickListener { measuredAt = measuredAt.plusMinutes(10) }
with(integralNumberPicker) {
minValue = 35
maxValue = 40
value = 36
wrapSelectorWheel = false
}
with(fractionNumberPicker) {
minValue = 0
maxValue = 9
value = 5
}
submitButton.setOnClickListener { submitTemp() }
tempRecyclerView.setHasFixedSize(true)
tempAdapter = TempAdapter(null)
tempRecyclerView.adapter = tempAdapter
tempProviderObserver = object : ContentObserver(Handler()) {
override fun onChange(selfChange: Boolean) {
super.onChange(selfChange)
queryTemp()
}
}
}
override fun onStart() {
super.onStart()
queryTemp()
}
override fun onResume() {
super.onResume()
measuredAt = LocalDateTime.now()
contentResolver.registerContentObserver(TempContract.CONTENT_URI, true, tempProviderObserver)
}
override fun onPause() {
super.onPause()
contentResolver.unregisterContentObserver(tempProviderObserver)
}
// 検温履歴の取得
private fun queryTemp() {
val cursor = contentResolver.query(
TempContract.CONTENT_URI, null, null, null, "_id DESC"
)
cursor?.let { tempAdapter.swapCursor(cursor) }
}
// 検温結果の登録
private fun submitTemp() {
val values = ContentValues().apply {
put(TempContract.TempEntry.MEASURED_AT, measuredAt.format(DateTimeFormatter.ISO_LOCAL_DATE_TIME))
put(TempContract.TempEntry.MEASUREMENT, integralNumberPicker.value + fractionNumberPicker.value / 10.0)
}
contentResolver.insert(TempContract.CONTENT_URI, values)
}
}
learn/android/TempRecorderClassicKt/app/src/main/java/com/torutk/temprecorder/kt/TempAdapter.kt
package com.torutk.temprecorder.kt
import android.database.Cursor
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.TextView
import androidx.recyclerview.widget.RecyclerView
import kotlinx.android.synthetic.main.list_item.view.*
import java.time.LocalDateTime
import java.time.format.DateTimeFormatter
class TempAdapter(private var cursor: Cursor?) : RecyclerView.Adapter<TempAdapter.TempViewHolder>() {
class TempViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
val dateTimeView: TextView = itemView.dateTimeView
val temperatureView: TextView = itemView.temperatureView
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): TempViewHolder {
return TempViewHolder(
LayoutInflater.from(parent.context).inflate(R.layout.list_item, parent, false)
)
}
override fun onBindViewHolder(holder: TempViewHolder, position: Int) {
cursor?.let {
if (it.isClosed) return
it.moveToPosition(position)
holder.dateTimeView.text = getDateTime(it)
holder.temperatureView.text = "%.1f".format(getTemperature(it))
}
}
private fun getDateTime(cursor: Cursor): String {
val isoDateTimetext = cursor.getString(cursor.getColumnIndex(TempContract.TempEntry.MEASURED_AT))
val measuredAt = LocalDateTime.parse(isoDateTimetext, DateTimeFormatter.ISO_LOCAL_DATE_TIME)
return measuredAt.format(DATE_TIME_VIEW_FORMATTER)
}
private fun getTemperature(cursor: Cursor): Double {
return cursor.getDouble(cursor.getColumnIndex(TempContract.TempEntry.MEASUREMENT))
}
override fun getItemCount() = cursor?.let { if (it.isClosed) 0 else it.count } ?: 0
fun swapCursor(newCursor: Cursor) {
if (cursor == newCursor) return
cursor = newCursor
if (cursor != null) {
notifyDataSetChanged()
} else {
notifyItemRangeChanged(0, itemCount)
}
}
}
learn/android/TempRecorderClassicKt/app/src/main/java/com/torutk/temprecorder/kt/TempContract.kt
package com.torutk.temprecorder.kt
import android.net.Uri
import android.provider.BaseColumns
class TempContract private constructor() {
companion object {
const val AUTHORITY = "com.tourtk.temprecorder.kt.provider"
val CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/temperatures")
}
class TempEntry private constructor() {
companion object {
const val _ID = BaseColumns._ID
const val _COUNT = BaseColumns._COUNT
const val TABLE_NAME = "Temperatures"
const val MEASURED_AT = "measured_at"
const val MEASUREMENT = "measurement"
}
}
}
learn/android/TempRecorderClassicKt/app/src/main/java/com/torutk/temprecorder/kt/TempDbHelper.kt
package com.torutk.temprecorder.kt
import android.content.Context
import android.database.sqlite.SQLiteDatabase
import android.database.sqlite.SQLiteOpenHelper
internal const val DATABASE_NAME = "body_temperature.db"
internal const val DATABASE_VERSION = 1
internal val CREATE_TABLE = """
|CREATE TABLE ${TempContract.TempEntry.TABLE_NAME} (
| ${TempContract.TempEntry._ID} INTEGER PRIMARY KEY AUTOINCREMENT,
| ${TempContract.TempEntry.MEASURED_AT} TEXT NOT NULL,
| ${TempContract.TempEntry.MEASUREMENT} REAL NOT NULL);
""".trimMargin()
class TempDbHelper(context: Context) : SQLiteOpenHelper(context, DATABASE_NAME, null, DATABASE_VERSION) {
override fun onCreate(db: SQLiteDatabase?) {
db?.execSQL(CREATE_TABLE)
}
override fun onUpgrade(db: SQLiteDatabase?, oldVersion: Int, newVersion: Int) {
db?.execSQL("DROP TABLE IF EXISTS ${TempContract.TempEntry.TABLE_NAME}")
onCreate(db)
}
}
learn/android/TempRecorderClassicKt/app/src/main/java/com/torutk/temprecorder/kt/TempProvider.kt
package com.torutk.temprecorder.kt
import android.content.ContentProvider
import android.content.ContentValues
import android.content.UriMatcher
import android.database.Cursor
import android.database.sqlite.SQLiteDatabase
import android.net.Uri
import java.lang.IllegalArgumentException
private const val TEMPERATURES = 1
private const val TEMPERATURES_ID = 2
private val uriMatcher = UriMatcher(UriMatcher.NO_MATCH).apply {
addURI(TempContract.AUTHORITY, "temperatures", TEMPERATURES)
addURI(TempContract.AUTHORITY, "temperatures/#", TEMPERATURES_ID)
}
class TempProvider : ContentProvider() {
private lateinit var dbHelper: TempDbHelper
override fun delete(uri: Uri, selection: String?, selectionArgs: Array<String>?): Int {
TODO("Implement this to handle requests to delete one or more rows")
}
override fun getType(uri: Uri): String? {
TODO(
"Implement this to handle requests for the MIME type of the data" +
"at the given URI"
)
}
override fun insert(uri: Uri, values: ContentValues?): Uri? {
val database = dbHelper.writableDatabase
val id = database.insert(TempContract.TempEntry.TABLE_NAME, null, values)
val insertedUri = Uri.withAppendedPath(uri, id.toString())
context?.contentResolver?.notifyChange(uri, null)
return insertedUri
}
override fun onCreate(): Boolean {
dbHelper = TempDbHelper(context!!)
return true
}
override fun query(
uri: Uri, projection: Array<String>?, selection: String?,
selectionArgs: Array<String>?, sortOrder: String?
): Cursor? {
val db = dbHelper.readableDatabase
val cursor: Cursor
when (uriMatcher.match(uri)) {
TEMPERATURES -> {
cursor = db.query(
TempContract.TempEntry.TABLE_NAME,
projection,
selection,
selectionArgs,
null,
null,
sortOrder
)
}
TEMPERATURES_ID -> {
cursor = db.query(
TempContract.TempEntry.TABLE_NAME,
projection,
"_id = ?",
arrayOf(uri.lastPathSegment),
null,
null,
sortOrder
)
}
else -> throw IllegalArgumentException("Unknown URI ${uri}")
}
cursor.setNotificationUri(context!!.contentResolver, uri)
return cursor
}
override fun update(
uri: Uri, values: ContentValues?, selection: String?,
selectionArgs: Array<String>?
): Int {
TODO("Implement this to handle requests to update one or more rows.")
}
}
learn/android/TempRecorderClassicKt/app/src/main/res/layout/activity_main.xml
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/timeTextView"
android:id="@+id/measuredAtTextView"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginTop="16dp"
android:fontFamily="monospace"
android:text="09.13 11:33"
android:text="09.15 18:50"
android:textAlignment="center"
android:textSize="36sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="1.0"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/textView" />
......
app:layout_constraintEnd_toStartOf="@+id/inc10MinButton"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/timeTextView" />
app:layout_constraintTop_toBottomOf="@+id/measuredAtTextView" />
<Button
android:id="@+id/inc10MinButton"
......
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginTop="16dp"
app:layoutManager="LinearLayoutManager"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
learn/android/TempRecorderClassicKt/app/src/main/res/layout/list_item.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="@+id/dateTimeView"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:fontFamily="monospace"
android:text="TextView"
android:textAlignment="center"
android:textSize="18sp" />
<TextView
android:id="@+id/temperatureView"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:fontFamily="monospace"
android:text="TextView"
android:textAlignment="center"
android:textSize="18sp" />
</LinearLayout>

他の形式にエクスポート: Unified diff