Generate push icon
This commit is contained in:
parent
5843b3e33a
commit
f00687d20e
7 changed files with 90 additions and 0 deletions
|
|
@ -36,6 +36,17 @@
|
||||||
android:grantUriPermissions="true">
|
android:grantUriPermissions="true">
|
||||||
<meta-data android:name="android.support.FILE_PROVIDER_PATHS" android:resource="@xml/file_paths" />
|
<meta-data android:name="android.support.FILE_PROVIDER_PATHS" android:resource="@xml/file_paths" />
|
||||||
</provider>
|
</provider>
|
||||||
|
|
||||||
|
<!-- FCM uses these when it renders a notification-payload message itself
|
||||||
|
(app backgrounded/killed). Without an explicit monochrome small icon
|
||||||
|
it falls back to the full-color adaptive launcher icon, which many
|
||||||
|
Android builds render as a blank square or drop entirely. -->
|
||||||
|
<meta-data
|
||||||
|
android:name="com.google.firebase.messaging.default_notification_icon"
|
||||||
|
android:resource="@drawable/ic_stat_notify" />
|
||||||
|
<meta-data
|
||||||
|
android:name="com.google.firebase.messaging.default_notification_channel_id"
|
||||||
|
android:value="@string/default_notification_channel_id" />
|
||||||
</application>
|
</application>
|
||||||
|
|
||||||
<!-- Permissions -->
|
<!-- Permissions -->
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,8 @@
|
||||||
package social.flotilla;
|
package social.flotilla;
|
||||||
|
|
||||||
|
import android.app.NotificationChannel;
|
||||||
|
import android.app.NotificationManager;
|
||||||
|
import android.os.Build;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
|
|
||||||
import com.getcapacitor.BridgeActivity;
|
import com.getcapacitor.BridgeActivity;
|
||||||
|
|
@ -10,6 +13,28 @@ public class MainActivity extends BridgeActivity {
|
||||||
@Override
|
@Override
|
||||||
public void onCreate(Bundle savedInstanceState) {
|
public void onCreate(Bundle savedInstanceState) {
|
||||||
registerPlugin(AndroidPushFallbackPlugin.class);
|
registerPlugin(AndroidPushFallbackPlugin.class);
|
||||||
|
createPushNotificationChannel();
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Ensures the channel named by the FCM default_notification_channel_id metadata
|
||||||
|
// exists. FCM does not create it for us; if it's missing when a notification
|
||||||
|
// arrives, Android falls back to a generic "Miscellaneous" channel. Channels
|
||||||
|
// persist once created, so doing this on launch is sufficient.
|
||||||
|
private void createPushNotificationChannel() {
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||||
|
NotificationManager manager = getSystemService(NotificationManager.class);
|
||||||
|
String id = getString(R.string.default_notification_channel_id);
|
||||||
|
|
||||||
|
if (manager != null && manager.getNotificationChannel(id) == null) {
|
||||||
|
NotificationChannel channel = new NotificationChannel(
|
||||||
|
id,
|
||||||
|
"Notifications",
|
||||||
|
NotificationManager.IMPORTANCE_HIGH
|
||||||
|
);
|
||||||
|
channel.setDescription("New messages and activity");
|
||||||
|
manager.createNotificationChannel(channel);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,4 +4,5 @@
|
||||||
<string name="title_activity_main">Flotilla</string>
|
<string name="title_activity_main">Flotilla</string>
|
||||||
<string name="package_name">social.flotilla</string>
|
<string name="package_name">social.flotilla</string>
|
||||||
<string name="custom_url_scheme">social.flotilla</string>
|
<string name="custom_url_scheme">social.flotilla</string>
|
||||||
|
<string name="default_notification_channel_id">flotilla_push</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
|
||||||
3
build.sh
3
build.sh
|
|
@ -40,3 +40,6 @@ npx @capacitor/assets generate \
|
||||||
--iconBackgroundColorDark '#222222' \
|
--iconBackgroundColorDark '#222222' \
|
||||||
--splashBackgroundColor '#ffffff' \
|
--splashBackgroundColor '#ffffff' \
|
||||||
--splashBackgroundColorDark '#191E24'
|
--splashBackgroundColorDark '#191E24'
|
||||||
|
|
||||||
|
# @capacitor/assets doesn't generate Android notification icons
|
||||||
|
node scripts/generate-notification-icon.mjs
|
||||||
|
|
|
||||||
|
|
@ -35,6 +35,7 @@
|
||||||
"postcss": "^8.5.15",
|
"postcss": "^8.5.15",
|
||||||
"prettier": "^3.8.1",
|
"prettier": "^3.8.1",
|
||||||
"prettier-plugin-svelte": "^3.4.1",
|
"prettier-plugin-svelte": "^3.4.1",
|
||||||
|
"sharp": "0.35.0-rc.0",
|
||||||
"svelte": "^5.55.9",
|
"svelte": "^5.55.9",
|
||||||
"svelte-check": "^4.3.5",
|
"svelte-check": "^4.3.5",
|
||||||
"tailwindcss": "^4.2.2",
|
"tailwindcss": "^4.2.2",
|
||||||
|
|
|
||||||
BIN
pnpm-lock.yaml
BIN
pnpm-lock.yaml
Binary file not shown.
49
scripts/generate-notification-icon.mjs
Normal file
49
scripts/generate-notification-icon.mjs
Normal file
|
|
@ -0,0 +1,49 @@
|
||||||
|
#!/usr/bin/env node
|
||||||
|
// Generates the Android notification (status-bar) small icon from the app's
|
||||||
|
// source logo. @capacitor/assets does NOT produce notification icons, so the
|
||||||
|
// build pipeline calls this after `@capacitor/assets generate` to keep the
|
||||||
|
// notification icon in sync with a custom platform logo (VITE_PLATFORM_LOGO).
|
||||||
|
//
|
||||||
|
// A notification small icon must be a monochrome silhouette: Android renders
|
||||||
|
// only its alpha channel and tints it. We take the logo's alpha channel as the
|
||||||
|
// shape and fill it white, with a little transparent padding so it isn't
|
||||||
|
// edge-to-edge in the status bar.
|
||||||
|
import sharp from "sharp"
|
||||||
|
import {mkdir} from "node:fs/promises"
|
||||||
|
import {dirname, join} from "node:path"
|
||||||
|
|
||||||
|
const source = process.argv[2] || "assets/logo.png"
|
||||||
|
const resDir = process.argv[3] || "android/app/src/main/res"
|
||||||
|
|
||||||
|
// density bucket -> small-icon size in px
|
||||||
|
const densities = {mdpi: 24, hdpi: 36, xhdpi: 48, xxhdpi: 72, xxxhdpi: 96}
|
||||||
|
|
||||||
|
const generate = async (size, out) => {
|
||||||
|
const pad = Math.round(size * 0.14)
|
||||||
|
const inner = size - pad * 2
|
||||||
|
|
||||||
|
// The logo's alpha channel is the shape we want to render.
|
||||||
|
const alpha = await sharp(source)
|
||||||
|
.resize(inner, inner, {fit: "contain", background: {r: 0, g: 0, b: 0, alpha: 0}})
|
||||||
|
.ensureAlpha()
|
||||||
|
.extractChannel(3)
|
||||||
|
.toColourspace("b-w")
|
||||||
|
.raw()
|
||||||
|
.toBuffer()
|
||||||
|
|
||||||
|
await sharp({
|
||||||
|
create: {width: inner, height: inner, channels: 3, background: {r: 255, g: 255, b: 255}},
|
||||||
|
})
|
||||||
|
.joinChannel(alpha, {raw: {width: inner, height: inner, channels: 1}})
|
||||||
|
.extend({top: pad, bottom: pad, left: pad, right: pad, background: {r: 0, g: 0, b: 0, alpha: 0}})
|
||||||
|
.png()
|
||||||
|
.toFile(out)
|
||||||
|
|
||||||
|
console.log(`wrote ${out} (${size}px)`)
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const [density, size] of Object.entries(densities)) {
|
||||||
|
const out = join(resDir, `drawable-${density}`, "ic_stat_notify.png")
|
||||||
|
await mkdir(dirname(out), {recursive: true})
|
||||||
|
await generate(size, out)
|
||||||
|
}
|
||||||
Loading…
Reference in a new issue