Thanks to visit codestin.com
Credit goes to github.com

Skip to content
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
7 changes: 7 additions & 0 deletions .idea/inspectionProfiles/Project_Default.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

16 changes: 11 additions & 5 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ android {
applicationId = "com.eva.recorderapp"
minSdk = 29
targetSdk = 35
versionCode = 4
versionName = "1.1.2"
versionCode = 5
versionName = "1.2.0"

testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
vectorDrawables {
Expand All @@ -40,11 +40,11 @@ android {
}
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
}
kotlinOptions {
jvmTarget = "1.8"
jvmTarget = "17"
}
buildFeatures {
compose = true
Expand Down Expand Up @@ -121,6 +121,12 @@ dependencies {
implementation(libs.androidx.datastore)
implementation(libs.protobuf.javalite)
implementation(libs.protobuf.kotlin.lite)
//glance
implementation(libs.androidx.glance)
implementation(libs.androidx.glance.appwidget)
implementation(libs.androidx.glance.material3)
implementation(libs.androidx.glance.preview)
implementation(libs.androidx.glance.appwidget.preview)
// tests
testImplementation(libs.junit)
testImplementation(libs.turbine)
Expand Down
24 changes: 24 additions & 0 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,30 @@
android:exported="false"
android:foregroundServiceType="microphone" />

<receiver
android:name=".voice_recorder.widgets.recordings.RecordingsWidgetReceiver"
android:enabled="true"
android:exported="true">
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
</intent-filter>
<meta-data
android:name="android.appwidget.provider"
android:resource="@xml/recordings_widget_provider" />
</receiver>

<receiver
android:name=".voice_recorder.widgets.recorder.RecorderWidgetReceiver"
android:enabled="true"
android:exported="true">
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
</intent-filter>
<meta-data
android:name="android.appwidget.provider"
android:resource="@xml/recorder_widget" />
</receiver>

<activity
android:name=".MainActivity"
android:exported="true"
Expand Down
8 changes: 4 additions & 4 deletions app/src/main/java/com/eva/recorderapp/ui/theme/Theme.kt
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ data class ExtendedColorScheme(
val customColor1: ColorFamily,
)

private val lightScheme = lightColorScheme(
val lightScheme = lightColorScheme(
primary = primaryLight,
onPrimary = onPrimaryLight,
primaryContainer = primaryContainerLight,
Expand Down Expand Up @@ -55,7 +55,7 @@ private val lightScheme = lightColorScheme(
surfaceContainerHighest = surfaceContainerHighestLight,
)

private val darkScheme = darkColorScheme(
val darkScheme = darkColorScheme(
primary = primaryDark,
onPrimary = onPrimaryDark,
primaryContainer = primaryContainerDark,
Expand Down Expand Up @@ -304,7 +304,7 @@ data class ColorFamily(
val color: Color,
val onColor: Color,
val colorContainer: Color,
val onColorContainer: Color
val onColorContainer: Color,
)

val unspecified_scheme = ColorFamily(
Expand All @@ -317,7 +317,7 @@ fun RecorderAppTheme(
darkTheme: Boolean = isSystemInDarkTheme(),
// Dynamic color is available on Android 12+
dynamicColor: Boolean = true,
content: @Composable() () -> Unit
content: @Composable() () -> Unit,
) {
val context = LocalContext.current

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.update
Expand Down Expand Up @@ -94,25 +95,24 @@ class AudioFilePlayerListener(
private fun computeMusicTrackInfo(state: PlayerState): Flow<PlayerTrackData> {
return flow {
Log.d(TAG, "CURRENT PLAYER STATE: $state")

// an empty data to prefill the entry
emit(PlayerTrackData())
// If the player can advertise positions ie, its ready or play or paused
// then continue the loop
while (state.canAdvertiseCurrentPosition) {

val current = player.currentPosition.milliseconds
val total = player.duration.milliseconds

if (current.isNegative() && total.isNegative())
if (current.isNegative() || total.isNegative())
continue

val trackData = PlayerTrackData(
current = player.currentPosition.milliseconds,
total = player.duration.milliseconds,
)
val trackData = PlayerTrackData(current = current, total = total)
emit(trackData)
delay(100.milliseconds)
}
}.distinctUntilChanged()
}.filter { it.allPositiveAndFinite }
.distinctUntilChanged()
}


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import com.eva.recorderapp.voice_recorder.domain.recorder.emums.RecorderAction
import com.eva.recorderapp.voice_recorder.domain.recorder.emums.RecorderState
import com.eva.recorderapp.voice_recorder.domain.use_cases.BluetoothScoUseCase
import com.eva.recorderapp.voice_recorder.domain.use_cases.PhoneStateObserverUsecase
import com.eva.recorderapp.voice_recorder.domain.util.AppWidgetsRepository
import dagger.hilt.android.AndroidEntryPoint
import kotlinx.collections.immutable.persistentListOf
import kotlinx.collections.immutable.toImmutableList
Expand All @@ -40,6 +41,7 @@ import kotlinx.coroutines.launch
import kotlinx.datetime.LocalTime
import javax.inject.Inject
import kotlin.time.Duration.Companion.milliseconds
import kotlin.time.Duration.Companion.seconds

private const val LOGGER_TAG = "RECORDER_SERVICE"

Expand All @@ -61,6 +63,9 @@ class VoiceRecorderService : LifecycleService() {
@Inject
lateinit var bookmarksProvider: RecordingBookmarksProvider

@Inject
lateinit var widgetFacade: AppWidgetsRepository


private val binder = LocalBinder()

Expand All @@ -69,9 +74,12 @@ class VoiceRecorderService : LifecycleService() {
@OptIn(ExperimentalCoroutinesApi::class)
val bookMarks = _bookMarks.mapLatest { bookMarks ->
// convert it to set such that common items are subtracted
bookMarks.map(LocalTime::roundToClosestSeconds).toSet().toImmutableList()
bookMarks.map(LocalTime::roundToClosestSeconds)
.toImmutableList()
}.stateIn(
scope = lifecycleScope, started = SharingStarted.Lazily, initialValue = persistentListOf()
scope = lifecycleScope,
started = SharingStarted.Lazily,
initialValue = persistentListOf()
)

private val _amplitudes = MutableStateFlow(emptyList<Pair<LocalTime, Float>>())
Expand All @@ -89,6 +97,13 @@ class VoiceRecorderService : LifecycleService() {
private val notificationTimer: Flow<LocalTime>
get() = voiceRecorder.recorderTimer.sample(800.milliseconds)

/**
* Recorder timer sampled per 1 seconds
*/
@OptIn(FlowPreview::class)
private val widgetTimer: Flow<LocalTime>
get() = voiceRecorder.recorderTimer.sample(1.seconds)

inner class LocalBinder : Binder() {

fun getService(): VoiceRecorderService = this@VoiceRecorderService
Expand All @@ -103,20 +118,20 @@ class VoiceRecorderService : LifecycleService() {
override fun onCreate() {
super.onCreate()
try {
// read phone states
// preparing the recorder
voiceRecorder.createRecorder()
// check use case
bluetoothScoUseCase.startConnectionIfAllowed()
// listen to changes
// read phone states
observeChangingPhoneState()
// inform bt connect
showBluetoothConnectedToast()
// updates the amplitudes
readAmplitudes()
// update the notification
readTimerAndUpdateNotification()

// update widget state
updateRecorderWidgetState()
Log.i(LOGGER_TAG, "SERVICE CREATED WITH OBSERVERS")
} catch (e: Exception) {
e.printStackTrace()
Expand Down Expand Up @@ -175,6 +190,13 @@ class VoiceRecorderService : LifecycleService() {
}.launchIn(lifecycleScope)
}

private fun updateRecorderWidgetState() {
combine(widgetTimer, recorderState) { time, state ->
// update the widget
widgetFacade.recorderWidgetUpdate(state, time)
}.launchIn(lifecycleScope)
}

private fun onStartRecording() {
//start the recorder
lifecycleScope.launch { voiceRecorder.startRecording() }.invokeOnCompletion {
Expand Down Expand Up @@ -212,6 +234,8 @@ class VoiceRecorderService : LifecycleService() {
voiceRecorder.cancelRecording()
//clear bookmarks
clearBookMarks()
//update widget
widgetFacade.resetRecorderWidget()
}.invokeOnCompletion {
// stop the foreground
stopForeground(STOP_FOREGROUND_REMOVE)
Expand All @@ -238,6 +262,8 @@ class VoiceRecorderService : LifecycleService() {

else -> {}
}
//update widget
widgetFacade.resetRecorderWidget()
}.invokeOnCompletion {
//clear and save bookmarks
// stop the foreground
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,11 @@ import com.eva.recorderapp.voice_recorder.data.util.ShareRecordingsUtilImpl
import com.eva.recorderapp.voice_recorder.domain.bookmarks.ExportBookMarkUriProvider
import com.eva.recorderapp.voice_recorder.domain.recorder.RecorderActionHandler
import com.eva.recorderapp.voice_recorder.domain.util.AppShortcutFacade
import com.eva.recorderapp.voice_recorder.domain.util.AppWidgetsRepository
import com.eva.recorderapp.voice_recorder.domain.util.BluetoothScoConnect
import com.eva.recorderapp.voice_recorder.domain.util.PhoneStateObserver
import com.eva.recorderapp.voice_recorder.domain.util.ShareRecordingsUtil
import com.eva.recorderapp.voice_recorder.widgets.data.AppWidgetsRepoImpl
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
Expand All @@ -26,7 +28,7 @@ object AppUtilsModule {
@Provides
@Singleton
fun providesRecorderActionHandler(
@ApplicationContext context: Context
@ApplicationContext context: Context,
): RecorderActionHandler = RecorderActionHandlerImpl(context)

@Provides
Expand Down Expand Up @@ -55,4 +57,9 @@ object AppUtilsModule {
exportBookMarkUriProvider: ExportBookMarkUriProvider,
): ShareRecordingsUtil = ShareRecordingsUtilImpl(context, exportBookMarkUriProvider)

@Provides
@Singleton
fun providesWidgetUtils(@ApplicationContext context: Context): AppWidgetsRepository =
AppWidgetsRepoImpl(context)

}
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ import com.eva.recorderapp.voice_recorder.domain.recordings.provider.RecordingCa
import com.eva.recorderapp.voice_recorder.domain.recordings.provider.RecordingsSecondaryDataProvider
import com.eva.recorderapp.voice_recorder.domain.recordings.provider.TrashRecordingsProvider
import com.eva.recorderapp.voice_recorder.domain.recordings.provider.VoiceRecordingsProvider
import com.eva.recorderapp.voice_recorder.domain.use_cases.GetRecordingsOfCurrentAppUseCase
import com.eva.recorderapp.voice_recorder.domain.util.AppWidgetsRepository
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
Expand Down Expand Up @@ -66,4 +68,13 @@ object RecorderRecordingsProvider {
): RecordingsSecondaryDataProvider =
RecordingSecondaryDataProviderImpl(context = context, recordingsDao = recordingsMetadataDao)

@Provides
@Singleton
fun providesOwnerRecordingsUseCase(
recordings: VoiceRecordingsProvider,
secondaryRecordingsData: RecordingsSecondaryDataProvider,
widgetFacade: AppWidgetsRepository,
): GetRecordingsOfCurrentAppUseCase =
GetRecordingsOfCurrentAppUseCase(recordings, secondaryRecordingsData, widgetFacade)

}
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@ data class PlayerTrackData(
return ratio.coerceIn(0f, 1f)
}

val allPositiveAndFinite: Boolean
get() = current.isFinite() && current.isPositive() && total.isFinite() && total.isPositive()

fun calculateSeekAmount(seek: Float): Duration {
try {
require(value = seek in 0f..1f)
Expand Down
Loading