Skip to content

Commit 9d77e10

Browse files
authored
Merge pull request #119 from oxters168/chat
Chat, Profiles, and basic Friend Management.
2 parents 45edf44 + 95865eb commit 9d77e10

File tree

79 files changed

+4309
-661
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

79 files changed

+4309
-661
lines changed

app/build.gradle.kts

+3-2
Original file line numberDiff line numberDiff line change
@@ -184,11 +184,12 @@ dependencies {
184184
// Support
185185
implementation(libs.androidx.core.ktx)
186186
implementation(libs.androidx.lifecycle.runtime.ktx)
187+
implementation(libs.apng)
188+
implementation(libs.datastore.preferences)
187189
implementation(libs.jetbrains.kotlinx.json)
188190
implementation(libs.kotlin.coroutines)
189-
implementation(libs.zxing)
190-
implementation(libs.datastore.preferences)
191191
implementation(libs.timber)
192+
implementation(libs.zxing)
192193

193194
// Google Protobufs
194195
implementation(libs.protobuf.java)

app/src/main/AndroidManifest.xml

+6-2
Original file line numberDiff line numberDiff line change
@@ -24,11 +24,15 @@
2424
android:label="@string/app_name"
2525
android:roundIcon="${roundIcon}"
2626
android:supportsRtl="true"
27-
android:theme="@style/Theme.Pluvia"
28-
android:windowSoftInputMode="adjustResize">
27+
android:theme="@style/Theme.Pluvia">
28+
<!--
29+
android:windowSoftInputMode must always be in the activity block.
30+
IME padding with composables like 'Scaffold' will have terrible resizing consequences, if in the activity tag.
31+
-->
2932
<activity
3033
android:name=".MainActivity"
3134
android:exported="true"
35+
android:windowSoftInputMode="adjustResize"
3236
android:theme="@style/Theme.Pluvia">
3337
<intent-filter>
3438
<action android:name="android.intent.action.MAIN" />

app/src/main/java/com/OxGames/Pluvia/Constants.kt

+12-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import androidx.compose.ui.unit.dp
44

55
/**
66
* Constants values that may be used around the app more than once.
7-
* Constants that are used in composables and or viewmodels should be here too.
7+
* Constants that are used in composables and or view models should be here too.
88
*/
99
object Constants {
1010

@@ -20,6 +20,17 @@ object Constants {
2020
object Persona {
2121
const val AVATAR_BASE_URL = "https://steamcdn-a.akamaihd.net/steamcommunity/public/images/avatars/"
2222
const val MISSING_AVATAR_URL = "${AVATAR_BASE_URL}fe/fef49e7fa7e1997310d705b2a6158ff8dc1cdfeb_full.jpg"
23+
const val PROFILE_URL = "https://steamcommunity.com/profiles/"
24+
}
25+
26+
object Chat {
27+
const val EMOTICON_URL = "https://steamcommunity-a.akamaihd.net/economy/emoticonlarge/"
28+
const val STICKER_URL = "https://steamcommunity-a.akamaihd.net/economy/sticker/"
29+
}
30+
31+
object Library {
32+
const val ICON_URL = "https://cdn.cloudflare.steamstatic.com/steamcommunity/public/images/apps/"
33+
const val STORE_URL = "https://store.steampowered.com/app/"
2334
}
2435

2536
object Misc {

app/src/main/java/com/OxGames/Pluvia/CrashHandler.kt

+5
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,11 @@ import java.text.SimpleDateFormat
88
import java.util.Date
99
import java.util.Locale
1010

11+
/**
12+
* A local running crash handler.
13+
* Any uncaught exceptions will be saved located locally in a text file, aka: Crash Report.
14+
* File location: /<user storage>/Android/data/com.OxGames.Pluvia/files/crash_logs/
15+
*/
1116
class CrashHandler(
1217
private val context: Context,
1318
private val defaultHandler: Thread.UncaughtExceptionHandler?,

app/src/main/java/com/OxGames/Pluvia/MainActivity.kt

+2
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ import com.OxGames.Pluvia.events.AndroidEvent
3131
import com.OxGames.Pluvia.service.SteamService
3232
import com.OxGames.Pluvia.ui.PluviaMain
3333
import com.OxGames.Pluvia.ui.enums.Orientation
34+
import com.OxGames.Pluvia.utils.AnimatedPngDecoder
3435
import com.OxGames.Pluvia.utils.IconDecoder
3536
import com.skydoves.landscapist.coil.LocalCoilImageLoader
3637
import com.winlator.core.AppUtils
@@ -118,6 +119,7 @@ class MainActivity : ComponentActivity() {
118119
.diskCache(diskCache)
119120
.components {
120121
add(IconDecoder.Factory())
122+
add(AnimatedPngDecoder.Factory())
121123
}
122124
.logger(logger)
123125
.build()

app/src/main/java/com/OxGames/Pluvia/PrefManager.kt

+32-1
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import androidx.datastore.preferences.core.stringPreferencesKey
1313
import androidx.datastore.preferences.preferencesDataStore
1414
import com.OxGames.Pluvia.enums.AppTheme
1515
import com.OxGames.Pluvia.service.SteamService
16+
import com.OxGames.Pluvia.ui.enums.HomeDestination
1617
import com.OxGames.Pluvia.ui.enums.Orientation
1718
import com.materialkolor.PaletteStyle
1819
import com.winlator.box86_64.Box86_64Preset
@@ -26,10 +27,12 @@ import kotlinx.coroutines.Dispatchers
2627
import kotlinx.coroutines.flow.first
2728
import kotlinx.coroutines.launch
2829
import kotlinx.coroutines.runBlocking
30+
import kotlinx.serialization.json.Json
2931
import timber.log.Timber
3032

3133
/**
32-
* Kind of ugly, but works to be a universal preference manager.
34+
* A universal Preference Manager that can be used anywhere within Pluvia.
35+
* Note: King of ugly though.
3336
*/
3437
object PrefManager {
3538

@@ -371,4 +374,32 @@ object PrefManager {
371374
set(value) {
372375
setPref(APP_THEME_PALETTE, value.ordinal)
373376
}
377+
378+
private val START_SCREEN = intPreferencesKey("start screen")
379+
var startScreen: HomeDestination
380+
get() {
381+
val value = getPref(START_SCREEN, HomeDestination.Library.ordinal)
382+
return HomeDestination.entries.getOrNull(value) ?: HomeDestination.Library
383+
}
384+
set(value) {
385+
setPref(START_SCREEN, value.ordinal)
386+
}
387+
388+
private val FRIENDS_LIST_HEADER = stringPreferencesKey("friends_list_header")
389+
var friendsListHeader: Set<String>
390+
get() {
391+
val value = getPref(FRIENDS_LIST_HEADER, "[]")
392+
return Json.decodeFromString<Set<String>>(value)
393+
}
394+
set(value) {
395+
setPref(FRIENDS_LIST_HEADER, Json.encodeToString(value))
396+
}
397+
398+
// NOTE: This should be removed once chat is considered stable.
399+
private val ACK_CHAT_PREVIEW = booleanPreferencesKey("ack_chat_preview")
400+
var ackChatPreview: Boolean
401+
get() = getPref(ACK_CHAT_PREVIEW, false)
402+
set(value) {
403+
setPref(ACK_CHAT_PREVIEW, value)
404+
}
374405
}

app/src/main/java/com/OxGames/Pluvia/ReleaseTree.kt

+6-3
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,13 @@ package com.OxGames.Pluvia
33
import android.util.Log
44
import timber.log.Timber
55

6+
/**
7+
* A log manager instance for release mode.
8+
* Debug mode uses [timber.log.Timber.DebugTree]
9+
*/
610
class ReleaseTree : Timber.Tree() {
7-
override fun isLoggable(tag: String?, priority: Int): Boolean {
8-
return priority >= Log.INFO // Ignore Verbose and Debug logs.
9-
}
11+
12+
override fun isLoggable(tag: String?, priority: Int): Boolean = priority >= Log.INFO // Ignore Verbose and Debug logs.
1013

1114
override fun log(priority: Int, tag: String?, message: String, t: Throwable?) {
1215
if (!isLoggable(tag, priority)) {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
package com.OxGames.Pluvia.data
2+
3+
import androidx.room.Entity
4+
import androidx.room.PrimaryKey
5+
6+
@Entity(tableName = "emoticon")
7+
data class Emoticon(@PrimaryKey val name: String, val appID: Int, val isSticker: Boolean)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package com.OxGames.Pluvia.data
2+
3+
import androidx.room.ColumnInfo
4+
import androidx.room.Entity
5+
import androidx.room.PrimaryKey
6+
7+
@Entity("chat_message")
8+
data class FriendMessage(
9+
@PrimaryKey(autoGenerate = true)
10+
@ColumnInfo(name = "id") val id: Long = 0L,
11+
@ColumnInfo(name = "steam_id_friend") val steamIDFriend: Long,
12+
@ColumnInfo(name = "from_local") val fromLocal: Boolean,
13+
@ColumnInfo(name = "message") val message: String,
14+
@ColumnInfo(name = "timestamp") val timestamp: Int,
15+
)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
package com.OxGames.Pluvia.data
2+
3+
/**
4+
* Data class for the Library list
5+
*/
6+
data class LibraryItem(
7+
val index: Int = 0,
8+
val appId: Int = 0,
9+
val name: String = "",
10+
val iconHash: String = "",
11+
) {
12+
val clientIconUrl: String
13+
get() = "https://steamcdn-a.akamaihd.net/steamcommunity/public/images/apps/$appId/$iconHash.ico"
14+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
package com.OxGames.Pluvia.data
2+
3+
data class OwnedGames(
4+
val appId: Int = 0,
5+
val name: String = "",
6+
val playtimeTwoWeeks: Int = 0,
7+
val playtimeForever: Int = 0,
8+
val imgIconUrl: String = "",
9+
val sortAs: String? = null,
10+
)

app/src/main/java/com/OxGames/Pluvia/data/SteamFriend.kt

+19
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import androidx.room.Entity
1313
import androidx.room.PrimaryKey
1414
import com.OxGames.Pluvia.ui.component.icons.VR
1515
import com.OxGames.Pluvia.ui.theme.friendAwayOrSnooze
16+
import com.OxGames.Pluvia.ui.theme.friendBlocked
1617
import com.OxGames.Pluvia.ui.theme.friendInGame
1718
import com.OxGames.Pluvia.ui.theme.friendInGameAwayOrSnooze
1819
import com.OxGames.Pluvia.ui.theme.friendOffline
@@ -69,6 +70,12 @@ data class SteamFriend(
6970
val clanTag: String = "",
7071
@ColumnInfo("online_session_instances")
7172
val onlineSessionInstances: Int = 0,
73+
74+
// Chat message
75+
@ColumnInfo("chat_entry_type")
76+
val isTyping: Boolean = false,
77+
@ColumnInfo("unread_messages")
78+
val unreadMessageCount: Int = 0,
7279
) {
7380
val isOnline: Boolean
7481
get() = (state.code() in 1..6)
@@ -82,6 +89,17 @@ data class SteamFriend(
8289
val isPlayingGame: Boolean
8390
get() = if (isOnline) gameAppID > 0 || gameName.isEmpty().not() else false
8491

92+
val isPlayingGameName: String
93+
get() = if (isPlayingGame) {
94+
gameName.ifEmpty { "Playing game id: $gameAppID" }
95+
} else {
96+
if (isBlocked) {
97+
relation.name
98+
} else {
99+
state.name
100+
}
101+
}
102+
85103
val isAwayOrSnooze: Boolean
86104
get() = state.let {
87105
it == EPersonaState.Away || it == EPersonaState.Snooze || it == EPersonaState.Busy
@@ -103,6 +121,7 @@ data class SteamFriend(
103121

104122
val statusColor: Color
105123
get() = when {
124+
isBlocked -> friendBlocked
106125
isOffline -> friendOffline
107126
isInGameAwayOrSnooze -> friendInGameAwayOrSnooze
108127
isAwayOrSnooze -> friendAwayOrSnooze

app/src/main/java/com/OxGames/Pluvia/db/PluviaDatabase.kt

+10-2
Original file line numberDiff line numberDiff line change
@@ -4,21 +4,25 @@ import androidx.room.Database
44
import androidx.room.RoomDatabase
55
import androidx.room.TypeConverters
66
import com.OxGames.Pluvia.data.ChangeNumbers
7+
import com.OxGames.Pluvia.data.Emoticon
78
import com.OxGames.Pluvia.data.FileChangeLists
9+
import com.OxGames.Pluvia.data.FriendMessage
810
import com.OxGames.Pluvia.data.SteamFriend
911
import com.OxGames.Pluvia.db.converters.ByteArrayConverter
1012
import com.OxGames.Pluvia.db.converters.FriendConverter
1113
import com.OxGames.Pluvia.db.converters.PathTypeConverter
1214
import com.OxGames.Pluvia.db.converters.UserFileInfoListConverter
1315
import com.OxGames.Pluvia.db.dao.ChangeNumbersDao
16+
import com.OxGames.Pluvia.db.dao.EmoticonDao
1417
import com.OxGames.Pluvia.db.dao.FileChangeListsDao
18+
import com.OxGames.Pluvia.db.dao.FriendMessagesDao
1519
import com.OxGames.Pluvia.db.dao.SteamFriendDao
1620

1721
const val DATABASE_NAME = "pluvia.db"
1822

1923
@Database(
20-
entities = [SteamFriend::class, ChangeNumbers::class, FileChangeLists::class],
21-
version = 1,
24+
entities = [SteamFriend::class, ChangeNumbers::class, FileChangeLists::class, FriendMessage::class, Emoticon::class],
25+
version = 2,
2226
exportSchema = false, // Should export once stable.
2327
)
2428
@TypeConverters(
@@ -34,4 +38,8 @@ abstract class PluviaDatabase : RoomDatabase() {
3438
abstract fun appChangeNumbersDao(): ChangeNumbersDao
3539

3640
abstract fun appFileChangeListsDao(): FileChangeListsDao
41+
42+
abstract fun friendMessagesDao(): FriendMessagesDao
43+
44+
abstract fun emoticonDao(): EmoticonDao
3745
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
package com.OxGames.Pluvia.db.dao
2+
3+
import androidx.room.Dao
4+
import androidx.room.Insert
5+
import androidx.room.OnConflictStrategy
6+
import androidx.room.Query
7+
import androidx.room.Transaction
8+
import com.OxGames.Pluvia.data.Emoticon
9+
import kotlinx.coroutines.flow.Flow
10+
11+
@Dao
12+
interface EmoticonDao {
13+
14+
@Insert(onConflict = OnConflictStrategy.REPLACE)
15+
suspend fun insertAll(emoticons: List<Emoticon>)
16+
17+
@Query("SELECT * FROM emoticon ORDER BY isSticker DESC, appID DESC, name DESC")
18+
fun getAll(): Flow<List<Emoticon>>
19+
20+
@Query("SELECT * FROM emoticon ORDER BY isSticker DESC, appID DESC, name DESC")
21+
fun getAllAsList(): List<Emoticon>
22+
23+
@Query("DELETE FROM emoticon")
24+
suspend fun deleteAll()
25+
26+
@Transaction
27+
suspend fun replaceAll(emoticons: List<Emoticon>) {
28+
deleteAll()
29+
insertAll(emoticons)
30+
}
31+
32+
@Query("SELECT COUNT(*) FROM emoticon")
33+
fun getCount(): Flow<Int>
34+
35+
@Query("SELECT * FROM emoticon WHERE isSticker = :isSticker ORDER BY name ASC")
36+
fun getByType(isSticker: Boolean): Flow<List<Emoticon>>
37+
}

0 commit comments

Comments
 (0)