Evolver: Fix and improve keybox data preference

* Add space for icon
* Use linear layout throughout
* Make toasts translatable
* Dynamically update summary when XML loaded/cleared
* Improve checks and XML loading

Signed-off-by: Pranav Vashi <neobuddy89@gmail.com>
This commit is contained in:
Pranav Vashi
2025-08-21 18:30:48 +05:30
committed by Joey
parent b1a5405a1c
commit a1d910c260
4 changed files with 95 additions and 33 deletions

View File

@@ -1,10 +1,35 @@
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:minHeight="?android:attr/listPreferredItemHeightSmall"
android:paddingStart="?android:attr/listPreferredItemPaddingStart"
android:paddingEnd="?android:attr/listPreferredItemPaddingEnd">
android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
android:clipToPadding="false"
android:baselineAligned="false">
<LinearLayout
android:id="@+id/icon_frame"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:minWidth="48dp"
android:gravity="start|center_vertical"
android:orientation="horizontal"
android:paddingStart="0dp"
android:paddingEnd="8dp"
android:paddingTop="4dp"
android:paddingBottom="4dp">
<androidx.preference.internal.PreferenceImageView
android:id="@android:id/icon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:contentDescription="@null"
app:maxWidth="48dp"
app:maxHeight="48dp" />
</LinearLayout>
<LinearLayout
android:layout_width="0dp"
@@ -16,28 +41,33 @@
<TextView
android:id="@+id/title"
android:layout_width="match_parent"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceListItem"
android:maxLines="1"
android:ellipsize="end"
android:maxLines="1" />
android:textAppearance="?android:attr/textAppearanceListItem" />
<TextView
android:id="@+id/summary"
android:layout_width="match_parent"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceSmall"
android:textAppearance="?android:attr/textAppearanceListItemSecondary"
android:textColor="?android:attr/textColorSecondary"
android:ellipsize="marquee" />
android:maxLines="5"
android:ellipsize="end" />
</LinearLayout>
<ImageButton
android:id="@+id/delete_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:minWidth="48dp"
android:minHeight="48dp"
android:layout_gravity="center_vertical"
android:src="@drawable/ic_trash_can"
android:background="?android:attr/selectableItemBackgroundBorderless"
android:scaleType="centerInside"
android:contentDescription="@string/keybox_delete_content_description"
android:duplicateParentState="true"
android:layout_marginStart="16dp"
android:layout_marginEnd="8dp" />
</LinearLayout>

View File

@@ -566,8 +566,14 @@
<string name="pif_spoofing_summary">Choose the PIF JSON file to be used to spoof Play Integrity</string>
<!-- Miscellaneous/Spoofing/Keybox Data -->
<string name="keybox_data_title">Select keybox XML to spoof</string>
<string name="keybox_data_summary">Select the keybox XML used for system-wide key attestation spoofing. Click delete to reset keybox data.</string>
<string name="keybox_data_title">Keybox attestation override</string>
<string name="keybox_data_summary">Load a custom keybox XML to override device key attestation</string>
<string name="keybox_data_loaded_summary">Custom keybox XML loaded. Delete to clear.</string>
<string name="keybox_delete_content_description">Clear keybox data</string>
<string name="keybox_toast_invalid_file_selected">Not an XML file. Choose a valid keybox XML.</string>
<string name="keybox_toast_missing_data">Invalid keybox XML: required fields missing</string>
<string name="keybox_toast_file_loaded">Keybox loaded</string>
<string name="keybox_toast_file_cleared">Keybox cleared</string>
<!-- About -->
<string name="about_info">Evolution X is a custom Android ROM that aims to replicate the Google Pixel experience, with added customization. Based on the LineageOS Project.</string>

View File

@@ -15,9 +15,7 @@
<!-- Keybox Data -->
<org.evolution.settings.preferences.KeyboxDataPreference
android:key="keybox_data_setting"
android:title="@string/keybox_data_title"
android:summary="@string/keybox_data_summary"
android:defaultValue=""/>
android:title="@string/keybox_data_title" />
<!-- GMS spoof -->
<org.evolution.settings.preferences.SystemPropertySwitchPreference

View File

@@ -1,11 +1,13 @@
package org.evolution.settings.preferences;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.provider.Settings;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
import android.widget.ImageButton;
import android.widget.TextView;
import android.widget.Toast;
@@ -42,42 +44,66 @@ public class KeyboxDataPreference extends Preference {
@Override
public void onBindViewHolder(PreferenceViewHolder holder) {
super.onBindViewHolder(holder);
final Context ctx = getContext();
final ContentResolver cr = ctx.getContentResolver();
TextView title = (TextView) holder.findViewById(R.id.title);
TextView summary = (TextView) holder.findViewById(R.id.summary);
ImageButton deleteButton = (ImageButton) holder.findViewById(R.id.delete_button);
title.setText(getTitle());
summary.setText(getSummary());
boolean hasData = Settings.Secure.getString(
cr, Settings.Secure.KEYBOX_DATA) != null;
summary.setText(ctx.getString(
hasData ? R.string.keybox_data_loaded_summary : R.string.keybox_data_summary));
deleteButton.setVisibility(hasData ? View.VISIBLE : View.GONE);
deleteButton.setEnabled(hasData);
holder.itemView.setOnClickListener(v -> {
if (mFilePickerLauncher != null) {
Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
intent.setType("text/xml");
intent.setType("*/*");
intent.putExtra(Intent.EXTRA_MIME_TYPES, new String[]{"text/xml", "application/xml"});
intent.addCategory(Intent.CATEGORY_OPENABLE);
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
mFilePickerLauncher.launch(intent);
}
});
deleteButton.setOnClickListener(v -> {
Settings.Secure.putString(getContext().getContentResolver(),
Settings.Secure.KEYBOX_DATA, null);
Toast.makeText(getContext(), "XML data cleared", Toast.LENGTH_SHORT).show();
callChangeListener(null);
if (!callChangeListener(Boolean.FALSE)) return;
Settings.Secure.putString(cr, Settings.Secure.KEYBOX_DATA, null);
Toast.makeText(ctx, ctx.getString(R.string.keybox_toast_file_cleared), Toast.LENGTH_SHORT).show();
notifyChanged();
});
}
public void handleFileSelected(Uri uri) {
if (uri == null ||
(!uri.toString().endsWith(".xml") &&
!"text/xml".equals(getContext().getContentResolver().getType(uri)))) {
Toast.makeText(getContext(), "Invalid file selected", Toast.LENGTH_SHORT).show();
final Context ctx = getContext();
final ContentResolver cr = ctx.getContentResolver();
if (uri == null) {
Toast.makeText(ctx,
ctx.getString(R.string.keybox_toast_invalid_file_selected), Toast.LENGTH_SHORT).show();
return;
}
try (InputStream inputStream = getContext().getContentResolver().openInputStream(uri);
BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream))) {
final String type = cr.getType(uri);
boolean isXmlMime = "text/xml".equals(type) || "application/xml".equals(type);
boolean hasXmlExt = (uri.getPath() != null && uri.getPath().toLowerCase().endsWith(".xml"));
if (!isXmlMime && !hasXmlExt) {
Toast.makeText(ctx,
ctx.getString(R.string.keybox_toast_invalid_file_selected), Toast.LENGTH_SHORT).show();
return;
}
try (InputStream inputStream = cr.openInputStream(uri);
BufferedReader reader = new BufferedReader(
new InputStreamReader(inputStream, java.nio.charset.StandardCharsets.UTF_8))) {
StringBuilder xmlContent = new StringBuilder();
String line;
while ((line = reader.readLine()) != null) {
@@ -86,18 +112,20 @@ public class KeyboxDataPreference extends Preference {
String xml = xmlContent.toString();
if (!validateXml(xml)) {
Toast.makeText(getContext(), "Invalid XML: missing required data", Toast.LENGTH_SHORT).show();
Toast.makeText(ctx,
ctx.getString(R.string.keybox_toast_missing_data), Toast.LENGTH_SHORT).show();
return;
}
Settings.Secure.putString(getContext().getContentResolver(),
Settings.Secure.KEYBOX_DATA, xml);
Toast.makeText(getContext(), "XML file loaded", Toast.LENGTH_SHORT).show();
callChangeListener(xml);
if (!callChangeListener(Boolean.TRUE)) return;
Settings.Secure.putString(cr, Settings.Secure.KEYBOX_DATA, xml);
Toast.makeText(ctx,
ctx.getString(R.string.keybox_toast_file_loaded), Toast.LENGTH_SHORT).show();
notifyChanged();
} catch (IOException e) {
Log.e(TAG, "Failed to read XML file", e);
Toast.makeText(getContext(), "Failed to read XML", Toast.LENGTH_SHORT).show();
Toast.makeText(ctx,
ctx.getString(R.string.keybox_toast_invalid_file_selected), Toast.LENGTH_SHORT).show();
}
}