diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 000000000..522756497 --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1,4 @@ +# These are supported funding model platforms +github: [PranshulGG] +ko_fi: pranshulgg +liberapay: PranshulGG diff --git a/app/schemas/com.pranshulgg.weather_master_app.data.local.WeatherMasterDatabase/46.json b/app/schemas/com.pranshulgg.weather_master_app.data.local.WeatherMasterDatabase/46.json index 964bc26ed..40072548c 100644 --- a/app/schemas/com.pranshulgg.weather_master_app.data.local.WeatherMasterDatabase/46.json +++ b/app/schemas/com.pranshulgg.weather_master_app.data.local.WeatherMasterDatabase/46.json @@ -2,11 +2,11 @@ "formatVersion": 1, "database": { "version": 46, - "identityHash": "b2ca5c75db987a1d27628fc45eac72bf", + "identityHash": "fd66b7531bfe72f6bcac04b8fe26dc9e", "entities": [ { "tableName": "weather_locations", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `name` TEXT NOT NULL, `country` TEXT NOT NULL, `lat` REAL NOT NULL, `lon` REAL NOT NULL, `timezone` TEXT NOT NULL, `source` TEXT NOT NULL, `state` TEXT, `isFavorite` INTEGER NOT NULL, `isPinned` INTEGER NOT NULL, `countryCode` TEXT, `isDefault` INTEGER NOT NULL, `isDeviceLocation` INTEGER NOT NULL, PRIMARY KEY(`id`))", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `name` TEXT NOT NULL, `country` TEXT NOT NULL, `lat` REAL NOT NULL, `lon` REAL NOT NULL, `timezone` TEXT NOT NULL, `source` TEXT NOT NULL, `state` TEXT, `isFavorite` INTEGER NOT NULL, `isPinned` INTEGER NOT NULL, `countryCode` TEXT, `isDefault` INTEGER NOT NULL, `isDeviceLocation` INTEGER NOT NULL, `alertSource` TEXT NOT NULL, `airQualitySource` TEXT NOT NULL, PRIMARY KEY(`id`))", "fields": [ { "fieldPath": "id", @@ -83,6 +83,18 @@ "columnName": "isDeviceLocation", "affinity": "INTEGER", "notNull": true + }, + { + "fieldPath": "alertSource", + "columnName": "alertSource", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "airQualitySource", + "columnName": "airQualitySource", + "affinity": "TEXT", + "notNull": true } ], "primaryKey": { @@ -845,7 +857,7 @@ ], "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, 'b2ca5c75db987a1d27628fc45eac72bf')" + "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'fd66b7531bfe72f6bcac04b8fe26dc9e')" ] } } \ No newline at end of file diff --git a/app/schemas/com.pranshulgg.weather_master_app.data.local.WeatherMasterDatabase/47.json b/app/schemas/com.pranshulgg.weather_master_app.data.local.WeatherMasterDatabase/47.json new file mode 100644 index 000000000..3f35b4c8d --- /dev/null +++ b/app/schemas/com.pranshulgg.weather_master_app.data.local.WeatherMasterDatabase/47.json @@ -0,0 +1,863 @@ +{ + "formatVersion": 1, + "database": { + "version": 47, + "identityHash": "fd66b7531bfe72f6bcac04b8fe26dc9e", + "entities": [ + { + "tableName": "weather_locations", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `name` TEXT NOT NULL, `country` TEXT NOT NULL, `lat` REAL NOT NULL, `lon` REAL NOT NULL, `timezone` TEXT NOT NULL, `source` TEXT NOT NULL, `state` TEXT, `isFavorite` INTEGER NOT NULL, `isPinned` INTEGER NOT NULL, `countryCode` TEXT, `isDefault` INTEGER NOT NULL, `isDeviceLocation` INTEGER NOT NULL, `alertSource` TEXT NOT NULL, `airQualitySource` TEXT NOT NULL, PRIMARY KEY(`id`))", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "country", + "columnName": "country", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "lat", + "columnName": "lat", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "lon", + "columnName": "lon", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "timezone", + "columnName": "timezone", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "source", + "columnName": "source", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "state", + "columnName": "state", + "affinity": "TEXT" + }, + { + "fieldPath": "isFavorite", + "columnName": "isFavorite", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isPinned", + "columnName": "isPinned", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "countryCode", + "columnName": "countryCode", + "affinity": "TEXT" + }, + { + "fieldPath": "isDefault", + "columnName": "isDefault", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isDeviceLocation", + "columnName": "isDeviceLocation", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "alertSource", + "columnName": "alertSource", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "airQualitySource", + "columnName": "airQualitySource", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "id" + ] + } + }, + { + "tableName": "weather_current", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`locationId` TEXT NOT NULL, `temperature` REAL, `humidity` REAL NOT NULL, `windSpeed` REAL, `windDirection` TEXT, `pressureMsl` REAL, `visibility` INTEGER, `cloudCover` REAL, `uvIndex` REAL, `weatherCondition` TEXT NOT NULL, `feelsLike` REAL, `time` INTEGER NOT NULL, `dewPoint` REAL, `utcOffsetSeconds` INTEGER, `lastUpdatedInMilli` INTEGER NOT NULL, PRIMARY KEY(`locationId`), FOREIGN KEY(`locationId`) REFERENCES `weather_locations`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "locationId", + "columnName": "locationId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "temperature", + "columnName": "temperature", + "affinity": "REAL" + }, + { + "fieldPath": "humidity", + "columnName": "humidity", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "windSpeed", + "columnName": "windSpeed", + "affinity": "REAL" + }, + { + "fieldPath": "windDirection", + "columnName": "windDirection", + "affinity": "TEXT" + }, + { + "fieldPath": "pressureMsl", + "columnName": "pressureMsl", + "affinity": "REAL" + }, + { + "fieldPath": "visibility", + "columnName": "visibility", + "affinity": "INTEGER" + }, + { + "fieldPath": "cloudCover", + "columnName": "cloudCover", + "affinity": "REAL" + }, + { + "fieldPath": "uvIndex", + "columnName": "uvIndex", + "affinity": "REAL" + }, + { + "fieldPath": "weatherCondition", + "columnName": "weatherCondition", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "feelsLike", + "columnName": "feelsLike", + "affinity": "REAL" + }, + { + "fieldPath": "time", + "columnName": "time", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "dewPoint", + "columnName": "dewPoint", + "affinity": "REAL" + }, + { + "fieldPath": "utcOffsetSeconds", + "columnName": "utcOffsetSeconds", + "affinity": "INTEGER" + }, + { + "fieldPath": "lastUpdatedInMilli", + "columnName": "lastUpdatedInMilli", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "locationId" + ] + }, + "indices": [ + { + "name": "index_weather_current_locationId", + "unique": false, + "columnNames": [ + "locationId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_weather_current_locationId` ON `${TABLE_NAME}` (`locationId`)" + } + ], + "foreignKeys": [ + { + "table": "weather_locations", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "locationId" + ], + "referencedColumns": [ + "id" + ] + } + ] + }, + { + "tableName": "weather_hourly", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`locationId` TEXT NOT NULL, `temperature` REAL, `windSpeed` REAL, `windDirection` TEXT, `rain` REAL NOT NULL, `snowfall` REAL, `uvIndex` REAL, `weatherCondition` TEXT NOT NULL, `pressureMsl` REAL, `visibility` INTEGER, `humidity` REAL, `dewPoint` REAL, `time` INTEGER NOT NULL, `precipitationProbability` INTEGER, PRIMARY KEY(`locationId`, `time`), FOREIGN KEY(`locationId`) REFERENCES `weather_locations`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "locationId", + "columnName": "locationId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "temperature", + "columnName": "temperature", + "affinity": "REAL" + }, + { + "fieldPath": "windSpeed", + "columnName": "windSpeed", + "affinity": "REAL" + }, + { + "fieldPath": "windDirection", + "columnName": "windDirection", + "affinity": "TEXT" + }, + { + "fieldPath": "rain", + "columnName": "rain", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "snowfall", + "columnName": "snowfall", + "affinity": "REAL" + }, + { + "fieldPath": "uvIndex", + "columnName": "uvIndex", + "affinity": "REAL" + }, + { + "fieldPath": "weatherCondition", + "columnName": "weatherCondition", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "pressureMsl", + "columnName": "pressureMsl", + "affinity": "REAL" + }, + { + "fieldPath": "visibility", + "columnName": "visibility", + "affinity": "INTEGER" + }, + { + "fieldPath": "humidity", + "columnName": "humidity", + "affinity": "REAL" + }, + { + "fieldPath": "dewPoint", + "columnName": "dewPoint", + "affinity": "REAL" + }, + { + "fieldPath": "time", + "columnName": "time", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "precipitationProbability", + "columnName": "precipitationProbability", + "affinity": "INTEGER" + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "locationId", + "time" + ] + }, + "indices": [ + { + "name": "index_weather_hourly_locationId", + "unique": false, + "columnNames": [ + "locationId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_weather_hourly_locationId` ON `${TABLE_NAME}` (`locationId`)" + } + ], + "foreignKeys": [ + { + "table": "weather_locations", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "locationId" + ], + "referencedColumns": [ + "id" + ] + } + ] + }, + { + "tableName": "weather_daily", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`locationId` TEXT NOT NULL, `temperatureMin` REAL, `temperatureMax` REAL, `windSpeed` REAL, `windDirection` TEXT, `rainSum` REAL NOT NULL, `snowfallSum` REAL, `uvIndexMax` REAL, `weatherCondition` TEXT NOT NULL, `time` INTEGER NOT NULL, `precipitationProbabilityMax` INTEGER, `pressureMsl` REAL, `visibility` INTEGER, `humidity` REAL, `dewPoint` REAL, `sunrise` INTEGER NOT NULL, `sunset` INTEGER NOT NULL, `moonrise` INTEGER NOT NULL, `moonset` INTEGER NOT NULL, `moonPhase` TEXT NOT NULL, `dawn` INTEGER NOT NULL, `dusk` INTEGER NOT NULL, PRIMARY KEY(`locationId`, `time`), FOREIGN KEY(`locationId`) REFERENCES `weather_locations`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "locationId", + "columnName": "locationId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "temperatureMin", + "columnName": "temperatureMin", + "affinity": "REAL" + }, + { + "fieldPath": "temperatureMax", + "columnName": "temperatureMax", + "affinity": "REAL" + }, + { + "fieldPath": "windSpeed", + "columnName": "windSpeed", + "affinity": "REAL" + }, + { + "fieldPath": "windDirection", + "columnName": "windDirection", + "affinity": "TEXT" + }, + { + "fieldPath": "rainSum", + "columnName": "rainSum", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "snowfallSum", + "columnName": "snowfallSum", + "affinity": "REAL" + }, + { + "fieldPath": "uvIndexMax", + "columnName": "uvIndexMax", + "affinity": "REAL" + }, + { + "fieldPath": "weatherCondition", + "columnName": "weatherCondition", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "time", + "columnName": "time", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "precipitationProbabilityMax", + "columnName": "precipitationProbabilityMax", + "affinity": "INTEGER" + }, + { + "fieldPath": "pressureMsl", + "columnName": "pressureMsl", + "affinity": "REAL" + }, + { + "fieldPath": "visibility", + "columnName": "visibility", + "affinity": "INTEGER" + }, + { + "fieldPath": "humidity", + "columnName": "humidity", + "affinity": "REAL" + }, + { + "fieldPath": "dewPoint", + "columnName": "dewPoint", + "affinity": "REAL" + }, + { + "fieldPath": "sunrise", + "columnName": "sunrise", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "sunset", + "columnName": "sunset", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "moonrise", + "columnName": "moonrise", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "moonset", + "columnName": "moonset", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "moonPhase", + "columnName": "moonPhase", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "dawn", + "columnName": "dawn", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "dusk", + "columnName": "dusk", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "locationId", + "time" + ] + }, + "indices": [ + { + "name": "index_weather_daily_locationId", + "unique": false, + "columnNames": [ + "locationId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_weather_daily_locationId` ON `${TABLE_NAME}` (`locationId`)" + } + ], + "foreignKeys": [ + { + "table": "weather_locations", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "locationId" + ], + "referencedColumns": [ + "id" + ] + } + ] + }, + { + "tableName": "weather_units", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `tempUnit` TEXT NOT NULL, `windUnit` TEXT NOT NULL, `distanceUnit` TEXT NOT NULL, `pressureUnit` TEXT NOT NULL, `precipitationUnit` TEXT NOT NULL, PRIMARY KEY(`id`))", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "tempUnit", + "columnName": "tempUnit", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "windUnit", + "columnName": "windUnit", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "distanceUnit", + "columnName": "distanceUnit", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "pressureUnit", + "columnName": "pressureUnit", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "precipitationUnit", + "columnName": "precipitationUnit", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "id" + ] + } + }, + { + "tableName": "weather_blocks", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `isDaily` INTEGER NOT NULL, `type` TEXT NOT NULL, `isHidden` INTEGER NOT NULL, `position` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isDaily", + "columnName": "isDaily", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "isHidden", + "columnName": "isHidden", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "position", + "columnName": "position", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + } + }, + { + "tableName": "air_quality_current", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`locationId` TEXT NOT NULL, `usAqi` INTEGER, `pm10` REAL, `pm25` REAL, `carbonMonoxide` REAL, `nitrogenDioxide` REAL, `sulphurDioxide` REAL, `ozone` REAL, `lastUpdatedInMilli` INTEGER NOT NULL, PRIMARY KEY(`locationId`), FOREIGN KEY(`locationId`) REFERENCES `weather_locations`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "locationId", + "columnName": "locationId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "usAqi", + "columnName": "usAqi", + "affinity": "INTEGER" + }, + { + "fieldPath": "pm10", + "columnName": "pm10", + "affinity": "REAL" + }, + { + "fieldPath": "pm25", + "columnName": "pm25", + "affinity": "REAL" + }, + { + "fieldPath": "carbonMonoxide", + "columnName": "carbonMonoxide", + "affinity": "REAL" + }, + { + "fieldPath": "nitrogenDioxide", + "columnName": "nitrogenDioxide", + "affinity": "REAL" + }, + { + "fieldPath": "sulphurDioxide", + "columnName": "sulphurDioxide", + "affinity": "REAL" + }, + { + "fieldPath": "ozone", + "columnName": "ozone", + "affinity": "REAL" + }, + { + "fieldPath": "lastUpdatedInMilli", + "columnName": "lastUpdatedInMilli", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "locationId" + ] + }, + "indices": [ + { + "name": "index_air_quality_current_locationId", + "unique": false, + "columnNames": [ + "locationId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_air_quality_current_locationId` ON `${TABLE_NAME}` (`locationId`)" + } + ], + "foreignKeys": [ + { + "table": "weather_locations", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "locationId" + ], + "referencedColumns": [ + "id" + ] + } + ] + }, + { + "tableName": "nws_location_gridpoints", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`locationId` TEXT NOT NULL, `officeId` TEXT NOT NULL, `gridX` INTEGER NOT NULL, `gridY` INTEGER NOT NULL, `stationIdentifier` TEXT NOT NULL, `lastUpdatedMilli` INTEGER NOT NULL, PRIMARY KEY(`locationId`), FOREIGN KEY(`locationId`) REFERENCES `weather_locations`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "locationId", + "columnName": "locationId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "officeId", + "columnName": "officeId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "gridX", + "columnName": "gridX", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "gridY", + "columnName": "gridY", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "stationIdentifier", + "columnName": "stationIdentifier", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "lastUpdatedMilli", + "columnName": "lastUpdatedMilli", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "locationId" + ] + }, + "indices": [ + { + "name": "index_nws_location_gridpoints_locationId", + "unique": false, + "columnNames": [ + "locationId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_nws_location_gridpoints_locationId` ON `${TABLE_NAME}` (`locationId`)" + } + ], + "foreignKeys": [ + { + "table": "weather_locations", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "locationId" + ], + "referencedColumns": [ + "id" + ] + } + ] + }, + { + "tableName": "github", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `currentTag` TEXT NOT NULL, `lastFetchedTag` TEXT NOT NULL, `lastFetchedTime` INTEGER NOT NULL, PRIMARY KEY(`id`))", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "currentTag", + "columnName": "currentTag", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "lastFetchedTag", + "columnName": "lastFetchedTag", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "lastFetchedTime", + "columnName": "lastFetchedTime", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "id" + ] + } + }, + { + "tableName": "air_quality_hourly", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `locationId` TEXT NOT NULL, `pm10` REAL, `pm25` REAL, `carbonMonoxide` REAL, `nitrogenDioxide` REAL, `sulphurDioxide` REAL, `ozone` REAL, `time` INTEGER NOT NULL, FOREIGN KEY(`locationId`) REFERENCES `weather_locations`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "locationId", + "columnName": "locationId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "pm10", + "columnName": "pm10", + "affinity": "REAL" + }, + { + "fieldPath": "pm25", + "columnName": "pm25", + "affinity": "REAL" + }, + { + "fieldPath": "carbonMonoxide", + "columnName": "carbonMonoxide", + "affinity": "REAL" + }, + { + "fieldPath": "nitrogenDioxide", + "columnName": "nitrogenDioxide", + "affinity": "REAL" + }, + { + "fieldPath": "sulphurDioxide", + "columnName": "sulphurDioxide", + "affinity": "REAL" + }, + { + "fieldPath": "ozone", + "columnName": "ozone", + "affinity": "REAL" + }, + { + "fieldPath": "time", + "columnName": "time", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + }, + "indices": [ + { + "name": "index_air_quality_hourly_locationId", + "unique": false, + "columnNames": [ + "locationId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_air_quality_hourly_locationId` ON `${TABLE_NAME}` (`locationId`)" + } + ], + "foreignKeys": [ + { + "table": "weather_locations", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "locationId" + ], + "referencedColumns": [ + "id" + ] + } + ] + } + ], + "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, 'fd66b7531bfe72f6bcac04b8fe26dc9e')" + ] + } +} \ No newline at end of file diff --git a/app/schemas/com.pranshulgg.weather_master_app.data.local.WeatherMasterDatabase/48.json b/app/schemas/com.pranshulgg.weather_master_app.data.local.WeatherMasterDatabase/48.json new file mode 100644 index 000000000..df4e2a4d0 --- /dev/null +++ b/app/schemas/com.pranshulgg.weather_master_app.data.local.WeatherMasterDatabase/48.json @@ -0,0 +1,868 @@ +{ + "formatVersion": 1, + "database": { + "version": 48, + "identityHash": "5ca7c9b34ef5941f3d7515379d9646f9", + "entities": [ + { + "tableName": "weather_locations", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `name` TEXT NOT NULL, `country` TEXT NOT NULL, `lat` REAL NOT NULL, `lon` REAL NOT NULL, `timezone` TEXT NOT NULL, `source` TEXT NOT NULL, `state` TEXT, `isFavorite` INTEGER NOT NULL, `isPinned` INTEGER NOT NULL, `countryCode` TEXT, `isDefault` INTEGER NOT NULL, `isDeviceLocation` INTEGER NOT NULL, `alertSource` TEXT NOT NULL, `airQualitySource` TEXT NOT NULL, `customName` TEXT, PRIMARY KEY(`id`))", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "country", + "columnName": "country", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "lat", + "columnName": "lat", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "lon", + "columnName": "lon", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "timezone", + "columnName": "timezone", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "source", + "columnName": "source", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "state", + "columnName": "state", + "affinity": "TEXT" + }, + { + "fieldPath": "isFavorite", + "columnName": "isFavorite", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isPinned", + "columnName": "isPinned", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "countryCode", + "columnName": "countryCode", + "affinity": "TEXT" + }, + { + "fieldPath": "isDefault", + "columnName": "isDefault", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isDeviceLocation", + "columnName": "isDeviceLocation", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "alertSource", + "columnName": "alertSource", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "airQualitySource", + "columnName": "airQualitySource", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "customName", + "columnName": "customName", + "affinity": "TEXT" + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "id" + ] + } + }, + { + "tableName": "weather_current", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`locationId` TEXT NOT NULL, `temperature` REAL, `humidity` REAL NOT NULL, `windSpeed` REAL, `windDirection` TEXT, `pressureMsl` REAL, `visibility` INTEGER, `cloudCover` REAL, `uvIndex` REAL, `weatherCondition` TEXT NOT NULL, `feelsLike` REAL, `time` INTEGER NOT NULL, `dewPoint` REAL, `utcOffsetSeconds` INTEGER, `lastUpdatedInMilli` INTEGER NOT NULL, PRIMARY KEY(`locationId`), FOREIGN KEY(`locationId`) REFERENCES `weather_locations`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "locationId", + "columnName": "locationId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "temperature", + "columnName": "temperature", + "affinity": "REAL" + }, + { + "fieldPath": "humidity", + "columnName": "humidity", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "windSpeed", + "columnName": "windSpeed", + "affinity": "REAL" + }, + { + "fieldPath": "windDirection", + "columnName": "windDirection", + "affinity": "TEXT" + }, + { + "fieldPath": "pressureMsl", + "columnName": "pressureMsl", + "affinity": "REAL" + }, + { + "fieldPath": "visibility", + "columnName": "visibility", + "affinity": "INTEGER" + }, + { + "fieldPath": "cloudCover", + "columnName": "cloudCover", + "affinity": "REAL" + }, + { + "fieldPath": "uvIndex", + "columnName": "uvIndex", + "affinity": "REAL" + }, + { + "fieldPath": "weatherCondition", + "columnName": "weatherCondition", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "feelsLike", + "columnName": "feelsLike", + "affinity": "REAL" + }, + { + "fieldPath": "time", + "columnName": "time", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "dewPoint", + "columnName": "dewPoint", + "affinity": "REAL" + }, + { + "fieldPath": "utcOffsetSeconds", + "columnName": "utcOffsetSeconds", + "affinity": "INTEGER" + }, + { + "fieldPath": "lastUpdatedInMilli", + "columnName": "lastUpdatedInMilli", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "locationId" + ] + }, + "indices": [ + { + "name": "index_weather_current_locationId", + "unique": false, + "columnNames": [ + "locationId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_weather_current_locationId` ON `${TABLE_NAME}` (`locationId`)" + } + ], + "foreignKeys": [ + { + "table": "weather_locations", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "locationId" + ], + "referencedColumns": [ + "id" + ] + } + ] + }, + { + "tableName": "weather_hourly", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`locationId` TEXT NOT NULL, `temperature` REAL, `windSpeed` REAL, `windDirection` TEXT, `rain` REAL NOT NULL, `snowfall` REAL, `uvIndex` REAL, `weatherCondition` TEXT NOT NULL, `pressureMsl` REAL, `visibility` INTEGER, `humidity` REAL, `dewPoint` REAL, `time` INTEGER NOT NULL, `precipitationProbability` INTEGER, PRIMARY KEY(`locationId`, `time`), FOREIGN KEY(`locationId`) REFERENCES `weather_locations`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "locationId", + "columnName": "locationId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "temperature", + "columnName": "temperature", + "affinity": "REAL" + }, + { + "fieldPath": "windSpeed", + "columnName": "windSpeed", + "affinity": "REAL" + }, + { + "fieldPath": "windDirection", + "columnName": "windDirection", + "affinity": "TEXT" + }, + { + "fieldPath": "rain", + "columnName": "rain", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "snowfall", + "columnName": "snowfall", + "affinity": "REAL" + }, + { + "fieldPath": "uvIndex", + "columnName": "uvIndex", + "affinity": "REAL" + }, + { + "fieldPath": "weatherCondition", + "columnName": "weatherCondition", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "pressureMsl", + "columnName": "pressureMsl", + "affinity": "REAL" + }, + { + "fieldPath": "visibility", + "columnName": "visibility", + "affinity": "INTEGER" + }, + { + "fieldPath": "humidity", + "columnName": "humidity", + "affinity": "REAL" + }, + { + "fieldPath": "dewPoint", + "columnName": "dewPoint", + "affinity": "REAL" + }, + { + "fieldPath": "time", + "columnName": "time", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "precipitationProbability", + "columnName": "precipitationProbability", + "affinity": "INTEGER" + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "locationId", + "time" + ] + }, + "indices": [ + { + "name": "index_weather_hourly_locationId", + "unique": false, + "columnNames": [ + "locationId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_weather_hourly_locationId` ON `${TABLE_NAME}` (`locationId`)" + } + ], + "foreignKeys": [ + { + "table": "weather_locations", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "locationId" + ], + "referencedColumns": [ + "id" + ] + } + ] + }, + { + "tableName": "weather_daily", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`locationId` TEXT NOT NULL, `temperatureMin` REAL, `temperatureMax` REAL, `windSpeed` REAL, `windDirection` TEXT, `rainSum` REAL NOT NULL, `snowfallSum` REAL, `uvIndexMax` REAL, `weatherCondition` TEXT NOT NULL, `time` INTEGER NOT NULL, `precipitationProbabilityMax` INTEGER, `pressureMsl` REAL, `visibility` INTEGER, `humidity` REAL, `dewPoint` REAL, `sunrise` INTEGER NOT NULL, `sunset` INTEGER NOT NULL, `moonrise` INTEGER NOT NULL, `moonset` INTEGER NOT NULL, `moonPhase` TEXT NOT NULL, `dawn` INTEGER NOT NULL, `dusk` INTEGER NOT NULL, PRIMARY KEY(`locationId`, `time`), FOREIGN KEY(`locationId`) REFERENCES `weather_locations`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "locationId", + "columnName": "locationId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "temperatureMin", + "columnName": "temperatureMin", + "affinity": "REAL" + }, + { + "fieldPath": "temperatureMax", + "columnName": "temperatureMax", + "affinity": "REAL" + }, + { + "fieldPath": "windSpeed", + "columnName": "windSpeed", + "affinity": "REAL" + }, + { + "fieldPath": "windDirection", + "columnName": "windDirection", + "affinity": "TEXT" + }, + { + "fieldPath": "rainSum", + "columnName": "rainSum", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "snowfallSum", + "columnName": "snowfallSum", + "affinity": "REAL" + }, + { + "fieldPath": "uvIndexMax", + "columnName": "uvIndexMax", + "affinity": "REAL" + }, + { + "fieldPath": "weatherCondition", + "columnName": "weatherCondition", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "time", + "columnName": "time", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "precipitationProbabilityMax", + "columnName": "precipitationProbabilityMax", + "affinity": "INTEGER" + }, + { + "fieldPath": "pressureMsl", + "columnName": "pressureMsl", + "affinity": "REAL" + }, + { + "fieldPath": "visibility", + "columnName": "visibility", + "affinity": "INTEGER" + }, + { + "fieldPath": "humidity", + "columnName": "humidity", + "affinity": "REAL" + }, + { + "fieldPath": "dewPoint", + "columnName": "dewPoint", + "affinity": "REAL" + }, + { + "fieldPath": "sunrise", + "columnName": "sunrise", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "sunset", + "columnName": "sunset", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "moonrise", + "columnName": "moonrise", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "moonset", + "columnName": "moonset", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "moonPhase", + "columnName": "moonPhase", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "dawn", + "columnName": "dawn", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "dusk", + "columnName": "dusk", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "locationId", + "time" + ] + }, + "indices": [ + { + "name": "index_weather_daily_locationId", + "unique": false, + "columnNames": [ + "locationId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_weather_daily_locationId` ON `${TABLE_NAME}` (`locationId`)" + } + ], + "foreignKeys": [ + { + "table": "weather_locations", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "locationId" + ], + "referencedColumns": [ + "id" + ] + } + ] + }, + { + "tableName": "weather_units", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `tempUnit` TEXT NOT NULL, `windUnit` TEXT NOT NULL, `distanceUnit` TEXT NOT NULL, `pressureUnit` TEXT NOT NULL, `precipitationUnit` TEXT NOT NULL, PRIMARY KEY(`id`))", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "tempUnit", + "columnName": "tempUnit", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "windUnit", + "columnName": "windUnit", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "distanceUnit", + "columnName": "distanceUnit", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "pressureUnit", + "columnName": "pressureUnit", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "precipitationUnit", + "columnName": "precipitationUnit", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "id" + ] + } + }, + { + "tableName": "weather_blocks", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `isDaily` INTEGER NOT NULL, `type` TEXT NOT NULL, `isHidden` INTEGER NOT NULL, `position` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isDaily", + "columnName": "isDaily", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "isHidden", + "columnName": "isHidden", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "position", + "columnName": "position", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + } + }, + { + "tableName": "air_quality_current", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`locationId` TEXT NOT NULL, `usAqi` INTEGER, `pm10` REAL, `pm25` REAL, `carbonMonoxide` REAL, `nitrogenDioxide` REAL, `sulphurDioxide` REAL, `ozone` REAL, `lastUpdatedInMilli` INTEGER NOT NULL, PRIMARY KEY(`locationId`), FOREIGN KEY(`locationId`) REFERENCES `weather_locations`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "locationId", + "columnName": "locationId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "usAqi", + "columnName": "usAqi", + "affinity": "INTEGER" + }, + { + "fieldPath": "pm10", + "columnName": "pm10", + "affinity": "REAL" + }, + { + "fieldPath": "pm25", + "columnName": "pm25", + "affinity": "REAL" + }, + { + "fieldPath": "carbonMonoxide", + "columnName": "carbonMonoxide", + "affinity": "REAL" + }, + { + "fieldPath": "nitrogenDioxide", + "columnName": "nitrogenDioxide", + "affinity": "REAL" + }, + { + "fieldPath": "sulphurDioxide", + "columnName": "sulphurDioxide", + "affinity": "REAL" + }, + { + "fieldPath": "ozone", + "columnName": "ozone", + "affinity": "REAL" + }, + { + "fieldPath": "lastUpdatedInMilli", + "columnName": "lastUpdatedInMilli", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "locationId" + ] + }, + "indices": [ + { + "name": "index_air_quality_current_locationId", + "unique": false, + "columnNames": [ + "locationId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_air_quality_current_locationId` ON `${TABLE_NAME}` (`locationId`)" + } + ], + "foreignKeys": [ + { + "table": "weather_locations", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "locationId" + ], + "referencedColumns": [ + "id" + ] + } + ] + }, + { + "tableName": "nws_location_gridpoints", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`locationId` TEXT NOT NULL, `officeId` TEXT NOT NULL, `gridX` INTEGER NOT NULL, `gridY` INTEGER NOT NULL, `stationIdentifier` TEXT NOT NULL, `lastUpdatedMilli` INTEGER NOT NULL, PRIMARY KEY(`locationId`), FOREIGN KEY(`locationId`) REFERENCES `weather_locations`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "locationId", + "columnName": "locationId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "officeId", + "columnName": "officeId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "gridX", + "columnName": "gridX", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "gridY", + "columnName": "gridY", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "stationIdentifier", + "columnName": "stationIdentifier", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "lastUpdatedMilli", + "columnName": "lastUpdatedMilli", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "locationId" + ] + }, + "indices": [ + { + "name": "index_nws_location_gridpoints_locationId", + "unique": false, + "columnNames": [ + "locationId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_nws_location_gridpoints_locationId` ON `${TABLE_NAME}` (`locationId`)" + } + ], + "foreignKeys": [ + { + "table": "weather_locations", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "locationId" + ], + "referencedColumns": [ + "id" + ] + } + ] + }, + { + "tableName": "github", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `currentTag` TEXT NOT NULL, `lastFetchedTag` TEXT NOT NULL, `lastFetchedTime` INTEGER NOT NULL, PRIMARY KEY(`id`))", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "currentTag", + "columnName": "currentTag", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "lastFetchedTag", + "columnName": "lastFetchedTag", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "lastFetchedTime", + "columnName": "lastFetchedTime", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "id" + ] + } + }, + { + "tableName": "air_quality_hourly", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `locationId` TEXT NOT NULL, `pm10` REAL, `pm25` REAL, `carbonMonoxide` REAL, `nitrogenDioxide` REAL, `sulphurDioxide` REAL, `ozone` REAL, `time` INTEGER NOT NULL, FOREIGN KEY(`locationId`) REFERENCES `weather_locations`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "locationId", + "columnName": "locationId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "pm10", + "columnName": "pm10", + "affinity": "REAL" + }, + { + "fieldPath": "pm25", + "columnName": "pm25", + "affinity": "REAL" + }, + { + "fieldPath": "carbonMonoxide", + "columnName": "carbonMonoxide", + "affinity": "REAL" + }, + { + "fieldPath": "nitrogenDioxide", + "columnName": "nitrogenDioxide", + "affinity": "REAL" + }, + { + "fieldPath": "sulphurDioxide", + "columnName": "sulphurDioxide", + "affinity": "REAL" + }, + { + "fieldPath": "ozone", + "columnName": "ozone", + "affinity": "REAL" + }, + { + "fieldPath": "time", + "columnName": "time", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + }, + "indices": [ + { + "name": "index_air_quality_hourly_locationId", + "unique": false, + "columnNames": [ + "locationId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_air_quality_hourly_locationId` ON `${TABLE_NAME}` (`locationId`)" + } + ], + "foreignKeys": [ + { + "table": "weather_locations", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "locationId" + ], + "referencedColumns": [ + "id" + ] + } + ] + } + ], + "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, '5ca7c9b34ef5941f3d7515379d9646f9')" + ] + } +} \ No newline at end of file diff --git a/app/src/main/java/com/pranshulgg/weather_master_app/core/model/domain/location/Location.kt b/app/src/main/java/com/pranshulgg/weather_master_app/core/model/domain/location/Location.kt index b4da08abf..fc8470903 100644 --- a/app/src/main/java/com/pranshulgg/weather_master_app/core/model/domain/location/Location.kt +++ b/app/src/main/java/com/pranshulgg/weather_master_app/core/model/domain/location/Location.kt @@ -1,5 +1,7 @@ package com.pranshulgg.weather_master_app.core.model.domain.location +import com.pranshulgg.weather_master_app.core.model.sources.AirQualitySource +import com.pranshulgg.weather_master_app.core.model.sources.AlertSource import com.pranshulgg.weather_master_app.core.model.sources.WeatherSource data class Location( @@ -15,5 +17,8 @@ data class Location( val isFavorite: Boolean = false, val isPinned: Boolean = false, val isDefault: Boolean, - val isDeviceLocation: Boolean = false + val isDeviceLocation: Boolean = false, + val alertSource: AlertSource = AlertSource.NONE, + val airQualitySource: AirQualitySource = AirQualitySource.OPEN_METEO, + val customName: String? = null ) \ No newline at end of file diff --git a/app/src/main/java/com/pranshulgg/weather_master_app/core/model/sources/AirQualitySource.kt b/app/src/main/java/com/pranshulgg/weather_master_app/core/model/sources/AirQualitySource.kt new file mode 100644 index 000000000..32f4bac4b --- /dev/null +++ b/app/src/main/java/com/pranshulgg/weather_master_app/core/model/sources/AirQualitySource.kt @@ -0,0 +1,21 @@ +package com.pranshulgg.weather_master_app.core.model.sources + +enum class AirQualitySource( + val displayName: String, + val displayLink: String, + val fullName: String + +) { + NONE(displayName = "None", fullName = "", displayLink = ""), + ACCU_WEATHER( + displayName = "AccuWeather", + fullName = "AccuWeather", + displayLink = "https://www.accuweather.com/" + ), + OPEN_METEO( + displayName = "Open Meteo", + fullName = "Open Meteo", + displayLink = "https://open-meteo.com/" + ), + +} \ No newline at end of file diff --git a/app/src/main/java/com/pranshulgg/weather_master_app/core/model/sources/AlertSource.kt b/app/src/main/java/com/pranshulgg/weather_master_app/core/model/sources/AlertSource.kt new file mode 100644 index 000000000..53f7c6c0f --- /dev/null +++ b/app/src/main/java/com/pranshulgg/weather_master_app/core/model/sources/AlertSource.kt @@ -0,0 +1,22 @@ +package com.pranshulgg.weather_master_app.core.model.sources + + +enum class AlertSource( + val displayName: String, + val displayLink: String, + val fullName: String + +) { + NONE(displayName = "None", fullName = "", displayLink = ""), + ACCU_WEATHER( + displayName = "AccuWeather", + fullName = "AccuWeather", + displayLink = "https://www.accuweather.com/" + ), + WEATHER_API( + displayName = "WeatherApi", + fullName = "WeatherApi.com", + displayLink = "https://www.weatherapi.com/" + ), + +} \ No newline at end of file diff --git a/app/src/main/java/com/pranshulgg/weather_master_app/core/ui/components/LargeTopBarScaffold.kt b/app/src/main/java/com/pranshulgg/weather_master_app/core/ui/components/LargeTopBarScaffold.kt index 4504cc3ac..31a8a4511 100644 --- a/app/src/main/java/com/pranshulgg/weather_master_app/core/ui/components/LargeTopBarScaffold.kt +++ b/app/src/main/java/com/pranshulgg/weather_master_app/core/ui/components/LargeTopBarScaffold.kt @@ -4,6 +4,7 @@ import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.RowScope import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi +import androidx.compose.material3.FabPosition import androidx.compose.material3.LargeFlexibleTopAppBar import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Scaffold @@ -24,6 +25,7 @@ fun LargeTopBarScaffold( fab: @Composable () -> Unit = {}, bottomBar: @Composable (() -> Unit) = {}, defaultCollapsed: Boolean = false, + floatingActionButtonPosition: FabPosition = FabPosition.End, content: @Composable (PaddingValues) -> Unit, ) { @@ -43,6 +45,7 @@ fun LargeTopBarScaffold( modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection), containerColor = MaterialTheme.colorScheme.surfaceContainer, floatingActionButton = fab, + floatingActionButtonPosition = floatingActionButtonPosition, topBar = { LargeFlexibleTopAppBar( title = { Text(text = title) }, diff --git a/app/src/main/java/com/pranshulgg/weather_master_app/core/ui/components/SettingTile.kt b/app/src/main/java/com/pranshulgg/weather_master_app/core/ui/components/SettingTile.kt index e01ed87d7..710d09990 100644 --- a/app/src/main/java/com/pranshulgg/weather_master_app/core/ui/components/SettingTile.kt +++ b/app/src/main/java/com/pranshulgg/weather_master_app/core/ui/components/SettingTile.kt @@ -92,7 +92,9 @@ sealed class SettingTile { val onTextSubmitted: (String) -> Unit, val placeholder: String, val placeholderTextField: String, - val initialText: String = "" + val initialText: String = "", + val trailing: (@Composable (() -> Unit))? = null, + val placeholderAsValue: Boolean = false ) : SettingTile() data class DialogSliderTile( @@ -247,7 +249,9 @@ fun SettingSection( placeholder = tile.placeholder, placeholderTextField = tile.placeholderTextField, initialText = tile.initialText, - itemBgColor = itemBgColor + itemBgColor = itemBgColor, + trailing = tile.trailing, + placeholderAsValue = tile.placeholderAsValue ) is SettingTile.DialogSliderTile -> DialogSliderTile( diff --git a/app/src/main/java/com/pranshulgg/weather_master_app/core/ui/components/tiles/DialogTextFieldTile.kt b/app/src/main/java/com/pranshulgg/weather_master_app/core/ui/components/tiles/DialogTextFieldTile.kt index 456caf786..6c8e6de4a 100644 --- a/app/src/main/java/com/pranshulgg/weather_master_app/core/ui/components/tiles/DialogTextFieldTile.kt +++ b/app/src/main/java/com/pranshulgg/weather_master_app/core/ui/components/tiles/DialogTextFieldTile.kt @@ -20,7 +20,9 @@ fun DialogTextFieldTile( placeholder: String, placeholderTextField: String, shapes: RoundedCornerShape, - itemBgColor: Color + itemBgColor: Color, + trailing: (@Composable (() -> Unit))? = null, + placeholderAsValue: Boolean = false ) { var showDialog by remember { mutableStateOf(false) } var textFieldValue by remember { mutableStateOf(initialText) } @@ -37,17 +39,21 @@ fun DialogTextFieldTile( leadingContent = leading, headlineContent = { Text(headline) }, supportingContent = { - if (description != null) Text(description) + if (description != null) Text( + description, + color = if (placeholderAsValue) MaterialTheme.colorScheme.tertiary else Color.Unspecified + ) else if (textFieldValue.isNotEmpty()) Text( textFieldValue, color = MaterialTheme.colorScheme.tertiary ) else { Text( placeholder, - color = MaterialTheme.colorScheme.onSurfaceVariant + color = if (placeholderAsValue) MaterialTheme.colorScheme.tertiary else MaterialTheme.colorScheme.onSurfaceVariant ) } }, + trailingContent = trailing ) } diff --git a/app/src/main/java/com/pranshulgg/weather_master_app/core/ui/navigation/AppNavHost.kt b/app/src/main/java/com/pranshulgg/weather_master_app/core/ui/navigation/AppNavHost.kt index 1e468f61e..63abc8189 100644 --- a/app/src/main/java/com/pranshulgg/weather_master_app/core/ui/navigation/AppNavHost.kt +++ b/app/src/main/java/com/pranshulgg/weather_master_app/core/ui/navigation/AppNavHost.kt @@ -21,16 +21,17 @@ import androidx.navigation.compose.NavHost import androidx.navigation.compose.composable import androidx.navigation.compose.navigation import androidx.navigation.navArgument -import com.pranshulgg.weather_master_app.feature.blocks.screens.humidity.HumidityScreen -import com.pranshulgg.weather_master_app.feature.blocks.screens.pressure.PressureScreen import com.pranshulgg.weather_master_app.feature.blocks.screens.SunMoonScreen import com.pranshulgg.weather_master_app.feature.blocks.screens.airquality.AirQualityScreen +import com.pranshulgg.weather_master_app.feature.blocks.screens.humidity.HumidityScreen import com.pranshulgg.weather_master_app.feature.blocks.screens.precipitation.RainScreen import com.pranshulgg.weather_master_app.feature.blocks.screens.precipitation.SnowScreen +import com.pranshulgg.weather_master_app.feature.blocks.screens.pressure.PressureScreen import com.pranshulgg.weather_master_app.feature.blocks.screens.uvindex.UvIndexScreen import com.pranshulgg.weather_master_app.feature.blocks.screens.visibility.VisibilityScreen import com.pranshulgg.weather_master_app.feature.blocks.screens.wind.WindScreen import com.pranshulgg.weather_master_app.feature.daily.DailyScreen +import com.pranshulgg.weather_master_app.feature.editlocation.EditLocationScreen import com.pranshulgg.weather_master_app.feature.main.MainScreen import com.pranshulgg.weather_master_app.feature.search.SearchScreen import com.pranshulgg.weather_master_app.feature.settings.SettingsScreen @@ -66,12 +67,8 @@ fun AppNavHost( ) { navigation( route = "root", - startDestination = NavRoutes.MAIN -// startDestination = NavRoutes.blockScreen( -// NavRoutes.AIR_QUALITY, -// 0, -// "ab73f0ac-0dd9-410b-aa85-c944abfdcf60" -// ) +// startDestination = NavRoutes.MAIN + startDestination = NavRoutes.editLocation("af673c89-72b7-4da9-865d-c26fdabeca07") ) { composable( NavRoutes.MAIN @@ -156,6 +153,19 @@ fun AppNavHost( ) { WorkerInfoScreen(navController) } + composable( + route = "${NavRoutes.EDIT_LOCATION}/{locationId}", + arguments = listOf( + navArgument("locationId") { + type = NavType.StringType + } + ) + ) { backStackEntry -> + + val locationId = backStackEntry.arguments?.getString("locationId") + + EditLocationScreen(navController, locationId!!) + } composable( route = "{block}/{index}/{locationId}", arguments = listOf( diff --git a/app/src/main/java/com/pranshulgg/weather_master_app/core/ui/navigation/NavRoutes.kt b/app/src/main/java/com/pranshulgg/weather_master_app/core/ui/navigation/NavRoutes.kt index 8f5666f3b..f18a462d0 100644 --- a/app/src/main/java/com/pranshulgg/weather_master_app/core/ui/navigation/NavRoutes.kt +++ b/app/src/main/java/com/pranshulgg/weather_master_app/core/ui/navigation/NavRoutes.kt @@ -45,6 +45,12 @@ object NavRoutes { const val WORKER_INFO = "worker_info" + const val EDIT_LOCATION = "edit_location" + + fun editLocation(locationId: String): String { + return "$EDIT_LOCATION/$locationId" + } + fun blockScreen(block: String, index: Int, locationId: String): String { return "$block/$index/$locationId" } diff --git a/app/src/main/java/com/pranshulgg/weather_master_app/data/local/WeatherMasterDatabase.kt b/app/src/main/java/com/pranshulgg/weather_master_app/data/local/WeatherMasterDatabase.kt index 457b46e82..738c6dbea 100644 --- a/app/src/main/java/com/pranshulgg/weather_master_app/data/local/WeatherMasterDatabase.kt +++ b/app/src/main/java/com/pranshulgg/weather_master_app/data/local/WeatherMasterDatabase.kt @@ -38,12 +38,13 @@ import com.pranshulgg.weather_master_app.data.local.entity.weather.nws.NwsGridPo GithubEntity::class, HourlyAirQualityEntity::class ], - version = 46, + version = 48, autoMigrations = [ AutoMigration(from = 39, to = 40), AutoMigration(from = 42, to = 43), AutoMigration(from = 43, to = 44), - AutoMigration(from = 44, to = 45) + AutoMigration(from = 44, to = 45), + AutoMigration(from = 47, to = 48) ] ) abstract class WeatherMasterDatabase : RoomDatabase() { @@ -67,7 +68,8 @@ abstract class WeatherMasterDatabase : RoomDatabase() { context.applicationContext, WeatherMasterDatabase::class.java, "weather_master.db" - ).addMigrations(MIGRATION_40_41, MIGRATION_41_42, MIGRATION_45_46).build() + ).addMigrations(MIGRATION_40_41, MIGRATION_41_42, MIGRATION_45_46, MIGRATION_46_47) + .build() .also { INSTANCE = it } } } @@ -144,4 +146,11 @@ val MIGRATION_45_46 = object : Migration(45, 46) { "CREATE INDEX IF NOT EXISTS index_air_quality_hourly_locationId ON air_quality_hourly(locationId)" ) } +} + +val MIGRATION_46_47 = object : Migration(46, 47) { + override fun migrate(db: SupportSQLiteDatabase) { + db.execSQL("ALTER TABLE weather_locations ADD COLUMN alertSource TEXT NOT NULL DEFAULT 'NONE'") + db.execSQL("ALTER TABLE weather_locations ADD COLUMN airQualitySource TEXT NOT NULL DEFAULT 'OPEN_METEO'") + } } \ No newline at end of file diff --git a/app/src/main/java/com/pranshulgg/weather_master_app/data/local/dao/location/LocationsDao.kt b/app/src/main/java/com/pranshulgg/weather_master_app/data/local/dao/location/LocationsDao.kt index 071efa38b..87cbae39a 100644 --- a/app/src/main/java/com/pranshulgg/weather_master_app/data/local/dao/location/LocationsDao.kt +++ b/app/src/main/java/com/pranshulgg/weather_master_app/data/local/dao/location/LocationsDao.kt @@ -5,6 +5,7 @@ import androidx.room.Insert import androidx.room.OnConflictStrategy import androidx.room.Query import androidx.room.Transaction +import com.pranshulgg.weather_master_app.core.model.sources.AirQualitySource import com.pranshulgg.weather_master_app.core.model.sources.WeatherSource import com.pranshulgg.weather_master_app.data.local.entity.location.WeatherLocationEntity import com.pranshulgg.weather_master_app.data.local.entity.weather.WeatherWithRelations @@ -72,4 +73,10 @@ interface LocationsDao { @Query("SELECT * FROM weather_locations WHERE isDeviceLocation = 1 LIMIT 1") suspend fun getDeviceLocation(): WeatherLocationEntity + + @Query("SELECT * FROM weather_locations WHERE id = :id LIMIT 1") + suspend fun getLocationForId(id: String): WeatherLocationEntity + + @Query("UPDATE weather_locations SET airQualitySource = :source WHERE id = :id") + suspend fun updateAirQualitySourceForLocation(id: String, source: AirQualitySource) } \ No newline at end of file diff --git a/app/src/main/java/com/pranshulgg/weather_master_app/data/local/entity/location/WeatherLocationEntity.kt b/app/src/main/java/com/pranshulgg/weather_master_app/data/local/entity/location/WeatherLocationEntity.kt index 03f6142b3..673503474 100644 --- a/app/src/main/java/com/pranshulgg/weather_master_app/data/local/entity/location/WeatherLocationEntity.kt +++ b/app/src/main/java/com/pranshulgg/weather_master_app/data/local/entity/location/WeatherLocationEntity.kt @@ -2,6 +2,8 @@ package com.pranshulgg.weather_master_app.data.local.entity.location import androidx.room.Entity import androidx.room.PrimaryKey +import com.pranshulgg.weather_master_app.core.model.sources.AirQualitySource +import com.pranshulgg.weather_master_app.core.model.sources.AlertSource import com.pranshulgg.weather_master_app.core.model.sources.WeatherSource import kotlin.uuid.ExperimentalUuidApi @@ -22,6 +24,8 @@ data class WeatherLocationEntity( val isPinned: Boolean = false, val countryCode: String? = null, val isDefault: Boolean = false, - val isDeviceLocation: Boolean = false - + val isDeviceLocation: Boolean = false, + val alertSource: AlertSource, + val airQualitySource: AirQualitySource, + val customName: String? = null ) \ No newline at end of file diff --git a/app/src/main/java/com/pranshulgg/weather_master_app/data/local/mapper/locations/LocationsMapper.kt b/app/src/main/java/com/pranshulgg/weather_master_app/data/local/mapper/locations/LocationsMapper.kt index 8f6da8731..1f7a58d06 100644 --- a/app/src/main/java/com/pranshulgg/weather_master_app/data/local/mapper/locations/LocationsMapper.kt +++ b/app/src/main/java/com/pranshulgg/weather_master_app/data/local/mapper/locations/LocationsMapper.kt @@ -66,7 +66,10 @@ fun Location.toEntity(): WeatherLocationEntity = isPinned = false, isFavorite = false, isDefault = isDefault, - isDeviceLocation = isDeviceLocation + isDeviceLocation = isDeviceLocation, + alertSource = alertSource, + airQualitySource = airQualitySource, + customName = customName ) fun List.toDomain(): List = @@ -88,5 +91,8 @@ fun WeatherLocationEntity.toDomain(): Location = isFavorite = isFavorite, isPinned = isPinned, isDefault = isDefault, - isDeviceLocation = isDeviceLocation + isDeviceLocation = isDeviceLocation, + alertSource = alertSource, + airQualitySource = airQualitySource, + customName = customName ) diff --git a/app/src/main/java/com/pranshulgg/weather_master_app/data/repository/LocationsRepository.kt b/app/src/main/java/com/pranshulgg/weather_master_app/data/repository/LocationsRepository.kt index 5c0908ecf..f28542cd0 100644 --- a/app/src/main/java/com/pranshulgg/weather_master_app/data/repository/LocationsRepository.kt +++ b/app/src/main/java/com/pranshulgg/weather_master_app/data/repository/LocationsRepository.kt @@ -7,6 +7,7 @@ import com.pranshulgg.weather_master_app.core.model.domain.AppException import com.pranshulgg.weather_master_app.core.model.domain.airquality.AirQuality import com.pranshulgg.weather_master_app.core.model.domain.location.Location import com.pranshulgg.weather_master_app.core.model.domain.weather.Weather +import com.pranshulgg.weather_master_app.core.model.sources.AirQualitySource import com.pranshulgg.weather_master_app.core.model.sources.WeatherSource import com.pranshulgg.weather_master_app.core.network.sources.address.nominatim.json.NominatimRepository import com.pranshulgg.weather_master_app.data.local.dao.airquality.AirQualityDao @@ -74,6 +75,14 @@ class LocationsRepository @Inject constructor( dao.updateSourceForLocation(id, source) } + suspend fun updateAirQualitySourceForLocation(id: String, source: AirQualitySource) { + dao.updateAirQualitySourceForLocation(id, source) + } + + suspend fun getLocationForId(id: String): Location { + return dao.getLocationForId(id).toDomain() + } + @Transaction suspend fun saveLocation(location: Location) { diff --git a/app/src/main/java/com/pranshulgg/weather_master_app/feature/editlocation/EditLocationScreen.kt b/app/src/main/java/com/pranshulgg/weather_master_app/feature/editlocation/EditLocationScreen.kt new file mode 100644 index 000000000..ec0708989 --- /dev/null +++ b/app/src/main/java/com/pranshulgg/weather_master_app/feature/editlocation/EditLocationScreen.kt @@ -0,0 +1,256 @@ +package com.pranshulgg.weather_master_app.feature.editlocation + +import android.util.Log +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.WindowInsets +import androidx.compose.foundation.layout.asPaddingValues +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.heightIn +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.systemBars +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.verticalScroll +import androidx.compose.material3.Button +import androidx.compose.material3.ButtonDefaults +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi +import androidx.compose.material3.FabPosition +import androidx.compose.material3.IconButton +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.SheetValue +import androidx.compose.material3.Text +import androidx.compose.material3.rememberBottomSheetState +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.unit.dp +import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel +import androidx.navigation.NavController +import com.pranshulgg.weather_master_app.R +import com.pranshulgg.weather_master_app.core.model.domain.location.Location +import com.pranshulgg.weather_master_app.core.model.sources.AirQualitySource +import com.pranshulgg.weather_master_app.core.model.sources.AlertSource +import com.pranshulgg.weather_master_app.core.model.sources.WeatherSource +import com.pranshulgg.weather_master_app.core.model.sources.getWeatherSourcesForCountry +import com.pranshulgg.weather_master_app.core.model.sources.getWeatherSourcesGlobal +import com.pranshulgg.weather_master_app.core.ui.components.Gap +import com.pranshulgg.weather_master_app.core.ui.components.LargeTopBarScaffold +import com.pranshulgg.weather_master_app.core.ui.components.NavigateUpBtn +import com.pranshulgg.weather_master_app.core.ui.components.SettingSection +import com.pranshulgg.weather_master_app.core.ui.components.SettingTile +import com.pranshulgg.weather_master_app.core.ui.components.Symbol +import com.pranshulgg.weather_master_app.feature.editlocation.ui.EditLocationBottomSheet +import com.pranshulgg.weather_master_app.feature.shared.ui.SharedBottomSheet + + +data class EditLocationScreenUiState( + val location: Location? = null, + val isWeatherSourcesForLocationSheetOpen: Boolean = false, + val selectedWeatherSource: WeatherSource? = null, + val selectedAlertSource: AlertSource? = null, + val selectedAirQualitySource: AirQualitySource? = null, + val isAlertSourcesSheetOpen: Boolean = false, + val isAirQualitySourcesSheetOpen: Boolean = false, + val isEditLocationNameSheetOpen: Boolean = false +) + +@OptIn(ExperimentalMaterial3ExpressiveApi::class, ExperimentalMaterial3Api::class) +@Composable +fun EditLocationScreen( + navController: NavController, id: String +) { + + val viewModel: EditLocationViewModel = hiltViewModel() + val uiState = viewModel.uiState.value + + LaunchedEffect(Unit) { + viewModel.getLocationForId(id) + } + + if (uiState.location == null) return + + val btnSize = ButtonDefaults.MediumContainerHeight + + val sheetState = rememberBottomSheetState( + initialValue = SheetValue.Hidden, + enabledValues = setOf(SheetValue.Expanded, SheetValue.Hidden) + ) + + val colorDesc = MaterialTheme.colorScheme.tertiary + + + val locationText = buildString { + append(uiState.location.name) + if (uiState.location.country.isNotBlank()) { + append(", ") + } + if (uiState.location.state.isNotBlank()) { + append(uiState.location.state) + append(", ") + } + append(uiState.location.country) + } + + val locationName = if (!uiState.location.customName.isNullOrBlank()) uiState.location.customName + else locationText + + var currentLocationName by remember { mutableStateOf(locationName) } + + + LargeTopBarScaffold( + title = stringResource(R.string.location_edit), + navigationIcon = { NavigateUpBtn(navController) }, + floatingActionButtonPosition = FabPosition.Center, + ) { paddingValues -> + Column( + modifier = + Modifier + .fillMaxSize() + .verticalScroll(rememberScrollState()) + .padding(top = paddingValues.calculateTopPadding()) + ) { + SettingSection( + tiles = listOf( + SettingTile.ActionTile( + title = "Location name", + description = currentLocationName, + onClick = { + viewModel.showEditLocationNameSheet() + }, + trailing = { + IconButton(onClick = { currentLocationName = locationName }) { + Symbol(R.drawable.refresh_24px) + } + }, + colorDesc = colorDesc + ) + ) + ) + + + Gap(10.dp) + SettingSection( + tiles = listOf( + SettingTile.ActionTile( + title = "Weather source", + description = uiState.selectedWeatherSource?.displayName + ?: uiState.location.source.displayName, + colorDesc = colorDesc, + onClick = { + viewModel.showWeatherSourcesForLocationSheet() + } + ), + SettingTile.ActionTile( + title = "Alert source", + description = uiState.selectedAlertSource?.displayName + ?: uiState.location.alertSource.displayName, + colorDesc = colorDesc, + onClick = { + viewModel.showAlertSourcesSheet() + } + ), + SettingTile.ActionTile( + title = "Air quality source", + description = uiState.selectedAirQualitySource?.displayName + ?: uiState.location.airQualitySource.displayName, + colorDesc = colorDesc, + onClick = { + viewModel.showAirQualitySourcesSheet() + } + ) + ) + ) + Text( + "Latitude: ${uiState.location.latitude}, Longitude: ${uiState.location.longitude}", + color = MaterialTheme.colorScheme.onSurfaceVariant, + style = MaterialTheme.typography.bodyMedium, + modifier = Modifier.padding(top = 10.dp, start = 16.dp, end = 16.dp) + ) + Gap(26.dp) + Button( + modifier = Modifier + .heightIn(btnSize) + .fillMaxWidth() + .padding(horizontal = 16.dp), + onClick = { /* Handle click */ }, + shapes = ButtonDefaults.shapes(), + contentPadding = ButtonDefaults.contentPaddingFor(btnSize), + ) { + Symbol( + R.drawable.check_24px, + color = MaterialTheme.colorScheme.onPrimaryContainer, + size = ButtonDefaults.iconSizeFor(btnSize) + ) + Gap(horizontal = ButtonDefaults.iconSpacingFor(btnSize)) + Text( + stringResource(R.string.action_save_changes), + style = ButtonDefaults.textStyleFor(btnSize), + color = MaterialTheme.colorScheme.onPrimaryContainer + ) + } + + // WEATHER SOURCES SHEET + SharedBottomSheet.WeatherSourcesForLocationSheet( + countryCode = uiState.location.countryCode, + show = uiState.isWeatherSourcesForLocationSheetOpen, + isEditing = true, + selectedSource = uiState.selectedWeatherSource ?: uiState.location.source, + onSave = { + viewModel.updateSelectedWeatherSource(it) + }, + onDismiss = viewModel::hideWeatherSourcesForLocationSheet, + sheetState = sheetState + ) + + + // ALERT SOURCES SHEET + EditLocationBottomSheet.AlertSourcesSheet( + show = uiState.isAlertSourcesSheetOpen, + sheetState = sheetState, + selectedSource = uiState.selectedAlertSource ?: uiState.location.alertSource, + onSave = { + viewModel.updateSelectedAlertSource(it) + }, + onDismiss = viewModel::hideAlertSourcesSheet + ) + + // AIR QUALITY SOURCES SHEET + EditLocationBottomSheet.AirQualitySourcesSheet( + show = uiState.isAirQualitySourcesSheetOpen, + sheetState = sheetState, + selectedSource = uiState.selectedAirQualitySource + ?: uiState.location.airQualitySource, + onSave = { + viewModel.updateSelectedAirQualitySource(it) + }, + onDismiss = viewModel::hideAirQualitySourcesSheet + ) + + // EDIT LOCATION NAME SHEET + EditLocationBottomSheet.EditLocationNameSheet( + show = uiState.isEditLocationNameSheetOpen, + sheetState = sheetState, + onDismiss = { + viewModel.hideEditLocationNameSheet() + }, + onSave = { + if (it.isNotBlank()) { + currentLocationName = it + } + }, + value = currentLocationName, + ) + + Gap(WindowInsets.systemBars.asPaddingValues().calculateBottomPadding()) + } + } +} + + diff --git a/app/src/main/java/com/pranshulgg/weather_master_app/feature/editlocation/EditLocationViewModel.kt b/app/src/main/java/com/pranshulgg/weather_master_app/feature/editlocation/EditLocationViewModel.kt new file mode 100644 index 000000000..1d743d48b --- /dev/null +++ b/app/src/main/java/com/pranshulgg/weather_master_app/feature/editlocation/EditLocationViewModel.kt @@ -0,0 +1,77 @@ +package com.pranshulgg.weather_master_app.feature.editlocation + +import androidx.compose.runtime.State +import androidx.compose.runtime.mutableStateOf +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import com.pranshulgg.weather_master_app.R +import com.pranshulgg.weather_master_app.core.model.sources.AirQualitySource +import com.pranshulgg.weather_master_app.core.model.sources.AlertSource +import com.pranshulgg.weather_master_app.core.model.sources.WeatherSource +import com.pranshulgg.weather_master_app.core.ui.snackbar.SnackbarManager +import com.pranshulgg.weather_master_app.data.repository.LocationsRepository +import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.coroutines.launch +import javax.inject.Inject + + +@HiltViewModel +class EditLocationViewModel @Inject constructor( + private val locationsRepo: LocationsRepository, +) : ViewModel() { + + private var _uiState = mutableStateOf(EditLocationScreenUiState()) + val uiState: State = _uiState + + fun getLocationForId(id: String) { + viewModelScope.launch { + val location = locationsRepo.getLocationForId(id) + _uiState.value = _uiState.value.copy(location = location) + } + } + + + fun showWeatherSourcesForLocationSheet() { + _uiState.value = _uiState.value.copy(isWeatherSourcesForLocationSheetOpen = true) + } + + fun hideWeatherSourcesForLocationSheet() { + _uiState.value = _uiState.value.copy(isWeatherSourcesForLocationSheetOpen = false) + } + + fun updateSelectedWeatherSource(source: WeatherSource) { + _uiState.value = _uiState.value.copy(selectedWeatherSource = source) + } + + fun updateSelectedAlertSource(source: AlertSource) { + _uiState.value = _uiState.value.copy(selectedAlertSource = source) + } + + fun updateSelectedAirQualitySource(source: AirQualitySource) { + _uiState.value = _uiState.value.copy(selectedAirQualitySource = source) + } + + fun showAlertSourcesSheet() { + _uiState.value = _uiState.value.copy(isAlertSourcesSheetOpen = true) + } + + fun hideAlertSourcesSheet() { + _uiState.value = _uiState.value.copy(isAlertSourcesSheetOpen = false) + } + + fun showAirQualitySourcesSheet() { + _uiState.value = _uiState.value.copy(isAirQualitySourcesSheetOpen = true) + } + + fun hideAirQualitySourcesSheet() { + _uiState.value = _uiState.value.copy(isAirQualitySourcesSheetOpen = false) + } + + fun showEditLocationNameSheet() { + _uiState.value = _uiState.value.copy(isEditLocationNameSheetOpen = true) + } + + fun hideEditLocationNameSheet() { + _uiState.value = _uiState.value.copy(isEditLocationNameSheetOpen = false) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/pranshulgg/weather_master_app/feature/editlocation/ui/EditLocationBottomSheet.kt b/app/src/main/java/com/pranshulgg/weather_master_app/feature/editlocation/ui/EditLocationBottomSheet.kt new file mode 100644 index 000000000..97e70c93d --- /dev/null +++ b/app/src/main/java/com/pranshulgg/weather_master_app/feature/editlocation/ui/EditLocationBottomSheet.kt @@ -0,0 +1,167 @@ +package com.pranshulgg.weather_master_app.feature.editlocation.ui + +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.OutlinedTextField +import androidx.compose.material3.SheetState +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.unit.dp +import com.pranshulgg.weather_master_app.R +import com.pranshulgg.weather_master_app.core.model.sources.AirQualitySource +import com.pranshulgg.weather_master_app.core.model.sources.AlertSource +import com.pranshulgg.weather_master_app.core.model.sources.WeatherSource +import com.pranshulgg.weather_master_app.core.model.sources.getWeatherSourcesForCountry +import com.pranshulgg.weather_master_app.core.model.sources.getWeatherSourcesGlobal +import com.pranshulgg.weather_master_app.core.ui.components.ActionBottomSheet +import com.pranshulgg.weather_master_app.core.ui.components.Gap +import com.pranshulgg.weather_master_app.core.ui.components.SettingSection +import com.pranshulgg.weather_master_app.core.ui.components.SettingTile +import com.pranshulgg.weather_master_app.core.ui.components.Symbol + + +object EditLocationBottomSheet { + + @OptIn(ExperimentalMaterial3Api::class) + @Composable + fun EditLocationNameSheet( + show: Boolean, + sheetState: SheetState, + onDismiss: () -> Unit, + onSave: (String) -> Unit, + value: String + ) { + if (show) { + + var currentText by remember { mutableStateOf(value) } + + ActionBottomSheet( + sheetState = sheetState, + onCancel = { onDismiss() }, + onConfirm = { onSave(currentText) }, + confirmText = stringResource(R.string.action_save), + cancelText = stringResource(R.string.action_cancel) + ) { + OutlinedTextField( + value = currentText, + onValueChange = { currentText = it }, + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = 16.dp), + placeholder = { Text("Name") }, + ) + } + } + + } + + @OptIn(ExperimentalMaterial3Api::class) + @Composable + fun AlertSourcesSheet( + show: Boolean, + sheetState: SheetState, + selectedSource: AlertSource = AlertSource.NONE, + onSave: (AlertSource) -> Unit, + onDismiss: () -> Unit + ) { + if (show) { + + var currentSelectedSource by remember( + show, + selectedSource + ) { + mutableStateOf(selectedSource) + } + + ActionBottomSheet( + sheetState = sheetState, + onCancel = { onDismiss() }, + onConfirm = { onSave(currentSelectedSource) }, + confirmText = stringResource(R.string.action_save), + cancelText = stringResource(R.string.action_cancel) + ) { + SettingSection( + title = stringResource(R.string.global_sources), + tiles = AlertSource.entries.map { source -> + val isSelected = currentSelectedSource == source + + SettingTile.ActionTile( + leading = { + if (isSelected) Symbol( + R.drawable.check_24px, + color = MaterialTheme.colorScheme.onSecondaryContainer + ) + }, + title = source.displayName, + selected = isSelected, + onClick = { + currentSelectedSource = source + } + ) + + } + ) + } + } + } + + + @OptIn(ExperimentalMaterial3Api::class) + @Composable + fun AirQualitySourcesSheet( + show: Boolean, + sheetState: SheetState, + selectedSource: AirQualitySource = AirQualitySource.OPEN_METEO, + onSave: (AirQualitySource) -> Unit, + onDismiss: () -> Unit + ) { + if (show) { + + var currentSelectedSource by remember( + show, + selectedSource + ) { + mutableStateOf(selectedSource) + } + + ActionBottomSheet( + sheetState = sheetState, + onCancel = { onDismiss() }, + onConfirm = { onSave(currentSelectedSource) }, + confirmText = stringResource(R.string.action_save), + cancelText = stringResource(R.string.action_cancel) + ) { + SettingSection( + title = stringResource(R.string.global_sources), + tiles = AirQualitySource.entries.map { source -> + val isSelected = currentSelectedSource == source + + SettingTile.ActionTile( + leading = { + if (isSelected) Symbol( + R.drawable.check_24px, + color = MaterialTheme.colorScheme.onSecondaryContainer + ) + }, + title = source.displayName, + selected = isSelected, + onClick = { + currentSelectedSource = source + } + ) + + } + ) + } + } + } +} + diff --git a/app/src/main/java/com/pranshulgg/weather_master_app/feature/locations/LocationsScreenContent.kt b/app/src/main/java/com/pranshulgg/weather_master_app/feature/locations/LocationsScreenContent.kt index 50f96b91d..55e697297 100644 --- a/app/src/main/java/com/pranshulgg/weather_master_app/feature/locations/LocationsScreenContent.kt +++ b/app/src/main/java/com/pranshulgg/weather_master_app/feature/locations/LocationsScreenContent.kt @@ -173,13 +173,13 @@ private fun UseDeviceLocationCard(onClick: () -> Unit, isLoading: Boolean = fals }, headlineContent = { Text( - "Use current location", + stringResource(R.string.location_use_current), color = MaterialTheme.colorScheme.onSurface ) }, supportingContent = { Text( - "Detect your device’s current location automatically and add it to your list", + stringResource(R.string.location_use_current_secondary), color = MaterialTheme.colorScheme.onSurfaceVariant ) }, diff --git a/app/src/main/java/com/pranshulgg/weather_master_app/feature/shared/WeatherViewModel.kt b/app/src/main/java/com/pranshulgg/weather_master_app/feature/shared/WeatherViewModel.kt index 7c4389557..16617ada6 100644 --- a/app/src/main/java/com/pranshulgg/weather_master_app/feature/shared/WeatherViewModel.kt +++ b/app/src/main/java/com/pranshulgg/weather_master_app/feature/shared/WeatherViewModel.kt @@ -10,6 +10,7 @@ import com.pranshulgg.weather_master_app.core.model.domain.location.Location import com.pranshulgg.weather_master_app.core.model.domain.toAppException import com.pranshulgg.weather_master_app.core.model.domain.toMessageRes import com.pranshulgg.weather_master_app.core.model.domain.weather.WeatherBlock +import com.pranshulgg.weather_master_app.core.model.sources.AirQualitySource import com.pranshulgg.weather_master_app.core.model.sources.WeatherSource import com.pranshulgg.weather_master_app.core.model.sources.isGlobal import com.pranshulgg.weather_master_app.core.model.sources.isSourceSupportedFor @@ -106,7 +107,8 @@ class WeatherViewModel @Inject constructor( location: Location, source: WeatherSource, isManualRefresh: Boolean = false, - isForceRefresh: Boolean = false + isForceRefresh: Boolean = false, + isForceRefreshForAirQuality: Boolean = false ) { setLoading(true) weatherJob?.cancel() @@ -190,6 +192,40 @@ class WeatherViewModel @Inject constructor( } + private fun handleSourceChangeForWeather( + location: Location, + source: WeatherSource, + airQualitySource: AirQualitySource, + ) { + val updatedLocation = location.copy(source = source, airQualitySource = airQualitySource) + + viewModelScope.launch { + + locationsRepo.updateSourceForLocation(location.id, source) + locationsRepo.updateAirQualitySourceForLocation(location.id, airQualitySource) + + val allowForceRefreshForWeather = location.source != source + val allowForceRefreshForAirQuality = location.airQualitySource != airQualitySource + + + if (allowForceRefreshForWeather) { + weatherDataReconcilerRepository.cleanUpStaleData(location.source, location.id) + } + _uiState.value = _uiState.value.copy( + activeLocation = updatedLocation + ) + getWeather( + updatedLocation, + source, + isForceRefresh = allowForceRefreshForWeather, + isForceRefreshForAirQuality = allowForceRefreshForAirQuality + ) + + + } + } + + fun saveBlocks( items: List, isDaily: Boolean = false diff --git a/app/src/main/res/values-fi-rFI/strings.xml b/app/src/main/res/values-fi-rFI/strings.xml index b46d525f1..95628439b 100644 --- a/app/src/main/res/values-fi-rFI/strings.xml +++ b/app/src/main/res/values-fi-rFI/strings.xml @@ -35,7 +35,7 @@ %d tuntia sitten - % päivä sitten + %d päivä sitten %d päivää sitten Juuri nyt diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 9c6e321fe..d472f9672 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -360,6 +360,27 @@ Conditions will turn %1$s at night. Gadget bridge is not installed + + Worker information + Send data to Gadgetbridge + Particulate matter less than 2.5 microns + Particulate matter less than 10 microns + Carbon monoxide + Nitrogen Dioxide + Sulfur Dioxide + Ground surface + Last successful run + Never + Run attempt + Tags + Next schedule + + Save changes + Alert source + Edit location + Air quality source + Location name + Location information