├── CHANGELOG.md ├── LICENSE.md ├── README.md ├── app ├── build.gradle ├── libs │ └── Croller │ │ ├── build.gradle │ │ ├── proguard-rules.pro │ │ └── src │ │ ├── androidTest │ │ └── java │ │ │ └── com │ │ │ └── sdsmdg │ │ │ └── harjot │ │ │ └── crollerTest │ │ │ └── ApplicationTest.java │ │ ├── main │ │ ├── AndroidManifest.xml │ │ ├── java │ │ │ └── com │ │ │ │ └── sdsmdg │ │ │ │ └── harjot │ │ │ │ └── crollerTest │ │ │ │ ├── Croller.java │ │ │ │ ├── OnCrollerChangeListener.java │ │ │ │ └── utilities │ │ │ │ └── Utils.java │ │ └── res │ │ │ ├── layout │ │ │ └── croller_view.xml │ │ │ └── values │ │ │ ├── attrs.xml │ │ │ └── strings.xml │ │ └── test │ │ └── java │ │ └── com │ │ └── sdsmdg │ │ └── harjot │ │ └── croller │ │ └── ExampleUnitTest.java ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── net │ │ └── synapticweb │ │ └── callrecorder │ │ └── data │ │ ├── RecordingTest.java │ │ └── RepositoryImplTest.java │ ├── eval │ ├── AndroidManifest.xml │ ├── java │ │ └── net │ │ │ └── synapticweb │ │ │ └── callrecorder │ │ │ └── Config.java │ └── res │ │ └── values │ │ └── strings.xml │ ├── full │ ├── AndroidManifest.xml │ └── java │ │ └── net │ │ └── synapticweb │ │ └── callrecorder │ │ └── Config.java │ ├── main │ ├── AndroidManifest.xml │ ├── assets │ │ ├── assign.png │ │ ├── common_style.css │ │ ├── dark_style.css │ │ ├── deleted_audio.jpg │ │ ├── dots_menu_dark.png │ │ ├── dots_menu_light.png │ │ ├── incognito.png │ │ ├── info_dark.png │ │ ├── info_light.png │ │ ├── ingoing.png │ │ ├── launcher_icon-web.png │ │ ├── launcher_icon.png │ │ ├── light_style.css │ │ ├── move_dark.png │ │ ├── move_light.png │ │ ├── notification_icon_dark.png │ │ ├── notification_icon_light.png │ │ ├── outgoing.png │ │ ├── plus.png │ │ ├── select_all_dark.png │ │ ├── select_all_light.png │ │ ├── selected_rec.jpg │ │ ├── stop_rec.jpg │ │ └── top_bar_selected.jpg │ ├── java │ │ └── net │ │ │ └── synapticweb │ │ │ └── callrecorder │ │ │ ├── BaseActivity.java │ │ │ ├── CrApp.java │ │ │ ├── CrLog.java │ │ │ ├── HelpActivity.java │ │ │ ├── Util.java │ │ │ ├── contactdetail │ │ │ ├── ContactDetailActivity.java │ │ │ ├── ContactDetailContract.java │ │ │ ├── ContactDetailFragment.java │ │ │ ├── ContactDetailPresenter.java │ │ │ ├── EditContactActivity.java │ │ │ ├── MoveAsyncTask.java │ │ │ └── di │ │ │ │ ├── ContactDetailComponent.java │ │ │ │ ├── PresenterModule.java │ │ │ │ └── ViewModule.java │ │ │ ├── contactslist │ │ │ ├── ContactsListActivityMain.java │ │ │ ├── ContactsListContract.java │ │ │ ├── ContactsListFragment.java │ │ │ ├── ContactsListPresenter.java │ │ │ ├── UnassignedRecordingsFragment.java │ │ │ └── di │ │ │ │ ├── ContactsListComponent.java │ │ │ │ ├── PresenterModule.java │ │ │ │ └── ViewModule.java │ │ │ ├── data │ │ │ ├── CallRecorderDbHelper.java │ │ │ ├── Contact.java │ │ │ ├── ContactsContract.java │ │ │ ├── Recording.java │ │ │ ├── RecordingsContract.java │ │ │ ├── Repository.java │ │ │ └── RepositoryImpl.java │ │ │ ├── di │ │ │ ├── AppComponent.java │ │ │ ├── AppSubcomponents.java │ │ │ ├── FragmentScope.java │ │ │ └── RepositoryModule.java │ │ │ ├── player │ │ │ ├── AudioPlayer.java │ │ │ ├── PlaybackListenerInterface.java │ │ │ ├── PlayerActivity.java │ │ │ ├── PlayerAdapter.java │ │ │ └── PlayerException.java │ │ │ ├── recorder │ │ │ ├── CallReceiver.java │ │ │ ├── ControlRecordingReceiver.java │ │ │ ├── Recorder.java │ │ │ ├── RecorderService.java │ │ │ ├── RecordingException.java │ │ │ ├── RecordingThread.java │ │ │ ├── RecordingThreadAac.java │ │ │ └── RecordingThreadWav.java │ │ │ ├── settings │ │ │ ├── SettingsActivity.java │ │ │ └── SettingsFragment.java │ │ │ └── setup │ │ │ ├── SetupActivity.java │ │ │ ├── SetupEulaFragment.java │ │ │ ├── SetupPermissionsFragment.java │ │ │ ├── SetupPowerFragment.java │ │ │ └── ShowEulaActivity.java │ ├── launcher_icon-web.png │ └── res │ │ ├── color │ │ ├── nav_item_state_list.xml │ │ ├── text_on_dark.xml │ │ └── text_on_light.xml │ │ ├── drawable-anydpi-v24 │ │ └── notification_icon_success.xml │ │ ├── drawable-hdpi │ │ ├── baseline_info_black_36.png │ │ ├── baseline_settings_black_36.png │ │ ├── ic_album_white_24dp.png │ │ ├── ic_play_grey600_24dp.png │ │ ├── notification_icon.png │ │ ├── notification_icon_error.png │ │ └── notification_icon_success.png │ │ ├── drawable-mdpi │ │ ├── baseline_info_black_36.png │ │ ├── baseline_more_vert_white_24.png │ │ ├── baseline_settings_black_36.png │ │ ├── notification_icon.png │ │ ├── notification_icon_error.png │ │ └── notification_icon_success.png │ │ ├── drawable-xhdpi │ │ ├── baseline_info_black_36.png │ │ ├── baseline_more_vert_white_24.png │ │ ├── baseline_settings_black_36.png │ │ ├── notification_icon.png │ │ ├── notification_icon_error.png │ │ └── notification_icon_success.png │ │ ├── drawable-xxhdpi │ │ ├── baseline_info_black_36.png │ │ ├── baseline_more_vert_white_24.png │ │ ├── baseline_settings_black_36.png │ │ ├── notification_icon.png │ │ ├── notification_icon_error.png │ │ └── notification_icon_success.png │ │ ├── drawable-xxxhdpi │ │ ├── baseline_info_black_36.png │ │ ├── baseline_more_vert_white_24.png │ │ ├── baseline_settings_black_36.png │ │ ├── notification_icon.png │ │ └── notification_icon_error.png │ │ ├── drawable │ │ ├── back.xml │ │ ├── call.xml │ │ ├── checkbox_checked.xml │ │ ├── checkbox_unchecked.xml │ │ ├── close.xml │ │ ├── done.xml │ │ ├── edit.xml │ │ ├── email.xml │ │ ├── error.xml │ │ ├── exclamation.xml │ │ ├── grid.xml │ │ ├── header.png │ │ ├── header_back.png │ │ ├── help.xml │ │ ├── ic_launcher_background.xml │ │ ├── ic_plus.xml │ │ ├── incognito.xml │ │ ├── incoming.xml │ │ ├── info.xml │ │ ├── launcher.png │ │ ├── menu.xml │ │ ├── outgoing_dark.xml │ │ ├── outgoing_light.xml │ │ ├── player_pause.xml │ │ ├── player_play.xml │ │ ├── player_stop.xml │ │ ├── question_mark.xml │ │ ├── recorder.xml │ │ ├── select_all.xml │ │ ├── select_export.xml │ │ ├── settings.png │ │ ├── sound_bottom.xml │ │ ├── sound_symbol_aac128_dark.xml │ │ ├── sound_symbol_aac128_light.xml │ │ ├── sound_symbol_aac32_dark.xml │ │ ├── sound_symbol_aac32_light.xml │ │ ├── sound_symbol_aac64_dark.xml │ │ ├── sound_symbol_aac64_light.xml │ │ ├── sound_symbol_wav_dark.xml │ │ ├── sound_symbol_wav_light.xml │ │ ├── speaker_phone_off.xml │ │ ├── speaker_phone_on.xml │ │ ├── stars.xml │ │ ├── success.xml │ │ ├── user_bottom.xml │ │ ├── user_contact.xml │ │ └── warning.xml │ │ ├── layout │ │ ├── contact.xml │ │ ├── contact_detail_activity.xml │ │ ├── contact_detail_fragment.xml │ │ ├── contacts_list_activity_onepane.xml │ │ ├── contacts_list_activity_twopane.xml │ │ ├── edit_contact_activity.xml │ │ ├── help_activity.xml │ │ ├── help_fragment.xml │ │ ├── info_dialog.xml │ │ ├── info_storage_dialog.xml │ │ ├── list_contacts_fragment.xml │ │ ├── navdraw_header.xml │ │ ├── player_activity.xml │ │ ├── recording.xml │ │ ├── settings_layout.xml │ │ ├── setup_activity.xml │ │ ├── setup_eula_fragment.xml │ │ ├── setup_permissions_fragment.xml │ │ ├── setup_power_fragment.xml │ │ ├── setup_show_eula_activity.xml │ │ └── unassigned_recordings_fragment.xml │ │ ├── menu │ │ ├── bottom_nav.xml │ │ ├── contact_popup.xml │ │ ├── drawer_view.xml │ │ ├── edit_contact_change_photo.xml │ │ ├── recording_selected_popup.xml │ │ └── storage_chooser_options.xml │ │ ├── mipmap-anydpi-v26 │ │ ├── launcher_icon.xml │ │ └── launcher_icon_round.xml │ │ ├── mipmap-hdpi │ │ ├── ic_launcher.png │ │ ├── ic_launcher_foreground.png │ │ ├── ic_launcher_round.png │ │ ├── launcher_icon.png │ │ └── launcher_icon_round.png │ │ ├── mipmap-mdpi │ │ ├── ic_launcher.png │ │ ├── ic_launcher_foreground.png │ │ ├── ic_launcher_round.png │ │ ├── launcher_icon.png │ │ └── launcher_icon_round.png │ │ ├── mipmap-xhdpi │ │ ├── ic_launcher.png │ │ ├── ic_launcher_foreground.png │ │ ├── ic_launcher_round.png │ │ ├── launcher_icon.png │ │ └── launcher_icon_round.png │ │ ├── mipmap-xxhdpi │ │ ├── ic_launcher.png │ │ ├── ic_launcher_foreground.png │ │ ├── ic_launcher_round.png │ │ ├── launcher_icon.png │ │ └── launcher_icon_round.png │ │ ├── mipmap-xxxhdpi │ │ ├── ic_launcher.png │ │ ├── ic_launcher_foreground.png │ │ ├── ic_launcher_round.png │ │ ├── launcher_icon.png │ │ └── launcher_icon_round.png │ │ ├── raw │ │ ├── eula.html │ │ ├── help_about.html │ │ ├── help_licences.html │ │ ├── help_managing_recordings.html │ │ ├── help_playing_recordings.html │ │ ├── help_recording_calls.html │ │ └── privacy_policy.html │ │ ├── values-sw380dp │ │ └── dimens.xml │ │ ├── values-sw600dp │ │ └── refs.xml │ │ ├── values │ │ ├── arrays.xml │ │ ├── attrs.xml │ │ ├── colors.xml │ │ ├── dimens.xml │ │ ├── refs.xml │ │ ├── strings.xml │ │ └── styles.xml │ │ └── xml │ │ ├── files.xml │ │ └── preferences.xml │ └── test │ └── java │ └── net │ └── synapticweb │ └── callrecorder │ └── data │ ├── ContactTest.java │ ├── ContactTestRobolectric.java │ ├── FakePhoneLookupProvider.java │ ├── FakeRepository.java │ ├── RecordingTest.java │ └── RecordingTestRobolectric.java ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat └── settings.gradle /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## 1.1.2 2 | - Audio source is now selectable by the user. 3 | 4 | ## 1.1.1 5 | - Recordings name now contain the full date with year and no "Recording" prefix. 6 | - The recordings listings are now retrieved ordered by date descending. 7 | - Removed arrows (type of call) from hidden number contacts recordings. 8 | 9 | 10 | ## 1.0.0 11 | - Reached production development stage -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SW Call Recorder 2 | 3 | SW Call Recorder is an Android application that automatically records phone calls based on user selected phone numbers. 4 | It has the following main features: 5 | 6 | - The phone numbers are organized into contacts. 7 | - Every contact can have an associated picture selected by the user. 8 | - There are 4 possible recording formats: WAV (Lossless), AAC 128kbs, AAC 64kbps and AAC 32kbps. 9 | - Recordings can be mono or stereo. 10 | - The device can be put automatically on speaker while the application records the call. 11 | - Recordings can be stored in the application's private storage space or in a public accessible location on the device. If they are stored in a public accessible location, they can be easily transferred to other devices, like a laptop. The location of the recordings on the device can be easily changed. 12 | - Phone calls from private (unknown) numbers are supported. 13 | - The user can rename the recordings. 14 | - The recordings can be played from within the application with the help of a dedicated audio player. 15 | - The audio player can modify the sound volume of the device while playing. After the playing finishes, the audio volume of the device automatically comes back to the previous value. 16 | - The audio player can apply a gain to the audio signal to further control how loud the sound is played. 17 | - The application has 2 themes: light and dark. 18 | 19 | Because of the restrictions imposed by Google on requesting permissions from the Call log permission group (https://play.google.com/about/privacy-security-deception/permissions/), the app is currently developed using two branches: the `original-app` branch and the `gpcompliant` (from "google play compliant") branch. 20 | 21 | The `gpcompliant` branch does not use the READ_CALL_LOG and PROCESS_OUTGOING_CALLS permissions and consequently the versions of the app compiled from this source cannot obtain the outgoing phone number in all supported versions of Android and cannot obtain the incoming phone number in Android versions 9 and above. This has consequences on the overall appearance and behavior of the app that are documented in the "Recording phone calls" section of the Help menu. These versions have the substring 'gpcompliant' appended to the version string. 22 | 23 | The `original-app` branch retains all the functions that were removed from the `gpcompliant` branch. The versions of the app compiled from this source have the substring 'original' appended to the version string. 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | apply plugin: 'com.google.gms.google-services' 3 | apply plugin: 'io.fabric' 4 | 5 | android { 6 | compileSdkVersion 29 7 | buildToolsVersion "30.0.0-rc4" 8 | defaultConfig { 9 | applicationId "net.synapticweb.callrecorder.gpcompliant" 10 | minSdkVersion 21 11 | targetSdkVersion 29 12 | versionCode 13 13 | versionName "1.1.3-gpcompliant" 14 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 15 | } 16 | flavorDimensions "version" 17 | 18 | productFlavors { 19 | eval { 20 | dimension "version" 21 | applicationIdSuffix ".eval" 22 | versionNameSuffix "-eval" 23 | } 24 | 25 | full { 26 | dimension "version" 27 | applicationIdSuffix ".full" 28 | versionNameSuffix "-full" 29 | } 30 | } 31 | 32 | buildTypes { 33 | release { 34 | minifyEnabled false 35 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 36 | } 37 | } 38 | compileOptions { 39 | sourceCompatibility JavaVersion.VERSION_1_8 40 | targetCompatibility JavaVersion.VERSION_1_8 41 | } 42 | testOptions { 43 | unitTests { 44 | includeAndroidResources = true 45 | } 46 | } 47 | } 48 | 49 | ext { 50 | assertjVersion = "3.11.1" 51 | acraVersion = "5.5.0" 52 | daggerVersion = "2.27" 53 | robolectricVersion = "4.3.1" 54 | androidXVersion = "1.2.0" 55 | mockitoVersion = "3.3.3" 56 | androidJunitVersion = "1.1.1" 57 | hamcrestVersion = "1.3" 58 | powermockVersion = "2.0.2" 59 | runnerVersion = "1.3.0-rc01" 60 | } 61 | 62 | 63 | dependencies { 64 | implementation fileTree(include: ['*.jar'], dir: 'libs') 65 | // androidTestImplementation('androidx.test.espresso:espresso-core:3.1.1-beta01', { 66 | // exclude group: 'com.android.support', module: 'support-annotations' 67 | // }) 68 | implementation 'androidx.appcompat:appcompat:1.1.0' 69 | // implementation 'com.android.support.constraint:constraint-layout:1.1.2' 70 | implementation 'androidx.media:media:1.1.0' 71 | implementation 'com.google.android.material:material:1.1.0' 72 | implementation 'androidx.recyclerview:recyclerview:1.1.0' 73 | implementation 'de.hdodenhof:circleimageview:2.2.0' 74 | implementation 'com.theartofdev.edmodo:android-image-cropper:2.8.0' 75 | implementation 'com.github.codekidX:storage-chooser:2.0.4.4' 76 | implementation 'com.afollestad.material-dialogs:core:0.9.6.0' 77 | implementation 'com.googlecode.libphonenumber:libphonenumber:8.12.4' 78 | // implementation 'androidx.cardview:cardview:1.0.0' 79 | // implementation 'com.github.topjohnwu:libsu:2.0.2' 80 | implementation 'androidx.preference:preference:1.1.1' 81 | implementation 'com.chibde:audiovisualizer:2.2.0' 82 | //Am renunțat la adăugarea dependenței din jitpack deoarece cînd am trecut la definirea dimensiunilor în 83 | // dimens.xml a apărut un bug: la label_size orice dimensiune puneam crăpa. Nu am făcut nicio modificare în 84 | //sursa bibliotecii, dar versiunea din jitpack era veche. (Am folosit sp în loc de plain integer în dimens.xml 85 | // al meu.) 86 | //https://stackoverflow.com/questions/25610727/adding-external-library-in-android-studio/29791082 87 | implementation project(':Croller') 88 | //https://developers.google.com/analytics/devguides/collection/firebase/android/start 89 | //Firebase folosim numai în release, fără debug 90 | releaseImplementation 'com.google.firebase:firebase-analytics:17.4.2' 91 | releaseImplementation 'com.crashlytics.sdk.android:crashlytics:2.10.1' 92 | 93 | implementation "ch.acra:acra-http:$acraVersion" 94 | implementation "com.google.dagger:dagger:$daggerVersion" 95 | annotationProcessor "com.google.dagger:dagger-compiler:$daggerVersion" 96 | 97 | //test 98 | //nu este nevoie de hamcrest. Este inclus în test.core, cred. 99 | testImplementation "androidx.test:core:$androidXVersion" 100 | testImplementation "androidx.test.ext:junit:$androidJunitVersion" 101 | testImplementation "org.robolectric:robolectric:$robolectricVersion" 102 | testImplementation "org.mockito:mockito-core:$mockitoVersion" 103 | 104 | //androidTest 105 | androidTestImplementation "androidx.test:runner:$runnerVersion" 106 | androidTestImplementation "androidx.test:core:$androidXVersion" 107 | androidTestImplementation "androidx.test.ext:junit:$androidJunitVersion" 108 | androidTestImplementation "org.assertj:assertj-core:$assertjVersion" 109 | } -------------------------------------------------------------------------------- /app/libs/Croller/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.library' 2 | 3 | ext { 4 | bintrayRepo = 'Croller' 5 | bintrayName = 'croller' 6 | 7 | publishedGroupId = 'com.sdsmdg.harjot' 8 | libraryName = 'Croller' 9 | artifact = 'croller' 10 | 11 | libraryDescription = 'A circular seekbar for Android, with a control knob! (for the lack of a better word).' 12 | 13 | siteUrl = 'https://github.com/harjot-oberai/Croller' 14 | gitUrl = 'https://github.com/harjot-oberai/Croller.git' 15 | 16 | libraryVersion = '1.0.7' 17 | 18 | developerId = 'harjot-oberai' 19 | developerName = 'Harjot Singh Oberai' 20 | developerEmail = 'harjot.oberai@gmail.com' 21 | 22 | licenseName = 'The Apache Software License, Version 2.0' 23 | licenseUrl = 'http://www.apache.org/licenses/LICENSE-2.0.txt' 24 | allLicenses = ["Apache-2.0"] 25 | } 26 | 27 | version = '1.0.1' 28 | 29 | android { 30 | compileSdkVersion 27 31 | buildToolsVersion '28.0.3' 32 | 33 | defaultConfig { 34 | minSdkVersion 16 35 | targetSdkVersion 27 36 | versionCode 1 37 | versionName "1.0" 38 | } 39 | buildTypes { 40 | release { 41 | minifyEnabled false 42 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 43 | } 44 | } 45 | } 46 | 47 | dependencies { 48 | implementation fileTree(dir: 'libs', include: ['*.jar']) 49 | testImplementation 'junit:junit:4.12' 50 | implementation 'androidx.appcompat:appcompat:1.1.0-rc01' 51 | } 52 | 53 | //apply from: 'https://raw.githubusercontent.com/nuuneoi/JCenter/master/installv1.gradle' 54 | //apply from: 'https://raw.githubusercontent.com/nuuneoi/JCenter/master/bintrayv1.gradle' -------------------------------------------------------------------------------- /app/libs/Croller/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in C:\Users\Harjot\AppData\Local\Android\sdk/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | -------------------------------------------------------------------------------- /app/libs/Croller/src/androidTest/java/com/sdsmdg/harjot/crollerTest/ApplicationTest.java: -------------------------------------------------------------------------------- 1 | package com.sdsmdg.harjot.crollerTest; 2 | 3 | import android.app.Application; 4 | import android.test.ApplicationTestCase; 5 | 6 | /** 7 | * Testing Fundamentals 8 | */ 9 | public class ApplicationTest extends ApplicationTestCase { 10 | public ApplicationTest() { 11 | super(Application.class); 12 | } 13 | } -------------------------------------------------------------------------------- /app/libs/Croller/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /app/libs/Croller/src/main/java/com/sdsmdg/harjot/crollerTest/OnCrollerChangeListener.java: -------------------------------------------------------------------------------- 1 | package com.sdsmdg.harjot.crollerTest; 2 | 3 | public interface OnCrollerChangeListener { 4 | void onProgressChanged(Croller croller, int progress); 5 | 6 | void onStartTrackingTouch(Croller croller); 7 | 8 | void onStopTrackingTouch(Croller croller); 9 | } 10 | -------------------------------------------------------------------------------- /app/libs/Croller/src/main/java/com/sdsmdg/harjot/crollerTest/utilities/Utils.java: -------------------------------------------------------------------------------- 1 | package com.sdsmdg.harjot.crollerTest.utilities; 2 | 3 | import android.content.Context; 4 | import android.content.res.Resources; 5 | import android.util.DisplayMetrics; 6 | 7 | public class Utils { 8 | public static float getDistance(float x1, float y1, float x2, float y2) { 9 | return (float) Math.sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2)); 10 | } 11 | 12 | public static float convertDpToPixel(float dp, Context context) { 13 | Resources resources = context.getResources(); 14 | DisplayMetrics metrics = resources.getDisplayMetrics(); 15 | return dp * ((float) metrics.densityDpi / DisplayMetrics.DENSITY_DEFAULT); 16 | } 17 | 18 | public static float convertPixelsToDp(float px, Context context) { 19 | Resources resources = context.getResources(); 20 | DisplayMetrics metrics = resources.getDisplayMetrics(); 21 | return px / ((float) metrics.densityDpi / DisplayMetrics.DENSITY_DEFAULT); 22 | } 23 | 24 | } 25 | -------------------------------------------------------------------------------- /app/libs/Croller/src/main/res/layout/croller_view.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /app/libs/Croller/src/main/res/values/attrs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /app/libs/Croller/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | Croller 3 | 4 | -------------------------------------------------------------------------------- /app/libs/Croller/src/test/java/com/sdsmdg/harjot/croller/ExampleUnitTest.java: -------------------------------------------------------------------------------- 1 | package com.sdsmdg.harjot.crollerTest; 2 | 3 | import org.junit.Test; 4 | 5 | import static org.junit.Assert.*; 6 | 7 | /** 8 | * To work on unit tests, switch the Test Artifact in the Build Variants view. 9 | */ 10 | public class ExampleUnitTest { 11 | @Test 12 | public void addition_isCorrect() throws Exception { 13 | assertEquals(4, 2 + 2); 14 | } 15 | } -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in /home/eugen/Android/Sdk/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | 19 | # Uncomment this to preserve the line number information for 20 | # debugging stack traces. 21 | #-keepattributes SourceFile,LineNumberTable 22 | 23 | # If you keep the line number information, uncomment this to 24 | # hide the original source file name. 25 | #-renamesourcefileattribute SourceFile 26 | -keep class android.support.v7.widget.** { *; } -------------------------------------------------------------------------------- /app/src/androidTest/java/net/synapticweb/callrecorder/data/RecordingTest.java: -------------------------------------------------------------------------------- 1 | package net.synapticweb.callrecorder.data; 2 | 3 | import android.content.Context; 4 | 5 | import androidx.test.core.app.ApplicationProvider; 6 | 7 | 8 | import org.junit.After; 9 | import org.junit.Before; 10 | import org.junit.Test; 11 | 12 | 13 | import java.io.File; 14 | import java.io.FileInputStream; 15 | import java.io.FileOutputStream; 16 | import java.io.IOException; 17 | import java.io.InputStream; 18 | import java.io.OutputStream; 19 | import java.security.MessageDigest; 20 | import java.util.concurrent.ThreadLocalRandom; 21 | 22 | import static org.hamcrest.CoreMatchers.is; 23 | 24 | import static org.junit.Assert.*; 25 | 26 | public class RecordingTest { 27 | private static final String FILE_NAME = "testfile"; 28 | private static String RANDOM_FILE_PATH; 29 | private static String DESTINATION_FOLDER; 30 | private static final int FILE_SIZE = 1048576; 31 | private RepositoryImpl repository; 32 | 33 | @Before 34 | public void setup() { 35 | Context context = ApplicationProvider.getApplicationContext(); 36 | RANDOM_FILE_PATH = new File(context.getFilesDir() + "/" + FILE_NAME).getAbsolutePath(); 37 | DESTINATION_FOLDER = context.getExternalFilesDir(null).getAbsolutePath(); 38 | repository = new RepositoryImpl(context, null); 39 | } 40 | 41 | @After 42 | public void removeFile() { 43 | if(new File(DESTINATION_FOLDER + "/" + FILE_NAME).exists()) 44 | new File(DESTINATION_FOLDER + "/" + FILE_NAME).delete(); 45 | } 46 | 47 | private void createRandomFile() { 48 | try { 49 | OutputStream os = new FileOutputStream(RANDOM_FILE_PATH); 50 | for(int i = 0; i < FILE_SIZE; ++i) { 51 | int number = ThreadLocalRandom.current().nextInt(0, 255 + 1); 52 | os.write(number); 53 | } 54 | os.flush(); 55 | } 56 | catch (IOException ignored) {} 57 | } 58 | 59 | private String byteArrayToHexString(byte[] bytes) { 60 | StringBuilder result = new StringBuilder(); 61 | for (byte value : bytes) { 62 | result.append(Integer.toString((value & 0xff) + 0x100, 16).substring(1)); 63 | } 64 | return result.toString(); 65 | } 66 | 67 | private String computeFileSha1(String path) { 68 | String hash = ""; 69 | try { 70 | MessageDigest sha1 = MessageDigest.getInstance("SHA-1"); 71 | InputStream input = new FileInputStream(path); 72 | byte[] buffer = new byte[8192]; 73 | int len = input.read(buffer); 74 | 75 | while (len != -1) { 76 | sha1.update(buffer, 0, len); 77 | len = input.read(buffer); 78 | } 79 | hash = byteArrayToHexString(sha1.digest()); 80 | } 81 | catch (Exception ignored) {} 82 | return hash; 83 | } 84 | 85 | @Test 86 | public void move() { 87 | createRandomFile(); 88 | String hash1 = computeFileSha1(RANDOM_FILE_PATH); 89 | 90 | Recording recording = new Recording(); 91 | recording.setPath(RANDOM_FILE_PATH); 92 | repository.insertRecording(recording); 93 | Long id = recording.getId(); 94 | 95 | try { 96 | recording.move(repository, DESTINATION_FOLDER, null, FILE_SIZE); 97 | } 98 | catch (IOException ignored) {} 99 | 100 | assertTrue(new File(DESTINATION_FOLDER + "/" + FILE_NAME).exists()); 101 | assertFalse(new File(RANDOM_FILE_PATH).exists()); 102 | String hash2 = computeFileSha1(DESTINATION_FOLDER + "/" + FILE_NAME); 103 | assertThat(hash1, is(hash2)); 104 | 105 | assertThat(recording.getPath(), is(DESTINATION_FOLDER + "/" + FILE_NAME)); 106 | recording = repository.getRecording(id); 107 | assertThat(recording.getPath(), is(DESTINATION_FOLDER + "/" + FILE_NAME)); 108 | } 109 | } -------------------------------------------------------------------------------- /app/src/eval/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | 7 | 12 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /app/src/eval/java/net/synapticweb/callrecorder/Config.java: -------------------------------------------------------------------------------- 1 | package net.synapticweb.callrecorder; 2 | 3 | 4 | public class Config { 5 | static final public String FILE_PROVIDER = "net.synapticweb.callrecorder.eval.fileprovider"; 6 | } -------------------------------------------------------------------------------- /app/src/eval/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | SW Call Recorder (evaluation) 3 | -------------------------------------------------------------------------------- /app/src/full/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | 7 | 12 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /app/src/full/java/net/synapticweb/callrecorder/Config.java: -------------------------------------------------------------------------------- 1 | package net.synapticweb.callrecorder; 2 | 3 | 4 | public class Config { 5 | static final public String FILE_PROVIDER = "net.synapticweb.callrecorder.full.fileprovider"; 6 | } -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 31 | 32 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 43 | 44 | 45 | 47 | 48 | 49 | 51 | 52 | 53 | 55 | 56 | 57 | 58 | 60 | 61 | 62 | 64 | 65 | 68 | 69 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | -------------------------------------------------------------------------------- /app/src/main/assets/assign.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/synapticweb/CallRecorder/b90f2a5c79bd338e25f34264e75e969604e3d10f/app/src/main/assets/assign.png -------------------------------------------------------------------------------- /app/src/main/assets/common_style.css: -------------------------------------------------------------------------------- 1 | body { 2 | font-size: 0.85em; 3 | line-height: 1.3em; 4 | } 5 | 6 | h1 { 7 | font-size: 1.6em; 8 | line-height: 1.1em; 9 | } 10 | 11 | h2 { 12 | font-size: 1.2em; 13 | line-height: 1.1em; 14 | } 15 | 16 | .small-image { 17 | width: 35px; 18 | height: 35px; 19 | vertical-align: middle; 20 | } 21 | 22 | .x-small-image { 23 | width: 25px; 24 | height: 25px; 25 | vertical-align: middle; 26 | } 27 | 28 | .block-image { 29 | width: 250px; 30 | display: block; 31 | margin-top:10px; 32 | margin-bottom: 10px; 33 | } 34 | 35 | .license-head { 36 | text-decoration: underline; 37 | } 38 | 39 | /*https://www.bestcssbuttongenerator.com/#/23*/ 40 | .send_logs { 41 | -moz-box-shadow:inset 0px 1px 0px 0px #ffffff; 42 | -webkit-box-shadow:inset 0px 1px 0px 0px #ffffff; 43 | box-shadow:inset 0px 1px 0px 0px #ffffff; 44 | background:-webkit-gradient(linear, left top, left bottom, color-stop(0.05, #ffffff), color-stop(1, #f6f6f6)); 45 | background:-moz-linear-gradient(top, #ffffff 5%, #f6f6f6 100%); 46 | background:-webkit-linear-gradient(top, #ffffff 5%, #f6f6f6 100%); 47 | background:-o-linear-gradient(top, #ffffff 5%, #f6f6f6 100%); 48 | background:-ms-linear-gradient(top, #ffffff 5%, #f6f6f6 100%); 49 | background:linear-gradient(to bottom, #ffffff 5%, #f6f6f6 100%); 50 | filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffff', endColorstr='#f6f6f6',GradientType=0); 51 | background-color:#ffffff; 52 | -moz-border-radius:6px; 53 | -webkit-border-radius:6px; 54 | border-radius:6px; 55 | border:1px solid #dcdcdc; 56 | display:inline-block; 57 | cursor:pointer; 58 | color:#666666; 59 | font-family:Arial; 60 | font-size:15px; 61 | font-weight:bold; 62 | padding:6px 24px; 63 | text-decoration:none; 64 | text-shadow:0px 1px 0px #ffffff; 65 | } 66 | .send_logs:hover { 67 | background:-webkit-gradient(linear, left top, left bottom, color-stop(0.05, #f6f6f6), color-stop(1, #ffffff)); 68 | background:-moz-linear-gradient(top, #f6f6f6 5%, #ffffff 100%); 69 | background:-webkit-linear-gradient(top, #f6f6f6 5%, #ffffff 100%); 70 | background:-o-linear-gradient(top, #f6f6f6 5%, #ffffff 100%); 71 | background:-ms-linear-gradient(top, #f6f6f6 5%, #ffffff 100%); 72 | background:linear-gradient(to bottom, #f6f6f6 5%, #ffffff 100%); 73 | filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#f6f6f6', endColorstr='#ffffff',GradientType=0); 74 | background-color:#f6f6f6; 75 | } 76 | .send_logs:active { 77 | position:relative; 78 | top:1px; 79 | } 80 | -------------------------------------------------------------------------------- /app/src/main/assets/dark_style.css: -------------------------------------------------------------------------------- 1 | body { 2 | background-color: rgba(48,48,48,1.0); 3 | color: white; 4 | } 5 | 6 | a { 7 | color:#ff5030; 8 | } -------------------------------------------------------------------------------- /app/src/main/assets/deleted_audio.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/synapticweb/CallRecorder/b90f2a5c79bd338e25f34264e75e969604e3d10f/app/src/main/assets/deleted_audio.jpg -------------------------------------------------------------------------------- /app/src/main/assets/dots_menu_dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/synapticweb/CallRecorder/b90f2a5c79bd338e25f34264e75e969604e3d10f/app/src/main/assets/dots_menu_dark.png -------------------------------------------------------------------------------- /app/src/main/assets/dots_menu_light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/synapticweb/CallRecorder/b90f2a5c79bd338e25f34264e75e969604e3d10f/app/src/main/assets/dots_menu_light.png -------------------------------------------------------------------------------- /app/src/main/assets/incognito.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/synapticweb/CallRecorder/b90f2a5c79bd338e25f34264e75e969604e3d10f/app/src/main/assets/incognito.png -------------------------------------------------------------------------------- /app/src/main/assets/info_dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/synapticweb/CallRecorder/b90f2a5c79bd338e25f34264e75e969604e3d10f/app/src/main/assets/info_dark.png -------------------------------------------------------------------------------- /app/src/main/assets/info_light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/synapticweb/CallRecorder/b90f2a5c79bd338e25f34264e75e969604e3d10f/app/src/main/assets/info_light.png -------------------------------------------------------------------------------- /app/src/main/assets/ingoing.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/synapticweb/CallRecorder/b90f2a5c79bd338e25f34264e75e969604e3d10f/app/src/main/assets/ingoing.png -------------------------------------------------------------------------------- /app/src/main/assets/launcher_icon-web.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/synapticweb/CallRecorder/b90f2a5c79bd338e25f34264e75e969604e3d10f/app/src/main/assets/launcher_icon-web.png -------------------------------------------------------------------------------- /app/src/main/assets/launcher_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/synapticweb/CallRecorder/b90f2a5c79bd338e25f34264e75e969604e3d10f/app/src/main/assets/launcher_icon.png -------------------------------------------------------------------------------- /app/src/main/assets/light_style.css: -------------------------------------------------------------------------------- 1 | body { 2 | background-color: #faf9f6; 3 | color: rgba(0,0,0,0.62); 4 | } 5 | 6 | a { 7 | color: #D91916; 8 | } -------------------------------------------------------------------------------- /app/src/main/assets/move_dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/synapticweb/CallRecorder/b90f2a5c79bd338e25f34264e75e969604e3d10f/app/src/main/assets/move_dark.png -------------------------------------------------------------------------------- /app/src/main/assets/move_light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/synapticweb/CallRecorder/b90f2a5c79bd338e25f34264e75e969604e3d10f/app/src/main/assets/move_light.png -------------------------------------------------------------------------------- /app/src/main/assets/notification_icon_dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/synapticweb/CallRecorder/b90f2a5c79bd338e25f34264e75e969604e3d10f/app/src/main/assets/notification_icon_dark.png -------------------------------------------------------------------------------- /app/src/main/assets/notification_icon_light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/synapticweb/CallRecorder/b90f2a5c79bd338e25f34264e75e969604e3d10f/app/src/main/assets/notification_icon_light.png -------------------------------------------------------------------------------- /app/src/main/assets/outgoing.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/synapticweb/CallRecorder/b90f2a5c79bd338e25f34264e75e969604e3d10f/app/src/main/assets/outgoing.png -------------------------------------------------------------------------------- /app/src/main/assets/plus.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/synapticweb/CallRecorder/b90f2a5c79bd338e25f34264e75e969604e3d10f/app/src/main/assets/plus.png -------------------------------------------------------------------------------- /app/src/main/assets/select_all_dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/synapticweb/CallRecorder/b90f2a5c79bd338e25f34264e75e969604e3d10f/app/src/main/assets/select_all_dark.png -------------------------------------------------------------------------------- /app/src/main/assets/select_all_light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/synapticweb/CallRecorder/b90f2a5c79bd338e25f34264e75e969604e3d10f/app/src/main/assets/select_all_light.png -------------------------------------------------------------------------------- /app/src/main/assets/selected_rec.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/synapticweb/CallRecorder/b90f2a5c79bd338e25f34264e75e969604e3d10f/app/src/main/assets/selected_rec.jpg -------------------------------------------------------------------------------- /app/src/main/assets/stop_rec.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/synapticweb/CallRecorder/b90f2a5c79bd338e25f34264e75e969604e3d10f/app/src/main/assets/stop_rec.jpg -------------------------------------------------------------------------------- /app/src/main/assets/top_bar_selected.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/synapticweb/CallRecorder/b90f2a5c79bd338e25f34264e75e969604e3d10f/app/src/main/assets/top_bar_selected.jpg -------------------------------------------------------------------------------- /app/src/main/java/net/synapticweb/callrecorder/BaseActivity.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2019 Eugen Rădulescu - All rights reserved. 3 | * 4 | * You may use, distribute and modify this code only under the conditions 5 | * stated in the SW Call Recorder license. You should have received a copy of the 6 | * SW Call Recorder license along with this file. If not, please write to . 7 | */ 8 | 9 | package net.synapticweb.callrecorder; 10 | 11 | import android.content.SharedPreferences; 12 | import android.preference.PreferenceManager; 13 | 14 | import androidx.appcompat.app.AppCompatActivity; 15 | import androidx.fragment.app.Fragment; 16 | import androidx.fragment.app.FragmentManager; 17 | 18 | import net.synapticweb.callrecorder.settings.SettingsFragment; 19 | 20 | 21 | public abstract class BaseActivity extends AppCompatActivity { 22 | private String settedTheme; 23 | public static final String LIGHT_THEME = "light_theme"; 24 | public static final String DARK_THEME = "dark_theme"; 25 | abstract protected Fragment createFragment(); 26 | 27 | public String getSettedTheme() { 28 | return settedTheme; 29 | } 30 | 31 | public enum LayoutType { 32 | SINGLE_PANE, 33 | DOUBLE_PANE 34 | } 35 | 36 | protected void insertFragment(int fragmentId) { 37 | FragmentManager fm = getSupportFragmentManager(); 38 | Fragment fragment = fm.findFragmentById(fragmentId); 39 | if(fragment == null) { 40 | fragment = createFragment(); 41 | fm.beginTransaction(). 42 | add(fragmentId, fragment). 43 | commit(); 44 | } 45 | } 46 | 47 | protected void setTheme() { 48 | final SharedPreferences settings = PreferenceManager.getDefaultSharedPreferences(this); 49 | if(settings.getString(SettingsFragment.APP_THEME, LIGHT_THEME).equals(LIGHT_THEME)) { 50 | settedTheme = LIGHT_THEME; 51 | setTheme(R.style.AppThemeLight); 52 | } 53 | else { 54 | settedTheme = DARK_THEME; 55 | setTheme(R.style.AppThemeDark); 56 | } 57 | } 58 | 59 | protected void checkIfThemeChanged() { 60 | final SharedPreferences settings = PreferenceManager.getDefaultSharedPreferences(this); 61 | if(!settings.getString(SettingsFragment.APP_THEME, LIGHT_THEME).equals(settedTheme)) { 62 | setTheme(); 63 | recreate(); 64 | } 65 | } 66 | 67 | public LayoutType getLayoutType() { 68 | boolean listBoxExists = findViewById(R.id.contacts_list_fragment_container) != null; 69 | boolean detailBoxExists = findViewById(R.id.contact_detail_fragment_container) != null; 70 | if(listBoxExists && detailBoxExists) 71 | return LayoutType.DOUBLE_PANE; 72 | else 73 | return LayoutType.SINGLE_PANE; 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /app/src/main/java/net/synapticweb/callrecorder/CrApp.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2019 Eugen Rădulescu - All rights reserved. 3 | * 4 | * You may use, distribute and modify this code only under the conditions 5 | * stated in the SW Call Recorder license. You should have received a copy of the 6 | * SW Call Recorder license along with this file. If not, please write to . 7 | */ 8 | 9 | package net.synapticweb.callrecorder; 10 | 11 | import android.app.Application; 12 | import android.content.Context; 13 | import net.synapticweb.callrecorder.di.AppComponent; 14 | import net.synapticweb.callrecorder.di.DaggerAppComponent; 15 | 16 | import org.acra.ACRA; 17 | import org.acra.annotation.AcraCore; 18 | import org.acra.annotation.AcraHttpSender; 19 | import org.acra.data.StringFormat; 20 | import org.acra.sender.HttpSender; 21 | 22 | //Oferă context cînd nu este nicio activitate disponibilă. Are nevoie ca să funcționeze de 23 | // android:name=".CrApp" în AndroidManifest.xml 24 | //Servește și ca bibliotecă a aplicației. 25 | @AcraCore(reportFormat = StringFormat.KEY_VALUE_LIST) 26 | @AcraHttpSender(uri = "http://crashes.infopsihologia.ro", 27 | httpMethod = HttpSender.Method.POST ) 28 | public class CrApp extends Application { 29 | public AppComponent appComponent; 30 | 31 | @Override 32 | protected void attachBaseContext(Context base) { 33 | super.attachBaseContext(base); 34 | if(!BuildConfig.DEBUG) 35 | ACRA.init(this); 36 | appComponent = DaggerAppComponent.factory().create(base); 37 | } 38 | 39 | private static CrApp instance; 40 | 41 | public CrApp() { 42 | instance = this; 43 | } 44 | 45 | @Override 46 | public void onCreate() { 47 | super.onCreate(); 48 | // Shell.Config.setFlags(Shell.FLAG_REDIRECT_STDERR); 49 | // Shell.Config.verboseLogging(BuildConfig.DEBUG); 50 | } 51 | 52 | public static CrApp getInstance() { 53 | return instance; 54 | } 55 | 56 | } 57 | -------------------------------------------------------------------------------- /app/src/main/java/net/synapticweb/callrecorder/contactdetail/ContactDetailActivity.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2019 Eugen Rădulescu - All rights reserved. 3 | * 4 | * You may use, distribute and modify this code only under the conditions 5 | * stated in the SW Call Recorder license. You should have received a copy of the 6 | * SW Call Recorder license along with this file. If not, please write to . 7 | */ 8 | 9 | package net.synapticweb.callrecorder.contactdetail; 10 | 11 | import android.content.Intent; 12 | import android.os.Bundle; 13 | import android.widget.TextView; 14 | 15 | 16 | import net.synapticweb.callrecorder.R; 17 | import net.synapticweb.callrecorder.BaseActivity; 18 | import net.synapticweb.callrecorder.contactslist.ContactsListFragment; 19 | import net.synapticweb.callrecorder.data.Contact; 20 | 21 | import androidx.appcompat.app.ActionBar; 22 | import androidx.appcompat.widget.Toolbar; 23 | import androidx.fragment.app.Fragment; 24 | 25 | public class ContactDetailActivity extends BaseActivity { 26 | Contact contact; 27 | 28 | @Override 29 | protected Fragment createFragment() { 30 | return ContactDetailFragment.newInstance(contact); 31 | } 32 | 33 | @Override 34 | protected void onResume() { 35 | super.onResume(); 36 | checkIfThemeChanged(); 37 | } 38 | 39 | @Override 40 | protected void onCreate(Bundle savedInstanceState) { 41 | super.onCreate(savedInstanceState); 42 | setTheme(); 43 | setContentView(R.layout.contact_detail_activity); 44 | Intent intent = getIntent(); 45 | contact = intent.getParcelableExtra(ContactsListFragment.ARG_CONTACT); 46 | 47 | insertFragment(R.id.contact_detail_fragment_container); 48 | 49 | Toolbar toolbar = findViewById(R.id.toolbar_detail); 50 | 51 | TextView title = findViewById(R.id.actionbar_title); 52 | title.setText(contact.getContactName()); 53 | setSupportActionBar(toolbar); 54 | ActionBar actionBar = getSupportActionBar(); 55 | if(actionBar != null) { 56 | actionBar.setDisplayHomeAsUpEnabled(false); 57 | actionBar.setDisplayShowTitleEnabled(false); 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /app/src/main/java/net/synapticweb/callrecorder/contactdetail/ContactDetailContract.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2019 Eugen Rădulescu - All rights reserved. 3 | * 4 | * You may use, distribute and modify this code only under the conditions 5 | * stated in the SW Call Recorder license. You should have received a copy of the 6 | * SW Call Recorder license along with this file. If not, please write to . 7 | */ 8 | 9 | package net.synapticweb.callrecorder.contactdetail; 10 | 11 | import android.app.Activity; 12 | import android.content.Context; 13 | import android.net.Uri; 14 | import net.synapticweb.callrecorder.Util.DialogInfo; 15 | import net.synapticweb.callrecorder.data.Contact; 16 | import net.synapticweb.callrecorder.data.Recording; 17 | import java.util.List; 18 | 19 | public interface ContactDetailContract { 20 | interface View { 21 | void setContact(Contact contact); 22 | void paintViews(List recordings); 23 | boolean isInvalid(); 24 | void setInvalid(boolean invalid); 25 | void removeRecording(Recording recording); 26 | Context getContext(); 27 | } 28 | 29 | interface Presenter { 30 | void loadRecordings(Contact contact); 31 | DialogInfo deleteContact(Contact contact); 32 | DialogInfo deleteRecordings(List recordings); 33 | DialogInfo renameRecording(CharSequence input, Recording recording); 34 | DialogInfo assignToContact(Context context, Uri numberUri, List recordings, Contact contact); 35 | DialogInfo assignToPrivate(Context context, List recordings, Contact contact); 36 | void moveSelectedRecordings(String path, int totalsize, Activity parentActivity, Recording[] recordings); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /app/src/main/java/net/synapticweb/callrecorder/contactdetail/di/ContactDetailComponent.java: -------------------------------------------------------------------------------- 1 | package net.synapticweb.callrecorder.contactdetail.di; 2 | 3 | import net.synapticweb.callrecorder.contactdetail.ContactDetailFragment; 4 | import net.synapticweb.callrecorder.di.FragmentScope; 5 | 6 | import dagger.Subcomponent; 7 | 8 | @Subcomponent(modules = {ViewModule.class, PresenterModule.class}) 9 | @FragmentScope 10 | public interface ContactDetailComponent { 11 | @Subcomponent.Factory 12 | interface Factory { 13 | ContactDetailComponent create(ViewModule module); 14 | } 15 | 16 | void inject(ContactDetailFragment fragment); 17 | } 18 | -------------------------------------------------------------------------------- /app/src/main/java/net/synapticweb/callrecorder/contactdetail/di/PresenterModule.java: -------------------------------------------------------------------------------- 1 | package net.synapticweb.callrecorder.contactdetail.di; 2 | 3 | import net.synapticweb.callrecorder.contactdetail.ContactDetailContract.Presenter; 4 | import net.synapticweb.callrecorder.contactdetail.ContactDetailPresenter; 5 | 6 | import dagger.Binds; 7 | import dagger.Module; 8 | 9 | @Module 10 | public abstract class PresenterModule { 11 | @Binds 12 | abstract Presenter providePresenter(ContactDetailPresenter presenter); 13 | } 14 | -------------------------------------------------------------------------------- /app/src/main/java/net/synapticweb/callrecorder/contactdetail/di/ViewModule.java: -------------------------------------------------------------------------------- 1 | package net.synapticweb.callrecorder.contactdetail.di; 2 | 3 | import net.synapticweb.callrecorder.contactdetail.ContactDetailContract; 4 | import net.synapticweb.callrecorder.contactdetail.ContactDetailFragment; 5 | 6 | import dagger.Module; 7 | import dagger.Provides; 8 | 9 | @Module 10 | public class ViewModule { 11 | private ContactDetailFragment fragment; 12 | public ViewModule(ContactDetailFragment fragment) { 13 | this.fragment = fragment; 14 | } 15 | 16 | @Provides 17 | public ContactDetailContract.View provideView() { 18 | return fragment; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /app/src/main/java/net/synapticweb/callrecorder/contactslist/ContactsListContract.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2019 Eugen Rădulescu - All rights reserved. 3 | * 4 | * You may use, distribute and modify this code only under the conditions 5 | * stated in the SW Call Recorder license. You should have received a copy of the 6 | * SW Call Recorder license along with this file. If not, please write to . 7 | */ 8 | 9 | package net.synapticweb.callrecorder.contactslist; 10 | import net.synapticweb.callrecorder.data.Contact; 11 | import java.util.List; 12 | 13 | 14 | public interface ContactsListContract { 15 | interface View { 16 | void showContacts(List contactList); 17 | void resetCurrentPosition(); 18 | } 19 | 20 | interface Presenter { 21 | void loadContacts(); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /app/src/main/java/net/synapticweb/callrecorder/contactslist/ContactsListPresenter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2019 Eugen Rădulescu - All rights reserved. 3 | * 4 | * You may use, distribute and modify this code only under the conditions 5 | * stated in the SW Call Recorder license. You should have received a copy of the 6 | * SW Call Recorder license along with this file. If not, please write to . 7 | */ 8 | 9 | package net.synapticweb.callrecorder.contactslist; 10 | 11 | import net.synapticweb.callrecorder.data.Repository; 12 | import net.synapticweb.callrecorder.di.FragmentScope; 13 | 14 | import androidx.annotation.NonNull; 15 | import javax.inject.Inject; 16 | 17 | @FragmentScope 18 | public class ContactsListPresenter implements ContactsListContract.Presenter { 19 | @NonNull private ContactsListContract.View view; 20 | private Repository repository; 21 | 22 | @Inject 23 | ContactsListPresenter(@NonNull ContactsListContract.View view, Repository repository) { 24 | this.view = view; 25 | this.repository = repository; 26 | } 27 | 28 | @Override 29 | public void loadContacts() { 30 | repository.getAllContacts(contacts -> view.showContacts(contacts)); 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /app/src/main/java/net/synapticweb/callrecorder/contactslist/di/ContactsListComponent.java: -------------------------------------------------------------------------------- 1 | package net.synapticweb.callrecorder.contactslist.di; 2 | 3 | import net.synapticweb.callrecorder.contactslist.ContactsListFragment; 4 | import net.synapticweb.callrecorder.di.FragmentScope; 5 | import dagger.Subcomponent; 6 | 7 | @Subcomponent(modules = {ViewModule.class, PresenterModule.class}) 8 | @FragmentScope 9 | public interface ContactsListComponent { 10 | 11 | @Subcomponent.Factory 12 | interface Factory { 13 | ContactsListComponent create(ViewModule module); 14 | } 15 | 16 | void inject(ContactsListFragment fragment); 17 | } 18 | -------------------------------------------------------------------------------- /app/src/main/java/net/synapticweb/callrecorder/contactslist/di/PresenterModule.java: -------------------------------------------------------------------------------- 1 | package net.synapticweb.callrecorder.contactslist.di; 2 | 3 | import dagger.Binds; 4 | import dagger.Module; 5 | import net.synapticweb.callrecorder.contactslist.ContactsListContract.Presenter; 6 | import net.synapticweb.callrecorder.contactslist.ContactsListPresenter; 7 | 8 | @Module 9 | public abstract class PresenterModule { 10 | @Binds 11 | public abstract Presenter providePresenter(ContactsListPresenter presenter); 12 | } 13 | -------------------------------------------------------------------------------- /app/src/main/java/net/synapticweb/callrecorder/contactslist/di/ViewModule.java: -------------------------------------------------------------------------------- 1 | package net.synapticweb.callrecorder.contactslist.di; 2 | 3 | import net.synapticweb.callrecorder.contactslist.ContactsListContract; 4 | import net.synapticweb.callrecorder.contactslist.ContactsListFragment; 5 | 6 | import dagger.Module; 7 | import dagger.Provides; 8 | 9 | @Module 10 | public class ViewModule { 11 | private ContactsListFragment fragment; 12 | public ViewModule(ContactsListFragment fragment) { 13 | this.fragment = fragment; 14 | } 15 | 16 | @Provides 17 | public ContactsListContract.View provideView() { 18 | return fragment; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /app/src/main/java/net/synapticweb/callrecorder/data/CallRecorderDbHelper.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2019 Eugen Rădulescu - All rights reserved. 3 | * 4 | * You may use, distribute and modify this code only under the conditions 5 | * stated in the SW Call Recorder license. You should have received a copy of the 6 | * SW Call Recorder license along with this file. If not, please write to . 7 | */ 8 | 9 | package net.synapticweb.callrecorder.data; 10 | 11 | import android.content.Context; 12 | import android.database.sqlite.SQLiteDatabase; 13 | import android.database.sqlite.SQLiteOpenHelper; 14 | import net.synapticweb.callrecorder.data.RecordingsContract.*; 15 | import net.synapticweb.callrecorder.data.ContactsContract.*; 16 | 17 | 18 | public class CallRecorderDbHelper extends SQLiteOpenHelper { 19 | public static final String SQL_CREATE_RECORDINGS = "CREATE TABLE " + 20 | Recordings.TABLE_NAME + " (" + Recordings._ID + " INTEGER NOT NULL PRIMARY KEY, " + 21 | Recordings.COLUMN_NAME_CONTACT_ID + " INTEGER , " + 22 | Recordings.COLUMN_NAME_INCOMING + " INTEGER , " + 23 | Recordings.COLUMN_NAME_PATH + " TEXT , " + 24 | Recordings.COLUMN_NAME_START_TIMESTAMP + " INTEGER , " + 25 | Recordings.COLUMN_NAME_END_TIMESTAMP + " INTEGER , " + 26 | Recordings.COLUMN_NAME_FORMAT + " TEXT , " + 27 | Recordings.COLUMN_NAME_IS_NAME_SET + " INTEGER DEFAULT 0, " + 28 | Recordings.COLUMN_NAME_MODE + " TEXT , " + 29 | Recordings.COLUMN_NAME_SOURCE + " TEXT DEFAULT 'unknown')"; 30 | 31 | public static final String SQL_CREATE_CONTACTS = "CREATE TABLE " + Contacts.TABLE_NAME + 32 | " (" + Contacts._ID + " INTEGER NOT NULL PRIMARY KEY, " + 33 | Contacts.COLUMN_NAME_NUMBER + " TEXT, " + 34 | Contacts.COLUMN_NAME_CONTACT_NAME + " TEXT, " + 35 | Contacts.COLUMN_NAME_PHOTO_URI + " TEXT, " + 36 | Contacts.COLUMN_NAME_PHONE_TYPE + " INTEGER NOT NULL, " + 37 | "CONSTRAINT no_duplicates UNIQUE(" + Contacts.COLUMN_NAME_NUMBER + ") )"; 38 | 39 | 40 | private static final int DATABASE_VERSION = 3; 41 | 42 | CallRecorderDbHelper(Context context, String dbName) { 43 | super(context, dbName, null, DATABASE_VERSION); 44 | } 45 | 46 | @Override 47 | public void onCreate(SQLiteDatabase db) { 48 | db.execSQL(SQL_CREATE_RECORDINGS); 49 | db.execSQL(SQL_CREATE_CONTACTS); 50 | } 51 | 52 | @Override 53 | public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { 54 | String version2 = "ALTER TABLE " + Recordings.TABLE_NAME + " ADD COLUMN " + Recordings.COLUMN_NAME_SOURCE + 55 | " TEXT NOT NULL DEFAULT 'unknown'"; 56 | String version3a = "ALTER TABLE " + Contacts.TABLE_NAME + " RENAME TO " + Contacts.TABLE_NAME + "_old"; 57 | String version3b = "INSERT INTO " + Contacts.TABLE_NAME + "(_id, phone_number, contact_name, photo_uri, phone_type) SELECT _id, phone_number, contact_name, photo_uri, phone_type FROM " 58 | + Contacts.TABLE_NAME + "_old"; 59 | String version3c = "DROP TABLE " + Contacts.TABLE_NAME + "_old"; 60 | if(oldVersion == 1) { 61 | db.execSQL(version2); 62 | db.execSQL(version3a); 63 | db.execSQL(SQL_CREATE_CONTACTS); 64 | db.execSQL(version3b); 65 | db.execSQL(version3c); 66 | } 67 | if(oldVersion == 2) { 68 | db.execSQL(version3a); 69 | db.execSQL(SQL_CREATE_CONTACTS); 70 | db.execSQL(version3b); 71 | db.execSQL(version3c); 72 | } 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /app/src/main/java/net/synapticweb/callrecorder/data/ContactsContract.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2019 Eugen Rădulescu - All rights reserved. 3 | * 4 | * You may use, distribute and modify this code only under the conditions 5 | * stated in the SW Call Recorder license. You should have received a copy of the 6 | * SW Call Recorder license along with this file. If not, please write to . 7 | */ 8 | 9 | package net.synapticweb.callrecorder.data; 10 | 11 | import android.provider.BaseColumns; 12 | 13 | public class ContactsContract { 14 | private ContactsContract(){} 15 | 16 | public static class Contacts implements BaseColumns { 17 | public static final String TABLE_NAME = "contacts"; 18 | 19 | public static final String COLUMN_NAME_NUMBER = "phone_number"; 20 | public static final String COLUMN_NAME_CONTACT_NAME = "contact_name"; 21 | public static final String COLUMN_NAME_PHOTO_URI = "photo_uri"; 22 | public static final String COLUMN_NAME_PHONE_TYPE = "phone_type"; 23 | public static final String COLUMN_NAME_SHOULD_RECORD = "should_record"; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /app/src/main/java/net/synapticweb/callrecorder/data/RecordingsContract.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2019 Eugen Rădulescu - All rights reserved. 3 | * 4 | * You may use, distribute and modify this code only under the conditions 5 | * stated in the SW Call Recorder license. You should have received a copy of the 6 | * SW Call Recorder license along with this file. If not, please write to . 7 | */ 8 | 9 | package net.synapticweb.callrecorder.data; 10 | 11 | import android.provider.BaseColumns; 12 | 13 | class RecordingsContract { 14 | private RecordingsContract(){} 15 | 16 | public static class Recordings implements BaseColumns { 17 | public static final String TABLE_NAME = "recordings"; 18 | public static final String COLUMN_NAME_CONTACT_ID = "contact_id"; 19 | public static final String COLUMN_NAME_INCOMING = "incoming"; 20 | public static final String COLUMN_NAME_PATH = "path"; 21 | public static final String COLUMN_NAME_START_TIMESTAMP = "start_timestamp"; 22 | public static final String COLUMN_NAME_END_TIMESTAMP = "end_timestamp"; 23 | public static final String COLUMN_NAME_IS_NAME_SET = "is_name_set"; 24 | public static final String COLUMN_NAME_FORMAT = "format"; 25 | public static final String COLUMN_NAME_MODE = "mode"; 26 | static final String COLUMN_NAME_SOURCE = "source"; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /app/src/main/java/net/synapticweb/callrecorder/data/Repository.java: -------------------------------------------------------------------------------- 1 | package net.synapticweb.callrecorder.data; 2 | 3 | 4 | import java.util.List; 5 | 6 | public interface Repository { 7 | //Contacts: 8 | interface LoadContactsCallback { 9 | void onContactsLoaded(List contacts); 10 | } 11 | 12 | List getAllContacts(); 13 | 14 | void getAllContacts(LoadContactsCallback callback); 15 | 16 | Long getHiddenNumberContactId(); 17 | 18 | void insertContact(Contact contact); 19 | 20 | void updateContact(Contact contact); 21 | 22 | void deleteContact(Contact contact); 23 | 24 | //Recordings: 25 | interface LoadRecordingsCallback { 26 | void onRecordingsLoaded(List recordings); 27 | } 28 | 29 | void getRecordings(Contact contact, LoadRecordingsCallback callback); 30 | 31 | List getRecordings(Contact contact); 32 | 33 | void insertRecording(Recording recording); 34 | 35 | void updateRecording(Recording recording); 36 | 37 | void deleteRecording(Recording recording); 38 | } 39 | -------------------------------------------------------------------------------- /app/src/main/java/net/synapticweb/callrecorder/di/AppComponent.java: -------------------------------------------------------------------------------- 1 | package net.synapticweb.callrecorder.di; 2 | 3 | import android.content.Context; 4 | 5 | import net.synapticweb.callrecorder.contactdetail.EditContactActivity; 6 | import net.synapticweb.callrecorder.contactdetail.di.ContactDetailComponent; 7 | import net.synapticweb.callrecorder.contactslist.di.ContactsListComponent; 8 | import net.synapticweb.callrecorder.recorder.RecorderService; 9 | 10 | import javax.inject.Singleton; 11 | import dagger.BindsInstance; 12 | import dagger.Component; 13 | 14 | @Singleton 15 | @Component(modules = { RepositoryModule.class, AppSubcomponents.class }) 16 | public interface AppComponent { 17 | @Component.Factory 18 | interface Factory { 19 | AppComponent create(@BindsInstance Context context); 20 | } 21 | 22 | void inject(EditContactActivity activity); 23 | void inject(RecorderService service); 24 | ContactsListComponent.Factory contactsListComponent(); 25 | ContactDetailComponent.Factory contactDetailComponent(); 26 | } 27 | -------------------------------------------------------------------------------- /app/src/main/java/net/synapticweb/callrecorder/di/AppSubcomponents.java: -------------------------------------------------------------------------------- 1 | package net.synapticweb.callrecorder.di; 2 | 3 | import net.synapticweb.callrecorder.contactdetail.di.ContactDetailComponent; 4 | import net.synapticweb.callrecorder.contactslist.di.ContactsListComponent; 5 | 6 | import dagger.Module; 7 | 8 | @Module(subcomponents = {ContactsListComponent.class, ContactDetailComponent.class}) 9 | class AppSubcomponents { 10 | } 11 | -------------------------------------------------------------------------------- /app/src/main/java/net/synapticweb/callrecorder/di/FragmentScope.java: -------------------------------------------------------------------------------- 1 | package net.synapticweb.callrecorder.di; 2 | 3 | import java.lang.annotation.Retention; 4 | import java.lang.annotation.RetentionPolicy; 5 | 6 | import javax.inject.Scope; 7 | 8 | @Scope 9 | @Retention(RetentionPolicy.RUNTIME) 10 | public @interface FragmentScope { 11 | } 12 | -------------------------------------------------------------------------------- /app/src/main/java/net/synapticweb/callrecorder/di/RepositoryModule.java: -------------------------------------------------------------------------------- 1 | package net.synapticweb.callrecorder.di; 2 | 3 | import android.content.Context; 4 | 5 | import net.synapticweb.callrecorder.data.Repository; 6 | import net.synapticweb.callrecorder.data.RepositoryImpl; 7 | 8 | import javax.inject.Singleton; 9 | 10 | import dagger.Module; 11 | import dagger.Provides; 12 | 13 | @Module 14 | class RepositoryModule { 15 | private static final String DATABASE_NAME = "callrecorder.db"; 16 | 17 | @Provides 18 | @Singleton 19 | Repository provideRepository(Context context) { 20 | return new RepositoryImpl(context, DATABASE_NAME); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /app/src/main/java/net/synapticweb/callrecorder/player/PlaybackListenerInterface.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2019 Eugen Rădulescu - All rights reserved. 3 | * 4 | * You may use, distribute and modify this code only under the conditions 5 | * stated in the SW Call Recorder license. You should have received a copy of the 6 | * SW Call Recorder license along with this file. If not, please write to . 7 | */ 8 | 9 | package net.synapticweb.callrecorder.player; 10 | 11 | public interface PlaybackListenerInterface { 12 | 13 | void onDurationChanged(int duration); 14 | 15 | void onPositionChanged(int position); 16 | 17 | void onPlaybackCompleted(); 18 | 19 | void onError(); 20 | 21 | void onReset(); 22 | } 23 | -------------------------------------------------------------------------------- /app/src/main/java/net/synapticweb/callrecorder/player/PlayerAdapter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2019 Eugen Rădulescu - All rights reserved. 3 | * 4 | * You may use, distribute and modify this code only under the conditions 5 | * stated in the SW Call Recorder license. You should have received a copy of the 6 | * SW Call Recorder license along with this file. If not, please write to . 7 | */ 8 | 9 | package net.synapticweb.callrecorder.player; 10 | 11 | import androidx.annotation.IntDef; 12 | import java.lang.annotation.Retention; 13 | import java.lang.annotation.RetentionPolicy; 14 | 15 | interface PlayerAdapter { 16 | 17 | @IntDef({State.UNINITIALIZED, State.INITIALIZED, State.PLAYING, State.PAUSED }) 18 | @Retention(RetentionPolicy.SOURCE) 19 | @interface State { 20 | int UNINITIALIZED = 0; 21 | int INITIALIZED = 1; 22 | int PLAYING = 2; 23 | int PAUSED = 3; 24 | int STOPPED = 4; 25 | } 26 | 27 | void setGain(float gain); 28 | 29 | boolean setMediaPosition(int position); 30 | 31 | boolean loadMedia(String mediaPath); 32 | 33 | void stopPlayer(); 34 | 35 | void play(); 36 | 37 | void reset(); 38 | 39 | void pause(); 40 | 41 | int getPlayerState(); 42 | 43 | void setPlayerState(int state); 44 | 45 | boolean seekTo(int position); 46 | 47 | int getCurrentPosition(); 48 | 49 | int getTotalDuration(); 50 | } 51 | -------------------------------------------------------------------------------- /app/src/main/java/net/synapticweb/callrecorder/player/PlayerException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2019 Eugen Rădulescu - All rights reserved. 3 | * 4 | * You may use, distribute and modify this code only under the conditions 5 | * stated in the SW Call Recorder license. You should have received a copy of the 6 | * SW Call Recorder license along with this file. If not, please write to . 7 | */ 8 | 9 | package net.synapticweb.callrecorder.player; 10 | 11 | class PlayerException extends Exception { 12 | PlayerException(String message) { 13 | super(message); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /app/src/main/java/net/synapticweb/callrecorder/recorder/ControlRecordingReceiver.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2019 Eugen Rădulescu - All rights reserved. 3 | * 4 | * You may use, distribute and modify this code only under the conditions 5 | * stated in the SW Call Recorder license. You should have received a copy of the 6 | * SW Call Recorder license along with this file. If not, please write to . 7 | */ 8 | 9 | package net.synapticweb.callrecorder.recorder; 10 | 11 | import android.app.NotificationManager; 12 | import android.content.BroadcastReceiver; 13 | import android.content.Context; 14 | import android.content.Intent; 15 | 16 | 17 | public class ControlRecordingReceiver extends BroadcastReceiver { 18 | @Override 19 | public void onReceive(Context context, Intent intent) { 20 | NotificationManager nm = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE); 21 | RecorderService service = RecorderService.getService(); 22 | 23 | if(intent.getAction().equals(RecorderService.ACTION_STOP_SPEAKER)) { 24 | service.putSpeakerOff(); 25 | if(nm != null) 26 | nm.notify(RecorderService.NOTIFICATION_ID, service.buildNotification(RecorderService.RECORD_AUTOMMATICALLY, 0)); 27 | } 28 | 29 | else if(intent.getAction().equals(RecorderService.ACTION_START_SPEAKER)) { 30 | service.putSpeakerOn(); 31 | if(nm != null) 32 | nm.notify(RecorderService.NOTIFICATION_ID, service.buildNotification(RecorderService.RECORD_AUTOMMATICALLY, 0)); 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /app/src/main/java/net/synapticweb/callrecorder/recorder/RecordingException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2019 Eugen Rădulescu - All rights reserved. 3 | * 4 | * You may use, distribute and modify this code only under the conditions 5 | * stated in the SW Call Recorder license. You should have received a copy of the 6 | * SW Call Recorder license along with this file. If not, please write to . 7 | */ 8 | 9 | package net.synapticweb.callrecorder.recorder; 10 | 11 | class RecordingException extends Exception { 12 | RecordingException(String message) { 13 | super(message); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /app/src/main/java/net/synapticweb/callrecorder/recorder/RecordingThread.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2019 Eugen Rădulescu - All rights reserved. 3 | * 4 | * You may use, distribute and modify this code only under the conditions 5 | * stated in the SW Call Recorder license. You should have received a copy of the 6 | * SW Call Recorder license along with this file. If not, please write to . 7 | */ 8 | 9 | package net.synapticweb.callrecorder.recorder; 10 | 11 | import android.app.NotificationManager; 12 | import android.content.Context; 13 | import android.content.Intent; 14 | import android.content.SharedPreferences; 15 | import android.media.AudioFormat; 16 | import android.media.AudioRecord; 17 | import android.preference.PreferenceManager; 18 | import net.synapticweb.callrecorder.CrLog; 19 | import net.synapticweb.callrecorder.R; 20 | import net.synapticweb.callrecorder.settings.SettingsFragment; 21 | 22 | 23 | import static android.media.MediaRecorder.AudioSource.VOICE_RECOGNITION; 24 | 25 | abstract class RecordingThread { 26 | static final int SAMPLE_RATE = 44100; 27 | final int channels; 28 | final int bufferSize; 29 | final AudioRecord audioRecord; 30 | protected final Recorder recorder; 31 | protected Context context; 32 | 33 | RecordingThread(Context context, String mode, Recorder recorder) throws RecordingException { 34 | this.context = context; 35 | channels = (mode.equals(Recorder.MONO) ? 1 : 2); 36 | this.recorder = recorder; 37 | bufferSize = AudioRecord.getMinBufferSize(SAMPLE_RATE, channels == 1 ? AudioFormat.CHANNEL_IN_MONO : AudioFormat.CHANNEL_IN_STEREO, 38 | AudioFormat.ENCODING_PCM_16BIT); 39 | audioRecord = createAudioRecord(); 40 | audioRecord.startRecording(); 41 | } 42 | 43 | private AudioRecord createAudioRecord() throws RecordingException { 44 | AudioRecord audioRecord; 45 | SharedPreferences settings = PreferenceManager.getDefaultSharedPreferences(context); 46 | int source = Integer.valueOf(settings.getString(SettingsFragment.SOURCE, 47 | String.valueOf(VOICE_RECOGNITION))); 48 | try { 49 | audioRecord = new AudioRecord(source, SAMPLE_RATE, 50 | channels == 1 ? AudioFormat.CHANNEL_IN_MONO : AudioFormat.CHANNEL_IN_STEREO, 51 | AudioFormat.ENCODING_PCM_16BIT, bufferSize * 10); 52 | } catch (Exception e) { //La VOICE_CALL dă IllegalArgumentException. Aplicația nu se oprește, rămîne 53 | //hanging, nu înregistrează nimic. 54 | throw new RecordingException(e.getMessage()); 55 | } 56 | 57 | if(audioRecord.getState() == AudioRecord.STATE_INITIALIZED) { 58 | CrLog.log(CrLog.DEBUG, "createAudioRecord(): Audio source chosen: " + source); 59 | recorder.setSource(audioRecord.getAudioSource()); 60 | } 61 | 62 | if(audioRecord.getState() != AudioRecord.STATE_INITIALIZED) 63 | throw new RecordingException("Unable to initialize AudioRecord"); 64 | 65 | return audioRecord; 66 | } 67 | 68 | void disposeAudioRecord() { 69 | audioRecord.stop(); 70 | audioRecord.release(); 71 | } 72 | 73 | //e statică ca să poată fi apelată din CopyPcmToWav 74 | static void notifyOnError(Context context) { 75 | RecorderService service = RecorderService.getService(); 76 | if (service != null) { 77 | NotificationManager nm = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE); 78 | if (nm != null) 79 | nm.notify(RecorderService.NOTIFICATION_ID, 80 | service.buildNotification(RecorderService.RECORD_ERROR, 81 | R.string.error_recorder_failed)); 82 | } 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /app/src/main/java/net/synapticweb/callrecorder/settings/SettingsActivity.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2019 Eugen Rădulescu - All rights reserved. 3 | * 4 | * You may use, distribute and modify this code only under the conditions 5 | * stated in the SW Call Recorder license. You should have received a copy of the 6 | * SW Call Recorder license along with this file. If not, please write to . 7 | */ 8 | 9 | package net.synapticweb.callrecorder.settings; 10 | 11 | import android.os.Bundle; 12 | 13 | 14 | import net.synapticweb.callrecorder.R; 15 | import net.synapticweb.callrecorder.BaseActivity; 16 | 17 | import androidx.appcompat.app.ActionBar; 18 | import androidx.appcompat.widget.Toolbar; 19 | import androidx.preference.PreferenceFragmentCompat; 20 | 21 | public class SettingsActivity extends BaseActivity { 22 | @Override 23 | protected PreferenceFragmentCompat createFragment() { 24 | return null; 25 | } 26 | 27 | @Override 28 | protected void onCreate(Bundle savedInstanceState) { 29 | super.onCreate(savedInstanceState); 30 | setTheme(); 31 | setContentView(R.layout.settings_layout); 32 | Toolbar toolbar = findViewById(R.id.toolbar_settings); 33 | setSupportActionBar(toolbar); 34 | ActionBar actionBar = getSupportActionBar(); 35 | if(actionBar != null) 36 | actionBar.setDisplayHomeAsUpEnabled(true); 37 | 38 | getSupportFragmentManager().beginTransaction() 39 | .replace(R.id.settings_container, new SettingsFragment()) 40 | .commit(); 41 | } 42 | 43 | } 44 | -------------------------------------------------------------------------------- /app/src/main/java/net/synapticweb/callrecorder/setup/SetupActivity.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2019 Eugen Rădulescu - All rights reserved. 3 | * 4 | * You may use, distribute and modify this code only under the conditions 5 | * stated in the SW Call Recorder license. You should have received a copy of the 6 | * SW Call Recorder license along with this file. If not, please write to . 7 | */ 8 | 9 | package net.synapticweb.callrecorder.setup; 10 | 11 | import android.content.Intent; 12 | import android.os.Bundle; 13 | 14 | import androidx.annotation.NonNull; 15 | import androidx.annotation.Nullable; 16 | import androidx.fragment.app.Fragment; 17 | import net.synapticweb.callrecorder.R; 18 | import net.synapticweb.callrecorder.BaseActivity; 19 | import net.synapticweb.callrecorder.contactslist.ContactsListActivityMain; 20 | 21 | public class SetupActivity extends BaseActivity { 22 | private int checkResult; 23 | public static final String EXIT_APP = "exit_app"; 24 | 25 | @Override 26 | protected Fragment createFragment() { 27 | if((checkResult & ContactsListActivityMain.EULA_NOT_ACCEPTED) != 0) 28 | return new SetupEulaFragment(); 29 | else if((checkResult & ContactsListActivityMain.PERMS_NOT_GRANTED) != 0) 30 | return new SetupPermissionsFragment(); 31 | else if((checkResult & ContactsListActivityMain.POWER_OPTIMIZED) != 0) 32 | return new SetupPowerFragment(); 33 | else 34 | return null; 35 | } 36 | 37 | @Override 38 | protected void onCreate(@Nullable Bundle savedInstanceState) { 39 | setTheme(); 40 | super.onCreate(savedInstanceState); 41 | setContentView(R.layout.setup_activity); 42 | checkResult = getIntent().getIntExtra(ContactsListActivityMain.SETUP_ARGUMENT, 43 | ContactsListActivityMain.EULA_NOT_ACCEPTED & ContactsListActivityMain.PERMS_NOT_GRANTED & 44 | ContactsListActivityMain.POWER_OPTIMIZED); 45 | insertFragment(R.id.setup_fragment_container); 46 | } 47 | 48 | public int getCheckResult() { 49 | return checkResult; 50 | } 51 | 52 | public void cancelSetup() { 53 | Intent intent = new Intent(); 54 | intent.putExtra(SetupActivity.EXIT_APP, true); 55 | setResult(RESULT_OK, intent); 56 | finish(); 57 | } 58 | 59 | @Override 60 | public void onBackPressed() { 61 | cancelSetup(); 62 | } 63 | 64 | @Override 65 | public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { 66 | super.onRequestPermissionsResult(requestCode, permissions, grantResults); 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /app/src/main/java/net/synapticweb/callrecorder/setup/SetupEulaFragment.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2019 Eugen Rădulescu - All rights reserved. 3 | * 4 | * You may use, distribute and modify this code only under the conditions 5 | * stated in the SW Call Recorder license. You should have received a copy of the 6 | * SW Call Recorder license along with this file. If not, please write to . 7 | */ 8 | 9 | package net.synapticweb.callrecorder.setup; 10 | 11 | import android.content.Intent; 12 | import android.content.SharedPreferences; 13 | import android.os.Bundle; 14 | import android.view.LayoutInflater; 15 | import android.view.View; 16 | import android.view.ViewGroup; 17 | import android.widget.Button; 18 | import android.widget.CheckBox; 19 | import android.widget.TextView; 20 | 21 | import androidx.annotation.NonNull; 22 | import androidx.annotation.Nullable; 23 | import androidx.fragment.app.Fragment; 24 | import androidx.preference.PreferenceManager; 25 | 26 | import net.synapticweb.callrecorder.BuildConfig; 27 | import net.synapticweb.callrecorder.R; 28 | import net.synapticweb.callrecorder.contactslist.ContactsListActivityMain; 29 | 30 | public class SetupEulaFragment extends Fragment { 31 | @Nullable 32 | @Override 33 | public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { 34 | return inflater.inflate(R.layout.setup_eula_fragment, container, false); 35 | 36 | } 37 | 38 | @Override 39 | public void onActivityCreated(@Nullable Bundle savedInstanceState) { 40 | super.onActivityCreated(savedInstanceState); 41 | final SetupActivity parentActivity = (SetupActivity) getActivity(); 42 | final int checkResult = parentActivity.getCheckResult(); 43 | 44 | TextView version = parentActivity.findViewById(R.id.app_version); 45 | version.setText(String.format(parentActivity.getResources().getString(R.string.version_eula_screen), 46 | BuildConfig.VERSION_NAME) ); 47 | 48 | Button showEula = parentActivity.findViewById(R.id.show_eula); 49 | showEula.setOnClickListener(new View.OnClickListener() { 50 | @Override 51 | public void onClick(View view) { 52 | startActivity(new Intent(getActivity(), ShowEulaActivity.class)); 53 | } 54 | }); 55 | 56 | Button cancelButton = parentActivity.findViewById(R.id.setup_confirm_cancel); 57 | cancelButton.setOnClickListener(new View.OnClickListener() { 58 | @Override 59 | public void onClick(View view) { 60 | parentActivity.cancelSetup(); 61 | } 62 | }); 63 | 64 | Button nextButton = parentActivity.findViewById(R.id.setup_confirm_next); 65 | nextButton.setOnClickListener(new View.OnClickListener() { 66 | @Override 67 | public void onClick(View view) { 68 | CheckBox hasAccepted = parentActivity.findViewById(R.id.has_accepted); 69 | if(!hasAccepted.isChecked()) 70 | return ; 71 | 72 | SharedPreferences settings = PreferenceManager.getDefaultSharedPreferences(parentActivity); 73 | SharedPreferences.Editor editor = settings.edit(); 74 | editor.putBoolean(ContactsListActivityMain.HAS_ACCEPTED_EULA, true); 75 | editor.apply(); 76 | 77 | if((checkResult & ContactsListActivityMain.PERMS_NOT_GRANTED) != 0) { 78 | SetupPermissionsFragment permissionsFragment = new SetupPermissionsFragment(); 79 | parentActivity.getSupportFragmentManager().beginTransaction() 80 | .replace(R.id.setup_fragment_container, permissionsFragment) 81 | .commitAllowingStateLoss(); 82 | 83 | } //dacă suntem în această metodă înseamnă că ne aflăm la prima rulare. Deci, trebuie să 84 | // se afișeze cel puțin avertismentul "protected apps" sau și controllerul doze, dacă nu avem 85 | //treabă cu permisiunile. 86 | else { 87 | SetupPowerFragment powerFragment = new SetupPowerFragment(); 88 | parentActivity.getSupportFragmentManager().beginTransaction() 89 | .replace(R.id.setup_fragment_container, powerFragment) 90 | .commitAllowingStateLoss(); 91 | } 92 | } 93 | }); 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /app/src/main/java/net/synapticweb/callrecorder/setup/ShowEulaActivity.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2019 Eugen Rădulescu - All rights reserved. 3 | * 4 | * You may use, distribute and modify this code only under the conditions 5 | * stated in the SW Call Recorder license. You should have received a copy of the 6 | * SW Call Recorder license along with this file. If not, please write to . 7 | */ 8 | 9 | package net.synapticweb.callrecorder.setup; 10 | 11 | import android.os.Bundle; 12 | import android.view.MenuItem; 13 | import android.webkit.WebView; 14 | 15 | import androidx.annotation.NonNull; 16 | import androidx.annotation.Nullable; 17 | import androidx.appcompat.app.ActionBar; 18 | import androidx.appcompat.widget.Toolbar; 19 | import androidx.fragment.app.Fragment; 20 | 21 | import net.synapticweb.callrecorder.HelpActivity; 22 | import net.synapticweb.callrecorder.R; 23 | import net.synapticweb.callrecorder.BaseActivity; 24 | import net.synapticweb.callrecorder.Util; 25 | 26 | public class ShowEulaActivity extends BaseActivity { 27 | @Override 28 | protected Fragment createFragment() { 29 | return null; 30 | } 31 | 32 | @Override 33 | protected void onCreate(@Nullable Bundle savedInstanceState) { 34 | super.onCreate(savedInstanceState); 35 | setContentView(R.layout.setup_show_eula_activity); 36 | 37 | Toolbar toolbar = findViewById(R.id.toolbar_show_eula); 38 | setSupportActionBar(toolbar); 39 | ActionBar actionBar = getSupportActionBar(); 40 | if(actionBar != null) 41 | actionBar.setDisplayHomeAsUpEnabled(true); 42 | String html = Util.rawHtmlToString(R.raw.eula); 43 | html = html.replace(HelpActivity.APP_NAME_PLACEHOLDER, getResources().getString(R.string.app_name)); 44 | 45 | WebView eulaHtml = findViewById(R.id.eula_hmtl); 46 | eulaHtml.loadDataWithBaseURL("file:///android_asset/", 47 | html, "text/html", null, null); 48 | } 49 | 50 | @Override 51 | public boolean onOptionsItemSelected(@NonNull MenuItem item) { 52 | if(item.getItemId() == android.R.id.home) 53 | finish(); 54 | return true; 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /app/src/main/launcher_icon-web.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/synapticweb/CallRecorder/b90f2a5c79bd338e25f34264e75e969604e3d10f/app/src/main/launcher_icon-web.png -------------------------------------------------------------------------------- /app/src/main/res/color/nav_item_state_list.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 7 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/color/text_on_dark.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/color/text_on_light.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-anydpi-v24/notification_icon_success.xml: -------------------------------------------------------------------------------- 1 | 7 | 9 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/baseline_info_black_36.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/synapticweb/CallRecorder/b90f2a5c79bd338e25f34264e75e969604e3d10f/app/src/main/res/drawable-hdpi/baseline_info_black_36.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/baseline_settings_black_36.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/synapticweb/CallRecorder/b90f2a5c79bd338e25f34264e75e969604e3d10f/app/src/main/res/drawable-hdpi/baseline_settings_black_36.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/ic_album_white_24dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/synapticweb/CallRecorder/b90f2a5c79bd338e25f34264e75e969604e3d10f/app/src/main/res/drawable-hdpi/ic_album_white_24dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/ic_play_grey600_24dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/synapticweb/CallRecorder/b90f2a5c79bd338e25f34264e75e969604e3d10f/app/src/main/res/drawable-hdpi/ic_play_grey600_24dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/notification_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/synapticweb/CallRecorder/b90f2a5c79bd338e25f34264e75e969604e3d10f/app/src/main/res/drawable-hdpi/notification_icon.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/notification_icon_error.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/synapticweb/CallRecorder/b90f2a5c79bd338e25f34264e75e969604e3d10f/app/src/main/res/drawable-hdpi/notification_icon_error.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/notification_icon_success.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/synapticweb/CallRecorder/b90f2a5c79bd338e25f34264e75e969604e3d10f/app/src/main/res/drawable-hdpi/notification_icon_success.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/baseline_info_black_36.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/synapticweb/CallRecorder/b90f2a5c79bd338e25f34264e75e969604e3d10f/app/src/main/res/drawable-mdpi/baseline_info_black_36.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/baseline_more_vert_white_24.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/synapticweb/CallRecorder/b90f2a5c79bd338e25f34264e75e969604e3d10f/app/src/main/res/drawable-mdpi/baseline_more_vert_white_24.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/baseline_settings_black_36.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/synapticweb/CallRecorder/b90f2a5c79bd338e25f34264e75e969604e3d10f/app/src/main/res/drawable-mdpi/baseline_settings_black_36.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/notification_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/synapticweb/CallRecorder/b90f2a5c79bd338e25f34264e75e969604e3d10f/app/src/main/res/drawable-mdpi/notification_icon.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/notification_icon_error.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/synapticweb/CallRecorder/b90f2a5c79bd338e25f34264e75e969604e3d10f/app/src/main/res/drawable-mdpi/notification_icon_error.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/notification_icon_success.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/synapticweb/CallRecorder/b90f2a5c79bd338e25f34264e75e969604e3d10f/app/src/main/res/drawable-mdpi/notification_icon_success.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/baseline_info_black_36.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/synapticweb/CallRecorder/b90f2a5c79bd338e25f34264e75e969604e3d10f/app/src/main/res/drawable-xhdpi/baseline_info_black_36.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/baseline_more_vert_white_24.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/synapticweb/CallRecorder/b90f2a5c79bd338e25f34264e75e969604e3d10f/app/src/main/res/drawable-xhdpi/baseline_more_vert_white_24.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/baseline_settings_black_36.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/synapticweb/CallRecorder/b90f2a5c79bd338e25f34264e75e969604e3d10f/app/src/main/res/drawable-xhdpi/baseline_settings_black_36.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/notification_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/synapticweb/CallRecorder/b90f2a5c79bd338e25f34264e75e969604e3d10f/app/src/main/res/drawable-xhdpi/notification_icon.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/notification_icon_error.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/synapticweb/CallRecorder/b90f2a5c79bd338e25f34264e75e969604e3d10f/app/src/main/res/drawable-xhdpi/notification_icon_error.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/notification_icon_success.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/synapticweb/CallRecorder/b90f2a5c79bd338e25f34264e75e969604e3d10f/app/src/main/res/drawable-xhdpi/notification_icon_success.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/baseline_info_black_36.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/synapticweb/CallRecorder/b90f2a5c79bd338e25f34264e75e969604e3d10f/app/src/main/res/drawable-xxhdpi/baseline_info_black_36.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/baseline_more_vert_white_24.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/synapticweb/CallRecorder/b90f2a5c79bd338e25f34264e75e969604e3d10f/app/src/main/res/drawable-xxhdpi/baseline_more_vert_white_24.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/baseline_settings_black_36.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/synapticweb/CallRecorder/b90f2a5c79bd338e25f34264e75e969604e3d10f/app/src/main/res/drawable-xxhdpi/baseline_settings_black_36.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/notification_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/synapticweb/CallRecorder/b90f2a5c79bd338e25f34264e75e969604e3d10f/app/src/main/res/drawable-xxhdpi/notification_icon.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/notification_icon_error.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/synapticweb/CallRecorder/b90f2a5c79bd338e25f34264e75e969604e3d10f/app/src/main/res/drawable-xxhdpi/notification_icon_error.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/notification_icon_success.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/synapticweb/CallRecorder/b90f2a5c79bd338e25f34264e75e969604e3d10f/app/src/main/res/drawable-xxhdpi/notification_icon_success.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxxhdpi/baseline_info_black_36.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/synapticweb/CallRecorder/b90f2a5c79bd338e25f34264e75e969604e3d10f/app/src/main/res/drawable-xxxhdpi/baseline_info_black_36.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxxhdpi/baseline_more_vert_white_24.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/synapticweb/CallRecorder/b90f2a5c79bd338e25f34264e75e969604e3d10f/app/src/main/res/drawable-xxxhdpi/baseline_more_vert_white_24.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxxhdpi/baseline_settings_black_36.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/synapticweb/CallRecorder/b90f2a5c79bd338e25f34264e75e969604e3d10f/app/src/main/res/drawable-xxxhdpi/baseline_settings_black_36.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxxhdpi/notification_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/synapticweb/CallRecorder/b90f2a5c79bd338e25f34264e75e969604e3d10f/app/src/main/res/drawable-xxxhdpi/notification_icon.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxxhdpi/notification_icon_error.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/synapticweb/CallRecorder/b90f2a5c79bd338e25f34264e75e969604e3d10f/app/src/main/res/drawable-xxxhdpi/notification_icon_error.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/back.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/call.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/checkbox_checked.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 10 | 13 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/checkbox_unchecked.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 11 | 13 | 14 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/close.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 11 | 13 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/done.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/edit.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/email.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/error.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 11 | 14 | 17 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/exclamation.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/grid.xml: -------------------------------------------------------------------------------- 1 | 3 | 6 | 9 | 12 | 15 | 18 | 21 | 24 | 27 | 30 | 33 | 36 | 39 | 42 | 45 | 48 | 51 | 54 | 57 | 58 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/header.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/synapticweb/CallRecorder/b90f2a5c79bd338e25f34264e75e969604e3d10f/app/src/main/res/drawable/header.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/header_back.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/synapticweb/CallRecorder/b90f2a5c79bd338e25f34264e75e969604e3d10f/app/src/main/res/drawable/header_back.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/help.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_launcher_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 10 | 12 | 14 | 16 | 18 | 20 | 22 | 24 | 26 | 28 | 30 | 32 | 34 | 36 | 38 | 40 | 42 | 44 | 46 | 48 | 50 | 52 | 54 | 56 | 58 | 60 | 62 | 64 | 66 | 68 | 70 | 72 | 74 | 75 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_plus.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/incognito.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/incoming.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/info.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/synapticweb/CallRecorder/b90f2a5c79bd338e25f34264e75e969604e3d10f/app/src/main/res/drawable/launcher.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/menu.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/outgoing_dark.xml: -------------------------------------------------------------------------------- 1 | 3 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/outgoing_light.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/player_pause.xml: -------------------------------------------------------------------------------- 1 | 3 | 6 | 9 | 12 | 13 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/player_play.xml: -------------------------------------------------------------------------------- 1 | 3 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/player_stop.xml: -------------------------------------------------------------------------------- 1 | 3 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/question_mark.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/recorder.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/select_all.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/select_export.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 11 | 14 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/settings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/synapticweb/CallRecorder/b90f2a5c79bd338e25f34264e75e969604e3d10f/app/src/main/res/drawable/settings.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/sound_bottom.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 7 | 8 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/speaker_phone_off.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 17 | 18 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/speaker_phone_on.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/stars.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/success.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 11 | 18 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/user_bottom.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/user_contact.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/warning.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 11 | 16 | 21 | 24 | 27 | 30 | -------------------------------------------------------------------------------- /app/src/main/res/layout/contact.xml: -------------------------------------------------------------------------------- 1 | 2 | 10 | 11 | 19 | 20 | 27 | 32 | 33 | 38 | 39 | 40 | 41 | 48 | 49 | 50 | 51 | 57 | 58 | -------------------------------------------------------------------------------- /app/src/main/res/layout/contact_detail_fragment.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 14 | 15 | 24 | 25 | 31 | 32 | 38 | 39 | 46 | 47 | 48 | 49 | 50 | 61 | 62 | 63 | 71 | 72 | -------------------------------------------------------------------------------- /app/src/main/res/layout/help_activity.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 19 | 20 | 26 | 27 | 28 | 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /app/src/main/res/layout/help_fragment.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /app/src/main/res/layout/info_storage_dialog.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 13 | 14 | 20 | 27 | 28 | 33 | 34 | 35 | 41 | 48 | 49 | 54 | 55 | 56 | -------------------------------------------------------------------------------- /app/src/main/res/layout/list_contacts_fragment.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 18 | 19 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /app/src/main/res/layout/navdraw_header.xml: -------------------------------------------------------------------------------- 1 | 2 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /app/src/main/res/layout/recording.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 16 | 17 | 25 | 26 | 34 | 35 | 44 | 45 | 53 | 54 | 55 | -------------------------------------------------------------------------------- /app/src/main/res/layout/settings_layout.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 20 | 21 | 22 | 23 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /app/src/main/res/layout/setup_activity.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/layout/setup_permissions_fragment.xml: -------------------------------------------------------------------------------- 1 | 2 | 10 | 11 | 19 | 20 | 28 | 29 | 35 | 36 | 41 | 42 | 48 | 49 | 55 | 56 | 62 | 63 | 69 | 70 | 71 | 72 | 73 | 74 |

22 | 23 | 24 | -------------------------------------------------------------------------------- /app/src/main/res/raw/help_licences.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Licenses 5 | 6 | 7 | 8 | 9 |

Apache License v2

10 |

License for libraries:

11 | 19 | 20 | License text 21 | 22 |

Mozilla Public License 2.0

23 |

License for libraries:

24 | 27 | 28 | License text 29 | 30 |

MIT License

31 |

License for libraries:

32 | 35 | 36 | License text 37 | 38 | 39 | -------------------------------------------------------------------------------- /app/src/main/res/raw/help_managing_recordings.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Managing recordings 5 | 6 | 7 | 8 | 9 | 10 |

Selecting recordings

11 |

While tapping a recording opens the audio player, long pressing a recording selects it. When a recording is selected, the contact details screen enters the select mode. When the select mode is on the following things happen: 12 |

    13 |
  1. The recordings are decorated with a checkbox at the left extremity. The selected recordings have this checkbox checked. 14 | 15 |
  2. 16 |
  3. The top bar hides the buttons and the menu for managing the contact and shows instead three buttons and a menu for managing the recording. 17 | 18 |
  4. 19 |
  5. While the select mode is on, simply tapping a recordings selects it.
  6. 20 |
21 | The select mode is cleared when there are no more selected recordings or when one or more recordings are deleted. 22 |

23 | 24 |

Select mode actions

25 |

While in select mode you can manage the selected recordings in several ways:

26 |

Tapping the button will open a dialog which shows some useful information about the currently selected recording, like date and hour of the call, its duration, the file size, the audio format and the location of the audio file. If two or more recordings are selected, this dialog only shows the total file size of the selected recordings.

27 |

Tapping the button will select all of the recordings.

28 |

Tapping the button will show a menu with 2 options: 29 |

    30 |
  • Move to private storage
  • 31 |
  • Move to public storage
  • 32 |
33 | If at least one of the selected recordings is already located in the private space of the application, the first option will be disabled. If you tap the second option a folder chooser dialog will show up assisting you in choosing the folder where you want to move the selected recordings. If you move the recordings in a public storage location you will be able to copy them to a computer or another mobile device.
34 | After the move finishes, a final dialog will inform you if the move was done successfully or if errors have happened during the file move. 35 |

36 | 37 |

Tapping the button in the right end of the top bar will show a menu with two entries: "Rename" and "Delete".
38 | "Rename" allows you to give a name of your choice to the recordings. If more than one recording is selected this entry will be disabled.
39 | "Delete" will delete the selected recordings. Caution: this action cannot be undone. 40 |

41 | 42 |

Unavailable audio files

43 |

If a recording is stored in a public storage you can accidentally delete or move the audio file. When this happens the recording will be grayed out and decorated with an exclamation sign: 44 | 45 | If you cannot recover the associated audio file you should delete the recording. 46 |

47 | 48 | 49 | -------------------------------------------------------------------------------- /app/src/main/res/raw/help_playing_recordings.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Playing recordings 5 | 6 | 7 | 8 | 9 |

To play a recording you simply tap the name of the recording in the contact details screen. This opens the audio player and the recording immediately starts playing. The audio player has support for the standard functions of play, pause, seek and reset.

10 |

Volume controls

11 |

The audio player has two aditional controls implemented with circular seekbars. The one on the left controls the audio volume of the device - i.e the volume of the speaker.
12 | The one on the right allows you to add gain to the audio signal of the recording while playing, i.e. to boost it.
13 | The two controls can be used simultaneously in order to obtain a fine control of the audio volume of the played recording. This can help circumventing the problem of playing on the device a recording with low voice volume.

14 | 15 | -------------------------------------------------------------------------------- /app/src/main/res/raw/privacy_policy.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Privacy policy 5 | 6 | 7 | 8 | 9 |

Privacy policy

10 |

Definitions

11 |

"Application" shall mean %s mobile application.

12 |

"User" shall mean an individual or organization that makes use of the Application.

13 | 14 |

Information collected

15 |

The Application collects the following information:

16 |
    17 |
  1. Information obtained through Google Firebase Analytics. This information may refer to the geographic location of the User, their operating system version, their device model, their pattern of interaction with the Application and the installed version of the Application. This information is anonymous and is used solely for the purpose of improving the Application. This information is not shared with third parties and is not used for advertisement purposes.

  2. 18 | 19 |
  3. Information obtained by analyzing the logs produced by the Application. This information refer to the hardware capabilities of the User's device, their operating system version, the installed version of the Application, the date and time of the phone calls, the type of the call (incoming or outgoing), the phone numbers involved in calls, the behavior of the Application while recording phone calls. Please keep in mind that unless the User decides to send their logs to the developer, the developer has no possibility to access the application's logs. The information obtained by analyzing the application's logs are used solely for the purpose of debugging and improving the Application. This information are not shared with third parties and are not used for advertisement purposes.

  4. 20 |
21 | 22 | 23 | -------------------------------------------------------------------------------- /app/src/main/res/values-sw380dp/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 80dp 4 | 20dp 5 | 30dp 6 | 10dp 7 | 20dp 8 | 10dp 9 | 20dp 10 | 60dp 11 | 20dp 12 | 25dp 13 | 25dp 14 | 150dp 15 | 150dp 16 | 15 17 | 13sp 18 | 25 19 | 15 20 | 170dp 21 | 90dp 22 | 65dp 23 | 110dp 24 | 110dp 25 | 100dp 26 | 26sp 27 | 18sp 28 | 40dp 29 | -------------------------------------------------------------------------------- /app/src/main/res/values-sw600dp/refs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | @layout/contacts_list_activity_twopane 5 | -------------------------------------------------------------------------------- /app/src/main/res/values/arrays.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | @color/primaryDarkColor 6 | @color/textOnDark 7 | #252525 8 | @android:color/white 9 | #e1e1e1 10 | @color/colorAccent 11 | 12 | 13 | @color/colorAccentLight 14 | #252525 15 | @android:color/white 16 | #151515 17 | @color/chevronBgColor 18 | @color/white 19 | @color/colorAccentLighter 20 | #2c9633 21 | #151515 22 | 23 | 24 | 25 | 26 | @color/primaryDarkColor 27 | @color/textOnDark 28 | @color/white 29 | @color/textOnLight 30 | @color/textOnLight 31 | @color/colorAccent 32 | 33 | 34 | @color/colorAccent 35 | @color/white 36 | @color/textOnLight 37 | #151515 38 | @color/chevronBgColor 39 | @color/textOnLight 40 | @color/colorAccentLighter 41 | #2c9633 42 | #151515 43 | 44 | 45 | 46 | @string/settings_storage_names_private 47 | @string/settings_storage_names_public- 48 | 49 | 50 | 51 | private 52 | public 53 | 54 | 55 | 56 | @string/settings_theme_light 57 | @string/settings_theme_dark 58 | 59 | 60 | 61 | light_theme 62 | dark_theme 63 | 64 | 65 | 66 | WAV 67 | AAC High (128kbps) 68 | AAC Medium (64kbps) 69 | AAC Basic (32kbps) 70 | 71 | 72 | 73 | wav 74 | aac_hi 75 | aac_med 76 | aac_bas 77 | 78 | 79 | 80 | Mono 81 | Stereo 82 | 83 | 84 | 85 | mono 86 | stereo 87 | 88 | 89 | 90 | Voice call 91 | Voice recognition 92 | Voice communication 93 | Microphone 94 | 95 | 96 | 97 | 4 98 | 6 99 | 7 100 | 1 101 | 102 | -------------------------------------------------------------------------------- /app/src/main/res/values/attrs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | #6C8C1C 5 | #597317 6 | 7 | #D91916 8 | #FF3633 9 | #ff775f 10 | #ffffff 11 | #ffffff 12 | 13 | #faf9f6 14 | #f5feff 15 | #5f5f5f 16 | #1e1e1e 17 | #f0edec 18 | 19 | #FFFFFF 20 | #FFF 21 | #99ffffff 22 | #ff424242 23 | #ff000000 24 | #fff 25 | #ddd 26 | #52CC52 27 | #008000 28 | #E62222 29 | #a0000000 30 | #f0e2e7 31 | #2e2e2e 32 | #2e2e2e 33 | @color/white 34 | #3c3c3c 35 | #f0f0f0 36 | #D94441 37 | #FF303030 38 | 39 | -------------------------------------------------------------------------------- /app/src/main/res/values/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 70dp 4 | 20dp 5 | 30dp 6 | 10dp 7 | 20dp 8 | 10dp 9 | 20dp 10 | 50dp 11 | 20dp 12 | 25dp 13 | 25dp 14 | 120dp 15 | 120dp 16 | 10 17 | 10sp 18 | 15 19 | 10 20 | 140dp 21 | 75dp 22 | 55dp 23 | 100dp 24 | 90dp 25 | 22sp 26 | 16sp 27 | 50dp 28 | 20dp 29 | -------------------------------------------------------------------------------- /app/src/main/res/values/refs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | @layout/contacts_list_activity_onepane 5 | -------------------------------------------------------------------------------- /app/src/main/res/xml/files.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/xml/preferences.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 10 | 11 | 18 | 19 | 22 | 23 | 28 | 29 | 37 | 38 | 45 | 46 | 52 | 53 | 54 | 61 | -------------------------------------------------------------------------------- /app/src/test/java/net/synapticweb/callrecorder/data/ContactTest.java: -------------------------------------------------------------------------------- 1 | package net.synapticweb.callrecorder.data; 2 | 3 | 4 | import net.synapticweb.callrecorder.Util; 5 | 6 | import org.junit.Before; 7 | import org.junit.Test; 8 | import static org.junit.Assert.*; 9 | import static org.hamcrest.CoreMatchers.*; 10 | 11 | 12 | public class ContactTest { 13 | private FakeRepository repository = new FakeRepository(); 14 | 15 | @Before 16 | public void setup() { 17 | repository.addContact("contact", "0744456740"); 18 | } 19 | 20 | @Test 21 | public void queryNumberInAppContacts_matches_number() { 22 | String[] numbers = {"0744456740", "0744 456 740", "+40744456740", "+40 744 456740"}; 23 | for(String number : numbers) { 24 | Contact foundContact = Contact.queryNumberInAppContacts(repository, number); 25 | assertNotNull(foundContact); 26 | } 27 | 28 | String numberNotInDb = "0723456880"; 29 | assertNull(Contact.queryNumberInAppContacts(repository, numberNotInDb)); 30 | } 31 | 32 | @Test 33 | public void setPhoneType_StringParam() { 34 | Contact contact = new Contact(); 35 | for(Util.PhoneTypeContainer container : Util.PHONE_TYPES) { 36 | contact.setPhoneType(container.getTypeName()); 37 | assertThat(contact.getPhoneTypeCode(), is(container.getTypeCode())); 38 | assertThat(contact.getPhoneTypeName(), is(container.getTypeName())); 39 | } 40 | 41 | contact = new Contact(); 42 | contact.setPhoneType("random string"); 43 | assertThat(contact.getPhoneTypeCode(), is(Util.UNKNOWN_TYPE_PHONE_CODE)); 44 | } 45 | 46 | @Test 47 | public void setPhoneType_IntegerParam() { 48 | Contact contact = new Contact(); 49 | for(Util.PhoneTypeContainer container : Util.PHONE_TYPES) { 50 | contact.setPhoneType(container.getTypeCode()); 51 | assertThat(contact.getPhoneTypeCode(), is(container.getTypeCode())); 52 | assertThat(contact.getPhoneTypeName(), is(container.getTypeName())); 53 | } 54 | 55 | contact = new Contact(); 56 | contact.setPhoneType(400); 57 | assertThat(contact.getPhoneTypeCode(), is(Util.UNKNOWN_TYPE_PHONE_CODE)); 58 | } 59 | } -------------------------------------------------------------------------------- /app/src/test/java/net/synapticweb/callrecorder/data/ContactTestRobolectric.java: -------------------------------------------------------------------------------- 1 | package net.synapticweb.callrecorder.data; 2 | 3 | import android.content.ContentResolver; 4 | import android.content.pm.ProviderInfo; 5 | import android.database.MatrixCursor; 6 | import android.net.Uri; 7 | import android.os.Build; 8 | 9 | import androidx.test.core.app.ApplicationProvider; 10 | import androidx.test.ext.junit.runners.AndroidJUnit4; 11 | 12 | import net.synapticweb.callrecorder.Util; 13 | import net.synapticweb.callrecorder.data.FakePhoneLookupProvider.ContactData; 14 | 15 | import org.junit.Test; 16 | import org.junit.runner.RunWith; 17 | import org.robolectric.Robolectric; 18 | import org.robolectric.android.controller.ContentProviderController; 19 | import org.robolectric.annotation.Config; 20 | 21 | import static org.hamcrest.CoreMatchers.is; 22 | import static org.junit.Assert.*; 23 | import static org.mockito.ArgumentMatchers.any; 24 | import static org.mockito.ArgumentMatchers.eq; 25 | import static org.mockito.Mockito.mock; 26 | import static org.mockito.Mockito.when; 27 | 28 | //@RunWith(RobolectricTestRunner.class) 29 | @RunWith(AndroidJUnit4.class) 30 | @Config(sdk = Build.VERSION_CODES.P) //https://stackoverflow.com/questions/56821193/does-robolectric-require-java-9 31 | public class ContactTestRobolectric { 32 | private static final String GOOD_NUMBER = "0736192257"; 33 | private static final String BAD_NUMBER = "0744567889"; 34 | private static final String CONTACT_NAME = "contact name"; 35 | private static final int CONTACT_TYPE = Util.UNKNOWN_TYPE_PHONE_CODE; 36 | private static final String PHOTO_URI = "photo_uri"; 37 | 38 | //Cod vechi, ideea de la care am pornit: https://github.com/juanmendez/android_dev/blob/master/16.observers/00.magazineAppWithRx/app/src/test/java/ContentProviderTest.java 39 | //https://stackoverflow.com/questions/18290864/create-a-cursor-from-hardcoded-array-instead-of-db 40 | //Ceea ce m-a lămurit într-un final: https://stackoverflow.com/questions/18022923/robolectric-contentprovider-testing 41 | @Test 42 | public void queryNumberInPhoneContacts_return_ok() { 43 | ContentResolver resolver = ApplicationProvider.getApplicationContext().getContentResolver(); 44 | ProviderInfo info = new ProviderInfo(); 45 | info.authority = FakePhoneLookupProvider.AUTHORITY; 46 | ContentProviderController controller = 47 | Robolectric.buildContentProvider(FakePhoneLookupProvider.class).create(info); 48 | 49 | FakePhoneLookupProvider provider = controller.get(); 50 | 51 | provider.addContact(new ContactData(CONTACT_NAME, GOOD_NUMBER, CONTACT_TYPE, PHOTO_URI)); 52 | Contact contact = Contact.queryNumberInPhoneContacts(GOOD_NUMBER, resolver); 53 | assertNotNull(contact); 54 | assertThat(contact.getContactName(), is(CONTACT_NAME)); 55 | assertThat(contact.getPhoneNumber(), is(GOOD_NUMBER)); 56 | assertThat(contact.getPhoneTypeCode(), is(CONTACT_TYPE)); 57 | assertThat(contact.getPhotoUri(), is(Uri.parse(PHOTO_URI))); 58 | 59 | contact = Contact.queryNumberInPhoneContacts(BAD_NUMBER, resolver); 60 | assertNull(contact); 61 | } 62 | 63 | @Test 64 | public void queryNumberInPhoneContacts_with_mock() { 65 | ContentResolver resolver = mock(ContentResolver.class); 66 | Uri goodLookupUri = Uri.withAppendedPath( 67 | android.provider.ContactsContract.PhoneLookup.CONTENT_FILTER_URI, 68 | Uri.encode(GOOD_NUMBER)); 69 | Uri badLookupUri = Uri.withAppendedPath( 70 | android.provider.ContactsContract.PhoneLookup.CONTENT_FILTER_URI, 71 | Uri.encode(BAD_NUMBER)); 72 | String[] columns = new String[] { android.provider.ContactsContract.PhoneLookup.NUMBER, 73 | android.provider.ContactsContract.PhoneLookup.TYPE, 74 | android.provider.ContactsContract.PhoneLookup.DISPLAY_NAME, 75 | android.provider.ContactsContract.PhoneLookup.PHOTO_URI }; 76 | MatrixCursor emptyCursor = new MatrixCursor(columns); 77 | MatrixCursor dataCursor = new MatrixCursor(columns); 78 | dataCursor.addRow(new Object[] {GOOD_NUMBER, Util.UNKNOWN_TYPE_PHONE_CODE, "NAME", null}); 79 | 80 | when(resolver.query(eq(goodLookupUri), any(), any(), any(), any())) 81 | .thenReturn(dataCursor); 82 | 83 | when(resolver.query(eq(badLookupUri), any(), any(), any(), any())) 84 | .thenReturn(emptyCursor); 85 | 86 | Contact contact = Contact.queryNumberInPhoneContacts(GOOD_NUMBER, resolver); 87 | assertNotNull(contact); 88 | contact = Contact.queryNumberInPhoneContacts(BAD_NUMBER, resolver); 89 | assertNull(contact); 90 | } 91 | } -------------------------------------------------------------------------------- /app/src/test/java/net/synapticweb/callrecorder/data/FakePhoneLookupProvider.java: -------------------------------------------------------------------------------- 1 | package net.synapticweb.callrecorder.data; 2 | 3 | import android.content.ContentProvider; 4 | import android.content.ContentValues; 5 | import android.database.Cursor; 6 | import android.database.MatrixCursor; 7 | import android.net.Uri; 8 | 9 | import androidx.annotation.NonNull; 10 | import androidx.annotation.Nullable; 11 | 12 | import java.util.ArrayList; 13 | import java.util.List; 14 | 15 | public class FakePhoneLookupProvider extends ContentProvider { 16 | private List data = new ArrayList<>(); 17 | static final String AUTHORITY = "com.android.contacts"; 18 | 19 | void addContact(ContactData contact) { 20 | data.add(contact); 21 | } 22 | 23 | private ContactData findByNumber(String number) { 24 | for(ContactData contact : data) 25 | if(contact.number.equals(number)) 26 | return contact; 27 | return null; 28 | } 29 | 30 | @Override 31 | public boolean onCreate() { 32 | return false; 33 | } 34 | 35 | @Nullable 36 | @Override 37 | public Cursor query(@NonNull Uri uri, @Nullable String[] strings, @Nullable String s, @Nullable String[] strings1, @Nullable String s1) { 38 | String number = uri.getLastPathSegment(); 39 | String[] columns = new String[] { android.provider.ContactsContract.PhoneLookup.NUMBER, 40 | android.provider.ContactsContract.PhoneLookup.TYPE, 41 | android.provider.ContactsContract.PhoneLookup.DISPLAY_NAME, 42 | android.provider.ContactsContract.PhoneLookup.PHOTO_URI }; 43 | MatrixCursor cursor = new MatrixCursor(columns); 44 | 45 | ContactData contact; 46 | if((contact = findByNumber(number)) != null ) { 47 | cursor.addRow(new Object[] {contact.number, contact.type, contact.name, contact.photoUri}); 48 | } 49 | 50 | return cursor; 51 | } 52 | 53 | @Nullable 54 | @Override 55 | public String getType(@NonNull Uri uri) { 56 | return null; 57 | } 58 | 59 | @Nullable 60 | @Override 61 | public Uri insert(@NonNull Uri uri, @Nullable ContentValues contentValues) { 62 | return null; 63 | } 64 | 65 | @Override 66 | public int delete(@NonNull Uri uri, @Nullable String s, @Nullable String[] strings) { 67 | return 0; 68 | } 69 | 70 | @Override 71 | public int update(@NonNull Uri uri, @Nullable ContentValues contentValues, @Nullable String s, @Nullable String[] strings) { 72 | return 0; 73 | } 74 | 75 | static class ContactData { 76 | ContactData(String name, String number, int type, String photoUri) { 77 | this.name = name; 78 | this.number = number; 79 | this.type = type; 80 | this.photoUri = photoUri; 81 | } 82 | 83 | String name; 84 | String number; 85 | int type; 86 | String photoUri; 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /app/src/test/java/net/synapticweb/callrecorder/data/FakeRepository.java: -------------------------------------------------------------------------------- 1 | package net.synapticweb.callrecorder.data; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | public class FakeRepository implements Repository { 7 | private List contacts = new ArrayList<>(); 8 | private List recordings = new ArrayList<>(); 9 | 10 | private Long getNextId(List list) { 11 | if(list.size() > 0) { 12 | T last = list.get(list.size() - 1); 13 | if (last instanceof Contact) 14 | return ((Contact) last).getId() + 1; 15 | else 16 | return ((Recording) last).getId() + 1; 17 | } 18 | else 19 | return 1L; 20 | } 21 | 22 | void addContact(String name, String number) { 23 | Contact contact = new Contact(getNextId(contacts), number, name, null, null); 24 | contacts.add(contact); 25 | } 26 | 27 | @Override 28 | public List getAllContacts() { 29 | return contacts; 30 | } 31 | 32 | @Override 33 | public void getAllContacts(LoadContactsCallback callback) { 34 | 35 | } 36 | 37 | @Override 38 | public Long getHiddenNumberContactId() { 39 | return null; 40 | } 41 | 42 | @Override 43 | public void insertContact(Contact contact) { 44 | 45 | } 46 | 47 | @Override 48 | public void updateContact(Contact contact) { 49 | 50 | } 51 | 52 | @Override 53 | public void deleteContact(Contact contact) { 54 | 55 | } 56 | 57 | @Override 58 | public void getRecordings(Contact contact, LoadRecordingsCallback callback) { 59 | 60 | } 61 | 62 | @Override 63 | public List getRecordings(Contact contact) { 64 | return null; 65 | } 66 | 67 | @Override 68 | public void insertRecording(Recording recording) { 69 | 70 | } 71 | 72 | @Override 73 | public void updateRecording(Recording recording) { 74 | 75 | } 76 | 77 | @Override 78 | public void deleteRecording(Recording recording) { 79 | 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /app/src/test/java/net/synapticweb/callrecorder/data/RecordingTest.java: -------------------------------------------------------------------------------- 1 | package net.synapticweb.callrecorder.data; 2 | 3 | import org.junit.Test; 4 | import static org.junit.Assert.*; 5 | 6 | public class RecordingTest { 7 | @Test 8 | public void hasIllegalCharacter_IllegalChar_returnsTrue() { 9 | String illegals = "~!@#$%^&*()+=?|\\':;\"><{}[]/"; 10 | for(int i = 0; i < illegals.length(); ++i) { 11 | char c = illegals.charAt(i); 12 | assertTrue(Recording.hasIllegalChar(Character.toString(c))); 13 | } 14 | } 15 | 16 | @Test 17 | public void hasIllegalCharacter_legalChars_returnsFalse() { 18 | String legalChars = "abcde10ABC.-"; 19 | assertFalse(Recording.hasIllegalChar(legalChars)); 20 | } 21 | 22 | } -------------------------------------------------------------------------------- /app/src/test/java/net/synapticweb/callrecorder/data/RecordingTestRobolectric.java: -------------------------------------------------------------------------------- 1 | package net.synapticweb.callrecorder.data; 2 | 3 | import android.content.Context; 4 | import android.os.Build; 5 | 6 | import androidx.test.core.app.ApplicationProvider; 7 | import androidx.test.ext.junit.runners.AndroidJUnit4; 8 | 9 | import org.junit.Before; 10 | import org.junit.Test; 11 | import org.junit.runner.RunWith; 12 | import org.robolectric.annotation.Config; 13 | 14 | import static org.hamcrest.CoreMatchers.is; 15 | import static org.junit.Assert.*; 16 | 17 | @RunWith(AndroidJUnit4.class) 18 | @Config(sdk = Build.VERSION_CODES.P) 19 | public class RecordingTestRobolectric { 20 | private Context context; 21 | 22 | @Before 23 | public void setup() { 24 | context = ApplicationProvider.getApplicationContext(); //nu merge în @BeforeClass 25 | } 26 | 27 | @Test 28 | public void isSavedInPrivateSpace_PrivatePath_ReturnsTrue() { 29 | String privatePath = context.getFilesDir() + "/testpath"; 30 | Recording recording = new Recording(); 31 | recording.setPath(privatePath); 32 | assertTrue(recording.isSavedInPrivateSpace(context)); 33 | } 34 | 35 | @Test 36 | public void isSavedInPrivateSpace_PublicPath_ReturnsFalse() { 37 | String publicPath = context.getCacheDir() + "/testpath"; 38 | Recording recording = new Recording(); 39 | recording.setPath(publicPath); 40 | assertFalse(recording.isSavedInPrivateSpace(context)); 41 | } 42 | 43 | @Test 44 | public void getName_NameSet_returnsFileName() { 45 | String path = context.getFilesDir() + "/testfile.aac"; 46 | Recording recording = new Recording(); 47 | recording.setIsNameSet(true); 48 | recording.setPath(path); 49 | assertThat(recording.getName(), is("testfile")); 50 | } 51 | 52 | } -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | 3 | buildscript { 4 | repositories { 5 | jcenter() 6 | google() 7 | maven { 8 | url 'https://maven.fabric.io/public' 9 | } 10 | 11 | } 12 | dependencies { 13 | classpath 'com.android.tools.build:gradle:4.0.0' 14 | classpath 'com.google.gms:google-services:4.3.3' 15 | classpath 'io.fabric.tools:gradle:1.31.2' 16 | // NOTE: Do not place your application dependencies here; they belong 17 | // in the individual module build.gradle files 18 | } 19 | } 20 | 21 | allprojects { 22 | repositories { 23 | jcenter() 24 | maven { url "https://jitpack.io" } 25 | maven { 26 | url "https://maven.google.com" 27 | } 28 | google() 29 | } 30 | } 31 | 32 | task clean(type: Delete) { 33 | delete rootProject.buildDir 34 | } 35 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | 3 | # IDE (e.g. Android Studio) users: 4 | # Gradle settings configured through the IDE *will override* 5 | # any settings specified in this file. 6 | 7 | # For more details on how to configure your build environment visit 8 | # http://www.gradle.org/docs/current/userguide/build_environment.html 9 | 10 | # Specifies the JVM arguments used for the daemon process. 11 | # The setting is particularly useful for tweaking memory settings. 12 | org.gradle.jvmargs=-Xmx1536m 13 | 14 | # When configured, Gradle will run in incubating parallel mode. 15 | # This option should only be used with decoupled projects. More details, visit 16 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 17 | # org.gradle.parallel=true 18 | android.useAndroidX=true 19 | android.enableJetifier=true 20 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/synapticweb/CallRecorder/b90f2a5c79bd338e25f34264e75e969604e3d10f/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Wed Jun 03 21:26:42 EEST 2020 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-6.1.1-all.zip 7 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 12 | set DEFAULT_JVM_OPTS= 13 | 14 | set DIRNAME=%~dp0 15 | if "%DIRNAME%" == "" set DIRNAME=. 16 | set APP_BASE_NAME=%~n0 17 | set APP_HOME=%DIRNAME% 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windowz variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | if "%@eval[2+2]" == "4" goto 4NT_args 53 | 54 | :win9xME_args 55 | @rem Slurp the command line arguments. 56 | set CMD_LINE_ARGS= 57 | set _SKIP=2 58 | 59 | :win9xME_args_slurp 60 | if "x%~1" == "x" goto execute 61 | 62 | set CMD_LINE_ARGS=%* 63 | goto execute 64 | 65 | :4NT_args 66 | @rem Get arguments from the 4NT Shell from JP Software 67 | set CMD_LINE_ARGS=%$ 68 | 69 | :execute 70 | @rem Setup the command line 71 | 72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if "%ERRORLEVEL%"=="0" goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 85 | exit /b 1 86 | 87 | :mainEnd 88 | if "%OS%"=="Windows_NT" endlocal 89 | 90 | :omega 91 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app', ':Croller' 2 | project(':Croller').projectDir = new File('app/libs/Croller') --------------------------------------------------------------------------------