Android: App redesign with multi-theme system

This commit is contained in:
Charles Lombardo
2022-08-31 18:01:15 -04:00
parent 9195e1a9b8
commit 940a38f172
91 changed files with 1707 additions and 1004 deletions
@@ -76,19 +76,19 @@
android:name=".features.settings.ui.SettingsActivity"
android:exported="false"
android:configChanges="orientation|screenSize"
android:theme="@style/Theme.Dolphin.Settings"
android:theme="@style/Theme.Dolphin.Main"
android:label="@string/settings"/>
<activity
android:name=".features.cheats.ui.CheatsActivity"
android:exported="false"
android:theme="@style/Theme.Dolphin.Settings"
android:theme="@style/Theme.Dolphin.Main"
android:label="@string/cheats"/>
<activity
android:name=".activities.EmulationActivity"
android:exported="false"
android:theme="@style/Theme.Dolphin.Main.Emulation"
android:theme="@style/Theme.Dolphin.Main"
android:preferMinimalPostProcessing="true"/>
<activity
@@ -125,7 +125,7 @@
android:name=".activities.UserDataActivity"
android:exported="false"
android:label="@string/user_data_submenu"
android:theme="@style/Theme.Dolphin.Settings" />
android:theme="@style/Theme.Dolphin.Main" />
<activity
android:name=".features.riivolution.ui.RiivolutionBootActivity"
@@ -10,6 +10,7 @@ import androidx.appcompat.app.AppCompatActivity;
import org.dolphinemu.dolphinemu.R;
import org.dolphinemu.dolphinemu.fragments.ConvertFragment;
import org.dolphinemu.dolphinemu.utils.ThemeHelper;
public class ConvertActivity extends AppCompatActivity
{
@@ -25,6 +26,8 @@ public class ConvertActivity extends AppCompatActivity
@Override
protected void onCreate(Bundle savedInstanceState)
{
ThemeHelper.setTheme(this);
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_convert);
@@ -14,12 +14,15 @@ import android.widget.Button;
import android.widget.TextView;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.Toolbar;
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
import org.dolphinemu.dolphinemu.R;
import org.dolphinemu.dolphinemu.utils.DirectoryInitialization;
import org.dolphinemu.dolphinemu.utils.Log;
import org.dolphinemu.dolphinemu.utils.ThemeHelper;
import org.dolphinemu.dolphinemu.utils.ThreadUtil;
import java.io.File;
@@ -50,6 +53,8 @@ public class UserDataActivity extends AppCompatActivity
@Override
protected void onCreate(Bundle savedInstanceState)
{
ThemeHelper.setTheme(this);
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_user_data);
@@ -80,7 +85,8 @@ public class UserDataActivity extends AppCompatActivity
buttonExportUserData.setOnClickListener(view -> exportUserData());
// show up button
Toolbar tb = findViewById(R.id.toolbar_user_data);
setSupportActionBar(tb);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
}
@@ -100,26 +106,25 @@ public class UserDataActivity extends AppCompatActivity
{
Uri uri = data.getData();
AlertDialog.Builder builder = new AlertDialog.Builder(this);
new MaterialAlertDialogBuilder(this)
.setMessage(R.string.user_data_import_warning)
.setNegativeButton(R.string.no, (dialog, i) -> dialog.dismiss())
.setPositiveButton(R.string.yes, (dialog, i) ->
{
dialog.dismiss();
builder.setMessage(R.string.user_data_import_warning);
builder.setNegativeButton(R.string.no, (dialog, i) -> dialog.dismiss());
builder.setPositiveButton(R.string.yes, (dialog, i) ->
{
dialog.dismiss();
ThreadUtil.runOnThreadAndShowResult(this, R.string.import_in_progress,
R.string.do_not_close_app, () -> getResources().getString(importUserData(uri)),
(dialogInterface) ->
{
if (sMustRestartApp)
{
System.exit(0);
}
});
});
builder.show();
ThreadUtil.runOnThreadAndShowResult(this, R.string.import_in_progress,
R.string.do_not_close_app,
() -> getResources().getString(importUserData(uri)),
(dialogInterface) ->
{
if (sMustRestartApp)
{
System.exit(0);
}
});
})
.show();
}
else if (requestCode == REQUEST_CODE_EXPORT && resultCode == Activity.RESULT_OK)
{
@@ -148,7 +153,7 @@ public class UserDataActivity extends AppCompatActivity
{
// Activity not found. Perhaps it was removed by the OEM, or by some new Android version
// that didn't exist at the time of writing. Not much we can do other than tell the user
new AlertDialog.Builder(this)
new MaterialAlertDialogBuilder(this)
.setMessage(R.string.user_data_open_system_file_manager_failed)
.setPositiveButton(R.string.ok, null)
.show();
@@ -62,7 +62,8 @@ public class PlatformPagerAdapter extends FragmentPagerAdapter
// Apparently a workaround for TabLayout not supporting icons.
// TODO: This workaround will eventually not be necessary; switch to more legit methods when that is the case
// TODO: Also remove additional hax from styles.xml
Drawable drawable = mContext.getResources().getDrawable(TAB_ICONS[position]);
Drawable drawable =
mContext.getResources().getDrawable(TAB_ICONS[position], mContext.getTheme());
drawable.setBounds(0, 0, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight());
ImageSpan imageSpan = new ImageSpan(drawable, ImageSpan.ALIGN_BOTTOM);
@@ -6,9 +6,10 @@ import android.app.Dialog;
import android.os.Bundle;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AlertDialog;
import androidx.fragment.app.DialogFragment;
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
import org.dolphinemu.dolphinemu.NativeLibrary;
import org.dolphinemu.dolphinemu.R;
import org.dolphinemu.dolphinemu.activities.EmulationActivity;
@@ -47,7 +48,7 @@ public final class AlertMessage extends DialogFragment
boolean isWarning = requireArguments().getBoolean(ARG_IS_WARNING);
setCancelable(false);
AlertDialog.Builder builder = new AlertDialog.Builder(emulationActivity)
MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(emulationActivity)
.setTitle(title)
.setMessage(message);
@@ -9,9 +9,10 @@ import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
import androidx.appcompat.app.AlertDialog;
import androidx.fragment.app.DialogFragment;
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
import org.dolphinemu.dolphinemu.NativeLibrary;
import org.dolphinemu.dolphinemu.R;
import org.dolphinemu.dolphinemu.model.GameFile;
@@ -38,7 +39,6 @@ public final class GameDetailsDialog extends DialogFragment
{
GameFile gameFile = GameFileCacheManager.addOrGet(getArguments().getString(ARG_GAME_PATH));
AlertDialog.Builder builder = new AlertDialog.Builder(requireActivity());
ViewGroup contents = (ViewGroup) getActivity().getLayoutInflater()
.inflate(R.layout.dialog_game_details, null);
@@ -116,7 +116,8 @@ public final class GameDetailsDialog extends DialogFragment
PicassoUtils.loadGameBanner(banner, gameFile);
builder.setView(contents);
return builder.create();
return new MaterialAlertDialogBuilder(requireActivity())
.setView(contents)
.create();
}
}
@@ -8,9 +8,10 @@ import android.os.Bundle;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AlertDialog;
import androidx.fragment.app.DialogFragment;
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
import org.dolphinemu.dolphinemu.DolphinApplication;
import org.dolphinemu.dolphinemu.R;
import org.dolphinemu.dolphinemu.activities.ConvertActivity;
@@ -117,16 +118,16 @@ public class GamePropertiesDialog extends DialogFragment
itemsBuilder.add(R.string.properties_clear_game_settings, (dialog, i) ->
clearGameSettingsWithConfirmation(gameId));
AlertDialog.Builder builder = new AlertDialog.Builder(requireContext());
MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(requireContext())
.setTitle(requireContext()
.getString(R.string.preferences_game_properties_with_game_id, gameId));
itemsBuilder.applyToBuilder(builder);
builder.setTitle(requireContext()
.getString(R.string.preferences_game_properties_with_game_id, gameId));
return builder.create();
}
private void clearGameSettingsWithConfirmation(String gameId)
{
new AlertDialog.Builder(requireContext())
new MaterialAlertDialogBuilder(requireContext())
.setTitle(R.string.properties_clear_game_settings)
.setMessage(R.string.properties_clear_game_settings_confirmation)
.setPositiveButton(R.string.yes, (dialog, i) -> clearGameSettings(gameId))
@@ -3,13 +3,29 @@
package org.dolphinemu.dolphinemu.dialogs;
import android.content.Context;
import android.content.res.ColorStateList;
import android.content.res.Resources;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.util.TypedValue;
import android.view.InputDevice;
import android.view.KeyEvent;
import android.view.MotionEvent;
import androidx.annotation.AttrRes;
import androidx.annotation.NonNull;
import androidx.annotation.StyleRes;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.view.ContextThemeWrapper;
import androidx.core.view.ViewCompat;
import com.google.android.material.color.MaterialColors;
import com.google.android.material.dialog.MaterialDialogs;
import com.google.android.material.resources.MaterialAttributes;
import com.google.android.material.shape.MaterialShapeDrawable;
import org.dolphinemu.dolphinemu.R;
import org.dolphinemu.dolphinemu.features.settings.model.view.InputBindingSetting;
import org.dolphinemu.dolphinemu.features.settings.ui.SettingsAdapter;
import org.dolphinemu.dolphinemu.utils.ControllerMappingHelper;
@@ -18,12 +34,20 @@ import org.dolphinemu.dolphinemu.utils.Log;
import java.util.ArrayList;
import java.util.List;
import static com.google.android.material.theme.overlay.MaterialThemeOverlay.wrap;
/**
* {@link AlertDialog} derivative that listens for
* motion events from controllers and joysticks.
*/
public final class MotionAlertDialog extends AlertDialog
{
@AttrRes private static final int DEF_STYLE_ATTR = R.attr.alertDialogStyle;
@StyleRes private static final int DEF_STYLE_RES = R.style.MaterialAlertDialog_MaterialComponents;
@AttrRes
private static final int MATERIAL_ALERT_DIALOG_THEME_OVERLAY = R.attr.materialAlertDialogTheme;
// The selected input preference
private final InputBindingSetting setting;
private final ArrayList<Float> mPreviousValues = new ArrayList<>();
@@ -39,10 +63,63 @@ public final class MotionAlertDialog extends AlertDialog
*/
public MotionAlertDialog(Context context, InputBindingSetting setting, SettingsAdapter adapter)
{
super(context);
super(createMaterialAlertDialogThemedContext(context));
this.setting = setting;
mAdapter = adapter;
// Using code from MaterialAlertDialogBuilder allows us to nearly perfectly recreate its look
context = getContext();
Resources.Theme theme = context.getTheme();
int surfaceColor =
MaterialColors.getColor(context, R.attr.colorSurface, getClass().getCanonicalName());
MaterialShapeDrawable materialShapeDrawable =
new MaterialShapeDrawable(context, null, R.attr.alertDialogStyle,
R.style.MaterialAlertDialog_MaterialComponents);
materialShapeDrawable.initializeElevationOverlay(context);
materialShapeDrawable.setFillColor(ColorStateList.valueOf(surfaceColor));
materialShapeDrawable.setElevation(ViewCompat.getElevation(this.getWindow().getDecorView()));
this.getWindow().setBackgroundDrawable(materialShapeDrawable);
Rect backgroundInsets =
MaterialDialogs.getDialogBackgroundInsets(context, R.attr.alertDialogStyle,
R.style.MaterialAlertDialog_MaterialComponents);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P)
{
TypedValue dialogCornerRadiusValue = new TypedValue();
theme.resolveAttribute(android.R.attr.dialogCornerRadius, dialogCornerRadiusValue, true);
float dialogCornerRadius =
dialogCornerRadiusValue.getDimension(context.getResources().getDisplayMetrics());
if (dialogCornerRadiusValue.type == TypedValue.TYPE_DIMENSION && dialogCornerRadius >= 0)
{
materialShapeDrawable.setCornerSize(dialogCornerRadius);
}
}
Drawable insetDrawable = MaterialDialogs.insetDrawable(materialShapeDrawable, backgroundInsets);
this.getWindow().setBackgroundDrawable(insetDrawable);
}
private static Context createMaterialAlertDialogThemedContext(@NonNull Context context)
{
int themeOverlayId = getMaterialAlertDialogThemeOverlay(context);
Context themedContext = wrap(context, null, DEF_STYLE_ATTR, DEF_STYLE_RES);
if (themeOverlayId == 0)
{
return themedContext;
}
return new ContextThemeWrapper(themedContext, themeOverlayId);
}
private static int getMaterialAlertDialogThemeOverlay(@NonNull Context context)
{
TypedValue materialAlertDialogThemeOverlay =
MaterialAttributes.resolve(context, MATERIAL_ALERT_DIALOG_THEME_OVERLAY);
if (materialAlertDialogThemeOverlay == null)
{
return 0;
}
return materialAlertDialogThemeOverlay.data;
}
public boolean onKeyEvent(int keyCode, KeyEvent event)
@@ -7,16 +7,17 @@ import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ScrollView;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AlertDialog;
import androidx.fragment.app.Fragment;
import androidx.lifecycle.ViewModelProvider;
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
import com.google.android.material.textfield.TextInputEditText;
import com.google.android.material.textfield.TextInputLayout;
import org.dolphinemu.dolphinemu.R;
import org.dolphinemu.dolphinemu.features.cheats.model.Cheat;
import org.dolphinemu.dolphinemu.features.cheats.model.CheatsViewModel;
@@ -25,14 +26,14 @@ public class CheatDetailsFragment extends Fragment
{
private View mRoot;
private ScrollView mScrollView;
private TextView mLabelName;
private EditText mEditName;
private TextView mLabelCreator;
private EditText mEditCreator;
private TextView mLabelNotes;
private EditText mEditNotes;
private TextView mLabelCode;
private EditText mEditCode;
private TextInputLayout mEditNameLayout;
private TextInputEditText mEditName;
private TextInputLayout mEditCreatorLayout;
private TextInputEditText mEditCreator;
private TextInputLayout mEditNotesLayout;
private TextInputEditText mEditNotes;
private TextInputLayout mEditCodeLayout;
private TextInputEditText mEditCode;
private Button mButtonDelete;
private Button mButtonEdit;
private Button mButtonCancel;
@@ -54,14 +55,14 @@ public class CheatDetailsFragment extends Fragment
{
mRoot = view.findViewById(R.id.root);
mScrollView = view.findViewById(R.id.scroll_view);
mLabelName = view.findViewById(R.id.label_name);
mEditName = view.findViewById(R.id.edit_name);
mLabelCreator = view.findViewById(R.id.label_creator);
mEditCreator = view.findViewById(R.id.edit_creator);
mLabelNotes = view.findViewById(R.id.label_notes);
mEditNotes = view.findViewById(R.id.edit_notes);
mLabelCode = view.findViewById(R.id.label_code);
mEditCode = view.findViewById(R.id.edit_code);
mEditNameLayout = view.findViewById(R.id.edit_name);
mEditName = view.findViewById(R.id.edit_name_input);
mEditCreatorLayout = view.findViewById(R.id.edit_creator);
mEditCreator = view.findViewById(R.id.edit_creator_input);
mEditNotesLayout = view.findViewById(R.id.edit_notes);
mEditNotes = view.findViewById(R.id.edit_notes_input);
mEditCodeLayout = view.findViewById(R.id.edit_code);
mEditCode = view.findViewById(R.id.edit_code_input);
mButtonDelete = view.findViewById(R.id.button_delete);
mButtonEdit = view.findViewById(R.id.button_edit);
mButtonCancel = view.findViewById(R.id.button_cancel);
@@ -84,18 +85,17 @@ public class CheatDetailsFragment extends Fragment
private void clearEditErrors()
{
mEditName.setError(null);
mEditCode.setError(null);
mEditNameLayout.setError(null);
mEditCodeLayout.setError(null);
}
private void onDeleteClicked(View view)
{
AlertDialog.Builder builder =
new AlertDialog.Builder(requireContext());
builder.setMessage(getString(R.string.cheats_delete_confirmation, mCheat.getName()));
builder.setPositiveButton(R.string.yes, (dialog, i) -> mViewModel.deleteSelectedCheat());
builder.setNegativeButton(R.string.no, null);
builder.show();
new MaterialAlertDialogBuilder(requireContext())
.setMessage(getString(R.string.cheats_delete_confirmation, mCheat.getName()))
.setPositiveButton(R.string.yes, (dialog, i) -> mViewModel.deleteSelectedCheat())
.setNegativeButton(R.string.no, null)
.show();
}
private void onEditClicked(View view)
@@ -134,19 +134,19 @@ public class CheatDetailsFragment extends Fragment
mButtonEdit.requestFocus();
break;
case Cheat.TRY_SET_FAIL_NO_NAME:
mEditName.setError(getString(R.string.cheats_error_no_name));
mScrollView.smoothScrollTo(0, mLabelName.getTop());
mEditNameLayout.setError(getString(R.string.cheats_error_no_name));
mScrollView.smoothScrollTo(0, mEditName.getTop());
break;
case Cheat.TRY_SET_FAIL_NO_CODE_LINES:
mEditCode.setError(getString(R.string.cheats_error_no_code_lines));
mEditCodeLayout.setError(getString(R.string.cheats_error_no_code_lines));
mScrollView.smoothScrollTo(0, mEditCode.getBottom());
break;
case Cheat.TRY_SET_FAIL_CODE_MIXED_ENCRYPTION:
mEditCode.setError(getString(R.string.cheats_error_mixed_encryption));
mEditCodeLayout.setError(getString(R.string.cheats_error_mixed_encryption));
mScrollView.smoothScrollTo(0, mEditCode.getBottom());
break;
default:
mEditCode.setError(getString(R.string.cheats_error_on_line, result));
mEditCodeLayout.setError(getString(R.string.cheats_error_on_line, result));
mScrollView.smoothScrollTo(0, mEditCode.getBottom());
break;
}
@@ -161,12 +161,9 @@ public class CheatDetailsFragment extends Fragment
int creatorVisibility = cheat != null && cheat.supportsCreator() ? View.VISIBLE : View.GONE;
int notesVisibility = cheat != null && cheat.supportsNotes() ? View.VISIBLE : View.GONE;
int codeVisibility = cheat != null && cheat.supportsCode() ? View.VISIBLE : View.GONE;
mLabelCreator.setVisibility(creatorVisibility);
mEditCreator.setVisibility(creatorVisibility);
mLabelNotes.setVisibility(notesVisibility);
mEditNotes.setVisibility(notesVisibility);
mLabelCode.setVisibility(codeVisibility);
mEditCode.setVisibility(codeVisibility);
mEditCreatorLayout.setVisibility(creatorVisibility);
mEditNotesLayout.setVisibility(notesVisibility);
mEditCodeLayout.setVisibility(codeVisibility);
boolean userDefined = cheat != null && cheat.getUserDefined();
mButtonDelete.setEnabled(userDefined);
@@ -13,10 +13,13 @@ import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.Toolbar;
import androidx.core.view.ViewCompat;
import androidx.lifecycle.ViewModelProvider;
import androidx.slidingpanelayout.widget.SlidingPaneLayout;
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
import org.dolphinemu.dolphinemu.R;
import org.dolphinemu.dolphinemu.features.cheats.model.Cheat;
import org.dolphinemu.dolphinemu.features.cheats.model.CheatsViewModel;
@@ -24,6 +27,7 @@ import org.dolphinemu.dolphinemu.features.cheats.model.GeckoCheat;
import org.dolphinemu.dolphinemu.features.settings.model.Settings;
import org.dolphinemu.dolphinemu.ui.TwoPaneOnBackPressedCallback;
import org.dolphinemu.dolphinemu.ui.main.MainPresenter;
import org.dolphinemu.dolphinemu.utils.ThemeHelper;
public class CheatsActivity extends AppCompatActivity
implements SlidingPaneLayout.PanelSlideListener
@@ -60,6 +64,8 @@ public class CheatsActivity extends AppCompatActivity
@Override
protected void onCreate(Bundle savedInstanceState)
{
ThemeHelper.setTheme(this);
super.onCreate(savedInstanceState);
MainPresenter.skipRescanningLibrary();
@@ -94,7 +100,8 @@ public class CheatsActivity extends AppCompatActivity
mViewModel.getOpenDetailsViewEvent().observe(this, this::openDetailsView);
// show up button
Toolbar tb = findViewById(R.id.toolbar_cheats);
setSupportActionBar(tb);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
}
@@ -191,10 +198,10 @@ public class CheatsActivity extends AppCompatActivity
public void downloadGeckoCodes()
{
AlertDialog progressDialog = new AlertDialog.Builder(this).create();
progressDialog.setTitle(R.string.cheats_downloading);
progressDialog.setCancelable(false);
progressDialog.show();
AlertDialog progressDialog = new MaterialAlertDialogBuilder(this)
.setMessage(R.string.cheats_downloading)
.setCancelable(false)
.show();
new Thread(() ->
{
@@ -206,14 +213,14 @@ public class CheatsActivity extends AppCompatActivity
if (codes == null)
{
new AlertDialog.Builder(this)
new MaterialAlertDialogBuilder(this)
.setMessage(getString(R.string.cheats_download_failed))
.setPositiveButton(R.string.ok, null)
.show();
}
else if (codes.length == 0)
{
new AlertDialog.Builder(this)
new MaterialAlertDialogBuilder(this)
.setMessage(getString(R.string.cheats_download_empty))
.setPositiveButton(R.string.ok, null)
.show();
@@ -223,7 +230,7 @@ public class CheatsActivity extends AppCompatActivity
int cheatsAdded = mViewModel.addDownloadedGeckoCodes(codes);
String message = getString(R.string.cheats_download_succeeded, codes.length, cheatsAdded);
new AlertDialog.Builder(this)
new MaterialAlertDialogBuilder(this)
.setMessage(message)
.setPositiveButton(R.string.ok, null)
.show();
@@ -17,6 +17,7 @@ import org.dolphinemu.dolphinemu.activities.EmulationActivity;
import org.dolphinemu.dolphinemu.features.riivolution.model.RiivolutionPatches;
import org.dolphinemu.dolphinemu.features.settings.model.StringSetting;
import org.dolphinemu.dolphinemu.utils.DirectoryInitialization;
import org.dolphinemu.dolphinemu.utils.ThemeHelper;
public class RiivolutionBootActivity extends AppCompatActivity
{
@@ -41,6 +42,8 @@ public class RiivolutionBootActivity extends AppCompatActivity
@Override
protected void onCreate(Bundle savedInstanceState)
{
ThemeHelper.setTheme(this);
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_riivolution_boot);
@@ -33,6 +33,7 @@ public enum IntSetting implements AbstractIntSetting
MAIN_CONTROL_OPACITY(Settings.FILE_DOLPHIN, Settings.SECTION_INI_ANDROID, "ControlOpacity", 65),
MAIN_EMULATION_ORIENTATION(Settings.FILE_DOLPHIN, Settings.SECTION_INI_ANDROID,
"EmulationOrientation", ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE),
MAIN_INTERFACE_THEME(Settings.FILE_DOLPHIN, Settings.SECTION_INI_ANDROID, "InterfaceTheme", 0),
MAIN_LAST_PLATFORM_TAB(Settings.FILE_DOLPHIN, Settings.SECTION_INI_ANDROID, "LastPlatformTab", 0),
MAIN_MOTION_CONTROLS(Settings.FILE_DOLPHIN, Settings.SECTION_INI_ANDROID, "MotionControls", 1),
MAIN_IR_MODE(Settings.FILE_DOLPHIN, Settings.SECTION_INI_ANDROID, "IRMode",
@@ -2,7 +2,6 @@
package org.dolphinemu.dolphinemu.features.settings.ui;
import android.app.ProgressDialog;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
@@ -15,13 +14,18 @@ import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.Toolbar;
import androidx.fragment.app.FragmentTransaction;
import androidx.lifecycle.ViewModelProvider;
import com.google.android.material.appbar.CollapsingToolbarLayout;
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
import org.dolphinemu.dolphinemu.NativeLibrary;
import org.dolphinemu.dolphinemu.R;
import org.dolphinemu.dolphinemu.ui.main.MainPresenter;
import org.dolphinemu.dolphinemu.utils.FileBrowserHelper;
import org.dolphinemu.dolphinemu.utils.ThemeHelper;
import java.util.Set;
@@ -34,7 +38,9 @@ public final class SettingsActivity extends AppCompatActivity implements Setting
private static final String FRAGMENT_TAG = "settings";
private SettingsActivityPresenter mPresenter;
private ProgressDialog dialog;
private AlertDialog dialog;
private CollapsingToolbarLayout mToolbarLayout;
public static void launch(Context context, MenuTag menuTag, String gameId, int revision,
boolean isWii)
@@ -58,6 +64,8 @@ public final class SettingsActivity extends AppCompatActivity implements Setting
@Override
protected void onCreate(Bundle savedInstanceState)
{
ThemeHelper.setTheme(this);
super.onCreate(savedInstanceState);
// If we came here from the game list, we don't want to rescan when returning to the game list.
@@ -80,7 +88,9 @@ public final class SettingsActivity extends AppCompatActivity implements Setting
mPresenter = new SettingsActivityPresenter(this, getSettings());
mPresenter.onCreate(savedInstanceState, menuTag, gameID, revision, isWii, this);
// show up button
Toolbar tb = findViewById(R.id.toolbar_settings);
mToolbarLayout = findViewById(R.id.toolbar_settings_layout);
setSupportActionBar(tb);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
}
@@ -211,11 +221,12 @@ public final class SettingsActivity extends AppCompatActivity implements Setting
{
if (dialog == null)
{
dialog = new ProgressDialog(this);
dialog.setMessage(getString(R.string.load_settings));
dialog.setIndeterminate(true);
dialog = new MaterialAlertDialogBuilder(this)
.setTitle(getString(R.string.load_settings))
.setView(getLayoutInflater().inflate(R.layout.dialog_indeterminate_progress, null,
false))
.create();
}
dialog.show();
}
@@ -228,7 +239,7 @@ public final class SettingsActivity extends AppCompatActivity implements Setting
@Override
public void showGameIniJunkDeletionQuestion()
{
new AlertDialog.Builder(this)
new MaterialAlertDialogBuilder(this)
.setTitle(getString(R.string.game_ini_junk_title))
.setMessage(getString(R.string.game_ini_junk_question))
.setPositiveButton(R.string.yes, (dialogInterface, i) -> mPresenter.clearSettings())
@@ -312,4 +323,9 @@ public final class SettingsActivity extends AppCompatActivity implements Setting
{
return (SettingsFragment) getSupportFragmentManager().findFragmentByTag(FRAGMENT_TAG);
}
public void setToolbarTitle(String title)
{
mToolbarLayout.setTitle(title);
}
}
@@ -108,4 +108,9 @@ public interface SettingsActivityView
* Tell the user that there is junk in the game INI and ask if they want to delete the whole file.
*/
void showGameIniJunkDeletionQuestion();
/**
* Accesses the material toolbar layout and changes the title
*/
void setToolbarTitle(String title);
}
@@ -10,13 +10,16 @@ import android.provider.DocumentsContract;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.EditText;
import android.widget.SeekBar;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AlertDialog;
import androidx.recyclerview.widget.RecyclerView;
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
import com.google.android.material.slider.Slider;
import com.google.android.material.textfield.TextInputEditText;
import org.dolphinemu.dolphinemu.R;
import org.dolphinemu.dolphinemu.dialogs.MotionAlertDialog;
import org.dolphinemu.dolphinemu.features.settings.model.Settings;
@@ -56,7 +59,7 @@ import java.io.RandomAccessFile;
import java.util.ArrayList;
public final class SettingsAdapter extends RecyclerView.Adapter<SettingViewHolder>
implements DialogInterface.OnClickListener, SeekBar.OnSeekBarChangeListener
implements DialogInterface.OnClickListener, Slider.OnChangeListener
{
private final SettingsFragmentView mView;
private final Context mContext;
@@ -64,7 +67,6 @@ public final class SettingsAdapter extends RecyclerView.Adapter<SettingViewHolde
private SettingsItem mClickedItem;
private int mClickedPosition;
private int mSeekbarMinValue;
private int mSeekbarProgress;
private AlertDialog mDialog;
@@ -204,28 +206,27 @@ public final class SettingsAdapter extends RecyclerView.Adapter<SettingViewHolde
{
LayoutInflater inflater = LayoutInflater.from(mContext);
AlertDialog.Builder builder = new AlertDialog.Builder(mContext, R.style.DolphinDialogBase);
View dialogView = inflater.inflate(R.layout.dialog_input_string, null);
EditText input = (EditText) dialogView.findViewById(R.id.input);
TextInputEditText input = dialogView.findViewById(R.id.input);
input.setText(item.getSelectedValue(getSettings()));
builder.setView(dialogView);
builder.setMessage(item.getDescription());
builder.setPositiveButton(R.string.ok, (dialogInterface, i) ->
{
String editTextInput = input.getText().toString();
mDialog = new MaterialAlertDialogBuilder(mView.getActivity())
.setView(dialogView)
.setMessage(item.getDescription())
.setPositiveButton(R.string.ok, (dialogInterface, i) ->
{
String editTextInput = input.getText().toString();
if (!item.getSelectedValue(mView.getSettings()).equals(editTextInput))
{
notifyItemChanged(position);
mView.onSettingChanged();
}
if (!item.getSelectedValue(mView.getSettings()).equals(editTextInput))
{
notifyItemChanged(position);
mView.onSettingChanged();
}
item.setSelectedValue(mView.getSettings(), editTextInput);
});
builder.setNegativeButton(R.string.cancel, null);
mDialog = builder.show();
item.setSelectedValue(mView.getSettings(), editTextInput);
})
.setNegativeButton(R.string.cancel, null)
.show();
}
public void onSingleChoiceClick(SingleChoiceSetting item, int position)
@@ -235,12 +236,10 @@ public final class SettingsAdapter extends RecyclerView.Adapter<SettingViewHolde
int value = getSelectionForSingleChoiceValue(item);
AlertDialog.Builder builder = new AlertDialog.Builder(mView.getActivity());
builder.setTitle(item.getName());
builder.setSingleChoiceItems(item.getChoicesId(), value, this);
mDialog = builder.show();
mDialog = new MaterialAlertDialogBuilder(mView.getActivity())
.setTitle(item.getName())
.setSingleChoiceItems(item.getChoicesId(), value, this)
.show();
}
public void onStringSingleChoiceClick(StringSingleChoiceSetting item, int position)
@@ -248,13 +247,11 @@ public final class SettingsAdapter extends RecyclerView.Adapter<SettingViewHolde
mClickedItem = item;
mClickedPosition = position;
AlertDialog.Builder builder = new AlertDialog.Builder(mView.getActivity());
builder.setTitle(item.getName());
builder.setSingleChoiceItems(item.getChoices(), item.getSelectedValueIndex(getSettings()),
this);
mDialog = builder.show();
mDialog = new MaterialAlertDialogBuilder(mView.getActivity())
.setTitle(item.getName())
.setSingleChoiceItems(item.getChoices(), item.getSelectedValueIndex(getSettings()),
this)
.show();
}
public void onSingleChoiceDynamicDescriptionsClick(SingleChoiceSettingDynamicDescriptions item,
@@ -265,29 +262,26 @@ public final class SettingsAdapter extends RecyclerView.Adapter<SettingViewHolde
int value = getSelectionForSingleChoiceDynamicDescriptionsValue(item);
AlertDialog.Builder builder = new AlertDialog.Builder(mView.getActivity());
builder.setTitle(item.getName());
builder.setSingleChoiceItems(item.getChoicesId(), value, this);
mDialog = builder.show();
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;
mSeekbarMinValue = item.getMin();
mSeekbarProgress = item.getSelectedValue(getSettings());
AlertDialog.Builder builder = new AlertDialog.Builder(mView.getActivity());
LayoutInflater inflater = LayoutInflater.from(mView.getActivity());
View view = inflater.inflate(R.layout.dialog_seekbar, null);
View view = inflater.inflate(R.layout.dialog_slider, null);
builder.setTitle(item.getName());
builder.setView(view);
builder.setPositiveButton(R.string.ok, this);
mDialog = builder.show();
mDialog = new MaterialAlertDialogBuilder(mView.getActivity())
.setTitle(item.getName())
.setView(view)
.setPositiveButton(R.string.ok, this)
.show();
mTextSliderValue = view.findViewById(R.id.text_value);
mTextSliderValue.setText(String.valueOf(mSeekbarProgress));
@@ -295,16 +289,12 @@ public final class SettingsAdapter extends RecyclerView.Adapter<SettingViewHolde
TextView units = view.findViewById(R.id.text_units);
units.setText(item.getUnits());
SeekBar seekbar = view.findViewById(R.id.seekbar);
// TODO: Once we require API 26, uncomment this line and remove the mSeekbarMinValue variable
//seekbar.setMin(item.getMin());
seekbar.setMax(item.getMax() - mSeekbarMinValue);
seekbar.setProgress(mSeekbarProgress - mSeekbarMinValue);
seekbar.setKeyProgressIncrement(5);
seekbar.setOnSeekBarChangeListener(this);
Slider slider = view.findViewById(R.id.slider);
slider.setValueFrom(item.getMin());
slider.setValueTo(item.getMax());
slider.setValue(mSeekbarProgress);
slider.setStepSize(1);
slider.addOnChangeListener(this);
}
public void onSubmenuClick(SubmenuSetting item)
@@ -339,10 +329,10 @@ public final class SettingsAdapter extends RecyclerView.Adapter<SettingViewHolde
if (!PermissionsHandler.isExternalStorageLegacy())
{
AlertDialog.Builder builder = new AlertDialog.Builder(mContext);
builder.setMessage(R.string.path_not_changeable_scoped_storage);
builder.setPositiveButton(R.string.ok, (dialog, i) -> dialog.dismiss());
builder.show();
new MaterialAlertDialogBuilder(mContext)
.setMessage(R.string.path_not_changeable_scoped_storage)
.setPositiveButton(R.string.ok, (dialog, i) -> dialog.dismiss())
.show();
}
else
{
@@ -509,22 +499,12 @@ public final class SettingsAdapter extends RecyclerView.Adapter<SettingViewHolde
}
@Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser)
public void onValueChange(@NonNull Slider slider, float progress, boolean fromUser)
{
mSeekbarProgress = progress + mSeekbarMinValue;
mSeekbarProgress = (int) progress;
mTextSliderValue.setText(String.valueOf(mSeekbarProgress));
}
@Override
public void onStartTrackingTouch(SeekBar seekBar)
{
}
@Override
public void onStopTrackingTouch(SeekBar seekBar)
{
}
private int getValueForSingleChoiceSelection(SingleChoiceSetting item, int which)
{
int valuesId = item.getValuesId();
@@ -127,7 +127,7 @@ public final class SettingsFragment extends Fragment implements SettingsFragment
if (titles.containsKey(menuTag))
{
getActivity().setTitle(titles.get(menuTag));
mActivity.setToolbarTitle(getString(titles.get(menuTag)));
}
LinearLayoutManager manager = new LinearLayoutManager(getActivity());
@@ -43,6 +43,7 @@ import org.dolphinemu.dolphinemu.features.settings.utils.SettingsFile;
import org.dolphinemu.dolphinemu.ui.main.MainPresenter;
import org.dolphinemu.dolphinemu.utils.BooleanSupplier;
import org.dolphinemu.dolphinemu.utils.EGLHelper;
import org.dolphinemu.dolphinemu.utils.ThemeHelper;
import org.dolphinemu.dolphinemu.utils.ThreadUtil;
import org.dolphinemu.dolphinemu.utils.WiiUtils;
@@ -323,6 +324,50 @@ public final class SettingsFragmentPresenter
R.string.download_game_covers, 0));
sl.add(new CheckBoxSetting(mContext, BooleanSetting.MAIN_SHOW_GAME_TITLES,
R.string.show_titles_in_game_list, R.string.show_titles_in_game_list_description));
AbstractIntSetting appTheme = new AbstractIntSetting()
{
@Override public boolean isOverridden(Settings settings)
{
return IntSetting.MAIN_INTERFACE_THEME.isOverridden(settings);
}
@Override public boolean isRuntimeEditable()
{
// This only affects app UI
return true;
}
@Override public boolean delete(Settings settings)
{
ThemeHelper.deleteThemeKey(mView.getActivity());
return IntSetting.MAIN_INTERFACE_THEME.delete(settings);
}
@Override public int getInt(Settings settings)
{
return IntSetting.MAIN_INTERFACE_THEME.getInt(settings);
}
@Override public void setInt(Settings settings, int newValue)
{
ThemeHelper.saveTheme(mView.getActivity(), newValue);
IntSetting.MAIN_INTERFACE_THEME.setInt(settings, newValue);
mView.getActivity().recreate();
}
};
// If a Monet theme is run on a device below API 31, the app will crash
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S)
{
sl.add(new SingleChoiceSetting(mContext, appTheme, R.string.change_theme, 0,
R.array.themeEntriesA12, R.array.themeValuesA12));
}
else
{
sl.add(new SingleChoiceSetting(mContext, appTheme, R.string.change_theme, 0,
R.array.themeEntries, R.array.themeValues));
}
}
private void addAudioSettings(ArrayList<SettingsItem> sl)
@@ -6,7 +6,7 @@ import android.content.Context;
import android.text.method.LinkMovementMethod;
import android.view.View;
import androidx.core.content.ContextCompat;
import com.google.android.material.color.MaterialColors;
import org.dolphinemu.dolphinemu.R;
import org.dolphinemu.dolphinemu.features.settings.model.view.SettingsItem;
@@ -27,7 +27,8 @@ public final class HeaderHyperLinkViewHolder extends HeaderViewHolder
public void bind(SettingsItem item)
{
super.bind(item);
mHeaderName.setMovementMethod(LinkMovementMethod.getInstance());
mHeaderName.setLinkTextColor(ContextCompat.getColor(mContext, R.color.dolphin_blue_secondary));
mHeaderName.setLinkTextColor(MaterialColors.getColor(itemView, R.attr.colorTertiary));
}
}
@@ -8,7 +8,8 @@ import android.widget.TextView;
import android.widget.Toast;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AlertDialog;
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
import org.dolphinemu.dolphinemu.R;
import org.dolphinemu.dolphinemu.features.settings.model.view.RunRunnable;
@@ -54,19 +55,16 @@ public final class RunRunnableViewHolder extends SettingViewHolder
if (alertTextID > 0)
{
AlertDialog.Builder builder = new AlertDialog.Builder(mContext)
new MaterialAlertDialogBuilder(mContext)
.setTitle(mItem.getName())
.setMessage(alertTextID);
builder
.setMessage(alertTextID)
.setPositiveButton(R.string.ok, (dialog, whichButton) ->
{
runRunnable();
dialog.dismiss();
})
.setNegativeButton(R.string.cancel, (dialog, whichButton) -> dialog.dismiss());
builder.show();
.setNegativeButton(R.string.cancel, (dialog, whichButton) -> dialog.dismiss())
.show();
}
else
{

Some files were not shown because too many files have changed in this diff Show More