Android: Use SwipeRefreshLayout in MainActivity
The main reason why I'm adding this isn't actually to allow users to swipe down to refresh, it's to add a loading indicator. Considering that the Storage Access Framework can be slow for folders with many items (many subfolders?), not showing a loading indicator might give users the impression that adding a folder resulted in nothing happening even though Dolphin is scanning for games in the background. But I suppose letting users swipe down to refresh is a nice bonus with the change.
This commit is contained in:
parent
cad4548b27
commit
4752ec8074
|
@ -10,6 +10,7 @@ import androidx.annotation.NonNull;
|
|||
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;
|
||||
|
@ -18,6 +19,7 @@ import org.dolphinemu.dolphinemu.ui.platform.PlatformGamesFragment;
|
|||
public class PlatformPagerAdapter extends FragmentPagerAdapter
|
||||
{
|
||||
private Context mContext;
|
||||
private SwipeRefreshLayout.OnRefreshListener mOnRefreshListener;
|
||||
|
||||
private final static int[] TAB_ICONS =
|
||||
{
|
||||
|
@ -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
|
||||
|
|
|
@ -13,6 +13,7 @@ import androidx.annotation.NonNull;
|
|||
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;
|
||||
|
@ -39,7 +40,8 @@ import org.dolphinemu.dolphinemu.utils.StartupHandler;
|
|||
* 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;
|
||||
|
@ -97,7 +99,11 @@ public final class MainActivity extends AppCompatActivity implements MainView
|
|||
|
||||
if (sShouldRescanLibrary && !cacheAlreadyLoading)
|
||||
{
|
||||
new AfterDirectoryInitializationRunner().run(this, false, () ->
|
||||
{
|
||||
setRefreshing(true);
|
||||
GameFileCacheService.startRescan(this);
|
||||
});
|
||||
}
|
||||
|
||||
sShouldRescanLibrary = true;
|
||||
|
@ -267,6 +273,28 @@ public final class MainActivity extends AppCompatActivity implements MainView
|
|||
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()
|
||||
{
|
||||
|
@ -297,7 +325,7 @@ public final class MainActivity extends AppCompatActivity implements MainView
|
|||
private void setPlatformTabsAndStartGameFileCacheService()
|
||||
{
|
||||
PlatformPagerAdapter platformPagerAdapter = new PlatformPagerAdapter(
|
||||
getSupportFragmentManager(), this);
|
||||
getSupportFragmentManager(), this, this);
|
||||
mViewPager.setAdapter(platformPagerAdapter);
|
||||
mViewPager.setOffscreenPageLimit(platformPagerAdapter.getCount());
|
||||
mTabLayout.setupWithViewPager(mViewPager);
|
||||
|
|
|
@ -54,12 +54,21 @@ public final class MainPresenter
|
|||
|
||||
IntentFilter filter = new IntentFilter();
|
||||
filter.addAction(GameFileCacheService.CACHE_UPDATED);
|
||||
filter.addAction(GameFileCacheService.DONE_LOADING);
|
||||
mBroadcastReceiver = new BroadcastReceiver()
|
||||
{
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent)
|
||||
{
|
||||
switch (intent.getAction())
|
||||
{
|
||||
case GameFileCacheService.CACHE_UPDATED:
|
||||
mView.showGames();
|
||||
break;
|
||||
case GameFileCacheService.DONE_LOADING:
|
||||
mView.setRefreshing(false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
LocalBroadcastManager.getInstance(mContext).registerReceiver(mBroadcastReceiver, filter);
|
||||
|
@ -87,6 +96,7 @@ public final class MainPresenter
|
|||
return true;
|
||||
|
||||
case R.id.menu_refresh:
|
||||
mView.setRefreshing(true);
|
||||
GameFileCacheService.startRescan(context);
|
||||
return true;
|
||||
|
||||
|
|
|
@ -23,6 +23,11 @@ public interface MainView
|
|||
|
||||
void launchOpenFileActivity(int requestCode);
|
||||
|
||||
/**
|
||||
* Shows or hides the loading indicator.
|
||||
*/
|
||||
void setRefreshing(boolean refreshing);
|
||||
|
||||
/**
|
||||
* 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.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.util.TypedValue;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
@ -15,6 +16,7 @@ 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.R;
|
||||
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.services.GameFileCacheService;
|
||||
import org.dolphinemu.dolphinemu.ui.platform.Platform;
|
||||
import org.dolphinemu.dolphinemu.utils.AfterDirectoryInitializationRunner;
|
||||
import org.dolphinemu.dolphinemu.utils.DirectoryInitialization;
|
||||
import org.dolphinemu.dolphinemu.utils.FileBrowserHelper;
|
||||
import org.dolphinemu.dolphinemu.utils.PermissionsHandler;
|
||||
|
@ -36,12 +39,15 @@ import org.dolphinemu.dolphinemu.viewholders.TvGameViewHolder;
|
|||
import java.util.ArrayList;
|
||||
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 final MainPresenter mPresenter = new MainPresenter(this, this);
|
||||
|
||||
private SwipeRefreshLayout mSwipeRefresh;
|
||||
|
||||
private BrowseSupportFragment mBrowseFragment;
|
||||
|
||||
private final ArrayList<ArrayObjectAdapter> mGameRows = new ArrayList<>();
|
||||
|
@ -84,7 +90,11 @@ public final class TvMainActivity extends FragmentActivity implements MainView
|
|||
|
||||
if (sShouldRescanLibrary && !cacheAlreadyLoading)
|
||||
{
|
||||
new AfterDirectoryInitializationRunner().run(this, false, () ->
|
||||
{
|
||||
setRefreshing(true);
|
||||
GameFileCacheService.startRescan(this);
|
||||
});
|
||||
}
|
||||
|
||||
sShouldRescanLibrary = true;
|
||||
|
@ -117,6 +127,16 @@ public final class TvMainActivity extends FragmentActivity implements MainView
|
|||
|
||||
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();
|
||||
mBrowseFragment = new BrowseSupportFragment();
|
||||
fragmentManager
|
||||
|
@ -188,6 +208,15 @@ public final class TvMainActivity extends FragmentActivity implements MainView
|
|||
startActivityForResult(intent, requestCode);
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows or hides the loading indicator.
|
||||
*/
|
||||
@Override
|
||||
public void setRefreshing(boolean refreshing)
|
||||
{
|
||||
mSwipeRefresh.setRefreshing(refreshing);
|
||||
}
|
||||
|
||||
@Override
|
||||
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()
|
||||
{
|
||||
ArrayObjectAdapter rowsAdapter = new ArrayObjectAdapter(new ListRowPresenter());
|
||||
|
|
|
@ -1,14 +1,17 @@
|
|||
package org.dolphinemu.dolphinemu.ui.platform;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.util.TypedValue;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.recyclerview.widget.GridLayoutManager;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
|
||||
|
||||
import org.dolphinemu.dolphinemu.R;
|
||||
import org.dolphinemu.dolphinemu.adapters.GameAdapter;
|
||||
|
@ -20,10 +23,13 @@ public final class PlatformGamesFragment extends Fragment implements PlatformGam
|
|||
|
||||
private GameAdapter mAdapter;
|
||||
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();
|
||||
args.putSerializable(ARG_PLATFORM, platform);
|
||||
|
@ -32,6 +38,11 @@ public final class PlatformGamesFragment extends Fragment implements PlatformGam
|
|||
return fragment;
|
||||
}
|
||||
|
||||
public PlatformGamesFragment(SwipeRefreshLayout.OnRefreshListener onRefreshListener)
|
||||
{
|
||||
mOnRefreshListener = onRefreshListener;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState)
|
||||
{
|
||||
|
@ -55,11 +66,19 @@ public final class PlatformGamesFragment extends Fragment implements PlatformGam
|
|||
RecyclerView.LayoutManager layoutManager = new GridLayoutManager(getActivity(), columns);
|
||||
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.setAdapter(mAdapter);
|
||||
|
||||
mRecyclerView.addItemDecoration(new GameAdapter.SpacesItemDecoration(8));
|
||||
|
||||
setRefreshing(GameFileCacheService.isLoading());
|
||||
|
||||
showGames();
|
||||
}
|
||||
|
||||
|
@ -91,8 +110,14 @@ public final class PlatformGamesFragment extends Fragment implements PlatformGam
|
|||
mAdapter.refetchMetadata();
|
||||
}
|
||||
|
||||
public void setRefreshing(boolean refreshing)
|
||||
{
|
||||
mSwipeRefresh.setRefreshing(refreshing);
|
||||
}
|
||||
|
||||
private void findViews(View root)
|
||||
{
|
||||
mSwipeRefresh = root.findViewById(R.id.swipe_refresh);
|
||||
mRecyclerView = root.findViewById(R.id.grid_games);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,8 @@
|
|||
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.
|
||||
*/
|
||||
|
@ -21,6 +24,11 @@ public interface PlatformGamesView
|
|||
*/
|
||||
void onItemClick(String gameId);
|
||||
|
||||
/**
|
||||
* Shows or hides the loading indicator.
|
||||
*/
|
||||
void setRefreshing(boolean refreshing);
|
||||
|
||||
/**
|
||||
* To be called when the game file cache is updated.
|
||||
*/
|
||||
|
|
|
@ -1,7 +1,20 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
|
||||
<!-- 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_height="match_parent">
|
||||
|
||||
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
|
||||
android:id="@+id/swipe_refresh"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginLeft="@dimen/activity_horizontal_margin"
|
||||
android:layout_marginRight="@dimen/activity_horizontal_margin">
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/grid_games"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginLeft="@dimen/activity_horizontal_margin"
|
||||
android:layout_marginRight="@dimen/activity_horizontal_margin"
|
||||
tools:listitem="@layout/card_game"/>
|
||||
|
||||
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
|
||||
|
||||
</FrameLayout>
|
||||
|
|
Loading…
Reference in New Issue