build(android): release signing config — key.properties based, debug fallback
正式签名(CN=PhotoLive):keystore/key.properties 不入库(gitignore 已覆盖); 无配置的构建机回退 debug 签名。首个正式签名 APK 已产出并 apksigner 验证。 Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
@@ -49,7 +49,7 @@
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
object-fit: cover;
|
object-fit: cover;
|
||||||
opacity: 0.4;
|
// opacity: 0.4;
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
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.
|
||||||
@@ -25,11 +26,26 @@ android {
|
|||||||
versionName = flutter.versionName
|
versionName = flutter.versionName
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 正式签名(key.properties + photo-live-release.jks,均不入库):
|
||||||
|
// 缺失时回退 debug 签名(CI/他人克隆仓库仍可构建)。
|
||||||
|
val keystoreProps = Properties().apply {
|
||||||
|
val f = rootProject.file("app/key.properties")
|
||||||
|
if (f.exists()) f.inputStream().use { load(it) }
|
||||||
|
}
|
||||||
|
|
||||||
buildTypes {
|
buildTypes {
|
||||||
release {
|
release {
|
||||||
// TODO: Add your own signing config for the release build.
|
signingConfig = if (keystoreProps.getProperty("storeFile") != null) {
|
||||||
// Signing with the debug keys for now, so `flutter run --release` works.
|
signingConfigs.create("release") {
|
||||||
signingConfig = signingConfigs.getByName("debug")
|
storeFile = rootProject.file("app/" + keystoreProps.getProperty("storeFile"))
|
||||||
|
storePassword = keystoreProps.getProperty("storePassword")
|
||||||
|
keyAlias = keystoreProps.getProperty("keyAlias")
|
||||||
|
keyPassword = keystoreProps.getProperty("keyPassword")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Signing with the debug keys(无正式签名配置的构建机回退)。
|
||||||
|
signingConfigs.getByName("debug")
|
||||||
|
}
|
||||||
// R8 混淆会破坏 WorkManager 反射初始化(NoSuchMethodException
|
// R8 混淆会破坏 WorkManager 反射初始化(NoSuchMethodException
|
||||||
// WorkDatabase_Impl),ProGuard keep 规则见 proguard-rules.pro。
|
// WorkDatabase_Impl),ProGuard keep 规则见 proguard-rules.pro。
|
||||||
isMinifyEnabled = true
|
isMinifyEnabled = true
|
||||||
|
|||||||
@@ -0,0 +1,48 @@
|
|||||||
|
import { createServer } from 'node:http';
|
||||||
|
import { chromium } from '@playwright/test';
|
||||||
|
const server = createServer((_q, res) => {
|
||||||
|
res.setHeader('content-type', 'text/html; charset=utf-8');
|
||||||
|
res.end(`<!doctype html><html><body style="min-height:900px">
|
||||||
|
<script src="http://219.157.255.213:32006/embed/v2/loader.js" data-slot="se1bt8mqvmeese4jx9gixnj4" defer></script></body></html>`);
|
||||||
|
});
|
||||||
|
await new Promise((r) => server.listen(3980, '127.0.0.1', r));
|
||||||
|
const browser = await chromium.launch();
|
||||||
|
const page = await browser.newPage({ viewport: { width: 1440, height: 900 } });
|
||||||
|
await page.goto('http://127.0.0.1:3980');
|
||||||
|
const frame = page.locator('iframe[src*="/embed/"][src*="v=2"]');
|
||||||
|
await frame.waitFor({ timeout: 20000 });
|
||||||
|
await page.waitForTimeout(3000);
|
||||||
|
const f = page.frames().find((fr) => fr.url().includes('v=2'));
|
||||||
|
const offset = async () => {
|
||||||
|
const t = await f.locator('.stream-track').evaluate((el) => el.style.transform);
|
||||||
|
return Number.parseFloat((/translateY\((-?[\d.]+)px\)/.exec(t) ?? ['0', '0'])[1]);
|
||||||
|
};
|
||||||
|
const box = await frame.boundingBox();
|
||||||
|
const cx = (box ? box.x : 0) + 90, cy = (box ? box.y : 0) + 150;
|
||||||
|
const scrolling = async (ms = 1000) => { const a = await offset(); await page.waitForTimeout(ms); return a !== (await offset()); };
|
||||||
|
|
||||||
|
console.log('1. baseline =', await scrolling());
|
||||||
|
|
||||||
|
// 窗内拖动后,鼠标移开再验证(窗内 up 后鼠标停在窗内属正常 hover 暂停)
|
||||||
|
await page.mouse.move(cx, cy); await page.mouse.down();
|
||||||
|
await page.mouse.move(cx, cy + 40, { steps: 4 }); await page.mouse.up();
|
||||||
|
await page.mouse.move(30, 30); await page.waitForTimeout(1500);
|
||||||
|
console.log('2. after in-frame drag (pointer away) =', await scrolling());
|
||||||
|
|
||||||
|
// 窗外释放
|
||||||
|
await page.mouse.move(cx, cy); await page.mouse.down();
|
||||||
|
await page.mouse.move(cx + 120, cy + 200, { steps: 4 }); await page.mouse.up();
|
||||||
|
await page.waitForTimeout(3500);
|
||||||
|
console.log('3. after outside-release =', await scrolling());
|
||||||
|
|
||||||
|
// 长跑 20s
|
||||||
|
const s1 = await offset(); await page.waitForTimeout(20000); const s2 = await offset();
|
||||||
|
console.log('4. 20s moved =', Math.abs(s2 - s1).toFixed(0), 'px');
|
||||||
|
|
||||||
|
// hover 语义仍正常
|
||||||
|
await page.mouse.move(cx, cy); await page.waitForTimeout(800);
|
||||||
|
const h1 = await offset(); await page.waitForTimeout(800); const h2 = await offset();
|
||||||
|
await page.mouse.move(30, 30); await page.waitForTimeout(1500);
|
||||||
|
const h3 = await offset(); await page.waitForTimeout(800); const h4 = await offset();
|
||||||
|
console.log('5. hover pause =', h1 === h2, '; leave resume =', h3 !== h4);
|
||||||
|
server.close(); await browser.close();
|
||||||
Reference in New Issue
Block a user