From 69f4c1d9cf55c09cd034920421efc97347397c2b Mon Sep 17 00:00:00 2001 From: Konstantin Tskhovrebov Date: Sun, 9 Feb 2025 16:11:47 +0100 Subject: [PATCH 1/3] Huge refactoring of the authorization flow to make the app as single activity application. --- app/src/androidMain/AndroidManifest.xml | 64 +- .../com/daniebeler/pfpixelix/AppActivity.kt | 54 ++ .../com/daniebeler/pfpixelix/LoginActivity.kt | 189 ----- .../com/daniebeler/pfpixelix/MainActivity.kt | 776 ++++-------------- .../com/daniebeler/pfpixelix/MyApplication.kt | 3 +- .../pfpixelix/di/WorkerComponent.kt | 2 - .../pfpixelix/utils/KmpPlatform.android.kt | 9 - .../widget/WidgetRepositoryProvider.kt | 82 -- .../widget/latest_image/LatestImageWidget.kt | 34 +- .../work_manager/LatestImageTask.kt | 20 +- .../notifications/NotificationsWidget.kt | 51 +- .../work_manager/NotificationsTask.kt | 18 +- app/src/androidMain/res/values/themes.xml | 2 +- .../kotlin/com/daniebeler/pfpixelix/App.kt | 485 +++++++++++ .../pfpixelix/common/Destinations.kt | 8 + .../data/repository/AuthRepositoryImpl.kt | 190 ++--- .../data/repository/WidgetRepositoryImpl.kt | 6 +- .../daniebeler/pfpixelix/di/AppComponent.kt | 69 +- .../pfpixelix/di/EntryPointComponent.kt | 26 - .../pfpixelix/di/HostSelectionInterceptor.kt | 32 - .../di/HostSelectionInterceptorInterface.kt | 13 - .../pfpixelix/di/ViewModelComponent.kt | 2 +- .../pfpixelix/domain/model/AuthData.kt | 15 +- .../domain/service/session/AuthApi.kt | 49 ++ .../domain/service/session/AuthService.kt | 151 ++++ .../domain/service/session/Credentials.kt | 54 ++ .../domain/service/session/Session.kt | 36 + .../service/session/SystemUrlHandler.kt | 28 + .../domain/usecase/AddNewLoginUseCase.kt | 21 - .../domain/usecase/FinishLoginUseCase.kt | 21 - .../domain/usecase/GetAuthDataUseCase.kt | 12 - .../usecase/GetCurrentLoginDataUseCase.kt | 10 +- .../usecase/GetOwnInstanceDomainUseCase.kt | 11 +- .../pfpixelix/domain/usecase/LogoutUseCase.kt | 6 +- .../usecase/UpdateCurrentUserUseCase.kt | 15 - .../domain/usecase/UpdateLoginDataUseCase.kt | 21 - .../ui/composables/LoginComposable.kt | 253 ------ .../ui/composables/LoginViewModel.kt | 78 -- .../own_profile/AccountSwitchBottomSheet.kt | 32 +- .../own_profile/AccountSwitchViewModel.kt | 27 +- .../own_profile/OwnProfileComposable.kt | 8 +- .../ui/composables/session/LoginComposable.kt | 243 ++++++ .../ui/composables/session/LoginViewModel.kt | 47 ++ .../preferences/prefs/prefs/LogoutPref.kt | 7 - .../daniebeler/pfpixelix/utils/KmpPlatform.kt | 1 - .../com/daniebeler/pfpixelix/App.ios.kt | 7 + .../pfpixelix/utils/KmpPlatform.ios.kt | 3 - 47 files changed, 1600 insertions(+), 1691 deletions(-) create mode 100644 app/src/androidMain/kotlin/com/daniebeler/pfpixelix/AppActivity.kt delete mode 100644 app/src/androidMain/kotlin/com/daniebeler/pfpixelix/LoginActivity.kt delete mode 100644 app/src/androidMain/kotlin/com/daniebeler/pfpixelix/widget/WidgetRepositoryProvider.kt create mode 100644 app/src/commonMain/kotlin/com/daniebeler/pfpixelix/App.kt delete mode 100644 app/src/commonMain/kotlin/com/daniebeler/pfpixelix/di/EntryPointComponent.kt delete mode 100644 app/src/commonMain/kotlin/com/daniebeler/pfpixelix/di/HostSelectionInterceptor.kt delete mode 100644 app/src/commonMain/kotlin/com/daniebeler/pfpixelix/di/HostSelectionInterceptorInterface.kt create mode 100644 app/src/commonMain/kotlin/com/daniebeler/pfpixelix/domain/service/session/AuthApi.kt create mode 100644 app/src/commonMain/kotlin/com/daniebeler/pfpixelix/domain/service/session/AuthService.kt create mode 100644 app/src/commonMain/kotlin/com/daniebeler/pfpixelix/domain/service/session/Credentials.kt create mode 100644 app/src/commonMain/kotlin/com/daniebeler/pfpixelix/domain/service/session/Session.kt create mode 100644 app/src/commonMain/kotlin/com/daniebeler/pfpixelix/domain/service/session/SystemUrlHandler.kt delete mode 100644 app/src/commonMain/kotlin/com/daniebeler/pfpixelix/domain/usecase/AddNewLoginUseCase.kt delete mode 100644 app/src/commonMain/kotlin/com/daniebeler/pfpixelix/domain/usecase/FinishLoginUseCase.kt delete mode 100644 app/src/commonMain/kotlin/com/daniebeler/pfpixelix/domain/usecase/GetAuthDataUseCase.kt delete mode 100644 app/src/commonMain/kotlin/com/daniebeler/pfpixelix/domain/usecase/UpdateCurrentUserUseCase.kt delete mode 100644 app/src/commonMain/kotlin/com/daniebeler/pfpixelix/domain/usecase/UpdateLoginDataUseCase.kt delete mode 100644 app/src/commonMain/kotlin/com/daniebeler/pfpixelix/ui/composables/LoginComposable.kt delete mode 100644 app/src/commonMain/kotlin/com/daniebeler/pfpixelix/ui/composables/LoginViewModel.kt create mode 100644 app/src/commonMain/kotlin/com/daniebeler/pfpixelix/ui/composables/session/LoginComposable.kt create mode 100644 app/src/commonMain/kotlin/com/daniebeler/pfpixelix/ui/composables/session/LoginViewModel.kt create mode 100644 app/src/iosMain/kotlin/com/daniebeler/pfpixelix/App.ios.kt diff --git a/app/src/androidMain/AndroidManifest.xml b/app/src/androidMain/AndroidManifest.xml index 272a6d33..32eb074d 100644 --- a/app/src/androidMain/AndroidManifest.xml +++ b/app/src/androidMain/AndroidManifest.xml @@ -17,12 +17,24 @@ android:theme="@style/Theme.Pixelix" tools:ignore="Instantiatable" tools:targetApi="31"> - + + + + + + android:windowSoftInputMode="adjustResize" + android:configChanges="orientation|screenSize|screenLayout|keyboardHidden" + android:launchMode="singleInstance"> @@ -51,7 +63,16 @@ + + + + + + + + android:targetActivity=".AppActivity"> @@ -73,7 +94,7 @@ android:enabled="false" android:icon="@mipmap/ic_launcher_05" android:roundIcon="@mipmap/ic_launcher_05_round" - android:targetActivity=".MainActivity"> + android:targetActivity=".AppActivity"> @@ -86,7 +107,7 @@ android:enabled="false" android:icon="@mipmap/ic_launcher_06" android:roundIcon="@mipmap/ic_launcher_06_round" - android:targetActivity=".MainActivity"> + android:targetActivity=".AppActivity"> @@ -99,7 +120,7 @@ android:enabled="false" android:icon="@mipmap/ic_launcher_07" android:roundIcon="@mipmap/ic_launcher_07_round" - android:targetActivity=".MainActivity"> + android:targetActivity=".AppActivity"> @@ -112,7 +133,7 @@ android:enabled="false" android:icon="@mipmap/ic_launcher_08" android:roundIcon="@mipmap/ic_launcher_08_round" - android:targetActivity=".MainActivity"> + android:targetActivity=".AppActivity"> @@ -125,7 +146,7 @@ android:enabled="false" android:icon="@mipmap/ic_launcher_09" android:roundIcon="@mipmap/ic_launcher_09_round" - android:targetActivity=".MainActivity"> + android:targetActivity=".AppActivity"> @@ -138,7 +159,7 @@ android:enabled="false" android:icon="@mipmap/ic_launcher_01" android:roundIcon="@mipmap/ic_launcher_01_round" - android:targetActivity=".MainActivity"> + android:targetActivity=".AppActivity"> @@ -151,32 +172,13 @@ android:enabled="false" android:icon="@mipmap/ic_launcher_03" android:roundIcon="@mipmap/ic_launcher_03_round" - android:targetActivity=".MainActivity"> + android:targetActivity=".AppActivity"> - - - - - - - - - - - = Build.VERSION_CODES.R) { + window.attributes.fitInsetsTypes = 0 + window.attributes.fitInsetsSides = 0 + } +} \ No newline at end of file diff --git a/app/src/androidMain/kotlin/com/daniebeler/pfpixelix/LoginActivity.kt b/app/src/androidMain/kotlin/com/daniebeler/pfpixelix/LoginActivity.kt deleted file mode 100644 index 5aebb337..00000000 --- a/app/src/androidMain/kotlin/com/daniebeler/pfpixelix/LoginActivity.kt +++ /dev/null @@ -1,189 +0,0 @@ -package com.daniebeler.pfpixelix - -import android.content.Intent -import android.net.Uri -import android.os.Build -import android.os.Bundle -import androidx.activity.ComponentActivity -import androidx.activity.compose.setContent -import androidx.activity.enableEdgeToEdge -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.padding -import androidx.compose.material3.Scaffold -import androidx.compose.material3.dynamicDarkColorScheme -import androidx.compose.runtime.CompositionLocalProvider -import androidx.compose.runtime.compositionLocalOf -import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.setValue -import androidx.compose.ui.Modifier -import androidx.core.view.WindowCompat -import com.daniebeler.pfpixelix.common.Resource -import com.daniebeler.pfpixelix.di.EntryPointComponent -import com.daniebeler.pfpixelix.di.HostSelectionInterceptorInterface -import com.daniebeler.pfpixelix.di.LocalAppComponent -import com.daniebeler.pfpixelix.di.create -import com.daniebeler.pfpixelix.domain.model.LoginData -import com.daniebeler.pfpixelix.domain.usecase.AddNewLoginUseCase -import com.daniebeler.pfpixelix.domain.usecase.FinishLoginUseCase -import com.daniebeler.pfpixelix.domain.usecase.GetOngoingLoginUseCase -import com.daniebeler.pfpixelix.domain.usecase.ObtainTokenUseCase -import com.daniebeler.pfpixelix.domain.usecase.UpdateLoginDataUseCase -import com.daniebeler.pfpixelix.domain.usecase.VerifyTokenUseCase -import com.daniebeler.pfpixelix.ui.composables.LoginComposable -import com.daniebeler.pfpixelix.ui.theme.PixelixTheme -import com.daniebeler.pfpixelix.utils.LocalKmpContext -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.launch - -class LoginActivity : ComponentActivity() { - lateinit var obtainTokenUseCase: ObtainTokenUseCase - lateinit var verifyTokenUseCase: VerifyTokenUseCase - lateinit var updateLoginDataUseCase: UpdateLoginDataUseCase - lateinit var finishLoginUseCase: FinishLoginUseCase - lateinit var newLoginDataUseCase: AddNewLoginUseCase - lateinit var getOngoingLoginUseCase: GetOngoingLoginUseCase - lateinit var hostSelectionInterceptorInterface: HostSelectionInterceptorInterface - - - private var isLoadingAfterRedirect: Boolean by mutableStateOf(false) - private var error: String by mutableStateOf("") - - override fun onCreate(savedInstanceState: Bundle?) { - EntryPointComponent::class.create(MyApplication.appComponent).let { - obtainTokenUseCase = it.obtainTokenUseCase - verifyTokenUseCase = it.verifyTokenUseCase - updateLoginDataUseCase = it.updateLoginDataUseCase - finishLoginUseCase = it.finishLoginUseCase - newLoginDataUseCase = it.newLoginDataUseCase - getOngoingLoginUseCase = it.getOngoingLoginUseCase - hostSelectionInterceptorInterface = it.hostSelectionInterceptorInterface - } - - super.onCreate(savedInstanceState) - enableEdgeToEdge() - WindowCompat.setDecorFitsSystemWindows(window, false) - - - setContent { - CompositionLocalProvider( - LocalAppComponent provides MyApplication.appComponent, - LocalKmpContext provides this, - ) { - PixelixTheme( - dynamicColor = Build.VERSION.SDK_INT >= Build.VERSION_CODES.S - ) { - Scaffold { paddingValues -> - Column(Modifier.padding(paddingValues)) { - - } - LoginComposable(isLoadingAfterRedirect, error) - } - } - } - } - } - - override fun onStart() { - super.onStart() - val extras = intent.extras - if (extras != null) { - val baseUrl = extras.getString("base_url") - val accessToken = extras.getString("access_token") - if (baseUrl != null && accessToken != null) { - hostSelectionInterceptorInterface.setHost(baseUrl) - hostSelectionInterceptorInterface.setToken(accessToken) - CoroutineScope(Dispatchers.Default).launch { - verifyToken(LoginData(baseUrl = baseUrl, accessToken = accessToken), true) - } - } - } - - val url: Uri? = intent.data - - //Check if the activity was started after the authentication - if (url == null || !url.toString().startsWith("pixelix-android-auth://callback")) return - - val code = url.getQueryParameter("code") ?: "" - - - if (code.isNotEmpty()) { - - isLoadingAfterRedirect = true - CoroutineScope(Dispatchers.Default).launch { - getTokenAndRedirect(code) - } - } - } - - private suspend fun getTokenAndRedirect(code: String) { - val loginData: LoginData? = getOngoingLoginUseCase() - if (loginData == null) { - error = "an unexpected error occured" - isLoadingAfterRedirect = false - } else { - obtainTokenUseCase(loginData.clientId, loginData.clientSecret, code).collect { result -> - when (result) { - is Resource.Success -> { - val newLoginData = loginData.copy(accessToken = result.data!!.accessToken) - updateLoginDataUseCase(newLoginData) - verifyToken(newLoginData, false) - } - - is Resource.Error -> { - error = result.message ?: "Error" - isLoadingAfterRedirect = false - } - - is Resource.Loading -> { - isLoadingAfterRedirect = true - } - } - } - } - } - - private suspend fun verifyToken(loginData: LoginData, updateToAuthV2: Boolean) { - verifyTokenUseCase(loginData.accessToken).collect { result -> - when (result) { - is Resource.Success -> { - if (result.data == null) { - error = "an unexpected error occured" - isLoadingAfterRedirect = false - return@collect - } - val newLoginData = loginData.copy( - accountId = result.data.id, - username = result.data.username, - avatar = result.data.avatar, - displayName = result.data.displayname, - followers = result.data.followersCount, - loginOngoing = false - ) - if (updateToAuthV2) { - newLoginDataUseCase.invoke(newLoginData) - } - finishLoginUseCase(newLoginData, newLoginData.accountId) - - redirect() - } - - is Resource.Error -> { - error = result.message ?: "Error" - isLoadingAfterRedirect = false - } - - is Resource.Loading -> { - isLoadingAfterRedirect = true - } - } - } - } - - private fun redirect() { - val intent = Intent(applicationContext, MainActivity::class.java) - intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK) - applicationContext.startActivity(intent) - } -} \ No newline at end of file diff --git a/app/src/androidMain/kotlin/com/daniebeler/pfpixelix/MainActivity.kt b/app/src/androidMain/kotlin/com/daniebeler/pfpixelix/MainActivity.kt index d7d75b85..bd5e21a4 100644 --- a/app/src/androidMain/kotlin/com/daniebeler/pfpixelix/MainActivity.kt +++ b/app/src/androidMain/kotlin/com/daniebeler/pfpixelix/MainActivity.kt @@ -1,619 +1,157 @@ -package com.daniebeler.pfpixelix - -import android.content.ContentResolver -import android.content.Context -import android.content.Intent -import android.net.Uri -import android.os.Build -import android.os.Bundle -import androidx.activity.ComponentActivity -import androidx.activity.compose.setContent -import androidx.activity.enableEdgeToEdge -import androidx.compose.animation.EnterTransition -import androidx.compose.animation.ExitTransition -import androidx.compose.foundation.interaction.MutableInteractionSource -import androidx.compose.foundation.interaction.PressInteraction -import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.WindowInsets -import androidx.compose.foundation.layout.asPaddingValues -import androidx.compose.foundation.layout.height -import androidx.compose.foundation.layout.navigationBars -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.size -import androidx.compose.foundation.layout.width -import androidx.compose.foundation.shape.CircleShape -import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.outlined.UnfoldMore -import androidx.compose.material3.DrawerValue -import androidx.compose.material3.ExperimentalMaterial3Api -import androidx.compose.material3.Icon -import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.MaterialTheme.shapes -import androidx.compose.material3.ModalBottomSheet -import androidx.compose.material3.ModalDrawerSheet -import androidx.compose.material3.NavigationBar -import androidx.compose.material3.NavigationBarItem -import androidx.compose.material3.NavigationBarItemDefaults -import androidx.compose.material3.Scaffold -import androidx.compose.material3.rememberDrawerState -import androidx.compose.material3.rememberModalBottomSheetState -import androidx.compose.runtime.Composable -import androidx.compose.runtime.CompositionLocalProvider -import androidx.compose.runtime.LaunchedEffect -import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.remember -import androidx.compose.runtime.rememberCoroutineScope -import androidx.compose.runtime.setValue -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.draw.clip -import androidx.compose.ui.graphics.Color -import org.jetbrains.compose.resources.painterResource -import androidx.compose.ui.unit.Density -import androidx.compose.ui.unit.dp -import androidx.core.view.WindowCompat -import androidx.navigation.NavHostController -import androidx.navigation.compose.NavHost -import androidx.navigation.compose.composable -import androidx.navigation.compose.currentBackStackEntryAsState -import androidx.navigation.compose.rememberNavController -import androidx.navigation.navArgument -import co.touchlab.kermit.Logger -import coil3.compose.AsyncImage -import com.daniebeler.pfpixelix.common.Destinations -import com.daniebeler.pfpixelix.di.EntryPointComponent -import com.daniebeler.pfpixelix.di.HostSelectionInterceptorInterface -import com.daniebeler.pfpixelix.di.LocalAppComponent -import com.daniebeler.pfpixelix.di.create -import com.daniebeler.pfpixelix.domain.model.LoginData -import com.daniebeler.pfpixelix.domain.repository.CountryRepository -import com.daniebeler.pfpixelix.domain.usecase.GetCurrentLoginDataUseCase -import com.daniebeler.pfpixelix.domain.usecase.VerifyTokenUseCase -import com.daniebeler.pfpixelix.ui.composables.HomeComposable -import com.daniebeler.pfpixelix.ui.composables.ReverseModalNavigationDrawer -import com.daniebeler.pfpixelix.ui.composables.collection.CollectionComposable -import com.daniebeler.pfpixelix.ui.composables.direct_messages.chat.ChatComposable -import com.daniebeler.pfpixelix.ui.composables.direct_messages.conversations.ConversationsComposable -import com.daniebeler.pfpixelix.ui.composables.edit_post.EditPostComposable -import com.daniebeler.pfpixelix.ui.composables.edit_profile.EditProfileComposable -import com.daniebeler.pfpixelix.ui.composables.explore.ExploreComposable -import com.daniebeler.pfpixelix.ui.composables.followers.FollowersMainComposable -import com.daniebeler.pfpixelix.ui.composables.mention.MentionComposable -import com.daniebeler.pfpixelix.ui.composables.newpost.NewPostComposable -import com.daniebeler.pfpixelix.ui.composables.notifications.NotificationsComposable -import com.daniebeler.pfpixelix.ui.composables.profile.other_profile.OtherProfileComposable -import com.daniebeler.pfpixelix.ui.composables.profile.own_profile.AccountSwitchBottomSheet -import com.daniebeler.pfpixelix.ui.composables.profile.own_profile.OwnProfileComposable -import com.daniebeler.pfpixelix.ui.composables.settings.about_instance.AboutInstanceComposable -import com.daniebeler.pfpixelix.ui.composables.settings.about_pixelix.AboutPixelixComposable -import com.daniebeler.pfpixelix.ui.composables.settings.blocked_accounts.BlockedAccountsComposable -import com.daniebeler.pfpixelix.ui.composables.settings.bookmarked_posts.BookmarkedPostsComposable -import com.daniebeler.pfpixelix.ui.composables.settings.followed_hashtags.FollowedHashtagsComposable -import com.daniebeler.pfpixelix.ui.composables.settings.icon_selection.IconSelectionComposable -import com.daniebeler.pfpixelix.ui.composables.settings.liked_posts.LikedPostsComposable -import com.daniebeler.pfpixelix.ui.composables.settings.muted_accounts.MutedAccountsComposable -import com.daniebeler.pfpixelix.ui.composables.settings.preferences.PreferencesComposable -import com.daniebeler.pfpixelix.ui.composables.single_post.SinglePostComposable -import com.daniebeler.pfpixelix.ui.composables.timelines.hashtag_timeline.HashtagTimelineComposable -import com.daniebeler.pfpixelix.ui.theme.PixelixTheme -import com.daniebeler.pfpixelix.utils.LocalKmpContext -import com.daniebeler.pfpixelix.utils.Navigate -import com.daniebeler.pfpixelix.utils.end -import com.daniebeler.pfpixelix.utils.openLoginScreen -import kotlinx.coroutines.cancelChildren -import kotlinx.coroutines.delay -import kotlinx.coroutines.flow.firstOrNull -import kotlinx.coroutines.launch -import kotlinx.coroutines.runBlocking -import kotlinx.serialization.json.Json -import org.jetbrains.compose.resources.stringResource -import org.jetbrains.compose.resources.vectorResource -import pixelix.app.generated.resources.Res -import pixelix.app.generated.resources.default_avatar -import java.io.File -import java.io.FileOutputStream -import java.io.InputStream - - -class MainActivity : ComponentActivity() { - lateinit var currentLoginDataUseCase: GetCurrentLoginDataUseCase - lateinit var hostSelectionInterceptorInterface: HostSelectionInterceptorInterface - lateinit var repository: CountryRepository - lateinit var verifyTokenUseCase: VerifyTokenUseCase - - companion object { - const val KEY_DESTINATION: String = "destination" - const val KEY_DESTINATION_PARAM: String = "destination_parameter" - - enum class StartNavigation { - Notifications, Profile, Post - } - } - - - @OptIn(ExperimentalMaterial3Api::class) - override fun onCreate(savedInstanceState: Bundle?) { - EntryPointComponent::class.create(MyApplication.appComponent).let { - currentLoginDataUseCase = it.currentLoginDataUseCase - hostSelectionInterceptorInterface = it.hostSelectionInterceptorInterface - repository = it.repository - verifyTokenUseCase = it.verifyTokenUseCase - } - - super.onCreate(savedInstanceState) - enableEdgeToEdge() - WindowCompat.setDecorFitsSystemWindows(window, false) - var avatar = "" - runBlocking { - val loginData: LoginData? = currentLoginDataUseCase() - if (loginData == null || loginData.accessToken.isBlank() || loginData.baseUrl.isBlank()) { - val oldBaseurl: String? = repository.getAuthV1Baseurl().firstOrNull() - val oldAccessToken: String? = repository.getAuthV1Token().firstOrNull() - if (oldBaseurl != null && oldAccessToken != null && oldBaseurl.isNotBlank() && oldAccessToken.isNotBlank()) { - repository.deleteAuthV1Data() - updateAuthToV2(this@MainActivity, oldBaseurl, oldAccessToken) - } else { - openLoginScreen() - } - } else { - if (loginData.accessToken.isNotEmpty()) { - hostSelectionInterceptorInterface.setToken(loginData.accessToken) - } - if (loginData.baseUrl.isNotEmpty()) { - hostSelectionInterceptorInterface.setHost( - loginData.baseUrl.replace( - "https://", "" - ) - ) - } - avatar = loginData.avatar - } - } - - val imageUris = handleSharePhotoIntent(intent, contentResolver, cacheDir) - - - setContent { - CompositionLocalProvider( - LocalAppComponent provides MyApplication.appComponent, - LocalKmpContext provides this, - ) { - val scope = rememberCoroutineScope() - val drawerState = rememberDrawerState(DrawerValue.Closed) - - val sheetState = rememberModalBottomSheetState(skipPartiallyExpanded = true) - var showAccountSwitchBottomSheet by remember { mutableStateOf(false) } - - PixelixTheme( - dynamicColor = Build.VERSION.SDK_INT >= Build.VERSION_CODES.S - ) { - val navController: NavHostController = rememberNavController() - ReverseModalNavigationDrawer( - gesturesEnabled = drawerState.isOpen, - drawerState = drawerState, - drawerContent = { - ModalDrawerSheet( - drawerState = drawerState, - drawerShape = shapes.extraLarge.end(0.dp), - windowInsets = WindowInsets(0, 0, 0, 0) - ) { - PreferencesComposable(navController, drawerState, { - scope.launch { - drawerState.close() - } - }) - } - }) { - - Scaffold(contentWindowInsets = WindowInsets(0.dp), bottomBar = { - BottomBar( - navController = navController, - avatar = avatar, - openAccountSwitchBottomSheet = { - showAccountSwitchBottomSheet = true - }, - context = this - ) - }) { paddingValues -> - Box( - modifier = Modifier.padding(paddingValues) - ) { - NavigationGraph(navController = navController, { - scope.launch { - drawerState.open() - } - }) - - LaunchedEffect(imageUris) { - imageUris.forEach { uri -> - try { - contentResolver.takePersistableUriPermission( - uri, Intent.FLAG_GRANT_READ_URI_PERMISSION - ) - } catch (e: SecurityException) { - e.printStackTrace() // Handle permission denial gracefully - } - } - if (imageUris.isNotEmpty()) { - val urisJson = - Json.encodeToString(imageUris.map { uri -> uri.toString() }) - Navigate.navigate( - "new_post_screen?uris=$urisJson", navController - ) - } - } - - val destination = intent.extras?.getString(KEY_DESTINATION) ?: "" - if (destination.isNotBlank()) { - // Delay the navigation action to ensure the graph is set - LaunchedEffect(Unit) { - when (destination) { - StartNavigation.Notifications.toString() -> Navigate.navigate( - "notifications_screen", navController - ) - - StartNavigation.Profile.toString() -> { - val accountId: String = intent.extras?.getString( - KEY_DESTINATION_PARAM - ) ?: "" - if (accountId.isNotBlank()) { - Navigate.navigate( - "profile_screen/$accountId", navController - ) - } - } - - StartNavigation.Post.toString() -> { - val postId: String = intent.extras?.getString( - KEY_DESTINATION_PARAM - ) ?: "" - if (postId.isNotBlank()) { - Navigate.navigate( - "single_post_screen/$postId", navController - ) - - } - } - } - } - } - } - - - } - } - if (showAccountSwitchBottomSheet) { - ModalBottomSheet( - onDismissRequest = { - showAccountSwitchBottomSheet = false - }, sheetState = sheetState - ) { - AccountSwitchBottomSheet(closeBottomSheet = { - showAccountSwitchBottomSheet = false - }, null) - } - } - } - } - } - } -} - -fun saveUriToCache(uri: Uri, contentResolver: ContentResolver, cacheDir: File): Uri? { - try { - val inputStream: InputStream? = contentResolver.openInputStream(uri) - inputStream?.use { input -> - val file = File(cacheDir, "shared_image_${System.currentTimeMillis()}.jpg") - FileOutputStream(file).use { output -> - input.copyTo(output) - } - return Uri.fromFile(file) // Return the new cached URI - } - } catch (e: Exception) { - e.printStackTrace() - } - return null -} - -private fun handleSharePhotoIntent( - intent: Intent, contentResolver: ContentResolver, cacheDir: File -): List { - val action = intent.action - val type = intent.type - intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) - - var imageUris: List = emptyList() - when { - Intent.ACTION_SEND == action && type != null -> { - if (type.startsWith("image/") || type.startsWith("video/")) { - val singleUri = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { - intent.getParcelableExtra( - Intent.EXTRA_STREAM, Uri::class.java - ) - } else { - @Suppress("DEPRECATION") intent.getParcelableExtra( - Intent.EXTRA_STREAM - ) as? Uri - } - singleUri?.let { uri -> - val cachedUri = saveUriToCache(uri, contentResolver, cacheDir) - imageUris = - cachedUri?.let { listOf(it) } ?: emptyList() // Wrap single image in a list - } - } - } - - Intent.ACTION_SEND_MULTIPLE == action && type != null -> { - val receivedUris = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { - intent.getParcelableArrayListExtra( - Intent.EXTRA_STREAM, Uri::class.java - ) - } else { - @Suppress("DEPRECATION") intent.getParcelableArrayListExtra( - Intent.EXTRA_STREAM - ) - } - imageUris = receivedUris?.mapNotNull { - saveUriToCache( - it, contentResolver, cacheDir - ) - } ?: emptyList() - } - } - return imageUris -} - -fun updateAuthToV2(context: Context, baseUrl: String, accessToken: String) { - val intent = Intent(context, LoginActivity::class.java) - intent.putExtra("base_url", baseUrl) - intent.putExtra("access_token", accessToken) - context.startActivity(intent) -} - -@Composable -fun NavigationGraph( - navController: NavHostController, - openPreferencesDrawer: () -> Unit -) { - NavHost(navController, - startDestination = Destinations.HomeScreen.route, - enterTransition = { EnterTransition.None }, - exitTransition = { ExitTransition.None }) { - composable(Destinations.HomeScreen.route) { - HomeComposable(navController, openPreferencesDrawer) - } - - composable(Destinations.NotificationsScreen.route) { - NotificationsComposable(navController) - } - - composable(Destinations.Profile.route) { navBackStackEntry -> - val uId = navBackStackEntry.arguments?.getString("userid") - - uId?.let { id -> - OtherProfileComposable(navController, userId = id, byUsername = null) - - } - } - - composable(Destinations.ProfileByUsername.route) { navBackStackEntry -> - val username = navBackStackEntry.arguments?.getString("username") - - username?.let { - OtherProfileComposable(navController, userId = "", byUsername = it) - } - } - - composable(Destinations.Hashtag.route) { navBackStackEntry -> - val uId = navBackStackEntry.arguments?.getString("hashtag") - uId?.let { id -> - HashtagTimelineComposable(navController, id) - } - } - - composable(Destinations.EditProfile.route) { - EditProfileComposable(navController) - } - - composable(Destinations.IconSelection.route) { - IconSelectionComposable(navController) - } - - composable("${Destinations.NewPost.route}?uris={uris}") { navBackStackEntry -> - val urisJson = navBackStackEntry.arguments?.getString("uris") - val imageUris: List? = urisJson?.let { json -> - Json.decodeFromString>(json).map { Uri.parse(it) } - } - NewPostComposable(navController, imageUris) - } - - composable(Destinations.EditPost.route) { navBackStackEntry -> - val postId = navBackStackEntry.arguments?.getString("postId") - postId?.let { id -> - EditPostComposable(postId, navController) - } - } - - composable(Destinations.MutedAccounts.route) { - MutedAccountsComposable(navController) - } - - composable(Destinations.BlockedAccounts.route) { - BlockedAccountsComposable(navController) - } - - composable(Destinations.LikedPosts.route) { - LikedPostsComposable(navController) - } - - composable(Destinations.BookmarkedPosts.route) { - BookmarkedPostsComposable(navController) - } - - composable(Destinations.FollowedHashtags.route) { - FollowedHashtagsComposable(navController) - } - - composable(Destinations.AboutInstance.route) { - AboutInstanceComposable(navController) - } - - composable(Destinations.AboutPixelix.route) { - AboutPixelixComposable(navController) - } - - composable(Destinations.OwnProfile.route) { - OwnProfileComposable(navController, openPreferencesDrawer) - } - - composable(Destinations.Followers.route) { navBackStackEntry -> - val uId = navBackStackEntry.arguments?.getString("userid") - val page = navBackStackEntry.arguments?.getString("page") - if (uId != null && page != null) { - FollowersMainComposable(navController, accountId = uId, page = page) - } - } - - composable( - "${Destinations.SinglePost.route}?refresh={refresh}&openReplies={openReplies}", - arguments = listOf(navArgument("refresh") { - defaultValue = false - }, navArgument("openReplies") { - defaultValue = false - }) - ) { navBackStackEntry -> - val uId = navBackStackEntry.arguments?.getString("postid") - val refresh = navBackStackEntry.arguments?.getBoolean("refresh")!! - val openReplies = navBackStackEntry.arguments?.getBoolean("openReplies")!! - Logger.d("refresh") { refresh.toString() } - Logger.d("openReplies") { openReplies.toString() } - uId?.let { id -> - SinglePostComposable(navController, postId = id, refresh, openReplies) - } - } - - composable(Destinations.Collection.route) { navBackStackEntry -> - val uId = navBackStackEntry.arguments?.getString("collectionid") - uId?.let { id -> - CollectionComposable(navController, collectionId = id) - } - } - - composable(Destinations.Search.route) { - ExploreComposable(navController) - } - - composable(Destinations.Conversation.route) { - ConversationsComposable(navController = navController) - } - - composable(Destinations.Chat.route) { navBackStackEntry -> - val uId = navBackStackEntry.arguments?.getString("userid") - uId?.let { id -> - ChatComposable(navController = navController, accountId = id) - } - } - - composable(Destinations.Mention.route) { navBackStackEntry -> - val mentionId = navBackStackEntry.arguments?.getString("mentionid") - mentionId?.let { id -> - MentionComposable(navController = navController, mentionId = id) - } - } - } -} - -@Composable -fun BottomBar( - navController: NavHostController, - avatar: String, - openAccountSwitchBottomSheet: () -> Unit, - context: Context -) { - val screens = listOf( - Destinations.HomeScreen, - Destinations.Search, - Destinations.NewPost, - Destinations.NotificationsScreen, - Destinations.OwnProfile - ) - val systemNavigationBarHeight = - WindowInsets.navigationBars.asPaddingValues(Density(context)).calculateBottomPadding() - - NavigationBar( - modifier = Modifier.height(60.dp + systemNavigationBarHeight) - ) { - val navBackStackEntry by navController.currentBackStackEntryAsState() - val currentRoute = navBackStackEntry?.destination?.route - screens.forEach { screen -> - val interactionSource = remember { MutableInteractionSource() } - val coroutineScope = rememberCoroutineScope() - var isLongPress by remember { mutableStateOf(false) } - - LaunchedEffect(interactionSource) { - interactionSource.interactions.collect { interaction -> - when (interaction) { - is PressInteraction.Press -> { - isLongPress = false // Reset flag before starting detection - coroutineScope.launch { - delay(500L) // Long-press threshold - if (screen.route == Destinations.OwnProfile.route) { - openAccountSwitchBottomSheet() - } - isLongPress = true - } - } - - is PressInteraction.Release, is PressInteraction.Cancel -> { - coroutineScope.coroutineContext.cancelChildren() - } - } - } - } - NavigationBarItem(icon = { - if (screen.route == Destinations.OwnProfile.route && avatar.isNotBlank()) { - Row(verticalAlignment = Alignment.CenterVertically) { - AsyncImage( - model = avatar, - error = painterResource(Res.drawable.default_avatar), - contentDescription = "", - modifier = Modifier - .height(30.dp) - .width(30.dp) - .clip(CircleShape) - ) - Icon( - Icons.Outlined.UnfoldMore, - contentDescription = "long press to switch account" - ) - } - } else if (currentRoute?.startsWith(screen.route) == true) { - Icon( - imageVector = vectorResource(screen.activeIcon), - modifier = Modifier.size(30.dp), - contentDescription = stringResource(screen.label) - ) - } else { - Icon( - imageVector = vectorResource(screen.icon), - modifier = Modifier.size(30.dp), - contentDescription = stringResource(screen.label) - ) - } - }, - selected = currentRoute == screen.route, - colors = NavigationBarItemDefaults.colors( - selectedIconColor = MaterialTheme.colorScheme.inverseSurface, - indicatorColor = Color.Transparent - ), - interactionSource = interactionSource, - onClick = { - if (!isLongPress) { - Navigate.navigateWithPopUp(screen.route, navController) - } - }) - } - } -} +//todo kmp +// +// runBlocking { +// val loginData: LoginData? = currentLoginDataUseCase() +// if (loginData == null || loginData.accessToken.isBlank() || loginData.baseUrl.isBlank()) { +// val oldBaseurl: String? = repository.getAuthV1Baseurl().firstOrNull() +// val oldAccessToken: String? = repository.getAuthV1Token().firstOrNull() +// if (oldBaseurl != null && oldAccessToken != null && oldBaseurl.isNotBlank() && oldAccessToken.isNotBlank()) { +// repository.deleteAuthV1Data() +// updateAuthToV2(this@MainActivity, oldBaseurl, oldAccessToken) +// } else { +// openLoginScreen() +// } +// } else { +// if (loginData.accessToken.isNotEmpty()) { +// hostSelectionInterceptorInterface.setToken(loginData.accessToken) +// } +// if (loginData.baseUrl.isNotEmpty()) { +// hostSelectionInterceptorInterface.setHost( +// loginData.baseUrl.replace( +// "https://", "" +// ) +// ) +// } +// avatar = loginData.avatar +// } +// } +// +// val imageUris = handleSharePhotoIntent(intent, contentResolver, cacheDir) +// +// +// LaunchedEffect(imageUris) { +// imageUris.forEach { uri -> +// try { +// contentResolver.takePersistableUriPermission( +// uri, Intent.FLAG_GRANT_READ_URI_PERMISSION +// ) +// } catch (e: SecurityException) { +// e.printStackTrace() // Handle permission denial gracefully +// } +// } +// if (imageUris.isNotEmpty()) { +// val urisJson = +// Json.encodeToString(imageUris.map { uri -> uri.toString() }) +// Navigate.navigate( +// "new_post_screen?uris=$urisJson", navController +// ) +// } +// } +// +// val destination = intent.extras?.getString(KEY_DESTINATION) ?: "" +// if (destination.isNotBlank()) { +// // Delay the navigation action to ensure the graph is set +// LaunchedEffect(Unit) { +// when (destination) { +// StartNavigation.Notifications.toString() -> Navigate.navigate( +// "notifications_screen", navController +// ) +// +// StartNavigation.Profile.toString() -> { +// val accountId: String = intent.extras?.getString( +// KEY_DESTINATION_PARAM +// ) ?: "" +// if (accountId.isNotBlank()) { +// Navigate.navigate( +// "profile_screen/$accountId", navController +// ) +// } +// } +// +// StartNavigation.Post.toString() -> { +// val postId: String = intent.extras?.getString( +// KEY_DESTINATION_PARAM +// ) ?: "" +// if (postId.isNotBlank()) { +// Navigate.navigate( +// "single_post_screen/$postId", navController +// ) +// +// } +// } +// } +// } +// } +// } +// +// +//fun saveUriToCache(uri: Uri, contentResolver: ContentResolver, cacheDir: File): Uri? { +// try { +// val inputStream: InputStream? = contentResolver.openInputStream(uri) +// inputStream?.use { input -> +// val file = File(cacheDir, "shared_image_${System.currentTimeMillis()}.jpg") +// FileOutputStream(file).use { output -> +// input.copyTo(output) +// } +// return Uri.fromFile(file) // Return the new cached URI +// } +// } catch (e: Exception) { +// e.printStackTrace() +// } +// return null +//} +// +//private fun handleSharePhotoIntent( +// intent: Intent, contentResolver: ContentResolver, cacheDir: File +//): List { +// val action = intent.action +// val type = intent.type +// intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) +// +// var imageUris: List = emptyList() +// when { +// Intent.ACTION_SEND == action && type != null -> { +// if (type.startsWith("image/") || type.startsWith("video/")) { +// val singleUri = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { +// intent.getParcelableExtra( +// Intent.EXTRA_STREAM, Uri::class.java +// ) +// } else { +// @Suppress("DEPRECATION") intent.getParcelableExtra( +// Intent.EXTRA_STREAM +// ) as? Uri +// } +// singleUri?.let { uri -> +// val cachedUri = saveUriToCache(uri, contentResolver, cacheDir) +// imageUris = +// cachedUri?.let { listOf(it) } ?: emptyList() // Wrap single image in a list +// } +// } +// } +// +// Intent.ACTION_SEND_MULTIPLE == action && type != null -> { +// val receivedUris = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { +// intent.getParcelableArrayListExtra( +// Intent.EXTRA_STREAM, Uri::class.java +// ) +// } else { +// @Suppress("DEPRECATION") intent.getParcelableArrayListExtra( +// Intent.EXTRA_STREAM +// ) +// } +// imageUris = receivedUris?.mapNotNull { +// saveUriToCache( +// it, contentResolver, cacheDir +// ) +// } ?: emptyList() +// } +// } +// return imageUris +//} +// +//fun updateAuthToV2(context: Context, baseUrl: String, accessToken: String) { +// val intent = Intent(context, LoginActivity::class.java) +// intent.putExtra("base_url", baseUrl) +// intent.putExtra("access_token", accessToken) +// context.startActivity(intent) +//} diff --git a/app/src/androidMain/kotlin/com/daniebeler/pfpixelix/MyApplication.kt b/app/src/androidMain/kotlin/com/daniebeler/pfpixelix/MyApplication.kt index 8357fb17..4bf0a463 100644 --- a/app/src/androidMain/kotlin/com/daniebeler/pfpixelix/MyApplication.kt +++ b/app/src/androidMain/kotlin/com/daniebeler/pfpixelix/MyApplication.kt @@ -21,7 +21,7 @@ class MyApplication : Application(), Configuration.Provider { get() = Configuration.Builder().setWorkerFactory(workerFactory).build() override fun onCreate() { - appComponent = AppComponent::class.create(this) + appComponent = AppComponent.create(this) SingletonImageLoader.setSafe { appComponent.provideImageLoader() } @@ -44,7 +44,6 @@ private class MyWorkerFactory( ): ListenableWorker? { val workerComponent = WorkerComponent::class.create( appComponent, - appContext, workerParameters ) return when(workerClassName) { diff --git a/app/src/androidMain/kotlin/com/daniebeler/pfpixelix/di/WorkerComponent.kt b/app/src/androidMain/kotlin/com/daniebeler/pfpixelix/di/WorkerComponent.kt index f908e416..0b260f65 100644 --- a/app/src/androidMain/kotlin/com/daniebeler/pfpixelix/di/WorkerComponent.kt +++ b/app/src/androidMain/kotlin/com/daniebeler/pfpixelix/di/WorkerComponent.kt @@ -1,6 +1,5 @@ package com.daniebeler.pfpixelix.di -import android.content.Context import androidx.work.WorkerParameters import com.daniebeler.pfpixelix.widget.notifications.work_manager.LatestImageTask import com.daniebeler.pfpixelix.widget.notifications.work_manager.NotificationsTask @@ -10,7 +9,6 @@ import me.tatarka.inject.annotations.Provides @Component abstract class WorkerComponent( @Component val appComponent: AppComponent, - @get:Provides val context: Context, @get:Provides val workerParameters: WorkerParameters ) { abstract val notificationsTask: NotificationsTask diff --git a/app/src/androidMain/kotlin/com/daniebeler/pfpixelix/utils/KmpPlatform.android.kt b/app/src/androidMain/kotlin/com/daniebeler/pfpixelix/utils/KmpPlatform.android.kt index 97054f33..0807dd31 100644 --- a/app/src/androidMain/kotlin/com/daniebeler/pfpixelix/utils/KmpPlatform.android.kt +++ b/app/src/androidMain/kotlin/com/daniebeler/pfpixelix/utils/KmpPlatform.android.kt @@ -16,7 +16,6 @@ import androidx.core.graphics.drawable.toBitmap import androidx.core.net.toUri import co.touchlab.kermit.Logger import coil3.PlatformContext -import com.daniebeler.pfpixelix.LoginActivity import com.daniebeler.pfpixelix.R import com.daniebeler.pfpixelix.ui.composables.settings.icon_selection.IconWithName import com.daniebeler.pfpixelix.utils.ThemePrefUtil.AMOLED @@ -77,14 +76,6 @@ actual fun KmpContext.setDefaultNightMode(mode: Int) { ) } -actual fun KmpContext.openLoginScreen(isAbleToGotBack: Boolean) { - val intent = Intent(this, LoginActivity::class.java) - if (!isAbleToGotBack) { - intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK) - } - startActivity(intent) -} - actual fun KmpContext.getCacheSizeInBytes(): Long { return cacheDir.walkBottomUp().fold(0L) { acc, file -> acc + file.length() } } diff --git a/app/src/androidMain/kotlin/com/daniebeler/pfpixelix/widget/WidgetRepositoryProvider.kt b/app/src/androidMain/kotlin/com/daniebeler/pfpixelix/widget/WidgetRepositoryProvider.kt deleted file mode 100644 index ec323834..00000000 --- a/app/src/androidMain/kotlin/com/daniebeler/pfpixelix/widget/WidgetRepositoryProvider.kt +++ /dev/null @@ -1,82 +0,0 @@ -package com.daniebeler.pfpixelix.widget - -import HostSelectionInterceptor -import androidx.datastore.core.DataStore -import co.touchlab.kermit.Logger -import com.daniebeler.pfpixelix.data.remote.PixelfedApi -import com.daniebeler.pfpixelix.data.remote.createPixelfedApi -import com.daniebeler.pfpixelix.data.repository.WidgetRepositoryImpl -import com.daniebeler.pfpixelix.domain.model.AuthData -import com.daniebeler.pfpixelix.domain.model.LoginData -import com.daniebeler.pfpixelix.domain.repository.WidgetRepository -import de.jensklingenberg.ktorfit.Ktorfit -import de.jensklingenberg.ktorfit.converter.CallConverterFactory -import io.ktor.client.HttpClient -import io.ktor.client.plugins.HttpSend -import io.ktor.client.plugins.contentnegotiation.ContentNegotiation -import io.ktor.client.plugins.logging.LogLevel -import io.ktor.client.plugins.logging.Logging -import io.ktor.client.plugins.plugin -import io.ktor.serialization.kotlinx.json.json -import kotlinx.coroutines.flow.first -import kotlinx.serialization.json.Json - -class WidgetRepositoryProvider(private val dataStore: DataStore) { - suspend operator fun invoke(): WidgetRepository? { - val hostSelectionInterceptor = HostSelectionInterceptor() - val loginData: LoginData? = getAuthData() - if (loginData == null) { - return null - } - val baseUrl = loginData.baseUrl - val jwtToken = loginData.accessToken - if (baseUrl.isBlank() || jwtToken.isBlank()) { - return null - } - hostSelectionInterceptor.setHost(baseUrl.replace("https://", "")) - hostSelectionInterceptor.setToken( - jwtToken - ) - - - val json = Json { - ignoreUnknownKeys = true - isLenient = true - explicitNulls = false - } - - val client = HttpClient { - install(ContentNegotiation) { json(json) } - install(Logging) { - logger = object : io.ktor.client.plugins.logging.Logger { - override fun log(message: String) { - Logger.v("HttpClient") { - message.lines().joinToString { "\n\t\t$it" } - } - } - } - level = LogLevel.BODY - } - }.apply { - plugin(HttpSend).intercept { request -> - with(hostSelectionInterceptor) { - intercept(request) - } - } - } - - val ktorfit = Ktorfit.Builder() - .converterFactories(CallConverterFactory()) - .httpClient(client) - .baseUrl("https://err.or/") - .build() - - val service: PixelfedApi = ktorfit.createPixelfedApi() - return WidgetRepositoryImpl(service) - } - - private suspend fun getAuthData(): LoginData? { - val currentlyLoggedIn = dataStore.data.first().currentlyLoggedIn - return dataStore.data.first().loginDataList.find { it.accountId == currentlyLoggedIn } - } -} \ No newline at end of file diff --git a/app/src/androidMain/kotlin/com/daniebeler/pfpixelix/widget/latest_image/LatestImageWidget.kt b/app/src/androidMain/kotlin/com/daniebeler/pfpixelix/widget/latest_image/LatestImageWidget.kt index e8ba484a..977dbc2d 100644 --- a/app/src/androidMain/kotlin/com/daniebeler/pfpixelix/widget/latest_image/LatestImageWidget.kt +++ b/app/src/androidMain/kotlin/com/daniebeler/pfpixelix/widget/latest_image/LatestImageWidget.kt @@ -11,8 +11,6 @@ import androidx.glance.Image import androidx.glance.ImageProvider import androidx.glance.LocalContext import androidx.glance.action.ActionParameters -import androidx.glance.action.actionParametersOf -import androidx.glance.action.actionStartActivity import androidx.glance.action.clickable import androidx.glance.appwidget.CircularProgressIndicator import androidx.glance.appwidget.GlanceAppWidget @@ -32,10 +30,7 @@ import androidx.glance.layout.padding import androidx.glance.state.GlanceStateDefinition import androidx.glance.text.Text import androidx.glance.text.TextStyle -import com.daniebeler.pfpixelix.MainActivity import com.daniebeler.pfpixelix.R -import pixelix.app.generated.resources.Res -import pixelix.app.generated.resources.* import com.daniebeler.pfpixelix.widget.WidgetColors import com.daniebeler.pfpixelix.widget.latest_image.utils.GetImageProvider import com.daniebeler.pfpixelix.widget.notifications.models.LatestImageStore @@ -46,12 +41,12 @@ class LatestImageWidget : GlanceAppWidget() { override var stateDefinition: GlanceStateDefinition<*> = CustomLatestImageStateDefinition - private val destinationKey = ActionParameters.Key( - MainActivity.KEY_DESTINATION - ) - private val destinationKeyParam = ActionParameters.Key( - MainActivity.KEY_DESTINATION_PARAM - ) +// private val destinationKey = ActionParameters.Key( +// MainActivity.KEY_DESTINATION +// ) +// private val destinationKeyParam = ActionParameters.Key( +// MainActivity.KEY_DESTINATION_PARAM +// ) override suspend fun provideGlance(context: Context, id: GlanceId) { provideContent { @@ -72,14 +67,15 @@ class LatestImageWidget : GlanceAppWidget() { Image( provider = GetImageProvider()(state.latestImageUri, context,50f), contentDescription = "latest home timeline picture", - modifier = GlanceModifier.fillMaxSize().clickable( - actionStartActivity( - actionParametersOf( - destinationKey to MainActivity.Companion.StartNavigation.Post.toString(), - destinationKeyParam to state.postId - ) - ) - ) + modifier = GlanceModifier.fillMaxSize() +// .clickable( +// actionStartActivity( +// actionParametersOf( +// destinationKey to MainActivity.Companion.StartNavigation.Post.toString(), +// destinationKeyParam to state.postId +// ) +// ) +// ) ) } else { Column( diff --git a/app/src/androidMain/kotlin/com/daniebeler/pfpixelix/widget/latest_image/work_manager/LatestImageTask.kt b/app/src/androidMain/kotlin/com/daniebeler/pfpixelix/widget/latest_image/work_manager/LatestImageTask.kt index 3058fafa..c63e4be0 100644 --- a/app/src/androidMain/kotlin/com/daniebeler/pfpixelix/widget/latest_image/work_manager/LatestImageTask.kt +++ b/app/src/androidMain/kotlin/com/daniebeler/pfpixelix/widget/latest_image/work_manager/LatestImageTask.kt @@ -1,35 +1,37 @@ package com.daniebeler.pfpixelix.widget.notifications.work_manager -import android.content.Context import android.content.Intent import android.content.Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION import android.content.Intent.FLAG_GRANT_READ_URI_PERMISSION import android.content.pm.PackageManager import androidx.core.content.FileProvider.getUriForFile -import androidx.datastore.core.DataStore import androidx.work.CoroutineWorker import androidx.work.WorkerParameters import coil3.imageLoader import coil3.request.ErrorResult import coil3.request.ImageRequest import com.daniebeler.pfpixelix.common.Resource -import com.daniebeler.pfpixelix.domain.model.AuthData -import com.daniebeler.pfpixelix.widget.WidgetRepositoryProvider +import com.daniebeler.pfpixelix.domain.repository.WidgetRepository +import com.daniebeler.pfpixelix.domain.service.session.AuthService +import com.daniebeler.pfpixelix.utils.KmpContext import com.daniebeler.pfpixelix.widget.latest_image.updateLatestImageWidget import com.daniebeler.pfpixelix.widget.latest_image.updateLatestImageWidgetRefreshing +import com.daniebeler.pfpixelix.widget.notifications.updateNotificationsWidget +import kotlinx.coroutines.flow.firstOrNull import me.tatarka.inject.annotations.Inject class LatestImageTask @Inject constructor( - private val context: Context, + private val context: KmpContext, workerParams: WorkerParameters, - private val dataStore: DataStore + private val authService: AuthService, + private val repository: WidgetRepository, ) : CoroutineWorker(context, workerParams) { override suspend fun doWork(): Result { try { updateLatestImageWidgetRefreshing(context) - val repository = WidgetRepositoryProvider(dataStore).invoke() - if (repository == null) { - updateLatestImageWidget("", "", context, "you have to be logged in to an account") + authService.openSessionIfExist() + if (authService.activeUser.firstOrNull() == null) { + updateNotificationsWidget(emptyList(), context, "you have to be logged in to an account") return Result.failure() } val res = repository.getLatestImage() diff --git a/app/src/androidMain/kotlin/com/daniebeler/pfpixelix/widget/notifications/NotificationsWidget.kt b/app/src/androidMain/kotlin/com/daniebeler/pfpixelix/widget/notifications/NotificationsWidget.kt index 75d508cf..b14b10a4 100644 --- a/app/src/androidMain/kotlin/com/daniebeler/pfpixelix/widget/notifications/NotificationsWidget.kt +++ b/app/src/androidMain/kotlin/com/daniebeler/pfpixelix/widget/notifications/NotificationsWidget.kt @@ -14,8 +14,6 @@ import androidx.glance.ImageProvider import androidx.glance.LocalContext import androidx.glance.LocalSize import androidx.glance.action.ActionParameters -import androidx.glance.action.actionParametersOf -import androidx.glance.action.actionStartActivity import androidx.glance.action.clickable import androidx.glance.appwidget.CircularProgressIndicator import androidx.glance.appwidget.GlanceAppWidget @@ -42,23 +40,20 @@ import androidx.glance.state.GlanceStateDefinition import androidx.glance.text.FontWeight import androidx.glance.text.Text import androidx.glance.text.TextStyle -import com.daniebeler.pfpixelix.MainActivity import com.daniebeler.pfpixelix.R -import pixelix.app.generated.resources.Res -import pixelix.app.generated.resources.* import com.daniebeler.pfpixelix.widget.WidgetColors import com.daniebeler.pfpixelix.widget.latest_image.utils.GetImageProvider import com.daniebeler.pfpixelix.widget.notifications.models.NotificationStoreItem import com.daniebeler.pfpixelix.widget.notifications.models.NotificationsStore import com.daniebeler.pfpixelix.widget.notifications.work_manager.NotificationsWorkManager -private val destinationKey = ActionParameters.Key( - MainActivity.KEY_DESTINATION -) - -private val destinationKeyParam = ActionParameters.Key( - MainActivity.KEY_DESTINATION_PARAM -) +//private val destinationKey = ActionParameters.Key( +// MainActivity.KEY_DESTINATION +//) +// +//private val destinationKeyParam = ActionParameters.Key( +// MainActivity.KEY_DESTINATION_PARAM +//) class NotificationsWidget : GlanceAppWidget() { @@ -106,13 +101,15 @@ class NotificationsWidget : GlanceAppWidget() { verticalAlignment = Alignment.CenterVertically ) { Row( - modifier = GlanceModifier.clickable( - actionStartActivity( - actionParametersOf( - destinationKey to MainActivity.Companion.StartNavigation.Notifications.toString(), - ) - ) - ), verticalAlignment = Alignment.CenterVertically + modifier = GlanceModifier +// .clickable( +// actionStartActivity( +// actionParametersOf( +// destinationKey to MainActivity.Companion.StartNavigation.Notifications.toString(), +// ) +// ) +// ) + , verticalAlignment = Alignment.CenterVertically ) { if (size.height >= BIG_SQUARE.height && size.width >= BIG_SQUARE.width) { Image( @@ -185,14 +182,14 @@ class NotificationsWidget : GlanceAppWidget() { private fun NotificationItem(notification: NotificationStoreItem, context: Context) { val size = LocalSize.current Box( - modifier = GlanceModifier.clickable( - actionStartActivity( - actionParametersOf( - destinationKey to MainActivity.Companion.StartNavigation.Profile.toString(), - destinationKeyParam to notification.accountId - ) - ) - ) +// modifier = GlanceModifier.clickable( +// actionStartActivity( +// actionParametersOf( +// destinationKey to MainActivity.Companion.StartNavigation.Profile.toString(), +// destinationKeyParam to notification.accountId +// ) +// ) +// ) ) { Column { Spacer(GlanceModifier.height(12.dp)) diff --git a/app/src/androidMain/kotlin/com/daniebeler/pfpixelix/widget/notifications/work_manager/NotificationsTask.kt b/app/src/androidMain/kotlin/com/daniebeler/pfpixelix/widget/notifications/work_manager/NotificationsTask.kt index ca9c761f..1356a990 100644 --- a/app/src/androidMain/kotlin/com/daniebeler/pfpixelix/widget/notifications/work_manager/NotificationsTask.kt +++ b/app/src/androidMain/kotlin/com/daniebeler/pfpixelix/widget/notifications/work_manager/NotificationsTask.kt @@ -1,36 +1,36 @@ package com.daniebeler.pfpixelix.widget.notifications.work_manager -import android.content.Context import android.content.Intent import android.content.Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION import android.content.Intent.FLAG_GRANT_READ_URI_PERMISSION import android.content.pm.PackageManager import androidx.core.content.FileProvider.getUriForFile -import androidx.datastore.core.DataStore import androidx.work.CoroutineWorker import androidx.work.WorkerParameters import coil3.imageLoader import coil3.request.ErrorResult import coil3.request.ImageRequest import com.daniebeler.pfpixelix.common.Resource -import com.daniebeler.pfpixelix.domain.model.AuthData -import com.daniebeler.pfpixelix.widget.WidgetRepositoryProvider +import com.daniebeler.pfpixelix.domain.repository.WidgetRepository +import com.daniebeler.pfpixelix.domain.service.session.AuthService +import com.daniebeler.pfpixelix.utils.KmpContext import com.daniebeler.pfpixelix.widget.notifications.models.NotificationStoreItem import com.daniebeler.pfpixelix.widget.notifications.updateNotificationsWidget import com.daniebeler.pfpixelix.widget.notifications.updateNotificationsWidgetRefreshing -import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.flow.firstOrNull import me.tatarka.inject.annotations.Inject class NotificationsTask @Inject constructor( - private val context: Context, + private val context: KmpContext, workerParams: WorkerParameters, - private val dataStore: DataStore + private val authService: AuthService, + private val repository: WidgetRepository, ) : CoroutineWorker(context, workerParams) { override suspend fun doWork(): Result { try { updateNotificationsWidgetRefreshing(context) - val repository = WidgetRepositoryProvider(dataStore).invoke() - if (repository == null) { + authService.openSessionIfExist() + if (authService.activeUser.firstOrNull() == null) { updateNotificationsWidget(emptyList(), context, "you have to be logged in to an account") return Result.failure() } diff --git a/app/src/androidMain/res/values/themes.xml b/app/src/androidMain/res/values/themes.xml index 668caadb..0b40f6d7 100644 --- a/app/src/androidMain/res/values/themes.xml +++ b/app/src/androidMain/res/values/themes.xml @@ -1,5 +1,5 @@ -