Compare commits

...

11 commits

Author SHA1 Message Date
Olga Miller 94821b1697 Version 2.13 2025-05-24 10:31:47 +02:00
Olga Miller cfda4eff70
Merge pull request #18 from xdsopl/master
Temporarily opt out of Edge-to-Edge enforcement
2025-05-24 10:22:08 +02:00
Ahmet Inan 446f1da12b temporarily opt out of edge to edge enforcement 2025-05-06 10:05:14 +02:00
Olga Miller decdb191af Version 2.12 2025-01-06 14:15:53 +01:00
Olga Miller 70f4cbbaa3 Combined font file names to font family names,
changed to store the font family name instead of the font file path (as it was before the previous commit),
 added reading of existing system font files with file extension .otf (i.e., additionally to .ttf)
2024-12-30 16:37:14 +01:00
Olga Miller 5c50012f71 Updated loading of the system fonts (by listing all existing /system/fonts/*.ttf files),
removed adjustment of italic and bold selection for fonts with suffix "-Italic" or "-Bold"
2024-12-29 17:01:16 +01:00
Olga Miller 53b1f39ca9 Updated gradle (SdkVersion 35) 2024-12-29 12:26:58 +01:00
Olga Miller a3aa15e025 Updated description of the "Transform Image" menu item in README 2024-08-18 10:26:14 +02:00
Olga Miller 34429ad4c3 Improved README 2024-08-18 10:26:14 +02:00
Olga Miller 2beda7adfd fastlane: Updated description of the "Transform Image" menu item 2024-08-18 10:04:00 +02:00
Olga Miller d381a06e1a fastlane: Improved full_description 2024-08-18 10:04:00 +02:00
9 changed files with 203 additions and 127 deletions

View file

@ -1,51 +1,53 @@
![Icon](app/src/main/res/mipmap-xhdpi/ic_launcher.png)
# SSTV Encoder 2
This app sends images via Slow-Scan Television (SSTV).
Image encoder for Slow-Scan Television (SSTV) audio signals
### Supported Modes
### Modes
**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
Supported SSTV 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:
http://www.barberdsp.com/downloads/Dayton%20Paper.pdf
### Image
Tap "Take Picture" or "Pick Picture" menu button or
use the Share option of any app like Gallery to load an image.
To load an image:
* tap **"Take Picture"** or **"Pick Picture"** menu button, or
* use the **Share** option of an app like e.g. Gallery.
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.
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.
### Text Overlay
Single tap **to add** a text overlay.
Single tap on text overlay **to edit** it.
Long press **to move** text overlay.
Remove the text **to remove** a text overlay.
Actions for working with text overlays:
* Single tap **to add** a text overlay.
* Single tap on text overlay **to edit** it.
* Long press **to move** 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
* **"Play"** - Sends the image.
* **"Stop"** - Stops the current sending and empties the queue.
* **"Pick Picture"** - Opens an image viewer app to select a picture.
* **"Take Picture"** - Starts a camera app to take a picture.
* **"Save as WAVE File"** - Creates a wave file in the Music folder in SSTV Encoder album.
* **"Rotate Image"** - Rotates the image by 90 degrees.
* **"Modes"** - Lists all supported modes.
Available menu options:
* **"Play"**: Sends the image
* **"Stop"**: Stops the current sending and empties the queue
* **"Pick Picture"**: Opens an image viewer app to select a picture
* **"Take Picture"**: Starts a camera app to take a picture
* **"Save as WAVE File"**: Creates a wave file in the Music folder in SSTV Encoder album
* **"Transform Image"**:
* **"Rotate"**: Rotates the image by 90 degrees
* **"Reset"**: Resets image rotation and scaling
* **"Modes"**: Lists all supported modes
### Installation

View file

@ -1,13 +1,13 @@
apply plugin: 'com.android.application'
android {
compileSdk 34
compileSdk 35
defaultConfig {
applicationId "om.sstvencoder"
minSdk 21
targetSdk 34
versionCode 32
versionName "2.11"
targetSdk 35
versionCode 34
versionName "2.13"
}
buildTypes {
release {

View file

@ -18,10 +18,12 @@ package om.sstvencoder;
import android.content.Intent;
import android.graphics.Color;
import android.graphics.drawable.Drawable;
import androidx.annotation.ColorInt;
import androidx.fragment.app.DialogFragment;
import androidx.core.content.ContextCompat;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.text.Editable;
import android.text.TextWatcher;
@ -52,8 +54,6 @@ public class EditTextActivity extends AppCompatActivity
public static final String EXTRA = "EDIT_TEXT_EXTRA";
private Label mLabel;
private EditColorMode mEditColor;
private FontFamilySet mFontFamilySet;
private FontFamilySet.FontFamily mSelectedFontFamily;
private List<String> mFontFamilyNameList;
private CheckBox mEditItalic, mEditBold, mEditOutline;
private int mClearTextButtonWidth;
@ -77,7 +77,6 @@ public class EditTextActivity extends AppCompatActivity
mEditBold.setChecked(mLabel.getBold());
mEditItalic.setChecked(mLabel.getItalic());
initFontFamilySpinner(mLabel.getFamilyName());
updateBoldAndItalic();
mEditOutline.setChecked(mLabel.getOutline());
initOutlineSizeSpinner(mLabel.getOutlineSize());
findViewById(R.id.edit_color).setBackgroundColor(mLabel.getForeColor());
@ -148,12 +147,10 @@ public class EditTextActivity extends AppCompatActivity
private void initFontFamilySpinner(String familyName) {
Spinner spinner = findViewById(R.id.edit_font_family);
spinner.setOnItemSelectedListener(this);
mFontFamilySet = new FontFamilySet(this);
mSelectedFontFamily = mFontFamilySet.getFontFamily(familyName);
mFontFamilyNameList = mFontFamilySet.getFontFamilyDisplayNameList();
mFontFamilyNameList = Utility.getSystemFontFamilyList();
spinner.setAdapter(new ArrayAdapter<>(this,
android.R.layout.simple_spinner_dropdown_item, mFontFamilyNameList));
spinner.setSelection(mFontFamilyNameList.indexOf(mSelectedFontFamily.displayName));
spinner.setSelection(mFontFamilyNameList.indexOf(familyName));
}
private void initTextSizeSpinner(float textSize) {
@ -219,28 +216,7 @@ public class EditTextActivity extends AppCompatActivity
mLabel.setOutlineSize(positionToOutlineSize(position));
}
else if (parentId == R.id.edit_font_family) {
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);
mLabel.setFamilyName(mFontFamilyNameList.get(position));
}
}

View file

@ -22,7 +22,9 @@ import android.graphics.Path;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.Typeface;
import androidx.annotation.NonNull;
import om.sstvencoder.Utility;
class LabelPainter {
private interface IDrawer {
@ -130,7 +132,10 @@ class LabelPainter {
private void setPaintSettings(float sizeFactor) {
mPaint.setAlpha(255);
mPaint.setTypeface(Typeface.create(mLabel.getFamilyName(), getTypeface()));
try {
mPaint.setTypeface(createTypeface());
} catch (Exception ignore) {
}
setTextPaintSettings();
setSizePaintSettings(sizeFactor);
}
@ -151,18 +156,22 @@ class LabelPainter {
mPaint.setStrokeWidth(mLabel.getOutlineSize() * textSize);
}
private int getTypeface() {
int typeface = Typeface.NORMAL;
private Typeface createTypeface() {
int style = Typeface.NORMAL;
if (mLabel.getBold() && mLabel.getItalic())
typeface = Typeface.BOLD_ITALIC;
style = Typeface.BOLD_ITALIC;
else {
if (mLabel.getBold())
typeface = Typeface.BOLD;
style = Typeface.BOLD;
else if (mLabel.getItalic())
typeface = Typeface.ITALIC;
style = Typeface.ITALIC;
}
return typeface;
String fontFilePath = Utility.getFontFilePath(mLabel.getFamilyName(), style);
Typeface family = Typeface.createFromFile(fontFilePath);
return Typeface.create(family, style);
}
}

View file

@ -18,7 +18,10 @@ package om.sstvencoder;
import android.content.Context;
import android.content.Intent;
import android.graphics.Rect;
import androidx.exifinterface.media.ExifInterface;
import android.graphics.Typeface;
import android.net.Uri;
import android.os.Build;
import android.os.Environment;
@ -28,10 +31,15 @@ import androidx.core.content.FileProvider;
import java.io.File;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Locale;
final class Utility {
public final class Utility {
private static final String DIRECTORY_SYSTEM_FONTS = "/system/fonts";
private static final String DEFAULT_FONT_FAMILY = "Default";
@NonNull
static Rect getEmbeddedRect(int w, int h, int iw, int ih) {
Rect rect;
@ -113,4 +121,94 @@ final class Utility {
String state = Environment.getExternalStorageState();
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 "";
}
}

View file

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="AppTheme" parent="Base.Theme.AppCompat">
<item name="android:windowOptOutEdgeToEdgeEnforcement">true</item>
</style>
</resources>

View file

@ -4,7 +4,7 @@ buildscript {
mavenCentral()
}
dependencies {
classpath 'com.android.tools.build:gradle:8.5.2'
classpath 'com.android.tools.build:gradle:8.7.3'
}
}

View file

@ -1,68 +1,53 @@
<h3>Supported Modes</h3>
<h3>Modes</h3>
Supported SSTV modes:
<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>
<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>
<p>
The mode specifications are taken from the Dayton Paper, JL Barber, "Proposal for SSTV Mode
Specifications", 2000:
<br>
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>
</p>
<h3>Image</h3>
<p>
Tap "Take Picture" or "Pick Picture" menu button or
<br>
use the Share option of any app like Gallery to load an image.
<br>
<br>
To keep the aspect ratio, black borders will be added if necessary.
<br>
Original image can be resend using another mode without reloading.
<br>
<br>
After image rotation or mode changing the image
<br>
will be scaled to that mode's native size.
<br>
<br>
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.
</p>
<h3>Text Overlay</h3>
<p>
Single tap <b>to add</b> a text overlay.
<br>
Single tap on text overlay <b>to edit</b> it.
<br>
Long press <b>to move</b> text overlay.
<br>
Remove the text <b>to remove</b> a text overlay.
<br>
<br>
After closing the app all text overlays
<br>
will be stored and reloaded when restarting.
</p>
Actions for working with text overlays:
<ul>
<li>Single tap <b>to add</b> a text overlay.</li>
<li>Single tap on text overlay <b>to edit</b> it.</li>
<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>
Available menu options:
<ul>
<li><b>"Play"</b> - Sends the image.</li>
<li><b>"Stop"</b> - Stops the current sending and empties the queue.</li>
<li><b>"Pick Picture"</b> - Opens an image viewer app to select a picture.</li>
<li><b>"Take Picture"</b> - Starts a camera app to take a picture.</li>
<li><b>"Save as WAVE File"</b> - Creates a wave file in the Music folder in SSTV Encoder album.
</li>
<li><b>"Rotate Image"</b> - Rotates the image by 90 degrees.</li>
<li><b>"Modes"</b> - Lists all supported modes.</li>
<li><b>"Play"</b>: Sends the image</li>
<li><b>"Stop"</b>: Stops the current sending and empties the queue</li>
<li><b>"Pick Picture"</b>: Opens an image viewer app to select a picture</li>
<li><b>"Take Picture"</b>: Starts a camera app to take a picture</li>
<li><b>"Save as WAVE File"</b>: Creates a wave file in the Music folder in SSTV Encoder album</li>
<li><b>"Transform Image"</b>:</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>

View file

@ -1,5 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-all.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-8.9-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists