Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
990c095
Merge pull request #15 from Suphax/master
mafik Jan 22, 2021
b06834e
Android SDK Update, Background Recording Support, Permission Fixes, U…
jenisha010 Mar 19, 2024
1e4816a
Merge pull request #21 from jenisha010/master
mafik Mar 27, 2024
7e74b88
Ignore .idea files
mafik Jun 5, 2025
8fb6b5b
Bump gradle wrapper
mafik Jun 5, 2025
791a3a6
Remove FakeService
mafik Jun 5, 2025
542b320
Migrate build config generation from gradle.properties to build.gradle
mafik Jun 5, 2025
e67ebbd
Bump minimum SDK level to 30
mafik Jun 5, 2025
526dddd
Remove more obsolete files
mafik Jun 5, 2025
b21ee7a
Fix UI responsiveness
mafik Jun 5, 2025
63f5b58
Disable constant logging
mafik Jun 5, 2025
8da0892
Fix crash when re-enabling listening
mafik Jun 5, 2025
a7cc7a6
Switch app links to GitHub/F-Droid (from Google Play)
mafik Jun 5, 2025
2358d2a
Bump version
mafik Jun 5, 2025
b496512
Fastlane metadata
mafik Jun 5, 2025
6946f49
feat: Complete UI overhaul and feature implementation
ElliotBadinger Aug 18, 2025
5ce8a1a
feat: Implement Recordings Manager and Fix Critical Warnings
ElliotBadinger Aug 18, 2025
5461ccc
refactor: Complete UI Polish and Crash Fixes
ElliotBadinger Aug 18, 2025
07572c2
feat: Implement comprehensive UI/UX overhaul and critical bug fixes
ElliotBadinger Aug 18, 2025
e34b082
feat: Implement time-based auto-save, fix playback, and add How-To guide
ElliotBadinger Aug 19, 2025
0744739
feat(auto-save): use timestamped filenames (Auto-save_YYYY-MM-DD_HH-m…
ElliotBadinger Aug 19, 2025
6043bcb
feat(memory): replace chunked lists with direct ring buffer for low-G…
ElliotBadinger Aug 19, 2025
6046024
feat(recording): switch to AAC-LC (.m4a) and save via MediaStore with…
ElliotBadinger Aug 19, 2025
9649c84
feat(ui): add Save Clip flow, sync listening toggle, and recordings l…
ElliotBadinger Aug 19, 2025
3c3b7cf
test: Add comprehensive unit and instrumentation tests
ElliotBadinger Aug 19, 2025
24b44f8
Refactor: Abstract system clock for testability and update gitignore
ElliotBadinger Aug 19, 2025
49750ef
Fix auto-save crash, improve performance, and add tests
google-labs-jules[bot] Aug 20, 2025
4731adf
Create android-ci.yml
ElliotBadinger Aug 20, 2025
15f01e4
Update android-ci.yml
ElliotBadinger Aug 20, 2025
532bbe3
Upload google services json config
ElliotBadinger Aug 20, 2025
70cf5f3
Update android-ci.yml
ElliotBadinger Aug 20, 2025
9a4074d
Updating android-ci.yml
ElliotBadinger Aug 20, 2025
d66fc3b
Delete .github/workflows/android-ci.yml
ElliotBadinger Aug 20, 2025
a788020
Create android-ci.yml
ElliotBadinger Aug 20, 2025
d2d65bd
feat: Configure project for Firebase Test Lab
google-labs-jules[bot] Aug 20, 2025
1dbc53f
feat: Implement auto-save and performance improvements
ElliotBadinger Aug 20, 2025
eae7f83
Merge remote changes
ElliotBadinger Aug 20, 2025
28edabb
refactor: Decouple service and UI and introduce audio pipeline
ElliotBadinger Aug 24, 2025
65617f8
test: stabilize JVM unit tests and build
openhands-agent Aug 29, 2025
8f63796
feat: Apply critical fixes and convert unit tests to instrumentation …
ElliotBadinger Aug 29, 2025
dbfd422
feat: Merge branch 'fix/auto-save-and-performance' with critical fixes
ElliotBadinger Aug 30, 2025
c023060
fix: Resolve compilation errors and disable minification to fix build
ElliotBadinger Aug 30, 2025
6abf028
refactor(service): Apply advanced threading and shutdown logic to Sai…
ElliotBadinger Aug 30, 2025
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
62 changes: 36 additions & 26 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,26 +1,36 @@
# Built application files
*.apk
*.ap_

# Files for the Dalvik VM
*.dex

# Java class files
*.class

# Generated files
bin/
gen/

# Gradle files
.gradle/
build/

# Local configuration file (sdk path, etc)
local.properties

# Proguard folder generated by Eclipse
proguard/

# Log Files
*.log
# Built application files
*.apk
*.ap_

# Files for the Dalvik VM
*.dex

# Java class files
*.class

# Generated files
bin/
gen/

# Gradle files
.gradle/
build/

# Local configuration file (sdk path, etc)
local.properties

# Proguard folder generated by Eclipse
proguard/

# Log Files
*.log

# IDEs
.idea/
.vscode/

# Ignore Markdown files
*.md

# Ignore Claude agent files
.claude/
53 changes: 38 additions & 15 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,21 +1,44 @@
Echo
====
# Echo - Never Miss a Moment

Time travelling recorder for Android.
It is free/libre and gratis software.
Echo is a modern Android application that continuously records audio in the background, allowing you to go back in time and save moments that have already happened. Whether it's a brilliant idea, a funny quote, or an important note, Echo ensures you never miss it.

Download
---
## Features

* [F-Droid](https://f-droid.org/repository/browse/?fdid=eu.mrogalski.saidit)
* **Continuous Background Recording:** Echo runs silently in the background, keeping a rolling buffer of the last few hours of audio.
* **Save Clips from the Past:** Instantly save audio clips of various lengths from the buffered memory.
* **Auto-Save:** Automatically save recordings when the memory buffer is full, ensuring you never lose important audio.
* **Modern, Intuitive Interface:** A clean, professional design built with Material You principles.
* **Customizable Settings:** Adjust the audio quality and memory usage to fit your needs.

Building
---
## Getting Started

1. Install gradle-1.10 (version is important)
1. Install SDK platform API 21 and 21.0.2 build-tools, with either [sdkmanager](https://developer.android.com/studio/command-line/sdkmanager) or [Android Studio](https://developer.android.com/studio)
1. Create a Key Store - [Instructions](http://stackoverflow.com/questions/3997748/how-can-i-create-a-keystore)
1. Fill Key Store details in `SaidIt/build.gradle`
1. From this directory run `gradle installDebug` - to install it on a phone or `gradle assembleRelease` - to generate signed APK
### Prerequisites

If you had any issues and fixed them, please correct these instructions in your fork!
* Android Studio
* Java Development Kit (JDK)

### Building the Project

1. **Clone the repository:**
```bash
git clone https://github.com/mafik/echo.git
```
2. **Open the project in Android Studio.**
3. **Create a `local.properties` file** in the root of the project and add the following line, pointing to your Android SDK location:
```
sdk.dir=/path/to/your/android/sdk
```
4. **Build the project:**
* From the command line, run:
```bash
./gradlew assembleDebug
```
* Or, use the "Build" menu in Android Studio.

## Contributing

We welcome contributions! Please feel free to open an issue or submit a pull request.

## Future Development

For a detailed roadmap of planned features and improvements, please see the [`spec.md`](spec.md) file.
107 changes: 84 additions & 23 deletions SaidIt/build.gradle
Original file line number Diff line number Diff line change
@@ -1,21 +1,30 @@
buildscript {
repositories {
maven { url "https://repo.maven.apache.org/maven2" }
}
dependencies {
classpath 'com.android.tools.build:gradle:0.11.+'
}
plugins {
id 'com.android.application'
id 'com.google.gms.google-services' version '4.4.1' apply false
}
apply plugin: 'android'

repositories {
jcenter()
maven { url "https://maven.google.com" }
// Apply Google Services plugin only if google-services.json is present
def hasGoogleServicesJson = file('google-services.json').exists() ||
file('src/debug/google-services.json').exists() ||
file('src/release/google-services.json').exists()
if (hasGoogleServicesJson) {
apply plugin: 'com.google.gms.google-services'
} else {
logger.lifecycle("google-services.json missing; skipping Google Services plugin")
}

android {
compileSdkVersion 21
buildToolsVersion "21.0.2"
namespace 'eu.mrogalski.saidit'
compileSdk 34

defaultConfig{
applicationId "eu.mrogalski.saidit"
minSdk 30
targetSdk 34
versionCode 15
versionName "2.0.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}

signingConfigs {
release {
Expand All @@ -28,27 +37,79 @@ android {

buildTypes {
release {
runProguard true
minifyEnabled false
proguardFile file('proguard.cfg')
proguardFile getDefaultProguardFile('proguard-android-optimize.txt')
signingConfig signingConfigs.release
// signingConfig signingConfigs.release
}

debug {
signingConfig signingConfigs.release
//signingConfig signingConfigs.release
}
}
testOptions {
unitTests {
includeAndroidResources = true
returnDefaultValues = true

all {
timeout = java.time.Duration.ofSeconds(60)
maxHeapSize = "2g"
forkEvery = 50 // Fork JVM every 50 tests
maxParallelForks = 2

testLogging {
events "started", "passed", "skipped", "failed"
showStandardStreams = false
exceptionFormat = "full"
}

systemProperty 'robolectric.logging', 'stdout'
systemProperty 'robolectric.dependency.proxy.host', ''
systemProperty 'robolectric.dependency.proxy.port', '0'
}
}

animationsDisabled = true
}
lintOptions {
// Or, if you prefer, you can continue to check for errors in release builds,
// but continue the build even when errors are found:

lint {
checkReleaseBuilds false
abortOnError false
checkDependencies false
}
buildFeatures {
buildConfig true
}

compileOptions {
sourceCompatibility JavaVersion.VERSION_17
targetCompatibility JavaVersion.VERSION_17
}

}

dependencies {
compile fileTree(dir: 'libs', include: '*.jar')
compile 'com.android.support:appcompat-v7:21.0.3'
compile 'com.nineoldandroids:library:2.4.0'
compile 'com.android.support:support-v4:21.0.3'
implementation fileTree(dir: 'libs', include: '*.jar')
implementation 'androidx.appcompat:appcompat:1.6.1'
implementation 'com.google.android.material:material:1.11.0'
implementation 'com.getkeepsafe.taptargetview:taptargetview:1.13.3'
testImplementation 'junit:junit:4.13.2'
testImplementation 'org.mockito:mockito-core:5.11.0'
testImplementation 'org.robolectric:robolectric:4.10.3'
testImplementation 'androidx.test:core:1.5.0'
androidTestImplementation 'androidx.test.ext:junit:1.1.5'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1'
androidTestImplementation 'androidx.test:runner:1.5.2'
androidTestImplementation 'androidx.test:rules:1.5.0'
implementation 'org.tensorflow:tensorflow-lite:2.12.0'
implementation 'org.tensorflow:tensorflow-lite-support:0.4.3'
implementation 'org.tensorflow:tensorflow-lite-task-audio:0.4.3'
}

configurations.all {
resolutionStrategy {
force 'org.robolectric:robolectric:4.11.1'
force 'junit:junit:4.13.2'
}
}
3 changes: 3 additions & 0 deletions SaidIt/proguard.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -36,3 +36,6 @@
}

-keep class com.android.vending.billing.**

-dontwarn com.google.auto.value.AutoValue$Builder
-dontwarn com.google.auto.value.AutoValue
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
package eu.mrogalski.saidit;

import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.platform.app.InstrumentationRegistry;

import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;

import java.io.File;
import java.io.IOException;

import eu.mrogalski.saidit.util.SafeFileManager;

@RunWith(AndroidJUnit4.class)
public class AacMp4WriterTest {

private SafeFileManager fileManager;
private AacMp4Writer writer;
private File testFile;

@Before
public void setUp() throws Exception {
fileManager = new SafeFileManager();
File cacheDir = InstrumentationRegistry.getInstrumentation().getContext().getCacheDir();
testFile = new File(cacheDir, "test.m4a");
if (testFile.exists()) {
testFile.delete();
}
fileManager.registerTempFile(testFile);
}

@After
public void tearDown() throws Exception {
if (writer != null) {
writer.close();
writer = null;
}
if (fileManager != null) {
fileManager.close();
fileManager = null;
}
}

@Test
public void testWriteAndClose() throws IOException {
writer = new AacMp4Writer(48000, 1, 96000, testFile);
fileManager.register(writer);

// Test writing a small amount of data
byte[] testData = new byte[2048]; // Use a realistic buffer size
writer.write(testData, 0, testData.length);

// Close should work without issues
writer.close();
writer = null; // Prevent double close in tearDown
}
}
Loading