oh, naive i am. there will be several more things needed to be able to release this...

This commit is contained in:
pszsh 2026-03-01 12:38:46 -08:00
parent 4e86256f1b
commit 58584e8ab2
6 changed files with 151 additions and 36 deletions

21
PRIVACY_POLICY.md Normal file
View File

@ -0,0 +1,21 @@
# Privacy Policy
**Yr Crystals** does not collect, store, or transmit any personal data.
## Device Permissions
The app may request access to the following device features solely for local, on-device functionality:
- **Music Library** — To play audio tracks from your library.
No data from these sources is recorded, uploaded, or shared. All processing happens entirely on your device.
## Third-Party Services
Yr Crystals does not use analytics, advertising, tracking, or any third-party services.
## Contact
If you have questions about this policy, contact: **[your email]**
*Last updated: March 1, 2026*

View File

@ -39,14 +39,8 @@
</array>
<!-- Permissions -->
<key>NSMicrophoneUsageDescription</key>
<string>This app requires audio access to visualize music.</string>
<key>NSAppleMusicUsageDescription</key>
<string>This app requires access to your music library to play tracks.</string>
<key>NSPhotoLibraryUsageDescription</key>
<string>This app requires access to the photo library to load album art.</string>
<key>NSCameraUsageDescription</key>
<string>This app requires camera access for visualizer input.</string>
<string>Yr Crystals needs access to your music library to play tracks.</string>
<!-- File Access -->
<key>UIFileSharingEnabled</key>

View File

@ -463,19 +463,21 @@ OverlayWidget::OverlayWidget(QWidget *content, QWidget *parent)
hide();
}
void OverlayWidget::showEvent(QShowEvent *event) {
QWidget::showEvent(event);
if constexpr (Theme::FrostedGlass) {
if (auto *w = window()) {
QPixmap shot = w->grab();
int sw = shot.width() / 10;
int sh = shot.height() / 10;
m_blurredBg = shot.scaled(sw, sh, Qt::IgnoreAspectRatio,
Qt::SmoothTransformation)
.scaled(shot.size(), Qt::IgnoreAspectRatio,
Qt::SmoothTransformation);
void OverlayWidget::setVisible(bool visible) {
if (visible && !isVisible()) {
if constexpr (Theme::FrostedGlass) {
if (auto *p = parentWidget()) {
QPixmap shot = p->grab();
int sw = shot.width() / 10;
int sh = shot.height() / 10;
m_blurredBg = shot.scaled(sw, sh, Qt::IgnoreAspectRatio,
Qt::SmoothTransformation)
.scaled(shot.size(), Qt::IgnoreAspectRatio,
Qt::SmoothTransformation);
}
}
}
QWidget::setVisible(visible);
}
void OverlayWidget::mousePressEvent(QMouseEvent *event) {

View File

@ -128,9 +128,9 @@ class OverlayWidget : public QWidget {
Q_OBJECT
public:
OverlayWidget(QWidget *content, QWidget *parent = nullptr);
void setVisible(bool visible) override;
protected:
void showEvent(QShowEvent *event) override;
void mousePressEvent(QMouseEvent *event) override;
void paintEvent(QPaintEvent *event) override;

View File

@ -4,6 +4,7 @@
#include "Theme.h"
#include <QGridLayout>
#include <QHBoxLayout>
#include <QPainter>
#include <QVBoxLayout>
#include <cmath>
@ -13,7 +14,7 @@ PlaybackWidget::PlaybackWidget(QWidget *parent) : QWidget(parent) {
.arg(Theme::rgba(Theme::Colors::PlaybackSurface),
Theme::hex(Theme::Colors::BorderSubtle)));
QVBoxLayout *mainLayout = new QVBoxLayout(this);
mainLayout->setContentsMargins(10, 5, 10, 10);
mainLayout->setContentsMargins(10, 5, 10, 2);
m_seekSlider = new TouchSlider(this);
m_seekSlider->setRange(0, 1000);
@ -42,12 +43,11 @@ PlaybackWidget::PlaybackWidget(QWidget *parent) : QWidget(parent) {
QString auxStyle = QString(
"QPushButton { background: transparent; color: %1; font-size: %2px; "
"border: none; padding: 10px;%3 } "
"QPushButton:pressed { color: %4; }")
"border: none; padding: 16px 20px; min-width: 48px; min-height: 48px; } "
"QPushButton:pressed { color: %3; }")
.arg(Theme::hex(Theme::Colors::TextSecondary))
.arg(Theme::Dims::BtnFontSize)
.arg(minSizeRule,
Theme::hex(Theme::Colors::TextPrimary));
.arg(Theme::hex(Theme::Colors::TextPrimary));
QPushButton *btnPrev = new QPushButton("<<", this);
btnPrev->setStyleSheet(btnStyle);
@ -62,23 +62,23 @@ PlaybackWidget::PlaybackWidget(QWidget *parent) : QWidget(parent) {
btnNext->setStyleSheet(btnStyle);
connect(btnNext, &QPushButton::clicked, this, &PlaybackWidget::nextClicked);
#if defined(PLATFORM_ANDROID)
QPushButton *btnSettings = new QPushButton("...", this);
#ifdef IS_MOBILE
rowLayout->addStretch();
rowLayout->addWidget(btnPrev);
rowLayout->addSpacing(10);
rowLayout->addWidget(m_btnPlay);
rowLayout->addSpacing(10);
rowLayout->addWidget(btnNext);
rowLayout->addStretch();
#else
QPushButton *btnSettings = new QPushButton("", this);
#endif
btnSettings->setStyleSheet(auxStyle);
connect(btnSettings, &QPushButton::clicked, this,
&PlaybackWidget::settingsClicked);
#if defined(PLATFORM_ANDROID)
QPushButton *btnHome = new QPushButton("<", this);
#else
QPushButton *btnHome = new QPushButton("", this);
#endif
btnHome->setStyleSheet(auxStyle);
connect(btnHome, &QPushButton::clicked, this, &PlaybackWidget::homeClicked);
rowLayout->addWidget(btnHome);
rowLayout->addStretch();
rowLayout->addWidget(btnPrev);
@ -88,6 +88,7 @@ PlaybackWidget::PlaybackWidget(QWidget *parent) : QWidget(parent) {
rowLayout->addWidget(btnNext);
rowLayout->addStretch();
rowLayout->addWidget(btnSettings);
#endif
mainLayout->addLayout(rowLayout);
}
@ -401,16 +402,70 @@ void SettingsWidget::onSmoothingChanged(int val) {
emitParams();
}
// --- SideButton ---
SideButton::SideButton(Icon icon, QWidget *parent)
: QWidget(parent), m_icon(icon) {
setAttribute(Qt::WA_TranslucentBackground);
}
void SideButton::paintEvent(QPaintEvent *) {
QPainter p(this);
p.setRenderHint(QPainter::Antialiasing);
int cx = width() / 2;
int cy = height() / 2;
float s = std::min(width(), height()) * 0.18f;
p.setPen(QPen(Theme::Colors::TextSecondary, s * 0.22f, Qt::SolidLine,
Qt::RoundCap, Qt::RoundJoin));
p.setBrush(Qt::NoBrush);
if (m_icon == Home) {
float gap = s * 0.35f;
float barH = s * 0.9f;
float barTop = cy;
p.drawLine(QPointF(cx - gap, barTop), QPointF(cx - gap, barTop + barH));
p.drawLine(QPointF(cx + gap, barTop), QPointF(cx + gap, barTop + barH));
float peakY = cy - s * 0.5f;
p.drawLine(QPointF(cx - s * 0.55f, cy + s * 0.1f), QPointF(cx, peakY));
p.drawLine(QPointF(cx, peakY), QPointF(cx + s * 0.55f, cy + s * 0.1f));
} else {
int rays = 6;
for (int i = 0; i < rays; i++) {
float a = i * M_PI / rays;
float dx = cos(a) * s * 0.7f;
float dy = sin(a) * s * 0.7f;
p.drawLine(QPointF(cx - dx, cy - dy), QPointF(cx + dx, cy + dy));
}
p.setPen(Qt::NoPen);
p.setBrush(Theme::Colors::TextSecondary);
p.drawEllipse(QPointF(cx, cy), s * 0.25f, s * 0.25f);
}
}
void SideButton::mousePressEvent(QMouseEvent *) { emit clicked(); }
// --- PlayerPage ---
PlayerPage::PlayerPage(QWidget *parent) : QWidget(parent) {
m_visualizer = new VisualizerWidget(this);
m_playback = new PlaybackWidget(this);
m_settings = new SettingsWidget();
m_overlay = new OverlayWidget(m_settings, this);
#ifdef IS_MOBILE
m_btnHome = new SideButton(SideButton::Home, this);
connect(m_btnHome, &SideButton::clicked, this, &PlayerPage::homeClicked);
m_btnSettings = new SideButton(SideButton::Settings, this);
connect(m_btnSettings, &SideButton::clicked, this, &PlayerPage::toggleOverlay);
#else
connect(m_playback, &PlaybackWidget::settingsClicked, this,
&PlayerPage::toggleOverlay);
connect(m_playback, &PlaybackWidget::homeClicked, this,
&PlayerPage::homeClicked);
#endif
connect(m_settings, &SettingsWidget::closeClicked, this,
&PlayerPage::closeOverlay);
@ -449,18 +504,30 @@ PlayerPage::PlayerPage(QWidget *parent) : QWidget(parent) {
}
}
void PlayerPage::setFullScreen(bool fs) { m_playback->setVisible(!fs); }
void PlayerPage::setFullScreen(bool fs) {
m_playback->setVisible(!fs);
if (m_btnHome) m_btnHome->setVisible(!fs);
if (m_btnSettings) m_btnSettings->setVisible(!fs);
}
void PlayerPage::toggleOverlay() {
if (m_overlay->isVisible())
if (m_overlay->isVisible()) {
m_overlay->hide();
else {
if (m_btnHome) m_btnHome->show();
if (m_btnSettings) m_btnSettings->show();
} else {
if (m_btnHome) m_btnHome->hide();
if (m_btnSettings) m_btnSettings->hide();
m_overlay->raise();
m_overlay->show();
}
}
void PlayerPage::closeOverlay() { m_overlay->hide(); }
void PlayerPage::closeOverlay() {
m_overlay->hide();
if (m_btnHome) m_btnHome->show();
if (m_btnSettings) m_btnSettings->show();
}
void PlayerPage::resizeEvent(QResizeEvent *event) {
int w = event->size().width();
@ -475,4 +542,19 @@ void PlayerPage::resizeEvent(QResizeEvent *event) {
if (m_expandedPad)
m_expandedPad->setGeometry(0, 0, w, h);
if (m_btnHome) {
int bw = w / 5;
int bh = h / 4;
int cy = h / 2 - bh / 2;
m_btnHome->setGeometry(0, cy, bw, bh);
m_btnHome->raise();
}
if (m_btnSettings) {
int bw = w / 5;
int bh = h / 4;
int cy = h / 2 - bh / 2;
m_btnSettings->setGeometry(w - bw, cy, bw, bh);
m_btnSettings->raise();
}
}

View File

@ -130,6 +130,20 @@ private:
std::function<QString(float, float)> m_colorFormatter;
};
class SideButton : public QWidget {
Q_OBJECT
public:
enum Icon { Home, Settings };
SideButton(Icon icon, QWidget *parent = nullptr);
signals:
void clicked();
protected:
void paintEvent(QPaintEvent *) override;
void mousePressEvent(QMouseEvent *) override;
private:
Icon m_icon;
};
class PlayerPage : public QWidget {
Q_OBJECT
public:
@ -155,4 +169,6 @@ private:
OverlayWidget *m_overlay;
ExpandedXYPad *m_expandedPad = nullptr;
bool m_expandedPadIsDsp = true;
SideButton *m_btnHome = nullptr;
SideButton *m_btnSettings = nullptr;
};