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">
|
||||
|
||||
<uses-permission
|
||||
android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
|
||||
android:name="android.permission.WRITE_EXTERNAL_STORAGE"
|
||||
android:maxSdkVersion="28"/>
|
||||
<uses-feature
|
||||
android:name="android.hardware.camera"
|
||||
android:required="false"/>
|
||||
|
|
|
@ -17,7 +17,6 @@ package om.sstvencoder;
|
|||
|
||||
import android.graphics.Bitmap;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
|
@ -26,6 +25,7 @@ import om.sstvencoder.ModeInterfaces.IModeInfo;
|
|||
import om.sstvencoder.Modes.ModeFactory;
|
||||
import om.sstvencoder.Output.IOutput;
|
||||
import om.sstvencoder.Output.OutputFactory;
|
||||
import om.sstvencoder.Output.WaveFileOutputContext;
|
||||
|
||||
// Creates IMode instance
|
||||
class Encoder {
|
||||
|
@ -108,21 +108,21 @@ class Encoder {
|
|||
enqueue(mode);
|
||||
}
|
||||
|
||||
void save(Bitmap bitmap, File file) {
|
||||
void save(Bitmap bitmap, WaveFileOutputContext context) {
|
||||
if (mSaveWaveThread != null && mSaveWaveThread.isAlive())
|
||||
return;
|
||||
IOutput output = OutputFactory.createOutputForSavingAsWave(file);
|
||||
IOutput output = OutputFactory.createOutputForSavingAsWave(context);
|
||||
IMode mode = ModeFactory.CreateMode(mModeClass, bitmap, output);
|
||||
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() {
|
||||
@Override
|
||||
public void run() {
|
||||
mode.init();
|
||||
mProgressBar2.begin(mode.getProcessCount(), file.getName() + " saving...");
|
||||
mProgressBar2.begin(mode.getProcessCount(), context.getFileName() + " saving...");
|
||||
|
||||
while (mode.process()) {
|
||||
mProgressBar2.step();
|
||||
|
@ -135,7 +135,7 @@ class Encoder {
|
|||
mode.finish(mQuit);
|
||||
mProgressBar2.end();
|
||||
if (!mQuit)
|
||||
mMessenger.carrySaveAsWaveIsDoneMessage(file);
|
||||
mMessenger.carrySaveAsWaveIsDoneMessage(context);
|
||||
}
|
||||
};
|
||||
mSaveWaveThread.start();
|
||||
|
|
|
@ -19,7 +19,6 @@ import android.Manifest;
|
|||
import android.annotation.TargetApi;
|
||||
import android.app.AlertDialog;
|
||||
import android.content.ContentResolver;
|
||||
import android.content.ContentValues;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.PackageManager;
|
||||
|
@ -43,10 +42,10 @@ import android.widget.ProgressBar;
|
|||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.InputStream;
|
||||
|
||||
import om.sstvencoder.ModeInterfaces.IModeInfo;
|
||||
import om.sstvencoder.Output.WaveFileOutputContext;
|
||||
import om.sstvencoder.TextOverlay.Label;
|
||||
|
||||
public class MainActivity extends AppCompatActivity {
|
||||
|
@ -191,7 +190,8 @@ public class MainActivity extends AppCompatActivity {
|
|||
}
|
||||
|
||||
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;
|
||||
String permission = Manifest.permission.WRITE_EXTERNAL_STORAGE;
|
||||
int state = ContextCompat.checkSelfPermission(this, permission);
|
||||
|
@ -443,18 +443,15 @@ public class MainActivity extends AppCompatActivity {
|
|||
}
|
||||
|
||||
private void save() {
|
||||
File file = Utility.createWaveFilePath();
|
||||
mEncoder.save(mCropView.getBitmap(), file);
|
||||
if (Utility.isExternalStorageWritable()) {
|
||||
WaveFileOutputContext context
|
||||
= new WaveFileOutputContext(getContentResolver(), Utility.createWaveFileName());
|
||||
mEncoder.save(mCropView.getBitmap(), context);
|
||||
}
|
||||
}
|
||||
|
||||
public void completeSaving(File file) {
|
||||
addFileToContentResolver(file);
|
||||
}
|
||||
|
||||
private void addFileToContentResolver(File file) {
|
||||
ContentValues values = Utility.getWavContentValues(file);
|
||||
Uri uri = MediaStore.Audio.Media.getContentUriForPath(file.getAbsolutePath());
|
||||
getContentResolver().insert(uri, values);
|
||||
public void completeSaving(WaveFileOutputContext context) {
|
||||
context.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -17,7 +17,7 @@ package om.sstvencoder;
|
|||
|
||||
import android.os.Handler;
|
||||
|
||||
import java.io.File;
|
||||
import om.sstvencoder.Output.WaveFileOutputContext;
|
||||
|
||||
class MainActivityMessenger {
|
||||
private final MainActivity mMainActivity;
|
||||
|
@ -28,11 +28,11 @@ class MainActivityMessenger {
|
|||
mHandler = new Handler();
|
||||
}
|
||||
|
||||
void carrySaveAsWaveIsDoneMessage(final File file) {
|
||||
void carrySaveAsWaveIsDoneMessage(final WaveFileOutputContext context) {
|
||||
mHandler.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
mMainActivity.completeSaving(file);
|
||||
mMainActivity.completeSaving(context);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
@ -15,8 +15,6 @@ limitations under the License.
|
|||
*/
|
||||
package om.sstvencoder.Output;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
public final class OutputFactory {
|
||||
|
||||
public static IOutput createOutputForSending() {
|
||||
|
@ -24,11 +22,8 @@ public final class OutputFactory {
|
|||
return new AudioOutput(sampleRate);
|
||||
}
|
||||
|
||||
public static IOutput createOutputForSavingAsWave(File filePath) {
|
||||
public static IOutput createOutputForSavingAsWave(WaveFileOutputContext context) {
|
||||
double sampleRate = 44100.0;
|
||||
|
||||
if (filePath == null)
|
||||
return null;
|
||||
return new WaveFileOutput(filePath, sampleRate);
|
||||
return new WaveFileOutput(context, sampleRate);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,17 +16,15 @@ limitations under the License.
|
|||
package om.sstvencoder.Output;
|
||||
|
||||
import java.io.BufferedOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
|
||||
class WaveFileOutput implements IOutput {
|
||||
private final double mSampleRate;
|
||||
private File mFile;
|
||||
private WaveFileOutputContext mContext;
|
||||
private BufferedOutputStream mOutputStream;
|
||||
private int mSamples, mWrittenSamples;
|
||||
|
||||
WaveFileOutput(File file, double sampleRate) {
|
||||
mFile = file;
|
||||
WaveFileOutput(WaveFileOutputContext context, double sampleRate) {
|
||||
mContext = context;
|
||||
mSampleRate = sampleRate;
|
||||
}
|
||||
|
||||
|
@ -67,7 +65,7 @@ class WaveFileOutput implements IOutput {
|
|||
|
||||
private void InitOutputStream() {
|
||||
try {
|
||||
mOutputStream = new BufferedOutputStream(new FileOutputStream(mFile));
|
||||
mOutputStream = new BufferedOutputStream(mContext.createWaveOutputStream());
|
||||
} catch (Exception ignore) {
|
||||
}
|
||||
}
|
||||
|
@ -98,11 +96,8 @@ class WaveFileOutput implements IOutput {
|
|||
} catch (Exception ignore) {
|
||||
}
|
||||
|
||||
if (mFile != null) {
|
||||
if (cancel)
|
||||
mFile.delete();
|
||||
mFile = null;
|
||||
}
|
||||
if (cancel)
|
||||
mContext.deleteFile();
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
import android.content.ContentValues;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.graphics.Rect;
|
||||
|
@ -23,7 +22,7 @@ import androidx.exifinterface.media.ExifInterface;
|
|||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.os.Environment;
|
||||
import android.provider.MediaStore;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.core.content.FileProvider;
|
||||
|
||||
|
@ -89,18 +88,6 @@ final class Utility {
|
|||
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) {
|
||||
if (!isExternalStorageWritable())
|
||||
return null;
|
||||
|
@ -114,18 +101,15 @@ final class Utility {
|
|||
return FileProvider.getUriForFile(context, "om.sstvencoder", file); // content:// URI
|
||||
}
|
||||
|
||||
static File createWaveFilePath() {
|
||||
if (!isExternalStorageWritable())
|
||||
return null;
|
||||
File dir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_MUSIC);
|
||||
return new File(dir, createFileName() + ".wav");
|
||||
static String createWaveFileName() {
|
||||
return createFileName() + ".wav";
|
||||
}
|
||||
|
||||
private static String createFileName() {
|
||||
return new SimpleDateFormat("yyyyMMdd_HHmmss", Locale.US).format(new Date());
|
||||
}
|
||||
|
||||
private static boolean isExternalStorageWritable() {
|
||||
static boolean isExternalStorageWritable() {
|
||||
String state = Environment.getExternalStorageState();
|
||||
return Environment.MEDIA_MOUNTED.equals(state);
|
||||
}
|
||||
|
|
Ładowanie…
Reference in New Issue