getAutoFillScript static method
Generate comprehensive auto-fill script for DigiLocker
Implementation
static String getAutoFillScript(String aadhaarNumber, List<String?>? autoSelectCheckBox) {
// Convert the list to JavaScript array format
final checkboxList =
autoSelectCheckBox != null && autoSelectCheckBox.isNotEmpty
? autoSelectCheckBox
.where((item) => item != null)
.map((item) => '"$item"')
.join(',')
: '';
return '''
(function() {
if (!window.FlutterChannel) {
window.FlutterChannel = { postMessage: function(msg) { console.log('FlutterChannel:', msg); } };
}
var AADHAAR_NUMBER = ${aadhaarNumber.isNotEmpty ? '"$aadhaarNumber"' : '""'};
var AUTO_SELECT_CHECKBOXES = [$checkboxList];
if (typeof window.digilockerAlreadyFilled === 'undefined') {
window.digilockerAlreadyFilled = false;
window.consentCheckboxSelected = false;
window.additionalCheckboxesSelected = {};
}
// Helper: Check if field is OTP/PIN/Password
function isOTPOrPINField(input) {
if (!input) return false;
var id = (input.id || '').toLowerCase();
var name = (input.name || '').toLowerCase();
var placeholder = (input.placeholder || '').toLowerCase();
var type = (input.type || '').toLowerCase();
var className = (input.className || '').toLowerCase();
var indicators = ['otp', 'pin', 'password', 'pwd', 'mpin', 'verification'];
for (var i = 0; i < indicators.length; i++) {
if (id.includes(indicators[i]) || name.includes(indicators[i]) ||
placeholder.includes(indicators[i]) || className.includes(indicators[i])) {
return true;
}
}
return type === 'password';
}
// Helper: Check if field is for Aadhaar
function isAadhaarField(input) {
var text = ((input.id || '') + (input.name || '') +
(input.placeholder || '') + (input.className || '')).toLowerCase();
return text.includes('aadhaar') || text.includes('aadhar') || text.includes('uid');
}
// Helper: Find best input field
function findBestInput() {
var inputs = document.querySelectorAll('input[type="text"], input[type="number"], input[type="tel"], input:not([type])');
// First pass: Look for Aadhaar-specific fields
for (var i = 0; i < inputs.length; i++) {
var input = inputs[i];
if (input.offsetParent === null) continue;
if (isOTPOrPINField(input)) continue;
if (input.value && input.value.length > 3) continue;
if (isAadhaarField(input)) return input;
}
// Second pass: Any visible empty field
for (var i = 0; i < inputs.length; i++) {
var input = inputs[i];
if (input.offsetParent === null) continue;
if (isOTPOrPINField(input)) continue;
if (!input.value) return input;
}
return null;
}
// Helper: Fill input with proper events
function fillInput(input, value) {
try {
input.focus();
var setter = Object.getOwnPropertyDescriptor(window.HTMLInputElement.prototype, 'value').set;
setter.call(input, value);
// Dispatch events
input.dispatchEvent(new Event('input', { bubbles: true }));
input.dispatchEvent(new Event('change', { bubbles: true }));
input.dispatchEvent(new KeyboardEvent('keyup', { bubbles: true }));
return true;
} catch (e) {
FlutterChannel.postMessage('debug:Fill error - ' + e.message);
return false;
}
}
function isTextEntryElement(element) {
if (!element || !element.tagName) return false;
var tagName = element.tagName.toLowerCase();
if (tagName === 'textarea') return true;
if (element.isContentEditable) return true;
if (tagName !== 'input') return false;
var type = (element.type || '').toLowerCase();
return ['text', 'number', 'tel', 'password', 'email', 'search', 'url', ''].indexOf(type) !== -1;
}
function getViewportHeight() {
if (window.visualViewport && window.visualViewport.height) {
return window.visualViewport.height;
}
return window.innerHeight || document.documentElement.clientHeight || document.body.clientHeight || 0;
}
function scheduleEnsureActiveFieldVisible(delay) {
if (window.digilockerEnsureFieldTimer) {
clearTimeout(window.digilockerEnsureFieldTimer);
}
window.digilockerEnsureFieldTimer = setTimeout(function() {
ensureActiveFieldVisible();
}, delay || 0);
}
function ensureActiveFieldVisible() {
var activeElement = document.activeElement;
if (!isTextEntryElement(activeElement)) return;
var rect = activeElement.getBoundingClientRect();
var viewportHeight = getViewportHeight();
if (!viewportHeight) return;
var topPadding = 24;
var bottomPadding = 32;
var scrollTop = window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop || 0;
var visibleTop = topPadding;
var visibleBottom = viewportHeight - bottomPadding;
if (rect.top >= visibleTop && rect.bottom <= visibleBottom) {
return;
}
var targetTop = scrollTop + rect.top - Math.max((viewportHeight - rect.height) / 2, topPadding);
if (Math.abs(targetTop - scrollTop) < 12) {
return;
}
window.scrollTo(0, Math.max(targetTop, 0));
}
function installKeyboardHandlers() {
if (window.digilockerKeyboardHandlersInstalled) return;
window.digilockerKeyboardHandlersInstalled = true;
document.addEventListener('focusin', function(event) {
if (!isTextEntryElement(event.target)) return;
scheduleEnsureActiveFieldVisible(80);
scheduleEnsureActiveFieldVisible(220);
}, true);
window.addEventListener('resize', function() {
scheduleEnsureActiveFieldVisible(120);
});
if (window.visualViewport) {
window.visualViewport.addEventListener('resize', function() {
scheduleEnsureActiveFieldVisible(120);
});
}
}
// Fill split Aadhaar fields (4-4-4 format for Sign-Up)
function fillSplitAadhaarFields() {
var f1 = document.getElementById('aadhaar_1');
var f2 = document.getElementById('aadhaar_2');
var f3 = document.getElementById('aadhaar_3');
if (!f1 || !f2 || !f3) return false;
if (!AADHAAR_NUMBER || AADHAAR_NUMBER.length !== 12) return false;
fillInput(f1, AADHAAR_NUMBER.substring(0, 4));
setTimeout(function() {
fillInput(f2, AADHAAR_NUMBER.substring(4, 8));
}, 120);
setTimeout(function() {
fillInput(f3, AADHAAR_NUMBER.substring(8, 12));
window.digilockerAlreadyFilled = true;
FlutterChannel.postMessage('prefillStatus:AADHAAR_SPLIT_FILLED');
}, 240);
return true;
}
// Main auto-fill function
function attemptAadhaarAutofill() {
if (window.digilockerAlreadyFilled) return;
if (!AADHAAR_NUMBER || AADHAAR_NUMBER.length < 10) return;
// Try split fields first (Sign-Up flow)
if (document.getElementById('aadhaar_1')) {
if (fillSplitAadhaarFields()) return;
}
// Try single field (Sign-In flow)
var input = findBestInput();
if (input && fillInput(input, AADHAAR_NUMBER)) {
window.digilockerAlreadyFilled = true;
FlutterChannel.postMessage('prefillStatus:AADHAAR_FILLED');
}
}
// Auto-select consent checkbox for Aadhaar card
function selectAadhaarConsentCheckbox() {
if (window.consentCheckboxSelected) return;
var labels = document.querySelectorAll('label.form-check-label');
var targetRow = null;
for (var i = 0; i < labels.length; i++) {
var text = (labels[i].innerText || '').toLowerCase();
if (text.includes('aadhaar') && text.includes('card')) {
targetRow = labels[i].closest('.d-flex');
break;
}
}
if (!targetRow) return;
var checkbox = targetRow.querySelector('input[type="checkbox"][name="consent"]');
if (!checkbox) return;
checkbox.checked = false;
checkbox.dispatchEvent(new Event('change', { bubbles: true }));
setTimeout(function() {
checkbox.checked = true;
checkbox.dispatchEvent(new Event('input', { bubbles: true }));
checkbox.dispatchEvent(new Event('change', { bubbles: true }));
checkbox.dispatchEvent(new Event('click', { bubbles: true }));
window.consentCheckboxSelected = true;
FlutterChannel.postMessage('checkboxSelected:AADHAAR_CARD');
}, 100);
}
// Auto-select additional checkboxes based on AUTO_SELECT_CHECKBOXES list
function selectAdditionalCheckboxes() {
if (!AUTO_SELECT_CHECKBOXES || AUTO_SELECT_CHECKBOXES.length === 0) return;
var labels = document.querySelectorAll('label.form-check-label');
for (var i = 0; i < AUTO_SELECT_CHECKBOXES.length; i++) {
var targetText = AUTO_SELECT_CHECKBOXES[i].toLowerCase();
// Skip if already selected
if (window.additionalCheckboxesSelected[targetText]) continue;
for (var j = 0; j < labels.length; j++) {
var labelText = (labels[j].innerText || '').toLowerCase();
// Check if label text matches the target text
if (labelText.includes(targetText) || targetText.includes(labelText.trim())) {
var targetRow = labels[j].closest('.d-flex');
if (!targetRow) continue;
var checkbox = targetRow.querySelector('input[type="checkbox"][name="consent"]');
if (!checkbox) continue;
// Mark as selected before attempting to avoid duplicates
window.additionalCheckboxesSelected[targetText] = true;
// Toggle checkbox to ensure it's checked
checkbox.checked = false;
checkbox.dispatchEvent(new Event('change', { bubbles: true }));
(function(cb, txt) {
setTimeout(function() {
cb.checked = true;
cb.dispatchEvent(new Event('input', { bubbles: true }));
cb.dispatchEvent(new Event('change', { bubbles: true }));
cb.dispatchEvent(new Event('click', { bubbles: true }));
FlutterChannel.postMessage('checkboxSelected:' + txt);
}, 150);
})(checkbox, AUTO_SELECT_CHECKBOXES[i]);
break;
}
}
}
}
// OTP fill function (called from Flutter)
window.fillOTP = function(otp) {
var selectors = [
'input[type="text"][name*="otp"]',
'input[type="number"][id*="otp"]',
'input[placeholder*="OTP"]',
'input[placeholder*="otp"]'
];
for (var i = 0; i < selectors.length; i++) {
var element = document.querySelector(selectors[i]);
if (element) {
fillInput(element, otp);
return true;
}
}
// Fallback: Fill last text input
var textInputs = document.querySelectorAll('input[type="text"], input[type="number"]');
if (textInputs.length > 0) {
fillInput(textInputs[textInputs.length - 1], otp);
return true;
}
return false;
};
// Error detection
if (typeof window.reportedErrors === 'undefined') {
window.reportedErrors = new Set();
}
function detectAndReportErrors() {
var errors = [];
var aadhaarError = document.getElementById('invalid_uid');
if (aadhaarError && aadhaarError.textContent) {
errors.push(aadhaarError.textContent.trim());
}
var otpError = document.getElementById('send_err');
if (otpError && otpError.textContent) {
errors.push(otpError.textContent.trim());
}
var pinError = document.getElementById('v_pin_err');
if (pinError && pinError.textContent) {
errors.push(pinError.textContent.trim());
}
if (errors.length > 0) {
var key = errors.join(', ');
if (!window.reportedErrors.has(key)) {
window.reportedErrors.add(key);
FlutterChannel.postMessage('reportError:' + key);
}
} else {
window.reportedErrors.clear();
}
}
// Hook OTP generate button
function hookOTPButtons() {
var buttons = document.querySelectorAll('button, input[type="button"], input[type="submit"]');
for (var i = 0; i < buttons.length; i++) {
var btn = buttons[i];
var text = (btn.textContent || btn.value || '').toLowerCase();
if (text.includes('otp') && text.includes('generate')) {
btn.addEventListener('click', function() {
FlutterChannel.postMessage('onOTPGenerateClicked');
});
}
}
}
// Retry mechanism
var attempts = 0;
var maxAttempts = 10;
function retry() {
if (attempts >= maxAttempts) return;
attempts++;
attemptAadhaarAutofill();
scheduleEnsureActiveFieldVisible(120);
selectAadhaarConsentCheckbox();
selectAdditionalCheckboxes();
hookOTPButtons();
detectAndReportErrors();
if (!window.digilockerAlreadyFilled || !window.consentCheckboxSelected) {
setTimeout(retry, 500);
}
}
installKeyboardHandlers();
setTimeout(retry, 300);
// Monitor DOM changes
if (typeof MutationObserver !== 'undefined') {
var observer = new MutationObserver(function() {
attemptAadhaarAutofill();
selectAadhaarConsentCheckbox();
selectAdditionalCheckboxes();
hookOTPButtons();
});
observer.observe(document.body, { childList: true, subtree: true });
}
// Check errors periodically
setInterval(detectAndReportErrors, 2000);
FlutterChannel.postMessage('debug:Script initialized');
})();
''';
}