Merge pull request #548 from KOWX712/new_webui
Some checks failed
Android CI / build (push) Has been cancelled

spoofVendingSdk setting
This commit is contained in:
chiteroman 2025-02-25 21:15:50 +01:00 committed by GitHub
commit e4eb52f554
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 474 additions and 31 deletions

View File

@ -97,6 +97,12 @@ sdk_version="$(getprop ro.build.version.sdk)"
sdk_version="${sdk_version:-25}" sdk_version="${sdk_version:-25}"
echo "Device SDK version: $sdk_version" echo "Device SDK version: $sdk_version"
# Preserve previous setting
spoofVending="$(grep -oE '"spoofVendingSdk": [01]' "$MODDIR/pif.json" | cut -d' ' -f2)"
if [ -z "$spoofVending" ] || [ "$spoofVending" != 1 ]; then
spoofVending=0
fi
echo "- Dumping values to pif.json ..." echo "- Dumping values to pif.json ..."
cat <<EOF | tee pif.json cat <<EOF | tee pif.json
{ {
@ -104,7 +110,8 @@ cat <<EOF | tee pif.json
"MANUFACTURER": "Google", "MANUFACTURER": "Google",
"MODEL": "$MODEL", "MODEL": "$MODEL",
"SECURITY_PATCH": "$SECURITY_PATCH", "SECURITY_PATCH": "$SECURITY_PATCH",
"DEVICE_INITIAL_SDK_INT": $sdk_version "DEVICE_INITIAL_SDK_INT": $sdk_version,
"spoofVendingSdk": $spoofVending
} }
EOF EOF

View File

@ -60,6 +60,12 @@ if [ -d "/data/adb/modules/MagiskHidePropsConf" ]; then
ui_print "! WARNING, MagiskHidePropsConf module may cause issues with PIF." ui_print "! WARNING, MagiskHidePropsConf module may cause issues with PIF."
fi fi
# Preserve previous setting
spoofVending="$(grep -oE '"spoofVendingSdk": [01]' "/data/adb/modules/playintegrityfix/pif.json" | cut -d' ' -f2)"
if [ -n "$spoofVending" ] && [ "$spoofVending" -eq 1 ]; then
sed -i 's/"spoofVendingSdk": 0/"spoofVendingSdk": 1/' "$MODPATH/pif.json"
fi
# Check custom fingerprint # Check custom fingerprint
if [ -f "/data/adb/pif.json" ]; then if [ -f "/data/adb/pif.json" ]; then
ui_print "- Backup custom pif.json" ui_print "- Backup custom pif.json"

View File

@ -9,6 +9,35 @@
<script type="module" crossorigin src="scripts.js"></script> <script type="module" crossorigin src="scripts.js"></script>
</head> </head>
<body> <body>
<div class="output">[+] Fetching pif.json, please wait...</div> <div class="content">
<div class="header">
<h3>
Play Integrity Fix
<span id="version-text"></span>
</h3>
</div>
<div class="button-box">
<div class="toggle-list ripple-element" id="fetch">
<span class="toggle-text">Fetch pif.json</span>
</div>
<div class="toggle-list ripple-element" id="sdk-vending-toggle-container">
<span class="toggle-text">Spoof sdk version to Play Store</span>
<label class="toggle-switch">
<input type="checkbox" id="toggle-sdk-vending" disabled>
<span class="slider round"></span>
</label>
</div>
</div>
<div class="output-terminal">
<div class="output-terminal-header">
<span>output</span>
<div class="clear-terminal">
<svg xmlns="http://www.w3.org/2000/svg" height="14px" viewBox="0 -960 960 960" width="14px" fill="#ccc"><path d="M280-120q-33 0-56.5-23.5T200-200v-520h-40v-80h200v-40h240v40h200v80h-40v520q0 33-23.5 56.5T680-120H280Zm400-600H280v520h400v-520ZM360-280h80v-360h-80v360Zm160 0h80v-360h-80v360ZM280-720v520-520Z" /></svg>
<span class="clear-terminal-text">clear</span>
</div>
</div>
<div class="output-terminal-content"></div>
</div>
</div>
</body> </body>
</html> </html>

View File

@ -1,3 +1,9 @@
let actionRunning = false;
let initialPinchDistance = null;
let currentFontSize = 14;
const MIN_FONT_SIZE = 8;
const MAX_FONT_SIZE = 24;
// Execute shell commands with ksu.exec // Execute shell commands with ksu.exec
async function execCommand(command) { async function execCommand(command) {
const callbackName = `exec_callback_${Date.now()}`; const callbackName = `exec_callback_${Date.now()}`;
@ -10,34 +16,204 @@ async function execCommand(command) {
}); });
} }
// Function to run the script and display its output // Apply button event listeners
async function runAction() { function applyButtonEventListeners() {
const output = document.querySelector('.output'); const fetchButton = document.getElementById('fetch');
try { const sdkVendingToggle = document.getElementById('sdk-vending-toggle-container');
const scriptOutput = await execCommand("sh /data/adb/modules/playintegrityfix/action.sh"); const clearButton = document.querySelector('.clear-terminal');
output.innerHTML = '';
const lines = scriptOutput.split('\n'); fetchButton.addEventListener('click', runAction);
lines.forEach(line => { sdkVendingToggle.addEventListener('click', async () => {
const lineElement = document.createElement('div'); try {
lineElement.style.whiteSpace = 'pre-wrap'; const pifPath = await execCommand(`
lineElement.textContent = line; [ ! -f /data/adb/modules/playintegrityfix/pif.json ] || echo /data/adb/modules/playintegrityfix/pif.json
if (line === '') { [ ! -f /data/adb/pif.json ] || echo /data/adb/pif.json
lineElement.innerHTML = '&nbsp;'; `);
if (pifPath.trim() === "") {
appendToOutput("[!] No pif.json found");
return;
} }
output.appendChild(lineElement); const isChecked = document.getElementById('toggle-sdk-vending').checked;
}); const paths = pifPath.trim().split('\n');
} catch (error) { for (const path of paths) {
console.error('Script execution failed:', error); if (path) {
if (typeof ksu !== 'undefined' && ksu.mmrl) { await execCommand(`sed -i 's/"spoofVendingSdk": [01]/"spoofVendingSdk": ${isChecked ? 0 : 1}/' ${path}`);
output.innerHTML = '[!] Please allow permission in MMRL settings<br><br>[-] Settings<br>[-] Security<br>[-] Allow JavaScript API<br>[-] Play Integrity Fix<br>[-] Enable Allow Advanced KernelSU API'; }
} else { }
output.innerHTML = '[!] Error: Fail to execute action.sh'; appendToOutput(`[+] Successfully changed spoofVendingSdk to ${isChecked ? 0 : 1}`);
document.getElementById('toggle-sdk-vending').checked = !isChecked;
} catch (error) {
appendToOutput("[-] Failed to change spoofVendingSdk");
console.error('Failed to toggle sdk vending:', error);
} }
});
clearButton.addEventListener('click', () => {
const output = document.querySelector('.output-terminal-content');
output.innerHTML = '';
currentFontSize = 14;
updateFontSize(currentFontSize);
});
const terminal = document.querySelector('.output-terminal-content');
terminal.addEventListener('touchstart', (e) => {
if (e.touches.length === 2) {
e.preventDefault();
initialPinchDistance = getDistance(e.touches[0], e.touches[1]);
}
}, { passive: false });
terminal.addEventListener('touchmove', (e) => {
if (e.touches.length === 2) {
e.preventDefault();
const currentDistance = getDistance(e.touches[0], e.touches[1]);
if (initialPinchDistance === null) {
initialPinchDistance = currentDistance;
return;
}
const scale = currentDistance / initialPinchDistance;
const newFontSize = currentFontSize * scale;
updateFontSize(newFontSize);
initialPinchDistance = currentDistance;
}
}, { passive: false });
terminal.addEventListener('touchend', () => {
initialPinchDistance = null;
});
}
// Function to load the version from module.prop
async function loadVersionFromModuleProp() {
const versionElement = document.getElementById('version-text');
try {
const version = await execCommand("grep '^version=' /data/adb/modules/playintegrityfix/module.prop | cut -d'=' -f2");
versionElement.textContent = version.trim();
} catch (error) {
appendToOutput("[-] Failed to read version from module.prop");
console.error("Failed to read version from module.prop:", error);
} }
} }
// Function to load spoofVendingSdk config
async function loadSpoofVendingSdkConfig() {
const sdkVendingToggle = document.getElementById('toggle-sdk-vending');
const isChecked = await execCommand(`grep -o '"spoofVendingSdk": [01]' /data/adb/modules/playintegrityfix/pif.json | cut -d' ' -f2`);
if (isChecked === '0') {
sdkVendingToggle.checked = false;
} else {
sdkVendingToggle.checked = true;
}
}
// Function to append element in output terminal
function appendToOutput(content) {
const output = document.querySelector('.output-terminal-content');
if (content.trim() === "") {
const lineBreak = document.createElement('br');
output.appendChild(lineBreak);
} else {
const line = document.createElement('p');
line.className = 'output-content';
line.textContent = content;
output.appendChild(line);
}
output.scrollTop = output.scrollHeight;
}
// Function to run the script and display its output
async function runAction() {
if (actionRunning) return;
actionRunning = true;
try {
appendToOutput("[+] Fetching pif.json...");
await new Promise(resolve => setTimeout(resolve, 200));
const scriptOutput = await execCommand("sh /data/adb/modules/playintegrityfix/action.sh");
const lines = scriptOutput.split('\n');
lines.forEach(line => {
appendToOutput(line)
});
appendToOutput("");
} catch (error) {
console.error('Script execution failed:', error);
if (typeof ksu !== 'undefined' && ksu.mmrl) {
appendToOutput("");
appendToOutput("[!] Please allow permission in MMRL settings");
appendToOutput("[-] Settings");
appendToOutput("[-] Security");
appendToOutput("[-] Allow JavaScript API");
appendToOutput("[-] Play Integrity Fix");
appendToOutput("[-] Enable Allow Advanced KernelSU API");
appendToOutput("");
} else {
appendToOutput("[!] Error: Fail to execute action.sh");
appendToOutput("");
}
}
actionRunning = false;
}
// Function to apply ripple effect
function applyRippleEffect() {
document.querySelectorAll('.ripple-element').forEach(element => {
if (element.dataset.rippleListener !== "true") {
element.addEventListener("pointerdown", function (event) {
const ripple = document.createElement("span");
ripple.classList.add("ripple");
// Calculate ripple size and position
const rect = element.getBoundingClientRect();
const width = rect.width;
const size = Math.max(rect.width, rect.height);
const x = event.clientX - rect.left - size / 2;
const y = event.clientY - rect.top - size / 2;
// Determine animation duration
let duration = 0.2 + (width / 800) * 0.4;
duration = Math.min(0.8, Math.max(0.2, duration));
// Set ripple styles
ripple.style.width = ripple.style.height = `${size}px`;
ripple.style.left = `${x}px`;
ripple.style.top = `${y}px`;
ripple.style.animationDuration = `${duration}s`;
ripple.style.transition = `opacity ${duration}s ease`;
// Adaptive color
const computedStyle = window.getComputedStyle(element);
const bgColor = computedStyle.backgroundColor || "rgba(0, 0, 0, 0)";
const textColor = computedStyle.color;
const isDarkColor = (color) => {
const rgb = color.match(/\d+/g);
if (!rgb) return false;
const [r, g, b] = rgb.map(Number);
return (r * 0.299 + g * 0.587 + b * 0.114) < 96; // Luma formula
};
ripple.style.backgroundColor = isDarkColor(bgColor) ? "rgba(255, 255, 255, 0.2)" : "";
// Append ripple and handle cleanup
element.appendChild(ripple);
const handlePointerUp = () => {
ripple.classList.add("end");
setTimeout(() => {
ripple.classList.remove("end");
ripple.remove();
}, duration * 1000);
element.removeEventListener("pointerup", handlePointerUp);
element.removeEventListener("pointercancel", handlePointerUp);
};
element.addEventListener("pointerup", handlePointerUp);
element.addEventListener("pointercancel", handlePointerUp);
});
element.dataset.rippleListener = "true";
}
});
}
// Function to check if running in MMRL // Function to check if running in MMRL
function checkMMRL() { async function checkMMRL() {
if (typeof ksu !== 'undefined' && ksu.mmrl) { if (typeof ksu !== 'undefined' && ksu.mmrl) {
// Set status bars theme based on device theme // Set status bars theme based on device theme
try { try {
@ -52,12 +228,40 @@ function checkMMRL() {
} catch (error) { } catch (error) {
console.log("Error requesting API:", error); console.log("Error requesting API:", error);
} }
try {
await execCommand("whoami");
} catch (error) {
appendToOutput("");
appendToOutput("[!] Please allow permission in MMRL settings");
appendToOutput("[-] Settings");
appendToOutput("[-] Security");
appendToOutput("[-] Allow JavaScript API");
appendToOutput("[-] Play Integrity Fix");
appendToOutput("[-] Enable Allow Advanced KernelSU API");
appendToOutput("");
}
} else { } else {
console.log("Not running in MMRL environment."); console.log("Not running in MMRL environment.");
} }
} }
function getDistance(touch1, touch2) {
return Math.hypot(
touch1.clientX - touch2.clientX,
touch1.clientY - touch2.clientY
);
}
function updateFontSize(newSize) {
currentFontSize = Math.min(Math.max(newSize, MIN_FONT_SIZE), MAX_FONT_SIZE);
const terminal = document.querySelector('.output-terminal-content');
terminal.style.fontSize = `${currentFontSize}px`;
}
document.addEventListener('DOMContentLoaded', async () => { document.addEventListener('DOMContentLoaded', async () => {
checkMMRL(); checkMMRL();
setTimeout(runAction, 200); loadVersionFromModuleProp();
loadSpoofVendingSdkConfig();
applyButtonEventListeners();
applyRippleEffect();
}); });

View File

@ -6,17 +6,188 @@
} }
body { body {
font-family: 'Mono'; background-color: #F5F5F5;
padding-top: var(--window-inset-top); padding-top: var(--window-inset-top);
padding-bottom: var(--window-inset-bottom); padding-bottom: var(--window-inset-bottom);
} }
.output { .content {
font-size: 14px; padding-bottom: 10px;
left: 10px; display: flex;
width: calc(100vw - 20px); flex-direction: column;
align-items: center;
height: 90vh;
gap: 15px;
}
.header {
user-select: none;
}
.button-box {
width: calc(85vw + 30px);
max-width: 800px;
flex-shrink: 0;
background-color: #fff;
border: none;
border-radius: 15px;
box-sizing: border-box;
box-shadow: 0px 2px 4px rgba(0, 0, 0, 0.1);
overflow: hidden;
}
.toggle-list {
display: flex;
align-items: center;
background-color: #fff;
padding: 10px 20px;
white-space: nowrap; white-space: nowrap;
word-break: break-all; text-align: left;
border-bottom: 1px solid #ccc;
position: relative;
overflow: hidden;
}
.toggle-list:last-child {
border-bottom: none;
margin-bottom: 0;
}
.toggle-text {
font-size: 16px;
font-weight: bold;
white-space: wrap;
max-width: calc(100% - 76px);
user-select: none;
}
.toggle-switch {
position: relative;
display: inline-block;
margin-left: auto;
width: 40px;
height: 25px;
}
.toggle-switch input {
opacity: 0;
width: 0;
height: 0;
}
.slider {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: #ccc;
-webkit-transition: .4s;
transition: .4s;
}
.slider:before {
position: absolute;
content: "";
height: 19px;
width: 19px;
left: 3px;
bottom: 3px;
background-color: white;
transition: .4s;
}
input:checked+.slider {
background-color: #007bff;
}
input:focus+.slider {
box-shadow: 0 0 1px #007bff;
}
input:checked+.slider:before {
transform: translateX(15px);
}
.slider.round {
border-radius: 25px;
}
.slider.round:before {
border-radius: 50%;
}
.output-terminal {
width: calc(85vw + 30px);
max-width: 800px;
flex-grow: 1;
background-color: #fff;
box-sizing: border-box;
border-radius: 15px;
box-shadow: 0px 2px 4px rgba(0, 0, 0, 0.1);
overflow: hidden;
}
.output-terminal-header {
display: flex;
padding: 5px 15px;
font-size: 14px;
justify-content: space-between;
background-color: #E0E0E0;
color: #333;
user-select: none;
}
.clear-terminal {
display: flex;
align-items: center;
}
.clear-terminal svg {
fill: #333;
}
.output-terminal-content {
font-family: 'Mono';
font-size: 14px;
padding: 10px;
width: calc(100% - 20px);
height: calc(100% - 50px);
overflow-y: scroll;
}
.output-content {
position: relative;
width: 100%;
padding: 0;
margin: 0;
white-space: pre-wrap;
word-break: break-word;
}
.ripple-element {
position: relative;
overflow: hidden;
}
.ripple {
position: absolute;
border-radius: 50%;
transform: scale(0);
opacity: 1;
animation: ripple-animation ease-out forwards;
pointer-events: none;
background: rgba(0, 0, 0, 0.2);
}
.ripple.end {
opacity: 0;
}
@keyframes ripple-animation {
to {
transform: scale(3);
}
} }
@media (prefers-color-scheme: dark) { @media (prefers-color-scheme: dark) {
@ -24,4 +195,30 @@ body {
background-color: #121212; background-color: #121212;
color: #fff; color: #fff;
} }
.toggle-list,
.button-box,
.output-terminal-header {
background-color: #343434;
}
.output-terminal-header {
color: #ccc;
}
.clear-terminal svg {
fill: #ccc;
}
.output-terminal {
background-color: #232323;
}
.toggle-list {
border-bottom: 1px solid #6E6E6E;
}
.slider {
background-color: #6E6E6E;
}
} }