mirror of
https://github.com/dolphin-emu/dolphin.git
synced 2025-03-12 14:46:49 +01:00
Android: Convert SettingsAdapter to Kotlin
This commit is contained in:
parent
673c8d9cb2
commit
9020b6aeb9
@ -1,703 +0,0 @@
|
|||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
||||||
|
|
||||||
package org.dolphinemu.dolphinemu.features.settings.ui;
|
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
import android.content.DialogInterface;
|
|
||||||
import android.content.Intent;
|
|
||||||
import android.graphics.PorterDuff;
|
|
||||||
import android.graphics.drawable.Drawable;
|
|
||||||
import android.os.Build;
|
|
||||||
import android.provider.DocumentsContract;
|
|
||||||
import android.text.format.DateFormat;
|
|
||||||
import android.view.LayoutInflater;
|
|
||||||
import android.view.ViewGroup;
|
|
||||||
import android.widget.TextView;
|
|
||||||
|
|
||||||
import androidx.annotation.ColorInt;
|
|
||||||
import androidx.annotation.NonNull;
|
|
||||||
import androidx.appcompat.app.AlertDialog;
|
|
||||||
import androidx.core.content.ContextCompat;
|
|
||||||
import androidx.recyclerview.widget.RecyclerView;
|
|
||||||
|
|
||||||
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.elevation.ElevationOverlayProvider;
|
|
||||||
import com.google.android.material.slider.Slider;
|
|
||||||
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.databinding.DialogAdvancedMappingBinding;
|
|
||||||
import org.dolphinemu.dolphinemu.databinding.DialogInputStringBinding;
|
|
||||||
import org.dolphinemu.dolphinemu.databinding.DialogSliderBinding;
|
|
||||||
import org.dolphinemu.dolphinemu.databinding.ListItemHeaderBinding;
|
|
||||||
import org.dolphinemu.dolphinemu.databinding.ListItemMappingBinding;
|
|
||||||
import org.dolphinemu.dolphinemu.databinding.ListItemSettingBinding;
|
|
||||||
import org.dolphinemu.dolphinemu.databinding.ListItemSettingSwitchBinding;
|
|
||||||
import org.dolphinemu.dolphinemu.databinding.ListItemSubmenuBinding;
|
|
||||||
import org.dolphinemu.dolphinemu.features.input.ui.AdvancedMappingDialog;
|
|
||||||
import org.dolphinemu.dolphinemu.features.input.ui.MotionAlertDialog;
|
|
||||||
import org.dolphinemu.dolphinemu.features.input.model.view.InputMappingControlSetting;
|
|
||||||
import org.dolphinemu.dolphinemu.features.input.ui.viewholder.InputMappingControlSettingViewHolder;
|
|
||||||
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.FilePicker;
|
|
||||||
import org.dolphinemu.dolphinemu.features.settings.model.view.FloatSliderSetting;
|
|
||||||
import org.dolphinemu.dolphinemu.features.settings.model.view.IntSliderSetting;
|
|
||||||
import org.dolphinemu.dolphinemu.features.settings.model.view.SettingsItem;
|
|
||||||
import org.dolphinemu.dolphinemu.features.settings.model.view.SingleChoiceSetting;
|
|
||||||
import org.dolphinemu.dolphinemu.features.settings.model.view.SingleChoiceSettingDynamicDescriptions;
|
|
||||||
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.StringSingleChoiceSetting;
|
|
||||||
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.HeaderHyperLinkViewHolder;
|
|
||||||
import org.dolphinemu.dolphinemu.features.settings.ui.viewholder.HeaderViewHolder;
|
|
||||||
import org.dolphinemu.dolphinemu.features.settings.ui.viewholder.InputStringSettingViewHolder;
|
|
||||||
import org.dolphinemu.dolphinemu.features.settings.ui.viewholder.RunRunnableViewHolder;
|
|
||||||
import org.dolphinemu.dolphinemu.features.settings.ui.viewholder.SettingViewHolder;
|
|
||||||
import org.dolphinemu.dolphinemu.features.settings.ui.viewholder.SingleChoiceViewHolder;
|
|
||||||
import org.dolphinemu.dolphinemu.features.settings.ui.viewholder.SliderViewHolder;
|
|
||||||
import org.dolphinemu.dolphinemu.features.settings.ui.viewholder.SubmenuViewHolder;
|
|
||||||
import org.dolphinemu.dolphinemu.features.settings.ui.viewholder.SwitchSettingViewHolder;
|
|
||||||
import org.dolphinemu.dolphinemu.utils.DirectoryInitialization;
|
|
||||||
import org.dolphinemu.dolphinemu.utils.FileBrowserHelper;
|
|
||||||
import org.dolphinemu.dolphinemu.utils.Log;
|
|
||||||
import org.dolphinemu.dolphinemu.utils.PermissionsHandler;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.RandomAccessFile;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Calendar;
|
|
||||||
import java.util.TimeZone;
|
|
||||||
|
|
||||||
public final class SettingsAdapter extends RecyclerView.Adapter<SettingViewHolder>
|
|
||||||
implements DialogInterface.OnClickListener, Slider.OnChangeListener
|
|
||||||
{
|
|
||||||
private final SettingsFragmentView mView;
|
|
||||||
private final Context mContext;
|
|
||||||
private ArrayList<SettingsItem> mSettings;
|
|
||||||
|
|
||||||
private SettingsItem mClickedItem;
|
|
||||||
private int mClickedPosition;
|
|
||||||
private int mSeekbarProgress;
|
|
||||||
|
|
||||||
private AlertDialog mDialog;
|
|
||||||
private TextView mTextSliderValue;
|
|
||||||
|
|
||||||
public SettingsAdapter(SettingsFragmentView view, Context context)
|
|
||||||
{
|
|
||||||
mView = view;
|
|
||||||
mContext = context;
|
|
||||||
mClickedPosition = -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
@NonNull
|
|
||||||
@Override
|
|
||||||
public SettingViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType)
|
|
||||||
{
|
|
||||||
LayoutInflater inflater = LayoutInflater.from(parent.getContext());
|
|
||||||
switch (viewType)
|
|
||||||
{
|
|
||||||
case SettingsItem.TYPE_HEADER:
|
|
||||||
return new HeaderViewHolder(ListItemHeaderBinding.inflate(inflater), this);
|
|
||||||
|
|
||||||
case SettingsItem.TYPE_SWITCH:
|
|
||||||
return new SwitchSettingViewHolder(ListItemSettingSwitchBinding.inflate(inflater),
|
|
||||||
this);
|
|
||||||
|
|
||||||
case SettingsItem.TYPE_STRING_SINGLE_CHOICE:
|
|
||||||
case SettingsItem.TYPE_SINGLE_CHOICE_DYNAMIC_DESCRIPTIONS:
|
|
||||||
case SettingsItem.TYPE_SINGLE_CHOICE:
|
|
||||||
return new SingleChoiceViewHolder(ListItemSettingBinding.inflate(inflater), this);
|
|
||||||
|
|
||||||
case SettingsItem.TYPE_SLIDER:
|
|
||||||
return new SliderViewHolder(ListItemSettingBinding.inflate(inflater), this, mContext);
|
|
||||||
|
|
||||||
case SettingsItem.TYPE_SUBMENU:
|
|
||||||
return new SubmenuViewHolder(ListItemSubmenuBinding.inflate(inflater), this);
|
|
||||||
|
|
||||||
case SettingsItem.TYPE_INPUT_MAPPING_CONTROL:
|
|
||||||
return new InputMappingControlSettingViewHolder(ListItemMappingBinding.inflate(inflater),
|
|
||||||
this);
|
|
||||||
|
|
||||||
case SettingsItem.TYPE_FILE_PICKER:
|
|
||||||
return new FilePickerViewHolder(ListItemSettingBinding.inflate(inflater), this);
|
|
||||||
|
|
||||||
case SettingsItem.TYPE_RUN_RUNNABLE:
|
|
||||||
return new RunRunnableViewHolder(ListItemSettingBinding.inflate(inflater), this, mContext);
|
|
||||||
|
|
||||||
case SettingsItem.TYPE_STRING:
|
|
||||||
return new InputStringSettingViewHolder(ListItemSettingBinding.inflate(inflater), this);
|
|
||||||
|
|
||||||
case SettingsItem.TYPE_HYPERLINK_HEADER:
|
|
||||||
return new HeaderHyperLinkViewHolder(ListItemHeaderBinding.inflate(inflater), this);
|
|
||||||
|
|
||||||
case SettingsItem.TYPE_DATETIME_CHOICE:
|
|
||||||
return new DateTimeSettingViewHolder(ListItemSettingBinding.inflate(inflater), this);
|
|
||||||
|
|
||||||
default:
|
|
||||||
throw new IllegalArgumentException("Invalid view type: " + viewType);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onBindViewHolder(@NonNull SettingViewHolder holder, int position)
|
|
||||||
{
|
|
||||||
holder.bind(getItem(position));
|
|
||||||
}
|
|
||||||
|
|
||||||
private SettingsItem getItem(int position)
|
|
||||||
{
|
|
||||||
return mSettings.get(position);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getItemCount()
|
|
||||||
{
|
|
||||||
if (mSettings != null)
|
|
||||||
{
|
|
||||||
return mSettings.size();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getItemViewType(int position)
|
|
||||||
{
|
|
||||||
return getItem(position).getType();
|
|
||||||
}
|
|
||||||
|
|
||||||
public Settings getSettings()
|
|
||||||
{
|
|
||||||
return mView.getSettings();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setSettings(ArrayList<SettingsItem> settings)
|
|
||||||
{
|
|
||||||
mSettings = settings;
|
|
||||||
notifyDataSetChanged();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void clearSetting(SettingsItem item)
|
|
||||||
{
|
|
||||||
item.clear(getSettings());
|
|
||||||
|
|
||||||
mView.onSettingChanged();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void notifyAllSettingsChanged()
|
|
||||||
{
|
|
||||||
notifyItemRangeChanged(0, getItemCount());
|
|
||||||
|
|
||||||
mView.onSettingChanged();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void onBooleanClick(SwitchSetting item, boolean checked)
|
|
||||||
{
|
|
||||||
item.setChecked(getSettings(), checked);
|
|
||||||
|
|
||||||
mView.onSettingChanged();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void onInputStringClick(InputStringSetting item, int position)
|
|
||||||
{
|
|
||||||
LayoutInflater inflater = LayoutInflater.from(mContext);
|
|
||||||
|
|
||||||
DialogInputStringBinding binding = DialogInputStringBinding.inflate(inflater);
|
|
||||||
TextInputEditText input = binding.input;
|
|
||||||
input.setText(item.getSelectedValue());
|
|
||||||
|
|
||||||
mDialog = new MaterialAlertDialogBuilder(mView.getActivity())
|
|
||||||
.setView(binding.getRoot())
|
|
||||||
.setMessage(item.getDescription())
|
|
||||||
.setPositiveButton(R.string.ok, (dialogInterface, i) ->
|
|
||||||
{
|
|
||||||
String editTextInput = input.getText().toString();
|
|
||||||
|
|
||||||
if (!item.getSelectedValue().equals(editTextInput))
|
|
||||||
{
|
|
||||||
notifyItemChanged(position);
|
|
||||||
mView.onSettingChanged();
|
|
||||||
}
|
|
||||||
|
|
||||||
item.setSelectedValue(mView.getSettings(), editTextInput);
|
|
||||||
})
|
|
||||||
.setNegativeButton(R.string.cancel, null)
|
|
||||||
.show();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void onSingleChoiceClick(SingleChoiceSetting item, int position)
|
|
||||||
{
|
|
||||||
mClickedItem = item;
|
|
||||||
mClickedPosition = position;
|
|
||||||
|
|
||||||
int value = getSelectionForSingleChoiceValue(item);
|
|
||||||
|
|
||||||
mDialog = new MaterialAlertDialogBuilder(mView.getActivity())
|
|
||||||
.setTitle(item.getName())
|
|
||||||
.setSingleChoiceItems(item.getChoicesId(), value, this)
|
|
||||||
.show();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void onStringSingleChoiceClick(StringSingleChoiceSetting item, int position)
|
|
||||||
{
|
|
||||||
mClickedItem = item;
|
|
||||||
mClickedPosition = position;
|
|
||||||
|
|
||||||
item.refreshChoicesAndValues();
|
|
||||||
|
|
||||||
String[] choices = item.getChoices();
|
|
||||||
int noChoicesAvailableString = item.getNoChoicesAvailableString();
|
|
||||||
if (noChoicesAvailableString != 0 && choices.length == 0)
|
|
||||||
{
|
|
||||||
mDialog = new MaterialAlertDialogBuilder(mView.getActivity())
|
|
||||||
.setTitle(item.getName())
|
|
||||||
.setMessage(noChoicesAvailableString)
|
|
||||||
.setPositiveButton(R.string.ok, null)
|
|
||||||
.show();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
mDialog = new MaterialAlertDialogBuilder(mView.getActivity())
|
|
||||||
.setTitle(item.getName())
|
|
||||||
.setSingleChoiceItems(item.getChoices(), item.getSelectedValueIndex(),
|
|
||||||
this)
|
|
||||||
.show();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void onSingleChoiceDynamicDescriptionsClick(SingleChoiceSettingDynamicDescriptions item,
|
|
||||||
int position)
|
|
||||||
{
|
|
||||||
mClickedItem = item;
|
|
||||||
mClickedPosition = position;
|
|
||||||
|
|
||||||
int value = getSelectionForSingleChoiceDynamicDescriptionsValue(item);
|
|
||||||
|
|
||||||
mDialog = new MaterialAlertDialogBuilder(mView.getActivity())
|
|
||||||
.setTitle(item.getName())
|
|
||||||
.setSingleChoiceItems(item.getChoicesId(), value, this)
|
|
||||||
.show();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void onSliderClick(SliderSetting item, int position)
|
|
||||||
{
|
|
||||||
mClickedItem = item;
|
|
||||||
mClickedPosition = position;
|
|
||||||
mSeekbarProgress = item.getSelectedValue();
|
|
||||||
|
|
||||||
LayoutInflater inflater = LayoutInflater.from(mView.getActivity());
|
|
||||||
DialogSliderBinding binding = DialogSliderBinding.inflate(inflater);
|
|
||||||
|
|
||||||
mTextSliderValue = binding.textValue;
|
|
||||||
mTextSliderValue.setText(String.valueOf(mSeekbarProgress));
|
|
||||||
|
|
||||||
binding.textUnits.setText(item.getUnits());
|
|
||||||
|
|
||||||
Slider slider = binding.slider;
|
|
||||||
slider.setValueFrom(item.getMin());
|
|
||||||
slider.setValueTo(item.getMax());
|
|
||||||
slider.setValue(mSeekbarProgress);
|
|
||||||
slider.setStepSize(item.getStepSize());
|
|
||||||
|
|
||||||
slider.addOnChangeListener(this);
|
|
||||||
|
|
||||||
mDialog = new MaterialAlertDialogBuilder(mView.getActivity())
|
|
||||||
.setTitle(item.getName())
|
|
||||||
.setView(binding.getRoot())
|
|
||||||
.setPositiveButton(R.string.ok, this)
|
|
||||||
.show();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void onSubmenuClick(SubmenuSetting item)
|
|
||||||
{
|
|
||||||
mView.loadSubMenu(item.getMenuKey());
|
|
||||||
}
|
|
||||||
|
|
||||||
public void onInputMappingClick(final InputMappingControlSetting item, final int position)
|
|
||||||
{
|
|
||||||
if (item.getController().getDefaultDevice().isEmpty() && !mView.isMappingAllDevices())
|
|
||||||
{
|
|
||||||
new MaterialAlertDialogBuilder(mView.getActivity())
|
|
||||||
.setMessage(R.string.input_binding_no_device)
|
|
||||||
.setPositiveButton(R.string.ok, this)
|
|
||||||
.show();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
final MotionAlertDialog dialog = new MotionAlertDialog(mView.getActivity(), item,
|
|
||||||
mView.isMappingAllDevices());
|
|
||||||
|
|
||||||
Drawable background = ContextCompat.getDrawable(mContext, R.drawable.dialog_round);
|
|
||||||
@ColorInt int color = new ElevationOverlayProvider(dialog.getContext()).compositeOverlay(
|
|
||||||
MaterialColors.getColor(dialog.getWindow().getDecorView(), R.attr.colorSurface),
|
|
||||||
dialog.getWindow().getDecorView().getElevation());
|
|
||||||
background.setColorFilter(color, PorterDuff.Mode.SRC_ATOP);
|
|
||||||
dialog.getWindow().setBackgroundDrawable(background);
|
|
||||||
|
|
||||||
dialog.setTitle(R.string.input_binding);
|
|
||||||
dialog.setMessage(String.format(mContext.getString(R.string.input_binding_description),
|
|
||||||
item.getName()));
|
|
||||||
dialog.setButton(AlertDialog.BUTTON_NEGATIVE, mContext.getString(R.string.cancel), this);
|
|
||||||
dialog.setButton(AlertDialog.BUTTON_NEUTRAL, mContext.getString(R.string.clear),
|
|
||||||
(dialogInterface, i) -> item.clearValue());
|
|
||||||
dialog.setOnDismissListener(dialog1 ->
|
|
||||||
{
|
|
||||||
notifyItemChanged(position);
|
|
||||||
mView.onSettingChanged();
|
|
||||||
});
|
|
||||||
dialog.setCanceledOnTouchOutside(false);
|
|
||||||
dialog.show();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void onAdvancedInputMappingClick(final InputMappingControlSetting item, final int position)
|
|
||||||
{
|
|
||||||
LayoutInflater inflater = LayoutInflater.from(mContext);
|
|
||||||
|
|
||||||
DialogAdvancedMappingBinding binding = DialogAdvancedMappingBinding.inflate(inflater);
|
|
||||||
|
|
||||||
final AdvancedMappingDialog dialog = new AdvancedMappingDialog(mContext, binding,
|
|
||||||
item.getControlReference(), item.getController());
|
|
||||||
|
|
||||||
Drawable background = ContextCompat.getDrawable(mContext, R.drawable.dialog_round);
|
|
||||||
@ColorInt int color = new ElevationOverlayProvider(dialog.getContext()).compositeOverlay(
|
|
||||||
MaterialColors.getColor(dialog.getWindow().getDecorView(), R.attr.colorSurface),
|
|
||||||
dialog.getWindow().getDecorView().getElevation());
|
|
||||||
background.setColorFilter(color, PorterDuff.Mode.SRC_ATOP);
|
|
||||||
dialog.getWindow().setBackgroundDrawable(background);
|
|
||||||
|
|
||||||
dialog.setTitle(item.isInput() ?
|
|
||||||
R.string.input_configure_input : R.string.input_configure_output);
|
|
||||||
dialog.setView(binding.getRoot());
|
|
||||||
dialog.setButton(AlertDialog.BUTTON_POSITIVE, mContext.getString(R.string.ok),
|
|
||||||
(dialogInterface, i) ->
|
|
||||||
{
|
|
||||||
item.setValue(dialog.getExpression());
|
|
||||||
notifyItemChanged(position);
|
|
||||||
mView.onSettingChanged();
|
|
||||||
});
|
|
||||||
dialog.setButton(AlertDialog.BUTTON_NEGATIVE, mContext.getString(R.string.cancel), this);
|
|
||||||
dialog.setButton(AlertDialog.BUTTON_NEUTRAL, mContext.getString(R.string.clear),
|
|
||||||
(dialogInterface, i) ->
|
|
||||||
{
|
|
||||||
item.clearValue();
|
|
||||||
notifyItemChanged(position);
|
|
||||||
mView.onSettingChanged();
|
|
||||||
});
|
|
||||||
dialog.setCanceledOnTouchOutside(false);
|
|
||||||
dialog.show();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void onFilePickerDirectoryClick(SettingsItem item, int position)
|
|
||||||
{
|
|
||||||
mClickedItem = item;
|
|
||||||
mClickedPosition = position;
|
|
||||||
|
|
||||||
if (!PermissionsHandler.isExternalStorageLegacy())
|
|
||||||
{
|
|
||||||
new MaterialAlertDialogBuilder(mContext)
|
|
||||||
.setMessage(R.string.path_not_changeable_scoped_storage)
|
|
||||||
.setPositiveButton(R.string.ok, (dialog, i) -> dialog.dismiss())
|
|
||||||
.show();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
FileBrowserHelper.openDirectoryPicker(mView.getActivity(), FileBrowserHelper.GAME_EXTENSIONS);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void onFilePickerFileClick(SettingsItem item, int position)
|
|
||||||
{
|
|
||||||
mClickedItem = item;
|
|
||||||
mClickedPosition = position;
|
|
||||||
FilePicker filePicker = (FilePicker) item;
|
|
||||||
|
|
||||||
Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
|
|
||||||
intent.addCategory(Intent.CATEGORY_OPENABLE);
|
|
||||||
intent.setType("*/*");
|
|
||||||
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
|
|
||||||
{
|
|
||||||
intent.putExtra(DocumentsContract.EXTRA_INITIAL_URI, filePicker.getSelectedValue());
|
|
||||||
}
|
|
||||||
|
|
||||||
mView.getActivity().startActivityForResult(intent, filePicker.getRequestType());
|
|
||||||
}
|
|
||||||
|
|
||||||
public void onDateTimeClick(DateTimeChoiceSetting item, int position)
|
|
||||||
{
|
|
||||||
mClickedItem = item;
|
|
||||||
mClickedPosition = position;
|
|
||||||
long storedTime = Long.decode(item.getSelectedValue()) * 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().equals(rtcString))
|
|
||||||
{
|
|
||||||
notifyItemChanged(mClickedPosition);
|
|
||||||
mView.onSettingChanged();
|
|
||||||
}
|
|
||||||
|
|
||||||
item.setSelectedValue(mView.getSettings(), rtcString);
|
|
||||||
|
|
||||||
mClickedItem = null;
|
|
||||||
});
|
|
||||||
datePicker.show(mView.getActivity().getSupportFragmentManager(), "DatePicker");
|
|
||||||
}
|
|
||||||
|
|
||||||
public void onFilePickerConfirmation(String selectedFile)
|
|
||||||
{
|
|
||||||
FilePicker filePicker = (FilePicker) mClickedItem;
|
|
||||||
|
|
||||||
if (!filePicker.getSelectedValue().equals(selectedFile))
|
|
||||||
{
|
|
||||||
notifyItemChanged(mClickedPosition);
|
|
||||||
mView.onSettingChanged();
|
|
||||||
}
|
|
||||||
|
|
||||||
filePicker.setSelectedValue(mView.getSettings(), selectedFile);
|
|
||||||
|
|
||||||
mClickedItem = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void clearLog()
|
|
||||||
{
|
|
||||||
// Don't delete the log in case it is being monitored by another app.
|
|
||||||
File log = new File(DirectoryInitialization.getUserDirectory() + "/Logs/dolphin.log");
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
RandomAccessFile raf = new RandomAccessFile(log, "rw");
|
|
||||||
raf.setLength(0);
|
|
||||||
}
|
|
||||||
catch (IOException e)
|
|
||||||
{
|
|
||||||
Log.error("[SettingsAdapter] Failed to clear log file: " + e.getMessage());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void onMenuTagAction(@NonNull MenuTag menuTag, int value)
|
|
||||||
{
|
|
||||||
mView.onMenuTagAction(menuTag, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean hasMenuTagActionForValue(@NonNull MenuTag menuTag, int value)
|
|
||||||
{
|
|
||||||
return mView.hasMenuTagActionForValue(menuTag, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onClick(DialogInterface dialog, int which)
|
|
||||||
{
|
|
||||||
if (mClickedItem instanceof SingleChoiceSetting)
|
|
||||||
{
|
|
||||||
SingleChoiceSetting scSetting = (SingleChoiceSetting) mClickedItem;
|
|
||||||
|
|
||||||
int value = getValueForSingleChoiceSelection(scSetting, which);
|
|
||||||
if (scSetting.getSelectedValue() != value)
|
|
||||||
mView.onSettingChanged();
|
|
||||||
|
|
||||||
scSetting.setSelectedValue(getSettings(), value);
|
|
||||||
|
|
||||||
closeDialog();
|
|
||||||
}
|
|
||||||
else if (mClickedItem instanceof SingleChoiceSettingDynamicDescriptions)
|
|
||||||
{
|
|
||||||
SingleChoiceSettingDynamicDescriptions scSetting =
|
|
||||||
(SingleChoiceSettingDynamicDescriptions) mClickedItem;
|
|
||||||
|
|
||||||
int value = getValueForSingleChoiceDynamicDescriptionsSelection(scSetting, which);
|
|
||||||
if (scSetting.getSelectedValue() != value)
|
|
||||||
mView.onSettingChanged();
|
|
||||||
|
|
||||||
scSetting.setSelectedValue(getSettings(), value);
|
|
||||||
|
|
||||||
closeDialog();
|
|
||||||
}
|
|
||||||
else if (mClickedItem instanceof StringSingleChoiceSetting)
|
|
||||||
{
|
|
||||||
StringSingleChoiceSetting scSetting = (StringSingleChoiceSetting) mClickedItem;
|
|
||||||
String value = scSetting.getValueAt(which);
|
|
||||||
if (!scSetting.getSelectedValue().equals(value))
|
|
||||||
mView.onSettingChanged();
|
|
||||||
|
|
||||||
scSetting.setSelectedValue(getSettings(), value);
|
|
||||||
|
|
||||||
closeDialog();
|
|
||||||
}
|
|
||||||
else if (mClickedItem instanceof IntSliderSetting)
|
|
||||||
{
|
|
||||||
IntSliderSetting sliderSetting = (IntSliderSetting) mClickedItem;
|
|
||||||
if (sliderSetting.getSelectedValue() != mSeekbarProgress)
|
|
||||||
mView.onSettingChanged();
|
|
||||||
|
|
||||||
sliderSetting.setSelectedValue(getSettings(), mSeekbarProgress);
|
|
||||||
|
|
||||||
closeDialog();
|
|
||||||
}
|
|
||||||
else if (mClickedItem instanceof FloatSliderSetting)
|
|
||||||
{
|
|
||||||
FloatSliderSetting sliderSetting = (FloatSliderSetting) mClickedItem;
|
|
||||||
if (sliderSetting.getSelectedValue() != mSeekbarProgress)
|
|
||||||
mView.onSettingChanged();
|
|
||||||
|
|
||||||
sliderSetting.setSelectedValue(getSettings(), mSeekbarProgress);
|
|
||||||
|
|
||||||
closeDialog();
|
|
||||||
}
|
|
||||||
|
|
||||||
mClickedItem = null;
|
|
||||||
mSeekbarProgress = -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void closeDialog()
|
|
||||||
{
|
|
||||||
if (mDialog != null)
|
|
||||||
{
|
|
||||||
if (mClickedPosition != -1)
|
|
||||||
{
|
|
||||||
notifyItemChanged(mClickedPosition);
|
|
||||||
mClickedPosition = -1;
|
|
||||||
}
|
|
||||||
mDialog.dismiss();
|
|
||||||
mDialog = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onValueChange(@NonNull Slider slider, float progress, boolean fromUser)
|
|
||||||
{
|
|
||||||
mSeekbarProgress = (int) progress;
|
|
||||||
mTextSliderValue.setText(String.valueOf(mSeekbarProgress));
|
|
||||||
}
|
|
||||||
|
|
||||||
private int getValueForSingleChoiceSelection(SingleChoiceSetting item, int which)
|
|
||||||
{
|
|
||||||
int valuesId = item.getValuesId();
|
|
||||||
|
|
||||||
if (valuesId > 0)
|
|
||||||
{
|
|
||||||
int[] valuesArray = mContext.getResources().getIntArray(valuesId);
|
|
||||||
return valuesArray[which];
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return which;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private int getSelectionForSingleChoiceValue(SingleChoiceSetting item)
|
|
||||||
{
|
|
||||||
int value = item.getSelectedValue();
|
|
||||||
int valuesId = item.getValuesId();
|
|
||||||
|
|
||||||
if (valuesId > 0)
|
|
||||||
{
|
|
||||||
int[] valuesArray = mContext.getResources().getIntArray(valuesId);
|
|
||||||
for (int index = 0; index < valuesArray.length; index++)
|
|
||||||
{
|
|
||||||
int current = valuesArray[index];
|
|
||||||
if (current == value)
|
|
||||||
{
|
|
||||||
return index;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
private int getValueForSingleChoiceDynamicDescriptionsSelection(
|
|
||||||
SingleChoiceSettingDynamicDescriptions item, int which)
|
|
||||||
{
|
|
||||||
int valuesId = item.getValuesId();
|
|
||||||
|
|
||||||
if (valuesId > 0)
|
|
||||||
{
|
|
||||||
int[] valuesArray = mContext.getResources().getIntArray(valuesId);
|
|
||||||
return valuesArray[which];
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return which;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private int getSelectionForSingleChoiceDynamicDescriptionsValue(
|
|
||||||
SingleChoiceSettingDynamicDescriptions item)
|
|
||||||
{
|
|
||||||
int value = item.getSelectedValue();
|
|
||||||
int valuesId = item.getValuesId();
|
|
||||||
|
|
||||||
if (valuesId > 0)
|
|
||||||
{
|
|
||||||
int[] valuesArray = mContext.getResources().getIntArray(valuesId);
|
|
||||||
for (int index = 0; index < valuesArray.length; index++)
|
|
||||||
{
|
|
||||||
int current = valuesArray[index];
|
|
||||||
if (current == value)
|
|
||||||
{
|
|
||||||
return index;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
}
|
|
@ -0,0 +1,590 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
package org.dolphinemu.dolphinemu.features.settings.ui
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.DialogInterface
|
||||||
|
import android.content.Intent
|
||||||
|
import android.graphics.PorterDuff
|
||||||
|
import android.os.Build
|
||||||
|
import android.provider.DocumentsContract
|
||||||
|
import android.text.format.DateFormat
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import android.widget.TextView
|
||||||
|
import androidx.annotation.ColorInt
|
||||||
|
import androidx.appcompat.app.AlertDialog
|
||||||
|
import androidx.core.content.ContextCompat
|
||||||
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
|
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.elevation.ElevationOverlayProvider
|
||||||
|
import com.google.android.material.slider.Slider
|
||||||
|
import com.google.android.material.timepicker.MaterialTimePicker
|
||||||
|
import com.google.android.material.timepicker.TimeFormat
|
||||||
|
import org.dolphinemu.dolphinemu.R
|
||||||
|
import org.dolphinemu.dolphinemu.databinding.*
|
||||||
|
import org.dolphinemu.dolphinemu.features.input.model.view.InputMappingControlSetting
|
||||||
|
import org.dolphinemu.dolphinemu.features.input.ui.AdvancedMappingDialog
|
||||||
|
import org.dolphinemu.dolphinemu.features.input.ui.MotionAlertDialog
|
||||||
|
import org.dolphinemu.dolphinemu.features.input.ui.viewholder.InputMappingControlSettingViewHolder
|
||||||
|
import org.dolphinemu.dolphinemu.features.settings.model.Settings
|
||||||
|
import org.dolphinemu.dolphinemu.features.settings.model.view.*
|
||||||
|
import org.dolphinemu.dolphinemu.features.settings.ui.viewholder.*
|
||||||
|
import org.dolphinemu.dolphinemu.utils.DirectoryInitialization
|
||||||
|
import org.dolphinemu.dolphinemu.utils.FileBrowserHelper
|
||||||
|
import org.dolphinemu.dolphinemu.utils.Log
|
||||||
|
import org.dolphinemu.dolphinemu.utils.PermissionsHandler
|
||||||
|
import java.io.File
|
||||||
|
import java.io.IOException
|
||||||
|
import java.io.RandomAccessFile
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
class SettingsAdapter(
|
||||||
|
private val fragmentView: SettingsFragmentView,
|
||||||
|
private val context: Context
|
||||||
|
) :
|
||||||
|
RecyclerView.Adapter<SettingViewHolder>(), DialogInterface.OnClickListener,
|
||||||
|
Slider.OnChangeListener {
|
||||||
|
private var settingsList: ArrayList<SettingsItem>? = null
|
||||||
|
private var clickedItem: SettingsItem? = null
|
||||||
|
private var clickedPosition: Int = -1
|
||||||
|
private var seekbarProgress = 0
|
||||||
|
private var dialog: AlertDialog? = null
|
||||||
|
private var textSliderValue: TextView? = null
|
||||||
|
|
||||||
|
val settings: Settings?
|
||||||
|
get() = fragmentView.settings
|
||||||
|
|
||||||
|
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): SettingViewHolder {
|
||||||
|
val inflater = LayoutInflater.from(parent.context)
|
||||||
|
return when (viewType) {
|
||||||
|
SettingsItem.TYPE_HEADER -> HeaderViewHolder(
|
||||||
|
ListItemHeaderBinding.inflate(inflater),
|
||||||
|
this
|
||||||
|
)
|
||||||
|
SettingsItem.TYPE_SWITCH -> SwitchSettingViewHolder(
|
||||||
|
ListItemSettingSwitchBinding.inflate(inflater),
|
||||||
|
this
|
||||||
|
)
|
||||||
|
SettingsItem.TYPE_STRING_SINGLE_CHOICE,
|
||||||
|
SettingsItem.TYPE_SINGLE_CHOICE_DYNAMIC_DESCRIPTIONS,
|
||||||
|
SettingsItem.TYPE_SINGLE_CHOICE -> SingleChoiceViewHolder(
|
||||||
|
ListItemSettingBinding.inflate(inflater),
|
||||||
|
this
|
||||||
|
)
|
||||||
|
SettingsItem.TYPE_SLIDER -> SliderViewHolder(
|
||||||
|
ListItemSettingBinding.inflate(
|
||||||
|
inflater
|
||||||
|
), this, context
|
||||||
|
)
|
||||||
|
SettingsItem.TYPE_SUBMENU -> SubmenuViewHolder(
|
||||||
|
ListItemSubmenuBinding.inflate(
|
||||||
|
inflater
|
||||||
|
), this
|
||||||
|
)
|
||||||
|
SettingsItem.TYPE_INPUT_MAPPING_CONTROL -> InputMappingControlSettingViewHolder(
|
||||||
|
ListItemMappingBinding.inflate(inflater),
|
||||||
|
this
|
||||||
|
)
|
||||||
|
SettingsItem.TYPE_FILE_PICKER -> FilePickerViewHolder(
|
||||||
|
ListItemSettingBinding.inflate(
|
||||||
|
inflater
|
||||||
|
), this
|
||||||
|
)
|
||||||
|
SettingsItem.TYPE_RUN_RUNNABLE -> RunRunnableViewHolder(
|
||||||
|
ListItemSettingBinding.inflate(
|
||||||
|
inflater
|
||||||
|
), this, context
|
||||||
|
)
|
||||||
|
SettingsItem.TYPE_STRING -> InputStringSettingViewHolder(
|
||||||
|
ListItemSettingBinding.inflate(
|
||||||
|
inflater
|
||||||
|
), this
|
||||||
|
)
|
||||||
|
SettingsItem.TYPE_HYPERLINK_HEADER -> HeaderHyperLinkViewHolder(
|
||||||
|
ListItemHeaderBinding.inflate(
|
||||||
|
inflater
|
||||||
|
), this
|
||||||
|
)
|
||||||
|
SettingsItem.TYPE_DATETIME_CHOICE -> DateTimeSettingViewHolder(
|
||||||
|
ListItemSettingBinding.inflate(
|
||||||
|
inflater
|
||||||
|
), this
|
||||||
|
)
|
||||||
|
else -> throw IllegalArgumentException("Invalid view type: $viewType")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onBindViewHolder(holder: SettingViewHolder, position: Int) {
|
||||||
|
holder.bind(getItem(position))
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getItem(position: Int): SettingsItem {
|
||||||
|
return settingsList!![position]
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getItemCount(): Int {
|
||||||
|
return if (settingsList != null) {
|
||||||
|
settingsList!!.size
|
||||||
|
} else {
|
||||||
|
0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getItemViewType(position: Int): Int {
|
||||||
|
return getItem(position).type
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setSettings(settings: ArrayList<SettingsItem>?) {
|
||||||
|
settingsList = settings
|
||||||
|
notifyDataSetChanged()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun clearSetting(item: SettingsItem) {
|
||||||
|
item.clear(settings!!)
|
||||||
|
fragmentView.onSettingChanged()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun notifyAllSettingsChanged() {
|
||||||
|
notifyItemRangeChanged(0, itemCount)
|
||||||
|
fragmentView.onSettingChanged()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun onBooleanClick(item: SwitchSetting, checked: Boolean) {
|
||||||
|
item.setChecked(settings, checked)
|
||||||
|
fragmentView.onSettingChanged()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun onInputStringClick(item: InputStringSetting, position: Int) {
|
||||||
|
val inflater = LayoutInflater.from(context)
|
||||||
|
val binding = DialogInputStringBinding.inflate(inflater)
|
||||||
|
val input = binding.input
|
||||||
|
input.setText(item.selectedValue)
|
||||||
|
dialog = MaterialAlertDialogBuilder(fragmentView.fragmentActivity)
|
||||||
|
.setView(binding.root)
|
||||||
|
.setMessage(item.description)
|
||||||
|
.setPositiveButton(R.string.ok) { _: DialogInterface?, _: Int ->
|
||||||
|
val editTextInput = input.text.toString()
|
||||||
|
if (item.selectedValue != editTextInput) {
|
||||||
|
notifyItemChanged(position)
|
||||||
|
fragmentView.onSettingChanged()
|
||||||
|
}
|
||||||
|
item.setSelectedValue(fragmentView.settings!!, editTextInput)
|
||||||
|
}
|
||||||
|
.setNegativeButton(R.string.cancel, null)
|
||||||
|
.show()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun onSingleChoiceClick(item: SingleChoiceSetting, position: Int) {
|
||||||
|
clickedItem = item
|
||||||
|
clickedPosition = position
|
||||||
|
val value = getSelectionForSingleChoiceValue(item)
|
||||||
|
dialog = MaterialAlertDialogBuilder(fragmentView.fragmentActivity)
|
||||||
|
.setTitle(item.name)
|
||||||
|
.setSingleChoiceItems(item.choicesId, value, this)
|
||||||
|
.show()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun onStringSingleChoiceClick(item: StringSingleChoiceSetting, position: Int) {
|
||||||
|
clickedItem = item
|
||||||
|
clickedPosition = position
|
||||||
|
item.refreshChoicesAndValues()
|
||||||
|
val choices = item.choices
|
||||||
|
val noChoicesAvailableString = item.noChoicesAvailableString
|
||||||
|
dialog = if (noChoicesAvailableString != 0 && choices!!.isEmpty()) {
|
||||||
|
MaterialAlertDialogBuilder(fragmentView.fragmentActivity)
|
||||||
|
.setTitle(item.name)
|
||||||
|
.setMessage(noChoicesAvailableString)
|
||||||
|
.setPositiveButton(R.string.ok, null)
|
||||||
|
.show()
|
||||||
|
} else {
|
||||||
|
MaterialAlertDialogBuilder(fragmentView.fragmentActivity)
|
||||||
|
.setTitle(item.name)
|
||||||
|
.setSingleChoiceItems(
|
||||||
|
item.choices, item.selectedValueIndex,
|
||||||
|
this
|
||||||
|
)
|
||||||
|
.show()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun onSingleChoiceDynamicDescriptionsClick(
|
||||||
|
item: SingleChoiceSettingDynamicDescriptions,
|
||||||
|
position: Int
|
||||||
|
) {
|
||||||
|
clickedItem = item
|
||||||
|
clickedPosition = position
|
||||||
|
|
||||||
|
val value = getSelectionForSingleChoiceDynamicDescriptionsValue(item)
|
||||||
|
|
||||||
|
dialog = MaterialAlertDialogBuilder(fragmentView.fragmentActivity)
|
||||||
|
.setTitle(item.name)
|
||||||
|
.setSingleChoiceItems(item.choicesId, value, this)
|
||||||
|
.show()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun onSliderClick(item: SliderSetting, position: Int) {
|
||||||
|
clickedItem = item
|
||||||
|
clickedPosition = position
|
||||||
|
seekbarProgress = item.selectedValue
|
||||||
|
|
||||||
|
val inflater = LayoutInflater.from(fragmentView.fragmentActivity)
|
||||||
|
val binding = DialogSliderBinding.inflate(inflater)
|
||||||
|
|
||||||
|
textSliderValue = binding.textValue
|
||||||
|
textSliderValue!!.text = seekbarProgress.toString()
|
||||||
|
|
||||||
|
binding.textUnits.text = item.units
|
||||||
|
|
||||||
|
val slider = binding.slider
|
||||||
|
slider.valueFrom = item.min.toFloat()
|
||||||
|
slider.valueTo = item.max.toFloat()
|
||||||
|
slider.value = seekbarProgress.toFloat()
|
||||||
|
slider.stepSize = item.stepSize.toFloat()
|
||||||
|
slider.addOnChangeListener(this)
|
||||||
|
|
||||||
|
dialog = MaterialAlertDialogBuilder(fragmentView.fragmentActivity)
|
||||||
|
.setTitle(item.name)
|
||||||
|
.setView(binding.root)
|
||||||
|
.setPositiveButton(R.string.ok, this)
|
||||||
|
.show()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun onSubmenuClick(item: SubmenuSetting) {
|
||||||
|
fragmentView.loadSubMenu(item.menuKey)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun onInputMappingClick(item: InputMappingControlSetting, position: Int) {
|
||||||
|
if (item.controller.defaultDevice.isEmpty() && !fragmentView.isMappingAllDevices) {
|
||||||
|
MaterialAlertDialogBuilder(fragmentView.fragmentActivity)
|
||||||
|
.setMessage(R.string.input_binding_no_device)
|
||||||
|
.setPositiveButton(R.string.ok, this)
|
||||||
|
.show()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
val dialog = MotionAlertDialog(
|
||||||
|
fragmentView.fragmentActivity, item,
|
||||||
|
fragmentView.isMappingAllDevices
|
||||||
|
)
|
||||||
|
|
||||||
|
val background = ContextCompat.getDrawable(context, R.drawable.dialog_round)
|
||||||
|
@ColorInt val color = ElevationOverlayProvider(dialog.context).compositeOverlay(
|
||||||
|
MaterialColors.getColor(dialog.window!!.decorView, R.attr.colorSurface),
|
||||||
|
dialog.window!!.decorView.elevation
|
||||||
|
)
|
||||||
|
background!!.setColorFilter(color, PorterDuff.Mode.SRC_ATOP)
|
||||||
|
dialog.window!!.setBackgroundDrawable(background)
|
||||||
|
|
||||||
|
dialog.setTitle(R.string.input_binding)
|
||||||
|
dialog.setMessage(
|
||||||
|
String.format(
|
||||||
|
context.getString(R.string.input_binding_description),
|
||||||
|
item.name
|
||||||
|
)
|
||||||
|
)
|
||||||
|
dialog.setButton(AlertDialog.BUTTON_NEGATIVE, context.getString(R.string.cancel), this)
|
||||||
|
dialog.setButton(
|
||||||
|
AlertDialog.BUTTON_NEUTRAL,
|
||||||
|
context.getString(R.string.clear)
|
||||||
|
) { _: DialogInterface?, _: Int -> item.clearValue() }
|
||||||
|
dialog.setOnDismissListener {
|
||||||
|
notifyItemChanged(position)
|
||||||
|
fragmentView.onSettingChanged()
|
||||||
|
}
|
||||||
|
dialog.setCanceledOnTouchOutside(false)
|
||||||
|
dialog.show()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun onAdvancedInputMappingClick(item: InputMappingControlSetting, position: Int) {
|
||||||
|
val inflater = LayoutInflater.from(context)
|
||||||
|
val binding = DialogAdvancedMappingBinding.inflate(inflater)
|
||||||
|
val dialog = AdvancedMappingDialog(
|
||||||
|
context,
|
||||||
|
binding,
|
||||||
|
item.controlReference,
|
||||||
|
item.controller
|
||||||
|
)
|
||||||
|
|
||||||
|
val background = ContextCompat.getDrawable(context, R.drawable.dialog_round)
|
||||||
|
@ColorInt val color = ElevationOverlayProvider(dialog.context).compositeOverlay(
|
||||||
|
MaterialColors.getColor(dialog.window!!.decorView, R.attr.colorSurface),
|
||||||
|
dialog.window!!.decorView.elevation
|
||||||
|
)
|
||||||
|
background!!.setColorFilter(color, PorterDuff.Mode.SRC_ATOP)
|
||||||
|
dialog.window!!.setBackgroundDrawable(background)
|
||||||
|
|
||||||
|
dialog.setTitle(if (item.isInput) R.string.input_configure_input else R.string.input_configure_output)
|
||||||
|
dialog.setView(binding.root)
|
||||||
|
dialog.setButton(
|
||||||
|
AlertDialog.BUTTON_POSITIVE, context.getString(R.string.ok)
|
||||||
|
) { _: DialogInterface?, _: Int ->
|
||||||
|
item.value = dialog.expression
|
||||||
|
notifyItemChanged(position)
|
||||||
|
fragmentView.onSettingChanged()
|
||||||
|
}
|
||||||
|
dialog.setButton(AlertDialog.BUTTON_NEGATIVE, context.getString(R.string.cancel), this)
|
||||||
|
dialog.setButton(
|
||||||
|
AlertDialog.BUTTON_NEUTRAL,
|
||||||
|
context.getString(R.string.clear)
|
||||||
|
) { _: DialogInterface?, _: Int ->
|
||||||
|
item.clearValue()
|
||||||
|
notifyItemChanged(position)
|
||||||
|
fragmentView.onSettingChanged()
|
||||||
|
}
|
||||||
|
dialog.setCanceledOnTouchOutside(false)
|
||||||
|
dialog.show()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun onFilePickerDirectoryClick(item: SettingsItem?, position: Int) {
|
||||||
|
clickedItem = item
|
||||||
|
clickedPosition = position
|
||||||
|
|
||||||
|
if (!PermissionsHandler.isExternalStorageLegacy()) {
|
||||||
|
MaterialAlertDialogBuilder(context)
|
||||||
|
.setMessage(R.string.path_not_changeable_scoped_storage)
|
||||||
|
.setPositiveButton(R.string.ok) { dialog: DialogInterface, _: Int -> dialog.dismiss() }
|
||||||
|
.show()
|
||||||
|
} else {
|
||||||
|
FileBrowserHelper.openDirectoryPicker(
|
||||||
|
fragmentView.fragmentActivity,
|
||||||
|
FileBrowserHelper.GAME_EXTENSIONS
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun onFilePickerFileClick(item: SettingsItem, position: Int) {
|
||||||
|
clickedItem = item
|
||||||
|
clickedPosition = position
|
||||||
|
val filePicker = item as FilePicker
|
||||||
|
|
||||||
|
val intent = Intent(Intent.ACTION_OPEN_DOCUMENT)
|
||||||
|
intent.addCategory(Intent.CATEGORY_OPENABLE)
|
||||||
|
intent.type = "*/*"
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||||
|
intent.putExtra(DocumentsContract.EXTRA_INITIAL_URI, filePicker.getSelectedValue())
|
||||||
|
}
|
||||||
|
|
||||||
|
fragmentView.fragmentActivity.startActivityForResult(intent, filePicker.requestType)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun onDateTimeClick(item: DateTimeChoiceSetting, position: Int) {
|
||||||
|
clickedItem = item
|
||||||
|
clickedPosition = position
|
||||||
|
val storedTime = java.lang.Long.decode(item.getSelectedValue()) * 1000
|
||||||
|
|
||||||
|
// Helper to extract hour and minute from epoch time
|
||||||
|
val calendar = Calendar.getInstance()
|
||||||
|
calendar.timeInMillis = storedTime
|
||||||
|
calendar.timeZone = TimeZone.getTimeZone("UTC")
|
||||||
|
|
||||||
|
// Start and end epoch times available for the Wii's date picker
|
||||||
|
val calendarConstraints = CalendarConstraints.Builder()
|
||||||
|
.setStart(946684800000L)
|
||||||
|
.setEnd(2082672000000L)
|
||||||
|
.build()
|
||||||
|
|
||||||
|
var timeFormat = TimeFormat.CLOCK_12H
|
||||||
|
if (DateFormat.is24HourFormat(fragmentView.fragmentActivity)) {
|
||||||
|
timeFormat = TimeFormat.CLOCK_24H
|
||||||
|
}
|
||||||
|
|
||||||
|
val datePicker = MaterialDatePicker.Builder.datePicker()
|
||||||
|
.setSelection(storedTime)
|
||||||
|
.setTitleText(R.string.select_rtc_date)
|
||||||
|
.setCalendarConstraints(calendarConstraints)
|
||||||
|
.build()
|
||||||
|
val timePicker = MaterialTimePicker.Builder()
|
||||||
|
.setTimeFormat(timeFormat)
|
||||||
|
.setHour(calendar[Calendar.HOUR_OF_DAY])
|
||||||
|
.setMinute(calendar[Calendar.MINUTE])
|
||||||
|
.setTitleText(R.string.select_rtc_time)
|
||||||
|
.build()
|
||||||
|
|
||||||
|
datePicker.addOnPositiveButtonClickListener {
|
||||||
|
timePicker.show(
|
||||||
|
fragmentView.fragmentActivity.supportFragmentManager,
|
||||||
|
"TimePicker"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
timePicker.addOnPositiveButtonClickListener {
|
||||||
|
var epochTime = datePicker.selection!! / 1000
|
||||||
|
epochTime += timePicker.hour.toLong() * 60 * 60
|
||||||
|
epochTime += timePicker.minute.toLong() * 60
|
||||||
|
val rtcString = "0x" + java.lang.Long.toHexString(epochTime)
|
||||||
|
if (item.getSelectedValue() != rtcString) {
|
||||||
|
notifyItemChanged(clickedPosition)
|
||||||
|
fragmentView.onSettingChanged()
|
||||||
|
}
|
||||||
|
item.setSelectedValue(fragmentView.settings!!, rtcString)
|
||||||
|
clickedItem = null
|
||||||
|
}
|
||||||
|
datePicker.show(fragmentView.fragmentActivity.supportFragmentManager, "DatePicker")
|
||||||
|
}
|
||||||
|
|
||||||
|
fun onFilePickerConfirmation(selectedFile: String) {
|
||||||
|
val filePicker = clickedItem as FilePicker?
|
||||||
|
|
||||||
|
if (filePicker!!.getSelectedValue() != selectedFile) {
|
||||||
|
notifyItemChanged(clickedPosition)
|
||||||
|
fragmentView.onSettingChanged()
|
||||||
|
}
|
||||||
|
|
||||||
|
filePicker.setSelectedValue(fragmentView.settings!!, selectedFile)
|
||||||
|
|
||||||
|
clickedItem = null
|
||||||
|
}
|
||||||
|
|
||||||
|
fun onMenuTagAction(menuTag: MenuTag, value: Int) {
|
||||||
|
fragmentView.onMenuTagAction(menuTag, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun hasMenuTagActionForValue(menuTag: MenuTag, value: Int): Boolean {
|
||||||
|
return fragmentView.hasMenuTagActionForValue(menuTag, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onClick(dialog: DialogInterface, which: Int) {
|
||||||
|
when (clickedItem) {
|
||||||
|
is SingleChoiceSetting -> {
|
||||||
|
val scSetting = clickedItem as SingleChoiceSetting
|
||||||
|
|
||||||
|
val value = getValueForSingleChoiceSelection(scSetting, which)
|
||||||
|
if (scSetting.selectedValue != value) fragmentView.onSettingChanged()
|
||||||
|
|
||||||
|
scSetting.setSelectedValue(settings, value)
|
||||||
|
|
||||||
|
closeDialog()
|
||||||
|
}
|
||||||
|
is SingleChoiceSettingDynamicDescriptions -> {
|
||||||
|
val scSetting = clickedItem as SingleChoiceSettingDynamicDescriptions
|
||||||
|
|
||||||
|
val value = getValueForSingleChoiceDynamicDescriptionsSelection(scSetting, which)
|
||||||
|
if (scSetting.selectedValue != value) fragmentView.onSettingChanged()
|
||||||
|
|
||||||
|
scSetting.setSelectedValue(settings!!, value)
|
||||||
|
|
||||||
|
closeDialog()
|
||||||
|
}
|
||||||
|
is StringSingleChoiceSetting -> {
|
||||||
|
val scSetting = clickedItem as StringSingleChoiceSetting
|
||||||
|
|
||||||
|
val value = scSetting.getValueAt(which)
|
||||||
|
if (scSetting.selectedValue != value) fragmentView.onSettingChanged()
|
||||||
|
|
||||||
|
scSetting.setSelectedValue(settings, value)
|
||||||
|
|
||||||
|
closeDialog()
|
||||||
|
}
|
||||||
|
is IntSliderSetting -> {
|
||||||
|
val sliderSetting = clickedItem as IntSliderSetting
|
||||||
|
if (sliderSetting.selectedValue != seekbarProgress) fragmentView.onSettingChanged()
|
||||||
|
sliderSetting.setSelectedValue(settings, seekbarProgress)
|
||||||
|
closeDialog()
|
||||||
|
}
|
||||||
|
is FloatSliderSetting -> {
|
||||||
|
val sliderSetting = clickedItem as FloatSliderSetting
|
||||||
|
|
||||||
|
if (sliderSetting.selectedValue != seekbarProgress) fragmentView.onSettingChanged()
|
||||||
|
|
||||||
|
sliderSetting.setSelectedValue(settings, seekbarProgress.toFloat())
|
||||||
|
|
||||||
|
closeDialog()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
clickedItem = null
|
||||||
|
seekbarProgress = -1
|
||||||
|
}
|
||||||
|
|
||||||
|
fun closeDialog() {
|
||||||
|
if (dialog != null) {
|
||||||
|
if (clickedPosition != -1) {
|
||||||
|
notifyItemChanged(clickedPosition)
|
||||||
|
clickedPosition = -1
|
||||||
|
}
|
||||||
|
dialog!!.dismiss()
|
||||||
|
dialog = null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onValueChange(slider: Slider, progress: Float, fromUser: Boolean) {
|
||||||
|
seekbarProgress = progress.toInt()
|
||||||
|
textSliderValue!!.text = seekbarProgress.toString()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getValueForSingleChoiceSelection(item: SingleChoiceSetting, which: Int): Int {
|
||||||
|
val valuesId = item.valuesId
|
||||||
|
|
||||||
|
return if (valuesId > 0) {
|
||||||
|
val valuesArray = context.resources.getIntArray(valuesId)
|
||||||
|
valuesArray[which]
|
||||||
|
} else {
|
||||||
|
which
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getSelectionForSingleChoiceValue(item: SingleChoiceSetting): Int {
|
||||||
|
val value = item.selectedValue
|
||||||
|
val valuesId = item.valuesId
|
||||||
|
|
||||||
|
if (valuesId > 0) {
|
||||||
|
val valuesArray = context.resources.getIntArray(valuesId)
|
||||||
|
for (index in valuesArray.indices) {
|
||||||
|
val current = valuesArray[index]
|
||||||
|
if (current == value) {
|
||||||
|
return index
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getValueForSingleChoiceDynamicDescriptionsSelection(
|
||||||
|
item: SingleChoiceSettingDynamicDescriptions,
|
||||||
|
which: Int
|
||||||
|
): Int {
|
||||||
|
val valuesId = item.valuesId
|
||||||
|
return if (valuesId > 0) {
|
||||||
|
val valuesArray = context.resources.getIntArray(valuesId)
|
||||||
|
valuesArray[which]
|
||||||
|
} else {
|
||||||
|
which
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getSelectionForSingleChoiceDynamicDescriptionsValue(
|
||||||
|
item: SingleChoiceSettingDynamicDescriptions
|
||||||
|
): Int {
|
||||||
|
val value = item.selectedValue
|
||||||
|
val valuesId = item.valuesId
|
||||||
|
if (valuesId > 0) {
|
||||||
|
val valuesArray = context.resources.getIntArray(valuesId)
|
||||||
|
for (index in valuesArray.indices) {
|
||||||
|
val current = valuesArray[index]
|
||||||
|
if (current == value) {
|
||||||
|
return index
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
fun clearLog() {
|
||||||
|
// Don't delete the log in case it is being monitored by another app.
|
||||||
|
val log = File(DirectoryInitialization.getUserDirectory() + "/Logs/dolphin.log")
|
||||||
|
try {
|
||||||
|
val raf = RandomAccessFile(log, "rw")
|
||||||
|
raf.setLength(0)
|
||||||
|
} catch (e: IOException) {
|
||||||
|
Log.error("[SettingsAdapter] Failed to clear log file: " + e.message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user