Compare commits

..

No commits in common. "main" and "v1.1.0" have entirely different histories.
main ... v1.1.0

656 changed files with 3421 additions and 27386 deletions

View File

@ -1,123 +0,0 @@
<component name="ProjectCodeStyleConfiguration">
<code_scheme name="Project" version="173">
<JetCodeStyleSettings>
<option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" />
</JetCodeStyleSettings>
<codeStyleSettings language="XML">
<option name="FORCE_REARRANGE_MODE" value="1" />
<indentOptions>
<option name="CONTINUATION_INDENT_SIZE" value="4" />
</indentOptions>
<arrangement>
<rules>
<section>
<rule>
<match>
<AND>
<NAME>xmlns:android</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>^$</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>xmlns:.*</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>^$</XML_NAMESPACE>
</AND>
</match>
<order>BY_NAME</order>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*:id</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*:name</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>name</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>^$</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>style</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>^$</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>^$</XML_NAMESPACE>
</AND>
</match>
<order>BY_NAME</order>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
</AND>
</match>
<order>ANDROID_ATTRIBUTE_ORDER</order>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>.*</XML_NAMESPACE>
</AND>
</match>
<order>BY_NAME</order>
</rule>
</section>
</rules>
</arrangement>
</codeStyleSettings>
<codeStyleSettings language="kotlin">
<option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" />
</codeStyleSettings>
</code_scheme>
</component>

View File

@ -1,5 +0,0 @@
<component name="ProjectCodeStyleConfiguration">
<state>
<option name="USE_PER_PROJECT_SETTINGS" value="true" />
</state>
</component>

View File

@ -1,10 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="deploymentTargetDropDown">
<value>
<entry key="app">
<State />
</entry>
</value>
</component>
</project>

View File

@ -1,18 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="deploymentTargetSelector">
<selectionStates>
<SelectionState runConfigName="app">
<option name="selectionMode" value="DROPDOWN" />
<DropdownSelection timestamp="2024-10-14T08:13:14.161127Z">
<Target type="DEFAULT_BOOT">
<handle>
<DeviceId pluginId="PhysicalDevice" identifier="serial=2cec640c34017ece" />
</handle>
</Target>
</DropdownSelection>
<DialogSelection />
</SelectionState>
</selectionStates>
</component>
</project>

View File

@ -1,10 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectMigrations">
<option name="MigrateToGradleLocalJavaHome">
<set>
<option value="$PROJECT_DIR$" />
</set>
</option>
</component>
</project>

View File

@ -1,329 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="direct_access_persist.xml">
<option name="deviceSelectionList">
<list>
<PersistentDeviceSelectionData>
<option name="api" value="27" />
<option name="brand" value="DOCOMO" />
<option name="codename" value="F01L" />
<option name="id" value="F01L" />
<option name="manufacturer" value="FUJITSU" />
<option name="name" value="F-01L" />
<option name="screenDensity" value="360" />
<option name="screenX" value="720" />
<option name="screenY" value="1280" />
</PersistentDeviceSelectionData>
<PersistentDeviceSelectionData>
<option name="api" value="28" />
<option name="brand" value="DOCOMO" />
<option name="codename" value="SH-01L" />
<option name="id" value="SH-01L" />
<option name="manufacturer" value="SHARP" />
<option name="name" value="AQUOS sense2 SH-01L" />
<option name="screenDensity" value="480" />
<option name="screenX" value="1080" />
<option name="screenY" value="2160" />
</PersistentDeviceSelectionData>
<PersistentDeviceSelectionData>
<option name="api" value="34" />
<option name="brand" value="Lenovo" />
<option name="codename" value="TB370FU" />
<option name="id" value="TB370FU" />
<option name="manufacturer" value="Lenovo" />
<option name="name" value="Tab P12" />
<option name="screenDensity" value="340" />
<option name="screenX" value="1840" />
<option name="screenY" value="2944" />
</PersistentDeviceSelectionData>
<PersistentDeviceSelectionData>
<option name="api" value="31" />
<option name="brand" value="samsung" />
<option name="codename" value="a51" />
<option name="id" value="a51" />
<option name="manufacturer" value="Samsung" />
<option name="name" value="Galaxy A51" />
<option name="screenDensity" value="420" />
<option name="screenX" value="1080" />
<option name="screenY" value="2400" />
</PersistentDeviceSelectionData>
<PersistentDeviceSelectionData>
<option name="api" value="34" />
<option name="brand" value="google" />
<option name="codename" value="akita" />
<option name="id" value="akita" />
<option name="manufacturer" value="Google" />
<option name="name" value="Pixel 8a" />
<option name="screenDensity" value="420" />
<option name="screenX" value="1080" />
<option name="screenY" value="2400" />
</PersistentDeviceSelectionData>
<PersistentDeviceSelectionData>
<option name="api" value="33" />
<option name="brand" value="samsung" />
<option name="codename" value="b0q" />
<option name="id" value="b0q" />
<option name="manufacturer" value="Samsung" />
<option name="name" value="Galaxy S22 Ultra" />
<option name="screenDensity" value="600" />
<option name="screenX" value="1440" />
<option name="screenY" value="3088" />
</PersistentDeviceSelectionData>
<PersistentDeviceSelectionData>
<option name="api" value="32" />
<option name="brand" value="google" />
<option name="codename" value="bluejay" />
<option name="id" value="bluejay" />
<option name="manufacturer" value="Google" />
<option name="name" value="Pixel 6a" />
<option name="screenDensity" value="420" />
<option name="screenX" value="1080" />
<option name="screenY" value="2400" />
</PersistentDeviceSelectionData>
<PersistentDeviceSelectionData>
<option name="api" value="34" />
<option name="brand" value="google" />
<option name="codename" value="caiman" />
<option name="id" value="caiman" />
<option name="manufacturer" value="Google" />
<option name="name" value="Pixel 9 Pro" />
<option name="screenDensity" value="360" />
<option name="screenX" value="960" />
<option name="screenY" value="2142" />
</PersistentDeviceSelectionData>
<PersistentDeviceSelectionData>
<option name="api" value="34" />
<option name="brand" value="google" />
<option name="codename" value="comet" />
<option name="id" value="comet" />
<option name="manufacturer" value="Google" />
<option name="name" value="Pixel 9 Pro Fold" />
<option name="screenDensity" value="390" />
<option name="screenX" value="2076" />
<option name="screenY" value="2152" />
</PersistentDeviceSelectionData>
<PersistentDeviceSelectionData>
<option name="api" value="29" />
<option name="brand" value="samsung" />
<option name="codename" value="crownqlteue" />
<option name="id" value="crownqlteue" />
<option name="manufacturer" value="Samsung" />
<option name="name" value="Galaxy Note9" />
<option name="screenDensity" value="420" />
<option name="screenX" value="2220" />
<option name="screenY" value="1080" />
</PersistentDeviceSelectionData>
<PersistentDeviceSelectionData>
<option name="api" value="34" />
<option name="brand" value="samsung" />
<option name="codename" value="dm3q" />
<option name="id" value="dm3q" />
<option name="manufacturer" value="Samsung" />
<option name="name" value="Galaxy S23 Ultra" />
<option name="screenDensity" value="600" />
<option name="screenX" value="1440" />
<option name="screenY" value="3088" />
</PersistentDeviceSelectionData>
<PersistentDeviceSelectionData>
<option name="api" value="34" />
<option name="brand" value="samsung" />
<option name="codename" value="e1q" />
<option name="id" value="e1q" />
<option name="manufacturer" value="Samsung" />
<option name="name" value="Galaxy S24" />
<option name="screenDensity" value="480" />
<option name="screenX" value="1080" />
<option name="screenY" value="2340" />
</PersistentDeviceSelectionData>
<PersistentDeviceSelectionData>
<option name="api" value="33" />
<option name="brand" value="google" />
<option name="codename" value="felix" />
<option name="id" value="felix" />
<option name="manufacturer" value="Google" />
<option name="name" value="Pixel Fold" />
<option name="screenDensity" value="420" />
<option name="screenX" value="2208" />
<option name="screenY" value="1840" />
</PersistentDeviceSelectionData>
<PersistentDeviceSelectionData>
<option name="api" value="34" />
<option name="brand" value="google" />
<option name="codename" value="felix" />
<option name="id" value="felix" />
<option name="manufacturer" value="Google" />
<option name="name" value="Pixel Fold" />
<option name="screenDensity" value="420" />
<option name="screenX" value="2208" />
<option name="screenY" value="1840" />
</PersistentDeviceSelectionData>
<PersistentDeviceSelectionData>
<option name="api" value="33" />
<option name="brand" value="google" />
<option name="codename" value="felix_camera" />
<option name="id" value="felix_camera" />
<option name="manufacturer" value="Google" />
<option name="name" value="Pixel Fold (Camera-enabled)" />
<option name="screenDensity" value="420" />
<option name="screenX" value="2208" />
<option name="screenY" value="1840" />
</PersistentDeviceSelectionData>
<PersistentDeviceSelectionData>
<option name="api" value="33" />
<option name="brand" value="samsung" />
<option name="codename" value="gts8uwifi" />
<option name="id" value="gts8uwifi" />
<option name="manufacturer" value="Samsung" />
<option name="name" value="Galaxy Tab S8 Ultra" />
<option name="screenDensity" value="320" />
<option name="screenX" value="1848" />
<option name="screenY" value="2960" />
</PersistentDeviceSelectionData>
<PersistentDeviceSelectionData>
<option name="api" value="34" />
<option name="brand" value="google" />
<option name="codename" value="husky" />
<option name="id" value="husky" />
<option name="manufacturer" value="Google" />
<option name="name" value="Pixel 8 Pro" />
<option name="screenDensity" value="390" />
<option name="screenX" value="1008" />
<option name="screenY" value="2244" />
</PersistentDeviceSelectionData>
<PersistentDeviceSelectionData>
<option name="api" value="30" />
<option name="brand" value="motorola" />
<option name="codename" value="java" />
<option name="id" value="java" />
<option name="manufacturer" value="Motorola" />
<option name="name" value="G20" />
<option name="screenDensity" value="280" />
<option name="screenX" value="720" />
<option name="screenY" value="1600" />
</PersistentDeviceSelectionData>
<PersistentDeviceSelectionData>
<option name="api" value="34" />
<option name="brand" value="google" />
<option name="codename" value="komodo" />
<option name="id" value="komodo" />
<option name="manufacturer" value="Google" />
<option name="name" value="Pixel 9 Pro XL" />
<option name="screenDensity" value="360" />
<option name="screenX" value="1008" />
<option name="screenY" value="2244" />
</PersistentDeviceSelectionData>
<PersistentDeviceSelectionData>
<option name="api" value="33" />
<option name="brand" value="google" />
<option name="codename" value="lynx" />
<option name="id" value="lynx" />
<option name="manufacturer" value="Google" />
<option name="name" value="Pixel 7a" />
<option name="screenDensity" value="420" />
<option name="screenX" value="1080" />
<option name="screenY" value="2400" />
</PersistentDeviceSelectionData>
<PersistentDeviceSelectionData>
<option name="api" value="31" />
<option name="brand" value="google" />
<option name="codename" value="oriole" />
<option name="id" value="oriole" />
<option name="manufacturer" value="Google" />
<option name="name" value="Pixel 6" />
<option name="screenDensity" value="420" />
<option name="screenX" value="1080" />
<option name="screenY" value="2400" />
</PersistentDeviceSelectionData>
<PersistentDeviceSelectionData>
<option name="api" value="33" />
<option name="brand" value="google" />
<option name="codename" value="panther" />
<option name="id" value="panther" />
<option name="manufacturer" value="Google" />
<option name="name" value="Pixel 7" />
<option name="screenDensity" value="420" />
<option name="screenX" value="1080" />
<option name="screenY" value="2400" />
</PersistentDeviceSelectionData>
<PersistentDeviceSelectionData>
<option name="api" value="34" />
<option name="brand" value="samsung" />
<option name="codename" value="q5q" />
<option name="id" value="q5q" />
<option name="manufacturer" value="Samsung" />
<option name="name" value="Galaxy Z Fold5" />
<option name="screenDensity" value="420" />
<option name="screenX" value="1812" />
<option name="screenY" value="2176" />
</PersistentDeviceSelectionData>
<PersistentDeviceSelectionData>
<option name="api" value="34" />
<option name="brand" value="samsung" />
<option name="codename" value="q6q" />
<option name="id" value="q6q" />
<option name="manufacturer" value="Samsung" />
<option name="name" value="SM-F956B" />
<option name="screenDensity" value="420" />
<option name="screenX" value="1856" />
<option name="screenY" value="2160" />
</PersistentDeviceSelectionData>
<PersistentDeviceSelectionData>
<option name="api" value="30" />
<option name="brand" value="google" />
<option name="codename" value="r11" />
<option name="id" value="r11" />
<option name="manufacturer" value="Google" />
<option name="name" value="Pixel Watch" />
<option name="screenDensity" value="320" />
<option name="screenX" value="384" />
<option name="screenY" value="384" />
<option name="type" value="WEAR_OS" />
</PersistentDeviceSelectionData>
<PersistentDeviceSelectionData>
<option name="api" value="30" />
<option name="brand" value="google" />
<option name="codename" value="redfin" />
<option name="id" value="redfin" />
<option name="manufacturer" value="Google" />
<option name="name" value="Pixel 5" />
<option name="screenDensity" value="440" />
<option name="screenX" value="1080" />
<option name="screenY" value="2340" />
</PersistentDeviceSelectionData>
<PersistentDeviceSelectionData>
<option name="api" value="34" />
<option name="brand" value="google" />
<option name="codename" value="shiba" />
<option name="id" value="shiba" />
<option name="manufacturer" value="Google" />
<option name="name" value="Pixel 8" />
<option name="screenDensity" value="420" />
<option name="screenX" value="1080" />
<option name="screenY" value="2400" />
</PersistentDeviceSelectionData>
<PersistentDeviceSelectionData>
<option name="api" value="33" />
<option name="brand" value="google" />
<option name="codename" value="tangorpro" />
<option name="id" value="tangorpro" />
<option name="manufacturer" value="Google" />
<option name="name" value="Pixel Tablet" />
<option name="screenDensity" value="320" />
<option name="screenX" value="1600" />
<option name="screenY" value="2560" />
</PersistentDeviceSelectionData>
<PersistentDeviceSelectionData>
<option name="api" value="34" />
<option name="brand" value="google" />
<option name="codename" value="tokay" />
<option name="id" value="tokay" />
<option name="manufacturer" value="Google" />
<option name="name" value="Pixel 9" />
<option name="screenDensity" value="420" />
<option name="screenX" value="1080" />
<option name="screenY" value="2424" />
</PersistentDeviceSelectionData>
</list>
</option>
</component>
</project>

View File

@ -14,7 +14,7 @@ plugins {
android { android {
namespace 'kr.co.vividnext.sodalive' namespace 'kr.co.vividnext.sodalive'
compileSdk 34 compileSdk 33
viewBinding { viewBinding {
enabled true enabled true
@ -24,6 +24,11 @@ android {
dataBinding true dataBinding true
} }
lintOptions {
checkDependencies true
checkReleaseBuilds false
}
dependenciesInfo { dependenciesInfo {
// Disables dependency metadata when building APKs. // Disables dependency metadata when building APKs.
includeInApk = false includeInApk = false
@ -34,9 +39,9 @@ android {
defaultConfig { defaultConfig {
applicationId "kr.co.vividnext.sodalive" applicationId "kr.co.vividnext.sodalive"
minSdk 23 minSdk 23
targetSdk 34 targetSdk 33
versionCode 122 versionCode 10
versionName "1.22.3" versionName "1.1.0"
} }
buildTypes { buildTypes {
@ -46,20 +51,17 @@ android {
buildConfigField 'String', 'BASE_URL', '"https://api.sodalive.net"' buildConfigField 'String', 'BASE_URL', '"https://api.sodalive.net"'
buildConfigField 'String', 'BOOTPAY_APP_ID', '"64c35be1d25985001dc50c87"' buildConfigField 'String', 'BOOTPAY_APP_ID', '"64c35be1d25985001dc50c87"'
buildConfigField 'String', 'BOOTPAY_APP_HECTO_ID', '"664c1707b18b225deca4b429"'
buildConfigField 'String', 'AGORA_APP_ID', '"e34e40046e9847baba3adfe2b8ffb4f6"' buildConfigField 'String', 'AGORA_APP_ID', '"e34e40046e9847baba3adfe2b8ffb4f6"'
buildConfigField 'String', 'AGORA_APP_CERTIFICATE', '"15cadeea4ba94ff7b091c9a10f4bf4a6"' buildConfigField 'String', 'AGORA_APP_CERTIFICATE', '"15cadeea4ba94ff7b091c9a10f4bf4a6"'
} }
debug { debug {
minifyEnabled true minifyEnabled false
debuggable true
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
applicationIdSuffix '.debug' applicationIdSuffix '.debug'
buildConfigField 'String', 'BASE_URL', '"https://test-api.sodalive.net"' buildConfigField 'String', 'BASE_URL', '"https://test-api.sodalive.net"'
buildConfigField 'String', 'BOOTPAY_APP_ID', '"6242a7772701800023f68b2e"' buildConfigField 'String', 'BOOTPAY_APP_ID', '"6242a7772701800023f68b2e"'
buildConfigField 'String', 'BOOTPAY_APP_HECTO_ID', '"667fca5d3bab7404f831c3e4"'
buildConfigField 'String', 'AGORA_APP_ID', '"b96574e191a9430fa54c605528aa3ef7"' buildConfigField 'String', 'AGORA_APP_ID', '"b96574e191a9430fa54c605528aa3ef7"'
buildConfigField 'String', 'AGORA_APP_CERTIFICATE', '"ae18ade3afcf4086bd4397726eb0654c"' buildConfigField 'String', 'AGORA_APP_CERTIFICATE', '"ae18ade3afcf4086bd4397726eb0654c"'
} }
@ -71,10 +73,6 @@ android {
kotlinOptions { kotlinOptions {
jvmTarget = JavaVersion.VERSION_17.toString() jvmTarget = JavaVersion.VERSION_17.toString()
} }
lint {
checkDependencies true
checkReleaseBuilds false
}
} }
dependencies { dependencies {
@ -137,10 +135,10 @@ dependencies {
implementation 'com.google.firebase:firebase-config-ktx' implementation 'com.google.firebase:firebase-config-ktx'
// bootpay // bootpay
implementation "io.github.bootpay:android:4.4.3" implementation "io.github.bootpay:android:4.3.4"
// agora // agora
implementation "io.agora.rtc:voice-sdk:4.2.6" implementation "io.agora.rtc:voice-sdk:4.1.0-1"
implementation 'io.agora.rtm:rtm-sdk:1.5.3' implementation 'io.agora.rtm:rtm-sdk:1.5.3'
// sound visualizer // sound visualizer
@ -152,13 +150,6 @@ dependencies {
implementation "com.michalsvec:single-row-calednar:1.0.0" implementation "com.michalsvec:single-row-calednar:1.0.0"
// google in-app-purchase // PointClick Maven Remote Repo
implementation "com.android.billingclient:billing-ktx:6.2.0" implementation 'kr.co.pointclick.sdk.offerwall:pointclick-sdk-offerwall:1.0.17'
// ROOM
kapt "androidx.room:room-compiler:2.5.0"
implementation "androidx.room:room-ktx:2.5.0"
implementation "androidx.room:room-runtime:2.5.0"
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.4"
} }

View File

@ -222,10 +222,9 @@
-keep class kr.co.bootpay.core.** { *; } -keep class kr.co.bootpay.core.** { *; }
-keep class retrofit2.** { *; } -keep class kr.co.pointclick.sdk.offerwall.core.consts.** {*;}
-keep interface kr.co.pointclick.sdk.offerwall.core.consts.** {*;}
-keep class com.google.gson.** { *; } -keep class kr.co.pointclick.sdk.offerwall.core.models.** {*;}
-keep class sun.misc.** { *; } -keep interface kr.co.pointclick.sdk.offerwall.core.models.** {*;}
-keep class kr.co.pointclick.sdk.offerwall.core.PointClickAd {*;}
# @Keep 애노테이션이 붙은 클래스, 메서드, 필드를 보호 -keep class kr.co.pointclick.sdk.offerwall.core.events.PackageReceiver {*;}
-keep @androidx.annotation.Keep class * { *; }

View File

@ -2,18 +2,17 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android" <manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"> xmlns:tools="http://schemas.android.com/tools">
<uses-permission android:name="com.android.vending.BILLING" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_MICROPHONE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_MEDIA_PLAYBACK" />
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" /> <uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
<uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.INTERNET" />
<uses-permission <uses-permission
android:name="android.permission.READ_EXTERNAL_STORAGE" android:name="android.permission.READ_EXTERNAL_STORAGE"
android:maxSdkVersion="32" /> android:maxSdkVersion="32" />
<uses-permission android:name="android.permission.READ_MEDIA_AUDIO" /> <uses-permission android:name="android.permission.READ_MEDIA_AUDIO" />
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
<uses-permission android:name="android.permission.RECORD_AUDIO" /> <uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" /> <uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
<uses-permission android:name="android.permission.USE_FULL_SCREEN_INTENT" />
<uses-permission android:name="android.permission.VIBRATE" /> <uses-permission android:name="android.permission.VIBRATE" />
<uses-permission <uses-permission
android:name="android.permission.BLUETOOTH" android:name="android.permission.BLUETOOTH"
@ -38,13 +37,6 @@
android:maxSdkVersion="32" android:maxSdkVersion="32"
tools:ignore="ScopedStorage" /> tools:ignore="ScopedStorage" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="com.android.alarm.permission.SET_ALARM" />
<uses-permission android:name="android.permission.SCHEDULE_EXACT_ALARM" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
<queries> <queries>
<intent> <intent>
<action android:name="android.intent.action.MAIN" /> <action android:name="android.intent.action.MAIN" />
@ -91,12 +83,8 @@
<activity android:name=".settings.terms.TermsActivity" /> <activity android:name=".settings.terms.TermsActivity" />
<activity android:name=".user.find_password.FindPasswordActivity" /> <activity android:name=".user.find_password.FindPasswordActivity" />
<activity android:name=".mypage.can.status.CanStatusActivity" /> <activity android:name=".mypage.can.status.CanStatusActivity" />
<activity <activity android:name=".mypage.can.charge.CanChargeActivity" />
android:name=".mypage.can.charge.CanChargeActivity"
android:configChanges="orientation|screenSize|keyboardHidden" />
<activity android:name=".mypage.can.payment.CanPaymentActivity" /> <activity android:name=".mypage.can.payment.CanPaymentActivity" />
<activity android:name=".mypage.can.payment.CanPaymentTempActivity" />
<activity android:name=".mypage.can.coupon.CanCouponActivity" />
<activity android:name=".live.room.create.LiveRoomCreateActivity" /> <activity android:name=".live.room.create.LiveRoomCreateActivity" />
<activity android:name=".live.room.update.LiveRoomEditActivity" /> <activity android:name=".live.room.update.LiveRoomEditActivity" />
<activity android:name=".live.reservation.complete.LiveReservationCompleteActivity" /> <activity android:name=".live.reservation.complete.LiveReservationCompleteActivity" />
@ -106,10 +94,8 @@
<activity android:name=".explorer.profile.UserProfileActivity" /> <activity android:name=".explorer.profile.UserProfileActivity" />
<activity android:name=".explorer.profile.donation.UserProfileDonationAllViewActivity" /> <activity android:name=".explorer.profile.donation.UserProfileDonationAllViewActivity" />
<activity android:name=".explorer.profile.fantalk.UserProfileFantalkAllViewActivity" /> <activity android:name=".explorer.profile.fantalk.UserProfileFantalkAllViewActivity" />
<activity android:name=".explorer.profile.CreatorNoticeWriteActivity" />
<activity android:name=".explorer.profile.follow.UserFollowerListActivity" /> <activity android:name=".explorer.profile.follow.UserFollowerListActivity" />
<activity android:name=".explorer.profile.creator_community.all.CreatorCommunityAllActivity" />
<activity android:name=".explorer.profile.creator_community.write.CreatorCommunityWriteActivity" />
<activity android:name=".explorer.profile.creator_community.modify.CreatorCommunityModifyActivity" />
<activity android:name=".message.text.TextMessageWriteActivity" /> <activity android:name=".message.text.TextMessageWriteActivity" />
<activity android:name=".message.text.TextMessageDetailActivity" /> <activity android:name=".message.text.TextMessageDetailActivity" />
<activity android:name=".message.SelectMessageRecipientActivity" /> <activity android:name=".message.SelectMessageRecipientActivity" />
@ -120,7 +106,6 @@
<activity android:name=".settings.event.EventActivity" /> <activity android:name=".settings.event.EventActivity" />
<activity android:name=".settings.event.EventDetailActivity" /> <activity android:name=".settings.event.EventDetailActivity" />
<activity android:name=".settings.notification.NotificationSettingsActivity" /> <activity android:name=".settings.notification.NotificationSettingsActivity" />
<activity android:name=".settings.ContentSettingsActivity" />
<activity android:name=".live.reservation_status.LiveReservationStatusActivity" /> <activity android:name=".live.reservation_status.LiveReservationStatusActivity" />
<activity android:name=".live.reservation_status.LiveReservationCancelActivity" /> <activity android:name=".live.reservation_status.LiveReservationCancelActivity" />
<activity android:name=".audio_content.AudioContentActivity" /> <activity android:name=".audio_content.AudioContentActivity" />
@ -139,27 +124,6 @@
<activity android:name=".audio_content.curation.AudioContentCurationActivity" /> <activity android:name=".audio_content.curation.AudioContentCurationActivity" />
<activity android:name=".audio_content.all.AudioContentNewAllActivity" /> <activity android:name=".audio_content.all.AudioContentNewAllActivity" />
<activity android:name=".audio_content.all.AudioContentRankingAllActivity" /> <activity android:name=".audio_content.all.AudioContentRankingAllActivity" />
<activity android:name=".audio_content.all.by_theme.AudioContentAllByThemeActivity" />
<activity android:name=".live.roulette.config.RouletteConfigActivity" />
<activity android:name=".live.room.menu.MenuConfigActivity" />
<activity android:name=".audio_content.series.SeriesListAllActivity" />
<activity android:name=".audio_content.series.detail.SeriesDetailActivity" />
<activity android:name=".audio_content.series.content.SeriesContentAllActivity" />
<activity android:name=".mypage.alarm.AlarmListActivity" />
<activity android:name=".mypage.alarm.AddAlarmActivity" />
<activity android:name=".mypage.alarm.select_audio_content.AlarmSelectAudioContentActivity" />
<activity android:name=".mypage.block.BlockMemberActivity" />
<activity
android:name=".mypage.alarm.AlarmActivity"
android:exported="true"
android:showWhenLocked="true"
android:turnScreenOn="true">
<intent-filter>
<action android:name="com.example.alarmapp.ALARM_ACTION" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
<activity <activity
android:name="com.google.android.gms.oss.licenses.OssLicensesMenuActivity" android:name="com.google.android.gms.oss.licenses.OssLicensesMenuActivity"
@ -170,13 +134,9 @@
<service <service
android:name=".common.SodaLiveService" android:name=".common.SodaLiveService"
android:foregroundServiceType="microphone|mediaPlayback"
android:stopWithTask="false" /> android:stopWithTask="false" />
<service <service android:name=".audio_content.AudioContentPlayService" />
android:name=".audio_content.AudioContentPlayService"
android:foregroundServiceType="mediaPlayback"
android:stopWithTask="false" />
<!-- [START firebase_service] --> <!-- [START firebase_service] -->
<service <service
@ -188,21 +148,6 @@
</service> </service>
<!-- [END firebase_service] --> <!-- [END firebase_service] -->
<!-- 부팅 시 알람 재설정을 위한 리시버 -->
<receiver
android:name=".mypage.alarm.receiver.AlarmBootReceiver"
android:enabled="true"
android:exported="false">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
</receiver>
<receiver
android:name=".mypage.alarm.receiver.AlarmReceiver"
android:enabled="true"
android:exported="false" />
<!-- [START fcm_default_channel] --> <!-- [START fcm_default_channel] -->
<meta-data <meta-data
android:name="com.google.firebase.messaging.default_notification_channel_id" android:name="com.google.firebase.messaging.default_notification_channel_id"

View File

@ -172,14 +172,13 @@ class Agora(
fun sendRawMessageToPeer( fun sendRawMessageToPeer(
receiverUid: String, receiverUid: String,
requestType: LiveRoomRequestType? = null, requestType: LiveRoomRequestType,
rawMessage: ByteArray? = null,
onSuccess: () -> Unit onSuccess: () -> Unit
) { ) {
val option = SendMessageOptions() val option = SendMessageOptions()
val message = rtmClient!!.createMessage() val message = rtmClient!!.createMessage()
message.rawMessage = rawMessage ?: requestType.toString().toByteArray() message.rawMessage = requestType.toString().toByteArray()
rtmClient!!.sendMessageToPeer( rtmClient!!.sendMessageToPeer(
receiverUid, receiverUid,

View File

@ -8,7 +8,6 @@ import androidx.appcompat.app.AppCompatDelegate
import com.orhanobut.logger.AndroidLogAdapter import com.orhanobut.logger.AndroidLogAdapter
import com.orhanobut.logger.Logger import com.orhanobut.logger.Logger
import kr.co.vividnext.sodalive.BuildConfig import kr.co.vividnext.sodalive.BuildConfig
import kr.co.vividnext.sodalive.common.ImageLoaderProvider
import kr.co.vividnext.sodalive.common.SharedPreferenceManager import kr.co.vividnext.sodalive.common.SharedPreferenceManager
import kr.co.vividnext.sodalive.di.AppDI import kr.co.vividnext.sodalive.di.AppDI
@ -27,8 +26,6 @@ class SodaLiveApp : Application() {
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES) AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES)
SharedPreferenceManager.init(applicationContext) SharedPreferenceManager.init(applicationContext)
ImageLoaderProvider.init(applicationContext)
} }
private fun isDebuggable(): Boolean { private fun isDebuggable(): Boolean {

View File

@ -1,16 +1,13 @@
package kr.co.vividnext.sodalive.audio_content package kr.co.vividnext.sodalive.audio_content
import androidx.annotation.Keep
import com.google.gson.annotations.SerializedName import com.google.gson.annotations.SerializedName
import java.util.TimeZone import java.util.TimeZone
@Keep
data class AddAllPlaybackTrackingRequest( data class AddAllPlaybackTrackingRequest(
@SerializedName("timezone") val timezone: String = TimeZone.getDefault().id, @SerializedName("timezone") val timezone: String = TimeZone.getDefault().id,
@SerializedName("trackingDataList") val trackingDataList: List<PlaybackTrackingData> @SerializedName("trackingDataList") val trackingDataList: List<PlaybackTrackingData>
) )
@Keep
data class PlaybackTrackingData( data class PlaybackTrackingData(
@SerializedName("contentId") val contentId: Long, @SerializedName("contentId") val contentId: Long,
@SerializedName("playDateTime") val playDateTime: String, @SerializedName("playDateTime") val playDateTime: String,

View File

@ -14,8 +14,6 @@ import androidx.core.content.ContextCompat
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import kr.co.vividnext.sodalive.R import kr.co.vividnext.sodalive.R
import kr.co.vividnext.sodalive.audio_content.category.AudioContentCategoryAdapter
import kr.co.vividnext.sodalive.audio_content.category.GetCategoryListResponse
import kr.co.vividnext.sodalive.audio_content.detail.AudioContentDetailActivity import kr.co.vividnext.sodalive.audio_content.detail.AudioContentDetailActivity
import kr.co.vividnext.sodalive.audio_content.upload.AudioContentUploadActivity import kr.co.vividnext.sodalive.audio_content.upload.AudioContentUploadActivity
import kr.co.vividnext.sodalive.base.BaseActivity import kr.co.vividnext.sodalive.base.BaseActivity
@ -34,7 +32,6 @@ class AudioContentActivity : BaseActivity<ActivityAudioContentBinding>(
private lateinit var loadingDialog: LoadingDialog private lateinit var loadingDialog: LoadingDialog
private lateinit var audioContentAdapter: AudioContentAdapter private lateinit var audioContentAdapter: AudioContentAdapter
private lateinit var categoryAdapter: AudioContentCategoryAdapter
private var userId: Long = 0 private var userId: Long = 0
private lateinit var activityResultLauncher: ActivityResultLauncher<Intent> private lateinit var activityResultLauncher: ActivityResultLauncher<Intent>
@ -46,7 +43,6 @@ class AudioContentActivity : BaseActivity<ActivityAudioContentBinding>(
) { ) {
if (it.resultCode == Activity.RESULT_OK) { if (it.resultCode == Activity.RESULT_OK) {
viewModel.page = 1 viewModel.page = 1
viewModel.isLast = false
viewModel.getAudioContentList(userId = userId) { finish() } viewModel.getAudioContentList(userId = userId) { finish() }
} }
} }
@ -58,7 +54,6 @@ class AudioContentActivity : BaseActivity<ActivityAudioContentBinding>(
} }
bindData() bindData()
viewModel.getCategoryList(userId = userId)
viewModel.getAudioContentList(userId = userId) { finish() } viewModel.getAudioContentList(userId = userId) { finish() }
} }
@ -75,67 +70,6 @@ class AudioContentActivity : BaseActivity<ActivityAudioContentBinding>(
activityResultLauncher.launch(intent) activityResultLauncher.launch(intent)
} }
categoryAdapter = AudioContentCategoryAdapter {
viewModel.selectCategory(it, userId = userId)
}
binding.rvCategory.layoutManager = LinearLayoutManager(
applicationContext,
LinearLayoutManager.HORIZONTAL,
false
)
binding.rvCategory.addItemDecoration(object : RecyclerView.ItemDecoration() {
override fun getItemOffsets(
outRect: Rect,
view: View,
parent: RecyclerView,
state: RecyclerView.State
) {
super.getItemOffsets(outRect, view, parent, state)
when (parent.getChildAdapterPosition(view)) {
0 -> {
outRect.left = 0
outRect.right = 4f.dpToPx().toInt()
}
categoryAdapter.itemCount - 1 -> {
outRect.left = 4f.dpToPx().toInt()
outRect.right = 0
}
else -> {
outRect.left = 4f.dpToPx().toInt()
outRect.right = 4f.dpToPx().toInt()
}
}
}
})
binding.rvCategory.adapter = categoryAdapter
binding.rvAudioContent.addItemDecoration(object : RecyclerView.ItemDecoration() {
override fun getItemOffsets(
outRect: Rect,
view: View,
parent: RecyclerView,
state: RecyclerView.State
) {
super.getItemOffsets(outRect, view, parent, state)
when (parent.getChildAdapterPosition(view)) {
audioContentAdapter.itemCount - 1 -> {
outRect.bottom = 0
}
else -> {
outRect.bottom = 13.3f.dpToPx().toInt()
}
}
}
})
binding.rvAudioContent.layoutManager = LinearLayoutManager( binding.rvAudioContent.layoutManager = LinearLayoutManager(
applicationContext, applicationContext,
LinearLayoutManager.VERTICAL, LinearLayoutManager.VERTICAL,
@ -254,17 +188,6 @@ class AudioContentActivity : BaseActivity<ActivityAudioContentBinding>(
) )
viewModel.getAudioContentList(userId = userId) { finish() } viewModel.getAudioContentList(userId = userId) { finish() }
} }
viewModel.categoryListLiveData.observe(this) {
if (it.isNotEmpty()) {
binding.rvCategory.visibility = View.VISIBLE
val items = it as MutableList<GetCategoryListResponse>
items.add(0, GetCategoryListResponse(0, "전체"))
categoryAdapter.addItems(items = items)
} else {
binding.rvCategory.visibility = View.GONE
}
}
} }
private fun deselectSort() { private fun deselectSort() {

View File

@ -1,7 +1,6 @@
package kr.co.vividnext.sodalive.audio_content package kr.co.vividnext.sodalive.audio_content
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import coil.load import coil.load
@ -22,12 +21,6 @@ class AudioContentAdapter(
private val binding: ItemAudioContentBinding private val binding: ItemAudioContentBinding
) : RecyclerView.ViewHolder(binding.root) { ) : RecyclerView.ViewHolder(binding.root) {
fun bind(item: GetAudioContentListItem) { fun bind(item: GetAudioContentListItem) {
binding.ivPin.visibility = if (item.isPin) {
View.VISIBLE
} else {
View.GONE
}
binding.ivCover.load(item.coverImageUrl) { binding.ivCover.load(item.coverImageUrl) {
crossfade(true) crossfade(true)
placeholder(R.drawable.bg_placeholder) placeholder(R.drawable.bg_placeholder)
@ -40,25 +33,6 @@ class AudioContentAdapter(
binding.tvLikeCount.text = item.likeCount.moneyFormat() binding.tvLikeCount.text = item.likeCount.moneyFormat()
binding.tvCommentCount.text = item.commentCount.moneyFormat() binding.tvCommentCount.text = item.commentCount.moneyFormat()
binding.tvPrice.visibility = View.GONE
binding.tvOwned.visibility = View.GONE
binding.tvRented.visibility = View.GONE
binding.tvSoldOut.visibility = View.GONE
binding.tvScheduledToOpen.visibility = if (item.isScheduledToOpen) {
View.VISIBLE
} else {
View.GONE
}
if (item.isOwned) {
binding.tvOwned.visibility = View.VISIBLE
} else if (item.isRented) {
binding.tvRented.visibility = View.VISIBLE
} else if (item.isSoldOut) {
binding.tvSoldOut.visibility = View.VISIBLE
} else {
binding.tvPrice.visibility = View.VISIBLE
if (item.price < 1) { if (item.price < 1) {
binding.tvPrice.text = "무료" binding.tvPrice.text = "무료"
binding.tvPrice.setCompoundDrawables(null, null, null, null) binding.tvPrice.setCompoundDrawables(null, null, null, null)
@ -71,7 +45,6 @@ class AudioContentAdapter(
0 0
) )
} }
}
binding.root.setOnClickListener { onClickItem(item.contentId) } binding.root.setOnClickListener { onClickItem(item.contentId) }
} }

View File

@ -2,7 +2,6 @@ package kr.co.vividnext.sodalive.audio_content
import io.reactivex.rxjava3.core.Single import io.reactivex.rxjava3.core.Single
import kr.co.vividnext.sodalive.audio_content.all.GetNewContentAllResponse import kr.co.vividnext.sodalive.audio_content.all.GetNewContentAllResponse
import kr.co.vividnext.sodalive.audio_content.all.by_theme.GetContentByThemeResponse
import kr.co.vividnext.sodalive.audio_content.comment.GetAudioContentCommentListResponse import kr.co.vividnext.sodalive.audio_content.comment.GetAudioContentCommentListResponse
import kr.co.vividnext.sodalive.audio_content.comment.ModifyCommentRequest import kr.co.vividnext.sodalive.audio_content.comment.ModifyCommentRequest
import kr.co.vividnext.sodalive.audio_content.comment.RegisterAudioContentCommentRequest import kr.co.vividnext.sodalive.audio_content.comment.RegisterAudioContentCommentRequest
@ -11,17 +10,14 @@ import kr.co.vividnext.sodalive.audio_content.detail.GetAudioContentDetailRespon
import kr.co.vividnext.sodalive.audio_content.detail.PutAudioContentLikeRequest import kr.co.vividnext.sodalive.audio_content.detail.PutAudioContentLikeRequest
import kr.co.vividnext.sodalive.audio_content.detail.PutAudioContentLikeResponse import kr.co.vividnext.sodalive.audio_content.detail.PutAudioContentLikeResponse
import kr.co.vividnext.sodalive.audio_content.donation.AudioContentDonationRequest import kr.co.vividnext.sodalive.audio_content.donation.AudioContentDonationRequest
import kr.co.vividnext.sodalive.audio_content.main.GetAudioContentBannerResponse
import kr.co.vividnext.sodalive.audio_content.main.GetAudioContentCurationResponse
import kr.co.vividnext.sodalive.audio_content.main.GetAudioContentMainItem import kr.co.vividnext.sodalive.audio_content.main.GetAudioContentMainItem
import kr.co.vividnext.sodalive.audio_content.main.GetAudioContentMainResponse
import kr.co.vividnext.sodalive.audio_content.main.GetAudioContentRanking import kr.co.vividnext.sodalive.audio_content.main.GetAudioContentRanking
import kr.co.vividnext.sodalive.audio_content.main.GetNewContentUploadCreator
import kr.co.vividnext.sodalive.audio_content.order.GetAudioContentOrderListResponse import kr.co.vividnext.sodalive.audio_content.order.GetAudioContentOrderListResponse
import kr.co.vividnext.sodalive.audio_content.order.OrderRequest import kr.co.vividnext.sodalive.audio_content.order.OrderRequest
import kr.co.vividnext.sodalive.audio_content.upload.theme.GetAudioContentThemeResponse import kr.co.vividnext.sodalive.audio_content.upload.theme.GetAudioContentThemeResponse
import kr.co.vividnext.sodalive.common.ApiResponse import kr.co.vividnext.sodalive.common.ApiResponse
import kr.co.vividnext.sodalive.explorer.profile.GetAudioContentListResponse import kr.co.vividnext.sodalive.explorer.profile.GetAudioContentListResponse
import kr.co.vividnext.sodalive.settings.ContentType
import okhttp3.MultipartBody import okhttp3.MultipartBody
import okhttp3.RequestBody import okhttp3.RequestBody
import retrofit2.http.Body import retrofit2.http.Body
@ -39,7 +35,6 @@ interface AudioContentApi {
@GET("/audio-content") @GET("/audio-content")
fun getAudioContentList( fun getAudioContentList(
@Query("creator-id") id: Long, @Query("creator-id") id: Long,
@Query("category-id") categoryId: Long,
@Query("page") page: Int, @Query("page") page: Int,
@Query("size") size: Int, @Query("size") size: Int,
@Query("sort-type") sort: AudioContentViewModel.Sort, @Query("sort-type") sort: AudioContentViewModel.Sort,
@ -51,17 +46,6 @@ interface AudioContentApi {
@Header("Authorization") authHeader: String @Header("Authorization") authHeader: String
): Single<ApiResponse<List<GetAudioContentThemeResponse>>> ): Single<ApiResponse<List<GetAudioContentThemeResponse>>>
@GET("/audio-content/theme/{id}/content")
fun getAudioContentByTheme(
@Path("id") id: Long,
@Query("isAdultContentVisible") isAdultContentVisible: Boolean,
@Query("contentType") contentType: ContentType,
@Query("page") page: Int,
@Query("size") size: Int,
@Query("sort-type") sort: AudioContentViewModel.Sort,
@Header("Authorization") authHeader: String
): Single<ApiResponse<GetContentByThemeResponse>>
@POST("/audio-content") @POST("/audio-content")
@Multipart @Multipart
fun uploadAudioContent( fun uploadAudioContent(
@ -141,19 +125,20 @@ interface AudioContentApi {
@Header("Authorization") authHeader: String @Header("Authorization") authHeader: String
): Single<ApiResponse<Any>> ): Single<ApiResponse<Any>>
@GET("/audio-content/main")
fun getMain(
@Header("Authorization") authHeader: String
): Single<ApiResponse<GetAudioContentMainResponse>>
@GET("/audio-content/main/new") @GET("/audio-content/main/new")
fun getNewContentOfTheme( fun getNewContentOfTheme(
@Query("theme") theme: String, @Query("theme") theme: String,
@Query("isAdultContentVisible") isAdultContentVisible: Boolean,
@Query("contentType") contentType: ContentType,
@Header("Authorization") authHeader: String @Header("Authorization") authHeader: String
): Single<ApiResponse<List<GetAudioContentMainItem>>> ): Single<ApiResponse<List<GetAudioContentMainItem>>>
@GET("/audio-content/main/new/all") @GET("/audio-content/main/new/all")
fun getNewContentAllOfTheme( fun getNewContentAllOfTheme(
@Query("theme") theme: String, @Query("theme") theme: String,
@Query("isAdultContentVisible") isAdultContentVisible: Boolean,
@Query("contentType") contentType: ContentType,
@Query("page") page: Int, @Query("page") page: Int,
@Query("size") size: Int, @Query("size") size: Int,
@Header("Authorization") authHeader: String @Header("Authorization") authHeader: String
@ -174,8 +159,6 @@ interface AudioContentApi {
@GET("/audio-content/curation/{id}") @GET("/audio-content/curation/{id}")
fun getAudioContentListByCurationId( fun getAudioContentListByCurationId(
@Path("id") id: Long, @Path("id") id: Long,
@Query("isAdultContentVisible") isAdultContentVisible: Boolean,
@Query("contentType") contentType: ContentType,
@Query("page") page: Int, @Query("page") page: Int,
@Query("size") size: Int, @Query("size") size: Int,
@Query("sort-type") sort: AudioContentViewModel.Sort, @Query("sort-type") sort: AudioContentViewModel.Sort,
@ -199,40 +182,4 @@ interface AudioContentApi {
@Query("sort-type") sortType: String, @Query("sort-type") sortType: String,
@Header("Authorization") authHeader: String @Header("Authorization") authHeader: String
): Single<ApiResponse<GetAudioContentRanking>> ): Single<ApiResponse<GetAudioContentRanking>>
@GET("/audio-content/main/curation-list")
fun getCurationList(
@Query("isAdultContentVisible") isAdultContentVisible: Boolean,
@Query("contentType") contentType: ContentType,
@Query("page") page: Int,
@Query("size") size: Int,
@Header("Authorization") authHeader: String
): Single<ApiResponse<List<GetAudioContentCurationResponse>>>
@GET("/audio-content/main/new-content-upload-creator")
fun getNewContentUploadCreatorList(
@Header("Authorization") authHeader: String
): Single<ApiResponse<List<GetNewContentUploadCreator>>>
@GET("/audio-content/main/banner-list")
fun getMainBannerList(
@Header("Authorization") authHeader: String
): Single<ApiResponse<List<GetAudioContentBannerResponse>>>
@GET("/audio-content/main/order-list")
fun getMainOrderList(
@Header("Authorization") authHeader: String
): Single<ApiResponse<List<GetAudioContentMainItem>>>
@POST("/audio-content/pin-to-the-top/{id}")
fun pinContent(
@Path("id") audioContentId: Long,
@Header("Authorization") authHeader: String
): Single<ApiResponse<Any>>
@PUT("/audio-content/unpin-at-the-top/{id}")
fun unpinContent(
@Path("id") audioContentId: Long,
@Header("Authorization") authHeader: String
): Single<ApiResponse<Any>>
} }

View File

@ -6,7 +6,6 @@ import android.app.PendingIntent
import android.app.Service import android.app.Service
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_MEDIA_PLAYBACK
import android.graphics.Bitmap import android.graphics.Bitmap
import android.graphics.drawable.Drawable import android.graphics.drawable.Drawable
import android.media.AudioAttributes import android.media.AudioAttributes
@ -52,7 +51,6 @@ class AudioContentPlayService :
putExtra(Constants.EXTRA_AUDIO_CONTENT_PROGRESS, mediaPlayer.currentPosition) putExtra(Constants.EXTRA_AUDIO_CONTENT_PROGRESS, mediaPlayer.currentPosition)
putExtra(Constants.EXTRA_AUDIO_CONTENT_ID, contentId) putExtra(Constants.EXTRA_AUDIO_CONTENT_ID, contentId)
} }
intent.setPackage(packageName)
sendBroadcast(intent) sendBroadcast(intent)
handler.postDelayed(this, 1000) handler.postDelayed(this, 1000)
} }
@ -66,7 +64,6 @@ class AudioContentPlayService :
sendBroadcast( sendBroadcast(
Intent(Constants.ACTION_AUDIO_CONTENT_RECEIVER) Intent(Constants.ACTION_AUDIO_CONTENT_RECEIVER)
.apply { .apply {
setPackage(packageName)
putExtra( putExtra(
Constants.EXTRA_AUDIO_CONTENT_CHANGE_UI, Constants.EXTRA_AUDIO_CONTENT_CHANGE_UI,
true true
@ -104,7 +101,6 @@ class AudioContentPlayService :
sendBroadcast( sendBroadcast(
Intent(Constants.ACTION_MAIN_AUDIO_CONTENT_RECEIVER) Intent(Constants.ACTION_MAIN_AUDIO_CONTENT_RECEIVER)
.apply { .apply {
setPackage(packageName)
putExtra( putExtra(
Constants.EXTRA_AUDIO_CONTENT_SHOWING, Constants.EXTRA_AUDIO_CONTENT_SHOWING,
true true
@ -140,7 +136,6 @@ class AudioContentPlayService :
sendBroadcast( sendBroadcast(
Intent(Constants.ACTION_MAIN_AUDIO_CONTENT_RECEIVER) Intent(Constants.ACTION_MAIN_AUDIO_CONTENT_RECEIVER)
.apply { .apply {
setPackage(packageName)
putExtra( putExtra(
Constants.EXTRA_AUDIO_CONTENT_SHOWING, Constants.EXTRA_AUDIO_CONTENT_SHOWING,
false false
@ -210,7 +205,6 @@ class AudioContentPlayService :
sendBroadcast( sendBroadcast(
Intent(Constants.ACTION_AUDIO_CONTENT_RECEIVER) Intent(Constants.ACTION_AUDIO_CONTENT_RECEIVER)
.apply { .apply {
setPackage(packageName)
putExtra( putExtra(
Constants.EXTRA_AUDIO_CONTENT_NEXT_ACTION, Constants.EXTRA_AUDIO_CONTENT_NEXT_ACTION,
MusicAction.PAUSE MusicAction.PAUSE
@ -226,7 +220,6 @@ class AudioContentPlayService :
sendBroadcast( sendBroadcast(
Intent(Constants.ACTION_AUDIO_CONTENT_RECEIVER) Intent(Constants.ACTION_AUDIO_CONTENT_RECEIVER)
.apply { .apply {
setPackage(packageName)
putExtra( putExtra(
Constants.EXTRA_AUDIO_CONTENT_NEXT_ACTION, Constants.EXTRA_AUDIO_CONTENT_NEXT_ACTION,
MusicAction.PLAY MusicAction.PLAY
@ -261,7 +254,6 @@ class AudioContentPlayService :
sendBroadcast( sendBroadcast(
Intent(Constants.ACTION_AUDIO_CONTENT_RECEIVER) Intent(Constants.ACTION_AUDIO_CONTENT_RECEIVER)
.apply { .apply {
setPackage(packageName)
putExtra( putExtra(
Constants.EXTRA_AUDIO_CONTENT_LOADING, Constants.EXTRA_AUDIO_CONTENT_LOADING,
true true
@ -325,7 +317,6 @@ class AudioContentPlayService :
sendBroadcast( sendBroadcast(
Intent(Constants.ACTION_AUDIO_CONTENT_RECEIVER) Intent(Constants.ACTION_AUDIO_CONTENT_RECEIVER)
.apply { .apply {
setPackage(packageName)
putExtra( putExtra(
Constants.EXTRA_AUDIO_CONTENT_CHANGE_UI, Constants.EXTRA_AUDIO_CONTENT_CHANGE_UI,
true true
@ -350,7 +341,6 @@ class AudioContentPlayService :
sendBroadcast( sendBroadcast(
Intent(Constants.ACTION_MAIN_AUDIO_CONTENT_RECEIVER) Intent(Constants.ACTION_MAIN_AUDIO_CONTENT_RECEIVER)
.apply { .apply {
setPackage(packageName)
putExtra( putExtra(
Constants.EXTRA_AUDIO_CONTENT_PLAYING, Constants.EXTRA_AUDIO_CONTENT_PLAYING,
this@AudioContentPlayService.isPlaying this@AudioContentPlayService.isPlaying
@ -377,7 +367,6 @@ class AudioContentPlayService :
mediaPlayer.setOnCompletionListener(this) mediaPlayer.setOnCompletionListener(this)
mediaPlayer.setAudioAttributes( mediaPlayer.setAudioAttributes(
AudioAttributes.Builder() AudioAttributes.Builder()
.setUsage(AudioAttributes.USAGE_MEDIA)
.setContentType(AudioAttributes.CONTENT_TYPE_MUSIC) .setContentType(AudioAttributes.CONTENT_TYPE_MUSIC)
.build() .build()
) )
@ -389,7 +378,6 @@ class AudioContentPlayService :
sendBroadcast( sendBroadcast(
Intent(Constants.ACTION_AUDIO_CONTENT_RECEIVER) Intent(Constants.ACTION_AUDIO_CONTENT_RECEIVER)
.apply { .apply {
setPackage(packageName)
putExtra( putExtra(
Constants.EXTRA_AUDIO_CONTENT_NEXT_ACTION, Constants.EXTRA_AUDIO_CONTENT_NEXT_ACTION,
MusicAction.PLAY MusicAction.PLAY
@ -415,7 +403,6 @@ class AudioContentPlayService :
sendBroadcast( sendBroadcast(
Intent(Constants.ACTION_MAIN_AUDIO_CONTENT_RECEIVER) Intent(Constants.ACTION_MAIN_AUDIO_CONTENT_RECEIVER)
.apply { .apply {
setPackage(packageName)
putExtra( putExtra(
Constants.EXTRA_AUDIO_CONTENT_PLAYING, Constants.EXTRA_AUDIO_CONTENT_PLAYING,
false false
@ -481,7 +468,7 @@ class AudioContentPlayService :
override fun onResourceReady(resource: Bitmap, transition: Transition<in Bitmap>?) { override fun onResourceReady(resource: Bitmap, transition: Transition<in Bitmap>?) {
val notificationBuilder = NotificationCompat val notificationBuilder = NotificationCompat
.Builder(this@AudioContentPlayService, channelId) .Builder(this@AudioContentPlayService, channelId)
.setSmallIcon(R.mipmap.ic_launcher) .setSmallIcon(R.drawable.ic_notification)
.setLargeIcon(resource) .setLargeIcon(resource)
.setContentTitle(title ?: "오디오 콘텐츠") .setContentTitle(title ?: "오디오 콘텐츠")
.setContentText(nickname ?: "") .setContentText(nickname ?: "")
@ -511,16 +498,7 @@ class AudioContentPlayService :
.setShowActionsInCompactView(0, 1) .setShowActionsInCompactView(0, 1)
) )
val notification = notificationBuilder.build() startForeground(1, notificationBuilder.build())
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
startForeground(
1,
notification,
FOREGROUND_SERVICE_TYPE_MEDIA_PLAYBACK
)
} else {
startForeground(1, notification)
}
} }
override fun onLoadCleared(placeholder: Drawable?) { override fun onLoadCleared(placeholder: Drawable?) {

View File

@ -1,12 +1,9 @@
package kr.co.vividnext.sodalive.audio_content package kr.co.vividnext.sodalive.audio_content
import kr.co.vividnext.sodalive.audio_content.category.CategoryApi
import kr.co.vividnext.sodalive.audio_content.detail.PutAudioContentLikeRequest import kr.co.vividnext.sodalive.audio_content.detail.PutAudioContentLikeRequest
import kr.co.vividnext.sodalive.audio_content.donation.AudioContentDonationRequest import kr.co.vividnext.sodalive.audio_content.donation.AudioContentDonationRequest
import kr.co.vividnext.sodalive.audio_content.order.OrderRequest import kr.co.vividnext.sodalive.audio_content.order.OrderRequest
import kr.co.vividnext.sodalive.audio_content.order.OrderType import kr.co.vividnext.sodalive.audio_content.order.OrderType
import kr.co.vividnext.sodalive.common.SharedPreferenceManager
import kr.co.vividnext.sodalive.settings.ContentType
import kr.co.vividnext.sodalive.user.CreatorFollowRequestRequest import kr.co.vividnext.sodalive.user.CreatorFollowRequestRequest
import kr.co.vividnext.sodalive.user.UserApi import kr.co.vividnext.sodalive.user.UserApi
import okhttp3.MultipartBody import okhttp3.MultipartBody
@ -15,8 +12,7 @@ import java.util.TimeZone
class AudioContentRepository( class AudioContentRepository(
private val api: AudioContentApi, private val api: AudioContentApi,
private val userApi: UserApi, private val userApi: UserApi
private val categoryApi: CategoryApi
) { ) {
fun getAudioContentListByCurationId( fun getAudioContentListByCurationId(
curationId: Long, curationId: Long,
@ -26,8 +22,6 @@ class AudioContentRepository(
token: String token: String
) = api.getAudioContentListByCurationId( ) = api.getAudioContentListByCurationId(
id = curationId, id = curationId,
isAdultContentVisible = SharedPreferenceManager.isAdultContentVisible,
contentType = ContentType.values()[SharedPreferenceManager.contentPreference],
page = page - 1, page = page - 1,
size = size, size = size,
sort = sort, sort = sort,
@ -36,14 +30,12 @@ class AudioContentRepository(
fun getAudioContentList( fun getAudioContentList(
id: Long, id: Long,
categoryId: Long,
page: Int, page: Int,
size: Int, size: Int,
sort: AudioContentViewModel.Sort, sort: AudioContentViewModel.Sort,
token: String token: String
) = api.getAudioContentList( ) = api.getAudioContentList(
id = id, id = id,
categoryId = categoryId,
page = page - 1, page = page - 1,
size = size, size = size,
sort = sort, sort = sort,
@ -137,10 +129,10 @@ class AudioContentRepository(
token: String token: String
) = api.likeContent(request, authHeader = token) ) = api.likeContent(request, authHeader = token)
fun getMain(token: String) = api.getMain(authHeader = token)
fun getNewContentOfTheme(theme: String, token: String) = api.getNewContentOfTheme( fun getNewContentOfTheme(theme: String, token: String) = api.getNewContentOfTheme(
theme = theme, theme = theme,
isAdultContentVisible = SharedPreferenceManager.isAdultContentVisible,
contentType = ContentType.values()[SharedPreferenceManager.contentPreference],
authHeader = token authHeader = token
) )
@ -151,8 +143,6 @@ class AudioContentRepository(
token: String token: String
) = api.getNewContentAllOfTheme( ) = api.getNewContentAllOfTheme(
theme = theme, theme = theme,
isAdultContentVisible = SharedPreferenceManager.isAdultContentVisible,
contentType = ContentType.values()[SharedPreferenceManager.contentPreference],
page = page - 1, page = page - 1,
size = size, size = size,
authHeader = token authHeader = token
@ -187,49 +177,4 @@ class AudioContentRepository(
sortType = sortType, sortType = sortType,
authHeader = token authHeader = token
) )
fun getCurationList(page: Int, size: Int, token: String) = api.getCurationList(
isAdultContentVisible = SharedPreferenceManager.isAdultContentVisible,
contentType = ContentType.values()[SharedPreferenceManager.contentPreference],
page = page - 1,
size = size,
authHeader = token
)
fun getNewContentUploadCreatorList(
token: String
) = api.getNewContentUploadCreatorList(authHeader = token)
fun getMainBannerList(token: String) = api.getMainBannerList(authHeader = token)
fun getMainOrderList(token: String) = api.getMainOrderList(authHeader = token)
fun pinContent(
audioContentId: Long,
token: String
) = api.pinContent(audioContentId, authHeader = token)
fun unpinContent(
audioContentId: Long,
token: String
) = api.unpinContent(audioContentId, authHeader = token)
fun getCategoryList(
creatorId: Long,
token: String
) = categoryApi.getCategoryList(creatorId, authHeader = token)
fun getAudioContentByTheme(
themeId: Long,
page: Int,
size: Int,
sort: AudioContentViewModel.Sort = AudioContentViewModel.Sort.NEWEST,
token: String
) = api.getAudioContentByTheme(
id = themeId,
isAdultContentVisible = SharedPreferenceManager.isAdultContentVisible,
contentType = ContentType.values()[SharedPreferenceManager.contentPreference],
page = page - 1,
size = size,
sort = sort,
authHeader = token
)
} }

View File

@ -6,7 +6,6 @@ import com.google.gson.annotations.SerializedName
import com.orhanobut.logger.Logger import com.orhanobut.logger.Logger
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
import io.reactivex.rxjava3.schedulers.Schedulers import io.reactivex.rxjava3.schedulers.Schedulers
import kr.co.vividnext.sodalive.audio_content.category.GetCategoryListResponse
import kr.co.vividnext.sodalive.base.BaseViewModel import kr.co.vividnext.sodalive.base.BaseViewModel
import kr.co.vividnext.sodalive.common.SharedPreferenceManager import kr.co.vividnext.sodalive.common.SharedPreferenceManager
import kr.co.vividnext.sodalive.explorer.profile.GetAudioContentListResponse import kr.co.vividnext.sodalive.explorer.profile.GetAudioContentListResponse
@ -25,10 +24,6 @@ class AudioContentViewModel(private val repository: AudioContentRepository) : Ba
val audioContentListLiveData: LiveData<GetAudioContentListResponse> val audioContentListLiveData: LiveData<GetAudioContentListResponse>
get() = _audioContentListLiveData get() = _audioContentListLiveData
private var _categoryListLiveData = MutableLiveData<List<GetCategoryListResponse>>()
val categoryListLiveData: LiveData<List<GetCategoryListResponse>>
get() = _categoryListLiveData
private val _sort = MutableLiveData(Sort.NEWEST) private val _sort = MutableLiveData(Sort.NEWEST)
val sort: LiveData<Sort> val sort: LiveData<Sort>
get() = _sort get() = _sort
@ -44,10 +39,9 @@ class AudioContentViewModel(private val repository: AudioContentRepository) : Ba
PRICE_LOW PRICE_LOW
} }
var isLast = false private var isLast = false
var page = 1 var page = 1
private val size = 10 private val size = 10
private var selectedCategoryId = 0L
fun getAudioContentList(userId: Long, onFailure: (() -> Unit)? = null) { fun getAudioContentList(userId: Long, onFailure: (() -> Unit)? = null) {
if (!_isLoading.value!! && !isLast) { if (!_isLoading.value!! && !isLast) {
@ -55,7 +49,6 @@ class AudioContentViewModel(private val repository: AudioContentRepository) : Ba
compositeDisposable.add( compositeDisposable.add(
repository.getAudioContentList( repository.getAudioContentList(
id = userId, id = userId,
categoryId = selectedCategoryId,
page = page, page = page,
size = size, size = size,
token = "Bearer ${SharedPreferenceManager.token}", token = "Bearer ${SharedPreferenceManager.token}",
@ -66,12 +59,12 @@ class AudioContentViewModel(private val repository: AudioContentRepository) : Ba
.subscribe( .subscribe(
{ {
if (it.success && it.data != null) { if (it.success && it.data != null) {
if (it.data.items.isEmpty()) { if (it.data.items.isNotEmpty()) {
isLast = true
}
page += 1 page += 1
_audioContentListLiveData.postValue(it.data!!) _audioContentListLiveData.postValue(it.data!!)
} else {
isLast = true
}
} else { } else {
if (it.message != null) { if (it.message != null) {
_toastLiveData.postValue(it.message) _toastLiveData.postValue(it.message)
@ -106,44 +99,4 @@ class AudioContentViewModel(private val repository: AudioContentRepository) : Ba
isLast = false isLast = false
_sort.postValue(sort) _sort.postValue(sort)
} }
fun selectCategory(categoryId: Long, userId: Long) {
isLast = false
page = 1
selectedCategoryId = categoryId
getAudioContentList(userId = userId)
}
fun getCategoryList(userId: Long) {
compositeDisposable.add(
repository.getCategoryList(
creatorId = userId,
token = "Bearer ${SharedPreferenceManager.token}",
)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
{
if (it.success && it.data != null) {
_categoryListLiveData.value = it.data!!
} else {
if (it.message != null) {
_toastLiveData.postValue(it.message)
} else {
_toastLiveData.postValue(
"알 수 없는 오류가 발생했습니다. 다시 시도해 주세요."
)
}
}
_isLoading.value = false
},
{
_isLoading.value = false
it.message?.let { message -> Logger.e(message) }
_toastLiveData.postValue("알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.")
}
)
)
}
} }

View File

@ -1,6 +1,5 @@
package kr.co.vividnext.sodalive.audio_content package kr.co.vividnext.sodalive.audio_content
import androidx.annotation.Keep
import io.objectbox.annotation.Entity import io.objectbox.annotation.Entity
import io.objectbox.annotation.Id import io.objectbox.annotation.Id
import java.text.SimpleDateFormat import java.text.SimpleDateFormat
@ -8,7 +7,6 @@ import java.util.Date
import java.util.Locale import java.util.Locale
@Entity @Entity
@Keep
data class PlaybackTracking( data class PlaybackTracking(
@Id @Id
var id: Long = 0, var id: Long = 0,

View File

@ -9,10 +9,9 @@ import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import kr.co.vividnext.sodalive.audio_content.detail.AudioContentDetailActivity import kr.co.vividnext.sodalive.audio_content.detail.AudioContentDetailActivity
import kr.co.vividnext.sodalive.audio_content.main.new_content.AudioContentMainNewContentThemeAdapter import kr.co.vividnext.sodalive.audio_content.main.AudioContentMainNewContentThemeAdapter
import kr.co.vividnext.sodalive.base.BaseActivity import kr.co.vividnext.sodalive.base.BaseActivity
import kr.co.vividnext.sodalive.common.Constants import kr.co.vividnext.sodalive.common.Constants
import kr.co.vividnext.sodalive.common.GridSpacingItemDecoration
import kr.co.vividnext.sodalive.common.LoadingDialog import kr.co.vividnext.sodalive.common.LoadingDialog
import kr.co.vividnext.sodalive.databinding.ActivityAudioContentNewAllBinding import kr.co.vividnext.sodalive.databinding.ActivityAudioContentNewAllBinding
import kr.co.vividnext.sodalive.explorer.profile.UserProfileActivity import kr.co.vividnext.sodalive.explorer.profile.UserProfileActivity
@ -90,10 +89,8 @@ class AudioContentNewAllActivity : BaseActivity<ActivityAudioContentNewAllBindin
} }
private fun setupNewContent() { private fun setupNewContent() {
val spanCount = 3
val spacing = 40
newContentAdapter = AudioContentNewAllAdapter( newContentAdapter = AudioContentNewAllAdapter(
itemWidth = (screenWidth - (spacing * (spanCount + 1))) / 3, itemWidth = (screenWidth - 40f.dpToPx().toInt()) / 2,
onClickItem = { onClickItem = {
startActivity( startActivity(
Intent(this, AudioContentDetailActivity::class.java).apply { Intent(this, AudioContentDetailActivity::class.java).apply {
@ -110,8 +107,44 @@ class AudioContentNewAllActivity : BaseActivity<ActivityAudioContentNewAllBindin
} }
) )
binding.rvContent.layoutManager = GridLayoutManager(this, spanCount) binding.rvContent.layoutManager = GridLayoutManager(this, 2)
binding.rvContent.addItemDecoration(GridSpacingItemDecoration(spanCount, spacing, true))
binding.rvContent.addItemDecoration(object : RecyclerView.ItemDecoration() {
override fun getItemOffsets(
outRect: Rect,
view: View,
parent: RecyclerView,
state: RecyclerView.State
) {
super.getItemOffsets(outRect, view, parent, state)
val position = parent.getChildAdapterPosition(view)
if (position % 2 == 0) {
outRect.left = 13.3f.dpToPx().toInt()
outRect.right = 6.7f.dpToPx().toInt()
} else {
outRect.left = 6.7f.dpToPx().toInt()
outRect.right = 13.3f.dpToPx().toInt()
}
when (position) {
0, 1 -> {
outRect.top = 13.3f.dpToPx().toInt()
outRect.bottom = 6.7f.dpToPx().toInt()
}
newContentAdapter.itemCount - 1, newContentAdapter.itemCount - 2 -> {
outRect.top = 6.7f.dpToPx().toInt()
outRect.bottom = 13.3f.dpToPx().toInt()
}
else -> {
outRect.top = 6.7f.dpToPx().toInt()
outRect.bottom = 6.7f.dpToPx().toInt()
}
}
}
})
binding.rvContent.addOnScrollListener(object : RecyclerView.OnScrollListener() { binding.rvContent.addOnScrollListener(object : RecyclerView.OnScrollListener() {
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) { override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {

View File

@ -1,27 +1,17 @@
package kr.co.vividnext.sodalive.audio_content.all package kr.co.vividnext.sodalive.audio_content.all
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.content.Context
import android.graphics.Bitmap
import android.graphics.drawable.Drawable
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import androidx.constraintlayout.widget.ConstraintLayout import androidx.constraintlayout.widget.ConstraintLayout
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import coil.load import coil.load
import coil.transform.CircleCropTransformation import coil.transform.CircleCropTransformation
import com.bumptech.glide.Glide import coil.transform.RoundedCornersTransformation
import com.bumptech.glide.load.resource.bitmap.CenterCrop
import com.bumptech.glide.load.resource.bitmap.RoundedCorners
import com.bumptech.glide.request.RequestOptions
import com.bumptech.glide.request.target.CustomTarget
import com.bumptech.glide.request.transition.Transition
import kr.co.vividnext.sodalive.R import kr.co.vividnext.sodalive.R
import kr.co.vividnext.sodalive.audio_content.main.GetAudioContentMainItem import kr.co.vividnext.sodalive.audio_content.main.GetAudioContentMainItem
import kr.co.vividnext.sodalive.databinding.ItemAudioContentNewAllBinding import kr.co.vividnext.sodalive.databinding.ItemAudioContentNewAllBinding
import kr.co.vividnext.sodalive.extensions.dpToPx import kr.co.vividnext.sodalive.extensions.dpToPx
import kr.co.vividnext.sodalive.extensions.moneyFormat
class AudioContentNewAllAdapter( class AudioContentNewAllAdapter(
private val itemWidth: Int, private val itemWidth: Int,
@ -30,27 +20,23 @@ class AudioContentNewAllAdapter(
) : RecyclerView.Adapter<AudioContentNewAllAdapter.ViewHolder>() { ) : RecyclerView.Adapter<AudioContentNewAllAdapter.ViewHolder>() {
inner class ViewHolder( inner class ViewHolder(
private val context: Context,
private val binding: ItemAudioContentNewAllBinding, private val binding: ItemAudioContentNewAllBinding,
private val onClickItem: (Long) -> Unit, private val onClickItem: (Long) -> Unit,
private val onClickCreator: (Long) -> Unit private val onClickCreator: (Long) -> Unit
) : RecyclerView.ViewHolder(binding.root) { ) : RecyclerView.ViewHolder(binding.root) {
fun bind(item: GetAudioContentMainItem) { fun bind(item: GetAudioContentMainItem) {
Glide binding.ivAudioContentCoverImage.load(item.coverImageUrl) {
.with(context) crossfade(true)
.load(item.coverImageUrl) placeholder(R.drawable.ic_place_holder)
.apply( transformations(RoundedCornersTransformation(2.7f.dpToPx()))
RequestOptions().transform(
CenterCrop(), val layoutParams = binding.ivAudioContentCoverImage
RoundedCorners(8) .layoutParams as ConstraintLayout.LayoutParams
)
)
.into(binding.ivAudioContentCoverImage)
val layoutParams = binding.ivAudioContentCoverImage.layoutParams as ConstraintLayout.LayoutParams
layoutParams.width = itemWidth layoutParams.width = itemWidth
layoutParams.height = itemWidth layoutParams.height = itemWidth
binding.ivAudioContentCoverImage.layoutParams = layoutParams binding.ivAudioContentCoverImage.layoutParams = layoutParams
}
binding.ivAudioContentCreator.load(item.creatorProfileImageUrl) { binding.ivAudioContentCreator.load(item.creatorProfileImageUrl) {
crossfade(true) crossfade(true)
@ -61,16 +47,6 @@ class AudioContentNewAllAdapter(
binding.tvAudioContentTitle.text = item.title binding.tvAudioContentTitle.text = item.title
binding.tvAudioContentCreatorNickname.text = item.creatorNickname binding.tvAudioContentCreatorNickname.text = item.creatorNickname
if (item.price > 0) {
binding.ivCan.visibility = View.VISIBLE
binding.tvCan.text = item.price.moneyFormat()
} else {
binding.ivCan.visibility = View.GONE
binding.tvCan.text = "무료"
}
binding.tvTime.text = item.duration
binding.ivAudioContentCreator.setOnClickListener { onClickCreator(item.creatorId) } binding.ivAudioContentCreator.setOnClickListener { onClickCreator(item.creatorId) }
binding.root.setOnClickListener { onClickItem(item.contentId) } binding.root.setOnClickListener { onClickItem(item.contentId) }
} }
@ -82,7 +58,6 @@ class AudioContentNewAllAdapter(
parent: ViewGroup, parent: ViewGroup,
viewType: Int viewType: Int
) = ViewHolder( ) = ViewHolder(
parent.context,
ItemAudioContentNewAllBinding.inflate( ItemAudioContentNewAllBinding.inflate(
LayoutInflater.from(parent.context), LayoutInflater.from(parent.context),
parent, parent,

View File

@ -9,7 +9,7 @@ import android.widget.Toast
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import kr.co.vividnext.sodalive.audio_content.detail.AudioContentDetailActivity import kr.co.vividnext.sodalive.audio_content.detail.AudioContentDetailActivity
import kr.co.vividnext.sodalive.audio_content.main.new_content.AudioContentMainNewContentThemeAdapter import kr.co.vividnext.sodalive.audio_content.main.AudioContentMainNewContentThemeAdapter
import kr.co.vividnext.sodalive.base.BaseActivity import kr.co.vividnext.sodalive.base.BaseActivity
import kr.co.vividnext.sodalive.common.Constants import kr.co.vividnext.sodalive.common.Constants
import kr.co.vividnext.sodalive.common.LoadingDialog import kr.co.vividnext.sodalive.common.LoadingDialog

View File

@ -1,10 +1,8 @@
package kr.co.vividnext.sodalive.audio_content.all package kr.co.vividnext.sodalive.audio_content.all
import androidx.annotation.Keep
import com.google.gson.annotations.SerializedName import com.google.gson.annotations.SerializedName
import kr.co.vividnext.sodalive.audio_content.main.GetAudioContentMainItem import kr.co.vividnext.sodalive.audio_content.main.GetAudioContentMainItem
@Keep
data class GetNewContentAllResponse( data class GetNewContentAllResponse(
@SerializedName("totalCount") val totalCount: Int, @SerializedName("totalCount") val totalCount: Int,
@SerializedName("items") val items: List<GetAudioContentMainItem> @SerializedName("items") val items: List<GetAudioContentMainItem>

View File

@ -1,179 +0,0 @@
package kr.co.vividnext.sodalive.audio_content.all.by_theme
import android.content.Intent
import android.graphics.Rect
import android.os.Bundle
import android.view.View
import android.widget.TextView
import android.widget.Toast
import androidx.core.content.ContextCompat
import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import kr.co.vividnext.sodalive.R
import kr.co.vividnext.sodalive.audio_content.AudioContentViewModel
import kr.co.vividnext.sodalive.audio_content.all.AudioContentNewAllAdapter
import kr.co.vividnext.sodalive.audio_content.detail.AudioContentDetailActivity
import kr.co.vividnext.sodalive.base.BaseActivity
import kr.co.vividnext.sodalive.common.Constants
import kr.co.vividnext.sodalive.common.GridSpacingItemDecoration
import kr.co.vividnext.sodalive.common.LoadingDialog
import kr.co.vividnext.sodalive.databinding.ActivityAudioContentAllByThemeBinding
import kr.co.vividnext.sodalive.explorer.profile.UserProfileActivity
import kr.co.vividnext.sodalive.extensions.dpToPx
import org.koin.android.ext.android.inject
class AudioContentAllByThemeActivity : BaseActivity<ActivityAudioContentAllByThemeBinding>(
ActivityAudioContentAllByThemeBinding::inflate
) {
private val viewModel: AudioContentAllByThemeViewModel by inject()
private lateinit var loadingDialog: LoadingDialog
private lateinit var adapter: AudioContentNewAllAdapter
private var themeId: Long = 0
override fun onCreate(savedInstanceState: Bundle?) {
themeId = intent.getLongExtra(Constants.EXTRA_THEME_ID, 0)
if (themeId <= 0) {
Toast.makeText(
applicationContext,
"잘못된 요청입니다.\n다시 시도해 주세요.",
Toast.LENGTH_LONG
).show()
finish()
}
super.onCreate(savedInstanceState)
bindData()
viewModel.getContentList(themeId = themeId)
}
override fun setupView() {
loadingDialog = LoadingDialog(this, layoutInflater)
binding.toolbar.tvBack.setOnClickListener { finish() }
val spanCount = 3
val spacing = 40
adapter = AudioContentNewAllAdapter(
itemWidth = (screenWidth - (spacing * (spanCount + 1))) / 3,
onClickItem = {
startActivity(
Intent(this, AudioContentDetailActivity::class.java).apply {
putExtra(Constants.EXTRA_AUDIO_CONTENT_ID, it)
}
)
},
onClickCreator = {
startActivity(
Intent(this, UserProfileActivity::class.java).apply {
putExtra(Constants.EXTRA_USER_ID, it)
}
)
}
)
binding.rvContentAll.layoutManager = GridLayoutManager(this, spanCount)
binding.rvContentAll.addItemDecoration(GridSpacingItemDecoration(spanCount, spacing, true))
binding.rvContentAll.addOnScrollListener(object : RecyclerView.OnScrollListener() {
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
super.onScrolled(recyclerView, dx, dy)
val lastVisibleItemPosition = (recyclerView.layoutManager as LinearLayoutManager?)!!
.findLastCompletelyVisibleItemPosition()
val itemTotalCount = recyclerView.adapter!!.itemCount - 1
// 스크롤이 끝에 도달했는지 확인
if (!recyclerView.canScrollVertically(1) &&
lastVisibleItemPosition == itemTotalCount
) {
viewModel.getContentList(themeId = themeId)
}
}
})
binding.rvContentAll.adapter = adapter
binding.tvSortNewest.setOnClickListener {
viewModel.changeSort(AudioContentViewModel.Sort.NEWEST)
}
binding.tvSortPriceLow.setOnClickListener {
viewModel.changeSort(AudioContentViewModel.Sort.PRICE_LOW)
}
binding.tvSortPriceHigh.setOnClickListener {
viewModel.changeSort(AudioContentViewModel.Sort.PRICE_HIGH)
}
}
private fun bindData() {
viewModel.toastLiveData.observe(this) {
it?.let { Toast.makeText(applicationContext, it, Toast.LENGTH_LONG).show() }
}
viewModel.isLoading.observe(this) {
if (it) {
loadingDialog.show(screenWidth, "")
} else {
loadingDialog.dismiss()
}
}
viewModel.contentListLiveData.observe(this) {
if (viewModel.page - 1 == 1) {
adapter.clear()
binding.rvContentAll.scrollToPosition(0)
}
binding.tvTotalCount.text = "${it.totalCount}"
binding.toolbar.tvBack.text = it.theme
adapter.addItems(it.items)
}
viewModel.sort.observe(this) {
deselectSort()
selectSort(
when (it) {
AudioContentViewModel.Sort.PRICE_HIGH -> {
binding.tvSortPriceHigh
}
AudioContentViewModel.Sort.PRICE_LOW -> {
binding.tvSortPriceLow
}
else -> {
binding.tvSortNewest
}
}
)
viewModel.getContentList(themeId = themeId)
}
}
private fun deselectSort() {
val color = ContextCompat.getColor(
applicationContext,
R.color.color_88e2e2e2
)
binding.tvSortNewest.setTextColor(color)
binding.tvSortPriceLow.setTextColor(color)
binding.tvSortPriceHigh.setTextColor(color)
}
private fun selectSort(view: TextView) {
view.setTextColor(
ContextCompat.getColor(
applicationContext,
R.color.color_e2e2e2
)
)
}
}

View File

@ -1,86 +0,0 @@
package kr.co.vividnext.sodalive.audio_content.all.by_theme
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import com.orhanobut.logger.Logger
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
import io.reactivex.rxjava3.schedulers.Schedulers
import kr.co.vividnext.sodalive.audio_content.AudioContentRepository
import kr.co.vividnext.sodalive.audio_content.AudioContentViewModel
import kr.co.vividnext.sodalive.base.BaseViewModel
import kr.co.vividnext.sodalive.common.SharedPreferenceManager
class AudioContentAllByThemeViewModel(
private val repository: AudioContentRepository
) : BaseViewModel() {
private val _toastLiveData = MutableLiveData<String?>()
val toastLiveData: LiveData<String?>
get() = _toastLiveData
private var _isLoading = MutableLiveData(false)
val isLoading: LiveData<Boolean>
get() = _isLoading
private var _contentListLiveData = MutableLiveData<GetContentByThemeResponse>()
val contentListLiveData: LiveData<GetContentByThemeResponse>
get() = _contentListLiveData
private val _sort = MutableLiveData(AudioContentViewModel.Sort.NEWEST)
val sort: LiveData<AudioContentViewModel.Sort>
get() = _sort
private var isLast = false
var page = 1
private val size = 15
fun getContentList(themeId: Long) {
if (!_isLoading.value!! && !isLast) {
_isLoading.value = true
compositeDisposable.add(
repository.getAudioContentByTheme(
themeId = themeId,
page = page,
size = size,
sort = _sort.value!!,
token = "Bearer ${SharedPreferenceManager.token}"
)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
{
if (it.success && it.data != null) {
if (it.data.items.isNotEmpty()) {
page += 1
_contentListLiveData.postValue(it.data!!)
} else {
isLast = true
}
} else {
if (it.message != null) {
_toastLiveData.postValue(it.message)
} else {
_toastLiveData.postValue(
"알 수 없는 오류가 발생했습니다. 다시 시도해 주세요."
)
}
}
_isLoading.value = false
},
{
_isLoading.value = false
it.message?.let { message -> Logger.e(message) }
_toastLiveData.postValue("알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.")
}
)
)
}
}
fun changeSort(sort: AudioContentViewModel.Sort) {
page = 1
isLast = false
_sort.postValue(sort)
}
}

View File

@ -1,12 +0,0 @@
package kr.co.vividnext.sodalive.audio_content.all.by_theme
import androidx.annotation.Keep
import com.google.gson.annotations.SerializedName
import kr.co.vividnext.sodalive.audio_content.main.GetAudioContentMainItem
@Keep
data class GetContentByThemeResponse(
@SerializedName("theme") val theme: String,
@SerializedName("totalCount") val totalCount: Int,
@SerializedName("items") val items: List<GetAudioContentMainItem>
)

View File

@ -1,79 +0,0 @@
package kr.co.vividnext.sodalive.audio_content.category
import android.annotation.SuppressLint
import android.content.Context
import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.core.content.ContextCompat
import androidx.recyclerview.widget.RecyclerView
import kr.co.vividnext.sodalive.R
import kr.co.vividnext.sodalive.databinding.ItemContentCategoryBinding
class AudioContentCategoryAdapter(
private val onClick: (Long) -> Unit
) : RecyclerView.Adapter<AudioContentCategoryAdapter.ViewHolder>() {
private val items = mutableListOf<GetCategoryListResponse>()
private var selectedCategory = ""
inner class ViewHolder(
private val context: Context,
private val binding: ItemContentCategoryBinding
) : RecyclerView.ViewHolder(binding.root) {
@SuppressLint("NotifyDataSetChanged")
fun bind(item: GetCategoryListResponse) {
if (
item.category == selectedCategory ||
(selectedCategory == "" && item.category == "전체")
) {
binding.tvCategory.setBackgroundResource(
R.drawable.bg_round_corner_16_7_transparent_3bb9f1
)
binding.tvCategory.setTextColor(
ContextCompat.getColor(context, R.color.color_3bb9f1)
)
} else {
binding.tvCategory.setBackgroundResource(
R.drawable.bg_round_corner_16_7_transparent_777777
)
binding.tvCategory.setTextColor(
ContextCompat.getColor(context, R.color.color_777777)
)
}
binding.tvCategory.text = item.category
binding.root.setOnClickListener {
onClick(item.categoryId)
selectedCategory = item.category
notifyDataSetChanged()
}
}
}
@SuppressLint("NotifyDataSetChanged")
fun addItems(items: List<GetCategoryListResponse>) {
this.selectedCategory = ""
this.items.clear()
this.items.addAll(items)
notifyDataSetChanged()
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = ViewHolder(
parent.context,
ItemContentCategoryBinding.inflate(
LayoutInflater.from(parent.context),
parent,
false
)
)
override fun getItemCount() = items.size
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
holder.bind(items[position])
}
fun clear() {
this.items.clear()
}
}

View File

@ -1,15 +0,0 @@
package kr.co.vividnext.sodalive.audio_content.category
import io.reactivex.rxjava3.core.Single
import kr.co.vividnext.sodalive.common.ApiResponse
import retrofit2.http.GET
import retrofit2.http.Header
import retrofit2.http.Query
interface CategoryApi {
@GET("/category")
fun getCategoryList(
@Query("creatorId") creatorId: Long,
@Header("Authorization") authHeader: String
): Single<ApiResponse<List<GetCategoryListResponse>>>
}

View File

@ -1,10 +0,0 @@
package kr.co.vividnext.sodalive.audio_content.category
import androidx.annotation.Keep
import com.google.gson.annotations.SerializedName
@Keep
data class GetCategoryListResponse(
@SerializedName("categoryId") val categoryId: Long,
@SerializedName("category") val category: String
)

View File

@ -9,7 +9,6 @@ import androidx.appcompat.widget.PopupMenu
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import coil.load import coil.load
import coil.transform.CircleCropTransformation import coil.transform.CircleCropTransformation
import com.orhanobut.logger.Logger
import kr.co.vividnext.sodalive.R import kr.co.vividnext.sodalive.R
import kr.co.vividnext.sodalive.common.SharedPreferenceManager import kr.co.vividnext.sodalive.common.SharedPreferenceManager
import kr.co.vividnext.sodalive.databinding.ItemAudioContentCommentBinding import kr.co.vividnext.sodalive.databinding.ItemAudioContentCommentBinding
@ -20,8 +19,7 @@ class AudioContentCommentAdapter(
private val creatorId: Long, private val creatorId: Long,
private val modifyComment: (Long, String) -> Unit, private val modifyComment: (Long, String) -> Unit,
private val onClickDelete: (Long) -> Unit, private val onClickDelete: (Long) -> Unit,
private val onItemClick: (GetAudioContentCommentListItem) -> Unit, private val onItemClick: (GetAudioContentCommentListItem) -> Unit
private val onClickProfile: (Long) -> Unit
) : RecyclerView.Adapter<AudioContentCommentAdapter.ViewHolder>() { ) : RecyclerView.Adapter<AudioContentCommentAdapter.ViewHolder>() {
var items = mutableSetOf<GetAudioContentCommentListItem>() var items = mutableSetOf<GetAudioContentCommentListItem>()
@ -32,24 +30,12 @@ class AudioContentCommentAdapter(
) : RecyclerView.ViewHolder(binding.root) { ) : RecyclerView.ViewHolder(binding.root) {
fun bind(item: GetAudioContentCommentListItem) { fun bind(item: GetAudioContentCommentListItem) {
binding.tvSecret.visibility = if (item.isSecret) {
View.VISIBLE
} else {
View.GONE
}
binding.ivCommentProfile.load(item.profileUrl) { binding.ivCommentProfile.load(item.profileUrl) {
crossfade(true) crossfade(true)
placeholder(R.drawable.bg_placeholder) placeholder(R.drawable.bg_placeholder)
transformations(CircleCropTransformation()) transformations(CircleCropTransformation())
} }
binding.ivCommentProfile.setOnClickListener {
if (SharedPreferenceManager.userId != item.writerId) {
onClickProfile(item.writerId)
}
}
val tvCommentLayoutParams = binding.tvComment.layoutParams as LinearLayout.LayoutParams val tvCommentLayoutParams = binding.tvComment.layoutParams as LinearLayout.LayoutParams
val can = item.donationCan val can = item.donationCan
if (can > 0) { if (can > 0) {
@ -58,27 +44,27 @@ class AudioContentCommentAdapter(
binding.tvDonationCan.text = can.moneyFormat() binding.tvDonationCan.text = can.moneyFormat()
binding.llDonationCan.setBackgroundResource( binding.llDonationCan.setBackgroundResource(
when { when {
can >= 10000 -> { can >= 100000 -> {
R.drawable.bg_round_corner_10_7_973a3a R.drawable.bg_round_corner_10_7_973a3a
} }
can >= 5000 -> { can >= 50000 -> {
R.drawable.bg_round_corner_10_7_d85e37 R.drawable.bg_round_corner_10_7_d85e37
} }
can >= 1000 -> { can >= 10000 -> {
R.drawable.bg_round_corner_10_7_d38c38 R.drawable.bg_round_corner_10_7_d38c38
} }
can >= 500 -> { can >= 5000 -> {
R.drawable.bg_round_corner_10_7_59548f R.drawable.bg_round_corner_10_7_59548f
} }
can >= 100 -> { can >= 1000 -> {
R.drawable.bg_round_corner_10_7_4d6aa4 R.drawable.bg_round_corner_10_7_4d6aa4
} }
can >= 50 -> { can >= 500 -> {
R.drawable.bg_round_corner_10_7_2d7390 R.drawable.bg_round_corner_10_7_2d7390
} }

View File

@ -14,8 +14,7 @@ import kr.co.vividnext.sodalive.databinding.DialogAudioContentCommentBinding
class AudioContentCommentFragment( class AudioContentCommentFragment(
private val creatorId: Long, private val creatorId: Long,
private val audioContentId: Long, private val audioContentId: Long
private val isShowSecret: Boolean
) : BottomSheetDialogFragment() { ) : BottomSheetDialogFragment() {
private lateinit var binding: DialogAudioContentCommentBinding private lateinit var binding: DialogAudioContentCommentBinding
@ -51,8 +50,7 @@ class AudioContentCommentFragment(
val commentListFragmentTag = "COMMENT_LIST_FRAGMENT" val commentListFragmentTag = "COMMENT_LIST_FRAGMENT"
val commentListFragment = AudioContentCommentListFragment.newInstance( val commentListFragment = AudioContentCommentListFragment.newInstance(
creatorId = creatorId, creatorId = creatorId,
audioContentId = audioContentId, audioContentId = audioContentId
isShowSecret = isShowSecret
) )
val fragmentTransaction = childFragmentManager.beginTransaction() val fragmentTransaction = childFragmentManager.beginTransaction()
fragmentTransaction.add(R.id.fl_container, commentListFragment, commentListFragmentTag) fragmentTransaction.add(R.id.fl_container, commentListFragment, commentListFragmentTag)

View File

@ -20,7 +20,6 @@ import kr.co.vividnext.sodalive.common.Constants
import kr.co.vividnext.sodalive.common.LoadingDialog import kr.co.vividnext.sodalive.common.LoadingDialog
import kr.co.vividnext.sodalive.common.SharedPreferenceManager import kr.co.vividnext.sodalive.common.SharedPreferenceManager
import kr.co.vividnext.sodalive.databinding.FragmentAudioContentCommentListBinding import kr.co.vividnext.sodalive.databinding.FragmentAudioContentCommentListBinding
import kr.co.vividnext.sodalive.dialog.MemberProfileDialog
import kr.co.vividnext.sodalive.extensions.dpToPx import kr.co.vividnext.sodalive.extensions.dpToPx
import org.koin.android.ext.android.inject import org.koin.android.ext.android.inject
@ -36,7 +35,6 @@ class AudioContentCommentListFragment : BaseFragment<FragmentAudioContentComment
private var creatorId: Long = 0 private var creatorId: Long = 0
private var audioContentId: Long = 0 private var audioContentId: Long = 0
private var isShowSecret: Boolean = false
override fun onCreateView( override fun onCreateView(
inflater: LayoutInflater, inflater: LayoutInflater,
@ -45,7 +43,6 @@ class AudioContentCommentListFragment : BaseFragment<FragmentAudioContentComment
): View? { ): View? {
creatorId = arguments?.getLong(Constants.EXTRA_AUDIO_CONTENT_CREATOR_ID) ?: 0 creatorId = arguments?.getLong(Constants.EXTRA_AUDIO_CONTENT_CREATOR_ID) ?: 0
audioContentId = arguments?.getLong(Constants.EXTRA_AUDIO_CONTENT_ID) ?: 0 audioContentId = arguments?.getLong(Constants.EXTRA_AUDIO_CONTENT_ID) ?: 0
isShowSecret = arguments?.getBoolean(Constants.EXTRA_IS_SHOW_SECRET) ?: false
return super.onCreateView(inflater, container, savedInstanceState) return super.onCreateView(inflater, container, savedInstanceState)
} }
@ -76,20 +73,8 @@ class AudioContentCommentListFragment : BaseFragment<FragmentAudioContentComment
binding.ivCommentSend.setOnClickListener { binding.ivCommentSend.setOnClickListener {
hideKeyboard() hideKeyboard()
val comment = binding.etComment.text.toString() val comment = binding.etComment.text.toString()
val isSecret = binding.tvSecret.isSelected
viewModel.registerComment(audioContentId, comment, isSecret)
binding.etComment.setText("") binding.etComment.setText("")
binding.tvSecret.isSelected = false viewModel.registerComment(audioContentId, comment)
}
if (isShowSecret) {
binding.tvSecret.visibility = View.VISIBLE
binding.tvSecret.setOnClickListener {
binding.tvSecret.isSelected = !binding.tvSecret.isSelected
}
} else {
binding.tvSecret.visibility = View.GONE
} }
adapter = AudioContentCommentAdapter( adapter = AudioContentCommentAdapter(
@ -122,14 +107,6 @@ class AudioContentCommentListFragment : BaseFragment<FragmentAudioContentComment
}, },
onItemClick = { onItemClick = {
(parentFragment as AudioContentCommentFragment).onClickComment(it) (parentFragment as AudioContentCommentFragment).onClickComment(it)
},
onClickProfile = {
MemberProfileDialog(
activity = requireActivity(),
layoutInflater = layoutInflater,
memberId = it,
screenWidth = screenWidth
).show()
} }
) )
@ -225,15 +202,10 @@ class AudioContentCommentListFragment : BaseFragment<FragmentAudioContentComment
} }
companion object { companion object {
fun newInstance( fun newInstance(creatorId: Long, audioContentId: Long): AudioContentCommentListFragment {
creatorId: Long,
audioContentId: Long,
isShowSecret: Boolean
): AudioContentCommentListFragment {
val args = Bundle() val args = Bundle()
args.putLong(Constants.EXTRA_AUDIO_CONTENT_CREATOR_ID, creatorId) args.putLong(Constants.EXTRA_AUDIO_CONTENT_CREATOR_ID, creatorId)
args.putLong(Constants.EXTRA_AUDIO_CONTENT_ID, audioContentId) args.putLong(Constants.EXTRA_AUDIO_CONTENT_ID, audioContentId)
args.putBoolean(Constants.EXTRA_IS_SHOW_SECRET, isShowSecret)
val fragment = AudioContentCommentListFragment() val fragment = AudioContentCommentListFragment()
fragment.arguments = args fragment.arguments = args

View File

@ -84,7 +84,7 @@ class AudioContentCommentListViewModel(
} }
} }
fun registerComment(contentId: Long, comment: String, isSecret: Boolean) { fun registerComment(contentId: Long, comment: String) {
if (!_isLoading.value!!) { if (!_isLoading.value!!) {
_isLoading.value = true _isLoading.value = true
} }
@ -93,7 +93,6 @@ class AudioContentCommentListViewModel(
repository.registerComment( repository.registerComment(
contentId = contentId, contentId = contentId,
comment = comment, comment = comment,
isSecret = isSecret,
token = "Bearer ${SharedPreferenceManager.token}" token = "Bearer ${SharedPreferenceManager.token}"
) )
.subscribeOn(Schedulers.io()) .subscribeOn(Schedulers.io())

View File

@ -124,27 +124,27 @@ class AudioContentCommentReplyHeaderViewHolder(
binding.tvDonationCan.text = can.moneyFormat() binding.tvDonationCan.text = can.moneyFormat()
binding.llDonationCan.setBackgroundResource( binding.llDonationCan.setBackgroundResource(
when { when {
can >= 10000 -> { can >= 100000 -> {
R.drawable.bg_round_corner_10_7_973a3a R.drawable.bg_round_corner_10_7_973a3a
} }
can >= 5000 -> { can >= 50000 -> {
R.drawable.bg_round_corner_10_7_d85e37 R.drawable.bg_round_corner_10_7_d85e37
} }
can >= 1000 -> { can >= 10000 -> {
R.drawable.bg_round_corner_10_7_d38c38 R.drawable.bg_round_corner_10_7_d38c38
} }
can >= 500 -> { can >= 5000 -> {
R.drawable.bg_round_corner_10_7_59548f R.drawable.bg_round_corner_10_7_59548f
} }
can >= 100 -> { can >= 1000 -> {
R.drawable.bg_round_corner_10_7_4d6aa4 R.drawable.bg_round_corner_10_7_4d6aa4
} }
can >= 50 -> { can >= 500 -> {
R.drawable.bg_round_corner_10_7_2d7390 R.drawable.bg_round_corner_10_7_2d7390
} }

View File

@ -8,14 +8,12 @@ class AudioContentCommentRepository(private val api: AudioContentApi) {
contentId: Long, contentId: Long,
comment: String, comment: String,
parentId: Long? = null, parentId: Long? = null,
isSecret: Boolean = false,
token: String token: String
) = api.registerComment( ) = api.registerComment(
request = RegisterAudioContentCommentRequest( request = RegisterAudioContentCommentRequest(
comment = comment, comment = comment,
contentId = contentId, contentId = contentId,
parentId = parentId, parentId = parentId
isSecret = isSecret
), ),
authHeader = token authHeader = token
) )

View File

@ -1,25 +1,21 @@
package kr.co.vividnext.sodalive.audio_content.comment package kr.co.vividnext.sodalive.audio_content.comment
import android.os.Parcelable import android.os.Parcelable
import androidx.annotation.Keep
import com.google.gson.annotations.SerializedName import com.google.gson.annotations.SerializedName
import kotlinx.parcelize.Parcelize import kotlinx.parcelize.Parcelize
@Keep
data class GetAudioContentCommentListResponse( data class GetAudioContentCommentListResponse(
@SerializedName("totalCount") val totalCount: Int, @SerializedName("totalCount") val totalCount: Int,
@SerializedName("items") val items: List<GetAudioContentCommentListItem> @SerializedName("items") val items: List<GetAudioContentCommentListItem>
) )
@Parcelize @Parcelize
@Keep
data class GetAudioContentCommentListItem( data class GetAudioContentCommentListItem(
@SerializedName("id") val id: Long, @SerializedName("id") val id: Long,
@SerializedName("writerId") val writerId: Long, @SerializedName("writerId") val writerId: Long,
@SerializedName("nickname") val nickname: String, @SerializedName("nickname") val nickname: String,
@SerializedName("profileUrl") val profileUrl: String, @SerializedName("profileUrl") val profileUrl: String,
@SerializedName("comment") val comment: String, @SerializedName("comment") val comment: String,
@SerializedName("isSecret") val isSecret: Boolean,
@SerializedName("donationCan") val donationCan: Int, @SerializedName("donationCan") val donationCan: Int,
@SerializedName("date") val date: String, @SerializedName("date") val date: String,
@SerializedName("replyCount") val replyCount: Int @SerializedName("replyCount") val replyCount: Int

View File

@ -1,9 +1,7 @@
package kr.co.vividnext.sodalive.audio_content.comment package kr.co.vividnext.sodalive.audio_content.comment
import androidx.annotation.Keep
import com.google.gson.annotations.SerializedName import com.google.gson.annotations.SerializedName
@Keep
data class ModifyCommentRequest( data class ModifyCommentRequest(
@SerializedName("commentId") val commentId: Long, @SerializedName("commentId") val commentId: Long,
@SerializedName("comment") var comment: String? = null, @SerializedName("comment") var comment: String? = null,

View File

@ -1,12 +1,9 @@
package kr.co.vividnext.sodalive.audio_content.comment package kr.co.vividnext.sodalive.audio_content.comment
import androidx.annotation.Keep
import com.google.gson.annotations.SerializedName import com.google.gson.annotations.SerializedName
@Keep
data class RegisterAudioContentCommentRequest( data class RegisterAudioContentCommentRequest(
@SerializedName("comment") val comment: String, @SerializedName("comment") val comment: String,
@SerializedName("contentId") val contentId: Long, @SerializedName("contentId") val contentId: Long,
@SerializedName("parentId") val parentId: Long?, @SerializedName("parentId") val parentId: Long?
@SerializedName("isSecret") val isSecret: Boolean
) )

View File

@ -17,7 +17,6 @@ import kr.co.vividnext.sodalive.audio_content.all.AudioContentNewAllAdapter
import kr.co.vividnext.sodalive.audio_content.detail.AudioContentDetailActivity import kr.co.vividnext.sodalive.audio_content.detail.AudioContentDetailActivity
import kr.co.vividnext.sodalive.base.BaseActivity import kr.co.vividnext.sodalive.base.BaseActivity
import kr.co.vividnext.sodalive.common.Constants import kr.co.vividnext.sodalive.common.Constants
import kr.co.vividnext.sodalive.common.GridSpacingItemDecoration
import kr.co.vividnext.sodalive.common.LoadingDialog import kr.co.vividnext.sodalive.common.LoadingDialog
import kr.co.vividnext.sodalive.databinding.ActivityAudioContentCurationBinding import kr.co.vividnext.sodalive.databinding.ActivityAudioContentCurationBinding
import kr.co.vividnext.sodalive.explorer.profile.UserProfileActivity import kr.co.vividnext.sodalive.explorer.profile.UserProfileActivity
@ -55,10 +54,8 @@ class AudioContentCurationActivity : BaseActivity<ActivityAudioContentCurationBi
binding.toolbar.tvBack.text = title binding.toolbar.tvBack.text = title
binding.toolbar.tvBack.setOnClickListener { finish() } binding.toolbar.tvBack.setOnClickListener { finish() }
val spanCount = 3
val spacing = 40
adapter = AudioContentNewAllAdapter( adapter = AudioContentNewAllAdapter(
itemWidth = (screenWidth - (spacing * (spanCount + 1))) / 3, itemWidth = (screenWidth - 40f.dpToPx().toInt()) / 2,
onClickItem = { onClickItem = {
startActivity( startActivity(
Intent(this, AudioContentDetailActivity::class.java).apply { Intent(this, AudioContentDetailActivity::class.java).apply {
@ -75,8 +72,44 @@ class AudioContentCurationActivity : BaseActivity<ActivityAudioContentCurationBi
} }
) )
binding.rvCuration.layoutManager = GridLayoutManager(this, spanCount) binding.rvCuration.layoutManager = GridLayoutManager(this, 2)
binding.rvCuration.addItemDecoration(GridSpacingItemDecoration(spanCount, spacing, true))
binding.rvCuration.addItemDecoration(object : RecyclerView.ItemDecoration() {
override fun getItemOffsets(
outRect: Rect,
view: View,
parent: RecyclerView,
state: RecyclerView.State
) {
super.getItemOffsets(outRect, view, parent, state)
val position = parent.getChildAdapterPosition(view)
if (position % 2 == 0) {
outRect.left = 13.3f.dpToPx().toInt()
outRect.right = 6.7f.dpToPx().toInt()
} else {
outRect.left = 6.7f.dpToPx().toInt()
outRect.right = 13.3f.dpToPx().toInt()
}
when (position) {
0, 1 -> {
outRect.top = 13.3f.dpToPx().toInt()
outRect.bottom = 6.7f.dpToPx().toInt()
}
adapter.itemCount - 1, adapter.itemCount - 2 -> {
outRect.top = 6.7f.dpToPx().toInt()
outRect.bottom = 13.3f.dpToPx().toInt()
}
else -> {
outRect.top = 6.7f.dpToPx().toInt()
outRect.bottom = 6.7f.dpToPx().toInt()
}
}
}
})
binding.rvCuration.addOnScrollListener(object : RecyclerView.OnScrollListener() { binding.rvCuration.addOnScrollListener(object : RecyclerView.OnScrollListener() {
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) { override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {

View File

@ -1,10 +1,8 @@
package kr.co.vividnext.sodalive.audio_content.curation package kr.co.vividnext.sodalive.audio_content.curation
import androidx.annotation.Keep
import com.google.gson.annotations.SerializedName import com.google.gson.annotations.SerializedName
import kr.co.vividnext.sodalive.audio_content.main.GetAudioContentMainItem import kr.co.vividnext.sodalive.audio_content.main.GetAudioContentMainItem
@Keep
data class GetCurationContentResponse( data class GetCurationContentResponse(
@SerializedName("totalCount") val totalCount: Int, @SerializedName("totalCount") val totalCount: Int,
@SerializedName("items") val items: List<GetAudioContentMainItem> @SerializedName("items") val items: List<GetAudioContentMainItem>

View File

@ -1,8 +1,6 @@
package kr.co.vividnext.sodalive.audio_content.detail package kr.co.vividnext.sodalive.audio_content.detail
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.app.Activity
import android.app.Service
import android.content.BroadcastReceiver import android.content.BroadcastReceiver
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
@ -10,17 +8,12 @@ import android.content.IntentFilter
import android.graphics.Rect import android.graphics.Rect
import android.os.Build import android.os.Build
import android.os.Bundle import android.os.Bundle
import android.os.Handler
import android.os.Looper
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.inputmethod.InputMethodManager
import android.widget.RelativeLayout import android.widget.RelativeLayout
import android.widget.SeekBar import android.widget.SeekBar
import android.widget.Toast import android.widget.Toast
import androidx.activity.result.ActivityResultLauncher import androidx.appcompat.widget.PopupMenu
import androidx.activity.result.contract.ActivityResultContracts
import androidx.core.content.ContextCompat
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import coil.load import coil.load
@ -36,25 +29,20 @@ import kr.co.vividnext.sodalive.audio_content.order.AudioContentOrderConfirmDial
import kr.co.vividnext.sodalive.audio_content.order.AudioContentOrderFragment import kr.co.vividnext.sodalive.audio_content.order.AudioContentOrderFragment
import kr.co.vividnext.sodalive.audio_content.order.OrderType import kr.co.vividnext.sodalive.audio_content.order.OrderType
import kr.co.vividnext.sodalive.base.BaseActivity import kr.co.vividnext.sodalive.base.BaseActivity
import kr.co.vividnext.sodalive.base.SodaDialog
import kr.co.vividnext.sodalive.common.Constants import kr.co.vividnext.sodalive.common.Constants
import kr.co.vividnext.sodalive.common.LoadingDialog import kr.co.vividnext.sodalive.common.LoadingDialog
import kr.co.vividnext.sodalive.common.SharedPreferenceManager import kr.co.vividnext.sodalive.common.SharedPreferenceManager
import kr.co.vividnext.sodalive.common.Utils import kr.co.vividnext.sodalive.common.Utils
import kr.co.vividnext.sodalive.databinding.ActivityAudioContentDetailBinding import kr.co.vividnext.sodalive.databinding.ActivityAudioContentDetailBinding
import kr.co.vividnext.sodalive.explorer.profile.CreatorFollowNotifyFragment
import kr.co.vividnext.sodalive.explorer.profile.UserProfileActivity import kr.co.vividnext.sodalive.explorer.profile.UserProfileActivity
import kr.co.vividnext.sodalive.extensions.dpToPx import kr.co.vividnext.sodalive.extensions.dpToPx
import kr.co.vividnext.sodalive.extensions.moneyFormat
import kr.co.vividnext.sodalive.live.room.donation.LiveRoomDonationDialog import kr.co.vividnext.sodalive.live.room.donation.LiveRoomDonationDialog
import kr.co.vividnext.sodalive.mypage.auth.Auth import kr.co.vividnext.sodalive.mypage.auth.Auth
import kr.co.vividnext.sodalive.mypage.auth.AuthVerifyRequest import kr.co.vividnext.sodalive.mypage.auth.AuthVerifyRequest
import kr.co.vividnext.sodalive.mypage.auth.BootpayResponse import kr.co.vividnext.sodalive.mypage.auth.BootpayResponse
import kr.co.vividnext.sodalive.mypage.can.charge.CanChargeActivity import kr.co.vividnext.sodalive.mypage.can.charge.CanChargeActivity
import kr.co.vividnext.sodalive.mypage.can.payment.CanPaymentTempActivity
import kr.co.vividnext.sodalive.report.ReportType import kr.co.vividnext.sodalive.report.ReportType
import org.koin.android.ext.android.inject import org.koin.android.ext.android.inject
import kotlin.math.ceil
class AudioContentDetailActivity : BaseActivity<ActivityAudioContentDetailBinding>( class AudioContentDetailActivity : BaseActivity<ActivityAudioContentDetailBinding>(
ActivityAudioContentDetailBinding::inflate ActivityAudioContentDetailBinding::inflate
@ -70,16 +58,14 @@ class AudioContentDetailActivity : BaseActivity<ActivityAudioContentDetailBindin
private val audioContentReceiver = AudioContentReceiver() private val audioContentReceiver = AudioContentReceiver()
private var creatorId: Long = 0 private var creatorId: Long = 0
private val handler = Handler(Looper.getMainLooper())
private var refresh = false private var refresh = false
set(value) {
field = value
setResult(RESULT_OK)
}
private var title = "" private var title = ""
private lateinit var activityResultLauncher: ActivityResultLauncher<Intent>
private lateinit var audioContent: GetAudioContentDetailResponse
private lateinit var orderType: OrderType
private lateinit var imm: InputMethodManager
@SuppressLint("SetTextI18n") @SuppressLint("SetTextI18n")
override fun onNewIntent(intent: Intent) { override fun onNewIntent(intent: Intent) {
super.onNewIntent(intent) super.onNewIntent(intent)
@ -99,21 +85,11 @@ class AudioContentDetailActivity : BaseActivity<ActivityAudioContentDetailBindin
audioContentId = intent.getLongExtra(Constants.EXTRA_AUDIO_CONTENT_ID, 0) audioContentId = intent.getLongExtra(Constants.EXTRA_AUDIO_CONTENT_ID, 0)
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
imm = getSystemService(Service.INPUT_METHOD_SERVICE) as InputMethodManager
if (audioContentId <= 0) { if (audioContentId <= 0) {
Toast.makeText(applicationContext, "잘못된 요청입니다.", Toast.LENGTH_LONG).show() Toast.makeText(applicationContext, "잘못된 요청입니다.", Toast.LENGTH_LONG).show()
finish() finish()
} }
activityResultLauncher = registerForActivityResult(
ActivityResultContracts.StartActivityForResult()
) {
if (it.resultCode == Activity.RESULT_OK) {
contentOrder(audioContent, orderType)
}
}
bindData() bindData()
viewModel.getAudioContentDetail(audioContentId = audioContentId) { finish() } viewModel.getAudioContentDetail(audioContentId = audioContentId) { finish() }
} }
@ -121,11 +97,7 @@ class AudioContentDetailActivity : BaseActivity<ActivityAudioContentDetailBindin
override fun onResume() { override fun onResume() {
super.onResume() super.onResume()
val intentFilter = IntentFilter(Constants.ACTION_AUDIO_CONTENT_RECEIVER) val intentFilter = IntentFilter(Constants.ACTION_AUDIO_CONTENT_RECEIVER)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
registerReceiver(audioContentReceiver, intentFilter, Context.RECEIVER_NOT_EXPORTED)
} else {
registerReceiver(audioContentReceiver, intentFilter) registerReceiver(audioContentReceiver, intentFilter)
}
if (refresh) { if (refresh) {
viewModel.getAudioContentDetail(audioContentId = audioContentId) { finish() } viewModel.getAudioContentDetail(audioContentId = audioContentId) { finish() }
@ -143,7 +115,10 @@ class AudioContentDetailActivity : BaseActivity<ActivityAudioContentDetailBindin
binding.tvBack.setOnClickListener { finish() } binding.tvBack.setOnClickListener { finish() }
binding.ivClosePreviewAlert.setOnClickListener { viewModel.toggleShowPreviewAlert() } binding.ivClosePreviewAlert.setOnClickListener { viewModel.toggleShowPreviewAlert() }
binding.ivMenu.setOnClickListener { binding.ivMenu.setOnClickListener {
showOptionMenu() showOptionMenu(
this,
binding.ivMenu,
)
} }
creatorOtherContentAdapter = OtherContentAdapter { creatorOtherContentAdapter = OtherContentAdapter {
@ -265,22 +240,16 @@ class AudioContentDetailActivity : BaseActivity<ActivityAudioContentDetailBindin
} }
}) })
val ivCoverLp = binding.ivCover.layoutParams as RelativeLayout.LayoutParams val layoutParams = binding.ivCover.layoutParams as RelativeLayout.LayoutParams
ivCoverLp.width = (screenWidth - 13.3f.dpToPx()).toInt() layoutParams.width = (screenWidth - 13.3f.dpToPx()).toInt()
ivCoverLp.height = (screenWidth - 13.3f.dpToPx()).toInt() layoutParams.height = (screenWidth - 13.3f.dpToPx()).toInt()
binding.ivCover.layoutParams = ivCoverLp binding.ivCover.layoutParams = layoutParams
val flSoldOutLp = binding.flSoldOut.layoutParams as RelativeLayout.LayoutParams
flSoldOutLp.width = (screenWidth - 13.3f.dpToPx()).toInt()
flSoldOutLp.height = (screenWidth - 13.3f.dpToPx()).toInt()
binding.flSoldOut.layoutParams = flSoldOutLp
binding.ivPlayLoop.setOnClickListener { viewModel.togglePlayLoop() } binding.ivPlayLoop.setOnClickListener { viewModel.togglePlayLoop() }
binding.llDonation.setOnClickListener { binding.llDonation.setOnClickListener {
val dialog = LiveRoomDonationDialog( val dialog = LiveRoomDonationDialog(
this, this,
LayoutInflater.from(this) LayoutInflater.from(this)
) { can, message, _ -> ) { can, message ->
if (can <= 0) { if (can <= 0) {
showToast("1캔 이상 후원하실 수 있습니다.") showToast("1캔 이상 후원하실 수 있습니다.")
} else if (message.isBlank()) { } else if (message.isBlank()) {
@ -290,7 +259,7 @@ class AudioContentDetailActivity : BaseActivity<ActivityAudioContentDetailBindin
} }
} }
dialog.show(screenWidth - 26.7f.dpToPx().toInt()) dialog.show(screenWidth)
} }
} }
@ -300,46 +269,50 @@ class AudioContentDetailActivity : BaseActivity<ActivityAudioContentDetailBindin
} }
} }
private fun showOptionMenu() { private fun showOptionMenu(context: Context, v: View) {
val dialog = AudioContentDetailMenuBottomSheetDialog( val popup = PopupMenu(context, v)
isPin = viewModel.audioContentLiveData.value!!.isPin, val inflater = popup.menuInflater
isCreator = viewModel.audioContentLiveData.value!!
.creator.creatorId == SharedPreferenceManager.userId, if (
onClickPin = { viewModel.audioContentLiveData.value!!.creator.creatorId ==
if (viewModel.audioContentLiveData.value!!.isPin) { SharedPreferenceManager.userId
viewModel.unPinContent(audioContentId) ) {
} else { inflater.inflate(R.menu.audio_content_detail_creator_menu, popup.menu)
if (viewModel.audioContentLiveData.value!!.isAvailablePin) {
viewModel.pinContent(audioContentId) popup.setOnMenuItemClickListener {
} else { when (it.itemId) {
SodaDialog(this@AudioContentDetailActivity, R.id.menu_modify -> {
layoutInflater,
"고정 한도 도달",
"이 콘텐츠를 고정하시겠어요? " +
"채널에 콘텐츠를 최대 3개까지 고정할 수 있습니다." +
"이 콘텐츠를 고정하면 가장 오래된 콘텐츠가 대체됩니다.",
"확인",
{ viewModel.pinContent(audioContentId) },
"취소",
{}
).show(screenWidth)
}
}
},
onClickModify = {
refresh = true refresh = true
setResult(RESULT_OK)
startActivity( startActivity(
Intent(applicationContext, AudioContentModifyActivity::class.java) Intent(applicationContext, AudioContentModifyActivity::class.java)
.apply { .apply {
putExtra(Constants.EXTRA_AUDIO_CONTENT_ID, audioContentId) putExtra(Constants.EXTRA_AUDIO_CONTENT_ID, audioContentId)
} }
) )
}, }
onClickDelete = { showDeleteDialog() },
onClickReport = { showReportDialog() } R.id.menu_delete -> {
) showDeleteDialog()
dialog.show(supportFragmentManager, dialog.tag) }
}
true
}
} else {
inflater.inflate(R.menu.audio_content_detail_user_menu, popup.menu)
popup.setOnMenuItemClickListener {
when (it.itemId) {
R.id.menu_report -> {
showReportDialog()
}
}
true
}
}
popup.show()
} }
private fun showDeleteDialog() { private fun showDeleteDialog() {
@ -414,6 +387,18 @@ class AudioContentDetailActivity : BaseActivity<ActivityAudioContentDetailBindin
setupCommentArea(it) setupCommentArea(it)
setupCreatorOtherContentListArea(it.creatorOtherContentList) setupCreatorOtherContentListArea(it.creatorOtherContentList)
setupSameThemeOtherContentList(it.sameThemeOtherContentList) setupSameThemeOtherContentList(it.sameThemeOtherContentList)
isAlertPreview = it.creator.creatorId != SharedPreferenceManager.userId &&
!it.existOrdered &&
it.price > 0
binding.ivPlayOrPause.setImageResource(
if (isAlertPreview) {
R.drawable.btn_audio_content_preview_play
} else {
R.drawable.btn_audio_content_play
}
)
} }
viewModel.isContentPlayLoopLiveData.observe(this) { viewModel.isContentPlayLoopLiveData.observe(this) {
@ -460,11 +445,7 @@ class AudioContentDetailActivity : BaseActivity<ActivityAudioContentDetailBindin
} }
private fun setupCommentArea(response: GetAudioContentDetailResponse) { private fun setupCommentArea(response: GetAudioContentDetailResponse) {
if ( if (response.isCommentAvailable) {
response.isCommentAvailable &&
response.contentUrl.isNotBlank() &&
response.releaseDate == null
) {
binding.llDonation.visibility = View.VISIBLE binding.llDonation.visibility = View.VISIBLE
binding.llComment.visibility = View.VISIBLE binding.llComment.visibility = View.VISIBLE
binding.tvCommentCount.text = "${response.commentCount}" binding.tvCommentCount.text = "${response.commentCount}"
@ -478,13 +459,8 @@ class AudioContentDetailActivity : BaseActivity<ActivityAudioContentDetailBindin
binding.tvCommentText.text = response.commentList[0].comment binding.tvCommentText.text = response.commentList[0].comment
binding.tvCommentText.visibility = View.VISIBLE binding.tvCommentText.visibility = View.VISIBLE
binding.rlInputComment.visibility = View.GONE binding.rlInputComment.visibility = View.GONE
binding.tvSecret.visibility = View.GONE
binding.llComment.setOnClickListener { binding.llComment.setOnClickListener { showCommentBottomSheetDialog() }
showCommentBottomSheetDialog(
isShowSecret = response.existOrdered
)
}
} else { } else {
binding.tvCommentText.visibility = View.GONE binding.tvCommentText.visibility = View.GONE
binding.rlInputComment.visibility = View.VISIBLE binding.rlInputComment.visibility = View.VISIBLE
@ -495,22 +471,9 @@ class AudioContentDetailActivity : BaseActivity<ActivityAudioContentDetailBindin
} }
binding.ivCommentSend.setOnClickListener { binding.ivCommentSend.setOnClickListener {
hideKeyboard()
val comment = binding.etComment.text.toString() val comment = binding.etComment.text.toString()
val isSecret = binding.tvSecret.isSelected
viewModel.registerComment(audioContentId, comment, isSecret)
binding.etComment.setText("") binding.etComment.setText("")
binding.tvSecret.isSelected = false viewModel.registerComment(audioContentId, comment)
}
if (response.existOrdered) {
binding.tvSecret.visibility = View.VISIBLE
binding.tvSecret.setOnClickListener {
binding.tvSecret.isSelected = !binding.tvSecret.isSelected
}
} else {
binding.tvSecret.visibility = View.GONE
} }
binding.llComment.setOnClickListener {} binding.llComment.setOnClickListener {}
@ -521,11 +484,10 @@ class AudioContentDetailActivity : BaseActivity<ActivityAudioContentDetailBindin
} }
} }
private fun showCommentBottomSheetDialog(isShowSecret: Boolean) { private fun showCommentBottomSheetDialog() {
val dialog = AudioContentCommentFragment( val dialog = AudioContentCommentFragment(
creatorId = creatorId, creatorId = creatorId,
audioContentId = audioContentId, audioContentId = audioContentId
isShowSecret = isShowSecret
) )
dialog.show( dialog.show(
supportFragmentManager, supportFragmentManager,
@ -534,56 +496,14 @@ class AudioContentDetailActivity : BaseActivity<ActivityAudioContentDetailBindin
} }
private fun setupPurchaseButton(response: GetAudioContentDetailResponse) { private fun setupPurchaseButton(response: GetAudioContentDetailResponse) {
if (response.releaseDate != null) { if (
binding.llPurchase.visibility = View.VISIBLE
binding.llPurchasePrice.visibility = View.GONE
binding.tvPurchaseSoldOut.visibility = View.GONE
binding.tvReleaseDate.visibility = View.VISIBLE
binding.llPurchase.background = ContextCompat.getDrawable(
applicationContext,
R.drawable.bg_round_corner_5_3_525252
)
binding.tvReleaseDate.text = response.releaseDate
} else if (
response.price > 0 && response.price > 0 &&
!response.existOrdered && !response.existOrdered &&
response.orderType == null && response.orderType == null &&
response.creator.creatorId != SharedPreferenceManager.userId response.creator.creatorId != SharedPreferenceManager.userId
) { ) {
if (
response.totalContentCount != null && response.remainingContentCount != null &&
response.remainingContentCount <= 0
) {
binding.llPurchase.visibility = View.GONE
binding.tvPurchaseSoldOut.visibility = View.VISIBLE
} else {
binding.tvPurchaseSoldOut.visibility = View.GONE
binding.tvReleaseDate.visibility = View.GONE
binding.llPurchase.visibility = View.VISIBLE binding.llPurchase.visibility = View.VISIBLE
binding.llPurchasePrice.visibility = View.VISIBLE binding.tvPrice.text = response.price.toString()
binding.llPurchase.background = ContextCompat.getDrawable(
applicationContext,
R.drawable.bg_round_corner_5_3_3bb9f1
)
binding.ivCan.visibility = if (SharedPreferenceManager.userId == 17958L) {
View.GONE
} else {
View.VISIBLE
}
binding.tvPrice.text = if (SharedPreferenceManager.userId == 17958L) {
(response.price * 110).moneyFormat()
} else {
response.price.moneyFormat()
}
binding.tvUnit.text = if (SharedPreferenceManager.userId == 17958L) {
"원으로"
} else {
"캔으로"
}
binding.tvStrPurchaseOrRental.text = if (response.isOnlyRental) { binding.tvStrPurchaseOrRental.text = if (response.isOnlyRental) {
" 대여하기" " 대여하기"
@ -592,26 +512,10 @@ class AudioContentDetailActivity : BaseActivity<ActivityAudioContentDetailBindin
} }
binding.llPurchase.setOnClickListener { binding.llPurchase.setOnClickListener {
if ( showOrderDialog(audioContent = response, isOnlyRental = response.isOnlyRental)
response.totalContentCount != null &&
response.remainingContentCount != null
) {
showOrderConfirmDialog(
audioContent = response,
isOnlyRental = false,
OrderType.KEEP
)
} else {
showOrderDialog(
audioContent = response,
isOnlyRental = response.isOnlyRental
)
}
}
} }
} else { } else {
binding.llPurchase.visibility = View.GONE binding.llPurchase.visibility = View.GONE
binding.tvPurchaseSoldOut.visibility = View.GONE
} }
} }
@ -625,44 +529,15 @@ class AudioContentDetailActivity : BaseActivity<ActivityAudioContentDetailBindin
.apply(RequestOptions().override((screenWidth - 13.3f.dpToPx()).toInt())) .apply(RequestOptions().override((screenWidth - 13.3f.dpToPx()).toInt()))
.into(binding.ivCover) .into(binding.ivCover)
binding.flSoldOut.visibility = View.GONE
binding.tvSoldOutBig.visibility = View.GONE
binding.ivPlayOrPause.visibility = View.GONE
binding.tvPreviewNo.visibility = View.GONE
binding.tvTotalDuration.text = " / ${response.duration}"
isAlertPreview = response.creator.creatorId != SharedPreferenceManager.userId &&
!response.existOrdered &&
response.price > 0
if (
response.creator.creatorId != SharedPreferenceManager.userId && !response.existOrdered &&
response.totalContentCount != null && response.remainingContentCount != null &&
response.remainingContentCount <= 0
) {
binding.flSoldOut.visibility = View.VISIBLE
binding.tvSoldOutBig.visibility = View.VISIBLE
} else if (
response.releaseDate == null &&
!isAlertPreview ||
(response.isActivePreview && response.contentUrl.isNotBlank())
) {
binding.ivPlayOrPause.visibility = View.VISIBLE
binding.ivPlayOrPause.setOnClickListener { binding.ivPlayOrPause.setOnClickListener {
startService( startService(
Intent(this, AudioContentPlayService::class.java).apply { Intent(this, AudioContentPlayService::class.java).apply {
putExtra( putExtra(Constants.EXTRA_AUDIO_CONTENT_COVER_IMAGE_URL, response.coverImageUrl)
Constants.EXTRA_AUDIO_CONTENT_COVER_IMAGE_URL,
response.coverImageUrl
)
putExtra(Constants.EXTRA_AUDIO_CONTENT_URL, response.contentUrl) putExtra(Constants.EXTRA_AUDIO_CONTENT_URL, response.contentUrl)
putExtra(Constants.EXTRA_NICKNAME, response.creator.nickname) putExtra(Constants.EXTRA_NICKNAME, response.creator.nickname)
putExtra(Constants.EXTRA_AUDIO_CONTENT_TITLE, response.title) putExtra(Constants.EXTRA_AUDIO_CONTENT_TITLE, response.title)
putExtra(Constants.EXTRA_AUDIO_CONTENT_ID, response.contentId) putExtra(Constants.EXTRA_AUDIO_CONTENT_ID, response.contentId)
putExtra( putExtra(Constants.EXTRA_AUDIO_CONTENT_CREATOR_ID, response.creator.creatorId)
Constants.EXTRA_AUDIO_CONTENT_CREATOR_ID,
response.creator.creatorId
)
putExtra(Constants.EXTRA_AUDIO_CONTENT_FREE, response.price <= 0) putExtra(Constants.EXTRA_AUDIO_CONTENT_FREE, response.price <= 0)
putExtra( putExtra(
Constants.EXTRA_AUDIO_CONTENT_PREVIEW, Constants.EXTRA_AUDIO_CONTENT_PREVIEW,
@ -672,25 +547,11 @@ class AudioContentDetailActivity : BaseActivity<ActivityAudioContentDetailBindin
) )
} }
binding.ivPlayOrPause.setImageResource( binding.tvTotalDuration.text = " / ${response.duration}"
if (!isAlertPreview) {
R.drawable.btn_audio_content_play
} else {
R.drawable.btn_audio_content_preview_play
}
)
} else if (response.releaseDate == null) {
binding.tvPreviewNo.visibility = View.VISIBLE
}
} }
@SuppressLint("SetTextI18n") @SuppressLint("SetTextI18n")
private fun setupInfoArea(response: GetAudioContentDetailResponse) { private fun setupInfoArea(response: GetAudioContentDetailResponse) {
binding.tvScheduledToOpen.visibility = if (response.releaseDate != null) {
View.VISIBLE
} else {
View.GONE
}
binding.tvTheme.text = response.themeStr binding.tvTheme.text = response.themeStr
binding.tv19.visibility = if (response.isAdult) { binding.tv19.visibility = if (response.isAdult) {
View.VISIBLE View.VISIBLE
@ -724,9 +585,6 @@ class AudioContentDetailActivity : BaseActivity<ActivityAudioContentDetailBindin
binding.tvTag.visibility = View.GONE binding.tvTag.visibility = View.GONE
} }
if (response.contentUrl.isNotBlank() && response.releaseDate == null) {
binding.svActionButtons.visibility = View.VISIBLE
binding.llLike.visibility = View.VISIBLE
binding.ivLike.setImageResource( binding.ivLike.setImageResource(
if (response.isLike) { if (response.isLike) {
R.drawable.ic_audio_content_heart_pressed R.drawable.ic_audio_content_heart_pressed
@ -753,7 +611,6 @@ class AudioContentDetailActivity : BaseActivity<ActivityAudioContentDetailBindin
} }
} }
binding.tvShare.visibility = View.VISIBLE
binding.tvShare.setOnClickListener { binding.tvShare.setOnClickListener {
viewModel.shareAudioContent( viewModel.shareAudioContent(
audioContentId = audioContentId, audioContentId = audioContentId,
@ -768,38 +625,6 @@ class AudioContentDetailActivity : BaseActivity<ActivityAudioContentDetailBindin
startActivity(shareIntent) startActivity(shareIntent)
} }
} }
} else {
binding.svActionButtons.visibility = View.GONE
binding.llLike.visibility = View.GONE
binding.tvShare.visibility = View.GONE
}
if (response.totalContentCount != null && response.remainingContentCount != null) {
binding.rlLimitedEdition.visibility = View.VISIBLE
if (response.existOrdered) {
binding.tvRemaining.visibility = View.GONE
binding.tvTotalCount.visibility = View.VISIBLE
binding.tvRemainingCount.visibility = View.VISIBLE
binding.tvRemainingCount.text = "${response.orderSequence}"
binding.tvTotalCount.text = " / ${response.totalContentCount}"
} else if (response.remainingContentCount <= 0) {
binding.tvRemainingCount.visibility = View.GONE
binding.tvTotalCount.visibility = View.GONE
binding.tvRemaining.visibility = View.GONE
binding.tvSoldOutSmall.visibility = View.VISIBLE
} else {
binding.tvRemainingCount.visibility = View.VISIBLE
binding.tvRemaining.visibility = View.VISIBLE
binding.tvTotalCount.visibility = View.GONE
binding.tvRemainingCount.text = "${response.remainingContentCount}"
}
} else {
binding.rlLimitedEdition.visibility = View.GONE
}
} }
private fun setupMosaicArea(isMosaic: Boolean) { private fun setupMosaicArea(isMosaic: Boolean) {
@ -844,50 +669,21 @@ class AudioContentDetailActivity : BaseActivity<ActivityAudioContentDetailBindin
if (creator.creatorId != SharedPreferenceManager.userId) { if (creator.creatorId != SharedPreferenceManager.userId) {
binding.ivFollow.visibility = View.VISIBLE binding.ivFollow.visibility = View.VISIBLE
if (creator.isFollow) { if (creator.isFollowing) {
binding.ivFollow.setImageResource( binding.ivFollow.setImageResource(R.drawable.btn_notification_selected)
if (creator.isNotify) {
R.drawable.btn_following_big
} else {
R.drawable.btn_following_no_alarm_big
}
)
binding.ivFollow.setOnClickListener { binding.ivFollow.setOnClickListener {
val notifyFragment = CreatorFollowNotifyFragment( viewModel.unRegisterNotification(
onClickNotifyAll = {
viewModel.follow(
contentId = audioContentId, contentId = audioContentId,
creatorId = creator.creatorId, creatorId = creator.creatorId
follow = true,
notify = true
) )
},
onClickNotifyNone = {
viewModel.follow(
contentId = audioContentId,
creatorId = creator.creatorId,
follow = true,
notify = false
)
},
onClickUnFollow = {
viewModel.follow(
contentId = audioContentId,
creatorId = creator.creatorId,
follow = false,
notify = false
)
}
)
if (notifyFragment.isAdded) return@setOnClickListener
notifyFragment.show(supportFragmentManager, notifyFragment.tag)
} }
} else { } else {
binding.ivFollow.setImageResource(R.drawable.btn_follow_big) binding.ivFollow.setImageResource(R.drawable.btn_notification)
binding.ivFollow.setOnClickListener { binding.ivFollow.setOnClickListener {
viewModel.follow(contentId = audioContentId, creatorId = creator.creatorId) viewModel.registerNotification(
contentId = audioContentId,
creatorId = creator.creatorId
)
} }
} }
} else { } else {
@ -939,33 +735,6 @@ class AudioContentDetailActivity : BaseActivity<ActivityAudioContentDetailBindin
binding.rlPreviewAlert.visibility = View.GONE binding.rlPreviewAlert.visibility = View.GONE
if (SharedPreferenceManager.userId == 17958L) {
this@AudioContentDetailActivity.audioContent = audioContent
this@AudioContentDetailActivity.orderType = orderType
activityResultLauncher.launch(
Intent(applicationContext, CanPaymentTempActivity::class.java).apply {
putExtra("title", audioContent.title)
putExtra(
"can",
if (orderType == OrderType.RENTAL) {
ceil(audioContent.price * 0.7).toInt()
} else {
audioContent.price
}
)
}
)
} else {
contentOrder(audioContent, orderType)
}
},
).show(screenWidth)
}
private fun contentOrder(
audioContent: GetAudioContentDetailResponse,
orderType: OrderType
) {
viewModel.order( viewModel.order(
contentId = audioContent.contentId, contentId = audioContent.contentId,
orderType = orderType orderType = orderType
@ -974,15 +743,8 @@ class AudioContentDetailActivity : BaseActivity<ActivityAudioContentDetailBindin
intent.putExtra(Constants.EXTRA_GO_TO_PREV_PAGE, true) intent.putExtra(Constants.EXTRA_GO_TO_PREV_PAGE, true)
startActivity(intent) startActivity(intent)
} }
} },
).show(screenWidth)
private fun hideKeyboard() {
handler.postDelayed({
imm.hideSoftInputFromWindow(
window.decorView.applicationWindowToken,
InputMethodManager.HIDE_NOT_ALWAYS
)
}, 100)
} }
inner class AudioContentReceiver : BroadcastReceiver() { inner class AudioContentReceiver : BroadcastReceiver() {

View File

@ -1,66 +0,0 @@
package kr.co.vividnext.sodalive.audio_content.detail
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import com.google.android.material.bottomsheet.BottomSheetDialogFragment
import kr.co.vividnext.sodalive.R
import kr.co.vividnext.sodalive.databinding.DialogAudioContentDetailMenuBinding
class AudioContentDetailMenuBottomSheetDialog(
private val isPin: Boolean,
private val isCreator: Boolean,
private val onClickPin: () -> Unit,
private val onClickModify: () -> Unit,
private val onClickDelete: () -> Unit,
private val onClickReport: () -> Unit
) : BottomSheetDialogFragment() {
private lateinit var dialog: DialogAudioContentDetailMenuBinding
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
dialog = DialogAudioContentDetailMenuBinding.inflate(inflater, container, false)
return dialog.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
if (isCreator) {
dialog.tvReport.visibility = View.GONE
dialog.llMenuCreator.visibility = View.VISIBLE
if (isPin) {
dialog.ivPin.setImageResource(R.drawable.ic_pin_cancel)
dialog.tvPin.text = "내 채널에 고정 취소"
} else {
dialog.ivPin.setImageResource(R.drawable.ic_pin)
dialog.tvPin.text = "내 채널에 고정"
}
dialog.llPin.setOnClickListener {
dismiss()
onClickPin()
}
dialog.llModify.setOnClickListener {
dismiss()
onClickModify()
}
dialog.llDelete.setOnClickListener {
dismiss()
onClickDelete()
}
} else {
dialog.llMenuCreator.visibility = View.GONE
dialog.tvReport.visibility = View.VISIBLE
dialog.tvReport.setOnClickListener {
dismiss()
onClickReport()
}
}
}
}

View File

@ -25,11 +25,9 @@ import kr.co.vividnext.sodalive.mypage.auth.AuthVerifyRequest
import kr.co.vividnext.sodalive.report.ReportRepository import kr.co.vividnext.sodalive.report.ReportRepository
import kr.co.vividnext.sodalive.report.ReportRequest import kr.co.vividnext.sodalive.report.ReportRequest
import kr.co.vividnext.sodalive.report.ReportType import kr.co.vividnext.sodalive.report.ReportType
import kr.co.vividnext.sodalive.user.UserRepository
class AudioContentDetailViewModel( class AudioContentDetailViewModel(
private val repository: AudioContentRepository, private val repository: AudioContentRepository,
private val userRepository: UserRepository,
private val authRepository: AuthRepository, private val authRepository: AuthRepository,
private val reportRepository: ReportRepository, private val reportRepository: ReportRepository,
private val commentRepository: AudioContentCommentRepository private val commentRepository: AudioContentCommentRepository
@ -104,41 +102,6 @@ class AudioContentDetailViewModel(
) )
} }
fun follow(contentId: Long, creatorId: Long, follow: Boolean = true, notify: Boolean = true) {
isLoading.value = true
compositeDisposable.add(
userRepository.creatorFollow(
creatorId = creatorId,
follow,
notify,
token = "Bearer ${SharedPreferenceManager.token}"
)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
{
if (it.success && it.data != null) {
getAudioContentDetail(contentId)
} else {
if (it.message != null) {
_toastLiveData.postValue(it.message)
} else {
_toastLiveData.postValue(
"알 수 없는 오류가 발생했습니다. 다시 시도해 주세요."
)
}
}
isLoading.value = false
},
{
isLoading.value = false
it.message?.let { message -> Logger.e(message) }
_toastLiveData.postValue("알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.")
}
)
)
}
fun registerNotification(contentId: Long, creatorId: Long) { fun registerNotification(contentId: Long, creatorId: Long) {
isLoading.value = true isLoading.value = true
compositeDisposable.add( compositeDisposable.add(
@ -291,7 +254,7 @@ class AudioContentDetailViewModel(
) )
} }
fun registerComment(audioContentId: Long, comment: String, isSecret: Boolean) { fun registerComment(audioContentId: Long, comment: String) {
if (!isLoading.value!!) { if (!isLoading.value!!) {
isLoading.value = true isLoading.value = true
} }
@ -300,7 +263,6 @@ class AudioContentDetailViewModel(
commentRepository.registerComment( commentRepository.registerComment(
contentId = audioContentId, contentId = audioContentId,
comment = comment, comment = comment,
isSecret = isSecret,
token = "Bearer ${SharedPreferenceManager.token}" token = "Bearer ${SharedPreferenceManager.token}"
) )
.subscribeOn(Schedulers.io()) .subscribeOn(Schedulers.io())
@ -387,7 +349,7 @@ class AudioContentDetailViewModel(
} }
socialMetaTagParameters { socialMetaTagParameters {
title = contentTitle title = contentTitle
description = "지금 보이스온에서 이 콘텐츠 감상하기" description = "지금 소다라이브에서 이 콘텐츠 감상하기"
imageUrl = contentImage.toUri() imageUrl = contentImage.toUri()
} }
}.addOnSuccessListener { }.addOnSuccessListener {
@ -519,74 +481,4 @@ class AudioContentDetailViewModel(
) )
) )
} }
fun pinContent(audioContentId: Long) {
isLoading.value = true
compositeDisposable.add(
repository.pinContent(
audioContentId,
token = "Bearer ${SharedPreferenceManager.token}"
)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
{
isLoading.value = false
if (it.success) {
_toastLiveData.postValue("고정되었습니다.")
getAudioContentDetail(audioContentId)
} else {
if (it.message != null) {
_toastLiveData.postValue(it.message)
} else {
_toastLiveData.postValue(
"알 수 없는 오류가 발생했습니다. 다시 시도해 주세요."
)
}
}
},
{
isLoading.value = false
it.message?.let { message -> Logger.e(message) }
_toastLiveData.postValue("알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.")
}
)
)
}
fun unPinContent(audioContentId: Long) {
isLoading.value = true
compositeDisposable.add(
repository.unpinContent(
audioContentId,
token = "Bearer ${SharedPreferenceManager.token}"
)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
{
isLoading.value = false
if (it.success) {
_toastLiveData.postValue("해제되었습니다.")
getAudioContentDetail(audioContentId)
} else {
if (it.message != null) {
_toastLiveData.postValue(it.message)
} else {
_toastLiveData.postValue(
"알 수 없는 오류가 발생했습니다. 다시 시도해 주세요."
)
}
}
},
{
isLoading.value = false
it.message?.let { message -> Logger.e(message) }
_toastLiveData.postValue("알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.")
}
)
)
}
} }

View File

@ -1,11 +1,9 @@
package kr.co.vividnext.sodalive.audio_content.detail package kr.co.vividnext.sodalive.audio_content.detail
import androidx.annotation.Keep
import com.google.gson.annotations.SerializedName import com.google.gson.annotations.SerializedName
import kr.co.vividnext.sodalive.audio_content.comment.GetAudioContentCommentListItem import kr.co.vividnext.sodalive.audio_content.comment.GetAudioContentCommentListItem
import kr.co.vividnext.sodalive.audio_content.order.OrderType import kr.co.vividnext.sodalive.audio_content.order.OrderType
@Keep
data class GetAudioContentDetailResponse( data class GetAudioContentDetailResponse(
@SerializedName("contentId") val contentId: Long, @SerializedName("contentId") val contentId: Long,
@SerializedName("title") val title: String, @SerializedName("title") val title: String,
@ -16,11 +14,6 @@ data class GetAudioContentDetailResponse(
@SerializedName("tag") val tag: String, @SerializedName("tag") val tag: String,
@SerializedName("price") val price: Int, @SerializedName("price") val price: Int,
@SerializedName("duration") val duration: String, @SerializedName("duration") val duration: String,
@SerializedName("releaseDate") val releaseDate: String?,
@SerializedName("totalContentCount") val totalContentCount: Int?,
@SerializedName("remainingContentCount") val remainingContentCount: Int?,
@SerializedName("orderSequence") val orderSequence: Int?,
@SerializedName("isActivePreview") val isActivePreview: Boolean,
@SerializedName("isAdult") val isAdult: Boolean, @SerializedName("isAdult") val isAdult: Boolean,
@SerializedName("isMosaic") val isMosaic: Boolean, @SerializedName("isMosaic") val isMosaic: Boolean,
@SerializedName("isOnlyRental") val isOnlyRental: Boolean, @SerializedName("isOnlyRental") val isOnlyRental: Boolean,
@ -36,24 +29,18 @@ data class GetAudioContentDetailResponse(
@SerializedName("likeCount") val likeCount: Int, @SerializedName("likeCount") val likeCount: Int,
@SerializedName("commentList") val commentList: List<GetAudioContentCommentListItem>, @SerializedName("commentList") val commentList: List<GetAudioContentCommentListItem>,
@SerializedName("commentCount") val commentCount: Int, @SerializedName("commentCount") val commentCount: Int,
@SerializedName("isPin") val isPin: Boolean,
@SerializedName("isAvailablePin") val isAvailablePin: Boolean,
@SerializedName("creator") val creator: AudioContentCreator @SerializedName("creator") val creator: AudioContentCreator
) )
@Keep
data class OtherContentResponse( data class OtherContentResponse(
@SerializedName("contentId") val contentId: Long, @SerializedName("contentId") val contentId: Long,
@SerializedName("title") val title: String, @SerializedName("title") val title: String,
@SerializedName("coverUrl") val coverUrl: String, @SerializedName("coverUrl") val coverUrl: String,
) )
@Keep
data class AudioContentCreator( data class AudioContentCreator(
@SerializedName("creatorId") val creatorId: Long, @SerializedName("creatorId") val creatorId: Long,
@SerializedName("nickname") val nickname: String, @SerializedName("nickname") val nickname: String,
@SerializedName("profileImageUrl") val profileImageUrl: String, @SerializedName("profileImageUrl") val profileImageUrl: String,
@SerializedName("isFollowing") val isFollowing: Boolean, @SerializedName("isFollowing") val isFollowing: Boolean
@SerializedName("isFollow") var isFollow: Boolean,
@SerializedName("isNotify") var isNotify: Boolean
) )

View File

@ -1,14 +1,11 @@
package kr.co.vividnext.sodalive.audio_content.detail package kr.co.vividnext.sodalive.audio_content.detail
import androidx.annotation.Keep
import com.google.gson.annotations.SerializedName import com.google.gson.annotations.SerializedName
@Keep
data class PutAudioContentLikeRequest( data class PutAudioContentLikeRequest(
@SerializedName("contentId") val contentId: Long @SerializedName("contentId") val contentId: Long
) )
@Keep
data class PutAudioContentLikeResponse( data class PutAudioContentLikeResponse(
@SerializedName("like") val like: Boolean @SerializedName("like") val like: Boolean
) )

View File

@ -1,9 +1,7 @@
package kr.co.vividnext.sodalive.audio_content.donation package kr.co.vividnext.sodalive.audio_content.donation
import androidx.annotation.Keep
import com.google.gson.annotations.SerializedName import com.google.gson.annotations.SerializedName
@Keep
data class AudioContentDonationRequest( data class AudioContentDonationRequest(
@SerializedName("contentId") val contentId: Long, @SerializedName("contentId") val contentId: Long,
@SerializedName("donationCan") val donationCan: Int, @SerializedName("donationCan") val donationCan: Int,

View File

@ -1,4 +1,4 @@
package kr.co.vividnext.sodalive.audio_content.main.banner package kr.co.vividnext.sodalive.audio_content.main
import android.content.Context import android.content.Context
import android.graphics.Bitmap import android.graphics.Bitmap
@ -11,7 +11,6 @@ import com.bumptech.glide.request.transition.Transition
import com.zhpan.bannerview.BaseBannerAdapter import com.zhpan.bannerview.BaseBannerAdapter
import com.zhpan.bannerview.BaseViewHolder import com.zhpan.bannerview.BaseViewHolder
import kr.co.vividnext.sodalive.R import kr.co.vividnext.sodalive.R
import kr.co.vividnext.sodalive.audio_content.main.GetAudioContentBannerResponse
class AudioContentMainBannerAdapter( class AudioContentMainBannerAdapter(
private val context: Context, private val context: Context,

View File

@ -1,4 +1,4 @@
package kr.co.vividnext.sodalive.audio_content.main.curation package kr.co.vividnext.sodalive.audio_content.main
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.content.Context import android.content.Context
@ -8,9 +8,6 @@ import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import kr.co.vividnext.sodalive.audio_content.main.AudioContentMainContentAdapter
import kr.co.vividnext.sodalive.audio_content.main.GetAudioContentCurationResponse
import kr.co.vividnext.sodalive.audio_content.main.GetAudioContentMainItem
import kr.co.vividnext.sodalive.databinding.ItemAudioContentMainCurationBinding import kr.co.vividnext.sodalive.databinding.ItemAudioContentMainCurationBinding
import kr.co.vividnext.sodalive.extensions.dpToPx import kr.co.vividnext.sodalive.extensions.dpToPx
@ -92,11 +89,8 @@ class AudioContentMainCurationAdapter(
@SuppressLint("NotifyDataSetChanged") @SuppressLint("NotifyDataSetChanged")
fun addItems(items: List<GetAudioContentCurationResponse>) { fun addItems(items: List<GetAudioContentCurationResponse>) {
this.items.clear()
this.items.addAll(items) this.items.addAll(items)
notifyDataSetChanged() notifyDataSetChanged()
} }
fun clear() {
this.items.clear()
}
} }

View File

@ -1,11 +1,13 @@
package kr.co.vividnext.sodalive.audio_content.main package kr.co.vividnext.sodalive.audio_content.main
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.app.Service
import android.content.Intent import android.content.Intent
import android.graphics.Rect import android.graphics.Rect
import android.net.Uri import android.net.Uri
import android.os.Bundle import android.os.Bundle
import android.view.View import android.view.View
import android.view.inputmethod.InputMethodManager
import android.widget.LinearLayout import android.widget.LinearLayout
import android.widget.Toast import android.widget.Toast
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
@ -15,33 +17,21 @@ import androidx.recyclerview.widget.RecyclerView
import com.zhpan.bannerview.BaseBannerAdapter import com.zhpan.bannerview.BaseBannerAdapter
import com.zhpan.indicator.enums.IndicatorSlideMode import com.zhpan.indicator.enums.IndicatorSlideMode
import com.zhpan.indicator.enums.IndicatorStyle import com.zhpan.indicator.enums.IndicatorStyle
import kr.co.pointclick.sdk.offerwall.core.PointClickAd
import kr.co.vividnext.sodalive.R import kr.co.vividnext.sodalive.R
import kr.co.vividnext.sodalive.audio_content.all.AudioContentNewAllActivity import kr.co.vividnext.sodalive.audio_content.all.AudioContentNewAllActivity
import kr.co.vividnext.sodalive.audio_content.all.AudioContentRankingAllActivity import kr.co.vividnext.sodalive.audio_content.all.AudioContentRankingAllActivity
import kr.co.vividnext.sodalive.audio_content.all.by_theme.AudioContentAllByThemeActivity
import kr.co.vividnext.sodalive.audio_content.curation.AudioContentCurationActivity import kr.co.vividnext.sodalive.audio_content.curation.AudioContentCurationActivity
import kr.co.vividnext.sodalive.audio_content.detail.AudioContentDetailActivity import kr.co.vividnext.sodalive.audio_content.detail.AudioContentDetailActivity
import kr.co.vividnext.sodalive.audio_content.main.banner.AudioContentMainBannerAdapter
import kr.co.vividnext.sodalive.audio_content.main.banner.AudioContentMainBannerViewModel
import kr.co.vividnext.sodalive.audio_content.main.curation.AudioContentMainCurationAdapter
import kr.co.vividnext.sodalive.audio_content.main.curation.AudioContentMainCurationViewModel
import kr.co.vividnext.sodalive.audio_content.main.new_content.AudioContentMainNewContentThemeAdapter
import kr.co.vividnext.sodalive.audio_content.main.new_content.AudioContentMainNewContentViewModel
import kr.co.vividnext.sodalive.audio_content.main.order.AudioContentMainOrderListViewModel
import kr.co.vividnext.sodalive.audio_content.main.ranking.AudioContentMainRankingAdapter
import kr.co.vividnext.sodalive.audio_content.main.ranking.AudioContentMainRankingViewModel
import kr.co.vividnext.sodalive.audio_content.main.recommend_series.AudioContentMainRecommendSeriesViewModel
import kr.co.vividnext.sodalive.audio_content.order.AudioContentOrderListActivity import kr.co.vividnext.sodalive.audio_content.order.AudioContentOrderListActivity
import kr.co.vividnext.sodalive.audio_content.series.detail.SeriesDetailActivity
import kr.co.vividnext.sodalive.audio_content.upload.AudioContentUploadActivity import kr.co.vividnext.sodalive.audio_content.upload.AudioContentUploadActivity
import kr.co.vividnext.sodalive.base.BaseFragment import kr.co.vividnext.sodalive.base.BaseFragment
import kr.co.vividnext.sodalive.common.Constants import kr.co.vividnext.sodalive.common.Constants
import kr.co.vividnext.sodalive.common.LoadingDialog
import kr.co.vividnext.sodalive.common.SharedPreferenceManager import kr.co.vividnext.sodalive.common.SharedPreferenceManager
import kr.co.vividnext.sodalive.databinding.FragmentAudioContentMainBinding import kr.co.vividnext.sodalive.databinding.FragmentAudioContentMainBinding
import kr.co.vividnext.sodalive.explorer.profile.UserProfileActivity import kr.co.vividnext.sodalive.explorer.profile.UserProfileActivity
import kr.co.vividnext.sodalive.explorer.profile.series.UserProfileSeriesListAdapter
import kr.co.vividnext.sodalive.extensions.dpToPx import kr.co.vividnext.sodalive.extensions.dpToPx
import kr.co.vividnext.sodalive.mypage.alarm.AlarmListActivity
import kr.co.vividnext.sodalive.settings.event.EventDetailActivity import kr.co.vividnext.sodalive.settings.event.EventDetailActivity
import kr.co.vividnext.sodalive.settings.notification.MemberRole import kr.co.vividnext.sodalive.settings.notification.MemberRole
import org.koin.android.ext.android.inject import org.koin.android.ext.android.inject
@ -50,37 +40,32 @@ import kotlin.math.roundToInt
class AudioContentMainFragment : BaseFragment<FragmentAudioContentMainBinding>( class AudioContentMainFragment : BaseFragment<FragmentAudioContentMainBinding>(
FragmentAudioContentMainBinding::inflate FragmentAudioContentMainBinding::inflate
) { ) {
private val recommendSeriesViewModel: AudioContentMainRecommendSeriesViewModel by inject() private val viewModel: AudioContentMainViewModel by inject()
private lateinit var seriesAdapter: UserProfileSeriesListAdapter
private val bannerViewModel: AudioContentMainBannerViewModel by inject() private lateinit var loadingDialog: LoadingDialog
private lateinit var imm: InputMethodManager
private lateinit var newContentCreatorAdapter: AudioContentMainNewContentCreatorAdapter
private lateinit var bannerAdapter: AudioContentMainBannerAdapter private lateinit var bannerAdapter: AudioContentMainBannerAdapter
private val orderListViewModel: AudioContentMainOrderListViewModel by inject()
private lateinit var orderListAdapter: AudioContentMainContentAdapter private lateinit var orderListAdapter: AudioContentMainContentAdapter
private val newContentViewModel: AudioContentMainNewContentViewModel by inject()
private lateinit var newContentThemeAdapter: AudioContentMainNewContentThemeAdapter private lateinit var newContentThemeAdapter: AudioContentMainNewContentThemeAdapter
private lateinit var newContentAdapter: AudioContentMainContentAdapter private lateinit var newContentAdapter: AudioContentMainContentAdapter
private val contentRankingViewModel: AudioContentMainRankingViewModel by inject()
private lateinit var contentRankingSortAdapter: AudioContentMainNewContentThemeAdapter private lateinit var contentRankingSortAdapter: AudioContentMainNewContentThemeAdapter
private lateinit var contentRankingAdapter: AudioContentMainRankingAdapter private lateinit var contentRankingAdapter: AudioContentMainRankingAdapter
private val curationViewModel: AudioContentMainCurationViewModel by inject()
private lateinit var curationAdapter: AudioContentMainCurationAdapter private lateinit var curationAdapter: AudioContentMainCurationAdapter
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
setupView()
curationViewModel.getCurationList() loadingDialog = LoadingDialog(requireActivity(), layoutInflater)
bannerViewModel.getMainBannerList() imm = requireContext().getSystemService(
newContentViewModel.getThemeList() Service.INPUT_METHOD_SERVICE
newContentViewModel.getNewContentOfTheme("전체") ) as InputMethodManager
contentRankingViewModel.getContentRanking()
contentRankingViewModel.getContentRankingSortType() setupView()
recommendSeriesViewModel.getRecommendSeriesList() bindData()
viewModel.getMain()
} }
private fun setupView() { private fun setupView() {
@ -98,7 +83,7 @@ class AudioContentMainFragment : BaseFragment<FragmentAudioContentMainBinding>(
binding.llUploadContent.visibility = View.GONE binding.llUploadContent.visibility = View.GONE
} }
setupRecommendSeries() setupNewContentCreator()
setupBanner() setupBanner()
setupOrderList() setupOrderList()
setupNewContentTheme() setupNewContentTheme()
@ -107,68 +92,30 @@ class AudioContentMainFragment : BaseFragment<FragmentAudioContentMainBinding>(
setupContentRanking() setupContentRanking()
setupCuration() setupCuration()
binding.llShortPlay.setOnClickListener { binding.swipeRefreshLayout.setOnRefreshListener {
startActivity( binding.swipeRefreshLayout.isRefreshing = false
Intent(requireContext(), AudioContentAllByThemeActivity::class.java).apply { viewModel.getMain()
putExtra(Constants.EXTRA_THEME_ID, 11L)
}
)
} }
binding.llMorningCall.setOnClickListener { binding.ivCanFree.setOnClickListener {
startActivity( PointClickAd.showOfferwall(requireActivity(), "무료충전")
Intent(requireContext(), AudioContentAllByThemeActivity::class.java).apply {
putExtra(Constants.EXTRA_THEME_ID, 12L)
}
)
}
binding.ivContentKeep.setOnClickListener {
startActivity(
Intent(
requireContext(),
AudioContentOrderListActivity::class.java
)
)
}
binding.ivAlarm.setOnClickListener {
startActivity(
Intent(
requireActivity(),
AlarmListActivity::class.java
)
)
} }
} }
private fun setupRecommendSeries() { private fun setupNewContentCreator() {
seriesAdapter = UserProfileSeriesListAdapter( newContentCreatorAdapter = AudioContentMainNewContentCreatorAdapter {
onClickItem = { val intent = Intent(requireContext(), UserProfileActivity::class.java)
startActivity( intent.putExtra(Constants.EXTRA_USER_ID, it)
Intent(requireContext(), SeriesDetailActivity::class.java).apply { startActivity(intent)
putExtra(Constants.EXTRA_SERIES_ID, it)
} }
)
},
onClickCreator = {
startActivity(
Intent(requireContext(), UserProfileActivity::class.java).apply {
putExtra(Constants.EXTRA_USER_ID, it)
}
)
},
isVisibleCreator = true
)
val recyclerView = binding.rvRecommendSeries binding.rvNewContentCreator.layoutManager = LinearLayoutManager(
recyclerView.layoutManager = LinearLayoutManager( context,
requireContext(),
LinearLayoutManager.HORIZONTAL, LinearLayoutManager.HORIZONTAL,
false false
) )
recyclerView.addItemDecoration(object : RecyclerView.ItemDecoration() { binding.rvNewContentCreator.addItemDecoration(object : RecyclerView.ItemDecoration() {
override fun getItemOffsets( override fun getItemOffsets(
outRect: Rect, outRect: Rect,
view: View, view: View,
@ -180,43 +127,23 @@ class AudioContentMainFragment : BaseFragment<FragmentAudioContentMainBinding>(
when (parent.getChildAdapterPosition(view)) { when (parent.getChildAdapterPosition(view)) {
0 -> { 0 -> {
outRect.left = 0 outRect.left = 0
outRect.right = 6.7f.dpToPx().toInt() outRect.right = 10.7f.dpToPx().toInt()
} }
seriesAdapter.itemCount - 1 -> { newContentCreatorAdapter.itemCount - 1 -> {
outRect.left = 10.7f.dpToPx().toInt()
outRect.right = 0 outRect.right = 0
outRect.left = 6.7f.dpToPx().toInt()
} }
else -> { else -> {
outRect.left = 6.7f.dpToPx().toInt() outRect.left = 10.7f.dpToPx().toInt()
outRect.right = 6.7f.dpToPx().toInt() outRect.right = 10.7f.dpToPx().toInt()
} }
} }
} }
}) })
recyclerView.adapter = seriesAdapter binding.rvNewContentCreator.adapter = newContentCreatorAdapter
recommendSeriesViewModel.seriesListLiveData.observe(viewLifecycleOwner) {
seriesAdapter.addItems(it)
binding.llRecommendSeries.visibility = if (
seriesAdapter.itemCount <= 0 && it.isEmpty()
) {
View.GONE
} else {
View.VISIBLE
}
}
recommendSeriesViewModel.toastLiveData.observe(viewLifecycleOwner) {
it?.let { Toast.makeText(requireContext(), it, Toast.LENGTH_LONG).show() }
}
binding.llRecommendSeriesRefresh.setOnClickListener {
seriesAdapter.clear()
recommendSeriesViewModel.getRecommendSeriesList()
}
} }
private fun setupBanner() { private fun setupBanner() {
@ -277,25 +204,10 @@ class AudioContentMainFragment : BaseFragment<FragmentAudioContentMainBinding>(
.setIndicatorVisibility(View.GONE) .setIndicatorVisibility(View.GONE)
.setIndicatorSliderColor( .setIndicatorSliderColor(
ContextCompat.getColor(requireContext(), R.color.color_909090), ContextCompat.getColor(requireContext(), R.color.color_909090),
ContextCompat.getColor(requireContext(), R.color.color_3bb9f1) ContextCompat.getColor(requireContext(), R.color.color_9970ff)
) )
.setIndicatorSliderWidth(4f.dpToPx().toInt(), 10f.dpToPx().toInt()) .setIndicatorSliderWidth(4f.dpToPx().toInt(), 10f.dpToPx().toInt())
.setIndicatorHeight(4f.dpToPx().toInt()) .setIndicatorHeight(4f.dpToPx().toInt())
bannerViewModel.bannerLiveData.observe(viewLifecycleOwner) {
if (bannerAdapter.itemCount <= 0 && it.isEmpty()) {
binding.rvBanner.visibility = View.GONE
binding.indicatorBanner.visibility = View.GONE
} else {
binding.rvBanner.visibility = View.VISIBLE
binding.indicatorBanner.visibility = View.VISIBLE
binding.rvBanner.refreshData(it)
}
}
bannerViewModel.toastLiveData.observe(viewLifecycleOwner) {
it?.let { Toast.makeText(requireContext(), it, Toast.LENGTH_LONG).show() }
}
} }
private fun setupOrderList() { private fun setupOrderList() {
@ -354,26 +266,11 @@ class AudioContentMainFragment : BaseFragment<FragmentAudioContentMainBinding>(
binding.tvMyStashViewAll.setOnClickListener { binding.tvMyStashViewAll.setOnClickListener {
startActivity(Intent(requireContext(), AudioContentOrderListActivity::class.java)) startActivity(Intent(requireContext(), AudioContentOrderListActivity::class.java))
} }
orderListViewModel.orderListLiveData.observe(viewLifecycleOwner) {
orderListAdapter.addItems(it)
binding.llMyStash.visibility = if (
orderListAdapter.itemCount <= 0 && it.isEmpty()
) {
View.GONE
} else {
View.VISIBLE
}
}
orderListViewModel.toastLiveData.observe(viewLifecycleOwner) {
it?.let { Toast.makeText(requireContext(), it, Toast.LENGTH_LONG).show() }
}
} }
private fun setupNewContentTheme() { private fun setupNewContentTheme() {
newContentThemeAdapter = AudioContentMainNewContentThemeAdapter { newContentThemeAdapter = AudioContentMainNewContentThemeAdapter {
newContentViewModel.getNewContentOfTheme(theme = it) viewModel.getNewContentOfTheme(theme = it)
} }
binding.rvNewContentTheme.layoutManager = LinearLayoutManager( binding.rvNewContentTheme.layoutManager = LinearLayoutManager(
@ -411,11 +308,6 @@ class AudioContentMainFragment : BaseFragment<FragmentAudioContentMainBinding>(
}) })
binding.rvNewContentTheme.adapter = newContentThemeAdapter binding.rvNewContentTheme.adapter = newContentThemeAdapter
newContentViewModel.themeListLiveData.observe(viewLifecycleOwner) {
binding.llNewContent.visibility = View.VISIBLE
newContentThemeAdapter.addItems(it)
}
} }
private fun setupNewContent() { private fun setupNewContent() {
@ -475,27 +367,11 @@ class AudioContentMainFragment : BaseFragment<FragmentAudioContentMainBinding>(
}) })
binding.rvNewContent.adapter = newContentAdapter binding.rvNewContent.adapter = newContentAdapter
newContentViewModel.newContentListLiveData.observe(viewLifecycleOwner) {
newContentAdapter.addItems(it)
}
newContentViewModel.isLoading.observe(viewLifecycleOwner) {
binding.pbNewContent.visibility = if (it) {
View.VISIBLE
} else {
View.GONE
}
}
newContentViewModel.toastLiveData.observe(viewLifecycleOwner) {
it?.let { Toast.makeText(requireContext(), it, Toast.LENGTH_LONG).show() }
}
} }
private fun setupContentRankingSortType() { private fun setupContentRankingSortType() {
contentRankingSortAdapter = AudioContentMainNewContentThemeAdapter { contentRankingSortAdapter = AudioContentMainNewContentThemeAdapter {
contentRankingViewModel.getContentRanking(sort = it) viewModel.getContentRanking(sort = it)
} }
binding.rvContentRankingSort.layoutManager = LinearLayoutManager( binding.rvContentRankingSort.layoutManager = LinearLayoutManager(
@ -533,14 +409,8 @@ class AudioContentMainFragment : BaseFragment<FragmentAudioContentMainBinding>(
}) })
binding.rvContentRankingSort.adapter = contentRankingSortAdapter binding.rvContentRankingSort.adapter = contentRankingSortAdapter
contentRankingViewModel.contentRankingSortListLiveData.observe(viewLifecycleOwner) {
binding.llContentRanking.visibility = View.VISIBLE
contentRankingSortAdapter.addItems(it)
}
} }
@SuppressLint("SetTextI18n")
private fun setupContentRanking() { private fun setupContentRanking() {
binding.ivContentRankingAll.setOnClickListener { binding.ivContentRankingAll.setOnClickListener {
startActivity(Intent(requireContext(), AudioContentRankingAllActivity::class.java)) startActivity(Intent(requireContext(), AudioContentRankingAllActivity::class.java))
@ -579,16 +449,6 @@ class AudioContentMainFragment : BaseFragment<FragmentAudioContentMainBinding>(
}) })
binding.rvContentRanking.adapter = contentRankingAdapter binding.rvContentRanking.adapter = contentRankingAdapter
contentRankingViewModel.contentRankingLiveData.observe(viewLifecycleOwner) {
binding.llContentRanking.visibility = View.VISIBLE
binding.tvDate.text = "${it.startDate}~${it.endDate}"
contentRankingAdapter.addItems(it.items)
}
contentRankingViewModel.toastLiveData.observe(viewLifecycleOwner) {
it?.let { Toast.makeText(requireContext(), it, Toast.LENGTH_LONG).show() }
}
} }
private fun setupCuration() { private fun setupCuration() {
@ -651,50 +511,85 @@ class AudioContentMainFragment : BaseFragment<FragmentAudioContentMainBinding>(
} }
} }
}) })
binding.rvCuration.addOnScrollListener(object : RecyclerView.OnScrollListener() {
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
super.onScrolled(recyclerView, dx, dy)
val lastVisibleItemPosition = (recyclerView.layoutManager as LinearLayoutManager?)!!
.findLastCompletelyVisibleItemPosition()
val itemTotalCount = recyclerView.adapter!!.itemCount - 1
// 스크롤이 끝에 도달했는지 확인
if (!recyclerView.canScrollVertically(1) &&
lastVisibleItemPosition == itemTotalCount
) {
curationViewModel.getCurationList()
}
}
})
binding.rvCuration.adapter = curationAdapter binding.rvCuration.adapter = curationAdapter
curationViewModel.curationListLiveData.observe(viewLifecycleOwner) {
if (curationViewModel.page == 2) {
curationAdapter.clear()
} }
curationAdapter.addItems(it) @SuppressLint("SetTextI18n")
private fun bindData() {
binding.rvCuration.visibility = if (curationAdapter.itemCount <= 0 && it.isEmpty()) { viewModel.isLoading.observe(viewLifecycleOwner) {
View.GONE if (it) {
loadingDialog.show(screenWidth)
} else { } else {
View.VISIBLE loadingDialog.dismiss()
} }
} }
curationViewModel.isLoading.observe(viewLifecycleOwner) { viewModel.toastLiveData.observe(viewLifecycleOwner) {
binding.pbCuration.visibility = if (it) {
View.VISIBLE
} else {
View.GONE
}
}
curationViewModel.toastLiveData.observe(viewLifecycleOwner) {
it?.let { Toast.makeText(requireContext(), it, Toast.LENGTH_LONG).show() } it?.let { Toast.makeText(requireContext(), it, Toast.LENGTH_LONG).show() }
} }
viewModel.newContentUploadCreatorListLiveData.observe(viewLifecycleOwner) {
newContentCreatorAdapter.addItems(it)
binding.rvNewContentCreator.visibility = if (
newContentCreatorAdapter.itemCount <= 0 && it.isEmpty()
) {
View.GONE
} else {
View.VISIBLE
}
}
viewModel.bannerLiveData.observe(viewLifecycleOwner) {
if (bannerAdapter.itemCount <= 0 && it.isEmpty()) {
binding.rvBanner.visibility = View.GONE
binding.indicatorBanner.visibility = View.GONE
} else {
binding.rvBanner.visibility = View.VISIBLE
binding.indicatorBanner.visibility = View.VISIBLE
binding.rvBanner.refreshData(it)
}
}
viewModel.orderListLiveData.observe(viewLifecycleOwner) {
orderListAdapter.addItems(it)
binding.llMyStash.visibility = if (
orderListAdapter.itemCount <= 0 && it.isEmpty()
) {
View.GONE
} else {
View.VISIBLE
}
}
viewModel.newContentListLiveData.observe(viewLifecycleOwner) {
newContentAdapter.addItems(it)
}
viewModel.themeListLiveData.observe(viewLifecycleOwner) {
binding.llNewContent.visibility = View.VISIBLE
newContentThemeAdapter.addItems(it)
}
viewModel.curationListLiveData.observe(viewLifecycleOwner) {
curationAdapter.addItems(it)
binding.rvCuration.visibility = if (
curationAdapter.itemCount <= 0 && it.isEmpty()
) {
View.GONE
} else {
View.VISIBLE
}
}
viewModel.contentRankingSortListLiveData.observe(viewLifecycleOwner) {
binding.llContentRanking.visibility = View.VISIBLE
contentRankingSortAdapter.addItems(it)
}
viewModel.contentRankingLiveData.observe(viewLifecycleOwner) {
binding.llContentRanking.visibility = View.VISIBLE
binding.tvDate.text = "${it.startDate}~${it.endDate}"
contentRankingAdapter.addItems(it.items)
}
} }
} }

View File

@ -0,0 +1,52 @@
package kr.co.vividnext.sodalive.audio_content.main
import android.annotation.SuppressLint
import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import coil.load
import coil.transform.CircleCropTransformation
import kr.co.vividnext.sodalive.R
import kr.co.vividnext.sodalive.databinding.ItemAudioContentMainNewContentCreatorBinding
class AudioContentMainNewContentCreatorAdapter(
private val onClickItem: (Long) -> Unit
) : RecyclerView.Adapter<AudioContentMainNewContentCreatorAdapter.ViewHolder>() {
private val items = mutableListOf<GetNewContentUploadCreator>()
inner class ViewHolder(
private val binding: ItemAudioContentMainNewContentCreatorBinding
) : RecyclerView.ViewHolder(binding.root) {
fun bind(item: GetNewContentUploadCreator) {
binding.tvNewContentCreator.text = item.creatorNickname
binding.ivNewContentCreator.load(item.creatorProfileImageUrl) {
crossfade(true)
placeholder(R.drawable.ic_place_holder)
transformations(CircleCropTransformation())
}
binding.root.setOnClickListener { onClickItem(item.creatorId) }
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = ViewHolder(
ItemAudioContentMainNewContentCreatorBinding.inflate(
LayoutInflater.from(parent.context),
parent,
false
)
)
override fun getItemCount() = items.size
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
holder.bind(items[position])
}
@SuppressLint("NotifyDataSetChanged")
fun addItems(items: List<GetNewContentUploadCreator>) {
this.items.clear()
this.items.addAll(items)
notifyDataSetChanged()
}
}

View File

@ -1,4 +1,4 @@
package kr.co.vividnext.sodalive.audio_content.main.new_content package kr.co.vividnext.sodalive.audio_content.main
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.content.Context import android.content.Context
@ -28,9 +28,9 @@ class AudioContentMainNewContentThemeAdapter(
(selectedTheme == "" && theme == "매출") (selectedTheme == "" && theme == "매출")
) { ) {
binding.tvTheme.setBackgroundResource( binding.tvTheme.setBackgroundResource(
R.drawable.bg_round_corner_16_7_transparent_3bb9f1 R.drawable.bg_round_corner_16_7_transparent_9970ff
) )
binding.tvTheme.setTextColor(ContextCompat.getColor(context, R.color.color_3bb9f1)) binding.tvTheme.setTextColor(ContextCompat.getColor(context, R.color.color_9970ff))
} else { } else {
binding.tvTheme.setBackgroundResource( binding.tvTheme.setBackgroundResource(
R.drawable.bg_round_corner_16_7_transparent_777777 R.drawable.bg_round_corner_16_7_transparent_777777

View File

@ -1,4 +1,4 @@
package kr.co.vividnext.sodalive.audio_content.main.ranking package kr.co.vividnext.sodalive.audio_content.main
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.view.LayoutInflater import android.view.LayoutInflater
@ -7,7 +7,6 @@ import androidx.recyclerview.widget.RecyclerView
import coil.load import coil.load
import coil.transform.RoundedCornersTransformation import coil.transform.RoundedCornersTransformation
import kr.co.vividnext.sodalive.R import kr.co.vividnext.sodalive.R
import kr.co.vividnext.sodalive.audio_content.main.GetAudioContentRankingItem
import kr.co.vividnext.sodalive.databinding.ItemAudioContentMainRankingBinding import kr.co.vividnext.sodalive.databinding.ItemAudioContentMainRankingBinding
import kr.co.vividnext.sodalive.extensions.dpToPx import kr.co.vividnext.sodalive.extensions.dpToPx

View File

@ -1,4 +1,4 @@
package kr.co.vividnext.sodalive.audio_content.main.new_content package kr.co.vividnext.sodalive.audio_content.main
import androidx.lifecycle.LiveData import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData import androidx.lifecycle.MutableLiveData
@ -6,11 +6,10 @@ import com.orhanobut.logger.Logger
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
import io.reactivex.rxjava3.schedulers.Schedulers import io.reactivex.rxjava3.schedulers.Schedulers
import kr.co.vividnext.sodalive.audio_content.AudioContentRepository import kr.co.vividnext.sodalive.audio_content.AudioContentRepository
import kr.co.vividnext.sodalive.audio_content.main.GetAudioContentMainItem
import kr.co.vividnext.sodalive.base.BaseViewModel import kr.co.vividnext.sodalive.base.BaseViewModel
import kr.co.vividnext.sodalive.common.SharedPreferenceManager import kr.co.vividnext.sodalive.common.SharedPreferenceManager
class AudioContentMainNewContentViewModel( class AudioContentMainViewModel(
private val repository: AudioContentRepository private val repository: AudioContentRepository
) : BaseViewModel() { ) : BaseViewModel() {
private val _toastLiveData = MutableLiveData<String?>() private val _toastLiveData = MutableLiveData<String?>()
@ -21,24 +20,60 @@ class AudioContentMainNewContentViewModel(
val isLoading: LiveData<Boolean> val isLoading: LiveData<Boolean>
get() = _isLoading get() = _isLoading
private var _newContentUploadCreatorListLiveData =
MutableLiveData<List<GetNewContentUploadCreator>>()
val newContentUploadCreatorListLiveData: LiveData<List<GetNewContentUploadCreator>>
get() = _newContentUploadCreatorListLiveData
private var _newContentListLiveData = MutableLiveData<List<GetAudioContentMainItem>>() private var _newContentListLiveData = MutableLiveData<List<GetAudioContentMainItem>>()
val newContentListLiveData: LiveData<List<GetAudioContentMainItem>> val newContentListLiveData: LiveData<List<GetAudioContentMainItem>>
get() = _newContentListLiveData get() = _newContentListLiveData
private var _bannerLiveData = MutableLiveData<List<GetAudioContentBannerResponse>>()
val bannerLiveData: LiveData<List<GetAudioContentBannerResponse>>
get() = _bannerLiveData
private var _orderListLiveData = MutableLiveData<List<GetAudioContentMainItem>>()
val orderListLiveData: LiveData<List<GetAudioContentMainItem>>
get() = _orderListLiveData
private var _themeListLiveData = MutableLiveData<List<String>>() private var _themeListLiveData = MutableLiveData<List<String>>()
val themeListLiveData: LiveData<List<String>> val themeListLiveData: LiveData<List<String>>
get() = _themeListLiveData get() = _themeListLiveData
fun getThemeList() { private var _curationListLiveData = MutableLiveData<List<GetAudioContentCurationResponse>>()
val curationListLiveData: LiveData<List<GetAudioContentCurationResponse>>
get() = _curationListLiveData
private var _contentRankingSortListLiveData = MutableLiveData<List<String>>()
val contentRankingSortListLiveData: LiveData<List<String>>
get() = _contentRankingSortListLiveData
private var _contentRankingLiveData = MutableLiveData<GetAudioContentRanking>()
val contentRankingLiveData: LiveData<GetAudioContentRanking>
get() = _contentRankingLiveData
fun getMain() {
_isLoading.value = true
compositeDisposable.add( compositeDisposable.add(
repository.getNewContentThemeList(token = "Bearer ${SharedPreferenceManager.token}") repository.getMain(token = "Bearer ${SharedPreferenceManager.token}")
.subscribeOn(Schedulers.io()) .subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread()) .observeOn(AndroidSchedulers.mainThread())
.subscribe( .subscribe(
{ {
if (it.success && it.data != null) { if (it.success && it.data != null) {
val themeList = listOf("전체").union(it.data).toList() val data = it.data
_themeListLiveData.postValue(themeList) _newContentUploadCreatorListLiveData.value =
data.newContentUploadCreatorList
_newContentListLiveData.value = data.newContentList
_orderListLiveData.value = data.orderList
_bannerLiveData.value = data.bannerList
_curationListLiveData.value = data.curationList
_contentRankingLiveData.value = data.contentRanking
_contentRankingSortListLiveData.value = data.contentRankingSortTypeList
val themeList = listOf("전체").union(data.themeList).toList()
_themeListLiveData.value = themeList
} else { } else {
if (it.message != null) { if (it.message != null) {
_toastLiveData.postValue(it.message) _toastLiveData.postValue(it.message)
@ -61,7 +96,6 @@ class AudioContentMainNewContentViewModel(
} }
fun getNewContentOfTheme(theme: String) { fun getNewContentOfTheme(theme: String) {
_isLoading.value = true
compositeDisposable.add( compositeDisposable.add(
repository.getNewContentOfTheme( repository.getNewContentOfTheme(
theme = if (theme == "전체") { theme = if (theme == "전체") {
@ -86,13 +120,45 @@ class AudioContentMainNewContentViewModel(
) )
} }
} }
_isLoading.value = false
}, },
{ {
it.message?.let { message -> Logger.e(message) } it.message?.let { message -> Logger.e(message) }
_toastLiveData.postValue("알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.") _toastLiveData.postValue("알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.")
}
)
)
}
fun getContentRanking(sort: String) {
_isLoading.value = true
compositeDisposable.add(
repository.getContentRanking(
page = 1,
size = 12,
sortType = sort,
token = "Bearer ${SharedPreferenceManager.token}"
)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
{
if (it.success && it.data != null) {
_isLoading.value = false _isLoading.value = false
_contentRankingLiveData.value = it.data!!
} else {
_isLoading.value = false
if (it.message != null) {
_toastLiveData.postValue(it.message)
} else {
_toastLiveData.postValue(
"알 수 없는 오류가 발생했습니다. 다시 시도해 주세요."
)
}
}
},
{
it.message?.let { message -> Logger.e(message) }
_toastLiveData.postValue("알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.")
} }
) )
) )

View File

@ -1,36 +1,41 @@
package kr.co.vividnext.sodalive.audio_content.main package kr.co.vividnext.sodalive.audio_content.main
import androidx.annotation.Keep
import com.google.gson.annotations.SerializedName import com.google.gson.annotations.SerializedName
import kr.co.vividnext.sodalive.settings.event.EventItem import kr.co.vividnext.sodalive.settings.event.EventItem
@Keep data class GetAudioContentMainResponse(
@SerializedName("newContentUploadCreatorList")
val newContentUploadCreatorList: List<GetNewContentUploadCreator>,
@SerializedName("bannerList") val bannerList: List<GetAudioContentBannerResponse>,
@SerializedName("orderList") val orderList: List<GetAudioContentMainItem>,
@SerializedName("themeList") val themeList: List<String>,
@SerializedName("newContentList") val newContentList: List<GetAudioContentMainItem>,
@SerializedName("curationList") val curationList: List<GetAudioContentCurationResponse>,
@SerializedName("contentRankingSortTypeList") val contentRankingSortTypeList: List<String>,
@SerializedName("contentRanking") val contentRanking: GetAudioContentRanking
)
data class GetNewContentUploadCreator( data class GetNewContentUploadCreator(
@SerializedName("creatorId") val creatorId: Long, @SerializedName("creatorId") val creatorId: Long,
@SerializedName("creatorNickname") val creatorNickname: String, @SerializedName("creatorNickname") val creatorNickname: String,
@SerializedName("creatorProfileImageUrl") val creatorProfileImageUrl: String @SerializedName("creatorProfileImageUrl") val creatorProfileImageUrl: String
) )
@Keep
data class GetAudioContentMainItem( data class GetAudioContentMainItem(
@SerializedName("contentId") val contentId: Long, @SerializedName("contentId") val contentId: Long,
@SerializedName("coverImageUrl") val coverImageUrl: String, @SerializedName("coverImageUrl") val coverImageUrl: String,
@SerializedName("title") val title: String, @SerializedName("title") val title: String,
@SerializedName("creatorId") val creatorId: Long, @SerializedName("creatorId") val creatorId: Long,
@SerializedName("creatorProfileImageUrl") val creatorProfileImageUrl: String, @SerializedName("creatorProfileImageUrl") val creatorProfileImageUrl: String,
@SerializedName("creatorNickname") val creatorNickname: String, @SerializedName("creatorNickname") val creatorNickname: String
@SerializedName("price") val price: Int,
@SerializedName("duration") val duration: String
) )
@Keep
data class GetAudioContentRanking( data class GetAudioContentRanking(
@SerializedName("startDate") val startDate: String, @SerializedName("startDate") val startDate: String,
@SerializedName("endDate") val endDate: String, @SerializedName("endDate") val endDate: String,
@SerializedName("items") val items: List<GetAudioContentRankingItem> @SerializedName("items") val items: List<GetAudioContentRankingItem>
) )
@Keep
data class GetAudioContentRankingItem( data class GetAudioContentRankingItem(
@SerializedName("contentId") val contentId: Long, @SerializedName("contentId") val contentId: Long,
@SerializedName("title") val title: String, @SerializedName("title") val title: String,
@ -42,7 +47,6 @@ data class GetAudioContentRankingItem(
@SerializedName("creatorNickname") val creatorNickname: String @SerializedName("creatorNickname") val creatorNickname: String
) )
@Keep
data class GetAudioContentCurationResponse( data class GetAudioContentCurationResponse(
@SerializedName("curationId") val curationId: Long, @SerializedName("curationId") val curationId: Long,
@SerializedName("title") val title: String, @SerializedName("title") val title: String,
@ -50,7 +54,6 @@ data class GetAudioContentCurationResponse(
@SerializedName("contents") val audioContents: List<GetAudioContentMainItem> @SerializedName("contents") val audioContents: List<GetAudioContentMainItem>
) )
@Keep
data class GetAudioContentBannerResponse( data class GetAudioContentBannerResponse(
@SerializedName("type") val type: AudioContentBannerType, @SerializedName("type") val type: AudioContentBannerType,
@SerializedName("thumbnailImageUrl") val thumbnailImageUrl: String, @SerializedName("thumbnailImageUrl") val thumbnailImageUrl: String,

View File

@ -1,54 +0,0 @@
package kr.co.vividnext.sodalive.audio_content.main.banner
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import com.orhanobut.logger.Logger
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
import io.reactivex.rxjava3.schedulers.Schedulers
import kr.co.vividnext.sodalive.audio_content.AudioContentRepository
import kr.co.vividnext.sodalive.audio_content.main.GetAudioContentBannerResponse
import kr.co.vividnext.sodalive.base.BaseViewModel
import kr.co.vividnext.sodalive.common.SharedPreferenceManager
class AudioContentMainBannerViewModel(
private val repository: AudioContentRepository
) : BaseViewModel() {
private val _toastLiveData = MutableLiveData<String?>()
val toastLiveData: LiveData<String?>
get() = _toastLiveData
private var _bannerLiveData = MutableLiveData<List<GetAudioContentBannerResponse>>()
val bannerLiveData: LiveData<List<GetAudioContentBannerResponse>>
get() = _bannerLiveData
fun getMainBannerList() {
compositeDisposable.add(
repository.getMainBannerList(token = "Bearer ${SharedPreferenceManager.token}")
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
{
if (it.success && it.data != null) {
_bannerLiveData.postValue(it.data!!)
} else {
if (it.message != null) {
_toastLiveData.postValue(it.message)
} else {
_toastLiveData.postValue(
"배너를 불러오지 못했습니다. 다시 시도해 주세요.\n" +
"계속 같은 문제가 발생할 경우 고객센터로 문의 주시기 바랍니다."
)
}
}
},
{
it.message?.let { message -> Logger.e(message) }
_toastLiveData.postValue(
"배너를 불러오지 못했습니다. 다시 시도해 주세요.\n" +
"계속 같은 문제가 발생할 경우 고객센터로 문의 주시기 바랍니다."
)
}
)
)
}
}

View File

@ -1,85 +0,0 @@
package kr.co.vividnext.sodalive.audio_content.main.curation
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import com.orhanobut.logger.Logger
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
import io.reactivex.rxjava3.schedulers.Schedulers
import kr.co.vividnext.sodalive.audio_content.AudioContentRepository
import kr.co.vividnext.sodalive.audio_content.main.GetAudioContentCurationResponse
import kr.co.vividnext.sodalive.base.BaseViewModel
import kr.co.vividnext.sodalive.common.SharedPreferenceManager
class AudioContentMainCurationViewModel(
private val repository: AudioContentRepository
) : BaseViewModel() {
private val _toastLiveData = MutableLiveData<String?>()
val toastLiveData: LiveData<String?>
get() = _toastLiveData
private var _isLoading = MutableLiveData(false)
val isLoading: LiveData<Boolean>
get() = _isLoading
private var _curationListLiveData = MutableLiveData<List<GetAudioContentCurationResponse>>()
val curationListLiveData: LiveData<List<GetAudioContentCurationResponse>>
get() = _curationListLiveData
var page = 1
var isLast = false
private val pageSize = 10
fun getCurationList() {
if (!_isLoading.value!! && !isLast) {
_isLoading.value = true
compositeDisposable.add(
repository.getCurationList(
page = page,
size = pageSize,
token = "Bearer ${SharedPreferenceManager.token}"
)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
{
if (it.success && it.data != null) {
page += 1
if (it.data.isNotEmpty()) {
_curationListLiveData.postValue(it.data!!)
} else {
_curationListLiveData.postValue(listOf())
isLast = true
}
} else {
if (it.message != null) {
_toastLiveData.postValue(it.message)
} else {
_toastLiveData.postValue(
"큐레이션을 불러오지 못했습니다. 다시 시도해 주세요.\n" +
"계속 같은 문제가 발생할 경우 고객센터로 문의 주시기 바랍니다."
)
}
}
_isLoading.value = false
},
{
_isLoading.value = false
it.message?.let { message -> Logger.e(message) }
_toastLiveData.postValue(
"큐레이션을 불러오지 못했습니다. 다시 시도해 주세요.\n" +
"계속 같은 문제가 발생할 경우 고객센터로 문의 주시기 바랍니다."
)
}
)
)
}
}
fun refresh() {
page = 1
isLast = false
getCurationList()
}
}

View File

@ -1,54 +0,0 @@
package kr.co.vividnext.sodalive.audio_content.main.order
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import com.orhanobut.logger.Logger
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
import io.reactivex.rxjava3.schedulers.Schedulers
import kr.co.vividnext.sodalive.audio_content.AudioContentRepository
import kr.co.vividnext.sodalive.audio_content.main.GetAudioContentMainItem
import kr.co.vividnext.sodalive.base.BaseViewModel
import kr.co.vividnext.sodalive.common.SharedPreferenceManager
class AudioContentMainOrderListViewModel(
private val repository: AudioContentRepository
) : BaseViewModel() {
private val _toastLiveData = MutableLiveData<String?>()
val toastLiveData: LiveData<String?>
get() = _toastLiveData
private var _orderListLiveData = MutableLiveData<List<GetAudioContentMainItem>>()
val orderListLiveData: LiveData<List<GetAudioContentMainItem>>
get() = _orderListLiveData
fun getOrderList() {
compositeDisposable.add(
repository.getMainOrderList(token = "Bearer ${SharedPreferenceManager.token}")
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
{
if (it.success && it.data != null) {
_orderListLiveData.postValue(it.data!!)
} else {
if (it.message != null) {
_toastLiveData.postValue(it.message)
} else {
_toastLiveData.postValue(
"주문정보를 불러오지 못했습니다. 다시 시도해 주세요.\n" +
"계속 같은 문제가 발생할 경우 고객센터로 문의 주시기 바랍니다."
)
}
}
},
{
it.message?.let { message -> Logger.e(message) }
_toastLiveData.postValue(
"주문정보를 불러오지 못했습니다. 다시 시도해 주세요.\n" +
"계속 같은 문제가 발생할 경우 고객센터로 문의 주시기 바랍니다."
)
}
)
)
}
}

View File

@ -1,86 +0,0 @@
package kr.co.vividnext.sodalive.audio_content.main.ranking
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import com.orhanobut.logger.Logger
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
import io.reactivex.rxjava3.schedulers.Schedulers
import kr.co.vividnext.sodalive.audio_content.AudioContentRepository
import kr.co.vividnext.sodalive.audio_content.main.GetAudioContentRanking
import kr.co.vividnext.sodalive.base.BaseViewModel
import kr.co.vividnext.sodalive.common.SharedPreferenceManager
class AudioContentMainRankingViewModel(
private val repository: AudioContentRepository
) : BaseViewModel() {
private val _toastLiveData = MutableLiveData<String?>()
val toastLiveData: LiveData<String?>
get() = _toastLiveData
private var _contentRankingSortListLiveData = MutableLiveData<List<String>>()
val contentRankingSortListLiveData: LiveData<List<String>>
get() = _contentRankingSortListLiveData
private var _contentRankingLiveData = MutableLiveData<GetAudioContentRanking>()
val contentRankingLiveData: LiveData<GetAudioContentRanking>
get() = _contentRankingLiveData
fun getContentRankingSortType() {
compositeDisposable.add(
repository.getContentRankingSortType(token = "Bearer ${SharedPreferenceManager.token}")
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
{
if (it.success && it.data != null) {
_contentRankingSortListLiveData.value = it.data!!
} else {
if (it.message != null) {
_toastLiveData.postValue(it.message)
} else {
_toastLiveData.postValue(
"알 수 없는 오류가 발생했습니다. 다시 시도해 주세요."
)
}
}
},
{
it.message?.let { message -> Logger.e(message) }
_toastLiveData.postValue("알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.")
}
)
)
}
fun getContentRanking(sort: String = "매출") {
compositeDisposable.add(
repository.getContentRanking(
page = 1,
size = 12,
sortType = sort,
token = "Bearer ${SharedPreferenceManager.token}"
)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
{
if (it.success && it.data != null) {
_contentRankingLiveData.value = it.data!!
} else {
if (it.message != null) {
_toastLiveData.postValue(it.message)
} else {
_toastLiveData.postValue(
"알 수 없는 오류가 발생했습니다. 다시 시도해 주세요."
)
}
}
},
{
it.message?.let { message -> Logger.e(message) }
_toastLiveData.postValue("알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.")
}
)
)
}
}

View File

@ -1,55 +0,0 @@
package kr.co.vividnext.sodalive.audio_content.main.recommend_series
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import com.orhanobut.logger.Logger
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
import io.reactivex.rxjava3.schedulers.Schedulers
import kr.co.vividnext.sodalive.audio_content.series.GetSeriesListResponse
import kr.co.vividnext.sodalive.audio_content.series.SeriesRepository
import kr.co.vividnext.sodalive.base.BaseViewModel
import kr.co.vividnext.sodalive.common.SharedPreferenceManager
class AudioContentMainRecommendSeriesViewModel(
private val repository: SeriesRepository
) : BaseViewModel() {
private val _toastLiveData = MutableLiveData<String?>()
val toastLiveData: LiveData<String?>
get() = _toastLiveData
private var _seriesListLiveData = MutableLiveData<List<GetSeriesListResponse.SeriesListItem>>()
val seriesListLiveData: LiveData<List<GetSeriesListResponse.SeriesListItem>>
get() = _seriesListLiveData
fun getRecommendSeriesList() {
compositeDisposable.add(
repository
.getRecommendSeriesList(token = "Bearer ${SharedPreferenceManager.token}")
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
{
if (it.success && it.data != null) {
_seriesListLiveData.value = it.data!!
} else {
if (it.message != null) {
_toastLiveData.postValue(it.message)
} else {
_toastLiveData.postValue(
"추천 시리즈를 불러오지 못했습니다. 다시 시도해 주세요.\n" +
"계속 같은 문제가 발생할 경우 고객센터로 문의 주시기 바랍니다."
)
}
}
},
{
it.message?.let { message -> Logger.e(message) }
_toastLiveData.postValue(
"추천 시리즈를 불러오지 못했습니다. 다시 시도해 주세요.\n" +
"계속 같은 문제가 발생할 경우 고객센터로 문의 주시기 바랍니다."
)
}
)
)
}
}

View File

@ -112,7 +112,7 @@ class AudioContentModifyActivity : BaseActivity<ActivityAudioContentModifyBindin
private fun checkPermissions() { private fun checkPermissions() {
val permissions = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { val permissions = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
listOf(Manifest.permission.READ_MEDIA_AUDIO) listOf(Manifest.permission.READ_MEDIA_AUDIO, Manifest.permission.READ_MEDIA_IMAGES)
} else { } else {
listOf(Manifest.permission.READ_EXTERNAL_STORAGE) listOf(Manifest.permission.READ_EXTERNAL_STORAGE)
} }
@ -173,17 +173,17 @@ class AudioContentModifyActivity : BaseActivity<ActivityAudioContentModifyBindin
R.color.color_eeeeee R.color.color_eeeeee
) )
) )
binding.llCommentYes.setBackgroundResource(R.drawable.bg_round_corner_6_7_3bb9f1) binding.llCommentYes.setBackgroundResource(R.drawable.bg_round_corner_6_7_9970ff)
binding.ivCommentNo.visibility = View.GONE binding.ivCommentNo.visibility = View.GONE
binding.tvCommentNo.setTextColor( binding.tvCommentNo.setTextColor(
ContextCompat.getColor( ContextCompat.getColor(
applicationContext, applicationContext,
R.color.color_3bb9f1 R.color.color_9970ff
) )
) )
binding.llCommentNo.setBackgroundResource( binding.llCommentNo.setBackgroundResource(
R.drawable.bg_round_corner_6_7_13181b_3bb9f1 R.drawable.bg_round_corner_6_7_1f1734_9970ff
) )
} else { } else {
binding.ivCommentNo.visibility = View.VISIBLE binding.ivCommentNo.visibility = View.VISIBLE
@ -193,17 +193,17 @@ class AudioContentModifyActivity : BaseActivity<ActivityAudioContentModifyBindin
R.color.color_eeeeee R.color.color_eeeeee
) )
) )
binding.llCommentNo.setBackgroundResource(R.drawable.bg_round_corner_6_7_3bb9f1) binding.llCommentNo.setBackgroundResource(R.drawable.bg_round_corner_6_7_9970ff)
binding.ivCommentYes.visibility = View.GONE binding.ivCommentYes.visibility = View.GONE
binding.tvCommentYes.setTextColor( binding.tvCommentYes.setTextColor(
ContextCompat.getColor( ContextCompat.getColor(
applicationContext, applicationContext,
R.color.color_3bb9f1 R.color.color_9970ff
) )
) )
binding.llCommentYes binding.llCommentYes
.setBackgroundResource(R.drawable.bg_round_corner_6_7_13181b_3bb9f1) .setBackgroundResource(R.drawable.bg_round_corner_6_7_1f1734_9970ff)
} }
} }
@ -223,17 +223,17 @@ class AudioContentModifyActivity : BaseActivity<ActivityAudioContentModifyBindin
if (it) { if (it) {
binding.ivAgeAll.visibility = View.GONE binding.ivAgeAll.visibility = View.GONE
binding.llAgeAll.setBackgroundResource( binding.llAgeAll.setBackgroundResource(
R.drawable.bg_round_corner_6_7_13181b R.drawable.bg_round_corner_6_7_1f1734
) )
binding.tvAgeAll.setTextColor( binding.tvAgeAll.setTextColor(
ContextCompat.getColor( ContextCompat.getColor(
applicationContext, applicationContext,
R.color.color_3bb9f1 R.color.color_9970ff
) )
) )
binding.ivAge19.visibility = View.VISIBLE binding.ivAge19.visibility = View.VISIBLE
binding.llAge19.setBackgroundResource(R.drawable.bg_round_corner_6_7_3bb9f1) binding.llAge19.setBackgroundResource(R.drawable.bg_round_corner_6_7_9970ff)
binding.tvAge19.setTextColor( binding.tvAge19.setTextColor(
ContextCompat.getColor( ContextCompat.getColor(
applicationContext, applicationContext,
@ -242,17 +242,17 @@ class AudioContentModifyActivity : BaseActivity<ActivityAudioContentModifyBindin
) )
} else { } else {
binding.ivAge19.visibility = View.GONE binding.ivAge19.visibility = View.GONE
binding.llAge19.setBackgroundResource(R.drawable.bg_round_corner_6_7_13181b) binding.llAge19.setBackgroundResource(R.drawable.bg_round_corner_6_7_1f1734)
binding.tvAge19.setTextColor( binding.tvAge19.setTextColor(
ContextCompat.getColor( ContextCompat.getColor(
applicationContext, applicationContext,
R.color.color_3bb9f1 R.color.color_9970ff
) )
) )
binding.ivAgeAll.visibility = View.VISIBLE binding.ivAgeAll.visibility = View.VISIBLE
binding.llAgeAll.setBackgroundResource( binding.llAgeAll.setBackgroundResource(
R.drawable.bg_round_corner_6_7_3bb9f1 R.drawable.bg_round_corner_6_7_9970ff
) )
binding.tvAgeAll.setTextColor( binding.tvAgeAll.setTextColor(
ContextCompat.getColor( ContextCompat.getColor(

View File

@ -1,9 +1,7 @@
package kr.co.vividnext.sodalive.audio_content.modify package kr.co.vividnext.sodalive.audio_content.modify
import androidx.annotation.Keep
import com.google.gson.annotations.SerializedName import com.google.gson.annotations.SerializedName
@Keep
data class ModifyAudioContentRequest( data class ModifyAudioContentRequest(
@SerializedName("contentId") val contentId: Long, @SerializedName("contentId") val contentId: Long,
@SerializedName("title") val title: String?, @SerializedName("title") val title: String?,

View File

@ -4,17 +4,14 @@ import android.app.Activity
import android.graphics.Color import android.graphics.Color
import android.graphics.drawable.ColorDrawable import android.graphics.drawable.ColorDrawable
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View
import android.view.WindowManager import android.view.WindowManager
import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AlertDialog
import coil.load import coil.load
import coil.transform.CircleCropTransformation import coil.transform.CircleCropTransformation
import coil.transform.RoundedCornersTransformation import coil.transform.RoundedCornersTransformation
import kr.co.vividnext.sodalive.R import kr.co.vividnext.sodalive.R
import kr.co.vividnext.sodalive.common.SharedPreferenceManager
import kr.co.vividnext.sodalive.databinding.DialogAudioContentOrderConfirmBinding import kr.co.vividnext.sodalive.databinding.DialogAudioContentOrderConfirmBinding
import kr.co.vividnext.sodalive.extensions.dpToPx import kr.co.vividnext.sodalive.extensions.dpToPx
import kr.co.vividnext.sodalive.extensions.moneyFormat
import kotlin.math.ceil import kotlin.math.ceil
class AudioContentOrderConfirmDialog( class AudioContentOrderConfirmDialog(
@ -61,36 +58,17 @@ class AudioContentOrderConfirmDialog(
} }
dialogView.tvDuration.text = duration dialogView.tvDuration.text = duration
if (SharedPreferenceManager.userId == 17958L) {
dialogView.ivCan.visibility = View.GONE
dialogView.tvPrice.text = if (orderType == OrderType.RENTAL && !isOnlyRental) { dialogView.tvPrice.text = if (orderType == OrderType.RENTAL && !isOnlyRental) {
"${(ceil(price * 0.7).toInt() * 110).moneyFormat()}" "${ceil(price * 0.6).toInt()}"
} else { } else {
"${(price * 110).moneyFormat()}" "$price"
}
} else {
dialogView.ivCan.visibility = View.VISIBLE
dialogView.tvPrice.text = if (orderType == OrderType.RENTAL && !isOnlyRental) {
ceil(price * 0.7).toInt().moneyFormat()
} else {
price.moneyFormat()
}
} }
if (SharedPreferenceManager.userId == 17958L) {
dialogView.tvNotice.text = if (orderType == OrderType.RENTAL) {
"콘텐츠를 대여하시겠습니까?"
} else {
"콘텐츠를 소장하시겠습니까?"
}
} else {
dialogView.tvNotice.text = if (orderType == OrderType.RENTAL) { dialogView.tvNotice.text = if (orderType == OrderType.RENTAL) {
"콘텐츠를 대여하시겠습니까?\n아래 캔이 차감됩니다." "콘텐츠를 대여하시겠습니까?\n아래 캔이 차감됩니다."
} else { } else {
"콘텐츠를 소장하시겠습니까?\n아래 캔이 차감됩니다." "콘텐츠를 소장하시겠습니까?\n아래 캔이 차감됩니다."
} }
}
dialogView.tvCancel.setOnClickListener { dialogView.tvCancel.setOnClickListener {
alertDialog.dismiss() alertDialog.dismiss()

View File

@ -1,14 +1,11 @@
package kr.co.vividnext.sodalive.audio_content.order package kr.co.vividnext.sodalive.audio_content.order
import android.annotation.SuppressLint
import android.os.Bundle import android.os.Bundle
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import com.google.android.material.bottomsheet.BottomSheetDialogFragment import com.google.android.material.bottomsheet.BottomSheetDialogFragment
import kr.co.vividnext.sodalive.common.SharedPreferenceManager
import kr.co.vividnext.sodalive.databinding.FragmentAudioContentOrderBinding import kr.co.vividnext.sodalive.databinding.FragmentAudioContentOrderBinding
import kr.co.vividnext.sodalive.extensions.moneyFormat
import kotlin.math.ceil import kotlin.math.ceil
class AudioContentOrderFragment( class AudioContentOrderFragment(
@ -29,35 +26,15 @@ class AudioContentOrderFragment(
return binding.root return binding.root
} }
@SuppressLint("SetTextI18n")
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
if (SharedPreferenceManager.userId == 17958L) {
binding.tvKeepDate.text = "(이용기간 1년)"
binding.ivKeepCan.visibility = View.GONE
binding.ivRentalCan.visibility = View.GONE
} else {
binding.tvKeepDate.text = "(서비스 종료시까지)"
binding.ivKeepCan.visibility = View.VISIBLE
binding.ivRentalCan.visibility = View.VISIBLE
}
if (isOnlyRental) { if (isOnlyRental) {
if (SharedPreferenceManager.userId == 17958L) { binding.tvRental.text = "$price"
binding.tvRental.text = "${(price * 110).moneyFormat()}"
} else {
binding.tvRental.text = price.moneyFormat()
}
binding.rlKeep.visibility = View.GONE binding.rlKeep.visibility = View.GONE
} else { } else {
if (SharedPreferenceManager.userId == 17958L) { binding.tvKeep.text = "$price"
binding.tvKeep.text = "${(price * 110).moneyFormat()}" binding.tvRental.text = "${ceil(price * 0.6).toInt()}"
binding.tvRental.text = "${(ceil(price * 0.7).toInt() * 110).moneyFormat()}"
} else {
binding.tvKeep.text = price.moneyFormat()
binding.tvRental.text = ceil(price * 0.7).toInt().moneyFormat()
}
binding.rlKeep.visibility = View.VISIBLE binding.rlKeep.visibility = View.VISIBLE
binding.llKeep.setOnClickListener { binding.llKeep.setOnClickListener {

View File

@ -34,7 +34,7 @@ class AudioContentOrderListActivity : BaseActivity<ActivityAudioContentOrderList
override fun setupView() { override fun setupView() {
loadingDialog = LoadingDialog(this, layoutInflater) loadingDialog = LoadingDialog(this, layoutInflater)
binding.toolbar.tvBack.text = "콘텐츠 보관함" binding.toolbar.tvBack.text = "구매목록"
binding.toolbar.tvBack.setOnClickListener { finish() } binding.toolbar.tvBack.setOnClickListener { finish() }
adapter = AudioContentOrderListAdapter { adapter = AudioContentOrderListAdapter {

View File

@ -48,11 +48,10 @@ class AudioContentOrderListViewModel(
{ {
if (it.success && it.data != null) { if (it.success && it.data != null) {
_totalCount.value = it.data.totalCount _totalCount.value = it.data.totalCount
page += 1
if (it.data.items.isNotEmpty()) { if (it.data.items.isNotEmpty()) {
page += 1
_orderList.postValue(it.data.items) _orderList.postValue(it.data.items)
} else { } else {
_orderList.postValue(listOf())
isLast = true isLast = true
} }
} else { } else {

View File

@ -1,15 +1,12 @@
package kr.co.vividnext.sodalive.audio_content.order package kr.co.vividnext.sodalive.audio_content.order
import androidx.annotation.Keep
import com.google.gson.annotations.SerializedName import com.google.gson.annotations.SerializedName
@Keep
data class GetAudioContentOrderListResponse( data class GetAudioContentOrderListResponse(
@SerializedName("totalCount") val totalCount: Int, @SerializedName("totalCount") val totalCount: Int,
@SerializedName("items") val items: List<GetAudioContentOrderListItem> @SerializedName("items") val items: List<GetAudioContentOrderListItem>
) )
@Keep
data class GetAudioContentOrderListItem( data class GetAudioContentOrderListItem(
@SerializedName("contentId") val contentId: Long, @SerializedName("contentId") val contentId: Long,
@SerializedName("coverImageUrl") val coverImageUrl: String, @SerializedName("coverImageUrl") val coverImageUrl: String,

View File

@ -1,9 +1,7 @@
package kr.co.vividnext.sodalive.audio_content.order package kr.co.vividnext.sodalive.audio_content.order
import androidx.annotation.Keep
import com.google.gson.annotations.SerializedName import com.google.gson.annotations.SerializedName
@Keep
data class OrderRequest( data class OrderRequest(
@SerializedName("contentId") val contentId: Long, @SerializedName("contentId") val contentId: Long,
@SerializedName("orderType") val orderType: OrderType, @SerializedName("orderType") val orderType: OrderType,

View File

@ -1,30 +0,0 @@
package kr.co.vividnext.sodalive.audio_content.series
import androidx.annotation.Keep
import com.google.gson.annotations.SerializedName
@Keep
data class GetSeriesListResponse(
@SerializedName("totalCount") val totalCount: Int,
@SerializedName("items") val items: List<SeriesListItem>
) {
@Keep
data class SeriesListItem(
@SerializedName("seriesId") val seriesId: Long,
@SerializedName("title") val title: String,
@SerializedName("coverImage") val coverImage: String,
@SerializedName("publishedDaysOfWeek") val publishedDaysOfWeek: String,
@SerializedName("isComplete") val isComplete: Boolean,
@SerializedName("creator") val creator: SeriesListItemCreator,
@SerializedName("numberOfContent") val numberOfContent: Int,
@SerializedName("isNew") val isNew: Boolean,
@SerializedName("isPopular") val isPopular: Boolean
)
@Keep
data class SeriesListItemCreator(
@SerializedName("creatorId") val creatorId: Long,
@SerializedName("nickname") val nickname: String,
@SerializedName("profileImage") val profileImage: String
)
}

View File

@ -1,44 +0,0 @@
package kr.co.vividnext.sodalive.audio_content.series
import io.reactivex.rxjava3.core.Single
import kr.co.vividnext.sodalive.audio_content.series.detail.GetSeriesContentListResponse
import kr.co.vividnext.sodalive.audio_content.series.detail.GetSeriesDetailResponse
import kr.co.vividnext.sodalive.common.ApiResponse
import kr.co.vividnext.sodalive.settings.ContentType
import retrofit2.http.GET
import retrofit2.http.Header
import retrofit2.http.Path
import retrofit2.http.Query
interface SeriesApi {
@GET("/audio-content/series")
fun getSeriesList(
@Query("creatorId") creatorId: Long,
@Query("sortType") sortType: SeriesListAllViewModel.SeriesSortType,
@Query("page") page: Int,
@Query("size") size: Int,
@Header("Authorization") authHeader: String
): Single<ApiResponse<GetSeriesListResponse>>
@GET("/audio-content/series/{id}")
fun getSeriesDetail(
@Path("id") seriesId: Long,
@Header("Authorization") authHeader: String
): Single<ApiResponse<GetSeriesDetailResponse>>
@GET("/audio-content/series/{id}/content")
fun getSeriesContentList(
@Path("id") seriesId: Long,
@Query("page") page: Int,
@Query("size") size: Int,
@Query("sortType") sortType: SeriesListAllViewModel.SeriesSortType,
@Header("Authorization") authHeader: String
): Single<ApiResponse<GetSeriesContentListResponse>>
@GET("/audio-content/series/recommend")
fun getRecommendSeriesList(
@Query("isAdultContentVisible") isAdultContentVisible: Boolean,
@Query("contentType") contentType: ContentType,
@Header("Authorization") authHeader: String
): Single<ApiResponse<List<GetSeriesListResponse.SeriesListItem>>>
}

View File

@ -1,104 +0,0 @@
package kr.co.vividnext.sodalive.audio_content.series
import android.annotation.SuppressLint
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.recyclerview.widget.RecyclerView
import coil.load
import coil.transform.CircleCropTransformation
import coil.transform.RoundedCornersTransformation
import kr.co.vividnext.sodalive.R
import kr.co.vividnext.sodalive.databinding.ItemSeriesListBinding
import kr.co.vividnext.sodalive.extensions.dpToPx
class SeriesListAdapter(
private val itemWidth: Int,
private val onClickItem: (Long) -> Unit,
private val onClickCreator: (Long) -> Unit,
private val isVisibleCreator: Boolean
) : RecyclerView.Adapter<SeriesListAdapter.ViewHolder>() {
val items = mutableListOf<GetSeriesListResponse.SeriesListItem>()
inner class ViewHolder(
private val binding: ItemSeriesListBinding
) : RecyclerView.ViewHolder(binding.root) {
@SuppressLint("SetTextI18n")
fun bind(item: GetSeriesListResponse.SeriesListItem) {
val lp = binding.ivCover.layoutParams as ConstraintLayout.LayoutParams
lp.width = itemWidth
lp.height = itemWidth * 432 / 306
binding.ivCover.layoutParams = lp
binding.ivCover.load(item.coverImage) {
crossfade(true)
placeholder(R.drawable.bg_placeholder)
transformations(RoundedCornersTransformation(5f.dpToPx()))
}
binding.tvTitle.text = item.title
binding.tvSeriesContentCount.text = "${item.numberOfContent}"
binding.tvPublishedDaysOfWeek.text = item.publishedDaysOfWeek
binding.tvNew.visibility = if (item.isNew) {
View.VISIBLE
} else {
View.GONE
}
binding.tvPopular.visibility = if (item.isPopular) {
View.VISIBLE
} else {
View.GONE
}
if (item.isComplete) {
binding.tvNew.visibility = View.GONE
binding.tvComplete.visibility = View.VISIBLE
} else {
binding.tvComplete.visibility = View.GONE
}
if (isVisibleCreator) {
binding.llCreator.visibility = View.VISIBLE
binding.tvCreator.text = item.creator.nickname
binding.ivCreator.load(item.creator.profileImage) {
crossfade(true)
placeholder(R.drawable.ic_place_holder)
transformations(CircleCropTransformation())
}
binding.llCreator.setOnClickListener { onClickCreator(item.creator.creatorId) }
} else {
binding.llCreator.visibility = View.GONE
}
binding.root.setOnClickListener { onClickItem(item.seriesId) }
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = ViewHolder(
ItemSeriesListBinding.inflate(
LayoutInflater.from(parent.context),
parent,
false
)
)
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
holder.bind(items[position])
}
override fun getItemCount() = items.count()
@SuppressLint("NotifyDataSetChanged")
fun addItems(items: List<GetSeriesListResponse.SeriesListItem>) {
this.items.addAll(items)
notifyDataSetChanged()
}
fun clear() {
this.items.clear()
}
}

View File

@ -1,118 +0,0 @@
package kr.co.vividnext.sodalive.audio_content.series
import android.content.Intent
import android.os.Bundle
import android.widget.Toast
import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import kr.co.vividnext.sodalive.audio_content.series.detail.SeriesDetailActivity
import kr.co.vividnext.sodalive.base.BaseActivity
import kr.co.vividnext.sodalive.common.Constants
import kr.co.vividnext.sodalive.common.DifferentSpacingItemDecoration
import kr.co.vividnext.sodalive.common.LoadingDialog
import kr.co.vividnext.sodalive.databinding.ActivitySeriesListAllBinding
import org.koin.android.ext.android.inject
import kotlin.math.roundToInt
class SeriesListAllActivity : BaseActivity<ActivitySeriesListAllBinding>(
ActivitySeriesListAllBinding::inflate
) {
private val viewModel: SeriesListAllViewModel by inject()
private lateinit var loadingDialog: LoadingDialog
private lateinit var seriesAdapter: SeriesListAdapter
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val creatorId = intent.getLongExtra(Constants.EXTRA_USER_ID, 0)
if (creatorId <= 0) {
Toast.makeText(applicationContext, "잘못된 요청입니다.", Toast.LENGTH_LONG).show()
finish()
}
bindData()
viewModel.creatorId = creatorId
viewModel.getSeriesList()
}
override fun setupView() {
loadingDialog = LoadingDialog(this, layoutInflater)
binding.toolbar.tvBack.text = "시리즈 전체보기"
binding.toolbar.tvBack.setOnClickListener { finish() }
seriesAdapter = SeriesListAdapter(
itemWidth = ((screenWidth - (13.3 * 3)) / 3).roundToInt(),
onClickItem = {
startActivity(
Intent(applicationContext, SeriesDetailActivity::class.java).apply {
putExtra(Constants.EXTRA_SERIES_ID, it)
}
)
},
onClickCreator = {},
isVisibleCreator = false
)
val spanCount = 3
val horizontalSpacing = 20
val verticalSpacing = 100
val recyclerView = binding.rvSeriesAll
recyclerView.layoutManager = GridLayoutManager(this, spanCount)
recyclerView.addItemDecoration(
DifferentSpacingItemDecoration(
spanCount = spanCount,
horizontalSpacing = horizontalSpacing,
verticalSpacing = verticalSpacing,
includeEdge = true
)
)
recyclerView.addOnScrollListener(object : RecyclerView.OnScrollListener() {
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
super.onScrolled(recyclerView, dx, dy)
val lastVisibleItemPosition = (recyclerView.layoutManager as LinearLayoutManager?)!!
.findLastCompletelyVisibleItemPosition()
val itemTotalCount = recyclerView.adapter!!.itemCount - 1
// 스크롤이 끝에 도달했는지 확인
if (!recyclerView.canScrollVertically(1) &&
lastVisibleItemPosition == itemTotalCount
) {
viewModel.getSeriesList()
}
}
})
recyclerView.adapter = seriesAdapter
}
private fun bindData() {
viewModel.toastLiveData.observe(this) {
it?.let { Toast.makeText(applicationContext, it, Toast.LENGTH_LONG).show() }
}
viewModel.isLoading.observe(this) {
if (it) {
loadingDialog.show(screenWidth, "")
} else {
loadingDialog.dismiss()
}
}
viewModel.seriesListLiveData.observe(this) {
if (viewModel.page - 1 == 1) {
seriesAdapter.clear()
binding.rvSeriesAll.scrollToPosition(0)
}
seriesAdapter.addItems(it)
}
}
}

View File

@ -1,80 +0,0 @@
package kr.co.vividnext.sodalive.audio_content.series
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import com.google.gson.annotations.SerializedName
import com.orhanobut.logger.Logger
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
import io.reactivex.rxjava3.schedulers.Schedulers
import kr.co.vividnext.sodalive.base.BaseViewModel
import kr.co.vividnext.sodalive.common.SharedPreferenceManager
class SeriesListAllViewModel(private val repository: SeriesRepository) : BaseViewModel() {
enum class SeriesSortType {
@SerializedName("NEWEST") NEWEST,
@SerializedName("OLDEST") OLDEST
}
private val _toastLiveData = MutableLiveData<String?>()
val toastLiveData: LiveData<String?>
get() = _toastLiveData
private var _isLoading = MutableLiveData(false)
val isLoading: LiveData<Boolean>
get() = _isLoading
private var _seriesListLiveData = MutableLiveData<List<GetSeriesListResponse.SeriesListItem>>()
val seriesListLiveData: LiveData<List<GetSeriesListResponse.SeriesListItem>>
get() = _seriesListLiveData
var creatorId = 0L
var isLast = false
var page = 1
private val size = 10
fun getSeriesList() {
if (!_isLoading.value!! && !isLast) {
_isLoading.value = true
compositeDisposable.add(
repository.getSeriesList(
creatorId = creatorId,
sortType = SeriesSortType.NEWEST,
page = page,
size = size,
token = "Bearer ${SharedPreferenceManager.token}"
)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
{
if (it.success && it.data != null) {
page += 1
if (it.data.items.isNotEmpty()) {
_seriesListLiveData.value = it.data.items
} else {
isLast = true
}
} else {
if (it.message != null) {
_toastLiveData.value = it.message
} else {
_toastLiveData.value = "알 수 없는 오류가 발생했습니다. 다시 시도해 주세요."
}
}
_isLoading.value = false
},
{
_isLoading.value = false
it.message?.let { message -> Logger.e(message) }
_toastLiveData.postValue("알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.")
}
)
)
}
}
}

View File

@ -1,45 +0,0 @@
package kr.co.vividnext.sodalive.audio_content.series
import kr.co.vividnext.sodalive.common.SharedPreferenceManager
import kr.co.vividnext.sodalive.settings.ContentType
class SeriesRepository(private val api: SeriesApi) {
fun getSeriesList(
creatorId: Long,
sortType: SeriesListAllViewModel.SeriesSortType,
page: Int,
size: Int,
token: String
) = api.getSeriesList(
creatorId = creatorId,
sortType = sortType,
page = page - 1,
size = size,
authHeader = token
)
fun getSeriesDetail(seriesId: Long, token: String) = api.getSeriesDetail(
seriesId = seriesId,
authHeader = token
)
fun getSeriesContentList(
seriesId: Long,
page: Int,
size: Int,
sortType: SeriesListAllViewModel.SeriesSortType,
token: String
) = api.getSeriesContentList(
seriesId = seriesId,
page = page - 1,
size = size,
sortType = sortType,
authHeader = token
)
fun getRecommendSeriesList(token: String) = api.getRecommendSeriesList(
isAdultContentVisible = SharedPreferenceManager.isAdultContentVisible,
contentType = ContentType.values()[SharedPreferenceManager.contentPreference],
authHeader = token
)
}

View File

@ -1,77 +0,0 @@
package kr.co.vividnext.sodalive.audio_content.series.content
import android.annotation.SuppressLint
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import coil.load
import coil.transform.RoundedCornersTransformation
import kr.co.vividnext.sodalive.R
import kr.co.vividnext.sodalive.audio_content.series.detail.GetSeriesContentListItem
import kr.co.vividnext.sodalive.databinding.ItemSeriesContentBinding
import kr.co.vividnext.sodalive.extensions.dpToPx
class SeriesContentAdapter(
private val onClickItem: (Long) -> Unit
) : RecyclerView.Adapter<SeriesContentAdapter.ViewHolder>() {
val items = mutableListOf<GetSeriesContentListItem>()
inner class ViewHolder(
private val binding: ItemSeriesContentBinding
) : RecyclerView.ViewHolder(binding.root) {
fun bind(item: GetSeriesContentListItem) {
binding.ivCover.load(item.coverImage) {
crossfade(true)
placeholder(R.drawable.bg_placeholder)
transformations(RoundedCornersTransformation(5.3f.dpToPx()))
}
binding.tvTitle.text = item.title
binding.tvDuration.text = item.duration
binding.tvPrice.visibility = View.GONE
binding.tvOwned.visibility = View.GONE
binding.tvRented.visibility = View.GONE
binding.tvPriceFree.visibility = View.GONE
if (item.isOwned) {
binding.tvOwned.visibility = View.VISIBLE
} else if (item.isRented) {
binding.tvRented.visibility = View.VISIBLE
} else if (item.price > 0) {
binding.tvPrice.text = "${item.price}"
binding.tvPrice.visibility = View.VISIBLE
} else {
binding.tvPriceFree.visibility = View.VISIBLE
}
binding.root.setOnClickListener { onClickItem(item.contentId) }
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = ViewHolder(
ItemSeriesContentBinding.inflate(
LayoutInflater.from(parent.context),
parent,
false
)
)
override fun getItemCount() = items.size
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
holder.bind(items[position])
}
@SuppressLint("NotifyDataSetChanged")
fun addItems(items: List<GetSeriesContentListItem>) {
this.items.addAll(items)
notifyDataSetChanged()
}
fun clear() {
this.items.clear()
}
}

View File

@ -1,157 +0,0 @@
package kr.co.vividnext.sodalive.audio_content.series.content
import android.content.Intent
import android.os.Bundle
import android.widget.TextView
import android.widget.Toast
import androidx.core.content.ContextCompat
import androidx.recyclerview.widget.DividerItemDecoration
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import kr.co.vividnext.sodalive.R
import kr.co.vividnext.sodalive.audio_content.detail.AudioContentDetailActivity
import kr.co.vividnext.sodalive.audio_content.series.SeriesListAllViewModel
import kr.co.vividnext.sodalive.base.BaseActivity
import kr.co.vividnext.sodalive.common.Constants
import kr.co.vividnext.sodalive.common.LoadingDialog
import kr.co.vividnext.sodalive.databinding.ActivitySeriesContentAllBinding
import org.koin.android.ext.android.inject
class SeriesContentAllActivity : BaseActivity<ActivitySeriesContentAllBinding>(
ActivitySeriesContentAllBinding::inflate
) {
private val viewModel: SeriesContentAllViewModel by inject()
private lateinit var loadingDialog: LoadingDialog
private lateinit var adapter: SeriesContentAdapter
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val seriesId = intent.getLongExtra(Constants.EXTRA_SERIES_ID, 0)
if (seriesId <= 0) {
Toast.makeText(applicationContext, "잘못된 요청입니다.", Toast.LENGTH_LONG).show()
finish()
}
bindData()
viewModel.seriesId = seriesId
viewModel.getSeriesContentList()
}
override fun setupView() {
loadingDialog = LoadingDialog(this, layoutInflater)
val seriesTitle = intent.getStringExtra(Constants.EXTRA_SERIES_TITLE) ?: ""
binding.toolbar.tvBack.text = if (seriesTitle.isNotBlank()) {
"$seriesTitle - 전체회차 듣기"
} else {
" 전체회차 듣기"
}
binding.toolbar.tvBack.setOnClickListener { finish() }
adapter = SeriesContentAdapter {
startActivity(
Intent(applicationContext, AudioContentDetailActivity::class.java).apply {
putExtra(Constants.EXTRA_AUDIO_CONTENT_ID, it)
}
)
}
binding.rvSeriesContentAll.layoutManager = LinearLayoutManager(
applicationContext,
LinearLayoutManager.VERTICAL,
false
)
binding.rvSeriesContentAll.addItemDecoration(
DividerItemDecoration(
applicationContext,
DividerItemDecoration.VERTICAL
)
)
binding.rvSeriesContentAll.addOnScrollListener(object : RecyclerView.OnScrollListener() {
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
super.onScrolled(recyclerView, dx, dy)
val lastVisibleItemPosition = (recyclerView.layoutManager as LinearLayoutManager?)!!
.findLastCompletelyVisibleItemPosition()
val itemTotalCount = recyclerView.adapter!!.itemCount - 1
// 스크롤이 끝에 도달했는지 확인
if (!recyclerView.canScrollVertically(1) &&
lastVisibleItemPosition == itemTotalCount
) {
viewModel.getSeriesContentList()
}
}
})
binding.rvSeriesContentAll.adapter = adapter
binding.tvSortNewest.setOnClickListener {
viewModel.changeSort(SeriesListAllViewModel.SeriesSortType.NEWEST)
}
binding.tvSortOldest.setOnClickListener {
viewModel.changeSort(SeriesListAllViewModel.SeriesSortType.OLDEST)
}
}
fun bindData() {
viewModel.toastLiveData.observe(this) {
it?.let { Toast.makeText(applicationContext, it, Toast.LENGTH_LONG).show() }
}
viewModel.isLoading.observe(this) {
if (it) {
loadingDialog.show(screenWidth, "")
} else {
loadingDialog.dismiss()
}
}
viewModel.seriesContentListLiveData.observe(this) {
adapter.addItems(it)
}
viewModel.sortLiveData.observe(this) {
deselectSort()
selectSort(
when (it) {
SeriesListAllViewModel.SeriesSortType.OLDEST -> {
binding.tvSortOldest
}
else -> {
binding.tvSortNewest
}
}
)
}
}
private fun deselectSort() {
val color = ContextCompat.getColor(
applicationContext,
R.color.color_88e2e2e2
)
binding.tvSortNewest.setTextColor(color)
binding.tvSortOldest.setTextColor(color)
}
private fun selectSort(view: TextView) {
view.setTextColor(
ContextCompat.getColor(
applicationContext,
R.color.color_e2e2e2
)
)
adapter.clear()
viewModel.getSeriesContentList()
}
}

View File

@ -1,87 +0,0 @@
package kr.co.vividnext.sodalive.audio_content.series.content
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import com.orhanobut.logger.Logger
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
import io.reactivex.rxjava3.schedulers.Schedulers
import kr.co.vividnext.sodalive.audio_content.series.SeriesListAllViewModel
import kr.co.vividnext.sodalive.audio_content.series.SeriesRepository
import kr.co.vividnext.sodalive.audio_content.series.detail.GetSeriesContentListItem
import kr.co.vividnext.sodalive.base.BaseViewModel
import kr.co.vividnext.sodalive.common.SharedPreferenceManager
class SeriesContentAllViewModel(private val repository: SeriesRepository) : BaseViewModel() {
private val _toastLiveData = MutableLiveData<String?>()
val toastLiveData: LiveData<String?>
get() = _toastLiveData
private var _isLoading = MutableLiveData(false)
val isLoading: LiveData<Boolean>
get() = _isLoading
private var _seriesContentListLiveData = MutableLiveData<List<GetSeriesContentListItem>>()
val seriesContentListLiveData: LiveData<List<GetSeriesContentListItem>>
get() = _seriesContentListLiveData
private var _sortLiveData = MutableLiveData(SeriesListAllViewModel.SeriesSortType.NEWEST)
val sortLiveData: LiveData<SeriesListAllViewModel.SeriesSortType>
get() = _sortLiveData
var seriesId = 0L
var page = 1
private var pageSize = 10
private var isLast = false
fun getSeriesContentList() {
if (!_isLoading.value!! && !isLast) {
_isLoading.value = true
compositeDisposable.add(
repository.getSeriesContentList(
seriesId = seriesId,
page = page,
size = pageSize,
sortType = _sortLiveData.value!!,
token = "Bearer ${SharedPreferenceManager.token}"
)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
{
if (it.success && it.data != null) {
page += 1
if (it.data.items.isNotEmpty()) {
_seriesContentListLiveData.value = it.data.items
} else {
isLast = true
}
} else {
if (it.message != null) {
_toastLiveData.value = it.message
} else {
_toastLiveData.value = "알 수 없는 오류가 발생했습니다. 다시 시도해 주세요."
}
}
_isLoading.value = false
},
{
_isLoading.value = false
it.message?.let { message -> Logger.e(message) }
_toastLiveData.postValue("알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.")
}
)
)
}
}
fun changeSort(sortType: SeriesListAllViewModel.SeriesSortType) {
if (_sortLiveData.value != sortType) {
page = 1
isLast = false
_sortLiveData.value = sortType
}
}
}

View File

@ -1,25 +0,0 @@
package kr.co.vividnext.sodalive.audio_content.series.detail
import android.os.Parcelable
import androidx.annotation.Keep
import com.google.gson.annotations.SerializedName
import kotlinx.parcelize.Parcelize
@Keep
data class GetSeriesContentListResponse(
@SerializedName("totalCount") val totalCount: Int,
@SerializedName("items") val items: List<GetSeriesContentListItem>
)
@Parcelize
@Keep
data class GetSeriesContentListItem(
@SerializedName("contentId") val contentId: Long,
@SerializedName("title") val title: String,
@SerializedName("coverImage") val coverImage: String,
@SerializedName("releaseDate") val releaseDate: String,
@SerializedName("duration") val duration: String,
@SerializedName("price") val price: Int,
@SerializedName("isRented") var isRented: Boolean,
@SerializedName("isOwned") var isOwned: Boolean
) : Parcelable

View File

@ -1,40 +0,0 @@
package kr.co.vividnext.sodalive.audio_content.series.detail
import android.os.Parcelable
import androidx.annotation.Keep
import com.google.gson.annotations.SerializedName
import kotlinx.parcelize.Parcelize
@Parcelize
@Keep
data class GetSeriesDetailResponse(
@SerializedName("seriesId") val seriesId: Long,
@SerializedName("title") val title: String,
@SerializedName("coverImage") val coverImage: String,
@SerializedName("introduction") val introduction: String,
@SerializedName("genre") val genre: String,
@SerializedName("isAdult") val isAdult: Boolean,
@SerializedName("writer") val writer: String?,
@SerializedName("studio") val studio: String?,
@SerializedName("publishedDate") val publishedDate: String,
@SerializedName("creator") val creator: GetSeriesDetailCreator,
@SerializedName("rentalMinPrice") var rentalMinPrice: Int,
@SerializedName("rentalMaxPrice") var rentalMaxPrice: Int,
@SerializedName("rentalPeriod") val rentalPeriod: Int,
@SerializedName("minPrice") var minPrice: Int,
@SerializedName("maxPrice") var maxPrice: Int,
@SerializedName("keywordList") var keywordList: List<String>,
@SerializedName("publishedDaysOfWeek") var publishedDaysOfWeek: String,
@SerializedName("contentList") val contentList: List<GetSeriesContentListItem>,
@SerializedName("contentCount") val contentCount: Int
) : Parcelable {
@Parcelize
@Keep
data class GetSeriesDetailCreator(
@SerializedName("creatorId") val creatorId: Long,
@SerializedName("nickname") val nickname: String,
@SerializedName("profileImage") val profileImage: String,
@SerializedName("isFollow") var isFollow: Boolean,
@SerializedName("isNotify") var isNotify: Boolean
) : Parcelable
}

View File

@ -1,252 +0,0 @@
package kr.co.vividnext.sodalive.audio_content.series.detail
import android.annotation.SuppressLint
import android.os.Bundle
import android.view.View
import android.widget.Toast
import androidx.core.content.ContextCompat
import androidx.core.content.res.ResourcesCompat
import coil.load
import coil.size.Scale
import coil.transform.BlurTransformation
import coil.transform.CircleCropTransformation
import coil.transform.RoundedCornersTransformation
import com.google.android.material.chip.Chip
import com.google.android.material.shape.ShapeAppearanceModel
import com.google.android.material.tabs.TabLayout
import kr.co.vividnext.sodalive.R
import kr.co.vividnext.sodalive.base.BaseActivity
import kr.co.vividnext.sodalive.common.Constants
import kr.co.vividnext.sodalive.common.LoadingDialog
import kr.co.vividnext.sodalive.common.SharedPreferenceManager
import kr.co.vividnext.sodalive.databinding.ActivitySeriesDetailBinding
import kr.co.vividnext.sodalive.explorer.profile.CreatorFollowNotifyFragment
import kr.co.vividnext.sodalive.extensions.dpToPx
import org.koin.android.ext.android.inject
class SeriesDetailActivity : BaseActivity<ActivitySeriesDetailBinding>(
ActivitySeriesDetailBinding::inflate
) {
private val viewModel: SeriesDetailViewModel by inject()
private lateinit var loadingDialog: LoadingDialog
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val seriesId = intent.getLongExtra(Constants.EXTRA_SERIES_ID, 0)
if (seriesId <= 0) {
Toast.makeText(applicationContext, "잘못된 요청입니다.", Toast.LENGTH_LONG).show()
finish()
}
bindData()
viewModel.seriesId = seriesId
viewModel.getSeriesDetail()
}
override fun setupView() {
loadingDialog = LoadingDialog(this, layoutInflater)
binding.ivBack.setOnClickListener { finish() }
setupTab()
}
private fun setupTab() {
val tabs = binding.tabs
tabs.addTab(tabs.newTab().setText("").setTag("home"))
tabs.addTab(tabs.newTab().setText("작품소개").setTag("introduction"))
tabs.addOnTabSelectedListener(object : TabLayout.OnTabSelectedListener {
override fun onTabSelected(tab: TabLayout.Tab) {
val tag = tab.tag as String
changeFragment(tag)
}
override fun onTabUnselected(tab: TabLayout.Tab) {
}
override fun onTabReselected(tab: TabLayout.Tab) {
}
})
}
private fun changeFragment(tag: String) {
val fragmentManager = supportFragmentManager
val fragmentTransaction = fragmentManager.beginTransaction()
val fragment = when (tag) {
"introduction" -> SeriesDetailIntroductionFragment()
else -> SeriesDetailHomeFragment()
}
val bundle = Bundle()
bundle.putParcelable(Constants.EXTRA_SERIES, viewModel.seriesDetailResponse)
fragment.arguments = bundle
fragmentTransaction.replace(R.id.container, fragment, tag)
fragmentTransaction.setPrimaryNavigationFragment(fragment)
fragmentTransaction.setReorderingAllowed(true)
fragmentTransaction.commitNow()
}
private fun bindData() {
viewModel.toastLiveData.observe(this) {
it?.let { Toast.makeText(applicationContext, it, Toast.LENGTH_LONG).show() }
}
viewModel.isLoading.observe(this) {
if (it) {
loadingDialog.show(screenWidth, "")
} else {
loadingDialog.dismiss()
}
}
viewModel.seriesDetailLiveData.observe(this) {
setSeriesBg(it.coverImage)
setSeriesInfo(it)
setSeriesCreator(it.creator)
setSeriesKeywordChipList(it.keywordList)
changeFragment("home")
}
}
private fun setSeriesKeywordChipList(keywordList: List<String>) {
binding.chipGroup.isSingleLine = true
binding.chipGroup.isHorizontalScrollBarEnabled = false
for (keyword in keywordList) {
val chip = Chip(this)
chip.text = keyword
chip.isClickable = false
chip.isCheckable = false
chip.textSize = 12f
chip.chipStrokeWidth = 0f
chip.setChipBackgroundColorResource(R.color.color_222222)
val shapeAppearanceModel = ShapeAppearanceModel.Builder()
.setAllCornerSizes(26.7f.dpToPx())
.build()
chip.shapeAppearanceModel = shapeAppearanceModel
chip.setPadding(0, 0, 0, 0)
chip.setTextColor(ContextCompat.getColor(applicationContext, R.color.color_d2d2d2))
chip.typeface = ResourcesCompat.getFont(applicationContext, R.font.gmarket_sans_medium)
binding.chipGroup.addView(chip)
}
}
@SuppressLint("SetTextI18n")
private fun setSeriesInfo(seriesDetail: GetSeriesDetailResponse) {
binding.ivCover.load(seriesDetail.coverImage) {
crossfade(true)
placeholder(R.drawable.bg_placeholder)
transformations(RoundedCornersTransformation(5f.dpToPx()))
}
binding.tvTitle.text = seriesDetail.title
binding.tvGenre.text = seriesDetail.genre
binding.tvPublishedDaysOfWeek.text = "${seriesDetail.publishedDaysOfWeek} 연재"
if (seriesDetail.isAdult) {
binding.tvAge19.visibility = View.VISIBLE
binding.tvAgeAll.visibility = View.GONE
} else {
binding.tvAge19.visibility = View.GONE
binding.tvAgeAll.visibility = View.VISIBLE
}
}
private fun setSeriesCreator(creator: GetSeriesDetailResponse.GetSeriesDetailCreator) {
binding.tvNickname.text = creator.nickname
binding.ivProfile.load(creator.profileImage) {
crossfade(true)
placeholder(R.drawable.bg_placeholder)
transformations(CircleCropTransformation())
}
if (SharedPreferenceManager.userId != creator.creatorId) {
binding.ivFollow.visibility = View.VISIBLE
if (creator.isFollow) {
binding.ivFollow.setImageResource(
if (creator.isNotify) {
R.drawable.btn_following_big
} else {
R.drawable.btn_following_no_alarm_big
}
)
} else {
binding.ivFollow.setImageResource(R.drawable.btn_follow_big)
}
} else {
binding.ivFollow.visibility = View.GONE
}
binding.ivFollow.setOnClickListener {
if (creator.isFollow) {
val notifyFragment = CreatorFollowNotifyFragment(
onClickNotifyAll = {
viewModel.follow(
creatorId = creator.creatorId,
follow = true,
notify = true
) {
creator.isFollow = true
creator.isNotify = true
binding.ivFollow.setImageResource(R.drawable.btn_following_big)
}
},
onClickNotifyNone = {
viewModel.follow(
creatorId = creator.creatorId,
follow = true,
notify = false
) {
creator.isFollow = true
creator.isNotify = false
binding.ivFollow.setImageResource(R.drawable.btn_following_no_alarm_big)
}
},
onClickUnFollow = {
viewModel.follow(
creatorId = creator.creatorId,
follow = false,
notify = false
) {
creator.isFollow = false
creator.isNotify = false
binding.ivFollow.setImageResource(R.drawable.btn_follow_big)
}
}
)
if (notifyFragment.isAdded) return@setOnClickListener
notifyFragment.show(supportFragmentManager, notifyFragment.tag)
} else {
viewModel.follow(creatorId = creator.creatorId) {
creator.isFollow = true
creator.isNotify = true
binding.ivFollow.setImageResource(R.drawable.btn_following_big)
}
}
}
}
private fun setSeriesBg(coverImage: String) {
binding.ivBg.load(coverImage) {
transformations(
BlurTransformation(
this@SeriesDetailActivity,
25f,
2.5f
)
)
scale(Scale.FILL)
}
}
}

View File

@ -1,83 +0,0 @@
package kr.co.vividnext.sodalive.audio_content.series.detail
import android.annotation.SuppressLint
import android.content.Intent
import android.os.Build
import android.os.Bundle
import android.view.View
import androidx.recyclerview.widget.DividerItemDecoration
import androidx.recyclerview.widget.LinearLayoutManager
import kr.co.vividnext.sodalive.audio_content.detail.AudioContentDetailActivity
import kr.co.vividnext.sodalive.audio_content.series.content.SeriesContentAdapter
import kr.co.vividnext.sodalive.audio_content.series.content.SeriesContentAllActivity
import kr.co.vividnext.sodalive.base.BaseFragment
import kr.co.vividnext.sodalive.common.Constants
import kr.co.vividnext.sodalive.databinding.FragmentSeriesDetailHomeBinding
class SeriesDetailHomeFragment : BaseFragment<FragmentSeriesDetailHomeBinding>(
FragmentSeriesDetailHomeBinding::inflate
) {
private var seriesDetailResponse: GetSeriesDetailResponse? = null
private lateinit var adapter: SeriesContentAdapter
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
if (arguments != null) {
seriesDetailResponse = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
requireArguments().getParcelable(
Constants.EXTRA_SERIES,
GetSeriesDetailResponse::class.java
)
} else {
requireArguments().getParcelable(Constants.EXTRA_SERIES)
}
}
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
if (seriesDetailResponse != null) {
setContent()
}
}
@SuppressLint("SetTextI18n")
private fun setContent() {
binding.tvTotalCount.text = "(${seriesDetailResponse!!.contentCount})"
binding.llContentAll.setOnClickListener {
startActivity(
Intent(requireActivity(), SeriesContentAllActivity::class.java).apply {
putExtra(Constants.EXTRA_SERIES_ID, seriesDetailResponse!!.seriesId)
putExtra(Constants.EXTRA_SERIES_TITLE, seriesDetailResponse!!.title)
}
)
}
adapter = SeriesContentAdapter {
startActivity(
Intent(requireActivity(), AudioContentDetailActivity::class.java).apply {
putExtra(Constants.EXTRA_AUDIO_CONTENT_ID, it)
}
)
}
binding.rvContent.layoutManager = LinearLayoutManager(
requireContext(),
LinearLayoutManager.VERTICAL,
false
)
binding.rvContent.addItemDecoration(
DividerItemDecoration(
requireContext(),
DividerItemDecoration.VERTICAL
)
)
binding.rvContent.adapter = adapter
adapter.addItems(seriesDetailResponse!!.contentList)
}
}

View File

@ -1,142 +0,0 @@
package kr.co.vividnext.sodalive.audio_content.series.detail
import android.annotation.SuppressLint
import android.os.Build
import android.os.Bundle
import android.view.View
import androidx.core.content.ContextCompat
import androidx.core.content.res.ResourcesCompat
import com.google.android.material.chip.Chip
import com.google.android.material.chip.ChipGroup
import com.google.android.material.shape.ShapeAppearanceModel
import kr.co.vividnext.sodalive.R
import kr.co.vividnext.sodalive.base.BaseFragment
import kr.co.vividnext.sodalive.common.Constants
import kr.co.vividnext.sodalive.databinding.FragmentSeriesDetailIntroductionBinding
import kr.co.vividnext.sodalive.extensions.dpToPx
class SeriesDetailIntroductionFragment : BaseFragment<FragmentSeriesDetailIntroductionBinding>(
FragmentSeriesDetailIntroductionBinding::inflate
) {
private var seriesDetailResponse: GetSeriesDetailResponse? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
if (arguments != null) {
seriesDetailResponse = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
requireArguments().getParcelable(
Constants.EXTRA_SERIES,
GetSeriesDetailResponse::class.java
)
} else {
requireArguments().getParcelable(Constants.EXTRA_SERIES)
}
}
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
if (seriesDetailResponse != null) {
setSeriesKeywordChipList(seriesDetailResponse!!.keywordList)
setSeriesIntroduction(seriesDetailResponse!!.introduction)
setSeriesPrice()
setSeriesInfo()
}
}
private fun setSeriesPrice() {
val rentalMinPrice = seriesDetailResponse!!.rentalMinPrice
val rentalMaxPrice = seriesDetailResponse!!.rentalMaxPrice
val minPrice = seriesDetailResponse!!.minPrice
val maxPrice = seriesDetailResponse!!.maxPrice
binding.tvRentalPrice.text = if (rentalMinPrice == rentalMaxPrice) {
if (rentalMaxPrice == 0) {
"무료(15일)"
} else {
"$rentalMaxPrice(15일)"
}
} else {
"${if (rentalMinPrice == 0) "무료" else rentalMinPrice} ~ ${rentalMaxPrice}캔 (15일)"
}
binding.tvPrice.text = if (minPrice == maxPrice) {
if (maxPrice == 0) {
"무료"
} else {
"$maxPrice"
}
} else {
"${if (minPrice == 0) "무료" else minPrice} ~ ${maxPrice}"
}
}
@SuppressLint("SetTextI18n")
private fun setSeriesInfo() {
binding.tvGenre.text = seriesDetailResponse!!.genre
binding.tvIsAdult.text = if (seriesDetailResponse!!.isAdult) {
"19세 이상"
} else {
"전체연령가"
}
binding.tvPublishedDate.text = seriesDetailResponse!!.publishedDate
binding.tvPublishedDaysOfWeek.text =
if (seriesDetailResponse!!.publishedDaysOfWeek == "랜덤") {
seriesDetailResponse!!.publishedDaysOfWeek
} else {
seriesDetailResponse!!.publishedDaysOfWeek
}
if (seriesDetailResponse!!.writer != null) {
binding.tvWriter.visibility = View.VISIBLE
binding.tvWriterLabel.visibility = View.VISIBLE
binding.tvWriter.text = seriesDetailResponse!!.writer
} else {
binding.tvWriter.visibility = View.GONE
binding.tvWriterLabel.visibility = View.GONE
}
if (seriesDetailResponse!!.studio != null) {
binding.tvStudio.visibility = View.VISIBLE
binding.tvStudioLabel.visibility = View.VISIBLE
binding.tvStudio.text = seriesDetailResponse!!.studio
} else {
binding.tvStudio.visibility = View.GONE
binding.tvStudioLabel.visibility = View.GONE
}
}
private fun setSeriesIntroduction(introduction: String) {
binding.tvIntroduce.text = introduction
}
private fun setSeriesKeywordChipList(keywordList: List<String>) {
binding.chipGroup.isHorizontalScrollBarEnabled = false
for (keyword in keywordList) {
val chip = Chip(requireActivity())
chip.text = keyword
chip.isClickable = false
chip.isCheckable = false
chip.textSize = 12f
chip.chipStrokeWidth = 0f
chip.setChipBackgroundColorResource(R.color.color_222222)
val shapeAppearanceModel = ShapeAppearanceModel.Builder()
.setAllCornerSizes(26.7f.dpToPx())
.build()
chip.shapeAppearanceModel = shapeAppearanceModel
chip.setEnsureMinTouchTargetSize(false)
chip.setPadding(0, 0, 0, 0)
chip.setTextColor(ContextCompat.getColor(requireContext(), R.color.color_d2d2d2))
chip.typeface = ResourcesCompat.getFont(requireContext(), R.font.gmarket_sans_medium)
binding.chipGroup.addView(chip)
}
}
}

View File

@ -1,105 +0,0 @@
package kr.co.vividnext.sodalive.audio_content.series.detail
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import com.orhanobut.logger.Logger
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
import io.reactivex.rxjava3.schedulers.Schedulers
import kr.co.vividnext.sodalive.audio_content.series.SeriesRepository
import kr.co.vividnext.sodalive.base.BaseViewModel
import kr.co.vividnext.sodalive.common.SharedPreferenceManager
import kr.co.vividnext.sodalive.user.UserRepository
class SeriesDetailViewModel(
private val repository: SeriesRepository,
private val userRepository: UserRepository
) : BaseViewModel() {
private val _toastLiveData = MutableLiveData<String?>()
val toastLiveData: LiveData<String?>
get() = _toastLiveData
private var _isLoading = MutableLiveData(false)
val isLoading: LiveData<Boolean>
get() = _isLoading
private var _seriesDetailLiveData = MutableLiveData<GetSeriesDetailResponse>()
val seriesDetailLiveData: LiveData<GetSeriesDetailResponse>
get() = _seriesDetailLiveData
var seriesId = 0L
lateinit var seriesDetailResponse: GetSeriesDetailResponse
fun getSeriesDetail() {
_isLoading.value = true
compositeDisposable.add(
repository.getSeriesDetail(
seriesId = seriesId,
token = "Bearer ${SharedPreferenceManager.token}"
)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
{
if (it.success && it.data != null) {
seriesDetailResponse = it.data
_seriesDetailLiveData.value = seriesDetailResponse
} else {
if (it.message != null) {
_toastLiveData.value = it.message
} else {
_toastLiveData.value = "알 수 없는 오류가 발생했습니다. 다시 시도해 주세요."
}
}
_isLoading.value = false
},
{
_isLoading.value = false
it.message?.let { message -> Logger.e(message) }
_toastLiveData.postValue("알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.")
}
)
)
}
fun follow(
creatorId: Long,
follow: Boolean = true,
notify: Boolean = true,
onSuccess: () -> Unit
) {
_isLoading.value = true
compositeDisposable.add(
userRepository.creatorFollow(
creatorId = creatorId,
follow,
notify,
token = "Bearer ${SharedPreferenceManager.token}"
)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
{
if (it.success && it.data != null) {
onSuccess()
} else {
if (it.message != null) {
_toastLiveData.postValue(it.message)
} else {
_toastLiveData.postValue(
"알 수 없는 오류가 발생했습니다. 다시 시도해 주세요."
)
}
}
_isLoading.value = false
},
{
_isLoading.value = false
it.message?.let { message -> Logger.e(message) }
_toastLiveData.postValue("알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.")
}
)
)
}
}

View File

@ -2,8 +2,6 @@ package kr.co.vividnext.sodalive.audio_content.upload
import android.Manifest import android.Manifest
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.app.DatePickerDialog
import android.app.TimePickerDialog
import android.content.Intent import android.content.Intent
import android.net.Uri import android.net.Uri
import android.os.Build import android.os.Build
@ -30,15 +28,9 @@ import kr.co.vividnext.sodalive.common.RealPathUtil
import kr.co.vividnext.sodalive.common.SharedPreferenceManager import kr.co.vividnext.sodalive.common.SharedPreferenceManager
import kr.co.vividnext.sodalive.databinding.ActivityAudioContentUploadBinding import kr.co.vividnext.sodalive.databinding.ActivityAudioContentUploadBinding
import kr.co.vividnext.sodalive.dialog.LiveDialog import kr.co.vividnext.sodalive.dialog.LiveDialog
import kr.co.vividnext.sodalive.dialog.SodaLiveTimePickerDialog
import kr.co.vividnext.sodalive.extensions.convertDateFormat
import kr.co.vividnext.sodalive.extensions.dpToPx import kr.co.vividnext.sodalive.extensions.dpToPx
import org.koin.android.ext.android.inject import org.koin.android.ext.android.inject
import java.io.File import java.io.File
import java.text.SimpleDateFormat
import java.util.Calendar
import java.util.Date
import java.util.Locale
class AudioContentUploadActivity : BaseActivity<ActivityAudioContentUploadBinding>( class AudioContentUploadActivity : BaseActivity<ActivityAudioContentUploadBinding>(
ActivityAudioContentUploadBinding::inflate ActivityAudioContentUploadBinding::inflate
@ -119,26 +111,6 @@ class AudioContentUploadActivity : BaseActivity<ActivityAudioContentUploadBindin
} }
} }
private val datePickerDialogListener =
DatePickerDialog.OnDateSetListener { _, year, monthOfYear, dayOfMonth ->
viewModel.releaseDate = String.format("%d-%02d-%02d", year, monthOfYear + 1, dayOfMonth)
viewModel.setReservationDate(
String.format(
"%d.%02d.%02d",
year,
monthOfYear + 1,
dayOfMonth
)
)
}
private val timePickerDialogListener =
TimePickerDialog.OnTimeSetListener { _, hourOfDay, minute ->
val timeString = String.format("%02d:%02d", hourOfDay, minute)
viewModel.releaseTime = timeString
viewModel.setReservationTime(timeString.convertDateFormat("HH:mm", "a hh:mm"))
}
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
checkPermissions() checkPermissions()
@ -153,11 +125,6 @@ class AudioContentUploadActivity : BaseActivity<ActivityAudioContentUploadBindin
override fun setupView() { override fun setupView() {
loadingDialog = LoadingDialog(this, layoutInflater) loadingDialog = LoadingDialog(this, layoutInflater)
binding.tvServiceDate.text = if (SharedPreferenceManager.userId == 17958L) {
"※ 이용기간 : 대여(15일) | 소장(이용 기간 1년)"
} else {
"※ 이용기간 : 대여(15일) | 소장(서비스 종료시까지)"
}
binding.toolbar.tvBack.text = "콘텐츠 등록" binding.toolbar.tvBack.text = "콘텐츠 등록"
binding.toolbar.tvBack.setOnClickListener { finish() } binding.toolbar.tvBack.setOnClickListener { finish() }
binding.llTheme.setOnClickListener { binding.llTheme.setOnClickListener {
@ -202,16 +169,10 @@ class AudioContentUploadActivity : BaseActivity<ActivityAudioContentUploadBindin
binding.llPricePaid.setOnClickListener { viewModel.setPriceFree(false) } binding.llPricePaid.setOnClickListener { viewModel.setPriceFree(false) }
binding.llPriceFree.setOnClickListener { viewModel.setPriceFree(true) } binding.llPriceFree.setOnClickListener { viewModel.setPriceFree(true) }
binding.llPreviewYes.setOnClickListener { viewModel.setGeneratePreview(true) }
binding.llPreviewNo.setOnClickListener { viewModel.setGeneratePreview(false) }
binding.llLimited.setOnClickListener { viewModel.setLimited(true) }
binding.llNotLimited.setOnClickListener { viewModel.setLimited(false) }
binding.llRentalAndKeep.setOnClickListener { viewModel.setIsOnlyRental(false) } binding.llRentalAndKeep.setOnClickListener { viewModel.setIsOnlyRental(false) }
binding.llOnlyRental.setOnClickListener { viewModel.setIsOnlyRental(true) } binding.llOnlyRental.setOnClickListener { viewModel.setIsOnlyRental(true) }
binding.llCommentNo.setOnClickListener { viewModel.setAvailableComment(false) } binding.llCommentNo.setOnClickListener { viewModel.setAvailableComment(false) }
binding.llCommentYes.setOnClickListener { viewModel.setAvailableComment(true) } binding.llCommentYes.setOnClickListener { viewModel.setAvailableComment(true) }
binding.llActiveNow.setOnClickListener { viewModel.setActiveReservation(false) }
binding.llActiveReservation.setOnClickListener { viewModel.setActiveReservation(true) }
binding.tvCancel.setOnClickListener { finish() } binding.tvCancel.setOnClickListener { finish() }
binding.tvUpload.setOnClickListener { binding.tvUpload.setOnClickListener {
@ -228,76 +189,11 @@ class AudioContentUploadActivity : BaseActivity<ActivityAudioContentUploadBindin
).show(screenWidth) ).show(screenWidth)
} }
} }
binding.tvReservationDate.setOnClickListener {
val reservationDate = viewModel.releaseDate.split("-")
val datePicker: DatePickerDialog
if (reservationDate.isNotEmpty() && reservationDate.size == 3) {
datePicker = DatePickerDialog(
this,
R.style.DatePickerStyle,
datePickerDialogListener,
reservationDate[0].toInt(),
reservationDate[1].toInt() - 1,
reservationDate[2].toInt()
)
} else {
val dateString = SimpleDateFormat(
"yyyy.MM.dd",
Locale.getDefault()
).format(Date()).split(".")
datePicker = DatePickerDialog(
this,
R.style.DatePickerStyle,
datePickerDialogListener,
dateString[0].toInt(),
dateString[1].toInt() - 1,
dateString[2].toInt()
)
}
datePicker.show()
}
binding.tvReservationTime.setOnClickListener {
val reservationTime = viewModel.releaseTime.split(":")
val timePicker: TimePickerDialog
if (reservationTime.isNotEmpty() && reservationTime.size == 2) {
timePicker = SodaLiveTimePickerDialog(
this,
R.style.TimePickerStyle,
timePickerDialogListener,
reservationTime[0].toInt(),
reservationTime[1].toInt(),
false
)
} else {
val calendar = Calendar.getInstance().apply {
add(Calendar.HOUR_OF_DAY, 1)
}
val initialHour = calendar.get(Calendar.HOUR_OF_DAY)
val initialMinute = calendar.get(Calendar.MINUTE) / 15 * 15
timePicker = SodaLiveTimePickerDialog(
this,
R.style.TimePickerStyle,
timePickerDialogListener,
initialHour,
initialMinute,
false
)
}
timePicker.show()
}
} }
private fun checkPermissions() { private fun checkPermissions() {
val permissions = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { val permissions = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
listOf(Manifest.permission.READ_MEDIA_AUDIO) listOf(Manifest.permission.READ_MEDIA_AUDIO, Manifest.permission.READ_MEDIA_IMAGES)
} else { } else {
listOf(Manifest.permission.READ_EXTERNAL_STORAGE) listOf(Manifest.permission.READ_EXTERNAL_STORAGE)
} }
@ -364,24 +260,6 @@ class AudioContentUploadActivity : BaseActivity<ActivityAudioContentUploadBindin
} }
) )
compositeDisposable.add(
binding.etLimited.textChanges().skip(1)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe {
val limited = it.toString().toIntOrNull()
if (limited != null) {
viewModel.limited = limited.toInt()
} else {
viewModel.limited = 0
if (it.isNotBlank()) {
binding.etLimited.setText(it.substring(0, it.length - 1))
binding.etLimited.setSelection(it.length - 1)
}
}
}
)
compositeDisposable.add( compositeDisposable.add(
binding.etPreviewStartTime.textChanges().skip(1) binding.etPreviewStartTime.textChanges().skip(1)
.subscribeOn(Schedulers.io()) .subscribeOn(Schedulers.io())
@ -428,22 +306,6 @@ class AudioContentUploadActivity : BaseActivity<ActivityAudioContentUploadBindin
} }
} }
viewModel.isLimitedLiveData.observe(this) {
if (it) {
checkLimited()
} else {
checkNotLimited()
}
}
viewModel.isGeneratePreviewLiveData.observe(this) {
if (it) {
checkGeneratePreview()
} else {
checkNotGeneratePreview()
}
}
viewModel.isOnlyRentalLiveData.observe(this) { viewModel.isOnlyRentalLiveData.observe(this) {
if (it) { if (it) {
checkOnlyRental() checkOnlyRental()
@ -461,17 +323,17 @@ class AudioContentUploadActivity : BaseActivity<ActivityAudioContentUploadBindin
R.color.color_eeeeee R.color.color_eeeeee
) )
) )
binding.llCommentYes.setBackgroundResource(R.drawable.bg_round_corner_6_7_3bb9f1) binding.llCommentYes.setBackgroundResource(R.drawable.bg_round_corner_6_7_9970ff)
binding.ivCommentNo.visibility = View.GONE binding.ivCommentNo.visibility = View.GONE
binding.tvCommentNo.setTextColor( binding.tvCommentNo.setTextColor(
ContextCompat.getColor( ContextCompat.getColor(
applicationContext, applicationContext,
R.color.color_3bb9f1 R.color.color_9970ff
) )
) )
binding.llCommentNo.setBackgroundResource( binding.llCommentNo.setBackgroundResource(
R.drawable.bg_round_corner_6_7_13181b R.drawable.bg_round_corner_6_7_1f1734_9970ff
) )
} else { } else {
binding.ivCommentNo.visibility = View.VISIBLE binding.ivCommentNo.visibility = View.VISIBLE
@ -481,17 +343,17 @@ class AudioContentUploadActivity : BaseActivity<ActivityAudioContentUploadBindin
R.color.color_eeeeee R.color.color_eeeeee
) )
) )
binding.llCommentNo.setBackgroundResource(R.drawable.bg_round_corner_6_7_3bb9f1) binding.llCommentNo.setBackgroundResource(R.drawable.bg_round_corner_6_7_9970ff)
binding.ivCommentYes.visibility = View.GONE binding.ivCommentYes.visibility = View.GONE
binding.tvCommentYes.setTextColor( binding.tvCommentYes.setTextColor(
ContextCompat.getColor( ContextCompat.getColor(
applicationContext, applicationContext,
R.color.color_3bb9f1 R.color.color_9970ff
) )
) )
binding.llCommentYes binding.llCommentYes
.setBackgroundResource(R.drawable.bg_round_corner_6_7_13181b) .setBackgroundResource(R.drawable.bg_round_corner_6_7_1f1734_9970ff)
} }
} }
@ -507,16 +369,16 @@ class AudioContentUploadActivity : BaseActivity<ActivityAudioContentUploadBindin
viewModel.isAdultLiveData.observe(this) { viewModel.isAdultLiveData.observe(this) {
if (it) { if (it) {
binding.ivAgeAll.visibility = View.GONE binding.ivAgeAll.visibility = View.GONE
binding.llAgeAll.setBackgroundResource(R.drawable.bg_round_corner_6_7_13181b) binding.llAgeAll.setBackgroundResource(R.drawable.bg_round_corner_6_7_1f1734)
binding.tvAgeAll.setTextColor( binding.tvAgeAll.setTextColor(
ContextCompat.getColor( ContextCompat.getColor(
applicationContext, applicationContext,
R.color.color_3bb9f1 R.color.color_9970ff
) )
) )
binding.ivAge19.visibility = View.VISIBLE binding.ivAge19.visibility = View.VISIBLE
binding.llAge19.setBackgroundResource(R.drawable.bg_round_corner_6_7_3bb9f1) binding.llAge19.setBackgroundResource(R.drawable.bg_round_corner_6_7_9970ff)
binding.tvAge19.setTextColor( binding.tvAge19.setTextColor(
ContextCompat.getColor( ContextCompat.getColor(
applicationContext, applicationContext,
@ -525,16 +387,16 @@ class AudioContentUploadActivity : BaseActivity<ActivityAudioContentUploadBindin
) )
} else { } else {
binding.ivAge19.visibility = View.GONE binding.ivAge19.visibility = View.GONE
binding.llAge19.setBackgroundResource(R.drawable.bg_round_corner_6_7_13181b) binding.llAge19.setBackgroundResource(R.drawable.bg_round_corner_6_7_1f1734)
binding.tvAge19.setTextColor( binding.tvAge19.setTextColor(
ContextCompat.getColor( ContextCompat.getColor(
applicationContext, applicationContext,
R.color.color_3bb9f1 R.color.color_9970ff
) )
) )
binding.ivAgeAll.visibility = View.VISIBLE binding.ivAgeAll.visibility = View.VISIBLE
binding.llAgeAll.setBackgroundResource(R.drawable.bg_round_corner_6_7_3bb9f1) binding.llAgeAll.setBackgroundResource(R.drawable.bg_round_corner_6_7_9970ff)
binding.tvAgeAll.setTextColor( binding.tvAgeAll.setTextColor(
ContextCompat.getColor( ContextCompat.getColor(
applicationContext, applicationContext,
@ -544,81 +406,6 @@ class AudioContentUploadActivity : BaseActivity<ActivityAudioContentUploadBindin
} }
} }
} }
viewModel.isActiveReservationLiveData.observe(this) {
if (it) {
checkActiveReservation()
} else {
checkActiveNow()
}
}
viewModel.reservationDateLiveData.observe(this) {
binding.tvReservationDate.text = it
}
viewModel.reservationTimeLiveData.observe(this) {
binding.tvReservationTime.text = it
}
viewModel.isShowConfigLimitedLiveData.observe(this) {
if (it) {
binding.llConfigLimited.visibility = View.VISIBLE
binding.tvConfigLimitedTitle.visibility = View.VISIBLE
} else {
binding.llConfigLimited.visibility = View.GONE
binding.tvConfigLimitedTitle.visibility = View.GONE
binding.etLimited.visibility = View.GONE
binding.etLimited.setText("")
}
}
}
private fun checkActiveNow() {
binding.llReservationDatetime.visibility = View.GONE
binding.ivActiveNow.visibility = View.VISIBLE
binding.tvActiveNow.setTextColor(
ContextCompat.getColor(
applicationContext,
R.color.color_eeeeee
)
)
binding.llActiveNow.setBackgroundResource(R.drawable.bg_round_corner_6_7_3bb9f1)
binding.ivActiveReservation.visibility = View.GONE
binding.tvActiveReservation.setTextColor(
ContextCompat.getColor(
applicationContext,
R.color.color_3bb9f1
)
)
binding.llActiveReservation.setBackgroundResource(
R.drawable.bg_round_corner_6_7_13181b
)
}
private fun checkActiveReservation() {
binding.llReservationDatetime.visibility = View.VISIBLE
binding.ivActiveReservation.visibility = View.VISIBLE
binding.tvActiveReservation.setTextColor(
ContextCompat.getColor(
applicationContext,
R.color.color_eeeeee
)
)
binding.llActiveReservation.setBackgroundResource(R.drawable.bg_round_corner_6_7_3bb9f1)
binding.ivActiveNow.visibility = View.GONE
binding.tvActiveNow.setTextColor(
ContextCompat.getColor(
applicationContext,
R.color.color_3bb9f1
)
)
binding.llActiveNow.setBackgroundResource(
R.drawable.bg_round_corner_6_7_13181b
)
} }
private fun checkPriceFree() { private fun checkPriceFree() {
@ -635,20 +422,19 @@ class AudioContentUploadActivity : BaseActivity<ActivityAudioContentUploadBindin
R.color.color_eeeeee R.color.color_eeeeee
) )
) )
binding.llPriceFree.setBackgroundResource(R.drawable.bg_round_corner_6_7_3bb9f1) binding.llPriceFree.setBackgroundResource(R.drawable.bg_round_corner_6_7_9970ff)
binding.ivPricePaid.visibility = View.GONE binding.ivPricePaid.visibility = View.GONE
binding.tvPricePaid.setTextColor( binding.tvPricePaid.setTextColor(
ContextCompat.getColor( ContextCompat.getColor(
applicationContext, applicationContext,
R.color.color_3bb9f1 R.color.color_9970ff
) )
) )
binding.llPricePaid.setBackgroundResource( binding.llPricePaid.setBackgroundResource(
R.drawable.bg_round_corner_6_7_13181b R.drawable.bg_round_corner_6_7_1f1734_9970ff
) )
binding.llConfigPreview.visibility = View.GONE
binding.llConfigPreviewTime.visibility = View.GONE binding.llConfigPreviewTime.visibility = View.GONE
} }
@ -664,114 +450,21 @@ class AudioContentUploadActivity : BaseActivity<ActivityAudioContentUploadBindin
R.color.color_eeeeee R.color.color_eeeeee
) )
) )
binding.llPricePaid.setBackgroundResource(R.drawable.bg_round_corner_6_7_3bb9f1) binding.llPricePaid.setBackgroundResource(R.drawable.bg_round_corner_6_7_9970ff)
binding.ivPriceFree.visibility = View.GONE binding.ivPriceFree.visibility = View.GONE
binding.tvPriceFree.setTextColor( binding.tvPriceFree.setTextColor(
ContextCompat.getColor( ContextCompat.getColor(
applicationContext, applicationContext,
R.color.color_3bb9f1 R.color.color_9970ff
) )
) )
binding.llPriceFree.setBackgroundResource( binding.llPriceFree.setBackgroundResource(
R.drawable.bg_round_corner_6_7_13181b R.drawable.bg_round_corner_6_7_1f1734_9970ff
)
binding.llConfigPreview.visibility = View.VISIBLE
}
private fun checkNotLimited() {
binding.ivNotLimited.visibility = View.VISIBLE
binding.tvNotLimited.setTextColor(
ContextCompat.getColor(
applicationContext,
R.color.color_eeeeee
)
)
binding.llNotLimited.setBackgroundResource(R.drawable.bg_round_corner_6_7_3bb9f1)
binding.ivLimited.visibility = View.GONE
binding.tvLimited.setTextColor(
ContextCompat.getColor(
applicationContext,
R.color.color_3bb9f1
)
)
binding.llLimited.setBackgroundResource(
R.drawable.bg_round_corner_6_7_13181b
)
binding.etLimited.visibility = View.GONE
binding.etLimited.setText("")
}
private fun checkLimited() {
binding.ivLimited.visibility = View.VISIBLE
binding.tvLimited.setTextColor(
ContextCompat.getColor(
applicationContext,
R.color.color_eeeeee
)
)
binding.llLimited.setBackgroundResource(R.drawable.bg_round_corner_6_7_3bb9f1)
binding.ivNotLimited.visibility = View.GONE
binding.tvNotLimited.setTextColor(
ContextCompat.getColor(
applicationContext,
R.color.color_3bb9f1
)
)
binding.llNotLimited.setBackgroundResource(
R.drawable.bg_round_corner_6_7_13181b
)
binding.etLimited.visibility = View.VISIBLE
}
private fun checkGeneratePreview() {
binding.ivPreviewYes.visibility = View.VISIBLE
binding.tvPreviewYes.setTextColor(
ContextCompat.getColor(
applicationContext,
R.color.color_eeeeee
)
)
binding.llPreviewYes.setBackgroundResource(R.drawable.bg_round_corner_6_7_3bb9f1)
binding.ivPreviewNo.visibility = View.GONE
binding.tvPreviewNo.setTextColor(
ContextCompat.getColor(
applicationContext,
R.color.color_3bb9f1
)
)
binding.llPreviewNo.setBackgroundResource(
R.drawable.bg_round_corner_6_7_13181b
) )
binding.llConfigPreviewTime.visibility = View.VISIBLE binding.llConfigPreviewTime.visibility = View.VISIBLE
} }
private fun checkNotGeneratePreview() {
binding.ivPreviewNo.visibility = View.VISIBLE
binding.tvPreviewNo.setTextColor(
ContextCompat.getColor(
applicationContext,
R.color.color_eeeeee
)
)
binding.llPreviewNo.setBackgroundResource(R.drawable.bg_round_corner_6_7_3bb9f1)
binding.ivPreviewYes.visibility = View.GONE
binding.tvPreviewYes.setTextColor(
ContextCompat.getColor(
applicationContext,
R.color.color_3bb9f1
)
)
binding.llPreviewYes.setBackgroundResource(
R.drawable.bg_round_corner_6_7_13181b
)
binding.llConfigPreviewTime.visibility = View.GONE
}
private fun checkRentalAndKeep() { private fun checkRentalAndKeep() {
binding.tvPriceTitle.text = "소장 가격" binding.tvPriceTitle.text = "소장 가격"
binding.ivRentalAndKeep.visibility = View.VISIBLE binding.ivRentalAndKeep.visibility = View.VISIBLE
@ -781,17 +474,17 @@ class AudioContentUploadActivity : BaseActivity<ActivityAudioContentUploadBindin
R.color.color_eeeeee R.color.color_eeeeee
) )
) )
binding.llRentalAndKeep.setBackgroundResource(R.drawable.bg_round_corner_6_7_3bb9f1) binding.llRentalAndKeep.setBackgroundResource(R.drawable.bg_round_corner_6_7_9970ff)
binding.ivOnlyRental.visibility = View.GONE binding.ivOnlyRental.visibility = View.GONE
binding.tvOnlyRental.setTextColor( binding.tvOnlyRental.setTextColor(
ContextCompat.getColor( ContextCompat.getColor(
applicationContext, applicationContext,
R.color.color_3bb9f1 R.color.color_9970ff
) )
) )
binding.llOnlyRental.setBackgroundResource( binding.llOnlyRental.setBackgroundResource(
R.drawable.bg_round_corner_6_7_13181b R.drawable.bg_round_corner_6_7_1f1734_9970ff
) )
} }
@ -804,17 +497,17 @@ class AudioContentUploadActivity : BaseActivity<ActivityAudioContentUploadBindin
R.color.color_eeeeee R.color.color_eeeeee
) )
) )
binding.llOnlyRental.setBackgroundResource(R.drawable.bg_round_corner_6_7_3bb9f1) binding.llOnlyRental.setBackgroundResource(R.drawable.bg_round_corner_6_7_9970ff)
binding.ivRentalAndKeep.visibility = View.GONE binding.ivRentalAndKeep.visibility = View.GONE
binding.tvRentalAndKeep.setTextColor( binding.tvRentalAndKeep.setTextColor(
ContextCompat.getColor( ContextCompat.getColor(
applicationContext, applicationContext,
R.color.color_3bb9f1 R.color.color_9970ff
) )
) )
binding.llRentalAndKeep.setBackgroundResource( binding.llRentalAndKeep.setBackgroundResource(
R.drawable.bg_round_corner_6_7_13181b R.drawable.bg_round_corner_6_7_1f1734_9970ff
) )
} }

View File

@ -20,7 +20,6 @@ import okio.BufferedSink
import java.io.File import java.io.File
import java.text.SimpleDateFormat import java.text.SimpleDateFormat
import java.util.Locale import java.util.Locale
import java.util.TimeZone
class AudioContentUploadViewModel( class AudioContentUploadViewModel(
private val repository: AudioContentRepository private val repository: AudioContentRepository
@ -50,39 +49,12 @@ class AudioContentUploadViewModel(
val isPriceFreeLiveData: LiveData<Boolean> val isPriceFreeLiveData: LiveData<Boolean>
get() = _isPriceFreeLiveData get() = _isPriceFreeLiveData
private val _isLimitedLiveData = MutableLiveData(false)
val isLimitedLiveData: LiveData<Boolean>
get() = _isLimitedLiveData
private val _isShowConfigLimitedLiveData = MutableLiveData(false)
val isShowConfigLimitedLiveData: LiveData<Boolean>
get() = _isShowConfigLimitedLiveData
private val _isGeneratePreviewLiveData = MutableLiveData(true)
val isGeneratePreviewLiveData: LiveData<Boolean>
get() = _isGeneratePreviewLiveData
private val _isActiveReservationLiveData = MutableLiveData(false)
val isActiveReservationLiveData: LiveData<Boolean>
get() = _isActiveReservationLiveData
private val _reservationDateLiveData = MutableLiveData("날짜를 선택해주세요")
val reservationDateLiveData: LiveData<String>
get() = _reservationDateLiveData
private val _reservationTimeLiveData = MutableLiveData("시간을 설정해주세요")
val reservationTimeLiveData: LiveData<String>
get() = _reservationTimeLiveData
lateinit var getRealPathFromURI: (Uri) -> String? lateinit var getRealPathFromURI: (Uri) -> String?
var title = "" var title = ""
var detail = "" var detail = ""
var tags = "" var tags = ""
var price = 0 var price = 0
var limited = 0
var releaseDate = ""
var releaseTime = ""
var theme: GetAudioContentThemeResponse? = null var theme: GetAudioContentThemeResponse? = null
var coverImageUri: Uri? = null var coverImageUri: Uri? = null
var contentUri: Uri? = null var contentUri: Uri? = null
@ -102,94 +74,30 @@ class AudioContentUploadViewModel(
if (isPriceFree) { if (isPriceFree) {
_isOnlyRentalLiveData.postValue(false) _isOnlyRentalLiveData.postValue(false)
_isShowConfigLimitedLiveData.postValue(false)
_isLimitedLiveData.postValue(false)
limited = 0
_isGeneratePreviewLiveData.postValue(true)
} else {
if (!_isOnlyRentalLiveData.value!!) {
_isShowConfigLimitedLiveData.postValue(true)
}
}
}
fun setGeneratePreview(isGeneratePreview: Boolean) {
_isGeneratePreviewLiveData.value = isGeneratePreview
}
fun setLimited(isLimited: Boolean) {
_isLimitedLiveData.value = isLimited
if (!isLimited) {
limited = 0
} }
} }
fun setIsOnlyRental(isOnlyRental: Boolean) { fun setIsOnlyRental(isOnlyRental: Boolean) {
_isOnlyRentalLiveData.postValue(isOnlyRental) _isOnlyRentalLiveData.postValue(isOnlyRental)
if (isOnlyRental) {
_isShowConfigLimitedLiveData.postValue(false)
_isLimitedLiveData.postValue(false)
limited = 0
} else {
if (!_isPriceFreeLiveData.value!!) {
_isShowConfigLimitedLiveData.postValue(true)
}
}
}
fun setActiveReservation(isActiveReservation: Boolean) {
_isActiveReservationLiveData.postValue(isActiveReservation)
} }
fun uploadAudioContent(onSuccess: () -> Unit) { fun uploadAudioContent(onSuccess: () -> Unit) {
if (!_isLoading.value!! && validateData()) { if (!_isLoading.value!! && validateData()) {
_isLoading.postValue(true) _isLoading.postValue(true)
val isGeneratePreview = _isGeneratePreviewLiveData.value!!
val request = CreateAudioContentRequest( val request = CreateAudioContentRequest(
title = title, title = title,
detail = detail, detail = detail,
tags = tags, tags = tags,
price = price, price = price,
limited = if (
price > 0 &&
limited > 0 &&
_isLimitedLiveData.value!! &&
_isShowConfigLimitedLiveData.value!! &&
!_isPriceFreeLiveData.value!!
) {
limited
} else {
null
},
releaseDate = if (_isActiveReservationLiveData.value!!) {
"$releaseDate $releaseTime"
} else {
null
},
timezone = TimeZone.getDefault().id,
themeId = theme!!.id, themeId = theme!!.id,
isAdult = _isAdultLiveData.value!!, isAdult = _isAdultLiveData.value!!,
isOnlyRental = _isOnlyRentalLiveData.value!!, isOnlyRental = _isOnlyRentalLiveData.value!!,
isGeneratePreview = isGeneratePreview,
isCommentAvailable = _isAvailableCommentLiveData.value!!, isCommentAvailable = _isAvailableCommentLiveData.value!!,
previewStartTime = if (isGeneratePreview) { previewStartTime = previewStartTime,
previewStartTime previewEndTime = previewEndTime
} else {
null
},
previewEndTime = if (isGeneratePreview) {
previewEndTime
} else {
null
}
) )
Logger.e("test - $request")
val requestJson = Gson().toJson(request) val requestJson = Gson().toJson(request)
val coverImage = if (coverImageUri != null) { val coverImage = if (coverImageUri != null) {
@ -346,9 +254,9 @@ class AudioContentUploadViewModel(
val timeDifference = timeDifference(previewStartTime!!, previewEndTime!!) val timeDifference = timeDifference(previewStartTime!!, previewEndTime!!)
if (timeDifference < 15000) { if (timeDifference < 30000) {
_toastLiveData.postValue( _toastLiveData.postValue(
"미리 듣기의 최소 시간은 15초 입니다." "미리 듣기의 최소 시간은 30초 입니다."
) )
return false return false
@ -373,14 +281,6 @@ class AudioContentUploadViewModel(
return false return false
} }
if (
_isActiveReservationLiveData.value!! &&
(releaseDate.isBlank() || releaseTime.isBlank())
) {
_toastLiveData.postValue("예약날짜와 시간을 선택해주세요.")
return false
}
return true return true
} }
@ -407,12 +307,4 @@ class AudioContentUploadViewModel(
return 0 return 0
} }
} }
fun setReservationDate(dateString: String) {
_reservationDateLiveData.postValue(dateString)
}
fun setReservationTime(timeString: String) {
_reservationTimeLiveData.postValue(timeString)
}
} }

View File

@ -1,22 +1,16 @@
package kr.co.vividnext.sodalive.audio_content.upload package kr.co.vividnext.sodalive.audio_content.upload
import androidx.annotation.Keep
import com.google.gson.annotations.SerializedName import com.google.gson.annotations.SerializedName
@Keep
data class CreateAudioContentRequest( data class CreateAudioContentRequest(
@SerializedName("title") val title: String, @SerializedName("title") val title: String,
@SerializedName("detail") val detail: String, @SerializedName("detail") val detail: String,
@SerializedName("tags") val tags: String, @SerializedName("tags") val tags: String,
@SerializedName("price") val price: Int, @SerializedName("price") val price: Int,
@SerializedName("limited") val limited: Int? = null,
@SerializedName("releaseDate") val releaseDate: String?,
@SerializedName("timezone") val timezone: String,
@SerializedName("themeId") val themeId: Long, @SerializedName("themeId") val themeId: Long,
@SerializedName("isAdult") val isAdult: Boolean, @SerializedName("isAdult") val isAdult: Boolean,
@SerializedName("isOnlyRental") val isOnlyRental: Boolean, @SerializedName("isOnlyRental") val isOnlyRental: Boolean,
@SerializedName("isGeneratePreview") val isGeneratePreview: Boolean,
@SerializedName("isCommentAvailable") val isCommentAvailable: Boolean, @SerializedName("isCommentAvailable") val isCommentAvailable: Boolean,
@SerializedName("previewStartTime") val previewStartTime: String? = null, @SerializedName("previewStartTime") val previewStartTime: String? = null,
@SerializedName("previewEndTime") val previewEndTime: String? = null, @SerializedName("previewEndTime") val previewEndTime: String? = null
) )

View File

@ -1,9 +1,7 @@
package kr.co.vividnext.sodalive.audio_content.upload.theme package kr.co.vividnext.sodalive.audio_content.upload.theme
import androidx.annotation.Keep
import com.google.gson.annotations.SerializedName import com.google.gson.annotations.SerializedName
@Keep
data class GetAudioContentThemeResponse( data class GetAudioContentThemeResponse(
@SerializedName("id") val id: Long, @SerializedName("id") val id: Long,
@SerializedName("theme") val theme: String, @SerializedName("theme") val theme: String,

View File

@ -1,9 +1,7 @@
package kr.co.vividnext.sodalive.common package kr.co.vividnext.sodalive.common
import androidx.annotation.Keep
import com.google.gson.annotations.SerializedName import com.google.gson.annotations.SerializedName
@Keep
data class ApiResponse<T>( data class ApiResponse<T>(
@SerializedName("success") val success: Boolean, @SerializedName("success") val success: Boolean,
@SerializedName("data") val data: T? = null, @SerializedName("data") val data: T? = null,

View File

@ -11,9 +11,7 @@ object Constants {
const val PREF_NO_CHAT_ROOM = "pref_no_chat" const val PREF_NO_CHAT_ROOM = "pref_no_chat"
const val PREF_PUSH_TOKEN = "pref_push_token" const val PREF_PUSH_TOKEN = "pref_push_token"
const val PREF_PROFILE_IMAGE = "pref_profile_image" const val PREF_PROFILE_IMAGE = "pref_profile_image"
const val PREF_CONTENT_PREFERENCE = "pref_content_preference"
const val PREF_IS_CONTENT_PLAY_LOOP = "pref_is_content_play_loop" const val PREF_IS_CONTENT_PLAY_LOOP = "pref_is_content_play_loop"
const val PREF_IS_ADULT_CONTENT_VISIBLE = "pref_is_adult_content_visible"
const val PREF_IS_FOLLOWED_CREATOR_LIVE = "pref_is_followed_creator_live" const val PREF_IS_FOLLOWED_CREATOR_LIVE = "pref_is_followed_creator_live"
const val PREF_NOT_SHOWING_EVENT_POPUP_ID = "pref_not_showing_event_popup_id" const val PREF_NOT_SHOWING_EVENT_POPUP_ID = "pref_not_showing_event_popup_id"
const val PREF_IS_VIEWED_ON_BOARDING_TUTORIAL = "pref_is_viewed_on_boarding_tutorial" const val PREF_IS_VIEWED_ON_BOARDING_TUTORIAL = "pref_is_viewed_on_boarding_tutorial"
@ -21,22 +19,16 @@ object Constants {
const val EXTRA_CAN = "extra_can" const val EXTRA_CAN = "extra_can"
const val EXTRA_DATA = "extra_data" const val EXTRA_DATA = "extra_data"
const val EXTRA_TERMS = "extra_terms" const val EXTRA_TERMS = "extra_terms"
const val EXTRA_PRIVACY = "extra_privacy"
const val EXTRA_EVENT = "extra_event" const val EXTRA_EVENT = "extra_event"
const val EXTRA_SERIES = "extra_series"
const val EXTRA_NOTICE = "extra_notice" const val EXTRA_NOTICE = "extra_notice"
const val EXTRA_ROOM_ID = "extra_room_id" const val EXTRA_ROOM_ID = "extra_room_id"
const val EXTRA_USER_ID = "extra_user_id" const val EXTRA_USER_ID = "extra_user_id"
const val EXTRA_THEME_ID = "extra_theme_id"
const val EXTRA_SERIES_ID = "extra_series_id"
const val EXTRA_NICKNAME = "extra_nickname" const val EXTRA_NICKNAME = "extra_nickname"
const val EXTRA_MESSAGE_ID = "extra_message_id" const val EXTRA_MESSAGE_ID = "extra_message_id"
const val EXTRA_ROOM_DETAIL = "extra_room_detail" const val EXTRA_ROOM_DETAIL = "extra_room_detail"
const val EXTRA_MESSAGE_BOX = "extra_message_box" const val EXTRA_MESSAGE_BOX = "extra_message_box"
const val EXTRA_SERIES_TITLE = "extra_series_title"
const val EXTRA_TEXT_MESSAGE = "extra_text_message" const val EXTRA_TEXT_MESSAGE = "extra_text_message"
const val EXTRA_LIVE_TIME_NOW = "extra_live_time_now" const val EXTRA_LIVE_TIME_NOW = "extra_live_time_now"
const val EXTRA_RESULT_ROULETTE = "extra_result_roulette"
const val EXTRA_GO_TO_PREV_PAGE = "extra_go_to_prev_page" const val EXTRA_GO_TO_PREV_PAGE = "extra_go_to_prev_page"
const val EXTRA_SELECT_RECIPIENT = "extra_select_recipient" const val EXTRA_SELECT_RECIPIENT = "extra_select_recipient"
const val EXTRA_ROOM_CHANNEL_NAME = "extra_room_channel_name" const val EXTRA_ROOM_CHANNEL_NAME = "extra_room_channel_name"
@ -55,22 +47,13 @@ object Constants {
const val EXTRA_AUDIO_CONTENT_COMMENT = "audio_content_comment" const val EXTRA_AUDIO_CONTENT_COMMENT = "audio_content_comment"
const val EXTRA_AUDIO_CONTENT_LOADING = "audio_content_loading" const val EXTRA_AUDIO_CONTENT_LOADING = "audio_content_loading"
const val EXTRA_AUDIO_CONTENT_CREATOR_ID = "audio_content_creator_id" const val EXTRA_AUDIO_CONTENT_CREATOR_ID = "audio_content_creator_id"
const val EXTRA_AUDIO_CONTENT_CREATOR_NICKNAME = "audio_content_creator_nickname"
const val EXTRA_AUDIO_CONTENT_CURATION_ID = "extra_audio_content_curation_id" const val EXTRA_AUDIO_CONTENT_CURATION_ID = "extra_audio_content_curation_id"
const val EXTRA_AUDIO_CONTENT_CURATION_TITLE = "extra_audio_content_curation_title" const val EXTRA_AUDIO_CONTENT_CURATION_TITLE = "extra_audio_content_curation_title"
const val EXTRA_AUDIO_CONTENT_NEXT_ACTION = "audio_content_next_action" const val EXTRA_AUDIO_CONTENT_NEXT_ACTION = "audio_content_next_action"
const val EXTRA_AUDIO_CONTENT_ALERT_PREVIEW = "audio_content_alert_preview" const val EXTRA_AUDIO_CONTENT_ALERT_PREVIEW = "audio_content_alert_preview"
const val EXTRA_AUDIO_CONTENT_COVER_IMAGE_URL = "audio_content_cover_image_url" const val EXTRA_AUDIO_CONTENT_COVER_IMAGE_URL = "audio_content_cover_image_url"
const val EXTRA_IS_SHOW_SECRET = "extra_is_show_secret"
const val LIVE_SERVICE_NOTIFICATION_ID: Int = 2 const val LIVE_SERVICE_NOTIFICATION_ID: Int = 2
const val ACTION_AUDIO_CONTENT_RECEIVER = "soda_live_action_content_receiver" const val ACTION_AUDIO_CONTENT_RECEIVER = "soda_live_action_content_receiver"
const val ACTION_MAIN_AUDIO_CONTENT_RECEIVER = "soda_live_action_main_content_receiver" const val ACTION_MAIN_AUDIO_CONTENT_RECEIVER = "soda_live_action_main_content_receiver"
const val EXTRA_COMMUNITY_POST_ID = "community_post_id"
const val EXTRA_COMMUNITY_CREATOR_ID = "community_creator_id"
const val EXTRA_COMMUNITY_POST_COMMENT = "community_post_comment_id"
const val EXTRA_ALARM_ID = "alarm_id"
const val EXTRA_ROULETTE_AVAILABLE_ACTIVE = "roulette_available_active"
} }

View File

@ -1,18 +0,0 @@
package kr.co.vividnext.sodalive.common
import androidx.room.TypeConverter
import com.google.gson.Gson
import com.google.gson.reflect.TypeToken
class Converter {
@TypeConverter
fun fromString(value: String): List<String> {
val listType = object : TypeToken<List<String>>() {}.type
return Gson().fromJson(value, listType)
}
@TypeConverter
fun fromList(list: List<String>): String {
return Gson().toJson(list)
}
}

View File

@ -1,39 +0,0 @@
package kr.co.vividnext.sodalive.common
import android.graphics.Rect
import android.view.View
import androidx.recyclerview.widget.RecyclerView
class DifferentSpacingItemDecoration(
private val spanCount: Int,
private val horizontalSpacing: Int,
private val verticalSpacing: Int,
private val includeEdge: Boolean
) : RecyclerView.ItemDecoration() {
override fun getItemOffsets(
outRect: Rect,
view: View,
parent: RecyclerView,
state: RecyclerView.State
) {
val position = parent.getChildAdapterPosition(view) // 아이템의 위치
val column = position % spanCount // 아이템의 열 위치
if (includeEdge) {
outRect.left = horizontalSpacing - column * horizontalSpacing / spanCount
outRect.right = (column + 1) * horizontalSpacing / spanCount
if (position < spanCount) {
outRect.top = verticalSpacing
}
outRect.bottom = verticalSpacing
} else {
outRect.left = column * horizontalSpacing / spanCount
outRect.right = horizontalSpacing - (column + 1) * horizontalSpacing / spanCount
if (position >= spanCount) {
outRect.top = verticalSpacing
}
}
}
}

View File

@ -1,37 +0,0 @@
package kr.co.vividnext.sodalive.common
import android.graphics.Rect
import android.view.View
import androidx.recyclerview.widget.RecyclerView
import androidx.recyclerview.widget.RecyclerView.ItemDecoration
class GridSpacingItemDecoration(
private val spanCount: Int,
private val spacing: Int,
private val includeEdge: Boolean
) : ItemDecoration() {
override fun getItemOffsets(
outRect: Rect,
view: View,
parent: RecyclerView,
state: RecyclerView.State
) {
val position = parent.getChildAdapterPosition(view) // Item position
val column = position % spanCount // Current column
if (includeEdge) {
outRect.left = spacing - column * spacing / spanCount
outRect.right = (column + 1) * spacing / spanCount
if (position < spanCount) { // Top edge
outRect.top = spacing
}
outRect.bottom = spacing // Item bottom
} else {
outRect.left = column * spacing / spanCount
outRect.right = spacing - (column + 1) * spacing / spanCount
if (position >= spanCount) {
outRect.top = spacing // Item top
}
}
}
}

View File

@ -1,32 +0,0 @@
package kr.co.vividnext.sodalive.common
import android.content.Context
import coil.ImageLoader
import okhttp3.Cache
import okhttp3.OkHttpClient
import java.io.File
object ImageLoaderProvider {
lateinit var imageLoader: ImageLoader
private set
val isInitialized: Boolean
get() = ::imageLoader.isInitialized
fun init(context: Context) {
val cacheSize = 250L * 1024L * 1024L // 250 MB
val cacheDirectory = File(
context.cacheDir,
"image_cache"
).apply { mkdirs() }
val cache = Cache(cacheDirectory, cacheSize)
imageLoader = ImageLoader.Builder(context)
.okHttpClient {
OkHttpClient().newBuilder()
.cache(cache)
.build()
}
.build()
}
}

View File

@ -95,18 +95,6 @@ object SharedPreferenceManager {
sharedPreferences[Constants.PREF_IS_ADULT] = value sharedPreferences[Constants.PREF_IS_ADULT] = value
} }
var isAdultContentVisible: Boolean
get() = sharedPreferences[Constants.PREF_IS_ADULT_CONTENT_VISIBLE, true]
set(value) {
sharedPreferences[Constants.PREF_IS_ADULT_CONTENT_VISIBLE] = value
}
var contentPreference: Int
get() = sharedPreferences[Constants.PREF_CONTENT_PREFERENCE, 0]
set(value) {
sharedPreferences[Constants.PREF_CONTENT_PREFERENCE] = value
}
var pushToken: String var pushToken: String
get() = sharedPreferences[Constants.PREF_PUSH_TOKEN, ""] get() = sharedPreferences[Constants.PREF_PUSH_TOKEN, ""]
set(value) { set(value) {

View File

@ -7,8 +7,6 @@ import android.app.PendingIntent
import android.app.Service import android.app.Service
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_MEDIA_PLAYBACK
import android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_MICROPHONE
import android.os.Build import android.os.Build
import android.os.IBinder import android.os.IBinder
import androidx.core.app.NotificationCompat import androidx.core.app.NotificationCompat
@ -35,19 +33,7 @@ class SodaLiveService : Service() {
} }
private fun updateNotification(content: String) { private fun updateNotification(content: String) {
val notification = createNotification(content) startForeground(Constants.LIVE_SERVICE_NOTIFICATION_ID, createNotification(content))
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
val foregroundServiceTypes = FOREGROUND_SERVICE_TYPE_MICROPHONE or
FOREGROUND_SERVICE_TYPE_MEDIA_PLAYBACK
startForeground(
Constants.LIVE_SERVICE_NOTIFICATION_ID,
notification,
foregroundServiceTypes
)
} else {
startForeground(Constants.LIVE_SERVICE_NOTIFICATION_ID, notification)
}
} }
private fun createNotification(content: String): Notification { private fun createNotification(content: String): Notification {
@ -76,7 +62,7 @@ class SodaLiveService : Service() {
) )
val notificationBuilder = NotificationCompat.Builder(this, notificationChannelId) val notificationBuilder = NotificationCompat.Builder(this, notificationChannelId)
.setSmallIcon(R.mipmap.ic_launcher) .setSmallIcon(R.drawable.ic_notification)
.setContentTitle(getString(R.string.app_name)) .setContentTitle(getString(R.string.app_name))
.setContentText(content) .setContentText(content)
.setOngoing(true) .setOngoing(true)

View File

@ -9,26 +9,14 @@ import kr.co.vividnext.sodalive.audio_content.AudioContentViewModel
import kr.co.vividnext.sodalive.audio_content.PlaybackTrackingRepository import kr.co.vividnext.sodalive.audio_content.PlaybackTrackingRepository
import kr.co.vividnext.sodalive.audio_content.all.AudioContentNewAllViewModel import kr.co.vividnext.sodalive.audio_content.all.AudioContentNewAllViewModel
import kr.co.vividnext.sodalive.audio_content.all.AudioContentRankingAllViewModel import kr.co.vividnext.sodalive.audio_content.all.AudioContentRankingAllViewModel
import kr.co.vividnext.sodalive.audio_content.all.by_theme.AudioContentAllByThemeViewModel
import kr.co.vividnext.sodalive.audio_content.category.CategoryApi
import kr.co.vividnext.sodalive.audio_content.comment.AudioContentCommentListViewModel import kr.co.vividnext.sodalive.audio_content.comment.AudioContentCommentListViewModel
import kr.co.vividnext.sodalive.audio_content.comment.AudioContentCommentReplyViewModel import kr.co.vividnext.sodalive.audio_content.comment.AudioContentCommentReplyViewModel
import kr.co.vividnext.sodalive.audio_content.comment.AudioContentCommentRepository import kr.co.vividnext.sodalive.audio_content.comment.AudioContentCommentRepository
import kr.co.vividnext.sodalive.audio_content.curation.AudioContentCurationViewModel import kr.co.vividnext.sodalive.audio_content.curation.AudioContentCurationViewModel
import kr.co.vividnext.sodalive.audio_content.detail.AudioContentDetailViewModel import kr.co.vividnext.sodalive.audio_content.detail.AudioContentDetailViewModel
import kr.co.vividnext.sodalive.audio_content.main.banner.AudioContentMainBannerViewModel import kr.co.vividnext.sodalive.audio_content.main.AudioContentMainViewModel
import kr.co.vividnext.sodalive.audio_content.main.curation.AudioContentMainCurationViewModel
import kr.co.vividnext.sodalive.audio_content.main.new_content.AudioContentMainNewContentViewModel
import kr.co.vividnext.sodalive.audio_content.main.order.AudioContentMainOrderListViewModel
import kr.co.vividnext.sodalive.audio_content.main.ranking.AudioContentMainRankingViewModel
import kr.co.vividnext.sodalive.audio_content.main.recommend_series.AudioContentMainRecommendSeriesViewModel
import kr.co.vividnext.sodalive.audio_content.modify.AudioContentModifyViewModel import kr.co.vividnext.sodalive.audio_content.modify.AudioContentModifyViewModel
import kr.co.vividnext.sodalive.audio_content.order.AudioContentOrderListViewModel import kr.co.vividnext.sodalive.audio_content.order.AudioContentOrderListViewModel
import kr.co.vividnext.sodalive.audio_content.series.SeriesApi
import kr.co.vividnext.sodalive.audio_content.series.SeriesListAllViewModel
import kr.co.vividnext.sodalive.audio_content.series.SeriesRepository
import kr.co.vividnext.sodalive.audio_content.series.content.SeriesContentAllViewModel
import kr.co.vividnext.sodalive.audio_content.series.detail.SeriesDetailViewModel
import kr.co.vividnext.sodalive.audio_content.upload.AudioContentUploadViewModel import kr.co.vividnext.sodalive.audio_content.upload.AudioContentUploadViewModel
import kr.co.vividnext.sodalive.audio_content.upload.theme.AudioContentThemeViewModel import kr.co.vividnext.sodalive.audio_content.upload.theme.AudioContentThemeViewModel
import kr.co.vividnext.sodalive.common.ApiBuilder import kr.co.vividnext.sodalive.common.ApiBuilder
@ -37,12 +25,6 @@ import kr.co.vividnext.sodalive.explorer.ExplorerApi
import kr.co.vividnext.sodalive.explorer.ExplorerRepository import kr.co.vividnext.sodalive.explorer.ExplorerRepository
import kr.co.vividnext.sodalive.explorer.ExplorerViewModel import kr.co.vividnext.sodalive.explorer.ExplorerViewModel
import kr.co.vividnext.sodalive.explorer.profile.UserProfileViewModel import kr.co.vividnext.sodalive.explorer.profile.UserProfileViewModel
import kr.co.vividnext.sodalive.explorer.profile.creator_community.CreatorCommunityApi
import kr.co.vividnext.sodalive.explorer.profile.creator_community.CreatorCommunityRepository
import kr.co.vividnext.sodalive.explorer.profile.creator_community.all.CreatorCommunityAllViewModel
import kr.co.vividnext.sodalive.explorer.profile.creator_community.all.comment.CreatorCommunityCommentListViewModel
import kr.co.vividnext.sodalive.explorer.profile.creator_community.modify.CreatorCommunityModifyViewModel
import kr.co.vividnext.sodalive.explorer.profile.creator_community.write.CreatorCommunityWriteViewModel
import kr.co.vividnext.sodalive.explorer.profile.donation.UserProfileDonationAllViewModel import kr.co.vividnext.sodalive.explorer.profile.donation.UserProfileDonationAllViewModel
import kr.co.vividnext.sodalive.explorer.profile.fantalk.UserProfileFantalkAllViewModel import kr.co.vividnext.sodalive.explorer.profile.fantalk.UserProfileFantalkAllViewModel
import kr.co.vividnext.sodalive.explorer.profile.follow.UserFollowerListViewModel import kr.co.vividnext.sodalive.explorer.profile.follow.UserFollowerListViewModel
@ -58,15 +40,9 @@ import kr.co.vividnext.sodalive.live.room.LiveRoomViewModel
import kr.co.vividnext.sodalive.live.room.create.LiveRoomCreateViewModel import kr.co.vividnext.sodalive.live.room.create.LiveRoomCreateViewModel
import kr.co.vividnext.sodalive.live.room.detail.LiveRoomDetailViewModel import kr.co.vividnext.sodalive.live.room.detail.LiveRoomDetailViewModel
import kr.co.vividnext.sodalive.live.room.donation.LiveRoomDonationMessageViewModel import kr.co.vividnext.sodalive.live.room.donation.LiveRoomDonationMessageViewModel
import kr.co.vividnext.sodalive.live.room.menu.MenuApi
import kr.co.vividnext.sodalive.live.room.menu.MenuConfigRepository
import kr.co.vividnext.sodalive.live.room.menu.MenuConfigViewModel
import kr.co.vividnext.sodalive.live.room.tag.LiveTagRepository import kr.co.vividnext.sodalive.live.room.tag.LiveTagRepository
import kr.co.vividnext.sodalive.live.room.tag.LiveTagViewModel import kr.co.vividnext.sodalive.live.room.tag.LiveTagViewModel
import kr.co.vividnext.sodalive.live.room.update.LiveRoomEditViewModel import kr.co.vividnext.sodalive.live.room.update.LiveRoomEditViewModel
import kr.co.vividnext.sodalive.live.roulette.RouletteRepository
import kr.co.vividnext.sodalive.live.roulette.config.RouletteApi
import kr.co.vividnext.sodalive.live.roulette.config.RouletteSettingsViewModel
import kr.co.vividnext.sodalive.main.MainViewModel import kr.co.vividnext.sodalive.main.MainViewModel
import kr.co.vividnext.sodalive.message.MessageApi import kr.co.vividnext.sodalive.message.MessageApi
import kr.co.vividnext.sodalive.message.MessageRepository import kr.co.vividnext.sodalive.message.MessageRepository
@ -77,21 +53,12 @@ import kr.co.vividnext.sodalive.message.text.TextMessageWriteViewModel
import kr.co.vividnext.sodalive.message.voice.VoiceMessageViewModel import kr.co.vividnext.sodalive.message.voice.VoiceMessageViewModel
import kr.co.vividnext.sodalive.message.voice.VoiceMessageWriteViewModel import kr.co.vividnext.sodalive.message.voice.VoiceMessageWriteViewModel
import kr.co.vividnext.sodalive.mypage.MyPageViewModel import kr.co.vividnext.sodalive.mypage.MyPageViewModel
import kr.co.vividnext.sodalive.mypage.alarm.AlarmListApi
import kr.co.vividnext.sodalive.mypage.alarm.AlarmListRepository
import kr.co.vividnext.sodalive.mypage.alarm.AlarmListViewModel
import kr.co.vividnext.sodalive.mypage.auth.AuthApi import kr.co.vividnext.sodalive.mypage.auth.AuthApi
import kr.co.vividnext.sodalive.mypage.auth.AuthRepository import kr.co.vividnext.sodalive.mypage.auth.AuthRepository
import kr.co.vividnext.sodalive.mypage.block.BlockMemberViewModel
import kr.co.vividnext.sodalive.mypage.can.CanApi import kr.co.vividnext.sodalive.mypage.can.CanApi
import kr.co.vividnext.sodalive.mypage.can.CanRepository import kr.co.vividnext.sodalive.mypage.can.CanRepository
import kr.co.vividnext.sodalive.mypage.can.charge.iap.CanChargeIapViewModel import kr.co.vividnext.sodalive.mypage.can.charge.CanChargeViewModel
import kr.co.vividnext.sodalive.mypage.can.charge.pg.CanChargePgViewModel
import kr.co.vividnext.sodalive.mypage.can.coupon.CanCouponViewModel
import kr.co.vividnext.sodalive.mypage.can.payment.CanPaymentTempRepository
import kr.co.vividnext.sodalive.mypage.can.payment.CanPaymentTempViewModel
import kr.co.vividnext.sodalive.mypage.can.payment.CanPaymentViewModel import kr.co.vividnext.sodalive.mypage.can.payment.CanPaymentViewModel
import kr.co.vividnext.sodalive.mypage.can.payment.CanTempApi
import kr.co.vividnext.sodalive.mypage.can.status.CanStatusViewModel import kr.co.vividnext.sodalive.mypage.can.status.CanStatusViewModel
import kr.co.vividnext.sodalive.mypage.profile.ProfileUpdateViewModel import kr.co.vividnext.sodalive.mypage.profile.ProfileUpdateViewModel
import kr.co.vividnext.sodalive.mypage.profile.nickname.NicknameUpdateViewModel import kr.co.vividnext.sodalive.mypage.profile.nickname.NicknameUpdateViewModel
@ -104,7 +71,6 @@ import kr.co.vividnext.sodalive.mypage.service_center.ServiceCenterViewModel
import kr.co.vividnext.sodalive.network.TokenAuthenticator import kr.co.vividnext.sodalive.network.TokenAuthenticator
import kr.co.vividnext.sodalive.report.ReportApi import kr.co.vividnext.sodalive.report.ReportApi
import kr.co.vividnext.sodalive.report.ReportRepository import kr.co.vividnext.sodalive.report.ReportRepository
import kr.co.vividnext.sodalive.settings.ContentSettingsViewModel
import kr.co.vividnext.sodalive.settings.SettingsViewModel import kr.co.vividnext.sodalive.settings.SettingsViewModel
import kr.co.vividnext.sodalive.settings.event.EventApi import kr.co.vividnext.sodalive.settings.event.EventApi
import kr.co.vividnext.sodalive.settings.event.EventRepository import kr.co.vividnext.sodalive.settings.event.EventRepository
@ -119,7 +85,6 @@ import kr.co.vividnext.sodalive.settings.terms.TermsRepository
import kr.co.vividnext.sodalive.settings.terms.TermsViewModel import kr.co.vividnext.sodalive.settings.terms.TermsViewModel
import kr.co.vividnext.sodalive.user.UserApi import kr.co.vividnext.sodalive.user.UserApi
import kr.co.vividnext.sodalive.user.UserRepository import kr.co.vividnext.sodalive.user.UserRepository
import kr.co.vividnext.sodalive.user.UserViewModel
import kr.co.vividnext.sodalive.user.find_password.FindPasswordViewModel import kr.co.vividnext.sodalive.user.find_password.FindPasswordViewModel
import kr.co.vividnext.sodalive.user.login.LoginViewModel import kr.co.vividnext.sodalive.user.login.LoginViewModel
import kr.co.vividnext.sodalive.user.signup.SignUpViewModel import kr.co.vividnext.sodalive.user.signup.SignUpViewModel
@ -132,7 +97,6 @@ import org.koin.dsl.module
import retrofit2.Retrofit import retrofit2.Retrofit
import retrofit2.adapter.rxjava3.RxJava3CallAdapterFactory import retrofit2.adapter.rxjava3.RxJava3CallAdapterFactory
import retrofit2.converter.gson.GsonConverterFactory import retrofit2.converter.gson.GsonConverterFactory
import java.util.concurrent.TimeUnit
class AppDI(private val context: Context, isDebugMode: Boolean) { class AppDI(private val context: Context, isDebugMode: Boolean) {
private val baseUrl = BuildConfig.BASE_URL private val baseUrl = BuildConfig.BASE_URL
@ -155,9 +119,6 @@ class AppDI(private val context: Context, isDebugMode: Boolean) {
OkHttpClient().newBuilder() OkHttpClient().newBuilder()
.addInterceptor(logging) .addInterceptor(logging)
.authenticator(TokenAuthenticator(get())) .authenticator(TokenAuthenticator(get()))
.connectTimeout(60, TimeUnit.SECONDS)
.readTimeout(60, TimeUnit.SECONDS)
.writeTimeout(60, TimeUnit.SECONDS)
.build() .build()
} }
@ -170,16 +131,12 @@ class AppDI(private val context: Context, isDebugMode: Boolean) {
.build() .build()
} }
single { ApiBuilder().build(get(), AlarmListApi::class.java) }
single { ApiBuilder().build(get(), CanApi::class.java) } single { ApiBuilder().build(get(), CanApi::class.java) }
single { ApiBuilder().build(get(), CanTempApi::class.java) }
single { ApiBuilder().build(get(), AuthApi::class.java) } single { ApiBuilder().build(get(), AuthApi::class.java) }
single { ApiBuilder().build(get(), UserApi::class.java) } single { ApiBuilder().build(get(), UserApi::class.java) }
single { ApiBuilder().build(get(), MenuApi::class.java) }
single { ApiBuilder().build(get(), LiveApi::class.java) } single { ApiBuilder().build(get(), LiveApi::class.java) }
single { ApiBuilder().build(get(), TermsApi::class.java) } single { ApiBuilder().build(get(), TermsApi::class.java) }
single { ApiBuilder().build(get(), EventApi::class.java) } single { ApiBuilder().build(get(), EventApi::class.java) }
single { ApiBuilder().build(get(), SeriesApi::class.java) }
single { ApiBuilder().build(get(), ReportApi::class.java) } single { ApiBuilder().build(get(), ReportApi::class.java) }
single { ApiBuilder().build(get(), LiveRecommendApi::class.java) } single { ApiBuilder().build(get(), LiveRecommendApi::class.java) }
single { ApiBuilder().build(get(), ExplorerApi::class.java) } single { ApiBuilder().build(get(), ExplorerApi::class.java) }
@ -188,9 +145,6 @@ class AppDI(private val context: Context, isDebugMode: Boolean) {
single { ApiBuilder().build(get(), AudioContentApi::class.java) } single { ApiBuilder().build(get(), AudioContentApi::class.java) }
single { ApiBuilder().build(get(), FaqApi::class.java) } single { ApiBuilder().build(get(), FaqApi::class.java) }
single { ApiBuilder().build(get(), MemberTagApi::class.java) } single { ApiBuilder().build(get(), MemberTagApi::class.java) }
single { ApiBuilder().build(get(), RouletteApi::class.java) }
single { ApiBuilder().build(get(), CreatorCommunityApi::class.java) }
single { ApiBuilder().build(get(), CategoryApi::class.java) }
} }
private val viewModelModule = module { private val viewModelModule = module {
@ -199,17 +153,16 @@ class AppDI(private val context: Context, isDebugMode: Boolean) {
viewModel { TermsViewModel(get()) } viewModel { TermsViewModel(get()) }
viewModel { FindPasswordViewModel(get()) } viewModel { FindPasswordViewModel(get()) }
viewModel { MainViewModel(get(), get(), get(), get()) } viewModel { MainViewModel(get(), get(), get(), get()) }
viewModel { LiveViewModel(get(), get(), get(), get()) } viewModel { LiveViewModel(get(), get(), get()) }
viewModel { MyPageViewModel(get(), get()) } viewModel { MyPageViewModel(get(), get()) }
viewModel { CanStatusViewModel(get()) } viewModel { CanStatusViewModel(get()) }
viewModel { CanChargePgViewModel(get()) } viewModel { CanChargeViewModel(get()) }
viewModel { CanPaymentViewModel(get()) } viewModel { CanPaymentViewModel(get()) }
viewModel { CanPaymentTempViewModel(get()) }
viewModel { LiveRoomDetailViewModel(get()) } viewModel { LiveRoomDetailViewModel(get()) }
viewModel { LiveRoomCreateViewModel(get()) } viewModel { LiveRoomCreateViewModel(get()) }
viewModel { LiveTagViewModel(get()) } viewModel { LiveTagViewModel(get()) }
viewModel { LiveRoomEditViewModel(get()) } viewModel { LiveRoomEditViewModel(get()) }
viewModel { LiveRoomViewModel(get(), get(), get(), get()) } viewModel { LiveRoomViewModel(get(), get(), get()) }
viewModel { LiveRoomDonationMessageViewModel(get()) } viewModel { LiveRoomDonationMessageViewModel(get()) }
viewModel { ExplorerViewModel(get()) } viewModel { ExplorerViewModel(get()) }
viewModel { UserProfileViewModel(get(), get(), get()) } viewModel { UserProfileViewModel(get(), get(), get()) }
@ -219,32 +172,23 @@ class AppDI(private val context: Context, isDebugMode: Boolean) {
viewModel { VoiceMessageViewModel(get()) } viewModel { VoiceMessageViewModel(get()) }
viewModel { VoiceMessageWriteViewModel(get()) } viewModel { VoiceMessageWriteViewModel(get()) }
viewModel { SelectMessageRecipientViewModel(get(), get()) } viewModel { SelectMessageRecipientViewModel(get(), get()) }
viewModel { SignOutViewModel(get()) }
viewModel { NoticeViewModel(get()) } viewModel { NoticeViewModel(get()) }
viewModel { EventViewModel(get()) } viewModel { EventViewModel(get()) }
viewModel { NotificationSettingsViewModel(get()) } viewModel { NotificationSettingsViewModel(get()) }
viewModel { ContentSettingsViewModel() }
viewModel { SettingsViewModel(get()) } viewModel { SettingsViewModel(get()) }
viewModel { SeriesDetailViewModel(get(), get()) }
viewModel { SeriesListAllViewModel(get()) }
viewModel { SeriesContentAllViewModel(get()) }
viewModel { SignOutViewModel(get()) }
viewModel { TextMessageDetailViewModel(get()) } viewModel { TextMessageDetailViewModel(get()) }
viewModel { LiveReservationStatusViewModel(get()) } viewModel { LiveReservationStatusViewModel(get()) }
viewModel { AudioContentMainBannerViewModel(get()) } viewModel { AudioContentMainViewModel(get()) }
viewModel { AudioContentMainRankingViewModel(get()) }
viewModel { AudioContentMainCurationViewModel(get()) }
viewModel { AudioContentMainOrderListViewModel(get()) }
viewModel { AudioContentMainNewContentViewModel(get()) }
viewModel { AudioContentMainRecommendSeriesViewModel(get()) }
viewModel { AudioContentViewModel(get()) } viewModel { AudioContentViewModel(get()) }
viewModel { AudioContentOrderListViewModel(get()) } viewModel { AudioContentOrderListViewModel(get()) }
viewModel { AudioContentUploadViewModel(get()) } viewModel { AudioContentUploadViewModel(get()) }
viewModel { AudioContentModifyViewModel(get()) } viewModel { AudioContentModifyViewModel(get()) }
viewModel { AudioContentThemeViewModel(get()) } viewModel { AudioContentThemeViewModel(get()) }
viewModel { AudioContentDetailViewModel(get(), get(), get(), get(), get()) } viewModel { AudioContentDetailViewModel(get(), get(), get(), get()) }
viewModel { AudioContentCommentListViewModel(get()) } viewModel { AudioContentCommentListViewModel(get()) }
viewModel { AudioContentCommentReplyViewModel(get()) } viewModel { AudioContentCommentReplyViewModel(get()) }
viewModel { FollowingCreatorViewModel(get(), get()) } viewModel { FollowingCreatorViewModel(get()) }
viewModel { ServiceCenterViewModel(get()) } viewModel { ServiceCenterViewModel(get()) }
viewModel { ProfileUpdateViewModel(get()) } viewModel { ProfileUpdateViewModel(get()) }
viewModel { NicknameUpdateViewModel(get()) } viewModel { NicknameUpdateViewModel(get()) }
@ -252,47 +196,29 @@ class AppDI(private val context: Context, isDebugMode: Boolean) {
viewModel { UserProfileDonationAllViewModel(get(), get()) } viewModel { UserProfileDonationAllViewModel(get(), get()) }
viewModel { AudioContentCurationViewModel(get()) } viewModel { AudioContentCurationViewModel(get()) }
viewModel { AudioContentNewAllViewModel(get()) } viewModel { AudioContentNewAllViewModel(get()) }
viewModel { AudioContentAllByThemeViewModel(get()) }
viewModel { AudioContentRankingAllViewModel(get()) } viewModel { AudioContentRankingAllViewModel(get()) }
viewModel { RouletteSettingsViewModel(get()) }
viewModel { CreatorCommunityAllViewModel(get(), get()) }
viewModel { CreatorCommunityCommentListViewModel(get()) }
viewModel { CreatorCommunityWriteViewModel(get()) }
viewModel { CreatorCommunityModifyViewModel(get()) }
viewModel { CanCouponViewModel(get()) }
viewModel { CanChargeIapViewModel(get()) }
viewModel { AlarmListViewModel(get()) }
viewModel { BlockMemberViewModel(get()) }
viewModel { UserViewModel(get(), get()) }
viewModel { MenuConfigViewModel(get()) }
} }
private val repositoryModule = module { private val repositoryModule = module {
factory { UserRepository(get()) } factory { UserRepository(get()) }
factory { TermsRepository(get()) } factory { TermsRepository(get()) }
factory { SeriesRepository(get()) } factory { LiveRepository(get(), get()) }
factory { LiveRepository(get(), get(), get()) }
factory { EventRepository(get()) } factory { EventRepository(get()) }
factory { LiveRecommendRepository(get()) } factory { LiveRecommendRepository(get()) }
factory { AuthRepository(get()) } factory { AuthRepository(get()) }
factory { CanRepository(get()) } factory { CanRepository(get()) }
factory { CanPaymentTempRepository(get()) }
factory { LiveTagRepository(get()) } factory { LiveTagRepository(get()) }
factory { ReportRepository(get()) } factory { ReportRepository(get()) }
factory { ExplorerRepository(get()) } factory { ExplorerRepository(get()) }
factory { MessageRepository(get()) } factory { MessageRepository(get()) }
factory { NoticeRepository(get()) } factory { NoticeRepository(get()) }
factory { AudioContentRepository(get(), get(), get()) } factory { AudioContentRepository(get(), get()) }
factory { AudioContentCommentRepository(get()) } factory { AudioContentCommentRepository(get()) }
factory { PlaybackTrackingRepository(get()) } factory { PlaybackTrackingRepository(get()) }
factory { FollowingCreatorRepository(get()) } factory { FollowingCreatorRepository(get(), get()) }
factory { FaqRepository(get()) } factory { FaqRepository(get()) }
factory { MemberTagRepository(get()) } factory { MemberTagRepository(get()) }
factory { UserProfileFantalkAllViewModel(get(), get()) } factory { UserProfileFantalkAllViewModel(get(), get()) }
factory { RouletteRepository(get()) }
factory { CreatorCommunityRepository(get()) }
factory { AlarmListRepository(get()) }
factory { MenuConfigRepository(get()) }
} }
private val moduleList = listOf( private val moduleList = listOf(

View File

@ -1,174 +0,0 @@
package kr.co.vividnext.sodalive.dialog
import android.graphics.Color
import android.graphics.drawable.ColorDrawable
import android.view.LayoutInflater
import android.view.Window
import android.view.WindowManager
import android.widget.Toast
import androidx.appcompat.app.AlertDialog
import androidx.fragment.app.FragmentActivity
import coil.load
import coil.transform.RoundedCornersTransformation
import kr.co.vividnext.sodalive.R
import kr.co.vividnext.sodalive.common.LoadingDialog
import kr.co.vividnext.sodalive.common.SharedPreferenceManager
import kr.co.vividnext.sodalive.databinding.DialogMemberProfileBinding
import kr.co.vividnext.sodalive.extensions.dpToPx
import kr.co.vividnext.sodalive.report.ProfileReportDialog
import kr.co.vividnext.sodalive.report.ReportType
import kr.co.vividnext.sodalive.report.UserReportDialog
import kr.co.vividnext.sodalive.settings.notification.MemberRole
import kr.co.vividnext.sodalive.user.UserViewModel
import org.koin.core.component.KoinComponent
import org.koin.core.component.inject
class MemberProfileDialog(
private val activity: FragmentActivity,
private val layoutInflater: LayoutInflater,
private val memberId: Long,
private val screenWidth: Int
) : KoinComponent {
private val viewModel: UserViewModel by inject()
private val alertDialog: AlertDialog
val dialogView = DialogMemberProfileBinding.inflate(layoutInflater)
private lateinit var loadingDialog: LoadingDialog
init {
val dialogBuilder = AlertDialog.Builder(activity)
dialogBuilder.setView(dialogView.root)
alertDialog = dialogBuilder.create()
alertDialog.setCancelable(false)
alertDialog.requestWindowFeature(Window.FEATURE_NO_TITLE)
alertDialog.window?.setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT))
setupView()
bindData()
}
fun dismiss() {
alertDialog.dismiss()
}
fun show() {
viewModel.getMemberProfile(memberId) {
alertDialog.show()
val lp = WindowManager.LayoutParams()
lp.copyFrom(alertDialog.window?.attributes)
lp.width = screenWidth - (40.dpToPx()).toInt()
lp.height = WindowManager.LayoutParams.WRAP_CONTENT
alertDialog.window?.attributes = lp
}
}
private fun setupView() {
loadingDialog = LoadingDialog(activity, layoutInflater)
val profileLP = dialogView.ivProfile.layoutParams
profileLP.width = activity.resources.displayMetrics.widthPixels - (66.7f.dpToPx()).toInt()
profileLP.height = activity.resources.displayMetrics.widthPixels - (66.7f.dpToPx()).toInt()
dialogView.ivProfile.layoutParams = profileLP
dialogView.ivClose.setOnClickListener { dismiss() }
}
private fun bindData() {
viewModel.isLoading.observe(activity) {
if (it) {
loadingDialog.show(screenWidth)
} else {
loadingDialog.dismiss()
}
}
viewModel.toastLiveData.observe(activity) {
Toast.makeText(activity, it, Toast.LENGTH_SHORT).show()
}
viewModel.memberProfile.observe(activity) {
dialogView.ivProfile.load(it.profileImageUrl) {
crossfade(true)
placeholder(R.drawable.ic_place_holder)
transformations(RoundedCornersTransformation(8.dpToPx()))
}
dialogView.tvNickname.text = it.nickname
dialogView.tvBlockMember.text = if (it.isBlocked) {
"차단 해제"
} else {
"차단"
}
dialogView.tvBlockMember.setOnClickListener { _ ->
if (it.isBlocked) {
viewModel.memberUnBlock(it.memberId) { dismiss() }
} else {
showMemberBlockDialog(it.memberId, it.nickname)
}
}
dialogView.tvReportMember.setOnClickListener { _ ->
showMemberReportDialog(it.memberId)
}
dialogView.tvReportMemberProfile.setOnClickListener { _ ->
showProfileReportDialog(it.memberId)
}
}
}
private fun showMemberBlockDialog(memberId: Long, nickname: String) {
val message = if (SharedPreferenceManager.role == MemberRole.CREATOR.name) {
"""
${nickname}님을 차단하시겠습니까?
사용자를 차단하면 사용자는 아래 기능이 제한됩니다.
- 내가 개설한 라이브 입장 불가
- 나에게 메시지 보내기 불가
- 채널의 팬Talk 작성불가
""".trimIndent()
} else {
"""
${nickname}님을 차단하시겠습니까?
- 사용자를 차단하면 '차단한 사용자의 라이브 채팅' 보이지 않습니다.
""".trimIndent()
}
val dialog = android.app.AlertDialog.Builder(activity)
dialog.setTitle("사용자 차단")
dialog.setMessage(message)
dialog.setPositiveButton("차단") { _, _ ->
viewModel.memberBlock(memberId) { dismiss() }
}
dialog.setNegativeButton("취소") { _, _ -> }
dialog.show()
}
private fun showMemberReportDialog(memberId: Long) {
val dialog = UserReportDialog(activity, layoutInflater) {
viewModel.report(
type = ReportType.USER,
userId = memberId,
reason = it
) { dismiss() }
}
dialog.show(screenWidth)
}
private fun showProfileReportDialog(memberId: Long) {
val dialog = ProfileReportDialog(activity, layoutInflater) {
viewModel.report(
type = ReportType.PROFILE,
userId = memberId
) { dismiss() }
}
dialog.show(screenWidth)
}
}

View File

@ -1,45 +0,0 @@
package kr.co.vividnext.sodalive.dialog
import android.app.TimePickerDialog
import android.content.Context
import android.widget.TimePicker
class SodaLiveTimePickerDialog(
context: Context,
themeResId: Int,
private val onTimeSetListener: OnTimeSetListener,
hourOfDay: Int,
minute: Int,
is24HourView: Boolean
) : TimePickerDialog(context, themeResId, null, hourOfDay, minute, is24HourView) {
private var timePicker: TimePicker? = null
init {
this.setTitle("Select Time")
setOnShowListener {
timePicker = window?.findViewById(
context.resources.getIdentifier(
"android:id/timePicker",
null,
null
)
)
timePicker?.apply {
setIs24HourView(is24HourView)
setOnTimeChangedListener { _, _, minute ->
// Snap minute to nearest quarter (0, 15, 30, 45)
val snappedMinute = minute / 15 * 15
if (snappedMinute != minute) {
this.minute = snappedMinute
}
}
}
getButton(BUTTON_POSITIVE).setOnClickListener {
timePicker?.let { picker ->
onTimeSetListener.onTimeSet(picker, picker.hour, picker.minute)
}
dismiss()
}
}
}
}

View File

@ -121,12 +121,8 @@ class ExplorerAdapter(
) )
override fun onBindViewHolder(holder: ViewHolder, position: Int) { override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val item = items[position]
if (item.creators.isNotEmpty()) {
holder.bind(items[position]) holder.bind(items[position])
} }
}
override fun getItemCount() = items.size override fun getItemCount() = items.size

View File

@ -1,14 +1,11 @@
package kr.co.vividnext.sodalive.explorer package kr.co.vividnext.sodalive.explorer
import androidx.annotation.Keep
import com.google.gson.annotations.SerializedName import com.google.gson.annotations.SerializedName
@Keep
data class GetExplorerResponse( data class GetExplorerResponse(
@SerializedName("sections") val sections: List<GetExplorerSectionResponse> @SerializedName("sections") val sections: List<GetExplorerSectionResponse>
) )
@Keep
data class GetExplorerSectionResponse( data class GetExplorerSectionResponse(
@SerializedName("title") val title: String, @SerializedName("title") val title: String,
@SerializedName("coloredTitle") val coloredTitle: String?, @SerializedName("coloredTitle") val coloredTitle: String?,
@ -17,7 +14,6 @@ data class GetExplorerSectionResponse(
@SerializedName("creators") val creators: List<GetExplorerSectionCreatorResponse> @SerializedName("creators") val creators: List<GetExplorerSectionCreatorResponse>
) )
@Keep
data class GetExplorerSectionCreatorResponse( data class GetExplorerSectionCreatorResponse(
@SerializedName("id") val id: Long, @SerializedName("id") val id: Long,
@SerializedName("nickname") val nickname: String, @SerializedName("nickname") val nickname: String,

View File

@ -1,42 +0,0 @@
package kr.co.vividnext.sodalive.explorer.profile
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.LinearLayout
import com.google.android.material.bottomsheet.BottomSheetDialogFragment
import kr.co.vividnext.sodalive.R
class CreatorFollowNotifyFragment(
private val onClickNotifyAll: () -> Unit,
private val onClickNotifyNone: () -> Unit,
private val onClickUnFollow: () -> Unit,
) : BottomSheetDialogFragment() {
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View = inflater.inflate(
R.layout.fragment_creator_follow_notify,
container,
false
)
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
view.findViewById<LinearLayout>(R.id.ll_notify_all).setOnClickListener {
onClickNotifyAll()
dialog?.dismiss()
}
view.findViewById<LinearLayout>(R.id.ll_notify_none).setOnClickListener {
onClickNotifyNone()
dialog?.dismiss()
}
view.findViewById<LinearLayout>(R.id.ll_unfollow).setOnClickListener {
onClickUnFollow()
dialog?.dismiss()
}
}
}

Some files were not shown because too many files have changed in this diff Show More