Merge branch 'audiorecorder-3.0.0'

This commit is contained in:
Alexey Kuznetsov 2017-06-27 18:19:27 +03:00
commit ca2ee7e19e
7 changed files with 148 additions and 48 deletions

View file

@ -8,8 +8,8 @@ android {
applicationId "com.github.axet.audiorecorder"
minSdkVersion 9
targetSdkVersion 23
versionCode 164
versionName "2.1.0"
versionCode 165
versionName "3.0.0"
}
signingConfigs {
release {
@ -43,5 +43,5 @@ android {
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
testCompile 'junit:junit:4.12'
compile 'com.github.axet:android-audio-library:0.1.0' // compile project(':android-audio-library')
compile 'com.github.axet:android-audio-library:1.0.0' // compile project(':android-audio-library')
}

View file

@ -3,6 +3,7 @@ package com.github.axet.audiorecorder.activities;
import android.Manifest;
import android.app.AlertDialog;
import android.content.BroadcastReceiver;
import android.content.ContentResolver;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
@ -10,12 +11,18 @@ import android.content.IntentFilter;
import android.content.SharedPreferences;
import android.content.res.Configuration;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.ParcelFileDescriptor;
import android.preference.PreferenceManager;
import android.provider.DocumentsContract;
import android.support.design.widget.FloatingActionButton;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.system.ErrnoException;
import android.system.Os;
import android.system.StructStatVfs;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
@ -33,6 +40,7 @@ import com.github.axet.audiorecorder.app.MainApplication;
import com.github.axet.audiorecorder.services.RecordingService;
import java.io.File;
import java.io.FileNotFoundException;
import java.util.Collections;
public class MainActivity extends AppCompatActivity {
@ -130,7 +138,7 @@ public class MainActivity extends AppCompatActivity {
}
Intent showIntent() {
Uri selectedUri = Uri.fromFile(storage.getStoragePath());
Uri selectedUri = storage.getStoragePath();
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setDataAndType(selectedUri, "resource/folder");
return intent;
@ -187,12 +195,10 @@ public class MainActivity extends AppCompatActivity {
return;
}
if (Storage.permitted(MainActivity.this, PERMISSIONS)) {
try {
storage.migrateLocalStorage();
} catch (RuntimeException e) {
Error(e);
}
try {
storage.migrateLocalStorage();
} catch (RuntimeException e) {
Error(e);
}
final int selected = getLastRecording();
@ -225,11 +231,9 @@ public class MainActivity extends AppCompatActivity {
int getLastRecording() {
final SharedPreferences shared = PreferenceManager.getDefaultSharedPreferences(this);
String last = shared.getString(MainApplication.PREFERENCE_LAST, "");
last = last.toLowerCase();
for (int i = 0; i < recordings.getCount(); i++) {
File f = recordings.getItem(i);
String n = f.getName().toLowerCase();
if (n.equals(last)) {
Uri f = recordings.getItem(i);
if (storage.getDocumentName(f).equals(last)) {
SharedPreferences.Editor edit = shared.edit();
edit.putString(MainApplication.PREFERENCE_LAST, "");
edit.commit();
@ -277,8 +281,8 @@ public class MainActivity extends AppCompatActivity {
}
void updateHeader() {
File f = storage.getStoragePath();
long free = Storage.getFree(f);
Uri uri = storage.getStoragePath();
long free = storage.getFree(uri);
long sec = Storage.average(this, free);
TextView text = (TextView) findViewById(R.id.space_left);
text.setText(MainApplication.formatFree(this, free, sec));

View file

@ -4,6 +4,7 @@ import android.Manifest;
import android.app.AlertDialog;
import android.app.ProgressDialog;
import android.content.BroadcastReceiver;
import android.content.ContentResolver;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
@ -13,10 +14,12 @@ import android.content.pm.PackageManager;
import android.media.AudioFormat;
import android.media.AudioRecord;
import android.media.MediaRecorder;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.preference.PreferenceManager;
import android.provider.DocumentsContract;
import android.support.v4.app.ActivityCompat;
import android.support.v4.content.ContextCompat;
import android.support.v7.app.ActionBar;
@ -36,6 +39,7 @@ import android.widget.Toast;
import com.github.axet.androidlibrary.animations.MarginBottomAnimation;
import com.github.axet.androidlibrary.sound.AudioTrack;
import com.github.axet.audiolibrary.app.RawSamples;
import com.github.axet.audiolibrary.app.Recordings;
import com.github.axet.audiolibrary.app.Sound;
import com.github.axet.audiolibrary.encoders.Encoder;
import com.github.axet.audiolibrary.encoders.EncoderInfo;
@ -47,7 +51,13 @@ import com.github.axet.audiorecorder.app.MainApplication;
import com.github.axet.audiorecorder.app.Storage;
import com.github.axet.audiorecorder.services.RecordingService;
import org.apache.commons.io.IOUtils;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.ShortBuffer;
@ -75,7 +85,7 @@ public class RecordingActivity extends AppCompatActivity {
// pitch size in samples. how many samples count need to update view. 4410 for 100ms update.
int samplesUpdate;
// output target file 2016-01-01 01.01.01.wav
File targetFile = null;
Uri targetUri = null;
// how many samples passed for current recording
long samplesTime;
// current cut position in samples from begining of file
@ -172,22 +182,26 @@ public class RecordingActivity extends AppCompatActivity {
try {
if (storage.recordingPending()) {
String file = shared.getString(MainApplication.PREFERENCE_TARGET, null);
if (file != null)
targetFile = new File(file);
if (file != null) {
if (file.startsWith(ContentResolver.SCHEME_CONTENT))
targetUri = Uri.parse(file);
else
targetUri = Uri.fromFile(new File(file));
}
}
if (targetFile == null)
targetFile = storage.getNewFile();
if (targetUri == null)
targetUri = storage.getNewFile();
SharedPreferences.Editor editor = shared.edit();
editor.putString(MainApplication.PREFERENCE_TARGET, targetFile.toString());
editor.putString(MainApplication.PREFERENCE_TARGET, targetUri.toString());
editor.commit();
} catch (RuntimeException e) {
Log.d(TAG, "onCreate", e);
Toast.makeText(this, e.getMessage(), Toast.LENGTH_SHORT).show();
finish();
return;
}
title.setText(targetFile.getName());
title.setText(storage.getDocumentName(targetUri));
if (shared.getBoolean(MainApplication.PREFERENCE_CALL, false)) {
TelephonyManager tm = (TelephonyManager) this.getSystemService(Context.TELEPHONY_SERVICE);
@ -343,7 +357,7 @@ public class RecordingActivity extends AppCompatActivity {
boolean recording = thread != null;
RecordingService.startService(this, targetFile.getName(), recording, encoder != null);
RecordingService.startService(this, storage.getDocumentName(targetUri), recording, encoder != null);
if (recording) {
pitch.record();
@ -368,7 +382,7 @@ public class RecordingActivity extends AppCompatActivity {
stopRecording();
RecordingService.startService(this, targetFile.getName(), thread != null, encoder != null);
RecordingService.startService(this, storage.getDocumentName(targetUri), thread != null, encoder != null);
pitch.setOnTouchListener(new View.OnTouchListener() {
@Override
@ -740,7 +754,7 @@ public class RecordingActivity extends AppCompatActivity {
}, "RecordingThread");
thread.start();
RecordingService.startService(this, targetFile.getName(), thread != null, encoder != null);
RecordingService.startService(this, storage.getDocumentName(targetUri), thread != null, encoder != null);
}
// calcuale buffer length dynamically, this way we can reduce thread cycles when activity in background
@ -823,9 +837,9 @@ public class RecordingActivity extends AppCompatActivity {
void encoding(final Runnable run) {
final File in = storage.getTempRecording();
final File out = targetFile;
final File out = storage.getTempEncoding();
File parent = targetFile.getParentFile();
File parent = out.getParentFile();
if (!parent.exists()) {
if (!parent.mkdirs()) { // in case if it were manually deleted
@ -844,11 +858,11 @@ public class RecordingActivity extends AppCompatActivity {
encoder = new FileEncoder(this, in, e);
RecordingService.startService(this, targetFile.getName(), thread != null, encoder != null);
RecordingService.startService(this, storage.getDocumentName(targetUri), thread != null, encoder != null);
final ProgressDialog d = new ProgressDialog(this);
d.setTitle(R.string.encoding_title);
d.setMessage(".../" + targetFile.getName());
d.setMessage(".../" + storage.getDocumentName(targetUri));
d.setMax(100);
d.setCancelable(false);
d.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
@ -864,10 +878,32 @@ public class RecordingActivity extends AppCompatActivity {
@Override
public void run() {
d.cancel();
storage.delete(in);
ContentResolver resolver = getContentResolver();
try {
InputStream is = new FileInputStream(out);
OutputStream os = resolver.openOutputStream(targetUri);
IOUtils.copy(is, os);
is.close();
os.close();
} catch (IOException e) {
storage.delete(out); // delete tmp encoding file
try {
storage.delete(targetUri);
} catch (RuntimeException ee) {
Log.d(TAG, "unable to delete target uri", e); // ignore, not even created?
}
Error(e);
d.cancel();
return;
}
storage.delete(in); // delete raw recording
String n = out.getName();
storage.delete(out); // delete tmp encoding file
SharedPreferences.Editor edit = shared.edit();
edit.putString(MainApplication.PREFERENCE_LAST, out.getName());
edit.putString(MainApplication.PREFERENCE_LAST, n);
edit.commit();
run.run();

View file

@ -244,7 +244,8 @@ public class SettingsActivity extends AppCompatActivity implements SharedPrefere
bindPreferenceSummaryToValue(pm.findPreference(MainApplication.PREFERENCE_CHANNELS));
bindPreferenceSummaryToValue(pm.findPreference(MainApplication.PREFERENCE_FORMAT));
StoragePathPreferenceCompat s = (StoragePathPreferenceCompat) pm.findPreference(MainApplication.PREFERENCE_STORAGE);
s.setPermissionsDialog(this, PERMISSIONS, 1);
// s.setPermissionsDialog(this, PERMISSIONS, 1);
s.setStorageAccessFramework(this, 2);
}
@Override
@ -281,6 +282,19 @@ public class SettingsActivity extends AppCompatActivity implements SharedPrefere
}
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
StoragePathPreferenceCompat s = (StoragePathPreferenceCompat) findPreference(MainApplication.PREFERENCE_STORAGE);
switch (requestCode) {
case 2:
s.onActivityResult(resultCode, data);
break;
}
}
@Override
public void onResume() {
super.onResume();

View file

@ -1,8 +1,13 @@
package com.github.axet.audiorecorder.app;
import android.content.ContentResolver;
import android.content.Context;
import android.content.SharedPreferences;
import android.net.Uri;
import android.os.Build;
import android.preference.PreferenceManager;
import android.provider.DocumentsContract;
import android.webkit.MimeTypeMap;
import java.io.File;
import java.text.SimpleDateFormat;
@ -10,13 +15,13 @@ import java.util.Date;
public class Storage extends com.github.axet.audiolibrary.app.Storage {
public static final String TMP_ENC = "encoding.data";
public Storage(Context context) {
super(context);
}
@Override
public File getNewFile() {
public Uri getNewFile() {
SharedPreferences shared = PreferenceManager.getDefaultSharedPreferences(context);
String ext = shared.getString(MainApplication.PREFERENCE_ENCODING, "");
@ -28,13 +33,55 @@ public class Storage extends com.github.axet.audiolibrary.app.Storage {
format = format.replaceAll("%I", ISO8601.format(new Date()));
format = format.replaceAll("%T", "" + System.currentTimeMillis() / 1000);
File parent = getStoragePath();
if (!parent.exists()) {
if (!parent.mkdirs())
throw new RuntimeException("Unable to create: " + parent);
}
Uri path = getStoragePath();
String s = path.getScheme();
return getNextFile(parent, format, ext);
if (Build.VERSION.SDK_INT >= 21 && s.startsWith(ContentResolver.SCHEME_CONTENT)) {
Uri n = getNextFile(path, format, ext);
String d = getDocumentName(n);
Uri docUri = DocumentsContract.buildDocumentUriUsingTree(path, DocumentsContract.getTreeDocumentId(path));
String mime = MimeTypeMap.getSingleton().getMimeTypeFromExtension(d);
Uri childrenUri = DocumentsContract.createDocument(context.getContentResolver(), docUri, mime, d);
return childrenUri;
} else if (s.startsWith(ContentResolver.SCHEME_FILE)) {
File f = new File(path.getPath());
if (!f.exists() && !f.mkdirs()) {
throw new RuntimeException("Unable to create: " + path);
}
return Uri.fromFile(getNextFile(f, format, ext));
} else {
throw new RuntimeException("unknown uri");
}
}
public File getTempEncoding() {
File internal = new File(context.getCacheDir(), TMP_ENC);
if (internal.exists())
return internal;
// Starting in KITKAT, no permissions are required to read or write to the returned path;
// it's always accessible to the calling app.
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) {
if (!permitted(context, PERMISSIONS))
return internal;
}
File c = context.getExternalCacheDir();
if (c == null) // some old phones <15API with disabled sdcard return null
return internal;
File external = new File(c, TMP_ENC);
if (external.exists())
return external;
long freeI = getFree(internal);
long freeE = getFree(external);
if (freeI > freeE)
return internal;
else
return external;
}
}

View file

@ -9,6 +9,7 @@ import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.SharedPreferences;
import android.net.Uri;
import android.os.Build;
import android.os.IBinder;
import android.preference.PreferenceManager;
@ -171,8 +172,8 @@ public class RecordingService extends Service {
String text;
if (targetFile == null) {
title = getString(R.string.app_name);
File f = storage.getStoragePath();
long free = Storage.getFree(f);
Uri f = storage.getStoragePath();
long free = storage.getFree(f);
long sec = Storage.average(this, free);
text = MainApplication.formatFree(this, free, sec);
view.setViewVisibility(R.id.notification_record, View.VISIBLE);
@ -227,7 +228,6 @@ public class RecordingService extends Service {
}
}
@Override
public void onTaskRemoved(Intent rootIntent) {
super.onTaskRemoved(rootIntent);

View file

@ -54,8 +54,7 @@
<ListView
android:id="@+id/list"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:layout_height="match_parent"
android:clipToPadding="false"
android:paddingBottom="61dp"></ListView>
</LinearLayout>