Fragments now have a proper lifecycle.

This commit is contained in:
Peter Serwylo 2013-04-12 08:07:20 +10:00
parent ff4ba92376
commit c354280a62
7 changed files with 368 additions and 300 deletions

View File

@ -0,0 +1,234 @@
package org.fdroid.fdroid;
import android.content.SharedPreferences;
import android.preference.PreferenceManager;
import android.util.Log;
import android.widget.ArrayAdapter;
import java.util.*;
/**
* Should be owned by the FDroid Activity, but used by the AppListFragments.
* The idea is that it takes a non-trivial amount of time to work this stuff
* out, and it is quicker if we only do it once for each view, rather than
* each fragment figuring out their own list independently.
*/
public class AppListManager {
private Vector<DB.App> allApps = null;
private FDroid fdroidActivity;
private AppListAdapter availableApps;
private AppListAdapter installedApps;
private AppListAdapter canUpgradeApps;
private ArrayAdapter<String> categories;
private String currentCategory = null;
private String categoryAll = null;
private String categoryWhatsNew = null;
private String categoryRecentlyUpdated = null;
public AppListAdapter getAvailableAdapter() {
return availableApps;
}
public AppListAdapter getInstalledAdapter() {
return installedApps;
}
public AppListAdapter getCanUpdateAdapter() {
return canUpgradeApps;
}
public ArrayAdapter<String> getCategoriesAdapter() {
return categories;
}
public AppListManager(FDroid activity) {
this.fdroidActivity = activity;
availableApps = new AppListAdapter(fdroidActivity);
installedApps = new AppListAdapter(fdroidActivity);
canUpgradeApps = new AppListAdapter(fdroidActivity);
// Needs to be created before createViews(), because that will use the
// getCategoriesAdapter() accessor which expects this object...
categories = new ArrayAdapter<String>(activity,
android.R.layout.simple_spinner_item, new Vector<String>());
categories
.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
}
private void clear() {
installedApps.clear();
availableApps.clear();
canUpgradeApps.clear();
categories.clear();
}
private void notifyLists() {
// Tell the lists that the data behind the adapter has changed, so
// they can refresh...
availableApps.notifyDataSetChanged();
installedApps.notifyDataSetChanged();
canUpgradeApps.notifyDataSetChanged();
categories.notifyDataSetChanged();
}
private void updateCategories() {
try {
DB db = DB.getDB();
// Populate the category list with the real categories, and the
// locally generated meta-categories for "All", "What's New" and
// "Recently Updated"...
categoryAll = fdroidActivity.getString(R.string.category_all);
categoryWhatsNew = fdroidActivity.getString(R.string.category_whatsnew);
categoryRecentlyUpdated = fdroidActivity.getString(R.string.category_recentlyupdated);
categories.add(categoryWhatsNew);
categories.add(categoryRecentlyUpdated);
categories.add(categoryAll);
for (String s : db.getCategories()) {
categories.add(s);
}
if (currentCategory == null)
currentCategory = categoryWhatsNew;
} finally {
DB.releaseDB();
}
}
// Tell the FDroid activity to update its "Update (x)" tab to correctly
// reflect the number of updates available.
private void notifyActivity() {
fdroidActivity.refreshUpdateTabLabel();
}
public void repopulateLists() {
long startTime = System.currentTimeMillis();
clear();
updateCategories();
updateApps();
notifyLists();
notifyActivity();
Log.d("FDroid", "Updated lists - " + allApps.size() + " allApps in total"
+ " (update took " + (System.currentTimeMillis() - startTime)
+ " ms)");
}
// Calculate the cutoff date we'll use for What's New and Recently
// Updated...
private Date calcMaxHistory() {
SharedPreferences prefs = PreferenceManager
.getDefaultSharedPreferences(fdroidActivity.getBaseContext());
String daysPreference = prefs.getString("updateHistoryDays", "14");
int maxHistoryDays = Integer.parseInt(daysPreference);
Calendar recent = Calendar.getInstance();
recent.add(Calendar.DAY_OF_YEAR, -maxHistoryDays);
return recent.getTime();
}
// recentDate could really be calculated here, but this is just a hack so
// it doesn't need to be caluculated for every single app. The reason it
// isn't an instance variable is because the preferences may change, and
// we wouldn't know.
private boolean isInCategory(DB.App app, String category, Date recentDate) {
boolean isInCategory;
if (category.equals(categoryAll)) {
isInCategory = true;
} else if (category.equals(categoryWhatsNew)) {
if (app.added == null)
isInCategory = false;
else if (app.added.compareTo(recentDate) < 0)
isInCategory = false;
else
isInCategory = true;
} else if (category.equals(categoryRecentlyUpdated)) {
if (app.lastUpdated == null)
isInCategory = false;
// Don't include in the recently updated category if the
// 'update' was actually it being added.
else if (app.lastUpdated.compareTo(app.added) == 0)
isInCategory = false;
else if (app.lastUpdated.compareTo(recentDate) < 0)
isInCategory = false;
else
isInCategory = true;
} else {
isInCategory = category.equals(app.category);
}
return isInCategory;
}
// Returns false if the app list is empty and the fdroid activity decided
// to attempt updating it.
private boolean updateApps() {
allApps = ((FDroidApp)fdroidActivity.getApplication()).getApps();
if (allApps.isEmpty()) {
// If its the first time we've run the app, this should update
// the repos. If not, it will do nothing, presuming that the repos
// are invalid, the internet is stuffed, the sky has fallen, etc...
return fdroidActivity.updateEmptyRepos();
}
Date recentDate = calcMaxHistory();
AppFilter appFilter = new AppFilter(fdroidActivity);
Vector<DB.App> availApps = new Vector<DB.App>();
for (DB.App app : allApps) {
boolean isInCategory = isInCategory(app, currentCategory, recentDate);
boolean isFiltered = appFilter.filter(app);
// Add it to the list(s). Always to installed and updates, but
// only to available if it's not filtered.
if (!isFiltered && isInCategory)
availApps.add(app);
if (app.installedVersion != null) {
installedApps.addItem(app);
if (app.hasUpdates)
canUpgradeApps.addItem(app);
}
}
if (currentCategory.equals(categoryWhatsNew)) {
Collections.sort(availApps, new WhatsNewComparator());
} else if (currentCategory.equals(categoryRecentlyUpdated)) {
Collections.sort(availApps, new RecentlyUpdatedComparator());
}
for (DB.App app : availApps)
availableApps.addItem(app);
return true;
}
public void setCurrentCategory(String currentCategory) {
this.currentCategory = currentCategory;
}
static class WhatsNewComparator implements Comparator<DB.App> {
@Override
public int compare(DB.App lhs, DB.App rhs) {
return rhs.added.compareTo(lhs.added);
}
}
static class RecentlyUpdatedComparator implements Comparator<DB.App> {
@Override
public int compare(DB.App lhs, DB.App rhs) {
return rhs.lastUpdated.compareTo(lhs.lastUpdated);
}
}
}

View File

@ -19,46 +19,36 @@
package org.fdroid.fdroid; package org.fdroid.fdroid;
import java.util.Calendar; import android.app.ActionBar;
import java.util.Collections; import android.app.AlertDialog;
import java.util.Comparator;
import java.util.Date;
import java.util.Vector;
import android.app.*;
import android.os.Build;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentActivity;
import android.support.v4.view.PagerAdapter;
import android.support.v4.view.ViewPager;
import android.view.*;
import android.widget.*;
import org.fdroid.fdroid.DB.App;
import android.app.AlertDialog.Builder; import android.app.AlertDialog.Builder;
import android.app.FragmentTransaction;
import android.app.ProgressDialog;
import android.content.DialogInterface; import android.content.DialogInterface;
import android.content.Intent; import android.content.Intent;
import android.content.pm.PackageInfo; import android.content.pm.PackageInfo;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
import android.content.SharedPreferences;
import android.net.Uri; import android.net.Uri;
import android.os.Build;
import android.os.Bundle; import android.os.Bundle;
import android.os.Handler; import android.os.Handler;
import android.os.ResultReceiver; import android.os.ResultReceiver;
import android.preference.PreferenceManager; import android.support.v4.app.FragmentActivity;
import android.support.v4.view.ViewPager;
import android.util.Log; import android.util.Log;
import android.widget.AdapterView.OnItemClickListener; import android.view.LayoutInflater;
import android.widget.AdapterView.OnItemSelectedListener; import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.*;
import android.widget.TabHost.TabSpec; import android.widget.TabHost.TabSpec;
import org.fdroid.fdroid.views.AppListFragmentPageAdapter; import org.fdroid.fdroid.views.AppListFragmentPageAdapter;
import org.fdroid.fdroid.views.AppListView;
public class FDroid extends FragmentActivity implements OnItemClickListener, public class FDroid extends FragmentActivity {
OnItemSelectedListener {
private static final int REQUEST_APPDETAILS = 0; public static final int REQUEST_APPDETAILS = 0;
private static final int REQUEST_MANAGEREPOS = 1; public static final int REQUEST_MANAGEREPOS = 1;
private static final int REQUEST_PREFS = 2; public static final int REQUEST_PREFS = 2;
public static final String EXTRA_TAB_UPDATE = "extraTab"; public static final String EXTRA_TAB_UPDATE = "extraTab";
@ -68,72 +58,33 @@ public class FDroid extends FragmentActivity implements OnItemClickListener,
private static final int ABOUT = Menu.FIRST + 3; private static final int ABOUT = Menu.FIRST + 3;
private static final int SEARCH = Menu.FIRST + 4; private static final int SEARCH = Menu.FIRST + 4;
// Apps that are available to be installed
private AppListAdapter apps_av = new AppListAdapter(this);
// Apps that are installed
private AppListAdapter apps_in = new AppListAdapter(this);
// Apps that can be upgraded
private AppListAdapter apps_up = new AppListAdapter(this);
// Category list
private ArrayAdapter<String> categories;
private String currentCategory = null;
private ProgressDialog pd; private ProgressDialog pd;
private boolean triedEmptyUpdate;
// List of apps.
private Vector<DB.App> apps = null;
private ViewPager viewPager; private ViewPager viewPager;
private AppListManager manager = null;
// Used by pre 3.0 devices which don't have an ActionBar... // Used by pre 3.0 devices which don't have an ActionBar...
private TabHost tabHost; private TabHost tabHost;
private AppListFragmentPageAdapter viewPageAdapter;
// The following getters public AppListManager getManager() {
// (availableAdapter/installedAdapter/canUpdateAdapter/categoriesAdapter) return manager;
// are used by the APpListViewFactory to construct views that can be used
// either by Android 3.0+ devices with ActionBars, or earlier devices
// with old fashioned tabs.
public AppListAdapter getAvailableAdapter() {
return apps_av;
}
public AppListAdapter getInstalledAdapter() {
return apps_in;
}
public AppListAdapter getCanUpdateAdapter() {
return apps_up;
}
public ArrayAdapter<String> getCategoriesAdapter() {
return categories;
} }
@Override @Override
protected void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
manager = new AppListManager(this);
setContentView(R.layout.fdroid); setContentView(R.layout.fdroid);
// Needs to be created before createViews(), because that will use the
// getCategoriesAdapter() accessor which expects this object...
categories = new ArrayAdapter<String>(this,
android.R.layout.simple_spinner_item, new Vector<String>());
categories
.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
createViews(); createViews();
createTabs(); createTabs();
// Must be done *after* createViews, because it will involve a
// callback to update the tab label for the "update" tab. This
// will fail unless the tabs have actually been created.
repopulateViews();
Intent i = getIntent(); Intent i = getIntent();
if (i.hasExtra("uri")) { if (i.hasExtra("uri")) {
Intent call = new Intent(this, ManageRepo.class); Intent call = new Intent(this, ManageRepo.class);
@ -145,16 +96,17 @@ public class FDroid extends FragmentActivity implements OnItemClickListener,
selectTab(2); selectTab(2);
} }
} }
triedEmptyUpdate = false;
} }
@Override @Override
protected void onStart() { protected void onStart() {
populateLists();
super.onStart(); super.onStart();
} }
protected void repopulateViews() {
manager.repopulateLists();
}
@Override @Override
public boolean onCreateOptionsMenu(Menu menu) { public boolean onCreateOptionsMenu(Menu menu) {
@ -237,7 +189,6 @@ public class FDroid extends FragmentActivity implements OnItemClickListener,
@Override @Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) { protected void onActivityResult(int requestCode, int resultCode, Intent data) {
triedEmptyUpdate = true;
switch (requestCode) { switch (requestCode) {
case REQUEST_APPDETAILS: case REQUEST_APPDETAILS:
break; break;
@ -274,7 +225,7 @@ public class FDroid extends FragmentActivity implements OnItemClickListener,
&& (data.hasExtra("reset") || data.hasExtra("update"))) { && (data.hasExtra("reset") || data.hasExtra("update"))) {
updateRepos(); updateRepos();
} else { } else {
populateLists(); repopulateViews();
} }
break; break;
@ -283,7 +234,7 @@ public class FDroid extends FragmentActivity implements OnItemClickListener,
private void createViews() { private void createViews() {
viewPager = (ViewPager)findViewById(R.id.main_pager); viewPager = (ViewPager)findViewById(R.id.main_pager);
viewPageAdapter = new AppListFragmentPageAdapter(this); AppListFragmentPageAdapter viewPageAdapter = new AppListFragmentPageAdapter(this);
viewPager.setAdapter(viewPageAdapter); viewPager.setAdapter(viewPageAdapter);
viewPager.setOnPageChangeListener( new ViewPager.SimpleOnPageChangeListener() { viewPager.setOnPageChangeListener( new ViewPager.SimpleOnPageChangeListener() {
public void onPageSelected(int position) { public void onPageSelected(int position) {
@ -308,12 +259,17 @@ public class FDroid extends FragmentActivity implements OnItemClickListener,
} }
} }
private void updateTabText(int index) { public void refreshUpdateTabLabel() {
CharSequence text = viewPager.getAdapter().getPageTitle(index); final int INDEX = 2;
CharSequence text = viewPager.getAdapter().getPageTitle(INDEX);
if ( Build.VERSION.SDK_INT >= 11) { if ( Build.VERSION.SDK_INT >= 11) {
getActionBar().getTabAt(index).setText(text); getActionBar().getTabAt(INDEX).setText(text);
} else { } else {
// Update the count on the 'Updates' tab to show the number available.
// This is quite unpleasant, but seems to be the only way to do it.
TextView textView = (TextView) tabHost.getTabWidget().getChildAt(2)
.findViewById(android.R.id.title);
textView.setText(text);
} }
} }
@ -407,142 +363,6 @@ public class FDroid extends FragmentActivity implements OnItemClickListener,
}); });
} }
// Populate the lists.
private void populateLists() {
apps_in.clear();
apps_av.clear();
apps_up.clear();
categories.clear();
long startTime = System.currentTimeMillis();
DB db;
String cat_all, cat_whatsnew, cat_recentlyupdated;
try {
db = DB.getDB();
// Populate the category list with the real categories, and the
// locally generated meta-categories for "All", "What's New" and
// "Recently Updated"...
cat_all = getString(R.string.category_all);
cat_whatsnew = getString(R.string.category_whatsnew);
cat_recentlyupdated = getString(R.string.category_recentlyupdated);
categories.add(cat_whatsnew);
categories.add(cat_recentlyupdated);
categories.add(cat_all);
for (String s : db.getCategories()) {
categories.add(s);
}
if (currentCategory == null)
currentCategory = cat_whatsnew;
} finally {
DB.releaseDB();
}
apps = ((FDroidApp) getApplication()).getApps();
if (apps.isEmpty()) {
// Don't attempt this more than once - we may have invalid
// repositories.
if (triedEmptyUpdate)
return;
// If there are no apps, update from the repos - it must be a
// new installation.
Log.d("FDroid", "Empty app list forces repo update");
updateRepos();
triedEmptyUpdate = true;
return;
}
// Calculate the cutoff date we'll use for What's New and Recently
// Updated...
SharedPreferences prefs = PreferenceManager
.getDefaultSharedPreferences(getBaseContext());
String sint = prefs.getString("updateHistoryDays", "14");
int history_days = Integer.parseInt(sint);
Calendar recent = Calendar.getInstance();
recent.add(Calendar.DAY_OF_YEAR, -history_days);
Date recentDate = recent.getTime();
AppFilter appfilter = new AppFilter(this);
boolean incat;
Vector<DB.App> availapps = new Vector<DB.App>();
for (DB.App app : apps) {
if (currentCategory.equals(cat_all)) {
incat = true;
} else if (currentCategory.equals(cat_whatsnew)) {
if (app.added == null)
incat = false;
else if (app.added.compareTo(recentDate) < 0)
incat = false;
else
incat = true;
} else if (currentCategory.equals(cat_recentlyupdated)) {
if (app.lastUpdated == null)
incat = false;
// Don't include in the recently updated category if the
// 'update' was actually it being added.
else if (app.lastUpdated.compareTo(app.added) == 0)
incat = false;
else if (app.lastUpdated.compareTo(recentDate) < 0)
incat = false;
else
incat = true;
} else {
incat = currentCategory.equals(app.category);
}
boolean filtered = appfilter.filter(app);
// Add it to the list(s). Always to installed and updates, but
// only to available if it's not filtered.
if (!filtered && incat)
availapps.add(app);
if (app.installedVersion != null) {
apps_in.addItem(app);
if (app.hasUpdates)
apps_up.addItem(app);
}
}
if (currentCategory.equals(cat_whatsnew)) {
class WhatsNewComparator implements Comparator<DB.App> {
@Override
public int compare(App lhs, App rhs) {
return rhs.added.compareTo(lhs.added);
}
}
Collections.sort(availapps, new WhatsNewComparator());
} else if (currentCategory.equals(cat_recentlyupdated)) {
class UpdatedComparator implements Comparator<DB.App> {
@Override
public int compare(App lhs, App rhs) {
return rhs.lastUpdated.compareTo(lhs.lastUpdated);
}
}
Collections.sort(availapps, new UpdatedComparator());
}
for (DB.App app : availapps)
apps_av.addItem(app);
updateTabText(2);
// Tell the lists that the data behind the adapter has changed, so
// they can refresh...
apps_av.notifyDataSetChanged();
apps_in.notifyDataSetChanged();
apps_up.notifyDataSetChanged();
categories.notifyDataSetChanged();
Log.d("FDroid", "Updated lists - " + apps.size() + " apps in total"
+ " (update took " + (System.currentTimeMillis() - startTime)
+ " ms)");
}
// For receiving results from the UpdateService when we've told it to // For receiving results from the UpdateService when we've told it to
// update in response to a user request. // update in response to a user request.
private class UpdateReceiver extends ResultReceiver { private class UpdateReceiver extends ResultReceiver {
@ -556,7 +376,7 @@ public class FDroid extends FragmentActivity implements OnItemClickListener,
Toast.makeText(FDroid.this, resultData.getString("errmsg"), Toast.makeText(FDroid.this, resultData.getString("errmsg"),
Toast.LENGTH_LONG).show(); Toast.LENGTH_LONG).show();
} else { } else {
populateLists(); repopulateViews();
} }
if (pd.isShowing()) if (pd.isShowing())
pd.dismiss(); pd.dismiss();
@ -565,10 +385,32 @@ public class FDroid extends FragmentActivity implements OnItemClickListener,
private UpdateReceiver mUpdateReceiver; private UpdateReceiver mUpdateReceiver;
/**
* The first time the app is run, we will have an empty app list.
* If this is the case, we will attempt to update with the default repo.
* However, if we have tried this at least once, then don't try to do
* it automatically again, because the repos or internet connection may
* be bad.
*/
public boolean updateEmptyRepos() {
final String TRIED_EMPTY_UPDATE = "triedEmptyUpdate";
boolean hasTriedEmptyUpdate = getPreferences(MODE_PRIVATE).getBoolean(TRIED_EMPTY_UPDATE, false);
if (!hasTriedEmptyUpdate) {
Log.d("FDroid", "Empty app list, and we haven't done an update yet. Forcing repo update.");
updateRepos();
getPreferences(MODE_PRIVATE).edit().putBoolean(TRIED_EMPTY_UPDATE, true);
return true;
} else {
Log.d("FDroid", "Empty app list, but it looks like we've had an update previously. Will not force repo update.");
return false;
}
}
// Force a repo update now. A progress dialog is shown and the UpdateService // Force a repo update now. A progress dialog is shown and the UpdateService
// is told to do the update, which will result in the database changing. The // is told to do the update, which will result in the database changing. The
// UpdateReceiver class should get told when this is finished. // UpdateReceiver class should get told when this is finished.
private void updateRepos() { public void updateRepos() {
pd = ProgressDialog.show(this, getString(R.string.process_wait_title), pd = ProgressDialog.show(this, getString(R.string.process_wait_title),
getString(R.string.process_update_msg), true, true); getString(R.string.process_update_msg), true, true);
pd.setIcon(android.R.drawable.ic_dialog_info); pd.setIcon(android.R.drawable.ic_dialog_info);
@ -580,36 +422,4 @@ public class FDroid extends FragmentActivity implements OnItemClickListener,
startService(intent); startService(intent);
} }
public void onItemSelected(AdapterView<?> parent, View view, int pos,
long id) {
currentCategory = parent.getItemAtPosition(pos).toString();
populateLists();
}
public void onNothingSelected(AdapterView<?> parent) {
// We always have at least "All"
}
// Handler for a click on one of the items in an application list. Pops
// up a dialog that shows the details of the application and all its
// available versions, with buttons to allow installation etc.
public void onItemClick(AdapterView<?> arg0, View arg1, final int arg2,
long arg3) {
int currentItem = viewPager.getCurrentItem();
Fragment fragment = viewPageAdapter.getItem(currentItem);
// The fragment.getView() returns a wrapper object which has the
// actual view we're interested in inside:
// http://stackoverflow.com/a/13684505
ViewGroup group = (ViewGroup)fragment.getView();
AppListView view = (AppListView)group.getChildAt(0);
final DB.App app = (DB.App)view.getAppList().getAdapter().getItem(arg2);
Intent intent = new Intent(this, AppDetails.class);
intent.putExtra("appid", app.id);
startActivityForResult(intent, REQUEST_APPDETAILS);
}
} }

View File

@ -8,6 +8,7 @@ import android.support.v4.app.FragmentPagerAdapter;
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 org.fdroid.fdroid.AppListManager;
import org.fdroid.fdroid.FDroid; import org.fdroid.fdroid.FDroid;
import org.fdroid.fdroid.R; import org.fdroid.fdroid.R;
import org.fdroid.fdroid.views.fragments.AvailableAppsFragment; import org.fdroid.fdroid.views.fragments.AvailableAppsFragment;
@ -20,25 +21,29 @@ import org.fdroid.fdroid.views.fragments.InstalledAppsFragment;
*/ */
public class AppListFragmentPageAdapter extends FragmentPagerAdapter { public class AppListFragmentPageAdapter extends FragmentPagerAdapter {
private FDroid parent; private FDroid parent = null;
private Fragment[] fragments = new Fragment[3];
public AppListFragmentPageAdapter(FDroid parent) { public AppListFragmentPageAdapter(FDroid parent) {
super(parent.getSupportFragmentManager()); super(parent.getSupportFragmentManager());
this.parent = parent; this.parent = parent;
fragments[0] = new AvailableAppsFragment().setAppListAdapter(parent.getAvailableAdapter());
fragments[1] = new InstalledAppsFragment().setAppListAdapter(parent.getInstalledAdapter());
fragments[2] = new CanUpdateAppsFragment().setAppListAdapter(parent.getCanUpdateAdapter()).;
} }
@Override @Override
public Fragment getItem(int i) { public Fragment getItem(int i) {
return fragments[i]; Fragment fragment = null;
if ( i == 0 ) {
fragment = new AvailableAppsFragment();
} else if ( i == 1 ) {
fragment = new InstalledAppsFragment();
} else if ( i == 2 ) {
fragment = new CanUpdateAppsFragment();
}
return fragment;
} }
@Override @Override
public int getCount() { public int getCount() {
return fragments.length; return 3;
} }
public String getPageTitle(int i) { public String getPageTitle(int i) {
@ -49,7 +54,7 @@ public class AppListFragmentPageAdapter extends FragmentPagerAdapter {
return parent.getString(R.string.tab_installed); return parent.getString(R.string.tab_installed);
case 2: case 2:
String updates = parent.getString(R.string.tab_updates); String updates = parent.getString(R.string.tab_updates);
updates += " (" + parent.getCanUpdateAdapter().getCount() + ")"; updates += " (" + parent.getManager().getCanUpdateAdapter().getCount() + ")";
return updates; return updates;
default: default:
return ""; return "";

View File

@ -1,29 +1,44 @@
package org.fdroid.fdroid.views.fragments; package org.fdroid.fdroid.views.fragments;
import android.app.Activity;
import android.content.Intent;
import android.support.v4.app.Fragment; import android.support.v4.app.Fragment;
import android.util.Log;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.widget.AdapterView; import android.widget.AdapterView;
import android.widget.ListView; import android.widget.ListView;
import org.fdroid.fdroid.AppListAdapter; import org.fdroid.fdroid.*;
import org.fdroid.fdroid.views.AppListView; import org.fdroid.fdroid.views.AppListView;
abstract class AppListFragment extends Fragment implements AdapterView.OnItemClickListener { abstract class AppListFragment extends Fragment implements AdapterView.OnItemClickListener {
private AppListAdapter appListAdapter; private AppListManager appListManager;
private FDroid parent;
public AppListAdapter getAppListAdapter() { protected abstract AppListAdapter getAppListAdapter();
return appListAdapter;
public void onAttach(Activity activity) {
super.onAttach(activity);
try {
parent = (FDroid)activity;
} catch (ClassCastException e) {
// I know fragments are meant to be activity agnostic, but I can't
// think of a better way to share the one application list between
// all three app list fragments.
throw new RuntimeException(
"AppListFragment can only be attached to FDroid activity. " +
"Here it was attached to a " + activity.getClass() );
}
} }
public AppListFragment setAppListAdapter(AppListAdapter adapter) { public AppListManager getAppListManager() {
appListAdapter = adapter; return parent.getManager();
return this;
} }
protected AppListView createPlainAppList(AppListAdapter adapter) { protected AppListView createPlainAppList() {
AppListView view = new AppListView(getActivity()); AppListView view = new AppListView(getActivity());
ListView list = createAppListView(adapter); ListView list = createAppListView();
view.addView( view.addView(
list, list,
new ViewGroup.LayoutParams( new ViewGroup.LayoutParams(
@ -33,11 +48,19 @@ abstract class AppListFragment extends Fragment implements AdapterView.OnItemCli
return view; return view;
} }
protected ListView createAppListView(AppListAdapter adapter) { protected ListView createAppListView() {
ListView list = new ListView(getActivity()); ListView list = new ListView(getActivity());
list.setFastScrollEnabled(true); list.setFastScrollEnabled(true);
list.setOnItemClickListener(this); list.setOnItemClickListener(this);
list.setAdapter(adapter); list.setAdapter(getAppListAdapter());
return list; return list;
} }
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
final DB.App app = (DB.App)getAppListAdapter().getItem(position);
Intent intent = new Intent(getActivity(), AppDetails.class);
intent.putExtra("appid", app.id);
startActivityForResult(intent, FDroid.REQUEST_APPDETAILS);
}
} }

View File

@ -1,17 +1,18 @@
package org.fdroid.fdroid.views.fragments; package org.fdroid.fdroid.views.fragments;
import android.os.Bundle; import android.os.Bundle;
import android.util.Log;
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 android.widget.*; import android.widget.*;
import org.fdroid.fdroid.AppListAdapter;
import org.fdroid.fdroid.AppListManager;
import org.fdroid.fdroid.R; import org.fdroid.fdroid.R;
import org.fdroid.fdroid.views.AppListView; import org.fdroid.fdroid.views.AppListView;
public class AvailableAppsFragment extends AppListFragment implements AdapterView.OnItemSelectedListener { public class AvailableAppsFragment extends AppListFragment implements AdapterView.OnItemSelectedListener {
private ArrayAdapter<String> categoriesAdapter;
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
AppListView view = new AppListView(getActivity()); AppListView view = new AppListView(getActivity());
view.setOrientation(LinearLayout.VERTICAL); view.setOrientation(LinearLayout.VERTICAL);
@ -20,7 +21,7 @@ public class AvailableAppsFragment extends AppListFragment implements AdapterVie
// Giving it an ID lets the default save/restore state // Giving it an ID lets the default save/restore state
// functionality do its stuff. // functionality do its stuff.
spinner.setId(R.id.categorySpinner); spinner.setId(R.id.categorySpinner);
spinner.setAdapter(getCategoriesAdapter()); spinner.setAdapter(getAppListManager().getCategoriesAdapter());
spinner.setOnItemSelectedListener(this); spinner.setOnItemSelectedListener(this);
view.addView( view.addView(
@ -29,7 +30,7 @@ public class AvailableAppsFragment extends AppListFragment implements AdapterVie
LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.MATCH_PARENT,
LinearLayout.LayoutParams.WRAP_CONTENT)); LinearLayout.LayoutParams.WRAP_CONTENT));
ListView list = createAppListView(getAppListAdapter()); ListView list = createAppListView();
view.setAppList(list); view.setAppList(list);
view.addView( view.addView(
list, list,
@ -48,26 +49,19 @@ public class AvailableAppsFragment extends AppListFragment implements AdapterVie
super.onActivityCreated(savedInstanceState); super.onActivityCreated(savedInstanceState);
} }
public ArrayAdapter<String> getCategoriesAdapter() { public void onItemSelected(AdapterView<?> parent, View view, int pos,
return categoriesAdapter; long id) {
} getAppListManager().setCurrentCategory(parent.getItemAtPosition(pos).toString());
getAppListManager().repopulateLists();
public void setCategoriesAdapter(ArrayAdapter<String> categoriesAdapter) {
this.categoriesAdapter = categoriesAdapter;
}
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
}
@Override
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
} }
@Override @Override
public void onNothingSelected(AdapterView<?> parent) { public void onNothingSelected(AdapterView<?> parent) {
} }
@Override
protected AppListAdapter getAppListAdapter() {
return getAppListManager().getAvailableAdapter();
}
} }

View File

@ -5,15 +5,16 @@ import android.view.LayoutInflater;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.widget.AdapterView; import android.widget.AdapterView;
import org.fdroid.fdroid.AppListAdapter;
public class CanUpdateAppsFragment extends AppListFragment { public class CanUpdateAppsFragment extends AppListFragment {
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
return createPlainAppList(getAppListAdapter()); return createPlainAppList();
} }
@Override @Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) { protected AppListAdapter getAppListAdapter() {
return getAppListManager().getCanUpdateAdapter();
} }
} }

View File

@ -5,15 +5,16 @@ import android.view.LayoutInflater;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.widget.AdapterView; import android.widget.AdapterView;
import org.fdroid.fdroid.AppListAdapter;
public class InstalledAppsFragment extends AppListFragment { public class InstalledAppsFragment extends AppListFragment {
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
return createPlainAppList(getAppListAdapter()); return createPlainAppList();
} }
@Override @Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) { protected AppListAdapter getAppListAdapter() {
return getAppListManager().getInstalledAdapter();
} }
} }