kopia lustrzana https://github.com/olgamiller/SSTVEncoder2
Support writing of wav-file also in Android 10
rodzic
4fc704604b
commit
d452255b62
|
@ -3,7 +3,8 @@
|
||||||
package="om.sstvencoder">
|
package="om.sstvencoder">
|
||||||
|
|
||||||
<uses-permission
|
<uses-permission
|
||||||
android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
|
android:name="android.permission.WRITE_EXTERNAL_STORAGE"
|
||||||
|
android:maxSdkVersion="28"/>
|
||||||
<uses-feature
|
<uses-feature
|
||||||
android:name="android.hardware.camera"
|
android:name="android.hardware.camera"
|
||||||
android:required="false"/>
|
android:required="false"/>
|
||||||
|
|
|
@ -17,7 +17,6 @@ package om.sstvencoder;
|
||||||
|
|
||||||
import android.graphics.Bitmap;
|
import android.graphics.Bitmap;
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
@ -26,6 +25,7 @@ import om.sstvencoder.ModeInterfaces.IModeInfo;
|
||||||
import om.sstvencoder.Modes.ModeFactory;
|
import om.sstvencoder.Modes.ModeFactory;
|
||||||
import om.sstvencoder.Output.IOutput;
|
import om.sstvencoder.Output.IOutput;
|
||||||
import om.sstvencoder.Output.OutputFactory;
|
import om.sstvencoder.Output.OutputFactory;
|
||||||
|
import om.sstvencoder.Output.WaveFileOutputContext;
|
||||||
|
|
||||||
// Creates IMode instance
|
// Creates IMode instance
|
||||||
class Encoder {
|
class Encoder {
|
||||||
|
@ -108,21 +108,21 @@ class Encoder {
|
||||||
enqueue(mode);
|
enqueue(mode);
|
||||||
}
|
}
|
||||||
|
|
||||||
void save(Bitmap bitmap, File file) {
|
void save(Bitmap bitmap, WaveFileOutputContext context) {
|
||||||
if (mSaveWaveThread != null && mSaveWaveThread.isAlive())
|
if (mSaveWaveThread != null && mSaveWaveThread.isAlive())
|
||||||
return;
|
return;
|
||||||
IOutput output = OutputFactory.createOutputForSavingAsWave(file);
|
IOutput output = OutputFactory.createOutputForSavingAsWave(context);
|
||||||
IMode mode = ModeFactory.CreateMode(mModeClass, bitmap, output);
|
IMode mode = ModeFactory.CreateMode(mModeClass, bitmap, output);
|
||||||
if (mode != null)
|
if (mode != null)
|
||||||
save(mode, file);
|
save(mode, context);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void save(final IMode mode, final File file) {
|
private void save(final IMode mode, final WaveFileOutputContext context) {
|
||||||
mSaveWaveThread = new Thread() {
|
mSaveWaveThread = new Thread() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
mode.init();
|
mode.init();
|
||||||
mProgressBar2.begin(mode.getProcessCount(), file.getName() + " saving...");
|
mProgressBar2.begin(mode.getProcessCount(), context.getFileName() + " saving...");
|
||||||
|
|
||||||
while (mode.process()) {
|
while (mode.process()) {
|
||||||
mProgressBar2.step();
|
mProgressBar2.step();
|
||||||
|
@ -135,7 +135,7 @@ class Encoder {
|
||||||
mode.finish(mQuit);
|
mode.finish(mQuit);
|
||||||
mProgressBar2.end();
|
mProgressBar2.end();
|
||||||
if (!mQuit)
|
if (!mQuit)
|
||||||
mMessenger.carrySaveAsWaveIsDoneMessage(file);
|
mMessenger.carrySaveAsWaveIsDoneMessage(context);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
mSaveWaveThread.start();
|
mSaveWaveThread.start();
|
||||||
|
|
|
@ -19,7 +19,6 @@ import android.Manifest;
|
||||||
import android.annotation.TargetApi;
|
import android.annotation.TargetApi;
|
||||||
import android.app.AlertDialog;
|
import android.app.AlertDialog;
|
||||||
import android.content.ContentResolver;
|
import android.content.ContentResolver;
|
||||||
import android.content.ContentValues;
|
|
||||||
import android.content.DialogInterface;
|
import android.content.DialogInterface;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.content.pm.PackageManager;
|
import android.content.pm.PackageManager;
|
||||||
|
@ -43,10 +42,10 @@ import android.widget.ProgressBar;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
|
||||||
import om.sstvencoder.ModeInterfaces.IModeInfo;
|
import om.sstvencoder.ModeInterfaces.IModeInfo;
|
||||||
|
import om.sstvencoder.Output.WaveFileOutputContext;
|
||||||
import om.sstvencoder.TextOverlay.Label;
|
import om.sstvencoder.TextOverlay.Label;
|
||||||
|
|
||||||
public class MainActivity extends AppCompatActivity {
|
public class MainActivity extends AppCompatActivity {
|
||||||
|
@ -191,7 +190,8 @@ public class MainActivity extends AppCompatActivity {
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean needsRequestWritePermission() {
|
private boolean needsRequestWritePermission() {
|
||||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M)
|
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M ||
|
||||||
|
Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q)
|
||||||
return false;
|
return false;
|
||||||
String permission = Manifest.permission.WRITE_EXTERNAL_STORAGE;
|
String permission = Manifest.permission.WRITE_EXTERNAL_STORAGE;
|
||||||
int state = ContextCompat.checkSelfPermission(this, permission);
|
int state = ContextCompat.checkSelfPermission(this, permission);
|
||||||
|
@ -443,18 +443,15 @@ public class MainActivity extends AppCompatActivity {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void save() {
|
private void save() {
|
||||||
File file = Utility.createWaveFilePath();
|
if (Utility.isExternalStorageWritable()) {
|
||||||
mEncoder.save(mCropView.getBitmap(), file);
|
WaveFileOutputContext context
|
||||||
|
= new WaveFileOutputContext(getContentResolver(), Utility.createWaveFileName());
|
||||||
|
mEncoder.save(mCropView.getBitmap(), context);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void completeSaving(File file) {
|
public void completeSaving(WaveFileOutputContext context) {
|
||||||
addFileToContentResolver(file);
|
context.clear();
|
||||||
}
|
|
||||||
|
|
||||||
private void addFileToContentResolver(File file) {
|
|
||||||
ContentValues values = Utility.getWavContentValues(file);
|
|
||||||
Uri uri = MediaStore.Audio.Media.getContentUriForPath(file.getAbsolutePath());
|
|
||||||
getContentResolver().insert(uri, values);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -17,7 +17,7 @@ package om.sstvencoder;
|
||||||
|
|
||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
|
|
||||||
import java.io.File;
|
import om.sstvencoder.Output.WaveFileOutputContext;
|
||||||
|
|
||||||
class MainActivityMessenger {
|
class MainActivityMessenger {
|
||||||
private final MainActivity mMainActivity;
|
private final MainActivity mMainActivity;
|
||||||
|
@ -28,11 +28,11 @@ class MainActivityMessenger {
|
||||||
mHandler = new Handler();
|
mHandler = new Handler();
|
||||||
}
|
}
|
||||||
|
|
||||||
void carrySaveAsWaveIsDoneMessage(final File file) {
|
void carrySaveAsWaveIsDoneMessage(final WaveFileOutputContext context) {
|
||||||
mHandler.post(new Runnable() {
|
mHandler.post(new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
mMainActivity.completeSaving(file);
|
mMainActivity.completeSaving(context);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,8 +15,6 @@ limitations under the License.
|
||||||
*/
|
*/
|
||||||
package om.sstvencoder.Output;
|
package om.sstvencoder.Output;
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
|
|
||||||
public final class OutputFactory {
|
public final class OutputFactory {
|
||||||
|
|
||||||
public static IOutput createOutputForSending() {
|
public static IOutput createOutputForSending() {
|
||||||
|
@ -24,11 +22,8 @@ public final class OutputFactory {
|
||||||
return new AudioOutput(sampleRate);
|
return new AudioOutput(sampleRate);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static IOutput createOutputForSavingAsWave(File filePath) {
|
public static IOutput createOutputForSavingAsWave(WaveFileOutputContext context) {
|
||||||
double sampleRate = 44100.0;
|
double sampleRate = 44100.0;
|
||||||
|
return new WaveFileOutput(context, sampleRate);
|
||||||
if (filePath == null)
|
|
||||||
return null;
|
|
||||||
return new WaveFileOutput(filePath, sampleRate);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,17 +16,15 @@ limitations under the License.
|
||||||
package om.sstvencoder.Output;
|
package om.sstvencoder.Output;
|
||||||
|
|
||||||
import java.io.BufferedOutputStream;
|
import java.io.BufferedOutputStream;
|
||||||
import java.io.File;
|
|
||||||
import java.io.FileOutputStream;
|
|
||||||
|
|
||||||
class WaveFileOutput implements IOutput {
|
class WaveFileOutput implements IOutput {
|
||||||
private final double mSampleRate;
|
private final double mSampleRate;
|
||||||
private File mFile;
|
private WaveFileOutputContext mContext;
|
||||||
private BufferedOutputStream mOutputStream;
|
private BufferedOutputStream mOutputStream;
|
||||||
private int mSamples, mWrittenSamples;
|
private int mSamples, mWrittenSamples;
|
||||||
|
|
||||||
WaveFileOutput(File file, double sampleRate) {
|
WaveFileOutput(WaveFileOutputContext context, double sampleRate) {
|
||||||
mFile = file;
|
mContext = context;
|
||||||
mSampleRate = sampleRate;
|
mSampleRate = sampleRate;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -67,7 +65,7 @@ class WaveFileOutput implements IOutput {
|
||||||
|
|
||||||
private void InitOutputStream() {
|
private void InitOutputStream() {
|
||||||
try {
|
try {
|
||||||
mOutputStream = new BufferedOutputStream(new FileOutputStream(mFile));
|
mOutputStream = new BufferedOutputStream(mContext.createWaveOutputStream());
|
||||||
} catch (Exception ignore) {
|
} catch (Exception ignore) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -98,11 +96,8 @@ class WaveFileOutput implements IOutput {
|
||||||
} catch (Exception ignore) {
|
} catch (Exception ignore) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mFile != null) {
|
|
||||||
if (cancel)
|
if (cancel)
|
||||||
mFile.delete();
|
mContext.deleteFile();
|
||||||
mFile = null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void padWithZeros(int count) {
|
private void padWithZeros(int count) {
|
||||||
|
|
|
@ -0,0 +1,101 @@
|
||||||
|
/*
|
||||||
|
Copyright 2020 Olga Miller <olga.rgb@gmail.com>
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
package om.sstvencoder.Output;
|
||||||
|
|
||||||
|
import android.content.ContentResolver;
|
||||||
|
import android.content.ContentValues;
|
||||||
|
import android.net.Uri;
|
||||||
|
import android.os.Build;
|
||||||
|
import android.os.Environment;
|
||||||
|
import android.provider.MediaStore;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileOutputStream;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
|
||||||
|
public class WaveFileOutputContext {
|
||||||
|
private ContentResolver mContentResolver;
|
||||||
|
private String mFileName;
|
||||||
|
private File mFile;
|
||||||
|
private Uri mUri;
|
||||||
|
private ContentValues mValues;
|
||||||
|
|
||||||
|
public WaveFileOutputContext(ContentResolver contentResolver, String fileName) {
|
||||||
|
mContentResolver = contentResolver;
|
||||||
|
mFileName = fileName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getFileName() {
|
||||||
|
return mFileName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public OutputStream createWaveOutputStream() {
|
||||||
|
if (init()) {
|
||||||
|
try {
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q)
|
||||||
|
return mContentResolver.openOutputStream(mUri);
|
||||||
|
else
|
||||||
|
return new FileOutputStream(mFile);
|
||||||
|
} catch (Exception ignore) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean init() {
|
||||||
|
String album = "SSTV Encoder";
|
||||||
|
mValues = new ContentValues();
|
||||||
|
mValues.put(MediaStore.Audio.Media.ALBUM, album);
|
||||||
|
mValues.put(MediaStore.Audio.Media.MIME_TYPE, "audio/wav");
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
||||||
|
mValues.put(MediaStore.Audio.Media.DISPLAY_NAME, mFileName);
|
||||||
|
mValues.put(MediaStore.Audio.Media.RELATIVE_PATH, (new File(Environment.DIRECTORY_MUSIC, album)).getPath());
|
||||||
|
mValues.put(MediaStore.Audio.Media.IS_PENDING, 1);
|
||||||
|
mUri = mContentResolver.insert(MediaStore.Audio.Media.getContentUri(MediaStore.VOLUME_EXTERNAL_PRIMARY), mValues);
|
||||||
|
if (mUri != null) {
|
||||||
|
String path = mUri.getPath();
|
||||||
|
if (path != null)
|
||||||
|
mFile = new File(path);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
mFile = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_MUSIC), mFileName);
|
||||||
|
mValues.put(MediaStore.Audio.Media.DATA, mFile.toString());
|
||||||
|
mValues.put(MediaStore.Audio.Media.TITLE, mFileName);
|
||||||
|
mValues.put(MediaStore.Audio.Media.IS_MUSIC, true);
|
||||||
|
mUri = MediaStore.Audio.Media.getContentUriForPath(mFile.getAbsolutePath());
|
||||||
|
if (mUri != null)
|
||||||
|
mContentResolver.insert(mUri, mValues);
|
||||||
|
}
|
||||||
|
return mUri != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void clear() {
|
||||||
|
if (mUri != null && mValues != null) {
|
||||||
|
mValues.clear();
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q)
|
||||||
|
mValues.put(MediaStore.Audio.Media.IS_PENDING, 0);
|
||||||
|
mContentResolver.update(mUri, mValues, null, null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void deleteFile() {
|
||||||
|
try {
|
||||||
|
if (mFile != null)
|
||||||
|
mFile.delete();
|
||||||
|
} catch (Exception ignore) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -15,7 +15,6 @@ limitations under the License.
|
||||||
*/
|
*/
|
||||||
package om.sstvencoder;
|
package om.sstvencoder;
|
||||||
|
|
||||||
import android.content.ContentValues;
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.graphics.Rect;
|
import android.graphics.Rect;
|
||||||
|
@ -23,7 +22,7 @@ import androidx.exifinterface.media.ExifInterface;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.os.Environment;
|
import android.os.Environment;
|
||||||
import android.provider.MediaStore;
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.core.content.FileProvider;
|
import androidx.core.content.FileProvider;
|
||||||
|
|
||||||
|
@ -89,18 +88,6 @@ final class Utility {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@NonNull
|
|
||||||
static ContentValues getWavContentValues(File file) {
|
|
||||||
ContentValues values = new ContentValues();
|
|
||||||
values.put(MediaStore.Audio.Media.ALBUM, "SSTV Encoder");
|
|
||||||
values.put(MediaStore.Audio.Media.ARTIST, "");
|
|
||||||
values.put(MediaStore.Audio.Media.DATA, file.toString());
|
|
||||||
values.put(MediaStore.Audio.Media.IS_MUSIC, true);
|
|
||||||
values.put(MediaStore.Audio.Media.MIME_TYPE, "audio/wav");
|
|
||||||
values.put(MediaStore.Audio.Media.TITLE, file.getName());
|
|
||||||
return values;
|
|
||||||
}
|
|
||||||
|
|
||||||
static Uri createImageUri(Context context) {
|
static Uri createImageUri(Context context) {
|
||||||
if (!isExternalStorageWritable())
|
if (!isExternalStorageWritable())
|
||||||
return null;
|
return null;
|
||||||
|
@ -114,18 +101,15 @@ final class Utility {
|
||||||
return FileProvider.getUriForFile(context, "om.sstvencoder", file); // content:// URI
|
return FileProvider.getUriForFile(context, "om.sstvencoder", file); // content:// URI
|
||||||
}
|
}
|
||||||
|
|
||||||
static File createWaveFilePath() {
|
static String createWaveFileName() {
|
||||||
if (!isExternalStorageWritable())
|
return createFileName() + ".wav";
|
||||||
return null;
|
|
||||||
File dir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_MUSIC);
|
|
||||||
return new File(dir, createFileName() + ".wav");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static String createFileName() {
|
private static String createFileName() {
|
||||||
return new SimpleDateFormat("yyyyMMdd_HHmmss", Locale.US).format(new Date());
|
return new SimpleDateFormat("yyyyMMdd_HHmmss", Locale.US).format(new Date());
|
||||||
}
|
}
|
||||||
|
|
||||||
private static boolean isExternalStorageWritable() {
|
static boolean isExternalStorageWritable() {
|
||||||
String state = Environment.getExternalStorageState();
|
String state = Environment.getExternalStorageState();
|
||||||
return Environment.MEDIA_MOUNTED.equals(state);
|
return Environment.MEDIA_MOUNTED.equals(state);
|
||||||
}
|
}
|
||||||
|
|
Ładowanie…
Reference in New Issue