Compare commits

...

9 Commits

Author SHA1 Message Date
77864cbef4 Suppress unused parameter warnings. 2021-09-27 00:02:50 +08:00
32c580bac7 Merge branch 'patch-1' into patch-develop 2021-09-26 22:29:09 +08:00
85fa0cae11 Add weather providers HERE.com and AccuWeather.com; fix Weather.gov. 2021-09-26 22:28:06 +08:00
05f2a745c2 Merge branch 'patch-1' into patch-develop
# Conflicts:
#	app/src/main/java/com/tommasoberlose/anotherwidget/helpers/WeatherHelper.kt
2021-09-25 11:34:02 +08:00
ef2e89b6ff Correct weather info for weatherbit.io, weather.gov and yr.no. 2021-09-25 11:31:53 +08:00
a306d92282 Make text shadows more visible. 2021-09-25 11:08:10 +08:00
da9fc362af Merge branch 'patch-3' into patch-develop 2021-09-24 10:45:07 +08:00
ff83cfd953 Update translation. 2021-09-24 10:44:05 +08:00
5d9dcd9701 Replace Realm with Room. 2021-09-23 00:23:42 +08:00
26 changed files with 657 additions and 389 deletions

View File

@ -7,8 +7,6 @@ apply plugin: 'com.google.firebase.crashlytics'
apply plugin: 'kotlin-android' apply plugin: 'kotlin-android'
apply plugin: 'kotlin-kapt' apply plugin: 'kotlin-kapt'
apply plugin: 'realm-android'
def apikeyPropertiesFile = rootProject.file("apikey.properties") def apikeyPropertiesFile = rootProject.file("apikey.properties")
def apikeyProperties = new Properties() def apikeyProperties = new Properties()
apikeyProperties.load(new FileInputStream(apikeyPropertiesFile)) apikeyProperties.load(new FileInputStream(apikeyPropertiesFile))
@ -88,6 +86,10 @@ dependencies {
// EventBus // EventBus
implementation 'org.greenrobot:eventbus:3.2.0' implementation 'org.greenrobot:eventbus:3.2.0'
// Room
implementation "androidx.room:room-runtime:2.3.0"
kapt "androidx.room:room-compiler:2.3.0"
// Navigation // Navigation
implementation 'androidx.navigation:navigation-fragment-ktx:2.3.5' implementation 'androidx.navigation:navigation-fragment-ktx:2.3.5'
implementation 'androidx.navigation:navigation-ui-ktx:2.3.5' implementation 'androidx.navigation:navigation-ui-ktx:2.3.5'

View File

@ -7,10 +7,6 @@ import androidx.appcompat.app.AppCompatDelegate
import com.chibatching.kotpref.Kotpref import com.chibatching.kotpref.Kotpref
import com.google.firebase.crashlytics.FirebaseCrashlytics import com.google.firebase.crashlytics.FirebaseCrashlytics
import com.tommasoberlose.anotherwidget.global.Preferences import com.tommasoberlose.anotherwidget.global.Preferences
import com.tommasoberlose.anotherwidget.utils.checkGrantedPermission
import io.realm.Realm
import io.realm.RealmConfiguration
import net.danlew.android.joda.JodaTimeAndroid
class AWApplication : Application() { class AWApplication : Application() {
override fun onCreate() { override fun onCreate() {
@ -24,12 +20,5 @@ class AWApplication : Application() {
// Dark theme // Dark theme
AppCompatDelegate.setDefaultNightMode(Preferences.darkThemePreference) AppCompatDelegate.setDefaultNightMode(Preferences.darkThemePreference)
// Realm
Realm.init(this)
val config = RealmConfiguration.Builder()
.deleteRealmIfMigrationNeeded()
.build()
Realm.setDefaultConfiguration(config)
} }
} }

View File

@ -18,8 +18,8 @@ class FixedFocusScrollView @JvmOverloads constructor(
var isScrollable = true var isScrollable = true
override fun scrollTo(x: Int, y: Int) { override fun scrollTo(x: Int, y: Int) {
if (isScrollable) { if (isScrollable || !isLaidOut) {
super.scrollTo(x, y) super.scrollTo(x, y)
} }
} }
} }

View File

@ -3,32 +3,37 @@ package com.tommasoberlose.anotherwidget.db
import android.content.Context import android.content.Context
import android.provider.CalendarContract import android.provider.CalendarContract
import android.util.Log import android.util.Log
import androidx.room.Dao
import androidx.room.Database
import androidx.room.Insert
import androidx.room.Query
import androidx.room.Room
import androidx.room.RoomDatabase
import com.chibatching.kotpref.bulk import com.chibatching.kotpref.bulk
import com.tommasoberlose.anotherwidget.global.Preferences import com.tommasoberlose.anotherwidget.global.Preferences
import com.tommasoberlose.anotherwidget.helpers.CalendarHelper.applyFilters import com.tommasoberlose.anotherwidget.helpers.CalendarHelper.applyFilters
import com.tommasoberlose.anotherwidget.helpers.CalendarHelper.sortEvents
import com.tommasoberlose.anotherwidget.models.Event import com.tommasoberlose.anotherwidget.models.Event
import com.tommasoberlose.anotherwidget.receivers.UpdatesReceiver import com.tommasoberlose.anotherwidget.receivers.UpdatesReceiver
import com.tommasoberlose.anotherwidget.ui.widgets.MainWidget import com.tommasoberlose.anotherwidget.ui.widgets.MainWidget
import io.realm.Realm
import io.realm.RealmResults
import java.util.* import java.util.*
import kotlin.Comparator import kotlin.Comparator
import kotlin.collections.ArrayList import kotlin.collections.ArrayList
class EventRepository(val context: Context) { class EventRepository(val context: Context) {
private val realm by lazy { Realm.getDefaultInstance() } private val db by lazy { EventDatabase.getDatabase(context) }
fun saveEvents(eventList: List<Event>) { fun saveEvents(eventList: List<Event>) {
realm.executeTransaction { realm -> db.runInTransaction{
realm.where(Event::class.java).findAll().deleteAllFromRealm() db.dao().run {
realm.copyToRealm(eventList) deleteAll()
insertAll(eventList)
}
} }
} }
fun clearEvents() { fun clearEvents() {
realm.executeTransaction { realm -> db.dao().deleteAll()
realm.where(Event::class.java).findAll().deleteAllFromRealm()
}
} }
fun resetNextEventData() { fun resetNextEventData() {
@ -44,11 +49,11 @@ class EventRepository(val context: Context) {
} }
fun saveNextEventData(event: Event) { fun saveNextEventData(event: Event) {
Preferences.nextEventId = event.eventID Preferences.nextEventId = event.id
} }
fun getNextEvent(): Event? { fun getNextEvent(): Event? {
val nextEvent = getEventByEventId(Preferences.nextEventId) val nextEvent = getEventById(Preferences.nextEventId)
val now = Calendar.getInstance().timeInMillis val now = Calendar.getInstance().timeInMillis
val limit = Calendar.getInstance().apply { val limit = Calendar.getInstance().apply {
timeInMillis = now timeInMillis = now
@ -64,43 +69,33 @@ class EventRepository(val context: Context) {
else -> add(Calendar.HOUR, 6) else -> add(Calendar.HOUR, 6)
} }
} }
val event = if (nextEvent != null && nextEvent.endDate > now && nextEvent.startDate <= limit.timeInMillis) { return if (nextEvent != null && nextEvent.endDate > now && nextEvent.startDate <= limit.timeInMillis) {
nextEvent nextEvent
} else { } else {
val events = getEvents() val events = getEvents()
if (events.isNotEmpty()) { if (events.isNotEmpty()) {
val newNextEvent = events.first() val newNextEvent = events.first()
Preferences.nextEventId = newNextEvent.eventID Preferences.nextEventId = newNextEvent.id
newNextEvent newNextEvent
} else { } else {
resetNextEventData() resetNextEventData()
null null
} }
} }
return try {
realm.copyFromRealm(event!!)
} catch (ex: Exception) {
event
}
} }
fun getEventByEventId(id: Long): Event? { fun getEventById(id: Long): Event? {
val event = realm.where(Event::class.java).equalTo("eventID", id).findFirst() return db.dao().findById(id)
return try {
realm.copyFromRealm(event!!)
} catch (ex: Exception) {
event
}
} }
fun goToNextEvent() { fun goToNextEvent() {
val eventList = getEvents() val eventList = getEvents()
if (eventList.isNotEmpty()) { if (eventList.isNotEmpty()) {
val index = eventList.indexOfFirst { it.eventID == Preferences.nextEventId } val index = eventList.indexOfFirst { it.id == Preferences.nextEventId }
if (index > -1 && index < eventList.size - 1) { if (index > -1 && index < eventList.size - 1) {
Preferences.nextEventId = eventList[index + 1].eventID Preferences.nextEventId = eventList[index + 1].id
} else { } else {
Preferences.nextEventId = eventList.first().eventID Preferences.nextEventId = eventList.first().id
} }
} else { } else {
resetNextEventData() resetNextEventData()
@ -114,11 +109,11 @@ class EventRepository(val context: Context) {
fun goToPreviousEvent() { fun goToPreviousEvent() {
val eventList = getEvents() val eventList = getEvents()
if (eventList.isNotEmpty()) { if (eventList.isNotEmpty()) {
val index = eventList.indexOfFirst { it.eventID == Preferences.nextEventId } val index = eventList.indexOfFirst { it.id == Preferences.nextEventId }
if (index > 0) { if (index > 0) {
Preferences.nextEventId = eventList[index - 1].eventID Preferences.nextEventId = eventList[index - 1].id
} else { } else {
Preferences.nextEventId = eventList.last().eventID Preferences.nextEventId = eventList.last().id
} }
} else { } else {
resetNextEventData() resetNextEventData()
@ -130,13 +125,7 @@ class EventRepository(val context: Context) {
} }
fun getFutureEvents(): List<Event> { fun getFutureEvents(): List<Event> {
val now = Calendar.getInstance().timeInMillis return db.dao().findFuture(Calendar.getInstance().timeInMillis).applyFilters().sortEvents()
realm.refresh()
return realm
.where(Event::class.java)
.greaterThan("endDate", now)
.findAll()
.applyFilters()
} }
private fun getEvents(): List<Event> { private fun getEvents(): List<Event> {
@ -155,18 +144,54 @@ class EventRepository(val context: Context) {
else -> add(Calendar.HOUR, 6) else -> add(Calendar.HOUR, 6)
} }
} }
realm.refresh() return db.dao().find(now, limit.timeInMillis).applyFilters().sortEvents()
return realm
.where(Event::class.java)
.greaterThan("endDate", now)
.lessThanOrEqualTo("startDate", limit.timeInMillis)
.findAll()
.applyFilters()
} }
fun getEventsCount(): Int = getEvents().size fun getEventsCount(): Int = getEvents().size
fun close() { fun close() {
realm.close() // db.close()
} }
}
@Dao
interface EventDao {
@Query("SELECT * FROM events WHERE id = :id LIMIT 1")
fun findById(id: Long) : Event?
@Query("SELECT * FROM events WHERE end_date > :from")
fun findFuture(from: Long) : List<Event>
@Query("SELECT * FROM events WHERE end_date > :from and start_date <= :to")
fun find(from: Long, to: Long) : List<Event>
@Insert
fun insertAll(events: List<Event>)
@Query("DELETE FROM events")
fun deleteAll()
}
@Database(entities = arrayOf(Event::class), version = 1, exportSchema = false)
abstract class EventDatabase : RoomDatabase() {
abstract fun dao(): EventDao
companion object {
private var INSTANCE: EventDatabase? = null
fun getDatabase(context: Context): EventDatabase {
// if the INSTANCE is not null, then return it,
// if it is, then create the database
return INSTANCE ?: synchronized(this) {
val instance = Room.databaseBuilder(
context.applicationContext,
EventDatabase::class.java,
"events"
).allowMainThreadQueries().build()
INSTANCE = instance
// return instance
instance
}
}
}
}
}

View File

@ -97,7 +97,7 @@ object Preferences : KotprefModel() {
var textSecondSize by floatPref(key = "PREF_TEXT_SECOND_SIZE", default = 16f) var textSecondSize by floatPref(key = "PREF_TEXT_SECOND_SIZE", default = 16f)
var clockTextSize by floatPref(key = "PREF_TEXT_CLOCK_SIZE", default = 72f) var clockTextSize by floatPref(key = "PREF_TEXT_CLOCK_SIZE", default = 72f)
var clockBottomMargin by intPref(default = Constants.ClockBottomMargin.MEDIUM.rawValue) var clockBottomMargin by intPref(default = Constants.ClockBottomMargin.MEDIUM.rawValue)
var secondRowTopMargin by intPref(default = Constants.SecondRowTopMargin.NONE.rawValue) var secondRowTopMargin by intPref(default = Constants.SecondRowTopMargin.SMALL.rawValue)
var showClock by booleanPref(key = "PREF_SHOW_CLOCK", default = false) var showClock by booleanPref(key = "PREF_SHOW_CLOCK", default = false)
var clockAppName by stringPref(key = "PREF_CLOCK_APP_NAME", default = "") var clockAppName by stringPref(key = "PREF_CLOCK_APP_NAME", default = "")
var clockAppPackage by stringPref(key = "PREF_CLOCK_APP_PACKAGE", default = "") var clockAppPackage by stringPref(key = "PREF_CLOCK_APP_PACKAGE", default = "")

View File

@ -17,7 +17,7 @@ object ImageHelper {
0 -> 0f * factor 0 -> 0f * factor
1 -> 8f * factor 1 -> 8f * factor
2 -> 16f * factor 2 -> 16f * factor
else -> 0f * factor else -> 8f * factor
}, resources.displayMetrics) }, resources.displayMetrics)
if (originalView.drawable != null && originalView.drawable.intrinsicWidth > 0 && originalView.drawable.intrinsicHeight > 0) { if (originalView.drawable != null && originalView.drawable.intrinsicWidth > 0 && originalView.drawable.intrinsicHeight > 0) {
@ -58,7 +58,7 @@ object ImageHelper {
0 -> 0f * factor 0 -> 0f * factor
1 -> 0.8f * factor 1 -> 0.8f * factor
2 -> 1f * factor 2 -> 1f * factor
else -> 0f else -> 0.8f * factor
})) }))
colorMatrixScript.setColorMatrix(matrix) colorMatrixScript.setColorMatrix(matrix)

View File

@ -339,92 +339,92 @@ object WeatherHelper {
} }
} }
fun getWeatherGovIcon(iconString: String, isDaytime: Boolean): String = when { fun getWeatherGovIcon(iconString: String, isDaytime: Boolean): String = when (iconString.substringBefore('?').substringAfterLast('/')) {
iconString.contains("skc") -> "01" "skc" -> "01"
iconString.contains("few") -> "02" "few" -> "02"
iconString.contains("sct") -> "03" "sct" -> "02"
iconString.contains("bkn") -> "04" "bkn" -> "03"
iconString.contains("ovc") -> "04" "ovc" -> "04"
iconString.contains("wind_skc") -> "01" "wind_skc" -> "01"
iconString.contains("wind_few") -> "02" "wind_few" -> "02"
iconString.contains("wind_sct") -> "03" "wind_sct" -> "02"
iconString.contains("wind_bkn") -> "04" "wind_bkn" -> "03"
iconString.contains("wind_ovc") -> "04" "wind_ovc" -> "04"
iconString.contains("snow") -> "13" "snow" -> "13"
iconString.contains("rain_snow") -> "81" "rain_snow" -> "81"
iconString.contains("rain_sleet") -> "81" "rain_sleet" -> "81"
iconString.contains("snow_sleet") -> "81" "snow_sleet" -> "81"
iconString.contains("fzra") -> "81" "fzra" -> "81"
iconString.contains("rain_fzra") -> "81" "rain_fzra" -> "81"
iconString.contains("snow_fzra") -> "81" "snow_fzra" -> "81"
iconString.contains("sleet") -> "81" "sleet" -> "81"
iconString.contains("rain") -> "10" "rain" -> "10"
iconString.contains("rain_showers") -> "10" "rain_showers" -> "10"
iconString.contains("rain_showers_hi") -> "10" "rain_showers_hi" -> "10"
iconString.contains("tsra") -> "82" "tsra" -> "09"
iconString.contains("tsra_sct") -> "82" "tsra_sct" -> "11"
iconString.contains("tsra_hi") -> "82" "tsra_hi" -> "11"
iconString.contains("tornado") -> "80" "tornado" -> "80"
iconString.contains("hurricane") -> "80" "hurricane" -> "80"
iconString.contains("tropical_storm") -> "09" "tropical_storm" -> "09"
iconString.contains("dust") -> "Dust" "dust" -> "50"
iconString.contains("smoke") -> "Smoke" "smoke" -> "50"
iconString.contains("haze") -> "50" "haze" -> "50"
iconString.contains("hot") -> "01" "hot" -> "01"
iconString.contains("cold") -> "13" "cold" -> "13"
iconString.contains("blizzard") -> "80" "blizzard" -> "13"
iconString.contains("fog") -> "82" "fog" -> "82"
else -> "" else -> ""
} + if (isDaytime) "d" else "n" } + if (isDaytime) "d" else "n"
fun getWeatherBitIcon(iconString: String): String = when { fun getWeatherBitIcon(iconString: String): String = when (iconString.substring(0, 3)) {
iconString.contains("t01") -> "11" "t01" -> "11"
iconString.contains("t02") -> "09" "t02" -> "11"
iconString.contains("t03") -> "09" "t03" -> "09"
iconString.contains("t04") -> "09" "t04" -> "11"
iconString.contains("t05") -> "09" "t05" -> "11"
iconString.contains("d01") -> "10" "d01" -> "10"
iconString.contains("d02") -> "10" "d02" -> "10"
iconString.contains("d03") -> "10" "d03" -> "10"
iconString.contains("r01") -> "10" "r01" -> "10"
iconString.contains("r02") -> "10" "r02" -> "10"
iconString.contains("r03") -> "10" "r03" -> "10"
iconString.contains("f01") -> "10" "f01" -> "10"
iconString.contains("r04") -> "10" "r04" -> "10"
iconString.contains("r05") -> "10" "r05" -> "10"
iconString.contains("r06") -> "10" "r06" -> "10"
iconString.contains("s01") -> "13" "s01" -> "13"
iconString.contains("s02") -> "13" "s02" -> "13"
iconString.contains("s03") -> "13" "s03" -> "13"
iconString.contains("s04") -> "81" "s04" -> "81"
iconString.contains("s05") -> "90" "s05" -> "81"
iconString.contains("s06") -> "13" "s06" -> "13"
iconString.contains("a01") -> "82" "a01" -> "50"
iconString.contains("a02") -> "82" "a02" -> "50"
iconString.contains("a03") -> "82" "a03" -> "50"
iconString.contains("a04") -> "82" "a04" -> "50"
iconString.contains("a05") -> "82" "a05" -> "82"
iconString.contains("a06") -> "82" "a06" -> "82"
iconString.contains("c01") -> "01" "c01" -> "01"
iconString.contains("c02") -> "02" "c02" -> "02"
iconString.contains("c03") -> "04" "c03" -> "03"
iconString.contains("c04") -> "04" "c04" -> "04"
else -> "" else -> ""
} + if (iconString.contains("d")) "d" else "n" } + iconString.substring(3)
fun getWeatherApiIcon(icon: Int, isDaytime: Boolean): String = when(icon) { fun getWeatherApiIcon(icon: Int, isDaytime: Boolean): String = when(icon) {
1000 -> "01" 1000 -> "01"
1003 -> "02" 1003 -> "02"
1006 -> "03" 1006 -> "03"
1009 -> "04" 1009 -> "04"
1030 -> "82" 1030 -> "50"
1063 -> "10" 1063 -> "10"
1066 -> "10" 1066 -> "13"
1069 -> "10" 1069 -> "81"
1072 -> "81" 1072 -> "81"
1087 -> "11" 1087 -> "11"
1114 -> "13" 1114 -> "13"
1117 -> "09" 1117 -> "13"
1135 -> "82" 1135 -> "82"
1147 -> "82" 1147 -> "82"
1150 -> "10" 1150 -> "10"
@ -439,8 +439,8 @@ object WeatherHelper {
1195 -> "10" 1195 -> "10"
1198 -> "81" 1198 -> "81"
1201 -> "81" 1201 -> "81"
1204 -> "13" 1204 -> "81"
1207 -> "13" 1207 -> "81"
1210 -> "13" 1210 -> "13"
1213 -> "13" 1213 -> "13"
1216 -> "13" 1216 -> "13"
@ -451,62 +451,62 @@ object WeatherHelper {
1240 -> "10" 1240 -> "10"
1243 -> "10" 1243 -> "10"
1246 -> "10" 1246 -> "10"
1249 -> "13" 1249 -> "81"
1252 -> "13" 1252 -> "81"
1255 -> "13" 1255 -> "13"
1258 -> "13" 1258 -> "13"
1261 -> "13" 1261 -> "13"
1264 -> "13" 1264 -> "13"
1273 -> "09" 1273 -> "11"
1276 -> "09" 1276 -> "09"
1279 -> "13" 1279 -> "13"
1282 -> "13" 1282 -> "13"
else -> "" else -> ""
} + if (isDaytime) "d" else "n" } + if (isDaytime) "d" else "n"
fun getYRIcon(iconCode: String, isDaytime: Boolean): String = when { fun getYRIcon(iconCode: String): String = when (iconCode.substringBefore('_')) {
iconCode.contains("clearsky") -> "01" "clearsky" -> "01"
iconCode.contains("cloudy") -> "04" "cloudy" -> "04"
iconCode.contains("fair") -> "02" "fair" -> "02"
iconCode.contains("fog") -> "82" "fog" -> "82"
iconCode.contains("heavyrain") -> "10" "heavyrain" -> "10"
iconCode.contains("heavyrainandthunder") -> "11" "heavyrainandthunder" -> "09"
iconCode.contains("heavyrainshowers") -> "10" "heavyrainshowers" -> "10"
iconCode.contains("heavyrainshowersandthunder") -> "11" "heavyrainshowersandthunder" -> "09"
iconCode.contains("heavysleet") -> "10" "heavysleet" -> "81"
iconCode.contains("heavysleetandthunder") -> "11" "heavysleetandthunder" -> "81"
iconCode.contains("heavysleetshowers") -> "10" "heavysleetshowers" -> "81"
iconCode.contains("heavysleetshowersandthunder") -> "11" "heavysleetshowersandthunder" -> "81"
iconCode.contains("heavysnow") -> "13" "heavysnow" -> "13"
iconCode.contains("heavysnowandthunder") -> "13" "heavysnowandthunder" -> "13"
iconCode.contains("heavysnowshowers") -> "13" "heavysnowshowers" -> "13"
iconCode.contains("heavysnowshowersandthunder") -> "13" "heavysnowshowersandthunder" -> "13"
iconCode.contains("lightrain") -> "10" "lightrain" -> "10"
iconCode.contains("lightrainandthunder") -> "11" "lightrainandthunder" -> "11"
iconCode.contains("lightrainshowers") -> "10" "lightrainshowers" -> "10"
iconCode.contains("lightrainshowersandthunder") -> "11" "lightrainshowersandthunder" -> "11"
iconCode.contains("lightsleet") -> "10" "lightsleet" -> "81"
iconCode.contains("lightsleetandthunder") -> "11" "lightsleetandthunder" -> "81"
iconCode.contains("lightsleetshowers") -> "10" "lightsleetshowers" -> "81"
iconCode.contains("lightsnow") -> "13" "lightsnow" -> "13"
iconCode.contains("lightsnowandthunder") -> "13" "lightsnowandthunder" -> "13"
iconCode.contains("lightsnowshowers") -> "13" "lightsnowshowers" -> "13"
iconCode.contains("lightssleetshowersandthunder") -> "81" "lightssleetshowersandthunder" -> "81"
iconCode.contains("lightssnowshowersandthunder") -> "81" "lightssnowshowersandthunder" -> "81"
iconCode.contains("partlycloudy") -> "03" "partlycloudy" -> "03"
iconCode.contains("rain") -> "10" "rain" -> "10"
iconCode.contains("rainandthunder") -> "11" "rainandthunder" -> "11"
iconCode.contains("rainshowers") -> "10" "rainshowers" -> "10"
iconCode.contains("rainshowersandthunder") -> "11" "rainshowersandthunder" -> "11"
iconCode.contains("sleet") -> "10" "sleet" -> "81"
iconCode.contains("sleetandthunder") -> "11" "sleetandthunder" -> "81"
iconCode.contains("sleetshowers") -> "10" "sleetshowers" -> "81"
iconCode.contains("sleetshowersandthunder") -> "11" "sleetshowersandthunder" -> "81"
iconCode.contains("snow") -> "13" "snow" -> "13"
iconCode.contains("snowandthunder") -> "13" "snowandthunder" -> "13"
iconCode.contains("snowshowers") -> "13" "snowshowers" -> "13"
iconCode.contains("snowshowersandthunder") -> "13" "snowshowersandthunder" -> "13"
else -> "" else -> ""
} + if (isDaytime) "d" else "n" } + if (iconCode.substringAfter('_', "day") == "day") "d" else "n"
} }

View File

@ -1,26 +1,35 @@
package com.tommasoberlose.anotherwidget.models package com.tommasoberlose.anotherwidget.models
import android.provider.CalendarContract import android.provider.CalendarContract
import io.realm.RealmObject import androidx.room.ColumnInfo
import java.util.Date import androidx.room.Entity
import androidx.room.PrimaryKey
/** /**
* Created by tommaso on 05/10/17. * Created by tommaso on 05/10/17.
*/ */
open class Event( @Entity(tableName = "events")
var id: Long = 0, data class Event(
var eventID: Long = 0, @PrimaryKey
var title: String = "", val id: Long = 0,
var startDate: Long = 0, @ColumnInfo(name = "event_id")
var endDate: Long = 0, val eventID: Long = 0,
var calendarID: Int = 0, val title: String = "",
var allDay: Boolean = false, @ColumnInfo(name = "start_date")
var address: String = "", val startDate: Long = 0,
var selfAttendeeStatus: Int = CalendarContract.Attendees.ATTENDEE_STATUS_NONE, @ColumnInfo(name = "end_date")
var availability: Int = CalendarContract.EventsEntity.AVAILABILITY_BUSY val endDate: Long = 0,
) : RealmObject() { @ColumnInfo(name = "calendar_id")
val calendarID: Long = 0,
@ColumnInfo(name = "all_day")
val allDay: Boolean = false,
val address: String = "",
@ColumnInfo(name = "self_attendee_status")
val selfAttendeeStatus: Int = CalendarContract.Attendees.ATTENDEE_STATUS_NONE,
val availability: Int = CalendarContract.EventsEntity.AVAILABILITY_BUSY
)/* {
override fun toString(): String { override fun toString(): String {
return "Event:\nEVENT ID: " + eventID + "\nTITLE: " + title + "\nSTART DATE: " + Date(startDate) + "\nEND DATE: " + Date(endDate) + "\nCAL ID: " + calendarID + "\nADDRESS: " + address return "Event:\nEVENT ID: " + eventID + "\nTITLE: " + title + "\nSTART DATE: " + Date(startDate) + "\nEND DATE: " + Date(endDate) + "\nCAL ID: " + calendarID + "\nADDRESS: " + address
} }
} }*/

View File

@ -26,9 +26,7 @@ class TimeZonesApi(val context: Context) {
when (val response = repository.getTimeZone(lat, long)) { when (val response = repository.getTimeZone(lat, long)) {
is NetworkResponse.Success -> { is NetworkResponse.Success -> {
try { try {
Log.d("ciao", response.body.toString())
id = response.body["timezoneId"] as String id = response.body["timezoneId"] as String
} catch(ex: Exception) { } catch(ex: Exception) {
ex.printStackTrace() ex.printStackTrace()
} }
@ -37,4 +35,4 @@ class TimeZonesApi(val context: Context) {
return id return id
} }
} }

View File

@ -121,25 +121,34 @@ class WeatherNetworkApi(val context: Context) {
val props = val props =
weatherResponse.body["properties"] as LinkedTreeMap<*, *> weatherResponse.body["properties"] as LinkedTreeMap<*, *>
val periods = props["periods"] as List<*> val periods = props["periods"] as List<*>
val now = periods[0] as LinkedTreeMap<*, *> @android.annotation.SuppressLint("SimpleDateFormat")
val format = SimpleDateFormat(
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.N)
"yyyy-MM-dd'T'HH:mm:ssXXX"
else
"yyyy-MM-dd'T'HH:mm:ssZ"
)
for (period in periods) {
val now = period as LinkedTreeMap<*, *>
val endTime = format.parse(now["endTime"] as String)!!
if (endTime.time > System.currentTimeMillis()) {
val temp = now["temperature"] as Double
val fullIcon = now["icon"] as String
val isDaytime = now["isDaytime"] as Boolean
val temp = now["temperature"] as Double Preferences.weatherTemp = temp.toFloat()
val fullIcon = now["icon"] as String Preferences.weatherIcon = WeatherHelper.getWeatherGovIcon(fullIcon, isDaytime)
val isDaytime = now["isDaytime"] as Boolean Preferences.weatherRealTempUnit = Preferences.weatherTempUnit
MainWidget.updateWidget(context)
Preferences.weatherTemp = temp.toFloat() Preferences.weatherProviderError = ""
Preferences.weatherIcon = WeatherHelper.getWeatherGovIcon(fullIcon, isDaytime) Preferences.weatherProviderLocationError = ""
Preferences.weatherRealTempUnit = Preferences.weatherTempUnit break
}
Preferences.weatherProviderError = "" }
Preferences.weatherProviderLocationError = ""
MainWidget.updateWidget(context)
} catch (ex: Exception) { } catch (ex: Exception) {
Preferences.weatherProviderError = context.getString(R.string.weather_provider_error_generic) Preferences.weatherProviderError = context.getString(R.string.weather_provider_error_generic)
Preferences.weatherProviderLocationError = "" Preferences.weatherProviderLocationError = ""
} finally {
EventBus.getDefault().post(MainFragment.UpdateUiMessageEvent())
} }
} }
else -> { else -> {
@ -155,14 +164,16 @@ class WeatherNetworkApi(val context: Context) {
} }
} }
is NetworkResponse.ServerError -> { is NetworkResponse.ServerError -> {
if (pointsResponse.body?.containsKey("status") == true && (pointsResponse.body?.get("status") as Double).toInt() == 404) { when (pointsResponse.code) {
Preferences.weatherProviderError = "" 404 -> {
Preferences.weatherProviderLocationError = context.getString(R.string.weather_provider_error_wrong_location) Preferences.weatherProviderError = ""
} else { Preferences.weatherProviderLocationError = context.getString(R.string.weather_provider_error_wrong_location)
Preferences.weatherProviderError = context.getString(R.string.weather_provider_error_generic) }
Preferences.weatherProviderLocationError = "" else -> {
Preferences.weatherProviderError = context.getString(R.string.weather_provider_error_generic)
Preferences.weatherProviderLocationError = ""
}
} }
WeatherHelper.removeWeather( WeatherHelper.removeWeather(
context context
) )
@ -183,7 +194,18 @@ class WeatherNetworkApi(val context: Context) {
when (val response = repository.getWeather()) { when (val response = repository.getWeather()) {
is NetworkResponse.Success -> { is NetworkResponse.Success -> {
try { try {
Log.d("ciao - here", response.body.toString()) val observations = response.body["observations"] as LinkedTreeMap<*, *>
val location = (observations["location"] as List<*>).first() as LinkedTreeMap<*, *>
val observation = (location["observation"] as List<*>).first() as LinkedTreeMap<*, *>
val iconName = observation["iconName"] as String
val daylight = observation["daylight"] as String
val temperature = observation["temperature"] as String
Preferences.weatherTemp = temperature.toFloat()
Preferences.weatherIcon = repository.getWeatherIcon(iconName, daylight != "N")
Preferences.weatherRealTempUnit = Preferences.weatherTempUnit
MainWidget.updateWidget(context)
Preferences.weatherProviderError = "" Preferences.weatherProviderError = ""
Preferences.weatherProviderLocationError = "" Preferences.weatherProviderLocationError = ""
} catch(ex: Exception) { } catch(ex: Exception) {
@ -194,8 +216,16 @@ class WeatherNetworkApi(val context: Context) {
} }
} }
is NetworkResponse.ServerError -> { is NetworkResponse.ServerError -> {
Preferences.weatherProviderError = context.getString(R.string.weather_provider_error_generic) when (response.code) {
Preferences.weatherProviderLocationError = "" 401 -> {
Preferences.weatherProviderError = context.getString(R.string.weather_provider_error_invalid_key)
Preferences.weatherProviderLocationError = ""
}
else -> {
Preferences.weatherProviderError = context.getString(R.string.weather_provider_error_generic)
Preferences.weatherProviderLocationError = ""
}
}
WeatherHelper.removeWeather( WeatherHelper.removeWeather(
context context
) )
@ -225,10 +255,10 @@ class WeatherNetworkApi(val context: Context) {
when (val response = repository.getWeather()) { when (val response = repository.getWeather()) {
is NetworkResponse.Success -> { is NetworkResponse.Success -> {
try { try {
val data = response.body["data"] as List<LinkedTreeMap<String, Any>>? val data = response.body["data"] as List<*>?
data?.first()?.let { data?.first()?.let { it as LinkedTreeMap<*, *>
val temp = it["temp"] as Double val temp = it["temp"] as Double
val weatherInfo = it["weather"] as LinkedTreeMap<String, Any> val weatherInfo = it["weather"] as LinkedTreeMap<*, *>
val iconCode = weatherInfo["icon"] as String val iconCode = weatherInfo["icon"] as String
Preferences.weatherTemp = temp.toFloat() Preferences.weatherTemp = temp.toFloat()
@ -238,8 +268,6 @@ class WeatherNetworkApi(val context: Context) {
Preferences.weatherProviderError = "" Preferences.weatherProviderError = ""
Preferences.weatherProviderLocationError = "" Preferences.weatherProviderLocationError = ""
EventBus.getDefault().post(MainFragment.UpdateUiMessageEvent())
} }
} catch(ex: Exception) { } catch(ex: Exception) {
Preferences.weatherProviderError = context.getString(R.string.weather_provider_error_generic) Preferences.weatherProviderError = context.getString(R.string.weather_provider_error_generic)
@ -288,12 +316,12 @@ class WeatherNetworkApi(val context: Context) {
when (val response = repository.getWeather()) { when (val response = repository.getWeather()) {
is NetworkResponse.Success -> { is NetworkResponse.Success -> {
try { try {
val current = response.body["current"] as LinkedTreeMap<String, Any>? val current = response.body["current"] as LinkedTreeMap<*, *>?
current?.let { current?.let {
val tempC = current["temp_c"] as Double val tempC = current["temp_c"] as Double
val tempF = current["temp_f"] as Double val tempF = current["temp_f"] as Double
val isDay = current["is_day"] as Double val isDay = current["is_day"] as Double
val condition = current["condition"] as LinkedTreeMap<String, Any> val condition = current["condition"] as LinkedTreeMap<*, *>
val iconCode = condition["code"] as Double val iconCode = condition["code"] as Double
Preferences.weatherTemp = if (Preferences.weatherTempUnit == "F") tempF.toFloat() else tempC.toFloat() Preferences.weatherTemp = if (Preferences.weatherTempUnit == "F") tempF.toFloat() else tempC.toFloat()
@ -303,8 +331,6 @@ class WeatherNetworkApi(val context: Context) {
Preferences.weatherProviderError = "" Preferences.weatherProviderError = ""
Preferences.weatherProviderLocationError = "" Preferences.weatherProviderLocationError = ""
EventBus.getDefault().post(MainFragment.UpdateUiMessageEvent())
} }
} catch(ex: Exception) { } catch(ex: Exception) {
Preferences.weatherProviderError = context.getString(R.string.weather_provider_error_generic) Preferences.weatherProviderError = context.getString(R.string.weather_provider_error_generic)
@ -353,28 +379,74 @@ class WeatherNetworkApi(val context: Context) {
private suspend fun useAccuweatherProvider(context: Context) { private suspend fun useAccuweatherProvider(context: Context) {
if (Preferences.weatherProviderApiAccuweather != "") { if (Preferences.weatherProviderApiAccuweather != "") {
// val repository = AccuweatherRepository() val repository = AccuweatherRepository()
// when (val response = repository.getWeather()) { when (val locationResponse = repository.getLocation()) {
// is NetworkResponse.Success -> { is NetworkResponse.Success -> {
// try { try {
// Log.d("ciao", response.body.toString()) val key = locationResponse.body["Key"] as String
// } catch(ex: Exception) {
//
// Preferences.weatherProviderError = context.getString(R.string.weather_provider_error_missing_key)
// Preferences.weatherProviderLocationError = ""
// }
// }
// is NetworkResponse.ServerError -> {
// WeatherHelper.removeWeather(
// context
// )
// }
// Preferences.weatherProviderError = context.getString(R.string.weather_provider_error_missing_key)
// Preferences.weatherProviderLocationError = ""
// EventBus.getDefault().post(MainFragment.UpdateUiMessageEvent())
// }
when (val weatherResponse = repository.getWeather(key)) {
is NetworkResponse.Success -> {
try {
weatherResponse.body.first().let {
val temp = it["Temperature"] as LinkedTreeMap<*, *>
val tempC = (temp["Metric"] as LinkedTreeMap<*, *>)["Value"] as Double
val tempF = (temp["Imperial"] as LinkedTreeMap<*, *>)["Value"] as Double
val isDay = it["IsDayTime"] as Boolean
val icon = it["WeatherIcon"] as Double
Preferences.weatherTemp = if (Preferences.weatherTempUnit == "F") tempF.toFloat() else tempC.toFloat()
Preferences.weatherIcon = repository.getWeatherIcon(icon.toInt(), isDay)
Preferences.weatherRealTempUnit = Preferences.weatherTempUnit
MainWidget.updateWidget(context)
}
Preferences.weatherProviderError = ""
Preferences.weatherProviderLocationError = ""
} catch (ex: Exception) {
Preferences.weatherProviderError = context.getString(R.string.weather_provider_error_generic)
Preferences.weatherProviderLocationError = ""
}
}
else -> {
Preferences.weatherProviderError = context.getString(R.string.weather_provider_error_connection)
Preferences.weatherProviderLocationError = ""
}
}
} catch(ex: Exception) {
Preferences.weatherProviderError = context.getString(R.string.weather_provider_error_generic)
Preferences.weatherProviderLocationError = ""
} finally {
EventBus.getDefault().post(MainFragment.UpdateUiMessageEvent())
}
}
is NetworkResponse.ServerError -> {
when (locationResponse.code) {
401 -> {
Preferences.weatherProviderError = context.getString(R.string.weather_provider_error_invalid_key)
Preferences.weatherProviderLocationError = ""
}
503 -> {
Preferences.weatherProviderError = context.getString(R.string.weather_provider_error_expired_key)
Preferences.weatherProviderLocationError = ""
}
else -> {
Preferences.weatherProviderError = context.getString(R.string.weather_provider_error_generic)
Preferences.weatherProviderLocationError = ""
}
}
WeatherHelper.removeWeather(
context
)
EventBus.getDefault().post(MainFragment.UpdateUiMessageEvent())
}
else -> {
Preferences.weatherProviderError = context.getString(R.string.weather_provider_error_connection)
Preferences.weatherProviderLocationError = ""
EventBus.getDefault().post(MainFragment.UpdateUiMessageEvent())
}
}
} else { } else {
Preferences.weatherProviderError = context.getString(R.string.weather_provider_error_missing_key) Preferences.weatherProviderError = context.getString(R.string.weather_provider_error_missing_key)
Preferences.weatherProviderLocationError = "" Preferences.weatherProviderLocationError = ""
@ -393,38 +465,27 @@ class WeatherNetworkApi(val context: Context) {
is NetworkResponse.Success -> { is NetworkResponse.Success -> {
try { try {
val pp = response.body["properties"] as LinkedTreeMap<*, *> val pp = response.body["properties"] as LinkedTreeMap<*, *>
val data = pp["timeseries"] as List<LinkedTreeMap<String, Any>>? val data = pp["timeseries"] as List<*>?
data?.let { data?.first()?.let { it as LinkedTreeMap<*, *>
val format = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'") val dd = it["data"] as LinkedTreeMap<*, *>
for (item in data) { val instant = dd["instant"] as LinkedTreeMap<*, *>
val time = Calendar.getInstance().apply { time = format.parse(item["time"] as String)!! } val next = dd["next_1_hours"] as LinkedTreeMap<*, *>
val now = Calendar.getInstance()
if (time.timeInMillis >= now.timeInMillis) {
val dd = item["data"] as LinkedTreeMap<*, *>
val instant = dd["instant"] as LinkedTreeMap<*, *>
val next = dd["next_1_hours"] as LinkedTreeMap<*, *>
val details = instant["details"] as LinkedTreeMap<*, *> val details = instant["details"] as LinkedTreeMap<*, *>
val temp = details["air_temperature"] as Double val temp = details["air_temperature"] as Double
val summary = next["summary"] as LinkedTreeMap<*, *> val summary = next["summary"] as LinkedTreeMap<*, *>
val iconCode = summary["symbol_code"] as String val iconCode = summary["symbol_code"] as String
Preferences.weatherTemp = temp.toFloat() Preferences.weatherTemp = temp.toFloat()
Preferences.weatherIcon = WeatherHelper.getYRIcon(iconCode, now.get(Calendar.HOUR_OF_DAY) >= 22 || now.get(Calendar.HOUR_OF_DAY) <= 8) Preferences.weatherIcon = WeatherHelper.getYRIcon(iconCode)
Preferences.weatherTempUnit = "C" Preferences.weatherTempUnit = "C"
Preferences.weatherRealTempUnit = Preferences.weatherTempUnit Preferences.weatherRealTempUnit = Preferences.weatherTempUnit
MainWidget.updateWidget(context) MainWidget.updateWidget(context)
Preferences.weatherProviderError = "" Preferences.weatherProviderError = ""
Preferences.weatherProviderLocationError = "" Preferences.weatherProviderLocationError = ""
break
}
}
} }
} catch(ex: Exception) { } catch(ex: Exception) {
ex.printStackTrace() ex.printStackTrace()
Preferences.weatherProviderError = context.getString(R.string.weather_provider_error_generic) Preferences.weatherProviderError = context.getString(R.string.weather_provider_error_generic)
@ -448,4 +509,4 @@ class WeatherNetworkApi(val context: Context) {
} }
} }
} }
} }

View File

@ -13,7 +13,7 @@ object ApiServices {
): NetworkResponse<HashMap<String, Any>, HashMap<String, Any>> ): NetworkResponse<HashMap<String, Any>, HashMap<String, Any>>
@Headers("User-Agent: (Another Widget, tommaso.berlose@gmail.com)") @Headers("User-Agent: (Another Widget, tommaso.berlose@gmail.com)")
@GET("gridpoints/{gridId}/{gridX},{gridY}/forecast") @GET("gridpoints/{gridId}/{gridX},{gridY}/forecast/hourly")
suspend fun getWeather( suspend fun getWeather(
@Path("gridId") gridId: String, @Path("gridId") gridId: String,
@Path("gridX") gridX: Int, @Path("gridX") gridX: Int,
@ -54,13 +54,17 @@ object ApiServices {
} }
interface AccuweatherService { interface AccuweatherService {
@GET("") @GET("locations/v1/cities/geoposition/search")
suspend fun getWeather( suspend fun getLocation(
@Path("gridId") gridId: String, @Query("apikey") apikey: String,
@Path("gridX") gridX: Int, @Query("q") location: String
@Path("gridY") gridY: Int,
@Query("units") unit: String
): NetworkResponse<HashMap<String, Any>, HashMap<String, Any>> ): NetworkResponse<HashMap<String, Any>, HashMap<String, Any>>
@GET("currentconditions/v1/{locationKey}")
suspend fun getWeather(
@Path("locationKey") locationKey: String,
@Query("apikey") apikey: String
): NetworkResponse<List<HashMap<String, Any>>, HashMap<String, Any>>
} }
interface YrService { interface YrService {
@ -80,4 +84,4 @@ object ApiServices {
@Query("username") username: String = "tommaso.berlose", @Query("username") username: String = "tommaso.berlose",
): NetworkResponse<HashMap<String, Any>, HashMap<String, Any>> ): NetworkResponse<HashMap<String, Any>, HashMap<String, Any>>
} }
} }

View File

@ -1,6 +1,7 @@
package com.tommasoberlose.anotherwidget.network.repository package com.tommasoberlose.anotherwidget.network.repository
import com.haroldadmin.cnradapter.NetworkResponseAdapterFactory import com.haroldadmin.cnradapter.NetworkResponseAdapterFactory
import com.tommasoberlose.anotherwidget.global.Preferences
import com.tommasoberlose.anotherwidget.network.api.ApiServices import com.tommasoberlose.anotherwidget.network.api.ApiServices
import retrofit2.Retrofit import retrofit2.Retrofit
import retrofit2.converter.gson.GsonConverterFactory import retrofit2.converter.gson.GsonConverterFactory
@ -9,10 +10,11 @@ class AccuweatherRepository {
/* ACCUWEATHER */ /* ACCUWEATHER */
private val apiServiceAccu: ApiServices.AccuweatherService = getRetrofit().create(ApiServices.AccuweatherService::class.java) private val apiServiceAccu: ApiServices.AccuweatherService = getRetrofit().create(ApiServices.AccuweatherService::class.java)
suspend fun getWeather(): Nothing = TODO() suspend fun getLocation() = apiServiceAccu.getLocation(Preferences.weatherProviderApiAccuweather, "${Preferences.customLocationLat},${Preferences.customLocationLon}")
suspend fun getWeather(locationKey: String) = apiServiceAccu.getWeather(locationKey, Preferences.weatherProviderApiAccuweather)
companion object { companion object {
private const val BASE_URL_ACCU = "" private const val BASE_URL_ACCU = "https://dataservice.accuweather.com/"
private fun getRetrofit(): Retrofit { private fun getRetrofit(): Retrofit {
return Retrofit.Builder() return Retrofit.Builder()
@ -22,4 +24,20 @@ class AccuweatherRepository {
.build() .build()
} }
} }
}
fun getWeatherIcon(icon: Int, isDaytime: Boolean): String = when(icon) {
1, 2, 30, 33, 34 -> "01"
3, 4, 35, 36 -> "02"
5, 37 -> "50"
6, 38 -> "03"
7, 8 -> "04"
11 -> "82"
12, 13, 14, 18, 39, 40 -> "10"
15 -> "09"
16, 17, 41, 42 -> "11"
32 -> "80"
19, 20, 21, 22, 23, 24, 31, 43, 44 -> "13"
25, 26, 29 -> "81"
else -> ""
} + if (isDaytime) "d" else "n"
}

View File

@ -23,4 +23,157 @@ class HereRepository {
.build() .build()
} }
} }
}
fun getWeatherIcon(iconName: String, isDaytime: Boolean): String = when(iconName.substringAfter("night_")) {
"sunny" -> "01"
"clear" -> "01"
"mostly_sunny" -> "01"
"mostly_clear" -> "01"
"passing_clounds" -> "02"
"more_sun_than_clouds" -> "02"
"scattered_clouds" -> "02"
"partly_cloudy" -> "02"
"a_mixture_of_sun_and_clouds" -> "03"
"increasing_cloudiness" -> "03"
"breaks_of_sun_late" -> "03"
"afternoon_clouds" -> "03"
"morning_clouds" -> "03"
"partly_sunny" -> "03"
"high_level_clouds" -> "03"
"decreasing_cloudiness" -> "03"
"clearing_skies" -> "01"
"high_clouds" -> "03"
"rain_early" -> "10"
"heavy_rain_early" -> "10"
"strong_thunderstorms" -> "09"
"severe_thunderstorms" -> "09"
"thundershowers" -> "11"
"thunderstorms" -> "11"
"tstorms_early" -> "11"
"isolated_tstorms_late" -> "11"
"scattered_tstorms_late" -> "11"
"tstorms_late" -> "11"
"tstorms" -> "11"
"ice_fog" -> "82"
"more_clouds_than_sun" -> "03"
"broken_clouds" -> "03"
"scattered_showers" -> "10"
"a_few_showers" -> "10"
"light_showers" -> "10"
"passing_showers" -> "10"
"rain_showers" -> "10"
"showers" -> "10"
"widely_scattered_tstorms" -> "11"
"isolated_tstorms" -> "11"
"a_few_tstorms" -> "11"
"scattered_tstorms" -> "11"
"hazy_sunshine" -> "50"
"haze" -> "50"
"smoke" -> "50"
"low_level_haze" -> "50"
"early_fog_followed_by_sunny_skies" -> "50"
"early_fog" -> "82"
"light_fog" -> "82"
"fog" -> "82"
"dense_fog" -> "82"
//"night_haze"
//"night_smoke"
//"night_low_level_haze"
//"night_widely_scattered_tstorms"
//"night_isolated_tstorms"
//"night_a_few_tstorms"
//"night_scattered_tstorms"
//"night_tstorms"
//"night_clear"
"mostly_cloudy" -> "03"
"cloudy" -> "04"
"overcast" -> "04"
"low_clouds" -> "03"
"hail" -> "10"
"sleet" -> "81"
"light_mixture_of_precip" -> "81"
"icy_mix" -> "81"
"mixture_of_precip" -> "81"
"heavy_mixture_of_precip" -> "81"
"snow_changing_to_rain" -> "81"
"snow_changing_to_an_icy_mix" -> "81"
"an_icy_mix_changing_to_snow" -> "81"
"an_icy_mix_changing_to_rain" -> "81"
"rain_changing_to_snow" -> "81"
"rain_changing_to_an_icy_mix" -> "81"
"light_icy_mix_early" -> "81"
"icy_mix_early" -> "81"
"light_icy_mix_late" -> "81"
"icy_mix_late" -> "81"
"snow_rain_mix" -> "81"
"scattered_flurries" -> "13"
"snow_flurries" -> "13"
"light_snow_showers" -> "13"
"snow_showers" -> "13"
"light_snow" -> "13"
"flurries_early" -> "13"
"snow_showers_early" -> "13"
"light_snow_early" -> "13"
"flurries_late" -> "13"
"snow_showers_late" -> "13"
"light_snow_late" -> "13"
//"night_decreasing_cloudiness"
//"night_clearing_skies"
//"night_high_level_clouds"
//"night_high_clouds"
//"night_scattered_showers"
//"night_a_few_showers"
//"night_light_showers"
//"night_passing_showers"
//"night_rain_showers"
//"night_sprinkles"
//"night_showers"
//"night_mostly_clear"
//"night_passing_clouds"
//"night_scattered_clouds"
//"night_partly_cloudy"
//"increasing_cloudiness"
//"night_afternoon_clouds"
//"night_morning_clouds"
//"night_broken_clouds"
//"night_mostly_cloudy"
"light_freezing_rain" -> "81"
"freezing_rain" -> "81"
"heavy_rain" -> "10"
"lots_of_rain" -> "10"
"tons_of_rain" -> "10"
//"heavy_rain_early" -> "10"
"heavy_rain_late" -> "10"
"flash_floods" -> "10"
"flood" -> "10"
"drizzle" -> "10"
"sprinkles" -> "10"
"light_rain" -> "10"
"sprinkles_early" -> "10"
"light_rain_early" -> "10"
"sprinkles_late" -> "10"
"light_rain_late" -> "10"
"rain" -> "10"
"numerous_showers" -> "10"
"showery" -> "10"
"showers_early" -> "10"
//"rain_early" -> "10"
"showers_late" -> "10"
"rain_late" -> "10"
"snow" -> "13"
"moderate_snow" -> "13"
"snow_early" -> "13"
"snow_late" -> "13"
"heavy_snow" -> "13"
"heavy_snow_early" -> "13"
"heavy_snow_late" -> "13"
"tornado" -> "80"
"tropical_storm" -> "09"
"hurricane" -> "80"
"sandstorm" -> "50"
"duststorm" -> "50"
"snowstorm" -> "13"
"blizzard" -> "13"
else -> ""
} + if (isDaytime) "d" else "n"
}

View File

@ -95,7 +95,7 @@ class UpdatesReceiver : BroadcastReceiver() {
setEventUpdate(context, event) setEventUpdate(context, event)
} }
} else { } else {
val event = eventRepository.getEventByEventId(eventId) val event = eventRepository.getEventById(eventId)
if (event != null) { if (event != null) {
setEventUpdate(context, event) setEventUpdate(context, event)
} }
@ -166,11 +166,11 @@ class UpdatesReceiver : BroadcastReceiver() {
fireTime.coerceAtLeast(now.timeInMillis + 1000 * 60), fireTime.coerceAtLeast(now.timeInMillis + 1000 * 60),
PendingIntent.getBroadcast( PendingIntent.getBroadcast(
context, context,
event.eventID.toInt(), event.id.toInt(),
Intent(context, UpdatesReceiver::class.java).apply { Intent(context, UpdatesReceiver::class.java).apply {
action = Actions.ACTION_TIME_UPDATE action = Actions.ACTION_TIME_UPDATE
if (event.startDate > now.timeInMillis) if (event.startDate > now.timeInMillis)
putExtra(EVENT_ID, event.eventID) putExtra(EVENT_ID, event.id)
}, },
PendingIntent.FLAG_UPDATE_CURRENT PendingIntent.FLAG_UPDATE_CURRENT
) )
@ -185,7 +185,7 @@ class UpdatesReceiver : BroadcastReceiver() {
}, 0)) }, 0))
val eventRepository = EventRepository(context) val eventRepository = EventRepository(context)
eventRepository.getFutureEvents().forEach { eventRepository.getFutureEvents().forEach {
cancel(PendingIntent.getBroadcast(context, it.eventID.toInt(), Intent(context, UpdatesReceiver::class.java).apply { cancel(PendingIntent.getBroadcast(context, it.id.toInt(), Intent(context, UpdatesReceiver::class.java).apply {
action = Actions.ACTION_TIME_UPDATE action = Actions.ACTION_TIME_UPDATE
}, 0)) }, 0))
} }

View File

@ -143,7 +143,7 @@ class UpdateCalendarService : Service() {
title = e.title ?: "", title = e.title ?: "",
startDate = instance.begin, startDate = instance.begin,
endDate = instance.end, endDate = instance.end,
calendarID = e.calendarId.toInt(), calendarID = e.calendarId,
allDay = e.allDay, allDay = e.allDay,
address = e.eventLocation ?: "", address = e.eventLocation ?: "",
selfAttendeeStatus = e.selfAttendeeStatus.toInt(), selfAttendeeStatus = e.selfAttendeeStatus.toInt(),

View File

@ -115,8 +115,6 @@ class WeatherProviderActivity : AppCompatActivity() {
adapter.updateData( adapter.updateData(
Constants.WeatherProvider.values().asList() Constants.WeatherProvider.values().asList()
.filter { it != Constants.WeatherProvider.HERE }
.filter { it != Constants.WeatherProvider.ACCUWEATHER }
) )
setupListener() setupListener()
@ -165,7 +163,7 @@ class WeatherProviderActivity : AppCompatActivity() {
} }
@Subscribe(threadMode = ThreadMode.MAIN) @Subscribe(threadMode = ThreadMode.MAIN)
fun onMessageEvent(ignore: MainFragment.UpdateUiMessageEvent?) { fun onMessageEvent(@Suppress("UNUSED_PARAMETER") ignore: MainFragment.UpdateUiMessageEvent?) {
binding.loader.isVisible = Preferences.weatherProviderError == "-" binding.loader.isVisible = Preferences.weatherProviderError == "-"
if (Preferences.weatherProviderError == "" && Preferences.weatherProviderLocationError == "") { if (Preferences.weatherProviderError == "" && Preferences.weatherProviderLocationError == "") {
Snackbar.make(binding.listView, getString(R.string.settings_weather_provider_api_key_subtitle_all_set), Snackbar.LENGTH_LONG).show() Snackbar.make(binding.listView, getString(R.string.settings_weather_provider_api_key_subtitle_all_set), Snackbar.LENGTH_LONG).show()

View File

@ -86,6 +86,7 @@ class MainFragment : Fragment() {
binding.actionSettings.isClickable = !show binding.actionSettings.isClickable = !show
binding.actionSettings.isFocusable = !show binding.actionSettings.isFocusable = !show
binding.fragmentTitle.text = if (show) destination.label.toString() else getString(R.string.app_name) binding.fragmentTitle.text = if (show) destination.label.toString() else getString(R.string.app_name)
binding.toolbar.cardElevation = 0f
} }
binding.actionSettings.setOnSingleClickListener { binding.actionSettings.setOnSingleClickListener {
@ -98,6 +99,10 @@ class MainFragment : Fragment() {
} }
private fun subscribeUi(viewModel: MainViewModel) { private fun subscribeUi(viewModel: MainViewModel) {
viewModel.showPreview.observe(viewLifecycleOwner) {
binding.preview.visibility = if (it) View.VISIBLE else View.GONE
}
viewModel.showWallpaper.observe(viewLifecycleOwner) { viewModel.showWallpaper.observe(viewLifecycleOwner) {
if (it) { if (it) {
val wallpaper = requireActivity().getCurrentWallpaper() val wallpaper = requireActivity().getCurrentWallpaper()
@ -138,7 +143,7 @@ class MainFragment : Fragment() {
} }
viewModel.fragmentScrollY.observe(viewLifecycleOwner) { viewModel.fragmentScrollY.observe(viewLifecycleOwner) {
binding.toolbar.cardElevation = if (it > 0) 24f else 0f binding.toolbar.cardElevation = if (it > 0) 32f else 0f
} }
viewModel.widgetPreferencesUpdate.observe(viewLifecycleOwner) { viewModel.widgetPreferencesUpdate.observe(viewLifecycleOwner) {
@ -218,7 +223,7 @@ class MainFragment : Fragment() {
class ChangeTabEvent(val page: Int) class ChangeTabEvent(val page: Int)
@Subscribe(threadMode = ThreadMode.MAIN) @Subscribe(threadMode = ThreadMode.MAIN)
fun onUpdateUiEvent(ignore: UpdateUiMessageEvent?) { fun onUpdateUiEvent(@Suppress("UNUSED_PARAMETER") ignore: UpdateUiMessageEvent?) {
delayJob?.cancel() delayJob?.cancel()
delayJob = lifecycleScope.launch(Dispatchers.IO) { delayJob = lifecycleScope.launch(Dispatchers.IO) {
delay(300) delay(300)
@ -229,7 +234,7 @@ class MainFragment : Fragment() {
} }
@Subscribe(threadMode = ThreadMode.MAIN) @Subscribe(threadMode = ThreadMode.MAIN)
fun onChangeTabEvent(ignore: ChangeTabEvent) { fun onChangeTabEvent(@Suppress("UNUSED_PARAMETER") ignore: ChangeTabEvent) {
val navHost = childFragmentManager.findFragmentById(R.id.settings_fragment) as? NavHostFragment? val navHost = childFragmentManager.findFragmentById(R.id.settings_fragment) as? NavHostFragment?
navHost?.navController?.navigateUp() navHost?.navController?.navigateUp()
} }

View File

@ -32,6 +32,7 @@ import com.tommasoberlose.anotherwidget.receivers.WidgetClickListenerReceiver
import com.tommasoberlose.anotherwidget.utils.checkGrantedPermission import com.tommasoberlose.anotherwidget.utils.checkGrantedPermission
import com.tommasoberlose.anotherwidget.utils.convertDpToPixel import com.tommasoberlose.anotherwidget.utils.convertDpToPixel
import com.tommasoberlose.anotherwidget.utils.isDarkTheme import com.tommasoberlose.anotherwidget.utils.isDarkTheme
import com.tommasoberlose.anotherwidget.utils.toPixel
import java.text.DateFormat import java.text.DateFormat
import java.util.* import java.util.*
import java.util.concurrent.TimeUnit import java.util.concurrent.TimeUnit
@ -875,24 +876,24 @@ class AlignedWidget(val context: Context, val rightAligned: Boolean = false) {
val shadowRadius = val shadowRadius =
when (if (context.isDarkTheme()) Preferences.textShadowDark else Preferences.textShadow) { when (if (context.isDarkTheme()) Preferences.textShadowDark else Preferences.textShadow) {
0 -> 0f 0 -> 0f
1 -> 5f 1 -> 2f
2 -> 5f 2 -> 3f
else -> 5f else -> 2f
} }.toPixel(context)
val shadowColor = val shadowColor =
when (if (context.isDarkTheme()) Preferences.textShadowDark else Preferences.textShadow) { when (if (context.isDarkTheme()) Preferences.textShadowDark else Preferences.textShadow) {
0 -> Color.TRANSPARENT 0 -> Color.TRANSPARENT
1 -> R.color.black_50 1 -> Color.DKGRAY
2 -> Color.BLACK 2 -> Color.BLACK
else -> R.color.black_50 else -> Color.DKGRAY
} }
val shadowDy = val shadowOffset =
when (if (context.isDarkTheme()) Preferences.textShadowDark else Preferences.textShadow) { when (if (context.isDarkTheme()) Preferences.textShadowDark else Preferences.textShadow) {
0 -> 0f 0 -> 0f
1 -> 0f 1 -> 0f
2 -> 1f 2 -> 0.5f
else -> 0f else -> 0f
} }.toPixel(context)
listOf<TextView>( listOf<TextView>(
bindingView.date, bindingView.date,
@ -903,7 +904,7 @@ class AlignedWidget(val context: Context, val rightAligned: Boolean = false) {
bindingView.weatherSubLineDivider, bindingView.weatherSubLineDivider,
bindingView.weatherSubLineTemperature, bindingView.weatherSubLineTemperature,
).forEach { ).forEach {
it.setShadowLayer(shadowRadius, 0f, shadowDy, shadowColor) it.setShadowLayer(shadowRadius, shadowOffset, shadowOffset, shadowColor)
} }
// Icons shadow // Icons shadow
@ -917,7 +918,7 @@ class AlignedWidget(val context: Context, val rightAligned: Boolean = false) {
it.second.isVisible = it.first.isVisible it.second.isVisible = it.first.isVisible
it.second.scaleX = it.first.scaleX it.second.scaleX = it.first.scaleX
it.second.scaleY = it.first.scaleY it.second.scaleY = it.first.scaleY
it.second.applyShadow(it.first, 0.7f) it.second.applyShadow(it.first, 0.8f)
} }
} }

View File

@ -912,24 +912,24 @@ class StandardWidget(val context: Context) {
val shadowRadius = val shadowRadius =
when (if (context.isDarkTheme()) Preferences.textShadowDark else Preferences.textShadow) { when (if (context.isDarkTheme()) Preferences.textShadowDark else Preferences.textShadow) {
0 -> 0f 0 -> 0f
1 -> 5f 1 -> 2f
2 -> 5f 2 -> 3f
else -> 5f else -> 2f
} }.toPixel(context)
val shadowColor = val shadowColor =
when (if (context.isDarkTheme()) Preferences.textShadowDark else Preferences.textShadow) { when (if (context.isDarkTheme()) Preferences.textShadowDark else Preferences.textShadow) {
0 -> Color.TRANSPARENT 0 -> Color.TRANSPARENT
1 -> R.color.black_50 1 -> Color.DKGRAY
2 -> Color.BLACK 2 -> Color.BLACK
else -> R.color.black_50 else -> Color.DKGRAY
} }
val shadowDy = val shadowOffset =
when (if (context.isDarkTheme()) Preferences.textShadowDark else Preferences.textShadow) { when (if (context.isDarkTheme()) Preferences.textShadowDark else Preferences.textShadow) {
0 -> 0f 0 -> 0f
1 -> 0f 1 -> 0f
2 -> 1f 2 -> 0.5f
else -> 0f else -> 0f
} }.toPixel(context)
listOf<TextView>( listOf<TextView>(
bindingView.date, bindingView.date,
@ -941,7 +941,7 @@ class StandardWidget(val context: Context) {
bindingView.weatherSubLineDivider, bindingView.weatherSubLineDivider,
bindingView.weatherSubLineTemperature, bindingView.weatherSubLineTemperature,
).forEach { ).forEach {
it.setShadowLayer(shadowRadius, 0f, shadowDy, shadowColor) it.setShadowLayer(shadowRadius, shadowOffset, shadowOffset, shadowColor)
} }
// Icons shadow // Icons shadow
@ -955,7 +955,7 @@ class StandardWidget(val context: Context) {
it.second.isVisible = it.first.isVisible it.second.isVisible = it.first.isVisible
it.second.scaleX = it.first.scaleX it.second.scaleX = it.first.scaleX
it.second.scaleY = it.first.scaleY it.second.scaleY = it.first.scaleY
it.second.applyShadow(it.first, 0.7f) it.second.applyShadow(it.first, 0.8f)
} }
} }

View File

@ -511,7 +511,7 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:paddingLeft="24dp" android:paddingLeft="24dp"
android:paddingRight="24dp" android:paddingRight="24dp"
android:paddingBottom="32dp" android:paddingBottom="12dp"
android:duplicateParentState="true" android:duplicateParentState="true"
android:textSize="14sp" android:textSize="14sp"
android:textColor="@color/colorSecondaryText" android:textColor="@color/colorSecondaryText"

View File

@ -16,7 +16,7 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:paddingTop="8dp" android:paddingTop="8dp"
android:paddingBottom="32dp" android:paddingBottom="48dp"
android:orientation="vertical"> android:orientation="vertical">
<androidx.appcompat.widget.AppCompatTextView <androidx.appcompat.widget.AppCompatTextView
android:layout_width="match_parent" android:layout_width="match_parent"

View File

@ -76,7 +76,6 @@
android:id="@+id/footer" android:id="@+id/footer"
android:padding="8dp" android:padding="8dp"
android:layout_marginTop="16dp" android:layout_marginTop="16dp"
android:layout_marginBottom="24dp"
android:orientation="horizontal"> android:orientation="horizontal">
<ImageView <ImageView
android:layout_width="48dp" android:layout_width="48dp"

View File

@ -138,6 +138,8 @@
android:layout_width="0dp" android:layout_width="0dp"
android:layout_weight="1" android:layout_weight="1"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:clipToPadding="false"
android:clipChildren="false"
android:gravity="center_vertical" android:gravity="center_vertical"
android:id="@+id/sub_line" android:id="@+id/sub_line"
android:visibility="gone" android:visibility="gone"
@ -145,12 +147,12 @@
<RelativeLayout <RelativeLayout
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:cropToPadding="false" android:clipToPadding="false"
android:clipChildren="false"> android:clipChildren="false">
<ImageView <ImageView
android:layout_width="16dp" android:layout_width="16dp"
android:layout_height="16dp" android:layout_height="16dp"
android:cropToPadding="false" android:clipToPadding="false"
android:clipChildren="false" android:clipChildren="false"
android:layout_marginEnd="6dp" android:layout_marginEnd="6dp"
android:id="@+id/sub_line_icon_shadow" android:id="@+id/sub_line_icon_shadow"
@ -158,7 +160,7 @@
<ImageView <ImageView
android:layout_width="16dp" android:layout_width="16dp"
android:layout_height="16dp" android:layout_height="16dp"
android:cropToPadding="false" android:clipToPadding="false"
android:clipChildren="false" android:clipChildren="false"
android:layout_marginEnd="6dp" android:layout_marginEnd="6dp"
android:id="@+id/sub_line_icon" android:id="@+id/sub_line_icon"
@ -179,6 +181,8 @@
android:orientation="horizontal" android:orientation="horizontal"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:clipToPadding="false"
android:clipChildren="false"
android:gravity="center" android:gravity="center"
android:visibility="gone" android:visibility="gone"
android:layout_marginStart="2dp" android:layout_marginStart="2dp"

View File

@ -157,12 +157,16 @@
android:layout_gravity="center" android:layout_gravity="center"
android:clipChildren="false" android:clipChildren="false"
android:clipToPadding="false" android:clipToPadding="false"
android:paddingStart="4dp"
android:paddingEnd="4dp"
android:layoutDirection="locale" android:layoutDirection="locale"
android:gravity="center"> android:gravity="center">
<LinearLayout <LinearLayout
android:layout_width="0dp" android:layout_width="0dp"
android:layout_weight="1" android:layout_weight="1"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:clipToPadding="false"
android:clipChildren="false"
android:gravity="center_vertical" android:gravity="center_vertical"
android:id="@+id/sub_line" android:id="@+id/sub_line"
android:visibility="gone" android:visibility="gone"
@ -170,12 +174,12 @@
<RelativeLayout <RelativeLayout
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:cropToPadding="false" android:clipToPadding="false"
android:clipChildren="false"> android:clipChildren="false">
<ImageView <ImageView
android:layout_width="16dp" android:layout_width="16dp"
android:layout_height="16dp" android:layout_height="16dp"
android:cropToPadding="false" android:clipToPadding="false"
android:clipChildren="false" android:clipChildren="false"
android:layout_marginEnd="6dp" android:layout_marginEnd="6dp"
android:id="@+id/sub_line_icon_shadow" android:id="@+id/sub_line_icon_shadow"
@ -183,7 +187,7 @@
<ImageView <ImageView
android:layout_width="16dp" android:layout_width="16dp"
android:layout_height="16dp" android:layout_height="16dp"
android:cropToPadding="false" android:clipToPadding="false"
android:clipChildren="false" android:clipChildren="false"
android:layout_marginEnd="6dp" android:layout_marginEnd="6dp"
android:id="@+id/sub_line_icon" android:id="@+id/sub_line_icon"
@ -203,6 +207,8 @@
android:orientation="horizontal" android:orientation="horizontal"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:clipToPadding="false"
android:clipChildren="false"
android:gravity="center" android:gravity="center"
android:visibility="gone" android:visibility="gone"
android:layout_marginStart="2dp" android:layout_marginStart="2dp"

View File

@ -66,12 +66,12 @@
<!-- Calendar --> <!-- Calendar -->
<string name="settings_calendar_title">日历</string> <string name="settings_calendar_title">日历</string>
<string name="title_permission_calendar">显示你的活动</string> <string name="title_permission_calendar">显示你的活动</string>
<string name="description_permission_calendar">授予应用查看日历的权限以在微件查看你的活动。</string> <string name="description_permission_calendar">授予访问日历的权限\n以在您的微件查看活动。</string>
<string name="settings_filter_calendar_title">过滤活动</string> <string name="settings_filter_calendar_title">过滤活动</string>
<string name="settings_filter_calendar_subtitle">改变日历可见性</string> <string name="settings_filter_calendar_subtitle">改变日历可见性</string>
<string name="settings_all_day_title">全天活动</string> <string name="settings_all_day_title">全天活动</string>
<string name="main_calendar">日历账户</string> <string name="main_calendar">日历</string>
<string name="calendar_settings_list_error">加载日历列表出现错误</string> <string name="calendar_settings_list_error">加载日历列表出</string>
<string name="all_day">全天活动</string> <string name="all_day">全天活动</string>
<string name="show_events_visible">活动可见</string> <string name="show_events_visible">活动可见</string>
<string name="show_events_not_visible">活动不可见</string> <string name="show_events_not_visible">活动不可见</string>
@ -100,7 +100,7 @@
<string name="settings_show_multiple_events_title">多活动切换器</string> <string name="settings_show_multiple_events_title">多活动切换器</string>
<string name="soon">即将</string> <string name="soon">即将</string>
<string name="now">即刻</string> <string name="now">即刻</string>
<string name="settings_widget_update_frequency_title">更新剩余时间频率</string> <string name="settings_widget_update_frequency_title">剩余时间更新频率</string>
<string name="settings_widget_update_frequency_subtitle">更新频率越高耗电量越大。</string> <string name="settings_widget_update_frequency_subtitle">更新频率越高耗电量越大。</string>
<string name="settings_widget_update_frequency_low"></string> <string name="settings_widget_update_frequency_low"></string>
<string name="settings_widget_update_frequency_default">默认</string> <string name="settings_widget_update_frequency_default">默认</string>
@ -123,8 +123,8 @@
<!-- Weather --> <!-- Weather -->
<string name="settings_weather_title">天气</string> <string name="settings_weather_title">天气</string>
<string name="title_permission_location">显示天气信息</string> <string name="title_permission_location">显示天气</string>
<string name="description_permission_location">授予位权限\n以在您的微件上查看天气。</string> <string name="description_permission_location">授予位权限\n以在您的微件上查看天气。</string>
<string name="settings_unit_title">单位</string> <string name="settings_unit_title">单位</string>
<string name="fahrenheit" translatable="false">°F - 华氏度</string> <string name="fahrenheit" translatable="false">°F - 华氏度</string>
<string name="celsius" translatable="false">°C - 摄氏度</string> <string name="celsius" translatable="false">°C - 摄氏度</string>
@ -141,15 +141,15 @@
<string name="show_weather_not_visible">天气信息不可见</string> <string name="show_weather_not_visible">天气信息不可见</string>
<string name="settings_weather_app_title">天气</string> <string name="settings_weather_app_title">天气</string>
<string name="settings_weather_provider_api_key_title">天气API密钥</string> <string name="settings_weather_provider_api_key_title">天气API密钥</string>
<string name="settings_weather_provider_api_key_subtitle_all_set">已正确配置天气信息来源。</string> <string name="settings_weather_provider_api_key_subtitle_all_set">已正确配置天气数据源。</string>
<string name="settings_weather_provider_api_key_subtitle_not_set">必须配置天气信息来源。</string> <string name="settings_weather_provider_api_key_subtitle_not_set">必须配置天气数据源。</string>
<string name="api_key_hint">OpenWeatherMap API密钥</string> <string name="api_key_hint">OpenWeatherMap API密钥</string>
<string name="default_weather_app">Google Weather</string> <string name="default_weather_app">Google天气</string>
<string name="weather_warning">Google Awareness API已被弃用。要在微件上显示天气您需要从第三方源获取API密钥。</string> <string name="weather_warning">Google Awareness API已被弃用。要在微件上显示天气您需要从第三方数据源获取API密钥。</string>
<string name="settings_weather_icon_pack_title">天气图标</string> <string name="settings_weather_icon_pack_title">图标</string>
<string name="settings_weather_icon_pack_default">图标包%d</string> <string name="settings_weather_icon_pack_default">图标包%d</string>
<string name="background_location_warning">选择地理定位后,即便应用未启动或在后台,我们也会收集您的定位数据来更新天气信息。\n这些数据不会用在其他地方。</string> <string name="background_location_warning">选择地理定位后,即便应用未启动或在后台,我们也会收集您的定位数据来更新天气信息。\n这些数据不会用在其他地方。</string>
<string name="settings_weather_provider_api">天气信息来</string> <string name="settings_weather_provider_api">天气数据</string>
<string name="settings_weather_provider_open_weather" translatable="false">OpenWeatherMap.org</string> <string name="settings_weather_provider_open_weather" translatable="false">OpenWeatherMap.org</string>
<string name="settings_weather_provider_weatherbit" translatable="false">Weatherbit.io</string> <string name="settings_weather_provider_weatherbit" translatable="false">Weatherbit.io</string>
@ -159,35 +159,35 @@
<string name="settings_weather_provider_weather_gov" translatable="false">Weather.gov (USA)\nby National Weather Services</string> <string name="settings_weather_provider_weather_gov" translatable="false">Weather.gov (USA)\nby National Weather Services</string>
<string name="settings_weather_provider_yr" translatable="false">YR.no/Met.no\nby Meteorologisk Institutt</string> <string name="settings_weather_provider_yr" translatable="false">YR.no/Met.no\nby Meteorologisk Institutt</string>
<string name="weather_provider_info_open_weather_title">该天气信息来源需要API密钥才能正常工作。</string> <string name="weather_provider_info_open_weather_title">该天气数据源需要API密钥才能正常工作。</string>
<string name="weather_provider_info_weatherbit_title">该天气信息来源需要API密钥才能正常工作。</string> <string name="weather_provider_info_weatherbit_title">该天气数据源需要API密钥才能正常工作。</string>
<string name="weather_provider_info_weatherapi_title">该天气信息来源需要API密钥才能正常工作。</string> <string name="weather_provider_info_weatherapi_title">该天气数据源需要API密钥才能正常工作。</string>
<string name="weather_provider_info_here_title">该天气信息来源需要API密钥才能正常工作。</string> <string name="weather_provider_info_here_title">该天气数据源需要API密钥才能正常工作。</string>
<string name="weather_provider_info_accuweather_title">该天气信息来源需要API密钥才能正常工作。</string> <string name="weather_provider_info_accuweather_title">该天气数据源需要API密钥才能正常工作。</string>
<string name="weather_provider_info_weather_gov_title">该天气信息来源仅美国境内有效。</string> <string name="weather_provider_info_weather_gov_title">该天气数据源仅美国境内有效。</string>
<string name="weather_provider_info_yr_title">该天气信息来源仅支持摄氏度单位。</string> <string name="weather_provider_info_yr_title">该天气数据源仅支持摄氏度单位。</string>
<string name="weather_provider_info_open_weather_subtitle">前往天气信息来源网站、创建个人账户并将默认密钥复制粘贴到此处。</string> <string name="weather_provider_info_open_weather_subtitle">前往天气数据源网站、创建个人账户并将默认密钥复制粘贴到此处。</string>
<string name="weather_provider_info_weatherbit_subtitle">前往天气信息来源网站、创建个人账户并将默认密钥复制粘贴到此处。</string> <string name="weather_provider_info_weatherbit_subtitle">前往天气数据源网站、创建个人账户并将默认密钥复制粘贴到此处。</string>
<string name="weather_provider_info_weatherapi_subtitle">前往天气信息来源网站、创建个人账户并将默认密钥复制粘贴到此处。</string> <string name="weather_provider_info_weatherapi_subtitle">前往天气数据源网站、创建个人账户并将默认密钥复制粘贴到此处。</string>
<string name="weather_provider_info_here_subtitle">前往天气信息来源网站、创建个人账户并将默认密钥复制粘贴到此处。</string> <string name="weather_provider_info_here_subtitle">前往天气数据源网站、创建个人账户并将默认密钥复制粘贴到此处。</string>
<string name="weather_provider_info_accuweather_subtitle">前往天气信息来源网站、创建个人账户并将默认密钥复制粘贴到此处。</string> <string name="weather_provider_info_accuweather_subtitle">前往天气数据源网站、创建个人账户并将默认密钥复制粘贴到此处。</string>
<string name="weather_provider_info_weather_gov_subtitle">\n</string> <string name="weather_provider_info_weather_gov_subtitle">\n</string>
<string name="weather_provider_info_yr_subtitle">\n</string> <string name="weather_provider_info_yr_subtitle">\n</string>
<string name="weather_provider_error_missing_key">您选择的天气信息来源需要API密钥。</string> <string name="weather_provider_error_missing_key">您选择的天气数据源需要API密钥。</string>
<string name="weather_provider_error_wrong_location">不支持当前位置。</string> <string name="weather_provider_error_wrong_location">不支持当前位置。</string>
<string name="weather_provider_error_missing_location">请选择一个有效的地址</string> <string name="weather_provider_error_missing_location">请选择一个有效的位置</string>
<string name="weather_provider_error_expired_key">您的API密钥似乎已经过期了。</string> <string name="weather_provider_error_expired_key">您的API密钥似乎已经过期了。</string>
<string name="weather_provider_error_invalid_key">API密钥无效或尚未激活。</string> <string name="weather_provider_error_invalid_key">API密钥无效或尚未激活。</string>
<string name="weather_provider_error_misconfigured">天气信息来源配置错误。</string> <string name="weather_provider_error_misconfigured">天气数据源配置错误。</string>
<string name="weather_provider_error_connection">网络连接错误。</string> <string name="weather_provider_error_connection">网络连接错误。</string>
<string name="weather_provider_error_generic">出错了,请检查天气信息来源设置。</string> <string name="weather_provider_error_generic">出错了,请检查天气数据源设置。</string>
<string name="api_key_required_message">需要账户</string> <string name="api_key_required_message">需要账户</string>
<string name="us_only_message">仅限美国</string> <string name="us_only_message">仅限美国</string>
<string name="celsius_only_message">仅公制单位</string> <string name="celsius_only_message">仅公制单位</string>
<string name="weather_select_a_provider">选择信息来</string> <string name="weather_select_a_provider">选择数据</string>
<string name="weather_provider_activity_subtitle">从列表中选择天气信息来源。\n部分源需要注册免费个人账户,\n但它们通常也更精准。</string> <string name="weather_provider_activity_subtitle">从列表中选择天气数据源。\n部分数据源需要注册免费个人账户,\n但它们通常也更精准。</string>
<string name="location_access_notification_channel_id" translatable="false">location-access</string> <string name="location_access_notification_channel_id" translatable="false">location-access</string>
<string name="location_access_notification_channel_name">天气更新服务</string> <string name="location_access_notification_channel_name">天气更新服务</string>
@ -241,8 +241,8 @@
<string name="title_show_glance">显示速览信息</string> <string name="title_show_glance">显示速览信息</string>
<string name="description_show_glance_visible">服务已启用</string> <string name="description_show_glance_visible">服务已启用</string>
<string name="description_show_glance_not_visible">服务已禁用</string> <string name="description_show_glance_not_visible">服务已禁用</string>
<string name="settings_sort_glance_providers_title">源优先级</string> <string name="settings_sort_glance_providers_title">数据源优先级</string>
<string name="settings_sort_glance_providers_subtitle">您可以通过拖放图标来调整下方列表的项目顺序,以此更改数据源优先级。</string> <string name="settings_sort_glance_providers_subtitle">您可以通过拖放图标来调整下方列表的项目顺序,以此更改数据源优先级。</string>
<string name="settings_custom_notes_title">自定义提示</string> <string name="settings_custom_notes_title">自定义提示</string>
<string name="settings_daily_steps_title">运动健康</string> <string name="settings_daily_steps_title">运动健康</string>
<string name="daily_steps_counter">目前走了%d步</string> <string name="daily_steps_counter">目前走了%d步</string>
@ -250,7 +250,7 @@
<string name="battery_low_warning">电量低</string> <string name="battery_low_warning">电量低</string>
<string name="charging">正在充电</string> <string name="charging">正在充电</string>
<string name="charged">完全充满</string> <string name="charged">完全充满</string>
<string name="providers"></string> <string name="providers">数据</string>
<string name="glance_info">仅在当前没有活动且满足特定条件时才会显示速览。</string> <string name="glance_info">仅在当前没有活动且满足特定条件时才会显示速览。</string>
<string name="settings_music_players_filter_title">音乐播放器</string> <string name="settings_music_players_filter_title">音乐播放器</string>
<string name="settings_music_players_filter_subtitle">选择您常用的音乐播放器</string> <string name="settings_music_players_filter_subtitle">选择您常用的音乐播放器</string>
@ -304,7 +304,7 @@
</string-array> </string-array>
<string name="settings_show_events_as_glance_provider_title">活动</string> <string name="settings_show_events_as_glance_provider_title">活动</string>
<string name="settings_show_events_as_glance_provider_subtitle">查看您的日历活动的速览,并始终显示当前日期。</string> <string name="settings_show_events_as_glance_provider_subtitle">查看您的日历活动的速览,并始终显示当前日期。</string>
<string name="settings_show_events_as_glance_provider_error">在日历标签中启用活动显示,并授予所需的权限。</string> <string name="settings_show_events_as_glance_provider_error">启用日历显示,并授予所需的权限。</string>
<string name="events_glance_provider_format">%1$s %2$s</string> <string name="events_glance_provider_format">%1$s %2$s</string>
<string name="settings_show_weather_as_glance_provider_title">天气</string> <string name="settings_show_weather_as_glance_provider_title">天气</string>
<string name="settings_show_weather_as_glance_provider_subtitle">查看有关天气的更多信息,并始终显示当前日期。</string> <string name="settings_show_weather_as_glance_provider_subtitle">查看有关天气的更多信息,并始终显示当前日期。</string>
@ -318,9 +318,9 @@
<string name="action_about">关于</string> <string name="action_about">关于</string>
<string name="action_refresh_widget">刷新微件</string> <string name="action_refresh_widget">刷新微件</string>
<string name="toolbar_transition_name" translatable="false">toolbar</string> <string name="toolbar_transition_name" translatable="false">toolbar</string>
<string name="error_opening_uri">打开URL时遭遇出错:链接已复制到剪贴板。</string> <string name="error_opening_uri">打开URL出错链接已复制到剪贴板。</string>
<string name="loading_text">正在加载数据……</string> <string name="loading_text">正在加载数据……</string>
<string name="error_opening_app">打开应用出错。</string> <string name="error_opening_app">打开应用出错。</string>
<string name="default_name">默认应用</string> <string name="default_name">默认应用</string>
<string name="action_save">保存</string> <string name="action_save">保存</string>
<string name="settings_visible">可见</string> <string name="settings_visible">可见</string>
@ -344,17 +344,17 @@
<string name="settings_feedback_subtitle">欢迎为该项目贡献您的力量!</string> <string name="settings_feedback_subtitle">欢迎为该项目贡献您的力量!</string>
<string name="settings_feedback_title">反馈和功能请求</string> <string name="settings_feedback_title">反馈和功能请求</string>
<string name="xiaomi_manufacturer" translatable="false">xiaomi</string> <string name="xiaomi_manufacturer" translatable="false">xiaomi</string>
<string name="xiaomi_warning_title">检测到小米设备</string> <string name="xiaomi_warning_title">小米设备</string>
<string name="xiaomi_warning_message">请在应用设置的其他权限」中授予允许程序在后台运行时显示弹出窗口的权限,否则您将无法通过点击微件打开任何应用。</string> <string name="xiaomi_warning_message">请在应用设置的其他权限部分授予后台弹出界面的权限,否则您将无法通过点击微件打开任何应用。</string>
<string name="action_ignore">忽略</string> <string name="action_ignore">忽略</string>
<string name="action_grant_permission">授予权限</string> <string name="action_grant_permission">授予权限</string>
<string name="settings_title">设置</string> <string name="settings_title">设置</string>
<string name="style_header">风格</string> <string name="style_header">风格</string>
<string name="actions_header">动作</string> <string name="actions_header">动作</string>
<string name="provider_header"></string> <string name="provider_header">数据</string>
<string name="appearance_header">外观</string> <string name="appearance_header">外观</string>
<string name="preferences_header">偏好</string> <string name="preferences_header">偏好</string>
<string name="settings_privacy_policy_title"><![CDATA[法务与隐私]]></string> <string name="settings_privacy_policy_title">法务与隐私</string>
<string name="settings_privacy_policy_subtitle">查看应用的隐私策略</string> <string name="settings_privacy_policy_subtitle">查看应用的隐私策略</string>
<string name="project_header">开源项目</string> <string name="project_header">开源项目</string>
<string name="information_header">信息</string> <string name="information_header">信息</string>
@ -369,8 +369,8 @@
<string name="donation_breakfast">一顿英式早餐</string> <string name="donation_breakfast">一顿英式早餐</string>
<string name="donation_lunch">一顿快餐</string> <string name="donation_lunch">一顿快餐</string>
<string name="action_show_widget_preview">显示微件预览</string> <string name="action_show_widget_preview">显示微件预览</string>
<string name="support_dev_subtitle">这是一个独立开发者的项目,谢谢您的支持!</string> <string name="support_dev_subtitle">这是一个独立开发者的项目,\n谢谢您的支持!</string>
<string name="settings_title_integrations">整合</string> <string name="settings_title_integrations">集成</string>
<string name="label_count_installed_integrations">已安装%d个集成插件</string> <string name="label_count_installed_integrations">已安装%d个集成插件</string>
<string name="nothing"></string> <string name="nothing"></string>
<string name="action_dismiss">拒绝</string> <string name="action_dismiss">拒绝</string>
@ -380,13 +380,13 @@
<string name="song_info_format_activity_subtitle">更改曲目的可见信息</string> <string name="song_info_format_activity_subtitle">更改曲目的可见信息</string>
<!-- More --> <!-- More -->
<string name="look_and_feel_header"><![CDATA[外观与视觉]]></string> <string name="look_and_feel_header">界面外观</string>
<string name="typography_settings_title">文本</string> <string name="typography_settings_title">文本</string>
<string name="typography_settings_subtitle">设置文本字体、大小和颜色</string> <string name="typography_settings_subtitle">设置文本字体、大小和颜色</string>
<string name="layout_settings_title">布局</string> <string name="layout_settings_title">布局</string>
<string name="gestures_settings_subtitle">动作、行为与快捷方式</string> <string name="gestures_settings_subtitle">动作、行为与快捷方式</string>
<string name="gestures_settings_title">交互</string> <string name="gestures_settings_title">手势</string>
<string name="gestures_amp_input_header"><![CDATA[操作与交互]]></string> <string name="gestures_amp_input_header">手势与输入</string>
<string name="clock_settings_subtitle">设置大小、颜色与时区</string> <string name="clock_settings_subtitle">设置大小、颜色与时区</string>
<string name="layout_settings_subtitle">微件间距及其它微调</string> <string name="layout_settings_subtitle">微件间距及其它微调</string>
<string name="smart_content_header">智能内容</string> <string name="smart_content_header">智能内容</string>

View File

@ -7,7 +7,6 @@ buildscript {
jcenter() jcenter()
maven { url 'https://jitpack.io' } maven { url 'https://jitpack.io' }
mavenCentral() mavenCentral()
} }
dependencies { dependencies {
classpath 'com.android.tools.build:gradle:4.2.0' classpath 'com.android.tools.build:gradle:4.2.0'
@ -17,8 +16,6 @@ buildscript {
// Add the Crashlytics Gradle plugin. // Add the Crashlytics Gradle plugin.
classpath 'com.google.firebase:firebase-crashlytics-gradle:2.5.2' classpath 'com.google.firebase:firebase-crashlytics-gradle:2.5.2'
classpath 'io.realm:realm-gradle-plugin:10.4.0'
// NOTE: Do not place your application dependencies here; they belong // NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files // in the individual module build.gradle files
} }
@ -30,7 +27,6 @@ allprojects {
jcenter() jcenter()
maven { url 'https://jitpack.io' } maven { url 'https://jitpack.io' }
mavenCentral() mavenCentral()
} }
} }