Merge branch 'audiorecorder-3.5.0'

This commit is contained in:
Alexey Kuznetsov 2022-02-27 17:32:58 +03:00
commit 875cfb1339
32 changed files with 337 additions and 124 deletions

View file

@ -2,15 +2,15 @@ apply plugin: 'com.android.application'
apply plugin: 'com.github.axet.dxplugin'
android {
compileSdkVersion 29
buildToolsVersion '28.0.3'
compileSdkVersion 30
ndkVersion "16.1.4479499" // strip debug symbols
defaultConfig {
applicationId "com.github.axet.audiorecorder"
minSdkVersion 9
targetSdkVersion 29
versionCode 355
versionName "3.4.5"
targetSdkVersion 30
versionCode 356
versionName "3.5.0"
}
signingConfigs {
release {
@ -53,8 +53,7 @@ android {
dependencies {
testImplementation 'junit:junit:4.12'
implementation 'com.github.axet:android-library:1.33.6'
implementation ('com.github.axet:android-audio-library:1.0.180') { exclude module: 'android-library' } // implementation project(':android-audio-library')
implementation ('com.github.axet:android-audio-library:1.1.1') // implementation project(':android-audio-library')
implementation ('com.github.axet:wget:1.7.0') { exclude group: 'org.json', module: 'json' }
assets('com.google.android.exoplayer:exoplayer:2.7.3') { exclude group: 'com.android.support' }
}

View file

@ -23,7 +23,7 @@
android:roundIcon="@mipmap/ic_launcher"
android:supportsRtl="true"
android:theme="@style/Translucent">
<service android:name=".services.RecordingService" />
<service android:name=".services.RecordingService" android:foregroundServiceType="mediaProjection" />
<service android:name=".services.EncodingService" />
<service android:name=".services.ControlsService" />
<service

View file

@ -40,6 +40,7 @@ import com.github.axet.androidlibrary.widgets.ErrorDialog;
import com.github.axet.androidlibrary.widgets.OpenFileDialog;
import com.github.axet.androidlibrary.widgets.SearchView;
import com.github.axet.audiolibrary.app.RawSamples;
import com.github.axet.audiolibrary.app.Sound;
import com.github.axet.audiolibrary.encoders.FormatWAV;
import com.github.axet.audiorecorder.R;
import com.github.axet.audiorecorder.app.AudioApplication;
@ -663,7 +664,7 @@ public class MainActivity extends AppCompatThemeActivity {
void updateHeader() {
Uri uri = storage.getStoragePath();
long free = Storage.getFree(this, uri);
long sec = Storage.average(this, free);
long sec = Storage.average(this, Sound.getAudioFormat(this), free);
TextView text = (TextView) findViewById(R.id.space_left);
text.setText(AudioApplication.formatFree(this, free, sec));
}

View file

@ -9,6 +9,7 @@ import android.content.Intent;
import android.content.SharedPreferences;
import android.graphics.Rect;
import android.media.AudioFormat;
import android.media.MediaRecorder;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
@ -63,6 +64,7 @@ public class RecordingActivity extends AppCompatThemeActivity {
public static final String TAG = RecordingActivity.class.getSimpleName();
public static final int RESULT_START = 1;
public static final int RESULT_INTERNAL = 2;
public static final String[] PERMISSIONS_AUDIO = new String[]{
Manifest.permission.RECORD_AUDIO
@ -357,7 +359,7 @@ public class RecordingActivity extends AppCompatThemeActivity {
public void onClick(DialogInterface dialog, int which) {
File to = new File(d.getCurrentPath(), Storage.getName(RecordingActivity.this, recording.targetUri));
recording.targetUri = Uri.fromFile(to);
EncodingService.saveAsWAV(RecordingActivity.this, recording.storage.getTempRecording(), to, recording.getInfo());
EncodingService.saveAsWAV(RecordingActivity.this, recording.storage.getTempRecording(), to, recording.info);
}
});
d.show();
@ -517,7 +519,7 @@ public class RecordingActivity extends AppCompatThemeActivity {
editor.commit();
}
Log.d(TAG, "create recording at: " + targetUri);
app.recording = new RecordingStorage(this, pitch.getPitchTime(), targetUri);
app.recording = new RecordingStorage(this, Sound.getAudioFormat(this), pitch.getPitchTime(), targetUri);
}
recording = app.recording;
synchronized (recording.handlers) {
@ -556,21 +558,21 @@ public class RecordingActivity extends AppCompatThemeActivity {
return;
}
RawSamples rs = new RawSamples(f);
recording.samplesTime = rs.getSamples() / Sound.getChannels(this);
RawSamples rs = new RawSamples(f, recording.info);
recording.samplesTime = rs.getSamples() / rs.info.channels;
DisplayMetrics metrics = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(metrics);
int count = pitch.getMaxPitchCount(metrics.widthPixels);
short[] buf = new short[count * recording.samplesUpdateStereo];
long cut = recording.samplesTime * Sound.getChannels(this) - buf.length;
AudioTrack.SamplesBuffer buf = new AudioTrack.SamplesBuffer(rs.info.format, count * recording.samplesUpdateStereo);
long cut = recording.samplesTime * Sound.getChannels(this) - buf.count;
if (cut < 0)
cut = 0;
rs.open(cut, buf.length);
rs.open(cut, buf.count);
int len = rs.read(buf);
rs.close();
@ -584,7 +586,7 @@ public class RecordingActivity extends AppCompatThemeActivity {
int diff = len - lenUpdate;
if (diff > 0) {
recording.dbBuffer = ShortBuffer.allocate(recording.samplesUpdateStereo);
recording.dbBuffer = new AudioTrack.SamplesBuffer(rs.info.format, recording.samplesUpdateStereo);
recording.dbBuffer.put(buf, lenUpdate, diff);
}
}
@ -745,14 +747,14 @@ public class RecordingActivity extends AppCompatThemeActivity {
int rate = Integer.parseInt(shared.getString(AudioApplication.PREFERENCE_RATE, ""));
int m = Sound.getChannels(this);
int c = Sound.DEFAULT_AUDIOFORMAT == AudioFormat.ENCODING_PCM_16BIT ? 2 : 1;
int c = RawSamples.getBytes(recording.info.format);
long perSec;
String ext = shared.getString(AudioApplication.PREFERENCE_ENCODING, "");
if (shared.getBoolean(AudioApplication.PREFERENCE_FLY, false)) {
perSec = Factory.getEncoderRate(ext, recording.sampleRate);
perSec = Factory.getEncoderRate(Sound.getAudioFormat(this), ext, recording.sampleRate);
try {
free = Storage.getFree(this, recording.targetUri);
} catch (RuntimeException e) { // IllegalArgumentException
@ -780,8 +782,8 @@ public class RecordingActivity extends AppCompatThemeActivity {
int playUpdate = PitchView.UPDATE_SPEED * recording.sampleRate / 1000;
RawSamples rs = new RawSamples(recording.storage.getTempRecording());
int len = (int) (rs.getSamples() - editSample * Sound.getChannels(this)); // in samples
RawSamples rs = new RawSamples(recording.storage.getTempRecording(), recording.info);
int len = (int) (rs.getSamples() - editSample * rs.info.channels); // in samples
final AudioTrack.OnPlaybackPositionUpdateListener listener = new AudioTrack.OnPlaybackPositionUpdateListener() {
@Override
@ -799,8 +801,8 @@ public class RecordingActivity extends AppCompatThemeActivity {
}
};
AudioTrack.AudioBuffer buf = new AudioTrack.AudioBuffer(recording.sampleRate, Sound.getOutMode(this), Sound.DEFAULT_AUDIOFORMAT, len);
rs.open(editSample * Sound.getChannels(this), buf.len); // len in samples
AudioTrack.AudioBuffer buf = new AudioTrack.AudioBuffer(recording.sampleRate, Sound.getOutMode(this), rs.info.format, len);
rs.open(editSample * rs.info.channels, buf.len); // len in samples
int r = rs.read(buf.buffer); // r in samples
if (r != buf.len)
throw new RuntimeException("unable to read data");
@ -827,8 +829,8 @@ public class RecordingActivity extends AppCompatThemeActivity {
if (editSample == -1)
return;
RawSamples rs = new RawSamples(recording.storage.getTempRecording());
rs.trunk((editSample + recording.samplesUpdate) * Sound.getChannels(this));
RawSamples rs = new RawSamples(recording.storage.getTempRecording(), recording.info);
rs.trunk((editSample + recording.samplesUpdate) * rs.info.channels);
rs.close();
edit(false, true);
@ -924,8 +926,28 @@ public class RecordingActivity extends AppCompatThemeActivity {
editor.commit();
}
void startRecording() {
boolean startRecording() {
try {
final SharedPreferences shared = PreferenceManager.getDefaultSharedPreferences(this);
String source = shared.getString(AudioApplication.PREFERENCE_SOURCE, getString(R.string.source_mic));
int user;
if (source.equals(getString(R.string.source_raw))) {
if (Sound.isUnprocessedSupported(this))
user = MediaRecorder.AudioSource.UNPROCESSED;
else
user = MediaRecorder.AudioSource.VOICE_RECOGNITION;
} else if (source.equals(this.getString(R.string.source_internal))) {
user = Sound.SOURCE_INTERNAL_AUDIO;
} else {
user = MediaRecorder.AudioSource.MIC;
}
if (user == Sound.SOURCE_INTERNAL_AUDIO && !recording.sound.permitted()) {
Sound.showInternalAudio(this, RESULT_INTERNAL);
return false;
}
recording.startRecording(user);
edit(false, true);
pitch.setOnTouchListener(null);
@ -938,13 +960,13 @@ public class RecordingActivity extends AppCompatThemeActivity {
headset(true, true);
recording.startRecording();
RecordingService.startService(this, Storage.getName(this, recording.targetUri), true, duration);
ControlsService.hideIcon(this);
return true;
} catch (RuntimeException e) {
Toast.Error(RecordingActivity.this, e);
finish();
return false;
}
}
@ -970,6 +992,23 @@ public class RecordingActivity extends AppCompatThemeActivity {
Toast.makeText(this, R.string.not_permitted, Toast.LENGTH_SHORT).show();
finish();
}
break;
}
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
switch (requestCode) {
case RESULT_INTERNAL:
if (resultCode == RESULT_OK) {
recording.sound.onActivityResult(data);
startRecording();
} else {
Toast.makeText(this, R.string.not_permitted, Toast.LENGTH_SHORT).show();
finish();
}
break;
}
}
@ -1042,7 +1081,7 @@ public class RecordingActivity extends AppCompatThemeActivity {
} else {
done.run();
}
encoding = EncodingService.startEncoding(this, in, recording.targetUri, recording.getInfo());
encoding = EncodingService.startEncoding(this, in, recording.targetUri, recording.info);
}
@Override

View file

@ -99,7 +99,7 @@ public class SettingsActivity extends AppCompatSettingsThemeActivity implements
storage.migrateLocalStorageDialog(this);
if (key.equals(AudioApplication.PREFERENCE_RATE)) {
int sampleRate = Integer.parseInt(sharedPreferences.getString(AudioApplication.PREFERENCE_RATE, ""));
if (sampleRate != Sound.getValidRecordRate(Sound.getInMode(this), sampleRate))
if (sampleRate != Sound.getValidRecordRate(Sound.getAudioFormat(this), Sound.getInMode(this), sampleRate))
Toast.Text(this, "Not supported Hz");
}
}
@ -163,6 +163,7 @@ public class SettingsActivity extends AppCompatSettingsThemeActivity implements
bindPreferenceSummaryToValue(pm.findPreference(AudioApplication.PREFERENCE_CHANNELS));
bindPreferenceSummaryToValue(pm.findPreference(AudioApplication.PREFERENCE_FORMAT));
bindPreferenceSummaryToValue(pm.findPreference(AudioApplication.PREFERENCE_VOLUME));
bindPreferenceSummaryToValue(pm.findPreference(AudioApplication.PREFERENCE_AUDIOFORMAT));
StoragePathPreferenceCompat s = (StoragePathPreferenceCompat) pm.findPreference(AudioApplication.PREFERENCE_STORAGE);
s.setStorage(new Storage(getContext()));
@ -189,6 +190,10 @@ public class SettingsActivity extends AppCompatSettingsThemeActivity implements
return true;
}
});
Preference af = pm.findPreference(AudioApplication.PREFERENCE_AUDIOFORMAT);
if (Build.VERSION.SDK_INT < 23 && af != null)
af.setVisible(false);
}
@Override

View file

@ -140,7 +140,7 @@ public class EncodingStorage extends HashMap<File, EncodingStorage.Info> {
for (File in : keySet()) {
EncodingStorage.Info info = get(in);
final OnFlyEncoding fly = new OnFlyEncoding(this.storage, info.targetUri, info.info);
encoder = new FileEncoder(storage.getContext(), in, fly);
encoder = new FileEncoder(storage.getContext(), in, info.info, fly);
filters(encoder, info.info);
encoding(encoder, fly, info.info, new Runnable() {
@Override
@ -208,7 +208,7 @@ public class EncodingStorage extends HashMap<File, EncodingStorage.Info> {
public void encoding(File in, Uri targetUri, RawSamples.Info info) {
OnFlyEncoding fly = new OnFlyEncoding(storage, targetUri, info);
encoder = new FileEncoder(storage.getContext(), in, fly);
encoder = new FileEncoder(storage.getContext(), in, info, fly);
filters(encoder, info);
encoding(encoder, fly, info, new Runnable() {
@Override
@ -220,7 +220,7 @@ public class EncodingStorage extends HashMap<File, EncodingStorage.Info> {
public void saveAsWAV(File in, File out, RawSamples.Info info) {
OnFlyEncoding fly = new OnFlyEncoding(storage, out, info);
encoder = new FileEncoder(storage.getContext(), in, fly);
encoder = new FileEncoder(storage.getContext(), in, info, fly);
encoding(encoder, fly, info, new Runnable() {
@Override
public void run() {

View file

@ -1,14 +1,18 @@
package com.github.axet.audiorecorder.app;
import android.app.Activity;
import android.content.Context;
import android.content.SharedPreferences;
import android.media.AudioFormat;
import android.media.AudioRecord;
import android.media.MediaRecorder;
import android.net.Uri;
import android.os.Build;
import android.os.Handler;
import android.os.PowerManager;
import android.os.Process;
import com.github.axet.androidlibrary.sound.AudioTrack;
import com.github.axet.audiolibrary.app.RawSamples;
import com.github.axet.audiolibrary.app.Sound;
import com.github.axet.audiolibrary.encoders.Encoder;
@ -16,8 +20,18 @@ import com.github.axet.audiolibrary.encoders.OnFlyEncoding;
import com.github.axet.audiorecorder.BuildConfig;
import com.github.axet.audiorecorder.R;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.FilenameUtils;
import org.json.JSONException;
import java.io.File;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.ShortBuffer;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Locale;
import java.util.concurrent.atomic.AtomicBoolean;
public class RecordingStorage {
@ -35,7 +49,7 @@ public class RecordingStorage {
public Sound sound;
public Storage storage;
public Encoder e;
public Encoder e; // recording encoder (onfly or raw data)
public AtomicBoolean interrupt = new AtomicBoolean(); // nio throws ClosedByInterruptException if thread interrupted
public Thread thread;
@ -46,12 +60,13 @@ public class RecordingStorage {
public int samplesUpdateStereo; // samplesUpdate * number of channels
public Uri targetUri = null; // output target file 2016-01-01 01.01.01.wav
public long samplesTime; // how many samples passed for current recording, stereo = samplesTime * 2
public RawSamples.Info info;
public ShortBuffer dbBuffer = null; // PinchView samples buffer
public AudioTrack.SamplesBuffer dbBuffer = null; // PinchView samples buffer
public int pitchTime; // screen width
public RecordingStorage(Context context, int pitchTime, Uri targetUri) {
public RecordingStorage(Context context, int format, int pitchTime, Uri targetUri) {
this.context = context;
this.pitchTime = pitchTime;
this.targetUri = targetUri;
@ -60,36 +75,26 @@ public class RecordingStorage {
sampleRate = Sound.getSampleRate(context);
samplesUpdate = (int) (pitchTime * sampleRate / 1000f);
samplesUpdateStereo = samplesUpdate * Sound.getChannels(context);
info = new RawSamples.Info(format, sampleRate, Sound.getChannels(context));
}
public void startRecording() {
sound.silent();
public void startRecording(int source) {
final SharedPreferences shared = android.preference.PreferenceManager.getDefaultSharedPreferences(context);
int user;
if (shared.getString(AudioApplication.PREFERENCE_SOURCE, context.getString(R.string.source_mic)).equals(context.getString(R.string.source_raw))) {
if (Sound.isUnprocessedSupported(context))
user = MediaRecorder.AudioSource.UNPROCESSED;
else
user = MediaRecorder.AudioSource.VOICE_RECOGNITION;
} else {
user = MediaRecorder.AudioSource.MIC;
}
sound.silent();
int[] ss = new int[]{
user,
source,
MediaRecorder.AudioSource.MIC,
MediaRecorder.AudioSource.DEFAULT
};
if (shared.getBoolean(AudioApplication.PREFERENCE_FLY, false)) {
final OnFlyEncoding fly = new OnFlyEncoding(storage, targetUri, getInfo());
if (e == null) { // do not recreate encoder if on-fly mode enabled
final OnFlyEncoding fly = new OnFlyEncoding(storage, targetUri, info);
e = new Encoder() {
@Override
public void encode(short[] buf, int pos, int len) {
public void encode(AudioTrack.SamplesBuffer buf, int pos, int len) {
fly.encode(buf, pos, len);
}
@ -100,11 +105,11 @@ public class RecordingStorage {
};
}
} else {
final RawSamples rs = new RawSamples(storage.getTempRecording());
rs.open(samplesTime * Sound.getChannels(context));
final RawSamples rs = new RawSamples(storage.getTempRecording(), info);
rs.open(samplesTime * rs.info.channels);
e = new Encoder() {
@Override
public void encode(short[] buf, int pos, int len) {
public void encode(AudioTrack.SamplesBuffer buf, int pos, int len) {
rs.write(buf, pos, len);
}
@ -115,7 +120,7 @@ public class RecordingStorage {
};
}
final AudioRecord recorder = Sound.createAudioRecorder(context, sampleRate, ss, 0);
final AudioRecord recorder = sound.createAudioRecorder(info.format, sampleRate, ss, 0);
final Thread old = thread;
final AtomicBoolean oldb = interrupt;
@ -154,17 +159,34 @@ public class RecordingStorage {
int samplesTimeCount = 0;
final int samplesTimeUpdate = 1000 * sampleRate / 1000; // how many samples we need to update 'samples'. time clock. every 1000ms.
short[] buffer = null;
AudioTrack.SamplesBuffer buffer = null;
boolean stableRefresh = false;
while (!interrupt.get()) {
synchronized (bufferSizeLock) {
if (buffer == null || buffer.length != bufferSize)
buffer = new short[bufferSize];
if (buffer == null || buffer.size() != bufferSize)
buffer = new AudioTrack.SamplesBuffer(info.format, bufferSize);
}
int readSize = -1;
switch (buffer.format) {
case AudioFormat.ENCODING_PCM_8BIT:
break;
case AudioFormat.ENCODING_PCM_16BIT:
readSize = recorder.read(buffer.shorts, 0, buffer.shorts.length);
case Sound.ENCODING_PCM_24BIT_PACKED:
break;
case Sound.ENCODING_PCM_32BIT:
break;
case AudioFormat.ENCODING_PCM_FLOAT:
if (Build.VERSION.SDK_INT >= 23)
readSize = recorder.read(buffer.floats, 0, buffer.floats.length, AudioRecord.READ_BLOCKING);
break;
default:
throw new RuntimeException("Unknown format");
}
int readSize = recorder.read(buffer, 0, buffer.length);
if (readSize < 0)
return;
long now = System.currentTimeMillis();
@ -178,18 +200,18 @@ public class RecordingStorage {
e.encode(buffer, 0, readSize);
short[] dbBuf;
AudioTrack.SamplesBuffer dbBuf;
int dbSize;
int readSizeUpdate;
if (dbBuffer != null) {
ShortBuffer bb = ShortBuffer.allocate(dbBuffer.position() + readSize);
AudioTrack.SamplesBuffer bb = new AudioTrack.SamplesBuffer(info.format, dbBuffer.position + readSize);
dbBuffer.flip();
bb.put(dbBuffer);
bb.put(buffer, 0, readSize);
dbBuf = new short[bb.position()];
dbSize = dbBuf.length;
dbBuf = new AudioTrack.SamplesBuffer(info.format, bb.position);
dbSize = dbBuf.count;
bb.flip();
bb.get(dbBuf, 0, dbBuf.length);
bb.get(dbBuf, 0, dbBuf.count);
} else {
dbBuf = buffer;
dbSize = readSize;
@ -204,7 +226,7 @@ public class RecordingStorage {
}
int readSizeLen = dbSize - readSizeUpdate;
if (readSizeLen > 0) {
dbBuffer = ShortBuffer.allocate(readSizeLen);
dbBuffer = new AudioTrack.SamplesBuffer(info.format, readSizeLen);
dbBuffer.put(dbBuf, readSizeUpdate, readSizeLen);
} else {
dbBuffer = null;
@ -277,10 +299,6 @@ public class RecordingStorage {
sound.unsilent();
}
public RawSamples.Info getInfo() {
return new RawSamples.Info(sampleRate, Sound.getChannels(context));
}
// calcuale buffer length dynamically, this way we can reduce thread cycles when activity in background
// or phone screen is off.
public void updateBufferSize(boolean pause) {

View file

@ -86,9 +86,8 @@ public class BluetoothReceiver extends BroadcastReceiver {
String a = intent.getAction();
if (a == null)
return;
if (bluetoothSource && a.equals(BluetoothDevice.ACTION_ACL_CONNECTED)) {
if (bluetoothSource && a.equals(BluetoothDevice.ACTION_ACL_CONNECTED))
handler.postDelayed(connected, CONNECT_DELAY);
}
if (bluetoothSource && a.equals(AudioManager.ACTION_SCO_AUDIO_STATE_UPDATED)) {
int state = intent.getIntExtra(AudioManager.EXTRA_SCO_AUDIO_STATE, -1);
switch (state) {

View file

@ -27,6 +27,7 @@ import com.github.axet.androidlibrary.services.PersistentService;
import com.github.axet.androidlibrary.widgets.NotificationChannelCompat;
import com.github.axet.androidlibrary.widgets.RemoteNotificationCompat;
import com.github.axet.androidlibrary.widgets.RemoteViewsCompat;
import com.github.axet.audiolibrary.app.Sound;
import com.github.axet.audiolibrary.app.Storage;
import com.github.axet.audiorecorder.R;
import com.github.axet.audiorecorder.activities.MainActivity;
@ -128,7 +129,7 @@ public class ControlsService extends PersistentService {
title = getString(R.string.app_name);
Uri f = storage.getStoragePath();
long free = Storage.getFree(context, f);
long sec = Storage.average(context, free);
long sec = Storage.average(context, Sound.getAudioFormat(context), free);
text = AudioApplication.formatFree(context, free, sec);
builder = new RemoteNotificationCompat.Low(context, R.layout.notifictaion);
builder.setViewVisibility(R.id.notification_record, View.VISIBLE);

View file

@ -0,0 +1,79 @@
package com.github.axet.audiorecorder.widgets;
import android.content.Context;
import android.content.SharedPreferences;
import android.content.res.TypedArray;
import android.os.Build;
import android.preference.PreferenceManager;
import android.support.v7.preference.ListPreference;
import android.support.v7.preference.ListPreferenceDialogFragmentCompat;
import android.support.v7.preference.PreferenceViewHolder;
import android.util.AttributeSet;
import com.github.axet.audiolibrary.app.Sound;
import com.github.axet.audiorecorder.R;
import com.github.axet.audiorecorder.app.AudioApplication;
import com.github.axet.audiorecorder.app.Storage;
import java.util.ArrayList;
import java.util.Date;
public class RecordingSourcePreferenceCompat extends ListPreference {
public RecordingSourcePreferenceCompat(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}
public RecordingSourcePreferenceCompat(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
public RecordingSourcePreferenceCompat(Context context, AttributeSet attrs) {
super(context, attrs);
}
public RecordingSourcePreferenceCompat(Context context) {
super(context);
}
@Override
public boolean callChangeListener(Object newValue) {
update(newValue);
return super.callChangeListener(newValue);
}
@Override
protected Object onGetDefaultValue(TypedArray a, int index) {
Object def = super.onGetDefaultValue(a, index);
update(def);
return def;
}
@Override
protected void onSetInitialValue(boolean restoreValue, Object defaultValue) {
super.onSetInitialValue(restoreValue, defaultValue);
CharSequence[] text = getEntries();
CharSequence[] values = getEntryValues();
ArrayList<CharSequence> tt = new ArrayList<>();
ArrayList<CharSequence> vv = new ArrayList<>();
String raw = getContext().getString(R.string.source_raw);
String internal = getContext().getString(R.string.source_internal);
for (int i = 0; i < values.length; i++) {
String v = values[i].toString();
String t = text[i].toString();
if (v.equals(raw) && !Sound.isUnprocessedSupported(getContext()))
continue;
if (v.equals(internal) && Build.VERSION.SDK_INT < 29)
continue;
vv.add(v);
tt.add(t);
}
setEntryValues(vv.toArray(new CharSequence[0]));
setEntries(tt.toArray(new CharSequence[0]));
update(getValue()); // defaultValue null after defaults set
}
public void update(Object value) {
String v = (String) value;
setSummary(v);
}
}

View file

@ -12,7 +12,7 @@
Przyjazny dla systemu Android!
</p>
<p>Audio Recorder with custom recording folder, nice recording volume indicator, recording notification, recording lock screen activity.</p>
<p>Rejestrator dźwięku z własnym folderem nagrywania, ładnym wskaźnikiem głośności nagrywania, powiadomieniem o nagrywaniu oraz sterowaniem nagrywania z poziomu blokady ekranu.</p>
<dl>
<dt><b>Licencja:</b></dt>

View file

@ -0,0 +1,23 @@
<html>
<head>
<meta name="viewport" content="width=device-width,initial-scale=1">
<style>
a { white-space: pre-wrap; word-wrap:break-word; }
</style>
</head>
<body>
<h3>Sobre</h3>
<p>
Compatível com Android!
</p>
<p>Gravador de áudio com pasta de gravação personalizada, controle na tela de bloqueio, indicador de volume e notificação de gravação.</p>
<dl>
<dt><b>Licença:</b></dt>
<dd>GPLv3</dd>
<dt><b>Código fonte:</b></dt>
<dd><a href="https://gitlab.com/axet/android-audio-recorder">https://gitlab.com/axet/android-audio-recorder</a></dd>
</dl>
</body>
</html>

View file

@ -1,5 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string-array name="source_text">
<item>Mikrofon</item>
<item>Unprocessed</item>
<item>Bluetooth</item>
<item>Internal Audio</item>
</string-array>
<string name="app_name">Audio Optager</string>
<string name="auto_close">Auto luk om (%1$d)</string>
<string name="recording_list_is_empty">Optagelseslisten er tom\n\nKlik Optag for at starte optagelse</string>

View file

@ -15,6 +15,7 @@
<item>Mikrofon</item>
<item>Unprocessed</item>
<item>Bluetooth</item>
<item>Internal Audio</item>
</string-array>
<string-array name="themes_text">

View file

@ -13,6 +13,7 @@
<item>Μικρόφωνο</item>
<item>Unprocessed</item>
<item>Bluetooth</item>
<item>Internal Audio</item>
</string-array>
<string-array name="themes_text">
<item>Φωτεινό</item>

View file

@ -15,6 +15,7 @@
<item>Mic</item>
<item>Unprocessed</item>
<item>Bluetooth</item>
<item>Internal Audio</item>
</string-array>
<string-array name="themes_text">

View file

@ -15,6 +15,7 @@
<item>Mikrofonoa</item>
<item>Prozesatu gabea</item>
<item>Bluetooth</item>
<item>Internal Audio</item>
</string-array>
<string-array name="themes_text">

View file

@ -15,6 +15,7 @@
<item>Mic</item>
<item>Unprocessed</item>
<item>Bluetooth</item>
<item>Internal Audio</item>
</string-array>
<string-array name="themes_text">

View file

@ -15,6 +15,7 @@
<item>Mik</item>
<item>Tidak diproses</item>
<item>Bluetooth</item>
<item>Internal Audio</item>
</string-array>
<string-array name="themes_text">

View file

@ -15,6 +15,7 @@
<item>Mic</item>
<item>Unprocessed</item>
<item>Bluetooth</item>
<item>Internal Audio</item>
</string-array>
<string-array name="themes_text">

View file

@ -15,6 +15,7 @@
<item>Mic</item>
<item>Unprocessed</item>
<item>Bluetooth</item>
<item>Internal Audio</item>
</string-array>
<string-array name="themes_text">

View file

@ -13,8 +13,9 @@
<string-array name="source_text">
<item>Mikrofon</item>
<item>Unprocessed</item>
<item>Nieprzetworzone</item>
<item>Bluetooth</item>
<item>Internal Audio</item>
</string-array>
<string-array name="themes_text">
@ -27,52 +28,52 @@
<item>Stereo</item>
</string-array>
<string name="no_folder_app">No folder view application installed</string>
<string name="no_folder_app">Brak zainstalowanej aplikacji do przeglądania folderów</string>
<string name="hold_by_call">pauza (wstrzymane przez połączenie)</string>
<string name="recording_status_recording">nagrywanie</string>
<string name="recording_status_encoding">enkodowanie</string>
<string name="recording_status_encoding">kodowanie</string>
<string name="recording_status_pause">pauza</string>
<string name="recording_status_edit">edytuj</string>
<string name="confirm_cancel">Potwierdź anulowanie</string>
<string name="encoding_title">Enkodowanie...</string>
<string name="encoding_title">Kodowanie...</string>
<string name="pause_title">Pauza...</string>
<string name="recording_title">Nagrywanie</string>
<string name="open_recording_folder">Otwórz Folder Nagrywania</string>
<string name="recording_list_is_empty">Lista nagrań jest pusta\n\nKliknij Nagraj, aby rozpocząć nagrywanie</string>
<string name="open_recording_folder">Otwórz folder z nagraniami</string>
<string name="recording_list_is_empty">Lista nagrań jest pusta\n\nDotknij ikonki "mikrofonu", aby rozpocząć nagrywanie</string>
<string name="record_button">Nagrywaj</string>
<string name="cut_button">Wytnij</string>
<string name="stop_button">Stop</string>
<string name="stop_button">Zatrzymaj</string>
<string name="cancel_button">Anuluj</string>
<string name="pause_button">Pauza</string>
<string name="pref_storage_title">Ścieżka Przechowywania</string>
<string name="pref_storage_title">Folder nagrań</string>
<string name="pref_rate_title">Tempo Próbkowania</string>
<string name="pref_encoding_title">Enkodowanie</string>
<string name="pref_encoding_title">Kodowanie</string>
<string name="pref_encoding_summary">Wyjściowy format pliku (.wav, .m4a, ...)</string>
<string name="pref_mode_title">Tryb</string>
<string name="pref_mode_summary">Recording channels</string>
<string name="pref_nameformat_title">Format Nazwy</string>
<string name="pref_mode_title">Kanały audio</string>
<string name="pref_mode_summary">Kanały nagrywania</string>
<string name="pref_nameformat_title">Format pliku</string>
<string name="pref_pausecalls_title">Wstrzymaj podczas połączeń</string>
<string name="pref_pausecalls_summary">Zatrzymaj nagrywanie po odebraniu i kontynuuj podcas rozłączania</string>
<string name="pref_silence_title">Tryb Wyciszenia</string>
<string name="pref_silence_summary">Przełącz telefon w \'tryb ciszy\' podczas nagrywania</string>
<string name="pref_pausecalls_summary">Zatrzymaj nagrywanie po odebraniu i kontynuuj po rozłączeniu</string>
<string name="pref_silence_title">Tryb cichy</string>
<string name="pref_silence_summary">Załącz \'tryb cichy\' podczas nagrywania</string>
<string name="pref_lockscreen_title">Sterowanie z ekranu blokady</string>
<string name="pref_lockscreen_summary">Pokaż elementy sterujące, gdy telefon jest zablokowany</string>
<string name="pref_theme_title">Motyw Aplikacji</string>
<string name="pref_theme_summary">Ustaw motyw aplikacji (ciemny / jasny)</string>
<string name="pref_theme_summary">Ustaw motyw aplikacji (ciemny/jasny)</string>
<string name="pref_application">Aplikacja</string>
<string name="pref_recordings">Nagrania</string>
<string name="pref_fly_title">Enkodowanie w Locie</string>
<string name="pref_fly_summary">Włączając enkodowanie w locie, wyłącza edytowanie i odzyskiwanie po awarii</string>
<string name="pref_fly_title">Kodowanie "w locie"</string>
<string name="pref_fly_summary">Włączając kodowanie "w locie", wyłącza edytowanie i odzyskiwanie po awarii</string>
<string name="hold_by_bluetooth">pauza (bluetooth rozłaczony)</string>
<string name="menu_search">Szukaj</string>
<string name="save_as_wav">Zapisz jako WAV</string>
<string name="auto_close">Auto close in (%1$d)</string>
<string name="mic_muted_error">Mic muted</string>
<string name="mic_muted_pie">Android Pie and above prevent idle background apps from using microphone. Please disable selinux or install previous android version!</string>
<string name="mic_paused">Mic paused by OS, recording time is less then data recorded, check if you device supports background recording or it is fast enougth for selected settings</string>
<string name="tile_start_recording">Start Recording</string>
<string name="tile_stop_recording">Stop Recording</string>
<string name="encoding_optimization">Background encoding paused due to android Battery Optimization settings, please allow this application work in background</string>
<string name="auto_close">Zamknij automatycznie za (%1$d)</string>
<string name="mic_muted_error">Mikrofon wyciszony</string>
<string name="mic_muted_pie">Android 9 (Pie) i nowsze wersje uniemożliwiają bezczynnym aplikacjom działającym w tle korzystanie z mikrofonu. Prosimy o wyłączenie SeLinux lub aktualizację do wcześniejszej wersji Androida!</string>
<string name="mic_paused">Mikrofon został wstrzymany przez system Android, czas nagrywania jest krótszy niż zapisane dane, sprawdź czy urządzenie obsługuje nagrywanie w tle lub czy jest wystarczająco szybkie dla wybranych ustawień</string>
<string name="tile_start_recording">Rozpocznij nagrywanie</string>
<string name="tile_stop_recording">Zakończ nagrywanie</string>
<string name="encoding_optimization">Kodowanie w tle zostało wstrzymane przez system Android w celu oszczędzania energii, proszę pozwolić tej aplikacji pracować w tle (ustawienia systemu: optymalizacja baterii)</string>
<string name="per_second">/s</string>
</resources>

View file

@ -1,5 +1,5 @@
<resources>
<string name="app_name">Audio Recorder</string>
<string name="app_name">Gravador de áudio</string>
<string-array name="sample_rate_text">
<item>48 kHz</item>
@ -8,18 +8,19 @@
<item>22 kHz</item>
<item>16 kHz (padrão)</item>
<item>11 kHz</item>
<item>8 kHz (ligação de tel.)</item>
<item>8 kHz (tipo ligação)</item>
</string-array>
<string-array name="source_text">
<item>Mic</item>
<item>Não processado</item>
<item>Bluetooth</item>
<item>Internal Audio</item>
</string-array>
<string-array name="themes_text">
<item>Tema Claro</item>
<item>Tema Escuro</item>
<item>Tema claro</item>
<item>Tema escuro</item>
</string-array>
<string-array name="channels_text">
@ -27,52 +28,52 @@
<item>Estéreo</item>
</string-array>
<string name="no_folder_app">Não foi encontrado nenhum aplicativo para explorar os arquivos</string>
<string name="hold_by_call">pausado (chamada atendida)</string>
<string name="no_folder_app">Não foi encontrado nenhum app para abrir os arquivos</string>
<string name="hold_by_call">pausado (ligação atendida)</string>
<string name="recording_status_recording">gravando</string>
<string name="recording_status_encoding">codificando</string>
<string name="recording_status_pause">pausado</string>
<string name="recording_status_edit">editando</string>
<string name="confirm_cancel">Deseja cancelar</string>
<string name="confirm_cancel">Quer cancelar</string>
<string name="encoding_title">Codificando...</string>
<string name="pause_title">Pausado...</string>
<string name="recording_title">Gravando</string>
<string name="open_recording_folder">Abrir pasta das gravações</string>
<string name="recording_list_is_empty">A lista das gravações está vazia\n\nToque no ícone do microfone para começar a gravar</string>
<string name="open_recording_folder">Abrir pasta de gravações</string>
<string name="recording_list_is_empty">A lista de gravações está vazia\n\nToque no ícone do microfone para começar a gravar</string>
<string name="record_button">Gravar</string>
<string name="cut_button">Cortar</string>
<string name="stop_button">Parar</string>
<string name="cancel_button">Cancelar</string>
<string name="pause_button">Pausar</string>
<string name="pref_storage_title">Pasta das gravações</string>
<string name="pref_storage_title">Pasta de gravações</string>
<string name="pref_rate_title">Taxa de amostragem</string>
<string name="pref_encoding_title">Codificação</string>
<string name="pref_encoding_summary">Formato de saída do arquivo (.wav, .mp3, etc.)</string>
<string name="pref_encoding_summary">Tipo de arquivo de áudio (.wav, .mp3, etc.)</string>
<string name="pref_mode_title">Canais de áudio</string>
<string name="pref_mode_summary">Canais de gravação</string>
<string name="pref_nameformat_title">Formato do nome de arquivo</string>
<string name="pref_pausecalls_title">Pausar durante a chamada</string>
<string name="pref_pausecalls_summary">e voltar a gravar quando a chamada for desligada</string>
<string name="pref_nameformat_title">Formato de nome de arquivo</string>
<string name="pref_pausecalls_title">Pausar durante a ligação</string>
<string name="pref_pausecalls_summary">e voltar a gravar quando a ligação for concluída</string>
<string name="pref_silence_title">Modo silencioso</string>
<string name="pref_silence_summary">Ativar o \'modo silencioso\' durante a gravação</string>
<string name="pref_lockscreen_title">Controle da tela bloqueada</string>
<string name="pref_lockscreen_summary">Mostrar os controles quando a tela estiver bloqueada</string>
<string name="pref_lockscreen_title">Controle na tela de bloqueio</string>
<string name="pref_lockscreen_summary">Mostrar menu de controle quando a tela for bloqueada</string>
<string name="pref_theme_title">Tema do App</string>
<string name="pref_theme_summary">Definir o tema (claro ou escuro)</string>
<string name="pref_application">Aplicativo</string>
<string name="pref_recordings">Gravações</string>
<string name="pref_fly_title">Codificação em tempo real</string>
<string name="pref_fly_summary">Ativando codificação em tempo real desabilitará a edição e recuperação após a falha</string>
<string name="pref_fly_summary">Ao permitir codificação em tempo real você desabilitará a edição e recuperação após uma falha</string>
<string name="hold_by_bluetooth">pausado (bluetooth desconectado)</string>
<string name="menu_search">Pesquisar</string>
<string name="save_as_wav">Salvar como WAV</string>
<string name="auto_close">Auto-fechar em (%1$d)</string>
<string name="save_as_wav">Salvar como .wav</string>
<string name="auto_close">Fechar autom. em (%1$d)</string>
<string name="mic_muted_error">Mic silenciado</string>
<string name="mic_muted_pie">Android 9 (Pie) e mais recente impedem que os Apps em segundo plano utilizem o microfone. Solução: ou desabilitar o SeLinux ou usar a versão anterior do Android!</string>
<string name="mic_paused">Microfone foi pausado pelo Android, o tempo de gravação é menor do que o conteúdo gravado. Verifique se seu dispositivo suporta gravação em segundo plano ou se é o suficiente rápido para utilizar as configurações selecionadas</string>
<string name="tile_start_recording">Iniciar Gravação</string>
<string name="tile_stop_recording">Parar a Gravação</string>
<string name="encoding_optimization">Codificação em segundo plano foi pausada devido a configuração do Android de otimização da bateria, por favor permita que o App funcione em segundo plano</string>
<string name="mic_muted_pie">Android 9 (Pie) e mais recentes impedem que os apps usem o microfone em segundo plano. Solução: desabilite o SeLinux ou use uma versão mais antiga do Android</string>
<string name="mic_paused">O microfone foi pausado pelo Android, o tempo de gravação é menor do que o conteúdo gravado. Verifique se seu dispositivo suporta gravação em segundo plano ou se é o suficientemente rápido para usar as configurações selecionadas</string>
<string name="tile_start_recording">Iniciar uma gravação</string>
<string name="tile_stop_recording">Parar a gravação</string>
<string name="encoding_optimization">A codificação em segundo plano foi pausada pela configuração de otimização da bateria do Android, por favor permita que o App funcione em segundo plano</string>
<string name="per_second">/s</string>
</resources>

View file

@ -20,6 +20,7 @@
<item>Микрофон</item>
<item>Необработанный</item>
<item>Bluetooth</item>
<item>Internal Audio</item>
</string-array>
<string-array name="channels_text">

View file

@ -15,6 +15,7 @@
<item>Mikrofón</item>
<item>Bez spracovania (surové)</item>
<item>Bluetooth</item>
<item>Internal Audio</item>
</string-array>
<string-array name="themes_text">

View file

@ -15,6 +15,7 @@
<item>Mikrofon</item>
<item>İşlenmemiş</item>
<item>Bluetooth</item>
<item>Internal Audio</item>
</string-array>
<string-array name="themes_text">

View file

@ -15,6 +15,7 @@
<item>话筒</item>
<item>未处理</item>
<item>蓝牙</item>
<item>Internal Audio</item>
</string-array>
<string-array name="themes_text">

View file

@ -15,6 +15,7 @@
<item>麥克風</item>
<item>未處理</item>
<item>藍牙</item>
<item>Internal Audio</item>
</string-array>
<string-array name="themes_text">

View file

@ -7,6 +7,7 @@
<string name="source_default" translatable="false">default</string>
<string name="source_raw" translatable="false">raw</string>
<string name="source_bluetooth" translatable="false">bluetooth</string>
<string name="source_internal" translatable="false">internal</string>
<string-array name="sample_rate_values" translatable="false">
<item>48000</item>
@ -22,6 +23,17 @@
<item>@string/source_mic</item>
<item>@string/source_raw</item>
<item>@string/source_bluetooth</item>
<item>@string/source_internal</item>
</string-array>
<string-array name="audioformat_text">
<item>16-bit PCM</item>
<item>24-bit PCM (float)</item>
</string-array>
<string-array name="audioformat_values" translatable="false">
<item>16</item>
<item>float</item>
</string-array>
<string-array name="themes_values" translatable="false">

View file

@ -15,6 +15,7 @@
<item>Mic</item>
<item>Unprocessed</item>
<item>Bluetooth</item>
<item>Internal Audio</item>
</string-array>
<string-array name="themes_text">

View file

@ -8,6 +8,16 @@
android:title="@string/pref_storage_title" />
<ListPreference
android:defaultValue="16"
android:entries="@array/audioformat_text"
android:entryValues="@array/audioformat_values"
android:key="audioformat"
android:negativeButtonText="@null"
android:positiveButtonText="@null"
android:summary=""
android:title="Audio Format" />
<com.github.axet.audiorecorder.widgets.RecordingSourcePreferenceCompat
android:defaultValue="mic"
android:entries="@array/source_text"
android:entryValues="@array/source_values"
@ -17,7 +27,7 @@
android:summary=""
android:title="@string/pref_source_title" />
<ListPreference
<com.github.axet.audiolibrary.widgets.SampleRatePreferenceCompat
android:defaultValue="16000"
android:entries="@array/sample_rate_text"
android:entryValues="@array/sample_rate_values"

View file

@ -0,0 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-6.5-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists