diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 27d8a144..8edf38f7 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -82,6 +82,9 @@ dependencies { implementation(libs.compose.activity) implementation(libs.compose.lifecycle.viewmodel) implementation(libs.compose.livedata) + implementation(libs.compose.navigation) + + implementation(libs.accompanist.adaptive) implementation(libs.core.ktx) implementation(libs.appcompat) diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 81f71e3e..c0d5eb12 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -5,22 +5,34 @@ - - + + - + - - - + + - - - + + + tools:ignore="UnusedAttribute" + tools:replace="android:theme"> @@ -51,15 +63,18 @@ - - - @@ -75,8 +90,8 @@ + android:exported="true" + android:parentActivityName=".ui.MainComposeActivity"> @@ -85,8 +100,8 @@ - - + + @@ -96,8 +111,8 @@ - - + + @@ -177,13 +192,13 @@ - + android:label="@string/settings_title" /> + android:exported="true" + android:theme="@android:style/Theme.Material.Light.NoActionBar" + android:windowSoftInputMode="adjustResize"> @@ -191,7 +206,6 @@ - diff --git a/app/src/main/java/xyz/quaver/pupil/Pupil.kt b/app/src/main/java/xyz/quaver/pupil/Pupil.kt index d91450e9..0c176dd8 100644 --- a/app/src/main/java/xyz/quaver/pupil/Pupil.kt +++ b/app/src/main/java/xyz/quaver/pupil/Pupil.kt @@ -26,7 +26,6 @@ import android.content.Context import android.content.Intent import android.net.Uri import android.os.Build -import android.util.Log import androidx.appcompat.app.AppCompatDelegate import androidx.core.content.ContextCompat import androidx.preference.PreferenceManager @@ -38,22 +37,22 @@ import com.google.android.gms.common.GooglePlayServicesRepairableException import com.google.android.gms.security.ProviderInstaller import com.google.firebase.FirebaseApp import com.google.firebase.crashlytics.FirebaseCrashlytics -import kotlinx.coroutines.* import okhttp3.Dispatcher import okhttp3.Interceptor import okhttp3.OkHttpClient import okhttp3.Response import xyz.quaver.io.FileX -import xyz.quaver.pupil.hitomi.evaluationContext -import xyz.quaver.pupil.hitomi.readText import xyz.quaver.pupil.types.Tag -import xyz.quaver.pupil.util.* +import xyz.quaver.pupil.util.Preferences +import xyz.quaver.pupil.util.SavedSet +import xyz.quaver.pupil.util.getProxyInfo +import xyz.quaver.pupil.util.preferences +import xyz.quaver.pupil.util.proxyInfo import java.io.File -import java.net.URL import java.security.KeyStore import java.security.SecureRandom import java.security.cert.CertificateFactory -import java.util.* +import java.util.UUID import java.util.concurrent.Executors import java.util.concurrent.TimeUnit import javax.net.ssl.SSLContext @@ -94,16 +93,19 @@ fun getSSLContext(context: Context): SSLContext { keyStore.setCertificateEntry("isrgrootx1", certificate) - val defaultTrustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()) + val defaultTrustManagerFactory = + TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()) defaultTrustManagerFactory.init(null as KeyStore?) - defaultTrustManagerFactory.trustManagers.filterIsInstance(X509TrustManager::class.java).forEach { trustManager -> - trustManager.acceptedIssuers.forEach { acceptedIssuer -> - keyStore.setCertificateEntry(acceptedIssuer.subjectDN.name, acceptedIssuer) + defaultTrustManagerFactory.trustManagers.filterIsInstance() + .forEach { trustManager -> + trustManager.acceptedIssuers.forEach { acceptedIssuer -> + keyStore.setCertificateEntry(acceptedIssuer.subjectDN.name, acceptedIssuer) + } } - } - val trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()) + val trustManagerFactory = + TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()) trustManagerFactory.init(keyStore) val sslContext = SSLContext.getInstance("TLS") @@ -125,7 +127,7 @@ class Pupil : Application() { preferences = PreferenceManager.getDefaultSharedPreferences(this) - val userID = Preferences["user_id", ""].let { userID -> + val userID = Preferences["user_id", ""].let { userID -> if (userID.isEmpty()) UUID.randomUUID().toString().also { Preferences["user_id"] = it } else userID } @@ -142,7 +144,10 @@ class Pupil : Application() { .proxyInfo(proxyInfo) .addInterceptor { chain -> val request = chain.request().newBuilder() - .addHeader("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/94.0.4606.81 Safari/537.36") + .addHeader( + "User-Agent", + "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/94.0.4606.81 Safari/537.36" + ) .header("Referer", "https://hitomi.la/") .build() @@ -178,7 +183,8 @@ class Pupil : Application() { histories = SavedSet(File(ContextCompat.getDataDir(this), "histories.json"), 0) favorites = SavedSet(File(ContextCompat.getDataDir(this), "favorites.json"), 0) - favoriteTags = SavedSet(File(ContextCompat.getDataDir(this), "favorites_tags.json"), Tag.parse("")) + favoriteTags = + SavedSet(File(ContextCompat.getDataDir(this), "favorites_tags.json"), Tag.parse("")) searchHistory = SavedSet(File(ContextCompat.getDataDir(this), "search_histories.json"), "") favoriteTags.filter { it.tag.contains('_') }.forEach { @@ -209,46 +215,73 @@ class Pupil : Application() { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { val manager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager - manager.createNotificationChannel(NotificationChannel("download", getString(R.string.channel_download), NotificationManager.IMPORTANCE_LOW).apply { - description = getString(R.string.channel_download_description) - enableLights(false) - enableVibration(false) - lockscreenVisibility = Notification.VISIBILITY_SECRET - }) + manager.createNotificationChannel( + NotificationChannel( + "download", + getString(R.string.channel_download), + NotificationManager.IMPORTANCE_LOW + ).apply { + description = getString(R.string.channel_download_description) + enableLights(false) + enableVibration(false) + lockscreenVisibility = Notification.VISIBILITY_SECRET + }) - manager.createNotificationChannel(NotificationChannel("downloader", getString(R.string.channel_downloader), NotificationManager.IMPORTANCE_LOW).apply { - description = getString(R.string.channel_downloader_description) - enableLights(false) - enableVibration(false) - lockscreenVisibility = Notification.VISIBILITY_SECRET - }) + manager.createNotificationChannel( + NotificationChannel( + "downloader", + getString(R.string.channel_downloader), + NotificationManager.IMPORTANCE_LOW + ).apply { + description = getString(R.string.channel_downloader_description) + enableLights(false) + enableVibration(false) + lockscreenVisibility = Notification.VISIBILITY_SECRET + }) - manager.createNotificationChannel(NotificationChannel("update", getString(R.string.channel_update), NotificationManager.IMPORTANCE_HIGH).apply { - description = getString(R.string.channel_update_description) - enableLights(true) - enableVibration(true) - lockscreenVisibility = Notification.VISIBILITY_SECRET - }) + manager.createNotificationChannel( + NotificationChannel( + "update", + getString(R.string.channel_update), + NotificationManager.IMPORTANCE_HIGH + ).apply { + description = getString(R.string.channel_update_description) + enableLights(true) + enableVibration(true) + lockscreenVisibility = Notification.VISIBILITY_SECRET + }) - manager.createNotificationChannel(NotificationChannel("import", getString(R.string.channel_update), NotificationManager.IMPORTANCE_LOW).apply { - description = getString(R.string.channel_update_description) - enableLights(false) - enableVibration(false) - lockscreenVisibility = Notification.VISIBILITY_SECRET - }) + manager.createNotificationChannel( + NotificationChannel( + "import", + getString(R.string.channel_update), + NotificationManager.IMPORTANCE_LOW + ).apply { + description = getString(R.string.channel_update_description) + enableLights(false) + enableVibration(false) + lockscreenVisibility = Notification.VISIBILITY_SECRET + }) - manager.createNotificationChannel(NotificationChannel("transfer", getString(R.string.channel_transfer), NotificationManager.IMPORTANCE_LOW).apply { - description = getString(R.string.channel_transfer_description) - enableLights(false) - enableVibration(false) - lockscreenVisibility = Notification.VISIBILITY_SECRET - }) + manager.createNotificationChannel( + NotificationChannel( + "transfer", + getString(R.string.channel_transfer), + NotificationManager.IMPORTANCE_LOW + ).apply { + description = getString(R.string.channel_transfer_description) + enableLights(false) + enableVibration(false) + lockscreenVisibility = Notification.VISIBILITY_SECRET + }) } - AppCompatDelegate.setDefaultNightMode(when (Preferences.get("dark_mode")) { - true -> AppCompatDelegate.MODE_NIGHT_YES - false -> AppCompatDelegate.MODE_NIGHT_NO - }) + AppCompatDelegate.setDefaultNightMode( + when (Preferences.get("dark_mode")) { + true -> AppCompatDelegate.MODE_NIGHT_YES + false -> AppCompatDelegate.MODE_NIGHT_NO + } + ) super.onCreate() } diff --git a/app/src/main/java/xyz/quaver/pupil/ui/BaseActivity.kt b/app/src/main/java/xyz/quaver/pupil/ui/BaseActivity.kt index 120a9986..959f8ae9 100644 --- a/app/src/main/java/xyz/quaver/pupil/ui/BaseActivity.kt +++ b/app/src/main/java/xyz/quaver/pupil/ui/BaseActivity.kt @@ -23,9 +23,11 @@ import android.os.Bundle import android.os.PersistableBundle import android.view.WindowManager import androidx.activity.ComponentActivity +import androidx.activity.enableEdgeToEdge import androidx.activity.result.contract.ActivityResultContracts import androidx.annotation.CallSuper import androidx.appcompat.app.AppCompatActivity +import androidx.core.view.WindowCompat import xyz.quaver.pupil.util.LockManager import xyz.quaver.pupil.util.Preferences @@ -41,8 +43,11 @@ open class BaseComponentActivity : ComponentActivity() { } @CallSuper - override fun onCreate(savedInstanceState: Bundle?, persistentState: PersistableBundle?) { - super.onCreate(savedInstanceState, persistentState) + override fun onCreate(savedInstanceState: Bundle?) { + enableEdgeToEdge() + super.onCreate(savedInstanceState) + + WindowCompat.setDecorFitsSystemWindows(window, false) locked = !LockManager(this).locks.isNullOrEmpty() } diff --git a/app/src/main/java/xyz/quaver/pupil/ui/MainComposeActivity.kt b/app/src/main/java/xyz/quaver/pupil/ui/MainComposeActivity.kt new file mode 100644 index 00000000..a5c6c6c3 --- /dev/null +++ b/app/src/main/java/xyz/quaver/pupil/ui/MainComposeActivity.kt @@ -0,0 +1,44 @@ +package xyz.quaver.pupil.ui + +import android.os.Bundle +import androidx.activity.compose.setContent +import androidx.activity.result.contract.ActivityResultContracts +import androidx.activity.viewModels +import androidx.compose.runtime.getValue +import androidx.lifecycle.compose.collectAsStateWithLifecycle +import com.google.accompanist.adaptive.calculateDisplayFeatures +import xyz.quaver.pupil.ui.compose.MainApp +import xyz.quaver.pupil.ui.theme.AppTheme +import xyz.quaver.pupil.ui.viewmodel.MainViewModel +import xyz.quaver.pupil.util.requestNotificationPermission +import xyz.quaver.pupil.util.showNotificationPermissionExplanationDialog + +class MainComposeActivity : BaseComponentActivity() { + private val requestNotificationPermissionLauncher = + registerForActivityResult(ActivityResultContracts.RequestPermission()) { isGranted -> + if (!isGranted) { + showNotificationPermissionExplanationDialog(this) + } + } + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + requestNotificationPermission(this, requestNotificationPermissionLauncher, false) + + val viewModel: MainViewModel by viewModels() + + setContent { + val displayFeatures = calculateDisplayFeatures(this) + + val uiState by viewModel.searchState.collectAsStateWithLifecycle() + + AppTheme { + MainApp( + uiState = uiState, + displayFeatures = displayFeatures + ) + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/xyz/quaver/pupil/ui/compose/MainApp.kt b/app/src/main/java/xyz/quaver/pupil/ui/compose/MainApp.kt new file mode 100644 index 00000000..8e7df129 --- /dev/null +++ b/app/src/main/java/xyz/quaver/pupil/ui/compose/MainApp.kt @@ -0,0 +1,20 @@ +package xyz.quaver.pupil.ui.compose + +import androidx.compose.material3.Text +import androidx.compose.material3.adaptive.currentWindowAdaptiveInfo +import androidx.compose.runtime.Composable +import androidx.navigation.NavController +import androidx.navigation.compose.rememberNavController +import androidx.window.core.layout.WindowSizeClass +import androidx.window.layout.DisplayFeature +import xyz.quaver.pupil.ui.viewmodel.SearchState + +@Composable +fun MainApp( + uiState: SearchState, + displayFeatures: List, + navController: NavController = rememberNavController(), + windowSizeClass: WindowSizeClass = currentWindowAdaptiveInfo().windowSizeClass, +) { + Text("Hello, World!") +} \ No newline at end of file diff --git a/app/src/main/java/xyz/quaver/pupil/ui/theme/Color.kt b/app/src/main/java/xyz/quaver/pupil/ui/theme/Color.kt new file mode 100644 index 00000000..319fd523 --- /dev/null +++ b/app/src/main/java/xyz/quaver/pupil/ui/theme/Color.kt @@ -0,0 +1,149 @@ +package xyz.quaver.pupil.ui.theme + +import androidx.compose.ui.graphics.Color + +val md_theme_light_primary = Color(0xFF006688) +val md_theme_light_onPrimary = Color(0xFFFFFFFF) +val md_theme_light_primaryContainer = Color(0xFFC2E8FF) +val md_theme_light_onPrimaryContainer = Color(0xFF001E2B) +val md_theme_light_secondary = Color(0xFF4E616D) +val md_theme_light_onSecondary = Color(0xFFFFFFFF) +val md_theme_light_secondaryContainer = Color(0xFFD1E5F3) +val md_theme_light_onSecondaryContainer = Color(0xFF091E28) +val md_theme_light_tertiary = Color(0xFF5F5A7D) +val md_theme_light_onTertiary = Color(0xFFFFFFFF) +val md_theme_light_tertiaryContainer = Color(0xFFE5DEFF) +val md_theme_light_onTertiaryContainer = Color(0xFF1C1736) +val md_theme_light_error = Color(0xFFBA1A1A) +val md_theme_light_errorContainer = Color(0xFFFFDAD6) +val md_theme_light_onError = Color(0xFFFFFFFF) +val md_theme_light_onErrorContainer = Color(0xFF410002) +val md_theme_light_background = Color(0xFFFBFCFE) +val md_theme_light_onBackground = Color(0xFF191C1E) +val md_theme_light_surface = Color(0xFFFBFCFE) +val md_theme_light_onSurface = Color(0xFF191C1E) +val md_theme_light_surfaceVariant = Color(0xFFDCE3E9) +val md_theme_light_onSurfaceVariant = Color(0xFF40484D) +val md_theme_light_outline = Color(0xFF71787D) +val md_theme_light_inverseOnSurface = Color(0xFFF0F1F3) +val md_theme_light_inverseSurface = Color(0xFF2E3133) +val md_theme_light_inversePrimary = Color(0xFF75D1FF) +val md_theme_light_shadow = Color(0xFF000000) +val md_theme_light_surfaceTint = Color(0xFF006688) +val md_theme_light_outlineVariant = Color(0xFFC0C7CD) +val md_theme_light_scrim = Color(0xFF000000) + +val md_theme_dark_primary = Color(0xFF75D1FF) +val md_theme_dark_onPrimary = Color(0xFF003548) +val md_theme_dark_primaryContainer = Color(0xFF004D67) +val md_theme_dark_onPrimaryContainer = Color(0xFFC2E8FF) +val md_theme_dark_secondary = Color(0xFFB5C9D7) +val md_theme_dark_onSecondary = Color(0xFF20333D) +val md_theme_dark_secondaryContainer = Color(0xFF364954) +val md_theme_dark_onSecondaryContainer = Color(0xFFD1E5F3) +val md_theme_dark_tertiary = Color(0xFFC9C2EA) +val md_theme_dark_onTertiary = Color(0xFF312C4C) +val md_theme_dark_tertiaryContainer = Color(0xFF474364) +val md_theme_dark_onTertiaryContainer = Color(0xFFE5DEFF) +val md_theme_dark_error = Color(0xFFFFB4AB) +val md_theme_dark_errorContainer = Color(0xFF93000A) +val md_theme_dark_onError = Color(0xFF690005) +val md_theme_dark_onErrorContainer = Color(0xFFFFDAD6) +val md_theme_dark_background = Color(0xFF191C1E) +val md_theme_dark_onBackground = Color(0xFFE1E2E5) +val md_theme_dark_surface = Color(0xFF191C1E) +val md_theme_dark_onSurface = Color(0xFFE1E2E5) +val md_theme_dark_surfaceVariant = Color(0xFF40484D) +val md_theme_dark_onSurfaceVariant = Color(0xFFC0C7CD) +val md_theme_dark_outline = Color(0xFF8A9297) +val md_theme_dark_inverseOnSurface = Color(0xFF191C1E) +val md_theme_dark_inverseSurface = Color(0xFFE1E2E5) +val md_theme_dark_inversePrimary = Color(0xFF006688) +val md_theme_dark_shadow = Color(0xFF000000) +val md_theme_dark_surfaceTint = Color(0xFF75D1FF) +val md_theme_dark_outlineVariant = Color(0xFF40484D) +val md_theme_dark_scrim = Color(0xFF000000) + + +val seed = Color(0xFF4FC3F7) + +val Gray50 = Color(0xFFF9FAFB) +val Gray100 = Color(0xFFF3F4F6) +val Gray200 = Color(0xFFE5E7EB) +val Gray300 = Color(0xFFD1D5DB) +val Gray400 = Color(0xFF9CA3AF) +val Gray500 = Color(0xFF6B7280) +val Gray600 = Color(0xFF4B5563) +val Gray700 = Color(0xFF374151) +val Gray800 = Color(0xFF1F2937) +val Gray900 = Color(0xFF111827) +val Red50 = Color(0xFFFEF2F2) +val Red100 = Color(0xFFFEE2E2) +val Red200 = Color(0xFFFECACA) +val Red300 = Color(0xFFFCA5A5) +val Red400 = Color(0xFFF87171) +val Red500 = Color(0xFFEF4444) +val Red600 = Color(0xFFDC2626) +val Red700 = Color(0xFFB91C1C) +val Red800 = Color(0xFF991B1B) +val Red900 = Color(0xFF7F1D1D) +val Yellow50 = Color(0xFFFFFBEB) +val Yellow100 = Color(0xFFFEF3C7) +val Yellow200 = Color(0xFFFDE68A) +val Yellow300 = Color(0xFFFCD34D) +val Yellow400 = Color(0xFFFBBF24) +val Yellow500 = Color(0xFFF59E0B) +val Yellow600 = Color(0xFFD97706) +val Yellow700 = Color(0xFFB45309) +val Yellow800 = Color(0xFF92400E) +val Yellow900 = Color(0xFF78350F) +val Green50 = Color(0xFFECFDF5) +val Green100 = Color(0xFFD1FAE5) +val Green200 = Color(0xFFA7F3D0) +val Green300 = Color(0xFF6EE7B7) +val Green400 = Color(0xFF34D399) +val Green500 = Color(0xFF10B981) +val Green600 = Color(0xFF059669) +val Green700 = Color(0xFF047857) +val Green800 = Color(0xFF065F46) +val Green900 = Color(0xFF064E3B) +val Blue50 = Color(0xFFEFF6FF) +val Blue100 = Color(0xFFDBEAFE) +val Blue200 = Color(0xFFBFDBFE) +val Blue300 = Color(0xFF93C5FD) +val Blue400 = Color(0xFF60A5FA) +val Blue500 = Color(0xFF3B82F6) +val Blue600 = Color(0xFF2563EB) +val Blue700 = Color(0xFF1D4ED8) +val Blue800 = Color(0xFF1E40AF) +val Blue900 = Color(0xFF1E3A8A) +val Indigo50 = Color(0xFFEEF2FF) +val Indigo100 = Color(0xFFE0E7FF) +val Indigo200 = Color(0xFFC7D2FE) +val Indigo300 = Color(0xFFA5B4FC) +val Indigo400 = Color(0xFF818CF8) +val Indigo500 = Color(0xFF6366F1) +val Indigo600 = Color(0xFF4F46E5) +val Indigo700 = Color(0xFF4338CA) +val Indigo800 = Color(0xFF3730A3) +val Indigo900 = Color(0xFF312E81) +val Purple50 = Color(0xFFF5F3FF) +val Purple100 = Color(0xFFEDE9FE) +val Purple200 = Color(0xFFDDD6FE) +val Purple300 = Color(0xFFC4B5FD) +val Purple400 = Color(0xFFA78BFA) +val Purple500 = Color(0xFF8B5CF6) +val Purple600 = Color(0xFF7C3AED) +val Purple700 = Color(0xFF6D28D9) +val Purple800 = Color(0xFF5B21B6) +val Purple900 = Color(0xFF4C1D95) +val Pink50 = Color(0xFFFDF2F8) +val Pink100 = Color(0xFFFCE7F3) +val Pink200 = Color(0xFFFBCFE8) +val Pink300 = Color(0xFFF9A8D4) +val Pink400 = Color(0xFFF472B6) +val Pink500 = Color(0xFFEC4899) +val Pink600 = Color(0xFFDB2777) +val Pink700 = Color(0xFFBE185D) +val Pink800 = Color(0xFF9D174D) +val Pink900 = Color(0xFF831843) \ No newline at end of file diff --git a/app/src/main/java/xyz/quaver/pupil/ui/theme/Theme.kt b/app/src/main/java/xyz/quaver/pupil/ui/theme/Theme.kt new file mode 100644 index 00000000..c2d38f1d --- /dev/null +++ b/app/src/main/java/xyz/quaver/pupil/ui/theme/Theme.kt @@ -0,0 +1,97 @@ +package xyz.quaver.pupil.ui.theme + +import android.os.Build +import androidx.compose.foundation.isSystemInDarkTheme +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.darkColorScheme +import androidx.compose.material3.dynamicDarkColorScheme +import androidx.compose.material3.dynamicLightColorScheme +import androidx.compose.material3.lightColorScheme +import androidx.compose.runtime.Composable +import androidx.compose.ui.platform.LocalContext + + +private val LightColors = lightColorScheme( + primary = md_theme_light_primary, + onPrimary = md_theme_light_onPrimary, + primaryContainer = md_theme_light_primaryContainer, + onPrimaryContainer = md_theme_light_onPrimaryContainer, + secondary = md_theme_light_secondary, + onSecondary = md_theme_light_onSecondary, + secondaryContainer = md_theme_light_secondaryContainer, + onSecondaryContainer = md_theme_light_onSecondaryContainer, + tertiary = md_theme_light_tertiary, + onTertiary = md_theme_light_onTertiary, + tertiaryContainer = md_theme_light_tertiaryContainer, + onTertiaryContainer = md_theme_light_onTertiaryContainer, + error = md_theme_light_error, + errorContainer = md_theme_light_errorContainer, + onError = md_theme_light_onError, + onErrorContainer = md_theme_light_onErrorContainer, + background = md_theme_light_background, + onBackground = md_theme_light_onBackground, + surface = md_theme_light_surface, + onSurface = md_theme_light_onSurface, + surfaceVariant = md_theme_light_surfaceVariant, + onSurfaceVariant = md_theme_light_onSurfaceVariant, + outline = md_theme_light_outline, + inverseOnSurface = md_theme_light_inverseOnSurface, + inverseSurface = md_theme_light_inverseSurface, + inversePrimary = md_theme_light_inversePrimary, + surfaceTint = md_theme_light_surfaceTint, + outlineVariant = md_theme_light_outlineVariant, + scrim = md_theme_light_scrim, +) + + +private val DarkColors = darkColorScheme( + primary = md_theme_dark_primary, + onPrimary = md_theme_dark_onPrimary, + primaryContainer = md_theme_dark_primaryContainer, + onPrimaryContainer = md_theme_dark_onPrimaryContainer, + secondary = md_theme_dark_secondary, + onSecondary = md_theme_dark_onSecondary, + secondaryContainer = md_theme_dark_secondaryContainer, + onSecondaryContainer = md_theme_dark_onSecondaryContainer, + tertiary = md_theme_dark_tertiary, + onTertiary = md_theme_dark_onTertiary, + tertiaryContainer = md_theme_dark_tertiaryContainer, + onTertiaryContainer = md_theme_dark_onTertiaryContainer, + error = md_theme_dark_error, + errorContainer = md_theme_dark_errorContainer, + onError = md_theme_dark_onError, + onErrorContainer = md_theme_dark_onErrorContainer, + background = md_theme_dark_background, + onBackground = md_theme_dark_onBackground, + surface = md_theme_dark_surface, + onSurface = md_theme_dark_onSurface, + surfaceVariant = md_theme_dark_surfaceVariant, + onSurfaceVariant = md_theme_dark_onSurfaceVariant, + outline = md_theme_dark_outline, + inverseOnSurface = md_theme_dark_inverseOnSurface, + inverseSurface = md_theme_dark_inverseSurface, + inversePrimary = md_theme_dark_inversePrimary, + surfaceTint = md_theme_dark_surfaceTint, + outlineVariant = md_theme_dark_outlineVariant, + scrim = md_theme_dark_scrim, +) + +@Composable +fun AppTheme( + darkTheme: Boolean = isSystemInDarkTheme(), + content: @Composable() () -> Unit, +) { + val dynamicColor = Build.VERSION.SDK_INT >= Build.VERSION_CODES.S + + val colors = when { + dynamicColor && darkTheme -> dynamicDarkColorScheme(LocalContext.current) + dynamicColor && !darkTheme -> dynamicLightColorScheme(LocalContext.current) + darkTheme -> DarkColors + else -> LightColors + } + + MaterialTheme( + colorScheme = colors, + content = content + ) +} \ No newline at end of file diff --git a/app/src/main/java/xyz/quaver/pupil/ui/viewmodel/MainViewModel.kt b/app/src/main/java/xyz/quaver/pupil/ui/viewmodel/MainViewModel.kt new file mode 100644 index 00000000..7e44d786 --- /dev/null +++ b/app/src/main/java/xyz/quaver/pupil/ui/viewmodel/MainViewModel.kt @@ -0,0 +1,12 @@ +package xyz.quaver.pupil.ui.viewmodel + +import androidx.lifecycle.ViewModel +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow + +class MainViewModel : ViewModel() { + private val _uiState = MutableStateFlow(SearchState()) + val searchState: StateFlow = _uiState +} + +data class SearchState(val stub: String = "") \ No newline at end of file diff --git a/app/src/main/java/xyz/quaver/pupil/util/misc.kt b/app/src/main/java/xyz/quaver/pupil/util/misc.kt index 327ba3f8..2ab7a2f2 100644 --- a/app/src/main/java/xyz/quaver/pupil/util/misc.kt +++ b/app/src/main/java/xyz/quaver/pupil/util/misc.kt @@ -24,24 +24,26 @@ import android.app.Activity import android.content.Context import android.content.pm.PackageManager import android.os.Build -import android.util.Log import androidx.activity.result.ActivityResultLauncher import androidx.appcompat.app.AlertDialog import androidx.core.app.ActivityCompat import androidx.core.content.ContextCompat import com.google.firebase.crashlytics.FirebaseCrashlytics -import kotlinx.serialization.json.* +import kotlinx.serialization.json.JsonElement +import kotlinx.serialization.json.contentOrNull +import kotlinx.serialization.json.jsonArray +import kotlinx.serialization.json.jsonObject +import kotlinx.serialization.json.jsonPrimitive import okhttp3.OkHttpClient import okhttp3.Request import xyz.quaver.pupil.R import xyz.quaver.pupil.hitomi.GalleryBlock import xyz.quaver.pupil.hitomi.GalleryInfo import xyz.quaver.pupil.hitomi.imageUrlFromImage -import java.util.* -import kotlin.collections.ArrayList +import java.util.Locale @OptIn(ExperimentalStdlibApi::class) -fun String.wordCapitalize() : String { +fun String.wordCapitalize(): String { val result = ArrayList() @SuppressLint("DefaultLocale") @@ -59,8 +61,9 @@ private val suffix = listOf( "TB" //really? ) -fun byteToString(byte: Long, precision : Int = 1) : String { - var size = byte.toDouble(); var suffixIndex = 0 +fun byteToString(byte: Long, precision: Int = 1): String { + var size = byte.toDouble() + var suffixIndex = 0 while (size >= 1024) { size /= 1024 @@ -92,6 +95,7 @@ val formatMap = mapOf (String)>( "-group-" to { if (groups.isNotEmpty()) groups.joinToString() else "N/A" } // TODO ) + /** * Formats download folder name with given Metadata */ @@ -117,20 +121,20 @@ suspend fun GalleryInfo.getRequestBuilders(): List { runCatching { imageUrlFromImage(galleryID, it, false) } - .onFailure { - FirebaseCrashlytics.getInstance().recordException(it) - } - .getOrDefault("https://a/") + .onFailure { + FirebaseCrashlytics.getInstance().recordException(it) + } + .getOrDefault("https://a/") ) .header("Referer", "https://hitomi.la/") } } fun byteCount(codePoint: Int): Int = when (codePoint) { - in 0 ..< 0x80 -> 1 - in 0x80 ..< 0x800 -> 2 - in 0x800 ..< 0x10000 -> 3 - in 0x10000 ..< 0x110000 -> 4 + in 0..<0x80 -> 1 + in 0x80..<0x800 -> 2 + in 0x800..<0x10000 -> 3 + in 0x10000..<0x110000 -> 4 else -> 0 } @@ -168,7 +172,10 @@ val JsonElement.content fun checkNotificationEnabled(context: Context) = Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU || - ContextCompat.checkSelfPermission(context, Manifest.permission.POST_NOTIFICATIONS) == PackageManager.PERMISSION_GRANTED + ContextCompat.checkSelfPermission( + context, + Manifest.permission.POST_NOTIFICATIONS + ) == PackageManager.PERMISSION_GRANTED fun showNotificationPermissionExplanationDialog(context: Context) { AlertDialog.Builder(context) @@ -182,12 +189,16 @@ fun requestNotificationPermission( activity: Activity, requestPermissionLauncher: ActivityResultLauncher, showRationale: Boolean = true, - ifGranted: () -> Unit, + ifGranted: () -> Unit = { }, ) { when { checkNotificationEnabled(activity) -> ifGranted() - showRationale && ActivityCompat.shouldShowRequestPermissionRationale(activity, Manifest.permission.POST_NOTIFICATIONS) -> + showRationale && ActivityCompat.shouldShowRequestPermissionRationale( + activity, + Manifest.permission.POST_NOTIFICATIONS + ) -> showNotificationPermissionExplanationDialog(activity) + else -> requestPermissionLauncher.launch(Manifest.permission.POST_NOTIFICATIONS) } diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 39728430..b4c8c6dc 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -46,6 +46,7 @@ runner = "1.6.2" skyfishjyLibrary = "1.0.1" dotsindicator = "5.1.0" workRuntimeKtx = "2.10.0" +material3WindowSizeClassAndroid = "1.3.1" [libraries] activity-ktx = { module = "androidx.activity:activity-ktx", version.ref = "activityKtx" } @@ -114,6 +115,9 @@ compose-adaptive = { module = "androidx.compose.material3.adaptive:adaptive" } compose-activity = { module = "androidx.activity:activity-compose", version = "1.10.1" } compose-lifecycle-viewmodel = { module = "androidx.lifecycle:lifecycle-viewmodel-compose", version = "2.8.7" } compose-livedata = { module = "androidx.compose.runtime:runtime-livedata" } +compose-navigation = { module = "androidx.navigation:navigation-compose", version = "2.8.8" } + +accompanist-adaptive = { module = "com.google.accompanist:accompanist-adaptive", version = "0.37.2" } [plugins] android-application = { id = "com.android.application", version.ref = "agp" }