From 93f3b9164679c0fb55fa5e3b4621eab11e4de405 Mon Sep 17 00:00:00 2001 From: Code-Apprentice Date: Tue, 6 Aug 2024 19:25:19 -0600 Subject: [PATCH 001/279] Delete project --- build.gradle | 15 --- gradle.properties | 30 ----- gradle/wrapper/gradle-wrapper.jar | Bin 53324 -> 0 bytes gradle/wrapper/gradle-wrapper.properties | 6 - gradlew | 164 ----------------------- gradlew.bat | 90 ------------- settings.gradle | 1 - 7 files changed, 306 deletions(-) delete mode 100644 build.gradle delete mode 100644 gradle.properties delete mode 100644 gradle/wrapper/gradle-wrapper.jar delete mode 100644 gradle/wrapper/gradle-wrapper.properties delete mode 100755 gradlew delete mode 100644 gradlew.bat delete mode 100644 settings.gradle diff --git a/build.gradle b/build.gradle deleted file mode 100644 index 0c3e65323..000000000 --- a/build.gradle +++ /dev/null @@ -1,15 +0,0 @@ -// Top-level build file where you can add configuration options common to all sub-projects/modules. - -buildscript { - repositories { - } - dependencies { - // NOTE: Do not place your application dependencies here; they belong - // in the individual module build.gradle files - } -} - -allprojects { - repositories { - } -} diff --git a/gradle.properties b/gradle.properties deleted file mode 100644 index 156140097..000000000 --- a/gradle.properties +++ /dev/null @@ -1,30 +0,0 @@ -# Project-wide Gradle settings. - -# IDE (e.g. Android Studio) users: -# Settings specified in this file will override any Gradle settings -# configured through the IDE. - -# For more details on how to configure your build environment visit -# http://www.gradle.org/docs/current/userguide/build_environment.html - -# Specifies the JVM arguments used for the daemon process. -# The setting is particularly useful for tweaking memory settings. -# Default value: -Xmx10248m -XX:MaxPermSize=256m -# org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 - -# When configured, Gradle will run in incubating parallel mode. -# This option should only be used with decoupled projects. More details, visit -# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects -org.gradle.parallel=true -org.gradle.daemon=false -org.gradle.configureondemand=true -android.useAndroidX=true -android.enableJetifier=true - -org.gradle.jvmargs=-Xmx1920M \ ---add-exports=jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED \ ---add-exports=jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED \ ---add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED -android.defaults.buildfeatures.buildconfig=true -android.nonTransitiveRClass=false -android.nonFinalResIds=false diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar deleted file mode 100644 index 3baa851b28c65f87dd36a6748e1a85cf360c1301..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 53324 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

*Ou)7mZ_?&1& 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 diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties deleted file mode 100644 index b4248f6fc..000000000 --- a/gradle/wrapper/gradle-wrapper.properties +++ /dev/null @@ -1,6 +0,0 @@ -#Fri Mar 18 14:13:21 MDT 2022 -distributionBase=GRADLE_USER_HOME -distributionUrl=https\://services.gradle.org/distributions/gradle-8.2-bin.zip -distributionPath=wrapper/dists -zipStorePath=wrapper/dists -zipStoreBase=GRADLE_USER_HOME diff --git a/gradlew b/gradlew deleted file mode 100755 index 27309d923..000000000 --- a/gradlew +++ /dev/null @@ -1,164 +0,0 @@ -#!/usr/bin/env bash - -############################################################################## -## -## Gradle start up script for UN*X -## -############################################################################## - -# Attempt to set APP_HOME -# Resolve links: $0 may be a link -PRG="$0" -# Need this for relative symlinks. -while [ -h "$PRG" ] ; do - ls=`ls -ld "$PRG"` - link=`expr "$ls" : '.*-> \(.*\)$'` - if expr "$link" : '/.*' > /dev/null; then - PRG="$link" - else - PRG=`dirname "$PRG"`"/$link" - fi -done -SAVED="`pwd`" -cd "`dirname \"$PRG\"`/" >/dev/null -APP_HOME="`pwd -P`" -cd "$SAVED" >/dev/null - -APP_NAME="Gradle" -APP_BASE_NAME=`basename "$0"` - -# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -DEFAULT_JVM_OPTS="" - -# Use the maximum available, or set MAX_FD != -1 to use that value. -MAX_FD="maximum" - -warn ( ) { - echo "$*" -} - -die ( ) { - echo - echo "$*" - echo - exit 1 -} - -# OS specific support (must be 'true' or 'false'). -cygwin=false -msys=false -darwin=false -nonstop=false -case "`uname`" in - CYGWIN* ) - cygwin=true - ;; - Darwin* ) - darwin=true - ;; - MINGW* ) - msys=true - ;; - NONSTOP* ) - nonstop=true - ;; -esac - -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar - -# Determine the Java command to use to start the JVM. -if [ -n "$JAVA_HOME" ] ; then - if [ -x "$JAVA_HOME/jre/sh/java" ] ; then - # IBM's JDK on AIX uses strange locations for the executables - JAVACMD="$JAVA_HOME/jre/sh/java" - else - JAVACMD="$JAVA_HOME/bin/java" - fi - if [ ! -x "$JAVACMD" ] ; then - die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME - -Please set the JAVA_HOME variable in your environment to match the -location of your Java installation." - fi -else - JAVACMD="java" - which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. - -Please set the JAVA_HOME variable in your environment to match the -location of your Java installation." -fi - -# Increase the maximum file descriptors if we can. -if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then - MAX_FD_LIMIT=`ulimit -H -n` - if [ $? -eq 0 ] ; then - if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then - MAX_FD="$MAX_FD_LIMIT" - fi - ulimit -n $MAX_FD - if [ $? -ne 0 ] ; then - warn "Could not set maximum file descriptor limit: $MAX_FD" - fi - else - warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" - fi -fi - -# For Darwin, add options to specify how the application appears in the dock -if $darwin; then - GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" -fi - -# For Cygwin, switch paths to Windows format before running java -if $cygwin ; then - APP_HOME=`cygpath --path --mixed "$APP_HOME"` - CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` - JAVACMD=`cygpath --unix "$JAVACMD"` - - # We build the pattern for arguments to be converted via cygpath - ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` - SEP="" - for dir in $ROOTDIRSRAW ; do - ROOTDIRS="$ROOTDIRS$SEP$dir" - SEP="|" - done - OURCYGPATTERN="(^($ROOTDIRS))" - # Add a user-defined pattern to the cygpath arguments - if [ "$GRADLE_CYGPATTERN" != "" ] ; then - OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" - fi - # Now convert the arguments - kludge to limit ourselves to /bin/sh - i=0 - for arg in "$@" ; do - CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` - CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option - - if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition - eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` - else - eval `echo args$i`="\"$arg\"" - fi - i=$((i+1)) - done - case $i in - (0) set -- ;; - (1) set -- "$args0" ;; - (2) set -- "$args0" "$args1" ;; - (3) set -- "$args0" "$args1" "$args2" ;; - (4) set -- "$args0" "$args1" "$args2" "$args3" ;; - (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; - (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; - (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; - (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; - (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; - esac -fi - -# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules -function splitJvmOpts() { - JVM_OPTS=("$@") -} -eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS -JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" - -exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" diff --git a/gradlew.bat b/gradlew.bat deleted file mode 100644 index 832fdb607..000000000 --- a/gradlew.bat +++ /dev/null @@ -1,90 +0,0 @@ -@if "%DEBUG%" == "" @echo off -@rem ########################################################################## -@rem -@rem Gradle startup script for Windows -@rem -@rem ########################################################################## - -@rem Set local scope for the variables with windows NT shell -if "%OS%"=="Windows_NT" setlocal - -set DIRNAME=%~dp0 -if "%DIRNAME%" == "" set DIRNAME=. -set APP_BASE_NAME=%~n0 -set APP_HOME=%DIRNAME% - -@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -set DEFAULT_JVM_OPTS= - -@rem Find java.exe -if defined JAVA_HOME goto findJavaFromJavaHome - -set JAVA_EXE=java.exe -%JAVA_EXE% -version >NUL 2>&1 -if "%ERRORLEVEL%" == "0" goto init - -echo. -echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. - -goto fail - -:findJavaFromJavaHome -set JAVA_HOME=%JAVA_HOME:"=% -set JAVA_EXE=%JAVA_HOME%/bin/java.exe - -if exist "%JAVA_EXE%" goto init - -echo. -echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. - -goto fail - -:init -@rem Get command-line arguments, handling Windows variants - -if not "%OS%" == "Windows_NT" goto win9xME_args -if "%@eval[2+2]" == "4" goto 4NT_args - -:win9xME_args -@rem Slurp the command line arguments. -set CMD_LINE_ARGS= -set _SKIP=2 - -:win9xME_args_slurp -if "x%~1" == "x" goto execute - -set CMD_LINE_ARGS=%* -goto execute - -:4NT_args -@rem Get arguments from the 4NT Shell from JP Software -set CMD_LINE_ARGS=%$ - -:execute -@rem Setup the command line - -set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar - -@rem Execute Gradle -"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% - -:end -@rem End local scope for the variables with windows NT shell -if "%ERRORLEVEL%"=="0" goto mainEnd - -:fail -rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of -rem the _cmd.exe /c_ return code! -if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 -exit /b 1 - -:mainEnd -if "%OS%"=="Windows_NT" endlocal - -:omega diff --git a/settings.gradle b/settings.gradle deleted file mode 100644 index 9bb447a64..000000000 --- a/settings.gradle +++ /dev/null @@ -1 +0,0 @@ -include 'android' \ No newline at end of file From e99a0c192d0fd1a6b8271b893e0b7e13c0d02eef Mon Sep 17 00:00:00 2001 From: Code-Apprentice Date: Tue, 6 Aug 2024 19:33:00 -0600 Subject: [PATCH 002/279] New Kotlin project --- app/.gitignore | 1 + app/build.gradle.kts | 69 +++++++ app/proguard-rules.pro | 21 ++ .../bbct/android/ExampleInstrumentedTest.kt | 24 +++ app/src/main/AndroidManifest.xml | 28 +++ .../main/java/bbct/android/MainActivity.kt | 47 +++++ .../main/java/bbct/android/ui/theme/Color.kt | 11 ++ .../main/java/bbct/android/ui/theme/Theme.kt | 58 ++++++ .../main/java/bbct/android/ui/theme/Type.kt | 34 ++++ .../res/drawable/ic_launcher_background.xml | 170 ++++++++++++++++ .../res/drawable/ic_launcher_foreground.xml | 30 +++ .../res/mipmap-anydpi-v26/ic_launcher.xml | 6 + .../mipmap-anydpi-v26/ic_launcher_round.xml | 6 + app/src/main/res/mipmap-hdpi/ic_launcher.webp | Bin 0 -> 1404 bytes .../res/mipmap-hdpi/ic_launcher_round.webp | Bin 0 -> 2898 bytes app/src/main/res/mipmap-mdpi/ic_launcher.webp | Bin 0 -> 982 bytes .../res/mipmap-mdpi/ic_launcher_round.webp | Bin 0 -> 1772 bytes .../main/res/mipmap-xhdpi/ic_launcher.webp | Bin 0 -> 1900 bytes .../res/mipmap-xhdpi/ic_launcher_round.webp | Bin 0 -> 3918 bytes .../main/res/mipmap-xxhdpi/ic_launcher.webp | Bin 0 -> 2884 bytes .../res/mipmap-xxhdpi/ic_launcher_round.webp | Bin 0 -> 5914 bytes .../main/res/mipmap-xxxhdpi/ic_launcher.webp | Bin 0 -> 3844 bytes .../res/mipmap-xxxhdpi/ic_launcher_round.webp | Bin 0 -> 7778 bytes app/src/main/res/values/colors.xml | 10 + app/src/main/res/values/strings.xml | 3 + app/src/main/res/values/themes.xml | 5 + app/src/main/res/xml/backup_rules.xml | 13 ++ .../main/res/xml/data_extraction_rules.xml | 19 ++ .../test/java/bbct/android/ExampleUnitTest.kt | 17 ++ build.gradle.kts | 5 + gradle.properties | 23 +++ gradle/libs.versions.toml | 31 +++ gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 59203 bytes gradle/wrapper/gradle-wrapper.properties | 6 + gradlew | 185 ++++++++++++++++++ gradlew.bat | 89 +++++++++ settings.gradle.kts | 23 +++ 37 files changed, 934 insertions(+) create mode 100644 app/.gitignore create mode 100644 app/build.gradle.kts create mode 100644 app/proguard-rules.pro create mode 100644 app/src/androidTest/java/bbct/android/ExampleInstrumentedTest.kt create mode 100644 app/src/main/AndroidManifest.xml create mode 100644 app/src/main/java/bbct/android/MainActivity.kt create mode 100644 app/src/main/java/bbct/android/ui/theme/Color.kt create mode 100644 app/src/main/java/bbct/android/ui/theme/Theme.kt create mode 100644 app/src/main/java/bbct/android/ui/theme/Type.kt create mode 100644 app/src/main/res/drawable/ic_launcher_background.xml create mode 100644 app/src/main/res/drawable/ic_launcher_foreground.xml create mode 100644 app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml create mode 100644 app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml create mode 100644 app/src/main/res/mipmap-hdpi/ic_launcher.webp create mode 100644 app/src/main/res/mipmap-hdpi/ic_launcher_round.webp create mode 100644 app/src/main/res/mipmap-mdpi/ic_launcher.webp create mode 100644 app/src/main/res/mipmap-mdpi/ic_launcher_round.webp create mode 100644 app/src/main/res/mipmap-xhdpi/ic_launcher.webp create mode 100644 app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp create mode 100644 app/src/main/res/mipmap-xxhdpi/ic_launcher.webp create mode 100644 app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp create mode 100644 app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp create mode 100644 app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp create mode 100644 app/src/main/res/values/colors.xml create mode 100644 app/src/main/res/values/strings.xml create mode 100644 app/src/main/res/values/themes.xml create mode 100644 app/src/main/res/xml/backup_rules.xml create mode 100644 app/src/main/res/xml/data_extraction_rules.xml create mode 100644 app/src/test/java/bbct/android/ExampleUnitTest.kt create mode 100644 build.gradle.kts create mode 100644 gradle.properties create mode 100644 gradle/libs.versions.toml create mode 100644 gradle/wrapper/gradle-wrapper.jar create mode 100644 gradle/wrapper/gradle-wrapper.properties create mode 100755 gradlew create mode 100644 gradlew.bat create mode 100644 settings.gradle.kts diff --git a/app/.gitignore b/app/.gitignore new file mode 100644 index 000000000..42afabfd2 --- /dev/null +++ b/app/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/app/build.gradle.kts b/app/build.gradle.kts new file mode 100644 index 000000000..fefb9db2e --- /dev/null +++ b/app/build.gradle.kts @@ -0,0 +1,69 @@ +plugins { + alias(libs.plugins.android.application) + alias(libs.plugins.jetbrains.kotlin.android) +} + +android { + namespace = "bbct.android" + compileSdk = 34 + + defaultConfig { + applicationId = "bbct.android" + minSdk = 24 + targetSdk = 34 + versionCode = 1 + versionName = "1.0" + + testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" + vectorDrawables { + useSupportLibrary = true + } + } + + buildTypes { + release { + isMinifyEnabled = false + proguardFiles( + getDefaultProguardFile("proguard-android-optimize.txt"), + "proguard-rules.pro" + ) + } + } + compileOptions { + sourceCompatibility = JavaVersion.VERSION_1_8 + targetCompatibility = JavaVersion.VERSION_1_8 + } + kotlinOptions { + jvmTarget = "1.8" + } + buildFeatures { + compose = true + } + composeOptions { + kotlinCompilerExtensionVersion = "1.5.1" + } + packaging { + resources { + excludes += "/META-INF/{AL2.0,LGPL2.1}" + } + } +} + +dependencies { + + implementation(libs.androidx.core.ktx) + implementation(libs.androidx.lifecycle.runtime.ktx) + implementation(libs.androidx.activity.compose) + implementation(platform(libs.androidx.compose.bom)) + implementation(libs.androidx.ui) + implementation(libs.androidx.ui.graphics) + implementation(libs.androidx.ui.tooling.preview) + implementation(libs.androidx.material3) + testImplementation(libs.junit) + androidTestImplementation(libs.androidx.junit) + androidTestImplementation(libs.androidx.espresso.core) + androidTestImplementation(platform(libs.androidx.compose.bom)) + androidTestImplementation(libs.androidx.ui.test.junit4) + debugImplementation(libs.androidx.ui.tooling) + debugImplementation(libs.androidx.ui.test.manifest) +} \ No newline at end of file diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro new file mode 100644 index 000000000..481bb4348 --- /dev/null +++ b/app/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile \ No newline at end of file diff --git a/app/src/androidTest/java/bbct/android/ExampleInstrumentedTest.kt b/app/src/androidTest/java/bbct/android/ExampleInstrumentedTest.kt new file mode 100644 index 000000000..e523e3f3c --- /dev/null +++ b/app/src/androidTest/java/bbct/android/ExampleInstrumentedTest.kt @@ -0,0 +1,24 @@ +package bbct.android + +import androidx.test.platform.app.InstrumentationRegistry +import androidx.test.ext.junit.runners.AndroidJUnit4 + +import org.junit.Test +import org.junit.runner.RunWith + +import org.junit.Assert.* + +/** + * Instrumented test, which will execute on an Android device. + * + * See [testing documentation](http://d.android.com/tools/testing). + */ +@RunWith(AndroidJUnit4::class) +class ExampleInstrumentedTest { + @Test + fun useAppContext() { + // Context of the app under test. + val appContext = InstrumentationRegistry.getInstrumentation().targetContext + assertEquals("bbct.android", appContext.packageName) + } +} \ No newline at end of file diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml new file mode 100644 index 000000000..663dc733d --- /dev/null +++ b/app/src/main/AndroidManifest.xml @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/java/bbct/android/MainActivity.kt b/app/src/main/java/bbct/android/MainActivity.kt new file mode 100644 index 000000000..73df42381 --- /dev/null +++ b/app/src/main/java/bbct/android/MainActivity.kt @@ -0,0 +1,47 @@ +package bbct.android + +import android.os.Bundle +import androidx.activity.ComponentActivity +import androidx.activity.compose.setContent +import androidx.activity.enableEdgeToEdge +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.padding +import androidx.compose.material3.Scaffold +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.tooling.preview.Preview +import bbct.android.ui.theme.BbctTheme + +class MainActivity : ComponentActivity() { + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + enableEdgeToEdge() + setContent { + BbctTheme { + Scaffold(modifier = Modifier.fillMaxSize()) { innerPadding -> + Greeting( + name = "Android", + modifier = Modifier.padding(innerPadding) + ) + } + } + } + } +} + +@Composable +fun Greeting(name: String, modifier: Modifier = Modifier) { + Text( + text = "Hello $name!", + modifier = modifier + ) +} + +@Preview(showBackground = true) +@Composable +fun GreetingPreview() { + BbctTheme { + Greeting("Android") + } +} \ No newline at end of file diff --git a/app/src/main/java/bbct/android/ui/theme/Color.kt b/app/src/main/java/bbct/android/ui/theme/Color.kt new file mode 100644 index 000000000..7ae536f1f --- /dev/null +++ b/app/src/main/java/bbct/android/ui/theme/Color.kt @@ -0,0 +1,11 @@ +package bbct.android.ui.theme + +import androidx.compose.ui.graphics.Color + +val Purple80 = Color(0xFFD0BCFF) +val PurpleGrey80 = Color(0xFFCCC2DC) +val Pink80 = Color(0xFFEFB8C8) + +val Purple40 = Color(0xFF6650a4) +val PurpleGrey40 = Color(0xFF625b71) +val Pink40 = Color(0xFF7D5260) \ No newline at end of file diff --git a/app/src/main/java/bbct/android/ui/theme/Theme.kt b/app/src/main/java/bbct/android/ui/theme/Theme.kt new file mode 100644 index 000000000..e29226065 --- /dev/null +++ b/app/src/main/java/bbct/android/ui/theme/Theme.kt @@ -0,0 +1,58 @@ +package bbct.android.ui.theme + +import android.app.Activity +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 DarkColorScheme = darkColorScheme( + primary = Purple80, + secondary = PurpleGrey80, + tertiary = Pink80 +) + +private val LightColorScheme = lightColorScheme( + primary = Purple40, + secondary = PurpleGrey40, + tertiary = Pink40 + + /* Other default colors to override + background = Color(0xFFFFFBFE), + surface = Color(0xFFFFFBFE), + onPrimary = Color.White, + onSecondary = Color.White, + onTertiary = Color.White, + onBackground = Color(0xFF1C1B1F), + onSurface = Color(0xFF1C1B1F), + */ +) + +@Composable +fun BbctTheme( + darkTheme: Boolean = isSystemInDarkTheme(), + // Dynamic color is available on Android 12+ + dynamicColor: Boolean = true, + content: @Composable () -> Unit +) { + val colorScheme = when { + dynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> { + val context = LocalContext.current + if (darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context) + } + + darkTheme -> DarkColorScheme + else -> LightColorScheme + } + + MaterialTheme( + colorScheme = colorScheme, + typography = Typography, + content = content + ) +} \ No newline at end of file diff --git a/app/src/main/java/bbct/android/ui/theme/Type.kt b/app/src/main/java/bbct/android/ui/theme/Type.kt new file mode 100644 index 000000000..47a84ed03 --- /dev/null +++ b/app/src/main/java/bbct/android/ui/theme/Type.kt @@ -0,0 +1,34 @@ +package bbct.android.ui.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.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, + letterSpacing = 0.5.sp + ) + /* Other default text styles to override + titleLarge = TextStyle( + fontFamily = FontFamily.Default, + fontWeight = FontWeight.Normal, + fontSize = 22.sp, + lineHeight = 28.sp, + letterSpacing = 0.sp + ), + labelSmall = TextStyle( + fontFamily = FontFamily.Default, + fontWeight = FontWeight.Medium, + fontSize = 11.sp, + lineHeight = 16.sp, + letterSpacing = 0.5.sp + ) + */ +) \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_launcher_background.xml b/app/src/main/res/drawable/ic_launcher_background.xml new file mode 100644 index 000000000..07d5da9cb --- /dev/null +++ b/app/src/main/res/drawable/ic_launcher_background.xml @@ -0,0 +1,170 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/drawable/ic_launcher_foreground.xml b/app/src/main/res/drawable/ic_launcher_foreground.xml new file mode 100644 index 000000000..2b068d114 --- /dev/null +++ b/app/src/main/res/drawable/ic_launcher_foreground.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml new file mode 100644 index 000000000..6f3b755bf --- /dev/null +++ b/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml new file mode 100644 index 000000000..6f3b755bf --- /dev/null +++ b/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher.webp b/app/src/main/res/mipmap-hdpi/ic_launcher.webp new file mode 100644 index 0000000000000000000000000000000000000000..c209e78ecd372343283f4157dcfd918ec5165bb3 GIT binary patch literal 1404 zcmV-?1%vuhNk&F=1pok7MM6+kP&il$0000G0000-002h-06|PpNX!5L00Dqw+t%{r zzW2vH!KF=w&cMnnN@{whkTw+#mAh0SV?YL=)3MimFYCWp#fpdtz~8$hD5VPuQgtcN zXl<@<#Cme5f5yr2h%@8TWh?)bSK`O z^Z@d={gn7J{iyxL_y_%J|L>ep{dUxUP8a{byupH&!UNR*OutO~0{*T4q5R6@ApLF! z5{w?Z150gC7#>(VHFJZ-^6O@PYp{t!jH(_Z*nzTK4 zkc{fLE4Q3|mA2`CWQ3{8;gxGizgM!zccbdQoOLZc8hThi-IhN90RFT|zlxh3Ty&VG z?Fe{#9RrRnxzsu|Lg2ddugg7k%>0JeD+{XZ7>Z~{=|M+sh1MF7~ zz>To~`~LVQe1nNoR-gEzkpe{Ak^7{{ZBk2i_<+`Bq<^GB!RYG+z)h;Y3+<{zlMUYd zrd*W4w&jZ0%kBuDZ1EW&KLpyR7r2=}fF2%0VwHM4pUs}ZI2egi#DRMYZPek*^H9YK zay4Iy3WXFG(F14xYsoDA|KXgGc5%2DhmQ1gFCkrgHBm!lXG8I5h*uf{rn48Z!_@ z4Bk6TJAB2CKYqPjiX&mWoW>OPFGd$wqroa($ne7EUK;#3VYkXaew%Kh^3OrMhtjYN?XEoY`tRPQsAkH-DSL^QqyN0>^ zmC>{#F14jz4GeW{pJoRpLFa_*GI{?T93^rX7SPQgT@LbLqpNA}<@2wH;q493)G=1Y z#-sCiRNX~qf3KgiFzB3I>4Z%AfS(3$`-aMIBU+6?gbgDb!)L~A)je+;fR0jWLL-Fu z4)P{c7{B4Hp91&%??2$v9iRSFnuckHUm}or9seH6 z>%NbT+5*@L5(I9j@06@(!{ZI?U0=pKn8uwIg&L{JV14+8s2hnvbRrU|hZCd}IJu7*;;ECgO%8_*W Kmw_-CKmY()leWbG literal 0 HcmV?d00001 diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp b/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp new file mode 100644 index 0000000000000000000000000000000000000000..b2dfe3d1ba5cf3ee31b3ecc1ced89044a1f3b7a9 GIT binary patch literal 2898 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 literal 0 HcmV?d00001 diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher.webp b/app/src/main/res/mipmap-mdpi/ic_launcher.webp new file mode 100644 index 0000000000000000000000000000000000000000..4f0f1d64e58ba64d180ce43ee13bf9a17835fbca GIT binary patch literal 982 zcmV;{11bDcNk&G_0{{S5MM6+kP&il$0000G0000l001ul06|PpNU8t;00Dqo+t#w^ z^1csucXz7-Qrhzl9HuHB%l>&>1tG2^vb*E&k^T3$FG1eQZ51g$uv4V+kI`0<^1Z@N zk?Jjh$olyC%l>)Xq;7!>{iBj&BjJ`P&$fsCfpve_epJOBkTF?nu-B7D!hO=2ZR}

C%4 zc_9eOXvPbC4kzU8YowIA8cW~Uv|eB&yYwAObSwL2vY~UYI7NXPvf3b+c^?wcs~_t{ ze_m66-0)^{JdOMKPwjpQ@Sna!*?$wTZ~su*tNv7o!gXT!GRgivP}ec?5>l1!7<(rT zds|8x(qGc673zrvYIz;J23FG{9nHMnAuP}NpAED^laz3mAN1sy+NXK)!6v1FxQ;lh zOBLA>$~P3r4b*NcqR;y6pwyhZ3_PiDb|%n1gGjl3ZU}ujInlP{eks-#oA6>rh&g+!f`hv#_%JrgYPu z(U^&XLW^QX7F9Z*SRPpQl{B%x)_AMp^}_v~?j7 zapvHMKxSf*Mtyx8I}-<*UGn3)oHd(nn=)BZ`d$lDBwq_GL($_TPaS{UeevT(AJ`p0 z9%+hQb6z)U9qjbuXjg|dExCLjpS8$VKQ55VsIC%@{N5t{NsW)=hNGI`J=x97_kbz@ E0Of=7!TQj4N+cqN`nQhxvX7dAV-`K|Ub$-q+H-5I?Tx0g9jWxd@A|?POE8`3b8fO$T))xP* z(X?&brZw({`)WU&rdAs1iTa0x6F@PIxJ&&L|dpySV!ID|iUhjCcKz(@mE z!x@~W#3H<)4Ae(4eQJRk`Iz3<1)6^m)0b_4_TRZ+cz#eD3f8V;2r-1fE!F}W zEi0MEkTTx}8i1{`l_6vo0(Vuh0HD$I4SjZ=?^?k82R51bC)2D_{y8mi_?X^=U?2|F{Vr7s!k(AZC$O#ZMyavHhlQ7 zUR~QXuH~#o#>(b$u4?s~HLF*3IcF7023AlwAYudn0FV~|odGH^05AYPEfR)8p`i{n zwg3zPVp{+wOsxKc>)(pMupKF!Y2HoUqQ3|Yu|8lwR=?5zZuhG6J?H`bSNk_wPoM{u zSL{c@pY7+c2kck>`^q1^^gR0QB7Y?KUD{vz-uVX~;V-rW)PDcI)$_UjgVV?S?=oLR zf4}zz{#*R_{LkiJ#0RdQLNC^2Vp%JPEUvG9ra2BVZ92(p9h7Ka@!yf9(lj#}>+|u* z;^_?KWdzkM`6gqPo9;;r6&JEa)}R3X{(CWv?NvgLeOTq$cZXqf7|sPImi-7cS8DCN zGf;DVt3Am`>hH3{4-WzH43Ftx)SofNe^-#|0HdCo<+8Qs!}TZP{HH8~z5n`ExcHuT zDL1m&|DVpIy=xsLO>8k92HcmfSKhflQ0H~9=^-{#!I1g(;+44xw~=* zxvNz35vfsQE)@)Zsp*6_GjYD};Squ83<_?^SbALb{a`j<0Gn%6JY!zhp=Fg}Ga2|8 z52e1WU%^L1}15Ex0fF$e@eCT(()_P zvV?CA%#Sy08_U6VPt4EtmVQraWJX` zh=N|WQ>LgrvF~R&qOfB$!%D3cGv?;Xh_z$z7k&s4N)$WYf*k=|*jCEkO19{h_(%W4 zPuOqbCw`SeAX*R}UUsbVsgtuG?xs(#Ikx9`JZoQFz0n*7ZG@Fv@kZk`gzO$HoA9kN z8U5{-yY zvV{`&WKU2$mZeoBmiJrEdzUZAv1sRxpePdg1)F*X^Y)zp^Y*R;;z~vOv-z&)&G)JQ{m!C9cmziu1^nHA z`#`0c>@PnQ9CJKgC5NjJD8HM3|KC(g5nnCq$n0Gsu_DXk36@ql%npEye|?%RmG)

FJ$wK}0tWNB{uH;AM~i literal 0 HcmV?d00001 diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher.webp b/app/src/main/res/mipmap-xhdpi/ic_launcher.webp new file mode 100644 index 0000000000000000000000000000000000000000..948a3070fe34c611c42c0d3ad3013a0dce358be0 GIT binary patch literal 1900 zcmV-y2b1_xNk&Fw2LJ$9MM6+kP&il$0000G0001A003VA06|PpNH75a00DqwTbm-~ zullQTcXxO9ki!OCRx^i?oR|n!<8G0=kI^!JSjFi-LL*`V;ET0H2IXfU0*i>o6o6Gy zRq6Ap5(_{XLdXcL-MzlN`ugSdZY_`jXhcENAu)N_0?GhF))9R;E`!bo9p?g?SRgw_ zEXHhFG$0{qYOqhdX<(wE4N@es3VIo$%il%6xP9gjiBri+2pI6aY4 zJbgh-Ud|V%3O!IcHKQx1FQH(_*TK;1>FQWbt^$K1zNn^cczkBs=QHCYZ8b&l!UV{K z{L0$KCf_&KR^}&2Fe|L&?1I7~pBENnCtCuH3sjcx6$c zwqkNkru);ie``q+_QI;IYLD9OV0ZxkuyBz|5<$1BH|vtey$> z5oto4=l-R-Aaq`Dk0}o9N0VrkqW_#;!u{!bJLDq%0092{Ghe=F;(kn} z+sQ@1=UlX30+2nWjkL$B^b!H2^QYO@iFc0{(-~yXj2TWz?VG{v`Jg zg}WyYnwGgn>{HFaG7E~pt=)sOO}*yd(UU-D(E&x{xKEl6OcU?pl)K%#U$dn1mDF19 zSw@l8G!GNFB3c3VVK0?uyqN&utT-D5%NM4g-3@Sii9tSXKtwce~uF zS&Jn746EW^wV~8zdQ1XC28~kXu8+Yo9p!<8h&(Q({J*4DBglPdpe4M_mD8AguZFn~ ztiuO~{6Bx?SfO~_ZV(GIboeR9~hAym{{fV|VM=77MxDrbW6`ujX z<3HF(>Zr;#*uCvC*bpoSr~C$h?_%nXps@A)=l_;({Fo#6Y1+Zv`!T5HB+)#^-Ud_; zBwftPN=d8Vx)*O1Mj+0oO=mZ+NVH*ptNDC-&zZ7Hwho6UQ#l-yNvc0Cm+2$$6YUk2D2t#vdZX-u3>-Be1u9gtTBiMB^xwWQ_rgvGpZ6(C@e23c!^K=>ai-Rqu zhqT`ZQof;9Bu!AD(i^PCbYV%yha9zuoKMp`U^z;3!+&d@Hud&_iy!O-$b9ZLcSRh? z)R|826w}TU!J#X6P%@Zh=La$I6zXa#h!B;{qfug}O%z@K{EZECu6zl)7CiNi%xti0 zB{OKfAj83~iJvmpTU|&q1^?^cIMn2RQ?jeSB95l}{DrEPTW{_gmU_pqTc)h@4T>~& zluq3)GM=xa(#^VU5}@FNqpc$?#SbVsX!~RH*5p0p@w z;~v{QMX0^bFT1!cXGM8K9FP+=9~-d~#TK#ZE{4umGT=;dfvWi?rYj;^l_Zxywze`W z^Cr{55U@*BalS}K%Czii_80e0#0#Zkhlij4-~I@}`-JFJ7$5{>LnoJSs??J8kWVl6|8A}RCGAu9^rAsfCE=2}tHwl93t0C?#+jMpvr7O3`2=tr{Hg$=HlnjVG^ewm|Js0J*kfPa6*GhtB>`fN!m#9J(sU!?(OSfzY*zS(FJ<-Vb zfAIg+`U)YaXv#sY(c--|X zEB+TVyZ%Ie4L$gi#Fc++`h6%vzsS$pjz9aLt+ZL(g;n$Dzy5=m=_TV(3H8^C{r0xd zp#a%}ht55dOq?yhwYPrtp-m1xXp;4X;)NhxxUpgP%XTLmO zcjaFva^}dP3$&sfFTIR_jC=2pHh9kpI@2(6V*GQo7Ws)`j)hd+tr@P~gR*2gO@+1? zG<`_tB+LJuF|SZ9tIec;h%}}6WClT`L>HSW?E{Hp1h^+mlbf_$9zA>!ug>NALJsO{ mU%z=YwVD?}XMya)Bp;vlyE5&E_6!fzx9pwrdz474!~g(M6R?N? literal 0 HcmV?d00001 diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp b/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp new file mode 100644 index 0000000000000000000000000000000000000000..1b9a6956b3acdc11f40ce2bb3f6efbd845cc243f GIT binary patch literal 3918 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�(p-{x$&=IACIfoAR!za)F-^da-t3#0Dycnp zwO~NVXwXCl;jE<}>%@xz|=8fIJAB?>+E{7)|4l${4ngA3G|=r z2Dyv;VVWSgZx9Wj>qUjleGl3Ei9K4>h!(lPS%8VOG>Xu0%6VDz^O=bjJmuP7>DeUv zrbI}MlHB^^d?{zv6d=@_ZD2lg1&G7UjnVN{1}9WkaM3H~btX0GtSzB+tZ^qRgWo4m z!GmimlG$=wgXCnr6j@m<1gAL46#T~5Bnm=2{^@>|t&`9mkEPddj zAvG~@Tv~TAm2i%VW}R-g(Z0)z-Y|szHr@rk>4MAyG*Ma*7Yh#H7(!-5>DZ@8r;_dx z{prSe<>~099F8vsYd2xff7uAS%7{S)f(|@me3t2$iy&NEc7OUEchp@9A|X;;IA>8!oX+y(BKJ$EzV* znR$z;!L$s7uy@{OT~nG#B!NRraT8(X##Ho!0r_o@gg0CA-9H^;-uE&?$2$nHv_00o z%cbuUc-tCx$Uh&EZ4Nf4Zgqv)Y6>usG3>GeQnxx_Z6+PcbX-+ysbt1hQ`K1LDpOE? zrAhIZhSN9yVIAOa22gn577tbc&i3|3V8NWy&!tw##`}9*x}gtI^h1DzZRA>UuaJG) zaZ7j)dq!O}{?#8Y7~7i6fHh4{`pL?>-18|p!S75Y#^DM>-S3)vuZG+Q7l@ek zQP~#cBpWgg#mApc_sPYjpw8odQuRokmTkzcNl`^CcKB7e&;zViV;{Y{o^Y$%7i0m# z62%#1Lq!RC?}lK>%mp}T!3Xv;L*0v*>USLm``N%>w>@fwC+#T&Tx2bN4w(20JB}oU zuSa6v^kXi0xPs?pbaOHnyiqq6By1EZY9OZ^^QA>{q-Hsd&m`pbQ%8121aWG-F5xf zlZ%;B{;C>X19|`^_?dVyCq>n+41w7|!tUS!{9rHlbhX=SZO5CQ^;!Du_E7*`GiR^Q w)2!4MKjfSAeNo!9>IaV6aUZ*?W>} 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

UmY2RIYtNPVDpE$%vda+HD#3m z&VuXJ{BK&Qe+rBa7eq}Q(bq|tn(RrJAk|ztj2(i{d>nmQnM?;HF2k&9sA6up5tmjl z7lySlzMbifH17-m-Lwa_F&e7nOH?ESi3#ckR3tsM+jsck3`oG!uMS}|eAwVXv>}qxwq?QY%QJ0}r@^;fhuUA9W z*BVl>TGo&N004@xSiwDUXUvp51sVmqO3m)=B55aPwf@0=e}cN+$-BdKxY`YrT_4)0 z_d10#i44Q*rFr8MC>*)v$EJvz``(pb{e&*6k+b zsMz%($|1+8hn8c2?P(l@;Rb&CsZeYoCI3?2!LqjbwPXW3z4G$Qfj=cT5Yb%vY0(AX oeb?AaKtwrnc|$|zzw9vfvn^aJJ!zd)XFXqqy0000001=f@-~a#s literal 0 HcmV?d00001 diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml new file mode 100644 index 000000000..f8c6127d3 --- /dev/null +++ b/app/src/main/res/values/colors.xml @@ -0,0 +1,10 @@ + + + #FFBB86FC + #FF6200EE + #FF3700B3 + #FF03DAC5 + #FF018786 + #FF000000 + #FFFFFFFF + \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml new file mode 100644 index 000000000..f00ac35a2 --- /dev/null +++ b/app/src/main/res/values/strings.xml @@ -0,0 +1,3 @@ + + bbct + \ No newline at end of file diff --git a/app/src/main/res/values/themes.xml b/app/src/main/res/values/themes.xml new file mode 100644 index 000000000..77d7f5e76 --- /dev/null +++ b/app/src/main/res/values/themes.xml @@ -0,0 +1,5 @@ + + + + - - #5677fc - #ff3367d6 - #ff69f0ae - diff --git a/android/src/premium/AndroidManifest.xml b/android/src/premium/AndroidManifest.xml deleted file mode 100644 index 791dc293b..000000000 --- a/android/src/premium/AndroidManifest.xml +++ /dev/null @@ -1,60 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/android/src/premium/java/bbct/android/premium/provider/PremiumProvider.java b/android/src/premium/java/bbct/android/premium/provider/PremiumProvider.java deleted file mode 100644 index 2c390c574..000000000 --- a/android/src/premium/java/bbct/android/premium/provider/PremiumProvider.java +++ /dev/null @@ -1,24 +0,0 @@ -package bbct.android.premium.provider; - -import android.content.Context; -import bbct.android.common.provider.BaseballCardContract; -import bbct.android.common.provider.BaseballCardProvider; -import bbct.android.common.provider.BaseballCardSQLHelper; - -public class PremiumProvider extends BaseballCardProvider { - - static { - uriMatcher.addURI(BaseballCardContract.PREMIUM_AUTHORITY, - BaseballCardContract.TABLE_NAME, ALL_CARDS); - uriMatcher.addURI(BaseballCardContract.PREMIUM_AUTHORITY, - BaseballCardContract.TABLE_NAME + "/#", CARD_ID); - uriMatcher.addURI(BaseballCardContract.PREMIUM_AUTHORITY, - BaseballCardContract.TABLE_NAME + "/distinct", DISTINCT); - } - - @Override - protected BaseballCardSQLHelper getSQLHelper(Context context) { - return new PremiumSQLHelper(context); - } - -} diff --git a/android/src/premium/java/bbct/android/premium/provider/PremiumSQLHelper.java b/android/src/premium/java/bbct/android/premium/provider/PremiumSQLHelper.java deleted file mode 100644 index 4d3dc478e..000000000 --- a/android/src/premium/java/bbct/android/premium/provider/PremiumSQLHelper.java +++ /dev/null @@ -1,113 +0,0 @@ -/* - * This file is part of BBCT for Android. - * - * Copyright 2012-14 codeguru - * - * BBCT for Android 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. - * - * BBCT for Android 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 bbct.android.premium.provider; - -import android.content.ContentResolver; -import android.content.Context; -import android.content.pm.PackageInfo; -import android.content.pm.PackageManager; -import android.content.pm.PackageManager.NameNotFoundException; -import android.database.Cursor; -import android.database.SQLException; -import android.database.sqlite.SQLiteDatabase; -import android.util.Log; - -import java.util.List; - -import bbct.android.common.R; -import bbct.android.common.database.BaseballCard; -import bbct.android.common.provider.BaseballCardContract; - -/** - * Overrides - * {@link bbct.android.common.provider.BaseballCardSQLHelper#onConfigure(SQLiteDatabase)} - * in order to import data from the Lite edition. - */ -public class PremiumSQLHelper extends - bbct.android.common.provider.BaseballCardSQLHelper { - private static final String TAG = PremiumSQLHelper.class.getName(); - private static final String LITE_PACKAGE = "bbct.android"; - private static final int MIN_LITE_VERSION = 3; - - private final Context context; - - public PremiumSQLHelper(Context context) { - super(context); - - Log.d(TAG, "ctor"); - - this.context = context; - } - - @Override - public void onCreate(SQLiteDatabase db) { - super.onCreate(db); - - Log.d(TAG, "onCreate()"); - - if (this.isLiteInstalled()) { - ContentResolver resolver = this.context.getContentResolver(); - Cursor results = resolver.query(BaseballCardContract.LITE_URI, - BaseballCardContract.PROJECTION, null, null, null); - - if (results != null) { - List cards = BaseballCardContract - .getAllBaseballCardsFromCursor(results); - this.insertAllBaseballCards(db, cards); - } else { - String errorMessage = this.context - .getString(R.string.import_error); - throw new SQLException(errorMessage); - } - } - - } - - private boolean isLiteInstalled() { - try { - PackageInfo liteInfo = this.context - .getPackageManager() - .getPackageInfo(LITE_PACKAGE, PackageManager.GET_ACTIVITIES); - if (liteInfo.versionCode < MIN_LITE_VERSION) { - String errorMessage = this.context - .getString(R.string.lite_update_message); - throw new SQLException(errorMessage); - } - - return true; - } catch (NameNotFoundException ex) { - Log.i(TAG, LITE_PACKAGE + " package not found", ex); - return false; - } - } - - private void insertAllBaseballCards(SQLiteDatabase db, - List cards) { - db.beginTransaction(); - try { - for (BaseballCard card : cards) { - db.insert(BaseballCardContract.TABLE_NAME, null, - BaseballCardContract.getContentValues(card)); - } - db.setTransactionSuccessful(); - } finally { - db.endTransaction(); - } - } -} diff --git a/android/src/premium/res/values/no_translate.xml b/android/src/premium/res/values/no_translate.xml deleted file mode 100644 index 3733e1c37..000000000 --- a/android/src/premium/res/values/no_translate.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - BBCT Premium - BBCT Premium - %1$s - - diff --git a/android/src/premium/res/values/strings.xml b/android/src/premium/res/values/strings.xml deleted file mode 100644 index cca282799..000000000 --- a/android/src/premium/res/values/strings.xml +++ /dev/null @@ -1,33 +0,0 @@ - - - - - Importing data from Lite Edition. - Cannot import data from Lite Edition. %1$s - Please update Lite Edition. - Lite Edition not installed. - - BBCT Premium Data - BBCT Read - Baseball Card data from BBCT Premium database - BBCT Write - Write Baseball Card data to BBCT Premium database - - diff --git a/android/src/test/java/bbct/android/common/activity/test/DetailsLayoutTest.java b/android/src/test/java/bbct/android/common/activity/test/DetailsLayoutTest.java deleted file mode 100644 index 672805ea6..000000000 --- a/android/src/test/java/bbct/android/common/activity/test/DetailsLayoutTest.java +++ /dev/null @@ -1,22 +0,0 @@ -/* - * This file is part of BBCT for Android. - * - * Copyright 2016 codeguru - * - * BBCT for Android 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. - * - * BBCT for Android 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 bbct.android.common.activity.test; - -public class DetailsLayoutTest { -} From 2cc83e62f4f0d3ff81270c3b134f0998396406cf Mon Sep 17 00:00:00 2001 From: Code-Apprentice Date: Mon, 23 Dec 2024 13:31:22 -0700 Subject: [PATCH 260/279] Update versionCode and versionName --- app/build.gradle.kts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 9de558931..969069907 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -16,8 +16,8 @@ android { applicationId = "bbct.android" minSdk = 24 targetSdk = 35 - versionCode = 25 - versionName = "1.1.1" + versionCode = 26 + versionName = "2025.01" testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" vectorDrawables { From b982dfd7f41da4d93a675b799eea28fb78a6e2c4 Mon Sep 17 00:00:00 2001 From: Code-Apprentice Date: Mon, 23 Dec 2024 13:43:37 -0700 Subject: [PATCH 261/279] Update dependencies --- gradle/libs.versions.toml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 5bc424af0..f803d5375 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,5 +1,5 @@ [versions] -agp = "8.7.2" +agp = "8.7.3" kotlin = "2.0.10" coreKtx = "1.15.0" junit = "4.13.2" @@ -9,14 +9,14 @@ kotlinxCoroutinesCore = "1.9.0-RC.2" kotlinxSerializationJson = "1.7.3" lifecycle = "2.8.7" activityCompose = "1.9.3" -composeBom = "2024.11.00" -navigationCompose = "2.8.4" -firebaseCrashlytics = "19.2.1" +composeBom = "2024.12.01" +navigationCompose = "2.8.5" +firebaseCrashlytics = "19.3.0" googleGmsGoogleServices = "4.4.2" googleFirebaseCrashlytics = "3.0.2" playServicesAds = "23.6.0" roomRuntime = "2.6.1" -uiTestJunit4Android = "1.7.5" +uiTestJunit4Android = "1.7.6" googleTruth = "1.4.4" [libraries] From 9fd9074cbe22cd8337f77ce810b660b6c82797c2 Mon Sep 17 00:00:00 2001 From: Code-Apprentice Date: Mon, 23 Dec 2024 13:53:59 -0700 Subject: [PATCH 262/279] Success and failure snackbars --- .../ui/details/BaseballCardDetailsScreen.kt | 25 +++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/bbct/android/ui/details/BaseballCardDetailsScreen.kt b/app/src/main/java/bbct/android/ui/details/BaseballCardDetailsScreen.kt index 5d4350c67..badfc0069 100644 --- a/app/src/main/java/bbct/android/ui/details/BaseballCardDetailsScreen.kt +++ b/app/src/main/java/bbct/android/ui/details/BaseballCardDetailsScreen.kt @@ -16,12 +16,15 @@ import androidx.compose.material3.Checkbox import androidx.compose.material3.FloatingActionButton import androidx.compose.material3.Icon import androidx.compose.material3.Scaffold +import androidx.compose.material3.SnackbarHost +import androidx.compose.material3.SnackbarHostState import androidx.compose.material3.Text import androidx.compose.material3.TextField import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.MutableState import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier @@ -48,8 +51,13 @@ fun BaseballCardCreateScreen( db: BaseballCardDatabase, ) { val viewModel: BaseballCardDetailsViewModel = viewModel() + val scope = rememberCoroutineScope() + val snackbarHostState = remember { SnackbarHostState() } Scaffold( + snackbarHost = { + SnackbarHost(hostState = snackbarHostState) + }, topBar = { TopBar( navigationIcon = { BackButton(navController = navController) }, @@ -70,6 +78,8 @@ fun BaseballCardCreateScreen( viewModel.baseballCardState, viewModel.errors, viewModel::validate, + onSuccess = { scope.launch { snackbarHostState.showSnackbar("Card created") } }, + onFailure = { scope.launch { snackbarHostState.showSnackbar("Failed to create card") } }, ) }, modifier = Modifier @@ -288,6 +298,8 @@ fun CreateCardButton( state: MutableState, errors: MutableState, onValidate: () -> Unit, + onSuccess: () -> Unit, + onFailure: () -> Unit, ) { val scope = rememberCoroutineScope() FloatingActionButton(onClick = { @@ -296,7 +308,9 @@ fun CreateCardButton( scope.launch { createCard( db, - state.value + state.value, + onSuccess, + onFailure, ) state.value = BaseballCardState() } @@ -312,8 +326,15 @@ fun CreateCardButton( suspend fun createCard( db: BaseballCardDatabase, cardState: BaseballCardState, + onSuccess: () -> Unit, + onFailure: () -> Unit, ) { - db.baseballCardDao.insertBaseballCard(cardState.toBaseballCard()) + try { + db.baseballCardDao.insertBaseballCard(cardState.toBaseballCard()) + onSuccess() + } catch (e: Exception) { + onFailure() + } } @Composable From f5b7d4c269ce12253c4c383943a31ef3d6c13cf2 Mon Sep 17 00:00:00 2001 From: Code-Apprentice Date: Mon, 23 Dec 2024 13:59:59 -0700 Subject: [PATCH 263/279] Simplify code in ViewModel --- .../java/bbct/android/ui/list/BaseballCardListViewModel.kt | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/bbct/android/ui/list/BaseballCardListViewModel.kt b/app/src/main/java/bbct/android/ui/list/BaseballCardListViewModel.kt index a8510ba9a..18e043388 100644 --- a/app/src/main/java/bbct/android/ui/list/BaseballCardListViewModel.kt +++ b/app/src/main/java/bbct/android/ui/list/BaseballCardListViewModel.kt @@ -16,13 +16,12 @@ import kotlinx.coroutines.launch class BaseballCardListViewModel(val baseballCardDao: BaseballCardDao) : ViewModel() { val filterState = MutableStateFlow(BaseballCardFilterState()) val isFiltered = mutableStateOf(false) - private val _baseballCards = MutableStateFlow>(emptyList()) - val baseballCards: Flow> = _baseballCards + val baseballCards = MutableStateFlow>(emptyList()) init { viewModelScope.launch { filterState.collect { filter -> - _baseballCards.value = getBaseballCards( + baseballCards.value = getBaseballCards( filter.brand, filter.year, filter.number, From f2d9c7d70bb181fc07f42099715bb00c05202ebe Mon Sep 17 00:00:00 2001 From: Code-Apprentice Date: Mon, 23 Dec 2024 14:13:33 -0700 Subject: [PATCH 264/279] Room Gradle Plugin --- app/build.gradle.kts | 4 ++++ build.gradle.kts | 2 ++ 2 files changed, 6 insertions(+) diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 969069907..04a4440e3 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -6,6 +6,7 @@ plugins { alias(libs.plugins.compose.compiler) alias(libs.plugins.google.devtools.ksp) alias(libs.plugins.kotlin.plugin.serialization) + id("androidx.room") } android { @@ -64,6 +65,9 @@ android { applicationId = "bbct.android.premium" } } + room { + schemaDirectory("$projectDir/schemas") + } } dependencies { diff --git a/build.gradle.kts b/build.gradle.kts index aa0036a10..5c23aa607 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -4,4 +4,6 @@ plugins { alias(libs.plugins.jetbrains.kotlin.android) apply false alias(libs.plugins.google.gms.google.services) apply false alias(libs.plugins.google.firebase.crashlytics) apply false + alias(libs.plugins.google.devtools.ksp) apply false + id("androidx.room") version "2.6.1" apply false } From 8185841850fdf01a2476997478a29ec0de265027 Mon Sep 17 00:00:00 2001 From: Code-Apprentice Date: Mon, 23 Dec 2024 20:03:23 -0700 Subject: [PATCH 265/279] Use runTest --- .../ui/details/BaseballCardEditTest.kt | 43 +++++++++++-------- 1 file changed, 26 insertions(+), 17 deletions(-) diff --git a/app/src/androidTest/java/bbct/android/ui/details/BaseballCardEditTest.kt b/app/src/androidTest/java/bbct/android/ui/details/BaseballCardEditTest.kt index 9563ffeeb..e53ea707b 100644 --- a/app/src/androidTest/java/bbct/android/ui/details/BaseballCardEditTest.kt +++ b/app/src/androidTest/java/bbct/android/ui/details/BaseballCardEditTest.kt @@ -7,34 +7,43 @@ import bbct.android.data.BaseballCardDatabase import bbct.android.data.cards import bbct.android.test.TestBase import com.google.common.truth.Truth.assertThat -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.single import kotlinx.coroutines.launch +import kotlinx.coroutines.test.runTest import org.junit.Test class BaseballCardEditTest : TestBase() { @Test fun testBaseballCardEdit() { - val card = cards[0] - val editCard = cards[2] + runTest { + val card = cards[0] + val editCard = cards[2] - val context = InstrumentationRegistry.getInstrumentation().targetContext - val db = inMemoryDatabaseBuilder(context, BaseballCardDatabase::class.java).build() - CoroutineScope(Dispatchers.IO).launch { - db.baseballCardDao.insertBaseballCard(card) - } + val context = InstrumentationRegistry.getInstrumentation().targetContext + val db = inMemoryDatabaseBuilder( + context, + BaseballCardDatabase::class.java + ).build() - composeTestRule.setContent { - val navController = rememberNavController() - BaseballCardEditScreen(navController = navController, db = db, cardId = card._id!!) - } + launch { + db.baseballCardDao.insertBaseballCard(card) + } + + composeTestRule.setContent { + val navController = rememberNavController() + BaseballCardEditScreen( + navController = navController, + db = db, + cardId = card._id!! + ) + } - enterCardData(editCard) + enterCardData(editCard) - CoroutineScope(Dispatchers.IO).launch { - val savedCard = db.baseballCardDao.baseballCards.single() - assertThat(savedCard).isEqualTo(listOf(editCard)) + launch { + val savedCard = db.baseballCardDao.baseballCards.single() + assertThat(savedCard).isEqualTo(listOf(editCard)) + } } } } From bafa53f5d6f54dcfb7fd7e3d263dba530cec7a34 Mon Sep 17 00:00:00 2001 From: Code-Apprentice Date: Mon, 23 Dec 2024 20:07:18 -0700 Subject: [PATCH 266/279] Upgrade kotlin version --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index f803d5375..9b34d914f 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,6 +1,6 @@ [versions] agp = "8.7.3" -kotlin = "2.0.10" +kotlin = "2.0.21" coreKtx = "1.15.0" junit = "4.13.2" junitVersion = "1.2.1" From c08f6d6a20fdff17d27967f0846deb4f9eb3707a Mon Sep 17 00:00:00 2001 From: Code-Apprentice Date: Mon, 23 Dec 2024 21:44:47 -0700 Subject: [PATCH 267/279] Preserve nullability in BaseballCard model to match Java implementation --- .../java/bbct/android/data/BaseballCard.kt | 20 ++++++------- .../ui/details/BaseballCardDetailsScreen.kt | 20 ++++++------- .../details/BaseballCardDetailsViewModel.kt | 24 ++++++++-------- .../android/ui/details/BaseballCardState.kt | 28 +++++++++---------- .../android/ui/list/BaseballCardListScreen.kt | 6 ++-- 5 files changed, 49 insertions(+), 49 deletions(-) diff --git a/app/src/main/java/bbct/android/data/BaseballCard.kt b/app/src/main/java/bbct/android/data/BaseballCard.kt index 89f00e908..8899faca7 100644 --- a/app/src/main/java/bbct/android/data/BaseballCard.kt +++ b/app/src/main/java/bbct/android/data/BaseballCard.kt @@ -8,17 +8,17 @@ import androidx.room.PrimaryKey data class BaseballCard( @PrimaryKey val _id: Long? = null, - val autographed: Boolean, - val condition: String, - val brand: String, - val year: Int, - val number: String, - val value: Int, + val autographed: Boolean?, + val condition: String?, + val brand: String?, + val year: Int?, + val number: String?, + val value: Int?, @ColumnInfo(name = "card_count") - val quantity: Int, + val quantity: Int?, @ColumnInfo(name = "player_name") - val playerName: String, - val team: String, + val playerName: String?, + val team: String?, @ColumnInfo(name = "player_position") - val position: String, + val position: String?, ) diff --git a/app/src/main/java/bbct/android/ui/details/BaseballCardDetailsScreen.kt b/app/src/main/java/bbct/android/ui/details/BaseballCardDetailsScreen.kt index badfc0069..d330b3f24 100644 --- a/app/src/main/java/bbct/android/ui/details/BaseballCardDetailsScreen.kt +++ b/app/src/main/java/bbct/android/ui/details/BaseballCardDetailsScreen.kt @@ -173,7 +173,7 @@ fun BaseballCardDetails( ) { Row(verticalAlignment = Alignment.CenterVertically) { Checkbox( - checked = state.value.autographed, + checked = state.value.autographed == true, onCheckedChange = { state.value = state.value.copy(autographed = it) }, ) Text(text = stringResource(id = R.string.autographed)) @@ -181,7 +181,7 @@ fun BaseballCardDetails( Select( label = { Text(stringResource(id = R.string.condition)) }, options = conditions, - selected = state.value.condition, + selected = state.value.condition ?: "", onSelectedChange = { state.value = state.value.copy(condition = it) onValidate() @@ -192,7 +192,7 @@ fun BaseballCardDetails( AutoComplete( label = { Text(text = stringResource(id = R.string.brand)) }, options = brands.value, - value = state.value.brand, + value = state.value.brand ?: "", onValueChange = { state.value = state.value.copy(brand = it) onValidate() @@ -203,7 +203,7 @@ fun BaseballCardDetails( ) TextField( label = { Text(text = stringResource(id = R.string.year)) }, - value = state.value.year, + value = state.value.year ?: "", onValueChange = { state.value = state.value.copy(year = it) onValidate() @@ -217,7 +217,7 @@ fun BaseballCardDetails( ) TextField( label = { Text(text = stringResource(id = R.string.number)) }, - value = state.value.number, + value = state.value.number ?: "", onValueChange = { state.value = state.value.copy(number = it) onValidate() @@ -228,7 +228,7 @@ fun BaseballCardDetails( ) TextField( label = { Text(text = stringResource(id = R.string.value)) }, - value = state.value.value, + value = state.value.value ?: "", onValueChange = { state.value = state.value.copy(value = it) onValidate() @@ -242,7 +242,7 @@ fun BaseballCardDetails( ) TextField( label = { Text(text = stringResource(id = R.string.quantity)) }, - value = state.value.quantity, + value = state.value.quantity ?: "", onValueChange = { state.value = state.value.copy(quantity = it) onValidate() @@ -257,7 +257,7 @@ fun BaseballCardDetails( AutoComplete( label = { Text(text = stringResource(id = R.string.player_name)) }, options = playerNames.value, - value = state.value.playerName, + value = state.value.playerName ?: "", onValueChange = { state.value = state.value.copy(playerName = it) onValidate() @@ -269,7 +269,7 @@ fun BaseballCardDetails( AutoComplete( label = { Text(text = stringResource(id = R.string.team)) }, options = teams.value, - value = state.value.team, + value = state.value.team ?: "", onValueChange = { state.value = state.value.copy(team = it) onValidate() @@ -281,7 +281,7 @@ fun BaseballCardDetails( Select( label = { Text(stringResource(id = R.string.player_position)) }, options = positions, - selected = state.value.position, + selected = state.value.position ?: "", onSelectedChange = { state.value = state.value.copy(position = it) onValidate() diff --git a/app/src/main/java/bbct/android/ui/details/BaseballCardDetailsViewModel.kt b/app/src/main/java/bbct/android/ui/details/BaseballCardDetailsViewModel.kt index 6255b5414..e84df39f5 100644 --- a/app/src/main/java/bbct/android/ui/details/BaseballCardDetailsViewModel.kt +++ b/app/src/main/java/bbct/android/ui/details/BaseballCardDetailsViewModel.kt @@ -30,62 +30,62 @@ class BaseballCardDetailsViewModel : ViewModel() { val baseballCard = baseballCardState.value val errors = BaseballCardDetailsErrors() - if (baseballCard.condition.isBlank()) { + if (baseballCard.condition.isNullOrBlank()) { errors.condition = FormFieldError("Condition is required") errors.condition.isValid = false } - if (baseballCard.brand.isBlank()) { + if (baseballCard.brand.isNullOrBlank()) { errors.brand = FormFieldError("Brand is required") errors.brand.isValid = false } - if (baseballCard.year.isBlank()) { + if (baseballCard.year.isNullOrBlank()) { errors.year = FormFieldError("Year is required") errors.year.isValid = false } - if (baseballCard.year.toIntOrNull() == null) { + if (baseballCard.year?.toIntOrNull() == null) { errors.year = FormFieldError("Year must be a number") errors.year.isValid = false } - if (baseballCard.number.isBlank()) { + if (baseballCard.number.isNullOrBlank()) { errors.number = FormFieldError("Number is required") errors.number.isValid = false } - if (baseballCard.value.isBlank()) { + if (baseballCard.value.isNullOrBlank()) { errors.value = FormFieldError("Value is required") errors.value.isValid = false } - if (baseballCard.value.toDoubleOrNull() == null) { + if (baseballCard.value?.toDoubleOrNull() == null) { errors.value = FormFieldError("Value must be a number") errors.value.isValid = false } - if (baseballCard.quantity.isBlank()) { + if (baseballCard.quantity.isNullOrBlank()) { errors.quantity = FormFieldError("Quantity is required") errors.quantity.isValid = false } - if (baseballCard.quantity.toIntOrNull() == null) { + if (baseballCard.quantity?.toIntOrNull() == null) { errors.quantity = FormFieldError("Quantity must be a number") errors.quantity.isValid = false } - if (baseballCard.playerName.isBlank()) { + if (baseballCard.playerName.isNullOrBlank()) { errors.playerName = FormFieldError("Player name is required") errors.playerName.isValid = false } - if (baseballCard.team.isBlank()) { + if (baseballCard.team.isNullOrBlank()) { errors.team = FormFieldError("Team is required") errors.team.isValid = false } - if (baseballCard.position.isBlank()) { + if (baseballCard.position.isNullOrBlank()) { errors.position = FormFieldError("Position is required") errors.position.isValid = false } diff --git a/app/src/main/java/bbct/android/ui/details/BaseballCardState.kt b/app/src/main/java/bbct/android/ui/details/BaseballCardState.kt index e1b775810..9f9e74d16 100644 --- a/app/src/main/java/bbct/android/ui/details/BaseballCardState.kt +++ b/app/src/main/java/bbct/android/ui/details/BaseballCardState.kt @@ -4,16 +4,16 @@ import bbct.android.data.BaseballCard data class BaseballCardState( var id: Long? = null, - var autographed: Boolean = false, - var condition: String = "", - var brand: String = "", - var year: String = "", - var number: String = "", - var value: String = "", - var quantity: String = "", - var playerName: String = "", - var team: String = "", - var position: String = "", + var autographed: Boolean? = false, + var condition: String? = null, + var brand: String? = null, + var year: String? = null, + var number: String? = null, + var value: String? = null, + var quantity: String? = null, + var playerName: String? = null, + var team: String? = null, + var position: String? = null, ) { constructor(card: BaseballCard) : this( card._id, @@ -22,7 +22,7 @@ data class BaseballCardState( card.brand, card.year.toString(), card.number, - (card.value / 100.0).toString(), + if (card.value == null) null else (card.value / 100.0).toString(), card.quantity.toString(), card.playerName, card.team, @@ -35,10 +35,10 @@ data class BaseballCardState( autographed = autographed, condition = condition, brand = brand, - year = year.toInt(), + year = year?.toInt(), number = number, - value = (value.toDouble() * 100).toInt(), - quantity = quantity.toInt(), + value = if (value == null) null else (value!!.toDouble() * 100).toInt(), + quantity = quantity?.toInt(), playerName = playerName, team = team, position = position diff --git a/app/src/main/java/bbct/android/ui/list/BaseballCardListScreen.kt b/app/src/main/java/bbct/android/ui/list/BaseballCardListScreen.kt index 7268296ce..d8138b043 100644 --- a/app/src/main/java/bbct/android/ui/list/BaseballCardListScreen.kt +++ b/app/src/main/java/bbct/android/ui/list/BaseballCardListScreen.kt @@ -221,7 +221,7 @@ fun BaseballCardRow( onCheckedChange = onSelectedChange, ) Text( - text = state.card.brand, + text = state.card.brand ?: "", modifier = Modifier.weight(0.2f) ) Text( @@ -229,11 +229,11 @@ fun BaseballCardRow( modifier = Modifier.weight(0.15f) ) Text( - text = state.card.number, + text = state.card.number ?: "", modifier = Modifier.weight(0.15f) ) Text( - text = state.card.playerName, + text = state.card.playerName ?: "", modifier = Modifier.weight(0.5f) ) } From e03c8c6dfcd6515931c27314d17c7577b464e899 Mon Sep 17 00:00:00 2001 From: Code-Apprentice Date: Mon, 23 Dec 2024 21:57:36 -0700 Subject: [PATCH 268/279] Copy generated schemas from Java implementation --- .../5.json | 93 ++++++++++++++++++ .../6.json | 94 +++++++++++++++++++ 2 files changed, 187 insertions(+) create mode 100644 app/schemas/bbct.android.data.BaseballCardDatabase/5.json create mode 100644 app/schemas/bbct.android.data.BaseballCardDatabase/6.json diff --git a/app/schemas/bbct.android.data.BaseballCardDatabase/5.json b/app/schemas/bbct.android.data.BaseballCardDatabase/5.json new file mode 100644 index 000000000..3bcb07318 --- /dev/null +++ b/app/schemas/bbct.android.data.BaseballCardDatabase/5.json @@ -0,0 +1,93 @@ +{ + "formatVersion": 1, + "database": { + "version": 5, + "identityHash": "d87e130401a9064c5e277d0c16974388", + "entities": [ + { + "tableName": "baseball_cards", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER, `autographed` INTEGER, `condition` TEXT, `brand` TEXT, `year` INTEGER, `number` INTEGER, `value` INTEGER, `card_count` INTEGER, `player_name` TEXT, `team` TEXT, `player_position` TEXT, PRIMARY KEY(`_id`))", + "fields": [ + { + "fieldPath": "_id", + "columnName": "_id", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "autographed", + "columnName": "autographed", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "condition", + "columnName": "condition", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "brand", + "columnName": "brand", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "year", + "columnName": "year", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "number", + "columnName": "number", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "value", + "columnName": "value", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "quantity", + "columnName": "card_count", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "playerName", + "columnName": "player_name", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "team", + "columnName": "team", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "position", + "columnName": "player_position", + "affinity": "TEXT", + "notNull": false + } + ], + "primaryKey": { + "columnNames": [ + "_id" + ], + "autoGenerate": false + }, + "indices": [], + "foreignKeys": [] + } + ], + "setupQueries": [ + "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", + "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, \"d87e130401a9064c5e277d0c16974388\")" + ] + } +} \ No newline at end of file diff --git a/app/schemas/bbct.android.data.BaseballCardDatabase/6.json b/app/schemas/bbct.android.data.BaseballCardDatabase/6.json new file mode 100644 index 000000000..5a0154e58 --- /dev/null +++ b/app/schemas/bbct.android.data.BaseballCardDatabase/6.json @@ -0,0 +1,94 @@ +{ + "formatVersion": 1, + "database": { + "version": 6, + "identityHash": "14e058026cab1bf2143c97c9eba60233", + "entities": [ + { + "tableName": "baseball_cards", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER, `autographed` INTEGER, `condition` TEXT, `brand` TEXT, `year` INTEGER, `number` TEXT, `value` INTEGER, `card_count` INTEGER, `player_name` TEXT, `team` TEXT, `player_position` TEXT, PRIMARY KEY(`_id`))", + "fields": [ + { + "fieldPath": "_id", + "columnName": "_id", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "autographed", + "columnName": "autographed", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "condition", + "columnName": "condition", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "brand", + "columnName": "brand", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "year", + "columnName": "year", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "number", + "columnName": "number", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "value", + "columnName": "value", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "quantity", + "columnName": "card_count", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "playerName", + "columnName": "player_name", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "team", + "columnName": "team", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "position", + "columnName": "player_position", + "affinity": "TEXT", + "notNull": false + } + ], + "primaryKey": { + "columnNames": [ + "_id" + ], + "autoGenerate": false + }, + "indices": [], + "foreignKeys": [] + } + ], + "views": [], + "setupQueries": [ + "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", + "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, \"14e058026cab1bf2143c97c9eba60233\")" + ] + } +} \ No newline at end of file From c93a335b3d77fe238a113a4cfec2c5ae3a2364f3 Mon Sep 17 00:00:00 2001 From: Code-Apprentice Date: Mon, 23 Dec 2024 22:44:13 -0700 Subject: [PATCH 269/279] Revert "Preserve nullability in BaseballCard model to match Java implementation" This reverts commit c08f6d6a20fdff17d27967f0846deb4f9eb3707a. --- .../java/bbct/android/data/BaseballCard.kt | 20 ++++++------- .../ui/details/BaseballCardDetailsScreen.kt | 20 ++++++------- .../details/BaseballCardDetailsViewModel.kt | 24 ++++++++-------- .../android/ui/details/BaseballCardState.kt | 28 +++++++++---------- .../android/ui/list/BaseballCardListScreen.kt | 6 ++-- 5 files changed, 49 insertions(+), 49 deletions(-) diff --git a/app/src/main/java/bbct/android/data/BaseballCard.kt b/app/src/main/java/bbct/android/data/BaseballCard.kt index 8899faca7..89f00e908 100644 --- a/app/src/main/java/bbct/android/data/BaseballCard.kt +++ b/app/src/main/java/bbct/android/data/BaseballCard.kt @@ -8,17 +8,17 @@ import androidx.room.PrimaryKey data class BaseballCard( @PrimaryKey val _id: Long? = null, - val autographed: Boolean?, - val condition: String?, - val brand: String?, - val year: Int?, - val number: String?, - val value: Int?, + val autographed: Boolean, + val condition: String, + val brand: String, + val year: Int, + val number: String, + val value: Int, @ColumnInfo(name = "card_count") - val quantity: Int?, + val quantity: Int, @ColumnInfo(name = "player_name") - val playerName: String?, - val team: String?, + val playerName: String, + val team: String, @ColumnInfo(name = "player_position") - val position: String?, + val position: String, ) diff --git a/app/src/main/java/bbct/android/ui/details/BaseballCardDetailsScreen.kt b/app/src/main/java/bbct/android/ui/details/BaseballCardDetailsScreen.kt index d330b3f24..badfc0069 100644 --- a/app/src/main/java/bbct/android/ui/details/BaseballCardDetailsScreen.kt +++ b/app/src/main/java/bbct/android/ui/details/BaseballCardDetailsScreen.kt @@ -173,7 +173,7 @@ fun BaseballCardDetails( ) { Row(verticalAlignment = Alignment.CenterVertically) { Checkbox( - checked = state.value.autographed == true, + checked = state.value.autographed, onCheckedChange = { state.value = state.value.copy(autographed = it) }, ) Text(text = stringResource(id = R.string.autographed)) @@ -181,7 +181,7 @@ fun BaseballCardDetails( Select( label = { Text(stringResource(id = R.string.condition)) }, options = conditions, - selected = state.value.condition ?: "", + selected = state.value.condition, onSelectedChange = { state.value = state.value.copy(condition = it) onValidate() @@ -192,7 +192,7 @@ fun BaseballCardDetails( AutoComplete( label = { Text(text = stringResource(id = R.string.brand)) }, options = brands.value, - value = state.value.brand ?: "", + value = state.value.brand, onValueChange = { state.value = state.value.copy(brand = it) onValidate() @@ -203,7 +203,7 @@ fun BaseballCardDetails( ) TextField( label = { Text(text = stringResource(id = R.string.year)) }, - value = state.value.year ?: "", + value = state.value.year, onValueChange = { state.value = state.value.copy(year = it) onValidate() @@ -217,7 +217,7 @@ fun BaseballCardDetails( ) TextField( label = { Text(text = stringResource(id = R.string.number)) }, - value = state.value.number ?: "", + value = state.value.number, onValueChange = { state.value = state.value.copy(number = it) onValidate() @@ -228,7 +228,7 @@ fun BaseballCardDetails( ) TextField( label = { Text(text = stringResource(id = R.string.value)) }, - value = state.value.value ?: "", + value = state.value.value, onValueChange = { state.value = state.value.copy(value = it) onValidate() @@ -242,7 +242,7 @@ fun BaseballCardDetails( ) TextField( label = { Text(text = stringResource(id = R.string.quantity)) }, - value = state.value.quantity ?: "", + value = state.value.quantity, onValueChange = { state.value = state.value.copy(quantity = it) onValidate() @@ -257,7 +257,7 @@ fun BaseballCardDetails( AutoComplete( label = { Text(text = stringResource(id = R.string.player_name)) }, options = playerNames.value, - value = state.value.playerName ?: "", + value = state.value.playerName, onValueChange = { state.value = state.value.copy(playerName = it) onValidate() @@ -269,7 +269,7 @@ fun BaseballCardDetails( AutoComplete( label = { Text(text = stringResource(id = R.string.team)) }, options = teams.value, - value = state.value.team ?: "", + value = state.value.team, onValueChange = { state.value = state.value.copy(team = it) onValidate() @@ -281,7 +281,7 @@ fun BaseballCardDetails( Select( label = { Text(stringResource(id = R.string.player_position)) }, options = positions, - selected = state.value.position ?: "", + selected = state.value.position, onSelectedChange = { state.value = state.value.copy(position = it) onValidate() diff --git a/app/src/main/java/bbct/android/ui/details/BaseballCardDetailsViewModel.kt b/app/src/main/java/bbct/android/ui/details/BaseballCardDetailsViewModel.kt index e84df39f5..6255b5414 100644 --- a/app/src/main/java/bbct/android/ui/details/BaseballCardDetailsViewModel.kt +++ b/app/src/main/java/bbct/android/ui/details/BaseballCardDetailsViewModel.kt @@ -30,62 +30,62 @@ class BaseballCardDetailsViewModel : ViewModel() { val baseballCard = baseballCardState.value val errors = BaseballCardDetailsErrors() - if (baseballCard.condition.isNullOrBlank()) { + if (baseballCard.condition.isBlank()) { errors.condition = FormFieldError("Condition is required") errors.condition.isValid = false } - if (baseballCard.brand.isNullOrBlank()) { + if (baseballCard.brand.isBlank()) { errors.brand = FormFieldError("Brand is required") errors.brand.isValid = false } - if (baseballCard.year.isNullOrBlank()) { + if (baseballCard.year.isBlank()) { errors.year = FormFieldError("Year is required") errors.year.isValid = false } - if (baseballCard.year?.toIntOrNull() == null) { + if (baseballCard.year.toIntOrNull() == null) { errors.year = FormFieldError("Year must be a number") errors.year.isValid = false } - if (baseballCard.number.isNullOrBlank()) { + if (baseballCard.number.isBlank()) { errors.number = FormFieldError("Number is required") errors.number.isValid = false } - if (baseballCard.value.isNullOrBlank()) { + if (baseballCard.value.isBlank()) { errors.value = FormFieldError("Value is required") errors.value.isValid = false } - if (baseballCard.value?.toDoubleOrNull() == null) { + if (baseballCard.value.toDoubleOrNull() == null) { errors.value = FormFieldError("Value must be a number") errors.value.isValid = false } - if (baseballCard.quantity.isNullOrBlank()) { + if (baseballCard.quantity.isBlank()) { errors.quantity = FormFieldError("Quantity is required") errors.quantity.isValid = false } - if (baseballCard.quantity?.toIntOrNull() == null) { + if (baseballCard.quantity.toIntOrNull() == null) { errors.quantity = FormFieldError("Quantity must be a number") errors.quantity.isValid = false } - if (baseballCard.playerName.isNullOrBlank()) { + if (baseballCard.playerName.isBlank()) { errors.playerName = FormFieldError("Player name is required") errors.playerName.isValid = false } - if (baseballCard.team.isNullOrBlank()) { + if (baseballCard.team.isBlank()) { errors.team = FormFieldError("Team is required") errors.team.isValid = false } - if (baseballCard.position.isNullOrBlank()) { + if (baseballCard.position.isBlank()) { errors.position = FormFieldError("Position is required") errors.position.isValid = false } diff --git a/app/src/main/java/bbct/android/ui/details/BaseballCardState.kt b/app/src/main/java/bbct/android/ui/details/BaseballCardState.kt index 9f9e74d16..e1b775810 100644 --- a/app/src/main/java/bbct/android/ui/details/BaseballCardState.kt +++ b/app/src/main/java/bbct/android/ui/details/BaseballCardState.kt @@ -4,16 +4,16 @@ import bbct.android.data.BaseballCard data class BaseballCardState( var id: Long? = null, - var autographed: Boolean? = false, - var condition: String? = null, - var brand: String? = null, - var year: String? = null, - var number: String? = null, - var value: String? = null, - var quantity: String? = null, - var playerName: String? = null, - var team: String? = null, - var position: String? = null, + var autographed: Boolean = false, + var condition: String = "", + var brand: String = "", + var year: String = "", + var number: String = "", + var value: String = "", + var quantity: String = "", + var playerName: String = "", + var team: String = "", + var position: String = "", ) { constructor(card: BaseballCard) : this( card._id, @@ -22,7 +22,7 @@ data class BaseballCardState( card.brand, card.year.toString(), card.number, - if (card.value == null) null else (card.value / 100.0).toString(), + (card.value / 100.0).toString(), card.quantity.toString(), card.playerName, card.team, @@ -35,10 +35,10 @@ data class BaseballCardState( autographed = autographed, condition = condition, brand = brand, - year = year?.toInt(), + year = year.toInt(), number = number, - value = if (value == null) null else (value!!.toDouble() * 100).toInt(), - quantity = quantity?.toInt(), + value = (value.toDouble() * 100).toInt(), + quantity = quantity.toInt(), playerName = playerName, team = team, position = position diff --git a/app/src/main/java/bbct/android/ui/list/BaseballCardListScreen.kt b/app/src/main/java/bbct/android/ui/list/BaseballCardListScreen.kt index d8138b043..7268296ce 100644 --- a/app/src/main/java/bbct/android/ui/list/BaseballCardListScreen.kt +++ b/app/src/main/java/bbct/android/ui/list/BaseballCardListScreen.kt @@ -221,7 +221,7 @@ fun BaseballCardRow( onCheckedChange = onSelectedChange, ) Text( - text = state.card.brand ?: "", + text = state.card.brand, modifier = Modifier.weight(0.2f) ) Text( @@ -229,11 +229,11 @@ fun BaseballCardRow( modifier = Modifier.weight(0.15f) ) Text( - text = state.card.number ?: "", + text = state.card.number, modifier = Modifier.weight(0.15f) ) Text( - text = state.card.playerName ?: "", + text = state.card.playerName, modifier = Modifier.weight(0.5f) ) } From 8995d935dda1fad9839747549bba8eb3a22f811f Mon Sep 17 00:00:00 2001 From: Code-Apprentice Date: Mon, 23 Dec 2024 22:55:30 -0700 Subject: [PATCH 270/279] Migrate to NOT NULL constraints --- .../7.json | 94 +++++++++++++++++++ .../java/bbct/android/data/BaseballCard.kt | 2 +- .../bbct/android/data/BaseballCardDatabase.kt | 93 +++++++++--------- .../android/data/BaseballCardSQLHelper.java | 3 + .../ui/details/BaseballCardDetailsScreen.kt | 2 +- .../details/BaseballCardDetailsViewModel.kt | 7 +- .../android/ui/details/BaseballCardState.kt | 6 +- 7 files changed, 153 insertions(+), 54 deletions(-) create mode 100644 app/schemas/bbct.android.data.BaseballCardDatabase/7.json diff --git a/app/schemas/bbct.android.data.BaseballCardDatabase/7.json b/app/schemas/bbct.android.data.BaseballCardDatabase/7.json new file mode 100644 index 000000000..effdc2396 --- /dev/null +++ b/app/schemas/bbct.android.data.BaseballCardDatabase/7.json @@ -0,0 +1,94 @@ +{ + "formatVersion": 1, + "database": { + "version": 7, + "identityHash": "1e7392f7aeba9fbe1179c3588d832937", + "entities": [ + { + "tableName": "baseball_cards", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER, `autographed` INTEGER NOT NULL, `condition` TEXT NOT NULL, `brand` TEXT NOT NULL, `year` INTEGER NOT NULL, `number` TEXT NOT NULL, `value` INTEGER, `card_count` INTEGER NOT NULL, `player_name` TEXT NOT NULL, `team` TEXT NOT NULL, `player_position` TEXT NOT NULL, PRIMARY KEY(`_id`))", + "fields": [ + { + "fieldPath": "_id", + "columnName": "_id", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "autographed", + "columnName": "autographed", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "condition", + "columnName": "condition", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "brand", + "columnName": "brand", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "year", + "columnName": "year", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "number", + "columnName": "number", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "value", + "columnName": "value", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "quantity", + "columnName": "card_count", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "playerName", + "columnName": "player_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "team", + "columnName": "team", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "position", + "columnName": "player_position", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "_id" + ] + }, + "indices": [], + "foreignKeys": [] + } + ], + "views": [], + "setupQueries": [ + "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", + "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '1e7392f7aeba9fbe1179c3588d832937')" + ] + } +} \ No newline at end of file diff --git a/app/src/main/java/bbct/android/data/BaseballCard.kt b/app/src/main/java/bbct/android/data/BaseballCard.kt index 89f00e908..454ea2b46 100644 --- a/app/src/main/java/bbct/android/data/BaseballCard.kt +++ b/app/src/main/java/bbct/android/data/BaseballCard.kt @@ -13,7 +13,7 @@ data class BaseballCard( val brand: String, val year: Int, val number: String, - val value: Int, + val value: Int?, @ColumnInfo(name = "card_count") val quantity: Int, @ColumnInfo(name = "player_name") diff --git a/app/src/main/java/bbct/android/data/BaseballCardDatabase.kt b/app/src/main/java/bbct/android/data/BaseballCardDatabase.kt index 5ad9c99d5..98ddf5873 100644 --- a/app/src/main/java/bbct/android/data/BaseballCardDatabase.kt +++ b/app/src/main/java/bbct/android/data/BaseballCardDatabase.kt @@ -1,6 +1,7 @@ package bbct.android.data import android.content.Context +import androidx.room.AutoMigration import androidx.room.Database import androidx.room.Room.databaseBuilder import androidx.room.RoomDatabase @@ -8,8 +9,14 @@ import androidx.room.migration.Migration import androidx.sqlite.db.SupportSQLiteDatabase @Database( - version = BaseballCardSQLHelper.ALPHA_NUMERIC_SCHEMA, - entities = [BaseballCard::class] + version = BaseballCardSQLHelper.NOT_NULL_SCHEMA, + entities = [BaseballCard::class], + autoMigrations = [ + AutoMigration( + from = BaseballCardSQLHelper.ALPHA_NUMERIC_SCHEMA, + to = BaseballCardSQLHelper.NOT_NULL_SCHEMA + ) + ] ) abstract class BaseballCardDatabase : RoomDatabase() { abstract val baseballCardDao: BaseballCardDao @@ -33,49 +40,49 @@ abstract class BaseballCardDatabase : RoomDatabase() { val temp_table_name = BaseballCardContract.TABLE_NAME + "_new" db.execSQL( "CREATE TABLE IF NOT EXISTS " - + temp_table_name + "(" - + BaseballCardContract.ID_COL_NAME - + " INTEGER PRIMARY KEY AUTOINCREMENT," - + BaseballCardContract.BRAND_COL_NAME + " TEXT, " - + BaseballCardContract.YEAR_COL_NAME + " INTEGER, " - + BaseballCardContract.NUMBER_COL_NAME + " TEXT, " - + BaseballCardContract.VALUE_COL_NAME + " INTEGER, " - + BaseballCardContract.COUNT_COL_NAME + " INTEGER, " - + BaseballCardContract.PLAYER_NAME_COL_NAME + " TEXT, " - + BaseballCardContract.TEAM_COL_NAME + " TEXT, " - + BaseballCardContract.PLAYER_POSITION_COL_NAME + " TEXT," - + BaseballCardContract.AUTOGRAPHED_COL_NAME + " INTEGER," - + BaseballCardContract.CONDITION_COL_NAME + " TEXT," - + "UNIQUE (" + BaseballCardContract.BRAND_COL_NAME + ", " - + BaseballCardContract.YEAR_COL_NAME + ", " - + BaseballCardContract.NUMBER_COL_NAME + "))" + + temp_table_name + "(" + + BaseballCardContract.ID_COL_NAME + + " INTEGER PRIMARY KEY AUTOINCREMENT," + + BaseballCardContract.BRAND_COL_NAME + " TEXT, " + + BaseballCardContract.YEAR_COL_NAME + " INTEGER, " + + BaseballCardContract.NUMBER_COL_NAME + " TEXT, " + + BaseballCardContract.VALUE_COL_NAME + " INTEGER, " + + BaseballCardContract.COUNT_COL_NAME + " INTEGER, " + + BaseballCardContract.PLAYER_NAME_COL_NAME + " TEXT, " + + BaseballCardContract.TEAM_COL_NAME + " TEXT, " + + BaseballCardContract.PLAYER_POSITION_COL_NAME + " TEXT," + + BaseballCardContract.AUTOGRAPHED_COL_NAME + " INTEGER," + + BaseballCardContract.CONDITION_COL_NAME + " TEXT," + + "UNIQUE (" + BaseballCardContract.BRAND_COL_NAME + ", " + + BaseballCardContract.YEAR_COL_NAME + ", " + + BaseballCardContract.NUMBER_COL_NAME + "))" ) db.execSQL( "INSERT INTO " + temp_table_name + " (" - + BaseballCardContract.ID_COL_NAME + ", " - + BaseballCardContract.BRAND_COL_NAME + ", " - + BaseballCardContract.YEAR_COL_NAME + ", " - + BaseballCardContract.NUMBER_COL_NAME + ", " - + BaseballCardContract.VALUE_COL_NAME + ", " - + BaseballCardContract.COUNT_COL_NAME + ", " - + BaseballCardContract.PLAYER_NAME_COL_NAME + ", " - + BaseballCardContract.TEAM_COL_NAME + ", " - + BaseballCardContract.PLAYER_POSITION_COL_NAME + ", " - + BaseballCardContract.AUTOGRAPHED_COL_NAME + ", " - + BaseballCardContract.CONDITION_COL_NAME - + ") SELECT " - + BaseballCardContract.ID_COL_NAME + ", " - + BaseballCardContract.BRAND_COL_NAME + ", " - + BaseballCardContract.YEAR_COL_NAME + ", " - + BaseballCardContract.NUMBER_COL_NAME + ", " - + BaseballCardContract.VALUE_COL_NAME + ", " - + BaseballCardContract.COUNT_COL_NAME + ", " - + BaseballCardContract.PLAYER_NAME_COL_NAME + ", " - + BaseballCardContract.TEAM_COL_NAME + ", " - + BaseballCardContract.PLAYER_POSITION_COL_NAME + ", " - + BaseballCardContract.AUTOGRAPHED_COL_NAME + ", " - + BaseballCardContract.CONDITION_COL_NAME - + " FROM " + BaseballCardContract.TABLE_NAME + + BaseballCardContract.ID_COL_NAME + ", " + + BaseballCardContract.BRAND_COL_NAME + ", " + + BaseballCardContract.YEAR_COL_NAME + ", " + + BaseballCardContract.NUMBER_COL_NAME + ", " + + BaseballCardContract.VALUE_COL_NAME + ", " + + BaseballCardContract.COUNT_COL_NAME + ", " + + BaseballCardContract.PLAYER_NAME_COL_NAME + ", " + + BaseballCardContract.TEAM_COL_NAME + ", " + + BaseballCardContract.PLAYER_POSITION_COL_NAME + ", " + + BaseballCardContract.AUTOGRAPHED_COL_NAME + ", " + + BaseballCardContract.CONDITION_COL_NAME + + ") SELECT " + + BaseballCardContract.ID_COL_NAME + ", " + + BaseballCardContract.BRAND_COL_NAME + ", " + + BaseballCardContract.YEAR_COL_NAME + ", " + + BaseballCardContract.NUMBER_COL_NAME + ", " + + BaseballCardContract.VALUE_COL_NAME + ", " + + BaseballCardContract.COUNT_COL_NAME + ", " + + BaseballCardContract.PLAYER_NAME_COL_NAME + ", " + + BaseballCardContract.TEAM_COL_NAME + ", " + + BaseballCardContract.PLAYER_POSITION_COL_NAME + ", " + + BaseballCardContract.AUTOGRAPHED_COL_NAME + ", " + + BaseballCardContract.CONDITION_COL_NAME + + " FROM " + BaseballCardContract.TABLE_NAME ) db.execSQL("DROP TABLE " + BaseballCardContract.TABLE_NAME) @@ -88,7 +95,7 @@ abstract class BaseballCardDatabase : RoomDatabase() { fun getInstance( context: Context, - dbName: String + dbName: String, ): BaseballCardDatabase { var instance = instances[dbName] if (instance == null) { diff --git a/app/src/main/java/bbct/android/data/BaseballCardSQLHelper.java b/app/src/main/java/bbct/android/data/BaseballCardSQLHelper.java index 3e1d8eb63..7012231c9 100644 --- a/app/src/main/java/bbct/android/data/BaseballCardSQLHelper.java +++ b/app/src/main/java/bbct/android/data/BaseballCardSQLHelper.java @@ -54,6 +54,9 @@ public class BaseballCardSQLHelper extends SQLiteOpenHelper { public static final int ROOM_SCHEMA = 5; public static final int ALPHA_NUMERIC_SCHEMA = 6; + + public static final int NOT_NULL_SCHEMA = 7; + private static final String TAG = BaseballCardSQLHelper.class.getName(); public BaseballCardSQLHelper(Context context) { diff --git a/app/src/main/java/bbct/android/ui/details/BaseballCardDetailsScreen.kt b/app/src/main/java/bbct/android/ui/details/BaseballCardDetailsScreen.kt index badfc0069..7dd6ee3ed 100644 --- a/app/src/main/java/bbct/android/ui/details/BaseballCardDetailsScreen.kt +++ b/app/src/main/java/bbct/android/ui/details/BaseballCardDetailsScreen.kt @@ -228,7 +228,7 @@ fun BaseballCardDetails( ) TextField( label = { Text(text = stringResource(id = R.string.value)) }, - value = state.value.value, + value = state.value.value ?: "", onValueChange = { state.value = state.value.copy(value = it) onValidate() diff --git a/app/src/main/java/bbct/android/ui/details/BaseballCardDetailsViewModel.kt b/app/src/main/java/bbct/android/ui/details/BaseballCardDetailsViewModel.kt index 6255b5414..58c26c619 100644 --- a/app/src/main/java/bbct/android/ui/details/BaseballCardDetailsViewModel.kt +++ b/app/src/main/java/bbct/android/ui/details/BaseballCardDetailsViewModel.kt @@ -55,12 +55,7 @@ class BaseballCardDetailsViewModel : ViewModel() { errors.number.isValid = false } - if (baseballCard.value.isBlank()) { - errors.value = FormFieldError("Value is required") - errors.value.isValid = false - } - - if (baseballCard.value.toDoubleOrNull() == null) { + if (baseballCard.value != null && baseballCard.value?.toDoubleOrNull() == null) { errors.value = FormFieldError("Value must be a number") errors.value.isValid = false } diff --git a/app/src/main/java/bbct/android/ui/details/BaseballCardState.kt b/app/src/main/java/bbct/android/ui/details/BaseballCardState.kt index e1b775810..be17bd349 100644 --- a/app/src/main/java/bbct/android/ui/details/BaseballCardState.kt +++ b/app/src/main/java/bbct/android/ui/details/BaseballCardState.kt @@ -9,7 +9,7 @@ data class BaseballCardState( var brand: String = "", var year: String = "", var number: String = "", - var value: String = "", + var value: String? = null, var quantity: String = "", var playerName: String = "", var team: String = "", @@ -22,7 +22,7 @@ data class BaseballCardState( card.brand, card.year.toString(), card.number, - (card.value / 100.0).toString(), + if (card.value == null) null else (card.value / 100.0).toString(), card.quantity.toString(), card.playerName, card.team, @@ -37,7 +37,7 @@ data class BaseballCardState( brand = brand, year = year.toInt(), number = number, - value = (value.toDouble() * 100).toInt(), + value = if (value == null) null else (value!!.toDouble() * 100).toInt(), quantity = quantity.toInt(), playerName = playerName, team = team, From cf7dd96d0678efa4c7973ab23077bf958f1fef34 Mon Sep 17 00:00:00 2001 From: Code-Apprentice Date: Mon, 23 Dec 2024 23:12:17 -0700 Subject: [PATCH 271/279] Delete BaseballCardSQLHelper.java, just declare schema names as consts --- .../bbct/android/data/BaseballCardDatabase.kt | 22 ++-- .../android/data/BaseballCardSQLHelper.java | 113 ------------------ .../android/data/BaseballCardSchemaVersion.kt | 10 -- 3 files changed, 15 insertions(+), 130 deletions(-) delete mode 100644 app/src/main/java/bbct/android/data/BaseballCardSQLHelper.java delete mode 100644 app/src/main/java/bbct/android/data/BaseballCardSchemaVersion.kt diff --git a/app/src/main/java/bbct/android/data/BaseballCardDatabase.kt b/app/src/main/java/bbct/android/data/BaseballCardDatabase.kt index 98ddf5873..ec5f5ef1d 100644 --- a/app/src/main/java/bbct/android/data/BaseballCardDatabase.kt +++ b/app/src/main/java/bbct/android/data/BaseballCardDatabase.kt @@ -8,13 +8,21 @@ import androidx.room.RoomDatabase import androidx.room.migration.Migration import androidx.sqlite.db.SupportSQLiteDatabase +const val ORIGINAL_SCHEMA = 1 +const val BAD_TEAM_SCHEMA = 2 +const val TEAM_SCHEMA = 3 +const val AUTO_AND_CONDITION_SCHEMA = 4 +const val ROOM_SCHEMA = 5 +const val ALPHA_NUMERIC_SCHEMA = 6 +const val NOT_NULL_SCHEMA = 7 + @Database( - version = BaseballCardSQLHelper.NOT_NULL_SCHEMA, + version = NOT_NULL_SCHEMA, entities = [BaseballCard::class], autoMigrations = [ AutoMigration( - from = BaseballCardSQLHelper.ALPHA_NUMERIC_SCHEMA, - to = BaseballCardSQLHelper.NOT_NULL_SCHEMA + from = ALPHA_NUMERIC_SCHEMA, + to = NOT_NULL_SCHEMA ) ] ) @@ -26,15 +34,15 @@ abstract class BaseballCardDatabase : RoomDatabase() { internal const val TEST_DATABASE_NAME = "bbct_test.db" private val MIGRATION_4_5: Migration = object : Migration( - BaseballCardSQLHelper.AUTO_AND_CONDITION_SCHEMA, - BaseballCardSQLHelper.ROOM_SCHEMA + AUTO_AND_CONDITION_SCHEMA, + ROOM_SCHEMA ) { override fun migrate(db: SupportSQLiteDatabase) { } } private val MIGRATION_5_6: Migration = object : Migration( - BaseballCardSQLHelper.ROOM_SCHEMA, - BaseballCardSQLHelper.ALPHA_NUMERIC_SCHEMA + ROOM_SCHEMA, + ALPHA_NUMERIC_SCHEMA ) { override fun migrate(db: SupportSQLiteDatabase) { val temp_table_name = BaseballCardContract.TABLE_NAME + "_new" diff --git a/app/src/main/java/bbct/android/data/BaseballCardSQLHelper.java b/app/src/main/java/bbct/android/data/BaseballCardSQLHelper.java deleted file mode 100644 index 7012231c9..000000000 --- a/app/src/main/java/bbct/android/data/BaseballCardSQLHelper.java +++ /dev/null @@ -1,113 +0,0 @@ -/* - * This file is part of BBCT for Android. - * - * Copyright 2012-14 codeguru - * - * BBCT for Android 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. - * - * BBCT for Android 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 bbct.android.data; - -import android.content.Context; -import android.database.sqlite.SQLiteDatabase; -import android.database.sqlite.SQLiteOpenHelper; -import android.util.Log; - -// TODO: Write JUnit tests. -public class BaseballCardSQLHelper extends SQLiteOpenHelper { - - public static final String DATABASE_NAME = "bbct.db"; - - /** - * Current schema version. - */ - public static final int SCHEMA_VERSION = 6; - /** - * Original schema version. - */ - public static final int ORIGINAL_SCHEMA = 1; - /** - * Schema version when attempting to add the team field. This version was - * buggy. - */ - public static final int BAD_TEAM_SCHEMA = 2; - /** - * Schema version which correctly added the team field.N - */ - public static final int TEAM_SCHEMA = 3; - - /** - * Schema version which adds Autographed and Condition fields. - */ - public static final int AUTO_AND_CONDITION_SCHEMA = 4; - - public static final int ROOM_SCHEMA = 5; - - public static final int ALPHA_NUMERIC_SCHEMA = 6; - - public static final int NOT_NULL_SCHEMA = 7; - - private static final String TAG = BaseballCardSQLHelper.class.getName(); - - public BaseballCardSQLHelper(Context context) { - super(context, DATABASE_NAME, null, SCHEMA_VERSION); - - Log.d(TAG, "ctor"); - } - - @Override - public void onCreate(SQLiteDatabase db) { - Log.d(TAG, "onCreate()"); - - String sqlCreate = "CREATE TABLE IF NOT EXISTS " - + BaseballCardContract.TABLE_NAME + "(" - + BaseballCardContract.ID_COL_NAME - + " INTEGER PRIMARY KEY AUTOINCREMENT," - + BaseballCardContract.BRAND_COL_NAME + " TEXT, " - + BaseballCardContract.YEAR_COL_NAME + " INTEGER, " - + BaseballCardContract.NUMBER_COL_NAME + " TEXT, " - + BaseballCardContract.VALUE_COL_NAME + " INTEGER, " - + BaseballCardContract.COUNT_COL_NAME + " INTEGER, " - + BaseballCardContract.PLAYER_NAME_COL_NAME + " TEXT, " - + BaseballCardContract.TEAM_COL_NAME + " TEXT, " - + BaseballCardContract.PLAYER_POSITION_COL_NAME + " TEXT," - + BaseballCardContract.AUTOGRAPHED_COL_NAME + " INTEGER," - + BaseballCardContract.CONDITION_COL_NAME + " TEXT," - + "UNIQUE (" + BaseballCardContract.BRAND_COL_NAME + ", " - + BaseballCardContract.YEAR_COL_NAME + ", " - + BaseballCardContract.NUMBER_COL_NAME + "))"; - - db.execSQL(sqlCreate); - } - - @Override - public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { - if (oldVersion < TEAM_SCHEMA) { - String sqlUpgrade = "ALTER TABLE " - + BaseballCardContract.TABLE_NAME + " ADD COLUMN " - + BaseballCardContract.TEAM_COL_NAME + " VARCHAR(50)"; - db.execSQL(sqlUpgrade); - } - - if (oldVersion < AUTO_AND_CONDITION_SCHEMA) { - String addAutographed = "ALTER TABLE " - + BaseballCardContract.TABLE_NAME + " ADD COLUMN " - + BaseballCardContract.AUTOGRAPHED_COL_NAME + " INTEGER;"; - String addCondition = "ALTER TABLE " - + BaseballCardContract.TABLE_NAME + " ADD COLUMN " - + BaseballCardContract.CONDITION_COL_NAME + " TEXT"; - db.execSQL(addAutographed); - db.execSQL(addCondition); - } - } -} diff --git a/app/src/main/java/bbct/android/data/BaseballCardSchemaVersion.kt b/app/src/main/java/bbct/android/data/BaseballCardSchemaVersion.kt deleted file mode 100644 index d9cace115..000000000 --- a/app/src/main/java/bbct/android/data/BaseballCardSchemaVersion.kt +++ /dev/null @@ -1,10 +0,0 @@ -package bbct.android.data - -enum class BaseballCardSchemaVersion(val version: Int) { - ORIGINAL_SCHEMA(1), - BAD_TEAM_SCHEMA(2), - TEAM_SCHEMA(3), - AUTO_AND_CONDITION_SCHEMA(4), - ROOM_SCHEMA(5), - ALPHA_NUMERIC_SCHEMA(6), -} From 1ce34a619e52925d1e86280891df312b474647a8 Mon Sep 17 00:00:00 2001 From: Code-Apprentice Date: Mon, 23 Dec 2024 23:33:09 -0700 Subject: [PATCH 272/279] Simplify names --- .../ui/details/BaseballCardCreateTest.kt | 4 +- .../ui/details/BaseballCardEditTest.kt | 2 +- .../ui/filter/BaseballCardFilterTest.kt | 2 +- .../ui/list/BaseballCardListScreenTest.kt | 2 +- .../android/ui/list/BaseballCardListTest.kt | 4 +- app/src/main/java/bbct/android/ui/App.kt | 12 +++--- ...lCardDetailsScreen.kt => DetailsScreen.kt} | 42 +++++++++---------- .../{BaseballCardState.kt => DetailsState.kt} | 2 +- ...etailsViewModel.kt => DetailsViewModel.kt} | 12 +++--- ...allCardFilterScreen.kt => FilterScreen.kt} | 12 +++--- ...eballCardFilterState.kt => FilterState.kt} | 2 +- ...dFilterViewModel.kt => FilterViewModel.kt} | 6 +-- ...aseballCardListScreen.kt => ListScreen.kt} | 28 ++++++------- ...lCardListViewModel.kt => ListViewModel.kt} | 16 +++---- 14 files changed, 73 insertions(+), 73 deletions(-) rename app/src/main/java/bbct/android/ui/details/{BaseballCardDetailsScreen.kt => DetailsScreen.kt} (92%) rename app/src/main/java/bbct/android/ui/details/{BaseballCardState.kt => DetailsState.kt} (97%) rename app/src/main/java/bbct/android/ui/details/{BaseballCardDetailsViewModel.kt => DetailsViewModel.kt} (90%) rename app/src/main/java/bbct/android/ui/filter/{BaseballCardFilterScreen.kt => FilterScreen.kt} (94%) rename app/src/main/java/bbct/android/ui/filter/{BaseballCardFilterState.kt => FilterState.kt} (86%) rename app/src/main/java/bbct/android/ui/filter/{BaseballCardFilterViewModel.kt => FilterViewModel.kt} (50%) rename app/src/main/java/bbct/android/ui/list/{BaseballCardListScreen.kt => ListScreen.kt} (91%) rename app/src/main/java/bbct/android/ui/list/{BaseballCardListViewModel.kt => ListViewModel.kt} (74%) diff --git a/app/src/androidTest/java/bbct/android/ui/details/BaseballCardCreateTest.kt b/app/src/androidTest/java/bbct/android/ui/details/BaseballCardCreateTest.kt index 125801839..836706821 100644 --- a/app/src/androidTest/java/bbct/android/ui/details/BaseballCardCreateTest.kt +++ b/app/src/androidTest/java/bbct/android/ui/details/BaseballCardCreateTest.kt @@ -37,7 +37,7 @@ class BaseballCardCreateTest : TestBase() { composeTestRule.setContent { val navController = rememberNavController() - BaseballCardCreateScreen(navController, db) + CreateScreen(navController, db) } enterCardData(card) @@ -68,7 +68,7 @@ class BaseballCardCreateTest : TestBase() { composeTestRule.setContent { val navController = rememberNavController() - BaseballCardCreateScreen(navController, db) + CreateScreen(navController, db) } enterCardData(card) diff --git a/app/src/androidTest/java/bbct/android/ui/details/BaseballCardEditTest.kt b/app/src/androidTest/java/bbct/android/ui/details/BaseballCardEditTest.kt index e53ea707b..7a1d0cd13 100644 --- a/app/src/androidTest/java/bbct/android/ui/details/BaseballCardEditTest.kt +++ b/app/src/androidTest/java/bbct/android/ui/details/BaseballCardEditTest.kt @@ -31,7 +31,7 @@ class BaseballCardEditTest : TestBase() { composeTestRule.setContent { val navController = rememberNavController() - BaseballCardEditScreen( + EditScreen( navController = navController, db = db, cardId = card._id!! diff --git a/app/src/androidTest/java/bbct/android/ui/filter/BaseballCardFilterTest.kt b/app/src/androidTest/java/bbct/android/ui/filter/BaseballCardFilterTest.kt index 15a8a1884..e147c3429 100644 --- a/app/src/androidTest/java/bbct/android/ui/filter/BaseballCardFilterTest.kt +++ b/app/src/androidTest/java/bbct/android/ui/filter/BaseballCardFilterTest.kt @@ -18,7 +18,7 @@ class BaseballCardFilterTest { @Before fun setUp() { composeTestRule.setContent { - BaseballCardFilterScreen( + FilterScreen( onApplyFilter = { }, onClose = { }) } diff --git a/app/src/androidTest/java/bbct/android/ui/list/BaseballCardListScreenTest.kt b/app/src/androidTest/java/bbct/android/ui/list/BaseballCardListScreenTest.kt index 6d0a03997..852f23a98 100644 --- a/app/src/androidTest/java/bbct/android/ui/list/BaseballCardListScreenTest.kt +++ b/app/src/androidTest/java/bbct/android/ui/list/BaseballCardListScreenTest.kt @@ -32,7 +32,7 @@ class BaseballCardListScreenTest { fun setup() { composeTestRule.setContent { var navController = rememberNavController() - BaseballCardListScreen(navController, db) + ListScreen(navController, db) } } diff --git a/app/src/androidTest/java/bbct/android/ui/list/BaseballCardListTest.kt b/app/src/androidTest/java/bbct/android/ui/list/BaseballCardListTest.kt index 86c1d6a83..52635c1e6 100644 --- a/app/src/androidTest/java/bbct/android/ui/list/BaseballCardListTest.kt +++ b/app/src/androidTest/java/bbct/android/ui/list/BaseballCardListTest.kt @@ -20,13 +20,13 @@ class BaseballCardListTest { @get:Rule val cardListRule = CardListTestRule("three_cards.csv") - private var cardListState = mutableListOf() + private var cardListState = mutableListOf() @Before fun setup() { cardListState = cardListRule.cards!! .map { - BaseballCardSelectedState( + SelectedState( it, false ) diff --git a/app/src/main/java/bbct/android/ui/App.kt b/app/src/main/java/bbct/android/ui/App.kt index aa3821a55..1695f7bad 100644 --- a/app/src/main/java/bbct/android/ui/App.kt +++ b/app/src/main/java/bbct/android/ui/App.kt @@ -6,9 +6,9 @@ import androidx.navigation.compose.composable import androidx.navigation.compose.rememberNavController import androidx.navigation.toRoute import bbct.android.data.BaseballCardDatabase -import bbct.android.ui.details.BaseballCardCreateScreen -import bbct.android.ui.details.BaseballCardEditScreen -import bbct.android.ui.list.BaseballCardListScreen +import bbct.android.ui.details.CreateScreen +import bbct.android.ui.details.EditScreen +import bbct.android.ui.list.ListScreen import bbct.android.ui.navigation.AboutDestination import bbct.android.ui.navigation.BaseballCardCreateDestination import bbct.android.ui.navigation.BaseballCardEditDestination @@ -25,20 +25,20 @@ fun App(db: BaseballCardDatabase) { startDestination = BaseballCardListDestination, ) { composable { - BaseballCardListScreen( + ListScreen( navController, db, ) } composable { - BaseballCardCreateScreen( + CreateScreen( navController, db, ) } composable { backStackEntry -> var destination: BaseballCardEditDestination = backStackEntry.toRoute() - BaseballCardEditScreen( + EditScreen( navController, db, destination.cardId, diff --git a/app/src/main/java/bbct/android/ui/details/BaseballCardDetailsScreen.kt b/app/src/main/java/bbct/android/ui/details/DetailsScreen.kt similarity index 92% rename from app/src/main/java/bbct/android/ui/details/BaseballCardDetailsScreen.kt rename to app/src/main/java/bbct/android/ui/details/DetailsScreen.kt index 7dd6ee3ed..9d2199641 100644 --- a/app/src/main/java/bbct/android/ui/details/BaseballCardDetailsScreen.kt +++ b/app/src/main/java/bbct/android/ui/details/DetailsScreen.kt @@ -46,11 +46,11 @@ import bbct.android.ui.navigation.AboutDestination import kotlinx.coroutines.launch @Composable -fun BaseballCardCreateScreen( +fun CreateScreen( navController: NavController, db: BaseballCardDatabase, ) { - val viewModel: BaseballCardDetailsViewModel = viewModel() + val viewModel: DetailsViewModel = viewModel() val scope = rememberCoroutineScope() val snackbarHostState = remember { SnackbarHostState() } @@ -75,7 +75,7 @@ fun BaseballCardCreateScreen( floatingActionButton = { CreateCardButton( db, - viewModel.baseballCardState, + viewModel.detailsState, viewModel.errors, viewModel::validate, onSuccess = { scope.launch { snackbarHostState.showSnackbar("Card created") } }, @@ -86,8 +86,8 @@ fun BaseballCardCreateScreen( .fillMaxSize() .imePadding() ) { innerPadding -> - BaseballCardDetails( - state = viewModel.baseballCardState, + Details( + state = viewModel.detailsState, db = db, errors = viewModel.errors, onValidate = viewModel::validate, @@ -97,16 +97,16 @@ fun BaseballCardCreateScreen( } @Composable -fun BaseballCardEditScreen( +fun EditScreen( navController: NavController, db: BaseballCardDatabase, cardId: Long, ) { - val viewModel: BaseballCardDetailsViewModel = viewModel() + val viewModel: DetailsViewModel = viewModel() LaunchedEffect(cardId) { val card = db.baseballCardDao.getBaseballCard(cardId) - viewModel.baseballCardState.value = BaseballCardState(card) + viewModel.detailsState.value = DetailsState(card) } Scaffold( @@ -128,7 +128,7 @@ fun BaseballCardEditScreen( UpdateCardButton( navController, db, - viewModel.baseballCardState, + viewModel.detailsState, viewModel.errors, viewModel::validate, ) @@ -137,8 +137,8 @@ fun BaseballCardEditScreen( .fillMaxSize() .imePadding() ) { innerPadding -> - BaseballCardDetails( - state = viewModel.baseballCardState, + Details( + state = viewModel.detailsState, db = db, errors = viewModel.errors, onValidate = viewModel::validate, @@ -148,10 +148,10 @@ fun BaseballCardEditScreen( } @Composable -fun BaseballCardDetails( - state: MutableState, +fun Details( + state: MutableState, db: BaseballCardDatabase, - errors: MutableState, + errors: MutableState, onValidate: () -> Unit, modifier: Modifier = Modifier, ) { @@ -295,8 +295,8 @@ fun BaseballCardDetails( @Composable fun CreateCardButton( db: BaseballCardDatabase, - state: MutableState, - errors: MutableState, + state: MutableState, + errors: MutableState, onValidate: () -> Unit, onSuccess: () -> Unit, onFailure: () -> Unit, @@ -312,7 +312,7 @@ fun CreateCardButton( onSuccess, onFailure, ) - state.value = BaseballCardState() + state.value = DetailsState() } } }) { @@ -325,7 +325,7 @@ fun CreateCardButton( suspend fun createCard( db: BaseballCardDatabase, - cardState: BaseballCardState, + cardState: DetailsState, onSuccess: () -> Unit, onFailure: () -> Unit, ) { @@ -341,8 +341,8 @@ suspend fun createCard( fun UpdateCardButton( navController: NavController, db: BaseballCardDatabase, - state: MutableState, - errors: MutableState, + state: MutableState, + errors: MutableState, onValidate: () -> Unit, ) { val scope = rememberCoroutineScope() @@ -367,7 +367,7 @@ fun UpdateCardButton( suspend fun updateCard( db: BaseballCardDatabase, - cardState: BaseballCardState, + cardState: DetailsState, ) { db.baseballCardDao.updateBaseballCard(cardState.toBaseballCard()) } diff --git a/app/src/main/java/bbct/android/ui/details/BaseballCardState.kt b/app/src/main/java/bbct/android/ui/details/DetailsState.kt similarity index 97% rename from app/src/main/java/bbct/android/ui/details/BaseballCardState.kt rename to app/src/main/java/bbct/android/ui/details/DetailsState.kt index be17bd349..bcf9f191b 100644 --- a/app/src/main/java/bbct/android/ui/details/BaseballCardState.kt +++ b/app/src/main/java/bbct/android/ui/details/DetailsState.kt @@ -2,7 +2,7 @@ package bbct.android.ui.details import bbct.android.data.BaseballCard -data class BaseballCardState( +data class DetailsState( var id: Long? = null, var autographed: Boolean = false, var condition: String = "", diff --git a/app/src/main/java/bbct/android/ui/details/BaseballCardDetailsViewModel.kt b/app/src/main/java/bbct/android/ui/details/DetailsViewModel.kt similarity index 90% rename from app/src/main/java/bbct/android/ui/details/BaseballCardDetailsViewModel.kt rename to app/src/main/java/bbct/android/ui/details/DetailsViewModel.kt index 58c26c619..3ddc8e2b9 100644 --- a/app/src/main/java/bbct/android/ui/details/BaseballCardDetailsViewModel.kt +++ b/app/src/main/java/bbct/android/ui/details/DetailsViewModel.kt @@ -5,7 +5,7 @@ import androidx.lifecycle.ViewModel data class FormFieldError(var message: String? = null, var isValid: Boolean = true) -data class BaseballCardDetailsErrors( +data class DetailsErrors( var condition: FormFieldError = FormFieldError(), var brand: FormFieldError = FormFieldError(), var year: FormFieldError = FormFieldError(), @@ -22,13 +22,13 @@ data class BaseballCardDetailsErrors( } } -class BaseballCardDetailsViewModel : ViewModel() { - var baseballCardState = mutableStateOf(BaseballCardState()) - var errors = mutableStateOf(BaseballCardDetailsErrors()) +class DetailsViewModel : ViewModel() { + var detailsState = mutableStateOf(DetailsState()) + var errors = mutableStateOf(DetailsErrors()) fun validate() { - val baseballCard = baseballCardState.value - val errors = BaseballCardDetailsErrors() + val baseballCard = detailsState.value + val errors = DetailsErrors() if (baseballCard.condition.isBlank()) { errors.condition = FormFieldError("Condition is required") diff --git a/app/src/main/java/bbct/android/ui/filter/BaseballCardFilterScreen.kt b/app/src/main/java/bbct/android/ui/filter/FilterScreen.kt similarity index 94% rename from app/src/main/java/bbct/android/ui/filter/BaseballCardFilterScreen.kt rename to app/src/main/java/bbct/android/ui/filter/FilterScreen.kt index d1c61aeda..13395ded7 100644 --- a/app/src/main/java/bbct/android/ui/filter/BaseballCardFilterScreen.kt +++ b/app/src/main/java/bbct/android/ui/filter/FilterScreen.kt @@ -27,11 +27,11 @@ import bbct.android.ui.CloseButton import bbct.android.ui.TopBar @Composable -fun BaseballCardFilterScreen( - onApplyFilter: (BaseballCardFilterState) -> Unit, +fun FilterScreen( + onApplyFilter: (FilterState) -> Unit, onClose: () -> Unit, ) { - var filterState = remember { mutableStateOf(BaseballCardFilterState()) } + var filterState = remember { mutableStateOf(FilterState()) } Scaffold( topBar = { TopBar( @@ -50,7 +50,7 @@ fun BaseballCardFilterScreen( modifier = Modifier .imePadding() ) { innerPadding -> - BaseballCardFilter( + Filter( filterState, modifier = Modifier.padding(innerPadding) ) @@ -58,8 +58,8 @@ fun BaseballCardFilterScreen( } @Composable -fun BaseballCardFilter( - filterState: MutableState, +fun Filter( + filterState: MutableState, modifier: Modifier = Modifier, ) { val textFieldModifier = Modifier.fillMaxWidth() diff --git a/app/src/main/java/bbct/android/ui/filter/BaseballCardFilterState.kt b/app/src/main/java/bbct/android/ui/filter/FilterState.kt similarity index 86% rename from app/src/main/java/bbct/android/ui/filter/BaseballCardFilterState.kt rename to app/src/main/java/bbct/android/ui/filter/FilterState.kt index ff66e8868..a45631784 100644 --- a/app/src/main/java/bbct/android/ui/filter/BaseballCardFilterState.kt +++ b/app/src/main/java/bbct/android/ui/filter/FilterState.kt @@ -3,7 +3,7 @@ package bbct.android.ui.filter import kotlinx.serialization.Serializable @Serializable -data class BaseballCardFilterState( +data class FilterState( var brand: String = "", var year: Int = -1, var number: String = "", diff --git a/app/src/main/java/bbct/android/ui/filter/BaseballCardFilterViewModel.kt b/app/src/main/java/bbct/android/ui/filter/FilterViewModel.kt similarity index 50% rename from app/src/main/java/bbct/android/ui/filter/BaseballCardFilterViewModel.kt rename to app/src/main/java/bbct/android/ui/filter/FilterViewModel.kt index d50accadb..14540754e 100644 --- a/app/src/main/java/bbct/android/ui/filter/BaseballCardFilterViewModel.kt +++ b/app/src/main/java/bbct/android/ui/filter/FilterViewModel.kt @@ -4,7 +4,7 @@ import androidx.compose.runtime.MutableState import androidx.compose.runtime.mutableStateOf import androidx.lifecycle.ViewModel -class BaseballCardFilterViewModel : ViewModel() { - val filterState: MutableState = - mutableStateOf(BaseballCardFilterState()) +class FilterViewModel : ViewModel() { + val filterState: MutableState = + mutableStateOf(FilterState()) } diff --git a/app/src/main/java/bbct/android/ui/list/BaseballCardListScreen.kt b/app/src/main/java/bbct/android/ui/list/ListScreen.kt similarity index 91% rename from app/src/main/java/bbct/android/ui/list/BaseballCardListScreen.kt rename to app/src/main/java/bbct/android/ui/list/ListScreen.kt index 7268296ce..01f64333d 100644 --- a/app/src/main/java/bbct/android/ui/list/BaseballCardListScreen.kt +++ b/app/src/main/java/bbct/android/ui/list/ListScreen.kt @@ -40,8 +40,8 @@ import bbct.android.data.BaseballCard import bbct.android.data.BaseballCardDatabase import bbct.android.ui.ListMenu import bbct.android.ui.TopBar -import bbct.android.ui.filter.BaseballCardFilterScreen -import bbct.android.ui.filter.BaseballCardFilterState +import bbct.android.ui.filter.FilterScreen +import bbct.android.ui.filter.FilterState import bbct.android.ui.navigation.AboutDestination import bbct.android.ui.navigation.BaseballCardCreateDestination import bbct.android.ui.navigation.BaseballCardEditDestination @@ -50,26 +50,26 @@ import com.google.android.gms.ads.AdSize import com.google.android.gms.ads.AdView import kotlinx.coroutines.launch -data class BaseballCardSelectedState( +data class SelectedState( var card: BaseballCard, var selected: Boolean, ) @OptIn(ExperimentalMaterial3Api::class) @Composable -fun BaseballCardListScreen( +fun ListScreen( navController: NavController, db: BaseballCardDatabase, ) { val scope = rememberCoroutineScope() - val viewModel: BaseballCardListViewModel = - viewModel(factory = BaseballCardListViewModelFactory(db.baseballCardDao)) + val viewModel: ListViewModel = + viewModel(factory = ListViewModelFactory(db.baseballCardDao)) val cards by viewModel.baseballCards.collectAsStateWithLifecycle(initialValue = emptyList()) val stateList by remember { derivedStateOf { cards .map { card -> - BaseballCardSelectedState( + SelectedState( card, false ) @@ -94,7 +94,7 @@ fun BaseballCardListScreen( isAnySelected = isAnySelected, isFiltered = viewModel.isFiltered.value, onFilterCards = { showFilterBottomSheet = true }, - onClearFilter = { viewModel.applyFilter(BaseballCardFilterState()) }, + onClearFilter = { viewModel.applyFilter(FilterState()) }, onAbout = { navController.navigate(AboutDestination) }, onDeleteCards = { scope.launch { @@ -129,7 +129,7 @@ fun BaseballCardListScreen( onDismissRequest = { showFilterBottomSheet = false }, sheetState = sheetState ) { - BaseballCardFilterScreen( + FilterScreen( onApplyFilter = { filter -> viewModel.applyFilter(filter) showFilterBottomSheet = false @@ -162,7 +162,7 @@ private fun AdBanner(modifier: Modifier = Modifier) { private suspend fun deleteCards( db: BaseballCardDatabase, - cards: List, + cards: List, ) { db.baseballCardDao.deleteBaseballCards( cards @@ -170,7 +170,7 @@ private suspend fun deleteCards( .map { it.card }) } -private fun selectAll(stateList: SnapshotStateList) { +private fun selectAll(stateList: SnapshotStateList) { stateList.forEachIndexed { i, card -> stateList[i] = card.copy(selected = true) } @@ -179,8 +179,8 @@ private fun selectAll(stateList: SnapshotStateList) { @Composable fun BaseballCardList( navController: NavController, - cards: List, - onCardChanged: (Int, BaseballCardSelectedState) -> Unit, + cards: List, + onCardChanged: (Int, SelectedState) -> Unit, modifier: Modifier = Modifier, ) { LazyColumn( @@ -207,7 +207,7 @@ fun BaseballCardList( @Composable fun BaseballCardRow( navController: NavController, - state: BaseballCardSelectedState, + state: SelectedState, onSelectedChange: (Boolean) -> Unit, ) { Row( diff --git a/app/src/main/java/bbct/android/ui/list/BaseballCardListViewModel.kt b/app/src/main/java/bbct/android/ui/list/ListViewModel.kt similarity index 74% rename from app/src/main/java/bbct/android/ui/list/BaseballCardListViewModel.kt rename to app/src/main/java/bbct/android/ui/list/ListViewModel.kt index 18e043388..c095c2b4a 100644 --- a/app/src/main/java/bbct/android/ui/list/BaseballCardListViewModel.kt +++ b/app/src/main/java/bbct/android/ui/list/ListViewModel.kt @@ -7,14 +7,14 @@ import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewmodel.CreationExtras import bbct.android.data.BaseballCard import bbct.android.data.BaseballCardDao -import bbct.android.ui.filter.BaseballCardFilterState +import bbct.android.ui.filter.FilterState import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.first import kotlinx.coroutines.launch -class BaseballCardListViewModel(val baseballCardDao: BaseballCardDao) : ViewModel() { - val filterState = MutableStateFlow(BaseballCardFilterState()) +class ListViewModel(val baseballCardDao: BaseballCardDao) : ViewModel() { + val filterState = MutableStateFlow(FilterState()) val isFiltered = mutableStateOf(false) val baseballCards = MutableStateFlow>(emptyList()) @@ -48,21 +48,21 @@ class BaseballCardListViewModel(val baseballCardDao: BaseballCardDao) : ViewMode ) } - fun applyFilter(filter: BaseballCardFilterState) { + fun applyFilter(filter: FilterState) { filterState.value = filter - isFiltered.value = filter != BaseballCardFilterState() + isFiltered.value = filter != FilterState() } } @Suppress("UNCHECKED_CAST") -class BaseballCardListViewModelFactory(private val baseballCardDao: BaseballCardDao) : +class ListViewModelFactory(private val baseballCardDao: BaseballCardDao) : ViewModelProvider.Factory { override fun create( modelClass: Class, extras: CreationExtras, ): T { - if (modelClass.isAssignableFrom(BaseballCardListViewModel::class.java)) { - return BaseballCardListViewModel(baseballCardDao) as T + if (modelClass.isAssignableFrom(ListViewModel::class.java)) { + return ListViewModel(baseballCardDao) as T } throw IllegalArgumentException("Unknown ViewModel class") } From 61a0ac7f12aadf5840dd73f5eadef8f9da50c21d Mon Sep 17 00:00:00 2001 From: Code-Apprentice Date: Tue, 24 Dec 2024 11:03:15 -0700 Subject: [PATCH 273/279] Rename tests --- ...aseballCardCreateTest.kt => CreateTest.kt} | 22 +++++++--- .../{BaseballCardEditTest.kt => EditTest.kt} | 2 +- ...aseballCardFilterTest.kt => FilterTest.kt} | 2 +- ...ardListScreenTest.kt => ListScreenTest.kt} | 44 ++++++++++++++----- 4 files changed, 52 insertions(+), 18 deletions(-) rename app/src/androidTest/java/bbct/android/ui/details/{BaseballCardCreateTest.kt => CreateTest.kt} (85%) rename app/src/androidTest/java/bbct/android/ui/details/{BaseballCardEditTest.kt => EditTest.kt} (97%) rename app/src/androidTest/java/bbct/android/ui/filter/{BaseballCardFilterTest.kt => FilterTest.kt} (98%) rename app/src/androidTest/java/bbct/android/ui/list/{BaseballCardListScreenTest.kt => ListScreenTest.kt} (53%) diff --git a/app/src/androidTest/java/bbct/android/ui/details/BaseballCardCreateTest.kt b/app/src/androidTest/java/bbct/android/ui/details/CreateTest.kt similarity index 85% rename from app/src/androidTest/java/bbct/android/ui/details/BaseballCardCreateTest.kt rename to app/src/androidTest/java/bbct/android/ui/details/CreateTest.kt index 836706821..22ca87975 100644 --- a/app/src/androidTest/java/bbct/android/ui/details/BaseballCardCreateTest.kt +++ b/app/src/androidTest/java/bbct/android/ui/details/CreateTest.kt @@ -16,7 +16,7 @@ import kotlinx.coroutines.flow.first import kotlinx.coroutines.launch import org.junit.Test -class BaseballCardCreateTest : TestBase() { +class CreateTest : TestBase() { @Test fun testBaseballCardDetailsSaveCard() { val card = BaseballCard( @@ -33,11 +33,17 @@ class BaseballCardCreateTest : TestBase() { ) val context = InstrumentationRegistry.getInstrumentation().targetContext - val db = inMemoryDatabaseBuilder(context, BaseballCardDatabase::class.java).build() + val db = inMemoryDatabaseBuilder( + context, + BaseballCardDatabase::class.java + ).build() composeTestRule.setContent { val navController = rememberNavController() - CreateScreen(navController, db) + CreateScreen( + navController, + db + ) } enterCardData(card) @@ -64,11 +70,17 @@ class BaseballCardCreateTest : TestBase() { ) val context = InstrumentationRegistry.getInstrumentation().targetContext - val db = inMemoryDatabaseBuilder(context, BaseballCardDatabase::class.java).build() + val db = inMemoryDatabaseBuilder( + context, + BaseballCardDatabase::class.java + ).build() composeTestRule.setContent { val navController = rememberNavController() - CreateScreen(navController, db) + CreateScreen( + navController, + db + ) } enterCardData(card) diff --git a/app/src/androidTest/java/bbct/android/ui/details/BaseballCardEditTest.kt b/app/src/androidTest/java/bbct/android/ui/details/EditTest.kt similarity index 97% rename from app/src/androidTest/java/bbct/android/ui/details/BaseballCardEditTest.kt rename to app/src/androidTest/java/bbct/android/ui/details/EditTest.kt index 7a1d0cd13..93a7d46df 100644 --- a/app/src/androidTest/java/bbct/android/ui/details/BaseballCardEditTest.kt +++ b/app/src/androidTest/java/bbct/android/ui/details/EditTest.kt @@ -12,7 +12,7 @@ import kotlinx.coroutines.launch import kotlinx.coroutines.test.runTest import org.junit.Test -class BaseballCardEditTest : TestBase() { +class EditTest : TestBase() { @Test fun testBaseballCardEdit() { runTest { diff --git a/app/src/androidTest/java/bbct/android/ui/filter/BaseballCardFilterTest.kt b/app/src/androidTest/java/bbct/android/ui/filter/FilterTest.kt similarity index 98% rename from app/src/androidTest/java/bbct/android/ui/filter/BaseballCardFilterTest.kt rename to app/src/androidTest/java/bbct/android/ui/filter/FilterTest.kt index e147c3429..4f282b2aa 100644 --- a/app/src/androidTest/java/bbct/android/ui/filter/BaseballCardFilterTest.kt +++ b/app/src/androidTest/java/bbct/android/ui/filter/FilterTest.kt @@ -11,7 +11,7 @@ import org.junit.Before import org.junit.Rule import org.junit.Test -class BaseballCardFilterTest { +class FilterTest { @get:Rule val composeTestRule = createComposeRule() diff --git a/app/src/androidTest/java/bbct/android/ui/list/BaseballCardListScreenTest.kt b/app/src/androidTest/java/bbct/android/ui/list/ListScreenTest.kt similarity index 53% rename from app/src/androidTest/java/bbct/android/ui/list/BaseballCardListScreenTest.kt rename to app/src/androidTest/java/bbct/android/ui/list/ListScreenTest.kt index 852f23a98..a3cfc4417 100644 --- a/app/src/androidTest/java/bbct/android/ui/list/BaseballCardListScreenTest.kt +++ b/app/src/androidTest/java/bbct/android/ui/list/ListScreenTest.kt @@ -15,7 +15,7 @@ import org.junit.Before import org.junit.Rule import org.junit.Test -class BaseballCardListScreenTest { +class ListScreenTest { val context = InstrumentationRegistry.getInstrumentation().targetContext val db = inMemoryDatabaseBuilder( context, @@ -26,29 +26,51 @@ class BaseballCardListScreenTest { val composeTestRule = createComposeRule() @get:Rule - val cardDatabaseTestRule = CardDatabaseTestRule("cards.csv", db) + val cardDatabaseTestRule = CardDatabaseTestRule( + "cards.csv", + db + ) @Before fun setup() { composeTestRule.setContent { var navController = rememberNavController() - ListScreen(navController, db) + ListScreen( + navController, + db + ) } } @Test fun testFilterCardsByYear() { - composeTestRule.onNodeWithContentDescription("Filter Cards").performClick() - composeTestRule.onNodeWithText("Filter Cards").assertIsDisplayed() - composeTestRule.onNodeWithText("Year").performTextInput("1993") - composeTestRule.onNodeWithContentDescription("Apply Filter").performClick() + composeTestRule + .onNodeWithContentDescription("Filter Cards") + .performClick() + composeTestRule + .onNodeWithText("Filter Cards") + .assertIsDisplayed() + composeTestRule + .onNodeWithText("Year") + .performTextInput("1993") + composeTestRule + .onNodeWithContentDescription("Apply Filter") + .performClick() val filteredCards = cardDatabaseTestRule.cards!!.filter { it.year == 1993 } for (card in filteredCards) { - composeTestRule.onNodeWithText(card.brand).assertIsDisplayed() - composeTestRule.onNodeWithText(card.year.toString()).assertIsDisplayed() - composeTestRule.onNodeWithText(card.number).assertIsDisplayed() - composeTestRule.onNodeWithText(card.playerName).assertIsDisplayed() + composeTestRule + .onNodeWithText(card.brand) + .assertIsDisplayed() + composeTestRule + .onNodeWithText(card.year.toString()) + .assertIsDisplayed() + composeTestRule + .onNodeWithText(card.number) + .assertIsDisplayed() + composeTestRule + .onNodeWithText(card.playerName) + .assertIsDisplayed() } } } From ca7ec2b01bee25eb80c0ee192037e9f8cb7aa1ac Mon Sep 17 00:00:00 2001 From: Code-Apprentice Date: Tue, 24 Dec 2024 11:34:18 -0700 Subject: [PATCH 274/279] Fix compiler error in test --- app/src/androidTest/java/bbct/android/test/TestBase.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/androidTest/java/bbct/android/test/TestBase.kt b/app/src/androidTest/java/bbct/android/test/TestBase.kt index 76fdb8572..3fca475b8 100644 --- a/app/src/androidTest/java/bbct/android/test/TestBase.kt +++ b/app/src/androidTest/java/bbct/android/test/TestBase.kt @@ -38,7 +38,7 @@ abstract class TestBase { composeTestRule .onNodeWithText("Value") .assertIsDisplayed() - .performTextReplacement((card.value / 100.0f).toString()) + .performTextReplacement((card.value!! / 100.0f).toString()) composeTestRule .onNodeWithText("Quantity") .assertIsDisplayed() From c4d1d9e05286c04957818afb452ee4eaf3a929b8 Mon Sep 17 00:00:00 2001 From: Code-Apprentice Date: Tue, 24 Dec 2024 16:53:06 -0700 Subject: [PATCH 275/279] Fix refresh bug --- app/src/main/java/bbct/android/ui/list/ListViewModel.kt | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/bbct/android/ui/list/ListViewModel.kt b/app/src/main/java/bbct/android/ui/list/ListViewModel.kt index c095c2b4a..c5585ae8f 100644 --- a/app/src/main/java/bbct/android/ui/list/ListViewModel.kt +++ b/app/src/main/java/bbct/android/ui/list/ListViewModel.kt @@ -10,24 +10,23 @@ import bbct.android.data.BaseballCardDao import bbct.android.ui.filter.FilterState import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.first import kotlinx.coroutines.launch class ListViewModel(val baseballCardDao: BaseballCardDao) : ViewModel() { val filterState = MutableStateFlow(FilterState()) val isFiltered = mutableStateOf(false) - val baseballCards = MutableStateFlow>(emptyList()) + var baseballCards = MutableStateFlow>(emptyList()) init { viewModelScope.launch { filterState.collect { filter -> - baseballCards.value = getBaseballCards( + getBaseballCards( filter.brand, filter.year, filter.number, filter.playerName, filter.team - ).first() + ).collect { baseballCards.value = it } } } } From 90005afb3a701a138207308018c60d34a07276e7 Mon Sep 17 00:00:00 2001 From: Code-Apprentice Date: Tue, 24 Dec 2024 17:31:19 -0700 Subject: [PATCH 276/279] Fix filter bug...and maintain refresh list --- .../bbct/android/ui/list/ListViewModel.kt | 23 +++++++++++-------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/app/src/main/java/bbct/android/ui/list/ListViewModel.kt b/app/src/main/java/bbct/android/ui/list/ListViewModel.kt index c5585ae8f..dd6587679 100644 --- a/app/src/main/java/bbct/android/ui/list/ListViewModel.kt +++ b/app/src/main/java/bbct/android/ui/list/ListViewModel.kt @@ -13,20 +13,13 @@ import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.launch class ListViewModel(val baseballCardDao: BaseballCardDao) : ViewModel() { - val filterState = MutableStateFlow(FilterState()) val isFiltered = mutableStateOf(false) var baseballCards = MutableStateFlow>(emptyList()) init { viewModelScope.launch { - filterState.collect { filter -> - getBaseballCards( - filter.brand, - filter.year, - filter.number, - filter.playerName, - filter.team - ).collect { baseballCards.value = it } + baseballCardDao.baseballCards.collect { + baseballCards.value = it } } } @@ -48,7 +41,17 @@ class ListViewModel(val baseballCardDao: BaseballCardDao) : ViewModel() { } fun applyFilter(filter: FilterState) { - filterState.value = filter + viewModelScope.launch { + getBaseballCards( + filter.brand, + filter.year, + filter.number, + filter.playerName, + filter.team + ).collect { + baseballCards.value = it + } + } isFiltered.value = filter != FilterState() } } From cd809d6d1b86ded50e0f566ccc801ae0a2d8deab Mon Sep 17 00:00:00 2001 From: Code-Apprentice Date: Tue, 24 Dec 2024 19:02:45 -0700 Subject: [PATCH 277/279] Fix flow inefficiencies --- .../bbct/android/ui/list/ListViewModel.kt | 31 ++++++++++--------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/app/src/main/java/bbct/android/ui/list/ListViewModel.kt b/app/src/main/java/bbct/android/ui/list/ListViewModel.kt index dd6587679..b72e8892d 100644 --- a/app/src/main/java/bbct/android/ui/list/ListViewModel.kt +++ b/app/src/main/java/bbct/android/ui/list/ListViewModel.kt @@ -1,6 +1,7 @@ package bbct.android.ui.list import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.snapshotFlow import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.viewModelScope @@ -8,19 +9,31 @@ import androidx.lifecycle.viewmodel.CreationExtras import bbct.android.data.BaseballCard import bbct.android.data.BaseballCardDao import bbct.android.ui.filter.FilterState +import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.flatMapLatest import kotlinx.coroutines.launch +@OptIn(ExperimentalCoroutinesApi::class) class ListViewModel(val baseballCardDao: BaseballCardDao) : ViewModel() { + val filterState = mutableStateOf(FilterState()) val isFiltered = mutableStateOf(false) var baseballCards = MutableStateFlow>(emptyList()) init { viewModelScope.launch { - baseballCardDao.baseballCards.collect { - baseballCards.value = it - } + snapshotFlow { + filterState.value + }.flatMapLatest { filter -> + getBaseballCards( + filter.brand, + filter.year, + filter.number, + filter.playerName, + filter.team + ) + }.collect { baseballCards.value = it } } } @@ -41,17 +54,7 @@ class ListViewModel(val baseballCardDao: BaseballCardDao) : ViewModel() { } fun applyFilter(filter: FilterState) { - viewModelScope.launch { - getBaseballCards( - filter.brand, - filter.year, - filter.number, - filter.playerName, - filter.team - ).collect { - baseballCards.value = it - } - } + filterState.value = filter isFiltered.value = filter != FilterState() } } From 46a869839b0786fa1a0a9d54b1581cb082500971 Mon Sep 17 00:00:00 2001 From: Code-Apprentice Date: Tue, 24 Dec 2024 23:56:29 -0700 Subject: [PATCH 278/279] Update copyright years --- app/src/main/res/values/strings.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 267ab68a2..a4172f5cc 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -63,7 +63,7 @@ Version %1$s - Copyright 2012–24 by codeguru + Copyright 2012–25 by codeguru Email: <bbct-list@lists.sourceforge.net> Website: <https://github.com/BaseballCardTracker/bbct/wiki> This software is licensed under the GPL. Visit\nhttp://www.gnu.org/licenses/ for a copy of the license. From 3556814de2421b4419579bdf58e96dd1a63550d3 Mon Sep 17 00:00:00 2001 From: Code-Apprentice Date: Tue, 24 Dec 2024 23:58:05 -0700 Subject: [PATCH 279/279] Fix build pipeline --- .github/workflows/android.yml | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/.github/workflows/android.yml b/.github/workflows/android.yml index 07a213cec..ea273849e 100644 --- a/.github/workflows/android.yml +++ b/.github/workflows/android.yml @@ -10,14 +10,14 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 - - name: set up JDK - uses: actions/setup-java@v1 - with: - java-version: 17 - - name: Decode google-services.json - env: - FIREBASE_CONFIG: ${{ secrets.FIREBASE_CONFIG }} - run: echo $FIREBASE_CONFIG > android/google-services.json - - name: Build with Gradle - run: ./gradlew android:bundleDebug + - uses: actions/checkout@v2 + - name: set up JDK + uses: actions/setup-java@v1 + with: + java-version: 17 + - name: Decode google-services.json + env: + FIREBASE_CONFIG: ${{ secrets.FIREBASE_CONFIG }} + run: echo $FIREBASE_CONFIG > app/google-services.json + - name: Build with Gradle + run: ./gradlew app:bundleDebug