Buat APK Gratis

API jembatan JavaScript

Widget layar utama

Kartu Android kecil di layar utama yang diisi halaman Anda dengan judul, nilai, dan beberapa baris - AppMint.widget.

AppMint.widget helper#

AppMint.widget.set(card) / update(partial) / clear() / placedCount() / requestPin()

Contoh

Mengisi widget layar utama aplikasi Anda: kartu native dengan judul, satu nilai besar, keterangan, hingga dua belas baris (masing-masing dengan warna, penanda, dan rute ketuk sendiri jika Anda mau), warna pilihan Anda, dan rute yang dibuka saat diketuk. Saat pengguna memperbesar widget, lebih banyak baris ditampilkan, dan dengan textSize: 'auto' teks membesar mengisi widget - selama setiap baris masih muat, baris panjang memakai dua baris alih-alih terpotong. Widget tidak bisa menampilkan HTML - Anda mengirimkan teksnya.

Mengembalikan: setiap metode mengembalikan Promise. set(card) → {ok: true, placed} (placed = jumlah salinan di layar utama) atau {ok: false, error}. clear() → undefined. placedCount() → sebuah angka. requestPin() → true jika lembar "Tambahkan" launcher terbuka. Membutuhkan: tidak ada - setiap build memiliki widget.

Atur kartu - sekali saat awal, dan lagi setiap kali data yang ditampilkannya berubah:

async function updateWidget(events) {
  if (!(window.AppMint && AppMint.widget)) return;          // in a browser
  var r = await AppMint.widget.set({
    title: 'Today',
    value: events.length + ' events',
    lines: events.slice(0, 6).map(function (ev) { return ev.time + ' ' + ev.name; }),
    route: '/calendar',      // a tap opens the app at this route
    bg: '#0E7C86',           // background
    ink: '#FFFFFF',          // title and lines
    accent: '#FFE08A'        // the big value
  });
  if (!r.ok) console.log('Widget not updated: ' + r.error);
}

Agenda yang memenuhi widget - latar belakang transparan, teks yang membesar saat widget diubah ukurannya, baris berwarna dengan penanda, baris terlambat berwarna merah, dan ketukan pada baris yang membuka hari tersebut:

AppMint.widget.set({
  title: 'Agenda',
  value: 'Sun 18 Oct',
  caption: '5 events, 1 overdue',          // small text under the value
  bg: 'rgba(20, 38, 74, 0.6)',             // 60 % opaque — the wallpaper shows through
  ink: '#FFFFFF', accent: '#FFE08A',
  textSize: 'auto',                        // 'auto' | 'small' | 'normal' | 'large' | 'xl' | a number 0.6–3
  align: 'start',                          // 'start' (left) | 'center' | 'end'
  lines: [
    { text: '09:00 Stand-up', marker: '#29B6F6', route: '/#/day/2026-10-18' },
    { text: '10:30 Dentist, Dr Rao', marker: '#43A047', route: '/#/day/2026-10-18' },
    { text: 'Overdue: renew the car insurance', color: '#FF5252', marker: '#FF5252', bold: true, route: '/#/tasks/overdue' },
    { text: 'Tue 20: Team lunch', route: '/#/day/2026-10-20' },
    'Wed 21: Gym'                           // a plain string is still a line
  ],
  route: '/#/today'                         // a tap anywhere else on the card
});

Satu baris berupa string atau { text, color?, marker?, bold?, wrap?, route? }; marker menaruh ● berwarna di depan teks, wrap: true pada satu baris (atau pada kartu) membuat baris panjang memakai dua baris di ukuran teks apa pun; dengan textSize: 'auto' baris memang sudah begitu, dan wrap: false menjaga baris tetap satu baris. Hingga enam baris selalu tampil; baris ketujuh sampai kedua belas tampil jika widget cukup tinggi.

Widget yang lebih kaya - daftar, angka besar dengan bilah kemajuan, gambar latar, dan tombol. Kartu digambar seperti ini hanya jika memiliki salah satu kunci tersebut; kartu tanpanya tampil persis seperti sebelumnya:

AppMint.widget.set({
  layout: 'list',                 // 'list' | 'progress' | 'card'
  title: 'Today', subtitle: 'Tue 30 Sep',
  items: [                         // up to 5 rows; a row tap opens its own route
    { text: '09:00 Dentist', secondary: 'Dr Rao, 2nd floor', image: 'img/tooth.png', route: '/event/41' },
    { text: '13:30 Lunch with Sam', image: 'https://example.com/sam.jpg', route: '/event/42' }
  ],
  buttons: [{ label: 'Add', route: '/new' }, { label: 'Week', route: '/week' }],   // up to 2
  bgImage: 'img/widget-bg.jpg', bg: '#101820', ink: '#FFFFFF', accent: '#FFB000'
});

AppMint.widget.set({
  layout: 'progress', title: 'Steps', value: '8 214',
  progress: { value: 8214, max: 10000, label: '82% of today's goal' }
});

AppMint.widget.update({ value: '8 950', progress: { value: 8950, max: 10000 } });   // change only these keys

Font Anda sendiri - taruh file font di aplikasi Anda (.ttf atau .otf, misalnya dari "Download family" Google Fonts) dan berikan path-nya, seperti untuk <img>; semua teks di kartu digambar dengan font tersebut:

AppMint.widget.set({
  title: 'Score', value: '1 250',
  font: 'fonts/RocknRollOne-Regular.ttf',   // a file in your app, an https address, or a data: URL
  lines: ['Level 7', { text: 'New record!', bold: true }]
}).then(function (r) { if (!r.ok) console.log(r.error); });   // e.g. "font file not found in the app: …"

NAMA font seperti 'RocknRoll One' tidak bisa berfungsi: layar utama yang menggambar widget, dan hanya memiliki font bawaan ponsel - set() menjawab {ok: false, error} yang menyatakan hal itu. Keluarga bawaan ini tidak butuh file: 'serif', 'monospace', 'sans-serif-condensed', 'sans-serif-light', 'sans-serif-medium', 'sans-serif-black', 'casual', 'cursive', 'sans-serif-smallcaps'. File WOFF / WOFF2 ditolak (Android tidak bisa membacanya) - gunakan .ttf. Dengan file font, kartu digambar sebagai gambar sesuai ukuran widget dan digambar ulang saat ukurannya diubah; ketukan pada baris, baris tabel, dan tombol serta pembaca layar berfungsi seperti pada kartu mana pun.

Segarkan saat aplikasi ditutup - berikan alamat https yang mengembalikan JSON yang sama; Android mengambilnya setiap refreshMinutes (30 atau lebih, bawaan 60) selama widget ada di layar utama, bahkan setelah restart:

AppMint.widget.set({
  title: 'Orders', value: '…',
  refreshUrl: 'https://api.example.com/widget.json?user=42',
  refreshMinutes: 30
}).then(function (r) { console.log('refreshing every ' + r.refreshMinutes + ' min'); });
// widget.json answers e.g. {"title":"Orders","value":"12 new","layout":"list","items":[{"text":"#1042 paid"}]}

Gambar: path di aplikasi Anda (diselesaikan seperti <img src> di halaman), alamat https, atau URL data:image/…; gambar diperkecil dan disimpan di ponsel. Di JSON penyegaran, path relatif berarti file milik aplikasi. refreshUrl: '' menghentikan penyegaran; clear() juga menghentikannya. Android menentukan waktu pasti setiap penyegaran (pekerjaan latar belakang dikelompokkan untuk menghemat baterai).

Tombol "Tambahkan ke layar utama":

async function addWidget() {
  if (!(window.AppMint && AppMint.widget)) return;
  if ((await AppMint.widget.placedCount()) > 0) { alert('The widget is already on your home screen'); return; }
  var opened = await AppMint.widget.requestPin();
  if (!opened) alert('Touch and hold your home screen, tap Widgets, and pick this app.');
}

Kosongkan (misalnya setelah pengguna keluar akun):

if (window.AppMint && AppMint.widget) AppMint.widget.clear();   // shows "Nothing to show yet"

Ketukan pada widget membuka aplikasi dengan appmint-shortcut://<package><route> sebagai tautan peluncurannya - bacalah dengan WebToApk.getLaunchUrl() saat mulai dan dengan event appmint:deep-link saat berjalan:

window.addEventListener('appmint:deep-link', function (e) {
  var m = /^appmint-shortcut:\/\/[^\/?#]*([^#]*)/i.exec(e.detail.url);
  if (m) goTo(m[1] || '/');
});

Warna: #RRGGBB, #RGB, rgb(20, 38, 74), rgba(20, 38, 74, 0.6) (juga rgb(20 38 74 / 60%)), transparent dan nama biasa seperti red. Hex delapan digit mengikuti urutan Android, alpha DULU: #AARRGGBB. #9914264A adalah 60 % buram; #14264A99 di CSS dibaca di sini sebagai alpha 14 (8 %), kartu yang nyaris tak terlihat. Untuk latar belakang transparan, rgba() menghindari kebingungan ini. Warna garis (color, marker) menerima bentuk yang sama; warna garis yang salah ditolak dengan {ok: false, error: 'lines[2].color is not a colour'}, warna kartu yang salah memakai warna bawaan.

Catatan: Semua kolom kartu bersifat opsional: title kosong menampilkan nama aplikasi; value, caption atau baris yang kosong disembunyikan; baris setelah yang kedua belas dibuang. textSize dan align berlaku untuk semua teks di kartu; nilai yang tidak dikenal ditolak dengan {ok: false, error}. Kartu tanpa kunci baru (caption, textSize, align, wrap, baris bergaya) tampil persis seperti sebelumnya. Widget menyimpan kartu terakhir, bahkan setelah restart, dan hanya berubah saat halaman Anda memanggil set() lagi. requestPin() membutuhkan Android 8+ dan launcher yang mendukungnya. Aplikasi buatan AI dapat menggunakan widget dari @/lib/appmintNative. Baris bergaya, caption, textSize, align, wrap dan rgba() membutuhkan aplikasi yang dibuat dengan AppMint 1.11.38 atau lebih baru - build ulang aplikasi sebelum mengirim objek baris, karena build lama akan menampilkannya sebagai teks mentah. font membutuhkan aplikasi yang dibuat dengan AppMint 1.11.41 atau lebih baru (build lama mengabaikannya). Aplikasi mode situs web: ketukan membuka rute di situs web Anda - /#/today memuat https://<your site>/#/today - dan getLaunchUrl() tetap mengembalikan tautan appmint-shortcut://.

widgetClear jembatan#

window.WebToApk.widgetClear()

Menghapus kartu; widget yang terpasang kembali ke keadaan kosong.

Contoh

Saluran internal di balik AppMint.widget.clear() - menghapus kartu widget; setiap widget yang terpasang kembali ke "Belum ada yang ditampilkan".

Mengembalikan: tidak ada. Membutuhkan: tidak ada.

API publik terlebih dahulu:

async function onSignOut() {
  if (window.AppMint && AppMint.widget) await AppMint.widget.clear();
}

Panggilan mentah:

if (window.WebToApk && typeof window.WebToApk.widgetClear === 'function') {
  window.WebToApk.widgetClear();
}

Kaitkan widget dengan sakelar "Tampilkan di layar utama" di pengaturan Anda - mati mengosongkannya, hidup mengisinya lagi:

document.getElementById('widget-toggle').onchange = async function (e) {
  if (!(window.AppMint && AppMint.widget)) return;
  if (e.target.checked) {
    await AppMint.widget.set(buildWidgetCard()); // your own card builder
  } else {
    await AppMint.widget.clear();
  }
};

Setelah menghapus akun, kosongkan widget dan beri tahu pengguna jika masih ada salinan di layar utama mereka:

function afterAccountDeleted() {
  var W = window.WebToApk;
  if (!W || typeof W.widgetClear !== 'function') return;
  W.widgetClear();
  if (W.widgetPlacedCount() > 0) {
    showMessage('Your widget is now empty. You can remove it from your home screen.');
  }
}

Catatan: Widget tetap ada di layar utama; hanya isinya yang dihapus. Panggil AppMint.widget.set() untuk mengisinya lagi.

widgetPlacedCount jembatan#

window.WebToApk.widgetPlacedCount(): Int

Berapa banyak salinan widget yang telah dipasang pengguna di layar utama.

Contoh

Saluran internal di balik AppMint.widget.placedCount() - berapa banyak salinan widget Anda yang telah diletakkan pengguna di layar utama.

Mengembalikan: sebuah angka, secara sinkron (0 jika tidak ada). Membutuhkan: tidak ada.

API publik dulu - tampilkan tombol "Tambahkan widget" hanya jika widget belum dipasang:

async function showAddWidgetButton() {
  var btn = document.getElementById('add-widget');
  if (!(window.AppMint && AppMint.widget)) { btn.hidden = true; return; }
  btn.hidden = (await AppMint.widget.placedCount()) > 0;
}

Panggilan mentah:

if (window.WebToApk && typeof window.WebToApk.widgetPlacedCount === 'function') {
  var n = window.WebToApk.widgetPlacedCount();
  console.log('Widgets on the home screen: ' + n);
}

Periksa lagi setiap kali pengguna kembali dari launcher, dan ucapkan terima kasih setelah widget terpasang:

document.addEventListener('visibilitychange', async function () {
  if (document.visibilityState !== 'visible') return;
  if (!(window.AppMint && AppMint.widget)) return;
  var n = await AppMint.widget.placedCount();
  document.getElementById('widget-promo').hidden = n > 0;
  if (n > 0) document.getElementById('widget-thanks').hidden = false;
});

Tampilkan status widget di halaman pengaturan dengan panggilan mentah yang sinkron:

var status = document.getElementById('widget-status');
if (window.WebToApk && typeof window.WebToApk.widgetPlacedCount === 'function') {
  var n = window.WebToApk.widgetPlacedCount();
  status.textContent = n === 0 ? 'Not on your home screen'
    : n === 1 ? 'On your home screen' : 'On your home screen ' + n + ' times';
} else {
  status.textContent = 'Available in the installed app';
}

Catatan: Periksa lagi saat aplikasi kembali ke depan (visibilitychange), karena pengguna menambahkan widget dari launcher, di luar aplikasi Anda.

widgetRequestPin jembatan#

window.WebToApk.widgetRequestPin(): Boolean

Membuka lembar "Tambahkan ke layar utama" launcher untuk widget (Android 8+). False jika launcher tidak mampu.

Contoh

Saluran internal di balik AppMint.widget.requestPin() - membuka lembar "Tambahkan ke layar utama" launcher untuk widget Anda.

Mengembalikan: true jika lembar terbuka, false jika launcher tidak mampu (secara sinkron). Membutuhkan: Android 8+ dan launcher yang mendukung penyematan widget.

API publik dulu, dari ketukan tombol:

document.getElementById('add-widget').onclick = async function () {
  if (!(window.AppMint && AppMint.widget)) return;
  var opened = await AppMint.widget.requestPin();
  if (!opened) {
    alert('Touch and hold your home screen, tap Widgets, then pick this app.');
  }
};

Panggilan mentah:

if (window.WebToApk && typeof window.WebToApk.widgetRequestPin === 'function') {
  if (!window.WebToApk.widgetRequestPin()) showManualSteps();
}

Isi kartu dulu, lalu buka lembarnya, agar widget menampilkan konten nyata begitu terpasang:

document.getElementById('add-widget').onclick = async function () {
  if (!(window.AppMint && AppMint.widget)) return;
  await AppMint.widget.set({ title: 'Streak', value: '12 days', lines: ['Best 30'], route: '/streak' });
  if (!(await AppMint.widget.requestPin())) {
    document.getElementById('widget-help').hidden = false; // manual steps
  }
};

Cari tahu apakah pengguna menerimanya: bandingkan jumlah yang terpasang sebelum lembar dibuka dan setelah mereka kembali:

document.getElementById('add-widget').onclick = function () {
  var W = window.WebToApk;
  if (!W || typeof W.widgetRequestPin !== 'function') return;
  var before = W.widgetPlacedCount();
  if (!W.widgetRequestPin()) { showManualSteps(); return; }
  document.addEventListener('visibilitychange', function check() {
    if (document.visibilityState !== 'visible') return;
    document.removeEventListener('visibilitychange', check);
    if (W.widgetPlacedCount() > before) showToast('Widget added');
  });
};

Catatan: true hanya berarti lembar terbuka - pengguna masih bisa membatalkannya. Gunakan AppMint.widget.placedCount() nanti untuk melihat apakah widget sudah ditambahkan. Isi kartu dengan AppMint.widget.set() terlebih dahulu, agar widget baru tidak kosong.

widgetSet jembatan#

window.WebToApk.widgetSet(cardJson: String): String

Menyimpan kartu widget [cardJson] `{title?, value?, lines?, route?, bg?, ink?, accent?}` dan menggambar ulang setiap widget yang terpasang. Mengembalikan `{ok, placed}`.

Contoh

Saluran internal di balik AppMint.widget.set(card) - halaman menggunakan helper itu untuk mengisi widget layar utama.

Mengembalikan: string JSON {"ok":true,"placed":<number>} atau {"ok":false,"error":"…"} (secara sinkron). Membutuhkan: tidak ada - setiap build memiliki widget.

API publik terlebih dahulu:

if (window.AppMint && AppMint.widget) {
  AppMint.widget.set({ title: 'Steps', value: '8 214', lines: ['Goal 10 000'], route: '/' })
    .then(function (r) { if (!r.ok) console.log(r.error); });
}

Panggilan mentah menerima kartu sebagai string JSON dan mengembalikan teks JSON:

if (window.WebToApk && typeof window.WebToApk.widgetSet === 'function') {
  var card = { title: 'Steps', value: '8 214', lines: ['Goal 10 000'], route: '/',
               bg: '#15161A', ink: '#FFFFFF', accent: '#22D3EE' };
  var r = JSON.parse(window.WebToApk.widgetSet(JSON.stringify(card)));
  console.log(r.ok ? 'on ' + r.placed + ' home screen(s)' : r.error);
}

Jaga widget tetap selaras dengan daftar tugas - jumlah yang belum selesai sebagai nilai, enam tugas pertama sebagai baris, dan ketukan membuka layar tugas:

async function syncTaskWidget(tasks) {
  if (!(window.AppMint && AppMint.widget)) return;
  var open = tasks.filter(function (t) { return !t.done; });
  var r = await AppMint.widget.set({
    title: 'To do',
    value: open.length ? open.length + ' left' : 'All done',
    lines: open.slice(0, 6).map(function (t) { return '• ' + t.title; }),
    route: '/tasks'
  });
  if (!r.ok) console.warn('Widget not updated: ' + r.error);
}

Tulis status terbaru saat pengguna meninggalkan aplikasi; panggilan mentah bersifat sinkron, jadi selesai sebelum halaman dijeda:

document.addEventListener('visibilitychange', function () {
  if (document.visibilityState !== 'hidden') return;
  if (!window.WebToApk || typeof window.WebToApk.widgetSet !== 'function') return;
  var s = getTodayStats(); // your app's own numbers
  window.WebToApk.widgetSet(JSON.stringify({
    title: 'Today', value: s.spent + ' spent',
    lines: ['Budget ' + s.budget, 'Left ' + (s.budget - s.spent)], route: '/budget'
  }));
});

Catatan: Kunci kartu: title, value, caption, lines (hingga dua belas; satu baris berupa string atau {text, color, marker, bold, wrap, route}), route (/ ditambahkan jika tidak ada), bg, ink, accent, textSize, align, wrap, font (file .ttf/.otf di aplikasi Anda atau keluarga font sistem seperti serif), ditambah kunci tata letak lengkap - lihat AppMint.widget. Warna: #RRGGBB, #AARRGGBB (alpha dulu), rgba(). Setiap widget yang terpasang digambar ulang sekaligus. placed bernilai 0 jika pengguna belum menambahkan widget - kartu tetap disimpan dan muncul saat mereka menambahkannya.

widgetUpdate jembatan#

window.WebToApk.widgetUpdate(partialJson: String): String

Menggabungkan [partialJson] ke kartu widget yang tersimpan (kunci bernama menggantikan, sisanya tetap) dan menggambar ulang. Mengembalikan `{ok, placed}`.

Contoh

Mengubah sebagian widget layar utama: kunci yang Anda berikan menggantikan yang tersimpan, yang lain tetap. Halaman biasanya memanggil AppMint.widget.update(partial).

Mengembalikan: string JSON {"ok":true,"placed":<n>} atau {"ok":false,"error":"…"}. Membutuhkan: tidak ada - setiap build memiliki widget.

// Only the number and the progress bar change; title, rows and buttons stay as they were.
if (window.AppMint && AppMint.widget) {
  AppMint.widget.update({ value: '7 420', progress: { value: 7420, max: 10000, label: '74% of goal' } });
}

// Raw form:
if (window.WebToApk && typeof window.WebToApk.widgetUpdate === 'function') {
  var r = JSON.parse(window.WebToApk.widgetUpdate(JSON.stringify({ subtitle: 'Updated ' + new Date().toLocaleTimeString() })));
  if (!r.ok) console.warn(r.error);
}

Hapus kartu saat keluar akun dan laporkan kesalahan dari update:

async function onSignOut() {
  if (!(window.AppMint && AppMint.widget)) return;
  await AppMint.widget.clear();
}

async function bumpCount(n) {
  if (!(window.AppMint && AppMint.widget)) return;
  const r = await AppMint.widget.update({ value: String(n) });
  if (!r.ok) console.warn('Widget not updated: ' + r.error);
}

Gabungkan dengan pemeriksaan penempatan: segarkan kartu hanya selama ada salinan di layar utama.

async function refreshWidget(stats) {
  if (!(window.AppMint && AppMint.widget)) return;
  if (await AppMint.widget.placedCount() === 0) return;
  AppMint.widget.update({ value: stats.total + ' today', subtitle: 'Updated ' + new Date().toLocaleTimeString() });
}

Catatan: Tanpa kartu tersimpan, update() berperilaku seperti set(). Lihat AppMint.widget untuk semua kunci kartu.

Dibuat dari runtime aplikasi dan file contohnya pada setiap build dokumentasi. Baca sebagai Markdown · Semua keluarga.

Diperiksa terhadap jembatan yang dirilis pada 2026-10-22.