add background encoding support
This commit is contained in:
parent
9917db7bc2
commit
fe90c9d954
10 changed files with 628 additions and 58 deletions
|
|
@ -53,7 +53,7 @@ android {
|
|||
|
||||
dependencies {
|
||||
testImplementation 'junit:junit:4.12'
|
||||
implementation 'com.github.axet:android-audio-library:1.0.175' // implementation project(':android-audio-library')
|
||||
implementation 'com.github.axet:android-audio-library:1.0.176' // implementation project(':android-audio-library')
|
||||
implementation ('com.github.axet:wget:1.6.1') { exclude group: 'org.json', module: 'json' }
|
||||
assets('com.google.android.exoplayer:exoplayer:2.7.3') { exclude group: 'com.android.support' }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@
|
|||
android:supportsRtl="true"
|
||||
android:theme="@style/RecThemeLight">
|
||||
<service android:name=".services.RecordingService" />
|
||||
<service android:name=".services.EncodingService" />
|
||||
<service
|
||||
android:name=".services.TileService"
|
||||
android:icon="@drawable/ic_mic_24dp"
|
||||
|
|
|
|||
|
|
@ -1,9 +1,12 @@
|
|||
package com.github.axet.audiorecorder.activities;
|
||||
|
||||
import android.app.KeyguardManager;
|
||||
import android.app.ProgressDialog;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.content.SharedPreferences;
|
||||
import android.content.res.Configuration;
|
||||
import android.net.Uri;
|
||||
|
|
@ -11,6 +14,7 @@ import android.os.Build;
|
|||
import android.os.Bundle;
|
||||
import android.preference.PreferenceManager;
|
||||
import android.support.design.widget.FloatingActionButton;
|
||||
import android.support.design.widget.Snackbar;
|
||||
import android.support.v4.view.MenuItemCompat;
|
||||
import android.support.v7.app.AlertDialog;
|
||||
import android.support.v7.widget.DividerItemDecoration;
|
||||
|
|
@ -26,18 +30,27 @@ import android.view.WindowManager;
|
|||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.github.axet.androidlibrary.services.StorageProvider;
|
||||
import com.github.axet.androidlibrary.preferences.AboutPreferenceCompat;
|
||||
import com.github.axet.androidlibrary.activities.AppCompatThemeActivity;
|
||||
import com.github.axet.androidlibrary.widgets.ErrorDialog;
|
||||
import com.github.axet.androidlibrary.preferences.AboutPreferenceCompat;
|
||||
import com.github.axet.androidlibrary.preferences.OptimizationPreferenceCompat;
|
||||
import com.github.axet.androidlibrary.services.StorageProvider;
|
||||
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.encoders.FormatWAV;
|
||||
import com.github.axet.audiorecorder.R;
|
||||
import com.github.axet.audiorecorder.app.AudioApplication;
|
||||
import com.github.axet.audiorecorder.app.Recordings;
|
||||
import com.github.axet.audiorecorder.app.Storage;
|
||||
import com.github.axet.audiorecorder.services.EncodingService;
|
||||
import com.github.axet.audiorecorder.services.RecordingService;
|
||||
|
||||
import org.json.JSONException;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
|
||||
public class MainActivity extends AppCompatThemeActivity {
|
||||
public final static String TAG = MainActivity.class.getSimpleName();
|
||||
|
||||
|
|
@ -50,6 +63,7 @@ public class MainActivity extends AppCompatThemeActivity {
|
|||
Storage storage;
|
||||
|
||||
ScreenReceiver receiver;
|
||||
EncodingDialog encoding;
|
||||
|
||||
public static void startActivity(Context context) {
|
||||
Intent i = new Intent(context, MainActivity.class);
|
||||
|
|
@ -58,6 +72,137 @@ public class MainActivity extends AppCompatThemeActivity {
|
|||
context.startActivity(i);
|
||||
}
|
||||
|
||||
public class EncodingDialog extends BroadcastReceiver {
|
||||
Context context;
|
||||
Snackbar snackbar;
|
||||
IntentFilter filter = new IntentFilter();
|
||||
ProgressDialog d;
|
||||
|
||||
public EncodingDialog() {
|
||||
filter.addAction(EncodingService.UPDATE_ENCODING);
|
||||
filter.addAction(EncodingService.ERROR);
|
||||
}
|
||||
|
||||
public void registerReceiver(Context context) {
|
||||
this.context = context;
|
||||
context.registerReceiver(this, filter);
|
||||
}
|
||||
|
||||
public void close() {
|
||||
context.unregisterReceiver(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onReceive(final Context context, Intent intent) {
|
||||
String a = intent.getAction();
|
||||
if (a == null)
|
||||
return;
|
||||
if (a.equals(EncodingService.UPDATE_ENCODING)) {
|
||||
final int progress = intent.getIntExtra("progress", -1);
|
||||
final Uri targetUri = intent.getParcelableExtra("targetUri");
|
||||
|
||||
String p = " (" + progress + "%)";
|
||||
|
||||
if (d != null) {
|
||||
d.setProgress(progress);
|
||||
}
|
||||
|
||||
EncodingService.EncodingStorage storage = new EncodingService.EncodingStorage(new Storage(context));
|
||||
String str = "";
|
||||
for (File f : storage.keySet()) {
|
||||
EncodingService.EncodingStorage.Info info = storage.get(f);
|
||||
String name = Storage.getName(context, info.targetUri);
|
||||
str += "- " + name;
|
||||
if (info.targetUri.equals(targetUri))
|
||||
str += p;
|
||||
str += "\n";
|
||||
}
|
||||
|
||||
str = str.trim();
|
||||
|
||||
if (snackbar == null || !snackbar.isShown()) {
|
||||
snackbar = Snackbar.make(fab, str, Snackbar.LENGTH_LONG);
|
||||
snackbar.setDuration(Snackbar.LENGTH_INDEFINITE);
|
||||
snackbar.getView().setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
d = new ProgressDialog(context);
|
||||
d.setTitle(R.string.encoding_title);
|
||||
d.setMessage(".../" + Storage.getName(context, targetUri));
|
||||
d.setMax(100);
|
||||
d.setProgress(progress);
|
||||
d.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
|
||||
d.setIndeterminate(false);
|
||||
d.show();
|
||||
EncodingService.startIfPending(context);
|
||||
}
|
||||
});
|
||||
snackbar.show();
|
||||
} else {
|
||||
snackbar.setText(str);
|
||||
snackbar.show();
|
||||
}
|
||||
|
||||
if (progress == 100) {
|
||||
recordings.load(false, null);
|
||||
if (snackbar != null && snackbar.isShown()) {
|
||||
snackbar.setDuration(Snackbar.LENGTH_SHORT);
|
||||
snackbar.show();
|
||||
}
|
||||
}
|
||||
}
|
||||
if (a.equals(EncodingService.ERROR)) {
|
||||
if (d != null) {
|
||||
d.dismiss();
|
||||
d = null;
|
||||
}
|
||||
File in = (File) intent.getSerializableExtra("in");
|
||||
RawSamples.Info info;
|
||||
try {
|
||||
info = new RawSamples.Info(intent.getStringExtra("info"));
|
||||
} catch (JSONException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
Throwable e = (Throwable) intent.getSerializableExtra("e");
|
||||
Error(in, info, e);
|
||||
}
|
||||
}
|
||||
|
||||
public void Error(final File in, final RawSamples.Info info, Throwable e) {
|
||||
ErrorDialog builder = new ErrorDialog(context, ErrorDialog.toMessage(e));
|
||||
builder.setOnCancelListener(new DialogInterface.OnCancelListener() {
|
||||
@Override
|
||||
public void onCancel(DialogInterface dialog) {
|
||||
}
|
||||
});
|
||||
builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
}
|
||||
});
|
||||
if (in.length() > 0) {
|
||||
builder.setNeutralButton(R.string.save_as_wav, new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
final OpenFileDialog d = new OpenFileDialog(context, OpenFileDialog.DIALOG_TYPE.FOLDER_DIALOG);
|
||||
d.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
EncodingService.saveAsWAV(context, in, storage.getNewFile(d.getCurrentPath(), FormatWAV.EXT), info);
|
||||
}
|
||||
});
|
||||
d.show();
|
||||
}
|
||||
});
|
||||
}
|
||||
builder.show();
|
||||
}
|
||||
|
||||
public void hide() {
|
||||
snackbar.dismiss();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getAppTheme() {
|
||||
return AudioApplication.getTheme(this, R.style.RecThemeLight_NoActionBar, R.style.RecThemeDark_NoActionBar);
|
||||
|
|
@ -72,6 +217,7 @@ public class MainActivity extends AppCompatThemeActivity {
|
|||
getWindow().addFlags(WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON);
|
||||
|
||||
setContentView(R.layout.activity_main);
|
||||
final View content = findViewById(android.R.id.content);
|
||||
|
||||
storage = new Storage(this);
|
||||
|
||||
|
|
@ -121,8 +267,11 @@ public class MainActivity extends AppCompatThemeActivity {
|
|||
}
|
||||
};
|
||||
receiver.registerReceiver(this);
|
||||
encoding = new EncodingDialog();
|
||||
encoding.registerReceiver(this);
|
||||
|
||||
RecordingService.startIfPending(this);
|
||||
EncodingService.startIfPending(this);
|
||||
|
||||
try {
|
||||
new Recordings.ExoLoader(this, false);
|
||||
|
|
@ -307,6 +456,7 @@ public class MainActivity extends AppCompatThemeActivity {
|
|||
super.onDestroy();
|
||||
recordings.close();
|
||||
receiver.close();
|
||||
encoding.close();
|
||||
}
|
||||
|
||||
void updateHeader() {
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@ package com.github.axet.audiorecorder.activities;
|
|||
|
||||
import android.Manifest;
|
||||
import android.app.Activity;
|
||||
import android.app.ProgressDialog;
|
||||
import android.content.ContentResolver;
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
|
|
@ -48,18 +47,14 @@ import com.github.axet.androidlibrary.widgets.Toast;
|
|||
import com.github.axet.audiolibrary.app.RawSamples;
|
||||
import com.github.axet.audiolibrary.app.Sound;
|
||||
import com.github.axet.audiolibrary.encoders.Factory;
|
||||
import com.github.axet.audiolibrary.encoders.FileEncoder;
|
||||
import com.github.axet.audiolibrary.encoders.FormatWAV;
|
||||
import com.github.axet.audiolibrary.encoders.OnFlyEncoding;
|
||||
import com.github.axet.audiolibrary.filters.AmplifierFilter;
|
||||
import com.github.axet.audiolibrary.filters.SkipSilenceFilter;
|
||||
import com.github.axet.audiolibrary.filters.VoiceFilter;
|
||||
import com.github.axet.audiolibrary.widgets.PitchView;
|
||||
import com.github.axet.audiorecorder.BuildConfig;
|
||||
import com.github.axet.audiorecorder.R;
|
||||
import com.github.axet.audiorecorder.app.AudioApplication;
|
||||
import com.github.axet.audiorecorder.app.Storage;
|
||||
import com.github.axet.audiorecorder.services.BluetoothReceiver;
|
||||
import com.github.axet.audiorecorder.services.EncodingService;
|
||||
import com.github.axet.audiorecorder.services.RecordingService;
|
||||
import com.github.axet.wget.SpeedInfo;
|
||||
|
||||
|
|
@ -84,7 +79,6 @@ public class RecordingActivity extends AppCompatThemeActivity {
|
|||
public static final String STOP_RECORDING = RecordingService.class.getCanonicalName() + ".STOP_RECORDING";
|
||||
|
||||
PhoneStateChangeListener pscl = new PhoneStateChangeListener();
|
||||
FileEncoder encoder;
|
||||
Headset headset;
|
||||
Intent recordSoundIntent = null;
|
||||
|
||||
|
|
@ -149,7 +143,7 @@ public class RecordingActivity extends AppCompatThemeActivity {
|
|||
}
|
||||
}
|
||||
if (msg.what == AudioApplication.RecordingStorage.ERROR)
|
||||
Error((Exception) msg.obj);
|
||||
Error((Throwable) msg.obj);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -435,15 +429,7 @@ public class RecordingActivity extends AppCompatThemeActivity {
|
|||
}
|
||||
|
||||
public String toMessage(Throwable e) {
|
||||
Throwable t;
|
||||
if (encoder == null) {
|
||||
t = e;
|
||||
} else {
|
||||
t = encoder.getException();
|
||||
if (t == null)
|
||||
t = e;
|
||||
}
|
||||
return ErrorDialog.toMessage(t);
|
||||
return ErrorDialog.toMessage(e);
|
||||
}
|
||||
|
||||
public void Error(Throwable e) {
|
||||
|
|
@ -474,14 +460,7 @@ public class RecordingActivity extends AppCompatThemeActivity {
|
|||
d.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
OnFlyEncoding fly = new OnFlyEncoding(recording.storage, recording.storage.getNewFile(d.getCurrentPath(), FormatWAV.EXT), recording.getInfo());
|
||||
FileEncoder encoder = new FileEncoder(RecordingActivity.this, in, fly);
|
||||
encoding(encoder, fly, new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
finish();
|
||||
}
|
||||
});
|
||||
EncodingService.saveAsWAV(RecordingActivity.this, recording.storage.getTempRecording(), recording.storage.getNewFile(d.getCurrentPath(), FormatWAV.EXT), recording.getInfo());
|
||||
}
|
||||
});
|
||||
d.show();
|
||||
|
|
@ -569,8 +548,6 @@ public class RecordingActivity extends AppCompatThemeActivity {
|
|||
done.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
if (encoder != null)
|
||||
return;
|
||||
String msg;
|
||||
if (shared.getBoolean(AudioApplication.PREFERENCE_FLY, false))
|
||||
msg = getString(R.string.recording_status_recording);
|
||||
|
|
@ -749,7 +726,7 @@ public class RecordingActivity extends AppCompatThemeActivity {
|
|||
|
||||
boolean r = recording.thread != null;
|
||||
|
||||
RecordingService.startService(this, Storage.getName(this, recording.targetUri), r, encoder != null, duration);
|
||||
RecordingService.startService(this, Storage.getName(this, recording.targetUri), r, duration);
|
||||
|
||||
if (r) {
|
||||
pitch.record();
|
||||
|
|
@ -780,7 +757,7 @@ public class RecordingActivity extends AppCompatThemeActivity {
|
|||
|
||||
stopRecording();
|
||||
|
||||
RecordingService.startService(this, Storage.getName(this, recording.targetUri), false, encoder != null, duration);
|
||||
RecordingService.startService(this, Storage.getName(this, recording.targetUri), false, duration);
|
||||
|
||||
final SharedPreferences shared = PreferenceManager.getDefaultSharedPreferences(this);
|
||||
|
||||
|
|
@ -1038,11 +1015,6 @@ public class RecordingActivity extends AppCompatThemeActivity {
|
|||
play = null;
|
||||
}
|
||||
|
||||
if (encoder != null) {
|
||||
encoder.close();
|
||||
encoder = null;
|
||||
}
|
||||
|
||||
final SharedPreferences shared = PreferenceManager.getDefaultSharedPreferences(this);
|
||||
SharedPreferences.Editor editor = shared.edit();
|
||||
editor.remove(AudioApplication.PREFERENCE_TARGET);
|
||||
|
|
@ -1065,7 +1037,7 @@ public class RecordingActivity extends AppCompatThemeActivity {
|
|||
|
||||
recording.startRecording();
|
||||
|
||||
RecordingService.startService(this, Storage.getName(this, recording.targetUri), true, encoder != null, duration);
|
||||
RecordingService.startService(this, Storage.getName(this, recording.targetUri), true, duration);
|
||||
} catch (RuntimeException e) {
|
||||
Toast.Error(RecordingActivity.this, e);
|
||||
finish();
|
||||
|
|
@ -1076,7 +1048,7 @@ public class RecordingActivity extends AppCompatThemeActivity {
|
|||
long ms = samplesTime / recording.sampleRate * 1000;
|
||||
duration = AudioApplication.formatDuration(this, ms);
|
||||
time.setText(duration);
|
||||
RecordingService.startService(this, Storage.getName(this, recording.targetUri), recording.thread != null, encoder != null, duration);
|
||||
RecordingService.startService(this, Storage.getName(this, recording.targetUri), recording.thread != null, duration);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -1108,8 +1080,6 @@ public class RecordingActivity extends AppCompatThemeActivity {
|
|||
}
|
||||
}
|
||||
|
||||
final File in = recording.storage.getTempRecording();
|
||||
|
||||
final Runnable last = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
|
|
@ -1120,12 +1090,14 @@ public class RecordingActivity extends AppCompatThemeActivity {
|
|||
}
|
||||
};
|
||||
|
||||
final File in = recording.storage.getTempRecording();
|
||||
|
||||
if (!in.exists() || in.length() == 0) {
|
||||
last.run();
|
||||
return;
|
||||
}
|
||||
|
||||
final OnFlyEncoding fly = new OnFlyEncoding(recording.storage, recording.targetUri, recording.getInfo());
|
||||
EncodingService.startEncoding(this, in, recording.targetUri, recording.getInfo());
|
||||
|
||||
encoder = new FileEncoder(this, in, fly);
|
||||
|
||||
|
|
@ -1168,6 +1140,7 @@ public class RecordingActivity extends AppCompatThemeActivity {
|
|||
Error(encoder.getException());
|
||||
}
|
||||
});
|
||||
last.run();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -344,7 +344,7 @@ public class AudioApplication extends com.github.axet.audiolibrary.app.MainAppli
|
|||
}
|
||||
}
|
||||
|
||||
public void Post(Exception e) {
|
||||
public void Post(Throwable e) {
|
||||
Post(ERROR, e);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -9,6 +9,11 @@ import android.widget.TextView;
|
|||
|
||||
import com.github.axet.androidlibrary.widgets.ErrorDialog;
|
||||
import com.github.axet.audiorecorder.R;
|
||||
import com.github.axet.audiorecorder.services.EncodingService;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class Recordings extends com.github.axet.audiolibrary.app.Recordings {
|
||||
public View progressEmpty;
|
||||
|
|
@ -53,4 +58,17 @@ public class Recordings extends com.github.axet.audiolibrary.app.Recordings {
|
|||
done.run();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void scan(List<Storage.Node> nn, boolean clean, Runnable done) {
|
||||
EncodingService.EncodingStorage storage = new EncodingService.EncodingStorage(new Storage(context));
|
||||
for (Storage.Node n : new ArrayList<>(nn)) {
|
||||
for (File key : storage.keySet()) {
|
||||
EncodingService.EncodingStorage.Info info = storage.get(key);
|
||||
if (n.uri.equals(info.targetUri))
|
||||
nn.remove(n);
|
||||
}
|
||||
}
|
||||
super.scan(nn, clean, done);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,427 @@
|
|||
package com.github.axet.audiorecorder.services;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.app.Notification;
|
||||
import android.app.PendingIntent;
|
||||
import android.app.Service;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.net.Uri;
|
||||
import android.os.IBinder;
|
||||
import android.preference.PreferenceManager;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
|
||||
import com.github.axet.androidlibrary.app.AlarmManager;
|
||||
import com.github.axet.androidlibrary.app.ProximityShader;
|
||||
import com.github.axet.androidlibrary.preferences.OptimizationPreferenceCompat;
|
||||
import com.github.axet.androidlibrary.services.PersistentService;
|
||||
import com.github.axet.androidlibrary.widgets.RemoteNotificationCompat;
|
||||
import com.github.axet.audiolibrary.app.RawSamples;
|
||||
import com.github.axet.audiolibrary.app.Storage;
|
||||
import com.github.axet.audiolibrary.encoders.FileEncoder;
|
||||
import com.github.axet.audiolibrary.encoders.OnFlyEncoding;
|
||||
import com.github.axet.audiolibrary.filters.AmplifierFilter;
|
||||
import com.github.axet.audiolibrary.filters.SkipSilenceFilter;
|
||||
import com.github.axet.audiolibrary.filters.VoiceFilter;
|
||||
import com.github.axet.audiorecorder.R;
|
||||
import com.github.axet.audiorecorder.activities.MainActivity;
|
||||
import com.github.axet.audiorecorder.activities.RecordingActivity;
|
||||
import com.github.axet.audiorecorder.app.AudioApplication;
|
||||
|
||||
import org.apache.commons.io.FileUtils;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FilenameFilter;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
|
||||
public class EncodingService extends PersistentService {
|
||||
public static final String TAG = EncodingService.class.getSimpleName();
|
||||
|
||||
public static final int NOTIFICATION_RECORDING_ICON = 2;
|
||||
|
||||
public static String SHOW_ACTIVITY = EncodingService.class.getCanonicalName() + ".SHOW_ACTIVITY";
|
||||
public static String SAVE_AS_WAV = EncodingService.class.getCanonicalName() + ".SAVE_AS_WAV";
|
||||
public static String UPDATE_ENCODING = EncodingService.class.getCanonicalName() + ".UPDATE_ENCODING";
|
||||
public static String START_ENCODING = EncodingService.class.getCanonicalName() + ".START_ENCODING";
|
||||
public static String ERROR = EncodingService.class.getCanonicalName() + ".ERROR";
|
||||
|
||||
public static String JSON_EXT = "json";
|
||||
|
||||
static {
|
||||
OptimizationPreferenceCompat.REFRESH = AlarmManager.MIN1;
|
||||
}
|
||||
|
||||
Storage storage; // for storage path
|
||||
EncodingStorage encodings;
|
||||
FileEncoder encoder;
|
||||
|
||||
public static void startIfPending(Context context) { // if encoding pending
|
||||
Storage storage = new Storage(context);
|
||||
EncodingStorage enc = new EncodingStorage(storage);
|
||||
if (!enc.isEmpty()) {
|
||||
start(context);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
public static void start(Context context) { // start persistent icon service
|
||||
start(context, new Intent(context, EncodingService.class));
|
||||
}
|
||||
|
||||
public static void saveAsWAV(Context context, File in, File out, RawSamples.Info info) { // start encoding process for selected file
|
||||
String j;
|
||||
try {
|
||||
j = info.save().toString();
|
||||
} catch (JSONException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
start(context, new Intent(context, EncodingService.class).setAction(SAVE_AS_WAV)
|
||||
.putExtra("in", in)
|
||||
.putExtra("out", out)
|
||||
.putExtra("info", j)
|
||||
);
|
||||
}
|
||||
|
||||
public static void stop(Context context) {
|
||||
stop(context, new Intent(context, EncodingService.class));
|
||||
}
|
||||
|
||||
public void Error(File in, RawSamples.Info info, Throwable e) {
|
||||
String json;
|
||||
try {
|
||||
json = info.save().toString();
|
||||
} catch (JSONException e1) {
|
||||
throw new RuntimeException(e1);
|
||||
}
|
||||
sendBroadcast(new Intent(ERROR)
|
||||
.putExtra("in", in)
|
||||
.putExtra("info", json)
|
||||
.putExtra("e", e)
|
||||
);
|
||||
}
|
||||
|
||||
public static void startEncoding(Context context, File in, Uri targetUri, RawSamples.Info info) {
|
||||
EncodingStorage storage = new EncodingStorage(new Storage(context));
|
||||
storage.save(in, targetUri, info);
|
||||
String json;
|
||||
try {
|
||||
json = info.save().toString();
|
||||
} catch (JSONException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
start(context, new Intent(context, EncodingService.class).setAction(START_ENCODING)
|
||||
.putExtra("in", in)
|
||||
.putExtra("targetUri", targetUri)
|
||||
.putExtra("info", json)
|
||||
);
|
||||
}
|
||||
|
||||
public static class EncodingStorage extends HashMap<File, EncodingStorage.Info> {
|
||||
public Storage storage;
|
||||
|
||||
public static File jsonFile(File f) {
|
||||
return new File(f.getParentFile(), Storage.getNameNoExt(f) + "." + JSON_EXT);
|
||||
}
|
||||
|
||||
public static class Info {
|
||||
public Uri targetUri;
|
||||
public RawSamples.Info info;
|
||||
|
||||
public Info() {
|
||||
}
|
||||
|
||||
public Info(Uri t, RawSamples.Info i) {
|
||||
this.targetUri = t;
|
||||
this.info = i;
|
||||
}
|
||||
|
||||
public Info(String json) throws JSONException {
|
||||
load(new JSONObject(json));
|
||||
}
|
||||
|
||||
public Info(JSONObject json) throws JSONException {
|
||||
load(json);
|
||||
}
|
||||
|
||||
public JSONObject save() throws JSONException {
|
||||
JSONObject json = new JSONObject();
|
||||
json.put("targetUri", targetUri.toString());
|
||||
json.put("info", info.save());
|
||||
return json;
|
||||
}
|
||||
|
||||
public void load(JSONObject json) throws JSONException {
|
||||
targetUri = Uri.parse(json.getString("targetUri"));
|
||||
info = new RawSamples.Info(json.getJSONObject("info"));
|
||||
}
|
||||
}
|
||||
|
||||
public EncodingStorage(Storage s) {
|
||||
storage = s;
|
||||
load();
|
||||
}
|
||||
|
||||
public void load() {
|
||||
clear();
|
||||
File storage = this.storage.getTempRecording().getParentFile();
|
||||
File[] ff = storage.listFiles(new FilenameFilter() {
|
||||
String start = Storage.getNameNoExt(Storage.TMP_ENC);
|
||||
String ext = Storage.getExt(Storage.TMP_ENC);
|
||||
|
||||
@Override
|
||||
public boolean accept(File dir, String name) {
|
||||
return name.startsWith(start) && name.endsWith("." + ext);
|
||||
}
|
||||
});
|
||||
if (ff == null)
|
||||
return;
|
||||
for (File f : ff) {
|
||||
File j = jsonFile(f);
|
||||
try {
|
||||
put(f, new Info(new JSONObject(FileUtils.readFileToString(j, Charset.defaultCharset()))));
|
||||
} catch (Exception e) {
|
||||
Log.d(TAG, "unable to read json", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void save(File in, Uri targetUri, RawSamples.Info info) {
|
||||
File to = storage.getTempEncoding();
|
||||
to = Storage.getNextFile(to);
|
||||
to = Storage.move(in, to);
|
||||
try {
|
||||
File j = jsonFile(to);
|
||||
Info rec = new Info(targetUri, info);
|
||||
JSONObject json = rec.save();
|
||||
FileUtils.writeStringToFile(j, json.toString(), Charset.defaultCharset());
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public ArrayList<String> asList() {
|
||||
ArrayList<String> list = new ArrayList<>();
|
||||
for (File key : keySet()) {
|
||||
EncodingStorage.Info k = get(key);
|
||||
list.add(k.targetUri.toString());
|
||||
}
|
||||
return list;
|
||||
}
|
||||
}
|
||||
|
||||
public EncodingService() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate() {
|
||||
super.onCreate();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreateOptimization() {
|
||||
storage = new Storage(this);
|
||||
encodings = new EncodingStorage(storage);
|
||||
optimization = new OptimizationPreferenceCompat.ServiceReceiver(this, NOTIFICATION_RECORDING_ICON, null, AudioApplication.PREFERENCE_NEXT) {
|
||||
Intent notificationIntent;
|
||||
|
||||
@Override
|
||||
public void onCreateIcon(Service service, int id) {
|
||||
icon = new OptimizationPreferenceCompat.OptimizationIcon(service, id, key) {
|
||||
@Override
|
||||
public void updateIcon() {
|
||||
icon.updateIcon(new Intent());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateIcon(Intent intent) {
|
||||
super.updateIcon(intent);
|
||||
notificationIntent = intent;
|
||||
}
|
||||
|
||||
@SuppressLint("RestrictedApi")
|
||||
public Notification build(Intent intent) {
|
||||
String targetFile = intent.getStringExtra("targetFile");
|
||||
int progress = intent.getIntExtra("progress", 0);
|
||||
|
||||
PendingIntent main;
|
||||
|
||||
RemoteNotificationCompat.Builder builder;
|
||||
|
||||
String title;
|
||||
String text;
|
||||
|
||||
title = getString(R.string.encoding_title);
|
||||
text = ".../" + targetFile + " (" + progress + "%)";
|
||||
builder = new RemoteNotificationCompat.Low(context, R.layout.notifictaion);
|
||||
builder.setViewVisibility(R.id.notification_record, View.VISIBLE);
|
||||
builder.setViewVisibility(R.id.notification_pause, View.GONE);
|
||||
main = PendingIntent.getActivity(context, 0, new Intent(context, MainActivity.class), PendingIntent.FLAG_UPDATE_CURRENT);
|
||||
|
||||
builder.setViewVisibility(R.id.notification_pause, View.GONE);
|
||||
builder.setViewVisibility(R.id.notification_record, View.GONE);
|
||||
|
||||
builder.setTheme(AudioApplication.getTheme(context, R.style.RecThemeLight, R.style.RecThemeDark))
|
||||
.setChannel(AudioApplication.from(context).channelStatus)
|
||||
.setImageViewTint(R.id.icon_circle, builder.getThemeColor(R.attr.colorButtonNormal))
|
||||
.setTitle(title)
|
||||
.setText(text)
|
||||
.setWhen(icon.notification)
|
||||
.setMainIntent(main)
|
||||
.setAdaptiveIcon(R.drawable.ic_launcher_foreground)
|
||||
.setSmallIcon(R.drawable.ic_launcher_notification)
|
||||
.setOngoing(true);
|
||||
|
||||
return builder.build();
|
||||
}
|
||||
};
|
||||
icon.create();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isOptimization() {
|
||||
return true; // we are not using optimization preference
|
||||
}
|
||||
};
|
||||
optimization.create();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStartCommand(Intent intent) {
|
||||
String a = intent.getAction();
|
||||
if (a == null) {
|
||||
optimization.icon.updateIcon(intent);
|
||||
} else if (a.equals(SHOW_ACTIVITY)) {
|
||||
ProximityShader.closeSystemDialogs(this);
|
||||
if (intent.getStringExtra("targetFile") == null)
|
||||
MainActivity.startActivity(this);
|
||||
else
|
||||
RecordingActivity.startActivity(this, !intent.getBooleanExtra("recording", false));
|
||||
} else if (a.equals(SAVE_AS_WAV)) {
|
||||
File in = (File) intent.getSerializableExtra("in");
|
||||
File out = (File) intent.getSerializableExtra("out");
|
||||
RawSamples.Info info;
|
||||
try {
|
||||
info = new RawSamples.Info(intent.getStringExtra("info"));
|
||||
} catch (JSONException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
if (encoder == null) {
|
||||
OnFlyEncoding fly = new OnFlyEncoding(storage, out, info);
|
||||
encoder = new FileEncoder(this, in, fly);
|
||||
encoding(encoder, fly, info, new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
encoder.close();
|
||||
encoder = null;
|
||||
startEncoding();
|
||||
}
|
||||
});
|
||||
}
|
||||
} else if (a.equals(START_ENCODING)) {
|
||||
File in = (File) intent.getSerializableExtra("in");
|
||||
Uri targetUri = intent.getParcelableExtra("targetUri");
|
||||
RawSamples.Info info;
|
||||
try {
|
||||
info = new RawSamples.Info(intent.getStringExtra("info"));
|
||||
} catch (JSONException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
if (encoder == null) {
|
||||
OnFlyEncoding fly = new OnFlyEncoding(storage, targetUri, info);
|
||||
encoder = new FileEncoder(this, in, fly);
|
||||
encoding(encoder, fly, info, new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
encoder.close();
|
||||
encoder = null;
|
||||
startEncoding();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
startEncoding();
|
||||
}
|
||||
|
||||
public void startEncoding() {
|
||||
if (encoder != null)
|
||||
return;
|
||||
SharedPreferences shared = PreferenceManager.getDefaultSharedPreferences(this);
|
||||
encodings.load();
|
||||
for (File in : encodings.keySet()) {
|
||||
EncodingStorage.Info info = encodings.get(in);
|
||||
final OnFlyEncoding fly = new OnFlyEncoding(this.storage, info.targetUri, info.info);
|
||||
|
||||
encoder = new FileEncoder(this, in, fly);
|
||||
|
||||
if (shared.getBoolean(AudioApplication.PREFERENCE_VOICE, false))
|
||||
encoder.filters.add(new VoiceFilter(info.info));
|
||||
float amp = shared.getFloat(AudioApplication.PREFERENCE_VOLUME, 1);
|
||||
if (amp != 1)
|
||||
encoder.filters.add(new AmplifierFilter(amp));
|
||||
if (shared.getBoolean(AudioApplication.PREFERENCE_SKIP, false))
|
||||
encoder.filters.add(new SkipSilenceFilter(info.info));
|
||||
|
||||
encoding(encoder, fly, info.info, new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
encoder.close();
|
||||
encoder = null;
|
||||
startEncoding();
|
||||
}
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
stopSelf();
|
||||
}
|
||||
|
||||
void encoding(final FileEncoder encoder, final OnFlyEncoding fly, final RawSamples.Info info, final Runnable done) {
|
||||
encoder.run(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
Intent intent = new Intent(UPDATE_ENCODING)
|
||||
.putExtra("progress", encoder.getProgress())
|
||||
.putExtra("targetUri", fly.targetUri)
|
||||
.putExtra("targetFile", Storage.getName(EncodingService.this, fly.targetUri));
|
||||
sendBroadcast(intent);
|
||||
optimization.icon.updateIcon(intent);
|
||||
}
|
||||
}, new Runnable() {
|
||||
@Override
|
||||
public void run() { // success
|
||||
Storage.delete(encoder.in); // delete raw recording
|
||||
Storage.delete(EncodingStorage.jsonFile(encoder.in)); // delete json file
|
||||
sendBroadcast(new Intent(UPDATE_ENCODING)
|
||||
.putExtra("progress", 100)
|
||||
.putExtra("targetUri", fly.targetUri)
|
||||
);
|
||||
done.run();
|
||||
}
|
||||
}, new Runnable() {
|
||||
@Override
|
||||
public void run() { // or error
|
||||
Storage.delete(EncodingService.this, fly.targetUri); // fly has fd, delete target manually
|
||||
Error(encoder.in, info, encoder.getException());
|
||||
stopSelf();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public IBinder onBind(Intent intent) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
super.onDestroy();
|
||||
}
|
||||
}
|
||||
|
|
@ -12,5 +12,6 @@ public class OnBootReceiver extends BroadcastReceiver {
|
|||
public void onReceive(Context context, Intent i) {
|
||||
Log.d(TAG, "onReceive");
|
||||
RecordingService.startIfPending(context);
|
||||
EncodingService.startIfPending(context);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,8 +19,8 @@ import android.widget.RemoteViews;
|
|||
|
||||
import com.github.axet.androidlibrary.app.AlarmManager;
|
||||
import com.github.axet.androidlibrary.app.ProximityShader;
|
||||
import com.github.axet.androidlibrary.services.PersistentService;
|
||||
import com.github.axet.androidlibrary.preferences.OptimizationPreferenceCompat;
|
||||
import com.github.axet.androidlibrary.services.PersistentService;
|
||||
import com.github.axet.androidlibrary.widgets.RemoteNotificationCompat;
|
||||
import com.github.axet.androidlibrary.widgets.RemoteViewsCompat;
|
||||
import com.github.axet.audiolibrary.app.Storage;
|
||||
|
|
@ -32,7 +32,8 @@ import com.github.axet.audiorecorder.app.AudioApplication;
|
|||
import java.io.File;
|
||||
|
||||
/**
|
||||
* Sometimes RecordingActivity started twice when launched from lockscreen. We need service and move recording into Application object.
|
||||
* Sometimes RecordingActivity started twice when launched from lockscreen.
|
||||
* We need service and keep recording into Application object.
|
||||
*/
|
||||
public class RecordingService extends PersistentService {
|
||||
public static final String TAG = RecordingService.class.getSimpleName();
|
||||
|
|
@ -49,16 +50,16 @@ public class RecordingService extends PersistentService {
|
|||
|
||||
Storage storage; // for storage path
|
||||
|
||||
public static void startIfEnabled(Context context) {
|
||||
public static void startIfEnabled(Context context) { // notification controls enabled?
|
||||
SharedPreferences shared = PreferenceManager.getDefaultSharedPreferences(context);
|
||||
if (!shared.getBoolean(AudioApplication.PREFERENCE_CONTROLS, false))
|
||||
return;
|
||||
start(context);
|
||||
}
|
||||
|
||||
public static void startIfPending(Context context) {
|
||||
Storage st = new Storage(context);
|
||||
if (st.recordingPending()) {
|
||||
public static void startIfPending(Context context) { // if recording pending or controls enabled
|
||||
Storage storage = new Storage(context);
|
||||
if (storage.recordingPending()) {
|
||||
final SharedPreferences shared = PreferenceManager.getDefaultSharedPreferences(context);
|
||||
String f = shared.getString(AudioApplication.PREFERENCE_TARGET, "");
|
||||
String d;
|
||||
|
|
@ -73,21 +74,20 @@ public class RecordingService extends PersistentService {
|
|||
File file = new File(f);
|
||||
d = file.getName();
|
||||
}
|
||||
startService(context, d, false, false, null);
|
||||
startService(context, d, false, null);
|
||||
return;
|
||||
}
|
||||
startIfEnabled(context);
|
||||
}
|
||||
|
||||
public static void start(Context context) {
|
||||
public static void start(Context context) { // start persistent icon service
|
||||
start(context, new Intent(context, RecordingService.class));
|
||||
}
|
||||
|
||||
public static void startService(Context context, String targetFile, boolean recording, boolean encoding, String duration) {
|
||||
public static void startService(Context context, String targetFile, boolean recording, String duration) { // start recording / pause service
|
||||
start(context, new Intent(context, RecordingService.class)
|
||||
.putExtra("targetFile", targetFile)
|
||||
.putExtra("recording", recording)
|
||||
.putExtra("encoding", encoding)
|
||||
.putExtra("duration", duration)
|
||||
);
|
||||
}
|
||||
|
|
@ -137,7 +137,7 @@ public class RecordingService extends PersistentService {
|
|||
public Notification build(Intent intent) {
|
||||
String targetFile = intent.getStringExtra("targetFile");
|
||||
boolean recording = intent.getBooleanExtra("recording", false);
|
||||
boolean encoding = intent.getBooleanExtra("encoding", false);
|
||||
boolean encoding = false;
|
||||
String duration = intent.getStringExtra("duration");
|
||||
|
||||
PendingIntent main;
|
||||
|
|
@ -146,7 +146,7 @@ public class RecordingService extends PersistentService {
|
|||
|
||||
String title;
|
||||
String text;
|
||||
if (targetFile == null) {
|
||||
if (targetFile == null) { // buildPersistentIcon();
|
||||
title = getString(R.string.app_name);
|
||||
Uri f = storage.getStoragePath();
|
||||
long free = Storage.getFree(context, f);
|
||||
|
|
@ -156,7 +156,7 @@ public class RecordingService extends PersistentService {
|
|||
builder.setViewVisibility(R.id.notification_record, View.VISIBLE);
|
||||
builder.setViewVisibility(R.id.notification_pause, View.GONE);
|
||||
main = PendingIntent.getActivity(context, 0, new Intent(context, MainActivity.class), PendingIntent.FLAG_UPDATE_CURRENT);
|
||||
} else {
|
||||
} else { // buildRecordingIcon();
|
||||
if (recording)
|
||||
title = getString(R.string.recording_title);
|
||||
else
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ buildscript {
|
|||
google()
|
||||
}
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:3.6.2'
|
||||
classpath 'com.android.tools.build:gradle:4.1.0'
|
||||
classpath 'com.github.axet:gradle-android-dx:0.0.4'
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue