Skip to content

Commit 2757098

Browse files
authored
🚀 release: promote develop to main for v1.0.4-beta.1
2 parents e85695e + ccc99ce commit 2757098

67 files changed

Lines changed: 4715 additions & 1606 deletions

Some content is hidden

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

README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ Room // Надёжная и типобезопасная просл
7171
- [x] Виджеты на главный экран
7272
- [x] Темы
7373
- [x] Интеграция новостей
74+
- [x] Личные кабинеты
7475

7576
## 💚 Стань садовником проекта
7677

@@ -79,7 +80,7 @@ Room // Надёжная и типобезопасная просл
7980
3. **Дизайнер экосистемы**[Написать](https://t.me/whatrushik)
8081

8182
```text
82-
MIT License · © 2026 whatrushik
83+
MIT License · © 2024 whatrushik
8384
Свободно расти и процветать 🌍
8485
```
8586

app/build.gradle.kts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ android {
2929
minSdk = 26
3030
targetSdk = 36
3131
versionCode = generateVersionCode()
32-
versionName = "1.0.3"
32+
versionName = "1.0.4-beta.1"
3333

3434
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
3535
vectorDrawables {
@@ -106,7 +106,6 @@ dependencies {
106106
implementation(project(":core:foundation"))
107107
implementation(project(":core:navigation"))
108108

109-
110109
ksp(libs.room.compiler)
111110

112111
implementation(platform(libs.firebase.bom))
@@ -118,6 +117,7 @@ dependencies {
118117
implementation(libs.bundles.room)
119118
implementation(libs.bundles.coil)
120119

120+
implementation(libs.qrose)
121121
implementation(libs.poi)
122122
implementation(libs.poi.ooxml)
123123
implementation(libs.process.phoenix)

app/src/main/java/app/what/schedule/CrashActivity.kt

Lines changed: 39 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,27 @@ import android.widget.Toast
88
import androidx.activity.ComponentActivity
99
import androidx.activity.compose.setContent
1010
import androidx.activity.enableEdgeToEdge
11+
import androidx.compose.foundation.layout.Box
12+
import androidx.compose.foundation.layout.padding
13+
import androidx.compose.foundation.layout.systemBarsPadding
14+
import androidx.compose.material3.FloatingActionButton
15+
import androidx.compose.material3.MaterialTheme.colorScheme
16+
import androidx.compose.ui.Alignment
17+
import androidx.compose.ui.Modifier
18+
import androidx.compose.ui.unit.dp
19+
import androidx.compose.ui.zIndex
1120
import androidx.core.content.FileProvider
1221
import app.what.foundation.services.AppLogger.Companion.Auditor
1322
import app.what.foundation.services.crash.CrashScreen
23+
import app.what.foundation.ui.Show
24+
import app.what.foundation.ui.controllers.rememberDialogController
1425
import app.what.foundation.utils.ShareUtils
26+
import app.what.navigation.core.ProvideGlobalDialog
1527
import app.what.schedule.data.local.settings.AppValues
28+
import app.what.schedule.features.dev.DevFeature
1629
import app.what.schedule.ui.theme.AppTheme
30+
import app.what.schedule.ui.theme.icons.WHATIcons
31+
import app.what.schedule.ui.theme.icons.filled.FrameBug
1732
import org.koin.compose.koinInject
1833
import java.io.File
1934

@@ -31,11 +46,30 @@ class CrashActivity : ComponentActivity() {
3146
}
3247

3348
AppTheme(koinInject<AppValues>()) {
34-
CrashScreen(
35-
crashReport = intent.getStringExtra("CRASH_REPORT") ?: "",
36-
onRestart = { restartApp() },
37-
onShare = { shareCrashReport() }
38-
)
49+
ProvideGlobalDialog {
50+
val dialog = rememberDialogController()
51+
52+
Box {
53+
FloatingActionButton(
54+
modifier = Modifier
55+
.align(Alignment.BottomEnd)
56+
.zIndex(2f)
57+
.systemBarsPadding()
58+
.padding(bottom = 20.dp, end = 20.dp),
59+
onClick = {
60+
dialog.open(full = true) { DevFeature() }
61+
}
62+
) {
63+
WHATIcons.FrameBug.Show(color = colorScheme.onSecondaryContainer)
64+
}
65+
66+
CrashScreen(
67+
crashReport = intent.getStringExtra("CRASH_REPORT") ?: "",
68+
onRestart = { restartApp() },
69+
onShare = { shareCrashReport() }
70+
)
71+
}
72+
}
3973
}
4074
}
4175
}

app/src/main/java/app/what/schedule/ScheduleApp.kt

Lines changed: 28 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
package app.what.schedule
22

33
import android.app.Application
4-
import android.widget.Toast
54
import androidx.room.Room
5+
import app.what.foundation.data.settings.PreferenceStorage
66
import app.what.foundation.services.AppLogger
77
import app.what.foundation.services.AppLogger.Companion.Auditor
88
import app.what.foundation.services.auto_update.AppUpdateManager
@@ -16,9 +16,11 @@ import app.what.foundation.services.crash.CrashHandler
1616
import app.what.schedule.data.local.database.AppDatabase
1717
import app.what.schedule.data.local.settings.AppValues
1818
import app.what.schedule.data.remote.api.InstitutionManager
19+
import app.what.schedule.data.remote.providers.dgtu.services.DGTUAccountService
1920
import app.what.schedule.domain.NewsRepository
2021
import app.what.schedule.domain.ScheduleRepository
2122
import app.what.schedule.features.dev.presentation.NetworkMonitorPlugin
23+
import app.what.schedule.features.insts.dgtu.domain.DgtuController
2224
import app.what.schedule.features.main.domain.MainController
2325
import app.what.schedule.features.news.domain.NewsController
2426
import app.what.schedule.features.newsDetail.domain.NewsDetailController
@@ -53,6 +55,8 @@ import kotlinx.serialization.json.Json
5355
import org.koin.android.ext.android.getKoin
5456
import org.koin.android.ext.koin.androidContext
5557
import org.koin.core.context.startKoin
58+
import org.koin.core.module.dsl.singleOf
59+
import org.koin.dsl.bind
5660
import org.koin.dsl.module
5761
import java.security.cert.X509Certificate
5862
import java.util.UUID
@@ -107,29 +111,37 @@ class ScheduleApp : Application() {
107111
val source = getInstallSource(this)
108112

109113
when (source) {
110-
InstallSource.APK -> Auditor.debug("d", "Apk")
111-
InstallSource.RuStore -> Auditor.debug("d", "RuStore")
114+
InstallSource.APK -> Auditor.debug("d", "install source Apk")
115+
InstallSource.RuStore -> Auditor.debug("d", "install source RuStore")
112116
}
113117
}
114118
}
115119

116120
val controllers = module {
117-
single<SettingsController> { SettingsController(get(), get()) }
118-
single<NewsController> { NewsController(get(), get()) }
121+
singleOf(::SettingsController)
122+
singleOf(::NewsController)
123+
singleOf(::ScheduleController)
124+
singleOf(::OnboardingController)
125+
singleOf(::MainController)
126+
singleOf(::DgtuController)
119127
factory<NewsDetailController> { params -> NewsDetailController(params.get(), get()) }
120-
single<ScheduleController> { ScheduleController(get(), get()) }
121-
single<OnboardingController> { OnboardingController(get(), get()) }
122-
single<MainController> { MainController() }
123128
}
124129

125130
val generalModule = module {
126131
single<CoroutineScope> { CoroutineScope(SupervisorJob() + Dispatchers.Main.immediate) }
127-
single { AppValues(get()) }
128-
single { AppUtils(get()) }
129-
single { GoogleDriveParser(get()) }
130-
single { FileManager(get()) }
131-
single<AppUpdateManager> {
132132

133+
singleOf(::AppValues) bind PreferenceStorage::class
134+
singleOf(::AppUtils)
135+
singleOf(::GoogleDriveParser)
136+
singleOf(::FileManager)
137+
138+
singleOf(::DGTUAccountService)
139+
140+
singleOf(::InstitutionManager)
141+
singleOf(::ScheduleRepository)
142+
singleOf(::NewsRepository)
143+
144+
single<AppUpdateManager> {
133145
val context = androidContext()
134146
val source = getInstallSource(context)
135147

@@ -149,11 +161,6 @@ val generalModule = module {
149161
}
150162
}
151163

152-
153-
single { InstitutionManager(get(), get()) }
154-
single { ScheduleRepository(get(), get(), get()) }
155-
single { NewsRepository(get(), get()) }
156-
157164
single {
158165
Room.databaseBuilder(
159166
androidContext(),
@@ -178,10 +185,11 @@ val generalModule = module {
178185

179186
install(ContentNegotiation) {
180187
json(Json {
181-
ignoreUnknownKeys = true
182-
isLenient = true
183188
classDiscriminator = "type"
189+
ignoreUnknownKeys = true
184190
prettyPrint = true
191+
isLenient = true
192+
explicitNulls = false
185193
})
186194
}
187195

app/src/main/java/app/what/schedule/data/local/settings/AppValues.kt

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,6 @@ import app.what.schedule.data.remote.api.models.ScheduleSearch
1111
import kotlinx.serialization.Serializable
1212
import kotlinx.serialization.builtins.serializer
1313

14-
@Serializable
15-
class ScheduleProvider(
16-
val inst: String,
17-
val filial: String,
18-
val provider: String
19-
)
20-
2114
@Serializable
2215
enum class ThemeType(override val displayName: String) : Named {
2316
Light("Светлая"),
@@ -115,5 +108,18 @@ class AppValues(context: Context) : PreferenceStorage(
115108
"debug_mode", false, Boolean.serializer(),
116109
"Режим отладки", "Включение дополнительной информации для разработки"
117110
)
111+
112+
// DGTU ---------------
113+
val dgtuToken = createValue(
114+
"dgtu_token",
115+
null,
116+
String.serializer(),
117+
)
118+
119+
val dgtuStudentId = createValue(
120+
"dgtu_userId",
121+
null,
122+
Int.serializer(),
123+
)
118124
}
119125

app/src/main/java/app/what/schedule/data/remote/api/ScheduleApi.kt

Lines changed: 67 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,17 @@
11
package app.what.schedule.data.remote.api
22

3+
import app.what.foundation.core.Feature
4+
import app.what.foundation.core.UIComponent
35
import app.what.schedule.data.local.settings.AppValues
46
import app.what.schedule.data.remote.api.models.DaySchedule
57
import app.what.schedule.data.remote.api.models.Group
68
import app.what.schedule.data.remote.api.models.NewItem
79
import app.what.schedule.data.remote.api.models.NewListItem
810
import app.what.schedule.data.remote.api.models.Teacher
9-
import app.what.schedule.data.remote.providers.DGTU
10-
import app.what.schedule.data.remote.providers.IUBIP
11-
import app.what.schedule.data.remote.providers.RINH
12-
import app.what.schedule.data.remote.providers.RKSI
11+
import app.what.schedule.data.remote.providers.dgtu.DGTU
12+
import app.what.schedule.data.remote.providers.iubip.IUBIP
13+
import app.what.schedule.data.remote.providers.rinh.RINH
14+
import app.what.schedule.data.remote.providers.rksi.RKSI
1315
import kotlinx.coroutines.CoroutineScope
1416
import java.time.LocalDateTime
1517

@@ -54,6 +56,47 @@ sealed interface ScheduleResponse {
5456
}
5557
}
5658

59+
fun List<ScheduleResponse>.sum(): ScheduleResponse {
60+
if (isEmpty()) return ScheduleResponse.Empty
61+
62+
val allSchedules = mutableListOf<DaySchedule>()
63+
var latestModified: LocalDateTime? = null
64+
65+
for (response in this) {
66+
when (response) {
67+
ScheduleResponse.Empty -> {
68+
69+
}
70+
71+
ScheduleResponse.UpToDate -> {
72+
73+
}
74+
75+
is ScheduleResponse.Available -> {
76+
allSchedules.addAll(response.schedules)
77+
if (latestModified == null || response.lastModified > latestModified) {
78+
latestModified = response.lastModified
79+
}
80+
}
81+
}
82+
}
83+
84+
if (allSchedules.isEmpty()) {
85+
return ScheduleResponse.Empty
86+
}
87+
88+
val hasFromSource = this.any { it is ScheduleResponse.Available.FromSource }
89+
val hasFromCache = this.any { it is ScheduleResponse.Available.FromCache }
90+
91+
val lastModified = latestModified ?: LocalDateTime.now()
92+
93+
return when {
94+
hasFromSource -> ScheduleResponse.Available.FromSource(allSchedules, lastModified)
95+
hasFromCache -> ScheduleResponse.Available.FromCache(allSchedules, lastModified)
96+
else -> ScheduleResponse.Available.FromSource(allSchedules, lastModified)
97+
}
98+
}
99+
57100

58101
typealias AdditionalData = Map<String, Any?>
59102

@@ -65,6 +108,20 @@ interface Institution {
65108

66109
val metadata: MetaInfo
67110

111+
val scheduleService: ScheduleService
112+
val newsService: NewsService
113+
114+
val accountFeature: Feature<*, *>?
115+
116+
fun generateFileName(
117+
additional: AdditionalData,
118+
fileExtension: String
119+
) = "${metadata.id}_${
120+
additional.map { (k, v) -> "$k&$v" }.joinToString("_")
121+
}.$fileExtension"
122+
}
123+
124+
interface ScheduleService {
68125
suspend fun getGroupSchedule(
69126
group: String,
70127
showReplacements: Boolean = false,
@@ -79,20 +136,20 @@ interface Institution {
79136

80137
suspend fun getGroups(): List<Group>
81138
suspend fun getTeachers(): List<Teacher>
139+
}
82140

141+
interface NewsService {
83142
suspend fun getNews(page: Int): List<NewListItem> = emptyList()
84143
suspend fun getNewDetail(id: String): NewItem {
85144
error("Not implemented")
86145
}
146+
}
87147

88-
fun generateFileName(
89-
additional: AdditionalData,
90-
fileExtension: String
91-
) = "${metadata.id}_${
92-
additional.map { (k, v) -> "$k&$v" }.joinToString("_")
93-
}.$fileExtension"
148+
interface AccountService {
149+
val ui: UIComponent
94150
}
95151

152+
96153
val insts: List<Institution.Factory> by lazy {
97154
listOf(
98155
RKSI.Factory,

0 commit comments

Comments
 (0)