From bde91e0dd3c8bdff8efadef06e52f3d770d16387 Mon Sep 17 00:00:00 2001 From: Fahim Masud Choudhury Date: Tue, 23 Dec 2025 20:31:00 +0600 Subject: [PATCH 1/4] chore: add Jetpack Compose dependencies --- app/build.gradle | 24 +++++++++++++++++++++++- build.gradle | 1 + gradle/libs.versions.toml | 16 +++++++++++++++- 3 files changed, 39 insertions(+), 2 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index a084b63bf..c7e27d5a8 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -10,6 +10,7 @@ plugins { id 'kotlin-allopen' id 'kotlin-parcelize' id 'jacoco' + alias libs.plugins.compose.compiler } jacoco { @@ -73,7 +74,7 @@ tasks.withType(Test).configureEach { } android { - compileSdk = 35 + compileSdk = 36 defaultConfig { applicationId = "foundation.e.apps" @@ -178,6 +179,7 @@ android { buildFeatures { buildConfig = true viewBinding = true + compose = true } compileOptions { sourceCompatibility = JavaVersion.VERSION_21 @@ -343,6 +345,26 @@ dependencies { // JSoup implementation(libs.jsoup) + + // Compose + def composeBom = platform(libs.compose.bom) + implementation composeBom + androidTestImplementation composeBom + + implementation libs.compose.material3 + implementation libs.compose.material.icons.extended + + implementation libs.activity.compose + implementation libs.lifecycle.viewmodel.compose + implementation libs.runtime.livedata + + // Android Studio Preview support for Compose + implementation libs.compose.ui.tooling.preview + debugImplementation libs.compose.ui.tooling + + // UI Tests for Compose + androidTestImplementation libs.compose.ui.test.junit4 + debugImplementation libs.compose.ui.test.manifest } def retrieveKey(String keyName, String defaultValue) { diff --git a/build.gradle b/build.gradle index 0c06063ba..73cead40b 100644 --- a/build.gradle +++ b/build.gradle @@ -11,6 +11,7 @@ plugins { alias libs.plugins.ksp apply false alias libs.plugins.navigation.safeargs apply false alias libs.plugins.detekt + alias libs.plugins.compose.compiler apply false } diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 48ab23260..552cd00ef 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,9 +1,11 @@ [versions] +activityCompose = "1.12.2" activityKtx = "1.10.0" -androidGradlePlugin = "8.8.0" +androidGradlePlugin = "8.9.3" appcompat = "1.7.0" bcpgJdk15on = "1.60" coil = "1.4.0" +composeBom = "2025.12.01" constraintlayout = "2.2.0" core = "1.6.1" coreKtx = "1.15.0" @@ -29,6 +31,7 @@ kotlinxCoroutinesAndroid = "1.10.1" kotlinxSerialization = "1.9.0" ksp = "2.1.0-1.0.29" legacySupportV4 = "1.0.0" +lifecycleViewmodelCompose = "2.8.5" lifecycleViewmodelKtx = "2.8.7" loggingInterceptor = "4.11.0" material = "1.12.0" @@ -55,10 +58,18 @@ recyclerview = "1.4.0" workRuntimeKtx = "2.10.0" [libraries] +activity-compose = { module = "androidx.activity:activity-compose", version.ref = "activityCompose" } activity-ktx = { module = "androidx.activity:activity-ktx", version.ref = "activityKtx" } appcompat = { module = "androidx.appcompat:appcompat", version.ref = "appcompat" } bcpg-jdk15on = { module = "org.bouncycastle:bcpg-jdk15on", version.ref = "bcpgJdk15on" } coil = { module = "io.coil-kt:coil", version.ref = "coil" } +compose-bom = { module = "androidx.compose:compose-bom", version.ref = "composeBom" } +compose-material3 = { module = "androidx.compose.material3:material3" } +compose-material-icons-extended = { module = "androidx.compose.material:material-icons-extended" } +compose-ui-test-junit4 = { module = "androidx.compose.ui:ui-test-junit4" } +compose-ui-test-manifest = { module = "androidx.compose.ui:ui-test-manifest" } +compose-ui-tooling-preview = { module = "androidx.compose.ui:ui-tooling-preview" } +compose-ui-tooling = { module = "androidx.compose.ui:ui-tooling" } constraintlayout = { module = "androidx.constraintlayout:constraintlayout", version.ref = "constraintlayout" } converter-gson = { module = "com.squareup.retrofit2:converter-gson", version.ref = "gson" } converter-moshi = { module = "com.squareup.retrofit2:converter-moshi", version.ref = "gson" } @@ -89,6 +100,7 @@ kotlinx-coroutines-test = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-t kotlinx-serialization-json = { module = "org.jetbrains.kotlinx:kotlinx-serialization-json", version.ref = "kotlinxSerialization" } legacy-support-v4 = { module = "androidx.legacy:legacy-support-v4", version.ref = "legacySupportV4" } lifecycle-livedata-ktx = { module = "androidx.lifecycle:lifecycle-livedata-ktx", version.ref = "lifecycleViewmodelKtx" } +lifecycle-viewmodel-compose = { module = "androidx.lifecycle:lifecycle-viewmodel-compose", version.ref = "lifecycleViewmodelCompose" } lifecycle-viewmodel-ktx = { module = "androidx.lifecycle:lifecycle-viewmodel-ktx", version.ref = "lifecycleViewmodelKtx" } lifecycle-runtime-ktx = { module = "androidx.lifecycle:lifecycle-runtime-ktx", version.ref = "lifecycleViewmodelKtx" } logging-interceptor = { module = "com.squareup.okhttp3:logging-interceptor", version.ref = "loggingInterceptor" } @@ -110,6 +122,7 @@ robolectric = { module = "org.robolectric:robolectric", version.ref = "robolectr room-compiler = { module = "androidx.room:room-compiler", version.ref = "room" } room-ktx = { module = "androidx.room:room-ktx", version.ref = "room" } room-runtime = { module = "androidx.room:room-runtime", version.ref = "room" } +runtime-livedata = { module = "androidx.compose.runtime:runtime-livedata" } shimmer = { module = "com.facebook.shimmer:shimmer", version.ref = "shimmer" } telemetry = { module = "foundation.e.lib:telemetry", version.ref = "telemetry" } timber = { module = "com.jakewharton.timber:timber", version.ref = "timber" } @@ -120,6 +133,7 @@ work-runtime-ktx = { module = "androidx.work:work-runtime-ktx", version.ref = "w [plugins] android-application = { id = "com.android.application", version.ref = "androidGradlePlugin" } android-library = { id = "com.android.library", version.ref = "androidGradlePlugin" } +compose-compiler = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" } kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" } kotlin-plugin-allopen = { id = "org.jetbrains.kotlin.plugin.allopen", version.ref = "kotlin" } kotlin-jvm = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" } -- GitLab From ebd18c487cce87e18d0afe177d931a66f06297cb Mon Sep 17 00:00:00 2001 From: Fahim Masud Choudhury Date: Thu, 15 Jan 2026 16:15:33 +0600 Subject: [PATCH 2/4] chore: add detekt plugin and rules for Compose code --- build.gradle | 1 + detekt.yml | 131 ++++++++++++++++++++++++++++++++++++++ gradle/libs.versions.toml | 2 + 3 files changed, 134 insertions(+) diff --git a/build.gradle b/build.gradle index 73cead40b..0a32817a8 100644 --- a/build.gradle +++ b/build.gradle @@ -39,6 +39,7 @@ detekt { dependencies { detektPlugins libs.detekt.formatting + detektPlugins libs.detekt.compose } tasks.register('clean', Delete) { diff --git a/detekt.yml b/detekt.yml index 773174aa0..c4dd749b4 100644 --- a/detekt.yml +++ b/detekt.yml @@ -7,6 +7,11 @@ naming: VariableNaming: active: false + FunctionNaming: + active: true + ignoreAnnotated: + - Composable + # Style rules style: @@ -14,8 +19,134 @@ style: ForbiddenComment: active: false + MagicNumber: + active: true + ignorePropertyDeclaration: true + + UnusedPrivateMember: + active: true + ignoreAnnotated: + - Preview + # Complexity rules complexity: TooManyFunctions: ignorePrivate: true + + LongMethod: + active: true + ignoreAnnotated: + - Composable + + CyclomaticComplexMethod: + active: true + ignoreAnnotated: + - Composable + + LongParameterList: + active: true + ignoreAnnotated: + - Composable + + NestedBlockDepth: + active: true + ignoreAnnotated: + - Composable + +Compose: + ComposableAnnotationNaming: + active: true + + ComposableNaming: + active: true + + ComposableParamOrder: + active: true + + CompositionLocalAllowlist: + active: true + + CompositionLocalNaming: + active: true + + ContentEmitterReturningValues: + active: true + + ContentTrailingLambda: + active: true + + ContentSlotReused: + active: true + + DefaultsVisibility: + active: true + + LambdaParameterEventTrailing: + active: true + + LambdaParameterInRestartableEffect: + active: true + + Material2: + active: false + + ModifierClickableOrder: + active: true + + ModifierComposed: + active: true + + ModifierMissing: + active: true + + ModifierNaming: + active: true + + ModifierNotUsedAtRoot: + active: true + + ModifierReused: + active: true + + ModifierWithoutDefault: + active: true + + MultipleEmitters: + active: true + + MutableParams: + active: true + + MutableStateAutoboxing: + active: true + + MutableStateParam: + active: true + + ParameterNaming: + active: true + + PreviewAnnotationNaming: + active: true + + PreviewNaming: + active: false + + PreviewPublic: + active: true + + RememberMissing: + active: true + + RememberContentMissing: + active: true + + UnstableCollections: + active: false + + ViewModelForwarding: + active: true + + ViewModelInjection: + active: true diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 552cd00ef..2b89cf589 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -12,6 +12,7 @@ coreKtx = "1.15.0" coreTesting = "2.2.0" datastorePreferences = "1.1.2" detektFormatting = "1.23.8" +detektCompose = "0.4.23" elib = "0.0.1-alpha11" espressoCore = "3.6.1" hiltCompiler = "1.2.0" @@ -79,6 +80,7 @@ core-ktx = { module = "androidx.core:core-ktx", version.ref = "coreKtx" } core-testing = { module = "androidx.arch.core:core-testing", version.ref = "coreTesting" } datastore-preferences = { module = "androidx.datastore:datastore-preferences", version.ref = "datastorePreferences" } detekt-formatting = { module = "io.gitlab.arturbosch.detekt:detekt-formatting", version.ref = "detektFormatting" } +detekt-compose = { module = "io.nlopez.compose.rules:detekt", version.ref = "detektCompose" } elib = { module = "foundation.e:elib", version.ref = "elib" } ext-junit = { module = "androidx.test.ext:junit", version.ref = "junitVersion" } espresso-core = { module = "androidx.test.espresso:espresso-core", version.ref = "espressoCore" } -- GitLab From fca14218048a37948c5a36ecfb89d6c1c4989800 Mon Sep 17 00:00:00 2001 From: Fahim Masud Choudhury Date: Thu, 15 Jan 2026 16:27:42 +0600 Subject: [PATCH 3/4] chore: resolve lint issues --- .../java/foundation/e/apps/data/DownloadManager.kt | 3 ++- .../e/apps/data/application/data/Application.kt | 5 +++-- .../foundation/e/apps/data/install/AppManagerImpl.kt | 3 ++- .../java/foundation/e/apps/install/pkg/PwaManager.kt | 10 +++++----- .../e/apps/ui/parentFragment/TimeoutFragment.kt | 4 ++-- .../foundation/e/apps/ui/settings/SettingsFragment.kt | 4 ++-- 6 files changed, 16 insertions(+), 13 deletions(-) diff --git a/app/src/main/java/foundation/e/apps/data/DownloadManager.kt b/app/src/main/java/foundation/e/apps/data/DownloadManager.kt index df1049b47..b20650f4f 100644 --- a/app/src/main/java/foundation/e/apps/data/DownloadManager.kt +++ b/app/src/main/java/foundation/e/apps/data/DownloadManager.kt @@ -21,6 +21,7 @@ import android.app.DownloadManager import android.content.Context import android.database.Cursor import android.net.Uri +import androidx.core.net.toUri import dagger.hilt.android.qualifiers.ApplicationContext import foundation.e.apps.OpenForTesting import foundation.e.apps.R @@ -78,7 +79,7 @@ class DownloadManager @Inject constructor( ): Long { var downloadId = -1L try { - val request = DownloadManager.Request(Uri.parse(url)) + val request = DownloadManager.Request(url.toUri()) .setTitle(context.getString(R.string.downloading)) .setDestinationUri(Uri.fromFile(downloadFile)) if (appLoungePreference.isOnlyUnmeteredNetworkEnabled()) { diff --git a/app/src/main/java/foundation/e/apps/data/application/data/Application.kt b/app/src/main/java/foundation/e/apps/data/application/data/Application.kt index 0ac131503..aa5767318 100644 --- a/app/src/main/java/foundation/e/apps/data/application/data/Application.kt +++ b/app/src/main/java/foundation/e/apps/data/application/data/Application.kt @@ -19,6 +19,7 @@ package foundation.e.apps.data.application.data import android.net.Uri +import androidx.core.net.toUri import com.aurora.gplayapi.Constants.Restriction import com.aurora.gplayapi.data.models.ContentRating import com.google.gson.annotations.SerializedName @@ -109,10 +110,10 @@ data class Application( val Application.shareUri: Uri get() = when (type) { - PWA -> Uri.parse(url) + PWA -> url.toUri() NATIVE -> when { isFDroidApp -> buildFDroidUri(package_name) - else -> Uri.parse(shareUrl) + else -> shareUrl.toUri() } } diff --git a/app/src/main/java/foundation/e/apps/data/install/AppManagerImpl.kt b/app/src/main/java/foundation/e/apps/data/install/AppManagerImpl.kt index 2899e605b..b7ff07e81 100644 --- a/app/src/main/java/foundation/e/apps/data/install/AppManagerImpl.kt +++ b/app/src/main/java/foundation/e/apps/data/install/AppManagerImpl.kt @@ -24,6 +24,7 @@ import android.app.NotificationManager import android.content.Context import android.net.Uri import android.os.Environment +import androidx.core.net.toUri import androidx.lifecycle.LiveData import dagger.hilt.android.qualifiers.ApplicationContext import foundation.e.apps.R @@ -236,7 +237,7 @@ class AppManagerImpl @Inject constructor( } else { context.getString(R.string.additional_file_for, appInstall.name) } - val request = DownloadManager.Request(Uri.parse(it)) + val request = DownloadManager.Request(it.toUri()) .setTitle(requestTitle) .setDestinationUri(Uri.fromFile(packagePath)) diff --git a/app/src/main/java/foundation/e/apps/install/pkg/PwaManager.kt b/app/src/main/java/foundation/e/apps/install/pkg/PwaManager.kt index c1b63a109..649318dcc 100644 --- a/app/src/main/java/foundation/e/apps/install/pkg/PwaManager.kt +++ b/app/src/main/java/foundation/e/apps/install/pkg/PwaManager.kt @@ -7,10 +7,10 @@ import android.content.Intent import android.database.Cursor import android.graphics.Bitmap import android.graphics.BitmapFactory -import android.net.Uri import androidx.core.content.pm.ShortcutInfoCompat import androidx.core.content.pm.ShortcutManagerCompat import androidx.core.graphics.drawable.IconCompat +import androidx.core.net.toUri import dagger.hilt.android.qualifiers.ApplicationContext import foundation.e.apps.OpenForTesting import foundation.e.apps.data.application.data.Application @@ -64,7 +64,7 @@ class PwaManager @Inject constructor( */ fun getPwaStatus(application: Application): Status { context.contentResolver.query( - Uri.parse(PWA_PLAYER), + PWA_PLAYER.toUri(), null, null, null, @@ -106,7 +106,7 @@ class PwaManager @Inject constructor( */ fun launchPwa(application: Application) { val launchIntent = Intent(VIEW_PWA).apply { - data = Uri.parse(application.url) + data = application.url.toUri() putExtra(PWA_ID, application.pwaPlayerDbId) addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) addFlags(Intent.FLAG_ACTIVITY_NEW_DOCUMENT or Intent.FLAG_ACTIVITY_RETAIN_IN_RECENTS) @@ -137,7 +137,7 @@ class PwaManager @Inject constructor( put(ICON, iconByteArray) } - context.contentResolver.insert(Uri.parse(PWA_PLAYER), values)?.let { + context.contentResolver.insert(PWA_PLAYER.toUri(), values)?.let { val databaseID = ContentUris.parseId(it) publishShortcut(appInstall, iconBitmap, databaseID) } @@ -170,7 +170,7 @@ class PwaManager @Inject constructor( val intent = Intent().apply { action = VIEW_PWA - data = Uri.parse(appInstall.downloadURLList[0]) + data = appInstall.downloadURLList[0].toUri() putExtra(PWA_NAME, appInstall.name) putExtra(PWA_ID, databaseID) addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) diff --git a/app/src/main/java/foundation/e/apps/ui/parentFragment/TimeoutFragment.kt b/app/src/main/java/foundation/e/apps/ui/parentFragment/TimeoutFragment.kt index 208769524..9f5f31e68 100644 --- a/app/src/main/java/foundation/e/apps/ui/parentFragment/TimeoutFragment.kt +++ b/app/src/main/java/foundation/e/apps/ui/parentFragment/TimeoutFragment.kt @@ -19,11 +19,11 @@ package foundation.e.apps.ui.parentFragment import android.content.Intent import android.graphics.Paint -import android.net.Uri import android.widget.TextView import androidx.annotation.LayoutRes import androidx.annotation.StringRes import androidx.appcompat.app.AlertDialog +import androidx.core.net.toUri import androidx.core.view.isVisible import androidx.fragment.app.Fragment import androidx.lifecycle.LifecycleOwner @@ -235,7 +235,7 @@ abstract class TimeoutFragment(@LayoutRes layoutId: Int) : Fragment(layoutId) { private fun openTroubleshootingPage() { val troubleshootUrl = getString(R.string.troubleshootURL, Locale.getDefault().language) val openUrlIntent = Intent(Intent.ACTION_VIEW) - openUrlIntent.data = Uri.parse(troubleshootUrl) + openUrlIntent.data = troubleshootUrl.toUri() startActivity(openUrlIntent) } diff --git a/app/src/main/java/foundation/e/apps/ui/settings/SettingsFragment.kt b/app/src/main/java/foundation/e/apps/ui/settings/SettingsFragment.kt index fb74c7f70..394d2824a 100644 --- a/app/src/main/java/foundation/e/apps/ui/settings/SettingsFragment.kt +++ b/app/src/main/java/foundation/e/apps/ui/settings/SettingsFragment.kt @@ -21,10 +21,10 @@ package foundation.e.apps.ui.settings import android.content.ClipData import android.content.ClipboardManager import android.content.Intent -import android.net.Uri import android.os.Bundle import android.view.View import android.widget.Toast +import androidx.core.net.toUri import androidx.core.view.isVisible import androidx.fragment.app.activityViewModels import androidx.lifecycle.ViewModelProvider @@ -140,7 +140,7 @@ class SettingsFragment : PreferenceFragmentCompat() { } val troubleshootUrl = getString(R.string.troubleshootURL, Locale.getDefault().language) - troubleShootPreference?.intent = Intent(Intent.ACTION_VIEW, Uri.parse(troubleshootUrl)) + troubleShootPreference?.intent = Intent(Intent.ACTION_VIEW, troubleshootUrl.toUri()) } /** -- GitLab From b8e064a9de0eea6a68eec931936f569758438728 Mon Sep 17 00:00:00 2001 From: Fahim Masud Choudhury Date: Thu, 15 Jan 2026 16:39:38 +0600 Subject: [PATCH 4/4] chore: add scaffolding for v2 search --- .../e/apps/ui/compose/theme/AppTheme.kt | 57 +++++++++++++++++ .../e/apps/ui/compose/theme/Typography.kt | 62 +++++++++++++++++++ .../e/apps/ui/search/v2/SearchFragmentV2.kt | 38 ++++++++++++ .../e/apps/ui/search/v2/SearchViewModelV2.kt | 26 ++++++++ .../main/res/layout/fragment_search_v2.xml | 7 +++ .../res/navigation/navigation_resource.xml | 5 ++ 6 files changed, 195 insertions(+) create mode 100644 app/src/main/java/foundation/e/apps/ui/compose/theme/AppTheme.kt create mode 100644 app/src/main/java/foundation/e/apps/ui/compose/theme/Typography.kt create mode 100644 app/src/main/java/foundation/e/apps/ui/search/v2/SearchFragmentV2.kt create mode 100644 app/src/main/java/foundation/e/apps/ui/search/v2/SearchViewModelV2.kt create mode 100644 app/src/main/res/layout/fragment_search_v2.xml diff --git a/app/src/main/java/foundation/e/apps/ui/compose/theme/AppTheme.kt b/app/src/main/java/foundation/e/apps/ui/compose/theme/AppTheme.kt new file mode 100644 index 000000000..f7e7dea08 --- /dev/null +++ b/app/src/main/java/foundation/e/apps/ui/compose/theme/AppTheme.kt @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2026 e Foundation + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ +package foundation.e.apps.ui.compose.theme + +import androidx.compose.foundation.isSystemInDarkTheme +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.darkColorScheme +import androidx.compose.material3.lightColorScheme +import androidx.compose.runtime.Composable +import androidx.compose.ui.res.colorResource +import foundation.e.elib.R as eR + +@Composable +fun AppTheme(darkTheme: Boolean = isSystemInDarkTheme(), content: @Composable () -> Unit) { + val colorScheme = if (darkTheme) { + darkColorScheme( + primary = colorResource(eR.color.e_action_bar_dark), + secondary = colorResource(eR.color.e_action_bar_dark), + tertiary = colorResource(eR.color.e_accent_dark), + background = colorResource(eR.color.e_background_dark), + surface = colorResource(eR.color.e_floating_background_dark), + onPrimary = colorResource(eR.color.e_primary_text_color_dark), + onSecondary = colorResource(eR.color.e_primary_text_color_light), + onBackground = colorResource(eR.color.e_primary_text_color_dark), + onSurface = colorResource(eR.color.e_primary_text_color_dark), + ) + } else { + lightColorScheme( + primary = colorResource(eR.color.e_action_bar_light), + secondary = colorResource(eR.color.e_action_bar_light), + tertiary = colorResource(eR.color.e_accent_light), + background = colorResource(eR.color.e_background_light), + surface = colorResource(eR.color.e_floating_background_light), + onPrimary = colorResource(eR.color.e_primary_text_color_light), + onSecondary = colorResource(eR.color.e_primary_text_color_dark), + onBackground = colorResource(eR.color.e_primary_text_color_light), + onSurface = colorResource(eR.color.e_primary_text_color_light), + ) + } + + MaterialTheme(colorScheme = colorScheme, typography = Typography, content = content) +} diff --git a/app/src/main/java/foundation/e/apps/ui/compose/theme/Typography.kt b/app/src/main/java/foundation/e/apps/ui/compose/theme/Typography.kt new file mode 100644 index 000000000..363f5d87d --- /dev/null +++ b/app/src/main/java/foundation/e/apps/ui/compose/theme/Typography.kt @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2026 e Foundation + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ +package foundation.e.apps.ui.compose.theme + +import androidx.compose.material3.Typography +import androidx.compose.ui.text.TextStyle +import androidx.compose.ui.text.font.FontFamily +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.style.Hyphens +import androidx.compose.ui.unit.em +import androidx.compose.ui.unit.sp + +// Set of Material typography styles to start with +val Typography = Typography( + bodyLarge = TextStyle( + fontFamily = FontFamily.Default, + fontWeight = FontWeight.Normal, + fontSize = 16.sp, + lineHeight = 24.sp, + ) +) + +val titleStyle = TextStyle( + fontFamily = FontFamily.SansSerif, + fontWeight = FontWeight.Normal, + fontSize = 20.sp, + lineHeight = 24.sp, + letterSpacing = 0.02.em, + hyphens = Hyphens.Auto, +) + +val summaryStyle = TextStyle( + fontFamily = FontFamily.SansSerif, + fontWeight = FontWeight.Normal, + fontSize = 14.sp, + lineHeight = 20.sp, + letterSpacing = 0.01.em, + hyphens = Hyphens.Auto, +) + +val categoryStyle = TextStyle( + fontFamily = FontFamily.SansSerif, + fontWeight = FontWeight.Medium, + fontSize = 14.sp, + lineHeight = 20.sp, + letterSpacing = 0.01.em, +) diff --git a/app/src/main/java/foundation/e/apps/ui/search/v2/SearchFragmentV2.kt b/app/src/main/java/foundation/e/apps/ui/search/v2/SearchFragmentV2.kt new file mode 100644 index 000000000..c78d7ff04 --- /dev/null +++ b/app/src/main/java/foundation/e/apps/ui/search/v2/SearchFragmentV2.kt @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2026 e Foundation + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +package foundation.e.apps.ui.search.v2 + +import android.os.Bundle +import android.view.View +import androidx.compose.ui.platform.ComposeView +import androidx.compose.ui.platform.ViewCompositionStrategy +import androidx.fragment.app.Fragment +import foundation.e.apps.R +import foundation.e.apps.ui.compose.theme.AppTheme + +class SearchFragmentV2 : Fragment(R.layout.fragment_search_v2) { + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + val composeView = view.findViewById(R.id.composeView) + composeView.setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed) + composeView.setContent { + AppTheme { } + } + } +} diff --git a/app/src/main/java/foundation/e/apps/ui/search/v2/SearchViewModelV2.kt b/app/src/main/java/foundation/e/apps/ui/search/v2/SearchViewModelV2.kt new file mode 100644 index 000000000..3c178adec --- /dev/null +++ b/app/src/main/java/foundation/e/apps/ui/search/v2/SearchViewModelV2.kt @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2026 e Foundation + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +package foundation.e.apps.ui.search.v2 + +import androidx.lifecycle.ViewModel +import dagger.hilt.android.lifecycle.HiltViewModel +import javax.inject.Inject + +@HiltViewModel +class SearchViewModelV2 @Inject constructor() : ViewModel() diff --git a/app/src/main/res/layout/fragment_search_v2.xml b/app/src/main/res/layout/fragment_search_v2.xml new file mode 100644 index 000000000..eb9fbeab7 --- /dev/null +++ b/app/src/main/res/layout/fragment_search_v2.xml @@ -0,0 +1,7 @@ + + + + diff --git a/app/src/main/res/navigation/navigation_resource.xml b/app/src/main/res/navigation/navigation_resource.xml index 774666361..9de936fd2 100644 --- a/app/src/main/res/navigation/navigation_resource.xml +++ b/app/src/main/res/navigation/navigation_resource.xml @@ -67,6 +67,11 @@ android:id="@+id/action_searchFragment_to_applicationFragment" app:destination="@id/applicationFragment" /> +