From 5efb5e92441afb89c4c1f29edaf86760288b275f Mon Sep 17 00:00:00 2001 From: Rods Date: Mon, 2 Dec 2024 13:18:13 -0300 Subject: [PATCH 01/96] [ISSUE-1] - add ktor in the .toml and update gradle --- gradle/libs.versions.toml | 12 +- gradle/wrapper/gradle-wrapper.jar | Bin 59203 -> 43504 bytes gradle/wrapper/gradle-wrapper.properties | 7 +- gradlew | 285 ++++++++++++++--------- gradlew.bat | 37 +-- 5 files changed, 209 insertions(+), 132 deletions(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 865020d6..00e76027 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -34,6 +34,7 @@ androidx_core_ktx = "1.13.1" moshi = "1.14.0" okhttp = "4.12.0" retrofit = "2.9.0" +ktor = "3.0.1" #Compose compose = "1.5.15" @@ -58,6 +59,7 @@ kotlin_gradle_plugin = { group = "org.jetbrains.kotlin", name = "kotlin-gradle-p android_gradle_plugin = { group = "com.android.tools.build", name = "gradle", version.ref = "android_gradle_plugin" } detekt-gradle-plugin = { module = "io.gitlab.arturbosch.detekt:detekt-gradle-plugin", version.ref = "detekt" } + #Kover kover-gradle-plugin = { module = "org.jetbrains.kotlinx:kover-gradle-plugin", version.ref = "kover" } @@ -126,7 +128,10 @@ moshi = { group = "com.squareup.moshi", name = "moshi-kotlin", version.ref = "mo moshi_converter = { group = "com.squareup.retrofit2", name = "converter-moshi", version.ref = "retrofit" } okhttp = { group = "com.squareup.okhttp3", name = "okhttp", version.ref = "okhttp" } interceptor = { group = "com.squareup.okhttp3", name = "logging-interceptor", version.ref = "okhttp" } -compose-material = { group = "androidx.wear.compose", name = "compose-material", version = "1.2.0" } +ktor_client_core = { module = "io.ktor:ktor-client-core", version.ref = "ktor" } +ktor_client_okhttp = { module = "io.ktor:ktor-client-okhttp", version.ref = "ktor" } +ktor_client_content_negotiation = { module = "io.ktor:ktor-client-content-negotiation", version.ref = "ktor" } +ktor_client_content_serialization_gson = { module = "io.ktor:ktor-serialization-gson", version.ref = "ktor" } #Room roomRuntime = { group = "androidx.room", name = "room-runtime", version.ref = "room"} @@ -140,7 +145,7 @@ dokka = { group = "org.jetbrains.dokka", name = "android-documentation-plugin", room = ["roomRuntime","roomKtx"] compose = ["compose.ui", "compose.icons", "compose.material3","compose_pagging", "compose.lifecycle", "compose.navigation", "compose.activity", "compose.ui.tooling"] composetest = ["compose.uitest", "compose.junit4", "compose.manifest", "compose.ui.test"] -networking = ["retrofit", "moshi", "moshi_converter", "okhttp", "interceptor"] +networking = ["retrofit", "moshi", "moshi_converter", "okhttp", "interceptor","ktor_client_core","ktor_client_okhttp","ktor_client_content_serialization_gson","ktor_client_content_negotiation"] koin = ["koin_android", "koin_compose"] test = ["junit", "mockk", "mockk_android", "viewmodel_test", "koin_test","coroutines_test"] androidSupport = ["androidx_core", "androidx_appcompat", "androidx_dynamicanimation","google_material"] @@ -151,8 +156,7 @@ android_application = { id = "com.android.application", version.ref = "android_g android_library = { id = "com.android.library", version.ref = "android_gradle_plugin" } kotlin_android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" } -kotlin_kapt = { id = "org.jetbrains.kotlin.kapt", version.ref = "kotlin" } -kotlin_parcelize = { id = "org.jetbrains.kotlin.plugin.parcelize", version.ref = "kotlin" } + ksp = { id = "com.google.devtools.ksp", version.ref = "ksp"} dokka = { id = "org.jetbrains.dokka", version.ref = "dokka"} diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index e708b1c023ec8b20f512888fe07c5bd3ff77bb8f..2c3521197d7c4586c843d1d3e9090525f1898cde 100644 GIT binary patch literal 43504 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 yu?e%!_ptzGB|8GrCfu%p?(_ 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 TZiJi*F`$tkWLn) literal 59203 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

K7UpUK$>l@ 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 E2O(~oYXATM diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 30be1904..09523c0e 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,7 @@ -#Sat Apr 08 14:40:45 BRT 2023 distributionBase=GRADLE_USER_HOME -distributionUrl=https\://services.gradle.org/distributions/gradle-8.2-bin.zip distributionPath=wrapper/dists -zipStorePath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-8.9-bin.zip +networkTimeout=10000 +validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew index 4f906e0c..f5feea6d 100755 --- a/gradlew +++ b/gradlew @@ -1,7 +1,7 @@ -#!/usr/bin/env sh +#!/bin/sh # -# Copyright 2015 the original author or authors. +# Copyright © 2015-2021 the original authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -15,69 +15,104 @@ # See the License for the specific language governing permissions and # limitations under the License. # +# SPDX-License-Identifier: Apache-2.0 +# ############################################################################## -## -## Gradle start up script for UN*X -## +# +# Gradle start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# ############################################################################## # 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 +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac 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='"-Xmx64m" "-Xms64m"' +# This is normally unused +# shellcheck disable=SC2034 +APP_BASE_NAME=${0##*/} +# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s +' "$PWD" ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. -MAX_FD="maximum" +MAX_FD=maximum warn () { echo "$*" -} +} >&2 die () { echo echo "$*" echo exit 1 -} +} >&2 # 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 - ;; +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; esac CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar @@ -87,9 +122,9 @@ CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 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" + JAVACMD=$JAVA_HOME/jre/sh/java else - JAVACMD="$JAVA_HOME/bin/java" + JAVACMD=$JAVA_HOME/bin/java fi if [ ! -x "$JAVACMD" ] ; then die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME @@ -98,88 +133,120 @@ 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. + JAVACMD=java + if ! command -v java >/dev/null 2>&1 + then + 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 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 +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac 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 +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. # For Cygwin or MSYS, switch paths to Windows format before running java -if [ "$cygwin" = "true" -o "$msys" = "true" ] ; 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 +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + # 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\"" + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) fi - i=`expr $i + 1` + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg 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 -# Escape application args -save () { - for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done - echo " " -} -APP_ARGS=`save "$@"` -# Collect all arguments for the java command, following the shell quoting and substitution rules -eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Collect all arguments for the java command: +# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, +# and any embedded shellness will be escaped. +# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be +# treated as '${Hostname}' itself on the command line. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" + +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" +fi + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat index 107acd32..9d21a218 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -13,8 +13,10 @@ @rem See the License for the specific language governing permissions and @rem limitations under the License. @rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem -@if "%DEBUG%" == "" @echo off +@if "%DEBUG%"=="" @echo off @rem ########################################################################## @rem @rem Gradle startup script for Windows @@ -25,7 +27,8 @@ if "%OS%"=="Windows_NT" setlocal set DIRNAME=%~dp0 -if "%DIRNAME%" == "" set DIRNAME=. +if "%DIRNAME%"=="" set DIRNAME=. +@rem This is normally unused set APP_BASE_NAME=%~n0 set APP_HOME=%DIRNAME% @@ -40,13 +43,13 @@ if defined JAVA_HOME goto findJavaFromJavaHome set JAVA_EXE=java.exe %JAVA_EXE% -version >NUL 2>&1 -if "%ERRORLEVEL%" == "0" goto execute +if %ERRORLEVEL% equ 0 goto execute -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. +echo. 1>&2 +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 goto fail @@ -56,11 +59,11 @@ set JAVA_EXE=%JAVA_HOME%/bin/java.exe if exist "%JAVA_EXE%" goto execute -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. +echo. 1>&2 +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 goto fail @@ -75,13 +78,15 @@ set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar :end @rem End local scope for the variables with windows NT shell -if "%ERRORLEVEL%"=="0" goto mainEnd +if %ERRORLEVEL% equ 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 +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% :mainEnd if "%OS%"=="Windows_NT" endlocal From 057c4b27f56d4f69bb645b74106a6301d331e94f Mon Sep 17 00:00:00 2001 From: Rods Date: Tue, 3 Dec 2024 18:22:14 -0300 Subject: [PATCH 02/96] [ISSUE-1] - implementing httpclient ktor --- build-logic/build.gradle.kts | 1 + build-logic/src/main/java/Config.kt | 4 +- build-logic/src/main/java/Keys.kt | 4 +- ...om.streamplayer.android-library.gradle.kts | 1 + build.gradle.kts | 1 + .../coroutines/NetworkResponseAdapter.kt | 15 -- .../NetworkResponseAdapterFactory.kt | 50 ------ .../coroutines/NetworkResponseCall.kt | 158 ---------------- .../core_networking/di/NetworkModule.kt | 170 ++++++++++-------- .../core_networking/di/QualifierNetworking.kt | 2 +- .../core_networking/handleError/Failure.kt | 5 +- .../handleError/HttpClientConfigExtensions.kt | 72 ++++++++ .../handleError/NetworkResponse.kt | 6 +- .../detail/data/DetailStreamService.kt | 29 ++- .../detail/data/model/DetailStreamResponse.kt | 3 + .../detail/data/model/VideoStreamResponse.kt | 4 + .../list/data/ListStreamService.kt | 69 +++++-- .../list/data/model/GenresResponse.kt | 4 + .../list/data/model/ListStreamResponse.kt | 16 +- .../list/domain/ListStreamMapper.kt | 6 +- .../data/api/MostPopularMoviesService.kt | 16 +- .../search/data/api/SearchStreamService.kt | 19 +- .../data/model/ListSearchStreamResponse.kt | 3 + .../data/ProfilePickerStreamService.kt | 16 +- .../data/model/ProfileStreamResponse.kt | 4 + gradle/libs.versions.toml | 39 ++-- 26 files changed, 347 insertions(+), 370 deletions(-) delete mode 100644 core-networking/src/main/java/com/codandotv/streamplayerapp/core_networking/coroutines/NetworkResponseAdapter.kt delete mode 100644 core-networking/src/main/java/com/codandotv/streamplayerapp/core_networking/coroutines/NetworkResponseAdapterFactory.kt delete mode 100644 core-networking/src/main/java/com/codandotv/streamplayerapp/core_networking/coroutines/NetworkResponseCall.kt create mode 100644 core-networking/src/main/java/com/codandotv/streamplayerapp/core_networking/handleError/HttpClientConfigExtensions.kt diff --git a/build-logic/build.gradle.kts b/build-logic/build.gradle.kts index eaf67121..b47ba3a7 100644 --- a/build-logic/build.gradle.kts +++ b/build-logic/build.gradle.kts @@ -15,4 +15,5 @@ dependencies { implementation(libs.kotlin.gradle.plugin) implementation(libs.kover.gradle.plugin) implementation(libs.detekt.gradle.plugin) + implementation(libs.serialization) } \ No newline at end of file diff --git a/build-logic/src/main/java/Config.kt b/build-logic/src/main/java/Config.kt index 2ba49cd2..4c699b1a 100644 --- a/build-logic/src/main/java/Config.kt +++ b/build-logic/src/main/java/Config.kt @@ -18,7 +18,7 @@ object Config { private const val bearear_without_environment = "eyJhbGciOiJIUzI1NiJ9.eyJhdWQiOiJiNDg2NWM4YTAzNzhmM2I4NjI0OWU1ZjNiYWFiMjU2NyIsInN1YiI6IjY0Mjk4YTg5YTNlNGJhMWM0NDgzM2U4OCIsInNjb3BlcyI6WyJhcGlfcmVhZCJdLCJ2ZXJzaW9uIjoxfQ.9cIxv29vkaZ2yW88DIFRUFK_nXbK2b6KS8t96kA8WAE" - val api_bearer_debug = "\"Bearer ${System.getenv(tmdb_token_name_debug) ?: bearear_without_environment}\"" - val api_bearer_release = "\"Bearer ${System.getenv(tmdb_token_name_release) ?: bearear_without_environment}\"" + val api_bearer_debug = "\"${System.getenv(tmdb_token_name_debug) ?: bearear_without_environment}\"" + val api_bearer_release = "\"${System.getenv(tmdb_token_name_release) ?: bearear_without_environment}\"" } } \ No newline at end of file diff --git a/build-logic/src/main/java/Keys.kt b/build-logic/src/main/java/Keys.kt index 4475b22e..8408abb8 100644 --- a/build-logic/src/main/java/Keys.kt +++ b/build-logic/src/main/java/Keys.kt @@ -5,8 +5,8 @@ object Keys { object BuildField { val api_bearer_debug = - "\"Bearer ${System.getenv(tmdb_token_name_debug) ?: default_tmdb_token}\"" + "\"${System.getenv(tmdb_token_name_debug) ?: default_tmdb_token}\"" val api_bearer_release = - "\"Bearer ${System.getenv(tmdb_token_name_release) ?: default_tmdb_token}\"" + "\"${System.getenv(tmdb_token_name_release) ?: default_tmdb_token}\"" } } \ No newline at end of file diff --git a/build-logic/src/main/java/com.streamplayer.android-library.gradle.kts b/build-logic/src/main/java/com.streamplayer.android-library.gradle.kts index 6b0ea5af..9423e906 100644 --- a/build-logic/src/main/java/com.streamplayer.android-library.gradle.kts +++ b/build-logic/src/main/java/com.streamplayer.android-library.gradle.kts @@ -17,6 +17,7 @@ plugins { id("com.streamplayer.dokka") id("org.jetbrains.kotlinx.kover") id("com.streamplayer.detekt") + id("org.jetbrains.kotlin.plugin.serialization") } android { diff --git a/build.gradle.kts b/build.gradle.kts index df8846e7..2eb41b1b 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -5,6 +5,7 @@ plugins { alias(libs.plugins.android.application) apply false alias(libs.plugins.android.library) apply false alias(libs.plugins.kotlin.android) apply false + alias(libs.plugins.serialization) apply false alias(libs.plugins.ksp) apply false alias(libs.plugins.dokka) apply false alias(libs.plugins.kover) apply false diff --git a/core-networking/src/main/java/com/codandotv/streamplayerapp/core_networking/coroutines/NetworkResponseAdapter.kt b/core-networking/src/main/java/com/codandotv/streamplayerapp/core_networking/coroutines/NetworkResponseAdapter.kt deleted file mode 100644 index eafeb5b9..00000000 --- a/core-networking/src/main/java/com/codandotv/streamplayerapp/core_networking/coroutines/NetworkResponseAdapter.kt +++ /dev/null @@ -1,15 +0,0 @@ -package com.codandotv.streamplayerapp.core_networking.coroutines - -import com.squareup.moshi.Moshi -import retrofit2.Call -import retrofit2.CallAdapter -import java.lang.reflect.Type - -class NetworkResponseAdapter( - private val responseType: Type, - private val moshi: Moshi -): CallAdapter { - - override fun responseType(): Type = responseType - override fun adapt(call: Call) = NetworkResponseCall(call,moshi) -} \ No newline at end of file diff --git a/core-networking/src/main/java/com/codandotv/streamplayerapp/core_networking/coroutines/NetworkResponseAdapterFactory.kt b/core-networking/src/main/java/com/codandotv/streamplayerapp/core_networking/coroutines/NetworkResponseAdapterFactory.kt deleted file mode 100644 index d2626078..00000000 --- a/core-networking/src/main/java/com/codandotv/streamplayerapp/core_networking/coroutines/NetworkResponseAdapterFactory.kt +++ /dev/null @@ -1,50 +0,0 @@ -package com.codandotv.streamplayerapp.core_networking.coroutines - -import com.codandotv.streamplayerapp.core_networking.handleError.NetworkResponse -import com.squareup.moshi.Moshi -import retrofit2.Call -import retrofit2.CallAdapter -import retrofit2.Retrofit -import java.lang.reflect.ParameterizedType -import java.lang.reflect.Type - -@Suppress("ReturnCount", "SwallowedException") -class NetworkResponseAdapterFactory(private val moshi: Moshi) : CallAdapter.Factory() { - override fun get( - returnType: Type, - annotations: Array, - retrofit: Retrofit - ): CallAdapter<*, *>? { - return try { - // suspend functions wrap the response type in `Call` - if (Call::class.java != getRawType(returnType)) { - return null - } - - // check first that the return type is `ParameterizedType` - check(returnType is ParameterizedType) { - "return type must be parameterized as Call> or Call>" - } - - // get the response type inside the `Call` type - val responseType = getParameterUpperBound(0, returnType) - - // if the response type is not ApiResponse then we can't handle this type, so we return null - if (getRawType(responseType) != NetworkResponse::class.java) { - return null - } - - // the response type is ApiResponse and should be parameterized - check(responseType is ParameterizedType) { - "Response must be parameterized as NetworkResponse " + - "or NetworkResponse" - } - - val successBodyType = getParameterUpperBound(0, responseType) - - return NetworkResponseAdapter(successBodyType, moshi) - } catch (ex: ClassCastException) { - null - } - } -} \ No newline at end of file diff --git a/core-networking/src/main/java/com/codandotv/streamplayerapp/core_networking/coroutines/NetworkResponseCall.kt b/core-networking/src/main/java/com/codandotv/streamplayerapp/core_networking/coroutines/NetworkResponseCall.kt deleted file mode 100644 index 710ad937..00000000 --- a/core-networking/src/main/java/com/codandotv/streamplayerapp/core_networking/coroutines/NetworkResponseCall.kt +++ /dev/null @@ -1,158 +0,0 @@ -package com.codandotv.streamplayerapp.core_networking.coroutines - -import com.codandotv.streamplayerapp.core_networking.handleError.Failure -import com.codandotv.streamplayerapp.core_networking.handleError.NetworkResponse -import com.squareup.moshi.Moshi -import okhttp3.Request -import okhttp3.ResponseBody -import okio.Timeout -import retrofit2.Call -import retrofit2.Callback -import retrofit2.HttpException -import retrofit2.Response -import java.io.IOException - -@Suppress("SwallowedException", "TooGenericExceptionCaught") -class NetworkResponseCall( - proxy: Call, - private val moshi: Moshi -) : - CallDelegate>(proxy) { - - override fun enqueueImpl(callback: Callback>) = - proxy.enqueue(object : Callback { - override fun onResponse(call: Call, response: Response) { - val body = response.body() - val code = response.code() - val error = response.errorBody() - if (response.isSuccessful) { - responseSuccessful(body, callback, code) - } else { - responseError(callback,error, code) - } - } - - override fun onFailure(call: Call, throwable: Throwable) { - val networkResponse = when (throwable) { - is IOException -> Failure.NetworkError(throwable = throwable) - is HttpException -> convertException(throwable) - else -> Failure.GenericError(msg = throwable.message) - } - callback.onResponse( - this@NetworkResponseCall, - Response.success(NetworkResponse.Error(exception = networkResponse)) - ) - } - - - private fun convertException(exception: HttpException): Failure { - return try { - val response = getErrorResponse(exception) - Failure.UnexpectedApiException(throwable = response.exception) - } catch (ex: Failure.ClientException) { - ex - }catch (ex : Failure.UnparsableResponseException){ - ex - } - } - - private fun getErrorResponse(ex: HttpException): NetworkResponse.Error { - val error = ex.response()?.errorBody()?.string() - if (error?.isEmpty() != false) { - throw Failure.ClientException(throwable = ex) - } - return parseError(error, ex) - } - - private fun parseError(error: String, ex: HttpException): NetworkResponse.Error { - try { - return moshi - .adapter(NetworkResponse.Error::class.java) - .fromJson(error)!! - } catch (e: Exception) { - throw Failure.UnparsableResponseException(throwable = ex) - } - } - }) - - private fun responseSuccessful( - body: T?, - callback: Callback>, - code: Int - ) { - if (body != null) { - callback.onResponse( - this@NetworkResponseCall, - Response.success(NetworkResponse.Success(body)) - ) - } else { - callback.onResponse( - this@NetworkResponseCall, - Response.success( - NetworkResponse.Error( - exception = Failure.NoDataContent( - code - ) - ) - ) - ) - } - } - - private fun responseError(callback: Callback>, error: ResponseBody?, code: Int) { - val errorBody = when { - error == null -> null - error.contentLength() == 0L -> null - else -> try { - moshi - .adapter(NetworkResponse.Error::class.java) - .fromJson(error.string()) - } catch (ex: Exception) { - null - } - } - if (errorBody != null) { - callback.onResponse( - this@NetworkResponseCall, - Response.success( - NetworkResponse.Error( - body = errorBody, - exception = Failure.ServerError(code) - ) - ) - ) - } else { - callback.onResponse( - this@NetworkResponseCall, - Response.success( - NetworkResponse.Error( - exception = Failure.UnknownError( - code - ) - ) - ) - ) - } - } - - - override fun cloneImpl(): Call> = - NetworkResponseCall(proxy.clone(), moshi) -} - -abstract class CallDelegate( - protected val proxy: Call -) : Call { - override fun execute(): Response = throw NotImplementedError() - final override fun enqueue(callback: Callback) = enqueueImpl(callback) - final override fun clone(): Call = cloneImpl() - - override fun cancel() = proxy.cancel() - override fun request(): Request = proxy.request() - override fun isExecuted() = proxy.isExecuted - override fun isCanceled() = proxy.isCanceled - - abstract fun enqueueImpl(callback: Callback) - abstract fun cloneImpl(): Call - override fun timeout(): Timeout = proxy.timeout() -} \ No newline at end of file diff --git a/core-networking/src/main/java/com/codandotv/streamplayerapp/core_networking/di/NetworkModule.kt b/core-networking/src/main/java/com/codandotv/streamplayerapp/core_networking/di/NetworkModule.kt index 7b0dc322..63535ec9 100644 --- a/core-networking/src/main/java/com/codandotv/streamplayerapp/core_networking/di/NetworkModule.kt +++ b/core-networking/src/main/java/com/codandotv/streamplayerapp/core_networking/di/NetworkModule.kt @@ -1,103 +1,121 @@ package com.codandotv.streamplayerapp.core_networking.di +import android.util.Log import com.codandotv.streamplayerapp.core.networking.BuildConfig -import com.codandotv.streamplayerapp.core_networking.coroutines.NetworkResponseAdapterFactory +import com.codandotv.streamplayerapp.core_networking.handleError.validator import com.squareup.moshi.Moshi -import com.squareup.moshi.kotlin.reflect.KotlinJsonAdapterFactory +import io.ktor.client.HttpClient +import io.ktor.client.HttpClientConfig +import io.ktor.client.engine.okhttp.OkHttp +import io.ktor.client.engine.okhttp.OkHttpConfig +import io.ktor.client.plugins.HttpResponseValidator +import io.ktor.client.plugins.HttpTimeout +import io.ktor.client.plugins.auth.Auth +import io.ktor.client.plugins.auth.providers.BearerTokens +import io.ktor.client.plugins.auth.providers.bearer +import io.ktor.client.plugins.contentnegotiation.ContentNegotiation +import io.ktor.client.plugins.defaultRequest +import io.ktor.client.plugins.logging.LogLevel +import io.ktor.client.plugins.logging.Logger +import io.ktor.client.plugins.logging.Logging +import io.ktor.client.request.accept +import io.ktor.http.ContentType +import io.ktor.http.contentType +import io.ktor.serialization.kotlinx.json.json +import kotlinx.serialization.ExperimentalSerializationApi +import kotlinx.serialization.json.Json import okhttp3.Interceptor -import okhttp3.OkHttpClient -import okhttp3.logging.HttpLoggingInterceptor import org.koin.dsl.module -import retrofit2.Retrofit -import retrofit2.converter.moshi.MoshiConverterFactory -import java.util.concurrent.TimeUnit -@Suppress("MagicNumber") object NetworkModule { val module = module { - single(QualifierHost) { - BuildConfig.HOST - } - - single(QualifierProfile) { - BuildConfig.PROFILE - } - - single { - Moshi.Builder() - .add(KotlinJsonAdapterFactory()) - .build() - } + single(QualifierHost) { BuildConfig.HOST } + single(QualifierProfile) { BuildConfig.PROFILE } - single(QualifierAuthInterceptor) { - Interceptor { chain -> - val newRequest = - chain.request() - .newBuilder() - .addHeader( - "Authorization", - BuildConfig.API_BEARER_AUTH - ) - .addHeader("Content-Type", "application/json;charset=utf-8") - .build() - chain.proceed(newRequest) - } - } +// single(QualifierAuthInterceptor) { +// Interceptor { chain -> +// val newRequest = chain.request() +// .newBuilder() +// .addHeader("Authorization", BuildConfig.API_BEARER_AUTH) +// .addHeader("Content-Type", "application/json;charset=utf-8") +// .build() +// chain.proceed(newRequest) +// } +// } - single(QualifierLoggerInterceptor) { - HttpLoggingInterceptor().setLevel( - if (BuildConfig.DEBUG) { - HttpLoggingInterceptor.Level.BODY - } else { - HttpLoggingInterceptor.Level.NONE - } - ) - } single { - provideRetrofit( - okHttpClient = get(), + provideKtorHttpClient( moshi = get(), - baseUrl = get(QualifierHost) + baseUrl = get(QualifierHost), ) } - single(QualifierProfileRetrofit) { - provideRetrofit( - okHttpClient = get(), + single(QualifierProfileHttpClient) { + provideKtorHttpClient( moshi = get(), - baseUrl = get(QualifierProfile) + baseUrl = get(QualifierProfile), ) } - single { - provideOkhttp( - get(QualifierAuthInterceptor), - get(QualifierLoggerInterceptor), - ) - } + single { Moshi.Builder().build() } } - private fun provideOkhttp( - vararg interceptor: Interceptor - ): OkHttpClient { - val okHttpClientBuilder = OkHttpClient.Builder() - interceptor.forEach { - okHttpClientBuilder.addInterceptor(it) + private fun provideKtorHttpClient( + moshi: Moshi, + baseUrl: String, + ): HttpClient { + return HttpClient(OkHttp) { + installPlugins(moshi, baseUrl) } - return okHttpClientBuilder - .connectTimeout(15, TimeUnit.SECONDS) - .build() } - private fun provideRetrofit( - okHttpClient: OkHttpClient, + private fun HttpClientConfig.installPlugins( moshi: Moshi, - baseUrl: String - ): Retrofit = - Retrofit.Builder() - .baseUrl(baseUrl) - .client(okHttpClient) - .addConverterFactory(MoshiConverterFactory.create(moshi)) - .addCallAdapterFactory(NetworkResponseAdapterFactory(moshi)) - .build() + baseUrl: String, + ) { + expectSuccess = false + + install(ContentNegotiation) { + json(Json { + explicitNulls = false + ignoreUnknownKeys = true + }) + } + + install(HttpTimeout) { + socketTimeoutMillis = 10000 + requestTimeoutMillis = 10000 + connectTimeoutMillis = 10000 + } + + defaultRequest { + url(baseUrl) + contentType(ContentType.Application.Json) + accept(ContentType.Application.Json) + } + + validator(moshi) + + install(Auth) { + bearer { + loadTokens { + BearerTokens( + accessToken = BuildConfig.API_BEARER_AUTH, + refreshToken = "" + ) + } + } + } + + if (BuildConfig.DEBUG) { + install(Logging) { + level = LogLevel.ALL + logger = object : Logger { + override fun log(message: String) { + Log.i("HttpClient", message) + } + } + } + } + } } \ No newline at end of file diff --git a/core-networking/src/main/java/com/codandotv/streamplayerapp/core_networking/di/QualifierNetworking.kt b/core-networking/src/main/java/com/codandotv/streamplayerapp/core_networking/di/QualifierNetworking.kt index c3564c8f..332ad883 100644 --- a/core-networking/src/main/java/com/codandotv/streamplayerapp/core_networking/di/QualifierNetworking.kt +++ b/core-networking/src/main/java/com/codandotv/streamplayerapp/core_networking/di/QualifierNetworking.kt @@ -13,7 +13,7 @@ object QualifierProfile : Qualifier { get() = "QualifierProfile" } -object QualifierProfileRetrofit : Qualifier { +object QualifierProfileHttpClient : Qualifier { override val value: QualifierValue get() = "QualifierProfileRetrofit" } diff --git a/core-networking/src/main/java/com/codandotv/streamplayerapp/core_networking/handleError/Failure.kt b/core-networking/src/main/java/com/codandotv/streamplayerapp/core_networking/handleError/Failure.kt index eeb1421a..7e9c2e26 100644 --- a/core-networking/src/main/java/com/codandotv/streamplayerapp/core_networking/handleError/Failure.kt +++ b/core-networking/src/main/java/com/codandotv/streamplayerapp/core_networking/handleError/Failure.kt @@ -1,6 +1,7 @@ package com.codandotv.streamplayerapp.core_networking.handleError import com.codandotv.streamplayerapp.core.networking.R +import kotlinx.serialization.Serializable import org.koin.core.component.KoinComponent /** @@ -11,6 +12,8 @@ import org.koin.core.component.KoinComponent "TooGenericExceptionCaught", "MagicNumber" ) + +@Serializable sealed class Failure( val code: Int? = -1, val errorMessage: String? = null, @@ -23,7 +26,7 @@ sealed class Failure( Failure(codeStatus, errorMessageRes = R.string.core_networking_no_server_error) data class GenericError( - val codeStatus: Int? = -12, private val msg: String? = null + val codeStatus: Int? = -12, private val msg: String? = "Unknow error" ) : Failure( codeStatus ) diff --git a/core-networking/src/main/java/com/codandotv/streamplayerapp/core_networking/handleError/HttpClientConfigExtensions.kt b/core-networking/src/main/java/com/codandotv/streamplayerapp/core_networking/handleError/HttpClientConfigExtensions.kt new file mode 100644 index 00000000..1f075821 --- /dev/null +++ b/core-networking/src/main/java/com/codandotv/streamplayerapp/core_networking/handleError/HttpClientConfigExtensions.kt @@ -0,0 +1,72 @@ +package com.codandotv.streamplayerapp.core_networking.handleError + +import com.squareup.moshi.Moshi +import io.ktor.client.HttpClient +import io.ktor.client.HttpClientConfig +import io.ktor.client.call.body +import io.ktor.client.engine.okhttp.OkHttpConfig +import io.ktor.client.plugins.ClientRequestException +import io.ktor.client.plugins.HttpResponseValidator +import io.ktor.client.request.HttpRequestBuilder +import io.ktor.client.request.request +import io.ktor.client.statement.bodyAsText +import io.ktor.utils.io.errors.IOException +import kotlinx.serialization.SerializationException + +internal fun HttpClientConfig.validator( + moshi: Moshi +) { + HttpResponseValidator { + validateResponse { response -> + when (response.status.value) { + in 200..299 -> Unit + in 400..499 -> { + val errorBody = response.bodyAsText() + val error = try { + moshi.adapter(NetworkResponse.Error::class.java).fromJson(errorBody) + } catch (e: Exception) { + throw Failure.UnparsableResponseException(throwable = e) + } + throw Failure.ClientException(throwable = error?.exception) + } + + in 500..599 -> throw Failure.ServerError(response.status.value) + else -> throw Failure.UnknownError(response.status.value) + } + } + + handleResponseExceptionWithRequest { exception, _ -> + when (exception) { + is IOException -> throw Failure.NetworkError(throwable = exception) + else -> throw Failure.GenericError( + msg = exception.message ?: "Unknown error" + ) + } + } + } +} + +suspend inline fun HttpClient.safeRequest( + block: HttpRequestBuilder.() -> Unit, +): NetworkResponse = + try { + val response = request { block() } + NetworkResponse.Success(response.body()) + } catch (exception: ClientRequestException) { + NetworkResponse.Error( + body = exception.response.bodyAsText(), + exception = Failure.ServerError(exception.response.status.value) + ) + } catch (e: SerializationException) { + NetworkResponse.Error( + exception = Failure.UnparsableResponseException(throwable = e) + ) + } catch (e: IOException) { + NetworkResponse.Error( + exception = Failure.NetworkError(throwable = e) + ) + } catch (e: Exception) { + NetworkResponse.Error( + exception = Failure.GenericError(msg = e.message ?: "Unknown error") + ) + } diff --git a/core-networking/src/main/java/com/codandotv/streamplayerapp/core_networking/handleError/NetworkResponse.kt b/core-networking/src/main/java/com/codandotv/streamplayerapp/core_networking/handleError/NetworkResponse.kt index ca705691..60ba6259 100644 --- a/core-networking/src/main/java/com/codandotv/streamplayerapp/core_networking/handleError/NetworkResponse.kt +++ b/core-networking/src/main/java/com/codandotv/streamplayerapp/core_networking/handleError/NetworkResponse.kt @@ -2,14 +2,18 @@ package com.codandotv.streamplayerapp.core_networking.handleError import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.flow +import kotlinx.serialization.Serializable +@Serializable sealed class NetworkResponse { + @Serializable data class Success( val value: T ) : NetworkResponse() + @Serializable data class Error( - val body: Any? = null, + val body: String? = null, @Transient val exception: Failure? = null ) : NetworkResponse() diff --git a/feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/detail/data/DetailStreamService.kt b/feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/detail/data/DetailStreamService.kt index d61f86a4..ea75d5b1 100644 --- a/feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/detail/data/DetailStreamService.kt +++ b/feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/detail/data/DetailStreamService.kt @@ -1,15 +1,30 @@ package com.codandotv.streamplayerapp.feature_list_streams.detail.data import com.codandotv.streamplayerapp.core_networking.handleError.NetworkResponse +import com.codandotv.streamplayerapp.core_networking.handleError.safeRequest import com.codandotv.streamplayerapp.feature_list_streams.detail.data.model.DetailStreamResponse import com.codandotv.streamplayerapp.feature_list_streams.detail.data.model.VideoStreamsResponse -import retrofit2.http.GET -import retrofit2.http.Path +import io.ktor.client.HttpClient +import io.ktor.client.request.url +import org.koin.core.annotation.Factory interface DetailStreamService { - @GET("movie/{movie_id}") - suspend fun getMovie(@Path("movie_id") movieId: String): NetworkResponse + suspend fun getMovie(movieId: String): NetworkResponse + suspend fun getVideoStreams(movieId: String): NetworkResponse +} - @GET("movie/{movie_id}/videos") - suspend fun getVideoStreams(@Path("movie_id") movieId: String): NetworkResponse -} \ No newline at end of file +@Factory +class DetailStreamServiceImpl( + private val client: HttpClient +) : DetailStreamService { + + override suspend fun getMovie(movieId: String): NetworkResponse = + client.safeRequest { + url("movie/$movieId") + } + + override suspend fun getVideoStreams(movieId: String): NetworkResponse = + client.safeRequest { + url("movie/$movieId/videos") + } +} diff --git a/feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/detail/data/model/DetailStreamResponse.kt b/feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/detail/data/model/DetailStreamResponse.kt index 58f66c52..8b85f24d 100644 --- a/feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/detail/data/model/DetailStreamResponse.kt +++ b/feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/detail/data/model/DetailStreamResponse.kt @@ -1,5 +1,8 @@ package com.codandotv.streamplayerapp.feature_list_streams.detail.data.model +import kotlinx.serialization.Serializable + +@Serializable @Suppress("ConstructorParameterNaming") data class DetailStreamResponse( val id : String, diff --git a/feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/detail/data/model/VideoStreamResponse.kt b/feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/detail/data/model/VideoStreamResponse.kt index e6b4a696..42fd495c 100644 --- a/feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/detail/data/model/VideoStreamResponse.kt +++ b/feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/detail/data/model/VideoStreamResponse.kt @@ -1,5 +1,8 @@ package com.codandotv.streamplayerapp.feature_list_streams.detail.data.model +import kotlinx.serialization.Serializable + +@Serializable data class VideoStreamResponse( val id: String, val name: String, @@ -10,6 +13,7 @@ data class VideoStreamResponse( val type: String, ) +@Serializable data class VideoStreamsResponse( val id: Long, val results: List diff --git a/feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/list/data/ListStreamService.kt b/feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/list/data/ListStreamService.kt index d702c49e..e2634895 100644 --- a/feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/list/data/ListStreamService.kt +++ b/feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/list/data/ListStreamService.kt @@ -1,28 +1,59 @@ package com.codandotv.streamplayerapp.feature_list_streams.list.data +import ListStreamResponse import com.codandotv.streamplayerapp.core_networking.handleError.NetworkResponse +import com.codandotv.streamplayerapp.core_networking.handleError.safeRequest import com.codandotv.streamplayerapp.feature_list_streams.list.data.model.GenresResponse -import com.codandotv.streamplayerapp.feature_list_streams.list.data.model.ListStreamResponse -import com.codandotv.streamplayerapp.feature_list_streams.list.data.model.StreamResponse -import retrofit2.http.GET -import retrofit2.http.Query +import io.ktor.client.HttpClient +import io.ktor.client.request.parameter +import io.ktor.client.request.url interface ListStreamService { - @GET("discover/movie") - suspend fun getMovies(@Query("with_genres") genres: String) : NetworkResponse - - @GET("discover/movie") - suspend fun getPaginatedMovies( - @Query("with_genres") genres: String, - @Query("page") page: Int - ) : NetworkResponse - - @GET("genre/movie/list") + suspend fun getMovies(genres: String): NetworkResponse + suspend fun getPaginatedMovies(genres: String, page: Int): NetworkResponse suspend fun getGenres(): NetworkResponse - - @GET("discover/movie") suspend fun getTopRatedMovies( - @Query("sort_by") sortBy: String = "vote_average.desc", - @Query("page") page: Int = 1 + sortBy: String = "vote_average.desc", + page: Int = 1 ): NetworkResponse -} \ No newline at end of file +} + +class ListStreamServiceImpl( + private val client: HttpClient +) : ListStreamService { + + override suspend fun getMovies(genres: String): NetworkResponse { + return client.safeRequest { + url("discover/movie") + parameter("with_genres", genres) + } + } + + override suspend fun getPaginatedMovies( + genres: String, + page: Int + ): NetworkResponse { + return client.safeRequest { + url("discover/movie") + parameter("with_genres", genres) + parameter("page", page) + } + } + + override suspend fun getGenres(): NetworkResponse { + return client.safeRequest { + url("genre/movie/list") + } + } + + override suspend fun getTopRatedMovies( + sortBy: String, + page: Int + ): NetworkResponse { + return client.safeRequest { + url("discover/movie") + parameter("sort_by", sortBy) + parameter("page", page) + } + } +} diff --git a/feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/list/data/model/GenresResponse.kt b/feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/list/data/model/GenresResponse.kt index 90f9bed1..3533dcdd 100644 --- a/feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/list/data/model/GenresResponse.kt +++ b/feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/list/data/model/GenresResponse.kt @@ -1,10 +1,14 @@ package com.codandotv.streamplayerapp.feature_list_streams.list.data.model +import kotlinx.serialization.Serializable + +@Serializable data class GenreResponse( val id: Long, val name: String ) +@Serializable data class GenresResponse( val genres: List ) \ No newline at end of file diff --git a/feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/list/data/model/ListStreamResponse.kt b/feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/list/data/model/ListStreamResponse.kt index 0cd75ada..89574e2c 100644 --- a/feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/list/data/model/ListStreamResponse.kt +++ b/feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/list/data/model/ListStreamResponse.kt @@ -1,12 +1,14 @@ -package com.codandotv.streamplayerapp.feature_list_streams.list.data.model +import kotlinx.serialization.Serializable -@Suppress("ConstructorParameterNaming") +@Serializable data class StreamResponse( - val id : String, - val title : String, - val overview : String, - val poster_path: String? = null, + val id: Int, + val title: String, + val overview: String, + val poster_path: String? = null ) + +@Serializable data class ListStreamResponse( val results: List -) \ No newline at end of file +) diff --git a/feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/list/domain/ListStreamMapper.kt b/feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/list/domain/ListStreamMapper.kt index 5e5d86c0..d3bb2b5c 100644 --- a/feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/list/domain/ListStreamMapper.kt +++ b/feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/list/domain/ListStreamMapper.kt @@ -1,9 +1,9 @@ package com.codandotv.streamplayerapp.feature_list_streams.list.domain +import ListStreamResponse +import StreamResponse import com.codandotv.streamplayerapp.core_networking.Url import com.codandotv.streamplayerapp.feature_list_streams.list.data.model.GenresResponse -import com.codandotv.streamplayerapp.feature_list_streams.list.data.model.ListStreamResponse -import com.codandotv.streamplayerapp.feature_list_streams.list.data.model.StreamResponse import com.codandotv.streamplayerapp.feature_list_streams.list.domain.model.Genre import com.codandotv.streamplayerapp.feature_list_streams.list.domain.model.ListStream import com.codandotv.streamplayerapp.feature_list_streams.list.domain.model.Stream @@ -24,5 +24,5 @@ fun StreamResponse.toStream(): Stream = Stream( description = overview, name = title, posterPathUrl = "${Url.IMAGE_URL_SIZE_300}${poster_path}", - id = id + id = id.toString() ) \ No newline at end of file diff --git a/feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/search/data/api/MostPopularMoviesService.kt b/feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/search/data/api/MostPopularMoviesService.kt index a2d6e452..ea3f8fed 100644 --- a/feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/search/data/api/MostPopularMoviesService.kt +++ b/feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/search/data/api/MostPopularMoviesService.kt @@ -1,10 +1,22 @@ package com.codandotv.streamplayerapp.feature_list_streams.search.data.api import com.codandotv.streamplayerapp.core_networking.handleError.NetworkResponse +import com.codandotv.streamplayerapp.core_networking.handleError.safeRequest import com.codandotv.streamplayerapp.feature_list_streams.search.data.model.ListSearchStreamResponse -import retrofit2.http.GET +import io.ktor.client.HttpClient +import io.ktor.client.call.body +import io.ktor.client.request.get +import io.ktor.client.request.url interface MostPopularMoviesService { - @GET("movie/popular") suspend fun getPopular(): NetworkResponse } + +class MostPopularMoviesServiceImpl( + private val client: HttpClient +) : MostPopularMoviesService { + override suspend fun getPopular(): NetworkResponse = + client.safeRequest { + url("movie/popular") + } +} diff --git a/feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/search/data/api/SearchStreamService.kt b/feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/search/data/api/SearchStreamService.kt index c9d43b1d..8750ae50 100644 --- a/feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/search/data/api/SearchStreamService.kt +++ b/feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/search/data/api/SearchStreamService.kt @@ -1,11 +1,22 @@ package com.codandotv.streamplayerapp.feature_list_streams.search.data.api import com.codandotv.streamplayerapp.core_networking.handleError.NetworkResponse +import com.codandotv.streamplayerapp.core_networking.handleError.safeRequest import com.codandotv.streamplayerapp.feature_list_streams.search.data.model.ListSearchStreamResponse -import retrofit2.http.GET -import retrofit2.http.Query +import io.ktor.client.HttpClient +import io.ktor.client.request.parameter +import io.ktor.client.request.url interface SearchStreamService { - @GET("search/movie") - suspend fun getSearch(@Query("query") query: String) : NetworkResponse + suspend fun getSearch(query: String): NetworkResponse +} + +class SearchStreamServiceImpl( + private val client: HttpClient +) : SearchStreamService { + override suspend fun getSearch(query: String): NetworkResponse = + client.safeRequest { + url("search/movie") + parameter("query", query) + } } diff --git a/feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/search/data/model/ListSearchStreamResponse.kt b/feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/search/data/model/ListSearchStreamResponse.kt index d15bdc21..11c89b8f 100644 --- a/feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/search/data/model/ListSearchStreamResponse.kt +++ b/feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/search/data/model/ListSearchStreamResponse.kt @@ -1,11 +1,14 @@ package com.codandotv.streamplayerapp.feature_list_streams.search.data.model import com.squareup.moshi.Json +import kotlinx.serialization.Serializable +@Serializable data class ListSearchStreamResponse( @Json(name = "results") val results: List ) { + @Serializable data class SearchStreamResponse( @Json(name = "id") val id: String, diff --git a/feature-profile/src/main/java/com/codandotv/streamplayerapp/feature_profile/profile/data/ProfilePickerStreamService.kt b/feature-profile/src/main/java/com/codandotv/streamplayerapp/feature_profile/profile/data/ProfilePickerStreamService.kt index 6c512286..8f900f79 100644 --- a/feature-profile/src/main/java/com/codandotv/streamplayerapp/feature_profile/profile/data/ProfilePickerStreamService.kt +++ b/feature-profile/src/main/java/com/codandotv/streamplayerapp/feature_profile/profile/data/ProfilePickerStreamService.kt @@ -1,10 +1,20 @@ package com.codandotv.streamplayerapp.feature_profile.profile.data import com.codandotv.streamplayerapp.core_networking.handleError.NetworkResponse +import com.codandotv.streamplayerapp.core_networking.handleError.safeRequest import com.codandotv.streamplayerapp.feature_profile.profile.data.model.ProfilesResponse -import retrofit2.http.GET +import io.ktor.client.HttpClient +import io.ktor.client.request.url interface ProfilePickerStreamService { - @GET("profiles") suspend fun getProfiles(): NetworkResponse -} \ No newline at end of file +} + +class ProfilePickerStreamServiceImpl( + private val client: HttpClient +) : ProfilePickerStreamService { + override suspend fun getProfiles(): NetworkResponse = + client.safeRequest { + url("profiles") + } +} diff --git a/feature-profile/src/main/java/com/codandotv/streamplayerapp/feature_profile/profile/data/model/ProfileStreamResponse.kt b/feature-profile/src/main/java/com/codandotv/streamplayerapp/feature_profile/profile/data/model/ProfileStreamResponse.kt index b8534939..2abc127a 100644 --- a/feature-profile/src/main/java/com/codandotv/streamplayerapp/feature_profile/profile/data/model/ProfileStreamResponse.kt +++ b/feature-profile/src/main/java/com/codandotv/streamplayerapp/feature_profile/profile/data/model/ProfileStreamResponse.kt @@ -1,5 +1,8 @@ package com.codandotv.streamplayerapp.feature_profile.profile.data.model +import kotlinx.serialization.Serializable + +@Serializable @Suppress("ConstructorParameterNaming") data class ProfileStreamResponse( val id: String, @@ -7,6 +10,7 @@ data class ProfileStreamResponse( val profile_url: String, ) +@Serializable data class ProfilesResponse( val profiles: List ) \ No newline at end of file diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 00e76027..e3e03973 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -12,7 +12,7 @@ detekt = "1.23.6" test_junit = "4.13.2" androidx_core_testing = "2.2.0" mockk = "1.13.7" -kotlinx-coroutines-test= "1.8.1" +kotlinx-coroutines-test = "1.8.1" #Android Test androidx_test_core = "1.6.1" @@ -33,8 +33,7 @@ androidx_core_ktx = "1.13.1" #Networking moshi = "1.14.0" okhttp = "4.12.0" -retrofit = "2.9.0" -ktor = "3.0.1" +ktor = "2.3.0" #Compose compose = "1.5.15" @@ -44,7 +43,7 @@ compose_activity = "1.5.0" compose_icons = "1.4.3" compose_navigation = "2.8.1" lifecycle_version = "2.8.6" -compose_pagging="3.3.2" +compose_pagging = "3.3.2" coil = "2.3.0" lottie = "5.2.0" @@ -58,7 +57,7 @@ android_youtube_player_version = "12.0.0" kotlin_gradle_plugin = { group = "org.jetbrains.kotlin", name = "kotlin-gradle-plugin", version.ref = "kotlin" } android_gradle_plugin = { group = "com.android.tools.build", name = "gradle", version.ref = "android_gradle_plugin" } detekt-gradle-plugin = { module = "io.gitlab.arturbosch.detekt:detekt-gradle-plugin", version.ref = "detekt" } - +serialization = { module = "org.jetbrains.kotlin.plugin.serialization:org.jetbrains.kotlin.plugin.serialization.gradle.plugin", version.ref = "kotlin" } #Kover kover-gradle-plugin = { module = "org.jetbrains.kotlinx:kover-gradle-plugin", version.ref = "kover" } @@ -123,32 +122,32 @@ koin_compiler = { group = "io.insert-koin", name = "koin-ksp-compiler", version. koin_compose = { group = "io.insert-koin", name = "koin-androidx-compose", version.ref = "koin" } #Networking -retrofit = { group = "com.squareup.retrofit2", name = "retrofit", version.ref = "retrofit" } moshi = { group = "com.squareup.moshi", name = "moshi-kotlin", version.ref = "moshi" } -moshi_converter = { group = "com.squareup.retrofit2", name = "converter-moshi", version.ref = "retrofit" } okhttp = { group = "com.squareup.okhttp3", name = "okhttp", version.ref = "okhttp" } interceptor = { group = "com.squareup.okhttp3", name = "logging-interceptor", version.ref = "okhttp" } + ktor_client_core = { module = "io.ktor:ktor-client-core", version.ref = "ktor" } ktor_client_okhttp = { module = "io.ktor:ktor-client-okhttp", version.ref = "ktor" } +ktor_client_logger = { module = "io.ktor:ktor-client-logging", version.ref = "ktor" } +ktor_client_auth = { module = "io.ktor:ktor-client-auth", version.ref = "ktor" } ktor_client_content_negotiation = { module = "io.ktor:ktor-client-content-negotiation", version.ref = "ktor" } -ktor_client_content_serialization_gson = { module = "io.ktor:ktor-serialization-gson", version.ref = "ktor" } +ktor_client_content_serialization_json = { module = "io.ktor:ktor-serialization-kotlinx-json", version.ref = "ktor" } #Room -roomRuntime = { group = "androidx.room", name = "room-runtime", version.ref = "room"} -roomCompiler = { group = "androidx.room", name= "room-compiler", version.ref = "room"} -roomKtx = { group = "androidx.room", name = "room-ktx", version.ref = "room"} +roomRuntime = { group = "androidx.room", name = "room-runtime", version.ref = "room" } +roomCompiler = { group = "androidx.room", name = "room-compiler", version.ref = "room" } +roomKtx = { group = "androidx.room", name = "room-ktx", version.ref = "room" } android_youtube_player = { group = "com.pierfrancescosoffritti.androidyoutubeplayer", name = "core", version.ref = "android_youtube_player_version" } dokka = { group = "org.jetbrains.dokka", name = "android-documentation-plugin", version.ref = "dokka" } [bundles] -room = ["roomRuntime","roomKtx"] -compose = ["compose.ui", "compose.icons", "compose.material3","compose_pagging", "compose.lifecycle", "compose.navigation", "compose.activity", "compose.ui.tooling"] -composetest = ["compose.uitest", "compose.junit4", "compose.manifest", "compose.ui.test"] -networking = ["retrofit", "moshi", "moshi_converter", "okhttp", "interceptor","ktor_client_core","ktor_client_okhttp","ktor_client_content_serialization_gson","ktor_client_content_negotiation"] +room = ["roomRuntime", "roomKtx"] +compose = ["compose.ui", "compose.icons", "compose.material3", "compose_pagging", "compose.lifecycle", "compose.navigation", "compose.activity", "compose.ui.tooling"] +networking = ["moshi", "okhttp", "interceptor", "ktor_client_core", "ktor_client_okhttp", "ktor_client_content_serialization_json", "ktor_client_content_negotiation", "ktor_client_logger", "ktor_client_auth"] koin = ["koin_android", "koin_compose"] -test = ["junit", "mockk", "mockk_android", "viewmodel_test", "koin_test","coroutines_test"] -androidSupport = ["androidx_core", "androidx_appcompat", "androidx_dynamicanimation","google_material"] +test = ["junit", "mockk", "mockk_android", "viewmodel_test", "koin_test", "coroutines_test"] +androidSupport = ["androidx_core", "androidx_appcompat", "androidx_dynamicanimation", "google_material"] kotlin = ["androidx_core", "kotlin_stdlib", "kotlin_reflect"] [plugins] @@ -156,9 +155,11 @@ android_application = { id = "com.android.application", version.ref = "android_g android_library = { id = "com.android.library", version.ref = "android_gradle_plugin" } kotlin_android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" } +serialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "kotlin" } + -ksp = { id = "com.google.devtools.ksp", version.ref = "ksp"} -dokka = { id = "org.jetbrains.dokka", version.ref = "dokka"} +ksp = { id = "com.google.devtools.ksp", version.ref = "ksp" } +dokka = { id = "org.jetbrains.dokka", version.ref = "dokka" } kover = { id = "org.jetbrains.kotlinx.kover", version.ref = "kover" } detekt = { id = "io.gitlab.arturbosch.detekt", version.ref = "detekt" } \ No newline at end of file From 4359f0ea8a81b19f580e56e751012984d966e674 Mon Sep 17 00:00:00 2001 From: Rods Date: Tue, 3 Dec 2024 19:51:18 -0300 Subject: [PATCH 03/96] [ISSUE-1] - bugs fixes --- .../core_networking/di/QualifierNetworking.kt | 1 - .../handleError/HttpClientConfigExtensions.kt | 3 +++ .../detail/data/DetailStreamService.kt | 1 - .../detail/data/model/DetailStreamResponse.kt | 2 +- .../feature_list_streams/detail/di/DetailStreamModule.kt | 8 ++++++-- .../detail/domain/DetailStreamMapper.kt | 2 +- .../feature_list_streams/list/di/ListStreamModule.kt | 7 ++++--- .../search/data/model/ListSearchStreamResponse.kt | 4 ++-- .../feature_list_streams/search/di/SearchModule.kt | 7 ++++--- .../search/domain/mapper/SearchMapper.kt | 2 +- .../profile/di/ProfilePickerStreamModule.kt | 9 +++++---- 11 files changed, 27 insertions(+), 19 deletions(-) diff --git a/core-networking/src/main/java/com/codandotv/streamplayerapp/core_networking/di/QualifierNetworking.kt b/core-networking/src/main/java/com/codandotv/streamplayerapp/core_networking/di/QualifierNetworking.kt index 332ad883..b21a5e27 100644 --- a/core-networking/src/main/java/com/codandotv/streamplayerapp/core_networking/di/QualifierNetworking.kt +++ b/core-networking/src/main/java/com/codandotv/streamplayerapp/core_networking/di/QualifierNetworking.kt @@ -18,7 +18,6 @@ object QualifierProfileHttpClient : Qualifier { get() = "QualifierProfileRetrofit" } - object QualifierLoggerInterceptor : Qualifier { override val value: QualifierValue get() = "QualifierLoggerInterceptor" diff --git a/core-networking/src/main/java/com/codandotv/streamplayerapp/core_networking/handleError/HttpClientConfigExtensions.kt b/core-networking/src/main/java/com/codandotv/streamplayerapp/core_networking/handleError/HttpClientConfigExtensions.kt index 1f075821..ec896a04 100644 --- a/core-networking/src/main/java/com/codandotv/streamplayerapp/core_networking/handleError/HttpClientConfigExtensions.kt +++ b/core-networking/src/main/java/com/codandotv/streamplayerapp/core_networking/handleError/HttpClientConfigExtensions.kt @@ -1,5 +1,6 @@ package com.codandotv.streamplayerapp.core_networking.handleError +import android.util.Log import com.squareup.moshi.Moshi import io.ktor.client.HttpClient import io.ktor.client.HttpClientConfig @@ -36,6 +37,8 @@ internal fun HttpClientConfig.validator( } handleResponseExceptionWithRequest { exception, _ -> + Log.d("HttpClientError",exception.stackTraceToString()) + when (exception) { is IOException -> throw Failure.NetworkError(throwable = exception) else -> throw Failure.GenericError( diff --git a/feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/detail/data/DetailStreamService.kt b/feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/detail/data/DetailStreamService.kt index ea75d5b1..909b90d8 100644 --- a/feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/detail/data/DetailStreamService.kt +++ b/feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/detail/data/DetailStreamService.kt @@ -13,7 +13,6 @@ interface DetailStreamService { suspend fun getVideoStreams(movieId: String): NetworkResponse } -@Factory class DetailStreamServiceImpl( private val client: HttpClient ) : DetailStreamService { diff --git a/feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/detail/data/model/DetailStreamResponse.kt b/feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/detail/data/model/DetailStreamResponse.kt index 8b85f24d..36b0e2fc 100644 --- a/feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/detail/data/model/DetailStreamResponse.kt +++ b/feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/detail/data/model/DetailStreamResponse.kt @@ -5,7 +5,7 @@ import kotlinx.serialization.Serializable @Serializable @Suppress("ConstructorParameterNaming") data class DetailStreamResponse( - val id : String, + val id : Int, val title : String, val overview : String, val tagline : String, diff --git a/feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/detail/di/DetailStreamModule.kt b/feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/detail/di/DetailStreamModule.kt index 30723490..eec3b2be 100644 --- a/feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/detail/di/DetailStreamModule.kt +++ b/feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/detail/di/DetailStreamModule.kt @@ -3,6 +3,7 @@ package com.codandotv.streamplayerapp.feature_list_streams.detail.di import com.codandotv.streamplayerapp.feature_list_streams.detail.data.DetailStreamRepository import com.codandotv.streamplayerapp.feature_list_streams.detail.data.DetailStreamRepositoryImpl import com.codandotv.streamplayerapp.feature_list_streams.detail.data.DetailStreamService +import com.codandotv.streamplayerapp.feature_list_streams.detail.data.DetailStreamServiceImpl import com.codandotv.streamplayerapp.feature_list_streams.detail.domain.DetailStreamUseCase import com.codandotv.streamplayerapp.feature_list_streams.detail.domain.DetailStreamUseCaseImpl import com.codandotv.streamplayerapp.feature_list_streams.detail.domain.VideoStreamsUseCase @@ -12,7 +13,6 @@ import kotlinx.coroutines.Dispatchers import org.koin.androidx.viewmodel.dsl.viewModel import org.koin.core.parameter.parametersOf import org.koin.dsl.module -import retrofit2.Retrofit object DetailStreamModule { val module = module { @@ -49,6 +49,10 @@ object DetailStreamModule { ) } - factory { get().create(DetailStreamService::class.java) } + factory { + DetailStreamServiceImpl( + client = get() + ) + } } } \ No newline at end of file diff --git a/feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/detail/domain/DetailStreamMapper.kt b/feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/detail/domain/DetailStreamMapper.kt index 174f7fc1..fe78b62c 100644 --- a/feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/detail/domain/DetailStreamMapper.kt +++ b/feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/detail/domain/DetailStreamMapper.kt @@ -8,7 +8,7 @@ import com.codandotv.streamplayerapp.feature_list_streams.detail.data.model.Vide @Suppress("MagicNumber") fun DetailStreamResponse.toDetailStream(isFavorite: Boolean = false): DetailStream = DetailStream( - id = this.id, + id = this.id.toString(), title = this.title, overview = this.overview, tagline = this.tagline, diff --git a/feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/list/di/ListStreamModule.kt b/feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/list/di/ListStreamModule.kt index 9a4ea1ac..6e300eef 100644 --- a/feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/list/di/ListStreamModule.kt +++ b/feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/list/di/ListStreamModule.kt @@ -1,11 +1,12 @@ package com.codandotv.streamplayerapp.feature_list_streams.list.di import com.codandotv.streamplayerapp.feature_list_streams.list.data.ListStreamService +import com.codandotv.streamplayerapp.feature_list_streams.list.data.ListStreamServiceImpl +import io.ktor.client.HttpClient import org.koin.core.annotation.ComponentScan import org.koin.core.annotation.Factory import org.koin.core.annotation.Module import org.koin.core.context.GlobalContext -import retrofit2.Retrofit @Module @ComponentScan("com.codandotv.streamplayerapp.feature_list_streams.list") @@ -14,7 +15,7 @@ class ListStreamModule { @Factory fun service(): ListStreamService { val koin = GlobalContext.get() - val retrofit = koin.get() - return retrofit.create(ListStreamService::class.java) + val httpClient = koin.get() + return ListStreamServiceImpl(httpClient) } } \ No newline at end of file diff --git a/feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/search/data/model/ListSearchStreamResponse.kt b/feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/search/data/model/ListSearchStreamResponse.kt index 11c89b8f..c7b23ca4 100644 --- a/feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/search/data/model/ListSearchStreamResponse.kt +++ b/feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/search/data/model/ListSearchStreamResponse.kt @@ -11,12 +11,12 @@ data class ListSearchStreamResponse( @Serializable data class SearchStreamResponse( @Json(name = "id") - val id: String, + val id: Int, @Json(name = "title") val title: String, @Json(name="overview") val overview: String, @Json(name = "poster_path") - val posterPath: String, + val posterPath: String? = null ) } diff --git a/feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/search/di/SearchModule.kt b/feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/search/di/SearchModule.kt index 44025733..b9426f38 100644 --- a/feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/search/di/SearchModule.kt +++ b/feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/search/di/SearchModule.kt @@ -7,6 +7,8 @@ import com.codandotv.streamplayerapp.feature_list_streams.search.data.datasource import com.codandotv.streamplayerapp.feature_list_streams.search.data.repository.MostPopularMoviesRepository import com.codandotv.streamplayerapp.feature_list_streams.search.data.repository.MostPopularMoviesRepositoryImpl import com.codandotv.streamplayerapp.feature_list_streams.search.data.api.MostPopularMoviesService +import com.codandotv.streamplayerapp.feature_list_streams.search.data.api.MostPopularMoviesServiceImpl +import com.codandotv.streamplayerapp.feature_list_streams.search.data.api.SearchStreamServiceImpl import com.codandotv.streamplayerapp.feature_list_streams.search.data.datasource.SearchStreamDataSource import com.codandotv.streamplayerapp.feature_list_streams.search.data.repository.SearchStreamRepository import com.codandotv.streamplayerapp.feature_list_streams.search.data.repository.SearchStreamRepositoryImp @@ -17,7 +19,6 @@ import com.codandotv.streamplayerapp.feature_list_streams.search.domain.SearchUs import com.codandotv.streamplayerapp.feature_list_streams.search.presentation.screens.SearchViewModel import org.koin.androidx.viewmodel.dsl.viewModel import org.koin.dsl.module -import retrofit2.Retrofit object SearchModule { val module = module { @@ -28,8 +29,8 @@ object SearchModule { ) } - factory { get().create(SearchStreamService::class.java) } - factory { get().create(MostPopularMoviesService::class.java) } + factory { SearchStreamServiceImpl(get()) } + factory { MostPopularMoviesServiceImpl(get()) } factory { MostPopularMoviesUseCaseImpl(repository = get()) } factory { MostPopularMoviesDataSourceImpl(service = get()) } diff --git a/feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/search/domain/mapper/SearchMapper.kt b/feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/search/domain/mapper/SearchMapper.kt index 2817a44b..f4586a63 100644 --- a/feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/search/domain/mapper/SearchMapper.kt +++ b/feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/search/domain/mapper/SearchMapper.kt @@ -5,7 +5,7 @@ import com.codandotv.streamplayerapp.feature_list_streams.search.data.model.List import com.codandotv.streamplayerapp.feature_list_streams.search.presentation.widgets.SearchStreamCardModel fun SearchStreamResponse.toSearchStreamCardModel() = SearchStreamCardModel( - id = id, + id = id.toString(), title = title, url = "${Url.IMAGE_URL_SIZE_200}${posterPath}" ) diff --git a/feature-profile/src/main/java/com/codandotv/streamplayerapp/feature_profile/profile/di/ProfilePickerStreamModule.kt b/feature-profile/src/main/java/com/codandotv/streamplayerapp/feature_profile/profile/di/ProfilePickerStreamModule.kt index d6ae9651..03d4c680 100644 --- a/feature-profile/src/main/java/com/codandotv/streamplayerapp/feature_profile/profile/di/ProfilePickerStreamModule.kt +++ b/feature-profile/src/main/java/com/codandotv/streamplayerapp/feature_profile/profile/di/ProfilePickerStreamModule.kt @@ -1,12 +1,13 @@ package com.codandotv.streamplayerapp.feature_profile.profile.di -import com.codandotv.streamplayerapp.core_networking.di.QualifierProfileRetrofit +import com.codandotv.streamplayerapp.core_networking.di.QualifierProfileHttpClient import com.codandotv.streamplayerapp.feature_profile.profile.data.ProfilePickerStreamService +import com.codandotv.streamplayerapp.feature_profile.profile.data.ProfilePickerStreamServiceImpl +import io.ktor.client.HttpClient import org.koin.core.annotation.ComponentScan import org.koin.core.annotation.Factory import org.koin.core.annotation.Module import org.koin.core.context.GlobalContext -import retrofit2.Retrofit @Module @ComponentScan("com.codandotv.streamplayerapp.feature_profile") @@ -15,8 +16,8 @@ class ProfilePickerStreamModule { @Factory fun service(): ProfilePickerStreamService { val koin = GlobalContext.get() - val retrofit = koin.get(QualifierProfileRetrofit) - return retrofit.create(ProfilePickerStreamService::class.java) + val httpClient = koin.get(QualifierProfileHttpClient) + return ProfilePickerStreamServiceImpl(httpClient) } } From cf1103f3df94eb2c0cb7f088875aad56e7ba0e67 Mon Sep 17 00:00:00 2001 From: Rods Date: Tue, 3 Dec 2024 19:59:30 -0300 Subject: [PATCH 04/96] [ISSUE-1] - bugs fixes --- .../feature_list_streams/detail/domain/DetailStreamMapper.kt | 1 - 1 file changed, 1 deletion(-) diff --git a/feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/detail/domain/DetailStreamMapper.kt b/feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/detail/domain/DetailStreamMapper.kt index fe78b62c..0362a3fb 100644 --- a/feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/detail/domain/DetailStreamMapper.kt +++ b/feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/detail/domain/DetailStreamMapper.kt @@ -5,7 +5,6 @@ import com.codandotv.streamplayerapp.core_networking.Url.IMAGE_URL_SIZE_500 import com.codandotv.streamplayerapp.feature_list_streams.detail.data.model.DetailStreamResponse import com.codandotv.streamplayerapp.feature_list_streams.detail.data.model.VideoStreamsResponse -@Suppress("MagicNumber") fun DetailStreamResponse.toDetailStream(isFavorite: Boolean = false): DetailStream = DetailStream( id = this.id.toString(), From e6f9658f8bde3f6288bac95690d96ef9f855d10a Mon Sep 17 00:00:00 2001 From: Rods Date: Tue, 3 Dec 2024 20:04:57 -0300 Subject: [PATCH 05/96] [ISSUE-1] - improvements --- .../core_networking/di/NetworkModule.kt | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/core-networking/src/main/java/com/codandotv/streamplayerapp/core_networking/di/NetworkModule.kt b/core-networking/src/main/java/com/codandotv/streamplayerapp/core_networking/di/NetworkModule.kt index 63535ec9..bbccaa5c 100644 --- a/core-networking/src/main/java/com/codandotv/streamplayerapp/core_networking/di/NetworkModule.kt +++ b/core-networking/src/main/java/com/codandotv/streamplayerapp/core_networking/di/NetworkModule.kt @@ -32,17 +32,6 @@ object NetworkModule { single(QualifierHost) { BuildConfig.HOST } single(QualifierProfile) { BuildConfig.PROFILE } -// single(QualifierAuthInterceptor) { -// Interceptor { chain -> -// val newRequest = chain.request() -// .newBuilder() -// .addHeader("Authorization", BuildConfig.API_BEARER_AUTH) -// .addHeader("Content-Type", "application/json;charset=utf-8") -// .build() -// chain.proceed(newRequest) -// } -// } - single { provideKtorHttpClient( moshi = get(), From 3a1eface99914619e9fa543fefa565a5eb875867 Mon Sep 17 00:00:00 2001 From: Rods Date: Tue, 3 Dec 2024 20:11:20 -0300 Subject: [PATCH 06/96] [ISSUE-1] - improvements --- build-logic/src/main/java/Keys.kt | 12 ------------ 1 file changed, 12 deletions(-) delete mode 100644 build-logic/src/main/java/Keys.kt diff --git a/build-logic/src/main/java/Keys.kt b/build-logic/src/main/java/Keys.kt deleted file mode 100644 index 8408abb8..00000000 --- a/build-logic/src/main/java/Keys.kt +++ /dev/null @@ -1,12 +0,0 @@ -object Keys { - private const val default_tmdb_token = "eyJhbGciOiJIUzI1NiJ9.eyJhdWQiOiJiNDg2NWM4YTAzNzhmM2I4NjI0OWU1ZjNiYWFiMjU2NyIsInN1YiI6IjY0Mjk4YTg5YTNlNGJhMWM0NDgzM2U4OCIsInNjb3BlcyI6WyJhcGlfcmVhZCJdLCJ2ZXJzaW9uIjoxfQ.9cIxv29vkaZ2yW88DIFRUFK_nXbK2b6KS8t96kA8WAE" - private const val tmdb_token_name_debug = "TMDB_BEARER_TOKEN_DEBUG" - private const val tmdb_token_name_release = "TMDB_BEARER_TOKEN_RELEASE" - - object BuildField { - val api_bearer_debug = - "\"${System.getenv(tmdb_token_name_debug) ?: default_tmdb_token}\"" - val api_bearer_release = - "\"${System.getenv(tmdb_token_name_release) ?: default_tmdb_token}\"" - } -} \ No newline at end of file From 19e9d55ebdb0b00e2dc8e1e2698d01373ba9bfdc Mon Sep 17 00:00:00 2001 From: Rods Date: Tue, 3 Dec 2024 20:13:29 -0300 Subject: [PATCH 07/96] [ISSUE-1] - improvements --- .../core_networking/di/NetworkModule.kt | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/core-networking/src/main/java/com/codandotv/streamplayerapp/core_networking/di/NetworkModule.kt b/core-networking/src/main/java/com/codandotv/streamplayerapp/core_networking/di/NetworkModule.kt index bbccaa5c..ddceb396 100644 --- a/core-networking/src/main/java/com/codandotv/streamplayerapp/core_networking/di/NetworkModule.kt +++ b/core-networking/src/main/java/com/codandotv/streamplayerapp/core_networking/di/NetworkModule.kt @@ -2,6 +2,7 @@ package com.codandotv.streamplayerapp.core_networking.di import android.util.Log import com.codandotv.streamplayerapp.core.networking.BuildConfig +import com.codandotv.streamplayerapp.core_networking.di.Network.TIMEOUT import com.codandotv.streamplayerapp.core_networking.handleError.validator import com.squareup.moshi.Moshi import io.ktor.client.HttpClient @@ -72,9 +73,9 @@ object NetworkModule { } install(HttpTimeout) { - socketTimeoutMillis = 10000 - requestTimeoutMillis = 10000 - connectTimeoutMillis = 10000 + socketTimeoutMillis = TIMEOUT + requestTimeoutMillis = TIMEOUT + connectTimeoutMillis = TIMEOUT } defaultRequest { @@ -107,4 +108,8 @@ object NetworkModule { } } } +} + +internal object Network{ + const val TIMEOUT = 10000L } \ No newline at end of file From 4a06ea71e0d6c1090bb82b4ac2b478159a189986 Mon Sep 17 00:00:00 2001 From: Rods Date: Mon, 9 Dec 2024 17:45:48 -0300 Subject: [PATCH 08/96] [ISSUE-1] - apply suggestions --- .../src/main/java/com.streamplayer.android-library.gradle.kts | 1 + .../detail/data/model/DetailStreamResponse.kt | 2 +- gradle/wrapper/gradle-wrapper.properties | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/build-logic/src/main/java/com.streamplayer.android-library.gradle.kts b/build-logic/src/main/java/com.streamplayer.android-library.gradle.kts index 9423e906..20d826af 100644 --- a/build-logic/src/main/java/com.streamplayer.android-library.gradle.kts +++ b/build-logic/src/main/java/com.streamplayer.android-library.gradle.kts @@ -17,6 +17,7 @@ plugins { id("com.streamplayer.dokka") id("org.jetbrains.kotlinx.kover") id("com.streamplayer.detekt") + //Note: The best approach might be to create a separate feature plugin to further isolate serialization, and do this later. id("org.jetbrains.kotlin.plugin.serialization") } diff --git a/feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/detail/data/model/DetailStreamResponse.kt b/feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/detail/data/model/DetailStreamResponse.kt index 36b0e2fc..1b873883 100644 --- a/feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/detail/data/model/DetailStreamResponse.kt +++ b/feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/detail/data/model/DetailStreamResponse.kt @@ -5,7 +5,7 @@ import kotlinx.serialization.Serializable @Serializable @Suppress("ConstructorParameterNaming") data class DetailStreamResponse( - val id : Int, + val id : Long, val title : String, val overview : String, val tagline : String, diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 09523c0e..62f495df 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.9-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.2-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME From a647227289433b77eef2c13ed08ae1e7f5bb1d5c Mon Sep 17 00:00:00 2001 From: Rods Date: Tue, 10 Dec 2024 19:41:11 -0300 Subject: [PATCH 09/96] =?UTF-8?q?[ISSUE-2]=20-=20=F0=9F=94=A5=20migrating?= =?UTF-8?q?=20android=20para=20KMP=20module=20list?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/build.gradle.kts | 3 - ...om.streamplayer.android-library.gradle.kts | 5 +- .../com.streamplayer.application.gradle.kts | 2 - .../java/com.streamplayer.compose.gradle.kts | 22 -- .../main/java/extensions/CommonExtensions.kt | 25 -- build.gradle.kts | 5 + core-local-storage/build.gradle.kts | 1 + core-navigation/build.gradle.kts | 6 +- core-networking/build.gradle.kts | 1 + core-shared-ui/build.gradle.kts | 6 +- core-shared/build.gradle.kts | 1 + feature-favorites/build.gradle.kts | 3 +- feature-list-streams/build.gradle.kts | 47 +-- .../feature_list_streams/core/ContentType.kt | 17 -- .../detail/data/DetailStreamRepository.kt | 54 ---- .../detail/data/DetailStreamService.kt | 29 -- .../detail/data/model/DetailStreamResponse.kt | 14 - .../detail/data/model/VideoStreamResponse.kt | 20 -- .../detail/di/DetailStreamModule.kt | 58 ---- .../detail/domain/DetailStream.kt | 11 - .../detail/domain/DetailStreamMapper.kt | 35 --- .../detail/domain/DetailStreamUseCase.kt | 26 -- .../detail/domain/VideoStream.kt | 6 - .../detail/domain/VideoStreamsUseCase.kt | 16 - .../navigation/DetailStreamNavigation.kt | 37 --- .../screens/DetailStreamViewModel.kt | 66 ----- .../screens/DetailStreamsScreen.kt | 203 ------------- .../screens/DetailStreamsUIState.kt | 12 - .../widget/DetailStreamActionOption.kt | 77 ----- .../widget/DetailStreamButtonAction.kt | 59 ---- .../widget/DetailStreamImagePreview.kt | 71 ----- .../widget/DetailStreamRowHeader.kt | 49 --- .../widget/DetailStreamToolbar.kt | 55 ---- .../list/data/ListStreamRepository.kt | 53 ---- .../list/data/ListStreamService.kt | 59 ---- .../list/data/StreamDataSource.kt | 44 --- .../list/data/model/GenresResponse.kt | 14 - .../list/data/model/ListStreamResponse.kt | 14 - .../list/di/ListStreamModule.kt | 21 -- .../list/domain/GetGenresUseCase.kt | 19 -- .../list/domain/GetLatestMovieUseCase.kt | 19 -- .../list/domain/ListMovieUseCase.kt | 21 -- .../list/domain/ListStreamAnalytics.kt | 8 - .../list/domain/ListStreamMapper.kt | 28 -- .../list/domain/model/Genre.kt | 6 - .../list/domain/model/HighlightBanner.kt | 24 -- .../list/domain/model/ListStream.kt | 12 - .../navigation/ListStreamsNavigation.kt | 43 --- .../screens/ListStreamViewModel.kt | 130 -------- .../presentation/screens/ListStreamsScreen.kt | 115 ------- .../screens/ListStreamsUIState.kt | 10 - .../presentation/widgets/HighlightBanner.kt | 280 ------------------ .../list/presentation/widgets/StreamsCard.kt | 64 ---- .../presentation/widgets/StreamsCarousel.kt | 82 ----- .../data/api/MostPopularMoviesService.kt | 22 -- .../search/data/api/SearchStreamService.kt | 22 -- .../datasource/MostPopularMoviesDataSource.kt | 18 -- .../data/datasource/SearchStreamDataSource.kt | 17 -- .../data/model/ListSearchStreamResponse.kt | 22 -- .../repository/MostPopularMoviesRepository.kt | 16 - .../data/repository/SearchStreamRepository.kt | 16 - .../search/di/SearchModule.kt | 43 --- .../search/domain/MostPopularMoviesUseCase.kt | 17 -- .../search/domain/SearchUseCase.kt | 15 - .../search/domain/mapper/SearchMapper.kt | 11 - .../navigation/SearchStreamNavigation.kt | 30 -- .../presentation/screens/SearchScreen.kt | 173 ----------- .../presentation/screens/SearchUIState.kt | 10 - .../presentation/screens/SearchViewModel.kt | 113 ------- .../presentation/widgets/SearchCarousel.kt | 165 ----------- .../presentation/widgets/SearchStreamCard.kt | 128 -------- .../presentation/widgets/SearchStreams.kt | 251 ---------------- .../src/main/res/drawable/ic_top_10.webp | Bin 1518 -> 0 bytes .../main/res/drawable/image_placeholder.xml | 11 - .../src/main/res/drawable/netflix_detail.webp | Bin 4428 -> 0 bytes .../res/drawable/netflix_horizontal_logo.xml | 9 - .../src/main/res/drawable/play_circle.xml | 9 - .../main/res/layout/activity_list_stream.xml | 18 -- .../main/res/values/content-description.xml | 8 - .../src/main/res/values/strings.xml | 34 --- feature-profile/build.gradle.kts | 3 +- gradle/libs.versions.toml | 50 +--- settings.gradle.kts | 4 +- 83 files changed, 67 insertions(+), 3276 deletions(-) delete mode 100644 build-logic/src/main/java/com.streamplayer.compose.gradle.kts delete mode 100644 feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/core/ContentType.kt delete mode 100644 feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/detail/data/DetailStreamRepository.kt delete mode 100644 feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/detail/data/DetailStreamService.kt delete mode 100644 feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/detail/data/model/DetailStreamResponse.kt delete mode 100644 feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/detail/data/model/VideoStreamResponse.kt delete mode 100644 feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/detail/di/DetailStreamModule.kt delete mode 100644 feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/detail/domain/DetailStream.kt delete mode 100644 feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/detail/domain/DetailStreamMapper.kt delete mode 100644 feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/detail/domain/DetailStreamUseCase.kt delete mode 100644 feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/detail/domain/VideoStream.kt delete mode 100644 feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/detail/domain/VideoStreamsUseCase.kt delete mode 100644 feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/detail/presentation/navigation/DetailStreamNavigation.kt delete mode 100644 feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/detail/presentation/screens/DetailStreamViewModel.kt delete mode 100644 feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/detail/presentation/screens/DetailStreamsScreen.kt delete mode 100644 feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/detail/presentation/screens/DetailStreamsUIState.kt delete mode 100644 feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/detail/presentation/widget/DetailStreamActionOption.kt delete mode 100644 feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/detail/presentation/widget/DetailStreamButtonAction.kt delete mode 100644 feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/detail/presentation/widget/DetailStreamImagePreview.kt delete mode 100644 feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/detail/presentation/widget/DetailStreamRowHeader.kt delete mode 100644 feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/detail/presentation/widget/DetailStreamToolbar.kt delete mode 100644 feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/list/data/ListStreamRepository.kt delete mode 100644 feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/list/data/ListStreamService.kt delete mode 100644 feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/list/data/StreamDataSource.kt delete mode 100644 feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/list/data/model/GenresResponse.kt delete mode 100644 feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/list/data/model/ListStreamResponse.kt delete mode 100644 feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/list/di/ListStreamModule.kt delete mode 100644 feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/list/domain/GetGenresUseCase.kt delete mode 100644 feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/list/domain/GetLatestMovieUseCase.kt delete mode 100644 feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/list/domain/ListMovieUseCase.kt delete mode 100644 feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/list/domain/ListStreamAnalytics.kt delete mode 100644 feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/list/domain/ListStreamMapper.kt delete mode 100644 feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/list/domain/model/Genre.kt delete mode 100644 feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/list/domain/model/HighlightBanner.kt delete mode 100644 feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/list/domain/model/ListStream.kt delete mode 100644 feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/list/presentation/navigation/ListStreamsNavigation.kt delete mode 100644 feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/list/presentation/screens/ListStreamViewModel.kt delete mode 100644 feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/list/presentation/screens/ListStreamsScreen.kt delete mode 100644 feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/list/presentation/screens/ListStreamsUIState.kt delete mode 100644 feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/list/presentation/widgets/HighlightBanner.kt delete mode 100644 feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/list/presentation/widgets/StreamsCard.kt delete mode 100644 feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/list/presentation/widgets/StreamsCarousel.kt delete mode 100644 feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/search/data/api/MostPopularMoviesService.kt delete mode 100644 feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/search/data/api/SearchStreamService.kt delete mode 100644 feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/search/data/datasource/MostPopularMoviesDataSource.kt delete mode 100644 feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/search/data/datasource/SearchStreamDataSource.kt delete mode 100644 feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/search/data/model/ListSearchStreamResponse.kt delete mode 100644 feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/search/data/repository/MostPopularMoviesRepository.kt delete mode 100644 feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/search/data/repository/SearchStreamRepository.kt delete mode 100644 feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/search/di/SearchModule.kt delete mode 100644 feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/search/domain/MostPopularMoviesUseCase.kt delete mode 100644 feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/search/domain/SearchUseCase.kt delete mode 100644 feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/search/domain/mapper/SearchMapper.kt delete mode 100644 feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/search/presentation/navigation/SearchStreamNavigation.kt delete mode 100644 feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/search/presentation/screens/SearchScreen.kt delete mode 100644 feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/search/presentation/screens/SearchUIState.kt delete mode 100644 feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/search/presentation/screens/SearchViewModel.kt delete mode 100644 feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/search/presentation/widgets/SearchCarousel.kt delete mode 100644 feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/search/presentation/widgets/SearchStreamCard.kt delete mode 100644 feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/search/presentation/widgets/SearchStreams.kt delete mode 100644 feature-list-streams/src/main/res/drawable/ic_top_10.webp delete mode 100644 feature-list-streams/src/main/res/drawable/image_placeholder.xml delete mode 100644 feature-list-streams/src/main/res/drawable/netflix_detail.webp delete mode 100644 feature-list-streams/src/main/res/drawable/netflix_horizontal_logo.xml delete mode 100644 feature-list-streams/src/main/res/drawable/play_circle.xml delete mode 100644 feature-list-streams/src/main/res/layout/activity_list_stream.xml delete mode 100644 feature-list-streams/src/main/res/values/content-description.xml delete mode 100644 feature-list-streams/src/main/res/values/strings.xml diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 4292f07d..ef98ee19 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -14,12 +14,9 @@ dependencies { implementation(projects.coreNetworking) implementation(projects.coreLocalStorage) - implementation(platform(libs.compose.bom)) - androidTestImplementation(platform(libs.compose.bom)) implementation(libs.bundles.koin) implementation(libs.bundles.androidSupport) - implementation(libs.bundles.compose) implementation(libs.bundles.kotlin) implementation(libs.lottie) diff --git a/build-logic/src/main/java/com.streamplayer.android-library.gradle.kts b/build-logic/src/main/java/com.streamplayer.android-library.gradle.kts index 20d826af..408de325 100644 --- a/build-logic/src/main/java/com.streamplayer.android-library.gradle.kts +++ b/build-logic/src/main/java/com.streamplayer.android-library.gradle.kts @@ -10,15 +10,14 @@ import extensions.setupPackingOptions val libs: VersionCatalog = extensions.getByType().named("libs") plugins { + //Note: The best approach might be to create a separate feature plugin to further isolate serialization, and do this later. + id("org.jetbrains.kotlin.plugin.serialization") id("com.android.library") - id("kotlin-android") id("kotlin-kapt") id("kotlin-parcelize") id("com.streamplayer.dokka") id("org.jetbrains.kotlinx.kover") id("com.streamplayer.detekt") - //Note: The best approach might be to create a separate feature plugin to further isolate serialization, and do this later. - id("org.jetbrains.kotlin.plugin.serialization") } android { diff --git a/build-logic/src/main/java/com.streamplayer.application.gradle.kts b/build-logic/src/main/java/com.streamplayer.application.gradle.kts index e5af04a1..9304e428 100644 --- a/build-logic/src/main/java/com.streamplayer.application.gradle.kts +++ b/build-logic/src/main/java/com.streamplayer.application.gradle.kts @@ -4,7 +4,6 @@ import extensions.dokkaPlugin import extensions.getLibrary import extensions.setupAndroidDefaultConfig import extensions.setupCompileOptions -import extensions.setupCompose import extensions.setupPackingOptions val libs: VersionCatalog = extensions.getByType().named("libs") @@ -27,7 +26,6 @@ android { setupCompileOptions() setupPackingOptions() setupAndroidDefaultConfig() - setupCompose(catalog) defaultConfig { applicationId = Config.applicationId diff --git a/build-logic/src/main/java/com.streamplayer.compose.gradle.kts b/build-logic/src/main/java/com.streamplayer.compose.gradle.kts deleted file mode 100644 index c6542e3e..00000000 --- a/build-logic/src/main/java/com.streamplayer.compose.gradle.kts +++ /dev/null @@ -1,22 +0,0 @@ -@file:Suppress("UnstableApiUsage") -import extensions.getBundle -import extensions.getLibrary -import extensions.setupCompose - -plugins { - id("com.streamplayer.android-library") -} - -val libs: VersionCatalog = extensions.getByType().named("libs") - -android { - setupCompose(libs) -} - -dependencies { - implementation(platform(libs.getLibrary("compose.bom"))) - androidTestImplementation(platform(libs.getLibrary("compose.bom"))) - - implementation(libs.getBundle("compose")) - debugImplementation(libs.getLibrary("compose.ui.tooling")) -} \ No newline at end of file diff --git a/build-logic/src/main/java/extensions/CommonExtensions.kt b/build-logic/src/main/java/extensions/CommonExtensions.kt index 84543ee2..fbb7328d 100644 --- a/build-logic/src/main/java/extensions/CommonExtensions.kt +++ b/build-logic/src/main/java/extensions/CommonExtensions.kt @@ -41,33 +41,8 @@ internal fun CommonExtension<*, *, *, *, *>.setupCompileOptions() { sourceCompatibility = JavaVersion.VERSION_17 targetCompatibility = JavaVersion.VERSION_17 } - - kotlinOptions { - jvmTarget = "17" - } -} - -fun CommonExtension<*, *, *, *, *>.setupCompose(catalog: VersionCatalog) { - buildFeatures { - compose = true - buildConfig = true - } - - composeOptions { - kotlinCompilerExtensionVersion = "${catalog.getVersion("compose")}" - } - - packaging { - resources { - excludes.apply { - add("META-INF/AL2.0") - add("META-INF/LGPL2.1") - } - } - } } - internal fun CommonExtension<*, *, *, *, *>.setupNameSpace(project: Project) { val moduleName = project.displayName .removePrefix("project ") diff --git a/build.gradle.kts b/build.gradle.kts index 2eb41b1b..7597b231 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -6,6 +6,9 @@ plugins { alias(libs.plugins.android.library) apply false alias(libs.plugins.kotlin.android) apply false alias(libs.plugins.serialization) apply false + alias(libs.plugins.kotlin.multiplatform) apply false + alias(libs.plugins.jetbrains.compose) apply false + alias(libs.plugins.compose.compiler) apply false alias(libs.plugins.ksp) apply false alias(libs.plugins.dokka) apply false alias(libs.plugins.kover) apply false @@ -15,6 +18,8 @@ tasks.register("clean", Delete::class) { delete(rootProject.buildDir) } + + allprojects { repositories { google() diff --git a/core-local-storage/build.gradle.kts b/core-local-storage/build.gradle.kts index c8e2ff05..6fc9f020 100644 --- a/core-local-storage/build.gradle.kts +++ b/core-local-storage/build.gradle.kts @@ -1,6 +1,7 @@ plugins { id("com.streamplayer.android-library") id("com.google.devtools.ksp") + id("kotlin-android") } dependencies { diff --git a/core-navigation/build.gradle.kts b/core-navigation/build.gradle.kts index f7a359d3..f1f5da6e 100644 --- a/core-navigation/build.gradle.kts +++ b/core-navigation/build.gradle.kts @@ -1,9 +1,13 @@ @file:Suppress("UnstableApiUsage") plugins { id("com.streamplayer.android-library") - id("com.streamplayer.compose") + id("kotlin-android") + alias(libs.plugins.jetbrains.compose) + alias(libs.plugins.compose.compiler) } dependencies { implementation(libs.bundles.kotlin) + implementation(libs.navigation.compose) + implementation(compose.material3) } \ No newline at end of file diff --git a/core-networking/build.gradle.kts b/core-networking/build.gradle.kts index f5602562..fd4f7b53 100644 --- a/core-networking/build.gradle.kts +++ b/core-networking/build.gradle.kts @@ -1,5 +1,6 @@ plugins { id("com.streamplayer.android-library") + id("kotlin-android") } android { buildFeatures { diff --git a/core-shared-ui/build.gradle.kts b/core-shared-ui/build.gradle.kts index 5f46ed8a..1450afc3 100644 --- a/core-shared-ui/build.gradle.kts +++ b/core-shared-ui/build.gradle.kts @@ -1,10 +1,14 @@ @file:Suppress("UnstableApiUsage") plugins { id("com.streamplayer.android-library") - id("com.streamplayer.compose") + alias(libs.plugins.jetbrains.compose) + alias(libs.plugins.compose.compiler) + id("kotlin-android") } dependencies { + implementation(compose.material3) + implementation(compose.preview) implementation(projects.coreShared) implementation(libs.bundles.koin) implementation(libs.bundles.kotlin) diff --git a/core-shared/build.gradle.kts b/core-shared/build.gradle.kts index f1b45dda..304bcb93 100644 --- a/core-shared/build.gradle.kts +++ b/core-shared/build.gradle.kts @@ -1,5 +1,6 @@ plugins { id("com.streamplayer.android-library") + id("kotlin-android") } dependencies { diff --git a/feature-favorites/build.gradle.kts b/feature-favorites/build.gradle.kts index 6bf94b61..ee61df78 100644 --- a/feature-favorites/build.gradle.kts +++ b/feature-favorites/build.gradle.kts @@ -1,7 +1,8 @@ @file:Suppress("UnstableApiUsage") plugins { id("com.streamplayer.android-library") - id("com.streamplayer.compose") + alias(libs.plugins.jetbrains.compose) + alias(libs.plugins.compose.compiler) } dependencies { diff --git a/feature-list-streams/build.gradle.kts b/feature-list-streams/build.gradle.kts index 7faf9beb..7e76b291 100644 --- a/feature-list-streams/build.gradle.kts +++ b/feature-list-streams/build.gradle.kts @@ -1,28 +1,41 @@ @file:Suppress("UnstableApiUsage") +import org.jetbrains.kotlin.gradle.ExperimentalKotlinGradlePluginApi +import org.jetbrains.kotlin.gradle.dsl.JvmTarget + + plugins { id("com.streamplayer.android-library") - id("com.streamplayer.compose") alias(libs.plugins.ksp) + alias(libs.plugins.kotlin.multiplatform) + alias(libs.plugins.jetbrains.compose) + alias(libs.plugins.compose.compiler) } -dependencies { - implementation(projects.coreNetworking) - implementation(projects.coreNavigation) - implementation(projects.coreShared) - implementation(projects.coreSharedUi) - implementation(projects.coreLocalStorage) - - implementation(libs.bundles.koin) - implementation(libs.koin.annotations) - ksp(libs.koin.compiler) - - implementation(libs.bundles.networking) - implementation(libs.roomRuntime) - implementation(libs.bundles.androidSupport) - implementation(libs.coil) +kotlin { + androidTarget { + @OptIn(ExperimentalKotlinGradlePluginApi::class) + compilerOptions { + jvmTarget.set(JvmTarget.JVM_17) + } + } - testImplementation(libs.bundles.test) + sourceSets { + androidMain.dependencies { + implementation(compose.material3) + implementation(compose.ui) + implementation(projects.coreNetworking) + implementation(projects.coreNavigation) + implementation(projects.coreShared) + implementation(projects.coreSharedUi) + implementation(projects.coreLocalStorage) + implementation(libs.bundles.koin) + implementation(libs.bundles.networking) + implementation(libs.coil) + implementation(libs.koin.annotations) + implementation(libs.bundles.androidSupport) + } + } } ksp { diff --git a/feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/core/ContentType.kt b/feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/core/ContentType.kt deleted file mode 100644 index eef6a23b..00000000 --- a/feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/core/ContentType.kt +++ /dev/null @@ -1,17 +0,0 @@ -package com.codandotv.streamplayerapp.feature_list_streams.core - -import androidx.annotation.StringRes -import com.codandotv.streamplayerapp.feature.list.streams.R - -enum class ContentType(@StringRes val contentName: Int, @StringRes val contentNameAsPlural: Int) { - SHOW(R.string.list_content_type_show, R.string.list_content_type_show_plural), - FILM(R.string.list_content_type_film, R.string.list_content_type_film_plural); - - companion object { - fun getContentName(contentType: ContentType) = - values().first { contentType == it }.contentName - - fun getContentNameAsPlural(contentType: ContentType) = - values().first { contentType == it }.contentNameAsPlural - } -} \ No newline at end of file diff --git a/feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/detail/data/DetailStreamRepository.kt b/feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/detail/data/DetailStreamRepository.kt deleted file mode 100644 index 7fd6c5e1..00000000 --- a/feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/detail/data/DetailStreamRepository.kt +++ /dev/null @@ -1,54 +0,0 @@ -package com.codandotv.streamplayerapp.feature_list_streams.detail.data - -import com.codandotv.streamplayerapp.core_local_storage.data.dao.FavoriteDao -import com.codandotv.streamplayerapp.core_networking.handleError.toFlow -import com.codandotv.streamplayerapp.feature_list_streams.detail.domain.DetailStream -import com.codandotv.streamplayerapp.feature_list_streams.detail.domain.VideoStream -import com.codandotv.streamplayerapp.feature_list_streams.detail.domain.toDetailStream -import com.codandotv.streamplayerapp.feature_list_streams.detail.domain.toDetailStreamLocal -import com.codandotv.streamplayerapp.feature_list_streams.detail.domain.toVideoStreams -import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.map - -interface DetailStreamRepository { - suspend fun getMovie(): Flow - suspend fun deleteFromMyList(movie: String) - suspend fun insertToMyList(movie: DetailStream) - suspend fun isFavorite(movieId:String) : Boolean - suspend fun getVideoStreams(): Flow> -} - -class DetailStreamRepositoryImpl( - private val movieId: String, - private val service: DetailStreamService, - private val favoriteDao: FavoriteDao, -) : DetailStreamRepository { - - override suspend fun getMovie(): Flow = - service.getMovie(movieId) - .toFlow() - .map { - it.toDetailStream(isFavorite(movieId)) - } - - - override suspend fun deleteFromMyList(movie: String) = favoriteDao.delete(movie) - - override suspend fun insertToMyList(movie: DetailStream) = favoriteDao.insert(movie.toDetailStreamLocal()) - - /** - * Verify if movieId was saved as favorite - * @param movieId - * @return Boolean - */ - override suspend fun isFavorite(movieId: String) : Boolean = favoriteDao.fetchAll().any { - movie -> movie.id == movieId - } - - override suspend fun getVideoStreams(): Flow> = - service.getVideoStreams(movieId) - .toFlow() - .map { - it.toVideoStreams() - } -} \ No newline at end of file diff --git a/feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/detail/data/DetailStreamService.kt b/feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/detail/data/DetailStreamService.kt deleted file mode 100644 index 909b90d8..00000000 --- a/feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/detail/data/DetailStreamService.kt +++ /dev/null @@ -1,29 +0,0 @@ -package com.codandotv.streamplayerapp.feature_list_streams.detail.data - -import com.codandotv.streamplayerapp.core_networking.handleError.NetworkResponse -import com.codandotv.streamplayerapp.core_networking.handleError.safeRequest -import com.codandotv.streamplayerapp.feature_list_streams.detail.data.model.DetailStreamResponse -import com.codandotv.streamplayerapp.feature_list_streams.detail.data.model.VideoStreamsResponse -import io.ktor.client.HttpClient -import io.ktor.client.request.url -import org.koin.core.annotation.Factory - -interface DetailStreamService { - suspend fun getMovie(movieId: String): NetworkResponse - suspend fun getVideoStreams(movieId: String): NetworkResponse -} - -class DetailStreamServiceImpl( - private val client: HttpClient -) : DetailStreamService { - - override suspend fun getMovie(movieId: String): NetworkResponse = - client.safeRequest { - url("movie/$movieId") - } - - override suspend fun getVideoStreams(movieId: String): NetworkResponse = - client.safeRequest { - url("movie/$movieId/videos") - } -} diff --git a/feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/detail/data/model/DetailStreamResponse.kt b/feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/detail/data/model/DetailStreamResponse.kt deleted file mode 100644 index 1b873883..00000000 --- a/feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/detail/data/model/DetailStreamResponse.kt +++ /dev/null @@ -1,14 +0,0 @@ -package com.codandotv.streamplayerapp.feature_list_streams.detail.data.model - -import kotlinx.serialization.Serializable - -@Serializable -@Suppress("ConstructorParameterNaming") -data class DetailStreamResponse( - val id : Long, - val title : String, - val overview : String, - val tagline : String, - val backdrop_path : String, - val release_date : String -) \ No newline at end of file diff --git a/feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/detail/data/model/VideoStreamResponse.kt b/feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/detail/data/model/VideoStreamResponse.kt deleted file mode 100644 index 42fd495c..00000000 --- a/feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/detail/data/model/VideoStreamResponse.kt +++ /dev/null @@ -1,20 +0,0 @@ -package com.codandotv.streamplayerapp.feature_list_streams.detail.data.model - -import kotlinx.serialization.Serializable - -@Serializable -data class VideoStreamResponse( - val id: String, - val name: String, - val key: String, - val site: String, - val size: Int, - val official: Boolean, - val type: String, -) - -@Serializable -data class VideoStreamsResponse( - val id: Long, - val results: List -) \ No newline at end of file diff --git a/feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/detail/di/DetailStreamModule.kt b/feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/detail/di/DetailStreamModule.kt deleted file mode 100644 index eec3b2be..00000000 --- a/feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/detail/di/DetailStreamModule.kt +++ /dev/null @@ -1,58 +0,0 @@ -package com.codandotv.streamplayerapp.feature_list_streams.detail.di - -import com.codandotv.streamplayerapp.feature_list_streams.detail.data.DetailStreamRepository -import com.codandotv.streamplayerapp.feature_list_streams.detail.data.DetailStreamRepositoryImpl -import com.codandotv.streamplayerapp.feature_list_streams.detail.data.DetailStreamService -import com.codandotv.streamplayerapp.feature_list_streams.detail.data.DetailStreamServiceImpl -import com.codandotv.streamplayerapp.feature_list_streams.detail.domain.DetailStreamUseCase -import com.codandotv.streamplayerapp.feature_list_streams.detail.domain.DetailStreamUseCaseImpl -import com.codandotv.streamplayerapp.feature_list_streams.detail.domain.VideoStreamsUseCase -import com.codandotv.streamplayerapp.feature_list_streams.detail.domain.VideoStreamsUseCaseImpl -import com.codandotv.streamplayerapp.feature_list_streams.detail.presentation.screens.DetailStreamViewModel -import kotlinx.coroutines.Dispatchers -import org.koin.androidx.viewmodel.dsl.viewModel -import org.koin.core.parameter.parametersOf -import org.koin.dsl.module - -object DetailStreamModule { - val module = module { - viewModel { (id: String) -> - DetailStreamViewModel( - detailStreamUseCase = get { - parametersOf(id) - }, - videoStreamsUseCase = get { - parametersOf(id) - }, - dispatcher = Dispatchers.IO - ) - } - factory { (id: String) -> - DetailStreamUseCaseImpl( - detailStreamRepository = get { - parametersOf(id) - } - ) - } - factory { (id: String) -> - VideoStreamsUseCaseImpl( - detailStreamRepository = get { - parametersOf(id) - } - ) - } - factory { (id: String) -> - DetailStreamRepositoryImpl( - favoriteDao = get(), - service = get(), - movieId = id, - ) - } - - factory { - DetailStreamServiceImpl( - client = get() - ) - } - } -} \ No newline at end of file diff --git a/feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/detail/domain/DetailStream.kt b/feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/detail/domain/DetailStream.kt deleted file mode 100644 index 0cd575a5..00000000 --- a/feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/detail/domain/DetailStream.kt +++ /dev/null @@ -1,11 +0,0 @@ -package com.codandotv.streamplayerapp.feature_list_streams.detail.domain - -data class DetailStream( - val id : String, - val title : String, - val overview : String, - val tagline : String, - val url : String, - val releaseYear : String, - val isFavorite: Boolean -) \ No newline at end of file diff --git a/feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/detail/domain/DetailStreamMapper.kt b/feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/detail/domain/DetailStreamMapper.kt deleted file mode 100644 index 0362a3fb..00000000 --- a/feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/detail/domain/DetailStreamMapper.kt +++ /dev/null @@ -1,35 +0,0 @@ -package com.codandotv.streamplayerapp.feature_list_streams.detail.domain - -import com.codandotv.streamplayerapp.core_local_storage.domain.model.MovieEntity -import com.codandotv.streamplayerapp.core_networking.Url.IMAGE_URL_SIZE_500 -import com.codandotv.streamplayerapp.feature_list_streams.detail.data.model.DetailStreamResponse -import com.codandotv.streamplayerapp.feature_list_streams.detail.data.model.VideoStreamsResponse - -fun DetailStreamResponse.toDetailStream(isFavorite: Boolean = false): DetailStream = - DetailStream( - id = this.id.toString(), - title = this.title, - overview = this.overview, - tagline = this.tagline, - url = "$IMAGE_URL_SIZE_500${this.backdrop_path}", - releaseYear = this.release_date.substring(0, 4), - isFavorite = isFavorite - ) - -fun DetailStream.toDetailStreamLocal(): MovieEntity = - MovieEntity( - id = this.id, - title = this.title, - overview = this.overview, - tagline = this.tagline, - url = this.url, - releaseYear = this.releaseYear, - ) - -fun VideoStreamsResponse.toVideoStreams(): List = - results.map { - VideoStream( - videoId = it.key, - movieId = this.id - ) - } diff --git a/feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/detail/domain/DetailStreamUseCase.kt b/feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/detail/domain/DetailStreamUseCase.kt deleted file mode 100644 index 88bd4705..00000000 --- a/feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/detail/domain/DetailStreamUseCase.kt +++ /dev/null @@ -1,26 +0,0 @@ -package com.codandotv.streamplayerapp.feature_list_streams.detail.domain - -import com.codandotv.streamplayerapp.feature_list_streams.detail.data.DetailStreamRepository -import kotlinx.coroutines.flow.Flow - -interface DetailStreamUseCase { - suspend fun getMovie(): Flow - - suspend fun toggleItemInFavorites(movie: DetailStream) -} - -class DetailStreamUseCaseImpl( - private val detailStreamRepository: DetailStreamRepository -) : DetailStreamUseCase { - - override suspend fun getMovie(): Flow = - detailStreamRepository.getMovie() - - override suspend fun toggleItemInFavorites(movie: DetailStream) { - if (detailStreamRepository.isFavorite(movie.id)) { - detailStreamRepository.deleteFromMyList(movie.id) - } else { - detailStreamRepository.insertToMyList(movie) - } - } -} \ No newline at end of file diff --git a/feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/detail/domain/VideoStream.kt b/feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/detail/domain/VideoStream.kt deleted file mode 100644 index b97c964f..00000000 --- a/feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/detail/domain/VideoStream.kt +++ /dev/null @@ -1,6 +0,0 @@ -package com.codandotv.streamplayerapp.feature_list_streams.detail.domain - -data class VideoStream( - val movieId: Long, - val videoId: String, -) \ No newline at end of file diff --git a/feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/detail/domain/VideoStreamsUseCase.kt b/feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/detail/domain/VideoStreamsUseCase.kt deleted file mode 100644 index b15bb7bd..00000000 --- a/feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/detail/domain/VideoStreamsUseCase.kt +++ /dev/null @@ -1,16 +0,0 @@ -package com.codandotv.streamplayerapp.feature_list_streams.detail.domain - -import com.codandotv.streamplayerapp.feature_list_streams.detail.data.DetailStreamRepository -import kotlinx.coroutines.flow.Flow - -interface VideoStreamsUseCase { - suspend fun getVideoStreams(): Flow> -} - -class VideoStreamsUseCaseImpl( - private val detailStreamRepository: DetailStreamRepository -) : VideoStreamsUseCase { - override suspend fun getVideoStreams(): Flow> { - return detailStreamRepository.getVideoStreams() - } -} \ No newline at end of file diff --git a/feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/detail/presentation/navigation/DetailStreamNavigation.kt b/feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/detail/presentation/navigation/DetailStreamNavigation.kt deleted file mode 100644 index 9db0b723..00000000 --- a/feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/detail/presentation/navigation/DetailStreamNavigation.kt +++ /dev/null @@ -1,37 +0,0 @@ -package com.codandotv.streamplayerapp.feature_list_streams.detail.presentation.navigation - -import androidx.lifecycle.Lifecycle -import androidx.navigation.NavGraphBuilder -import androidx.navigation.NavHostController -import androidx.navigation.compose.composable -import com.codandotv.streamplayerapp.core_navigation.routes.Routes -import com.codandotv.streamplayerapp.core_navigation.routes.Routes.DETAIL_COMPLETE -import com.codandotv.streamplayerapp.core_navigation.routes.Routes.PARAM.ID -import com.codandotv.streamplayerapp.feature_list_streams.detail.di.DetailStreamModule -import com.codandotv.streamplayerapp.feature_list_streams.detail.presentation.screens.DetailStreamScreen -import org.koin.androidx.compose.koinViewModel -import org.koin.core.context.loadKoinModules -import org.koin.core.context.unloadKoinModules -import org.koin.core.parameter.parametersOf - -internal const val DEFAULT_ID = "0" - -fun NavGraphBuilder.detailStreamNavGraph(navController: NavHostController) { - composable(DETAIL_COMPLETE) { nav -> - if (nav.lifecycle.currentState == Lifecycle.State.STARTED) { - loadKoinModules(DetailStreamModule.module) - } - DetailStreamScreen( - viewModel = koinViewModel { - parametersOf(nav.arguments?.getString(ID) ?: DEFAULT_ID) - }, - navController = navController, - onNavigateSearchScreen = { - navController.navigate(Routes.SEARCH) - }, - disposable = { - unloadKoinModules(DetailStreamModule.module) - } - ) - } -} diff --git a/feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/detail/presentation/screens/DetailStreamViewModel.kt b/feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/detail/presentation/screens/DetailStreamViewModel.kt deleted file mode 100644 index 5d3ca5ba..00000000 --- a/feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/detail/presentation/screens/DetailStreamViewModel.kt +++ /dev/null @@ -1,66 +0,0 @@ -package com.codandotv.streamplayerapp.feature_list_streams.detail.presentation.screens - -import androidx.lifecycle.ViewModel -import androidx.lifecycle.viewModelScope -import com.codandotv.streamplayerapp.core_networking.handleError.catchFailure -import com.codandotv.streamplayerapp.feature_list_streams.detail.domain.DetailStream -import com.codandotv.streamplayerapp.feature_list_streams.detail.domain.DetailStreamUseCase -import com.codandotv.streamplayerapp.feature_list_streams.detail.domain.VideoStreamsUseCase -import com.codandotv.streamplayerapp.feature_list_streams.detail.presentation.screens.DetailStreamsUIState.DetailStreamsLoadedUIState -import com.codandotv.streamplayerapp.feature_list_streams.detail.presentation.screens.DetailStreamsUIState.LoadingStreamUIState -import kotlinx.coroutines.CoroutineDispatcher -import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.SharingStarted -import kotlinx.coroutines.flow.StateFlow -import kotlinx.coroutines.flow.flowOn -import kotlinx.coroutines.flow.onStart -import kotlinx.coroutines.flow.stateIn -import kotlinx.coroutines.flow.update -import kotlinx.coroutines.flow.zip -import kotlinx.coroutines.launch - -class DetailStreamViewModel( - private val detailStreamUseCase: DetailStreamUseCase, - private val videoStreamsUseCase: VideoStreamsUseCase, - private val dispatcher: CoroutineDispatcher -) : ViewModel() { - - private val _uiState = MutableStateFlow(LoadingStreamUIState) - val uiState: StateFlow = _uiState.stateIn( - viewModelScope, - SharingStarted.Eagerly, - initialValue = _uiState.value - ) - - fun loadDetail() { - viewModelScope.launch { - detailStreamUseCase.getMovie() - .zip(videoStreamsUseCase.getVideoStreams()) { detailStream, videoUrl -> - DetailStreamsLoadedUIState( - detailStream = detailStream, - videoId = videoUrl.firstOrNull()?.videoId - ) - } - .flowOn(dispatcher) - .onStart { onLoading() } - .catchFailure { - println(">>>> ${it.errorMessage}") - } - .collect { result -> - _uiState.update { - result - } - } - } - } - - private fun onLoading() { - _uiState.update { LoadingStreamUIState } - } - - fun toggleItemInFavorites(detailStream: DetailStream) { - viewModelScope.launch { - detailStreamUseCase.toggleItemInFavorites(detailStream) - } - } -} \ No newline at end of file diff --git a/feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/detail/presentation/screens/DetailStreamsScreen.kt b/feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/detail/presentation/screens/DetailStreamsScreen.kt deleted file mode 100644 index d4cbc47a..00000000 --- a/feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/detail/presentation/screens/DetailStreamsScreen.kt +++ /dev/null @@ -1,203 +0,0 @@ -package com.codandotv.streamplayerapp.feature_list_streams.detail.presentation.screens - -import android.annotation.SuppressLint -import androidx.activity.compose.BackHandler -import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.Spacer -import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.height -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.rememberScrollState -import androidx.compose.foundation.verticalScroll -import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.filled.FileDownload -import androidx.compose.material.icons.filled.PlayArrow -import androidx.compose.material3.ButtonDefaults -import androidx.compose.material3.CircularProgressIndicator -import androidx.compose.material3.ExperimentalMaterial3Api -import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.Scaffold -import androidx.compose.material3.Text -import androidx.compose.runtime.Composable -import androidx.compose.runtime.DisposableEffect -import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.remember -import androidx.compose.runtime.setValue -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.platform.LocalLifecycleOwner -import androidx.compose.ui.res.stringResource -import androidx.compose.ui.text.font.FontWeight -import androidx.compose.ui.unit.dp -import androidx.compose.ui.unit.em -import androidx.compose.ui.unit.sp -import androidx.lifecycle.Lifecycle -import androidx.lifecycle.compose.LifecycleEventEffect -import androidx.lifecycle.compose.collectAsStateWithLifecycle -import androidx.navigation.NavController -import com.codandotv.streamplayerapp.core_shared_ui.widget.SharingStreamCustomView -import com.codandotv.streamplayerapp.feature.list.streams.R -import com.codandotv.streamplayerapp.feature_list_streams.detail.domain.DetailStream -import com.codandotv.streamplayerapp.feature_list_streams.detail.presentation.widget.DetailStreamActionOption -import com.codandotv.streamplayerapp.feature_list_streams.detail.presentation.widget.DetailStreamButtonAction -import com.codandotv.streamplayerapp.feature_list_streams.detail.presentation.widget.DetailStreamImagePreview -import com.codandotv.streamplayerapp.feature_list_streams.detail.presentation.widget.DetailStreamRowHeader -import com.codandotv.streamplayerapp.feature_list_streams.detail.presentation.widget.DetailStreamToolbar -import org.koin.androidx.compose.koinViewModel - -@Composable -fun DetailStreamScreen( - viewModel: DetailStreamViewModel = koinViewModel(), - navController: NavController, - disposable: () -> Unit = {}, - onNavigateSearchScreen: () -> Unit = {}, -) { - val uiState by viewModel.uiState.collectAsStateWithLifecycle() - - val lifecycleOwner = LocalLifecycleOwner.current - - LifecycleEventEffect(Lifecycle.Event.ON_START) { - viewModel.loadDetail() - } - - DisposableEffect(lifecycleOwner) { - onDispose { - disposable.invoke() - } - } - - when (uiState) { - is DetailStreamsUIState.DetailStreamsLoadedUIState -> { - SetupDetailScreen( - onToggleToMyList = { detailStream -> viewModel.toggleItemInFavorites(detailStream) }, - uiState = uiState as DetailStreamsUIState.DetailStreamsLoadedUIState, - navController = navController, - onNavigateSearchScreen = onNavigateSearchScreen - ) - } - - else -> { - Box(Modifier.fillMaxSize()) { - CircularProgressIndicator( - modifier = Modifier.align( - Alignment.Center - ) - ) - } - } - - } -} - -@OptIn(ExperimentalMaterial3Api::class) -@Suppress("LongMethod") -@SuppressLint("UnusedMaterial3ScaffoldPaddingParameter") -@Composable -private fun SetupDetailScreen( - onToggleToMyList: (DetailStream) -> Unit, - uiState: DetailStreamsUIState.DetailStreamsLoadedUIState, - navController: NavController, - onNavigateSearchScreen: () -> Unit = {}, -) { - val showDialog = remember { mutableStateOf(false) } - - var showPlayer by remember { mutableStateOf(false) } - - Scaffold( - topBar = { - DetailStreamToolbar( - navController = navController, - onNavigateSearchScreen = onNavigateSearchScreen - ) - }, - content = { innerPadding -> - Column( - Modifier - .fillMaxWidth() - .verticalScroll(rememberScrollState()) - .padding(innerPadding) - ) { - DetailStreamImagePreview( - uiState = uiState, - onPlayEvent = { - showPlayer = true - }, - showPlayer = showPlayer - ) - Column( - modifier = Modifier - .fillMaxWidth() - .padding(start = 16.dp, end = 16.dp, top = 8.dp) - ) { - DetailStreamRowHeader() - Text( - text = uiState.detailStream.title, - style = MaterialTheme.typography.headlineMedium.copy( - fontWeight = FontWeight.Bold, fontSize = 28.sp - ) - ) - Spacer(modifier = Modifier.height(8.dp)) - Text( - text = uiState.detailStream.releaseYear, - style = MaterialTheme.typography.headlineMedium.copy( - color = MaterialTheme.colorScheme.onSurfaceVariant, - fontSize = 14.sp, fontWeight = FontWeight.Bold - ) - ) - Spacer(modifier = Modifier.height(8.dp)) - DetailStreamButtonAction( - buttonsColors = ButtonDefaults.buttonColors( - containerColor = MaterialTheme.colorScheme.surface - ), - imageVector = Icons.Filled.PlayArrow, - imageVectorColor = MaterialTheme.colorScheme.onSurface, - text = stringResource(R.string.detail_watch_primary_button), - textColor = MaterialTheme.colorScheme.onSurface - ) - Spacer(modifier = Modifier.height(4.dp)) - DetailStreamButtonAction( - buttonsColors = ButtonDefaults.buttonColors( - containerColor = MaterialTheme.colorScheme.onSurfaceVariant - ), - imageVector = Icons.Filled.FileDownload, - imageVectorColor = MaterialTheme.colorScheme.onSurface, - text = stringResource(id = R.string.detail_default_text_secondary_button), - textColor = MaterialTheme.colorScheme.onSurface, - ) - Text( - text = uiState.detailStream.overview, - style = MaterialTheme.typography.headlineMedium.copy( - color = MaterialTheme.colorScheme.onSurface, - fontSize = 16.sp, - lineHeight = 1.25.em - ), - modifier = Modifier.padding(top = 8.dp, bottom = 16.dp) - ) - Spacer(modifier = Modifier.height(8.dp)) - DetailStreamActionOption( - uiState.detailStream, - onToggleToMyList, - { showDialog.value = true }) - Spacer(modifier = Modifier.height(16.dp)) - } - } - if (showDialog.value) { - SharingStreamCustomView( - contentTitle = uiState.detailStream.title, - contentUrl = uiState.detailStream.url, - setShowDialog = { - showDialog.value = it - }) - } - BackHandler { - if (showDialog.value) { - showDialog.value = false - } else { - navController.navigateUp() - } - } - }) -} \ No newline at end of file diff --git a/feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/detail/presentation/screens/DetailStreamsUIState.kt b/feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/detail/presentation/screens/DetailStreamsUIState.kt deleted file mode 100644 index cfc4ebb7..00000000 --- a/feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/detail/presentation/screens/DetailStreamsUIState.kt +++ /dev/null @@ -1,12 +0,0 @@ -package com.codandotv.streamplayerapp.feature_list_streams.detail.presentation.screens - -import com.codandotv.streamplayerapp.feature_list_streams.detail.domain.DetailStream - -sealed class DetailStreamsUIState { - data class DetailStreamsLoadedUIState( - val detailStream: DetailStream, - val videoId: String?, - ) : DetailStreamsUIState() - - object LoadingStreamUIState : DetailStreamsUIState() -} diff --git a/feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/detail/presentation/widget/DetailStreamActionOption.kt b/feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/detail/presentation/widget/DetailStreamActionOption.kt deleted file mode 100644 index e4b92603..00000000 --- a/feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/detail/presentation/widget/DetailStreamActionOption.kt +++ /dev/null @@ -1,77 +0,0 @@ -package com.codandotv.streamplayerapp.feature_list_streams.detail.presentation.widget - -import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.filled.Add -import androidx.compose.material.icons.filled.Check -import androidx.compose.material.icons.filled.Download -import androidx.compose.material.icons.filled.Share -import androidx.compose.material.icons.filled.ThumbUp -import androidx.compose.runtime.Composable -import androidx.compose.runtime.LaunchedEffect -import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.remember -import androidx.compose.runtime.saveable.rememberSaveable -import androidx.compose.runtime.setValue -import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.res.stringResource -import com.codandotv.streamplayerapp.core_shared_ui.widget.IconWithText -import com.codandotv.streamplayerapp.feature.list.streams.R -import com.codandotv.streamplayerapp.feature_list_streams.detail.domain.DetailStream - -@Composable -fun DetailStreamActionOption( - detailStream: DetailStream, - onToggleToMyList: (DetailStream) -> Unit, - onShowSharingOptions: () -> Unit, - modifier: Modifier = Modifier.fillMaxWidth() -) { - var checked by rememberSaveable { mutableStateOf(detailStream.isFavorite) } - var iconCheckList by remember { mutableStateOf(Icons.Filled.Add) } - - LaunchedEffect(checked) { - iconCheckList = - if (checked) Icons.Filled.Check else Icons.Filled.Add - } - - Row( - modifier = modifier, - horizontalArrangement = Arrangement.SpaceEvenly - ) { - IconWithText( - onClick = { - checked = !checked - onToggleToMyList(detailStream) - }, - imageVector = iconCheckList, - imageColor = Color.White, - text = stringResource(id = R.string.detail_my_list), - textColor = Color.Gray, - ) - IconWithText( - onClick = { TODO("Implementar mecanismo de classificação.") }, - imageVector = Icons.Filled.ThumbUp, - imageColor = Color.White, - text = stringResource(id = R.string.detail_classification), - textColor = Color.Gray, - ) - IconWithText( - onClick = { onShowSharingOptions.invoke() }, - imageVector = Icons.Filled.Share, - imageColor = Color.White, - text = stringResource(id = R.string.detail_share), - textColor = Color.Gray, - ) - IconWithText( - onClick = { TODO("Implementar mecanismo de download.") }, - imageVector = Icons.Filled.Download, - imageColor = Color.White, - text = stringResource(id = R.string.detail_download), - textColor = Color.Gray, - ) - } -} \ No newline at end of file diff --git a/feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/detail/presentation/widget/DetailStreamButtonAction.kt b/feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/detail/presentation/widget/DetailStreamButtonAction.kt deleted file mode 100644 index 0cdefd53..00000000 --- a/feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/detail/presentation/widget/DetailStreamButtonAction.kt +++ /dev/null @@ -1,59 +0,0 @@ -package com.codandotv.streamplayerapp.feature_list_streams.detail.presentation.widget - -import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.Spacer -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.size -import androidx.compose.foundation.layout.width -import androidx.compose.foundation.shape.RoundedCornerShape -import androidx.compose.material3.Button -import androidx.compose.material3.ButtonColors -import androidx.compose.material3.Icon -import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.Text -import androidx.compose.runtime.Composable -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.graphics.vector.ImageVector -import androidx.compose.ui.text.font.FontWeight -import androidx.compose.ui.unit.dp -import androidx.compose.ui.unit.sp - -@Suppress("LongParameterList") -@Composable -fun DetailStreamButtonAction( - buttonsColors: ButtonColors, - imageVector: ImageVector, - imageVectorColor: Color, - text: String, - textColor: Color, - modifier: Modifier = Modifier.fillMaxWidth(), -) { - Button( - onClick = { }, - shape = RoundedCornerShape(4.dp), - modifier = modifier, - colors = buttonsColors, - ) { - Row( - verticalAlignment = Alignment.CenterVertically - ) { - Icon( - imageVector, - contentDescription = null, - tint = imageVectorColor, - modifier = Modifier.size(28.dp) - ) - Spacer(modifier = Modifier.width(8.dp)) - Text( - text = text, - style = MaterialTheme.typography.headlineMedium.copy( - color = textColor, - fontWeight = FontWeight.Bold, - fontSize = 16.sp - ) - ) - } - } -} \ No newline at end of file diff --git a/feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/detail/presentation/widget/DetailStreamImagePreview.kt b/feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/detail/presentation/widget/DetailStreamImagePreview.kt deleted file mode 100644 index e1451f60..00000000 --- a/feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/detail/presentation/widget/DetailStreamImagePreview.kt +++ /dev/null @@ -1,71 +0,0 @@ -package com.codandotv.streamplayerapp.feature_list_streams.detail.presentation.widget - -import androidx.compose.foundation.background -import androidx.compose.foundation.clickable -import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.aspectRatio -import androidx.compose.foundation.layout.fillMaxHeight -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.size -import androidx.compose.foundation.shape.CircleShape -import androidx.compose.material3.Icon -import androidx.compose.runtime.Composable -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.layout.ContentScale -import androidx.compose.ui.res.painterResource -import androidx.compose.ui.unit.dp -import coil.compose.AsyncImage -import com.codandotv.streamplayerapp.core_shared_ui.widget.PlayerComponent -import com.codandotv.streamplayerapp.feature.list.streams.R -import com.codandotv.streamplayerapp.feature_list_streams.detail.presentation.screens.DetailStreamsUIState.DetailStreamsLoadedUIState - -@Suppress("MagicNumber") -@Composable -fun DetailStreamImagePreview( - uiState: DetailStreamsLoadedUIState, - modifier: Modifier = Modifier, - showPlayer: Boolean = false, - onPlayEvent: (() -> Unit) -) { - Box( - modifier = modifier - .fillMaxWidth() - .aspectRatio(16f / 9f), - contentAlignment = Alignment.Center - ) { - if (showPlayer) { - PlayerComponent( - videoId = uiState.videoId ?: "" - ) - } else { - AsyncImage( - model = uiState.detailStream.url, - contentScale = ContentScale.FillBounds, - contentDescription = uiState.detailStream.tagline, - modifier = Modifier - .fillMaxWidth() - .fillMaxHeight() - ) - - Box( - modifier = Modifier - .background(Color.Black.copy(alpha = 0.5f), CircleShape) - .size(50.dp) - .align(Alignment.Center), - ) - Icon( - painter = painterResource(id = R.drawable.play_circle), - tint = Color.White, - contentDescription = null, - modifier = Modifier - .size(64.dp) - .align(Alignment.Center) - .clickable { - onPlayEvent() - } - ) - } - } -} \ No newline at end of file diff --git a/feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/detail/presentation/widget/DetailStreamRowHeader.kt b/feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/detail/presentation/widget/DetailStreamRowHeader.kt deleted file mode 100644 index 5ef2dfa5..00000000 --- a/feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/detail/presentation/widget/DetailStreamRowHeader.kt +++ /dev/null @@ -1,49 +0,0 @@ -package com.codandotv.streamplayerapp.feature_list_streams.detail.presentation.widget - -import androidx.compose.foundation.Image -import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.offset -import androidx.compose.foundation.layout.size -import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.Text -import androidx.compose.runtime.Composable -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.res.painterResource -import androidx.compose.ui.res.stringResource -import androidx.compose.ui.text.font.FontWeight -import androidx.compose.ui.unit.dp -import androidx.compose.ui.unit.em -import androidx.compose.ui.unit.sp -import com.codandotv.streamplayerapp.feature.list.streams.R - -@Composable -fun DetailStreamRowHeader( - modifier: Modifier = Modifier.fillMaxWidth() -) { - Row( - verticalAlignment = Alignment.CenterVertically, - modifier = modifier - ) { - Image( - painter = painterResource(id = R.drawable.netflix_detail), - contentDescription = null, - modifier = Modifier - .size(26.dp) - .offset(x = (-6).dp) - - ) - Text( - text = stringResource(id = R.string.detail_movie), - modifier = Modifier.offset(x = (-6).dp), - style = MaterialTheme.typography.headlineMedium.copy( - color = Color.Gray, - fontWeight = FontWeight.Bold, - fontSize = 14.sp, - letterSpacing = 0.3.em - ) - ) - } -} \ No newline at end of file diff --git a/feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/detail/presentation/widget/DetailStreamToolbar.kt b/feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/detail/presentation/widget/DetailStreamToolbar.kt deleted file mode 100644 index 152ebe46..00000000 --- a/feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/detail/presentation/widget/DetailStreamToolbar.kt +++ /dev/null @@ -1,55 +0,0 @@ -package com.codandotv.streamplayerapp.feature_list_streams.detail.presentation.widget - -import androidx.compose.foundation.Image -import androidx.compose.foundation.layout.height -import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.filled.ArrowBack -import androidx.compose.material.icons.filled.Search -import androidx.compose.material3.ExperimentalMaterial3Api -import androidx.compose.material3.Icon -import androidx.compose.material3.IconButton -import androidx.compose.material3.Text -import androidx.compose.material3.TopAppBar -import androidx.compose.runtime.Composable -import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.res.painterResource -import androidx.compose.ui.res.stringResource -import androidx.compose.ui.unit.dp -import androidx.navigation.NavController -import com.codandotv.streamplayerapp.feature.list.streams.R - -@OptIn(ExperimentalMaterial3Api::class) -@Composable -fun DetailStreamToolbar( - navController: NavController, - onNavigateSearchScreen: () -> Unit = {} -) { - TopAppBar( - title = { Text(text = "") }, - modifier = Modifier.height(56.dp), - navigationIcon = { - IconButton(onClick = { navController.navigateUp() }) { - Icon( - imageVector = Icons.Filled.ArrowBack, - contentDescription = stringResource(id = R.string.detail_back) - ) - } - }, actions = { - IconButton(onClick = { - onNavigateSearchScreen.invoke() - }) { - Icon( - imageVector = Icons.Default.Search, - tint = Color.White, - contentDescription = stringResource(id = R.string.detail_search) - ) - } - IconButton(onClick = { }) { - Image( - painter = painterResource(id = com.codandotv.streamplayerapp.core.shared.ui.R.drawable.perfil_fake), - contentDescription = null - ) - } - }) -} \ No newline at end of file diff --git a/feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/list/data/ListStreamRepository.kt b/feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/list/data/ListStreamRepository.kt deleted file mode 100644 index 42a14d44..00000000 --- a/feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/list/data/ListStreamRepository.kt +++ /dev/null @@ -1,53 +0,0 @@ -package com.codandotv.streamplayerapp.feature_list_streams.list.data - -import androidx.paging.Pager -import androidx.paging.PagingConfig -import androidx.paging.PagingData -import com.codandotv.streamplayerapp.core_networking.handleError.toFlow -import com.codandotv.streamplayerapp.feature_list_streams.list.domain.model.Genre -import com.codandotv.streamplayerapp.feature_list_streams.list.domain.model.Stream -import com.codandotv.streamplayerapp.feature_list_streams.list.domain.toGenres -import com.codandotv.streamplayerapp.feature_list_streams.list.domain.toStream -import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.map -import org.koin.core.annotation.Factory - -interface ListStreamRepository { - suspend fun getGenres(): Flow> - - suspend fun topRatedStream(): Flow - - fun loadMovies(genre: Genre): Flow> -} - -@Factory -class ListStreamRepositoryImpl( - private val service: ListStreamService, -) : ListStreamRepository { - - override suspend fun getGenres(): Flow> { - return service.getGenres().toFlow().map { it.toGenres() } - } - - override suspend fun topRatedStream() = service.getTopRatedMovies().toFlow().map { - it.results.first { it.poster_path != null }.toStream() - } - - override fun loadMovies(genre: Genre): Flow> { - return Pager( - config = PagingConfig( - pageSize = PAGE_SIZE, - maxSize = MAX_SIZE, - ), - pagingSourceFactory = { - StreamDataSource(service, genreName = genre.name, genreId = genre.id) - }, - initialKey = 1 - ).flow - } - - companion object { - private const val PAGE_SIZE = 20 - private const val MAX_SIZE = 500 - } -} \ No newline at end of file diff --git a/feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/list/data/ListStreamService.kt b/feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/list/data/ListStreamService.kt deleted file mode 100644 index e2634895..00000000 --- a/feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/list/data/ListStreamService.kt +++ /dev/null @@ -1,59 +0,0 @@ -package com.codandotv.streamplayerapp.feature_list_streams.list.data - -import ListStreamResponse -import com.codandotv.streamplayerapp.core_networking.handleError.NetworkResponse -import com.codandotv.streamplayerapp.core_networking.handleError.safeRequest -import com.codandotv.streamplayerapp.feature_list_streams.list.data.model.GenresResponse -import io.ktor.client.HttpClient -import io.ktor.client.request.parameter -import io.ktor.client.request.url - -interface ListStreamService { - suspend fun getMovies(genres: String): NetworkResponse - suspend fun getPaginatedMovies(genres: String, page: Int): NetworkResponse - suspend fun getGenres(): NetworkResponse - suspend fun getTopRatedMovies( - sortBy: String = "vote_average.desc", - page: Int = 1 - ): NetworkResponse -} - -class ListStreamServiceImpl( - private val client: HttpClient -) : ListStreamService { - - override suspend fun getMovies(genres: String): NetworkResponse { - return client.safeRequest { - url("discover/movie") - parameter("with_genres", genres) - } - } - - override suspend fun getPaginatedMovies( - genres: String, - page: Int - ): NetworkResponse { - return client.safeRequest { - url("discover/movie") - parameter("with_genres", genres) - parameter("page", page) - } - } - - override suspend fun getGenres(): NetworkResponse { - return client.safeRequest { - url("genre/movie/list") - } - } - - override suspend fun getTopRatedMovies( - sortBy: String, - page: Int - ): NetworkResponse { - return client.safeRequest { - url("discover/movie") - parameter("sort_by", sortBy) - parameter("page", page) - } - } -} diff --git a/feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/list/data/StreamDataSource.kt b/feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/list/data/StreamDataSource.kt deleted file mode 100644 index feec9ad6..00000000 --- a/feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/list/data/StreamDataSource.kt +++ /dev/null @@ -1,44 +0,0 @@ -package com.codandotv.streamplayerapp.feature_list_streams.list.data - -import androidx.paging.PagingSource -import androidx.paging.PagingState -import com.codandotv.streamplayerapp.core_networking.handleError.NetworkResponse -import com.codandotv.streamplayerapp.feature_list_streams.list.domain.model.Stream -import com.codandotv.streamplayerapp.feature_list_streams.list.domain.toListStream - -@Suppress("TooGenericExceptionCaught", "UseCheckOrError") -class StreamDataSource( - private val service: ListStreamService, - private val genreId: Long, - private val genreName: String, -) : PagingSource() { - - override suspend fun load(params: LoadParams): LoadResult { - val nextPageNumber = params.key ?: START_PAGE_INDEX - - return try { - val response = service.getPaginatedMovies( - genres = genreId.toString(), - page = nextPageNumber - ) - - if (response is NetworkResponse.Success) { - LoadResult.Page( - data = response.value.toListStream(genreName).streams, - prevKey = if (nextPageNumber > 1) nextPageNumber - 1 else null, - nextKey = nextPageNumber.plus(1) - ) - } else { - throw IllegalStateException("Something wrong") - } - } catch (exception: Exception) { - LoadResult.Error(exception) - } - } - - override fun getRefreshKey(state: PagingState): Int? = null - - companion object { - private const val START_PAGE_INDEX = 1 - } -} \ No newline at end of file diff --git a/feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/list/data/model/GenresResponse.kt b/feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/list/data/model/GenresResponse.kt deleted file mode 100644 index 3533dcdd..00000000 --- a/feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/list/data/model/GenresResponse.kt +++ /dev/null @@ -1,14 +0,0 @@ -package com.codandotv.streamplayerapp.feature_list_streams.list.data.model - -import kotlinx.serialization.Serializable - -@Serializable -data class GenreResponse( - val id: Long, - val name: String -) - -@Serializable -data class GenresResponse( - val genres: List -) \ No newline at end of file diff --git a/feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/list/data/model/ListStreamResponse.kt b/feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/list/data/model/ListStreamResponse.kt deleted file mode 100644 index 89574e2c..00000000 --- a/feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/list/data/model/ListStreamResponse.kt +++ /dev/null @@ -1,14 +0,0 @@ -import kotlinx.serialization.Serializable - -@Serializable -data class StreamResponse( - val id: Int, - val title: String, - val overview: String, - val poster_path: String? = null -) - -@Serializable -data class ListStreamResponse( - val results: List -) diff --git a/feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/list/di/ListStreamModule.kt b/feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/list/di/ListStreamModule.kt deleted file mode 100644 index 6e300eef..00000000 --- a/feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/list/di/ListStreamModule.kt +++ /dev/null @@ -1,21 +0,0 @@ -package com.codandotv.streamplayerapp.feature_list_streams.list.di - -import com.codandotv.streamplayerapp.feature_list_streams.list.data.ListStreamService -import com.codandotv.streamplayerapp.feature_list_streams.list.data.ListStreamServiceImpl -import io.ktor.client.HttpClient -import org.koin.core.annotation.ComponentScan -import org.koin.core.annotation.Factory -import org.koin.core.annotation.Module -import org.koin.core.context.GlobalContext - -@Module -@ComponentScan("com.codandotv.streamplayerapp.feature_list_streams.list") -class ListStreamModule { - - @Factory - fun service(): ListStreamService { - val koin = GlobalContext.get() - val httpClient = koin.get() - return ListStreamServiceImpl(httpClient) - } -} \ No newline at end of file diff --git a/feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/list/domain/GetGenresUseCase.kt b/feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/list/domain/GetGenresUseCase.kt deleted file mode 100644 index 36b2b4d9..00000000 --- a/feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/list/domain/GetGenresUseCase.kt +++ /dev/null @@ -1,19 +0,0 @@ -package com.codandotv.streamplayerapp.feature_list_streams.list.domain - -import com.codandotv.streamplayerapp.feature_list_streams.list.data.ListStreamRepository -import com.codandotv.streamplayerapp.feature_list_streams.list.domain.model.Genre -import kotlinx.coroutines.flow.Flow -import org.koin.core.annotation.Factory - -interface GetGenresUseCase { - suspend operator fun invoke(): Flow> -} - -@Factory -class GetGenresUseCaseImpl( - private val repository: ListStreamRepository -) : GetGenresUseCase { - override suspend fun invoke(): Flow> { - return repository.getGenres() - } -} \ No newline at end of file diff --git a/feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/list/domain/GetLatestMovieUseCase.kt b/feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/list/domain/GetLatestMovieUseCase.kt deleted file mode 100644 index 7257a40c..00000000 --- a/feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/list/domain/GetLatestMovieUseCase.kt +++ /dev/null @@ -1,19 +0,0 @@ -package com.codandotv.streamplayerapp.feature_list_streams.list.domain - -import com.codandotv.streamplayerapp.feature_list_streams.list.data.ListStreamRepository -import com.codandotv.streamplayerapp.feature_list_streams.list.domain.model.Stream -import kotlinx.coroutines.flow.Flow -import org.koin.core.annotation.Factory - -interface GetTopRatedStream { - suspend operator fun invoke(): Flow -} - -@Factory -class GetTopRatedStreamImpl( - private val repository: ListStreamRepository -) : GetTopRatedStream { - override suspend operator fun invoke(): Flow { - return repository.topRatedStream() - } -} \ No newline at end of file diff --git a/feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/list/domain/ListMovieUseCase.kt b/feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/list/domain/ListMovieUseCase.kt deleted file mode 100644 index 367f47be..00000000 --- a/feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/list/domain/ListMovieUseCase.kt +++ /dev/null @@ -1,21 +0,0 @@ -package com.codandotv.streamplayerapp.feature_list_streams.list.domain - -import androidx.paging.PagingData -import com.codandotv.streamplayerapp.feature_list_streams.list.data.ListStreamRepository -import com.codandotv.streamplayerapp.feature_list_streams.list.domain.model.Genre -import com.codandotv.streamplayerapp.feature_list_streams.list.domain.model.Stream -import kotlinx.coroutines.flow.Flow -import org.koin.core.annotation.Factory - -interface ListStreamUseCase { - operator fun invoke(genre: Genre): Flow> -} - -@Factory -class ListStreamUseCaseImpl( - private val repository: ListStreamRepository -) : ListStreamUseCase { - override operator fun invoke(genre: Genre): Flow> { - return repository.loadMovies(genre) - } -} \ No newline at end of file diff --git a/feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/list/domain/ListStreamAnalytics.kt b/feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/list/domain/ListStreamAnalytics.kt deleted file mode 100644 index e599c1fa..00000000 --- a/feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/list/domain/ListStreamAnalytics.kt +++ /dev/null @@ -1,8 +0,0 @@ -package com.codandotv.streamplayerapp.feature_list_streams.list.domain - -import org.koin.core.annotation.Factory - -interface ListStreamAnalytics - -@Factory -class ListStreamAnalyticsImpl : ListStreamAnalytics \ No newline at end of file diff --git a/feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/list/domain/ListStreamMapper.kt b/feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/list/domain/ListStreamMapper.kt deleted file mode 100644 index d3bb2b5c..00000000 --- a/feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/list/domain/ListStreamMapper.kt +++ /dev/null @@ -1,28 +0,0 @@ -package com.codandotv.streamplayerapp.feature_list_streams.list.domain - -import ListStreamResponse -import StreamResponse -import com.codandotv.streamplayerapp.core_networking.Url -import com.codandotv.streamplayerapp.feature_list_streams.list.data.model.GenresResponse -import com.codandotv.streamplayerapp.feature_list_streams.list.domain.model.Genre -import com.codandotv.streamplayerapp.feature_list_streams.list.domain.model.ListStream -import com.codandotv.streamplayerapp.feature_list_streams.list.domain.model.Stream - -fun ListStreamResponse.toListStream(genre: String): ListStream = - ListStream( - categoryName = genre, - streams = this.results.map { streamResponse -> - streamResponse.toStream() - } - ) - -fun GenresResponse.toGenres(): List = this.genres.map { genreResponse -> - Genre(id = genreResponse.id, name = genreResponse.name) -} - -fun StreamResponse.toStream(): Stream = Stream( - description = overview, - name = title, - posterPathUrl = "${Url.IMAGE_URL_SIZE_300}${poster_path}", - id = id.toString() -) \ No newline at end of file diff --git a/feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/list/domain/model/Genre.kt b/feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/list/domain/model/Genre.kt deleted file mode 100644 index aa3c00ee..00000000 --- a/feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/list/domain/model/Genre.kt +++ /dev/null @@ -1,6 +0,0 @@ -package com.codandotv.streamplayerapp.feature_list_streams.list.domain.model - -data class Genre( - val id: Long, - val name: String -) diff --git a/feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/list/domain/model/HighlightBanner.kt b/feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/list/domain/model/HighlightBanner.kt deleted file mode 100644 index 7bdd369d..00000000 --- a/feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/list/domain/model/HighlightBanner.kt +++ /dev/null @@ -1,24 +0,0 @@ -package com.codandotv.streamplayerapp.feature_list_streams.list.domain.model - -import android.os.Parcelable -import androidx.annotation.DrawableRes -import androidx.annotation.StringRes -import kotlinx.parcelize.Parcelize - -@Parcelize -data class HighlightBanner( - val name: String, - val imageUrl: String, - val contentType: Int, - val contentTypeAsPlural: Int, - val extraInfo: IconAndTextInfo, - val leftButton: IconAndTextInfo, - val centralButton: IconAndTextInfo, - val rightButton: IconAndTextInfo -) : Parcelable - -@Parcelize -data class IconAndTextInfo( - @DrawableRes val icon: Int, - @StringRes val text: Int -) : Parcelable \ No newline at end of file diff --git a/feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/list/domain/model/ListStream.kt b/feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/list/domain/model/ListStream.kt deleted file mode 100644 index efebe7e1..00000000 --- a/feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/list/domain/model/ListStream.kt +++ /dev/null @@ -1,12 +0,0 @@ -package com.codandotv.streamplayerapp.feature_list_streams.list.domain.model - -data class Stream( - val id : String, - val name : String, - val description : String, - val posterPathUrl: String, -) -data class ListStream( - val categoryName: String, - val streams: List -) \ No newline at end of file diff --git a/feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/list/presentation/navigation/ListStreamsNavigation.kt b/feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/list/presentation/navigation/ListStreamsNavigation.kt deleted file mode 100644 index ac58e2be..00000000 --- a/feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/list/presentation/navigation/ListStreamsNavigation.kt +++ /dev/null @@ -1,43 +0,0 @@ -package com.codandotv.streamplayerapp.feature_list_streams.list.presentation.navigation - -import androidx.activity.compose.BackHandler -import androidx.lifecycle.Lifecycle -import androidx.navigation.NavGraphBuilder -import androidx.navigation.NavHostController -import androidx.navigation.compose.composable -import com.codandotv.streamplayerapp.core_navigation.routes.BottomNavRoutes.HOME_COMPLETE -import com.codandotv.streamplayerapp.core_navigation.routes.BottomNavRoutes.PARAM.PROFILE_ID -import com.codandotv.streamplayerapp.core_navigation.routes.Routes -import com.codandotv.streamplayerapp.core_navigation.routes.Routes.DETAIL -import com.codandotv.streamplayerapp.core_navigation.routes.Routes.PROFILE_PICKER -import com.codandotv.streamplayerapp.feature_list_streams.list.di.ListStreamModule -import com.codandotv.streamplayerapp.feature_list_streams.list.presentation.screens.ListStreamsScreen -import org.koin.core.context.loadKoinModules -import org.koin.core.context.unloadKoinModules -import org.koin.ksp.generated.module - -internal const val DEFAULT_ID = "" - -fun NavGraphBuilder.listStreamsNavGraph(navController: NavHostController) { - composable(HOME_COMPLETE) { nav -> - BackHandler(true) {} - if (nav.lifecycle.currentState == Lifecycle.State.STARTED) { - loadKoinModules(ListStreamModule().module) - } - ListStreamsScreen(navController = navController, - onNavigateDetailList = { id -> - navController.navigate("${DETAIL}${id}") - }, - onNavigateProfilePicker = { - navController.navigate(PROFILE_PICKER) - }, - onNavigateSearchScreen = { - navController.navigate(Routes.SEARCH) - }, - disposable = { - unloadKoinModules(ListStreamModule().module) - }, - profilePicture = nav.arguments?.getString(PROFILE_ID) ?: DEFAULT_ID - ) - } -} diff --git a/feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/list/presentation/screens/ListStreamViewModel.kt b/feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/list/presentation/screens/ListStreamViewModel.kt deleted file mode 100644 index f470f26f..00000000 --- a/feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/list/presentation/screens/ListStreamViewModel.kt +++ /dev/null @@ -1,130 +0,0 @@ -package com.codandotv.streamplayerapp.feature_list_streams.list.presentation.screens - -import androidx.lifecycle.ViewModel -import androidx.lifecycle.viewModelScope -import androidx.paging.cachedIn -import androidx.paging.map -import com.codandotv.streamplayerapp.core_networking.handleError.catchFailure -import com.codandotv.streamplayerapp.feature.list.streams.R -import com.codandotv.streamplayerapp.feature_list_streams.core.ContentType -import com.codandotv.streamplayerapp.feature_list_streams.list.domain.GetGenresUseCase -import com.codandotv.streamplayerapp.feature_list_streams.list.domain.GetTopRatedStream -import com.codandotv.streamplayerapp.feature_list_streams.list.domain.ListStreamUseCase -import com.codandotv.streamplayerapp.feature_list_streams.list.domain.model.Genre -import com.codandotv.streamplayerapp.feature_list_streams.list.domain.model.HighlightBanner -import com.codandotv.streamplayerapp.feature_list_streams.list.domain.model.IconAndTextInfo -import com.codandotv.streamplayerapp.feature_list_streams.list.domain.model.Stream -import com.codandotv.streamplayerapp.feature_list_streams.list.presentation.widgets.StreamsCardContent -import com.codandotv.streamplayerapp.feature_list_streams.list.presentation.widgets.StreamsCarouselContent -import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.SharingStarted -import kotlinx.coroutines.flow.combine -import kotlinx.coroutines.flow.map -import kotlinx.coroutines.flow.onCompletion -import kotlinx.coroutines.flow.onStart -import kotlinx.coroutines.flow.stateIn -import kotlinx.coroutines.flow.update -import kotlinx.coroutines.launch -import org.koin.android.annotation.KoinViewModel -import com.codandotv.streamplayerapp.core.shared.ui.R as SharedUiR - -@KoinViewModel -class ListStreamViewModel( - private val listStreams: ListStreamUseCase, - private val listGenres: GetGenresUseCase, - private val latestStream: GetTopRatedStream -) : ViewModel() { - - private val _uiState = MutableStateFlow( - ListStreamsUIState( - streamsCarouselContent = emptyList(), - isLoading = false - ) - ) - val uiState = _uiState.stateIn( - viewModelScope, - SharingStarted.Eagerly, - initialValue = _uiState.value - ) - - init { - viewModelScope.launch { - latestStream() - .combine( - listGenres() - ) { latest, genres -> - Pair(latest, genres) - } - .onStart { - onLoading() - } - .onCompletion { loaded() } - .catchFailure { - println(">>>> ${it.errorMessage}") - } - .collect { pair -> - val (latest, genres) = pair - - _uiState.update { - it.copy( - streamsCarouselContent = genres.map { genreTarget -> - getStreamsByGenre(genreTarget) - }, - highlightBanner = getHighlightBanner(latest) - ) - } - } - } - } - - private fun getHighlightBanner(latest: Stream) = - HighlightBanner( - name = latest.name, - imageUrl = latest.posterPathUrl, - contentType = ContentType.getContentName(ContentType.FILM), - contentTypeAsPlural = ContentType.getContentNameAsPlural(ContentType.FILM), - extraInfo = IconAndTextInfo( - R.drawable.ic_top_10, - R.string.list_highlight_banner_stream_ranking - ), - leftButton = IconAndTextInfo( - SharedUiR.drawable.ic_add, - R.string.list_highlight_banner_add - ), - centralButton = IconAndTextInfo( - SharedUiR.drawable.ic_play, - R.string.list_highlight_banner_watch - ), - rightButton = IconAndTextInfo( - SharedUiR.drawable.ic_info, - R.string.list_highlight_banner_info - ), - ) - - private fun getStreamsByGenre(genre: Genre): StreamsCarouselContent { - return StreamsCarouselContent( - genre.name, - listStreams(genre).map { - it.map { stream -> - StreamsCardContent( - contentDescription = stream.name, - url = stream.posterPathUrl, - id = stream.id - ) - } - }.cachedIn(viewModelScope) - ) - } - - private fun loaded() { - this._uiState.update { - it.copy(isLoading = false) - } - } - - private fun onLoading() { - this._uiState.update { - it.copy(isLoading = true) - } - } -} \ No newline at end of file diff --git a/feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/list/presentation/screens/ListStreamsScreen.kt b/feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/list/presentation/screens/ListStreamsScreen.kt deleted file mode 100644 index 345d078b..00000000 --- a/feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/list/presentation/screens/ListStreamsScreen.kt +++ /dev/null @@ -1,115 +0,0 @@ -package com.codandotv.streamplayerapp.feature_list_streams.list.presentation.screens - -import android.annotation.SuppressLint -import androidx.compose.foundation.background -import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.Spacer -import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.foundation.layout.height -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.rememberScrollState -import androidx.compose.foundation.verticalScroll -import androidx.compose.material3.CircularProgressIndicator -import androidx.compose.material3.ExperimentalMaterial3Api -import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.Scaffold -import androidx.compose.material3.TopAppBarDefaults -import androidx.compose.material3.rememberTopAppBarState -import androidx.compose.runtime.Composable -import androidx.compose.runtime.DisposableEffect -import androidx.compose.runtime.getValue -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.input.nestedscroll.nestedScroll -import androidx.compose.ui.platform.LocalLifecycleOwner -import androidx.compose.ui.unit.dp -import androidx.lifecycle.compose.collectAsStateWithLifecycle -import androidx.navigation.NavController -import androidx.navigation.compose.rememberNavController -import com.codandotv.streamplayerapp.core_navigation.bottomnavigation.StreamPlayerBottomNavigation -import com.codandotv.streamplayerapp.core_shared_ui.theme.ThemePreviews -import com.codandotv.streamplayerapp.core_shared_ui.widget.StreamPlayerTopBar -import com.codandotv.streamplayerapp.feature_list_streams.list.presentation.widgets.HighlightBanner -import com.codandotv.streamplayerapp.feature_list_streams.list.presentation.widgets.StreamsCarousel -import org.koin.androidx.compose.koinViewModel - -@Suppress("LongParameterList") -@SuppressLint("UnusedMaterial3ScaffoldPaddingParameter") -@OptIn(ExperimentalMaterial3Api::class) -@Composable -fun ListStreamsScreen( - viewModel: ListStreamViewModel = koinViewModel(), - navController: NavController, - onNavigateDetailList: (String) -> Unit = {}, - onNavigateProfilePicker: () -> Unit = {}, - onNavigateSearchScreen: () -> Unit = {}, - disposable: () -> Unit = {}, - profilePicture: String -) { - val uiState by viewModel.uiState.collectAsStateWithLifecycle() - val lifecycleOwner = LocalLifecycleOwner.current - val scrollBehavior = - TopAppBarDefaults.exitUntilCollapsedScrollBehavior(rememberTopAppBarState()) - val baseScrollState = rememberScrollState() - - DisposableEffect(lifecycleOwner) { - onDispose { - disposable.invoke() - } - } - - Scaffold( - modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection), - topBar = { - StreamPlayerTopBar( - scrollBehavior = scrollBehavior, - onNavigateProfilePicker = onNavigateProfilePicker, - onNavigateSearchScreen = onNavigateSearchScreen, - onSelectedProfilePicture = profilePicture - ) - }, - bottomBar = { - StreamPlayerBottomNavigation(navController = navController) - } - ) { paddingValues -> - Box( - modifier = Modifier - .padding(paddingValues) - .fillMaxSize() - .background(MaterialTheme.colorScheme.background) - ) { - if (uiState.isLoading) { - CircularProgressIndicator( - modifier = Modifier.align( - Alignment.Center - ) - ) - } else { - Column( - modifier = Modifier - .fillMaxSize() - .align(Alignment.TopCenter) - .verticalScroll(baseScrollState) - ) { - - HighlightBanner(data = uiState.highlightBanner) - - uiState.streamsCarouselContent.forEach { streamCarouselContent -> - StreamsCarousel( - content = streamCarouselContent, - onNavigateDetailList = onNavigateDetailList, - ) - Spacer(modifier = Modifier.height(12.dp)) - } - } - } - } - } -} - -@ThemePreviews -@Composable -fun ListStreamsScreenPreview() { - ListStreamsScreen(navController = rememberNavController(), profilePicture = "") -} diff --git a/feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/list/presentation/screens/ListStreamsUIState.kt b/feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/list/presentation/screens/ListStreamsUIState.kt deleted file mode 100644 index fba66712..00000000 --- a/feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/list/presentation/screens/ListStreamsUIState.kt +++ /dev/null @@ -1,10 +0,0 @@ -package com.codandotv.streamplayerapp.feature_list_streams.list.presentation.screens - -import com.codandotv.streamplayerapp.feature_list_streams.list.domain.model.HighlightBanner -import com.codandotv.streamplayerapp.feature_list_streams.list.presentation.widgets.StreamsCarouselContent - -data class ListStreamsUIState( - val highlightBanner: HighlightBanner? = null, - val streamsCarouselContent: List, - val isLoading: Boolean -) \ No newline at end of file diff --git a/feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/list/presentation/widgets/HighlightBanner.kt b/feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/list/presentation/widgets/HighlightBanner.kt deleted file mode 100644 index bbeb30ec..00000000 --- a/feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/list/presentation/widgets/HighlightBanner.kt +++ /dev/null @@ -1,280 +0,0 @@ -package com.codandotv.streamplayerapp.feature_list_streams.list.presentation.widgets - -import androidx.annotation.StringRes -import androidx.compose.foundation.background -import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.PaddingValues -import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.Spacer -import androidx.compose.foundation.layout.defaultMinSize -import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.height -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.size -import androidx.compose.foundation.layout.width -import androidx.compose.foundation.shape.RoundedCornerShape -import androidx.compose.material3.Button -import androidx.compose.material3.ButtonDefaults -import androidx.compose.material3.Icon -import androidx.compose.material3.IconButton -import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.Text -import androidx.compose.runtime.Composable -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.Brush -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.layout.ContentScale -import androidx.compose.ui.res.painterResource -import androidx.compose.ui.res.stringResource -import androidx.compose.ui.text.TextStyle -import androidx.compose.ui.text.font.FontWeight -import androidx.compose.ui.text.style.TextAlign -import androidx.compose.ui.unit.dp -import androidx.compose.ui.unit.sp -import coil.compose.AsyncImage -import com.codandotv.streamplayerapp.core_shared_ui.theme.ThemePreviews -import com.codandotv.streamplayerapp.feature.list.streams.R -import com.codandotv.streamplayerapp.feature_list_streams.core.ContentType -import com.codandotv.streamplayerapp.feature_list_streams.list.domain.model.HighlightBanner -import com.codandotv.streamplayerapp.feature_list_streams.list.domain.model.IconAndTextInfo -import com.codandotv.streamplayerapp.core.shared.ui.R as RSharedUI - -@Suppress("MagicNumber") -@Composable -fun HighlightBanner(modifier: Modifier = Modifier, data: HighlightBanner?) { - data ?: return - - Box( - modifier = modifier - .fillMaxWidth() - .height(500.dp) - ) { - ContentImage(imageUrl = data.imageUrl) - BackgroundGradient() - Column( - modifier = Modifier - .align(Alignment.BottomEnd) - .padding(bottom = 16.dp), - horizontalAlignment = Alignment.CenterHorizontally - ) { - Spacer(modifier = Modifier.weight(1F)) - ContentType(contentType = data.contentType) - ContentName(name = data.name) - ContentRanking( - extraInfo = data.extraInfo, - contentTypeAsPlural = data.contentTypeAsPlural - ) - ActionButtons(Modifier.weight(0.3F), data) - } - } -} - -@Composable -fun ContentImage(modifier: Modifier = Modifier, imageUrl: String) { - AsyncImage( - modifier = modifier.fillMaxSize(), - model = imageUrl, - contentScale = ContentScale.Crop, - contentDescription = stringResource(id = R.string.list_highligh_banner_content) - ) -} - -@Composable -fun BackgroundGradient(modifier: Modifier = Modifier) { - Box( - modifier = modifier - .fillMaxSize() - .background( - brush = Brush.verticalGradient( - colors = listOf( - Color.Transparent, - Color.Black - ), - ) - ) - ) -} - -@Composable -fun ContentName(modifier: Modifier = Modifier, name: String) { - Text( - text = name, - fontSize = 24.sp, - modifier = modifier - .padding(horizontal = 50.dp) - .padding(vertical = 4.dp), - textAlign = TextAlign.Center, - color = MaterialTheme.colorScheme.onBackground - ) -} - -@Composable -fun ContentRanking( - modifier: Modifier = Modifier, - extraInfo: IconAndTextInfo, - @StringRes contentTypeAsPlural: Int -) { - Row(modifier = modifier, verticalAlignment = Alignment.CenterVertically) { - Icon( - painter = painterResource(id = extraInfo.icon), - contentDescription = stringResource(id = R.string.list_icon_highligh_banner_ranking), - modifier = Modifier.size(24.dp), - tint = Color.Unspecified - ) - Text( - text = stringResource( - id = extraInfo.text, - stringResource(contentTypeAsPlural).lowercase() - ), - Modifier.padding(start = 4.dp), - style = TextStyle(fontWeight = FontWeight.Bold), - fontSize = 14.sp, - color = MaterialTheme.colorScheme.onBackground - ) - } -} - -@Composable -fun ContentType(modifier: Modifier = Modifier, @StringRes contentType: Int) { - Row(modifier = modifier, verticalAlignment = Alignment.CenterVertically) { - Icon( - painter = painterResource(id = RSharedUI.drawable.ic_netflix), - contentDescription = stringResource(id = RSharedUI.string.icon_netflix), - modifier = Modifier.size(16.dp), - tint = Color.Unspecified - ) - Text( - text = stringResource(contentType).uppercase(), - Modifier.padding(start = 4.dp), - fontSize = 12.sp, - color = MaterialTheme.colorScheme.onBackground, - letterSpacing = 4.sp - ) - } - -} - -@Composable -fun ActionButtons(modifier: Modifier, data: HighlightBanner) { - Row(verticalAlignment = Alignment.CenterVertically, modifier = modifier) { - AddToListButton( - modifier = Modifier - .weight(1F) - .fillMaxSize(), data - ) { - /* todo */ - } - PlayButton { - /* todo */ - } - InfoButton( - modifier = Modifier - .weight(1F) - .fillMaxSize(), data - ) { - /* todo */ - } - } -} - -@Composable -fun AddToListButton( - modifier: Modifier = Modifier, - data: HighlightBanner, - onClick: () -> Unit -) { - IconButton(onClick = { onClick.invoke() }, modifier = modifier) { - Column(horizontalAlignment = Alignment.CenterHorizontally) { - Icon( - painter = painterResource(id = RSharedUI.drawable.ic_add), - contentDescription = stringResource(id = R.string.list_icon_add), - tint = Color.White, - ) - Text( - fontSize = 10.sp, - text = stringResource(id = data.leftButton.text), - color = Color.White, - ) - } - } -} - -@Composable -fun InfoButton( - modifier: Modifier = Modifier, - data: HighlightBanner, - onClick: () -> Unit -) { - IconButton(onClick = { onClick.invoke() }, modifier = modifier) { - Column(horizontalAlignment = Alignment.CenterHorizontally) { - Icon( - painter = painterResource(id = RSharedUI.drawable.ic_info), - contentDescription = stringResource(id = R.string.list_icon_info), - tint = Color.White - ) - Text( - text = stringResource(id = data.rightButton.text), - fontSize = 10.sp, - color = Color.White - ) - } - } -} - -@Composable -fun PlayButton( - modifier: Modifier = Modifier, - onClick: () -> Unit -) { - Button( - onClick = { onClick.invoke() }, - colors = ButtonDefaults.buttonColors(containerColor = Color.White), - shape = RoundedCornerShape(4.dp), - contentPadding = PaddingValues(horizontal = 8.dp), - modifier = modifier - .padding(16.dp) - .defaultMinSize( - minWidth = 28.dp, - minHeight = 28.dp - ) - ) { - Icon( - painter = painterResource(RSharedUI.drawable.ic_play), - contentDescription = stringResource(id = R.string.list_icon_play), - tint = Color.Black, - modifier = Modifier.padding(vertical = 8.dp) - ) - Spacer(modifier = Modifier.width(4.dp)) - Text( - text = stringResource(id = R.string.list_highlight_banner_watch), - color = Color.Black, - fontSize = 14.sp, - modifier = Modifier - .padding(bottom = 2.dp) - .padding(end = 8.dp) - ) - } -} - -@ThemePreviews -@Composable -fun HighlightBannerPreview() { - HighlightBanner( - data = HighlightBanner( - name = stringResource(id = RSharedUI.string.app_name), - imageUrl = String(), - contentType = ContentType.getContentName(ContentType.SHOW), - contentTypeAsPlural = ContentType.getContentNameAsPlural(ContentType.SHOW), - extraInfo = IconAndTextInfo( - R.drawable.ic_top_10, - ContentType.getContentName(ContentType.SHOW) - ), - leftButton = IconAndTextInfo(RSharedUI.drawable.ic_add, R.string.list_highlight_banner_add), - centralButton = IconAndTextInfo(RSharedUI.drawable.ic_play, R.string.list_highlight_banner_watch), - rightButton = IconAndTextInfo(RSharedUI.drawable.ic_info, R.string.list_highlight_banner_info), - ) - ) -} diff --git a/feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/list/presentation/widgets/StreamsCard.kt b/feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/list/presentation/widgets/StreamsCard.kt deleted file mode 100644 index e58661be..00000000 --- a/feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/list/presentation/widgets/StreamsCard.kt +++ /dev/null @@ -1,64 +0,0 @@ -package com.codandotv.streamplayerapp.feature_list_streams.list.presentation.widgets - -import android.os.Parcelable -import androidx.compose.foundation.clickable -import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.size -import androidx.compose.foundation.shape.RoundedCornerShape -import androidx.compose.material3.Card -import androidx.compose.runtime.Composable -import androidx.compose.ui.Modifier -import androidx.compose.ui.layout.ContentScale -import androidx.compose.ui.tooling.preview.Preview -import androidx.compose.ui.unit.dp -import coil.compose.AsyncImage -import com.codandotv.streamplayerapp.core_networking.Url.IMAGE_URL_SIZE_300 -import kotlinx.parcelize.Parcelize - -@Composable -fun StreamsCard( - content: StreamsCardContent, - onNavigateDetailList: (String) -> Unit = {}, -) { - Card( - shape = RoundedCornerShape(6.dp), - modifier = Modifier - .size( - width = 100.dp, - height = 140.dp - ) - .padding( - horizontal = 4.dp - ) - .clickable { - onNavigateDetailList.invoke(content.id) - } - ) { - AsyncImage( - model = content.url, - modifier = Modifier.fillMaxSize(), - contentScale = ContentScale.FillBounds, - contentDescription = content.contentDescription - ) - } -} - -@Parcelize -data class StreamsCardContent( - val id: String, - val url: String, - val contentDescription: String, -) : Parcelable - -@Preview -@Composable -fun StreamsCardPreview() { - StreamsCard( - StreamsCardContent( - url = "${IMAGE_URL_SIZE_300}evgwd37VHBJhXvSr88Mrx5riFil.jpg", - contentDescription = "Test 1", - id = "", - ) - ) -} \ No newline at end of file diff --git a/feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/list/presentation/widgets/StreamsCarousel.kt b/feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/list/presentation/widgets/StreamsCarousel.kt deleted file mode 100644 index 61b5e89f..00000000 --- a/feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/list/presentation/widgets/StreamsCarousel.kt +++ /dev/null @@ -1,82 +0,0 @@ -package com.codandotv.streamplayerapp.feature_list_streams.list.presentation.widgets - -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.Spacer -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.height -import androidx.compose.foundation.layout.size -import androidx.compose.foundation.lazy.LazyRow -import androidx.compose.foundation.lazy.rememberLazyListState -import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.Text -import androidx.compose.runtime.Composable -import androidx.compose.ui.Modifier -import androidx.compose.ui.text.font.FontWeight -import androidx.compose.ui.tooling.preview.Preview -import androidx.compose.ui.unit.dp -import androidx.compose.ui.unit.sp -import androidx.paging.PagingData -import androidx.paging.compose.collectAsLazyPagingItems -import androidx.paging.compose.itemContentType -import androidx.paging.compose.itemKey -import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.emptyFlow - -@Composable -fun StreamsCarousel( - content: StreamsCarouselContent, - modifier: Modifier = Modifier, - onNavigateDetailList: (String) -> Unit = {}, -) { - val lazyPagingItems = content.contentList.collectAsLazyPagingItems() - val lazyListState = rememberLazyListState() - - Column(modifier = modifier) { - Text( - content.genreTitle, - style = MaterialTheme.typography.headlineMedium.copy( - fontWeight = FontWeight.Bold, - fontSize = 20.sp - ) - ) - - Spacer(modifier = Modifier.size(8.dp)) - - LazyRow( - state = lazyListState, - modifier = Modifier - .fillMaxWidth() - .height(140.dp) - ) { - items( - count = lazyPagingItems.itemCount, - key = lazyPagingItems.itemKey(), - contentType = lazyPagingItems.itemContentType() - ) { index -> - val item = lazyPagingItems[index] - item?.let { - StreamsCard( - content = it, - onNavigateDetailList - ) - } - } - } - } -} - -data class StreamsCarouselContent( - val genreTitle: String, - val contentList: Flow> -) - -@Composable -@Preview -fun StreamsCarouselPreview() { - StreamsCarousel( - content = StreamsCarouselContent( - genreTitle = "Ação", - contentList = emptyFlow() - ) - ) -} \ No newline at end of file diff --git a/feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/search/data/api/MostPopularMoviesService.kt b/feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/search/data/api/MostPopularMoviesService.kt deleted file mode 100644 index ea3f8fed..00000000 --- a/feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/search/data/api/MostPopularMoviesService.kt +++ /dev/null @@ -1,22 +0,0 @@ -package com.codandotv.streamplayerapp.feature_list_streams.search.data.api - -import com.codandotv.streamplayerapp.core_networking.handleError.NetworkResponse -import com.codandotv.streamplayerapp.core_networking.handleError.safeRequest -import com.codandotv.streamplayerapp.feature_list_streams.search.data.model.ListSearchStreamResponse -import io.ktor.client.HttpClient -import io.ktor.client.call.body -import io.ktor.client.request.get -import io.ktor.client.request.url - -interface MostPopularMoviesService { - suspend fun getPopular(): NetworkResponse -} - -class MostPopularMoviesServiceImpl( - private val client: HttpClient -) : MostPopularMoviesService { - override suspend fun getPopular(): NetworkResponse = - client.safeRequest { - url("movie/popular") - } -} diff --git a/feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/search/data/api/SearchStreamService.kt b/feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/search/data/api/SearchStreamService.kt deleted file mode 100644 index 8750ae50..00000000 --- a/feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/search/data/api/SearchStreamService.kt +++ /dev/null @@ -1,22 +0,0 @@ -package com.codandotv.streamplayerapp.feature_list_streams.search.data.api - -import com.codandotv.streamplayerapp.core_networking.handleError.NetworkResponse -import com.codandotv.streamplayerapp.core_networking.handleError.safeRequest -import com.codandotv.streamplayerapp.feature_list_streams.search.data.model.ListSearchStreamResponse -import io.ktor.client.HttpClient -import io.ktor.client.request.parameter -import io.ktor.client.request.url - -interface SearchStreamService { - suspend fun getSearch(query: String): NetworkResponse -} - -class SearchStreamServiceImpl( - private val client: HttpClient -) : SearchStreamService { - override suspend fun getSearch(query: String): NetworkResponse = - client.safeRequest { - url("search/movie") - parameter("query", query) - } -} diff --git a/feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/search/data/datasource/MostPopularMoviesDataSource.kt b/feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/search/data/datasource/MostPopularMoviesDataSource.kt deleted file mode 100644 index 82f01044..00000000 --- a/feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/search/data/datasource/MostPopularMoviesDataSource.kt +++ /dev/null @@ -1,18 +0,0 @@ -package com.codandotv.streamplayerapp.feature_list_streams.search.data.datasource - -import com.codandotv.streamplayerapp.core_networking.handleError.toFlow -import com.codandotv.streamplayerapp.feature_list_streams.search.data.model.ListSearchStreamResponse -import com.codandotv.streamplayerapp.feature_list_streams.search.data.api.MostPopularMoviesService -import kotlinx.coroutines.flow.Flow - -interface MostPopularMoviesDataSource { - suspend fun getMostPopularMovies(): Flow -} - -class MostPopularMoviesDataSourceImpl( - private val service: MostPopularMoviesService -) : MostPopularMoviesDataSource { - - override suspend fun getMostPopularMovies(): Flow = - service.getPopular().toFlow() -} diff --git a/feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/search/data/datasource/SearchStreamDataSource.kt b/feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/search/data/datasource/SearchStreamDataSource.kt deleted file mode 100644 index 77f25ce3..00000000 --- a/feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/search/data/datasource/SearchStreamDataSource.kt +++ /dev/null @@ -1,17 +0,0 @@ -package com.codandotv.streamplayerapp.feature_list_streams.search.data.datasource - -import com.codandotv.streamplayerapp.core_networking.handleError.toFlow -import com.codandotv.streamplayerapp.feature_list_streams.search.data.model.ListSearchStreamResponse -import com.codandotv.streamplayerapp.feature_list_streams.search.data.api.SearchStreamService -import kotlinx.coroutines.flow.Flow - -interface SearchStreamDataSource { - suspend fun getMovieSearch(query: String): Flow -} -class SearchStreamDataSourceImpl( - private val service: SearchStreamService -): SearchStreamDataSource { - - override suspend fun getMovieSearch(query:String): Flow = - service.getSearch(query = query).toFlow() -} diff --git a/feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/search/data/model/ListSearchStreamResponse.kt b/feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/search/data/model/ListSearchStreamResponse.kt deleted file mode 100644 index c7b23ca4..00000000 --- a/feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/search/data/model/ListSearchStreamResponse.kt +++ /dev/null @@ -1,22 +0,0 @@ -package com.codandotv.streamplayerapp.feature_list_streams.search.data.model - -import com.squareup.moshi.Json -import kotlinx.serialization.Serializable - -@Serializable -data class ListSearchStreamResponse( - @Json(name = "results") - val results: List -) { - @Serializable - data class SearchStreamResponse( - @Json(name = "id") - val id: Int, - @Json(name = "title") - val title: String, - @Json(name="overview") - val overview: String, - @Json(name = "poster_path") - val posterPath: String? = null - ) -} diff --git a/feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/search/data/repository/MostPopularMoviesRepository.kt b/feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/search/data/repository/MostPopularMoviesRepository.kt deleted file mode 100644 index cbf9ddb3..00000000 --- a/feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/search/data/repository/MostPopularMoviesRepository.kt +++ /dev/null @@ -1,16 +0,0 @@ -package com.codandotv.streamplayerapp.feature_list_streams.search.data.repository - -import com.codandotv.streamplayerapp.feature_list_streams.search.data.model.ListSearchStreamResponse -import com.codandotv.streamplayerapp.feature_list_streams.search.data.datasource.MostPopularMoviesDataSource -import kotlinx.coroutines.flow.Flow - -interface MostPopularMoviesRepository { - suspend fun getMostPopularMovies(): Flow -} - -class MostPopularMoviesRepositoryImpl( - private val dataSource: MostPopularMoviesDataSource -) : MostPopularMoviesRepository { - override suspend fun getMostPopularMovies(): Flow = - dataSource.getMostPopularMovies() -} diff --git a/feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/search/data/repository/SearchStreamRepository.kt b/feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/search/data/repository/SearchStreamRepository.kt deleted file mode 100644 index d336d2ba..00000000 --- a/feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/search/data/repository/SearchStreamRepository.kt +++ /dev/null @@ -1,16 +0,0 @@ -package com.codandotv.streamplayerapp.feature_list_streams.search.data.repository - -import com.codandotv.streamplayerapp.feature_list_streams.search.data.datasource.SearchStreamDataSource -import com.codandotv.streamplayerapp.feature_list_streams.search.data.model.ListSearchStreamResponse -import kotlinx.coroutines.flow.Flow - -interface SearchStreamRepository { - suspend fun getMovieSearch(query: String) : Flow - -} -class SearchStreamRepositoryImp( - private val dataSource: SearchStreamDataSource -) : SearchStreamRepository { - override suspend fun getMovieSearch(query: String): Flow = - dataSource.getMovieSearch(query) -} diff --git a/feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/search/di/SearchModule.kt b/feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/search/di/SearchModule.kt deleted file mode 100644 index b9426f38..00000000 --- a/feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/search/di/SearchModule.kt +++ /dev/null @@ -1,43 +0,0 @@ -package com.codandotv.streamplayerapp.feature_list_streams.search.di - -import com.codandotv.streamplayerapp.feature_list_streams.search.data.api.SearchStreamService -import com.codandotv.streamplayerapp.feature_list_streams.search.data.datasource.SearchStreamDataSourceImpl -import com.codandotv.streamplayerapp.feature_list_streams.search.data.datasource.MostPopularMoviesDataSource -import com.codandotv.streamplayerapp.feature_list_streams.search.data.datasource.MostPopularMoviesDataSourceImpl -import com.codandotv.streamplayerapp.feature_list_streams.search.data.repository.MostPopularMoviesRepository -import com.codandotv.streamplayerapp.feature_list_streams.search.data.repository.MostPopularMoviesRepositoryImpl -import com.codandotv.streamplayerapp.feature_list_streams.search.data.api.MostPopularMoviesService -import com.codandotv.streamplayerapp.feature_list_streams.search.data.api.MostPopularMoviesServiceImpl -import com.codandotv.streamplayerapp.feature_list_streams.search.data.api.SearchStreamServiceImpl -import com.codandotv.streamplayerapp.feature_list_streams.search.data.datasource.SearchStreamDataSource -import com.codandotv.streamplayerapp.feature_list_streams.search.data.repository.SearchStreamRepository -import com.codandotv.streamplayerapp.feature_list_streams.search.data.repository.SearchStreamRepositoryImp -import com.codandotv.streamplayerapp.feature_list_streams.search.domain.MostPopularMoviesUseCase -import com.codandotv.streamplayerapp.feature_list_streams.search.domain.MostPopularMoviesUseCaseImpl -import com.codandotv.streamplayerapp.feature_list_streams.search.domain.SearchUseCase -import com.codandotv.streamplayerapp.feature_list_streams.search.domain.SearchUseCaseImpl -import com.codandotv.streamplayerapp.feature_list_streams.search.presentation.screens.SearchViewModel -import org.koin.androidx.viewmodel.dsl.viewModel -import org.koin.dsl.module - -object SearchModule { - val module = module { - viewModel { - SearchViewModel( - searchUseCase = get(), - mostPopularMoviesUseCase = get() - ) - } - - factory { SearchStreamServiceImpl(get()) } - factory { MostPopularMoviesServiceImpl(get()) } - - factory { MostPopularMoviesUseCaseImpl(repository = get()) } - factory { MostPopularMoviesDataSourceImpl(service = get()) } - factory { MostPopularMoviesRepositoryImpl(dataSource = get()) } - - factory { SearchUseCaseImpl(repository = get()) } - factory { SearchStreamDataSourceImpl(service = get()) } - factory { SearchStreamRepositoryImp(dataSource = get()) } - } -} diff --git a/feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/search/domain/MostPopularMoviesUseCase.kt b/feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/search/domain/MostPopularMoviesUseCase.kt deleted file mode 100644 index 1ba461fc..00000000 --- a/feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/search/domain/MostPopularMoviesUseCase.kt +++ /dev/null @@ -1,17 +0,0 @@ -package com.codandotv.streamplayerapp.feature_list_streams.search.domain - -import com.codandotv.streamplayerapp.feature_list_streams.search.data.model.ListSearchStreamResponse -import com.codandotv.streamplayerapp.feature_list_streams.search.data.repository.MostPopularMoviesRepository -import kotlinx.coroutines.flow.Flow - -interface MostPopularMoviesUseCase { - suspend operator fun invoke(): Flow -} - -class MostPopularMoviesUseCaseImpl( - val repository: MostPopularMoviesRepository -) : MostPopularMoviesUseCase { - override suspend operator fun invoke(): Flow { - return repository.getMostPopularMovies() - } -} diff --git a/feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/search/domain/SearchUseCase.kt b/feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/search/domain/SearchUseCase.kt deleted file mode 100644 index 90e2e3b9..00000000 --- a/feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/search/domain/SearchUseCase.kt +++ /dev/null @@ -1,15 +0,0 @@ -package com.codandotv.streamplayerapp.feature_list_streams.search.domain - -import com.codandotv.streamplayerapp.feature_list_streams.search.data.model.ListSearchStreamResponse -import com.codandotv.streamplayerapp.feature_list_streams.search.data.repository.SearchStreamRepository -import kotlinx.coroutines.flow.Flow - -interface SearchUseCase { - suspend operator fun invoke(query:String): Flow -} - -class SearchUseCaseImpl(val repository: SearchStreamRepository) : SearchUseCase { - override suspend operator fun invoke(query:String): Flow { - return repository.getMovieSearch(query = query) - } -} diff --git a/feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/search/domain/mapper/SearchMapper.kt b/feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/search/domain/mapper/SearchMapper.kt deleted file mode 100644 index f4586a63..00000000 --- a/feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/search/domain/mapper/SearchMapper.kt +++ /dev/null @@ -1,11 +0,0 @@ -package com.codandotv.streamplayerapp.feature_list_streams.search.domain.mapper - -import com.codandotv.streamplayerapp.core_networking.Url -import com.codandotv.streamplayerapp.feature_list_streams.search.data.model.ListSearchStreamResponse.SearchStreamResponse -import com.codandotv.streamplayerapp.feature_list_streams.search.presentation.widgets.SearchStreamCardModel - -fun SearchStreamResponse.toSearchStreamCardModel() = SearchStreamCardModel( - id = id.toString(), - title = title, - url = "${Url.IMAGE_URL_SIZE_200}${posterPath}" -) diff --git a/feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/search/presentation/navigation/SearchStreamNavigation.kt b/feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/search/presentation/navigation/SearchStreamNavigation.kt deleted file mode 100644 index adcec747..00000000 --- a/feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/search/presentation/navigation/SearchStreamNavigation.kt +++ /dev/null @@ -1,30 +0,0 @@ -package com.codandotv.streamplayerapp.feature_list_streams.search.presentation.navigation - -import androidx.activity.compose.BackHandler -import androidx.lifecycle.Lifecycle -import androidx.navigation.NavGraphBuilder -import androidx.navigation.NavHostController -import androidx.navigation.compose.composable -import com.codandotv.streamplayerapp.core_navigation.routes.Routes -import com.codandotv.streamplayerapp.feature_list_streams.search.di.SearchModule -import com.codandotv.streamplayerapp.feature_list_streams.search.presentation.screens.SearchScreen -import org.koin.core.context.loadKoinModules -import org.koin.core.context.unloadKoinModules - -fun NavGraphBuilder.searchStreamsNavGraph(navController: NavHostController) { - composable(Routes.SEARCH) { nav -> - BackHandler(true) {} - if (nav.lifecycle.currentState == Lifecycle.State.STARTED) { - loadKoinModules(SearchModule.module) - } - SearchScreen( - navController = navController, - onNavigateDetailList = { id -> - navController.navigate("${Routes.DETAIL}${id}") - }, - disposable = { - unloadKoinModules(SearchModule.module) - } - ) - } -} diff --git a/feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/search/presentation/screens/SearchScreen.kt b/feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/search/presentation/screens/SearchScreen.kt deleted file mode 100644 index f3f68d21..00000000 --- a/feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/search/presentation/screens/SearchScreen.kt +++ /dev/null @@ -1,173 +0,0 @@ -package com.codandotv.streamplayerapp.feature_list_streams.search.presentation.screens - -import androidx.activity.compose.BackHandler -import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.rememberScrollState -import androidx.compose.foundation.verticalScroll -import androidx.compose.material3.CircularProgressIndicator -import androidx.compose.material3.ExperimentalMaterial3Api -import androidx.compose.material3.Scaffold -import androidx.compose.material3.Text -import androidx.compose.runtime.Composable -import androidx.compose.runtime.DisposableEffect -import androidx.compose.runtime.collectAsState -import androidx.compose.runtime.getValue -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.platform.LocalLifecycleOwner -import androidx.compose.ui.res.stringResource -import androidx.compose.ui.text.font.FontWeight -import androidx.compose.ui.unit.dp -import androidx.compose.ui.unit.sp -import androidx.lifecycle.LifecycleOwner -import androidx.lifecycle.compose.collectAsStateWithLifecycle -import androidx.navigation.NavController -import com.codandotv.streamplayerapp.core_navigation.extensions.goBack -import com.codandotv.streamplayerapp.feature.list.streams.R -import com.codandotv.streamplayerapp.feature_list_streams.search.domain.mapper.toSearchStreamCardModel -import com.codandotv.streamplayerapp.feature_list_streams.search.presentation.widgets.SearchStreamCard -import com.codandotv.streamplayerapp.feature_list_streams.search.presentation.widgets.SearchableTopBar -import com.codandotv.streamplayerapp.feature_list_streams.search.presentation.widgets.StreamsEmpty -import com.codandotv.streamplayerapp.feature_list_streams.search.presentation.widgets.StreamsError -import org.koin.androidx.compose.koinViewModel - -@Composable -fun SearchScreen( - viewModel: SearchViewModel = koinViewModel(), - onNavigateDetailList: (String) -> Unit = {}, - navController: NavController, - disposable: () -> Unit = {} -) { - - val uiState by viewModel.uiState.collectAsStateWithLifecycle() - Lifecycle( - lifecycleOwner = LocalLifecycleOwner.current, - viewModel = viewModel, - disposable = disposable - ) - - when (uiState) { - is SearchUIState.Success -> { - SetupSearchScreen( - navController = navController, - uiState = uiState as SearchUIState.Success, - viewModel = viewModel, - onNavigateDetailList = onNavigateDetailList - ) - } - - is SearchUIState.Error -> { - StreamsError( - onRetry = { viewModel.onTryAgain() }, - onCloseButton = { navController.goBack() } - ) - } - - is SearchUIState.Empty -> { - SetupSearchScreen( - navController = navController, - uiState = uiState as SearchUIState.Empty, - viewModel = viewModel, - onNavigateDetailList = onNavigateDetailList - ) - } - - else -> { - Box(Modifier.fillMaxSize()) { - CircularProgressIndicator( - modifier = Modifier.align( - Alignment.Center - ) - ) - } - } - } -} - -@OptIn(ExperimentalMaterial3Api::class) -@Composable -private fun SetupSearchScreen( - onNavigateDetailList: (String) -> Unit = {}, - navController: NavController, - uiState: SearchUIState, - viewModel: SearchViewModel -) { - Scaffold( - topBar = { - val currentText by viewModel.currentSearchText.collectAsState() - SearchableTopBar( - currentSearchText = currentText, - onSearchTextChanged = { value -> - viewModel.setCurrentSearchText( - newText = value - ) - }, - onSearchDispatched = { - viewModel.fetchMovies() - }, - onSearchIconPressed = { - viewModel.fetchMovies() - }, - onBackPressed = { - navController.goBack() - }, - onCleanTextPressed = { - viewModel.onCleanText() - } - ) - } - ) { paddingValues -> - - if (uiState is SearchUIState.Success) { - Column( - modifier = Modifier - .padding(paddingValues) - .verticalScroll(rememberScrollState()), - ) { - Text( - text = stringResource(id = R.string.search_list_describle), - color = Color.White, - fontWeight = FontWeight.SemiBold, - fontSize = 20.sp, - modifier = Modifier.padding(vertical = 8.dp) - ) - uiState.listCharacters.results.map { - SearchStreamCard( - content = it.toSearchStreamCardModel(), - onSearchStreamPressed = { id -> - onNavigateDetailList(id) - } - ) - } - } - } else { - Box(modifier = Modifier.padding(paddingValues)) { - StreamsEmpty() - } - } - } - BackHandler { - navController.goBack() - } - -} - -@Composable -private fun Lifecycle( - lifecycleOwner: LifecycleOwner, viewModel: SearchViewModel, disposable: () -> Unit -) { - DisposableEffect(lifecycleOwner) { - val lifecycle = lifecycleOwner.lifecycle - - lifecycle.addObserver(viewModel) - - onDispose { - lifecycle.removeObserver(viewModel) - disposable.invoke() - } - } -} diff --git a/feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/search/presentation/screens/SearchUIState.kt b/feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/search/presentation/screens/SearchUIState.kt deleted file mode 100644 index 965ec7dd..00000000 --- a/feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/search/presentation/screens/SearchUIState.kt +++ /dev/null @@ -1,10 +0,0 @@ -package com.codandotv.streamplayerapp.feature_list_streams.search.presentation.screens - -import com.codandotv.streamplayerapp.feature_list_streams.search.data.model.ListSearchStreamResponse - -sealed class SearchUIState { - data class Success(val listCharacters: ListSearchStreamResponse) : SearchUIState() - data class Error(val messageError: String = String()) : SearchUIState() - object Loading : SearchUIState() - object Empty : SearchUIState() -} diff --git a/feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/search/presentation/screens/SearchViewModel.kt b/feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/search/presentation/screens/SearchViewModel.kt deleted file mode 100644 index f02c07ac..00000000 --- a/feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/search/presentation/screens/SearchViewModel.kt +++ /dev/null @@ -1,113 +0,0 @@ -package com.codandotv.streamplayerapp.feature_list_streams.search.presentation.screens - -import androidx.lifecycle.DefaultLifecycleObserver -import androidx.lifecycle.ViewModel -import androidx.lifecycle.viewModelScope -import com.codandotv.streamplayerapp.core_networking.handleError.catchFailure -import com.codandotv.streamplayerapp.feature_list_streams.search.domain.MostPopularMoviesUseCase -import com.codandotv.streamplayerapp.feature_list_streams.search.domain.SearchUseCase -import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.SharingStarted -import kotlinx.coroutines.flow.StateFlow -import kotlinx.coroutines.flow.asStateFlow -import kotlinx.coroutines.flow.onStart -import kotlinx.coroutines.flow.stateIn -import kotlinx.coroutines.flow.update -import kotlinx.coroutines.launch - -class SearchViewModel( - private val searchUseCase: SearchUseCase, - private val mostPopularMoviesUseCase: MostPopularMoviesUseCase -) : ViewModel(), DefaultLifecycleObserver { - - private var tryAgain: () -> Unit = {} - - private val _uiState = MutableStateFlow(SearchUIState.Loading) - val uiState: StateFlow = _uiState.stateIn( - scope = viewModelScope, - started = SharingStarted.Eagerly, - initialValue = _uiState.value - ) - - private val _currentSearchText = MutableStateFlow("") - val currentSearchText = _currentSearchText.asStateFlow() - - init { - fetchMostPopularMovies() - } - - fun fetchMovies() { - if (_currentSearchText.value.isBlank()) { - fetchMostPopularMovies() - } else { - fetchMovieByQuery() - } - } - - private fun fetchMovieByQuery() { - tryAgain = ::fetchMovieByQuery - - viewModelScope.launch { - searchUseCase( - query = _currentSearchText.value - ).onStart { - showLoading() - }.catchFailure { response -> - showError(messageError = response.errorMessage.orEmpty()) - }.collect { result -> - _uiState.update { - if (result.results.isEmpty()) { - SearchUIState.Empty - } else { - SearchUIState.Success(result) - } - } - } - } - } - - private fun fetchMostPopularMovies() { - tryAgain = ::fetchMostPopularMovies - - viewModelScope.launch { - mostPopularMoviesUseCase().onStart { - showLoading() - }.catchFailure { response -> - showError(messageError = response.errorMessage.orEmpty()) - }.collect { result -> - _uiState.update { - if (result.results.isEmpty()) { - SearchUIState.Empty - } else { - SearchUIState.Success(result) - } - } - } - } - } - - private fun showError(messageError: String) { - _uiState.update { - SearchUIState.Error(messageError = messageError) - } - } - - private fun showLoading() { - _uiState.update { - SearchUIState.Loading - } - } - - fun onTryAgain() { - tryAgain() - } - - fun setCurrentSearchText(newText: String) { - _currentSearchText.value = newText - } - - fun onCleanText() { - _currentSearchText.value = "" - fetchMostPopularMovies() - } -} diff --git a/feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/search/presentation/widgets/SearchCarousel.kt b/feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/search/presentation/widgets/SearchCarousel.kt deleted file mode 100644 index 0e884005..00000000 --- a/feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/search/presentation/widgets/SearchCarousel.kt +++ /dev/null @@ -1,165 +0,0 @@ -package com.codandotv.streamplayerapp.feature_list_streams.search.presentation.widgets - -import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.Spacer -import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.height -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.lazy.LazyColumn -import androidx.compose.foundation.shape.RoundedCornerShape -import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.filled.Close -import androidx.compose.material3.Button -import androidx.compose.material3.ButtonDefaults -import androidx.compose.material3.Icon -import androidx.compose.material3.IconButton -import androidx.compose.material3.Text -import androidx.compose.runtime.Composable -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.res.stringResource -import androidx.compose.ui.text.font.FontWeight -import androidx.compose.ui.text.style.TextAlign -import androidx.compose.ui.tooling.preview.Preview -import androidx.compose.ui.unit.dp -import androidx.compose.ui.unit.sp -import androidx.paging.PagingData -import androidx.paging.compose.collectAsLazyPagingItems -import androidx.paging.compose.itemContentType -import androidx.paging.compose.itemKey -import com.codandotv.streamplayerapp.feature.list.streams.R -import com.codandotv.streamplayerapp.feature_list_streams.list.presentation.widgets.StreamsCard -import com.codandotv.streamplayerapp.feature_list_streams.list.presentation.widgets.StreamsCardContent -import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.emptyFlow - -data class SearchCarousel( - val genreTitle: String, - val contentList: Flow> -) - -@Composable -fun SearchCarouselStream( - content: SearchCarousel, - onNavigateDetailList: (String) -> Unit = {}, - modifier: Modifier = Modifier -) { - val lazyPagingItems = content.contentList.collectAsLazyPagingItems() - - Text( - text = stringResource(id = R.string.search_list_describle), - color = Color.Black, - fontSize = 14.sp - ) - - Column { - LazyColumn( - modifier - .fillMaxSize() - .height(140.dp) - ) { - items( - count = lazyPagingItems.itemCount, - key = lazyPagingItems.itemKey(), - contentType = lazyPagingItems.itemContentType() - ) { index -> - val item = lazyPagingItems[index] - item?.let { - StreamsCard( - content = it, - onNavigateDetailList = onNavigateDetailList - ) - } - } - } - } -} - -@Composable -@Preview -fun StreamsCarouselPreview() { - SearchCarouselStream( - content = SearchCarousel( - genreTitle = "Comédia", - contentList = emptyFlow() - ) - ) -} - -@Composable -fun StreamsError( - onRetry: () -> Unit, - onCloseButton: () -> Unit -) { - Box( - modifier = Modifier - .fillMaxSize() - .padding(16.dp) - ) { - IconButton(onClick = { - onCloseButton() - }) { - Icon( - imageVector = Icons.Filled.Close, - contentDescription = stringResource(id = R.string.detail_back), - tint = Color.White - ) - } - Text( - text = stringResource(id = R.string.search_list_error), - color = Color.White, - fontWeight = FontWeight.SemiBold, - fontSize = 20.sp, - textAlign= TextAlign.Center, - modifier = Modifier - .align(Alignment.Center) - ) - Spacer(modifier = Modifier.height(8.dp)) - Button( - onClick = onRetry, - modifier = Modifier - .fillMaxWidth() - .padding(8.dp) - .align(Alignment.BottomCenter), - enabled = true, - colors = ButtonDefaults.buttonColors( - contentColor = Color.White, - containerColor = Color.Red - ), - shape = RoundedCornerShape(8.dp), - ) { - Text(text = stringResource(id = R.string.bottom_search_list_error)) - } - } -} - -@Composable -@Preview -fun StreamsEmpty() { - Row(verticalAlignment = Alignment.CenterVertically) { - Column { - Text( - text = stringResource(id = R.string.empty_search_list), - color = Color.White, - fontWeight = FontWeight.SemiBold, - fontSize = 20.sp, - textAlign = TextAlign.Center, - modifier = Modifier.padding(vertical = 16.dp) - - ) - } - } -} - -@Composable -@Preview -fun StreamsErrorPreview() { - StreamsError( - onRetry = {}, - onCloseButton = {} - ) -} \ No newline at end of file diff --git a/feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/search/presentation/widgets/SearchStreamCard.kt b/feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/search/presentation/widgets/SearchStreamCard.kt deleted file mode 100644 index 86d1d674..00000000 --- a/feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/search/presentation/widgets/SearchStreamCard.kt +++ /dev/null @@ -1,128 +0,0 @@ -package com.codandotv.streamplayerapp.feature_list_streams.search.presentation.widgets - -import androidx.compose.foundation.background -import androidx.compose.foundation.border -import androidx.compose.foundation.clickable -import androidx.compose.foundation.layout.Box -import androidx.compose.material3.Icon -import androidx.compose.runtime.Composable -import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.Color -import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.height -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.shape.RoundedCornerShape -import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.filled.PlayArrow -import androidx.compose.material3.Card -import androidx.compose.material3.Text -import androidx.compose.ui.Alignment -import androidx.compose.ui.layout.ContentScale -import androidx.compose.ui.text.font.FontWeight -import androidx.compose.ui.text.style.TextOverflow -import androidx.compose.ui.unit.dp -import androidx.compose.ui.unit.sp -import coil.compose.AsyncImage -import com.codandotv.streamplayerapp.core_shared_ui.theme.ThemePreviews - -@Suppress("MagicNumber") -@Composable -fun SearchStreamCard( - content: SearchStreamCardModel, - onSearchStreamPressed: (id: String) -> Unit -) { - Row( - verticalAlignment = Alignment.CenterVertically, - modifier = Modifier - .fillMaxWidth() - .height(90.dp) - .background(color = Color.Black) - .padding(vertical = 4.dp, horizontal = 8.dp) - .clickable { - onSearchStreamPressed(content.id) - } - ) { - ImageStream( - url = content.url, - modifier = Modifier - .weight(2.5f) - .padding(vertical = 2.dp) - ) - Text( - text = content.title, - color = Color.White, - modifier = Modifier - .weight(4.5f) - .padding(8.dp), - fontWeight = FontWeight.SemiBold, - fontSize = 18.sp, - overflow = TextOverflow.Ellipsis - ) - PlayerIcon( - modifier = Modifier - .weight(2f) - .padding(2.dp) - ) - } -} - -data class SearchStreamCardModel( - val id: String, - val title: String, - val url: String -) - -@Composable -fun ImageStream(modifier: Modifier, url: String) { - Card( - shape = RoundedCornerShape(4.dp), - modifier = modifier - ) { - AsyncImage( - model = url, - contentScale = ContentScale.FillBounds, - contentDescription = "", - modifier = Modifier.fillMaxSize() - ) - } -} - -@Suppress("MagicNumber") -@Composable -fun PlayerIcon(modifier: Modifier) { - Box( - modifier = Modifier.border( - width = 0.5.dp, - shape = RoundedCornerShape(100), - color = Color.White - ) - ) { - Icon( - imageVector = Icons.Filled.PlayArrow, - tint = Color.White, - contentDescription = "", - modifier = modifier - ) - } -} - -@ThemePreviews -@Composable -fun SearchStreamCardPreview() { - SearchStreamCard( - content = SearchStreamCardModel( - id = "1", - title = "The Witcher", - url = "https://image.tmdb.org/t/p/w200/iwsMu0ehRPbtaSxqiaUDQB9qMWT.jpg" - ), - onSearchStreamPressed = {} - ) -} - -@ThemePreviews -@Composable -fun PlayerPreview() { - PlayerIcon(modifier = Modifier) -} diff --git a/feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/search/presentation/widgets/SearchStreams.kt b/feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/search/presentation/widgets/SearchStreams.kt deleted file mode 100644 index 6626e3ca..00000000 --- a/feature-list-streams/src/main/java/com/codandotv/streamplayerapp/feature_list_streams/search/presentation/widgets/SearchStreams.kt +++ /dev/null @@ -1,251 +0,0 @@ -package com.codandotv.streamplayerapp.feature_list_streams.search.presentation.widgets - -import androidx.compose.foundation.background -import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.Spacer -import androidx.compose.foundation.layout.fillMaxHeight -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.height -import androidx.compose.foundation.shape.RoundedCornerShape -import androidx.compose.foundation.text.KeyboardActions -import androidx.compose.foundation.text.KeyboardOptions -import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.automirrored.filled.ArrowBack -import androidx.compose.material.icons.filled.Cast -import androidx.compose.material.icons.filled.Close -import androidx.compose.material.icons.filled.MicNone -import androidx.compose.material.icons.filled.Search -import androidx.compose.material3.ExperimentalMaterial3Api -import androidx.compose.material3.Icon -import androidx.compose.material3.IconButton -import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.Text -import androidx.compose.material3.TextField -import androidx.compose.material3.TextFieldDefaults -import androidx.compose.runtime.Composable -import androidx.compose.ui.Modifier -import androidx.compose.ui.draw.clip -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.graphics.vector.ImageVector -import androidx.compose.ui.res.painterResource -import androidx.compose.ui.res.stringResource -import androidx.compose.ui.text.TextStyle -import androidx.compose.ui.text.input.ImeAction -import androidx.compose.ui.tooling.preview.Preview -import androidx.compose.ui.unit.dp -import com.codandotv.streamplayerapp.core.shared.ui.R -import com.codandotv.streamplayerapp.core_shared_ui.resources.Colors -import com.codandotv.streamplayerapp.feature.list.streams.R as ResourceListStream - -@Suppress("LongParameterList") -@Composable -fun SearchableTopBar( - currentSearchText: String, - onSearchTextChanged: (String) -> Unit, - onSearchDispatched: () -> Unit, - onSearchIconPressed: () -> Unit, - onBackPressed: () -> Unit, - onCleanTextPressed: () -> Unit -) { - Column { - StreamPlayerTopBar( - onBackPressed = onBackPressed - ) - SearchTopBar( - currentSearchText = currentSearchText, - onSearchTextChanged = onSearchTextChanged, - onSearchDispatched = onSearchDispatched, - onCleanTextPressed = onCleanTextPressed, - onSearchIconPressed = onSearchIconPressed - ) - } -} - -@Composable -private fun StreamPlayerTopBar( - onBackPressed: () -> Unit -) { - Row( - modifier = Modifier - .height(56.dp) - ) { - IconButton( - modifier = Modifier.fillMaxHeight(), - onClick = { - onBackPressed() - } - ) { - Icon( - imageVector = Icons.AutoMirrored.Filled.ArrowBack, - contentDescription = stringResource(id = R.string.icon_back), - tint = Color.White, - ) - } - Spacer(modifier = Modifier.weight(1f)) - - IconButton( - modifier = Modifier.fillMaxHeight(), - onClick = { /* todo */ } - ) { - Icon( - imageVector = Icons.Default.Cast, - contentDescription = stringResource(id = R.string.icon_cast), - tint = Color.White, - ) - } - - IconButton( - modifier = Modifier.fillMaxHeight(), - onClick = { /* todo */ } - ) { - Icon( - modifier = Modifier - .height(24.dp) - .clip(RoundedCornerShape(4.dp)), - painter = painterResource(R.drawable.perfil_fake), - contentDescription = stringResource(id = R.string.icon_profile), - tint = Color.Unspecified, - ) - } - } -} - -@OptIn(ExperimentalMaterial3Api::class) -@Composable -fun SearchTopBar( - currentSearchText: String, - onSearchTextChanged: (String) -> Unit, - onSearchDispatched: () -> Unit, - onCleanTextPressed: () -> Unit, - onSearchIconPressed: () -> Unit -) { - Box( - modifier = Modifier - .fillMaxWidth() - .height(56.dp) - .background(Colors.Gray100) - ) { - TextField( - modifier = Modifier.fillMaxWidth(), - value = currentSearchText, - colors = TextFieldDefaults.colors( - focusedContainerColor = Colors.Gray100, - focusedIndicatorColor = Color.Transparent, - disabledIndicatorColor = Color.Transparent, - unfocusedIndicatorColor = Color.Transparent, - cursorColor = Color.Gray - ), - onValueChange = { - onSearchTextChanged(it) - }, - placeholder = { - Text( - text = stringResource(id = ResourceListStream.string.search_list_main_search), - color = Color.Gray - ) - }, - textStyle = TextStyle( - fontSize = MaterialTheme.typography.bodyMedium.fontSize, - color = Color.White - ), - singleLine = true, - maxLines = 1, - leadingIcon = { - SearchIcon(action = onSearchIconPressed) - }, - trailingIcon = { - if (currentSearchText.isEmpty()) MicButton() else CloseButton( - action = onCleanTextPressed - ) - }, - keyboardOptions = KeyboardOptions(imeAction = ImeAction.Search), - keyboardActions = KeyboardActions(onSearch = { - onSearchDispatched() - }), - ) - } -} - -@Composable -fun DefaultIcon( - modifier: Modifier = Modifier, - searchIcon: ImageVector = Icons.Default.Search, - iconColor: Color = Color.White, - contentDescription: String = "", - onIconClickAction: () -> Unit = {} -) { - IconButton( - modifier = modifier, - onClick = onIconClickAction - ) { - Icon( - imageVector = searchIcon, - contentDescription = contentDescription, - tint = iconColor - ) - } -} - -@Composable -fun SearchIcon(action: () -> Unit = {}) { - DefaultIcon( - searchIcon = Icons.Filled.Search, - contentDescription = stringResource(id = R.string.icon_search), - onIconClickAction = action, - iconColor = Color.Gray - ) -} - -@Composable -fun CloseButton(action: () -> Unit = {}) { - DefaultIcon( - searchIcon = Icons.Default.Close, - contentDescription = stringResource(id = R.string.icon_close), - onIconClickAction = action, - iconColor = Color.Gray - ) -} - -@Composable -private fun MicButton(action: () -> Unit = {}) { - DefaultIcon( - searchIcon = Icons.Default.MicNone, - contentDescription = stringResource(id = R.string.icon_mic), - onIconClickAction = action, - iconColor = Color.Gray - ) -} - -@Composable -@Preview -fun SearchBarPreview() { - StreamPlayerTopBar( - onBackPressed = {} - ) -} - -@Composable -@Preview -fun SearchTopBarEmptyPreview() { - SearchTopBar( - currentSearchText = "", - onSearchTextChanged = {}, - onSearchDispatched = {}, - onCleanTextPressed = {}, - onSearchIconPressed = {} - ) -} - -@Composable -@Preview -fun SearchTopBarPreview() { - SearchTopBar( - currentSearchText = "Texto de busca", - onSearchTextChanged = {}, - onSearchDispatched = {}, - onCleanTextPressed = {}, - onSearchIconPressed = {} - ) -} diff --git a/feature-list-streams/src/main/res/drawable/ic_top_10.webp b/feature-list-streams/src/main/res/drawable/ic_top_10.webp deleted file mode 100644 index e165119df7ba756fc18552ab94df550ee2d80d8e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1518 zcmV)?%hS# zw;#iP;Mp}2Qk=itBZX_)p*hO0Qt19_s^xR0v2kR8sualGvd69+}7bWi2 zrqp3rp=kEOD7sr0=>iqf!Kj5Y3@wzCQ9@Z@D`ogFDRH+NWx@(YedrX~Eu|WMP^P=A z)V^1RG8~9+xsi^&niO%+{hD3rg+0lKA@4^;MhgMA2Y;3)Ya4&$$qw5sqo{-UTw;7E zIHgvyvp^MxgVL(~?P`HB} z)z_Byt<*l$DK0nlx83$^sB>6u>T}CGrq8l9^}A(m)u_IY%B)5G7hYZZj0dM`dx};V zH5DF3B*IkqR1_@hX764nNN;TFm@)0MIbwuMMWIrS*daQZ)FEQ@4iO$zMaBv+L1B)?$wy2x5cT0E}H~ z=aKDW4;0u9zW}uC&ynr(?k3`LkP++tkliK3=D-G^v*!eex$FR8PXr_<2SC}Qz{|-* z6y{_^c4COhoQW{E18^{B1q??5NX$t9adZNfz(@dL&I0IxA%OHxt{OZucxv#Ze>yCH z9vBHAfp-EH=2Sp)B!I-62~bB3a4;t$vKs|X<`|+nnTW!j2w0pP2;F~}l77to==VH{c! zIw7SY78Y#{F)E-&MHUuWM`*0Hu1GN+6^O<{sG`uxB!+@vP%$nQjOsYbJEcYi zPc~TsJ&H(E_-TCF2F__KjI#L@hjGf5Cpe8)Cf>!$tr|UR*|=r3dDXJ<%X0LnW#gFD z=*`H+GppSbsm3+4t9j54x+=UuLBMSX|g~aO=pEYwWtMo5)00w5{Z~y=R diff --git a/feature-list-streams/src/main/res/drawable/image_placeholder.xml b/feature-list-streams/src/main/res/drawable/image_placeholder.xml deleted file mode 100644 index 13e24402..00000000 --- a/feature-list-streams/src/main/res/drawable/image_placeholder.xml +++ /dev/null @@ -1,11 +0,0 @@ - - - \ No newline at end of file diff --git a/feature-list-streams/src/main/res/drawable/netflix_detail.webp b/feature-list-streams/src/main/res/drawable/netflix_detail.webp deleted file mode 100644 index fcd5b3a4cadf673e393c1bcba48c4e475c464389..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4428 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 S+f?|DTeyiUIDjj#5dZ*@hVd5w diff --git a/feature-list-streams/src/main/res/drawable/netflix_horizontal_logo.xml b/feature-list-streams/src/main/res/drawable/netflix_horizontal_logo.xml deleted file mode 100644 index e6367f02..00000000 --- a/feature-list-streams/src/main/res/drawable/netflix_horizontal_logo.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/feature-list-streams/src/main/res/drawable/play_circle.xml b/feature-list-streams/src/main/res/drawable/play_circle.xml deleted file mode 100644 index 1a68c27f..00000000 --- a/feature-list-streams/src/main/res/drawable/play_circle.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/feature-list-streams/src/main/res/layout/activity_list_stream.xml b/feature-list-streams/src/main/res/layout/activity_list_stream.xml deleted file mode 100644 index 5377cc83..00000000 --- a/feature-list-streams/src/main/res/layout/activity_list_stream.xml +++ /dev/null @@ -1,18 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/feature-list-streams/src/main/res/values/content-description.xml b/feature-list-streams/src/main/res/values/content-description.xml deleted file mode 100644 index 99cae6c4..00000000 --- a/feature-list-streams/src/main/res/values/content-description.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - Ícone Reprodução - Ícone adicionar - Ícone informações - Poster de conteúdo em destaque - Ícone top 10 - \ No newline at end of file diff --git a/feature-list-streams/src/main/res/values/strings.xml b/feature-list-streams/src/main/res/values/strings.xml deleted file mode 100644 index beceb11d..00000000 --- a/feature-list-streams/src/main/res/values/strings.xml +++ /dev/null @@ -1,34 +0,0 @@ - - - Assistir - Baixar E1 - Filme - Minha Lista - Classificar - Compartilhar - Baixar completo - Pesquisar - Voltar - - - Série - Séries - Filme - Filmes - - Minha lista - Saiba mais - Assistir - Top 1 em %s hoje - - - - Principais buscas - Principais buscas - - - Houve um problema ao conectar à Netflix. Tente novamente mais tarde. - Tente novamente - Conteúdo não encontrado - - \ No newline at end of file diff --git a/feature-profile/build.gradle.kts b/feature-profile/build.gradle.kts index 4695d8f9..c6e65f20 100644 --- a/feature-profile/build.gradle.kts +++ b/feature-profile/build.gradle.kts @@ -2,7 +2,8 @@ plugins { id("com.streamplayer.android-library") - id("com.streamplayer.compose") + alias(libs.plugins.jetbrains.compose) + alias(libs.plugins.compose.compiler) alias(libs.plugins.ksp) } diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index e3e03973..31327de6 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,12 +1,14 @@ [versions] -kotlin = "1.9.25" +kotlin = "2.0.21" android_gradle_plugin = "8.2.2" koin = "3.4.0" koin-annotations-bom = "1.3.0" -ksp = "1.9.25-1.0.20" +ksp = "2.0.21-1.0.27" dokka = "1.9.10" kover = "0.7.5" detekt = "1.23.6" +compose_plugin_multiplataform = "1.7.1" +navigation-compose-version = "2.7.0-alpha07" #Test test_junit = "4.13.2" @@ -33,17 +35,7 @@ androidx_core_ktx = "1.13.1" #Networking moshi = "1.14.0" okhttp = "4.12.0" -ktor = "2.3.0" - -#Compose -compose = "1.5.15" -compose_bom = "2024.09.02" -compose_material_3 = "1.3.0" -compose_activity = "1.5.0" -compose_icons = "1.4.3" -compose_navigation = "2.8.1" -lifecycle_version = "2.8.6" -compose_pagging = "3.3.2" +ktor = "3.0.1" coil = "2.3.0" lottie = "5.2.0" @@ -84,36 +76,12 @@ mockk_android = { group = "io.mockk", name = "mockk-android", version.ref = "moc viewmodel_test = { group = "androidx.arch.core", name = "core-testing", version.ref = "androidx_core_testing" } coroutines_test = { group = "org.jetbrains.kotlinx", name = "kotlinx-coroutines-test", version.ref = "kotlinx-coroutines-test" } -# AndroidX Lifecycle -androidx_viewmodel_lifecycle-extensions = { group = "androidx.lifecycle", name = "lifecycle-extensions", version.ref = "viewmodel" } -androidx_common_java = { group = "androidx.lifecycle", name = "lifecycle-reactivestreams", version.ref = "viewmodel" } - -# AndroidX test -androidx_test_core = { group = "androidx.test", name = "core-ktx", version.ref = "androidx_test_core" } -androidx_test_rules = { group = "androidx.test", name = "rules", version.ref = "androidx_test_rules" } -androidx_test_runner = { group = "androidx.test", name = "runner", version.ref = "androidx_test_runner" } -androidx_test_junit_ext = { group = "androidx.test.ext", name = "junit-ktx", version.ref = "androidx_test_junit_ext" } -android_test_mockwebserver = { group = "com.squareup.okhttp3", name = "mockwebserver", version.ref = "mockWebServer" } +#Navigation +navigation-compose = { module = "org.jetbrains.androidx.navigation:navigation-compose", version.ref = "navigation-compose-version" } # Google google_material = { group = "com.google.android.material", name = "material", version.ref = "material" } -# Compose -compose_bom = { group = "androidx.compose", name = "compose-bom", version.ref = "compose_bom" } -compose_ui = { module = "androidx.compose.ui:ui" } -compose_toolingpreview = { module = "androidx.compose.ui:ui-tooling-preview" } -compose_icons = { module = "androidx.compose.material:material-icons-extended" } -compose_lifecycle = { group = "androidx.lifecycle", name = "lifecycle-runtime-compose", version.ref = "lifecycle_version" } -compose_material3 = { group = "androidx.compose.material3", name = "material3", version.ref = "compose_material_3" } -compose_navigation = { group = "androidx.navigation", name = "navigation-compose", version.ref = "compose_navigation" } -compose_pagging = { group = "androidx.paging", name = "paging-compose", version.ref = "compose_pagging" } -compose_activity = { module = "androidx.activity:activity-compose" } -compose_ui_tooling = { module = "androidx.compose.ui:ui-tooling" } -compose_ui_test = { module = "androidx.compose.ui:ui-test-junit4" } -compose_manifest = { module = "androidx.compose.ui:ui-test-manifest" } -compose_uitest = { module = "androidx.compose.ui:ui-test" } -compose_junit4 = { module = "androidx.compose.ui:ui-test-junit4" } - # Koin koin_test = { group = "io.insert-koin", name = "koin-test-junit4", version.ref = "koin" } koin_android = { group = "io.insert-koin", name = "koin-android", version.ref = "koin" } @@ -143,7 +111,6 @@ dokka = { group = "org.jetbrains.dokka", name = "android-documentation-plugin", [bundles] room = ["roomRuntime", "roomKtx"] -compose = ["compose.ui", "compose.icons", "compose.material3", "compose_pagging", "compose.lifecycle", "compose.navigation", "compose.activity", "compose.ui.tooling"] networking = ["moshi", "okhttp", "interceptor", "ktor_client_core", "ktor_client_okhttp", "ktor_client_content_serialization_json", "ktor_client_content_negotiation", "ktor_client_logger", "ktor_client_auth"] koin = ["koin_android", "koin_compose"] test = ["junit", "mockk", "mockk_android", "viewmodel_test", "koin_test", "coroutines_test"] @@ -157,6 +124,9 @@ android_library = { id = "com.android.library", version.ref = "android_gradle_pl kotlin_android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" } serialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "kotlin" } +jetbrains-compose = { id = "org.jetbrains.compose", version.ref = "compose_plugin_multiplataform" } +kotlin_multiplatform = { id = "org.jetbrains.kotlin.multiplatform", version.ref = "kotlin" } +compose_compiler = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" } ksp = { id = "com.google.devtools.ksp", version.ref = "ksp" } dokka = { id = "org.jetbrains.dokka", version.ref = "dokka" } diff --git a/settings.gradle.kts b/settings.gradle.kts index 2e3a17f0..0aeaa485 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -1,4 +1,5 @@ -@file:Suppress("UnstableApiUsage") +rootProject.name = "StreamPlayerApp-KMP" + pluginManagement { includeBuild("build-logic") repositories { @@ -14,6 +15,7 @@ dependencyResolutionManagement { mavenCentral() maven { setUrl("https://jitpack.io") } maven(url = uri("https://oss.sonatype.org/content/repositories/snapshots/")) + } } From 41cb6a2346d6c086aca33b271a93bdb43f7bea0e Mon Sep 17 00:00:00 2001 From: Rods Date: Thu, 12 Dec 2024 16:34:52 -0300 Subject: [PATCH 10/96] =?UTF-8?q?[ISSUE-2]=20-=20=F0=9F=94=A5=20migrating?= =?UTF-8?q?=20android=20para=20KMP=20module=20list=20/=20koin?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/build.gradle.kts | 7 ++- .../widget/SharingStreamCustomView.kt | 11 ++--- feature-list-streams/build.gradle.kts | 24 ++++++++-- feature-profile/build.gradle.kts | 5 +- .../profile/di/ProfilePickerStreamModule.kt | 48 ++++++++++++------- .../ProfilePickerStreamNavigation.kt | 5 +- gradle/libs.versions.toml | 15 ++++-- 7 files changed, 80 insertions(+), 35 deletions(-) diff --git a/app/build.gradle.kts b/app/build.gradle.kts index ef98ee19..32e66691 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -2,6 +2,8 @@ plugins { id("com.streamplayer.application") + alias(libs.plugins.jetbrains.compose) + alias(libs.plugins.compose.compiler) } dependencies { @@ -14,7 +16,10 @@ dependencies { implementation(projects.coreNetworking) implementation(projects.coreLocalStorage) - + implementation(libs.navigation.compose) + implementation(compose.material3) + implementation(compose.ui) + implementation(compose.preview) implementation(libs.bundles.koin) implementation(libs.bundles.androidSupport) implementation(libs.bundles.kotlin) diff --git a/core-shared-ui/src/main/java/com/codandotv/streamplayerapp/core_shared_ui/widget/SharingStreamCustomView.kt b/core-shared-ui/src/main/java/com/codandotv/streamplayerapp/core_shared_ui/widget/SharingStreamCustomView.kt index b3063630..e1f1e6af 100644 --- a/core-shared-ui/src/main/java/com/codandotv/streamplayerapp/core_shared_ui/widget/SharingStreamCustomView.kt +++ b/core-shared-ui/src/main/java/com/codandotv/streamplayerapp/core_shared_ui/widget/SharingStreamCustomView.kt @@ -8,7 +8,6 @@ import android.content.Context import android.content.Intent import android.net.Uri import android.widget.Toast -import androidx.activity.compose.BackHandler import androidx.compose.animation.AnimatedVisibility import androidx.compose.animation.AnimatedVisibilityScope import androidx.compose.animation.core.keyframes @@ -293,11 +292,11 @@ fun SharingStreamCustomView( } } } - BackHandler { - coroutineScope.launch { - startDismissWithExitAnimation(animateTrigger) { setShowDialog(false) } - } - } +// BackHandler { +// coroutineScope.launch { +// startDismissWithExitAnimation(animateTrigger) { setShowDialog(false) } +// } +// } } } diff --git a/feature-list-streams/build.gradle.kts b/feature-list-streams/build.gradle.kts index 7e76b291..85620e3c 100644 --- a/feature-list-streams/build.gradle.kts +++ b/feature-list-streams/build.gradle.kts @@ -3,7 +3,6 @@ import org.jetbrains.kotlin.gradle.ExperimentalKotlinGradlePluginApi import org.jetbrains.kotlin.gradle.dsl.JvmTarget - plugins { id("com.streamplayer.android-library") alias(libs.plugins.ksp) @@ -21,23 +20,42 @@ kotlin { } sourceSets { + commonMain.dependencies { + implementation(libs.koin.annotations) + implementation(libs.koin.core) + } + androidMain.dependencies { - implementation(compose.material3) - implementation(compose.ui) implementation(projects.coreNetworking) implementation(projects.coreNavigation) implementation(projects.coreShared) implementation(projects.coreSharedUi) implementation(projects.coreLocalStorage) + + implementation(compose.material3) + implementation(compose.ui) + implementation(compose.preview) + implementation(libs.paging.compose) + implementation(libs.navigation.compose) implementation(libs.bundles.koin) implementation(libs.bundles.networking) implementation(libs.coil) implementation(libs.koin.annotations) implementation(libs.bundles.androidSupport) + + } + + sourceSets.named("androidMain").configure { + kotlin.srcDir("build/generated/ksp/metadata/androidMain/kotlin") } } } +dependencies { + add("kspCommonMainMetadata",libs.koin.compiler) + add("kspAndroid", libs.koin.compiler) +} + ksp { arg("KOIN_CONFIG_CHECK","true") } \ No newline at end of file diff --git a/feature-profile/build.gradle.kts b/feature-profile/build.gradle.kts index c6e65f20..7cba8dde 100644 --- a/feature-profile/build.gradle.kts +++ b/feature-profile/build.gradle.kts @@ -12,8 +12,11 @@ dependencies { implementation(projects.coreNavigation) implementation(projects.coreShared) implementation(projects.coreSharedUi) + implementation(libs.navigation.compose) + implementation(compose.material3) + implementation(compose.ui) - implementation(libs.bundles.koin) + implementation(libs.koin.android) implementation(libs.koin.annotations) ksp(libs.koin.compiler) diff --git a/feature-profile/src/main/java/com/codandotv/streamplayerapp/feature_profile/profile/di/ProfilePickerStreamModule.kt b/feature-profile/src/main/java/com/codandotv/streamplayerapp/feature_profile/profile/di/ProfilePickerStreamModule.kt index 03d4c680..033494fb 100644 --- a/feature-profile/src/main/java/com/codandotv/streamplayerapp/feature_profile/profile/di/ProfilePickerStreamModule.kt +++ b/feature-profile/src/main/java/com/codandotv/streamplayerapp/feature_profile/profile/di/ProfilePickerStreamModule.kt @@ -1,23 +1,39 @@ package com.codandotv.streamplayerapp.feature_profile.profile.di -import com.codandotv.streamplayerapp.core_networking.di.QualifierProfileHttpClient +import com.codandotv.streamplayerapp.feature_profile.profile.data.ProfilePickerStreamRepository +import com.codandotv.streamplayerapp.feature_profile.profile.data.ProfilePickerStreamRepositoryImpl import com.codandotv.streamplayerapp.feature_profile.profile.data.ProfilePickerStreamService import com.codandotv.streamplayerapp.feature_profile.profile.data.ProfilePickerStreamServiceImpl -import io.ktor.client.HttpClient -import org.koin.core.annotation.ComponentScan -import org.koin.core.annotation.Factory -import org.koin.core.annotation.Module -import org.koin.core.context.GlobalContext +import com.codandotv.streamplayerapp.feature_profile.profile.domain.ProfilePickerStreamUseCase +import com.codandotv.streamplayerapp.feature_profile.profile.domain.ProfilePickerStreamUseCaseImpl +import com.codandotv.streamplayerapp.feature_profile.profile.presentation.screens.ProfilePickerStreamViewModel +import org.koin.androidx.viewmodel.dsl.viewModel +import org.koin.dsl.module -@Module -@ComponentScan("com.codandotv.streamplayerapp.feature_profile") -class ProfilePickerStreamModule { +object ProfilePickerStreamModule { + val module = module { + viewModel { + ProfilePickerStreamViewModel( + useCase = get() + ) + } - @Factory - fun service(): ProfilePickerStreamService { - val koin = GlobalContext.get() - val httpClient = koin.get(QualifierProfileHttpClient) - return ProfilePickerStreamServiceImpl(httpClient) - } + factory { + ProfilePickerStreamUseCaseImpl( + profilePickerStreamRepository = get() + ) + } + + factory { + ProfilePickerStreamRepositoryImpl( + service = get() + ) + } -} + factory { + ProfilePickerStreamServiceImpl( + client = get() + ) + } + } +} \ No newline at end of file diff --git a/feature-profile/src/main/java/com/codandotv/streamplayerapp/feature_profile/profile/presentation/navigation/ProfilePickerStreamNavigation.kt b/feature-profile/src/main/java/com/codandotv/streamplayerapp/feature_profile/profile/presentation/navigation/ProfilePickerStreamNavigation.kt index 3624f939..c03c4a27 100644 --- a/feature-profile/src/main/java/com/codandotv/streamplayerapp/feature_profile/profile/presentation/navigation/ProfilePickerStreamNavigation.kt +++ b/feature-profile/src/main/java/com/codandotv/streamplayerapp/feature_profile/profile/presentation/navigation/ProfilePickerStreamNavigation.kt @@ -11,19 +11,18 @@ import com.codandotv.streamplayerapp.feature_profile.profile.di.ProfilePickerStr import com.codandotv.streamplayerapp.feature_profile.profile.presentation.screens.ProfilePickerStreamScreen import org.koin.core.context.loadKoinModules import org.koin.core.context.unloadKoinModules -import org.koin.ksp.generated.module fun NavGraphBuilder.profilePickerStreamNavGraph(navController: NavHostController) { composable(Routes.PROFILE_PICKER) { nav -> if (nav.lifecycle.currentState == Lifecycle.State.STARTED) { - loadKoinModules(ProfilePickerStreamModule().module) + loadKoinModules(ProfilePickerStreamModule.module) } ProfilePickerStreamScreen( onNavigateListStreams = { profilePic -> navController.navigate("$HOME?$PROFILE_ID=$profilePic") } ) { - unloadKoinModules(ProfilePickerStreamModule().module) + unloadKoinModules(ProfilePickerStreamModule.module) } } } diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 31327de6..958ad604 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,14 +1,16 @@ [versions] kotlin = "2.0.21" android_gradle_plugin = "8.2.2" -koin = "3.4.0" -koin-annotations-bom = "1.3.0" +koin = "3.5.3" +koin-ksp = "1.3.1" ksp = "2.0.21-1.0.27" + dokka = "1.9.10" kover = "0.7.5" detekt = "1.23.6" compose_plugin_multiplataform = "1.7.1" navigation-compose-version = "2.7.0-alpha07" +paging-compose="3.3.5" #Test test_junit = "4.13.2" @@ -78,6 +80,7 @@ coroutines_test = { group = "org.jetbrains.kotlinx", name = "kotlinx-coroutines- #Navigation navigation-compose = { module = "org.jetbrains.androidx.navigation:navigation-compose", version.ref = "navigation-compose-version" } +paging-compose = { module = "androidx.paging:paging-compose", version.ref = "paging-compose" } # Google google_material = { group = "com.google.android.material", name = "material", version.ref = "material" } @@ -85,8 +88,9 @@ google_material = { group = "com.google.android.material", name = "material", ve # Koin koin_test = { group = "io.insert-koin", name = "koin-test-junit4", version.ref = "koin" } koin_android = { group = "io.insert-koin", name = "koin-android", version.ref = "koin" } -koin_annotations = { group = "io.insert-koin", name = "koin-annotations", version.ref = "koin-annotations-bom" } -koin_compiler = { group = "io.insert-koin", name = "koin-ksp-compiler", version.ref = "koin-annotations-bom" } +koin_core = { group = "io.insert-koin", name = "koin-core", version.ref = "koin" } +koin_annotations = { group = "io.insert-koin", name = "koin-annotations", version.ref = "koin-ksp" } +koin_compiler = { group = "io.insert-koin", name = "koin-ksp-compiler", version.ref = "koin-ksp" } koin_compose = { group = "io.insert-koin", name = "koin-androidx-compose", version.ref = "koin" } #Networking @@ -112,7 +116,8 @@ dokka = { group = "org.jetbrains.dokka", name = "android-documentation-plugin", [bundles] room = ["roomRuntime", "roomKtx"] networking = ["moshi", "okhttp", "interceptor", "ktor_client_core", "ktor_client_okhttp", "ktor_client_content_serialization_json", "ktor_client_content_negotiation", "ktor_client_logger", "ktor_client_auth"] -koin = ["koin_android", "koin_compose"] +koin = ["koin_android","koin_compose"] + test = ["junit", "mockk", "mockk_android", "viewmodel_test", "koin_test", "coroutines_test"] androidSupport = ["androidx_core", "androidx_appcompat", "androidx_dynamicanimation", "google_material"] kotlin = ["androidx_core", "kotlin_stdlib", "kotlin_reflect"] From ece7a9d57d05abd773750774fdbf0198e58aac91 Mon Sep 17 00:00:00 2001 From: Rods Date: Fri, 13 Dec 2024 19:21:52 -0300 Subject: [PATCH 11/96] =?UTF-8?q?[ISSUE-2]=20-=20=F0=9F=94=A5=20migrating?= =?UTF-8?q?=20android=20para=20KMP=20module=20list?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../navigation/NavigationGraph.kt | 3 +- .../main/java/extensions/CommonExtensions.kt | 6 +-- feature-profile/build.gradle.kts | 3 +- .../profile/di/ProfilePickerStreamModule.kt | 46 ++++++------------- .../ProfilePickerStreamNavigation.kt | 5 +- gradle/libs.versions.toml | 9 +--- 6 files changed, 25 insertions(+), 47 deletions(-) diff --git a/app/src/main/java/com/codandotv/streamplayerapp/navigation/NavigationGraph.kt b/app/src/main/java/com/codandotv/streamplayerapp/navigation/NavigationGraph.kt index b8df253a..55fcf55c 100644 --- a/app/src/main/java/com/codandotv/streamplayerapp/navigation/NavigationGraph.kt +++ b/app/src/main/java/com/codandotv/streamplayerapp/navigation/NavigationGraph.kt @@ -18,6 +18,7 @@ import com.codandotv.streamplayerapp.feature_list_streams.detail.presentation.na import com.codandotv.streamplayerapp.feature_list_streams.list.presentation.navigation.listStreamsNavGraph import com.codandotv.streamplayerapp.feature_list_streams.search.presentation.navigation.searchStreamsNavGraph import com.codandotv.streamplayerapp.feature_profile.profile.presentation.navigation.profilePickerStreamNavGraph + import com.codandotv.streamplayerapp.splah.presentation.navigation.splashNavGraph @Composable @@ -27,11 +28,11 @@ fun NavigationGraph(navController: NavHostController) { listStreamsNavGraph(navController = navController) searchStreamsNavGraph(navController = navController) detailStreamNavGraph(navController = navController) + profilePickerStreamNavGraph(navController = navController) temporaryFun(BottomNavRoutes.GAMES, navController) temporaryFun(BottomNavRoutes.NEWS, navController) temporaryFun(BottomNavRoutes.SCENES, navController) temporaryFun(BottomNavRoutes.DOWNLOADS, navController) - profilePickerStreamNavGraph(navController = navController) } } diff --git a/build-logic/src/main/java/extensions/CommonExtensions.kt b/build-logic/src/main/java/extensions/CommonExtensions.kt index fbb7328d..27b146b2 100644 --- a/build-logic/src/main/java/extensions/CommonExtensions.kt +++ b/build-logic/src/main/java/extensions/CommonExtensions.kt @@ -51,8 +51,6 @@ internal fun CommonExtension<*, *, *, *, *>.setupNameSpace(project: Project) { .replace("-", ".") namespace = "${Config.applicationId}$moduleName" -} -private fun CommonExtension<*, *, *, *, *>.kotlinOptions(block: KotlinJvmOptions.() -> Unit) { - (this as ExtensionAware).extensions.configure("kotlinOptions", block) -} + println(">>>> $namespace") +} \ No newline at end of file diff --git a/feature-profile/build.gradle.kts b/feature-profile/build.gradle.kts index 7cba8dde..7dd2669e 100644 --- a/feature-profile/build.gradle.kts +++ b/feature-profile/build.gradle.kts @@ -2,6 +2,7 @@ plugins { id("com.streamplayer.android-library") + id("kotlin-android") alias(libs.plugins.jetbrains.compose) alias(libs.plugins.compose.compiler) alias(libs.plugins.ksp) @@ -16,7 +17,7 @@ dependencies { implementation(compose.material3) implementation(compose.ui) - implementation(libs.koin.android) + implementation(libs.bundles.koin) implementation(libs.koin.annotations) ksp(libs.koin.compiler) diff --git a/feature-profile/src/main/java/com/codandotv/streamplayerapp/feature_profile/profile/di/ProfilePickerStreamModule.kt b/feature-profile/src/main/java/com/codandotv/streamplayerapp/feature_profile/profile/di/ProfilePickerStreamModule.kt index 033494fb..991e2c03 100644 --- a/feature-profile/src/main/java/com/codandotv/streamplayerapp/feature_profile/profile/di/ProfilePickerStreamModule.kt +++ b/feature-profile/src/main/java/com/codandotv/streamplayerapp/feature_profile/profile/di/ProfilePickerStreamModule.kt @@ -1,39 +1,21 @@ package com.codandotv.streamplayerapp.feature_profile.profile.di -import com.codandotv.streamplayerapp.feature_profile.profile.data.ProfilePickerStreamRepository -import com.codandotv.streamplayerapp.feature_profile.profile.data.ProfilePickerStreamRepositoryImpl import com.codandotv.streamplayerapp.feature_profile.profile.data.ProfilePickerStreamService import com.codandotv.streamplayerapp.feature_profile.profile.data.ProfilePickerStreamServiceImpl -import com.codandotv.streamplayerapp.feature_profile.profile.domain.ProfilePickerStreamUseCase -import com.codandotv.streamplayerapp.feature_profile.profile.domain.ProfilePickerStreamUseCaseImpl -import com.codandotv.streamplayerapp.feature_profile.profile.presentation.screens.ProfilePickerStreamViewModel -import org.koin.androidx.viewmodel.dsl.viewModel -import org.koin.dsl.module +import io.ktor.client.HttpClient +import org.koin.core.annotation.ComponentScan +import org.koin.core.annotation.Factory +import org.koin.core.annotation.Module +import org.koin.core.context.GlobalContext -object ProfilePickerStreamModule { - val module = module { - viewModel { - ProfilePickerStreamViewModel( - useCase = get() - ) - } +@Module +@ComponentScan("com.codandotv.streamplayerapp.feature_profile") +class ProfilePickerStreamModule { - factory { - ProfilePickerStreamUseCaseImpl( - profilePickerStreamRepository = get() - ) - } - - factory { - ProfilePickerStreamRepositoryImpl( - service = get() - ) - } - - factory { - ProfilePickerStreamServiceImpl( - client = get() - ) - } + @Factory + fun service(): ProfilePickerStreamService { + val koin = GlobalContext.get() + val client = koin.get() + return ProfilePickerStreamServiceImpl(client) } -} \ No newline at end of file +} diff --git a/feature-profile/src/main/java/com/codandotv/streamplayerapp/feature_profile/profile/presentation/navigation/ProfilePickerStreamNavigation.kt b/feature-profile/src/main/java/com/codandotv/streamplayerapp/feature_profile/profile/presentation/navigation/ProfilePickerStreamNavigation.kt index c03c4a27..3624f939 100644 --- a/feature-profile/src/main/java/com/codandotv/streamplayerapp/feature_profile/profile/presentation/navigation/ProfilePickerStreamNavigation.kt +++ b/feature-profile/src/main/java/com/codandotv/streamplayerapp/feature_profile/profile/presentation/navigation/ProfilePickerStreamNavigation.kt @@ -11,18 +11,19 @@ import com.codandotv.streamplayerapp.feature_profile.profile.di.ProfilePickerStr import com.codandotv.streamplayerapp.feature_profile.profile.presentation.screens.ProfilePickerStreamScreen import org.koin.core.context.loadKoinModules import org.koin.core.context.unloadKoinModules +import org.koin.ksp.generated.module fun NavGraphBuilder.profilePickerStreamNavGraph(navController: NavHostController) { composable(Routes.PROFILE_PICKER) { nav -> if (nav.lifecycle.currentState == Lifecycle.State.STARTED) { - loadKoinModules(ProfilePickerStreamModule.module) + loadKoinModules(ProfilePickerStreamModule().module) } ProfilePickerStreamScreen( onNavigateListStreams = { profilePic -> navController.navigate("$HOME?$PROFILE_ID=$profilePic") } ) { - unloadKoinModules(ProfilePickerStreamModule.module) + unloadKoinModules(ProfilePickerStreamModule().module) } } } diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 958ad604..0c4d7112 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -18,13 +18,6 @@ androidx_core_testing = "2.2.0" mockk = "1.13.7" kotlinx-coroutines-test = "1.8.1" -#Android Test -androidx_test_core = "1.6.1" -androidx_test_rules = "1.6.1" -androidx_test_runner = "1.6.2" -androidx_test_junit_ext = "1.2.1" -mockWebServer = "4.10.0" - #Android Support android_core_ktx = "1.7.0" androidx_appcompat = "1.7.0" @@ -45,6 +38,7 @@ lottie = "5.2.0" #Room Database room = "2.5.2" android_youtube_player_version = "12.0.0" +uiAndroid = "1.7.5" [libraries] @@ -81,6 +75,7 @@ coroutines_test = { group = "org.jetbrains.kotlinx", name = "kotlinx-coroutines- #Navigation navigation-compose = { module = "org.jetbrains.androidx.navigation:navigation-compose", version.ref = "navigation-compose-version" } paging-compose = { module = "androidx.paging:paging-compose", version.ref = "paging-compose" } +ui-android = { group = "androidx.compose.ui", name = "ui-android", version.ref = "uiAndroid" } # Google google_material = { group = "com.google.android.material", name = "material", version.ref = "material" } From f3d19ca407b173ebc9b363ede87f07483efb2c5e Mon Sep 17 00:00:00 2001 From: Rods Date: Fri, 13 Dec 2024 19:32:04 -0300 Subject: [PATCH 12/96] =?UTF-8?q?[ISSUE-2]=20-=20=F0=9F=94=A5=20migrating?= =?UTF-8?q?=20android=20para=20KMP=20module=20list?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- core-shared-ui/build.gradle.kts | 6 ++++-- .../core_shared_ui/widget/SharingStreamCustomView.kt | 11 ++++++----- gradle/libs.versions.toml | 1 - 3 files changed, 10 insertions(+), 8 deletions(-) diff --git a/core-shared-ui/build.gradle.kts b/core-shared-ui/build.gradle.kts index 1450afc3..f6d8ba48 100644 --- a/core-shared-ui/build.gradle.kts +++ b/core-shared-ui/build.gradle.kts @@ -7,13 +7,15 @@ plugins { } dependencies { + implementation(projects.coreShared) implementation(compose.material3) implementation(compose.preview) - implementation(projects.coreShared) + implementation(compose.ui) + implementation(libs.navigation.compose) implementation(libs.bundles.koin) implementation(libs.bundles.kotlin) implementation(libs.bundles.androidSupport) implementation(libs.android.youtube.player) - testImplementation(libs.bundles.test) implementation(libs.coil) + testImplementation(libs.bundles.test) } \ No newline at end of file diff --git a/core-shared-ui/src/main/java/com/codandotv/streamplayerapp/core_shared_ui/widget/SharingStreamCustomView.kt b/core-shared-ui/src/main/java/com/codandotv/streamplayerapp/core_shared_ui/widget/SharingStreamCustomView.kt index e1f1e6af..b3063630 100644 --- a/core-shared-ui/src/main/java/com/codandotv/streamplayerapp/core_shared_ui/widget/SharingStreamCustomView.kt +++ b/core-shared-ui/src/main/java/com/codandotv/streamplayerapp/core_shared_ui/widget/SharingStreamCustomView.kt @@ -8,6 +8,7 @@ import android.content.Context import android.content.Intent import android.net.Uri import android.widget.Toast +import androidx.activity.compose.BackHandler import androidx.compose.animation.AnimatedVisibility import androidx.compose.animation.AnimatedVisibilityScope import androidx.compose.animation.core.keyframes @@ -292,11 +293,11 @@ fun SharingStreamCustomView( } } } -// BackHandler { -// coroutineScope.launch { -// startDismissWithExitAnimation(animateTrigger) { setShowDialog(false) } -// } -// } + BackHandler { + coroutineScope.launch { + startDismissWithExitAnimation(animateTrigger) { setShowDialog(false) } + } + } } } diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 0c4d7112..63de171e 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -75,7 +75,6 @@ coroutines_test = { group = "org.jetbrains.kotlinx", name = "kotlinx-coroutines- #Navigation navigation-compose = { module = "org.jetbrains.androidx.navigation:navigation-compose", version.ref = "navigation-compose-version" } paging-compose = { module = "androidx.paging:paging-compose", version.ref = "paging-compose" } -ui-android = { group = "androidx.compose.ui", name = "ui-android", version.ref = "uiAndroid" } # Google google_material = { group = "com.google.android.material", name = "material", version.ref = "material" } From 84ce65aa16c3dcab00ed64a0612ef12bed469e59 Mon Sep 17 00:00:00 2001 From: Rods Date: Tue, 17 Dec 2024 20:53:35 -0300 Subject: [PATCH 13/96] =?UTF-8?q?[ISSUE-2]=20-=20=F0=9F=94=A5=20migrating?= =?UTF-8?q?=20android=20para=20KMP=20module=20list?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build-logic/build.gradle.kts | 5 +- ...om.streamplayer.android-library.gradle.kts | 1 + .../com.streamplayer.kmp-library.gradle.kts | 59 +++++++++++++++++++ .../main/java/extensions/CommonExtensions.kt | 3 - core-local-storage/build.gradle.kts | 1 - core-networking/build.gradle.kts | 1 - core-shared-ui/build.gradle.kts | 1 - core-shared/build.gradle.kts | 1 - feature-list-streams/build.gradle.kts | 13 +--- feature-profile/build.gradle.kts | 1 - gradle/libs.versions.toml | 1 + 11 files changed, 66 insertions(+), 21 deletions(-) create mode 100644 build-logic/src/main/java/com.streamplayer.kmp-library.gradle.kts diff --git a/build-logic/build.gradle.kts b/build-logic/build.gradle.kts index b47ba3a7..8377f141 100644 --- a/build-logic/build.gradle.kts +++ b/build-logic/build.gradle.kts @@ -6,8 +6,10 @@ plugins { } repositories { - mavenCentral() google() + mavenCentral() + gradlePluginPortal() + maven(url = "https://plugins.gradle.org/m2/") } dependencies { @@ -16,4 +18,5 @@ dependencies { implementation(libs.kover.gradle.plugin) implementation(libs.detekt.gradle.plugin) implementation(libs.serialization) + implementation(libs.com.google.devtools.ksp.gradle.plugin) } \ No newline at end of file diff --git a/build-logic/src/main/java/com.streamplayer.android-library.gradle.kts b/build-logic/src/main/java/com.streamplayer.android-library.gradle.kts index 408de325..85c5eb3a 100644 --- a/build-logic/src/main/java/com.streamplayer.android-library.gradle.kts +++ b/build-logic/src/main/java/com.streamplayer.android-library.gradle.kts @@ -14,6 +14,7 @@ plugins { id("org.jetbrains.kotlin.plugin.serialization") id("com.android.library") id("kotlin-kapt") + id("kotlin-android") id("kotlin-parcelize") id("com.streamplayer.dokka") id("org.jetbrains.kotlinx.kover") diff --git a/build-logic/src/main/java/com.streamplayer.kmp-library.gradle.kts b/build-logic/src/main/java/com.streamplayer.kmp-library.gradle.kts new file mode 100644 index 00000000..d865de17 --- /dev/null +++ b/build-logic/src/main/java/com.streamplayer.kmp-library.gradle.kts @@ -0,0 +1,59 @@ +@file:Suppress("UnstableApiUsage") + +import extensions.dokkaPlugin +import extensions.getLibrary +import extensions.setupAndroidDefaultConfig +import extensions.setupCompileOptions +import extensions.setupNameSpace +import extensions.setupPackingOptions +import org.jetbrains.kotlin.gradle.ExperimentalKotlinGradlePluginApi +import org.jetbrains.kotlin.gradle.dsl.JvmTarget + + +val libs: VersionCatalog = extensions.getByType().named("libs") +plugins { + id("org.jetbrains.kotlin.multiplatform") + id("com.android.library") + id("org.jetbrains.kotlin.plugin.serialization") + id("kotlin-kapt") + id("kotlin-parcelize") + id("com.streamplayer.dokka") + id("org.jetbrains.kotlinx.kover") + id("com.streamplayer.detekt") +} + +kotlin { + androidTarget { + @OptIn(ExperimentalKotlinGradlePluginApi::class) + compilerOptions { + jvmTarget.set(JvmTarget.JVM_17) + } + } +} + +android { + setupNameSpace(project) + + setupCompileOptions() + + setupPackingOptions() + + setupAndroidDefaultConfig() + defaultConfig.targetSdk = Config.targetSdkVersion + + buildTypes { + getByName("release") { + isMinifyEnabled = true + proguardFiles("proguard-android.txt", "proguard-rules.pro") + consumerProguardFiles("proguard-rules.pro") + } + + getByName("debug") { + isMinifyEnabled = false + } + } +} + +dependencies { + dokkaPlugin(libs.getLibrary("dokka")) +} diff --git a/build-logic/src/main/java/extensions/CommonExtensions.kt b/build-logic/src/main/java/extensions/CommonExtensions.kt index 27b146b2..2f4a525b 100644 --- a/build-logic/src/main/java/extensions/CommonExtensions.kt +++ b/build-logic/src/main/java/extensions/CommonExtensions.kt @@ -6,9 +6,6 @@ import Config import com.android.build.api.dsl.CommonExtension import org.gradle.api.JavaVersion import org.gradle.api.Project -import org.gradle.api.artifacts.VersionCatalog -import org.gradle.api.plugins.ExtensionAware -import org.jetbrains.kotlin.gradle.dsl.KotlinJvmOptions internal fun CommonExtension<*, *, *, *, *>.setupPackingOptions() { packaging { diff --git a/core-local-storage/build.gradle.kts b/core-local-storage/build.gradle.kts index 6fc9f020..c8e2ff05 100644 --- a/core-local-storage/build.gradle.kts +++ b/core-local-storage/build.gradle.kts @@ -1,7 +1,6 @@ plugins { id("com.streamplayer.android-library") id("com.google.devtools.ksp") - id("kotlin-android") } dependencies { diff --git a/core-networking/build.gradle.kts b/core-networking/build.gradle.kts index fd4f7b53..f5602562 100644 --- a/core-networking/build.gradle.kts +++ b/core-networking/build.gradle.kts @@ -1,6 +1,5 @@ plugins { id("com.streamplayer.android-library") - id("kotlin-android") } android { buildFeatures { diff --git a/core-shared-ui/build.gradle.kts b/core-shared-ui/build.gradle.kts index f6d8ba48..34351e22 100644 --- a/core-shared-ui/build.gradle.kts +++ b/core-shared-ui/build.gradle.kts @@ -3,7 +3,6 @@ plugins { id("com.streamplayer.android-library") alias(libs.plugins.jetbrains.compose) alias(libs.plugins.compose.compiler) - id("kotlin-android") } dependencies { diff --git a/core-shared/build.gradle.kts b/core-shared/build.gradle.kts index 304bcb93..f1b45dda 100644 --- a/core-shared/build.gradle.kts +++ b/core-shared/build.gradle.kts @@ -1,6 +1,5 @@ plugins { id("com.streamplayer.android-library") - id("kotlin-android") } dependencies { diff --git a/feature-list-streams/build.gradle.kts b/feature-list-streams/build.gradle.kts index 85620e3c..9dfc8ab3 100644 --- a/feature-list-streams/build.gradle.kts +++ b/feature-list-streams/build.gradle.kts @@ -1,24 +1,13 @@ @file:Suppress("UnstableApiUsage") -import org.jetbrains.kotlin.gradle.ExperimentalKotlinGradlePluginApi -import org.jetbrains.kotlin.gradle.dsl.JvmTarget - plugins { - id("com.streamplayer.android-library") + id("com.streamplayer.kmp-library") alias(libs.plugins.ksp) - alias(libs.plugins.kotlin.multiplatform) alias(libs.plugins.jetbrains.compose) alias(libs.plugins.compose.compiler) } kotlin { - androidTarget { - @OptIn(ExperimentalKotlinGradlePluginApi::class) - compilerOptions { - jvmTarget.set(JvmTarget.JVM_17) - } - } - sourceSets { commonMain.dependencies { implementation(libs.koin.annotations) diff --git a/feature-profile/build.gradle.kts b/feature-profile/build.gradle.kts index 7dd2669e..7f44787a 100644 --- a/feature-profile/build.gradle.kts +++ b/feature-profile/build.gradle.kts @@ -2,7 +2,6 @@ plugins { id("com.streamplayer.android-library") - id("kotlin-android") alias(libs.plugins.jetbrains.compose) alias(libs.plugins.compose.compiler) alias(libs.plugins.ksp) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 63de171e..4ab1e2b9 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -46,6 +46,7 @@ kotlin_gradle_plugin = { group = "org.jetbrains.kotlin", name = "kotlin-gradle-p android_gradle_plugin = { group = "com.android.tools.build", name = "gradle", version.ref = "android_gradle_plugin" } detekt-gradle-plugin = { module = "io.gitlab.arturbosch.detekt:detekt-gradle-plugin", version.ref = "detekt" } serialization = { module = "org.jetbrains.kotlin.plugin.serialization:org.jetbrains.kotlin.plugin.serialization.gradle.plugin", version.ref = "kotlin" } +com-google-devtools-ksp-gradle-plugin = { module = "com.google.devtools.ksp:com.google.devtools.ksp.gradle.plugin", version.ref = "ksp" } #Kover kover-gradle-plugin = { module = "org.jetbrains.kotlinx:kover-gradle-plugin", version.ref = "kover" } From 8d38d07a607f1e4d2e2f9d6ba689613110056e89 Mon Sep 17 00:00:00 2001 From: Rods Date: Tue, 17 Dec 2024 21:20:56 -0300 Subject: [PATCH 14/96] =?UTF-8?q?[ISSUE-2]=20-=20=F0=9F=94=A5=20migrating?= =?UTF-8?q?=20detail=20to=20kmp=20module?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/build.gradle.kts | 1 + .../navigation/NavigationGraph.kt | 2 +- feature-detail/.gitignore | 1 + feature-detail/build.gradle.kts | 50 +++++++++++++++++++ feature-detail/consumer-rules.pro | 0 feature-detail/proguard-rules.pro | 21 ++++++++ .../src/androidMain/res/values/strings.xml | 12 +++++ .../detail/DetailStreamRepositoryTest.kt | 12 ++--- .../detail/DetailStreamUseCaseTest.kt | 14 +++--- .../detail/DetailStreamViewModelTest.kt | 20 ++++---- .../feature_list_streams/detail/Shared.kt | 14 +++--- gradle/libs.versions.toml | 4 ++ settings.gradle.kts | 3 +- 13 files changed, 122 insertions(+), 32 deletions(-) create mode 100644 feature-detail/.gitignore create mode 100644 feature-detail/build.gradle.kts create mode 100644 feature-detail/consumer-rules.pro create mode 100644 feature-detail/proguard-rules.pro create mode 100644 feature-detail/src/androidMain/res/values/strings.xml diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 32e66691..6f5990db 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -9,6 +9,7 @@ plugins { dependencies { implementation(projects.featureFavorites) implementation(projects.featureListStreams) + implementation(projects.featureDetail) implementation(projects.featureProfile) implementation(projects.coreShared) implementation(projects.coreSharedUi) diff --git a/app/src/main/java/com/codandotv/streamplayerapp/navigation/NavigationGraph.kt b/app/src/main/java/com/codandotv/streamplayerapp/navigation/NavigationGraph.kt index 55fcf55c..81570e38 100644 --- a/app/src/main/java/com/codandotv/streamplayerapp/navigation/NavigationGraph.kt +++ b/app/src/main/java/com/codandotv/streamplayerapp/navigation/NavigationGraph.kt @@ -14,7 +14,7 @@ import androidx.navigation.compose.composable import com.codandotv.streamplayerapp.core_navigation.bottomnavigation.StreamPlayerBottomNavigation import com.codandotv.streamplayerapp.core_navigation.routes.BottomNavRoutes import com.codandotv.streamplayerapp.core_navigation.routes.Routes -import com.codandotv.streamplayerapp.feature_list_streams.detail.presentation.navigation.detailStreamNavGraph +import detail.presentation.navigation.detailStreamNavGraph import com.codandotv.streamplayerapp.feature_list_streams.list.presentation.navigation.listStreamsNavGraph import com.codandotv.streamplayerapp.feature_list_streams.search.presentation.navigation.searchStreamsNavGraph import com.codandotv.streamplayerapp.feature_profile.profile.presentation.navigation.profilePickerStreamNavGraph diff --git a/feature-detail/.gitignore b/feature-detail/.gitignore new file mode 100644 index 00000000..42afabfd --- /dev/null +++ b/feature-detail/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/feature-detail/build.gradle.kts b/feature-detail/build.gradle.kts new file mode 100644 index 00000000..9dfc8ab3 --- /dev/null +++ b/feature-detail/build.gradle.kts @@ -0,0 +1,50 @@ +@file:Suppress("UnstableApiUsage") + +plugins { + id("com.streamplayer.kmp-library") + alias(libs.plugins.ksp) + alias(libs.plugins.jetbrains.compose) + alias(libs.plugins.compose.compiler) +} + +kotlin { + sourceSets { + commonMain.dependencies { + implementation(libs.koin.annotations) + implementation(libs.koin.core) + } + + androidMain.dependencies { + implementation(projects.coreNetworking) + implementation(projects.coreNavigation) + implementation(projects.coreShared) + implementation(projects.coreSharedUi) + implementation(projects.coreLocalStorage) + + implementation(compose.material3) + implementation(compose.ui) + implementation(compose.preview) + implementation(libs.paging.compose) + implementation(libs.navigation.compose) + implementation(libs.bundles.koin) + implementation(libs.bundles.networking) + implementation(libs.coil) + implementation(libs.koin.annotations) + implementation(libs.bundles.androidSupport) + + } + + sourceSets.named("androidMain").configure { + kotlin.srcDir("build/generated/ksp/metadata/androidMain/kotlin") + } + } +} + +dependencies { + add("kspCommonMainMetadata",libs.koin.compiler) + add("kspAndroid", libs.koin.compiler) +} + +ksp { + arg("KOIN_CONFIG_CHECK","true") +} \ No newline at end of file diff --git a/feature-detail/consumer-rules.pro b/feature-detail/consumer-rules.pro new file mode 100644 index 00000000..e69de29b diff --git a/feature-detail/proguard-rules.pro b/feature-detail/proguard-rules.pro new file mode 100644 index 00000000..481bb434 --- /dev/null +++ b/feature-detail/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/feature-detail/src/androidMain/res/values/strings.xml b/feature-detail/src/androidMain/res/values/strings.xml new file mode 100644 index 00000000..ac85d916 --- /dev/null +++ b/feature-detail/src/androidMain/res/values/strings.xml @@ -0,0 +1,12 @@ + + + Assistir + Baixar E1 + Filme + Minha Lista + Classificar + Compartilhar + Baixar completo + Pesquisar + Voltar + \ No newline at end of file diff --git a/feature-list-streams/src/test/java/com/codandotv/streamplayerapp/feature_list_streams/detail/DetailStreamRepositoryTest.kt b/feature-list-streams/src/test/java/com/codandotv/streamplayerapp/feature_list_streams/detail/DetailStreamRepositoryTest.kt index f2f00d88..e2a79adc 100644 --- a/feature-list-streams/src/test/java/com/codandotv/streamplayerapp/feature_list_streams/detail/DetailStreamRepositoryTest.kt +++ b/feature-list-streams/src/test/java/com/codandotv/streamplayerapp/feature_list_streams/detail/DetailStreamRepositoryTest.kt @@ -2,9 +2,9 @@ package com.codandotv.streamplayerapp.feature_list_streams.detail import com.codandotv.streamplayerapp.core_local_storage.data.dao.FavoriteDao import com.codandotv.streamplayerapp.core_networking.handleError.NetworkResponse -import com.codandotv.streamplayerapp.feature_list_streams.detail.data.DetailStreamRepository -import com.codandotv.streamplayerapp.feature_list_streams.detail.data.DetailStreamRepositoryImpl -import com.codandotv.streamplayerapp.feature_list_streams.detail.data.DetailStreamService +import detail.data.DetailStreamRepository +import detail.data.DetailStreamRepositoryImpl +import detail.data.DetailStreamService import io.mockk.coEvery import io.mockk.coVerifyOrder import io.mockk.mockk @@ -14,16 +14,16 @@ import org.junit.Before import org.junit.Test class DetailStreamRepositoryTest { - private lateinit var repository: DetailStreamRepository + private lateinit var repository: detail.data.DetailStreamRepository private val movieId = MOVIE_ID_STRING - private lateinit var service: DetailStreamService + private lateinit var service: detail.data.DetailStreamService private lateinit var favoriteDao: FavoriteDao @Before fun setUp() { service = mockk() favoriteDao = mockk() - repository = DetailStreamRepositoryImpl( + repository = detail.data.DetailStreamRepositoryImpl( movieId = movieId, service = service, favoriteDao = favoriteDao diff --git a/feature-list-streams/src/test/java/com/codandotv/streamplayerapp/feature_list_streams/detail/DetailStreamUseCaseTest.kt b/feature-list-streams/src/test/java/com/codandotv/streamplayerapp/feature_list_streams/detail/DetailStreamUseCaseTest.kt index f08442b4..ee9f0a0d 100644 --- a/feature-list-streams/src/test/java/com/codandotv/streamplayerapp/feature_list_streams/detail/DetailStreamUseCaseTest.kt +++ b/feature-list-streams/src/test/java/com/codandotv/streamplayerapp/feature_list_streams/detail/DetailStreamUseCaseTest.kt @@ -1,9 +1,9 @@ package com.codandotv.streamplayerapp.feature_list_streams.detail -import com.codandotv.streamplayerapp.feature_list_streams.detail.data.DetailStreamRepository -import com.codandotv.streamplayerapp.feature_list_streams.detail.domain.DetailStream -import com.codandotv.streamplayerapp.feature_list_streams.detail.domain.DetailStreamUseCase -import com.codandotv.streamplayerapp.feature_list_streams.detail.domain.DetailStreamUseCaseImpl +import detail.data.DetailStreamRepository +import detail.domain.DetailStream +import detail.domain.DetailStreamUseCase +import detail.domain.DetailStreamUseCaseImpl import io.mockk.coEvery import io.mockk.coVerify import io.mockk.mockk @@ -16,13 +16,13 @@ import org.junit.Test import kotlin.test.assertTrue class DetailStreamUseCaseTest { - private lateinit var detailStreamUseCase: DetailStreamUseCase - private lateinit var detailStreamRepository: DetailStreamRepository + private lateinit var detailStreamUseCase: detail.domain.DetailStreamUseCase + private lateinit var detailStreamRepository: detail.data.DetailStreamRepository @Before fun setUp() { detailStreamRepository = mockk() - detailStreamUseCase = DetailStreamUseCaseImpl( + detailStreamUseCase = detail.domain.DetailStreamUseCaseImpl( detailStreamRepository = detailStreamRepository ) } diff --git a/feature-list-streams/src/test/java/com/codandotv/streamplayerapp/feature_list_streams/detail/DetailStreamViewModelTest.kt b/feature-list-streams/src/test/java/com/codandotv/streamplayerapp/feature_list_streams/detail/DetailStreamViewModelTest.kt index 60a47271..79699b5b 100644 --- a/feature-list-streams/src/test/java/com/codandotv/streamplayerapp/feature_list_streams/detail/DetailStreamViewModelTest.kt +++ b/feature-list-streams/src/test/java/com/codandotv/streamplayerapp/feature_list_streams/detail/DetailStreamViewModelTest.kt @@ -2,10 +2,10 @@ package com.codandotv.streamplayerapp.feature_list_streams.detail import androidx.arch.core.executor.testing.InstantTaskExecutorRule import androidx.lifecycle.LifecycleOwner -import com.codandotv.streamplayerapp.feature_list_streams.detail.domain.DetailStreamUseCase -import com.codandotv.streamplayerapp.feature_list_streams.detail.domain.VideoStreamsUseCase -import com.codandotv.streamplayerapp.feature_list_streams.detail.presentation.screens.DetailStreamViewModel -import com.codandotv.streamplayerapp.feature_list_streams.detail.presentation.screens.DetailStreamsUIState +import detail.domain.DetailStreamUseCase +import detail.domain.VideoStreamsUseCase +import detail.presentation.screens.DetailStreamViewModel +import detail.presentation.screens.DetailStreamsUIState import io.mockk.coEvery import io.mockk.coVerify import io.mockk.mockk @@ -17,9 +17,9 @@ import org.junit.Test import kotlin.test.assertTrue class DetailStreamViewModelTest { - private lateinit var detailStreamViewModel: DetailStreamViewModel - private lateinit var detailUseCase: DetailStreamUseCase - private lateinit var videoUseCase: VideoStreamsUseCase + private lateinit var detailStreamViewModel: detail.presentation.screens.DetailStreamViewModel + private lateinit var detailUseCase: detail.domain.DetailStreamUseCase + private lateinit var videoUseCase: detail.domain.VideoStreamsUseCase @get:Rule val rule = InstantTaskExecutorRule() @@ -32,7 +32,7 @@ class DetailStreamViewModelTest { detailUseCase = mockk() videoUseCase = mockk() - detailStreamViewModel = DetailStreamViewModel( + detailStreamViewModel = detail.presentation.screens.DetailStreamViewModel( detailStreamUseCase = detailUseCase, videoStreamsUseCase = videoUseCase, dispatcher = executorRule.dispatcher @@ -49,12 +49,12 @@ class DetailStreamViewModelTest { coVerify { detailStreamViewModel.uiState.value.let { - DetailStreamsUIState.LoadingStreamUIState + detail.presentation.screens.DetailStreamsUIState.LoadingStreamUIState } detailUseCase.getMovie() detailStreamViewModel.uiState.value.let { assertTrue { - it == DetailStreamsUIState.DetailStreamsLoadedUIState( + it == detail.presentation.screens.DetailStreamsUIState.DetailStreamsLoadedUIState( detailStream = detailStream, videoId = videosStreamsList.first().videoId ) } diff --git a/feature-list-streams/src/test/java/com/codandotv/streamplayerapp/feature_list_streams/detail/Shared.kt b/feature-list-streams/src/test/java/com/codandotv/streamplayerapp/feature_list_streams/detail/Shared.kt index 3adda93e..d9958a6f 100644 --- a/feature-list-streams/src/test/java/com/codandotv/streamplayerapp/feature_list_streams/detail/Shared.kt +++ b/feature-list-streams/src/test/java/com/codandotv/streamplayerapp/feature_list_streams/detail/Shared.kt @@ -1,17 +1,17 @@ package com.codandotv.streamplayerapp.feature_list_streams.detail -import com.codandotv.streamplayerapp.feature_list_streams.detail.data.model.DetailStreamResponse -import com.codandotv.streamplayerapp.feature_list_streams.detail.domain.DetailStream -import com.codandotv.streamplayerapp.feature_list_streams.detail.domain.VideoStream +import detail.data.model.DetailStreamResponse +import detail.domain.DetailStream +import detail.domain.VideoStream -val videoStream = VideoStream( +val videoStream = detail.domain.VideoStream( movieId = 123, videoId = "123" ) const val MOVIE_ID_STRING = "123" -val videoStream1 = VideoStream( +val videoStream1 = detail.domain.VideoStream( movieId = 1234565, videoId = "123565" ) @@ -21,7 +21,7 @@ val videosStreamsList = listOf( videoStream1 ) -val detailStreamResponse = DetailStreamResponse( +val detailStreamResponse = detail.data.model.DetailStreamResponse( id = "id", title = "title", overview = "overview", @@ -30,7 +30,7 @@ val detailStreamResponse = DetailStreamResponse( release_date = "release" ) -val detailStream = DetailStream( +val detailStream = detail.domain.DetailStream( id = "id", title = "title", overview = "overview", diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 4ab1e2b9..96b0f53a 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -39,6 +39,8 @@ lottie = "5.2.0" room = "2.5.2" android_youtube_player_version = "12.0.0" uiAndroid = "1.7.5" +junit = "1.2.1" +espressoCore = "3.6.1" [libraries] @@ -107,6 +109,8 @@ roomKtx = { group = "androidx.room", name = "room-ktx", version.ref = "room" } android_youtube_player = { group = "com.pierfrancescosoffritti.androidyoutubeplayer", name = "core", version.ref = "android_youtube_player_version" } dokka = { group = "org.jetbrains.dokka", name = "android-documentation-plugin", version.ref = "dokka" } +ext-junit = { group = "androidx.test.ext", name = "junit", version.ref = "junit" } +espresso-core = { group = "androidx.test.espresso", name = "espresso-core", version.ref = "espressoCore" } [bundles] room = ["roomRuntime", "roomKtx"] diff --git a/settings.gradle.kts b/settings.gradle.kts index 0aeaa485..f33ec449 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -30,4 +30,5 @@ include(":feature-profile") include(":core-local-storage") include(":feature-favorites") -enableFeaturePreview("TYPESAFE_PROJECT_ACCESSORS") \ No newline at end of file +enableFeaturePreview("TYPESAFE_PROJECT_ACCESSORS") +include(":feature-detail") From 9e7e880c0e7d836389fc0b3b4f61bcf2609f9f89 Mon Sep 17 00:00:00 2001 From: Rods Date: Tue, 17 Dec 2024 21:22:38 -0300 Subject: [PATCH 15/96] =?UTF-8?q?[ISSUE-2]=20-=20=F0=9F=94=A5=20migrating?= =?UTF-8?q?=20detail=20to=20kmp=20module?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../feature_detail}/DetailStreamRepositoryTest.kt | 5 +---- .../feature_detail}/DetailStreamUseCaseTest.kt | 8 +------- .../feature_detail}/DetailStreamViewModelTest.kt | 7 +------ .../codandotv/streamplayerapp/feature_detail}/Shared.kt | 8 +++----- 4 files changed, 6 insertions(+), 22 deletions(-) rename {feature-list-streams/src/test/java/com/codandotv/streamplayerapp/feature_list_streams/detail => feature-detail/src/test/java/com/codandotv/streamplayerapp/feature_detail}/DetailStreamRepositoryTest.kt (88%) rename {feature-list-streams/src/test/java/com/codandotv/streamplayerapp/feature_list_streams/detail => feature-detail/src/test/java/com/codandotv/streamplayerapp/feature_detail}/DetailStreamUseCaseTest.kt (77%) rename {feature-list-streams/src/test/java/com/codandotv/streamplayerapp/feature_list_streams/detail => feature-detail/src/test/java/com/codandotv/streamplayerapp/feature_detail}/DetailStreamViewModelTest.kt (86%) rename {feature-list-streams/src/test/java/com/codandotv/streamplayerapp/feature_list_streams/detail => feature-detail/src/test/java/com/codandotv/streamplayerapp/feature_detail}/Shared.kt (76%) diff --git a/feature-list-streams/src/test/java/com/codandotv/streamplayerapp/feature_list_streams/detail/DetailStreamRepositoryTest.kt b/feature-detail/src/test/java/com/codandotv/streamplayerapp/feature_detail/DetailStreamRepositoryTest.kt similarity index 88% rename from feature-list-streams/src/test/java/com/codandotv/streamplayerapp/feature_list_streams/detail/DetailStreamRepositoryTest.kt rename to feature-detail/src/test/java/com/codandotv/streamplayerapp/feature_detail/DetailStreamRepositoryTest.kt index e2a79adc..228d20d5 100644 --- a/feature-list-streams/src/test/java/com/codandotv/streamplayerapp/feature_list_streams/detail/DetailStreamRepositoryTest.kt +++ b/feature-detail/src/test/java/com/codandotv/streamplayerapp/feature_detail/DetailStreamRepositoryTest.kt @@ -1,10 +1,7 @@ -package com.codandotv.streamplayerapp.feature_list_streams.detail +package com.codandotv.streamplayerapp.feature_detail import com.codandotv.streamplayerapp.core_local_storage.data.dao.FavoriteDao import com.codandotv.streamplayerapp.core_networking.handleError.NetworkResponse -import detail.data.DetailStreamRepository -import detail.data.DetailStreamRepositoryImpl -import detail.data.DetailStreamService import io.mockk.coEvery import io.mockk.coVerifyOrder import io.mockk.mockk diff --git a/feature-list-streams/src/test/java/com/codandotv/streamplayerapp/feature_list_streams/detail/DetailStreamUseCaseTest.kt b/feature-detail/src/test/java/com/codandotv/streamplayerapp/feature_detail/DetailStreamUseCaseTest.kt similarity index 77% rename from feature-list-streams/src/test/java/com/codandotv/streamplayerapp/feature_list_streams/detail/DetailStreamUseCaseTest.kt rename to feature-detail/src/test/java/com/codandotv/streamplayerapp/feature_detail/DetailStreamUseCaseTest.kt index ee9f0a0d..34719236 100644 --- a/feature-list-streams/src/test/java/com/codandotv/streamplayerapp/feature_list_streams/detail/DetailStreamUseCaseTest.kt +++ b/feature-detail/src/test/java/com/codandotv/streamplayerapp/feature_detail/DetailStreamUseCaseTest.kt @@ -1,15 +1,9 @@ -package com.codandotv.streamplayerapp.feature_list_streams.detail +package com.codandotv.streamplayerapp.feature_detail -import detail.data.DetailStreamRepository -import detail.domain.DetailStream -import detail.domain.DetailStreamUseCase -import detail.domain.DetailStreamUseCaseImpl import io.mockk.coEvery import io.mockk.coVerify import io.mockk.mockk -import kotlinx.coroutines.flow.collect import kotlinx.coroutines.flow.flowOf -import kotlinx.coroutines.runBlocking import kotlinx.coroutines.test.runTest import org.junit.Before import org.junit.Test diff --git a/feature-list-streams/src/test/java/com/codandotv/streamplayerapp/feature_list_streams/detail/DetailStreamViewModelTest.kt b/feature-detail/src/test/java/com/codandotv/streamplayerapp/feature_detail/DetailStreamViewModelTest.kt similarity index 86% rename from feature-list-streams/src/test/java/com/codandotv/streamplayerapp/feature_list_streams/detail/DetailStreamViewModelTest.kt rename to feature-detail/src/test/java/com/codandotv/streamplayerapp/feature_detail/DetailStreamViewModelTest.kt index 79699b5b..f96446ed 100644 --- a/feature-list-streams/src/test/java/com/codandotv/streamplayerapp/feature_list_streams/detail/DetailStreamViewModelTest.kt +++ b/feature-detail/src/test/java/com/codandotv/streamplayerapp/feature_detail/DetailStreamViewModelTest.kt @@ -1,11 +1,6 @@ -package com.codandotv.streamplayerapp.feature_list_streams.detail +package com.codandotv.streamplayerapp.feature_detail import androidx.arch.core.executor.testing.InstantTaskExecutorRule -import androidx.lifecycle.LifecycleOwner -import detail.domain.DetailStreamUseCase -import detail.domain.VideoStreamsUseCase -import detail.presentation.screens.DetailStreamViewModel -import detail.presentation.screens.DetailStreamsUIState import io.mockk.coEvery import io.mockk.coVerify import io.mockk.mockk diff --git a/feature-list-streams/src/test/java/com/codandotv/streamplayerapp/feature_list_streams/detail/Shared.kt b/feature-detail/src/test/java/com/codandotv/streamplayerapp/feature_detail/Shared.kt similarity index 76% rename from feature-list-streams/src/test/java/com/codandotv/streamplayerapp/feature_list_streams/detail/Shared.kt rename to feature-detail/src/test/java/com/codandotv/streamplayerapp/feature_detail/Shared.kt index d9958a6f..235214ae 100644 --- a/feature-list-streams/src/test/java/com/codandotv/streamplayerapp/feature_list_streams/detail/Shared.kt +++ b/feature-detail/src/test/java/com/codandotv/streamplayerapp/feature_detail/Shared.kt @@ -1,8 +1,6 @@ -package com.codandotv.streamplayerapp.feature_list_streams.detail +package com.codandotv.streamplayerapp.feature_detail import detail.data.model.DetailStreamResponse -import detail.domain.DetailStream -import detail.domain.VideoStream val videoStream = detail.domain.VideoStream( movieId = 123, @@ -21,8 +19,8 @@ val videosStreamsList = listOf( videoStream1 ) -val detailStreamResponse = detail.data.model.DetailStreamResponse( - id = "id", +val detailStreamResponse = DetailStreamResponse( + id = 12, title = "title", overview = "overview", tagline = "tagline", From b8ba5fc9f0007fbfd87328142657ffd989693cf5 Mon Sep 17 00:00:00 2001 From: Rods Date: Tue, 17 Dec 2024 21:25:13 -0300 Subject: [PATCH 16/96] =?UTF-8?q?[ISSUE-2]=20-=20=F0=9F=94=A5=20migrating?= =?UTF-8?q?=20detail=20to=20kmp=20module?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- feature-detail/build.gradle.kts | 4 ++++ feature-list-streams/build.gradle.kts | 1 - 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/feature-detail/build.gradle.kts b/feature-detail/build.gradle.kts index 9dfc8ab3..f4231731 100644 --- a/feature-detail/build.gradle.kts +++ b/feature-detail/build.gradle.kts @@ -34,6 +34,10 @@ kotlin { } + commonTest.dependencies { + implementation(libs.bundles.test) + } + sourceSets.named("androidMain").configure { kotlin.srcDir("build/generated/ksp/metadata/androidMain/kotlin") } diff --git a/feature-list-streams/build.gradle.kts b/feature-list-streams/build.gradle.kts index 9dfc8ab3..d06f8932 100644 --- a/feature-list-streams/build.gradle.kts +++ b/feature-list-streams/build.gradle.kts @@ -33,7 +33,6 @@ kotlin { implementation(libs.bundles.androidSupport) } - sourceSets.named("androidMain").configure { kotlin.srcDir("build/generated/ksp/metadata/androidMain/kotlin") } From 4c70844b08a4db89488749299a5af7ee5d3df90e Mon Sep 17 00:00:00 2001 From: Rods Date: Tue, 17 Dec 2024 21:27:57 -0300 Subject: [PATCH 17/96] =?UTF-8?q?[ISSUE-2]=20-=20=F0=9F=94=A5=20migrating?= =?UTF-8?q?=20detail=20to=20kmp=20module?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../feature_detail}/InstantTaskCoroutinesExecutorRule.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename {feature-list-streams/src/test/java/com/codandotv/streamplayerapp/feature_list_streams/detail => feature-detail/src/test/java/com/codandotv/streamplayerapp/feature_detail}/InstantTaskCoroutinesExecutorRule.kt (91%) diff --git a/feature-list-streams/src/test/java/com/codandotv/streamplayerapp/feature_list_streams/detail/InstantTaskCoroutinesExecutorRule.kt b/feature-detail/src/test/java/com/codandotv/streamplayerapp/feature_detail/InstantTaskCoroutinesExecutorRule.kt similarity index 91% rename from feature-list-streams/src/test/java/com/codandotv/streamplayerapp/feature_list_streams/detail/InstantTaskCoroutinesExecutorRule.kt rename to feature-detail/src/test/java/com/codandotv/streamplayerapp/feature_detail/InstantTaskCoroutinesExecutorRule.kt index f30e6ca3..f0cf84a7 100644 --- a/feature-list-streams/src/test/java/com/codandotv/streamplayerapp/feature_list_streams/detail/InstantTaskCoroutinesExecutorRule.kt +++ b/feature-detail/src/test/java/com/codandotv/streamplayerapp/feature_detail/InstantTaskCoroutinesExecutorRule.kt @@ -1,4 +1,4 @@ -package com.codandotv.streamplayerapp.feature_list_streams.detail +package com.codandotv.streamplayerapp.feature_detail import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.ExperimentalCoroutinesApi From 3a2584b1ca0e759d27ed4f8f793adb7a35446116 Mon Sep 17 00:00:00 2001 From: Rods Date: Tue, 17 Dec 2024 21:49:30 -0300 Subject: [PATCH 18/96] =?UTF-8?q?[ISSUE-2]=20-=20=F0=9F=94=A5=20migrating?= =?UTF-8?q?=20search=20to=20kmp=20module?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .kotlin/errors/errors-1733793011356.log | 4 + app/build.gradle.kts | 1 + .../navigation/NavigationGraph.kt | 2 +- core-shared-ui/build.gradle.kts | 1 + .../core_shared_ui/widget/StreamsCard.kt | 64 ++++ .../core_shared_ui/widget/StreamsCarousel.kt | 82 +++++ .../streamplayerapp/core_shared}/Url.kt | 2 +- .../detail/data/DetailStreamRepository.kt | 54 ++++ .../kotlin/detail/data/DetailStreamService.kt | 29 ++ .../detail/data/model/DetailStreamResponse.kt | 14 + .../detail/data/model/VideoStreamResponse.kt | 20 ++ .../kotlin/detail/di/DetailStreamModule.kt | 58 ++++ .../kotlin/detail/domain/DetailStream.kt | 11 + .../detail/domain/DetailStreamMapper.kt | 35 +++ .../detail/domain/DetailStreamUseCase.kt | 26 ++ .../kotlin/detail/domain/VideoStream.kt | 6 + .../detail/domain/VideoStreamsUseCase.kt | 16 + .../navigation/DetailStreamNavigation.kt | 37 +++ .../screens/DetailStreamViewModel.kt | 66 +++++ .../screens/DetailStreamsScreen.kt | 203 +++++++++++++ .../screens/DetailStreamsUIState.kt | 12 + .../widget/DetailStreamActionOption.kt | 76 +++++ .../widget/DetailStreamButtonAction.kt | 59 ++++ .../widget/DetailStreamImagePreview.kt | 71 +++++ .../widget/DetailStreamRowHeader.kt | 49 +++ .../widget/DetailStreamToolbar.kt | 55 ++++ .../res/drawable/netflix_detail.webp | Bin 0 -> 4428 bytes .../androidMain/res/drawable/play_circle.xml | 9 + .../feature_list_streams/core/ContentType.kt | 17 ++ .../list/data/ListStreamRepository.kt | 53 ++++ .../list/data/ListStreamService.kt | 59 ++++ .../list/data/StreamDataSource.kt | 44 +++ .../list/data/model/GenresResponse.kt | 14 + .../list/data/model/ListStreamResponse.kt | 14 + .../list/di/ListStreamModule.kt | 21 ++ .../list/domain/GetGenresUseCase.kt | 19 ++ .../list/domain/GetLatestMovieUseCase.kt | 19 ++ .../list/domain/ListMovieUseCase.kt | 21 ++ .../list/domain/ListStreamAnalytics.kt | 8 + .../list/domain/ListStreamMapper.kt | 28 ++ .../list/domain/model/Genre.kt | 6 + .../list/domain/model/HighlightBanner.kt | 24 ++ .../list/domain/model/ListStream.kt | 12 + .../navigation/ListStreamsNavigation.kt | 43 +++ .../screens/ListStreamViewModel.kt | 130 ++++++++ .../presentation/screens/ListStreamsScreen.kt | 115 +++++++ .../screens/ListStreamsUIState.kt | 10 + .../presentation/widgets/HighlightBanner.kt | 280 ++++++++++++++++++ .../androidMain/res/drawable/ic_top_10.webp | Bin 0 -> 1518 bytes .../res/drawable/image_placeholder.xml | 11 + .../res/drawable/netflix_horizontal_logo.xml | 9 + .../res/layout/activity_list_stream.xml | 18 ++ .../res/values/content-description.xml | 8 + .../src/androidMain/res/values/strings.xml | 14 + feature-search/.gitignore | 1 + feature-search/build.gradle.kts | 49 +++ feature-search/consumer-rules.pro | 0 feature-search/proguard-rules.pro | 21 ++ .../data/api/MostPopularMoviesService.kt | 22 ++ .../search/data/api/SearchStreamService.kt | 22 ++ .../datasource/MostPopularMoviesDataSource.kt | 18 ++ .../data/datasource/SearchStreamDataSource.kt | 17 ++ .../data/model/ListSearchStreamResponse.kt | 22 ++ .../repository/MostPopularMoviesRepository.kt | 16 + .../data/repository/SearchStreamRepository.kt | 16 + .../kotlin/search/di/SearchModule.kt | 43 +++ .../search/domain/MostPopularMoviesUseCase.kt | 17 ++ .../kotlin/search/domain/SearchUseCase.kt | 15 + .../search/domain/mapper/SearchMapper.kt | 11 + .../navigation/SearchStreamNavigation.kt | 30 ++ .../presentation/screens/SearchScreen.kt | 171 +++++++++++ .../presentation/screens/SearchUIState.kt | 10 + .../presentation/screens/SearchViewModel.kt | 113 +++++++ .../presentation/widgets/SearchCarousel.kt | 165 +++++++++++ .../presentation/widgets/SearchStreamCard.kt | 128 ++++++++ .../presentation/widgets/SearchStreams.kt | 249 ++++++++++++++++ .../src/androidMain/res/values/strings.xml | 12 + settings.gradle.kts | 4 +- 78 files changed, 3228 insertions(+), 3 deletions(-) create mode 100644 .kotlin/errors/errors-1733793011356.log create mode 100644 core-shared-ui/src/main/java/com/codandotv/streamplayerapp/core_shared_ui/widget/StreamsCard.kt create mode 100644 core-shared-ui/src/main/java/com/codandotv/streamplayerapp/core_shared_ui/widget/StreamsCarousel.kt rename {core-networking/src/main/java/com/codandotv/streamplayerapp/core_networking => core-shared/src/main/java/com/codandotv/streamplayerapp/core_shared}/Url.kt (80%) create mode 100644 feature-detail/src/androidMain/kotlin/detail/data/DetailStreamRepository.kt create mode 100644 feature-detail/src/androidMain/kotlin/detail/data/DetailStreamService.kt create mode 100644 feature-detail/src/androidMain/kotlin/detail/data/model/DetailStreamResponse.kt create mode 100644 feature-detail/src/androidMain/kotlin/detail/data/model/VideoStreamResponse.kt create mode 100644 feature-detail/src/androidMain/kotlin/detail/di/DetailStreamModule.kt create mode 100644 feature-detail/src/androidMain/kotlin/detail/domain/DetailStream.kt create mode 100644 feature-detail/src/androidMain/kotlin/detail/domain/DetailStreamMapper.kt create mode 100644 feature-detail/src/androidMain/kotlin/detail/domain/DetailStreamUseCase.kt create mode 100644 feature-detail/src/androidMain/kotlin/detail/domain/VideoStream.kt create mode 100644 feature-detail/src/androidMain/kotlin/detail/domain/VideoStreamsUseCase.kt create mode 100644 feature-detail/src/androidMain/kotlin/detail/presentation/navigation/DetailStreamNavigation.kt create mode 100644 feature-detail/src/androidMain/kotlin/detail/presentation/screens/DetailStreamViewModel.kt create mode 100644 feature-detail/src/androidMain/kotlin/detail/presentation/screens/DetailStreamsScreen.kt create mode 100644 feature-detail/src/androidMain/kotlin/detail/presentation/screens/DetailStreamsUIState.kt create mode 100644 feature-detail/src/androidMain/kotlin/detail/presentation/widget/DetailStreamActionOption.kt create mode 100644 feature-detail/src/androidMain/kotlin/detail/presentation/widget/DetailStreamButtonAction.kt create mode 100644 feature-detail/src/androidMain/kotlin/detail/presentation/widget/DetailStreamImagePreview.kt create mode 100644 feature-detail/src/androidMain/kotlin/detail/presentation/widget/DetailStreamRowHeader.kt create mode 100644 feature-detail/src/androidMain/kotlin/detail/presentation/widget/DetailStreamToolbar.kt create mode 100644 feature-detail/src/androidMain/res/drawable/netflix_detail.webp create mode 100644 feature-detail/src/androidMain/res/drawable/play_circle.xml create mode 100644 feature-list-streams/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_list_streams/core/ContentType.kt create mode 100644 feature-list-streams/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_list_streams/list/data/ListStreamRepository.kt create mode 100644 feature-list-streams/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_list_streams/list/data/ListStreamService.kt create mode 100644 feature-list-streams/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_list_streams/list/data/StreamDataSource.kt create mode 100644 feature-list-streams/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_list_streams/list/data/model/GenresResponse.kt create mode 100644 feature-list-streams/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_list_streams/list/data/model/ListStreamResponse.kt create mode 100644 feature-list-streams/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_list_streams/list/di/ListStreamModule.kt create mode 100644 feature-list-streams/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_list_streams/list/domain/GetGenresUseCase.kt create mode 100644 feature-list-streams/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_list_streams/list/domain/GetLatestMovieUseCase.kt create mode 100644 feature-list-streams/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_list_streams/list/domain/ListMovieUseCase.kt create mode 100644 feature-list-streams/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_list_streams/list/domain/ListStreamAnalytics.kt create mode 100644 feature-list-streams/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_list_streams/list/domain/ListStreamMapper.kt create mode 100644 feature-list-streams/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_list_streams/list/domain/model/Genre.kt create mode 100644 feature-list-streams/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_list_streams/list/domain/model/HighlightBanner.kt create mode 100644 feature-list-streams/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_list_streams/list/domain/model/ListStream.kt create mode 100644 feature-list-streams/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_list_streams/list/presentation/navigation/ListStreamsNavigation.kt create mode 100644 feature-list-streams/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_list_streams/list/presentation/screens/ListStreamViewModel.kt create mode 100644 feature-list-streams/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_list_streams/list/presentation/screens/ListStreamsScreen.kt create mode 100644 feature-list-streams/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_list_streams/list/presentation/screens/ListStreamsUIState.kt create mode 100644 feature-list-streams/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_list_streams/list/presentation/widgets/HighlightBanner.kt create mode 100644 feature-list-streams/src/androidMain/res/drawable/ic_top_10.webp create mode 100644 feature-list-streams/src/androidMain/res/drawable/image_placeholder.xml create mode 100644 feature-list-streams/src/androidMain/res/drawable/netflix_horizontal_logo.xml create mode 100644 feature-list-streams/src/androidMain/res/layout/activity_list_stream.xml create mode 100644 feature-list-streams/src/androidMain/res/values/content-description.xml create mode 100644 feature-list-streams/src/androidMain/res/values/strings.xml create mode 100644 feature-search/.gitignore create mode 100644 feature-search/build.gradle.kts create mode 100644 feature-search/consumer-rules.pro create mode 100644 feature-search/proguard-rules.pro create mode 100644 feature-search/src/androidMain/kotlin/search/data/api/MostPopularMoviesService.kt create mode 100644 feature-search/src/androidMain/kotlin/search/data/api/SearchStreamService.kt create mode 100644 feature-search/src/androidMain/kotlin/search/data/datasource/MostPopularMoviesDataSource.kt create mode 100644 feature-search/src/androidMain/kotlin/search/data/datasource/SearchStreamDataSource.kt create mode 100644 feature-search/src/androidMain/kotlin/search/data/model/ListSearchStreamResponse.kt create mode 100644 feature-search/src/androidMain/kotlin/search/data/repository/MostPopularMoviesRepository.kt create mode 100644 feature-search/src/androidMain/kotlin/search/data/repository/SearchStreamRepository.kt create mode 100644 feature-search/src/androidMain/kotlin/search/di/SearchModule.kt create mode 100644 feature-search/src/androidMain/kotlin/search/domain/MostPopularMoviesUseCase.kt create mode 100644 feature-search/src/androidMain/kotlin/search/domain/SearchUseCase.kt create mode 100644 feature-search/src/androidMain/kotlin/search/domain/mapper/SearchMapper.kt create mode 100644 feature-search/src/androidMain/kotlin/search/presentation/navigation/SearchStreamNavigation.kt create mode 100644 feature-search/src/androidMain/kotlin/search/presentation/screens/SearchScreen.kt create mode 100644 feature-search/src/androidMain/kotlin/search/presentation/screens/SearchUIState.kt create mode 100644 feature-search/src/androidMain/kotlin/search/presentation/screens/SearchViewModel.kt create mode 100644 feature-search/src/androidMain/kotlin/search/presentation/widgets/SearchCarousel.kt create mode 100644 feature-search/src/androidMain/kotlin/search/presentation/widgets/SearchStreamCard.kt create mode 100644 feature-search/src/androidMain/kotlin/search/presentation/widgets/SearchStreams.kt create mode 100644 feature-search/src/androidMain/res/values/strings.xml diff --git a/.kotlin/errors/errors-1733793011356.log b/.kotlin/errors/errors-1733793011356.log new file mode 100644 index 00000000..1219b509 --- /dev/null +++ b/.kotlin/errors/errors-1733793011356.log @@ -0,0 +1,4 @@ +kotlin version: 2.0.21 +error message: The daemon has terminated unexpectedly on startup attempt #1 with error code: 0. The daemon process output: + 1. Kotlin compile daemon is ready + diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 6f5990db..6b65c1b1 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -10,6 +10,7 @@ dependencies { implementation(projects.featureFavorites) implementation(projects.featureListStreams) implementation(projects.featureDetail) + implementation(projects.featureSearch) implementation(projects.featureProfile) implementation(projects.coreShared) implementation(projects.coreSharedUi) diff --git a/app/src/main/java/com/codandotv/streamplayerapp/navigation/NavigationGraph.kt b/app/src/main/java/com/codandotv/streamplayerapp/navigation/NavigationGraph.kt index 81570e38..31902b97 100644 --- a/app/src/main/java/com/codandotv/streamplayerapp/navigation/NavigationGraph.kt +++ b/app/src/main/java/com/codandotv/streamplayerapp/navigation/NavigationGraph.kt @@ -16,10 +16,10 @@ import com.codandotv.streamplayerapp.core_navigation.routes.BottomNavRoutes import com.codandotv.streamplayerapp.core_navigation.routes.Routes import detail.presentation.navigation.detailStreamNavGraph import com.codandotv.streamplayerapp.feature_list_streams.list.presentation.navigation.listStreamsNavGraph -import com.codandotv.streamplayerapp.feature_list_streams.search.presentation.navigation.searchStreamsNavGraph import com.codandotv.streamplayerapp.feature_profile.profile.presentation.navigation.profilePickerStreamNavGraph import com.codandotv.streamplayerapp.splah.presentation.navigation.splashNavGraph +import search.presentation.navigation.searchStreamsNavGraph @Composable fun NavigationGraph(navController: NavHostController) { diff --git a/core-shared-ui/build.gradle.kts b/core-shared-ui/build.gradle.kts index 34351e22..1a4e4c49 100644 --- a/core-shared-ui/build.gradle.kts +++ b/core-shared-ui/build.gradle.kts @@ -15,6 +15,7 @@ dependencies { implementation(libs.bundles.kotlin) implementation(libs.bundles.androidSupport) implementation(libs.android.youtube.player) + implementation(libs.paging.compose) implementation(libs.coil) testImplementation(libs.bundles.test) } \ No newline at end of file diff --git a/core-shared-ui/src/main/java/com/codandotv/streamplayerapp/core_shared_ui/widget/StreamsCard.kt b/core-shared-ui/src/main/java/com/codandotv/streamplayerapp/core_shared_ui/widget/StreamsCard.kt new file mode 100644 index 00000000..1633791e --- /dev/null +++ b/core-shared-ui/src/main/java/com/codandotv/streamplayerapp/core_shared_ui/widget/StreamsCard.kt @@ -0,0 +1,64 @@ +package com.codandotv.streamplayerapp.core_shared_ui.widget + +import android.os.Parcelable +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material3.Card +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.layout.ContentScale +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import coil.compose.AsyncImage +import com.codandotv.streamplayerapp.core_shared.Url.IMAGE_URL_SIZE_300 +import kotlinx.parcelize.Parcelize + +@Composable +fun StreamsCard( + content: StreamsCardContent, + onNavigateDetailList: (String) -> Unit = {}, +) { + Card( + shape = RoundedCornerShape(6.dp), + modifier = Modifier + .size( + width = 100.dp, + height = 140.dp + ) + .padding( + horizontal = 4.dp + ) + .clickable { + onNavigateDetailList.invoke(content.id) + } + ) { + AsyncImage( + model = content.url, + modifier = Modifier.fillMaxSize(), + contentScale = ContentScale.FillBounds, + contentDescription = content.contentDescription + ) + } +} + +@Parcelize +data class StreamsCardContent( + val id: String, + val url: String, + val contentDescription: String, +) : Parcelable + +@Preview +@Composable +fun StreamsCardPreview() { + StreamsCard( + StreamsCardContent( + url = "${IMAGE_URL_SIZE_300}evgwd37VHBJhXvSr88Mrx5riFil.jpg", + contentDescription = "Test 1", + id = "", + ) + ) +} \ No newline at end of file diff --git a/core-shared-ui/src/main/java/com/codandotv/streamplayerapp/core_shared_ui/widget/StreamsCarousel.kt b/core-shared-ui/src/main/java/com/codandotv/streamplayerapp/core_shared_ui/widget/StreamsCarousel.kt new file mode 100644 index 00000000..40100d2a --- /dev/null +++ b/core-shared-ui/src/main/java/com/codandotv/streamplayerapp/core_shared_ui/widget/StreamsCarousel.kt @@ -0,0 +1,82 @@ +package com.codandotv.streamplayerapp.core_shared_ui.widget + +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.lazy.LazyRow +import androidx.compose.foundation.lazy.rememberLazyListState +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import androidx.paging.PagingData +import androidx.paging.compose.collectAsLazyPagingItems +import androidx.paging.compose.itemContentType +import androidx.paging.compose.itemKey +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.emptyFlow + +@Composable +fun StreamsCarousel( + content: StreamsCarouselContent, + modifier: Modifier = Modifier, + onNavigateDetailList: (String) -> Unit = {}, +) { + val lazyPagingItems = content.contentList.collectAsLazyPagingItems() + val lazyListState = rememberLazyListState() + + Column(modifier = modifier) { + Text( + content.genreTitle, + style = MaterialTheme.typography.headlineMedium.copy( + fontWeight = FontWeight.Bold, + fontSize = 20.sp + ) + ) + + Spacer(modifier = Modifier.size(8.dp)) + + LazyRow( + state = lazyListState, + modifier = Modifier + .fillMaxWidth() + .height(140.dp) + ) { + items( + count = lazyPagingItems.itemCount, + key = lazyPagingItems.itemKey(), + contentType = lazyPagingItems.itemContentType() + ) { index -> + val item = lazyPagingItems[index] + item?.let { + StreamsCard( + content = it, + onNavigateDetailList + ) + } + } + } + } +} + +data class StreamsCarouselContent( + val genreTitle: String, + val contentList: Flow> +) + +@Composable +@Preview +fun StreamsCarouselPreview() { + StreamsCarousel( + content = StreamsCarouselContent( + genreTitle = "Ação", + contentList = emptyFlow() + ) + ) +} \ No newline at end of file diff --git a/core-networking/src/main/java/com/codandotv/streamplayerapp/core_networking/Url.kt b/core-shared/src/main/java/com/codandotv/streamplayerapp/core_shared/Url.kt similarity index 80% rename from core-networking/src/main/java/com/codandotv/streamplayerapp/core_networking/Url.kt rename to core-shared/src/main/java/com/codandotv/streamplayerapp/core_shared/Url.kt index 1b0d9b77..8ea4c8e2 100644 --- a/core-networking/src/main/java/com/codandotv/streamplayerapp/core_networking/Url.kt +++ b/core-shared/src/main/java/com/codandotv/streamplayerapp/core_shared/Url.kt @@ -1,4 +1,4 @@ -package com.codandotv.streamplayerapp.core_networking +package com.codandotv.streamplayerapp.core_shared object Url { const val IMAGE_URL_SIZE_200 = "https://image.tmdb.org/t/p/w200/" diff --git a/feature-detail/src/androidMain/kotlin/detail/data/DetailStreamRepository.kt b/feature-detail/src/androidMain/kotlin/detail/data/DetailStreamRepository.kt new file mode 100644 index 00000000..332dcc7b --- /dev/null +++ b/feature-detail/src/androidMain/kotlin/detail/data/DetailStreamRepository.kt @@ -0,0 +1,54 @@ +package detail.data + +import com.codandotv.streamplayerapp.core_local_storage.data.dao.FavoriteDao +import com.codandotv.streamplayerapp.core_networking.handleError.toFlow +import detail.domain.DetailStream +import detail.domain.VideoStream +import detail.domain.toDetailStream +import detail.domain.toDetailStreamLocal +import detail.domain.toVideoStreams +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.map + +interface DetailStreamRepository { + suspend fun getMovie(): Flow + suspend fun deleteFromMyList(movie: String) + suspend fun insertToMyList(movie: DetailStream) + suspend fun isFavorite(movieId:String) : Boolean + suspend fun getVideoStreams(): Flow> +} + +class DetailStreamRepositoryImpl( + private val movieId: String, + private val service: DetailStreamService, + private val favoriteDao: FavoriteDao, +) : DetailStreamRepository { + + override suspend fun getMovie(): Flow = + service.getMovie(movieId) + .toFlow() + .map { + it.toDetailStream(isFavorite(movieId)) + } + + + override suspend fun deleteFromMyList(movie: String) = favoriteDao.delete(movie) + + override suspend fun insertToMyList(movie: DetailStream) = favoriteDao.insert(movie.toDetailStreamLocal()) + + /** + * Verify if movieId was saved as favorite + * @param movieId + * @return Boolean + */ + override suspend fun isFavorite(movieId: String) : Boolean = favoriteDao.fetchAll().any { + movie -> movie.id == movieId + } + + override suspend fun getVideoStreams(): Flow> = + service.getVideoStreams(movieId) + .toFlow() + .map { + it.toVideoStreams() + } +} \ No newline at end of file diff --git a/feature-detail/src/androidMain/kotlin/detail/data/DetailStreamService.kt b/feature-detail/src/androidMain/kotlin/detail/data/DetailStreamService.kt new file mode 100644 index 00000000..4f4174b5 --- /dev/null +++ b/feature-detail/src/androidMain/kotlin/detail/data/DetailStreamService.kt @@ -0,0 +1,29 @@ +package detail.data + +import com.codandotv.streamplayerapp.core_networking.handleError.NetworkResponse +import com.codandotv.streamplayerapp.core_networking.handleError.safeRequest +import detail.data.model.DetailStreamResponse +import detail.data.model.VideoStreamsResponse +import io.ktor.client.HttpClient +import io.ktor.client.request.url +import org.koin.core.annotation.Factory + +interface DetailStreamService { + suspend fun getMovie(movieId: String): NetworkResponse + suspend fun getVideoStreams(movieId: String): NetworkResponse +} + +class DetailStreamServiceImpl( + private val client: HttpClient +) : DetailStreamService { + + override suspend fun getMovie(movieId: String): NetworkResponse = + client.safeRequest { + url("movie/$movieId") + } + + override suspend fun getVideoStreams(movieId: String): NetworkResponse = + client.safeRequest { + url("movie/$movieId/videos") + } +} diff --git a/feature-detail/src/androidMain/kotlin/detail/data/model/DetailStreamResponse.kt b/feature-detail/src/androidMain/kotlin/detail/data/model/DetailStreamResponse.kt new file mode 100644 index 00000000..7e1b1dd8 --- /dev/null +++ b/feature-detail/src/androidMain/kotlin/detail/data/model/DetailStreamResponse.kt @@ -0,0 +1,14 @@ +package detail.data.model + +import kotlinx.serialization.Serializable + +@Serializable +@Suppress("ConstructorParameterNaming") +data class DetailStreamResponse( + val id : Long, + val title : String, + val overview : String, + val tagline : String, + val backdrop_path : String, + val release_date : String +) \ No newline at end of file diff --git a/feature-detail/src/androidMain/kotlin/detail/data/model/VideoStreamResponse.kt b/feature-detail/src/androidMain/kotlin/detail/data/model/VideoStreamResponse.kt new file mode 100644 index 00000000..64e2efbc --- /dev/null +++ b/feature-detail/src/androidMain/kotlin/detail/data/model/VideoStreamResponse.kt @@ -0,0 +1,20 @@ +package detail.data.model + +import kotlinx.serialization.Serializable + +@Serializable +data class VideoStreamResponse( + val id: String, + val name: String, + val key: String, + val site: String, + val size: Int, + val official: Boolean, + val type: String, +) + +@Serializable +data class VideoStreamsResponse( + val id: Long, + val results: List +) \ No newline at end of file diff --git a/feature-detail/src/androidMain/kotlin/detail/di/DetailStreamModule.kt b/feature-detail/src/androidMain/kotlin/detail/di/DetailStreamModule.kt new file mode 100644 index 00000000..304c5e8d --- /dev/null +++ b/feature-detail/src/androidMain/kotlin/detail/di/DetailStreamModule.kt @@ -0,0 +1,58 @@ +package detail.di + +import detail.data.DetailStreamRepository +import detail.data.DetailStreamRepositoryImpl +import detail.data.DetailStreamService +import detail.data.DetailStreamServiceImpl +import detail.domain.DetailStreamUseCase +import detail.domain.DetailStreamUseCaseImpl +import detail.domain.VideoStreamsUseCase +import detail.domain.VideoStreamsUseCaseImpl +import detail.presentation.screens.DetailStreamViewModel +import kotlinx.coroutines.Dispatchers +import org.koin.androidx.viewmodel.dsl.viewModel +import org.koin.core.parameter.parametersOf +import org.koin.dsl.module + +object DetailStreamModule { + val module = module { + viewModel { (id: String) -> + DetailStreamViewModel( + detailStreamUseCase = get { + parametersOf(id) + }, + videoStreamsUseCase = get { + parametersOf(id) + }, + dispatcher = Dispatchers.IO + ) + } + factory { (id: String) -> + DetailStreamUseCaseImpl( + detailStreamRepository = get { + parametersOf(id) + } + ) + } + factory { (id: String) -> + VideoStreamsUseCaseImpl( + detailStreamRepository = get { + parametersOf(id) + } + ) + } + factory { (id: String) -> + DetailStreamRepositoryImpl( + favoriteDao = get(), + service = get(), + movieId = id, + ) + } + + factory { + DetailStreamServiceImpl( + client = get() + ) + } + } +} \ No newline at end of file diff --git a/feature-detail/src/androidMain/kotlin/detail/domain/DetailStream.kt b/feature-detail/src/androidMain/kotlin/detail/domain/DetailStream.kt new file mode 100644 index 00000000..0afcc317 --- /dev/null +++ b/feature-detail/src/androidMain/kotlin/detail/domain/DetailStream.kt @@ -0,0 +1,11 @@ +package detail.domain + +data class DetailStream( + val id : String, + val title : String, + val overview : String, + val tagline : String, + val url : String, + val releaseYear : String, + val isFavorite: Boolean +) \ No newline at end of file diff --git a/feature-detail/src/androidMain/kotlin/detail/domain/DetailStreamMapper.kt b/feature-detail/src/androidMain/kotlin/detail/domain/DetailStreamMapper.kt new file mode 100644 index 00000000..52974336 --- /dev/null +++ b/feature-detail/src/androidMain/kotlin/detail/domain/DetailStreamMapper.kt @@ -0,0 +1,35 @@ +package detail.domain + +import com.codandotv.streamplayerapp.core_local_storage.domain.model.MovieEntity +import com.codandotv.streamplayerapp.core_shared.Url.IMAGE_URL_SIZE_500 +import detail.data.model.DetailStreamResponse +import detail.data.model.VideoStreamsResponse + +fun DetailStreamResponse.toDetailStream(isFavorite: Boolean = false): DetailStream = + DetailStream( + id = this.id.toString(), + title = this.title, + overview = this.overview, + tagline = this.tagline, + url = "$IMAGE_URL_SIZE_500${this.backdrop_path}", + releaseYear = this.release_date.substring(0, 4), + isFavorite = isFavorite + ) + +fun DetailStream.toDetailStreamLocal(): MovieEntity = + MovieEntity( + id = this.id, + title = this.title, + overview = this.overview, + tagline = this.tagline, + url = this.url, + releaseYear = this.releaseYear, + ) + +fun VideoStreamsResponse.toVideoStreams(): List = + results.map { + VideoStream( + videoId = it.key, + movieId = this.id + ) + } diff --git a/feature-detail/src/androidMain/kotlin/detail/domain/DetailStreamUseCase.kt b/feature-detail/src/androidMain/kotlin/detail/domain/DetailStreamUseCase.kt new file mode 100644 index 00000000..a47a6c1f --- /dev/null +++ b/feature-detail/src/androidMain/kotlin/detail/domain/DetailStreamUseCase.kt @@ -0,0 +1,26 @@ +package detail.domain + +import detail.data.DetailStreamRepository +import kotlinx.coroutines.flow.Flow + +interface DetailStreamUseCase { + suspend fun getMovie(): Flow + + suspend fun toggleItemInFavorites(movie: DetailStream) +} + +class DetailStreamUseCaseImpl( + private val detailStreamRepository: DetailStreamRepository +) : DetailStreamUseCase { + + override suspend fun getMovie(): Flow = + detailStreamRepository.getMovie() + + override suspend fun toggleItemInFavorites(movie: DetailStream) { + if (detailStreamRepository.isFavorite(movie.id)) { + detailStreamRepository.deleteFromMyList(movie.id) + } else { + detailStreamRepository.insertToMyList(movie) + } + } +} \ No newline at end of file diff --git a/feature-detail/src/androidMain/kotlin/detail/domain/VideoStream.kt b/feature-detail/src/androidMain/kotlin/detail/domain/VideoStream.kt new file mode 100644 index 00000000..1bc7f99f --- /dev/null +++ b/feature-detail/src/androidMain/kotlin/detail/domain/VideoStream.kt @@ -0,0 +1,6 @@ +package detail.domain + +data class VideoStream( + val movieId: Long, + val videoId: String, +) \ No newline at end of file diff --git a/feature-detail/src/androidMain/kotlin/detail/domain/VideoStreamsUseCase.kt b/feature-detail/src/androidMain/kotlin/detail/domain/VideoStreamsUseCase.kt new file mode 100644 index 00000000..77285702 --- /dev/null +++ b/feature-detail/src/androidMain/kotlin/detail/domain/VideoStreamsUseCase.kt @@ -0,0 +1,16 @@ +package detail.domain + +import detail.data.DetailStreamRepository +import kotlinx.coroutines.flow.Flow + +interface VideoStreamsUseCase { + suspend fun getVideoStreams(): Flow> +} + +class VideoStreamsUseCaseImpl( + private val detailStreamRepository: DetailStreamRepository +) : VideoStreamsUseCase { + override suspend fun getVideoStreams(): Flow> { + return detailStreamRepository.getVideoStreams() + } +} \ No newline at end of file diff --git a/feature-detail/src/androidMain/kotlin/detail/presentation/navigation/DetailStreamNavigation.kt b/feature-detail/src/androidMain/kotlin/detail/presentation/navigation/DetailStreamNavigation.kt new file mode 100644 index 00000000..e5eb36bc --- /dev/null +++ b/feature-detail/src/androidMain/kotlin/detail/presentation/navigation/DetailStreamNavigation.kt @@ -0,0 +1,37 @@ +package detail.presentation.navigation + +import androidx.lifecycle.Lifecycle +import androidx.navigation.NavGraphBuilder +import androidx.navigation.NavHostController +import androidx.navigation.compose.composable +import com.codandotv.streamplayerapp.core_navigation.routes.Routes +import com.codandotv.streamplayerapp.core_navigation.routes.Routes.DETAIL_COMPLETE +import com.codandotv.streamplayerapp.core_navigation.routes.Routes.PARAM.ID +import detail.di.DetailStreamModule +import detail.presentation.screens.DetailStreamScreen +import org.koin.androidx.compose.koinViewModel +import org.koin.core.context.loadKoinModules +import org.koin.core.context.unloadKoinModules +import org.koin.core.parameter.parametersOf + +internal const val DEFAULT_ID = "0" + +fun NavGraphBuilder.detailStreamNavGraph(navController: NavHostController) { + composable(DETAIL_COMPLETE) { nav -> + if (nav.lifecycle.currentState == Lifecycle.State.STARTED) { + loadKoinModules(DetailStreamModule.module) + } + DetailStreamScreen( + viewModel = koinViewModel { + parametersOf(nav.arguments?.getString(ID) ?: DEFAULT_ID) + }, + navController = navController, + onNavigateSearchScreen = { + navController.navigate(Routes.SEARCH) + }, + disposable = { + unloadKoinModules(DetailStreamModule.module) + } + ) + } +} diff --git a/feature-detail/src/androidMain/kotlin/detail/presentation/screens/DetailStreamViewModel.kt b/feature-detail/src/androidMain/kotlin/detail/presentation/screens/DetailStreamViewModel.kt new file mode 100644 index 00000000..ea3ce405 --- /dev/null +++ b/feature-detail/src/androidMain/kotlin/detail/presentation/screens/DetailStreamViewModel.kt @@ -0,0 +1,66 @@ +package detail.presentation.screens + +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import com.codandotv.streamplayerapp.core_networking.handleError.catchFailure +import detail.domain.DetailStream +import detail.domain.DetailStreamUseCase +import detail.domain.VideoStreamsUseCase +import detail.presentation.screens.DetailStreamsUIState.DetailStreamsLoadedUIState +import detail.presentation.screens.DetailStreamsUIState.LoadingStreamUIState +import kotlinx.coroutines.CoroutineDispatcher +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.SharingStarted +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.flowOn +import kotlinx.coroutines.flow.onStart +import kotlinx.coroutines.flow.stateIn +import kotlinx.coroutines.flow.update +import kotlinx.coroutines.flow.zip +import kotlinx.coroutines.launch + +class DetailStreamViewModel( + private val detailStreamUseCase: DetailStreamUseCase, + private val videoStreamsUseCase: VideoStreamsUseCase, + private val dispatcher: CoroutineDispatcher +) : ViewModel() { + + private val _uiState = MutableStateFlow(LoadingStreamUIState) + val uiState: StateFlow = _uiState.stateIn( + viewModelScope, + SharingStarted.Eagerly, + initialValue = _uiState.value + ) + + fun loadDetail() { + viewModelScope.launch { + detailStreamUseCase.getMovie() + .zip(videoStreamsUseCase.getVideoStreams()) { detailStream, videoUrl -> + DetailStreamsLoadedUIState( + detailStream = detailStream, + videoId = videoUrl.firstOrNull()?.videoId + ) + } + .flowOn(dispatcher) + .onStart { onLoading() } + .catchFailure { + println(">>>> ${it.errorMessage}") + } + .collect { result -> + _uiState.update { + result + } + } + } + } + + private fun onLoading() { + _uiState.update { LoadingStreamUIState } + } + + fun toggleItemInFavorites(detailStream: DetailStream) { + viewModelScope.launch { + detailStreamUseCase.toggleItemInFavorites(detailStream) + } + } +} \ No newline at end of file diff --git a/feature-detail/src/androidMain/kotlin/detail/presentation/screens/DetailStreamsScreen.kt b/feature-detail/src/androidMain/kotlin/detail/presentation/screens/DetailStreamsScreen.kt new file mode 100644 index 00000000..158fba69 --- /dev/null +++ b/feature-detail/src/androidMain/kotlin/detail/presentation/screens/DetailStreamsScreen.kt @@ -0,0 +1,203 @@ +package detail.presentation.screens + +import android.annotation.SuppressLint +import androidx.activity.compose.BackHandler +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.verticalScroll +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Add +import androidx.compose.material.icons.filled.PlayArrow +import androidx.compose.material3.ButtonDefaults +import androidx.compose.material3.CircularProgressIndicator +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Scaffold +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.DisposableEffect +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.LocalLifecycleOwner +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.em +import androidx.compose.ui.unit.sp +import androidx.lifecycle.Lifecycle +import androidx.lifecycle.compose.LifecycleEventEffect +import androidx.lifecycle.compose.collectAsStateWithLifecycle +import androidx.navigation.NavController +import com.codandotv.streamplayerapp.core_shared_ui.widget.SharingStreamCustomView +import com.codandotv.streamplayerapp.feature.detail.R +import detail.domain.DetailStream +import detail.presentation.widget.DetailStreamActionOption +import detail.presentation.widget.DetailStreamButtonAction +import detail.presentation.widget.DetailStreamImagePreview +import detail.presentation.widget.DetailStreamRowHeader +import detail.presentation.widget.DetailStreamToolbar +import org.koin.androidx.compose.koinViewModel + +@Composable +fun DetailStreamScreen( + viewModel: DetailStreamViewModel = koinViewModel(), + navController: NavController, + disposable: () -> Unit = {}, + onNavigateSearchScreen: () -> Unit = {}, +) { + val uiState by viewModel.uiState.collectAsStateWithLifecycle() + + val lifecycleOwner = LocalLifecycleOwner.current + + LifecycleEventEffect(Lifecycle.Event.ON_START) { + viewModel.loadDetail() + } + + DisposableEffect(lifecycleOwner) { + onDispose { + disposable.invoke() + } + } + + when (uiState) { + is DetailStreamsUIState.DetailStreamsLoadedUIState -> { + SetupDetailScreen( + onToggleToMyList = { detailStream -> viewModel.toggleItemInFavorites(detailStream) }, + uiState = uiState as DetailStreamsUIState.DetailStreamsLoadedUIState, + navController = navController, + onNavigateSearchScreen = onNavigateSearchScreen + ) + } + + else -> { + Box(Modifier.fillMaxSize()) { + CircularProgressIndicator( + modifier = Modifier.align( + Alignment.Center + ) + ) + } + } + + } +} + +@OptIn(ExperimentalMaterial3Api::class) +@Suppress("LongMethod") +@SuppressLint("UnusedMaterial3ScaffoldPaddingParameter") +@Composable +private fun SetupDetailScreen( + onToggleToMyList: (DetailStream) -> Unit, + uiState: DetailStreamsUIState.DetailStreamsLoadedUIState, + navController: NavController, + onNavigateSearchScreen: () -> Unit = {}, +) { + val showDialog = remember { mutableStateOf(false) } + + var showPlayer by remember { mutableStateOf(false) } + + Scaffold( + topBar = { + DetailStreamToolbar( + navController = navController, + onNavigateSearchScreen = onNavigateSearchScreen + ) + }, + content = { innerPadding -> + Column( + Modifier + .fillMaxWidth() + .verticalScroll(rememberScrollState()) + .padding(innerPadding) + ) { + DetailStreamImagePreview( + uiState = uiState, + onPlayEvent = { + showPlayer = true + }, + showPlayer = showPlayer + ) + Column( + modifier = Modifier + .fillMaxWidth() + .padding(start = 16.dp, end = 16.dp, top = 8.dp) + ) { + DetailStreamRowHeader() + Text( + text = uiState.detailStream.title, + style = MaterialTheme.typography.headlineMedium.copy( + fontWeight = FontWeight.Bold, fontSize = 28.sp + ) + ) + Spacer(modifier = Modifier.height(8.dp)) + Text( + text = uiState.detailStream.releaseYear, + style = MaterialTheme.typography.headlineMedium.copy( + color = MaterialTheme.colorScheme.onSurfaceVariant, + fontSize = 14.sp, fontWeight = FontWeight.Bold + ) + ) + Spacer(modifier = Modifier.height(8.dp)) + DetailStreamButtonAction( + buttonsColors = ButtonDefaults.buttonColors( + containerColor = MaterialTheme.colorScheme.surface + ), + imageVector = Icons.Filled.PlayArrow, + imageVectorColor = MaterialTheme.colorScheme.onSurface, + text = stringResource(R.string.detail_watch_primary_button), + textColor = MaterialTheme.colorScheme.onSurface + ) + Spacer(modifier = Modifier.height(4.dp)) + DetailStreamButtonAction( + buttonsColors = ButtonDefaults.buttonColors( + containerColor = MaterialTheme.colorScheme.onSurfaceVariant + ), + imageVector = Icons.Filled.Add, + imageVectorColor = MaterialTheme.colorScheme.onSurface, + text = stringResource(id = R.string.detail_default_text_secondary_button), + textColor = MaterialTheme.colorScheme.onSurface, + ) + Text( + text = uiState.detailStream.overview, + style = MaterialTheme.typography.headlineMedium.copy( + color = MaterialTheme.colorScheme.onSurface, + fontSize = 16.sp, + lineHeight = 1.25.em + ), + modifier = Modifier.padding(top = 8.dp, bottom = 16.dp) + ) + Spacer(modifier = Modifier.height(8.dp)) + DetailStreamActionOption( + uiState.detailStream, + onToggleToMyList, + { showDialog.value = true }) + Spacer(modifier = Modifier.height(16.dp)) + } + } + if (showDialog.value) { + SharingStreamCustomView( + contentTitle = uiState.detailStream.title, + contentUrl = uiState.detailStream.url, + setShowDialog = { + showDialog.value = it + }) + } + BackHandler { + if (showDialog.value) { + showDialog.value = false + } else { + navController.navigateUp() + } + } + }) +} \ No newline at end of file diff --git a/feature-detail/src/androidMain/kotlin/detail/presentation/screens/DetailStreamsUIState.kt b/feature-detail/src/androidMain/kotlin/detail/presentation/screens/DetailStreamsUIState.kt new file mode 100644 index 00000000..80ee69bc --- /dev/null +++ b/feature-detail/src/androidMain/kotlin/detail/presentation/screens/DetailStreamsUIState.kt @@ -0,0 +1,12 @@ +package detail.presentation.screens + +import detail.domain.DetailStream + +sealed class DetailStreamsUIState { + data class DetailStreamsLoadedUIState( + val detailStream: DetailStream, + val videoId: String?, + ) : DetailStreamsUIState() + + object LoadingStreamUIState : DetailStreamsUIState() +} diff --git a/feature-detail/src/androidMain/kotlin/detail/presentation/widget/DetailStreamActionOption.kt b/feature-detail/src/androidMain/kotlin/detail/presentation/widget/DetailStreamActionOption.kt new file mode 100644 index 00000000..64e7a624 --- /dev/null +++ b/feature-detail/src/androidMain/kotlin/detail/presentation/widget/DetailStreamActionOption.kt @@ -0,0 +1,76 @@ +package detail.presentation.widget + +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Add +import androidx.compose.material.icons.filled.Check +import androidx.compose.material.icons.filled.Share +import androidx.compose.material.icons.filled.ThumbUp +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.saveable.rememberSaveable +import androidx.compose.runtime.setValue +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.res.stringResource +import com.codandotv.streamplayerapp.core_shared_ui.widget.IconWithText +import com.codandotv.streamplayerapp.feature.detail.R +import detail.domain.DetailStream + +@Composable +fun DetailStreamActionOption( + detailStream: DetailStream, + onToggleToMyList: (DetailStream) -> Unit, + onShowSharingOptions: () -> Unit, + modifier: Modifier = Modifier.fillMaxWidth() +) { + var checked by rememberSaveable { mutableStateOf(detailStream.isFavorite) } + var iconCheckList by remember { mutableStateOf(Icons.Filled.Add) } + + LaunchedEffect(checked) { + iconCheckList = + if (checked) Icons.Filled.Check else Icons.Filled.Add + } + + Row( + modifier = modifier, + horizontalArrangement = Arrangement.SpaceEvenly + ) { + IconWithText( + onClick = { + checked = !checked + onToggleToMyList(detailStream) + }, + imageVector = iconCheckList, + imageColor = Color.White, + text = stringResource(id = R.string.detail_my_list), + textColor = Color.Gray, + ) + IconWithText( + onClick = { TODO("Implementar mecanismo de classificação.") }, + imageVector = Icons.Filled.ThumbUp, + imageColor = Color.White, + text = stringResource(id = R.string.detail_classification), + textColor = Color.Gray, + ) + IconWithText( + onClick = { onShowSharingOptions.invoke() }, + imageVector = Icons.Filled.Share, + imageColor = Color.White, + text = stringResource(id = R.string.detail_share), + textColor = Color.Gray, + ) + IconWithText( + onClick = { TODO("Implementar mecanismo de download.") }, + imageVector = Icons.Filled.Share, + imageColor = Color.White, + text = stringResource(id = R.string.detail_download), + textColor = Color.Gray, + ) + } +} \ No newline at end of file diff --git a/feature-detail/src/androidMain/kotlin/detail/presentation/widget/DetailStreamButtonAction.kt b/feature-detail/src/androidMain/kotlin/detail/presentation/widget/DetailStreamButtonAction.kt new file mode 100644 index 00000000..dc583630 --- /dev/null +++ b/feature-detail/src/androidMain/kotlin/detail/presentation/widget/DetailStreamButtonAction.kt @@ -0,0 +1,59 @@ +package detail.presentation.widget + +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.layout.width +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material3.Button +import androidx.compose.material3.ButtonColors +import androidx.compose.material3.Icon +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.vector.ImageVector +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp + +@Suppress("LongParameterList") +@Composable +fun DetailStreamButtonAction( + buttonsColors: ButtonColors, + imageVector: ImageVector, + imageVectorColor: Color, + text: String, + textColor: Color, + modifier: Modifier = Modifier.fillMaxWidth(), +) { + Button( + onClick = { }, + shape = RoundedCornerShape(4.dp), + modifier = modifier, + colors = buttonsColors, + ) { + Row( + verticalAlignment = Alignment.CenterVertically + ) { + Icon( + imageVector, + contentDescription = null, + tint = imageVectorColor, + modifier = Modifier.size(28.dp) + ) + Spacer(modifier = Modifier.width(8.dp)) + Text( + text = text, + style = MaterialTheme.typography.headlineMedium.copy( + color = textColor, + fontWeight = FontWeight.Bold, + fontSize = 16.sp + ) + ) + } + } +} \ No newline at end of file diff --git a/feature-detail/src/androidMain/kotlin/detail/presentation/widget/DetailStreamImagePreview.kt b/feature-detail/src/androidMain/kotlin/detail/presentation/widget/DetailStreamImagePreview.kt new file mode 100644 index 00000000..4d95b242 --- /dev/null +++ b/feature-detail/src/androidMain/kotlin/detail/presentation/widget/DetailStreamImagePreview.kt @@ -0,0 +1,71 @@ +package detail.presentation.widget + +import androidx.compose.foundation.background +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.aspectRatio +import androidx.compose.foundation.layout.fillMaxHeight +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.shape.CircleShape +import androidx.compose.material3.Icon +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.layout.ContentScale +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.unit.dp +import coil.compose.AsyncImage +import com.codandotv.streamplayerapp.core_shared_ui.widget.PlayerComponent +import com.codandotv.streamplayerapp.feature.detail.R +import detail.presentation.screens.DetailStreamsUIState.DetailStreamsLoadedUIState + +@Suppress("MagicNumber") +@Composable +fun DetailStreamImagePreview( + uiState: DetailStreamsLoadedUIState, + modifier: Modifier = Modifier, + showPlayer: Boolean = false, + onPlayEvent: (() -> Unit) +) { + Box( + modifier = modifier + .fillMaxWidth() + .aspectRatio(16f / 9f), + contentAlignment = Alignment.Center + ) { + if (showPlayer) { + PlayerComponent( + videoId = uiState.videoId ?: "" + ) + } else { + AsyncImage( + model = uiState.detailStream.url, + contentScale = ContentScale.FillBounds, + contentDescription = uiState.detailStream.tagline, + modifier = Modifier + .fillMaxWidth() + .fillMaxHeight() + ) + + Box( + modifier = Modifier + .background(Color.Black.copy(alpha = 0.5f), CircleShape) + .size(50.dp) + .align(Alignment.Center), + ) + Icon( + painter = painterResource(id = R.drawable.play_circle), + tint = Color.White, + contentDescription = null, + modifier = Modifier + .size(64.dp) + .align(Alignment.Center) + .clickable { + onPlayEvent() + } + ) + } + } +} \ No newline at end of file diff --git a/feature-detail/src/androidMain/kotlin/detail/presentation/widget/DetailStreamRowHeader.kt b/feature-detail/src/androidMain/kotlin/detail/presentation/widget/DetailStreamRowHeader.kt new file mode 100644 index 00000000..a4649cb6 --- /dev/null +++ b/feature-detail/src/androidMain/kotlin/detail/presentation/widget/DetailStreamRowHeader.kt @@ -0,0 +1,49 @@ +package detail.presentation.widget + +import androidx.compose.foundation.Image +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.offset +import androidx.compose.foundation.layout.size +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.em +import androidx.compose.ui.unit.sp +import com.codandotv.streamplayerapp.feature.detail.R + +@Composable +fun DetailStreamRowHeader( + modifier: Modifier = Modifier.fillMaxWidth() +) { + Row( + verticalAlignment = Alignment.CenterVertically, + modifier = modifier + ) { + Image( + painter = painterResource(id = R.drawable.netflix_detail), + contentDescription = null, + modifier = Modifier + .size(26.dp) + .offset(x = (-6).dp) + + ) + Text( + text = stringResource(id = R.string.detail_movie), + modifier = Modifier.offset(x = (-6).dp), + style = MaterialTheme.typography.headlineMedium.copy( + color = Color.Gray, + fontWeight = FontWeight.Bold, + fontSize = 14.sp, + letterSpacing = 0.3.em + ) + ) + } +} \ No newline at end of file diff --git a/feature-detail/src/androidMain/kotlin/detail/presentation/widget/DetailStreamToolbar.kt b/feature-detail/src/androidMain/kotlin/detail/presentation/widget/DetailStreamToolbar.kt new file mode 100644 index 00000000..abc397ef --- /dev/null +++ b/feature-detail/src/androidMain/kotlin/detail/presentation/widget/DetailStreamToolbar.kt @@ -0,0 +1,55 @@ +package detail.presentation.widget + +import androidx.compose.foundation.Image +import androidx.compose.foundation.layout.height +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.ArrowBack +import androidx.compose.material.icons.filled.Search +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.Icon +import androidx.compose.material3.IconButton +import androidx.compose.material3.Text +import androidx.compose.material3.TopAppBar +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.unit.dp +import androidx.navigation.NavController +import com.codandotv.streamplayerapp.feature.detail.R + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun DetailStreamToolbar( + navController: NavController, + onNavigateSearchScreen: () -> Unit = {} +) { + TopAppBar( + title = { Text(text = "") }, + modifier = Modifier.height(56.dp), + navigationIcon = { + IconButton(onClick = { navController.navigateUp() }) { + Icon( + imageVector = Icons.Filled.ArrowBack, + contentDescription = stringResource(id = R.string.detail_back) + ) + } + }, actions = { + IconButton(onClick = { + onNavigateSearchScreen.invoke() + }) { + Icon( + imageVector = Icons.Default.Search, + tint = Color.White, + contentDescription = stringResource(id = R.string.detail_search) + ) + } + IconButton(onClick = { }) { + Image( + painter = painterResource(id = com.codandotv.streamplayerapp.core.shared.ui.R.drawable.perfil_fake), + contentDescription = null + ) + } + }) +} \ No newline at end of file diff --git a/feature-detail/src/androidMain/res/drawable/netflix_detail.webp b/feature-detail/src/androidMain/res/drawable/netflix_detail.webp new file mode 100644 index 0000000000000000000000000000000000000000..fcd5b3a4cadf673e393c1bcba48c4e475c464389 GIT binary patch literal 4428 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 S+f?|DTeyiUIDjj#5dZ*@hVd5w literal 0 HcmV?d00001 diff --git a/feature-detail/src/androidMain/res/drawable/play_circle.xml b/feature-detail/src/androidMain/res/drawable/play_circle.xml new file mode 100644 index 00000000..1a68c27f --- /dev/null +++ b/feature-detail/src/androidMain/res/drawable/play_circle.xml @@ -0,0 +1,9 @@ + + + diff --git a/feature-list-streams/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_list_streams/core/ContentType.kt b/feature-list-streams/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_list_streams/core/ContentType.kt new file mode 100644 index 00000000..eef6a23b --- /dev/null +++ b/feature-list-streams/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_list_streams/core/ContentType.kt @@ -0,0 +1,17 @@ +package com.codandotv.streamplayerapp.feature_list_streams.core + +import androidx.annotation.StringRes +import com.codandotv.streamplayerapp.feature.list.streams.R + +enum class ContentType(@StringRes val contentName: Int, @StringRes val contentNameAsPlural: Int) { + SHOW(R.string.list_content_type_show, R.string.list_content_type_show_plural), + FILM(R.string.list_content_type_film, R.string.list_content_type_film_plural); + + companion object { + fun getContentName(contentType: ContentType) = + values().first { contentType == it }.contentName + + fun getContentNameAsPlural(contentType: ContentType) = + values().first { contentType == it }.contentNameAsPlural + } +} \ No newline at end of file diff --git a/feature-list-streams/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_list_streams/list/data/ListStreamRepository.kt b/feature-list-streams/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_list_streams/list/data/ListStreamRepository.kt new file mode 100644 index 00000000..42a14d44 --- /dev/null +++ b/feature-list-streams/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_list_streams/list/data/ListStreamRepository.kt @@ -0,0 +1,53 @@ +package com.codandotv.streamplayerapp.feature_list_streams.list.data + +import androidx.paging.Pager +import androidx.paging.PagingConfig +import androidx.paging.PagingData +import com.codandotv.streamplayerapp.core_networking.handleError.toFlow +import com.codandotv.streamplayerapp.feature_list_streams.list.domain.model.Genre +import com.codandotv.streamplayerapp.feature_list_streams.list.domain.model.Stream +import com.codandotv.streamplayerapp.feature_list_streams.list.domain.toGenres +import com.codandotv.streamplayerapp.feature_list_streams.list.domain.toStream +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.map +import org.koin.core.annotation.Factory + +interface ListStreamRepository { + suspend fun getGenres(): Flow> + + suspend fun topRatedStream(): Flow + + fun loadMovies(genre: Genre): Flow> +} + +@Factory +class ListStreamRepositoryImpl( + private val service: ListStreamService, +) : ListStreamRepository { + + override suspend fun getGenres(): Flow> { + return service.getGenres().toFlow().map { it.toGenres() } + } + + override suspend fun topRatedStream() = service.getTopRatedMovies().toFlow().map { + it.results.first { it.poster_path != null }.toStream() + } + + override fun loadMovies(genre: Genre): Flow> { + return Pager( + config = PagingConfig( + pageSize = PAGE_SIZE, + maxSize = MAX_SIZE, + ), + pagingSourceFactory = { + StreamDataSource(service, genreName = genre.name, genreId = genre.id) + }, + initialKey = 1 + ).flow + } + + companion object { + private const val PAGE_SIZE = 20 + private const val MAX_SIZE = 500 + } +} \ No newline at end of file diff --git a/feature-list-streams/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_list_streams/list/data/ListStreamService.kt b/feature-list-streams/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_list_streams/list/data/ListStreamService.kt new file mode 100644 index 00000000..e2634895 --- /dev/null +++ b/feature-list-streams/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_list_streams/list/data/ListStreamService.kt @@ -0,0 +1,59 @@ +package com.codandotv.streamplayerapp.feature_list_streams.list.data + +import ListStreamResponse +import com.codandotv.streamplayerapp.core_networking.handleError.NetworkResponse +import com.codandotv.streamplayerapp.core_networking.handleError.safeRequest +import com.codandotv.streamplayerapp.feature_list_streams.list.data.model.GenresResponse +import io.ktor.client.HttpClient +import io.ktor.client.request.parameter +import io.ktor.client.request.url + +interface ListStreamService { + suspend fun getMovies(genres: String): NetworkResponse + suspend fun getPaginatedMovies(genres: String, page: Int): NetworkResponse + suspend fun getGenres(): NetworkResponse + suspend fun getTopRatedMovies( + sortBy: String = "vote_average.desc", + page: Int = 1 + ): NetworkResponse +} + +class ListStreamServiceImpl( + private val client: HttpClient +) : ListStreamService { + + override suspend fun getMovies(genres: String): NetworkResponse { + return client.safeRequest { + url("discover/movie") + parameter("with_genres", genres) + } + } + + override suspend fun getPaginatedMovies( + genres: String, + page: Int + ): NetworkResponse { + return client.safeRequest { + url("discover/movie") + parameter("with_genres", genres) + parameter("page", page) + } + } + + override suspend fun getGenres(): NetworkResponse { + return client.safeRequest { + url("genre/movie/list") + } + } + + override suspend fun getTopRatedMovies( + sortBy: String, + page: Int + ): NetworkResponse { + return client.safeRequest { + url("discover/movie") + parameter("sort_by", sortBy) + parameter("page", page) + } + } +} diff --git a/feature-list-streams/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_list_streams/list/data/StreamDataSource.kt b/feature-list-streams/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_list_streams/list/data/StreamDataSource.kt new file mode 100644 index 00000000..feec9ad6 --- /dev/null +++ b/feature-list-streams/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_list_streams/list/data/StreamDataSource.kt @@ -0,0 +1,44 @@ +package com.codandotv.streamplayerapp.feature_list_streams.list.data + +import androidx.paging.PagingSource +import androidx.paging.PagingState +import com.codandotv.streamplayerapp.core_networking.handleError.NetworkResponse +import com.codandotv.streamplayerapp.feature_list_streams.list.domain.model.Stream +import com.codandotv.streamplayerapp.feature_list_streams.list.domain.toListStream + +@Suppress("TooGenericExceptionCaught", "UseCheckOrError") +class StreamDataSource( + private val service: ListStreamService, + private val genreId: Long, + private val genreName: String, +) : PagingSource() { + + override suspend fun load(params: LoadParams): LoadResult { + val nextPageNumber = params.key ?: START_PAGE_INDEX + + return try { + val response = service.getPaginatedMovies( + genres = genreId.toString(), + page = nextPageNumber + ) + + if (response is NetworkResponse.Success) { + LoadResult.Page( + data = response.value.toListStream(genreName).streams, + prevKey = if (nextPageNumber > 1) nextPageNumber - 1 else null, + nextKey = nextPageNumber.plus(1) + ) + } else { + throw IllegalStateException("Something wrong") + } + } catch (exception: Exception) { + LoadResult.Error(exception) + } + } + + override fun getRefreshKey(state: PagingState): Int? = null + + companion object { + private const val START_PAGE_INDEX = 1 + } +} \ No newline at end of file diff --git a/feature-list-streams/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_list_streams/list/data/model/GenresResponse.kt b/feature-list-streams/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_list_streams/list/data/model/GenresResponse.kt new file mode 100644 index 00000000..3533dcdd --- /dev/null +++ b/feature-list-streams/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_list_streams/list/data/model/GenresResponse.kt @@ -0,0 +1,14 @@ +package com.codandotv.streamplayerapp.feature_list_streams.list.data.model + +import kotlinx.serialization.Serializable + +@Serializable +data class GenreResponse( + val id: Long, + val name: String +) + +@Serializable +data class GenresResponse( + val genres: List +) \ No newline at end of file diff --git a/feature-list-streams/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_list_streams/list/data/model/ListStreamResponse.kt b/feature-list-streams/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_list_streams/list/data/model/ListStreamResponse.kt new file mode 100644 index 00000000..89574e2c --- /dev/null +++ b/feature-list-streams/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_list_streams/list/data/model/ListStreamResponse.kt @@ -0,0 +1,14 @@ +import kotlinx.serialization.Serializable + +@Serializable +data class StreamResponse( + val id: Int, + val title: String, + val overview: String, + val poster_path: String? = null +) + +@Serializable +data class ListStreamResponse( + val results: List +) diff --git a/feature-list-streams/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_list_streams/list/di/ListStreamModule.kt b/feature-list-streams/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_list_streams/list/di/ListStreamModule.kt new file mode 100644 index 00000000..6e300eef --- /dev/null +++ b/feature-list-streams/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_list_streams/list/di/ListStreamModule.kt @@ -0,0 +1,21 @@ +package com.codandotv.streamplayerapp.feature_list_streams.list.di + +import com.codandotv.streamplayerapp.feature_list_streams.list.data.ListStreamService +import com.codandotv.streamplayerapp.feature_list_streams.list.data.ListStreamServiceImpl +import io.ktor.client.HttpClient +import org.koin.core.annotation.ComponentScan +import org.koin.core.annotation.Factory +import org.koin.core.annotation.Module +import org.koin.core.context.GlobalContext + +@Module +@ComponentScan("com.codandotv.streamplayerapp.feature_list_streams.list") +class ListStreamModule { + + @Factory + fun service(): ListStreamService { + val koin = GlobalContext.get() + val httpClient = koin.get() + return ListStreamServiceImpl(httpClient) + } +} \ No newline at end of file diff --git a/feature-list-streams/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_list_streams/list/domain/GetGenresUseCase.kt b/feature-list-streams/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_list_streams/list/domain/GetGenresUseCase.kt new file mode 100644 index 00000000..36b2b4d9 --- /dev/null +++ b/feature-list-streams/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_list_streams/list/domain/GetGenresUseCase.kt @@ -0,0 +1,19 @@ +package com.codandotv.streamplayerapp.feature_list_streams.list.domain + +import com.codandotv.streamplayerapp.feature_list_streams.list.data.ListStreamRepository +import com.codandotv.streamplayerapp.feature_list_streams.list.domain.model.Genre +import kotlinx.coroutines.flow.Flow +import org.koin.core.annotation.Factory + +interface GetGenresUseCase { + suspend operator fun invoke(): Flow> +} + +@Factory +class GetGenresUseCaseImpl( + private val repository: ListStreamRepository +) : GetGenresUseCase { + override suspend fun invoke(): Flow> { + return repository.getGenres() + } +} \ No newline at end of file diff --git a/feature-list-streams/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_list_streams/list/domain/GetLatestMovieUseCase.kt b/feature-list-streams/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_list_streams/list/domain/GetLatestMovieUseCase.kt new file mode 100644 index 00000000..7257a40c --- /dev/null +++ b/feature-list-streams/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_list_streams/list/domain/GetLatestMovieUseCase.kt @@ -0,0 +1,19 @@ +package com.codandotv.streamplayerapp.feature_list_streams.list.domain + +import com.codandotv.streamplayerapp.feature_list_streams.list.data.ListStreamRepository +import com.codandotv.streamplayerapp.feature_list_streams.list.domain.model.Stream +import kotlinx.coroutines.flow.Flow +import org.koin.core.annotation.Factory + +interface GetTopRatedStream { + suspend operator fun invoke(): Flow +} + +@Factory +class GetTopRatedStreamImpl( + private val repository: ListStreamRepository +) : GetTopRatedStream { + override suspend operator fun invoke(): Flow { + return repository.topRatedStream() + } +} \ No newline at end of file diff --git a/feature-list-streams/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_list_streams/list/domain/ListMovieUseCase.kt b/feature-list-streams/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_list_streams/list/domain/ListMovieUseCase.kt new file mode 100644 index 00000000..367f47be --- /dev/null +++ b/feature-list-streams/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_list_streams/list/domain/ListMovieUseCase.kt @@ -0,0 +1,21 @@ +package com.codandotv.streamplayerapp.feature_list_streams.list.domain + +import androidx.paging.PagingData +import com.codandotv.streamplayerapp.feature_list_streams.list.data.ListStreamRepository +import com.codandotv.streamplayerapp.feature_list_streams.list.domain.model.Genre +import com.codandotv.streamplayerapp.feature_list_streams.list.domain.model.Stream +import kotlinx.coroutines.flow.Flow +import org.koin.core.annotation.Factory + +interface ListStreamUseCase { + operator fun invoke(genre: Genre): Flow> +} + +@Factory +class ListStreamUseCaseImpl( + private val repository: ListStreamRepository +) : ListStreamUseCase { + override operator fun invoke(genre: Genre): Flow> { + return repository.loadMovies(genre) + } +} \ No newline at end of file diff --git a/feature-list-streams/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_list_streams/list/domain/ListStreamAnalytics.kt b/feature-list-streams/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_list_streams/list/domain/ListStreamAnalytics.kt new file mode 100644 index 00000000..e599c1fa --- /dev/null +++ b/feature-list-streams/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_list_streams/list/domain/ListStreamAnalytics.kt @@ -0,0 +1,8 @@ +package com.codandotv.streamplayerapp.feature_list_streams.list.domain + +import org.koin.core.annotation.Factory + +interface ListStreamAnalytics + +@Factory +class ListStreamAnalyticsImpl : ListStreamAnalytics \ No newline at end of file diff --git a/feature-list-streams/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_list_streams/list/domain/ListStreamMapper.kt b/feature-list-streams/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_list_streams/list/domain/ListStreamMapper.kt new file mode 100644 index 00000000..105d61a4 --- /dev/null +++ b/feature-list-streams/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_list_streams/list/domain/ListStreamMapper.kt @@ -0,0 +1,28 @@ +package com.codandotv.streamplayerapp.feature_list_streams.list.domain + +import ListStreamResponse +import StreamResponse +import com.codandotv.streamplayerapp.core_shared.Url +import com.codandotv.streamplayerapp.feature_list_streams.list.data.model.GenresResponse +import com.codandotv.streamplayerapp.feature_list_streams.list.domain.model.Genre +import com.codandotv.streamplayerapp.feature_list_streams.list.domain.model.ListStream +import com.codandotv.streamplayerapp.feature_list_streams.list.domain.model.Stream + +fun ListStreamResponse.toListStream(genre: String): ListStream = + ListStream( + categoryName = genre, + streams = this.results.map { streamResponse -> + streamResponse.toStream() + } + ) + +fun GenresResponse.toGenres(): List = this.genres.map { genreResponse -> + Genre(id = genreResponse.id, name = genreResponse.name) +} + +fun StreamResponse.toStream(): Stream = Stream( + description = overview, + name = title, + posterPathUrl = "${Url.IMAGE_URL_SIZE_300}${poster_path}", + id = id.toString() +) \ No newline at end of file diff --git a/feature-list-streams/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_list_streams/list/domain/model/Genre.kt b/feature-list-streams/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_list_streams/list/domain/model/Genre.kt new file mode 100644 index 00000000..aa3c00ee --- /dev/null +++ b/feature-list-streams/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_list_streams/list/domain/model/Genre.kt @@ -0,0 +1,6 @@ +package com.codandotv.streamplayerapp.feature_list_streams.list.domain.model + +data class Genre( + val id: Long, + val name: String +) diff --git a/feature-list-streams/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_list_streams/list/domain/model/HighlightBanner.kt b/feature-list-streams/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_list_streams/list/domain/model/HighlightBanner.kt new file mode 100644 index 00000000..7bdd369d --- /dev/null +++ b/feature-list-streams/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_list_streams/list/domain/model/HighlightBanner.kt @@ -0,0 +1,24 @@ +package com.codandotv.streamplayerapp.feature_list_streams.list.domain.model + +import android.os.Parcelable +import androidx.annotation.DrawableRes +import androidx.annotation.StringRes +import kotlinx.parcelize.Parcelize + +@Parcelize +data class HighlightBanner( + val name: String, + val imageUrl: String, + val contentType: Int, + val contentTypeAsPlural: Int, + val extraInfo: IconAndTextInfo, + val leftButton: IconAndTextInfo, + val centralButton: IconAndTextInfo, + val rightButton: IconAndTextInfo +) : Parcelable + +@Parcelize +data class IconAndTextInfo( + @DrawableRes val icon: Int, + @StringRes val text: Int +) : Parcelable \ No newline at end of file diff --git a/feature-list-streams/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_list_streams/list/domain/model/ListStream.kt b/feature-list-streams/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_list_streams/list/domain/model/ListStream.kt new file mode 100644 index 00000000..efebe7e1 --- /dev/null +++ b/feature-list-streams/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_list_streams/list/domain/model/ListStream.kt @@ -0,0 +1,12 @@ +package com.codandotv.streamplayerapp.feature_list_streams.list.domain.model + +data class Stream( + val id : String, + val name : String, + val description : String, + val posterPathUrl: String, +) +data class ListStream( + val categoryName: String, + val streams: List +) \ No newline at end of file diff --git a/feature-list-streams/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_list_streams/list/presentation/navigation/ListStreamsNavigation.kt b/feature-list-streams/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_list_streams/list/presentation/navigation/ListStreamsNavigation.kt new file mode 100644 index 00000000..ac58e2be --- /dev/null +++ b/feature-list-streams/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_list_streams/list/presentation/navigation/ListStreamsNavigation.kt @@ -0,0 +1,43 @@ +package com.codandotv.streamplayerapp.feature_list_streams.list.presentation.navigation + +import androidx.activity.compose.BackHandler +import androidx.lifecycle.Lifecycle +import androidx.navigation.NavGraphBuilder +import androidx.navigation.NavHostController +import androidx.navigation.compose.composable +import com.codandotv.streamplayerapp.core_navigation.routes.BottomNavRoutes.HOME_COMPLETE +import com.codandotv.streamplayerapp.core_navigation.routes.BottomNavRoutes.PARAM.PROFILE_ID +import com.codandotv.streamplayerapp.core_navigation.routes.Routes +import com.codandotv.streamplayerapp.core_navigation.routes.Routes.DETAIL +import com.codandotv.streamplayerapp.core_navigation.routes.Routes.PROFILE_PICKER +import com.codandotv.streamplayerapp.feature_list_streams.list.di.ListStreamModule +import com.codandotv.streamplayerapp.feature_list_streams.list.presentation.screens.ListStreamsScreen +import org.koin.core.context.loadKoinModules +import org.koin.core.context.unloadKoinModules +import org.koin.ksp.generated.module + +internal const val DEFAULT_ID = "" + +fun NavGraphBuilder.listStreamsNavGraph(navController: NavHostController) { + composable(HOME_COMPLETE) { nav -> + BackHandler(true) {} + if (nav.lifecycle.currentState == Lifecycle.State.STARTED) { + loadKoinModules(ListStreamModule().module) + } + ListStreamsScreen(navController = navController, + onNavigateDetailList = { id -> + navController.navigate("${DETAIL}${id}") + }, + onNavigateProfilePicker = { + navController.navigate(PROFILE_PICKER) + }, + onNavigateSearchScreen = { + navController.navigate(Routes.SEARCH) + }, + disposable = { + unloadKoinModules(ListStreamModule().module) + }, + profilePicture = nav.arguments?.getString(PROFILE_ID) ?: DEFAULT_ID + ) + } +} diff --git a/feature-list-streams/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_list_streams/list/presentation/screens/ListStreamViewModel.kt b/feature-list-streams/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_list_streams/list/presentation/screens/ListStreamViewModel.kt new file mode 100644 index 00000000..d33a1ae7 --- /dev/null +++ b/feature-list-streams/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_list_streams/list/presentation/screens/ListStreamViewModel.kt @@ -0,0 +1,130 @@ +package com.codandotv.streamplayerapp.feature_list_streams.list.presentation.screens + +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import androidx.paging.cachedIn +import androidx.paging.map +import com.codandotv.streamplayerapp.core_networking.handleError.catchFailure +import com.codandotv.streamplayerapp.feature.list.streams.R +import com.codandotv.streamplayerapp.feature_list_streams.core.ContentType +import com.codandotv.streamplayerapp.feature_list_streams.list.domain.GetGenresUseCase +import com.codandotv.streamplayerapp.feature_list_streams.list.domain.GetTopRatedStream +import com.codandotv.streamplayerapp.feature_list_streams.list.domain.ListStreamUseCase +import com.codandotv.streamplayerapp.feature_list_streams.list.domain.model.Genre +import com.codandotv.streamplayerapp.feature_list_streams.list.domain.model.HighlightBanner +import com.codandotv.streamplayerapp.feature_list_streams.list.domain.model.IconAndTextInfo +import com.codandotv.streamplayerapp.feature_list_streams.list.domain.model.Stream +import com.codandotv.streamplayerapp.core_shared_ui.widget.StreamsCardContent +import com.codandotv.streamplayerapp.core_shared_ui.widget.StreamsCarouselContent +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.SharingStarted +import kotlinx.coroutines.flow.combine +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.onCompletion +import kotlinx.coroutines.flow.onStart +import kotlinx.coroutines.flow.stateIn +import kotlinx.coroutines.flow.update +import kotlinx.coroutines.launch +import org.koin.android.annotation.KoinViewModel +import com.codandotv.streamplayerapp.core.shared.ui.R as SharedUiR + +@KoinViewModel +class ListStreamViewModel( + private val listStreams: ListStreamUseCase, + private val listGenres: GetGenresUseCase, + private val latestStream: GetTopRatedStream +) : ViewModel() { + + private val _uiState = MutableStateFlow( + ListStreamsUIState( + streamsCarouselContent = emptyList(), + isLoading = false + ) + ) + val uiState = _uiState.stateIn( + viewModelScope, + SharingStarted.Eagerly, + initialValue = _uiState.value + ) + + init { + viewModelScope.launch { + latestStream() + .combine( + listGenres() + ) { latest, genres -> + Pair(latest, genres) + } + .onStart { + onLoading() + } + .onCompletion { loaded() } + .catchFailure { + println(">>>> ${it.errorMessage}") + } + .collect { pair -> + val (latest, genres) = pair + + _uiState.update { + it.copy( + streamsCarouselContent = genres.map { genreTarget -> + getStreamsByGenre(genreTarget) + }, + highlightBanner = getHighlightBanner(latest) + ) + } + } + } + } + + private fun getHighlightBanner(latest: Stream) = + HighlightBanner( + name = latest.name, + imageUrl = latest.posterPathUrl, + contentType = ContentType.getContentName(ContentType.FILM), + contentTypeAsPlural = ContentType.getContentNameAsPlural(ContentType.FILM), + extraInfo = IconAndTextInfo( + R.drawable.ic_top_10, + R.string.list_highlight_banner_stream_ranking + ), + leftButton = IconAndTextInfo( + SharedUiR.drawable.ic_add, + R.string.list_highlight_banner_add + ), + centralButton = IconAndTextInfo( + SharedUiR.drawable.ic_play, + R.string.list_highlight_banner_watch + ), + rightButton = IconAndTextInfo( + SharedUiR.drawable.ic_info, + R.string.list_highlight_banner_info + ), + ) + + private fun getStreamsByGenre(genre: Genre): StreamsCarouselContent { + return StreamsCarouselContent( + genre.name, + listStreams(genre).map { + it.map { stream -> + StreamsCardContent( + contentDescription = stream.name, + url = stream.posterPathUrl, + id = stream.id + ) + } + }.cachedIn(viewModelScope) + ) + } + + private fun loaded() { + this._uiState.update { + it.copy(isLoading = false) + } + } + + private fun onLoading() { + this._uiState.update { + it.copy(isLoading = true) + } + } +} \ No newline at end of file diff --git a/feature-list-streams/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_list_streams/list/presentation/screens/ListStreamsScreen.kt b/feature-list-streams/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_list_streams/list/presentation/screens/ListStreamsScreen.kt new file mode 100644 index 00000000..5916e5ee --- /dev/null +++ b/feature-list-streams/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_list_streams/list/presentation/screens/ListStreamsScreen.kt @@ -0,0 +1,115 @@ +package com.codandotv.streamplayerapp.feature_list_streams.list.presentation.screens + +import android.annotation.SuppressLint +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.verticalScroll +import androidx.compose.material3.CircularProgressIndicator +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Scaffold +import androidx.compose.material3.TopAppBarDefaults +import androidx.compose.material3.rememberTopAppBarState +import androidx.compose.runtime.Composable +import androidx.compose.runtime.DisposableEffect +import androidx.compose.runtime.getValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.input.nestedscroll.nestedScroll +import androidx.compose.ui.platform.LocalLifecycleOwner +import androidx.compose.ui.unit.dp +import androidx.lifecycle.compose.collectAsStateWithLifecycle +import androidx.navigation.NavController +import androidx.navigation.compose.rememberNavController +import com.codandotv.streamplayerapp.core_navigation.bottomnavigation.StreamPlayerBottomNavigation +import com.codandotv.streamplayerapp.core_shared_ui.theme.ThemePreviews +import com.codandotv.streamplayerapp.core_shared_ui.widget.StreamPlayerTopBar +import com.codandotv.streamplayerapp.feature_list_streams.list.presentation.widgets.HighlightBanner +import com.codandotv.streamplayerapp.core_shared_ui.widget.StreamsCarousel +import org.koin.androidx.compose.koinViewModel + +@Suppress("LongParameterList") +@SuppressLint("UnusedMaterial3ScaffoldPaddingParameter") +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun ListStreamsScreen( + viewModel: ListStreamViewModel = koinViewModel(), + navController: NavController, + onNavigateDetailList: (String) -> Unit = {}, + onNavigateProfilePicker: () -> Unit = {}, + onNavigateSearchScreen: () -> Unit = {}, + disposable: () -> Unit = {}, + profilePicture: String +) { + val uiState by viewModel.uiState.collectAsStateWithLifecycle() + val lifecycleOwner = LocalLifecycleOwner.current + val scrollBehavior = + TopAppBarDefaults.exitUntilCollapsedScrollBehavior(rememberTopAppBarState()) + val baseScrollState = rememberScrollState() + + DisposableEffect(lifecycleOwner) { + onDispose { + disposable.invoke() + } + } + + Scaffold( + modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection), + topBar = { + StreamPlayerTopBar( + scrollBehavior = scrollBehavior, + onNavigateProfilePicker = onNavigateProfilePicker, + onNavigateSearchScreen = onNavigateSearchScreen, + onSelectedProfilePicture = profilePicture + ) + }, + bottomBar = { + StreamPlayerBottomNavigation(navController = navController) + } + ) { paddingValues -> + Box( + modifier = Modifier + .padding(paddingValues) + .fillMaxSize() + .background(MaterialTheme.colorScheme.background) + ) { + if (uiState.isLoading) { + CircularProgressIndicator( + modifier = Modifier.align( + Alignment.Center + ) + ) + } else { + Column( + modifier = Modifier + .fillMaxSize() + .align(Alignment.TopCenter) + .verticalScroll(baseScrollState) + ) { + + HighlightBanner(data = uiState.highlightBanner) + + uiState.streamsCarouselContent.forEach { streamCarouselContent -> + StreamsCarousel( + content = streamCarouselContent, + onNavigateDetailList = onNavigateDetailList, + ) + Spacer(modifier = Modifier.height(12.dp)) + } + } + } + } + } +} + +@ThemePreviews +@Composable +fun ListStreamsScreenPreview() { + ListStreamsScreen(navController = rememberNavController(), profilePicture = "") +} diff --git a/feature-list-streams/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_list_streams/list/presentation/screens/ListStreamsUIState.kt b/feature-list-streams/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_list_streams/list/presentation/screens/ListStreamsUIState.kt new file mode 100644 index 00000000..5260d36c --- /dev/null +++ b/feature-list-streams/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_list_streams/list/presentation/screens/ListStreamsUIState.kt @@ -0,0 +1,10 @@ +package com.codandotv.streamplayerapp.feature_list_streams.list.presentation.screens + +import com.codandotv.streamplayerapp.feature_list_streams.list.domain.model.HighlightBanner +import com.codandotv.streamplayerapp.core_shared_ui.widget.StreamsCarouselContent + +data class ListStreamsUIState( + val highlightBanner: HighlightBanner? = null, + val streamsCarouselContent: List, + val isLoading: Boolean +) \ No newline at end of file diff --git a/feature-list-streams/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_list_streams/list/presentation/widgets/HighlightBanner.kt b/feature-list-streams/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_list_streams/list/presentation/widgets/HighlightBanner.kt new file mode 100644 index 00000000..bbeb30ec --- /dev/null +++ b/feature-list-streams/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_list_streams/list/presentation/widgets/HighlightBanner.kt @@ -0,0 +1,280 @@ +package com.codandotv.streamplayerapp.feature_list_streams.list.presentation.widgets + +import androidx.annotation.StringRes +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.defaultMinSize +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.layout.width +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material3.Button +import androidx.compose.material3.ButtonDefaults +import androidx.compose.material3.Icon +import androidx.compose.material3.IconButton +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Brush +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.layout.ContentScale +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.TextStyle +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import coil.compose.AsyncImage +import com.codandotv.streamplayerapp.core_shared_ui.theme.ThemePreviews +import com.codandotv.streamplayerapp.feature.list.streams.R +import com.codandotv.streamplayerapp.feature_list_streams.core.ContentType +import com.codandotv.streamplayerapp.feature_list_streams.list.domain.model.HighlightBanner +import com.codandotv.streamplayerapp.feature_list_streams.list.domain.model.IconAndTextInfo +import com.codandotv.streamplayerapp.core.shared.ui.R as RSharedUI + +@Suppress("MagicNumber") +@Composable +fun HighlightBanner(modifier: Modifier = Modifier, data: HighlightBanner?) { + data ?: return + + Box( + modifier = modifier + .fillMaxWidth() + .height(500.dp) + ) { + ContentImage(imageUrl = data.imageUrl) + BackgroundGradient() + Column( + modifier = Modifier + .align(Alignment.BottomEnd) + .padding(bottom = 16.dp), + horizontalAlignment = Alignment.CenterHorizontally + ) { + Spacer(modifier = Modifier.weight(1F)) + ContentType(contentType = data.contentType) + ContentName(name = data.name) + ContentRanking( + extraInfo = data.extraInfo, + contentTypeAsPlural = data.contentTypeAsPlural + ) + ActionButtons(Modifier.weight(0.3F), data) + } + } +} + +@Composable +fun ContentImage(modifier: Modifier = Modifier, imageUrl: String) { + AsyncImage( + modifier = modifier.fillMaxSize(), + model = imageUrl, + contentScale = ContentScale.Crop, + contentDescription = stringResource(id = R.string.list_highligh_banner_content) + ) +} + +@Composable +fun BackgroundGradient(modifier: Modifier = Modifier) { + Box( + modifier = modifier + .fillMaxSize() + .background( + brush = Brush.verticalGradient( + colors = listOf( + Color.Transparent, + Color.Black + ), + ) + ) + ) +} + +@Composable +fun ContentName(modifier: Modifier = Modifier, name: String) { + Text( + text = name, + fontSize = 24.sp, + modifier = modifier + .padding(horizontal = 50.dp) + .padding(vertical = 4.dp), + textAlign = TextAlign.Center, + color = MaterialTheme.colorScheme.onBackground + ) +} + +@Composable +fun ContentRanking( + modifier: Modifier = Modifier, + extraInfo: IconAndTextInfo, + @StringRes contentTypeAsPlural: Int +) { + Row(modifier = modifier, verticalAlignment = Alignment.CenterVertically) { + Icon( + painter = painterResource(id = extraInfo.icon), + contentDescription = stringResource(id = R.string.list_icon_highligh_banner_ranking), + modifier = Modifier.size(24.dp), + tint = Color.Unspecified + ) + Text( + text = stringResource( + id = extraInfo.text, + stringResource(contentTypeAsPlural).lowercase() + ), + Modifier.padding(start = 4.dp), + style = TextStyle(fontWeight = FontWeight.Bold), + fontSize = 14.sp, + color = MaterialTheme.colorScheme.onBackground + ) + } +} + +@Composable +fun ContentType(modifier: Modifier = Modifier, @StringRes contentType: Int) { + Row(modifier = modifier, verticalAlignment = Alignment.CenterVertically) { + Icon( + painter = painterResource(id = RSharedUI.drawable.ic_netflix), + contentDescription = stringResource(id = RSharedUI.string.icon_netflix), + modifier = Modifier.size(16.dp), + tint = Color.Unspecified + ) + Text( + text = stringResource(contentType).uppercase(), + Modifier.padding(start = 4.dp), + fontSize = 12.sp, + color = MaterialTheme.colorScheme.onBackground, + letterSpacing = 4.sp + ) + } + +} + +@Composable +fun ActionButtons(modifier: Modifier, data: HighlightBanner) { + Row(verticalAlignment = Alignment.CenterVertically, modifier = modifier) { + AddToListButton( + modifier = Modifier + .weight(1F) + .fillMaxSize(), data + ) { + /* todo */ + } + PlayButton { + /* todo */ + } + InfoButton( + modifier = Modifier + .weight(1F) + .fillMaxSize(), data + ) { + /* todo */ + } + } +} + +@Composable +fun AddToListButton( + modifier: Modifier = Modifier, + data: HighlightBanner, + onClick: () -> Unit +) { + IconButton(onClick = { onClick.invoke() }, modifier = modifier) { + Column(horizontalAlignment = Alignment.CenterHorizontally) { + Icon( + painter = painterResource(id = RSharedUI.drawable.ic_add), + contentDescription = stringResource(id = R.string.list_icon_add), + tint = Color.White, + ) + Text( + fontSize = 10.sp, + text = stringResource(id = data.leftButton.text), + color = Color.White, + ) + } + } +} + +@Composable +fun InfoButton( + modifier: Modifier = Modifier, + data: HighlightBanner, + onClick: () -> Unit +) { + IconButton(onClick = { onClick.invoke() }, modifier = modifier) { + Column(horizontalAlignment = Alignment.CenterHorizontally) { + Icon( + painter = painterResource(id = RSharedUI.drawable.ic_info), + contentDescription = stringResource(id = R.string.list_icon_info), + tint = Color.White + ) + Text( + text = stringResource(id = data.rightButton.text), + fontSize = 10.sp, + color = Color.White + ) + } + } +} + +@Composable +fun PlayButton( + modifier: Modifier = Modifier, + onClick: () -> Unit +) { + Button( + onClick = { onClick.invoke() }, + colors = ButtonDefaults.buttonColors(containerColor = Color.White), + shape = RoundedCornerShape(4.dp), + contentPadding = PaddingValues(horizontal = 8.dp), + modifier = modifier + .padding(16.dp) + .defaultMinSize( + minWidth = 28.dp, + minHeight = 28.dp + ) + ) { + Icon( + painter = painterResource(RSharedUI.drawable.ic_play), + contentDescription = stringResource(id = R.string.list_icon_play), + tint = Color.Black, + modifier = Modifier.padding(vertical = 8.dp) + ) + Spacer(modifier = Modifier.width(4.dp)) + Text( + text = stringResource(id = R.string.list_highlight_banner_watch), + color = Color.Black, + fontSize = 14.sp, + modifier = Modifier + .padding(bottom = 2.dp) + .padding(end = 8.dp) + ) + } +} + +@ThemePreviews +@Composable +fun HighlightBannerPreview() { + HighlightBanner( + data = HighlightBanner( + name = stringResource(id = RSharedUI.string.app_name), + imageUrl = String(), + contentType = ContentType.getContentName(ContentType.SHOW), + contentTypeAsPlural = ContentType.getContentNameAsPlural(ContentType.SHOW), + extraInfo = IconAndTextInfo( + R.drawable.ic_top_10, + ContentType.getContentName(ContentType.SHOW) + ), + leftButton = IconAndTextInfo(RSharedUI.drawable.ic_add, R.string.list_highlight_banner_add), + centralButton = IconAndTextInfo(RSharedUI.drawable.ic_play, R.string.list_highlight_banner_watch), + rightButton = IconAndTextInfo(RSharedUI.drawable.ic_info, R.string.list_highlight_banner_info), + ) + ) +} diff --git a/feature-list-streams/src/androidMain/res/drawable/ic_top_10.webp b/feature-list-streams/src/androidMain/res/drawable/ic_top_10.webp new file mode 100644 index 0000000000000000000000000000000000000000..e165119df7ba756fc18552ab94df550ee2d80d8e GIT binary patch literal 1518 zcmV)?%hS# zw;#iP;Mp}2Qk=itBZX_)p*hO0Qt19_s^xR0v2kR8sualGvd69+}7bWi2 zrqp3rp=kEOD7sr0=>iqf!Kj5Y3@wzCQ9@Z@D`ogFDRH+NWx@(YedrX~Eu|WMP^P=A z)V^1RG8~9+xsi^&niO%+{hD3rg+0lKA@4^;MhgMA2Y;3)Ya4&$$qw5sqo{-UTw;7E zIHgvyvp^MxgVL(~?P`HB} z)z_Byt<*l$DK0nlx83$^sB>6u>T}CGrq8l9^}A(m)u_IY%B)5G7hYZZj0dM`dx};V zH5DF3B*IkqR1_@hX764nNN;TFm@)0MIbwuMMWIrS*daQZ)FEQ@4iO$zMaBv+L1B)?$wy2x5cT0E}H~ z=aKDW4;0u9zW}uC&ynr(?k3`LkP++tkliK3=D-G^v*!eex$FR8PXr_<2SC}Qz{|-* z6y{_^c4COhoQW{E18^{B1q??5NX$t9adZNfz(@dL&I0IxA%OHxt{OZucxv#Ze>yCH z9vBHAfp-EH=2Sp)B!I-62~bB3a4;t$vKs|X<`|+nnTW!j2w0pP2;F~}l77to==VH{c! zIw7SY78Y#{F)E-&MHUuWM`*0Hu1GN+6^O<{sG`uxB!+@vP%$nQjOsYbJEcYi zPc~TsJ&H(E_-TCF2F__KjI#L@hjGf5Cpe8)Cf>!$tr|UR*|=r3dDXJ<%X0LnW#gFD z=*`H+GppSbsm3+4t9j54x+=UuLBMSX|g~aO=pEYwWtMo5)00w5{Z~y=R literal 0 HcmV?d00001 diff --git a/feature-list-streams/src/androidMain/res/drawable/image_placeholder.xml b/feature-list-streams/src/androidMain/res/drawable/image_placeholder.xml new file mode 100644 index 00000000..13e24402 --- /dev/null +++ b/feature-list-streams/src/androidMain/res/drawable/image_placeholder.xml @@ -0,0 +1,11 @@ + + + \ No newline at end of file diff --git a/feature-list-streams/src/androidMain/res/drawable/netflix_horizontal_logo.xml b/feature-list-streams/src/androidMain/res/drawable/netflix_horizontal_logo.xml new file mode 100644 index 00000000..e6367f02 --- /dev/null +++ b/feature-list-streams/src/androidMain/res/drawable/netflix_horizontal_logo.xml @@ -0,0 +1,9 @@ + + + diff --git a/feature-list-streams/src/androidMain/res/layout/activity_list_stream.xml b/feature-list-streams/src/androidMain/res/layout/activity_list_stream.xml new file mode 100644 index 00000000..5377cc83 --- /dev/null +++ b/feature-list-streams/src/androidMain/res/layout/activity_list_stream.xml @@ -0,0 +1,18 @@ + + + + + + \ No newline at end of file diff --git a/feature-list-streams/src/androidMain/res/values/content-description.xml b/feature-list-streams/src/androidMain/res/values/content-description.xml new file mode 100644 index 00000000..99cae6c4 --- /dev/null +++ b/feature-list-streams/src/androidMain/res/values/content-description.xml @@ -0,0 +1,8 @@ + + + Ícone Reprodução + Ícone adicionar + Ícone informações + Poster de conteúdo em destaque + Ícone top 10 + \ No newline at end of file diff --git a/feature-list-streams/src/androidMain/res/values/strings.xml b/feature-list-streams/src/androidMain/res/values/strings.xml new file mode 100644 index 00000000..85d4e9b4 --- /dev/null +++ b/feature-list-streams/src/androidMain/res/values/strings.xml @@ -0,0 +1,14 @@ + + + + Série + Séries + Filme + Filmes + + Minha lista + Saiba mais + Assistir + Top 1 em %s hoje + + \ No newline at end of file diff --git a/feature-search/.gitignore b/feature-search/.gitignore new file mode 100644 index 00000000..42afabfd --- /dev/null +++ b/feature-search/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/feature-search/build.gradle.kts b/feature-search/build.gradle.kts new file mode 100644 index 00000000..d06f8932 --- /dev/null +++ b/feature-search/build.gradle.kts @@ -0,0 +1,49 @@ +@file:Suppress("UnstableApiUsage") + +plugins { + id("com.streamplayer.kmp-library") + alias(libs.plugins.ksp) + alias(libs.plugins.jetbrains.compose) + alias(libs.plugins.compose.compiler) +} + +kotlin { + sourceSets { + commonMain.dependencies { + implementation(libs.koin.annotations) + implementation(libs.koin.core) + } + + androidMain.dependencies { + implementation(projects.coreNetworking) + implementation(projects.coreNavigation) + implementation(projects.coreShared) + implementation(projects.coreSharedUi) + implementation(projects.coreLocalStorage) + + implementation(compose.material3) + implementation(compose.ui) + implementation(compose.preview) + implementation(libs.paging.compose) + implementation(libs.navigation.compose) + implementation(libs.bundles.koin) + implementation(libs.bundles.networking) + implementation(libs.coil) + implementation(libs.koin.annotations) + implementation(libs.bundles.androidSupport) + + } + sourceSets.named("androidMain").configure { + kotlin.srcDir("build/generated/ksp/metadata/androidMain/kotlin") + } + } +} + +dependencies { + add("kspCommonMainMetadata",libs.koin.compiler) + add("kspAndroid", libs.koin.compiler) +} + +ksp { + arg("KOIN_CONFIG_CHECK","true") +} \ No newline at end of file diff --git a/feature-search/consumer-rules.pro b/feature-search/consumer-rules.pro new file mode 100644 index 00000000..e69de29b diff --git a/feature-search/proguard-rules.pro b/feature-search/proguard-rules.pro new file mode 100644 index 00000000..481bb434 --- /dev/null +++ b/feature-search/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/feature-search/src/androidMain/kotlin/search/data/api/MostPopularMoviesService.kt b/feature-search/src/androidMain/kotlin/search/data/api/MostPopularMoviesService.kt new file mode 100644 index 00000000..745d0cb6 --- /dev/null +++ b/feature-search/src/androidMain/kotlin/search/data/api/MostPopularMoviesService.kt @@ -0,0 +1,22 @@ +package search.data.api + +import com.codandotv.streamplayerapp.core_networking.handleError.NetworkResponse +import com.codandotv.streamplayerapp.core_networking.handleError.safeRequest +import search.data.model.ListSearchStreamResponse +import io.ktor.client.HttpClient +import io.ktor.client.call.body +import io.ktor.client.request.get +import io.ktor.client.request.url + +interface MostPopularMoviesService { + suspend fun getPopular(): NetworkResponse +} + +class MostPopularMoviesServiceImpl( + private val client: HttpClient +) : MostPopularMoviesService { + override suspend fun getPopular(): NetworkResponse = + client.safeRequest { + url("movie/popular") + } +} diff --git a/feature-search/src/androidMain/kotlin/search/data/api/SearchStreamService.kt b/feature-search/src/androidMain/kotlin/search/data/api/SearchStreamService.kt new file mode 100644 index 00000000..51d28216 --- /dev/null +++ b/feature-search/src/androidMain/kotlin/search/data/api/SearchStreamService.kt @@ -0,0 +1,22 @@ +package search.data.api + +import com.codandotv.streamplayerapp.core_networking.handleError.NetworkResponse +import com.codandotv.streamplayerapp.core_networking.handleError.safeRequest +import search.data.model.ListSearchStreamResponse +import io.ktor.client.HttpClient +import io.ktor.client.request.parameter +import io.ktor.client.request.url + +interface SearchStreamService { + suspend fun getSearch(query: String): NetworkResponse +} + +class SearchStreamServiceImpl( + private val client: HttpClient +) : SearchStreamService { + override suspend fun getSearch(query: String): NetworkResponse = + client.safeRequest { + url("search/movie") + parameter("query", query) + } +} diff --git a/feature-search/src/androidMain/kotlin/search/data/datasource/MostPopularMoviesDataSource.kt b/feature-search/src/androidMain/kotlin/search/data/datasource/MostPopularMoviesDataSource.kt new file mode 100644 index 00000000..562339f8 --- /dev/null +++ b/feature-search/src/androidMain/kotlin/search/data/datasource/MostPopularMoviesDataSource.kt @@ -0,0 +1,18 @@ +package search.data.datasource + +import com.codandotv.streamplayerapp.core_networking.handleError.toFlow +import search.data.model.ListSearchStreamResponse +import search.data.api.MostPopularMoviesService +import kotlinx.coroutines.flow.Flow + +interface MostPopularMoviesDataSource { + suspend fun getMostPopularMovies(): Flow +} + +class MostPopularMoviesDataSourceImpl( + private val service: MostPopularMoviesService +) : MostPopularMoviesDataSource { + + override suspend fun getMostPopularMovies(): Flow = + service.getPopular().toFlow() +} diff --git a/feature-search/src/androidMain/kotlin/search/data/datasource/SearchStreamDataSource.kt b/feature-search/src/androidMain/kotlin/search/data/datasource/SearchStreamDataSource.kt new file mode 100644 index 00000000..d40f6727 --- /dev/null +++ b/feature-search/src/androidMain/kotlin/search/data/datasource/SearchStreamDataSource.kt @@ -0,0 +1,17 @@ +package search.data.datasource + +import com.codandotv.streamplayerapp.core_networking.handleError.toFlow +import search.data.model.ListSearchStreamResponse +import search.data.api.SearchStreamService +import kotlinx.coroutines.flow.Flow + +interface SearchStreamDataSource { + suspend fun getMovieSearch(query: String): Flow +} +class SearchStreamDataSourceImpl( + private val service: SearchStreamService +): SearchStreamDataSource { + + override suspend fun getMovieSearch(query:String): Flow = + service.getSearch(query = query).toFlow() +} diff --git a/feature-search/src/androidMain/kotlin/search/data/model/ListSearchStreamResponse.kt b/feature-search/src/androidMain/kotlin/search/data/model/ListSearchStreamResponse.kt new file mode 100644 index 00000000..27ee89c4 --- /dev/null +++ b/feature-search/src/androidMain/kotlin/search/data/model/ListSearchStreamResponse.kt @@ -0,0 +1,22 @@ +package search.data.model + +import com.squareup.moshi.Json +import kotlinx.serialization.Serializable + +@Serializable +data class ListSearchStreamResponse( + @Json(name = "results") + val results: List +) { + @Serializable + data class SearchStreamResponse( + @Json(name = "id") + val id: Int, + @Json(name = "title") + val title: String, + @Json(name="overview") + val overview: String, + @Json(name = "poster_path") + val posterPath: String? = null + ) +} diff --git a/feature-search/src/androidMain/kotlin/search/data/repository/MostPopularMoviesRepository.kt b/feature-search/src/androidMain/kotlin/search/data/repository/MostPopularMoviesRepository.kt new file mode 100644 index 00000000..fca60f45 --- /dev/null +++ b/feature-search/src/androidMain/kotlin/search/data/repository/MostPopularMoviesRepository.kt @@ -0,0 +1,16 @@ +package search.data.repository + +import search.data.model.ListSearchStreamResponse +import search.data.datasource.MostPopularMoviesDataSource +import kotlinx.coroutines.flow.Flow + +interface MostPopularMoviesRepository { + suspend fun getMostPopularMovies(): Flow +} + +class MostPopularMoviesRepositoryImpl( + private val dataSource: MostPopularMoviesDataSource +) : MostPopularMoviesRepository { + override suspend fun getMostPopularMovies(): Flow = + dataSource.getMostPopularMovies() +} diff --git a/feature-search/src/androidMain/kotlin/search/data/repository/SearchStreamRepository.kt b/feature-search/src/androidMain/kotlin/search/data/repository/SearchStreamRepository.kt new file mode 100644 index 00000000..aba513d1 --- /dev/null +++ b/feature-search/src/androidMain/kotlin/search/data/repository/SearchStreamRepository.kt @@ -0,0 +1,16 @@ +package search.data.repository + +import search.data.datasource.SearchStreamDataSource +import search.data.model.ListSearchStreamResponse +import kotlinx.coroutines.flow.Flow + +interface SearchStreamRepository { + suspend fun getMovieSearch(query: String) : Flow + +} +class SearchStreamRepositoryImp( + private val dataSource: SearchStreamDataSource +) : SearchStreamRepository { + override suspend fun getMovieSearch(query: String): Flow = + dataSource.getMovieSearch(query) +} diff --git a/feature-search/src/androidMain/kotlin/search/di/SearchModule.kt b/feature-search/src/androidMain/kotlin/search/di/SearchModule.kt new file mode 100644 index 00000000..4a55f059 --- /dev/null +++ b/feature-search/src/androidMain/kotlin/search/di/SearchModule.kt @@ -0,0 +1,43 @@ +package search.di + +import search.data.api.SearchStreamService +import search.data.datasource.SearchStreamDataSourceImpl +import search.data.datasource.MostPopularMoviesDataSource +import search.data.datasource.MostPopularMoviesDataSourceImpl +import search.data.repository.MostPopularMoviesRepository +import search.data.repository.MostPopularMoviesRepositoryImpl +import search.data.api.MostPopularMoviesService +import search.data.api.MostPopularMoviesServiceImpl +import search.data.api.SearchStreamServiceImpl +import search.data.datasource.SearchStreamDataSource +import search.data.repository.SearchStreamRepository +import search.data.repository.SearchStreamRepositoryImp +import search.domain.MostPopularMoviesUseCase +import search.domain.MostPopularMoviesUseCaseImpl +import search.domain.SearchUseCase +import search.domain.SearchUseCaseImpl +import search.presentation.screens.SearchViewModel +import org.koin.androidx.viewmodel.dsl.viewModel +import org.koin.dsl.module + +object SearchModule { + val module = module { + viewModel { + SearchViewModel( + searchUseCase = get(), + mostPopularMoviesUseCase = get() + ) + } + + factory { SearchStreamServiceImpl(get()) } + factory { MostPopularMoviesServiceImpl(get()) } + + factory { MostPopularMoviesUseCaseImpl(repository = get()) } + factory { MostPopularMoviesDataSourceImpl(service = get()) } + factory { MostPopularMoviesRepositoryImpl(dataSource = get()) } + + factory { SearchUseCaseImpl(repository = get()) } + factory { SearchStreamDataSourceImpl(service = get()) } + factory { SearchStreamRepositoryImp(dataSource = get()) } + } +} diff --git a/feature-search/src/androidMain/kotlin/search/domain/MostPopularMoviesUseCase.kt b/feature-search/src/androidMain/kotlin/search/domain/MostPopularMoviesUseCase.kt new file mode 100644 index 00000000..f3414d0c --- /dev/null +++ b/feature-search/src/androidMain/kotlin/search/domain/MostPopularMoviesUseCase.kt @@ -0,0 +1,17 @@ +package search.domain + +import search.data.model.ListSearchStreamResponse +import search.data.repository.MostPopularMoviesRepository +import kotlinx.coroutines.flow.Flow + +interface MostPopularMoviesUseCase { + suspend operator fun invoke(): Flow +} + +class MostPopularMoviesUseCaseImpl( + val repository: MostPopularMoviesRepository +) : MostPopularMoviesUseCase { + override suspend operator fun invoke(): Flow { + return repository.getMostPopularMovies() + } +} diff --git a/feature-search/src/androidMain/kotlin/search/domain/SearchUseCase.kt b/feature-search/src/androidMain/kotlin/search/domain/SearchUseCase.kt new file mode 100644 index 00000000..78e9578c --- /dev/null +++ b/feature-search/src/androidMain/kotlin/search/domain/SearchUseCase.kt @@ -0,0 +1,15 @@ +package search.domain + +import search.data.model.ListSearchStreamResponse +import search.data.repository.SearchStreamRepository +import kotlinx.coroutines.flow.Flow + +interface SearchUseCase { + suspend operator fun invoke(query:String): Flow +} + +class SearchUseCaseImpl(val repository: SearchStreamRepository) : SearchUseCase { + override suspend operator fun invoke(query:String): Flow { + return repository.getMovieSearch(query = query) + } +} diff --git a/feature-search/src/androidMain/kotlin/search/domain/mapper/SearchMapper.kt b/feature-search/src/androidMain/kotlin/search/domain/mapper/SearchMapper.kt new file mode 100644 index 00000000..72b8fa04 --- /dev/null +++ b/feature-search/src/androidMain/kotlin/search/domain/mapper/SearchMapper.kt @@ -0,0 +1,11 @@ +package search.domain.mapper + +import com.codandotv.streamplayerapp.core_shared.Url +import search.data.model.ListSearchStreamResponse.SearchStreamResponse +import search.presentation.widgets.SearchStreamCardModel + +fun SearchStreamResponse.toSearchStreamCardModel() = SearchStreamCardModel( + id = id.toString(), + title = title, + url = "${Url.IMAGE_URL_SIZE_200}${posterPath}" +) diff --git a/feature-search/src/androidMain/kotlin/search/presentation/navigation/SearchStreamNavigation.kt b/feature-search/src/androidMain/kotlin/search/presentation/navigation/SearchStreamNavigation.kt new file mode 100644 index 00000000..afc7bbbe --- /dev/null +++ b/feature-search/src/androidMain/kotlin/search/presentation/navigation/SearchStreamNavigation.kt @@ -0,0 +1,30 @@ +package search.presentation.navigation + +import androidx.activity.compose.BackHandler +import androidx.lifecycle.Lifecycle +import androidx.navigation.NavGraphBuilder +import androidx.navigation.NavHostController +import androidx.navigation.compose.composable +import com.codandotv.streamplayerapp.core_navigation.routes.Routes +import search.di.SearchModule +import search.presentation.screens.SearchScreen +import org.koin.core.context.loadKoinModules +import org.koin.core.context.unloadKoinModules + +fun NavGraphBuilder.searchStreamsNavGraph(navController: NavHostController) { + composable(Routes.SEARCH) { nav -> + BackHandler(true) {} + if (nav.lifecycle.currentState == Lifecycle.State.STARTED) { + loadKoinModules(SearchModule.module) + } + SearchScreen( + navController = navController, + onNavigateDetailList = { id -> + navController.navigate("${Routes.DETAIL}${id}") + }, + disposable = { + unloadKoinModules(SearchModule.module) + } + ) + } +} diff --git a/feature-search/src/androidMain/kotlin/search/presentation/screens/SearchScreen.kt b/feature-search/src/androidMain/kotlin/search/presentation/screens/SearchScreen.kt new file mode 100644 index 00000000..45d8da1b --- /dev/null +++ b/feature-search/src/androidMain/kotlin/search/presentation/screens/SearchScreen.kt @@ -0,0 +1,171 @@ +package search.presentation.screens + +import androidx.activity.compose.BackHandler +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.verticalScroll +import androidx.compose.material3.CircularProgressIndicator +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.Scaffold +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.DisposableEffect +import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.getValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.platform.LocalLifecycleOwner +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import androidx.lifecycle.LifecycleOwner +import androidx.navigation.NavController +import com.codandotv.streamplayerapp.core_navigation.extensions.goBack +import com.codandotv.streamplayerapp.feature.search.R +import search.domain.mapper.toSearchStreamCardModel +import search.presentation.widgets.SearchStreamCard +import search.presentation.widgets.SearchableTopBar +import search.presentation.widgets.StreamsEmpty +import search.presentation.widgets.StreamsError +import org.koin.androidx.compose.koinViewModel + +@Composable +fun SearchScreen( + viewModel: SearchViewModel = koinViewModel(), + onNavigateDetailList: (String) -> Unit = {}, + navController: NavController, + disposable: () -> Unit = {} +) { + + val uiState by viewModel.uiState.collectAsState() + Lifecycle( + lifecycleOwner = LocalLifecycleOwner.current, + viewModel = viewModel, + disposable = disposable + ) + + when (uiState) { + is SearchUIState.Success -> { + SetupSearchScreen( + navController = navController, + uiState = uiState as SearchUIState.Success, + viewModel = viewModel, + onNavigateDetailList = onNavigateDetailList + ) + } + + is SearchUIState.Error -> { + StreamsError( + onRetry = { viewModel.onTryAgain() }, + onCloseButton = { navController.goBack() } + ) + } + + is SearchUIState.Empty -> { + SetupSearchScreen( + navController = navController, + uiState = uiState as SearchUIState.Empty, + viewModel = viewModel, + onNavigateDetailList = onNavigateDetailList + ) + } + + else -> { + Box(Modifier.fillMaxSize()) { + CircularProgressIndicator( + modifier = Modifier.align( + Alignment.Center + ) + ) + } + } + } +} + +@Composable +private fun SetupSearchScreen( + onNavigateDetailList: (String) -> Unit = {}, + navController: NavController, + uiState: SearchUIState, + viewModel: SearchViewModel +) { + Scaffold( + topBar = { + val currentText by viewModel.currentSearchText.collectAsState() + SearchableTopBar( + currentSearchText = currentText, + onSearchTextChanged = { value -> + viewModel.setCurrentSearchText( + newText = value + ) + }, + onSearchDispatched = { + viewModel.fetchMovies() + }, + onSearchIconPressed = { + viewModel.fetchMovies() + }, + onBackPressed = { + navController.goBack() + }, + onCleanTextPressed = { + viewModel.onCleanText() + } + ) + } + ) { paddingValues -> + + if (uiState is SearchUIState.Success) { + Column( + modifier = Modifier + .padding(paddingValues) + .verticalScroll(rememberScrollState()), + ) { + Text( + text = stringResource(id = R.string.search_list_describle), + color = Color.White, + fontWeight = FontWeight.SemiBold, + fontSize = 20.sp, + modifier = Modifier.padding(vertical = 8.dp) + ) + uiState.listCharacters.results.map { + SearchStreamCard( + content = it.toSearchStreamCardModel(), + onSearchStreamPressed = { id -> + onNavigateDetailList(id) + } + ) + } + } + } else { + Box(modifier = Modifier.padding(paddingValues)) { + StreamsEmpty() + } + } + } + BackHandler { + navController.goBack() + } + +} + +@Composable +private fun Lifecycle( + lifecycleOwner: LifecycleOwner, viewModel: SearchViewModel, disposable: () -> Unit +) { + DisposableEffect(lifecycleOwner) { + val lifecycle = lifecycleOwner.lifecycle + + lifecycle.addObserver(viewModel) + + onDispose { + lifecycle.removeObserver(viewModel) + disposable.invoke() + } + } +} diff --git a/feature-search/src/androidMain/kotlin/search/presentation/screens/SearchUIState.kt b/feature-search/src/androidMain/kotlin/search/presentation/screens/SearchUIState.kt new file mode 100644 index 00000000..47ab8e68 --- /dev/null +++ b/feature-search/src/androidMain/kotlin/search/presentation/screens/SearchUIState.kt @@ -0,0 +1,10 @@ +package search.presentation.screens + +import search.data.model.ListSearchStreamResponse + +sealed class SearchUIState { + data class Success(val listCharacters: ListSearchStreamResponse) : SearchUIState() + data class Error(val messageError: String = String()) : SearchUIState() + object Loading : SearchUIState() + object Empty : SearchUIState() +} diff --git a/feature-search/src/androidMain/kotlin/search/presentation/screens/SearchViewModel.kt b/feature-search/src/androidMain/kotlin/search/presentation/screens/SearchViewModel.kt new file mode 100644 index 00000000..21d8b30d --- /dev/null +++ b/feature-search/src/androidMain/kotlin/search/presentation/screens/SearchViewModel.kt @@ -0,0 +1,113 @@ +package search.presentation.screens + +import androidx.lifecycle.DefaultLifecycleObserver +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import com.codandotv.streamplayerapp.core_networking.handleError.catchFailure +import search.domain.MostPopularMoviesUseCase +import search.domain.SearchUseCase +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.SharingStarted +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.flow.onStart +import kotlinx.coroutines.flow.stateIn +import kotlinx.coroutines.flow.update +import kotlinx.coroutines.launch + +class SearchViewModel( + private val searchUseCase: SearchUseCase, + private val mostPopularMoviesUseCase: MostPopularMoviesUseCase +) : ViewModel(), DefaultLifecycleObserver { + + private var tryAgain: () -> Unit = {} + + private val _uiState = MutableStateFlow(SearchUIState.Loading) + val uiState: StateFlow = _uiState.stateIn( + scope = viewModelScope, + started = SharingStarted.Eagerly, + initialValue = _uiState.value + ) + + private val _currentSearchText = MutableStateFlow("") + val currentSearchText = _currentSearchText.asStateFlow() + + init { + fetchMostPopularMovies() + } + + fun fetchMovies() { + if (_currentSearchText.value.isBlank()) { + fetchMostPopularMovies() + } else { + fetchMovieByQuery() + } + } + + private fun fetchMovieByQuery() { + tryAgain = ::fetchMovieByQuery + + viewModelScope.launch { + searchUseCase( + query = _currentSearchText.value + ).onStart { + showLoading() + }.catchFailure { response -> + showError(messageError = response.errorMessage.orEmpty()) + }.collect { result -> + _uiState.update { + if (result.results.isEmpty()) { + SearchUIState.Empty + } else { + SearchUIState.Success(result) + } + } + } + } + } + + private fun fetchMostPopularMovies() { + tryAgain = ::fetchMostPopularMovies + + viewModelScope.launch { + mostPopularMoviesUseCase().onStart { + showLoading() + }.catchFailure { response -> + showError(messageError = response.errorMessage.orEmpty()) + }.collect { result -> + _uiState.update { + if (result.results.isEmpty()) { + SearchUIState.Empty + } else { + SearchUIState.Success(result) + } + } + } + } + } + + private fun showError(messageError: String) { + _uiState.update { + SearchUIState.Error(messageError = messageError) + } + } + + private fun showLoading() { + _uiState.update { + SearchUIState.Loading + } + } + + fun onTryAgain() { + tryAgain() + } + + fun setCurrentSearchText(newText: String) { + _currentSearchText.value = newText + } + + fun onCleanText() { + _currentSearchText.value = "" + fetchMostPopularMovies() + } +} diff --git a/feature-search/src/androidMain/kotlin/search/presentation/widgets/SearchCarousel.kt b/feature-search/src/androidMain/kotlin/search/presentation/widgets/SearchCarousel.kt new file mode 100644 index 00000000..dc490347 --- /dev/null +++ b/feature-search/src/androidMain/kotlin/search/presentation/widgets/SearchCarousel.kt @@ -0,0 +1,165 @@ +package search.presentation.widgets + +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Close +import androidx.compose.material3.Button +import androidx.compose.material3.ButtonDefaults +import androidx.compose.material3.Icon +import androidx.compose.material3.IconButton +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import androidx.paging.PagingData +import androidx.paging.compose.collectAsLazyPagingItems +import androidx.paging.compose.itemContentType +import androidx.paging.compose.itemKey +import com.codandotv.streamplayerapp.core_shared_ui.widget.StreamsCard +import com.codandotv.streamplayerapp.core_shared_ui.widget.StreamsCardContent +import com.codandotv.streamplayerapp.feature.search.R +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.emptyFlow + +data class SearchCarousel( + val genreTitle: String, + val contentList: Flow> +) + +@Composable +fun SearchCarouselStream( + content: SearchCarousel, + onNavigateDetailList: (String) -> Unit = {}, + modifier: Modifier = Modifier +) { + val lazyPagingItems = content.contentList.collectAsLazyPagingItems() + + Text( + text = stringResource(id = R.string.search_list_describle), + color = Color.Black, + fontSize = 14.sp + ) + + Column { + LazyColumn( + modifier + .fillMaxSize() + .height(140.dp) + ) { + items( + count = lazyPagingItems.itemCount, + key = lazyPagingItems.itemKey(), + contentType = lazyPagingItems.itemContentType() + ) { index -> + val item = lazyPagingItems[index] + item?.let { + StreamsCard( + content = it, + onNavigateDetailList = onNavigateDetailList + ) + } + } + } + } +} + +@Composable +@Preview +fun StreamsCarouselPreview() { + SearchCarouselStream( + content = SearchCarousel( + genreTitle = "Comédia", + contentList = emptyFlow() + ) + ) +} + +@Composable +fun StreamsError( + onRetry: () -> Unit, + onCloseButton: () -> Unit +) { + Box( + modifier = Modifier + .fillMaxSize() + .padding(16.dp) + ) { + IconButton(onClick = { + onCloseButton() + }) { + Icon( + imageVector = Icons.Filled.Close, + contentDescription = stringResource(id = R.string.search_back), + tint = Color.White + ) + } + Text( + text = stringResource(id = R.string.search_list_error), + color = Color.White, + fontWeight = FontWeight.SemiBold, + fontSize = 20.sp, + textAlign= TextAlign.Center, + modifier = Modifier + .align(Alignment.Center) + ) + Spacer(modifier = Modifier.height(8.dp)) + Button( + onClick = onRetry, + modifier = Modifier + .fillMaxWidth() + .padding(8.dp) + .align(Alignment.BottomCenter), + enabled = true, + colors = ButtonDefaults.buttonColors( + contentColor = Color.White, + containerColor = Color.Red + ), + shape = RoundedCornerShape(8.dp), + ) { + Text(text = stringResource(id = R.string.bottom_search_list_error)) + } + } +} + +@Composable +@Preview +fun StreamsEmpty() { + Row(verticalAlignment = Alignment.CenterVertically) { + Column { + Text( + text = stringResource(id = R.string.empty_search_list), + color = Color.White, + fontWeight = FontWeight.SemiBold, + fontSize = 20.sp, + textAlign = TextAlign.Center, + modifier = Modifier.padding(vertical = 16.dp) + + ) + } + } +} + +@Composable +@Preview +fun StreamsErrorPreview() { + StreamsError( + onRetry = {}, + onCloseButton = {} + ) +} \ No newline at end of file diff --git a/feature-search/src/androidMain/kotlin/search/presentation/widgets/SearchStreamCard.kt b/feature-search/src/androidMain/kotlin/search/presentation/widgets/SearchStreamCard.kt new file mode 100644 index 00000000..9b14c597 --- /dev/null +++ b/feature-search/src/androidMain/kotlin/search/presentation/widgets/SearchStreamCard.kt @@ -0,0 +1,128 @@ +package search.presentation.widgets + +import androidx.compose.foundation.background +import androidx.compose.foundation.border +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Box +import androidx.compose.material3.Icon +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.PlayArrow +import androidx.compose.material3.Card +import androidx.compose.material3.Text +import androidx.compose.ui.Alignment +import androidx.compose.ui.layout.ContentScale +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.style.TextOverflow +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import coil.compose.AsyncImage +import com.codandotv.streamplayerapp.core_shared_ui.theme.ThemePreviews + +@Suppress("MagicNumber") +@Composable +fun SearchStreamCard( + content: SearchStreamCardModel, + onSearchStreamPressed: (id: String) -> Unit +) { + Row( + verticalAlignment = Alignment.CenterVertically, + modifier = Modifier + .fillMaxWidth() + .height(90.dp) + .background(color = Color.Black) + .padding(vertical = 4.dp, horizontal = 8.dp) + .clickable { + onSearchStreamPressed(content.id) + } + ) { + ImageStream( + url = content.url, + modifier = Modifier + .weight(2.5f) + .padding(vertical = 2.dp) + ) + Text( + text = content.title, + color = Color.White, + modifier = Modifier + .weight(4.5f) + .padding(8.dp), + fontWeight = FontWeight.SemiBold, + fontSize = 18.sp, + overflow = TextOverflow.Ellipsis + ) + PlayerIcon( + modifier = Modifier + .weight(2f) + .padding(2.dp) + ) + } +} + +data class SearchStreamCardModel( + val id: String, + val title: String, + val url: String +) + +@Composable +fun ImageStream(modifier: Modifier, url: String) { + Card( + shape = RoundedCornerShape(4.dp), + modifier = modifier + ) { + AsyncImage( + model = url, + contentScale = ContentScale.FillBounds, + contentDescription = "", + modifier = Modifier.fillMaxSize() + ) + } +} + +@Suppress("MagicNumber") +@Composable +fun PlayerIcon(modifier: Modifier) { + Box( + modifier = Modifier.border( + width = 0.5.dp, + shape = RoundedCornerShape(100), + color = Color.White + ) + ) { + Icon( + imageVector = Icons.Filled.PlayArrow, + tint = Color.White, + contentDescription = "", + modifier = modifier + ) + } +} + +@ThemePreviews +@Composable +fun SearchStreamCardPreview() { + SearchStreamCard( + content = SearchStreamCardModel( + id = "1", + title = "The Witcher", + url = "https://image.tmdb.org/t/p/w200/iwsMu0ehRPbtaSxqiaUDQB9qMWT.jpg" + ), + onSearchStreamPressed = {} + ) +} + +@ThemePreviews +@Composable +fun PlayerPreview() { + PlayerIcon(modifier = Modifier) +} diff --git a/feature-search/src/androidMain/kotlin/search/presentation/widgets/SearchStreams.kt b/feature-search/src/androidMain/kotlin/search/presentation/widgets/SearchStreams.kt new file mode 100644 index 00000000..47307f20 --- /dev/null +++ b/feature-search/src/androidMain/kotlin/search/presentation/widgets/SearchStreams.kt @@ -0,0 +1,249 @@ +package search.presentation.widgets + +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxHeight +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.foundation.text.KeyboardActions +import androidx.compose.foundation.text.KeyboardOptions +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.automirrored.filled.ArrowBack +import androidx.compose.material.icons.filled.Check +import androidx.compose.material.icons.filled.Close +import androidx.compose.material.icons.filled.Search +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.Icon +import androidx.compose.material3.IconButton +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.material3.TextField +import androidx.compose.material3.TextFieldDefaults +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.vector.ImageVector +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.TextStyle +import androidx.compose.ui.text.input.ImeAction +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import com.codandotv.streamplayerapp.core.shared.ui.R +import com.codandotv.streamplayerapp.core_shared_ui.resources.Colors +import com.codandotv.streamplayerapp.feature.search.R.* + +@Suppress("LongParameterList") +@Composable +fun SearchableTopBar( + currentSearchText: String, + onSearchTextChanged: (String) -> Unit, + onSearchDispatched: () -> Unit, + onSearchIconPressed: () -> Unit, + onBackPressed: () -> Unit, + onCleanTextPressed: () -> Unit +) { + Column { + StreamPlayerTopBar( + onBackPressed = onBackPressed + ) + SearchTopBar( + currentSearchText = currentSearchText, + onSearchTextChanged = onSearchTextChanged, + onSearchDispatched = onSearchDispatched, + onCleanTextPressed = onCleanTextPressed, + onSearchIconPressed = onSearchIconPressed + ) + } +} + +@Composable +private fun StreamPlayerTopBar( + onBackPressed: () -> Unit +) { + Row( + modifier = Modifier + .height(56.dp) + ) { + IconButton( + modifier = Modifier.fillMaxHeight(), + onClick = { + onBackPressed() + } + ) { + Icon( + imageVector = Icons.AutoMirrored.Filled.ArrowBack, + contentDescription = stringResource(id = R.string.icon_back), + tint = Color.White, + ) + } + Spacer(modifier = Modifier.weight(1f)) + + IconButton( + modifier = Modifier.fillMaxHeight(), + onClick = { /* todo */ } + ) { + Icon( + imageVector = Icons.Default.Check, + contentDescription = stringResource(id = R.string.icon_cast), + tint = Color.White, + ) + } + + IconButton( + modifier = Modifier.fillMaxHeight(), + onClick = { /* todo */ } + ) { + Icon( + modifier = Modifier + .height(24.dp) + .clip(RoundedCornerShape(4.dp)), + painter = painterResource(R.drawable.perfil_fake), + contentDescription = stringResource(id = R.string.icon_profile), + tint = Color.Unspecified, + ) + } + } +} + +@Composable +fun SearchTopBar( + currentSearchText: String, + onSearchTextChanged: (String) -> Unit, + onSearchDispatched: () -> Unit, + onCleanTextPressed: () -> Unit, + onSearchIconPressed: () -> Unit +) { + Box( + modifier = Modifier + .fillMaxWidth() + .height(56.dp) + .background(Colors.Gray100) + ) { + TextField( + modifier = Modifier.fillMaxWidth(), + value = currentSearchText, + colors = TextFieldDefaults.colors( + focusedContainerColor = Colors.Gray100, + focusedIndicatorColor = Color.Transparent, + disabledIndicatorColor = Color.Transparent, + unfocusedIndicatorColor = Color.Transparent, + cursorColor = Color.Gray + ), + onValueChange = { + onSearchTextChanged(it) + }, + placeholder = { + Text( + text = stringResource(id = string.search_list_main_search), + color = Color.Gray + ) + }, + textStyle = TextStyle( + fontSize = MaterialTheme.typography.bodyMedium.fontSize, + color = Color.White + ), + singleLine = true, + maxLines = 1, + leadingIcon = { + SearchIcon(action = onSearchIconPressed) + }, + trailingIcon = { + if (currentSearchText.isEmpty()) MicButton() else CloseButton( + action = onCleanTextPressed + ) + }, + keyboardOptions = KeyboardOptions(imeAction = ImeAction.Search), + keyboardActions = KeyboardActions(onSearch = { + onSearchDispatched() + }), + ) + } +} + +@Composable +fun DefaultIcon( + modifier: Modifier = Modifier, + searchIcon: ImageVector = Icons.Default.Search, + iconColor: Color = Color.White, + contentDescription: String = "", + onIconClickAction: () -> Unit = {} +) { + IconButton( + modifier = modifier, + onClick = onIconClickAction + ) { + Icon( + imageVector = searchIcon, + contentDescription = contentDescription, + tint = iconColor + ) + } +} + +@Composable +fun SearchIcon(action: () -> Unit = {}) { + DefaultIcon( + searchIcon = Icons.Filled.Search, + contentDescription = stringResource(id = R.string.icon_search), + onIconClickAction = action, + iconColor = Color.Gray + ) +} + +@Composable +fun CloseButton(action: () -> Unit = {}) { + DefaultIcon( + searchIcon = Icons.Default.Close, + contentDescription = stringResource(id = R.string.icon_close), + onIconClickAction = action, + iconColor = Color.Gray + ) +} + +@Composable +private fun MicButton(action: () -> Unit = {}) { + DefaultIcon( + searchIcon = Icons.Default.Check, + contentDescription = stringResource(id = R.string.icon_mic), + onIconClickAction = action, + iconColor = Color.Gray + ) +} + +@Composable +@Preview +fun SearchBarPreview() { + StreamPlayerTopBar( + onBackPressed = {} + ) +} + +@Composable +@Preview +fun SearchTopBarEmptyPreview() { + SearchTopBar( + currentSearchText = "", + onSearchTextChanged = {}, + onSearchDispatched = {}, + onCleanTextPressed = {}, + onSearchIconPressed = {} + ) +} + +@Composable +@Preview +fun SearchTopBarPreview() { + SearchTopBar( + currentSearchText = "Texto de busca", + onSearchTextChanged = {}, + onSearchDispatched = {}, + onCleanTextPressed = {}, + onSearchIconPressed = {} + ) +} diff --git a/feature-search/src/androidMain/res/values/strings.xml b/feature-search/src/androidMain/res/values/strings.xml new file mode 100644 index 00000000..e8445742 --- /dev/null +++ b/feature-search/src/androidMain/res/values/strings.xml @@ -0,0 +1,12 @@ + + + + Principais buscas + Principais buscas + Voltar + + + Houve um problema ao conectar à Netflix. Tente novamente mais tarde. + Tente novamente + Conteúdo não encontrado + \ No newline at end of file diff --git a/settings.gradle.kts b/settings.gradle.kts index f33ec449..bb5d9dd0 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -29,6 +29,8 @@ include(":core-navigation") include(":feature-profile") include(":core-local-storage") include(":feature-favorites") +include(":feature-detail") +include(":feature-search") + enableFeaturePreview("TYPESAFE_PROJECT_ACCESSORS") -include(":feature-detail") From b0dbb542bf60176350e92757ffb4c46a01d9321e Mon Sep 17 00:00:00 2001 From: Rods Date: Tue, 17 Dec 2024 21:49:54 -0300 Subject: [PATCH 19/96] =?UTF-8?q?[ISSUE-2]=20-=20=F0=9F=94=A5=20migrating?= =?UTF-8?q?=20search=20to=20kmp=20module?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index bd6785e4..e599ccf5 100644 --- a/.gitignore +++ b/.gitignore @@ -13,4 +13,5 @@ /captures .externalNativeBuild .cxx +.kotlin local.properties From 00746e622cf6d390fe7493116823a0189140f36f Mon Sep 17 00:00:00 2001 From: Rods Date: Mon, 23 Dec 2024 12:13:11 -0300 Subject: [PATCH 20/96] [ISSUE-2] - PRs suggestions --- .kotlin/errors/errors-1733793011356.log | 4 -- .../navigation/NavigationGraph.kt | 4 +- .../data/DetailStreamRepository.kt | 12 +++--- .../data/DetailStreamService.kt | 6 +-- .../data/model/DetailStreamResponse.kt | 2 +- .../data/model/VideoStreamResponse.kt | 2 +- .../feature_detail}/di/DetailStreamModule.kt | 20 ++++----- .../feature_detail}/domain/DetailStream.kt | 2 +- .../domain/DetailStreamMapper.kt | 6 +-- .../domain/DetailStreamUseCase.kt | 4 +- .../feature_detail}/domain/VideoStream.kt | 2 +- .../domain/VideoStreamsUseCase.kt | 4 +- .../navigation/DetailStreamNavigation.kt | 6 +-- .../screens/DetailStreamViewModel.kt | 12 +++--- .../screens/DetailStreamsScreen.kt | 14 +++--- .../screens/DetailStreamsUIState.kt | 4 +- .../widget/DetailStreamActionOption.kt | 4 +- .../widget/DetailStreamButtonAction.kt | 2 +- .../widget/DetailStreamImagePreview.kt | 4 +- .../widget/DetailStreamRowHeader.kt | 2 +- .../widget/DetailStreamToolbar.kt | 2 +- .../DetailStreamRepositoryTest.kt | 9 ++-- .../feature_detail/DetailStreamUseCaseTest.kt | 9 ++-- .../DetailStreamViewModelTest.kt | 16 ++++--- .../streamplayerapp/feature_detail/Shared.kt | 10 +++-- feature-search/.gitignore | 2 +- .../data/api/MostPopularMoviesService.kt | 4 +- .../data/api/SearchStreamService.kt | 4 +- .../datasource/MostPopularMoviesDataSource.kt | 6 +-- .../data/datasource/SearchStreamDataSource.kt | 6 +-- .../data/model/ListSearchStreamResponse.kt | 2 +- .../repository/MostPopularMoviesRepository.kt | 6 +-- .../data/repository/SearchStreamRepository.kt | 6 +-- .../feature_search/di/SearchModule.kt | 43 +++++++++++++++++++ .../domain/MostPopularMoviesUseCase.kt | 6 +-- .../feature_search}/domain/SearchUseCase.kt | 6 +-- .../domain/mapper/SearchMapper.kt | 11 +++++ .../navigation/SearchStreamNavigation.kt | 6 +-- .../presentation/screens/SearchScreen.kt | 12 +++--- .../presentation/screens/SearchUIState.kt | 4 +- .../presentation/screens/SearchViewModel.kt | 6 +-- .../presentation/widgets/SearchCarousel.kt | 2 +- .../presentation/widgets/SearchStreamCard.kt | 2 +- .../presentation/widgets/SearchStreams.kt | 2 +- .../kotlin/search/di/SearchModule.kt | 43 ------------------- .../search/domain/mapper/SearchMapper.kt | 11 ----- 46 files changed, 180 insertions(+), 172 deletions(-) delete mode 100644 .kotlin/errors/errors-1733793011356.log rename feature-detail/src/androidMain/kotlin/{detail => com/codandotv/streamplayerapp/feature_detail}/data/DetailStreamRepository.kt (78%) rename feature-detail/src/androidMain/kotlin/{detail => com/codandotv/streamplayerapp/feature_detail}/data/DetailStreamService.kt (80%) rename feature-detail/src/androidMain/kotlin/{detail => com/codandotv/streamplayerapp/feature_detail}/data/model/DetailStreamResponse.kt (81%) rename feature-detail/src/androidMain/kotlin/{detail => com/codandotv/streamplayerapp/feature_detail}/data/model/VideoStreamResponse.kt (84%) rename feature-detail/src/androidMain/kotlin/{detail => com/codandotv/streamplayerapp/feature_detail}/di/DetailStreamModule.kt (62%) rename feature-detail/src/androidMain/kotlin/{detail => com/codandotv/streamplayerapp/feature_detail}/domain/DetailStream.kt (77%) rename feature-detail/src/androidMain/kotlin/{detail => com/codandotv/streamplayerapp/feature_detail}/domain/DetailStreamMapper.kt (81%) rename feature-detail/src/androidMain/kotlin/{detail => com/codandotv/streamplayerapp/feature_detail}/domain/DetailStreamUseCase.kt (83%) rename feature-detail/src/androidMain/kotlin/{detail => com/codandotv/streamplayerapp/feature_detail}/domain/VideoStream.kt (55%) rename feature-detail/src/androidMain/kotlin/{detail => com/codandotv/streamplayerapp/feature_detail}/domain/VideoStreamsUseCase.kt (73%) rename feature-detail/src/androidMain/kotlin/{detail => com/codandotv/streamplayerapp/feature_detail}/presentation/navigation/DetailStreamNavigation.kt (84%) rename feature-detail/src/androidMain/kotlin/{detail => com/codandotv/streamplayerapp/feature_detail}/presentation/screens/DetailStreamViewModel.kt (79%) rename feature-detail/src/androidMain/kotlin/{detail => com/codandotv/streamplayerapp/feature_detail}/presentation/screens/DetailStreamsScreen.kt (92%) rename feature-detail/src/androidMain/kotlin/{detail => com/codandotv/streamplayerapp/feature_detail}/presentation/screens/DetailStreamsUIState.kt (62%) rename feature-detail/src/androidMain/kotlin/{detail => com/codandotv/streamplayerapp/feature_detail}/presentation/widget/DetailStreamActionOption.kt (95%) rename feature-detail/src/androidMain/kotlin/{detail => com/codandotv/streamplayerapp/feature_detail}/presentation/widget/DetailStreamButtonAction.kt (96%) rename feature-detail/src/androidMain/kotlin/{detail => com/codandotv/streamplayerapp/feature_detail}/presentation/widget/DetailStreamImagePreview.kt (92%) rename feature-detail/src/androidMain/kotlin/{detail => com/codandotv/streamplayerapp/feature_detail}/presentation/widget/DetailStreamRowHeader.kt (95%) rename feature-detail/src/androidMain/kotlin/{detail => com/codandotv/streamplayerapp/feature_detail}/presentation/widget/DetailStreamToolbar.kt (96%) rename feature-search/src/androidMain/kotlin/{search => com/codandotv/streamplayerapp/feature_search}/data/api/MostPopularMoviesService.kt (81%) rename feature-search/src/androidMain/kotlin/{search => com/codandotv/streamplayerapp/feature_search}/data/api/SearchStreamService.kt (82%) rename feature-search/src/androidMain/kotlin/{search => com/codandotv/streamplayerapp/feature_search}/data/datasource/MostPopularMoviesDataSource.kt (65%) rename feature-search/src/androidMain/kotlin/{search => com/codandotv/streamplayerapp/feature_search}/data/datasource/SearchStreamDataSource.kt (66%) rename feature-search/src/androidMain/kotlin/{search => com/codandotv/streamplayerapp/feature_search}/data/model/ListSearchStreamResponse.kt (88%) rename feature-search/src/androidMain/kotlin/{search => com/codandotv/streamplayerapp/feature_search}/data/repository/MostPopularMoviesRepository.kt (61%) rename feature-search/src/androidMain/kotlin/{search => com/codandotv/streamplayerapp/feature_search}/data/repository/SearchStreamRepository.kt (61%) create mode 100644 feature-search/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_search/di/SearchModule.kt rename feature-search/src/androidMain/kotlin/{search => com/codandotv/streamplayerapp/feature_search}/domain/MostPopularMoviesUseCase.kt (61%) rename feature-search/src/androidMain/kotlin/{search => com/codandotv/streamplayerapp/feature_search}/domain/SearchUseCase.kt (61%) create mode 100644 feature-search/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_search/domain/mapper/SearchMapper.kt rename feature-search/src/androidMain/kotlin/{search => com/codandotv/streamplayerapp/feature_search}/presentation/navigation/SearchStreamNavigation.kt (80%) rename feature-search/src/androidMain/kotlin/{search => com/codandotv/streamplayerapp/feature_search}/presentation/screens/SearchScreen.kt (91%) rename feature-search/src/androidMain/kotlin/{search => com/codandotv/streamplayerapp/feature_search}/presentation/screens/SearchUIState.kt (62%) rename feature-search/src/androidMain/kotlin/{search => com/codandotv/streamplayerapp/feature_search}/presentation/screens/SearchViewModel.kt (93%) rename feature-search/src/androidMain/kotlin/{search => com/codandotv/streamplayerapp/feature_search}/presentation/widgets/SearchCarousel.kt (98%) rename feature-search/src/androidMain/kotlin/{search => com/codandotv/streamplayerapp/feature_search}/presentation/widgets/SearchStreamCard.kt (97%) rename feature-search/src/androidMain/kotlin/{search => com/codandotv/streamplayerapp/feature_search}/presentation/widgets/SearchStreams.kt (99%) delete mode 100644 feature-search/src/androidMain/kotlin/search/di/SearchModule.kt delete mode 100644 feature-search/src/androidMain/kotlin/search/domain/mapper/SearchMapper.kt diff --git a/.kotlin/errors/errors-1733793011356.log b/.kotlin/errors/errors-1733793011356.log deleted file mode 100644 index 1219b509..00000000 --- a/.kotlin/errors/errors-1733793011356.log +++ /dev/null @@ -1,4 +0,0 @@ -kotlin version: 2.0.21 -error message: The daemon has terminated unexpectedly on startup attempt #1 with error code: 0. The daemon process output: - 1. Kotlin compile daemon is ready - diff --git a/app/src/main/java/com/codandotv/streamplayerapp/navigation/NavigationGraph.kt b/app/src/main/java/com/codandotv/streamplayerapp/navigation/NavigationGraph.kt index 31902b97..cbc81815 100644 --- a/app/src/main/java/com/codandotv/streamplayerapp/navigation/NavigationGraph.kt +++ b/app/src/main/java/com/codandotv/streamplayerapp/navigation/NavigationGraph.kt @@ -14,12 +14,12 @@ import androidx.navigation.compose.composable import com.codandotv.streamplayerapp.core_navigation.bottomnavigation.StreamPlayerBottomNavigation import com.codandotv.streamplayerapp.core_navigation.routes.BottomNavRoutes import com.codandotv.streamplayerapp.core_navigation.routes.Routes -import detail.presentation.navigation.detailStreamNavGraph import com.codandotv.streamplayerapp.feature_list_streams.list.presentation.navigation.listStreamsNavGraph import com.codandotv.streamplayerapp.feature_profile.profile.presentation.navigation.profilePickerStreamNavGraph import com.codandotv.streamplayerapp.splah.presentation.navigation.splashNavGraph -import search.presentation.navigation.searchStreamsNavGraph +import com.codandotv.streamplayerapp.feature_detail.presentation.navigation.detailStreamNavGraph +import com.codandotv.streamplayerapp.feature_search.presentation.navigation.searchStreamsNavGraph @Composable fun NavigationGraph(navController: NavHostController) { diff --git a/feature-detail/src/androidMain/kotlin/detail/data/DetailStreamRepository.kt b/feature-detail/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_detail/data/DetailStreamRepository.kt similarity index 78% rename from feature-detail/src/androidMain/kotlin/detail/data/DetailStreamRepository.kt rename to feature-detail/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_detail/data/DetailStreamRepository.kt index 332dcc7b..21adf8f1 100644 --- a/feature-detail/src/androidMain/kotlin/detail/data/DetailStreamRepository.kt +++ b/feature-detail/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_detail/data/DetailStreamRepository.kt @@ -1,12 +1,12 @@ -package detail.data +package com.codandotv.streamplayerapp.feature_detail.data import com.codandotv.streamplayerapp.core_local_storage.data.dao.FavoriteDao import com.codandotv.streamplayerapp.core_networking.handleError.toFlow -import detail.domain.DetailStream -import detail.domain.VideoStream -import detail.domain.toDetailStream -import detail.domain.toDetailStreamLocal -import detail.domain.toVideoStreams +import com.codandotv.streamplayerapp.feature_detail.domain.DetailStream +import com.codandotv.streamplayerapp.feature_detail.domain.VideoStream +import com.codandotv.streamplayerapp.feature_detail.domain.toDetailStream +import com.codandotv.streamplayerapp.feature_detail.domain.toDetailStreamLocal +import com.codandotv.streamplayerapp.feature_detail.domain.toVideoStreams import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.map diff --git a/feature-detail/src/androidMain/kotlin/detail/data/DetailStreamService.kt b/feature-detail/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_detail/data/DetailStreamService.kt similarity index 80% rename from feature-detail/src/androidMain/kotlin/detail/data/DetailStreamService.kt rename to feature-detail/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_detail/data/DetailStreamService.kt index 4f4174b5..c50f698c 100644 --- a/feature-detail/src/androidMain/kotlin/detail/data/DetailStreamService.kt +++ b/feature-detail/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_detail/data/DetailStreamService.kt @@ -1,9 +1,9 @@ -package detail.data +package com.codandotv.streamplayerapp.feature_detail.data import com.codandotv.streamplayerapp.core_networking.handleError.NetworkResponse import com.codandotv.streamplayerapp.core_networking.handleError.safeRequest -import detail.data.model.DetailStreamResponse -import detail.data.model.VideoStreamsResponse +import com.codandotv.streamplayerapp.feature_detail.data.model.DetailStreamResponse +import com.codandotv.streamplayerapp.feature_detail.data.model.VideoStreamsResponse import io.ktor.client.HttpClient import io.ktor.client.request.url import org.koin.core.annotation.Factory diff --git a/feature-detail/src/androidMain/kotlin/detail/data/model/DetailStreamResponse.kt b/feature-detail/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_detail/data/model/DetailStreamResponse.kt similarity index 81% rename from feature-detail/src/androidMain/kotlin/detail/data/model/DetailStreamResponse.kt rename to feature-detail/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_detail/data/model/DetailStreamResponse.kt index 7e1b1dd8..27c79e86 100644 --- a/feature-detail/src/androidMain/kotlin/detail/data/model/DetailStreamResponse.kt +++ b/feature-detail/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_detail/data/model/DetailStreamResponse.kt @@ -1,4 +1,4 @@ -package detail.data.model +package com.codandotv.streamplayerapp.feature_detail.data.model import kotlinx.serialization.Serializable diff --git a/feature-detail/src/androidMain/kotlin/detail/data/model/VideoStreamResponse.kt b/feature-detail/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_detail/data/model/VideoStreamResponse.kt similarity index 84% rename from feature-detail/src/androidMain/kotlin/detail/data/model/VideoStreamResponse.kt rename to feature-detail/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_detail/data/model/VideoStreamResponse.kt index 64e2efbc..e7fcc7eb 100644 --- a/feature-detail/src/androidMain/kotlin/detail/data/model/VideoStreamResponse.kt +++ b/feature-detail/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_detail/data/model/VideoStreamResponse.kt @@ -1,4 +1,4 @@ -package detail.data.model +package com.codandotv.streamplayerapp.feature_detail.data.model import kotlinx.serialization.Serializable diff --git a/feature-detail/src/androidMain/kotlin/detail/di/DetailStreamModule.kt b/feature-detail/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_detail/di/DetailStreamModule.kt similarity index 62% rename from feature-detail/src/androidMain/kotlin/detail/di/DetailStreamModule.kt rename to feature-detail/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_detail/di/DetailStreamModule.kt index 304c5e8d..ff8c0934 100644 --- a/feature-detail/src/androidMain/kotlin/detail/di/DetailStreamModule.kt +++ b/feature-detail/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_detail/di/DetailStreamModule.kt @@ -1,14 +1,14 @@ -package detail.di +package com.codandotv.streamplayerapp.feature_detail.di -import detail.data.DetailStreamRepository -import detail.data.DetailStreamRepositoryImpl -import detail.data.DetailStreamService -import detail.data.DetailStreamServiceImpl -import detail.domain.DetailStreamUseCase -import detail.domain.DetailStreamUseCaseImpl -import detail.domain.VideoStreamsUseCase -import detail.domain.VideoStreamsUseCaseImpl -import detail.presentation.screens.DetailStreamViewModel +import com.codandotv.streamplayerapp.feature_detail.data.DetailStreamRepository +import com.codandotv.streamplayerapp.feature_detail.data.DetailStreamRepositoryImpl +import com.codandotv.streamplayerapp.feature_detail.data.DetailStreamService +import com.codandotv.streamplayerapp.feature_detail.data.DetailStreamServiceImpl +import com.codandotv.streamplayerapp.feature_detail.domain.DetailStreamUseCase +import com.codandotv.streamplayerapp.feature_detail.domain.DetailStreamUseCaseImpl +import com.codandotv.streamplayerapp.feature_detail.domain.VideoStreamsUseCase +import com.codandotv.streamplayerapp.feature_detail.domain.VideoStreamsUseCaseImpl +import com.codandotv.streamplayerapp.feature_detail.presentation.screens.DetailStreamViewModel import kotlinx.coroutines.Dispatchers import org.koin.androidx.viewmodel.dsl.viewModel import org.koin.core.parameter.parametersOf diff --git a/feature-detail/src/androidMain/kotlin/detail/domain/DetailStream.kt b/feature-detail/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_detail/domain/DetailStream.kt similarity index 77% rename from feature-detail/src/androidMain/kotlin/detail/domain/DetailStream.kt rename to feature-detail/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_detail/domain/DetailStream.kt index 0afcc317..0d9f6689 100644 --- a/feature-detail/src/androidMain/kotlin/detail/domain/DetailStream.kt +++ b/feature-detail/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_detail/domain/DetailStream.kt @@ -1,4 +1,4 @@ -package detail.domain +package com.codandotv.streamplayerapp.feature_detail.domain data class DetailStream( val id : String, diff --git a/feature-detail/src/androidMain/kotlin/detail/domain/DetailStreamMapper.kt b/feature-detail/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_detail/domain/DetailStreamMapper.kt similarity index 81% rename from feature-detail/src/androidMain/kotlin/detail/domain/DetailStreamMapper.kt rename to feature-detail/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_detail/domain/DetailStreamMapper.kt index 52974336..e12cf78f 100644 --- a/feature-detail/src/androidMain/kotlin/detail/domain/DetailStreamMapper.kt +++ b/feature-detail/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_detail/domain/DetailStreamMapper.kt @@ -1,9 +1,9 @@ -package detail.domain +package com.codandotv.streamplayerapp.feature_detail.domain import com.codandotv.streamplayerapp.core_local_storage.domain.model.MovieEntity import com.codandotv.streamplayerapp.core_shared.Url.IMAGE_URL_SIZE_500 -import detail.data.model.DetailStreamResponse -import detail.data.model.VideoStreamsResponse +import com.codandotv.streamplayerapp.feature_detail.data.model.DetailStreamResponse +import com.codandotv.streamplayerapp.feature_detail.data.model.VideoStreamsResponse fun DetailStreamResponse.toDetailStream(isFavorite: Boolean = false): DetailStream = DetailStream( diff --git a/feature-detail/src/androidMain/kotlin/detail/domain/DetailStreamUseCase.kt b/feature-detail/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_detail/domain/DetailStreamUseCase.kt similarity index 83% rename from feature-detail/src/androidMain/kotlin/detail/domain/DetailStreamUseCase.kt rename to feature-detail/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_detail/domain/DetailStreamUseCase.kt index a47a6c1f..74c3c256 100644 --- a/feature-detail/src/androidMain/kotlin/detail/domain/DetailStreamUseCase.kt +++ b/feature-detail/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_detail/domain/DetailStreamUseCase.kt @@ -1,6 +1,6 @@ -package detail.domain +package com.codandotv.streamplayerapp.feature_detail.domain -import detail.data.DetailStreamRepository +import com.codandotv.streamplayerapp.feature_detail.data.DetailStreamRepository import kotlinx.coroutines.flow.Flow interface DetailStreamUseCase { diff --git a/feature-detail/src/androidMain/kotlin/detail/domain/VideoStream.kt b/feature-detail/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_detail/domain/VideoStream.kt similarity index 55% rename from feature-detail/src/androidMain/kotlin/detail/domain/VideoStream.kt rename to feature-detail/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_detail/domain/VideoStream.kt index 1bc7f99f..39ccb50e 100644 --- a/feature-detail/src/androidMain/kotlin/detail/domain/VideoStream.kt +++ b/feature-detail/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_detail/domain/VideoStream.kt @@ -1,4 +1,4 @@ -package detail.domain +package com.codandotv.streamplayerapp.feature_detail.domain data class VideoStream( val movieId: Long, diff --git a/feature-detail/src/androidMain/kotlin/detail/domain/VideoStreamsUseCase.kt b/feature-detail/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_detail/domain/VideoStreamsUseCase.kt similarity index 73% rename from feature-detail/src/androidMain/kotlin/detail/domain/VideoStreamsUseCase.kt rename to feature-detail/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_detail/domain/VideoStreamsUseCase.kt index 77285702..d35544be 100644 --- a/feature-detail/src/androidMain/kotlin/detail/domain/VideoStreamsUseCase.kt +++ b/feature-detail/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_detail/domain/VideoStreamsUseCase.kt @@ -1,6 +1,6 @@ -package detail.domain +package com.codandotv.streamplayerapp.feature_detail.domain -import detail.data.DetailStreamRepository +import com.codandotv.streamplayerapp.feature_detail.data.DetailStreamRepository import kotlinx.coroutines.flow.Flow interface VideoStreamsUseCase { diff --git a/feature-detail/src/androidMain/kotlin/detail/presentation/navigation/DetailStreamNavigation.kt b/feature-detail/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_detail/presentation/navigation/DetailStreamNavigation.kt similarity index 84% rename from feature-detail/src/androidMain/kotlin/detail/presentation/navigation/DetailStreamNavigation.kt rename to feature-detail/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_detail/presentation/navigation/DetailStreamNavigation.kt index e5eb36bc..7917c3f3 100644 --- a/feature-detail/src/androidMain/kotlin/detail/presentation/navigation/DetailStreamNavigation.kt +++ b/feature-detail/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_detail/presentation/navigation/DetailStreamNavigation.kt @@ -1,4 +1,4 @@ -package detail.presentation.navigation +package com.codandotv.streamplayerapp.feature_detail.presentation.navigation import androidx.lifecycle.Lifecycle import androidx.navigation.NavGraphBuilder @@ -7,8 +7,8 @@ import androidx.navigation.compose.composable import com.codandotv.streamplayerapp.core_navigation.routes.Routes import com.codandotv.streamplayerapp.core_navigation.routes.Routes.DETAIL_COMPLETE import com.codandotv.streamplayerapp.core_navigation.routes.Routes.PARAM.ID -import detail.di.DetailStreamModule -import detail.presentation.screens.DetailStreamScreen +import com.codandotv.streamplayerapp.feature_detail.di.DetailStreamModule +import com.codandotv.streamplayerapp.feature_detail.presentation.screens.DetailStreamScreen import org.koin.androidx.compose.koinViewModel import org.koin.core.context.loadKoinModules import org.koin.core.context.unloadKoinModules diff --git a/feature-detail/src/androidMain/kotlin/detail/presentation/screens/DetailStreamViewModel.kt b/feature-detail/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_detail/presentation/screens/DetailStreamViewModel.kt similarity index 79% rename from feature-detail/src/androidMain/kotlin/detail/presentation/screens/DetailStreamViewModel.kt rename to feature-detail/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_detail/presentation/screens/DetailStreamViewModel.kt index ea3ce405..3a0cd02f 100644 --- a/feature-detail/src/androidMain/kotlin/detail/presentation/screens/DetailStreamViewModel.kt +++ b/feature-detail/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_detail/presentation/screens/DetailStreamViewModel.kt @@ -1,13 +1,13 @@ -package detail.presentation.screens +package com.codandotv.streamplayerapp.feature_detail.presentation.screens import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import com.codandotv.streamplayerapp.core_networking.handleError.catchFailure -import detail.domain.DetailStream -import detail.domain.DetailStreamUseCase -import detail.domain.VideoStreamsUseCase -import detail.presentation.screens.DetailStreamsUIState.DetailStreamsLoadedUIState -import detail.presentation.screens.DetailStreamsUIState.LoadingStreamUIState +import com.codandotv.streamplayerapp.feature_detail.domain.DetailStream +import com.codandotv.streamplayerapp.feature_detail.domain.DetailStreamUseCase +import com.codandotv.streamplayerapp.feature_detail.domain.VideoStreamsUseCase +import com.codandotv.streamplayerapp.feature_detail.presentation.screens.DetailStreamsUIState.DetailStreamsLoadedUIState +import com.codandotv.streamplayerapp.feature_detail.presentation.screens.DetailStreamsUIState.LoadingStreamUIState import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.SharingStarted diff --git a/feature-detail/src/androidMain/kotlin/detail/presentation/screens/DetailStreamsScreen.kt b/feature-detail/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_detail/presentation/screens/DetailStreamsScreen.kt similarity index 92% rename from feature-detail/src/androidMain/kotlin/detail/presentation/screens/DetailStreamsScreen.kt rename to feature-detail/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_detail/presentation/screens/DetailStreamsScreen.kt index 158fba69..ef6f59f5 100644 --- a/feature-detail/src/androidMain/kotlin/detail/presentation/screens/DetailStreamsScreen.kt +++ b/feature-detail/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_detail/presentation/screens/DetailStreamsScreen.kt @@ -1,4 +1,4 @@ -package detail.presentation.screens +package com.codandotv.streamplayerapp.feature_detail.presentation.screens import android.annotation.SuppressLint import androidx.activity.compose.BackHandler @@ -40,12 +40,12 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.navigation.NavController import com.codandotv.streamplayerapp.core_shared_ui.widget.SharingStreamCustomView import com.codandotv.streamplayerapp.feature.detail.R -import detail.domain.DetailStream -import detail.presentation.widget.DetailStreamActionOption -import detail.presentation.widget.DetailStreamButtonAction -import detail.presentation.widget.DetailStreamImagePreview -import detail.presentation.widget.DetailStreamRowHeader -import detail.presentation.widget.DetailStreamToolbar +import com.codandotv.streamplayerapp.feature_detail.domain.DetailStream +import com.codandotv.streamplayerapp.feature_detail.presentation.widget.DetailStreamActionOption +import com.codandotv.streamplayerapp.feature_detail.presentation.widget.DetailStreamButtonAction +import com.codandotv.streamplayerapp.feature_detail.presentation.widget.DetailStreamImagePreview +import com.codandotv.streamplayerapp.feature_detail.presentation.widget.DetailStreamRowHeader +import com.codandotv.streamplayerapp.feature_detail.presentation.widget.DetailStreamToolbar import org.koin.androidx.compose.koinViewModel @Composable diff --git a/feature-detail/src/androidMain/kotlin/detail/presentation/screens/DetailStreamsUIState.kt b/feature-detail/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_detail/presentation/screens/DetailStreamsUIState.kt similarity index 62% rename from feature-detail/src/androidMain/kotlin/detail/presentation/screens/DetailStreamsUIState.kt rename to feature-detail/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_detail/presentation/screens/DetailStreamsUIState.kt index 80ee69bc..66c2a144 100644 --- a/feature-detail/src/androidMain/kotlin/detail/presentation/screens/DetailStreamsUIState.kt +++ b/feature-detail/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_detail/presentation/screens/DetailStreamsUIState.kt @@ -1,6 +1,6 @@ -package detail.presentation.screens +package com.codandotv.streamplayerapp.feature_detail.presentation.screens -import detail.domain.DetailStream +import com.codandotv.streamplayerapp.feature_detail.domain.DetailStream sealed class DetailStreamsUIState { data class DetailStreamsLoadedUIState( diff --git a/feature-detail/src/androidMain/kotlin/detail/presentation/widget/DetailStreamActionOption.kt b/feature-detail/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_detail/presentation/widget/DetailStreamActionOption.kt similarity index 95% rename from feature-detail/src/androidMain/kotlin/detail/presentation/widget/DetailStreamActionOption.kt rename to feature-detail/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_detail/presentation/widget/DetailStreamActionOption.kt index 64e7a624..1363ccf4 100644 --- a/feature-detail/src/androidMain/kotlin/detail/presentation/widget/DetailStreamActionOption.kt +++ b/feature-detail/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_detail/presentation/widget/DetailStreamActionOption.kt @@ -1,4 +1,4 @@ -package detail.presentation.widget +package com.codandotv.streamplayerapp.feature_detail.presentation.widget import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Row @@ -20,7 +20,7 @@ import androidx.compose.ui.graphics.Color import androidx.compose.ui.res.stringResource import com.codandotv.streamplayerapp.core_shared_ui.widget.IconWithText import com.codandotv.streamplayerapp.feature.detail.R -import detail.domain.DetailStream +import com.codandotv.streamplayerapp.feature_detail.domain.DetailStream @Composable fun DetailStreamActionOption( diff --git a/feature-detail/src/androidMain/kotlin/detail/presentation/widget/DetailStreamButtonAction.kt b/feature-detail/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_detail/presentation/widget/DetailStreamButtonAction.kt similarity index 96% rename from feature-detail/src/androidMain/kotlin/detail/presentation/widget/DetailStreamButtonAction.kt rename to feature-detail/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_detail/presentation/widget/DetailStreamButtonAction.kt index dc583630..c97b9f1e 100644 --- a/feature-detail/src/androidMain/kotlin/detail/presentation/widget/DetailStreamButtonAction.kt +++ b/feature-detail/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_detail/presentation/widget/DetailStreamButtonAction.kt @@ -1,4 +1,4 @@ -package detail.presentation.widget +package com.codandotv.streamplayerapp.feature_detail.presentation.widget import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer diff --git a/feature-detail/src/androidMain/kotlin/detail/presentation/widget/DetailStreamImagePreview.kt b/feature-detail/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_detail/presentation/widget/DetailStreamImagePreview.kt similarity index 92% rename from feature-detail/src/androidMain/kotlin/detail/presentation/widget/DetailStreamImagePreview.kt rename to feature-detail/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_detail/presentation/widget/DetailStreamImagePreview.kt index 4d95b242..18657683 100644 --- a/feature-detail/src/androidMain/kotlin/detail/presentation/widget/DetailStreamImagePreview.kt +++ b/feature-detail/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_detail/presentation/widget/DetailStreamImagePreview.kt @@ -1,4 +1,4 @@ -package detail.presentation.widget +package com.codandotv.streamplayerapp.feature_detail.presentation.widget import androidx.compose.foundation.background import androidx.compose.foundation.clickable @@ -19,7 +19,7 @@ import androidx.compose.ui.unit.dp import coil.compose.AsyncImage import com.codandotv.streamplayerapp.core_shared_ui.widget.PlayerComponent import com.codandotv.streamplayerapp.feature.detail.R -import detail.presentation.screens.DetailStreamsUIState.DetailStreamsLoadedUIState +import com.codandotv.streamplayerapp.feature_detail.presentation.screens.DetailStreamsUIState.DetailStreamsLoadedUIState @Suppress("MagicNumber") @Composable diff --git a/feature-detail/src/androidMain/kotlin/detail/presentation/widget/DetailStreamRowHeader.kt b/feature-detail/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_detail/presentation/widget/DetailStreamRowHeader.kt similarity index 95% rename from feature-detail/src/androidMain/kotlin/detail/presentation/widget/DetailStreamRowHeader.kt rename to feature-detail/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_detail/presentation/widget/DetailStreamRowHeader.kt index a4649cb6..41b6e3e8 100644 --- a/feature-detail/src/androidMain/kotlin/detail/presentation/widget/DetailStreamRowHeader.kt +++ b/feature-detail/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_detail/presentation/widget/DetailStreamRowHeader.kt @@ -1,4 +1,4 @@ -package detail.presentation.widget +package com.codandotv.streamplayerapp.feature_detail.presentation.widget import androidx.compose.foundation.Image import androidx.compose.foundation.layout.Row diff --git a/feature-detail/src/androidMain/kotlin/detail/presentation/widget/DetailStreamToolbar.kt b/feature-detail/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_detail/presentation/widget/DetailStreamToolbar.kt similarity index 96% rename from feature-detail/src/androidMain/kotlin/detail/presentation/widget/DetailStreamToolbar.kt rename to feature-detail/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_detail/presentation/widget/DetailStreamToolbar.kt index abc397ef..9abdcf24 100644 --- a/feature-detail/src/androidMain/kotlin/detail/presentation/widget/DetailStreamToolbar.kt +++ b/feature-detail/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_detail/presentation/widget/DetailStreamToolbar.kt @@ -1,4 +1,4 @@ -package detail.presentation.widget +package com.codandotv.streamplayerapp.feature_detail.presentation.widget import androidx.compose.foundation.Image import androidx.compose.foundation.layout.height diff --git a/feature-detail/src/test/java/com/codandotv/streamplayerapp/feature_detail/DetailStreamRepositoryTest.kt b/feature-detail/src/test/java/com/codandotv/streamplayerapp/feature_detail/DetailStreamRepositoryTest.kt index 228d20d5..042fb260 100644 --- a/feature-detail/src/test/java/com/codandotv/streamplayerapp/feature_detail/DetailStreamRepositoryTest.kt +++ b/feature-detail/src/test/java/com/codandotv/streamplayerapp/feature_detail/DetailStreamRepositoryTest.kt @@ -2,6 +2,9 @@ package com.codandotv.streamplayerapp.feature_detail import com.codandotv.streamplayerapp.core_local_storage.data.dao.FavoriteDao import com.codandotv.streamplayerapp.core_networking.handleError.NetworkResponse +import com.codandotv.streamplayerapp.feature_detail.data.DetailStreamRepository +import com.codandotv.streamplayerapp.feature_detail.data.DetailStreamRepositoryImpl +import com.codandotv.streamplayerapp.feature_detail.data.DetailStreamService import io.mockk.coEvery import io.mockk.coVerifyOrder import io.mockk.mockk @@ -11,16 +14,16 @@ import org.junit.Before import org.junit.Test class DetailStreamRepositoryTest { - private lateinit var repository: detail.data.DetailStreamRepository + private lateinit var repository: DetailStreamRepository private val movieId = MOVIE_ID_STRING - private lateinit var service: detail.data.DetailStreamService + private lateinit var service: DetailStreamService private lateinit var favoriteDao: FavoriteDao @Before fun setUp() { service = mockk() favoriteDao = mockk() - repository = detail.data.DetailStreamRepositoryImpl( + repository = DetailStreamRepositoryImpl( movieId = movieId, service = service, favoriteDao = favoriteDao diff --git a/feature-detail/src/test/java/com/codandotv/streamplayerapp/feature_detail/DetailStreamUseCaseTest.kt b/feature-detail/src/test/java/com/codandotv/streamplayerapp/feature_detail/DetailStreamUseCaseTest.kt index 34719236..37e4c128 100644 --- a/feature-detail/src/test/java/com/codandotv/streamplayerapp/feature_detail/DetailStreamUseCaseTest.kt +++ b/feature-detail/src/test/java/com/codandotv/streamplayerapp/feature_detail/DetailStreamUseCaseTest.kt @@ -1,5 +1,8 @@ package com.codandotv.streamplayerapp.feature_detail +import com.codandotv.streamplayerapp.feature_detail.data.DetailStreamRepository +import com.codandotv.streamplayerapp.feature_detail.domain.DetailStreamUseCase +import com.codandotv.streamplayerapp.feature_detail.domain.DetailStreamUseCaseImpl import io.mockk.coEvery import io.mockk.coVerify import io.mockk.mockk @@ -10,13 +13,13 @@ import org.junit.Test import kotlin.test.assertTrue class DetailStreamUseCaseTest { - private lateinit var detailStreamUseCase: detail.domain.DetailStreamUseCase - private lateinit var detailStreamRepository: detail.data.DetailStreamRepository + private lateinit var detailStreamUseCase: DetailStreamUseCase + private lateinit var detailStreamRepository: DetailStreamRepository @Before fun setUp() { detailStreamRepository = mockk() - detailStreamUseCase = detail.domain.DetailStreamUseCaseImpl( + detailStreamUseCase = DetailStreamUseCaseImpl( detailStreamRepository = detailStreamRepository ) } diff --git a/feature-detail/src/test/java/com/codandotv/streamplayerapp/feature_detail/DetailStreamViewModelTest.kt b/feature-detail/src/test/java/com/codandotv/streamplayerapp/feature_detail/DetailStreamViewModelTest.kt index f96446ed..65a2ae0d 100644 --- a/feature-detail/src/test/java/com/codandotv/streamplayerapp/feature_detail/DetailStreamViewModelTest.kt +++ b/feature-detail/src/test/java/com/codandotv/streamplayerapp/feature_detail/DetailStreamViewModelTest.kt @@ -1,6 +1,10 @@ package com.codandotv.streamplayerapp.feature_detail import androidx.arch.core.executor.testing.InstantTaskExecutorRule +import com.codandotv.streamplayerapp.feature_detail.domain.DetailStreamUseCase +import com.codandotv.streamplayerapp.feature_detail.domain.VideoStreamsUseCase +import com.codandotv.streamplayerapp.feature_detail.presentation.screens.DetailStreamViewModel +import com.codandotv.streamplayerapp.feature_detail.presentation.screens.DetailStreamsUIState import io.mockk.coEvery import io.mockk.coVerify import io.mockk.mockk @@ -12,9 +16,9 @@ import org.junit.Test import kotlin.test.assertTrue class DetailStreamViewModelTest { - private lateinit var detailStreamViewModel: detail.presentation.screens.DetailStreamViewModel - private lateinit var detailUseCase: detail.domain.DetailStreamUseCase - private lateinit var videoUseCase: detail.domain.VideoStreamsUseCase + private lateinit var detailStreamViewModel: DetailStreamViewModel + private lateinit var detailUseCase: DetailStreamUseCase + private lateinit var videoUseCase: VideoStreamsUseCase @get:Rule val rule = InstantTaskExecutorRule() @@ -27,7 +31,7 @@ class DetailStreamViewModelTest { detailUseCase = mockk() videoUseCase = mockk() - detailStreamViewModel = detail.presentation.screens.DetailStreamViewModel( + detailStreamViewModel = DetailStreamViewModel( detailStreamUseCase = detailUseCase, videoStreamsUseCase = videoUseCase, dispatcher = executorRule.dispatcher @@ -44,12 +48,12 @@ class DetailStreamViewModelTest { coVerify { detailStreamViewModel.uiState.value.let { - detail.presentation.screens.DetailStreamsUIState.LoadingStreamUIState + DetailStreamsUIState.LoadingStreamUIState } detailUseCase.getMovie() detailStreamViewModel.uiState.value.let { assertTrue { - it == detail.presentation.screens.DetailStreamsUIState.DetailStreamsLoadedUIState( + it == DetailStreamsUIState.DetailStreamsLoadedUIState( detailStream = detailStream, videoId = videosStreamsList.first().videoId ) } diff --git a/feature-detail/src/test/java/com/codandotv/streamplayerapp/feature_detail/Shared.kt b/feature-detail/src/test/java/com/codandotv/streamplayerapp/feature_detail/Shared.kt index 235214ae..095ab5dc 100644 --- a/feature-detail/src/test/java/com/codandotv/streamplayerapp/feature_detail/Shared.kt +++ b/feature-detail/src/test/java/com/codandotv/streamplayerapp/feature_detail/Shared.kt @@ -1,15 +1,17 @@ package com.codandotv.streamplayerapp.feature_detail -import detail.data.model.DetailStreamResponse +import com.codandotv.streamplayerapp.feature_detail.data.model.DetailStreamResponse +import com.codandotv.streamplayerapp.feature_detail.domain.DetailStream +import com.codandotv.streamplayerapp.feature_detail.domain.VideoStream -val videoStream = detail.domain.VideoStream( +val videoStream = VideoStream( movieId = 123, videoId = "123" ) const val MOVIE_ID_STRING = "123" -val videoStream1 = detail.domain.VideoStream( +val videoStream1 = VideoStream( movieId = 1234565, videoId = "123565" ) @@ -28,7 +30,7 @@ val detailStreamResponse = DetailStreamResponse( release_date = "release" ) -val detailStream = detail.domain.DetailStream( +val detailStream = DetailStream( id = "id", title = "title", overview = "overview", diff --git a/feature-search/.gitignore b/feature-search/.gitignore index 42afabfd..581f8daa 100644 --- a/feature-search/.gitignore +++ b/feature-search/.gitignore @@ -1 +1 @@ -/build \ No newline at end of file +**/build/** \ No newline at end of file diff --git a/feature-search/src/androidMain/kotlin/search/data/api/MostPopularMoviesService.kt b/feature-search/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_search/data/api/MostPopularMoviesService.kt similarity index 81% rename from feature-search/src/androidMain/kotlin/search/data/api/MostPopularMoviesService.kt rename to feature-search/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_search/data/api/MostPopularMoviesService.kt index 745d0cb6..aa4ac25f 100644 --- a/feature-search/src/androidMain/kotlin/search/data/api/MostPopularMoviesService.kt +++ b/feature-search/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_search/data/api/MostPopularMoviesService.kt @@ -1,8 +1,8 @@ -package search.data.api +package com.codandotv.streamplayerapp.feature_search.data.api import com.codandotv.streamplayerapp.core_networking.handleError.NetworkResponse import com.codandotv.streamplayerapp.core_networking.handleError.safeRequest -import search.data.model.ListSearchStreamResponse +import com.codandotv.streamplayerapp.feature_search.data.model.ListSearchStreamResponse import io.ktor.client.HttpClient import io.ktor.client.call.body import io.ktor.client.request.get diff --git a/feature-search/src/androidMain/kotlin/search/data/api/SearchStreamService.kt b/feature-search/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_search/data/api/SearchStreamService.kt similarity index 82% rename from feature-search/src/androidMain/kotlin/search/data/api/SearchStreamService.kt rename to feature-search/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_search/data/api/SearchStreamService.kt index 51d28216..1020971f 100644 --- a/feature-search/src/androidMain/kotlin/search/data/api/SearchStreamService.kt +++ b/feature-search/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_search/data/api/SearchStreamService.kt @@ -1,8 +1,8 @@ -package search.data.api +package com.codandotv.streamplayerapp.feature_search.data.api import com.codandotv.streamplayerapp.core_networking.handleError.NetworkResponse import com.codandotv.streamplayerapp.core_networking.handleError.safeRequest -import search.data.model.ListSearchStreamResponse +import com.codandotv.streamplayerapp.feature_search.data.model.ListSearchStreamResponse import io.ktor.client.HttpClient import io.ktor.client.request.parameter import io.ktor.client.request.url diff --git a/feature-search/src/androidMain/kotlin/search/data/datasource/MostPopularMoviesDataSource.kt b/feature-search/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_search/data/datasource/MostPopularMoviesDataSource.kt similarity index 65% rename from feature-search/src/androidMain/kotlin/search/data/datasource/MostPopularMoviesDataSource.kt rename to feature-search/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_search/data/datasource/MostPopularMoviesDataSource.kt index 562339f8..b074864d 100644 --- a/feature-search/src/androidMain/kotlin/search/data/datasource/MostPopularMoviesDataSource.kt +++ b/feature-search/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_search/data/datasource/MostPopularMoviesDataSource.kt @@ -1,8 +1,8 @@ -package search.data.datasource +package com.codandotv.streamplayerapp.feature_search.data.datasource import com.codandotv.streamplayerapp.core_networking.handleError.toFlow -import search.data.model.ListSearchStreamResponse -import search.data.api.MostPopularMoviesService +import com.codandotv.streamplayerapp.feature_search.data.model.ListSearchStreamResponse +import com.codandotv.streamplayerapp.feature_search.data.api.MostPopularMoviesService import kotlinx.coroutines.flow.Flow interface MostPopularMoviesDataSource { diff --git a/feature-search/src/androidMain/kotlin/search/data/datasource/SearchStreamDataSource.kt b/feature-search/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_search/data/datasource/SearchStreamDataSource.kt similarity index 66% rename from feature-search/src/androidMain/kotlin/search/data/datasource/SearchStreamDataSource.kt rename to feature-search/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_search/data/datasource/SearchStreamDataSource.kt index d40f6727..e3d65b3b 100644 --- a/feature-search/src/androidMain/kotlin/search/data/datasource/SearchStreamDataSource.kt +++ b/feature-search/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_search/data/datasource/SearchStreamDataSource.kt @@ -1,8 +1,8 @@ -package search.data.datasource +package com.codandotv.streamplayerapp.feature_search.data.datasource import com.codandotv.streamplayerapp.core_networking.handleError.toFlow -import search.data.model.ListSearchStreamResponse -import search.data.api.SearchStreamService +import com.codandotv.streamplayerapp.feature_search.data.model.ListSearchStreamResponse +import com.codandotv.streamplayerapp.feature_search.data.api.SearchStreamService import kotlinx.coroutines.flow.Flow interface SearchStreamDataSource { diff --git a/feature-search/src/androidMain/kotlin/search/data/model/ListSearchStreamResponse.kt b/feature-search/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_search/data/model/ListSearchStreamResponse.kt similarity index 88% rename from feature-search/src/androidMain/kotlin/search/data/model/ListSearchStreamResponse.kt rename to feature-search/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_search/data/model/ListSearchStreamResponse.kt index 27ee89c4..86baae53 100644 --- a/feature-search/src/androidMain/kotlin/search/data/model/ListSearchStreamResponse.kt +++ b/feature-search/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_search/data/model/ListSearchStreamResponse.kt @@ -1,4 +1,4 @@ -package search.data.model +package com.codandotv.streamplayerapp.feature_search.data.model import com.squareup.moshi.Json import kotlinx.serialization.Serializable diff --git a/feature-search/src/androidMain/kotlin/search/data/repository/MostPopularMoviesRepository.kt b/feature-search/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_search/data/repository/MostPopularMoviesRepository.kt similarity index 61% rename from feature-search/src/androidMain/kotlin/search/data/repository/MostPopularMoviesRepository.kt rename to feature-search/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_search/data/repository/MostPopularMoviesRepository.kt index fca60f45..5fbf97e5 100644 --- a/feature-search/src/androidMain/kotlin/search/data/repository/MostPopularMoviesRepository.kt +++ b/feature-search/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_search/data/repository/MostPopularMoviesRepository.kt @@ -1,7 +1,7 @@ -package search.data.repository +package com.codandotv.streamplayerapp.feature_search.data.repository -import search.data.model.ListSearchStreamResponse -import search.data.datasource.MostPopularMoviesDataSource +import com.codandotv.streamplayerapp.feature_search.data.model.ListSearchStreamResponse +import com.codandotv.streamplayerapp.feature_search.data.datasource.MostPopularMoviesDataSource import kotlinx.coroutines.flow.Flow interface MostPopularMoviesRepository { diff --git a/feature-search/src/androidMain/kotlin/search/data/repository/SearchStreamRepository.kt b/feature-search/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_search/data/repository/SearchStreamRepository.kt similarity index 61% rename from feature-search/src/androidMain/kotlin/search/data/repository/SearchStreamRepository.kt rename to feature-search/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_search/data/repository/SearchStreamRepository.kt index aba513d1..9b101176 100644 --- a/feature-search/src/androidMain/kotlin/search/data/repository/SearchStreamRepository.kt +++ b/feature-search/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_search/data/repository/SearchStreamRepository.kt @@ -1,7 +1,7 @@ -package search.data.repository +package com.codandotv.streamplayerapp.feature_search.data.repository -import search.data.datasource.SearchStreamDataSource -import search.data.model.ListSearchStreamResponse +import com.codandotv.streamplayerapp.feature_search.data.datasource.SearchStreamDataSource +import com.codandotv.streamplayerapp.feature_search.data.model.ListSearchStreamResponse import kotlinx.coroutines.flow.Flow interface SearchStreamRepository { diff --git a/feature-search/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_search/di/SearchModule.kt b/feature-search/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_search/di/SearchModule.kt new file mode 100644 index 00000000..4d6215cd --- /dev/null +++ b/feature-search/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_search/di/SearchModule.kt @@ -0,0 +1,43 @@ +package com.codandotv.streamplayerapp.feature_search.di + +import com.codandotv.streamplayerapp.feature_search.data.api.SearchStreamService +import com.codandotv.streamplayerapp.feature_search.data.datasource.SearchStreamDataSourceImpl +import com.codandotv.streamplayerapp.feature_search.data.datasource.MostPopularMoviesDataSource +import com.codandotv.streamplayerapp.feature_search.data.datasource.MostPopularMoviesDataSourceImpl +import com.codandotv.streamplayerapp.feature_search.data.repository.MostPopularMoviesRepository +import com.codandotv.streamplayerapp.feature_search.data.repository.MostPopularMoviesRepositoryImpl +import com.codandotv.streamplayerapp.feature_search.data.api.MostPopularMoviesService +import com.codandotv.streamplayerapp.feature_search.data.api.MostPopularMoviesServiceImpl +import com.codandotv.streamplayerapp.feature_search.data.api.SearchStreamServiceImpl +import com.codandotv.streamplayerapp.feature_search.data.datasource.SearchStreamDataSource +import com.codandotv.streamplayerapp.feature_search.data.repository.SearchStreamRepository +import com.codandotv.streamplayerapp.feature_search.data.repository.SearchStreamRepositoryImp +import com.codandotv.streamplayerapp.feature_search.domain.MostPopularMoviesUseCase +import com.codandotv.streamplayerapp.feature_search.domain.MostPopularMoviesUseCaseImpl +import com.codandotv.streamplayerapp.feature_search.domain.SearchUseCase +import com.codandotv.streamplayerapp.feature_search.domain.SearchUseCaseImpl +import com.codandotv.streamplayerapp.feature_search.presentation.screens.SearchViewModel +import org.koin.androidx.viewmodel.dsl.viewModel +import org.koin.dsl.module + +object SearchModule { + val module = module { + viewModel { + SearchViewModel( + searchUseCase = get(), + mostPopularMoviesUseCase = get() + ) + } + + factory { SearchStreamServiceImpl(get()) } + factory { MostPopularMoviesServiceImpl(get()) } + + factory { MostPopularMoviesUseCaseImpl(repository = get()) } + factory { MostPopularMoviesDataSourceImpl(service = get()) } + factory { MostPopularMoviesRepositoryImpl(dataSource = get()) } + + factory { SearchUseCaseImpl(repository = get()) } + factory { SearchStreamDataSourceImpl(service = get()) } + factory { SearchStreamRepositoryImp(dataSource = get()) } + } +} diff --git a/feature-search/src/androidMain/kotlin/search/domain/MostPopularMoviesUseCase.kt b/feature-search/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_search/domain/MostPopularMoviesUseCase.kt similarity index 61% rename from feature-search/src/androidMain/kotlin/search/domain/MostPopularMoviesUseCase.kt rename to feature-search/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_search/domain/MostPopularMoviesUseCase.kt index f3414d0c..b924c782 100644 --- a/feature-search/src/androidMain/kotlin/search/domain/MostPopularMoviesUseCase.kt +++ b/feature-search/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_search/domain/MostPopularMoviesUseCase.kt @@ -1,7 +1,7 @@ -package search.domain +package com.codandotv.streamplayerapp.feature_search.domain -import search.data.model.ListSearchStreamResponse -import search.data.repository.MostPopularMoviesRepository +import com.codandotv.streamplayerapp.feature_search.data.model.ListSearchStreamResponse +import com.codandotv.streamplayerapp.feature_search.data.repository.MostPopularMoviesRepository import kotlinx.coroutines.flow.Flow interface MostPopularMoviesUseCase { diff --git a/feature-search/src/androidMain/kotlin/search/domain/SearchUseCase.kt b/feature-search/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_search/domain/SearchUseCase.kt similarity index 61% rename from feature-search/src/androidMain/kotlin/search/domain/SearchUseCase.kt rename to feature-search/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_search/domain/SearchUseCase.kt index 78e9578c..075f7bfe 100644 --- a/feature-search/src/androidMain/kotlin/search/domain/SearchUseCase.kt +++ b/feature-search/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_search/domain/SearchUseCase.kt @@ -1,7 +1,7 @@ -package search.domain +package com.codandotv.streamplayerapp.feature_search.domain -import search.data.model.ListSearchStreamResponse -import search.data.repository.SearchStreamRepository +import com.codandotv.streamplayerapp.feature_search.data.model.ListSearchStreamResponse +import com.codandotv.streamplayerapp.feature_search.data.repository.SearchStreamRepository import kotlinx.coroutines.flow.Flow interface SearchUseCase { diff --git a/feature-search/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_search/domain/mapper/SearchMapper.kt b/feature-search/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_search/domain/mapper/SearchMapper.kt new file mode 100644 index 00000000..a96edcb1 --- /dev/null +++ b/feature-search/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_search/domain/mapper/SearchMapper.kt @@ -0,0 +1,11 @@ +package com.codandotv.streamplayerapp.feature_search.domain.mapper + +import com.codandotv.streamplayerapp.core_shared.Url +import com.codandotv.streamplayerapp.feature_search.data.model.ListSearchStreamResponse.SearchStreamResponse +import com.codandotv.streamplayerapp.feature_search.presentation.widgets.SearchStreamCardModel + +fun SearchStreamResponse.toSearchStreamCardModel() = SearchStreamCardModel( + id = id.toString(), + title = title, + url = "${Url.IMAGE_URL_SIZE_200}${posterPath}" +) diff --git a/feature-search/src/androidMain/kotlin/search/presentation/navigation/SearchStreamNavigation.kt b/feature-search/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_search/presentation/navigation/SearchStreamNavigation.kt similarity index 80% rename from feature-search/src/androidMain/kotlin/search/presentation/navigation/SearchStreamNavigation.kt rename to feature-search/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_search/presentation/navigation/SearchStreamNavigation.kt index afc7bbbe..ebcf0742 100644 --- a/feature-search/src/androidMain/kotlin/search/presentation/navigation/SearchStreamNavigation.kt +++ b/feature-search/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_search/presentation/navigation/SearchStreamNavigation.kt @@ -1,4 +1,4 @@ -package search.presentation.navigation +package com.codandotv.streamplayerapp.feature_search.presentation.navigation import androidx.activity.compose.BackHandler import androidx.lifecycle.Lifecycle @@ -6,8 +6,8 @@ import androidx.navigation.NavGraphBuilder import androidx.navigation.NavHostController import androidx.navigation.compose.composable import com.codandotv.streamplayerapp.core_navigation.routes.Routes -import search.di.SearchModule -import search.presentation.screens.SearchScreen +import com.codandotv.streamplayerapp.feature_search.di.SearchModule +import com.codandotv.streamplayerapp.feature_search.presentation.screens.SearchScreen import org.koin.core.context.loadKoinModules import org.koin.core.context.unloadKoinModules diff --git a/feature-search/src/androidMain/kotlin/search/presentation/screens/SearchScreen.kt b/feature-search/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_search/presentation/screens/SearchScreen.kt similarity index 91% rename from feature-search/src/androidMain/kotlin/search/presentation/screens/SearchScreen.kt rename to feature-search/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_search/presentation/screens/SearchScreen.kt index 45d8da1b..2064ee7a 100644 --- a/feature-search/src/androidMain/kotlin/search/presentation/screens/SearchScreen.kt +++ b/feature-search/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_search/presentation/screens/SearchScreen.kt @@ -1,4 +1,4 @@ -package search.presentation.screens +package com.codandotv.streamplayerapp.feature_search.presentation.screens import androidx.activity.compose.BackHandler import androidx.compose.foundation.layout.Box @@ -27,11 +27,11 @@ import androidx.lifecycle.LifecycleOwner import androidx.navigation.NavController import com.codandotv.streamplayerapp.core_navigation.extensions.goBack import com.codandotv.streamplayerapp.feature.search.R -import search.domain.mapper.toSearchStreamCardModel -import search.presentation.widgets.SearchStreamCard -import search.presentation.widgets.SearchableTopBar -import search.presentation.widgets.StreamsEmpty -import search.presentation.widgets.StreamsError +import com.codandotv.streamplayerapp.feature_search.domain.mapper.toSearchStreamCardModel +import com.codandotv.streamplayerapp.feature_search.presentation.widgets.SearchStreamCard +import com.codandotv.streamplayerapp.feature_search.presentation.widgets.SearchableTopBar +import com.codandotv.streamplayerapp.feature_search.presentation.widgets.StreamsEmpty +import com.codandotv.streamplayerapp.feature_search.presentation.widgets.StreamsError import org.koin.androidx.compose.koinViewModel @Composable diff --git a/feature-search/src/androidMain/kotlin/search/presentation/screens/SearchUIState.kt b/feature-search/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_search/presentation/screens/SearchUIState.kt similarity index 62% rename from feature-search/src/androidMain/kotlin/search/presentation/screens/SearchUIState.kt rename to feature-search/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_search/presentation/screens/SearchUIState.kt index 47ab8e68..3e716dfc 100644 --- a/feature-search/src/androidMain/kotlin/search/presentation/screens/SearchUIState.kt +++ b/feature-search/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_search/presentation/screens/SearchUIState.kt @@ -1,6 +1,6 @@ -package search.presentation.screens +package com.codandotv.streamplayerapp.feature_search.presentation.screens -import search.data.model.ListSearchStreamResponse +import com.codandotv.streamplayerapp.feature_search.data.model.ListSearchStreamResponse sealed class SearchUIState { data class Success(val listCharacters: ListSearchStreamResponse) : SearchUIState() diff --git a/feature-search/src/androidMain/kotlin/search/presentation/screens/SearchViewModel.kt b/feature-search/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_search/presentation/screens/SearchViewModel.kt similarity index 93% rename from feature-search/src/androidMain/kotlin/search/presentation/screens/SearchViewModel.kt rename to feature-search/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_search/presentation/screens/SearchViewModel.kt index 21d8b30d..f1d48fdd 100644 --- a/feature-search/src/androidMain/kotlin/search/presentation/screens/SearchViewModel.kt +++ b/feature-search/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_search/presentation/screens/SearchViewModel.kt @@ -1,11 +1,11 @@ -package search.presentation.screens +package com.codandotv.streamplayerapp.feature_search.presentation.screens import androidx.lifecycle.DefaultLifecycleObserver import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import com.codandotv.streamplayerapp.core_networking.handleError.catchFailure -import search.domain.MostPopularMoviesUseCase -import search.domain.SearchUseCase +import com.codandotv.streamplayerapp.feature_search.domain.MostPopularMoviesUseCase +import com.codandotv.streamplayerapp.feature_search.domain.SearchUseCase import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow diff --git a/feature-search/src/androidMain/kotlin/search/presentation/widgets/SearchCarousel.kt b/feature-search/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_search/presentation/widgets/SearchCarousel.kt similarity index 98% rename from feature-search/src/androidMain/kotlin/search/presentation/widgets/SearchCarousel.kt rename to feature-search/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_search/presentation/widgets/SearchCarousel.kt index dc490347..a12d559d 100644 --- a/feature-search/src/androidMain/kotlin/search/presentation/widgets/SearchCarousel.kt +++ b/feature-search/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_search/presentation/widgets/SearchCarousel.kt @@ -1,4 +1,4 @@ -package search.presentation.widgets +package com.codandotv.streamplayerapp.feature_search.presentation.widgets import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column diff --git a/feature-search/src/androidMain/kotlin/search/presentation/widgets/SearchStreamCard.kt b/feature-search/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_search/presentation/widgets/SearchStreamCard.kt similarity index 97% rename from feature-search/src/androidMain/kotlin/search/presentation/widgets/SearchStreamCard.kt rename to feature-search/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_search/presentation/widgets/SearchStreamCard.kt index 9b14c597..54f75499 100644 --- a/feature-search/src/androidMain/kotlin/search/presentation/widgets/SearchStreamCard.kt +++ b/feature-search/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_search/presentation/widgets/SearchStreamCard.kt @@ -1,4 +1,4 @@ -package search.presentation.widgets +package com.codandotv.streamplayerapp.feature_search.presentation.widgets import androidx.compose.foundation.background import androidx.compose.foundation.border diff --git a/feature-search/src/androidMain/kotlin/search/presentation/widgets/SearchStreams.kt b/feature-search/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_search/presentation/widgets/SearchStreams.kt similarity index 99% rename from feature-search/src/androidMain/kotlin/search/presentation/widgets/SearchStreams.kt rename to feature-search/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_search/presentation/widgets/SearchStreams.kt index 47307f20..978aa2e0 100644 --- a/feature-search/src/androidMain/kotlin/search/presentation/widgets/SearchStreams.kt +++ b/feature-search/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_search/presentation/widgets/SearchStreams.kt @@ -1,4 +1,4 @@ -package search.presentation.widgets +package com.codandotv.streamplayerapp.feature_search.presentation.widgets import androidx.compose.foundation.background import androidx.compose.foundation.layout.Box diff --git a/feature-search/src/androidMain/kotlin/search/di/SearchModule.kt b/feature-search/src/androidMain/kotlin/search/di/SearchModule.kt deleted file mode 100644 index 4a55f059..00000000 --- a/feature-search/src/androidMain/kotlin/search/di/SearchModule.kt +++ /dev/null @@ -1,43 +0,0 @@ -package search.di - -import search.data.api.SearchStreamService -import search.data.datasource.SearchStreamDataSourceImpl -import search.data.datasource.MostPopularMoviesDataSource -import search.data.datasource.MostPopularMoviesDataSourceImpl -import search.data.repository.MostPopularMoviesRepository -import search.data.repository.MostPopularMoviesRepositoryImpl -import search.data.api.MostPopularMoviesService -import search.data.api.MostPopularMoviesServiceImpl -import search.data.api.SearchStreamServiceImpl -import search.data.datasource.SearchStreamDataSource -import search.data.repository.SearchStreamRepository -import search.data.repository.SearchStreamRepositoryImp -import search.domain.MostPopularMoviesUseCase -import search.domain.MostPopularMoviesUseCaseImpl -import search.domain.SearchUseCase -import search.domain.SearchUseCaseImpl -import search.presentation.screens.SearchViewModel -import org.koin.androidx.viewmodel.dsl.viewModel -import org.koin.dsl.module - -object SearchModule { - val module = module { - viewModel { - SearchViewModel( - searchUseCase = get(), - mostPopularMoviesUseCase = get() - ) - } - - factory { SearchStreamServiceImpl(get()) } - factory { MostPopularMoviesServiceImpl(get()) } - - factory { MostPopularMoviesUseCaseImpl(repository = get()) } - factory { MostPopularMoviesDataSourceImpl(service = get()) } - factory { MostPopularMoviesRepositoryImpl(dataSource = get()) } - - factory { SearchUseCaseImpl(repository = get()) } - factory { SearchStreamDataSourceImpl(service = get()) } - factory { SearchStreamRepositoryImp(dataSource = get()) } - } -} diff --git a/feature-search/src/androidMain/kotlin/search/domain/mapper/SearchMapper.kt b/feature-search/src/androidMain/kotlin/search/domain/mapper/SearchMapper.kt deleted file mode 100644 index 72b8fa04..00000000 --- a/feature-search/src/androidMain/kotlin/search/domain/mapper/SearchMapper.kt +++ /dev/null @@ -1,11 +0,0 @@ -package search.domain.mapper - -import com.codandotv.streamplayerapp.core_shared.Url -import search.data.model.ListSearchStreamResponse.SearchStreamResponse -import search.presentation.widgets.SearchStreamCardModel - -fun SearchStreamResponse.toSearchStreamCardModel() = SearchStreamCardModel( - id = id.toString(), - title = title, - url = "${Url.IMAGE_URL_SIZE_200}${posterPath}" -) From c05cdd60a0636ca3db518dfe9613aa400e3305be Mon Sep 17 00:00:00 2001 From: Rods Date: Mon, 23 Dec 2024 12:23:14 -0300 Subject: [PATCH 21/96] [ISSUE-2] - PRs suggestions --- .../com.streamplayer.kmp-library.gradle.kts | 3 +++ core-navigation/build.gradle.kts | 1 - feature-detail/build.gradle.kts | 4 ---- feature-list-streams/.gitignore | 2 +- feature-list-streams/build.gradle.kts | 3 --- feature-list-streams/consumer-rules.pro | 0 feature-list-streams/proguard-rules.pro | 21 ------------------- feature-search/build.gradle.kts | 3 --- feature-search/consumer-rules.pro | 0 feature-search/proguard-rules.pro | 21 ------------------- 10 files changed, 4 insertions(+), 54 deletions(-) delete mode 100644 feature-list-streams/consumer-rules.pro delete mode 100644 feature-list-streams/proguard-rules.pro delete mode 100644 feature-search/consumer-rules.pro delete mode 100644 feature-search/proguard-rules.pro diff --git a/build-logic/src/main/java/com.streamplayer.kmp-library.gradle.kts b/build-logic/src/main/java/com.streamplayer.kmp-library.gradle.kts index d865de17..0f8d3d61 100644 --- a/build-logic/src/main/java/com.streamplayer.kmp-library.gradle.kts +++ b/build-logic/src/main/java/com.streamplayer.kmp-library.gradle.kts @@ -29,6 +29,9 @@ kotlin { jvmTarget.set(JvmTarget.JVM_17) } } + sourceSets.named("androidMain").configure { + kotlin.srcDir("build/generated/ksp/metadata/androidMain/kotlin") + } } android { diff --git a/core-navigation/build.gradle.kts b/core-navigation/build.gradle.kts index f1f5da6e..93248caf 100644 --- a/core-navigation/build.gradle.kts +++ b/core-navigation/build.gradle.kts @@ -1,7 +1,6 @@ @file:Suppress("UnstableApiUsage") plugins { id("com.streamplayer.android-library") - id("kotlin-android") alias(libs.plugins.jetbrains.compose) alias(libs.plugins.compose.compiler) } diff --git a/feature-detail/build.gradle.kts b/feature-detail/build.gradle.kts index f4231731..5f4648bf 100644 --- a/feature-detail/build.gradle.kts +++ b/feature-detail/build.gradle.kts @@ -37,10 +37,6 @@ kotlin { commonTest.dependencies { implementation(libs.bundles.test) } - - sourceSets.named("androidMain").configure { - kotlin.srcDir("build/generated/ksp/metadata/androidMain/kotlin") - } } } diff --git a/feature-list-streams/.gitignore b/feature-list-streams/.gitignore index 42afabfd..581f8daa 100644 --- a/feature-list-streams/.gitignore +++ b/feature-list-streams/.gitignore @@ -1 +1 @@ -/build \ No newline at end of file +**/build/** \ No newline at end of file diff --git a/feature-list-streams/build.gradle.kts b/feature-list-streams/build.gradle.kts index d06f8932..cc103e72 100644 --- a/feature-list-streams/build.gradle.kts +++ b/feature-list-streams/build.gradle.kts @@ -33,9 +33,6 @@ kotlin { implementation(libs.bundles.androidSupport) } - sourceSets.named("androidMain").configure { - kotlin.srcDir("build/generated/ksp/metadata/androidMain/kotlin") - } } } diff --git a/feature-list-streams/consumer-rules.pro b/feature-list-streams/consumer-rules.pro deleted file mode 100644 index e69de29b..00000000 diff --git a/feature-list-streams/proguard-rules.pro b/feature-list-streams/proguard-rules.pro deleted file mode 100644 index 481bb434..00000000 --- a/feature-list-streams/proguard-rules.pro +++ /dev/null @@ -1,21 +0,0 @@ -# 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/feature-search/build.gradle.kts b/feature-search/build.gradle.kts index d06f8932..cc103e72 100644 --- a/feature-search/build.gradle.kts +++ b/feature-search/build.gradle.kts @@ -33,9 +33,6 @@ kotlin { implementation(libs.bundles.androidSupport) } - sourceSets.named("androidMain").configure { - kotlin.srcDir("build/generated/ksp/metadata/androidMain/kotlin") - } } } diff --git a/feature-search/consumer-rules.pro b/feature-search/consumer-rules.pro deleted file mode 100644 index e69de29b..00000000 diff --git a/feature-search/proguard-rules.pro b/feature-search/proguard-rules.pro deleted file mode 100644 index 481bb434..00000000 --- a/feature-search/proguard-rules.pro +++ /dev/null @@ -1,21 +0,0 @@ -# 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 From 22c90d02dfc82dabe498027fa497917e94b54c57 Mon Sep 17 00:00:00 2001 From: Rods Date: Mon, 23 Dec 2024 12:29:53 -0300 Subject: [PATCH 22/96] [ISSUE-2] - PRs suggestions --- .../src/main/java/com.streamplayer.android-library.gradle.kts | 2 -- 1 file changed, 2 deletions(-) diff --git a/build-logic/src/main/java/com.streamplayer.android-library.gradle.kts b/build-logic/src/main/java/com.streamplayer.android-library.gradle.kts index 85c5eb3a..196d8a85 100644 --- a/build-logic/src/main/java/com.streamplayer.android-library.gradle.kts +++ b/build-logic/src/main/java/com.streamplayer.android-library.gradle.kts @@ -10,8 +10,6 @@ import extensions.setupPackingOptions val libs: VersionCatalog = extensions.getByType().named("libs") plugins { - //Note: The best approach might be to create a separate feature plugin to further isolate serialization, and do this later. - id("org.jetbrains.kotlin.plugin.serialization") id("com.android.library") id("kotlin-kapt") id("kotlin-android") From c3c02d49dbc10f785b1d4f5b6f52b483d8b3abde Mon Sep 17 00:00:00 2001 From: Rods Date: Tue, 31 Dec 2024 17:56:00 -0300 Subject: [PATCH 23/96] [ISSUE-2] - removing block unnecessary --- .../src/main/java/com.streamplayer.kmp-library.gradle.kts | 3 --- 1 file changed, 3 deletions(-) diff --git a/build-logic/src/main/java/com.streamplayer.kmp-library.gradle.kts b/build-logic/src/main/java/com.streamplayer.kmp-library.gradle.kts index 0f8d3d61..d865de17 100644 --- a/build-logic/src/main/java/com.streamplayer.kmp-library.gradle.kts +++ b/build-logic/src/main/java/com.streamplayer.kmp-library.gradle.kts @@ -29,9 +29,6 @@ kotlin { jvmTarget.set(JvmTarget.JVM_17) } } - sourceSets.named("androidMain").configure { - kotlin.srcDir("build/generated/ksp/metadata/androidMain/kotlin") - } } android { From a58631c7bd675964b1c0fa26a85cf00805e4ee23 Mon Sep 17 00:00:00 2001 From: Rods Date: Wed, 15 Jan 2025 20:34:43 -0300 Subject: [PATCH 24/96] [ISSUE-3] - core-shared-ui to androidMain --- core-shared-ui/build.gradle.kts | 34 ++++++++++-------- .../core_shared_ui/resources/Colors.kt | 0 .../core_shared_ui/theme/StreamPlayerTheme.kt | 0 .../core_shared_ui/theme/ThemePreviews.kt | 0 .../core_shared_ui/utils/Sharing.kt | 0 .../core_shared_ui/widget/IconWithText.kt | 0 .../core_shared_ui/widget/PlayerComponent.kt | 0 .../widget/SharingStreamCustomView.kt | 0 .../widget/StreamPlayerTopBar.kt | 0 .../core_shared_ui/widget/StreamsCard.kt | 0 .../core_shared_ui/widget/StreamsCarousel.kt | 0 .../res/drawable/ic_add.xml | 0 .../res/drawable/ic_close.xml | 0 .../res/drawable/ic_copy_content.xml | 0 .../res/drawable/ic_info.xml | 0 .../res/drawable/ic_instagram.xml | 0 .../res/drawable/ic_message.xml | 0 .../res/drawable/ic_netflix.png | Bin .../res/drawable/ic_netflix_background.xml | 0 .../res/drawable/ic_netflix_foreground.xml | 0 .../res/drawable/ic_play.xml | 0 .../res/drawable/ic_whatsapp.xml | 0 .../res/drawable/perfil_fake.png | Bin .../res/drawable/transparent_image.xml | 0 .../res/mipmap-anydpi-v26/ic_netflix.xml | 0 .../mipmap-anydpi-v26/ic_netflix_round.xml | 0 .../res/mipmap-hdpi/ic_netflix.png | Bin .../res/mipmap-hdpi/ic_netflix_round.png | Bin .../res/mipmap-mdpi/ic_netflix.png | Bin .../res/mipmap-mdpi/ic_netflix_round.png | Bin .../res/mipmap-xhdpi/ic_netflix.png | Bin .../res/mipmap-xhdpi/ic_netflix_round.png | Bin .../res/mipmap-xxhdpi/ic_netflix.png | Bin .../res/mipmap-xxhdpi/ic_netflix_round.png | Bin .../res/mipmap-xxxhdpi/ic_netflix.png | Bin .../res/mipmap-xxxhdpi/ic_netflix_round.png | Bin .../{main => androidMain}/res/raw/logo.json | 0 .../res/values-night/themes.xml | 0 .../res/values-v31/themes.xml | 0 .../res/values/colors.xml | 0 .../res/values/content-description.xml | 0 .../res/values/strings.xml | 0 .../res/values/themes.xml | 0 core-shared-ui/src/main/AndroidManifest.xml | 8 ----- 44 files changed, 19 insertions(+), 23 deletions(-) rename core-shared-ui/src/{main/java => androidMain/kotlin}/com/codandotv/streamplayerapp/core_shared_ui/resources/Colors.kt (100%) rename core-shared-ui/src/{main/java => androidMain/kotlin}/com/codandotv/streamplayerapp/core_shared_ui/theme/StreamPlayerTheme.kt (100%) rename core-shared-ui/src/{main/java => androidMain/kotlin}/com/codandotv/streamplayerapp/core_shared_ui/theme/ThemePreviews.kt (100%) rename core-shared-ui/src/{main/java => androidMain/kotlin}/com/codandotv/streamplayerapp/core_shared_ui/utils/Sharing.kt (100%) rename core-shared-ui/src/{main/java => androidMain/kotlin}/com/codandotv/streamplayerapp/core_shared_ui/widget/IconWithText.kt (100%) rename core-shared-ui/src/{main/java => androidMain/kotlin}/com/codandotv/streamplayerapp/core_shared_ui/widget/PlayerComponent.kt (100%) rename core-shared-ui/src/{main/java => androidMain/kotlin}/com/codandotv/streamplayerapp/core_shared_ui/widget/SharingStreamCustomView.kt (100%) rename core-shared-ui/src/{main/java => androidMain/kotlin}/com/codandotv/streamplayerapp/core_shared_ui/widget/StreamPlayerTopBar.kt (100%) rename core-shared-ui/src/{main/java => androidMain/kotlin}/com/codandotv/streamplayerapp/core_shared_ui/widget/StreamsCard.kt (100%) rename core-shared-ui/src/{main/java => androidMain/kotlin}/com/codandotv/streamplayerapp/core_shared_ui/widget/StreamsCarousel.kt (100%) rename core-shared-ui/src/{main => androidMain}/res/drawable/ic_add.xml (100%) rename core-shared-ui/src/{main => androidMain}/res/drawable/ic_close.xml (100%) rename core-shared-ui/src/{main => androidMain}/res/drawable/ic_copy_content.xml (100%) rename core-shared-ui/src/{main => androidMain}/res/drawable/ic_info.xml (100%) rename core-shared-ui/src/{main => androidMain}/res/drawable/ic_instagram.xml (100%) rename core-shared-ui/src/{main => androidMain}/res/drawable/ic_message.xml (100%) rename core-shared-ui/src/{main => androidMain}/res/drawable/ic_netflix.png (100%) rename core-shared-ui/src/{main => androidMain}/res/drawable/ic_netflix_background.xml (100%) rename core-shared-ui/src/{main => androidMain}/res/drawable/ic_netflix_foreground.xml (100%) rename core-shared-ui/src/{main => androidMain}/res/drawable/ic_play.xml (100%) rename core-shared-ui/src/{main => androidMain}/res/drawable/ic_whatsapp.xml (100%) rename core-shared-ui/src/{main => androidMain}/res/drawable/perfil_fake.png (100%) rename core-shared-ui/src/{main => androidMain}/res/drawable/transparent_image.xml (100%) rename core-shared-ui/src/{main => androidMain}/res/mipmap-anydpi-v26/ic_netflix.xml (100%) rename core-shared-ui/src/{main => androidMain}/res/mipmap-anydpi-v26/ic_netflix_round.xml (100%) rename core-shared-ui/src/{main => androidMain}/res/mipmap-hdpi/ic_netflix.png (100%) rename core-shared-ui/src/{main => androidMain}/res/mipmap-hdpi/ic_netflix_round.png (100%) rename core-shared-ui/src/{main => androidMain}/res/mipmap-mdpi/ic_netflix.png (100%) rename core-shared-ui/src/{main => androidMain}/res/mipmap-mdpi/ic_netflix_round.png (100%) rename core-shared-ui/src/{main => androidMain}/res/mipmap-xhdpi/ic_netflix.png (100%) rename core-shared-ui/src/{main => androidMain}/res/mipmap-xhdpi/ic_netflix_round.png (100%) rename core-shared-ui/src/{main => androidMain}/res/mipmap-xxhdpi/ic_netflix.png (100%) rename core-shared-ui/src/{main => androidMain}/res/mipmap-xxhdpi/ic_netflix_round.png (100%) rename core-shared-ui/src/{main => androidMain}/res/mipmap-xxxhdpi/ic_netflix.png (100%) rename core-shared-ui/src/{main => androidMain}/res/mipmap-xxxhdpi/ic_netflix_round.png (100%) rename core-shared-ui/src/{main => androidMain}/res/raw/logo.json (100%) rename core-shared-ui/src/{main => androidMain}/res/values-night/themes.xml (100%) rename core-shared-ui/src/{main => androidMain}/res/values-v31/themes.xml (100%) rename core-shared-ui/src/{main => androidMain}/res/values/colors.xml (100%) rename core-shared-ui/src/{main => androidMain}/res/values/content-description.xml (100%) rename core-shared-ui/src/{main => androidMain}/res/values/strings.xml (100%) rename core-shared-ui/src/{main => androidMain}/res/values/themes.xml (100%) delete mode 100644 core-shared-ui/src/main/AndroidManifest.xml diff --git a/core-shared-ui/build.gradle.kts b/core-shared-ui/build.gradle.kts index 1a4e4c49..5e12fe36 100644 --- a/core-shared-ui/build.gradle.kts +++ b/core-shared-ui/build.gradle.kts @@ -1,21 +1,25 @@ @file:Suppress("UnstableApiUsage") plugins { - id("com.streamplayer.android-library") + id("com.streamplayer.kmp-library") alias(libs.plugins.jetbrains.compose) alias(libs.plugins.compose.compiler) } -dependencies { - implementation(projects.coreShared) - implementation(compose.material3) - implementation(compose.preview) - implementation(compose.ui) - implementation(libs.navigation.compose) - implementation(libs.bundles.koin) - implementation(libs.bundles.kotlin) - implementation(libs.bundles.androidSupport) - implementation(libs.android.youtube.player) - implementation(libs.paging.compose) - implementation(libs.coil) - testImplementation(libs.bundles.test) -} \ No newline at end of file +kotlin { + sourceSets { + androidMain.dependencies { + implementation(projects.coreShared) + implementation(compose.material3) + implementation(compose.preview) + implementation(compose.ui) + implementation(libs.navigation.compose) + implementation(libs.bundles.koin) + implementation(libs.bundles.kotlin) + implementation(libs.bundles.androidSupport) + implementation(libs.android.youtube.player) + implementation(libs.paging.compose) + implementation(libs.coil) + } + } +} + diff --git a/core-shared-ui/src/main/java/com/codandotv/streamplayerapp/core_shared_ui/resources/Colors.kt b/core-shared-ui/src/androidMain/kotlin/com/codandotv/streamplayerapp/core_shared_ui/resources/Colors.kt similarity index 100% rename from core-shared-ui/src/main/java/com/codandotv/streamplayerapp/core_shared_ui/resources/Colors.kt rename to core-shared-ui/src/androidMain/kotlin/com/codandotv/streamplayerapp/core_shared_ui/resources/Colors.kt diff --git a/core-shared-ui/src/main/java/com/codandotv/streamplayerapp/core_shared_ui/theme/StreamPlayerTheme.kt b/core-shared-ui/src/androidMain/kotlin/com/codandotv/streamplayerapp/core_shared_ui/theme/StreamPlayerTheme.kt similarity index 100% rename from core-shared-ui/src/main/java/com/codandotv/streamplayerapp/core_shared_ui/theme/StreamPlayerTheme.kt rename to core-shared-ui/src/androidMain/kotlin/com/codandotv/streamplayerapp/core_shared_ui/theme/StreamPlayerTheme.kt diff --git a/core-shared-ui/src/main/java/com/codandotv/streamplayerapp/core_shared_ui/theme/ThemePreviews.kt b/core-shared-ui/src/androidMain/kotlin/com/codandotv/streamplayerapp/core_shared_ui/theme/ThemePreviews.kt similarity index 100% rename from core-shared-ui/src/main/java/com/codandotv/streamplayerapp/core_shared_ui/theme/ThemePreviews.kt rename to core-shared-ui/src/androidMain/kotlin/com/codandotv/streamplayerapp/core_shared_ui/theme/ThemePreviews.kt diff --git a/core-shared-ui/src/main/java/com/codandotv/streamplayerapp/core_shared_ui/utils/Sharing.kt b/core-shared-ui/src/androidMain/kotlin/com/codandotv/streamplayerapp/core_shared_ui/utils/Sharing.kt similarity index 100% rename from core-shared-ui/src/main/java/com/codandotv/streamplayerapp/core_shared_ui/utils/Sharing.kt rename to core-shared-ui/src/androidMain/kotlin/com/codandotv/streamplayerapp/core_shared_ui/utils/Sharing.kt diff --git a/core-shared-ui/src/main/java/com/codandotv/streamplayerapp/core_shared_ui/widget/IconWithText.kt b/core-shared-ui/src/androidMain/kotlin/com/codandotv/streamplayerapp/core_shared_ui/widget/IconWithText.kt similarity index 100% rename from core-shared-ui/src/main/java/com/codandotv/streamplayerapp/core_shared_ui/widget/IconWithText.kt rename to core-shared-ui/src/androidMain/kotlin/com/codandotv/streamplayerapp/core_shared_ui/widget/IconWithText.kt diff --git a/core-shared-ui/src/main/java/com/codandotv/streamplayerapp/core_shared_ui/widget/PlayerComponent.kt b/core-shared-ui/src/androidMain/kotlin/com/codandotv/streamplayerapp/core_shared_ui/widget/PlayerComponent.kt similarity index 100% rename from core-shared-ui/src/main/java/com/codandotv/streamplayerapp/core_shared_ui/widget/PlayerComponent.kt rename to core-shared-ui/src/androidMain/kotlin/com/codandotv/streamplayerapp/core_shared_ui/widget/PlayerComponent.kt diff --git a/core-shared-ui/src/main/java/com/codandotv/streamplayerapp/core_shared_ui/widget/SharingStreamCustomView.kt b/core-shared-ui/src/androidMain/kotlin/com/codandotv/streamplayerapp/core_shared_ui/widget/SharingStreamCustomView.kt similarity index 100% rename from core-shared-ui/src/main/java/com/codandotv/streamplayerapp/core_shared_ui/widget/SharingStreamCustomView.kt rename to core-shared-ui/src/androidMain/kotlin/com/codandotv/streamplayerapp/core_shared_ui/widget/SharingStreamCustomView.kt diff --git a/core-shared-ui/src/main/java/com/codandotv/streamplayerapp/core_shared_ui/widget/StreamPlayerTopBar.kt b/core-shared-ui/src/androidMain/kotlin/com/codandotv/streamplayerapp/core_shared_ui/widget/StreamPlayerTopBar.kt similarity index 100% rename from core-shared-ui/src/main/java/com/codandotv/streamplayerapp/core_shared_ui/widget/StreamPlayerTopBar.kt rename to core-shared-ui/src/androidMain/kotlin/com/codandotv/streamplayerapp/core_shared_ui/widget/StreamPlayerTopBar.kt diff --git a/core-shared-ui/src/main/java/com/codandotv/streamplayerapp/core_shared_ui/widget/StreamsCard.kt b/core-shared-ui/src/androidMain/kotlin/com/codandotv/streamplayerapp/core_shared_ui/widget/StreamsCard.kt similarity index 100% rename from core-shared-ui/src/main/java/com/codandotv/streamplayerapp/core_shared_ui/widget/StreamsCard.kt rename to core-shared-ui/src/androidMain/kotlin/com/codandotv/streamplayerapp/core_shared_ui/widget/StreamsCard.kt diff --git a/core-shared-ui/src/main/java/com/codandotv/streamplayerapp/core_shared_ui/widget/StreamsCarousel.kt b/core-shared-ui/src/androidMain/kotlin/com/codandotv/streamplayerapp/core_shared_ui/widget/StreamsCarousel.kt similarity index 100% rename from core-shared-ui/src/main/java/com/codandotv/streamplayerapp/core_shared_ui/widget/StreamsCarousel.kt rename to core-shared-ui/src/androidMain/kotlin/com/codandotv/streamplayerapp/core_shared_ui/widget/StreamsCarousel.kt diff --git a/core-shared-ui/src/main/res/drawable/ic_add.xml b/core-shared-ui/src/androidMain/res/drawable/ic_add.xml similarity index 100% rename from core-shared-ui/src/main/res/drawable/ic_add.xml rename to core-shared-ui/src/androidMain/res/drawable/ic_add.xml diff --git a/core-shared-ui/src/main/res/drawable/ic_close.xml b/core-shared-ui/src/androidMain/res/drawable/ic_close.xml similarity index 100% rename from core-shared-ui/src/main/res/drawable/ic_close.xml rename to core-shared-ui/src/androidMain/res/drawable/ic_close.xml diff --git a/core-shared-ui/src/main/res/drawable/ic_copy_content.xml b/core-shared-ui/src/androidMain/res/drawable/ic_copy_content.xml similarity index 100% rename from core-shared-ui/src/main/res/drawable/ic_copy_content.xml rename to core-shared-ui/src/androidMain/res/drawable/ic_copy_content.xml diff --git a/core-shared-ui/src/main/res/drawable/ic_info.xml b/core-shared-ui/src/androidMain/res/drawable/ic_info.xml similarity index 100% rename from core-shared-ui/src/main/res/drawable/ic_info.xml rename to core-shared-ui/src/androidMain/res/drawable/ic_info.xml diff --git a/core-shared-ui/src/main/res/drawable/ic_instagram.xml b/core-shared-ui/src/androidMain/res/drawable/ic_instagram.xml similarity index 100% rename from core-shared-ui/src/main/res/drawable/ic_instagram.xml rename to core-shared-ui/src/androidMain/res/drawable/ic_instagram.xml diff --git a/core-shared-ui/src/main/res/drawable/ic_message.xml b/core-shared-ui/src/androidMain/res/drawable/ic_message.xml similarity index 100% rename from core-shared-ui/src/main/res/drawable/ic_message.xml rename to core-shared-ui/src/androidMain/res/drawable/ic_message.xml diff --git a/core-shared-ui/src/main/res/drawable/ic_netflix.png b/core-shared-ui/src/androidMain/res/drawable/ic_netflix.png similarity index 100% rename from core-shared-ui/src/main/res/drawable/ic_netflix.png rename to core-shared-ui/src/androidMain/res/drawable/ic_netflix.png diff --git a/core-shared-ui/src/main/res/drawable/ic_netflix_background.xml b/core-shared-ui/src/androidMain/res/drawable/ic_netflix_background.xml similarity index 100% rename from core-shared-ui/src/main/res/drawable/ic_netflix_background.xml rename to core-shared-ui/src/androidMain/res/drawable/ic_netflix_background.xml diff --git a/core-shared-ui/src/main/res/drawable/ic_netflix_foreground.xml b/core-shared-ui/src/androidMain/res/drawable/ic_netflix_foreground.xml similarity index 100% rename from core-shared-ui/src/main/res/drawable/ic_netflix_foreground.xml rename to core-shared-ui/src/androidMain/res/drawable/ic_netflix_foreground.xml diff --git a/core-shared-ui/src/main/res/drawable/ic_play.xml b/core-shared-ui/src/androidMain/res/drawable/ic_play.xml similarity index 100% rename from core-shared-ui/src/main/res/drawable/ic_play.xml rename to core-shared-ui/src/androidMain/res/drawable/ic_play.xml diff --git a/core-shared-ui/src/main/res/drawable/ic_whatsapp.xml b/core-shared-ui/src/androidMain/res/drawable/ic_whatsapp.xml similarity index 100% rename from core-shared-ui/src/main/res/drawable/ic_whatsapp.xml rename to core-shared-ui/src/androidMain/res/drawable/ic_whatsapp.xml diff --git a/core-shared-ui/src/main/res/drawable/perfil_fake.png b/core-shared-ui/src/androidMain/res/drawable/perfil_fake.png similarity index 100% rename from core-shared-ui/src/main/res/drawable/perfil_fake.png rename to core-shared-ui/src/androidMain/res/drawable/perfil_fake.png diff --git a/core-shared-ui/src/main/res/drawable/transparent_image.xml b/core-shared-ui/src/androidMain/res/drawable/transparent_image.xml similarity index 100% rename from core-shared-ui/src/main/res/drawable/transparent_image.xml rename to core-shared-ui/src/androidMain/res/drawable/transparent_image.xml diff --git a/core-shared-ui/src/main/res/mipmap-anydpi-v26/ic_netflix.xml b/core-shared-ui/src/androidMain/res/mipmap-anydpi-v26/ic_netflix.xml similarity index 100% rename from core-shared-ui/src/main/res/mipmap-anydpi-v26/ic_netflix.xml rename to core-shared-ui/src/androidMain/res/mipmap-anydpi-v26/ic_netflix.xml diff --git a/core-shared-ui/src/main/res/mipmap-anydpi-v26/ic_netflix_round.xml b/core-shared-ui/src/androidMain/res/mipmap-anydpi-v26/ic_netflix_round.xml similarity index 100% rename from core-shared-ui/src/main/res/mipmap-anydpi-v26/ic_netflix_round.xml rename to core-shared-ui/src/androidMain/res/mipmap-anydpi-v26/ic_netflix_round.xml diff --git a/core-shared-ui/src/main/res/mipmap-hdpi/ic_netflix.png b/core-shared-ui/src/androidMain/res/mipmap-hdpi/ic_netflix.png similarity index 100% rename from core-shared-ui/src/main/res/mipmap-hdpi/ic_netflix.png rename to core-shared-ui/src/androidMain/res/mipmap-hdpi/ic_netflix.png diff --git a/core-shared-ui/src/main/res/mipmap-hdpi/ic_netflix_round.png b/core-shared-ui/src/androidMain/res/mipmap-hdpi/ic_netflix_round.png similarity index 100% rename from core-shared-ui/src/main/res/mipmap-hdpi/ic_netflix_round.png rename to core-shared-ui/src/androidMain/res/mipmap-hdpi/ic_netflix_round.png diff --git a/core-shared-ui/src/main/res/mipmap-mdpi/ic_netflix.png b/core-shared-ui/src/androidMain/res/mipmap-mdpi/ic_netflix.png similarity index 100% rename from core-shared-ui/src/main/res/mipmap-mdpi/ic_netflix.png rename to core-shared-ui/src/androidMain/res/mipmap-mdpi/ic_netflix.png diff --git a/core-shared-ui/src/main/res/mipmap-mdpi/ic_netflix_round.png b/core-shared-ui/src/androidMain/res/mipmap-mdpi/ic_netflix_round.png similarity index 100% rename from core-shared-ui/src/main/res/mipmap-mdpi/ic_netflix_round.png rename to core-shared-ui/src/androidMain/res/mipmap-mdpi/ic_netflix_round.png diff --git a/core-shared-ui/src/main/res/mipmap-xhdpi/ic_netflix.png b/core-shared-ui/src/androidMain/res/mipmap-xhdpi/ic_netflix.png similarity index 100% rename from core-shared-ui/src/main/res/mipmap-xhdpi/ic_netflix.png rename to core-shared-ui/src/androidMain/res/mipmap-xhdpi/ic_netflix.png diff --git a/core-shared-ui/src/main/res/mipmap-xhdpi/ic_netflix_round.png b/core-shared-ui/src/androidMain/res/mipmap-xhdpi/ic_netflix_round.png similarity index 100% rename from core-shared-ui/src/main/res/mipmap-xhdpi/ic_netflix_round.png rename to core-shared-ui/src/androidMain/res/mipmap-xhdpi/ic_netflix_round.png diff --git a/core-shared-ui/src/main/res/mipmap-xxhdpi/ic_netflix.png b/core-shared-ui/src/androidMain/res/mipmap-xxhdpi/ic_netflix.png similarity index 100% rename from core-shared-ui/src/main/res/mipmap-xxhdpi/ic_netflix.png rename to core-shared-ui/src/androidMain/res/mipmap-xxhdpi/ic_netflix.png diff --git a/core-shared-ui/src/main/res/mipmap-xxhdpi/ic_netflix_round.png b/core-shared-ui/src/androidMain/res/mipmap-xxhdpi/ic_netflix_round.png similarity index 100% rename from core-shared-ui/src/main/res/mipmap-xxhdpi/ic_netflix_round.png rename to core-shared-ui/src/androidMain/res/mipmap-xxhdpi/ic_netflix_round.png diff --git a/core-shared-ui/src/main/res/mipmap-xxxhdpi/ic_netflix.png b/core-shared-ui/src/androidMain/res/mipmap-xxxhdpi/ic_netflix.png similarity index 100% rename from core-shared-ui/src/main/res/mipmap-xxxhdpi/ic_netflix.png rename to core-shared-ui/src/androidMain/res/mipmap-xxxhdpi/ic_netflix.png diff --git a/core-shared-ui/src/main/res/mipmap-xxxhdpi/ic_netflix_round.png b/core-shared-ui/src/androidMain/res/mipmap-xxxhdpi/ic_netflix_round.png similarity index 100% rename from core-shared-ui/src/main/res/mipmap-xxxhdpi/ic_netflix_round.png rename to core-shared-ui/src/androidMain/res/mipmap-xxxhdpi/ic_netflix_round.png diff --git a/core-shared-ui/src/main/res/raw/logo.json b/core-shared-ui/src/androidMain/res/raw/logo.json similarity index 100% rename from core-shared-ui/src/main/res/raw/logo.json rename to core-shared-ui/src/androidMain/res/raw/logo.json diff --git a/core-shared-ui/src/main/res/values-night/themes.xml b/core-shared-ui/src/androidMain/res/values-night/themes.xml similarity index 100% rename from core-shared-ui/src/main/res/values-night/themes.xml rename to core-shared-ui/src/androidMain/res/values-night/themes.xml diff --git a/core-shared-ui/src/main/res/values-v31/themes.xml b/core-shared-ui/src/androidMain/res/values-v31/themes.xml similarity index 100% rename from core-shared-ui/src/main/res/values-v31/themes.xml rename to core-shared-ui/src/androidMain/res/values-v31/themes.xml diff --git a/core-shared-ui/src/main/res/values/colors.xml b/core-shared-ui/src/androidMain/res/values/colors.xml similarity index 100% rename from core-shared-ui/src/main/res/values/colors.xml rename to core-shared-ui/src/androidMain/res/values/colors.xml diff --git a/core-shared-ui/src/main/res/values/content-description.xml b/core-shared-ui/src/androidMain/res/values/content-description.xml similarity index 100% rename from core-shared-ui/src/main/res/values/content-description.xml rename to core-shared-ui/src/androidMain/res/values/content-description.xml diff --git a/core-shared-ui/src/main/res/values/strings.xml b/core-shared-ui/src/androidMain/res/values/strings.xml similarity index 100% rename from core-shared-ui/src/main/res/values/strings.xml rename to core-shared-ui/src/androidMain/res/values/strings.xml diff --git a/core-shared-ui/src/main/res/values/themes.xml b/core-shared-ui/src/androidMain/res/values/themes.xml similarity index 100% rename from core-shared-ui/src/main/res/values/themes.xml rename to core-shared-ui/src/androidMain/res/values/themes.xml diff --git a/core-shared-ui/src/main/AndroidManifest.xml b/core-shared-ui/src/main/AndroidManifest.xml deleted file mode 100644 index ee784d7a..00000000 --- a/core-shared-ui/src/main/AndroidManifest.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - \ No newline at end of file From 23ffc5b40ea51c62ccd6614aed8b3e66d9b45be9 Mon Sep 17 00:00:00 2001 From: Rods Date: Wed, 15 Jan 2025 20:40:15 -0300 Subject: [PATCH 25/96] [ISSUE-3] - core-shared to androidMain --- core-shared/build.gradle.kts | 11 ++++++++--- .../com/codandotv/streamplayerapp/core_shared/Url.kt | 0 .../streamplayerapp/core_shared/extension/ErrorExt.kt | 0 .../core_shared/extension/String.Ext.kt | 0 .../streamplayerapp/core_shared/extension/UriExt.kt | 0 .../core_shared/qualifier/QualifierDispatcherIO.kt | 0 core-shared/src/main/AndroidManifest.xml | 4 ---- 7 files changed, 8 insertions(+), 7 deletions(-) rename core-shared/src/{main/java => androidMain/kotlin}/com/codandotv/streamplayerapp/core_shared/Url.kt (100%) rename core-shared/src/{main/java => androidMain/kotlin}/com/codandotv/streamplayerapp/core_shared/extension/ErrorExt.kt (100%) rename core-shared/src/{main/java => androidMain/kotlin}/com/codandotv/streamplayerapp/core_shared/extension/String.Ext.kt (100%) rename core-shared/src/{main/java => androidMain/kotlin}/com/codandotv/streamplayerapp/core_shared/extension/UriExt.kt (100%) rename core-shared/src/{main/java => androidMain/kotlin}/com/codandotv/streamplayerapp/core_shared/qualifier/QualifierDispatcherIO.kt (100%) delete mode 100644 core-shared/src/main/AndroidManifest.xml diff --git a/core-shared/build.gradle.kts b/core-shared/build.gradle.kts index f1b45dda..b10865f8 100644 --- a/core-shared/build.gradle.kts +++ b/core-shared/build.gradle.kts @@ -1,7 +1,12 @@ plugins { - id("com.streamplayer.android-library") + id("com.streamplayer.kmp-library") } -dependencies { - implementation(libs.bundles.koin) + +kotlin { + sourceSets { + androidMain.dependencies { + implementation(libs.bundles.koin) + } + } } \ No newline at end of file diff --git a/core-shared/src/main/java/com/codandotv/streamplayerapp/core_shared/Url.kt b/core-shared/src/androidMain/kotlin/com/codandotv/streamplayerapp/core_shared/Url.kt similarity index 100% rename from core-shared/src/main/java/com/codandotv/streamplayerapp/core_shared/Url.kt rename to core-shared/src/androidMain/kotlin/com/codandotv/streamplayerapp/core_shared/Url.kt diff --git a/core-shared/src/main/java/com/codandotv/streamplayerapp/core_shared/extension/ErrorExt.kt b/core-shared/src/androidMain/kotlin/com/codandotv/streamplayerapp/core_shared/extension/ErrorExt.kt similarity index 100% rename from core-shared/src/main/java/com/codandotv/streamplayerapp/core_shared/extension/ErrorExt.kt rename to core-shared/src/androidMain/kotlin/com/codandotv/streamplayerapp/core_shared/extension/ErrorExt.kt diff --git a/core-shared/src/main/java/com/codandotv/streamplayerapp/core_shared/extension/String.Ext.kt b/core-shared/src/androidMain/kotlin/com/codandotv/streamplayerapp/core_shared/extension/String.Ext.kt similarity index 100% rename from core-shared/src/main/java/com/codandotv/streamplayerapp/core_shared/extension/String.Ext.kt rename to core-shared/src/androidMain/kotlin/com/codandotv/streamplayerapp/core_shared/extension/String.Ext.kt diff --git a/core-shared/src/main/java/com/codandotv/streamplayerapp/core_shared/extension/UriExt.kt b/core-shared/src/androidMain/kotlin/com/codandotv/streamplayerapp/core_shared/extension/UriExt.kt similarity index 100% rename from core-shared/src/main/java/com/codandotv/streamplayerapp/core_shared/extension/UriExt.kt rename to core-shared/src/androidMain/kotlin/com/codandotv/streamplayerapp/core_shared/extension/UriExt.kt diff --git a/core-shared/src/main/java/com/codandotv/streamplayerapp/core_shared/qualifier/QualifierDispatcherIO.kt b/core-shared/src/androidMain/kotlin/com/codandotv/streamplayerapp/core_shared/qualifier/QualifierDispatcherIO.kt similarity index 100% rename from core-shared/src/main/java/com/codandotv/streamplayerapp/core_shared/qualifier/QualifierDispatcherIO.kt rename to core-shared/src/androidMain/kotlin/com/codandotv/streamplayerapp/core_shared/qualifier/QualifierDispatcherIO.kt diff --git a/core-shared/src/main/AndroidManifest.xml b/core-shared/src/main/AndroidManifest.xml deleted file mode 100644 index a8800291..00000000 --- a/core-shared/src/main/AndroidManifest.xml +++ /dev/null @@ -1,4 +0,0 @@ - - - - \ No newline at end of file From 5cf3d801e63fd365259d839429e4e322cc6b2fb8 Mon Sep 17 00:00:00 2001 From: Rods Date: Wed, 15 Jan 2025 20:42:50 -0300 Subject: [PATCH 26/96] [ISSUE-3] - core-networking to androidMain --- core-networking/build.gradle.kts | 3 ++- .../streamplayerapp/core_networking/di/NetworkModule.kt | 0 .../streamplayerapp/core_networking/di/QualifierNetworking.kt | 0 .../streamplayerapp/core_networking/handleError/Failure.kt | 0 .../core_networking/handleError/HttpClientConfigExtensions.kt | 0 .../core_networking/handleError/NetworkResponse.kt | 0 .../core_networking/handleError/ResultExtensions.kt | 0 .../src/{main => androidMain}/res/values/strings.xml | 0 core-networking/src/main/AndroidManifest.xml | 4 ---- 9 files changed, 2 insertions(+), 5 deletions(-) rename core-networking/src/{main/java => androidMain/kotlin}/com/codandotv/streamplayerapp/core_networking/di/NetworkModule.kt (100%) rename core-networking/src/{main/java => androidMain/kotlin}/com/codandotv/streamplayerapp/core_networking/di/QualifierNetworking.kt (100%) rename core-networking/src/{main/java => androidMain/kotlin}/com/codandotv/streamplayerapp/core_networking/handleError/Failure.kt (100%) rename core-networking/src/{main/java => androidMain/kotlin}/com/codandotv/streamplayerapp/core_networking/handleError/HttpClientConfigExtensions.kt (100%) rename core-networking/src/{main/java => androidMain/kotlin}/com/codandotv/streamplayerapp/core_networking/handleError/NetworkResponse.kt (100%) rename core-networking/src/{main/java => androidMain/kotlin}/com/codandotv/streamplayerapp/core_networking/handleError/ResultExtensions.kt (100%) rename core-networking/src/{main => androidMain}/res/values/strings.xml (100%) delete mode 100644 core-networking/src/main/AndroidManifest.xml diff --git a/core-networking/build.gradle.kts b/core-networking/build.gradle.kts index f5602562..2ba275b6 100644 --- a/core-networking/build.gradle.kts +++ b/core-networking/build.gradle.kts @@ -1,6 +1,7 @@ plugins { - id("com.streamplayer.android-library") + id("com.streamplayer.kmp-library") } + android { buildFeatures { buildConfig = true diff --git a/core-networking/src/main/java/com/codandotv/streamplayerapp/core_networking/di/NetworkModule.kt b/core-networking/src/androidMain/kotlin/com/codandotv/streamplayerapp/core_networking/di/NetworkModule.kt similarity index 100% rename from core-networking/src/main/java/com/codandotv/streamplayerapp/core_networking/di/NetworkModule.kt rename to core-networking/src/androidMain/kotlin/com/codandotv/streamplayerapp/core_networking/di/NetworkModule.kt diff --git a/core-networking/src/main/java/com/codandotv/streamplayerapp/core_networking/di/QualifierNetworking.kt b/core-networking/src/androidMain/kotlin/com/codandotv/streamplayerapp/core_networking/di/QualifierNetworking.kt similarity index 100% rename from core-networking/src/main/java/com/codandotv/streamplayerapp/core_networking/di/QualifierNetworking.kt rename to core-networking/src/androidMain/kotlin/com/codandotv/streamplayerapp/core_networking/di/QualifierNetworking.kt diff --git a/core-networking/src/main/java/com/codandotv/streamplayerapp/core_networking/handleError/Failure.kt b/core-networking/src/androidMain/kotlin/com/codandotv/streamplayerapp/core_networking/handleError/Failure.kt similarity index 100% rename from core-networking/src/main/java/com/codandotv/streamplayerapp/core_networking/handleError/Failure.kt rename to core-networking/src/androidMain/kotlin/com/codandotv/streamplayerapp/core_networking/handleError/Failure.kt diff --git a/core-networking/src/main/java/com/codandotv/streamplayerapp/core_networking/handleError/HttpClientConfigExtensions.kt b/core-networking/src/androidMain/kotlin/com/codandotv/streamplayerapp/core_networking/handleError/HttpClientConfigExtensions.kt similarity index 100% rename from core-networking/src/main/java/com/codandotv/streamplayerapp/core_networking/handleError/HttpClientConfigExtensions.kt rename to core-networking/src/androidMain/kotlin/com/codandotv/streamplayerapp/core_networking/handleError/HttpClientConfigExtensions.kt diff --git a/core-networking/src/main/java/com/codandotv/streamplayerapp/core_networking/handleError/NetworkResponse.kt b/core-networking/src/androidMain/kotlin/com/codandotv/streamplayerapp/core_networking/handleError/NetworkResponse.kt similarity index 100% rename from core-networking/src/main/java/com/codandotv/streamplayerapp/core_networking/handleError/NetworkResponse.kt rename to core-networking/src/androidMain/kotlin/com/codandotv/streamplayerapp/core_networking/handleError/NetworkResponse.kt diff --git a/core-networking/src/main/java/com/codandotv/streamplayerapp/core_networking/handleError/ResultExtensions.kt b/core-networking/src/androidMain/kotlin/com/codandotv/streamplayerapp/core_networking/handleError/ResultExtensions.kt similarity index 100% rename from core-networking/src/main/java/com/codandotv/streamplayerapp/core_networking/handleError/ResultExtensions.kt rename to core-networking/src/androidMain/kotlin/com/codandotv/streamplayerapp/core_networking/handleError/ResultExtensions.kt diff --git a/core-networking/src/main/res/values/strings.xml b/core-networking/src/androidMain/res/values/strings.xml similarity index 100% rename from core-networking/src/main/res/values/strings.xml rename to core-networking/src/androidMain/res/values/strings.xml diff --git a/core-networking/src/main/AndroidManifest.xml b/core-networking/src/main/AndroidManifest.xml deleted file mode 100644 index a8800291..00000000 --- a/core-networking/src/main/AndroidManifest.xml +++ /dev/null @@ -1,4 +0,0 @@ - - - - \ No newline at end of file From a9563f83b20935bff0f2a3cfc2cf55aae4ae7a2b Mon Sep 17 00:00:00 2001 From: Rods Date: Wed, 15 Jan 2025 21:02:55 -0300 Subject: [PATCH 27/96] [ISSUE-3] - core-navigation to androidMain --- core-navigation/build.gradle.kts | 14 +++++++++----- .../bottomnavigation/BottomNavItem.kt | 0 .../StreamPlayerBottomNavigation.kt | 0 .../extensions/NavControllerExtension.kt | 0 .../core_navigation/helper/NavigationHelper.kt | 0 .../core_navigation/routes/BottomNavRoutes.kt | 0 .../core_navigation/routes/Routes.kt | 2 +- .../res/drawable/ic_downloads_selected.xml | 0 .../res/drawable/ic_downloads_unselected.xml | 0 .../res/drawable/ic_games_selected.xml | 0 .../res/drawable/ic_games_unselected.xml | 0 .../res/drawable/ic_home_selected.xml | 0 .../res/drawable/ic_home_unselected.xml | 0 .../res/drawable/ic_news_selected.xml | 0 .../res/drawable/ic_news_unselected.xml | 0 .../{main => androidMain}/res/values/strings.xml | 0 core-networking/build.gradle.kts | 16 ++++++++++------ 17 files changed, 20 insertions(+), 12 deletions(-) rename core-navigation/src/{main/java => androidMain/kotlin}/com/codandotv/streamplayerapp/core_navigation/bottomnavigation/BottomNavItem.kt (100%) rename core-navigation/src/{main/java => androidMain/kotlin}/com/codandotv/streamplayerapp/core_navigation/bottomnavigation/StreamPlayerBottomNavigation.kt (100%) rename core-navigation/src/{main/java => androidMain/kotlin}/com/codandotv/streamplayerapp/core_navigation/extensions/NavControllerExtension.kt (100%) rename core-navigation/src/{main/java => androidMain/kotlin}/com/codandotv/streamplayerapp/core_navigation/helper/NavigationHelper.kt (100%) rename core-navigation/src/{main/java => androidMain/kotlin}/com/codandotv/streamplayerapp/core_navigation/routes/BottomNavRoutes.kt (100%) rename core-navigation/src/{main/java => androidMain/kotlin}/com/codandotv/streamplayerapp/core_navigation/routes/Routes.kt (87%) rename core-navigation/src/{main => androidMain}/res/drawable/ic_downloads_selected.xml (100%) rename core-navigation/src/{main => androidMain}/res/drawable/ic_downloads_unselected.xml (100%) rename core-navigation/src/{main => androidMain}/res/drawable/ic_games_selected.xml (100%) rename core-navigation/src/{main => androidMain}/res/drawable/ic_games_unselected.xml (100%) rename core-navigation/src/{main => androidMain}/res/drawable/ic_home_selected.xml (100%) rename core-navigation/src/{main => androidMain}/res/drawable/ic_home_unselected.xml (100%) rename core-navigation/src/{main => androidMain}/res/drawable/ic_news_selected.xml (100%) rename core-navigation/src/{main => androidMain}/res/drawable/ic_news_unselected.xml (100%) rename core-navigation/src/{main => androidMain}/res/values/strings.xml (100%) diff --git a/core-navigation/build.gradle.kts b/core-navigation/build.gradle.kts index 93248caf..482d37d5 100644 --- a/core-navigation/build.gradle.kts +++ b/core-navigation/build.gradle.kts @@ -1,12 +1,16 @@ @file:Suppress("UnstableApiUsage") plugins { - id("com.streamplayer.android-library") + id("com.streamplayer.kmp-library") alias(libs.plugins.jetbrains.compose) alias(libs.plugins.compose.compiler) } -dependencies { - implementation(libs.bundles.kotlin) - implementation(libs.navigation.compose) - implementation(compose.material3) +kotlin { + sourceSets { + androidMain.dependencies { + implementation(libs.bundles.kotlin) + implementation(libs.navigation.compose) + implementation(compose.material3) + } + } } \ No newline at end of file diff --git a/core-navigation/src/main/java/com/codandotv/streamplayerapp/core_navigation/bottomnavigation/BottomNavItem.kt b/core-navigation/src/androidMain/kotlin/com/codandotv/streamplayerapp/core_navigation/bottomnavigation/BottomNavItem.kt similarity index 100% rename from core-navigation/src/main/java/com/codandotv/streamplayerapp/core_navigation/bottomnavigation/BottomNavItem.kt rename to core-navigation/src/androidMain/kotlin/com/codandotv/streamplayerapp/core_navigation/bottomnavigation/BottomNavItem.kt diff --git a/core-navigation/src/main/java/com/codandotv/streamplayerapp/core_navigation/bottomnavigation/StreamPlayerBottomNavigation.kt b/core-navigation/src/androidMain/kotlin/com/codandotv/streamplayerapp/core_navigation/bottomnavigation/StreamPlayerBottomNavigation.kt similarity index 100% rename from core-navigation/src/main/java/com/codandotv/streamplayerapp/core_navigation/bottomnavigation/StreamPlayerBottomNavigation.kt rename to core-navigation/src/androidMain/kotlin/com/codandotv/streamplayerapp/core_navigation/bottomnavigation/StreamPlayerBottomNavigation.kt diff --git a/core-navigation/src/main/java/com/codandotv/streamplayerapp/core_navigation/extensions/NavControllerExtension.kt b/core-navigation/src/androidMain/kotlin/com/codandotv/streamplayerapp/core_navigation/extensions/NavControllerExtension.kt similarity index 100% rename from core-navigation/src/main/java/com/codandotv/streamplayerapp/core_navigation/extensions/NavControllerExtension.kt rename to core-navigation/src/androidMain/kotlin/com/codandotv/streamplayerapp/core_navigation/extensions/NavControllerExtension.kt diff --git a/core-navigation/src/main/java/com/codandotv/streamplayerapp/core_navigation/helper/NavigationHelper.kt b/core-navigation/src/androidMain/kotlin/com/codandotv/streamplayerapp/core_navigation/helper/NavigationHelper.kt similarity index 100% rename from core-navigation/src/main/java/com/codandotv/streamplayerapp/core_navigation/helper/NavigationHelper.kt rename to core-navigation/src/androidMain/kotlin/com/codandotv/streamplayerapp/core_navigation/helper/NavigationHelper.kt diff --git a/core-navigation/src/main/java/com/codandotv/streamplayerapp/core_navigation/routes/BottomNavRoutes.kt b/core-navigation/src/androidMain/kotlin/com/codandotv/streamplayerapp/core_navigation/routes/BottomNavRoutes.kt similarity index 100% rename from core-navigation/src/main/java/com/codandotv/streamplayerapp/core_navigation/routes/BottomNavRoutes.kt rename to core-navigation/src/androidMain/kotlin/com/codandotv/streamplayerapp/core_navigation/routes/BottomNavRoutes.kt diff --git a/core-navigation/src/main/java/com/codandotv/streamplayerapp/core_navigation/routes/Routes.kt b/core-navigation/src/androidMain/kotlin/com/codandotv/streamplayerapp/core_navigation/routes/Routes.kt similarity index 87% rename from core-navigation/src/main/java/com/codandotv/streamplayerapp/core_navigation/routes/Routes.kt rename to core-navigation/src/androidMain/kotlin/com/codandotv/streamplayerapp/core_navigation/routes/Routes.kt index 572636d9..552cfc46 100644 --- a/core-navigation/src/main/java/com/codandotv/streamplayerapp/core_navigation/routes/Routes.kt +++ b/core-navigation/src/androidMain/kotlin/com/codandotv/streamplayerapp/core_navigation/routes/Routes.kt @@ -4,7 +4,7 @@ import com.codandotv.streamplayerapp.core_navigation.routes.Routes.PARAM.ID object Routes { const val DETAIL = "DetailList/" - const val DETAIL_COMPLETE = "${DETAIL}{${ID}}" + const val DETAIL_COMPLETE = "$DETAIL{${ID}}" const val Splash = "splash" const val SEARCH = "Search" const val PROFILE_PICKER = "profilePicker" diff --git a/core-navigation/src/main/res/drawable/ic_downloads_selected.xml b/core-navigation/src/androidMain/res/drawable/ic_downloads_selected.xml similarity index 100% rename from core-navigation/src/main/res/drawable/ic_downloads_selected.xml rename to core-navigation/src/androidMain/res/drawable/ic_downloads_selected.xml diff --git a/core-navigation/src/main/res/drawable/ic_downloads_unselected.xml b/core-navigation/src/androidMain/res/drawable/ic_downloads_unselected.xml similarity index 100% rename from core-navigation/src/main/res/drawable/ic_downloads_unselected.xml rename to core-navigation/src/androidMain/res/drawable/ic_downloads_unselected.xml diff --git a/core-navigation/src/main/res/drawable/ic_games_selected.xml b/core-navigation/src/androidMain/res/drawable/ic_games_selected.xml similarity index 100% rename from core-navigation/src/main/res/drawable/ic_games_selected.xml rename to core-navigation/src/androidMain/res/drawable/ic_games_selected.xml diff --git a/core-navigation/src/main/res/drawable/ic_games_unselected.xml b/core-navigation/src/androidMain/res/drawable/ic_games_unselected.xml similarity index 100% rename from core-navigation/src/main/res/drawable/ic_games_unselected.xml rename to core-navigation/src/androidMain/res/drawable/ic_games_unselected.xml diff --git a/core-navigation/src/main/res/drawable/ic_home_selected.xml b/core-navigation/src/androidMain/res/drawable/ic_home_selected.xml similarity index 100% rename from core-navigation/src/main/res/drawable/ic_home_selected.xml rename to core-navigation/src/androidMain/res/drawable/ic_home_selected.xml diff --git a/core-navigation/src/main/res/drawable/ic_home_unselected.xml b/core-navigation/src/androidMain/res/drawable/ic_home_unselected.xml similarity index 100% rename from core-navigation/src/main/res/drawable/ic_home_unselected.xml rename to core-navigation/src/androidMain/res/drawable/ic_home_unselected.xml diff --git a/core-navigation/src/main/res/drawable/ic_news_selected.xml b/core-navigation/src/androidMain/res/drawable/ic_news_selected.xml similarity index 100% rename from core-navigation/src/main/res/drawable/ic_news_selected.xml rename to core-navigation/src/androidMain/res/drawable/ic_news_selected.xml diff --git a/core-navigation/src/main/res/drawable/ic_news_unselected.xml b/core-navigation/src/androidMain/res/drawable/ic_news_unselected.xml similarity index 100% rename from core-navigation/src/main/res/drawable/ic_news_unselected.xml rename to core-navigation/src/androidMain/res/drawable/ic_news_unselected.xml diff --git a/core-navigation/src/main/res/values/strings.xml b/core-navigation/src/androidMain/res/values/strings.xml similarity index 100% rename from core-navigation/src/main/res/values/strings.xml rename to core-navigation/src/androidMain/res/values/strings.xml diff --git a/core-networking/build.gradle.kts b/core-networking/build.gradle.kts index 2ba275b6..c190c720 100644 --- a/core-networking/build.gradle.kts +++ b/core-networking/build.gradle.kts @@ -20,9 +20,13 @@ android { } } } -dependencies { - implementation(libs.bundles.kotlin) - implementation(libs.bundles.networking) - implementation(libs.bundles.koin) - testImplementation(libs.bundles.test) -} + +kotlin { + sourceSets { + androidMain.dependencies { + implementation(libs.bundles.kotlin) + implementation(libs.bundles.networking) + implementation(libs.bundles.koin) + } + } +} \ No newline at end of file From 29822f7c22a8fe714413aa7f1a4864721c6da0ec Mon Sep 17 00:00:00 2001 From: Rods Date: Sat, 18 Jan 2025 10:33:14 -0300 Subject: [PATCH 28/96] [ISSUE-3] - core-localstorage to androidMain --- core-local-storage/build.gradle.kts | 24 +++++++++++++++---- .../data/dao/FavoriteDao.kt | 0 .../data/database/StreamPlayerAppDatabase.kt | 0 .../di/LocalStorageModule.kt | 0 .../domain/model/MovieEntity.kt | 0 5 files changed, 19 insertions(+), 5 deletions(-) rename core-local-storage/src/{main/java => androidMain/kotlin}/com/codandotv/streamplayerapp/core_local_storage/data/dao/FavoriteDao.kt (100%) rename core-local-storage/src/{main/java => androidMain/kotlin}/com/codandotv/streamplayerapp/core_local_storage/data/database/StreamPlayerAppDatabase.kt (100%) rename core-local-storage/src/{main/java => androidMain/kotlin}/com/codandotv/streamplayerapp/core_local_storage/di/LocalStorageModule.kt (100%) rename core-local-storage/src/{main/java => androidMain/kotlin}/com/codandotv/streamplayerapp/core_local_storage/domain/model/MovieEntity.kt (100%) diff --git a/core-local-storage/build.gradle.kts b/core-local-storage/build.gradle.kts index c8e2ff05..4437ccef 100644 --- a/core-local-storage/build.gradle.kts +++ b/core-local-storage/build.gradle.kts @@ -1,13 +1,27 @@ plugins { - id("com.streamplayer.android-library") + id("com.streamplayer.kmp-library") id("com.google.devtools.ksp") } +kotlin { + sourceSets { + androidMain.dependencies { + implementation(libs.bundles.room) + implementation(libs.bundles.kotlin) + implementation(libs.bundles.koin) + } + } +} + dependencies { + add("kspCommonMainMetadata",libs.koin.compiler) + add("kspAndroid", libs.koin.compiler) +} +ksp { + arg("KOIN_CONFIG_CHECK","true") +} + +dependencies { ksp(libs.roomCompiler) - implementation(libs.bundles.room) - implementation(libs.bundles.kotlin) - implementation(libs.bundles.koin) - testImplementation(libs.bundles.test) } \ No newline at end of file diff --git a/core-local-storage/src/main/java/com/codandotv/streamplayerapp/core_local_storage/data/dao/FavoriteDao.kt b/core-local-storage/src/androidMain/kotlin/com/codandotv/streamplayerapp/core_local_storage/data/dao/FavoriteDao.kt similarity index 100% rename from core-local-storage/src/main/java/com/codandotv/streamplayerapp/core_local_storage/data/dao/FavoriteDao.kt rename to core-local-storage/src/androidMain/kotlin/com/codandotv/streamplayerapp/core_local_storage/data/dao/FavoriteDao.kt diff --git a/core-local-storage/src/main/java/com/codandotv/streamplayerapp/core_local_storage/data/database/StreamPlayerAppDatabase.kt b/core-local-storage/src/androidMain/kotlin/com/codandotv/streamplayerapp/core_local_storage/data/database/StreamPlayerAppDatabase.kt similarity index 100% rename from core-local-storage/src/main/java/com/codandotv/streamplayerapp/core_local_storage/data/database/StreamPlayerAppDatabase.kt rename to core-local-storage/src/androidMain/kotlin/com/codandotv/streamplayerapp/core_local_storage/data/database/StreamPlayerAppDatabase.kt diff --git a/core-local-storage/src/main/java/com/codandotv/streamplayerapp/core_local_storage/di/LocalStorageModule.kt b/core-local-storage/src/androidMain/kotlin/com/codandotv/streamplayerapp/core_local_storage/di/LocalStorageModule.kt similarity index 100% rename from core-local-storage/src/main/java/com/codandotv/streamplayerapp/core_local_storage/di/LocalStorageModule.kt rename to core-local-storage/src/androidMain/kotlin/com/codandotv/streamplayerapp/core_local_storage/di/LocalStorageModule.kt diff --git a/core-local-storage/src/main/java/com/codandotv/streamplayerapp/core_local_storage/domain/model/MovieEntity.kt b/core-local-storage/src/androidMain/kotlin/com/codandotv/streamplayerapp/core_local_storage/domain/model/MovieEntity.kt similarity index 100% rename from core-local-storage/src/main/java/com/codandotv/streamplayerapp/core_local_storage/domain/model/MovieEntity.kt rename to core-local-storage/src/androidMain/kotlin/com/codandotv/streamplayerapp/core_local_storage/domain/model/MovieEntity.kt From 0d26e1a21a25e307f7c9f4b7addac145b470fe4b Mon Sep 17 00:00:00 2001 From: Rods Date: Sat, 18 Jan 2025 19:46:24 -0300 Subject: [PATCH 29/96] [ISSUE-3] - feature-profile to androidMain --- .../navigation/NavigationGraph.kt | 2 +- feature-profile/build.gradle.kts | 38 +++++++++++-------- .../data/ProfilePickerStreamRepository.kt | 6 +-- .../data/ProfilePickerStreamService.kt | 4 +- .../data/model/ProfileStreamResponse.kt | 2 +- .../profile/di/ProfilePickerStreamModule.kt | 6 +-- .../domain/ProfilePickerStreamMapper.kt | 4 +- .../domain/ProfilePickerStreamUseCase.kt | 4 +- .../profile/domain/ProfileStream.kt | 2 +- .../ProfilePickerStreamNavigation.kt | 6 +-- .../screens/ProfilePickerStreamScreen.kt | 16 ++++---- .../screens/ProfilePickerStreamViewModel.kt | 6 +-- .../screens/ProfilePickerStreamsUIState.kt | 4 +- .../presentation/widget/ComposeExtensions.kt | 2 +- .../widget/ProfilePickerOpacityLayer.kt | 2 +- .../widget/ProfilePickerProfilesGrid.kt | 6 +-- .../ProfilePickerSelectedProfileContainer.kt | 4 +- .../widget/ProfilePickerStreamLoad.kt | 2 +- .../widget/ProfilePickerStreamToolbar.kt | 2 +- .../res/drawable/image_placeholder.xml | 0 .../res/drawable/netflix_horizontal_logo.xml | 0 .../res/values/strings.xml | 0 22 files changed, 62 insertions(+), 56 deletions(-) rename feature-profile/src/{main/java/com/codandotv/streamplayerapp/feature_profile => androidMain/kotlin/com/codandotv/streamplayerapp}/profile/data/ProfilePickerStreamRepository.kt (90%) rename feature-profile/src/{main/java/com/codandotv/streamplayerapp/feature_profile => androidMain/kotlin/com/codandotv/streamplayerapp}/profile/data/ProfilePickerStreamService.kt (78%) rename feature-profile/src/{main/java/com/codandotv/streamplayerapp/feature_profile => androidMain/kotlin/com/codandotv/streamplayerapp}/profile/data/model/ProfileStreamResponse.kt (80%) rename feature-profile/src/{main/java/com/codandotv/streamplayerapp/feature_profile => androidMain/kotlin/com/codandotv/streamplayerapp}/profile/di/ProfilePickerStreamModule.kt (66%) rename feature-profile/src/{main/java/com/codandotv/streamplayerapp/feature_profile => androidMain/kotlin/com/codandotv/streamplayerapp}/profile/domain/ProfilePickerStreamMapper.kt (60%) rename feature-profile/src/{main/java/com/codandotv/streamplayerapp/feature_profile => androidMain/kotlin/com/codandotv/streamplayerapp}/profile/domain/ProfilePickerStreamUseCase.kt (93%) rename feature-profile/src/{main/java/com/codandotv/streamplayerapp/feature_profile => androidMain/kotlin/com/codandotv/streamplayerapp}/profile/domain/ProfileStream.kt (57%) rename feature-profile/src/{main/java/com/codandotv/streamplayerapp/feature_profile => androidMain/kotlin/com/codandotv/streamplayerapp}/profile/presentation/navigation/ProfilePickerStreamNavigation.kt (79%) rename feature-profile/src/{main/java/com/codandotv/streamplayerapp/feature_profile => androidMain/kotlin/com/codandotv/streamplayerapp}/profile/presentation/screens/ProfilePickerStreamScreen.kt (90%) rename feature-profile/src/{main/java/com/codandotv/streamplayerapp/feature_profile => androidMain/kotlin/com/codandotv/streamplayerapp}/profile/presentation/screens/ProfilePickerStreamViewModel.kt (94%) rename feature-profile/src/{main/java/com/codandotv/streamplayerapp/feature_profile => androidMain/kotlin/com/codandotv/streamplayerapp}/profile/presentation/screens/ProfilePickerStreamsUIState.kt (83%) rename feature-profile/src/{main/java/com/codandotv/streamplayerapp/feature_profile => androidMain/kotlin/com/codandotv/streamplayerapp}/profile/presentation/widget/ComposeExtensions.kt (72%) rename feature-profile/src/{main/java/com/codandotv/streamplayerapp/feature_profile => androidMain/kotlin/com/codandotv/streamplayerapp}/profile/presentation/widget/ProfilePickerOpacityLayer.kt (84%) rename feature-profile/src/{main/java/com/codandotv/streamplayerapp/feature_profile => androidMain/kotlin/com/codandotv/streamplayerapp}/profile/presentation/widget/ProfilePickerProfilesGrid.kt (93%) rename feature-profile/src/{main/java/com/codandotv/streamplayerapp/feature_profile => androidMain/kotlin/com/codandotv/streamplayerapp}/profile/presentation/widget/ProfilePickerSelectedProfileContainer.kt (92%) rename feature-profile/src/{main/java/com/codandotv/streamplayerapp/feature_profile => androidMain/kotlin/com/codandotv/streamplayerapp}/profile/presentation/widget/ProfilePickerStreamLoad.kt (85%) rename feature-profile/src/{main/java/com/codandotv/streamplayerapp/feature_profile => androidMain/kotlin/com/codandotv/streamplayerapp}/profile/presentation/widget/ProfilePickerStreamToolbar.kt (95%) rename feature-profile/src/{main => androidMain}/res/drawable/image_placeholder.xml (100%) rename feature-profile/src/{main => androidMain}/res/drawable/netflix_horizontal_logo.xml (100%) rename feature-profile/src/{main => androidMain}/res/values/strings.xml (100%) diff --git a/app/src/main/java/com/codandotv/streamplayerapp/navigation/NavigationGraph.kt b/app/src/main/java/com/codandotv/streamplayerapp/navigation/NavigationGraph.kt index cbc81815..192ab17b 100644 --- a/app/src/main/java/com/codandotv/streamplayerapp/navigation/NavigationGraph.kt +++ b/app/src/main/java/com/codandotv/streamplayerapp/navigation/NavigationGraph.kt @@ -15,7 +15,7 @@ import com.codandotv.streamplayerapp.core_navigation.bottomnavigation.StreamPlay import com.codandotv.streamplayerapp.core_navigation.routes.BottomNavRoutes import com.codandotv.streamplayerapp.core_navigation.routes.Routes import com.codandotv.streamplayerapp.feature_list_streams.list.presentation.navigation.listStreamsNavGraph -import com.codandotv.streamplayerapp.feature_profile.profile.presentation.navigation.profilePickerStreamNavGraph +import com.codandotv.streamplayerapp.profile.presentation.navigation.profilePickerStreamNavGraph import com.codandotv.streamplayerapp.splah.presentation.navigation.splashNavGraph import com.codandotv.streamplayerapp.feature_detail.presentation.navigation.detailStreamNavGraph diff --git a/feature-profile/build.gradle.kts b/feature-profile/build.gradle.kts index 7f44787a..fe8df508 100644 --- a/feature-profile/build.gradle.kts +++ b/feature-profile/build.gradle.kts @@ -1,30 +1,36 @@ @file:Suppress("UnstableApiUsage") plugins { - id("com.streamplayer.android-library") + id("com.streamplayer.kmp-library") alias(libs.plugins.jetbrains.compose) alias(libs.plugins.compose.compiler) alias(libs.plugins.ksp) } -dependencies { - implementation(projects.coreNetworking) - implementation(projects.coreNavigation) - implementation(projects.coreShared) - implementation(projects.coreSharedUi) - implementation(libs.navigation.compose) - implementation(compose.material3) - implementation(compose.ui) +kotlin { + sourceSets { + androidMain.dependencies { + implementation(projects.coreNetworking) + implementation(projects.coreNavigation) + implementation(projects.coreShared) + implementation(projects.coreSharedUi) + implementation(libs.navigation.compose) + implementation(compose.material3) + implementation(compose.ui) - implementation(libs.bundles.koin) - implementation(libs.koin.annotations) - ksp(libs.koin.compiler) + implementation(libs.bundles.koin) + implementation(libs.koin.annotations) + implementation(libs.bundles.networking) + implementation(libs.bundles.androidSupport) + implementation(libs.coil) - implementation(libs.bundles.networking) - implementation(libs.bundles.androidSupport) - implementation(libs.coil) + } + } +} - testImplementation(libs.bundles.test) +dependencies { + add("kspCommonMainMetadata",libs.koin.compiler) + add("kspAndroid", libs.koin.compiler) } ksp { diff --git a/feature-profile/src/main/java/com/codandotv/streamplayerapp/feature_profile/profile/data/ProfilePickerStreamRepository.kt b/feature-profile/src/androidMain/kotlin/com/codandotv/streamplayerapp/profile/data/ProfilePickerStreamRepository.kt similarity index 90% rename from feature-profile/src/main/java/com/codandotv/streamplayerapp/feature_profile/profile/data/ProfilePickerStreamRepository.kt rename to feature-profile/src/androidMain/kotlin/com/codandotv/streamplayerapp/profile/data/ProfilePickerStreamRepository.kt index 7fdba887..6474a36c 100644 --- a/feature-profile/src/main/java/com/codandotv/streamplayerapp/feature_profile/profile/data/ProfilePickerStreamRepository.kt +++ b/feature-profile/src/androidMain/kotlin/com/codandotv/streamplayerapp/profile/data/ProfilePickerStreamRepository.kt @@ -1,10 +1,10 @@ -package com.codandotv.streamplayerapp.feature_profile.profile.data +package com.codandotv.streamplayerapp.profile.data import android.util.Log import com.codandotv.streamplayerapp.core_networking.handleError.toFlow import com.codandotv.streamplayerapp.core_networking.handleError.toResult -import com.codandotv.streamplayerapp.feature_profile.profile.domain.ProfileStream -import com.codandotv.streamplayerapp.feature_profile.profile.domain.toProfiles +import com.codandotv.streamplayerapp.profile.domain.ProfileStream +import com.codandotv.streamplayerapp.profile.domain.toProfiles import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.flow.map diff --git a/feature-profile/src/main/java/com/codandotv/streamplayerapp/feature_profile/profile/data/ProfilePickerStreamService.kt b/feature-profile/src/androidMain/kotlin/com/codandotv/streamplayerapp/profile/data/ProfilePickerStreamService.kt similarity index 78% rename from feature-profile/src/main/java/com/codandotv/streamplayerapp/feature_profile/profile/data/ProfilePickerStreamService.kt rename to feature-profile/src/androidMain/kotlin/com/codandotv/streamplayerapp/profile/data/ProfilePickerStreamService.kt index 8f900f79..e071ddf2 100644 --- a/feature-profile/src/main/java/com/codandotv/streamplayerapp/feature_profile/profile/data/ProfilePickerStreamService.kt +++ b/feature-profile/src/androidMain/kotlin/com/codandotv/streamplayerapp/profile/data/ProfilePickerStreamService.kt @@ -1,8 +1,8 @@ -package com.codandotv.streamplayerapp.feature_profile.profile.data +package com.codandotv.streamplayerapp.profile.data import com.codandotv.streamplayerapp.core_networking.handleError.NetworkResponse import com.codandotv.streamplayerapp.core_networking.handleError.safeRequest -import com.codandotv.streamplayerapp.feature_profile.profile.data.model.ProfilesResponse +import com.codandotv.streamplayerapp.profile.data.model.ProfilesResponse import io.ktor.client.HttpClient import io.ktor.client.request.url diff --git a/feature-profile/src/main/java/com/codandotv/streamplayerapp/feature_profile/profile/data/model/ProfileStreamResponse.kt b/feature-profile/src/androidMain/kotlin/com/codandotv/streamplayerapp/profile/data/model/ProfileStreamResponse.kt similarity index 80% rename from feature-profile/src/main/java/com/codandotv/streamplayerapp/feature_profile/profile/data/model/ProfileStreamResponse.kt rename to feature-profile/src/androidMain/kotlin/com/codandotv/streamplayerapp/profile/data/model/ProfileStreamResponse.kt index 2abc127a..afac8e93 100644 --- a/feature-profile/src/main/java/com/codandotv/streamplayerapp/feature_profile/profile/data/model/ProfileStreamResponse.kt +++ b/feature-profile/src/androidMain/kotlin/com/codandotv/streamplayerapp/profile/data/model/ProfileStreamResponse.kt @@ -1,4 +1,4 @@ -package com.codandotv.streamplayerapp.feature_profile.profile.data.model +package com.codandotv.streamplayerapp.profile.data.model import kotlinx.serialization.Serializable diff --git a/feature-profile/src/main/java/com/codandotv/streamplayerapp/feature_profile/profile/di/ProfilePickerStreamModule.kt b/feature-profile/src/androidMain/kotlin/com/codandotv/streamplayerapp/profile/di/ProfilePickerStreamModule.kt similarity index 66% rename from feature-profile/src/main/java/com/codandotv/streamplayerapp/feature_profile/profile/di/ProfilePickerStreamModule.kt rename to feature-profile/src/androidMain/kotlin/com/codandotv/streamplayerapp/profile/di/ProfilePickerStreamModule.kt index 991e2c03..3a1f4719 100644 --- a/feature-profile/src/main/java/com/codandotv/streamplayerapp/feature_profile/profile/di/ProfilePickerStreamModule.kt +++ b/feature-profile/src/androidMain/kotlin/com/codandotv/streamplayerapp/profile/di/ProfilePickerStreamModule.kt @@ -1,7 +1,7 @@ -package com.codandotv.streamplayerapp.feature_profile.profile.di +package com.codandotv.streamplayerapp.profile.di -import com.codandotv.streamplayerapp.feature_profile.profile.data.ProfilePickerStreamService -import com.codandotv.streamplayerapp.feature_profile.profile.data.ProfilePickerStreamServiceImpl +import com.codandotv.streamplayerapp.profile.data.ProfilePickerStreamService +import com.codandotv.streamplayerapp.profile.data.ProfilePickerStreamServiceImpl import io.ktor.client.HttpClient import org.koin.core.annotation.ComponentScan import org.koin.core.annotation.Factory diff --git a/feature-profile/src/main/java/com/codandotv/streamplayerapp/feature_profile/profile/domain/ProfilePickerStreamMapper.kt b/feature-profile/src/androidMain/kotlin/com/codandotv/streamplayerapp/profile/domain/ProfilePickerStreamMapper.kt similarity index 60% rename from feature-profile/src/main/java/com/codandotv/streamplayerapp/feature_profile/profile/domain/ProfilePickerStreamMapper.kt rename to feature-profile/src/androidMain/kotlin/com/codandotv/streamplayerapp/profile/domain/ProfilePickerStreamMapper.kt index 6d5ce0ac..ff732549 100644 --- a/feature-profile/src/main/java/com/codandotv/streamplayerapp/feature_profile/profile/domain/ProfilePickerStreamMapper.kt +++ b/feature-profile/src/androidMain/kotlin/com/codandotv/streamplayerapp/profile/domain/ProfilePickerStreamMapper.kt @@ -1,6 +1,6 @@ -package com.codandotv.streamplayerapp.feature_profile.profile.domain +package com.codandotv.streamplayerapp.profile.domain -import com.codandotv.streamplayerapp.feature_profile.profile.data.model.ProfilesResponse +import com.codandotv.streamplayerapp.profile.data.model.ProfilesResponse fun ProfilesResponse.toProfiles(): List = this.profiles.map { profileResponse -> ProfileStream( diff --git a/feature-profile/src/main/java/com/codandotv/streamplayerapp/feature_profile/profile/domain/ProfilePickerStreamUseCase.kt b/feature-profile/src/androidMain/kotlin/com/codandotv/streamplayerapp/profile/domain/ProfilePickerStreamUseCase.kt similarity index 93% rename from feature-profile/src/main/java/com/codandotv/streamplayerapp/feature_profile/profile/domain/ProfilePickerStreamUseCase.kt rename to feature-profile/src/androidMain/kotlin/com/codandotv/streamplayerapp/profile/domain/ProfilePickerStreamUseCase.kt index 225a15be..490d855b 100644 --- a/feature-profile/src/main/java/com/codandotv/streamplayerapp/feature_profile/profile/domain/ProfilePickerStreamUseCase.kt +++ b/feature-profile/src/androidMain/kotlin/com/codandotv/streamplayerapp/profile/domain/ProfilePickerStreamUseCase.kt @@ -1,7 +1,7 @@ @file:Suppress("MagicNumber") -package com.codandotv.streamplayerapp.feature_profile.profile.domain +package com.codandotv.streamplayerapp.profile.domain -import com.codandotv.streamplayerapp.feature_profile.profile.data.ProfilePickerStreamRepository +import com.codandotv.streamplayerapp.profile.data.ProfilePickerStreamRepository import kotlinx.coroutines.flow.Flow import org.koin.core.annotation.Factory diff --git a/feature-profile/src/main/java/com/codandotv/streamplayerapp/feature_profile/profile/domain/ProfileStream.kt b/feature-profile/src/androidMain/kotlin/com/codandotv/streamplayerapp/profile/domain/ProfileStream.kt similarity index 57% rename from feature-profile/src/main/java/com/codandotv/streamplayerapp/feature_profile/profile/domain/ProfileStream.kt rename to feature-profile/src/androidMain/kotlin/com/codandotv/streamplayerapp/profile/domain/ProfileStream.kt index 7c365999..5440084a 100644 --- a/feature-profile/src/main/java/com/codandotv/streamplayerapp/feature_profile/profile/domain/ProfileStream.kt +++ b/feature-profile/src/androidMain/kotlin/com/codandotv/streamplayerapp/profile/domain/ProfileStream.kt @@ -1,4 +1,4 @@ -package com.codandotv.streamplayerapp.feature_profile.profile.domain +package com.codandotv.streamplayerapp.profile.domain data class ProfileStream( val id: String, diff --git a/feature-profile/src/main/java/com/codandotv/streamplayerapp/feature_profile/profile/presentation/navigation/ProfilePickerStreamNavigation.kt b/feature-profile/src/androidMain/kotlin/com/codandotv/streamplayerapp/profile/presentation/navigation/ProfilePickerStreamNavigation.kt similarity index 79% rename from feature-profile/src/main/java/com/codandotv/streamplayerapp/feature_profile/profile/presentation/navigation/ProfilePickerStreamNavigation.kt rename to feature-profile/src/androidMain/kotlin/com/codandotv/streamplayerapp/profile/presentation/navigation/ProfilePickerStreamNavigation.kt index 3624f939..61c12602 100644 --- a/feature-profile/src/main/java/com/codandotv/streamplayerapp/feature_profile/profile/presentation/navigation/ProfilePickerStreamNavigation.kt +++ b/feature-profile/src/androidMain/kotlin/com/codandotv/streamplayerapp/profile/presentation/navigation/ProfilePickerStreamNavigation.kt @@ -1,4 +1,4 @@ -package com.codandotv.streamplayerapp.feature_profile.profile.presentation.navigation +package com.codandotv.streamplayerapp.profile.presentation.navigation import androidx.lifecycle.Lifecycle import androidx.navigation.NavGraphBuilder @@ -7,8 +7,8 @@ import androidx.navigation.compose.composable import com.codandotv.streamplayerapp.core_navigation.routes.BottomNavRoutes.HOME import com.codandotv.streamplayerapp.core_navigation.routes.BottomNavRoutes.PARAM.PROFILE_ID import com.codandotv.streamplayerapp.core_navigation.routes.Routes -import com.codandotv.streamplayerapp.feature_profile.profile.di.ProfilePickerStreamModule -import com.codandotv.streamplayerapp.feature_profile.profile.presentation.screens.ProfilePickerStreamScreen +import com.codandotv.streamplayerapp.profile.di.ProfilePickerStreamModule +import com.codandotv.streamplayerapp.profile.presentation.screens.ProfilePickerStreamScreen import org.koin.core.context.loadKoinModules import org.koin.core.context.unloadKoinModules import org.koin.ksp.generated.module diff --git a/feature-profile/src/main/java/com/codandotv/streamplayerapp/feature_profile/profile/presentation/screens/ProfilePickerStreamScreen.kt b/feature-profile/src/androidMain/kotlin/com/codandotv/streamplayerapp/profile/presentation/screens/ProfilePickerStreamScreen.kt similarity index 90% rename from feature-profile/src/main/java/com/codandotv/streamplayerapp/feature_profile/profile/presentation/screens/ProfilePickerStreamScreen.kt rename to feature-profile/src/androidMain/kotlin/com/codandotv/streamplayerapp/profile/presentation/screens/ProfilePickerStreamScreen.kt index e54b0442..1fa56e33 100644 --- a/feature-profile/src/main/java/com/codandotv/streamplayerapp/feature_profile/profile/presentation/screens/ProfilePickerStreamScreen.kt +++ b/feature-profile/src/androidMain/kotlin/com/codandotv/streamplayerapp/profile/presentation/screens/ProfilePickerStreamScreen.kt @@ -1,4 +1,4 @@ -package com.codandotv.streamplayerapp.feature_profile.profile.presentation.screens +package com.codandotv.streamplayerapp.profile.presentation.screens import android.annotation.SuppressLint import androidx.compose.animation.animateColorAsState @@ -24,13 +24,13 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.codandotv.streamplayerapp.core_shared_ui.theme.ThemePreview import com.codandotv.streamplayerapp.core_shared_ui.theme.ThemePreviews import com.codandotv.streamplayerapp.feature.profile.R -import com.codandotv.streamplayerapp.feature_profile.profile.domain.ProfileStream -import com.codandotv.streamplayerapp.feature_profile.profile.presentation.widget.LoadScreen -import com.codandotv.streamplayerapp.feature_profile.profile.presentation.widget.ProfilePickerOpacityLayer -import com.codandotv.streamplayerapp.feature_profile.profile.presentation.widget.ProfilePickerProfilesGrid -import com.codandotv.streamplayerapp.feature_profile.profile.presentation.widget.ProfilePickerSelectedProfileContainer -import com.codandotv.streamplayerapp.feature_profile.profile.presentation.widget.ProfilePickerStreamToolbar -import com.codandotv.streamplayerapp.feature_profile.profile.presentation.widget.dpToPx +import com.codandotv.streamplayerapp.profile.domain.ProfileStream +import com.codandotv.streamplayerapp.profile.presentation.widget.LoadScreen +import com.codandotv.streamplayerapp.profile.presentation.widget.ProfilePickerOpacityLayer +import com.codandotv.streamplayerapp.profile.presentation.widget.ProfilePickerProfilesGrid +import com.codandotv.streamplayerapp.profile.presentation.widget.ProfilePickerSelectedProfileContainer +import com.codandotv.streamplayerapp.profile.presentation.widget.ProfilePickerStreamToolbar +import com.codandotv.streamplayerapp.profile.presentation.widget.dpToPx import org.koin.androidx.compose.koinViewModel @Composable diff --git a/feature-profile/src/main/java/com/codandotv/streamplayerapp/feature_profile/profile/presentation/screens/ProfilePickerStreamViewModel.kt b/feature-profile/src/androidMain/kotlin/com/codandotv/streamplayerapp/profile/presentation/screens/ProfilePickerStreamViewModel.kt similarity index 94% rename from feature-profile/src/main/java/com/codandotv/streamplayerapp/feature_profile/profile/presentation/screens/ProfilePickerStreamViewModel.kt rename to feature-profile/src/androidMain/kotlin/com/codandotv/streamplayerapp/profile/presentation/screens/ProfilePickerStreamViewModel.kt index 36e3aa90..5e1c9b59 100644 --- a/feature-profile/src/main/java/com/codandotv/streamplayerapp/feature_profile/profile/presentation/screens/ProfilePickerStreamViewModel.kt +++ b/feature-profile/src/androidMain/kotlin/com/codandotv/streamplayerapp/profile/presentation/screens/ProfilePickerStreamViewModel.kt @@ -1,12 +1,12 @@ -package com.codandotv.streamplayerapp.feature_profile.profile.presentation.screens +package com.codandotv.streamplayerapp.profile.presentation.screens import androidx.lifecycle.DefaultLifecycleObserver import androidx.lifecycle.LifecycleOwner import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import com.codandotv.streamplayerapp.core_networking.handleError.catchFailure -import com.codandotv.streamplayerapp.feature_profile.profile.domain.ProfilePickerStreamUseCase -import com.codandotv.streamplayerapp.feature_profile.profile.domain.ProfileStream +import com.codandotv.streamplayerapp.profile.domain.ProfilePickerStreamUseCase +import com.codandotv.streamplayerapp.profile.domain.ProfileStream import kotlinx.coroutines.delay import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.SharingStarted diff --git a/feature-profile/src/main/java/com/codandotv/streamplayerapp/feature_profile/profile/presentation/screens/ProfilePickerStreamsUIState.kt b/feature-profile/src/androidMain/kotlin/com/codandotv/streamplayerapp/profile/presentation/screens/ProfilePickerStreamsUIState.kt similarity index 83% rename from feature-profile/src/main/java/com/codandotv/streamplayerapp/feature_profile/profile/presentation/screens/ProfilePickerStreamsUIState.kt rename to feature-profile/src/androidMain/kotlin/com/codandotv/streamplayerapp/profile/presentation/screens/ProfilePickerStreamsUIState.kt index 414b8d83..0ca2751b 100644 --- a/feature-profile/src/main/java/com/codandotv/streamplayerapp/feature_profile/profile/presentation/screens/ProfilePickerStreamsUIState.kt +++ b/feature-profile/src/androidMain/kotlin/com/codandotv/streamplayerapp/profile/presentation/screens/ProfilePickerStreamsUIState.kt @@ -1,6 +1,6 @@ -package com.codandotv.streamplayerapp.feature_profile.profile.presentation.screens +package com.codandotv.streamplayerapp.profile.presentation.screens -import com.codandotv.streamplayerapp.feature_profile.profile.domain.ProfileStream +import com.codandotv.streamplayerapp.profile.domain.ProfileStream data class ProfilePickerStreamsUIState( diff --git a/feature-profile/src/main/java/com/codandotv/streamplayerapp/feature_profile/profile/presentation/widget/ComposeExtensions.kt b/feature-profile/src/androidMain/kotlin/com/codandotv/streamplayerapp/profile/presentation/widget/ComposeExtensions.kt similarity index 72% rename from feature-profile/src/main/java/com/codandotv/streamplayerapp/feature_profile/profile/presentation/widget/ComposeExtensions.kt rename to feature-profile/src/androidMain/kotlin/com/codandotv/streamplayerapp/profile/presentation/widget/ComposeExtensions.kt index 2d30f8c5..109a6561 100644 --- a/feature-profile/src/main/java/com/codandotv/streamplayerapp/feature_profile/profile/presentation/widget/ComposeExtensions.kt +++ b/feature-profile/src/androidMain/kotlin/com/codandotv/streamplayerapp/profile/presentation/widget/ComposeExtensions.kt @@ -1,4 +1,4 @@ -package com.codandotv.streamplayerapp.feature_profile.profile.presentation.widget +package com.codandotv.streamplayerapp.profile.presentation.widget import androidx.compose.runtime.Composable import androidx.compose.ui.platform.LocalDensity diff --git a/feature-profile/src/main/java/com/codandotv/streamplayerapp/feature_profile/profile/presentation/widget/ProfilePickerOpacityLayer.kt b/feature-profile/src/androidMain/kotlin/com/codandotv/streamplayerapp/profile/presentation/widget/ProfilePickerOpacityLayer.kt similarity index 84% rename from feature-profile/src/main/java/com/codandotv/streamplayerapp/feature_profile/profile/presentation/widget/ProfilePickerOpacityLayer.kt rename to feature-profile/src/androidMain/kotlin/com/codandotv/streamplayerapp/profile/presentation/widget/ProfilePickerOpacityLayer.kt index f8fb3601..efbd403f 100644 --- a/feature-profile/src/main/java/com/codandotv/streamplayerapp/feature_profile/profile/presentation/widget/ProfilePickerOpacityLayer.kt +++ b/feature-profile/src/androidMain/kotlin/com/codandotv/streamplayerapp/profile/presentation/widget/ProfilePickerOpacityLayer.kt @@ -1,4 +1,4 @@ -package com.codandotv.streamplayerapp.feature_profile.profile.presentation.widget +package com.codandotv.streamplayerapp.profile.presentation.widget import androidx.compose.foundation.background import androidx.compose.foundation.layout.Box diff --git a/feature-profile/src/main/java/com/codandotv/streamplayerapp/feature_profile/profile/presentation/widget/ProfilePickerProfilesGrid.kt b/feature-profile/src/androidMain/kotlin/com/codandotv/streamplayerapp/profile/presentation/widget/ProfilePickerProfilesGrid.kt similarity index 93% rename from feature-profile/src/main/java/com/codandotv/streamplayerapp/feature_profile/profile/presentation/widget/ProfilePickerProfilesGrid.kt rename to feature-profile/src/androidMain/kotlin/com/codandotv/streamplayerapp/profile/presentation/widget/ProfilePickerProfilesGrid.kt index f24737a2..45ae69e2 100644 --- a/feature-profile/src/main/java/com/codandotv/streamplayerapp/feature_profile/profile/presentation/widget/ProfilePickerProfilesGrid.kt +++ b/feature-profile/src/androidMain/kotlin/com/codandotv/streamplayerapp/profile/presentation/widget/ProfilePickerProfilesGrid.kt @@ -1,4 +1,4 @@ -package com.codandotv.streamplayerapp.feature_profile.profile.presentation.widget +package com.codandotv.streamplayerapp.profile.presentation.widget import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.BoxWithConstraints @@ -24,8 +24,8 @@ import androidx.compose.ui.unit.IntOffset import androidx.compose.ui.unit.dp import coil.compose.AsyncImage import com.codandotv.streamplayerapp.feature.profile.R -import com.codandotv.streamplayerapp.feature_profile.profile.domain.ProfileStream -import com.codandotv.streamplayerapp.feature_profile.profile.presentation.screens.ProfilePickerStreamsUIState +import com.codandotv.streamplayerapp.profile.domain.ProfileStream +import com.codandotv.streamplayerapp.profile.presentation.screens.ProfilePickerStreamsUIState @Composable fun ProfilePickerProfilesGrid( diff --git a/feature-profile/src/main/java/com/codandotv/streamplayerapp/feature_profile/profile/presentation/widget/ProfilePickerSelectedProfileContainer.kt b/feature-profile/src/androidMain/kotlin/com/codandotv/streamplayerapp/profile/presentation/widget/ProfilePickerSelectedProfileContainer.kt similarity index 92% rename from feature-profile/src/main/java/com/codandotv/streamplayerapp/feature_profile/profile/presentation/widget/ProfilePickerSelectedProfileContainer.kt rename to feature-profile/src/androidMain/kotlin/com/codandotv/streamplayerapp/profile/presentation/widget/ProfilePickerSelectedProfileContainer.kt index beade0ac..be4e5e34 100644 --- a/feature-profile/src/main/java/com/codandotv/streamplayerapp/feature_profile/profile/presentation/widget/ProfilePickerSelectedProfileContainer.kt +++ b/feature-profile/src/androidMain/kotlin/com/codandotv/streamplayerapp/profile/presentation/widget/ProfilePickerSelectedProfileContainer.kt @@ -1,4 +1,4 @@ -package com.codandotv.streamplayerapp.feature_profile.profile.presentation.widget +package com.codandotv.streamplayerapp.profile.presentation.widget import androidx.compose.foundation.layout.BoxWithConstraints import androidx.compose.foundation.layout.Column @@ -21,7 +21,7 @@ import androidx.compose.ui.unit.dp import coil.compose.AsyncImage import coil.request.ImageRequest import com.codandotv.streamplayerapp.feature.profile.R -import com.codandotv.streamplayerapp.feature_profile.profile.presentation.screens.ProfilePickerStreamsUIState +import com.codandotv.streamplayerapp.profile.presentation.screens.ProfilePickerStreamsUIState @Suppress("MagicNumber") @Composable diff --git a/feature-profile/src/main/java/com/codandotv/streamplayerapp/feature_profile/profile/presentation/widget/ProfilePickerStreamLoad.kt b/feature-profile/src/androidMain/kotlin/com/codandotv/streamplayerapp/profile/presentation/widget/ProfilePickerStreamLoad.kt similarity index 85% rename from feature-profile/src/main/java/com/codandotv/streamplayerapp/feature_profile/profile/presentation/widget/ProfilePickerStreamLoad.kt rename to feature-profile/src/androidMain/kotlin/com/codandotv/streamplayerapp/profile/presentation/widget/ProfilePickerStreamLoad.kt index 7c2bc50d..a2746cbd 100644 --- a/feature-profile/src/main/java/com/codandotv/streamplayerapp/feature_profile/profile/presentation/widget/ProfilePickerStreamLoad.kt +++ b/feature-profile/src/androidMain/kotlin/com/codandotv/streamplayerapp/profile/presentation/widget/ProfilePickerStreamLoad.kt @@ -1,4 +1,4 @@ -package com.codandotv.streamplayerapp.feature_profile.profile.presentation.widget +package com.codandotv.streamplayerapp.profile.presentation.widget import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.fillMaxSize diff --git a/feature-profile/src/main/java/com/codandotv/streamplayerapp/feature_profile/profile/presentation/widget/ProfilePickerStreamToolbar.kt b/feature-profile/src/androidMain/kotlin/com/codandotv/streamplayerapp/profile/presentation/widget/ProfilePickerStreamToolbar.kt similarity index 95% rename from feature-profile/src/main/java/com/codandotv/streamplayerapp/feature_profile/profile/presentation/widget/ProfilePickerStreamToolbar.kt rename to feature-profile/src/androidMain/kotlin/com/codandotv/streamplayerapp/profile/presentation/widget/ProfilePickerStreamToolbar.kt index 50a2c455..fbde3c83 100644 --- a/feature-profile/src/main/java/com/codandotv/streamplayerapp/feature_profile/profile/presentation/widget/ProfilePickerStreamToolbar.kt +++ b/feature-profile/src/androidMain/kotlin/com/codandotv/streamplayerapp/profile/presentation/widget/ProfilePickerStreamToolbar.kt @@ -1,4 +1,4 @@ -package com.codandotv.streamplayerapp.feature_profile.profile.presentation.widget +package com.codandotv.streamplayerapp.profile.presentation.widget import androidx.compose.foundation.Image import androidx.compose.foundation.layout.Arrangement diff --git a/feature-profile/src/main/res/drawable/image_placeholder.xml b/feature-profile/src/androidMain/res/drawable/image_placeholder.xml similarity index 100% rename from feature-profile/src/main/res/drawable/image_placeholder.xml rename to feature-profile/src/androidMain/res/drawable/image_placeholder.xml diff --git a/feature-profile/src/main/res/drawable/netflix_horizontal_logo.xml b/feature-profile/src/androidMain/res/drawable/netflix_horizontal_logo.xml similarity index 100% rename from feature-profile/src/main/res/drawable/netflix_horizontal_logo.xml rename to feature-profile/src/androidMain/res/drawable/netflix_horizontal_logo.xml diff --git a/feature-profile/src/main/res/values/strings.xml b/feature-profile/src/androidMain/res/values/strings.xml similarity index 100% rename from feature-profile/src/main/res/values/strings.xml rename to feature-profile/src/androidMain/res/values/strings.xml From d2678f78f5f99d94ffb9b82b87a14d1bcc3c310d Mon Sep 17 00:00:00 2001 From: Rods Date: Sat, 18 Jan 2025 19:59:10 -0300 Subject: [PATCH 30/96] [ISSUE-3] - feature-profile to androidMain --- .../streamplayerapp/profile/di/ProfilePickerStreamModule.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/feature-profile/src/androidMain/kotlin/com/codandotv/streamplayerapp/profile/di/ProfilePickerStreamModule.kt b/feature-profile/src/androidMain/kotlin/com/codandotv/streamplayerapp/profile/di/ProfilePickerStreamModule.kt index 3a1f4719..978c3a81 100644 --- a/feature-profile/src/androidMain/kotlin/com/codandotv/streamplayerapp/profile/di/ProfilePickerStreamModule.kt +++ b/feature-profile/src/androidMain/kotlin/com/codandotv/streamplayerapp/profile/di/ProfilePickerStreamModule.kt @@ -9,7 +9,7 @@ import org.koin.core.annotation.Module import org.koin.core.context.GlobalContext @Module -@ComponentScan("com.codandotv.streamplayerapp.feature_profile") +@ComponentScan("com.codandotv.streamplayerapp.profile") class ProfilePickerStreamModule { @Factory From 22d31c8cca16e0ac71a451f04197561ab6eaec1f Mon Sep 17 00:00:00 2001 From: Rods Date: Sat, 18 Jan 2025 20:06:33 -0300 Subject: [PATCH 31/96] [ISSUE-3] - remove feature-favorite because havent nothing and remove plugin android --- app/build.gradle.kts | 1 - ...om.streamplayer.android-library.gradle.kts | 47 ------------------- feature-favorites/.gitignore | 1 - feature-favorites/build.gradle.kts | 20 -------- feature-favorites/consumer-rules.pro | 0 feature-favorites/proguard-rules.pro | 21 --------- settings.gradle.kts | 1 - 7 files changed, 91 deletions(-) delete mode 100644 build-logic/src/main/java/com.streamplayer.android-library.gradle.kts delete mode 100644 feature-favorites/.gitignore delete mode 100644 feature-favorites/build.gradle.kts delete mode 100644 feature-favorites/consumer-rules.pro delete mode 100644 feature-favorites/proguard-rules.pro diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 6b65c1b1..22cccc43 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -7,7 +7,6 @@ plugins { } dependencies { - implementation(projects.featureFavorites) implementation(projects.featureListStreams) implementation(projects.featureDetail) implementation(projects.featureSearch) diff --git a/build-logic/src/main/java/com.streamplayer.android-library.gradle.kts b/build-logic/src/main/java/com.streamplayer.android-library.gradle.kts deleted file mode 100644 index 196d8a85..00000000 --- a/build-logic/src/main/java/com.streamplayer.android-library.gradle.kts +++ /dev/null @@ -1,47 +0,0 @@ -@file:Suppress("UnstableApiUsage") - -import extensions.dokkaPlugin -import extensions.getLibrary -import extensions.setupAndroidDefaultConfig -import extensions.setupCompileOptions -import extensions.setupNameSpace -import extensions.setupPackingOptions - -val libs: VersionCatalog = extensions.getByType().named("libs") - -plugins { - id("com.android.library") - id("kotlin-kapt") - id("kotlin-android") - id("kotlin-parcelize") - id("com.streamplayer.dokka") - id("org.jetbrains.kotlinx.kover") - id("com.streamplayer.detekt") -} - -android { - setupNameSpace(project) - - setupCompileOptions() - - setupPackingOptions() - - setupAndroidDefaultConfig() - defaultConfig.targetSdk = Config.targetSdkVersion - - buildTypes { - getByName("release") { - isMinifyEnabled = true - proguardFiles("proguard-android.txt", "proguard-rules.pro") - consumerProguardFiles("proguard-rules.pro") - } - - getByName("debug") { - isMinifyEnabled = false - } - } -} - -dependencies { - dokkaPlugin(libs.getLibrary("dokka")) -} \ No newline at end of file diff --git a/feature-favorites/.gitignore b/feature-favorites/.gitignore deleted file mode 100644 index 42afabfd..00000000 --- a/feature-favorites/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/build \ No newline at end of file diff --git a/feature-favorites/build.gradle.kts b/feature-favorites/build.gradle.kts deleted file mode 100644 index ee61df78..00000000 --- a/feature-favorites/build.gradle.kts +++ /dev/null @@ -1,20 +0,0 @@ -@file:Suppress("UnstableApiUsage") -plugins { - id("com.streamplayer.android-library") - alias(libs.plugins.jetbrains.compose) - alias(libs.plugins.compose.compiler) -} - -dependencies { - implementation(projects.coreNetworking) - implementation(projects.coreNavigation) - implementation(projects.coreShared) - implementation(projects.coreSharedUi) - - implementation(libs.bundles.koin) - implementation(libs.bundles.networking) - implementation(libs.bundles.androidSupport) - implementation(libs.coil) - - testImplementation(libs.bundles.test) -} \ No newline at end of file diff --git a/feature-favorites/consumer-rules.pro b/feature-favorites/consumer-rules.pro deleted file mode 100644 index e69de29b..00000000 diff --git a/feature-favorites/proguard-rules.pro b/feature-favorites/proguard-rules.pro deleted file mode 100644 index 481bb434..00000000 --- a/feature-favorites/proguard-rules.pro +++ /dev/null @@ -1,21 +0,0 @@ -# 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/settings.gradle.kts b/settings.gradle.kts index bb5d9dd0..a064c3e1 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -28,7 +28,6 @@ include(":core-shared-ui") include(":core-navigation") include(":feature-profile") include(":core-local-storage") -include(":feature-favorites") include(":feature-detail") include(":feature-search") From 28a5262c102de116fef74a8b44f1fa63174a19bd Mon Sep 17 00:00:00 2001 From: Rods Date: Sun, 19 Jan 2025 11:57:39 -0300 Subject: [PATCH 32/96] [ISSUE-3] - module app to kmp --- app/build.gradle.kts | 46 ++++++++++--------- .../{main => androidMain}/AndroidManifest.xml | 4 +- .../streamplayerapp/CustomApplication.kt | 0 .../codandotv/streamplayerapp/MainActivity.kt | 0 .../codandotv/streamplayerapp/di/AppModule.kt | 0 .../navigation/NavigationGraph.kt | 0 .../navigation/SplashNavigation.kt | 0 .../presentation/screens/SplashScreen.kt | 0 .../res/xml/backup_rules.xml | 0 .../res/xml/data_extraction_rules.xml | 0 .../streamplayerapp/ExampleUnitTest.kt | 17 ------- .../com.streamplayer.application.gradle.kts | 12 ++++- 12 files changed, 38 insertions(+), 41 deletions(-) rename app/src/{main => androidMain}/AndroidManifest.xml (85%) rename app/src/{main/java => androidMain/kotlin}/com/codandotv/streamplayerapp/CustomApplication.kt (100%) rename app/src/{main/java => androidMain/kotlin}/com/codandotv/streamplayerapp/MainActivity.kt (100%) rename app/src/{main/java => androidMain/kotlin}/com/codandotv/streamplayerapp/di/AppModule.kt (100%) rename app/src/{main/java => androidMain/kotlin}/com/codandotv/streamplayerapp/navigation/NavigationGraph.kt (100%) rename app/src/{main/java => androidMain/kotlin}/com/codandotv/streamplayerapp/splah/presentation/navigation/SplashNavigation.kt (100%) rename app/src/{main/java => androidMain/kotlin}/com/codandotv/streamplayerapp/splah/presentation/screens/SplashScreen.kt (100%) rename app/src/{main => androidMain}/res/xml/backup_rules.xml (100%) rename app/src/{main => androidMain}/res/xml/data_extraction_rules.xml (100%) delete mode 100644 app/src/test/java/com/codandotv/streamplayerapp/ExampleUnitTest.kt diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 22cccc43..658249ca 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -5,30 +5,34 @@ plugins { alias(libs.plugins.jetbrains.compose) alias(libs.plugins.compose.compiler) } +kotlin { + sourceSets { + androidMain.dependencies { + implementation(projects.featureListStreams) + implementation(projects.featureDetail) + implementation(projects.featureSearch) + implementation(projects.featureProfile) + implementation(projects.coreShared) + implementation(projects.coreSharedUi) + implementation(projects.coreNavigation) + implementation(projects.coreNetworking) + implementation(projects.coreLocalStorage) -dependencies { - implementation(projects.featureListStreams) - implementation(projects.featureDetail) - implementation(projects.featureSearch) - implementation(projects.featureProfile) - implementation(projects.coreShared) - implementation(projects.coreSharedUi) - implementation(projects.coreNavigation) - implementation(projects.coreNetworking) - implementation(projects.coreLocalStorage) - - implementation(libs.navigation.compose) - implementation(compose.material3) - implementation(compose.ui) - implementation(compose.preview) - implementation(libs.bundles.koin) - implementation(libs.bundles.androidSupport) - implementation(libs.bundles.kotlin) + implementation(libs.navigation.compose) + implementation(compose.material3) + implementation(compose.ui) + implementation(compose.preview) + implementation(libs.bundles.koin) + implementation(libs.bundles.androidSupport) + implementation(libs.bundles.kotlin) - implementation(libs.lottie) - implementation(libs.lottie) - testImplementation(libs.bundles.test) + implementation(libs.lottie) + implementation(libs.lottie) + } + } +} +dependencies { // Kover - Combined report rootProject.subprojects.forEach { kover(it) } } \ No newline at end of file diff --git a/app/src/main/AndroidManifest.xml b/app/src/androidMain/AndroidManifest.xml similarity index 85% rename from app/src/main/AndroidManifest.xml rename to app/src/androidMain/AndroidManifest.xml index 43b331ce..4c4f5b7c 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/androidMain/AndroidManifest.xml @@ -9,10 +9,10 @@ android:roundIcon="@mipmap/ic_netflix_round" android:label="@string/app_name" android:supportsRtl="true" - android:name="com.codandotv.streamplayerapp.CustomApplication"> + android:name=".CustomApplication"> diff --git a/app/src/main/java/com/codandotv/streamplayerapp/CustomApplication.kt b/app/src/androidMain/kotlin/com/codandotv/streamplayerapp/CustomApplication.kt similarity index 100% rename from app/src/main/java/com/codandotv/streamplayerapp/CustomApplication.kt rename to app/src/androidMain/kotlin/com/codandotv/streamplayerapp/CustomApplication.kt diff --git a/app/src/main/java/com/codandotv/streamplayerapp/MainActivity.kt b/app/src/androidMain/kotlin/com/codandotv/streamplayerapp/MainActivity.kt similarity index 100% rename from app/src/main/java/com/codandotv/streamplayerapp/MainActivity.kt rename to app/src/androidMain/kotlin/com/codandotv/streamplayerapp/MainActivity.kt diff --git a/app/src/main/java/com/codandotv/streamplayerapp/di/AppModule.kt b/app/src/androidMain/kotlin/com/codandotv/streamplayerapp/di/AppModule.kt similarity index 100% rename from app/src/main/java/com/codandotv/streamplayerapp/di/AppModule.kt rename to app/src/androidMain/kotlin/com/codandotv/streamplayerapp/di/AppModule.kt diff --git a/app/src/main/java/com/codandotv/streamplayerapp/navigation/NavigationGraph.kt b/app/src/androidMain/kotlin/com/codandotv/streamplayerapp/navigation/NavigationGraph.kt similarity index 100% rename from app/src/main/java/com/codandotv/streamplayerapp/navigation/NavigationGraph.kt rename to app/src/androidMain/kotlin/com/codandotv/streamplayerapp/navigation/NavigationGraph.kt diff --git a/app/src/main/java/com/codandotv/streamplayerapp/splah/presentation/navigation/SplashNavigation.kt b/app/src/androidMain/kotlin/com/codandotv/streamplayerapp/splah/presentation/navigation/SplashNavigation.kt similarity index 100% rename from app/src/main/java/com/codandotv/streamplayerapp/splah/presentation/navigation/SplashNavigation.kt rename to app/src/androidMain/kotlin/com/codandotv/streamplayerapp/splah/presentation/navigation/SplashNavigation.kt diff --git a/app/src/main/java/com/codandotv/streamplayerapp/splah/presentation/screens/SplashScreen.kt b/app/src/androidMain/kotlin/com/codandotv/streamplayerapp/splah/presentation/screens/SplashScreen.kt similarity index 100% rename from app/src/main/java/com/codandotv/streamplayerapp/splah/presentation/screens/SplashScreen.kt rename to app/src/androidMain/kotlin/com/codandotv/streamplayerapp/splah/presentation/screens/SplashScreen.kt diff --git a/app/src/main/res/xml/backup_rules.xml b/app/src/androidMain/res/xml/backup_rules.xml similarity index 100% rename from app/src/main/res/xml/backup_rules.xml rename to app/src/androidMain/res/xml/backup_rules.xml diff --git a/app/src/main/res/xml/data_extraction_rules.xml b/app/src/androidMain/res/xml/data_extraction_rules.xml similarity index 100% rename from app/src/main/res/xml/data_extraction_rules.xml rename to app/src/androidMain/res/xml/data_extraction_rules.xml diff --git a/app/src/test/java/com/codandotv/streamplayerapp/ExampleUnitTest.kt b/app/src/test/java/com/codandotv/streamplayerapp/ExampleUnitTest.kt deleted file mode 100644 index e83e0c8a..00000000 --- a/app/src/test/java/com/codandotv/streamplayerapp/ExampleUnitTest.kt +++ /dev/null @@ -1,17 +0,0 @@ -package com.codandotv.streamplayerapp - -import org.junit.Test - -import org.junit.Assert.assertEquals - -/** - * Example local unit test, which will execute on the development machine (host). - * - * See [testing documentation](http://d.android.com/tools/testing). - */ -class ExampleUnitTest { - @Test - fun addition_isCorrect() { - assertEquals(4, 2 + 2) - } -} \ No newline at end of file diff --git a/build-logic/src/main/java/com.streamplayer.application.gradle.kts b/build-logic/src/main/java/com.streamplayer.application.gradle.kts index 9304e428..b1bcd23f 100644 --- a/build-logic/src/main/java/com.streamplayer.application.gradle.kts +++ b/build-logic/src/main/java/com.streamplayer.application.gradle.kts @@ -5,12 +5,14 @@ import extensions.getLibrary import extensions.setupAndroidDefaultConfig import extensions.setupCompileOptions import extensions.setupPackingOptions +import org.jetbrains.kotlin.gradle.ExperimentalKotlinGradlePluginApi +import org.jetbrains.kotlin.gradle.dsl.JvmTarget val libs: VersionCatalog = extensions.getByType().named("libs") plugins { id("com.android.application") - id("kotlin-android") + id("org.jetbrains.kotlin.multiplatform") id("kotlin-kapt") id("kotlin-parcelize") id("com.streamplayer.dokka") @@ -19,6 +21,14 @@ plugins { } val catalog: VersionCatalog = extensions.getByType().named("libs") +kotlin { + androidTarget { + @OptIn(ExperimentalKotlinGradlePluginApi::class) + compilerOptions { + jvmTarget.set(JvmTarget.JVM_17) + } + } +} android { namespace = Config.applicationId From 8e76edff3c8ec074b4c354b78f720e6244d6dda7 Mon Sep 17 00:00:00 2001 From: Rods Date: Sun, 19 Jan 2025 12:19:14 -0300 Subject: [PATCH 33/96] [ISSUE-3] - module app to composeApp --- {app => composeApp}/.gitignore | 0 {app => composeApp}/build.gradle.kts | 0 {app => composeApp}/proguard-rules.pro | 0 {app => composeApp}/src/androidMain/AndroidManifest.xml | 0 .../kotlin/com/codandotv/streamplayerapp/CustomApplication.kt | 0 .../kotlin/com/codandotv/streamplayerapp/MainActivity.kt | 0 .../kotlin/com/codandotv/streamplayerapp/di/AppModule.kt | 0 .../com/codandotv/streamplayerapp/navigation/NavigationGraph.kt | 0 .../splah/presentation/navigation/SplashNavigation.kt | 0 .../streamplayerapp/splah/presentation/screens/SplashScreen.kt | 0 {app => composeApp}/src/androidMain/res/xml/backup_rules.xml | 0 .../src/androidMain/res/xml/data_extraction_rules.xml | 0 settings.gradle.kts | 2 +- 13 files changed, 1 insertion(+), 1 deletion(-) rename {app => composeApp}/.gitignore (100%) rename {app => composeApp}/build.gradle.kts (100%) rename {app => composeApp}/proguard-rules.pro (100%) rename {app => composeApp}/src/androidMain/AndroidManifest.xml (100%) rename {app => composeApp}/src/androidMain/kotlin/com/codandotv/streamplayerapp/CustomApplication.kt (100%) rename {app => composeApp}/src/androidMain/kotlin/com/codandotv/streamplayerapp/MainActivity.kt (100%) rename {app => composeApp}/src/androidMain/kotlin/com/codandotv/streamplayerapp/di/AppModule.kt (100%) rename {app => composeApp}/src/androidMain/kotlin/com/codandotv/streamplayerapp/navigation/NavigationGraph.kt (100%) rename {app => composeApp}/src/androidMain/kotlin/com/codandotv/streamplayerapp/splah/presentation/navigation/SplashNavigation.kt (100%) rename {app => composeApp}/src/androidMain/kotlin/com/codandotv/streamplayerapp/splah/presentation/screens/SplashScreen.kt (100%) rename {app => composeApp}/src/androidMain/res/xml/backup_rules.xml (100%) rename {app => composeApp}/src/androidMain/res/xml/data_extraction_rules.xml (100%) diff --git a/app/.gitignore b/composeApp/.gitignore similarity index 100% rename from app/.gitignore rename to composeApp/.gitignore diff --git a/app/build.gradle.kts b/composeApp/build.gradle.kts similarity index 100% rename from app/build.gradle.kts rename to composeApp/build.gradle.kts diff --git a/app/proguard-rules.pro b/composeApp/proguard-rules.pro similarity index 100% rename from app/proguard-rules.pro rename to composeApp/proguard-rules.pro diff --git a/app/src/androidMain/AndroidManifest.xml b/composeApp/src/androidMain/AndroidManifest.xml similarity index 100% rename from app/src/androidMain/AndroidManifest.xml rename to composeApp/src/androidMain/AndroidManifest.xml diff --git a/app/src/androidMain/kotlin/com/codandotv/streamplayerapp/CustomApplication.kt b/composeApp/src/androidMain/kotlin/com/codandotv/streamplayerapp/CustomApplication.kt similarity index 100% rename from app/src/androidMain/kotlin/com/codandotv/streamplayerapp/CustomApplication.kt rename to composeApp/src/androidMain/kotlin/com/codandotv/streamplayerapp/CustomApplication.kt diff --git a/app/src/androidMain/kotlin/com/codandotv/streamplayerapp/MainActivity.kt b/composeApp/src/androidMain/kotlin/com/codandotv/streamplayerapp/MainActivity.kt similarity index 100% rename from app/src/androidMain/kotlin/com/codandotv/streamplayerapp/MainActivity.kt rename to composeApp/src/androidMain/kotlin/com/codandotv/streamplayerapp/MainActivity.kt diff --git a/app/src/androidMain/kotlin/com/codandotv/streamplayerapp/di/AppModule.kt b/composeApp/src/androidMain/kotlin/com/codandotv/streamplayerapp/di/AppModule.kt similarity index 100% rename from app/src/androidMain/kotlin/com/codandotv/streamplayerapp/di/AppModule.kt rename to composeApp/src/androidMain/kotlin/com/codandotv/streamplayerapp/di/AppModule.kt diff --git a/app/src/androidMain/kotlin/com/codandotv/streamplayerapp/navigation/NavigationGraph.kt b/composeApp/src/androidMain/kotlin/com/codandotv/streamplayerapp/navigation/NavigationGraph.kt similarity index 100% rename from app/src/androidMain/kotlin/com/codandotv/streamplayerapp/navigation/NavigationGraph.kt rename to composeApp/src/androidMain/kotlin/com/codandotv/streamplayerapp/navigation/NavigationGraph.kt diff --git a/app/src/androidMain/kotlin/com/codandotv/streamplayerapp/splah/presentation/navigation/SplashNavigation.kt b/composeApp/src/androidMain/kotlin/com/codandotv/streamplayerapp/splah/presentation/navigation/SplashNavigation.kt similarity index 100% rename from app/src/androidMain/kotlin/com/codandotv/streamplayerapp/splah/presentation/navigation/SplashNavigation.kt rename to composeApp/src/androidMain/kotlin/com/codandotv/streamplayerapp/splah/presentation/navigation/SplashNavigation.kt diff --git a/app/src/androidMain/kotlin/com/codandotv/streamplayerapp/splah/presentation/screens/SplashScreen.kt b/composeApp/src/androidMain/kotlin/com/codandotv/streamplayerapp/splah/presentation/screens/SplashScreen.kt similarity index 100% rename from app/src/androidMain/kotlin/com/codandotv/streamplayerapp/splah/presentation/screens/SplashScreen.kt rename to composeApp/src/androidMain/kotlin/com/codandotv/streamplayerapp/splah/presentation/screens/SplashScreen.kt diff --git a/app/src/androidMain/res/xml/backup_rules.xml b/composeApp/src/androidMain/res/xml/backup_rules.xml similarity index 100% rename from app/src/androidMain/res/xml/backup_rules.xml rename to composeApp/src/androidMain/res/xml/backup_rules.xml diff --git a/app/src/androidMain/res/xml/data_extraction_rules.xml b/composeApp/src/androidMain/res/xml/data_extraction_rules.xml similarity index 100% rename from app/src/androidMain/res/xml/data_extraction_rules.xml rename to composeApp/src/androidMain/res/xml/data_extraction_rules.xml diff --git a/settings.gradle.kts b/settings.gradle.kts index a064c3e1..7bbc2720 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -20,7 +20,7 @@ dependencyResolutionManagement { } -include(":app") +include(":composeApp") include(":feature-list-streams") include(":core-shared") include(":core-networking") From c42fd53f175c97ddd47903a5034b46373643f8bd Mon Sep 17 00:00:00 2001 From: Rods Date: Sun, 19 Jan 2025 12:27:23 -0300 Subject: [PATCH 34/96] [ISSUE-3] - test to commonTest --- .../streamplayerapp/feature_detail/DetailStreamRepositoryTest.kt | 0 .../streamplayerapp/feature_detail/DetailStreamUseCaseTest.kt | 0 .../streamplayerapp/feature_detail/DetailStreamViewModelTest.kt | 0 .../feature_detail/InstantTaskCoroutinesExecutorRule.kt | 0 .../com/codandotv/streamplayerapp/feature_detail/Shared.kt | 0 5 files changed, 0 insertions(+), 0 deletions(-) rename feature-detail/src/{test/java => commonTest/kotlin}/com/codandotv/streamplayerapp/feature_detail/DetailStreamRepositoryTest.kt (100%) rename feature-detail/src/{test/java => commonTest/kotlin}/com/codandotv/streamplayerapp/feature_detail/DetailStreamUseCaseTest.kt (100%) rename feature-detail/src/{test/java => commonTest/kotlin}/com/codandotv/streamplayerapp/feature_detail/DetailStreamViewModelTest.kt (100%) rename feature-detail/src/{test/java => commonTest/kotlin}/com/codandotv/streamplayerapp/feature_detail/InstantTaskCoroutinesExecutorRule.kt (100%) rename feature-detail/src/{test/java => commonTest/kotlin}/com/codandotv/streamplayerapp/feature_detail/Shared.kt (100%) diff --git a/feature-detail/src/test/java/com/codandotv/streamplayerapp/feature_detail/DetailStreamRepositoryTest.kt b/feature-detail/src/commonTest/kotlin/com/codandotv/streamplayerapp/feature_detail/DetailStreamRepositoryTest.kt similarity index 100% rename from feature-detail/src/test/java/com/codandotv/streamplayerapp/feature_detail/DetailStreamRepositoryTest.kt rename to feature-detail/src/commonTest/kotlin/com/codandotv/streamplayerapp/feature_detail/DetailStreamRepositoryTest.kt diff --git a/feature-detail/src/test/java/com/codandotv/streamplayerapp/feature_detail/DetailStreamUseCaseTest.kt b/feature-detail/src/commonTest/kotlin/com/codandotv/streamplayerapp/feature_detail/DetailStreamUseCaseTest.kt similarity index 100% rename from feature-detail/src/test/java/com/codandotv/streamplayerapp/feature_detail/DetailStreamUseCaseTest.kt rename to feature-detail/src/commonTest/kotlin/com/codandotv/streamplayerapp/feature_detail/DetailStreamUseCaseTest.kt diff --git a/feature-detail/src/test/java/com/codandotv/streamplayerapp/feature_detail/DetailStreamViewModelTest.kt b/feature-detail/src/commonTest/kotlin/com/codandotv/streamplayerapp/feature_detail/DetailStreamViewModelTest.kt similarity index 100% rename from feature-detail/src/test/java/com/codandotv/streamplayerapp/feature_detail/DetailStreamViewModelTest.kt rename to feature-detail/src/commonTest/kotlin/com/codandotv/streamplayerapp/feature_detail/DetailStreamViewModelTest.kt diff --git a/feature-detail/src/test/java/com/codandotv/streamplayerapp/feature_detail/InstantTaskCoroutinesExecutorRule.kt b/feature-detail/src/commonTest/kotlin/com/codandotv/streamplayerapp/feature_detail/InstantTaskCoroutinesExecutorRule.kt similarity index 100% rename from feature-detail/src/test/java/com/codandotv/streamplayerapp/feature_detail/InstantTaskCoroutinesExecutorRule.kt rename to feature-detail/src/commonTest/kotlin/com/codandotv/streamplayerapp/feature_detail/InstantTaskCoroutinesExecutorRule.kt diff --git a/feature-detail/src/test/java/com/codandotv/streamplayerapp/feature_detail/Shared.kt b/feature-detail/src/commonTest/kotlin/com/codandotv/streamplayerapp/feature_detail/Shared.kt similarity index 100% rename from feature-detail/src/test/java/com/codandotv/streamplayerapp/feature_detail/Shared.kt rename to feature-detail/src/commonTest/kotlin/com/codandotv/streamplayerapp/feature_detail/Shared.kt From 77a30eb89d33380b7647fda22505684b65b29fe1 Mon Sep 17 00:00:00 2001 From: Rods Date: Mon, 27 Jan 2025 16:54:48 -0300 Subject: [PATCH 35/96] [ISSUE-6] - detail and profile androidMain to CommonMain --- feature-detail/build.gradle.kts | 5 --- .../data/DetailStreamRepository.kt | 0 .../data/DetailStreamService.kt | 0 .../data/model/DetailStreamResponse.kt | 0 .../data/model/VideoStreamResponse.kt | 0 .../feature_detail/di/DetailStreamModule.kt | 0 .../feature_detail/domain/DetailStream.kt | 0 .../domain/DetailStreamMapper.kt | 0 .../domain/DetailStreamUseCase.kt | 0 .../feature_detail/domain/VideoStream.kt | 0 .../domain/VideoStreamsUseCase.kt | 0 .../navigation/DetailStreamNavigation.kt | 0 .../screens/DetailStreamViewModel.kt | 0 .../screens/DetailStreamsScreen.kt | 0 .../screens/DetailStreamsUIState.kt | 0 .../widget/DetailStreamActionOption.kt | 0 .../widget/DetailStreamButtonAction.kt | 0 .../widget/DetailStreamImagePreview.kt | 0 .../widget/DetailStreamRowHeader.kt | 0 .../widget/DetailStreamToolbar.kt | 0 feature-profile/build.gradle.kts | 14 +------ .../profile/di/ProfilePickerStreamModule.kt | 21 ---------- .../data/ProfilePickerStreamRepository.kt | 0 .../data/ProfilePickerStreamService.kt | 0 .../data/model/ProfileStreamResponse.kt | 0 .../profile/di/ProfilePickerStreamModule.kt | 39 +++++++++++++++++++ .../domain/ProfilePickerStreamMapper.kt | 0 .../domain/ProfilePickerStreamUseCase.kt | 0 .../profile/domain/ProfileStream.kt | 0 .../ProfilePickerStreamNavigation.kt | 5 +-- .../screens/ProfilePickerStreamScreen.kt | 0 .../screens/ProfilePickerStreamViewModel.kt | 0 .../screens/ProfilePickerStreamsUIState.kt | 0 .../presentation/widget/ComposeExtensions.kt | 0 .../widget/ProfilePickerOpacityLayer.kt | 0 .../widget/ProfilePickerProfilesGrid.kt | 0 .../ProfilePickerSelectedProfileContainer.kt | 0 .../widget/ProfilePickerStreamLoad.kt | 0 .../widget/ProfilePickerStreamToolbar.kt | 0 39 files changed, 43 insertions(+), 41 deletions(-) rename feature-detail/src/{androidMain => commonMain}/kotlin/com/codandotv/streamplayerapp/feature_detail/data/DetailStreamRepository.kt (100%) rename feature-detail/src/{androidMain => commonMain}/kotlin/com/codandotv/streamplayerapp/feature_detail/data/DetailStreamService.kt (100%) rename feature-detail/src/{androidMain => commonMain}/kotlin/com/codandotv/streamplayerapp/feature_detail/data/model/DetailStreamResponse.kt (100%) rename feature-detail/src/{androidMain => commonMain}/kotlin/com/codandotv/streamplayerapp/feature_detail/data/model/VideoStreamResponse.kt (100%) rename feature-detail/src/{androidMain => commonMain}/kotlin/com/codandotv/streamplayerapp/feature_detail/di/DetailStreamModule.kt (100%) rename feature-detail/src/{androidMain => commonMain}/kotlin/com/codandotv/streamplayerapp/feature_detail/domain/DetailStream.kt (100%) rename feature-detail/src/{androidMain => commonMain}/kotlin/com/codandotv/streamplayerapp/feature_detail/domain/DetailStreamMapper.kt (100%) rename feature-detail/src/{androidMain => commonMain}/kotlin/com/codandotv/streamplayerapp/feature_detail/domain/DetailStreamUseCase.kt (100%) rename feature-detail/src/{androidMain => commonMain}/kotlin/com/codandotv/streamplayerapp/feature_detail/domain/VideoStream.kt (100%) rename feature-detail/src/{androidMain => commonMain}/kotlin/com/codandotv/streamplayerapp/feature_detail/domain/VideoStreamsUseCase.kt (100%) rename feature-detail/src/{androidMain => commonMain}/kotlin/com/codandotv/streamplayerapp/feature_detail/presentation/navigation/DetailStreamNavigation.kt (100%) rename feature-detail/src/{androidMain => commonMain}/kotlin/com/codandotv/streamplayerapp/feature_detail/presentation/screens/DetailStreamViewModel.kt (100%) rename feature-detail/src/{androidMain => commonMain}/kotlin/com/codandotv/streamplayerapp/feature_detail/presentation/screens/DetailStreamsScreen.kt (100%) rename feature-detail/src/{androidMain => commonMain}/kotlin/com/codandotv/streamplayerapp/feature_detail/presentation/screens/DetailStreamsUIState.kt (100%) rename feature-detail/src/{androidMain => commonMain}/kotlin/com/codandotv/streamplayerapp/feature_detail/presentation/widget/DetailStreamActionOption.kt (100%) rename feature-detail/src/{androidMain => commonMain}/kotlin/com/codandotv/streamplayerapp/feature_detail/presentation/widget/DetailStreamButtonAction.kt (100%) rename feature-detail/src/{androidMain => commonMain}/kotlin/com/codandotv/streamplayerapp/feature_detail/presentation/widget/DetailStreamImagePreview.kt (100%) rename feature-detail/src/{androidMain => commonMain}/kotlin/com/codandotv/streamplayerapp/feature_detail/presentation/widget/DetailStreamRowHeader.kt (100%) rename feature-detail/src/{androidMain => commonMain}/kotlin/com/codandotv/streamplayerapp/feature_detail/presentation/widget/DetailStreamToolbar.kt (100%) delete mode 100644 feature-profile/src/androidMain/kotlin/com/codandotv/streamplayerapp/profile/di/ProfilePickerStreamModule.kt rename feature-profile/src/{androidMain => commonMain}/kotlin/com/codandotv/streamplayerapp/profile/data/ProfilePickerStreamRepository.kt (100%) rename feature-profile/src/{androidMain => commonMain}/kotlin/com/codandotv/streamplayerapp/profile/data/ProfilePickerStreamService.kt (100%) rename feature-profile/src/{androidMain => commonMain}/kotlin/com/codandotv/streamplayerapp/profile/data/model/ProfileStreamResponse.kt (100%) create mode 100644 feature-profile/src/commonMain/kotlin/com/codandotv/streamplayerapp/profile/di/ProfilePickerStreamModule.kt rename feature-profile/src/{androidMain => commonMain}/kotlin/com/codandotv/streamplayerapp/profile/domain/ProfilePickerStreamMapper.kt (100%) rename feature-profile/src/{androidMain => commonMain}/kotlin/com/codandotv/streamplayerapp/profile/domain/ProfilePickerStreamUseCase.kt (100%) rename feature-profile/src/{androidMain => commonMain}/kotlin/com/codandotv/streamplayerapp/profile/domain/ProfileStream.kt (100%) rename feature-profile/src/{androidMain => commonMain}/kotlin/com/codandotv/streamplayerapp/profile/presentation/navigation/ProfilePickerStreamNavigation.kt (87%) rename feature-profile/src/{androidMain => commonMain}/kotlin/com/codandotv/streamplayerapp/profile/presentation/screens/ProfilePickerStreamScreen.kt (100%) rename feature-profile/src/{androidMain => commonMain}/kotlin/com/codandotv/streamplayerapp/profile/presentation/screens/ProfilePickerStreamViewModel.kt (100%) rename feature-profile/src/{androidMain => commonMain}/kotlin/com/codandotv/streamplayerapp/profile/presentation/screens/ProfilePickerStreamsUIState.kt (100%) rename feature-profile/src/{androidMain => commonMain}/kotlin/com/codandotv/streamplayerapp/profile/presentation/widget/ComposeExtensions.kt (100%) rename feature-profile/src/{androidMain => commonMain}/kotlin/com/codandotv/streamplayerapp/profile/presentation/widget/ProfilePickerOpacityLayer.kt (100%) rename feature-profile/src/{androidMain => commonMain}/kotlin/com/codandotv/streamplayerapp/profile/presentation/widget/ProfilePickerProfilesGrid.kt (100%) rename feature-profile/src/{androidMain => commonMain}/kotlin/com/codandotv/streamplayerapp/profile/presentation/widget/ProfilePickerSelectedProfileContainer.kt (100%) rename feature-profile/src/{androidMain => commonMain}/kotlin/com/codandotv/streamplayerapp/profile/presentation/widget/ProfilePickerStreamLoad.kt (100%) rename feature-profile/src/{androidMain => commonMain}/kotlin/com/codandotv/streamplayerapp/profile/presentation/widget/ProfilePickerStreamToolbar.kt (100%) diff --git a/feature-detail/build.gradle.kts b/feature-detail/build.gradle.kts index 5f4648bf..c6d6971e 100644 --- a/feature-detail/build.gradle.kts +++ b/feature-detail/build.gradle.kts @@ -12,9 +12,6 @@ kotlin { commonMain.dependencies { implementation(libs.koin.annotations) implementation(libs.koin.core) - } - - androidMain.dependencies { implementation(projects.coreNetworking) implementation(projects.coreNavigation) implementation(projects.coreShared) @@ -31,9 +28,7 @@ kotlin { implementation(libs.coil) implementation(libs.koin.annotations) implementation(libs.bundles.androidSupport) - } - commonTest.dependencies { implementation(libs.bundles.test) } diff --git a/feature-detail/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_detail/data/DetailStreamRepository.kt b/feature-detail/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_detail/data/DetailStreamRepository.kt similarity index 100% rename from feature-detail/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_detail/data/DetailStreamRepository.kt rename to feature-detail/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_detail/data/DetailStreamRepository.kt diff --git a/feature-detail/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_detail/data/DetailStreamService.kt b/feature-detail/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_detail/data/DetailStreamService.kt similarity index 100% rename from feature-detail/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_detail/data/DetailStreamService.kt rename to feature-detail/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_detail/data/DetailStreamService.kt diff --git a/feature-detail/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_detail/data/model/DetailStreamResponse.kt b/feature-detail/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_detail/data/model/DetailStreamResponse.kt similarity index 100% rename from feature-detail/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_detail/data/model/DetailStreamResponse.kt rename to feature-detail/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_detail/data/model/DetailStreamResponse.kt diff --git a/feature-detail/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_detail/data/model/VideoStreamResponse.kt b/feature-detail/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_detail/data/model/VideoStreamResponse.kt similarity index 100% rename from feature-detail/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_detail/data/model/VideoStreamResponse.kt rename to feature-detail/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_detail/data/model/VideoStreamResponse.kt diff --git a/feature-detail/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_detail/di/DetailStreamModule.kt b/feature-detail/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_detail/di/DetailStreamModule.kt similarity index 100% rename from feature-detail/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_detail/di/DetailStreamModule.kt rename to feature-detail/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_detail/di/DetailStreamModule.kt diff --git a/feature-detail/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_detail/domain/DetailStream.kt b/feature-detail/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_detail/domain/DetailStream.kt similarity index 100% rename from feature-detail/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_detail/domain/DetailStream.kt rename to feature-detail/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_detail/domain/DetailStream.kt diff --git a/feature-detail/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_detail/domain/DetailStreamMapper.kt b/feature-detail/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_detail/domain/DetailStreamMapper.kt similarity index 100% rename from feature-detail/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_detail/domain/DetailStreamMapper.kt rename to feature-detail/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_detail/domain/DetailStreamMapper.kt diff --git a/feature-detail/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_detail/domain/DetailStreamUseCase.kt b/feature-detail/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_detail/domain/DetailStreamUseCase.kt similarity index 100% rename from feature-detail/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_detail/domain/DetailStreamUseCase.kt rename to feature-detail/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_detail/domain/DetailStreamUseCase.kt diff --git a/feature-detail/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_detail/domain/VideoStream.kt b/feature-detail/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_detail/domain/VideoStream.kt similarity index 100% rename from feature-detail/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_detail/domain/VideoStream.kt rename to feature-detail/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_detail/domain/VideoStream.kt diff --git a/feature-detail/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_detail/domain/VideoStreamsUseCase.kt b/feature-detail/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_detail/domain/VideoStreamsUseCase.kt similarity index 100% rename from feature-detail/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_detail/domain/VideoStreamsUseCase.kt rename to feature-detail/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_detail/domain/VideoStreamsUseCase.kt diff --git a/feature-detail/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_detail/presentation/navigation/DetailStreamNavigation.kt b/feature-detail/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_detail/presentation/navigation/DetailStreamNavigation.kt similarity index 100% rename from feature-detail/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_detail/presentation/navigation/DetailStreamNavigation.kt rename to feature-detail/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_detail/presentation/navigation/DetailStreamNavigation.kt diff --git a/feature-detail/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_detail/presentation/screens/DetailStreamViewModel.kt b/feature-detail/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_detail/presentation/screens/DetailStreamViewModel.kt similarity index 100% rename from feature-detail/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_detail/presentation/screens/DetailStreamViewModel.kt rename to feature-detail/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_detail/presentation/screens/DetailStreamViewModel.kt diff --git a/feature-detail/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_detail/presentation/screens/DetailStreamsScreen.kt b/feature-detail/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_detail/presentation/screens/DetailStreamsScreen.kt similarity index 100% rename from feature-detail/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_detail/presentation/screens/DetailStreamsScreen.kt rename to feature-detail/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_detail/presentation/screens/DetailStreamsScreen.kt diff --git a/feature-detail/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_detail/presentation/screens/DetailStreamsUIState.kt b/feature-detail/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_detail/presentation/screens/DetailStreamsUIState.kt similarity index 100% rename from feature-detail/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_detail/presentation/screens/DetailStreamsUIState.kt rename to feature-detail/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_detail/presentation/screens/DetailStreamsUIState.kt diff --git a/feature-detail/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_detail/presentation/widget/DetailStreamActionOption.kt b/feature-detail/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_detail/presentation/widget/DetailStreamActionOption.kt similarity index 100% rename from feature-detail/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_detail/presentation/widget/DetailStreamActionOption.kt rename to feature-detail/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_detail/presentation/widget/DetailStreamActionOption.kt diff --git a/feature-detail/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_detail/presentation/widget/DetailStreamButtonAction.kt b/feature-detail/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_detail/presentation/widget/DetailStreamButtonAction.kt similarity index 100% rename from feature-detail/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_detail/presentation/widget/DetailStreamButtonAction.kt rename to feature-detail/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_detail/presentation/widget/DetailStreamButtonAction.kt diff --git a/feature-detail/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_detail/presentation/widget/DetailStreamImagePreview.kt b/feature-detail/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_detail/presentation/widget/DetailStreamImagePreview.kt similarity index 100% rename from feature-detail/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_detail/presentation/widget/DetailStreamImagePreview.kt rename to feature-detail/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_detail/presentation/widget/DetailStreamImagePreview.kt diff --git a/feature-detail/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_detail/presentation/widget/DetailStreamRowHeader.kt b/feature-detail/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_detail/presentation/widget/DetailStreamRowHeader.kt similarity index 100% rename from feature-detail/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_detail/presentation/widget/DetailStreamRowHeader.kt rename to feature-detail/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_detail/presentation/widget/DetailStreamRowHeader.kt diff --git a/feature-detail/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_detail/presentation/widget/DetailStreamToolbar.kt b/feature-detail/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_detail/presentation/widget/DetailStreamToolbar.kt similarity index 100% rename from feature-detail/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_detail/presentation/widget/DetailStreamToolbar.kt rename to feature-detail/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_detail/presentation/widget/DetailStreamToolbar.kt diff --git a/feature-profile/build.gradle.kts b/feature-profile/build.gradle.kts index fe8df508..387f47d6 100644 --- a/feature-profile/build.gradle.kts +++ b/feature-profile/build.gradle.kts @@ -4,12 +4,11 @@ plugins { id("com.streamplayer.kmp-library") alias(libs.plugins.jetbrains.compose) alias(libs.plugins.compose.compiler) - alias(libs.plugins.ksp) } kotlin { sourceSets { - androidMain.dependencies { + commonMain.dependencies { implementation(projects.coreNetworking) implementation(projects.coreNavigation) implementation(projects.coreShared) @@ -26,13 +25,4 @@ kotlin { } } -} - -dependencies { - add("kspCommonMainMetadata",libs.koin.compiler) - add("kspAndroid", libs.koin.compiler) -} - -ksp { - arg("KOIN_CONFIG_CHECK","true") -} +} \ No newline at end of file diff --git a/feature-profile/src/androidMain/kotlin/com/codandotv/streamplayerapp/profile/di/ProfilePickerStreamModule.kt b/feature-profile/src/androidMain/kotlin/com/codandotv/streamplayerapp/profile/di/ProfilePickerStreamModule.kt deleted file mode 100644 index 978c3a81..00000000 --- a/feature-profile/src/androidMain/kotlin/com/codandotv/streamplayerapp/profile/di/ProfilePickerStreamModule.kt +++ /dev/null @@ -1,21 +0,0 @@ -package com.codandotv.streamplayerapp.profile.di - -import com.codandotv.streamplayerapp.profile.data.ProfilePickerStreamService -import com.codandotv.streamplayerapp.profile.data.ProfilePickerStreamServiceImpl -import io.ktor.client.HttpClient -import org.koin.core.annotation.ComponentScan -import org.koin.core.annotation.Factory -import org.koin.core.annotation.Module -import org.koin.core.context.GlobalContext - -@Module -@ComponentScan("com.codandotv.streamplayerapp.profile") -class ProfilePickerStreamModule { - - @Factory - fun service(): ProfilePickerStreamService { - val koin = GlobalContext.get() - val client = koin.get() - return ProfilePickerStreamServiceImpl(client) - } -} diff --git a/feature-profile/src/androidMain/kotlin/com/codandotv/streamplayerapp/profile/data/ProfilePickerStreamRepository.kt b/feature-profile/src/commonMain/kotlin/com/codandotv/streamplayerapp/profile/data/ProfilePickerStreamRepository.kt similarity index 100% rename from feature-profile/src/androidMain/kotlin/com/codandotv/streamplayerapp/profile/data/ProfilePickerStreamRepository.kt rename to feature-profile/src/commonMain/kotlin/com/codandotv/streamplayerapp/profile/data/ProfilePickerStreamRepository.kt diff --git a/feature-profile/src/androidMain/kotlin/com/codandotv/streamplayerapp/profile/data/ProfilePickerStreamService.kt b/feature-profile/src/commonMain/kotlin/com/codandotv/streamplayerapp/profile/data/ProfilePickerStreamService.kt similarity index 100% rename from feature-profile/src/androidMain/kotlin/com/codandotv/streamplayerapp/profile/data/ProfilePickerStreamService.kt rename to feature-profile/src/commonMain/kotlin/com/codandotv/streamplayerapp/profile/data/ProfilePickerStreamService.kt diff --git a/feature-profile/src/androidMain/kotlin/com/codandotv/streamplayerapp/profile/data/model/ProfileStreamResponse.kt b/feature-profile/src/commonMain/kotlin/com/codandotv/streamplayerapp/profile/data/model/ProfileStreamResponse.kt similarity index 100% rename from feature-profile/src/androidMain/kotlin/com/codandotv/streamplayerapp/profile/data/model/ProfileStreamResponse.kt rename to feature-profile/src/commonMain/kotlin/com/codandotv/streamplayerapp/profile/data/model/ProfileStreamResponse.kt diff --git a/feature-profile/src/commonMain/kotlin/com/codandotv/streamplayerapp/profile/di/ProfilePickerStreamModule.kt b/feature-profile/src/commonMain/kotlin/com/codandotv/streamplayerapp/profile/di/ProfilePickerStreamModule.kt new file mode 100644 index 00000000..b026f91b --- /dev/null +++ b/feature-profile/src/commonMain/kotlin/com/codandotv/streamplayerapp/profile/di/ProfilePickerStreamModule.kt @@ -0,0 +1,39 @@ +package com.codandotv.streamplayerapp.profile.di + +import com.codandotv.streamplayerapp.profile.data.ProfilePickerStreamRepository +import com.codandotv.streamplayerapp.profile.data.ProfilePickerStreamRepositoryImpl +import com.codandotv.streamplayerapp.profile.data.ProfilePickerStreamService +import com.codandotv.streamplayerapp.profile.data.ProfilePickerStreamServiceImpl +import com.codandotv.streamplayerapp.profile.domain.ProfilePickerStreamUseCase +import com.codandotv.streamplayerapp.profile.domain.ProfilePickerStreamUseCaseImpl +import com.codandotv.streamplayerapp.profile.presentation.screens.ProfilePickerStreamViewModel +import org.koin.androidx.viewmodel.dsl.viewModel +import org.koin.dsl.module + +object ProfilePickerStreamModule { + val module = module { + viewModel { + ProfilePickerStreamViewModel( + useCase = get() + ) + } + + factory { + ProfilePickerStreamUseCaseImpl( + profilePickerStreamRepository = get() + ) + } + + factory { + ProfilePickerStreamRepositoryImpl( + service = get() + ) + } + + factory { + ProfilePickerStreamServiceImpl( + client = get() + ) + } + } +} \ No newline at end of file diff --git a/feature-profile/src/androidMain/kotlin/com/codandotv/streamplayerapp/profile/domain/ProfilePickerStreamMapper.kt b/feature-profile/src/commonMain/kotlin/com/codandotv/streamplayerapp/profile/domain/ProfilePickerStreamMapper.kt similarity index 100% rename from feature-profile/src/androidMain/kotlin/com/codandotv/streamplayerapp/profile/domain/ProfilePickerStreamMapper.kt rename to feature-profile/src/commonMain/kotlin/com/codandotv/streamplayerapp/profile/domain/ProfilePickerStreamMapper.kt diff --git a/feature-profile/src/androidMain/kotlin/com/codandotv/streamplayerapp/profile/domain/ProfilePickerStreamUseCase.kt b/feature-profile/src/commonMain/kotlin/com/codandotv/streamplayerapp/profile/domain/ProfilePickerStreamUseCase.kt similarity index 100% rename from feature-profile/src/androidMain/kotlin/com/codandotv/streamplayerapp/profile/domain/ProfilePickerStreamUseCase.kt rename to feature-profile/src/commonMain/kotlin/com/codandotv/streamplayerapp/profile/domain/ProfilePickerStreamUseCase.kt diff --git a/feature-profile/src/androidMain/kotlin/com/codandotv/streamplayerapp/profile/domain/ProfileStream.kt b/feature-profile/src/commonMain/kotlin/com/codandotv/streamplayerapp/profile/domain/ProfileStream.kt similarity index 100% rename from feature-profile/src/androidMain/kotlin/com/codandotv/streamplayerapp/profile/domain/ProfileStream.kt rename to feature-profile/src/commonMain/kotlin/com/codandotv/streamplayerapp/profile/domain/ProfileStream.kt diff --git a/feature-profile/src/androidMain/kotlin/com/codandotv/streamplayerapp/profile/presentation/navigation/ProfilePickerStreamNavigation.kt b/feature-profile/src/commonMain/kotlin/com/codandotv/streamplayerapp/profile/presentation/navigation/ProfilePickerStreamNavigation.kt similarity index 87% rename from feature-profile/src/androidMain/kotlin/com/codandotv/streamplayerapp/profile/presentation/navigation/ProfilePickerStreamNavigation.kt rename to feature-profile/src/commonMain/kotlin/com/codandotv/streamplayerapp/profile/presentation/navigation/ProfilePickerStreamNavigation.kt index 61c12602..8d42759a 100644 --- a/feature-profile/src/androidMain/kotlin/com/codandotv/streamplayerapp/profile/presentation/navigation/ProfilePickerStreamNavigation.kt +++ b/feature-profile/src/commonMain/kotlin/com/codandotv/streamplayerapp/profile/presentation/navigation/ProfilePickerStreamNavigation.kt @@ -11,19 +11,18 @@ import com.codandotv.streamplayerapp.profile.di.ProfilePickerStreamModule import com.codandotv.streamplayerapp.profile.presentation.screens.ProfilePickerStreamScreen import org.koin.core.context.loadKoinModules import org.koin.core.context.unloadKoinModules -import org.koin.ksp.generated.module fun NavGraphBuilder.profilePickerStreamNavGraph(navController: NavHostController) { composable(Routes.PROFILE_PICKER) { nav -> if (nav.lifecycle.currentState == Lifecycle.State.STARTED) { - loadKoinModules(ProfilePickerStreamModule().module) + loadKoinModules(ProfilePickerStreamModule.module) } ProfilePickerStreamScreen( onNavigateListStreams = { profilePic -> navController.navigate("$HOME?$PROFILE_ID=$profilePic") } ) { - unloadKoinModules(ProfilePickerStreamModule().module) + unloadKoinModules(ProfilePickerStreamModule.module) } } } diff --git a/feature-profile/src/androidMain/kotlin/com/codandotv/streamplayerapp/profile/presentation/screens/ProfilePickerStreamScreen.kt b/feature-profile/src/commonMain/kotlin/com/codandotv/streamplayerapp/profile/presentation/screens/ProfilePickerStreamScreen.kt similarity index 100% rename from feature-profile/src/androidMain/kotlin/com/codandotv/streamplayerapp/profile/presentation/screens/ProfilePickerStreamScreen.kt rename to feature-profile/src/commonMain/kotlin/com/codandotv/streamplayerapp/profile/presentation/screens/ProfilePickerStreamScreen.kt diff --git a/feature-profile/src/androidMain/kotlin/com/codandotv/streamplayerapp/profile/presentation/screens/ProfilePickerStreamViewModel.kt b/feature-profile/src/commonMain/kotlin/com/codandotv/streamplayerapp/profile/presentation/screens/ProfilePickerStreamViewModel.kt similarity index 100% rename from feature-profile/src/androidMain/kotlin/com/codandotv/streamplayerapp/profile/presentation/screens/ProfilePickerStreamViewModel.kt rename to feature-profile/src/commonMain/kotlin/com/codandotv/streamplayerapp/profile/presentation/screens/ProfilePickerStreamViewModel.kt diff --git a/feature-profile/src/androidMain/kotlin/com/codandotv/streamplayerapp/profile/presentation/screens/ProfilePickerStreamsUIState.kt b/feature-profile/src/commonMain/kotlin/com/codandotv/streamplayerapp/profile/presentation/screens/ProfilePickerStreamsUIState.kt similarity index 100% rename from feature-profile/src/androidMain/kotlin/com/codandotv/streamplayerapp/profile/presentation/screens/ProfilePickerStreamsUIState.kt rename to feature-profile/src/commonMain/kotlin/com/codandotv/streamplayerapp/profile/presentation/screens/ProfilePickerStreamsUIState.kt diff --git a/feature-profile/src/androidMain/kotlin/com/codandotv/streamplayerapp/profile/presentation/widget/ComposeExtensions.kt b/feature-profile/src/commonMain/kotlin/com/codandotv/streamplayerapp/profile/presentation/widget/ComposeExtensions.kt similarity index 100% rename from feature-profile/src/androidMain/kotlin/com/codandotv/streamplayerapp/profile/presentation/widget/ComposeExtensions.kt rename to feature-profile/src/commonMain/kotlin/com/codandotv/streamplayerapp/profile/presentation/widget/ComposeExtensions.kt diff --git a/feature-profile/src/androidMain/kotlin/com/codandotv/streamplayerapp/profile/presentation/widget/ProfilePickerOpacityLayer.kt b/feature-profile/src/commonMain/kotlin/com/codandotv/streamplayerapp/profile/presentation/widget/ProfilePickerOpacityLayer.kt similarity index 100% rename from feature-profile/src/androidMain/kotlin/com/codandotv/streamplayerapp/profile/presentation/widget/ProfilePickerOpacityLayer.kt rename to feature-profile/src/commonMain/kotlin/com/codandotv/streamplayerapp/profile/presentation/widget/ProfilePickerOpacityLayer.kt diff --git a/feature-profile/src/androidMain/kotlin/com/codandotv/streamplayerapp/profile/presentation/widget/ProfilePickerProfilesGrid.kt b/feature-profile/src/commonMain/kotlin/com/codandotv/streamplayerapp/profile/presentation/widget/ProfilePickerProfilesGrid.kt similarity index 100% rename from feature-profile/src/androidMain/kotlin/com/codandotv/streamplayerapp/profile/presentation/widget/ProfilePickerProfilesGrid.kt rename to feature-profile/src/commonMain/kotlin/com/codandotv/streamplayerapp/profile/presentation/widget/ProfilePickerProfilesGrid.kt diff --git a/feature-profile/src/androidMain/kotlin/com/codandotv/streamplayerapp/profile/presentation/widget/ProfilePickerSelectedProfileContainer.kt b/feature-profile/src/commonMain/kotlin/com/codandotv/streamplayerapp/profile/presentation/widget/ProfilePickerSelectedProfileContainer.kt similarity index 100% rename from feature-profile/src/androidMain/kotlin/com/codandotv/streamplayerapp/profile/presentation/widget/ProfilePickerSelectedProfileContainer.kt rename to feature-profile/src/commonMain/kotlin/com/codandotv/streamplayerapp/profile/presentation/widget/ProfilePickerSelectedProfileContainer.kt diff --git a/feature-profile/src/androidMain/kotlin/com/codandotv/streamplayerapp/profile/presentation/widget/ProfilePickerStreamLoad.kt b/feature-profile/src/commonMain/kotlin/com/codandotv/streamplayerapp/profile/presentation/widget/ProfilePickerStreamLoad.kt similarity index 100% rename from feature-profile/src/androidMain/kotlin/com/codandotv/streamplayerapp/profile/presentation/widget/ProfilePickerStreamLoad.kt rename to feature-profile/src/commonMain/kotlin/com/codandotv/streamplayerapp/profile/presentation/widget/ProfilePickerStreamLoad.kt diff --git a/feature-profile/src/androidMain/kotlin/com/codandotv/streamplayerapp/profile/presentation/widget/ProfilePickerStreamToolbar.kt b/feature-profile/src/commonMain/kotlin/com/codandotv/streamplayerapp/profile/presentation/widget/ProfilePickerStreamToolbar.kt similarity index 100% rename from feature-profile/src/androidMain/kotlin/com/codandotv/streamplayerapp/profile/presentation/widget/ProfilePickerStreamToolbar.kt rename to feature-profile/src/commonMain/kotlin/com/codandotv/streamplayerapp/profile/presentation/widget/ProfilePickerStreamToolbar.kt From 65cb71983bb0ffb19d203227dd7e642a3445ba03 Mon Sep 17 00:00:00 2001 From: Rods Date: Mon, 27 Jan 2025 17:07:03 -0300 Subject: [PATCH 36/96] [ISSUE-6] - search and app AndroidMain to commonMain --- composeApp/build.gradle.kts | 4 +--- .../com.codandotv.streamplayerapp}/CustomApplication.kt | 0 .../kotlin/com.codandotv.streamplayerapp}/MainActivity.kt | 0 .../kotlin/com.codandotv.streamplayerapp}/di/AppModule.kt | 0 .../navigation/NavigationGraph.kt | 2 +- .../presentation/navigation/SplashNavigation.kt | 4 ++-- .../presentation/screens/SplashScreen.kt | 2 +- feature-search/build.gradle.kts | 4 ---- .../feature_search/data/api/MostPopularMoviesService.kt | 0 .../feature_search/data/api/SearchStreamService.kt | 0 .../data/datasource/MostPopularMoviesDataSource.kt | 0 .../feature_search/data/datasource/SearchStreamDataSource.kt | 0 .../feature_search/data/model/ListSearchStreamResponse.kt | 0 .../data/repository/MostPopularMoviesRepository.kt | 0 .../feature_search/data/repository/SearchStreamRepository.kt | 0 .../streamplayerapp/feature_search/di/SearchModule.kt | 0 .../feature_search/domain/MostPopularMoviesUseCase.kt | 0 .../streamplayerapp/feature_search/domain/SearchUseCase.kt | 0 .../feature_search/domain/mapper/SearchMapper.kt | 0 .../presentation/navigation/SearchStreamNavigation.kt | 0 .../feature_search/presentation/screens/SearchScreen.kt | 0 .../feature_search/presentation/screens/SearchUIState.kt | 0 .../feature_search/presentation/screens/SearchViewModel.kt | 0 .../feature_search/presentation/widgets/SearchCarousel.kt | 0 .../feature_search/presentation/widgets/SearchStreamCard.kt | 0 .../feature_search/presentation/widgets/SearchStreams.kt | 0 26 files changed, 5 insertions(+), 11 deletions(-) rename composeApp/src/{androidMain/kotlin/com/codandotv/streamplayerapp => commonMain/kotlin/com.codandotv.streamplayerapp}/CustomApplication.kt (100%) rename composeApp/src/{androidMain/kotlin/com/codandotv/streamplayerapp => commonMain/kotlin/com.codandotv.streamplayerapp}/MainActivity.kt (100%) rename composeApp/src/{androidMain/kotlin/com/codandotv/streamplayerapp => commonMain/kotlin/com.codandotv.streamplayerapp}/di/AppModule.kt (100%) rename composeApp/src/{androidMain/kotlin/com/codandotv/streamplayerapp => commonMain/kotlin/com.codandotv.streamplayerapp}/navigation/NavigationGraph.kt (96%) rename composeApp/src/{androidMain/kotlin/com/codandotv/streamplayerapp/splah => commonMain/kotlin/com.codandotv.streamplayerapp}/presentation/navigation/SplashNavigation.kt (77%) rename composeApp/src/{androidMain/kotlin/com/codandotv/streamplayerapp/splah => commonMain/kotlin/com.codandotv.streamplayerapp}/presentation/screens/SplashScreen.kt (96%) rename feature-search/src/{androidMain => commonMain}/kotlin/com/codandotv/streamplayerapp/feature_search/data/api/MostPopularMoviesService.kt (100%) rename feature-search/src/{androidMain => commonMain}/kotlin/com/codandotv/streamplayerapp/feature_search/data/api/SearchStreamService.kt (100%) rename feature-search/src/{androidMain => commonMain}/kotlin/com/codandotv/streamplayerapp/feature_search/data/datasource/MostPopularMoviesDataSource.kt (100%) rename feature-search/src/{androidMain => commonMain}/kotlin/com/codandotv/streamplayerapp/feature_search/data/datasource/SearchStreamDataSource.kt (100%) rename feature-search/src/{androidMain => commonMain}/kotlin/com/codandotv/streamplayerapp/feature_search/data/model/ListSearchStreamResponse.kt (100%) rename feature-search/src/{androidMain => commonMain}/kotlin/com/codandotv/streamplayerapp/feature_search/data/repository/MostPopularMoviesRepository.kt (100%) rename feature-search/src/{androidMain => commonMain}/kotlin/com/codandotv/streamplayerapp/feature_search/data/repository/SearchStreamRepository.kt (100%) rename feature-search/src/{androidMain => commonMain}/kotlin/com/codandotv/streamplayerapp/feature_search/di/SearchModule.kt (100%) rename feature-search/src/{androidMain => commonMain}/kotlin/com/codandotv/streamplayerapp/feature_search/domain/MostPopularMoviesUseCase.kt (100%) rename feature-search/src/{androidMain => commonMain}/kotlin/com/codandotv/streamplayerapp/feature_search/domain/SearchUseCase.kt (100%) rename feature-search/src/{androidMain => commonMain}/kotlin/com/codandotv/streamplayerapp/feature_search/domain/mapper/SearchMapper.kt (100%) rename feature-search/src/{androidMain => commonMain}/kotlin/com/codandotv/streamplayerapp/feature_search/presentation/navigation/SearchStreamNavigation.kt (100%) rename feature-search/src/{androidMain => commonMain}/kotlin/com/codandotv/streamplayerapp/feature_search/presentation/screens/SearchScreen.kt (100%) rename feature-search/src/{androidMain => commonMain}/kotlin/com/codandotv/streamplayerapp/feature_search/presentation/screens/SearchUIState.kt (100%) rename feature-search/src/{androidMain => commonMain}/kotlin/com/codandotv/streamplayerapp/feature_search/presentation/screens/SearchViewModel.kt (100%) rename feature-search/src/{androidMain => commonMain}/kotlin/com/codandotv/streamplayerapp/feature_search/presentation/widgets/SearchCarousel.kt (100%) rename feature-search/src/{androidMain => commonMain}/kotlin/com/codandotv/streamplayerapp/feature_search/presentation/widgets/SearchStreamCard.kt (100%) rename feature-search/src/{androidMain => commonMain}/kotlin/com/codandotv/streamplayerapp/feature_search/presentation/widgets/SearchStreams.kt (100%) diff --git a/composeApp/build.gradle.kts b/composeApp/build.gradle.kts index 658249ca..ee1a1a8c 100644 --- a/composeApp/build.gradle.kts +++ b/composeApp/build.gradle.kts @@ -7,7 +7,7 @@ plugins { } kotlin { sourceSets { - androidMain.dependencies { + commonMain.dependencies { implementation(projects.featureListStreams) implementation(projects.featureDetail) implementation(projects.featureSearch) @@ -25,8 +25,6 @@ kotlin { implementation(libs.bundles.koin) implementation(libs.bundles.androidSupport) implementation(libs.bundles.kotlin) - - implementation(libs.lottie) implementation(libs.lottie) } } diff --git a/composeApp/src/androidMain/kotlin/com/codandotv/streamplayerapp/CustomApplication.kt b/composeApp/src/commonMain/kotlin/com.codandotv.streamplayerapp/CustomApplication.kt similarity index 100% rename from composeApp/src/androidMain/kotlin/com/codandotv/streamplayerapp/CustomApplication.kt rename to composeApp/src/commonMain/kotlin/com.codandotv.streamplayerapp/CustomApplication.kt diff --git a/composeApp/src/androidMain/kotlin/com/codandotv/streamplayerapp/MainActivity.kt b/composeApp/src/commonMain/kotlin/com.codandotv.streamplayerapp/MainActivity.kt similarity index 100% rename from composeApp/src/androidMain/kotlin/com/codandotv/streamplayerapp/MainActivity.kt rename to composeApp/src/commonMain/kotlin/com.codandotv.streamplayerapp/MainActivity.kt diff --git a/composeApp/src/androidMain/kotlin/com/codandotv/streamplayerapp/di/AppModule.kt b/composeApp/src/commonMain/kotlin/com.codandotv.streamplayerapp/di/AppModule.kt similarity index 100% rename from composeApp/src/androidMain/kotlin/com/codandotv/streamplayerapp/di/AppModule.kt rename to composeApp/src/commonMain/kotlin/com.codandotv.streamplayerapp/di/AppModule.kt diff --git a/composeApp/src/androidMain/kotlin/com/codandotv/streamplayerapp/navigation/NavigationGraph.kt b/composeApp/src/commonMain/kotlin/com.codandotv.streamplayerapp/navigation/NavigationGraph.kt similarity index 96% rename from composeApp/src/androidMain/kotlin/com/codandotv/streamplayerapp/navigation/NavigationGraph.kt rename to composeApp/src/commonMain/kotlin/com.codandotv.streamplayerapp/navigation/NavigationGraph.kt index 192ab17b..fa57d367 100644 --- a/composeApp/src/androidMain/kotlin/com/codandotv/streamplayerapp/navigation/NavigationGraph.kt +++ b/composeApp/src/commonMain/kotlin/com.codandotv.streamplayerapp/navigation/NavigationGraph.kt @@ -17,7 +17,7 @@ import com.codandotv.streamplayerapp.core_navigation.routes.Routes import com.codandotv.streamplayerapp.feature_list_streams.list.presentation.navigation.listStreamsNavGraph import com.codandotv.streamplayerapp.profile.presentation.navigation.profilePickerStreamNavGraph -import com.codandotv.streamplayerapp.splah.presentation.navigation.splashNavGraph +import com.codandotv.streamplayerapp.presentation.navigation.splashNavGraph import com.codandotv.streamplayerapp.feature_detail.presentation.navigation.detailStreamNavGraph import com.codandotv.streamplayerapp.feature_search.presentation.navigation.searchStreamsNavGraph diff --git a/composeApp/src/androidMain/kotlin/com/codandotv/streamplayerapp/splah/presentation/navigation/SplashNavigation.kt b/composeApp/src/commonMain/kotlin/com.codandotv.streamplayerapp/presentation/navigation/SplashNavigation.kt similarity index 77% rename from composeApp/src/androidMain/kotlin/com/codandotv/streamplayerapp/splah/presentation/navigation/SplashNavigation.kt rename to composeApp/src/commonMain/kotlin/com.codandotv.streamplayerapp/presentation/navigation/SplashNavigation.kt index a272c263..9952dc97 100644 --- a/composeApp/src/androidMain/kotlin/com/codandotv/streamplayerapp/splah/presentation/navigation/SplashNavigation.kt +++ b/composeApp/src/commonMain/kotlin/com.codandotv.streamplayerapp/presentation/navigation/SplashNavigation.kt @@ -1,11 +1,11 @@ -package com.codandotv.streamplayerapp.splah.presentation.navigation +package com.codandotv.streamplayerapp.presentation.navigation import androidx.navigation.NavGraphBuilder import androidx.navigation.NavHostController import androidx.navigation.compose.composable import com.codandotv.streamplayerapp.core_navigation.routes.BottomNavRoutes import com.codandotv.streamplayerapp.core_navigation.routes.Routes -import com.codandotv.streamplayerapp.splah.presentation.screens.SplashScreen +import com.codandotv.streamplayerapp.presentation.screens.SplashScreen fun NavGraphBuilder.splashNavGraph(navController: NavHostController) { composable(Routes.Splash) { diff --git a/composeApp/src/androidMain/kotlin/com/codandotv/streamplayerapp/splah/presentation/screens/SplashScreen.kt b/composeApp/src/commonMain/kotlin/com.codandotv.streamplayerapp/presentation/screens/SplashScreen.kt similarity index 96% rename from composeApp/src/androidMain/kotlin/com/codandotv/streamplayerapp/splah/presentation/screens/SplashScreen.kt rename to composeApp/src/commonMain/kotlin/com.codandotv.streamplayerapp/presentation/screens/SplashScreen.kt index f08c7848..97aa353b 100644 --- a/composeApp/src/androidMain/kotlin/com/codandotv/streamplayerapp/splah/presentation/screens/SplashScreen.kt +++ b/composeApp/src/commonMain/kotlin/com.codandotv.streamplayerapp/presentation/screens/SplashScreen.kt @@ -1,4 +1,4 @@ -package com.codandotv.streamplayerapp.splah.presentation.screens +package com.codandotv.streamplayerapp.presentation.screens import androidx.compose.foundation.background import androidx.compose.foundation.layout.Box diff --git a/feature-search/build.gradle.kts b/feature-search/build.gradle.kts index cc103e72..800182de 100644 --- a/feature-search/build.gradle.kts +++ b/feature-search/build.gradle.kts @@ -12,9 +12,6 @@ kotlin { commonMain.dependencies { implementation(libs.koin.annotations) implementation(libs.koin.core) - } - - androidMain.dependencies { implementation(projects.coreNetworking) implementation(projects.coreNavigation) implementation(projects.coreShared) @@ -31,7 +28,6 @@ kotlin { implementation(libs.coil) implementation(libs.koin.annotations) implementation(libs.bundles.androidSupport) - } } } diff --git a/feature-search/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_search/data/api/MostPopularMoviesService.kt b/feature-search/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_search/data/api/MostPopularMoviesService.kt similarity index 100% rename from feature-search/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_search/data/api/MostPopularMoviesService.kt rename to feature-search/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_search/data/api/MostPopularMoviesService.kt diff --git a/feature-search/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_search/data/api/SearchStreamService.kt b/feature-search/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_search/data/api/SearchStreamService.kt similarity index 100% rename from feature-search/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_search/data/api/SearchStreamService.kt rename to feature-search/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_search/data/api/SearchStreamService.kt diff --git a/feature-search/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_search/data/datasource/MostPopularMoviesDataSource.kt b/feature-search/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_search/data/datasource/MostPopularMoviesDataSource.kt similarity index 100% rename from feature-search/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_search/data/datasource/MostPopularMoviesDataSource.kt rename to feature-search/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_search/data/datasource/MostPopularMoviesDataSource.kt diff --git a/feature-search/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_search/data/datasource/SearchStreamDataSource.kt b/feature-search/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_search/data/datasource/SearchStreamDataSource.kt similarity index 100% rename from feature-search/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_search/data/datasource/SearchStreamDataSource.kt rename to feature-search/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_search/data/datasource/SearchStreamDataSource.kt diff --git a/feature-search/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_search/data/model/ListSearchStreamResponse.kt b/feature-search/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_search/data/model/ListSearchStreamResponse.kt similarity index 100% rename from feature-search/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_search/data/model/ListSearchStreamResponse.kt rename to feature-search/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_search/data/model/ListSearchStreamResponse.kt diff --git a/feature-search/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_search/data/repository/MostPopularMoviesRepository.kt b/feature-search/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_search/data/repository/MostPopularMoviesRepository.kt similarity index 100% rename from feature-search/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_search/data/repository/MostPopularMoviesRepository.kt rename to feature-search/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_search/data/repository/MostPopularMoviesRepository.kt diff --git a/feature-search/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_search/data/repository/SearchStreamRepository.kt b/feature-search/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_search/data/repository/SearchStreamRepository.kt similarity index 100% rename from feature-search/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_search/data/repository/SearchStreamRepository.kt rename to feature-search/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_search/data/repository/SearchStreamRepository.kt diff --git a/feature-search/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_search/di/SearchModule.kt b/feature-search/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_search/di/SearchModule.kt similarity index 100% rename from feature-search/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_search/di/SearchModule.kt rename to feature-search/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_search/di/SearchModule.kt diff --git a/feature-search/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_search/domain/MostPopularMoviesUseCase.kt b/feature-search/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_search/domain/MostPopularMoviesUseCase.kt similarity index 100% rename from feature-search/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_search/domain/MostPopularMoviesUseCase.kt rename to feature-search/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_search/domain/MostPopularMoviesUseCase.kt diff --git a/feature-search/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_search/domain/SearchUseCase.kt b/feature-search/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_search/domain/SearchUseCase.kt similarity index 100% rename from feature-search/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_search/domain/SearchUseCase.kt rename to feature-search/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_search/domain/SearchUseCase.kt diff --git a/feature-search/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_search/domain/mapper/SearchMapper.kt b/feature-search/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_search/domain/mapper/SearchMapper.kt similarity index 100% rename from feature-search/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_search/domain/mapper/SearchMapper.kt rename to feature-search/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_search/domain/mapper/SearchMapper.kt diff --git a/feature-search/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_search/presentation/navigation/SearchStreamNavigation.kt b/feature-search/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_search/presentation/navigation/SearchStreamNavigation.kt similarity index 100% rename from feature-search/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_search/presentation/navigation/SearchStreamNavigation.kt rename to feature-search/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_search/presentation/navigation/SearchStreamNavigation.kt diff --git a/feature-search/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_search/presentation/screens/SearchScreen.kt b/feature-search/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_search/presentation/screens/SearchScreen.kt similarity index 100% rename from feature-search/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_search/presentation/screens/SearchScreen.kt rename to feature-search/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_search/presentation/screens/SearchScreen.kt diff --git a/feature-search/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_search/presentation/screens/SearchUIState.kt b/feature-search/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_search/presentation/screens/SearchUIState.kt similarity index 100% rename from feature-search/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_search/presentation/screens/SearchUIState.kt rename to feature-search/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_search/presentation/screens/SearchUIState.kt diff --git a/feature-search/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_search/presentation/screens/SearchViewModel.kt b/feature-search/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_search/presentation/screens/SearchViewModel.kt similarity index 100% rename from feature-search/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_search/presentation/screens/SearchViewModel.kt rename to feature-search/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_search/presentation/screens/SearchViewModel.kt diff --git a/feature-search/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_search/presentation/widgets/SearchCarousel.kt b/feature-search/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_search/presentation/widgets/SearchCarousel.kt similarity index 100% rename from feature-search/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_search/presentation/widgets/SearchCarousel.kt rename to feature-search/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_search/presentation/widgets/SearchCarousel.kt diff --git a/feature-search/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_search/presentation/widgets/SearchStreamCard.kt b/feature-search/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_search/presentation/widgets/SearchStreamCard.kt similarity index 100% rename from feature-search/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_search/presentation/widgets/SearchStreamCard.kt rename to feature-search/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_search/presentation/widgets/SearchStreamCard.kt diff --git a/feature-search/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_search/presentation/widgets/SearchStreams.kt b/feature-search/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_search/presentation/widgets/SearchStreams.kt similarity index 100% rename from feature-search/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_search/presentation/widgets/SearchStreams.kt rename to feature-search/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_search/presentation/widgets/SearchStreams.kt From 2c703a1fdf2cbb99277579ae0277f24e7c20702d Mon Sep 17 00:00:00 2001 From: Gabriel Bronzatti Moro Date: Tue, 28 Jan 2025 08:00:54 -0300 Subject: [PATCH 37/96] Migrate paging library to KMP --- core-shared-ui/build.gradle.kts | 5 ++++- feature-detail/build.gradle.kts | 1 - feature-list-streams/build.gradle.kts | 4 +++- feature-search/build.gradle.kts | 4 +++- 4 files changed, 10 insertions(+), 4 deletions(-) diff --git a/core-shared-ui/build.gradle.kts b/core-shared-ui/build.gradle.kts index 5e12fe36..e3000310 100644 --- a/core-shared-ui/build.gradle.kts +++ b/core-shared-ui/build.gradle.kts @@ -17,9 +17,12 @@ kotlin { implementation(libs.bundles.kotlin) implementation(libs.bundles.androidSupport) implementation(libs.android.youtube.player) - implementation(libs.paging.compose) implementation(libs.coil) } + + commonMain.dependencies { + implementation(libs.paging.compose) + } } } diff --git a/feature-detail/build.gradle.kts b/feature-detail/build.gradle.kts index 5f4648bf..2ccc31a9 100644 --- a/feature-detail/build.gradle.kts +++ b/feature-detail/build.gradle.kts @@ -24,7 +24,6 @@ kotlin { implementation(compose.material3) implementation(compose.ui) implementation(compose.preview) - implementation(libs.paging.compose) implementation(libs.navigation.compose) implementation(libs.bundles.koin) implementation(libs.bundles.networking) diff --git a/feature-list-streams/build.gradle.kts b/feature-list-streams/build.gradle.kts index cc103e72..5f08c9f9 100644 --- a/feature-list-streams/build.gradle.kts +++ b/feature-list-streams/build.gradle.kts @@ -24,14 +24,16 @@ kotlin { implementation(compose.material3) implementation(compose.ui) implementation(compose.preview) - implementation(libs.paging.compose) implementation(libs.navigation.compose) implementation(libs.bundles.koin) implementation(libs.bundles.networking) implementation(libs.coil) implementation(libs.koin.annotations) implementation(libs.bundles.androidSupport) + } + commonMain.dependencies { + implementation(libs.paging.compose) } } } diff --git a/feature-search/build.gradle.kts b/feature-search/build.gradle.kts index cc103e72..5f08c9f9 100644 --- a/feature-search/build.gradle.kts +++ b/feature-search/build.gradle.kts @@ -24,14 +24,16 @@ kotlin { implementation(compose.material3) implementation(compose.ui) implementation(compose.preview) - implementation(libs.paging.compose) implementation(libs.navigation.compose) implementation(libs.bundles.koin) implementation(libs.bundles.networking) implementation(libs.coil) implementation(libs.koin.annotations) implementation(libs.bundles.androidSupport) + } + commonMain.dependencies { + implementation(libs.paging.compose) } } } From 077cad6c418ad1d3f40a00ff1efa89a1264f43c4 Mon Sep 17 00:00:00 2001 From: Gabriel Bronzatti Moro Date: Tue, 28 Jan 2025 08:27:45 -0300 Subject: [PATCH 38/96] Using existing commonMain block --- feature-list-streams/build.gradle.kts | 5 +---- feature-search/build.gradle.kts | 5 +---- 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/feature-list-streams/build.gradle.kts b/feature-list-streams/build.gradle.kts index 5f08c9f9..e7061869 100644 --- a/feature-list-streams/build.gradle.kts +++ b/feature-list-streams/build.gradle.kts @@ -12,6 +12,7 @@ kotlin { commonMain.dependencies { implementation(libs.koin.annotations) implementation(libs.koin.core) + implementation(libs.paging.compose) } androidMain.dependencies { @@ -31,10 +32,6 @@ kotlin { implementation(libs.koin.annotations) implementation(libs.bundles.androidSupport) } - - commonMain.dependencies { - implementation(libs.paging.compose) - } } } diff --git a/feature-search/build.gradle.kts b/feature-search/build.gradle.kts index 5f08c9f9..083414c8 100644 --- a/feature-search/build.gradle.kts +++ b/feature-search/build.gradle.kts @@ -10,6 +10,7 @@ plugins { kotlin { sourceSets { commonMain.dependencies { + implementation(libs.paging.compose) implementation(libs.koin.annotations) implementation(libs.koin.core) } @@ -31,10 +32,6 @@ kotlin { implementation(libs.koin.annotations) implementation(libs.bundles.androidSupport) } - - commonMain.dependencies { - implementation(libs.paging.compose) - } } } From 19d05a36b01e2308b12314ba5589ee2e0be2a8c6 Mon Sep 17 00:00:00 2001 From: Rods Date: Tue, 28 Jan 2025 18:28:14 -0300 Subject: [PATCH 39/96] [ISSUE-6] - commonMain AndroidMain to commonMain --- core-shared/build.gradle.kts | 2 +- .../kotlin/com/codandotv/streamplayerapp/core_shared/Url.kt | 0 .../codandotv/streamplayerapp/core_shared/extension/ErrorExt.kt | 0 .../streamplayerapp/core_shared/extension/String.Ext.kt | 0 .../codandotv/streamplayerapp/core_shared/extension/UriExt.kt | 0 .../core_shared/qualifier/QualifierDispatcherIO.kt | 0 6 files changed, 1 insertion(+), 1 deletion(-) rename core-shared/src/{androidMain => commonMain}/kotlin/com/codandotv/streamplayerapp/core_shared/Url.kt (100%) rename core-shared/src/{androidMain => commonMain}/kotlin/com/codandotv/streamplayerapp/core_shared/extension/ErrorExt.kt (100%) rename core-shared/src/{androidMain => commonMain}/kotlin/com/codandotv/streamplayerapp/core_shared/extension/String.Ext.kt (100%) rename core-shared/src/{androidMain => commonMain}/kotlin/com/codandotv/streamplayerapp/core_shared/extension/UriExt.kt (100%) rename core-shared/src/{androidMain => commonMain}/kotlin/com/codandotv/streamplayerapp/core_shared/qualifier/QualifierDispatcherIO.kt (100%) diff --git a/core-shared/build.gradle.kts b/core-shared/build.gradle.kts index b10865f8..e4ee2dcd 100644 --- a/core-shared/build.gradle.kts +++ b/core-shared/build.gradle.kts @@ -5,7 +5,7 @@ plugins { kotlin { sourceSets { - androidMain.dependencies { + commonMain.dependencies { implementation(libs.bundles.koin) } } diff --git a/core-shared/src/androidMain/kotlin/com/codandotv/streamplayerapp/core_shared/Url.kt b/core-shared/src/commonMain/kotlin/com/codandotv/streamplayerapp/core_shared/Url.kt similarity index 100% rename from core-shared/src/androidMain/kotlin/com/codandotv/streamplayerapp/core_shared/Url.kt rename to core-shared/src/commonMain/kotlin/com/codandotv/streamplayerapp/core_shared/Url.kt diff --git a/core-shared/src/androidMain/kotlin/com/codandotv/streamplayerapp/core_shared/extension/ErrorExt.kt b/core-shared/src/commonMain/kotlin/com/codandotv/streamplayerapp/core_shared/extension/ErrorExt.kt similarity index 100% rename from core-shared/src/androidMain/kotlin/com/codandotv/streamplayerapp/core_shared/extension/ErrorExt.kt rename to core-shared/src/commonMain/kotlin/com/codandotv/streamplayerapp/core_shared/extension/ErrorExt.kt diff --git a/core-shared/src/androidMain/kotlin/com/codandotv/streamplayerapp/core_shared/extension/String.Ext.kt b/core-shared/src/commonMain/kotlin/com/codandotv/streamplayerapp/core_shared/extension/String.Ext.kt similarity index 100% rename from core-shared/src/androidMain/kotlin/com/codandotv/streamplayerapp/core_shared/extension/String.Ext.kt rename to core-shared/src/commonMain/kotlin/com/codandotv/streamplayerapp/core_shared/extension/String.Ext.kt diff --git a/core-shared/src/androidMain/kotlin/com/codandotv/streamplayerapp/core_shared/extension/UriExt.kt b/core-shared/src/commonMain/kotlin/com/codandotv/streamplayerapp/core_shared/extension/UriExt.kt similarity index 100% rename from core-shared/src/androidMain/kotlin/com/codandotv/streamplayerapp/core_shared/extension/UriExt.kt rename to core-shared/src/commonMain/kotlin/com/codandotv/streamplayerapp/core_shared/extension/UriExt.kt diff --git a/core-shared/src/androidMain/kotlin/com/codandotv/streamplayerapp/core_shared/qualifier/QualifierDispatcherIO.kt b/core-shared/src/commonMain/kotlin/com/codandotv/streamplayerapp/core_shared/qualifier/QualifierDispatcherIO.kt similarity index 100% rename from core-shared/src/androidMain/kotlin/com/codandotv/streamplayerapp/core_shared/qualifier/QualifierDispatcherIO.kt rename to core-shared/src/commonMain/kotlin/com/codandotv/streamplayerapp/core_shared/qualifier/QualifierDispatcherIO.kt From e3a4801eee81942d078586f8c0de68eec5db0cc9 Mon Sep 17 00:00:00 2001 From: Rods Date: Wed, 29 Jan 2025 09:17:05 -0300 Subject: [PATCH 40/96] [ISSUE-6] - networking AndroidMain to commonMain --- .../codandotv/streamplayerapp/core_networking/di/NetworkModule.kt | 0 .../streamplayerapp/core_networking/di/QualifierNetworking.kt | 0 .../streamplayerapp/core_networking/handleError/Failure.kt | 0 .../core_networking/handleError/HttpClientConfigExtensions.kt | 0 .../core_networking/handleError/NetworkResponse.kt | 0 .../core_networking/handleError/ResultExtensions.kt | 0 6 files changed, 0 insertions(+), 0 deletions(-) rename core-networking/src/{androidMain => commonMain}/kotlin/com/codandotv/streamplayerapp/core_networking/di/NetworkModule.kt (100%) rename core-networking/src/{androidMain => commonMain}/kotlin/com/codandotv/streamplayerapp/core_networking/di/QualifierNetworking.kt (100%) rename core-networking/src/{androidMain => commonMain}/kotlin/com/codandotv/streamplayerapp/core_networking/handleError/Failure.kt (100%) rename core-networking/src/{androidMain => commonMain}/kotlin/com/codandotv/streamplayerapp/core_networking/handleError/HttpClientConfigExtensions.kt (100%) rename core-networking/src/{androidMain => commonMain}/kotlin/com/codandotv/streamplayerapp/core_networking/handleError/NetworkResponse.kt (100%) rename core-networking/src/{androidMain => commonMain}/kotlin/com/codandotv/streamplayerapp/core_networking/handleError/ResultExtensions.kt (100%) diff --git a/core-networking/src/androidMain/kotlin/com/codandotv/streamplayerapp/core_networking/di/NetworkModule.kt b/core-networking/src/commonMain/kotlin/com/codandotv/streamplayerapp/core_networking/di/NetworkModule.kt similarity index 100% rename from core-networking/src/androidMain/kotlin/com/codandotv/streamplayerapp/core_networking/di/NetworkModule.kt rename to core-networking/src/commonMain/kotlin/com/codandotv/streamplayerapp/core_networking/di/NetworkModule.kt diff --git a/core-networking/src/androidMain/kotlin/com/codandotv/streamplayerapp/core_networking/di/QualifierNetworking.kt b/core-networking/src/commonMain/kotlin/com/codandotv/streamplayerapp/core_networking/di/QualifierNetworking.kt similarity index 100% rename from core-networking/src/androidMain/kotlin/com/codandotv/streamplayerapp/core_networking/di/QualifierNetworking.kt rename to core-networking/src/commonMain/kotlin/com/codandotv/streamplayerapp/core_networking/di/QualifierNetworking.kt diff --git a/core-networking/src/androidMain/kotlin/com/codandotv/streamplayerapp/core_networking/handleError/Failure.kt b/core-networking/src/commonMain/kotlin/com/codandotv/streamplayerapp/core_networking/handleError/Failure.kt similarity index 100% rename from core-networking/src/androidMain/kotlin/com/codandotv/streamplayerapp/core_networking/handleError/Failure.kt rename to core-networking/src/commonMain/kotlin/com/codandotv/streamplayerapp/core_networking/handleError/Failure.kt diff --git a/core-networking/src/androidMain/kotlin/com/codandotv/streamplayerapp/core_networking/handleError/HttpClientConfigExtensions.kt b/core-networking/src/commonMain/kotlin/com/codandotv/streamplayerapp/core_networking/handleError/HttpClientConfigExtensions.kt similarity index 100% rename from core-networking/src/androidMain/kotlin/com/codandotv/streamplayerapp/core_networking/handleError/HttpClientConfigExtensions.kt rename to core-networking/src/commonMain/kotlin/com/codandotv/streamplayerapp/core_networking/handleError/HttpClientConfigExtensions.kt diff --git a/core-networking/src/androidMain/kotlin/com/codandotv/streamplayerapp/core_networking/handleError/NetworkResponse.kt b/core-networking/src/commonMain/kotlin/com/codandotv/streamplayerapp/core_networking/handleError/NetworkResponse.kt similarity index 100% rename from core-networking/src/androidMain/kotlin/com/codandotv/streamplayerapp/core_networking/handleError/NetworkResponse.kt rename to core-networking/src/commonMain/kotlin/com/codandotv/streamplayerapp/core_networking/handleError/NetworkResponse.kt diff --git a/core-networking/src/androidMain/kotlin/com/codandotv/streamplayerapp/core_networking/handleError/ResultExtensions.kt b/core-networking/src/commonMain/kotlin/com/codandotv/streamplayerapp/core_networking/handleError/ResultExtensions.kt similarity index 100% rename from core-networking/src/androidMain/kotlin/com/codandotv/streamplayerapp/core_networking/handleError/ResultExtensions.kt rename to core-networking/src/commonMain/kotlin/com/codandotv/streamplayerapp/core_networking/handleError/ResultExtensions.kt From 5f33c4b19251d1b7ba01f1d01273ca179427b555 Mon Sep 17 00:00:00 2001 From: Gabriel Bronzatti Moro Date: Tue, 28 Jan 2025 08:00:54 -0300 Subject: [PATCH 41/96] Migrate paging library to KMP --- core-shared-ui/build.gradle.kts | 5 ++++- feature-detail/build.gradle.kts | 1 - feature-list-streams/build.gradle.kts | 4 +++- feature-search/build.gradle.kts | 6 +++--- 4 files changed, 10 insertions(+), 6 deletions(-) diff --git a/core-shared-ui/build.gradle.kts b/core-shared-ui/build.gradle.kts index 5e12fe36..e3000310 100644 --- a/core-shared-ui/build.gradle.kts +++ b/core-shared-ui/build.gradle.kts @@ -17,9 +17,12 @@ kotlin { implementation(libs.bundles.kotlin) implementation(libs.bundles.androidSupport) implementation(libs.android.youtube.player) - implementation(libs.paging.compose) implementation(libs.coil) } + + commonMain.dependencies { + implementation(libs.paging.compose) + } } } diff --git a/feature-detail/build.gradle.kts b/feature-detail/build.gradle.kts index c6d6971e..92457ab6 100644 --- a/feature-detail/build.gradle.kts +++ b/feature-detail/build.gradle.kts @@ -21,7 +21,6 @@ kotlin { implementation(compose.material3) implementation(compose.ui) implementation(compose.preview) - implementation(libs.paging.compose) implementation(libs.navigation.compose) implementation(libs.bundles.koin) implementation(libs.bundles.networking) diff --git a/feature-list-streams/build.gradle.kts b/feature-list-streams/build.gradle.kts index cc103e72..5f08c9f9 100644 --- a/feature-list-streams/build.gradle.kts +++ b/feature-list-streams/build.gradle.kts @@ -24,14 +24,16 @@ kotlin { implementation(compose.material3) implementation(compose.ui) implementation(compose.preview) - implementation(libs.paging.compose) implementation(libs.navigation.compose) implementation(libs.bundles.koin) implementation(libs.bundles.networking) implementation(libs.coil) implementation(libs.koin.annotations) implementation(libs.bundles.androidSupport) + } + commonMain.dependencies { + implementation(libs.paging.compose) } } } diff --git a/feature-search/build.gradle.kts b/feature-search/build.gradle.kts index 800182de..ae1f5036 100644 --- a/feature-search/build.gradle.kts +++ b/feature-search/build.gradle.kts @@ -21,20 +21,20 @@ kotlin { implementation(compose.material3) implementation(compose.ui) implementation(compose.preview) - implementation(libs.paging.compose) implementation(libs.navigation.compose) implementation(libs.bundles.koin) implementation(libs.bundles.networking) implementation(libs.coil) implementation(libs.koin.annotations) implementation(libs.bundles.androidSupport) + implementation(libs.paging.compose) } } } dependencies { - add("kspCommonMainMetadata",libs.koin.compiler) - add("kspAndroid", libs.koin.compiler) + add("kspCommonMainMetadata",libs.koin.ksp.compiler) + add("kspAndroid", libs.koin.ksp.compiler) } ksp { From b20234301337e01cf78e39904fa183d4894f4f9a Mon Sep 17 00:00:00 2001 From: Gabriel Bronzatti Moro Date: Tue, 28 Jan 2025 08:27:45 -0300 Subject: [PATCH 42/96] Using existing commonMain block --- feature-list-streams/build.gradle.kts | 5 +---- feature-search/build.gradle.kts | 2 +- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/feature-list-streams/build.gradle.kts b/feature-list-streams/build.gradle.kts index 5f08c9f9..e7061869 100644 --- a/feature-list-streams/build.gradle.kts +++ b/feature-list-streams/build.gradle.kts @@ -12,6 +12,7 @@ kotlin { commonMain.dependencies { implementation(libs.koin.annotations) implementation(libs.koin.core) + implementation(libs.paging.compose) } androidMain.dependencies { @@ -31,10 +32,6 @@ kotlin { implementation(libs.koin.annotations) implementation(libs.bundles.androidSupport) } - - commonMain.dependencies { - implementation(libs.paging.compose) - } } } diff --git a/feature-search/build.gradle.kts b/feature-search/build.gradle.kts index ae1f5036..3016a44e 100644 --- a/feature-search/build.gradle.kts +++ b/feature-search/build.gradle.kts @@ -10,6 +10,7 @@ plugins { kotlin { sourceSets { commonMain.dependencies { + implementation(libs.paging.compose) implementation(libs.koin.annotations) implementation(libs.koin.core) implementation(projects.coreNetworking) @@ -28,7 +29,6 @@ kotlin { implementation(libs.koin.annotations) implementation(libs.bundles.androidSupport) implementation(libs.paging.compose) - } } } From a681aaa46ec5b5510a78d4d9469c46d2c861111c Mon Sep 17 00:00:00 2001 From: Rods Date: Thu, 30 Jan 2025 12:49:34 -0300 Subject: [PATCH 43/96] [ISSUE-6] - list AndroidMain to commonMain and create plugin to ksp --- .../com.streamplayer.koin-setup.gradle.kts | 53 ++++++++++++++++ .../main/java/extensions/CommonExtensions.kt | 11 +++- .../main/java/extensions/VersionCatalog.kt | 8 +++ core-local-storage/build.gradle.kts | 4 +- feature-detail/build.gradle.kts | 12 ++-- feature-list-streams/build.gradle.kts | 46 +++++--------- .../list/di/ListStreamModule.kt | 21 ------- .../feature_list_streams/core/ContentType.kt | 0 .../list/data/ListStreamRepository.kt | 2 - .../list/data/ListStreamService.kt | 0 .../list/data/StreamDataSource.kt | 0 .../list/data/model/GenresResponse.kt | 0 .../list/data/model/ListStreamResponse.kt | 0 .../list/di/ListStreamModule.kt | 63 +++++++++++++++++++ .../list/domain/GetGenresUseCase.kt | 2 - .../list/domain/GetLatestMovieUseCase.kt | 2 - .../list/domain/ListMovieUseCase.kt | 2 - .../list/domain/ListStreamAnalytics.kt | 2 - .../list/domain/ListStreamMapper.kt | 0 .../list/domain/model/Genre.kt | 0 .../list/domain/model/HighlightBanner.kt | 0 .../list/domain/model/ListStream.kt | 0 .../navigation/ListStreamsNavigation.kt | 5 +- .../screens/ListStreamViewModel.kt | 2 - .../presentation/screens/ListStreamsScreen.kt | 0 .../screens/ListStreamsUIState.kt | 0 .../presentation/widgets/HighlightBanner.kt | 0 feature-profile/build.gradle.kts | 2 +- feature-search/build.gradle.kts | 11 +++- gradle/libs.versions.toml | 2 +- 30 files changed, 171 insertions(+), 79 deletions(-) create mode 100644 build-logic/src/main/java/com.streamplayer.koin-setup.gradle.kts delete mode 100644 feature-list-streams/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_list_streams/list/di/ListStreamModule.kt rename feature-list-streams/src/{androidMain => commonMain}/kotlin/com/codandotv/streamplayerapp/feature_list_streams/core/ContentType.kt (100%) rename feature-list-streams/src/{androidMain => commonMain}/kotlin/com/codandotv/streamplayerapp/feature_list_streams/list/data/ListStreamRepository.kt (97%) rename feature-list-streams/src/{androidMain => commonMain}/kotlin/com/codandotv/streamplayerapp/feature_list_streams/list/data/ListStreamService.kt (100%) rename feature-list-streams/src/{androidMain => commonMain}/kotlin/com/codandotv/streamplayerapp/feature_list_streams/list/data/StreamDataSource.kt (100%) rename feature-list-streams/src/{androidMain => commonMain}/kotlin/com/codandotv/streamplayerapp/feature_list_streams/list/data/model/GenresResponse.kt (100%) rename feature-list-streams/src/{androidMain => commonMain}/kotlin/com/codandotv/streamplayerapp/feature_list_streams/list/data/model/ListStreamResponse.kt (100%) create mode 100644 feature-list-streams/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_list_streams/list/di/ListStreamModule.kt rename feature-list-streams/src/{androidMain => commonMain}/kotlin/com/codandotv/streamplayerapp/feature_list_streams/list/domain/GetGenresUseCase.kt (92%) rename feature-list-streams/src/{androidMain => commonMain}/kotlin/com/codandotv/streamplayerapp/feature_list_streams/list/domain/GetLatestMovieUseCase.kt (92%) rename feature-list-streams/src/{androidMain => commonMain}/kotlin/com/codandotv/streamplayerapp/feature_list_streams/list/domain/ListMovieUseCase.kt (93%) rename feature-list-streams/src/{androidMain => commonMain}/kotlin/com/codandotv/streamplayerapp/feature_list_streams/list/domain/ListStreamAnalytics.kt (75%) rename feature-list-streams/src/{androidMain => commonMain}/kotlin/com/codandotv/streamplayerapp/feature_list_streams/list/domain/ListStreamMapper.kt (100%) rename feature-list-streams/src/{androidMain => commonMain}/kotlin/com/codandotv/streamplayerapp/feature_list_streams/list/domain/model/Genre.kt (100%) rename feature-list-streams/src/{androidMain => commonMain}/kotlin/com/codandotv/streamplayerapp/feature_list_streams/list/domain/model/HighlightBanner.kt (100%) rename feature-list-streams/src/{androidMain => commonMain}/kotlin/com/codandotv/streamplayerapp/feature_list_streams/list/domain/model/ListStream.kt (100%) rename feature-list-streams/src/{androidMain => commonMain}/kotlin/com/codandotv/streamplayerapp/feature_list_streams/list/presentation/navigation/ListStreamsNavigation.kt (92%) rename feature-list-streams/src/{androidMain => commonMain}/kotlin/com/codandotv/streamplayerapp/feature_list_streams/list/presentation/screens/ListStreamViewModel.kt (98%) rename feature-list-streams/src/{androidMain => commonMain}/kotlin/com/codandotv/streamplayerapp/feature_list_streams/list/presentation/screens/ListStreamsScreen.kt (100%) rename feature-list-streams/src/{androidMain => commonMain}/kotlin/com/codandotv/streamplayerapp/feature_list_streams/list/presentation/screens/ListStreamsUIState.kt (100%) rename feature-list-streams/src/{androidMain => commonMain}/kotlin/com/codandotv/streamplayerapp/feature_list_streams/list/presentation/widgets/HighlightBanner.kt (100%) diff --git a/build-logic/src/main/java/com.streamplayer.koin-setup.gradle.kts b/build-logic/src/main/java/com.streamplayer.koin-setup.gradle.kts new file mode 100644 index 00000000..408060b4 --- /dev/null +++ b/build-logic/src/main/java/com.streamplayer.koin-setup.gradle.kts @@ -0,0 +1,53 @@ +import extensions.koinAnnotationsDependency +import extensions.koinCompiler +import extensions.koinComposeDependency +import extensions.koinCoreDependency +import extensions.libs +import org.jetbrains.kotlin.gradle.dsl.KotlinCompile + +plugins { + id("com.google.devtools.ksp") + id("kotlin-multiplatform") +} + +kotlin { + sourceSets { + commonMain { + kotlin.srcDir("build/generated/ksp/metadata/commonMain/kotlin") + + dependencies { + implementation(libs.koinCoreDependency()) + api(libs.koinAnnotationsDependency()) + api(libs.koinComposeDependency()) + } + } + } +} + +dependencies { + add("kspCommonMainMetadata", libs.koinCompiler()) + add("kspAndroid", libs.koinCompiler()) +// add("kspIosX64", libs.koin.ksp.compiler) +// add("kspIosArm64", libs.koin.ksp.compiler) +// add("kspIosSimulatorArm64", libs.koin.ksp.compiler) +} + +// WORKAROUND: ADD this dependsOn("kspCommonMainKotlinMetadata") instead of above dependencies +tasks.withType>().configureEach { + if (name != "kspCommonMainKotlinMetadata") { + dependsOn("kspCommonMainKotlinMetadata") + } +} + +afterEvaluate { + tasks.filter { + it.name.contains("SourcesJar", true) + }.forEach { + println("SourceJarTask====>${it.name}") + it.dependsOn("kspCommonMainKotlinMetadata") + } +} + +ksp { + arg("KOIN_CONFIG_CHECK", "false") +} \ No newline at end of file diff --git a/build-logic/src/main/java/extensions/CommonExtensions.kt b/build-logic/src/main/java/extensions/CommonExtensions.kt index 2f4a525b..88e2b69b 100644 --- a/build-logic/src/main/java/extensions/CommonExtensions.kt +++ b/build-logic/src/main/java/extensions/CommonExtensions.kt @@ -6,6 +6,9 @@ import Config import com.android.build.api.dsl.CommonExtension import org.gradle.api.JavaVersion import org.gradle.api.Project +import org.gradle.api.artifacts.VersionCatalog +import org.gradle.api.artifacts.VersionCatalogsExtension +import org.gradle.kotlin.dsl.getByType internal fun CommonExtension<*, *, *, *, *>.setupPackingOptions() { packaging { @@ -50,4 +53,10 @@ internal fun CommonExtension<*, *, *, *, *>.setupNameSpace(project: Project) { namespace = "${Config.applicationId}$moduleName" println(">>>> $namespace") -} \ No newline at end of file +} + +internal val Project.libs: VersionCatalog + get() { + return project.extensions.getByType() + .named("libs") + } \ No newline at end of file diff --git a/build-logic/src/main/java/extensions/VersionCatalog.kt b/build-logic/src/main/java/extensions/VersionCatalog.kt index ee317e56..fcfdaf10 100644 --- a/build-logic/src/main/java/extensions/VersionCatalog.kt +++ b/build-logic/src/main/java/extensions/VersionCatalog.kt @@ -6,3 +6,11 @@ internal fun VersionCatalog.getLibrary(library: String) = findLibrary(library).g internal fun VersionCatalog.getVersion(library: String) = findVersion(library).get() internal fun VersionCatalog.getBundle(bundle: String) = findBundle(bundle).get() +internal fun VersionCatalog.koinCoreDependency() = findLibrary("koin_core").get() + +internal fun VersionCatalog.koinAnnotationsDependency() = findLibrary("koin_annotations").get() + +internal fun VersionCatalog.koinComposeDependency() = findLibrary("koin_compose").get() + +internal fun VersionCatalog.koinCompiler() = findLibrary("koin_ksp_compiler").get() + diff --git a/core-local-storage/build.gradle.kts b/core-local-storage/build.gradle.kts index 4437ccef..c24ab094 100644 --- a/core-local-storage/build.gradle.kts +++ b/core-local-storage/build.gradle.kts @@ -14,8 +14,8 @@ kotlin { } dependencies { - add("kspCommonMainMetadata",libs.koin.compiler) - add("kspAndroid", libs.koin.compiler) + add("kspCommonMainMetadata",libs.koin.ksp.compiler) + add("kspAndroid", libs.koin.ksp.compiler) } ksp { diff --git a/feature-detail/build.gradle.kts b/feature-detail/build.gradle.kts index 92457ab6..717a1a8e 100644 --- a/feature-detail/build.gradle.kts +++ b/feature-detail/build.gradle.kts @@ -10,7 +10,7 @@ plugins { kotlin { sourceSets { commonMain.dependencies { - implementation(libs.koin.annotations) + api(libs.koin.annotations) implementation(libs.koin.core) implementation(projects.coreNetworking) implementation(projects.coreNavigation) @@ -25,7 +25,7 @@ kotlin { implementation(libs.bundles.koin) implementation(libs.bundles.networking) implementation(libs.coil) - implementation(libs.koin.annotations) + api(libs.koin.annotations) implementation(libs.bundles.androidSupport) } commonTest.dependencies { @@ -35,8 +35,12 @@ kotlin { } dependencies { - add("kspCommonMainMetadata",libs.koin.compiler) - add("kspAndroid", libs.koin.compiler) + add("kspCommonMainMetadata", libs.koin.ksp.compiler) + add("kspAndroid", libs.koin.ksp.compiler) +// add after with iOS +// add("kspIosX64", libs.koin.ksp.compiler) +// add("kspIosArm64", libs.koin.ksp.compiler) +// add("kspIosSimulatorArm64", libs.koin.ksp.compiler) } ksp { diff --git a/feature-list-streams/build.gradle.kts b/feature-list-streams/build.gradle.kts index e7061869..4909bb83 100644 --- a/feature-list-streams/build.gradle.kts +++ b/feature-list-streams/build.gradle.kts @@ -2,7 +2,6 @@ plugins { id("com.streamplayer.kmp-library") - alias(libs.plugins.ksp) alias(libs.plugins.jetbrains.compose) alias(libs.plugins.compose.compiler) } @@ -10,36 +9,21 @@ plugins { kotlin { sourceSets { commonMain.dependencies { - implementation(libs.koin.annotations) - implementation(libs.koin.core) - implementation(libs.paging.compose) - } - - androidMain.dependencies { - implementation(projects.coreNetworking) - implementation(projects.coreNavigation) - implementation(projects.coreShared) - implementation(projects.coreSharedUi) - implementation(projects.coreLocalStorage) + implementation(libs.bundles.koin) + implementation(libs.paging.compose) + implementation(projects.coreNetworking) + implementation(projects.coreNavigation) + implementation(projects.coreShared) + implementation(projects.coreSharedUi) + implementation(projects.coreLocalStorage) - implementation(compose.material3) - implementation(compose.ui) - implementation(compose.preview) - implementation(libs.navigation.compose) - implementation(libs.bundles.koin) - implementation(libs.bundles.networking) - implementation(libs.coil) - implementation(libs.koin.annotations) - implementation(libs.bundles.androidSupport) + implementation(compose.material3) + implementation(compose.ui) + implementation(compose.preview) + implementation(libs.navigation.compose) + implementation(libs.bundles.networking) + implementation(libs.coil) + implementation(libs.bundles.androidSupport) + } } - } -} - -dependencies { - add("kspCommonMainMetadata",libs.koin.compiler) - add("kspAndroid", libs.koin.compiler) -} - -ksp { - arg("KOIN_CONFIG_CHECK","true") } \ No newline at end of file diff --git a/feature-list-streams/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_list_streams/list/di/ListStreamModule.kt b/feature-list-streams/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_list_streams/list/di/ListStreamModule.kt deleted file mode 100644 index 6e300eef..00000000 --- a/feature-list-streams/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_list_streams/list/di/ListStreamModule.kt +++ /dev/null @@ -1,21 +0,0 @@ -package com.codandotv.streamplayerapp.feature_list_streams.list.di - -import com.codandotv.streamplayerapp.feature_list_streams.list.data.ListStreamService -import com.codandotv.streamplayerapp.feature_list_streams.list.data.ListStreamServiceImpl -import io.ktor.client.HttpClient -import org.koin.core.annotation.ComponentScan -import org.koin.core.annotation.Factory -import org.koin.core.annotation.Module -import org.koin.core.context.GlobalContext - -@Module -@ComponentScan("com.codandotv.streamplayerapp.feature_list_streams.list") -class ListStreamModule { - - @Factory - fun service(): ListStreamService { - val koin = GlobalContext.get() - val httpClient = koin.get() - return ListStreamServiceImpl(httpClient) - } -} \ No newline at end of file diff --git a/feature-list-streams/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_list_streams/core/ContentType.kt b/feature-list-streams/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_list_streams/core/ContentType.kt similarity index 100% rename from feature-list-streams/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_list_streams/core/ContentType.kt rename to feature-list-streams/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_list_streams/core/ContentType.kt diff --git a/feature-list-streams/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_list_streams/list/data/ListStreamRepository.kt b/feature-list-streams/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_list_streams/list/data/ListStreamRepository.kt similarity index 97% rename from feature-list-streams/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_list_streams/list/data/ListStreamRepository.kt rename to feature-list-streams/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_list_streams/list/data/ListStreamRepository.kt index 42a14d44..dce60971 100644 --- a/feature-list-streams/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_list_streams/list/data/ListStreamRepository.kt +++ b/feature-list-streams/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_list_streams/list/data/ListStreamRepository.kt @@ -10,7 +10,6 @@ import com.codandotv.streamplayerapp.feature_list_streams.list.domain.toGenres import com.codandotv.streamplayerapp.feature_list_streams.list.domain.toStream import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.map -import org.koin.core.annotation.Factory interface ListStreamRepository { suspend fun getGenres(): Flow> @@ -20,7 +19,6 @@ interface ListStreamRepository { fun loadMovies(genre: Genre): Flow> } -@Factory class ListStreamRepositoryImpl( private val service: ListStreamService, ) : ListStreamRepository { diff --git a/feature-list-streams/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_list_streams/list/data/ListStreamService.kt b/feature-list-streams/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_list_streams/list/data/ListStreamService.kt similarity index 100% rename from feature-list-streams/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_list_streams/list/data/ListStreamService.kt rename to feature-list-streams/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_list_streams/list/data/ListStreamService.kt diff --git a/feature-list-streams/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_list_streams/list/data/StreamDataSource.kt b/feature-list-streams/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_list_streams/list/data/StreamDataSource.kt similarity index 100% rename from feature-list-streams/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_list_streams/list/data/StreamDataSource.kt rename to feature-list-streams/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_list_streams/list/data/StreamDataSource.kt diff --git a/feature-list-streams/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_list_streams/list/data/model/GenresResponse.kt b/feature-list-streams/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_list_streams/list/data/model/GenresResponse.kt similarity index 100% rename from feature-list-streams/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_list_streams/list/data/model/GenresResponse.kt rename to feature-list-streams/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_list_streams/list/data/model/GenresResponse.kt diff --git a/feature-list-streams/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_list_streams/list/data/model/ListStreamResponse.kt b/feature-list-streams/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_list_streams/list/data/model/ListStreamResponse.kt similarity index 100% rename from feature-list-streams/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_list_streams/list/data/model/ListStreamResponse.kt rename to feature-list-streams/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_list_streams/list/data/model/ListStreamResponse.kt diff --git a/feature-list-streams/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_list_streams/list/di/ListStreamModule.kt b/feature-list-streams/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_list_streams/list/di/ListStreamModule.kt new file mode 100644 index 00000000..e6a3a5af --- /dev/null +++ b/feature-list-streams/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_list_streams/list/di/ListStreamModule.kt @@ -0,0 +1,63 @@ +package com.codandotv.streamplayerapp.feature_list_streams.list.di + +import com.codandotv.streamplayerapp.feature_list_streams.list.data.ListStreamRepository +import com.codandotv.streamplayerapp.feature_list_streams.list.data.ListStreamRepositoryImpl +import com.codandotv.streamplayerapp.feature_list_streams.list.data.ListStreamService +import com.codandotv.streamplayerapp.feature_list_streams.list.data.ListStreamServiceImpl +import com.codandotv.streamplayerapp.feature_list_streams.list.domain.GetGenresUseCase +import com.codandotv.streamplayerapp.feature_list_streams.list.domain.GetGenresUseCaseImpl +import com.codandotv.streamplayerapp.feature_list_streams.list.domain.GetTopRatedStream +import com.codandotv.streamplayerapp.feature_list_streams.list.domain.GetTopRatedStreamImpl +import com.codandotv.streamplayerapp.feature_list_streams.list.domain.ListStreamAnalytics +import com.codandotv.streamplayerapp.feature_list_streams.list.domain.ListStreamAnalyticsImpl +import com.codandotv.streamplayerapp.feature_list_streams.list.domain.ListStreamUseCase +import com.codandotv.streamplayerapp.feature_list_streams.list.domain.ListStreamUseCaseImpl +import com.codandotv.streamplayerapp.feature_list_streams.list.presentation.screens.ListStreamViewModel +import org.koin.androidx.viewmodel.dsl.viewModel +import org.koin.dsl.module + +object ListStreamModule { + val module = module { + viewModel { + ListStreamViewModel( + listStreams = get(), + listGenres = get(), + latestStream = get() + ) + } + + factory { + ListStreamUseCaseImpl( + repository = get() + ) + } + + factory { + GetGenresUseCaseImpl( + repository = get() + ) + } + + factory { + GetTopRatedStreamImpl( + repository = get() + ) + } + + factory { + ListStreamAnalyticsImpl() + } + + factory { + ListStreamRepositoryImpl( + service = get(), + ) + } + + factory { + ListStreamServiceImpl( + client = get() + ) + } + } +} \ No newline at end of file diff --git a/feature-list-streams/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_list_streams/list/domain/GetGenresUseCase.kt b/feature-list-streams/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_list_streams/list/domain/GetGenresUseCase.kt similarity index 92% rename from feature-list-streams/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_list_streams/list/domain/GetGenresUseCase.kt rename to feature-list-streams/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_list_streams/list/domain/GetGenresUseCase.kt index 36b2b4d9..e3da0b8c 100644 --- a/feature-list-streams/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_list_streams/list/domain/GetGenresUseCase.kt +++ b/feature-list-streams/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_list_streams/list/domain/GetGenresUseCase.kt @@ -3,13 +3,11 @@ package com.codandotv.streamplayerapp.feature_list_streams.list.domain import com.codandotv.streamplayerapp.feature_list_streams.list.data.ListStreamRepository import com.codandotv.streamplayerapp.feature_list_streams.list.domain.model.Genre import kotlinx.coroutines.flow.Flow -import org.koin.core.annotation.Factory interface GetGenresUseCase { suspend operator fun invoke(): Flow> } -@Factory class GetGenresUseCaseImpl( private val repository: ListStreamRepository ) : GetGenresUseCase { diff --git a/feature-list-streams/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_list_streams/list/domain/GetLatestMovieUseCase.kt b/feature-list-streams/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_list_streams/list/domain/GetLatestMovieUseCase.kt similarity index 92% rename from feature-list-streams/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_list_streams/list/domain/GetLatestMovieUseCase.kt rename to feature-list-streams/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_list_streams/list/domain/GetLatestMovieUseCase.kt index 7257a40c..a5300816 100644 --- a/feature-list-streams/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_list_streams/list/domain/GetLatestMovieUseCase.kt +++ b/feature-list-streams/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_list_streams/list/domain/GetLatestMovieUseCase.kt @@ -3,13 +3,11 @@ package com.codandotv.streamplayerapp.feature_list_streams.list.domain import com.codandotv.streamplayerapp.feature_list_streams.list.data.ListStreamRepository import com.codandotv.streamplayerapp.feature_list_streams.list.domain.model.Stream import kotlinx.coroutines.flow.Flow -import org.koin.core.annotation.Factory interface GetTopRatedStream { suspend operator fun invoke(): Flow } -@Factory class GetTopRatedStreamImpl( private val repository: ListStreamRepository ) : GetTopRatedStream { diff --git a/feature-list-streams/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_list_streams/list/domain/ListMovieUseCase.kt b/feature-list-streams/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_list_streams/list/domain/ListMovieUseCase.kt similarity index 93% rename from feature-list-streams/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_list_streams/list/domain/ListMovieUseCase.kt rename to feature-list-streams/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_list_streams/list/domain/ListMovieUseCase.kt index 367f47be..b4aff62b 100644 --- a/feature-list-streams/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_list_streams/list/domain/ListMovieUseCase.kt +++ b/feature-list-streams/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_list_streams/list/domain/ListMovieUseCase.kt @@ -5,13 +5,11 @@ import com.codandotv.streamplayerapp.feature_list_streams.list.data.ListStreamRe import com.codandotv.streamplayerapp.feature_list_streams.list.domain.model.Genre import com.codandotv.streamplayerapp.feature_list_streams.list.domain.model.Stream import kotlinx.coroutines.flow.Flow -import org.koin.core.annotation.Factory interface ListStreamUseCase { operator fun invoke(genre: Genre): Flow> } -@Factory class ListStreamUseCaseImpl( private val repository: ListStreamRepository ) : ListStreamUseCase { diff --git a/feature-list-streams/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_list_streams/list/domain/ListStreamAnalytics.kt b/feature-list-streams/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_list_streams/list/domain/ListStreamAnalytics.kt similarity index 75% rename from feature-list-streams/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_list_streams/list/domain/ListStreamAnalytics.kt rename to feature-list-streams/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_list_streams/list/domain/ListStreamAnalytics.kt index e599c1fa..e2fc2866 100644 --- a/feature-list-streams/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_list_streams/list/domain/ListStreamAnalytics.kt +++ b/feature-list-streams/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_list_streams/list/domain/ListStreamAnalytics.kt @@ -1,8 +1,6 @@ package com.codandotv.streamplayerapp.feature_list_streams.list.domain -import org.koin.core.annotation.Factory interface ListStreamAnalytics -@Factory class ListStreamAnalyticsImpl : ListStreamAnalytics \ No newline at end of file diff --git a/feature-list-streams/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_list_streams/list/domain/ListStreamMapper.kt b/feature-list-streams/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_list_streams/list/domain/ListStreamMapper.kt similarity index 100% rename from feature-list-streams/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_list_streams/list/domain/ListStreamMapper.kt rename to feature-list-streams/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_list_streams/list/domain/ListStreamMapper.kt diff --git a/feature-list-streams/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_list_streams/list/domain/model/Genre.kt b/feature-list-streams/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_list_streams/list/domain/model/Genre.kt similarity index 100% rename from feature-list-streams/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_list_streams/list/domain/model/Genre.kt rename to feature-list-streams/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_list_streams/list/domain/model/Genre.kt diff --git a/feature-list-streams/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_list_streams/list/domain/model/HighlightBanner.kt b/feature-list-streams/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_list_streams/list/domain/model/HighlightBanner.kt similarity index 100% rename from feature-list-streams/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_list_streams/list/domain/model/HighlightBanner.kt rename to feature-list-streams/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_list_streams/list/domain/model/HighlightBanner.kt diff --git a/feature-list-streams/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_list_streams/list/domain/model/ListStream.kt b/feature-list-streams/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_list_streams/list/domain/model/ListStream.kt similarity index 100% rename from feature-list-streams/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_list_streams/list/domain/model/ListStream.kt rename to feature-list-streams/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_list_streams/list/domain/model/ListStream.kt diff --git a/feature-list-streams/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_list_streams/list/presentation/navigation/ListStreamsNavigation.kt b/feature-list-streams/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_list_streams/list/presentation/navigation/ListStreamsNavigation.kt similarity index 92% rename from feature-list-streams/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_list_streams/list/presentation/navigation/ListStreamsNavigation.kt rename to feature-list-streams/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_list_streams/list/presentation/navigation/ListStreamsNavigation.kt index ac58e2be..633a92fd 100644 --- a/feature-list-streams/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_list_streams/list/presentation/navigation/ListStreamsNavigation.kt +++ b/feature-list-streams/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_list_streams/list/presentation/navigation/ListStreamsNavigation.kt @@ -14,7 +14,6 @@ import com.codandotv.streamplayerapp.feature_list_streams.list.di.ListStreamModu import com.codandotv.streamplayerapp.feature_list_streams.list.presentation.screens.ListStreamsScreen import org.koin.core.context.loadKoinModules import org.koin.core.context.unloadKoinModules -import org.koin.ksp.generated.module internal const val DEFAULT_ID = "" @@ -22,7 +21,7 @@ fun NavGraphBuilder.listStreamsNavGraph(navController: NavHostController) { composable(HOME_COMPLETE) { nav -> BackHandler(true) {} if (nav.lifecycle.currentState == Lifecycle.State.STARTED) { - loadKoinModules(ListStreamModule().module) + loadKoinModules(ListStreamModule.module) } ListStreamsScreen(navController = navController, onNavigateDetailList = { id -> @@ -35,7 +34,7 @@ fun NavGraphBuilder.listStreamsNavGraph(navController: NavHostController) { navController.navigate(Routes.SEARCH) }, disposable = { - unloadKoinModules(ListStreamModule().module) + unloadKoinModules(ListStreamModule.module) }, profilePicture = nav.arguments?.getString(PROFILE_ID) ?: DEFAULT_ID ) diff --git a/feature-list-streams/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_list_streams/list/presentation/screens/ListStreamViewModel.kt b/feature-list-streams/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_list_streams/list/presentation/screens/ListStreamViewModel.kt similarity index 98% rename from feature-list-streams/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_list_streams/list/presentation/screens/ListStreamViewModel.kt rename to feature-list-streams/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_list_streams/list/presentation/screens/ListStreamViewModel.kt index d33a1ae7..523ad5fe 100644 --- a/feature-list-streams/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_list_streams/list/presentation/screens/ListStreamViewModel.kt +++ b/feature-list-streams/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_list_streams/list/presentation/screens/ListStreamViewModel.kt @@ -25,10 +25,8 @@ import kotlinx.coroutines.flow.onStart import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.flow.update import kotlinx.coroutines.launch -import org.koin.android.annotation.KoinViewModel import com.codandotv.streamplayerapp.core.shared.ui.R as SharedUiR -@KoinViewModel class ListStreamViewModel( private val listStreams: ListStreamUseCase, private val listGenres: GetGenresUseCase, diff --git a/feature-list-streams/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_list_streams/list/presentation/screens/ListStreamsScreen.kt b/feature-list-streams/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_list_streams/list/presentation/screens/ListStreamsScreen.kt similarity index 100% rename from feature-list-streams/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_list_streams/list/presentation/screens/ListStreamsScreen.kt rename to feature-list-streams/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_list_streams/list/presentation/screens/ListStreamsScreen.kt diff --git a/feature-list-streams/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_list_streams/list/presentation/screens/ListStreamsUIState.kt b/feature-list-streams/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_list_streams/list/presentation/screens/ListStreamsUIState.kt similarity index 100% rename from feature-list-streams/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_list_streams/list/presentation/screens/ListStreamsUIState.kt rename to feature-list-streams/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_list_streams/list/presentation/screens/ListStreamsUIState.kt diff --git a/feature-list-streams/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_list_streams/list/presentation/widgets/HighlightBanner.kt b/feature-list-streams/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_list_streams/list/presentation/widgets/HighlightBanner.kt similarity index 100% rename from feature-list-streams/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_list_streams/list/presentation/widgets/HighlightBanner.kt rename to feature-list-streams/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_list_streams/list/presentation/widgets/HighlightBanner.kt diff --git a/feature-profile/build.gradle.kts b/feature-profile/build.gradle.kts index 387f47d6..df7913b0 100644 --- a/feature-profile/build.gradle.kts +++ b/feature-profile/build.gradle.kts @@ -18,7 +18,7 @@ kotlin { implementation(compose.ui) implementation(libs.bundles.koin) - implementation(libs.koin.annotations) + api(libs.koin.annotations) implementation(libs.bundles.networking) implementation(libs.bundles.androidSupport) implementation(libs.coil) diff --git a/feature-search/build.gradle.kts b/feature-search/build.gradle.kts index 3016a44e..f0536e8c 100644 --- a/feature-search/build.gradle.kts +++ b/feature-search/build.gradle.kts @@ -11,7 +11,7 @@ kotlin { sourceSets { commonMain.dependencies { implementation(libs.paging.compose) - implementation(libs.koin.annotations) + api(libs.koin.annotations) implementation(libs.koin.core) implementation(projects.coreNetworking) implementation(projects.coreNavigation) @@ -26,17 +26,22 @@ kotlin { implementation(libs.bundles.koin) implementation(libs.bundles.networking) implementation(libs.coil) - implementation(libs.koin.annotations) + api(libs.koin.annotations) implementation(libs.bundles.androidSupport) implementation(libs.paging.compose) } } dependencies { - add("kspCommonMainMetadata",libs.koin.ksp.compiler) + add("kspCommonMainMetadata", libs.koin.ksp.compiler) add("kspAndroid", libs.koin.ksp.compiler) +// add after with iOS +// add("kspIosX64", libs.koin.ksp.compiler) +// add("kspIosArm64", libs.koin.ksp.compiler) +// add("kspIosSimulatorArm64", libs.koin.ksp.compiler) } + ksp { arg("KOIN_CONFIG_CHECK","true") } \ No newline at end of file diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 96b0f53a..fe274061 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -87,7 +87,7 @@ koin_test = { group = "io.insert-koin", name = "koin-test-junit4", version.ref = koin_android = { group = "io.insert-koin", name = "koin-android", version.ref = "koin" } koin_core = { group = "io.insert-koin", name = "koin-core", version.ref = "koin" } koin_annotations = { group = "io.insert-koin", name = "koin-annotations", version.ref = "koin-ksp" } -koin_compiler = { group = "io.insert-koin", name = "koin-ksp-compiler", version.ref = "koin-ksp" } +koin_ksp_compiler = { group = "io.insert-koin", name = "koin-ksp-compiler", version.ref = "koin-ksp" } koin_compose = { group = "io.insert-koin", name = "koin-androidx-compose", version.ref = "koin" } #Networking From eaa33fbfc9c4d53ab4b6edbc782f25179b6b032e Mon Sep 17 00:00:00 2001 From: Rods Date: Thu, 30 Jan 2025 12:58:44 -0300 Subject: [PATCH 44/96] [ISSUE-6] - local-storage and navigation AndroidMain to commonMain --- core-local-storage/build.gradle.kts | 2 +- .../streamplayerapp/core_local_storage/data/dao/FavoriteDao.kt | 0 .../core_local_storage/data/database/StreamPlayerAppDatabase.kt | 0 .../streamplayerapp/core_local_storage/di/LocalStorageModule.kt | 0 .../core_local_storage/domain/model/MovieEntity.kt | 0 core-navigation/build.gradle.kts | 2 +- .../core_navigation/bottomnavigation/BottomNavItem.kt | 0 .../bottomnavigation/StreamPlayerBottomNavigation.kt | 0 .../core_navigation/extensions/NavControllerExtension.kt | 0 .../streamplayerapp/core_navigation/helper/NavigationHelper.kt | 0 .../streamplayerapp/core_navigation/routes/BottomNavRoutes.kt | 0 .../codandotv/streamplayerapp/core_navigation/routes/Routes.kt | 0 12 files changed, 2 insertions(+), 2 deletions(-) rename core-local-storage/src/{androidMain => commonMain}/kotlin/com/codandotv/streamplayerapp/core_local_storage/data/dao/FavoriteDao.kt (100%) rename core-local-storage/src/{androidMain => commonMain}/kotlin/com/codandotv/streamplayerapp/core_local_storage/data/database/StreamPlayerAppDatabase.kt (100%) rename core-local-storage/src/{androidMain => commonMain}/kotlin/com/codandotv/streamplayerapp/core_local_storage/di/LocalStorageModule.kt (100%) rename core-local-storage/src/{androidMain => commonMain}/kotlin/com/codandotv/streamplayerapp/core_local_storage/domain/model/MovieEntity.kt (100%) rename core-navigation/src/{androidMain => commonMain}/kotlin/com/codandotv/streamplayerapp/core_navigation/bottomnavigation/BottomNavItem.kt (100%) rename core-navigation/src/{androidMain => commonMain}/kotlin/com/codandotv/streamplayerapp/core_navigation/bottomnavigation/StreamPlayerBottomNavigation.kt (100%) rename core-navigation/src/{androidMain => commonMain}/kotlin/com/codandotv/streamplayerapp/core_navigation/extensions/NavControllerExtension.kt (100%) rename core-navigation/src/{androidMain => commonMain}/kotlin/com/codandotv/streamplayerapp/core_navigation/helper/NavigationHelper.kt (100%) rename core-navigation/src/{androidMain => commonMain}/kotlin/com/codandotv/streamplayerapp/core_navigation/routes/BottomNavRoutes.kt (100%) rename core-navigation/src/{androidMain => commonMain}/kotlin/com/codandotv/streamplayerapp/core_navigation/routes/Routes.kt (100%) diff --git a/core-local-storage/build.gradle.kts b/core-local-storage/build.gradle.kts index c24ab094..940bb96b 100644 --- a/core-local-storage/build.gradle.kts +++ b/core-local-storage/build.gradle.kts @@ -5,7 +5,7 @@ plugins { kotlin { sourceSets { - androidMain.dependencies { + commonMain.dependencies { implementation(libs.bundles.room) implementation(libs.bundles.kotlin) implementation(libs.bundles.koin) diff --git a/core-local-storage/src/androidMain/kotlin/com/codandotv/streamplayerapp/core_local_storage/data/dao/FavoriteDao.kt b/core-local-storage/src/commonMain/kotlin/com/codandotv/streamplayerapp/core_local_storage/data/dao/FavoriteDao.kt similarity index 100% rename from core-local-storage/src/androidMain/kotlin/com/codandotv/streamplayerapp/core_local_storage/data/dao/FavoriteDao.kt rename to core-local-storage/src/commonMain/kotlin/com/codandotv/streamplayerapp/core_local_storage/data/dao/FavoriteDao.kt diff --git a/core-local-storage/src/androidMain/kotlin/com/codandotv/streamplayerapp/core_local_storage/data/database/StreamPlayerAppDatabase.kt b/core-local-storage/src/commonMain/kotlin/com/codandotv/streamplayerapp/core_local_storage/data/database/StreamPlayerAppDatabase.kt similarity index 100% rename from core-local-storage/src/androidMain/kotlin/com/codandotv/streamplayerapp/core_local_storage/data/database/StreamPlayerAppDatabase.kt rename to core-local-storage/src/commonMain/kotlin/com/codandotv/streamplayerapp/core_local_storage/data/database/StreamPlayerAppDatabase.kt diff --git a/core-local-storage/src/androidMain/kotlin/com/codandotv/streamplayerapp/core_local_storage/di/LocalStorageModule.kt b/core-local-storage/src/commonMain/kotlin/com/codandotv/streamplayerapp/core_local_storage/di/LocalStorageModule.kt similarity index 100% rename from core-local-storage/src/androidMain/kotlin/com/codandotv/streamplayerapp/core_local_storage/di/LocalStorageModule.kt rename to core-local-storage/src/commonMain/kotlin/com/codandotv/streamplayerapp/core_local_storage/di/LocalStorageModule.kt diff --git a/core-local-storage/src/androidMain/kotlin/com/codandotv/streamplayerapp/core_local_storage/domain/model/MovieEntity.kt b/core-local-storage/src/commonMain/kotlin/com/codandotv/streamplayerapp/core_local_storage/domain/model/MovieEntity.kt similarity index 100% rename from core-local-storage/src/androidMain/kotlin/com/codandotv/streamplayerapp/core_local_storage/domain/model/MovieEntity.kt rename to core-local-storage/src/commonMain/kotlin/com/codandotv/streamplayerapp/core_local_storage/domain/model/MovieEntity.kt diff --git a/core-navigation/build.gradle.kts b/core-navigation/build.gradle.kts index 482d37d5..334cd190 100644 --- a/core-navigation/build.gradle.kts +++ b/core-navigation/build.gradle.kts @@ -7,7 +7,7 @@ plugins { kotlin { sourceSets { - androidMain.dependencies { + commonMain.dependencies { implementation(libs.bundles.kotlin) implementation(libs.navigation.compose) implementation(compose.material3) diff --git a/core-navigation/src/androidMain/kotlin/com/codandotv/streamplayerapp/core_navigation/bottomnavigation/BottomNavItem.kt b/core-navigation/src/commonMain/kotlin/com/codandotv/streamplayerapp/core_navigation/bottomnavigation/BottomNavItem.kt similarity index 100% rename from core-navigation/src/androidMain/kotlin/com/codandotv/streamplayerapp/core_navigation/bottomnavigation/BottomNavItem.kt rename to core-navigation/src/commonMain/kotlin/com/codandotv/streamplayerapp/core_navigation/bottomnavigation/BottomNavItem.kt diff --git a/core-navigation/src/androidMain/kotlin/com/codandotv/streamplayerapp/core_navigation/bottomnavigation/StreamPlayerBottomNavigation.kt b/core-navigation/src/commonMain/kotlin/com/codandotv/streamplayerapp/core_navigation/bottomnavigation/StreamPlayerBottomNavigation.kt similarity index 100% rename from core-navigation/src/androidMain/kotlin/com/codandotv/streamplayerapp/core_navigation/bottomnavigation/StreamPlayerBottomNavigation.kt rename to core-navigation/src/commonMain/kotlin/com/codandotv/streamplayerapp/core_navigation/bottomnavigation/StreamPlayerBottomNavigation.kt diff --git a/core-navigation/src/androidMain/kotlin/com/codandotv/streamplayerapp/core_navigation/extensions/NavControllerExtension.kt b/core-navigation/src/commonMain/kotlin/com/codandotv/streamplayerapp/core_navigation/extensions/NavControllerExtension.kt similarity index 100% rename from core-navigation/src/androidMain/kotlin/com/codandotv/streamplayerapp/core_navigation/extensions/NavControllerExtension.kt rename to core-navigation/src/commonMain/kotlin/com/codandotv/streamplayerapp/core_navigation/extensions/NavControllerExtension.kt diff --git a/core-navigation/src/androidMain/kotlin/com/codandotv/streamplayerapp/core_navigation/helper/NavigationHelper.kt b/core-navigation/src/commonMain/kotlin/com/codandotv/streamplayerapp/core_navigation/helper/NavigationHelper.kt similarity index 100% rename from core-navigation/src/androidMain/kotlin/com/codandotv/streamplayerapp/core_navigation/helper/NavigationHelper.kt rename to core-navigation/src/commonMain/kotlin/com/codandotv/streamplayerapp/core_navigation/helper/NavigationHelper.kt diff --git a/core-navigation/src/androidMain/kotlin/com/codandotv/streamplayerapp/core_navigation/routes/BottomNavRoutes.kt b/core-navigation/src/commonMain/kotlin/com/codandotv/streamplayerapp/core_navigation/routes/BottomNavRoutes.kt similarity index 100% rename from core-navigation/src/androidMain/kotlin/com/codandotv/streamplayerapp/core_navigation/routes/BottomNavRoutes.kt rename to core-navigation/src/commonMain/kotlin/com/codandotv/streamplayerapp/core_navigation/routes/BottomNavRoutes.kt diff --git a/core-navigation/src/androidMain/kotlin/com/codandotv/streamplayerapp/core_navigation/routes/Routes.kt b/core-navigation/src/commonMain/kotlin/com/codandotv/streamplayerapp/core_navigation/routes/Routes.kt similarity index 100% rename from core-navigation/src/androidMain/kotlin/com/codandotv/streamplayerapp/core_navigation/routes/Routes.kt rename to core-navigation/src/commonMain/kotlin/com/codandotv/streamplayerapp/core_navigation/routes/Routes.kt From eafbd19c161708c6c8ef01217f6cce36b6d04efe Mon Sep 17 00:00:00 2001 From: Rods Date: Thu, 30 Jan 2025 13:04:54 -0300 Subject: [PATCH 45/96] [ISSUE-6] - remove ksp --- feature-detail/build.gradle.kts | 16 ---------------- .../feature_detail/data/DetailStreamService.kt | 1 - feature-search/build.gradle.kts | 18 +----------------- 3 files changed, 1 insertion(+), 34 deletions(-) diff --git a/feature-detail/build.gradle.kts b/feature-detail/build.gradle.kts index 717a1a8e..cdb31651 100644 --- a/feature-detail/build.gradle.kts +++ b/feature-detail/build.gradle.kts @@ -2,7 +2,6 @@ plugins { id("com.streamplayer.kmp-library") - alias(libs.plugins.ksp) alias(libs.plugins.jetbrains.compose) alias(libs.plugins.compose.compiler) } @@ -10,7 +9,6 @@ plugins { kotlin { sourceSets { commonMain.dependencies { - api(libs.koin.annotations) implementation(libs.koin.core) implementation(projects.coreNetworking) implementation(projects.coreNavigation) @@ -25,24 +23,10 @@ kotlin { implementation(libs.bundles.koin) implementation(libs.bundles.networking) implementation(libs.coil) - api(libs.koin.annotations) implementation(libs.bundles.androidSupport) } commonTest.dependencies { implementation(libs.bundles.test) } } -} - -dependencies { - add("kspCommonMainMetadata", libs.koin.ksp.compiler) - add("kspAndroid", libs.koin.ksp.compiler) -// add after with iOS -// add("kspIosX64", libs.koin.ksp.compiler) -// add("kspIosArm64", libs.koin.ksp.compiler) -// add("kspIosSimulatorArm64", libs.koin.ksp.compiler) -} - -ksp { - arg("KOIN_CONFIG_CHECK","true") } \ No newline at end of file diff --git a/feature-detail/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_detail/data/DetailStreamService.kt b/feature-detail/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_detail/data/DetailStreamService.kt index c50f698c..f4c69ba1 100644 --- a/feature-detail/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_detail/data/DetailStreamService.kt +++ b/feature-detail/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_detail/data/DetailStreamService.kt @@ -6,7 +6,6 @@ import com.codandotv.streamplayerapp.feature_detail.data.model.DetailStreamRespo import com.codandotv.streamplayerapp.feature_detail.data.model.VideoStreamsResponse import io.ktor.client.HttpClient import io.ktor.client.request.url -import org.koin.core.annotation.Factory interface DetailStreamService { suspend fun getMovie(movieId: String): NetworkResponse diff --git a/feature-search/build.gradle.kts b/feature-search/build.gradle.kts index f0536e8c..f05eca76 100644 --- a/feature-search/build.gradle.kts +++ b/feature-search/build.gradle.kts @@ -2,7 +2,6 @@ plugins { id("com.streamplayer.kmp-library") - alias(libs.plugins.ksp) alias(libs.plugins.jetbrains.compose) alias(libs.plugins.compose.compiler) } @@ -11,7 +10,6 @@ kotlin { sourceSets { commonMain.dependencies { implementation(libs.paging.compose) - api(libs.koin.annotations) implementation(libs.koin.core) implementation(projects.coreNetworking) implementation(projects.coreNavigation) @@ -26,22 +24,8 @@ kotlin { implementation(libs.bundles.koin) implementation(libs.bundles.networking) implementation(libs.coil) - api(libs.koin.annotations) implementation(libs.bundles.androidSupport) implementation(libs.paging.compose) + } } -} - -dependencies { - add("kspCommonMainMetadata", libs.koin.ksp.compiler) - add("kspAndroid", libs.koin.ksp.compiler) -// add after with iOS -// add("kspIosX64", libs.koin.ksp.compiler) -// add("kspIosArm64", libs.koin.ksp.compiler) -// add("kspIosSimulatorArm64", libs.koin.ksp.compiler) -} - - -ksp { - arg("KOIN_CONFIG_CHECK","true") } \ No newline at end of file From c1e6a0f12eceebf99998841e0b943c094514c212 Mon Sep 17 00:00:00 2001 From: Rods Date: Thu, 30 Jan 2025 13:37:03 -0300 Subject: [PATCH 46/96] [ISSUE-9] - migrate strings.xml from detail --- feature-detail/build.gradle.kts | 1 + .../composeResources}/values/strings.xml | 0 .../presentation/screens/DetailStreamsScreen.kt | 10 ++++++---- .../widget/DetailStreamActionOption.kt | 15 ++++++++++----- .../presentation/widget/DetailStreamRowHeader.kt | 6 ++++-- .../presentation/widget/DetailStreamToolbar.kt | 9 ++++++--- 6 files changed, 27 insertions(+), 14 deletions(-) rename feature-detail/src/{androidMain/res => commonMain/composeResources}/values/strings.xml (100%) diff --git a/feature-detail/build.gradle.kts b/feature-detail/build.gradle.kts index cdb31651..2c2392d8 100644 --- a/feature-detail/build.gradle.kts +++ b/feature-detail/build.gradle.kts @@ -24,6 +24,7 @@ kotlin { implementation(libs.bundles.networking) implementation(libs.coil) implementation(libs.bundles.androidSupport) + implementation(compose.components.resources) } commonTest.dependencies { implementation(libs.bundles.test) diff --git a/feature-detail/src/androidMain/res/values/strings.xml b/feature-detail/src/commonMain/composeResources/values/strings.xml similarity index 100% rename from feature-detail/src/androidMain/res/values/strings.xml rename to feature-detail/src/commonMain/composeResources/values/strings.xml diff --git a/feature-detail/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_detail/presentation/screens/DetailStreamsScreen.kt b/feature-detail/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_detail/presentation/screens/DetailStreamsScreen.kt index ef6f59f5..2ce51aec 100644 --- a/feature-detail/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_detail/presentation/screens/DetailStreamsScreen.kt +++ b/feature-detail/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_detail/presentation/screens/DetailStreamsScreen.kt @@ -29,7 +29,6 @@ import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalLifecycleOwner -import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.em @@ -39,14 +38,17 @@ import androidx.lifecycle.compose.LifecycleEventEffect import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.navigation.NavController import com.codandotv.streamplayerapp.core_shared_ui.widget.SharingStreamCustomView -import com.codandotv.streamplayerapp.feature.detail.R import com.codandotv.streamplayerapp.feature_detail.domain.DetailStream import com.codandotv.streamplayerapp.feature_detail.presentation.widget.DetailStreamActionOption import com.codandotv.streamplayerapp.feature_detail.presentation.widget.DetailStreamButtonAction import com.codandotv.streamplayerapp.feature_detail.presentation.widget.DetailStreamImagePreview import com.codandotv.streamplayerapp.feature_detail.presentation.widget.DetailStreamRowHeader import com.codandotv.streamplayerapp.feature_detail.presentation.widget.DetailStreamToolbar +import org.jetbrains.compose.resources.stringResource import org.koin.androidx.compose.koinViewModel +import streamplayerapp_kmp.feature_detail.generated.resources.Res +import streamplayerapp_kmp.feature_detail.generated.resources.detail_default_text_secondary_button +import streamplayerapp_kmp.feature_detail.generated.resources.detail_watch_primary_button @Composable fun DetailStreamScreen( @@ -154,7 +156,7 @@ private fun SetupDetailScreen( ), imageVector = Icons.Filled.PlayArrow, imageVectorColor = MaterialTheme.colorScheme.onSurface, - text = stringResource(R.string.detail_watch_primary_button), + text = stringResource(Res.string.detail_watch_primary_button), textColor = MaterialTheme.colorScheme.onSurface ) Spacer(modifier = Modifier.height(4.dp)) @@ -164,7 +166,7 @@ private fun SetupDetailScreen( ), imageVector = Icons.Filled.Add, imageVectorColor = MaterialTheme.colorScheme.onSurface, - text = stringResource(id = R.string.detail_default_text_secondary_button), + text = stringResource(Res.string.detail_default_text_secondary_button), textColor = MaterialTheme.colorScheme.onSurface, ) Text( diff --git a/feature-detail/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_detail/presentation/widget/DetailStreamActionOption.kt b/feature-detail/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_detail/presentation/widget/DetailStreamActionOption.kt index 1363ccf4..d694d8c2 100644 --- a/feature-detail/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_detail/presentation/widget/DetailStreamActionOption.kt +++ b/feature-detail/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_detail/presentation/widget/DetailStreamActionOption.kt @@ -17,10 +17,15 @@ import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color -import androidx.compose.ui.res.stringResource import com.codandotv.streamplayerapp.core_shared_ui.widget.IconWithText import com.codandotv.streamplayerapp.feature.detail.R import com.codandotv.streamplayerapp.feature_detail.domain.DetailStream +import org.jetbrains.compose.resources.stringResource +import streamplayerapp_kmp.feature_detail.generated.resources.Res +import streamplayerapp_kmp.feature_detail.generated.resources.detail_classification +import streamplayerapp_kmp.feature_detail.generated.resources.detail_download +import streamplayerapp_kmp.feature_detail.generated.resources.detail_my_list +import streamplayerapp_kmp.feature_detail.generated.resources.detail_share @Composable fun DetailStreamActionOption( @@ -48,28 +53,28 @@ fun DetailStreamActionOption( }, imageVector = iconCheckList, imageColor = Color.White, - text = stringResource(id = R.string.detail_my_list), + text = stringResource(Res.string.detail_my_list), textColor = Color.Gray, ) IconWithText( onClick = { TODO("Implementar mecanismo de classificação.") }, imageVector = Icons.Filled.ThumbUp, imageColor = Color.White, - text = stringResource(id = R.string.detail_classification), + text = stringResource(Res.string.detail_classification), textColor = Color.Gray, ) IconWithText( onClick = { onShowSharingOptions.invoke() }, imageVector = Icons.Filled.Share, imageColor = Color.White, - text = stringResource(id = R.string.detail_share), + text = stringResource(Res.string.detail_share), textColor = Color.Gray, ) IconWithText( onClick = { TODO("Implementar mecanismo de download.") }, imageVector = Icons.Filled.Share, imageColor = Color.White, - text = stringResource(id = R.string.detail_download), + text = stringResource(Res.string.detail_download), textColor = Color.Gray, ) } diff --git a/feature-detail/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_detail/presentation/widget/DetailStreamRowHeader.kt b/feature-detail/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_detail/presentation/widget/DetailStreamRowHeader.kt index 41b6e3e8..17d7ef5d 100644 --- a/feature-detail/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_detail/presentation/widget/DetailStreamRowHeader.kt +++ b/feature-detail/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_detail/presentation/widget/DetailStreamRowHeader.kt @@ -12,12 +12,14 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.res.painterResource -import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.em import androidx.compose.ui.unit.sp import com.codandotv.streamplayerapp.feature.detail.R +import org.jetbrains.compose.resources.stringResource +import streamplayerapp_kmp.feature_detail.generated.resources.Res +import streamplayerapp_kmp.feature_detail.generated.resources.detail_movie @Composable fun DetailStreamRowHeader( @@ -36,7 +38,7 @@ fun DetailStreamRowHeader( ) Text( - text = stringResource(id = R.string.detail_movie), + text = stringResource(Res.string.detail_movie), modifier = Modifier.offset(x = (-6).dp), style = MaterialTheme.typography.headlineMedium.copy( color = Color.Gray, diff --git a/feature-detail/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_detail/presentation/widget/DetailStreamToolbar.kt b/feature-detail/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_detail/presentation/widget/DetailStreamToolbar.kt index 9abdcf24..41b244a9 100644 --- a/feature-detail/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_detail/presentation/widget/DetailStreamToolbar.kt +++ b/feature-detail/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_detail/presentation/widget/DetailStreamToolbar.kt @@ -14,10 +14,13 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.res.painterResource -import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp import androidx.navigation.NavController import com.codandotv.streamplayerapp.feature.detail.R +import org.jetbrains.compose.resources.stringResource +import streamplayerapp_kmp.feature_detail.generated.resources.Res +import streamplayerapp_kmp.feature_detail.generated.resources.detail_back +import streamplayerapp_kmp.feature_detail.generated.resources.detail_search @OptIn(ExperimentalMaterial3Api::class) @Composable @@ -32,7 +35,7 @@ fun DetailStreamToolbar( IconButton(onClick = { navController.navigateUp() }) { Icon( imageVector = Icons.Filled.ArrowBack, - contentDescription = stringResource(id = R.string.detail_back) + contentDescription = stringResource(Res.string.detail_back) ) } }, actions = { @@ -42,7 +45,7 @@ fun DetailStreamToolbar( Icon( imageVector = Icons.Default.Search, tint = Color.White, - contentDescription = stringResource(id = R.string.detail_search) + contentDescription = stringResource(Res.string.detail_search) ) } IconButton(onClick = { }) { From ded86fe506be9973cdeb573aeb775d7fcff45842 Mon Sep 17 00:00:00 2001 From: Rods Date: Thu, 30 Jan 2025 23:20:03 -0300 Subject: [PATCH 47/96] [ISSUE-6] - remove plugin and migrate core-shared-ui android to common --- .../com.streamplayer.koin-setup.gradle.kts | 53 ------------------- core-shared-ui/build.gradle.kts | 5 +- .../core_shared_ui/resources/Colors.kt | 0 .../core_shared_ui/theme/StreamPlayerTheme.kt | 0 .../core_shared_ui/theme/ThemePreviews.kt | 0 .../core_shared_ui/utils/Sharing.kt | 0 .../core_shared_ui/widget/IconWithText.kt | 0 .../core_shared_ui/widget/PlayerComponent.kt | 0 .../widget/SharingStreamCustomView.kt | 0 .../widget/StreamPlayerTopBar.kt | 0 .../core_shared_ui/widget/StreamsCard.kt | 0 .../core_shared_ui/widget/StreamsCarousel.kt | 0 12 files changed, 1 insertion(+), 57 deletions(-) delete mode 100644 build-logic/src/main/java/com.streamplayer.koin-setup.gradle.kts rename core-shared-ui/src/{androidMain => commonMain}/kotlin/com/codandotv/streamplayerapp/core_shared_ui/resources/Colors.kt (100%) rename core-shared-ui/src/{androidMain => commonMain}/kotlin/com/codandotv/streamplayerapp/core_shared_ui/theme/StreamPlayerTheme.kt (100%) rename core-shared-ui/src/{androidMain => commonMain}/kotlin/com/codandotv/streamplayerapp/core_shared_ui/theme/ThemePreviews.kt (100%) rename core-shared-ui/src/{androidMain => commonMain}/kotlin/com/codandotv/streamplayerapp/core_shared_ui/utils/Sharing.kt (100%) rename core-shared-ui/src/{androidMain => commonMain}/kotlin/com/codandotv/streamplayerapp/core_shared_ui/widget/IconWithText.kt (100%) rename core-shared-ui/src/{androidMain => commonMain}/kotlin/com/codandotv/streamplayerapp/core_shared_ui/widget/PlayerComponent.kt (100%) rename core-shared-ui/src/{androidMain => commonMain}/kotlin/com/codandotv/streamplayerapp/core_shared_ui/widget/SharingStreamCustomView.kt (100%) rename core-shared-ui/src/{androidMain => commonMain}/kotlin/com/codandotv/streamplayerapp/core_shared_ui/widget/StreamPlayerTopBar.kt (100%) rename core-shared-ui/src/{androidMain => commonMain}/kotlin/com/codandotv/streamplayerapp/core_shared_ui/widget/StreamsCard.kt (100%) rename core-shared-ui/src/{androidMain => commonMain}/kotlin/com/codandotv/streamplayerapp/core_shared_ui/widget/StreamsCarousel.kt (100%) diff --git a/build-logic/src/main/java/com.streamplayer.koin-setup.gradle.kts b/build-logic/src/main/java/com.streamplayer.koin-setup.gradle.kts deleted file mode 100644 index 408060b4..00000000 --- a/build-logic/src/main/java/com.streamplayer.koin-setup.gradle.kts +++ /dev/null @@ -1,53 +0,0 @@ -import extensions.koinAnnotationsDependency -import extensions.koinCompiler -import extensions.koinComposeDependency -import extensions.koinCoreDependency -import extensions.libs -import org.jetbrains.kotlin.gradle.dsl.KotlinCompile - -plugins { - id("com.google.devtools.ksp") - id("kotlin-multiplatform") -} - -kotlin { - sourceSets { - commonMain { - kotlin.srcDir("build/generated/ksp/metadata/commonMain/kotlin") - - dependencies { - implementation(libs.koinCoreDependency()) - api(libs.koinAnnotationsDependency()) - api(libs.koinComposeDependency()) - } - } - } -} - -dependencies { - add("kspCommonMainMetadata", libs.koinCompiler()) - add("kspAndroid", libs.koinCompiler()) -// add("kspIosX64", libs.koin.ksp.compiler) -// add("kspIosArm64", libs.koin.ksp.compiler) -// add("kspIosSimulatorArm64", libs.koin.ksp.compiler) -} - -// WORKAROUND: ADD this dependsOn("kspCommonMainKotlinMetadata") instead of above dependencies -tasks.withType>().configureEach { - if (name != "kspCommonMainKotlinMetadata") { - dependsOn("kspCommonMainKotlinMetadata") - } -} - -afterEvaluate { - tasks.filter { - it.name.contains("SourcesJar", true) - }.forEach { - println("SourceJarTask====>${it.name}") - it.dependsOn("kspCommonMainKotlinMetadata") - } -} - -ksp { - arg("KOIN_CONFIG_CHECK", "false") -} \ No newline at end of file diff --git a/core-shared-ui/build.gradle.kts b/core-shared-ui/build.gradle.kts index e3000310..69bfc15c 100644 --- a/core-shared-ui/build.gradle.kts +++ b/core-shared-ui/build.gradle.kts @@ -7,7 +7,7 @@ plugins { kotlin { sourceSets { - androidMain.dependencies { + commonMain.dependencies { implementation(projects.coreShared) implementation(compose.material3) implementation(compose.preview) @@ -18,9 +18,6 @@ kotlin { implementation(libs.bundles.androidSupport) implementation(libs.android.youtube.player) implementation(libs.coil) - } - - commonMain.dependencies { implementation(libs.paging.compose) } } diff --git a/core-shared-ui/src/androidMain/kotlin/com/codandotv/streamplayerapp/core_shared_ui/resources/Colors.kt b/core-shared-ui/src/commonMain/kotlin/com/codandotv/streamplayerapp/core_shared_ui/resources/Colors.kt similarity index 100% rename from core-shared-ui/src/androidMain/kotlin/com/codandotv/streamplayerapp/core_shared_ui/resources/Colors.kt rename to core-shared-ui/src/commonMain/kotlin/com/codandotv/streamplayerapp/core_shared_ui/resources/Colors.kt diff --git a/core-shared-ui/src/androidMain/kotlin/com/codandotv/streamplayerapp/core_shared_ui/theme/StreamPlayerTheme.kt b/core-shared-ui/src/commonMain/kotlin/com/codandotv/streamplayerapp/core_shared_ui/theme/StreamPlayerTheme.kt similarity index 100% rename from core-shared-ui/src/androidMain/kotlin/com/codandotv/streamplayerapp/core_shared_ui/theme/StreamPlayerTheme.kt rename to core-shared-ui/src/commonMain/kotlin/com/codandotv/streamplayerapp/core_shared_ui/theme/StreamPlayerTheme.kt diff --git a/core-shared-ui/src/androidMain/kotlin/com/codandotv/streamplayerapp/core_shared_ui/theme/ThemePreviews.kt b/core-shared-ui/src/commonMain/kotlin/com/codandotv/streamplayerapp/core_shared_ui/theme/ThemePreviews.kt similarity index 100% rename from core-shared-ui/src/androidMain/kotlin/com/codandotv/streamplayerapp/core_shared_ui/theme/ThemePreviews.kt rename to core-shared-ui/src/commonMain/kotlin/com/codandotv/streamplayerapp/core_shared_ui/theme/ThemePreviews.kt diff --git a/core-shared-ui/src/androidMain/kotlin/com/codandotv/streamplayerapp/core_shared_ui/utils/Sharing.kt b/core-shared-ui/src/commonMain/kotlin/com/codandotv/streamplayerapp/core_shared_ui/utils/Sharing.kt similarity index 100% rename from core-shared-ui/src/androidMain/kotlin/com/codandotv/streamplayerapp/core_shared_ui/utils/Sharing.kt rename to core-shared-ui/src/commonMain/kotlin/com/codandotv/streamplayerapp/core_shared_ui/utils/Sharing.kt diff --git a/core-shared-ui/src/androidMain/kotlin/com/codandotv/streamplayerapp/core_shared_ui/widget/IconWithText.kt b/core-shared-ui/src/commonMain/kotlin/com/codandotv/streamplayerapp/core_shared_ui/widget/IconWithText.kt similarity index 100% rename from core-shared-ui/src/androidMain/kotlin/com/codandotv/streamplayerapp/core_shared_ui/widget/IconWithText.kt rename to core-shared-ui/src/commonMain/kotlin/com/codandotv/streamplayerapp/core_shared_ui/widget/IconWithText.kt diff --git a/core-shared-ui/src/androidMain/kotlin/com/codandotv/streamplayerapp/core_shared_ui/widget/PlayerComponent.kt b/core-shared-ui/src/commonMain/kotlin/com/codandotv/streamplayerapp/core_shared_ui/widget/PlayerComponent.kt similarity index 100% rename from core-shared-ui/src/androidMain/kotlin/com/codandotv/streamplayerapp/core_shared_ui/widget/PlayerComponent.kt rename to core-shared-ui/src/commonMain/kotlin/com/codandotv/streamplayerapp/core_shared_ui/widget/PlayerComponent.kt diff --git a/core-shared-ui/src/androidMain/kotlin/com/codandotv/streamplayerapp/core_shared_ui/widget/SharingStreamCustomView.kt b/core-shared-ui/src/commonMain/kotlin/com/codandotv/streamplayerapp/core_shared_ui/widget/SharingStreamCustomView.kt similarity index 100% rename from core-shared-ui/src/androidMain/kotlin/com/codandotv/streamplayerapp/core_shared_ui/widget/SharingStreamCustomView.kt rename to core-shared-ui/src/commonMain/kotlin/com/codandotv/streamplayerapp/core_shared_ui/widget/SharingStreamCustomView.kt diff --git a/core-shared-ui/src/androidMain/kotlin/com/codandotv/streamplayerapp/core_shared_ui/widget/StreamPlayerTopBar.kt b/core-shared-ui/src/commonMain/kotlin/com/codandotv/streamplayerapp/core_shared_ui/widget/StreamPlayerTopBar.kt similarity index 100% rename from core-shared-ui/src/androidMain/kotlin/com/codandotv/streamplayerapp/core_shared_ui/widget/StreamPlayerTopBar.kt rename to core-shared-ui/src/commonMain/kotlin/com/codandotv/streamplayerapp/core_shared_ui/widget/StreamPlayerTopBar.kt diff --git a/core-shared-ui/src/androidMain/kotlin/com/codandotv/streamplayerapp/core_shared_ui/widget/StreamsCard.kt b/core-shared-ui/src/commonMain/kotlin/com/codandotv/streamplayerapp/core_shared_ui/widget/StreamsCard.kt similarity index 100% rename from core-shared-ui/src/androidMain/kotlin/com/codandotv/streamplayerapp/core_shared_ui/widget/StreamsCard.kt rename to core-shared-ui/src/commonMain/kotlin/com/codandotv/streamplayerapp/core_shared_ui/widget/StreamsCard.kt diff --git a/core-shared-ui/src/androidMain/kotlin/com/codandotv/streamplayerapp/core_shared_ui/widget/StreamsCarousel.kt b/core-shared-ui/src/commonMain/kotlin/com/codandotv/streamplayerapp/core_shared_ui/widget/StreamsCarousel.kt similarity index 100% rename from core-shared-ui/src/androidMain/kotlin/com/codandotv/streamplayerapp/core_shared_ui/widget/StreamsCarousel.kt rename to core-shared-ui/src/commonMain/kotlin/com/codandotv/streamplayerapp/core_shared_ui/widget/StreamsCarousel.kt From 132a5ca5030ea1363dffce94be90e9c2f72a83aa Mon Sep 17 00:00:00 2001 From: Rods Date: Fri, 31 Jan 2025 00:17:34 -0300 Subject: [PATCH 48/96] [ISSUE-9] - migration string resources: list, profile, search, coreshared-ui --- core-shared-ui/build.gradle.kts | 1 + .../androidMain/res/values-night/themes.xml | 2 +- .../res/values/content-description.xml | 10 ---- .../src/androidMain/res/values/strings.xml | 24 --------- .../composeResources/values/strings.xml | 35 +++++++++++++ .../resources/StringCoreSharedUi.kt | 23 +++++++++ .../widget/SharingStreamCustomView.kt | 46 ++++++++++------- .../widget/StreamPlayerTopBar.kt | 21 +++++--- feature-list-streams/build.gradle.kts | 31 ++++++------ .../res/layout/activity_list_stream.xml | 18 ------- .../res/values/content-description.xml | 8 --- .../composeResources}/values/strings.xml | 6 +++ .../feature_list_streams/core/ContentType.kt | 16 ++++-- .../list/domain/model/HighlightBanner.kt | 16 +++--- .../screens/ListStreamViewModel.kt | 15 ++++-- .../presentation/widgets/HighlightBanner.kt | 49 +++++++++++-------- feature-profile/build.gradle.kts | 1 + .../composeResources}/values/strings.xml | 0 .../screens/ProfilePickerStreamScreen.kt | 15 ++++-- .../widget/ProfilePickerProfilesGrid.kt | 9 ++-- .../ProfilePickerSelectedProfileContainer.kt | 11 +++-- feature-search/build.gradle.kts | 1 + .../composeResources}/values/strings.xml | 0 .../presentation/screens/SearchScreen.kt | 8 +-- .../presentation/widgets/SearchCarousel.kt | 18 ++++--- .../presentation/widgets/SearchStreams.kt | 20 ++++---- 26 files changed, 231 insertions(+), 173 deletions(-) delete mode 100644 core-shared-ui/src/androidMain/res/values/content-description.xml create mode 100644 core-shared-ui/src/commonMain/composeResources/values/strings.xml create mode 100644 core-shared-ui/src/commonMain/kotlin/com/codandotv/streamplayerapp/core_shared_ui/resources/StringCoreSharedUi.kt delete mode 100644 feature-list-streams/src/androidMain/res/layout/activity_list_stream.xml delete mode 100644 feature-list-streams/src/androidMain/res/values/content-description.xml rename feature-list-streams/src/{androidMain/res => commonMain/composeResources}/values/strings.xml (65%) rename feature-profile/src/{androidMain/res => commonMain/composeResources}/values/strings.xml (100%) rename feature-search/src/{androidMain/res => commonMain/composeResources}/values/strings.xml (100%) diff --git a/core-shared-ui/build.gradle.kts b/core-shared-ui/build.gradle.kts index 69bfc15c..789c397f 100644 --- a/core-shared-ui/build.gradle.kts +++ b/core-shared-ui/build.gradle.kts @@ -12,6 +12,7 @@ kotlin { implementation(compose.material3) implementation(compose.preview) implementation(compose.ui) + implementation(compose.components.resources) implementation(libs.navigation.compose) implementation(libs.bundles.koin) implementation(libs.bundles.kotlin) diff --git a/core-shared-ui/src/androidMain/res/values-night/themes.xml b/core-shared-ui/src/androidMain/res/values-night/themes.xml index 04c0b155..75508352 100644 --- a/core-shared-ui/src/androidMain/res/values-night/themes.xml +++ b/core-shared-ui/src/androidMain/res/values-night/themes.xml @@ -1,4 +1,4 @@ - + - diff --git a/core-shared-ui/src/androidMain/res/values-v31/themes.xml b/core-shared-ui/src/androidMain/res/values-v31/themes.xml index f234904b..d6657630 100644 --- a/core-shared-ui/src/androidMain/res/values-v31/themes.xml +++ b/core-shared-ui/src/androidMain/res/values-v31/themes.xml @@ -2,8 +2,9 @@ \ No newline at end of file diff --git a/core-shared-ui/src/androidMain/res/values/colors.xml b/core-shared-ui/src/androidMain/res/values/colors.xml deleted file mode 100644 index f8c6127d..00000000 --- a/core-shared-ui/src/androidMain/res/values/colors.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - #FFBB86FC - #FF6200EE - #FF3700B3 - #FF03DAC5 - #FF018786 - #FF000000 - #FFFFFFFF - \ No newline at end of file diff --git a/core-shared-ui/src/androidMain/res/values/themes.xml b/core-shared-ui/src/androidMain/res/values/themes.xml index 56f74635..4dfb4c91 100644 --- a/core-shared-ui/src/androidMain/res/values/themes.xml +++ b/core-shared-ui/src/androidMain/res/values/themes.xml @@ -1,22 +1,11 @@ \ No newline at end of file From f12c41d9e181421b0751674756d4a95b8c0e92e5 Mon Sep 17 00:00:00 2001 From: Rods Date: Sun, 2 Feb 2025 11:32:26 -0300 Subject: [PATCH 54/96] [ISSUE-13] - migration room --- build.gradle.kts | 3 +- core-local-storage/build.gradle.kts | 25 +++++++++---- .../database/AppDatabasePlatform.android.kt | 12 +++++++ .../data/database/AppDatabasePlatform.kt | 25 +++++++++++++ .../data/database/StreamPlayerAppDatabase.kt | 34 ------------------ .../di/LocalStorageModule.kt | 11 +++--- .../data/database/AppDatabasePlatform.ios.kt | 35 +++++++++++++++++++ gradle/libs.versions.toml | 13 ++++--- 8 files changed, 103 insertions(+), 55 deletions(-) create mode 100644 core-local-storage/src/androidMain/kotlin/com/codandotv/streamplayerapp/core_local_storage/data/database/AppDatabasePlatform.android.kt create mode 100644 core-local-storage/src/commonMain/kotlin/com/codandotv/streamplayerapp/core_local_storage/data/database/AppDatabasePlatform.kt delete mode 100644 core-local-storage/src/commonMain/kotlin/com/codandotv/streamplayerapp/core_local_storage/data/database/StreamPlayerAppDatabase.kt create mode 100644 core-local-storage/src/iosMain/kotlin/com/codandotv/streamplayerapp/core_local_storage/data/database/AppDatabasePlatform.ios.kt diff --git a/build.gradle.kts b/build.gradle.kts index 7597b231..099395bb 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -3,6 +3,7 @@ import java.net.URI @Suppress("DSL_SCOPE_VIOLATION") plugins { alias(libs.plugins.android.application) apply false + alias(libs.plugins.room) apply false alias(libs.plugins.android.library) apply false alias(libs.plugins.kotlin.android) apply false alias(libs.plugins.serialization) apply false @@ -18,8 +19,6 @@ tasks.register("clean", Delete::class) { delete(rootProject.buildDir) } - - allprojects { repositories { google() diff --git a/core-local-storage/build.gradle.kts b/core-local-storage/build.gradle.kts index 940bb96b..dd964194 100644 --- a/core-local-storage/build.gradle.kts +++ b/core-local-storage/build.gradle.kts @@ -1,6 +1,7 @@ plugins { id("com.streamplayer.kmp-library") id("com.google.devtools.ksp") + alias(libs.plugins.room) } kotlin { @@ -11,17 +12,27 @@ kotlin { implementation(libs.bundles.koin) } } + + listOf( + iosX64(), + iosArm64(), + iosSimulatorArm64() + ).forEach { iosTarget -> + iosTarget.binaries.framework { + baseName = "composeApp" + isStatic = true + } + } } dependencies { - add("kspCommonMainMetadata",libs.koin.ksp.compiler) + add("kspCommonMainMetadata", libs.koin.ksp.compiler) add("kspAndroid", libs.koin.ksp.compiler) + add("kspIosSimulatorArm64", libs.koin.ksp.compiler) + add("kspIosX64", libs.koin.ksp.compiler) + add("kspIosArm64", libs.koin.ksp.compiler) } -ksp { - arg("KOIN_CONFIG_CHECK","true") +room { + schemaDirectory("$projectDir/schemas") } - -dependencies { - ksp(libs.roomCompiler) -} \ No newline at end of file diff --git a/core-local-storage/src/androidMain/kotlin/com/codandotv/streamplayerapp/core_local_storage/data/database/AppDatabasePlatform.android.kt b/core-local-storage/src/androidMain/kotlin/com/codandotv/streamplayerapp/core_local_storage/data/database/AppDatabasePlatform.android.kt new file mode 100644 index 00000000..8540c5f0 --- /dev/null +++ b/core-local-storage/src/androidMain/kotlin/com/codandotv/streamplayerapp/core_local_storage/data/database/AppDatabasePlatform.android.kt @@ -0,0 +1,12 @@ +package com.codandotv.streamplayerapp.core_local_storage.data.database + +import androidx.room.Room +import org.koin.mp.KoinPlatform + +actual fun databaseInstance(): AppDatabase { + return Room.databaseBuilder( + KoinPlatform.getKoin().get(), + AppDatabase::class.java, + dbFileName + ).build() +} \ No newline at end of file diff --git a/core-local-storage/src/commonMain/kotlin/com/codandotv/streamplayerapp/core_local_storage/data/database/AppDatabasePlatform.kt b/core-local-storage/src/commonMain/kotlin/com/codandotv/streamplayerapp/core_local_storage/data/database/AppDatabasePlatform.kt new file mode 100644 index 00000000..64f0a620 --- /dev/null +++ b/core-local-storage/src/commonMain/kotlin/com/codandotv/streamplayerapp/core_local_storage/data/database/AppDatabasePlatform.kt @@ -0,0 +1,25 @@ +package com.codandotv.streamplayerapp.core_local_storage.data.database + +import androidx.room.ConstructedBy +import androidx.room.Database +import androidx.room.RoomDatabase +import androidx.room.RoomDatabaseConstructor +import com.codandotv.streamplayerapp.core_local_storage.data.dao.FavoriteDao +import com.codandotv.streamplayerapp.core_local_storage.domain.model.MovieEntity + + +@Database(entities = [MovieEntity::class], version = 1, exportSchema = false) +@ConstructedBy(AppDatabaseConstructor::class) +abstract class AppDatabase : RoomDatabase() { + abstract fun favoriteDao(): FavoriteDao +} + +// The Room compiler generates the `actual` implementations. +@Suppress("NO_ACTUAL_FOR_EXPECT") +expect object AppDatabaseConstructor : RoomDatabaseConstructor { + override fun initialize(): AppDatabase +} + +expect fun databaseInstance(): AppDatabase + +internal const val dbFileName = "app-database.db" \ No newline at end of file diff --git a/core-local-storage/src/commonMain/kotlin/com/codandotv/streamplayerapp/core_local_storage/data/database/StreamPlayerAppDatabase.kt b/core-local-storage/src/commonMain/kotlin/com/codandotv/streamplayerapp/core_local_storage/data/database/StreamPlayerAppDatabase.kt deleted file mode 100644 index d5d60723..00000000 --- a/core-local-storage/src/commonMain/kotlin/com/codandotv/streamplayerapp/core_local_storage/data/database/StreamPlayerAppDatabase.kt +++ /dev/null @@ -1,34 +0,0 @@ -package com.codandotv.streamplayerapp.core_local_storage.data.database - -import android.content.Context -import androidx.room.Database -import androidx.room.Room -import androidx.room.RoomDatabase -import com.codandotv.streamplayerapp.core_local_storage.data.dao.FavoriteDao -import com.codandotv.streamplayerapp.core_local_storage.domain.model.MovieEntity - -@Database(entities = [MovieEntity::class], version = 1) -abstract class StreamPlayerAppDatabase : RoomDatabase() { - - abstract fun favoriteDao(): FavoriteDao - - companion object { - - private var instance: StreamPlayerAppDatabase? = null - - fun getInstance(context: Context): StreamPlayerAppDatabase { - if (instance == null) { - synchronized(this) { - instance = Room.databaseBuilder( - context.applicationContext, - StreamPlayerAppDatabase::class.java, - DATABASE_NAME - ).build() - } - } - return instance!! - } - - const val DATABASE_NAME = "app-database.db" - } -} \ No newline at end of file diff --git a/core-local-storage/src/commonMain/kotlin/com/codandotv/streamplayerapp/core_local_storage/di/LocalStorageModule.kt b/core-local-storage/src/commonMain/kotlin/com/codandotv/streamplayerapp/core_local_storage/di/LocalStorageModule.kt index a42c8854..9d934c0d 100644 --- a/core-local-storage/src/commonMain/kotlin/com/codandotv/streamplayerapp/core_local_storage/di/LocalStorageModule.kt +++ b/core-local-storage/src/commonMain/kotlin/com/codandotv/streamplayerapp/core_local_storage/di/LocalStorageModule.kt @@ -1,15 +1,12 @@ package com.codandotv.streamplayerapp.core_local_storage.di -import androidx.room.Room -import com.codandotv.streamplayerapp.core_local_storage.data.database.StreamPlayerAppDatabase -import com.codandotv.streamplayerapp.core_local_storage.data.database.StreamPlayerAppDatabase.Companion.DATABASE_NAME -import org.koin.android.ext.koin.androidContext +import com.codandotv.streamplayerapp.core_local_storage.data.database.AppDatabase +import com.codandotv.streamplayerapp.core_local_storage.data.database.databaseInstance import org.koin.dsl.module object LocalStorageModule { val module = module { - single { Room.databaseBuilder(androidContext(), StreamPlayerAppDatabase::class.java, DATABASE_NAME).build() } - single { StreamPlayerAppDatabase.getInstance(get()) } - single { get().favoriteDao() } + single { databaseInstance() } + single { get().favoriteDao() } } } \ No newline at end of file diff --git a/core-local-storage/src/iosMain/kotlin/com/codandotv/streamplayerapp/core_local_storage/data/database/AppDatabasePlatform.ios.kt b/core-local-storage/src/iosMain/kotlin/com/codandotv/streamplayerapp/core_local_storage/data/database/AppDatabasePlatform.ios.kt new file mode 100644 index 00000000..469b746b --- /dev/null +++ b/core-local-storage/src/iosMain/kotlin/com/codandotv/streamplayerapp/core_local_storage/data/database/AppDatabasePlatform.ios.kt @@ -0,0 +1,35 @@ +package com.codandotv.streamplayerapp.core_local_storage.data.database + +import androidx.room.Room +import androidx.room.util.findDatabaseConstructorAndInitDatabaseImpl +import androidx.sqlite.driver.bundled.BundledSQLiteDriver +import kotlinx.cinterop.ExperimentalForeignApi +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.IO +import platform.Foundation.NSDocumentDirectory +import platform.Foundation.NSFileManager +import platform.Foundation.NSURL +import platform.Foundation.NSUserDomainMask + +@OptIn(ExperimentalForeignApi::class) +private fun documentDirectory(): String { + val documentDirectory: NSURL? = NSFileManager.defaultManager.URLForDirectory( + directory = NSDocumentDirectory, + inDomain = NSUserDomainMask, + appropriateForURL = null, + create = false, + error = null + ) + return requireNotNull(documentDirectory).path!! +} + +actual fun databaseInstance(): AppDatabase { + val dbFile = "${documentDirectory()}/$dbFileName" + return Room.databaseBuilder( + name = dbFile, + factory = { findDatabaseConstructorAndInitDatabaseImpl(AppDatabase::class) } + ).setDriver(BundledSQLiteDriver()) + .setQueryCoroutineContext(Dispatchers.IO) + .build() +} + diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index fe274061..f6cb3bc8 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -10,7 +10,7 @@ kover = "0.7.5" detekt = "1.23.6" compose_plugin_multiplataform = "1.7.1" navigation-compose-version = "2.7.0-alpha07" -paging-compose="3.3.5" +paging-compose = "3.3.5" #Test test_junit = "4.13.2" @@ -36,7 +36,9 @@ coil = "2.3.0" lottie = "5.2.0" #Room Database -room = "2.5.2" +room = "2.7.0-alpha13" +sqlite = "2.5.0-SNAPSHOT" + android_youtube_player_version = "12.0.0" uiAndroid = "1.7.5" junit = "1.2.1" @@ -105,7 +107,7 @@ ktor_client_content_serialization_json = { module = "io.ktor:ktor-serialization- #Room roomRuntime = { group = "androidx.room", name = "room-runtime", version.ref = "room" } roomCompiler = { group = "androidx.room", name = "room-compiler", version.ref = "room" } -roomKtx = { group = "androidx.room", name = "room-ktx", version.ref = "room" } +room_bundled = { group = "androidx.sqlite", name = "sqlite-bundled", version.ref = "sqlite" } android_youtube_player = { group = "com.pierfrancescosoffritti.androidyoutubeplayer", name = "core", version.ref = "android_youtube_player_version" } dokka = { group = "org.jetbrains.dokka", name = "android-documentation-plugin", version.ref = "dokka" } @@ -113,9 +115,9 @@ ext-junit = { group = "androidx.test.ext", name = "junit", version.ref = "junit" espresso-core = { group = "androidx.test.espresso", name = "espresso-core", version.ref = "espressoCore" } [bundles] -room = ["roomRuntime", "roomKtx"] +room = ["roomRuntime", "roomCompiler","room_bundled"] networking = ["moshi", "okhttp", "interceptor", "ktor_client_core", "ktor_client_okhttp", "ktor_client_content_serialization_json", "ktor_client_content_negotiation", "ktor_client_logger", "ktor_client_auth"] -koin = ["koin_android","koin_compose"] +koin = ["koin_android", "koin_compose"] test = ["junit", "mockk", "mockk_android", "viewmodel_test", "koin_test", "coroutines_test"] androidSupport = ["androidx_core", "androidx_appcompat", "androidx_dynamicanimation", "google_material"] @@ -131,6 +133,7 @@ serialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref jetbrains-compose = { id = "org.jetbrains.compose", version.ref = "compose_plugin_multiplataform" } kotlin_multiplatform = { id = "org.jetbrains.kotlin.multiplatform", version.ref = "kotlin" } compose_compiler = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" } +room = { id = "androidx.room", version.ref = "room" } ksp = { id = "com.google.devtools.ksp", version.ref = "ksp" } dokka = { id = "org.jetbrains.dokka", version.ref = "dokka" } From c2ad8a2cc8e5f49cdbdbf43feda7ef7a9c53f0b7 Mon Sep 17 00:00:00 2001 From: Rods Date: Sun, 2 Feb 2025 11:50:59 -0300 Subject: [PATCH 55/96] [ISSUE-13] - fix bug annotation --- core-local-storage/build.gradle.kts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/core-local-storage/build.gradle.kts b/core-local-storage/build.gradle.kts index dd964194..74cec138 100644 --- a/core-local-storage/build.gradle.kts +++ b/core-local-storage/build.gradle.kts @@ -33,6 +33,10 @@ dependencies { add("kspIosArm64", libs.koin.ksp.compiler) } +configurations.implementation{ + exclude(group = "com.intellij", module = "annotations") +} + room { schemaDirectory("$projectDir/schemas") } From f419cf2774acff8318854d9c666b6da26fcb15a8 Mon Sep 17 00:00:00 2001 From: Rods Date: Sun, 2 Feb 2025 15:25:44 -0300 Subject: [PATCH 56/96] [ISSUE-13] - fix bug room --- build.gradle.kts | 1 - core-local-storage/build.gradle.kts | 12 ++++++------ gradle/libs.versions.toml | 5 ++--- 3 files changed, 8 insertions(+), 10 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index 099395bb..a6c82513 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -3,7 +3,6 @@ import java.net.URI @Suppress("DSL_SCOPE_VIOLATION") plugins { alias(libs.plugins.android.application) apply false - alias(libs.plugins.room) apply false alias(libs.plugins.android.library) apply false alias(libs.plugins.kotlin.android) apply false alias(libs.plugins.serialization) apply false diff --git a/core-local-storage/build.gradle.kts b/core-local-storage/build.gradle.kts index 74cec138..e62f9b43 100644 --- a/core-local-storage/build.gradle.kts +++ b/core-local-storage/build.gradle.kts @@ -7,7 +7,8 @@ plugins { kotlin { sourceSets { commonMain.dependencies { - implementation(libs.bundles.room) + implementation(libs.room.bundled) + implementation(libs.room.runtime) implementation(libs.bundles.kotlin) implementation(libs.bundles.koin) } @@ -26,11 +27,10 @@ kotlin { } dependencies { - add("kspCommonMainMetadata", libs.koin.ksp.compiler) - add("kspAndroid", libs.koin.ksp.compiler) - add("kspIosSimulatorArm64", libs.koin.ksp.compiler) - add("kspIosX64", libs.koin.ksp.compiler) - add("kspIosArm64", libs.koin.ksp.compiler) + add("kspAndroid", libs.room.compiler) + add("kspIosSimulatorArm64", libs.room.compiler) + add("kspIosX64", libs.room.compiler) + add("kspIosArm64", libs.room.compiler) } configurations.implementation{ diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index f6cb3bc8..6eaaea92 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -105,8 +105,8 @@ ktor_client_content_negotiation = { module = "io.ktor:ktor-client-content-negoti ktor_client_content_serialization_json = { module = "io.ktor:ktor-serialization-kotlinx-json", version.ref = "ktor" } #Room -roomRuntime = { group = "androidx.room", name = "room-runtime", version.ref = "room" } -roomCompiler = { group = "androidx.room", name = "room-compiler", version.ref = "room" } +room_runtime = { group = "androidx.room", name = "room-runtime", version.ref = "room" } +room_compiler = { group = "androidx.room", name = "room-compiler", version.ref = "room" } room_bundled = { group = "androidx.sqlite", name = "sqlite-bundled", version.ref = "sqlite" } android_youtube_player = { group = "com.pierfrancescosoffritti.androidyoutubeplayer", name = "core", version.ref = "android_youtube_player_version" } @@ -115,7 +115,6 @@ ext-junit = { group = "androidx.test.ext", name = "junit", version.ref = "junit" espresso-core = { group = "androidx.test.espresso", name = "espresso-core", version.ref = "espressoCore" } [bundles] -room = ["roomRuntime", "roomCompiler","room_bundled"] networking = ["moshi", "okhttp", "interceptor", "ktor_client_core", "ktor_client_okhttp", "ktor_client_content_serialization_json", "ktor_client_content_negotiation", "ktor_client_logger", "ktor_client_auth"] koin = ["koin_android", "koin_compose"] From 48d9600a41b497c169107a21e9e2d7c2d989d6e2 Mon Sep 17 00:00:00 2001 From: Gabriel Bronzatti Moro Date: Sun, 2 Feb 2025 19:31:13 -0300 Subject: [PATCH 57/96] Solve local storage module --- core-local-storage/build.gradle.kts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/core-local-storage/build.gradle.kts b/core-local-storage/build.gradle.kts index e62f9b43..69743322 100644 --- a/core-local-storage/build.gradle.kts +++ b/core-local-storage/build.gradle.kts @@ -9,8 +9,7 @@ kotlin { commonMain.dependencies { implementation(libs.room.bundled) implementation(libs.room.runtime) - implementation(libs.bundles.kotlin) - implementation(libs.bundles.koin) + implementation(libs.koin.core) } } From 9233012865bfe1f07ebe3536d96a207943eadca9 Mon Sep 17 00:00:00 2001 From: Gabriel Bronzatti Moro Date: Sun, 2 Feb 2025 23:12:59 -0300 Subject: [PATCH 58/96] Migrate core-networking to androidMain --- core-networking/build.gradle.kts | 17 +++- .../HttpClientEngineProvider.android.kt | 7 ++ .../HttpClientEngineProvider.kt | 7 ++ .../core_networking/di/NetworkModule.kt | 79 +++++++------------ .../handleError/HttpClientConfigExtensions.kt | 40 ---------- 5 files changed, 56 insertions(+), 94 deletions(-) create mode 100644 core-networking/src/androidMain/kotlin/com/codandotv/streamplayerapp/core_networking/HttpClientEngineProvider.android.kt create mode 100644 core-networking/src/commonMain/kotlin/com/codandotv/streamplayerapp/core_networking/HttpClientEngineProvider.kt diff --git a/core-networking/build.gradle.kts b/core-networking/build.gradle.kts index e8ebc126..9f86234e 100644 --- a/core-networking/build.gradle.kts +++ b/core-networking/build.gradle.kts @@ -26,10 +26,21 @@ android { kotlin { sourceSets { commonMain.dependencies { - implementation(libs.bundles.kotlin) - implementation(libs.bundles.networking) - implementation(libs.bundles.koin) + implementation(libs.kotlin.stdlib) + implementation(libs.koin.core) + implementation(libs.ktor.client.core) + implementation(libs.ktor.client.content.serialization.json) + implementation(libs.ktor.client.content.negotiation) + implementation(libs.ktor.client.logger) + implementation(libs.ktor.client.auth) implementation(compose.components.resources) + implementation(compose.runtime) + } + + androidMain.dependencies { + implementation(libs.okhttp) + implementation(libs.interceptor) + implementation(libs.ktor.client.okhttp) } } } \ No newline at end of file diff --git a/core-networking/src/androidMain/kotlin/com/codandotv/streamplayerapp/core_networking/HttpClientEngineProvider.android.kt b/core-networking/src/androidMain/kotlin/com/codandotv/streamplayerapp/core_networking/HttpClientEngineProvider.android.kt new file mode 100644 index 00000000..04598d01 --- /dev/null +++ b/core-networking/src/androidMain/kotlin/com/codandotv/streamplayerapp/core_networking/HttpClientEngineProvider.android.kt @@ -0,0 +1,7 @@ +package com.codandotv.streamplayerapp.core_networking + +import io.ktor.client.engine.HttpClientEngine +import io.ktor.client.engine.okhttp.OkHttpConfig +import io.ktor.client.engine.okhttp.OkHttpEngine + +actual fun httpClientEngine() : HttpClientEngine = OkHttpEngine(OkHttpConfig()) \ No newline at end of file diff --git a/core-networking/src/commonMain/kotlin/com/codandotv/streamplayerapp/core_networking/HttpClientEngineProvider.kt b/core-networking/src/commonMain/kotlin/com/codandotv/streamplayerapp/core_networking/HttpClientEngineProvider.kt new file mode 100644 index 00000000..a8f99a4a --- /dev/null +++ b/core-networking/src/commonMain/kotlin/com/codandotv/streamplayerapp/core_networking/HttpClientEngineProvider.kt @@ -0,0 +1,7 @@ +@file:Suppress("EXPECT_AND_ACTUAL_IN_THE_SAME_MODULE") + +package com.codandotv.streamplayerapp.core_networking + +import io.ktor.client.engine.HttpClientEngine + +expect fun httpClientEngine(): HttpClientEngine \ No newline at end of file diff --git a/core-networking/src/commonMain/kotlin/com/codandotv/streamplayerapp/core_networking/di/NetworkModule.kt b/core-networking/src/commonMain/kotlin/com/codandotv/streamplayerapp/core_networking/di/NetworkModule.kt index ddceb396..8325ce22 100644 --- a/core-networking/src/commonMain/kotlin/com/codandotv/streamplayerapp/core_networking/di/NetworkModule.kt +++ b/core-networking/src/commonMain/kotlin/com/codandotv/streamplayerapp/core_networking/di/NetworkModule.kt @@ -3,13 +3,8 @@ package com.codandotv.streamplayerapp.core_networking.di import android.util.Log import com.codandotv.streamplayerapp.core.networking.BuildConfig import com.codandotv.streamplayerapp.core_networking.di.Network.TIMEOUT -import com.codandotv.streamplayerapp.core_networking.handleError.validator -import com.squareup.moshi.Moshi +import com.codandotv.streamplayerapp.core_networking.httpClientEngine import io.ktor.client.HttpClient -import io.ktor.client.HttpClientConfig -import io.ktor.client.engine.okhttp.OkHttp -import io.ktor.client.engine.okhttp.OkHttpConfig -import io.ktor.client.plugins.HttpResponseValidator import io.ktor.client.plugins.HttpTimeout import io.ktor.client.plugins.auth.Auth import io.ktor.client.plugins.auth.providers.BearerTokens @@ -23,9 +18,7 @@ import io.ktor.client.request.accept import io.ktor.http.ContentType import io.ktor.http.contentType import io.ktor.serialization.kotlinx.json.json -import kotlinx.serialization.ExperimentalSerializationApi import kotlinx.serialization.json.Json -import okhttp3.Interceptor import org.koin.dsl.module object NetworkModule { @@ -35,69 +28,53 @@ object NetworkModule { single { provideKtorHttpClient( - moshi = get(), baseUrl = get(QualifierHost), ) } single(QualifierProfileHttpClient) { provideKtorHttpClient( - moshi = get(), baseUrl = get(QualifierProfile), ) } - - single { Moshi.Builder().build() } } private fun provideKtorHttpClient( - moshi: Moshi, baseUrl: String, ): HttpClient { - return HttpClient(OkHttp) { - installPlugins(moshi, baseUrl) - } - } - - private fun HttpClientConfig.installPlugins( - moshi: Moshi, - baseUrl: String, - ) { - expectSuccess = false - - install(ContentNegotiation) { - json(Json { - explicitNulls = false - ignoreUnknownKeys = true - }) - } + return HttpClient(engine = httpClientEngine()) { + expectSuccess = false - install(HttpTimeout) { - socketTimeoutMillis = TIMEOUT - requestTimeoutMillis = TIMEOUT - connectTimeoutMillis = TIMEOUT - } + install(ContentNegotiation) { + json(Json { + explicitNulls = false + ignoreUnknownKeys = true + }) + } - defaultRequest { - url(baseUrl) - contentType(ContentType.Application.Json) - accept(ContentType.Application.Json) - } + install(HttpTimeout) { + socketTimeoutMillis = TIMEOUT + requestTimeoutMillis = TIMEOUT + connectTimeoutMillis = TIMEOUT + } - validator(moshi) + defaultRequest { + url(baseUrl) + contentType(ContentType.Application.Json) + accept(ContentType.Application.Json) + } - install(Auth) { - bearer { - loadTokens { - BearerTokens( - accessToken = BuildConfig.API_BEARER_AUTH, - refreshToken = "" - ) + install(Auth) { + bearer { + loadTokens { + BearerTokens( + accessToken = BuildConfig.API_BEARER_AUTH, + refreshToken = "" + ) + } } } - } - if (BuildConfig.DEBUG) { install(Logging) { level = LogLevel.ALL logger = object : Logger { @@ -110,6 +87,6 @@ object NetworkModule { } } -internal object Network{ +internal object Network { const val TIMEOUT = 10000L } \ No newline at end of file diff --git a/core-networking/src/commonMain/kotlin/com/codandotv/streamplayerapp/core_networking/handleError/HttpClientConfigExtensions.kt b/core-networking/src/commonMain/kotlin/com/codandotv/streamplayerapp/core_networking/handleError/HttpClientConfigExtensions.kt index ec896a04..5ccadb2c 100644 --- a/core-networking/src/commonMain/kotlin/com/codandotv/streamplayerapp/core_networking/handleError/HttpClientConfigExtensions.kt +++ b/core-networking/src/commonMain/kotlin/com/codandotv/streamplayerapp/core_networking/handleError/HttpClientConfigExtensions.kt @@ -1,54 +1,14 @@ package com.codandotv.streamplayerapp.core_networking.handleError -import android.util.Log -import com.squareup.moshi.Moshi import io.ktor.client.HttpClient -import io.ktor.client.HttpClientConfig import io.ktor.client.call.body -import io.ktor.client.engine.okhttp.OkHttpConfig import io.ktor.client.plugins.ClientRequestException -import io.ktor.client.plugins.HttpResponseValidator import io.ktor.client.request.HttpRequestBuilder import io.ktor.client.request.request import io.ktor.client.statement.bodyAsText import io.ktor.utils.io.errors.IOException import kotlinx.serialization.SerializationException -internal fun HttpClientConfig.validator( - moshi: Moshi -) { - HttpResponseValidator { - validateResponse { response -> - when (response.status.value) { - in 200..299 -> Unit - in 400..499 -> { - val errorBody = response.bodyAsText() - val error = try { - moshi.adapter(NetworkResponse.Error::class.java).fromJson(errorBody) - } catch (e: Exception) { - throw Failure.UnparsableResponseException(throwable = e) - } - throw Failure.ClientException(throwable = error?.exception) - } - - in 500..599 -> throw Failure.ServerError(response.status.value) - else -> throw Failure.UnknownError(response.status.value) - } - } - - handleResponseExceptionWithRequest { exception, _ -> - Log.d("HttpClientError",exception.stackTraceToString()) - - when (exception) { - is IOException -> throw Failure.NetworkError(throwable = exception) - else -> throw Failure.GenericError( - msg = exception.message ?: "Unknown error" - ) - } - } - } -} - suspend inline fun HttpClient.safeRequest( block: HttpRequestBuilder.() -> Unit, ): NetworkResponse = From 89717f76fc172204601696a9dbe031a474f0d3fd Mon Sep 17 00:00:00 2001 From: Gabriel Bronzatti Moro Date: Sun, 2 Feb 2025 23:26:02 -0300 Subject: [PATCH 59/96] Migrate core-networking to androidMain --- build-logic/src/main/java/Config.kt | 10 +++----- build.gradle.kts | 1 + core-networking/build.gradle.kts | 24 +++++++------------ .../core_networking/di/NetworkModule.kt | 8 +++---- gradle/libs.versions.toml | 4 +++- 5 files changed, 20 insertions(+), 27 deletions(-) diff --git a/build-logic/src/main/java/Config.kt b/build-logic/src/main/java/Config.kt index 4c699b1a..e21583a3 100644 --- a/build-logic/src/main/java/Config.kt +++ b/build-logic/src/main/java/Config.kt @@ -8,17 +8,13 @@ object Config { const val testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" object BuildField { - const val host_debug = "\"https://api.themoviedb.org/3/\"" - const val host_release = "\"https://api.themoviedb.org/3/\"" - const val api_profile_debug = "\"https://demo3364084.mockable.io/\"" - const val api_profile_release = "\"https://demo3364084.mockable.io/\"" + const val host = "https://api.themoviedb.org/3/" + const val api_profile = "https://demo3364084.mockable.io/" private const val tmdb_token_name_debug = "TMDB_BEARER_TOKEN_DEBUG" - private const val tmdb_token_name_release = "TMDB_BEARER_TOKEN_RELEASE" private const val bearear_without_environment = "eyJhbGciOiJIUzI1NiJ9.eyJhdWQiOiJiNDg2NWM4YTAzNzhmM2I4NjI0OWU1ZjNiYWFiMjU2NyIsInN1YiI6IjY0Mjk4YTg5YTNlNGJhMWM0NDgzM2U4OCIsInNjb3BlcyI6WyJhcGlfcmVhZCJdLCJ2ZXJzaW9uIjoxfQ.9cIxv29vkaZ2yW88DIFRUFK_nXbK2b6KS8t96kA8WAE" - val api_bearer_debug = "\"${System.getenv(tmdb_token_name_debug) ?: bearear_without_environment}\"" - val api_bearer_release = "\"${System.getenv(tmdb_token_name_release) ?: bearear_without_environment}\"" + val api_bearer = System.getenv(tmdb_token_name_debug) ?: bearear_without_environment } } \ No newline at end of file diff --git a/build.gradle.kts b/build.gradle.kts index a6c82513..4e1f6f7b 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -12,6 +12,7 @@ plugins { alias(libs.plugins.ksp) apply false alias(libs.plugins.dokka) apply false alias(libs.plugins.kover) apply false + alias(libs.plugins.buildkonfig.plugin) apply false } tasks.register("clean", Delete::class) { diff --git a/core-networking/build.gradle.kts b/core-networking/build.gradle.kts index 9f86234e..f30e603a 100644 --- a/core-networking/build.gradle.kts +++ b/core-networking/build.gradle.kts @@ -1,25 +1,19 @@ +import com.codingfeline.buildkonfig.compiler.FieldSpec + plugins { id("com.streamplayer.kmp-library") alias(libs.plugins.jetbrains.compose) alias(libs.plugins.compose.compiler) + alias(libs.plugins.buildkonfig.plugin) } -android { - buildFeatures { - buildConfig = true - } - buildTypes { - debug { - buildConfigField("String", "HOST", Config.BuildField.host_debug) - buildConfigField("String", "API_BEARER_AUTH", Config.BuildField.api_bearer_debug) - buildConfigField("String", "PROFILE", Config.BuildField.api_profile_debug) +buildkonfig { + packageName = "core.networking" - } - getByName("release") { - buildConfigField("String", "HOST", Config.BuildField.host_release) - buildConfigField("String", "API_BEARER_AUTH", Config.BuildField.api_bearer_release) - buildConfigField("String", "PROFILE", Config.BuildField.api_profile_release) - } + defaultConfigs { + buildConfigField(FieldSpec.Type.STRING, "HOST", Config.BuildField.host) + buildConfigField(FieldSpec.Type.STRING, "API_BEARER_AUTH", Config.BuildField.api_bearer) + buildConfigField(FieldSpec.Type.STRING, "PROFILE", Config.BuildField.api_profile) } } diff --git a/core-networking/src/commonMain/kotlin/com/codandotv/streamplayerapp/core_networking/di/NetworkModule.kt b/core-networking/src/commonMain/kotlin/com/codandotv/streamplayerapp/core_networking/di/NetworkModule.kt index 8325ce22..aca7f32e 100644 --- a/core-networking/src/commonMain/kotlin/com/codandotv/streamplayerapp/core_networking/di/NetworkModule.kt +++ b/core-networking/src/commonMain/kotlin/com/codandotv/streamplayerapp/core_networking/di/NetworkModule.kt @@ -1,9 +1,9 @@ package com.codandotv.streamplayerapp.core_networking.di import android.util.Log -import com.codandotv.streamplayerapp.core.networking.BuildConfig import com.codandotv.streamplayerapp.core_networking.di.Network.TIMEOUT import com.codandotv.streamplayerapp.core_networking.httpClientEngine +import core.networking.BuildKonfig import io.ktor.client.HttpClient import io.ktor.client.plugins.HttpTimeout import io.ktor.client.plugins.auth.Auth @@ -23,8 +23,8 @@ import org.koin.dsl.module object NetworkModule { val module = module { - single(QualifierHost) { BuildConfig.HOST } - single(QualifierProfile) { BuildConfig.PROFILE } + single(QualifierHost) { BuildKonfig.HOST } + single(QualifierProfile) { BuildKonfig.PROFILE } single { provideKtorHttpClient( @@ -68,7 +68,7 @@ object NetworkModule { bearer { loadTokens { BearerTokens( - accessToken = BuildConfig.API_BEARER_AUTH, + accessToken = BuildKonfig.API_BEARER_AUTH, refreshToken = "" ) } diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 6eaaea92..03cb4d4f 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -11,6 +11,7 @@ detekt = "1.23.6" compose_plugin_multiplataform = "1.7.1" navigation-compose-version = "2.7.0-alpha07" paging-compose = "3.3.5" +buildkonfig = "0.15.2" #Test test_junit = "4.13.2" @@ -138,4 +139,5 @@ ksp = { id = "com.google.devtools.ksp", version.ref = "ksp" } dokka = { id = "org.jetbrains.dokka", version.ref = "dokka" } kover = { id = "org.jetbrains.kotlinx.kover", version.ref = "kover" } -detekt = { id = "io.gitlab.arturbosch.detekt", version.ref = "detekt" } \ No newline at end of file +detekt = { id = "io.gitlab.arturbosch.detekt", version.ref = "detekt" } +buildkonfig_plugin = { id = "com.codingfeline.buildkonfig", version.ref = "buildkonfig" } From 881624df862e24099bd14f62336f9ee9816eb971 Mon Sep 17 00:00:00 2001 From: Gabriel Bronzatti Moro Date: Sun, 2 Feb 2025 23:28:41 -0300 Subject: [PATCH 60/96] Migrate core-local-storage to androidMain --- core-local-storage/build.gradle.kts | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/core-local-storage/build.gradle.kts b/core-local-storage/build.gradle.kts index 69743322..56092c54 100644 --- a/core-local-storage/build.gradle.kts +++ b/core-local-storage/build.gradle.kts @@ -12,24 +12,10 @@ kotlin { implementation(libs.koin.core) } } - - listOf( - iosX64(), - iosArm64(), - iosSimulatorArm64() - ).forEach { iosTarget -> - iosTarget.binaries.framework { - baseName = "composeApp" - isStatic = true - } - } } dependencies { add("kspAndroid", libs.room.compiler) - add("kspIosSimulatorArm64", libs.room.compiler) - add("kspIosX64", libs.room.compiler) - add("kspIosArm64", libs.room.compiler) } configurations.implementation{ From 331540d69eca01c3cfefe51e4369c2e7c0346e19 Mon Sep 17 00:00:00 2001 From: Gabriel Bronzatti Moro Date: Mon, 3 Feb 2025 07:42:21 -0300 Subject: [PATCH 61/96] Remove unnecessary annotations + add ktor dependencies to the client modules --- feature-detail/build.gradle.kts | 4 +++- feature-list-streams/build.gradle.kts | 4 +++- feature-profile/build.gradle.kts | 5 +++-- .../profile/data/ProfilePickerStreamRepository.kt | 2 -- .../profile/domain/ProfilePickerStreamUseCase.kt | 2 -- .../screens/ProfilePickerStreamViewModel.kt | 2 -- feature-search/build.gradle.kts | 4 +++- .../data/model/ListSearchStreamResponse.kt | 12 ++++++------ gradle/libs.versions.toml | 5 ----- 9 files changed, 18 insertions(+), 22 deletions(-) diff --git a/feature-detail/build.gradle.kts b/feature-detail/build.gradle.kts index 2c2392d8..0da20c8d 100644 --- a/feature-detail/build.gradle.kts +++ b/feature-detail/build.gradle.kts @@ -21,10 +21,12 @@ kotlin { implementation(compose.preview) implementation(libs.navigation.compose) implementation(libs.bundles.koin) - implementation(libs.bundles.networking) implementation(libs.coil) implementation(libs.bundles.androidSupport) implementation(compose.components.resources) + + implementation(libs.ktor.client.content.serialization.json) + implementation(libs.ktor.client.content.negotiation) } commonTest.dependencies { implementation(libs.bundles.test) diff --git a/feature-list-streams/build.gradle.kts b/feature-list-streams/build.gradle.kts index b5eb796b..e8f628d6 100644 --- a/feature-list-streams/build.gradle.kts +++ b/feature-list-streams/build.gradle.kts @@ -22,9 +22,11 @@ kotlin { implementation(compose.ui) implementation(compose.preview) implementation(libs.navigation.compose) - implementation(libs.bundles.networking) implementation(libs.coil) implementation(libs.bundles.androidSupport) + + implementation(libs.ktor.client.content.serialization.json) + implementation(libs.ktor.client.content.negotiation) } } } \ No newline at end of file diff --git a/feature-profile/build.gradle.kts b/feature-profile/build.gradle.kts index 37880dfa..1dd0accc 100644 --- a/feature-profile/build.gradle.kts +++ b/feature-profile/build.gradle.kts @@ -19,11 +19,12 @@ kotlin { implementation(compose.components.resources) implementation(libs.bundles.koin) - api(libs.koin.annotations) - implementation(libs.bundles.networking) implementation(libs.bundles.androidSupport) implementation(libs.coil) + implementation(libs.ktor.client.content.serialization.json) + implementation(libs.ktor.client.content.negotiation) + } } } \ No newline at end of file diff --git a/feature-profile/src/commonMain/kotlin/com/codandotv/streamplayerapp/profile/data/ProfilePickerStreamRepository.kt b/feature-profile/src/commonMain/kotlin/com/codandotv/streamplayerapp/profile/data/ProfilePickerStreamRepository.kt index 6474a36c..91e2404f 100644 --- a/feature-profile/src/commonMain/kotlin/com/codandotv/streamplayerapp/profile/data/ProfilePickerStreamRepository.kt +++ b/feature-profile/src/commonMain/kotlin/com/codandotv/streamplayerapp/profile/data/ProfilePickerStreamRepository.kt @@ -8,13 +8,11 @@ import com.codandotv.streamplayerapp.profile.domain.toProfiles import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.flow.map -import org.koin.core.annotation.Factory interface ProfilePickerStreamRepository { suspend fun getProfiles(): Flow> } -@Factory class ProfilePickerStreamRepositoryImpl( private val service: ProfilePickerStreamService ) : ProfilePickerStreamRepository { diff --git a/feature-profile/src/commonMain/kotlin/com/codandotv/streamplayerapp/profile/domain/ProfilePickerStreamUseCase.kt b/feature-profile/src/commonMain/kotlin/com/codandotv/streamplayerapp/profile/domain/ProfilePickerStreamUseCase.kt index 490d855b..abf1a4a6 100644 --- a/feature-profile/src/commonMain/kotlin/com/codandotv/streamplayerapp/profile/domain/ProfilePickerStreamUseCase.kt +++ b/feature-profile/src/commonMain/kotlin/com/codandotv/streamplayerapp/profile/domain/ProfilePickerStreamUseCase.kt @@ -3,7 +3,6 @@ package com.codandotv.streamplayerapp.profile.domain import com.codandotv.streamplayerapp.profile.data.ProfilePickerStreamRepository import kotlinx.coroutines.flow.Flow -import org.koin.core.annotation.Factory interface ProfilePickerStreamUseCase { suspend fun getProfile(): Flow> @@ -22,7 +21,6 @@ interface ProfilePickerStreamUseCase { ): Pair } -@Factory class ProfilePickerStreamUseCaseImpl( private val profilePickerStreamRepository: ProfilePickerStreamRepository ) : ProfilePickerStreamUseCase { diff --git a/feature-profile/src/commonMain/kotlin/com/codandotv/streamplayerapp/profile/presentation/screens/ProfilePickerStreamViewModel.kt b/feature-profile/src/commonMain/kotlin/com/codandotv/streamplayerapp/profile/presentation/screens/ProfilePickerStreamViewModel.kt index 5e1c9b59..75b5a2b6 100644 --- a/feature-profile/src/commonMain/kotlin/com/codandotv/streamplayerapp/profile/presentation/screens/ProfilePickerStreamViewModel.kt +++ b/feature-profile/src/commonMain/kotlin/com/codandotv/streamplayerapp/profile/presentation/screens/ProfilePickerStreamViewModel.kt @@ -13,9 +13,7 @@ import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.flow.update import kotlinx.coroutines.launch -import org.koin.android.annotation.KoinViewModel -@KoinViewModel class ProfilePickerStreamViewModel( private val useCase: ProfilePickerStreamUseCase, ) : ViewModel(), DefaultLifecycleObserver { diff --git a/feature-search/build.gradle.kts b/feature-search/build.gradle.kts index fd7adf4e..415c6dc1 100644 --- a/feature-search/build.gradle.kts +++ b/feature-search/build.gradle.kts @@ -23,9 +23,11 @@ kotlin { implementation(compose.preview) implementation(libs.navigation.compose) implementation(libs.bundles.koin) - implementation(libs.bundles.networking) implementation(libs.coil) implementation(libs.bundles.androidSupport) + + implementation(libs.ktor.client.content.serialization.json) + implementation(libs.ktor.client.content.negotiation) } } } \ No newline at end of file diff --git a/feature-search/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_search/data/model/ListSearchStreamResponse.kt b/feature-search/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_search/data/model/ListSearchStreamResponse.kt index 86baae53..88fa6d04 100644 --- a/feature-search/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_search/data/model/ListSearchStreamResponse.kt +++ b/feature-search/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_search/data/model/ListSearchStreamResponse.kt @@ -1,22 +1,22 @@ package com.codandotv.streamplayerapp.feature_search.data.model -import com.squareup.moshi.Json +import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable @Serializable data class ListSearchStreamResponse( - @Json(name = "results") + @SerialName("results") val results: List ) { @Serializable data class SearchStreamResponse( - @Json(name = "id") + @SerialName("id") val id: Int, - @Json(name = "title") + @SerialName("title") val title: String, - @Json(name="overview") + @SerialName("overview") val overview: String, - @Json(name = "poster_path") + @SerialName("poster_path") val posterPath: String? = null ) } diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 03cb4d4f..35a417ab 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -29,7 +29,6 @@ viewmodel = "2.8.6" androidx_core_ktx = "1.13.1" #Networking -moshi = "1.14.0" okhttp = "4.12.0" ktor = "3.0.1" @@ -89,12 +88,9 @@ google_material = { group = "com.google.android.material", name = "material", ve koin_test = { group = "io.insert-koin", name = "koin-test-junit4", version.ref = "koin" } koin_android = { group = "io.insert-koin", name = "koin-android", version.ref = "koin" } koin_core = { group = "io.insert-koin", name = "koin-core", version.ref = "koin" } -koin_annotations = { group = "io.insert-koin", name = "koin-annotations", version.ref = "koin-ksp" } -koin_ksp_compiler = { group = "io.insert-koin", name = "koin-ksp-compiler", version.ref = "koin-ksp" } koin_compose = { group = "io.insert-koin", name = "koin-androidx-compose", version.ref = "koin" } #Networking -moshi = { group = "com.squareup.moshi", name = "moshi-kotlin", version.ref = "moshi" } okhttp = { group = "com.squareup.okhttp3", name = "okhttp", version.ref = "okhttp" } interceptor = { group = "com.squareup.okhttp3", name = "logging-interceptor", version.ref = "okhttp" } @@ -116,7 +112,6 @@ ext-junit = { group = "androidx.test.ext", name = "junit", version.ref = "junit" espresso-core = { group = "androidx.test.espresso", name = "espresso-core", version.ref = "espressoCore" } [bundles] -networking = ["moshi", "okhttp", "interceptor", "ktor_client_core", "ktor_client_okhttp", "ktor_client_content_serialization_json", "ktor_client_content_negotiation", "ktor_client_logger", "ktor_client_auth"] koin = ["koin_android", "koin_compose"] test = ["junit", "mockk", "mockk_android", "viewmodel_test", "koin_test", "coroutines_test"] From 5905ff1a0ee7c6879bc23126b1bef4e81c4c386d Mon Sep 17 00:00:00 2001 From: Gabriel Bronzatti Moro Date: Mon, 3 Feb 2025 07:54:14 -0300 Subject: [PATCH 62/96] Format build.gradle dependencies --- composeApp/build.gradle.kts | 11 ++++++++--- core-local-storage/build.gradle.kts | 1 + core-navigation/build.gradle.kts | 2 +- core-networking/build.gradle.kts | 4 ++++ core-shared-ui/build.gradle.kts | 8 +++++--- core-shared/build.gradle.kts | 2 +- feature-detail/build.gradle.kts | 8 +++++--- feature-list-streams/build.gradle.kts | 10 +++++++--- feature-profile/build.gradle.kts | 6 ++++-- feature-search/build.gradle.kts | 9 ++++++--- gradle/libs.versions.toml | 4 ---- 11 files changed, 42 insertions(+), 23 deletions(-) diff --git a/composeApp/build.gradle.kts b/composeApp/build.gradle.kts index ee1a1a8c..183dc9ad 100644 --- a/composeApp/build.gradle.kts +++ b/composeApp/build.gradle.kts @@ -7,6 +7,9 @@ plugins { } kotlin { sourceSets { + androidMain.dependencies { + implementation(libs.koin.android) + } commonMain.dependencies { implementation(projects.featureListStreams) implementation(projects.featureDetail) @@ -19,13 +22,15 @@ kotlin { implementation(projects.coreLocalStorage) implementation(libs.navigation.compose) + implementation(compose.material3) implementation(compose.ui) implementation(compose.preview) - implementation(libs.bundles.koin) - implementation(libs.bundles.androidSupport) - implementation(libs.bundles.kotlin) + implementation(libs.lottie) + + implementation(libs.koin.core) + implementation(libs.koin.compose) } } } diff --git a/core-local-storage/build.gradle.kts b/core-local-storage/build.gradle.kts index 56092c54..cbc3aa8e 100644 --- a/core-local-storage/build.gradle.kts +++ b/core-local-storage/build.gradle.kts @@ -9,6 +9,7 @@ kotlin { commonMain.dependencies { implementation(libs.room.bundled) implementation(libs.room.runtime) + implementation(libs.koin.core) } } diff --git a/core-navigation/build.gradle.kts b/core-navigation/build.gradle.kts index d53cf82b..21fc6a77 100644 --- a/core-navigation/build.gradle.kts +++ b/core-navigation/build.gradle.kts @@ -8,8 +8,8 @@ plugins { kotlin { sourceSets { commonMain.dependencies { - implementation(libs.bundles.kotlin) implementation(libs.navigation.compose) + implementation(compose.material3) implementation(compose.components.resources) } diff --git a/core-networking/build.gradle.kts b/core-networking/build.gradle.kts index f30e603a..273aa0e4 100644 --- a/core-networking/build.gradle.kts +++ b/core-networking/build.gradle.kts @@ -21,19 +21,23 @@ kotlin { sourceSets { commonMain.dependencies { implementation(libs.kotlin.stdlib) + implementation(libs.koin.core) implementation(libs.ktor.client.core) implementation(libs.ktor.client.content.serialization.json) implementation(libs.ktor.client.content.negotiation) implementation(libs.ktor.client.logger) implementation(libs.ktor.client.auth) + implementation(compose.components.resources) implementation(compose.runtime) } androidMain.dependencies { implementation(libs.okhttp) + implementation(libs.interceptor) + implementation(libs.ktor.client.okhttp) } } diff --git a/core-shared-ui/build.gradle.kts b/core-shared-ui/build.gradle.kts index 2e111f7c..6f6a642f 100644 --- a/core-shared-ui/build.gradle.kts +++ b/core-shared-ui/build.gradle.kts @@ -9,16 +9,18 @@ kotlin { sourceSets { commonMain.dependencies { implementation(projects.coreShared) + implementation(compose.material3) implementation(compose.preview) implementation(compose.ui) implementation(compose.components.resources) + implementation(libs.navigation.compose) - implementation(libs.bundles.koin) - implementation(libs.bundles.kotlin) - implementation(libs.bundles.androidSupport) + implementation(libs.android.youtube.player) + implementation(libs.coil) + implementation(libs.paging.compose) } } diff --git a/core-shared/build.gradle.kts b/core-shared/build.gradle.kts index e4ee2dcd..692b2f9a 100644 --- a/core-shared/build.gradle.kts +++ b/core-shared/build.gradle.kts @@ -6,7 +6,7 @@ plugins { kotlin { sourceSets { commonMain.dependencies { - implementation(libs.bundles.koin) + implementation(libs.koin.core) } } } \ No newline at end of file diff --git a/feature-detail/build.gradle.kts b/feature-detail/build.gradle.kts index 0da20c8d..7b57b974 100644 --- a/feature-detail/build.gradle.kts +++ b/feature-detail/build.gradle.kts @@ -10,6 +10,8 @@ kotlin { sourceSets { commonMain.dependencies { implementation(libs.koin.core) + implementation(libs.koin.compose) + implementation(projects.coreNetworking) implementation(projects.coreNavigation) implementation(projects.coreShared) @@ -19,11 +21,11 @@ kotlin { implementation(compose.material3) implementation(compose.ui) implementation(compose.preview) + implementation(compose.components.resources) + implementation(libs.navigation.compose) - implementation(libs.bundles.koin) + implementation(libs.coil) - implementation(libs.bundles.androidSupport) - implementation(compose.components.resources) implementation(libs.ktor.client.content.serialization.json) implementation(libs.ktor.client.content.negotiation) diff --git a/feature-list-streams/build.gradle.kts b/feature-list-streams/build.gradle.kts index e8f628d6..da9998b1 100644 --- a/feature-list-streams/build.gradle.kts +++ b/feature-list-streams/build.gradle.kts @@ -9,8 +9,6 @@ plugins { kotlin { sourceSets { commonMain.dependencies { - implementation(libs.bundles.koin) - implementation(libs.paging.compose) implementation(projects.coreNetworking) implementation(projects.coreNavigation) implementation(projects.coreShared) @@ -21,12 +19,18 @@ kotlin { implementation(compose.material3) implementation(compose.ui) implementation(compose.preview) + + implementation(libs.paging.compose) + implementation(libs.navigation.compose) + implementation(libs.coil) - implementation(libs.bundles.androidSupport) implementation(libs.ktor.client.content.serialization.json) implementation(libs.ktor.client.content.negotiation) + + implementation(libs.koin.core) + implementation(libs.koin.compose) } } } \ No newline at end of file diff --git a/feature-profile/build.gradle.kts b/feature-profile/build.gradle.kts index 1dd0accc..781d5b15 100644 --- a/feature-profile/build.gradle.kts +++ b/feature-profile/build.gradle.kts @@ -13,18 +13,20 @@ kotlin { implementation(projects.coreNavigation) implementation(projects.coreShared) implementation(projects.coreSharedUi) + implementation(libs.navigation.compose) + implementation(compose.material3) implementation(compose.ui) implementation(compose.components.resources) - implementation(libs.bundles.koin) - implementation(libs.bundles.androidSupport) implementation(libs.coil) implementation(libs.ktor.client.content.serialization.json) implementation(libs.ktor.client.content.negotiation) + implementation(libs.koin.core) + implementation(libs.koin.compose) } } } \ No newline at end of file diff --git a/feature-search/build.gradle.kts b/feature-search/build.gradle.kts index 415c6dc1..08be24cd 100644 --- a/feature-search/build.gradle.kts +++ b/feature-search/build.gradle.kts @@ -10,7 +10,7 @@ kotlin { sourceSets { commonMain.dependencies { implementation(libs.paging.compose) - implementation(libs.koin.core) + implementation(projects.coreNetworking) implementation(projects.coreNavigation) implementation(projects.coreShared) @@ -21,13 +21,16 @@ kotlin { implementation(compose.material3) implementation(compose.ui) implementation(compose.preview) + implementation(libs.navigation.compose) - implementation(libs.bundles.koin) + implementation(libs.coil) - implementation(libs.bundles.androidSupport) implementation(libs.ktor.client.content.serialization.json) implementation(libs.ktor.client.content.negotiation) + + implementation(libs.koin.core) + implementation(libs.koin.compose) } } } \ No newline at end of file diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 35a417ab..c179d4cc 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -112,11 +112,7 @@ ext-junit = { group = "androidx.test.ext", name = "junit", version.ref = "junit" espresso-core = { group = "androidx.test.espresso", name = "espresso-core", version.ref = "espressoCore" } [bundles] -koin = ["koin_android", "koin_compose"] - test = ["junit", "mockk", "mockk_android", "viewmodel_test", "koin_test", "coroutines_test"] -androidSupport = ["androidx_core", "androidx_appcompat", "androidx_dynamicanimation", "google_material"] -kotlin = ["androidx_core", "kotlin_stdlib", "kotlin_reflect"] [plugins] android_application = { id = "com.android.application", version.ref = "android_gradle_plugin" } From 9eda7931f703261453f98c1f6540faed010611b2 Mon Sep 17 00:00:00 2001 From: Gabriel Bronzatti Moro Date: Mon, 3 Feb 2025 07:59:08 -0300 Subject: [PATCH 63/96] Dependencies cleanup --- gradle/libs.versions.toml | 33 +-------------------------------- 1 file changed, 1 insertion(+), 32 deletions(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index c179d4cc..57383d29 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -2,7 +2,6 @@ kotlin = "2.0.21" android_gradle_plugin = "8.2.2" koin = "3.5.3" -koin-ksp = "1.3.1" ksp = "2.0.21-1.0.27" dokka = "1.9.10" @@ -19,15 +18,6 @@ androidx_core_testing = "2.2.0" mockk = "1.13.7" kotlinx-coroutines-test = "1.8.1" -#Android Support -android_core_ktx = "1.7.0" -androidx_appcompat = "1.7.0" -material = "1.12.0" -dynamic_animation = "1.0.0" -constraint_motion = "2.1.3" -viewmodel = "2.8.6" -androidx_core_ktx = "1.13.1" - #Networking okhttp = "4.12.0" ktor = "3.0.1" @@ -40,9 +30,6 @@ room = "2.7.0-alpha13" sqlite = "2.5.0-SNAPSHOT" android_youtube_player_version = "12.0.0" -uiAndroid = "1.7.5" -junit = "1.2.1" -espressoCore = "3.6.1" [libraries] @@ -50,40 +37,25 @@ kotlin_gradle_plugin = { group = "org.jetbrains.kotlin", name = "kotlin-gradle-p android_gradle_plugin = { group = "com.android.tools.build", name = "gradle", version.ref = "android_gradle_plugin" } detekt-gradle-plugin = { module = "io.gitlab.arturbosch.detekt:detekt-gradle-plugin", version.ref = "detekt" } serialization = { module = "org.jetbrains.kotlin.plugin.serialization:org.jetbrains.kotlin.plugin.serialization.gradle.plugin", version.ref = "kotlin" } + com-google-devtools-ksp-gradle-plugin = { module = "com.google.devtools.ksp:com.google.devtools.ksp.gradle.plugin", version.ref = "ksp" } -#Kover kover-gradle-plugin = { module = "org.jetbrains.kotlinx:kover-gradle-plugin", version.ref = "kover" } -#Coil coil = { group = "io.coil-kt", name = "coil-compose", version.ref = "coil" } -#Lottie lottie = { group = "com.airbnb.android", name = "lottie-compose", version.ref = "lottie" } -# Kotlin kotlin_stdlib = { group = "org.jetbrains.kotlin", name = "kotlin-stdlib-jdk7", version.ref = "kotlin" } -kotlin_reflect = { group = "org.jetbrains.kotlin", name = "kotlin-reflect", version.ref = "kotlin" } - -# Android Support -androidx_core = { group = "androidx.core", name = "core-ktx", version.ref = "androidx_core_ktx" } -androidx_appcompat = { group = "androidx.appcompat", name = "appcompat", version.ref = "androidx_appcompat" } -androidx_dynamicanimation = { group = "androidx.dynamicanimation", name = "dynamicanimation", version.ref = "dynamic_animation" } -# Test junit = { group = "junit", name = "junit", version.ref = "test_junit" } mockk = { group = "io.mockk", name = "mockk", version.ref = "mockk" } mockk_android = { group = "io.mockk", name = "mockk-android", version.ref = "mockk" } viewmodel_test = { group = "androidx.arch.core", name = "core-testing", version.ref = "androidx_core_testing" } coroutines_test = { group = "org.jetbrains.kotlinx", name = "kotlinx-coroutines-test", version.ref = "kotlinx-coroutines-test" } - -#Navigation navigation-compose = { module = "org.jetbrains.androidx.navigation:navigation-compose", version.ref = "navigation-compose-version" } paging-compose = { module = "androidx.paging:paging-compose", version.ref = "paging-compose" } -# Google -google_material = { group = "com.google.android.material", name = "material", version.ref = "material" } - # Koin koin_test = { group = "io.insert-koin", name = "koin-test-junit4", version.ref = "koin" } koin_android = { group = "io.insert-koin", name = "koin-android", version.ref = "koin" } @@ -108,8 +80,6 @@ room_bundled = { group = "androidx.sqlite", name = "sqlite-bundled", version.ref android_youtube_player = { group = "com.pierfrancescosoffritti.androidyoutubeplayer", name = "core", version.ref = "android_youtube_player_version" } dokka = { group = "org.jetbrains.dokka", name = "android-documentation-plugin", version.ref = "dokka" } -ext-junit = { group = "androidx.test.ext", name = "junit", version.ref = "junit" } -espresso-core = { group = "androidx.test.espresso", name = "espresso-core", version.ref = "espressoCore" } [bundles] test = ["junit", "mockk", "mockk_android", "viewmodel_test", "koin_test", "coroutines_test"] @@ -130,5 +100,4 @@ ksp = { id = "com.google.devtools.ksp", version.ref = "ksp" } dokka = { id = "org.jetbrains.dokka", version.ref = "dokka" } kover = { id = "org.jetbrains.kotlinx.kover", version.ref = "kover" } -detekt = { id = "io.gitlab.arturbosch.detekt", version.ref = "detekt" } buildkonfig_plugin = { id = "com.codingfeline.buildkonfig", version.ref = "buildkonfig" } From aeddeaeaca11feb7aadc2ae08eb42f6d30c32258 Mon Sep 17 00:00:00 2001 From: Gabriel Bronzatti Moro Date: Mon, 3 Feb 2025 08:03:56 -0300 Subject: [PATCH 64/96] VideoPlayer dependency isolation --- .../widget/PlayerComponent.android.kt | 95 ++++++++++++++++ .../core_shared_ui/widget/PlayerComponent.kt | 101 +----------------- 2 files changed, 98 insertions(+), 98 deletions(-) create mode 100644 core-shared-ui/src/androidMain/kotlin/com/codandotv/streamplayerapp/core_shared_ui/widget/PlayerComponent.android.kt diff --git a/core-shared-ui/src/androidMain/kotlin/com/codandotv/streamplayerapp/core_shared_ui/widget/PlayerComponent.android.kt b/core-shared-ui/src/androidMain/kotlin/com/codandotv/streamplayerapp/core_shared_ui/widget/PlayerComponent.android.kt new file mode 100644 index 00000000..51fc8e77 --- /dev/null +++ b/core-shared-ui/src/androidMain/kotlin/com/codandotv/streamplayerapp/core_shared_ui/widget/PlayerComponent.android.kt @@ -0,0 +1,95 @@ +package com.codandotv.streamplayerapp.core_shared_ui.widget + +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.runtime.Composable +import androidx.compose.runtime.DisposableEffect +import androidx.compose.runtime.remember +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.unit.dp +import androidx.compose.ui.viewinterop.AndroidView +import com.pierfrancescosoffritti.androidyoutubeplayer.core.player.PlayerConstants +import com.pierfrancescosoffritti.androidyoutubeplayer.core.player.YouTubePlayer +import com.pierfrancescosoffritti.androidyoutubeplayer.core.player.listeners.YouTubePlayerListener +import com.pierfrancescosoffritti.androidyoutubeplayer.core.player.views.YouTubePlayerView + +@Composable +actual fun PlayerComponent(videoId: String, modifier: Modifier) { + + val context = LocalContext.current + + val youtubePlayerView = remember { + YouTubePlayerView(context).apply { + enableAutomaticInitialization = false + + addYouTubePlayerListener(object : YouTubePlayerListener { + override fun onApiChange(youTubePlayer: YouTubePlayer) = Unit + + override fun onCurrentSecond(youTubePlayer: YouTubePlayer, second: Float) = Unit + + override fun onError( + youTubePlayer: YouTubePlayer, + error: PlayerConstants.PlayerError + ) = Unit + + override fun onPlaybackQualityChange( + youTubePlayer: YouTubePlayer, + playbackQuality: PlayerConstants.PlaybackQuality + ) = Unit + + override fun onPlaybackRateChange( + youTubePlayer: YouTubePlayer, + playbackRate: PlayerConstants.PlaybackRate + ) = Unit + + override fun onReady(youTubePlayer: YouTubePlayer) { + youTubePlayer.loadVideo(videoId, 0f) + } + + override fun onStateChange( + youTubePlayer: YouTubePlayer, + state: PlayerConstants.PlayerState + ) = Unit + + override fun onVideoDuration(youTubePlayer: YouTubePlayer, duration: Float) = Unit + + override fun onVideoId(youTubePlayer: YouTubePlayer, videoId: String) = Unit + + override fun onVideoLoadedFraction( + youTubePlayer: YouTubePlayer, + loadedFraction: Float + ) = Unit + }) + } + } + + + Column( + modifier = modifier + .fillMaxSize() + ) { + Box { + AndroidView( + modifier = Modifier + .fillMaxWidth() + .height(200.dp) + .align(Alignment.TopCenter), + factory = { + youtubePlayerView + } + ) + } + } + + DisposableEffect( + key1 = Unit, + effect = { + onDispose { youtubePlayerView.release() } + }, + ) +} \ No newline at end of file diff --git a/core-shared-ui/src/commonMain/kotlin/com/codandotv/streamplayerapp/core_shared_ui/widget/PlayerComponent.kt b/core-shared-ui/src/commonMain/kotlin/com/codandotv/streamplayerapp/core_shared_ui/widget/PlayerComponent.kt index 3013210d..e11d12a3 100644 --- a/core-shared-ui/src/commonMain/kotlin/com/codandotv/streamplayerapp/core_shared_ui/widget/PlayerComponent.kt +++ b/core-shared-ui/src/commonMain/kotlin/com/codandotv/streamplayerapp/core_shared_ui/widget/PlayerComponent.kt @@ -1,105 +1,10 @@ +@file:Suppress("EXPECT_AND_ACTUAL_IN_THE_SAME_MODULE") + package com.codandotv.streamplayerapp.core_shared_ui.widget -import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.height import androidx.compose.runtime.Composable -import androidx.compose.runtime.DisposableEffect -import androidx.compose.runtime.remember -import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.platform.LocalContext -import androidx.compose.ui.unit.dp -import androidx.compose.ui.viewinterop.AndroidView -import com.codandotv.streamplayerapp.core_shared_ui.theme.ThemePreviews -import com.pierfrancescosoffritti.androidyoutubeplayer.core.player.PlayerConstants -import com.pierfrancescosoffritti.androidyoutubeplayer.core.player.YouTubePlayer -import com.pierfrancescosoffritti.androidyoutubeplayer.core.player.listeners.YouTubePlayerListener -import com.pierfrancescosoffritti.androidyoutubeplayer.core.player.views.YouTubePlayerView @Composable -fun PlayerComponent(videoId: String, modifier: Modifier = Modifier) { - - val context = LocalContext.current - - val youtubePlayerView = remember { - YouTubePlayerView(context).apply { - enableAutomaticInitialization = false - - addYouTubePlayerListener(object : YouTubePlayerListener { - override fun onApiChange(youTubePlayer: YouTubePlayer) = Unit - - override fun onCurrentSecond(youTubePlayer: YouTubePlayer, second: Float) = Unit - - override fun onError( - youTubePlayer: YouTubePlayer, - error: PlayerConstants.PlayerError - ) = Unit - - override fun onPlaybackQualityChange( - youTubePlayer: YouTubePlayer, - playbackQuality: PlayerConstants.PlaybackQuality - ) = Unit - - override fun onPlaybackRateChange( - youTubePlayer: YouTubePlayer, - playbackRate: PlayerConstants.PlaybackRate - ) = Unit - - override fun onReady(youTubePlayer: YouTubePlayer) { - youTubePlayer.loadVideo(videoId, 0f) - } +expect fun PlayerComponent(videoId: String, modifier: Modifier = Modifier) - override fun onStateChange( - youTubePlayer: YouTubePlayer, - state: PlayerConstants.PlayerState - ) = Unit - - override fun onVideoDuration(youTubePlayer: YouTubePlayer, duration: Float) = Unit - - override fun onVideoId(youTubePlayer: YouTubePlayer, videoId: String) = Unit - - override fun onVideoLoadedFraction( - youTubePlayer: YouTubePlayer, - loadedFraction: Float - ) = Unit - }) - } - } - - - Column( - modifier = modifier - .fillMaxSize() - ) { - Box { - AndroidView( - modifier = Modifier - .fillMaxWidth() - .height(200.dp) - .align(Alignment.TopCenter), - factory = { - youtubePlayerView - } - ) - } - } - - DisposableEffect( - key1 = Unit, - effect = { - onDispose { youtubePlayerView.release() } - }, - ) -} - -@Composable -@ThemePreviews -fun PlayerComponentPreview() { - PlayerComponent( - videoId = "BigBuckBunny.mp4", - modifier = Modifier.fillMaxWidth() - ) -} From 48b3de5b6f51543dcc4b3d7fa300a3d77d2a6eb2 Mon Sep 17 00:00:00 2001 From: Gabriel Bronzatti Moro Date: Mon, 3 Feb 2025 08:09:50 -0300 Subject: [PATCH 65/96] Enable iosTarget but not implementing it --- build-logic/src/main/java/Config.kt | 3 ++- .../com.streamplayer.application.gradle.kts | 3 +++ .../com.streamplayer.kmp-library.gradle.kts | 3 +++ .../java/extensions/KotlinMultiPlatformExt.kt | 17 +++++++++++++++++ core-local-storage/build.gradle.kts | 4 ---- 5 files changed, 25 insertions(+), 5 deletions(-) create mode 100644 build-logic/src/main/java/extensions/KotlinMultiPlatformExt.kt diff --git a/build-logic/src/main/java/Config.kt b/build-logic/src/main/java/Config.kt index e21583a3..2bcfd844 100644 --- a/build-logic/src/main/java/Config.kt +++ b/build-logic/src/main/java/Config.kt @@ -1,5 +1,6 @@ object Config { - const val applicationId = "com.codandotv.streamplayerapp" + const val appName = "streamplayerapp" + const val applicationId = "com.codandotv.$appName" const val compileSdkVersion = 34 const val minSdkVersion = 24 const val targetSdkVersion = 34 diff --git a/build-logic/src/main/java/com.streamplayer.application.gradle.kts b/build-logic/src/main/java/com.streamplayer.application.gradle.kts index b1bcd23f..3c0fac6e 100644 --- a/build-logic/src/main/java/com.streamplayer.application.gradle.kts +++ b/build-logic/src/main/java/com.streamplayer.application.gradle.kts @@ -2,6 +2,7 @@ import extensions.dokkaPlugin import extensions.getLibrary +import extensions.iosTarget import extensions.setupAndroidDefaultConfig import extensions.setupCompileOptions import extensions.setupPackingOptions @@ -28,6 +29,8 @@ kotlin { jvmTarget.set(JvmTarget.JVM_17) } } + + iosTarget() } android { diff --git a/build-logic/src/main/java/com.streamplayer.kmp-library.gradle.kts b/build-logic/src/main/java/com.streamplayer.kmp-library.gradle.kts index d865de17..5d5d4c7b 100644 --- a/build-logic/src/main/java/com.streamplayer.kmp-library.gradle.kts +++ b/build-logic/src/main/java/com.streamplayer.kmp-library.gradle.kts @@ -2,6 +2,7 @@ import extensions.dokkaPlugin import extensions.getLibrary +import extensions.iosTarget import extensions.setupAndroidDefaultConfig import extensions.setupCompileOptions import extensions.setupNameSpace @@ -29,6 +30,8 @@ kotlin { jvmTarget.set(JvmTarget.JVM_17) } } + + iosTarget() } android { diff --git a/build-logic/src/main/java/extensions/KotlinMultiPlatformExt.kt b/build-logic/src/main/java/extensions/KotlinMultiPlatformExt.kt new file mode 100644 index 00000000..bd263ef0 --- /dev/null +++ b/build-logic/src/main/java/extensions/KotlinMultiPlatformExt.kt @@ -0,0 +1,17 @@ +package extensions + +import Config +import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension + +fun KotlinMultiplatformExtension.iosTarget() { + listOf( + iosX64(), + iosArm64(), + iosSimulatorArm64() + ).forEach { iosTarget -> + iosTarget.binaries.framework { + baseName = Config.appName + isStatic = true + } + } +} diff --git a/core-local-storage/build.gradle.kts b/core-local-storage/build.gradle.kts index cbc3aa8e..2ffd79e4 100644 --- a/core-local-storage/build.gradle.kts +++ b/core-local-storage/build.gradle.kts @@ -19,10 +19,6 @@ dependencies { add("kspAndroid", libs.room.compiler) } -configurations.implementation{ - exclude(group = "com.intellij", module = "annotations") -} - room { schemaDirectory("$projectDir/schemas") } From 78783ab30c92d80c8954dd33dc9fdbd1fd690594 Mon Sep 17 00:00:00 2001 From: Gabriel Bronzatti Moro Date: Mon, 3 Feb 2025 08:18:42 -0300 Subject: [PATCH 66/96] Lottie is also android specific --- composeApp/build.gradle.kts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/composeApp/build.gradle.kts b/composeApp/build.gradle.kts index 183dc9ad..97f3e978 100644 --- a/composeApp/build.gradle.kts +++ b/composeApp/build.gradle.kts @@ -9,6 +9,7 @@ kotlin { sourceSets { androidMain.dependencies { implementation(libs.koin.android) + implementation(libs.lottie) } commonMain.dependencies { implementation(projects.featureListStreams) @@ -27,8 +28,6 @@ kotlin { implementation(compose.ui) implementation(compose.preview) - implementation(libs.lottie) - implementation(libs.koin.core) implementation(libs.koin.compose) } From 45264b748afca10533f1781fd6f7f78cb05d0d6d Mon Sep 17 00:00:00 2001 From: Gabriel Bronzatti Moro Date: Wed, 5 Feb 2025 16:04:33 -0300 Subject: [PATCH 67/96] Bringing back the build configs variables --- build-logic/src/main/java/Config.kt | 9 +++++++-- core-networking/build.gradle.kts | 12 +++++++++--- 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/build-logic/src/main/java/Config.kt b/build-logic/src/main/java/Config.kt index 2bcfd844..6624f13c 100644 --- a/build-logic/src/main/java/Config.kt +++ b/build-logic/src/main/java/Config.kt @@ -9,13 +9,18 @@ object Config { const val testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" object BuildField { - const val host = "https://api.themoviedb.org/3/" - const val api_profile = "https://demo3364084.mockable.io/" + const val host_debug = "https://api.themoviedb.org/3/" + const val host_release = "https://api.themoviedb.org/3/" + const val api_profile_debug = "https://demo3364084.mockable.io/" + const val api_profile_release = "https://demo3364084.mockable.io/" private const val tmdb_token_name_debug = "TMDB_BEARER_TOKEN_DEBUG" + private const val tmdb_token_name_release = "TMDB_BEARER_TOKEN_RELEASE" private const val bearear_without_environment = "eyJhbGciOiJIUzI1NiJ9.eyJhdWQiOiJiNDg2NWM4YTAzNzhmM2I4NjI0OWU1ZjNiYWFiMjU2NyIsInN1YiI6IjY0Mjk4YTg5YTNlNGJhMWM0NDgzM2U4OCIsInNjb3BlcyI6WyJhcGlfcmVhZCJdLCJ2ZXJzaW9uIjoxfQ.9cIxv29vkaZ2yW88DIFRUFK_nXbK2b6KS8t96kA8WAE" val api_bearer = System.getenv(tmdb_token_name_debug) ?: bearear_without_environment + val api_bearer_debug = System.getenv(tmdb_token_name_debug) ?: bearear_without_environment + val api_bearer_release = System.getenv(tmdb_token_name_release) ?: bearear_without_environment } } \ No newline at end of file diff --git a/core-networking/build.gradle.kts b/core-networking/build.gradle.kts index 273aa0e4..4bea4636 100644 --- a/core-networking/build.gradle.kts +++ b/core-networking/build.gradle.kts @@ -11,9 +11,15 @@ buildkonfig { packageName = "core.networking" defaultConfigs { - buildConfigField(FieldSpec.Type.STRING, "HOST", Config.BuildField.host) - buildConfigField(FieldSpec.Type.STRING, "API_BEARER_AUTH", Config.BuildField.api_bearer) - buildConfigField(FieldSpec.Type.STRING, "PROFILE", Config.BuildField.api_profile) + buildConfigField(FieldSpec.Type.STRING, "HOST", Config.BuildField.host_debug) + buildConfigField(FieldSpec.Type.STRING, "API_BEARER_AUTH", Config.BuildField.api_bearer_debug) + buildConfigField(FieldSpec.Type.STRING, "PROFILE", Config.BuildField.api_profile_debug) + } + + defaultConfigs("release") { + buildConfigField(FieldSpec.Type.STRING, "HOST", Config.BuildField.host_release) + buildConfigField(FieldSpec.Type.STRING, "API_BEARER_AUTH", Config.BuildField.api_bearer_release) + buildConfigField(FieldSpec.Type.STRING, "PROFILE", Config.BuildField.api_profile_release) } } From 3831391de3cd28dd107ddb8892c250bbb6f8cdfa Mon Sep 17 00:00:00 2001 From: Gabriel Bronzatti Moro Date: Wed, 5 Feb 2025 16:05:01 -0300 Subject: [PATCH 68/96] Bringing back the ksp configuration for iOS --- core-local-storage/build.gradle.kts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/core-local-storage/build.gradle.kts b/core-local-storage/build.gradle.kts index 2ffd79e4..408d1805 100644 --- a/core-local-storage/build.gradle.kts +++ b/core-local-storage/build.gradle.kts @@ -17,6 +17,9 @@ kotlin { dependencies { add("kspAndroid", libs.room.compiler) + add("kspIosSimulatorArm64", libs.room.compiler) + add("kspIosX64", libs.room.compiler) + add("kspIosArm64", libs.room.compiler) } room { From c45120d9eee3e52bfba78352fd7a11783ef8681a Mon Sep 17 00:00:00 2001 From: Gabriel Bronzatti Moro Date: Wed, 5 Feb 2025 16:08:13 -0300 Subject: [PATCH 69/96] Add platform suffix to the httpClientEngineProvider --- ...android.kt => HttpClientEngineProviderPlatform.android.kt} | 2 +- ...tEngineProvider.kt => HttpClientEngineProviderPlatform.kt} | 2 +- .../streamplayerapp/core_networking/di/NetworkModule.kt | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) rename core-networking/src/androidMain/kotlin/com/codandotv/streamplayerapp/core_networking/{HttpClientEngineProvider.android.kt => HttpClientEngineProviderPlatform.android.kt} (69%) rename core-networking/src/commonMain/kotlin/com/codandotv/streamplayerapp/core_networking/{HttpClientEngineProvider.kt => HttpClientEngineProviderPlatform.kt} (74%) diff --git a/core-networking/src/androidMain/kotlin/com/codandotv/streamplayerapp/core_networking/HttpClientEngineProvider.android.kt b/core-networking/src/androidMain/kotlin/com/codandotv/streamplayerapp/core_networking/HttpClientEngineProviderPlatform.android.kt similarity index 69% rename from core-networking/src/androidMain/kotlin/com/codandotv/streamplayerapp/core_networking/HttpClientEngineProvider.android.kt rename to core-networking/src/androidMain/kotlin/com/codandotv/streamplayerapp/core_networking/HttpClientEngineProviderPlatform.android.kt index 04598d01..5373464e 100644 --- a/core-networking/src/androidMain/kotlin/com/codandotv/streamplayerapp/core_networking/HttpClientEngineProvider.android.kt +++ b/core-networking/src/androidMain/kotlin/com/codandotv/streamplayerapp/core_networking/HttpClientEngineProviderPlatform.android.kt @@ -4,4 +4,4 @@ import io.ktor.client.engine.HttpClientEngine import io.ktor.client.engine.okhttp.OkHttpConfig import io.ktor.client.engine.okhttp.OkHttpEngine -actual fun httpClientEngine() : HttpClientEngine = OkHttpEngine(OkHttpConfig()) \ No newline at end of file +actual fun httpClientEnginePlatform() : HttpClientEngine = OkHttpEngine(OkHttpConfig()) \ No newline at end of file diff --git a/core-networking/src/commonMain/kotlin/com/codandotv/streamplayerapp/core_networking/HttpClientEngineProvider.kt b/core-networking/src/commonMain/kotlin/com/codandotv/streamplayerapp/core_networking/HttpClientEngineProviderPlatform.kt similarity index 74% rename from core-networking/src/commonMain/kotlin/com/codandotv/streamplayerapp/core_networking/HttpClientEngineProvider.kt rename to core-networking/src/commonMain/kotlin/com/codandotv/streamplayerapp/core_networking/HttpClientEngineProviderPlatform.kt index a8f99a4a..b28059f0 100644 --- a/core-networking/src/commonMain/kotlin/com/codandotv/streamplayerapp/core_networking/HttpClientEngineProvider.kt +++ b/core-networking/src/commonMain/kotlin/com/codandotv/streamplayerapp/core_networking/HttpClientEngineProviderPlatform.kt @@ -4,4 +4,4 @@ package com.codandotv.streamplayerapp.core_networking import io.ktor.client.engine.HttpClientEngine -expect fun httpClientEngine(): HttpClientEngine \ No newline at end of file +expect fun httpClientEnginePlatform(): HttpClientEngine \ No newline at end of file diff --git a/core-networking/src/commonMain/kotlin/com/codandotv/streamplayerapp/core_networking/di/NetworkModule.kt b/core-networking/src/commonMain/kotlin/com/codandotv/streamplayerapp/core_networking/di/NetworkModule.kt index aca7f32e..002e7d7d 100644 --- a/core-networking/src/commonMain/kotlin/com/codandotv/streamplayerapp/core_networking/di/NetworkModule.kt +++ b/core-networking/src/commonMain/kotlin/com/codandotv/streamplayerapp/core_networking/di/NetworkModule.kt @@ -2,7 +2,7 @@ package com.codandotv.streamplayerapp.core_networking.di import android.util.Log import com.codandotv.streamplayerapp.core_networking.di.Network.TIMEOUT -import com.codandotv.streamplayerapp.core_networking.httpClientEngine +import com.codandotv.streamplayerapp.core_networking.httpClientEnginePlatform import core.networking.BuildKonfig import io.ktor.client.HttpClient import io.ktor.client.plugins.HttpTimeout @@ -42,7 +42,7 @@ object NetworkModule { private fun provideKtorHttpClient( baseUrl: String, ): HttpClient { - return HttpClient(engine = httpClientEngine()) { + return HttpClient(engine = httpClientEnginePlatform()) { expectSuccess = false install(ContentNegotiation) { From bf705208a432fb3dacbe54b95c31949b03a05b9c Mon Sep 17 00:00:00 2001 From: Gabriel Bronzatti Moro Date: Wed, 5 Feb 2025 16:11:05 -0300 Subject: [PATCH 70/96] Add platform suffix to the Playercomponent --- .../core_shared_ui/widget/PlayerComponent.android.kt | 2 +- .../widget/{PlayerComponent.kt => PlayerComponentPlatform.kt} | 2 +- .../presentation/widget/DetailStreamImagePreview.kt | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) rename core-shared-ui/src/commonMain/kotlin/com/codandotv/streamplayerapp/core_shared_ui/widget/{PlayerComponent.kt => PlayerComponentPlatform.kt} (71%) diff --git a/core-shared-ui/src/androidMain/kotlin/com/codandotv/streamplayerapp/core_shared_ui/widget/PlayerComponent.android.kt b/core-shared-ui/src/androidMain/kotlin/com/codandotv/streamplayerapp/core_shared_ui/widget/PlayerComponent.android.kt index 51fc8e77..56986e93 100644 --- a/core-shared-ui/src/androidMain/kotlin/com/codandotv/streamplayerapp/core_shared_ui/widget/PlayerComponent.android.kt +++ b/core-shared-ui/src/androidMain/kotlin/com/codandotv/streamplayerapp/core_shared_ui/widget/PlayerComponent.android.kt @@ -19,7 +19,7 @@ import com.pierfrancescosoffritti.androidyoutubeplayer.core.player.listeners.You import com.pierfrancescosoffritti.androidyoutubeplayer.core.player.views.YouTubePlayerView @Composable -actual fun PlayerComponent(videoId: String, modifier: Modifier) { +actual fun PlayerComponentPlatform(videoId: String, modifier: Modifier) { val context = LocalContext.current diff --git a/core-shared-ui/src/commonMain/kotlin/com/codandotv/streamplayerapp/core_shared_ui/widget/PlayerComponent.kt b/core-shared-ui/src/commonMain/kotlin/com/codandotv/streamplayerapp/core_shared_ui/widget/PlayerComponentPlatform.kt similarity index 71% rename from core-shared-ui/src/commonMain/kotlin/com/codandotv/streamplayerapp/core_shared_ui/widget/PlayerComponent.kt rename to core-shared-ui/src/commonMain/kotlin/com/codandotv/streamplayerapp/core_shared_ui/widget/PlayerComponentPlatform.kt index e11d12a3..18dcb366 100644 --- a/core-shared-ui/src/commonMain/kotlin/com/codandotv/streamplayerapp/core_shared_ui/widget/PlayerComponent.kt +++ b/core-shared-ui/src/commonMain/kotlin/com/codandotv/streamplayerapp/core_shared_ui/widget/PlayerComponentPlatform.kt @@ -6,5 +6,5 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier @Composable -expect fun PlayerComponent(videoId: String, modifier: Modifier = Modifier) +expect fun PlayerComponentPlatform(videoId: String, modifier: Modifier = Modifier) diff --git a/feature-detail/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_detail/presentation/widget/DetailStreamImagePreview.kt b/feature-detail/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_detail/presentation/widget/DetailStreamImagePreview.kt index f21e7bf9..3d1fda7a 100644 --- a/feature-detail/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_detail/presentation/widget/DetailStreamImagePreview.kt +++ b/feature-detail/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_detail/presentation/widget/DetailStreamImagePreview.kt @@ -16,7 +16,7 @@ import androidx.compose.ui.graphics.Color import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.unit.dp import coil.compose.AsyncImage -import com.codandotv.streamplayerapp.core_shared_ui.widget.PlayerComponent +import com.codandotv.streamplayerapp.core_shared_ui.widget.PlayerComponentPlatform import com.codandotv.streamplayerapp.feature_detail.presentation.screens.DetailStreamsUIState.DetailStreamsLoadedUIState import org.jetbrains.compose.resources.painterResource import streamplayerapp_kmp.feature_detail.generated.resources.Res @@ -37,7 +37,7 @@ fun DetailStreamImagePreview( contentAlignment = Alignment.Center ) { if (showPlayer) { - PlayerComponent( + PlayerComponentPlatform( videoId = uiState.videoId ?: "" ) } else { From 52a9803a3d3e8c79c1a982ad7110fad216f1f85a Mon Sep 17 00:00:00 2001 From: Gabriel Bronzatti Moro Date: Tue, 11 Feb 2025 08:48:10 -0300 Subject: [PATCH 71/96] Move previews to androidMain --- composeApp/build.gradle.kts | 2 +- core-shared-ui/build.gradle.kts | 4 +- .../core_shared_ui/theme/ThemePreviews.kt | 0 .../widget/StreamPlayerTopBarPreview.kt | 24 +++++++++ .../widget/StreamsCardPreview.kt | 17 +++++++ .../widget/StreamsCarouselPreview.kt | 16 ++++++ .../widget/StreamPlayerTopBar.kt | 17 ------- .../core_shared_ui/widget/StreamsCard.kt | 13 ----- .../core_shared_ui/widget/StreamsCarousel.kt | 12 ----- feature-detail/build.gradle.kts | 4 +- feature-list-streams/build.gradle.kts | 5 +- .../screen/ListStreamsScreenPreview.kt | 12 +++++ .../widgets/HighlightBannerPreview.kt | 50 +++++++++++++++++++ .../presentation/screens/ListStreamsScreen.kt | 6 --- .../presentation/widgets/HighlightBanner.kt | 21 -------- .../ProfilePickerStreamToolbarPreview.kt | 14 ++++++ .../SetupProfilePickerScreenPreview.kt | 18 +++++++ .../screens/ProfilePickerStreamScreen.kt | 16 +----- .../widget/ProfilePickerStreamToolbar.kt | 10 ---- feature-search/build.gradle.kts | 6 ++- .../widgets/SearchStreamCardPreview.kt | 25 ++++++++++ .../widgets/SearchStreamsPreview.kt | 36 +++++++++++++ .../widgets/StreamsCarouselPreview.kt | 19 +++++++ .../presentation/widgets/SearchCarousel.kt | 22 -------- .../presentation/widgets/SearchStreamCard.kt | 20 -------- .../presentation/widgets/SearchStreams.kt | 36 +------------ 26 files changed, 249 insertions(+), 176 deletions(-) rename core-shared-ui/src/{commonMain => androidMain}/kotlin/com/codandotv/streamplayerapp/core_shared_ui/theme/ThemePreviews.kt (100%) create mode 100644 core-shared-ui/src/androidMain/kotlin/com/codandotv/streamplayerapp/core_shared_ui/widget/StreamPlayerTopBarPreview.kt create mode 100644 core-shared-ui/src/androidMain/kotlin/com/codandotv/streamplayerapp/core_shared_ui/widget/StreamsCardPreview.kt create mode 100644 core-shared-ui/src/androidMain/kotlin/com/codandotv/streamplayerapp/core_shared_ui/widget/StreamsCarouselPreview.kt create mode 100644 feature-list-streams/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_list_streams/list/presentation/screen/ListStreamsScreenPreview.kt create mode 100644 feature-list-streams/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_list_streams/list/presentation/widgets/HighlightBannerPreview.kt create mode 100644 feature-profile/src/androidMain/kotlin/com/codandotv/streamplayerapp/profile/presentation/screens/ProfilePickerStreamToolbarPreview.kt create mode 100644 feature-profile/src/androidMain/kotlin/com/codandotv/streamplayerapp/profile/presentation/screens/SetupProfilePickerScreenPreview.kt create mode 100644 feature-search/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_search/presentation/widgets/SearchStreamCardPreview.kt create mode 100644 feature-search/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_search/presentation/widgets/SearchStreamsPreview.kt create mode 100644 feature-search/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_search/presentation/widgets/StreamsCarouselPreview.kt diff --git a/composeApp/build.gradle.kts b/composeApp/build.gradle.kts index 97f3e978..1e4a350a 100644 --- a/composeApp/build.gradle.kts +++ b/composeApp/build.gradle.kts @@ -10,6 +10,7 @@ kotlin { androidMain.dependencies { implementation(libs.koin.android) implementation(libs.lottie) + implementation(compose.preview) } commonMain.dependencies { implementation(projects.featureListStreams) @@ -26,7 +27,6 @@ kotlin { implementation(compose.material3) implementation(compose.ui) - implementation(compose.preview) implementation(libs.koin.core) implementation(libs.koin.compose) diff --git a/core-shared-ui/build.gradle.kts b/core-shared-ui/build.gradle.kts index 6f6a642f..ddaef9d7 100644 --- a/core-shared-ui/build.gradle.kts +++ b/core-shared-ui/build.gradle.kts @@ -7,11 +7,13 @@ plugins { kotlin { sourceSets { + androidMain.dependencies { + implementation(compose.preview) + } commonMain.dependencies { implementation(projects.coreShared) implementation(compose.material3) - implementation(compose.preview) implementation(compose.ui) implementation(compose.components.resources) diff --git a/core-shared-ui/src/commonMain/kotlin/com/codandotv/streamplayerapp/core_shared_ui/theme/ThemePreviews.kt b/core-shared-ui/src/androidMain/kotlin/com/codandotv/streamplayerapp/core_shared_ui/theme/ThemePreviews.kt similarity index 100% rename from core-shared-ui/src/commonMain/kotlin/com/codandotv/streamplayerapp/core_shared_ui/theme/ThemePreviews.kt rename to core-shared-ui/src/androidMain/kotlin/com/codandotv/streamplayerapp/core_shared_ui/theme/ThemePreviews.kt diff --git a/core-shared-ui/src/androidMain/kotlin/com/codandotv/streamplayerapp/core_shared_ui/widget/StreamPlayerTopBarPreview.kt b/core-shared-ui/src/androidMain/kotlin/com/codandotv/streamplayerapp/core_shared_ui/widget/StreamPlayerTopBarPreview.kt new file mode 100644 index 00000000..40b01ddb --- /dev/null +++ b/core-shared-ui/src/androidMain/kotlin/com/codandotv/streamplayerapp/core_shared_ui/widget/StreamPlayerTopBarPreview.kt @@ -0,0 +1,24 @@ +package com.codandotv.streamplayerapp.core_shared_ui.widget + +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.TopAppBarDefaults +import androidx.compose.material3.rememberTopAppBarState +import androidx.compose.runtime.Composable +import com.codandotv.streamplayerapp.core_shared.extension.empty +import com.codandotv.streamplayerapp.core_shared_ui.theme.ThemePreview +import com.codandotv.streamplayerapp.core_shared_ui.theme.ThemePreviews + +@OptIn(ExperimentalMaterial3Api::class) +@ThemePreviews +@Composable +fun StreamPlayerTopBarPreview() { + ThemePreview { + StreamPlayerTopBar( + scrollBehavior = TopAppBarDefaults.exitUntilCollapsedScrollBehavior( + rememberTopAppBarState() + ), + onNavigateProfilePicker = {}, + onSelectedProfilePicture = String.empty() + ) + } +} \ No newline at end of file diff --git a/core-shared-ui/src/androidMain/kotlin/com/codandotv/streamplayerapp/core_shared_ui/widget/StreamsCardPreview.kt b/core-shared-ui/src/androidMain/kotlin/com/codandotv/streamplayerapp/core_shared_ui/widget/StreamsCardPreview.kt new file mode 100644 index 00000000..ae074713 --- /dev/null +++ b/core-shared-ui/src/androidMain/kotlin/com/codandotv/streamplayerapp/core_shared_ui/widget/StreamsCardPreview.kt @@ -0,0 +1,17 @@ +package com.codandotv.streamplayerapp.core_shared_ui.widget + +import androidx.compose.runtime.Composable +import androidx.compose.ui.tooling.preview.Preview +import com.codandotv.streamplayerapp.core_shared.Url.IMAGE_URL_SIZE_300 + +@Preview +@Composable +fun StreamsCardPreview() { + StreamsCard( + StreamsCardContent( + url = "${IMAGE_URL_SIZE_300}evgwd37VHBJhXvSr88Mrx5riFil.jpg", + contentDescription = "Test 1", + id = "", + ) + ) +} \ No newline at end of file diff --git a/core-shared-ui/src/androidMain/kotlin/com/codandotv/streamplayerapp/core_shared_ui/widget/StreamsCarouselPreview.kt b/core-shared-ui/src/androidMain/kotlin/com/codandotv/streamplayerapp/core_shared_ui/widget/StreamsCarouselPreview.kt new file mode 100644 index 00000000..e05cd728 --- /dev/null +++ b/core-shared-ui/src/androidMain/kotlin/com/codandotv/streamplayerapp/core_shared_ui/widget/StreamsCarouselPreview.kt @@ -0,0 +1,16 @@ +package com.codandotv.streamplayerapp.core_shared_ui.widget + +import androidx.compose.runtime.Composable +import androidx.compose.ui.tooling.preview.Preview +import kotlinx.coroutines.flow.emptyFlow + +@Composable +@Preview +fun StreamsCarouselPreview() { + StreamsCarousel( + content = StreamsCarouselContent( + genreTitle = "Ação", + contentList = emptyFlow() + ) + ) +} \ No newline at end of file diff --git a/core-shared-ui/src/commonMain/kotlin/com/codandotv/streamplayerapp/core_shared_ui/widget/StreamPlayerTopBar.kt b/core-shared-ui/src/commonMain/kotlin/com/codandotv/streamplayerapp/core_shared_ui/widget/StreamPlayerTopBar.kt index b0954304..d1ed1dc4 100644 --- a/core-shared-ui/src/commonMain/kotlin/com/codandotv/streamplayerapp/core_shared_ui/widget/StreamPlayerTopBar.kt +++ b/core-shared-ui/src/commonMain/kotlin/com/codandotv/streamplayerapp/core_shared_ui/widget/StreamPlayerTopBar.kt @@ -28,8 +28,6 @@ import androidx.compose.ui.unit.dp import coil.compose.AsyncImage import com.codandotv.streamplayerapp.core_shared.extension.empty import com.codandotv.streamplayerapp.core_shared_ui.resources.Colors -import com.codandotv.streamplayerapp.core_shared_ui.theme.ThemePreview -import com.codandotv.streamplayerapp.core_shared_ui.theme.ThemePreviews import org.jetbrains.compose.resources.painterResource import org.jetbrains.compose.resources.stringResource import streamplayerapp_kmp.core_shared_ui.generated.resources.Res @@ -145,19 +143,4 @@ private fun StreamPlayerOptionsTopBar(modifier: Modifier, scrollBehavior: TopApp } }, colors = TopAppBarDefaults.topAppBarColors(containerColor = Color.Transparent) ) -} - -@OptIn(ExperimentalMaterial3Api::class) -@ThemePreviews -@Composable -fun StreamPlayerTopBarPreview() { - ThemePreview { - StreamPlayerTopBar( - scrollBehavior = TopAppBarDefaults.exitUntilCollapsedScrollBehavior( - rememberTopAppBarState() - ), - onNavigateProfilePicker = {}, - onSelectedProfilePicture = String.empty() - ) - } } \ No newline at end of file diff --git a/core-shared-ui/src/commonMain/kotlin/com/codandotv/streamplayerapp/core_shared_ui/widget/StreamsCard.kt b/core-shared-ui/src/commonMain/kotlin/com/codandotv/streamplayerapp/core_shared_ui/widget/StreamsCard.kt index 1633791e..4e30f7ec 100644 --- a/core-shared-ui/src/commonMain/kotlin/com/codandotv/streamplayerapp/core_shared_ui/widget/StreamsCard.kt +++ b/core-shared-ui/src/commonMain/kotlin/com/codandotv/streamplayerapp/core_shared_ui/widget/StreamsCard.kt @@ -10,7 +10,6 @@ import androidx.compose.material3.Card import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.layout.ContentScale -import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import coil.compose.AsyncImage import com.codandotv.streamplayerapp.core_shared.Url.IMAGE_URL_SIZE_300 @@ -50,15 +49,3 @@ data class StreamsCardContent( val url: String, val contentDescription: String, ) : Parcelable - -@Preview -@Composable -fun StreamsCardPreview() { - StreamsCard( - StreamsCardContent( - url = "${IMAGE_URL_SIZE_300}evgwd37VHBJhXvSr88Mrx5riFil.jpg", - contentDescription = "Test 1", - id = "", - ) - ) -} \ No newline at end of file diff --git a/core-shared-ui/src/commonMain/kotlin/com/codandotv/streamplayerapp/core_shared_ui/widget/StreamsCarousel.kt b/core-shared-ui/src/commonMain/kotlin/com/codandotv/streamplayerapp/core_shared_ui/widget/StreamsCarousel.kt index 40100d2a..fb47bacd 100644 --- a/core-shared-ui/src/commonMain/kotlin/com/codandotv/streamplayerapp/core_shared_ui/widget/StreamsCarousel.kt +++ b/core-shared-ui/src/commonMain/kotlin/com/codandotv/streamplayerapp/core_shared_ui/widget/StreamsCarousel.kt @@ -12,7 +12,6 @@ import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.text.font.FontWeight -import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import androidx.paging.PagingData @@ -69,14 +68,3 @@ data class StreamsCarouselContent( val genreTitle: String, val contentList: Flow> ) - -@Composable -@Preview -fun StreamsCarouselPreview() { - StreamsCarousel( - content = StreamsCarouselContent( - genreTitle = "Ação", - contentList = emptyFlow() - ) - ) -} \ No newline at end of file diff --git a/feature-detail/build.gradle.kts b/feature-detail/build.gradle.kts index 7b57b974..8e66f7a3 100644 --- a/feature-detail/build.gradle.kts +++ b/feature-detail/build.gradle.kts @@ -8,6 +8,9 @@ plugins { kotlin { sourceSets { + androidMain.dependencies { + implementation(compose.preview) + } commonMain.dependencies { implementation(libs.koin.core) implementation(libs.koin.compose) @@ -20,7 +23,6 @@ kotlin { implementation(compose.material3) implementation(compose.ui) - implementation(compose.preview) implementation(compose.components.resources) implementation(libs.navigation.compose) diff --git a/feature-list-streams/build.gradle.kts b/feature-list-streams/build.gradle.kts index da9998b1..08f6d7b9 100644 --- a/feature-list-streams/build.gradle.kts +++ b/feature-list-streams/build.gradle.kts @@ -8,6 +8,10 @@ plugins { kotlin { sourceSets { + androidMain.dependencies { + implementation(compose.preview) + } + commonMain.dependencies { implementation(projects.coreNetworking) implementation(projects.coreNavigation) @@ -18,7 +22,6 @@ kotlin { implementation(compose.components.resources) implementation(compose.material3) implementation(compose.ui) - implementation(compose.preview) implementation(libs.paging.compose) diff --git a/feature-list-streams/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_list_streams/list/presentation/screen/ListStreamsScreenPreview.kt b/feature-list-streams/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_list_streams/list/presentation/screen/ListStreamsScreenPreview.kt new file mode 100644 index 00000000..0717265b --- /dev/null +++ b/feature-list-streams/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_list_streams/list/presentation/screen/ListStreamsScreenPreview.kt @@ -0,0 +1,12 @@ +package com.codandotv.streamplayerapp.feature_list_streams.list.presentation.screen + +import androidx.compose.runtime.Composable +import androidx.navigation.compose.rememberNavController +import com.codandotv.streamplayerapp.core_shared_ui.theme.ThemePreviews +import com.codandotv.streamplayerapp.feature_list_streams.list.presentation.screens.ListStreamsScreen + +@ThemePreviews +@Composable +fun ListStreamsScreenPreview() { + ListStreamsScreen(navController = rememberNavController(), profilePicture = "") +} diff --git a/feature-list-streams/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_list_streams/list/presentation/widgets/HighlightBannerPreview.kt b/feature-list-streams/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_list_streams/list/presentation/widgets/HighlightBannerPreview.kt new file mode 100644 index 00000000..b5e899a9 --- /dev/null +++ b/feature-list-streams/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_list_streams/list/presentation/widgets/HighlightBannerPreview.kt @@ -0,0 +1,50 @@ +package com.codandotv.streamplayerapp.feature_list_streams.list.presentation.widgets + +import androidx.compose.runtime.Composable +import com.codandotv.streamplayerapp.core_shared_ui.theme.ThemePreviews +import com.codandotv.streamplayerapp.feature_list_streams.list.domain.model.IconAndTextInfo +import org.jetbrains.compose.resources.stringResource +import streamplayerapp_kmp.core_shared_ui.generated.resources.Res +import streamplayerapp_kmp.core_shared_ui.generated.resources.app_name +import streamplayerapp_kmp.core_shared_ui.generated.resources.ic_add +import streamplayerapp_kmp.core_shared_ui.generated.resources.ic_info +import streamplayerapp_kmp.core_shared_ui.generated.resources.ic_play +import streamplayerapp_kmp.feature_list_streams.generated.resources.ic_top_10 +import streamplayerapp_kmp.feature_list_streams.generated.resources.list_highlight_banner_add +import streamplayerapp_kmp.feature_list_streams.generated.resources.list_highlight_banner_info +import streamplayerapp_kmp.feature_list_streams.generated.resources.list_highlight_banner_watch + +@ThemePreviews +@Composable +fun HighlightBannerPreview() { + HighlightBanner( + data = com.codandotv.streamplayerapp.feature_list_streams.list.domain.model.HighlightBanner( + name = stringResource(Res.string.app_name), + imageUrl = String(), + contentType = com.codandotv.streamplayerapp.feature_list_streams.core.ContentType.getContentName( + com.codandotv.streamplayerapp.feature_list_streams.core.ContentType.SHOW + ), + contentTypeAsPlural = com.codandotv.streamplayerapp.feature_list_streams.core.ContentType.getContentNameAsPlural( + com.codandotv.streamplayerapp.feature_list_streams.core.ContentType.SHOW + ), + extraInfo = IconAndTextInfo( + streamplayerapp_kmp.feature_list_streams.generated.resources.Res.drawable.ic_top_10, + com.codandotv.streamplayerapp.feature_list_streams.core.ContentType.getContentName( + com.codandotv.streamplayerapp.feature_list_streams.core.ContentType.SHOW + ) + ), + leftButton = IconAndTextInfo( + Res.drawable.ic_add, + streamplayerapp_kmp.feature_list_streams.generated.resources.Res.string.list_highlight_banner_add + ), + centralButton = IconAndTextInfo( + Res.drawable.ic_play, + streamplayerapp_kmp.feature_list_streams.generated.resources.Res.string.list_highlight_banner_watch + ), + rightButton = IconAndTextInfo( + Res.drawable.ic_info, + streamplayerapp_kmp.feature_list_streams.generated.resources.Res.string.list_highlight_banner_info + ), + ) + ) +} diff --git a/feature-list-streams/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_list_streams/list/presentation/screens/ListStreamsScreen.kt b/feature-list-streams/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_list_streams/list/presentation/screens/ListStreamsScreen.kt index 5916e5ee..d7f847a2 100644 --- a/feature-list-streams/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_list_streams/list/presentation/screens/ListStreamsScreen.kt +++ b/feature-list-streams/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_list_streams/list/presentation/screens/ListStreamsScreen.kt @@ -28,7 +28,6 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.navigation.NavController import androidx.navigation.compose.rememberNavController import com.codandotv.streamplayerapp.core_navigation.bottomnavigation.StreamPlayerBottomNavigation -import com.codandotv.streamplayerapp.core_shared_ui.theme.ThemePreviews import com.codandotv.streamplayerapp.core_shared_ui.widget.StreamPlayerTopBar import com.codandotv.streamplayerapp.feature_list_streams.list.presentation.widgets.HighlightBanner import com.codandotv.streamplayerapp.core_shared_ui.widget.StreamsCarousel @@ -108,8 +107,3 @@ fun ListStreamsScreen( } } -@ThemePreviews -@Composable -fun ListStreamsScreenPreview() { - ListStreamsScreen(navController = rememberNavController(), profilePicture = "") -} diff --git a/feature-list-streams/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_list_streams/list/presentation/widgets/HighlightBanner.kt b/feature-list-streams/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_list_streams/list/presentation/widgets/HighlightBanner.kt index a5a35a4d..662752e3 100644 --- a/feature-list-streams/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_list_streams/list/presentation/widgets/HighlightBanner.kt +++ b/feature-list-streams/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_list_streams/list/presentation/widgets/HighlightBanner.kt @@ -32,7 +32,6 @@ import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import coil.compose.AsyncImage -import com.codandotv.streamplayerapp.core_shared_ui.theme.ThemePreviews import com.codandotv.streamplayerapp.feature.list.streams.R import com.codandotv.streamplayerapp.feature_list_streams.core.ContentType import com.codandotv.streamplayerapp.feature_list_streams.list.domain.model.HighlightBanner @@ -272,23 +271,3 @@ fun PlayButton( ) } } - -@ThemePreviews -@Composable -fun HighlightBannerPreview() { - HighlightBanner( - data = HighlightBanner( - name = stringResource(SharedRes.string.app_name), - imageUrl = String(), - contentType = ContentType.getContentName(ContentType.SHOW), - contentTypeAsPlural = ContentType.getContentNameAsPlural(ContentType.SHOW), - extraInfo = IconAndTextInfo( - Res.drawable.ic_top_10, - ContentType.getContentName(ContentType.SHOW) - ), - leftButton = IconAndTextInfo(SharedRes.drawable.ic_add, Res.string.list_highlight_banner_add), - centralButton = IconAndTextInfo(SharedRes.drawable.ic_play, Res.string.list_highlight_banner_watch), - rightButton = IconAndTextInfo(SharedRes.drawable.ic_info, Res.string.list_highlight_banner_info), - ) - ) -} diff --git a/feature-profile/src/androidMain/kotlin/com/codandotv/streamplayerapp/profile/presentation/screens/ProfilePickerStreamToolbarPreview.kt b/feature-profile/src/androidMain/kotlin/com/codandotv/streamplayerapp/profile/presentation/screens/ProfilePickerStreamToolbarPreview.kt new file mode 100644 index 00000000..f26eac1c --- /dev/null +++ b/feature-profile/src/androidMain/kotlin/com/codandotv/streamplayerapp/profile/presentation/screens/ProfilePickerStreamToolbarPreview.kt @@ -0,0 +1,14 @@ +package com.codandotv.streamplayerapp.profile.presentation.screens + +import androidx.compose.material3.MaterialTheme +import androidx.compose.runtime.Composable +import com.codandotv.streamplayerapp.core_shared_ui.theme.ThemePreviews +import com.codandotv.streamplayerapp.profile.presentation.widget.ProfilePickerStreamToolbar + +@ThemePreviews +@Composable +fun ProfilePickerStreamToolbarPreview() { + MaterialTheme { + ProfilePickerStreamToolbar() + } +} \ No newline at end of file diff --git a/feature-profile/src/androidMain/kotlin/com/codandotv/streamplayerapp/profile/presentation/screens/SetupProfilePickerScreenPreview.kt b/feature-profile/src/androidMain/kotlin/com/codandotv/streamplayerapp/profile/presentation/screens/SetupProfilePickerScreenPreview.kt new file mode 100644 index 00000000..363bd326 --- /dev/null +++ b/feature-profile/src/androidMain/kotlin/com/codandotv/streamplayerapp/profile/presentation/screens/SetupProfilePickerScreenPreview.kt @@ -0,0 +1,18 @@ +package com.codandotv.streamplayerapp.profile.presentation.screens + +import androidx.compose.runtime.Composable +import com.codandotv.streamplayerapp.core_shared_ui.theme.ThemePreview +import com.codandotv.streamplayerapp.core_shared_ui.theme.ThemePreviews +import com.codandotv.streamplayerapp.profile.domain.ProfileStream + +@ThemePreviews +@Composable +fun SetupProfilePickerScreenPreview() { + ThemePreview { + SetupProfilePickerScreen( + uiState = ProfilePickerStreamsUIState( + selectedItem = ProfileStream("1", "Name", ""), + ) + ) + } +} \ No newline at end of file diff --git a/feature-profile/src/commonMain/kotlin/com/codandotv/streamplayerapp/profile/presentation/screens/ProfilePickerStreamScreen.kt b/feature-profile/src/commonMain/kotlin/com/codandotv/streamplayerapp/profile/presentation/screens/ProfilePickerStreamScreen.kt index 0e8818f2..a2e47d22 100644 --- a/feature-profile/src/commonMain/kotlin/com/codandotv/streamplayerapp/profile/presentation/screens/ProfilePickerStreamScreen.kt +++ b/feature-profile/src/commonMain/kotlin/com/codandotv/streamplayerapp/profile/presentation/screens/ProfilePickerStreamScreen.kt @@ -20,8 +20,6 @@ import androidx.compose.ui.unit.IntOffset import androidx.compose.ui.unit.dp import androidx.lifecycle.LifecycleOwner import androidx.lifecycle.compose.collectAsStateWithLifecycle -import com.codandotv.streamplayerapp.core_shared_ui.theme.ThemePreview -import com.codandotv.streamplayerapp.core_shared_ui.theme.ThemePreviews import com.codandotv.streamplayerapp.feature.profile.R import com.codandotv.streamplayerapp.profile.domain.ProfileStream import com.codandotv.streamplayerapp.profile.presentation.widget.LoadScreen @@ -70,7 +68,7 @@ fun ProfilePickerStreamScreen( @OptIn(ExperimentalMaterial3Api::class) @SuppressLint("UnusedMaterial3ScaffoldPaddingParameter") @Composable -private fun SetupProfilePickerScreen( +fun SetupProfilePickerScreen( uiState: ProfilePickerStreamsUIState, onSetCenterImageAlpha: (Float) -> Unit = {}, onSetScreenSize: (Float, Float, Int, Int) -> Unit = { _, _, _, _ -> }, @@ -174,15 +172,3 @@ private fun Lifecycle( } } } - -@ThemePreviews -@Composable -fun SetupProfilePickerScreenPreview() { - ThemePreview { - SetupProfilePickerScreen( - uiState = ProfilePickerStreamsUIState( - selectedItem = ProfileStream("1", "Name", ""), - ) - ) - } -} \ No newline at end of file diff --git a/feature-profile/src/commonMain/kotlin/com/codandotv/streamplayerapp/profile/presentation/widget/ProfilePickerStreamToolbar.kt b/feature-profile/src/commonMain/kotlin/com/codandotv/streamplayerapp/profile/presentation/widget/ProfilePickerStreamToolbar.kt index e8727675..32ef6ca5 100644 --- a/feature-profile/src/commonMain/kotlin/com/codandotv/streamplayerapp/profile/presentation/widget/ProfilePickerStreamToolbar.kt +++ b/feature-profile/src/commonMain/kotlin/com/codandotv/streamplayerapp/profile/presentation/widget/ProfilePickerStreamToolbar.kt @@ -15,7 +15,6 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp -import com.codandotv.streamplayerapp.core_shared_ui.theme.ThemePreviews import org.jetbrains.compose.resources.painterResource import streamplayerapp_kmp.feature_profile.generated.resources.Res import streamplayerapp_kmp.feature_profile.generated.resources.netflix_horizontal_logo @@ -57,12 +56,3 @@ fun ProfilePickerStreamToolbar(modifier: Modifier = Modifier) { .height(56.dp) ) } - - -@ThemePreviews -@Composable -fun ProfilePickerStreamToolbarPreview() { - MaterialTheme { - ProfilePickerStreamToolbar() - } -} \ No newline at end of file diff --git a/feature-search/build.gradle.kts b/feature-search/build.gradle.kts index 08be24cd..12553d73 100644 --- a/feature-search/build.gradle.kts +++ b/feature-search/build.gradle.kts @@ -8,6 +8,10 @@ plugins { kotlin { sourceSets { + androidMain.dependencies { + implementation(compose.preview) + } + commonMain.dependencies { implementation(libs.paging.compose) @@ -20,7 +24,7 @@ kotlin { implementation(compose.components.resources) implementation(compose.material3) implementation(compose.ui) - implementation(compose.preview) + implementation(libs.navigation.compose) diff --git a/feature-search/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_search/presentation/widgets/SearchStreamCardPreview.kt b/feature-search/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_search/presentation/widgets/SearchStreamCardPreview.kt new file mode 100644 index 00000000..2597746e --- /dev/null +++ b/feature-search/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_search/presentation/widgets/SearchStreamCardPreview.kt @@ -0,0 +1,25 @@ +package com.codandotv.streamplayerapp.feature_search.presentation.widgets + +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import com.codandotv.streamplayerapp.core_shared_ui.theme.ThemePreviews + + +@ThemePreviews +@Composable +fun SearchStreamCardPreview() { + SearchStreamCard( + content = SearchStreamCardModel( + id = "1", + title = "The Witcher", + url = "https://image.tmdb.org/t/p/w200/iwsMu0ehRPbtaSxqiaUDQB9qMWT.jpg" + ), + onSearchStreamPressed = {} + ) +} + +@ThemePreviews +@Composable +fun PlayerPreview() { + PlayerIcon(modifier = Modifier) +} \ No newline at end of file diff --git a/feature-search/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_search/presentation/widgets/SearchStreamsPreview.kt b/feature-search/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_search/presentation/widgets/SearchStreamsPreview.kt new file mode 100644 index 00000000..8b37e056 --- /dev/null +++ b/feature-search/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_search/presentation/widgets/SearchStreamsPreview.kt @@ -0,0 +1,36 @@ +package com.codandotv.streamplayerapp.feature_search.presentation.widgets + +import androidx.compose.runtime.Composable +import androidx.compose.ui.tooling.preview.Preview + +@Composable +@Preview +fun SearchBarPreview() { + StreamPlayerTopBar( + onBackPressed = {} + ) +} + +@Composable +@Preview +fun SearchTopBarEmptyPreview() { + SearchTopBar( + currentSearchText = "", + onSearchTextChanged = {}, + onSearchDispatched = {}, + onCleanTextPressed = {}, + onSearchIconPressed = {} + ) +} + +@Composable +@Preview +fun SearchTopBarPreview() { + SearchTopBar( + currentSearchText = "Texto de busca", + onSearchTextChanged = {}, + onSearchDispatched = {}, + onCleanTextPressed = {}, + onSearchIconPressed = {} + ) +} diff --git a/feature-search/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_search/presentation/widgets/StreamsCarouselPreview.kt b/feature-search/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_search/presentation/widgets/StreamsCarouselPreview.kt new file mode 100644 index 00000000..75b34df3 --- /dev/null +++ b/feature-search/src/androidMain/kotlin/com/codandotv/streamplayerapp/feature_search/presentation/widgets/StreamsCarouselPreview.kt @@ -0,0 +1,19 @@ +package com.codandotv.streamplayerapp.feature_search.presentation.widgets + +import androidx.compose.runtime.Composable +import androidx.compose.ui.tooling.preview.Preview + +@Composable +@Preview +fun StreamsErrorPreview() { + StreamsError( + onRetry = {}, + onCloseButton = {} + ) +} + +@Composable +@Preview +fun StreamEmptyPreview() { + StreamsEmpty() +} \ No newline at end of file diff --git a/feature-search/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_search/presentation/widgets/SearchCarousel.kt b/feature-search/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_search/presentation/widgets/SearchCarousel.kt index 5009e593..b73a6a07 100644 --- a/feature-search/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_search/presentation/widgets/SearchCarousel.kt +++ b/feature-search/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_search/presentation/widgets/SearchCarousel.kt @@ -23,7 +23,6 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.style.TextAlign -import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import androidx.paging.PagingData @@ -85,17 +84,6 @@ fun SearchCarouselStream( } } -@Composable -@Preview -fun StreamsCarouselPreview() { - SearchCarouselStream( - content = SearchCarousel( - genreTitle = "Comédia", - contentList = emptyFlow() - ) - ) -} - @Composable fun StreamsError( onRetry: () -> Unit, @@ -144,7 +132,6 @@ fun StreamsError( } @Composable -@Preview fun StreamsEmpty() { Row(verticalAlignment = Alignment.CenterVertically) { Column { @@ -160,12 +147,3 @@ fun StreamsEmpty() { } } } - -@Composable -@Preview -fun StreamsErrorPreview() { - StreamsError( - onRetry = {}, - onCloseButton = {} - ) -} \ No newline at end of file diff --git a/feature-search/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_search/presentation/widgets/SearchStreamCard.kt b/feature-search/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_search/presentation/widgets/SearchStreamCard.kt index 54f75499..cc17a450 100644 --- a/feature-search/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_search/presentation/widgets/SearchStreamCard.kt +++ b/feature-search/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_search/presentation/widgets/SearchStreamCard.kt @@ -25,7 +25,6 @@ import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import coil.compose.AsyncImage -import com.codandotv.streamplayerapp.core_shared_ui.theme.ThemePreviews @Suppress("MagicNumber") @Composable @@ -107,22 +106,3 @@ fun PlayerIcon(modifier: Modifier) { ) } } - -@ThemePreviews -@Composable -fun SearchStreamCardPreview() { - SearchStreamCard( - content = SearchStreamCardModel( - id = "1", - title = "The Witcher", - url = "https://image.tmdb.org/t/p/w200/iwsMu0ehRPbtaSxqiaUDQB9qMWT.jpg" - ), - onSearchStreamPressed = {} - ) -} - -@ThemePreviews -@Composable -fun PlayerPreview() { - PlayerIcon(modifier = Modifier) -} diff --git a/feature-search/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_search/presentation/widgets/SearchStreams.kt b/feature-search/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_search/presentation/widgets/SearchStreams.kt index 0aaf1e8e..01775583 100644 --- a/feature-search/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_search/presentation/widgets/SearchStreams.kt +++ b/feature-search/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_search/presentation/widgets/SearchStreams.kt @@ -26,7 +26,6 @@ import androidx.compose.ui.draw.clip import androidx.compose.ui.graphics.Color import androidx.compose.ui.text.TextStyle import androidx.compose.ui.text.input.ImeAction -import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import com.codandotv.streamplayerapp.core_shared_ui.resources.Colors import com.codandotv.streamplayerapp.core_shared_ui.widget.CloseButton @@ -67,7 +66,7 @@ fun SearchableTopBar( } @Composable -private fun StreamPlayerTopBar( +internal fun StreamPlayerTopBar( onBackPressed: () -> Unit ) { Row( @@ -169,36 +168,3 @@ fun SearchTopBar( ) } } - - -@Composable -@Preview -fun SearchBarPreview() { - StreamPlayerTopBar( - onBackPressed = {} - ) -} - -@Composable -@Preview -fun SearchTopBarEmptyPreview() { - SearchTopBar( - currentSearchText = "", - onSearchTextChanged = {}, - onSearchDispatched = {}, - onCleanTextPressed = {}, - onSearchIconPressed = {} - ) -} - -@Composable -@Preview -fun SearchTopBarPreview() { - SearchTopBar( - currentSearchText = "Texto de busca", - onSearchTextChanged = {}, - onSearchDispatched = {}, - onCleanTextPressed = {}, - onSearchIconPressed = {} - ) -} From aac5b7886ab0c7b4549439ff81f2fd161a7f7588 Mon Sep 17 00:00:00 2001 From: Rods Date: Wed, 12 Feb 2025 22:25:13 -0300 Subject: [PATCH 72/96] [ISSUE-21/24] - remove lifecycle and add remember Koin --- .../navigation/DetailStreamNavigation.kt | 12 ++++----- .../screens/DetailStreamsScreen.kt | 12 --------- .../navigation/ListStreamsNavigation.kt | 15 ++++++----- .../presentation/screens/ListStreamsScreen.kt | 8 ------ .../ProfilePickerStreamNavigation.kt | 16 ++++++------ .../screens/ProfilePickerStreamScreen.kt | 19 -------------- .../screens/ProfilePickerStreamViewModel.kt | 8 ++---- .../navigation/SearchStreamNavigation.kt | 12 ++++----- .../presentation/screens/SearchScreen.kt | 25 +------------------ .../presentation/screens/SearchViewModel.kt | 3 +-- 10 files changed, 31 insertions(+), 99 deletions(-) diff --git a/feature-detail/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_detail/presentation/navigation/DetailStreamNavigation.kt b/feature-detail/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_detail/presentation/navigation/DetailStreamNavigation.kt index 7917c3f3..172d5752 100644 --- a/feature-detail/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_detail/presentation/navigation/DetailStreamNavigation.kt +++ b/feature-detail/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_detail/presentation/navigation/DetailStreamNavigation.kt @@ -1,6 +1,5 @@ package com.codandotv.streamplayerapp.feature_detail.presentation.navigation -import androidx.lifecycle.Lifecycle import androidx.navigation.NavGraphBuilder import androidx.navigation.NavHostController import androidx.navigation.compose.composable @@ -10,16 +9,18 @@ import com.codandotv.streamplayerapp.core_navigation.routes.Routes.PARAM.ID import com.codandotv.streamplayerapp.feature_detail.di.DetailStreamModule import com.codandotv.streamplayerapp.feature_detail.presentation.screens.DetailStreamScreen import org.koin.androidx.compose.koinViewModel -import org.koin.core.context.loadKoinModules +import org.koin.compose.module.rememberKoinModules +import org.koin.core.annotation.KoinExperimentalAPI import org.koin.core.context.unloadKoinModules import org.koin.core.parameter.parametersOf internal const val DEFAULT_ID = "0" +@OptIn(KoinExperimentalAPI::class) fun NavGraphBuilder.detailStreamNavGraph(navController: NavHostController) { composable(DETAIL_COMPLETE) { nav -> - if (nav.lifecycle.currentState == Lifecycle.State.STARTED) { - loadKoinModules(DetailStreamModule.module) + rememberKoinModules { + listOf(DetailStreamModule.module) } DetailStreamScreen( viewModel = koinViewModel { @@ -29,9 +30,6 @@ fun NavGraphBuilder.detailStreamNavGraph(navController: NavHostController) { onNavigateSearchScreen = { navController.navigate(Routes.SEARCH) }, - disposable = { - unloadKoinModules(DetailStreamModule.module) - } ) } } diff --git a/feature-detail/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_detail/presentation/screens/DetailStreamsScreen.kt b/feature-detail/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_detail/presentation/screens/DetailStreamsScreen.kt index 2ce51aec..2e32247c 100644 --- a/feature-detail/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_detail/presentation/screens/DetailStreamsScreen.kt +++ b/feature-detail/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_detail/presentation/screens/DetailStreamsScreen.kt @@ -21,14 +21,12 @@ import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Scaffold import androidx.compose.material3.Text import androidx.compose.runtime.Composable -import androidx.compose.runtime.DisposableEffect import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.platform.LocalLifecycleOwner import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.em @@ -54,23 +52,13 @@ import streamplayerapp_kmp.feature_detail.generated.resources.detail_watch_prima fun DetailStreamScreen( viewModel: DetailStreamViewModel = koinViewModel(), navController: NavController, - disposable: () -> Unit = {}, onNavigateSearchScreen: () -> Unit = {}, ) { val uiState by viewModel.uiState.collectAsStateWithLifecycle() - - val lifecycleOwner = LocalLifecycleOwner.current - LifecycleEventEffect(Lifecycle.Event.ON_START) { viewModel.loadDetail() } - DisposableEffect(lifecycleOwner) { - onDispose { - disposable.invoke() - } - } - when (uiState) { is DetailStreamsUIState.DetailStreamsLoadedUIState -> { SetupDetailScreen( diff --git a/feature-list-streams/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_list_streams/list/presentation/navigation/ListStreamsNavigation.kt b/feature-list-streams/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_list_streams/list/presentation/navigation/ListStreamsNavigation.kt index 633a92fd..cb3a5a83 100644 --- a/feature-list-streams/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_list_streams/list/presentation/navigation/ListStreamsNavigation.kt +++ b/feature-list-streams/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_list_streams/list/presentation/navigation/ListStreamsNavigation.kt @@ -12,18 +12,24 @@ import com.codandotv.streamplayerapp.core_navigation.routes.Routes.DETAIL import com.codandotv.streamplayerapp.core_navigation.routes.Routes.PROFILE_PICKER import com.codandotv.streamplayerapp.feature_list_streams.list.di.ListStreamModule import com.codandotv.streamplayerapp.feature_list_streams.list.presentation.screens.ListStreamsScreen +import org.koin.compose.module.rememberKoinModules +import org.koin.core.annotation.KoinExperimentalAPI import org.koin.core.context.loadKoinModules import org.koin.core.context.unloadKoinModules internal const val DEFAULT_ID = "" +@OptIn(KoinExperimentalAPI::class) fun NavGraphBuilder.listStreamsNavGraph(navController: NavHostController) { composable(HOME_COMPLETE) { nav -> BackHandler(true) {} - if (nav.lifecycle.currentState == Lifecycle.State.STARTED) { - loadKoinModules(ListStreamModule.module) + + rememberKoinModules { + listOf(ListStreamModule.module) } - ListStreamsScreen(navController = navController, + + ListStreamsScreen( + navController = navController, onNavigateDetailList = { id -> navController.navigate("${DETAIL}${id}") }, @@ -33,9 +39,6 @@ fun NavGraphBuilder.listStreamsNavGraph(navController: NavHostController) { onNavigateSearchScreen = { navController.navigate(Routes.SEARCH) }, - disposable = { - unloadKoinModules(ListStreamModule.module) - }, profilePicture = nav.arguments?.getString(PROFILE_ID) ?: DEFAULT_ID ) } diff --git a/feature-list-streams/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_list_streams/list/presentation/screens/ListStreamsScreen.kt b/feature-list-streams/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_list_streams/list/presentation/screens/ListStreamsScreen.kt index 5916e5ee..581085cf 100644 --- a/feature-list-streams/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_list_streams/list/presentation/screens/ListStreamsScreen.kt +++ b/feature-list-streams/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_list_streams/list/presentation/screens/ListStreamsScreen.kt @@ -44,21 +44,13 @@ fun ListStreamsScreen( onNavigateDetailList: (String) -> Unit = {}, onNavigateProfilePicker: () -> Unit = {}, onNavigateSearchScreen: () -> Unit = {}, - disposable: () -> Unit = {}, profilePicture: String ) { val uiState by viewModel.uiState.collectAsStateWithLifecycle() - val lifecycleOwner = LocalLifecycleOwner.current val scrollBehavior = TopAppBarDefaults.exitUntilCollapsedScrollBehavior(rememberTopAppBarState()) val baseScrollState = rememberScrollState() - DisposableEffect(lifecycleOwner) { - onDispose { - disposable.invoke() - } - } - Scaffold( modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection), topBar = { diff --git a/feature-profile/src/commonMain/kotlin/com/codandotv/streamplayerapp/profile/presentation/navigation/ProfilePickerStreamNavigation.kt b/feature-profile/src/commonMain/kotlin/com/codandotv/streamplayerapp/profile/presentation/navigation/ProfilePickerStreamNavigation.kt index 8d42759a..eb885448 100644 --- a/feature-profile/src/commonMain/kotlin/com/codandotv/streamplayerapp/profile/presentation/navigation/ProfilePickerStreamNavigation.kt +++ b/feature-profile/src/commonMain/kotlin/com/codandotv/streamplayerapp/profile/presentation/navigation/ProfilePickerStreamNavigation.kt @@ -1,6 +1,5 @@ package com.codandotv.streamplayerapp.profile.presentation.navigation -import androidx.lifecycle.Lifecycle import androidx.navigation.NavGraphBuilder import androidx.navigation.NavHostController import androidx.navigation.compose.composable @@ -9,20 +8,19 @@ import com.codandotv.streamplayerapp.core_navigation.routes.BottomNavRoutes.PARA import com.codandotv.streamplayerapp.core_navigation.routes.Routes import com.codandotv.streamplayerapp.profile.di.ProfilePickerStreamModule import com.codandotv.streamplayerapp.profile.presentation.screens.ProfilePickerStreamScreen -import org.koin.core.context.loadKoinModules -import org.koin.core.context.unloadKoinModules +import org.koin.compose.module.rememberKoinModules +import org.koin.core.annotation.KoinExperimentalAPI +@OptIn(KoinExperimentalAPI::class) fun NavGraphBuilder.profilePickerStreamNavGraph(navController: NavHostController) { - composable(Routes.PROFILE_PICKER) { nav -> - if (nav.lifecycle.currentState == Lifecycle.State.STARTED) { - loadKoinModules(ProfilePickerStreamModule.module) + composable(Routes.PROFILE_PICKER) { + rememberKoinModules { + listOf(ProfilePickerStreamModule.module) } ProfilePickerStreamScreen( onNavigateListStreams = { profilePic -> navController.navigate("$HOME?$PROFILE_ID=$profilePic") } - ) { - unloadKoinModules(ProfilePickerStreamModule.module) - } + ) } } diff --git a/feature-profile/src/commonMain/kotlin/com/codandotv/streamplayerapp/profile/presentation/screens/ProfilePickerStreamScreen.kt b/feature-profile/src/commonMain/kotlin/com/codandotv/streamplayerapp/profile/presentation/screens/ProfilePickerStreamScreen.kt index 0e8818f2..16046e87 100644 --- a/feature-profile/src/commonMain/kotlin/com/codandotv/streamplayerapp/profile/presentation/screens/ProfilePickerStreamScreen.kt +++ b/feature-profile/src/commonMain/kotlin/com/codandotv/streamplayerapp/profile/presentation/screens/ProfilePickerStreamScreen.kt @@ -42,11 +42,8 @@ import streamplayerapp_kmp.feature_profile.generated.resources.profile_animation fun ProfilePickerStreamScreen( viewModel: ProfilePickerStreamViewModel = koinViewModel(), onNavigateListStreams: (String) -> Unit = {}, - disposable: () -> Unit = {} ) { val uiState by viewModel.uiState.collectAsStateWithLifecycle() - val lifecycleOwner = LocalLifecycleOwner.current - Lifecycle(lifecycleOwner, viewModel, disposable) if (uiState.isLoading) { LoadScreen() @@ -159,22 +156,6 @@ private fun SetupProfilePickerScreen( ) } -@Composable -private fun Lifecycle( - lifecycleOwner: LifecycleOwner, viewModel: ProfilePickerStreamViewModel, disposable: () -> Unit -) { - DisposableEffect(lifecycleOwner) { - val lifecycle = lifecycleOwner.lifecycle - - lifecycle.addObserver(viewModel) - - onDispose { - lifecycle.removeObserver(viewModel) - disposable.invoke() - } - } -} - @ThemePreviews @Composable fun SetupProfilePickerScreenPreview() { diff --git a/feature-profile/src/commonMain/kotlin/com/codandotv/streamplayerapp/profile/presentation/screens/ProfilePickerStreamViewModel.kt b/feature-profile/src/commonMain/kotlin/com/codandotv/streamplayerapp/profile/presentation/screens/ProfilePickerStreamViewModel.kt index 75b5a2b6..1eac734f 100644 --- a/feature-profile/src/commonMain/kotlin/com/codandotv/streamplayerapp/profile/presentation/screens/ProfilePickerStreamViewModel.kt +++ b/feature-profile/src/commonMain/kotlin/com/codandotv/streamplayerapp/profile/presentation/screens/ProfilePickerStreamViewModel.kt @@ -1,7 +1,5 @@ package com.codandotv.streamplayerapp.profile.presentation.screens -import androidx.lifecycle.DefaultLifecycleObserver -import androidx.lifecycle.LifecycleOwner import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import com.codandotv.streamplayerapp.core_networking.handleError.catchFailure @@ -16,7 +14,7 @@ import kotlinx.coroutines.launch class ProfilePickerStreamViewModel( private val useCase: ProfilePickerStreamUseCase, -) : ViewModel(), DefaultLifecycleObserver { +) : ViewModel() { private val _uiState = MutableStateFlow(ProfilePickerStreamsUIState()) val uiState = _uiState.stateIn( @@ -25,9 +23,7 @@ class ProfilePickerStreamViewModel( initialValue = _uiState.value ) - override fun onCreate(owner: LifecycleOwner) { - super.onCreate(owner) - + init { viewModelScope.launch { useCase.getProfile() .catchFailure { diff --git a/feature-search/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_search/presentation/navigation/SearchStreamNavigation.kt b/feature-search/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_search/presentation/navigation/SearchStreamNavigation.kt index ebcf0742..134ad7e5 100644 --- a/feature-search/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_search/presentation/navigation/SearchStreamNavigation.kt +++ b/feature-search/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_search/presentation/navigation/SearchStreamNavigation.kt @@ -8,22 +8,22 @@ import androidx.navigation.compose.composable import com.codandotv.streamplayerapp.core_navigation.routes.Routes import com.codandotv.streamplayerapp.feature_search.di.SearchModule import com.codandotv.streamplayerapp.feature_search.presentation.screens.SearchScreen +import org.koin.compose.module.rememberKoinModules +import org.koin.core.annotation.KoinExperimentalAPI import org.koin.core.context.loadKoinModules import org.koin.core.context.unloadKoinModules +@OptIn(KoinExperimentalAPI::class) fun NavGraphBuilder.searchStreamsNavGraph(navController: NavHostController) { - composable(Routes.SEARCH) { nav -> + composable(Routes.SEARCH) { _ -> BackHandler(true) {} - if (nav.lifecycle.currentState == Lifecycle.State.STARTED) { - loadKoinModules(SearchModule.module) + rememberKoinModules { + listOf(SearchModule.module) } SearchScreen( navController = navController, onNavigateDetailList = { id -> navController.navigate("${Routes.DETAIL}${id}") - }, - disposable = { - unloadKoinModules(SearchModule.module) } ) } diff --git a/feature-search/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_search/presentation/screens/SearchScreen.kt b/feature-search/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_search/presentation/screens/SearchScreen.kt index 94ff555e..61b389c6 100644 --- a/feature-search/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_search/presentation/screens/SearchScreen.kt +++ b/feature-search/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_search/presentation/screens/SearchScreen.kt @@ -39,16 +39,9 @@ fun SearchScreen( viewModel: SearchViewModel = koinViewModel(), onNavigateDetailList: (String) -> Unit = {}, navController: NavController, - disposable: () -> Unit = {} ) { val uiState by viewModel.uiState.collectAsState() - Lifecycle( - lifecycleOwner = LocalLifecycleOwner.current, - viewModel = viewModel, - disposable = disposable - ) - when (uiState) { is SearchUIState.Success -> { SetupSearchScreen( @@ -152,20 +145,4 @@ private fun SetupSearchScreen( navController.goBack() } -} - -@Composable -private fun Lifecycle( - lifecycleOwner: LifecycleOwner, viewModel: SearchViewModel, disposable: () -> Unit -) { - DisposableEffect(lifecycleOwner) { - val lifecycle = lifecycleOwner.lifecycle - - lifecycle.addObserver(viewModel) - - onDispose { - lifecycle.removeObserver(viewModel) - disposable.invoke() - } - } -} +} \ No newline at end of file diff --git a/feature-search/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_search/presentation/screens/SearchViewModel.kt b/feature-search/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_search/presentation/screens/SearchViewModel.kt index f1d48fdd..8a4314b7 100644 --- a/feature-search/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_search/presentation/screens/SearchViewModel.kt +++ b/feature-search/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_search/presentation/screens/SearchViewModel.kt @@ -1,6 +1,5 @@ package com.codandotv.streamplayerapp.feature_search.presentation.screens -import androidx.lifecycle.DefaultLifecycleObserver import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import com.codandotv.streamplayerapp.core_networking.handleError.catchFailure @@ -18,7 +17,7 @@ import kotlinx.coroutines.launch class SearchViewModel( private val searchUseCase: SearchUseCase, private val mostPopularMoviesUseCase: MostPopularMoviesUseCase -) : ViewModel(), DefaultLifecycleObserver { +) : ViewModel() { private var tryAgain: () -> Unit = {} From 1b3376fb4409298d7d1164ecc1e1fcfbc1e68d31 Mon Sep 17 00:00:00 2001 From: Rods Date: Wed, 12 Feb 2025 22:27:33 -0300 Subject: [PATCH 73/96] Revert "[ISSUE-21/24] - remove lifecycle and add remember Koin" This reverts commit aac5b7886ab0c7b4549439ff81f2fd161a7f7588. --- .../navigation/DetailStreamNavigation.kt | 12 +++++---- .../screens/DetailStreamsScreen.kt | 12 +++++++++ .../navigation/ListStreamsNavigation.kt | 15 +++++------ .../presentation/screens/ListStreamsScreen.kt | 8 ++++++ .../ProfilePickerStreamNavigation.kt | 16 ++++++------ .../screens/ProfilePickerStreamScreen.kt | 19 ++++++++++++++ .../screens/ProfilePickerStreamViewModel.kt | 8 ++++-- .../navigation/SearchStreamNavigation.kt | 12 ++++----- .../presentation/screens/SearchScreen.kt | 25 ++++++++++++++++++- .../presentation/screens/SearchViewModel.kt | 3 ++- 10 files changed, 99 insertions(+), 31 deletions(-) diff --git a/feature-detail/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_detail/presentation/navigation/DetailStreamNavigation.kt b/feature-detail/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_detail/presentation/navigation/DetailStreamNavigation.kt index 172d5752..7917c3f3 100644 --- a/feature-detail/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_detail/presentation/navigation/DetailStreamNavigation.kt +++ b/feature-detail/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_detail/presentation/navigation/DetailStreamNavigation.kt @@ -1,5 +1,6 @@ package com.codandotv.streamplayerapp.feature_detail.presentation.navigation +import androidx.lifecycle.Lifecycle import androidx.navigation.NavGraphBuilder import androidx.navigation.NavHostController import androidx.navigation.compose.composable @@ -9,18 +10,16 @@ import com.codandotv.streamplayerapp.core_navigation.routes.Routes.PARAM.ID import com.codandotv.streamplayerapp.feature_detail.di.DetailStreamModule import com.codandotv.streamplayerapp.feature_detail.presentation.screens.DetailStreamScreen import org.koin.androidx.compose.koinViewModel -import org.koin.compose.module.rememberKoinModules -import org.koin.core.annotation.KoinExperimentalAPI +import org.koin.core.context.loadKoinModules import org.koin.core.context.unloadKoinModules import org.koin.core.parameter.parametersOf internal const val DEFAULT_ID = "0" -@OptIn(KoinExperimentalAPI::class) fun NavGraphBuilder.detailStreamNavGraph(navController: NavHostController) { composable(DETAIL_COMPLETE) { nav -> - rememberKoinModules { - listOf(DetailStreamModule.module) + if (nav.lifecycle.currentState == Lifecycle.State.STARTED) { + loadKoinModules(DetailStreamModule.module) } DetailStreamScreen( viewModel = koinViewModel { @@ -30,6 +29,9 @@ fun NavGraphBuilder.detailStreamNavGraph(navController: NavHostController) { onNavigateSearchScreen = { navController.navigate(Routes.SEARCH) }, + disposable = { + unloadKoinModules(DetailStreamModule.module) + } ) } } diff --git a/feature-detail/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_detail/presentation/screens/DetailStreamsScreen.kt b/feature-detail/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_detail/presentation/screens/DetailStreamsScreen.kt index 2e32247c..2ce51aec 100644 --- a/feature-detail/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_detail/presentation/screens/DetailStreamsScreen.kt +++ b/feature-detail/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_detail/presentation/screens/DetailStreamsScreen.kt @@ -21,12 +21,14 @@ import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Scaffold import androidx.compose.material3.Text import androidx.compose.runtime.Composable +import androidx.compose.runtime.DisposableEffect import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.LocalLifecycleOwner import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.em @@ -52,13 +54,23 @@ import streamplayerapp_kmp.feature_detail.generated.resources.detail_watch_prima fun DetailStreamScreen( viewModel: DetailStreamViewModel = koinViewModel(), navController: NavController, + disposable: () -> Unit = {}, onNavigateSearchScreen: () -> Unit = {}, ) { val uiState by viewModel.uiState.collectAsStateWithLifecycle() + + val lifecycleOwner = LocalLifecycleOwner.current + LifecycleEventEffect(Lifecycle.Event.ON_START) { viewModel.loadDetail() } + DisposableEffect(lifecycleOwner) { + onDispose { + disposable.invoke() + } + } + when (uiState) { is DetailStreamsUIState.DetailStreamsLoadedUIState -> { SetupDetailScreen( diff --git a/feature-list-streams/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_list_streams/list/presentation/navigation/ListStreamsNavigation.kt b/feature-list-streams/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_list_streams/list/presentation/navigation/ListStreamsNavigation.kt index cb3a5a83..633a92fd 100644 --- a/feature-list-streams/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_list_streams/list/presentation/navigation/ListStreamsNavigation.kt +++ b/feature-list-streams/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_list_streams/list/presentation/navigation/ListStreamsNavigation.kt @@ -12,24 +12,18 @@ import com.codandotv.streamplayerapp.core_navigation.routes.Routes.DETAIL import com.codandotv.streamplayerapp.core_navigation.routes.Routes.PROFILE_PICKER import com.codandotv.streamplayerapp.feature_list_streams.list.di.ListStreamModule import com.codandotv.streamplayerapp.feature_list_streams.list.presentation.screens.ListStreamsScreen -import org.koin.compose.module.rememberKoinModules -import org.koin.core.annotation.KoinExperimentalAPI import org.koin.core.context.loadKoinModules import org.koin.core.context.unloadKoinModules internal const val DEFAULT_ID = "" -@OptIn(KoinExperimentalAPI::class) fun NavGraphBuilder.listStreamsNavGraph(navController: NavHostController) { composable(HOME_COMPLETE) { nav -> BackHandler(true) {} - - rememberKoinModules { - listOf(ListStreamModule.module) + if (nav.lifecycle.currentState == Lifecycle.State.STARTED) { + loadKoinModules(ListStreamModule.module) } - - ListStreamsScreen( - navController = navController, + ListStreamsScreen(navController = navController, onNavigateDetailList = { id -> navController.navigate("${DETAIL}${id}") }, @@ -39,6 +33,9 @@ fun NavGraphBuilder.listStreamsNavGraph(navController: NavHostController) { onNavigateSearchScreen = { navController.navigate(Routes.SEARCH) }, + disposable = { + unloadKoinModules(ListStreamModule.module) + }, profilePicture = nav.arguments?.getString(PROFILE_ID) ?: DEFAULT_ID ) } diff --git a/feature-list-streams/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_list_streams/list/presentation/screens/ListStreamsScreen.kt b/feature-list-streams/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_list_streams/list/presentation/screens/ListStreamsScreen.kt index 581085cf..5916e5ee 100644 --- a/feature-list-streams/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_list_streams/list/presentation/screens/ListStreamsScreen.kt +++ b/feature-list-streams/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_list_streams/list/presentation/screens/ListStreamsScreen.kt @@ -44,13 +44,21 @@ fun ListStreamsScreen( onNavigateDetailList: (String) -> Unit = {}, onNavigateProfilePicker: () -> Unit = {}, onNavigateSearchScreen: () -> Unit = {}, + disposable: () -> Unit = {}, profilePicture: String ) { val uiState by viewModel.uiState.collectAsStateWithLifecycle() + val lifecycleOwner = LocalLifecycleOwner.current val scrollBehavior = TopAppBarDefaults.exitUntilCollapsedScrollBehavior(rememberTopAppBarState()) val baseScrollState = rememberScrollState() + DisposableEffect(lifecycleOwner) { + onDispose { + disposable.invoke() + } + } + Scaffold( modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection), topBar = { diff --git a/feature-profile/src/commonMain/kotlin/com/codandotv/streamplayerapp/profile/presentation/navigation/ProfilePickerStreamNavigation.kt b/feature-profile/src/commonMain/kotlin/com/codandotv/streamplayerapp/profile/presentation/navigation/ProfilePickerStreamNavigation.kt index eb885448..8d42759a 100644 --- a/feature-profile/src/commonMain/kotlin/com/codandotv/streamplayerapp/profile/presentation/navigation/ProfilePickerStreamNavigation.kt +++ b/feature-profile/src/commonMain/kotlin/com/codandotv/streamplayerapp/profile/presentation/navigation/ProfilePickerStreamNavigation.kt @@ -1,5 +1,6 @@ package com.codandotv.streamplayerapp.profile.presentation.navigation +import androidx.lifecycle.Lifecycle import androidx.navigation.NavGraphBuilder import androidx.navigation.NavHostController import androidx.navigation.compose.composable @@ -8,19 +9,20 @@ import com.codandotv.streamplayerapp.core_navigation.routes.BottomNavRoutes.PARA import com.codandotv.streamplayerapp.core_navigation.routes.Routes import com.codandotv.streamplayerapp.profile.di.ProfilePickerStreamModule import com.codandotv.streamplayerapp.profile.presentation.screens.ProfilePickerStreamScreen -import org.koin.compose.module.rememberKoinModules -import org.koin.core.annotation.KoinExperimentalAPI +import org.koin.core.context.loadKoinModules +import org.koin.core.context.unloadKoinModules -@OptIn(KoinExperimentalAPI::class) fun NavGraphBuilder.profilePickerStreamNavGraph(navController: NavHostController) { - composable(Routes.PROFILE_PICKER) { - rememberKoinModules { - listOf(ProfilePickerStreamModule.module) + composable(Routes.PROFILE_PICKER) { nav -> + if (nav.lifecycle.currentState == Lifecycle.State.STARTED) { + loadKoinModules(ProfilePickerStreamModule.module) } ProfilePickerStreamScreen( onNavigateListStreams = { profilePic -> navController.navigate("$HOME?$PROFILE_ID=$profilePic") } - ) + ) { + unloadKoinModules(ProfilePickerStreamModule.module) + } } } diff --git a/feature-profile/src/commonMain/kotlin/com/codandotv/streamplayerapp/profile/presentation/screens/ProfilePickerStreamScreen.kt b/feature-profile/src/commonMain/kotlin/com/codandotv/streamplayerapp/profile/presentation/screens/ProfilePickerStreamScreen.kt index 16046e87..0e8818f2 100644 --- a/feature-profile/src/commonMain/kotlin/com/codandotv/streamplayerapp/profile/presentation/screens/ProfilePickerStreamScreen.kt +++ b/feature-profile/src/commonMain/kotlin/com/codandotv/streamplayerapp/profile/presentation/screens/ProfilePickerStreamScreen.kt @@ -42,8 +42,11 @@ import streamplayerapp_kmp.feature_profile.generated.resources.profile_animation fun ProfilePickerStreamScreen( viewModel: ProfilePickerStreamViewModel = koinViewModel(), onNavigateListStreams: (String) -> Unit = {}, + disposable: () -> Unit = {} ) { val uiState by viewModel.uiState.collectAsStateWithLifecycle() + val lifecycleOwner = LocalLifecycleOwner.current + Lifecycle(lifecycleOwner, viewModel, disposable) if (uiState.isLoading) { LoadScreen() @@ -156,6 +159,22 @@ private fun SetupProfilePickerScreen( ) } +@Composable +private fun Lifecycle( + lifecycleOwner: LifecycleOwner, viewModel: ProfilePickerStreamViewModel, disposable: () -> Unit +) { + DisposableEffect(lifecycleOwner) { + val lifecycle = lifecycleOwner.lifecycle + + lifecycle.addObserver(viewModel) + + onDispose { + lifecycle.removeObserver(viewModel) + disposable.invoke() + } + } +} + @ThemePreviews @Composable fun SetupProfilePickerScreenPreview() { diff --git a/feature-profile/src/commonMain/kotlin/com/codandotv/streamplayerapp/profile/presentation/screens/ProfilePickerStreamViewModel.kt b/feature-profile/src/commonMain/kotlin/com/codandotv/streamplayerapp/profile/presentation/screens/ProfilePickerStreamViewModel.kt index 1eac734f..75b5a2b6 100644 --- a/feature-profile/src/commonMain/kotlin/com/codandotv/streamplayerapp/profile/presentation/screens/ProfilePickerStreamViewModel.kt +++ b/feature-profile/src/commonMain/kotlin/com/codandotv/streamplayerapp/profile/presentation/screens/ProfilePickerStreamViewModel.kt @@ -1,5 +1,7 @@ package com.codandotv.streamplayerapp.profile.presentation.screens +import androidx.lifecycle.DefaultLifecycleObserver +import androidx.lifecycle.LifecycleOwner import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import com.codandotv.streamplayerapp.core_networking.handleError.catchFailure @@ -14,7 +16,7 @@ import kotlinx.coroutines.launch class ProfilePickerStreamViewModel( private val useCase: ProfilePickerStreamUseCase, -) : ViewModel() { +) : ViewModel(), DefaultLifecycleObserver { private val _uiState = MutableStateFlow(ProfilePickerStreamsUIState()) val uiState = _uiState.stateIn( @@ -23,7 +25,9 @@ class ProfilePickerStreamViewModel( initialValue = _uiState.value ) - init { + override fun onCreate(owner: LifecycleOwner) { + super.onCreate(owner) + viewModelScope.launch { useCase.getProfile() .catchFailure { diff --git a/feature-search/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_search/presentation/navigation/SearchStreamNavigation.kt b/feature-search/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_search/presentation/navigation/SearchStreamNavigation.kt index 134ad7e5..ebcf0742 100644 --- a/feature-search/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_search/presentation/navigation/SearchStreamNavigation.kt +++ b/feature-search/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_search/presentation/navigation/SearchStreamNavigation.kt @@ -8,22 +8,22 @@ import androidx.navigation.compose.composable import com.codandotv.streamplayerapp.core_navigation.routes.Routes import com.codandotv.streamplayerapp.feature_search.di.SearchModule import com.codandotv.streamplayerapp.feature_search.presentation.screens.SearchScreen -import org.koin.compose.module.rememberKoinModules -import org.koin.core.annotation.KoinExperimentalAPI import org.koin.core.context.loadKoinModules import org.koin.core.context.unloadKoinModules -@OptIn(KoinExperimentalAPI::class) fun NavGraphBuilder.searchStreamsNavGraph(navController: NavHostController) { - composable(Routes.SEARCH) { _ -> + composable(Routes.SEARCH) { nav -> BackHandler(true) {} - rememberKoinModules { - listOf(SearchModule.module) + if (nav.lifecycle.currentState == Lifecycle.State.STARTED) { + loadKoinModules(SearchModule.module) } SearchScreen( navController = navController, onNavigateDetailList = { id -> navController.navigate("${Routes.DETAIL}${id}") + }, + disposable = { + unloadKoinModules(SearchModule.module) } ) } diff --git a/feature-search/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_search/presentation/screens/SearchScreen.kt b/feature-search/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_search/presentation/screens/SearchScreen.kt index 61b389c6..94ff555e 100644 --- a/feature-search/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_search/presentation/screens/SearchScreen.kt +++ b/feature-search/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_search/presentation/screens/SearchScreen.kt @@ -39,9 +39,16 @@ fun SearchScreen( viewModel: SearchViewModel = koinViewModel(), onNavigateDetailList: (String) -> Unit = {}, navController: NavController, + disposable: () -> Unit = {} ) { val uiState by viewModel.uiState.collectAsState() + Lifecycle( + lifecycleOwner = LocalLifecycleOwner.current, + viewModel = viewModel, + disposable = disposable + ) + when (uiState) { is SearchUIState.Success -> { SetupSearchScreen( @@ -145,4 +152,20 @@ private fun SetupSearchScreen( navController.goBack() } -} \ No newline at end of file +} + +@Composable +private fun Lifecycle( + lifecycleOwner: LifecycleOwner, viewModel: SearchViewModel, disposable: () -> Unit +) { + DisposableEffect(lifecycleOwner) { + val lifecycle = lifecycleOwner.lifecycle + + lifecycle.addObserver(viewModel) + + onDispose { + lifecycle.removeObserver(viewModel) + disposable.invoke() + } + } +} diff --git a/feature-search/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_search/presentation/screens/SearchViewModel.kt b/feature-search/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_search/presentation/screens/SearchViewModel.kt index 8a4314b7..f1d48fdd 100644 --- a/feature-search/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_search/presentation/screens/SearchViewModel.kt +++ b/feature-search/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_search/presentation/screens/SearchViewModel.kt @@ -1,5 +1,6 @@ package com.codandotv.streamplayerapp.feature_search.presentation.screens +import androidx.lifecycle.DefaultLifecycleObserver import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import com.codandotv.streamplayerapp.core_networking.handleError.catchFailure @@ -17,7 +18,7 @@ import kotlinx.coroutines.launch class SearchViewModel( private val searchUseCase: SearchUseCase, private val mostPopularMoviesUseCase: MostPopularMoviesUseCase -) : ViewModel() { +) : ViewModel(), DefaultLifecycleObserver { private var tryAgain: () -> Unit = {} From 3184308d82573d74d94ced6bd5955a29fe6910d0 Mon Sep 17 00:00:00 2001 From: Rods Date: Wed, 12 Feb 2025 22:28:33 -0300 Subject: [PATCH 74/96] [ISSUE-21/24] - remove lifecycle and add remember Koin --- .../navigation/DetailStreamNavigation.kt | 12 ++++----- .../screens/DetailStreamsScreen.kt | 12 --------- .../navigation/ListStreamsNavigation.kt | 15 ++++++----- .../presentation/screens/ListStreamsScreen.kt | 8 ------ .../ProfilePickerStreamNavigation.kt | 16 ++++++------ .../screens/ProfilePickerStreamScreen.kt | 19 -------------- .../screens/ProfilePickerStreamViewModel.kt | 8 ++---- .../navigation/SearchStreamNavigation.kt | 12 ++++----- .../presentation/screens/SearchScreen.kt | 25 +------------------ .../presentation/screens/SearchViewModel.kt | 3 +-- 10 files changed, 31 insertions(+), 99 deletions(-) diff --git a/feature-detail/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_detail/presentation/navigation/DetailStreamNavigation.kt b/feature-detail/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_detail/presentation/navigation/DetailStreamNavigation.kt index 7917c3f3..172d5752 100644 --- a/feature-detail/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_detail/presentation/navigation/DetailStreamNavigation.kt +++ b/feature-detail/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_detail/presentation/navigation/DetailStreamNavigation.kt @@ -1,6 +1,5 @@ package com.codandotv.streamplayerapp.feature_detail.presentation.navigation -import androidx.lifecycle.Lifecycle import androidx.navigation.NavGraphBuilder import androidx.navigation.NavHostController import androidx.navigation.compose.composable @@ -10,16 +9,18 @@ import com.codandotv.streamplayerapp.core_navigation.routes.Routes.PARAM.ID import com.codandotv.streamplayerapp.feature_detail.di.DetailStreamModule import com.codandotv.streamplayerapp.feature_detail.presentation.screens.DetailStreamScreen import org.koin.androidx.compose.koinViewModel -import org.koin.core.context.loadKoinModules +import org.koin.compose.module.rememberKoinModules +import org.koin.core.annotation.KoinExperimentalAPI import org.koin.core.context.unloadKoinModules import org.koin.core.parameter.parametersOf internal const val DEFAULT_ID = "0" +@OptIn(KoinExperimentalAPI::class) fun NavGraphBuilder.detailStreamNavGraph(navController: NavHostController) { composable(DETAIL_COMPLETE) { nav -> - if (nav.lifecycle.currentState == Lifecycle.State.STARTED) { - loadKoinModules(DetailStreamModule.module) + rememberKoinModules { + listOf(DetailStreamModule.module) } DetailStreamScreen( viewModel = koinViewModel { @@ -29,9 +30,6 @@ fun NavGraphBuilder.detailStreamNavGraph(navController: NavHostController) { onNavigateSearchScreen = { navController.navigate(Routes.SEARCH) }, - disposable = { - unloadKoinModules(DetailStreamModule.module) - } ) } } diff --git a/feature-detail/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_detail/presentation/screens/DetailStreamsScreen.kt b/feature-detail/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_detail/presentation/screens/DetailStreamsScreen.kt index 2ce51aec..2e32247c 100644 --- a/feature-detail/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_detail/presentation/screens/DetailStreamsScreen.kt +++ b/feature-detail/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_detail/presentation/screens/DetailStreamsScreen.kt @@ -21,14 +21,12 @@ import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Scaffold import androidx.compose.material3.Text import androidx.compose.runtime.Composable -import androidx.compose.runtime.DisposableEffect import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.platform.LocalLifecycleOwner import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.em @@ -54,23 +52,13 @@ import streamplayerapp_kmp.feature_detail.generated.resources.detail_watch_prima fun DetailStreamScreen( viewModel: DetailStreamViewModel = koinViewModel(), navController: NavController, - disposable: () -> Unit = {}, onNavigateSearchScreen: () -> Unit = {}, ) { val uiState by viewModel.uiState.collectAsStateWithLifecycle() - - val lifecycleOwner = LocalLifecycleOwner.current - LifecycleEventEffect(Lifecycle.Event.ON_START) { viewModel.loadDetail() } - DisposableEffect(lifecycleOwner) { - onDispose { - disposable.invoke() - } - } - when (uiState) { is DetailStreamsUIState.DetailStreamsLoadedUIState -> { SetupDetailScreen( diff --git a/feature-list-streams/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_list_streams/list/presentation/navigation/ListStreamsNavigation.kt b/feature-list-streams/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_list_streams/list/presentation/navigation/ListStreamsNavigation.kt index 633a92fd..cb3a5a83 100644 --- a/feature-list-streams/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_list_streams/list/presentation/navigation/ListStreamsNavigation.kt +++ b/feature-list-streams/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_list_streams/list/presentation/navigation/ListStreamsNavigation.kt @@ -12,18 +12,24 @@ import com.codandotv.streamplayerapp.core_navigation.routes.Routes.DETAIL import com.codandotv.streamplayerapp.core_navigation.routes.Routes.PROFILE_PICKER import com.codandotv.streamplayerapp.feature_list_streams.list.di.ListStreamModule import com.codandotv.streamplayerapp.feature_list_streams.list.presentation.screens.ListStreamsScreen +import org.koin.compose.module.rememberKoinModules +import org.koin.core.annotation.KoinExperimentalAPI import org.koin.core.context.loadKoinModules import org.koin.core.context.unloadKoinModules internal const val DEFAULT_ID = "" +@OptIn(KoinExperimentalAPI::class) fun NavGraphBuilder.listStreamsNavGraph(navController: NavHostController) { composable(HOME_COMPLETE) { nav -> BackHandler(true) {} - if (nav.lifecycle.currentState == Lifecycle.State.STARTED) { - loadKoinModules(ListStreamModule.module) + + rememberKoinModules { + listOf(ListStreamModule.module) } - ListStreamsScreen(navController = navController, + + ListStreamsScreen( + navController = navController, onNavigateDetailList = { id -> navController.navigate("${DETAIL}${id}") }, @@ -33,9 +39,6 @@ fun NavGraphBuilder.listStreamsNavGraph(navController: NavHostController) { onNavigateSearchScreen = { navController.navigate(Routes.SEARCH) }, - disposable = { - unloadKoinModules(ListStreamModule.module) - }, profilePicture = nav.arguments?.getString(PROFILE_ID) ?: DEFAULT_ID ) } diff --git a/feature-list-streams/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_list_streams/list/presentation/screens/ListStreamsScreen.kt b/feature-list-streams/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_list_streams/list/presentation/screens/ListStreamsScreen.kt index 5916e5ee..581085cf 100644 --- a/feature-list-streams/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_list_streams/list/presentation/screens/ListStreamsScreen.kt +++ b/feature-list-streams/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_list_streams/list/presentation/screens/ListStreamsScreen.kt @@ -44,21 +44,13 @@ fun ListStreamsScreen( onNavigateDetailList: (String) -> Unit = {}, onNavigateProfilePicker: () -> Unit = {}, onNavigateSearchScreen: () -> Unit = {}, - disposable: () -> Unit = {}, profilePicture: String ) { val uiState by viewModel.uiState.collectAsStateWithLifecycle() - val lifecycleOwner = LocalLifecycleOwner.current val scrollBehavior = TopAppBarDefaults.exitUntilCollapsedScrollBehavior(rememberTopAppBarState()) val baseScrollState = rememberScrollState() - DisposableEffect(lifecycleOwner) { - onDispose { - disposable.invoke() - } - } - Scaffold( modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection), topBar = { diff --git a/feature-profile/src/commonMain/kotlin/com/codandotv/streamplayerapp/profile/presentation/navigation/ProfilePickerStreamNavigation.kt b/feature-profile/src/commonMain/kotlin/com/codandotv/streamplayerapp/profile/presentation/navigation/ProfilePickerStreamNavigation.kt index 8d42759a..eb885448 100644 --- a/feature-profile/src/commonMain/kotlin/com/codandotv/streamplayerapp/profile/presentation/navigation/ProfilePickerStreamNavigation.kt +++ b/feature-profile/src/commonMain/kotlin/com/codandotv/streamplayerapp/profile/presentation/navigation/ProfilePickerStreamNavigation.kt @@ -1,6 +1,5 @@ package com.codandotv.streamplayerapp.profile.presentation.navigation -import androidx.lifecycle.Lifecycle import androidx.navigation.NavGraphBuilder import androidx.navigation.NavHostController import androidx.navigation.compose.composable @@ -9,20 +8,19 @@ import com.codandotv.streamplayerapp.core_navigation.routes.BottomNavRoutes.PARA import com.codandotv.streamplayerapp.core_navigation.routes.Routes import com.codandotv.streamplayerapp.profile.di.ProfilePickerStreamModule import com.codandotv.streamplayerapp.profile.presentation.screens.ProfilePickerStreamScreen -import org.koin.core.context.loadKoinModules -import org.koin.core.context.unloadKoinModules +import org.koin.compose.module.rememberKoinModules +import org.koin.core.annotation.KoinExperimentalAPI +@OptIn(KoinExperimentalAPI::class) fun NavGraphBuilder.profilePickerStreamNavGraph(navController: NavHostController) { - composable(Routes.PROFILE_PICKER) { nav -> - if (nav.lifecycle.currentState == Lifecycle.State.STARTED) { - loadKoinModules(ProfilePickerStreamModule.module) + composable(Routes.PROFILE_PICKER) { + rememberKoinModules { + listOf(ProfilePickerStreamModule.module) } ProfilePickerStreamScreen( onNavigateListStreams = { profilePic -> navController.navigate("$HOME?$PROFILE_ID=$profilePic") } - ) { - unloadKoinModules(ProfilePickerStreamModule.module) - } + ) } } diff --git a/feature-profile/src/commonMain/kotlin/com/codandotv/streamplayerapp/profile/presentation/screens/ProfilePickerStreamScreen.kt b/feature-profile/src/commonMain/kotlin/com/codandotv/streamplayerapp/profile/presentation/screens/ProfilePickerStreamScreen.kt index 0e8818f2..16046e87 100644 --- a/feature-profile/src/commonMain/kotlin/com/codandotv/streamplayerapp/profile/presentation/screens/ProfilePickerStreamScreen.kt +++ b/feature-profile/src/commonMain/kotlin/com/codandotv/streamplayerapp/profile/presentation/screens/ProfilePickerStreamScreen.kt @@ -42,11 +42,8 @@ import streamplayerapp_kmp.feature_profile.generated.resources.profile_animation fun ProfilePickerStreamScreen( viewModel: ProfilePickerStreamViewModel = koinViewModel(), onNavigateListStreams: (String) -> Unit = {}, - disposable: () -> Unit = {} ) { val uiState by viewModel.uiState.collectAsStateWithLifecycle() - val lifecycleOwner = LocalLifecycleOwner.current - Lifecycle(lifecycleOwner, viewModel, disposable) if (uiState.isLoading) { LoadScreen() @@ -159,22 +156,6 @@ private fun SetupProfilePickerScreen( ) } -@Composable -private fun Lifecycle( - lifecycleOwner: LifecycleOwner, viewModel: ProfilePickerStreamViewModel, disposable: () -> Unit -) { - DisposableEffect(lifecycleOwner) { - val lifecycle = lifecycleOwner.lifecycle - - lifecycle.addObserver(viewModel) - - onDispose { - lifecycle.removeObserver(viewModel) - disposable.invoke() - } - } -} - @ThemePreviews @Composable fun SetupProfilePickerScreenPreview() { diff --git a/feature-profile/src/commonMain/kotlin/com/codandotv/streamplayerapp/profile/presentation/screens/ProfilePickerStreamViewModel.kt b/feature-profile/src/commonMain/kotlin/com/codandotv/streamplayerapp/profile/presentation/screens/ProfilePickerStreamViewModel.kt index 75b5a2b6..1eac734f 100644 --- a/feature-profile/src/commonMain/kotlin/com/codandotv/streamplayerapp/profile/presentation/screens/ProfilePickerStreamViewModel.kt +++ b/feature-profile/src/commonMain/kotlin/com/codandotv/streamplayerapp/profile/presentation/screens/ProfilePickerStreamViewModel.kt @@ -1,7 +1,5 @@ package com.codandotv.streamplayerapp.profile.presentation.screens -import androidx.lifecycle.DefaultLifecycleObserver -import androidx.lifecycle.LifecycleOwner import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import com.codandotv.streamplayerapp.core_networking.handleError.catchFailure @@ -16,7 +14,7 @@ import kotlinx.coroutines.launch class ProfilePickerStreamViewModel( private val useCase: ProfilePickerStreamUseCase, -) : ViewModel(), DefaultLifecycleObserver { +) : ViewModel() { private val _uiState = MutableStateFlow(ProfilePickerStreamsUIState()) val uiState = _uiState.stateIn( @@ -25,9 +23,7 @@ class ProfilePickerStreamViewModel( initialValue = _uiState.value ) - override fun onCreate(owner: LifecycleOwner) { - super.onCreate(owner) - + init { viewModelScope.launch { useCase.getProfile() .catchFailure { diff --git a/feature-search/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_search/presentation/navigation/SearchStreamNavigation.kt b/feature-search/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_search/presentation/navigation/SearchStreamNavigation.kt index ebcf0742..134ad7e5 100644 --- a/feature-search/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_search/presentation/navigation/SearchStreamNavigation.kt +++ b/feature-search/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_search/presentation/navigation/SearchStreamNavigation.kt @@ -8,22 +8,22 @@ import androidx.navigation.compose.composable import com.codandotv.streamplayerapp.core_navigation.routes.Routes import com.codandotv.streamplayerapp.feature_search.di.SearchModule import com.codandotv.streamplayerapp.feature_search.presentation.screens.SearchScreen +import org.koin.compose.module.rememberKoinModules +import org.koin.core.annotation.KoinExperimentalAPI import org.koin.core.context.loadKoinModules import org.koin.core.context.unloadKoinModules +@OptIn(KoinExperimentalAPI::class) fun NavGraphBuilder.searchStreamsNavGraph(navController: NavHostController) { - composable(Routes.SEARCH) { nav -> + composable(Routes.SEARCH) { _ -> BackHandler(true) {} - if (nav.lifecycle.currentState == Lifecycle.State.STARTED) { - loadKoinModules(SearchModule.module) + rememberKoinModules { + listOf(SearchModule.module) } SearchScreen( navController = navController, onNavigateDetailList = { id -> navController.navigate("${Routes.DETAIL}${id}") - }, - disposable = { - unloadKoinModules(SearchModule.module) } ) } diff --git a/feature-search/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_search/presentation/screens/SearchScreen.kt b/feature-search/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_search/presentation/screens/SearchScreen.kt index 94ff555e..61b389c6 100644 --- a/feature-search/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_search/presentation/screens/SearchScreen.kt +++ b/feature-search/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_search/presentation/screens/SearchScreen.kt @@ -39,16 +39,9 @@ fun SearchScreen( viewModel: SearchViewModel = koinViewModel(), onNavigateDetailList: (String) -> Unit = {}, navController: NavController, - disposable: () -> Unit = {} ) { val uiState by viewModel.uiState.collectAsState() - Lifecycle( - lifecycleOwner = LocalLifecycleOwner.current, - viewModel = viewModel, - disposable = disposable - ) - when (uiState) { is SearchUIState.Success -> { SetupSearchScreen( @@ -152,20 +145,4 @@ private fun SetupSearchScreen( navController.goBack() } -} - -@Composable -private fun Lifecycle( - lifecycleOwner: LifecycleOwner, viewModel: SearchViewModel, disposable: () -> Unit -) { - DisposableEffect(lifecycleOwner) { - val lifecycle = lifecycleOwner.lifecycle - - lifecycle.addObserver(viewModel) - - onDispose { - lifecycle.removeObserver(viewModel) - disposable.invoke() - } - } -} +} \ No newline at end of file diff --git a/feature-search/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_search/presentation/screens/SearchViewModel.kt b/feature-search/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_search/presentation/screens/SearchViewModel.kt index f1d48fdd..8a4314b7 100644 --- a/feature-search/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_search/presentation/screens/SearchViewModel.kt +++ b/feature-search/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_search/presentation/screens/SearchViewModel.kt @@ -1,6 +1,5 @@ package com.codandotv.streamplayerapp.feature_search.presentation.screens -import androidx.lifecycle.DefaultLifecycleObserver import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import com.codandotv.streamplayerapp.core_networking.handleError.catchFailure @@ -18,7 +17,7 @@ import kotlinx.coroutines.launch class SearchViewModel( private val searchUseCase: SearchUseCase, private val mostPopularMoviesUseCase: MostPopularMoviesUseCase -) : ViewModel(), DefaultLifecycleObserver { +) : ViewModel() { private var tryAgain: () -> Unit = {} From fbbee50444b7a6c0638c736180426b232b7fbe40 Mon Sep 17 00:00:00 2001 From: Gabriel Bronzatti Moro Date: Sat, 15 Feb 2025 16:12:27 -0300 Subject: [PATCH 75/96] Isolate lottie specific implementation --- .../components/LottieComponent.android.kt | 25 + composeApp/src/androidMain/res/raw/logo.json | 8141 +++++++++++++++++ .../components/LottieComponent.kt | 7 + .../presentation/screens/SplashScreen.kt | 21 +- .../components/LottieComponent.ios.kt | 7 + .../src/androidMain/res/raw/logo.json | 1 - 6 files changed, 8182 insertions(+), 20 deletions(-) create mode 100644 composeApp/src/androidMain/kotlin/com.codandotv.streamplayerapp/presentation/components/LottieComponent.android.kt create mode 100644 composeApp/src/androidMain/res/raw/logo.json create mode 100644 composeApp/src/commonMain/kotlin/com.codandotv.streamplayerapp/presentation/components/LottieComponent.kt create mode 100644 composeApp/src/iosMain/kotlin/com.codandotv.streamplayerapp/presentation/components/LottieComponent.ios.kt delete mode 100644 core-shared-ui/src/androidMain/res/raw/logo.json diff --git a/composeApp/src/androidMain/kotlin/com.codandotv.streamplayerapp/presentation/components/LottieComponent.android.kt b/composeApp/src/androidMain/kotlin/com.codandotv.streamplayerapp/presentation/components/LottieComponent.android.kt new file mode 100644 index 00000000..5cc9afe6 --- /dev/null +++ b/composeApp/src/androidMain/kotlin/com.codandotv.streamplayerapp/presentation/components/LottieComponent.android.kt @@ -0,0 +1,25 @@ +package com.codandotv.streamplayerapp.presentation.components + +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.ui.Modifier +import com.airbnb.lottie.compose.LottieAnimation +import com.airbnb.lottie.compose.LottieCompositionSpec +import com.airbnb.lottie.compose.animateLottieCompositionAsState +import com.airbnb.lottie.compose.rememberLottieComposition +import com.codandotv.streamplayerapp.R + +@Composable +actual fun LottieComponent(modifier: Modifier, onAnimationFinished: () -> Unit) { + val composition by rememberLottieComposition(LottieCompositionSpec.RawRes(R.raw.logo)) + val logoAnimationState = animateLottieCompositionAsState(composition = composition) + + LottieAnimation( + composition = composition, + progress = { logoAnimationState.progress } + ) + + if (logoAnimationState.isAtEnd && logoAnimationState.isPlaying) { + onAnimationFinished() + } +} diff --git a/composeApp/src/androidMain/res/raw/logo.json b/composeApp/src/androidMain/res/raw/logo.json new file mode 100644 index 00000000..32536ffa --- /dev/null +++ b/composeApp/src/androidMain/res/raw/logo.json @@ -0,0 +1,8141 @@ +{ + "v": "5.5.7", + "meta": { + "g": "LottieFiles AE 0.1.21", + "a": "", + "k": "", + "d": "", + "tc": "#FFFFFF" + }, + "fr": 60, + "ip": 0, + "op": 306, + "w": 512, + "h": 512, + "nm": "Nettflix", + "ddd": 0, + "assets": [], + "layers": [ + { + "ddd": 0, + "ind": 1, + "ty": 4, + "nm": "Big N 1", + "sr": 1, + "ks": { + "o": { + "a": 1, + "k": [ + { + "i": { + "x": [ + 0.667 + ], + "y": [ + 1 + ] + }, + "o": { + "x": [ + 0.333 + ], + "y": [ + 0 + ] + }, + "t": 18, + "s": [ + 0 + ] + }, + { + "i": { + "x": [ + 0.833 + ], + "y": [ + 1 + ] + }, + "o": { + "x": [ + 0.167 + ], + "y": [ + 0 + ] + }, + "t": 28.666, + "s": [ + 100 + ] + }, + { + "i": { + "x": [ + 1 + ], + "y": [ + 1 + ] + }, + "o": { + "x": [ + 0.612 + ], + "y": [ + 0 + ] + }, + "t": 114, + "s": [ + 100 + ] + }, + { + "t": 142, + "s": [ + 0 + ] + } + ], + "ix": 11 + }, + "r": { + "a": 0, + "k": 0, + "ix": 10 + }, + "p": { + "a": 1, + "k": [ + { + "i": { + "x": 0.455, + "y": 1 + }, + "o": { + "x": 0.684, + "y": 0 + }, + "t": 104, + "s": [ + 256, + 256, + 0 + ], + "to": [ + -24.667, + 0, + 0 + ], + "ti": [ + 24.667, + 0, + 0 + ] + }, + { + "t": 154.74609375, + "s": [ + 108, + 256, + 0 + ] + } + ], + "ix": 2 + }, + "a": { + "a": 0, + "k": [ + 0.209, + 0.143, + 0 + ], + "ix": 1 + }, + "s": { + "a": 1, + "k": [ + { + "i": { + "x": [ + 0.461, + 0.461, + 0.667 + ], + "y": [ + 1, + 1, + 1 + ] + }, + "o": { + "x": [ + 0.655, + 0.655, + 0.333 + ], + "y": [ + 0, + 0, + 0 + ] + }, + "t": 46, + "s": [ + 66, + 66, + 100 + ] + }, + { + "t": 104, + "s": [ + 21, + 21, + 100 + ] + } + ], + "ix": 6 + } + }, + "ao": 0, + "shapes": [ + { + "ty": "gr", + "it": [ + { + "ind": 0, + "ty": "sh", + "ix": 1, + "ks": { + "a": 1, + "k": [ + { + "i": { + "x": 0.252, + "y": 1 + }, + "o": { + "x": 0.434, + "y": 0 + }, + "t": 18, + "s": [ + { + "i": [ + [ + 0, + 0 + ], + [ + 0, + 0 + ], + [ + 0, + 0 + ], + [ + 0, + 0 + ] + ], + "o": [ + [ + 0, + 0 + ], + [ + 0, + 0 + ], + [ + 42.684, + 1.078 + ], + [ + 0, + 0 + ] + ], + "v": [ + [ + 120.607, + 218.722 + ], + [ + 31.805, + 211.525 + ], + [ + 31.977, + 211.212 + ], + [ + 120.318, + 218.496 + ] + ], + "c": true + } + ] + }, + { + "i": { + "x": 0.833, + "y": 1 + }, + "o": { + "x": 0.167, + "y": 0 + }, + "t": 46, + "s": [ + { + "i": [ + [ + 0, + 0 + ], + [ + 0, + 0 + ], + [ + 0, + 0 + ], + [ + 0, + 0 + ] + ], + "o": [ + [ + 0, + 0 + ], + [ + 0, + 0 + ], + [ + 42.684, + 1.078 + ], + [ + 0, + 0 + ] + ], + "v": [ + [ + -34.128, + -218.21 + ], + [ + -119.9, + -218.21 + ], + [ + 31.977, + 211.212 + ], + [ + 120.318, + 218.496 + ] + ], + "c": true + } + ] + }, + { + "i": { + "x": 0.833, + "y": 1 + }, + "o": { + "x": 0.167, + "y": 0 + }, + "t": 124, + "s": [ + { + "i": [ + [ + 0, + 0 + ], + [ + 0, + 0 + ], + [ + 0, + 0 + ], + [ + 0, + 0 + ] + ], + "o": [ + [ + 0, + 0 + ], + [ + 0, + 0 + ], + [ + 42.684, + 1.078 + ], + [ + 0, + 0 + ] + ], + "v": [ + [ + -34.128, + -218.21 + ], + [ + -119.9, + -218.21 + ], + [ + 31.977, + 211.212 + ], + [ + 120.318, + 218.496 + ] + ], + "c": true + } + ] + }, + { + "t": 156, + "s": [ + { + "i": [ + [ + 0, + 0 + ], + [ + 0, + 0 + ], + [ + 0, + 0 + ], + [ + 0, + 0 + ] + ], + "o": [ + [ + 0, + 0 + ], + [ + 0, + 0 + ], + [ + 44.422, + -6.307 + ], + [ + 0, + 0 + ] + ], + "v": [ + [ + -47.223, + -218.706 + ], + [ + -102.043, + -218.706 + ], + [ + 41.501, + 198.117 + ], + [ + 102.46, + 192.802 + ] + ], + "c": true + } + ] + } + ], + "ix": 2 + }, + "nm": "Path 1", + "mn": "ADBE Vector Shape - Group", + "hd": false + }, + { + "ty": "fl", + "c": { + "a": 0, + "k": [ + 0.898039215686, + 0.035294117647, + 0.078431372549, + 1 + ], + "ix": 4 + }, + "o": { + "a": 0, + "k": 100, + "ix": 5 + }, + "r": 1, + "bm": 0, + "nm": "Fill 1", + "mn": "ADBE Vector Graphic - Fill", + "hd": false + }, + { + "ty": "tr", + "p": { + "a": 0, + "k": [ + 0, + 0 + ], + "ix": 2 + }, + "a": { + "a": 0, + "k": [ + 0, + 0 + ], + "ix": 1 + }, + "s": { + "a": 0, + "k": [ + 100, + 100 + ], + "ix": 3 + }, + "r": { + "a": 0, + "k": 0, + "ix": 6 + }, + "o": { + "a": 0, + "k": 100, + "ix": 7 + }, + "sk": { + "a": 0, + "k": 0, + "ix": 4 + }, + "sa": { + "a": 0, + "k": 0, + "ix": 5 + }, + "nm": "Transform" + } + ], + "nm": "Big N 1", + "np": 2, + "cix": 2, + "bm": 0, + "ix": 1, + "mn": "ADBE Vector Group", + "hd": false + } + ], + "ip": 0, + "op": 3868, + "st": 0, + "bm": 0 + }, + { + "ddd": 0, + "ind": 2, + "ty": 4, + "nm": "Big N 2 matte", + "parent": 1, + "td": 1, + "sr": 1, + "ks": { + "o": { + "a": 1, + "k": [ + { + "i": { + "x": [ + 0.667 + ], + "y": [ + 1 + ] + }, + "o": { + "x": [ + 0.333 + ], + "y": [ + 0 + ] + }, + "t": 0, + "s": [ + 0 + ] + }, + { + "i": { + "x": [ + 0.833 + ], + "y": [ + 1 + ] + }, + "o": { + "x": [ + 0.333 + ], + "y": [ + 0 + ] + }, + "t": 10.666, + "s": [ + 100 + ] + }, + { + "i": { + "x": [ + 1 + ], + "y": [ + 1 + ] + }, + "o": { + "x": [ + 0.612 + ], + "y": [ + 0 + ] + }, + "t": 114, + "s": [ + 100 + ] + }, + { + "t": 142, + "s": [ + 0 + ] + } + ], + "ix": 11 + }, + "r": { + "a": 0, + "k": 0, + "ix": 10 + }, + "p": { + "a": 0, + "k": [ + 77.223, + 0, + 0 + ], + "ix": 2 + }, + "a": { + "a": 0, + "k": [ + 77.223, + 0, + 0 + ], + "ix": 1 + }, + "s": { + "a": 0, + "k": [ + 100, + 100, + 100 + ], + "ix": 6 + } + }, + "ao": 0, + "shapes": [ + { + "ty": "gr", + "it": [ + { + "ind": 0, + "ty": "sh", + "ix": 1, + "ks": { + "a": 1, + "k": [ + { + "i": { + "x": 0.252, + "y": 1 + }, + "o": { + "x": 0.434, + "y": 0 + }, + "t": 0, + "s": [ + { + "i": [ + [ + 0, + 0 + ], + [ + 0, + 0 + ], + [ + 0, + 0 + ], + [ + 1.198, + 0 + ] + ], + "o": [ + [ + 0, + 0 + ], + [ + 0, + 0 + ], + [ + -0.901, + 0 + ], + [ + 0, + 0 + ] + ], + "v": [ + [ + 34.099, + -359.119 + ], + [ + 120.288, + -359.43 + ], + [ + 120.49, + -359.888 + ], + [ + 33.164, + -359.117 + ] + ], + "c": true + } + ] + }, + { + "i": { + "x": 0.833, + "y": 1 + }, + "o": { + "x": 0.167, + "y": 0 + }, + "t": 24, + "s": [ + { + "i": [ + [ + 0, + 0 + ], + [ + 0, + 0 + ], + [ + 0, + 0 + ], + [ + 28.673, + 0.875 + ] + ], + "o": [ + [ + 0, + 0 + ], + [ + 0, + 0 + ], + [ + -28.673, + -3.601 + ], + [ + 0, + 0 + ] + ], + "v": [ + [ + 34.128, + -218.21 + ], + [ + 120.318, + -218.521 + ], + [ + 120.318, + 218.521 + ], + [ + 34.128, + 211.337 + ] + ], + "c": true + } + ] + }, + { + "i": { + "x": 0.833, + "y": 1 + }, + "o": { + "x": 0.167, + "y": 0 + }, + "t": 124, + "s": [ + { + "i": [ + [ + 0, + 0 + ], + [ + 0, + 0 + ], + [ + 0, + 0 + ], + [ + 28.673, + 0.875 + ] + ], + "o": [ + [ + 0, + 0 + ], + [ + 0, + 0 + ], + [ + -28.673, + -3.601 + ], + [ + 0, + 0 + ] + ], + "v": [ + [ + 34.128, + -218.21 + ], + [ + 120.318, + -218.521 + ], + [ + 120.318, + 218.521 + ], + [ + 34.128, + 211.337 + ] + ], + "c": true + } + ] + }, + { + "t": 156, + "s": [ + { + "i": [ + [ + 0, + 0 + ], + [ + 0, + 0 + ], + [ + 0, + 0 + ], + [ + 28.249, + -4.53 + ] + ], + "o": [ + [ + 0, + 0 + ], + [ + 0, + 0 + ], + [ + -27.545, + 1.34 + ], + [ + 0, + 0 + ] + ], + "v": [ + [ + 48.414, + -218.706 + ], + [ + 114.365, + -217.965 + ], + [ + 116.869, + 194.041 + ], + [ + 44.408, + 201.102 + ] + ], + "c": true + } + ] + } + ], + "ix": 2 + }, + "nm": "Path 1", + "mn": "ADBE Vector Shape - Group", + "hd": false + }, + { + "ty": "fl", + "c": { + "a": 0, + "k": [ + 0.694117647059, + 0.023529411765, + 0.058823529412, + 1 + ], + "ix": 4 + }, + "o": { + "a": 0, + "k": 100, + "ix": 5 + }, + "r": 1, + "bm": 0, + "nm": "Fill 1", + "mn": "ADBE Vector Graphic - Fill", + "hd": false + }, + { + "ty": "tr", + "p": { + "a": 0, + "k": [ + 0, + 0 + ], + "ix": 2 + }, + "a": { + "a": 0, + "k": [ + 0, + 0 + ], + "ix": 1 + }, + "s": { + "a": 0, + "k": [ + 100, + 100 + ], + "ix": 3 + }, + "r": { + "a": 0, + "k": 0, + "ix": 6 + }, + "o": { + "a": 0, + "k": 100, + "ix": 7 + }, + "sk": { + "a": 0, + "k": 0, + "ix": 4 + }, + "sa": { + "a": 0, + "k": 0, + "ix": 5 + }, + "nm": "Transform" + } + ], + "nm": "Big N 2", + "np": 2, + "cix": 2, + "bm": 0, + "ix": 1, + "mn": "ADBE Vector Group", + "hd": false + } + ], + "ip": 0, + "op": 3868, + "st": 0, + "bm": 0 + }, + { + "ddd": 0, + "ind": 3, + "ty": 4, + "nm": "Big N 1 shadow 2", + "tt": 1, + "sr": 1, + "ks": { + "o": { + "a": 1, + "k": [ + { + "i": { + "x": [ + 0.667 + ], + "y": [ + 1 + ] + }, + "o": { + "x": [ + 0.333 + ], + "y": [ + 0 + ] + }, + "t": 18, + "s": [ + 0 + ] + }, + { + "i": { + "x": [ + 0.833 + ], + "y": [ + 1 + ] + }, + "o": { + "x": [ + 0.167 + ], + "y": [ + 0 + ] + }, + "t": 28.666, + "s": [ + 100 + ] + }, + { + "i": { + "x": [ + 1 + ], + "y": [ + 1 + ] + }, + "o": { + "x": [ + 0.612 + ], + "y": [ + 0 + ] + }, + "t": 114, + "s": [ + 100 + ] + }, + { + "t": 142, + "s": [ + 0 + ] + } + ], + "ix": 11 + }, + "r": { + "a": 0, + "k": 0, + "ix": 10 + }, + "p": { + "a": 1, + "k": [ + { + "i": { + "x": 0.455, + "y": 1 + }, + "o": { + "x": 0.684, + "y": 0 + }, + "t": 104, + "s": [ + 256, + 256, + 0 + ], + "to": [ + -24.667, + 0, + 0 + ], + "ti": [ + 24.667, + 0, + 0 + ] + }, + { + "t": 154.74609375, + "s": [ + 108, + 256, + 0 + ] + } + ], + "ix": 2 + }, + "a": { + "a": 0, + "k": [ + 0.209, + 0.143, + 0 + ], + "ix": 1 + }, + "s": { + "a": 1, + "k": [ + { + "i": { + "x": [ + 0.461, + 0.461, + 0.667 + ], + "y": [ + 1, + 1, + 1 + ] + }, + "o": { + "x": [ + 0.655, + 0.655, + 0.333 + ], + "y": [ + 0, + 0, + 0 + ] + }, + "t": 46, + "s": [ + 66, + 66, + 100 + ] + }, + { + "t": 104, + "s": [ + 21, + 21, + 100 + ] + } + ], + "ix": 6 + } + }, + "ao": 0, + "ef": [ + { + "ty": 25, + "nm": "Drop Shadow", + "np": 8, + "mn": "ADBE Drop Shadow", + "ix": 1, + "en": 1, + "ef": [ + { + "ty": 2, + "nm": "Shadow Color", + "mn": "ADBE Drop Shadow-0001", + "ix": 1, + "v": { + "a": 0, + "k": [ + 0.086274512112, + 0, + 0.0074725952, + 1 + ], + "ix": 1 + } + }, + { + "ty": 0, + "nm": "Opacity", + "mn": "ADBE Drop Shadow-0002", + "ix": 2, + "v": { + "a": 0, + "k": 178.5, + "ix": 2 + } + }, + { + "ty": 0, + "nm": "Direction", + "mn": "ADBE Drop Shadow-0003", + "ix": 3, + "v": { + "a": 0, + "k": 0, + "ix": 3 + } + }, + { + "ty": 0, + "nm": "Distance", + "mn": "ADBE Drop Shadow-0004", + "ix": 4, + "v": { + "a": 0, + "k": 0, + "ix": 4 + } + }, + { + "ty": 0, + "nm": "Softness", + "mn": "ADBE Drop Shadow-0005", + "ix": 5, + "v": { + "a": 0, + "k": 100, + "ix": 5 + } + }, + { + "ty": 7, + "nm": "Shadow Only", + "mn": "ADBE Drop Shadow-0006", + "ix": 6, + "v": { + "a": 0, + "k": 0, + "ix": 6 + } + } + ] + } + ], + "shapes": [ + { + "ty": "gr", + "it": [ + { + "ind": 0, + "ty": "sh", + "ix": 1, + "ks": { + "a": 1, + "k": [ + { + "i": { + "x": 0.252, + "y": 1 + }, + "o": { + "x": 0.434, + "y": 0 + }, + "t": 18, + "s": [ + { + "i": [ + [ + 0, + 0 + ], + [ + 0, + 0 + ], + [ + 0, + 0 + ], + [ + 0, + 0 + ] + ], + "o": [ + [ + 0, + 0 + ], + [ + 0, + 0 + ], + [ + 42.684, + 1.078 + ], + [ + 0, + 0 + ] + ], + "v": [ + [ + 120.607, + 218.722 + ], + [ + 31.805, + 211.525 + ], + [ + 31.977, + 211.212 + ], + [ + 120.318, + 218.496 + ] + ], + "c": true + } + ] + }, + { + "i": { + "x": 0.833, + "y": 1 + }, + "o": { + "x": 0.167, + "y": 0 + }, + "t": 46, + "s": [ + { + "i": [ + [ + 0, + 0 + ], + [ + 0, + 0 + ], + [ + 0, + 0 + ], + [ + 0, + 0 + ] + ], + "o": [ + [ + 0, + 0 + ], + [ + 0, + 0 + ], + [ + 42.684, + 1.078 + ], + [ + 0, + 0 + ] + ], + "v": [ + [ + -34.128, + -218.21 + ], + [ + -119.9, + -218.21 + ], + [ + 31.977, + 211.212 + ], + [ + 120.318, + 218.496 + ] + ], + "c": true + } + ] + }, + { + "i": { + "x": 0.833, + "y": 1 + }, + "o": { + "x": 0.167, + "y": 0 + }, + "t": 124, + "s": [ + { + "i": [ + [ + 0, + 0 + ], + [ + 0, + 0 + ], + [ + 0, + 0 + ], + [ + 0, + 0 + ] + ], + "o": [ + [ + 0, + 0 + ], + [ + 0, + 0 + ], + [ + 42.684, + 1.078 + ], + [ + 0, + 0 + ] + ], + "v": [ + [ + -34.128, + -218.21 + ], + [ + -119.9, + -218.21 + ], + [ + 31.977, + 211.212 + ], + [ + 120.318, + 218.496 + ] + ], + "c": true + } + ] + }, + { + "t": 156, + "s": [ + { + "i": [ + [ + 0, + 0 + ], + [ + 0, + 0 + ], + [ + 0, + 0 + ], + [ + 0, + 0 + ] + ], + "o": [ + [ + 0, + 0 + ], + [ + 0, + 0 + ], + [ + 44.422, + -6.307 + ], + [ + 0, + 0 + ] + ], + "v": [ + [ + -47.223, + -218.706 + ], + [ + -102.043, + -218.706 + ], + [ + 41.501, + 198.117 + ], + [ + 102.46, + 192.802 + ] + ], + "c": true + } + ] + } + ], + "ix": 2 + }, + "nm": "Path 1", + "mn": "ADBE Vector Shape - Group", + "hd": false + }, + { + "ty": "fl", + "c": { + "a": 0, + "k": [ + 0.898039215686, + 0.035294117647, + 0.078431372549, + 1 + ], + "ix": 4 + }, + "o": { + "a": 0, + "k": 100, + "ix": 5 + }, + "r": 1, + "bm": 0, + "nm": "Fill 1", + "mn": "ADBE Vector Graphic - Fill", + "hd": false + }, + { + "ty": "tr", + "p": { + "a": 0, + "k": [ + 0, + 0 + ], + "ix": 2 + }, + "a": { + "a": 0, + "k": [ + 0, + 0 + ], + "ix": 1 + }, + "s": { + "a": 0, + "k": [ + 100, + 100 + ], + "ix": 3 + }, + "r": { + "a": 0, + "k": 0, + "ix": 6 + }, + "o": { + "a": 0, + "k": 100, + "ix": 7 + }, + "sk": { + "a": 0, + "k": 0, + "ix": 4 + }, + "sa": { + "a": 0, + "k": 0, + "ix": 5 + }, + "nm": "Transform" + } + ], + "nm": "Big N 1", + "np": 2, + "cix": 2, + "bm": 0, + "ix": 1, + "mn": "ADBE Vector Group", + "hd": false + }, + { + "ty": "gr", + "it": [ + { + "ty": "tr", + "p": { + "a": 0, + "k": [ + 0, + 0 + ], + "ix": 2 + }, + "a": { + "a": 0, + "k": [ + 0, + 0 + ], + "ix": 1 + }, + "s": { + "a": 0, + "k": [ + 100, + 100 + ], + "ix": 3 + }, + "r": { + "a": 0, + "k": 0, + "ix": 6 + }, + "o": { + "a": 0, + "k": 100, + "ix": 7 + }, + "sk": { + "a": 0, + "k": 0, + "ix": 4 + }, + "sa": { + "a": 0, + "k": 0, + "ix": 5 + }, + "nm": "Transform" + } + ], + "nm": "Group 1", + "np": 0, + "cix": 2, + "bm": 0, + "ix": 2, + "mn": "ADBE Vector Group", + "hd": false + } + ], + "ip": 0, + "op": 3868, + "st": 0, + "bm": 0 + }, + { + "ddd": 0, + "ind": 4, + "ty": 4, + "nm": "Big N 2", + "parent": 1, + "sr": 1, + "ks": { + "o": { + "a": 1, + "k": [ + { + "i": { + "x": [ + 0.667 + ], + "y": [ + 1 + ] + }, + "o": { + "x": [ + 0.333 + ], + "y": [ + 0 + ] + }, + "t": 0, + "s": [ + 0 + ] + }, + { + "i": { + "x": [ + 0.833 + ], + "y": [ + 1 + ] + }, + "o": { + "x": [ + 0.333 + ], + "y": [ + 0 + ] + }, + "t": 10.666, + "s": [ + 100 + ] + }, + { + "i": { + "x": [ + 1 + ], + "y": [ + 1 + ] + }, + "o": { + "x": [ + 0.612 + ], + "y": [ + 0 + ] + }, + "t": 114, + "s": [ + 100 + ] + }, + { + "t": 142, + "s": [ + 0 + ] + } + ], + "ix": 11 + }, + "r": { + "a": 0, + "k": 0, + "ix": 10 + }, + "p": { + "a": 0, + "k": [ + 77.223, + 0, + 0 + ], + "ix": 2 + }, + "a": { + "a": 0, + "k": [ + 77.223, + 0, + 0 + ], + "ix": 1 + }, + "s": { + "a": 0, + "k": [ + 100, + 100, + 100 + ], + "ix": 6 + } + }, + "ao": 0, + "shapes": [ + { + "ty": "gr", + "it": [ + { + "ind": 0, + "ty": "sh", + "ix": 1, + "ks": { + "a": 1, + "k": [ + { + "i": { + "x": 0.252, + "y": 1 + }, + "o": { + "x": 0.434, + "y": 0 + }, + "t": 0, + "s": [ + { + "i": [ + [ + 0, + 0 + ], + [ + 0, + 0 + ], + [ + 0, + 0 + ], + [ + 1.198, + 0 + ] + ], + "o": [ + [ + 0, + 0 + ], + [ + 0, + 0 + ], + [ + -0.901, + 0 + ], + [ + 0, + 0 + ] + ], + "v": [ + [ + 34.099, + -359.119 + ], + [ + 120.288, + -359.43 + ], + [ + 120.49, + -359.888 + ], + [ + 33.164, + -359.117 + ] + ], + "c": true + } + ] + }, + { + "i": { + "x": 0.833, + "y": 1 + }, + "o": { + "x": 0.167, + "y": 0 + }, + "t": 24, + "s": [ + { + "i": [ + [ + 0, + 0 + ], + [ + 0, + 0 + ], + [ + 0, + 0 + ], + [ + 28.673, + 0.875 + ] + ], + "o": [ + [ + 0, + 0 + ], + [ + 0, + 0 + ], + [ + -28.673, + -3.601 + ], + [ + 0, + 0 + ] + ], + "v": [ + [ + 34.128, + -218.21 + ], + [ + 120.318, + -218.521 + ], + [ + 120.318, + 218.521 + ], + [ + 34.128, + 211.337 + ] + ], + "c": true + } + ] + }, + { + "i": { + "x": 0.833, + "y": 1 + }, + "o": { + "x": 0.167, + "y": 0 + }, + "t": 124, + "s": [ + { + "i": [ + [ + 0, + 0 + ], + [ + 0, + 0 + ], + [ + 0, + 0 + ], + [ + 28.673, + 0.875 + ] + ], + "o": [ + [ + 0, + 0 + ], + [ + 0, + 0 + ], + [ + -28.673, + -3.601 + ], + [ + 0, + 0 + ] + ], + "v": [ + [ + 34.128, + -218.21 + ], + [ + 120.318, + -218.521 + ], + [ + 120.318, + 218.521 + ], + [ + 34.128, + 211.337 + ] + ], + "c": true + } + ] + }, + { + "t": 156, + "s": [ + { + "i": [ + [ + 0, + 0 + ], + [ + 0, + 0 + ], + [ + 0, + 0 + ], + [ + 28.249, + -4.53 + ] + ], + "o": [ + [ + 0, + 0 + ], + [ + 0, + 0 + ], + [ + -27.545, + 1.34 + ], + [ + 0, + 0 + ] + ], + "v": [ + [ + 48.414, + -218.706 + ], + [ + 114.365, + -217.965 + ], + [ + 116.869, + 194.041 + ], + [ + 44.408, + 201.102 + ] + ], + "c": true + } + ] + } + ], + "ix": 2 + }, + "nm": "Path 1", + "mn": "ADBE Vector Shape - Group", + "hd": false + }, + { + "ty": "fl", + "c": { + "a": 0, + "k": [ + 0.694117647059, + 0.023529411765, + 0.058823529412, + 1 + ], + "ix": 4 + }, + "o": { + "a": 0, + "k": 100, + "ix": 5 + }, + "r": 1, + "bm": 0, + "nm": "Fill 1", + "mn": "ADBE Vector Graphic - Fill", + "hd": false + }, + { + "ty": "tr", + "p": { + "a": 0, + "k": [ + 0, + 0 + ], + "ix": 2 + }, + "a": { + "a": 0, + "k": [ + 0, + 0 + ], + "ix": 1 + }, + "s": { + "a": 0, + "k": [ + 100, + 100 + ], + "ix": 3 + }, + "r": { + "a": 0, + "k": 0, + "ix": 6 + }, + "o": { + "a": 0, + "k": 100, + "ix": 7 + }, + "sk": { + "a": 0, + "k": 0, + "ix": 4 + }, + "sa": { + "a": 0, + "k": 0, + "ix": 5 + }, + "nm": "Transform" + } + ], + "nm": "Big N 2", + "np": 2, + "cix": 2, + "bm": 0, + "ix": 1, + "mn": "ADBE Vector Group", + "hd": false + } + ], + "ip": 0, + "op": 3868, + "st": 0, + "bm": 0 + }, + { + "ddd": 0, + "ind": 5, + "ty": 4, + "nm": "Big N 3 matte", + "parent": 1, + "td": 1, + "sr": 1, + "ks": { + "o": { + "a": 1, + "k": [ + { + "i": { + "x": [ + 0.667 + ], + "y": [ + 1 + ] + }, + "o": { + "x": [ + 0.333 + ], + "y": [ + 0 + ] + }, + "t": 38, + "s": [ + 0 + ] + }, + { + "i": { + "x": [ + 0.833 + ], + "y": [ + 1 + ] + }, + "o": { + "x": [ + 0.167 + ], + "y": [ + 0 + ] + }, + "t": 48.666, + "s": [ + 100 + ] + }, + { + "i": { + "x": [ + 1 + ], + "y": [ + 1 + ] + }, + "o": { + "x": [ + 0.612 + ], + "y": [ + 0 + ] + }, + "t": 114, + "s": [ + 100 + ] + }, + { + "t": 142, + "s": [ + 0 + ] + } + ], + "ix": 11 + }, + "r": { + "a": 0, + "k": 0, + "ix": 10 + }, + "p": { + "a": 0, + "k": [ + -77.223, + 0, + 0 + ], + "ix": 2 + }, + "a": { + "a": 0, + "k": [ + -77.223, + 0, + 0 + ], + "ix": 1 + }, + "s": { + "a": 0, + "k": [ + 100, + 100, + 100 + ], + "ix": 6 + } + }, + "ao": 0, + "shapes": [ + { + "ty": "gr", + "it": [ + { + "ind": 0, + "ty": "sh", + "ix": 1, + "ks": { + "a": 1, + "k": [ + { + "i": { + "x": 0.252, + "y": 1 + }, + "o": { + "x": 0.434, + "y": 0 + }, + "t": 38, + "s": [ + { + "i": [ + [ + 0, + 0 + ], + [ + 0, + 0 + ], + [ + 0, + 0 + ], + [ + 0, + 1.089 + ] + ], + "o": [ + [ + 0, + 0 + ], + [ + 0, + 0 + ], + [ + 26.294, + 0.724 + ], + [ + 0, + 0 + ] + ], + "v": [ + [ + -34.128, + -218.21 + ], + [ + -120.318, + -218.521 + ], + [ + -120.72, + -218.979 + ], + [ + -34.705, + -218.587 + ] + ], + "c": true + } + ] + }, + { + "i": { + "x": 0.833, + "y": 1 + }, + "o": { + "x": 0.167, + "y": 0 + }, + "t": 60, + "s": [ + { + "i": [ + [ + 0, + 0 + ], + [ + 0, + 0 + ], + [ + 0, + 0 + ], + [ + -28.673, + 0.875 + ] + ], + "o": [ + [ + 0, + 0 + ], + [ + 0, + 0 + ], + [ + 28.673, + -3.601 + ], + [ + 0, + 0 + ] + ], + "v": [ + [ + -34.128, + -218.21 + ], + [ + -120.318, + -218.521 + ], + [ + -120.318, + 218.521 + ], + [ + -34.128, + 211.337 + ] + ], + "c": true + } + ] + }, + { + "i": { + "x": 0.833, + "y": 1 + }, + "o": { + "x": 0.167, + "y": 0 + }, + "t": 124, + "s": [ + { + "i": [ + [ + 0, + 0 + ], + [ + 0, + 0 + ], + [ + 0, + 0 + ], + [ + -28.673, + 0.875 + ] + ], + "o": [ + [ + 0, + 0 + ], + [ + 0, + 0 + ], + [ + 28.673, + -3.601 + ], + [ + 0, + 0 + ] + ], + "v": [ + [ + -34.128, + -218.21 + ], + [ + -120.318, + -218.521 + ], + [ + -120.318, + 218.521 + ], + [ + -34.128, + 211.337 + ] + ], + "c": true + } + ] + }, + { + "t": 156, + "s": [ + { + "i": [ + [ + 0, + 0 + ], + [ + 0, + 0 + ], + [ + 0, + 0 + ], + [ + -28.758, + -0.959 + ] + ], + "o": [ + [ + 0, + 0 + ], + [ + 0, + 0 + ], + [ + 28.673, + -3.601 + ], + [ + 0, + 0 + ] + ], + "v": [ + [ + -43.652, + -218.74 + ], + [ + -106.032, + -218.137 + ], + [ + -106.032, + 217.33 + ], + [ + -37.7, + 211.816 + ] + ], + "c": true + } + ] + } + ], + "ix": 2 + }, + "nm": "Path 1", + "mn": "ADBE Vector Shape - Group", + "hd": false + }, + { + "ty": "fl", + "c": { + "a": 0, + "k": [ + 0.694117647059, + 0.023529411765, + 0.058823529412, + 1 + ], + "ix": 4 + }, + "o": { + "a": 0, + "k": 100, + "ix": 5 + }, + "r": 1, + "bm": 0, + "nm": "Fill 1", + "mn": "ADBE Vector Graphic - Fill", + "hd": false + }, + { + "ty": "tr", + "p": { + "a": 0, + "k": [ + 0, + 0 + ], + "ix": 2 + }, + "a": { + "a": 0, + "k": [ + 0, + 0 + ], + "ix": 1 + }, + "s": { + "a": 0, + "k": [ + 100, + 100 + ], + "ix": 3 + }, + "r": { + "a": 0, + "k": 0, + "ix": 6 + }, + "o": { + "a": 0, + "k": 100, + "ix": 7 + }, + "sk": { + "a": 0, + "k": 0, + "ix": 4 + }, + "sa": { + "a": 0, + "k": 0, + "ix": 5 + }, + "nm": "Transform" + } + ], + "nm": "Big N 3", + "np": 2, + "cix": 2, + "bm": 0, + "ix": 1, + "mn": "ADBE Vector Group", + "hd": false + } + ], + "ip": 0, + "op": 3868, + "st": 0, + "bm": 0 + }, + { + "ddd": 0, + "ind": 6, + "ty": 4, + "nm": "Big N 1 shadow", + "tt": 1, + "sr": 1, + "ks": { + "o": { + "a": 1, + "k": [ + { + "i": { + "x": [ + 0.667 + ], + "y": [ + 1 + ] + }, + "o": { + "x": [ + 0.333 + ], + "y": [ + 0 + ] + }, + "t": 18, + "s": [ + 0 + ] + }, + { + "i": { + "x": [ + 0.833 + ], + "y": [ + 1 + ] + }, + "o": { + "x": [ + 0.167 + ], + "y": [ + 0 + ] + }, + "t": 28.666, + "s": [ + 100 + ] + }, + { + "i": { + "x": [ + 1 + ], + "y": [ + 1 + ] + }, + "o": { + "x": [ + 0.612 + ], + "y": [ + 0 + ] + }, + "t": 114, + "s": [ + 100 + ] + }, + { + "t": 142, + "s": [ + 0 + ] + } + ], + "ix": 11 + }, + "r": { + "a": 0, + "k": 0, + "ix": 10 + }, + "p": { + "a": 1, + "k": [ + { + "i": { + "x": 0.455, + "y": 1 + }, + "o": { + "x": 0.684, + "y": 0 + }, + "t": 104, + "s": [ + 256, + 256, + 0 + ], + "to": [ + -24.667, + 0, + 0 + ], + "ti": [ + 24.667, + 0, + 0 + ] + }, + { + "t": 154.74609375, + "s": [ + 108, + 256, + 0 + ] + } + ], + "ix": 2 + }, + "a": { + "a": 0, + "k": [ + 0.209, + 0.143, + 0 + ], + "ix": 1 + }, + "s": { + "a": 1, + "k": [ + { + "i": { + "x": [ + 0.461, + 0.461, + 0.667 + ], + "y": [ + 1, + 1, + 1 + ] + }, + "o": { + "x": [ + 0.655, + 0.655, + 0.333 + ], + "y": [ + 0, + 0, + 0 + ] + }, + "t": 46, + "s": [ + 66, + 66, + 100 + ] + }, + { + "t": 104, + "s": [ + 21, + 21, + 100 + ] + } + ], + "ix": 6 + } + }, + "ao": 0, + "ef": [ + { + "ty": 25, + "nm": "Drop Shadow", + "np": 8, + "mn": "ADBE Drop Shadow", + "ix": 1, + "en": 1, + "ef": [ + { + "ty": 2, + "nm": "Shadow Color", + "mn": "ADBE Drop Shadow-0001", + "ix": 1, + "v": { + "a": 0, + "k": [ + 0.086274512112, + 0, + 0.0074725952, + 1 + ], + "ix": 1 + } + }, + { + "ty": 0, + "nm": "Opacity", + "mn": "ADBE Drop Shadow-0002", + "ix": 2, + "v": { + "a": 0, + "k": 178.5, + "ix": 2 + } + }, + { + "ty": 0, + "nm": "Direction", + "mn": "ADBE Drop Shadow-0003", + "ix": 3, + "v": { + "a": 0, + "k": 0, + "ix": 3 + } + }, + { + "ty": 0, + "nm": "Distance", + "mn": "ADBE Drop Shadow-0004", + "ix": 4, + "v": { + "a": 0, + "k": 0, + "ix": 4 + } + }, + { + "ty": 0, + "nm": "Softness", + "mn": "ADBE Drop Shadow-0005", + "ix": 5, + "v": { + "a": 0, + "k": 100, + "ix": 5 + } + }, + { + "ty": 7, + "nm": "Shadow Only", + "mn": "ADBE Drop Shadow-0006", + "ix": 6, + "v": { + "a": 0, + "k": 0, + "ix": 6 + } + } + ] + } + ], + "shapes": [ + { + "ty": "gr", + "it": [ + { + "ind": 0, + "ty": "sh", + "ix": 1, + "ks": { + "a": 1, + "k": [ + { + "i": { + "x": 0.252, + "y": 1 + }, + "o": { + "x": 0.434, + "y": 0 + }, + "t": 18, + "s": [ + { + "i": [ + [ + 0, + 0 + ], + [ + 0, + 0 + ], + [ + 0, + 0 + ], + [ + 0, + 0 + ] + ], + "o": [ + [ + 0, + 0 + ], + [ + 0, + 0 + ], + [ + 42.684, + 1.078 + ], + [ + 0, + 0 + ] + ], + "v": [ + [ + 120.607, + 218.722 + ], + [ + 31.805, + 211.525 + ], + [ + 31.977, + 211.212 + ], + [ + 120.318, + 218.496 + ] + ], + "c": true + } + ] + }, + { + "i": { + "x": 0.833, + "y": 1 + }, + "o": { + "x": 0.167, + "y": 0 + }, + "t": 46, + "s": [ + { + "i": [ + [ + 0, + 0 + ], + [ + 0, + 0 + ], + [ + 0, + 0 + ], + [ + 0, + 0 + ] + ], + "o": [ + [ + 0, + 0 + ], + [ + 0, + 0 + ], + [ + 42.684, + 1.078 + ], + [ + 0, + 0 + ] + ], + "v": [ + [ + -34.128, + -218.21 + ], + [ + -119.9, + -218.21 + ], + [ + 31.977, + 211.212 + ], + [ + 120.318, + 218.496 + ] + ], + "c": true + } + ] + }, + { + "i": { + "x": 0.833, + "y": 1 + }, + "o": { + "x": 0.167, + "y": 0 + }, + "t": 124, + "s": [ + { + "i": [ + [ + 0, + 0 + ], + [ + 0, + 0 + ], + [ + 0, + 0 + ], + [ + 0, + 0 + ] + ], + "o": [ + [ + 0, + 0 + ], + [ + 0, + 0 + ], + [ + 42.684, + 1.078 + ], + [ + 0, + 0 + ] + ], + "v": [ + [ + -34.128, + -218.21 + ], + [ + -119.9, + -218.21 + ], + [ + 31.977, + 211.212 + ], + [ + 120.318, + 218.496 + ] + ], + "c": true + } + ] + }, + { + "t": 156, + "s": [ + { + "i": [ + [ + 0, + 0 + ], + [ + 0, + 0 + ], + [ + 0, + 0 + ], + [ + 0, + 0 + ] + ], + "o": [ + [ + 0, + 0 + ], + [ + 0, + 0 + ], + [ + 44.422, + -6.307 + ], + [ + 0, + 0 + ] + ], + "v": [ + [ + -47.223, + -218.706 + ], + [ + -102.043, + -218.706 + ], + [ + 41.501, + 198.117 + ], + [ + 102.46, + 192.802 + ] + ], + "c": true + } + ] + } + ], + "ix": 2 + }, + "nm": "Path 1", + "mn": "ADBE Vector Shape - Group", + "hd": false + }, + { + "ty": "fl", + "c": { + "a": 0, + "k": [ + 0.898039215686, + 0.035294117647, + 0.078431372549, + 1 + ], + "ix": 4 + }, + "o": { + "a": 0, + "k": 100, + "ix": 5 + }, + "r": 1, + "bm": 0, + "nm": "Fill 1", + "mn": "ADBE Vector Graphic - Fill", + "hd": false + }, + { + "ty": "tr", + "p": { + "a": 0, + "k": [ + 0, + 0 + ], + "ix": 2 + }, + "a": { + "a": 0, + "k": [ + 0, + 0 + ], + "ix": 1 + }, + "s": { + "a": 0, + "k": [ + 100, + 100 + ], + "ix": 3 + }, + "r": { + "a": 0, + "k": 0, + "ix": 6 + }, + "o": { + "a": 0, + "k": 100, + "ix": 7 + }, + "sk": { + "a": 0, + "k": 0, + "ix": 4 + }, + "sa": { + "a": 0, + "k": 0, + "ix": 5 + }, + "nm": "Transform" + } + ], + "nm": "Big N 1", + "np": 2, + "cix": 2, + "bm": 0, + "ix": 1, + "mn": "ADBE Vector Group", + "hd": false + } + ], + "ip": 0, + "op": 3868, + "st": 0, + "bm": 0 + }, + { + "ddd": 0, + "ind": 7, + "ty": 4, + "nm": "Big N 3", + "parent": 1, + "sr": 1, + "ks": { + "o": { + "a": 1, + "k": [ + { + "i": { + "x": [ + 0.667 + ], + "y": [ + 1 + ] + }, + "o": { + "x": [ + 0.333 + ], + "y": [ + 0 + ] + }, + "t": 38, + "s": [ + 0 + ] + }, + { + "i": { + "x": [ + 0.833 + ], + "y": [ + 1 + ] + }, + "o": { + "x": [ + 0.167 + ], + "y": [ + 0 + ] + }, + "t": 48.666, + "s": [ + 100 + ] + }, + { + "i": { + "x": [ + 1 + ], + "y": [ + 1 + ] + }, + "o": { + "x": [ + 0.612 + ], + "y": [ + 0 + ] + }, + "t": 114, + "s": [ + 100 + ] + }, + { + "t": 142, + "s": [ + 0 + ] + } + ], + "ix": 11 + }, + "r": { + "a": 0, + "k": 0, + "ix": 10 + }, + "p": { + "a": 0, + "k": [ + -77.223, + 0, + 0 + ], + "ix": 2 + }, + "a": { + "a": 0, + "k": [ + -77.223, + 0, + 0 + ], + "ix": 1 + }, + "s": { + "a": 0, + "k": [ + 100, + 100, + 100 + ], + "ix": 6 + } + }, + "ao": 0, + "shapes": [ + { + "ty": "gr", + "it": [ + { + "ind": 0, + "ty": "sh", + "ix": 1, + "ks": { + "a": 1, + "k": [ + { + "i": { + "x": 0.252, + "y": 1 + }, + "o": { + "x": 0.434, + "y": 0 + }, + "t": 38, + "s": [ + { + "i": [ + [ + 0, + 0 + ], + [ + 0, + 0 + ], + [ + 0, + 0 + ], + [ + 0, + 1.089 + ] + ], + "o": [ + [ + 0, + 0 + ], + [ + 0, + 0 + ], + [ + 26.294, + 0.724 + ], + [ + 0, + 0 + ] + ], + "v": [ + [ + -34.128, + -218.21 + ], + [ + -120.318, + -218.521 + ], + [ + -120.72, + -218.979 + ], + [ + -34.705, + -218.587 + ] + ], + "c": true + } + ] + }, + { + "i": { + "x": 0.833, + "y": 1 + }, + "o": { + "x": 0.167, + "y": 0 + }, + "t": 60, + "s": [ + { + "i": [ + [ + 0, + 0 + ], + [ + 0, + 0 + ], + [ + 0, + 0 + ], + [ + -28.673, + 0.875 + ] + ], + "o": [ + [ + 0, + 0 + ], + [ + 0, + 0 + ], + [ + 28.673, + -3.601 + ], + [ + 0, + 0 + ] + ], + "v": [ + [ + -34.128, + -218.21 + ], + [ + -120.318, + -218.521 + ], + [ + -120.318, + 218.521 + ], + [ + -34.128, + 211.337 + ] + ], + "c": true + } + ] + }, + { + "i": { + "x": 0.833, + "y": 1 + }, + "o": { + "x": 0.167, + "y": 0 + }, + "t": 124, + "s": [ + { + "i": [ + [ + 0, + 0 + ], + [ + 0, + 0 + ], + [ + 0, + 0 + ], + [ + -28.673, + 0.875 + ] + ], + "o": [ + [ + 0, + 0 + ], + [ + 0, + 0 + ], + [ + 28.673, + -3.601 + ], + [ + 0, + 0 + ] + ], + "v": [ + [ + -34.128, + -218.21 + ], + [ + -120.318, + -218.521 + ], + [ + -120.318, + 218.521 + ], + [ + -34.128, + 211.337 + ] + ], + "c": true + } + ] + }, + { + "t": 156, + "s": [ + { + "i": [ + [ + 0, + 0 + ], + [ + 0, + 0 + ], + [ + 0, + 0 + ], + [ + -28.758, + -0.959 + ] + ], + "o": [ + [ + 0, + 0 + ], + [ + 0, + 0 + ], + [ + 28.673, + -3.601 + ], + [ + 0, + 0 + ] + ], + "v": [ + [ + -43.652, + -218.74 + ], + [ + -106.032, + -218.137 + ], + [ + -106.032, + 217.33 + ], + [ + -37.7, + 211.816 + ] + ], + "c": true + } + ] + } + ], + "ix": 2 + }, + "nm": "Path 1", + "mn": "ADBE Vector Shape - Group", + "hd": false + }, + { + "ty": "fl", + "c": { + "a": 0, + "k": [ + 0.694117647059, + 0.023529411765, + 0.058823529412, + 1 + ], + "ix": 4 + }, + "o": { + "a": 0, + "k": 100, + "ix": 5 + }, + "r": 1, + "bm": 0, + "nm": "Fill 1", + "mn": "ADBE Vector Graphic - Fill", + "hd": false + }, + { + "ty": "tr", + "p": { + "a": 0, + "k": [ + 0, + 0 + ], + "ix": 2 + }, + "a": { + "a": 0, + "k": [ + 0, + 0 + ], + "ix": 1 + }, + "s": { + "a": 0, + "k": [ + 100, + 100 + ], + "ix": 3 + }, + "r": { + "a": 0, + "k": 0, + "ix": 6 + }, + "o": { + "a": 0, + "k": 100, + "ix": 7 + }, + "sk": { + "a": 0, + "k": 0, + "ix": 4 + }, + "sa": { + "a": 0, + "k": 0, + "ix": 5 + }, + "nm": "Transform" + } + ], + "nm": "Big N 3", + "np": 2, + "cix": 2, + "bm": 0, + "ix": 1, + "mn": "ADBE Vector Group", + "hd": false + } + ], + "ip": 0, + "op": 3868, + "st": 0, + "bm": 0 + }, + { + "ddd": 0, + "ind": 8, + "ty": 4, + "nm": "Netflix reveal", + "parent": 9, + "td": 1, + "sr": 1, + "ks": { + "o": { + "a": 0, + "k": 100, + "ix": 11 + }, + "r": { + "a": 0, + "k": 0, + "ix": 10 + }, + "p": { + "a": 0, + "k": [ + 0, + 0, + 0 + ], + "ix": 2 + }, + "a": { + "a": 0, + "k": [ + 0, + 0, + 0 + ], + "ix": 1 + }, + "s": { + "a": 0, + "k": [ + 100, + 100, + 100 + ], + "ix": 6 + } + }, + "ao": 0, + "shapes": [ + { + "ty": "gr", + "it": [ + { + "ty": "gr", + "it": [ + { + "ind": 0, + "ty": "sh", + "ix": 1, + "ks": { + "a": 1, + "k": [ + { + "i": { + "x": 0.715, + "y": 1 + }, + "o": { + "x": 0.414, + "y": 0 + }, + "t": 206.207, + "s": [ + { + "i": [ + [ + 0, + 0 + ], + [ + 0, + 0 + ] + ], + "o": [ + [ + 0, + 0 + ], + [ + 0, + 0 + ] + ], + "v": [ + [ + 166, + -55 + ], + [ + 165, + -52 + ] + ], + "c": false + } + ] + }, + { + "t": 220, + "s": [ + { + "i": [ + [ + 0, + 0 + ], + [ + 0, + 0 + ] + ], + "o": [ + [ + 0, + 0 + ], + [ + 0, + 0 + ] + ], + "v": [ + [ + 164, + -51 + ], + [ + 125, + 45.5 + ] + ], + "c": false + } + ] + } + ], + "ix": 2 + }, + "nm": "Path 1", + "mn": "ADBE Vector Shape - Group", + "hd": false + }, + { + "ty": "st", + "c": { + "a": 0, + "k": [ + 1, + 1, + 1, + 1 + ], + "ix": 3 + }, + "o": { + "a": 0, + "k": 100, + "ix": 4 + }, + "w": { + "a": 0, + "k": 20, + "ix": 5 + }, + "lc": 1, + "lj": 1, + "ml": 4, + "bm": 0, + "nm": "Stroke 1", + "mn": "ADBE Vector Graphic - Stroke", + "hd": false + }, + { + "ty": "tr", + "p": { + "a": 0, + "k": [ + 0, + 0 + ], + "ix": 2 + }, + "a": { + "a": 0, + "k": [ + 0, + 0 + ], + "ix": 1 + }, + "s": { + "a": 0, + "k": [ + 100, + 100 + ], + "ix": 3 + }, + "r": { + "a": 0, + "k": 0, + "ix": 6 + }, + "o": { + "a": 0, + "k": 100, + "ix": 7 + }, + "sk": { + "a": 0, + "k": 0, + "ix": 4 + }, + "sa": { + "a": 0, + "k": 0, + "ix": 5 + }, + "nm": "Transform" + } + ], + "nm": "Shape 2", + "np": 3, + "cix": 2, + "bm": 0, + "ix": 1, + "mn": "ADBE Vector Group", + "hd": false + }, + { + "ty": "gr", + "it": [ + { + "ind": 0, + "ty": "sh", + "ix": 1, + "ks": { + "a": 1, + "k": [ + { + "i": { + "x": 0.715, + "y": 1 + }, + "o": { + "x": 0.414, + "y": 0 + }, + "t": 200.297, + "s": [ + { + "i": [ + [ + 0, + 0 + ], + [ + 0, + 0 + ] + ], + "o": [ + [ + 0, + 0 + ], + [ + 0, + 0 + ] + ], + "v": [ + [ + 125, + -54.5 + ], + [ + 125, + -52 + ] + ], + "c": false + } + ] + }, + { + "t": 214.08984375, + "s": [ + { + "i": [ + [ + 0, + 0 + ], + [ + 0, + 0 + ] + ], + "o": [ + [ + 0, + 0 + ], + [ + 0, + 0 + ] + ], + "v": [ + [ + 126.5, + -52 + ], + [ + 164, + 50 + ] + ], + "c": false + } + ] + } + ], + "ix": 2 + }, + "nm": "Path 1", + "mn": "ADBE Vector Shape - Group", + "hd": false + }, + { + "ty": "st", + "c": { + "a": 0, + "k": [ + 1, + 1, + 1, + 1 + ], + "ix": 3 + }, + "o": { + "a": 0, + "k": 100, + "ix": 4 + }, + "w": { + "a": 0, + "k": 20, + "ix": 5 + }, + "lc": 1, + "lj": 1, + "ml": 4, + "bm": 0, + "nm": "Stroke 1", + "mn": "ADBE Vector Graphic - Stroke", + "hd": false + }, + { + "ty": "tr", + "p": { + "a": 0, + "k": [ + 0, + 0 + ], + "ix": 2 + }, + "a": { + "a": 0, + "k": [ + 0, + 0 + ], + "ix": 1 + }, + "s": { + "a": 0, + "k": [ + 100, + 100 + ], + "ix": 3 + }, + "r": { + "a": 0, + "k": 0, + "ix": 6 + }, + "o": { + "a": 0, + "k": 100, + "ix": 7 + }, + "sk": { + "a": 0, + "k": 0, + "ix": 4 + }, + "sa": { + "a": 0, + "k": 0, + "ix": 5 + }, + "nm": "Transform" + } + ], + "nm": "Shape 1", + "np": 3, + "cix": 2, + "bm": 0, + "ix": 2, + "mn": "ADBE Vector Group", + "hd": false + }, + { + "ty": "tr", + "p": { + "a": 0, + "k": [ + 144.5, + -1 + ], + "ix": 2 + }, + "a": { + "a": 0, + "k": [ + 144.5, + -1 + ], + "ix": 1 + }, + "s": { + "a": 0, + "k": [ + 100, + 100 + ], + "ix": 3 + }, + "r": { + "a": 0, + "k": 0, + "ix": 6 + }, + "o": { + "a": 0, + "k": 100, + "ix": 7 + }, + "sk": { + "a": 0, + "k": 0, + "ix": 4 + }, + "sa": { + "a": 0, + "k": 0, + "ix": 5 + }, + "nm": "Transform" + } + ], + "nm": "X", + "np": 2, + "cix": 2, + "bm": 0, + "ix": 1, + "mn": "ADBE Vector Group", + "hd": false + }, + { + "ty": "gr", + "it": [ + { + "ind": 0, + "ty": "sh", + "ix": 1, + "ks": { + "a": 1, + "k": [ + { + "i": { + "x": 0.715, + "y": 1 + }, + "o": { + "x": 0.414, + "y": 0 + }, + "t": 186.271, + "s": [ + { + "i": [ + [ + 0, + 0 + ], + [ + 0, + 0 + ] + ], + "o": [ + [ + 0, + 0 + ], + [ + 0, + 0 + ] + ], + "v": [ + [ + 100, + -51 + ], + [ + 100, + -48 + ] + ], + "c": false + } + ] + }, + { + "t": 198.09375, + "s": [ + { + "i": [ + [ + 0, + 0 + ], + [ + 0, + 0 + ] + ], + "o": [ + [ + 0, + 0 + ], + [ + 0, + 0 + ] + ], + "v": [ + [ + 100, + -47 + ], + [ + 100, + 42 + ] + ], + "c": false + } + ] + } + ], + "ix": 2 + }, + "nm": "Path 1", + "mn": "ADBE Vector Shape - Group", + "hd": false + }, + { + "ty": "st", + "c": { + "a": 0, + "k": [ + 1, + 1, + 1, + 1 + ], + "ix": 3 + }, + "o": { + "a": 0, + "k": 100, + "ix": 4 + }, + "w": { + "a": 0, + "k": 20, + "ix": 5 + }, + "lc": 1, + "lj": 1, + "ml": 4, + "bm": 0, + "nm": "Stroke 1", + "mn": "ADBE Vector Graphic - Stroke", + "hd": false + }, + { + "ty": "tr", + "p": { + "a": 0, + "k": [ + 0, + 0 + ], + "ix": 2 + }, + "a": { + "a": 0, + "k": [ + 0, + 0 + ], + "ix": 1 + }, + "s": { + "a": 0, + "k": [ + 100, + 100 + ], + "ix": 3 + }, + "r": { + "a": 0, + "k": 0, + "ix": 6 + }, + "o": { + "a": 0, + "k": 100, + "ix": 7 + }, + "sk": { + "a": 0, + "k": 0, + "ix": 4 + }, + "sa": { + "a": 0, + "k": 0, + "ix": 5 + }, + "nm": "Transform" + } + ], + "nm": "I", + "np": 3, + "cix": 2, + "bm": 0, + "ix": 2, + "mn": "ADBE Vector Group", + "hd": false + }, + { + "ty": "gr", + "it": [ + { + "ty": "gr", + "it": [ + { + "ind": 0, + "ty": "sh", + "ix": 1, + "ks": { + "a": 1, + "k": [ + { + "i": { + "x": 0.715, + "y": 1 + }, + "o": { + "x": 0.414, + "y": 0 + }, + "t": 175.586, + "s": [ + { + "i": [ + [ + 0, + 0 + ], + [ + 0, + 0 + ] + ], + "o": [ + [ + 0, + 0 + ], + [ + 0, + 0 + ] + ], + "v": [ + [ + 37, + 29 + ], + [ + 40.5, + 29 + ] + ], + "c": false + } + ] + }, + { + "t": 189.37890625, + "s": [ + { + "i": [ + [ + 0, + 0 + ], + [ + 0, + 0 + ] + ], + "o": [ + [ + 0, + 0 + ], + [ + 0, + 0 + ] + ], + "v": [ + [ + 37, + 29 + ], + [ + 83.5, + 28.5 + ] + ], + "c": false + } + ] + } + ], + "ix": 2 + }, + "nm": "Path 1", + "mn": "ADBE Vector Shape - Group", + "hd": false + }, + { + "ty": "st", + "c": { + "a": 0, + "k": [ + 1, + 1, + 1, + 1 + ], + "ix": 3 + }, + "o": { + "a": 0, + "k": 100, + "ix": 4 + }, + "w": { + "a": 0, + "k": 20, + "ix": 5 + }, + "lc": 1, + "lj": 1, + "ml": 4, + "bm": 0, + "nm": "Stroke 1", + "mn": "ADBE Vector Graphic - Stroke", + "hd": false + }, + { + "ty": "tr", + "p": { + "a": 0, + "k": [ + 0, + 0 + ], + "ix": 2 + }, + "a": { + "a": 0, + "k": [ + 0, + 0 + ], + "ix": 1 + }, + "s": { + "a": 0, + "k": [ + 100, + 100 + ], + "ix": 3 + }, + "r": { + "a": 0, + "k": 0, + "ix": 6 + }, + "o": { + "a": 0, + "k": 100, + "ix": 7 + }, + "sk": { + "a": 0, + "k": 0, + "ix": 4 + }, + "sa": { + "a": 0, + "k": 0, + "ix": 5 + }, + "nm": "Transform" + } + ], + "nm": "L 2", + "np": 3, + "cix": 2, + "bm": 0, + "ix": 1, + "mn": "ADBE Vector Group", + "hd": false + }, + { + "ty": "gr", + "it": [ + { + "ind": 0, + "ty": "sh", + "ix": 1, + "ks": { + "a": 1, + "k": [ + { + "i": { + "x": 0.715, + "y": 1 + }, + "o": { + "x": 0.414, + "y": 0 + }, + "t": 171.645, + "s": [ + { + "i": [ + [ + 0, + 0 + ], + [ + 0, + 0 + ] + ], + "o": [ + [ + 0, + 0 + ], + [ + 0, + 0 + ] + ], + "v": [ + [ + 49, + -50.5 + ], + [ + 49, + -48.5 + ] + ], + "c": true + } + ] + }, + { + "t": 185.4375, + "s": [ + { + "i": [ + [ + 0, + 0 + ], + [ + 0, + 0 + ] + ], + "o": [ + [ + 0, + 0 + ], + [ + 0, + 0 + ] + ], + "v": [ + [ + 49, + -47.5 + ], + [ + 49.5, + 38 + ] + ], + "c": false + } + ] + } + ], + "ix": 2 + }, + "nm": "Path 1", + "mn": "ADBE Vector Shape - Group", + "hd": false + }, + { + "ty": "st", + "c": { + "a": 0, + "k": [ + 1, + 1, + 1, + 1 + ], + "ix": 3 + }, + "o": { + "a": 0, + "k": 100, + "ix": 4 + }, + "w": { + "a": 0, + "k": 20, + "ix": 5 + }, + "lc": 1, + "lj": 1, + "ml": 4, + "bm": 0, + "nm": "Stroke 1", + "mn": "ADBE Vector Graphic - Stroke", + "hd": false + }, + { + "ty": "tr", + "p": { + "a": 0, + "k": [ + 0, + 0 + ], + "ix": 2 + }, + "a": { + "a": 0, + "k": [ + 0, + 0 + ], + "ix": 1 + }, + "s": { + "a": 0, + "k": [ + 100, + 100 + ], + "ix": 3 + }, + "r": { + "a": 0, + "k": 0, + "ix": 6 + }, + "o": { + "a": 0, + "k": 100, + "ix": 7 + }, + "sk": { + "a": 0, + "k": 0, + "ix": 4 + }, + "sa": { + "a": 0, + "k": 0, + "ix": 5 + }, + "nm": "Transform" + } + ], + "nm": "L1", + "np": 3, + "cix": 2, + "bm": 0, + "ix": 2, + "mn": "ADBE Vector Group", + "hd": false + }, + { + "ty": "tr", + "p": { + "a": 0, + "k": [ + 60.25, + -4.75 + ], + "ix": 2 + }, + "a": { + "a": 0, + "k": [ + 60.25, + -4.75 + ], + "ix": 1 + }, + "s": { + "a": 0, + "k": [ + 100, + 100 + ], + "ix": 3 + }, + "r": { + "a": 0, + "k": 0, + "ix": 6 + }, + "o": { + "a": 0, + "k": 100, + "ix": 7 + }, + "sk": { + "a": 0, + "k": 0, + "ix": 4 + }, + "sa": { + "a": 0, + "k": 0, + "ix": 5 + }, + "nm": "Transform" + } + ], + "nm": "L", + "np": 2, + "cix": 2, + "bm": 0, + "ix": 3, + "mn": "ADBE Vector Group", + "hd": false + }, + { + "ty": "gr", + "it": [ + { + "ty": "gr", + "it": [ + { + "ind": 0, + "ty": "sh", + "ix": 1, + "ks": { + "a": 1, + "k": [ + { + "i": { + "x": 0.715, + "y": 1 + }, + "o": { + "x": 0.414, + "y": 0 + }, + "t": 159.992, + "s": [ + { + "i": [ + [ + 0, + 0 + ], + [ + 0, + 0 + ] + ], + "o": [ + [ + 0, + 0 + ], + [ + 0, + 0 + ] + ], + "v": [ + [ + -9.5, + -7.5 + ], + [ + -11.5, + -7.5 + ] + ], + "c": false + } + ] + }, + { + "t": 175.75390625, + "s": [ + { + "i": [ + [ + 0, + 0 + ], + [ + 0, + 0 + ] + ], + "o": [ + [ + 0, + 0 + ], + [ + 0, + 0 + ] + ], + "v": [ + [ + 28, + -7.5 + ], + [ + -9.5, + -7.5 + ] + ], + "c": false + } + ] + } + ], + "ix": 2 + }, + "nm": "Path 1", + "mn": "ADBE Vector Shape - Group", + "hd": false + }, + { + "ty": "st", + "c": { + "a": 0, + "k": [ + 1, + 1, + 1, + 1 + ], + "ix": 3 + }, + "o": { + "a": 0, + "k": 100, + "ix": 4 + }, + "w": { + "a": 0, + "k": 20, + "ix": 5 + }, + "lc": 1, + "lj": 1, + "ml": 4, + "bm": 0, + "nm": "Stroke 1", + "mn": "ADBE Vector Graphic - Stroke", + "hd": false + }, + { + "ty": "tr", + "p": { + "a": 0, + "k": [ + 0, + 0 + ], + "ix": 2 + }, + "a": { + "a": 0, + "k": [ + 0, + 0 + ], + "ix": 1 + }, + "s": { + "a": 0, + "k": [ + 100, + 100 + ], + "ix": 3 + }, + "r": { + "a": 0, + "k": 0, + "ix": 6 + }, + "o": { + "a": 0, + "k": 100, + "ix": 7 + }, + "sk": { + "a": 0, + "k": 0, + "ix": 4 + }, + "sa": { + "a": 0, + "k": 0, + "ix": 5 + }, + "nm": "Transform" + } + ], + "nm": "Shape 2", + "np": 3, + "cix": 2, + "bm": 0, + "ix": 1, + "mn": "ADBE Vector Group", + "hd": false + }, + { + "ty": "gr", + "it": [ + { + "ind": 0, + "ty": "sh", + "ix": 1, + "ks": { + "a": 1, + "k": [ + { + "i": { + "x": 0.715, + "y": 1 + }, + "o": { + "x": 0.414, + "y": 0 + }, + "t": 154.08, + "s": [ + { + "i": [ + [ + 0, + 0 + ], + [ + 0, + 0 + ] + ], + "o": [ + [ + 0, + 0 + ], + [ + 0, + 0 + ] + ], + "v": [ + [ + 0.5, + -50 + ], + [ + 0.5, + -48 + ] + ], + "c": false + } + ] + }, + { + "t": 169.84375, + "s": [ + { + "i": [ + [ + 0, + 0 + ], + [ + 0, + 0 + ] + ], + "o": [ + [ + 0, + 0 + ], + [ + 0, + 0 + ] + ], + "v": [ + [ + 0.5, + -48 + ], + [ + 0, + 37 + ] + ], + "c": false + } + ] + } + ], + "ix": 2 + }, + "nm": "Path 1", + "mn": "ADBE Vector Shape - Group", + "hd": false + }, + { + "ty": "st", + "c": { + "a": 0, + "k": [ + 1, + 1, + 1, + 1 + ], + "ix": 3 + }, + "o": { + "a": 0, + "k": 100, + "ix": 4 + }, + "w": { + "a": 0, + "k": 20, + "ix": 5 + }, + "lc": 1, + "lj": 1, + "ml": 4, + "bm": 0, + "nm": "Stroke 1", + "mn": "ADBE Vector Graphic - Stroke", + "hd": false + }, + { + "ty": "tr", + "p": { + "a": 0, + "k": [ + 0, + 0 + ], + "ix": 2 + }, + "a": { + "a": 0, + "k": [ + 0, + 0 + ], + "ix": 1 + }, + "s": { + "a": 0, + "k": [ + 100, + 100 + ], + "ix": 3 + }, + "r": { + "a": 0, + "k": 0, + "ix": 6 + }, + "o": { + "a": 0, + "k": 100, + "ix": 7 + }, + "sk": { + "a": 0, + "k": 0, + "ix": 4 + }, + "sa": { + "a": 0, + "k": 0, + "ix": 5 + }, + "nm": "Transform" + } + ], + "nm": "Shape 3", + "np": 3, + "cix": 2, + "bm": 0, + "ix": 2, + "mn": "ADBE Vector Group", + "hd": false + }, + { + "ty": "gr", + "it": [ + { + "ind": 0, + "ty": "sh", + "ix": 1, + "ks": { + "a": 1, + "k": [ + { + "i": { + "x": 0.715, + "y": 1 + }, + "o": { + "x": 0.414, + "y": 0 + }, + "t": 145.266, + "s": [ + { + "i": [ + [ + 0, + 0 + ], + [ + 0, + 0 + ] + ], + "o": [ + [ + 0, + 0 + ], + [ + 0, + 0 + ] + ], + "v": [ + [ + -10.5, + -39.5 + ], + [ + -12, + -39.5 + ] + ], + "c": false + } + ] + }, + { + "t": 160.693359375, + "s": [ + { + "i": [ + [ + 0, + 0 + ], + [ + 0, + 0 + ] + ], + "o": [ + [ + 0, + 0 + ], + [ + 0, + 0 + ] + ], + "v": [ + [ + 36, + -39.5 + ], + [ + -9, + -39.5 + ] + ], + "c": false + } + ] + } + ], + "ix": 2 + }, + "nm": "Path 1", + "mn": "ADBE Vector Shape - Group", + "hd": false + }, + { + "ty": "st", + "c": { + "a": 0, + "k": [ + 1, + 1, + 1, + 1 + ], + "ix": 3 + }, + "o": { + "a": 0, + "k": 100, + "ix": 4 + }, + "w": { + "a": 0, + "k": 20, + "ix": 5 + }, + "lc": 1, + "lj": 1, + "ml": 4, + "bm": 0, + "nm": "Stroke 1", + "mn": "ADBE Vector Graphic - Stroke", + "hd": false + }, + { + "ty": "tr", + "p": { + "a": 0, + "k": [ + 0, + 0 + ], + "ix": 2 + }, + "a": { + "a": 0, + "k": [ + 0, + 0 + ], + "ix": 1 + }, + "s": { + "a": 0, + "k": [ + 100, + 100 + ], + "ix": 3 + }, + "r": { + "a": 0, + "k": 0, + "ix": 6 + }, + "o": { + "a": 0, + "k": 100, + "ix": 7 + }, + "sk": { + "a": 0, + "k": 0, + "ix": 4 + }, + "sa": { + "a": 0, + "k": 0, + "ix": 5 + }, + "nm": "Transform" + } + ], + "nm": "Shape 1", + "np": 3, + "cix": 2, + "bm": 0, + "ix": 3, + "mn": "ADBE Vector Group", + "hd": false + }, + { + "ty": "tr", + "p": { + "a": 0, + "k": [ + 13.25, + -5.5 + ], + "ix": 2 + }, + "a": { + "a": 0, + "k": [ + 13.25, + -5.5 + ], + "ix": 1 + }, + "s": { + "a": 0, + "k": [ + 100, + 100 + ], + "ix": 3 + }, + "r": { + "a": 0, + "k": 0, + "ix": 6 + }, + "o": { + "a": 0, + "k": 100, + "ix": 7 + }, + "sk": { + "a": 0, + "k": 0, + "ix": 4 + }, + "sa": { + "a": 0, + "k": 0, + "ix": 5 + }, + "nm": "Transform" + } + ], + "nm": "F", + "np": 3, + "cix": 2, + "bm": 0, + "ix": 4, + "mn": "ADBE Vector Group", + "hd": false + }, + { + "ty": "gr", + "it": [ + { + "ty": "gr", + "it": [ + { + "ind": 0, + "ty": "sh", + "ix": 1, + "ks": { + "a": 1, + "k": [ + { + "i": { + "x": 0.715, + "y": 1 + }, + "o": { + "x": 0.414, + "y": 0 + }, + "t": 136.115, + "s": [ + { + "i": [ + [ + 0, + 0 + ], + [ + 0, + 0 + ] + ], + "o": [ + [ + 0, + 0 + ], + [ + 0, + 0 + ] + ], + "v": [ + [ + -40.5, + -54 + ], + [ + -40.5, + -49.5 + ] + ], + "c": false + } + ] + }, + { + "t": 151.876953125, + "s": [ + { + "i": [ + [ + 0, + 0 + ], + [ + 0, + 0 + ] + ], + "o": [ + [ + 0, + 0 + ], + [ + 0, + 0 + ] + ], + "v": [ + [ + -38.5, + -45.5 + ], + [ + -39, + 38.5 + ] + ], + "c": false + } + ] + } + ], + "ix": 2 + }, + "nm": "Path 1", + "mn": "ADBE Vector Shape - Group", + "hd": false + }, + { + "ty": "st", + "c": { + "a": 0, + "k": [ + 1, + 1, + 1, + 1 + ], + "ix": 3 + }, + "o": { + "a": 0, + "k": 100, + "ix": 4 + }, + "w": { + "a": 0, + "k": 20, + "ix": 5 + }, + "lc": 1, + "lj": 1, + "ml": 4, + "bm": 0, + "nm": "Stroke 1", + "mn": "ADBE Vector Graphic - Stroke", + "hd": false + }, + { + "ty": "tr", + "p": { + "a": 0, + "k": [ + 0, + 0 + ], + "ix": 2 + }, + "a": { + "a": 0, + "k": [ + 0, + 0 + ], + "ix": 1 + }, + "s": { + "a": 0, + "k": [ + 100, + 100 + ], + "ix": 3 + }, + "r": { + "a": 0, + "k": 0, + "ix": 6 + }, + "o": { + "a": 0, + "k": 100, + "ix": 7 + }, + "sk": { + "a": 0, + "k": 0, + "ix": 4 + }, + "sa": { + "a": 0, + "k": 0, + "ix": 5 + }, + "nm": "Transform" + } + ], + "nm": "Shape 2", + "np": 3, + "cix": 2, + "bm": 0, + "ix": 1, + "mn": "ADBE Vector Group", + "hd": false + }, + { + "ty": "gr", + "it": [ + { + "ind": 0, + "ty": "sh", + "ix": 1, + "ks": { + "a": 1, + "k": [ + { + "i": { + "x": 0.715, + "y": 1 + }, + "o": { + "x": 0.414, + "y": 0 + }, + "t": 132.174, + "s": [ + { + "i": [ + [ + 0, + 0 + ], + [ + 0, + 0 + ] + ], + "o": [ + [ + 0, + 0 + ], + [ + 0, + 0 + ] + ], + "v": [ + [ + -62.5, + -38.5 + ], + [ + -66, + -38.5 + ] + ], + "c": false + } + ] + }, + { + "t": 147.9375, + "s": [ + { + "i": [ + [ + 0, + 0 + ], + [ + 0, + 0 + ] + ], + "o": [ + [ + 0, + 0 + ], + [ + 0, + 0 + ] + ], + "v": [ + [ + -14.5, + -40.5 + ], + [ + -63.5, + -38.5 + ] + ], + "c": false + } + ] + } + ], + "ix": 2 + }, + "nm": "Path 1", + "mn": "ADBE Vector Shape - Group", + "hd": false + }, + { + "ty": "st", + "c": { + "a": 0, + "k": [ + 1, + 1, + 1, + 1 + ], + "ix": 3 + }, + "o": { + "a": 0, + "k": 100, + "ix": 4 + }, + "w": { + "a": 0, + "k": 20, + "ix": 5 + }, + "lc": 1, + "lj": 1, + "ml": 4, + "bm": 0, + "nm": "Stroke 1", + "mn": "ADBE Vector Graphic - Stroke", + "hd": false + }, + { + "ty": "tr", + "p": { + "a": 0, + "k": [ + 0, + 0 + ], + "ix": 2 + }, + "a": { + "a": 0, + "k": [ + 0, + 0 + ], + "ix": 1 + }, + "s": { + "a": 0, + "k": [ + 100, + 100 + ], + "ix": 3 + }, + "r": { + "a": 0, + "k": 0, + "ix": 6 + }, + "o": { + "a": 0, + "k": 100, + "ix": 7 + }, + "sk": { + "a": 0, + "k": 0, + "ix": 4 + }, + "sa": { + "a": 0, + "k": 0, + "ix": 5 + }, + "nm": "Transform" + } + ], + "nm": "Shape 1", + "np": 3, + "cix": 2, + "bm": 0, + "ix": 2, + "mn": "ADBE Vector Group", + "hd": false + }, + { + "ty": "tr", + "p": { + "a": 0, + "k": [ + -39, + -3.5 + ], + "ix": 2 + }, + "a": { + "a": 0, + "k": [ + -39, + -3.5 + ], + "ix": 1 + }, + "s": { + "a": 0, + "k": [ + 100, + 100 + ], + "ix": 3 + }, + "r": { + "a": 0, + "k": 0, + "ix": 6 + }, + "o": { + "a": 0, + "k": 100, + "ix": 7 + }, + "sk": { + "a": 0, + "k": 0, + "ix": 4 + }, + "sa": { + "a": 0, + "k": 0, + "ix": 5 + }, + "nm": "Transform" + } + ], + "nm": "T", + "np": 2, + "cix": 2, + "bm": 0, + "ix": 5, + "mn": "ADBE Vector Group", + "hd": false + }, + { + "ty": "gr", + "it": [ + { + "ty": "gr", + "it": [ + { + "ind": 0, + "ty": "sh", + "ix": 1, + "ks": { + "a": 1, + "k": [ + { + "i": { + "x": 0.715, + "y": 1 + }, + "o": { + "x": 0.414, + "y": 0 + }, + "t": 127.73, + "s": [ + { + "i": [ + [ + 0, + 0 + ], + [ + 0, + 0 + ] + ], + "o": [ + [ + 0, + 0 + ], + [ + 0, + 0 + ] + ], + "v": [ + [ + -114, + 32.5 + ], + [ + -119, + 32.5 + ] + ], + "c": false + } + ] + }, + { + "t": 147.435546875, + "s": [ + { + "i": [ + [ + 0, + 0 + ], + [ + 0, + 0 + ] + ], + "o": [ + [ + 0, + 0 + ], + [ + 0, + 0 + ] + ], + "v": [ + [ + -67.5, + 29 + ], + [ + -112, + 32.5 + ] + ], + "c": false + } + ] + } + ], + "ix": 2 + }, + "nm": "Path 1", + "mn": "ADBE Vector Shape - Group", + "hd": false + }, + { + "ty": "st", + "c": { + "a": 0, + "k": [ + 1, + 1, + 1, + 1 + ], + "ix": 3 + }, + "o": { + "a": 0, + "k": 100, + "ix": 4 + }, + "w": { + "a": 0, + "k": 20, + "ix": 5 + }, + "lc": 1, + "lj": 1, + "ml": 4, + "bm": 0, + "nm": "Stroke 1", + "mn": "ADBE Vector Graphic - Stroke", + "hd": false + }, + { + "ty": "tr", + "p": { + "a": 0, + "k": [ + 0, + 0 + ], + "ix": 2 + }, + "a": { + "a": 0, + "k": [ + 0, + 0 + ], + "ix": 1 + }, + "s": { + "a": 0, + "k": [ + 100, + 100 + ], + "ix": 3 + }, + "r": { + "a": 0, + "k": 0, + "ix": 6 + }, + "o": { + "a": 0, + "k": 100, + "ix": 7 + }, + "sk": { + "a": 0, + "k": 0, + "ix": 4 + }, + "sa": { + "a": 0, + "k": 0, + "ix": 5 + }, + "nm": "Transform" + } + ], + "nm": "Shape 3", + "np": 3, + "cix": 2, + "bm": 0, + "ix": 1, + "mn": "ADBE Vector Group", + "hd": false + }, + { + "ty": "gr", + "it": [ + { + "ind": 0, + "ty": "sh", + "ix": 1, + "ks": { + "a": 1, + "k": [ + { + "i": { + "x": 0.715, + "y": 1 + }, + "o": { + "x": 0.414, + "y": 0 + }, + "t": 121.82, + "s": [ + { + "i": [ + [ + 0, + 0 + ], + [ + 0, + 0 + ] + ], + "o": [ + [ + 0, + 0 + ], + [ + 0, + 0 + ] + ], + "v": [ + [ + -115, + -6.5 + ], + [ + -119.5, + -6.5 + ] + ], + "c": true + } + ] + }, + { + "t": 141.033203125, + "s": [ + { + "i": [ + [ + 0, + 0 + ], + [ + 0, + 0 + ] + ], + "o": [ + [ + 0, + 0 + ], + [ + 0, + 0 + ] + ], + "v": [ + [ + -74.5, + -6 + ], + [ + -113, + -6 + ] + ], + "c": true + } + ] + } + ], + "ix": 2 + }, + "nm": "Path 1", + "mn": "ADBE Vector Shape - Group", + "hd": false + }, + { + "ty": "st", + "c": { + "a": 0, + "k": [ + 1, + 1, + 1, + 1 + ], + "ix": 3 + }, + "o": { + "a": 0, + "k": 100, + "ix": 4 + }, + "w": { + "a": 0, + "k": 20, + "ix": 5 + }, + "lc": 1, + "lj": 1, + "ml": 4, + "bm": 0, + "nm": "Stroke 1", + "mn": "ADBE Vector Graphic - Stroke", + "hd": false + }, + { + "ty": "tr", + "p": { + "a": 0, + "k": [ + 0, + 0 + ], + "ix": 2 + }, + "a": { + "a": 0, + "k": [ + 0, + 0 + ], + "ix": 1 + }, + "s": { + "a": 0, + "k": [ + 100, + 100 + ], + "ix": 3 + }, + "r": { + "a": 0, + "k": 0, + "ix": 6 + }, + "o": { + "a": 0, + "k": 100, + "ix": 7 + }, + "sk": { + "a": 0, + "k": 0, + "ix": 4 + }, + "sa": { + "a": 0, + "k": 0, + "ix": 5 + }, + "nm": "Transform" + } + ], + "nm": "Shape 2", + "np": 3, + "cix": 2, + "bm": 0, + "ix": 2, + "mn": "ADBE Vector Group", + "hd": false + }, + { + "ty": "gr", + "it": [ + { + "ind": 0, + "ty": "sh", + "ix": 1, + "ks": { + "a": 1, + "k": [ + { + "i": { + "x": 0.715, + "y": 1 + }, + "o": { + "x": 0.414, + "y": 0 + }, + "t": 117.881, + "s": [ + { + "i": [ + [ + 0, + 0 + ], + [ + 0, + 0 + ] + ], + "o": [ + [ + 0, + 0 + ], + [ + 0, + 0 + ] + ], + "v": [ + [ + -103.5, + -54.5 + ], + [ + -103.5, + -49 + ] + ], + "c": false + } + ] + }, + { + "t": 137.58203125, + "s": [ + { + "i": [ + [ + 0, + 0 + ], + [ + 0, + 0 + ] + ], + "o": [ + [ + 0, + 0 + ], + [ + 0, + 0 + ] + ], + "v": [ + [ + -103.5, + -48.5 + ], + [ + -102.5, + 42 + ] + ], + "c": false + } + ] + } + ], + "ix": 2 + }, + "nm": "Path 1", + "mn": "ADBE Vector Shape - Group", + "hd": false + }, + { + "ty": "st", + "c": { + "a": 0, + "k": [ + 1, + 1, + 1, + 1 + ], + "ix": 3 + }, + "o": { + "a": 0, + "k": 100, + "ix": 4 + }, + "w": { + "a": 0, + "k": 20, + "ix": 5 + }, + "lc": 1, + "lj": 1, + "ml": 4, + "bm": 0, + "nm": "Stroke 1", + "mn": "ADBE Vector Graphic - Stroke", + "hd": false + }, + { + "ty": "tr", + "p": { + "a": 0, + "k": [ + 0, + 0 + ], + "ix": 2 + }, + "a": { + "a": 0, + "k": [ + 0, + 0 + ], + "ix": 1 + }, + "s": { + "a": 0, + "k": [ + 100, + 100 + ], + "ix": 3 + }, + "r": { + "a": 0, + "k": 0, + "ix": 6 + }, + "o": { + "a": 0, + "k": 100, + "ix": 7 + }, + "sk": { + "a": 0, + "k": 0, + "ix": 4 + }, + "sa": { + "a": 0, + "k": 0, + "ix": 5 + }, + "nm": "Transform" + } + ], + "nm": "Shape 4", + "np": 3, + "cix": 2, + "bm": 0, + "ix": 3, + "mn": "ADBE Vector Group", + "hd": false + }, + { + "ty": "gr", + "it": [ + { + "ind": 0, + "ty": "sh", + "ix": 1, + "ks": { + "a": 1, + "k": [ + { + "i": { + "x": 0.715, + "y": 1 + }, + "o": { + "x": 0.414, + "y": 0 + }, + "t": 110, + "s": [ + { + "i": [ + [ + 0, + 0 + ], + [ + 0, + 0 + ] + ], + "o": [ + [ + 0, + 0 + ], + [ + 0, + 0 + ] + ], + "v": [ + [ + -117, + -38 + ], + [ + -120.5, + -38 + ] + ], + "c": false + } + ] + }, + { + "t": 129.2109375, + "s": [ + { + "i": [ + [ + 0, + 0 + ], + [ + 0, + 0 + ] + ], + "o": [ + [ + 0, + 0 + ], + [ + 0, + 0 + ] + ], + "v": [ + [ + -68, + -38 + ], + [ + -114.5, + -38 + ] + ], + "c": false + } + ] + } + ], + "ix": 2 + }, + "nm": "Path 1", + "mn": "ADBE Vector Shape - Group", + "hd": false + }, + { + "ty": "st", + "c": { + "a": 0, + "k": [ + 1, + 1, + 1, + 1 + ], + "ix": 3 + }, + "o": { + "a": 0, + "k": 100, + "ix": 4 + }, + "w": { + "a": 0, + "k": 20, + "ix": 5 + }, + "lc": 1, + "lj": 1, + "ml": 4, + "bm": 0, + "nm": "Stroke 1", + "mn": "ADBE Vector Graphic - Stroke", + "hd": false + }, + { + "ty": "tr", + "p": { + "a": 0, + "k": [ + 0, + 0 + ], + "ix": 2 + }, + "a": { + "a": 0, + "k": [ + 0, + 0 + ], + "ix": 1 + }, + "s": { + "a": 0, + "k": [ + 100, + 100 + ], + "ix": 3 + }, + "r": { + "a": 0, + "k": 0, + "ix": 6 + }, + "o": { + "a": 0, + "k": 100, + "ix": 7 + }, + "sk": { + "a": 0, + "k": 0, + "ix": 4 + }, + "sa": { + "a": 0, + "k": 0, + "ix": 5 + }, + "nm": "Transform" + } + ], + "nm": "Shape 1", + "np": 3, + "cix": 2, + "bm": 0, + "ix": 4, + "mn": "ADBE Vector Group", + "hd": false + }, + { + "ty": "tr", + "p": { + "a": 0, + "k": [ + -90, + -3.25 + ], + "ix": 2 + }, + "a": { + "a": 0, + "k": [ + -90, + -3.25 + ], + "ix": 1 + }, + "s": { + "a": 0, + "k": [ + 100, + 100 + ], + "ix": 3 + }, + "r": { + "a": 0, + "k": 0, + "ix": 6 + }, + "o": { + "a": 0, + "k": 100, + "ix": 7 + }, + "sk": { + "a": 0, + "k": 0, + "ix": 4 + }, + "sa": { + "a": 0, + "k": 0, + "ix": 5 + }, + "nm": "Transform" + } + ], + "nm": "E", + "np": 4, + "cix": 2, + "bm": 0, + "ix": 6, + "mn": "ADBE Vector Group", + "hd": false + } + ], + "ip": 101, + "op": 3868, + "st": 0, + "bm": 0 + }, + { + "ddd": 0, + "ind": 9, + "ty": 4, + "nm": "Etflix", + "tt": 1, + "sr": 1, + "ks": { + "o": { + "a": 0, + "k": 100, + "ix": 11 + }, + "r": { + "a": 0, + "k": 0, + "ix": 10 + }, + "p": { + "s": true, + "x": { + "a": 1, + "k": [ + { + "i": { + "x": [ + 0.455 + ], + "y": [ + 1 + ] + }, + "o": { + "x": [ + 0.684 + ], + "y": [ + 0 + ] + }, + "t": 104, + "s": [ + 402.75 + ] + }, + { + "t": 154.74609375, + "s": [ + 256 + ] + } + ], + "ix": 3 + }, + "y": { + "a": 0, + "k": 256, + "ix": 4 + } + }, + "a": { + "a": 0, + "k": [ + 0, + 0, + 0 + ], + "ix": 1 + }, + "s": { + "a": 0, + "k": [ + 100, + 100, + 100 + ], + "ix": 6 + } + }, + "ao": 0, + "shapes": [ + { + "ty": "gr", + "it": [ + { + "ind": 1, + "ty": "sh", + "ix": 2, + "ks": { + "a": 0, + "k": { + "i": [ + [ + 0, + 0 + ], + [ + -5.131, + 0 + ], + [ + 0, + 0 + ], + [ + 5.401, + -0.27 + ], + [ + 0, + 0 + ], + [ + -8.551, + 0.27 + ], + [ + 0, + 0 + ], + [ + 0, + 0 + ], + [ + 0, + 0 + ], + [ + 0, + 0 + ], + [ + 0, + 0 + ], + [ + 0, + 0 + ] + ], + "o": [ + [ + 5.582, + 0 + ], + [ + 0, + 0 + ], + [ + -6.392, + 0 + ], + [ + 0, + 0 + ], + [ + 8.462, + -0.54 + ], + [ + 0, + 0 + ], + [ + 0, + 0 + ], + [ + 0, + 0 + ], + [ + 0, + 0 + ], + [ + 0, + 0 + ], + [ + 0, + 0 + ], + [ + 0, + 0 + ] + ], + "v": [ + [ + -95.285, + -12.333 + ], + [ + -76.021, + -12.603 + ], + [ + -76.021, + 1.8 + ], + [ + -95.285, + 2.07 + ], + [ + -95.285, + 23.495 + ], + [ + -69.81, + 21.964 + ], + [ + -69.81, + 35.826 + ], + [ + -109.689, + 38.978 + ], + [ + -109.689, + -46.09 + ], + [ + -69.81, + -46.09 + ], + [ + -69.81, + -31.687 + ], + [ + -95.285, + -31.687 + ] + ], + "c": true + }, + "ix": 2 + }, + "nm": "Path 2", + "mn": "ADBE Vector Shape - Group", + "hd": false + }, + { + "ind": 2, + "ty": "sh", + "ix": 3, + "ks": { + "a": 0, + "k": { + "i": [ + [ + 0, + 0 + ], + [ + 0, + 0 + ], + [ + 0, + 0 + ], + [ + 4.68, + -0.18 + ], + [ + 0, + 0 + ], + [ + 0, + 0 + ], + [ + 0, + 0 + ], + [ + 0, + 0 + ] + ], + "o": [ + [ + 0, + 0 + ], + [ + 0, + 0 + ], + [ + -4.861, + 0 + ], + [ + 0, + 0 + ], + [ + 0, + 0 + ], + [ + 0, + 0 + ], + [ + 0, + 0 + ], + [ + 0, + 0 + ] + ], + "v": [ + [ + -16.249, + -31.686 + ], + [ + -31.192, + -31.686 + ], + [ + -31.192, + 34.568 + ], + [ + -45.594, + 34.747 + ], + [ + -45.594, + -31.686 + ], + [ + -60.537, + -31.686 + ], + [ + -60.537, + -46.09 + ], + [ + -16.248, + -46.09 + ] + ], + "c": true + }, + "ix": 2 + }, + "nm": "Path 3", + "mn": "ADBE Vector Shape - Group", + "hd": false + }, + { + "ind": 3, + "ty": "sh", + "ix": 4, + "ks": { + "a": 0, + "k": { + "i": [ + [ + 0, + 0 + ], + [ + 0, + 0 + ], + [ + 0, + 0 + ], + [ + 0, + 0 + ], + [ + 0, + 0 + ], + [ + 0, + 0 + ], + [ + 0, + 0 + ], + [ + 0, + 0 + ], + [ + 0, + 0 + ], + [ + 0, + 0 + ] + ], + "o": [ + [ + 0, + 0 + ], + [ + 0, + 0 + ], + [ + 0, + 0 + ], + [ + 0, + 0 + ], + [ + 0, + 0 + ], + [ + 0, + 0 + ], + [ + 0, + 0 + ], + [ + 0, + 0 + ], + [ + 0, + 0 + ], + [ + 0, + 0 + ] + ], + "v": [ + [ + 7.156, + -13.323 + ], + [ + 26.871, + -13.323 + ], + [ + 26.871, + 1.08 + ], + [ + 7.156, + 1.08 + ], + [ + 7.156, + 33.757 + ], + [ + -6.978, + 33.757 + ], + [ + -6.978, + -46.09 + ], + [ + 33.262, + -46.09 + ], + [ + 33.262, + -31.687 + ], + [ + 7.156, + -31.687 + ] + ], + "c": true + }, + "ix": 2 + }, + "nm": "Path 4", + "mn": "ADBE Vector Shape - Group", + "hd": false + }, + { + "ind": 4, + "ty": "sh", + "ix": 5, + "ks": { + "a": 0, + "k": { + "i": [ + [ + 0, + 0 + ], + [ + -8.012, + -0.449 + ], + [ + 0, + 0 + ], + [ + 13.143, + 0.271 + ], + [ + 0, + 0 + ], + [ + 0, + 0 + ] + ], + "o": [ + [ + 8.192, + 0.18 + ], + [ + 0, + 0 + ], + [ + -12.873, + -0.811 + ], + [ + 0, + 0 + ], + [ + 0, + 0 + ], + [ + 0, + 0 + ] + ], + "v": [ + [ + 56.667, + 21.154 + ], + [ + 81.152, + 22.415 + ], + [ + 81.152, + 36.638 + ], + [ + 42.264, + 34.747 + ], + [ + 42.264, + -46.09 + ], + [ + 56.667, + -46.09 + ] + ], + "c": true + }, + "ix": 2 + }, + "nm": "Path 5", + "mn": "ADBE Vector Shape - Group", + "hd": false + }, + { + "ind": 5, + "ty": "sh", + "ix": 6, + "ks": { + "a": 0, + "k": { + "i": [ + [ + 0, + 0 + ], + [ + -4.681, + -0.539 + ], + [ + 0, + 0 + ], + [ + 0, + 0 + ] + ], + "o": [ + [ + 4.591, + 0.27 + ], + [ + 0, + 0 + ], + [ + 0, + 0 + ], + [ + 0, + 0 + ] + ], + "v": [ + [ + 93.304, + 37.628 + ], + [ + 107.437, + 38.708 + ], + [ + 107.437, + -46.09 + ], + [ + 93.304, + -46.09 + ] + ], + "c": true + }, + "ix": 2 + }, + "nm": "Path 6", + "mn": "ADBE Vector Shape - Group", + "hd": false + }, + { + "ind": 6, + "ty": "sh", + "ix": 7, + "ks": { + "a": 0, + "k": { + "i": [ + [ + 0, + 0 + ], + [ + 0, + 0 + ], + [ + 0, + 0 + ], + [ + 5.401, + 0.9 + ], + [ + 0, + 0 + ], + [ + 0, + 0 + ], + [ + 5.22, + 0.72 + ], + [ + 0, + 0 + ], + [ + 0, + 0 + ], + [ + 0, + 0 + ], + [ + 0, + 0 + ], + [ + 0, + 0 + ] + ], + "o": [ + [ + 0, + 0 + ], + [ + 0, + 0 + ], + [ + -5.403, + -0.72 + ], + [ + 0, + 0 + ], + [ + 0, + 0 + ], + [ + -5.222, + -0.901 + ], + [ + 0, + 0 + ], + [ + 0, + 0 + ], + [ + 0, + 0 + ], + [ + 0, + 0 + ], + [ + 0, + 0 + ], + [ + 0, + 0 + ] + ], + "v": [ + [ + 170.541, + -46.09 + ], + [ + 152.267, + -2.25 + ], + [ + 170.541, + 46.09 + ], + [ + 154.337, + 43.479 + ], + [ + 143.985, + 16.834 + ], + [ + 133.454, + 41.319 + ], + [ + 117.971, + 39.428 + ], + [ + 136.515, + -2.791 + ], + [ + 119.771, + -46.09 + ], + [ + 135.253, + -46.09 + ], + [ + 144.705, + -21.875 + ], + [ + 154.788, + -46.09 + ] + ], + "c": true + }, + "ix": 2 + }, + "nm": "Path 7", + "mn": "ADBE Vector Shape - Group", + "hd": false + }, + { + "ty": "fl", + "c": { + "a": 0, + "k": [ + 0.898039215686, + 0.035294117647, + 0.078431372549, + 1 + ], + "ix": 4 + }, + "o": { + "a": 0, + "k": 100, + "ix": 5 + }, + "r": 1, + "bm": 0, + "nm": "Fill 1", + "mn": "ADBE Vector Graphic - Fill", + "hd": false + }, + { + "ty": "tr", + "p": { + "a": 0, + "k": [ + 0, + 0 + ], + "ix": 2 + }, + "a": { + "a": 0, + "k": [ + 0, + 0 + ], + "ix": 1 + }, + "s": { + "a": 0, + "k": [ + 100, + 100 + ], + "ix": 3 + }, + "r": { + "a": 0, + "k": 0, + "ix": 6 + }, + "o": { + "a": 0, + "k": 100, + "ix": 7 + }, + "sk": { + "a": 0, + "k": 0, + "ix": 4 + }, + "sa": { + "a": 0, + "k": 0, + "ix": 5 + }, + "nm": "Transform" + } + ], + "nm": "Netflix Wordmark", + "np": 8, + "cix": 2, + "bm": 0, + "ix": 1, + "mn": "ADBE Vector Group", + "hd": false + } + ], + "ip": 102, + "op": 3868, + "st": 0, + "bm": 0 + }, + { + "ddd": 0, + "ind": 10, + "ty": 4, + "nm": "Netflix N", + "sr": 1, + "ks": { + "o": { + "a": 0, + "k": 100, + "ix": 11 + }, + "r": { + "a": 0, + "k": 0, + "ix": 10 + }, + "p": { + "s": true, + "x": { + "a": 1, + "k": [ + { + "i": { + "x": [ + 0.455 + ], + "y": [ + 1 + ] + }, + "o": { + "x": [ + 0.684 + ], + "y": [ + 0 + ] + }, + "t": 104, + "s": [ + 402.75 + ] + }, + { + "t": 154.74609375, + "s": [ + 256 + ] + } + ], + "ix": 3 + }, + "y": { + "a": 0, + "k": 256, + "ix": 4 + } + }, + "a": { + "a": 0, + "k": [ + 0, + 0, + 0 + ], + "ix": 1 + }, + "s": { + "a": 0, + "k": [ + 100, + 100, + 100 + ], + "ix": 6 + } + }, + "ao": 0, + "shapes": [ + { + "ty": "gr", + "it": [ + { + "ind": 0, + "ty": "sh", + "ix": 1, + "ks": { + "a": 0, + "k": { + "i": [ + [ + 0, + 0 + ], + [ + 5.401, + -0.719 + ], + [ + 0, + 0 + ], + [ + 0, + 0 + ], + [ + 4.861, + -0.72 + ], + [ + 0, + 0 + ], + [ + 0, + 0 + ], + [ + 0, + 0 + ], + [ + 0, + 0 + ], + [ + 0, + 0 + ] + ], + "o": [ + [ + -5.131, + 0.901 + ], + [ + 0, + 0 + ], + [ + 0, + 0 + ], + [ + -5.131, + 0.54 + ], + [ + 0, + 0 + ], + [ + 0, + 0 + ], + [ + 0, + 0 + ], + [ + 0, + 0 + ], + [ + 0, + 0 + ], + [ + 0, + 0 + ] + ], + "v": [ + [ + -123.641, + 40.148 + ], + [ + -139.394, + 42.039 + ], + [ + -155.868, + -6.211 + ], + [ + -155.868, + 44.11 + ], + [ + -170.541, + 46.09 + ], + [ + -170.541, + -46.09 + ], + [ + -156.858, + -46.09 + ], + [ + -138.134, + 6.212 + ], + [ + -138.134, + -46.09 + ], + [ + -123.641, + -46.09 + ] + ], + "c": true + }, + "ix": 2 + }, + "nm": "N", + "mn": "ADBE Vector Shape - Group", + "hd": false + }, + { + "ty": "fl", + "c": { + "a": 0, + "k": [ + 0.898039215686, + 0.035294117647, + 0.078431372549, + 1 + ], + "ix": 4 + }, + "o": { + "a": 0, + "k": 100, + "ix": 5 + }, + "r": 1, + "bm": 0, + "nm": "Fill 1", + "mn": "ADBE Vector Graphic - Fill", + "hd": false + }, + { + "ty": "tr", + "p": { + "a": 0, + "k": [ + 0, + 0 + ], + "ix": 2 + }, + "a": { + "a": 0, + "k": [ + 0, + 0 + ], + "ix": 1 + }, + "s": { + "a": 0, + "k": [ + 100, + 100 + ], + "ix": 3 + }, + "r": { + "a": 0, + "k": 0, + "ix": 6 + }, + "o": { + "a": 0, + "k": 100, + "ix": 7 + }, + "sk": { + "a": 0, + "k": 0, + "ix": 4 + }, + "sa": { + "a": 0, + "k": 0, + "ix": 5 + }, + "nm": "Transform" + } + ], + "nm": "Netflix Wordmark", + "np": 2, + "cix": 2, + "bm": 0, + "ix": 1, + "mn": "ADBE Vector Group", + "hd": false + } + ], + "ip": 102, + "op": 3868, + "st": 0, + "bm": 0 + } + ], + "markers": [] +} \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/com.codandotv.streamplayerapp/presentation/components/LottieComponent.kt b/composeApp/src/commonMain/kotlin/com.codandotv.streamplayerapp/presentation/components/LottieComponent.kt new file mode 100644 index 00000000..75db37a0 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/com.codandotv.streamplayerapp/presentation/components/LottieComponent.kt @@ -0,0 +1,7 @@ +package com.codandotv.streamplayerapp.presentation.components + +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier + +@Composable +expect fun LottieComponent(modifier : Modifier = Modifier, onAnimationFinished: () -> Unit) \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/com.codandotv.streamplayerapp/presentation/screens/SplashScreen.kt b/composeApp/src/commonMain/kotlin/com.codandotv.streamplayerapp/presentation/screens/SplashScreen.kt index 97aa353b..2a5444a9 100644 --- a/composeApp/src/commonMain/kotlin/com.codandotv.streamplayerapp/presentation/screens/SplashScreen.kt +++ b/composeApp/src/commonMain/kotlin/com.codandotv.streamplayerapp/presentation/screens/SplashScreen.kt @@ -5,16 +5,10 @@ import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.runtime.Composable -import androidx.compose.runtime.getValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color -import androidx.compose.ui.tooling.preview.Preview -import com.airbnb.lottie.compose.LottieAnimation -import com.airbnb.lottie.compose.LottieCompositionSpec -import com.airbnb.lottie.compose.animateLottieCompositionAsState -import com.airbnb.lottie.compose.rememberLottieComposition -import com.codandotv.streamplayerapp.core.shared.ui.R as SharedUiR +import com.codandotv.streamplayerapp.presentation.components.LottieComponent @Composable fun SplashScreen( @@ -29,18 +23,7 @@ fun SplashScreen( .fillMaxSize() .background(Color.Black) ) { - val composition by rememberLottieComposition(LottieCompositionSpec.RawRes(SharedUiR.raw.logo)) - val logoAnimationState = animateLottieCompositionAsState(composition = composition) - LottieAnimation(composition = composition, progress = { logoAnimationState.progress }) - if (logoAnimationState.isAtEnd && logoAnimationState.isPlaying) { - onAnimationFinished() - } + LottieComponent(modifier = Modifier, onAnimationFinished = onAnimationFinished) } } } - -@Composable -@Preview -fun SplashScreenPreview() { - SplashScreen(onAnimationFinished = {}) -} \ No newline at end of file diff --git a/composeApp/src/iosMain/kotlin/com.codandotv.streamplayerapp/presentation/components/LottieComponent.ios.kt b/composeApp/src/iosMain/kotlin/com.codandotv.streamplayerapp/presentation/components/LottieComponent.ios.kt new file mode 100644 index 00000000..ef15e368 --- /dev/null +++ b/composeApp/src/iosMain/kotlin/com.codandotv.streamplayerapp/presentation/components/LottieComponent.ios.kt @@ -0,0 +1,7 @@ +package com.codandotv.streamplayerapp.presentation.components + +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier + +@Composable +actual fun LottieComponent(modifier: Modifier, onAnimationFinished: () -> Unit) = Unit \ No newline at end of file diff --git a/core-shared-ui/src/androidMain/res/raw/logo.json b/core-shared-ui/src/androidMain/res/raw/logo.json deleted file mode 100644 index 11491082..00000000 --- a/core-shared-ui/src/androidMain/res/raw/logo.json +++ /dev/null @@ -1 +0,0 @@ -{"v":"5.5.7","meta":{"g":"LottieFiles AE 0.1.21","a":"","k":"","d":"","tc":"#FFFFFF"},"fr":60,"ip":0,"op":306,"w":512,"h":512,"nm":"Nettflix","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Big N 1","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":18,"s":[0]},{"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":28.666,"s":[100]},{"i":{"x":[1],"y":[1]},"o":{"x":[0.612],"y":[0]},"t":114,"s":[100]},{"t":142,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.455,"y":1},"o":{"x":0.684,"y":0},"t":104,"s":[256,256,0],"to":[-24.667,0,0],"ti":[24.667,0,0]},{"t":154.74609375,"s":[108,256,0]}],"ix":2},"a":{"a":0,"k":[0.209,0.143,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.461,0.461,0.667],"y":[1,1,1]},"o":{"x":[0.655,0.655,0.333],"y":[0,0,0]},"t":46,"s":[66,66,100]},{"t":104,"s":[21,21,100]}],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.252,"y":1},"o":{"x":0.434,"y":0},"t":18,"s":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[42.684,1.078],[0,0]],"v":[[120.607,218.722],[31.805,211.525],[31.977,211.212],[120.318,218.496]],"c":true}]},{"i":{"x":0.833,"y":1},"o":{"x":0.167,"y":0},"t":46,"s":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[42.684,1.078],[0,0]],"v":[[-34.128,-218.21],[-119.9,-218.21],[31.977,211.212],[120.318,218.496]],"c":true}]},{"i":{"x":0.833,"y":1},"o":{"x":0.167,"y":0},"t":124,"s":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[42.684,1.078],[0,0]],"v":[[-34.128,-218.21],[-119.9,-218.21],[31.977,211.212],[120.318,218.496]],"c":true}]},{"t":156,"s":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[44.422,-6.307],[0,0]],"v":[[-47.223,-218.706],[-102.043,-218.706],[41.501,198.117],[102.46,192.802]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.898039215686,0.035294117647,0.078431372549,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Big N 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":3868,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"Big N 2 matte","parent":1,"td":1,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":0,"s":[0]},{"i":{"x":[0.833],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":10.666,"s":[100]},{"i":{"x":[1],"y":[1]},"o":{"x":[0.612],"y":[0]},"t":114,"s":[100]},{"t":142,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[77.223,0,0],"ix":2},"a":{"a":0,"k":[77.223,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.252,"y":1},"o":{"x":0.434,"y":0},"t":0,"s":[{"i":[[0,0],[0,0],[0,0],[1.198,0]],"o":[[0,0],[0,0],[-0.901,0],[0,0]],"v":[[34.099,-359.119],[120.288,-359.43],[120.49,-359.888],[33.164,-359.117]],"c":true}]},{"i":{"x":0.833,"y":1},"o":{"x":0.167,"y":0},"t":24,"s":[{"i":[[0,0],[0,0],[0,0],[28.673,0.875]],"o":[[0,0],[0,0],[-28.673,-3.601],[0,0]],"v":[[34.128,-218.21],[120.318,-218.521],[120.318,218.521],[34.128,211.337]],"c":true}]},{"i":{"x":0.833,"y":1},"o":{"x":0.167,"y":0},"t":124,"s":[{"i":[[0,0],[0,0],[0,0],[28.673,0.875]],"o":[[0,0],[0,0],[-28.673,-3.601],[0,0]],"v":[[34.128,-218.21],[120.318,-218.521],[120.318,218.521],[34.128,211.337]],"c":true}]},{"t":156,"s":[{"i":[[0,0],[0,0],[0,0],[28.249,-4.53]],"o":[[0,0],[0,0],[-27.545,1.34],[0,0]],"v":[[48.414,-218.706],[114.365,-217.965],[116.869,194.041],[44.408,201.102]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.694117647059,0.023529411765,0.058823529412,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Big N 2","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":3868,"st":0,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"Big N 1 shadow 2","tt":1,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":18,"s":[0]},{"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":28.666,"s":[100]},{"i":{"x":[1],"y":[1]},"o":{"x":[0.612],"y":[0]},"t":114,"s":[100]},{"t":142,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.455,"y":1},"o":{"x":0.684,"y":0},"t":104,"s":[256,256,0],"to":[-24.667,0,0],"ti":[24.667,0,0]},{"t":154.74609375,"s":[108,256,0]}],"ix":2},"a":{"a":0,"k":[0.209,0.143,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.461,0.461,0.667],"y":[1,1,1]},"o":{"x":[0.655,0.655,0.333],"y":[0,0,0]},"t":46,"s":[66,66,100]},{"t":104,"s":[21,21,100]}],"ix":6}},"ao":0,"ef":[{"ty":25,"nm":"Drop Shadow","np":8,"mn":"ADBE Drop Shadow","ix":1,"en":1,"ef":[{"ty":2,"nm":"Shadow Color","mn":"ADBE Drop Shadow-0001","ix":1,"v":{"a":0,"k":[0.086274512112,0,0.0074725952,1],"ix":1}},{"ty":0,"nm":"Opacity","mn":"ADBE Drop Shadow-0002","ix":2,"v":{"a":0,"k":178.5,"ix":2}},{"ty":0,"nm":"Direction","mn":"ADBE Drop Shadow-0003","ix":3,"v":{"a":0,"k":0,"ix":3}},{"ty":0,"nm":"Distance","mn":"ADBE Drop Shadow-0004","ix":4,"v":{"a":0,"k":0,"ix":4}},{"ty":0,"nm":"Softness","mn":"ADBE Drop Shadow-0005","ix":5,"v":{"a":0,"k":100,"ix":5}},{"ty":7,"nm":"Shadow Only","mn":"ADBE Drop Shadow-0006","ix":6,"v":{"a":0,"k":0,"ix":6}}]}],"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.252,"y":1},"o":{"x":0.434,"y":0},"t":18,"s":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[42.684,1.078],[0,0]],"v":[[120.607,218.722],[31.805,211.525],[31.977,211.212],[120.318,218.496]],"c":true}]},{"i":{"x":0.833,"y":1},"o":{"x":0.167,"y":0},"t":46,"s":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[42.684,1.078],[0,0]],"v":[[-34.128,-218.21],[-119.9,-218.21],[31.977,211.212],[120.318,218.496]],"c":true}]},{"i":{"x":0.833,"y":1},"o":{"x":0.167,"y":0},"t":124,"s":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[42.684,1.078],[0,0]],"v":[[-34.128,-218.21],[-119.9,-218.21],[31.977,211.212],[120.318,218.496]],"c":true}]},{"t":156,"s":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[44.422,-6.307],[0,0]],"v":[[-47.223,-218.706],[-102.043,-218.706],[41.501,198.117],[102.46,192.802]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.898039215686,0.035294117647,0.078431372549,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Big N 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":0,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":3868,"st":0,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"Big N 2","parent":1,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":0,"s":[0]},{"i":{"x":[0.833],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":10.666,"s":[100]},{"i":{"x":[1],"y":[1]},"o":{"x":[0.612],"y":[0]},"t":114,"s":[100]},{"t":142,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[77.223,0,0],"ix":2},"a":{"a":0,"k":[77.223,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.252,"y":1},"o":{"x":0.434,"y":0},"t":0,"s":[{"i":[[0,0],[0,0],[0,0],[1.198,0]],"o":[[0,0],[0,0],[-0.901,0],[0,0]],"v":[[34.099,-359.119],[120.288,-359.43],[120.49,-359.888],[33.164,-359.117]],"c":true}]},{"i":{"x":0.833,"y":1},"o":{"x":0.167,"y":0},"t":24,"s":[{"i":[[0,0],[0,0],[0,0],[28.673,0.875]],"o":[[0,0],[0,0],[-28.673,-3.601],[0,0]],"v":[[34.128,-218.21],[120.318,-218.521],[120.318,218.521],[34.128,211.337]],"c":true}]},{"i":{"x":0.833,"y":1},"o":{"x":0.167,"y":0},"t":124,"s":[{"i":[[0,0],[0,0],[0,0],[28.673,0.875]],"o":[[0,0],[0,0],[-28.673,-3.601],[0,0]],"v":[[34.128,-218.21],[120.318,-218.521],[120.318,218.521],[34.128,211.337]],"c":true}]},{"t":156,"s":[{"i":[[0,0],[0,0],[0,0],[28.249,-4.53]],"o":[[0,0],[0,0],[-27.545,1.34],[0,0]],"v":[[48.414,-218.706],[114.365,-217.965],[116.869,194.041],[44.408,201.102]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.694117647059,0.023529411765,0.058823529412,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Big N 2","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":3868,"st":0,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":"Big N 3 matte","parent":1,"td":1,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":38,"s":[0]},{"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":48.666,"s":[100]},{"i":{"x":[1],"y":[1]},"o":{"x":[0.612],"y":[0]},"t":114,"s":[100]},{"t":142,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[-77.223,0,0],"ix":2},"a":{"a":0,"k":[-77.223,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.252,"y":1},"o":{"x":0.434,"y":0},"t":38,"s":[{"i":[[0,0],[0,0],[0,0],[0,1.089]],"o":[[0,0],[0,0],[26.294,0.724],[0,0]],"v":[[-34.128,-218.21],[-120.318,-218.521],[-120.72,-218.979],[-34.705,-218.587]],"c":true}]},{"i":{"x":0.833,"y":1},"o":{"x":0.167,"y":0},"t":60,"s":[{"i":[[0,0],[0,0],[0,0],[-28.673,0.875]],"o":[[0,0],[0,0],[28.673,-3.601],[0,0]],"v":[[-34.128,-218.21],[-120.318,-218.521],[-120.318,218.521],[-34.128,211.337]],"c":true}]},{"i":{"x":0.833,"y":1},"o":{"x":0.167,"y":0},"t":124,"s":[{"i":[[0,0],[0,0],[0,0],[-28.673,0.875]],"o":[[0,0],[0,0],[28.673,-3.601],[0,0]],"v":[[-34.128,-218.21],[-120.318,-218.521],[-120.318,218.521],[-34.128,211.337]],"c":true}]},{"t":156,"s":[{"i":[[0,0],[0,0],[0,0],[-28.758,-0.959]],"o":[[0,0],[0,0],[28.673,-3.601],[0,0]],"v":[[-43.652,-218.74],[-106.032,-218.137],[-106.032,217.33],[-37.7,211.816]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.694117647059,0.023529411765,0.058823529412,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Big N 3","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":3868,"st":0,"bm":0},{"ddd":0,"ind":6,"ty":4,"nm":"Big N 1 shadow","tt":1,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":18,"s":[0]},{"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":28.666,"s":[100]},{"i":{"x":[1],"y":[1]},"o":{"x":[0.612],"y":[0]},"t":114,"s":[100]},{"t":142,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.455,"y":1},"o":{"x":0.684,"y":0},"t":104,"s":[256,256,0],"to":[-24.667,0,0],"ti":[24.667,0,0]},{"t":154.74609375,"s":[108,256,0]}],"ix":2},"a":{"a":0,"k":[0.209,0.143,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.461,0.461,0.667],"y":[1,1,1]},"o":{"x":[0.655,0.655,0.333],"y":[0,0,0]},"t":46,"s":[66,66,100]},{"t":104,"s":[21,21,100]}],"ix":6}},"ao":0,"ef":[{"ty":25,"nm":"Drop Shadow","np":8,"mn":"ADBE Drop Shadow","ix":1,"en":1,"ef":[{"ty":2,"nm":"Shadow Color","mn":"ADBE Drop Shadow-0001","ix":1,"v":{"a":0,"k":[0.086274512112,0,0.0074725952,1],"ix":1}},{"ty":0,"nm":"Opacity","mn":"ADBE Drop Shadow-0002","ix":2,"v":{"a":0,"k":178.5,"ix":2}},{"ty":0,"nm":"Direction","mn":"ADBE Drop Shadow-0003","ix":3,"v":{"a":0,"k":0,"ix":3}},{"ty":0,"nm":"Distance","mn":"ADBE Drop Shadow-0004","ix":4,"v":{"a":0,"k":0,"ix":4}},{"ty":0,"nm":"Softness","mn":"ADBE Drop Shadow-0005","ix":5,"v":{"a":0,"k":100,"ix":5}},{"ty":7,"nm":"Shadow Only","mn":"ADBE Drop Shadow-0006","ix":6,"v":{"a":0,"k":0,"ix":6}}]}],"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.252,"y":1},"o":{"x":0.434,"y":0},"t":18,"s":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[42.684,1.078],[0,0]],"v":[[120.607,218.722],[31.805,211.525],[31.977,211.212],[120.318,218.496]],"c":true}]},{"i":{"x":0.833,"y":1},"o":{"x":0.167,"y":0},"t":46,"s":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[42.684,1.078],[0,0]],"v":[[-34.128,-218.21],[-119.9,-218.21],[31.977,211.212],[120.318,218.496]],"c":true}]},{"i":{"x":0.833,"y":1},"o":{"x":0.167,"y":0},"t":124,"s":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[42.684,1.078],[0,0]],"v":[[-34.128,-218.21],[-119.9,-218.21],[31.977,211.212],[120.318,218.496]],"c":true}]},{"t":156,"s":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[44.422,-6.307],[0,0]],"v":[[-47.223,-218.706],[-102.043,-218.706],[41.501,198.117],[102.46,192.802]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.898039215686,0.035294117647,0.078431372549,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Big N 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":3868,"st":0,"bm":0},{"ddd":0,"ind":7,"ty":4,"nm":"Big N 3","parent":1,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":38,"s":[0]},{"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":48.666,"s":[100]},{"i":{"x":[1],"y":[1]},"o":{"x":[0.612],"y":[0]},"t":114,"s":[100]},{"t":142,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[-77.223,0,0],"ix":2},"a":{"a":0,"k":[-77.223,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.252,"y":1},"o":{"x":0.434,"y":0},"t":38,"s":[{"i":[[0,0],[0,0],[0,0],[0,1.089]],"o":[[0,0],[0,0],[26.294,0.724],[0,0]],"v":[[-34.128,-218.21],[-120.318,-218.521],[-120.72,-218.979],[-34.705,-218.587]],"c":true}]},{"i":{"x":0.833,"y":1},"o":{"x":0.167,"y":0},"t":60,"s":[{"i":[[0,0],[0,0],[0,0],[-28.673,0.875]],"o":[[0,0],[0,0],[28.673,-3.601],[0,0]],"v":[[-34.128,-218.21],[-120.318,-218.521],[-120.318,218.521],[-34.128,211.337]],"c":true}]},{"i":{"x":0.833,"y":1},"o":{"x":0.167,"y":0},"t":124,"s":[{"i":[[0,0],[0,0],[0,0],[-28.673,0.875]],"o":[[0,0],[0,0],[28.673,-3.601],[0,0]],"v":[[-34.128,-218.21],[-120.318,-218.521],[-120.318,218.521],[-34.128,211.337]],"c":true}]},{"t":156,"s":[{"i":[[0,0],[0,0],[0,0],[-28.758,-0.959]],"o":[[0,0],[0,0],[28.673,-3.601],[0,0]],"v":[[-43.652,-218.74],[-106.032,-218.137],[-106.032,217.33],[-37.7,211.816]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.694117647059,0.023529411765,0.058823529412,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Big N 3","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":3868,"st":0,"bm":0},{"ddd":0,"ind":8,"ty":4,"nm":"Netflix reveal","parent":9,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.715,"y":1},"o":{"x":0.414,"y":0},"t":206.207,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[166,-55],[165,-52]],"c":false}]},{"t":220,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[164,-51],[125,45.5]],"c":false}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":20,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 2","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.715,"y":1},"o":{"x":0.414,"y":0},"t":200.297,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[125,-54.5],[125,-52]],"c":false}]},{"t":214.08984375,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[126.5,-52],[164,50]],"c":false}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":20,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[144.5,-1],"ix":2},"a":{"a":0,"k":[144.5,-1],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"X","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.715,"y":1},"o":{"x":0.414,"y":0},"t":186.271,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[100,-51],[100,-48]],"c":false}]},{"t":198.09375,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[100,-47],[100,42]],"c":false}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":20,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"I","np":3,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.715,"y":1},"o":{"x":0.414,"y":0},"t":175.586,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[37,29],[40.5,29]],"c":false}]},{"t":189.37890625,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[37,29],[83.5,28.5]],"c":false}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":20,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"L 2","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.715,"y":1},"o":{"x":0.414,"y":0},"t":171.645,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[49,-50.5],[49,-48.5]],"c":true}]},{"t":185.4375,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[49,-47.5],[49.5,38]],"c":false}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":20,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"L1","np":3,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[60.25,-4.75],"ix":2},"a":{"a":0,"k":[60.25,-4.75],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"L","np":2,"cix":2,"bm":0,"ix":3,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.715,"y":1},"o":{"x":0.414,"y":0},"t":159.992,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-9.5,-7.5],[-11.5,-7.5]],"c":false}]},{"t":175.75390625,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[28,-7.5],[-9.5,-7.5]],"c":false}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":20,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 2","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.715,"y":1},"o":{"x":0.414,"y":0},"t":154.08,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[0.5,-50],[0.5,-48]],"c":false}]},{"t":169.84375,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[0.5,-48],[0,37]],"c":false}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":20,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 3","np":3,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.715,"y":1},"o":{"x":0.414,"y":0},"t":145.266,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-10.5,-39.5],[-12,-39.5]],"c":false}]},{"t":160.693359375,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[36,-39.5],[-9,-39.5]],"c":false}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":20,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":3,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[13.25,-5.5],"ix":2},"a":{"a":0,"k":[13.25,-5.5],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"F","np":3,"cix":2,"bm":0,"ix":4,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.715,"y":1},"o":{"x":0.414,"y":0},"t":136.115,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-40.5,-54],[-40.5,-49.5]],"c":false}]},{"t":151.876953125,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-38.5,-45.5],[-39,38.5]],"c":false}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":20,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 2","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.715,"y":1},"o":{"x":0.414,"y":0},"t":132.174,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-62.5,-38.5],[-66,-38.5]],"c":false}]},{"t":147.9375,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-14.5,-40.5],[-63.5,-38.5]],"c":false}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":20,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[-39,-3.5],"ix":2},"a":{"a":0,"k":[-39,-3.5],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"T","np":2,"cix":2,"bm":0,"ix":5,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.715,"y":1},"o":{"x":0.414,"y":0},"t":127.73,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-114,32.5],[-119,32.5]],"c":false}]},{"t":147.435546875,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-67.5,29],[-112,32.5]],"c":false}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":20,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 3","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.715,"y":1},"o":{"x":0.414,"y":0},"t":121.82,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-115,-6.5],[-119.5,-6.5]],"c":true}]},{"t":141.033203125,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-74.5,-6],[-113,-6]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":20,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 2","np":3,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.715,"y":1},"o":{"x":0.414,"y":0},"t":117.881,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-103.5,-54.5],[-103.5,-49]],"c":false}]},{"t":137.58203125,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-103.5,-48.5],[-102.5,42]],"c":false}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":20,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 4","np":3,"cix":2,"bm":0,"ix":3,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.715,"y":1},"o":{"x":0.414,"y":0},"t":110,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-117,-38],[-120.5,-38]],"c":false}]},{"t":129.2109375,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-68,-38],[-114.5,-38]],"c":false}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":20,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":4,"mn":"ADBE Vector Group","hd":false},{"ty":"tr","p":{"a":0,"k":[-90,-3.25],"ix":2},"a":{"a":0,"k":[-90,-3.25],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"E","np":4,"cix":2,"bm":0,"ix":6,"mn":"ADBE Vector Group","hd":false}],"ip":101,"op":3868,"st":0,"bm":0},{"ddd":0,"ind":9,"ty":4,"nm":"Etflix","tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"s":true,"x":{"a":1,"k":[{"i":{"x":[0.455],"y":[1]},"o":{"x":[0.684],"y":[0]},"t":104,"s":[402.75]},{"t":154.74609375,"s":[256]}],"ix":3},"y":{"a":0,"k":256,"ix":4}},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":1,"ty":"sh","ix":2,"ks":{"a":0,"k":{"i":[[0,0],[-5.131,0],[0,0],[5.401,-0.27],[0,0],[-8.551,0.27],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[5.582,0],[0,0],[-6.392,0],[0,0],[8.462,-0.54],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[-95.285,-12.333],[-76.021,-12.603],[-76.021,1.8],[-95.285,2.07],[-95.285,23.495],[-69.81,21.964],[-69.81,35.826],[-109.689,38.978],[-109.689,-46.09],[-69.81,-46.09],[-69.81,-31.687],[-95.285,-31.687]],"c":true},"ix":2},"nm":"Path 2","mn":"ADBE Vector Shape - Group","hd":false},{"ind":2,"ty":"sh","ix":3,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[4.68,-0.18],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[-4.861,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[-16.249,-31.686],[-31.192,-31.686],[-31.192,34.568],[-45.594,34.747],[-45.594,-31.686],[-60.537,-31.686],[-60.537,-46.09],[-16.248,-46.09]],"c":true},"ix":2},"nm":"Path 3","mn":"ADBE Vector Shape - Group","hd":false},{"ind":3,"ty":"sh","ix":4,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[7.156,-13.323],[26.871,-13.323],[26.871,1.08],[7.156,1.08],[7.156,33.757],[-6.978,33.757],[-6.978,-46.09],[33.262,-46.09],[33.262,-31.687],[7.156,-31.687]],"c":true},"ix":2},"nm":"Path 4","mn":"ADBE Vector Shape - Group","hd":false},{"ind":4,"ty":"sh","ix":5,"ks":{"a":0,"k":{"i":[[0,0],[-8.012,-0.449],[0,0],[13.143,0.271],[0,0],[0,0]],"o":[[8.192,0.18],[0,0],[-12.873,-0.811],[0,0],[0,0],[0,0]],"v":[[56.667,21.154],[81.152,22.415],[81.152,36.638],[42.264,34.747],[42.264,-46.09],[56.667,-46.09]],"c":true},"ix":2},"nm":"Path 5","mn":"ADBE Vector Shape - Group","hd":false},{"ind":5,"ty":"sh","ix":6,"ks":{"a":0,"k":{"i":[[0,0],[-4.681,-0.539],[0,0],[0,0]],"o":[[4.591,0.27],[0,0],[0,0],[0,0]],"v":[[93.304,37.628],[107.437,38.708],[107.437,-46.09],[93.304,-46.09]],"c":true},"ix":2},"nm":"Path 6","mn":"ADBE Vector Shape - Group","hd":false},{"ind":6,"ty":"sh","ix":7,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[5.401,0.9],[0,0],[0,0],[5.22,0.72],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[-5.403,-0.72],[0,0],[0,0],[-5.222,-0.901],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[170.541,-46.09],[152.267,-2.25],[170.541,46.09],[154.337,43.479],[143.985,16.834],[133.454,41.319],[117.971,39.428],[136.515,-2.791],[119.771,-46.09],[135.253,-46.09],[144.705,-21.875],[154.788,-46.09]],"c":true},"ix":2},"nm":"Path 7","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.898039215686,0.035294117647,0.078431372549,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Netflix Wordmark","np":8,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":102,"op":3868,"st":0,"bm":0},{"ddd":0,"ind":10,"ty":4,"nm":"Netflix N","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"s":true,"x":{"a":1,"k":[{"i":{"x":[0.455],"y":[1]},"o":{"x":[0.684],"y":[0]},"t":104,"s":[402.75]},{"t":154.74609375,"s":[256]}],"ix":3},"y":{"a":0,"k":256,"ix":4}},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[5.401,-0.719],[0,0],[0,0],[4.861,-0.72],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[-5.131,0.901],[0,0],[0,0],[-5.131,0.54],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[-123.641,40.148],[-139.394,42.039],[-155.868,-6.211],[-155.868,44.11],[-170.541,46.09],[-170.541,-46.09],[-156.858,-46.09],[-138.134,6.212],[-138.134,-46.09],[-123.641,-46.09]],"c":true},"ix":2},"nm":"N","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.898039215686,0.035294117647,0.078431372549,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Netflix Wordmark","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":102,"op":3868,"st":0,"bm":0}],"markers":[]} \ No newline at end of file From 6e5061d387f790867bc2ab6beaae34ee212c046e Mon Sep 17 00:00:00 2001 From: Gabriel Bronzatti Moro Date: Sat, 15 Feb 2025 16:16:33 -0300 Subject: [PATCH 76/96] Isolate MainActivity and CustomApplication class - Android specific --- composeApp/src/androidMain/AndroidManifest.xml | 4 ++-- .../presentation}/CustomApplication.kt | 2 +- .../presentation}/MainActivity.kt | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) rename composeApp/src/{commonMain/kotlin/com.codandotv.streamplayerapp => androidMain/kotlin/com.codandotv.streamplayerapp/presentation}/CustomApplication.kt (88%) rename composeApp/src/{commonMain/kotlin/com.codandotv.streamplayerapp => androidMain/kotlin/com.codandotv.streamplayerapp/presentation}/MainActivity.kt (93%) diff --git a/composeApp/src/androidMain/AndroidManifest.xml b/composeApp/src/androidMain/AndroidManifest.xml index 80a3280c..a074f2bb 100644 --- a/composeApp/src/androidMain/AndroidManifest.xml +++ b/composeApp/src/androidMain/AndroidManifest.xml @@ -7,10 +7,10 @@ android:roundIcon="@mipmap/ic_netflix_round" android:label="@string/app_name" android:supportsRtl="true" - android:name=".CustomApplication"> + android:name=".presentation.CustomApplication"> diff --git a/composeApp/src/commonMain/kotlin/com.codandotv.streamplayerapp/CustomApplication.kt b/composeApp/src/androidMain/kotlin/com.codandotv.streamplayerapp/presentation/CustomApplication.kt similarity index 88% rename from composeApp/src/commonMain/kotlin/com.codandotv.streamplayerapp/CustomApplication.kt rename to composeApp/src/androidMain/kotlin/com.codandotv.streamplayerapp/presentation/CustomApplication.kt index 9c565d56..8cbc1494 100644 --- a/composeApp/src/commonMain/kotlin/com.codandotv.streamplayerapp/CustomApplication.kt +++ b/composeApp/src/androidMain/kotlin/com.codandotv.streamplayerapp/presentation/CustomApplication.kt @@ -1,4 +1,4 @@ -package com.codandotv.streamplayerapp +package com.codandotv.streamplayerapp.presentation import android.app.Application import com.codandotv.streamplayerapp.di.AppModule diff --git a/composeApp/src/commonMain/kotlin/com.codandotv.streamplayerapp/MainActivity.kt b/composeApp/src/androidMain/kotlin/com.codandotv.streamplayerapp/presentation/MainActivity.kt similarity index 93% rename from composeApp/src/commonMain/kotlin/com.codandotv.streamplayerapp/MainActivity.kt rename to composeApp/src/androidMain/kotlin/com.codandotv.streamplayerapp/presentation/MainActivity.kt index 94deedd3..2ae2e145 100644 --- a/composeApp/src/commonMain/kotlin/com.codandotv.streamplayerapp/MainActivity.kt +++ b/composeApp/src/androidMain/kotlin/com.codandotv.streamplayerapp/presentation/MainActivity.kt @@ -1,4 +1,4 @@ -package com.codandotv.streamplayerapp +package com.codandotv.streamplayerapp.presentation import android.os.Bundle import androidx.activity.ComponentActivity From bbb12cdb8e11eee1e24d7c4f352d84d741ade165 Mon Sep 17 00:00:00 2001 From: Rods Date: Sun, 16 Feb 2025 08:14:15 -0300 Subject: [PATCH 77/96] [ISSUE-21/24] - remove lifecycle and add remember Koin --- .../screens/ProfilePickerStreamScreen.kt | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/feature-profile/src/commonMain/kotlin/com/codandotv/streamplayerapp/profile/presentation/screens/ProfilePickerStreamScreen.kt b/feature-profile/src/commonMain/kotlin/com/codandotv/streamplayerapp/profile/presentation/screens/ProfilePickerStreamScreen.kt index e1ee37f1..396526c5 100644 --- a/feature-profile/src/commonMain/kotlin/com/codandotv/streamplayerapp/profile/presentation/screens/ProfilePickerStreamScreen.kt +++ b/feature-profile/src/commonMain/kotlin/com/codandotv/streamplayerapp/profile/presentation/screens/ProfilePickerStreamScreen.kt @@ -20,6 +20,8 @@ import androidx.compose.ui.unit.IntOffset import androidx.compose.ui.unit.dp import androidx.lifecycle.LifecycleOwner import androidx.lifecycle.compose.collectAsStateWithLifecycle +import com.codandotv.streamplayerapp.core_shared_ui.theme.ThemePreview +import com.codandotv.streamplayerapp.core_shared_ui.theme.ThemePreviews import com.codandotv.streamplayerapp.feature.profile.R import com.codandotv.streamplayerapp.profile.domain.ProfileStream import com.codandotv.streamplayerapp.profile.presentation.widget.LoadScreen @@ -152,16 +154,4 @@ fun SetupProfilePickerScreen( } } ) -} - -@ThemePreviews -@Composable -fun SetupProfilePickerScreenPreview() { - ThemePreview { - SetupProfilePickerScreen( - uiState = ProfilePickerStreamsUIState( - selectedItem = ProfileStream("1", "Name", ""), - ) - ) - } } \ No newline at end of file From 3716dc0afea14170ae1a284bda241f6b62f2f8bf Mon Sep 17 00:00:00 2001 From: Rods Date: Mon, 17 Feb 2025 19:19:09 -0300 Subject: [PATCH 78/96] [ISSUE-16] - add iOSApp --- iosApp/Configuration/Config.xcconfig | 3 + iosApp/iosApp.xcodeproj/project.pbxproj | 393 ++++++++++++++++++ .../AccentColor.colorset/Contents.json | 11 + .../AppIcon.appiconset/Contents.json | 14 + .../AppIcon.appiconset/app-icon-1024.png | Bin 0 -> 67285 bytes iosApp/iosApp/Assets.xcassets/Contents.json | 6 + iosApp/iosApp/ContentView.swift | 21 + iosApp/iosApp/Info.plist | 50 +++ .../Preview Assets.xcassets/Contents.json | 6 + iosApp/iosApp/iOSApp.swift | 10 + 10 files changed, 514 insertions(+) create mode 100644 iosApp/Configuration/Config.xcconfig create mode 100644 iosApp/iosApp.xcodeproj/project.pbxproj create mode 100644 iosApp/iosApp/Assets.xcassets/AccentColor.colorset/Contents.json create mode 100644 iosApp/iosApp/Assets.xcassets/AppIcon.appiconset/Contents.json create mode 100644 iosApp/iosApp/Assets.xcassets/AppIcon.appiconset/app-icon-1024.png create mode 100644 iosApp/iosApp/Assets.xcassets/Contents.json create mode 100644 iosApp/iosApp/ContentView.swift create mode 100644 iosApp/iosApp/Info.plist create mode 100644 iosApp/iosApp/Preview Content/Preview Assets.xcassets/Contents.json create mode 100644 iosApp/iosApp/iOSApp.swift diff --git a/iosApp/Configuration/Config.xcconfig b/iosApp/Configuration/Config.xcconfig new file mode 100644 index 00000000..81c20de5 --- /dev/null +++ b/iosApp/Configuration/Config.xcconfig @@ -0,0 +1,3 @@ +TEAM_ID= +BUNDLE_ID=com.codandotv.streamplayerapp.KotlinProject +APP_NAME=KotlinProject \ No newline at end of file diff --git a/iosApp/iosApp.xcodeproj/project.pbxproj b/iosApp/iosApp.xcodeproj/project.pbxproj new file mode 100644 index 00000000..74749301 --- /dev/null +++ b/iosApp/iosApp.xcodeproj/project.pbxproj @@ -0,0 +1,393 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 56; + objects = { + +/* Begin PBXBuildFile section */ + 058557BB273AAA24004C7B11 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 058557BA273AAA24004C7B11 /* Assets.xcassets */; }; + 058557D9273AAEEB004C7B11 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 058557D8273AAEEB004C7B11 /* Preview Assets.xcassets */; }; + 2152FB042600AC8F00CF470E /* iOSApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2152FB032600AC8F00CF470E /* iOSApp.swift */; }; + 7555FF83242A565900829871 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7555FF82242A565900829871 /* ContentView.swift */; }; +/* End PBXBuildFile section */ + +/* Begin PBXFileReference section */ + 058557BA273AAA24004C7B11 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + 058557D8273AAEEB004C7B11 /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = ""; }; + 2152FB032600AC8F00CF470E /* iOSApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = iOSApp.swift; sourceTree = ""; }; + 7555FF7B242A565900829871 /* KotlinProject.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = KotlinProject.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 7555FF82242A565900829871 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = ""; }; + 7555FF8C242A565B00829871 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + AB3632DC29227652001CCB65 /* Config.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Config.xcconfig; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + B92378962B6B1156000C7307 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 058557D7273AAEEB004C7B11 /* Preview Content */ = { + isa = PBXGroup; + children = ( + 058557D8273AAEEB004C7B11 /* Preview Assets.xcassets */, + ); + path = "Preview Content"; + sourceTree = ""; + }; + 42799AB246E5F90AF97AA0EF /* Frameworks */ = { + isa = PBXGroup; + children = ( + ); + name = Frameworks; + sourceTree = ""; + }; + 7555FF72242A565900829871 = { + isa = PBXGroup; + children = ( + AB1DB47929225F7C00F7AF9C /* Configuration */, + 7555FF7D242A565900829871 /* iosApp */, + 7555FF7C242A565900829871 /* Products */, + 42799AB246E5F90AF97AA0EF /* Frameworks */, + ); + sourceTree = ""; + }; + 7555FF7C242A565900829871 /* Products */ = { + isa = PBXGroup; + children = ( + 7555FF7B242A565900829871 /* KotlinProject.app */, + ); + name = Products; + sourceTree = ""; + }; + 7555FF7D242A565900829871 /* iosApp */ = { + isa = PBXGroup; + children = ( + 058557BA273AAA24004C7B11 /* Assets.xcassets */, + 7555FF82242A565900829871 /* ContentView.swift */, + 7555FF8C242A565B00829871 /* Info.plist */, + 2152FB032600AC8F00CF470E /* iOSApp.swift */, + 058557D7273AAEEB004C7B11 /* Preview Content */, + ); + path = iosApp; + sourceTree = ""; + }; + AB1DB47929225F7C00F7AF9C /* Configuration */ = { + isa = PBXGroup; + children = ( + AB3632DC29227652001CCB65 /* Config.xcconfig */, + ); + path = Configuration; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 7555FF7A242A565900829871 /* iosApp */ = { + isa = PBXNativeTarget; + buildConfigurationList = 7555FFA5242A565B00829871 /* Build configuration list for PBXNativeTarget "iosApp" */; + buildPhases = ( + F36B1CEB2AD83DDC00CB74D5 /* Compile Kotlin Framework */, + 7555FF77242A565900829871 /* Sources */, + B92378962B6B1156000C7307 /* Frameworks */, + 7555FF79242A565900829871 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = iosApp; + packageProductDependencies = ( + ); + productName = iosApp; + productReference = 7555FF7B242A565900829871 /* KotlinProject.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 7555FF73242A565900829871 /* Project object */ = { + isa = PBXProject; + attributes = { + BuildIndependentTargetsInParallel = YES; + LastSwiftUpdateCheck = 1130; + LastUpgradeCheck = 1540; + ORGANIZATIONNAME = orgName; + TargetAttributes = { + 7555FF7A242A565900829871 = { + CreatedOnToolsVersion = 11.3.1; + }; + }; + }; + buildConfigurationList = 7555FF76242A565900829871 /* Build configuration list for PBXProject "iosApp" */; + compatibilityVersion = "Xcode 14.0"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 7555FF72242A565900829871; + packageReferences = ( + ); + productRefGroup = 7555FF7C242A565900829871 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 7555FF7A242A565900829871 /* iosApp */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 7555FF79242A565900829871 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 058557D9273AAEEB004C7B11 /* Preview Assets.xcassets in Resources */, + 058557BB273AAA24004C7B11 /* Assets.xcassets in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + F36B1CEB2AD83DDC00CB74D5 /* Compile Kotlin Framework */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + ); + name = "Compile Kotlin Framework"; + outputFileListPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "if [ \"YES\" = \"$OVERRIDE_KOTLIN_BUILD_IDE_SUPPORTED\" ]; then\n echo \"Skipping Gradle build task invocation due to OVERRIDE_KOTLIN_BUILD_IDE_SUPPORTED environment variable set to \\\"YES\\\"\"\n exit 0\nfi\ncd \"$SRCROOT/..\"\n./gradlew :composeApp:embedAndSignAppleFrameworkForXcode\n"; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 7555FF77242A565900829871 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 2152FB042600AC8F00CF470E /* iOSApp.swift in Sources */, + 7555FF83242A565900829871 /* ContentView.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin XCBuildConfiguration section */ + 7555FFA3242A565B00829871 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = AB3632DC29227652001CCB65 /* Config.xcconfig */; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + ENABLE_USER_SCRIPT_SANDBOXING = NO; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 15.3; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + }; + name = Debug; + }; + 7555FFA4242A565B00829871 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = AB3632DC29227652001CCB65 /* Config.xcconfig */; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_USER_SCRIPT_SANDBOXING = NO; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 15.3; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + SDKROOT = iphoneos; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 7555FFA6242A565B00829871 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_ASSET_PATHS = "\"iosApp/Preview Content\""; + DEVELOPMENT_TEAM = "${TEAM_ID}"; + ENABLE_PREVIEWS = YES; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(SRCROOT)/../shared/build/xcode-frameworks/$(CONFIGURATION)/$(SDK_NAME)\n$(SRCROOT)/../composeApp/build/xcode-frameworks/$(CONFIGURATION)/$(SDK_NAME)", + ); + INFOPLIST_FILE = iosApp/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 15.3; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = "${BUNDLE_ID}${TEAM_ID}"; + PRODUCT_NAME = "${APP_NAME}"; + PROVISIONING_PROFILE_SPECIFIER = ""; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 7555FFA7242A565B00829871 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_ASSET_PATHS = "\"iosApp/Preview Content\""; + DEVELOPMENT_TEAM = "${TEAM_ID}"; + ENABLE_PREVIEWS = YES; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(SRCROOT)/../shared/build/xcode-frameworks/$(CONFIGURATION)/$(SDK_NAME)\n$(SRCROOT)/../composeApp/build/xcode-frameworks/$(CONFIGURATION)/$(SDK_NAME)", + ); + INFOPLIST_FILE = iosApp/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 15.3; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = "${BUNDLE_ID}${TEAM_ID}"; + PRODUCT_NAME = "${APP_NAME}"; + PROVISIONING_PROFILE_SPECIFIER = ""; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 7555FF76242A565900829871 /* Build configuration list for PBXProject "iosApp" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 7555FFA3242A565B00829871 /* Debug */, + 7555FFA4242A565B00829871 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 7555FFA5242A565B00829871 /* Build configuration list for PBXNativeTarget "iosApp" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 7555FFA6242A565B00829871 /* Debug */, + 7555FFA7242A565B00829871 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 7555FF73242A565900829871 /* Project object */; +} \ No newline at end of file diff --git a/iosApp/iosApp/Assets.xcassets/AccentColor.colorset/Contents.json b/iosApp/iosApp/Assets.xcassets/AccentColor.colorset/Contents.json new file mode 100644 index 00000000..ee7e3ca0 --- /dev/null +++ b/iosApp/iosApp/Assets.xcassets/AccentColor.colorset/Contents.json @@ -0,0 +1,11 @@ +{ + "colors" : [ + { + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} \ No newline at end of file diff --git a/iosApp/iosApp/Assets.xcassets/AppIcon.appiconset/Contents.json b/iosApp/iosApp/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 00000000..8edf56e7 --- /dev/null +++ b/iosApp/iosApp/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,14 @@ +{ + "images" : [ + { + "filename" : "app-icon-1024.png", + "idiom" : "universal", + "platform" : "ios", + "size" : "1024x1024" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/iosApp/iosApp/Assets.xcassets/AppIcon.appiconset/app-icon-1024.png b/iosApp/iosApp/Assets.xcassets/AppIcon.appiconset/app-icon-1024.png new file mode 100644 index 0000000000000000000000000000000000000000..53fc536fb9ac5c1dbb27c7e1da13db3760070a11 GIT binary patch literal 67285 zcmeFZcOaGT{|9`Wj$QUBI}*w$dt??uHYvwQvK>VBJV}y7GAcwFB{SpLdzOqi=5Y|& zGkc%sy7l?}zMtRo{Qvy*{X-w8PwxA=uj@Ttuh;u^i_p_iKSRMn0fWKLXxzME0D~dG zw+I*+3HVPi`{hvZfy&|fbv>u+>epSJUEK}ctgLO+ZCq^J9jp!1RbVjbs3>D|dp2VR zg`|q&%NM#ru~}KMRL2r=CC&yvpNz~M+Z3Zl1z$UtD93zT!lyV~6q`ECa1c;nP^M}4 zJn?#hfNbD9@0hb3DfF>K?;|3Vf465}{X;J^`C^4wan;rny=6QA1$QnZO>Q%P-?E#a|?1oocKbSzhI89UI&(+acI3 z=If~wJ;R3$+Q|p+?~*smIVW>X(lwRBOwPWiUMuQ;`%3hg zrK%wRmlwy)xM!rZJlm!SQjay<%WD#!^8~m%RKH2)ywl<7s|h^_#;D?*nsK4J(ZyE+ z8OBeQZzo=IPxuv1lWP2X^wF~dVTa-t8iGxQ1Nk2wn0Zxom^;NEg=TAG|7y0mN7-Mb ze%4?9gnesAGal;W*>LT9>&lJ8(yNxq6rMo_$){(iIbai$mxK!ac6c}nwH+=!>xeS3 zmuy>qwp%{KWD5^m5wdfT9qf_Gw0*8DxDq+FPJ8>4LbFNs`$Ux^OQAA`R$lq17Rjd{ zwO{c(+}igtNqI{)87sp~$?}3%7OWA=IlSrW!it(?Vng0Zxq-&hLssP z9=9*f{k)=*Mc`TM`O>&*Z_HDDI>^^P$Fqmr){O^yRYOE0HguPb`}OZD=gy~d#qxbK zeDLDIPgzYWiM9l8j|UqSKe4_ zv5*aPF^Q~FyPaA!;4%N`f*p&a(4+PdY>Im~q0w@7u+VZ=%JlRxY0#>(j)g7_EtKv>81?gWYW*idrM^jZyhlH;2KM0d= zY-)Uy?E+~R>>ibiS)Bzyr`Q>$X9 zbX=yM@MtKW;|@br`8`?Q%JK@*k{>BRw|e|>zD9gMz%oEwfkCm+E%e-YWUc+d%`S-4ybBrlMlUopH5y zi;daHxI$p?fB!)vh)&RMWEm3rqDLSMz4i=FKL}?9C?N4x9`=T24ub=pP0WM?+ObJ64P5b}49$6ZUCX$ynw8-bd-bKk%OPYcu{E8vjnn|AxkYL*u`-^*>$ZzxnXreE4rZ{5K!|iz@#YxBveErPBltNUy2= zgW(C}ad&Ul+4L1sIowtkqNd2!XexZiMq?m$P@vHiv(VD`e7Gz~kh_KFe0={aItPKb z-}&`z2s$qP`xFja`!8<0w%d2^=b73Ngpesed*h8w>jb7088lz~!#Cu}X<$PUp`?G= zOSuTmSJ%}hWa9kL^(I-2IXnAL(cJ4v1H)d1malsg)ic-a=T=3&KC8EQxr%wPIV@$o z|7iGj;F@Z@f~i4v|2Q4P5aqeLzx1PC2CX-X6vB3+|G8Bc#gk=@qjrqV!pPTKiq4km zZKc^fB4m0?)?wx<)jPhKw!sG3-U|8HGD(k+Q~&JvC?gka!Ud-%3gI*~9n)IY0-@0Q zhTV`h;qCS~ddvF-wklGT&~ZsS)iV1oXIANhz1!ZDn&18wZhn0tIE;5>&4?AcT)jNe zDidL@sRO(E`)YbL{ID>xz9FHMpl;V9z83e)W@dbP5Pi_lIBmR--;B$`<%T@6nfRg}_IK%S z79p^Z4ec95CoJ#rMYp*IEAw%=e2hp+t;X7qJ}9e#2|=xY=-uy!6{ z*AoV-Hv%8)Jg)CcudML?F?jBXvj6$2P=4>TuZ*T8ar3Y+(b;P!%gW?cf~A#=B#oTh zjp615*8016z`cqQaiJFD<5Kl)FY>boUZ&AHn)Z0L?bDxYE)?82Nr-zU;OVN~t5 zc^h?0kF?g>(t^8Wn@n=VSgtC3C{uh;6_Wg6UF~F*yqCc$A0)khei9D9Rni0nw^o_@ zg#xV|?{uXE3*YkI;cyK$&3 zKVR&nZAx%HDrX~z^^zzCbHDS{IF)$_PUH)>%!=qmf2 zRL|pl&u}QX=N^&=*1VgC<(HnBR)!A3O$&r4a#`8o2KnFu3<=dBz8ntN{~e z<6f^mtt_!GMGfnBE<7M;JOst=$c@WZDi;^`^K%5bc1p^??Mc`n@83Kvd=0iNMcU_Y z(k{R~t$IsESc`Bb*XeWDbKXpJtramb8i`|*vNx(8#x{#OVbk4 zg;qC(sJ^6obvDVCsNPZMU>kV2{N2b!8Lr4qnP5Es{-H*v<&7YiVkxVQD)jK}1>k;% z`|B$w`>sGsHr#t`@#)4Re?s{?@wGNt0;A*?#lWDC|glm zE1O%Di)-)*y>lH}_gXZJ2u3Jj`}`j2m~xK9 zc_q47v0^Fbm*~0o^~;`(l)1}=6n(e7`GPIAXLF}l=UnCJ4nONj&=i6qhscr7K6CO( z0x|hBMi?V;JUDDh_}nCOJmC6muHvpkRBHSW+~%>PoAIK+*vAO^Xu-benUPLg((-^G zNP|pT>(~36TI;9EM|I-PK!t^C2dYP|-{np!g!H8ee8ziEgB#vd&vIIbR`NH-liTOM z4I223VM;fq;a%8ea zsJBngyv#O~^Zu0WZ+MjY_EoPKCh>@*V{~M)zV4tJPl5ahLYv;LvkU@n*Qng1Le*^!{$~Mye8Fl zDk`pBT7%^;L3W=UavfOEnwFNn4)h7lLhj>q5T4A~f2L;gQuM%FCUM|;BO}K0=uO7V z$n79yh3b@3`Gv`pCU;(jJga(rWwUEGo<-*3hZal|{GU`-2H8(j!j!3SvZ{pvfsem1 zU3Kv`d)`~SU37=?;xgG0u31LLDm(9llAd@bm1;*%jdoJUeC=lr4!WGzW}#_+bdey^ z;ikGS^%GTGWp2>$-2 z4(clbH*YN?%jMYbz2>#vd@N3Hn`z{*cTW1GM9{2Nf#9nv)crwl=y<&Z+Udj+#Big?GiHUsxUwYRNJCaHR6na zF$UQ)kcT1S7y6-^r>URzgCv?Xg`;1)#`+7h_YTQAWfhuDMj=}!VJ_O*1ikOI5v;vh zE-Wwqv9PN1Cd_UyYl`o027|4eC?-iSKly|s){$?`ilG)XNy=IoyXunLK4+D*(9N*E zur(qn)L3bK&kP^!?oS?GW;|tRsOe9xzGWI`cd}#U7nNZ3rA#0GHaUMrdnc)gljd~O z+m%j(yKL~{=&VT1L|38mv?Hz=Kk+iL`42imqh`~~f%oC4-P9k%No;%~CWA@iuQ5i)=smbrWIle6`!n@e>cx8;)v8z!t>TFU^>~!wN_)o9WJpy}&oJ+|x`xd*!*jKl` z?L(OIcJVIu!1fT!F=tOq7n~?xd&iW599VFN4jVM97e8nx~i+i4@fNymoB6t7?+2@a3sn+yaQeW!uZ4 z`P$LM3wrL##mD8Q?7vr>VmX_e^%$bT5*JQ4;L7odT4vCjp9bWpo+Efz&AgUu z5%6K+nNs9ME4-sqg+IsYifnMS{QCF*ddE}ih*0T?MdMEM7 zo9P?HqWYK%t=JpYBAnOn@RMBF1MoY>(sGO)ibO80G#9~)4(H`@-mhu-zKH|lbG z3s6Vfd|G$vQu?3hC<;cqtXi7*A9eg1>OHVDa%eugep4F%mY)r*h(-xOHzH@FFHb;i zDd(ptQXYQKha=0&8+Pff$J37VTab9O{zo=uaI2HmHPxy&=XI4n%vI;x zP+6bfBRV+^qXJ`JCa5IU9|Pz)WT|X%(k2Ua(J#YMmb2quORKIQ3$V_Oe+~CneLjDD z;B1t7?N>Puz=acUUdj&PYs+|f<*&(ncqnG5DfX+GPd@TKbehKuAWgcx(y`#uAtH!( zBNodR3EQ=Nl_{Bl3)PzP_tK9q4;JO6ipbtRLwOEE&KFpD!!v1F^k@4o^NY2nPJ2YH zyqg07qS^z65x%m}0+l2{A{)^^|8!Cuj4Zia77In@Y5Pm%??11UJB6f77*<%GihWo2 z%xZ9MEHAie|UiDKzgwV`6 zerr(!$x>(~mLl$&f|i1~rsgeB>?0(k`yp(w&g+&@#$1(Gx`OS(f9QV{zxm@uT#%wf zb|>Sg(R7Z;?sT9Wr%i~SCxTSiyc(PaN-Q7 zLGY}FD_OJ7*L?^!J0;ju*U`2~eOY2;+tRZ3T@`;KF1yF(GNsn6cl5%H!c~b9UU)u7 zq=}1V{`v|$A*XyqEshepL@0Q0#S%Ij2pF?5tPN~a%Uu4#>eph-;aM0GEYjP^=rtvN zF}nhj|Lzo8o?JYaxwkZMs&cpFS+&q*knFqm{#=WT#)u*_6wmiCCQ;0&F3 zIvg*jD*j_&udGOrkk2uW`Zjmobzw6}!1!UoZ$~j1lYFnd#!4qWGjrMUB+j(ngraMm z228X2RKyV9J>&wHqRzW<4tj9)lU8}9N@l^?Kc~viN8{*y=@B;dZ>yY8N|S_tVrTwo zp1@zIZS5UuwkT;M?#KO2(5bJsngl#3zcEOZ%#n30#9BY20TIJ}QnwuH&r%{&AU{e`mxBpM093Vs*8?!)-5~Bci&WzHBsF1b0>_+0Ja&}mfY=HrF zbxhCqQbfHwp43MXDg^wX&^+#q#X>B-{i{-R zccPUPh(|c@Yu$Sqx7d6gkC(h+bG4AqQfofC;G*%X`{cJ24otJ zaYq%Ef|?|z;Pd$yx@qX4DMUc6UYkj#1*>#3sK=2kFDN`TAL(31^~?z7mTYyA3*GG! zx8svDh+w$H^h#KUFUzSbO2CESwY7^&OyI1?G#vicN@)9^0OZdA{Yk~qLl|s9y)wF} z5L@SORJIwBZBIZQ`akpG0jU(#c(qP3m?$CE?zA0 zlHVXQbK(0A2?W0(ZM8PcHyFB}6}n43-eEWG4VBZ%%DWjMfq5xII+hJJO$U;z>?_)t z<|Qw~;~j=T1(RvU*JV;frpU`md{ETY6;Nf%E0Gf{RfnNtLABN^($;OERZ5E^HkG1W ze5w2}B_o$j8cQD zWUlWGqQl-Yem)Q^F_%FsR>b}egpdR$88(NtSJ$uQQ3Yyw7WHR#;m_E8+<>cd7?ZF~ zN?i`>M#Z+Eo)l9rqr7$H)J1dEZ>2CU*}22(sJ$2CU%8 z@0Gzl!N#o`rb~*R>qBqh+20=8nyc-MD9nhB@p_1eD6r2-(sy&*SU&7kYZ}A8xv$*6A^>dmaV6 zcaxUVYgP4g_}o;&mn$RztJ!gNGvrPWx72Yw{1JC4=ZlHRd#EySO(=rv9XpAg2xUfE zX<<_PKFVgZpq0+0o4ks^=9<*e~h>D@(RmT+?h?qEkDif+E^pi=Sk%1 zRdg+v3hM>fJH(yu-CBNEaZq-UffD9AsU=FM_8OSiFu&RCksf1Mxvc$%-gc{k zW)_+Lt-KODVhPKLIunEI2pY04ARp5(f?Fyuv=U`=`g!wSo-a=R%?zI2Bwv{XaY0R2 zf@!5rqgP^#g!$m4Lrf`yJCTcx!nD3xerEDnfqK~od>1x5S>S&87}}GHv3&uk6S|^@ zY*59}tFPjdUd(v5Qc}}`WSdxFZybp_hj%r6`ss(xH>COx04e*KrI#iOpHf9EK0uC4 zExf|y!3p=Y{EopF=E5G2cWDYgGjupYp!y=8wEb-}>X_2fMnKH~`5dJ1mm=2HElYZA z@_NLqK^vWJ9&vx~Mw0ru-B5dQ@uIjVm4>|eKaDHE5~wyi61!4R zq^AA9J8PLMD<(jq@3A?kGczJYt`Xg;n9SKN`Ke3MmB{Vr>S+b**nRt}9f6}LUQMVF z-9*6Vi2p7wsAA2s{Qg0hVnhSm@=b=zG;j;9H8o0v#e@&nTINolU;Fy0+~b$$l+bfN zMnD0C^MOZm)7Av4B^Mby=*@n|z&+(T2W*2YJm?NZ+)XXrAR4UWRY?6wuVM;oPcf-O& zWoP(J3UpSw*w$@fw+d6>LDq640afTdn2dwZ7y>;0=P(enrfGlZKpt>0!_8lQ6{;m^ z?a%t#Ixp8jm8cQGC{&~(5QE%IChj0*#RK$ish4_r=k)xmD@;bLcwK}}4-HmIGnAEi zAB4geB^;C08Fn_4L>_jIykeqC#k%+bYZ2a(Ao_IA{B7RvVM-XKp~;BZ6qbJWBWp*a zas0$&QR%s;!b4c_UWg!i7}ahKtt=HZ`1R}#f2bLc)7#$>$;dfq_H>X!&aSR_R@esL z&VDsTXIhlJRXOgYa2yd*fLMqRe`HheCdgUqMRlfHK1aY<`G_cl+a5#E$6pSbfHi5r;qB->T5r%qM1=z2xU$G7z{(c=mE&Et8q zI0hm_053piCY`EQv`Y0N@Vq1xr>ESMeYiUQv`4bd^zm{ec^%rW6WGBp?(A-Q2+^O|1J-o!<1?&&mT1p;4OkGaf>eF$m&4L6;-WswmGU| z8+3>Op^3zR3u0iLVc(%%iDlMb3ov3-G za52~5V&Qau%bWJC2M$+fRtLw_DrnoILO8uH{K0Sr+S+Q?CB@>(5S=-m@f9Pz^x|LUs6!YeWNbiVVW+3GQSHvzt{EzEm&-!Iy%Pu%#JMYN8CYMf3t9`xjZ!biZef}>pwWK zCpNe0D5furNM@3rj46D2MtD#oyn=Q57Seg+8_*&K5~PeXb_+c!uj@;LtWyIeN=#c> z8APlNAeA^-Lc>*0(EnQ8zE_nGa~m>>bfh> zwy4&7!?m56>V+g(>$gJYA`^But>{ws^Mm#80WR?Z)SE_W4<-<85g}6FwsK!{S9&O! z2~oLue_sR*O@5aSd4DehsecOr=XEox62%8v-D+c-T#4m(UF>Viy11p-H@q*dmlFLQ zJXH`SVBD@MV;~tGbGtpjiE8;V8h-LxvA|~KWZ2neZ2DIf;?0zMbJ8~D7tkT&i0X{b z^13hQs6+%DuX~4Pb`08xyQ`>(&6?i$JK|FUtp@=TdL15x${>*7wjD!kcD?s}rqVT| zSQ2~I`xBguu`1BtI$6vZ+%k+)kQ0V*yQ9EO1-YT-EyE?ez+r-`Jce~-*t zJsUGpkL9$>+G_3~M-_3M=*$y*Xj!Xl%fZhs^YjoZK2sD_aWUP$^|t*>p@K=Mm1;up zFS|s1>qc5LF^dG*{7CIX^C1atZxQv(yPPJDo4ZeHO~1tiM|j`;5*@NiywHDUeqrN& zWr@F$&590L4>I+(`Kxm5jNpL-Awh+YRu^1ekQ5PxZxfwD4z7{QP^%}tb7vdyp98@7_X zId&fY%vtP=U6i^y!ceYr6Ce^mEyi+li7*%Hlj8f+M)4DZRRv3!z1{P0GK3P?JQ&NX zOCYGd&`-CVYaCL`g_ms?5AikmSZ7?9>+kX>34(S$5w!pZX9~E5@RC+{trwa7p0;_o zyRpATec3a0+U9QUyY9u_rEDwvg{F9WRh3_e!d zYqI@fzRj+@reM=Q64D^Tn1pQb_Ow-$pTJEyDcG=AGLpKY7Y|)}UHKi` z(|`M;8Q3FIG!?3mMIpm1Wu&62`LfMx7)RMCtXo@4;MJtzIQ7wUQEt5juuRPwQoUeA z09Vhq*z0FFPjb`(ar=%%9iK&MWIa$Mt+ zdO*$4KH?c#-BI)JJU*_w6PNq_02P<0)o8A`;Lh>1BP-}j|C#uOgr1BqK_C_sJ?uMfgI_1EkCpYvUdIp# z^)F9C3V{5!Te-)74c%G4PP~6eel&fGu9=~<$;};9YoMiv zygd2WYgry+&OFC~x-S??*$!m)u)gt?!75?5zvBC9KktH$$fc);_M67YI~TkWE?c%T zw~&;yv&uwKLsO97r2O`zzko^OUvuCvx-~l4fB0as&Rog8x4e&760wJ>KgI=(#wVZw zjS>oBDsg793rHlxKYtyD42L zg9kKd@iO(xLMa0-Kjs<|W8WQmX(B7sa;z?IJc7ur51fzVZkAO7XIdbo_r@t_Fg^mU zqGrujGv2tRc=88$6h9~)3p%r}!d2;|iLeB)a|6K6 zFQg$4C@`1f&cXGr7Yk1xqS4)Qq<&{_iIpmT@4IGx@W2c?9Ozvo)4)ffL66@NpTEPtb#@wYNmpe z9^6U5_vM|^1$Aqau@}|uy8m3NJ}IWGXi=@}VndkI)qkqrEVSUyAOiNcz^E*^ zc=;3{n=rH)G}Vf~uo?<%5aNzBy`F(nEWJ=W{giPx*wSu~aZymKy3HUEfGSU-RsY5P zpoeExCbxG6E(Zhgf}YOwYeKeT=9pc!B3Ka^n^3Bboq`-oY6c`HLrFY`#vf6kXtq>r za`agZfnO_{{eKI0^;@T=@VLc{CbqE;t+kc!1LQO9EVaLIYXpUuv%KO2hgJ&B5t5$s zafbl@cA~cCWjgm^@mGUg3#K8p^~v3((qw$lUoX#Yc>Os()1VMaL2qpy@4CJL=k~cV zX1aIVE~e)uVFdeY#{jMLgCVva>eBmXFt{9Ie znHIlP+TnN?%gGa>lmHNuAPon1NPRxs#wt5_2f{;!P43>ShlzQeL$ZV?V~1QdPQ1J1 zphkdFBEhh$3^1&`be1))63Fz8wd)+gyxEF1?~R@p)UjZ$=&Gk}f+iDZkz{C%aJVB3m-APx|Av@{Jb%Q!zj54F1gH zVC!O-+K3Agz_CFgH6{_`;9$rBG~xf%`e}h|NjuH6xNzkx!{9mf#N}lN)uR+|w3wBS zX>|3Qp2{e*6^7EQ($FY}#tprG=Vl_(B_yZo`K8Gflk_p98Bn>5<~D2uLn(a{GyKS~ zngFQe4f)W*8yG*ENM)pMKA(5TjdbHCyZf7}>d#%ps6-~XqyMHZNStSIA(n7YTu6DB z{20_2=r|8Byp5%YFhqOk5M?$!yp$OnyuX}9gi;z}0c_xy`Nzr{*IT3m-u}k`pz;T<&9qNDyx=%)29}g|wWGm&yOiL2ay*O>4-XKW5K683 zp3rSRv%6kVrkGbU?Li(``gqzyVa0`k9eqRxV$m|7`Ycf}1-A5tnj+?gn#p@q#EVh( z&B5{7O)%`<`bKAPa8Ue7-w~?WC5XcqCGVV;UV^k(9v^BaIVy=fH}N)gCgvY)EG{Ob zEM8yN^>X^glp~l{dLBa)hY_{IPs8oOPn}-VEqpi`<&r(E|Aq>32b3Rx&+7Z}3K9kVtDg(8Qof?SLq1FpSBlz=#|D&wR5x6$x7NFRR`w~+2 zx+`Qw9}k33lIax^Jab+l>J$otKfqjrDAZ#xK}Cx;3E}qZuKrPpiJ52mfuGl(Ai`HEt?uA@^b)-|AB(eFO{cCgIG{6wAGH$L0#vTVd&_z+dhI%$1|J{#ugKl;ETi zr{~oUj%z0vI;i#1JO*aOA@`OtE+zb$eCbaxeJF>Nro8PmaWd>psChCElQlxhtG5rr z>O-QH&n*KFMQg+dwKG3ngW?ZJoJ!jDq{7aL%Y)?Mm2#ooxa`?K4jS@OLYWA;t+*R? z8LEFg#E&mi)W-`hQzHnz3=5&HC3tf?oX05jKD5lA- zW&eemHUwH7UNyF%UtXuB`TPM?QlIE2 zs4Pz1=UG|wnnJ31HQ$eYp95J!!EMpsmesc>0PF$b9K>wzD0b*l`ZlNr)tcJT_Qbo_ z?{~|STD(&I_z6H+0*$lq`eTARKnbEqD(T%9pIxqr0HdzA>rveuH!7%WHjL?!QNL$)MLY>!P@=pQc4V>_kBYT22+}`ZpTAL~DRL{E5pP z7FMDNto0vir2ZG4ljywyw_>_`(kk5=m6$HTEKBTeH~09 zZ&uLo`vOwNJ5CI9(@#T10`320PRHLF<*hnMZA}Mis}+6UvDuP(961z-Tz5_Y{m;u; zmz_z|o>kGqH&6UKi9O7g#cWsZ$j6KzltISPn7)!lsHIue#N@Bg4`$-QNVSS6s1vh% zs5ZiU5IY_4l{9NZ|5YsQngWuW37Kn6xM^Z*^ey$_w-R~AGcT2LvaIkfVu)^q)+6-e zHs`c^@~4O!<^!`JFd?$W-Io5a-S8APNo?KvBXM7puUmzlgo}FYg zHmx2#F8(Q(u#G57)e|F7CigU~pE@0pU2~LD<>##VV6*2z0!8JBLR`-O_T4swET?f+ z6=};Odk^or>asiTsp?r5#J8j3qRz^a+p<}kk3+Bp^w0J%>F9ehM%Li?p8jEF^n(oS|+zn`6W8y&J)3;m2#`<$F z;cRXdFa;k+4YgW&ieGtLBR&lubxmxJh3^E?Q+CMQxM+QLFqWCN& zo(`D8+~ynMc@BXE`|(><&w}?$<7Vy_i9k`To)*PRSKGIK>QQlhT26S`=G@zJ0`fAv z*`3I<_uQamUjYyiQEZ+a9||91sQKTfE>f>&E_9~$ZsN~&fB^S`Oapia>0TwCk0B*m zZ6#>3;;TM8HD@o4a|-43hSI)RzCUj;$TtEZ7M>98*>7EZdzeI&a?0YI9Jo|bTR*@)vI^MjY2h_$S(pxPHXKHkWP*!XuLQhjbQozm4`y>D$zt&qSK4ze_NUTBD> zf5yu4ZwWmI`}ncYqt}4e{^x~Uoba>7(J6e&)7jFN8_4d1n5g}N($f<_xR`hv;+-7? z_}Q7#?CMTI|2j^pRr&`%kPh;)0v}d~wmYb`)y`?%s890s39KuBI&_*lQBm6ha=4W( zz5))n3kf#|Gv29!5~PQCq;oC+UHLU8XjClga`#JF31cbbv8$yY&@T3yivm1O_K1Dt z32H#ELKgI%fu6CFYE&IZkWBU;F+*pbaw-0xa3wS`@JwQCh)z6{XmZ!G51+C=ZNBK# z%)KdkMSnuLab6SBp~%HWjRljH+8Y;Y1bKFr0S~*s=m`XDRJ(nN>d*nh7B#I^K4Ey>BGf;}19Dh$of9}D(UVe%rZGroNQbRqW|Wf2m{v>2er}x06haOn`6aC2eP)Yi3RPp zh}^IE=Rl@S+XnT`(Y5U|_9>}742XKr?*h;=<8pahA@cRd=wIk!AS+ZTRJn2vQUGpr zX;pU^1hyeYN-3N^<9Aa>8h%m7TzivO{5u44P8FdJrk9Dk0I_r-J50+%vD(Wqv5ybn z-@YJsZTo0~YWoP(q9W^8tnA?iyE>q~tiF2zXGYeurf-OPjLUH4GciecZ{4YSc%Zr+ zH*EHx3K#%##EDr3DChtBPl_H^9ni+^w4RrK>wRA*L@A26x;uj-WtpXI{gk+;&(14X zpyt;kbbu)kP!U>7e-o3%LDtA#mtaTB>u8>ux$?XXZy7P~k*r|_)UXHP9<6)U@IWCN zxXyeT_$jrHDpft5AaiHpT1s%jpSX%Kj3uLK=X!?VISy{UYiReRX`i>#B;_Nx&h}p# znyW(FUSeN*K4v(z zWK@l)`W(!9Txap826JLKBJJ@3#r zNQ2&{*YqrQ-_-idsDMN|1mw>U`QEii17_*HInkq~kM8VCYaA7j&r4Y=OJY7R?#tOt zku71ZBX&AyKt++H;Ge0TD&(=_H+=qUO62-6vxVMkhZ?z@H8S)h#S_%DL8`Dmen2Ek zZ3}PSy4gSSB4{fh?0EmGe#qqZ*{&7fPJo#ppSm+@*C(w6&rZ01`c&onw)n(yfk_#- zNC}53Ei2ptp7$POG)IMFDbYCPEfRz88SxjW*2P?P&D$|Cih8PU>-^wW@j4C2QKKwzy#G2 zbsWR+2@)&pYKWlu{1jw=hxlmh6EEk^m|%(WFGq2mUw@TKI!r;}n@-_VH> zc?g*XwUVp5qkl>ouB#p#-oxoj?VriyuLavVSw_U`rj+(73VVc`o?ZxwtFpXrnfs-; z{f|cH-ZKFd)uVIIA*Dv#fuUDB;X+9rDy8L>BAR#moKH6xty-D79>@6FAso;54Ckk; zaGbF4GeNb*g$9bjSt?FI7pMA@KqU2TRH=J*|X*C&l>qW`?`)hG5f*C_ZKaN(wCoV-^h&|ph-T9 z2KG60&pe-+I2P0D=#Wle3u9hOfL}xT>IJzXNnI{dYyM&l5#uf-ML$hoTN?pNTY%{e z3mpdL=&Kl;34SfncidDH_c!#i;Ltk>FwswLx@pQaF~{S^)3W{BGhTn*{6{U>@ctUe zZ#YlE28w27?e(|D&jpU-gRyIC6=K#KJ8Yb~bZ*+Ju7pOB1 zL+Qwp0Sw2qQW_RgJ4_=DElV9}2R^3`7$&u@gk>cT4@iu041uA4p}09CQ6i%H+WEol zsKv&7$uH9e4g4LFXktrbP{>#4)t8qHl?b>nd9s(;4ev8AEQ+kYTb%7Sp6jm@ zT{Bn;YTTm)qHLPmKyr3F+%B2sXF)!HqPOzu_h058UnadCa9w`viB}W8WA4EG9Ua0q z!Ar)jP;Q1wx-zr+iQ`of<$jx>R6Q7tg9(90zb;DsZm5u(UQ>)qA-f?-^5od9FaFNk z)2W|u_NPhVyg=|yL$JKPqzT-MWFp*C~%enl!sUR*{`PYPFtY$Di% zObZ-Bc#f&R&f<4#XK)aYlW;Gl=UT*xelv|>vX!%P;pZ^rx7nsLlm~W3^ ziP0Xi>YJ9BneniWy@&*}ne)imZZ9$6&C}mQ>Jl-x$&OwYFgh>SYtnE@Jh?0KJiU(MSElx zpKHNoSKQnC>^aV^!#^=y!6Q`(0na@jv^bJzVJ>87MI1tXjf#$<(p;F z{GA+#+LM>^G_>EQ#4QD8LdPEf*tXJ zF}q0;9bEP#_z3l+peMX6VUuv2tpcZ_#j!w;#f>N2>BprCwG{D za~`qp8MQFW%0B9uXA$YF@Os8g0r*WZP2wN))LKOzjZ zT+Z3l)it*N=1!+hTpOydYP87EtFEWNOXMr z=K_M_d{36@ow|~@sp@6I&J6e7m>+b$=@1W5DY-h^o(c}Y%N+tVpYxTfZd>7GFXbDKFxy4hdv<)=I20(nAE?HI(keW+it7?S z&V^^Hak;_ATy&+V1qW^Llx07htX0(%_Y1U5kJwWY=tVtVqw_%Dzz!+rE@&q(%v|cA zLOyF^CEsuHa3(b*bLv7v6Qlv^`AUU{M{~egpO-F8)BdUcbbKR+mO2svp+5CE8->pA_BEa>{YwL_wUGi3f5zTMLGzmXy<|T{ujFpb<+Yw z@Lr7s@_iTFz-r-4nE643JfJ2+;0?nMCk75)5dlG4(Ow)O>JJ#)OXD-#HEq zs?c{r`O<(;qyOBu5EpzLHcp}KOMCW_pHZkzCjm>)Mag|$TpiDq$ldzbcV6!iIyC9& z)~cfLAoLEg(fG#@HZlf%E>osn2le>*(JuYK3fr98i#N@h2PUv&?e1b4hU0lg{;X_{ zPUFmb*SML2T?WcuTJW8}r|{Ny^&0t=Q(U@*)u>}cbxlp%5%N@j=f)8Myii{Gr$NZn zwT}RqD1G2t&d&*q!0s4^S~i(Or9L-t>ROUQ-=(}H;b^9!Wg?3F;fhlC4dtBx7KHJ^ zeq$-hp6P?~=`y4^_^pMHyUN5?Q<3Pyr)}=Y+hb?YDEOdhV?n_9p@^w|W>Wdyr?&HY zM(Dz657|}hv({s$Ky!R(65*pH3E%i9CGV=?vm3?x3GvtR{X8jOzi>_sntKAqU zc&X#jwdz~CX9_-9TA1dyV)9>~B2pytQO-#nx)o2(R07@^ytH~1Iw}jUlmv^Q?qj}g z^`xxxTLSg5*lQ-CWg=IJ5};OlP*X|pM44|%3lj`0y`+7APWhuWXJe;t&5v3&5_n>C z(OINV9~Glkhj*F}N%z<9Qjf6`>E1(6zdCnSGMm~NcLh?FUer^M0Luzs(Tw(7cAZaO zkQ}FKCxnLZriVFLbrsbCV!CY-Gst{vf^_-&=BBwPrB^LG-}j-}J?IUb>_qzCr-snb z?W`e(0A~t&e<@}_v8yKdrKfMzeadR*h(?Zp^N@res<(uhIBZ~CbH9P_QOqaeV?NgU zU8_MZzd?b6lazTA=h%WbGWy@6^E>4g^K!)Gm|Qj$Sv^2*g9*e!i`4MC0PblU8TNL4 z()qy3sBP+E&px50$*5E4Gzy=^SkBZ0tVf^03kH(XSJ@`|i2Gi3!9VX_H6PFMA$qXN z@^!V&)j&0t%TiyKh%fIIC`K#~|NOpBUIGy19j*M|jb9%a#|Oy^XV(S&h|^&n2^HNn znRs@+kwvoHjE`Nd_6z~T&0CONPl1yP_`UnYwmOxmj6$M+YLD#jdVMKuy`c4?xEDz= z?D(h3VF&c`OFriG^oYhps<6OdjBr?LZ>iz=B97{L)ZPQ;hbIQ5%h8u^uIC~Io+*LnTDJdAt#En+;j4c9 zp@vC#+8kBsLQg39r1ZwA3W?OAB(6C`SP=3M0Vv5O<*XG$=vVVb_1c}dSU zxaof_Q67tyUyefj2-oWm22Org!N~qEPu4xEz3|fnm3uqzFF621u?(gDK4%!U0sMtgz+*#{BzJ{DHz<-sE$zs(DEP%Hf&oX320YoV2HS@-ri z_gi;C*%(zSrJX4Q_s^W9;BT+i44$8MQ!LE{o;vjxd1iqSwdet#w0G37sZgLD z&u>=s6Q8v%R(P-Q zAV=z~hF0IrKq)Sb=-CMMu<+%tWN;1q3B1MA0~#JNg|mci+#){}j!152|ZRLpRvSSv_gy zZy7o|+153k%nmy~O}clbY!zHS^?>hX#`w$QY&(=@XK+-A6(U+U^hHE@@9!)JV4w;4 zn!FOVeJ2e!x#vSi#a<{#+=PY?9llR8j(d&paOZVO^9xq;2hJ@fM1a&|Ok?+Y!NZPE z_LpIa)8%z%#klqSX{NAq`=*)LREU)0_|O5rC~$ts8tQJGc&~jze4CG@HnLSil9g1r z1mj##Uke~p{#LX1qRN}9Tjav1jH%r5iP6_#;GLPKrDppj`n_rYgHk#9mh4fj8z|lp z%b6XcI&`%8rGoREKi^P7zql}G+Xo{Agn6VhttFR*%#XLUya)&W#=!r>2_Q zh^{NX08AXmv({yI=}vEoz{>Q%khL>##yrPV6Tq2qIyv{W*HL&wI!*g(aM2b-k_;Ug zg2eH!`lr=^p0S1};ID3p4hH-Z#zZ-`9i3IQC{Zq{Oh0z<$z@K>Z;WY_;UPxt(~@FcoAbcZhXi+qO?3^?kcug zDb{C>a02XQ+4eTyudNc@ZMQyYeBi;hC65Q$1{=53KfF>*a8OEf)J#vBcfTzmBm_pk zcLqW%^>@>f4)*wfUE(VM9BFbgiH6+FSKZZ>_xsiQPuI*;-TfqYa*-^1GazVPt5HVJ z?HH%K6%G^B;hke^Z(9o=a@Ve zlHq3E(9xD@ldfl8jb}HCVutPjFXm%&-cVH`z5_#Icv@;-ex!YGoXtc%*UDh7(yYIR zp=9~np_*7DAU}+8J+%|kE{3sc`j6=ZFPdy|y223+m~{?ev=yn|r|`jH8L~2DgCa=U z%SM%yIqSbS@4c~ctTKHH-B*s09h*^|eEO-`(w* zD7=7=y({jhT#v2`{rJ_wlP-~aFtXMsy8ef(qwFYo-BH|DKDFzC0D|K{>->?i;BTjhs^?r}YkcYN%8LW|v5@QVwOz z_$|nkJ6pyN`igsF$XIk=)75*7BTrkk#PTA72j0dFPLww$p*cq6$E|wXCP)}26tkyk zk)HH8B8INOp-^Or7T?hT@(DmHN^&zLHwIVu2WeTf;B#$`q zsU9bfdGj{Q8XBrDrVu{)-mA?trJ|(TEx(+Wme&&;`lVv>)CWo#T=pp=Luav~$87)E z@e6$iXPOxhZw!gk2`sTCxe02~Qr}4)CopobJEMS(dyyqhX{`_>BCZ{07pwsu{$ zH0Zg$qr$_hy0;|HKets}&&;5S(nWL7=zvhN zKO+9w(@UOu)I&be=WU-PJGKAicxU2(6* ztPTAaQ{u->1+VgBuO1XKj4rnh;y?K~-?q+W^X9JF`UGy7L(IwBW)F$>c%Tdn{K{VY=8aA?MR1gmzDyRfd1!ASZdds8+kAz3 z(0T=*2j_60i)8*pMT$Ac>d(#>D94l8m-wb?xL^42BFZMP!R7_bq@Lu=>vp&r1(BGB zW4?uccR-B~o33CheM|C3lI!yeHT;}(wUy$(Ug>At7N-3$%>F{zALhr$2A|3Y*44{W z5*F@rHb#|Fr-T6zpot|x{hjp4-6Ac&YmIvk?fh~?B{n*wTu3EpJF9QTuLvirE{lS{ z=Q0`UW7GyEHojKU^Xixeyx7lo_MsdbDzL$U3}nY`C;H+z&c|_TPgQE5ciK%BdqgL- zn}jOw8CEz`ryWBjKL}E;MHXi7?yQyhd;9AJ+OGI<(0#4`tl1w#d$tnd+*xTFbTA?_ z@#3D|_xUz~rA_tjY;%KA)@*9sX<9|k9^Is4+9IET4BLcBlFGrs{|SS3?nYPGq~dn} zB#x{2kh#)Wg}>dM6z=7i>b@U-=R&Mmj5$C)EAE{f)ZNo{p@InI$!I~3j6B|*UJLkz z9d#vLXd~H;0NtSEV?%5iQ(SXxnx=J$Szlr6+oJTZNl4bcn)$1i7B-u@laQK6H@^MpVxvYj56COOl-N)zLMpszLH7tw`nnXuu9jt8h zj1ASBZs#X`hQ$I0KMNPUswyTm#X(%J4+tPD5~TFkbPUM$I*jU&fgl3qM|n=A`{x~5%G5S^b0SqZ>LUq52Eg>;k0coH#|@7V7m%4e0(0uRH3XcXd&VKY@)d9 zf?0PFo{I%U@Q>2!yBXK_4LK@#Z0(25fFuMNp@^)ZbT(^uqYX)V&4SK#rXQ6Rv8$44 zxjktX4E(l^)hb1y_sAnvVpV@8d~o9jaenaP&?=B4_1dL4#aWwSvv5&qoMVTh))I++ zA84Vdz~egANZMG#>;oJ#@56aiv9h<+=>ky_zRIHGA)|_09@bYY9f-_*^>TY>iM?72 zE(R0xfo*a^f80xyVW2V@ry5u7ut@ibX*0&e`KtT1&|hM(u^>;4D zH9vS}y=}JjMceX~D)&OIUW2QN)uU8%ZI!^&+$xO|qqv;6W^4^p?|83Q^oj%*j=q@0 z2C;%LyfQoDzAMASgKV|SJF@!l&kI8}XcjmR_v+lvuhfi-K-+1bPNPc{P^|)6umFYG zM_~9!7=M#e`}C-`vl{*&L^xj5IxYkm_zsoo%%i*>8R9MYxmv7l{nYt_yTJyhKJNrx z%5O@XZ*bW{m-^ya^-P1VXw5EOrYLoF7Q)=n(;jTK4lWoYK zbWsc|d<0(2tP1oY0J%@F- z&QJR~1#$nj-DGk^JzZia()X8jby#=KiAG|Rt%~khSg&o!BtiKCHT#;}8!wKp zK1)PC%91$ytZ;+>^v*TiN^6t*FcrD?%dWNew}#N=CQg~~3}%ngWeqN>cJe-P6iFTU zfmlA<0EbP6@J2}>V4<9vN^x|P4cFtX06#6&562as&HRQH>FnqERRdhHh#XHir*GVA zd%_i<2bHpKZ4CBw}Zo!sL8+|)>1)fA))o1T)qErlm#(WJoEjL{ z1i{RC@MkM(?bjWF`IxcN6qy}4ZFWC|+O3pc^)jN&6erJ~f_%m6I-Bsq;Nqyv_%e}K zhQl3@A*p3o>TxdVbAZMm6T|L!y33UkbpPoKrUEn>O_`>myLq3OLKFzmT)q_r$$aPE zsM#3zt1WQ2apQ_Pw;T^T3(H5Ckt`9(O+u1)@45P&vZt#XKQhsg)O=KK zu1rnmF6WB4ZB`#F?PPX0BoYY*0{4W89yszK6qp0s3PC zZ;8lbTi<(>IJY0ZWYhlY2ss#}aL3^7zF4|)*ZIC`?c!0=!-cIJJl<}o$qRc@Mf+cC zkl}Ftv^3hsIk3h`T{o&oavDORfXuFYwGPf|t5-5jqoynm20~5+?Ck^zT8nsRcaC2a zO?;Bx0QlzFN&*&Rz zXuv^d*xFK`Sao!v#^ zCA!*{rAwVn7hhlN%?U9V5~4siC!MB_e61iU&Kb1)y2Q$%_?J>~7jB`_tuNZz-#Uelp6~rouJ$4#I{5=a4$DprS9Ia@ma-ofEt($u24Snu9tX}gQe7OCeuBT)S!+Z z!X?wBoAcf#pWn@)KwO-|#Wm~QhdiO#L>D{JsfRgXDIe5-s0=Zi(4KH``rGa-Dh_oa zq3dVAI*=E|wB^3fOLf^h=XJ69v|y|qSkc>97(3)#duScWlW~it^Y0rooP#u;3bcb7 zC<$2zj$wtbjPb{i#1CoWg)ozFyGF-qaVPzd`~^LshuxS|$F+Iu`IDSOgEF@MiPo_% zYM%`UrKPvRLXVriv)yP8f)S0_oG|Pxna%TKvTUY4op{3PANe|AaeBN1Dapc;^nJY^ zDTqAX^kld?LLs4W|>99wyUqTOy!Foyvrdm*40b1w}H*+sz;N1RB@7>Jy*P_uGZpp z9=`rs`}68AQI;k=n^3`u$hyLx=nERIQWmAZlyWDwZ54jhb%Yx>-Vi*Gm|m}OZyVVs z>qZI^NTeQa4t#soft>b~I$}oWz#H+Z{OO!CDvn-(!)9Q>4yAm;th!P&9=B5Gpc^-~ zl85Y*GkC%gX;qwhlKQBPW#!788_Rl$ey*N>Ui}`;&I;{Mj1NtSRM*CQLd*Mj1 z;)=QaCJuFetiQ@tW=~`%gIC}hw`v{PdwZUuzP#Xx4aiIrY=4!I7F!JoagL!hT6$7kHm{paE=10Gv5S_UAT76 z73E&s3-eETh61H(U&|vIO?SiI>j}_soRpPrHFj{0P^|`gS)ZM-w$Br#5Id%+T<0pM z9}(bq{8_Par~^5C6+@sKX_${Zb+Aai_z~EuO2qULf&;tz%f%8yfZ_3T-1#Ln!&&}Y zMz}VVeP6o_HF+1eDv;+Ve8E}1{`{HxqCqx6aQkxM?)%Ui%rME8rRbgDy+=oZ>S}7a z{P$05{EnZMCqva=-6=a5^Cs7||FIchXfhe)pO7=0LwTo{$n1Hwm$O3Z5Zr?Sr>o)v zq9Kv1S}zCN9{#HS5nptjuiE0#G?GspLokeH`aXgRO>~oKZTrJLY*PK1akD|^rpXxN zp;z!S=u`KxzAnjgepMHLU5?0=cL4{h{mFx*N4dftW995`6|ugX!YL1{*pE4*&9291 zHyS(iWsV9e26AJJO$>t~hO*}HxVI$u;ccTL-kDLpADmLX1I(8+xWpAWlKnLZP*E5%eaJhQ+xlItKx7k zY^uB8coejXjz^~1x(7zLt2e^`Wv;>J`8fKeDm*dvz7Aq|B>M^KK zwYIU(l9ZUrI0j#d_d37gRx`qUEI7E}b#BPkJ~(mM-S?delsxs6hGD=2e?4TSV4kT| z3}&fM@K+cfOZ~iu*42Y|MIF+TcV;s_RL4dS9n6_xwDyCo%I3`FLnfEvJ$Kh@Dvqmj zqY*&}k$@PH=26nF9Gwm*D2%-kt@ReB27^EKCv6 zpv|Oc^{Qd`lX5k^3tD|#>y&tnOA$g@my`l;TX!w^l@i!CcTb;e&D?HNQ}I;%4g$}H z`@)lWTjnc9NAg0m+j0ky2xn|AH$_R(4T7$LK~?WH>R8$uV_5i?G}{sDhS>_KhZlJ% z({y*6m%O-bebut-voLukB`n__z`MI_a*o$WeoUFhCoD=j$95splHbR$Vd~BC1~t<4 z2mvI#eS4UE>J>=kZWy9iY2Wxvs(xqboykYzRhhs?kME@Kp;7fRViH&u^TMC`Ox2VZ zH08azO;F++VLs!3pKXb2)o_>-o8i$;$6A=u@Q3M~)g=brn3f;C%6qHV3!T-{!#R?? z*O#3VGU%p)B2-#laGu4<@3&1yX}Yoex?bZ-hdib54?3}OiwinP^#Hl3=!lBfJyaOC zX}1=FwS}Jrk0#9rU{RVa7TtH@mV6w?xAtWZO{sj*!aS!*$!cq7=xOjF!9aPuYOyOz zP@G-;)V_?OOU=2PT0Hr9k$mEys=a0meau)!>z z&AuDX9mLTF(`|0A;R%ZltF8@h4Zf-Q(KCh^r?g--)J~b?*aM{F6gjFRhCR>USx^y0 zN8?}9)fTeUFJFudte}3jVp_uTLtE_lTia)%ujXHiD~g}_3_V;tI_Lu;VQD%_nLTx} zd+`?B1^ZAPAiCtNLLoYv(ZbDXF$UUM;7?n*;#%&i<$aQ$*fL4}z7@}<)Oi(SlkHW- zNko>hy}bJeBW)P8U0|)oi%eKHxM*6um0FcSaP7HMgNdwQ$|+QPIpY;SXHTy(=@6UB z9a~ZBel2;9!5j1uCw@{96IQ%~!P2+{Y4YS|xdrilOexcPbhmndsibQfH353Rz%Zjq#H!{>e5{o0szX&`sD zkUG>-!I1H)@+mR;z{rSpBA@MID-++4(d$0VXu+-d*9Rm0V#n7HYEsN0U4AIAdx%kHDO>vSYMvT}m@W0DLh zV@N#h4$l$SwJT+W_HnG`J$Vcv8~w~e0yh%vK1-jfN=}@Aiw%ukG>tD9;&rkAk=;X< z#V!`cf-8EJJskoS$9vuRfsiQ{mJlj-oK+@vU@qG=#AwN=b&S!;cCiO%v_2{G|GH-s7mIb?Dlr#;OzJ~#J4CyIMz8c;{}^s+>P`sE=u^KNXIC&N!^;4?!C!s#Ye z<~KccDN`DQV7Z;nV_%7uOEYAEO)3xPX4U>hV>7(Q!_FkKp zO55ji&gdZJ6Ae=yLQ0q`;bD?w!65dK<&XkjN#HkcVxPNd=vPIIUjw zCj9C|Yox{83STYz>o@_oeqVQ?{nLTr1?@zYK{o%LNU^wB3s^ZEDv?aH%pdJ?q@IkIDh=O;KN`N{F36{y~k>glB|+)dq(#?{e+5sz5?W_&xmCA1#8M8G%&)5C&OX{ zBtKQ5t}qln-Vsvauv`KzwX`D1gCLEOjT_M>qT|}nYqKO$;Ky@S$)1lN1|>2UA7eDW zS+5+AZF|P}&?c2kxL9)kCqY2ixq;ZOu?|(=TgDiUNU`nUc*^?2rO>?7pFi?khrMQ? zA|ed=yDov((bN%pr&L7C`HM~PRQZ;1YEk4thI#76IZ<_y=2L-E&s3Ma}p!P(E_p}UWUR7&XoB66W=>OOn+0(DvDZfR#TgSj>VSPtcf{n$( zIvm3L?)CM6eBGCG1^3N(4CLNT3b7;%mz6{u3-0hx+LiRj?nel42hRWK=xUjaez#K} zVQ!2{a}9$)iG>LWrDiP9&DW>zXMfwL0&HxNClQZz)|xDu6Pmp;Ts|E$xJ8UB)cacN`QNP14Zm6w**P`sNrq7PCx=;`%!1Q`>@$4N>1v(K5UC zC^28B>eI9Bhn=tA)+Aal9HnK`DX6T254J8!Xhz1b4zY`65rqg;!T3+gFbpX>7T<13 zbiIzn8;ZP|TifJ)J9!!-5}K^GNe_GlrUWX7yc#Y%bo8eBk0HZ=9wNzx&M^)^(wh1z z_K5FxtR}+KB@pAYTTe?yf4}oZDYLfzlM5pH>mt~k6|ysw`uH0It0jHF9Kq2eJf8Fp zql`hI$@+D|ZRgHhC#&&~52--2lQ9WQh26+0qKlNp>5mEFP_*HddtjN&BHe~I$MJ*Q zfG8jVh9op-TQ)qt)MzN>%;o9@^3%}O_<}vO<7TrocXx^N5q(yuq_0zgk}oe^T(uc``>C!RKyBzJ`>w|qf*K3qUAv~aJM&GDP~xSAdby~iGBX(rYz@lrB8j2=sb)7+dn zO>BOx0P(o!q=F_im{UYw&a1I|*C?}ETwr}zV@Hd|7WZ@)v!gAqg zRh}&MNE8|&?8k1c6W_;t+ZKD|F3`zh<$Lfk#2BK6=Gq!-WRLp`v*u5yxP^7Tu#8tZ zAstMf;tn&oICb!7y+ZDP5pXBe8A>R{EYUO48RKk4J(u;~cp?S`A1j)yXH zLjy-q2=N2(AkH5|+Zelr~f3y}}{DHe%p{jMBxra8!$Cx-3o?WSXz77p;Zs^$3a=2O|pD!q* zTG;zBC*wS6V50pO<2RYRzltzPZFRy-_+BV_WPONHFd4^iRbkEXOw0>J{H6Y zjjpK|iu63|*NNGs5g9;ch}{-S42N~1GuIRONZ}PI_Z>q5%Os>Y^V_t)~Mc=*2>-c7NgGf!Z6c-LFumg>Z;gRv5UJhu*SPH zP_*-~Bgr4TgaIFM;**Lm{8|RCwzQa?Wt5y$?2~D-+$O%-rD!x2C(;d7QjjsG$P{Bs`4j-EjoNdJ_V!E&&d;f+|1op&-3mKw}tb}DPJeo zD!I!Dt%a+}b}_}YAIq4<H*m5F_lHYH)+I29~tQk^9B z+>Fk zS#s{&e5;0q!H3Ulw8?|1D0fG$&rgf5jH>Uidt0Unb z$|T3Onz}K`d^3R2C)>2kH>mksFX*E5e)`?F(c?evnSEoms{UlCgg+Le$V&0c*oK0k z0qBx$$HbV5cHxBU4-gmVr!hOwuw`0w4ZOMwD~+z64`t#augqQ--0Ug2wTG66uZ2c& zAZ?}+q}n$~zsqcMgWwF0sr$oix~;)?*44XR3ZtqdkT`I0U)SZmlg=IC?-vP7$AMkQ zi`QP~{@1zB9w2y8C`!U|I|K&BRPuva7_i zac6)Pn_yIZw+BpNI}Ac_U7X}|VvvUQlge6G%ej}M=DGRtcN!R}pG<`qo#&@)Ki9Co zo%CL2dV4$x&fvooE2RdD{jkKE2u#Xgh)bYOV*ktE?(F5+0xE@etOZcIde z^$Hga0@*8|DlOaHcBxVYO58J(1_|)}ZmkH-MYFk=(jT2GhD6^42lm)p95}UpE=Qgk zav@KTgpg1Kz#J-aU_9A|^!b7^heokuHTuIa>Ow`k>%t5S!LBp2?O%$a$ml%$1J$-1 zLjaI3+?kW%bTx2#~OcxqG@tLNNiR#mSC1|cCW8bTYm z>QhOzGU(7p>S&{SPR@MN6kAC+vqAF=Q)x&*8b*ijHg92f+s~6%^BdC{yxen?! zA7ii8@sk_wIk61cDDkhYmfhZ$d)mmMfh|;U6_Z6>xZ1^7jiE!OUFPhQo3RVFM?d`j zJ?{)l+`$r5%?1Nva7ugL^`nnPE2 z)wD20VZH?IiPdz_%N#q}YpXY0S34C=x1B>0#>gnfK(Q|haO_1+)c&A8V=S)ibRwQ{ z(u3$;>yd-{_*l8}+wKq2jKRE8=fEnt`W|*+nl+3@R6XK9sVAefFC?^0WH8BmC~)m=(#nzoI7}@Da9}BHSBv=&c$%rHQyc36@8G>pyrB9 zO9kqi*<4==Wp5ZwXX7WL5F+)yiXLf)&k&++HC50Rj3DDLHz_l^OxzB@tt zJsl>;B(jN@WC9?xAm1xlhfmUK>jp4~qG(X_u8b&=)Qnt!e0*pDH8<|zt6cZ9mUgS^ z&C&NypYn9WVY_#51FmD3*T=mTl;~)I1=2ZB5pgqz+HMgy{49}*&$Z;hEA>I82^MPQW1px(p##lOQ#emR;R-FdXUAJhudz zR;6RFW3SLQW?5e4-`}M`;{-l}E$3ZJpA>XqDzzc2xh8VH=V-7Ouk3!lW2yGnQ!wyJ z^E$_rUX;S-du;TI1AeqAN5Z49dIe?pr>vZnE(v%U?(OyLS;o|lB$ST!5jP6L#3FeW z)tzRIR4clp)lN0X^fau@w7R97SH284z!1B`@G1M^gcfb^8bxgA$&buE2C)z4m~S&K zl1Nf{gm718Q=GC7g{r95ZsR}*u)-No^`-1_;zQp*DdllK$jr5ncDe5=Rv<1o)W)Yy(vx>(aJ0dsqKshcqmZ(!U3R26_-QJ zAHrg^u#aMI!P)fpI_sfNOul|4a?~~2c#)UvuCEax!F88>IRuT3VyQytzUA6gYL-d{K zFHmLnP^E4FYdXO0NA=5)!aQHxekpds5_2we3zR034j_w%(1=W4-Q~cVZL@Cl1 zfWCdn9@hXigbj4QDGI|PR4##rF|9E-R4nY2^{`?Bd8P&?!yhk_NmsPcPJ z+l6Lxt>j*L&ADJ=H@vzpikRmzt&aG%{B6e!)ht?Id$A4JU0>%%y1Hng?Z5LwRYW>CHWreT0 zp3G-vh>h{gXgMTV>*1wfdR+R4P!llF0G?OlzE) zZ+6v88wa4b0Am!s$BH$hz;%aAE2X8itkP3wk&Crfnx+RmG)}X9;2>U|bSWCvMF#`L z(81ZTBugwQwOsW}$HOLlG?Ob>%66hj?}Hx-OT%PnkTve@-p+Ek?8QP1`5GdKLS|~b zx|RtjwOm{QEvV5jEZHJ2^Nz*5DHL)^X34;0Fq3@G2i4dlgrP_w_yW3htI;)-41ym9 zi^ME>cDG-04%yU9n{Bg-^Rh}*M>UZ1j0wTK(fp|oNF(fIgbnfwy)I>yegAVHoT3nG zk>H~LIMBirNp9#N_;PVAaZV`J#k=oK&3%Kz+9Hwk{z`-DtJx+;@o3Ru>Ouxbg(`3!9&Az@+YA5@D@5NiQfCG=kyRr z06KPF0sWvB#2g=0khO{hT;!h_xPz*?*j1cSAGzXATJE5sVbCYsLqk~oF^(XMQ3zQv z?Tkl&X(GwwCU-UzdxVCt3tKVHN;z)Vct$ zD*@emiu#wK;PCr^0p0*bKarDgvb=}vz4}Yj{&zkaOF$Pd$efNrIB5e(dQH*h1BKv! z-q!@@RrRe+1tnR2AGJskfKz`v9o19ia`wMJs!(gcq2Uge_{UE$eK5^h$kqJIc5c6o zhPVNsP*7B&{`>H#-`9WwXQU}+dD%Pi_t6S~LB#P@ObV))?C*2@6QlFb>i;*SBT5Zn z&08BF3rJ?a{($en+|hVVfbPUZ3Bw3M;tUQ~EHBW#-w7H@6#GwF{v z!R&`9Fu;F3LUpeB13sUg!7!xq*?fVnVoQeosAXZH_b)>EYe{*eU~gtxmZX1d0PLp= zMQuaT^(YPY_sNX1K>QJFM zi1xp^_@vV52Vmq#waYhH!NFIA?QTrBB-_oziooh6)fn!yLQ$RF@7MDcEK3@gb$fB^uyM+i1dKyUEkPcXq?!zfN8{-W$ZaD@bTqj2CV zG3P%-{(^(>-Qyk{08yYlcmeRH63|lqJ3CXE6o=*#owHasu493xfUCc)5Dr9AHb&yV z_`ih*-i1ScLjTK%KJjA_d5|kERiS;#B#>}dWQ8U+M_ zW3hZqR*2G3en0zv%&Gd40eWr){+x5q{x@RLlYqyT8IlXZmw!_MM3@Pn>3#V7+gsU? z$c(yMg7At&U}&LJg#SJ=Y9cLFU>oqh>H8llgTV~JIuH3vcJY8-!$mOI{58ww-;ERi zVdWSeOZi_mViXAu+Q*paF!r&Y&{hrv^6x7EwLnZ2gxqNqRN|(2jE(jgkNiP`$v?39 zO_lf;^-$kd02_YHNCe8H{s%5601N7?K`QLL%rJ(pI{V!BUq(7kVX$bh}fr&hD z$^ALjClDwhmGbcK*1rD&a1%v!{@0fO=57BB=myUHQ}k={fBx~mxn}$T2~0)OijTaO zaGTv2U9|5^m-siRlUd-9y~oP0)a8yZ$WAWaN02qClkFCL`7 z1>3rf(>(s))o;B6aOIQSXKe16_m6M(%t{uv=}3x4i{RaL!h+S z(4K?iGOD%UKky<2nwV6twA2;wR)83$vsXh}<^K*F%t4STM0AQ`dYeQ*qx$!)%Wt2+ zYE*zi_~&%!fc?@y?q`So_wm2{xBr0S@?dBnV5{harZp%6|6_O@NY|f_g6IEVhMtr1 zC>H6d&q4k*ybuE+u5bmbJGj;W+@uF*DDz^m=-;WQZnSt+E|=9I(34p)u@)UE0HY{+ zLgoM8^}!@jR|mR?UC=P&4*&#&1B4l2B9H{VFIh1U=Sq0k_;CMu24RoJk+B{@kdL|> z{r(<;2rMOntAvCRgNbA9<=vA%focuJ$m3ePX%wo6(Mh>I?|vB)bg6M^aUeS1&ZB+w z^1^eBSX6Go|9w={BtfcTN^=%G>=g>GjaQ_Dt{s({9890-*NFsJr_s-u( zqj3Oh^dc#_l7o@R=VYxaxy~4Kwrta|6DdU!8+NG8#f*N)i+>J`ReHoT83&6+&wLNh z?|f&xSp2bPS@C&{QN*?J|FcT;f|l^(hzu7x<&42Q2)5(a@@03|e{oC75k;1aLqi9A z58DQhZ}v+4zQe5ofYF;jB4Yo`?H;3czL)*$|AL{XCIGI7iCp{NQY+vExYAj(#q(c9 zX&n;)4ioI!`zYB!Do+!~+7lpj?H@#k<)9>lh%X-%u!j^qRF%2{F0}ug`woyRQIS-e z|K$z{I&eH<#7v3*Fmh7$^q2GAp{?D;sJG?74u!t8sQhzsP`rnY=NpF7K5}OMYq4T+9DL9zx523U&bDV~lh_a5E@1p#hsN<)2MWkT4Ch z{#e)LciM!k-9n*PIt|zk?zfKnsP!IT+|AlpPZCGLU)E?<;GSCBnIxk$1mor+F^uMF zT_|7{{^%nEeiDv$Ay{_X@1*!T93ta>$>iagP z`&42i@-ow5MlwJnDQK=o{O0*4yag-=)k{$`?0&cy$}D1tvsOw+zSMxrlyV?>0R|hfP`Zg$ zm(a^^P_kDqFZKNh)aCAdbPDQ}nr@6(mqzWbbu{@nWgvQqwz3iUx^XT1Ip6C?J#|oB zZ)qN*ObC0%zhuCIU>+D)ls96sYgiyCBOlO2EAkcQDv(Jb2@2nXq@pk%oE}|sKD^TF zK@17N=1qAB382BT)u4KZ^lpAJV0H|y<6hYDj28#^RxIp^PK(i3=^XanNJSiFNW7t+ zJmd#6!5JD4P~=R2cLyq^wQpOPRd*SG5RSc8uAV#L@ua$J;$_lBIM+5%xw(L3{EBa> z`3Qo+x8({H&Qo?Hj`>1iagL-V%S)ROurpJod~-fIGE@6ebTQ_6NQF8*W) z{3`0?C&)((gAWXx_4HZ_s~tLt2)ABHS03Bnsz|I zw7TAbU~TpLAPv@f9&%t`Hhq9rby!QTf{5TM}Y^*~$m$rP@#w`%^jIH=O_*~}AeX|;-;Q4gaIT)Zg z+ppQq3cRSKO7RC}-3$Td+fjOBf((q*q%pdT_vT*-^0M8sREJsOp|cppBE^g^UZ3WA zJQZMH?1INLHibOXGb8O!GXXwf^y23qBD{8ng;#^w3ho&M#IA2=GOnUSENWW?=hJX#(JD2hr=!Ht&#B+7i*t}0Axx!_b;DA4Y+%uRr_x4=? zUJx{CE?nHD`M&+-Ft76gNKvbK@x1V>IK`3|EvAB7@q&at9Z!|T(~dSu+kNcQ#|hD! znn-O+)rXeAP%r>=2PwZSPZU8A8lkzY_IkjJb|*yH2$cJ8T*=PPe833sF2O03i803e27cQ5t?-{_sa3_EVSXBUYXbsAwLPze|Me z?iGLPSkW}))|UxZt&i^_{5&HFZwAEb1kS$5FyU{lK)8+tQl`{KF+ZWYMxhKy8mPRN z*40!Jd9xM>si5FWw!_MA6@}H$20&QmX~ZP1A(helTuvm_SITeG5%6C@~_?k93WF9kQZnv9JHnB=EOnF82#V_TZeOq{pu^&-5Ow;Y!GFZc(f zw$)lJfvC%4L>MOTaUBu^20&Z%qC77D`oR5TdL%->&8*|gt!hopYg!HOmTwPXg$CVF zrXj;=eH1J+Z%Zj`5_DebrD!x(8|J#B@!b;G74kR{X(_;=aT|y%+9I_$10HEE>9E*x z9s>rBDc#ILgBxgaI?EVtD*(EOivj050f= zQ->;u%iG~zeFq(?cdUCq7F$`9-gq6ix~R%|jV8>aE6>v2%2Yj-JIhK=g0`DHOIrv} zY3jc?7TUfI&J(5f))#*;170ekfFnaBlNX(s#izs{#Np0L z2>KfQ6MZdN!)F{<+`Qn#JcbdYWHxfsE72F4H$ldZe+1Bv@o^k67YONVL0sK8+`49B zrB|39Tb7iSHg^vQn4`%T%;zKCJks8!WW^F{X)j&%$ubnkGTytvw^xH=r#)4E>|&Z^?qZ?9fE%nd*%{8vPbDLo$(ZZv|dkkIckik z#u#y+Gx7F1a6;Sm@zF2thO|1tEk1|F&1&h6$1Sh$W=G(lMEr~!TK1)p4VrUN3yQzEpQi>3>>N~FSz%nno1d*qi z!4RYP2Z~it+7oYZLSEe6Ontee)*N$$u;{4~Qu%@NAhVO#%txM4Gn<8D-P;UuiEf?p zDJQCv+H!28fG?36!fr#FBGEuA>;PF@-`YH#sa_oj>6kTrdXvL=gBwZp5rLD}YU%3< zK8btO?Eie=)!}Gd@eoFG^`G1Osyox9c~~uMqZ^kG6G1$-=ysna z#+Fr8nu5P~8RgkKNG~bbNQ!%t`FkvK<&Pd(WgM~@j;R6ukx0bFGmLBgLHzo2WQ;I! zqW}CUDy;X9|C_1hhDD*uAJ$!{1QIru*uPbIvG1EfADf$UF|l_9KEw@Te^zjVh`%Fl zJH}T23UDg;GQsX`(qsYW2vKCAdX=76$7~PXV)ko;8j|p+pHEoNUd=G@DjJ<-@hhLl z6e>ogRtkX4gCh6(R4uv@|JH2^&WIUf3D(|-a`>|wL0B1lK5vFZJIS&Q%Vjd{SvFHCA(5ON>0jM(ak zdE+u_{|u%cV^&qe+%jIiaYiObG*%in?yAUkk34FaE}4+-@6kEcQ%N-ZRwh>E4koM& zLr!fBFl%-RekWdMKU$>YbMt|vX2`B$c-v+`m|;dP4cgQF7&Rv z-z5vv{LM4T{+rKlp_-fJ-DUghWy+P=E7VUmTa-WY(5_)q%K7FUmG{LbP#}OBS@hzF z4qUa#eU)eEd^hXp)!_O|OSFSqLr$~-e|F0KlctJzO++bwM60ic(vpjA)Ln0#hIB7i zxjs}Cj#l=|tq#*08QI;`T1tWi}7Hvv%|_e5AXazy6^F;`6Qh; zE7$nvUNmDjXj<(t6=S!y3#X|*;KD@_2KPMxb$bP5_0<4MDm})Dk2lWCNRuSH;=+r; zX{}amIqImF!EY>u_3(Cgw!wR%()iC(4wcW{8zrVsCH((d(~d4{MtNa_Mzy zg!aYh8%8^EaDh83z@+%3<|8m5wFKJhpM#(6s&xIL7EVw*#tkNh9pf~vAiT0kU9&Y?P0%^hZI*Z2j;nU?7Fn|9K zkAO{MQ*G@HJoVP?GNBfv6rfH=|Mfl^x1*p}qAGgCKI=egbtS99=^?881WCBvYFP-1 z1WxPUx4^Ww8fM0Ab+WD`G?XBzw*_GHfcYT?lASG@;}dAvkk zSc@R5^xMG4Lx5>@mV!}?aTW0n1^PIEa=B-qJJ3+`GH7w5jN#Xoepc$%h^yZEi0ij< zd$y46Z-?zPf`5}sXT&+jZe4dez&hQa4juh%Gn4d_C?EkGK`s=pV5+UV9U@`D=oZ4m z0t{vhf}Z{#U{3WR41uu;RUdV__N1RA@CYvrl9ch49u#}UIi2;M)Wp4JzeUqfS?^!OD0 zpbWmkp$gRF$tN~pMoBUAUe>HF@j+iek+0BYlH@zEY)G1p0V(zBBPEt&xKA1t>*M9* zWRHb+3sz}=Uq;kw=gH?IS*%6{OLxt5BB)$d(KU`Z0HDba67=2BvQAp_-V3kFoIl!S~J1j2lr$_vKRlYQls^B~pqcb0TXas)kuW*9e6!m#0#E7j^alzt|x@uG@8~byE zg!Z_i%(L*1K&Sg2C+IqTv1kS#1DGG_t$Ahn^xqR*Dkwm2ca{45JvGOU$hJMYNi3k1paD~SI(WoLp+Bzg6j0R(* z$n~r18}pvXtlfS^Gt17jGviwKr;4;`B*V$@!!j-p=Xu$9T)ka@$}0c;DKZ;@yK6Cl zzuqV>Bv((r{~{Wd?dQXe40^#j5vkI3B`U!4>;JErs0O9#8Gem?wLd{Q_BbrZw z6rwio#~ymx%Q!eoZR16(luo*Xk`4uwU~ZvsIw4*Y5dBc>z<+N8kg*!K?U z+0gmp7O9OkAnat@!YjQ`a(zv%?+5C2c~JRiY6sm0e3K^x+FKu1a}4Z&i9~g}tF89H zsQr=^8Lg2@nj^VL&a*;~nNnkgfu63wLCuur2m2g+gxyn;mS{#OzdZHSTP}0w6Na?H zVrNx#6?s);~EdeHTS6YHD+?6#Fu$qML@WL?Ou^Hxd#nRFKUi-O=t{`K6> z`vzZ0)4>EOK=lnW;aLnTv{SY%#jl;lQQcP)_-n0{Rp3~pj8SV&*nF<6TYSlG^+!13 zEB;A}3=-4~JYcgqcUJ?cfNk4=4!I7WUNPYwnX+q z?Y{i-?NY;=>f4r2o@-WKv+T|6sH}urejE8COmvD;W=%HZG04rTGK}$@Hli3MTBVUG z2bG;B#JHVGC3OiPVQV<8riMIvb9x-nn`*uCopM&lod&!808PRnSYp5ILERFlQ=DHl z*vT4Nx8y&24rz7DV_Q27>*mi8eEyTl7Ur1H^@}fm<;Lb^L_Gdcip<)-zYj2Bz(EJj zr^DG_D=u%c8F>2u4X<*f#!{bmn=*FCFb;1oaENYw@x(84_9~>l`MRO(?jv5-RSAM= zT|=ff9uuL)Ljs&D{2woG@!Yg+Bl}3I-uz0=38;Dhg}<%(4+@R!)B!l5p0zg!jM^zg zV7|L+yMbmSP)2TGtft3kT}$l=_U4^O%!>4l=(IF0L7a`PJ%StmXRXa;&97?%3jw_0 zc^`&0gII7Fu(t<%tVF{Scoe#ztbf%adJphXRN;La^um%ngRP0NaU`F5?B2 z8P7_y-Ex2g^Grg*s=G3@K0iK?H@SJqbzSvu7A7CS&1}X0%5VWiMz{z`z{5x0Pjv@? zn8x{XJseX^D0^o$eO-#EYRP2!yBax7kaJ3N+1g+~`RB*b*tuVr7O|RY#1U1uBSUE} z2B{ojHozw*?>oLh>j(qF;4NMM;&E#jAvCX8`7I7ouCl)KDy3FLL=Y4UR}aj2VP-&D zg{b-KDNXk`FbZf{n)^O*5kXytKOJMAAjnwI8E)LdKvzcG%SxY=z_4Jfn)-!Yu{kR= z8~}a{XFQUdO98mdSQ3sYxc&ws^srm%l5p;yipR?Ek^S3ioIMF*gQ68Q+&!E$d z5XBV=HQc@G(bHGnIqxJ-Z-a8?;|jlt+usK~RP{w)&op%F?6jDYh(o(?#N9alD8)!N z$Dzd>Cmt#tTjzGV3a_5Qdm*oc?_i|-gi{tvPEPkXO=U1i z6;PU-79=0>bK#Dj^O}-+z+A~=5j90YsDW1v&*LyG&D5!_IBL{VKQ4RFwZG|kO2%J& zw*tr;)7b=(KAap2<*T^tlQwUmehY$|SGQ=HF|OQ$&c3k!FHZ_cAR3w2^`t+?DCXxb zGttS;S=mT^mZa%|2scVleSUuNd$}5*P<3pO%*@=dUy-!aF>89CW^{+% zRd(^Pyx6MCDWMX{n``*+5oeQQX|&%IX~8pi$=y9Yy0_Bnp#>76T+DH1YQ1&5qj2R5RVT_Ie<3}u{S%VilZoghIv(z0Q?c0#0?>e_BZ~gpE!Np zoE1zF?%gbj_uSv<7M#w>dF|cycG4G%{h*0-o~}^lw7Mtbiy-F;BtMr*eRw zpB*-TS?9RAy)e%z9mCjW=<<4bMU+NV;S+Xdv3n_v z^NvWBi+4T9;(uSUx5#sP(w&@o_?%q16s`2;j#X;&$?9z)X5>`Ju?!3Pjn_LYSuO71 zl?qK&0|j^lj0Iep6IcA8MFb?dGP198*5}bu7N|_-)4Y z#3^0#ZCDl|w^2geEAqI5W~z%Nn$EmM9&D6Vb#CWnpZg*RwJMgm3re8)9e zNH7P6S9|h!s4Hu?!J-2uuTcQqyo{&wcPj6u%~lm({WWVd4-dJMx!7o=Oa_Jr6%2yk zmzkBYrO0YE>`ipaM=BcfU1_n7m*S5}7xJ?_SssT%FqhH*nl1r<24UDr-#v8cR!N%s z^*BdEZrbTbGX}|r=sYI#Qg|KE5dn(7@3|9?!N5mANk190(^7X~!APgFf}RtIKoi$y znC8*EX-3U_c*$w?$mJ!?#*`@28Uqcb@HkId6&ae}BEc6k?8kg+*AlCk`CR#Nf4%77 zt@zu5hS_7Q5A<{w&JV=HF`kG$Y##pq7@zP!7$@DA%Tcb4R2?k!b^2I=+hHo{p3`$7 zYj}8Pa^};`B}BAo@h+a>WVDc{)RW&b4(sIeV%U1Eaj*L-%TWVa8z;xHRK9ZAhFP*A zEeT>~ePbJJmD1P;R7&ewO_y2f-Dfm*qD?lcxE{BkhyCikyE3Qb1y0RzJZ^MNrNHh% z5laa5DcxWtewzIXVj?aAH9GpCCvokfPvPVF06Se8K{#w5_2)UvWBmL}NQu=>uhs|k z>u~sKvHRnru=f)DJgmSqL|K@c*E(orC;+s=Bp72xH?B|DHBp`UdB2ISZGf7p24bBu z_s+}nrq*`A=IX0k)D-*TRf@A2gI%m5cAu+t)lp2G2JbgA`geXTSAvMAFut0HB zw8ejz%L+CgH$HYhpxF-{e@qiQ!!)Lnr-CgK{L?))@N=1*j! z1=<na=37hB74esjq%3(%v(Xy?@O4B zDSv5nOqKx6grv1ZqeS{%>Fmbm& z;V@;+T<)DIt}7MO( zN(k^;VY-D}9Vi{D_NKXUk&m&HD~0T)AJ@=_yD(|i!N0N&uww)@329+$CazK9DXB>Y zuPt{lc0_QJ)?Cu2;R3y+S{K zvgKE0+E&L57VkU!nxh#CKk!JMDFLQ~2T zbn)kf=mtFWJ&lruy!yxJ=RN#-<+0r^ z0_psBU*sn}A!u%86%#pB3#thAMnkM0?o*Pm zy&ft}upsaPMF3D8cG~@E^D?SGG`AgC(>X{WL>L?*h5Tg}*}-m=HrPvG1whNrmHfa{ zy4myWy7v**jGCk{979LPy*(8g51U+W*H?||PsM&bCEW{_Q8-)#w?`!|-P9L$=#@EsP!A`Wpd_PA7mlvqj5e(FKW%OY2qTzp1Eln#pw{pZY2v zmdu_4CNd@qzQq6>A4#f4EKxOFxYhITWnt%G2hP|*cap!fnF)g^S?(KtMowV%U@=&R zJaGGbP;2Q9p?F1=q1S$YczR#X1(fG;K<^Vw1&m25vT0^yU=d}P@np~fEFg)nWczV8 zBo96;P$e*egzEK{#??GD7@3-;!?ens!K6AfbfM>M6n;Rxg-7drgB8Fu>PHz#~ewX8jwP8>~H6n%cO90L#65jCiuJx>cWZEO_1pvTX)94<-NEXY$*87 zj+U9!^Yq=&vhJl)-4$?;$e53s=i}ZF^@n1oJM&#WgBL>>c+kZ&r~RrR-)I^gP(F|< zuS@vv}e`4&G}QBp6RBFUMTI`~NfioNwG0`(Rr5la*e?T{&W{rw34#M{qI zKPkzXyUX@&ZqYmo&qtTBSSOafPqmld@ZsJ7hnU9ahJnmTR$`ZW(8MfWj!5HLLEG`2 zt9&*mre3DQ6I6xIUXh4C;SKa0&7YY$UW#KmnpLnyMS*UHYkEAL80(`$N$=e|(}E<* zrwa`z#UC8EPTqko+?~Soh~)J6)<%!TE(4lwH@@Yhp^<1qY*n2-hYl9tZOHXH^Lg*g z_#6G!4>H*}s$bfAH6nVuP3GDL(r%vWS~o8Z)YxagQ(7}Ylm5l{Z`qav`@TFVdftw4 z>oi<>^tz2Waz_mL3_by|E*$)#0SZx6or38&;ln4`S1jfShTm*#au(XgyXun=C4{^A zizC#vB6u{0;9d~*@EEZtxfcR2#}}L`LYUp`J4i2I;!zke=GOeWy|sRo z;fJtQ8n+$s+Rdk6=kkgW4RXcN-5h}pwxq;PNELpj^9UOl@9$Q=b?ONEb8CSHtVy$J zB`F7=UmI3Pzg6J_J#1xPC1;5`)!Xy^=MEjy7$2oG;ti0o@Us4o$SFS3Y41nmBikfe zu12^7E^I zM}wOgA8)NHbEHU!_m5IZ<0eZP@KmU!-Dxxa<V4{ayVJSW2AsWysuDH^-L24_)M(ixu>cS(qU?b@)RaT zymKz5h&uwF#Kn+^x+D8#$mlM9l~&nt?InHgn_xmMB4dX~;tKFJh(Sxpz3Z2TQR9?Y z3KCg~M9kcQ^lnHmBu~p9>6=EOH;97wCBr$CAXZVRXBS2hU0>R{H2~+V--H62ZF%k! zQEEMU&yO}JXd(1e<^;hZ@2GR~7FxvygKuk`p1ZF*26m!7Sud^UMtPxO+uNBN4D57XLv}Qi>1w4uIaw!zpg}DyDWMlx z#=ZOicz66?jTX3D8+iY{S@>Y3jy&nS?mv6Pl{9P6J=@P9e+I#90{3k5#6AeL1VFO) z9hlc~;`ro4bA@~fK^`6wb!FvTUOTj1#D1DUdr~4 zuqEZ|@YWbdEoVqUXg0vN*&~tVA+c_-7}NsbbZfR@51hzRl0J|Isnv=G|KThT8p)70FBTgI6V~ne zihQ_NIq)7zR-psuCKp>=488hOQ4rr5?(Sw=OuW;h0jJ1n_O>^q59H zD4VU;d#9n^OtsPT;gu`uI87Wad`7&j24I;o$iuU~(ge3|PnT)aH+QudVtjNRK1fgZ z#FEFvaupkv&%$&3+AEzAJUW5^>0s0r&DNqPJjW#1_QoI{>E zkjXsrE-@%oq9%*G^dhD9i429Qc>23NEy)k2FIBM!4YxPS=^(duC=;I_7ec=jUrvl) zh8eoAnnklbylp~zd*QGdP%{QY9{JGO7UNthm>KL|#I^dG>2~9!ViyeAVS+Sekq(wo z$CCi8c)D5}{eX_z6Q9K+6qPZ^W)-h{Cj1Nq>Il$(oB$V(ac-yQN zhXF1o<%!&)Ee?1U%}4gPmvi7#hF4p&znIl`E5`#OOvvKeZ6SeTf1z5k~Z|t04W2rktvq9&IhPC&7@;sm^Dj z>IZkLf1s(FWy6)0!Z=K+EJ52n);NU(O|D^4*!9d07I@exx2;tH3B?&taG3I2)T}hq zyQpvwjT4PuH4eWxnPPK-<{>W$IT6YEhICcTUDQ*h3TiAU=F$ zeJuqwt-f$0z%_2mF-`1Vdcb@lj1u_m@5Z3hDS87=o8i8?yVrhS6jb_m=+sd!#YLI>HqO$zs zQ!lGAeE4-1RF73pGCk(}Q}Ug~H$K1wyo_MG_MHJgBPU%Q*W#_vVo8g&Eo@!g)#bb} z4qrdr)K@KAnrGB72tjgTDs-12;lya_^t{nn5n|$@AuGkiuMZb^`)mrG@&J>vsAg>3 z`}bqHJa#5!ovkyIX`Y;P#pmSsR%k2vMSTeV23bwf)-!?ng_iMFs&O@CYKl$|2XFTg zEzuP+*X)izXes8rJ4zcS?Sui#?60AATadMoV6G_dH4RbHYpfR zoL8%i&VRg5Q**ib_5f}75 z(`7ovo`y1JCgrL77+xKts_lMfxz)4f8b_RW0#>JKSPfTf{&BiB0EKX<>;nVLz-$8T z{E^0n$5qXXwsr^wdM56@47f9Bm}L_7{3ep;8c!UZ!XQz9-n*pL@Q_EBNQ4)nj_+8f z6J|Wg&St{X3im83H=Q1IxL`pxzEC#!UBJcnA+q*Dj*%X}n?uZGlZfuXtc$6S_|Ij4 za>CVCSbXy-{)g0ie>)tm`M_#H@!x(;LNdk94H81rqkJ#vlJ2oSVSjsT!%7_(5l)5z zTp04dn1d0uO=_$QF>I_?#sDgv78V8u} z2s+&RtOeS29I1}gp7f5E7goLged~o=M;*`;3BV}6Lq1J*ANCpLf>h7WDcTK;Mis5! zOMS{Fk1Z#N$@{irDwq_L67SGf5D1n%Ltlh48=TJ9%o`zB%JM~En1XuprP!s}Z6 zl7crXv#6v6Tkd&^Pb?bQ2oqYom`^$*ES$H=yO4IKda36A4C&wEg9&M%I!n6EdQY0| zi?iZP(`xs&jK_v)mY%s7X{_C)#o?gGMcm!8W&1-QD;oTzWs;APsO8(@DhiX%UO+7ECYvWR$?nY|*r8|I#+yEeb7^z4f z_v~@V^XFqNRV@gQ>u^kOsU5o=+})2j7MjCK*hOSY9nAL-;$_gCq>48uFNFGeyOM0$ zQm5(|H}%9t3i5^?2)$JAmF?dQ#rS+H){H{)y9S(n1jT6*&x!FX(W8I5#hT{DY+Bf!>6d zum2_aAyIkCE^6GLMZ|>u)=`TH#O=@rg%e2LSP7L4Qr4oaEAO|A)uQ%GwX?=O|HKA* zurj-#xxPH`SrSJ(yAz-P8c7&u@2o!HGq z`;8UDwy?O1#b{kWQbE|quuxupt!wBMJ1;aBN?X@I!zDDua*Mi5&@&d~w2VjqpdP6A zVZLP>s|2zu84syGkp5zjhb z&B?U!`9=ETf|LalrImxUA( z?bw$>U!2rp4L!ygRgdh1a58@9tev zU!qz@OAH=o+4ztU{H7-BstPvSJzM3^)s;3q>bWSnSs>>KZ2XY&)R+GDHa!dpvVgPO z_+~PT43MDQ;0KaR7d!CxsY2DLvUD^4MN@%DXJ$&Q8#1|@4>A}yhRNbyD6vO{!*iD5 zlc?dt(mhVC+9O@9;xrqdHr783coeE|KDTW>;fs_)L5r=1+gNB5Z1A#;ub>h^Pa3A zox(8dMigPW&2PE+#b|LqQf|z)l69FwykX==meJ9XG)hnt+=Ni&AMgE)e{6ht%OQAp zdI<0^@Jy68G^KE^jxo#br;oZ;>1UTt9T(l`=@9w6Q8sK++u#Ag46jV4jv;=%2oPka zhRfvO6M3o=fqA;8h~AO((Ocd=!v`3I9zt2fONy+cxfw0dT)d`9WAE8}YR0%v(0!kF zkeO;;-33=86P$UkbfkRn40_XS!oGCt+Y$BOMjKdRQ;S4tiGgbfARxTua{X$MwoGju z7%VlX5}x}02ze%5J&Cx|d(1sgIr~Sh7mIsQn(fF)K-_kH5Rb-!O+dQnRue+4(?{eP3X_`(24xHEvcd*6OFjo z^5_Rhc{mj&iah_2pLNq$Hf&&XM8-tz@#BdsS+0eC`-_7JQ=v~@JNxyUb*v}Vza(LZ z#`tw>fjQKquGhTBo;2NRbLwzTzSgv}H3NX^gV7EG+YyAN1lck=x;JK*INvPbgsZP_ zqN`p`%e4n%L_JB3fd9b3P5S`9nZW6O2d#=SyRHlAJx&)bM0XPZ;++Wubwny{&XVs0 zZV&M(25iNx_?@{WnImg`#hOyZJ0X!&i z4152#r>6tzFYF4U_*b3qD1gI`%=cwc=XIRcS=~aEW!}I|yRp8ROHi0M(h(VLG%{;d z?^S<3to03>BU; zQ}gfMN(uA~a4NsM_s#O2?eyeF!)D%Mj=@KBe1cf9QUAuB!X#VkvcUPCNl~2Gq`~;$ zEx(PO5`#JE+H>$vBONn*i#q}bqOq-}cEyDMI+)Zwg z+uGCDHT~qiBas)<@(CMy_JLzd_!ojR4g*-R!CcYNN>5@#4US!Km$V{y*ckm%z;)vx z$YqH6KkY=(#cPru_O(UMWL6)+-81P;mcQSvh{XJ=hPMoQz%sWTBXvD@aVrt6)UuvJXQjdDOLeYL_H1?~ef*Thp;5K(gQ&4Gtg zz?&5P((=@{Q-WU|KC%i;av#}jot$)9H$qeL>*j45+e-Prn&2&?Q!!qlDQbx59q`R4 z#wlV*6#f}kI6Ar5$FW!?@~`IDI8Do9)3M*EL7hk@GC3SnuXZN9dCW zF&bdJ&qsk5+OiB|0g&UBcdf&GIWk%Me%v*u{`Uqag!estK)Rq(gB*s?)|0>6c2Mfki%!PQYx3lph6?3xSrsw1A{-kZjjm3LQmU2ACv3eVJN^CgiR zVQYx#CAXvp74M=yqNVS6+FUUaibtOg?_3-=xV3YeEFqs)RV*;9`K7io@dVN8(Wyext2s))XYMjizn3Ay-fnsG5P};b$EXAW zMa0W$v~CW_Ig_!)s>3$fKtzp*I>}UNJMz-??o--W;!ECT$osBnMp{rF+>&K@yhDRj zgp+1UE!V(kW`Q^hhrjE^Q%3@pOfQwtpD>2VyuQ_L~{%y z2Q><2h7-&7Y?jS@xSCu%Q9P@=(xA*_bbSccPsqq0f8bXb9FB=ee7_$pmL{!G$o7p3 zEqkQnt>9T#w>fZ`rMI5Ak*Qn0me?kQ74nhMyaB+Yy;yRGqy^C!lvtbJI{ndPEg*V) z7^d>fzuj{u`~5xko%G!{ah*bx-vA;mug^I#f8F?g-VqH<37M!(mzAg(}0>W1eJ}A3hW99;90kA@9?wq;Rfsmt9Te}eS(Q!<|3Y;xy zdG#CSp;{en;Rw~DiT#sI-16y|u~I9JbBD8kTcm-a;xvvgspYj99^+mMu0`(l>Lf#QEYadv5; zn9J6$zA=?R6T&P%K_ z(DbZP*1$Wdw(7~IhH+$vm_@`q3+R=QPO-;+b}Gf1N84|L(hZpsos+iwJc()%EVXl& zOvpc1TV0mPMF77M5I!iKZ8NWHYw5?`cuAeo=qmgs8 zL6vvOa98>U%uxeKH)H&@PC{jDv5Poyn{9VXqOX*VlhO*~)M%%DPk$?-hWUvFogAO> zfIO9=%625LKV9{M^`j9oFb3IF5Vd>qM_VxE>t-8Ovgc4Ir)k4Ne5)11b1JKAdon{) z;C^t7wtCW#nU4x4gwVJUyNp&}uV>ydo?FOTl)fB`*bNfP z-Du@|oq?BHz0m=k96F!&AVPbP~$)=O@OIF;RXg-~K~(})TJ=XlbB2AN_ivPjw& zMM2V)rxYiVk(8;AT7dk+t+#D8b|nE23m;dQ66cI0kk{JZlfB1_N-uwT~ zU+z6Y8(+hza8hg-FFFihQixo16*%9|&?Y%-ZY!PnmrHWzs->mux;RAGQUhz=DsT`L zpk~!?fR{2RHJ)KR$jI0;sIxML3@vk_st4H7_ zp3AM-tM(H2!^OAp5@px#q}SImA-Bzh z{pT*{v}IN!Z zMKU!8Xug!*qKPa0b^42s(_@QBqgWO4&x85@tq4*Gj1lP2Exvaa4L-R0&I8y@5O9$S z>0Q3_|1IRDB#YkK8)lh_yU+o|w@(sO?|HWO7Ht7%ND-W5zQ3&|z^V|(Ete&m7$vWO)%d6)C$1P$QIIR|dyDwypp9G-Y%UQqzVEW;% z4>llUG=!(`XV3)EbNjB1?-KO6K}|uI=061`a5a2{=8EYFGxpq4%d2Ja_zv_VJB}ZqIu}bnLR{yg(?aFZ>3hu6KpxdVU2&=?5c_f@Sb1MZd|H-S-L|zVNxYgIw#Y>VS~#_C(kGciBw^3^pKHFN)|HsSGDDv z>1?XUxd!eZtA;Lb5P&eM=?$jTvu-H^P!Ur=Qp8P&*N^`p80Fsn5q<+9bN>#Vr{On| z7W}U$(@1MBYCGvMqsoh4ora?J_FVwKAHe>>OIX3X%%lon4Zr6vI>HBQjC6feswhn% zX*1`xSK{$uq^S>A@l4<5jahON>OWN*idzP8tIjGAcld(-LcHuzQ5>>>+zw{`BO+b{CX z>4ABUlK#HATBvZby_srza7?6Z<2&GLrhfG*tRq^v0P*4^NO!;>VR%j>zuJi%as5u9 z5-p6RKpP+OABzI}N(y=NAy~yilpLfx8%O{F* zo^xF}e%>{w@q0C={T@)QapXIV6RO|u-=R;KS5y_J2&ul!BXAy-Q0{^9?N96*NekYh za)Ckk$+{!5^Yw`8@b&-Xf*gbr{rp-M2ADI`U*vz0R;V!2M6Z7h!oS{3ueV4n+dplO zQc+7!82PFvz|?Lxw)chqpX-bNpd(g<3IYt;89HJA&w=v3@uFi@{X!($kEvf4@L0M%tLde3&xu4(-05|b-{L+yhnqMOG0G-YA<4?^}kh1 zm*b>`-TnmEscJ@Co)ZX;mLu!Dp^#M{^r5ANt~?2ZGvv{?f`G$J$`9=VPr$RtcXt}q zmt4k>s(skurGCmMJaLK0JUm)w(%5kP@|5x`z5(DQ#xt~|cfmJwafFBV$YgYZ z^ry*rmiz?I3-AzGma8&(-CJNmg2vJOeJE9m}mC*Iv@;}dMnSLCQ z79U9pBq{bd}wVXyRGi77~tBQb<0Tc0$^?@-Fns~3U{HJTnx0j)hnfO&-&{S{ z1^eh|3EXMR>nA_)5gY(W=mQPx0Xu=Z6-RVNyeI=>PL&t*k}JebcSLT?PDfHUTKP4M zyZo(MfuHRI_Z*q*yO5Kcj)xy{JO33w=zw(pX(cTXmq*FWrng*|xLBCI<)^tEs4G4D z`NTaRwJVyrTBZaDj{lNryh$`KI!a^+TvLEoD5J@RD^V>{+DYv{Z8DJJuN1;IM^GSh z>dZeU!CC0F%1=*Q*RsmI^gZcuqlV%>wRux;@;Tp(5z)BWp4<)nJ>n@XI=q z`Qmg~*<_aei!uPnt%?OKq-5qS2gS(>KFQcIeSLnxdi1=?+@^0N`V;8QcqSPvy6iio zGF*x*e##vo|4je)zfi zrg=zfoTI!xc>@-(?8SE1(2KVnUJ@lEzT%(%zGyi zE`Bku`2CLm^UXr$#WQfLNLP~#x{VBNog;k9tDiCUJO6*186fOAf_3mCilG!-2|$W2 zvwj21;Q>NHmpj8_c`WO$0*KD>oeT|5kLM}*o**M!7{5Eri(bREAnw?6b!-7Z1UMRQ zoAH~M_zGsL5sK&IU2^XjDR^{R(%b{04*y0;`yC=;FG$wDHWvP#&xSaRdeY2cdH|J`;_w>oP zV;yQqJTne``jfwe+}6r^C*psqwGhw#5XweRzlJ9Pa+L#(m~#Kz8t)TKUZy<^$#|^? zmYK{X8sV)Co&G=VU3py0>-TR}NgCN&RTOUSMJg3xB1_YTgwb{@Z6ZS>H_=Rlh>A*^ zniiF$g%-kSP(&N1(qdY)Z&GSnXXbaF&$t)&_x(rvdXyovY&*<+!OYn?^dgMy`r?Pkek!{s3aQere+9KDee|Fp9$Y0 zfM9dfBL=g-!~M-AC7cCUVUd5X`IVl|YwWE0Yk(Rdp=c31=>EW`lZK)-pjqHZJ&U7J zpjs+=cCThj^R{ItcF_WsMvn^K$n30iD!rIy$y$#>Htn{@7k!$VYmby5+~`u{yoi6Qn7Y< z(ux_&PH>5u^*&YhlPzABwb|uNk4_&n{0UuVcOXHI<&D82jw5>bic$>b-R6gCcQCVh zl|P7f3PCPbRXIwq*Y4bH?T6cKpx)rN`7o>QxKq`ASi!88-0d#c@&lI zN)cVsf=8~#8mU;{AS>CjT%*J3qIz|H9Gw{%s}l^-l;>3oYv0CEF{txcm$>rC0LLeq zu95s&%X0FNm^0_F(smfA4C@tu#yW1Nwqfo^<}a41)YJZgyOZ(q%>7z%gqndZE92#a8*Xl}ZKYiFJc94#raYEK`$vjz&A z9iQN|`Z8uinHgpMIV0ds1O&@KlKU6nVjxx)pSR^t-etjsG>=2kW5}qE1~%E6kl905 ztqK+=i(xeGzD*^vx(*vU-EGUsyj>C}+?>0}lugIR+RNlP?&gH`C$-ow*3IsL$WtX$ zS}@3BaQK}q>ezs>x^S`3t8QsKrKhc^a1z{7m2)!UYoL##gK0?J)AV|1`_wm767L=9 zrAfX$K1|;tnYYp4PT#hrH4kFxY1^~u_K6bAvQh4`azA~t_QXn9lgfAo!IIR;oZ4X> zq!<9;08+u6rD7TX0G}tkt}bgDG2v@?B>sEVr&fyhrI zum32KHMEC7JN=AINt>|@03mdpT@E)f-M~A>7U_+6wH@46`MQ!X)<5^IDuk4Lq|~@e zV%hCDUC!uGErG=)6Uv&)102NPiD70DgwAr_tQd5+h#10qQ8LY7C&OO*K8;vC{3y{l z|FC0M1m%s*Aan;zd$qua;40lO$U_|+VaHs!B6^ROE<$Rt47@x69 z`nfn~&gp8`=F&r-t{k6`B=NBg@C4vGCayadA;VcBWCaxozL(NGDp)mksTUq)TED-` z_Ok-YS8qjXI>3Cp_!~u~^45ByF>8bSSGejoga_q)N1Zyr32wTX9BPMLiMK?Z?+us8 zx%@dRKw!2J4f1!~Q(9x`#ZhSaEusQ^F zPFj&MYV$m%>tz==1fa7;DY4}*2x&-7K1tlQvnZh^^)&iqTJH>=OWB_^ae{3CN1TLkbA#BbKt#xW08vJnyjlyZj~B<;j zuV3LqsQZvVeZcg)5!JY~kv8OdT=HB*yu;pJrys+ParjziBFECzRp+_#hl~NA3rUaV z-XeNfQ{qsR4BMpq+lS;mvq;N(3kMIyE=hXid2lz~Oo&lCkPRu2MweS7t!a0^xbk^I z=!Qt87wOwxnE_35fY_Xq;7DEKUwKT|q-_o-$$m3*Q_G5q^O$ze^*P*LnPz!l_|(!@ zbk~!Z9Dhh~B0(vkJmYpfv1acA;>W>lxuy0VxplOwu|-WK=S<$8`YSPQPfQO#!-$L{ zP(uJ?w%{~@rAc_mEl{R!i3J0TsFqV2pt}x%Lu9$9PEpwEOwJKyi#%yK0Fo`EsW~-k z`vopCuwY1zfW1;IPAceJ>He_EtUHNT+_9?Mt*yY_BxR|ARaV4OK?cSuQ1Li0E)i8i z9!#Ufkr16RTXagrc61e6Y+5h1?}A#*lY4RdxE=02P3M0z)3xMsiqXedkiHl~_=F4R z4-aE#Ld>YQfW%}`^iz%6{>gzg=uu8=3yUYXXAt`_5*M^I0Rhkh#cn8uYKelF?Xtp` z%{HBD0qaF<36uA6G4*cx8d*!(n`oWtd*HFZHMd0Rnj)lsz?L^6TmC!$HFN1sE6s!u zqLkmw=tWJb=QATO@1D9bhvi31uVr8L`1HHQ(c|y_dV6fQOvHuJ%Y89mN#+f5RZ1NZ zF$PskEez@voqKt06;_BK0)Zr+oeOWNbzRay&K~73{VKC&SZl@D}udE&T z2KhR&Wq7ZMza42PpMTKm?$6;|)#)gN_FU8Q&g@g|G~DwV3c)amO+d9+=q776a>^>9 z%Rpr95(NT}HzW~_+P2-e!!u^bpS?SggXN4_Av@~k{kelAj$9xVj@L~!KA?&#&O~BR ziNdZ%*W6RnPF21QM^Ymn-!G|(SHU1(BZP`{fnye2>aDu=d~En9*3a zpO!eIwOt((f+{X&O!v4rsRu|Nc-t`mraKkK?j)~;1edxCe8AWDrIllsJY|w>o#IJZ zm*VWP#;T$d2s;FjHbc>~%7|*}Ie05fk_Ld#(tPddQNwkiqn%)zS9|7u$gVQE?eMYk zSY#z(Y}N2cw^uw6?gO)AGEtTYR~icl<_UZ{16xl)gq!Y2B?f$U^z!drwZpZqmTq}z zdK2Z0ZpPHY)clufB8TlmvYeTL+eQf8XX7<9%GRJdEL*MJ4NoF!I7gIt7%al86bUV$ z33WVZ>&MiT@drwBo0^Tul^NJ->ZLol79Z@oPHrylxDu>B%sc&M>-p4GRo(UbwD#5{ zhsZu@3t91QM{ZOr!_u+Vd~{6b%nJ!EgUnNnAGuIZgbtkH0JqU>F?im%sR!WV{0!D`9LxFesx@E&?ys+^3JQF5NxO0k-9jg^}l=9)566Z}byaHruJ z(85Sd>eO)h0}TVyE_uH##=0fr6Iz70WcJ3+#V0?8-fGCpnaW~6BTb)}UF)|;mD2jc zG9;H=&pD@KAZ_nE)i#rLptC1)Ec!D|%+4D_TsRU4Lr_|!0=wT!K?*K}54Jig z4x^6Vg?-2VV&}08WR8s;w(znuFQchG zar&61Gsi|r7-pBk%M-j&SlU&Rf#vBHvGnSP7^`vL6AlA53eSs5e(yi|syuu__M1Ro z?pmXOwV0$tU0^ z!s>OPV+2^WXTKXX69a>qBXZVGGeP{IzJB}t2f2^Dwh@#m&&a%+)cbSMnF9oZVGwfO z>-Zh)?ZF9E@5^x+RhD1!5w+XktKUbYesTP+;d$}JV){bZB zD`q1i3#5MoNnhe+876()?R2*2c37-s(W)vRqgxU=yqjScE{JpZ=AYr&CM#l>4#kz&=yw&Kjeg$ z#FkN<6Buj6fI?i`rd5ec6ir3O$Hr+olG7VTYzPV)KRs{0=3t?VZRvM3IB(Z#H??=xcjhQx*q?nxWXS;CS3QIcZg*Y z@LxSM&tra#{!%$oaP<7Q>H@E+h{%84aQDWOYc+j?2iv37u=xj=m} z)i=M%W;)GG<{Ku2I#|?6bpKFNKHo8&-kuO0J)czFDpmbCFmPgSP3y(2HBWXK{ZZcU zzu@Yv7xLSz9B<5r5*sObBQ_^a^JM?YG>!bmue_!V+m49I(~l=|Gk3>67^qojzppnp zTVrIX%Qqr(yi#=nyV+p-B0Cv-)Ud8XNOUTar|B8H?FZlV4oIK-DA|BUSR%WhSg?9b zh@ZK@4D{>ff`xsD$l z(=XTY%XRQ2@ar=C(JuZ=)KMH?;VA$J!`R4h&o@LPA@B=`lThzn^6X_|{~yn) zlnZh5DP*InhdYD<^vhAj&5tU>a2DjnG#9aXyp^XM+mCC6whO?Q@m6!Atj&L({XYoP BXNCX( literal 0 HcmV?d00001 diff --git a/iosApp/iosApp/Assets.xcassets/Contents.json b/iosApp/iosApp/Assets.xcassets/Contents.json new file mode 100644 index 00000000..4aa7c535 --- /dev/null +++ b/iosApp/iosApp/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} \ No newline at end of file diff --git a/iosApp/iosApp/ContentView.swift b/iosApp/iosApp/ContentView.swift new file mode 100644 index 00000000..3cd5c325 --- /dev/null +++ b/iosApp/iosApp/ContentView.swift @@ -0,0 +1,21 @@ +import UIKit +import SwiftUI +import ComposeApp + +struct ComposeView: UIViewControllerRepresentable { + func makeUIViewController(context: Context) -> UIViewController { + MainViewControllerKt.MainViewController() + } + + func updateUIViewController(_ uiViewController: UIViewController, context: Context) {} +} + +struct ContentView: View { + var body: some View { + ComposeView() + .ignoresSafeArea(.keyboard) // Compose has own keyboard handler + } +} + + + diff --git a/iosApp/iosApp/Info.plist b/iosApp/iosApp/Info.plist new file mode 100644 index 00000000..412e3781 --- /dev/null +++ b/iosApp/iosApp/Info.plist @@ -0,0 +1,50 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + $(PRODUCT_BUNDLE_PACKAGE_TYPE) + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + LSRequiresIPhoneOS + + CADisableMinimumFrameDurationOnPhone + + UIApplicationSceneManifest + + UIApplicationSupportsMultipleScenes + + + UILaunchScreen + + UIRequiredDeviceCapabilities + + armv7 + + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + + diff --git a/iosApp/iosApp/Preview Content/Preview Assets.xcassets/Contents.json b/iosApp/iosApp/Preview Content/Preview Assets.xcassets/Contents.json new file mode 100644 index 00000000..4aa7c535 --- /dev/null +++ b/iosApp/iosApp/Preview Content/Preview Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} \ No newline at end of file diff --git a/iosApp/iosApp/iOSApp.swift b/iosApp/iosApp/iOSApp.swift new file mode 100644 index 00000000..d83dca61 --- /dev/null +++ b/iosApp/iosApp/iOSApp.swift @@ -0,0 +1,10 @@ +import SwiftUI + +@main +struct iOSApp: App { + var body: some Scene { + WindowGroup { + ContentView() + } + } +} \ No newline at end of file From 17cbe351e85a30ef042c43b1361f4e5150315bb4 Mon Sep 17 00:00:00 2001 From: Rods Date: Mon, 17 Feb 2025 23:04:56 -0300 Subject: [PATCH 79/96] [ISSUE-16] - add iOSApp --- 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 57383d29..5dc1573d 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -27,7 +27,7 @@ lottie = "5.2.0" #Room Database room = "2.7.0-alpha13" -sqlite = "2.5.0-SNAPSHOT" +sqlite = "2.5.0-beta01" android_youtube_player_version = "12.0.0" From 5def70ad2f324d68b2c0b38950e0eccc9270352b Mon Sep 17 00:00:00 2001 From: Rods Date: Mon, 17 Feb 2025 23:47:46 -0300 Subject: [PATCH 80/96] [ISSUE-16] - add iOSApp --- .../core_networking/HttpClientEngineProviderPlatform.kt | 2 ++ .../streamplayerapp/core_networking/di/NetworkModule.kt | 4 ++-- .../core_networking/handleError/NetworkResponse.kt | 1 + gradle/libs.versions.toml | 4 ++-- 4 files changed, 7 insertions(+), 4 deletions(-) diff --git a/core-networking/src/commonMain/kotlin/com/codandotv/streamplayerapp/core_networking/HttpClientEngineProviderPlatform.kt b/core-networking/src/commonMain/kotlin/com/codandotv/streamplayerapp/core_networking/HttpClientEngineProviderPlatform.kt index b28059f0..a3206d95 100644 --- a/core-networking/src/commonMain/kotlin/com/codandotv/streamplayerapp/core_networking/HttpClientEngineProviderPlatform.kt +++ b/core-networking/src/commonMain/kotlin/com/codandotv/streamplayerapp/core_networking/HttpClientEngineProviderPlatform.kt @@ -4,4 +4,6 @@ package com.codandotv.streamplayerapp.core_networking import io.ktor.client.engine.HttpClientEngine +@Suppress("NO_ACTUAL_FOR_EXPECT") +//TODO: Another task expect fun httpClientEnginePlatform(): HttpClientEngine \ No newline at end of file diff --git a/core-networking/src/commonMain/kotlin/com/codandotv/streamplayerapp/core_networking/di/NetworkModule.kt b/core-networking/src/commonMain/kotlin/com/codandotv/streamplayerapp/core_networking/di/NetworkModule.kt index 002e7d7d..a55c169b 100644 --- a/core-networking/src/commonMain/kotlin/com/codandotv/streamplayerapp/core_networking/di/NetworkModule.kt +++ b/core-networking/src/commonMain/kotlin/com/codandotv/streamplayerapp/core_networking/di/NetworkModule.kt @@ -1,6 +1,5 @@ package com.codandotv.streamplayerapp.core_networking.di -import android.util.Log import com.codandotv.streamplayerapp.core_networking.di.Network.TIMEOUT import com.codandotv.streamplayerapp.core_networking.httpClientEnginePlatform import core.networking.BuildKonfig @@ -79,7 +78,8 @@ object NetworkModule { level = LogLevel.ALL logger = object : Logger { override fun log(message: String) { - Log.i("HttpClient", message) + //TODO: Migrar Logs para Utilizar Kermit + println("HttpClient${message}") } } } diff --git a/core-networking/src/commonMain/kotlin/com/codandotv/streamplayerapp/core_networking/handleError/NetworkResponse.kt b/core-networking/src/commonMain/kotlin/com/codandotv/streamplayerapp/core_networking/handleError/NetworkResponse.kt index 60ba6259..51378aab 100644 --- a/core-networking/src/commonMain/kotlin/com/codandotv/streamplayerapp/core_networking/handleError/NetworkResponse.kt +++ b/core-networking/src/commonMain/kotlin/com/codandotv/streamplayerapp/core_networking/handleError/NetworkResponse.kt @@ -3,6 +3,7 @@ package com.codandotv.streamplayerapp.core_networking.handleError import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.flow import kotlinx.serialization.Serializable +import kotlinx.serialization.Transient @Serializable sealed class NetworkResponse { diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 5dc1573d..595cbd16 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -26,8 +26,8 @@ coil = "2.3.0" lottie = "5.2.0" #Room Database -room = "2.7.0-alpha13" -sqlite = "2.5.0-beta01" +room = "2.7.0-alpha11" +sqlite = "2.5.0-SNAPSHOT" android_youtube_player_version = "12.0.0" From 76c3aa400676cf582073e039ddf3bf385b288d5f Mon Sep 17 00:00:00 2001 From: Rods Date: Tue, 18 Feb 2025 17:47:49 -0300 Subject: [PATCH 81/96] [ISSUE-16] - move commonMain to androidMain --- ....kt => LottieComponentPlatform.android.kt} | 0 ...omponent.kt => LottieComponentPlatform.kt} | 0 ....ios.kt => LottieComponentPlatform.ios.kt} | 0 .../extension/ErrorExtPlatform.android.kt | 10 +++ .../extension/PackageExtension.kt | 12 +++ .../utils/SharingPlatform.android.kt | 17 +++++ .../widget/SharingStreamPlatform.android.kt} | 75 +++++++++---------- .../extension/ErrorExtPlatform.kt | 6 ++ .../core_shared_ui/utils/Sharing.kt | 26 +------ .../core_shared_ui/utils/SharingPlatform.kt | 7 ++ .../widget/SharingStreamPlatform.kt | 12 +++ .../extension/ErrorExtPlatform.ios.kt | 7 ++ .../utils/SharingPlatform.ios.kt | 9 +++ .../widget/SharingStreamPlatform.ios.kt | 13 ++++ .../core_shared/extension/ErrorExt.kt | 8 -- .../core_shared/extension/UriExt.kt | 53 ------------- .../screens/DetailStreamViewModel.kt | 4 + .../screens/DetailStreamsScreen.kt | 12 +-- .../widget/DetailStreamActionOption.kt | 1 - 19 files changed, 135 insertions(+), 137 deletions(-) rename composeApp/src/androidMain/kotlin/com.codandotv.streamplayerapp/presentation/components/{LottieComponent.android.kt => LottieComponentPlatform.android.kt} (100%) rename composeApp/src/commonMain/kotlin/com.codandotv.streamplayerapp/presentation/components/{LottieComponent.kt => LottieComponentPlatform.kt} (100%) rename composeApp/src/iosMain/kotlin/com.codandotv.streamplayerapp/presentation/components/{LottieComponent.ios.kt => LottieComponentPlatform.ios.kt} (100%) create mode 100644 core-shared-ui/src/androidMain/kotlin/com/codandotv/streamplayerapp/core_shared_ui/extension/ErrorExtPlatform.android.kt create mode 100644 core-shared-ui/src/androidMain/kotlin/com/codandotv/streamplayerapp/core_shared_ui/extension/PackageExtension.kt create mode 100644 core-shared-ui/src/androidMain/kotlin/com/codandotv/streamplayerapp/core_shared_ui/utils/SharingPlatform.android.kt rename core-shared-ui/src/{commonMain/kotlin/com/codandotv/streamplayerapp/core_shared_ui/widget/SharingStreamCustomView.kt => androidMain/kotlin/com/codandotv/streamplayerapp/core_shared_ui/widget/SharingStreamPlatform.android.kt} (91%) create mode 100644 core-shared-ui/src/commonMain/kotlin/com/codandotv/streamplayerapp/core_shared_ui/extension/ErrorExtPlatform.kt create mode 100644 core-shared-ui/src/commonMain/kotlin/com/codandotv/streamplayerapp/core_shared_ui/utils/SharingPlatform.kt create mode 100644 core-shared-ui/src/commonMain/kotlin/com/codandotv/streamplayerapp/core_shared_ui/widget/SharingStreamPlatform.kt create mode 100644 core-shared-ui/src/iosMain/kotlin/com/codandotv/streamplayerapp/core_shared_ui/extension/ErrorExtPlatform.ios.kt create mode 100644 core-shared-ui/src/iosMain/kotlin/com/codandotv/streamplayerapp/core_shared_ui/utils/SharingPlatform.ios.kt create mode 100644 core-shared-ui/src/iosMain/kotlin/com/codandotv/streamplayerapp/core_shared_ui/widget/SharingStreamPlatform.ios.kt delete mode 100644 core-shared/src/commonMain/kotlin/com/codandotv/streamplayerapp/core_shared/extension/ErrorExt.kt delete mode 100644 core-shared/src/commonMain/kotlin/com/codandotv/streamplayerapp/core_shared/extension/UriExt.kt diff --git a/composeApp/src/androidMain/kotlin/com.codandotv.streamplayerapp/presentation/components/LottieComponent.android.kt b/composeApp/src/androidMain/kotlin/com.codandotv.streamplayerapp/presentation/components/LottieComponentPlatform.android.kt similarity index 100% rename from composeApp/src/androidMain/kotlin/com.codandotv.streamplayerapp/presentation/components/LottieComponent.android.kt rename to composeApp/src/androidMain/kotlin/com.codandotv.streamplayerapp/presentation/components/LottieComponentPlatform.android.kt diff --git a/composeApp/src/commonMain/kotlin/com.codandotv.streamplayerapp/presentation/components/LottieComponent.kt b/composeApp/src/commonMain/kotlin/com.codandotv.streamplayerapp/presentation/components/LottieComponentPlatform.kt similarity index 100% rename from composeApp/src/commonMain/kotlin/com.codandotv.streamplayerapp/presentation/components/LottieComponent.kt rename to composeApp/src/commonMain/kotlin/com.codandotv.streamplayerapp/presentation/components/LottieComponentPlatform.kt diff --git a/composeApp/src/iosMain/kotlin/com.codandotv.streamplayerapp/presentation/components/LottieComponent.ios.kt b/composeApp/src/iosMain/kotlin/com.codandotv.streamplayerapp/presentation/components/LottieComponentPlatform.ios.kt similarity index 100% rename from composeApp/src/iosMain/kotlin/com.codandotv.streamplayerapp/presentation/components/LottieComponent.ios.kt rename to composeApp/src/iosMain/kotlin/com.codandotv.streamplayerapp/presentation/components/LottieComponentPlatform.ios.kt diff --git a/core-shared-ui/src/androidMain/kotlin/com/codandotv/streamplayerapp/core_shared_ui/extension/ErrorExtPlatform.android.kt b/core-shared-ui/src/androidMain/kotlin/com/codandotv/streamplayerapp/core_shared_ui/extension/ErrorExtPlatform.android.kt new file mode 100644 index 00000000..eac583b7 --- /dev/null +++ b/core-shared-ui/src/androidMain/kotlin/com/codandotv/streamplayerapp/core_shared_ui/extension/ErrorExtPlatform.android.kt @@ -0,0 +1,10 @@ +package com.codandotv.streamplayerapp.core_shared_ui.extension + +import android.widget.Toast +import androidx.compose.runtime.Composable +import androidx.compose.ui.platform.LocalContext + +@Composable +actual fun ShowErrorMessage(errorMessage: String) { + Toast.makeText(LocalContext.current,errorMessage,Toast.LENGTH_SHORT).show() +} \ No newline at end of file diff --git a/core-shared-ui/src/androidMain/kotlin/com/codandotv/streamplayerapp/core_shared_ui/extension/PackageExtension.kt b/core-shared-ui/src/androidMain/kotlin/com/codandotv/streamplayerapp/core_shared_ui/extension/PackageExtension.kt new file mode 100644 index 00000000..b2131aad --- /dev/null +++ b/core-shared-ui/src/androidMain/kotlin/com/codandotv/streamplayerapp/core_shared_ui/extension/PackageExtension.kt @@ -0,0 +1,12 @@ +package com.codandotv.streamplayerapp.core_shared_ui.extension + +import android.content.pm.PackageInfo +import android.content.pm.PackageManager +import android.os.Build + +fun PackageManager.getPackageInfoCompat(packageName: String, flags: Int = 0): PackageInfo = + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { + getPackageInfo(packageName, PackageManager.PackageInfoFlags.of(flags.toLong())) + } else { + @Suppress("DEPRECATION") getPackageInfo(packageName, flags) + } diff --git a/core-shared-ui/src/androidMain/kotlin/com/codandotv/streamplayerapp/core_shared_ui/utils/SharingPlatform.android.kt b/core-shared-ui/src/androidMain/kotlin/com/codandotv/streamplayerapp/core_shared_ui/utils/SharingPlatform.android.kt new file mode 100644 index 00000000..1b9ab4c2 --- /dev/null +++ b/core-shared-ui/src/androidMain/kotlin/com/codandotv/streamplayerapp/core_shared_ui/utils/SharingPlatform.android.kt @@ -0,0 +1,17 @@ +package com.codandotv.streamplayerapp.core_shared_ui.utils + +import android.content.pm.PackageManager +import androidx.compose.runtime.Composable +import androidx.compose.ui.platform.LocalContext +import com.codandotv.streamplayerapp.core_shared_ui.extension.getPackageInfoCompat + +@Composable +actual fun isPackageInstalled(packageName: String): Boolean { + val pm = LocalContext.current.packageManager + return try { + pm.getPackageInfoCompat(packageName) + true + } catch (e: PackageManager.NameNotFoundException) { + false + } +} \ No newline at end of file diff --git a/core-shared-ui/src/commonMain/kotlin/com/codandotv/streamplayerapp/core_shared_ui/widget/SharingStreamCustomView.kt b/core-shared-ui/src/androidMain/kotlin/com/codandotv/streamplayerapp/core_shared_ui/widget/SharingStreamPlatform.android.kt similarity index 91% rename from core-shared-ui/src/commonMain/kotlin/com/codandotv/streamplayerapp/core_shared_ui/widget/SharingStreamCustomView.kt rename to core-shared-ui/src/androidMain/kotlin/com/codandotv/streamplayerapp/core_shared_ui/widget/SharingStreamPlatform.android.kt index dd788dbc..88a4e83b 100644 --- a/core-shared-ui/src/commonMain/kotlin/com/codandotv/streamplayerapp/core_shared_ui/widget/SharingStreamCustomView.kt +++ b/core-shared-ui/src/androidMain/kotlin/com/codandotv/streamplayerapp/core_shared_ui/widget/SharingStreamPlatform.android.kt @@ -1,6 +1,5 @@ package com.codandotv.streamplayerapp.core_shared_ui.widget -import android.app.Activity import android.content.ActivityNotFoundException import android.content.ClipData import android.content.ClipboardManager @@ -33,6 +32,7 @@ import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.MutableState +import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope @@ -47,20 +47,14 @@ import androidx.compose.ui.text.font.FontFamily import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import androidx.core.content.ContextCompat -import com.codandotv.streamplayerapp.core_shared.extension.getUriFromUrlImage import com.codandotv.streamplayerapp.core_shared_ui.resources.Colors import com.codandotv.streamplayerapp.core_shared_ui.utils.Sharing.ANIMATION_DURATION import com.codandotv.streamplayerapp.core_shared_ui.utils.Sharing.ANIMATION_EXECUTION_DELAY import com.codandotv.streamplayerapp.core_shared_ui.utils.Sharing.COPY_CONTENT_TYPE_TEXT -import com.codandotv.streamplayerapp.core_shared_ui.utils.Sharing.INSTAGRAM_PACKAGE_SHARING -import com.codandotv.streamplayerapp.core_shared_ui.utils.Sharing.INSTAGRAM_STORY_DESTINATION import com.codandotv.streamplayerapp.core_shared_ui.utils.Sharing.OPTIONS_TITLE_MESSAGE -import com.codandotv.streamplayerapp.core_shared_ui.utils.Sharing.SHARING_DATA_TYPE_IMAGE import com.codandotv.streamplayerapp.core_shared_ui.utils.Sharing.SHARING_DATA_TYPE_TEXT import com.codandotv.streamplayerapp.core_shared_ui.utils.Sharing.SMS_CONTENT_BODY import com.codandotv.streamplayerapp.core_shared_ui.utils.Sharing.SMS_CONTENT_TYPE -import com.codandotv.streamplayerapp.core_shared_ui.utils.Sharing.WHATSAPP_PACKAGE_SHARING -import com.codandotv.streamplayerapp.core_shared_ui.utils.isPackageInstalled import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.delay import kotlinx.coroutines.launch @@ -84,9 +78,9 @@ import streamplayerapp_kmp.core_shared_ui.generated.resources.sharing_whatsapp_m import streamplayerapp_kmp.core_shared_ui.generated.resources.sms_app_error_message import streamplayerapp_kmp.core_shared_ui.generated.resources.whatsapp_not_installed_message -@Suppress("LongMethod") @Composable -fun SharingStreamCustomView( +actual fun SharingStreamPlatform( + modifier: Modifier, contentTitle: String, contentUrl: String, setShowDialog: (Boolean) -> Unit @@ -322,29 +316,30 @@ private fun shareInstagramStory( contentUrl: String, errorMessage: String ) { - if (isPackageInstalled(WHATSAPP_PACKAGE_SHARING, context)) { - Thread { - val imgBitmapUri = getUriFromUrlImage(contentUrl, context) - - val storiesIntent = Intent(INSTAGRAM_STORY_DESTINATION) - storiesIntent.setDataAndType(imgBitmapUri, SHARING_DATA_TYPE_IMAGE) - storiesIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) - storiesIntent.setPackage(INSTAGRAM_PACKAGE_SHARING) - - context.grantUriPermission( - INSTAGRAM_PACKAGE_SHARING, imgBitmapUri, Intent.FLAG_GRANT_READ_URI_PERMISSION - ) - (context as Activity).runOnUiThread { - context.startActivity(storiesIntent) - } - }.start() - } else { - Toast.makeText( - context, - errorMessage, - Toast.LENGTH_SHORT - ).show() - } +// if (isPackageInstalled(WHATSAPP_PACKAGE_SHARING)) { +// Thread { + //TODO: +// val imgBitmapUri = getUriFromUrlImage(contentUrl) +// +// val storiesIntent = Intent(INSTAGRAM_STORY_DESTINATION) +// storiesIntent.setDataAndType(imgBitmapUri, SHARING_DATA_TYPE_IMAGE) +// storiesIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) +// storiesIntent.setPackage(INSTAGRAM_PACKAGE_SHARING) +// +// context.grantUriPermission( +// INSTAGRAM_PACKAGE_SHARING, imgBitmapUri, Intent.FLAG_GRANT_READ_URI_PERMISSION +// ) +// (context as Activity).runOnUiThread { +// context.startActivity(storiesIntent) +// } +// }.start() +// } else { +// Toast.makeText( +// context, +// errorMessage, +// Toast.LENGTH_SHORT +// ).show() +// } } private fun shareWhatsAppMessage( @@ -352,19 +347,19 @@ private fun shareWhatsAppMessage( message: String, errorMessage: String ) { - if (isPackageInstalled(WHATSAPP_PACKAGE_SHARING, context)) { - val intent = Intent(Intent.ACTION_SEND) - intent.type = SHARING_DATA_TYPE_TEXT - intent.setPackage(WHATSAPP_PACKAGE_SHARING) - intent.putExtra(Intent.EXTRA_TEXT, message) - context.startActivity(intent) - } else { +// if (isPackageInstalled(WHATSAPP_PACKAGE_SHARING)) { +// val intent = Intent(Intent.ACTION_SEND) +// intent.type = SHARING_DATA_TYPE_TEXT +// intent.setPackage(WHATSAPP_PACKAGE_SHARING) +// intent.putExtra(Intent.EXTRA_TEXT, message) +// context.startActivity(intent) +// } else { Toast.makeText( context, errorMessage, Toast.LENGTH_SHORT ).show() - } +// } } private fun copyContentLink(context: Context, linkCopiedMessage: String, contentUrl: String) { diff --git a/core-shared-ui/src/commonMain/kotlin/com/codandotv/streamplayerapp/core_shared_ui/extension/ErrorExtPlatform.kt b/core-shared-ui/src/commonMain/kotlin/com/codandotv/streamplayerapp/core_shared_ui/extension/ErrorExtPlatform.kt new file mode 100644 index 00000000..d4884c7e --- /dev/null +++ b/core-shared-ui/src/commonMain/kotlin/com/codandotv/streamplayerapp/core_shared_ui/extension/ErrorExtPlatform.kt @@ -0,0 +1,6 @@ +package com.codandotv.streamplayerapp.core_shared_ui.extension + +import androidx.compose.runtime.Composable + +@Composable +expect fun ShowErrorMessage(errorMessage: String) \ No newline at end of file diff --git a/core-shared-ui/src/commonMain/kotlin/com/codandotv/streamplayerapp/core_shared_ui/utils/Sharing.kt b/core-shared-ui/src/commonMain/kotlin/com/codandotv/streamplayerapp/core_shared_ui/utils/Sharing.kt index d7963573..0a53109c 100644 --- a/core-shared-ui/src/commonMain/kotlin/com/codandotv/streamplayerapp/core_shared_ui/utils/Sharing.kt +++ b/core-shared-ui/src/commonMain/kotlin/com/codandotv/streamplayerapp/core_shared_ui/utils/Sharing.kt @@ -1,12 +1,6 @@ package com.codandotv.streamplayerapp.core_shared_ui.utils -import android.content.Context -import android.content.pm.PackageInfo -import android.content.pm.PackageManager -import android.os.Build - object Sharing { - const val SHARING_DATA_TYPE_TEXT = "text/plain" const val SHARING_DATA_TYPE_IMAGE = "image/*" const val COPY_CONTENT_TYPE_TEXT = "text" @@ -18,22 +12,4 @@ object Sharing { const val OPTIONS_TITLE_MESSAGE = "Compartilhar usando" const val ANIMATION_EXECUTION_DELAY = 100L const val ANIMATION_DURATION = 300 -} - -@Suppress("SwallowedException") -fun isPackageInstalled(packageName: String, context: Context): Boolean { - val pm = context.packageManager - return try { - pm.getPackageInfoCompat(packageName) - true - } catch (e: PackageManager.NameNotFoundException) { - false - } -} - -fun PackageManager.getPackageInfoCompat(packageName: String, flags: Int = 0): PackageInfo = - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { - getPackageInfo(packageName, PackageManager.PackageInfoFlags.of(flags.toLong())) - } else { - @Suppress("DEPRECATION") getPackageInfo(packageName, flags) - } \ No newline at end of file +} \ No newline at end of file diff --git a/core-shared-ui/src/commonMain/kotlin/com/codandotv/streamplayerapp/core_shared_ui/utils/SharingPlatform.kt b/core-shared-ui/src/commonMain/kotlin/com/codandotv/streamplayerapp/core_shared_ui/utils/SharingPlatform.kt new file mode 100644 index 00000000..f69166c8 --- /dev/null +++ b/core-shared-ui/src/commonMain/kotlin/com/codandotv/streamplayerapp/core_shared_ui/utils/SharingPlatform.kt @@ -0,0 +1,7 @@ +package com.codandotv.streamplayerapp.core_shared_ui.utils + +import androidx.compose.runtime.Composable + + +@Composable +expect fun isPackageInstalled(packageName: String) : Boolean \ No newline at end of file diff --git a/core-shared-ui/src/commonMain/kotlin/com/codandotv/streamplayerapp/core_shared_ui/widget/SharingStreamPlatform.kt b/core-shared-ui/src/commonMain/kotlin/com/codandotv/streamplayerapp/core_shared_ui/widget/SharingStreamPlatform.kt new file mode 100644 index 00000000..23ac5f44 --- /dev/null +++ b/core-shared-ui/src/commonMain/kotlin/com/codandotv/streamplayerapp/core_shared_ui/widget/SharingStreamPlatform.kt @@ -0,0 +1,12 @@ +package com.codandotv.streamplayerapp.core_shared_ui.widget + +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier + +@Composable +expect fun SharingStreamPlatform( + modifier : Modifier = Modifier, + contentTitle: String, + contentUrl: String, + setShowDialog: (Boolean) -> Unit +) \ No newline at end of file diff --git a/core-shared-ui/src/iosMain/kotlin/com/codandotv/streamplayerapp/core_shared_ui/extension/ErrorExtPlatform.ios.kt b/core-shared-ui/src/iosMain/kotlin/com/codandotv/streamplayerapp/core_shared_ui/extension/ErrorExtPlatform.ios.kt new file mode 100644 index 00000000..fc9f5e50 --- /dev/null +++ b/core-shared-ui/src/iosMain/kotlin/com/codandotv/streamplayerapp/core_shared_ui/extension/ErrorExtPlatform.ios.kt @@ -0,0 +1,7 @@ +package com.codandotv.streamplayerapp.core_shared_ui.extension + +import androidx.compose.runtime.Composable + +@Composable +actual fun ShowErrorMessage(errorMessage: String) { +} \ No newline at end of file diff --git a/core-shared-ui/src/iosMain/kotlin/com/codandotv/streamplayerapp/core_shared_ui/utils/SharingPlatform.ios.kt b/core-shared-ui/src/iosMain/kotlin/com/codandotv/streamplayerapp/core_shared_ui/utils/SharingPlatform.ios.kt new file mode 100644 index 00000000..abf58cee --- /dev/null +++ b/core-shared-ui/src/iosMain/kotlin/com/codandotv/streamplayerapp/core_shared_ui/utils/SharingPlatform.ios.kt @@ -0,0 +1,9 @@ +package com.codandotv.streamplayerapp.core_shared_ui.utils + +import androidx.compose.runtime.Composable + +@Composable +actual fun isPackageInstalled(packageName: String): Boolean { + //TODO: + return false +} \ No newline at end of file diff --git a/core-shared-ui/src/iosMain/kotlin/com/codandotv/streamplayerapp/core_shared_ui/widget/SharingStreamPlatform.ios.kt b/core-shared-ui/src/iosMain/kotlin/com/codandotv/streamplayerapp/core_shared_ui/widget/SharingStreamPlatform.ios.kt new file mode 100644 index 00000000..25cf421f --- /dev/null +++ b/core-shared-ui/src/iosMain/kotlin/com/codandotv/streamplayerapp/core_shared_ui/widget/SharingStreamPlatform.ios.kt @@ -0,0 +1,13 @@ +package com.codandotv.streamplayerapp.core_shared_ui.widget + +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier + +@Composable +actual fun SharingStreamPlatform( + modifier: Modifier, + contentTitle: String, + contentUrl: String, + setShowDialog: (Boolean) -> Unit +) { +} \ No newline at end of file diff --git a/core-shared/src/commonMain/kotlin/com/codandotv/streamplayerapp/core_shared/extension/ErrorExt.kt b/core-shared/src/commonMain/kotlin/com/codandotv/streamplayerapp/core_shared/extension/ErrorExt.kt deleted file mode 100644 index 606f8b9d..00000000 --- a/core-shared/src/commonMain/kotlin/com/codandotv/streamplayerapp/core_shared/extension/ErrorExt.kt +++ /dev/null @@ -1,8 +0,0 @@ -package com.codandotv.streamplayerapp.core_shared.extension - -import android.content.Context -import android.widget.Toast - -fun showErrorMessage(context: Context, errorMessage: String, duration: Int = Toast.LENGTH_LONG) { - Toast.makeText(context, errorMessage, duration).show() -} \ No newline at end of file diff --git a/core-shared/src/commonMain/kotlin/com/codandotv/streamplayerapp/core_shared/extension/UriExt.kt b/core-shared/src/commonMain/kotlin/com/codandotv/streamplayerapp/core_shared/extension/UriExt.kt deleted file mode 100644 index 573f734e..00000000 --- a/core-shared/src/commonMain/kotlin/com/codandotv/streamplayerapp/core_shared/extension/UriExt.kt +++ /dev/null @@ -1,53 +0,0 @@ -package com.codandotv.streamplayerapp.core_shared.extension - -import android.content.Context -import android.graphics.BitmapFactory -import android.net.Uri -import android.provider.MediaStore -import java.io.IOException -import java.io.InputStream -import java.net.HttpURLConnection -import java.net.MalformedURLException -import java.net.URL -import java.util.* - -@Suppress("MagicNumber", "SwallowedException") -fun getUriFromUrlImage( - contentUrl: String, - context: Context -): Uri? { - var url: URL? = null - try { - url = URL(contentUrl) - } catch (e: MalformedURLException) { - e.printStackTrace() - } - var connection: HttpURLConnection? = null - try { - assert(url != null) - connection = url!!.openConnection() as HttpURLConnection - } catch (e: IOException) { - showErrorMessage(context, "Erro ao buscar imagem") - } - assert(connection != null) - connection!!.doInput = true - try { - connection.connect() - } catch (e: IOException) { - showErrorMessage(context, "Erro ao buscar imagem") - } - var input: InputStream? = null - try { - input = connection.inputStream - } catch (e: IOException) { - showErrorMessage(context, "Erro ao buscar imagem") - } - val imgBitmap = BitmapFactory.decodeStream(input) - val rand = Random() - val randNo = rand.nextInt(100000) - val imgBitmapPath = MediaStore.Images.Media.insertImage( - context.contentResolver, imgBitmap, - "IMG:$randNo", null - ) - return Uri.parse(imgBitmapPath) -} \ No newline at end of file diff --git a/feature-detail/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_detail/presentation/screens/DetailStreamViewModel.kt b/feature-detail/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_detail/presentation/screens/DetailStreamViewModel.kt index ba330dbb..a9ae55f1 100644 --- a/feature-detail/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_detail/presentation/screens/DetailStreamViewModel.kt +++ b/feature-detail/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_detail/presentation/screens/DetailStreamViewModel.kt @@ -26,6 +26,10 @@ class DetailStreamViewModel( private val dispatcher: CoroutineDispatcher ) : ViewModel() { + init { + loadDetail() + } + private val _uiState = MutableStateFlow(LoadingStreamUIState) val uiState: StateFlow = _uiState.stateIn( viewModelScope, diff --git a/feature-detail/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_detail/presentation/screens/DetailStreamsScreen.kt b/feature-detail/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_detail/presentation/screens/DetailStreamsScreen.kt index 2e32247c..0891f603 100644 --- a/feature-detail/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_detail/presentation/screens/DetailStreamsScreen.kt +++ b/feature-detail/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_detail/presentation/screens/DetailStreamsScreen.kt @@ -1,6 +1,5 @@ package com.codandotv.streamplayerapp.feature_detail.presentation.screens -import android.annotation.SuppressLint import androidx.activity.compose.BackHandler import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column @@ -31,11 +30,9 @@ import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.em import androidx.compose.ui.unit.sp -import androidx.lifecycle.Lifecycle -import androidx.lifecycle.compose.LifecycleEventEffect import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.navigation.NavController -import com.codandotv.streamplayerapp.core_shared_ui.widget.SharingStreamCustomView +import com.codandotv.streamplayerapp.core_shared_ui.widget.SharingStreamPlatform import com.codandotv.streamplayerapp.feature_detail.domain.DetailStream import com.codandotv.streamplayerapp.feature_detail.presentation.widget.DetailStreamActionOption import com.codandotv.streamplayerapp.feature_detail.presentation.widget.DetailStreamButtonAction @@ -55,9 +52,6 @@ fun DetailStreamScreen( onNavigateSearchScreen: () -> Unit = {}, ) { val uiState by viewModel.uiState.collectAsStateWithLifecycle() - LifecycleEventEffect(Lifecycle.Event.ON_START) { - viewModel.loadDetail() - } when (uiState) { is DetailStreamsUIState.DetailStreamsLoadedUIState -> { @@ -82,9 +76,7 @@ fun DetailStreamScreen( } } -@OptIn(ExperimentalMaterial3Api::class) @Suppress("LongMethod") -@SuppressLint("UnusedMaterial3ScaffoldPaddingParameter") @Composable private fun SetupDetailScreen( onToggleToMyList: (DetailStream) -> Unit, @@ -175,7 +167,7 @@ private fun SetupDetailScreen( } } if (showDialog.value) { - SharingStreamCustomView( + SharingStreamPlatform( contentTitle = uiState.detailStream.title, contentUrl = uiState.detailStream.url, setShowDialog = { diff --git a/feature-detail/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_detail/presentation/widget/DetailStreamActionOption.kt b/feature-detail/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_detail/presentation/widget/DetailStreamActionOption.kt index d694d8c2..03b52f98 100644 --- a/feature-detail/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_detail/presentation/widget/DetailStreamActionOption.kt +++ b/feature-detail/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_detail/presentation/widget/DetailStreamActionOption.kt @@ -18,7 +18,6 @@ import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import com.codandotv.streamplayerapp.core_shared_ui.widget.IconWithText -import com.codandotv.streamplayerapp.feature.detail.R import com.codandotv.streamplayerapp.feature_detail.domain.DetailStream import org.jetbrains.compose.resources.stringResource import streamplayerapp_kmp.feature_detail.generated.resources.Res From 890bd36ad70c815b98da86b6307507bf787ee7d2 Mon Sep 17 00:00:00 2001 From: Rods Date: Tue, 18 Feb 2025 18:34:17 -0300 Subject: [PATCH 82/96] [ISSUE-16] - downgrade sqlite --- gradle/libs.versions.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 595cbd16..1aa4164b 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -9,7 +9,7 @@ kover = "0.7.5" detekt = "1.23.6" compose_plugin_multiplataform = "1.7.1" navigation-compose-version = "2.7.0-alpha07" -paging-compose = "3.3.5" +paging-compose = "3.3.6" buildkonfig = "0.15.2" #Test @@ -27,7 +27,7 @@ lottie = "5.2.0" #Room Database room = "2.7.0-alpha11" -sqlite = "2.5.0-SNAPSHOT" +sqlite = "2.5.0-alpha11" android_youtube_player_version = "12.0.0" From 0213df82cda6c315ca03b454e2c4348b9eb3b366 Mon Sep 17 00:00:00 2001 From: Gabriel Bronzatti Moro Date: Wed, 19 Feb 2025 11:05:59 -0300 Subject: [PATCH 83/96] Replace paging library for paging library provided by cash app team --- gradle/libs.versions.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 1aa4164b..013379aa 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -9,7 +9,7 @@ kover = "0.7.5" detekt = "1.23.6" compose_plugin_multiplataform = "1.7.1" navigation-compose-version = "2.7.0-alpha07" -paging-compose = "3.3.6" +paging-compose = "3.3.0-alpha02-0.5.1" buildkonfig = "0.15.2" #Test @@ -54,7 +54,7 @@ mockk_android = { group = "io.mockk", name = "mockk-android", version.ref = "moc viewmodel_test = { group = "androidx.arch.core", name = "core-testing", version.ref = "androidx_core_testing" } coroutines_test = { group = "org.jetbrains.kotlinx", name = "kotlinx-coroutines-test", version.ref = "kotlinx-coroutines-test" } navigation-compose = { module = "org.jetbrains.androidx.navigation:navigation-compose", version.ref = "navigation-compose-version" } -paging-compose = { module = "androidx.paging:paging-compose", version.ref = "paging-compose" } +paging-compose = { module = "app.cash.paging:paging-compose-common", version.ref = "paging-compose" } # Koin koin_test = { group = "io.insert-koin", name = "koin-test-junit4", version.ref = "koin" } From c4efcecc941d5a77e905cbdc34944ea9a57d2374 Mon Sep 17 00:00:00 2001 From: Gabriel Bronzatti Moro Date: Wed, 19 Feb 2025 12:11:48 -0300 Subject: [PATCH 84/96] Solving some issues with: coil + lifecycle problems + koin view model initialization --- build-logic/src/main/java/Config.kt | 4 +- .../main/java/extensions/CommonExtensions.kt | 8 +-- composeApp/build.gradle.kts | 1 - core-shared-ui/build.gradle.kts | 11 ++-- .../widget/StreamPlayerTopBar.kt | 4 +- .../core_shared_ui/widget/StreamsCard.kt | 3 +- .../core_shared_ui/widget/WebImage.kt | 58 +++++++++++++++++++ feature-detail/build.gradle.kts | 5 +- .../DetailStreamRepositoryTest.kt | 0 .../feature_detail/DetailStreamUseCaseTest.kt | 0 .../DetailStreamViewModelTest.kt | 0 .../InstantTaskCoroutinesExecutorRule.kt | 0 .../streamplayerapp/feature_detail/Shared.kt | 0 .../feature_detail/di/DetailStreamModule.kt | 3 +- .../navigation/DetailStreamNavigation.kt | 3 +- .../screens/DetailStreamsScreen.kt | 12 ++-- .../widget/DetailStreamImagePreview.kt | 6 +- feature-list-streams/build.gradle.kts | 4 +- .../list/di/ListStreamModule.kt | 2 +- .../presentation/screens/ListStreamsScreen.kt | 11 +--- .../presentation/widgets/HighlightBanner.kt | 13 +---- feature-profile/build.gradle.kts | 3 +- .../profile/di/ProfilePickerStreamModule.kt | 2 +- .../screens/ProfilePickerStreamScreen.kt | 16 +---- .../widget/ProfilePickerProfilesGrid.kt | 14 +++-- .../ProfilePickerSelectedProfileContainer.kt | 48 +++++++-------- feature-search/build.gradle.kts | 3 +- .../feature_search/di/SearchModule.kt | 2 +- .../presentation/screens/SearchScreen.kt | 10 +--- .../presentation/widgets/SearchStreamCard.kt | 6 +- gradle/libs.versions.toml | 13 +++-- gradle/wrapper/gradle-wrapper.properties | 3 +- 32 files changed, 145 insertions(+), 123 deletions(-) create mode 100644 core-shared-ui/src/commonMain/kotlin/com/codandotv/streamplayerapp/core_shared_ui/widget/WebImage.kt rename feature-detail/src/{commonTest => androidUnitTest}/kotlin/com/codandotv/streamplayerapp/feature_detail/DetailStreamRepositoryTest.kt (100%) rename feature-detail/src/{commonTest => androidUnitTest}/kotlin/com/codandotv/streamplayerapp/feature_detail/DetailStreamUseCaseTest.kt (100%) rename feature-detail/src/{commonTest => androidUnitTest}/kotlin/com/codandotv/streamplayerapp/feature_detail/DetailStreamViewModelTest.kt (100%) rename feature-detail/src/{commonTest => androidUnitTest}/kotlin/com/codandotv/streamplayerapp/feature_detail/InstantTaskCoroutinesExecutorRule.kt (100%) rename feature-detail/src/{commonTest => androidUnitTest}/kotlin/com/codandotv/streamplayerapp/feature_detail/Shared.kt (100%) diff --git a/build-logic/src/main/java/Config.kt b/build-logic/src/main/java/Config.kt index 6624f13c..3738e1fd 100644 --- a/build-logic/src/main/java/Config.kt +++ b/build-logic/src/main/java/Config.kt @@ -1,9 +1,9 @@ object Config { const val appName = "streamplayerapp" const val applicationId = "com.codandotv.$appName" - const val compileSdkVersion = 34 + const val compileSdkVersion = 35 const val minSdkVersion = 24 - const val targetSdkVersion = 34 + const val targetSdkVersion = 35 const val versionName = "1.0" const val versionCode = 1 const val testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" diff --git a/build-logic/src/main/java/extensions/CommonExtensions.kt b/build-logic/src/main/java/extensions/CommonExtensions.kt index 88e2b69b..a54f3d85 100644 --- a/build-logic/src/main/java/extensions/CommonExtensions.kt +++ b/build-logic/src/main/java/extensions/CommonExtensions.kt @@ -10,7 +10,7 @@ import org.gradle.api.artifacts.VersionCatalog import org.gradle.api.artifacts.VersionCatalogsExtension import org.gradle.kotlin.dsl.getByType -internal fun CommonExtension<*, *, *, *, *>.setupPackingOptions() { +internal fun CommonExtension<*, *, *, *, *, *>.setupPackingOptions() { packaging { resources { with(pickFirsts) { @@ -26,7 +26,7 @@ internal fun CommonExtension<*, *, *, *, *>.setupPackingOptions() { } } -internal fun CommonExtension<*, *, *, *, *>.setupAndroidDefaultConfig() { +internal fun CommonExtension<*, *, *, *, *, *>.setupAndroidDefaultConfig() { defaultConfig { compileSdk = Config.compileSdkVersion minSdk = Config.minSdkVersion @@ -36,14 +36,14 @@ internal fun CommonExtension<*, *, *, *, *>.setupAndroidDefaultConfig() { } } -internal fun CommonExtension<*, *, *, *, *>.setupCompileOptions() { +internal fun CommonExtension<*, *, *, *, *, *>.setupCompileOptions() { compileOptions { sourceCompatibility = JavaVersion.VERSION_17 targetCompatibility = JavaVersion.VERSION_17 } } -internal fun CommonExtension<*, *, *, *, *>.setupNameSpace(project: Project) { +internal fun CommonExtension<*, *, *, *, *, *>.setupNameSpace(project: Project) { val moduleName = project.displayName .removePrefix("project ") .replace(":", ".") diff --git a/composeApp/build.gradle.kts b/composeApp/build.gradle.kts index 1e4a350a..53dae137 100644 --- a/composeApp/build.gradle.kts +++ b/composeApp/build.gradle.kts @@ -29,7 +29,6 @@ kotlin { implementation(compose.ui) implementation(libs.koin.core) - implementation(libs.koin.compose) } } } diff --git a/core-shared-ui/build.gradle.kts b/core-shared-ui/build.gradle.kts index ddaef9d7..9af7989e 100644 --- a/core-shared-ui/build.gradle.kts +++ b/core-shared-ui/build.gradle.kts @@ -9,22 +9,25 @@ kotlin { sourceSets { androidMain.dependencies { implementation(compose.preview) + implementation(libs.android.youtube.player) + implementation(libs.ktor.client.okhttp) } commonMain.dependencies { implementation(projects.coreShared) - implementation(compose.material3) implementation(compose.ui) implementation(compose.components.resources) - implementation(libs.navigation.compose) - implementation(libs.android.youtube.player) - implementation(libs.coil) + implementation(libs.coil.network.ktor3) implementation(libs.paging.compose) } + + iosMain.dependencies { + implementation(libs.ktor.client.darwin) + } } } diff --git a/core-shared-ui/src/commonMain/kotlin/com/codandotv/streamplayerapp/core_shared_ui/widget/StreamPlayerTopBar.kt b/core-shared-ui/src/commonMain/kotlin/com/codandotv/streamplayerapp/core_shared_ui/widget/StreamPlayerTopBar.kt index d1ed1dc4..062251f8 100644 --- a/core-shared-ui/src/commonMain/kotlin/com/codandotv/streamplayerapp/core_shared_ui/widget/StreamPlayerTopBar.kt +++ b/core-shared-ui/src/commonMain/kotlin/com/codandotv/streamplayerapp/core_shared_ui/widget/StreamPlayerTopBar.kt @@ -19,14 +19,12 @@ import androidx.compose.material3.Text import androidx.compose.material3.TopAppBar import androidx.compose.material3.TopAppBarDefaults import androidx.compose.material3.TopAppBarScrollBehavior -import androidx.compose.material3.rememberTopAppBarState import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.graphics.Color import androidx.compose.ui.unit.dp -import coil.compose.AsyncImage -import com.codandotv.streamplayerapp.core_shared.extension.empty +import coil3.compose.AsyncImage import com.codandotv.streamplayerapp.core_shared_ui.resources.Colors import org.jetbrains.compose.resources.painterResource import org.jetbrains.compose.resources.stringResource diff --git a/core-shared-ui/src/commonMain/kotlin/com/codandotv/streamplayerapp/core_shared_ui/widget/StreamsCard.kt b/core-shared-ui/src/commonMain/kotlin/com/codandotv/streamplayerapp/core_shared_ui/widget/StreamsCard.kt index 4e30f7ec..3dab50ff 100644 --- a/core-shared-ui/src/commonMain/kotlin/com/codandotv/streamplayerapp/core_shared_ui/widget/StreamsCard.kt +++ b/core-shared-ui/src/commonMain/kotlin/com/codandotv/streamplayerapp/core_shared_ui/widget/StreamsCard.kt @@ -11,8 +11,7 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.unit.dp -import coil.compose.AsyncImage -import com.codandotv.streamplayerapp.core_shared.Url.IMAGE_URL_SIZE_300 +import coil3.compose.AsyncImage import kotlinx.parcelize.Parcelize @Composable diff --git a/core-shared-ui/src/commonMain/kotlin/com/codandotv/streamplayerapp/core_shared_ui/widget/WebImage.kt b/core-shared-ui/src/commonMain/kotlin/com/codandotv/streamplayerapp/core_shared_ui/widget/WebImage.kt new file mode 100644 index 00000000..18282996 --- /dev/null +++ b/core-shared-ui/src/commonMain/kotlin/com/codandotv/streamplayerapp/core_shared_ui/widget/WebImage.kt @@ -0,0 +1,58 @@ +package com.codandotv.streamplayerapp.core_shared_ui.widget + +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.heightIn +import androidx.compose.foundation.layout.widthIn +import androidx.compose.material3.CircularProgressIndicator +import androidx.compose.runtime.Composable +import androidx.compose.runtime.remember +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.FilterQuality +import androidx.compose.ui.layout.ContentScale +import androidx.compose.ui.unit.dp +import coil3.compose.LocalPlatformContext +import coil3.compose.SubcomposeAsyncImage +import coil3.network.ktor3.KtorNetworkFetcherFactory +import coil3.request.ImageRequest + +@Composable +fun WebImage( + imageUrl: String, + contentDescription: String?, + contentScale: ContentScale, + onFailure: (@Composable () -> Unit) = {}, + filterQuality: FilterQuality = FilterQuality.High, + modifier: Modifier = Modifier +) { + val platformContext = LocalPlatformContext.current + val imageRequest = remember { + ImageRequest.Builder( + context = platformContext + ).data( + imageUrl + ).fetcherFactory( + KtorNetworkFetcherFactory() + ).build() + } + + SubcomposeAsyncImage( + model = imageRequest, + filterQuality = filterQuality, + contentDescription = contentDescription, + contentScale = contentScale, + modifier = modifier, + loading = { + Box { + CircularProgressIndicator( + modifier = Modifier.align(Alignment.Center) + .heightIn(max = 52.dp) + .widthIn(max = 52.dp) + ) + } + }, + error = { + onFailure() + } + ) +} \ No newline at end of file diff --git a/feature-detail/build.gradle.kts b/feature-detail/build.gradle.kts index 8e66f7a3..0ad22fc8 100644 --- a/feature-detail/build.gradle.kts +++ b/feature-detail/build.gradle.kts @@ -14,6 +14,7 @@ kotlin { commonMain.dependencies { implementation(libs.koin.core) implementation(libs.koin.compose) + implementation(libs.koin.compose.viewmodel) implementation(projects.coreNetworking) implementation(projects.coreNavigation) @@ -27,12 +28,10 @@ kotlin { implementation(libs.navigation.compose) - implementation(libs.coil) - implementation(libs.ktor.client.content.serialization.json) implementation(libs.ktor.client.content.negotiation) } - commonTest.dependencies { + androidUnitTest.dependencies { implementation(libs.bundles.test) } } diff --git a/feature-detail/src/commonTest/kotlin/com/codandotv/streamplayerapp/feature_detail/DetailStreamRepositoryTest.kt b/feature-detail/src/androidUnitTest/kotlin/com/codandotv/streamplayerapp/feature_detail/DetailStreamRepositoryTest.kt similarity index 100% rename from feature-detail/src/commonTest/kotlin/com/codandotv/streamplayerapp/feature_detail/DetailStreamRepositoryTest.kt rename to feature-detail/src/androidUnitTest/kotlin/com/codandotv/streamplayerapp/feature_detail/DetailStreamRepositoryTest.kt diff --git a/feature-detail/src/commonTest/kotlin/com/codandotv/streamplayerapp/feature_detail/DetailStreamUseCaseTest.kt b/feature-detail/src/androidUnitTest/kotlin/com/codandotv/streamplayerapp/feature_detail/DetailStreamUseCaseTest.kt similarity index 100% rename from feature-detail/src/commonTest/kotlin/com/codandotv/streamplayerapp/feature_detail/DetailStreamUseCaseTest.kt rename to feature-detail/src/androidUnitTest/kotlin/com/codandotv/streamplayerapp/feature_detail/DetailStreamUseCaseTest.kt diff --git a/feature-detail/src/commonTest/kotlin/com/codandotv/streamplayerapp/feature_detail/DetailStreamViewModelTest.kt b/feature-detail/src/androidUnitTest/kotlin/com/codandotv/streamplayerapp/feature_detail/DetailStreamViewModelTest.kt similarity index 100% rename from feature-detail/src/commonTest/kotlin/com/codandotv/streamplayerapp/feature_detail/DetailStreamViewModelTest.kt rename to feature-detail/src/androidUnitTest/kotlin/com/codandotv/streamplayerapp/feature_detail/DetailStreamViewModelTest.kt diff --git a/feature-detail/src/commonTest/kotlin/com/codandotv/streamplayerapp/feature_detail/InstantTaskCoroutinesExecutorRule.kt b/feature-detail/src/androidUnitTest/kotlin/com/codandotv/streamplayerapp/feature_detail/InstantTaskCoroutinesExecutorRule.kt similarity index 100% rename from feature-detail/src/commonTest/kotlin/com/codandotv/streamplayerapp/feature_detail/InstantTaskCoroutinesExecutorRule.kt rename to feature-detail/src/androidUnitTest/kotlin/com/codandotv/streamplayerapp/feature_detail/InstantTaskCoroutinesExecutorRule.kt diff --git a/feature-detail/src/commonTest/kotlin/com/codandotv/streamplayerapp/feature_detail/Shared.kt b/feature-detail/src/androidUnitTest/kotlin/com/codandotv/streamplayerapp/feature_detail/Shared.kt similarity index 100% rename from feature-detail/src/commonTest/kotlin/com/codandotv/streamplayerapp/feature_detail/Shared.kt rename to feature-detail/src/androidUnitTest/kotlin/com/codandotv/streamplayerapp/feature_detail/Shared.kt diff --git a/feature-detail/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_detail/di/DetailStreamModule.kt b/feature-detail/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_detail/di/DetailStreamModule.kt index ff8c0934..2a5e35db 100644 --- a/feature-detail/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_detail/di/DetailStreamModule.kt +++ b/feature-detail/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_detail/di/DetailStreamModule.kt @@ -10,7 +10,8 @@ import com.codandotv.streamplayerapp.feature_detail.domain.VideoStreamsUseCase import com.codandotv.streamplayerapp.feature_detail.domain.VideoStreamsUseCaseImpl import com.codandotv.streamplayerapp.feature_detail.presentation.screens.DetailStreamViewModel import kotlinx.coroutines.Dispatchers -import org.koin.androidx.viewmodel.dsl.viewModel +import kotlinx.coroutines.IO +import org.koin.core.module.dsl.viewModel import org.koin.core.parameter.parametersOf import org.koin.dsl.module diff --git a/feature-detail/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_detail/presentation/navigation/DetailStreamNavigation.kt b/feature-detail/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_detail/presentation/navigation/DetailStreamNavigation.kt index 172d5752..a52a01fb 100644 --- a/feature-detail/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_detail/presentation/navigation/DetailStreamNavigation.kt +++ b/feature-detail/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_detail/presentation/navigation/DetailStreamNavigation.kt @@ -8,10 +8,9 @@ import com.codandotv.streamplayerapp.core_navigation.routes.Routes.DETAIL_COMPLE import com.codandotv.streamplayerapp.core_navigation.routes.Routes.PARAM.ID import com.codandotv.streamplayerapp.feature_detail.di.DetailStreamModule import com.codandotv.streamplayerapp.feature_detail.presentation.screens.DetailStreamScreen -import org.koin.androidx.compose.koinViewModel import org.koin.compose.module.rememberKoinModules +import org.koin.compose.viewmodel.koinViewModel import org.koin.core.annotation.KoinExperimentalAPI -import org.koin.core.context.unloadKoinModules import org.koin.core.parameter.parametersOf internal const val DEFAULT_ID = "0" diff --git a/feature-detail/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_detail/presentation/screens/DetailStreamsScreen.kt b/feature-detail/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_detail/presentation/screens/DetailStreamsScreen.kt index 0891f603..df3f2bfa 100644 --- a/feature-detail/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_detail/presentation/screens/DetailStreamsScreen.kt +++ b/feature-detail/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_detail/presentation/screens/DetailStreamsScreen.kt @@ -1,6 +1,5 @@ package com.codandotv.streamplayerapp.feature_detail.presentation.screens -import androidx.activity.compose.BackHandler import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Spacer @@ -15,11 +14,11 @@ import androidx.compose.material.icons.filled.Add import androidx.compose.material.icons.filled.PlayArrow import androidx.compose.material3.ButtonDefaults import androidx.compose.material3.CircularProgressIndicator -import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Scaffold import androidx.compose.material3.Text import androidx.compose.runtime.Composable +import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember @@ -30,7 +29,6 @@ import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.em import androidx.compose.ui.unit.sp -import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.navigation.NavController import com.codandotv.streamplayerapp.core_shared_ui.widget.SharingStreamPlatform import com.codandotv.streamplayerapp.feature_detail.domain.DetailStream @@ -40,7 +38,7 @@ import com.codandotv.streamplayerapp.feature_detail.presentation.widget.DetailSt import com.codandotv.streamplayerapp.feature_detail.presentation.widget.DetailStreamRowHeader import com.codandotv.streamplayerapp.feature_detail.presentation.widget.DetailStreamToolbar import org.jetbrains.compose.resources.stringResource -import org.koin.androidx.compose.koinViewModel +import org.koin.compose.viewmodel.koinViewModel import streamplayerapp_kmp.feature_detail.generated.resources.Res import streamplayerapp_kmp.feature_detail.generated.resources.detail_default_text_secondary_button import streamplayerapp_kmp.feature_detail.generated.resources.detail_watch_primary_button @@ -51,7 +49,7 @@ fun DetailStreamScreen( navController: NavController, onNavigateSearchScreen: () -> Unit = {}, ) { - val uiState by viewModel.uiState.collectAsStateWithLifecycle() + val uiState by viewModel.uiState.collectAsState() when (uiState) { is DetailStreamsUIState.DetailStreamsLoadedUIState -> { @@ -174,12 +172,12 @@ private fun SetupDetailScreen( showDialog.value = it }) } - BackHandler { + /*BackHandler { if (showDialog.value) { showDialog.value = false } else { navController.navigateUp() } - } + }*/ }) } \ No newline at end of file diff --git a/feature-detail/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_detail/presentation/widget/DetailStreamImagePreview.kt b/feature-detail/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_detail/presentation/widget/DetailStreamImagePreview.kt index 3d1fda7a..5d17576d 100644 --- a/feature-detail/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_detail/presentation/widget/DetailStreamImagePreview.kt +++ b/feature-detail/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_detail/presentation/widget/DetailStreamImagePreview.kt @@ -15,8 +15,8 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.unit.dp -import coil.compose.AsyncImage import com.codandotv.streamplayerapp.core_shared_ui.widget.PlayerComponentPlatform +import com.codandotv.streamplayerapp.core_shared_ui.widget.WebImage import com.codandotv.streamplayerapp.feature_detail.presentation.screens.DetailStreamsUIState.DetailStreamsLoadedUIState import org.jetbrains.compose.resources.painterResource import streamplayerapp_kmp.feature_detail.generated.resources.Res @@ -41,8 +41,8 @@ fun DetailStreamImagePreview( videoId = uiState.videoId ?: "" ) } else { - AsyncImage( - model = uiState.detailStream.url, + WebImage( + imageUrl = uiState.detailStream.url, contentScale = ContentScale.FillBounds, contentDescription = uiState.detailStream.tagline, modifier = Modifier diff --git a/feature-list-streams/build.gradle.kts b/feature-list-streams/build.gradle.kts index 08f6d7b9..53b2eb8c 100644 --- a/feature-list-streams/build.gradle.kts +++ b/feature-list-streams/build.gradle.kts @@ -27,13 +27,11 @@ kotlin { implementation(libs.navigation.compose) - implementation(libs.coil) - implementation(libs.ktor.client.content.serialization.json) implementation(libs.ktor.client.content.negotiation) - implementation(libs.koin.core) implementation(libs.koin.compose) + implementation(libs.koin.compose.viewmodel) } } } \ No newline at end of file diff --git a/feature-list-streams/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_list_streams/list/di/ListStreamModule.kt b/feature-list-streams/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_list_streams/list/di/ListStreamModule.kt index e6a3a5af..ffbfb75e 100644 --- a/feature-list-streams/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_list_streams/list/di/ListStreamModule.kt +++ b/feature-list-streams/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_list_streams/list/di/ListStreamModule.kt @@ -13,7 +13,7 @@ import com.codandotv.streamplayerapp.feature_list_streams.list.domain.ListStream import com.codandotv.streamplayerapp.feature_list_streams.list.domain.ListStreamUseCase import com.codandotv.streamplayerapp.feature_list_streams.list.domain.ListStreamUseCaseImpl import com.codandotv.streamplayerapp.feature_list_streams.list.presentation.screens.ListStreamViewModel -import org.koin.androidx.viewmodel.dsl.viewModel +import org.koin.core.module.dsl.viewModel import org.koin.dsl.module object ListStreamModule { diff --git a/feature-list-streams/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_list_streams/list/presentation/screens/ListStreamsScreen.kt b/feature-list-streams/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_list_streams/list/presentation/screens/ListStreamsScreen.kt index dbbcf312..c8691035 100644 --- a/feature-list-streams/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_list_streams/list/presentation/screens/ListStreamsScreen.kt +++ b/feature-list-streams/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_list_streams/list/presentation/screens/ListStreamsScreen.kt @@ -1,6 +1,5 @@ package com.codandotv.streamplayerapp.feature_list_streams.list.presentation.screens -import android.annotation.SuppressLint import androidx.compose.foundation.background import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column @@ -17,24 +16,20 @@ import androidx.compose.material3.Scaffold import androidx.compose.material3.TopAppBarDefaults import androidx.compose.material3.rememberTopAppBarState import androidx.compose.runtime.Composable -import androidx.compose.runtime.DisposableEffect +import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.input.nestedscroll.nestedScroll -import androidx.compose.ui.platform.LocalLifecycleOwner import androidx.compose.ui.unit.dp -import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.navigation.NavController -import androidx.navigation.compose.rememberNavController import com.codandotv.streamplayerapp.core_navigation.bottomnavigation.StreamPlayerBottomNavigation import com.codandotv.streamplayerapp.core_shared_ui.widget.StreamPlayerTopBar import com.codandotv.streamplayerapp.feature_list_streams.list.presentation.widgets.HighlightBanner import com.codandotv.streamplayerapp.core_shared_ui.widget.StreamsCarousel -import org.koin.androidx.compose.koinViewModel +import org.koin.compose.viewmodel.koinViewModel @Suppress("LongParameterList") -@SuppressLint("UnusedMaterial3ScaffoldPaddingParameter") @OptIn(ExperimentalMaterial3Api::class) @Composable fun ListStreamsScreen( @@ -45,7 +40,7 @@ fun ListStreamsScreen( onNavigateSearchScreen: () -> Unit = {}, profilePicture: String ) { - val uiState by viewModel.uiState.collectAsStateWithLifecycle() + val uiState by viewModel.uiState.collectAsState() val scrollBehavior = TopAppBarDefaults.exitUntilCollapsedScrollBehavior(rememberTopAppBarState()) val baseScrollState = rememberScrollState() diff --git a/feature-list-streams/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_list_streams/list/presentation/widgets/HighlightBanner.kt b/feature-list-streams/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_list_streams/list/presentation/widgets/HighlightBanner.kt index 662752e3..8b411424 100644 --- a/feature-list-streams/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_list_streams/list/presentation/widgets/HighlightBanner.kt +++ b/feature-list-streams/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_list_streams/list/presentation/widgets/HighlightBanner.kt @@ -31,31 +31,24 @@ import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp -import coil.compose.AsyncImage -import com.codandotv.streamplayerapp.feature.list.streams.R -import com.codandotv.streamplayerapp.feature_list_streams.core.ContentType +import com.codandotv.streamplayerapp.core_shared_ui.widget.WebImage import com.codandotv.streamplayerapp.feature_list_streams.list.domain.model.HighlightBanner import com.codandotv.streamplayerapp.feature_list_streams.list.domain.model.IconAndTextInfo import org.jetbrains.compose.resources.StringResource import org.jetbrains.compose.resources.painterResource import org.jetbrains.compose.resources.stringResource -import streamplayerapp_kmp.core_shared_ui.generated.resources.app_name import streamplayerapp_kmp.core_shared_ui.generated.resources.ic_add import streamplayerapp_kmp.core_shared_ui.generated.resources.ic_info import streamplayerapp_kmp.core_shared_ui.generated.resources.ic_netflix import streamplayerapp_kmp.core_shared_ui.generated.resources.ic_play import streamplayerapp_kmp.core_shared_ui.generated.resources.icon_netflix import streamplayerapp_kmp.feature_list_streams.generated.resources.Res -import streamplayerapp_kmp.feature_list_streams.generated.resources.ic_top_10 import streamplayerapp_kmp.feature_list_streams.generated.resources.list_highligh_banner_content -import streamplayerapp_kmp.feature_list_streams.generated.resources.list_highlight_banner_add -import streamplayerapp_kmp.feature_list_streams.generated.resources.list_highlight_banner_info import streamplayerapp_kmp.feature_list_streams.generated.resources.list_highlight_banner_watch import streamplayerapp_kmp.feature_list_streams.generated.resources.list_icon_add import streamplayerapp_kmp.feature_list_streams.generated.resources.list_icon_highligh_banner_ranking import streamplayerapp_kmp.feature_list_streams.generated.resources.list_icon_info import streamplayerapp_kmp.feature_list_streams.generated.resources.list_icon_play -import com.codandotv.streamplayerapp.core.shared.ui.R as RSharedUI import streamplayerapp_kmp.core_shared_ui.generated.resources.Res as SharedRes @Suppress("MagicNumber") @@ -90,9 +83,9 @@ fun HighlightBanner(modifier: Modifier = Modifier, data: HighlightBanner?) { @Composable fun ContentImage(modifier: Modifier = Modifier, imageUrl: String) { - AsyncImage( + WebImage( modifier = modifier.fillMaxSize(), - model = imageUrl, + imageUrl = imageUrl, contentScale = ContentScale.Crop, contentDescription = stringResource(Res.string.list_highligh_banner_content) ) diff --git a/feature-profile/build.gradle.kts b/feature-profile/build.gradle.kts index 781d5b15..c6695c53 100644 --- a/feature-profile/build.gradle.kts +++ b/feature-profile/build.gradle.kts @@ -20,13 +20,12 @@ kotlin { implementation(compose.ui) implementation(compose.components.resources) - implementation(libs.coil) - implementation(libs.ktor.client.content.serialization.json) implementation(libs.ktor.client.content.negotiation) implementation(libs.koin.core) implementation(libs.koin.compose) + implementation(libs.koin.compose.viewmodel) } } } \ No newline at end of file diff --git a/feature-profile/src/commonMain/kotlin/com/codandotv/streamplayerapp/profile/di/ProfilePickerStreamModule.kt b/feature-profile/src/commonMain/kotlin/com/codandotv/streamplayerapp/profile/di/ProfilePickerStreamModule.kt index b026f91b..127e6887 100644 --- a/feature-profile/src/commonMain/kotlin/com/codandotv/streamplayerapp/profile/di/ProfilePickerStreamModule.kt +++ b/feature-profile/src/commonMain/kotlin/com/codandotv/streamplayerapp/profile/di/ProfilePickerStreamModule.kt @@ -7,7 +7,7 @@ import com.codandotv.streamplayerapp.profile.data.ProfilePickerStreamServiceImpl import com.codandotv.streamplayerapp.profile.domain.ProfilePickerStreamUseCase import com.codandotv.streamplayerapp.profile.domain.ProfilePickerStreamUseCaseImpl import com.codandotv.streamplayerapp.profile.presentation.screens.ProfilePickerStreamViewModel -import org.koin.androidx.viewmodel.dsl.viewModel +import org.koin.core.module.dsl.viewModel import org.koin.dsl.module object ProfilePickerStreamModule { diff --git a/feature-profile/src/commonMain/kotlin/com/codandotv/streamplayerapp/profile/presentation/screens/ProfilePickerStreamScreen.kt b/feature-profile/src/commonMain/kotlin/com/codandotv/streamplayerapp/profile/presentation/screens/ProfilePickerStreamScreen.kt index 396526c5..5dfa8488 100644 --- a/feature-profile/src/commonMain/kotlin/com/codandotv/streamplayerapp/profile/presentation/screens/ProfilePickerStreamScreen.kt +++ b/feature-profile/src/commonMain/kotlin/com/codandotv/streamplayerapp/profile/presentation/screens/ProfilePickerStreamScreen.kt @@ -1,28 +1,20 @@ package com.codandotv.streamplayerapp.profile.presentation.screens -import android.annotation.SuppressLint import androidx.compose.animation.animateColorAsState import androidx.compose.animation.core.animateDpAsState import androidx.compose.animation.core.animateFloatAsState import androidx.compose.animation.core.animateIntOffsetAsState import androidx.compose.animation.core.tween import androidx.compose.foundation.background -import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Scaffold import androidx.compose.runtime.Composable -import androidx.compose.runtime.DisposableEffect +import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color -import androidx.compose.ui.platform.LocalLifecycleOwner import androidx.compose.ui.unit.IntOffset import androidx.compose.ui.unit.dp -import androidx.lifecycle.LifecycleOwner -import androidx.lifecycle.compose.collectAsStateWithLifecycle -import com.codandotv.streamplayerapp.core_shared_ui.theme.ThemePreview -import com.codandotv.streamplayerapp.core_shared_ui.theme.ThemePreviews -import com.codandotv.streamplayerapp.feature.profile.R import com.codandotv.streamplayerapp.profile.domain.ProfileStream import com.codandotv.streamplayerapp.profile.presentation.widget.LoadScreen import com.codandotv.streamplayerapp.profile.presentation.widget.ProfilePickerOpacityLayer @@ -31,7 +23,7 @@ import com.codandotv.streamplayerapp.profile.presentation.widget.ProfilePickerSe import com.codandotv.streamplayerapp.profile.presentation.widget.ProfilePickerStreamToolbar import com.codandotv.streamplayerapp.profile.presentation.widget.dpToPx import org.jetbrains.compose.resources.stringResource -import org.koin.androidx.compose.koinViewModel +import org.koin.compose.viewmodel.koinViewModel import streamplayerapp_kmp.feature_profile.generated.resources.Res import streamplayerapp_kmp.feature_profile.generated.resources.profile_animation_background_opacity import streamplayerapp_kmp.feature_profile.generated.resources.profile_animation_selected_image_position @@ -43,7 +35,7 @@ fun ProfilePickerStreamScreen( viewModel: ProfilePickerStreamViewModel = koinViewModel(), onNavigateListStreams: (String) -> Unit = {}, ) { - val uiState by viewModel.uiState.collectAsStateWithLifecycle() + val uiState by viewModel.uiState.collectAsState() if (uiState.isLoading) { LoadScreen() @@ -64,8 +56,6 @@ fun ProfilePickerStreamScreen( } @Suppress("LongMethod", "LongParameterList") -@OptIn(ExperimentalMaterial3Api::class) -@SuppressLint("UnusedMaterial3ScaffoldPaddingParameter") @Composable fun SetupProfilePickerScreen( uiState: ProfilePickerStreamsUIState, diff --git a/feature-profile/src/commonMain/kotlin/com/codandotv/streamplayerapp/profile/presentation/widget/ProfilePickerProfilesGrid.kt b/feature-profile/src/commonMain/kotlin/com/codandotv/streamplayerapp/profile/presentation/widget/ProfilePickerProfilesGrid.kt index 0fe678f4..d207b644 100644 --- a/feature-profile/src/commonMain/kotlin/com/codandotv/streamplayerapp/profile/presentation/widget/ProfilePickerProfilesGrid.kt +++ b/feature-profile/src/commonMain/kotlin/com/codandotv/streamplayerapp/profile/presentation/widget/ProfilePickerProfilesGrid.kt @@ -18,10 +18,10 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.draw.alpha import androidx.compose.ui.draw.clip import androidx.compose.ui.graphics.graphicsLayer +import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.unit.IntOffset import androidx.compose.ui.unit.dp -import coil.compose.AsyncImage -import com.codandotv.streamplayerapp.feature.profile.R +import com.codandotv.streamplayerapp.core_shared_ui.widget.WebImage import com.codandotv.streamplayerapp.profile.domain.ProfileStream import com.codandotv.streamplayerapp.profile.presentation.screens.ProfilePickerStreamsUIState import org.jetbrains.compose.resources.painterResource @@ -93,12 +93,14 @@ private fun ProfileItem( profileItemPosition } ) { - AsyncImage( - model = profile.imageUrl, - placeholder = painterResource(Res.drawable.image_placeholder), + WebImage( + imageUrl = profile.imageUrl, + //placeholder = painterResource(Res.drawable.image_placeholder), contentDescription = stringResource( Res.string.profile_current_profile_name, - ).format(profile.name), + profile.name + ), + contentScale = ContentScale.Fit, modifier = Modifier .clip(RoundedCornerShape(5)) .alpha(if (selectedItem == profile) selectedImageAlpha else 1f) diff --git a/feature-profile/src/commonMain/kotlin/com/codandotv/streamplayerapp/profile/presentation/widget/ProfilePickerSelectedProfileContainer.kt b/feature-profile/src/commonMain/kotlin/com/codandotv/streamplayerapp/profile/presentation/widget/ProfilePickerSelectedProfileContainer.kt index e42b7786..652b8181 100644 --- a/feature-profile/src/commonMain/kotlin/com/codandotv/streamplayerapp/profile/presentation/widget/ProfilePickerSelectedProfileContainer.kt +++ b/feature-profile/src/commonMain/kotlin/com/codandotv/streamplayerapp/profile/presentation/widget/ProfilePickerSelectedProfileContainer.kt @@ -1,6 +1,5 @@ package com.codandotv.streamplayerapp.profile.presentation.widget -import android.annotation.SuppressLint import androidx.compose.foundation.layout.BoxWithConstraints import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxSize @@ -13,20 +12,13 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.alpha import androidx.compose.ui.draw.clip -import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.IntOffset import androidx.compose.ui.unit.dp -import coil.compose.AsyncImage -import coil.request.ImageRequest +import com.codandotv.streamplayerapp.core_shared_ui.widget.WebImage import com.codandotv.streamplayerapp.profile.presentation.screens.ProfilePickerStreamsUIState -import org.jetbrains.compose.resources.painterResource -import org.jetbrains.compose.resources.stringResource -import streamplayerapp_kmp.feature_profile.generated.resources.Res -import streamplayerapp_kmp.feature_profile.generated.resources.image_placeholder -import streamplayerapp_kmp.feature_profile.generated.resources.profile_current_profile_name -@SuppressLint("UnusedBoxWithConstraintsScope") @Suppress("MagicNumber") @Composable fun ProfilePickerSelectedProfileContainer( @@ -46,22 +38,26 @@ fun ProfilePickerSelectedProfileContainer( modifier = Modifier .offset { offsetSelectedProfileImage } ) { - AsyncImage( - model = ImageRequest.Builder(LocalContext.current) - .data(selectedItem?.imageUrl) - .crossfade(true) - .build(), - placeholder = painterResource(Res.drawable.image_placeholder), - contentDescription = selectedItem?.let { - stringResource( - Res.string.profile_current_profile_name, - ).format(it.name) - }, - modifier = Modifier - .clip(RoundedCornerShape(5)) - .size(animatedSizeImage) - .alpha(centerImageAlpha) - ) + selectedItem?.imageUrl?.let { imageUrl -> + WebImage( + imageUrl =imageUrl, /*ImageRequest.Builder(LocalPlatformContext.current) + .data(selectedItem?.imageUrl) + .crossfade(true) + .build()*/ + contentScale = ContentScale.Fit, + contentDescription = null, + //placeholder = painterResource(Res.drawable.image_placeholder), + /*contentDescription = selectedItem?.let { + stringResource( + Res.string.profile_current_profile_name, + ).format(it.name) + },*/ + modifier = Modifier + .clip(RoundedCornerShape(5)) + .size(animatedSizeImage) + .alpha(centerImageAlpha) + ) + } } } } diff --git a/feature-search/build.gradle.kts b/feature-search/build.gradle.kts index 12553d73..c7562960 100644 --- a/feature-search/build.gradle.kts +++ b/feature-search/build.gradle.kts @@ -27,14 +27,13 @@ kotlin { implementation(libs.navigation.compose) - - implementation(libs.coil) implementation(libs.ktor.client.content.serialization.json) implementation(libs.ktor.client.content.negotiation) implementation(libs.koin.core) implementation(libs.koin.compose) + implementation(libs.koin.compose.viewmodel) } } } \ No newline at end of file diff --git a/feature-search/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_search/di/SearchModule.kt b/feature-search/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_search/di/SearchModule.kt index 4d6215cd..7848bf61 100644 --- a/feature-search/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_search/di/SearchModule.kt +++ b/feature-search/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_search/di/SearchModule.kt @@ -17,7 +17,7 @@ import com.codandotv.streamplayerapp.feature_search.domain.MostPopularMoviesUseC import com.codandotv.streamplayerapp.feature_search.domain.SearchUseCase import com.codandotv.streamplayerapp.feature_search.domain.SearchUseCaseImpl import com.codandotv.streamplayerapp.feature_search.presentation.screens.SearchViewModel -import org.koin.androidx.viewmodel.dsl.viewModel +import org.koin.core.module.dsl.viewModel import org.koin.dsl.module object SearchModule { diff --git a/feature-search/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_search/presentation/screens/SearchScreen.kt b/feature-search/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_search/presentation/screens/SearchScreen.kt index 61b389c6..379193e8 100644 --- a/feature-search/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_search/presentation/screens/SearchScreen.kt +++ b/feature-search/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_search/presentation/screens/SearchScreen.kt @@ -1,6 +1,5 @@ package com.codandotv.streamplayerapp.feature_search.presentation.screens -import androidx.activity.compose.BackHandler import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxSize @@ -11,17 +10,14 @@ import androidx.compose.material3.CircularProgressIndicator import androidx.compose.material3.Scaffold import androidx.compose.material3.Text import androidx.compose.runtime.Composable -import androidx.compose.runtime.DisposableEffect import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color -import androidx.compose.ui.platform.LocalLifecycleOwner import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp -import androidx.lifecycle.LifecycleOwner import androidx.navigation.NavController import com.codandotv.streamplayerapp.core_navigation.extensions.goBack import com.codandotv.streamplayerapp.feature_search.domain.mapper.toSearchStreamCardModel @@ -30,7 +26,7 @@ import com.codandotv.streamplayerapp.feature_search.presentation.widgets.Searcha import com.codandotv.streamplayerapp.feature_search.presentation.widgets.StreamsEmpty import com.codandotv.streamplayerapp.feature_search.presentation.widgets.StreamsError import org.jetbrains.compose.resources.stringResource -import org.koin.androidx.compose.koinViewModel +import org.koin.compose.viewmodel.koinViewModel import streamplayerapp_kmp.feature_search.generated.resources.Res import streamplayerapp_kmp.feature_search.generated.resources.search_list_describle @@ -141,8 +137,4 @@ private fun SetupSearchScreen( } } } - BackHandler { - navController.goBack() - } - } \ No newline at end of file diff --git a/feature-search/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_search/presentation/widgets/SearchStreamCard.kt b/feature-search/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_search/presentation/widgets/SearchStreamCard.kt index cc17a450..f496d701 100644 --- a/feature-search/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_search/presentation/widgets/SearchStreamCard.kt +++ b/feature-search/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_search/presentation/widgets/SearchStreamCard.kt @@ -24,7 +24,7 @@ import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp -import coil.compose.AsyncImage +import com.codandotv.streamplayerapp.core_shared_ui.widget.WebImage @Suppress("MagicNumber") @Composable @@ -79,8 +79,8 @@ fun ImageStream(modifier: Modifier, url: String) { shape = RoundedCornerShape(4.dp), modifier = modifier ) { - AsyncImage( - model = url, + WebImage( + imageUrl = url, contentScale = ContentScale.FillBounds, contentDescription = "", modifier = Modifier.fillMaxSize() diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 013379aa..db8a5765 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,7 +1,7 @@ [versions] kotlin = "2.0.21" -android_gradle_plugin = "8.2.2" -koin = "3.5.3" +android_gradle_plugin = "8.8.0" +koin = "4.0.1" ksp = "2.0.21-1.0.27" dokka = "1.9.10" @@ -22,7 +22,7 @@ kotlinx-coroutines-test = "1.8.1" okhttp = "4.12.0" ktor = "3.0.1" -coil = "2.3.0" +coil = "3.1.0" lottie = "5.2.0" #Room Database @@ -42,7 +42,8 @@ com-google-devtools-ksp-gradle-plugin = { module = "com.google.devtools.ksp:com. kover-gradle-plugin = { module = "org.jetbrains.kotlinx:kover-gradle-plugin", version.ref = "kover" } -coil = { group = "io.coil-kt", name = "coil-compose", version.ref = "coil" } +coil = { group = "io.coil-kt.coil3", name = "coil-compose", version.ref = "coil" } +coil-network-ktor3 = { module = "io.coil-kt.coil3:coil-network-ktor3", version.ref="coil"} lottie = { group = "com.airbnb.android", name = "lottie-compose", version.ref = "lottie" } @@ -60,7 +61,8 @@ paging-compose = { module = "app.cash.paging:paging-compose-common", version.ref koin_test = { group = "io.insert-koin", name = "koin-test-junit4", version.ref = "koin" } koin_android = { group = "io.insert-koin", name = "koin-android", version.ref = "koin" } koin_core = { group = "io.insert-koin", name = "koin-core", version.ref = "koin" } -koin_compose = { group = "io.insert-koin", name = "koin-androidx-compose", version.ref = "koin" } +koin_compose = { module = "io.insert-koin:koin-compose", version.ref = "koin" } +koin_compose_viewmodel = { module = "io.insert-koin:koin-compose-viewmodel", version.ref = "koin" } #Networking okhttp = { group = "com.squareup.okhttp3", name = "okhttp", version.ref = "okhttp" } @@ -70,6 +72,7 @@ ktor_client_core = { module = "io.ktor:ktor-client-core", version.ref = "ktor" } ktor_client_okhttp = { module = "io.ktor:ktor-client-okhttp", version.ref = "ktor" } ktor_client_logger = { module = "io.ktor:ktor-client-logging", version.ref = "ktor" } ktor_client_auth = { module = "io.ktor:ktor-client-auth", version.ref = "ktor" } +ktor_client_darwin = { group = "io.ktor", name = "ktor-client-darwin", version.ref = "ktor" } ktor_client_content_negotiation = { module = "io.ktor:ktor-client-content-negotiation", version.ref = "ktor" } ktor_client_content_serialization_json = { module = "io.ktor:ktor-serialization-kotlinx-json", version.ref = "ktor" } diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 62f495df..960f6e70 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,7 @@ +#Wed Feb 19 11:41:18 BRT 2025 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.2-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.2-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME From 706453ba406f2ca37acd3e1a0900c7fadf43b034 Mon Sep 17 00:00:00 2001 From: Gabriel Bronzatti Moro Date: Thu, 20 Feb 2025 07:13:33 -0300 Subject: [PATCH 85/96] =?UTF-8?q?=F0=9F=94=A5=F0=9F=94=A5=F0=9F=94=A5?= =?UTF-8?q?=F0=9F=94=A5=20Compile=20on=20iOS?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build-logic/src/main/java/Config.kt | 2 +- .../presentation/MainActivity.kt | 13 +------------ .../StreamPlayerApp.kt | 14 ++++++++++++++ .../com.codandotv.streamplayerapp/di/AppModule.kt | 5 ++--- .../navigation/NavigationGraph.kt | 3 --- .../MainViewController.kt | 7 +++++++ core-networking/build.gradle.kts | 4 ++++ .../HttpClientEngineProviderPlatform.kt | 4 ---- .../HttpClientEngineProviderPlatform.ios.kt | 8 ++++++++ .../core_shared_ui/widget/StreamsCard.kt | 5 +---- .../widget/PlayerComponentPlatform.ios.kt | 7 +++++++ .../feature_list_streams/core/ContentType.kt | 2 -- .../navigation/ListStreamsNavigation.kt | 6 +----- .../profile/data/ProfilePickerStreamRepository.kt | 3 +-- .../navigation/SearchStreamNavigation.kt | 6 +----- .../presentation/widgets/SearchCarousel.kt | 8 +++----- gradle/libs.versions.toml | 10 +++++----- iosApp/iosApp/ContentView.swift | 2 +- 18 files changed, 57 insertions(+), 52 deletions(-) create mode 100644 composeApp/src/commonMain/kotlin/com.codandotv.streamplayerapp/StreamPlayerApp.kt create mode 100644 composeApp/src/iosMain/kotlin/com.codandotv.streamplayerapp/MainViewController.kt create mode 100644 core-networking/src/iosMain/kotlin/com/codandotv/streamplayerapp/core_networking/HttpClientEngineProviderPlatform.ios.kt create mode 100644 core-shared-ui/src/iosMain/kotlin/com/codandotv/streamplayerapp/core_shared_ui/widget/PlayerComponentPlatform.ios.kt diff --git a/build-logic/src/main/java/Config.kt b/build-logic/src/main/java/Config.kt index 3738e1fd..ede08d28 100644 --- a/build-logic/src/main/java/Config.kt +++ b/build-logic/src/main/java/Config.kt @@ -2,7 +2,7 @@ object Config { const val appName = "streamplayerapp" const val applicationId = "com.codandotv.$appName" const val compileSdkVersion = 35 - const val minSdkVersion = 24 + const val minSdkVersion = 28 const val targetSdkVersion = 35 const val versionName = "1.0" const val versionCode = 1 diff --git a/composeApp/src/androidMain/kotlin/com.codandotv.streamplayerapp/presentation/MainActivity.kt b/composeApp/src/androidMain/kotlin/com.codandotv.streamplayerapp/presentation/MainActivity.kt index 2ae2e145..d3824ddd 100644 --- a/composeApp/src/androidMain/kotlin/com.codandotv.streamplayerapp/presentation/MainActivity.kt +++ b/composeApp/src/androidMain/kotlin/com.codandotv.streamplayerapp/presentation/MainActivity.kt @@ -3,10 +3,7 @@ package com.codandotv.streamplayerapp.presentation import android.os.Bundle import androidx.activity.ComponentActivity import androidx.activity.compose.setContent -import androidx.compose.runtime.Composable -import androidx.navigation.compose.rememberNavController -import com.codandotv.streamplayerapp.core_shared_ui.theme.StreamPlayerTheme -import com.codandotv.streamplayerapp.navigation.NavigationGraph +import com.codandotv.streamplayerapp.StreamPlayerApp class MainActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { @@ -16,11 +13,3 @@ class MainActivity : ComponentActivity() { } } } - -@Composable -fun StreamPlayerApp() { - StreamPlayerTheme { - val navController = rememberNavController() - NavigationGraph(navController = navController) - } -} diff --git a/composeApp/src/commonMain/kotlin/com.codandotv.streamplayerapp/StreamPlayerApp.kt b/composeApp/src/commonMain/kotlin/com.codandotv.streamplayerapp/StreamPlayerApp.kt new file mode 100644 index 00000000..98362c68 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/com.codandotv.streamplayerapp/StreamPlayerApp.kt @@ -0,0 +1,14 @@ +package com.codandotv.streamplayerapp + +import androidx.compose.runtime.Composable +import androidx.navigation.compose.rememberNavController +import com.codandotv.streamplayerapp.core_shared_ui.theme.StreamPlayerTheme +import com.codandotv.streamplayerapp.navigation.NavigationGraph + +@Composable +fun StreamPlayerApp() { + StreamPlayerTheme { + val navController = rememberNavController() + NavigationGraph(navController = navController) + } +} diff --git a/composeApp/src/commonMain/kotlin/com.codandotv.streamplayerapp/di/AppModule.kt b/composeApp/src/commonMain/kotlin/com.codandotv.streamplayerapp/di/AppModule.kt index 6fcc0006..d904287c 100644 --- a/composeApp/src/commonMain/kotlin/com.codandotv.streamplayerapp/di/AppModule.kt +++ b/composeApp/src/commonMain/kotlin/com.codandotv.streamplayerapp/di/AppModule.kt @@ -1,16 +1,15 @@ package com.codandotv.streamplayerapp.di -import android.content.res.Resources import com.codandotv.streamplayerapp.core_local_storage.di.LocalStorageModule import com.codandotv.streamplayerapp.core_networking.di.NetworkModule import com.codandotv.streamplayerapp.core_shared.qualifier.QualifierDispatcherIO import kotlinx.coroutines.Dispatchers -import org.koin.android.ext.koin.androidContext +import kotlinx.coroutines.IO import org.koin.dsl.module object AppModule { private val module = module { - single { androidContext().resources } +// single { androidContext().resources } single(QualifierDispatcherIO) { Dispatchers.IO } } val list = module + NetworkModule.module + LocalStorageModule.module diff --git a/composeApp/src/commonMain/kotlin/com.codandotv.streamplayerapp/navigation/NavigationGraph.kt b/composeApp/src/commonMain/kotlin/com.codandotv.streamplayerapp/navigation/NavigationGraph.kt index 23d9c4cc..63251780 100644 --- a/composeApp/src/commonMain/kotlin/com.codandotv.streamplayerapp/navigation/NavigationGraph.kt +++ b/composeApp/src/commonMain/kotlin/com.codandotv.streamplayerapp/navigation/NavigationGraph.kt @@ -1,7 +1,5 @@ package com.codandotv.streamplayerapp.navigation -import android.annotation.SuppressLint -import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.Scaffold import androidx.compose.material3.Text import androidx.compose.runtime.Composable @@ -42,7 +40,6 @@ fun NavGraphBuilder.temporaryFun(route: String, navController: NavController) { } } -@SuppressLint("UnusedMaterial3ScaffoldPaddingParameter") @Composable fun example(navController: NavController, route: String) { Scaffold( diff --git a/composeApp/src/iosMain/kotlin/com.codandotv.streamplayerapp/MainViewController.kt b/composeApp/src/iosMain/kotlin/com.codandotv.streamplayerapp/MainViewController.kt new file mode 100644 index 00000000..2332ac68 --- /dev/null +++ b/composeApp/src/iosMain/kotlin/com.codandotv.streamplayerapp/MainViewController.kt @@ -0,0 +1,7 @@ +package com.codandotv.streamplayerapp + +import androidx.compose.ui.window.ComposeUIViewController + +fun MainViewController() = ComposeUIViewController { + StreamPlayerApp() +} \ No newline at end of file diff --git a/core-networking/build.gradle.kts b/core-networking/build.gradle.kts index 4bea4636..367251c8 100644 --- a/core-networking/build.gradle.kts +++ b/core-networking/build.gradle.kts @@ -46,5 +46,9 @@ kotlin { implementation(libs.ktor.client.okhttp) } + + iosMain.dependencies { + implementation(libs.ktor.client.darwin) + } } } \ No newline at end of file diff --git a/core-networking/src/commonMain/kotlin/com/codandotv/streamplayerapp/core_networking/HttpClientEngineProviderPlatform.kt b/core-networking/src/commonMain/kotlin/com/codandotv/streamplayerapp/core_networking/HttpClientEngineProviderPlatform.kt index a3206d95..40eb9de0 100644 --- a/core-networking/src/commonMain/kotlin/com/codandotv/streamplayerapp/core_networking/HttpClientEngineProviderPlatform.kt +++ b/core-networking/src/commonMain/kotlin/com/codandotv/streamplayerapp/core_networking/HttpClientEngineProviderPlatform.kt @@ -1,9 +1,5 @@ -@file:Suppress("EXPECT_AND_ACTUAL_IN_THE_SAME_MODULE") - package com.codandotv.streamplayerapp.core_networking import io.ktor.client.engine.HttpClientEngine -@Suppress("NO_ACTUAL_FOR_EXPECT") -//TODO: Another task expect fun httpClientEnginePlatform(): HttpClientEngine \ No newline at end of file diff --git a/core-networking/src/iosMain/kotlin/com/codandotv/streamplayerapp/core_networking/HttpClientEngineProviderPlatform.ios.kt b/core-networking/src/iosMain/kotlin/com/codandotv/streamplayerapp/core_networking/HttpClientEngineProviderPlatform.ios.kt new file mode 100644 index 00000000..9470ffa4 --- /dev/null +++ b/core-networking/src/iosMain/kotlin/com/codandotv/streamplayerapp/core_networking/HttpClientEngineProviderPlatform.ios.kt @@ -0,0 +1,8 @@ +package com.codandotv.streamplayerapp.core_networking + +import io.ktor.client.engine.HttpClientEngine +import io.ktor.client.engine.darwin.Darwin + +actual fun httpClientEnginePlatform(): HttpClientEngine { + return Darwin.create() +} \ No newline at end of file diff --git a/core-shared-ui/src/commonMain/kotlin/com/codandotv/streamplayerapp/core_shared_ui/widget/StreamsCard.kt b/core-shared-ui/src/commonMain/kotlin/com/codandotv/streamplayerapp/core_shared_ui/widget/StreamsCard.kt index 3dab50ff..e52ef1b4 100644 --- a/core-shared-ui/src/commonMain/kotlin/com/codandotv/streamplayerapp/core_shared_ui/widget/StreamsCard.kt +++ b/core-shared-ui/src/commonMain/kotlin/com/codandotv/streamplayerapp/core_shared_ui/widget/StreamsCard.kt @@ -1,6 +1,5 @@ package com.codandotv.streamplayerapp.core_shared_ui.widget -import android.os.Parcelable import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.padding @@ -12,7 +11,6 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.unit.dp import coil3.compose.AsyncImage -import kotlinx.parcelize.Parcelize @Composable fun StreamsCard( @@ -42,9 +40,8 @@ fun StreamsCard( } } -@Parcelize data class StreamsCardContent( val id: String, val url: String, val contentDescription: String, -) : Parcelable +) \ No newline at end of file diff --git a/core-shared-ui/src/iosMain/kotlin/com/codandotv/streamplayerapp/core_shared_ui/widget/PlayerComponentPlatform.ios.kt b/core-shared-ui/src/iosMain/kotlin/com/codandotv/streamplayerapp/core_shared_ui/widget/PlayerComponentPlatform.ios.kt new file mode 100644 index 00000000..9d06f243 --- /dev/null +++ b/core-shared-ui/src/iosMain/kotlin/com/codandotv/streamplayerapp/core_shared_ui/widget/PlayerComponentPlatform.ios.kt @@ -0,0 +1,7 @@ +package com.codandotv.streamplayerapp.core_shared_ui.widget + +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier + +@Composable +actual fun PlayerComponentPlatform(videoId: String, modifier: Modifier) = Unit \ No newline at end of file diff --git a/feature-list-streams/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_list_streams/core/ContentType.kt b/feature-list-streams/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_list_streams/core/ContentType.kt index 57dec607..38c98665 100644 --- a/feature-list-streams/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_list_streams/core/ContentType.kt +++ b/feature-list-streams/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_list_streams/core/ContentType.kt @@ -1,7 +1,5 @@ package com.codandotv.streamplayerapp.feature_list_streams.core -import androidx.annotation.StringRes -import com.codandotv.streamplayerapp.feature.list.streams.R import org.jetbrains.compose.resources.StringResource import streamplayerapp_kmp.feature_list_streams.generated.resources.Res import streamplayerapp_kmp.feature_list_streams.generated.resources.list_content_type_film diff --git a/feature-list-streams/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_list_streams/list/presentation/navigation/ListStreamsNavigation.kt b/feature-list-streams/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_list_streams/list/presentation/navigation/ListStreamsNavigation.kt index cb3a5a83..1ed73ef9 100644 --- a/feature-list-streams/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_list_streams/list/presentation/navigation/ListStreamsNavigation.kt +++ b/feature-list-streams/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_list_streams/list/presentation/navigation/ListStreamsNavigation.kt @@ -1,7 +1,5 @@ package com.codandotv.streamplayerapp.feature_list_streams.list.presentation.navigation -import androidx.activity.compose.BackHandler -import androidx.lifecycle.Lifecycle import androidx.navigation.NavGraphBuilder import androidx.navigation.NavHostController import androidx.navigation.compose.composable @@ -14,15 +12,13 @@ import com.codandotv.streamplayerapp.feature_list_streams.list.di.ListStreamModu import com.codandotv.streamplayerapp.feature_list_streams.list.presentation.screens.ListStreamsScreen import org.koin.compose.module.rememberKoinModules import org.koin.core.annotation.KoinExperimentalAPI -import org.koin.core.context.loadKoinModules -import org.koin.core.context.unloadKoinModules internal const val DEFAULT_ID = "" @OptIn(KoinExperimentalAPI::class) fun NavGraphBuilder.listStreamsNavGraph(navController: NavHostController) { composable(HOME_COMPLETE) { nav -> - BackHandler(true) {} + // BackHandler(true) {} rememberKoinModules { listOf(ListStreamModule.module) diff --git a/feature-profile/src/commonMain/kotlin/com/codandotv/streamplayerapp/profile/data/ProfilePickerStreamRepository.kt b/feature-profile/src/commonMain/kotlin/com/codandotv/streamplayerapp/profile/data/ProfilePickerStreamRepository.kt index 91e2404f..793670c5 100644 --- a/feature-profile/src/commonMain/kotlin/com/codandotv/streamplayerapp/profile/data/ProfilePickerStreamRepository.kt +++ b/feature-profile/src/commonMain/kotlin/com/codandotv/streamplayerapp/profile/data/ProfilePickerStreamRepository.kt @@ -1,6 +1,5 @@ package com.codandotv.streamplayerapp.profile.data -import android.util.Log import com.codandotv.streamplayerapp.core_networking.handleError.toFlow import com.codandotv.streamplayerapp.core_networking.handleError.toResult import com.codandotv.streamplayerapp.profile.domain.ProfileStream @@ -21,7 +20,7 @@ class ProfilePickerStreamRepositoryImpl( with(service.getProfiles()) { if (this.toResult().isFailure || this.toResult().getOrNull() == null) { - Log.i("ProfilePickerStreamRepositoryImpl", "versão off carregada") + // Log.i("ProfilePickerStreamRepositoryImpl", "versão off carregada") return flowOf(mockProfiles) } else { return this.toFlow().map { it.toProfiles() } diff --git a/feature-search/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_search/presentation/navigation/SearchStreamNavigation.kt b/feature-search/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_search/presentation/navigation/SearchStreamNavigation.kt index 134ad7e5..e8aa897e 100644 --- a/feature-search/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_search/presentation/navigation/SearchStreamNavigation.kt +++ b/feature-search/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_search/presentation/navigation/SearchStreamNavigation.kt @@ -1,7 +1,5 @@ package com.codandotv.streamplayerapp.feature_search.presentation.navigation -import androidx.activity.compose.BackHandler -import androidx.lifecycle.Lifecycle import androidx.navigation.NavGraphBuilder import androidx.navigation.NavHostController import androidx.navigation.compose.composable @@ -10,13 +8,11 @@ import com.codandotv.streamplayerapp.feature_search.di.SearchModule import com.codandotv.streamplayerapp.feature_search.presentation.screens.SearchScreen import org.koin.compose.module.rememberKoinModules import org.koin.core.annotation.KoinExperimentalAPI -import org.koin.core.context.loadKoinModules -import org.koin.core.context.unloadKoinModules @OptIn(KoinExperimentalAPI::class) fun NavGraphBuilder.searchStreamsNavGraph(navController: NavHostController) { composable(Routes.SEARCH) { _ -> - BackHandler(true) {} +// BackHandler(true) {} rememberKoinModules { listOf(SearchModule.module) } diff --git a/feature-search/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_search/presentation/widgets/SearchCarousel.kt b/feature-search/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_search/presentation/widgets/SearchCarousel.kt index b73a6a07..d736bac1 100644 --- a/feature-search/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_search/presentation/widgets/SearchCarousel.kt +++ b/feature-search/src/commonMain/kotlin/com/codandotv/streamplayerapp/feature_search/presentation/widgets/SearchCarousel.kt @@ -26,14 +26,12 @@ import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import androidx.paging.PagingData -import androidx.paging.compose.collectAsLazyPagingItems -import androidx.paging.compose.itemContentType -import androidx.paging.compose.itemKey +import app.cash.paging.compose.collectAsLazyPagingItems +import app.cash.paging.compose.itemContentType +import app.cash.paging.compose.itemKey import com.codandotv.streamplayerapp.core_shared_ui.widget.StreamsCard import com.codandotv.streamplayerapp.core_shared_ui.widget.StreamsCardContent -import com.codandotv.streamplayerapp.feature.search.R import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.emptyFlow import org.jetbrains.compose.resources.stringResource import streamplayerapp_kmp.feature_search.generated.resources.Res import streamplayerapp_kmp.feature_search.generated.resources.bottom_search_list_error diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index db8a5765..68367601 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,13 +1,13 @@ [versions] -kotlin = "2.0.21" +kotlin = "2.1.10" android_gradle_plugin = "8.8.0" koin = "4.0.1" -ksp = "2.0.21-1.0.27" +ksp = "2.1.10-1.0.29" dokka = "1.9.10" kover = "0.7.5" detekt = "1.23.6" -compose_plugin_multiplataform = "1.7.1" +compose_plugin_multiplataform = "1.7.3" navigation-compose-version = "2.7.0-alpha07" paging-compose = "3.3.0-alpha02-0.5.1" buildkonfig = "0.15.2" @@ -26,8 +26,8 @@ coil = "3.1.0" lottie = "5.2.0" #Room Database -room = "2.7.0-alpha11" -sqlite = "2.5.0-alpha11" +room = "2.7.0-alpha13" +sqlite = "2.5.0-SNAPSHOT" android_youtube_player_version = "12.0.0" diff --git a/iosApp/iosApp/ContentView.swift b/iosApp/iosApp/ContentView.swift index 3cd5c325..fad7f5d8 100644 --- a/iosApp/iosApp/ContentView.swift +++ b/iosApp/iosApp/ContentView.swift @@ -1,6 +1,6 @@ import UIKit import SwiftUI -import ComposeApp +import streamplayerapp struct ComposeView: UIViewControllerRepresentable { func makeUIViewController(context: Context) -> UIViewController { From 5e99a077ac482e51a254dde222152093fb6c7cbf Mon Sep 17 00:00:00 2001 From: Rods Date: Mon, 24 Feb 2025 21:06:45 -0300 Subject: [PATCH 86/96] [ISSUE-16] - downgrade agp to work with android --- 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 68367601..11cd12dd 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,6 +1,6 @@ [versions] kotlin = "2.1.10" -android_gradle_plugin = "8.8.0" +android_gradle_plugin = "8.7.2" koin = "4.0.1" ksp = "2.1.10-1.0.29" From bd639687f9d7a6f2a23ac42cb6e637bf13a54ac8 Mon Sep 17 00:00:00 2001 From: Gabriel Bronzatti Moro Date: Tue, 25 Feb 2025 07:38:48 -0300 Subject: [PATCH 87/96] Lottie animation for android - compatible multiplatform way --- composeApp/build.gradle.kts | 1 + .../LottieComponentPlatform.android.kt | 17 +++++++++---- .../composeResources/files/lottie}/logo.json | 0 .../components/LottieComponentPlatform.kt | 6 ++++- .../presentation/screens/SplashScreen.kt | 24 ++++++++++++++++++- .../components/LottieComponentPlatform.ios.kt | 6 ++++- 6 files changed, 47 insertions(+), 7 deletions(-) rename composeApp/src/{androidMain/res/raw => commonMain/composeResources/files/lottie}/logo.json (100%) diff --git a/composeApp/build.gradle.kts b/composeApp/build.gradle.kts index 53dae137..21c08c2b 100644 --- a/composeApp/build.gradle.kts +++ b/composeApp/build.gradle.kts @@ -27,6 +27,7 @@ kotlin { implementation(compose.material3) implementation(compose.ui) + implementation(compose.components.resources) implementation(libs.koin.core) } diff --git a/composeApp/src/androidMain/kotlin/com.codandotv.streamplayerapp/presentation/components/LottieComponentPlatform.android.kt b/composeApp/src/androidMain/kotlin/com.codandotv.streamplayerapp/presentation/components/LottieComponentPlatform.android.kt index 5cc9afe6..d7a7f74f 100644 --- a/composeApp/src/androidMain/kotlin/com.codandotv.streamplayerapp/presentation/components/LottieComponentPlatform.android.kt +++ b/composeApp/src/androidMain/kotlin/com.codandotv.streamplayerapp/presentation/components/LottieComponentPlatform.android.kt @@ -7,12 +7,21 @@ import com.airbnb.lottie.compose.LottieAnimation import com.airbnb.lottie.compose.LottieCompositionSpec import com.airbnb.lottie.compose.animateLottieCompositionAsState import com.airbnb.lottie.compose.rememberLottieComposition -import com.codandotv.streamplayerapp.R @Composable -actual fun LottieComponent(modifier: Modifier, onAnimationFinished: () -> Unit) { - val composition by rememberLottieComposition(LottieCompositionSpec.RawRes(R.raw.logo)) - val logoAnimationState = animateLottieCompositionAsState(composition = composition) +actual fun LottieComponent( + jsonString: String, + modifier: Modifier, + onAnimationFinished: () -> Unit +) { + val composition by rememberLottieComposition( + spec = LottieCompositionSpec.JsonString( + jsonString = jsonString + ) + ) + val logoAnimationState = animateLottieCompositionAsState( + composition = composition + ) LottieAnimation( composition = composition, diff --git a/composeApp/src/androidMain/res/raw/logo.json b/composeApp/src/commonMain/composeResources/files/lottie/logo.json similarity index 100% rename from composeApp/src/androidMain/res/raw/logo.json rename to composeApp/src/commonMain/composeResources/files/lottie/logo.json diff --git a/composeApp/src/commonMain/kotlin/com.codandotv.streamplayerapp/presentation/components/LottieComponentPlatform.kt b/composeApp/src/commonMain/kotlin/com.codandotv.streamplayerapp/presentation/components/LottieComponentPlatform.kt index 75db37a0..56f103ff 100644 --- a/composeApp/src/commonMain/kotlin/com.codandotv.streamplayerapp/presentation/components/LottieComponentPlatform.kt +++ b/composeApp/src/commonMain/kotlin/com.codandotv.streamplayerapp/presentation/components/LottieComponentPlatform.kt @@ -4,4 +4,8 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier @Composable -expect fun LottieComponent(modifier : Modifier = Modifier, onAnimationFinished: () -> Unit) \ No newline at end of file +expect fun LottieComponent( + jsonString: String, + modifier: Modifier = Modifier, + onAnimationFinished: () -> Unit +) \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/com.codandotv.streamplayerapp/presentation/screens/SplashScreen.kt b/composeApp/src/commonMain/kotlin/com.codandotv.streamplayerapp/presentation/screens/SplashScreen.kt index 2a5444a9..eff77cbc 100644 --- a/composeApp/src/commonMain/kotlin/com.codandotv.streamplayerapp/presentation/screens/SplashScreen.kt +++ b/composeApp/src/commonMain/kotlin/com.codandotv.streamplayerapp/presentation/screens/SplashScreen.kt @@ -5,15 +5,31 @@ import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import com.codandotv.streamplayerapp.presentation.components.LottieComponent +import org.jetbrains.compose.resources.ExperimentalResourceApi +import streamplayerapp_kmp.composeapp.generated.resources.Res +@OptIn(ExperimentalResourceApi::class) @Composable fun SplashScreen( onAnimationFinished: () -> Unit ) { + var lottieAnimationString: String? by remember { + mutableStateOf(null) + } + LaunchedEffect(Unit) { + lottieAnimationString = Res.readBytes( + "files/lottie/logo.json" + ).decodeToString() + } Column( modifier = Modifier.fillMaxSize() ) { @@ -23,7 +39,13 @@ fun SplashScreen( .fillMaxSize() .background(Color.Black) ) { - LottieComponent(modifier = Modifier, onAnimationFinished = onAnimationFinished) + lottieAnimationString?.let { + LottieComponent( + jsonString = it, + modifier = Modifier, + onAnimationFinished = onAnimationFinished + ) + } } } } diff --git a/composeApp/src/iosMain/kotlin/com.codandotv.streamplayerapp/presentation/components/LottieComponentPlatform.ios.kt b/composeApp/src/iosMain/kotlin/com.codandotv.streamplayerapp/presentation/components/LottieComponentPlatform.ios.kt index ef15e368..bb31b848 100644 --- a/composeApp/src/iosMain/kotlin/com.codandotv.streamplayerapp/presentation/components/LottieComponentPlatform.ios.kt +++ b/composeApp/src/iosMain/kotlin/com.codandotv.streamplayerapp/presentation/components/LottieComponentPlatform.ios.kt @@ -4,4 +4,8 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier @Composable -actual fun LottieComponent(modifier: Modifier, onAnimationFinished: () -> Unit) = Unit \ No newline at end of file +actual fun LottieComponent( + jsonString: String, + modifier: Modifier, + onAnimationFinished: () -> Unit +) = Unit \ No newline at end of file From 1586a46d0846f2c55b5697b4703c1fcac8d17f6e Mon Sep 17 00:00:00 2001 From: Gabriel Bronzatti Moro Date: Tue, 25 Feb 2025 07:58:04 -0300 Subject: [PATCH 88/96] Lottie animation for iOS - compatible multiplatform way --- iosApp/iosApp.xcodeproj/project.pbxproj | 35 ++++++++++- .../xcshareddata/swiftpm/Package.resolved | 15 +++++ iosApp/iosApp/Lottie/Lottie.swift | 59 +++++++++++++++++++ 3 files changed, 107 insertions(+), 2 deletions(-) create mode 100644 iosApp/iosApp.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved create mode 100644 iosApp/iosApp/Lottie/Lottie.swift diff --git a/iosApp/iosApp.xcodeproj/project.pbxproj b/iosApp/iosApp.xcodeproj/project.pbxproj index 74749301..0b0409ff 100644 --- a/iosApp/iosApp.xcodeproj/project.pbxproj +++ b/iosApp/iosApp.xcodeproj/project.pbxproj @@ -3,7 +3,7 @@ archiveVersion = 1; classes = { }; - objectVersion = 56; + objectVersion = 70; objects = { /* Begin PBXBuildFile section */ @@ -11,6 +11,7 @@ 058557D9273AAEEB004C7B11 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 058557D8273AAEEB004C7B11 /* Preview Assets.xcassets */; }; 2152FB042600AC8F00CF470E /* iOSApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2152FB032600AC8F00CF470E /* iOSApp.swift */; }; 7555FF83242A565900829871 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7555FF82242A565900829871 /* ContentView.swift */; }; + 8CF748CA2D6DD5FF00AB6801 /* Lottie in Frameworks */ = {isa = PBXBuildFile; productRef = 8CF748C92D6DD5FF00AB6801 /* Lottie */; }; /* End PBXBuildFile section */ /* Begin PBXFileReference section */ @@ -23,11 +24,16 @@ AB3632DC29227652001CCB65 /* Config.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Config.xcconfig; sourceTree = ""; }; /* End PBXFileReference section */ +/* Begin PBXFileSystemSynchronizedRootGroup section */ + 8CF748CB2D6DD60A00AB6801 /* Lottie */ = {isa = PBXFileSystemSynchronizedRootGroup; explicitFileTypes = {}; explicitFolders = (); path = Lottie; sourceTree = ""; }; +/* End PBXFileSystemSynchronizedRootGroup section */ + /* Begin PBXFrameworksBuildPhase section */ B92378962B6B1156000C7307 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 8CF748CA2D6DD5FF00AB6801 /* Lottie in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -70,6 +76,7 @@ 7555FF7D242A565900829871 /* iosApp */ = { isa = PBXGroup; children = ( + 8CF748CB2D6DD60A00AB6801 /* Lottie */, 058557BA273AAA24004C7B11 /* Assets.xcassets */, 7555FF82242A565900829871 /* ContentView.swift */, 7555FF8C242A565B00829871 /* Info.plist */, @@ -103,8 +110,12 @@ ); dependencies = ( ); + fileSystemSynchronizedGroups = ( + 8CF748CB2D6DD60A00AB6801 /* Lottie */, + ); name = iosApp; packageProductDependencies = ( + 8CF748C92D6DD5FF00AB6801 /* Lottie */, ); productName = iosApp; productReference = 7555FF7B242A565900829871 /* KotlinProject.app */; @@ -136,6 +147,7 @@ ); mainGroup = 7555FF72242A565900829871; packageReferences = ( + 8CF748C82D6DD5FF00AB6801 /* XCRemoteSwiftPackageReference "lottie-spm" */, ); productRefGroup = 7555FF7C242A565900829871 /* Products */; projectDirPath = ""; @@ -388,6 +400,25 @@ defaultConfigurationName = Release; }; /* End XCConfigurationList section */ + +/* Begin XCRemoteSwiftPackageReference section */ + 8CF748C82D6DD5FF00AB6801 /* XCRemoteSwiftPackageReference "lottie-spm" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/airbnb/lottie-spm.git"; + requirement = { + kind = upToNextMajorVersion; + minimumVersion = 4.5.1; + }; + }; +/* End XCRemoteSwiftPackageReference section */ + +/* Begin XCSwiftPackageProductDependency section */ + 8CF748C92D6DD5FF00AB6801 /* Lottie */ = { + isa = XCSwiftPackageProductDependency; + package = 8CF748C82D6DD5FF00AB6801 /* XCRemoteSwiftPackageReference "lottie-spm" */; + productName = Lottie; + }; +/* End XCSwiftPackageProductDependency section */ }; rootObject = 7555FF73242A565900829871 /* Project object */; -} \ No newline at end of file +} diff --git a/iosApp/iosApp.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/iosApp/iosApp.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved new file mode 100644 index 00000000..766617e2 --- /dev/null +++ b/iosApp/iosApp.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -0,0 +1,15 @@ +{ + "originHash" : "6b3386dc9ff1f3a74f1534de9c41d47137eae0901cfe819ed442f1b241549359", + "pins" : [ + { + "identity" : "lottie-spm", + "kind" : "remoteSourceControl", + "location" : "https://github.com/airbnb/lottie-spm.git", + "state" : { + "revision" : "8c6edf4f0fa84fe9c058600a4295eb0c01661c69", + "version" : "4.5.1" + } + } + ], + "version" : 3 +} diff --git a/iosApp/iosApp/Lottie/Lottie.swift b/iosApp/iosApp/Lottie/Lottie.swift new file mode 100644 index 00000000..1f3fa7cb --- /dev/null +++ b/iosApp/iosApp/Lottie/Lottie.swift @@ -0,0 +1,59 @@ +// +// Lottie.swift +// iosApp +// +// Created by Gabriel Bronzatti Moro on 25/02/25. +// Copyright © 2025 orgName. All rights reserved. +// + +import Lottie +import UIKit +import streamplayerapp + +class LottieViewProviderImpl : LottieViewProvider { + func provideLottieView(lottieAnimationJson: String) -> UIView { + let lottieView = LottieView() + lottieView.setupLottieAnimationView( + animationContentJson: lottieAnimationJson + ) + return lottieView + } +} + +class LottieView : UIView { + + private let animationView: LottieAnimationView + + override init(frame: CGRect) { + animationView = LottieAnimationView() + + super.init(frame: frame) + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented.") + } + + func setupLottieAnimationView(animationContentJson: String) { + + animationView.contentMode = .scaleAspectFit + + addSubview(animationView) + + animationView.translatesAutoresizingMaskIntoConstraints = false + + animationView.leftAnchor.constraint(equalTo: leftAnchor).isActive = true + animationView.rightAnchor.constraint(equalTo: rightAnchor).isActive = true + animationView.topAnchor.constraint(equalTo: topAnchor).isActive = true + animationView.bottomAnchor.constraint(equalTo: bottomAnchor).isActive = true + + animationView.loopMode = .loop + animationView.frame = bounds + + guard let data = jsonString.data(using: .utf8) else { return nil } + let animation = JSONDecoder().decode(LottieAnimation.self, from: data) + animationView.animation = animation + animationView.loopMode = .loop + animationView.play() + } +} From d501d07f13d41196583c8fbcf113835e1372550b Mon Sep 17 00:00:00 2001 From: Gabriel Bronzatti Moro Date: Tue, 25 Feb 2025 08:01:48 -0300 Subject: [PATCH 89/96] Lottie animation for iOS - compatible multiplatform way --- .../presentation/components/LottieViewProvider.kt | 7 +++++++ iosApp/iosApp/Lottie/Lottie.swift | 13 +++++++++---- 2 files changed, 16 insertions(+), 4 deletions(-) create mode 100644 composeApp/src/iosMain/kotlin/com.codandotv.streamplayerapp/presentation/components/LottieViewProvider.kt diff --git a/composeApp/src/iosMain/kotlin/com.codandotv.streamplayerapp/presentation/components/LottieViewProvider.kt b/composeApp/src/iosMain/kotlin/com.codandotv.streamplayerapp/presentation/components/LottieViewProvider.kt new file mode 100644 index 00000000..775adcad --- /dev/null +++ b/composeApp/src/iosMain/kotlin/com.codandotv.streamplayerapp/presentation/components/LottieViewProvider.kt @@ -0,0 +1,7 @@ +package com.codandotv.streamplayerapp.presentation.components + +import platform.UIKit.UIView + +interface LottieViewProvider { + fun provideLottieView(lottieAnimationJson: String) : UIView +} \ No newline at end of file diff --git a/iosApp/iosApp/Lottie/Lottie.swift b/iosApp/iosApp/Lottie/Lottie.swift index 1f3fa7cb..45761c57 100644 --- a/iosApp/iosApp/Lottie/Lottie.swift +++ b/iosApp/iosApp/Lottie/Lottie.swift @@ -50,10 +50,15 @@ class LottieView : UIView { animationView.loopMode = .loop animationView.frame = bounds + if let animation = loadLottieAnimation(from: animationContentJson) { + animationView.animation = animation + animationView.loopMode = .loop + animationView.play() + } + } + + func loadLottieAnimation(from jsonString: String) -> LottieAnimation? { guard let data = jsonString.data(using: .utf8) else { return nil } - let animation = JSONDecoder().decode(LottieAnimation.self, from: data) - animationView.animation = animation - animationView.loopMode = .loop - animationView.play() + return try? JSONDecoder().decode(LottieAnimation.self, from: data) } } From 25bede9339ee71158875617a4d473fe50b238d9b Mon Sep 17 00:00:00 2001 From: Pedro Alvarez <14023675+pnalvarez@users.noreply.github.com> Date: Tue, 25 Feb 2025 20:40:41 -0300 Subject: [PATCH 90/96] ios build --- .../commonizedNativeDistributionLocation.txt | 1 + build/tmp/.cache/expanded/expanded.lock | Bin 0 -> 17 bytes .../contents.xcworkspacedata | 7 +++++++ .../xcshareddata/IDEWorkspaceChecks.plist | 8 ++++++++ .../UserInterfaceState.xcuserstate | Bin 0 -> 18017 bytes .../xcschemes/xcschememanagement.plist | 14 ++++++++++++++ 6 files changed, 30 insertions(+) create mode 100644 build/kotlin/commonizedNativeDistributionLocation.txt create mode 100644 build/tmp/.cache/expanded/expanded.lock create mode 100644 iosApp/iosApp.xcodeproj/project.xcworkspace/contents.xcworkspacedata create mode 100644 iosApp/iosApp.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist create mode 100644 iosApp/iosApp.xcodeproj/project.xcworkspace/xcuserdata/pedroalvarez.xcuserdatad/UserInterfaceState.xcuserstate create mode 100644 iosApp/iosApp.xcodeproj/xcuserdata/pedroalvarez.xcuserdatad/xcschemes/xcschememanagement.plist diff --git a/build/kotlin/commonizedNativeDistributionLocation.txt b/build/kotlin/commonizedNativeDistributionLocation.txt new file mode 100644 index 00000000..aac56bbe --- /dev/null +++ b/build/kotlin/commonizedNativeDistributionLocation.txt @@ -0,0 +1 @@ +/Users/pedroalvarez/.konan/kotlin-native-prebuilt-macos-aarch64-2.1.10/klib/commonized/2.1.10 \ No newline at end of file diff --git a/build/tmp/.cache/expanded/expanded.lock b/build/tmp/.cache/expanded/expanded.lock new file mode 100644 index 0000000000000000000000000000000000000000..f22d2d1b290549f1ca8f299a9e27ea9ac913018c GIT binary patch literal 17 TcmZSHm21=*e3WH70~7!NF17=; literal 0 HcmV?d00001 diff --git a/iosApp/iosApp.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/iosApp/iosApp.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 00000000..919434a6 --- /dev/null +++ b/iosApp/iosApp.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/iosApp/iosApp.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/iosApp/iosApp.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 00000000..18d98100 --- /dev/null +++ b/iosApp/iosApp.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/iosApp/iosApp.xcodeproj/project.xcworkspace/xcuserdata/pedroalvarez.xcuserdatad/UserInterfaceState.xcuserstate b/iosApp/iosApp.xcodeproj/project.xcworkspace/xcuserdata/pedroalvarez.xcuserdatad/UserInterfaceState.xcuserstate new file mode 100644 index 0000000000000000000000000000000000000000..332f996b055be0edf1ec4eaa695b1aad25c45a79 GIT binary patch literal 18017 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 VcCToYB0{83*(m8V@NRqV{{j8T7Q+Al literal 0 HcmV?d00001 diff --git a/iosApp/iosApp.xcodeproj/xcuserdata/pedroalvarez.xcuserdatad/xcschemes/xcschememanagement.plist b/iosApp/iosApp.xcodeproj/xcuserdata/pedroalvarez.xcuserdatad/xcschemes/xcschememanagement.plist new file mode 100644 index 00000000..56b5955f --- /dev/null +++ b/iosApp/iosApp.xcodeproj/xcuserdata/pedroalvarez.xcuserdatad/xcschemes/xcschememanagement.plist @@ -0,0 +1,14 @@ + + + + + SchemeUserState + + iosApp.xcscheme_^#shared#^_ + + orderHint + 0 + + + + From 0b0fc4b2e8f6f1b89e9c6876594ed7d2cb6443f2 Mon Sep 17 00:00:00 2001 From: Pedro Alvarez <14023675+pnalvarez@users.noreply.github.com> Date: Tue, 25 Feb 2025 21:41:20 -0300 Subject: [PATCH 91/96] initializing koin in ios app --- .../di/KoinIosHelper.kt | 11 +++++++++++ .../UserInterfaceState.xcuserstate | Bin 18017 -> 23510 bytes iosApp/iosApp/iOSApp.swift | 8 +++++++- 3 files changed, 18 insertions(+), 1 deletion(-) create mode 100644 composeApp/src/iosMain/kotlin/com.codandotv.streamplayerapp/di/KoinIosHelper.kt diff --git a/composeApp/src/iosMain/kotlin/com.codandotv.streamplayerapp/di/KoinIosHelper.kt b/composeApp/src/iosMain/kotlin/com.codandotv.streamplayerapp/di/KoinIosHelper.kt new file mode 100644 index 00000000..bb06509f --- /dev/null +++ b/composeApp/src/iosMain/kotlin/com.codandotv.streamplayerapp/di/KoinIosHelper.kt @@ -0,0 +1,11 @@ +package com.codandotv.streamplayerapp.di + +import org.koin.core.context.startKoin + +class KoinIosHelper { + fun initKoin() { + startKoin { + modules(AppModule.list) + } + } +} \ No newline at end of file diff --git a/iosApp/iosApp.xcodeproj/project.xcworkspace/xcuserdata/pedroalvarez.xcuserdatad/UserInterfaceState.xcuserstate b/iosApp/iosApp.xcodeproj/project.xcworkspace/xcuserdata/pedroalvarez.xcuserdatad/UserInterfaceState.xcuserstate index 332f996b055be0edf1ec4eaa695b1aad25c45a79..82332d934540fefe1281f15978777722d30598bd 100644 GIT binary patch literal 23510 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 delta 9563 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 Date: Thu, 6 Mar 2025 13:02:57 -0300 Subject: [PATCH 92/96] lottie animation --- .../di/KoinIosHelper.kt | 10 ++++++++-- .../components/LottieComponentPlatform.ios.kt | 14 +++++++++++++- iosApp/iosApp.xcodeproj/project.pbxproj | 2 +- iosApp/iosApp/Lottie/Lottie.swift | 8 -------- iosApp/iosApp/iOSApp.swift | 2 +- 5 files changed, 23 insertions(+), 13 deletions(-) diff --git a/composeApp/src/iosMain/kotlin/com.codandotv.streamplayerapp/di/KoinIosHelper.kt b/composeApp/src/iosMain/kotlin/com.codandotv.streamplayerapp/di/KoinIosHelper.kt index bb06509f..bd8d968c 100644 --- a/composeApp/src/iosMain/kotlin/com.codandotv.streamplayerapp/di/KoinIosHelper.kt +++ b/composeApp/src/iosMain/kotlin/com.codandotv.streamplayerapp/di/KoinIosHelper.kt @@ -1,11 +1,17 @@ package com.codandotv.streamplayerapp.di +import com.codandotv.streamplayerapp.presentation.components.LottieViewProvider import org.koin.core.context.startKoin +import org.koin.dsl.module class KoinIosHelper { - fun initKoin() { + fun initKoin(lottieViewProvider: LottieViewProvider) { startKoin { - modules(AppModule.list) + modules(AppModule.list + module { + single { + lottieViewProvider + } + }) } } } \ No newline at end of file diff --git a/composeApp/src/iosMain/kotlin/com.codandotv.streamplayerapp/presentation/components/LottieComponentPlatform.ios.kt b/composeApp/src/iosMain/kotlin/com.codandotv.streamplayerapp/presentation/components/LottieComponentPlatform.ios.kt index bb31b848..3716131e 100644 --- a/composeApp/src/iosMain/kotlin/com.codandotv.streamplayerapp/presentation/components/LottieComponentPlatform.ios.kt +++ b/composeApp/src/iosMain/kotlin/com.codandotv.streamplayerapp/presentation/components/LottieComponentPlatform.ios.kt @@ -2,10 +2,22 @@ package com.codandotv.streamplayerapp.presentation.components import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier +import androidx.compose.ui.viewinterop.UIKitView +import org.koin.mp.KoinPlatform +import platform.UIKit.UIView @Composable actual fun LottieComponent( jsonString: String, modifier: Modifier, onAnimationFinished: () -> Unit -) = Unit \ No newline at end of file +) { + val provider = KoinPlatform.getKoin().get() + + UIKitView( + modifier = modifier, + factory = { + provider.provideLottieView(jsonString) + } + ) +} \ No newline at end of file diff --git a/iosApp/iosApp.xcodeproj/project.pbxproj b/iosApp/iosApp.xcodeproj/project.pbxproj index 0b0409ff..9e05f001 100644 --- a/iosApp/iosApp.xcodeproj/project.pbxproj +++ b/iosApp/iosApp.xcodeproj/project.pbxproj @@ -3,7 +3,7 @@ archiveVersion = 1; classes = { }; - objectVersion = 70; + objectVersion = 56; objects = { /* Begin PBXBuildFile section */ diff --git a/iosApp/iosApp/Lottie/Lottie.swift b/iosApp/iosApp/Lottie/Lottie.swift index 45761c57..4f0144d0 100644 --- a/iosApp/iosApp/Lottie/Lottie.swift +++ b/iosApp/iosApp/Lottie/Lottie.swift @@ -1,11 +1,3 @@ -// -// Lottie.swift -// iosApp -// -// Created by Gabriel Bronzatti Moro on 25/02/25. -// Copyright © 2025 orgName. All rights reserved. -// - import Lottie import UIKit import streamplayerapp diff --git a/iosApp/iosApp/iOSApp.swift b/iosApp/iosApp/iOSApp.swift index 84e1ff36..2f91dd84 100644 --- a/iosApp/iosApp/iOSApp.swift +++ b/iosApp/iosApp/iOSApp.swift @@ -5,7 +5,7 @@ import streamplayerapp struct iOSApp: App { init() { - KoinIosHelper().doInitKoin() + KoinIosHelper().doInitKoin(LottieViewProviderImpl()) } var body: some Scene { From 6bc3c0d237842b2ec4ddd7399e85a95600320800 Mon Sep 17 00:00:00 2001 From: Rods Date: Sat, 8 Mar 2025 16:21:15 -0300 Subject: [PATCH 93/96] lottie --- build/tmp/.cache/expanded/expanded.lock | Bin 17 -> 0 bytes iosApp/iosApp.xcodeproj/project.pbxproj | 28 ++++----- .../xcshareddata/swiftpm/Package.resolved | 15 ----- iosApp/iosApp/Lottie.swift | 56 ++++++++++++++++++ iosApp/iosApp/iOSApp.swift | 2 +- 5 files changed, 70 insertions(+), 31 deletions(-) delete mode 100644 build/tmp/.cache/expanded/expanded.lock delete mode 100644 iosApp/iosApp.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved create mode 100644 iosApp/iosApp/Lottie.swift diff --git a/build/tmp/.cache/expanded/expanded.lock b/build/tmp/.cache/expanded/expanded.lock deleted file mode 100644 index f22d2d1b290549f1ca8f299a9e27ea9ac913018c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 17 TcmZSHm21=*e3WH70~7!NF17=; diff --git a/iosApp/iosApp.xcodeproj/project.pbxproj b/iosApp/iosApp.xcodeproj/project.pbxproj index 9e05f001..25552509 100644 --- a/iosApp/iosApp.xcodeproj/project.pbxproj +++ b/iosApp/iosApp.xcodeproj/project.pbxproj @@ -10,30 +10,28 @@ 058557BB273AAA24004C7B11 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 058557BA273AAA24004C7B11 /* Assets.xcassets */; }; 058557D9273AAEEB004C7B11 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 058557D8273AAEEB004C7B11 /* Preview Assets.xcassets */; }; 2152FB042600AC8F00CF470E /* iOSApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2152FB032600AC8F00CF470E /* iOSApp.swift */; }; + 5BAD97872D7CCDDA00D93987 /* Lottie.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BAD97862D7CCDDA00D93987 /* Lottie.swift */; }; + 5BAD97892D7CCEA700D93987 /* Lottie in Frameworks */ = {isa = PBXBuildFile; productRef = 5BAD97882D7CCEA700D93987 /* Lottie */; }; 7555FF83242A565900829871 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7555FF82242A565900829871 /* ContentView.swift */; }; - 8CF748CA2D6DD5FF00AB6801 /* Lottie in Frameworks */ = {isa = PBXBuildFile; productRef = 8CF748C92D6DD5FF00AB6801 /* Lottie */; }; /* End PBXBuildFile section */ /* Begin PBXFileReference section */ 058557BA273AAA24004C7B11 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 058557D8273AAEEB004C7B11 /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = ""; }; 2152FB032600AC8F00CF470E /* iOSApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = iOSApp.swift; sourceTree = ""; }; + 5BAD97862D7CCDDA00D93987 /* Lottie.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Lottie.swift; sourceTree = ""; }; 7555FF7B242A565900829871 /* KotlinProject.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = KotlinProject.app; sourceTree = BUILT_PRODUCTS_DIR; }; 7555FF82242A565900829871 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = ""; }; 7555FF8C242A565B00829871 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; AB3632DC29227652001CCB65 /* Config.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Config.xcconfig; sourceTree = ""; }; /* End PBXFileReference section */ -/* Begin PBXFileSystemSynchronizedRootGroup section */ - 8CF748CB2D6DD60A00AB6801 /* Lottie */ = {isa = PBXFileSystemSynchronizedRootGroup; explicitFileTypes = {}; explicitFolders = (); path = Lottie; sourceTree = ""; }; -/* End PBXFileSystemSynchronizedRootGroup section */ - /* Begin PBXFrameworksBuildPhase section */ B92378962B6B1156000C7307 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 8CF748CA2D6DD5FF00AB6801 /* Lottie in Frameworks */, + 5BAD97892D7CCEA700D93987 /* Lottie in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -76,7 +74,7 @@ 7555FF7D242A565900829871 /* iosApp */ = { isa = PBXGroup; children = ( - 8CF748CB2D6DD60A00AB6801 /* Lottie */, + 5BAD97862D7CCDDA00D93987 /* Lottie.swift */, 058557BA273AAA24004C7B11 /* Assets.xcassets */, 7555FF82242A565900829871 /* ContentView.swift */, 7555FF8C242A565B00829871 /* Info.plist */, @@ -110,12 +108,9 @@ ); dependencies = ( ); - fileSystemSynchronizedGroups = ( - 8CF748CB2D6DD60A00AB6801 /* Lottie */, - ); name = iosApp; packageProductDependencies = ( - 8CF748C92D6DD5FF00AB6801 /* Lottie */, + 5BAD97882D7CCEA700D93987 /* Lottie */, ); productName = iosApp; productReference = 7555FF7B242A565900829871 /* KotlinProject.app */; @@ -147,7 +142,7 @@ ); mainGroup = 7555FF72242A565900829871; packageReferences = ( - 8CF748C82D6DD5FF00AB6801 /* XCRemoteSwiftPackageReference "lottie-spm" */, + 5BAD97852D7CCDB400D93987 /* XCRemoteSwiftPackageReference "lottie-spm" */, ); productRefGroup = 7555FF7C242A565900829871 /* Products */; projectDirPath = ""; @@ -198,6 +193,7 @@ files = ( 2152FB042600AC8F00CF470E /* iOSApp.swift in Sources */, 7555FF83242A565900829871 /* ContentView.swift in Sources */, + 5BAD97872D7CCDDA00D93987 /* Lottie.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -328,6 +324,7 @@ isa = XCBuildConfiguration; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = NO; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; DEVELOPMENT_ASSET_PATHS = "\"iosApp/Preview Content\""; @@ -355,6 +352,7 @@ isa = XCBuildConfiguration; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = NO; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; DEVELOPMENT_ASSET_PATHS = "\"iosApp/Preview Content\""; @@ -402,7 +400,7 @@ /* End XCConfigurationList section */ /* Begin XCRemoteSwiftPackageReference section */ - 8CF748C82D6DD5FF00AB6801 /* XCRemoteSwiftPackageReference "lottie-spm" */ = { + 5BAD97852D7CCDB400D93987 /* XCRemoteSwiftPackageReference "lottie-spm" */ = { isa = XCRemoteSwiftPackageReference; repositoryURL = "https://github.com/airbnb/lottie-spm.git"; requirement = { @@ -413,9 +411,9 @@ /* End XCRemoteSwiftPackageReference section */ /* Begin XCSwiftPackageProductDependency section */ - 8CF748C92D6DD5FF00AB6801 /* Lottie */ = { + 5BAD97882D7CCEA700D93987 /* Lottie */ = { isa = XCSwiftPackageProductDependency; - package = 8CF748C82D6DD5FF00AB6801 /* XCRemoteSwiftPackageReference "lottie-spm" */; + package = 5BAD97852D7CCDB400D93987 /* XCRemoteSwiftPackageReference "lottie-spm" */; productName = Lottie; }; /* End XCSwiftPackageProductDependency section */ diff --git a/iosApp/iosApp.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/iosApp/iosApp.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved deleted file mode 100644 index 766617e2..00000000 --- a/iosApp/iosApp.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ /dev/null @@ -1,15 +0,0 @@ -{ - "originHash" : "6b3386dc9ff1f3a74f1534de9c41d47137eae0901cfe819ed442f1b241549359", - "pins" : [ - { - "identity" : "lottie-spm", - "kind" : "remoteSourceControl", - "location" : "https://github.com/airbnb/lottie-spm.git", - "state" : { - "revision" : "8c6edf4f0fa84fe9c058600a4295eb0c01661c69", - "version" : "4.5.1" - } - } - ], - "version" : 3 -} diff --git a/iosApp/iosApp/Lottie.swift b/iosApp/iosApp/Lottie.swift new file mode 100644 index 00000000..4f0144d0 --- /dev/null +++ b/iosApp/iosApp/Lottie.swift @@ -0,0 +1,56 @@ +import Lottie +import UIKit +import streamplayerapp + +class LottieViewProviderImpl : LottieViewProvider { + func provideLottieView(lottieAnimationJson: String) -> UIView { + let lottieView = LottieView() + lottieView.setupLottieAnimationView( + animationContentJson: lottieAnimationJson + ) + return lottieView + } +} + +class LottieView : UIView { + + private let animationView: LottieAnimationView + + override init(frame: CGRect) { + animationView = LottieAnimationView() + + super.init(frame: frame) + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented.") + } + + func setupLottieAnimationView(animationContentJson: String) { + + animationView.contentMode = .scaleAspectFit + + addSubview(animationView) + + animationView.translatesAutoresizingMaskIntoConstraints = false + + animationView.leftAnchor.constraint(equalTo: leftAnchor).isActive = true + animationView.rightAnchor.constraint(equalTo: rightAnchor).isActive = true + animationView.topAnchor.constraint(equalTo: topAnchor).isActive = true + animationView.bottomAnchor.constraint(equalTo: bottomAnchor).isActive = true + + animationView.loopMode = .loop + animationView.frame = bounds + + if let animation = loadLottieAnimation(from: animationContentJson) { + animationView.animation = animation + animationView.loopMode = .loop + animationView.play() + } + } + + func loadLottieAnimation(from jsonString: String) -> LottieAnimation? { + guard let data = jsonString.data(using: .utf8) else { return nil } + return try? JSONDecoder().decode(LottieAnimation.self, from: data) + } +} diff --git a/iosApp/iosApp/iOSApp.swift b/iosApp/iosApp/iOSApp.swift index 2f91dd84..dec7c719 100644 --- a/iosApp/iosApp/iOSApp.swift +++ b/iosApp/iosApp/iOSApp.swift @@ -5,7 +5,7 @@ import streamplayerapp struct iOSApp: App { init() { - KoinIosHelper().doInitKoin(LottieViewProviderImpl()) + KoinIosHelper().doInitKoin(lottieViewProvider: LottieViewProviderImpl()) } var body: some Scene { From 9a543395e654bb11519cb881d61ebe2819543dda Mon Sep 17 00:00:00 2001 From: Rods Date: Sat, 8 Mar 2025 16:25:39 -0300 Subject: [PATCH 94/96] lottie --- .../presentation/screens/SplashScreen.kt | 6 +++++- .../presentation/components/LottieComponentPlatform.ios.kt | 2 ++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/composeApp/src/commonMain/kotlin/com.codandotv.streamplayerapp/presentation/screens/SplashScreen.kt b/composeApp/src/commonMain/kotlin/com.codandotv.streamplayerapp/presentation/screens/SplashScreen.kt index eff77cbc..ab8cc565 100644 --- a/composeApp/src/commonMain/kotlin/com.codandotv.streamplayerapp/presentation/screens/SplashScreen.kt +++ b/composeApp/src/commonMain/kotlin/com.codandotv.streamplayerapp/presentation/screens/SplashScreen.kt @@ -3,7 +3,9 @@ package com.codandotv.streamplayerapp.presentation.screens import androidx.compose.foundation.background import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.fillMaxHeight import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue @@ -42,7 +44,9 @@ fun SplashScreen( lottieAnimationString?.let { LottieComponent( jsonString = it, - modifier = Modifier, + modifier = Modifier + .fillMaxWidth() + .fillMaxHeight(), onAnimationFinished = onAnimationFinished ) } diff --git a/composeApp/src/iosMain/kotlin/com.codandotv.streamplayerapp/presentation/components/LottieComponentPlatform.ios.kt b/composeApp/src/iosMain/kotlin/com.codandotv.streamplayerapp/presentation/components/LottieComponentPlatform.ios.kt index 3716131e..43ae991d 100644 --- a/composeApp/src/iosMain/kotlin/com.codandotv.streamplayerapp/presentation/components/LottieComponentPlatform.ios.kt +++ b/composeApp/src/iosMain/kotlin/com.codandotv.streamplayerapp/presentation/components/LottieComponentPlatform.ios.kt @@ -14,6 +14,8 @@ actual fun LottieComponent( ) { val provider = KoinPlatform.getKoin().get() + println(">>>> ${provider.hashCode()}") + UIKitView( modifier = modifier, factory = { From 7d5a8763b35b2187c029e174d01faebf08620aa3 Mon Sep 17 00:00:00 2001 From: Rods Date: Sat, 8 Mar 2025 16:28:46 -0300 Subject: [PATCH 95/96] lottie background black --- .../presentation/components/LottieComponentPlatform.ios.kt | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/composeApp/src/iosMain/kotlin/com.codandotv.streamplayerapp/presentation/components/LottieComponentPlatform.ios.kt b/composeApp/src/iosMain/kotlin/com.codandotv.streamplayerapp/presentation/components/LottieComponentPlatform.ios.kt index 43ae991d..8aa061ac 100644 --- a/composeApp/src/iosMain/kotlin/com.codandotv.streamplayerapp/presentation/components/LottieComponentPlatform.ios.kt +++ b/composeApp/src/iosMain/kotlin/com.codandotv.streamplayerapp/presentation/components/LottieComponentPlatform.ios.kt @@ -4,6 +4,7 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.viewinterop.UIKitView import org.koin.mp.KoinPlatform +import platform.UIKit.UIColor import platform.UIKit.UIView @Composable @@ -19,7 +20,9 @@ actual fun LottieComponent( UIKitView( modifier = modifier, factory = { - provider.provideLottieView(jsonString) + val view = provider.provideLottieView(jsonString) + view.backgroundColor = UIColor.blackColor() + view } ) } \ No newline at end of file From b3d110ebd157f1894d46574d51d866afdd462a2b Mon Sep 17 00:00:00 2001 From: Rods Date: Sat, 8 Mar 2025 16:44:03 -0300 Subject: [PATCH 96/96] finish lottie --- .../commonizedNativeDistributionLocation.txt | 2 +- .../components/LottieComponentPlatform.ios.kt | 4 +-- .../components/LottieViewProvider.kt | 2 +- iosApp/iosApp/Lottie.swift | 36 ++++++++++--------- 4 files changed, 23 insertions(+), 21 deletions(-) diff --git a/build/kotlin/commonizedNativeDistributionLocation.txt b/build/kotlin/commonizedNativeDistributionLocation.txt index aac56bbe..64f5ff0e 100644 --- a/build/kotlin/commonizedNativeDistributionLocation.txt +++ b/build/kotlin/commonizedNativeDistributionLocation.txt @@ -1 +1 @@ -/Users/pedroalvarez/.konan/kotlin-native-prebuilt-macos-aarch64-2.1.10/klib/commonized/2.1.10 \ No newline at end of file +/Users/rodrigo/.konan/kotlin-native-prebuilt-macos-aarch64-2.1.10/klib/commonized/2.1.10 \ No newline at end of file diff --git a/composeApp/src/iosMain/kotlin/com.codandotv.streamplayerapp/presentation/components/LottieComponentPlatform.ios.kt b/composeApp/src/iosMain/kotlin/com.codandotv.streamplayerapp/presentation/components/LottieComponentPlatform.ios.kt index 8aa061ac..9ed08746 100644 --- a/composeApp/src/iosMain/kotlin/com.codandotv.streamplayerapp/presentation/components/LottieComponentPlatform.ios.kt +++ b/composeApp/src/iosMain/kotlin/com.codandotv.streamplayerapp/presentation/components/LottieComponentPlatform.ios.kt @@ -15,12 +15,10 @@ actual fun LottieComponent( ) { val provider = KoinPlatform.getKoin().get() - println(">>>> ${provider.hashCode()}") - UIKitView( modifier = modifier, factory = { - val view = provider.provideLottieView(jsonString) + val view = provider.provideLottieView(jsonString,onAnimationFinished) view.backgroundColor = UIColor.blackColor() view } diff --git a/composeApp/src/iosMain/kotlin/com.codandotv.streamplayerapp/presentation/components/LottieViewProvider.kt b/composeApp/src/iosMain/kotlin/com.codandotv.streamplayerapp/presentation/components/LottieViewProvider.kt index 775adcad..f1bf123d 100644 --- a/composeApp/src/iosMain/kotlin/com.codandotv.streamplayerapp/presentation/components/LottieViewProvider.kt +++ b/composeApp/src/iosMain/kotlin/com.codandotv.streamplayerapp/presentation/components/LottieViewProvider.kt @@ -3,5 +3,5 @@ package com.codandotv.streamplayerapp.presentation.components import platform.UIKit.UIView interface LottieViewProvider { - fun provideLottieView(lottieAnimationJson: String) : UIView + fun provideLottieView(lottieAnimationJson: String, onAnimationFinish : () -> Unit) : UIView } \ No newline at end of file diff --git a/iosApp/iosApp/Lottie.swift b/iosApp/iosApp/Lottie.swift index 4f0144d0..dbd28a6f 100644 --- a/iosApp/iosApp/Lottie.swift +++ b/iosApp/iosApp/Lottie.swift @@ -3,22 +3,22 @@ import UIKit import streamplayerapp class LottieViewProviderImpl : LottieViewProvider { - func provideLottieView(lottieAnimationJson: String) -> UIView { + func provideLottieView(lottieAnimationJson: String,onAnimationFinish: @escaping () -> Void) -> UIView { let lottieView = LottieView() lottieView.setupLottieAnimationView( - animationContentJson: lottieAnimationJson + animationContentJson: lottieAnimationJson, + onAnimationFinished: onAnimationFinish ) return lottieView } } - class LottieView : UIView { private let animationView: LottieAnimationView + var onAnimationFinished: (() -> Void)? override init(frame: CGRect) { animationView = LottieAnimationView() - super.init(frame: frame) } @@ -26,26 +26,30 @@ class LottieView : UIView { fatalError("init(coder:) has not been implemented.") } - func setupLottieAnimationView(animationContentJson: String) { - + func setupLottieAnimationView(animationContentJson: String, onAnimationFinished: @escaping () -> Void) { + self.onAnimationFinished = onAnimationFinished + animationView.contentMode = .scaleAspectFit - addSubview(animationView) - - animationView.translatesAutoresizingMaskIntoConstraints = false - animationView.leftAnchor.constraint(equalTo: leftAnchor).isActive = true - animationView.rightAnchor.constraint(equalTo: rightAnchor).isActive = true - animationView.topAnchor.constraint(equalTo: topAnchor).isActive = true - animationView.bottomAnchor.constraint(equalTo: bottomAnchor).isActive = true + animationView.translatesAutoresizingMaskIntoConstraints = false + NSLayoutConstraint.activate([ + animationView.leftAnchor.constraint(equalTo: leftAnchor), + animationView.rightAnchor.constraint(equalTo: rightAnchor), + animationView.topAnchor.constraint(equalTo: topAnchor), + animationView.bottomAnchor.constraint(equalTo: bottomAnchor) + ]) - animationView.loopMode = .loop + animationView.loopMode = .playOnce // Toca apenas uma vez animationView.frame = bounds if let animation = loadLottieAnimation(from: animationContentJson) { animationView.animation = animation - animationView.loopMode = .loop - animationView.play() + animationView.play { finished in + if finished { + self.onAnimationFinished?() // Chama o callback quando terminar + } + } } }