mirror of
https://github.com/dolphin-emu/dolphin.git
synced 2025-02-10 14:39:01 +01:00
Merge pull request #11886 from t895/kotlin-ui
Android: Convert "ui" package to Kotlin
This commit is contained in:
commit
36ca747d55
@ -1,27 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013 Dolphin Emulator Project
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
package org.dolphinemu.dolphinemu.ui;
|
||||
|
||||
import android.content.Context;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.View;
|
||||
|
||||
/**
|
||||
* Work around a bug with the nVidia Shield.
|
||||
*
|
||||
* Without this View, the emulation SurfaceView acts like it has the
|
||||
* highest Z-value, blocking any other View, such as the menu fragments.
|
||||
*/
|
||||
public final class NVidiaShieldWorkaroundView extends View
|
||||
{
|
||||
public NVidiaShieldWorkaroundView(Context context, AttributeSet attrs)
|
||||
{
|
||||
super(context, attrs);
|
||||
|
||||
// Setting this seems to workaround the bug
|
||||
setWillNotDraw(false);
|
||||
}
|
||||
}
|
@ -0,0 +1,21 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
package org.dolphinemu.dolphinemu.ui
|
||||
|
||||
import android.content.Context
|
||||
import android.util.AttributeSet
|
||||
import android.view.View
|
||||
|
||||
/**
|
||||
* Work around a bug with the nVidia Shield.
|
||||
*
|
||||
*
|
||||
* Without this View, the emulation SurfaceView acts like it has the
|
||||
* highest Z-value, blocking any other View, such as the menu fragments.
|
||||
*/
|
||||
class NVidiaShieldWorkaroundView(context: Context?, attrs: AttributeSet?) : View(context, attrs) {
|
||||
init {
|
||||
// Setting this seems to workaround the bug
|
||||
setWillNotDraw(false)
|
||||
}
|
||||
}
|
@ -1,85 +0,0 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
package org.dolphinemu.dolphinemu.ui.main;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.leanback.widget.TitleViewAdapter;
|
||||
|
||||
import org.dolphinemu.dolphinemu.R;
|
||||
|
||||
public class CustomTitleView extends LinearLayout implements TitleViewAdapter.Provider
|
||||
{
|
||||
private final TextView mTitleView;
|
||||
private final View mBadgeView;
|
||||
|
||||
private final TitleViewAdapter mTitleViewAdapter = new TitleViewAdapter()
|
||||
{
|
||||
@Override
|
||||
public View getSearchAffordanceView()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setTitle(CharSequence titleText)
|
||||
{
|
||||
CustomTitleView.this.setTitle(titleText);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBadgeDrawable(Drawable drawable)
|
||||
{
|
||||
CustomTitleView.this.setBadgeDrawable(drawable);
|
||||
}
|
||||
};
|
||||
|
||||
public CustomTitleView(Context context)
|
||||
{
|
||||
this(context, null);
|
||||
}
|
||||
|
||||
public CustomTitleView(Context context, AttributeSet attrs)
|
||||
{
|
||||
this(context, attrs, 0);
|
||||
}
|
||||
|
||||
public CustomTitleView(Context context, AttributeSet attrs, int defStyle)
|
||||
{
|
||||
super(context, attrs, defStyle);
|
||||
View root = LayoutInflater.from(context).inflate(R.layout.tv_title, this);
|
||||
mTitleView = root.findViewById(R.id.title);
|
||||
mBadgeView = root.findViewById(R.id.badge);
|
||||
}
|
||||
|
||||
public void setTitle(CharSequence title)
|
||||
{
|
||||
if (title != null)
|
||||
{
|
||||
mTitleView.setText(title);
|
||||
mTitleView.setVisibility(View.VISIBLE);
|
||||
mBadgeView.setVisibility(View.VISIBLE);
|
||||
}
|
||||
}
|
||||
|
||||
public void setBadgeDrawable(Drawable drawable)
|
||||
{
|
||||
if (drawable != null)
|
||||
{
|
||||
mTitleView.setVisibility(View.GONE);
|
||||
mBadgeView.setVisibility(View.VISIBLE);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public TitleViewAdapter getTitleViewAdapter()
|
||||
{
|
||||
return mTitleViewAdapter;
|
||||
}
|
||||
}
|
@ -0,0 +1,53 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
package org.dolphinemu.dolphinemu.ui.main
|
||||
|
||||
import android.content.Context
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.util.AttributeSet
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.widget.LinearLayout
|
||||
import android.widget.TextView
|
||||
import androidx.leanback.widget.TitleViewAdapter
|
||||
import org.dolphinemu.dolphinemu.R
|
||||
|
||||
class CustomTitleView @JvmOverloads constructor(
|
||||
context: Context?,
|
||||
attrs: AttributeSet? = null,
|
||||
defStyle: Int = 0
|
||||
) : LinearLayout(context, attrs, defStyle), TitleViewAdapter.Provider {
|
||||
private val titleView: TextView
|
||||
private val badgeView: View
|
||||
private val titleViewAdapter: TitleViewAdapter = object : TitleViewAdapter() {
|
||||
override fun getSearchAffordanceView(): View? = null
|
||||
|
||||
override fun setTitle(titleText: CharSequence?) = this@CustomTitleView.setTitle(titleText)
|
||||
|
||||
override fun setBadgeDrawable(drawable: Drawable?) =
|
||||
this@CustomTitleView.setBadgeDrawable(drawable)
|
||||
}
|
||||
|
||||
init {
|
||||
val root = LayoutInflater.from(context).inflate(R.layout.tv_title, this)
|
||||
titleView = root.findViewById(R.id.title)
|
||||
badgeView = root.findViewById(R.id.badge)
|
||||
}
|
||||
|
||||
fun setTitle(title: CharSequence?) {
|
||||
if (title != null) {
|
||||
titleView.text = title
|
||||
titleView.visibility = VISIBLE
|
||||
badgeView.visibility = VISIBLE
|
||||
}
|
||||
}
|
||||
|
||||
fun setBadgeDrawable(drawable: Drawable?) {
|
||||
if (drawable != null) {
|
||||
titleView.visibility = GONE
|
||||
badgeView.visibility = VISIBLE
|
||||
}
|
||||
}
|
||||
|
||||
override fun getTitleViewAdapter(): TitleViewAdapter = titleViewAdapter
|
||||
}
|
@ -1,441 +0,0 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
package org.dolphinemu.dolphinemu.ui.main;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuInflater;
|
||||
import android.view.MenuItem;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.core.graphics.Insets;
|
||||
import androidx.core.splashscreen.SplashScreen;
|
||||
import androidx.core.view.ViewCompat;
|
||||
import androidx.core.view.WindowCompat;
|
||||
import androidx.core.view.WindowInsetsCompat;
|
||||
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
|
||||
|
||||
import com.google.android.material.color.MaterialColors;
|
||||
import com.google.android.material.tabs.TabLayout;
|
||||
|
||||
import org.dolphinemu.dolphinemu.fragments.GridOptionDialogFragment;
|
||||
import org.dolphinemu.dolphinemu.R;
|
||||
import org.dolphinemu.dolphinemu.activities.EmulationActivity;
|
||||
import org.dolphinemu.dolphinemu.adapters.PlatformPagerAdapter;
|
||||
import org.dolphinemu.dolphinemu.databinding.ActivityMainBinding;
|
||||
import org.dolphinemu.dolphinemu.features.settings.model.IntSetting;
|
||||
import org.dolphinemu.dolphinemu.features.settings.model.NativeConfig;
|
||||
import org.dolphinemu.dolphinemu.features.settings.ui.MenuTag;
|
||||
import org.dolphinemu.dolphinemu.features.settings.ui.SettingsActivity;
|
||||
import org.dolphinemu.dolphinemu.services.GameFileCacheManager;
|
||||
import org.dolphinemu.dolphinemu.ui.platform.Platform;
|
||||
import org.dolphinemu.dolphinemu.ui.platform.PlatformGamesView;
|
||||
import org.dolphinemu.dolphinemu.utils.Action1;
|
||||
import org.dolphinemu.dolphinemu.utils.AfterDirectoryInitializationRunner;
|
||||
import org.dolphinemu.dolphinemu.utils.DirectoryInitialization;
|
||||
import org.dolphinemu.dolphinemu.utils.FileBrowserHelper;
|
||||
import org.dolphinemu.dolphinemu.utils.InsetsHelper;
|
||||
import org.dolphinemu.dolphinemu.utils.PermissionsHandler;
|
||||
import org.dolphinemu.dolphinemu.utils.StartupHandler;
|
||||
import org.dolphinemu.dolphinemu.utils.ThemeHelper;
|
||||
import org.dolphinemu.dolphinemu.utils.WiiUtils;
|
||||
|
||||
/**
|
||||
* The main Activity of the Lollipop style UI. Manages several PlatformGamesFragments, which
|
||||
* individually display a grid of available games for each Fragment, in a tabbed layout.
|
||||
*/
|
||||
public final class MainActivity extends AppCompatActivity
|
||||
implements MainView, SwipeRefreshLayout.OnRefreshListener, ThemeProvider
|
||||
{
|
||||
private int mThemeId;
|
||||
|
||||
private final MainPresenter mPresenter = new MainPresenter(this, this);
|
||||
|
||||
private ActivityMainBinding mBinding;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState)
|
||||
{
|
||||
SplashScreen splashScreen = SplashScreen.installSplashScreen(this);
|
||||
splashScreen.setKeepOnScreenCondition(
|
||||
() -> !DirectoryInitialization.areDolphinDirectoriesReady());
|
||||
|
||||
ThemeHelper.setTheme(this);
|
||||
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
mBinding = ActivityMainBinding.inflate(getLayoutInflater());
|
||||
setContentView(mBinding.getRoot());
|
||||
|
||||
WindowCompat.setDecorFitsSystemWindows(getWindow(), false);
|
||||
setInsets();
|
||||
ThemeHelper.enableStatusBarScrollTint(this, mBinding.appbarMain);
|
||||
|
||||
mBinding.toolbarMain.setTitle(R.string.app_name);
|
||||
setSupportActionBar(mBinding.toolbarMain);
|
||||
|
||||
// Set up the FAB.
|
||||
mBinding.buttonAddDirectory.setOnClickListener(view -> mPresenter.onFabClick());
|
||||
mBinding.appbarMain.addOnOffsetChangedListener((appBarLayout, verticalOffset) ->
|
||||
{
|
||||
if (verticalOffset == 0)
|
||||
{
|
||||
mBinding.buttonAddDirectory.extend();
|
||||
}
|
||||
else if (appBarLayout.getTotalScrollRange() == -verticalOffset)
|
||||
{
|
||||
mBinding.buttonAddDirectory.shrink();
|
||||
}
|
||||
});
|
||||
|
||||
mPresenter.onCreate();
|
||||
|
||||
// Stuff in this block only happens when this activity is newly created (i.e. not a rotation)
|
||||
if (savedInstanceState == null)
|
||||
{
|
||||
StartupHandler.HandleInit(this);
|
||||
new AfterDirectoryInitializationRunner().runWithLifecycle(this, this::checkTheme);
|
||||
}
|
||||
|
||||
if (!DirectoryInitialization.isWaitingForWriteAccess(this))
|
||||
{
|
||||
new AfterDirectoryInitializationRunner()
|
||||
.runWithLifecycle(this, this::setPlatformTabsAndStartGameFileCacheService);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onResume()
|
||||
{
|
||||
ThemeHelper.setCorrectTheme(this);
|
||||
|
||||
super.onResume();
|
||||
|
||||
if (DirectoryInitialization.shouldStart(this))
|
||||
{
|
||||
DirectoryInitialization.start(this);
|
||||
new AfterDirectoryInitializationRunner()
|
||||
.runWithLifecycle(this, this::setPlatformTabsAndStartGameFileCacheService);
|
||||
}
|
||||
|
||||
mPresenter.onResume();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDestroy()
|
||||
{
|
||||
super.onDestroy();
|
||||
mPresenter.onDestroy();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onStart()
|
||||
{
|
||||
super.onStart();
|
||||
StartupHandler.checkSessionReset(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onStop()
|
||||
{
|
||||
super.onStop();
|
||||
|
||||
if (isChangingConfigurations())
|
||||
{
|
||||
MainPresenter.skipRescanningLibrary();
|
||||
}
|
||||
else if (DirectoryInitialization.areDolphinDirectoriesReady())
|
||||
{
|
||||
// If the currently selected platform tab changed, save it to disk
|
||||
NativeConfig.save(NativeConfig.LAYER_BASE);
|
||||
}
|
||||
|
||||
StartupHandler.setSessionTime(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCreateOptionsMenu(Menu menu)
|
||||
{
|
||||
MenuInflater inflater = getMenuInflater();
|
||||
inflater.inflate(R.menu.menu_game_grid, menu);
|
||||
|
||||
if (WiiUtils.isSystemMenuInstalled())
|
||||
{
|
||||
int resId = WiiUtils.isSystemMenuvWii() ?
|
||||
R.string.grid_menu_load_vwii_system_menu_installed :
|
||||
R.string.grid_menu_load_wii_system_menu_installed;
|
||||
|
||||
menu.findItem(R.id.menu_load_wii_system_menu).setTitle(
|
||||
getString(resId, WiiUtils.getSystemMenuVersion()));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* MainView
|
||||
*/
|
||||
|
||||
@Override
|
||||
public void setVersionString(String version)
|
||||
{
|
||||
mBinding.toolbarMain.setSubtitle(version);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void launchSettingsActivity(MenuTag menuTag)
|
||||
{
|
||||
SettingsActivity.launch(this, menuTag);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void launchFileListActivity()
|
||||
{
|
||||
if (DirectoryInitialization.preferOldFolderPicker(this))
|
||||
{
|
||||
FileBrowserHelper.openDirectoryPicker(this, FileBrowserHelper.GAME_EXTENSIONS);
|
||||
}
|
||||
else
|
||||
{
|
||||
Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE);
|
||||
startActivityForResult(intent, MainPresenter.REQUEST_DIRECTORY);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void launchOpenFileActivity(int requestCode)
|
||||
{
|
||||
Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
|
||||
intent.addCategory(Intent.CATEGORY_OPENABLE);
|
||||
intent.setType("*/*");
|
||||
startActivityForResult(intent, requestCode);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param requestCode An int describing whether the Activity that is returning did so successfully.
|
||||
* @param resultCode An int describing what Activity is giving us this callback.
|
||||
* @param result The information the returning Activity is providing us.
|
||||
*/
|
||||
@Override
|
||||
protected void onActivityResult(int requestCode, int resultCode, Intent result)
|
||||
{
|
||||
super.onActivityResult(requestCode, resultCode, result);
|
||||
|
||||
// If the user picked a file, as opposed to just backing out.
|
||||
if (resultCode == RESULT_OK)
|
||||
{
|
||||
Uri uri = result.getData();
|
||||
switch (requestCode)
|
||||
{
|
||||
case MainPresenter.REQUEST_DIRECTORY:
|
||||
if (DirectoryInitialization.preferOldFolderPicker(this))
|
||||
{
|
||||
mPresenter.onDirectorySelected(FileBrowserHelper.getSelectedPath(result));
|
||||
}
|
||||
else
|
||||
{
|
||||
mPresenter.onDirectorySelected(result);
|
||||
}
|
||||
break;
|
||||
|
||||
case MainPresenter.REQUEST_GAME_FILE:
|
||||
FileBrowserHelper.runAfterExtensionCheck(this, uri,
|
||||
FileBrowserHelper.GAME_LIKE_EXTENSIONS,
|
||||
() -> EmulationActivity.launch(this, result.getData().toString(), false));
|
||||
break;
|
||||
|
||||
case MainPresenter.REQUEST_WAD_FILE:
|
||||
FileBrowserHelper.runAfterExtensionCheck(this, uri, FileBrowserHelper.WAD_EXTENSION,
|
||||
() -> mPresenter.installWAD(result.getData().toString()));
|
||||
break;
|
||||
|
||||
case MainPresenter.REQUEST_WII_SAVE_FILE:
|
||||
FileBrowserHelper.runAfterExtensionCheck(this, uri, FileBrowserHelper.BIN_EXTENSION,
|
||||
() -> mPresenter.importWiiSave(result.getData().toString()));
|
||||
break;
|
||||
|
||||
case MainPresenter.REQUEST_NAND_BIN_FILE:
|
||||
FileBrowserHelper.runAfterExtensionCheck(this, uri, FileBrowserHelper.BIN_EXTENSION,
|
||||
() -> mPresenter.importNANDBin(result.getData().toString()));
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
MainPresenter.skipRescanningLibrary();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,
|
||||
@NonNull int[] grantResults)
|
||||
{
|
||||
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
|
||||
|
||||
if (requestCode == PermissionsHandler.REQUEST_CODE_WRITE_PERMISSION)
|
||||
{
|
||||
if (grantResults[0] == PackageManager.PERMISSION_DENIED)
|
||||
{
|
||||
PermissionsHandler.setWritePermissionDenied();
|
||||
}
|
||||
|
||||
DirectoryInitialization.start(this);
|
||||
new AfterDirectoryInitializationRunner()
|
||||
.runWithLifecycle(this, this::setPlatformTabsAndStartGameFileCacheService);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called by the framework whenever any actionbar/toolbar icon is clicked.
|
||||
*
|
||||
* @param item The icon that was clicked on.
|
||||
* @return True if the event was handled, false to bubble it up to the OS.
|
||||
*/
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item)
|
||||
{
|
||||
return mPresenter.handleOptionSelection(item.getItemId(), this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the user requests a refresh by swiping down.
|
||||
*/
|
||||
@Override
|
||||
public void onRefresh()
|
||||
{
|
||||
setRefreshing(true);
|
||||
GameFileCacheManager.startRescan();
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows or hides the loading indicator.
|
||||
*/
|
||||
@Override
|
||||
public void setRefreshing(boolean refreshing)
|
||||
{
|
||||
forEachPlatformGamesView(view -> view.setRefreshing(refreshing));
|
||||
}
|
||||
|
||||
/**
|
||||
* To be called when the game file cache is updated.
|
||||
*/
|
||||
@Override
|
||||
public void showGames()
|
||||
{
|
||||
forEachPlatformGamesView(PlatformGamesView::showGames);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reloadGrid()
|
||||
{
|
||||
forEachPlatformGamesView(PlatformGamesView::refetchMetadata);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void showGridOptions()
|
||||
{
|
||||
new GridOptionDialogFragment().show(getSupportFragmentManager(), "gridOptions");
|
||||
}
|
||||
|
||||
private void forEachPlatformGamesView(Action1<PlatformGamesView> action)
|
||||
{
|
||||
for (Platform platform : Platform.values())
|
||||
{
|
||||
PlatformGamesView fragment = getPlatformGamesView(platform);
|
||||
if (fragment != null)
|
||||
{
|
||||
action.call(fragment);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private PlatformGamesView getPlatformGamesView(Platform platform)
|
||||
{
|
||||
String fragmentTag =
|
||||
"android:switcher:" + mBinding.pagerPlatforms.getId() + ":" + platform.toInt();
|
||||
|
||||
return (PlatformGamesView) getSupportFragmentManager().findFragmentByTag(fragmentTag);
|
||||
}
|
||||
|
||||
// Don't call this before DirectoryInitialization completes.
|
||||
private void setPlatformTabsAndStartGameFileCacheService()
|
||||
{
|
||||
PlatformPagerAdapter platformPagerAdapter = new PlatformPagerAdapter(
|
||||
getSupportFragmentManager(), this);
|
||||
mBinding.pagerPlatforms.setAdapter(platformPagerAdapter);
|
||||
mBinding.pagerPlatforms.setOffscreenPageLimit(platformPagerAdapter.getCount());
|
||||
mBinding.tabsPlatforms.setupWithViewPager(mBinding.pagerPlatforms);
|
||||
mBinding.tabsPlatforms.addOnTabSelectedListener(
|
||||
new TabLayout.ViewPagerOnTabSelectedListener(mBinding.pagerPlatforms)
|
||||
{
|
||||
@Override
|
||||
public void onTabSelected(@NonNull TabLayout.Tab tab)
|
||||
{
|
||||
super.onTabSelected(tab);
|
||||
IntSetting.MAIN_LAST_PLATFORM_TAB.setInt(NativeConfig.LAYER_BASE,
|
||||
tab.getPosition());
|
||||
}
|
||||
});
|
||||
|
||||
for (int i = 0; i < PlatformPagerAdapter.TAB_ICONS.length; i++)
|
||||
{
|
||||
mBinding.tabsPlatforms.getTabAt(i).setIcon(PlatformPagerAdapter.TAB_ICONS[i]);
|
||||
}
|
||||
|
||||
mBinding.pagerPlatforms.setCurrentItem(IntSetting.MAIN_LAST_PLATFORM_TAB.getInt());
|
||||
|
||||
showGames();
|
||||
GameFileCacheManager.startLoad();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setTheme(int themeId)
|
||||
{
|
||||
super.setTheme(themeId);
|
||||
this.mThemeId = themeId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getThemeId()
|
||||
{
|
||||
return mThemeId;
|
||||
}
|
||||
|
||||
private void checkTheme()
|
||||
{
|
||||
ThemeHelper.setCorrectTheme(this);
|
||||
}
|
||||
|
||||
private void setInsets()
|
||||
{
|
||||
ViewCompat.setOnApplyWindowInsetsListener(mBinding.appbarMain, (v, windowInsets) ->
|
||||
{
|
||||
Insets insets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars());
|
||||
|
||||
InsetsHelper.insetAppBar(insets, mBinding.appbarMain);
|
||||
|
||||
ViewGroup.MarginLayoutParams mlpFab =
|
||||
(ViewGroup.MarginLayoutParams) mBinding.buttonAddDirectory.getLayoutParams();
|
||||
int fabPadding = getResources().getDimensionPixelSize(R.dimen.spacing_large);
|
||||
mlpFab.leftMargin = insets.left + fabPadding;
|
||||
mlpFab.bottomMargin = insets.bottom + fabPadding;
|
||||
mlpFab.rightMargin = insets.right + fabPadding;
|
||||
mBinding.buttonAddDirectory.setLayoutParams(mlpFab);
|
||||
|
||||
mBinding.pagerPlatforms.setPadding(insets.left, 0, insets.right, 0);
|
||||
|
||||
InsetsHelper.applyNavbarWorkaround(insets.bottom, mBinding.workaroundView);
|
||||
ThemeHelper.setNavigationBarColor(this,
|
||||
MaterialColors.getColor(mBinding.appbarMain, R.attr.colorSurface));
|
||||
|
||||
return windowInsets;
|
||||
});
|
||||
}
|
||||
}
|
@ -0,0 +1,327 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
package org.dolphinemu.dolphinemu.ui.main
|
||||
|
||||
import android.content.Intent
|
||||
import android.content.pm.PackageManager
|
||||
import android.os.Bundle
|
||||
import android.view.Menu
|
||||
import android.view.MenuItem
|
||||
import android.view.View
|
||||
import android.view.ViewGroup.MarginLayoutParams
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
|
||||
import androidx.core.view.ViewCompat
|
||||
import androidx.core.view.WindowCompat
|
||||
import androidx.core.view.WindowInsetsCompat
|
||||
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout.OnRefreshListener
|
||||
import com.google.android.material.appbar.AppBarLayout
|
||||
import com.google.android.material.color.MaterialColors
|
||||
import com.google.android.material.tabs.TabLayout
|
||||
import org.dolphinemu.dolphinemu.R
|
||||
import org.dolphinemu.dolphinemu.activities.EmulationActivity
|
||||
import org.dolphinemu.dolphinemu.adapters.PlatformPagerAdapter
|
||||
import org.dolphinemu.dolphinemu.databinding.ActivityMainBinding
|
||||
import org.dolphinemu.dolphinemu.features.settings.model.IntSetting
|
||||
import org.dolphinemu.dolphinemu.features.settings.model.NativeConfig
|
||||
import org.dolphinemu.dolphinemu.features.settings.ui.MenuTag
|
||||
import org.dolphinemu.dolphinemu.features.settings.ui.SettingsActivity
|
||||
import org.dolphinemu.dolphinemu.fragments.GridOptionDialogFragment
|
||||
import org.dolphinemu.dolphinemu.services.GameFileCacheManager
|
||||
import org.dolphinemu.dolphinemu.ui.platform.Platform
|
||||
import org.dolphinemu.dolphinemu.ui.platform.PlatformGamesView
|
||||
import org.dolphinemu.dolphinemu.utils.Action1
|
||||
import org.dolphinemu.dolphinemu.utils.AfterDirectoryInitializationRunner
|
||||
import org.dolphinemu.dolphinemu.utils.DirectoryInitialization
|
||||
import org.dolphinemu.dolphinemu.utils.FileBrowserHelper
|
||||
import org.dolphinemu.dolphinemu.utils.InsetsHelper
|
||||
import org.dolphinemu.dolphinemu.utils.PermissionsHandler
|
||||
import org.dolphinemu.dolphinemu.utils.StartupHandler
|
||||
import org.dolphinemu.dolphinemu.utils.ThemeHelper
|
||||
import org.dolphinemu.dolphinemu.utils.WiiUtils
|
||||
|
||||
class MainActivity : AppCompatActivity(), MainView, OnRefreshListener, ThemeProvider {
|
||||
override var themeId = 0
|
||||
|
||||
private val presenter = MainPresenter(this, this)
|
||||
|
||||
private lateinit var binding: ActivityMainBinding
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
installSplashScreen().setKeepOnScreenCondition { !DirectoryInitialization.areDolphinDirectoriesReady() }
|
||||
|
||||
ThemeHelper.setTheme(this)
|
||||
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
binding = ActivityMainBinding.inflate(layoutInflater)
|
||||
setContentView(binding.root)
|
||||
|
||||
WindowCompat.setDecorFitsSystemWindows(window, false)
|
||||
setInsets()
|
||||
ThemeHelper.enableStatusBarScrollTint(this, binding.appbarMain)
|
||||
|
||||
binding.toolbarMain.setTitle(R.string.app_name)
|
||||
setSupportActionBar(binding.toolbarMain)
|
||||
|
||||
// Set up the FAB.
|
||||
binding.buttonAddDirectory.setOnClickListener { presenter.onFabClick() }
|
||||
binding.appbarMain.addOnOffsetChangedListener { appBarLayout: AppBarLayout, verticalOffset: Int ->
|
||||
if (verticalOffset == 0) {
|
||||
binding.buttonAddDirectory.extend()
|
||||
} else if (appBarLayout.totalScrollRange == -verticalOffset) {
|
||||
binding.buttonAddDirectory.shrink()
|
||||
}
|
||||
}
|
||||
|
||||
presenter.onCreate()
|
||||
|
||||
// Stuff in this block only happens when this activity is newly created (i.e. not a rotation)
|
||||
if (savedInstanceState == null) {
|
||||
StartupHandler.HandleInit(this)
|
||||
AfterDirectoryInitializationRunner().runWithLifecycle(this) {
|
||||
ThemeHelper.setCorrectTheme(this)
|
||||
}
|
||||
}
|
||||
if (!DirectoryInitialization.isWaitingForWriteAccess(this)) {
|
||||
AfterDirectoryInitializationRunner()
|
||||
.runWithLifecycle(this) { setPlatformTabsAndStartGameFileCacheService() }
|
||||
}
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
ThemeHelper.setCorrectTheme(this)
|
||||
|
||||
super.onResume()
|
||||
if (DirectoryInitialization.shouldStart(this)) {
|
||||
DirectoryInitialization.start(this)
|
||||
AfterDirectoryInitializationRunner()
|
||||
.runWithLifecycle(this) { setPlatformTabsAndStartGameFileCacheService() }
|
||||
}
|
||||
|
||||
presenter.onResume()
|
||||
}
|
||||
|
||||
override fun onStart() {
|
||||
super.onStart()
|
||||
StartupHandler.checkSessionReset(this)
|
||||
}
|
||||
|
||||
override fun onStop() {
|
||||
super.onStop()
|
||||
if (isChangingConfigurations) {
|
||||
MainPresenter.skipRescanningLibrary()
|
||||
} else if (DirectoryInitialization.areDolphinDirectoriesReady()) {
|
||||
// If the currently selected platform tab changed, save it to disk
|
||||
NativeConfig.save(NativeConfig.LAYER_BASE)
|
||||
}
|
||||
|
||||
StartupHandler.setSessionTime(this)
|
||||
}
|
||||
|
||||
override fun onCreateOptionsMenu(menu: Menu): Boolean {
|
||||
menuInflater.inflate(R.menu.menu_game_grid, menu)
|
||||
if (WiiUtils.isSystemMenuInstalled()) {
|
||||
val resId =
|
||||
if (WiiUtils.isSystemMenuvWii()) R.string.grid_menu_load_vwii_system_menu_installed else R.string.grid_menu_load_wii_system_menu_installed
|
||||
menu.findItem(R.id.menu_load_wii_system_menu).title =
|
||||
getString(resId, WiiUtils.getSystemMenuVersion())
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
/**
|
||||
* MainView
|
||||
*/
|
||||
override fun setVersionString(version: String) {
|
||||
binding.toolbarMain.subtitle = version
|
||||
}
|
||||
|
||||
override fun launchSettingsActivity(menuTag: MenuTag?) {
|
||||
SettingsActivity.launch(this, menuTag)
|
||||
}
|
||||
|
||||
override fun launchFileListActivity() {
|
||||
if (DirectoryInitialization.preferOldFolderPicker(this)) {
|
||||
FileBrowserHelper.openDirectoryPicker(this, FileBrowserHelper.GAME_EXTENSIONS)
|
||||
} else {
|
||||
val intent = Intent(Intent.ACTION_OPEN_DOCUMENT_TREE)
|
||||
startActivityForResult(intent, MainPresenter.REQUEST_DIRECTORY)
|
||||
}
|
||||
}
|
||||
|
||||
override fun launchOpenFileActivity(requestCode: Int) {
|
||||
val intent = Intent(Intent.ACTION_OPEN_DOCUMENT)
|
||||
intent.addCategory(Intent.CATEGORY_OPENABLE)
|
||||
intent.type = "*/*"
|
||||
startActivityForResult(intent, requestCode)
|
||||
}
|
||||
|
||||
/**
|
||||
* @param requestCode An int describing whether the Activity that is returning did so successfully.
|
||||
* @param resultCode An int describing what Activity is giving us this callback.
|
||||
* @param result The information the returning Activity is providing us.
|
||||
*/
|
||||
override fun onActivityResult(requestCode: Int, resultCode: Int, result: Intent?) {
|
||||
super.onActivityResult(requestCode, resultCode, result)
|
||||
|
||||
// If the user picked a file, as opposed to just backing out.
|
||||
if (resultCode == RESULT_OK) {
|
||||
val uri = result!!.data
|
||||
when (requestCode) {
|
||||
MainPresenter.REQUEST_DIRECTORY -> {
|
||||
if (DirectoryInitialization.preferOldFolderPicker(this)) {
|
||||
presenter.onDirectorySelected(FileBrowserHelper.getSelectedPath(result))
|
||||
} else {
|
||||
presenter.onDirectorySelected(result)
|
||||
}
|
||||
}
|
||||
|
||||
MainPresenter.REQUEST_GAME_FILE -> FileBrowserHelper.runAfterExtensionCheck(
|
||||
this, uri, FileBrowserHelper.GAME_LIKE_EXTENSIONS
|
||||
) { EmulationActivity.launch(this, result.data.toString(), false) }
|
||||
|
||||
MainPresenter.REQUEST_WAD_FILE -> FileBrowserHelper.runAfterExtensionCheck(
|
||||
this, uri, FileBrowserHelper.WAD_EXTENSION
|
||||
) { presenter.installWAD(result.data.toString()) }
|
||||
|
||||
MainPresenter.REQUEST_WII_SAVE_FILE -> FileBrowserHelper.runAfterExtensionCheck(
|
||||
this, uri, FileBrowserHelper.BIN_EXTENSION
|
||||
) { presenter.importWiiSave(result.data.toString()) }
|
||||
|
||||
MainPresenter.REQUEST_NAND_BIN_FILE -> FileBrowserHelper.runAfterExtensionCheck(
|
||||
this, uri, FileBrowserHelper.BIN_EXTENSION
|
||||
) { presenter.importNANDBin(result.data.toString()) }
|
||||
}
|
||||
} else {
|
||||
MainPresenter.skipRescanningLibrary()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onRequestPermissionsResult(
|
||||
requestCode: Int,
|
||||
permissions: Array<String>,
|
||||
grantResults: IntArray
|
||||
) {
|
||||
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
|
||||
if (requestCode == PermissionsHandler.REQUEST_CODE_WRITE_PERMISSION) {
|
||||
if (grantResults[0] == PackageManager.PERMISSION_DENIED) {
|
||||
PermissionsHandler.setWritePermissionDenied()
|
||||
}
|
||||
|
||||
DirectoryInitialization.start(this)
|
||||
AfterDirectoryInitializationRunner()
|
||||
.runWithLifecycle(this) { setPlatformTabsAndStartGameFileCacheService() }
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called by the framework whenever any actionbar/toolbar icon is clicked.
|
||||
*
|
||||
* @param item The icon that was clicked on.
|
||||
* @return True if the event was handled, false to bubble it up to the OS.
|
||||
*/
|
||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||
return presenter.handleOptionSelection(item.itemId, this)
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the user requests a refresh by swiping down.
|
||||
*/
|
||||
override fun onRefresh() {
|
||||
setRefreshing(true)
|
||||
GameFileCacheManager.startRescan()
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows or hides the loading indicator.
|
||||
*/
|
||||
override fun setRefreshing(refreshing: Boolean) =
|
||||
forEachPlatformGamesView { view: PlatformGamesView -> view.setRefreshing(refreshing) }
|
||||
|
||||
/**
|
||||
* To be called when the game file cache is updated.
|
||||
*/
|
||||
override fun showGames() =
|
||||
forEachPlatformGamesView { obj: PlatformGamesView -> obj.showGames() }
|
||||
|
||||
override fun reloadGrid() {
|
||||
forEachPlatformGamesView { obj: PlatformGamesView -> obj.refetchMetadata() }
|
||||
}
|
||||
|
||||
override fun showGridOptions() =
|
||||
GridOptionDialogFragment().show(supportFragmentManager, "gridOptions")
|
||||
|
||||
private fun forEachPlatformGamesView(action: Action1<PlatformGamesView>) {
|
||||
for (platform in Platform.values()) {
|
||||
val fragment = getPlatformGamesView(platform)
|
||||
if (fragment != null) {
|
||||
action.call(fragment)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun getPlatformGamesView(platform: Platform): PlatformGamesView? {
|
||||
val fragmentTag =
|
||||
"android:switcher:" + binding.pagerPlatforms.id + ":" + platform.toInt()
|
||||
return supportFragmentManager.findFragmentByTag(fragmentTag) as PlatformGamesView?
|
||||
}
|
||||
|
||||
// Don't call this before DirectoryInitialization completes.
|
||||
private fun setPlatformTabsAndStartGameFileCacheService() {
|
||||
val platformPagerAdapter = PlatformPagerAdapter(
|
||||
supportFragmentManager, this
|
||||
)
|
||||
binding.pagerPlatforms.adapter = platformPagerAdapter
|
||||
binding.pagerPlatforms.offscreenPageLimit = platformPagerAdapter.count
|
||||
binding.tabsPlatforms.setupWithViewPager(binding.pagerPlatforms)
|
||||
binding.tabsPlatforms.addOnTabSelectedListener(
|
||||
object : TabLayout.ViewPagerOnTabSelectedListener(binding.pagerPlatforms) {
|
||||
override fun onTabSelected(tab: TabLayout.Tab) {
|
||||
super.onTabSelected(tab)
|
||||
IntSetting.MAIN_LAST_PLATFORM_TAB.setInt(
|
||||
NativeConfig.LAYER_BASE,
|
||||
tab.position
|
||||
)
|
||||
}
|
||||
})
|
||||
|
||||
for (i in PlatformPagerAdapter.TAB_ICONS.indices) {
|
||||
binding.tabsPlatforms.getTabAt(i)?.setIcon(PlatformPagerAdapter.TAB_ICONS[i])
|
||||
}
|
||||
|
||||
binding.pagerPlatforms.currentItem = IntSetting.MAIN_LAST_PLATFORM_TAB.int
|
||||
|
||||
showGames()
|
||||
GameFileCacheManager.startLoad()
|
||||
}
|
||||
|
||||
override fun setTheme(themeId: Int) {
|
||||
super.setTheme(themeId)
|
||||
this.themeId = themeId
|
||||
}
|
||||
|
||||
private fun setInsets() =
|
||||
ViewCompat.setOnApplyWindowInsetsListener(binding.appbarMain) { _: View?, windowInsets: WindowInsetsCompat ->
|
||||
val insets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars())
|
||||
|
||||
InsetsHelper.insetAppBar(insets, binding.appbarMain)
|
||||
|
||||
val mlpFab = binding.buttonAddDirectory.layoutParams as MarginLayoutParams
|
||||
val fabPadding = resources.getDimensionPixelSize(R.dimen.spacing_large)
|
||||
mlpFab.leftMargin = insets.left + fabPadding
|
||||
mlpFab.bottomMargin = insets.bottom + fabPadding
|
||||
mlpFab.rightMargin = insets.right + fabPadding
|
||||
binding.buttonAddDirectory.layoutParams = mlpFab
|
||||
|
||||
binding.pagerPlatforms.setPadding(insets.left, 0, insets.right, 0)
|
||||
|
||||
InsetsHelper.applyNavbarWorkaround(insets.bottom, binding.workaroundView)
|
||||
ThemeHelper.setNavigationBarColor(
|
||||
this,
|
||||
MaterialColors.getColor(binding.appbarMain, R.attr.colorSurface)
|
||||
)
|
||||
|
||||
windowInsets
|
||||
}
|
||||
}
|
@ -1,349 +0,0 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
package org.dolphinemu.dolphinemu.ui.main;
|
||||
|
||||
import android.content.ContentResolver;
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
|
||||
import androidx.activity.ComponentActivity;
|
||||
import androidx.fragment.app.FragmentActivity;
|
||||
import androidx.lifecycle.Observer;
|
||||
import androidx.lifecycle.ViewModelProvider;
|
||||
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
|
||||
|
||||
import org.dolphinemu.dolphinemu.BuildConfig;
|
||||
import org.dolphinemu.dolphinemu.R;
|
||||
import org.dolphinemu.dolphinemu.activities.EmulationActivity;
|
||||
import org.dolphinemu.dolphinemu.features.settings.model.BooleanSetting;
|
||||
import org.dolphinemu.dolphinemu.features.settings.ui.MenuTag;
|
||||
import org.dolphinemu.dolphinemu.features.sysupdate.ui.SystemUpdateProgressBarDialogFragment;
|
||||
import org.dolphinemu.dolphinemu.features.sysupdate.ui.SystemMenuNotInstalledDialogFragment;
|
||||
import org.dolphinemu.dolphinemu.features.sysupdate.ui.SystemUpdateViewModel;
|
||||
import org.dolphinemu.dolphinemu.fragments.AboutDialogFragment;
|
||||
import org.dolphinemu.dolphinemu.model.GameFileCache;
|
||||
import org.dolphinemu.dolphinemu.services.GameFileCacheManager;
|
||||
import org.dolphinemu.dolphinemu.utils.AfterDirectoryInitializationRunner;
|
||||
import org.dolphinemu.dolphinemu.utils.BooleanSupplier;
|
||||
import org.dolphinemu.dolphinemu.utils.CompletableFuture;
|
||||
import org.dolphinemu.dolphinemu.utils.ContentHandler;
|
||||
import org.dolphinemu.dolphinemu.utils.DirectoryInitialization;
|
||||
import org.dolphinemu.dolphinemu.utils.FileBrowserHelper;
|
||||
import org.dolphinemu.dolphinemu.utils.PermissionsHandler;
|
||||
import org.dolphinemu.dolphinemu.utils.ThreadUtil;
|
||||
import org.dolphinemu.dolphinemu.utils.WiiUtils;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
|
||||
public final class MainPresenter
|
||||
{
|
||||
public static final int REQUEST_DIRECTORY = 1;
|
||||
public static final int REQUEST_GAME_FILE = 2;
|
||||
public static final int REQUEST_SD_FILE = 3;
|
||||
public static final int REQUEST_WAD_FILE = 4;
|
||||
public static final int REQUEST_WII_SAVE_FILE = 5;
|
||||
public static final int REQUEST_NAND_BIN_FILE = 6;
|
||||
|
||||
private static boolean sShouldRescanLibrary = true;
|
||||
|
||||
private final MainView mView;
|
||||
private final FragmentActivity mActivity;
|
||||
private String mDirToAdd;
|
||||
|
||||
public MainPresenter(MainView view, FragmentActivity activity)
|
||||
{
|
||||
mView = view;
|
||||
mActivity = activity;
|
||||
}
|
||||
|
||||
public void onCreate()
|
||||
{
|
||||
// Ask the user to grant write permission if relevant and not already granted
|
||||
if (DirectoryInitialization.isWaitingForWriteAccess(mActivity))
|
||||
PermissionsHandler.requestWritePermission(mActivity);
|
||||
|
||||
String versionName = BuildConfig.VERSION_NAME;
|
||||
mView.setVersionString(versionName);
|
||||
|
||||
GameFileCacheManager.getGameFiles().observe(mActivity, (gameFiles) -> mView.showGames());
|
||||
|
||||
Observer<Boolean> refreshObserver = (isLoading) ->
|
||||
{
|
||||
mView.setRefreshing(GameFileCacheManager.isLoadingOrRescanning());
|
||||
};
|
||||
GameFileCacheManager.isLoading().observe(mActivity, refreshObserver);
|
||||
GameFileCacheManager.isRescanning().observe(mActivity, refreshObserver);
|
||||
}
|
||||
|
||||
public void onDestroy()
|
||||
{
|
||||
}
|
||||
|
||||
public void onFabClick()
|
||||
{
|
||||
new AfterDirectoryInitializationRunner().runWithLifecycle(mActivity,
|
||||
mView::launchFileListActivity);
|
||||
}
|
||||
|
||||
public boolean handleOptionSelection(int itemId, ComponentActivity activity)
|
||||
{
|
||||
switch (itemId)
|
||||
{
|
||||
case R.id.menu_settings:
|
||||
mView.launchSettingsActivity(MenuTag.SETTINGS);
|
||||
return true;
|
||||
|
||||
case R.id.menu_grid_options:
|
||||
mView.showGridOptions();
|
||||
return true;
|
||||
|
||||
case R.id.menu_refresh:
|
||||
mView.setRefreshing(true);
|
||||
GameFileCacheManager.startRescan();
|
||||
return true;
|
||||
|
||||
case R.id.button_add_directory:
|
||||
new AfterDirectoryInitializationRunner().runWithLifecycle(activity,
|
||||
mView::launchFileListActivity);
|
||||
return true;
|
||||
|
||||
case R.id.menu_open_file:
|
||||
mView.launchOpenFileActivity(REQUEST_GAME_FILE);
|
||||
return true;
|
||||
|
||||
case R.id.menu_load_wii_system_menu:
|
||||
launchWiiSystemMenu();
|
||||
return true;
|
||||
|
||||
case R.id.menu_online_system_update:
|
||||
new AfterDirectoryInitializationRunner().runWithLifecycle(activity,
|
||||
this::launchOnlineUpdate);
|
||||
return true;
|
||||
|
||||
case R.id.menu_install_wad:
|
||||
new AfterDirectoryInitializationRunner().runWithLifecycle(activity,
|
||||
() -> mView.launchOpenFileActivity(REQUEST_WAD_FILE));
|
||||
return true;
|
||||
|
||||
case R.id.menu_import_wii_save:
|
||||
new AfterDirectoryInitializationRunner().runWithLifecycle(activity,
|
||||
() -> mView.launchOpenFileActivity(REQUEST_WII_SAVE_FILE));
|
||||
return true;
|
||||
|
||||
case R.id.menu_import_nand_backup:
|
||||
new AfterDirectoryInitializationRunner().runWithLifecycle(activity,
|
||||
() -> mView.launchOpenFileActivity(REQUEST_NAND_BIN_FILE));
|
||||
return true;
|
||||
|
||||
case R.id.menu_about:
|
||||
showAboutDialog();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public void onResume()
|
||||
{
|
||||
if (mDirToAdd != null)
|
||||
{
|
||||
GameFileCache.addGameFolder(mDirToAdd);
|
||||
mDirToAdd = null;
|
||||
}
|
||||
|
||||
if (sShouldRescanLibrary)
|
||||
{
|
||||
GameFileCacheManager.startRescan();
|
||||
}
|
||||
|
||||
sShouldRescanLibrary = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when a selection is made using the legacy folder picker.
|
||||
*/
|
||||
public void onDirectorySelected(String dir)
|
||||
{
|
||||
mDirToAdd = dir;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when a selection is made using the Storage Access Framework folder picker.
|
||||
*/
|
||||
public void onDirectorySelected(Intent result)
|
||||
{
|
||||
Uri uri = result.getData();
|
||||
|
||||
boolean recursive = BooleanSetting.MAIN_RECURSIVE_ISO_PATHS.getBoolean();
|
||||
String[] childNames = ContentHandler.getChildNames(uri, recursive);
|
||||
if (Arrays.stream(childNames).noneMatch((name) -> FileBrowserHelper.GAME_EXTENSIONS.contains(
|
||||
FileBrowserHelper.getExtension(name, false))))
|
||||
{
|
||||
new MaterialAlertDialogBuilder(mActivity)
|
||||
.setMessage(mActivity.getString(R.string.wrong_file_extension_in_directory,
|
||||
FileBrowserHelper.setToSortedDelimitedString(
|
||||
FileBrowserHelper.GAME_EXTENSIONS)))
|
||||
.setPositiveButton(R.string.ok, null)
|
||||
.show();
|
||||
}
|
||||
|
||||
ContentResolver contentResolver = mActivity.getContentResolver();
|
||||
Uri canonicalizedUri = contentResolver.canonicalize(uri);
|
||||
if (canonicalizedUri != null)
|
||||
uri = canonicalizedUri;
|
||||
|
||||
int takeFlags = result.getFlags() & Intent.FLAG_GRANT_READ_URI_PERMISSION;
|
||||
mActivity.getContentResolver().takePersistableUriPermission(uri, takeFlags);
|
||||
|
||||
mDirToAdd = uri.toString();
|
||||
}
|
||||
|
||||
public void installWAD(String path)
|
||||
{
|
||||
ThreadUtil.runOnThreadAndShowResult(mActivity, R.string.import_in_progress, 0, () ->
|
||||
{
|
||||
boolean success = WiiUtils.installWAD(path);
|
||||
int message = success ? R.string.wad_install_success : R.string.wad_install_failure;
|
||||
return mActivity.getResources().getString(message);
|
||||
});
|
||||
}
|
||||
|
||||
public void importWiiSave(String path)
|
||||
{
|
||||
CompletableFuture<Boolean> canOverwriteFuture = new CompletableFuture<>();
|
||||
|
||||
ThreadUtil.runOnThreadAndShowResult(mActivity, R.string.import_in_progress, 0, () ->
|
||||
{
|
||||
BooleanSupplier canOverwrite = () ->
|
||||
{
|
||||
mActivity.runOnUiThread(() ->
|
||||
{
|
||||
new MaterialAlertDialogBuilder(mActivity)
|
||||
.setMessage(R.string.wii_save_exists)
|
||||
.setCancelable(false)
|
||||
.setPositiveButton(R.string.yes, (dialog, i) -> canOverwriteFuture.complete(true))
|
||||
.setNegativeButton(R.string.no, (dialog, i) -> canOverwriteFuture.complete(false))
|
||||
.show();
|
||||
});
|
||||
|
||||
try
|
||||
{
|
||||
return canOverwriteFuture.get();
|
||||
}
|
||||
catch (ExecutionException | InterruptedException e)
|
||||
{
|
||||
// Shouldn't happen
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
};
|
||||
|
||||
int result = WiiUtils.importWiiSave(path, canOverwrite);
|
||||
|
||||
int message;
|
||||
switch (result)
|
||||
{
|
||||
case WiiUtils.RESULT_SUCCESS:
|
||||
message = R.string.wii_save_import_success;
|
||||
break;
|
||||
case WiiUtils.RESULT_CORRUPTED_SOURCE:
|
||||
message = R.string.wii_save_import_corruped_source;
|
||||
break;
|
||||
case WiiUtils.RESULT_TITLE_MISSING:
|
||||
message = R.string.wii_save_import_title_missing;
|
||||
break;
|
||||
case WiiUtils.RESULT_CANCELLED:
|
||||
return null;
|
||||
default:
|
||||
message = R.string.wii_save_import_error;
|
||||
break;
|
||||
}
|
||||
return mActivity.getResources().getString(message);
|
||||
});
|
||||
}
|
||||
|
||||
public void importNANDBin(String path)
|
||||
{
|
||||
new MaterialAlertDialogBuilder(mActivity)
|
||||
.setMessage(R.string.nand_import_warning)
|
||||
.setNegativeButton(R.string.no, (dialog, i) -> dialog.dismiss())
|
||||
.setPositiveButton(R.string.yes, (dialog, i) ->
|
||||
{
|
||||
dialog.dismiss();
|
||||
|
||||
ThreadUtil.runOnThreadAndShowResult(mActivity, R.string.import_in_progress,
|
||||
R.string.do_not_close_app, () ->
|
||||
{
|
||||
// ImportNANDBin unfortunately doesn't provide any result value...
|
||||
// It does however show a panic alert if something goes wrong.
|
||||
WiiUtils.importNANDBin(path);
|
||||
return null;
|
||||
});
|
||||
})
|
||||
.show();
|
||||
}
|
||||
|
||||
public static void skipRescanningLibrary()
|
||||
{
|
||||
sShouldRescanLibrary = false;
|
||||
}
|
||||
|
||||
private void launchOnlineUpdate()
|
||||
{
|
||||
if (WiiUtils.isSystemMenuInstalled())
|
||||
{
|
||||
SystemUpdateViewModel viewModel =
|
||||
new ViewModelProvider(mActivity).get(SystemUpdateViewModel.class);
|
||||
viewModel.setRegion(-1);
|
||||
launchUpdateProgressBarFragment(mActivity);
|
||||
}
|
||||
else
|
||||
{
|
||||
SystemMenuNotInstalledDialogFragment dialogFragment =
|
||||
new SystemMenuNotInstalledDialogFragment();
|
||||
dialogFragment
|
||||
.show(mActivity.getSupportFragmentManager(), "SystemMenuNotInstalledDialogFragment");
|
||||
}
|
||||
}
|
||||
|
||||
public static void launchDiscUpdate(String path, FragmentActivity activity)
|
||||
{
|
||||
SystemUpdateViewModel viewModel =
|
||||
new ViewModelProvider(activity).get(SystemUpdateViewModel.class);
|
||||
viewModel.setDiscPath(path);
|
||||
launchUpdateProgressBarFragment(activity);
|
||||
}
|
||||
|
||||
private static void launchUpdateProgressBarFragment(FragmentActivity activity)
|
||||
{
|
||||
SystemUpdateProgressBarDialogFragment progressBarFragment =
|
||||
new SystemUpdateProgressBarDialogFragment();
|
||||
progressBarFragment
|
||||
.show(activity.getSupportFragmentManager(), SystemUpdateProgressBarDialogFragment.TAG);
|
||||
progressBarFragment.setCancelable(false);
|
||||
}
|
||||
|
||||
private void launchWiiSystemMenu()
|
||||
{
|
||||
new AfterDirectoryInitializationRunner().runWithLifecycle(mActivity, () ->
|
||||
{
|
||||
if (WiiUtils.isSystemMenuInstalled())
|
||||
{
|
||||
EmulationActivity.launchSystemMenu(mActivity);
|
||||
}
|
||||
else
|
||||
{
|
||||
SystemMenuNotInstalledDialogFragment dialogFragment =
|
||||
new SystemMenuNotInstalledDialogFragment();
|
||||
dialogFragment
|
||||
.show(mActivity.getSupportFragmentManager(),
|
||||
"SystemMenuNotInstalledDialogFragment");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void showAboutDialog()
|
||||
{
|
||||
new AboutDialogFragment().show(mActivity.getSupportFragmentManager(), AboutDialogFragment.TAG);
|
||||
}
|
||||
}
|
@ -0,0 +1,311 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
package org.dolphinemu.dolphinemu.ui.main
|
||||
|
||||
import android.content.DialogInterface
|
||||
import android.content.Intent
|
||||
import androidx.activity.ComponentActivity
|
||||
import androidx.fragment.app.FragmentActivity
|
||||
import androidx.lifecycle.Observer
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import org.dolphinemu.dolphinemu.BuildConfig
|
||||
import org.dolphinemu.dolphinemu.R
|
||||
import org.dolphinemu.dolphinemu.activities.EmulationActivity
|
||||
import org.dolphinemu.dolphinemu.features.settings.model.BooleanSetting
|
||||
import org.dolphinemu.dolphinemu.features.settings.ui.MenuTag
|
||||
import org.dolphinemu.dolphinemu.features.sysupdate.ui.SystemMenuNotInstalledDialogFragment
|
||||
import org.dolphinemu.dolphinemu.features.sysupdate.ui.SystemUpdateProgressBarDialogFragment
|
||||
import org.dolphinemu.dolphinemu.features.sysupdate.ui.SystemUpdateViewModel
|
||||
import org.dolphinemu.dolphinemu.fragments.AboutDialogFragment
|
||||
import org.dolphinemu.dolphinemu.model.GameFileCache
|
||||
import org.dolphinemu.dolphinemu.services.GameFileCacheManager
|
||||
import org.dolphinemu.dolphinemu.utils.AfterDirectoryInitializationRunner
|
||||
import org.dolphinemu.dolphinemu.utils.BooleanSupplier
|
||||
import org.dolphinemu.dolphinemu.utils.CompletableFuture
|
||||
import org.dolphinemu.dolphinemu.utils.ContentHandler
|
||||
import org.dolphinemu.dolphinemu.utils.DirectoryInitialization
|
||||
import org.dolphinemu.dolphinemu.utils.FileBrowserHelper
|
||||
import org.dolphinemu.dolphinemu.utils.PermissionsHandler
|
||||
import org.dolphinemu.dolphinemu.utils.ThreadUtil
|
||||
import org.dolphinemu.dolphinemu.utils.WiiUtils
|
||||
import java.util.Arrays
|
||||
import java.util.concurrent.ExecutionException
|
||||
|
||||
class MainPresenter(private val mainView: MainView, private val activity: FragmentActivity) {
|
||||
private var dirToAdd: String? = null
|
||||
|
||||
fun onCreate() {
|
||||
// Ask the user to grant write permission if relevant and not already granted
|
||||
if (DirectoryInitialization.isWaitingForWriteAccess(activity))
|
||||
PermissionsHandler.requestWritePermission(activity)
|
||||
|
||||
val versionName = BuildConfig.VERSION_NAME
|
||||
mainView.setVersionString(versionName)
|
||||
|
||||
GameFileCacheManager.getGameFiles().observe(activity) { mainView.showGames() }
|
||||
val refreshObserver =
|
||||
Observer<Boolean> { _: Boolean? -> mainView.setRefreshing(GameFileCacheManager.isLoadingOrRescanning()) }
|
||||
GameFileCacheManager.isLoading().observe(activity, refreshObserver)
|
||||
GameFileCacheManager.isRescanning().observe(activity, refreshObserver)
|
||||
}
|
||||
|
||||
fun onFabClick() {
|
||||
AfterDirectoryInitializationRunner().runWithLifecycle(activity) { mainView.launchFileListActivity() }
|
||||
}
|
||||
|
||||
fun handleOptionSelection(itemId: Int, activity: ComponentActivity): Boolean =
|
||||
when (itemId) {
|
||||
R.id.menu_settings -> {
|
||||
mainView.launchSettingsActivity(MenuTag.SETTINGS)
|
||||
true
|
||||
}
|
||||
|
||||
R.id.menu_grid_options -> {
|
||||
mainView.showGridOptions()
|
||||
true
|
||||
}
|
||||
|
||||
R.id.menu_refresh -> {
|
||||
mainView.setRefreshing(true)
|
||||
GameFileCacheManager.startRescan()
|
||||
true
|
||||
}
|
||||
|
||||
R.id.button_add_directory -> {
|
||||
AfterDirectoryInitializationRunner().runWithLifecycle(activity) { mainView.launchFileListActivity() }
|
||||
true
|
||||
}
|
||||
|
||||
R.id.menu_open_file -> {
|
||||
mainView.launchOpenFileActivity(REQUEST_GAME_FILE)
|
||||
true
|
||||
}
|
||||
|
||||
R.id.menu_load_wii_system_menu -> {
|
||||
launchWiiSystemMenu()
|
||||
true
|
||||
}
|
||||
|
||||
R.id.menu_online_system_update -> {
|
||||
AfterDirectoryInitializationRunner().runWithLifecycle(activity) { launchOnlineUpdate() }
|
||||
true
|
||||
}
|
||||
|
||||
R.id.menu_install_wad -> {
|
||||
AfterDirectoryInitializationRunner().runWithLifecycle(
|
||||
activity
|
||||
) { mainView.launchOpenFileActivity(REQUEST_WAD_FILE) }
|
||||
true
|
||||
}
|
||||
|
||||
R.id.menu_import_wii_save -> {
|
||||
AfterDirectoryInitializationRunner().runWithLifecycle(
|
||||
activity
|
||||
) { mainView.launchOpenFileActivity(REQUEST_WII_SAVE_FILE) }
|
||||
true
|
||||
}
|
||||
|
||||
R.id.menu_import_nand_backup -> {
|
||||
AfterDirectoryInitializationRunner().runWithLifecycle(
|
||||
activity
|
||||
) { mainView.launchOpenFileActivity(REQUEST_NAND_BIN_FILE) }
|
||||
true
|
||||
}
|
||||
|
||||
R.id.menu_about -> {
|
||||
showAboutDialog()
|
||||
false
|
||||
}
|
||||
|
||||
else -> false
|
||||
}
|
||||
|
||||
fun onResume() {
|
||||
if (dirToAdd != null) {
|
||||
GameFileCache.addGameFolder(dirToAdd)
|
||||
dirToAdd = null
|
||||
}
|
||||
|
||||
if (shouldRescanLibrary) {
|
||||
GameFileCacheManager.startRescan()
|
||||
}
|
||||
|
||||
shouldRescanLibrary = true
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when a selection is made using the legacy folder picker.
|
||||
*/
|
||||
fun onDirectorySelected(dir: String?) {
|
||||
dirToAdd = dir
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when a selection is made using the Storage Access Framework folder picker.
|
||||
*/
|
||||
fun onDirectorySelected(result: Intent) {
|
||||
var uri = result.data!!
|
||||
|
||||
val recursive = BooleanSetting.MAIN_RECURSIVE_ISO_PATHS.boolean
|
||||
val childNames = ContentHandler.getChildNames(uri, recursive)
|
||||
if (Arrays.stream(childNames).noneMatch {
|
||||
FileBrowserHelper.GAME_EXTENSIONS
|
||||
.contains(FileBrowserHelper.getExtension(it, false))
|
||||
}) {
|
||||
MaterialAlertDialogBuilder(activity)
|
||||
.setMessage(
|
||||
activity.getString(
|
||||
R.string.wrong_file_extension_in_directory,
|
||||
FileBrowserHelper.setToSortedDelimitedString(FileBrowserHelper.GAME_EXTENSIONS)
|
||||
)
|
||||
)
|
||||
.setPositiveButton(android.R.string.ok, null)
|
||||
.show()
|
||||
}
|
||||
|
||||
val contentResolver = activity.contentResolver
|
||||
val canonicalizedUri = contentResolver.canonicalize(uri)
|
||||
if (canonicalizedUri != null)
|
||||
uri = canonicalizedUri
|
||||
|
||||
val takeFlags = result.flags and Intent.FLAG_GRANT_READ_URI_PERMISSION
|
||||
activity.contentResolver.takePersistableUriPermission(uri, takeFlags)
|
||||
|
||||
dirToAdd = uri.toString()
|
||||
}
|
||||
|
||||
fun installWAD(path: String?) {
|
||||
ThreadUtil.runOnThreadAndShowResult(
|
||||
activity,
|
||||
R.string.import_in_progress,
|
||||
0,
|
||||
{
|
||||
val success = WiiUtils.installWAD(path!!)
|
||||
val message =
|
||||
if (success) R.string.wad_install_success else R.string.wad_install_failure
|
||||
activity.getString(message)
|
||||
})
|
||||
}
|
||||
|
||||
fun importWiiSave(path: String?) {
|
||||
val canOverwriteFuture = CompletableFuture<Boolean>()
|
||||
ThreadUtil.runOnThreadAndShowResult(
|
||||
activity,
|
||||
R.string.import_in_progress,
|
||||
0,
|
||||
{
|
||||
val canOverwrite = BooleanSupplier {
|
||||
activity.runOnUiThread {
|
||||
MaterialAlertDialogBuilder(activity)
|
||||
.setMessage(R.string.wii_save_exists)
|
||||
.setCancelable(false)
|
||||
.setPositiveButton(R.string.yes) { _: DialogInterface?, _: Int ->
|
||||
canOverwriteFuture.complete(true)
|
||||
}
|
||||
.setNegativeButton(R.string.no) { _: DialogInterface?, _: Int ->
|
||||
canOverwriteFuture.complete(false)
|
||||
}
|
||||
.show()
|
||||
}
|
||||
try {
|
||||
return@BooleanSupplier canOverwriteFuture.get()
|
||||
} catch (e: ExecutionException) {
|
||||
// Shouldn't happen
|
||||
throw RuntimeException(e)
|
||||
} catch (e: InterruptedException) {
|
||||
throw RuntimeException(e)
|
||||
}
|
||||
}
|
||||
|
||||
val message: Int = when (WiiUtils.importWiiSave(path!!, canOverwrite)) {
|
||||
WiiUtils.RESULT_SUCCESS -> R.string.wii_save_import_success
|
||||
WiiUtils.RESULT_CORRUPTED_SOURCE -> R.string.wii_save_import_corruped_source
|
||||
WiiUtils.RESULT_TITLE_MISSING -> R.string.wii_save_import_title_missing
|
||||
WiiUtils.RESULT_CANCELLED -> return@runOnThreadAndShowResult null
|
||||
else -> R.string.wii_save_import_error
|
||||
}
|
||||
activity.resources.getString(message)
|
||||
})
|
||||
}
|
||||
|
||||
fun importNANDBin(path: String?) {
|
||||
MaterialAlertDialogBuilder(activity)
|
||||
.setMessage(R.string.nand_import_warning)
|
||||
.setNegativeButton(R.string.no) { dialog: DialogInterface, _: Int -> dialog.dismiss() }
|
||||
.setPositiveButton(R.string.yes) { dialog: DialogInterface, _: Int ->
|
||||
dialog.dismiss()
|
||||
ThreadUtil.runOnThreadAndShowResult(
|
||||
activity,
|
||||
R.string.import_in_progress,
|
||||
R.string.do_not_close_app,
|
||||
{
|
||||
// ImportNANDBin unfortunately doesn't provide any result value...
|
||||
// It does however show a panic alert if something goes wrong.
|
||||
WiiUtils.importNANDBin(path!!)
|
||||
null
|
||||
})
|
||||
}
|
||||
.show()
|
||||
}
|
||||
|
||||
private fun launchOnlineUpdate() {
|
||||
if (WiiUtils.isSystemMenuInstalled()) {
|
||||
val viewModel = ViewModelProvider(activity)[SystemUpdateViewModel::class.java]
|
||||
viewModel.region = -1
|
||||
launchUpdateProgressBarFragment(activity)
|
||||
} else {
|
||||
SystemMenuNotInstalledDialogFragment().show(
|
||||
activity.supportFragmentManager,
|
||||
SystemMenuNotInstalledDialogFragment.TAG
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun launchWiiSystemMenu() {
|
||||
AfterDirectoryInitializationRunner().runWithLifecycle(activity) {
|
||||
if (WiiUtils.isSystemMenuInstalled()) {
|
||||
EmulationActivity.launchSystemMenu(activity)
|
||||
} else {
|
||||
SystemMenuNotInstalledDialogFragment().show(
|
||||
activity.supportFragmentManager,
|
||||
SystemMenuNotInstalledDialogFragment.TAG
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun showAboutDialog() {
|
||||
AboutDialogFragment().show(activity.supportFragmentManager, AboutDialogFragment.TAG)
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val REQUEST_DIRECTORY = 1
|
||||
const val REQUEST_GAME_FILE = 2
|
||||
const val REQUEST_SD_FILE = 3
|
||||
const val REQUEST_WAD_FILE = 4
|
||||
const val REQUEST_WII_SAVE_FILE = 5
|
||||
const val REQUEST_NAND_BIN_FILE = 6
|
||||
|
||||
private var shouldRescanLibrary = true
|
||||
|
||||
@JvmStatic
|
||||
fun skipRescanningLibrary() {
|
||||
shouldRescanLibrary = false
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun launchDiscUpdate(path: String, activity: FragmentActivity) {
|
||||
val viewModel = ViewModelProvider(activity)[SystemUpdateViewModel::class.java]
|
||||
viewModel.discPath = path
|
||||
launchUpdateProgressBarFragment(activity)
|
||||
}
|
||||
|
||||
private fun launchUpdateProgressBarFragment(activity: FragmentActivity) {
|
||||
val progressBarFragment = SystemUpdateProgressBarDialogFragment()
|
||||
progressBarFragment
|
||||
.show(activity.supportFragmentManager, SystemUpdateProgressBarDialogFragment.TAG)
|
||||
progressBarFragment.isCancelable = false
|
||||
}
|
||||
}
|
||||
}
|
@ -1,41 +0,0 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
package org.dolphinemu.dolphinemu.ui.main;
|
||||
|
||||
import org.dolphinemu.dolphinemu.features.settings.ui.MenuTag;
|
||||
|
||||
/**
|
||||
* Abstraction for the screen that shows on application launch.
|
||||
* Implementations will differ primarily to target touch-screen
|
||||
* or non-touch screen devices.
|
||||
*/
|
||||
public interface MainView
|
||||
{
|
||||
/**
|
||||
* Pass the view the native library's version string. Displaying
|
||||
* it is optional.
|
||||
*
|
||||
* @param version A string pulled from native code.
|
||||
*/
|
||||
void setVersionString(String version);
|
||||
|
||||
void launchSettingsActivity(MenuTag menuTag);
|
||||
|
||||
void launchFileListActivity();
|
||||
|
||||
void launchOpenFileActivity(int requestCode);
|
||||
|
||||
/**
|
||||
* Shows or hides the loading indicator.
|
||||
*/
|
||||
void setRefreshing(boolean refreshing);
|
||||
|
||||
/**
|
||||
* To be called when the game file cache is updated.
|
||||
*/
|
||||
void showGames();
|
||||
|
||||
void reloadGrid();
|
||||
|
||||
void showGridOptions();
|
||||
}
|
@ -0,0 +1,40 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
package org.dolphinemu.dolphinemu.ui.main
|
||||
|
||||
import org.dolphinemu.dolphinemu.features.settings.ui.MenuTag
|
||||
|
||||
/**
|
||||
* Abstraction for the screen that shows on application launch.
|
||||
* Implementations will differ primarily to target touch-screen
|
||||
* or non-touch screen devices.
|
||||
*/
|
||||
interface MainView {
|
||||
/**
|
||||
* Pass the view the native library's version string. Displaying
|
||||
* it is optional.
|
||||
*
|
||||
* @param version A string pulled from native code.
|
||||
*/
|
||||
fun setVersionString(version: String)
|
||||
|
||||
fun launchSettingsActivity(menuTag: MenuTag?)
|
||||
|
||||
fun launchFileListActivity()
|
||||
|
||||
fun launchOpenFileActivity(requestCode: Int)
|
||||
|
||||
/**
|
||||
* Shows or hides the loading indicator.
|
||||
*/
|
||||
fun setRefreshing(refreshing: Boolean)
|
||||
|
||||
/**
|
||||
* To be called when the game file cache is updated.
|
||||
*/
|
||||
fun showGames()
|
||||
|
||||
fun reloadGrid()
|
||||
|
||||
fun showGridOptions()
|
||||
}
|
@ -1,9 +0,0 @@
|
||||
package org.dolphinemu.dolphinemu.ui.main;
|
||||
|
||||
public interface ThemeProvider
|
||||
{
|
||||
/**
|
||||
* Provides theme ID by overriding an activity's 'setTheme' method and returning that result
|
||||
*/
|
||||
int getThemeId();
|
||||
}
|
@ -0,0 +1,10 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
package org.dolphinemu.dolphinemu.ui.main
|
||||
|
||||
interface ThemeProvider {
|
||||
/**
|
||||
* Provides theme ID by overriding an activity's 'setTheme' method and returning that result
|
||||
*/
|
||||
val themeId: Int
|
||||
}
|
@ -1,418 +0,0 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
package org.dolphinemu.dolphinemu.ui.main;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.core.content.ContextCompat;
|
||||
import androidx.core.splashscreen.SplashScreen;
|
||||
import androidx.fragment.app.FragmentActivity;
|
||||
import androidx.fragment.app.FragmentManager;
|
||||
import androidx.leanback.app.BrowseSupportFragment;
|
||||
import androidx.leanback.widget.ArrayObjectAdapter;
|
||||
import androidx.leanback.widget.HeaderItem;
|
||||
import androidx.leanback.widget.ListRow;
|
||||
import androidx.leanback.widget.ListRowPresenter;
|
||||
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
|
||||
|
||||
import org.dolphinemu.dolphinemu.fragments.GridOptionDialogFragment;
|
||||
import org.dolphinemu.dolphinemu.R;
|
||||
import org.dolphinemu.dolphinemu.activities.EmulationActivity;
|
||||
import org.dolphinemu.dolphinemu.adapters.GameRowPresenter;
|
||||
import org.dolphinemu.dolphinemu.adapters.SettingsRowPresenter;
|
||||
import org.dolphinemu.dolphinemu.databinding.ActivityTvMainBinding;
|
||||
import org.dolphinemu.dolphinemu.features.settings.ui.MenuTag;
|
||||
import org.dolphinemu.dolphinemu.features.settings.ui.SettingsActivity;
|
||||
import org.dolphinemu.dolphinemu.model.GameFile;
|
||||
import org.dolphinemu.dolphinemu.model.TvSettingsItem;
|
||||
import org.dolphinemu.dolphinemu.services.GameFileCacheManager;
|
||||
import org.dolphinemu.dolphinemu.ui.platform.Platform;
|
||||
import org.dolphinemu.dolphinemu.utils.DirectoryInitialization;
|
||||
import org.dolphinemu.dolphinemu.utils.FileBrowserHelper;
|
||||
import org.dolphinemu.dolphinemu.utils.PermissionsHandler;
|
||||
import org.dolphinemu.dolphinemu.utils.StartupHandler;
|
||||
import org.dolphinemu.dolphinemu.utils.TvUtil;
|
||||
import org.dolphinemu.dolphinemu.viewholders.TvGameViewHolder;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
|
||||
public final class TvMainActivity extends FragmentActivity
|
||||
implements MainView, SwipeRefreshLayout.OnRefreshListener
|
||||
{
|
||||
private final MainPresenter mPresenter = new MainPresenter(this, this);
|
||||
|
||||
private SwipeRefreshLayout mSwipeRefresh;
|
||||
|
||||
private BrowseSupportFragment mBrowseFragment;
|
||||
|
||||
private final ArrayList<ArrayObjectAdapter> mGameRows = new ArrayList<>();
|
||||
|
||||
private ActivityTvMainBinding mBinding;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState)
|
||||
{
|
||||
SplashScreen splashScreen = SplashScreen.installSplashScreen(this);
|
||||
splashScreen.setKeepOnScreenCondition(
|
||||
() -> !DirectoryInitialization.areDolphinDirectoriesReady());
|
||||
|
||||
super.onCreate(savedInstanceState);
|
||||
mBinding = ActivityTvMainBinding.inflate(getLayoutInflater());
|
||||
setContentView(mBinding.getRoot());
|
||||
|
||||
setupUI();
|
||||
|
||||
mPresenter.onCreate();
|
||||
|
||||
// Stuff in this block only happens when this activity is newly created (i.e. not a rotation)
|
||||
if (savedInstanceState == null)
|
||||
{
|
||||
StartupHandler.HandleInit(this);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onResume()
|
||||
{
|
||||
super.onResume();
|
||||
|
||||
if (DirectoryInitialization.shouldStart(this))
|
||||
{
|
||||
DirectoryInitialization.start(this);
|
||||
GameFileCacheManager.startLoad();
|
||||
}
|
||||
|
||||
mPresenter.onResume();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDestroy()
|
||||
{
|
||||
super.onDestroy();
|
||||
mPresenter.onDestroy();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onStart()
|
||||
{
|
||||
super.onStart();
|
||||
StartupHandler.checkSessionReset(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onStop()
|
||||
{
|
||||
super.onStop();
|
||||
|
||||
if (isChangingConfigurations())
|
||||
{
|
||||
MainPresenter.skipRescanningLibrary();
|
||||
}
|
||||
|
||||
StartupHandler.setSessionTime(this);
|
||||
}
|
||||
|
||||
void setupUI()
|
||||
{
|
||||
mSwipeRefresh = mBinding.swipeRefresh;
|
||||
|
||||
mSwipeRefresh.setOnRefreshListener(this);
|
||||
|
||||
setRefreshing(GameFileCacheManager.isLoadingOrRescanning());
|
||||
|
||||
final FragmentManager fragmentManager = getSupportFragmentManager();
|
||||
mBrowseFragment = new BrowseSupportFragment();
|
||||
fragmentManager
|
||||
.beginTransaction()
|
||||
.add(R.id.content, mBrowseFragment, "BrowseFragment")
|
||||
.commit();
|
||||
|
||||
// Set display parameters for the BrowseFragment
|
||||
mBrowseFragment.setHeadersState(BrowseSupportFragment.HEADERS_ENABLED);
|
||||
mBrowseFragment.setBrandColor(ContextCompat.getColor(this, R.color.dolphin_blue));
|
||||
buildRowsAdapter();
|
||||
|
||||
mBrowseFragment.setOnItemViewClickedListener(
|
||||
(itemViewHolder, item, rowViewHolder, row) ->
|
||||
{
|
||||
// Special case: user clicked on a settings row item.
|
||||
if (item instanceof TvSettingsItem)
|
||||
{
|
||||
TvSettingsItem settingsItem = (TvSettingsItem) item;
|
||||
mPresenter.handleOptionSelection(settingsItem.getItemId(), this);
|
||||
}
|
||||
else
|
||||
{
|
||||
TvGameViewHolder holder = (TvGameViewHolder) itemViewHolder;
|
||||
|
||||
// Start the emulation activity and send the path of the clicked ISO to it.
|
||||
String[] paths = GameFileCacheManager.findSecondDiscAndGetPaths(holder.gameFile);
|
||||
EmulationActivity.launch(TvMainActivity.this, paths, false);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* MainView
|
||||
*/
|
||||
|
||||
@Override
|
||||
public void setVersionString(String version)
|
||||
{
|
||||
mBrowseFragment.setTitle(version);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void launchSettingsActivity(MenuTag menuTag)
|
||||
{
|
||||
SettingsActivity.launch(this, menuTag);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void launchFileListActivity()
|
||||
{
|
||||
if (DirectoryInitialization.preferOldFolderPicker(this))
|
||||
{
|
||||
FileBrowserHelper.openDirectoryPicker(this, FileBrowserHelper.GAME_EXTENSIONS);
|
||||
}
|
||||
else
|
||||
{
|
||||
Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE);
|
||||
startActivityForResult(intent, MainPresenter.REQUEST_DIRECTORY);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void launchOpenFileActivity(int requestCode)
|
||||
{
|
||||
Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
|
||||
intent.addCategory(Intent.CATEGORY_OPENABLE);
|
||||
intent.setType("*/*");
|
||||
startActivityForResult(intent, requestCode);
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows or hides the loading indicator.
|
||||
*/
|
||||
@Override
|
||||
public void setRefreshing(boolean refreshing)
|
||||
{
|
||||
mSwipeRefresh.setRefreshing(refreshing);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void showGames()
|
||||
{
|
||||
// Kicks off the program services to update all channels
|
||||
TvUtil.updateAllChannels(getApplicationContext());
|
||||
|
||||
buildRowsAdapter();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reloadGrid()
|
||||
{
|
||||
for (ArrayObjectAdapter row : mGameRows)
|
||||
{
|
||||
row.notifyArrayItemRangeChanged(0, row.size());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void showGridOptions()
|
||||
{
|
||||
new GridOptionDialogFragment().show(getSupportFragmentManager(), "gridOptions");
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback from AddDirectoryActivity. Applies any changes necessary to the GameGridActivity.
|
||||
*
|
||||
* @param requestCode An int describing whether the Activity that is returning did so successfully.
|
||||
* @param resultCode An int describing what Activity is giving us this callback.
|
||||
* @param result The information the returning Activity is providing us.
|
||||
*/
|
||||
@Override
|
||||
protected void onActivityResult(int requestCode, int resultCode, Intent result)
|
||||
{
|
||||
super.onActivityResult(requestCode, resultCode, result);
|
||||
|
||||
// If the user picked a file, as opposed to just backing out.
|
||||
if (resultCode == RESULT_OK)
|
||||
{
|
||||
Uri uri = result.getData();
|
||||
switch (requestCode)
|
||||
{
|
||||
case MainPresenter.REQUEST_DIRECTORY:
|
||||
if (DirectoryInitialization.preferOldFolderPicker(this))
|
||||
{
|
||||
mPresenter.onDirectorySelected(FileBrowserHelper.getSelectedPath(result));
|
||||
}
|
||||
else
|
||||
{
|
||||
mPresenter.onDirectorySelected(result);
|
||||
}
|
||||
break;
|
||||
|
||||
case MainPresenter.REQUEST_GAME_FILE:
|
||||
FileBrowserHelper.runAfterExtensionCheck(this, uri,
|
||||
FileBrowserHelper.GAME_LIKE_EXTENSIONS,
|
||||
() -> EmulationActivity.launch(this, result.getData().toString(), false));
|
||||
break;
|
||||
|
||||
case MainPresenter.REQUEST_WAD_FILE:
|
||||
FileBrowserHelper.runAfterExtensionCheck(this, uri, FileBrowserHelper.WAD_EXTENSION,
|
||||
() -> mPresenter.installWAD(result.getData().toString()));
|
||||
break;
|
||||
|
||||
case MainPresenter.REQUEST_WII_SAVE_FILE:
|
||||
FileBrowserHelper.runAfterExtensionCheck(this, uri, FileBrowserHelper.BIN_EXTENSION,
|
||||
() -> mPresenter.importWiiSave(result.getData().toString()));
|
||||
break;
|
||||
|
||||
case MainPresenter.REQUEST_NAND_BIN_FILE:
|
||||
FileBrowserHelper.runAfterExtensionCheck(this, uri, FileBrowserHelper.BIN_EXTENSION,
|
||||
() -> mPresenter.importNANDBin(result.getData().toString()));
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
MainPresenter.skipRescanningLibrary();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,
|
||||
@NonNull int[] grantResults)
|
||||
{
|
||||
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
|
||||
|
||||
if (requestCode == PermissionsHandler.REQUEST_CODE_WRITE_PERMISSION)
|
||||
{
|
||||
if (grantResults[0] == PackageManager.PERMISSION_DENIED)
|
||||
{
|
||||
PermissionsHandler.setWritePermissionDenied();
|
||||
}
|
||||
|
||||
DirectoryInitialization.start(this);
|
||||
GameFileCacheManager.startLoad();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the user requests a refresh by swiping down.
|
||||
*/
|
||||
@Override
|
||||
public void onRefresh()
|
||||
{
|
||||
setRefreshing(true);
|
||||
GameFileCacheManager.startRescan();
|
||||
}
|
||||
|
||||
private void buildRowsAdapter()
|
||||
{
|
||||
ArrayObjectAdapter rowsAdapter = new ArrayObjectAdapter(new ListRowPresenter());
|
||||
mGameRows.clear();
|
||||
|
||||
if (!DirectoryInitialization.isWaitingForWriteAccess(this))
|
||||
{
|
||||
GameFileCacheManager.startLoad();
|
||||
}
|
||||
|
||||
for (Platform platform : Platform.values())
|
||||
{
|
||||
ListRow row = buildGamesRow(platform, GameFileCacheManager.getGameFilesForPlatform(platform));
|
||||
|
||||
// Add row to the adapter only if it is not empty.
|
||||
if (row != null)
|
||||
{
|
||||
rowsAdapter.add(row);
|
||||
}
|
||||
}
|
||||
|
||||
rowsAdapter.add(buildSettingsRow());
|
||||
|
||||
mBrowseFragment.setAdapter(rowsAdapter);
|
||||
}
|
||||
|
||||
private ListRow buildGamesRow(Platform platform, Collection<GameFile> gameFiles)
|
||||
{
|
||||
// If there are no games, don't return a Row.
|
||||
if (gameFiles.size() == 0)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
// Create an adapter for this row.
|
||||
ArrayObjectAdapter row = new ArrayObjectAdapter(new GameRowPresenter(this));
|
||||
row.addAll(0, gameFiles);
|
||||
|
||||
// Keep a reference to the row in case we need to refresh it.
|
||||
mGameRows.add(row);
|
||||
|
||||
// Create a header for this row.
|
||||
HeaderItem header = new HeaderItem(platform.toInt(), getString(platform.getHeaderName()));
|
||||
|
||||
// Create the row, passing it the filled adapter and the header, and give it to the master adapter.
|
||||
return new ListRow(header, row);
|
||||
}
|
||||
|
||||
private ListRow buildSettingsRow()
|
||||
{
|
||||
ArrayObjectAdapter rowItems = new ArrayObjectAdapter(new SettingsRowPresenter());
|
||||
|
||||
rowItems.add(new TvSettingsItem(R.id.menu_settings,
|
||||
R.drawable.ic_settings_tv,
|
||||
R.string.grid_menu_settings));
|
||||
|
||||
rowItems.add(new TvSettingsItem(R.id.button_add_directory,
|
||||
R.drawable.ic_add_tv,
|
||||
R.string.add_directory_title));
|
||||
|
||||
rowItems.add(new TvSettingsItem(R.id.menu_grid_options,
|
||||
R.drawable.ic_list_tv,
|
||||
R.string.grid_menu_grid_options));
|
||||
|
||||
rowItems.add(new TvSettingsItem(R.id.menu_refresh,
|
||||
R.drawable.ic_refresh_tv,
|
||||
R.string.grid_menu_refresh));
|
||||
|
||||
rowItems.add(new TvSettingsItem(R.id.menu_open_file,
|
||||
R.drawable.ic_play_tv,
|
||||
R.string.grid_menu_open_file));
|
||||
|
||||
rowItems.add(new TvSettingsItem(R.id.menu_install_wad,
|
||||
R.drawable.ic_folder_tv,
|
||||
R.string.grid_menu_install_wad));
|
||||
|
||||
rowItems.add(new TvSettingsItem(R.id.menu_load_wii_system_menu,
|
||||
R.drawable.ic_folder_tv,
|
||||
R.string.grid_menu_load_wii_system_menu));
|
||||
|
||||
rowItems.add(new TvSettingsItem(R.id.menu_import_wii_save,
|
||||
R.drawable.ic_folder_tv,
|
||||
R.string.grid_menu_import_wii_save));
|
||||
|
||||
rowItems.add(new TvSettingsItem(R.id.menu_import_nand_backup,
|
||||
R.drawable.ic_folder_tv,
|
||||
R.string.grid_menu_import_nand_backup));
|
||||
|
||||
rowItems.add(new TvSettingsItem(R.id.menu_online_system_update,
|
||||
R.drawable.ic_folder_tv,
|
||||
R.string.grid_menu_online_system_update));
|
||||
|
||||
rowItems.add(new TvSettingsItem(R.id.menu_about,
|
||||
R.drawable.ic_info_tv,
|
||||
R.string.grid_menu_about));
|
||||
|
||||
// Create a header for this row.
|
||||
HeaderItem header = new HeaderItem(R.string.settings, getString(R.string.settings));
|
||||
|
||||
return new ListRow(header, rowItems);
|
||||
}
|
||||
}
|
@ -0,0 +1,370 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
package org.dolphinemu.dolphinemu.ui.main
|
||||
|
||||
import android.content.Intent
|
||||
import android.content.pm.PackageManager
|
||||
import android.os.Bundle
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
|
||||
import androidx.fragment.app.FragmentActivity
|
||||
import androidx.leanback.app.BrowseSupportFragment
|
||||
import androidx.leanback.widget.ArrayObjectAdapter
|
||||
import androidx.leanback.widget.HeaderItem
|
||||
import androidx.leanback.widget.ListRow
|
||||
import androidx.leanback.widget.ListRowPresenter
|
||||
import androidx.leanback.widget.OnItemViewClickedListener
|
||||
import androidx.leanback.widget.Presenter
|
||||
import androidx.leanback.widget.Row
|
||||
import androidx.leanback.widget.RowPresenter
|
||||
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
|
||||
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout.OnRefreshListener
|
||||
import org.dolphinemu.dolphinemu.R
|
||||
import org.dolphinemu.dolphinemu.activities.EmulationActivity
|
||||
import org.dolphinemu.dolphinemu.adapters.GameRowPresenter
|
||||
import org.dolphinemu.dolphinemu.adapters.SettingsRowPresenter
|
||||
import org.dolphinemu.dolphinemu.databinding.ActivityTvMainBinding
|
||||
import org.dolphinemu.dolphinemu.features.settings.ui.MenuTag
|
||||
import org.dolphinemu.dolphinemu.features.settings.ui.SettingsActivity
|
||||
import org.dolphinemu.dolphinemu.fragments.GridOptionDialogFragment
|
||||
import org.dolphinemu.dolphinemu.model.GameFile
|
||||
import org.dolphinemu.dolphinemu.model.TvSettingsItem
|
||||
import org.dolphinemu.dolphinemu.services.GameFileCacheManager
|
||||
import org.dolphinemu.dolphinemu.ui.platform.Platform
|
||||
import org.dolphinemu.dolphinemu.utils.DirectoryInitialization
|
||||
import org.dolphinemu.dolphinemu.utils.FileBrowserHelper
|
||||
import org.dolphinemu.dolphinemu.utils.PermissionsHandler
|
||||
import org.dolphinemu.dolphinemu.utils.StartupHandler
|
||||
import org.dolphinemu.dolphinemu.utils.TvUtil
|
||||
import org.dolphinemu.dolphinemu.viewholders.TvGameViewHolder
|
||||
|
||||
class TvMainActivity : FragmentActivity(), MainView, OnRefreshListener {
|
||||
private val presenter = MainPresenter(this, this)
|
||||
|
||||
private var swipeRefresh: SwipeRefreshLayout? = null
|
||||
|
||||
private var browseFragment: BrowseSupportFragment? = null
|
||||
|
||||
private val gameRows = ArrayList<ArrayObjectAdapter>()
|
||||
|
||||
private lateinit var binding: ActivityTvMainBinding
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
installSplashScreen().setKeepOnScreenCondition { !DirectoryInitialization.areDolphinDirectoriesReady() }
|
||||
|
||||
super.onCreate(savedInstanceState)
|
||||
binding = ActivityTvMainBinding.inflate(layoutInflater)
|
||||
setContentView(binding.root)
|
||||
|
||||
setupUI()
|
||||
|
||||
presenter.onCreate()
|
||||
|
||||
// Stuff in this block only happens when this activity is newly created (i.e. not a rotation)
|
||||
if (savedInstanceState == null) {
|
||||
StartupHandler.HandleInit(this)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
if (DirectoryInitialization.shouldStart(this)) {
|
||||
DirectoryInitialization.start(this)
|
||||
GameFileCacheManager.startLoad()
|
||||
}
|
||||
|
||||
presenter.onResume()
|
||||
}
|
||||
|
||||
override fun onStart() {
|
||||
super.onStart()
|
||||
StartupHandler.checkSessionReset(this)
|
||||
}
|
||||
|
||||
override fun onStop() {
|
||||
super.onStop()
|
||||
if (isChangingConfigurations) {
|
||||
MainPresenter.skipRescanningLibrary()
|
||||
}
|
||||
StartupHandler.setSessionTime(this)
|
||||
}
|
||||
|
||||
private fun setupUI() {
|
||||
swipeRefresh = binding.swipeRefresh
|
||||
swipeRefresh!!.setOnRefreshListener(this)
|
||||
setRefreshing(GameFileCacheManager.isLoadingOrRescanning())
|
||||
|
||||
browseFragment = BrowseSupportFragment()
|
||||
supportFragmentManager
|
||||
.beginTransaction()
|
||||
.add(R.id.content, browseFragment!!, "BrowseFragment")
|
||||
.commit()
|
||||
|
||||
// Set display parameters for the BrowseFragment
|
||||
browseFragment?.headersState = BrowseSupportFragment.HEADERS_ENABLED
|
||||
browseFragment?.brandColor = ContextCompat.getColor(this, R.color.dolphin_blue)
|
||||
buildRowsAdapter()
|
||||
|
||||
browseFragment?.onItemViewClickedListener =
|
||||
OnItemViewClickedListener { itemViewHolder: Presenter.ViewHolder, item: Any?, _: RowPresenter.ViewHolder?, _: Row? ->
|
||||
// Special case: user clicked on a settings row item.
|
||||
if (item is TvSettingsItem) {
|
||||
presenter.handleOptionSelection(item.itemId, this)
|
||||
} else {
|
||||
val holder = itemViewHolder as TvGameViewHolder
|
||||
|
||||
// Start the emulation activity and send the path of the clicked ISO to it.
|
||||
val paths = GameFileCacheManager.findSecondDiscAndGetPaths(holder.gameFile)
|
||||
EmulationActivity.launch(this@TvMainActivity, paths, false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* MainView
|
||||
*/
|
||||
override fun setVersionString(version: String) {
|
||||
browseFragment?.title = version
|
||||
}
|
||||
|
||||
override fun launchSettingsActivity(menuTag: MenuTag?) {
|
||||
SettingsActivity.launch(this, menuTag)
|
||||
}
|
||||
|
||||
override fun launchFileListActivity() {
|
||||
if (DirectoryInitialization.preferOldFolderPicker(this)) {
|
||||
FileBrowserHelper.openDirectoryPicker(this, FileBrowserHelper.GAME_EXTENSIONS)
|
||||
} else {
|
||||
val intent = Intent(Intent.ACTION_OPEN_DOCUMENT_TREE)
|
||||
startActivityForResult(intent, MainPresenter.REQUEST_DIRECTORY)
|
||||
}
|
||||
}
|
||||
|
||||
override fun launchOpenFileActivity(requestCode: Int) {
|
||||
val intent = Intent(Intent.ACTION_OPEN_DOCUMENT)
|
||||
intent.addCategory(Intent.CATEGORY_OPENABLE)
|
||||
intent.type = "*/*"
|
||||
startActivityForResult(intent, requestCode)
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows or hides the loading indicator.
|
||||
*/
|
||||
override fun setRefreshing(refreshing: Boolean) {
|
||||
swipeRefresh?.isRefreshing = refreshing
|
||||
}
|
||||
|
||||
override fun showGames() {
|
||||
// Kicks off the program services to update all channels
|
||||
TvUtil.updateAllChannels(applicationContext)
|
||||
|
||||
buildRowsAdapter()
|
||||
}
|
||||
|
||||
override fun reloadGrid() {
|
||||
for (row in gameRows) {
|
||||
row.notifyArrayItemRangeChanged(0, row.size())
|
||||
}
|
||||
}
|
||||
|
||||
override fun showGridOptions() {
|
||||
GridOptionDialogFragment().show(supportFragmentManager, "gridOptions")
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback from AddDirectoryActivity. Applies any changes necessary to the GameGridActivity.
|
||||
*
|
||||
* @param requestCode An int describing whether the Activity that is returning did so successfully.
|
||||
* @param resultCode An int describing what Activity is giving us this callback.
|
||||
* @param result The information the returning Activity is providing us.
|
||||
*/
|
||||
override fun onActivityResult(requestCode: Int, resultCode: Int, result: Intent?) {
|
||||
super.onActivityResult(requestCode, resultCode, result)
|
||||
|
||||
// If the user picked a file, as opposed to just backing out.
|
||||
if (resultCode == RESULT_OK) {
|
||||
val uri = result!!.data
|
||||
when (requestCode) {
|
||||
MainPresenter.REQUEST_DIRECTORY -> {
|
||||
if (DirectoryInitialization.preferOldFolderPicker(this)) {
|
||||
presenter.onDirectorySelected(FileBrowserHelper.getSelectedPath(result))
|
||||
} else {
|
||||
presenter.onDirectorySelected(result)
|
||||
}
|
||||
}
|
||||
|
||||
MainPresenter.REQUEST_GAME_FILE -> FileBrowserHelper.runAfterExtensionCheck(
|
||||
this, uri, FileBrowserHelper.GAME_LIKE_EXTENSIONS
|
||||
) { EmulationActivity.launch(this, result.data.toString(), false) }
|
||||
|
||||
MainPresenter.REQUEST_WAD_FILE -> FileBrowserHelper.runAfterExtensionCheck(
|
||||
this, uri, FileBrowserHelper.WAD_EXTENSION
|
||||
) { presenter.installWAD(result.data.toString()) }
|
||||
|
||||
MainPresenter.REQUEST_WII_SAVE_FILE -> FileBrowserHelper.runAfterExtensionCheck(
|
||||
this, uri, FileBrowserHelper.BIN_EXTENSION
|
||||
) { presenter.importWiiSave(result.data.toString()) }
|
||||
|
||||
MainPresenter.REQUEST_NAND_BIN_FILE -> FileBrowserHelper.runAfterExtensionCheck(
|
||||
this, uri, FileBrowserHelper.BIN_EXTENSION
|
||||
) { presenter.importNANDBin(result.data.toString()) }
|
||||
}
|
||||
} else {
|
||||
MainPresenter.skipRescanningLibrary()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onRequestPermissionsResult(
|
||||
requestCode: Int,
|
||||
permissions: Array<String>,
|
||||
grantResults: IntArray
|
||||
) {
|
||||
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
|
||||
if (requestCode == PermissionsHandler.REQUEST_CODE_WRITE_PERMISSION) {
|
||||
if (grantResults[0] == PackageManager.PERMISSION_DENIED) {
|
||||
PermissionsHandler.setWritePermissionDenied()
|
||||
}
|
||||
|
||||
DirectoryInitialization.start(this)
|
||||
GameFileCacheManager.startLoad()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the user requests a refresh by swiping down.
|
||||
*/
|
||||
override fun onRefresh() {
|
||||
setRefreshing(true)
|
||||
GameFileCacheManager.startRescan()
|
||||
}
|
||||
|
||||
private fun buildRowsAdapter() {
|
||||
val rowsAdapter = ArrayObjectAdapter(ListRowPresenter())
|
||||
gameRows.clear()
|
||||
|
||||
if (!DirectoryInitialization.isWaitingForWriteAccess(this)) {
|
||||
GameFileCacheManager.startLoad()
|
||||
}
|
||||
|
||||
for (platform in Platform.values()) {
|
||||
val row =
|
||||
buildGamesRow(platform, GameFileCacheManager.getGameFilesForPlatform(platform))
|
||||
|
||||
// Add row to the adapter only if it is not empty.
|
||||
if (row != null) {
|
||||
rowsAdapter.add(row)
|
||||
}
|
||||
}
|
||||
|
||||
rowsAdapter.add(buildSettingsRow())
|
||||
|
||||
browseFragment!!.adapter = rowsAdapter
|
||||
}
|
||||
|
||||
private fun buildGamesRow(platform: Platform, gameFiles: Collection<GameFile?>): ListRow? {
|
||||
// If there are no games, don't return a Row.
|
||||
if (gameFiles.isEmpty()) {
|
||||
return null
|
||||
}
|
||||
|
||||
// Create an adapter for this row.
|
||||
val row = ArrayObjectAdapter(GameRowPresenter(this))
|
||||
row.addAll(0, gameFiles)
|
||||
|
||||
// Keep a reference to the row in case we need to refresh it.
|
||||
gameRows.add(row)
|
||||
|
||||
// Create a header for this row.
|
||||
val header = HeaderItem(platform.toInt().toLong(), getString(platform.headerName))
|
||||
|
||||
// Create the row, passing it the filled adapter and the header, and give it to the master adapter.
|
||||
return ListRow(header, row)
|
||||
}
|
||||
|
||||
private fun buildSettingsRow(): ListRow {
|
||||
val rowItems = ArrayObjectAdapter(SettingsRowPresenter())
|
||||
rowItems.apply {
|
||||
add(
|
||||
TvSettingsItem(
|
||||
R.id.menu_settings,
|
||||
R.drawable.ic_settings_tv,
|
||||
R.string.grid_menu_settings
|
||||
)
|
||||
)
|
||||
add(
|
||||
TvSettingsItem(
|
||||
R.id.button_add_directory,
|
||||
R.drawable.ic_add_tv,
|
||||
R.string.add_directory_title
|
||||
)
|
||||
)
|
||||
add(
|
||||
TvSettingsItem(
|
||||
R.id.menu_grid_options,
|
||||
R.drawable.ic_list_tv,
|
||||
R.string.grid_menu_grid_options
|
||||
)
|
||||
)
|
||||
add(
|
||||
TvSettingsItem(
|
||||
R.id.menu_refresh,
|
||||
R.drawable.ic_refresh_tv,
|
||||
R.string.grid_menu_refresh
|
||||
)
|
||||
)
|
||||
add(
|
||||
TvSettingsItem(
|
||||
R.id.menu_open_file,
|
||||
R.drawable.ic_play_tv,
|
||||
R.string.grid_menu_open_file
|
||||
)
|
||||
)
|
||||
add(
|
||||
TvSettingsItem(
|
||||
R.id.menu_install_wad,
|
||||
R.drawable.ic_folder_tv,
|
||||
R.string.grid_menu_install_wad
|
||||
)
|
||||
)
|
||||
add(
|
||||
TvSettingsItem(
|
||||
R.id.menu_load_wii_system_menu,
|
||||
R.drawable.ic_folder_tv,
|
||||
R.string.grid_menu_load_wii_system_menu
|
||||
)
|
||||
)
|
||||
add(
|
||||
TvSettingsItem(
|
||||
R.id.menu_import_wii_save,
|
||||
R.drawable.ic_folder_tv,
|
||||
R.string.grid_menu_import_wii_save
|
||||
)
|
||||
)
|
||||
add(
|
||||
TvSettingsItem(
|
||||
R.id.menu_import_nand_backup,
|
||||
R.drawable.ic_folder_tv,
|
||||
R.string.grid_menu_import_nand_backup
|
||||
)
|
||||
)
|
||||
add(
|
||||
TvSettingsItem(
|
||||
R.id.menu_online_system_update,
|
||||
R.drawable.ic_folder_tv,
|
||||
R.string.grid_menu_online_system_update
|
||||
)
|
||||
)
|
||||
add(
|
||||
TvSettingsItem(
|
||||
R.id.menu_about,
|
||||
R.drawable.ic_info_tv,
|
||||
R.string.grid_menu_about
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
// Create a header for this row.
|
||||
val header = HeaderItem(R.string.settings.toLong(), getString(R.string.settings))
|
||||
return ListRow(header, rowItems)
|
||||
}
|
||||
}
|
@ -1,58 +0,0 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
package org.dolphinemu.dolphinemu.ui.platform;
|
||||
|
||||
import org.dolphinemu.dolphinemu.R;
|
||||
|
||||
/**
|
||||
* Enum to represent platform (eg GameCube, Wii).
|
||||
*/
|
||||
public enum Platform
|
||||
{
|
||||
GAMECUBE(0, R.string.platform_gamecube, "GameCube Games"),
|
||||
WII(1, R.string.platform_wii, "Wii Games"),
|
||||
WIIWARE(2, R.string.platform_wiiware, "WiiWare Games");
|
||||
|
||||
private final int value;
|
||||
private final int headerName;
|
||||
private final String idString;
|
||||
|
||||
Platform(int value, int headerName, String idString)
|
||||
{
|
||||
this.value = value;
|
||||
this.headerName = headerName;
|
||||
this.idString = idString;
|
||||
}
|
||||
|
||||
public static Platform fromInt(int i)
|
||||
{
|
||||
return values()[i];
|
||||
}
|
||||
|
||||
public static Platform fromNativeInt(int i)
|
||||
{
|
||||
// TODO: Proper support for DOL and ELF files
|
||||
boolean in_range = i >= 0 && i < values().length;
|
||||
return values()[in_range ? i : WIIWARE.value];
|
||||
}
|
||||
|
||||
public static Platform fromPosition(int position)
|
||||
{
|
||||
return values()[position];
|
||||
}
|
||||
|
||||
public int toInt()
|
||||
{
|
||||
return value;
|
||||
}
|
||||
|
||||
public int getHeaderName()
|
||||
{
|
||||
return headerName;
|
||||
}
|
||||
|
||||
public String getIdString()
|
||||
{
|
||||
return idString;
|
||||
}
|
||||
}
|
@ -0,0 +1,36 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
package org.dolphinemu.dolphinemu.ui.platform
|
||||
|
||||
import org.dolphinemu.dolphinemu.R
|
||||
|
||||
/**
|
||||
* Enum to represent platform (eg GameCube, Wii).
|
||||
*/
|
||||
enum class Platform(private val value: Int, val headerName: Int, val idString: String) {
|
||||
GAMECUBE(0, R.string.platform_gamecube, "GameCube Games"),
|
||||
WII(1, R.string.platform_wii, "Wii Games"),
|
||||
WIIWARE(2, R.string.platform_wiiware, "WiiWare Games");
|
||||
|
||||
fun toInt(): Int {
|
||||
return value
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun fromInt(i: Int): Platform {
|
||||
return values()[i]
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun fromNativeInt(i: Int): Platform {
|
||||
// TODO: Proper support for DOL and ELF files
|
||||
val inRange = i >= 0 && i < values().size
|
||||
return values()[if (inRange) i else WIIWARE.value]
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun fromPosition(position: Int): Platform {
|
||||
return values()[position]
|
||||
}
|
||||
}
|
||||
}
|
@ -1,147 +0,0 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
package org.dolphinemu.dolphinemu.ui.platform;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.core.graphics.Insets;
|
||||
import androidx.core.view.ViewCompat;
|
||||
import androidx.core.view.WindowInsetsCompat;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
|
||||
|
||||
import com.google.android.material.color.MaterialColors;
|
||||
|
||||
import org.dolphinemu.dolphinemu.R;
|
||||
import org.dolphinemu.dolphinemu.adapters.GameAdapter;
|
||||
import org.dolphinemu.dolphinemu.databinding.FragmentGridBinding;
|
||||
import org.dolphinemu.dolphinemu.layout.AutofitGridLayoutManager;
|
||||
import org.dolphinemu.dolphinemu.services.GameFileCacheManager;
|
||||
|
||||
public final class PlatformGamesFragment extends Fragment implements PlatformGamesView
|
||||
{
|
||||
private static final String ARG_PLATFORM = "platform";
|
||||
|
||||
private SwipeRefreshLayout mSwipeRefresh;
|
||||
private SwipeRefreshLayout.OnRefreshListener mOnRefreshListener;
|
||||
|
||||
private FragmentGridBinding mBinding;
|
||||
|
||||
public static PlatformGamesFragment newInstance(Platform platform)
|
||||
{
|
||||
PlatformGamesFragment fragment = new PlatformGamesFragment();
|
||||
|
||||
Bundle args = new Bundle();
|
||||
args.putSerializable(ARG_PLATFORM, platform);
|
||||
|
||||
fragment.setArguments(args);
|
||||
return fragment;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState)
|
||||
{
|
||||
super.onCreate(savedInstanceState);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container,
|
||||
Bundle savedInstanceState)
|
||||
{
|
||||
mBinding = FragmentGridBinding.inflate(inflater, container, false);
|
||||
return mBinding.getRoot();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onViewCreated(@NonNull View view, Bundle savedInstanceState)
|
||||
{
|
||||
mSwipeRefresh = mBinding.swipeRefresh;
|
||||
GameAdapter adapter = new GameAdapter(requireActivity());
|
||||
adapter.setStateRestorationPolicy(
|
||||
RecyclerView.Adapter.StateRestorationPolicy.PREVENT_WHEN_EMPTY);
|
||||
mBinding.gridGames.setAdapter(adapter);
|
||||
mBinding.gridGames.setLayoutManager(new AutofitGridLayoutManager(requireContext(),
|
||||
getResources().getDimensionPixelSize(R.dimen.card_width)));
|
||||
|
||||
// Set theme color to the refresh animation's background
|
||||
mSwipeRefresh.setProgressBackgroundColorSchemeColor(
|
||||
MaterialColors.getColor(mSwipeRefresh, R.attr.colorPrimary));
|
||||
mSwipeRefresh.setColorSchemeColors(
|
||||
MaterialColors.getColor(mSwipeRefresh, R.attr.colorOnPrimary));
|
||||
|
||||
mSwipeRefresh.setOnRefreshListener(mOnRefreshListener);
|
||||
|
||||
setInsets();
|
||||
|
||||
setRefreshing(GameFileCacheManager.isLoadingOrRescanning());
|
||||
|
||||
showGames();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroyView()
|
||||
{
|
||||
super.onDestroyView();
|
||||
mBinding = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onItemClick(String gameId)
|
||||
{
|
||||
// No-op for now
|
||||
}
|
||||
|
||||
@Override
|
||||
public void showGames()
|
||||
{
|
||||
if (mBinding == null)
|
||||
return;
|
||||
|
||||
if (mBinding.gridGames.getAdapter() != null)
|
||||
{
|
||||
Platform platform = (Platform) getArguments().getSerializable(ARG_PLATFORM);
|
||||
((GameAdapter) mBinding.gridGames.getAdapter()).swapDataSet(
|
||||
GameFileCacheManager.getGameFilesForPlatform(platform));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void refetchMetadata()
|
||||
{
|
||||
((GameAdapter) mBinding.gridGames.getAdapter()).refetchMetadata();
|
||||
}
|
||||
|
||||
public void setOnRefreshListener(@Nullable SwipeRefreshLayout.OnRefreshListener listener)
|
||||
{
|
||||
mOnRefreshListener = listener;
|
||||
|
||||
if (mSwipeRefresh != null)
|
||||
{
|
||||
mSwipeRefresh.setOnRefreshListener(listener);
|
||||
}
|
||||
}
|
||||
|
||||
public void setRefreshing(boolean refreshing)
|
||||
{
|
||||
mBinding.swipeRefresh.setRefreshing(refreshing);
|
||||
}
|
||||
|
||||
private void setInsets()
|
||||
{
|
||||
ViewCompat.setOnApplyWindowInsetsListener(mBinding.gridGames, (v, windowInsets) ->
|
||||
{
|
||||
Insets insets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars());
|
||||
v.setPadding(0, 0, 0,
|
||||
insets.bottom + getResources().getDimensionPixelSize(R.dimen.spacing_list) +
|
||||
getResources().getDimensionPixelSize(R.dimen.spacing_fab));
|
||||
return windowInsets;
|
||||
});
|
||||
}
|
||||
}
|
@ -0,0 +1,122 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
package org.dolphinemu.dolphinemu.ui.platform
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.core.view.ViewCompat
|
||||
import androidx.core.view.WindowInsetsCompat
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
|
||||
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout.OnRefreshListener
|
||||
import com.google.android.material.color.MaterialColors
|
||||
import org.dolphinemu.dolphinemu.R
|
||||
import org.dolphinemu.dolphinemu.adapters.GameAdapter
|
||||
import org.dolphinemu.dolphinemu.databinding.FragmentGridBinding
|
||||
import org.dolphinemu.dolphinemu.layout.AutofitGridLayoutManager
|
||||
import org.dolphinemu.dolphinemu.services.GameFileCacheManager
|
||||
|
||||
class PlatformGamesFragment : Fragment(), PlatformGamesView {
|
||||
private var swipeRefresh: SwipeRefreshLayout? = null
|
||||
private var onRefreshListener: OnRefreshListener? = null
|
||||
|
||||
private var _binding: FragmentGridBinding? = null
|
||||
private val binding: FragmentGridBinding get() = _binding!!
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View {
|
||||
_binding = FragmentGridBinding.inflate(inflater, container, false)
|
||||
return binding.root
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
swipeRefresh = binding.swipeRefresh
|
||||
val gameAdapter = GameAdapter(requireActivity())
|
||||
gameAdapter.stateRestorationPolicy =
|
||||
RecyclerView.Adapter.StateRestorationPolicy.PREVENT_WHEN_EMPTY
|
||||
|
||||
binding.gridGames.apply {
|
||||
adapter = gameAdapter
|
||||
layoutManager = AutofitGridLayoutManager(
|
||||
requireContext(),
|
||||
resources.getDimensionPixelSize(R.dimen.card_width)
|
||||
)
|
||||
}
|
||||
|
||||
// Set theme color to the refresh animation's background
|
||||
binding.swipeRefresh.apply {
|
||||
setProgressBackgroundColorSchemeColor(
|
||||
MaterialColors.getColor(swipeRefresh!!, R.attr.colorPrimary)
|
||||
)
|
||||
setColorSchemeColors(MaterialColors.getColor(swipeRefresh!!, R.attr.colorOnPrimary))
|
||||
setOnRefreshListener(onRefreshListener)
|
||||
}
|
||||
|
||||
setInsets()
|
||||
setRefreshing(GameFileCacheManager.isLoadingOrRescanning())
|
||||
showGames()
|
||||
}
|
||||
|
||||
override fun onDestroyView() {
|
||||
super.onDestroyView()
|
||||
_binding = null
|
||||
}
|
||||
|
||||
override fun showGames() {
|
||||
if (_binding == null)
|
||||
return
|
||||
|
||||
if (binding.gridGames.adapter != null) {
|
||||
val platform = requireArguments().getSerializable(ARG_PLATFORM) as Platform
|
||||
(binding.gridGames.adapter as GameAdapter?)!!.swapDataSet(
|
||||
GameFileCacheManager.getGameFilesForPlatform(platform)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override fun refetchMetadata() {
|
||||
(binding.gridGames.adapter as GameAdapter).refetchMetadata()
|
||||
}
|
||||
|
||||
fun setOnRefreshListener(listener: OnRefreshListener?) {
|
||||
onRefreshListener = listener
|
||||
swipeRefresh?.setOnRefreshListener(listener)
|
||||
}
|
||||
|
||||
override fun setRefreshing(refreshing: Boolean) {
|
||||
binding.swipeRefresh.isRefreshing = refreshing
|
||||
}
|
||||
|
||||
private fun setInsets() {
|
||||
ViewCompat.setOnApplyWindowInsetsListener(binding.gridGames) { v: View, windowInsets: WindowInsetsCompat ->
|
||||
val insets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars())
|
||||
v.setPadding(
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
insets.bottom + resources.getDimensionPixelSize(R.dimen.spacing_list)
|
||||
+ resources.getDimensionPixelSize(R.dimen.spacing_fab)
|
||||
)
|
||||
windowInsets
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val ARG_PLATFORM = "platform"
|
||||
|
||||
@JvmStatic
|
||||
fun newInstance(platform: Platform?): PlatformGamesFragment {
|
||||
val fragment = PlatformGamesFragment()
|
||||
val args = Bundle()
|
||||
args.putSerializable(ARG_PLATFORM, platform)
|
||||
fragment.arguments = args
|
||||
return fragment
|
||||
}
|
||||
}
|
||||
}
|
@ -1,32 +0,0 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
package org.dolphinemu.dolphinemu.ui.platform;
|
||||
|
||||
/**
|
||||
* Abstraction for a screen representing a single platform's games.
|
||||
*/
|
||||
public interface PlatformGamesView
|
||||
{
|
||||
/**
|
||||
* Pass a click event to the view's Presenter. Typically called from the
|
||||
* view's list adapter.
|
||||
*
|
||||
* @param gameId The ID of the game that was clicked.
|
||||
*/
|
||||
void onItemClick(String gameId);
|
||||
|
||||
/**
|
||||
* Shows or hides the loading indicator.
|
||||
*/
|
||||
void setRefreshing(boolean refreshing);
|
||||
|
||||
/**
|
||||
* To be called when the game file cache is updated.
|
||||
*/
|
||||
void showGames();
|
||||
|
||||
/**
|
||||
* Re-fetches game metadata from the game file cache.
|
||||
*/
|
||||
void refetchMetadata();
|
||||
}
|
@ -0,0 +1,31 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
package org.dolphinemu.dolphinemu.ui.platform
|
||||
|
||||
/**
|
||||
* Abstraction for a screen representing a single platform's games.
|
||||
*/
|
||||
interface PlatformGamesView {
|
||||
/**
|
||||
* Pass a click event to the view's Presenter. Typically called from the
|
||||
* view's list adapter.
|
||||
*
|
||||
* @param gameId The ID of the game that was clicked.
|
||||
*/
|
||||
fun onItemClick(gameId: String) { /* Empty default impl */ }
|
||||
|
||||
/**
|
||||
* Shows or hides the loading indicator.
|
||||
*/
|
||||
fun setRefreshing(refreshing: Boolean)
|
||||
|
||||
/**
|
||||
* To be called when the game file cache is updated.
|
||||
*/
|
||||
fun showGames()
|
||||
|
||||
/**
|
||||
* Re-fetches game metadata from the game file cache.
|
||||
*/
|
||||
fun refetchMetadata()
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user