diff --git a/.idea/deploymentTargetDropDown.xml b/.idea/deploymentTargetDropDown.xml index c822231..da3e07a 100644 --- a/.idea/deploymentTargetDropDown.xml +++ b/.idea/deploymentTargetDropDown.xml @@ -16,12 +16,12 @@ - + - + diff --git a/.idea/kotlinc.xml b/.idea/kotlinc.xml index fe63bb6..53bf319 100644 --- a/.idea/kotlinc.xml +++ b/.idea/kotlinc.xml @@ -1,6 +1,6 @@ - \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml index 0ad17cb..8978d23 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -1,4 +1,3 @@ - diff --git a/app/build.gradle.kts b/app/build.gradle.kts index b85c705..fc46473 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -1,6 +1,7 @@ plugins { alias(libs.plugins.androidApplication) alias(libs.plugins.jetbrainsKotlinAndroid) + alias(libs.plugins.compose.compiler) alias(libs.plugins.googleHilt) alias(libs.plugins.googleKsp) alias(libs.plugins.googleGms) @@ -16,8 +17,8 @@ android { applicationId = "com.yangdai.opennote" minSdk = 29 targetSdk = 34 - versionCode = 120 - versionName = "1.2.0" + versionCode = 122 + versionName = "1.2.2" resourceConfigurations += listOf("en", "zh") testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" @@ -51,8 +52,10 @@ android { buildFeatures { compose = true } - composeOptions { - kotlinCompilerExtensionVersion = "1.5.12" + composeCompiler { + enableStrongSkippingMode = true + enableIntrinsicRemember = true + enableNonSkippingGroupOptimization = true } packaging { resources { @@ -95,11 +98,10 @@ dependencies { annotationProcessor(libs.androidx.room.compiler) ksp(libs.androidx.room.compiler) implementation(libs.androidx.room.ktx) - implementation(libs.androidx.room.paging) testImplementation(libs.androidx.room.testing) // Hilt, for dependency injection - implementation(libs.androidx.hilt) + implementation(libs.androidx.hilt.navigation) ksp(libs.google.hilt.compiler) implementation(libs.google.hilt) @@ -124,7 +126,6 @@ dependencies { implementation(libs.androidx.core.splashscreen) implementation(libs.androidx.biometric) implementation(libs.androidx.datastore.preferences) - implementation(libs.androidx.core.ktx) implementation(libs.androidx.appcompat) // Test diff --git a/app/release/app-release.apk b/app/release/app-release.apk index 16aea74..9203e94 100644 Binary files a/app/release/app-release.apk and b/app/release/app-release.apk differ diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index a786dd7..d3f451d 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -4,15 +4,11 @@ - + - - @@ -36,15 +32,15 @@ android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:localeConfig="@xml/locales_config" + android:requestLegacyExternalStorage="true" android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" - android:theme="@style/Theme.OpenNote" + android:theme="@style/Theme.OpenNote.Starting" android:usesCleartextTraffic="true" tools:targetApi="tiramisu"> diff --git a/app/src/main/java/com/yangdai/opennote/MainActivity.kt b/app/src/main/java/com/yangdai/opennote/MainActivity.kt index f702f71..076bde4 100644 --- a/app/src/main/java/com/yangdai/opennote/MainActivity.kt +++ b/app/src/main/java/com/yangdai/opennote/MainActivity.kt @@ -43,7 +43,7 @@ class MainActivity : AppCompatActivity() { 0f ) - animation.duration = 200L + animation.duration = 300L // Call SplashScreenView.remove at the end of your custom animation. animation.doOnEnd { splashScreenView.remove() } @@ -54,7 +54,6 @@ class MainActivity : AppCompatActivity() { } enableEdgeToEdge() - super.onCreate(savedInstanceState) setContent { diff --git a/app/src/main/java/com/yangdai/opennote/data/repository/DataStoreRepositoryImpl.kt b/app/src/main/java/com/yangdai/opennote/data/repository/DataStoreRepositoryImpl.kt index 328f647..01b651c 100644 --- a/app/src/main/java/com/yangdai/opennote/data/repository/DataStoreRepositoryImpl.kt +++ b/app/src/main/java/com/yangdai/opennote/data/repository/DataStoreRepositoryImpl.kt @@ -5,6 +5,7 @@ import androidx.datastore.core.DataStore import androidx.datastore.preferences.core.Preferences import androidx.datastore.preferences.core.booleanPreferencesKey import androidx.datastore.preferences.core.edit +import androidx.datastore.preferences.core.floatPreferencesKey import androidx.datastore.preferences.core.intPreferencesKey import androidx.datastore.preferences.core.stringPreferencesKey import androidx.datastore.preferences.core.stringSetPreferencesKey @@ -38,6 +39,13 @@ class DataStoreRepositoryImpl @Inject constructor( } } + override suspend fun putFloat(key: String, value: Float) { + val preferencesKey = floatPreferencesKey(key) + context.dataStore.edit { preferences -> + preferences[preferencesKey] = value + } + } + override suspend fun putBoolean(key: String, value: Boolean) { val preferencesKey = booleanPreferencesKey(key) context.dataStore.edit { preferences -> @@ -67,6 +75,17 @@ class DataStoreRepositoryImpl @Inject constructor( } } + override suspend fun getFloat(key: String): Float? { + return try { + val preferencesKey = floatPreferencesKey(key) + val preferences = context.dataStore.data.first() + preferences[preferencesKey] + } catch (e: Exception) { + e.printStackTrace() + null + } + } + override suspend fun getBoolean(key: String): Boolean? { return try { val preferencesKey = booleanPreferencesKey(key) @@ -103,6 +122,13 @@ class DataStoreRepositoryImpl @Inject constructor( } } + override fun floatFlow(key: String): Flow { + val preferencesKey = floatPreferencesKey(key) + return context.dataStore.data.map { preferences -> + preferences[preferencesKey] ?: 0f + } + } + override fun stringFlow(key: String): Flow { val preferencesKey = stringPreferencesKey(key) return context.dataStore.data.map { preferences -> @@ -123,12 +149,4 @@ class DataStoreRepositoryImpl @Inject constructor( preferences[preferencesKey] ?: setOf() } } - - override fun preferencesFlow(): Flow { - return context.dataStore.data - } - - override fun getDataStore(): DataStore { - return context.dataStore - } } diff --git a/app/src/main/java/com/yangdai/opennote/domain/repository/DataStoreRepository.kt b/app/src/main/java/com/yangdai/opennote/domain/repository/DataStoreRepository.kt index 4c2537c..18ba4cf 100644 --- a/app/src/main/java/com/yangdai/opennote/domain/repository/DataStoreRepository.kt +++ b/app/src/main/java/com/yangdai/opennote/domain/repository/DataStoreRepository.kt @@ -1,24 +1,24 @@ package com.yangdai.opennote.domain.repository -import androidx.datastore.core.DataStore -import androidx.datastore.preferences.core.Preferences import kotlinx.coroutines.flow.Flow interface DataStoreRepository { suspend fun putString(key: String, value: String) suspend fun putInt(key: String, value: Int) + suspend fun putFloat(key: String, value: Float) + suspend fun putBoolean(key: String, value: Boolean) + suspend fun putStringSet(key: String, value: Set) + suspend fun getString(key: String): String? suspend fun getInt(key: String): Int? - suspend fun putBoolean(key: String, value: Boolean) + suspend fun getFloat(key: String): Float? suspend fun getBoolean(key: String): Boolean? - suspend fun putStringSet(key: String, value: Set) suspend fun getStringSet(key: String): Set? fun intFlow(key: String): Flow + fun floatFlow(key: String): Flow fun stringFlow(key: String): Flow fun booleanFlow(key: String): Flow fun stringSetFlow(key: String): Flow> - fun preferencesFlow(): Flow - fun getDataStore(): DataStore } \ No newline at end of file diff --git a/app/src/main/java/com/yangdai/opennote/presentation/component/CurlyCornerShape.kt b/app/src/main/java/com/yangdai/opennote/presentation/component/CurlyCornerShape.kt index ba8f852..b405cea 100644 --- a/app/src/main/java/com/yangdai/opennote/presentation/component/CurlyCornerShape.kt +++ b/app/src/main/java/com/yangdai/opennote/presentation/component/CurlyCornerShape.kt @@ -41,7 +41,7 @@ class CurlyCornerShape( topEnd: Float, bottomEnd: Float, bottomStart: Float, - layoutDirection: LayoutDirection, + layoutDirection: LayoutDirection ): Outline { val d = 2.0 val r2: Double = size.width / d diff --git a/app/src/main/java/com/yangdai/opennote/presentation/component/MarkdownText.kt b/app/src/main/java/com/yangdai/opennote/presentation/component/MarkdownText.kt index 4dc3bee..0c4ce52 100644 --- a/app/src/main/java/com/yangdai/opennote/presentation/component/MarkdownText.kt +++ b/app/src/main/java/com/yangdai/opennote/presentation/component/MarkdownText.kt @@ -73,7 +73,8 @@ fun MarkdownText(html: String) { setPadding(0, 0, 0, 0) setBackgroundColor(Color.TRANSPARENT) } - }, update = { + }, + update = { val data = """ diff --git a/app/src/main/java/com/yangdai/opennote/presentation/component/SelectableColorPlatte.kt b/app/src/main/java/com/yangdai/opennote/presentation/component/SelectableColorPlatte.kt index 2a9dfab..ebbfa63 100644 --- a/app/src/main/java/com/yangdai/opennote/presentation/component/SelectableColorPlatte.kt +++ b/app/src/main/java/com/yangdai/opennote/presentation/component/SelectableColorPlatte.kt @@ -62,7 +62,7 @@ fun SelectableColorPlatte( modifier = Modifier .align(Alignment.Center) .clip(CircleShape) - .background(MaterialTheme.colorScheme.primary), + .background(colorScheme.tertiaryContainer), enter = fadeIn() + expandIn(expandFrom = Alignment.Center), exit = shrinkOut(shrinkTowards = Alignment.Center) + fadeOut() ) { diff --git a/app/src/main/java/com/yangdai/opennote/presentation/component/SettingsDetailPane.kt b/app/src/main/java/com/yangdai/opennote/presentation/component/SettingsDetailPane.kt index 0fff97e..d9c1842 100644 --- a/app/src/main/java/com/yangdai/opennote/presentation/component/SettingsDetailPane.kt +++ b/app/src/main/java/com/yangdai/opennote/presentation/component/SettingsDetailPane.kt @@ -1,11 +1,8 @@ package com.yangdai.opennote.presentation.component -import android.Manifest -import android.app.Activity import android.app.KeyguardManager import android.content.Context import android.content.Intent -import android.content.pm.PackageManager import android.net.Uri import android.os.Build import android.widget.Toast @@ -69,12 +66,11 @@ import androidx.compose.material3.TextButton import androidx.compose.material3.TopAppBarDefaults import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect -import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableFloatStateOf import androidx.compose.runtime.mutableIntStateOf import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember -import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment @@ -91,11 +87,6 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.semantics.Role import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp -import androidx.core.app.ActivityCompat -import androidx.core.content.ContextCompat -import androidx.datastore.preferences.core.booleanPreferencesKey -import androidx.datastore.preferences.core.edit -import androidx.datastore.preferences.core.floatPreferencesKey import androidx.hilt.navigation.compose.hiltViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.google.firebase.analytics.FirebaseAnalytics @@ -110,8 +101,6 @@ import com.yangdai.opennote.presentation.theme.DarkPurpleColors import com.yangdai.opennote.presentation.util.Constants import com.yangdai.opennote.presentation.viewmodel.SharedViewModel import kotlinx.collections.immutable.toImmutableList -import kotlinx.coroutines.flow.map -import kotlinx.coroutines.launch @OptIn(ExperimentalMaterial3Api::class) @Composable @@ -123,7 +112,8 @@ fun SettingsDetailPane( val context = LocalContext.current val haptic = LocalHapticFeedback.current - val scope = rememberCoroutineScope() + val settingsState by sharedViewModel.settingsStateFlow.collectAsStateWithLifecycle() + val customTabsIntent = CustomTabsIntent.Builder() .setShowTitle(true) .build() @@ -165,22 +155,8 @@ fun SettingsDetailPane( mutableStateOf(initFirebaseSelect) } - val isAppInDarkTheme by sharedViewModel.preferencesFlow() - .map { preferences -> - preferences[booleanPreferencesKey(Constants.Preferences.IS_APP_IN_DARK_MODE)] ?: false - } - .collectAsState(initial = false) - fun switchTheme() { - scope.launch { - sharedViewModel - .getDataStore() - .edit { - it[floatPreferencesKey(Constants.Preferences.MASK_CLICK_X)] = 0f - it[floatPreferencesKey(Constants.Preferences.MASK_CLICK_Y)] = 0f - it[booleanPreferencesKey(Constants.Preferences.IS_SWITCH_ACTIVE)] = true - } - } + sharedViewModel.putPreferenceValue(Constants.Preferences.IS_SWITCH_ACTIVE, true) } val isSystemDarkTheme = isSystemInDarkTheme() @@ -329,7 +305,7 @@ fun SettingsDetailPane( if (mode != index) { when (index) { 0 -> { - if (isSystemDarkTheme != isAppInDarkTheme) { + if (isSystemDarkTheme != settingsState.isAppInDarkMode) { switchTheme() } else { sharedViewModel.putPreferenceValue( @@ -344,7 +320,7 @@ fun SettingsDetailPane( } 1 -> { - if (isAppInDarkTheme) { + if (settingsState.isAppInDarkMode) { switchTheme() } sharedViewModel.putPreferenceValue( @@ -358,7 +334,7 @@ fun SettingsDetailPane( } 2 -> { - if (!isAppInDarkTheme) { + if (!settingsState.isAppInDarkMode) { switchTheme() } sharedViewModel.putPreferenceValue( @@ -419,13 +395,7 @@ fun SettingsDetailPane( ListItem( modifier = Modifier.clickable { - if (!hasStoragePermissions(context)) { - ActivityCompat.requestPermissions( - context as Activity, STORAGE_PERMISSIONS, 0 - ) - } else { - showFolderDialog = true - } + showFolderDialog = true }, leadingContent = { Icon( @@ -559,7 +529,7 @@ fun SettingsDetailPane( 0 ) val version = packageInfo.versionName - var pressAMP by remember { mutableStateOf(16f) } + var pressAMP by remember { mutableFloatStateOf(16f) } val animatedPress by animateFloatAsState( targetValue = pressAMP, animationSpec = tween(), label = "" @@ -567,18 +537,7 @@ fun SettingsDetailPane( Column( modifier = Modifier .fillMaxWidth() - .padding(vertical = 16.dp) - .pointerInput(Unit) { - detectTapGestures( - onPress = { - haptic.performHapticFeedback(HapticFeedbackType.LongPress) - pressAMP = 0f - tryAwaitRelease() - haptic.performHapticFeedback(HapticFeedbackType.LongPress) - pressAMP = 16f - } - ) - }, + .padding(vertical = 16.dp), verticalArrangement = Arrangement.Center, horizontalAlignment = Alignment.CenterHorizontally ) { @@ -594,7 +553,18 @@ fun SettingsDetailPane( shape = CurlyCornerShape(amp = animatedPress.toDouble()), ambientColor = MaterialTheme.colorScheme.primaryContainer, spotColor = MaterialTheme.colorScheme.primaryContainer, - ), + ) + .pointerInput(Unit) { + detectTapGestures( + onPress = { + haptic.performHapticFeedback(HapticFeedbackType.LongPress) + pressAMP = 0f + tryAwaitRelease() + haptic.performHapticFeedback(HapticFeedbackType.LongPress) + pressAMP = 16f + } + ) + }, contentAlignment = Alignment.Center, ) { Image( @@ -783,21 +753,6 @@ fun SettingsDetailPane( } } -fun hasStoragePermissions( - context: Context -): Boolean { - if (Build.VERSION.SDK_INT > Build.VERSION_CODES.S) { - return true - } - return STORAGE_PERMISSIONS.all { - ContextCompat.checkSelfPermission(context, it) == PackageManager.PERMISSION_GRANTED - } -} - -val STORAGE_PERMISSIONS = arrayOf( - Manifest.permission.READ_EXTERNAL_STORAGE -) - @Composable fun SettingsDetailPlaceHolder() = Column(horizontalAlignment = Alignment.CenterHorizontally) { diff --git a/app/src/main/java/com/yangdai/opennote/presentation/navigation/AnimatedNavHost.kt b/app/src/main/java/com/yangdai/opennote/presentation/navigation/AnimatedNavHost.kt index 9730bac..23a3f27 100644 --- a/app/src/main/java/com/yangdai/opennote/presentation/navigation/AnimatedNavHost.kt +++ b/app/src/main/java/com/yangdai/opennote/presentation/navigation/AnimatedNavHost.kt @@ -22,7 +22,7 @@ import com.yangdai.opennote.presentation.screen.SettingsScreen import com.yangdai.opennote.presentation.util.Constants.NAV_ANIMATION_TIME import com.yangdai.opennote.presentation.util.parseSharedContent -private fun sharedAxisXIn( +fun sharedAxisXIn( initialOffsetX: (fullWidth: Int) -> Int, durationMillis: Int = NAV_ANIMATION_TIME, ): EnterTransition = slideInHorizontally( @@ -33,7 +33,7 @@ private fun sharedAxisXIn( initialOffsetX = initialOffsetX ) -private fun sharedAxisXOut( +fun sharedAxisXOut( targetOffsetX: (fullWidth: Int) -> Int, durationMillis: Int = NAV_ANIMATION_TIME, ): ExitTransition = slideOutHorizontally( diff --git a/app/src/main/java/com/yangdai/opennote/presentation/screen/BaseScreen.kt b/app/src/main/java/com/yangdai/opennote/presentation/screen/BaseScreen.kt index 3f3e6a7..571ba30 100644 --- a/app/src/main/java/com/yangdai/opennote/presentation/screen/BaseScreen.kt +++ b/app/src/main/java/com/yangdai/opennote/presentation/screen/BaseScreen.kt @@ -8,7 +8,6 @@ import androidx.compose.foundation.isSystemInDarkTheme import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect -import androidx.compose.runtime.State import androidx.compose.runtime.derivedStateOf import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf @@ -19,9 +18,6 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.draw.blur import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.unit.dp -import androidx.datastore.preferences.core.Preferences -import androidx.datastore.preferences.core.booleanPreferencesKey -import androidx.datastore.preferences.core.floatPreferencesKey import androidx.hilt.navigation.compose.hiltViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.navigation.compose.rememberNavController @@ -32,7 +28,6 @@ import com.yangdai.opennote.presentation.component.MaskBox import com.yangdai.opennote.presentation.navigation.AnimatedNavHost import com.yangdai.opennote.presentation.theme.OpenNoteTheme import com.yangdai.opennote.presentation.util.Constants -import kotlinx.coroutines.flow.map @Composable fun BaseScreen( @@ -54,32 +49,6 @@ fun BaseScreen( } } - val isAppInDarkTheme by getPreferenceState( - sharedViewModel, - booleanPreferencesKey(Constants.Preferences.IS_APP_IN_DARK_MODE), - false - ) - val shouldFollowSystem by getPreferenceState( - sharedViewModel, - booleanPreferencesKey(Constants.Preferences.SHOULD_FOLLOW_SYSTEM), - false - ) - val switchActive by getPreferenceState( - sharedViewModel, - booleanPreferencesKey(Constants.Preferences.IS_SWITCH_ACTIVE), - false - ) - val maskClickX by getPreferenceState( - sharedViewModel, - floatPreferencesKey(Constants.Preferences.MASK_CLICK_X), - 0f - ) - val maskClickY by getPreferenceState( - sharedViewModel, - floatPreferencesKey(Constants.Preferences.MASK_CLICK_Y), - 0f - ) - val isSystemInDarkTheme = isSystemInDarkTheme() LaunchedEffect(settingsState.theme) { @@ -90,29 +59,29 @@ fun BaseScreen( OpenNoteTheme( color = settingsState.color, - darkMode = isAppInDarkTheme + darkMode = settingsState.isAppInDarkMode ) { // MaskBox is a custom composable that animates a mask over the screen MaskBox( maskComplete = { - sharedViewModel.putPreferenceValue(Constants.Preferences.IS_APP_IN_DARK_MODE, !isAppInDarkTheme) + sharedViewModel.putPreferenceValue(Constants.Preferences.IS_APP_IN_DARK_MODE, !settingsState.isAppInDarkMode) }, animFinish = { sharedViewModel.putPreferenceValue(Constants.Preferences.IS_SWITCH_ACTIVE, false) - if (shouldFollowSystem) { + if (settingsState.shouldFollowSystem) { sharedViewModel.putPreferenceValue(Constants.Preferences.APP_THEME, 0) } } ) { maskActiveEvent -> - LaunchedEffect(switchActive) { - if (!switchActive) return@LaunchedEffect + LaunchedEffect(settingsState.isSwitchActive) { + if (!settingsState.isSwitchActive) return@LaunchedEffect - if (isAppInDarkTheme) - maskActiveEvent(MaskAnimModel.SHRINK, maskClickX, maskClickY) + if (settingsState.isAppInDarkMode) + maskActiveEvent(MaskAnimModel.SHRINK, Constants.Preferences.MASK_CLICK_X, Constants.Preferences.MASK_CLICK_Y) else - maskActiveEvent(MaskAnimModel.EXPEND, maskClickX, maskClickY) + maskActiveEvent(MaskAnimModel.EXPEND, Constants.Preferences.MASK_CLICK_X, Constants.Preferences.MASK_CLICK_Y) } val blur by animateDpAsState(targetValue = if (!loggedIn) 16.dp else 0.dp, label = "") @@ -138,14 +107,3 @@ fun BaseScreen( } } } - -@Composable -private fun getPreferenceState( - viewModel: SharedViewModel, - key: Preferences.Key, - defaultValue: T -): State { - return viewModel.preferencesFlow() - .map { preferences -> preferences[key] ?: defaultValue } - .collectAsStateWithLifecycle(initialValue = defaultValue) -} diff --git a/app/src/main/java/com/yangdai/opennote/presentation/state/SettingsState.kt b/app/src/main/java/com/yangdai/opennote/presentation/state/SettingsState.kt index a1831ca..961af8c 100644 --- a/app/src/main/java/com/yangdai/opennote/presentation/state/SettingsState.kt +++ b/app/src/main/java/com/yangdai/opennote/presentation/state/SettingsState.kt @@ -3,5 +3,8 @@ package com.yangdai.opennote.presentation.state data class SettingsState( val theme: Int = -1, val color: Int = 0, - val needPassword: Boolean = false + val needPassword: Boolean = false, + val isAppInDarkMode: Boolean = false, + val shouldFollowSystem: Boolean = false, + val isSwitchActive: Boolean = false ) diff --git a/app/src/main/java/com/yangdai/opennote/presentation/theme/Theme.kt b/app/src/main/java/com/yangdai/opennote/presentation/theme/Theme.kt index e466fdd..41e2f34 100644 --- a/app/src/main/java/com/yangdai/opennote/presentation/theme/Theme.kt +++ b/app/src/main/java/com/yangdai/opennote/presentation/theme/Theme.kt @@ -8,8 +8,6 @@ import androidx.compose.material3.dynamicDarkColorScheme import androidx.compose.material3.dynamicLightColorScheme import androidx.compose.runtime.Composable import androidx.compose.runtime.SideEffect -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.graphics.toArgb import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalView import androidx.core.view.WindowCompat @@ -36,20 +34,10 @@ fun OpenNoteTheme( } } -// val colorScheme = when { -// dynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> { -// val context = LocalContext.current -// if (darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context) -// } -// -// darkTheme -> DarkPurpleColors -// else -> LightPurpleColors -// } val view = LocalView.current if (!view.isInEditMode) { SideEffect { val window = (view.context as Activity).window - window.statusBarColor = Color.Transparent.toArgb() WindowCompat.getInsetsController(window, view).isAppearanceLightStatusBars = !darkMode } } diff --git a/app/src/main/java/com/yangdai/opennote/presentation/util/Constants.kt b/app/src/main/java/com/yangdai/opennote/presentation/util/Constants.kt index d2a740e..9afe769 100644 --- a/app/src/main/java/com/yangdai/opennote/presentation/util/Constants.kt +++ b/app/src/main/java/com/yangdai/opennote/presentation/util/Constants.kt @@ -7,7 +7,6 @@ object Constants { object File { const val OPENNOTE = "OpenNote" - const val BACKUP = "Backup" const val OPENNOTE_BACKUP = "OpenNote/Backup" } @@ -20,8 +19,8 @@ object Constants { const val IS_APP_IN_DARK_MODE = "IS_APP_IN_DARK_MODE" const val SHOULD_FOLLOW_SYSTEM = "SHOULD_FOLLOW_SYSTEM" const val IS_SWITCH_ACTIVE = "IS_DARK_SWITCH_ACTIVE" - const val MASK_CLICK_X = "MASK_CLICK_X" - const val MASK_CLICK_Y = "MASK_CLICK_Y" + const val MASK_CLICK_X = 0f + const val MASK_CLICK_Y = 0f } object Editor { diff --git a/app/src/main/java/com/yangdai/opennote/presentation/util/Utils.kt b/app/src/main/java/com/yangdai/opennote/presentation/util/Utils.kt index d3db485..760e2b5 100644 --- a/app/src/main/java/com/yangdai/opennote/presentation/util/Utils.kt +++ b/app/src/main/java/com/yangdai/opennote/presentation/util/Utils.kt @@ -2,8 +2,6 @@ package com.yangdai.opennote.presentation.util import android.content.Intent import android.icu.text.DateFormat -import android.os.Environment -import java.io.File import java.util.Date fun Intent.isTextMimeType() = type?.startsWith(Constants.MIME_TYPE_TEXT) == true @@ -22,25 +20,3 @@ fun Long.timestampToFormatLocalDateTime(): String { return DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT) .format(Date(this)) } - -fun createAppDirectory(): String { - val directory = File( - Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS), - Constants.File.OPENNOTE - ) - if (!directory.exists()) { - directory.mkdirs() - } - return directory.absolutePath -} - -fun createBackupDirectory(): String { - val directory = File( - Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS), - Constants.File.OPENNOTE_BACKUP - ) - if (!directory.exists()) { - directory.mkdirs() - } - return directory.absolutePath -} \ No newline at end of file diff --git a/app/src/main/java/com/yangdai/opennote/presentation/viewmodel/SharedViewModel.kt b/app/src/main/java/com/yangdai/opennote/presentation/viewmodel/SharedViewModel.kt index c3e7f90..6dd7f80 100644 --- a/app/src/main/java/com/yangdai/opennote/presentation/viewmodel/SharedViewModel.kt +++ b/app/src/main/java/com/yangdai/opennote/presentation/viewmodel/SharedViewModel.kt @@ -12,7 +12,6 @@ import androidx.compose.foundation.text.input.TextFieldState import androidx.compose.foundation.text.input.clearText import androidx.compose.foundation.text.input.setTextAndPlaceCursorAtEnd import androidx.compose.runtime.snapshotFlow -import androidx.datastore.preferences.core.Preferences import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import com.google.gson.Gson @@ -41,9 +40,7 @@ import com.yangdai.opennote.presentation.util.add import com.yangdai.opennote.presentation.util.addLink import com.yangdai.opennote.presentation.util.addTask import com.yangdai.opennote.presentation.util.bold -import com.yangdai.opennote.presentation.util.createBackupDirectory import com.yangdai.opennote.presentation.util.diagram -import com.yangdai.opennote.presentation.util.createAppDirectory import com.yangdai.opennote.presentation.util.inlineCode import com.yangdai.opennote.presentation.util.inlineFunction import com.yangdai.opennote.presentation.util.italic @@ -57,7 +54,6 @@ import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.FlowPreview import kotlinx.coroutines.Job import kotlinx.coroutines.delay -import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.SharingStarted @@ -158,7 +154,7 @@ class SharedViewModel @Inject constructor( } else { TextRecognition.getClient(TextRecognizerOptions.DEFAULT_OPTIONS) } - delay(500) + delay(300) //任务完成后将 isLoading 设置为 false 以隐藏启动屏幕 _isLoadingData.value = false } @@ -170,11 +166,17 @@ class SharedViewModel @Inject constructor( dataStoreRepository.intFlow(Constants.Preferences.APP_THEME), dataStoreRepository.intFlow(Constants.Preferences.APP_COLOR), dataStoreRepository.booleanFlow(Constants.Preferences.NEED_PASSWORD), - ) { theme, color, needPassword -> + dataStoreRepository.booleanFlow(Constants.Preferences.IS_APP_IN_DARK_MODE), + dataStoreRepository.booleanFlow(Constants.Preferences.SHOULD_FOLLOW_SYSTEM), + dataStoreRepository.booleanFlow(Constants.Preferences.IS_SWITCH_ACTIVE) + ) { values -> SettingsState( - theme = theme, - color = color, - needPassword = needPassword + theme = values[0] as Int, + color = values[1] as Int, + needPassword = values[2] as Boolean, + isAppInDarkMode = values[3] as Boolean, + shouldFollowSystem = values[4] as Boolean, + isSwitchActive = values[5] as Boolean ) }.flowOn(Dispatchers.IO).stateIn( scope = viewModelScope, @@ -182,10 +184,6 @@ class SharedViewModel @Inject constructor( initialValue = SettingsState() ) - fun preferencesFlow(): Flow { - return dataStoreRepository.preferencesFlow() - } - fun putPreferenceValue(key: String, value: T) { viewModelScope.launch(Dispatchers.IO) { when (value) { @@ -210,9 +208,6 @@ class SharedViewModel @Inject constructor( dataStoreRepository.getBoolean(key) } - fun getDataStore() = dataStoreRepository.getDataStore() - - val historyStateFlow: StateFlow> = dataStoreRepository.stringSetFlow(Constants.Preferences.SEARCH_HISTORY) .stateIn( @@ -499,9 +494,9 @@ class SharedViewModel @Inject constructor( fun getFileName(contentResolver: ContentResolver, uri: Uri): String? { var result: String? = null if (uri.scheme == "content") { - val cursor = contentResolver.query(uri, null, null, null, null) - cursor.use { - if (it != null && it.moveToFirst()) { + val cursor = contentResolver.query(uri, null, null, null, null, null) + cursor?.use { + if (it.moveToFirst()) { result = it.getString(it.getColumnIndex(OpenableColumns.DISPLAY_NAME)) } } @@ -573,8 +568,6 @@ class SharedViewModel @Inject constructor( else -> ".html" } - createAppDirectory() - dataActionJob = viewModelScope.launch(Dispatchers.IO) { notes.forEachIndexed { index, noteEntity -> _dataActionState.update { @@ -627,7 +620,6 @@ class SharedViewModel @Inject constructor( dataActionJob?.cancel() _dataActionState.update { it.copy(loading = true) } - createBackupDirectory() _dataActionState.update { it.copy(progress = 0.2f) } diff --git a/app/src/main/res/values-zh/strings.xml b/app/src/main/res/values-zh/strings.xml index 3be6915..a4c5dc7 100644 --- a/app/src/main/res/values-zh/strings.xml +++ b/app/src/main/res/values-zh/strings.xml @@ -30,7 +30,6 @@ 登录 数据和安全 重置数据库 - 请重启应用 提醒 全部恢复 全部删除 diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index d550724..a8312db 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -30,7 +30,6 @@ Login Data and security Reset database - Please relaunch the app Welcome to \"Open Note\", a lightweight, aesthetically pleasing application that adheres to the Material 3 design style. Our app offers local storage, allowing you to jot down your thoughts anytime, anywhere, even without an internet connection. The efficient search function helps you quickly locate your notes, while the folder categorization feature aids in better organization of your notes. Additionally, it provides a text OCR feature, capable of converting text in images into editable text. The goal is to provide you with the most convenient and efficient note-taking experience. Download: https://play.google.com/store/apps/details?id=com.yangdai.opennote Remind All restore diff --git a/app/src/main/res/values/themes.xml b/app/src/main/res/values/themes.xml index 5605d7c..2d1351b 100644 --- a/app/src/main/res/values/themes.xml +++ b/app/src/main/res/values/themes.xml @@ -2,9 +2,7 @@ diff --git a/build.gradle.kts b/build.gradle.kts index 5ec66aa..d95a47e 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -2,6 +2,7 @@ plugins { alias(libs.plugins.androidApplication) apply false alias(libs.plugins.jetbrainsKotlinAndroid) apply false + alias(libs.plugins.compose.compiler) apply false alias(libs.plugins.googleHilt) apply false alias(libs.plugins.googleKsp) apply false alias(libs.plugins.googleGms) apply false diff --git a/gradle.properties b/gradle.properties index 20e2a01..126cd0f 100644 --- a/gradle.properties +++ b/gradle.properties @@ -20,4 +20,5 @@ kotlin.code.style=official # Enables namespacing of each library's R class so that its R class includes only the # resources declared in the library itself and none from the library's dependencies, # thereby reducing the size of the R class for that library -android.nonTransitiveRClass=true \ No newline at end of file +android.nonTransitiveRClass=true +kotlin.native.disableCompilerDaemon = true \ No newline at end of file diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 4323681..b7446f5 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,34 +1,33 @@ [versions] -agp = "8.3.2" +agp = "8.4.0" browser = "1.8.0" cameraCore = "1.3.3" commonmark = "0.22.0" -coreSplashscreen = "1.0.1" -datastorePreferences = "1.1.0" +datastorePreferences = "1.1.1" firebaseBom = "32.8.1" -kotlin = "1.9.23" -coreKtx = "1.13.0" +kotlin = "2.0.0-RC2" junit = "4.13.2" junitVersion = "1.1.5" espressoCore = "3.5.1" kotlinxCollectionsImmutable = "0.3.7" activityCompose = "1.9.0" -composeBom = "2024.04.01" +composeBom = "2024.05.00" appcompat = "1.6.1" hilt = "2.51.1" hiltNav = "1.2.0" room = "2.6.1" -ksp = "1.9.23-1.0.20" lifecycleRuntimeCompose = "2.7.0" gms = "4.4.1" textRecognition = "16.0.0" firebase-crashlytics = "2.9.9" gson = "2.10.1" +coreSplashscreen = "1.2.0-alpha01" +ksp = "2.0.0-RC2-1.0.20" biometricVersion = "1.2.0-alpha05" -navVersion = "2.8.0-alpha07" -foundation = "1.7.0-alpha07" -m3 = "1.3.0-alpha05" -adaptiveVersion = "1.0.0-alpha11" +navVersion = "2.8.0-alpha08" +foundation = "1.7.0-alpha08" +m3 = "1.3.0-alpha06" +adaptiveVersion = "1.0.0-alpha12" [libraries] @@ -38,7 +37,6 @@ androidx-camera-lifecycle = { group = "androidx.camera", name = "camera-lifecycl androidx-camera-view = { group = "androidx.camera", name = "camera-view", version.ref = "cameraCore" } androidx-appcompat = { group = "androidx.appcompat", name = "appcompat", version.ref = "appcompat" } -androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" } androidx-core-splashscreen = { group = "androidx.core", name = "core-splashscreen", version.ref = "coreSplashscreen" } androidx-datastore-preferences = { group = "androidx.datastore", name = "datastore-preferences", version.ref = "datastorePreferences" } androidx-biometric = { group = "androidx.biometric", name = "biometric-ktx", version.ref = "biometricVersion" } @@ -78,12 +76,11 @@ androidx-navigation-compose = { group = "androidx.navigation", name = "navigatio google-hilt = { group = "com.google.dagger", name = "hilt-android", version.ref = "hilt" } google-hilt-compiler = { group = "com.google.dagger", name = "hilt-android-compiler", version.ref = "hilt" } -androidx-hilt = { group = "androidx.hilt", name = "hilt-navigation-compose", version.ref = "hiltNav" } +androidx-hilt-navigation = { group = "androidx.hilt", name = "hilt-navigation-compose", version.ref = "hiltNav" } androidx-room-runtime = { group = "androidx.room", name = "room-runtime", version.ref = "room" } androidx-room-compiler = { group = "androidx.room", name = "room-compiler", version.ref = "room" } androidx-room-ktx = { group = "androidx.room", name = "room-ktx", version.ref = "room" } -androidx-room-paging = { group = "androidx.room", name = "room-paging", version.ref = "room" } androidx-room-testing = { group = "androidx.room", name = "room-testing", version.ref = "room" } google-gson = { group = "com.google.code.gson", name = "gson", version.ref = "gson" } @@ -94,6 +91,7 @@ text-recognition-chinese = { group = "com.google.mlkit", name = "text-recognitio [plugins] androidApplication = { id = "com.android.application", version.ref = "agp" } jetbrainsKotlinAndroid = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" } +compose-compiler = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" } googleHilt = { id = "com.google.dagger.hilt.android", version.ref = "hilt" } googleKsp = { id = "com.google.devtools.ksp", version.ref = "ksp" } googleGms = { id = "com.google.gms.google-services", version.ref = "gms" }