Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
Merge pull request #9530 from JosJuice/android-loading-indicator
Android: Use SwipeRefreshLayout in MainActivity
  • Loading branch information
leoetlino committed Mar 4, 2021
2 parents 511e9dc + 4752ec8 commit 794e093
Show file tree
Hide file tree
Showing 13 changed files with 220 additions and 62 deletions.
Expand Up @@ -67,8 +67,7 @@ private void initResources()
mAfterDirectoryInitializationRunner = new AfterDirectoryInitializationRunner();
mAfterDirectoryInitializationRunner.run(this, true, () -> tryPlay(playAction));

IntentFilter gameFileCacheIntentFilter = new IntentFilter(
GameFileCacheService.BROADCAST_ACTION);
IntentFilter gameFileCacheIntentFilter = new IntentFilter(GameFileCacheService.DONE_LOADING);

BroadcastReceiver gameFileCacheReceiver = new BroadcastReceiver()
{
Expand Down Expand Up @@ -109,7 +108,7 @@ private void tryPlay(AppLinkHelper.PlayAction action)

// 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 || GameFileCacheService.hasLoadedCache())
if (game != null || !GameFileCacheService.isLoading())
{
play(action, game);
}
Expand Down
Expand Up @@ -10,6 +10,7 @@
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.FragmentPagerAdapter;
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;

import org.dolphinemu.dolphinemu.R;
import org.dolphinemu.dolphinemu.ui.platform.Platform;
Expand All @@ -18,6 +19,7 @@
public class PlatformPagerAdapter extends FragmentPagerAdapter
{
private Context mContext;
private SwipeRefreshLayout.OnRefreshListener mOnRefreshListener;

private final static int[] TAB_ICONS =
{
Expand All @@ -26,17 +28,19 @@ public class PlatformPagerAdapter extends FragmentPagerAdapter
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);
mContext = context;
mOnRefreshListener = onRefreshListener;
}

@NonNull
@Override
public Fragment getItem(int position)
{
return PlatformGamesFragment.newInstance(Platform.fromPosition(position));
return PlatformGamesFragment.newInstance(Platform.fromPosition(position), mOnRefreshListener);
}

@Override
Expand Down
Expand Up @@ -116,6 +116,8 @@ public boolean scanLibrary()
return cacheChanged;
}

public native int getSize();

public native GameFile[] getAllGames();

public native GameFile addOrGet(String gamePath);
Expand Down
Expand Up @@ -15,24 +15,33 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;

/**
* A service that loads game list data on a separate thread.
*/
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_RESCAN = "org.dolphinemu.dolphinemu.RESCAN_GAME_FILE_CACHE";

private static GameFileCache gameFileCache = null;
private static final AtomicReference<GameFile[]> gameFiles =
new AtomicReference<>(new GameFile[]{});
private static final AtomicBoolean hasLoadedCache = new AtomicBoolean(false);
private static final AtomicBoolean hasScannedLibrary = new AtomicBoolean(false);
private static final AtomicInteger unhandledIntents = new AtomicInteger(0);

public GameFileCacheService()
{
Expand Down Expand Up @@ -96,14 +105,9 @@ public static String[] findSecondDiscAndGetPaths(GameFile gameFile)
return new String[]{gameFile.getPath(), secondFile.getPath()};
}

public static boolean hasLoadedCache()
{
return hasLoadedCache.get();
}

public static boolean hasScannedLibrary()
public static boolean isLoading()
{
return hasScannedLibrary.get();
return unhandledIntents.get() != 0;
}

private static void startService(Context context, String action)
Expand All @@ -119,6 +123,8 @@ private static void startService(Context context, String action)
*/
public static void startLoad(Context context)
{
unhandledIntents.getAndIncrement();

new AfterDirectoryInitializationRunner().run(context, false,
() -> startService(context, ACTION_LOAD));
}
Expand All @@ -130,6 +136,8 @@ public static void startLoad(Context context)
*/
public static void startRescan(Context context)
{
unhandledIntents.getAndIncrement();

new AfterDirectoryInitializationRunner().run(context, false,
() -> startService(context, ACTION_RESCAN));
}
Expand All @@ -156,9 +164,11 @@ protected void onHandleIntent(Intent intent)
{
gameFileCache = temp;
gameFileCache.load();
updateGameFileArray();
hasLoadedCache.set(true);
sendBroadcast();
if (gameFileCache.getSize() != 0)
{
updateGameFileArray();
sendBroadcast(CACHE_UPDATED);
}
}
}

Expand All @@ -169,11 +179,18 @@ protected void onHandleIntent(Intent intent)
{
boolean changed = gameFileCache.scanLibrary();
if (changed)
{
updateGameFileArray();
hasScannedLibrary.set(true);
sendBroadcast();
sendBroadcast(CACHE_UPDATED);
}
}
}

int intentsLeft = unhandledIntents.decrementAndGet();
if (intentsLeft == 0)
{
sendBroadcast(DONE_LOADING);
}
}

private void updateGameFileArray()
Expand All @@ -183,8 +200,8 @@ private void updateGameFileArray()
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));
}
}
Expand Up @@ -13,6 +13,7 @@
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.Toolbar;
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
import androidx.viewpager.widget.ViewPager;

import com.google.android.material.floatingactionbutton.FloatingActionButton;
Expand All @@ -28,6 +29,7 @@
import org.dolphinemu.dolphinemu.services.GameFileCacheService;
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;
Expand All @@ -38,7 +40,8 @@
* 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
public final class MainActivity extends AppCompatActivity
implements MainView, SwipeRefreshLayout.OnRefreshListener
{
private ViewPager mViewPager;
private Toolbar mToolbar;
Expand Down Expand Up @@ -79,6 +82,8 @@ protected void onResume()
{
super.onResume();

boolean cacheAlreadyLoading = GameFileCacheService.isLoading();

if (DirectoryInitialization.shouldStart(this))
{
DirectoryInitialization.start(this);
Expand All @@ -90,16 +95,18 @@ protected void onResume()

// In case the user changed a setting that affects how games are displayed,
// such as system language, cover downloading...
refetchMetadata();
forEachPlatformGamesView(PlatformGamesView::refetchMetadata);

if (sShouldRescanLibrary)
{
GameFileCacheService.startRescan(this);
}
else
if (sShouldRescanLibrary && !cacheAlreadyLoading)
{
sShouldRescanLibrary = true;
new AfterDirectoryInitializationRunner().run(this, false, () ->
{
setRefreshing(true);
GameFileCacheService.startRescan(this);
});
}

sShouldRescanLibrary = true;
}

@Override
Expand Down Expand Up @@ -266,26 +273,42 @@ 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);
GameFileCacheService.startRescan(this);
}

/**
* 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()
{
for (Platform platform : Platform.values())
{
PlatformGamesView fragment = getPlatformGamesView(platform);
if (fragment != null)
{
fragment.showGames();
}
}
forEachPlatformGamesView(PlatformGamesView::showGames);
}

private void refetchMetadata()
private void forEachPlatformGamesView(Action1<PlatformGamesView> action)
{
for (Platform platform : Platform.values())
{
PlatformGamesView fragment = getPlatformGamesView(platform);
if (fragment != null)
{
fragment.refetchMetadata();
action.call(fragment);
}
}
}
Expand All @@ -302,7 +325,7 @@ private PlatformGamesView getPlatformGamesView(Platform platform)
private void setPlatformTabsAndStartGameFileCacheService()
{
PlatformPagerAdapter platformPagerAdapter = new PlatformPagerAdapter(
getSupportFragmentManager(), this);
getSupportFragmentManager(), this, this);
mViewPager.setAdapter(platformPagerAdapter);
mViewPager.setOffscreenPageLimit(platformPagerAdapter.getCount());
mTabLayout.setupWithViewPager(mViewPager);
Expand Down
Expand Up @@ -53,13 +53,22 @@ public void onCreate()
mView.setVersionString(versionName);

IntentFilter filter = new IntentFilter();
filter.addAction(GameFileCacheService.BROADCAST_ACTION);
filter.addAction(GameFileCacheService.CACHE_UPDATED);
filter.addAction(GameFileCacheService.DONE_LOADING);
mBroadcastReceiver = new BroadcastReceiver()
{
@Override
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);
Expand Down Expand Up @@ -87,6 +96,7 @@ public boolean handleOptionSelection(int itemId, Context context)
return true;

case R.id.menu_refresh:
mView.setRefreshing(true);
GameFileCacheService.startRescan(context);
return true;

Expand Down
Expand Up @@ -23,6 +23,11 @@

void launchOpenFileActivity(int requestCode);

/**
* Shows or hides the loading indicator.
*/
void setRefreshing(boolean refreshing);

/**
* To be called when the game file cache is updated.
*/
Expand Down

0 comments on commit 794e093

Please sign in to comment.