diff --git a/app/src/main/java/io/github/wulkanowy/services/shortcuts/ShortcutsHelper.kt b/app/src/main/java/io/github/wulkanowy/services/shortcuts/ShortcutsHelper.kt index ee31af46..5e59aa54 100644 --- a/app/src/main/java/io/github/wulkanowy/services/shortcuts/ShortcutsHelper.kt +++ b/app/src/main/java/io/github/wulkanowy/services/shortcuts/ShortcutsHelper.kt @@ -15,79 +15,41 @@ import javax.inject.Singleton @Singleton class ShortcutsHelper @Inject constructor(@ApplicationContext private val context: Context) { - // Destination cannot be used here as shortcuts - // require their intents to only use primitive types (see PersistableBundle.isValidType). - - private val destinations = mapOf( - "grade" to Destination.Grade, - "attendance" to Destination.Attendance, - "exam" to Destination.Exam, - "timetable" to Destination.Timetable() - ) - - init { - initializeShortcuts() - } - - fun getDestination(intent: Intent) = - destinations[intent.getStringExtra(EXTRA_SHORTCUT_DESTINATION_ID)] - - private fun initializeShortcuts() { + fun initializeShortcuts() { val shortcutsInfo = listOf( ShortcutInfoCompat.Builder(context, "grade_shortcut") .setShortLabel(context.getString(R.string.grade_title)) .setLongLabel(context.getString(R.string.grade_title)) .setIcon(IconCompat.createWithResource(context, R.drawable.ic_shortcut_grade)) - .setIntent(SplashActivity.getStartIntent(context) - .apply { - action = Intent.ACTION_VIEW - putExtra(EXTRA_SHORTCUT_DESTINATION_ID, "grade") - } - ) + .setIntent(SplashActivity.getStartIntent(context, Destination.Grade) + .apply { action = Intent.ACTION_VIEW }) .build(), ShortcutInfoCompat.Builder(context, "attendance_shortcut") .setShortLabel(context.getString(R.string.attendance_title)) .setLongLabel(context.getString(R.string.attendance_title)) .setIcon(IconCompat.createWithResource(context, R.drawable.ic_shortcut_attendance)) - .setIntent(SplashActivity.getStartIntent(context) - .apply { - action = Intent.ACTION_VIEW - putExtra(EXTRA_SHORTCUT_DESTINATION_ID, "attendance") - } - ) + .setIntent(SplashActivity.getStartIntent(context, Destination.Attendance) + .apply { action = Intent.ACTION_VIEW }) .build(), ShortcutInfoCompat.Builder(context, "exam_shortcut") .setShortLabel(context.getString(R.string.exam_title)) .setLongLabel(context.getString(R.string.exam_title)) .setIcon(IconCompat.createWithResource(context, R.drawable.ic_shortcut_exam)) - .setIntent(SplashActivity.getStartIntent(context) - .apply { - action = Intent.ACTION_VIEW - putExtra(EXTRA_SHORTCUT_DESTINATION_ID, "exam") - } - ) + .setIntent(SplashActivity.getStartIntent(context, Destination.Exam) + .apply { action = Intent.ACTION_VIEW }) .build(), ShortcutInfoCompat.Builder(context, "timetable_shortcut") .setShortLabel(context.getString(R.string.timetable_title)) .setLongLabel(context.getString(R.string.timetable_title)) .setIcon(IconCompat.createWithResource(context, R.drawable.ic_shortcut_timetable)) - .setIntent(SplashActivity.getStartIntent(context) - .apply { - action = Intent.ACTION_VIEW - putExtra(EXTRA_SHORTCUT_DESTINATION_ID, "timetable") - } - ) + .setIntent(SplashActivity.getStartIntent(context, Destination.Timetable()) + .apply { action = Intent.ACTION_VIEW }) .build() ) shortcutsInfo.forEach { ShortcutManagerCompat.pushDynamicShortcut(context, it) } } - - private companion object { - - private const val EXTRA_SHORTCUT_DESTINATION_ID = "shortcut_destination_id" - } -} \ No newline at end of file +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/Destination.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/Destination.kt index 73a680cf..561419a0 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/Destination.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/Destination.kt @@ -1,6 +1,5 @@ package io.github.wulkanowy.ui.modules -import android.os.Parcelable import androidx.fragment.app.Fragment import io.github.wulkanowy.data.serializers.LocalDateSerializer import io.github.wulkanowy.ui.modules.attendance.AttendanceFragment @@ -16,12 +15,11 @@ import io.github.wulkanowy.ui.modules.note.NoteFragment import io.github.wulkanowy.ui.modules.schoolandteachers.school.SchoolFragment import io.github.wulkanowy.ui.modules.schoolannouncement.SchoolAnnouncementFragment import io.github.wulkanowy.ui.modules.timetable.TimetableFragment -import kotlinx.parcelize.Parcelize import kotlinx.serialization.Serializable import java.time.LocalDate @Serializable -sealed class Destination : Parcelable { +sealed class Destination { /* Type in children classes have to be as getter to avoid null in enums @@ -47,35 +45,30 @@ sealed class Destination : Parcelable { MESSAGE(Message); } - @Parcelize @Serializable object Dashboard : Destination() { override val destinationType get() = Type.DASHBOARD override val destinationFragment get() = DashboardFragment.newInstance() } - @Parcelize @Serializable object Grade : Destination() { override val destinationType get() = Type.GRADE override val destinationFragment get() = GradeFragment.newInstance() } - @Parcelize @Serializable object Attendance : Destination() { override val destinationType get() = Type.ATTENDANCE override val destinationFragment get() = AttendanceFragment.newInstance() } - @Parcelize @Serializable object Exam : Destination() { override val destinationType get() = Type.EXAM override val destinationFragment get() = ExamFragment.newInstance() } - @Parcelize @Serializable data class Timetable( @Serializable(with = LocalDateSerializer::class) @@ -85,56 +78,48 @@ sealed class Destination : Parcelable { override val destinationFragment get() = TimetableFragment.newInstance(date) } - @Parcelize @Serializable object Homework : Destination() { override val destinationType get() = Type.HOMEWORK override val destinationFragment get() = HomeworkFragment.newInstance() } - @Parcelize @Serializable object Note : Destination() { override val destinationType get() = Type.NOTE override val destinationFragment get() = NoteFragment.newInstance() } - @Parcelize @Serializable object Conference : Destination() { override val destinationType get() = Type.CONFERENCE override val destinationFragment get() = ConferenceFragment.newInstance() } - @Parcelize @Serializable object SchoolAnnouncement : Destination() { override val destinationType get() = Type.SCHOOL_ANNOUNCEMENT override val destinationFragment get() = SchoolAnnouncementFragment.newInstance() } - @Parcelize @Serializable object School : Destination() { override val destinationType get() = Type.SCHOOL override val destinationFragment get() = SchoolFragment.newInstance() } - @Parcelize @Serializable object LuckyNumber : Destination() { override val destinationType get() = Type.LUCKY_NUMBER override val destinationFragment get() = LuckyNumberFragment.newInstance() } - @Parcelize @Serializable object More : Destination() { override val destinationType get() = Type.MORE override val destinationFragment get() = MoreFragment.newInstance() } - @Parcelize @Serializable object Message : Destination() { override val destinationType get() = Type.MESSAGE diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/main/MainActivity.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/main/MainActivity.kt index b6d41e2c..1bfc8ba5 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/main/MainActivity.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/main/MainActivity.kt @@ -24,6 +24,8 @@ import io.github.wulkanowy.ui.base.BaseActivity import io.github.wulkanowy.ui.modules.Destination import io.github.wulkanowy.ui.modules.account.accountquick.AccountQuickDialog import io.github.wulkanowy.utils.* +import kotlinx.serialization.encodeToString +import kotlinx.serialization.json.Json import timber.log.Timber import javax.inject.Inject @@ -55,13 +57,13 @@ class MainActivity : BaseActivity(), MainVie companion object { - private const val EXTRA_START_DESTINATION = "start_destination" + private const val EXTRA_START_DESTINATION = "start_destination_json" fun getStartIntent( context: Context, destination: Destination? = null, ) = Intent(context, MainActivity::class.java).apply { - putExtra(EXTRA_START_DESTINATION, destination) + destination?.let { putExtra(EXTRA_START_DESTINATION, Json.encodeToString(it)) } } } @@ -70,9 +72,8 @@ class MainActivity : BaseActivity(), MainVie override val currentStackSize get() = navController.currentStack?.size override val currentViewTitle - get() = (navController.currentFrag as? MainView.TitledView)?.titleStringId?.let { - getString(it) - } + get() = (navController.currentFrag as? MainView.TitledView)?.titleStringId + ?.let { getString(it) } override val currentViewSubtitle get() = (navController.currentFrag as? MainView.TitledView)?.subtitleString @@ -86,7 +87,7 @@ class MainActivity : BaseActivity(), MainVie messageContainer = binding.mainMessageContainer updateHelper.messageContainer = binding.mainFragmentContainer - val destination = (intent.getParcelableExtra(EXTRA_START_DESTINATION) as Destination?) + val destination = intent.getStringExtra(EXTRA_START_DESTINATION) ?.takeIf { savedInstanceState == null } presenter.onAttachView(this, destination) diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/main/MainPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/main/MainPresenter.kt index cb414fcb..a07bdb37 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/main/MainPresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/main/MainPresenter.kt @@ -19,6 +19,8 @@ import io.github.wulkanowy.ui.modules.message.MessageView import io.github.wulkanowy.ui.modules.schoolandteachers.SchoolAndTeachersView import io.github.wulkanowy.ui.modules.studentinfo.StudentInfoView import io.github.wulkanowy.utils.AnalyticsHelper +import kotlinx.serialization.decodeFromString +import kotlinx.serialization.json.Json import timber.log.Timber import java.time.Duration import java.time.Instant @@ -30,6 +32,7 @@ class MainPresenter @Inject constructor( private val prefRepository: PreferencesRepository, private val syncManager: SyncManager, private val analytics: AnalyticsHelper, + private val json: Json ) : BasePresenter(errorHandler, studentRepository) { private var studentsWitSemesters: List? = null @@ -51,9 +54,11 @@ class MainPresenter @Inject constructor( else -> 4 } - fun onAttachView(view: MainView, initDestination: Destination?) { + fun onAttachView(view: MainView, initDestinationJson: String?) { super.onAttachView(view) + val initDestination: Destination? = initDestinationJson?.let { json.decodeFromString(it) } + val startMenuIndex = initDestination.startMenuIndex val destinations = rootDestinationTypeList.map { if (it == initDestination?.destinationType) initDestination else it.defaultDestination diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/splash/SplashActivity.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/splash/SplashActivity.kt index 24347e73..cfb62849 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/splash/SplashActivity.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/splash/SplashActivity.kt @@ -15,6 +15,8 @@ import io.github.wulkanowy.ui.modules.Destination import io.github.wulkanowy.ui.modules.login.LoginActivity import io.github.wulkanowy.ui.modules.main.MainActivity import io.github.wulkanowy.utils.openInternetBrowser +import kotlinx.serialization.encodeToString +import kotlinx.serialization.json.Json import javax.inject.Inject @SuppressLint("CustomSplashScreen") @@ -29,13 +31,13 @@ class SplashActivity : BaseActivity(), SplashView companion object { - private const val EXTRA_START_DESTINATION = "start_destination" + private const val EXTRA_START_DESTINATION = "start_destination_json" private const val EXTRA_EXTERNAL_URL = "external_url" fun getStartIntent(context: Context, destination: Destination? = null) = Intent(context, SplashActivity::class.java).apply { - putExtra(EXTRA_START_DESTINATION, destination) + destination?.let { putExtra(EXTRA_START_DESTINATION, Json.encodeToString(it)) } flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK } } @@ -43,12 +45,12 @@ class SplashActivity : BaseActivity(), SplashView override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) installSplashScreen().setKeepOnScreenCondition { true } + shortcutsHelper.initializeShortcuts() val externalLink = intent?.getStringExtra(EXTRA_EXTERNAL_URL) - val startDestination = intent?.getParcelableExtra(EXTRA_START_DESTINATION) as Destination? - ?: shortcutsHelper.getDestination(intent) + val startDestinationJson = intent?.getStringExtra(EXTRA_START_DESTINATION) - presenter.onAttachView(this, externalLink, startDestination) + presenter.onAttachView(this, externalLink, startDestinationJson) } override fun openLoginView() { diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/splash/SplashPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/splash/SplashPresenter.kt index 0b740902..767c885c 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/splash/SplashPresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/splash/SplashPresenter.kt @@ -5,16 +5,21 @@ import io.github.wulkanowy.ui.base.BasePresenter import io.github.wulkanowy.ui.base.ErrorHandler import io.github.wulkanowy.ui.modules.Destination import kotlinx.coroutines.launch +import kotlinx.serialization.decodeFromString +import kotlinx.serialization.json.Json import javax.inject.Inject class SplashPresenter @Inject constructor( errorHandler: ErrorHandler, studentRepository: StudentRepository, + private val json: Json ) : BasePresenter(errorHandler, studentRepository) { - fun onAttachView(view: SplashView, externalUrl: String?, startDestination: Destination?) { + fun onAttachView(view: SplashView, externalUrl: String?, startDestinationJson: String?) { super.onAttachView(view) + val startDestination: Destination? = startDestinationJson?.let { json.decodeFromString(it) } + if (!externalUrl.isNullOrBlank()) { view.openExternalUrlAndFinish(externalUrl) return diff --git a/app/src/test/java/io/github/wulkanowy/ui/modules/main/MainPresenterTest.kt b/app/src/test/java/io/github/wulkanowy/ui/modules/main/MainPresenterTest.kt index 7557d745..720239e6 100644 --- a/app/src/test/java/io/github/wulkanowy/ui/modules/main/MainPresenterTest.kt +++ b/app/src/test/java/io/github/wulkanowy/ui/modules/main/MainPresenterTest.kt @@ -5,13 +5,9 @@ import io.github.wulkanowy.data.repositories.StudentRepository import io.github.wulkanowy.services.sync.SyncManager import io.github.wulkanowy.ui.base.ErrorHandler import io.github.wulkanowy.utils.AnalyticsHelper -import io.mockk.MockKAnnotations -import io.mockk.Runs -import io.mockk.clearMocks -import io.mockk.every +import io.mockk.* import io.mockk.impl.annotations.MockK -import io.mockk.just -import io.mockk.verify +import kotlinx.serialization.json.Json import org.junit.Before import org.junit.Test @@ -43,8 +39,14 @@ class MainPresenterTest { clearMocks(mainView) every { mainView.initView(any(), any()) } just Runs - presenter = - MainPresenter(errorHandler, studentRepository, prefRepository, syncManager, analytics) + presenter = MainPresenter( + errorHandler = errorHandler, + studentRepository = studentRepository, + prefRepository = prefRepository, + syncManager = syncManager, + analytics = analytics, + json = Json + ) presenter.onAttachView(mainView, null) } diff --git a/app/src/test/java/io/github/wulkanowy/ui/modules/splash/SplashPresenterTest.kt b/app/src/test/java/io/github/wulkanowy/ui/modules/splash/SplashPresenterTest.kt index 32311974..eb362978 100644 --- a/app/src/test/java/io/github/wulkanowy/ui/modules/splash/SplashPresenterTest.kt +++ b/app/src/test/java/io/github/wulkanowy/ui/modules/splash/SplashPresenterTest.kt @@ -7,6 +7,7 @@ import io.mockk.MockKAnnotations import io.mockk.coEvery import io.mockk.impl.annotations.MockK import io.mockk.verify +import kotlinx.serialization.json.Json import org.junit.Before import org.junit.Rule import org.junit.Test @@ -30,7 +31,7 @@ class SplashPresenterTest { @Before fun setUp() { MockKAnnotations.init(this) - presenter = SplashPresenter(errorHandler, studentRepository) + presenter = SplashPresenter(errorHandler, studentRepository, Json) } @Test