Android: Expose custom rtc options
This commit is contained in:
parent
d63462a14e
commit
bbb83054af
|
@ -44,6 +44,8 @@ public enum BooleanSetting implements AbstractBooleanSetting
|
||||||
MAIN_OVERCLOCK_ENABLE(Settings.FILE_DOLPHIN, Settings.SECTION_INI_CORE, "OverclockEnable", false),
|
MAIN_OVERCLOCK_ENABLE(Settings.FILE_DOLPHIN, Settings.SECTION_INI_CORE, "OverclockEnable", false),
|
||||||
MAIN_RAM_OVERRIDE_ENABLE(Settings.FILE_DOLPHIN, Settings.SECTION_INI_CORE, "RAMOverrideEnable",
|
MAIN_RAM_OVERRIDE_ENABLE(Settings.FILE_DOLPHIN, Settings.SECTION_INI_CORE, "RAMOverrideEnable",
|
||||||
false),
|
false),
|
||||||
|
MAIN_CUSTOM_RTC_ENABLE(Settings.FILE_DOLPHIN, Settings.SECTION_INI_CORE, "EnableCustomRTC",
|
||||||
|
false),
|
||||||
MAIN_AUTO_DISC_CHANGE(Settings.FILE_DOLPHIN, Settings.SECTION_INI_CORE, "AutoDiscChange", false),
|
MAIN_AUTO_DISC_CHANGE(Settings.FILE_DOLPHIN, Settings.SECTION_INI_CORE, "AutoDiscChange", false),
|
||||||
MAIN_ALLOW_SD_WRITES(Settings.FILE_DOLPHIN, Settings.SECTION_INI_CORE, "WiiSDCardAllowWrites",
|
MAIN_ALLOW_SD_WRITES(Settings.FILE_DOLPHIN, Settings.SECTION_INI_CORE, "WiiSDCardAllowWrites",
|
||||||
true),
|
true),
|
||||||
|
@ -267,6 +269,7 @@ public enum BooleanSetting implements AbstractBooleanSetting
|
||||||
MAIN_PAUSE_ON_PANIC,
|
MAIN_PAUSE_ON_PANIC,
|
||||||
MAIN_ACCURATE_CPU_CACHE,
|
MAIN_ACCURATE_CPU_CACHE,
|
||||||
MAIN_RAM_OVERRIDE_ENABLE,
|
MAIN_RAM_OVERRIDE_ENABLE,
|
||||||
|
MAIN_CUSTOM_RTC_ENABLE,
|
||||||
MAIN_DSP_JIT,
|
MAIN_DSP_JIT,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -21,6 +21,9 @@ public enum StringSetting implements AbstractStringSetting
|
||||||
MAIN_BBA_BUILTIN_DNS(Settings.FILE_DOLPHIN, Settings.SECTION_INI_CORE, "BBA_BUILTIN_DNS",
|
MAIN_BBA_BUILTIN_DNS(Settings.FILE_DOLPHIN, Settings.SECTION_INI_CORE, "BBA_BUILTIN_DNS",
|
||||||
"149.56.167.128"),
|
"149.56.167.128"),
|
||||||
|
|
||||||
|
MAIN_CUSTOM_RTC_VALUE(Settings.FILE_DOLPHIN, Settings.SECTION_INI_CORE, "CustomRTCValue",
|
||||||
|
"0x386d4380"),
|
||||||
|
|
||||||
MAIN_GFX_BACKEND(Settings.FILE_DOLPHIN, Settings.SECTION_INI_CORE, "GFXBackend",
|
MAIN_GFX_BACKEND(Settings.FILE_DOLPHIN, Settings.SECTION_INI_CORE, "GFXBackend",
|
||||||
NativeLibrary.GetDefaultGraphicsBackendName()),
|
NativeLibrary.GetDefaultGraphicsBackendName()),
|
||||||
|
|
||||||
|
@ -39,6 +42,7 @@ public enum StringSetting implements AbstractStringSetting
|
||||||
"PostProcessingShader", "");
|
"PostProcessingShader", "");
|
||||||
|
|
||||||
private static final StringSetting[] NOT_RUNTIME_EDITABLE_ARRAY = new StringSetting[]{
|
private static final StringSetting[] NOT_RUNTIME_EDITABLE_ARRAY = new StringSetting[]{
|
||||||
|
MAIN_CUSTOM_RTC_VALUE,
|
||||||
MAIN_GFX_BACKEND,
|
MAIN_GFX_BACKEND,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,31 @@
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
package org.dolphinemu.dolphinemu.features.settings.model.view
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import org.dolphinemu.dolphinemu.features.settings.model.AbstractSetting
|
||||||
|
import org.dolphinemu.dolphinemu.features.settings.model.AbstractStringSetting
|
||||||
|
import org.dolphinemu.dolphinemu.features.settings.model.Settings
|
||||||
|
|
||||||
|
class DateTimeChoiceSetting(
|
||||||
|
context: Context,
|
||||||
|
private val setting: AbstractStringSetting,
|
||||||
|
nameId: Int,
|
||||||
|
descriptionId: Int
|
||||||
|
) : SettingsItem(context, nameId, descriptionId) {
|
||||||
|
override fun getType(): Int {
|
||||||
|
return TYPE_DATETIME_CHOICE
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getSetting(): AbstractSetting {
|
||||||
|
return setting
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setSelectedValue(settings: Settings?, selection: String) {
|
||||||
|
setting.setString(settings, selection)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getSelectedValue(settings: Settings): String {
|
||||||
|
return setting.getString(settings)
|
||||||
|
}
|
||||||
|
}
|
|
@ -29,6 +29,7 @@ public abstract class SettingsItem
|
||||||
public static final int TYPE_RUN_RUNNABLE = 10;
|
public static final int TYPE_RUN_RUNNABLE = 10;
|
||||||
public static final int TYPE_STRING = 11;
|
public static final int TYPE_STRING = 11;
|
||||||
public static final int TYPE_HYPERLINK_HEADER = 12;
|
public static final int TYPE_HYPERLINK_HEADER = 12;
|
||||||
|
public static final int TYPE_DATETIME_CHOICE = 13;
|
||||||
|
|
||||||
private final CharSequence mName;
|
private final CharSequence mName;
|
||||||
private final CharSequence mDescription;
|
private final CharSequence mDescription;
|
||||||
|
|
|
@ -9,6 +9,7 @@ import android.graphics.PorterDuff;
|
||||||
import android.graphics.drawable.Drawable;
|
import android.graphics.drawable.Drawable;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.provider.DocumentsContract;
|
import android.provider.DocumentsContract;
|
||||||
|
import android.text.format.DateFormat;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
@ -20,10 +21,14 @@ import androidx.core.content.ContextCompat;
|
||||||
import androidx.recyclerview.widget.RecyclerView;
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
|
|
||||||
import com.google.android.material.color.MaterialColors;
|
import com.google.android.material.color.MaterialColors;
|
||||||
|
import com.google.android.material.datepicker.CalendarConstraints;
|
||||||
|
import com.google.android.material.datepicker.MaterialDatePicker;
|
||||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
|
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
|
||||||
import com.google.android.material.elevation.ElevationOverlayProvider;
|
import com.google.android.material.elevation.ElevationOverlayProvider;
|
||||||
import com.google.android.material.slider.Slider;
|
import com.google.android.material.slider.Slider;
|
||||||
import com.google.android.material.textfield.TextInputEditText;
|
import com.google.android.material.textfield.TextInputEditText;
|
||||||
|
import com.google.android.material.timepicker.MaterialTimePicker;
|
||||||
|
import com.google.android.material.timepicker.TimeFormat;
|
||||||
|
|
||||||
import org.dolphinemu.dolphinemu.R;
|
import org.dolphinemu.dolphinemu.R;
|
||||||
import org.dolphinemu.dolphinemu.databinding.DialogInputStringBinding;
|
import org.dolphinemu.dolphinemu.databinding.DialogInputStringBinding;
|
||||||
|
@ -34,6 +39,7 @@ import org.dolphinemu.dolphinemu.databinding.ListItemSettingSwitchBinding;
|
||||||
import org.dolphinemu.dolphinemu.databinding.ListItemSubmenuBinding;
|
import org.dolphinemu.dolphinemu.databinding.ListItemSubmenuBinding;
|
||||||
import org.dolphinemu.dolphinemu.dialogs.MotionAlertDialog;
|
import org.dolphinemu.dolphinemu.dialogs.MotionAlertDialog;
|
||||||
import org.dolphinemu.dolphinemu.features.settings.model.Settings;
|
import org.dolphinemu.dolphinemu.features.settings.model.Settings;
|
||||||
|
import org.dolphinemu.dolphinemu.features.settings.model.view.DateTimeChoiceSetting;
|
||||||
import org.dolphinemu.dolphinemu.features.settings.model.view.SwitchSetting;
|
import org.dolphinemu.dolphinemu.features.settings.model.view.SwitchSetting;
|
||||||
import org.dolphinemu.dolphinemu.features.settings.model.view.FilePicker;
|
import org.dolphinemu.dolphinemu.features.settings.model.view.FilePicker;
|
||||||
import org.dolphinemu.dolphinemu.features.settings.model.view.FloatSliderSetting;
|
import org.dolphinemu.dolphinemu.features.settings.model.view.FloatSliderSetting;
|
||||||
|
@ -47,6 +53,7 @@ import org.dolphinemu.dolphinemu.features.settings.model.view.SliderSetting;
|
||||||
import org.dolphinemu.dolphinemu.features.settings.model.view.InputStringSetting;
|
import org.dolphinemu.dolphinemu.features.settings.model.view.InputStringSetting;
|
||||||
import org.dolphinemu.dolphinemu.features.settings.model.view.StringSingleChoiceSetting;
|
import org.dolphinemu.dolphinemu.features.settings.model.view.StringSingleChoiceSetting;
|
||||||
import org.dolphinemu.dolphinemu.features.settings.model.view.SubmenuSetting;
|
import org.dolphinemu.dolphinemu.features.settings.model.view.SubmenuSetting;
|
||||||
|
import org.dolphinemu.dolphinemu.features.settings.ui.viewholder.DateTimeSettingViewHolder;
|
||||||
import org.dolphinemu.dolphinemu.features.settings.ui.viewholder.FilePickerViewHolder;
|
import org.dolphinemu.dolphinemu.features.settings.ui.viewholder.FilePickerViewHolder;
|
||||||
import org.dolphinemu.dolphinemu.features.settings.ui.viewholder.HeaderHyperLinkViewHolder;
|
import org.dolphinemu.dolphinemu.features.settings.ui.viewholder.HeaderHyperLinkViewHolder;
|
||||||
import org.dolphinemu.dolphinemu.features.settings.ui.viewholder.HeaderViewHolder;
|
import org.dolphinemu.dolphinemu.features.settings.ui.viewholder.HeaderViewHolder;
|
||||||
|
@ -68,6 +75,8 @@ import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.RandomAccessFile;
|
import java.io.RandomAccessFile;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Calendar;
|
||||||
|
import java.util.TimeZone;
|
||||||
|
|
||||||
public final class SettingsAdapter extends RecyclerView.Adapter<SettingViewHolder>
|
public final class SettingsAdapter extends RecyclerView.Adapter<SettingViewHolder>
|
||||||
implements DialogInterface.OnClickListener, Slider.OnChangeListener
|
implements DialogInterface.OnClickListener, Slider.OnChangeListener
|
||||||
|
@ -135,6 +144,9 @@ public final class SettingsAdapter extends RecyclerView.Adapter<SettingViewHolde
|
||||||
case SettingsItem.TYPE_HYPERLINK_HEADER:
|
case SettingsItem.TYPE_HYPERLINK_HEADER:
|
||||||
return new HeaderHyperLinkViewHolder(ListItemHeaderBinding.inflate(inflater), this);
|
return new HeaderHyperLinkViewHolder(ListItemHeaderBinding.inflate(inflater), this);
|
||||||
|
|
||||||
|
case SettingsItem.TYPE_DATETIME_CHOICE:
|
||||||
|
return new DateTimeSettingViewHolder(ListItemSettingBinding.inflate(inflater), this);
|
||||||
|
|
||||||
default:
|
default:
|
||||||
throw new IllegalArgumentException("Invalid view type: " + viewType);
|
throw new IllegalArgumentException("Invalid view type: " + viewType);
|
||||||
}
|
}
|
||||||
|
@ -366,6 +378,63 @@ public final class SettingsAdapter extends RecyclerView.Adapter<SettingViewHolde
|
||||||
mView.getActivity().startActivityForResult(intent, filePicker.getRequestType());
|
mView.getActivity().startActivityForResult(intent, filePicker.getRequestType());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void onDateTimeClick(DateTimeChoiceSetting item, int position)
|
||||||
|
{
|
||||||
|
mClickedItem = item;
|
||||||
|
mClickedPosition = position;
|
||||||
|
long storedTime = Long.decode(item.getSelectedValue(mView.getSettings())) * 1000;
|
||||||
|
|
||||||
|
// Helper to extract hour and minute from epoch time
|
||||||
|
Calendar calendar = Calendar.getInstance();
|
||||||
|
calendar.setTimeInMillis(storedTime);
|
||||||
|
calendar.setTimeZone(TimeZone.getTimeZone("UTC"));
|
||||||
|
|
||||||
|
// Start and end epoch times available for the Wii's date picker
|
||||||
|
CalendarConstraints calendarConstraints = new CalendarConstraints.Builder()
|
||||||
|
.setStart(946684800000L)
|
||||||
|
.setEnd(2082672000000L)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
int timeFormat = TimeFormat.CLOCK_12H;
|
||||||
|
if (DateFormat.is24HourFormat(mView.getActivity()))
|
||||||
|
{
|
||||||
|
timeFormat = TimeFormat.CLOCK_24H;
|
||||||
|
}
|
||||||
|
|
||||||
|
MaterialDatePicker<Long> datePicker = MaterialDatePicker.Builder.datePicker()
|
||||||
|
.setSelection(storedTime)
|
||||||
|
.setTitleText(R.string.select_rtc_date)
|
||||||
|
.setCalendarConstraints(calendarConstraints)
|
||||||
|
.build();
|
||||||
|
MaterialTimePicker timePicker = new MaterialTimePicker.Builder()
|
||||||
|
.setTimeFormat(timeFormat)
|
||||||
|
.setHour(calendar.get(Calendar.HOUR_OF_DAY))
|
||||||
|
.setMinute(calendar.get(Calendar.MINUTE))
|
||||||
|
.setTitleText(R.string.select_rtc_time)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
datePicker.addOnPositiveButtonClickListener(
|
||||||
|
selection -> timePicker.show(mView.getActivity().getSupportFragmentManager(),
|
||||||
|
"TimePicker"));
|
||||||
|
timePicker.addOnPositiveButtonClickListener(selection ->
|
||||||
|
{
|
||||||
|
long epochTime = datePicker.getSelection() / 1000;
|
||||||
|
epochTime += (long) timePicker.getHour() * 60 * 60;
|
||||||
|
epochTime += (long) timePicker.getMinute() * 60;
|
||||||
|
String rtcString = "0x" + Long.toHexString(epochTime);
|
||||||
|
if (!item.getSelectedValue(mView.getSettings()).equals(rtcString))
|
||||||
|
{
|
||||||
|
notifyItemChanged(mClickedPosition);
|
||||||
|
mView.onSettingChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
item.setSelectedValue(mView.getSettings(), rtcString);
|
||||||
|
|
||||||
|
mClickedItem = null;
|
||||||
|
});
|
||||||
|
datePicker.show(mView.getActivity().getSupportFragmentManager(), "DatePicker");
|
||||||
|
}
|
||||||
|
|
||||||
public void onFilePickerConfirmation(String selectedFile)
|
public void onFilePickerConfirmation(String selectedFile)
|
||||||
{
|
{
|
||||||
FilePicker filePicker = (FilePicker) mClickedItem;
|
FilePicker filePicker = (FilePicker) mClickedItem;
|
||||||
|
|
|
@ -25,6 +25,7 @@ import org.dolphinemu.dolphinemu.features.settings.model.PostProcessing;
|
||||||
import org.dolphinemu.dolphinemu.features.settings.model.Settings;
|
import org.dolphinemu.dolphinemu.features.settings.model.Settings;
|
||||||
import org.dolphinemu.dolphinemu.features.settings.model.StringSetting;
|
import org.dolphinemu.dolphinemu.features.settings.model.StringSetting;
|
||||||
import org.dolphinemu.dolphinemu.features.settings.model.WiimoteProfileStringSetting;
|
import org.dolphinemu.dolphinemu.features.settings.model.WiimoteProfileStringSetting;
|
||||||
|
import org.dolphinemu.dolphinemu.features.settings.model.view.DateTimeChoiceSetting;
|
||||||
import org.dolphinemu.dolphinemu.features.settings.model.view.SwitchSetting;
|
import org.dolphinemu.dolphinemu.features.settings.model.view.SwitchSetting;
|
||||||
import org.dolphinemu.dolphinemu.features.settings.model.view.FilePicker;
|
import org.dolphinemu.dolphinemu.features.settings.model.view.FilePicker;
|
||||||
import org.dolphinemu.dolphinemu.features.settings.model.view.HeaderSetting;
|
import org.dolphinemu.dolphinemu.features.settings.model.view.HeaderSetting;
|
||||||
|
@ -811,6 +812,12 @@ public final class SettingsFragmentPresenter
|
||||||
sl.add(new SingleChoiceSetting(mContext, synchronizeGpuThread, R.string.synchronize_gpu_thread,
|
sl.add(new SingleChoiceSetting(mContext, synchronizeGpuThread, R.string.synchronize_gpu_thread,
|
||||||
R.string.synchronize_gpu_thread_description, R.array.synchronizeGpuThreadEntries,
|
R.string.synchronize_gpu_thread_description, R.array.synchronizeGpuThreadEntries,
|
||||||
R.array.synchronizeGpuThreadValues));
|
R.array.synchronizeGpuThreadValues));
|
||||||
|
|
||||||
|
sl.add(new HeaderSetting(mContext, R.string.custom_rtc_options, 0));
|
||||||
|
sl.add(new SwitchSetting(mContext, BooleanSetting.MAIN_CUSTOM_RTC_ENABLE,
|
||||||
|
R.string.custom_rtc_enable, R.string.custom_rtc_description));
|
||||||
|
sl.add(new DateTimeChoiceSetting(mContext, StringSetting.MAIN_CUSTOM_RTC_VALUE,
|
||||||
|
R.string.set_custom_rtc, 0));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addSerialPortSubSettings(ArrayList<SettingsItem> sl, int serialPort1Type)
|
private void addSerialPortSubSettings(ArrayList<SettingsItem> sl, int serialPort1Type)
|
||||||
|
|
|
@ -0,0 +1,52 @@
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
package org.dolphinemu.dolphinemu.features.settings.ui.viewholder
|
||||||
|
|
||||||
|
import android.text.TextUtils
|
||||||
|
import android.view.View
|
||||||
|
import org.dolphinemu.dolphinemu.databinding.ListItemSettingBinding
|
||||||
|
import org.dolphinemu.dolphinemu.features.settings.model.view.DateTimeChoiceSetting
|
||||||
|
import org.dolphinemu.dolphinemu.features.settings.model.view.SettingsItem
|
||||||
|
import org.dolphinemu.dolphinemu.features.settings.ui.SettingsAdapter
|
||||||
|
import java.time.Instant
|
||||||
|
import java.time.ZoneId
|
||||||
|
import java.time.ZonedDateTime
|
||||||
|
import java.time.format.DateTimeFormatter
|
||||||
|
import java.time.format.FormatStyle
|
||||||
|
|
||||||
|
class DateTimeSettingViewHolder(
|
||||||
|
private val binding: ListItemSettingBinding,
|
||||||
|
adapter: SettingsAdapter
|
||||||
|
) : SettingViewHolder(binding.root, adapter) {
|
||||||
|
private var mItem: DateTimeChoiceSetting? = null
|
||||||
|
|
||||||
|
override fun bind(item: SettingsItem) {
|
||||||
|
mItem = item as DateTimeChoiceSetting
|
||||||
|
val inputTime = mItem!!.getSelectedValue(adapter.settings)
|
||||||
|
binding.textSettingName.text = item.getName()
|
||||||
|
|
||||||
|
if (!TextUtils.isEmpty(inputTime)) {
|
||||||
|
val epochTime = inputTime.substring(2).toLong(16)
|
||||||
|
val instant = Instant.ofEpochMilli(epochTime * 1000)
|
||||||
|
val zonedTime = ZonedDateTime.ofInstant(instant, ZoneId.of("UTC"))
|
||||||
|
val dateFormatter = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.MEDIUM)
|
||||||
|
binding.textSettingDescription.text = dateFormatter.format(zonedTime)
|
||||||
|
} else {
|
||||||
|
binding.textSettingDescription.text = item.getDescription()
|
||||||
|
}
|
||||||
|
setStyle(binding.textSettingName, mItem)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onClick(clicked: View) {
|
||||||
|
if (!mItem!!.isEditable) {
|
||||||
|
showNotRuntimeEditableError()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
adapter.onDateTimeClick(mItem, bindingAdapterPosition)
|
||||||
|
setStyle(binding.textSettingName, mItem)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getItem(): SettingsItem? {
|
||||||
|
return mItem
|
||||||
|
}
|
||||||
|
}
|
|
@ -404,6 +404,12 @@
|
||||||
<string name="gpu_options">GPU Options</string>
|
<string name="gpu_options">GPU Options</string>
|
||||||
<string name="synchronize_gpu_thread">Synchronize GPU Thread</string>
|
<string name="synchronize_gpu_thread">Synchronize GPU Thread</string>
|
||||||
<string name="synchronize_gpu_thread_description">Synchronizing the GPU thread reduces the risk of games crashing or becoming unstable with dual core enabled, but can also reduce the performance gain of dual core. If unsure, select \"On Idle Skipping\". Selecting \"Never\" is risky and not recommended!</string>
|
<string name="synchronize_gpu_thread_description">Synchronizing the GPU thread reduces the risk of games crashing or becoming unstable with dual core enabled, but can also reduce the performance gain of dual core. If unsure, select \"On Idle Skipping\". Selecting \"Never\" is risky and not recommended!</string>
|
||||||
|
<string name="custom_rtc_options">Custom RTC Options</string>
|
||||||
|
<string name="custom_rtc_enable">Enable Custom RTC</string>
|
||||||
|
<string name="custom_rtc_description">This settings allows you to set a custom real time clock (RTC) separate from your current system time.</string>
|
||||||
|
<string name="set_custom_rtc">Set Custom RTC</string>
|
||||||
|
<string name="select_rtc_date">Select RTC Date</string>
|
||||||
|
<string name="select_rtc_time">Select RTC Time</string>
|
||||||
|
|
||||||
<!-- Log Configuration -->
|
<!-- Log Configuration -->
|
||||||
<string name="log_submenu">Log</string>
|
<string name="log_submenu">Log</string>
|
||||||
|
|
Loading…
Reference in New Issue