Skip to content
This repository has been archived by the owner on May 8, 2023. It is now read-only.

1.3 Feature/uploading #224

Merged
merged 1 commit into from
Mar 24, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,9 @@ dependencies {
implementation 'com.google.firebase:firebase-crashlytics-ktx'
implementation 'com.google.firebase:firebase-messaging-ktx'

// Background work using WorkManager
implementation "androidx.work:work-runtime-ktx:2.5.0" // Kotlin + coroutines

// Leak analysis
debugImplementation Libs.leakcanary_android

Expand Down
20 changes: 11 additions & 9 deletions app/src/main/java/com/laixer/swabbr/Modules.kt
Original file line number Diff line number Diff line change
Expand Up @@ -3,28 +3,26 @@ package com.laixer.swabbr
import com.google.firebase.analytics.FirebaseAnalytics
import com.google.firebase.crashlytics.FirebaseCrashlytics
import com.google.firebase.messaging.FirebaseMessaging
import com.laixer.swabbr.utils.cache.Cache
import com.laixer.swabbr.data.api.*
import com.laixer.swabbr.data.cache.*
import com.laixer.swabbr.data.datasource.*
import com.laixer.swabbr.data.interfaces.*
import com.laixer.swabbr.data.repository.*
import com.laixer.swabbr.domain.interfaces.*
import com.laixer.swabbr.domain.usecase.*
import com.laixer.swabbr.services.okhttp.AuthInterceptor
import com.laixer.swabbr.presentation.auth.AuthViewModel
import com.laixer.swabbr.services.users.UserManager
import com.laixer.swabbr.presentation.likeoverview.LikeOverviewViewModel
import com.laixer.swabbr.presentation.profile.ProfileViewModel
import com.laixer.swabbr.presentation.reaction.list.ReactionListViewModel
import com.laixer.swabbr.presentation.reaction.playback.ReactionViewModel
import com.laixer.swabbr.presentation.reaction.recording.RecordReactionViewModel
import com.laixer.swabbr.presentation.search.SearchViewModel
import com.laixer.swabbr.presentation.vlogs.list.VlogListViewModel
import com.laixer.swabbr.presentation.vlogs.playback.VlogViewModel
import com.laixer.swabbr.presentation.vlogs.recording.VlogRecordingViewModel
import com.laixer.swabbr.services.moshi.buildWithCustomAdapters
import com.laixer.swabbr.services.okhttp.AuthInterceptor
import com.laixer.swabbr.services.okhttp.CacheInterceptor
import com.laixer.swabbr.services.users.UserManager
import com.laixer.swabbr.utils.cache.Cache
import com.squareup.moshi.Moshi
import io.reactivex.schedulers.Schedulers
import okhttp3.OkHttpClient
Expand Down Expand Up @@ -59,6 +57,7 @@ private val loadFeature by lazy {
)
}

// TODO This means our firebase instance is injectable!
val firebaseModule: Module = module {
single { FirebaseCrashlytics.getInstance() }
single { FirebaseAnalytics.getInstance(androidContext()) }
Expand Down Expand Up @@ -88,11 +87,9 @@ val viewModelModule: Module = module {
}
viewModel { VlogListViewModel(usersVlogsUseCase = get(), vlogUseCase = get()) }
viewModel { VlogViewModel(authUserUseCase = get(), reactionsUseCase = get(), vlogUseCase = get()) }
viewModel { VlogRecordingViewModel(mHttpClient = get(), vlogUseCase = get()) }
viewModel { SearchViewModel(usersUseCase = get(), followUseCase = get()) }
viewModel { ReactionViewModel(reactionsUseCase = get()) }
viewModel { ReactionListViewModel(reactionsUseCase = get()) }
viewModel { RecordReactionViewModel(mHttpClient = get(), reactionsUseCase = get()) }
}

val useCaseModule: Module = module {
Expand Down Expand Up @@ -152,9 +149,14 @@ val networkModule: Module = module {
.addInterceptor(get<AuthInterceptor>())
.addInterceptor(get<CacheInterceptor>())
.addNetworkInterceptor(HttpLoggingInterceptor().apply {
this.level = HttpLoggingInterceptor.Level.BASIC // TODO Put back
this.level = HttpLoggingInterceptor.Level.BASIC
})
.cache(okhttp3.Cache(File(androidContext().cacheDir, "http-cache"), 10 * 1024 * 1024)) // 10Mb cache TODO Do we want this?
.cache(
okhttp3.Cache(
File(androidContext().cacheDir, "http-cache"),
10 * 1024 * 1024
)
) // 10Mb cache TODO Do we want this?
.connectTimeout(5, TimeUnit.SECONDS) // TODO Fix for production
.readTimeout(300, TimeUnit.SECONDS)
.callTimeout(300, TimeUnit.SECONDS)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package com.laixer.swabbr.presentation.auth

import android.content.Context
import androidx.lifecycle.MutableLiveData
import androidx.work.WorkManager
import com.google.android.gms.tasks.Tasks.await
import com.google.firebase.messaging.FirebaseMessaging
import com.laixer.swabbr.utils.resources.Resource
Expand All @@ -11,10 +13,13 @@ import com.laixer.swabbr.domain.usecase.AuthUseCase
import com.laixer.swabbr.presentation.model.RegistrationItem
import com.laixer.swabbr.presentation.model.mapToDomain
import com.laixer.swabbr.presentation.abstraction.ViewModelBase
import com.laixer.swabbr.services.uploading.ReactionUploadWorker
import com.laixer.swabbr.services.uploading.VlogUploadWorker
import com.laixer.swabbr.services.users.UserManager
import io.reactivex.Single
import io.reactivex.schedulers.Schedulers
import java.util.*
import kotlin.coroutines.coroutineContext

/**
* View model for managing user login, logout and registration.
Expand Down Expand Up @@ -122,13 +127,23 @@ open class AuthViewModel constructor(
* Logs the user out. Note that this will also disable any
* future notifications through firebase.
*/
fun logout() =
fun logout(context: Context) {
// Scoped function to also cancel existing jobs.
fun onLogout() {
// First cancel, then logout. These jobs expect us to be logged in.
WorkManager.getInstance(context).cancelAllWorkByTag(ReactionUploadWorker.WORK_TAG)
WorkManager.getInstance(context).cancelAllWorkByTag(VlogUploadWorker.WORK_TAG)

userManager.logout()
}

compositeDisposable.add(authUseCase
.logout()
.subscribeOn(Schedulers.io())
.subscribe(
{ userManager.logout() },
{ userManager.logout() }
{ onLogout() },
{ onLogout() }
)
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,7 @@ class ProfileDetailsFragment(private val userId: UUID) : AuthFragment() {
button_profile_details_save.setOnClickListener { confirmChanges() }
button_profile_logout.setOnClickListener {
// Perform logout operation which take us back to the login screen.
authVm.logout()
authVm.logout(requireContext())
}

// Setup spinner values
Expand Down
Original file line number Diff line number Diff line change
@@ -1,24 +1,24 @@
package com.laixer.swabbr.presentation.reaction.recording

import android.media.MediaScannerConnection
import android.os.Bundle
import androidx.lifecycle.lifecycleScope
import androidx.navigation.fragment.navArgs
import com.laixer.swabbr.extensions.goBack
import com.laixer.swabbr.presentation.recording.RecordVideoWithPreviewFragment
import com.laixer.swabbr.presentation.types.VideoRecordingState
import com.laixer.swabbr.services.uploading.ReactionUploadWorker
import kotlinx.android.synthetic.main.fragment_record_video_minmax.*
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import org.koin.androidx.viewmodel.ext.android.viewModel
import java.time.Duration
import java.util.*


/**
* Fragment for recording a reaction.
*/
class RecordReactionFragment : RecordVideoWithPreviewFragment() {
private val vm: RecordReactionViewModel by viewModel()

private val args: RecordReactionFragmentArgs by navArgs()
private val targetVlogId: UUID by lazy { UUID.fromString(args.targetVlogId) }
Expand Down Expand Up @@ -52,12 +52,25 @@ class RecordReactionFragment : RecordVideoWithPreviewFragment() {
override fun onPreviewConfirmed() {
super.onPreviewConfirmed()

// Dispatch the post reaction operation.
vm.postReaction(
// TODO This doesn't seem to do anything currently
// Broadcasts the media file to the rest of the system.
// Note that does not broadcast to the media gallery,
// which it should be doing.
MediaScannerConnection.scanFile(
requireContext(),
arrayOf(outputFile.absolutePath),
arrayOf(VIDEO_MIME_TYPE),
null
)

// FUTURE: Implement isPrivate
// Dispatch the uploading process
ReactionUploadWorker.enqueue(
context = requireContext(),
isPrivate = false,
userId = getSelfId(),
videoFile = outputFile,
targetVlogId = targetVlogId,
videoFile = outputFile
isPrivate = false
)

// Go back, which should take us back to the vlog we are posting to.
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import android.content.Context
import android.hardware.camera2.*
import android.media.MediaRecorder
import android.media.MediaScannerConnection
import android.net.Uri
import android.os.Bundle
import android.os.Handler
import android.os.HandlerThread
Expand Down Expand Up @@ -215,9 +216,7 @@ abstract class RecordVideoFragment : RecordVideoInnerMethods() {
/**
* Override this method to listen to [state] changes.
*/
protected open fun onStateChanged(state: VideoRecordingState) {
Log.d(TAG, "State changed to ${state.value}")
}
protected open fun onStateChanged(state: VideoRecordingState) { }

/**
* Called when we can't obtain the required permissions for whatever reason.
Expand Down Expand Up @@ -396,15 +395,6 @@ abstract class RecordVideoFragment : RecordVideoInnerMethods() {
recorderSurface?.release()
tryCleanupCameraResources()

// Broadcasts the media file to the rest of the system.
// Note that does not broadcast to the media gallery.
MediaScannerConnection.scanFile(
requireContext(),
arrayOf(outputFile.absolutePath),
arrayOf(VIDEO_MIME_TYPE),
null
)

state.postValue(VideoRecordingState.DONE_RECORDING)
} catch (e: Exception) {
Log.e(TAG, "Error while trying to stop recording", e)
Expand Down Expand Up @@ -521,9 +511,9 @@ abstract class RecordVideoFragment : RecordVideoInnerMethods() {

// TODO Move to some config file
// Video constants
private const val VIDEO_BASE_NAME = "video"
private const val VIDEO_MIME_TYPE = MediaConstants.VIDEO_MP4_MIME_TYPE
private val PREFERRED_VIDEO_SIZE = MediaConstants.SIZE_1080p
private val CAMERA_BEGIN_DIRECTION = CameraDirection.FRONT
internal const val VIDEO_BASE_NAME = "video"
internal const val VIDEO_MIME_TYPE = MediaConstants.VIDEO_MP4_MIME_TYPE
internal val PREFERRED_VIDEO_SIZE = MediaConstants.SIZE_1080p
internal val CAMERA_BEGIN_DIRECTION = CameraDirection.FRONT
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ abstract class RecordVideoInnerMethods : FixedOrientationFragment(ActivityInfo.S
setVideoSource(MediaRecorder.VideoSource.SURFACE)
setOutputFormat(MediaRecorder.OutputFormat.MPEG_4)
setVideoFrameRate(30)
setVideoEncodingBitRate(5_000_000)
setVideoEncodingBitRate(3_000_000)
setAudioEncodingBitRate(192_000)
setVideoEncoder(MediaRecorder.VideoEncoder.H264)
setAudioEncoder(MediaRecorder.AudioEncoder.AAC)
Expand Down
Loading