diff --git a/res/layout/fdroid.xml b/res/layout/fdroid.xml index dd8766859..5f84b230b 100644 --- a/res/layout/fdroid.xml +++ b/res/layout/fdroid.xml @@ -1,23 +1,15 @@ - + android:orientation="vertical"> - - - + android:layout_height="fill_parent"> - \ No newline at end of file + + + diff --git a/src/org/fdroid/fdroid/FDroid.java b/src/org/fdroid/fdroid/FDroid.java index 9e7f8f583..4af2bf571 100644 --- a/src/org/fdroid/fdroid/FDroid.java +++ b/src/org/fdroid/fdroid/FDroid.java @@ -29,14 +29,11 @@ import android.app.*; import android.os.Build; import android.support.v4.app.Fragment; import android.support.v4.app.FragmentActivity; -import android.support.v4.app.FragmentStatePagerAdapter; -import android.support.v4.view.PagerTitleStrip; import android.support.v4.view.ViewPager; +import android.view.*; import android.widget.*; import org.fdroid.fdroid.DB.App; -import org.fdroid.fdroid.R; -import android.R.drawable; import android.app.AlertDialog.Builder; import android.content.DialogInterface; import android.content.Intent; @@ -49,15 +46,11 @@ import android.os.Handler; import android.os.ResultReceiver; import android.preference.PreferenceManager; import android.util.Log; -import android.view.LayoutInflater; -import android.view.Menu; -import android.view.MenuItem; -import android.view.View; -import android.view.ViewGroup.LayoutParams; import android.widget.AdapterView.OnItemClickListener; import android.widget.AdapterView.OnItemSelectedListener; import android.widget.TabHost.TabSpec; -import org.fdroid.fdroid.views.AppListFragmentStatePageAdapter; +import org.fdroid.fdroid.views.AppListFragmentPageAdapter; +import org.fdroid.fdroid.views.AppListView; public class FDroid extends FragmentActivity implements OnItemClickListener, OnItemSelectedListener { @@ -97,6 +90,9 @@ public class FDroid extends FragmentActivity implements OnItemClickListener, private ViewPager viewPager; + // Used by pre 3.0 devices which don't have an ActionBar... + private TabHost tabHost; + // The following getters // (availableAdapter/installedAdapter/canUpdateAdapter/categoriesAdapter) // are used by the APpListViewFactory to construct views that can be used @@ -123,14 +119,15 @@ public class FDroid extends FragmentActivity implements OnItemClickListener, protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); + setContentView(R.layout.fdroid); // Needs to be created before createViews(), because that will use the // getCategoriesAdapter() accessor which expects this object... - categories = new ArrayAdapter( - this, android.R.layout.simple_spinner_item, new Vector()); - categories.setDropDownViewResource( - android.R.layout.simple_spinner_dropdown_item); + categories = new ArrayAdapter(this, + android.R.layout.simple_spinner_item, new Vector()); + categories + .setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); createViews(); createTabs(); @@ -141,9 +138,9 @@ public class FDroid extends FragmentActivity implements OnItemClickListener, call.putExtra("uri", i.getStringExtra("uri")); startActivityForResult(call, REQUEST_MANAGEREPOS); } else if (i.hasExtra(EXTRA_TAB_UPDATE)) { - boolean updateTab = i.getBooleanExtra(EXTRA_TAB_UPDATE, false); - if (updateTab) { - tabHost.setCurrentTab(2); + boolean showUpdateTab = i.getBooleanExtra(EXTRA_TAB_UPDATE, false); + if (showUpdateTab) { + selectTab(2); } } @@ -284,7 +281,12 @@ public class FDroid extends FragmentActivity implements OnItemClickListener, private void createViews() { viewPager = (ViewPager)findViewById(R.id.main_pager); - viewPager.setAdapter(new AppListFragmentStatePageAdapter(this)); + viewPager.setAdapter(new AppListFragmentPageAdapter(this)); + viewPager.setOnPageChangeListener( new ViewPager.SimpleOnPageChangeListener() { + public void onPageSelected(int position) { + selectTab(position); + } + }); } private void createTabs() { @@ -295,6 +297,14 @@ public class FDroid extends FragmentActivity implements OnItemClickListener, } } + private void selectTab(int index) { + if (Build.VERSION.SDK_INT >= 11) { + getActionBar().setSelectedNavigationItem(index); + } else { + tabHost.setCurrentTab(index); + } + } + private void updateTabText(int index) { CharSequence text = viewPager.getAdapter().getPageTitle(index); if ( Build.VERSION.SDK_INT >= 11) { @@ -328,17 +338,70 @@ public class FDroid extends FragmentActivity implements OnItemClickListener, } })); } - - pager.setOnPageChangeListener( new ViewPager.SimpleOnPageChangeListener() { - public void onPageSelected(int position) { - actionBar.setSelectedNavigationItem(position); - } - }); } + /** + * There is a bit of boiler-plate code required to get a TabWidget showing, + * which includes creating a TabHost, populating it with the TabWidget, + * and giving it a FrameLayout as a child. This will make the tabs have + * dummy empty contents and then hook them up to our ViewPager. + */ private void createOldTabs() { + tabHost = new TabHost(this); + tabHost.setLayoutParams(new TabHost.LayoutParams( + TabHost.LayoutParams.MATCH_PARENT, TabHost.LayoutParams.WRAP_CONTENT)); + TabWidget tabWidget = new TabWidget(this); - tabWidget. + tabWidget.setId(android.R.id.tabs); + tabHost.setLayoutParams(new TabHost.LayoutParams( + TabWidget.LayoutParams.MATCH_PARENT, TabWidget.LayoutParams.WRAP_CONTENT)); + + FrameLayout layout = new FrameLayout(this); + layout.setId(android.R.id.tabcontent); + layout.setLayoutParams(new TabWidget.LayoutParams(0, 0)); + + tabHost.addView(tabWidget); + tabHost.addView(layout); + tabHost.setup(); + + TabHost.TabContentFactory factory = new TabHost.TabContentFactory() { + @Override + public View createTabContent(String tag) { + return new View(FDroid.this); + } + }; + + TabSpec availableTabSpec = tabHost.newTabSpec("available") + .setIndicator( + getString(R.string.tab_noninstalled), + getResources().getDrawable(android.R.drawable.ic_input_add)) + .setContent(factory); + + TabSpec installedTabSpec = tabHost.newTabSpec("installed") + .setIndicator( + getString(R.string.tab_installed), + getResources().getDrawable(android.R.drawable.star_off)) + .setContent(factory); + + TabSpec canUpdateTabSpec = tabHost.newTabSpec("canUpdate") + .setIndicator( + getString(R.string.tab_updates), + getResources().getDrawable(android.R.drawable.star_on)) + .setContent(factory); + + tabHost.addTab(availableTabSpec); + tabHost.addTab(installedTabSpec); + tabHost.addTab(canUpdateTabSpec); + + LinearLayout contentView = (LinearLayout)findViewById(R.id.fdroid_layout); + contentView.addView(tabHost, 0); + + tabHost.setOnTabChangedListener( new TabHost.OnTabChangeListener() { + @Override + public void onTabChanged(String tabId) { + viewPager.setCurrentItem(tabHost.getCurrentTab()); + } + }); } // Populate the lists. @@ -530,17 +593,13 @@ public class FDroid extends FragmentActivity implements OnItemClickListener, public void onItemClick(AdapterView arg0, View arg1, final int arg2, long arg3) { - ViewPager pager = (ViewPager)findViewById(R.id.main_pager); + Fragment fragment = ((AppListFragmentPageAdapter)viewPager.getAdapter()).getItem(viewPager.getCurrentItem()); - final DB.App app; - String curtab = tabHost.getCurrentTabTag(); - if (curtab.equalsIgnoreCase(TAB_Installed)) { - app = (DB.App) apps_in.getItem(arg2); - } else if (curtab.equalsIgnoreCase(TAB_Updates)) { - app = (DB.App) apps_up.getItem(arg2); - } else { - app = (DB.App) apps_av.getItem(arg2); - } + // The fragment.getView() returns a wrapper object which has the + // actual view we're interested in inside: + // http://stackoverflow.com/a/13684505 + AppListView view = ((AppListView)((ViewGroup)fragment.getView()).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); diff --git a/src/org/fdroid/fdroid/views/AppListFragmentStatePageAdapter.java b/src/org/fdroid/fdroid/views/AppListFragmentPageAdapter.java similarity index 81% rename from src/org/fdroid/fdroid/views/AppListFragmentStatePageAdapter.java rename to src/org/fdroid/fdroid/views/AppListFragmentPageAdapter.java index 0c74692f6..b26e1f891 100644 --- a/src/org/fdroid/fdroid/views/AppListFragmentStatePageAdapter.java +++ b/src/org/fdroid/fdroid/views/AppListFragmentPageAdapter.java @@ -2,19 +2,23 @@ package org.fdroid.fdroid.views; import android.os.Bundle; import android.support.v4.app.Fragment; -import android.support.v4.app.FragmentStatePagerAdapter; +import android.support.v4.app.FragmentPagerAdapter; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import org.fdroid.fdroid.FDroid; import org.fdroid.fdroid.R; -public class AppListFragmentStatePageAdapter extends FragmentStatePagerAdapter { +/** + * Used by the FDroid activity in conjunction with its ViewPager to support + * swiping of tabs for both old devices (< 3.0) and new devices. + */ +public class AppListFragmentPageAdapter extends FragmentPagerAdapter { private FDroid parent; private Fragment[] fragments = new Fragment[3]; - public AppListFragmentStatePageAdapter(FDroid parent) { + public AppListFragmentPageAdapter(FDroid parent) { super(parent.getSupportFragmentManager()); this.parent = parent; @@ -57,7 +61,7 @@ public class AppListFragmentStatePageAdapter extends FragmentStatePagerAdapter { return parent.getString(R.string.tab_installed); case 2: String updates = parent.getString(R.string.tab_updates); - updates += " (" + parent.getCanUpdateAdapter() + ")"; + updates += " (" + parent.getCanUpdateAdapter().getCount() + ")"; return updates; default: return ""; diff --git a/src/org/fdroid/fdroid/views/AppListView.java b/src/org/fdroid/fdroid/views/AppListView.java new file mode 100644 index 000000000..c1b1f9272 --- /dev/null +++ b/src/org/fdroid/fdroid/views/AppListView.java @@ -0,0 +1,32 @@ +package org.fdroid.fdroid.views; + +import android.content.Context; +import android.widget.LinearLayout; +import android.widget.ListView; + +/** + * There are three main app-lists in the UI: + * - Available + * - Installed + * - Apps which can be updated + * This class provides a View which knows about these app lists, but can have + * different contents (e.g. a drop down list of categories). It allows us to + * get a reference to the selected item in the FDroid Activity, without having + * to know which list we are actually looking at. + */ +public class AppListView extends LinearLayout { + + private ListView appList; + + public AppListView(Context context) { + super(context); + } + + public void setAppList(ListView appList) { + this.appList = appList; + } + + public ListView getAppList() { + return appList; + } +} diff --git a/src/org/fdroid/fdroid/views/AppListViewFactory.java b/src/org/fdroid/fdroid/views/AppListViewFactory.java index 62d538ff1..927641c88 100644 --- a/src/org/fdroid/fdroid/views/AppListViewFactory.java +++ b/src/org/fdroid/fdroid/views/AppListViewFactory.java @@ -2,7 +2,6 @@ package org.fdroid.fdroid.views; import android.view.View; import android.view.ViewGroup; -import android.widget.ArrayAdapter; import android.widget.LinearLayout; import android.widget.ListView; import android.widget.Spinner; @@ -10,6 +9,10 @@ import org.fdroid.fdroid.AppListAdapter; import org.fdroid.fdroid.FDroid; import org.fdroid.fdroid.R; +/** + * Provides functionality to create the three lists of applications + * required for the FDroid activity. + */ public class AppListViewFactory { private FDroid parent; @@ -18,32 +21,52 @@ public class AppListViewFactory { this.parent = parent; } - public View createAvailableView() { - ListView list = createAppListView(parent.getAvailableAdapter()); - LinearLayout view = new LinearLayout(parent); + public AppListView createAvailableView() { + AppListView view = new AppListView(parent); view.setOrientation(LinearLayout.VERTICAL); - Spinner cats = new Spinner(parent); + Spinner spinner = new Spinner(parent); // Giving it an ID lets the default save/restore state // functionality do its stuff. - cats.setId(R.id.categorySpinner); - cats.setAdapter(parent.getCategoriesAdapter()); - cats.setOnItemSelectedListener(parent); - view.addView(cats, new ViewGroup.LayoutParams( - LinearLayout.LayoutParams.FILL_PARENT, - LinearLayout.LayoutParams.WRAP_CONTENT)); - view.addView(list, new ViewGroup.LayoutParams( - LinearLayout.LayoutParams.FILL_PARENT, - LinearLayout.LayoutParams.FILL_PARENT)); + spinner.setId(R.id.categorySpinner); + spinner.setAdapter(parent.getCategoriesAdapter()); + spinner.setOnItemSelectedListener(parent); + + view.addView( + spinner, + new ViewGroup.LayoutParams( + LinearLayout.LayoutParams.MATCH_PARENT, + LinearLayout.LayoutParams.WRAP_CONTENT)); + + ListView list = createAppListView(parent.getAvailableAdapter()); + view.setAppList(list); + view.addView( + list, + new ViewGroup.LayoutParams( + LinearLayout.LayoutParams.MATCH_PARENT, + LinearLayout.LayoutParams.MATCH_PARENT)); + return view; } - public View createInstalledView() { - return createAppListView(parent.getInstalledAdapter()); + public AppListView createInstalledView() { + return createPlainAppList(parent.getInstalledAdapter()); } - public View createCanUpdateView() { - return createAppListView(parent.getCanUpdateAdapter()); + public AppListView createCanUpdateView() { + return createPlainAppList(parent.getCanUpdateAdapter()); + } + + protected AppListView createPlainAppList(AppListAdapter adapter) { + AppListView view = new AppListView(parent); + ListView list = createAppListView(adapter); + view.addView( + list, + new ViewGroup.LayoutParams( + ViewGroup.LayoutParams.MATCH_PARENT, + ViewGroup.LayoutParams.WRAP_CONTENT)); + view.setAppList(list); + return view; } protected ListView createAppListView(AppListAdapter adapter) {