From bb1e260a4ccdb45fdb6a2b17a45af1e2d71fe8a2 Mon Sep 17 00:00:00 2001 From: klaus Date: Wed, 9 Aug 2023 13:52:26 +0900 Subject: [PATCH] =?UTF-8?q?=ED=91=B8=EC=8B=9C=EB=A9=94=EC=8B=9C=EC=A7=80?= =?UTF-8?q?=20=ED=84=B0=EC=B9=98=20=EC=95=A1=EC=85=98=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/kr/co/vividnext/sodalive/di/AppDI.kt | 2 +- .../vividnext/sodalive/live/LiveFragment.kt | 20 ++ .../sodalive/main/EventPopupDialogFragment.kt | 84 +++++++ .../vividnext/sodalive/main/MainActivity.kt | 206 +++++++++++++++++- .../vividnext/sodalive/main/MainViewModel.kt | 64 ++++++ .../sodalive/splash/SplashActivity.kt | 8 +- app/src/main/res/layout/activity_main.xml | 68 ++++++ .../layout/fragment_event_popup_dialog.xml | 40 ++++ 8 files changed, 486 insertions(+), 6 deletions(-) create mode 100644 app/src/main/java/kr/co/vividnext/sodalive/main/EventPopupDialogFragment.kt create mode 100644 app/src/main/res/layout/fragment_event_popup_dialog.xml diff --git a/app/src/main/java/kr/co/vividnext/sodalive/di/AppDI.kt b/app/src/main/java/kr/co/vividnext/sodalive/di/AppDI.kt index ce6777c..cb6d148 100644 --- a/app/src/main/java/kr/co/vividnext/sodalive/di/AppDI.kt +++ b/app/src/main/java/kr/co/vividnext/sodalive/di/AppDI.kt @@ -141,7 +141,7 @@ class AppDI(private val context: Context, isDebugMode: Boolean) { viewModel { SignUpViewModel(get()) } viewModel { TermsViewModel(get()) } viewModel { FindPasswordViewModel(get()) } - viewModel { MainViewModel(get()) } + viewModel { MainViewModel(get(), get(), get(), get()) } viewModel { LiveViewModel(get(), get(), get()) } viewModel { MyPageViewModel(get(), get()) } viewModel { CanStatusViewModel(get()) } diff --git a/app/src/main/java/kr/co/vividnext/sodalive/live/LiveFragment.kt b/app/src/main/java/kr/co/vividnext/sodalive/live/LiveFragment.kt index 89d1e82..ca0b54e 100644 --- a/app/src/main/java/kr/co/vividnext/sodalive/live/LiveFragment.kt +++ b/app/src/main/java/kr/co/vividnext/sodalive/live/LiveFragment.kt @@ -22,6 +22,7 @@ import com.zhpan.indicator.enums.IndicatorSlideMode import com.zhpan.indicator.enums.IndicatorStyle import kr.co.vividnext.sodalive.R import kr.co.vividnext.sodalive.audio_content.AudioContentPlayService +import kr.co.vividnext.sodalive.audio_content.detail.AudioContentDetailActivity import kr.co.vividnext.sodalive.base.BaseFragment import kr.co.vividnext.sodalive.common.Constants import kr.co.vividnext.sodalive.common.LoadingDialog @@ -94,6 +95,25 @@ class LiveFragment : BaseFragment(FragmentLiveBinding::infl message = "라이브를 불러오고 있습니다." viewModel.getSummary() + + try { + val roomId = requireArguments().getLong(Constants.EXTRA_ROOM_ID) + val channelId = requireArguments().getLong(Constants.EXTRA_USER_ID) + val audioContentId = requireArguments().getLong(Constants.EXTRA_AUDIO_CONTENT_ID) + + if (roomId > 0) { + enterLiveRoom(roomId) + } else if (channelId > 0) { + val nextIntent = Intent(requireContext(), UserProfileActivity::class.java) + nextIntent.putExtra(Constants.EXTRA_USER_ID, channelId) + startActivity(nextIntent) + } else if (audioContentId > 0) { + val nextIntent = Intent(requireContext(), AudioContentDetailActivity::class.java) + nextIntent.putExtra(Constants.EXTRA_AUDIO_CONTENT_ID, audioContentId) + startActivity(nextIntent) + } + } catch (_: IllegalStateException) { + } } private fun setupView() { diff --git a/app/src/main/java/kr/co/vividnext/sodalive/main/EventPopupDialogFragment.kt b/app/src/main/java/kr/co/vividnext/sodalive/main/EventPopupDialogFragment.kt new file mode 100644 index 0000000..09965d3 --- /dev/null +++ b/app/src/main/java/kr/co/vividnext/sodalive/main/EventPopupDialogFragment.kt @@ -0,0 +1,84 @@ +package kr.co.vividnext.sodalive.main + +import android.app.Dialog +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.FrameLayout +import android.widget.ImageView +import android.widget.LinearLayout +import android.widget.TextView +import coil.load +import coil.transform.RoundedCornersTransformation +import com.google.android.material.bottomsheet.BottomSheetBehavior +import com.google.android.material.bottomsheet.BottomSheetDialog +import com.google.android.material.bottomsheet.BottomSheetDialogFragment +import kr.co.vividnext.sodalive.R +import kr.co.vividnext.sodalive.common.SharedPreferenceManager +import kr.co.vividnext.sodalive.extensions.dpToPx +import kr.co.vividnext.sodalive.settings.event.EventItem + +class EventPopupDialogFragment( + private val screenWidth: Int, + private val eventItem: EventItem, + private val onClick: () -> Unit +) : BottomSheetDialogFragment() { + + override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { + val dialog = super.onCreateDialog(savedInstanceState) + + dialog.setOnShowListener { + val d = it as BottomSheetDialog + val bottomSheet = d.findViewById( + com.google.android.material.R.id.design_bottom_sheet + ) + if (bottomSheet != null) { + BottomSheetBehavior.from(bottomSheet).state = BottomSheetBehavior.STATE_EXPANDED + } + } + + return dialog + } + + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ): View = inflater.inflate(R.layout.fragment_event_popup_dialog, container, false) + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + view.findViewById(R.id.tv_close).setOnClickListener { + dialog?.dismiss() + } + + view.findViewById(R.id.tv_close_always).setOnClickListener { + SharedPreferenceManager.notShowingEventPopupId = eventItem.id + dialog?.dismiss() + } + + val imageView = view.findViewById(R.id.iv_event_popup) + val layoutParams = imageView.layoutParams as LinearLayout.LayoutParams + + layoutParams.width = screenWidth + layoutParams.height = screenWidth + + imageView.load(eventItem.popupImageUrl) { + crossfade(true) + transformations( + RoundedCornersTransformation( + topLeft = 16.7f.dpToPx(), + topRight = 16.7f.dpToPx(), + bottomLeft = 0f, + bottomRight = 0f + ) + ) + } + imageView.layoutParams = layoutParams + + imageView.setOnClickListener { + dialog?.dismiss() + onClick() + } + } +} diff --git a/app/src/main/java/kr/co/vividnext/sodalive/main/MainActivity.kt b/app/src/main/java/kr/co/vividnext/sodalive/main/MainActivity.kt index 934f657..076cf90 100644 --- a/app/src/main/java/kr/co/vividnext/sodalive/main/MainActivity.kt +++ b/app/src/main/java/kr/co/vividnext/sodalive/main/MainActivity.kt @@ -1,26 +1,41 @@ package kr.co.vividnext.sodalive.main import android.Manifest +import android.content.BroadcastReceiver +import android.content.Context +import android.content.Intent +import android.content.IntentFilter import android.content.res.ColorStateList import android.os.Build import android.os.Bundle +import android.os.Handler +import android.os.Looper +import android.view.View import androidx.core.content.ContextCompat import androidx.core.content.res.ResourcesCompat +import coil.load +import coil.transform.RoundedCornersTransformation import com.google.firebase.messaging.FirebaseMessaging import com.gun0912.tedpermission.PermissionListener import com.gun0912.tedpermission.normal.TedPermission import com.orhanobut.logger.Logger import kr.co.vividnext.sodalive.R +import kr.co.vividnext.sodalive.audio_content.AudioContentPlayService +import kr.co.vividnext.sodalive.audio_content.detail.AudioContentDetailActivity import kr.co.vividnext.sodalive.audio_content.main.AudioContentMainFragment 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.ActivityMainBinding import kr.co.vividnext.sodalive.databinding.ItemMainTabBinding import kr.co.vividnext.sodalive.explorer.ExplorerFragment +import kr.co.vividnext.sodalive.explorer.profile.UserProfileActivity +import kr.co.vividnext.sodalive.extensions.dpToPx import kr.co.vividnext.sodalive.live.LiveFragment import kr.co.vividnext.sodalive.message.MessageFragment import kr.co.vividnext.sodalive.mypage.MyPageFragment +import kr.co.vividnext.sodalive.settings.event.EventDetailActivity import kr.co.vividnext.sodalive.settings.notification.NotificationSettingsDialog import org.koin.android.ext.android.inject @@ -32,17 +47,93 @@ class MainActivity : BaseActivity(ActivityMainBinding::infl private lateinit var loadingDialog: LoadingDialog private lateinit var notificationSettingsDialog: NotificationSettingsDialog + private val handler = Handler(Looper.getMainLooper()) + private val audioContentReceiver = AudioContentReceiver() + + override fun onNewIntent(intent: Intent) { + super.onNewIntent(intent) + + val bundle = intent.getBundleExtra(Constants.EXTRA_DATA) + if (bundle != null) { + try { + val roomId = bundle.getLong(Constants.EXTRA_ROOM_ID) + val channelId = bundle.getLong(Constants.EXTRA_USER_ID) + val audioContentId = bundle.getLong(Constants.EXTRA_AUDIO_CONTENT_ID) + val isLiveReservation = bundle.getBoolean(Constants.EXTRA_LIVE_RESERVATION_RESPONSE) + + if (roomId > 0) { + if (isLiveReservation) { + liveFragment.reservationRoom(roomId) + } else { + handler.postDelayed({ + liveFragment.enterLiveRoom(roomId) + }, 500) + } + } else if (channelId > 0) { + val nextIntent = Intent(applicationContext, UserProfileActivity::class.java) + nextIntent.putExtra(Constants.EXTRA_USER_ID, channelId) + startActivity(nextIntent) + } else if (audioContentId > 0) { + val nextIntent = Intent( + applicationContext, + AudioContentDetailActivity::class.java + ) + nextIntent.putExtra(Constants.EXTRA_AUDIO_CONTENT_ID, audioContentId) + startActivity(nextIntent) + } + } catch (_: IllegalStateException) { + } + } + + checkReceivedMessage(intent) + } + override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) checkPermissions() pushTokenUpdate() getMemberInfo() + getEventPopup() + checkReceivedMessage(intent) + } + + override fun onResume() { + super.onResume() + val intentFilter = IntentFilter(Constants.ACTION_MAIN_AUDIO_CONTENT_RECEIVER) + registerReceiver(audioContentReceiver, intentFilter) + + startService( + Intent(this, AudioContentPlayService::class.java).apply { + action = AudioContentPlayService.MusicAction.INIT.name + } + ) + } + + override fun onPause() { + super.onPause() + unregisterReceiver(audioContentReceiver) + } + + private fun checkReceivedMessage(intent: Intent) { + handler.postDelayed({ + val messageId = + intent.getBundleExtra(Constants.EXTRA_DATA)?.getLong(Constants.EXTRA_MESSAGE_ID) + if (messageId != null && messageId > 0) { + changeFragment(MainViewModel.CurrentTab.MESSAGE) + setTabSelected(binding.tabSuda, isSelected = false) + setTabSelected(binding.tabExplorer, isSelected = false) + setTabSelected(binding.tabMessage, isSelected = true) + setTabSelected(binding.tabMy, isSelected = false) + } + }, 500) } override fun setupView() { loadingDialog = LoadingDialog(this, layoutInflater) - liveFragment = LiveFragment() + liveFragment = LiveFragment().apply { + arguments = intent.getBundleExtra(Constants.EXTRA_DATA) + } notificationSettingsDialog = NotificationSettingsDialog( this, @@ -245,4 +336,117 @@ class MainActivity : BaseActivity(ActivityMainBinding::infl notificationSettingsDialog.show(screenWidth) } } + + private fun getEventPopup() { + viewModel.getEventPopup { + if (SharedPreferenceManager.notShowingEventPopupId != it.id) { + val eventPopupDialog = EventPopupDialogFragment( + screenWidth = screenWidth, + eventItem = it + ) { + startActivity( + Intent( + applicationContext, + EventDetailActivity::class.java + ).apply { + putExtra(Constants.EXTRA_EVENT, it) + } + ) + } + + eventPopupDialog.show(supportFragmentManager, eventPopupDialog.tag) + } + } + } + + inner class AudioContentReceiver : BroadcastReceiver() { + override fun onReceive(context: Context?, intent: Intent?) { + val contentId = intent?.getLongExtra(Constants.EXTRA_AUDIO_CONTENT_ID, 0) + val title = intent?.getStringExtra(Constants.EXTRA_AUDIO_CONTENT_TITLE) + val nickname = intent?.getStringExtra(Constants.EXTRA_NICKNAME) + val coverImageUrl = intent?.getStringExtra( + Constants.EXTRA_AUDIO_CONTENT_COVER_IMAGE_URL + ) + + val isPlaying = intent?.getBooleanExtra(Constants.EXTRA_AUDIO_CONTENT_PLAYING, false) + val isShowing = intent?.getBooleanExtra(Constants.EXTRA_AUDIO_CONTENT_SHOWING, false) + + if (isShowing == true) { + binding.rlMiniPlayer.visibility = View.VISIBLE + if (contentId != null && contentId > 0) { + binding.rlMiniPlayer.setOnClickListener { + startActivity( + Intent(applicationContext, AudioContentDetailActivity::class.java) + .apply { + putExtra(Constants.EXTRA_AUDIO_CONTENT_ID, contentId) + } + ) + } + } + + if (isPlaying == true) { + binding.ivPlayOrPause.setImageResource(R.drawable.ic_noti_pause) + binding.ivPlayOrPause.setOnClickListener { + startService( + Intent( + this@MainActivity, + AudioContentPlayService::class.java + ).apply { + action = AudioContentPlayService.MusicAction.PAUSE.name + } + ) + } + } else { + binding.ivPlayOrPause.setImageResource(R.drawable.ic_noti_play) + binding.ivPlayOrPause.setOnClickListener { + startService( + Intent( + this@MainActivity, + AudioContentPlayService::class.java + ).apply { + action = AudioContentPlayService.MusicAction.PLAY.name + } + ) + } + } + + binding.ivStop.setOnClickListener { + startService( + Intent( + this@MainActivity, + AudioContentPlayService::class.java + ).apply { + action = AudioContentPlayService.MusicAction.STOP.name + } + ) + } + + if (!title.isNullOrBlank()) { + binding.tvTitle.text = title + } + + if (!nickname.isNullOrBlank()) { + binding.tvNickname.text = nickname + } + + if (!coverImageUrl.isNullOrBlank()) { + binding.ivCover.load(coverImageUrl) { + crossfade(true) + placeholder(R.drawable.bg_placeholder) + transformations(RoundedCornersTransformation(5.3f.dpToPx())) + } + } + } else { + handler.post { + binding.ivPlayOrPause.setImageResource(0) + binding.ivCover.setImageResource(0) + binding.tvTitle.text = "" + binding.tvNickname.text = "" + binding.rlMiniPlayer.visibility = View.GONE + binding.ivPlayOrPause.setOnClickListener {} + viewModel.addAllPlaybackTracking() + } + } + } + } } diff --git a/app/src/main/java/kr/co/vividnext/sodalive/main/MainViewModel.kt b/app/src/main/java/kr/co/vividnext/sodalive/main/MainViewModel.kt index 2996929..aec7cd5 100644 --- a/app/src/main/java/kr/co/vividnext/sodalive/main/MainViewModel.kt +++ b/app/src/main/java/kr/co/vividnext/sodalive/main/MainViewModel.kt @@ -5,13 +5,22 @@ import androidx.lifecycle.MutableLiveData import com.google.gson.annotations.SerializedName import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers import io.reactivex.rxjava3.schedulers.Schedulers +import kr.co.vividnext.sodalive.audio_content.AddAllPlaybackTrackingRequest +import kr.co.vividnext.sodalive.audio_content.AudioContentRepository +import kr.co.vividnext.sodalive.audio_content.PlaybackTrackingData +import kr.co.vividnext.sodalive.audio_content.PlaybackTrackingRepository import kr.co.vividnext.sodalive.base.BaseViewModel import kr.co.vividnext.sodalive.common.SharedPreferenceManager +import kr.co.vividnext.sodalive.settings.event.EventItem +import kr.co.vividnext.sodalive.settings.event.EventRepository import kr.co.vividnext.sodalive.settings.notification.UpdateNotificationSettingRequest import kr.co.vividnext.sodalive.user.UserRepository class MainViewModel( private val userRepository: UserRepository, + private val eventRepository: EventRepository, + private val audioContentRepository: AudioContentRepository, + private val playbackTrackingRepository: PlaybackTrackingRepository ) : BaseViewModel() { enum class CurrentTab { @SerializedName("CONTENT") @@ -99,4 +108,59 @@ class MainViewModel( ) ) } + + fun addAllPlaybackTracking() { + val playbackTrackingList = playbackTrackingRepository.getAllPlaybackTracking() + val trackingDataList = playbackTrackingList + .filter { it.endPosition != null } + .filter { + if (it.isFree) { + // 무료 콘텐츠의 경우 + // 러닝타임의 30% 이상 재생된 데이터 + it.endPosition!! - it.startPosition > it.totalDuration * 0.3 + } else { + // 유료 콘텐츠의 경우 + // 러닝타임의 20% 이상 재생된 데이터 + it.endPosition!! - it.startPosition > it.totalDuration * 0.2 + } + } + .map { + PlaybackTrackingData(it.contentId, it.playDateTime, it.isPreview) + } + + if (trackingDataList.isNotEmpty()) { + compositeDisposable.add( + audioContentRepository.addAllPlaybackTracking( + request = AddAllPlaybackTrackingRequest(trackingDataList = trackingDataList), + token = "Bearer ${SharedPreferenceManager.token}" + ) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe( + { + if (it.success) { + playbackTrackingRepository.removeAllPlaybackTracking() + } + }, + {} + ) + ) + } + } + + fun getEventPopup(onSuccess: (EventItem) -> Unit) { + compositeDisposable.add( + eventRepository.getEventPopup(token = "Bearer ${SharedPreferenceManager.token}") + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe( + { + if (it.success && it.data != null) { + onSuccess(it.data) + } + }, + {} + ) + ) + } } diff --git a/app/src/main/java/kr/co/vividnext/sodalive/splash/SplashActivity.kt b/app/src/main/java/kr/co/vividnext/sodalive/splash/SplashActivity.kt index 8bbba0a..6b1c84e 100644 --- a/app/src/main/java/kr/co/vividnext/sodalive/splash/SplashActivity.kt +++ b/app/src/main/java/kr/co/vividnext/sodalive/splash/SplashActivity.kt @@ -183,8 +183,8 @@ class SplashActivity : BaseActivity(ActivitySplashBinding startActivity( Intent(applicationContext, MainActivity::class.java).apply { putExtra(Constants.EXTRA_DATA, extras) - intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP) - intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP) + addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP) + addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP) } ) finish() @@ -196,8 +196,8 @@ class SplashActivity : BaseActivity(ActivitySplashBinding startActivity( Intent(applicationContext, LoginActivity::class.java).apply { putExtra(Constants.EXTRA_DATA, extras) - intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP) - intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP) + addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP) + addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP) } ) finish() diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index 454b926..cd06738 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -1,6 +1,7 @@ @@ -12,6 +13,73 @@ android:layout_above="@+id/ll_tab" android:layout_alignParentTop="true" /> + + + + + + + + + + + + + + + + + + + + + + + + + + + +