diff --git a/app/build.gradle.kts b/app/build.gradle.kts
index c77a4b604..8dff34dd3 100644
--- a/app/build.gradle.kts
+++ b/app/build.gradle.kts
@@ -135,6 +135,7 @@ dependencies {
implementation(projects.core.accounts)
implementation(projects.core.common)
implementation(projects.core.database)
+ implementation(projects.core.navigation)
implementation(projects.core.network)
implementation(projects.core.preferences)
diff --git a/app/lint-baseline.xml b/app/lint-baseline.xml
index 5bbfd9ed1..d7d98cce9 100644
--- a/app/lint-baseline.xml
+++ b/app/lint-baseline.xml
@@ -872,7 +872,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
@@ -2049,7 +2049,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
@@ -2060,7 +2060,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
@@ -2071,7 +2071,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
@@ -2082,7 +2082,7 @@
errorLine2=" ~~~~~~~">
@@ -2093,7 +2093,7 @@
errorLine2=" ~~~~~~~">
@@ -2104,7 +2104,7 @@
errorLine2=" ~~~~~~~">
@@ -2115,7 +2115,7 @@
errorLine2=" ~~~~~~~">
@@ -2126,7 +2126,7 @@
errorLine2=" ~~~~~~~">
@@ -2137,7 +2137,7 @@
errorLine2=" ~~~~~~~">
@@ -2148,7 +2148,7 @@
errorLine2=" ~~~~~~~~~~~~~">
@@ -2159,7 +2159,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~">
@@ -2170,7 +2170,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~">
@@ -2335,7 +2335,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~">
@@ -2346,7 +2346,7 @@
errorLine2=" ~~~~~~~~~">
@@ -2357,7 +2357,7 @@
errorLine2=" ~~~~~~~~~">
@@ -2445,7 +2445,7 @@
errorLine2=" ~~~~~~~~~">
@@ -2456,7 +2456,7 @@
errorLine2=" ~~~~~~~~~">
@@ -2467,7 +2467,7 @@
errorLine2=" ~~~~~~~~~">
@@ -2478,7 +2478,7 @@
errorLine2=" ~~~~~~~~~">
@@ -2731,7 +2731,7 @@
errorLine2=" ~~~~~~~~~~~~~~">
@@ -2742,7 +2742,7 @@
errorLine2=" ~~~~~~">
@@ -2775,7 +2775,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~">
@@ -2786,7 +2786,7 @@
errorLine2=" ~~~~~~~">
@@ -2797,7 +2797,7 @@
errorLine2=" ~~~~~~~">
@@ -2808,7 +2808,7 @@
errorLine2=" ~~~~~~~~~~">
@@ -2819,7 +2819,7 @@
errorLine2=" ~~~~~~~~~~">
@@ -2830,7 +2830,7 @@
errorLine2=" ~~~~~~~~~~">
@@ -2841,7 +2841,7 @@
errorLine2=" ~~~~~~~~~~">
@@ -2852,7 +2852,7 @@
errorLine2=" ~~~~~~~~~~">
@@ -2863,7 +2863,7 @@
errorLine2=" ~~~~~~~~~~">
@@ -2874,7 +2874,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~">
@@ -2885,7 +2885,7 @@
errorLine2=" ~~~~~~~~~~~~~">
@@ -2896,7 +2896,7 @@
errorLine2=" ~~~~~~~">
@@ -2907,7 +2907,7 @@
errorLine2=" ~~~~~~~">
@@ -2918,7 +2918,7 @@
errorLine2=" ~~~~~~~">
@@ -2929,7 +2929,7 @@
errorLine2=" ~~~~~~~">
@@ -2940,7 +2940,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~">
@@ -2951,7 +2951,7 @@
errorLine2=" ~~~~~~~">
@@ -2962,7 +2962,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~">
@@ -2973,7 +2973,7 @@
errorLine2=" ~~~~~~~">
@@ -2984,7 +2984,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~">
@@ -2995,7 +2995,7 @@
errorLine2=" ~~~~~~~">
@@ -3006,7 +3006,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~">
@@ -3017,29 +3017,7 @@
errorLine2=" ~~~~~~~">
-
-
-
-
-
-
-
-
@@ -3072,7 +3050,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~">
@@ -3083,7 +3061,7 @@
errorLine2=" ~~~~~~~">
@@ -3105,18 +3083,18 @@
errorLine2=" ~~~~~~~">
+ message="Access to `private` method `primaryDrawerItem` of class `MainActivityKt` requires synthetic accessor"
+ errorLine1=" primaryDrawerItem {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~">
@@ -3127,7 +3105,7 @@
errorLine2=" ~~~~~~~">
@@ -3138,7 +3116,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~">
@@ -3149,7 +3127,7 @@
errorLine2=" ~~~~~~~">
@@ -3160,7 +3138,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~">
@@ -3171,7 +3149,7 @@
errorLine2=" ~~~~~~~">
@@ -3182,7 +3160,29 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~">
+
+
+
+
+
+
+
+
@@ -3193,7 +3193,7 @@
errorLine2=" ~~~~~~~">
@@ -3204,7 +3204,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~">
@@ -3215,7 +3215,7 @@
errorLine2=" ~~~~~~~">
@@ -3226,7 +3226,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~">
@@ -3237,7 +3237,7 @@
errorLine2=" ~~~~~~~">
@@ -3248,7 +3248,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~">
@@ -3259,7 +3259,7 @@
errorLine2=" ~~~~~~~">
@@ -3270,7 +3270,7 @@
errorLine2=" ~~~~~~~">
@@ -3281,7 +3281,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~">
@@ -3292,7 +3292,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~">
@@ -3534,7 +3534,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~">
@@ -3644,7 +3644,7 @@
errorLine2=" ~~~~~~~~~">
@@ -3699,7 +3699,7 @@
errorLine2=" ~~~~~~~">
@@ -3710,7 +3710,7 @@
errorLine2=" ~~~~~~~~~~~~">
@@ -3721,7 +3721,7 @@
errorLine2=" ~~~~~~~~~~~~">
@@ -3732,7 +3732,7 @@
errorLine2=" ~~~~~~~">
@@ -3743,7 +3743,7 @@
errorLine2=" ~~~~~~~">
diff --git a/app/src/main/java/app/pachli/AboutActivity.kt b/app/src/main/java/app/pachli/AboutActivity.kt
index df8b772c8..7e3152dfe 100644
--- a/app/src/main/java/app/pachli/AboutActivity.kt
+++ b/app/src/main/java/app/pachli/AboutActivity.kt
@@ -3,7 +3,6 @@ package app.pachli
import android.content.ClipData
import android.content.ClipboardManager
import android.content.Context
-import android.content.Intent
import android.os.Build
import android.os.Bundle
import android.text.SpannableString
@@ -16,6 +15,8 @@ import android.widget.Toast
import androidx.annotation.StringRes
import androidx.lifecycle.lifecycleScope
import app.pachli.components.instanceinfo.InstanceInfoRepository
+import app.pachli.core.navigation.LicenseActivityIntent
+import app.pachli.core.navigation.PrivacyPolicyActivityIntent
import app.pachli.databinding.ActivityAboutBinding
import app.pachli.util.NoUnderlineURLSpan
import app.pachli.util.hide
@@ -76,8 +77,7 @@ class AboutActivity : BottomSheetActivity() {
binding.aboutBugsFeaturesInfoTextView.setClickableTextWithoutUnderlines(R.string.about_bug_feature_request_site)
binding.aboutPrivacyPolicyTextView.setOnClickListener {
- val intent = Intent(this, PrivacyPolicyActivity::class.java)
- startActivity(intent)
+ startActivity(PrivacyPolicyActivityIntent(this))
}
binding.appProfileButton.setOnClickListener {
@@ -85,7 +85,7 @@ class AboutActivity : BottomSheetActivity() {
}
binding.aboutLicensesButton.setOnClickListener {
- startActivityWithSlideInAnimation(Intent(this, LicenseActivity::class.java))
+ startActivityWithSlideInAnimation(LicenseActivityIntent(this))
}
binding.copyDeviceInfo.setOnClickListener {
diff --git a/app/src/main/java/app/pachli/BaseActivity.kt b/app/src/main/java/app/pachli/BaseActivity.kt
index d12bb25f1..d3a69bc22 100644
--- a/app/src/main/java/app/pachli/BaseActivity.kt
+++ b/app/src/main/java/app/pachli/BaseActivity.kt
@@ -34,9 +34,9 @@ import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
import app.pachli.MainActivity.Companion.redirectIntent
import app.pachli.adapter.AccountSelectionAdapter
-import app.pachli.components.login.LoginActivity
import app.pachli.core.accounts.AccountManager
import app.pachli.core.database.model.AccountEntity
+import app.pachli.core.navigation.LoginActivityIntent
import app.pachli.core.preferences.PrefKeys
import app.pachli.core.preferences.PrefKeys.APP_THEME
import app.pachli.core.preferences.SharedPreferencesRepository
@@ -182,7 +182,7 @@ abstract class BaseActivity : AppCompatActivity() {
private fun redirectIfNotLoggedIn() {
val account = accountManager.activeAccount
if (account == null) {
- val intent = Intent(this, LoginActivity::class.java)
+ val intent = LoginActivityIntent(this)
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_NEW_TASK)
startActivityWithSlideInAnimation(intent)
finish()
diff --git a/app/src/main/java/app/pachli/BottomSheetActivity.kt b/app/src/main/java/app/pachli/BottomSheetActivity.kt
index 169aba027..fc9d38b96 100644
--- a/app/src/main/java/app/pachli/BottomSheetActivity.kt
+++ b/app/src/main/java/app/pachli/BottomSheetActivity.kt
@@ -17,15 +17,14 @@
package app.pachli
import android.content.Context
-import android.content.Intent
import android.os.Bundle
import android.view.View
import android.widget.LinearLayout
import android.widget.Toast
import androidx.annotation.VisibleForTesting
import androidx.lifecycle.lifecycleScope
-import app.pachli.components.account.AccountActivity
-import app.pachli.components.viewthread.ViewThreadActivity
+import app.pachli.core.navigation.AccountActivityIntent
+import app.pachli.core.navigation.ViewThreadActivityIntent
import app.pachli.core.network.retrofit.MastodonApi
import app.pachli.util.looksLikeMastodonUrl
import app.pachli.util.openLink
@@ -104,15 +103,13 @@ abstract class BottomSheetActivity : BaseActivity() {
open fun viewThread(statusId: String, url: String?) {
if (!isSearching()) {
- val intent = Intent(this, ViewThreadActivity::class.java)
- intent.putExtra("id", statusId)
- intent.putExtra("url", url)
+ val intent = ViewThreadActivityIntent(this, statusId, url)
startActivityWithSlideInAnimation(intent)
}
}
open fun viewAccount(id: String) {
- val intent = AccountActivity.getIntent(this, id)
+ val intent = AccountActivityIntent(this, id)
startActivityWithSlideInAnimation(intent)
}
diff --git a/app/src/main/java/app/pachli/ListsActivity.kt b/app/src/main/java/app/pachli/ListsActivity.kt
index 4ac77bacd..14879e191 100644
--- a/app/src/main/java/app/pachli/ListsActivity.kt
+++ b/app/src/main/java/app/pachli/ListsActivity.kt
@@ -18,8 +18,6 @@
package app.pachli
import android.app.Dialog
-import android.content.Context
-import android.content.Intent
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
@@ -36,6 +34,7 @@ import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.ListAdapter
import androidx.recyclerview.widget.RecyclerView
+import app.pachli.core.navigation.StatusListActivityIntent
import app.pachli.core.network.model.MastoList
import app.pachli.databinding.ActivityListsBinding
import app.pachli.databinding.DialogListBinding
@@ -191,7 +190,7 @@ class ListsActivity : BaseActivity() {
private fun onListSelected(listId: String, listTitle: String) {
startActivityWithSlideInAnimation(
- StatusListActivity.newListIntent(this, listId, listTitle),
+ StatusListActivityIntent.list(this, listId, listTitle),
)
}
@@ -277,8 +276,4 @@ class ListsActivity : BaseActivity() {
viewModel.updateList(listId, name, exclusive)
}
}
-
- companion object {
- fun newIntent(context: Context) = Intent(context, ListsActivity::class.java)
- }
}
diff --git a/app/src/main/java/app/pachli/MainActivity.kt b/app/src/main/java/app/pachli/MainActivity.kt
index f71615f29..16390e368 100644
--- a/app/src/main/java/app/pachli/MainActivity.kt
+++ b/app/src/main/java/app/pachli/MainActivity.kt
@@ -46,7 +46,6 @@ import androidx.appcompat.app.AlertDialog
import androidx.appcompat.content.res.AppCompatResources
import androidx.coordinatorlayout.widget.CoordinatorLayout
import androidx.core.app.ActivityCompat
-import androidx.core.content.IntentCompat
import androidx.core.content.edit
import androidx.core.content.pm.ShortcutManagerCompat
import androidx.core.content.res.ResourcesCompat
@@ -61,24 +60,32 @@ import app.pachli.appstore.CacheUpdater
import app.pachli.appstore.EventHub
import app.pachli.appstore.MainTabsChangedEvent
import app.pachli.appstore.ProfileEditedEvent
-import app.pachli.components.account.AccountActivity
-import app.pachli.components.accountlist.AccountListActivity
-import app.pachli.components.announcements.AnnouncementsActivity
-import app.pachli.components.compose.ComposeActivity
import app.pachli.components.compose.ComposeActivity.Companion.canHandleMimeType
-import app.pachli.components.drafts.DraftsActivity
-import app.pachli.components.login.LoginActivity
import app.pachli.components.notifications.createNotificationChannelsForAccount
import app.pachli.components.notifications.disableAllNotifications
import app.pachli.components.notifications.enablePushNotificationsWithFallback
import app.pachli.components.notifications.notificationsAreEnabled
import app.pachli.components.notifications.showMigrationNoticeIfNecessary
-import app.pachli.components.preference.PreferencesActivity
-import app.pachli.components.scheduled.ScheduledStatusActivity
-import app.pachli.components.search.SearchActivity
-import app.pachli.components.trending.TrendingActivity
import app.pachli.core.database.model.AccountEntity
import app.pachli.core.database.model.TabKind
+import app.pachli.core.navigation.AboutActivityIntent
+import app.pachli.core.navigation.AccountActivityIntent
+import app.pachli.core.navigation.AccountListActivityIntent
+import app.pachli.core.navigation.AnnouncementsActivityIntent
+import app.pachli.core.navigation.ComposeActivityIntent
+import app.pachli.core.navigation.ComposeActivityIntent.ComposeOptions
+import app.pachli.core.navigation.DraftsActivityIntent
+import app.pachli.core.navigation.EditProfileActivityIntent
+import app.pachli.core.navigation.ListActivityIntent
+import app.pachli.core.navigation.LoginActivityIntent
+import app.pachli.core.navigation.LoginActivityIntent.LoginMode
+import app.pachli.core.navigation.MainActivityIntent
+import app.pachli.core.navigation.PreferencesActivityIntent
+import app.pachli.core.navigation.PreferencesActivityIntent.PreferenceScreen
+import app.pachli.core.navigation.ScheduledStatusActivityIntent
+import app.pachli.core.navigation.SearchActivityIntent
+import app.pachli.core.navigation.StatusListActivityIntent
+import app.pachli.core.navigation.TrendingActivityIntent
import app.pachli.core.network.model.Account
import app.pachli.core.network.model.Notification
import app.pachli.core.preferences.PrefKeys
@@ -253,13 +260,13 @@ class MainActivity : BottomSheetActivity(), ActionButtonActivity, MenuProvider {
)
}
} else if (openDrafts) {
- val intent = DraftsActivity.newIntent(this)
+ val intent = DraftsActivityIntent(this)
startActivity(intent)
} else if (accountRequested && intent.hasExtra(NOTIFICATION_TYPE)) {
// user clicked a notification, show follow requests for type FOLLOW_REQUEST,
// otherwise show notification tab
if (intent.getSerializableExtra(NOTIFICATION_TYPE) == Notification.Type.FOLLOW_REQUEST) {
- val intent = AccountListActivity.newIntent(this, AccountListActivity.Type.FOLLOW_REQUESTS)
+ val intent = AccountListActivityIntent(this, AccountListActivityIntent.Kind.FOLLOW_REQUESTS)
startActivityWithSlideInAnimation(intent)
} else {
showNotificationTab = true
@@ -272,7 +279,7 @@ class MainActivity : BottomSheetActivity(), ActionButtonActivity, MenuProvider {
glide = Glide.with(this)
binding.composeButton.setOnClickListener {
- val composeIntent = Intent(applicationContext, ComposeActivity::class.java)
+ val composeIntent = ComposeActivityIntent(applicationContext)
startActivity(composeIntent)
}
@@ -393,7 +400,7 @@ class MainActivity : BottomSheetActivity(), ActionButtonActivity, MenuProvider {
override fun onMenuItemSelected(item: MenuItem): Boolean {
return when (item.itemId) {
R.id.action_search -> {
- startActivity(SearchActivity.getIntent(this@MainActivity))
+ startActivity(SearchActivityIntent(this@MainActivity))
true
}
else -> super.onOptionsItemSelected(item)
@@ -503,7 +510,7 @@ class MainActivity : BottomSheetActivity(), ActionButtonActivity, MenuProvider {
return true
}
KeyEvent.KEYCODE_SEARCH -> {
- startActivityWithSlideInAnimation(SearchActivity.getIntent(this))
+ startActivityWithSlideInAnimation(SearchActivityIntent(this))
return true
}
}
@@ -512,7 +519,7 @@ class MainActivity : BottomSheetActivity(), ActionButtonActivity, MenuProvider {
when (keyCode) {
KeyEvent.KEYCODE_N -> {
// open compose activity by pressing SHIFT + N (or CTRL + N)
- val composeIntent = Intent(applicationContext, ComposeActivity::class.java)
+ val composeIntent = ComposeActivityIntent(applicationContext)
startActivity(composeIntent)
return true
}
@@ -533,12 +540,12 @@ class MainActivity : BottomSheetActivity(), ActionButtonActivity, MenuProvider {
}
private fun forwardToComposeActivity(intent: Intent) {
- val composeOptions = IntentCompat.getParcelableExtra(intent, COMPOSE_OPTIONS, ComposeActivity.ComposeOptions::class.java)
+ val composeOptions = ComposeActivityIntent.getOptions(intent)
val composeIntent = if (composeOptions != null) {
- ComposeActivity.startIntent(this, composeOptions)
+ ComposeActivityIntent(this, composeOptions)
} else {
- Intent(this, ComposeActivity::class.java).apply {
+ ComposeActivityIntent(this).apply {
action = intent.action
type = intent.type
putExtras(intent)
@@ -640,7 +647,7 @@ class MainActivity : BottomSheetActivity(), ActionButtonActivity, MenuProvider {
nameRes = R.string.action_edit_profile
iconicsIcon = GoogleMaterial.Icon.gmd_person
onClick = {
- val intent = Intent(context, EditProfileActivity::class.java)
+ val intent = EditProfileActivityIntent(context)
startActivityWithSlideInAnimation(intent)
}
},
@@ -649,7 +656,7 @@ class MainActivity : BottomSheetActivity(), ActionButtonActivity, MenuProvider {
isSelectable = false
iconicsIcon = GoogleMaterial.Icon.gmd_star
onClick = {
- val intent = StatusListActivity.newFavouritesIntent(context)
+ val intent = StatusListActivityIntent.favourites(context)
startActivityWithSlideInAnimation(intent)
}
},
@@ -657,7 +664,7 @@ class MainActivity : BottomSheetActivity(), ActionButtonActivity, MenuProvider {
nameRes = R.string.action_view_bookmarks
iconicsIcon = GoogleMaterial.Icon.gmd_bookmark
onClick = {
- val intent = StatusListActivity.newBookmarksIntent(context)
+ val intent = StatusListActivityIntent.bookmarks(context)
startActivityWithSlideInAnimation(intent)
}
},
@@ -665,7 +672,7 @@ class MainActivity : BottomSheetActivity(), ActionButtonActivity, MenuProvider {
nameRes = R.string.action_view_follow_requests
iconicsIcon = GoogleMaterial.Icon.gmd_person_add
onClick = {
- val intent = AccountListActivity.newIntent(context, AccountListActivity.Type.FOLLOW_REQUESTS)
+ val intent = AccountListActivityIntent(context, AccountListActivityIntent.Kind.FOLLOW_REQUESTS)
startActivityWithSlideInAnimation(intent)
}
},
@@ -673,14 +680,14 @@ class MainActivity : BottomSheetActivity(), ActionButtonActivity, MenuProvider {
nameRes = R.string.action_lists
iconicsIcon = GoogleMaterial.Icon.gmd_list
onClick = {
- startActivityWithSlideInAnimation(ListsActivity.newIntent(context))
+ startActivityWithSlideInAnimation(ListActivityIntent(context))
}
},
primaryDrawerItem {
nameRes = R.string.action_access_drafts
iconRes = R.drawable.ic_notebook
onClick = {
- val intent = DraftsActivity.newIntent(context)
+ val intent = DraftsActivityIntent(context)
startActivityWithSlideInAnimation(intent)
}
},
@@ -688,7 +695,7 @@ class MainActivity : BottomSheetActivity(), ActionButtonActivity, MenuProvider {
nameRes = R.string.action_access_scheduled_posts
iconRes = R.drawable.ic_access_time
onClick = {
- startActivityWithSlideInAnimation(ScheduledStatusActivity.newIntent(context))
+ startActivityWithSlideInAnimation(ScheduledStatusActivityIntent(context))
}
},
primaryDrawerItem {
@@ -696,7 +703,7 @@ class MainActivity : BottomSheetActivity(), ActionButtonActivity, MenuProvider {
nameRes = R.string.title_announcements
iconRes = R.drawable.ic_bullhorn_24dp
onClick = {
- startActivityWithSlideInAnimation(AnnouncementsActivity.newIntent(context))
+ startActivityWithSlideInAnimation(AnnouncementsActivityIntent(context))
}
badgeStyle = BadgeStyle().apply {
textColor = ColorHolder.fromColor(MaterialColors.getColor(binding.mainDrawer, com.google.android.material.R.attr.colorOnPrimary))
@@ -708,7 +715,7 @@ class MainActivity : BottomSheetActivity(), ActionButtonActivity, MenuProvider {
nameRes = R.string.action_view_account_preferences
iconRes = R.drawable.ic_account_settings
onClick = {
- val intent = PreferencesActivity.newIntent(context, PreferencesActivity.ACCOUNT_PREFERENCES)
+ val intent = PreferencesActivityIntent(context, PreferenceScreen.ACCOUNT)
startActivityWithSlideInAnimation(intent)
}
},
@@ -716,7 +723,7 @@ class MainActivity : BottomSheetActivity(), ActionButtonActivity, MenuProvider {
nameRes = R.string.action_view_preferences
iconicsIcon = GoogleMaterial.Icon.gmd_settings
onClick = {
- val intent = PreferencesActivity.newIntent(context, PreferencesActivity.GENERAL_PREFERENCES)
+ val intent = PreferencesActivityIntent(context, PreferenceScreen.GENERAL)
startActivityWithSlideInAnimation(intent)
}
},
@@ -724,7 +731,7 @@ class MainActivity : BottomSheetActivity(), ActionButtonActivity, MenuProvider {
nameRes = R.string.about_title_activity
iconicsIcon = GoogleMaterial.Icon.gmd_info
onClick = {
- val intent = Intent(context, AboutActivity::class.java)
+ val intent = AboutActivityIntent(context)
startActivityWithSlideInAnimation(intent)
}
},
@@ -742,7 +749,7 @@ class MainActivity : BottomSheetActivity(), ActionButtonActivity, MenuProvider {
nameRes = R.string.action_search
iconicsIcon = GoogleMaterial.Icon.gmd_search
onClick = {
- startActivityWithSlideInAnimation(SearchActivity.getIntent(context))
+ startActivityWithSlideInAnimation(SearchActivityIntent(context))
}
},
)
@@ -754,7 +761,7 @@ class MainActivity : BottomSheetActivity(), ActionButtonActivity, MenuProvider {
nameRes = R.string.title_public_trending
iconicsIcon = GoogleMaterial.Icon.gmd_trending_up
onClick = {
- startActivityWithSlideInAnimation(TrendingActivity.getIntent(context))
+ startActivityWithSlideInAnimation(TrendingActivityIntent(context))
}
},
)
@@ -941,13 +948,15 @@ class MainActivity : BottomSheetActivity(), ActionButtonActivity, MenuProvider {
// open profile when active image was clicked
if (current && activeAccount != null) {
- val intent = AccountActivity.getIntent(this, activeAccount.accountId)
+ val intent = AccountActivityIntent(this, activeAccount.accountId)
startActivityWithSlideInAnimation(intent)
return
}
// open LoginActivity to add new account
if (profile.identifier == DRAWER_ITEM_ADD_ACCOUNT) {
- startActivityWithSlideInAnimation(LoginActivity.getIntent(this, LoginActivity.MODE_ADDITIONAL_LOGIN))
+ startActivityWithSlideInAnimation(
+ LoginActivityIntent(this, LoginMode.ADDITIONAL_LOGIN),
+ )
return
}
// change Account
@@ -958,7 +967,7 @@ class MainActivity : BottomSheetActivity(), ActionButtonActivity, MenuProvider {
private fun changeAccount(newSelectedId: Long, forward: Intent?) {
cacheUpdater.stop()
accountManager.setActiveAccount(newSelectedId)
- val intent = Intent(this, MainActivity::class.java)
+ val intent = MainActivityIntent(this)
intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
if (forward != null) {
intent.type = forward.type
@@ -988,9 +997,9 @@ class MainActivity : BottomSheetActivity(), ActionButtonActivity, MenuProvider {
lifecycleScope.launch {
val otherAccountAvailable = logoutUsecase.logout()
val intent = if (otherAccountAvailable) {
- Intent(this@MainActivity, MainActivity::class.java)
+ MainActivityIntent(this@MainActivity)
} else {
- LoginActivity.getIntent(this@MainActivity, LoginActivity.MODE_DEFAULT)
+ LoginActivityIntent(this@MainActivity, LoginMode.DEFAULT)
}
startActivity(intent)
finishWithoutSlideOutAnimation()
@@ -1197,7 +1206,7 @@ class MainActivity : BottomSheetActivity(), ActionButtonActivity, MenuProvider {
*/
@JvmStatic
fun accountSwitchIntent(context: Context, pachliAccountId: Long): Intent {
- return Intent(context, MainActivity::class.java).apply {
+ return MainActivityIntent(context).apply {
putExtra(PACHLI_ACCOUNT_ID, pachliAccountId)
}
}
@@ -1221,7 +1230,7 @@ class MainActivity : BottomSheetActivity(), ActionButtonActivity, MenuProvider {
@JvmStatic
fun composeIntent(
context: Context,
- options: ComposeActivity.ComposeOptions,
+ options: ComposeOptions,
pachliAccountId: Long = -1,
notificationTag: String? = null,
notificationId: Int = -1,
diff --git a/app/src/main/java/app/pachli/SplashActivity.kt b/app/src/main/java/app/pachli/SplashActivity.kt
index 0d966fc45..862c70315 100644
--- a/app/src/main/java/app/pachli/SplashActivity.kt
+++ b/app/src/main/java/app/pachli/SplashActivity.kt
@@ -18,12 +18,13 @@
package app.pachli
import android.annotation.SuppressLint
-import android.content.Intent
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
-import app.pachli.components.login.LoginActivity
import app.pachli.core.accounts.AccountManager
+import app.pachli.core.navigation.LoginActivityIntent
+import app.pachli.core.navigation.LoginActivityIntent.LoginMode
+import app.pachli.core.navigation.MainActivityIntent
import dagger.hilt.android.AndroidEntryPoint
import javax.inject.Inject
@@ -41,9 +42,9 @@ class SplashActivity : AppCompatActivity() {
/** Determine whether the user is currently logged in, and if so go ahead and load the
* timeline. Otherwise, start the activity_login screen. */
val intent = if (accountManager.activeAccount != null) {
- Intent(this, MainActivity::class.java)
+ MainActivityIntent(this)
} else {
- LoginActivity.getIntent(this, LoginActivity.MODE_DEFAULT)
+ LoginActivityIntent(this, LoginMode.DEFAULT)
}
startActivity(intent)
finish()
diff --git a/app/src/main/java/app/pachli/StatusListActivity.kt b/app/src/main/java/app/pachli/StatusListActivity.kt
index b968bb457..828b1f356 100644
--- a/app/src/main/java/app/pachli/StatusListActivity.kt
+++ b/app/src/main/java/app/pachli/StatusListActivity.kt
@@ -17,8 +17,6 @@
package app.pachli
-import android.content.Context
-import android.content.Intent
import android.os.Bundle
import android.view.Menu
import android.view.MenuItem
@@ -26,8 +24,10 @@ import androidx.fragment.app.commit
import androidx.lifecycle.lifecycleScope
import app.pachli.appstore.EventHub
import app.pachli.appstore.FilterChangedEvent
-import app.pachli.components.compose.ComposeActivity
import app.pachli.components.timeline.TimelineFragment
+import app.pachli.core.navigation.ComposeActivityIntent
+import app.pachli.core.navigation.ComposeActivityIntent.ComposeOptions
+import app.pachli.core.navigation.StatusListActivityIntent
import app.pachli.core.network.model.Filter
import app.pachli.core.network.model.FilterV1
import app.pachli.core.network.model.TimelineKind
@@ -84,7 +84,7 @@ class StatusListActivity : BottomSheetActivity(), AppBarLayoutHost, ActionButton
setSupportActionBar(binding.includedToolbar.toolbar)
- timelineKind = intent.getParcelableExtra(EXTRA_KIND)!!
+ timelineKind = StatusListActivityIntent.getKind(intent)
val title = when (timelineKind) {
is TimelineKind.Favourites -> getString(R.string.title_favourites)
@@ -113,11 +113,11 @@ class StatusListActivity : BottomSheetActivity(), AppBarLayoutHost, ActionButton
val composeIntent = when (timelineKind) {
is TimelineKind.Tag -> {
val tag = (timelineKind as TimelineKind.Tag).tags.first()
- ComposeActivity.startIntent(
+ ComposeActivityIntent(
this,
- ComposeActivity.ComposeOptions(
+ ComposeOptions(
content = getString(R.string.title_tag_with_initial_position).format(tag),
- initialCursorPosition = ComposeActivity.InitialCursorPosition.START,
+ initialCursorPosition = ComposeOptions.InitialCursorPosition.START,
),
)
}
@@ -125,10 +125,7 @@ class StatusListActivity : BottomSheetActivity(), AppBarLayoutHost, ActionButton
is TimelineKind.Favourites,
is TimelineKind.UserList,
-> {
- ComposeActivity.startIntent(
- this,
- ComposeActivity.ComposeOptions(),
- )
+ ComposeActivityIntent(this, ComposeOptions())
}
else -> null
}
@@ -367,29 +364,4 @@ class StatusListActivity : BottomSheetActivity(), AppBarLayoutHost, ActionButton
return true
}
-
- companion object {
- private const val EXTRA_KIND = "kind"
-
- fun newFavouritesIntent(context: Context) =
- Intent(context, StatusListActivity::class.java).apply {
- putExtra(EXTRA_KIND, TimelineKind.Favourites)
- }
-
- fun newBookmarksIntent(context: Context) =
- Intent(context, StatusListActivity::class.java).apply {
- putExtra(EXTRA_KIND, TimelineKind.Bookmarks)
- }
-
- fun newListIntent(context: Context, listId: String, listTitle: String) =
- Intent(context, StatusListActivity::class.java).apply {
- putExtra(EXTRA_KIND, TimelineKind.UserList(listId, listTitle))
- }
-
- @JvmStatic
- fun newHashtagIntent(context: Context, hashtag: String) =
- Intent(context, StatusListActivity::class.java).apply {
- putExtra(EXTRA_KIND, TimelineKind.Tag(listOf(hashtag)))
- }
- }
}
diff --git a/app/src/main/java/app/pachli/TabPreferenceActivity.kt b/app/src/main/java/app/pachli/TabPreferenceActivity.kt
index 9dd740b16..a4b9e081d 100644
--- a/app/src/main/java/app/pachli/TabPreferenceActivity.kt
+++ b/app/src/main/java/app/pachli/TabPreferenceActivity.kt
@@ -17,7 +17,6 @@
package app.pachli
-import android.content.Intent
import android.graphics.Color
import android.os.Bundle
import android.view.Gravity
@@ -44,6 +43,7 @@ import app.pachli.appstore.EventHub
import app.pachli.appstore.MainTabsChangedEvent
import app.pachli.core.database.model.TabData
import app.pachli.core.database.model.TabKind
+import app.pachli.core.navigation.ListActivityIntent
import app.pachli.core.network.model.MastoList
import app.pachli.core.network.retrofit.MastodonApi
import app.pachli.databinding.ActivityTabPreferenceBinding
@@ -303,7 +303,7 @@ class TabPreferenceActivity : BaseActivity(), ItemInteractionListener {
val dialogBuilder = AlertDialog.Builder(this)
.setTitle(R.string.select_list_title)
.setNeutralButton(R.string.select_list_manage) { _, _ ->
- val listIntent = Intent(applicationContext, ListsActivity::class.java)
+ val listIntent = ListActivityIntent(applicationContext)
startActivity(listIntent)
}
.setNegativeButton(android.R.string.cancel, null)
diff --git a/app/src/main/java/app/pachli/ViewMediaActivity.kt b/app/src/main/java/app/pachli/ViewMediaActivity.kt
index 9e12e3b3f..990a56f0b 100644
--- a/app/src/main/java/app/pachli/ViewMediaActivity.kt
+++ b/app/src/main/java/app/pachli/ViewMediaActivity.kt
@@ -24,7 +24,6 @@ import android.app.DownloadManager
import android.content.ClipData
import android.content.ClipboardManager
import android.content.Context
-import android.content.Intent
import android.content.pm.PackageManager
import android.graphics.Bitmap
import android.graphics.Color
@@ -40,13 +39,14 @@ import android.webkit.MimeTypeMap
import android.widget.Toast
import androidx.core.app.ShareCompat
import androidx.core.content.FileProvider
-import androidx.core.content.IntentCompat
import androidx.fragment.app.FragmentActivity
import androidx.lifecycle.Lifecycle
import androidx.viewpager2.adapter.FragmentStateAdapter
import androidx.viewpager2.widget.ViewPager2
import app.pachli.BuildConfig.APPLICATION_ID
-import app.pachli.components.viewthread.ViewThreadActivity
+import app.pachli.core.navigation.AttachmentViewData
+import app.pachli.core.navigation.ViewMediaActivityIntent
+import app.pachli.core.navigation.ViewThreadActivityIntent
import app.pachli.core.network.model.Attachment
import app.pachli.databinding.ActivityViewMediaBinding
import app.pachli.fragment.ViewImageFragment
@@ -55,7 +55,6 @@ import app.pachli.pager.ImagePagerAdapter
import app.pachli.pager.SingleImagePagerAdapter
import app.pachli.util.getTemporaryMediaFilename
import app.pachli.util.viewBinding
-import app.pachli.viewdata.AttachmentViewData
import autodispose2.androidx.lifecycle.AndroidLifecycleScopeProvider
import autodispose2.autoDispose
import com.bumptech.glide.Glide
@@ -73,6 +72,9 @@ import java.util.Locale
typealias ToolbarVisibilityListener = (isVisible: Boolean) -> Unit
+/**
+ * Show one or more media items (pictures, video, audio, etc).
+ */
@AndroidEntryPoint
class ViewMediaActivity : BaseActivity(), ViewImageFragment.PhotoActionsListener, ViewVideoFragment.VideoActionsListener {
private val binding by viewBinding(ActivityViewMediaBinding::inflate)
@@ -100,8 +102,8 @@ class ViewMediaActivity : BaseActivity(), ViewImageFragment.PhotoActionsListener
supportPostponeEnterTransition()
// Gather the parameters.
- attachments = IntentCompat.getParcelableArrayListExtra(intent, EXTRA_ATTACHMENTS, AttachmentViewData::class.java)
- val initialPosition = intent.getIntExtra(EXTRA_ATTACHMENT_INDEX, 0)
+ attachments = ViewMediaActivityIntent.getAttachments(intent)
+ val initialPosition = ViewMediaActivityIntent.getAttachmentIndex(intent)
// Adapter is actually of existential type PageAdapter & SharedElementsTransitionListener
// but it cannot be expressed and if I don't specify type explicitly compilation fails
@@ -111,7 +113,7 @@ class ViewMediaActivity : BaseActivity(), ViewImageFragment.PhotoActionsListener
// Setup the view pager.
ImagePagerAdapter(this, realAttachs, initialPosition)
} else {
- imageUrl = intent.getStringExtra(EXTRA_SINGLE_IMAGE_URL)
+ imageUrl = ViewMediaActivityIntent.getImageUrl(intent)
?: throw IllegalArgumentException("attachment list or image url has to be set")
SingleImagePagerAdapter(this, imageUrl!!)
@@ -241,7 +243,7 @@ class ViewMediaActivity : BaseActivity(), ViewImageFragment.PhotoActionsListener
private fun onOpenStatus() {
val attach = attachments!![binding.viewPager.currentItem]
- startActivityWithSlideInAnimation(ViewThreadActivity.startIntent(this, attach.statusId, attach.statusUrl))
+ startActivityWithSlideInAnimation(ViewThreadActivityIntent(this, attach.statusId, attach.statusUrl))
}
private fun copyLink() {
@@ -344,27 +346,6 @@ class ViewMediaActivity : BaseActivity(), ViewImageFragment.PhotoActionsListener
shareFile(file, mimeType)
}
-
- companion object {
- private const val EXTRA_ATTACHMENTS = "attachments"
- private const val EXTRA_ATTACHMENT_INDEX = "index"
- private const val EXTRA_SINGLE_IMAGE_URL = "single_image"
-
- @JvmStatic
- fun newIntent(context: Context?, attachments: List, index: Int): Intent {
- val intent = Intent(context, ViewMediaActivity::class.java)
- intent.putParcelableArrayListExtra(EXTRA_ATTACHMENTS, ArrayList(attachments))
- intent.putExtra(EXTRA_ATTACHMENT_INDEX, index)
- return intent
- }
-
- @JvmStatic
- fun newSingleImageIntent(context: Context, url: String): Intent {
- val intent = Intent(context, ViewMediaActivity::class.java)
- intent.putExtra(EXTRA_SINGLE_IMAGE_URL, url)
- return intent
- }
- }
}
abstract class ViewMediaAdapter(activity: FragmentActivity) : FragmentStateAdapter(activity) {
diff --git a/app/src/main/java/app/pachli/adapter/StatusBaseViewHolder.kt b/app/src/main/java/app/pachli/adapter/StatusBaseViewHolder.kt
index 6504a6c7a..4698dc058 100644
--- a/app/src/main/java/app/pachli/adapter/StatusBaseViewHolder.kt
+++ b/app/src/main/java/app/pachli/adapter/StatusBaseViewHolder.kt
@@ -20,10 +20,10 @@ import androidx.core.content.ContextCompat
import androidx.core.text.HtmlCompat
import androidx.recyclerview.widget.RecyclerView
import app.pachli.R
-import app.pachli.ViewMediaActivity.Companion.newSingleImageIntent
import app.pachli.core.common.util.AbsoluteTimeFormatter
import app.pachli.core.common.util.formatNumber
import app.pachli.core.database.model.TranslationState
+import app.pachli.core.navigation.ViewMediaActivityIntent
import app.pachli.core.network.model.Attachment
import app.pachli.core.network.model.Emoji
import app.pachli.core.network.model.PreviewCardKind
@@ -877,7 +877,7 @@ abstract class StatusBaseViewHolder protected constructor(itemView: View) :
cardView.bind(card, status.actionable.sensitive, statusDisplayOptions) { target ->
if (card.kind == PreviewCardKind.PHOTO && card.embedUrl.isNotEmpty() && target == PreviewCardView.Target.IMAGE) {
context.startActivity(
- newSingleImageIntent(context, card.embedUrl),
+ ViewMediaActivityIntent(context, card.embedUrl),
)
} else {
listener.onViewUrl(card.url)
diff --git a/app/src/main/java/app/pachli/components/account/AccountActivity.kt b/app/src/main/java/app/pachli/components/account/AccountActivity.kt
index 66c6cb319..544d53307 100644
--- a/app/src/main/java/app/pachli/components/account/AccountActivity.kt
+++ b/app/src/main/java/app/pachli/components/account/AccountActivity.kt
@@ -47,15 +47,17 @@ import androidx.core.widget.doAfterTextChanged
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.viewpager2.widget.MarginPageTransformer
import app.pachli.BottomSheetActivity
-import app.pachli.EditProfileActivity
import app.pachli.R
-import app.pachli.StatusListActivity
-import app.pachli.ViewMediaActivity
import app.pachli.components.account.list.ListsForAccountFragment
-import app.pachli.components.accountlist.AccountListActivity
-import app.pachli.components.compose.ComposeActivity
-import app.pachli.components.report.ReportActivity
import app.pachli.core.database.model.AccountEntity
+import app.pachli.core.navigation.AccountActivityIntent
+import app.pachli.core.navigation.AccountListActivityIntent
+import app.pachli.core.navigation.ComposeActivityIntent
+import app.pachli.core.navigation.ComposeActivityIntent.ComposeOptions
+import app.pachli.core.navigation.EditProfileActivityIntent
+import app.pachli.core.navigation.ReportActivityIntent
+import app.pachli.core.navigation.StatusListActivityIntent
+import app.pachli.core.navigation.ViewMediaActivityIntent
import app.pachli.core.network.model.Account
import app.pachli.core.network.model.Relationship
import app.pachli.core.network.parseAsMastodonHtml
@@ -100,6 +102,9 @@ import java.util.Locale
import javax.inject.Inject
import kotlin.math.abs
+/**
+ * Show a single account's profile details.
+ */
@AndroidEntryPoint
class AccountActivity :
BottomSheetActivity(),
@@ -172,7 +177,7 @@ class AccountActivity :
addMenuProvider(this)
// Obtain information to fill out the profile.
- viewModel.setAccountInfo(intent.getStringExtra(KEY_ACCOUNT_ID)!!)
+ viewModel.setAccountInfo(AccountActivityIntent.getAccountId(intent))
animateAvatar = sharedPreferencesRepository.getBoolean(PrefKeys.ANIMATE_GIF_AVATARS, false)
animateEmojis = sharedPreferencesRepository.getBoolean(PrefKeys.ANIMATE_CUSTOM_EMOJIS, false)
@@ -221,12 +226,12 @@ class AccountActivity :
binding.accountFieldList.adapter = accountFieldAdapter
val accountListClickListener = { v: View ->
- val type = when (v.id) {
- R.id.accountFollowers -> AccountListActivity.Type.FOLLOWERS
- R.id.accountFollowing -> AccountListActivity.Type.FOLLOWS
+ val kind = when (v.id) {
+ R.id.accountFollowers -> AccountListActivityIntent.Kind.FOLLOWERS
+ R.id.accountFollowing -> AccountListActivityIntent.Kind.FOLLOWS
else -> throw AssertionError()
}
- val accountListIntent = AccountListActivity.newIntent(this, type, viewModel.accountId)
+ val accountListIntent = AccountListActivityIntent(this, kind, viewModel.accountId)
startActivityWithSlideInAnimation(accountListIntent)
}
binding.accountFollowers.setOnClickListener(accountListClickListener)
@@ -540,7 +545,7 @@ class AccountActivity :
private fun viewImage(view: View, uri: String) {
view.transitionName = uri
startActivity(
- ViewMediaActivity.newSingleImageIntent(view.context, uri),
+ ViewMediaActivityIntent(view.context, uri),
ActivityOptionsCompat.makeSceneTransitionAnimation(this, view, uri).toBundle(),
)
}
@@ -606,7 +611,7 @@ class AccountActivity :
binding.accountFollowButton.setOnClickListener {
if (viewModel.isSelf) {
- val intent = Intent(this@AccountActivity, EditProfileActivity::class.java)
+ val intent = EditProfileActivityIntent(this@AccountActivity)
startActivity(intent)
return@setOnClickListener
}
@@ -879,26 +884,25 @@ class AccountActivity :
private fun mention() {
loadedAccount?.let {
val options = if (viewModel.isSelf) {
- ComposeActivity.ComposeOptions(kind = ComposeActivity.ComposeKind.NEW)
+ ComposeOptions(kind = ComposeOptions.ComposeKind.NEW)
} else {
- ComposeActivity.ComposeOptions(
+ ComposeOptions(
mentionedUsernames = setOf(it.username),
- kind = ComposeActivity.ComposeKind.NEW,
+ kind = ComposeOptions.ComposeKind.NEW,
)
}
- val intent = ComposeActivity.startIntent(this, options)
+ val intent = ComposeActivityIntent(this, options)
startActivity(intent)
}
}
override fun onViewTag(tag: String) {
- val intent = StatusListActivity.newHashtagIntent(this, tag)
+ val intent = StatusListActivityIntent.hashtag(this, tag)
startActivityWithSlideInAnimation(intent)
}
override fun onViewAccount(id: String) {
- val intent = Intent(this, AccountActivity::class.java)
- intent.putExtra("id", id)
+ val intent = AccountActivityIntent(this, id)
startActivityWithSlideInAnimation(intent)
}
@@ -979,7 +983,7 @@ class AccountActivity :
}
R.id.action_report -> {
loadedAccount?.let { loadedAccount ->
- startActivity(ReportActivity.getIntent(this, viewModel.accountId, loadedAccount.username))
+ startActivity(ReportActivityIntent(this, viewModel.accountId, loadedAccount.username))
}
return true
}
@@ -999,14 +1003,6 @@ class AccountActivity :
}
companion object {
- private const val KEY_ACCOUNT_ID = "id"
private val argbEvaluator = ArgbEvaluator()
-
- @JvmStatic
- fun getIntent(context: Context, accountId: String): Intent {
- val intent = Intent(context, AccountActivity::class.java)
- intent.putExtra(KEY_ACCOUNT_ID, accountId)
- return intent
- }
}
}
diff --git a/app/src/main/java/app/pachli/components/account/media/AccountMediaFragment.kt b/app/src/main/java/app/pachli/components/account/media/AccountMediaFragment.kt
index bd7526c4c..e3b1c46d2 100644
--- a/app/src/main/java/app/pachli/components/account/media/AccountMediaFragment.kt
+++ b/app/src/main/java/app/pachli/components/account/media/AccountMediaFragment.kt
@@ -32,8 +32,9 @@ import androidx.paging.LoadState
import androidx.recyclerview.widget.GridLayoutManager
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout.OnRefreshListener
import app.pachli.R
-import app.pachli.ViewMediaActivity
import app.pachli.core.accounts.AccountManager
+import app.pachli.core.navigation.AttachmentViewData
+import app.pachli.core.navigation.ViewMediaActivityIntent
import app.pachli.core.network.model.Attachment
import app.pachli.core.preferences.PrefKeys
import app.pachli.core.preferences.SharedPreferencesRepository
@@ -43,7 +44,6 @@ import app.pachli.util.hide
import app.pachli.util.openLink
import app.pachli.util.show
import app.pachli.util.viewBinding
-import app.pachli.viewdata.AttachmentViewData
import com.google.android.material.color.MaterialColors
import com.mikepenz.iconics.IconicsDrawable
import com.mikepenz.iconics.typeface.library.googlematerial.GoogleMaterial
@@ -175,7 +175,7 @@ class AccountMediaFragment :
Attachment.Type.VIDEO,
Attachment.Type.AUDIO,
-> {
- val intent = ViewMediaActivity.newIntent(context, attachmentsFromSameStatus, currentIndex)
+ val intent = ViewMediaActivityIntent(requireContext(), attachmentsFromSameStatus, currentIndex)
if (activity != null) {
val url = selected.attachment.url
ViewCompat.setTransitionName(view, url)
diff --git a/app/src/main/java/app/pachli/components/account/media/AccountMediaGridAdapter.kt b/app/src/main/java/app/pachli/components/account/media/AccountMediaGridAdapter.kt
index 607d84f9a..4705fe945 100644
--- a/app/src/main/java/app/pachli/components/account/media/AccountMediaGridAdapter.kt
+++ b/app/src/main/java/app/pachli/components/account/media/AccountMediaGridAdapter.kt
@@ -11,6 +11,7 @@ import androidx.core.view.setPadding
import androidx.paging.PagingDataAdapter
import androidx.recyclerview.widget.DiffUtil
import app.pachli.R
+import app.pachli.core.navigation.AttachmentViewData
import app.pachli.core.network.model.Attachment
import app.pachli.databinding.ItemAccountMediaBinding
import app.pachli.util.BindingHolder
@@ -18,7 +19,6 @@ import app.pachli.util.decodeBlurHash
import app.pachli.util.getFormattedDescription
import app.pachli.util.hide
import app.pachli.util.show
-import app.pachli.viewdata.AttachmentViewData
import com.bumptech.glide.Glide
import com.google.android.material.color.MaterialColors
import java.util.Random
diff --git a/app/src/main/java/app/pachli/components/account/media/AccountMediaPagingSource.kt b/app/src/main/java/app/pachli/components/account/media/AccountMediaPagingSource.kt
index ae294cc93..ede39c679 100644
--- a/app/src/main/java/app/pachli/components/account/media/AccountMediaPagingSource.kt
+++ b/app/src/main/java/app/pachli/components/account/media/AccountMediaPagingSource.kt
@@ -18,7 +18,7 @@ package app.pachli.components.account.media
import androidx.paging.PagingSource
import androidx.paging.PagingState
-import app.pachli.viewdata.AttachmentViewData
+import app.pachli.core.navigation.AttachmentViewData
class AccountMediaPagingSource(
private val viewModel: AccountMediaViewModel,
diff --git a/app/src/main/java/app/pachli/components/account/media/AccountMediaRemoteMediator.kt b/app/src/main/java/app/pachli/components/account/media/AccountMediaRemoteMediator.kt
index c11524df2..dc3604f74 100644
--- a/app/src/main/java/app/pachli/components/account/media/AccountMediaRemoteMediator.kt
+++ b/app/src/main/java/app/pachli/components/account/media/AccountMediaRemoteMediator.kt
@@ -22,8 +22,8 @@ import androidx.paging.PagingState
import androidx.paging.RemoteMediator
import app.pachli.components.timeline.util.ifExpected
import app.pachli.core.database.model.AccountEntity
+import app.pachli.core.navigation.AttachmentViewData
import app.pachli.core.network.retrofit.MastodonApi
-import app.pachli.viewdata.AttachmentViewData
import retrofit2.HttpException
@OptIn(ExperimentalPagingApi::class)
diff --git a/app/src/main/java/app/pachli/components/account/media/AccountMediaViewModel.kt b/app/src/main/java/app/pachli/components/account/media/AccountMediaViewModel.kt
index 40c71b636..b591cc33f 100644
--- a/app/src/main/java/app/pachli/components/account/media/AccountMediaViewModel.kt
+++ b/app/src/main/java/app/pachli/components/account/media/AccountMediaViewModel.kt
@@ -23,8 +23,8 @@ import androidx.paging.Pager
import androidx.paging.PagingConfig
import androidx.paging.cachedIn
import app.pachli.core.accounts.AccountManager
+import app.pachli.core.navigation.AttachmentViewData
import app.pachli.core.network.retrofit.MastodonApi
-import app.pachli.viewdata.AttachmentViewData
import dagger.hilt.android.lifecycle.HiltViewModel
import javax.inject.Inject
diff --git a/app/src/main/java/app/pachli/components/accountlist/AccountListActivity.kt b/app/src/main/java/app/pachli/components/accountlist/AccountListActivity.kt
index 740c044ef..7cc90e4d6 100644
--- a/app/src/main/java/app/pachli/components/accountlist/AccountListActivity.kt
+++ b/app/src/main/java/app/pachli/components/accountlist/AccountListActivity.kt
@@ -16,18 +16,27 @@
package app.pachli.components.accountlist
-import android.content.Context
-import android.content.Intent
import android.os.Bundle
import androidx.fragment.app.commit
import app.pachli.BottomSheetActivity
import app.pachli.R
+import app.pachli.core.navigation.AccountListActivityIntent
+import app.pachli.core.navigation.AccountListActivityIntent.Kind.BLOCKS
+import app.pachli.core.navigation.AccountListActivityIntent.Kind.FAVOURITED
+import app.pachli.core.navigation.AccountListActivityIntent.Kind.FOLLOWERS
+import app.pachli.core.navigation.AccountListActivityIntent.Kind.FOLLOWS
+import app.pachli.core.navigation.AccountListActivityIntent.Kind.FOLLOW_REQUESTS
+import app.pachli.core.navigation.AccountListActivityIntent.Kind.MUTES
+import app.pachli.core.navigation.AccountListActivityIntent.Kind.REBLOGGED
import app.pachli.databinding.ActivityAccountListBinding
import app.pachli.interfaces.AppBarLayoutHost
import app.pachli.util.viewBinding
import com.google.android.material.appbar.AppBarLayout
import dagger.hilt.android.AndroidEntryPoint
+/**
+ * Show a list of accounts of a particular kind.
+ */
@AndroidEntryPoint
class AccountListActivity : BottomSheetActivity(), AppBarLayoutHost {
private val binding: ActivityAccountListBinding by viewBinding(ActivityAccountListBinding::inflate)
@@ -35,52 +44,30 @@ class AccountListActivity : BottomSheetActivity(), AppBarLayoutHost {
override val appBarLayout: AppBarLayout
get() = binding.includedToolbar.appbar
- enum class Type {
- FOLLOWS,
- FOLLOWERS,
- BLOCKS,
- MUTES,
- FOLLOW_REQUESTS,
- REBLOGGED,
- FAVOURITED,
- }
-
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(binding.root)
- val type = intent.getSerializableExtra(EXTRA_TYPE) as Type
- val id: String? = intent.getStringExtra(EXTRA_ID)
+ val kind = AccountListActivityIntent.getKind(intent)
+ val id = AccountListActivityIntent.getId(intent)
setSupportActionBar(binding.includedToolbar.toolbar)
supportActionBar?.apply {
- when (type) {
- Type.BLOCKS -> setTitle(R.string.title_blocks)
- Type.MUTES -> setTitle(R.string.title_mutes)
- Type.FOLLOW_REQUESTS -> setTitle(R.string.title_follow_requests)
- Type.FOLLOWERS -> setTitle(R.string.title_followers)
- Type.FOLLOWS -> setTitle(R.string.title_follows)
- Type.REBLOGGED -> setTitle(R.string.title_reblogged_by)
- Type.FAVOURITED -> setTitle(R.string.title_favourited_by)
+ when (kind) {
+ BLOCKS -> setTitle(R.string.title_blocks)
+ MUTES -> setTitle(R.string.title_mutes)
+ FOLLOW_REQUESTS -> setTitle(R.string.title_follow_requests)
+ FOLLOWERS -> setTitle(R.string.title_followers)
+ FOLLOWS -> setTitle(R.string.title_follows)
+ REBLOGGED -> setTitle(R.string.title_reblogged_by)
+ FAVOURITED -> setTitle(R.string.title_favourited_by)
}
setDisplayHomeAsUpEnabled(true)
setDisplayShowHomeEnabled(true)
}
supportFragmentManager.commit {
- replace(R.id.fragment_container, AccountListFragment.newInstance(type, id))
- }
- }
-
- companion object {
- private const val EXTRA_TYPE = "type"
- private const val EXTRA_ID = "id"
-
- fun newIntent(context: Context, type: Type, id: String? = null): Intent {
- return Intent(context, AccountListActivity::class.java).apply {
- putExtra(EXTRA_TYPE, type)
- putExtra(EXTRA_ID, id)
- }
+ replace(R.id.fragment_container, AccountListFragment.newInstance(kind, id))
}
}
}
diff --git a/app/src/main/java/app/pachli/components/accountlist/AccountListFragment.kt b/app/src/main/java/app/pachli/components/accountlist/AccountListFragment.kt
index 0df85b0b5..1fb2a7f1f 100644
--- a/app/src/main/java/app/pachli/components/accountlist/AccountListFragment.kt
+++ b/app/src/main/java/app/pachli/components/accountlist/AccountListFragment.kt
@@ -28,9 +28,6 @@ import app.pachli.BaseActivity
import app.pachli.BottomSheetActivity
import app.pachli.PostLookupFallbackBehavior
import app.pachli.R
-import app.pachli.StatusListActivity
-import app.pachli.components.account.AccountActivity
-import app.pachli.components.accountlist.AccountListActivity.Type
import app.pachli.components.accountlist.adapter.AccountAdapter
import app.pachli.components.accountlist.adapter.BlocksAdapter
import app.pachli.components.accountlist.adapter.FollowAdapter
@@ -38,6 +35,16 @@ import app.pachli.components.accountlist.adapter.FollowRequestsAdapter
import app.pachli.components.accountlist.adapter.FollowRequestsHeaderAdapter
import app.pachli.components.accountlist.adapter.MutesAdapter
import app.pachli.core.accounts.AccountManager
+import app.pachli.core.navigation.AccountActivityIntent
+import app.pachli.core.navigation.AccountListActivityIntent.Kind
+import app.pachli.core.navigation.AccountListActivityIntent.Kind.BLOCKS
+import app.pachli.core.navigation.AccountListActivityIntent.Kind.FAVOURITED
+import app.pachli.core.navigation.AccountListActivityIntent.Kind.FOLLOWERS
+import app.pachli.core.navigation.AccountListActivityIntent.Kind.FOLLOWS
+import app.pachli.core.navigation.AccountListActivityIntent.Kind.FOLLOW_REQUESTS
+import app.pachli.core.navigation.AccountListActivityIntent.Kind.MUTES
+import app.pachli.core.navigation.AccountListActivityIntent.Kind.REBLOGGED
+import app.pachli.core.navigation.StatusListActivityIntent
import app.pachli.core.network.model.HttpHeaderLink
import app.pachli.core.network.model.Relationship
import app.pachli.core.network.model.TimelineAccount
@@ -80,7 +87,7 @@ class AccountListFragment :
private val binding by viewBinding(FragmentAccountListBinding::bind)
- private lateinit var type: Type
+ private lateinit var kind: Kind
private var id: String? = null
private lateinit var scrollListener: EndlessOnScrollListener
@@ -90,7 +97,7 @@ class AccountListFragment :
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
- type = requireArguments().getSerializable(ARG_TYPE) as Type
+ kind = requireArguments().getSerializable(ARG_KIND) as Kind
id = requireArguments().getString(ARG_ID)
}
@@ -112,10 +119,10 @@ class AccountListFragment :
val activeAccount = accountManager.activeAccount!!
- adapter = when (type) {
- Type.BLOCKS -> BlocksAdapter(this, animateAvatar, animateEmojis, showBotOverlay)
- Type.MUTES -> MutesAdapter(this, animateAvatar, animateEmojis, showBotOverlay)
- Type.FOLLOW_REQUESTS -> {
+ adapter = when (kind) {
+ BLOCKS -> BlocksAdapter(this, animateAvatar, animateEmojis, showBotOverlay)
+ MUTES -> MutesAdapter(this, animateAvatar, animateEmojis, showBotOverlay)
+ FOLLOW_REQUESTS -> {
val headerAdapter = FollowRequestsHeaderAdapter(
instanceName = activeAccount.domain,
accountLocked = activeAccount.locked,
@@ -151,12 +158,12 @@ class AccountListFragment :
override fun onViewTag(tag: String) {
(activity as BaseActivity?)
- ?.startActivityWithSlideInAnimation(StatusListActivity.newHashtagIntent(requireContext(), tag))
+ ?.startActivityWithSlideInAnimation(StatusListActivityIntent.hashtag(requireContext(), tag))
}
override fun onViewAccount(id: String) {
(activity as BaseActivity?)?.let {
- val intent = AccountActivity.getIntent(it, id)
+ val intent = AccountActivityIntent(it, id)
it.startActivityWithSlideInAnimation(intent)
}
}
@@ -283,31 +290,31 @@ class AccountListFragment :
}
private suspend fun getFetchCallByListType(fromId: String?): Response> {
- return when (type) {
- Type.FOLLOWS -> {
- val accountId = requireId(type, id)
+ return when (kind) {
+ FOLLOWS -> {
+ val accountId = requireId(kind, id)
api.accountFollowing(accountId, fromId)
}
- Type.FOLLOWERS -> {
- val accountId = requireId(type, id)
+ FOLLOWERS -> {
+ val accountId = requireId(kind, id)
api.accountFollowers(accountId, fromId)
}
- Type.BLOCKS -> api.blocks(fromId)
- Type.MUTES -> api.mutes(fromId)
- Type.FOLLOW_REQUESTS -> api.followRequests(fromId)
- Type.REBLOGGED -> {
- val statusId = requireId(type, id)
+ BLOCKS -> api.blocks(fromId)
+ MUTES -> api.mutes(fromId)
+ FOLLOW_REQUESTS -> api.followRequests(fromId)
+ REBLOGGED -> {
+ val statusId = requireId(kind, id)
api.statusRebloggedBy(statusId, fromId)
}
- Type.FAVOURITED -> {
- val statusId = requireId(type, id)
+ FAVOURITED -> {
+ val statusId = requireId(kind, id)
api.statusFavouritedBy(statusId, fromId)
}
}
}
- private fun requireId(type: Type, id: String?): String {
- return requireNotNull(id) { "id must not be null for type " + type.name }
+ private fun requireId(kind: Kind, id: String?): String {
+ return requireNotNull(id) { "id must not be null for kind " + kind.name }
}
private fun fetchAccounts(fromId: String? = null) {
@@ -410,13 +417,13 @@ class AccountListFragment :
}
companion object {
- private const val ARG_TYPE = "type"
+ private const val ARG_KIND = "kind"
private const val ARG_ID = "id"
- fun newInstance(type: Type, id: String? = null): AccountListFragment {
+ fun newInstance(kind: Kind, id: String? = null): AccountListFragment {
return AccountListFragment().apply {
arguments = Bundle(3).apply {
- putSerializable(ARG_TYPE, type)
+ putSerializable(ARG_KIND, kind)
putString(ARG_ID, id)
}
}
diff --git a/app/src/main/java/app/pachli/components/announcements/AnnouncementsActivity.kt b/app/src/main/java/app/pachli/components/announcements/AnnouncementsActivity.kt
index 7c0387535..14b21238e 100644
--- a/app/src/main/java/app/pachli/components/announcements/AnnouncementsActivity.kt
+++ b/app/src/main/java/app/pachli/components/announcements/AnnouncementsActivity.kt
@@ -16,8 +16,6 @@
package app.pachli.components.announcements
-import android.content.Context
-import android.content.Intent
import android.os.Bundle
import android.view.Menu
import android.view.MenuInflater
@@ -29,9 +27,9 @@ import androidx.core.view.MenuProvider
import androidx.recyclerview.widget.LinearLayoutManager
import app.pachli.BottomSheetActivity
import app.pachli.R
-import app.pachli.StatusListActivity
import app.pachli.adapter.EmojiAdapter
import app.pachli.adapter.OnEmojiSelectedListener
+import app.pachli.core.navigation.StatusListActivityIntent
import app.pachli.core.preferences.PrefKeys
import app.pachli.databinding.ActivityAnnouncementsBinding
import app.pachli.util.Error
@@ -185,7 +183,7 @@ class AnnouncementsActivity :
}
override fun onViewTag(tag: String) {
- val intent = StatusListActivity.newHashtagIntent(this, tag)
+ val intent = StatusListActivityIntent.hashtag(this, tag)
startActivityWithSlideInAnimation(intent)
}
@@ -196,8 +194,4 @@ class AnnouncementsActivity :
override fun onViewUrl(url: String) {
viewUrl(url)
}
-
- companion object {
- fun newIntent(context: Context) = Intent(context, AnnouncementsActivity::class.java)
- }
}
diff --git a/app/src/main/java/app/pachli/components/compose/ComposeActivity.kt b/app/src/main/java/app/pachli/components/compose/ComposeActivity.kt
index 17d5fda99..87cd79cf4 100644
--- a/app/src/main/java/app/pachli/components/compose/ComposeActivity.kt
+++ b/app/src/main/java/app/pachli/components/compose/ComposeActivity.kt
@@ -19,7 +19,6 @@ package app.pachli.components.compose
import android.Manifest
import android.app.ProgressDialog
import android.content.ClipData
-import android.content.Context
import android.content.Intent
import android.content.pm.PackageManager
import android.graphics.Bitmap
@@ -28,7 +27,6 @@ import android.graphics.PorterDuffColorFilter
import android.net.Uri
import android.os.Build
import android.os.Bundle
-import android.os.Parcelable
import android.provider.MediaStore
import android.text.Spanned
import android.text.style.URLSpan
@@ -77,10 +75,11 @@ import app.pachli.components.compose.view.ComposeOptionsListener
import app.pachli.components.compose.view.ComposeScheduleView
import app.pachli.components.instanceinfo.InstanceInfoRepository
import app.pachli.core.database.model.AccountEntity
-import app.pachli.core.database.model.DraftAttachment
+import app.pachli.core.navigation.ComposeActivityIntent
+import app.pachli.core.navigation.ComposeActivityIntent.ComposeOptions
+import app.pachli.core.navigation.ComposeActivityIntent.ComposeOptions.InitialCursorPosition
import app.pachli.core.network.model.Attachment
import app.pachli.core.network.model.Emoji
-import app.pachli.core.network.model.NewPoll
import app.pachli.core.network.model.Status
import app.pachli.core.preferences.PrefKeys
import app.pachli.core.preferences.PrefKeys.APP_THEME
@@ -116,7 +115,6 @@ import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.launch
-import kotlinx.parcelize.Parcelize
import timber.log.Timber
import java.io.File
import java.io.IOException
@@ -125,6 +123,10 @@ import java.util.Locale
import kotlin.math.max
import kotlin.math.min
+/**
+ * Compose a status, either by creating one from scratch, or by editing an existing
+ * status, draft, or scheduled status.
+ */
@AndroidEntryPoint
class ComposeActivity :
BaseActivity(),
@@ -233,7 +235,7 @@ class ComposeActivity :
/* If the composer is started up as a reply to another post, override the "starting" state
* based on what the intent from the reply request passes. */
- val composeOptions: ComposeOptions? = IntentCompat.getParcelableExtra(intent, COMPOSE_OPTIONS_EXTRA, ComposeOptions::class.java)
+ val composeOptions: ComposeOptions? = ComposeActivityIntent.getOptions(intent)
viewModel.setup(composeOptions)
setupButtons()
@@ -1297,60 +1299,6 @@ class ComposeActivity :
viewModel.updateDescription(localId, description)
}
- /**
- * Status' kind. This particularly affects how the status is handled if the user
- * backs out of the edit.
- */
- enum class ComposeKind {
- /** Status is new */
- NEW,
-
- /** Editing a posted status */
- EDIT_POSTED,
-
- /** Editing a status started as an existing draft */
- EDIT_DRAFT,
-
- /** Editing an an existing scheduled status */
- EDIT_SCHEDULED,
- }
-
- /**
- * Initial position of the cursor in EditText when the compose button is clicked
- * in a hashtag timeline
- */
- enum class InitialCursorPosition {
- START,
- END,
- }
-
- @Parcelize
- data class ComposeOptions(
- // Let's keep fields var until all consumers are Kotlin
- var scheduledTootId: String? = null,
- var draftId: Int? = null,
- var content: String? = null,
- var mediaUrls: List? = null,
- var mediaDescriptions: List? = null,
- var mentionedUsernames: Set? = null,
- var inReplyToId: String? = null,
- var replyVisibility: Status.Visibility? = null,
- var visibility: Status.Visibility? = null,
- var contentWarning: String? = null,
- var replyingStatusAuthor: String? = null,
- var replyingStatusContent: String? = null,
- var mediaAttachments: List? = null,
- var draftAttachments: List? = null,
- var scheduledAt: String? = null,
- var sensitive: Boolean? = null,
- var poll: NewPoll? = null,
- var modifiedInitialState: Boolean? = null,
- var language: String? = null,
- var statusId: String? = null,
- var kind: ComposeKind? = null,
- var initialCursorPosition: InitialCursorPosition = InitialCursorPosition.END,
- ) : Parcelable
-
companion object {
private const val PERMISSIONS_REQUEST_READ_EXTERNAL_STORAGE = 1
@@ -1360,20 +1308,6 @@ class ComposeActivity :
private const val SCHEDULED_TIME_KEY = "SCHEDULE"
private const val CONTENT_WARNING_VISIBLE_KEY = "CONTENT_WARNING_VISIBLE"
- /**
- * @param options ComposeOptions to configure the ComposeActivity
- * @return an Intent to start the ComposeActivity
- */
- @JvmStatic
- fun startIntent(
- context: Context,
- options: ComposeOptions,
- ): Intent {
- return Intent(context, ComposeActivity::class.java).apply {
- putExtra(COMPOSE_OPTIONS_EXTRA, options)
- }
- }
-
fun canHandleMimeType(mimeType: String?): Boolean {
return mimeType != null && (mimeType.startsWith("image/") || mimeType.startsWith("video/") || mimeType.startsWith("audio/") || mimeType == "text/plain")
}
diff --git a/app/src/main/java/app/pachli/components/compose/ComposeViewModel.kt b/app/src/main/java/app/pachli/components/compose/ComposeViewModel.kt
index e34134103..d05a6bdf6 100644
--- a/app/src/main/java/app/pachli/components/compose/ComposeViewModel.kt
+++ b/app/src/main/java/app/pachli/components/compose/ComposeViewModel.kt
@@ -20,7 +20,6 @@ import android.net.Uri
import androidx.core.net.toUri
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
-import app.pachli.components.compose.ComposeActivity.ComposeKind
import app.pachli.components.compose.ComposeActivity.QueuedMedia
import app.pachli.components.compose.ComposeAutoCompleteAdapter.AutocompleteResult
import app.pachli.components.drafts.DraftHelper
@@ -29,6 +28,8 @@ import app.pachli.components.instanceinfo.InstanceInfoRepository
import app.pachli.components.search.SearchType
import app.pachli.core.accounts.AccountManager
import app.pachli.core.common.string.randomAlphanumericString
+import app.pachli.core.navigation.ComposeActivityIntent.ComposeOptions
+import app.pachli.core.navigation.ComposeActivityIntent.ComposeOptions.ComposeKind
import app.pachli.core.network.model.Attachment
import app.pachli.core.network.model.Emoji
import app.pachli.core.network.model.NewPoll
@@ -412,7 +413,7 @@ class ComposeViewModel @Inject constructor(
}
}
- fun setup(composeOptions: ComposeActivity.ComposeOptions?) {
+ fun setup(composeOptions: ComposeOptions?) {
if (setupComplete) {
return
}
diff --git a/app/src/main/java/app/pachli/components/conversation/ConversationsFragment.kt b/app/src/main/java/app/pachli/components/conversation/ConversationsFragment.kt
index 3c4b4e59a..612471eea 100644
--- a/app/src/main/java/app/pachli/components/conversation/ConversationsFragment.kt
+++ b/app/src/main/java/app/pachli/components/conversation/ConversationsFragment.kt
@@ -36,10 +36,11 @@ import androidx.recyclerview.widget.RecyclerView
import androidx.recyclerview.widget.SimpleItemAnimator
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout.OnRefreshListener
import app.pachli.R
-import app.pachli.StatusListActivity
import app.pachli.adapter.StatusBaseViewHolder
import app.pachli.appstore.EventHub
-import app.pachli.components.account.AccountActivity
+import app.pachli.core.navigation.AccountActivityIntent
+import app.pachli.core.navigation.AttachmentViewData
+import app.pachli.core.navigation.StatusListActivityIntent
import app.pachli.core.preferences.PrefKeys
import app.pachli.core.preferences.SharedPreferencesRepository
import app.pachli.databinding.FragmentTimelineBinding
@@ -52,7 +53,6 @@ import app.pachli.util.hide
import app.pachli.util.show
import app.pachli.util.viewBinding
import app.pachli.util.visible
-import app.pachli.viewdata.AttachmentViewData
import at.connyduck.sparkbutton.helpers.Utils
import com.google.android.material.color.MaterialColors
import com.google.android.material.divider.MaterialDividerItemDecoration
@@ -327,12 +327,12 @@ class ConversationsFragment :
}
override fun onViewAccount(id: String) {
- val intent = AccountActivity.getIntent(requireContext(), id)
+ val intent = AccountActivityIntent(requireContext(), id)
startActivity(intent)
}
override fun onViewTag(tag: String) {
- val intent = StatusListActivity.newHashtagIntent(requireContext(), tag)
+ val intent = StatusListActivityIntent.hashtag(requireContext(), tag)
startActivity(intent)
}
diff --git a/app/src/main/java/app/pachli/components/drafts/DraftsActivity.kt b/app/src/main/java/app/pachli/components/drafts/DraftsActivity.kt
index eb3b97540..ff28ae210 100644
--- a/app/src/main/java/app/pachli/components/drafts/DraftsActivity.kt
+++ b/app/src/main/java/app/pachli/components/drafts/DraftsActivity.kt
@@ -17,7 +17,6 @@
package app.pachli.components.drafts
import android.content.Context
-import android.content.Intent
import android.os.Bundle
import android.widget.LinearLayout
import android.widget.Toast
@@ -26,8 +25,9 @@ import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.LinearLayoutManager
import app.pachli.BaseActivity
import app.pachli.R
-import app.pachli.components.compose.ComposeActivity
import app.pachli.core.database.model.DraftEntity
+import app.pachli.core.navigation.ComposeActivityIntent
+import app.pachli.core.navigation.ComposeActivityIntent.ComposeOptions
import app.pachli.core.network.parseAsMastodonHtml
import app.pachli.databinding.ActivityDraftsBinding
import app.pachli.db.DraftsAlert
@@ -106,7 +106,7 @@ class DraftsActivity : BaseActivity(), DraftActionListener {
viewModel.getStatus(draft.inReplyToId!!)
.fold(
{ status ->
- val composeOptions = ComposeActivity.ComposeOptions(
+ val composeOptions = ComposeOptions(
draftId = draft.id,
content = draft.content,
contentWarning = draft.contentWarning,
@@ -120,12 +120,12 @@ class DraftsActivity : BaseActivity(), DraftActionListener {
scheduledAt = draft.scheduledAt,
language = draft.language,
statusId = draft.statusId,
- kind = ComposeActivity.ComposeKind.EDIT_DRAFT,
+ kind = ComposeOptions.ComposeKind.EDIT_DRAFT,
)
bottomSheet.state = BottomSheetBehavior.STATE_HIDDEN
- startActivity(ComposeActivity.startIntent(context, composeOptions))
+ startActivity(ComposeActivityIntent(context, composeOptions))
},
{ throwable ->
bottomSheet.state = BottomSheetBehavior.STATE_HIDDEN
@@ -147,7 +147,7 @@ class DraftsActivity : BaseActivity(), DraftActionListener {
}
private fun openDraftWithoutReply(draft: DraftEntity) {
- val composeOptions = ComposeActivity.ComposeOptions(
+ val composeOptions = ComposeOptions(
draftId = draft.id,
content = draft.content,
contentWarning = draft.contentWarning,
@@ -158,10 +158,10 @@ class DraftsActivity : BaseActivity(), DraftActionListener {
scheduledAt = draft.scheduledAt,
language = draft.language,
statusId = draft.statusId,
- kind = ComposeActivity.ComposeKind.EDIT_DRAFT,
+ kind = ComposeOptions.ComposeKind.EDIT_DRAFT,
)
- startActivity(ComposeActivity.startIntent(this, composeOptions))
+ startActivity(ComposeActivityIntent(this, composeOptions))
}
override fun onDeleteDraft(draft: DraftEntity) {
@@ -172,8 +172,4 @@ class DraftsActivity : BaseActivity(), DraftActionListener {
}
.show()
}
-
- companion object {
- fun newIntent(context: Context) = Intent(context, DraftsActivity::class.java)
- }
}
diff --git a/app/src/main/java/app/pachli/components/filters/EditFilterActivity.kt b/app/src/main/java/app/pachli/components/filters/EditFilterActivity.kt
index 3e81c2fc4..1a7493bbd 100644
--- a/app/src/main/java/app/pachli/components/filters/EditFilterActivity.kt
+++ b/app/src/main/java/app/pachli/components/filters/EditFilterActivity.kt
@@ -8,13 +8,13 @@ import android.widget.AdapterView
import android.widget.ArrayAdapter
import androidx.activity.viewModels
import androidx.appcompat.app.AlertDialog
-import androidx.core.content.IntentCompat
import androidx.core.view.size
import androidx.core.widget.doAfterTextChanged
import androidx.lifecycle.lifecycleScope
import app.pachli.BaseActivity
import app.pachli.R
import app.pachli.appstore.EventHub
+import app.pachli.core.navigation.EditFilterActivityIntent
import app.pachli.core.network.model.Filter
import app.pachli.core.network.model.FilterKeyword
import app.pachli.core.network.retrofit.MastodonApi
@@ -32,6 +32,9 @@ import retrofit2.HttpException
import java.util.Date
import javax.inject.Inject
+/**
+ * Edit a single server-side filter.
+ */
@AndroidEntryPoint
class EditFilterActivity : BaseActivity() {
@Inject
@@ -50,7 +53,7 @@ class EditFilterActivity : BaseActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
- originalFilter = IntentCompat.getParcelableExtra(intent, FILTER_TO_EDIT, Filter::class.java)
+ originalFilter = EditFilterActivityIntent.getFilter(intent)
filter = originalFilter ?: Filter("", "", listOf(), null, Filter.Action.WARN.action, listOf())
binding.apply {
contextSwitches = mapOf(
@@ -295,8 +298,6 @@ class EditFilterActivity : BaseActivity() {
}
companion object {
- const val FILTER_TO_EDIT = "FilterToEdit"
-
// Mastodon *stores* the absolute date in the filter,
// but create/edit take a number of seconds (relative to the time the operation is posted)
fun getSecondsForDurationIndex(index: Int, context: Context?, default: Date? = null): Int? {
diff --git a/app/src/main/java/app/pachli/components/filters/FiltersActivity.kt b/app/src/main/java/app/pachli/components/filters/FiltersActivity.kt
index 0fb6365c1..7d601c904 100644
--- a/app/src/main/java/app/pachli/components/filters/FiltersActivity.kt
+++ b/app/src/main/java/app/pachli/components/filters/FiltersActivity.kt
@@ -1,12 +1,12 @@
package app.pachli.components.filters
import android.content.DialogInterface.BUTTON_POSITIVE
-import android.content.Intent
import android.os.Bundle
import androidx.activity.viewModels
import androidx.lifecycle.lifecycleScope
import app.pachli.BaseActivity
import app.pachli.R
+import app.pachli.core.navigation.EditFilterActivityIntent
import app.pachli.core.network.model.Filter
import app.pachli.databinding.ActivityFiltersBinding
import app.pachli.util.hide
@@ -95,11 +95,7 @@ class FiltersActivity : BaseActivity(), FiltersListener {
}
private fun launchEditFilterActivity(filter: Filter? = null) {
- val intent = Intent(this, EditFilterActivity::class.java).apply {
- if (filter != null) {
- putExtra(EditFilterActivity.FILTER_TO_EDIT, filter)
- }
- }
+ val intent = EditFilterActivityIntent(this, filter)
startActivity(intent)
overridePendingTransition(R.anim.slide_from_right, R.anim.slide_to_left)
}
diff --git a/app/src/main/java/app/pachli/components/login/LoginActivity.kt b/app/src/main/java/app/pachli/components/login/LoginActivity.kt
index c38d5462c..13c893c08 100644
--- a/app/src/main/java/app/pachli/components/login/LoginActivity.kt
+++ b/app/src/main/java/app/pachli/components/login/LoginActivity.kt
@@ -30,8 +30,9 @@ import androidx.core.net.toUri
import androidx.lifecycle.lifecycleScope
import app.pachli.BaseActivity
import app.pachli.BuildConfig
-import app.pachli.MainActivity
import app.pachli.R
+import app.pachli.core.navigation.LoginActivityIntent
+import app.pachli.core.navigation.MainActivityIntent
import app.pachli.core.network.model.AccessToken
import app.pachli.core.network.retrofit.MastodonApi
import app.pachli.core.preferences.getNonNullString
@@ -48,7 +49,11 @@ import okhttp3.HttpUrl
import timber.log.Timber
import javax.inject.Inject
-/** Main login page, the first thing that users see. Has prompt for instance and login button. */
+/**
+ * Main login page, the first thing that users see.
+ *
+ * Has prompt for instance and login button.
+ */
@AndroidEntryPoint
class LoginActivity : BaseActivity() {
@@ -318,7 +323,7 @@ class LoginActivity : BaseActivity() {
newAccount = newAccount,
)
- val intent = Intent(this, MainActivity::class.java)
+ val intent = MainActivityIntent(this)
intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
startActivity(intent)
finish()
@@ -343,33 +348,19 @@ class LoginActivity : BaseActivity() {
}
private fun isAdditionalLogin(): Boolean {
- return intent.getIntExtra(LOGIN_MODE, MODE_DEFAULT) == MODE_ADDITIONAL_LOGIN
+ return LoginActivityIntent.getLoginMode(intent) == LoginActivityIntent.LoginMode.ADDITIONAL_LOGIN
}
private fun isAccountMigration(): Boolean {
- return intent.getIntExtra(LOGIN_MODE, MODE_DEFAULT) == MODE_MIGRATION
+ return LoginActivityIntent.getLoginMode(intent) == LoginActivityIntent.LoginMode.MIGRATION
}
companion object {
private const val OAUTH_SCOPES = "read write follow push"
- private const val LOGIN_MODE = "LOGIN_MODE"
private const val DOMAIN = "domain"
private const val CLIENT_ID = "clientId"
private const val CLIENT_SECRET = "clientSecret"
- const val MODE_DEFAULT = 0
- const val MODE_ADDITIONAL_LOGIN = 1
-
- // "Migration" is used to update the OAuth scope granted to the client
- const val MODE_MIGRATION = 2
-
- @JvmStatic
- fun getIntent(context: Context, mode: Int): Intent {
- val loginIntent = Intent(context, LoginActivity::class.java)
- loginIntent.putExtra(LOGIN_MODE, mode)
- return loginIntent
- }
-
/** Make sure the user-entered text is just a fully-qualified domain name. */
private fun canonicalizeDomain(domain: String): String {
// Strip any schemes out.
diff --git a/app/src/main/java/app/pachli/components/login/LoginWebViewActivity.kt b/app/src/main/java/app/pachli/components/login/LoginWebViewActivity.kt
index c50d1d25e..51826581c 100644
--- a/app/src/main/java/app/pachli/components/login/LoginWebViewActivity.kt
+++ b/app/src/main/java/app/pachli/components/login/LoginWebViewActivity.kt
@@ -39,6 +39,7 @@ import androidx.lifecycle.lifecycleScope
import app.pachli.BaseActivity
import app.pachli.BuildConfig
import app.pachli.R
+import app.pachli.core.navigation.LoginWebViewActivityIntent
import app.pachli.databinding.ActivityLoginWebviewBinding
import app.pachli.util.hide
import app.pachli.util.viewBinding
@@ -51,7 +52,7 @@ import timber.log.Timber
/** Contract for starting [LoginWebViewActivity]. */
class OauthLogin : ActivityResultContract() {
override fun createIntent(context: Context, input: LoginData): Intent {
- val intent = Intent(context, LoginWebViewActivity::class.java)
+ val intent = LoginWebViewActivityIntent(context)
intent.putExtra(DATA_EXTRA, input)
return intent
}
diff --git a/app/src/main/java/app/pachli/components/notifications/NotificationHelper.kt b/app/src/main/java/app/pachli/components/notifications/NotificationHelper.kt
index f1a6f1c24..d797a65f2 100644
--- a/app/src/main/java/app/pachli/components/notifications/NotificationHelper.kt
+++ b/app/src/main/java/app/pachli/components/notifications/NotificationHelper.kt
@@ -16,6 +16,7 @@
*/
package app.pachli.components.notifications
+import android.annotation.SuppressLint
import android.app.NotificationChannel
import android.app.NotificationChannelGroup
import android.app.NotificationManager
@@ -44,10 +45,10 @@ import app.pachli.MainActivity
import app.pachli.MainActivity.Companion.composeIntent
import app.pachli.MainActivity.Companion.openNotificationIntent
import app.pachli.R
-import app.pachli.components.compose.ComposeActivity
import app.pachli.core.accounts.AccountManager
import app.pachli.core.common.string.unicodeWrap
import app.pachli.core.database.model.AccountEntity
+import app.pachli.core.navigation.ComposeActivityIntent.ComposeOptions
import app.pachli.core.network.model.Notification
import app.pachli.core.network.parseAsMastodonHtml
import app.pachli.receiver.SendStatusBroadcastReceiver
@@ -382,6 +383,9 @@ private fun getStatusReplyIntent(
}
mentionedUsernames.removeAll(setOf(account.username))
mentionedUsernames = ArrayList(LinkedHashSet(mentionedUsernames))
+
+ // TODO: Revisit suppressing this when this file is moved
+ @SuppressLint("IntentDetector")
val replyIntent = Intent(context, SendStatusBroadcastReceiver::class.java)
.setAction(REPLY_ACTION)
.putExtra(KEY_SENDER_ACCOUNT_ID, account.id)
@@ -417,16 +421,17 @@ private fun getStatusComposeIntent(
mentionedUsernames.add(mentionedUsername)
}
}
- val composeOptions = ComposeActivity.ComposeOptions()
- composeOptions.inReplyToId = inReplyToId
- composeOptions.replyVisibility = replyVisibility
- composeOptions.contentWarning = contentWarning
- composeOptions.replyingStatusAuthor = citedLocalAuthor
- composeOptions.replyingStatusContent = citedText
- composeOptions.mentionedUsernames = mentionedUsernames
- composeOptions.modifiedInitialState = true
- composeOptions.language = language
- composeOptions.kind = ComposeActivity.ComposeKind.NEW
+ val composeOptions = ComposeOptions(
+ inReplyToId = inReplyToId,
+ replyVisibility = replyVisibility,
+ contentWarning = contentWarning,
+ replyingStatusAuthor = citedLocalAuthor,
+ replyingStatusContent = citedText,
+ mentionedUsernames = mentionedUsernames,
+ modifiedInitialState = true,
+ language = language,
+ kind = ComposeOptions.ComposeKind.NEW,
+ )
val composeIntent =
composeIntent(context, composeOptions, account.id, body.id, account.id.toInt())
composeIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
diff --git a/app/src/main/java/app/pachli/components/notifications/NotificationsFragment.kt b/app/src/main/java/app/pachli/components/notifications/NotificationsFragment.kt
index fdaa60824..5774e1967 100644
--- a/app/src/main/java/app/pachli/components/notifications/NotificationsFragment.kt
+++ b/app/src/main/java/app/pachli/components/notifications/NotificationsFragment.kt
@@ -46,6 +46,7 @@ import androidx.swiperefreshlayout.widget.SwipeRefreshLayout.OnRefreshListener
import app.pachli.R
import app.pachli.adapter.StatusBaseViewHolder
import app.pachli.components.timeline.TimelineLoadStateAdapter
+import app.pachli.core.navigation.AttachmentViewData.Companion.list
import app.pachli.core.network.model.Filter
import app.pachli.core.network.model.Notification
import app.pachli.core.network.model.Status
@@ -63,7 +64,6 @@ import app.pachli.util.openLink
import app.pachli.util.show
import app.pachli.util.viewBinding
import app.pachli.util.visible
-import app.pachli.viewdata.AttachmentViewData.Companion.list
import app.pachli.viewdata.NotificationViewData
import at.connyduck.sparkbutton.helpers.Utils
import com.google.android.material.color.MaterialColors
diff --git a/app/src/main/java/app/pachli/components/notifications/PushNotificationHelper.kt b/app/src/main/java/app/pachli/components/notifications/PushNotificationHelper.kt
index 528118d68..cc3674a5d 100644
--- a/app/src/main/java/app/pachli/components/notifications/PushNotificationHelper.kt
+++ b/app/src/main/java/app/pachli/components/notifications/PushNotificationHelper.kt
@@ -22,9 +22,10 @@ import android.os.Build
import android.view.View
import androidx.appcompat.app.AlertDialog
import app.pachli.R
-import app.pachli.components.login.LoginActivity
import app.pachli.core.accounts.AccountManager
import app.pachli.core.database.model.AccountEntity
+import app.pachli.core.navigation.LoginActivityIntent
+import app.pachli.core.navigation.LoginActivityIntent.LoginMode
import app.pachli.core.network.model.Notification
import app.pachli.core.network.retrofit.MastodonApi
import app.pachli.core.preferences.SharedPreferencesRepository
@@ -78,7 +79,12 @@ private fun showMigrationExplanationDialog(
if (currentAccountNeedsMigration(accountManager)) {
setMessage(R.string.dialog_push_notification_migration)
setPositiveButton(R.string.title_migration_relogin) { _, _ ->
- context.startActivity(LoginActivity.getIntent(context, LoginActivity.MODE_MIGRATION))
+ context.startActivity(
+ LoginActivityIntent(
+ context,
+ LoginMode.MIGRATION,
+ ),
+ )
}
} else {
setMessage(R.string.dialog_push_notification_migration_other_accounts)
diff --git a/app/src/main/java/app/pachli/components/preference/AccountPreferencesFragment.kt b/app/src/main/java/app/pachli/components/preference/AccountPreferencesFragment.kt
index e7b31acb5..fd81905c6 100644
--- a/app/src/main/java/app/pachli/components/preference/AccountPreferencesFragment.kt
+++ b/app/src/main/java/app/pachli/components/preference/AccountPreferencesFragment.kt
@@ -24,15 +24,18 @@ import androidx.preference.PreferenceFragmentCompat
import app.pachli.BaseActivity
import app.pachli.BuildConfig
import app.pachli.R
-import app.pachli.TabPreferenceActivity
import app.pachli.appstore.EventHub
-import app.pachli.components.accountlist.AccountListActivity
-import app.pachli.components.filters.FiltersActivity
-import app.pachli.components.followedtags.FollowedTagsActivity
-import app.pachli.components.instancemute.InstanceListActivity
-import app.pachli.components.login.LoginActivity
import app.pachli.components.notifications.currentAccountNeedsMigration
import app.pachli.core.accounts.AccountManager
+import app.pachli.core.navigation.AccountListActivityIntent
+import app.pachli.core.navigation.FiltersActivityIntent
+import app.pachli.core.navigation.FollowedTagsActivityIntent
+import app.pachli.core.navigation.InstanceListActivityIntent
+import app.pachli.core.navigation.LoginActivityIntent
+import app.pachli.core.navigation.LoginActivityIntent.LoginMode
+import app.pachli.core.navigation.PreferencesActivityIntent
+import app.pachli.core.navigation.PreferencesActivityIntent.PreferenceScreen
+import app.pachli.core.navigation.TabPreferenceActivityIntent
import app.pachli.core.network.model.Account
import app.pachli.core.network.model.Status
import app.pachli.core.network.retrofit.MastodonApi
@@ -90,7 +93,7 @@ class AccountPreferencesFragment : PreferenceFragmentCompat() {
setTitle(R.string.title_tab_preferences)
setIcon(R.drawable.ic_tabs)
setOnPreferenceClickListener {
- val intent = Intent(context, TabPreferenceActivity::class.java)
+ val intent = TabPreferenceActivityIntent(context)
activity?.startActivity(intent)
activity?.overridePendingTransition(
R.anim.slide_from_right,
@@ -104,7 +107,7 @@ class AccountPreferencesFragment : PreferenceFragmentCompat() {
setTitle(R.string.title_followed_hashtags)
setIcon(R.drawable.ic_hashtag)
setOnPreferenceClickListener {
- val intent = Intent(context, FollowedTagsActivity::class.java)
+ val intent = FollowedTagsActivityIntent(context)
activity?.startActivity(intent)
activity?.overridePendingTransition(
R.anim.slide_from_right,
@@ -118,8 +121,7 @@ class AccountPreferencesFragment : PreferenceFragmentCompat() {
setTitle(R.string.action_view_mutes)
setIcon(R.drawable.ic_mute_24dp)
setOnPreferenceClickListener {
- val intent = Intent(context, AccountListActivity::class.java)
- intent.putExtra("type", AccountListActivity.Type.MUTES)
+ val intent = AccountListActivityIntent(context, AccountListActivityIntent.Kind.MUTES)
activity?.startActivity(intent)
activity?.overridePendingTransition(
R.anim.slide_from_right,
@@ -133,8 +135,7 @@ class AccountPreferencesFragment : PreferenceFragmentCompat() {
setTitle(R.string.action_view_blocks)
icon = makeIcon(GoogleMaterial.Icon.gmd_block)
setOnPreferenceClickListener {
- val intent = Intent(context, AccountListActivity::class.java)
- intent.putExtra("type", AccountListActivity.Type.BLOCKS)
+ val intent = AccountListActivityIntent(context, AccountListActivityIntent.Kind.BLOCKS)
activity?.startActivity(intent)
activity?.overridePendingTransition(
R.anim.slide_from_right,
@@ -148,7 +149,7 @@ class AccountPreferencesFragment : PreferenceFragmentCompat() {
setTitle(R.string.title_domain_mutes)
setIcon(R.drawable.ic_mute_24dp)
setOnPreferenceClickListener {
- val intent = Intent(context, InstanceListActivity::class.java)
+ val intent = InstanceListActivityIntent(context)
activity?.startActivity(intent)
activity?.overridePendingTransition(
R.anim.slide_from_right,
@@ -163,7 +164,7 @@ class AccountPreferencesFragment : PreferenceFragmentCompat() {
setTitle(R.string.title_migration_relogin)
setIcon(R.drawable.ic_logout)
setOnPreferenceClickListener {
- val intent = LoginActivity.getIntent(context, LoginActivity.MODE_MIGRATION)
+ val intent = LoginActivityIntent(context, LoginMode.MIGRATION)
(activity as BaseActivity).startActivityWithSlideInAnimation(intent)
true
}
@@ -280,7 +281,7 @@ class AccountPreferencesFragment : PreferenceFragmentCompat() {
} else {
activity?.let {
val intent =
- PreferencesActivity.newIntent(it, PreferencesActivity.NOTIFICATION_PREFERENCES)
+ PreferencesActivityIntent(it, PreferenceScreen.NOTIFICATION)
it.startActivity(intent)
it.overridePendingTransition(R.anim.slide_from_right, R.anim.slide_to_left)
}
@@ -346,7 +347,7 @@ class AccountPreferencesFragment : PreferenceFragmentCompat() {
}
private fun launchFilterActivity() {
- val intent = Intent(context, FiltersActivity::class.java)
+ val intent = FiltersActivityIntent(requireContext())
activity?.startActivity(intent)
activity?.overridePendingTransition(R.anim.slide_from_right, R.anim.slide_to_left)
}
diff --git a/app/src/main/java/app/pachli/components/preference/PreferencesActivity.kt b/app/src/main/java/app/pachli/components/preference/PreferencesActivity.kt
index af44b01a0..c73808f70 100644
--- a/app/src/main/java/app/pachli/components/preference/PreferencesActivity.kt
+++ b/app/src/main/java/app/pachli/components/preference/PreferencesActivity.kt
@@ -16,7 +16,6 @@
package app.pachli.components.preference
-import android.content.Context
import android.content.Intent
import android.os.Bundle
import androidx.activity.OnBackPressedCallback
@@ -26,9 +25,11 @@ import androidx.lifecycle.lifecycleScope
import androidx.preference.Preference
import androidx.preference.PreferenceFragmentCompat
import app.pachli.BaseActivity
-import app.pachli.MainActivity
import app.pachli.R
import app.pachli.appstore.EventHub
+import app.pachli.core.navigation.MainActivityIntent
+import app.pachli.core.navigation.PreferencesActivityIntent
+import app.pachli.core.navigation.PreferencesActivityIntent.PreferenceScreen
import app.pachli.core.preferences.PrefKeys
import app.pachli.core.preferences.PrefKeys.APP_THEME
import app.pachli.core.preferences.getNonNullString
@@ -41,6 +42,9 @@ import kotlinx.coroutines.launch
import timber.log.Timber
import javax.inject.Inject
+/**
+ * Show specific preferences.
+ */
@AndroidEntryPoint
class PreferencesActivity :
BaseActivity(),
@@ -55,7 +59,7 @@ class PreferencesActivity :
* Either the back stack activities need to all be recreated, or do the easier thing, which
* is hijack the back button press and use it to launch a new MainActivity and clear the
* back stack. */
- val intent = Intent(this@PreferencesActivity, MainActivity::class.java)
+ val intent = MainActivityIntent(this@PreferencesActivity)
intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
startActivityWithSlideInAnimation(intent)
}
@@ -73,15 +77,15 @@ class PreferencesActivity :
setDisplayShowHomeEnabled(true)
}
- val preferenceType = intent.getIntExtra(EXTRA_PREFERENCE_TYPE, 0)
+ val preferenceType = PreferencesActivityIntent.getPreferenceType(intent)
val fragmentTag = "preference_fragment_$preferenceType"
val fragment: Fragment = supportFragmentManager.findFragmentByTag(fragmentTag)
?: when (preferenceType) {
- GENERAL_PREFERENCES -> PreferencesFragment.newInstance()
- ACCOUNT_PREFERENCES -> AccountPreferencesFragment.newInstance()
- NOTIFICATION_PREFERENCES -> NotificationPreferencesFragment.newInstance()
+ PreferenceScreen.GENERAL -> PreferencesFragment.newInstance()
+ PreferenceScreen.ACCOUNT -> AccountPreferencesFragment.newInstance()
+ PreferenceScreen.NOTIFICATION -> NotificationPreferencesFragment.newInstance()
else -> throw IllegalArgumentException("preferenceType not known")
}
@@ -164,17 +168,6 @@ class PreferencesActivity :
}
companion object {
- const val GENERAL_PREFERENCES = 0
- const val ACCOUNT_PREFERENCES = 1
- const val NOTIFICATION_PREFERENCES = 2
- private const val EXTRA_PREFERENCE_TYPE = "EXTRA_PREFERENCE_TYPE"
private const val EXTRA_RESTART_ON_BACK = "restart"
-
- @JvmStatic
- fun newIntent(context: Context, preferenceType: Int): Intent {
- val intent = Intent(context, PreferencesActivity::class.java)
- intent.putExtra(EXTRA_PREFERENCE_TYPE, preferenceType)
- return intent
- }
}
}
diff --git a/app/src/main/java/app/pachli/components/report/ReportActivity.kt b/app/src/main/java/app/pachli/components/report/ReportActivity.kt
index f3f3bf6b4..1f9360ed8 100644
--- a/app/src/main/java/app/pachli/components/report/ReportActivity.kt
+++ b/app/src/main/java/app/pachli/components/report/ReportActivity.kt
@@ -16,17 +16,19 @@
package app.pachli.components.report
-import android.content.Context
-import android.content.Intent
import android.os.Bundle
import androidx.activity.viewModels
import app.pachli.BottomSheetActivity
import app.pachli.R
import app.pachli.components.report.adapter.ReportPagerAdapter
+import app.pachli.core.navigation.ReportActivityIntent
import app.pachli.databinding.ActivityReportBinding
import app.pachli.util.viewBinding
import dagger.hilt.android.AndroidEntryPoint
+/**
+ * Report a status or user.
+ */
@AndroidEntryPoint
class ReportActivity : BottomSheetActivity() {
private val viewModel: ReportViewModel by viewModels()
@@ -35,13 +37,13 @@ class ReportActivity : BottomSheetActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
- val accountId = intent?.getStringExtra(ACCOUNT_ID)
- val accountUserName = intent?.getStringExtra(ACCOUNT_USERNAME)
- if (accountId.isNullOrBlank() || accountUserName.isNullOrBlank()) {
- throw IllegalStateException("accountId ($accountId) or accountUserName ($accountUserName) is null")
+ val accountId = ReportActivityIntent.getAccountId(intent)
+ val accountUserName = ReportActivityIntent.getAccountUserName(intent)
+ if (accountId.isBlank() || accountUserName.isBlank()) {
+ throw IllegalStateException("accountId ($accountId) or accountUserName ($accountUserName) is blank")
}
- viewModel.init(accountId, accountUserName, intent?.getStringExtra(STATUS_ID))
+ viewModel.init(accountId, accountUserName, ReportActivityIntent.getStatusId(intent))
setContentView(binding.root)
@@ -115,19 +117,4 @@ class ReportActivity : BottomSheetActivity() {
private fun showStatusesPage() {
binding.wizard.currentItem = 0
}
-
- companion object {
- private const val ACCOUNT_ID = "account_id"
- private const val ACCOUNT_USERNAME = "account_username"
- private const val STATUS_ID = "status_id"
-
- @JvmStatic
- fun getIntent(context: Context, accountId: String, userName: String, statusId: String? = null) =
- Intent(context, ReportActivity::class.java)
- .apply {
- putExtra(ACCOUNT_ID, accountId)
- putExtra(ACCOUNT_USERNAME, userName)
- putExtra(STATUS_ID, statusId)
- }
- }
}
diff --git a/app/src/main/java/app/pachli/components/report/fragments/ReportStatusesFragment.kt b/app/src/main/java/app/pachli/components/report/fragments/ReportStatusesFragment.kt
index 4c5f708d9..a0bfd98e9 100644
--- a/app/src/main/java/app/pachli/components/report/fragments/ReportStatusesFragment.kt
+++ b/app/src/main/java/app/pachli/components/report/fragments/ReportStatusesFragment.kt
@@ -33,20 +33,20 @@ import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.SimpleItemAnimator
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout.OnRefreshListener
import app.pachli.R
-import app.pachli.StatusListActivity
-import app.pachli.ViewMediaActivity
-import app.pachli.components.account.AccountActivity
import app.pachli.components.report.ReportViewModel
import app.pachli.components.report.Screen
import app.pachli.components.report.adapter.AdapterHandler
import app.pachli.components.report.adapter.StatusesAdapter
import app.pachli.core.accounts.AccountManager
+import app.pachli.core.navigation.AccountActivityIntent
+import app.pachli.core.navigation.AttachmentViewData
+import app.pachli.core.navigation.StatusListActivityIntent
+import app.pachli.core.navigation.ViewMediaActivityIntent
import app.pachli.core.network.model.Attachment
import app.pachli.core.network.model.Status
import app.pachli.databinding.FragmentReportStatusesBinding
import app.pachli.util.viewBinding
import app.pachli.util.visible
-import app.pachli.viewdata.AttachmentViewData
import com.google.android.material.color.MaterialColors
import com.google.android.material.divider.MaterialDividerItemDecoration
import com.google.android.material.snackbar.Snackbar
@@ -82,7 +82,7 @@ class ReportStatusesFragment :
when (actionable.attachments[idx].type) {
Attachment.Type.GIFV, Attachment.Type.VIDEO, Attachment.Type.IMAGE, Attachment.Type.AUDIO -> {
val attachments = AttachmentViewData.list(actionable)
- val intent = ViewMediaActivity.newIntent(context, attachments, idx)
+ val intent = ViewMediaActivityIntent(requireContext(), attachments, idx)
if (v != null) {
val url = actionable.attachments[idx].url
ViewCompat.setTransitionName(v, url)
@@ -209,9 +209,9 @@ class ReportStatusesFragment :
return viewModel.isStatusChecked(id)
}
- override fun onViewAccount(id: String) = startActivity(AccountActivity.getIntent(requireContext(), id))
+ override fun onViewAccount(id: String) = startActivity(AccountActivityIntent(requireContext(), id))
- override fun onViewTag(tag: String) = startActivity(StatusListActivity.newHashtagIntent(requireContext(), tag))
+ override fun onViewTag(tag: String) = startActivity(StatusListActivityIntent.hashtag(requireContext(), tag))
override fun onViewUrl(url: String) = viewModel.checkClickedUrl(url)
diff --git a/app/src/main/java/app/pachli/components/scheduled/ScheduledStatusActivity.kt b/app/src/main/java/app/pachli/components/scheduled/ScheduledStatusActivity.kt
index 8f4a15a71..a03fac186 100644
--- a/app/src/main/java/app/pachli/components/scheduled/ScheduledStatusActivity.kt
+++ b/app/src/main/java/app/pachli/components/scheduled/ScheduledStatusActivity.kt
@@ -16,8 +16,6 @@
package app.pachli.components.scheduled
-import android.content.Context
-import android.content.Intent
import android.os.Bundle
import android.view.Menu
import android.view.MenuInflater
@@ -32,7 +30,8 @@ import app.pachli.BaseActivity
import app.pachli.R
import app.pachli.appstore.EventHub
import app.pachli.appstore.StatusScheduledEvent
-import app.pachli.components.compose.ComposeActivity
+import app.pachli.core.navigation.ComposeActivityIntent
+import app.pachli.core.navigation.ComposeActivityIntent.ComposeOptions
import app.pachli.core.network.model.ScheduledStatus
import app.pachli.databinding.ActivityScheduledStatusBinding
import app.pachli.util.hide
@@ -151,9 +150,9 @@ class ScheduledStatusActivity :
}
override fun edit(item: ScheduledStatus) {
- val intent = ComposeActivity.startIntent(
+ val intent = ComposeActivityIntent(
this,
- ComposeActivity.ComposeOptions(
+ ComposeOptions(
scheduledTootId = item.id,
content = item.params.text,
contentWarning = item.params.spoilerText,
@@ -162,7 +161,7 @@ class ScheduledStatusActivity :
visibility = item.params.visibility,
scheduledAt = item.scheduledAt,
sensitive = item.params.sensitive,
- kind = ComposeActivity.ComposeKind.EDIT_SCHEDULED,
+ kind = ComposeOptions.ComposeKind.EDIT_SCHEDULED,
),
)
startActivity(intent)
@@ -177,8 +176,4 @@ class ScheduledStatusActivity :
}
.show()
}
-
- companion object {
- fun newIntent(context: Context) = Intent(context, ScheduledStatusActivity::class.java)
- }
}
diff --git a/app/src/main/java/app/pachli/components/search/SearchActivity.kt b/app/src/main/java/app/pachli/components/search/SearchActivity.kt
index c94699e93..e953c6046 100644
--- a/app/src/main/java/app/pachli/components/search/SearchActivity.kt
+++ b/app/src/main/java/app/pachli/components/search/SearchActivity.kt
@@ -153,8 +153,4 @@ class SearchActivity : BottomSheetActivity(), MenuProvider, SearchView.OnQueryTe
return false
}
-
- companion object {
- fun getIntent(context: Context) = Intent(context, SearchActivity::class.java)
- }
}
diff --git a/app/src/main/java/app/pachli/components/search/fragments/SearchFragment.kt b/app/src/main/java/app/pachli/components/search/fragments/SearchFragment.kt
index b295ebe82..0a1252a38 100644
--- a/app/src/main/java/app/pachli/components/search/fragments/SearchFragment.kt
+++ b/app/src/main/java/app/pachli/components/search/fragments/SearchFragment.kt
@@ -18,9 +18,9 @@ import androidx.recyclerview.widget.SimpleItemAnimator
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
import app.pachli.BottomSheetActivity
import app.pachli.R
-import app.pachli.StatusListActivity
-import app.pachli.components.account.AccountActivity
import app.pachli.components.search.SearchViewModel
+import app.pachli.core.navigation.AccountActivityIntent
+import app.pachli.core.navigation.StatusListActivityIntent
import app.pachli.core.network.retrofit.MastodonApi
import app.pachli.databinding.FragmentSearchBinding
import app.pachli.interfaces.LinkListener
@@ -141,11 +141,11 @@ abstract class SearchFragment :
}
override fun onViewAccount(id: String) {
- bottomSheetActivity?.startActivityWithSlideInAnimation(AccountActivity.getIntent(requireContext(), id))
+ bottomSheetActivity?.startActivityWithSlideInAnimation(AccountActivityIntent(requireContext(), id))
}
override fun onViewTag(tag: String) {
- bottomSheetActivity?.startActivityWithSlideInAnimation(StatusListActivity.newHashtagIntent(requireContext(), tag))
+ bottomSheetActivity?.startActivityWithSlideInAnimation(StatusListActivityIntent.hashtag(requireContext(), tag))
}
override fun onViewUrl(url: String) {
diff --git a/app/src/main/java/app/pachli/components/search/fragments/SearchStatusesFragment.kt b/app/src/main/java/app/pachli/components/search/fragments/SearchStatusesFragment.kt
index 1168f34bf..5e613aaa2 100644
--- a/app/src/main/java/app/pachli/components/search/fragments/SearchStatusesFragment.kt
+++ b/app/src/main/java/app/pachli/components/search/fragments/SearchStatusesFragment.kt
@@ -38,12 +38,13 @@ import androidx.paging.PagingDataAdapter
import androidx.recyclerview.widget.LinearLayoutManager
import app.pachli.BaseActivity
import app.pachli.R
-import app.pachli.ViewMediaActivity
-import app.pachli.components.compose.ComposeActivity
-import app.pachli.components.compose.ComposeActivity.ComposeOptions
-import app.pachli.components.report.ReportActivity
import app.pachli.components.search.adapter.SearchStatusesAdapter
import app.pachli.core.database.model.AccountEntity
+import app.pachli.core.navigation.AttachmentViewData
+import app.pachli.core.navigation.ComposeActivityIntent
+import app.pachli.core.navigation.ComposeActivityIntent.ComposeOptions
+import app.pachli.core.navigation.ReportActivityIntent
+import app.pachli.core.navigation.ViewMediaActivityIntent
import app.pachli.core.network.model.Attachment
import app.pachli.core.network.model.Status
import app.pachli.core.network.model.Status.Mention
@@ -52,7 +53,6 @@ import app.pachli.interfaces.StatusActionListener
import app.pachli.util.StatusDisplayOptionsRepository
import app.pachli.util.openLink
import app.pachli.view.showMuteAccountDialog
-import app.pachli.viewdata.AttachmentViewData
import app.pachli.viewdata.StatusViewData
import at.connyduck.calladapter.networkresult.fold
import com.google.android.material.divider.MaterialDividerItemDecoration
@@ -119,8 +119,8 @@ class SearchStatusesFragment : SearchFragment(), StatusActionLis
when (actionable.attachments[attachmentIndex].type) {
Attachment.Type.GIFV, Attachment.Type.VIDEO, Attachment.Type.IMAGE, Attachment.Type.AUDIO -> {
val attachments = AttachmentViewData.list(actionable)
- val intent = ViewMediaActivity.newIntent(
- context,
+ val intent = ViewMediaActivityIntent(
+ requireContext(),
attachments,
attachmentIndex,
)
@@ -202,7 +202,7 @@ class SearchStatusesFragment : SearchFragment(), StatusActionLis
remove(viewModel.activeAccount?.username)
}
- val intent = ComposeActivity.startIntent(
+ val intent = ComposeActivityIntent(
requireContext(),
ComposeOptions(
inReplyToId = status.actionableId,
@@ -212,7 +212,7 @@ class SearchStatusesFragment : SearchFragment(), StatusActionLis
replyingStatusAuthor = actionableStatus.account.localUsername,
replyingStatusContent = status.content.toString(),
language = actionableStatus.language,
- kind = ComposeActivity.ComposeKind.NEW,
+ kind = ComposeOptions.ComposeKind.NEW,
),
)
bottomSheetActivity?.startActivityWithSlideInAnimation(intent)
@@ -423,7 +423,7 @@ class SearchStatusesFragment : SearchFragment(), StatusActionLis
}
private fun openReportPage(accountId: String, accountUsername: String, statusId: String) {
- startActivity(ReportActivity.getIntent(requireContext(), accountId, accountUsername, statusId))
+ startActivity(ReportActivityIntent(requireContext(), accountId, accountUsername, statusId))
}
private fun showConfirmDeleteDialog(id: String, position: Int) {
@@ -455,7 +455,7 @@ class SearchStatusesFragment : SearchFragment(), StatusActionLis
deletedStatus
}
- val intent = ComposeActivity.startIntent(
+ val intent = ComposeActivityIntent(
requireContext(),
ComposeOptions(
content = redraftStatus.text.orEmpty(),
@@ -466,7 +466,7 @@ class SearchStatusesFragment : SearchFragment(), StatusActionLis
sensitive = redraftStatus.sensitive,
poll = redraftStatus.poll?.toNewPoll(status.createdAt),
language = redraftStatus.language,
- kind = ComposeActivity.ComposeKind.NEW,
+ kind = ComposeOptions.ComposeKind.NEW,
),
)
startActivity(intent)
@@ -497,9 +497,9 @@ class SearchStatusesFragment : SearchFragment(), StatusActionLis
language = status.language,
statusId = source.id,
poll = status.poll?.toNewPoll(status.createdAt),
- kind = ComposeActivity.ComposeKind.EDIT_POSTED,
+ kind = ComposeOptions.ComposeKind.EDIT_POSTED,
)
- startActivity(ComposeActivity.startIntent(requireContext(), composeOptions))
+ startActivity(ComposeActivityIntent(requireContext(), composeOptions))
},
{
Snackbar.make(
diff --git a/app/src/main/java/app/pachli/components/timeline/TimelineFragment.kt b/app/src/main/java/app/pachli/components/timeline/TimelineFragment.kt
index b85099a06..cd2ceab92 100644
--- a/app/src/main/java/app/pachli/components/timeline/TimelineFragment.kt
+++ b/app/src/main/java/app/pachli/components/timeline/TimelineFragment.kt
@@ -42,8 +42,6 @@ import androidx.swiperefreshlayout.widget.SwipeRefreshLayout.OnRefreshListener
import app.pachli.BaseActivity
import app.pachli.R
import app.pachli.adapter.StatusBaseViewHolder
-import app.pachli.components.accountlist.AccountListActivity
-import app.pachli.components.accountlist.AccountListActivity.Companion.newIntent
import app.pachli.components.timeline.viewmodel.CachedTimelineViewModel
import app.pachli.components.timeline.viewmodel.InfallibleUiAction
import app.pachli.components.timeline.viewmodel.NetworkTimelineViewModel
@@ -52,6 +50,8 @@ import app.pachli.components.timeline.viewmodel.StatusActionSuccess
import app.pachli.components.timeline.viewmodel.TimelineViewModel
import app.pachli.components.timeline.viewmodel.UiSuccess
import app.pachli.core.database.model.TranslationState
+import app.pachli.core.navigation.AccountListActivityIntent
+import app.pachli.core.navigation.AttachmentViewData
import app.pachli.core.network.model.Status
import app.pachli.core.network.model.TimelineKind
import app.pachli.databinding.FragmentTimelineBinding
@@ -72,7 +72,6 @@ import app.pachli.util.show
import app.pachli.util.viewBinding
import app.pachli.util.visible
import app.pachli.util.withPresentationState
-import app.pachli.viewdata.AttachmentViewData
import app.pachli.viewdata.StatusViewData
import at.connyduck.sparkbutton.helpers.Utils
import com.google.android.material.color.MaterialColors
@@ -635,13 +634,13 @@ class TimelineFragment :
override fun onShowReblogs(position: Int) {
val statusId = adapter.peek(position)?.id ?: return
- val intent = newIntent(requireContext(), AccountListActivity.Type.REBLOGGED, statusId)
+ val intent = AccountListActivityIntent(requireContext(), AccountListActivityIntent.Kind.REBLOGGED, statusId)
(activity as BaseActivity).startActivityWithSlideInAnimation(intent)
}
override fun onShowFavs(position: Int) {
val statusId = adapter.peek(position)?.id ?: return
- val intent = newIntent(requireContext(), AccountListActivity.Type.FAVOURITED, statusId)
+ val intent = AccountListActivityIntent(requireContext(), AccountListActivityIntent.Kind.FAVOURITED, statusId)
(activity as BaseActivity).startActivityWithSlideInAnimation(intent)
}
diff --git a/app/src/main/java/app/pachli/components/trending/TrendingActivity.kt b/app/src/main/java/app/pachli/components/trending/TrendingActivity.kt
index a246bd6a1..c5220ed8b 100644
--- a/app/src/main/java/app/pachli/components/trending/TrendingActivity.kt
+++ b/app/src/main/java/app/pachli/components/trending/TrendingActivity.kt
@@ -18,8 +18,6 @@
package app.pachli.components.trending
import android.annotation.SuppressLint
-import android.content.Context
-import android.content.Intent
import android.os.Bundle
import android.view.Menu
import android.view.MenuInflater
@@ -86,10 +84,6 @@ class TrendingActivity : BottomSheetActivity(), AppBarLayoutHost, MenuProvider {
override fun onMenuItemSelected(menuItem: MenuItem): Boolean {
return super.onOptionsItemSelected(menuItem)
}
-
- companion object {
- fun getIntent(context: Context) = Intent(context, TrendingActivity::class.java)
- }
}
class TrendingFragmentAdapter(val activity: FragmentActivity) : FragmentStateAdapter(activity) {
diff --git a/app/src/main/java/app/pachli/components/trending/TrendingTagsFragment.kt b/app/src/main/java/app/pachli/components/trending/TrendingTagsFragment.kt
index 06fb52e32..00178d351 100644
--- a/app/src/main/java/app/pachli/components/trending/TrendingTagsFragment.kt
+++ b/app/src/main/java/app/pachli/components/trending/TrendingTagsFragment.kt
@@ -38,8 +38,8 @@ import androidx.recyclerview.widget.SimpleItemAnimator
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout.OnRefreshListener
import app.pachli.BaseActivity
import app.pachli.R
-import app.pachli.StatusListActivity
import app.pachli.components.trending.viewmodel.TrendingTagsViewModel
+import app.pachli.core.navigation.StatusListActivityIntent
import app.pachli.databinding.FragmentTrendingTagsBinding
import app.pachli.interfaces.ActionButtonActivity
import app.pachli.interfaces.AppBarLayoutHost
@@ -173,7 +173,7 @@ class TrendingTagsFragment :
fun onViewTag(tag: String) {
(requireActivity() as BaseActivity).startActivityWithSlideInAnimation(
- StatusListActivity.newHashtagIntent(
+ StatusListActivityIntent.hashtag(
requireContext(),
tag,
),
diff --git a/app/src/main/java/app/pachli/components/viewthread/ViewThreadActivity.kt b/app/src/main/java/app/pachli/components/viewthread/ViewThreadActivity.kt
index 4055af7f5..484044f92 100644
--- a/app/src/main/java/app/pachli/components/viewthread/ViewThreadActivity.kt
+++ b/app/src/main/java/app/pachli/components/viewthread/ViewThreadActivity.kt
@@ -16,16 +16,18 @@
package app.pachli.components.viewthread
-import android.content.Context
-import android.content.Intent
import android.os.Bundle
import androidx.fragment.app.commit
import app.pachli.BottomSheetActivity
import app.pachli.R
+import app.pachli.core.navigation.ViewThreadActivityIntent
import app.pachli.databinding.ActivityViewThreadBinding
import app.pachli.util.viewBinding
import dagger.hilt.android.AndroidEntryPoint
+/**
+ * View the statuses in a single thread.
+ */
@AndroidEntryPoint
class ViewThreadActivity : BottomSheetActivity() {
private val binding by viewBinding(ActivityViewThreadBinding::inflate)
@@ -39,8 +41,8 @@ class ViewThreadActivity : BottomSheetActivity() {
setDisplayShowHomeEnabled(true)
setDisplayShowTitleEnabled(true)
}
- val id = intent.getStringExtra(ID_EXTRA)!!
- val url = intent.getStringExtra(URL_EXTRA)!!
+ val id = ViewThreadActivityIntent.getStatusId(intent)
+ val url = ViewThreadActivityIntent.getUrl(intent)
val fragment =
supportFragmentManager.findFragmentByTag(FRAGMENT_TAG + id) as ViewThreadFragment?
?: ViewThreadFragment.newInstance(id, url)
@@ -51,15 +53,6 @@ class ViewThreadActivity : BottomSheetActivity() {
}
companion object {
- fun startIntent(context: Context, id: String, url: String): Intent {
- val intent = Intent(context, ViewThreadActivity::class.java)
- intent.putExtra(ID_EXTRA, id)
- intent.putExtra(URL_EXTRA, url)
- return intent
- }
-
- private const val ID_EXTRA = "id"
- private const val URL_EXTRA = "url"
private const val FRAGMENT_TAG = "ViewThreadFragment_"
}
}
diff --git a/app/src/main/java/app/pachli/components/viewthread/ViewThreadFragment.kt b/app/src/main/java/app/pachli/components/viewthread/ViewThreadFragment.kt
index 8aaaf9401..a54c29a25 100644
--- a/app/src/main/java/app/pachli/components/viewthread/ViewThreadFragment.kt
+++ b/app/src/main/java/app/pachli/components/viewthread/ViewThreadFragment.kt
@@ -34,9 +34,9 @@ import androidx.recyclerview.widget.SimpleItemAnimator
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout.OnRefreshListener
import app.pachli.BaseActivity
import app.pachli.R
-import app.pachli.components.accountlist.AccountListActivity
-import app.pachli.components.accountlist.AccountListActivity.Companion.newIntent
import app.pachli.components.viewthread.edits.ViewEditsFragment
+import app.pachli.core.navigation.AccountListActivityIntent
+import app.pachli.core.navigation.AttachmentViewData.Companion.list
import app.pachli.databinding.FragmentViewThreadBinding
import app.pachli.fragment.SFragment
import app.pachli.interfaces.StatusActionListener
@@ -45,7 +45,6 @@ import app.pachli.util.hide
import app.pachli.util.openLink
import app.pachli.util.show
import app.pachli.util.viewBinding
-import app.pachli.viewdata.AttachmentViewData.Companion.list
import app.pachli.viewdata.StatusViewData
import com.google.android.material.color.MaterialColors
import com.google.android.material.divider.MaterialDividerItemDecoration
@@ -366,13 +365,13 @@ class ViewThreadFragment :
override fun onShowReblogs(position: Int) {
val statusId = adapter.currentList[position].id
- val intent = newIntent(requireContext(), AccountListActivity.Type.REBLOGGED, statusId)
+ val intent = AccountListActivityIntent(requireContext(), AccountListActivityIntent.Kind.REBLOGGED, statusId)
(requireActivity() as BaseActivity).startActivityWithSlideInAnimation(intent)
}
override fun onShowFavs(position: Int) {
val statusId = adapter.currentList[position].id
- val intent = newIntent(requireContext(), AccountListActivity.Type.FAVOURITED, statusId)
+ val intent = AccountListActivityIntent(requireContext(), AccountListActivityIntent.Kind.FAVOURITED, statusId)
(requireActivity() as BaseActivity).startActivityWithSlideInAnimation(intent)
}
@@ -423,7 +422,7 @@ class ViewThreadFragment :
private const val ID_EXTRA = "id"
private const val URL_EXTRA = "url"
- fun newInstance(id: String, url: String): ViewThreadFragment {
+ fun newInstance(id: String, url: String?): ViewThreadFragment {
val arguments = Bundle(2)
val fragment = ViewThreadFragment()
arguments.putString(ID_EXTRA, id)
diff --git a/app/src/main/java/app/pachli/components/viewthread/edits/ViewEditsFragment.kt b/app/src/main/java/app/pachli/components/viewthread/edits/ViewEditsFragment.kt
index 01baf3b24..72788c5d6 100644
--- a/app/src/main/java/app/pachli/components/viewthread/edits/ViewEditsFragment.kt
+++ b/app/src/main/java/app/pachli/components/viewthread/edits/ViewEditsFragment.kt
@@ -31,9 +31,9 @@ import androidx.recyclerview.widget.SimpleItemAnimator
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout.OnRefreshListener
import app.pachli.BottomSheetActivity
import app.pachli.R
-import app.pachli.StatusListActivity
-import app.pachli.components.account.AccountActivity
import app.pachli.core.common.string.unicodeWrap
+import app.pachli.core.navigation.AccountActivityIntent
+import app.pachli.core.navigation.StatusListActivityIntent
import app.pachli.core.preferences.PrefKeys
import app.pachli.core.preferences.SharedPreferencesRepository
import app.pachli.databinding.FragmentViewEditsBinding
@@ -184,11 +184,11 @@ class ViewEditsFragment :
}
override fun onViewAccount(id: String) {
- bottomSheetActivity?.startActivityWithSlideInAnimation(AccountActivity.getIntent(requireContext(), id))
+ bottomSheetActivity?.startActivityWithSlideInAnimation(AccountActivityIntent(requireContext(), id))
}
override fun onViewTag(tag: String) {
- bottomSheetActivity?.startActivityWithSlideInAnimation(StatusListActivity.newHashtagIntent(requireContext(), tag))
+ bottomSheetActivity?.startActivityWithSlideInAnimation(StatusListActivityIntent.hashtag(requireContext(), tag))
}
override fun onViewUrl(url: String) {
diff --git a/app/src/main/java/app/pachli/db/DraftsAlert.kt b/app/src/main/java/app/pachli/db/DraftsAlert.kt
index 960653760..c78c61318 100644
--- a/app/src/main/java/app/pachli/db/DraftsAlert.kt
+++ b/app/src/main/java/app/pachli/db/DraftsAlert.kt
@@ -23,9 +23,9 @@ import androidx.lifecycle.LifecycleCoroutineScope
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.lifecycleScope
import app.pachli.R
-import app.pachli.components.drafts.DraftsActivity
import app.pachli.core.accounts.AccountManager
import app.pachli.core.database.dao.DraftDao
+import app.pachli.core.navigation.DraftsActivityIntent
import kotlinx.coroutines.launch
import timber.log.Timber
import javax.inject.Inject
@@ -66,7 +66,7 @@ class DraftsAlert @Inject constructor(private val draftDao: DraftDao) {
.setPositiveButton(R.string.action_post_failed_show_drafts) { _: DialogInterface?, _: Int ->
clearDraftsAlert(coroutineScope, activeAccountId) // User looked at drafts
- val intent = DraftsActivity.newIntent(context)
+ val intent = DraftsActivityIntent(context)
context.startActivity(intent)
}
.setNegativeButton(R.string.action_post_failed_do_nothing) { _: DialogInterface?, _: Int ->
diff --git a/app/src/main/java/app/pachli/fragment/SFragment.kt b/app/src/main/java/app/pachli/fragment/SFragment.kt
index 499faf860..9e1b5ae0b 100644
--- a/app/src/main/java/app/pachli/fragment/SFragment.kt
+++ b/app/src/main/java/app/pachli/fragment/SFragment.kt
@@ -42,15 +42,15 @@ import app.pachli.BaseActivity
import app.pachli.BottomSheetActivity
import app.pachli.PostLookupFallbackBehavior
import app.pachli.R
-import app.pachli.StatusListActivity.Companion.newHashtagIntent
-import app.pachli.ViewMediaActivity.Companion.newIntent
-import app.pachli.components.compose.ComposeActivity
-import app.pachli.components.compose.ComposeActivity.Companion.startIntent
-import app.pachli.components.compose.ComposeActivity.ComposeOptions
-import app.pachli.components.report.ReportActivity.Companion.getIntent
import app.pachli.core.accounts.AccountManager
import app.pachli.core.database.model.AccountEntity
import app.pachli.core.database.model.TranslationState
+import app.pachli.core.navigation.AttachmentViewData
+import app.pachli.core.navigation.ComposeActivityIntent
+import app.pachli.core.navigation.ComposeActivityIntent.ComposeOptions
+import app.pachli.core.navigation.ReportActivityIntent
+import app.pachli.core.navigation.StatusListActivityIntent
+import app.pachli.core.navigation.ViewMediaActivityIntent
import app.pachli.core.network.ServerOperation
import app.pachli.core.network.model.Attachment
import app.pachli.core.network.model.Status
@@ -61,7 +61,6 @@ import app.pachli.network.ServerCapabilitiesRepository
import app.pachli.usecase.TimelineCases
import app.pachli.util.openLink
import app.pachli.view.showMuteAccountDialog
-import app.pachli.viewdata.AttachmentViewData
import app.pachli.viewdata.StatusViewData
import at.connyduck.calladapter.networkresult.fold
import at.connyduck.calladapter.networkresult.onFailure
@@ -163,10 +162,10 @@ abstract class SFragment : Fragment() {
replyingStatusAuthor = account.localUsername,
replyingStatusContent = actionableStatus.content.parseAsMastodonHtml().toString(),
language = actionableStatus.language,
- kind = ComposeActivity.ComposeKind.NEW,
+ kind = ComposeOptions.ComposeKind.NEW,
)
- val intent = startIntent(requireContext(), composeOptions)
+ val intent = ComposeActivityIntent(requireContext(), composeOptions)
requireActivity().startActivity(intent)
}
@@ -379,7 +378,7 @@ abstract class SFragment : Fragment() {
val (attachment) = attachments[urlIndex]
when (attachment.type) {
Attachment.Type.GIFV, Attachment.Type.VIDEO, Attachment.Type.IMAGE, Attachment.Type.AUDIO -> {
- val intent = newIntent(context, attachments, urlIndex)
+ val intent = ViewMediaActivityIntent(requireContext(), attachments, urlIndex)
if (view != null) {
val url = attachment.url
view.transitionName = url
@@ -400,11 +399,11 @@ abstract class SFragment : Fragment() {
}
protected fun viewTag(tag: String) {
- startActivity(newHashtagIntent(requireContext(), tag))
+ startActivity(StatusListActivityIntent.hashtag(requireContext(), tag))
}
private fun openReportPage(accountId: String, accountUsername: String, statusId: String) {
- startActivity(getIntent(requireContext(), accountId, accountUsername, statusId))
+ startActivity(ReportActivityIntent(requireContext(), accountId, accountUsername, statusId))
}
private fun showConfirmDeleteDialog(id: String, position: Int) {
@@ -455,9 +454,9 @@ abstract class SFragment : Fragment() {
modifiedInitialState = true,
language = sourceStatus.language,
poll = sourceStatus.poll?.toNewPoll(sourceStatus.createdAt),
- kind = ComposeActivity.ComposeKind.NEW,
+ kind = ComposeOptions.ComposeKind.NEW,
)
- startActivity(startIntent(requireContext(), composeOptions))
+ startActivity(ComposeActivityIntent(requireContext(), composeOptions))
},
{ error: Throwable? ->
Timber.w(error, "error deleting status")
@@ -485,9 +484,9 @@ abstract class SFragment : Fragment() {
language = status.language,
statusId = source.id,
poll = status.poll?.toNewPoll(status.createdAt),
- kind = ComposeActivity.ComposeKind.EDIT_POSTED,
+ kind = ComposeOptions.ComposeKind.EDIT_POSTED,
)
- startActivity(startIntent(requireContext(), composeOptions))
+ startActivity(ComposeActivityIntent(requireContext(), composeOptions))
},
{
Snackbar.make(
diff --git a/app/src/main/java/app/pachli/service/PachliTileService.kt b/app/src/main/java/app/pachli/service/PachliTileService.kt
index 942a990de..7c2a41d30 100644
--- a/app/src/main/java/app/pachli/service/PachliTileService.kt
+++ b/app/src/main/java/app/pachli/service/PachliTileService.kt
@@ -24,8 +24,8 @@ import android.content.Intent
import android.os.Build
import android.service.quicksettings.TileService
import app.pachli.MainActivity
-import app.pachli.components.compose.ComposeActivity
import app.pachli.components.notifications.pendingIntentFlags
+import app.pachli.core.navigation.ComposeActivityIntent.ComposeOptions
/**
* Small Addition that adds in a QuickSettings tile
@@ -35,7 +35,7 @@ import app.pachli.components.notifications.pendingIntentFlags
class PachliTileService : TileService() {
@SuppressLint("StartActivityAndCollapseDeprecated")
override fun onClick() {
- val intent = MainActivity.composeIntent(this, ComposeActivity.ComposeOptions())
+ val intent = MainActivity.composeIntent(this, ComposeOptions())
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
startActivityAndCollapse(getActivityPendingIntent(this, 0, intent))
diff --git a/app/src/main/java/app/pachli/service/SendStatusService.kt b/app/src/main/java/app/pachli/service/SendStatusService.kt
index 300243d3e..2bcaabbcf 100644
--- a/app/src/main/java/app/pachli/service/SendStatusService.kt
+++ b/app/src/main/java/app/pachli/service/SendStatusService.kt
@@ -1,5 +1,6 @@
package app.pachli.service
+import android.annotation.SuppressLint
import android.app.Notification
import android.app.NotificationChannel
import android.app.NotificationManager
@@ -373,6 +374,8 @@ class SendStatusService : Service() {
}
private fun cancelSendingIntent(statusId: Int): PendingIntent {
+ // TODO: Revisit suppressing this when this file is moved
+ @SuppressLint("IntentDetector")
val intent = Intent(this, SendStatusService::class.java)
intent.putExtra(KEY_CANCEL, statusId)
return PendingIntent.getService(
@@ -428,6 +431,8 @@ class SendStatusService : Service() {
context: Context,
statusToSend: StatusToSend,
): Intent {
+ // TODO: Revisit suppressing this when this file is moved
+ @SuppressLint("IntentDetector")
val intent = Intent(context, SendStatusService::class.java)
intent.putExtra(KEY_STATUS, statusToSend)
diff --git a/app/src/main/java/app/pachli/util/ShareShortcutHelper.kt b/app/src/main/java/app/pachli/util/ShareShortcutHelper.kt
index 403d991ca..91ec2e0b0 100644
--- a/app/src/main/java/app/pachli/util/ShareShortcutHelper.kt
+++ b/app/src/main/java/app/pachli/util/ShareShortcutHelper.kt
@@ -25,9 +25,9 @@ import androidx.core.app.Person
import androidx.core.content.pm.ShortcutInfoCompat
import androidx.core.content.pm.ShortcutManagerCompat
import androidx.core.graphics.drawable.IconCompat
-import app.pachli.MainActivity
import app.pachli.R
import app.pachli.core.database.model.AccountEntity
+import app.pachli.core.navigation.MainActivityIntent
import com.bumptech.glide.Glide
import io.reactivex.rxjava3.core.Single
import io.reactivex.rxjava3.schedulers.Schedulers
@@ -67,7 +67,7 @@ fun updateShortcut(context: Context, account: AccountEntity) {
.build()
// This intent will be sent when the user clicks on one of the launcher shortcuts. Intent from share sheet will be different
- val intent = Intent(context, MainActivity::class.java).apply {
+ val intent = MainActivityIntent(context).apply {
action = Intent.ACTION_SEND
type = "text/plain"
putExtra(ShortcutManagerCompat.EXTRA_SHORTCUT_ID, account.id.toString())
diff --git a/app/src/test/java/app/pachli/MainActivityTest.kt b/app/src/test/java/app/pachli/MainActivityTest.kt
index feb3a9d55..4b1564091 100644
--- a/app/src/test/java/app/pachli/MainActivityTest.kt
+++ b/app/src/test/java/app/pachli/MainActivityTest.kt
@@ -33,6 +33,7 @@ import app.pachli.core.accounts.AccountManager
import app.pachli.core.database.model.AccountEntity
import app.pachli.core.database.model.TabKind
import app.pachli.core.database.model.defaultTabs
+import app.pachli.core.navigation.AccountListActivityIntent
import app.pachli.core.network.model.Account
import app.pachli.core.network.model.Notification
import app.pachli.core.network.model.TimelineAccount
@@ -176,7 +177,7 @@ class MainActivityTest {
nextActivity.component,
)
assertEquals(
- AccountListActivity.Type.FOLLOW_REQUESTS,
+ AccountListActivityIntent.Kind.FOLLOW_REQUESTS,
nextActivity.getSerializableExtra("type"),
)
}
diff --git a/app/src/test/java/app/pachli/components/compose/ComposeActivityTest.kt b/app/src/test/java/app/pachli/components/compose/ComposeActivityTest.kt
index a227f9f0b..6763d3bbc 100644
--- a/app/src/test/java/app/pachli/components/compose/ComposeActivityTest.kt
+++ b/app/src/test/java/app/pachli/components/compose/ComposeActivityTest.kt
@@ -24,6 +24,8 @@ import app.pachli.PachliApplication
import app.pachli.R
import app.pachli.components.instanceinfo.InstanceInfoRepository
import app.pachli.core.accounts.AccountManager
+import app.pachli.core.navigation.ComposeActivityIntent
+import app.pachli.core.navigation.ComposeActivityIntent.ComposeOptions
import app.pachli.core.network.model.Account
import app.pachli.core.network.model.InstanceConfiguration
import app.pachli.core.network.model.InstanceV1
@@ -138,7 +140,7 @@ class ComposeActivityTest {
@Test
fun whenModifiedInitialState_andCloseButtonPressed_notFinish() {
- rule.launch(intent(ComposeActivity.ComposeOptions(modifiedInitialState = true)))
+ rule.launch(intent(ComposeOptions(modifiedInitialState = true)))
rule.getScenario().onActivity {
clickUp(it)
assertFalse(it.isFinishing)
@@ -167,7 +169,7 @@ class ComposeActivityTest {
@Test
fun whenModifiedInitialState_andBackButtonPressed_notFinish() {
- rule.launch(intent(ComposeActivity.ComposeOptions(modifiedInitialState = true)))
+ rule.launch(intent(ComposeOptions(modifiedInitialState = true)))
rule.getScenario().onActivity {
clickBack(it)
assertFalse(it.isFinishing)
@@ -512,7 +514,7 @@ class ComposeActivityTest {
@Test
fun languageGivenInComposeOptionsIsRespected() {
- rule.launch(intent(ComposeActivity.ComposeOptions(language = "no")))
+ rule.launch(intent(ComposeOptions(language = "no")))
rule.getScenario().onActivity {
assertEquals("no", it.selectedLanguage)
}
@@ -522,7 +524,7 @@ class ComposeActivityTest {
fun modernLanguageCodeIsUsed() {
// https://github.com/tuskyapp/Tusky/issues/2903
// "ji" was deprecated in favor of "yi"
- rule.launch(intent(ComposeActivity.ComposeOptions(language = "ji")))
+ rule.launch(intent(ComposeOptions(language = "ji")))
rule.getScenario().onActivity {
assertEquals("yi", it.selectedLanguage)
}
@@ -530,14 +532,14 @@ class ComposeActivityTest {
@Test
fun unknownLanguageGivenInComposeOptionsIsRespected() {
- rule.launch(intent(ComposeActivity.ComposeOptions(language = "zzz")))
+ rule.launch(intent(ComposeOptions(language = "zzz")))
rule.getScenario().onActivity {
assertEquals("zzz", it.selectedLanguage)
}
}
/** Returns an intent to launch [ComposeActivity] with the given options */
- private fun intent(composeOptions: ComposeActivity.ComposeOptions) = ComposeActivity.startIntent(
+ private fun intent(composeOptions: ComposeOptions) = ComposeActivityIntent(
ApplicationProvider.getApplicationContext(),
composeOptions,
)
diff --git a/build.gradle b/build.gradle
index 67b38bf3f..05d6769d1 100644
--- a/build.gradle
+++ b/build.gradle
@@ -9,6 +9,7 @@ plugins {
alias(libs.plugins.ktlint) apply false
alias(libs.plugins.aboutlibraries) apply false
alias(libs.plugins.hilt) apply false
+ alias(libs.plugins.quadrant) apply false
}
allprojects {
diff --git a/checks/src/main/java/app/pachli/lint/checks/IntentDetector.kt b/checks/src/main/java/app/pachli/lint/checks/IntentDetector.kt
new file mode 100644
index 000000000..a712b4938
--- /dev/null
+++ b/checks/src/main/java/app/pachli/lint/checks/IntentDetector.kt
@@ -0,0 +1,80 @@
+/*
+ * Copyright 2023 Pachli Association
+ *
+ * This file is a part of Pachli.
+ *
+ * This program is free software; you can redistribute it and/or modify it under the terms of the
+ * GNU General Public License as published by the Free Software Foundation; either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * Pachli is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
+ * the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
+ * Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with Pachli; if not,
+ * see .
+ */
+
+package app.pachli.lint.checks
+
+import com.android.SdkConstants.CLASS_INTENT
+import com.android.tools.lint.client.api.UElementHandler
+import com.android.tools.lint.detector.api.Category
+import com.android.tools.lint.detector.api.Detector
+import com.android.tools.lint.detector.api.Implementation
+import com.android.tools.lint.detector.api.Issue
+import com.android.tools.lint.detector.api.JavaContext
+import com.android.tools.lint.detector.api.Scope
+import com.android.tools.lint.detector.api.Severity
+import org.jetbrains.uast.UCallExpression
+import org.jetbrains.uast.getQualifiedName
+import org.jetbrains.uast.util.isConstructorCall
+
+class IntentDetector : Detector(), Detector.UastScanner {
+ override fun getApplicableUastTypes() = listOf(UCallExpression::class.java)
+
+ override fun createUastHandler(context: JavaContext) = object : UElementHandler() {
+ override fun visitCallExpression(node: UCallExpression) {
+ // Ignore anything that is not constructing an Intent
+ if (!node.isConstructorCall()) return
+ val classRef = node.classReference ?: return
+ val className = classRef.getQualifiedName()
+ if (className != CLASS_INTENT) return
+
+ // Ignore calls that don't have 2 or 4 parameters
+ val constructor = node.resolve() ?: return
+ val parameters = constructor.parameterList.parameters
+ if (parameters.size != 2 && parameters.size != 4) return
+
+ // Ignore calls where the last parameter is not a class literal
+ val lastParam = parameters.last()
+ if (lastParam.type.canonicalText != "java.lang.Class>") return
+
+ context.report(
+ issue = ISSUE,
+ scope = node,
+ location = context.getCallLocation(node, true, true),
+ message = "Use functions from `core.navigation`",
+ )
+ }
+ }
+
+ companion object {
+ val ISSUE = Issue.create(
+ id = "IntentDetector",
+ briefDescription = "Don't use `Intent(...)`, use functions from core.navigation",
+ explanation = """
+ Creating an `Intent` with a class from another module can create unnecessary or circular
+ dependencies. Use the `...Intent` classes in `core.navigation` to create an intent for
+ the appropriate `Activity`.
+ """.trimIndent(),
+ category = Category.CORRECTNESS,
+ priority = 6,
+ severity = Severity.WARNING,
+ implementation = Implementation(
+ IntentDetector::class.java,
+ Scope.JAVA_FILE_SCOPE,
+ ),
+ )
+ }
+}
diff --git a/checks/src/main/java/app/pachli/lint/checks/LintRegistry.kt b/checks/src/main/java/app/pachli/lint/checks/LintRegistry.kt
index c0cfde5fc..23f343381 100644
--- a/checks/src/main/java/app/pachli/lint/checks/LintRegistry.kt
+++ b/checks/src/main/java/app/pachli/lint/checks/LintRegistry.kt
@@ -7,7 +7,10 @@ import com.android.tools.lint.detector.api.Issue
@Suppress("UnstableApiUsage")
class LintRegistry : IssueRegistry() {
override val issues: List
- get() = listOf(AndroidxToolbarDetector.ISSUE)
+ get() = listOf(
+ AndroidxToolbarDetector.ISSUE,
+ IntentDetector.ISSUE,
+ )
override val api: Int
get() = CURRENT_API
diff --git a/checks/src/test/java/app/pachli/lint/checks/IntentDetectorTest.kt b/checks/src/test/java/app/pachli/lint/checks/IntentDetectorTest.kt
new file mode 100644
index 000000000..6d73e9007
--- /dev/null
+++ b/checks/src/test/java/app/pachli/lint/checks/IntentDetectorTest.kt
@@ -0,0 +1,171 @@
+/*
+ * Copyright 2023 Pachli Association
+ *
+ * This file is a part of Pachli.
+ *
+ * This program is free software; you can redistribute it and/or modify it under the terms of the
+ * GNU General Public License as published by the Free Software Foundation; either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * Pachli is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
+ * the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
+ * Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with Pachli; if not,
+ * see .
+ */
+
+package app.pachli.lint.checks
+
+import com.android.tools.lint.checks.infrastructure.LintDetectorTest
+import com.android.tools.lint.detector.api.Detector
+import com.android.tools.lint.detector.api.Issue
+
+class IntentDetectorTest : LintDetectorTest() {
+ override fun getDetector(): Detector = IntentDetector()
+
+ override fun getIssues(): List = listOf(IntentDetector.ISSUE)
+
+ fun `test Intent component constructor emits warning`() {
+ lint().files(
+ Context,
+ Intent,
+ kotlin(
+ """
+ package test.pkg
+
+ import android.content.Context
+ import android.content.Intent
+
+ fun makeIntent(context: Context) = Intent(context, String::class.java)
+ """,
+
+ ).indented(),
+ ).allowMissingSdk().run().expect(
+ """src/test/pkg/test.kt:6: Warning: Use functions from core.navigation [IntentDetector]
+fun makeIntent(context: Context) = Intent(context, String::class.java)
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+0 errors, 1 warnings""",
+ )
+ }
+
+ fun `test Intent action and data component constructor emits warning`() {
+ lint().files(
+ Context,
+ Intent,
+ kotlin(
+ """
+ package test.pkg
+
+ import android.content.Context
+ import android.content.Intent
+
+ fun makeIntent(context: Context) = Intent(
+ "someAction",
+ Uri.parse("https://example.com"),
+ context,
+ String::class.java,
+ )
+ """,
+ ).indented(),
+ ).allowMissingSdk().run().expect(
+ """src/test/pkg/test.kt:6: Warning: Use functions from core.navigation [IntentDetector]
+fun makeIntent(context: Context) = Intent(
+ ^
+0 errors, 1 warnings""",
+ )
+ }
+
+ fun `test empty constructor does not warn`() {
+ lint().files(
+ Context,
+ Intent,
+ kotlin(
+ """
+ package test.pkg
+
+ import android.content.Intent
+
+ fun makeIntent() = Intent()
+ """,
+ ).indented(),
+ ).allowMissingSdk().run().expectClean()
+ }
+
+ fun `test copy constructor does not warn`() {
+ lint().files(
+ Context,
+ Intent,
+ kotlin(
+ """
+ package test.pkg
+
+ import android.content.Intent
+
+ fun makeIntent(intent: Intent) = Intent(intent, 0)
+ """,
+ ).indented(),
+ ).allowMissingSdk().run().expectClean()
+ }
+
+ fun `test action constructor does not warn`() {
+ lint().files(
+ Context,
+ Intent,
+ kotlin(
+ """
+ package test.pkg
+
+ import android.content.Intent
+
+ fun makeIntent() = Intent("some action")
+ """,
+ ).indented(),
+ ).allowMissingSdk().run().expectClean()
+ }
+
+ fun `test action and uri constructor does not warn`() {
+ lint().files(
+ Context,
+ Intent,
+ kotlin(
+ """
+ package test.pkg
+
+ import android.content.Intent
+
+ fun makeIntent() = Intent("some action", Uri.parse("http://example.com"))
+ """,
+ ).indented(),
+ ).allowMissingSdk().run().expectClean()
+ }
+
+ companion object Stubs {
+ /** Stub for `android.content.Context` */
+ private val Context = java(
+ """
+ package android.content;
+
+ public class Context {}
+ """,
+ ).indented()
+
+ /** Stub for `android.content.Intent` */
+ private val Intent = java(
+ """
+ package android.content;
+
+ import android.content.Context;
+
+ public class Intent {
+ public Intent() { return null; }
+ public Intent(Intent o, int copyMode) { return null; }
+ public Intent(String action) { return null; }
+ public Intent(String action, Uri uri) { return null; }
+ public Intent(Context packageContext, Class> cls) { return null; }
+ public Intent(String action, Uri uri, Context packageContext, Class> cls) { return null; }
+ }
+ """,
+ ).indented()
+ }
+}
diff --git a/core/database/build.gradle.kts b/core/database/build.gradle.kts
index 07e5c982b..d0c33575c 100644
--- a/core/database/build.gradle.kts
+++ b/core/database/build.gradle.kts
@@ -36,5 +36,5 @@ dependencies {
implementation(projects.core.preferences)
// Because of the use of @SerializedName in DraftEntity
- compileOnly(libs.gson)
+ implementation(libs.gson)
}
diff --git a/core/navigation/README.md b/core/navigation/README.md
new file mode 100644
index 000000000..4e0cc3843
--- /dev/null
+++ b/core/navigation/README.md
@@ -0,0 +1,23 @@
+# :core:navigation
+
+## package app.pachli.core.navigation
+
+Intents for starting activities to break circular dependencies.
+
+A common approach for surfacing type-safe (ish) intents to start activities is for the activity-to-be-launched to provide a method in a companion object that returns the relevant intent, possibly taking additional parameters that will be included in the intent as extras.
+
+E.g., if A wants to start B, B provides the method that returns the intent.
+
+This introduces a dependency between A and B.
+
+This is worse if B also wants to start A.
+
+For example, if A is `StatusListActivity` and B is`ViewThreadActivity`. The user might click a status in `StatusListActivity` to view the thread, starting `ViewThreadActivity`. But from the thread they might click a hashtag to view the list of statuses with that hashtag. Now `StatusListActivity` and `ViewThreadActivity` have a circular dependency.
+
+Even if that doesn't happen the dependency means that any changes to B will trigger a rebuild of A, even if the changes to B are not relevant.
+
+This package contains `Intent` subclasses that should be used instead. The `quadrant` plugin is used to generate constants that can be used to launch activities by name instead of by class, breaking the dependency chain.
+
+If the activity's intent requires specific extras those are passed via the constructor, with companion object methods to extract them from the intent.
+
+Using the intent classes from this package is enforced by a lint `IntentDetector` which will warn if any intents are created using a class literal.
diff --git a/core/navigation/build.gradle.kts b/core/navigation/build.gradle.kts
new file mode 100644
index 000000000..a7e28fd7a
--- /dev/null
+++ b/core/navigation/build.gradle.kts
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2023 Pachli Association
+ *
+ * This file is a part of Pachli.
+ *
+ * This program is free software; you can redistribute it and/or modify it under the terms of the
+ * GNU General Public License as published by the Free Software Foundation; either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * Pachli is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
+ * the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
+ * Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with Pachli; if not,
+ * see .
+ */
+
+plugins {
+ alias(libs.plugins.pachli.android.library)
+ alias(libs.plugins.kotlin.parcelize)
+ alias(libs.plugins.quadrant)
+}
+
+android {
+ namespace = "app.pachli.core.navigation"
+
+ defaultConfig {
+ testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
+ }
+}
+
+dependencies {
+ implementation(projects.core.database) // For DraftAttachment, used in ComposeOptions
+ implementation(projects.core.network) // For Attachment, used in AttachmentViewData
+
+ implementation(libs.androidx.core.ktx) // IntentCompat
+}
diff --git a/core/navigation/lint-baseline.xml b/core/navigation/lint-baseline.xml
new file mode 100644
index 000000000..ddc2d3f41
--- /dev/null
+++ b/core/navigation/lint-baseline.xml
@@ -0,0 +1,4 @@
+
+
+
+
diff --git a/core/navigation/src/main/AndroidManifest.xml b/core/navigation/src/main/AndroidManifest.xml
new file mode 100644
index 000000000..76e9ba6e7
--- /dev/null
+++ b/core/navigation/src/main/AndroidManifest.xml
@@ -0,0 +1,21 @@
+
+
+
+
+
+
diff --git a/app/src/main/java/app/pachli/viewdata/AttachmentViewData.kt b/core/navigation/src/main/kotlin/app/pachli/core/navigation/AttachmentViewData.kt
similarity index 95%
rename from app/src/main/java/app/pachli/viewdata/AttachmentViewData.kt
rename to core/navigation/src/main/kotlin/app/pachli/core/navigation/AttachmentViewData.kt
index 504c074fd..daa947027 100644
--- a/app/src/main/java/app/pachli/viewdata/AttachmentViewData.kt
+++ b/core/navigation/src/main/kotlin/app/pachli/core/navigation/AttachmentViewData.kt
@@ -1,4 +1,5 @@
-/* Copyright 2022 Tusky Contributors
+/*
+ * Copyright 2022 Tusky Contributors
*
* This file is a part of Pachli.
*
@@ -14,7 +15,7 @@
* see .
*/
-package app.pachli.viewdata
+package app.pachli.core.navigation
import android.os.Parcelable
import app.pachli.core.network.model.Attachment
diff --git a/core/navigation/src/main/kotlin/app/pachli/core/navigation/Navigation.kt b/core/navigation/src/main/kotlin/app/pachli/core/navigation/Navigation.kt
new file mode 100644
index 000000000..48bfd3b32
--- /dev/null
+++ b/core/navigation/src/main/kotlin/app/pachli/core/navigation/Navigation.kt
@@ -0,0 +1,482 @@
+/*
+ * Copyright 2023 Pachli Association
+ *
+ * This file is a part of Pachli.
+ *
+ * This program is free software; you can redistribute it and/or modify it under the terms of the
+ * GNU General Public License as published by the Free Software Foundation; either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * Pachli is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
+ * the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
+ * Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with Pachli; if not,
+ * see .
+ */
+
+package app.pachli.core.navigation
+
+import android.content.Context
+import android.content.Intent
+import android.os.Parcelable
+import androidx.core.content.IntentCompat
+import app.pachli.core.database.model.DraftAttachment
+import app.pachli.core.navigation.LoginActivityIntent.LoginMode
+import app.pachli.core.navigation.StatusListActivityIntent.Companion.bookmarks
+import app.pachli.core.navigation.StatusListActivityIntent.Companion.favourites
+import app.pachli.core.navigation.StatusListActivityIntent.Companion.hashtag
+import app.pachli.core.navigation.StatusListActivityIntent.Companion.list
+import app.pachli.core.network.model.Attachment
+import app.pachli.core.network.model.Filter
+import app.pachli.core.network.model.NewPoll
+import app.pachli.core.network.model.Status
+import app.pachli.core.network.model.TimelineKind
+import com.gaelmarhic.quadrant.QuadrantConstants
+import kotlinx.parcelize.Parcelize
+
+/**
+ * @param context
+ * @param accountId Server ID of the account to view
+ * @see [app.pachli.components.account.AccountActivity]
+ */
+class AccountActivityIntent(context: Context, accountId: String) : Intent() {
+ init {
+ setClassName(context, "app.pachli${QuadrantConstants.ACCOUNT_ACTIVITY}")
+ putExtra(EXTRA_KEY_ACCOUNT_ID, accountId)
+ }
+
+ companion object {
+ private const val EXTRA_KEY_ACCOUNT_ID = "id"
+
+ /** @return the account ID passed in this intent */
+ fun getAccountId(intent: Intent) = intent.getStringExtra(EXTRA_KEY_ACCOUNT_ID)!!
+ }
+}
+
+/**
+ * @param context
+ * @param kind The kind of accounts to show
+ * @param id Optional ID. Sometimes an account ID, sometimes a status ID, and
+ * sometimes ignored. See [Kind] for details of how `id` is interpreted.
+ * @see [app.pachli.components.accountlist.AccountListActivity]
+ */
+class AccountListActivityIntent(context: Context, kind: Kind, id: String? = null) : Intent() {
+ enum class Kind {
+ /** Show the accounts the account with `id` is following */
+ FOLLOWS,
+
+ /** Show the accounts following the account with `id` */
+ FOLLOWERS,
+
+ /** Show the accounts the account with `id` is blocking */
+ BLOCKS,
+
+ /** Show the accounts the account with `id` is muting */
+ MUTES,
+
+ /** Show the logged in account's follow requests (`id` is ignored) */
+ FOLLOW_REQUESTS,
+
+ /** Show the accounts that reblogged the status with `id` */
+ REBLOGGED,
+
+ /** Show the accounts that favourited the status with `id` */
+ FAVOURITED,
+ }
+
+ init {
+ setClassName(context, "app.pachli${QuadrantConstants.ACCOUNT_LIST_ACTIVITY}")
+ putExtra(EXTRA_KIND, kind)
+ putExtra(EXTRA_ID, id)
+ }
+
+ companion object {
+ private const val EXTRA_KIND = "kind"
+ private const val EXTRA_ID = "id"
+
+ /** @return The [Kind] passed in this intent */
+ fun getKind(intent: Intent) = intent.getSerializableExtra(EXTRA_KIND) as Kind
+
+ /** @return The ID passed in this intent, or null */
+ fun getId(intent: Intent) = intent.getStringExtra(EXTRA_ID)
+ }
+}
+
+/**
+ * @param context
+ * @see [app.pachli.components.compose.ComposeActivity]
+ */
+class ComposeActivityIntent(context: Context) : Intent() {
+ @Parcelize
+ data class ComposeOptions(
+ val scheduledTootId: String? = null,
+ val draftId: Int? = null,
+ val content: String? = null,
+ val mediaUrls: List? = null,
+ val mediaDescriptions: List? = null,
+ val mentionedUsernames: Set? = null,
+ val inReplyToId: String? = null,
+ val replyVisibility: Status.Visibility? = null,
+ val visibility: Status.Visibility? = null,
+ val contentWarning: String? = null,
+ val replyingStatusAuthor: String? = null,
+ val replyingStatusContent: String? = null,
+ val mediaAttachments: List? = null,
+ val draftAttachments: List? = null,
+ val scheduledAt: String? = null,
+ val sensitive: Boolean? = null,
+ val poll: NewPoll? = null,
+ val modifiedInitialState: Boolean? = null,
+ val language: String? = null,
+ val statusId: String? = null,
+ val kind: ComposeKind? = null,
+ val initialCursorPosition: InitialCursorPosition = InitialCursorPosition.END,
+ ) : Parcelable {
+ /**
+ * Status' kind. This particularly affects how the status is handled if the user
+ * backs out of the edit.
+ */
+ enum class ComposeKind {
+ /** Status is new */
+ NEW,
+
+ /** Editing a posted status */
+ EDIT_POSTED,
+
+ /** Editing a status started as an existing draft */
+ EDIT_DRAFT,
+
+ /** Editing an an existing scheduled status */
+ EDIT_SCHEDULED,
+ }
+
+ /**
+ * Initial position of the cursor in EditText when the compose button is clicked
+ * in a hashtag timeline
+ */
+ enum class InitialCursorPosition {
+ /** Position the cursor at the start of the line */
+ START,
+
+ /** Position the cursor at the end of the line */
+ END,
+ }
+ }
+
+ init {
+ setClassName(context, "app.pachli${QuadrantConstants.COMPOSE_ACTIVITY}")
+ }
+
+ /**
+ * @param context
+ * @param options Configure the initial state of the activity
+ * @see [app.pachli.components.compose.ComposeActivity]
+ */
+ constructor(context: Context, options: ComposeOptions) : this(context) {
+ putExtra(EXTRA_COMPOSE_OPTIONS, options)
+ }
+
+ companion object {
+ private const val EXTRA_COMPOSE_OPTIONS = "composeOptions"
+
+ /** @return the [ComposeOptions] passed in this intent, or null */
+ fun getOptions(intent: Intent) = IntentCompat.getParcelableExtra(intent, EXTRA_COMPOSE_OPTIONS, ComposeOptions::class.java)
+ }
+}
+
+/**
+ * @param context
+ * @param filter Optional filter to edit. If null an empty filter is created.
+ * @see [app.pachli.components.filters.EditFilterActivity]
+ */
+class EditFilterActivityIntent(context: Context, filter: Filter? = null) : Intent() {
+ init {
+ setClassName(context, "app.pachli${QuadrantConstants.EDIT_FILTER_ACTIVITY}")
+ filter?.let {
+ putExtra(EXTRA_FILTER_TO_EDIT, it)
+ }
+ }
+
+ companion object {
+ const val EXTRA_FILTER_TO_EDIT = "filterToEdit"
+
+ /** @return the [Filter] passed in this intent, or null */
+ fun getFilter(intent: Intent) = IntentCompat.getParcelableExtra(intent, EXTRA_FILTER_TO_EDIT, Filter::class.java)
+ }
+}
+
+/**
+ * @param context
+ * @param loginMode See [LoginMode]
+ * @see [app.pachli.components.login.LoginActivity]
+ */
+class LoginActivityIntent(context: Context, loginMode: LoginMode = LoginMode.DEFAULT) : Intent() {
+ /** How to log in */
+ enum class LoginMode {
+ DEFAULT,
+
+ /** Already logged in, log in with an additional account */
+ ADDITIONAL_LOGIN,
+
+ /** Update the OAuth scope granted to the client */
+ MIGRATION,
+ }
+
+ init {
+ setClassName(context, "app.pachli${QuadrantConstants.LOGIN_ACTIVITY}")
+ putExtra(EXTRA_LOGIN_MODE, loginMode)
+ }
+
+ companion object {
+ private const val EXTRA_LOGIN_MODE = "loginMode"
+
+ /** @return the `loginMode` passed to this intent */
+ fun getLoginMode(intent: Intent) = intent.getSerializableExtra(EXTRA_LOGIN_MODE)!! as LoginMode
+ }
+}
+
+/**
+ * @param context
+ * @param screen The preference screen to show
+ * @see [app.pachli.components.preference.PreferencesActivity]
+ */
+class PreferencesActivityIntent(context: Context, screen: PreferenceScreen) : Intent() {
+ /** A specific preference screen */
+ enum class PreferenceScreen {
+ /** General preferences */
+ GENERAL,
+
+ /** Account-specific preferences */
+ ACCOUNT,
+
+ /** Notification preferences */
+ NOTIFICATION,
+ }
+ init {
+ setClassName(context, "app.pachli${QuadrantConstants.PREFERENCES_ACTIVITY}")
+ putExtra(EXTRA_PREFERENCE_SCREEN, screen)
+ }
+
+ companion object {
+ private const val EXTRA_PREFERENCE_SCREEN = "preferenceScreen"
+
+ /** @return the `screen` passed to this intent */
+ fun getPreferenceType(intent: Intent) = intent.getSerializableExtra(EXTRA_PREFERENCE_SCREEN)!! as PreferenceScreen
+ }
+}
+
+/**
+ * @param context
+ * @param accountId The ID of the account to report
+ * @param userName The username of the account to report
+ * @param statusId Optional ID of a status to include in the report
+ * @see [app.pachli.components.report.ReportActivity]
+ */
+class ReportActivityIntent(context: Context, accountId: String, userName: String, statusId: String? = null) : Intent() {
+ init {
+ setClassName(context, "app.pachli${QuadrantConstants.REPORT_ACTIVITY}")
+ putExtra(EXTRA_ACCOUNT_ID, accountId)
+ putExtra(EXTRA_ACCOUNT_USERNAME, userName)
+ putExtra(EXTRA_STATUS_ID, statusId)
+ }
+
+ companion object {
+ private const val EXTRA_ACCOUNT_ID = "accountId"
+ private const val EXTRA_ACCOUNT_USERNAME = "accountUsername"
+ private const val EXTRA_STATUS_ID = "statusId"
+
+ /** @return the `accountId` passed to this intent */
+ fun getAccountId(intent: Intent) = intent.getStringExtra(EXTRA_ACCOUNT_ID)!!
+ /** @return the `userName` passed to this intent */
+ fun getAccountUserName(intent: Intent) = intent.getStringExtra(EXTRA_ACCOUNT_USERNAME)!!
+ /** @return the `statusId` passed to this intent, or null */
+ fun getStatusId(intent: Intent) = intent.getStringExtra(EXTRA_STATUS_ID)
+ }
+}
+
+/**
+ * Use one of [bookmarks], [favourites], [hashtag], or [list] to construct.
+ */
+class StatusListActivityIntent private constructor (context: Context) : Intent() {
+ init {
+ setClassName(context, "app.pachli${QuadrantConstants.STATUS_LIST_ACTIVITY}")
+ }
+
+ companion object {
+ private const val EXTRA_KIND = "kind"
+
+ /**
+ * Show the user's bookmarks.
+ *
+ * @param context
+ */
+ fun bookmarks(context: Context) = StatusListActivityIntent(context).apply {
+ putExtra(EXTRA_KIND, TimelineKind.Bookmarks)
+ }
+
+ /**
+ * Show the user's favourites.
+ *
+ * @param context
+ */
+ fun favourites(context: Context) = StatusListActivityIntent(context).apply {
+ putExtra(EXTRA_KIND, TimelineKind.Favourites)
+ }
+
+ /**
+ * Show statuses containing [hashtag].
+ *
+ * @param context
+ * @param hashtag The hashtag to show, without the leading "`#`"
+ */
+ fun hashtag(context: Context, hashtag: String) = StatusListActivityIntent(context).apply {
+ putExtra(EXTRA_KIND, TimelineKind.Tag(listOf(hashtag)))
+ }
+
+ /**
+ * Show statuses from a list.
+ *
+ * @param context
+ * @param listId ID of the list to show
+ * @param title The title to display
+ */
+ fun list(context: Context, listId: String, title: String) = StatusListActivityIntent(context).apply {
+ putExtra(EXTRA_KIND, TimelineKind.UserList(listId, title))
+ }
+
+ /** @return The [TimelineKind] to show */
+ fun getKind(intent: Intent) = IntentCompat.getParcelableExtra(intent, EXTRA_KIND, TimelineKind::class.java)!!
+ }
+}
+
+class ViewMediaActivityIntent private constructor(context: Context) : Intent() {
+ init {
+ setClassName(context, "app.pachli${QuadrantConstants.VIEW_MEDIA_ACTIVITY}")
+ }
+
+ /**
+ * Show a collection of media attachments.
+ *
+ * @param context
+ * @param attachments The attachments to show
+ * @param index The index of the attachment in [attachments] to focus on
+ */
+ constructor(context: Context, attachments: List, index: Int) : this(context) {
+ putParcelableArrayListExtra(EXTRA_ATTACHMENTS, ArrayList(attachments))
+ putExtra(EXTRA_ATTACHMENT_INDEX, index)
+ }
+
+ /**
+ * Show a single image identified by a URL
+ *
+ * @param context
+ * @param url The URL of the image
+ */
+ constructor(context: Context, url: String) : this(context) {
+ putExtra(EXTRA_SINGLE_IMAGE_URL, url)
+ }
+
+ companion object {
+ private const val EXTRA_ATTACHMENTS = "attachments"
+ private const val EXTRA_ATTACHMENT_INDEX = "index"
+ private const val EXTRA_SINGLE_IMAGE_URL = "singleImage"
+
+ /** @return the list of [AttachmentViewData] passed in this intent, or null */
+ fun getAttachments(intent: Intent): ArrayList? = IntentCompat.getParcelableArrayListExtra(intent, EXTRA_ATTACHMENTS, AttachmentViewData::class.java)
+
+ /** @return the index of the attachment to show, or 0 */
+ fun getAttachmentIndex(intent: Intent) = intent.getIntExtra(EXTRA_ATTACHMENT_INDEX, 0)
+
+ /** @return the URL of the single image to show, null if no URL was included */
+ fun getImageUrl(intent: Intent) = intent.getStringExtra(EXTRA_SINGLE_IMAGE_URL)
+ }
+}
+
+/**
+ * @param context
+ * @param statusId ID of the status to start from (may be in the middle of the thread)
+ * @param statusUrl Optional URL of the status in `statusId`
+ * @see [app.pachli.components.viewthread.ViewThreadActivity]
+ */
+class ViewThreadActivityIntent(context: Context, statusId: String, statusUrl: String? = null) : Intent() {
+ init {
+ setClassName(context, "app.pachli${QuadrantConstants.VIEW_THREAD_ACTIVITY}")
+ putExtra(EXTRA_STATUS_ID, statusId)
+ putExtra(EXTRA_STATUS_URL, statusUrl)
+ }
+
+ companion object {
+ private const val EXTRA_STATUS_ID = "id"
+ private const val EXTRA_STATUS_URL = "url"
+
+ /** @return the `statusId` passed to this intent */
+ fun getStatusId(intent: Intent) = intent.getStringExtra(EXTRA_STATUS_ID)!!
+ /** @return the `statusUrl` passed to this intent, or null */
+ fun getUrl(intent: Intent) = intent.getStringExtra(EXTRA_STATUS_URL)
+ }
+}
+
+class AboutActivityIntent(context: Context) : Intent() {
+ init { setClassName(context, "app.pachli${QuadrantConstants.ABOUT_ACTIVITY}") }
+}
+
+class AnnouncementsActivityIntent(context: Context) : Intent() {
+ init { setClassName(context, "app.pachli${QuadrantConstants.ANNOUNCEMENTS_ACTIVITY}") }
+}
+
+class DraftsActivityIntent(context: Context) : Intent() {
+ init { setClassName(context, "app.pachli${QuadrantConstants.DRAFTS_ACTIVITY}") }
+}
+
+class EditProfileActivityIntent(context: Context) : Intent() {
+ init { setClassName(context, "app.pachli${QuadrantConstants.EDIT_PROFILE_ACTIVITY}") }
+}
+
+class FiltersActivityIntent(context: Context) : Intent() {
+ init { setClassName(context, "app.pachli${QuadrantConstants.FILTERS_ACTIVITY}") }
+}
+
+class FollowedTagsActivityIntent(context: Context) : Intent() {
+ init { setClassName(context, "app.pachli${QuadrantConstants.FOLLOWED_TAGS_ACTIVITY}") }
+}
+
+class InstanceListActivityIntent(context: Context) : Intent() {
+ init { setClassName(context, "app.pachli${QuadrantConstants.INSTANCE_LIST_ACTIVITY}") }
+}
+
+class LicenseActivityIntent(context: Context) : Intent() {
+ init { setClassName(context, "app.pachli${QuadrantConstants.LICENSE_ACTIVITY}") }
+}
+
+class ListActivityIntent(context: Context) : Intent() {
+ init { setClassName(context, "app.pachli${QuadrantConstants.LISTS_ACTIVITY}") }
+}
+
+class LoginWebViewActivityIntent(context: Context) : Intent() {
+ init { setClassName(context, "app.pachli${QuadrantConstants.LOGIN_WEB_VIEW_ACTIVITY}") }
+}
+
+class MainActivityIntent(context: Context) : Intent() {
+ init { setClassName(context, "app.pachli${QuadrantConstants.MAIN_ACTIVITY}") }
+}
+
+class PrivacyPolicyActivityIntent(context: Context) : Intent() {
+ init { setClassName(context, "app.pachli${QuadrantConstants.PRIVACY_POLICY_ACTIVITY}") }
+}
+
+class ScheduledStatusActivityIntent(context: Context) : Intent() {
+ init { setClassName(context, "app.pachli${QuadrantConstants.SCHEDULED_STATUS_ACTIVITY}") }
+}
+
+class SearchActivityIntent(context: Context) : Intent() {
+ init { setClassName(context, "app.pachli${QuadrantConstants.SEARCH_ACTIVITY}") }
+}
+
+class TabPreferenceActivityIntent(context: Context) : Intent() {
+ init { setClassName(context, "app.pachli${QuadrantConstants.TAB_PREFERENCE_ACTIVITY}") }
+}
+
+class TrendingActivityIntent(context: Context) : Intent() {
+ init { setClassName(context, "app.pachli${QuadrantConstants.TRENDING_ACTIVITY}") }
+}
diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
index 67d7e53d9..d97456ffe 100644
--- a/gradle/libs.versions.toml
+++ b/gradle/libs.versions.toml
@@ -50,6 +50,7 @@ mockito-inline = "5.2.0"
mockito-kotlin = "5.2.1"
networkresult-calladapter = "1.0.0"
okhttp = "4.12.0"
+quadrant = "1.7"
retrofit = "2.9.0"
robolectric = "4.11.1"
rxandroid3 = "3.0.2"
@@ -76,6 +77,7 @@ kotlin-jvm = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" }
kotlin-parcelize = { id = "org.jetbrains.kotlin.plugin.parcelize", version.ref = "kotlin" }
ktlint = "org.jlleitschuh.gradle.ktlint:11.6.1"
room = { id = "androidx.room", version.ref = "androidx-room" }
+quadrant = { id = "com.gaelmarhic.quadrant", version.ref = "quadrant" }
# Plugins defined by this project
pachli-android-application = { id = "pachli.android.application", version = "unspecified" }
diff --git a/settings.gradle.kts b/settings.gradle.kts
index 3c8e22ee0..76ae589ce 100644
--- a/settings.gradle.kts
+++ b/settings.gradle.kts
@@ -27,6 +27,7 @@ include(":core:accounts")
include(":core:common")
include(":core:database")
include(":core:preferences")
+include(":core:navigation")
include(":core:network")
include(":core:testing")
include(":tools:mklanguages")