diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index ad5e46c2..d2198716 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -28,6 +28,7 @@ tools:replace="android:theme" tools:targetApi="31"> + entriesList = new ArrayList<>(); - for (renderEntries enums : renderEntries.values()) - entriesList.add(enums.name); +// Set entryValueList = renderersMap.keySet(); + + List entriesList = new ArrayList<>(); //用户友好名称,内容为name + List entryValueList = new ArrayList<>(); //存储的值, 内容为key + for (String key : renderersMap.keySet()) { + entriesList.add(renderersMap.get(key).getString("name")); + entryValueList.add(key); + } renderPref.setEntries(entriesList.toArray(new String[0])); - renderPref.setEntryValues(entriesList.toArray(new String[0])); //entry和value用同一个吧,也没啥区别 - renderPref.setDefaultValue(entriesList.get(0)); + renderPref.setEntryValues(entryValueList.toArray(new String[0])); + renderPref.setDefaultValue(entryValueList.get(0)); getPreferenceScreen().addPreference(renderPref); } @@ -123,8 +141,10 @@ public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, Strin */ @Override public void onDisplayPreferenceDialog(Preference preference) { - if (preference.getKey().equals("SCREEN_SIZE") && funcEnabled(enable_custom_resolution)) { + if (preference.getKey().equals("SCREEN_SIZE") && enable_custom_resolution) { buildResolutionDialog(preference); + } else if (preference.getKey().equals(KEY_RENDERER) && enable_different_renderers) { + ConSetRenderer.buildRendererDialog((ListPreference) preference); } else { super.onDisplayPreferenceDialog(preference); return; @@ -132,9 +152,8 @@ public void onDisplayPreferenceDialog(Preference preference) { } - private boolean funcEnabled(boolean func) { - return func || QH.isTesting(); - } + + private void buildResolutionDialog(Preference preference) { //如果是分辨率选项,自定义一下 @@ -196,38 +215,32 @@ private void buildResolutionDialog(Preference preference) { //设置监听,点击单选项时存到curResolution - radioGroup.setOnCheckedChangeListener(new RadioGroup.OnCheckedChangeListener() { - @Override - public void onCheckedChanged(RadioGroup group, int checkedId) { - String selectStr = ((RadioButton) group.findViewById(checkedId)).getText().toString(); - for (int i = 0; i < strEntries.length; i++) { - if (strEntries[i].equals(selectStr)) { - curResolution = strValues[i]; - break; - } + radioGroup.setOnCheckedChangeListener((group, checkedId) -> { + String selectStr = ((RadioButton) group.findViewById(checkedId)).getText().toString(); + for (int i = 0; i < strEntries.length; i++) { + if (strEntries[i].equals(selectStr)) { + curResolution = strValues[i]; + break; } } }); //设置监听,开关切换时禁用和开启对应项 - switchToCustom.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { - @Override - public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { - if (isChecked) { - //禁用radiobutton - for (int i = 0; i < radioGroup.getChildCount(); i++) { - radioGroup.getChildAt(i).setEnabled(false); - } - //启用edittext - widthEText.setEnabled(true); - heightEText.setEnabled(true); - - } else { - for (int i = 0; i < radioGroup.getChildCount(); i++) { - radioGroup.getChildAt(i).setEnabled(true); - } - widthEText.setEnabled(false); - heightEText.setEnabled(false); + switchToCustom.setOnCheckedChangeListener((buttonView, isChecked) -> { + if (isChecked) { + //禁用radiobutton + for (int i = 0; i < radioGroup.getChildCount(); i++) { + radioGroup.getChildAt(i).setEnabled(false); } + //启用edittext + widthEText.setEnabled(true); + heightEText.setEnabled(true); + + } else { + for (int i = 0; i < radioGroup.getChildCount(); i++) { + radioGroup.getChildAt(i).setEnabled(true); + } + widthEText.setEnabled(false); + heightEText.setEnabled(false); } }); @@ -253,20 +266,17 @@ else if (i == strEntries.length - 1) { } Log.d(TAG, "onDisplayPreferenceDialog: 获取到的array为" + Arrays.toString(strEntries)); - new AlertDialog.Builder(requireContext()).setView(dialogView).setTitle(preference.getTitle()).setPositiveButton(android.R.string.yes, new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - //关闭对话框且点的是确认键,则修改sharePref - //如果是自定义分辨率,检查完整性并转为字符串形式 - if (switchToCustom.isChecked()) { - //不知道怎么取消退出dialog,如果没填就改成0吧 - curResolution = (TextUtils.isEmpty(widthEText.getText().toString()) ? "0" : widthEText.getText().toString()) + "," + (TextUtils.isEmpty(heightEText.getText().toString()) ? "0" : heightEText.getText().toString()); - } - preference.getSharedPreferences().edit().putString("SCREEN_SIZE", curResolution).apply(); -// Toast.makeText(getContext(), "修改设置成功,重启应用生效", Toast.LENGTH_SHORT).show(); - //更新summary的显示 - preference.setSummary(curResolution); + new AlertDialog.Builder(requireContext()).setView(dialogView).setTitle(preference.getTitle()).setPositiveButton(android.R.string.yes, (dialog, which) -> { + //关闭对话框且点的是确认键,则修改sharePref + //如果是自定义分辨率,检查完整性并转为字符串形式 + if (switchToCustom.isChecked()) { + //不知道怎么取消退出dialog,如果没填就改成0吧 + curResolution = (TextUtils.isEmpty(widthEText.getText().toString()) ? "0" : widthEText.getText().toString()) + "," + (TextUtils.isEmpty(heightEText.getText().toString()) ? "0" : heightEText.getText().toString()); } + preference.getSharedPreferences().edit().putString("SCREEN_SIZE", curResolution).apply(); +// Toast.makeText(getContext(), "修改设置成功,重启应用生效", Toast.LENGTH_SHORT).show(); + //更新summary的显示 + preference.setSummary(curResolution); }).setNegativeButton(android.R.string.cancel, null).show(); } @@ -286,28 +296,4 @@ private void updatePreference(Preference preference) { } - /** - * 渲染器选项。entry和entryvalue都用这一个了 - * 需要在添加环境变量的那个action(即外部)获取对应的字符串,如果硬编码,modder需要修改两处,可能会忽略。用enum只需要改一处即可。 - */ - public enum renderEntries { - LLVMPipe("LLVMPipe", "/opt/lib/llvm"), - VirGL_Overlay("VirGL Overlay", "/opt/lib/vo"), - VirGL_built_in("VirGL built-in", "/opt/lib/vb"), - VirtIO_GPU("VirtIO-GPU", "/opt/lib/vg"), - Turnip_Zink("Turnip Zink", "/opt/lib/tz"), - Turnip_DXVK("Turnip DXVK","/opt/lib/td"), - ; - - - public final String name; - public final String path; - - renderEntries(String name, String path) { - this.name = name; - this.path = path; - } - } - - } diff --git a/app/src/main/java/com/example/datainsert/exagear/FAB/FabMenu.java b/app/src/main/java/com/example/datainsert/exagear/FAB/FabMenu.java index cc711e3e..3d37e893 100644 --- a/app/src/main/java/com/example/datainsert/exagear/FAB/FabMenu.java +++ b/app/src/main/java/com/example/datainsert/exagear/FAB/FabMenu.java @@ -21,6 +21,7 @@ import com.example.datainsert.exagear.FAB.dialogfragment.PulseAudio; import com.example.datainsert.exagear.FAB.dialogfragment.customcontrols.CustomControls; import com.example.datainsert.exagear.QH; +import com.example.datainsert.exagear.RR; import java.util.ArrayList; import java.util.List; @@ -43,6 +44,9 @@ public class FabMenu { @SuppressLint("RtlHardcoded") public FabMenu(AppCompatActivity a) { + //重启activity时刷新locale + RR.locale = RR.refreshLocale(); + FloatingActionButton fab = new FloatingActionButton(a); //不知道为什么,下面设置了customSize,这里如果是wrap content 宽高都变成0 LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(AndroidHelpers.dpToPx(60), AndroidHelpers.dpToPx(60));//AndroidHelpers.dpToPx(60),AndroidHelpers.dpToPx(60) diff --git a/app/src/main/java/com/example/datainsert/exagear/FAB/dialogfragment/AboutFab.java b/app/src/main/java/com/example/datainsert/exagear/FAB/dialogfragment/AboutFab.java index 1ebbb545..83ad6689 100644 --- a/app/src/main/java/com/example/datainsert/exagear/FAB/dialogfragment/AboutFab.java +++ b/app/src/main/java/com/example/datainsert/exagear/FAB/dialogfragment/AboutFab.java @@ -4,12 +4,19 @@ import android.content.Context; import android.content.DialogInterface; +import android.graphics.Typeface; import android.os.Environment; import android.support.design.widget.Snackbar; import android.support.v7.app.AppCompatActivity; import android.text.Html; +import android.util.Log; +import android.util.TypedValue; +import android.view.Gravity; +import android.view.View; import android.view.ViewGroup; +import android.view.animation.Animation; import android.view.animation.AnimationUtils; +import android.view.animation.ScaleAnimation; import android.widget.LinearLayout; import android.widget.TextView; @@ -17,6 +24,7 @@ import com.example.datainsert.exagear.QH; import com.example.datainsert.exagear.RR; +import java.io.File; import java.io.IOException; /** @@ -30,6 +38,7 @@ public class AboutFab extends BaseFragment { * 首次安装应用后显示提示。默认false表示还没显示过提示。 */ private static final String PREF_FIRST_LAUNCH_INFO_SHOWN = "PREF_FIRST_LAUNCH_INFO_SHOWN"; + private static boolean appFirstLaunching = true; @@ -57,6 +66,47 @@ protected ViewGroup buildUI() { linearLayout.addView(tvInfo, tvInfoParams); + //显示一个可以点击的颜文字 + String fulltext = "( ˡ ᴗ ˡ ) ";//⩊ ᴗ + +// AnimationUtils.loadAnimation(requireContext(),R.anim.text_scale) + ScaleAnimation scale = new ScaleAnimation(0,1,0,1, Animation.RELATIVE_TO_SELF,0f,Animation.RELATIVE_TO_SELF,0.5f); + scale.setDuration(150); + scale.setInterpolator(requireContext(), android.R.anim.overshoot_interpolator); + + LinearLayout linearFun = new LinearLayout(c); + linearFun.setPadding(40,40,40,40); + linearFun.setOrientation(LinearLayout.HORIZONTAL); + View.OnClickListener aniListener = new View.OnClickListener() { + private int animIndex = 0; + private final int viewCount = fulltext.length() / 2; + @Override + public void onClick(View v) { + //设置部分文字可见 + for(int i=0; i getPreference().edit().putBoolean(PREF_KEY_PULSE_ENABLE_LOG, isChecked).apply()); checkEnableLog.setChecked(getPreference().getBoolean(PREF_KEY_PULSE_ENABLE_LOG, true)); - checkEnableLog.setEnabled(getPreference().getBoolean(PREF_KEY_PULSE_AUTORUN, true)); + checkEnableLog.setEnabled(getPreference().getBoolean(PREF_KEY_PULSE_AUTORUN, PREF_DEF_VAL_PULSE_AUTORUN)); linearRoot.addView(checkEnableLog, paddingParams); linearRoot.addView(getDescriptionTextView(String.format(checkLogStr[1], requireContext().getPackageName()))); @@ -193,7 +204,7 @@ protected ViewGroup buildUI() { getPreference().edit().putBoolean(PREF_KEY_PULSE_AUTORUN, isChecked).apply(); checkEnableLog.setEnabled(isChecked); }); - checkAutorun.setChecked(getPreference().getBoolean(PREF_KEY_PULSE_AUTORUN, true)); + checkAutorun.setChecked(getPreference().getBoolean(PREF_KEY_PULSE_AUTORUN, PREF_DEF_VAL_PULSE_AUTORUN)); //修改启动参数 diff --git a/app/src/main/java/com/example/datainsert/exagear/QH.java b/app/src/main/java/com/example/datainsert/exagear/QH.java index f1138103..2e82384e 100644 --- a/app/src/main/java/com/example/datainsert/exagear/QH.java +++ b/app/src/main/java/com/example/datainsert/exagear/QH.java @@ -26,6 +26,11 @@ import com.eltechs.axs.activities.FrameworkActivity; import com.eltechs.axs.applicationState.ApplicationStateBase; +import java.io.File; + +/** + * 不要随意修改已有方法的定义,否则会与旧功能不兼容。但是可以修改其内容 + */ public class QH { private final static String TAG = "Helpers"; public final static String MY_SHARED_PREFERENCE_SETTING = "some_settings"; @@ -193,4 +198,18 @@ public static void showTipDialogWithDisable(Context a,String tips,String PREF_K new AlertDialog.Builder(a).setView(scrollView).setPositiveButton(android.R.string.yes, null).create().show(); } + + public static class Files{ + /** + * 日志输出的文件夹。设为Android/data/包名/files/logs + * @return file对象。确保该文件夹已经创建 + */ + public static File logsDir(){ + File logDir = new File(Globals.getAppContext().getExternalFilesDir(null), "logs"); + if(!logDir.exists()){ + boolean b = logDir.mkdirs(); + } + return logDir; + } + } } diff --git a/app/src/main/java/com/example/datainsert/exagear/RR.java b/app/src/main/java/com/example/datainsert/exagear/RR.java index eefe88fe..a17b64a3 100644 --- a/app/src/main/java/com/example/datainsert/exagear/RR.java +++ b/app/src/main/java/com/example/datainsert/exagear/RR.java @@ -139,6 +139,7 @@ public class RR { public static int mw_tips = 117; public static int mw_contNoWineTips = 118; public static int render_title = 119; + public static int fab_hide = 120; public static int pa_title = 130; public static int pa_explain = 131; public static int pa_checkRun = 132; @@ -147,6 +148,7 @@ public class RR { public static int pa_troubleShooting = 135; + public static String locale = refreshLocale(); static { @@ -281,6 +283,7 @@ public class RR { "\n"); zhArray.put(mw_contNoWineTips, "没有检测到已启用的wine,本次创建的容器可能无法启动。建议删除该容器,点击下载按钮下载并安装wine后,重新创建容器。"); zhArray.put(render_title, "图形渲染设置"); + zhArray.put(fab_hide,"隐藏"); zhArray.put(pa_title, "PulseAudio (XSDL)"); zhArray.put(pa_explain, "PulseAudio用于播放音频,可以缓解一部分声音问题。本功能用到的PulseAudio服务端提取自Xserver XSDL,需要手机支持64位。"); zhArray.put(pa_checkRun, "开启pulseaudio服务$在启动容器时一并启动PulseAudio服务"); @@ -295,7 +298,6 @@ public class RR { "\n4. wine注册表声音驱动项设置正确:某些数据包会在左下角起点提供切换pulse声音的便捷注册表项。也可以手动修改:左下角起点 - 运行 - 输入regedit打开,找到以下路径:[HKEY_CURENT_USER\\Software\\Wine\\Drivers] 若右侧包含“Audio”项,则该项的值应包含字符串“pulse”。" + "\n \n "); - /* @@ -434,6 +436,7 @@ public class RR { "\n"); enArray.put(mw_contNoWineTips, "No active Wine detected. This container probably can not launch. Please delete it, click the download button to install Wines and try again."); enArray.put(render_title, "Renderer"); + enArray.put(fab_hide,"Hide"); enArray.put(pa_title, "PulseAudio (XSDL)"); enArray.put(pa_explain, "PulseAudio is used to play audio, reducing sound problems. This function uses PulseAudio server extracted from Xserver XSDL. It requires 64-bit support on your device."); enArray.put(pa_checkRun, "Enable PulseAudio service$Start PulseAudio server when launching a container"); @@ -589,19 +592,19 @@ public class RR { "
  •   Загрузка файлов wine:
    загрузка всех видов wine из интернета. Доступные источники: WineHQ (официальная сборка, перечислены только сборки ubuntu18) и Kron4ek (уменьшенный размер, промежуточные версии wine не включены). Загруженные версии wine появляются на странице 'Установленные'.
  • \n" + "\n"); ruArray.put(mw_contNoWineTips, "Активный Wine не найден. Этот контейнер вероятно, не может запуститься. Удалите его, затем нажмите кнопку загрузки, чтобы установить необходимый Wine и повторите попытку."); - ruArray.put(render_title, "Renderer"); + ruArray.put(fab_hide,"Скрыть"); ruArray.put(pa_title, "PulseAudio (XSDL)"); - ruArray.put(pa_explain, "PulseAudio is used to play audio, reducing sound problems. This function uses PulseAudio server extracted from Xserver XSDL. It requires 64-bit support on your device."); - ruArray.put(pa_checkRun, "Enable PulseAudio service$Start PulseAudio server when launching a container"); - ruArray.put(pa_checkLog, "Output logs$Save runtime logs to Android/data/%s/files/logs/palog.txt"); - ruArray.put(pa_btnParam, "Edit launching params$Wrong params will result in errors, please modify with caution"); - ruArray.put(pa_troubleShooting, "Troubleshooting ⓘ$" + - "Check if PulseAudio service is working properly: After Launching the container, click start menu - Run - type winecfg to open it - Audio: \"Selected drivers\" should be winepulse.drv, click \"Test Sound\" button to hear the test sound." + - "\n\nIf the result is incorrect, please check the following conditions:" + - "$1. Pulseaudio elf file is executed correctly: Check \"Output logs\" option, launch the container and look into the log for errors. If started with default parameters, there should be \"Daemon startup successful.\"" + - "\n2. Pulse tcp module is loaded correctly: If started with default parameters, . /pulseaudio.conf will automatically load the tcp module without additional settings: load-module module-native-protocol-tcp auth-anonymous=true port=4713." + - "\n3. Export environment variables: PULSE_SERVER=tcp:127.0.0.1:4713. This function will automatically add environment variables when starting the container, no additional settings are needed. Environment variables can be confirmed with Exaterm or Putty." + - "\n4. Wine sound driver in registry: Some caches provides a convenient registry entry for switching into pulse sound at the start menu. Or you can change it manually: Click start menu - Run - Type regedit to open it and find the following path: [HKEY_CURENT_USER\\Software\\Wine\\Drivers]. If \"Audio\" appears at the right side, it's value should contain the string \"pulse\"." + + ruArray.put(pa_explain, "PulseAudio используется для воспроизведения звука, уменьшения проблемы связанных со звуком. Эта функция использует сервер PulseAudio, извлеченный из apk Xserver XSDL. Что требуется поддержка 64-битной версии Андроид на вашем девайсе."); + ruArray.put(pa_checkRun, "Включить службу PulseAudio$Запускать сервер PulseAudio при запуске контейнера"); + ruArray.put(pa_checkLog, "Вывод лога$Сохранить лог в Android/data/%s/files/logs/palog.txt"); + ruArray.put(pa_btnParam, "Изменить параметры запуска$Неверные параметры приведут к ошибкам изменяйте их только с пониманием того что делаете."); + ruArray.put(pa_troubleShooting, "Устранение неполадок ⓘ$" + + "Проверьте, правильно ли работает служба PulseAudio: для этого после запуска контейнера щелкните меню Пуск - Выполнить - введите winecfg, чтобы открыть его, на вкладке Аудио: \"В выбранные драйверы\" должны быть winepulse.drv, нажмите кнопку \"Проверить звук\", чтобы услышать тестовый звук." + + "\n\nЕсли нет результата, проверьте следующие условия:" + + "$1. Файл Pulseaudio для работы выполняется правильно: установите флаг \"Вывод лога\", запустите контейнер и просмотрите лог на наличие ошибок. При запуске с параметрами по умолчанию должно быть сообщение \"Успешный запуск образа\"" + + "\n2. Модуль Pulse tcp загружается правильно: если он запущен с параметрами по умолчанию, . /pulseaudio.conf автоматически загрузит модуль tcp без дополнительных настроек: load-module module-native-protocol-tcp auth-anonymous=true port=4713." + + "\n3. Экспорт переменных среды: PULSE_SERVER=tcp:127.0.0.1:4713. Эта функция автоматически добавит переменные окружения при запуске контейнера, никаких дополнительных настроек не требуется. Переменные среды можно проверить с помощью Exaterm или Putty." + + "\n4. Драйвер звука Wine в реестре: некоторые кеши предоставляют удобную запись в реестре для переключения на Pulseaudio в меню Пуск. Или вы можете отредактировать его вручную: Нажмите меню Пуск - Выполнить - введите regedit, чтобы открыть редактор реестра, и найдите следующий путь: [HKEY_CURENT_USER\\Software\\Wine\\Drivers]. создайте строковый параметр \"Audio\" и укажите его значением \"pulse\"." + "\n \n "); diff --git a/app/src/main/java/com/example/datainsert/exagear/action/AddEnvironmentVariables.java b/app/src/main/java/com/example/datainsert/exagear/action/AddEnvironmentVariables.java index b2f0bd3e..b3c048cc 100644 --- a/app/src/main/java/com/example/datainsert/exagear/action/AddEnvironmentVariables.java +++ b/app/src/main/java/com/example/datainsert/exagear/action/AddEnvironmentVariables.java @@ -1,10 +1,13 @@ package com.example.datainsert.exagear.action; -import static com.eltechs.ed.fragments.ContainerSettingsFragment.renderEntries.Turnip_DXVK; -import static com.eltechs.ed.fragments.ContainerSettingsFragment.renderEntries.VirGL_Overlay; -import static com.eltechs.ed.fragments.ContainerSettingsFragment.renderEntries.VirGL_built_in; -import static com.eltechs.ed.fragments.ContainerSettingsFragment.renderEntries.VirtIO_GPU; +import static com.example.datainsert.exagear.containerSettings.ConSetRenderer.RenEnum.LLVMPipe; +import static com.example.datainsert.exagear.containerSettings.ConSetRenderer.RenEnum.Turnip_DXVK; +import static com.example.datainsert.exagear.containerSettings.ConSetRenderer.RenEnum.Turnip_Zink; +import static com.example.datainsert.exagear.containerSettings.ConSetRenderer.RenEnum.VirGL_Overlay; +import static com.example.datainsert.exagear.containerSettings.ConSetRenderer.RenEnum.VirGL_built_in; +import static com.example.datainsert.exagear.containerSettings.ConSetRenderer.RenEnum.VirtIO_GPU; +import static com.example.datainsert.exagear.containerSettings.ConSetRenderer.renderersMap; import static com.eltechs.ed.guestContainers.GuestContainerConfig.CONTAINER_CONFIG_FILE_KEY_PREFIX; import static com.example.datainsert.exagear.mutiWine.MutiWine.KEY_WINE_INSTALL_PATH; @@ -23,13 +26,17 @@ import com.eltechs.ed.fragments.ContainerSettingsFragment; import com.example.datainsert.exagear.FAB.dialogfragment.PulseAudio; import com.example.datainsert.exagear.QH; +import com.example.datainsert.exagear.containerSettings.ConSetRenderer; import org.apache.commons.io.FileUtils; import java.io.File; +import java.io.FileNotFoundException; import java.io.IOException; +import java.io.PrintWriter; import java.lang.reflect.Field; import java.util.List; +import java.util.Objects; /** * 作为action,在startguest启动时添加环境变量。 @@ -39,11 +46,16 @@ public class AddEnvironmentVariables extends AbstractStartupAction { private static final String TAG = "AddEnvironmentVariables"; + private static Mcat mcat; /** * 可能多个地方用到的,放到成员变量里,最后再加入。每次拼接的时候,路径前带冒号(e.g. LD_LIBRARY_PATH.insert(0,ldPath).insert(0,':'); ) */ private StringBuilder LD_LIBRARY_PATH = new StringBuilder(":/usr/lib/i386-linux-gnu"); + public AddEnvironmentVariables() { + + } + @Override public void execute() { UBTLaunchConfiguration ubtConfig = getApplicationState().getUBTLaunchConfiguration(); @@ -82,71 +94,103 @@ public void execute() { //添加渲染对应的ld_library_path环境变量 (改成在容器设置里修改 try { - Field field = ContainerSettingsFragment.class.getField("KEY_RENDERER"); //直接获取成员,抛出的是NoSuchFieldError 不属于exception - addRendererPath(sp, ubtConfig); + Field field = ContainerSettingsFragment.class.getField("enable_different_renderers"); //直接获取成员,抛出的是NoSuchFieldError 不属于exception + if (ContainerSettingsFragment.enable_different_renderers) { + addRendererPath(sp, ubtConfig); + } } catch (Exception | NoSuchFieldError e) { Log.w(TAG, "execute: 功能未安装:环境设置-渲染器新选择" + e.getMessage()); } - //最后再添加动态库路径环境变量到ubt - ubtConfig.addEnvironmentVariable("LD_LIBRARY_PATH", LD_LIBRARY_PATH.toString()); + //最后再添加动态库路径环境变量到ubt(不对,如果这样会覆盖上一次的,要求没改LD的话就不添加。主要是适配多wine v1 且每添加新渲染设置的情况) + List envList = ubtConfig.getGuestEnvironmentVariables(); + for (String var : envList) { + if (var.startsWith("LD_LIBRARY_PATH=")) { + String oldLDPath = var.substring("LD_LIBRARY_PATH=".length()); + LD_LIBRARY_PATH.append(oldLDPath.startsWith(":") ? "" : ":").append(oldLDPath); //添加分隔符并将原变量添加到末尾 + envList.remove(var); + break; + } + } + ubtConfig.addEnvironmentVariable("LD_LIBRARY_PATH", LD_LIBRARY_PATH.deleteCharAt(0).toString()); //删除第一个冒号 sendDone(); } private void startPulseAudio(UBTLaunchConfiguration ubtConfig) { PulseAudio.installAndRun(); - ubtConfig.addEnvironmentVariable("PULSE_SERVER","tcp:127.0.0.1:4713"); + ubtConfig.addEnvironmentVariable("PULSE_SERVER", "tcp:127.0.0.1:4713"); } private void addRendererPath(SharedPreferences sp, UBTLaunchConfiguration ubtConfig) { - ContainerSettingsFragment.renderEntries[] entries = ContainerSettingsFragment.renderEntries.values(); + ConSetRenderer.readRendererTxt(); - String rendererName = sp.getString(ContainerSettingsFragment.KEY_RENDERER, entries[0].name); - String ldPath = ""; - for (ContainerSettingsFragment.renderEntries entry : entries) - if (entry.name.equals(rendererName)) - ldPath = entry.path; + if (renderersMap.size() == 0) { + Log.d(TAG, "addRendererPath: 渲染方式map为空,不设置ldPath"); + return; + } - //一些渲染的额外设置 + String altRendererName = renderersMap.keySet().iterator().next(); + String saveRendererName = sp.getString(ContainerSettingsFragment.KEY_RENDERER, "no_such_key"); + //如果存的值在map中搜不到,那就换成默认的(map中第一个)(在新建容器时,loadDefault()方法可能写入的值并非是map包含的值) + String rendererName = renderersMap.containsKey(saveRendererName) ? saveRendererName : altRendererName; - if (testRenderExist("VirGL_Overlay") && VirGL_Overlay.name.equals(rendererName)) { - //默认取消悬浮窗 - ubtConfig.addEnvironmentVariable("VTEST_WIN", "1"); + //先去除掉原先设置的GALLIUM_DRIVER吧 + for (int i = 0; i < ubtConfig.getGuestEnvironmentVariables().size(); i++) + if (ubtConfig.getGuestEnvironmentVariables().get(i).startsWith("GALLIUM_DRIVER=")) { + ubtConfig.getGuestEnvironmentVariables().remove(i); + break; + } + + //一些渲染的额外设置 + if (LLVMPipe.toString().equals(rendererName)) { +// ubtConfig.addEnvironmentVariable("GALLIUM_DRIVER", "llvmpipe"); //gallium其实不指定也行吧 + ubtConfig.addEnvironmentVariable("VK_ICD_FILENAMES", "/usr/share/vulkan/icd.d/lvp_icd.i686.json"); //除turnip外其余都不指定vulkan,不过llvm以后或许可以用lavapipe + } else if (VirGL_Overlay.toString().equals(rendererName)) { + ubtConfig.addEnvironmentVariable("VK_ICD_FILENAMES", "/usr/share/vulkan/icd.d/virtio_icd.i686.json"); + ubtConfig.addEnvironmentVariable("VTEST_WIN", "1"); //默认取消悬浮窗 ubtConfig.addEnvironmentVariable("VTEST_SOCK", ""); - } else if (testRenderExist("VirGL_built_in") && VirGL_built_in.name.equals(rendererName)) { - //调用so - File virglServerFile = new File(getAppContext().getApplicationInfo().nativeLibraryDir, "libvirgl_test_server.so"); + } else if (VirGL_built_in.toString().equals(rendererName)) { + ubtConfig.addEnvironmentVariable("VK_ICD_FILENAMES", "/usr/share/vulkan/icd.d/virtio_icd.i686.json"); + File logFile = new File(QH.Files.logsDir(), "virglLog.txt"); try { - ProcessBuilder builder = new ProcessBuilder(virglServerFile.getAbsolutePath()); + //调用so + ProcessBuilder builder = new ProcessBuilder(getAppContext().getApplicationInfo().nativeLibraryDir + "/libvirgl_test_server.so"); builder.environment().put("TMPDIR", getApplicationState().getExagearImage().getPath().getAbsolutePath() + "/tmp"); -// builder.environment().put("VTEST_SOCK", ""); -// builder.directory(paDir); - builder.redirectErrorStream(true); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - File logDir = new File(Globals.getAppContext().getExternalFilesDir(null),"logs"); - logDir.mkdirs(); - builder.redirectOutput(new File(logDir,"virglLog.txt")); + builder.redirectErrorStream(true); + builder.redirectOutput(logFile); } builder.start(); - -// Runtime.getRuntime().exec(virglServerFile.getAbsolutePath()); } catch (IOException e) { + try (PrintWriter printWriter = new PrintWriter(logFile);) { + e.printStackTrace(printWriter); + } catch (FileNotFoundException ignored) { + } e.printStackTrace(); } - } else if (testRenderExist("VirtIO_GPU") && VirtIO_GPU.name.equals(rendererName)) { - if (QH.classExist("com.eltechs.axs.MCat")) - new Mcat().start(); - } else if (testRenderExist("Turnip_DXVK") && Turnip_DXVK.name.equals(rendererName)) { + } else if (VirtIO_GPU.toString().equals(rendererName)) { + ubtConfig.addEnvironmentVariable("VK_ICD_FILENAMES", "/usr/share/vulkan/icd.d/virtio_icd.i686.json"); + if (QH.classExist("com.eltechs.axs.MCat")) { + if (mcat == null) + mcat = new Mcat(); + mcat.start(); + } + } else if (Turnip_Zink.toString().equals(rendererName)) { + + } else if (Turnip_DXVK.toString().equals(rendererName)) { ubtConfig.addEnvironmentVariable("GALLIUM_DRIVER", "zink"); ubtConfig.addEnvironmentVariable("MESA_VK_WSI_DEBUG", "sw"); } - if (!ldPath.equals("")) + //渲染对应的路径从map中获取。map通过刚才的readRendererTxt初始化,从本地txt中读取 + String ldPath = Objects.requireNonNull(renderersMap.get(rendererName)).getString("path"); + if (ldPath != null && !"".equals(ldPath)) LD_LIBRARY_PATH.insert(0, ldPath).insert(0, ":"); Log.d(TAG, "getEnvVarBin: 渲染路径为" + ldPath + ", 模式为" + rendererName); + // String[] rendererValues = {""}; // try { // rendererValues = getAppContext().getResources().getStringArray(QH.rslvID(R.array.cont_pref_renderer_values, 0x7f030009)); @@ -172,17 +216,6 @@ private void addRendererPath(SharedPreferences sp, UBTLaunchConfiguration ubtCon // Log.d(TAG, "getEnvVarBin: 渲染路径为" + ldPath + ", 模式为" + renderer); } - /** - * enum可能被修改,所以最好加个判断其是否存在 - */ - private boolean testRenderExist(String name) { - try { - ContainerSettingsFragment.renderEntries.valueOf(name); - return true; - } catch (IllegalArgumentException e) { - return false; - } - } private void addWinePath(SharedPreferences sp, UBTLaunchConfiguration ubtConfig, long contId, File xdroidFile) { //网上查,环境变量中路径即使带空格也无所谓。只通过冒号分割。 diff --git a/app/src/main/java/com/example/datainsert/exagear/containerSettings/ConSetRenderer.java b/app/src/main/java/com/example/datainsert/exagear/containerSettings/ConSetRenderer.java index 78af3b5d..99fdba38 100644 --- a/app/src/main/java/com/example/datainsert/exagear/containerSettings/ConSetRenderer.java +++ b/app/src/main/java/com/example/datainsert/exagear/containerSettings/ConSetRenderer.java @@ -1,5 +1,219 @@ package com.example.datainsert.exagear.containerSettings; +import android.app.AlertDialog; +import android.content.Context; +import android.os.Bundle; +import android.support.v7.preference.ListPreference; +import android.support.v7.preference.Preference; +import android.text.TextUtils; +import android.util.TypedValue; +import android.view.View; +import android.widget.LinearLayout; +import android.widget.RadioButton; +import android.widget.RadioGroup; +import android.widget.ScrollView; +import android.widget.TextView; + +import com.eltechs.axs.Globals; +import com.eltechs.axs.applicationState.ExagearImageAware; +import com.eltechs.axs.helpers.AndroidHelpers; +import com.example.datainsert.exagear.QH; +import com.example.datainsert.exagear.RR; + +import org.apache.commons.io.FileUtils; + +import java.io.File; +import java.io.IOException; +import java.util.Arrays; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + public class ConSetRenderer { - private static final int VERSION_FOR_EDPATCH = 1; + public static final String DEFAULT_RENDERER_TXT_CONTENT = "" + + "# 每个key代表一种渲染方式,该值不能随意修改。name代表环境设置中此选项显示的名称。path代表选择该选项后,启动容器时设置的LD_LIBRARY_PATH路径。若缺少name行或path行,该渲染方式不会显示在选项中。" + + "\n# Each 'key' represents a renderer, its value shouldn't be changed. 'name' represents the name of this renderer option in container settings. 'path' represents the path to LD_LIBRARY_PATH that is set when the container is started. 'key' with no 'name' or 'path' line will not be added to options in container settings." + + "\n\nkey:" + RenEnum.LLVMPipe + "\n name:LLVMPipe" + "\n path:/opt/lib/llvm" + + "\n\nkey:" + RenEnum.VirGL_Overlay + "\n name:VirGL Overlay" + "\n path:/opt/lib/vo" + + "\n\nkey:" + RenEnum.VirGL_built_in + "\n name:VirGL built-in" + "\n path:/opt/lib/vb" + + "\n\nkey:" + RenEnum.VirtIO_GPU + "\n name:VirtIO-GPU" + "\n path:/opt/lib/vg" + + "\n\nkey:" + RenEnum.Turnip_Zink + "\n name:Turnip Zink" + "\n path:/opt/lib/tz" + + "\n\nkey:" + RenEnum.Turnip_DXVK + "\n name:Turnip DXVK" + "\n path:/opt/lib/td"; + private static final String TAG = "ConSetRenderer"; + /** + * 1: 初次添加 + * 2: 路径和名称存在/opt/renderers.txt中。点击选项下方有文字提示修改了哪些内容 + */ + private static final int VERSION_FOR_EDPATCH = 2; + /** + * 从/opt/renderers.txt读取并存储到map. + *

    + * map的key为txt中的key,value为包含txt中name和path的bundle。 + * 通过getString("name") 或 "path" 获取 + *

    + * 存入pref中的数值为key,所以要求key不要随便修改 + */ + public static Map renderersMap = new LinkedHashMap<>();//要求有序,否则顺序会乱 + + static { + ConSetRenderer.readRendererTxt(); + } + + /** + * 从/opt/renderers.txt读取并存储到map + * `#` 为注释。 空行跳过 + * 每行:key:keyValue name:nameValue path:pathValue + * key的值不能修改,每个key对应一个渲染方式,name为显示在容器设置里的名称,path为该渲染方式对应读取的libGL.so.1等库的路径 + * 若一行没有name: 或 path: ,则该行无效,不会被添加到选项中 + * key是key,value是name和path的bundle + */ + public static void readRendererTxt() { + File configFile = new File(((ExagearImageAware) Globals.getApplicationState()).getExagearImage().getPath(), "opt/renderers.txt"); + + + renderersMap.clear(); + try { + //若没有该文件,自己创建一个并写入默认内容 + if (!configFile.exists()) { + FileUtils.writeLines(configFile, "UTF-8", Arrays.asList(DEFAULT_RENDERER_TXT_CONTENT.split("\n"))); + } + + List lines = FileUtils.readLines(configFile); + + for (int i = 0; i < lines.size(); i++) { + String trimLine = lines.get(i).trim(); + if (trimLine.startsWith("#") || trimLine.equals("") || i + 3 > lines.size()) + continue; + + if (!(trimLine.startsWith("key:") || i + 3 <= lines.size())) + continue; + + //读取三个属性 + String localLine; + localLine = lines.get(i).trim(); + String key = localLine.startsWith("key:") ? localLine.substring("key:".length()).trim() : null; + localLine = lines.get(i + 1).trim(); + String name = localLine.startsWith("name:") ? localLine.substring("name:".length()).trim() : null; + localLine = lines.get(i + 2).trim(); + String path = localLine.startsWith("path:") ? localLine.substring("path:".length()).trim() : null; + + if (key != null && name != null && path != null) { + Bundle bundle = new Bundle(); + bundle.putString("name", name); + bundle.putString("path", path); + renderersMap.put(key, bundle); + + i += 2;//只有在完整读取三行之后才跳到第三行,否则不管,让for自动一行一行过渡 + } + } + } catch (IOException e) { + e.printStackTrace(); + } + } + + public static void buildRendererDialog(ListPreference preference) { + ConSetRenderer.readRendererTxt(); + + CharSequence[] names = preference.getEntries(); + CharSequence[] keys = preference.getEntryValues(); + Context c = preference.getContext(); + + ScrollView dialogView = new ScrollView(c); + LinearLayout linearLayout = new LinearLayout(c); + dialogView.addView(linearLayout); + linearLayout.setOrientation(LinearLayout.VERTICAL); + int px = QH.px(c, RR.attr.dialogPaddingDp); + linearLayout.setPadding(px, px, px, px); + + RadioGroup radioGroup = new RadioGroup(c); + for (int i = 0; i < names.length; i++) { + RadioButton radioButton = new RadioButton(c); + radioButton.setTag(i); + radioButton.setText(names[i]); + radioButton.setTextSize(TypedValue.COMPLEX_UNIT_SP, 18); + RadioGroup.LayoutParams radioBtnParams = new RadioGroup.LayoutParams(-1, -2); + radioBtnParams.topMargin = AndroidHelpers.dpToPx(16); + radioGroup.addView(radioButton, radioBtnParams); + + //每个渲染的简介 + ConSetRenderer.RenEnum renEnum; + try { + renEnum = ConSetRenderer.RenEnum.valueOf(keys[i].toString()); + } catch (Exception e) { + continue; //找不到对应的enum就直接跳过了,下面应该没啥其他要做的了吧? + } + + LinearLayout linearInfo = new LinearLayout(c); + linearInfo.setOrientation(LinearLayout.VERTICAL); + LinearLayout.LayoutParams tvInfoParams = new LinearLayout.LayoutParams(-1, -2); + tvInfoParams.topMargin = AndroidHelpers.dpToPx(4); + tvInfoParams.setMarginStart(AndroidHelpers.dpToPx(16)); + + //每一项太长了,先默认缩成一行,点击展开 + Bundle rendBundle = renderersMap.get(keys[i].toString()); //一定能获取到,因为上面获取renEnum如果报错就不会走到这 + assert rendBundle != null; + String infoTotal = String.format("LD_LIBRARY_PATH=%s", rendBundle.getString("path")) + + ((renEnum.info.length() > 0) ? "\n" + renEnum.info : ""); + for (String oneStr : infoTotal.split("\n")) { + TextView tvInfo = new TextView(c); + tvInfo.setTextIsSelectable(true); +// tvInfo.setLineSpacing(0, 1.2f); + tvInfo.setSingleLine(true); + tvInfo.setEllipsize(TextUtils.TruncateAt.END); + tvInfo.setText(oneStr); + tvInfo.setOnClickListener(v -> tvInfo.setSingleLine(tvInfo.getMaxLines() != 1)); + linearInfo.addView(tvInfo, tvInfoParams); + } + + radioGroup.addView(linearInfo); + linearInfo.setVisibility(View.GONE); + + //选择变化时,隐藏上一个渲染的简介,先显示勾选渲染的简介。将勾选的渲染的key存入pref中。 + String value = keys[i].toString(); + radioButton.setOnCheckedChangeListener((buttonView, isChecked) -> { + linearInfo.setVisibility(isChecked ? View.VISIBLE : View.GONE); + if (isChecked) + preference.setValue(value); + }); + + //勾选当前设置项 + if (keys[i].equals(preference.getValue())) + radioButton.setChecked(true); + } + + linearLayout.addView(radioGroup); + + new AlertDialog.Builder(c) + .setView(dialogView) + .setTitle(preference.getDialogTitle()) + .setPositiveButton(android.R.string.yes, (dialog, which) -> { + + }) + .show(); + + + } + + /** + * 渲染器选项。entry和entryvalue都用这一个了 + * 需要在添加环境变量的那个action(即外部)获取对应的字符串,如果硬编码,modder需要修改两处,可能会忽略。用enum只需要改一处即可。 + */ + public enum RenEnum { + LLVMPipe("VK_ICD_FILENAMES=/usr/share/vulkan/icd.d/lvp_icd.i686.json"), + VirGL_Overlay("VK_ICD_FILENAMES=/usr/share/vulkan/icd.d/lvp_icd.i686.json" + + "\nVTEST_WIN=1" + + "\nVTEST_SOCK="), + VirGL_built_in("VK_ICD_FILENAMES=/usr/share/vulkan/icd.d/lvp_icd.i686.json" + + "\nTMPDIR=z:/tmp libvirgl_test_server.so"), + VirtIO_GPU("new Mcat().start()"), + Turnip_Zink(""), + Turnip_DXVK("GALLIUM_DRIVER=zink" + + "\nMESA_VK_WSI_DEBUG=sw"), + ; + public final String info ; + + RenEnum(String s) { + info = s; + } + } } diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml index 8fc6ef8c..c8d990f0 100644 --- a/app/src/main/res/values/styles.xml +++ b/app/src/main/res/values/styles.xml @@ -2,6 +2,8 @@ - \ No newline at end of file diff --git a/patchapp/build.gradle b/patchapp/build.gradle index a329164e..5d20def9 100644 --- a/patchapp/build.gradle +++ b/patchapp/build.gradle @@ -10,8 +10,8 @@ android { applicationId "com.ewt45.patchapp" minSdk 21 targetSdk 27 - versionCode 5 - versionName '0.0.4' + versionCode 6 + versionName '0.0.5' resourceConfigurations += ['en', 'zh', 'ru'] testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" } diff --git a/patchapp/release/output-metadata.json b/patchapp/release/output-metadata.json index 002eb1fe..216ca4aa 100644 --- a/patchapp/release/output-metadata.json +++ b/patchapp/release/output-metadata.json @@ -11,8 +11,8 @@ "type": "SINGLE", "filters": [], "attributes": [], - "versionCode": 5, - "versionName": "0.0.4", + "versionCode": 6, + "versionName": "0.0.5", "outputFile": "patchapp-release.apk" } ], diff --git a/patchapp/src/main/java/com/ewt45/patchapp/fragment/FragmentChoosePatch.java b/patchapp/src/main/java/com/ewt45/patchapp/fragment/FragmentChoosePatch.java index dee74055..51fb38ac 100644 --- a/patchapp/src/main/java/com/ewt45/patchapp/fragment/FragmentChoosePatch.java +++ b/patchapp/src/main/java/com/ewt45/patchapp/fragment/FragmentChoosePatch.java @@ -2,6 +2,7 @@ import android.content.Context; import android.content.Intent; +import android.graphics.Typeface; import android.net.Uri; import android.os.Bundle; import android.support.annotation.NonNull; @@ -11,10 +12,18 @@ import android.support.v4.content.FileProvider; import android.text.SpannableStringBuilder; import android.util.Log; +import android.util.TypedValue; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; +import android.view.ViewStub; +import android.view.animation.Animation; +import android.view.animation.AnimationSet; +import android.view.animation.AnimationUtils; +import android.view.animation.ScaleAnimation; import android.widget.CheckBox; +import android.widget.LinearLayout; +import android.widget.TextView; import com.ewt45.patchapp.ActionPool; import com.ewt45.patchapp.MyApplication; @@ -34,7 +43,6 @@ import com.ewt45.patchapp.thread.FuncShortcut; import com.ewt45.patchapp.thread.SignApk; import com.ewt45.patchapp.thread.SignalDone; -import com.ewt45.patchapp.thread.WriteFuncVer; import com.ewt45.patchapp.widget.SelectApkDialog; import org.apache.commons.io.FileUtils; @@ -70,7 +78,6 @@ public class FragmentChoosePatch extends Fragment { @Override public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - // Inflate the layout for this fragment binding = FragmentChoosePatchBinding.inflate(inflater, container, false); @@ -84,6 +91,7 @@ public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, funcList.add(new FuncWithCheckBox(binding.checkShortcut,new FuncShortcut())); funcList.add(new FuncWithCheckBox(binding.checkMw,new FuncMultiWine())); funcList.add(new FuncWithCheckBox(binding.checkRenderer,new FuncRenderer())); + return binding.getRoot(); } diff --git a/patchapp/src/main/java/com/ewt45/patchapp/fragment/FragmentHelp.java b/patchapp/src/main/java/com/ewt45/patchapp/fragment/FragmentHelp.java index e03d61cb..a5dce6a4 100644 --- a/patchapp/src/main/java/com/ewt45/patchapp/fragment/FragmentHelp.java +++ b/patchapp/src/main/java/com/ewt45/patchapp/fragment/FragmentHelp.java @@ -6,6 +6,8 @@ import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.support.v4.app.Fragment; +import android.support.v7.widget.CardView; +import android.support.v7.widget.GridLayoutManager; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; import android.view.LayoutInflater; @@ -13,6 +15,7 @@ import android.view.ViewGroup; import android.widget.TextView; +import com.ewt45.patchapp.AndroidUtils; import com.ewt45.patchapp.R; import java.io.IOException; @@ -31,6 +34,11 @@ public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup c // LinearLayoutManager layoutManager = new LinearLayoutManager(context); // layoutManager.setOrientation(HORIZONTAL); // recyclerView.setLayoutManager(new LinearLayoutManager(requireContext())); + if (requireActivity().getWindowManager().getDefaultDisplay().getWidth() > AndroidUtils.toPx(requireContext(), 800)) { + GridLayoutManager layoutManager = new GridLayoutManager(requireContext(), 2, LinearLayoutManager.VERTICAL, false); + recyclerView.setLayoutManager(layoutManager); + } + recyclerView.setAdapter(new FuncdescpAdapter( getResources().getStringArray(R.array.func_name), getResources().getStringArray(R.array.func_description), @@ -43,7 +51,8 @@ public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup c R.drawable.softinput, R.drawable.selectobb, R.drawable.shortcut, - R.drawable.multiwine + R.drawable.multiwine, + R.drawable.renderer } )); return rootView; @@ -75,6 +84,7 @@ public void onBindViewHolder(@NonNull ViewHolder viewHolder, int i) { if (images[i] != 0) viewHolder.image.setImageResource(images[i]); + } @Override @@ -83,12 +93,14 @@ public int getItemCount() { } public static class ViewHolder extends RecyclerView.ViewHolder { + private final CardView root; private final TextView title; private final TextView description; private final GifImageView image; public ViewHolder(@NonNull View itemView) { super(itemView); + root = (CardView) itemView; title = itemView.findViewById(R.id.title); description = itemView.findViewById(R.id.description); image = itemView.findViewById(R.id.gif_image); diff --git a/patchapp/src/main/java/com/ewt45/patchapp/patching/SmaliFile.java b/patchapp/src/main/java/com/ewt45/patchapp/patching/SmaliFile.java index 9562aaa5..7f2d91d4 100644 --- a/patchapp/src/main/java/com/ewt45/patchapp/patching/SmaliFile.java +++ b/patchapp/src/main/java/com/ewt45/patchapp/patching/SmaliFile.java @@ -389,7 +389,13 @@ public boolean containsLine(String s) { return false; } - + /** + * 获取该文件内全部行。请谨慎修改 + * @return 列表 + */ + public List getAllLines() { + return mAllLines; + } /** * 从某个smali文件中,尝试寻找成员变量,以获取当前功能的安装版本号 diff --git a/patchapp/src/main/java/com/ewt45/patchapp/thread/FuncRenderer.java b/patchapp/src/main/java/com/ewt45/patchapp/thread/FuncRenderer.java index 23477937..bdf7702e 100644 --- a/patchapp/src/main/java/com/ewt45/patchapp/thread/FuncRenderer.java +++ b/patchapp/src/main/java/com/ewt45/patchapp/thread/FuncRenderer.java @@ -19,12 +19,23 @@ public int getInstalledVersion() { @Override public int getLatestVersion() { - return 1; + return 2; } @Override public Integer call() throws Exception { + //Mcat可能在别的类(UBTLaunchConfiguration)有调用,尝试寻找并删除 + SmaliFile ubtLanConfig = new SmaliFile().findSmali("com.eltechs.axs.configuration.UBTLaunchConfiguration"); + for(int i=0; istart()V")){ + ubtLanConfig.getAllLines().remove(i); + i--; + } + } + ubtLanConfig.close(); + + PatcherFile.copy(TYPE_SMALI, new String[]{ "/com/eltechs/ed/fragments/ContainerSettingsFragment.smali", "/com/example/datainsert/exagear/containerSettings/ConSetRenderer.smali", //复制标识类 diff --git a/patchapp/src/main/res/anim/text_scale.xml b/patchapp/src/main/res/anim/text_scale.xml new file mode 100644 index 00000000..d8d64ae7 --- /dev/null +++ b/patchapp/src/main/res/anim/text_scale.xml @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + diff --git a/patchapp/src/main/res/drawable/renderer.jpg b/patchapp/src/main/res/drawable/renderer.jpg new file mode 100644 index 00000000..3f1a8818 Binary files /dev/null and b/patchapp/src/main/res/drawable/renderer.jpg differ diff --git a/patchapp/src/main/res/layout/fragment_choose_patch.xml b/patchapp/src/main/res/layout/fragment_choose_patch.xml index 3eb7de17..03280b11 100644 --- a/patchapp/src/main/res/layout/fragment_choose_patch.xml +++ b/patchapp/src/main/res/layout/fragment_choose_patch.xml @@ -13,6 +13,7 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical"> + @@ -112,6 +113,15 @@ android:layout_width="wrap_content" android:layout_height="wrap_content"/> + + + android:layout_height="match_parent"/> diff --git a/patchapp/src/main/res/values-ru/strings.xml b/patchapp/src/main/res/values-ru/strings.xml index 3de725cb..38bae255 100644 --- a/patchapp/src/main/res/values-ru/strings.xml +++ b/patchapp/src/main/res/values-ru/strings.xml @@ -10,7 +10,7 @@ Отображать курсор добавление функции: кнопки настроек добавление функции: выбора разрешений экрана - Кнопка настроек\n- Выбор локации диска D\n- Кастомное управление\n- PulseAudio (XSDL) + Кнопка настроек - Выбор локации диска D - Кастомное управление - PulseAudio (XSDL) Настройки контейнера - Кастомное разрешение Исправление работы клавиатуры на android 11+ запуск исправлений @@ -73,5 +73,6 @@ MultiWine v2 Настройки контейнера - Выбор рендера PulseAudio (XSDL) - PulseAudio is used to play audio, reducing sound problems. This function uses PulseAudio server extracted from Xserver XSDL. It requires 64-bit support on your device. + - Раньше Multiwine v1 поддерживал по умолчанию использование разных рендеров для каждого контейнера в v2 этого больше нет. Если вы хотите продолжать использовать разные рендеры для каждого контейнера в MultiWine v2 вам нужно исправить ее вручную.\n - Установите разные LD_LIBRARY_PATH для разных рендеров. libGL.so.1 будет загружаться с выбранного пути с более высоким приоритетом. Вы можете редактировать пути самостоятельно в файле Z:/opt/renderers.txt.\n - Некоторые параметры рендера имеют дополнительные опции при загрузке:\n - Не работает рендер Turnip: Перенаправьте VK_ICD_FILENAMES в несуществующий файл, этот параметр на случай, если Turnip рендеры не будут работать.\n - Встроенный VirGl: запуск libvirgl_test_server.so в новом java-процессе. Работает только в apk с поддержкой xegw. Больше нет необходимости в Mcat и /opt/start.sh.\nЛог при этом выводится в Android/data/packageName/logs/virglLog.txt.\n - VirtIO-GPU: запускается через Mcat. До Xegw Mcat использовался для запуска среды proot для этого рендера, но в xegw он переписан для запуска /opt/start.sh и не запускает proot автоматически. + PulseAudio используется для воспроизведения звука, уменьшения проблемы связанных со звуком. Эта функция использует сервер PulseAudio, извлеченный из apk Xserver XSDL. Что требуется поддержка 64-битной версии Андроид на вашем девайсе. \ No newline at end of file diff --git a/patchapp/src/main/res/values-zh/strings.xml b/patchapp/src/main/res/values-zh/strings.xml index a42ed1ee..48f694d5 100644 --- a/patchapp/src/main/res/values-zh/strings.xml +++ b/patchapp/src/main/res/values-zh/strings.xml @@ -75,5 +75,6 @@ 环境设置 - 图形渲染设置 PulseAudio (XSDL) (悬浮操作按钮) PulseAudio用于播放音频,可以缓解一部分声音问题。本功能用到的PulseAudio服务端提取自Xserver XSDL,需要手机支持64位。 + - 多wine共存v2 不包含渲染路径分离功能(v1时这两功能混杂在一起),升为v2后,想继续使用渲染路径分离请单独添加此功能。\n- 在容器设置中添加一个渲染设置,为不同的渲染指定不同的动态链接库路径LD_LIBRARY_PATH。系统会优先从从该路径寻找libGL.so.1等文件。路径可以在/opt/renderers.txt中配置。\n- 添加该功能后,需要多做一步准备工作,将每个渲染对应的libGL.so.1复制到自己定义的路径。之后每次切换渲染在容器设置中选择即可。\n- 此外,某些渲染方式还会有额外的操作(容器设置中选中对应项也会有文字提示):\n\t- 非turnip渲染:指定一个不存在的VK_ICD_FILENAMES路径,以防与turnip冲突而无法使用。\n\t- VirGL_built_in: 新建java进程运行 libvirgl_test_server.so(仅xegw的apk支持)。不需要Mcat和/opt/start.sh。日志输出到Android/data/包名/logs/virglLog.txt。\n\t- virtio-gpu: 尝试启动Mcat。在xegw之前,Mcat用于启动proot环境,即免termux使用该渲染。在第一版的xegw apk中,mcat被重写 设定为运行/opt/start.sh,用于启动virgl built-in,不会自动启动proot。 \ No newline at end of file diff --git a/patchapp/src/main/res/values/arrays.xml b/patchapp/src/main/res/values/arrays.xml index 829e7750..fe52cb71 100644 --- a/patchapp/src/main/res/values/arrays.xml +++ b/patchapp/src/main/res/values/arrays.xml @@ -26,6 +26,7 @@ @string/funcname_selobb @string/funcname_shortcut @string/funcname_mw + @string/funcname_renderer @@ -38,6 +39,7 @@ @string/fraghelp_body_selobb @string/fraghelp_body_shortcut @string/fraghelp_body_mw + @string/fraghelp_body_renderer \ No newline at end of file diff --git a/patchapp/src/main/res/values/strings.xml b/patchapp/src/main/res/values/strings.xml index 70b2a13b..78dc2095 100644 --- a/patchapp/src/main/res/values/strings.xml +++ b/patchapp/src/main/res/values/strings.xml @@ -11,7 +11,7 @@ add function: float action button add function: custom resolution show cursor - float action button\n- Custom location of drive D\n- Custom Control\n- PulseAudio (XSDL) + float action button - Custom location of drive D - Custom Control - PulseAudio (XSDL) container settings - custom resolution android 11+ soft-input no-crashing start patch @@ -78,4 +78,5 @@ container settings - renderer options PulseAudio (XSDL) PulseAudio is used to play audio, reducing sound problems. This function uses PulseAudio server extracted from Xserver XSDL. It requires 64-bit support on your device. + - Multiwine v1 used to be combined with this function internally, but v2 no more. If v2 is patched, you have to patch this manually if you want to continue to use it.\n- Set diffrent LD_LIBRARY_PATH for different renderers. libGL.so.1 will be loaded from this path with higher priority. You can edit paths in z:/opt/renderers.txt.\n- Some renderer options have additional operations:\n - None turnip renderers: Redirect VK_ICD_FILENAMES to a non-exist file, in case these renderers won\'t work.\n - virgl built-in: Launch libvirgl_test_server.so in a new java process. Only works in xegw apk. No need of Mcat or /opt/start.sh. Log is outputed at Android/data/packageName/logs/virglLog.txt.\n - virtio-gpu: start Mcat. Before Xegw, mcat is used to start proot environment for this renderer, but in xegw it is rewriten to launch /opt/start.sh and won\'t launch proot automatically. \ No newline at end of file diff --git a/readme.md b/readme.md index e30e3b15..42eda09c 100644 --- a/readme.md +++ b/readme.md @@ -33,12 +33,13 @@ - [悬浮操作按钮](https://ewt45.github.io/blogs/2022/winter/exagearFab/) - [自定义d盘路径](https://ewt45.github.io/blogs/2022/winter/exagearFab/driveD.html) - [自定义操作模式](https://www.bilibili.com/video/BV1fL41167Ji/) + - PulseAudio (XSDL) - [强制显示鼠标光标](https://ewt45.github.io/blogs/2022/winter/exagearDefaultCursor/) - [环境设置- 自定义分辨率](https://ewt45.github.io/blogs/2022/autumn/exagearCustomResl/) - [安卓11+调起输入法](https://ewt45.github.io/blogs/2022/autumn/exagearKeyboard/) - [手动选择obb](https://ewt45.github.io/blogs/2022/winter/exagearFindObb/) -- exe快捷方式直接启动 -- 多版本wine共存 v2 +- [exe快捷方式直接启动](https://www.bilibili.com/video/BV1QM4y1v7RG/) +- [多版本wine共存 v2](https://www.bilibili.com/video/BV1bk4y1K7jR/) - 环境设置 - 渲染方式 ## 第三方依赖 @@ -49,10 +50,32 @@ - [android-gif-drawable](https://github.com/koral--/android-gif-drawable) - [apksig](https://android.googlesource.com/platform/tools/apksig) - [AndroidBinaryXml](https://github.com/senswrong/AndroidBinaryXml) - +- [Gson](https://github.com/google/gson) +- [org.tukaani.xz](https://tukaani.org/xz/) ## 更新历史 +### v0.0.5 +- 添加新功能: +1. PulseAudio (XSDL): PulseAudio用于播放音频,可以缓解一部分声音问题。本功能用到的PulseAudio服务端提取自Xserver XSDL,需要手机支持64位。 + +- 更新旧功能: +1. 悬浮操作按钮(齿轮): + - 长按可隐藏。 + - 导出logcat日志:若d盘位置存在名为logcat的文件夹,则将logcat日志保存到这个文件夹中。便于检查输出和调试。 +2. 环境设置 - 渲染方式: + - 多wine共存v2 不包含渲染路径分离功能(v1时这两功能混杂在一起),升为v2后,想继续使用渲染路径分离请单独添加此功能。 + - 在容器设置中添加一个渲染设置,为不同的渲染指定不同的动态链接库路径LD_LIBRARY_PATH。系统会优先从从该路径寻找libGL.so.1等文件。路径可以在/opt/renderers.txt中配置。 + - 添加该功能后,需要多做一步准备工作,将每个渲染对应的libGL.so.1复制到自己定义的路径。之后每次切换渲染在容器设置中选择即可。 + - 此外,某些渲染方式还会有额外的操作(容器设置中选中对应项也会有文字提示): + - 非turnip渲染:指定一个不存在的VK_ICD_FILENAMES路径,以防与turnip冲突而无法使用。 + - VirGL_built_in: 新建java进程运行 libvirgl_test_server.so(仅xegw的apk支持)。不需要Mcat和/opt/start.sh。日志输出到Android/data/包名/logs/virglLog.txt。 + - virtio-gpu: 尝试启动Mcat。在xegw之前,Mcat用于启动proot环境,即免termux使用该渲染。在第一版的xegw apk中,mcat被重写 设定为运行/opt/start.sh,用于启动virgl built-in,不会自动启动proot。 + +- 其他 + - pulseaudio启动方法, icd路径 均由在那老虎山上大佬提供 + - 缩减apk体积 + ### v0.0.4 - 新功能: 1. multiwine v2 可在线下载wine。在容器管理界面右上角新增下载按钮,点击进入wine版本管理界面。 diff --git a/readme/readme_en.md b/readme/readme_en.md index 7141ed36..7995ef5e 100644 --- a/readme/readme_en.md +++ b/readme/readme_en.md @@ -29,6 +29,7 @@ So I'm wondering if I can make an application. The user only needs to click a bu - [float action button](https://ewt45.github.io/blogs/2022/winter/exagearFab/) - Custom location of drive D - Custom Control + - PulseAudio (XSDL) - [show cursor](https://ewt45.github.io/blogs/2022/winter/exagearDefaultCursor/) - [container settings - custom resolution](https://ewt45.github.io/blogs/2022/autumn/exagearCustomResl/) - [android 11+ soft-input no-crashing](https://ewt45.github.io/blogs/2022/autumn/exagearKeyboard/) @@ -45,10 +46,33 @@ So I'm wondering if I can make an application. The user only needs to click a bu - [android-gif-drawable](https://github.com/koral--/android-gif-drawable) - [apksig](https://android.googlesource.com/platform/tools/apksig) - [AndroidBinaryXml](https://github.com/senswrong/AndroidBinaryXml) +- [Gson](https://github.com/google/gson) +- [org.tukaani.xz](https://tukaani.org/xz/) ## Change Log + +### v0.0.5 +- New functions available: +1. PulseAudio (XSDL): PulseAudio is used to play audio, reducing sound problems. This function uses PulseAudio server extracted from Xserver XSDL. It requires 64-bit support on your device. + +- Old functions updated: +1. float action button: + - Long press to hide. + - export logcat logs: If there's a folder named logcat in drive d, the logs will be saved inside this folder. Helpful when debugging. +2. container settings - renderer: + - Multiwine v1 used to be combined with this function internally, but v2 no more. If v2 is patched, you have to patch this manually if you want to continue to use it. + - Set diffrent LD_LIBRARY_PATH for different renderers. libGL.so.1 will be loaded from this path with higher priority. You can edit paths in z:/opt/renderers.txt. + - Some renderer options have additional operations: + - None turnip renderers: Redirect VK_ICD_FILENAMES to a non-exist file, in case these renderers won't work. + - virgl built-in: Launch libvirgl_test_server.so in a new java process. Only works in xegw apk. No need of Mcat or /opt/start.sh. Log is outputed at Android/data/packageName/logs/virglLog.txt. + - virtio-gpu: start Mcat. Before Xegw, mcat is used to start proot environment for this renderer, but in xegw it is rewriten to launch /opt/start.sh and won't launch proot automatically. + +- Others: + - apk size shrunk. + + ### v0.0.4 - MultiWIne v2 :Added a download icon on the left-top of "containers manager" page, click to enter the "add/remove wines" page. diff --git a/readme/readme_ru.md b/readme/readme_ru.md index 1a910788..7b2e55fe 100644 --- a/readme/readme_ru.md +++ b/readme/readme_ru.md @@ -28,6 +28,7 @@ - [Кнопка настроек](https://ewt45.github.io/blogs/2022/winter/exagearFab/) - Выбор локации диска D - Кастомное управление + - PulseAudio (XSDL) - [Отображать курсор](https://ewt45.github.io/blogs/2022/winter/exagearDefaultCursor/) - [Настройки контейнера - Кастомное разрешение](https://ewt45.github.io/blogs/2022/autumn/exagearCustomResl/) - [Исправление работы клавиатуры на android 11+](https://ewt45.github.io/blogs/2022/autumn/exagearKeyboard/) @@ -42,13 +43,35 @@ - [android-gif-drawable](https://github.com/koral--/android-gif-drawable) - [apksig](https://android.googlesource.com/platform/tools/apksig) - [AndroidBinaryXml](https://github.com/senswrong/AndroidBinaryXml) - +- [Gson](https://github.com/google/gson) +- [org.tukaani.xz](https://tukaani.org/xz/) ## Change Log + +### v0.0.5 +- New functions available: +1. PulseAudio (XSDL): PulseAudio используется для воспроизведения звука, уменьшения проблемы связанных со звуком. Эта функция использует сервер PulseAudio, извлеченный из apk Xserver XSDL. Что требуется поддержка 64-битной версии Андроид на вашем девайсе. + +- Old functions updated: +1. Кнопка настроек: + - Long press to hide. + - export logcat logs: If there's a folder named logcat in drive d, the logs will be saved inside this folder. Helpful when debugging. +2. Настройки контейнера - Выбор рендера: + - Раньше Multiwine v1 поддерживал по умолчанию использование разных рендеров для каждого контейнера в v2 этого больше нет. Если вы хотите продолжать использовать разные рендеры для каждого контейнера в MultiWine v2 вам нужно исправить ее вручную. + - Установите разные LD_LIBRARY_PATH для разных рендеров. libGL.so.1 будет загружаться с выбранного пути с более высоким приоритетом. Вы можете редактировать пути самостоятельно в файле Z:/opt/renderers.txt. + - Некоторые параметры рендера имеют дополнительные опции при загрузке: + - Не работает рендер Turnip: Перенаправьте VK_ICD_FILENAMES в несуществующий файл, этот параметр на случай, если Turnip рендеры не будут работать. + - Встроенный VirGl: запуск libvirgl_test_server.so в новом java-процессе. Работает только в apk с поддержкой xegw. Больше нет необходимости в Mcat и /opt/start.sh. + Лог при этом выводится в Android/data/packageName/logs/virglLog.txt. + - VirtIO-GPU: запускается через Mcat. До Xegw Mcat использовался для запуска среды proot для этого рендера, но в xegw он переписан для запуска /opt/start.sh и не запускает proot автоматически. + +- Others: + - apk size shrunk. + ### v0.0.4 - MultiWine v2: Добавляется значок загрузки необходимой версии wine в левом верхнем углу страницы "создание контейнеров", тапните на вкладку "добавление/удаление wine".