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>
@ -3,6 +3,7 @@
|
||||
*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)
|
||||
- Store release (Codemagic, TestFlight, Play): [docs/app-store-release.md](docs/app-store-release.md)
|
||||
- Design canvas: https://claude.ai/artifact/9ZLCbGn4rUTVTqfRXTtMkq
|
||||
|
||||
## Run
|
||||
|
||||
@ -1,9 +1,19 @@
|
||||
import java.util.Properties
|
||||
|
||||
plugins {
|
||||
id("com.android.application")
|
||||
// The Flutter Gradle Plugin must be applied after the Android and Kotlin Gradle plugins.
|
||||
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 {
|
||||
namespace = "com.metatroncubesolutions.nanga_kathai"
|
||||
compileSdk = flutter.compileSdkVersion
|
||||
@ -15,7 +25,6 @@ android {
|
||||
}
|
||||
|
||||
defaultConfig {
|
||||
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
|
||||
applicationId = "com.metatroncubesolutions.nanga_kathai"
|
||||
// You can update the following values to match your application needs.
|
||||
// For more information, see: https://flutter.dev/to/review-gradle-config.
|
||||
@ -29,11 +38,20 @@ android {
|
||||
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 {
|
||||
release {
|
||||
// TODO: Add your own signing config for the release build.
|
||||
// Signing with the debug keys for now, so `flutter run --release` works.
|
||||
signingConfig = signingConfigs.getByName("debug")
|
||||
signingConfig = signingConfigs.getByName(if (releaseStoreFile != null) "release" else "debug")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,12 +1,5 @@
|
||||
<?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">
|
||||
<item android:drawable="?android:colorBackground" />
|
||||
|
||||
<!-- You can insert your own image assets here -->
|
||||
<!-- <item>
|
||||
<bitmap
|
||||
android:gravity="center"
|
||||
android:src="@mipmap/launch_image" />
|
||||
</item> -->
|
||||
<item android:drawable="@color/nk_ground" />
|
||||
</layer-list>
|
||||
|
||||
@ -1,12 +1,5 @@
|
||||
<?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">
|
||||
<item android:drawable="@android:color/white" />
|
||||
|
||||
<!-- You can insert your own image assets here -->
|
||||
<!-- <item>
|
||||
<bitmap
|
||||
android:gravity="center"
|
||||
android:src="@mipmap/launch_image" />
|
||||
</item> -->
|
||||
<item android:drawable="@color/nk_ground" />
|
||||
</layer-list>
|
||||
|
||||
@ -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>
|
||||
|
Before Width: | Height: | Size: 544 B After Width: | Height: | Size: 8.1 KiB |
BIN
android/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png
Normal file
|
After Width: | Height: | Size: 20 KiB |
|
Before Width: | Height: | Size: 442 B After Width: | Height: | Size: 4.1 KiB |
BIN
android/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png
Normal file
|
After Width: | Height: | Size: 9.8 KiB |
|
Before Width: | Height: | Size: 721 B After Width: | Height: | Size: 13 KiB |
BIN
android/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png
Normal file
|
After Width: | Height: | Size: 32 KiB |
|
Before Width: | Height: | Size: 1.0 KiB After Width: | Height: | Size: 26 KiB |
|
After Width: | Height: | Size: 64 KiB |
|
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 43 KiB |
|
After Width: | Height: | Size: 104 KiB |
@ -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>
|
||||
BIN
assets/brand/app_icon_1024.png
Normal file
|
After Width: | Height: | Size: 711 KiB |
BIN
assets/brand/app_icon_foreground.png
Normal file
|
After Width: | Height: | Size: 447 KiB |
BIN
assets/brand/play_store_icon_512.png
Normal file
|
After Width: | Height: | Size: 231 KiB |
107
codemagic.yaml
Normal 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
@ -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.
|
||||
@ -365,7 +365,7 @@
|
||||
SDKROOT = iphoneos;
|
||||
STRING_CATALOG_GENERATE_SYMBOLS = YES;
|
||||
SUPPORTED_PLATFORMS = iphoneos;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
TARGETED_DEVICE_FAMILY = 1;
|
||||
VALIDATE_PRODUCT = YES;
|
||||
};
|
||||
name = Profile;
|
||||
@ -492,7 +492,7 @@
|
||||
ONLY_ACTIVE_ARCH = YES;
|
||||
SDKROOT = iphoneos;
|
||||
STRING_CATALOG_GENERATE_SYMBOLS = YES;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
TARGETED_DEVICE_FAMILY = 1;
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
@ -546,7 +546,7 @@
|
||||
SUPPORTED_PLATFORMS = iphoneos;
|
||||
SWIFT_COMPILATION_MODE = wholemodule;
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-O";
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
TARGETED_DEVICE_FAMILY = 1;
|
||||
VALIDATE_PRODUCT = YES;
|
||||
};
|
||||
name = Release;
|
||||
|
||||
|
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 711 KiB |
|
Before Width: | Height: | Size: 295 B After Width: | Height: | Size: 1.0 KiB |
|
Before Width: | Height: | Size: 406 B After Width: | Height: | Size: 3.0 KiB |
|
Before Width: | Height: | Size: 450 B After Width: | Height: | Size: 5.9 KiB |
|
Before Width: | Height: | Size: 282 B After Width: | Height: | Size: 1.8 KiB |
|
Before Width: | Height: | Size: 462 B After Width: | Height: | Size: 5.6 KiB |
|
Before Width: | Height: | Size: 704 B After Width: | Height: | Size: 11 KiB |
|
Before Width: | Height: | Size: 406 B After Width: | Height: | Size: 3.0 KiB |
|
Before Width: | Height: | Size: 586 B After Width: | Height: | Size: 9.7 KiB |
|
Before Width: | Height: | Size: 862 B After Width: | Height: | Size: 20 KiB |
|
Before Width: | Height: | Size: 862 B After Width: | Height: | Size: 20 KiB |
|
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 39 KiB |
|
Before Width: | Height: | Size: 762 B After Width: | Height: | Size: 8.9 KiB |
|
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 29 KiB |
|
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 34 KiB |
@ -19,7 +19,7 @@
|
||||
<imageView opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" image="LaunchImage" translatesAutoresizingMaskIntoConstraints="NO" id="YRO-k0-Ey4">
|
||||
</imageView>
|
||||
</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>
|
||||
<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"/>
|
||||
|
||||
@ -15,7 +15,7 @@
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>nanga_kathai</string>
|
||||
<string>Nanga Kathai</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
@ -24,6 +24,17 @@
|
||||
<string>????</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<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>
|
||||
<true/>
|
||||
<key>UIApplicationSceneManifest</key>
|
||||
@ -60,8 +71,6 @@
|
||||
<key>UISupportedInterfaceOrientations</key>
|
||||
<array>
|
||||
<string>UIInterfaceOrientationPortrait</string>
|
||||
<string>UIInterfaceOrientationLandscapeLeft</string>
|
||||
<string>UIInterfaceOrientationLandscapeRight</string>
|
||||
</array>
|
||||
<key>UISupportedInterfaceOrientations~ipad</key>
|
||||
<array>
|
||||
|
||||
@ -74,7 +74,7 @@ class DocumentScreen extends StatelessWidget {
|
||||
/// **bold** and tappable email addresses.
|
||||
static TextSpan _inline(String s, TextStyle? base) {
|
||||
final spans = <InlineSpan>[];
|
||||
final pattern = RegExp(r'\*\*(.+?)\*\*|([\w.+-]+@[\w-]+\.[\w.]+)');
|
||||
final pattern = RegExp(r'\*\*(.+?)\*\*|([\w.+-]+@[\w-]+(?:\.[\w-]+)+)');
|
||||
var last = 0;
|
||||
for (final m in pattern.allMatches(s)) {
|
||||
if (m.start > last) spans.add(TextSpan(text: s.substring(last, m.start)));
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
import 'package:audio_service/audio_service.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
@ -17,6 +18,7 @@ import 'offline/download_store.dart';
|
||||
|
||||
Future<void> main() async {
|
||||
WidgetsFlutterBinding.ensureInitialized();
|
||||
await SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]);
|
||||
|
||||
if (Env.hasSupabase) {
|
||||
await Supabase.initialize(url: Env.supabaseUrl, publishableKey: Env.supabasePublishableKey);
|
||||
|
||||
21
site/delete-account.html
Normal 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>
|
||||
@ -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>
|
||||
<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>
|
||||
<ul><li><strong>See and edit</strong> your profile in the app (Library > 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 > 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's Digital Personal Data Protection Act, 2023.</p>
|
||||
<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>
|
||||
|
||||
@ -18,7 +18,7 @@ strong{color:#FBF7EE} a{color:#F5B335} li{margin:4px 0} .brand{color:#F5B335;fon
|
||||
<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>
|
||||
<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 "Is this your song?" 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 "Is this your song?" 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>
|
||||
<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>
|
||||
|
||||
@ -24,7 +24,7 @@ strong{{color:#FBF7EE}} a{{color:#F5B335}} li{{margin:4px 0}} .brand{{color:#F5B
|
||||
def inline(s):
|
||||
s = html.escape(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):
|
||||
|
||||