mirror of
https://github.com/olgamiller/SSTVEncoder2.git
synced 2025-12-06 06:52:01 +01:00
Compare commits
No commits in common. "master" and "Version2.7" have entirely different histories.
master
...
Version2.7
162
README.md
162
README.md
|
|
@ -1,75 +1,151 @@
|
||||||

|
|
||||||
# SSTV Encoder 2
|
# SSTV Encoder 2
|
||||||
|
|
||||||
Image encoder for Slow-Scan Television (SSTV) audio signals
|
### Short Description
|
||||||
|
|
||||||
### Modes
|
This app sends images via Slow Scan Television (SSTV).
|
||||||
|
|
||||||
Supported SSTV modes:
|
### Supported Modes
|
||||||
* **Martin Modes**: Martin 1, Martin 2
|
|
||||||
* **PD Modes**: PD 50, PD 90, PD 120, PD 160, PD 180, PD 240, PD 290
|
|
||||||
* **Robot Modes**: Robot 36 Color, Robot 72 Color
|
|
||||||
* **Scottie Modes**: Scottie 1, Scottie 2, Scottie DX
|
|
||||||
* **Wraase Modes**: Wraase SC2 180
|
|
||||||
|
|
||||||
The mode specifications are taken from the Dayton Paper, JL Barber, "Proposal for SSTV Mode Specifications", 2000:
|
**Martin Modes:** Martin 1, Martin 2
|
||||||
|
**PD Modes:** PD 50, PD 90, PD 120, PD 160, PD 180, PD 240, PD 290
|
||||||
|
**Scottie Modes:** Scottie 1, Scottie 2, Scottie DX
|
||||||
|
**Robot Modes:** Robot 36 Color, Robot 72 Color
|
||||||
|
**Wraase Modes:** Wraase SC2 180
|
||||||
|
|
||||||
|
The mode specifications are taken from the Dayton Paper of JL Barber:
|
||||||
http://www.barberdsp.com/downloads/Dayton%20Paper.pdf
|
http://www.barberdsp.com/downloads/Dayton%20Paper.pdf
|
||||||
|
|
||||||
### Image
|
### Image
|
||||||
|
|
||||||
To load an image:
|
Tap "Take Picture" or "Pick Picture" menu button or
|
||||||
* tap **"Take Picture"** or **"Pick Picture"** menu button, or
|
use the Share option of any app like Gallery to load an image.
|
||||||
* use the **Share** option of an app like e.g. Gallery.
|
|
||||||
|
|
||||||
To keep the aspect ratio, black borders will be added if necessary.
|
To keep the aspect ratio, black borders will be added if necessary.
|
||||||
Original image can be resend using another mode without reloading.
|
Original image can be resend using another mode without reloading.
|
||||||
After image rotation or mode changing the image will be scaled to that mode's native size.
|
|
||||||
|
After image rotation or mode changing the image
|
||||||
|
will be scaled to that mode's native size.
|
||||||
|
|
||||||
After closing the app the loaded image will not be stored.
|
After closing the app the loaded image will not be stored.
|
||||||
|
|
||||||
### Text Overlay
|
### Text Overlay
|
||||||
|
|
||||||
Actions for working with text overlays:
|
Single tap **to add** a text overlay.
|
||||||
* Single tap **to add** a text overlay.
|
Single tap on text overlay **to edit** it.
|
||||||
* Single tap on text overlay **to edit** it.
|
Long press **to move** text overlay.
|
||||||
* Long press **to move** text overlay.
|
Remove the text **to remove** a text overlay.
|
||||||
* Remove the text **to remove** a text overlay.
|
|
||||||
|
|
||||||
After closing the app all text overlays will be stored and reloaded when restarting.
|
After closing the app all text overlays
|
||||||
|
will be stored and reloaded when restarting.
|
||||||
|
|
||||||
### Menu
|
### Menu
|
||||||
|
|
||||||
Available menu options:
|
* "Pick Picture"
|
||||||
* **"Play"**: Sends the image
|
- Opens an image viewer app to select a picture.
|
||||||
* **"Stop"**: Stops the current sending and empties the queue
|
* "Take Picture"
|
||||||
* **"Pick Picture"**: Opens an image viewer app to select a picture
|
- Starts a camera app to take a picture.
|
||||||
* **"Take Picture"**: Starts a camera app to take a picture
|
* "Save Wave File"
|
||||||
* **"Save as WAVE File"**: Creates a wave file in the Music folder in SSTV Encoder album
|
- Creates a wave file in the Music folder in SSTV Encoder album.
|
||||||
* **"Transform Image"**:
|
* "Play"
|
||||||
* **"Rotate"**: Rotates the image by 90 degrees
|
- Sends the image.
|
||||||
* **"Reset"**: Resets image rotation and scaling
|
* "Stop"
|
||||||
* **"Modes"**: Lists all supported modes
|
- Stops the current sending and empties the queue.
|
||||||
|
* "Rotate Image"
|
||||||
|
- Rotates the image by 90 degrees.
|
||||||
|
* "Modes"
|
||||||
|
- Lists all supported modes.
|
||||||
|
|
||||||
### Installation
|
### Google Play
|
||||||
|
|
||||||
The working app "SSTV Encoder" can be installed
|
On Google Play or F-Droid the Working App:
|
||||||
|
SSTV Encoder
|
||||||
on Google Play:
|
|
||||||
https://play.google.com/store/apps/details?id=om.sstvencoder
|
https://play.google.com/store/apps/details?id=om.sstvencoder
|
||||||
|
|
||||||
or on F-Droid:
|
|
||||||
https://f-droid.org/packages/om.sstvencoder/
|
https://f-droid.org/packages/om.sstvencoder/
|
||||||
|
|
||||||
# SSTV Image Decoder
|
### SSTV Image Decoder
|
||||||
|
|
||||||
Open Source Code:
|
Open Source Code:
|
||||||
https://github.com/xdsopl/robot36/tree/android
|
https://github.com/xdsopl/robot36/tree/android
|
||||||
|
|
||||||
### Installation
|
On Google Play or F-Droid the Working App:
|
||||||
|
Robot36 - SSTV Image Decoder
|
||||||
The working app "Robot36 - SSTV Image Decoder" can be installed
|
https://play.google.com/store/apps/details?id=xdsopl.robot36
|
||||||
|
|
||||||
on Google Play:
|
https://f-droid.org/packages/xdsopl.robot36/
|
||||||
|
|
||||||
|
##### __________________ DEUTSCH __________________
|
||||||
|
|
||||||
|
# SSTV-Kodierer 2
|
||||||
|
|
||||||
|
### Kurzbeschreibung
|
||||||
|
|
||||||
|
Diese Applikation sendet Bilder via Slow Scan Television (SSTV).
|
||||||
|
|
||||||
|
### Unterstützte Modi
|
||||||
|
|
||||||
|
**Martin Modi:** Martin 1, Martin 2
|
||||||
|
**PD Modi:** PD 50, PD 90, PD 120, PD 160, PD 180, PD 240, PD 290
|
||||||
|
**Scottie Modi:** Scottie 1, Scottie 2, Scottie DX
|
||||||
|
**Robot Modi:** Robot 36 Color, Robot 72 Color
|
||||||
|
**Wraase Modi:** Wraase SC2 180
|
||||||
|
|
||||||
|
Modi-Spezifikation:
|
||||||
|
Dayton Paper of JL Barber
|
||||||
|
http://www.barberdsp.com/downloads/Dayton%20Paper.pdf
|
||||||
|
|
||||||
|
### Bild
|
||||||
|
|
||||||
|
Bilder können durch das Berühren des "Take Picture" oder "Pick Picture" Menü-Knopfes oder
|
||||||
|
durch die "Teilen" (oder "Bild Teilen", "Share") Option anderer Applikationen wie "Galerie" geladen.
|
||||||
|
|
||||||
|
Um das Seitenverhältnis beizubehalten, werden (falls notwendig) schwarze Ränder hinzugefügt.
|
||||||
|
Das Originalbild kann in einem anderen Modus neu gesendet werden ohne das Bild neu laden zu müssen.
|
||||||
|
|
||||||
|
Nach dem Rotieren des Bildes oder nach dem Auswählen eines Übertragungsmodus
|
||||||
|
wird das Bild auf die native Größe skaliert.
|
||||||
|
|
||||||
|
Nach dem Schließen der Applikation wird das Bild nicht gespeichert.
|
||||||
|
|
||||||
|
### Text
|
||||||
|
|
||||||
|
Tippen der Bildfläche startet den Text-Editor.
|
||||||
|
Bewegen des Textes wird durch langes Berühren initiiert.
|
||||||
|
|
||||||
|
Nach dem Schließen der Applikation alle Texte werden gespeichert und wieder geladen beim Neustart.
|
||||||
|
|
||||||
|
### Menu
|
||||||
|
|
||||||
|
* "Pick Picture"
|
||||||
|
- Öffnet eine Applikation wie "Galerie" um ein Bild auszuwählen.
|
||||||
|
* "Take Picture"
|
||||||
|
- Startet eine Kamera-Applikation um ein Bild aufzunehmen.
|
||||||
|
* "Save Wave File"
|
||||||
|
- Erstellt eine Wave-Datei in "Musik" Ordner in "SSTV Encoder"-Album.
|
||||||
|
* "Play"
|
||||||
|
- Sendet das Bild.
|
||||||
|
* "Stop"
|
||||||
|
- Stoppt das aktuelle Senden und leert die Warteschlange.
|
||||||
|
* "Rotate Image"
|
||||||
|
- Rotiert das Bild um 90 Grad.
|
||||||
|
* "Modes"
|
||||||
|
- Listet alle unterstützten Modi.
|
||||||
|
|
||||||
|
### Google Play
|
||||||
|
|
||||||
|
Auf Google Play oder F-Droid die funktionierende Applikation:
|
||||||
|
SSTV Encoder
|
||||||
|
https://play.google.com/store/apps/details?id=om.sstvencoder
|
||||||
|
|
||||||
|
https://f-droid.org/packages/om.sstvencoder/
|
||||||
|
|
||||||
|
### SSTV-Dekodierer
|
||||||
|
|
||||||
|
Quelltext:
|
||||||
|
https://github.com/xdsopl/robot36/tree/android
|
||||||
|
|
||||||
|
Auf Google Play oder F-Droid die funktionierende Applikation:
|
||||||
|
Robot36 - SSTV Bild Dekodierer
|
||||||
https://play.google.com/store/apps/details?id=xdsopl.robot36
|
https://play.google.com/store/apps/details?id=xdsopl.robot36
|
||||||
|
|
||||||
or on F-Droid:
|
|
||||||
https://f-droid.org/packages/xdsopl.robot36/
|
https://f-droid.org/packages/xdsopl.robot36/
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,13 @@
|
||||||
apply plugin: 'com.android.application'
|
apply plugin: 'com.android.application'
|
||||||
|
|
||||||
android {
|
android {
|
||||||
compileSdk 35
|
compileSdkVersion 32
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
applicationId "om.sstvencoder"
|
applicationId "om.sstvencoder"
|
||||||
minSdk 21
|
minSdkVersion 21
|
||||||
targetSdk 35
|
targetSdkVersion 32
|
||||||
versionCode 34
|
versionCode 28
|
||||||
versionName "2.13"
|
versionName "2.7"
|
||||||
}
|
}
|
||||||
buildTypes {
|
buildTypes {
|
||||||
release {
|
release {
|
||||||
|
|
@ -15,14 +15,10 @@ android {
|
||||||
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
|
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
namespace 'om.sstvencoder'
|
|
||||||
buildFeatures {
|
|
||||||
buildConfig true
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation fileTree(dir: 'libs', include: ['*.jar'])
|
implementation fileTree(dir: 'libs', include: ['*.jar'])
|
||||||
implementation 'androidx.appcompat:appcompat:1.7.0'
|
implementation 'androidx.appcompat:appcompat:1.5.1'
|
||||||
implementation "androidx.exifinterface:exifinterface:1.3.7"
|
implementation "androidx.exifinterface:exifinterface:1.3.5"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
package="om.sstvencoder">
|
||||||
|
|
||||||
<uses-permission
|
<uses-permission
|
||||||
android:name="android.permission.WRITE_EXTERNAL_STORAGE"
|
android:name="android.permission.WRITE_EXTERNAL_STORAGE"
|
||||||
|
|
@ -15,15 +16,6 @@
|
||||||
<intent>
|
<intent>
|
||||||
<action android:name="android.media.action.IMAGE_CAPTURE"/>
|
<action android:name="android.media.action.IMAGE_CAPTURE"/>
|
||||||
</intent>
|
</intent>
|
||||||
<intent>
|
|
||||||
<action android:name="android.intent.action.PICK" />
|
|
||||||
<data android:mimeType="image/*"/>
|
|
||||||
</intent>
|
|
||||||
<intent>
|
|
||||||
<action android:name="android.intent.action.VIEW" />
|
|
||||||
<category android:name="android.intent.category.BROWSABLE" />
|
|
||||||
<data android:scheme="https" />
|
|
||||||
</intent>
|
|
||||||
</queries>
|
</queries>
|
||||||
<application
|
<application
|
||||||
android:requestLegacyExternalStorage="true"
|
android:requestLegacyExternalStorage="true"
|
||||||
|
|
|
||||||
|
|
@ -175,19 +175,6 @@ public class CropView extends AppCompatImageView {
|
||||||
invalidate();
|
invalidate();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void resetImage() {
|
|
||||||
if (!mImageOK)
|
|
||||||
return;
|
|
||||||
if (mOrientation == 90 || mOrientation == 270) {
|
|
||||||
int tmp = mImageWidth;
|
|
||||||
mImageWidth = mImageHeight;
|
|
||||||
mImageHeight = tmp;
|
|
||||||
}
|
|
||||||
mOrientation = 0;
|
|
||||||
resetInputRect();
|
|
||||||
invalidate();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setNoBitmap() {
|
public void setNoBitmap() {
|
||||||
mImageOK = false;
|
mImageOK = false;
|
||||||
mOrientation = 0;
|
mOrientation = 0;
|
||||||
|
|
@ -195,7 +182,7 @@ public class CropView extends AppCompatImageView {
|
||||||
invalidate();
|
invalidate();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setBitmap(@NonNull InputStream stream) throws Exception {
|
public void setBitmap(@NonNull InputStream stream) throws IOException, IllegalArgumentException {
|
||||||
mImageOK = false;
|
mImageOK = false;
|
||||||
mOrientation = 0;
|
mOrientation = 0;
|
||||||
recycle();
|
recycle();
|
||||||
|
|
@ -203,48 +190,35 @@ public class CropView extends AppCompatImageView {
|
||||||
invalidate();
|
invalidate();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void loadImage(InputStream stream) throws Exception {
|
private void loadImage(InputStream stream) throws IOException, IllegalArgumentException {
|
||||||
|
// app6 + exif
|
||||||
|
int bufferBytes = 1048576;
|
||||||
|
if (!stream.markSupported())
|
||||||
|
stream = new BufferedInputStream(stream, bufferBytes);
|
||||||
|
stream.mark(bufferBytes);
|
||||||
BitmapFactory.Options options = new BitmapFactory.Options();
|
BitmapFactory.Options options = new BitmapFactory.Options();
|
||||||
options.inJustDecodeBounds = true;
|
options.inJustDecodeBounds = true;
|
||||||
byte[] streamBytes = null;
|
BitmapFactory.decodeStream(new BufferedInputStream(stream), null, options);
|
||||||
String errorMessage = null;
|
stream.reset();
|
||||||
try {
|
|
||||||
int length = stream.available();
|
|
||||||
if (length > 0) {
|
|
||||||
streamBytes = new byte[length];
|
|
||||||
if (length == stream.read(streamBytes, 0, streamBytes.length)) {
|
|
||||||
BitmapFactory.decodeByteArray(streamBytes, 0, streamBytes.length, options);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
streamBytes = null;
|
|
||||||
}
|
|
||||||
} catch (Exception ex) {
|
|
||||||
errorMessage = ex.getMessage();
|
|
||||||
streamBytes = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
mImageWidth = options.outWidth;
|
mImageWidth = options.outWidth;
|
||||||
mImageHeight = options.outHeight;
|
mImageHeight = options.outHeight;
|
||||||
|
|
||||||
if (streamBytes != null && mImageWidth > 0 && mImageHeight > 0) {
|
if (mImageWidth * mImageHeight < 1024 * 1024) {
|
||||||
mSmallImage = mImageWidth * mImageHeight < 1024 * 1024;
|
mCacheBitmap = BitmapFactory.decodeStream(stream);
|
||||||
if (mSmallImage) {
|
mSmallImage = true;
|
||||||
mCacheBitmap = BitmapFactory.decodeByteArray(streamBytes, 0, streamBytes.length, null);
|
} else {
|
||||||
} else {
|
mRegionDecoder = BitmapRegionDecoder.newInstance(stream, true);
|
||||||
mRegionDecoder = BitmapRegionDecoder.newInstance(streamBytes, 0, streamBytes.length, true);
|
mCacheRect.setEmpty();
|
||||||
mCacheRect.setEmpty();
|
mSmallImage = false;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mCacheBitmap == null && mRegionDecoder == null) {
|
if (mCacheBitmap == null && mRegionDecoder == null) {
|
||||||
String message = errorMessage;
|
String size = options.outWidth + "x" + options.outHeight;
|
||||||
if (message == null) {
|
String message = "Stream could not be decoded. Image size: " + size;
|
||||||
message = "Stream could not be decoded.";
|
if (mImageWidth <= 0 || mImageHeight <= 0)
|
||||||
if (mImageWidth > 0 && mImageHeight > 0) {
|
throw new IllegalArgumentException(message);
|
||||||
message += " Image size: " + mImageWidth + "x" + mImageHeight;
|
else
|
||||||
}
|
throw new IOException(message);
|
||||||
}
|
|
||||||
throw new Exception(message);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
mImageOK = true;
|
mImageOK = true;
|
||||||
|
|
|
||||||
|
|
@ -18,12 +18,10 @@ package om.sstvencoder;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.graphics.Color;
|
import android.graphics.Color;
|
||||||
import android.graphics.drawable.Drawable;
|
import android.graphics.drawable.Drawable;
|
||||||
|
|
||||||
import androidx.annotation.ColorInt;
|
import androidx.annotation.ColorInt;
|
||||||
import androidx.fragment.app.DialogFragment;
|
import androidx.fragment.app.DialogFragment;
|
||||||
import androidx.core.content.ContextCompat;
|
import androidx.core.content.ContextCompat;
|
||||||
import androidx.appcompat.app.AppCompatActivity;
|
import androidx.appcompat.app.AppCompatActivity;
|
||||||
|
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.text.Editable;
|
import android.text.Editable;
|
||||||
import android.text.TextWatcher;
|
import android.text.TextWatcher;
|
||||||
|
|
@ -54,6 +52,8 @@ public class EditTextActivity extends AppCompatActivity
|
||||||
public static final String EXTRA = "EDIT_TEXT_EXTRA";
|
public static final String EXTRA = "EDIT_TEXT_EXTRA";
|
||||||
private Label mLabel;
|
private Label mLabel;
|
||||||
private EditColorMode mEditColor;
|
private EditColorMode mEditColor;
|
||||||
|
private FontFamilySet mFontFamilySet;
|
||||||
|
private FontFamilySet.FontFamily mSelectedFontFamily;
|
||||||
private List<String> mFontFamilyNameList;
|
private List<String> mFontFamilyNameList;
|
||||||
private CheckBox mEditItalic, mEditBold, mEditOutline;
|
private CheckBox mEditItalic, mEditBold, mEditOutline;
|
||||||
private int mClearTextButtonWidth;
|
private int mClearTextButtonWidth;
|
||||||
|
|
@ -77,6 +77,7 @@ public class EditTextActivity extends AppCompatActivity
|
||||||
mEditBold.setChecked(mLabel.getBold());
|
mEditBold.setChecked(mLabel.getBold());
|
||||||
mEditItalic.setChecked(mLabel.getItalic());
|
mEditItalic.setChecked(mLabel.getItalic());
|
||||||
initFontFamilySpinner(mLabel.getFamilyName());
|
initFontFamilySpinner(mLabel.getFamilyName());
|
||||||
|
updateBoldAndItalic();
|
||||||
mEditOutline.setChecked(mLabel.getOutline());
|
mEditOutline.setChecked(mLabel.getOutline());
|
||||||
initOutlineSizeSpinner(mLabel.getOutlineSize());
|
initOutlineSizeSpinner(mLabel.getOutlineSize());
|
||||||
findViewById(R.id.edit_color).setBackgroundColor(mLabel.getForeColor());
|
findViewById(R.id.edit_color).setBackgroundColor(mLabel.getForeColor());
|
||||||
|
|
@ -147,22 +148,18 @@ public class EditTextActivity extends AppCompatActivity
|
||||||
private void initFontFamilySpinner(String familyName) {
|
private void initFontFamilySpinner(String familyName) {
|
||||||
Spinner spinner = findViewById(R.id.edit_font_family);
|
Spinner spinner = findViewById(R.id.edit_font_family);
|
||||||
spinner.setOnItemSelectedListener(this);
|
spinner.setOnItemSelectedListener(this);
|
||||||
mFontFamilyNameList = Utility.getSystemFontFamilyList();
|
mFontFamilySet = new FontFamilySet();
|
||||||
|
mSelectedFontFamily = mFontFamilySet.getFontFamily(familyName);
|
||||||
|
mFontFamilyNameList = mFontFamilySet.getFontFamilyDisplayNameList();
|
||||||
spinner.setAdapter(new ArrayAdapter<>(this,
|
spinner.setAdapter(new ArrayAdapter<>(this,
|
||||||
android.R.layout.simple_spinner_dropdown_item, mFontFamilyNameList));
|
android.R.layout.simple_spinner_dropdown_item, mFontFamilyNameList));
|
||||||
spinner.setSelection(mFontFamilyNameList.indexOf(familyName));
|
spinner.setSelection(mFontFamilyNameList.indexOf(mSelectedFontFamily.displayName));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void initTextSizeSpinner(float textSize) {
|
private void initTextSizeSpinner(float textSize) {
|
||||||
Spinner spinner = findViewById(R.id.edit_text_size);
|
Spinner spinner = findViewById(R.id.edit_text_size);
|
||||||
spinner.setOnItemSelectedListener(this);
|
spinner.setOnItemSelectedListener(this);
|
||||||
String[] sizeList = new String[]
|
String[] sizeList = new String[]{"Small", "Normal", "Large", "Huge"};
|
||||||
{
|
|
||||||
getString(R.string.font_size_small),
|
|
||||||
getString(R.string.font_size_normal),
|
|
||||||
getString(R.string.font_size_large),
|
|
||||||
getString(R.string.font_size_huge)
|
|
||||||
};
|
|
||||||
spinner.setAdapter(new ArrayAdapter<>(this,
|
spinner.setAdapter(new ArrayAdapter<>(this,
|
||||||
android.R.layout.simple_spinner_dropdown_item, sizeList));
|
android.R.layout.simple_spinner_dropdown_item, sizeList));
|
||||||
spinner.setSelection(textSizeToPosition(textSize));
|
spinner.setSelection(textSizeToPosition(textSize));
|
||||||
|
|
@ -171,12 +168,7 @@ public class EditTextActivity extends AppCompatActivity
|
||||||
private void initOutlineSizeSpinner(float outlineSize) {
|
private void initOutlineSizeSpinner(float outlineSize) {
|
||||||
Spinner spinner = findViewById(R.id.edit_outline_size);
|
Spinner spinner = findViewById(R.id.edit_outline_size);
|
||||||
spinner.setOnItemSelectedListener(this);
|
spinner.setOnItemSelectedListener(this);
|
||||||
String[] sizeList = new String[]
|
String[] sizeList = new String[]{"Thin", "Normal", "Thick"};
|
||||||
{
|
|
||||||
getString(R.string.outline_size_thin),
|
|
||||||
getString(R.string.outline_size_normal),
|
|
||||||
getString(R.string.outline_size_thick)
|
|
||||||
};
|
|
||||||
spinner.setAdapter(new ArrayAdapter<>(this,
|
spinner.setAdapter(new ArrayAdapter<>(this,
|
||||||
android.R.layout.simple_spinner_dropdown_item, sizeList));
|
android.R.layout.simple_spinner_dropdown_item, sizeList));
|
||||||
spinner.setSelection(outlineSizeToPosition(outlineSize));
|
spinner.setSelection(outlineSizeToPosition(outlineSize));
|
||||||
|
|
@ -216,7 +208,28 @@ public class EditTextActivity extends AppCompatActivity
|
||||||
mLabel.setOutlineSize(positionToOutlineSize(position));
|
mLabel.setOutlineSize(positionToOutlineSize(position));
|
||||||
}
|
}
|
||||||
else if (parentId == R.id.edit_font_family) {
|
else if (parentId == R.id.edit_font_family) {
|
||||||
mLabel.setFamilyName(mFontFamilyNameList.get(position));
|
String displayName = mFontFamilyNameList.get(position);
|
||||||
|
mSelectedFontFamily = mFontFamilySet.getFontFamilyFromDisplayName(displayName);
|
||||||
|
mLabel.setFamilyName(mSelectedFontFamily.name);
|
||||||
|
updateBoldAndItalic();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateBoldAndItalic() {
|
||||||
|
boolean bold = mSelectedFontFamily.bold;
|
||||||
|
mEditBold.setEnabled(bold);
|
||||||
|
findViewById(R.id.text_bold).setEnabled(bold);
|
||||||
|
if (!mEditBold.isEnabled()) {
|
||||||
|
mEditBold.setChecked(false);
|
||||||
|
mLabel.setBold(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean italic = mSelectedFontFamily.italic;
|
||||||
|
mEditItalic.setEnabled(italic);
|
||||||
|
findViewById(R.id.text_italic).setEnabled(italic);
|
||||||
|
if (!mEditItalic.isEnabled()) {
|
||||||
|
mEditItalic.setChecked(false);
|
||||||
|
mLabel.setItalic(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -66,8 +66,7 @@ class Encoder {
|
||||||
mode = mQueue.remove(0);
|
mode = mQueue.remove(0);
|
||||||
}
|
}
|
||||||
mode.init();
|
mode.init();
|
||||||
mProgressBar.begin(mode.getProcessCount(),
|
mProgressBar.begin(mode.getProcessCount(), "Sending...");
|
||||||
mMessenger.getString(R.string.progressbar_message_sending));
|
|
||||||
|
|
||||||
while (mode.process()) {
|
while (mode.process()) {
|
||||||
mProgressBar.step();
|
mProgressBar.step();
|
||||||
|
|
@ -123,8 +122,7 @@ class Encoder {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
mode.init();
|
mode.init();
|
||||||
mProgressBar2.begin(mode.getProcessCount(),
|
mProgressBar2.begin(mode.getProcessCount(), context.getFileName() + " saving...");
|
||||||
mMessenger.getString(R.string.progressbar_message_saving_to_file, context.getFileName()));
|
|
||||||
|
|
||||||
while (mode.process()) {
|
while (mode.process()) {
|
||||||
mProgressBar2.step();
|
mProgressBar2.step();
|
||||||
|
|
|
||||||
|
|
@ -16,8 +16,6 @@ limitations under the License.
|
||||||
package om.sstvencoder;
|
package om.sstvencoder;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
import android.util.Xml;
|
import android.util.Xml;
|
||||||
|
|
||||||
import org.xmlpull.v1.XmlPullParser;
|
import org.xmlpull.v1.XmlPullParser;
|
||||||
|
|
@ -39,10 +37,8 @@ class FontFamilySet {
|
||||||
}
|
}
|
||||||
|
|
||||||
private final List<FontFamily> mFamilySet;
|
private final List<FontFamily> mFamilySet;
|
||||||
private final Context mContext;
|
|
||||||
|
|
||||||
FontFamilySet(Context context) {
|
FontFamilySet() {
|
||||||
mContext = context;
|
|
||||||
mFamilySet = new ArrayList<>();
|
mFamilySet = new ArrayList<>();
|
||||||
fillWithSystemFonts(mFamilySet);
|
fillWithSystemFonts(mFamilySet);
|
||||||
if (mFamilySet.size() == 0)
|
if (mFamilySet.size() == 0)
|
||||||
|
|
@ -53,7 +49,7 @@ class FontFamilySet {
|
||||||
private FontFamily getDefaultFontFamily() {
|
private FontFamily getDefaultFontFamily() {
|
||||||
FontFamily defaultFontFamily = new FontFamily();
|
FontFamily defaultFontFamily = new FontFamily();
|
||||||
defaultFontFamily.name = null;
|
defaultFontFamily.name = null;
|
||||||
defaultFontFamily.displayName = mContext.getString(R.string.font_default);
|
defaultFontFamily.displayName = "Default";
|
||||||
defaultFontFamily.bold = true;
|
defaultFontFamily.bold = true;
|
||||||
defaultFontFamily.italic = true;
|
defaultFontFamily.italic = true;
|
||||||
return defaultFontFamily;
|
return defaultFontFamily;
|
||||||
|
|
|
||||||
|
|
@ -119,30 +119,41 @@ public class MainActivity extends AppCompatActivity {
|
||||||
|
|
||||||
// Set verbose to false for any Uri that might have expired (e.g. shared from browser).
|
// Set verbose to false for any Uri that might have expired (e.g. shared from browser).
|
||||||
private boolean loadImage(Uri uri, boolean verbose) {
|
private boolean loadImage(Uri uri, boolean verbose) {
|
||||||
boolean succeeded = false;
|
|
||||||
ContentResolver resolver = getContentResolver();
|
ContentResolver resolver = getContentResolver();
|
||||||
|
InputStream stream = null;
|
||||||
if (uri != null) {
|
if (uri != null) {
|
||||||
|
mSettings.setImageUri(uri);
|
||||||
try {
|
try {
|
||||||
InputStream stream = resolver.openInputStream(uri);
|
stream = resolver.openInputStream(uri);
|
||||||
if (stream != null)
|
|
||||||
mCropView.setBitmap(stream);
|
|
||||||
succeeded = true;
|
|
||||||
} catch (Exception ex) { // e.g. FileNotFoundException, SecurityException
|
} catch (Exception ex) { // e.g. FileNotFoundException, SecurityException
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && isPermissionException(ex)
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && isPermissionException(ex)
|
||||||
&& needsRequestReadPermission()) {
|
&& needsRequestReadPermission()) {
|
||||||
requestReadPermission(REQUEST_LOAD_IMAGE_PERMISSION);
|
requestReadPermission(REQUEST_LOAD_IMAGE_PERMISSION);
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
else
|
showFileNotLoadedMessage(ex, verbose);
|
||||||
showFileNotLoadedMessage(ex, verbose);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (succeeded) {
|
if (stream == null || !loadImage(stream, resolver, uri)) {
|
||||||
mCropView.rotateImage(getOrientation(resolver, uri));
|
|
||||||
mSettings.setImageUri(uri);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
setDefaultBitmap();
|
setDefaultBitmap();
|
||||||
return succeeded;
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean loadImage(InputStream stream, ContentResolver resolver, Uri uri) {
|
||||||
|
try {
|
||||||
|
mCropView.setBitmap(stream);
|
||||||
|
} catch (IllegalArgumentException ex) {
|
||||||
|
Toast.makeText(this, ex.getMessage(), Toast.LENGTH_LONG).show();
|
||||||
|
return false;
|
||||||
|
} catch (Exception ex) {
|
||||||
|
String s = Utility.createMessage(ex) + "\n\n" + uri;
|
||||||
|
showErrorMessage(getString(R.string.load_img_err_title), ex.getMessage(), s);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
mCropView.rotateImage(getOrientation(resolver, uri));
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setDefaultBitmap() {
|
private void setDefaultBitmap() {
|
||||||
|
|
@ -332,32 +343,19 @@ public class MainActivity extends AppCompatActivity {
|
||||||
else if (id == R.id.action_rotate) {
|
else if (id == R.id.action_rotate) {
|
||||||
mCropView.rotateImage(90);
|
mCropView.rotateImage(90);
|
||||||
}
|
}
|
||||||
else if (id == R.id.action_reset) {
|
|
||||||
mCropView.resetImage();
|
|
||||||
}
|
|
||||||
else if (id == R.id.action_privacy_policy) {
|
else if (id == R.id.action_privacy_policy) {
|
||||||
openLinkInBrowser("https://sites.google.com/view/olgamiller/sstvencoder/privacypolicy/");
|
showTextPage(getString(R.string.action_privacy_policy), getString(R.string.action_privacy_policy_text));
|
||||||
}
|
}
|
||||||
else if (id == R.id.action_about) {
|
else if (id == R.id.action_about) {
|
||||||
showTextPage(getString(R.string.action_about), getString(R.string.action_about_text, BuildConfig.VERSION_NAME));
|
showTextPage(getString(R.string.action_about), getString(R.string.action_about_text, BuildConfig.VERSION_NAME));
|
||||||
}
|
}
|
||||||
else if (id != R.id.action_modes && id != R.id.action_transform) {
|
else if (id != R.id.action_modes) {
|
||||||
String className = item.getIntent().getStringExtra(CLASS_NAME);
|
String className = item.getIntent().getStringExtra(CLASS_NAME);
|
||||||
setMode(className);
|
setMode(className);
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void openLinkInBrowser(String link) {
|
|
||||||
Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(link));
|
|
||||||
if (intent.resolveActivity(getPackageManager()) != null) {
|
|
||||||
startActivity(intent);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
showErrorMessage(getString(R.string.another_activity_start_err), link, "");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void showTextPage(String title, String message) {
|
private void showTextPage(String title, String message) {
|
||||||
AlertDialog.Builder builder = new AlertDialog.Builder(this);
|
AlertDialog.Builder builder = new AlertDialog.Builder(this);
|
||||||
builder.setTitle(title);
|
builder.setTitle(title);
|
||||||
|
|
@ -393,34 +391,25 @@ public class MainActivity extends AppCompatActivity {
|
||||||
public void startEditTextActivity(@NonNull Label label) {
|
public void startEditTextActivity(@NonNull Label label) {
|
||||||
Intent intent = new Intent(this, EditTextActivity.class);
|
Intent intent = new Intent(this, EditTextActivity.class);
|
||||||
intent.putExtra(EditTextActivity.EXTRA, label);
|
intent.putExtra(EditTextActivity.EXTRA, label);
|
||||||
tryToStartActivityForResult(intent, EditTextActivity.REQUEST_CODE);
|
startActivityForResult(intent, EditTextActivity.REQUEST_CODE);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void dispatchTakePictureIntent() {
|
private void dispatchTakePictureIntent() {
|
||||||
Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
|
Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
|
||||||
Uri uri = Utility.createImageUri(this);
|
if (intent.resolveActivity(getPackageManager()) != null) {
|
||||||
if (uri != null) {
|
Uri uri = Utility.createImageUri(this);
|
||||||
mSettings.setImageUri(uri);
|
if (uri != null) {
|
||||||
intent.putExtra(MediaStore.EXTRA_OUTPUT, uri);
|
mSettings.setImageUri(uri);
|
||||||
tryToStartActivityForResult(intent, REQUEST_IMAGE_CAPTURE);
|
intent.putExtra(MediaStore.EXTRA_OUTPUT, uri);
|
||||||
|
startActivityForResult(intent, REQUEST_IMAGE_CAPTURE);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void dispatchPickPictureIntent() {
|
private void dispatchPickPictureIntent() {
|
||||||
Intent intent = new Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
|
Intent intent = new Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
|
||||||
tryToStartActivityForResult(intent, REQUEST_PICK_IMAGE);
|
if (intent.resolveActivity(getPackageManager()) != null)
|
||||||
}
|
startActivityForResult(intent, REQUEST_PICK_IMAGE);
|
||||||
|
|
||||||
private void tryToStartActivityForResult(Intent intent, int requestCode) {
|
|
||||||
if (intent.resolveActivity(getPackageManager()) == null) {
|
|
||||||
Toast.makeText(this, R.string.another_activity_resolve_err, Toast.LENGTH_LONG).show();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
startActivityForResult(intent, requestCode);
|
|
||||||
} catch (Exception ignore) {
|
|
||||||
Toast.makeText(this, R.string.another_activity_start_err, Toast.LENGTH_LONG).show();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
||||||
|
|
@ -36,8 +36,4 @@ class MainActivityMessenger {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getString(int resId, Object... formatArgs) {
|
|
||||||
return mMainActivity.getString(resId, formatArgs);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,7 @@ limitations under the License.
|
||||||
package om.sstvencoder.ModeInterfaces;
|
package om.sstvencoder.ModeInterfaces;
|
||||||
|
|
||||||
public interface IModeInfo {
|
public interface IModeInfo {
|
||||||
String getModeName();
|
int getModeName();
|
||||||
|
|
||||||
String getModeClassName();
|
String getModeClassName();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -19,12 +19,11 @@ import android.graphics.Bitmap;
|
||||||
|
|
||||||
import om.sstvencoder.ModeInterfaces.ModeSize;
|
import om.sstvencoder.ModeInterfaces.ModeSize;
|
||||||
import om.sstvencoder.Output.IOutput;
|
import om.sstvencoder.Output.IOutput;
|
||||||
|
import om.sstvencoder.R;
|
||||||
|
|
||||||
@ModeSize(width = 320, height = 256)
|
@ModeSize(width = 320, height = 256)
|
||||||
@ModeDescription(name = Martin1.Name)
|
@ModeDescription(name = R.string.action_martin1)
|
||||||
class Martin1 extends Martin {
|
class Martin1 extends Martin {
|
||||||
public static final String Name = "Martin 1";
|
|
||||||
|
|
||||||
Martin1(Bitmap bitmap, IOutput output) {
|
Martin1(Bitmap bitmap, IOutput output) {
|
||||||
super(bitmap, output);
|
super(bitmap, output);
|
||||||
mVISCode = 44;
|
mVISCode = 44;
|
||||||
|
|
|
||||||
|
|
@ -19,12 +19,11 @@ import android.graphics.Bitmap;
|
||||||
|
|
||||||
import om.sstvencoder.ModeInterfaces.ModeSize;
|
import om.sstvencoder.ModeInterfaces.ModeSize;
|
||||||
import om.sstvencoder.Output.IOutput;
|
import om.sstvencoder.Output.IOutput;
|
||||||
|
import om.sstvencoder.R;
|
||||||
|
|
||||||
@ModeSize(width = 320, height = 256)
|
@ModeSize(width = 320, height = 256)
|
||||||
@ModeDescription(name = Martin2.Name)
|
@ModeDescription(name = R.string.action_martin2)
|
||||||
class Martin2 extends Martin {
|
class Martin2 extends Martin {
|
||||||
public static final String Name = "Martin 2";
|
|
||||||
|
|
||||||
Martin2(Bitmap bitmap, IOutput output) {
|
Martin2(Bitmap bitmap, IOutput output) {
|
||||||
super(bitmap, output);
|
super(bitmap, output);
|
||||||
mVISCode = 40;
|
mVISCode = 40;
|
||||||
|
|
|
||||||
|
|
@ -23,5 +23,5 @@ import java.lang.annotation.Target;
|
||||||
@Target({ElementType.TYPE})
|
@Target({ElementType.TYPE})
|
||||||
@Retention(RetentionPolicy.RUNTIME)
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
@interface ModeDescription {
|
@interface ModeDescription {
|
||||||
String name();
|
int name();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,7 @@ class ModeInfo implements IModeInfo {
|
||||||
mModeClass = modeClass;
|
mModeClass = modeClass;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getModeName() {
|
public int getModeName() {
|
||||||
return mModeClass.getAnnotation(ModeDescription.class).name();
|
return mModeClass.getAnnotation(ModeDescription.class).name();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -19,12 +19,11 @@ import android.graphics.Bitmap;
|
||||||
|
|
||||||
import om.sstvencoder.ModeInterfaces.ModeSize;
|
import om.sstvencoder.ModeInterfaces.ModeSize;
|
||||||
import om.sstvencoder.Output.IOutput;
|
import om.sstvencoder.Output.IOutput;
|
||||||
|
import om.sstvencoder.R;
|
||||||
|
|
||||||
@ModeSize(width = 640, height = 496)
|
@ModeSize(width = 640, height = 496)
|
||||||
@ModeDescription(name = PD120.Name)
|
@ModeDescription(name = R.string.action_pd120)
|
||||||
class PD120 extends PD {
|
class PD120 extends PD {
|
||||||
public static final String Name = "PD 120";
|
|
||||||
|
|
||||||
PD120(Bitmap bitmap, IOutput output) {
|
PD120(Bitmap bitmap, IOutput output) {
|
||||||
super(bitmap, output);
|
super(bitmap, output);
|
||||||
mVISCode = 95;
|
mVISCode = 95;
|
||||||
|
|
|
||||||
|
|
@ -19,12 +19,11 @@ import android.graphics.Bitmap;
|
||||||
|
|
||||||
import om.sstvencoder.ModeInterfaces.ModeSize;
|
import om.sstvencoder.ModeInterfaces.ModeSize;
|
||||||
import om.sstvencoder.Output.IOutput;
|
import om.sstvencoder.Output.IOutput;
|
||||||
|
import om.sstvencoder.R;
|
||||||
|
|
||||||
@ModeSize(width = 512, height = 400)
|
@ModeSize(width = 512, height = 400)
|
||||||
@ModeDescription(name = PD160.Name)
|
@ModeDescription(name = R.string.action_pd160)
|
||||||
class PD160 extends PD {
|
class PD160 extends PD {
|
||||||
public static final String Name = "PD 160";
|
|
||||||
|
|
||||||
PD160(Bitmap bitmap, IOutput output) {
|
PD160(Bitmap bitmap, IOutput output) {
|
||||||
super(bitmap, output);
|
super(bitmap, output);
|
||||||
mVISCode = 98;
|
mVISCode = 98;
|
||||||
|
|
|
||||||
|
|
@ -19,12 +19,11 @@ import android.graphics.Bitmap;
|
||||||
|
|
||||||
import om.sstvencoder.ModeInterfaces.ModeSize;
|
import om.sstvencoder.ModeInterfaces.ModeSize;
|
||||||
import om.sstvencoder.Output.IOutput;
|
import om.sstvencoder.Output.IOutput;
|
||||||
|
import om.sstvencoder.R;
|
||||||
|
|
||||||
@ModeSize(width = 640, height = 496)
|
@ModeSize(width = 640, height = 496)
|
||||||
@ModeDescription(name = PD180.Name)
|
@ModeDescription(name = R.string.action_pd180)
|
||||||
class PD180 extends PD {
|
class PD180 extends PD {
|
||||||
public static final String Name = "PD 180";
|
|
||||||
|
|
||||||
PD180(Bitmap bitmap, IOutput output) {
|
PD180(Bitmap bitmap, IOutput output) {
|
||||||
super(bitmap, output);
|
super(bitmap, output);
|
||||||
mVISCode = 96;
|
mVISCode = 96;
|
||||||
|
|
|
||||||
|
|
@ -19,12 +19,11 @@ import android.graphics.Bitmap;
|
||||||
|
|
||||||
import om.sstvencoder.ModeInterfaces.ModeSize;
|
import om.sstvencoder.ModeInterfaces.ModeSize;
|
||||||
import om.sstvencoder.Output.IOutput;
|
import om.sstvencoder.Output.IOutput;
|
||||||
|
import om.sstvencoder.R;
|
||||||
|
|
||||||
@ModeSize(width = 640, height = 496)
|
@ModeSize(width = 640, height = 496)
|
||||||
@ModeDescription(name = PD240.Name)
|
@ModeDescription(name = R.string.action_pd240)
|
||||||
class PD240 extends PD {
|
class PD240 extends PD {
|
||||||
public static final String Name = "PD 240";
|
|
||||||
|
|
||||||
PD240(Bitmap bitmap, IOutput output) {
|
PD240(Bitmap bitmap, IOutput output) {
|
||||||
super(bitmap, output);
|
super(bitmap, output);
|
||||||
mVISCode = 97;
|
mVISCode = 97;
|
||||||
|
|
|
||||||
|
|
@ -19,12 +19,11 @@ import android.graphics.Bitmap;
|
||||||
|
|
||||||
import om.sstvencoder.ModeInterfaces.ModeSize;
|
import om.sstvencoder.ModeInterfaces.ModeSize;
|
||||||
import om.sstvencoder.Output.IOutput;
|
import om.sstvencoder.Output.IOutput;
|
||||||
|
import om.sstvencoder.R;
|
||||||
|
|
||||||
@ModeSize(width = 800, height = 616)
|
@ModeSize(width = 800, height = 616)
|
||||||
@ModeDescription(name = PD290.Name)
|
@ModeDescription(name = R.string.action_pd290)
|
||||||
class PD290 extends PD {
|
class PD290 extends PD {
|
||||||
public static final String Name = "PD 290";
|
|
||||||
|
|
||||||
PD290(Bitmap bitmap, IOutput output) {
|
PD290(Bitmap bitmap, IOutput output) {
|
||||||
super(bitmap, output);
|
super(bitmap, output);
|
||||||
mVISCode = 94;
|
mVISCode = 94;
|
||||||
|
|
|
||||||
|
|
@ -19,12 +19,11 @@ import android.graphics.Bitmap;
|
||||||
|
|
||||||
import om.sstvencoder.ModeInterfaces.ModeSize;
|
import om.sstvencoder.ModeInterfaces.ModeSize;
|
||||||
import om.sstvencoder.Output.IOutput;
|
import om.sstvencoder.Output.IOutput;
|
||||||
|
import om.sstvencoder.R;
|
||||||
|
|
||||||
@ModeSize(width = 320, height = 256)
|
@ModeSize(width = 320, height = 256)
|
||||||
@ModeDescription(name = PD50.Name)
|
@ModeDescription(name = R.string.action_pd50)
|
||||||
class PD50 extends PD {
|
class PD50 extends PD {
|
||||||
public static final String Name = "PD 50";
|
|
||||||
|
|
||||||
PD50(Bitmap bitmap, IOutput output) {
|
PD50(Bitmap bitmap, IOutput output) {
|
||||||
super(bitmap, output);
|
super(bitmap, output);
|
||||||
mVISCode = 93;
|
mVISCode = 93;
|
||||||
|
|
|
||||||
|
|
@ -19,12 +19,11 @@ import android.graphics.Bitmap;
|
||||||
|
|
||||||
import om.sstvencoder.ModeInterfaces.ModeSize;
|
import om.sstvencoder.ModeInterfaces.ModeSize;
|
||||||
import om.sstvencoder.Output.IOutput;
|
import om.sstvencoder.Output.IOutput;
|
||||||
|
import om.sstvencoder.R;
|
||||||
|
|
||||||
@ModeSize(width = 320, height = 256)
|
@ModeSize(width = 320, height = 256)
|
||||||
@ModeDescription(name = PD90.Name)
|
@ModeDescription(name = R.string.action_pd90)
|
||||||
class PD90 extends PD {
|
class PD90 extends PD {
|
||||||
public static final String Name = "PD 90";
|
|
||||||
|
|
||||||
PD90(Bitmap bitmap, IOutput output) {
|
PD90(Bitmap bitmap, IOutput output) {
|
||||||
super(bitmap, output);
|
super(bitmap, output);
|
||||||
mVISCode = 99;
|
mVISCode = 99;
|
||||||
|
|
|
||||||
|
|
@ -22,12 +22,11 @@ import om.sstvencoder.Modes.ImageFormats.YuvFactory;
|
||||||
import om.sstvencoder.Modes.ImageFormats.YuvImageFormat;
|
import om.sstvencoder.Modes.ImageFormats.YuvImageFormat;
|
||||||
import om.sstvencoder.ModeInterfaces.ModeSize;
|
import om.sstvencoder.ModeInterfaces.ModeSize;
|
||||||
import om.sstvencoder.Output.IOutput;
|
import om.sstvencoder.Output.IOutput;
|
||||||
|
import om.sstvencoder.R;
|
||||||
|
|
||||||
@ModeSize(width = 320, height = 240)
|
@ModeSize(width = 320, height = 240)
|
||||||
@ModeDescription(name = Robot36.Name)
|
@ModeDescription(name = R.string.action_robot36)
|
||||||
class Robot36 extends Mode {
|
class Robot36 extends Mode {
|
||||||
public static final String Name = "Robot 36";
|
|
||||||
|
|
||||||
private final Yuv mYuv;
|
private final Yuv mYuv;
|
||||||
|
|
||||||
private final int mLumaScanSamples;
|
private final int mLumaScanSamples;
|
||||||
|
|
|
||||||
|
|
@ -22,12 +22,11 @@ import om.sstvencoder.Modes.ImageFormats.YuvFactory;
|
||||||
import om.sstvencoder.Modes.ImageFormats.YuvImageFormat;
|
import om.sstvencoder.Modes.ImageFormats.YuvImageFormat;
|
||||||
import om.sstvencoder.ModeInterfaces.ModeSize;
|
import om.sstvencoder.ModeInterfaces.ModeSize;
|
||||||
import om.sstvencoder.Output.IOutput;
|
import om.sstvencoder.Output.IOutput;
|
||||||
|
import om.sstvencoder.R;
|
||||||
|
|
||||||
@ModeSize(width = 320, height = 240)
|
@ModeSize(width = 320, height = 240)
|
||||||
@ModeDescription(name = Robot72.Name)
|
@ModeDescription(name = R.string.action_robot72)
|
||||||
class Robot72 extends Mode {
|
class Robot72 extends Mode {
|
||||||
public static final String Name = "Robot 72";
|
|
||||||
|
|
||||||
private final Yuv mYuv;
|
private final Yuv mYuv;
|
||||||
|
|
||||||
private final int mLumaScanSamples;
|
private final int mLumaScanSamples;
|
||||||
|
|
|
||||||
|
|
@ -19,12 +19,11 @@ import android.graphics.Bitmap;
|
||||||
|
|
||||||
import om.sstvencoder.ModeInterfaces.ModeSize;
|
import om.sstvencoder.ModeInterfaces.ModeSize;
|
||||||
import om.sstvencoder.Output.IOutput;
|
import om.sstvencoder.Output.IOutput;
|
||||||
|
import om.sstvencoder.R;
|
||||||
|
|
||||||
@ModeSize(width = 320, height = 256)
|
@ModeSize(width = 320, height = 256)
|
||||||
@ModeDescription(name = Scottie1.Name)
|
@ModeDescription(name = R.string.action_scottie1)
|
||||||
class Scottie1 extends Scottie {
|
class Scottie1 extends Scottie {
|
||||||
public static final String Name = "Scottie 1";
|
|
||||||
|
|
||||||
Scottie1(Bitmap bitmap, IOutput output) {
|
Scottie1(Bitmap bitmap, IOutput output) {
|
||||||
super(bitmap, output);
|
super(bitmap, output);
|
||||||
mVISCode = 60;
|
mVISCode = 60;
|
||||||
|
|
|
||||||
|
|
@ -19,12 +19,11 @@ import android.graphics.Bitmap;
|
||||||
|
|
||||||
import om.sstvencoder.ModeInterfaces.ModeSize;
|
import om.sstvencoder.ModeInterfaces.ModeSize;
|
||||||
import om.sstvencoder.Output.IOutput;
|
import om.sstvencoder.Output.IOutput;
|
||||||
|
import om.sstvencoder.R;
|
||||||
|
|
||||||
@ModeSize(width = 320, height = 256)
|
@ModeSize(width = 320, height = 256)
|
||||||
@ModeDescription(name = Scottie2.Name)
|
@ModeDescription(name = R.string.action_scottie2)
|
||||||
class Scottie2 extends Scottie {
|
class Scottie2 extends Scottie {
|
||||||
public static final String Name = "Scottie 2";
|
|
||||||
|
|
||||||
Scottie2(Bitmap bitmap, IOutput output){
|
Scottie2(Bitmap bitmap, IOutput output){
|
||||||
super(bitmap, output);
|
super(bitmap, output);
|
||||||
mVISCode = 56;
|
mVISCode = 56;
|
||||||
|
|
|
||||||
|
|
@ -19,12 +19,11 @@ import android.graphics.Bitmap;
|
||||||
|
|
||||||
import om.sstvencoder.ModeInterfaces.ModeSize;
|
import om.sstvencoder.ModeInterfaces.ModeSize;
|
||||||
import om.sstvencoder.Output.IOutput;
|
import om.sstvencoder.Output.IOutput;
|
||||||
|
import om.sstvencoder.R;
|
||||||
|
|
||||||
@ModeSize(width = 320, height = 256)
|
@ModeSize(width = 320, height = 256)
|
||||||
@ModeDescription(name = ScottieDX.Name)
|
@ModeDescription(name = R.string.action_scottie_dx)
|
||||||
class ScottieDX extends Scottie {
|
class ScottieDX extends Scottie {
|
||||||
public static final String Name = "Scottie DX";
|
|
||||||
|
|
||||||
ScottieDX(Bitmap bitmap, IOutput output) {
|
ScottieDX(Bitmap bitmap, IOutput output) {
|
||||||
super(bitmap, output);
|
super(bitmap, output);
|
||||||
mVISCode = 76;
|
mVISCode = 76;
|
||||||
|
|
|
||||||
|
|
@ -20,12 +20,11 @@ import android.graphics.Color;
|
||||||
|
|
||||||
import om.sstvencoder.ModeInterfaces.ModeSize;
|
import om.sstvencoder.ModeInterfaces.ModeSize;
|
||||||
import om.sstvencoder.Output.IOutput;
|
import om.sstvencoder.Output.IOutput;
|
||||||
|
import om.sstvencoder.R;
|
||||||
|
|
||||||
@ModeSize(width = 320, height = 256)
|
@ModeSize(width = 320, height = 256)
|
||||||
@ModeDescription(name = Wraase.Name)
|
@ModeDescription(name = R.string.action_wraaseSC2_180)
|
||||||
class Wraase extends Mode {
|
class Wraase extends Mode {
|
||||||
public static final String Name = "Wraase SC2 180";
|
|
||||||
|
|
||||||
private final int mSyncPulseSamples;
|
private final int mSyncPulseSamples;
|
||||||
private final double mSyncPulseFrequency;
|
private final double mSyncPulseFrequency;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -22,9 +22,7 @@ import android.graphics.Path;
|
||||||
import android.graphics.Rect;
|
import android.graphics.Rect;
|
||||||
import android.graphics.RectF;
|
import android.graphics.RectF;
|
||||||
import android.graphics.Typeface;
|
import android.graphics.Typeface;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import om.sstvencoder.Utility;
|
|
||||||
|
|
||||||
class LabelPainter {
|
class LabelPainter {
|
||||||
private interface IDrawer {
|
private interface IDrawer {
|
||||||
|
|
@ -132,10 +130,7 @@ class LabelPainter {
|
||||||
|
|
||||||
private void setPaintSettings(float sizeFactor) {
|
private void setPaintSettings(float sizeFactor) {
|
||||||
mPaint.setAlpha(255);
|
mPaint.setAlpha(255);
|
||||||
try {
|
mPaint.setTypeface(Typeface.create(mLabel.getFamilyName(), getTypeface()));
|
||||||
mPaint.setTypeface(createTypeface());
|
|
||||||
} catch (Exception ignore) {
|
|
||||||
}
|
|
||||||
setTextPaintSettings();
|
setTextPaintSettings();
|
||||||
setSizePaintSettings(sizeFactor);
|
setSizePaintSettings(sizeFactor);
|
||||||
}
|
}
|
||||||
|
|
@ -156,22 +151,18 @@ class LabelPainter {
|
||||||
mPaint.setStrokeWidth(mLabel.getOutlineSize() * textSize);
|
mPaint.setStrokeWidth(mLabel.getOutlineSize() * textSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Typeface createTypeface() {
|
private int getTypeface() {
|
||||||
int style = Typeface.NORMAL;
|
int typeface = Typeface.NORMAL;
|
||||||
|
|
||||||
if (mLabel.getBold() && mLabel.getItalic())
|
if (mLabel.getBold() && mLabel.getItalic())
|
||||||
style = Typeface.BOLD_ITALIC;
|
typeface = Typeface.BOLD_ITALIC;
|
||||||
else {
|
else {
|
||||||
if (mLabel.getBold())
|
if (mLabel.getBold())
|
||||||
style = Typeface.BOLD;
|
typeface = Typeface.BOLD;
|
||||||
else if (mLabel.getItalic())
|
else if (mLabel.getItalic())
|
||||||
style = Typeface.ITALIC;
|
typeface = Typeface.ITALIC;
|
||||||
}
|
}
|
||||||
|
return typeface;
|
||||||
String fontFilePath = Utility.getFontFilePath(mLabel.getFamilyName(), style);
|
|
||||||
Typeface family = Typeface.createFromFile(fontFilePath);
|
|
||||||
|
|
||||||
return Typeface.create(family, style);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -18,10 +18,7 @@ package om.sstvencoder;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.graphics.Rect;
|
import android.graphics.Rect;
|
||||||
|
|
||||||
import androidx.exifinterface.media.ExifInterface;
|
import androidx.exifinterface.media.ExifInterface;
|
||||||
|
|
||||||
import android.graphics.Typeface;
|
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.os.Environment;
|
import android.os.Environment;
|
||||||
|
|
@ -31,15 +28,10 @@ import androidx.core.content.FileProvider;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.text.SimpleDateFormat;
|
import java.text.SimpleDateFormat;
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.List;
|
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
|
|
||||||
public final class Utility {
|
final class Utility {
|
||||||
private static final String DIRECTORY_SYSTEM_FONTS = "/system/fonts";
|
|
||||||
private static final String DEFAULT_FONT_FAMILY = "Default";
|
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
static Rect getEmbeddedRect(int w, int h, int iw, int ih) {
|
static Rect getEmbeddedRect(int w, int h, int iw, int ih) {
|
||||||
Rect rect;
|
Rect rect;
|
||||||
|
|
@ -121,94 +113,4 @@ public final class Utility {
|
||||||
String state = Environment.getExternalStorageState();
|
String state = Environment.getExternalStorageState();
|
||||||
return Environment.MEDIA_MOUNTED.equals(state);
|
return Environment.MEDIA_MOUNTED.equals(state);
|
||||||
}
|
}
|
||||||
|
|
||||||
static List<String> getSystemFontFamilyList() {
|
|
||||||
List<String> fontFamilyNameList = new ArrayList<>();
|
|
||||||
File fontsDir = new File(DIRECTORY_SYSTEM_FONTS);
|
|
||||||
|
|
||||||
if (fontsDir.exists() && fontsDir.isDirectory()) {
|
|
||||||
File[] files = fontsDir.listFiles();
|
|
||||||
if (files != null) {
|
|
||||||
for (File file : files) {
|
|
||||||
String fileName = file.getName();
|
|
||||||
if (file.isFile() && isSupportedFontFileFormat(fileName)) {
|
|
||||||
String fontFamilyName = getFontFamilyName(fileName);
|
|
||||||
if (!fontFamilyNameList.contains(fontFamilyName))
|
|
||||||
fontFamilyNameList.add(fontFamilyName);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fontFamilyNameList.add(0, Utility.DEFAULT_FONT_FAMILY);
|
|
||||||
return fontFamilyNameList;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static boolean isSupportedFontFileFormat(String fileName) {
|
|
||||||
return fileName.endsWith(".ttf") || fileName.endsWith(".otf");
|
|
||||||
}
|
|
||||||
|
|
||||||
private static String getFontFamilyName(String fileName) {
|
|
||||||
String fontFamilyName = fileName;
|
|
||||||
int lastIndex = fileName.length() - 1;
|
|
||||||
|
|
||||||
int charIndex = fileName.indexOf('-');
|
|
||||||
if (0 < charIndex && charIndex < lastIndex) {
|
|
||||||
fontFamilyName = fileName.substring(0, charIndex);
|
|
||||||
} else {
|
|
||||||
charIndex = fileName.lastIndexOf('.');
|
|
||||||
if (0 < charIndex && charIndex < lastIndex) {
|
|
||||||
fontFamilyName = fileName.substring(0, charIndex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return fontFamilyName;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static String getFontFilePath(String fontFamilyName, int style) {
|
|
||||||
List<String> fontFamilyFilePathList = getFontFamilyFilePathList(fontFamilyName);
|
|
||||||
String fontFilePath = fontFamilyFilePathList.get(0);
|
|
||||||
|
|
||||||
String styleString = getFontFileStyleString(style);
|
|
||||||
if (!styleString.isEmpty()) {
|
|
||||||
for (String path : fontFamilyFilePathList) {
|
|
||||||
if (path.contains(styleString)) {
|
|
||||||
fontFilePath = path;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return fontFilePath;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static List<String> getFontFamilyFilePathList(String fontFamilyName) {
|
|
||||||
List<String> fontFamilyFilePathList = new ArrayList<>();
|
|
||||||
File fontsDir = new File(DIRECTORY_SYSTEM_FONTS);
|
|
||||||
|
|
||||||
if (fontsDir.exists() && fontsDir.isDirectory()) {
|
|
||||||
File[] files = fontsDir.listFiles();
|
|
||||||
if (files != null) {
|
|
||||||
for (File file : files) {
|
|
||||||
if (file.isFile()) {
|
|
||||||
String path = file.getAbsolutePath();
|
|
||||||
if (path.contains(fontFamilyName)) {
|
|
||||||
fontFamilyFilePathList.add(path);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return fontFamilyFilePathList;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static String getFontFileStyleString(int style) {
|
|
||||||
if (style == Typeface.NORMAL)
|
|
||||||
return "-Regular";
|
|
||||||
if (style == Typeface.BOLD_ITALIC)
|
|
||||||
return "-BoldItalic";
|
|
||||||
if (style == Typeface.BOLD)
|
|
||||||
return "-Bold";
|
|
||||||
if (style == Typeface.ITALIC)
|
|
||||||
return "-Italic";
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -91,7 +91,7 @@
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/text_outline_size"
|
android:id="@+id/text_outline_size"
|
||||||
android:text="@string/outline_size" />
|
android:text="@string/size"/>
|
||||||
|
|
||||||
<Spinner
|
<Spinner
|
||||||
android:id="@+id/edit_outline_size"
|
android:id="@+id/edit_outline_size"
|
||||||
|
|
@ -126,7 +126,7 @@
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/text_outline_color"
|
android:id="@+id/text_outline_color"
|
||||||
android:text="@string/outline_color" />
|
android:text="@string/color"/>
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:clickable="true"
|
android:clickable="true"
|
||||||
|
|
|
||||||
|
|
@ -30,22 +30,10 @@
|
||||||
android:title="@string/action_save_wave"
|
android:title="@string/action_save_wave"
|
||||||
app:showAsAction="ifRoom"/>
|
app:showAsAction="ifRoom"/>
|
||||||
<item
|
<item
|
||||||
android:id="@+id/action_transform"
|
android:id="@+id/action_rotate"
|
||||||
android:title="@string/action_transform"
|
android:icon="@android:drawable/ic_menu_rotate"
|
||||||
app:showAsAction="ifRoom">
|
android:title="@string/action_rotate"
|
||||||
<menu>
|
app:showAsAction="ifRoom"/>
|
||||||
<item
|
|
||||||
android:id="@+id/action_rotate"
|
|
||||||
android:icon="@android:drawable/ic_menu_rotate"
|
|
||||||
android:title="@string/action_rotate"
|
|
||||||
app:showAsAction="ifRoom"/>
|
|
||||||
<item
|
|
||||||
android:id="@+id/action_reset"
|
|
||||||
android:icon="@android:drawable/ic_menu_revert"
|
|
||||||
android:title="@string/action_reset"
|
|
||||||
app:showAsAction="ifRoom"/>
|
|
||||||
</menu>
|
|
||||||
</item>
|
|
||||||
<item
|
<item
|
||||||
android:id="@+id/action_modes"
|
android:id="@+id/action_modes"
|
||||||
android:title="@string/action_modes"
|
android:title="@string/action_modes"
|
||||||
|
|
|
||||||
|
|
@ -1,6 +0,0 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<resources>
|
|
||||||
<style name="AppTheme" parent="Base.Theme.AppCompat">
|
|
||||||
<item name="android:windowOptOutEdgeToEdgeEnforcement">true</item>
|
|
||||||
</style>
|
|
||||||
</resources>
|
|
||||||
|
|
@ -1,46 +0,0 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<resources>
|
|
||||||
<string name="action_pick_picture">选择照片</string>
|
|
||||||
<string name="action_take_picture">拍摄照片</string>
|
|
||||||
<string name="action_save_wave">保存为波形(.wav)文件</string>
|
|
||||||
<string name="action_stop">停止</string>
|
|
||||||
<string name="action_play">播放</string>
|
|
||||||
<string name="action_transform">更改图片</string>
|
|
||||||
<string name="action_rotate">旋转</string>
|
|
||||||
<string name="action_reset">重置</string>
|
|
||||||
<string name="action_done">完成</string>
|
|
||||||
<string name="action_modes">编码模式</string>
|
|
||||||
<string name="action_privacy_policy">隐私政策</string>
|
|
||||||
<string name="action_about">关于SSTV Encoder</string>
|
|
||||||
<string name="action_about_text">"\n SSTV Encoder %1$s\n版权所有 2017 Olga Miller\n \n\nSSTV Encoder通过慢扫描电视/Slow Scan Television (SSTV)发送图片.\n \n\n要获取详细信息,请参阅本软件源码: \nhttps://github.com/olgamiller/SSTVEncoder2\n \n\nDISCLAIMER:\nTHE SOFTWARE IS PROVIDED \"AS IS\" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.\n "</string>
|
|
||||||
<string name="load_img_err_title">加载图像时出错</string>
|
|
||||||
<string name="load_img_orientation_err_title">定位图像时出错</string>
|
|
||||||
<string name="load_img_err_txt_unsupported">不受支持的内容</string>
|
|
||||||
<string name="message_prev_img_not_loaded">先前所使用的图像未能被加载</string>
|
|
||||||
<string name="message_no_camera">这个设备没有摄像头</string>
|
|
||||||
<string name="progressbar_message_sending">发送…</string>
|
|
||||||
<string name="progressbar_message_saving_to_file">%1$s 另存为文件…</string>
|
|
||||||
<string name="another_activity_resolve_err">未能解析另一个活动</string>
|
|
||||||
<string name="another_activity_start_err">另一个活动未能被启动</string>
|
|
||||||
<string name="btn_send_email">发送邮件</string>
|
|
||||||
<string name="btn_ok">好的</string>
|
|
||||||
<string name="email_subject">SSTV Encoder - BUG反馈</string>
|
|
||||||
<string name="chooser_title">发送BUG反馈</string>
|
|
||||||
<string name="bold">粗体</string>
|
|
||||||
<string name="italic">斜体</string>
|
|
||||||
<string name="outline">描边</string>
|
|
||||||
<string name="color">颜色</string>
|
|
||||||
<string name="outline_color">描边颜色</string>
|
|
||||||
<string name="size">大小</string>
|
|
||||||
<string name="font">字体</string>
|
|
||||||
<string name="font_default">默认字体</string>
|
|
||||||
<string name="text">文本</string>
|
|
||||||
<string name="font_size_small">小</string>
|
|
||||||
<string name="font_size_normal">常规</string>
|
|
||||||
<string name="font_size_large">大</string>
|
|
||||||
<string name="font_size_huge">很大</string>
|
|
||||||
<string name="outline_size">描边大小</string>
|
|
||||||
<string name="outline_size_thin">细</string>
|
|
||||||
<string name="outline_size_normal">常规</string>
|
|
||||||
<string name="outline_size_thick">粗</string>
|
|
||||||
</resources>
|
|
||||||
|
|
@ -1,17 +1,38 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<resources>
|
<resources>
|
||||||
<string name="app_name" translatable="false">SSTV Encoder</string>
|
<string name="app_name">SSTV Encoder</string>
|
||||||
<string name="action_pick_picture">Pick Picture</string>
|
<string name="action_pick_picture">Pick Picture</string>
|
||||||
<string name="action_take_picture">Take Picture</string>
|
<string name="action_take_picture">Take Picture</string>
|
||||||
<string name="action_save_wave">Save as WAVE File</string>
|
<string name="action_save_wave">Save as WAVE File</string>
|
||||||
<string name="action_stop">Stop</string>
|
<string name="action_stop">Stop</string>
|
||||||
<string name="action_play">Play</string>
|
<string name="action_play">Play</string>
|
||||||
<string name="action_transform">Transform Image</string>
|
<string name="action_rotate">Rotate Image</string>
|
||||||
<string name="action_rotate">Rotate</string>
|
|
||||||
<string name="action_reset">Reset</string>
|
|
||||||
<string name="action_done">Done</string>
|
<string name="action_done">Done</string>
|
||||||
<string name="action_modes">Modes</string>
|
<string name="action_modes">Modes</string>
|
||||||
|
<string name="action_martin1">Martin 1</string>
|
||||||
|
<string name="action_martin2">Martin 2</string>
|
||||||
|
<string name="action_pd50">PD 50</string>
|
||||||
|
<string name="action_pd90">PD 90</string>
|
||||||
|
<string name="action_pd120">PD 120</string>
|
||||||
|
<string name="action_pd160">PD 160</string>
|
||||||
|
<string name="action_pd180">PD 180</string>
|
||||||
|
<string name="action_pd240">PD 240</string>
|
||||||
|
<string name="action_pd290">PD 290</string>
|
||||||
|
<string name="action_scottie1">Scottie 1</string>
|
||||||
|
<string name="action_scottie2">Scottie 2</string>
|
||||||
|
<string name="action_scottie_dx">Scottie DX</string>
|
||||||
|
<string name="action_robot36">Robot 36</string>
|
||||||
|
<string name="action_robot72">Robot 72</string>
|
||||||
|
<string name="action_wraaseSC2_180">Wraase SC2 180</string>
|
||||||
<string name="action_privacy_policy">Privacy Policy</string>
|
<string name="action_privacy_policy">Privacy Policy</string>
|
||||||
|
<string name="action_privacy_policy_text">
|
||||||
|
SSTV Encoder converts images to audio.
|
||||||
|
\n\nTo convert the image, it will be loaded into temporary memory.
|
||||||
|
\n\nThe loaded image can be scaled and rotated. Neither the modified image nor the modifications will be stored. The link to the last loaded image will be stored in the app settings locally.
|
||||||
|
\n\nA text overlay can be added. It will be stored in the app settings locally.
|
||||||
|
\n\nThe modified image with text overlays will be encoded to audio using the selected mode. The selected mode will be stored in the app settings locally.
|
||||||
|
\n\nThe resulting audio can either be played back by pressing the play button or saved locally on the device by pressing the save button.
|
||||||
|
</string>
|
||||||
<string name="action_about">About SSTV Encoder</string>
|
<string name="action_about">About SSTV Encoder</string>
|
||||||
<string name="action_about_text">
|
<string name="action_about_text">
|
||||||
SSTV Encoder %1$s\nCopyright 2017 Olga Miller
|
SSTV Encoder %1$s\nCopyright 2017 Olga Miller
|
||||||
|
|
@ -24,10 +45,6 @@
|
||||||
<string name="load_img_err_txt_unsupported">Unsupported content.</string>
|
<string name="load_img_err_txt_unsupported">Unsupported content.</string>
|
||||||
<string name="message_prev_img_not_loaded">Previous image could not be loaded.</string>
|
<string name="message_prev_img_not_loaded">Previous image could not be loaded.</string>
|
||||||
<string name="message_no_camera">Device has no camera.</string>
|
<string name="message_no_camera">Device has no camera.</string>
|
||||||
<string name="progressbar_message_sending">Sending…</string>
|
|
||||||
<string name="progressbar_message_saving_to_file">%1$s saving…</string>
|
|
||||||
<string name="another_activity_resolve_err">Another activity could not be resolved.</string>
|
|
||||||
<string name="another_activity_start_err">Another activity could not be started.</string>
|
|
||||||
<string name="btn_send_email">Send Email</string>
|
<string name="btn_send_email">Send Email</string>
|
||||||
<string name="btn_ok">OK</string>
|
<string name="btn_ok">OK</string>
|
||||||
<string name="email_subject">SSTV Encoder - Bug Report</string>
|
<string name="email_subject">SSTV Encoder - Bug Report</string>
|
||||||
|
|
@ -36,17 +53,8 @@
|
||||||
<string name="italic">Italic</string>
|
<string name="italic">Italic</string>
|
||||||
<string name="outline">Outline</string>
|
<string name="outline">Outline</string>
|
||||||
<string name="color">Color</string>
|
<string name="color">Color</string>
|
||||||
<string name="outline_color">Color</string>
|
<string name="outline_color">Outline Color</string>
|
||||||
<string name="size">Size</string>
|
<string name="size">Size</string>
|
||||||
<string name="font">Font</string>
|
<string name="font">Font</string>
|
||||||
<string name="font_default">Default</string>
|
|
||||||
<string name="text">Text</string>
|
<string name="text">Text</string>
|
||||||
<string name="font_size_small">Small</string>
|
|
||||||
<string name="font_size_normal">Normal</string>
|
|
||||||
<string name="font_size_large">Large</string>
|
|
||||||
<string name="font_size_huge">Huge</string>
|
|
||||||
<string name="outline_size">Size</string>
|
|
||||||
<string name="outline_size_thin">Thin</string>
|
|
||||||
<string name="outline_size_normal">Normal</string>
|
|
||||||
<string name="outline_size_thick">Thick</string>
|
|
||||||
</resources>
|
</resources>
|
||||||
13
build.gradle
13
build.gradle
|
|
@ -4,24 +4,17 @@ buildscript {
|
||||||
mavenCentral()
|
mavenCentral()
|
||||||
}
|
}
|
||||||
dependencies {
|
dependencies {
|
||||||
classpath 'com.android.tools.build:gradle:8.7.3'
|
classpath 'com.android.tools.build:gradle:7.3.1'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
allprojects {
|
allprojects {
|
||||||
configurations.configureEach {
|
|
||||||
resolutionStrategy.eachDependency { details ->
|
|
||||||
if (details.requested.group == 'org.jetbrains.kotlin') {
|
|
||||||
details.useVersion "1.8.22"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
repositories {
|
repositories {
|
||||||
google()
|
google()
|
||||||
mavenCentral()
|
mavenCentral()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
tasks.register('clean', Delete) {
|
task clean(type: Delete) {
|
||||||
delete rootProject.layout.buildDirectory.get().asFile
|
delete rootProject.buildDir
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,53 +1,43 @@
|
||||||
|
--- Supported modes:
|
||||||
|
|
||||||
<h3>Modes</h3>
|
<ul><li><b>Martin Modes:</b> Martin 1, Martin 2</li>
|
||||||
|
<li><b>PD Modes:</b> PD 50, PD 90, PD 120, PD 160, PD 180, PD 240, PD 290</li>
|
||||||
|
<li><b>Scottie Modes:</b> Scottie 1, Scottie 2, Scottie DX</li>
|
||||||
|
<li><b>Robot Modes:</b> Robot 36 Color, Robot 72 Color</li>
|
||||||
|
<li><b>Wraase Modes:</b> Wraase SC2 180</li></ul>
|
||||||
|
|
||||||
Supported SSTV modes:
|
The mode specifications are taken from the Dayton Paper of JL Barber:
|
||||||
<ul>
|
http://www.barberdsp.com/downloads/Dayton%20Paper.pdf
|
||||||
<li><b>Martin Modes</b>: Martin 1, Martin 2</li>
|
|
||||||
<li><b>PD Modes</b>: PD 50, PD 90, PD 120, PD 160, PD 180, PD 240, PD 290</li>
|
|
||||||
<li><b>Robot Modes</b>: Robot 36 Color, Robot 72 Color</li>
|
|
||||||
<li><b>Scottie Modes</b>: Scottie 1, Scottie 2, Scottie DX</li>
|
|
||||||
<li><b>Wraase Modes</b>: Wraase SC2 180</li>
|
|
||||||
</ul>
|
|
||||||
The mode specifications are taken from the Dayton Paper, JL Barber, "Proposal for SSTV Mode Specifications", 2000:<br>
|
|
||||||
<a href='http://www.barberdsp.com/downloads/Dayton%20Paper.pdf'>http://www.barberdsp.com/downloads/Dayton%20Paper.pdf</a>
|
|
||||||
|
|
||||||
<h3>Image</h3>
|
--- Image:
|
||||||
|
|
||||||
|
Tap "Take Picture" or "Pick Picture" menu button or
|
||||||
|
use the Share option of any app like Gallery to load an image.
|
||||||
|
|
||||||
|
To keep the aspect ratio, black borders will be added if necessary.
|
||||||
|
Original image can be resend using another mode without reloading.
|
||||||
|
|
||||||
|
After image rotation or mode changing the image
|
||||||
|
will be scaled to that mode's native size.
|
||||||
|
|
||||||
To load an image:
|
|
||||||
<ul>
|
|
||||||
<li>tap <b>"Take Picture"</b> or <b>"Pick Picture"</b> menu button, or
|
|
||||||
<li>use the <b>Share</b> option of an app like e.g. Gallery.
|
|
||||||
</ul>
|
|
||||||
To keep the aspect ratio, black borders will be added if necessary.<br>
|
|
||||||
Original image can be resend using another mode without reloading.<br>
|
|
||||||
After image rotation or mode changing the image will be scaled to that mode's native size.<br>
|
|
||||||
After closing the app the loaded image will not be stored.
|
After closing the app the loaded image will not be stored.
|
||||||
|
|
||||||
<h3>Text Overlay</h3>
|
--- Text Overlay:
|
||||||
|
|
||||||
Actions for working with text overlays:
|
Single tap <b>to add</b> a text overlay.
|
||||||
<ul>
|
Single tap on text overlay <b>to edit</b> it.
|
||||||
<li>Single tap <b>to add</b> a text overlay.</li>
|
Long press <b>to move</b> text overlay.
|
||||||
<li>Single tap on text overlay <b>to edit</b> it.</li>
|
Remove the text <b>to remove</b> a text overlay.
|
||||||
<li>Long press <b>to move</b> text overlay.</li>
|
|
||||||
<li>Remove the text <b>to remove</b> a text overlay.</li>
|
|
||||||
</ul>
|
|
||||||
After closing the app all text overlays will be stored and reloaded when restarting.
|
|
||||||
|
|
||||||
<h3>Menu</h3>
|
After closing the app all text overlays
|
||||||
|
will be stored and reloaded when restarting.
|
||||||
|
|
||||||
Available menu options:
|
--- Menu:
|
||||||
<ul>
|
|
||||||
<li><b>"Play"</b>: Sends the image</li>
|
<ul><li><b>"Pick Picture"</b> - Opens an image viewer app to select a picture.</li>
|
||||||
<li><b>"Stop"</b>: Stops the current sending and empties the queue</li>
|
<li><b>"Take Picture"</b> - Starts a camera app to take a picture.</li>
|
||||||
<li><b>"Pick Picture"</b>: Opens an image viewer app to select a picture</li>
|
<li><b>"Save Wave File"</b> - Creates a wave file in the Music folder in SSTV Encoder album.</li>
|
||||||
<li><b>"Take Picture"</b>: Starts a camera app to take a picture</li>
|
<li><b>"Play"</b> - Sends the image.</li>
|
||||||
<li><b>"Save as WAVE File"</b>: Creates a wave file in the Music folder in SSTV Encoder album</li>
|
<li><b>"Stop"</b> - Stops the current sending and empties the queue.</li>
|
||||||
<li><b>"Transform Image"</b>:</li>
|
<li><b>"Rotate Image"</b> - Rotates the image by 90 degrees.</li>
|
||||||
<ul>
|
<li><b>"Modes"</b> - Lists all supported modes.</li></ul>
|
||||||
<li><b>"Rotate"</b>: Rotates the image by 90 degrees</li>
|
|
||||||
<li><b>"Reset"</b>: Resets image rotation and scaling</li>
|
|
||||||
</ul>
|
|
||||||
<li><b>"Modes"</b>: Lists all supported modes</li>
|
|
||||||
</ul>
|
|
||||||
|
|
|
||||||
Binary file not shown.
|
Before Width: | Height: | Size: 16 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 1.3 MiB |
Binary file not shown.
|
Before Width: | Height: | Size: 62 KiB |
|
|
@ -1 +0,0 @@
|
||||||
SSTV Encoder
|
|
||||||
2
gradle/wrapper/gradle-wrapper.properties
vendored
2
gradle/wrapper/gradle-wrapper.properties
vendored
|
|
@ -1,5 +1,5 @@
|
||||||
distributionBase=GRADLE_USER_HOME
|
distributionBase=GRADLE_USER_HOME
|
||||||
distributionPath=wrapper/dists
|
distributionPath=wrapper/dists
|
||||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.9-bin.zip
|
distributionUrl=https\://services.gradle.org/distributions/gradle-7.4-all.zip
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
zipStorePath=wrapper/dists
|
zipStorePath=wrapper/dists
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue