Merge pull request #9530 from JosJuice/android-loading-indicator
Android: Use SwipeRefreshLayout in MainActivity
This commit is contained in:
commit
794e093223
|
@ -67,8 +67,7 @@ public class AppLinkActivity extends FragmentActivity
|
||||||
mAfterDirectoryInitializationRunner = new AfterDirectoryInitializationRunner();
|
mAfterDirectoryInitializationRunner = new AfterDirectoryInitializationRunner();
|
||||||
mAfterDirectoryInitializationRunner.run(this, true, () -> tryPlay(playAction));
|
mAfterDirectoryInitializationRunner.run(this, true, () -> tryPlay(playAction));
|
||||||
|
|
||||||
IntentFilter gameFileCacheIntentFilter = new IntentFilter(
|
IntentFilter gameFileCacheIntentFilter = new IntentFilter(GameFileCacheService.DONE_LOADING);
|
||||||
GameFileCacheService.BROADCAST_ACTION);
|
|
||||||
|
|
||||||
BroadcastReceiver gameFileCacheReceiver = new BroadcastReceiver()
|
BroadcastReceiver gameFileCacheReceiver = new BroadcastReceiver()
|
||||||
{
|
{
|
||||||
|
@ -109,7 +108,7 @@ public class AppLinkActivity extends FragmentActivity
|
||||||
|
|
||||||
// If game == null and the load isn't done, wait for the next GameFileCacheService broadcast.
|
// If game == null and the load isn't done, wait for the next GameFileCacheService broadcast.
|
||||||
// If game == null and the load is done, call play with a null game, making us exit in failure.
|
// If game == null and the load is done, call play with a null game, making us exit in failure.
|
||||||
if (game != null || GameFileCacheService.hasLoadedCache())
|
if (game != null || !GameFileCacheService.isLoading())
|
||||||
{
|
{
|
||||||
play(action, game);
|
play(action, game);
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,6 +10,7 @@ import androidx.annotation.NonNull;
|
||||||
import androidx.fragment.app.Fragment;
|
import androidx.fragment.app.Fragment;
|
||||||
import androidx.fragment.app.FragmentManager;
|
import androidx.fragment.app.FragmentManager;
|
||||||
import androidx.fragment.app.FragmentPagerAdapter;
|
import androidx.fragment.app.FragmentPagerAdapter;
|
||||||
|
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
|
||||||
|
|
||||||
import org.dolphinemu.dolphinemu.R;
|
import org.dolphinemu.dolphinemu.R;
|
||||||
import org.dolphinemu.dolphinemu.ui.platform.Platform;
|
import org.dolphinemu.dolphinemu.ui.platform.Platform;
|
||||||
|
@ -18,6 +19,7 @@ import org.dolphinemu.dolphinemu.ui.platform.PlatformGamesFragment;
|
||||||
public class PlatformPagerAdapter extends FragmentPagerAdapter
|
public class PlatformPagerAdapter extends FragmentPagerAdapter
|
||||||
{
|
{
|
||||||
private Context mContext;
|
private Context mContext;
|
||||||
|
private SwipeRefreshLayout.OnRefreshListener mOnRefreshListener;
|
||||||
|
|
||||||
private final static int[] TAB_ICONS =
|
private final static int[] TAB_ICONS =
|
||||||
{
|
{
|
||||||
|
@ -26,17 +28,19 @@ public class PlatformPagerAdapter extends FragmentPagerAdapter
|
||||||
R.drawable.ic_folder
|
R.drawable.ic_folder
|
||||||
};
|
};
|
||||||
|
|
||||||
public PlatformPagerAdapter(FragmentManager fm, Context context)
|
public PlatformPagerAdapter(FragmentManager fm, Context context,
|
||||||
|
SwipeRefreshLayout.OnRefreshListener onRefreshListener)
|
||||||
{
|
{
|
||||||
super(fm, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT);
|
super(fm, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT);
|
||||||
mContext = context;
|
mContext = context;
|
||||||
|
mOnRefreshListener = onRefreshListener;
|
||||||
}
|
}
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
@Override
|
@Override
|
||||||
public Fragment getItem(int position)
|
public Fragment getItem(int position)
|
||||||
{
|
{
|
||||||
return PlatformGamesFragment.newInstance(Platform.fromPosition(position));
|
return PlatformGamesFragment.newInstance(Platform.fromPosition(position), mOnRefreshListener);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -116,6 +116,8 @@ public class GameFileCache
|
||||||
return cacheChanged;
|
return cacheChanged;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public native int getSize();
|
||||||
|
|
||||||
public native GameFile[] getAllGames();
|
public native GameFile[] getAllGames();
|
||||||
|
|
||||||
public native GameFile addOrGet(String gamePath);
|
public native GameFile addOrGet(String gamePath);
|
||||||
|
|
|
@ -15,7 +15,7 @@ import java.io.File;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.concurrent.atomic.AtomicBoolean;
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
import java.util.concurrent.atomic.AtomicReference;
|
import java.util.concurrent.atomic.AtomicReference;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -23,7 +23,17 @@ import java.util.concurrent.atomic.AtomicReference;
|
||||||
*/
|
*/
|
||||||
public final class GameFileCacheService extends IntentService
|
public final class GameFileCacheService extends IntentService
|
||||||
{
|
{
|
||||||
public static final String BROADCAST_ACTION = "org.dolphinemu.dolphinemu.GAME_FILE_CACHE_UPDATED";
|
/**
|
||||||
|
* This is broadcast when the contents of the cache change.
|
||||||
|
*/
|
||||||
|
public static final String CACHE_UPDATED = "org.dolphinemu.dolphinemu.GAME_FILE_CACHE_UPDATED";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is broadcast when the service is done with all requested work, regardless of whether
|
||||||
|
* the contents of the cache actually changed. (Maybe the cache was already up to date.)
|
||||||
|
*/
|
||||||
|
public static final String DONE_LOADING =
|
||||||
|
"org.dolphinemu.dolphinemu.GAME_FILE_CACHE_DONE_LOADING";
|
||||||
|
|
||||||
private static final String ACTION_LOAD = "org.dolphinemu.dolphinemu.LOAD_GAME_FILE_CACHE";
|
private static final String ACTION_LOAD = "org.dolphinemu.dolphinemu.LOAD_GAME_FILE_CACHE";
|
||||||
private static final String ACTION_RESCAN = "org.dolphinemu.dolphinemu.RESCAN_GAME_FILE_CACHE";
|
private static final String ACTION_RESCAN = "org.dolphinemu.dolphinemu.RESCAN_GAME_FILE_CACHE";
|
||||||
|
@ -31,8 +41,7 @@ public final class GameFileCacheService extends IntentService
|
||||||
private static GameFileCache gameFileCache = null;
|
private static GameFileCache gameFileCache = null;
|
||||||
private static final AtomicReference<GameFile[]> gameFiles =
|
private static final AtomicReference<GameFile[]> gameFiles =
|
||||||
new AtomicReference<>(new GameFile[]{});
|
new AtomicReference<>(new GameFile[]{});
|
||||||
private static final AtomicBoolean hasLoadedCache = new AtomicBoolean(false);
|
private static final AtomicInteger unhandledIntents = new AtomicInteger(0);
|
||||||
private static final AtomicBoolean hasScannedLibrary = new AtomicBoolean(false);
|
|
||||||
|
|
||||||
public GameFileCacheService()
|
public GameFileCacheService()
|
||||||
{
|
{
|
||||||
|
@ -96,14 +105,9 @@ public final class GameFileCacheService extends IntentService
|
||||||
return new String[]{gameFile.getPath(), secondFile.getPath()};
|
return new String[]{gameFile.getPath(), secondFile.getPath()};
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean hasLoadedCache()
|
public static boolean isLoading()
|
||||||
{
|
{
|
||||||
return hasLoadedCache.get();
|
return unhandledIntents.get() != 0;
|
||||||
}
|
|
||||||
|
|
||||||
public static boolean hasScannedLibrary()
|
|
||||||
{
|
|
||||||
return hasScannedLibrary.get();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void startService(Context context, String action)
|
private static void startService(Context context, String action)
|
||||||
|
@ -119,6 +123,8 @@ public final class GameFileCacheService extends IntentService
|
||||||
*/
|
*/
|
||||||
public static void startLoad(Context context)
|
public static void startLoad(Context context)
|
||||||
{
|
{
|
||||||
|
unhandledIntents.getAndIncrement();
|
||||||
|
|
||||||
new AfterDirectoryInitializationRunner().run(context, false,
|
new AfterDirectoryInitializationRunner().run(context, false,
|
||||||
() -> startService(context, ACTION_LOAD));
|
() -> startService(context, ACTION_LOAD));
|
||||||
}
|
}
|
||||||
|
@ -130,6 +136,8 @@ public final class GameFileCacheService extends IntentService
|
||||||
*/
|
*/
|
||||||
public static void startRescan(Context context)
|
public static void startRescan(Context context)
|
||||||
{
|
{
|
||||||
|
unhandledIntents.getAndIncrement();
|
||||||
|
|
||||||
new AfterDirectoryInitializationRunner().run(context, false,
|
new AfterDirectoryInitializationRunner().run(context, false,
|
||||||
() -> startService(context, ACTION_RESCAN));
|
() -> startService(context, ACTION_RESCAN));
|
||||||
}
|
}
|
||||||
|
@ -156,9 +164,11 @@ public final class GameFileCacheService extends IntentService
|
||||||
{
|
{
|
||||||
gameFileCache = temp;
|
gameFileCache = temp;
|
||||||
gameFileCache.load();
|
gameFileCache.load();
|
||||||
updateGameFileArray();
|
if (gameFileCache.getSize() != 0)
|
||||||
hasLoadedCache.set(true);
|
{
|
||||||
sendBroadcast();
|
updateGameFileArray();
|
||||||
|
sendBroadcast(CACHE_UPDATED);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -169,11 +179,18 @@ public final class GameFileCacheService extends IntentService
|
||||||
{
|
{
|
||||||
boolean changed = gameFileCache.scanLibrary();
|
boolean changed = gameFileCache.scanLibrary();
|
||||||
if (changed)
|
if (changed)
|
||||||
|
{
|
||||||
updateGameFileArray();
|
updateGameFileArray();
|
||||||
hasScannedLibrary.set(true);
|
sendBroadcast(CACHE_UPDATED);
|
||||||
sendBroadcast();
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int intentsLeft = unhandledIntents.decrementAndGet();
|
||||||
|
if (intentsLeft == 0)
|
||||||
|
{
|
||||||
|
sendBroadcast(DONE_LOADING);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateGameFileArray()
|
private void updateGameFileArray()
|
||||||
|
@ -183,8 +200,8 @@ public final class GameFileCacheService extends IntentService
|
||||||
gameFiles.set(gameFilesTemp);
|
gameFiles.set(gameFilesTemp);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void sendBroadcast()
|
private void sendBroadcast(String action)
|
||||||
{
|
{
|
||||||
LocalBroadcastManager.getInstance(this).sendBroadcast(new Intent(BROADCAST_ACTION));
|
LocalBroadcastManager.getInstance(this).sendBroadcast(new Intent(action));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,6 +13,7 @@ import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import androidx.appcompat.app.AppCompatActivity;
|
import androidx.appcompat.app.AppCompatActivity;
|
||||||
import androidx.appcompat.widget.Toolbar;
|
import androidx.appcompat.widget.Toolbar;
|
||||||
|
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
|
||||||
import androidx.viewpager.widget.ViewPager;
|
import androidx.viewpager.widget.ViewPager;
|
||||||
|
|
||||||
import com.google.android.material.floatingactionbutton.FloatingActionButton;
|
import com.google.android.material.floatingactionbutton.FloatingActionButton;
|
||||||
|
@ -28,6 +29,7 @@ import org.dolphinemu.dolphinemu.features.settings.ui.SettingsActivity;
|
||||||
import org.dolphinemu.dolphinemu.services.GameFileCacheService;
|
import org.dolphinemu.dolphinemu.services.GameFileCacheService;
|
||||||
import org.dolphinemu.dolphinemu.ui.platform.Platform;
|
import org.dolphinemu.dolphinemu.ui.platform.Platform;
|
||||||
import org.dolphinemu.dolphinemu.ui.platform.PlatformGamesView;
|
import org.dolphinemu.dolphinemu.ui.platform.PlatformGamesView;
|
||||||
|
import org.dolphinemu.dolphinemu.utils.Action1;
|
||||||
import org.dolphinemu.dolphinemu.utils.AfterDirectoryInitializationRunner;
|
import org.dolphinemu.dolphinemu.utils.AfterDirectoryInitializationRunner;
|
||||||
import org.dolphinemu.dolphinemu.utils.DirectoryInitialization;
|
import org.dolphinemu.dolphinemu.utils.DirectoryInitialization;
|
||||||
import org.dolphinemu.dolphinemu.utils.FileBrowserHelper;
|
import org.dolphinemu.dolphinemu.utils.FileBrowserHelper;
|
||||||
|
@ -38,7 +40,8 @@ import org.dolphinemu.dolphinemu.utils.StartupHandler;
|
||||||
* The main Activity of the Lollipop style UI. Manages several PlatformGamesFragments, which
|
* 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.
|
* individually display a grid of available games for each Fragment, in a tabbed layout.
|
||||||
*/
|
*/
|
||||||
public final class MainActivity extends AppCompatActivity implements MainView
|
public final class MainActivity extends AppCompatActivity
|
||||||
|
implements MainView, SwipeRefreshLayout.OnRefreshListener
|
||||||
{
|
{
|
||||||
private ViewPager mViewPager;
|
private ViewPager mViewPager;
|
||||||
private Toolbar mToolbar;
|
private Toolbar mToolbar;
|
||||||
|
@ -79,6 +82,8 @@ public final class MainActivity extends AppCompatActivity implements MainView
|
||||||
{
|
{
|
||||||
super.onResume();
|
super.onResume();
|
||||||
|
|
||||||
|
boolean cacheAlreadyLoading = GameFileCacheService.isLoading();
|
||||||
|
|
||||||
if (DirectoryInitialization.shouldStart(this))
|
if (DirectoryInitialization.shouldStart(this))
|
||||||
{
|
{
|
||||||
DirectoryInitialization.start(this);
|
DirectoryInitialization.start(this);
|
||||||
|
@ -90,16 +95,18 @@ public final class MainActivity extends AppCompatActivity implements MainView
|
||||||
|
|
||||||
// In case the user changed a setting that affects how games are displayed,
|
// In case the user changed a setting that affects how games are displayed,
|
||||||
// such as system language, cover downloading...
|
// such as system language, cover downloading...
|
||||||
refetchMetadata();
|
forEachPlatformGamesView(PlatformGamesView::refetchMetadata);
|
||||||
|
|
||||||
if (sShouldRescanLibrary)
|
if (sShouldRescanLibrary && !cacheAlreadyLoading)
|
||||||
{
|
{
|
||||||
GameFileCacheService.startRescan(this);
|
new AfterDirectoryInitializationRunner().run(this, false, () ->
|
||||||
}
|
{
|
||||||
else
|
setRefreshing(true);
|
||||||
{
|
GameFileCacheService.startRescan(this);
|
||||||
sShouldRescanLibrary = true;
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sShouldRescanLibrary = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -266,26 +273,42 @@ public final class MainActivity extends AppCompatActivity implements MainView
|
||||||
return mPresenter.handleOptionSelection(item.getItemId(), this);
|
return mPresenter.handleOptionSelection(item.getItemId(), this);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void showGames()
|
/**
|
||||||
|
* Called when the user requests a refresh by swiping down.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void onRefresh()
|
||||||
{
|
{
|
||||||
for (Platform platform : Platform.values())
|
setRefreshing(true);
|
||||||
{
|
GameFileCacheService.startRescan(this);
|
||||||
PlatformGamesView fragment = getPlatformGamesView(platform);
|
|
||||||
if (fragment != null)
|
|
||||||
{
|
|
||||||
fragment.showGames();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void refetchMetadata()
|
/**
|
||||||
|
* 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void forEachPlatformGamesView(Action1<PlatformGamesView> action)
|
||||||
{
|
{
|
||||||
for (Platform platform : Platform.values())
|
for (Platform platform : Platform.values())
|
||||||
{
|
{
|
||||||
PlatformGamesView fragment = getPlatformGamesView(platform);
|
PlatformGamesView fragment = getPlatformGamesView(platform);
|
||||||
if (fragment != null)
|
if (fragment != null)
|
||||||
{
|
{
|
||||||
fragment.refetchMetadata();
|
action.call(fragment);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -302,7 +325,7 @@ public final class MainActivity extends AppCompatActivity implements MainView
|
||||||
private void setPlatformTabsAndStartGameFileCacheService()
|
private void setPlatformTabsAndStartGameFileCacheService()
|
||||||
{
|
{
|
||||||
PlatformPagerAdapter platformPagerAdapter = new PlatformPagerAdapter(
|
PlatformPagerAdapter platformPagerAdapter = new PlatformPagerAdapter(
|
||||||
getSupportFragmentManager(), this);
|
getSupportFragmentManager(), this, this);
|
||||||
mViewPager.setAdapter(platformPagerAdapter);
|
mViewPager.setAdapter(platformPagerAdapter);
|
||||||
mViewPager.setOffscreenPageLimit(platformPagerAdapter.getCount());
|
mViewPager.setOffscreenPageLimit(platformPagerAdapter.getCount());
|
||||||
mTabLayout.setupWithViewPager(mViewPager);
|
mTabLayout.setupWithViewPager(mViewPager);
|
||||||
|
|
|
@ -53,13 +53,22 @@ public final class MainPresenter
|
||||||
mView.setVersionString(versionName);
|
mView.setVersionString(versionName);
|
||||||
|
|
||||||
IntentFilter filter = new IntentFilter();
|
IntentFilter filter = new IntentFilter();
|
||||||
filter.addAction(GameFileCacheService.BROADCAST_ACTION);
|
filter.addAction(GameFileCacheService.CACHE_UPDATED);
|
||||||
|
filter.addAction(GameFileCacheService.DONE_LOADING);
|
||||||
mBroadcastReceiver = new BroadcastReceiver()
|
mBroadcastReceiver = new BroadcastReceiver()
|
||||||
{
|
{
|
||||||
@Override
|
@Override
|
||||||
public void onReceive(Context context, Intent intent)
|
public void onReceive(Context context, Intent intent)
|
||||||
{
|
{
|
||||||
mView.showGames();
|
switch (intent.getAction())
|
||||||
|
{
|
||||||
|
case GameFileCacheService.CACHE_UPDATED:
|
||||||
|
mView.showGames();
|
||||||
|
break;
|
||||||
|
case GameFileCacheService.DONE_LOADING:
|
||||||
|
mView.setRefreshing(false);
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
LocalBroadcastManager.getInstance(mContext).registerReceiver(mBroadcastReceiver, filter);
|
LocalBroadcastManager.getInstance(mContext).registerReceiver(mBroadcastReceiver, filter);
|
||||||
|
@ -87,6 +96,7 @@ public final class MainPresenter
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
case R.id.menu_refresh:
|
case R.id.menu_refresh:
|
||||||
|
mView.setRefreshing(true);
|
||||||
GameFileCacheService.startRescan(context);
|
GameFileCacheService.startRescan(context);
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
|
|
|
@ -23,6 +23,11 @@ public interface MainView
|
||||||
|
|
||||||
void launchOpenFileActivity(int requestCode);
|
void launchOpenFileActivity(int requestCode);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shows or hides the loading indicator.
|
||||||
|
*/
|
||||||
|
void setRefreshing(boolean refreshing);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* To be called when the game file cache is updated.
|
* To be called when the game file cache is updated.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -4,6 +4,7 @@ import android.content.Intent;
|
||||||
import android.content.pm.PackageManager;
|
import android.content.pm.PackageManager;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
|
import android.util.TypedValue;
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
|
@ -15,6 +16,7 @@ import androidx.leanback.widget.ArrayObjectAdapter;
|
||||||
import androidx.leanback.widget.HeaderItem;
|
import androidx.leanback.widget.HeaderItem;
|
||||||
import androidx.leanback.widget.ListRow;
|
import androidx.leanback.widget.ListRow;
|
||||||
import androidx.leanback.widget.ListRowPresenter;
|
import androidx.leanback.widget.ListRowPresenter;
|
||||||
|
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
|
||||||
|
|
||||||
import org.dolphinemu.dolphinemu.R;
|
import org.dolphinemu.dolphinemu.R;
|
||||||
import org.dolphinemu.dolphinemu.activities.EmulationActivity;
|
import org.dolphinemu.dolphinemu.activities.EmulationActivity;
|
||||||
|
@ -26,6 +28,7 @@ import org.dolphinemu.dolphinemu.model.GameFile;
|
||||||
import org.dolphinemu.dolphinemu.model.TvSettingsItem;
|
import org.dolphinemu.dolphinemu.model.TvSettingsItem;
|
||||||
import org.dolphinemu.dolphinemu.services.GameFileCacheService;
|
import org.dolphinemu.dolphinemu.services.GameFileCacheService;
|
||||||
import org.dolphinemu.dolphinemu.ui.platform.Platform;
|
import org.dolphinemu.dolphinemu.ui.platform.Platform;
|
||||||
|
import org.dolphinemu.dolphinemu.utils.AfterDirectoryInitializationRunner;
|
||||||
import org.dolphinemu.dolphinemu.utils.DirectoryInitialization;
|
import org.dolphinemu.dolphinemu.utils.DirectoryInitialization;
|
||||||
import org.dolphinemu.dolphinemu.utils.FileBrowserHelper;
|
import org.dolphinemu.dolphinemu.utils.FileBrowserHelper;
|
||||||
import org.dolphinemu.dolphinemu.utils.PermissionsHandler;
|
import org.dolphinemu.dolphinemu.utils.PermissionsHandler;
|
||||||
|
@ -36,12 +39,15 @@ import org.dolphinemu.dolphinemu.viewholders.TvGameViewHolder;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
|
||||||
public final class TvMainActivity extends FragmentActivity implements MainView
|
public final class TvMainActivity extends FragmentActivity
|
||||||
|
implements MainView, SwipeRefreshLayout.OnRefreshListener
|
||||||
{
|
{
|
||||||
private static boolean sShouldRescanLibrary = true;
|
private static boolean sShouldRescanLibrary = true;
|
||||||
|
|
||||||
private final MainPresenter mPresenter = new MainPresenter(this, this);
|
private final MainPresenter mPresenter = new MainPresenter(this, this);
|
||||||
|
|
||||||
|
private SwipeRefreshLayout mSwipeRefresh;
|
||||||
|
|
||||||
private BrowseSupportFragment mBrowseFragment;
|
private BrowseSupportFragment mBrowseFragment;
|
||||||
|
|
||||||
private final ArrayList<ArrayObjectAdapter> mGameRows = new ArrayList<>();
|
private final ArrayList<ArrayObjectAdapter> mGameRows = new ArrayList<>();
|
||||||
|
@ -68,6 +74,8 @@ public final class TvMainActivity extends FragmentActivity implements MainView
|
||||||
{
|
{
|
||||||
super.onResume();
|
super.onResume();
|
||||||
|
|
||||||
|
boolean cacheAlreadyLoading = GameFileCacheService.isLoading();
|
||||||
|
|
||||||
if (DirectoryInitialization.shouldStart(this))
|
if (DirectoryInitialization.shouldStart(this))
|
||||||
{
|
{
|
||||||
DirectoryInitialization.start(this);
|
DirectoryInitialization.start(this);
|
||||||
|
@ -80,14 +88,16 @@ public final class TvMainActivity extends FragmentActivity implements MainView
|
||||||
// such as system language, cover downloading...
|
// such as system language, cover downloading...
|
||||||
refetchMetadata();
|
refetchMetadata();
|
||||||
|
|
||||||
if (sShouldRescanLibrary)
|
if (sShouldRescanLibrary && !cacheAlreadyLoading)
|
||||||
{
|
{
|
||||||
GameFileCacheService.startRescan(this);
|
new AfterDirectoryInitializationRunner().run(this, false, () ->
|
||||||
}
|
{
|
||||||
else
|
setRefreshing(true);
|
||||||
{
|
GameFileCacheService.startRescan(this);
|
||||||
sShouldRescanLibrary = true;
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sShouldRescanLibrary = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -117,6 +127,16 @@ public final class TvMainActivity extends FragmentActivity implements MainView
|
||||||
|
|
||||||
void setupUI()
|
void setupUI()
|
||||||
{
|
{
|
||||||
|
mSwipeRefresh = findViewById(R.id.swipe_refresh);
|
||||||
|
|
||||||
|
TypedValue typedValue = new TypedValue();
|
||||||
|
getTheme().resolveAttribute(R.attr.colorPrimary, typedValue, true);
|
||||||
|
mSwipeRefresh.setColorSchemeColors(typedValue.data);
|
||||||
|
|
||||||
|
mSwipeRefresh.setOnRefreshListener(this);
|
||||||
|
|
||||||
|
setRefreshing(GameFileCacheService.isLoading());
|
||||||
|
|
||||||
final FragmentManager fragmentManager = getSupportFragmentManager();
|
final FragmentManager fragmentManager = getSupportFragmentManager();
|
||||||
mBrowseFragment = new BrowseSupportFragment();
|
mBrowseFragment = new BrowseSupportFragment();
|
||||||
fragmentManager
|
fragmentManager
|
||||||
|
@ -188,6 +208,15 @@ public final class TvMainActivity extends FragmentActivity implements MainView
|
||||||
startActivityForResult(intent, requestCode);
|
startActivityForResult(intent, requestCode);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shows or hides the loading indicator.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void setRefreshing(boolean refreshing)
|
||||||
|
{
|
||||||
|
mSwipeRefresh.setRefreshing(refreshing);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void showGames()
|
public void showGames()
|
||||||
{
|
{
|
||||||
|
@ -277,6 +306,16 @@ public final class TvMainActivity extends FragmentActivity implements MainView
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when the user requests a refresh by swiping down.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void onRefresh()
|
||||||
|
{
|
||||||
|
setRefreshing(true);
|
||||||
|
GameFileCacheService.startRescan(this);
|
||||||
|
}
|
||||||
|
|
||||||
private void buildRowsAdapter()
|
private void buildRowsAdapter()
|
||||||
{
|
{
|
||||||
ArrayObjectAdapter rowsAdapter = new ArrayObjectAdapter(new ListRowPresenter());
|
ArrayObjectAdapter rowsAdapter = new ArrayObjectAdapter(new ListRowPresenter());
|
||||||
|
|
|
@ -1,14 +1,17 @@
|
||||||
package org.dolphinemu.dolphinemu.ui.platform;
|
package org.dolphinemu.dolphinemu.ui.platform;
|
||||||
|
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
|
import android.util.TypedValue;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
import androidx.fragment.app.Fragment;
|
import androidx.fragment.app.Fragment;
|
||||||
import androidx.recyclerview.widget.GridLayoutManager;
|
import androidx.recyclerview.widget.GridLayoutManager;
|
||||||
import androidx.recyclerview.widget.RecyclerView;
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
|
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
|
||||||
|
|
||||||
import org.dolphinemu.dolphinemu.R;
|
import org.dolphinemu.dolphinemu.R;
|
||||||
import org.dolphinemu.dolphinemu.adapters.GameAdapter;
|
import org.dolphinemu.dolphinemu.adapters.GameAdapter;
|
||||||
|
@ -20,10 +23,13 @@ public final class PlatformGamesFragment extends Fragment implements PlatformGam
|
||||||
|
|
||||||
private GameAdapter mAdapter;
|
private GameAdapter mAdapter;
|
||||||
private RecyclerView mRecyclerView;
|
private RecyclerView mRecyclerView;
|
||||||
|
private SwipeRefreshLayout mSwipeRefresh;
|
||||||
|
private SwipeRefreshLayout.OnRefreshListener mOnRefreshListener;
|
||||||
|
|
||||||
public static PlatformGamesFragment newInstance(Platform platform)
|
public static PlatformGamesFragment newInstance(Platform platform,
|
||||||
|
SwipeRefreshLayout.OnRefreshListener onRefreshListener)
|
||||||
{
|
{
|
||||||
PlatformGamesFragment fragment = new PlatformGamesFragment();
|
PlatformGamesFragment fragment = new PlatformGamesFragment(onRefreshListener);
|
||||||
|
|
||||||
Bundle args = new Bundle();
|
Bundle args = new Bundle();
|
||||||
args.putSerializable(ARG_PLATFORM, platform);
|
args.putSerializable(ARG_PLATFORM, platform);
|
||||||
|
@ -32,6 +38,11 @@ public final class PlatformGamesFragment extends Fragment implements PlatformGam
|
||||||
return fragment;
|
return fragment;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public PlatformGamesFragment(SwipeRefreshLayout.OnRefreshListener onRefreshListener)
|
||||||
|
{
|
||||||
|
mOnRefreshListener = onRefreshListener;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCreate(Bundle savedInstanceState)
|
public void onCreate(Bundle savedInstanceState)
|
||||||
{
|
{
|
||||||
|
@ -55,11 +66,19 @@ public final class PlatformGamesFragment extends Fragment implements PlatformGam
|
||||||
RecyclerView.LayoutManager layoutManager = new GridLayoutManager(getActivity(), columns);
|
RecyclerView.LayoutManager layoutManager = new GridLayoutManager(getActivity(), columns);
|
||||||
mAdapter = new GameAdapter();
|
mAdapter = new GameAdapter();
|
||||||
|
|
||||||
|
TypedValue typedValue = new TypedValue();
|
||||||
|
requireActivity().getTheme().resolveAttribute(R.attr.colorPrimary, typedValue, true);
|
||||||
|
mSwipeRefresh.setColorSchemeColors(typedValue.data);
|
||||||
|
|
||||||
|
mSwipeRefresh.setOnRefreshListener(mOnRefreshListener);
|
||||||
|
|
||||||
mRecyclerView.setLayoutManager(layoutManager);
|
mRecyclerView.setLayoutManager(layoutManager);
|
||||||
mRecyclerView.setAdapter(mAdapter);
|
mRecyclerView.setAdapter(mAdapter);
|
||||||
|
|
||||||
mRecyclerView.addItemDecoration(new GameAdapter.SpacesItemDecoration(8));
|
mRecyclerView.addItemDecoration(new GameAdapter.SpacesItemDecoration(8));
|
||||||
|
|
||||||
|
setRefreshing(GameFileCacheService.isLoading());
|
||||||
|
|
||||||
showGames();
|
showGames();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -91,8 +110,14 @@ public final class PlatformGamesFragment extends Fragment implements PlatformGam
|
||||||
mAdapter.refetchMetadata();
|
mAdapter.refetchMetadata();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setRefreshing(boolean refreshing)
|
||||||
|
{
|
||||||
|
mSwipeRefresh.setRefreshing(refreshing);
|
||||||
|
}
|
||||||
|
|
||||||
private void findViews(View root)
|
private void findViews(View root)
|
||||||
{
|
{
|
||||||
|
mSwipeRefresh = root.findViewById(R.id.swipe_refresh);
|
||||||
mRecyclerView = root.findViewById(R.id.grid_games);
|
mRecyclerView = root.findViewById(R.id.grid_games);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,8 @@
|
||||||
package org.dolphinemu.dolphinemu.ui.platform;
|
package org.dolphinemu.dolphinemu.ui.platform;
|
||||||
|
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Abstraction for a screen representing a single platform's games.
|
* Abstraction for a screen representing a single platform's games.
|
||||||
*/
|
*/
|
||||||
|
@ -21,6 +24,11 @@ public interface PlatformGamesView
|
||||||
*/
|
*/
|
||||||
void onItemClick(String gameId);
|
void onItemClick(String gameId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shows or hides the loading indicator.
|
||||||
|
*/
|
||||||
|
void setRefreshing(boolean refreshing);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* To be called when the game file cache is updated.
|
* To be called when the game file cache is updated.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -1,7 +1,20 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
android:id="@+id/content"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent">
|
|
||||||
|
|
||||||
</FrameLayout>
|
<!-- The SwipeRefreshLayout is used mainly for its ability to show a loading indicator, not for
|
||||||
|
its ability to detect swipes. But if someone is using this activity with a touchscreen
|
||||||
|
for whatever reason, we get the ability to refresh by swiping down more or less for free. -->
|
||||||
|
|
||||||
|
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:id="@+id/swipe_refresh"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent">
|
||||||
|
|
||||||
|
<FrameLayout
|
||||||
|
android:id="@+id/content"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent">
|
||||||
|
|
||||||
|
</FrameLayout>
|
||||||
|
|
||||||
|
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
|
||||||
|
|
|
@ -4,12 +4,19 @@
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent">
|
android:layout_height="match_parent">
|
||||||
|
|
||||||
<androidx.recyclerview.widget.RecyclerView
|
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
|
||||||
android:id="@+id/grid_games"
|
android:id="@+id/swipe_refresh"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:layout_marginLeft="@dimen/activity_horizontal_margin"
|
android:layout_marginLeft="@dimen/activity_horizontal_margin"
|
||||||
android:layout_marginRight="@dimen/activity_horizontal_margin"
|
android:layout_marginRight="@dimen/activity_horizontal_margin">
|
||||||
tools:listitem="@layout/card_game"/>
|
|
||||||
|
<androidx.recyclerview.widget.RecyclerView
|
||||||
|
android:id="@+id/grid_games"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
tools:listitem="@layout/card_game"/>
|
||||||
|
|
||||||
|
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
|
||||||
|
|
||||||
</FrameLayout>
|
</FrameLayout>
|
||||||
|
|
|
@ -39,6 +39,12 @@ JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_model_GameFileCache_finali
|
||||||
delete GetPointer(env, obj);
|
delete GetPointer(env, obj);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
JNIEXPORT jint JNICALL Java_org_dolphinemu_dolphinemu_model_GameFileCache_getSize(JNIEnv* env,
|
||||||
|
jobject obj)
|
||||||
|
{
|
||||||
|
return static_cast<jint>(GetPointer(env, obj)->GetSize());
|
||||||
|
}
|
||||||
|
|
||||||
JNIEXPORT jobjectArray JNICALL
|
JNIEXPORT jobjectArray JNICALL
|
||||||
Java_org_dolphinemu_dolphinemu_model_GameFileCache_getAllGames(JNIEnv* env, jobject obj)
|
Java_org_dolphinemu_dolphinemu_model_GameFileCache_getAllGames(JNIEnv* env, jobject obj)
|
||||||
{
|
{
|
||||||
|
|
Loading…
Reference in New Issue