Store release setup: app icon, launch screen, iOS config, Android signing, Codemagic

- App icon from the NK emblem (iOS set, Android adaptive icon, Play 512 icon)
- Dark launch screen on both platforms; app locked to portrait; iPhone-only
- Info.plist: display name, photo/camera reasons, export compliance
- Android release signing from key.properties or Codemagic CM_KEYSTORE_*
- codemagic.yaml: iOS to TestFlight, Android to Play internal testing
- Account deletion web page; fix trailing dot in email links
- docs/app-store-release.md: step-by-step store guide

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
metatroncubeswdev 2026-10-31 00:06:31 -04:00
parent d257fc4c65
commit fc989b4c77
45 changed files with 283 additions and 33 deletions

View File

@ -3,6 +3,7 @@
*Our song*: a music app for the Badaga people of the Nilgiris. Flutter app for iOS and Android. *Our song*: a music app for the Badaga people of the Nilgiris. Flutter app for iOS and Android.
- Plan: [docs/NK_IMPLEMENTATION_PLAN.md](docs/NK_IMPLEMENTATION_PLAN.md) - Plan: [docs/NK_IMPLEMENTATION_PLAN.md](docs/NK_IMPLEMENTATION_PLAN.md)
- Store release (Codemagic, TestFlight, Play): [docs/app-store-release.md](docs/app-store-release.md)
- Design canvas: https://claude.ai/artifact/9ZLCbGn4rUTVTqfRXTtMkq - Design canvas: https://claude.ai/artifact/9ZLCbGn4rUTVTqfRXTtMkq
## Run ## Run

View File

@ -1,9 +1,19 @@
import java.util.Properties
plugins { plugins {
id("com.android.application") id("com.android.application")
// The Flutter Gradle Plugin must be applied after the Android and Kotlin Gradle plugins. // The Flutter Gradle Plugin must be applied after the Android and Kotlin Gradle plugins.
id("dev.flutter.flutter-gradle-plugin") id("dev.flutter.flutter-gradle-plugin")
} }
// Release signing: android/key.properties on a developer machine, or the CM_KEYSTORE_* variables
// that Codemagic sets. Without either, release builds fall back to the debug key (local testing only).
val keyProps = Properties().apply {
val f = rootProject.file("key.properties")
if (f.exists()) f.inputStream().use { load(it) }
}
val releaseStoreFile: String? = keyProps.getProperty("storeFile") ?: System.getenv("CM_KEYSTORE_PATH")
android { android {
namespace = "com.metatroncubesolutions.nanga_kathai" namespace = "com.metatroncubesolutions.nanga_kathai"
compileSdk = flutter.compileSdkVersion compileSdk = flutter.compileSdkVersion
@ -15,7 +25,6 @@ android {
} }
defaultConfig { defaultConfig {
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
applicationId = "com.metatroncubesolutions.nanga_kathai" applicationId = "com.metatroncubesolutions.nanga_kathai"
// You can update the following values to match your application needs. // You can update the following values to match your application needs.
// For more information, see: https://flutter.dev/to/review-gradle-config. // For more information, see: https://flutter.dev/to/review-gradle-config.
@ -29,11 +38,20 @@ android {
versionName = flutter.versionName versionName = flutter.versionName
} }
signingConfigs {
if (releaseStoreFile != null) {
create("release") {
storeFile = file(releaseStoreFile)
storePassword = keyProps.getProperty("storePassword") ?: System.getenv("CM_KEYSTORE_PASSWORD")
keyAlias = keyProps.getProperty("keyAlias") ?: System.getenv("CM_KEY_ALIAS")
keyPassword = keyProps.getProperty("keyPassword") ?: System.getenv("CM_KEY_PASSWORD")
}
}
}
buildTypes { buildTypes {
release { release {
// TODO: Add your own signing config for the release build. signingConfig = signingConfigs.getByName(if (releaseStoreFile != null) "release" else "debug")
// Signing with the debug keys for now, so `flutter run --release` works.
signingConfig = signingConfigs.getByName("debug")
} }
} }
} }

View File

@ -1,12 +1,5 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<!-- Modify this file to customize your launch splash screen --> <!-- Launch screen: the app's night-tea background, so there is no white flash. -->
<layer-list xmlns:android="http://schemas.android.com/apk/res/android"> <layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="?android:colorBackground" /> <item android:drawable="@color/nk_ground" />
<!-- You can insert your own image assets here -->
<!-- <item>
<bitmap
android:gravity="center"
android:src="@mipmap/launch_image" />
</item> -->
</layer-list> </layer-list>

View File

@ -1,12 +1,5 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<!-- Modify this file to customize your launch splash screen --> <!-- Launch screen: the app's night-tea background, so there is no white flash. -->
<layer-list xmlns:android="http://schemas.android.com/apk/res/android"> <layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@android:color/white" /> <item android:drawable="@color/nk_ground" />
<!-- You can insert your own image assets here -->
<!-- <item>
<bitmap
android:gravity="center"
android:src="@mipmap/launch_image" />
</item> -->
</layer-list> </layer-list>

View File

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@color/ic_launcher_background"/>
<foreground android:drawable="@mipmap/ic_launcher_foreground"/>
</adaptive-icon>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 544 B

After

Width:  |  Height:  |  Size: 8.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 442 B

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 721 B

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.0 KiB

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 64 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 43 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 104 KiB

View File

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="ic_launcher_background">#FDFCF5</color>
<color name="nk_ground">#0B1A14</color>
</resources>

Binary file not shown.

After

Width:  |  Height:  |  Size: 711 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 447 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 231 KiB

107
codemagic.yaml Normal file
View File

@ -0,0 +1,107 @@
# Codemagic builds for Nanga Kathai. Setup steps: docs/app-store-release.md
#
# Needed in Codemagic (Team settings / app settings):
# - Environment variable group "nk_app": SUPABASE_URL, SUPABASE_PUBLISHABLE_KEY, MEDIA_BASE_URL
# - App Store Connect API key integration named "NK App Store Connect" (iOS)
# - Android keystore uploaded with reference name "nk_upload_keystore" (Android)
# - Variable GCLOUD_SERVICE_ACCOUNT_CREDENTIALS in group "nk_google_play" (Android publishing)
definitions:
write_env: &write_env
name: Write env.json from Codemagic variables
script: |
cat > env.json <<EOF
{
"SUPABASE_URL": "$SUPABASE_URL",
"SUPABASE_PUBLISHABLE_KEY": "$SUPABASE_PUBLISHABLE_KEY",
"MEDIA_BASE_URL": "$MEDIA_BASE_URL"
}
EOF
checks: &checks
name: Analyze and test
script: |
flutter pub get
flutter analyze --no-fatal-infos
flutter test
email: &email
email:
recipients:
- info@metatroncubesolutions.com
notify:
success: true
failure: true
workflows:
ios-release:
name: iOS release (TestFlight)
instance_type: mac_mini_m2
max_build_duration: 60
integrations:
app_store_connect: NK App Store Connect
environment:
groups:
- nk_app
flutter: stable
xcode: latest
cocoapods: default
ios_signing:
distribution_type: app_store
bundle_identifier: com.metatroncubesolutions.nangaKathai
triggering:
events:
- tag
tag_patterns:
- pattern: 'v*'
scripts:
- *write_env
- *checks
- name: Use the App Store signing profiles
script: xcode-project use-profiles
- name: Build the iOS app
script: |
flutter build ipa --release \
--build-number=$BUILD_NUMBER \
--dart-define-from-file=env.json \
--export-options-plist=/Users/builder/export_options.plist
artifacts:
- build/ios/ipa/*.ipa
- /tmp/xcodebuild_logs/*.log
publishing:
<<: *email
app_store_connect:
auth: integration
submit_to_testflight: true
android-release:
name: Android release (Play internal testing)
instance_type: linux_x2
max_build_duration: 45
environment:
groups:
- nk_app
- nk_google_play
flutter: stable
android_signing:
- nk_upload_keystore
triggering:
events:
- tag
tag_patterns:
- pattern: 'v*'
scripts:
- *write_env
- *checks
- name: Build the Android app bundle
script: |
flutter build appbundle --release \
--build-number=$((BUILD_NUMBER + 1)) \
--dart-define-from-file=env.json
artifacts:
- build/app/outputs/bundle/release/*.aab
- build/app/outputs/mapping/release/mapping.txt
publishing:
<<: *email
google_play:
credentials: $GCLOUD_SERVICE_ACCOUNT_CREDENTIALS
track: internal
submit_as_draft: true

96
docs/app-store-release.md Normal file
View File

@ -0,0 +1,96 @@
# Releasing Nanga Kathai to Google Play and the App Store
| | Android | iOS |
|---|---|---|
| App ID | `com.metatroncubesolutions.nanga_kathai` | `com.metatroncubesolutions.nangaKathai` |
| Built by | Codemagic (`android-release`) or locally | Codemagic (`ios-release`) |
| First stop | Play Console, Internal testing | TestFlight |
The version comes from `pubspec.yaml` (`version: 1.0.0+1`). Raise the first part (1.0.1, 1.1.0…) for
each store release. Build numbers are set automatically by Codemagic.
## 1. Accounts (one-time)
- **Apple Developer Program** (US$99 a year), enrolled as an organisation. This needs a D-U-N-S
number for Metatroncube Software Solutions.
- **Google Play Console** (US$25 once), as an organisation account. A *personal* account must run
a closed test with at least 12 testers for 14 days before it can publish, so use an organisation account.
- **Codemagic**, signed in with GitHub, with the `metatroncubeswdev/nkbmusic_app` repo added.
## 2. Codemagic setup (one-time)
1. **Variables.** Open App settings > Environment variables and create the group **`nk_app`** with:
- `SUPABASE_URL` = `https://app.nkbmusic.com`
- `SUPABASE_PUBLISHABLE_KEY` = the value from your local `env.json`
- `MEDIA_BASE_URL` = `https://app.nkbmusic.com/storage/v1/object/public/audio`
2. **App Store Connect API key.**
- In App Store Connect, open Users and Access > Integrations > App Store Connect API and generate a key with the **App Manager** role.
- In Codemagic, open Team settings > Team integrations > Developer Portal and add the key with the name **`NK App Store Connect`**.
3. **iOS signing.** Open Team settings > codemagic.yaml settings > Code signing identities.
- Under iOS certificates, generate an **Apple Distribution** certificate.
- Under iOS provisioning profiles, fetch the **App Store** profile for `com.metatroncubesolutions.nangaKathai`. Create that profile on developer.apple.com first if it doesn't exist (see step 3.1).
4. **Android signing.** On the same page, under Android keystores, upload
`C:\Users\manes\nk-keys\nk-upload.jks` with the reference name **`nk_upload_keystore`**,
alias `upload`. The password is in `C:\Users\manes\nk-keys\key.properties.backup`.
5. **Google Play publishing (optional).**
- Create a Google Cloud service account, invite it in Play Console > Users and permissions with release rights, and download its JSON key.
- Store the key as `GCLOUD_SERVICE_ACCOUNT_CREDENTIALS` (secure) in a variable group **`nk_google_play`**.
## 3. iOS: first release
1. On developer.apple.com, open Identifiers and register the App ID
`com.metatroncubesolutions.nangaKathai`. In-App Purchase is on by default. Background audio
needs nothing extra.
2. In App Store Connect, create the app with the name **Nanga Kathai**, that bundle ID, and primary language English (India or UK).
3. In Codemagic, start the **iOS release (TestFlight)** workflow, or push a tag:
`git tag v1.0.0 && git push github v1.0.0`.
The build is uploaded to TestFlight automatically after about 20–30 minutes plus Apple processing.
4. Test it from the TestFlight app on an iPhone.
5. Fill in the listing (section 5) and submit for review.
## 4. Android: first release
1. In Play Console, create the app: Nanga Kathai, App, Free.
2. Build the first bundle locally and upload it manually. Google requires the very first upload to be done by hand.
```
flutter build appbundle --release --dart-define-from-file=env.json
```
The file is `build/app/outputs/bundle/release/app-release.aab`. Upload it under Testing >
Internal testing > Create release. Accept **Play App Signing**: Google keeps the app signing key,
and you sign uploads with `nk-upload.jks`.
3. Later builds come from the Codemagic **Android release** workflow, which uploads a draft to Internal testing.
4. Promote it from Internal testing, to Closed testing (optional), to Production.
## 5. Store listing checklist
**Links (publish the files from `site/` on nkbmusic.com first)**
- Privacy Policy: `https://nkbmusic.com/privacy`
- Terms: `https://nkbmusic.com/terms`
- Account deletion (required by Play): `https://nkbmusic.com/delete-account`
- Support: `support@nkbmusic.com`
**Graphics** (in `assets/brand/`: `app_icon_1024.png` and `play_store_icon_512.png`)
- Play: a 512×512 icon, a 1024×500 feature graphic, and 2–8 phone screenshots.
- App Store: 6.9-inch iPhone screenshots (1320×2868). The app is iPhone-only, so no iPad screenshots are needed.
**Questionnaires**
- **Data collected** (Play Data safety and Apple App Privacy):
- email, name, optional phone and photo;
- app interactions (plays) and crash/diagnostics data if crash reporting is added;
- not used for tracking or ads, data encrypted in transit, users can request deletion.
- **Content rating:** music app, no violence and no user chat. Expect *Everyone* / *4+*.
- **Ads:** none. **In-app purchases:** yes (the Support products in `docs/support-payments.md`).
**App Review notes (important)**
- Create a reviewer account (for example `review@nkbmusic.com`) with a password, and enter it as the sign-in details. Apple and Google can't receive the email code.
- **Music rights** are the most likely reason for a rejection (Apple guideline 5.2.3 and Google's IP
policy). Explain it in the notes: *"Nanga Kathai preserves Badaga community music from a family
collection. Every song has an in-app 'Is this your song?' claim or removal option, and we act on
requests promptly. New songs will come only from verified creators who confirm their rights."*
Keep written permission from any singers you can reach, in case review asks.
## 6. Keys: back these up now
`C:\Users\manes\nk-keys\` holds the Android upload key and its password. Copy that folder to
two safe places, such as a password manager and an offline drive. If it is lost, Google can reset
the upload key, but that takes a support request and several days. Never commit it to git.

View File

@ -365,7 +365,7 @@
SDKROOT = iphoneos; SDKROOT = iphoneos;
STRING_CATALOG_GENERATE_SYMBOLS = YES; STRING_CATALOG_GENERATE_SYMBOLS = YES;
SUPPORTED_PLATFORMS = iphoneos; SUPPORTED_PLATFORMS = iphoneos;
TARGETED_DEVICE_FAMILY = "1,2"; TARGETED_DEVICE_FAMILY = 1;
VALIDATE_PRODUCT = YES; VALIDATE_PRODUCT = YES;
}; };
name = Profile; name = Profile;
@ -492,7 +492,7 @@
ONLY_ACTIVE_ARCH = YES; ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos; SDKROOT = iphoneos;
STRING_CATALOG_GENERATE_SYMBOLS = YES; STRING_CATALOG_GENERATE_SYMBOLS = YES;
TARGETED_DEVICE_FAMILY = "1,2"; TARGETED_DEVICE_FAMILY = 1;
}; };
name = Debug; name = Debug;
}; };
@ -546,7 +546,7 @@
SUPPORTED_PLATFORMS = iphoneos; SUPPORTED_PLATFORMS = iphoneos;
SWIFT_COMPILATION_MODE = wholemodule; SWIFT_COMPILATION_MODE = wholemodule;
SWIFT_OPTIMIZATION_LEVEL = "-O"; SWIFT_OPTIMIZATION_LEVEL = "-O";
TARGETED_DEVICE_FAMILY = "1,2"; TARGETED_DEVICE_FAMILY = 1;
VALIDATE_PRODUCT = YES; VALIDATE_PRODUCT = YES;
}; };
name = Release; name = Release;

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 711 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 295 B

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 406 B

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 450 B

After

Width:  |  Height:  |  Size: 5.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 282 B

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 462 B

After

Width:  |  Height:  |  Size: 5.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 704 B

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 406 B

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 586 B

After

Width:  |  Height:  |  Size: 9.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 862 B

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 862 B

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

After

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 762 B

After

Width:  |  Height:  |  Size: 8.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 34 KiB

View File

@ -19,7 +19,7 @@
<imageView opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" image="LaunchImage" translatesAutoresizingMaskIntoConstraints="NO" id="YRO-k0-Ey4"> <imageView opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" image="LaunchImage" translatesAutoresizingMaskIntoConstraints="NO" id="YRO-k0-Ey4">
</imageView> </imageView>
</subviews> </subviews>
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> <color key="backgroundColor" red="0.043" green="0.102" blue="0.078" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<constraints> <constraints>
<constraint firstItem="YRO-k0-Ey4" firstAttribute="centerX" secondItem="Ze5-6b-2t3" secondAttribute="centerX" id="1a2-6s-vTC"/> <constraint firstItem="YRO-k0-Ey4" firstAttribute="centerX" secondItem="Ze5-6b-2t3" secondAttribute="centerX" id="1a2-6s-vTC"/>
<constraint firstItem="YRO-k0-Ey4" firstAttribute="centerY" secondItem="Ze5-6b-2t3" secondAttribute="centerY" id="4X2-HB-R7a"/> <constraint firstItem="YRO-k0-Ey4" firstAttribute="centerY" secondItem="Ze5-6b-2t3" secondAttribute="centerY" id="4X2-HB-R7a"/>

View File

@ -15,7 +15,7 @@
<key>CFBundleInfoDictionaryVersion</key> <key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string> <string>6.0</string>
<key>CFBundleName</key> <key>CFBundleName</key>
<string>nanga_kathai</string> <string>Nanga Kathai</string>
<key>CFBundlePackageType</key> <key>CFBundlePackageType</key>
<string>APPL</string> <string>APPL</string>
<key>CFBundleShortVersionString</key> <key>CFBundleShortVersionString</key>
@ -24,6 +24,17 @@
<string>????</string> <string>????</string>
<key>CFBundleVersion</key> <key>CFBundleVersion</key>
<string>$(FLUTTER_BUILD_NUMBER)</string> <string>$(FLUTTER_BUILD_NUMBER)</string>
<key>ITSAppUsesNonExemptEncryption</key>
<false/>
<key>NSPhotoLibraryUsageDescription</key>
<string>Choose a photo for your Nanga Kathai profile.</string>
<key>NSCameraUsageDescription</key>
<string>Take a photo for your Nanga Kathai profile.</string>
<key>LSApplicationQueriesSchemes</key>
<array>
<string>mailto</string>
<string>https</string>
</array>
<key>LSRequiresIPhoneOS</key> <key>LSRequiresIPhoneOS</key>
<true/> <true/>
<key>UIApplicationSceneManifest</key> <key>UIApplicationSceneManifest</key>
@ -60,8 +71,6 @@
<key>UISupportedInterfaceOrientations</key> <key>UISupportedInterfaceOrientations</key>
<array> <array>
<string>UIInterfaceOrientationPortrait</string> <string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array> </array>
<key>UISupportedInterfaceOrientations~ipad</key> <key>UISupportedInterfaceOrientations~ipad</key>
<array> <array>

View File

@ -74,7 +74,7 @@ class DocumentScreen extends StatelessWidget {
/// **bold** and tappable email addresses. /// **bold** and tappable email addresses.
static TextSpan _inline(String s, TextStyle? base) { static TextSpan _inline(String s, TextStyle? base) {
final spans = <InlineSpan>[]; final spans = <InlineSpan>[];
final pattern = RegExp(r'\*\*(.+?)\*\*|([\w.+-]+@[\w-]+\.[\w.]+)'); final pattern = RegExp(r'\*\*(.+?)\*\*|([\w.+-]+@[\w-]+(?:\.[\w-]+)+)');
var last = 0; var last = 0;
for (final m in pattern.allMatches(s)) { for (final m in pattern.allMatches(s)) {
if (m.start > last) spans.add(TextSpan(text: s.substring(last, m.start))); if (m.start > last) spans.add(TextSpan(text: s.substring(last, m.start)));

View File

@ -1,5 +1,6 @@
import 'package:audio_service/audio_service.dart'; import 'package:audio_service/audio_service.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:go_router/go_router.dart'; import 'package:go_router/go_router.dart';
import 'package:shared_preferences/shared_preferences.dart'; import 'package:shared_preferences/shared_preferences.dart';
@ -17,6 +18,7 @@ import 'offline/download_store.dart';
Future<void> main() async { Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized(); WidgetsFlutterBinding.ensureInitialized();
await SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]);
if (Env.hasSupabase) { if (Env.hasSupabase) {
await Supabase.initialize(url: Env.supabaseUrl, publishableKey: Env.supabasePublishableKey); await Supabase.initialize(url: Env.supabaseUrl, publishableKey: Env.supabasePublishableKey);

21
site/delete-account.html Normal file
View File

@ -0,0 +1,21 @@
<!doctype html>
<html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1">
<title>Delete your account | Nanga Kathai</title>
<style>
body{margin:0;background:#0B1A14;color:#DCE5DF;font:17px/1.6 system-ui,-apple-system,"Segoe UI",sans-serif}
main{max-width:720px;margin:0 auto;padding:40px 20px 64px}
h1{color:#FBF7EE;font-size:32px;margin:0 0 4px} h2{color:#FBF7EE;font-size:21px;margin:32px 0 8px}
strong{color:#FBF7EE} a{color:#F5B335} li{margin:4px 0} .brand{color:#F5B335;font-weight:700;text-decoration:none}
</style></head>
<body><main><a class="brand" href="https://nkbmusic.com">Nanga Kathai</a>
<h1>Delete your Nanga Kathai account</h1>
<p>You can delete your Nanga Kathai account, made by Metatroncube Software Solutions, at any time.</p>
<h2>In the app</h2>
<ol><li>Open Nanga Kathai and tap the person icon on Home or in Library.</li><li>Scroll down and tap <strong>Delete account</strong>.</li><li>Confirm. Your account is deleted straight away.</li></ol>
<h2>Without the app</h2>
<p>Email <a href="mailto:support@nkbmusic.com?subject=Delete%20my%20account">support@nkbmusic.com</a> from the address you signed up with and ask us to delete your account. We will confirm within 7 days.</p>
<h2>What is deleted</h2>
<ul><li>Your account, email address and password.</li><li>Your profile: name, photo, village and phone number.</li><li>Your likes, playlists and listening history on our servers.</li></ul>
<p>Copies in our backups are overwritten within about 14 days. Song reports or rights requests you sent may be kept, without your account details, where we need a record of them. Songs you downloaded stay encrypted on your phone until you remove the app.</p>
<p>See our <a href="/privacy">Privacy Policy</a> for more.</p>
</main></body></html>

View File

@ -30,7 +30,7 @@ strong{color:#FBF7EE} a{color:#F5B335} li{margin:4px 0} .brand{color:#F5B335;fon
<h2>How long we keep it</h2> <h2>How long we keep it</h2>
<p>We keep your account details for as long as your account exists. When you delete your account, your profile, likes, playlists and history on our servers are removed. Backups are overwritten within about 14 days. Reports you sent may be kept, without your account, where we need a record of a rights or removal request.</p> <p>We keep your account details for as long as your account exists. When you delete your account, your profile, likes, playlists and history on our servers are removed. Backups are overwritten within about 14 days. Reports you sent may be kept, without your account, where we need a record of a rights or removal request.</p>
<h2>Your choices and rights</h2> <h2>Your choices and rights</h2>
<ul><li><strong>See and edit</strong> your profile in the app (Library &gt; your profile).</li><li><strong>Delete your account</strong> from your profile page. This cannot be undone.</li><li><strong>Ask us</strong> for a copy of your data, a correction, or anything else about your privacy by writing to <a href="mailto:support@nkbmusic.com.">support@nkbmusic.com.</a></li></ul> <ul><li><strong>See and edit</strong> your profile in the app (Library &gt; your profile).</li><li><strong>Delete your account</strong> from your profile page. This cannot be undone.</li><li><strong>Ask us</strong> for a copy of your data, a correction, or anything else about your privacy by writing to <a href="mailto:support@nkbmusic.com">support@nkbmusic.com</a>.</li></ul>
<p>These rights apply wherever you live, including your rights under India&#x27;s Digital Personal Data Protection Act, 2023.</p> <p>These rights apply wherever you live, including your rights under India&#x27;s Digital Personal Data Protection Act, 2023.</p>
<h2>Children</h2> <h2>Children</h2>
<p>Nanga Kathai is meant for listeners of all ages, but accounts are intended for people aged 18 and over. Younger listeners should use the app with the consent and supervision of a parent or guardian.</p> <p>Nanga Kathai is meant for listeners of all ages, but accounts are intended for people aged 18 and over. Younger listeners should use the app with the consent and supervision of a parent or guardian.</p>

View File

@ -18,7 +18,7 @@ strong{color:#FBF7EE} a{color:#F5B335} li{margin:4px 0} .brand{color:#F5B335;fon
<h2>Our older recordings</h2> <h2>Our older recordings</h2>
<p>Our first songs come from a family collection gathered over many years, and not every artist has been identified yet. We respect the people behind these songs. If you are an artist, rights holder or family member, you can, at any time:</p> <p>Our first songs come from a family collection gathered over many years, and not every artist has been identified yet. We respect the people behind these songs. If you are an artist, rights holder or family member, you can, at any time:</p>
<ul><li><strong>claim a song</strong> so we credit you correctly,</li><li><strong>ask us to correct</strong> its details, or</li><li><strong>ask us to remove it.</strong></li></ul> <ul><li><strong>claim a song</strong> so we credit you correctly,</li><li><strong>ask us to correct</strong> its details, or</li><li><strong>ask us to remove it.</strong></li></ul>
<p>Use &quot;Is this your song?&quot; on the song in the app, or write to <a href="mailto:support@nkbmusic.com.">support@nkbmusic.com.</a> We review every request and act on genuine removal requests promptly.</p> <p>Use &quot;Is this your song?&quot; on the song in the app, or write to <a href="mailto:support@nkbmusic.com">support@nkbmusic.com</a>. We review every request and act on genuine removal requests promptly.</p>
<h2>Creators</h2> <h2>Creators</h2>
<p>In a future update, singers, lyricists and music directors will be able to apply to become verified creators. Verification will be manual and may require identity documents. Creators who upload songs will confirm that they own them or have permission from everyone with rights in them, and will accept separate creator terms. Any way for listeners to support creators with money, and any fee we keep, will be set out in those terms before it begins.</p> <p>In a future update, singers, lyricists and music directors will be able to apply to become verified creators. Verification will be manual and may require identity documents. Creators who upload songs will confirm that they own them or have permission from everyone with rights in them, and will accept separate creator terms. Any way for listeners to support creators with money, and any fee we keep, will be set out in those terms before it begins.</p>
<h2>Supporting Nanga Kathai</h2> <h2>Supporting Nanga Kathai</h2>

View File

@ -24,7 +24,7 @@ strong{{color:#FBF7EE}} a{{color:#F5B335}} li{{margin:4px 0}} .brand{{color:#F5B
def inline(s): def inline(s):
s = html.escape(s) s = html.escape(s)
s = re.sub(r"\*\*(.+?)\*\*", r"<strong>\1</strong>", s) s = re.sub(r"\*\*(.+?)\*\*", r"<strong>\1</strong>", s)
return re.sub(r"([\w.+-]+@[\w-]+\.[\w.]+)", r'<a href="mailto:\1">\1</a>', s) return re.sub(r"([\w.+-]+@[\w-]+(?:\.[\w-]+)+)", r'<a href="mailto:\1">\1</a>', s)
def convert(md): def convert(md):