From f48be3c6aef99e752df8a7e64d8489c5f1fef496 Mon Sep 17 00:00:00 2001 From: jo-elimu <1451036+jo-elimu@users.noreply.github.com> Date: Fri, 27 Sep 2024 17:52:49 +0700 Subject: [PATCH] feat: add video learning event - add db entity Refs #155 --- README.md | 2 - .../ai.elimu.analytics.db.RoomDb/7.json | 412 ++++++++++++++++++ .../java/ai/elimu/analytics/db/RoomDb.java | 17 +- .../analytics/entity/VideoLearningEvent.java | 42 ++ .../receiver/VideoLearningEventReceiver.java | 27 +- 5 files changed, 488 insertions(+), 12 deletions(-) create mode 100644 app/schemas/ai.elimu.analytics.db.RoomDb/7.json create mode 100644 app/src/main/java/ai/elimu/analytics/entity/VideoLearningEvent.java diff --git a/README.md b/README.md index 2c9ffaa..a8ad1c1 100644 --- a/README.md +++ b/README.md @@ -74,8 +74,6 @@ migration (SQL script) in Follow these steps: 1. Add the new/modified `@Entity` to [`app/src/main/java/ai/elimu/analytics/entity/`](app/src/main/java/ai/elimu/analytics/entity/) -1. Add the entity's DAO interface to [`app/src/main/java/ai/elimu/analytics/dao/`](app/src/main/java/ai/elimu/analytics/dao/) -1. Include the DAO interface in [`app/src/main/java/ai/elimu/analytics/db/RoomDb.java`](app/src/main/java/ai/elimu/analytics/db/RoomDb.java) 1. Include the entity in the `entities` section of the `@Database` in [`app/src/main/java/ai/elimu/analytics/db/RoomDb.java`](app/src/main/java/ai/elimu/analytics/db/RoomDb.java) 1. Bump the `@Database` version in [`app/src/main/java/ai/elimu/analytics/db/RoomDb.java`](app/src/main/java/ai/elimu/analytics/db/RoomDb.java) 1. Build the code with `./gradlew clean build` diff --git a/app/schemas/ai.elimu.analytics.db.RoomDb/7.json b/app/schemas/ai.elimu.analytics.db.RoomDb/7.json new file mode 100644 index 0000000..42c8384 --- /dev/null +++ b/app/schemas/ai.elimu.analytics.db.RoomDb/7.json @@ -0,0 +1,412 @@ +{ + "formatVersion": 1, + "database": { + "version": 7, + "identityHash": "d5ac45a7057d09a8aaf670fbc2725b4c", + "entities": [ + { + "tableName": "LetterLearningEvent", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`letterId` INTEGER, `letterText` TEXT NOT NULL, `learningEventType` TEXT NOT NULL, `androidId` TEXT NOT NULL, `packageName` TEXT NOT NULL, `time` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT)", + "fields": [ + { + "fieldPath": "letterId", + "columnName": "letterId", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "letterText", + "columnName": "letterText", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "learningEventType", + "columnName": "learningEventType", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "androidId", + "columnName": "androidId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "packageName", + "columnName": "packageName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "time", + "columnName": "time", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": false + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "LetterAssessmentEvent", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`letterId` INTEGER, `letterText` TEXT NOT NULL, `masteryScore` REAL NOT NULL, `timeSpentMs` INTEGER NOT NULL, `androidId` TEXT NOT NULL, `packageName` TEXT NOT NULL, `time` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT)", + "fields": [ + { + "fieldPath": "letterId", + "columnName": "letterId", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "letterText", + "columnName": "letterText", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "masteryScore", + "columnName": "masteryScore", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "timeSpentMs", + "columnName": "timeSpentMs", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "androidId", + "columnName": "androidId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "packageName", + "columnName": "packageName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "time", + "columnName": "time", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": false + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "LetterSoundLearningEvent", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`letterSoundId` INTEGER, `letterSoundLetterTexts` TEXT NOT NULL, `letterSoundSoundValuesIpa` TEXT NOT NULL, `androidId` TEXT NOT NULL, `packageName` TEXT NOT NULL, `time` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT)", + "fields": [ + { + "fieldPath": "letterSoundId", + "columnName": "letterSoundId", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "letterSoundLetterTexts", + "columnName": "letterSoundLetterTexts", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "letterSoundSoundValuesIpa", + "columnName": "letterSoundSoundValuesIpa", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "androidId", + "columnName": "androidId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "packageName", + "columnName": "packageName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "time", + "columnName": "time", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": false + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "WordLearningEvent", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`wordId` INTEGER, `wordText` TEXT NOT NULL, `learningEventType` TEXT NOT NULL, `androidId` TEXT NOT NULL, `packageName` TEXT NOT NULL, `time` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT)", + "fields": [ + { + "fieldPath": "wordId", + "columnName": "wordId", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "wordText", + "columnName": "wordText", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "learningEventType", + "columnName": "learningEventType", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "androidId", + "columnName": "androidId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "packageName", + "columnName": "packageName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "time", + "columnName": "time", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": false + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "WordAssessmentEvent", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`wordId` INTEGER, `wordText` TEXT NOT NULL, `masteryScore` REAL NOT NULL, `timeSpentMs` INTEGER NOT NULL, `androidId` TEXT NOT NULL, `packageName` TEXT NOT NULL, `time` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT)", + "fields": [ + { + "fieldPath": "wordId", + "columnName": "wordId", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "wordText", + "columnName": "wordText", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "masteryScore", + "columnName": "masteryScore", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "timeSpentMs", + "columnName": "timeSpentMs", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "androidId", + "columnName": "androidId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "packageName", + "columnName": "packageName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "time", + "columnName": "time", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": false + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "StoryBookLearningEvent", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`storyBookId` INTEGER NOT NULL, `learningEventType` TEXT NOT NULL, `androidId` TEXT NOT NULL, `packageName` TEXT NOT NULL, `time` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT)", + "fields": [ + { + "fieldPath": "storyBookId", + "columnName": "storyBookId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "learningEventType", + "columnName": "learningEventType", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "androidId", + "columnName": "androidId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "packageName", + "columnName": "packageName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "time", + "columnName": "time", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": false + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "VideoLearningEvent", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`videoId` INTEGER, `videoTitle` TEXT NOT NULL, `learningEventType` TEXT NOT NULL, `androidId` TEXT NOT NULL, `packageName` TEXT NOT NULL, `time` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT)", + "fields": [ + { + "fieldPath": "videoId", + "columnName": "videoId", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "videoTitle", + "columnName": "videoTitle", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "learningEventType", + "columnName": "learningEventType", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "androidId", + "columnName": "androidId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "packageName", + "columnName": "packageName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "time", + "columnName": "time", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": false + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + } + ], + "views": [], + "setupQueries": [ + "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", + "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'd5ac45a7057d09a8aaf670fbc2725b4c')" + ] + } +} \ No newline at end of file diff --git a/app/src/main/java/ai/elimu/analytics/db/RoomDb.java b/app/src/main/java/ai/elimu/analytics/db/RoomDb.java index 6bfdd49..df15ffe 100644 --- a/app/src/main/java/ai/elimu/analytics/db/RoomDb.java +++ b/app/src/main/java/ai/elimu/analytics/db/RoomDb.java @@ -23,11 +23,12 @@ import ai.elimu.analytics.entity.LetterLearningEvent; import ai.elimu.analytics.entity.LetterSoundLearningEvent; import ai.elimu.analytics.entity.StoryBookLearningEvent; +import ai.elimu.analytics.entity.VideoLearningEvent; import ai.elimu.analytics.entity.WordAssessmentEvent; import ai.elimu.analytics.entity.WordLearningEvent; import timber.log.Timber; -@Database(version = 6, entities = {LetterLearningEvent.class, LetterAssessmentEvent.class, LetterSoundLearningEvent.class, WordLearningEvent.class, WordAssessmentEvent.class, StoryBookLearningEvent.class}) +@Database(version = 7, entities = {LetterLearningEvent.class, LetterAssessmentEvent.class, LetterSoundLearningEvent.class, WordLearningEvent.class, WordAssessmentEvent.class, StoryBookLearningEvent.class, VideoLearningEvent.class}) @TypeConverters({Converters.class}) public abstract class RoomDb extends RoomDatabase { @@ -58,7 +59,8 @@ public static RoomDb getDatabase(final Context context) { MIGRATION_2_3, MIGRATION_3_4, MIGRATION_4_5, - MIGRATION_5_6 + MIGRATION_5_6, + MIGRATION_6_7 ) .build(); } @@ -122,4 +124,15 @@ public void migrate(@NonNull SupportSQLiteDatabase database) { database.execSQL(sql); } }; + + private static final Migration MIGRATION_6_7 = new Migration(6, 7) { + @Override + public void migrate(@NonNull SupportSQLiteDatabase database) { + Timber.i("migrate (" + database.getVersion() + " --> 7)"); + + String sql = "CREATE TABLE IF NOT EXISTS `VideoLearningEvent` (`videoId` INTEGER, `videoTitle` TEXT NOT NULL, `learningEventType` TEXT NOT NULL, `androidId` TEXT NOT NULL, `packageName` TEXT NOT NULL, `time` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT)"; + Timber.i("sql: " + sql); + database.execSQL(sql); + } + }; } diff --git a/app/src/main/java/ai/elimu/analytics/entity/VideoLearningEvent.java b/app/src/main/java/ai/elimu/analytics/entity/VideoLearningEvent.java new file mode 100644 index 0000000..5dcb993 --- /dev/null +++ b/app/src/main/java/ai/elimu/analytics/entity/VideoLearningEvent.java @@ -0,0 +1,42 @@ +package ai.elimu.analytics.entity; + +import androidx.annotation.NonNull; +import androidx.room.Entity; + +import ai.elimu.model.v2.enums.analytics.LearningEventType; + +@Entity +public class VideoLearningEvent extends LearningEvent { + + private Long videoId; + + @NonNull + private String videoTitle; + + @NonNull + private LearningEventType learningEventType; + + public Long getVideoId() { + return videoId; + } + + public void setVideoId(Long videoId) { + this.videoId = videoId; + } + + public String getVideoTitle() { + return videoTitle; + } + + public void setVideoTitle(String videoTitle) { + this.videoTitle = videoTitle; + } + + public LearningEventType getLearningEventType() { + return learningEventType; + } + + public void setLearningEventType(LearningEventType learningEventType) { + this.learningEventType = learningEventType; + } +} diff --git a/app/src/main/java/ai/elimu/analytics/receiver/VideoLearningEventReceiver.java b/app/src/main/java/ai/elimu/analytics/receiver/VideoLearningEventReceiver.java index 6e952a3..2602d0a 100644 --- a/app/src/main/java/ai/elimu/analytics/receiver/VideoLearningEventReceiver.java +++ b/app/src/main/java/ai/elimu/analytics/receiver/VideoLearningEventReceiver.java @@ -3,9 +3,11 @@ import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; +import android.provider.Settings; import java.util.Calendar; +import ai.elimu.analytics.entity.VideoLearningEvent; import ai.elimu.model.v2.enums.analytics.LearningEventType; import timber.log.Timber; @@ -15,24 +17,33 @@ public class VideoLearningEventReceiver extends BroadcastReceiver { public void onReceive(Context context, Intent intent) { Timber.i("onReceive"); - String packageName = intent.getStringExtra("packageName"); - Timber.i("packageName: \"" + packageName + "\""); - Calendar timestamp = Calendar.getInstance(); Timber.i("timestamp.getTime(): " + timestamp.getTime()); - Long videoId = intent.getLongExtra("videoId", 0); - Timber.i("videoId: " + videoId); + String androidId = Settings.Secure.getString(context.getContentResolver(), Settings.Secure.ANDROID_ID); + Timber.i("androidId: \"" + androidId + "\""); - String videoTitle = intent.getStringExtra("videoTitle"); - Timber.i("videoTitle: \"" + videoTitle + "\""); + String packageName = intent.getStringExtra("packageName"); + Timber.i("packageName: \"" + packageName + "\""); String learningEventTypeAsString = intent.getStringExtra("learningEventType"); Timber.i("learningEventTypeAsString: \"" + learningEventTypeAsString + "\""); LearningEventType learningEventType = LearningEventType.valueOf(learningEventTypeAsString); Timber.i("learningEventType: " + learningEventType); - // TODO: new VideoLearningEvent() + Long videoId = intent.getLongExtra("videoId", 0); + Timber.i("videoId: " + videoId); + + String videoTitle = intent.getStringExtra("videoTitle"); + Timber.i("videoTitle: \"" + videoTitle + "\""); + + VideoLearningEvent videoLearningEvent = new VideoLearningEvent(); + videoLearningEvent.setTime(timestamp); + videoLearningEvent.setAndroidId(androidId); + videoLearningEvent.setPackageName(packageName); + videoLearningEvent.setLearningEventType(learningEventType); + videoLearningEvent.setVideoId(videoId); + videoLearningEvent.setVideoTitle(videoTitle); // TODO: Store in database }