Skip to content
Draft
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
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,11 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [Unreleased]
### Added
- Added Bluetooth audio recording support

### Changed
- Improved Android 12+ compatibility

## [1.7.1] - 2026-02-14
### Changed
Expand Down
10 changes: 8 additions & 2 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@

import org.jetbrains.kotlin.gradle.dsl.JvmTarget
import java.io.FileInputStream
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
import org.jetbrains.kotlin.konan.properties.Properties
import java.io.FileInputStream

plugins {
alias(libs.plugins.android)
alias(libs.plugins.androidApplication)
alias(libs.plugins.ksp)
alias(libs.plugins.detekt)


}

val keystorePropertiesFile: File = rootProject.file("keystore.properties")
Expand Down Expand Up @@ -147,4 +150,7 @@ dependencies {
implementation(libs.tandroidlame)
implementation(libs.autofittextview)
detektPlugins(libs.compose.detekt)

implementation(project(":store"))
implementation(project(":transcribe"))
}
2 changes: 2 additions & 0 deletions app/detekt-baseline.xml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@
<ID>TooManyFunctions:Activity.kt$org.fossify.voicerecorder.extensions.Activity.kt</ID>
<ID>TooManyFunctions:Context.kt$org.fossify.voicerecorder.extensions.Context.kt</ID>
<ID>TooManyFunctions:MainActivity.kt$MainActivity : SimpleActivity</ID>
<ID>TooManyFunctions:MediaRecorderWrapper.kt$MediaRecorderWrapper : Recorder</ID>
<ID>TooManyFunctions:Mp3Recorder.kt$Mp3Recorder : Recorder</ID>
<ID>TooManyFunctions:PlayerFragment.kt$PlayerFragment : MyViewPagerFragmentRefreshRecordingsListener</ID>
<ID>TooManyFunctions:RecorderFragment.kt$RecorderFragment : MyViewPagerFragment</ID>
<ID>TooManyFunctions:RecorderService.kt$RecorderService : Service</ID>
Expand Down
22 changes: 21 additions & 1 deletion app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,18 @@
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<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_DATA_SYNC" />
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
<uses-permission android:name="android.permission.INTERNET" />

<uses-permission
android:name="android.permission.READ_EXTERNAL_STORAGE"
android:maxSdkVersion="28" />
<uses-permission
android:name="android.permission.WRITE_EXTERNAL_STORAGE"
android:maxSdkVersion="29" />
android:maxSdkVersion="28" />

<uses-feature
android:name="android.hardware.faketouch"
Expand Down Expand Up @@ -92,6 +100,13 @@
</intent-filter>
</activity>

<activity
android:name=".activities.TranscriptActivity"
android:configChanges="orientation"
android:exported="false"
android:label="@string/transcript"
android:parentActivityName=".activities.MainActivity" />

<activity
android:name="org.fossify.commons.activities.AboutActivity"
android:configChanges="orientation"
Expand All @@ -117,6 +132,11 @@
</intent-filter>
</service>

<service
android:name=".services.TranscriptionService"
android:exported="false"
android:foregroundServiceType="dataSync" />

<activity-alias
android:name=".activities.SplashActivity.Red"
android:enabled="false"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ class BackgroundRecordActivity : SimpleActivity() {
if (RecorderService.isRunning) {
stopService(this)
} else {
startService(this)
startForegroundService(this)
}
} catch (ignored: Exception) {
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package org.fossify.voicerecorder.activities

import android.app.Activity
import android.content.Intent
import android.os.Bundle
import android.provider.MediaStore
Expand All @@ -22,16 +21,13 @@ import org.fossify.commons.helpers.LICENSE_AUDIO_RECORD_VIEW
import org.fossify.commons.helpers.LICENSE_AUTOFITTEXTVIEW
import org.fossify.commons.helpers.LICENSE_EVENT_BUS
import org.fossify.commons.helpers.PERMISSION_RECORD_AUDIO
import org.fossify.commons.helpers.PERMISSION_WRITE_STORAGE
import org.fossify.commons.helpers.isRPlus
import org.fossify.commons.models.FAQItem
import org.fossify.voicerecorder.BuildConfig
import org.fossify.voicerecorder.R
import org.fossify.voicerecorder.adapters.ViewPagerAdapter
import org.fossify.voicerecorder.databinding.ActivityMainBinding
import org.fossify.voicerecorder.extensions.config
import org.fossify.voicerecorder.extensions.deleteExpiredTrashedRecordings
import org.fossify.voicerecorder.extensions.ensureStoragePermission
import org.fossify.voicerecorder.helpers.STOP_AMPLITUDE_UPDATE
import org.fossify.voicerecorder.models.Events
import org.fossify.voicerecorder.services.RecorderService
Expand All @@ -40,7 +36,6 @@ import org.greenrobot.eventbus.Subscribe
import org.greenrobot.eventbus.ThreadMode

class MainActivity : SimpleActivity() {

private var bus: EventBus? = null

override var isSearchBarEnabled = true
Expand Down Expand Up @@ -80,7 +75,7 @@ class MainActivity : SimpleActivity() {
Intent(this@MainActivity, RecorderService::class.java).apply {
try {
startService(this)
} catch (ignored: Exception) {
} catch (_: Exception) {
}
}
}
Expand All @@ -96,6 +91,11 @@ class MainActivity : SimpleActivity() {
getPagerAdapter()?.onResume()
}

override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<String>, grantResults: IntArray) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
getPagerAdapter()?.onPermissionResult(requestCode, grantResults)
}

override fun onPause() {
super.onPause()
config.lastUsedViewPagerPage = binding.viewPager.currentItem
Expand All @@ -110,7 +110,7 @@ class MainActivity : SimpleActivity() {
action = STOP_AMPLITUDE_UPDATE
try {
startService(this)
} catch (ignored: Exception) {
} catch (_: Exception) {
}
}
}
Expand All @@ -120,7 +120,7 @@ class MainActivity : SimpleActivity() {
binding.mainMenu.closeSearch()
true
} else if (isThirdPartyIntent()) {
setResult(Activity.RESULT_CANCELED, null)
setResult(RESULT_CANCELED, null)
false
} else {
false
Expand Down Expand Up @@ -150,41 +150,24 @@ class MainActivity : SimpleActivity() {
getPagerAdapter()?.searchTextChanged(text)
}

binding.mainMenu.requireToolbar().setOnMenuItemClickListener { menuItem ->
when (menuItem.itemId) {
R.id.more_apps_from_us -> launchMoreAppsFromUsIntent()
R.id.settings -> launchSettings()
R.id.about -> launchAbout()
else -> return@setOnMenuItemClickListener false
binding.mainMenu.requireToolbar()
.setOnMenuItemClickListener { menuItem ->
when (menuItem.itemId) {
R.id.more_apps_from_us -> launchMoreAppsFromUsIntent()
R.id.settings -> launchSettings()
R.id.about -> launchAbout()
else -> return@setOnMenuItemClickListener false
}
return@setOnMenuItemClickListener true
}
return@setOnMenuItemClickListener true
}
}

private fun updateMenuColors() {
binding.mainMenu.updateColors()
}

private fun tryInitVoiceRecorder() {
if (isRPlus()) {
ensureStoragePermission { granted ->
if (granted) {
setupViewPager()
} else {
toast(org.fossify.commons.R.string.no_storage_permissions)
finish()
}
}
} else {
handlePermission(PERMISSION_WRITE_STORAGE) {
if (it) {
setupViewPager()
} else {
toast(org.fossify.commons.R.string.no_storage_permissions)
finish()
}
}
}
setupViewPager()
}

private fun setupViewPager() {
Expand All @@ -201,18 +184,16 @@ class MainActivity : SimpleActivity() {

tabDrawables.forEachIndexed { i, drawableId ->
binding.mainTabsHolder.newTab()
.setCustomView(org.fossify.commons.R.layout.bottom_tablayout_item).apply {
customView
?.findViewById<ImageView>(org.fossify.commons.R.id.tab_item_icon)
.setCustomView(org.fossify.commons.R.layout.bottom_tablayout_item)
.apply {
customView?.findViewById<ImageView>(org.fossify.commons.R.id.tab_item_icon)
?.setImageDrawable(
AppCompatResources.getDrawable(
this@MainActivity,
drawableId
this@MainActivity, drawableId
)
)

customView
?.findViewById<TextView>(org.fossify.commons.R.id.tab_item_label)
customView?.findViewById<TextView>(org.fossify.commons.R.id.tab_item_label)
?.setText(tabLabels[i])

AutofitHelper.create(
Expand All @@ -223,18 +204,15 @@ class MainActivity : SimpleActivity() {
}
}

binding.mainTabsHolder.onTabSelectionChanged(
tabUnselectedAction = {
updateBottomTabItemColors(it.customView, false)
if (it.position == 1 || it.position == 2) {
binding.mainMenu.closeSearch()
}
},
tabSelectedAction = {
binding.viewPager.currentItem = it.position
updateBottomTabItemColors(it.customView, true)
binding.mainTabsHolder.onTabSelectionChanged(tabUnselectedAction = {
updateBottomTabItemColors(it.customView, false)
if (it.position == 1 || it.position == 2) {
binding.mainMenu.closeSearch()
}
)
}, tabSelectedAction = {
binding.viewPager.currentItem = it.position
updateBottomTabItemColors(it.customView, true)
})

binding.viewPager.adapter = ViewPagerAdapter(this, config.useRecycleBin)
binding.viewPager.offscreenPageLimit = 2
Expand All @@ -247,16 +225,19 @@ class MainActivity : SimpleActivity() {
binding.viewPager.currentItem = 0
} else {
binding.viewPager.currentItem = config.lastUsedViewPagerPage
binding.mainTabsHolder.getTabAt(config.lastUsedViewPagerPage)?.select()
binding.mainTabsHolder.getTabAt(config.lastUsedViewPagerPage)
?.select()
}
}

private fun setupTabColors() {
val activeView = binding.mainTabsHolder.getTabAt(binding.viewPager.currentItem)?.customView
val activeView =
binding.mainTabsHolder.getTabAt(binding.viewPager.currentItem)?.customView
updateBottomTabItemColors(activeView, true)
for (i in 0 until binding.mainTabsHolder.tabCount) {
if (i != binding.viewPager.currentItem) {
val inactiveView = binding.mainTabsHolder.getTabAt(i)?.customView
val inactiveView =
binding.mainTabsHolder.getTabAt(i)?.customView
updateBottomTabItemColors(inactiveView, false)
}
}
Expand All @@ -266,25 +247,22 @@ class MainActivity : SimpleActivity() {
binding.mainTabsHolder.setBackgroundColor(bottomBarColor)
}

private fun getPagerAdapter() = (binding.viewPager.adapter as? ViewPagerAdapter)
private fun getPagerAdapter() =
(binding.viewPager.adapter as? ViewPagerAdapter)

private fun launchSettings() {
hideKeyboard()
startActivity(Intent(applicationContext, SettingsActivity::class.java))
}

private fun launchAbout() {
val licenses = LICENSE_EVENT_BUS or
LICENSE_AUDIO_RECORD_VIEW or
LICENSE_ANDROID_LAME or
LICENSE_AUTOFITTEXTVIEW
val licenses =
LICENSE_EVENT_BUS or LICENSE_AUDIO_RECORD_VIEW or LICENSE_ANDROID_LAME or LICENSE_AUTOFITTEXTVIEW

val faqItems = arrayListOf(
FAQItem(
title = R.string.faq_1_title,
text = R.string.faq_1_text
),
FAQItem(
title = R.string.faq_1_title, text = R.string.faq_1_text
), FAQItem(
title = org.fossify.commons.R.string.faq_9_title_commons,
text = org.fossify.commons.R.string.faq_9_text_commons
)
Expand Down Expand Up @@ -314,18 +292,25 @@ class MainActivity : SimpleActivity() {
)
}

private fun isThirdPartyIntent() = intent?.action == MediaStore.Audio.Media.RECORD_SOUND_ACTION
private fun isThirdPartyIntent() =
intent?.action == MediaStore.Audio.Media.RECORD_SOUND_ACTION

@Suppress("unused")
@Subscribe(threadMode = ThreadMode.MAIN)
fun recordingSaved(event: Events.RecordingSaved) {
if (isThirdPartyIntent()) {
Intent().apply {
data = event.uri!!
data = event.uri
flags = Intent.FLAG_GRANT_READ_URI_PERMISSION
setResult(Activity.RESULT_OK, this)
setResult(RESULT_OK, this)
}
finish()
}
}

@Suppress("unused")
@Subscribe(threadMode = ThreadMode.MAIN)
fun recordingFailed(event: Events.RecordingFailed) {
handleRecordingStoreError(event.exception)
}
}
Loading
Loading