diff --git a/res/layout/addrepo.xml b/res/layout/addrepo.xml index 0f0845155..c67ee5d7b 100644 --- a/res/layout/addrepo.xml +++ b/res/layout/addrepo.xml @@ -15,7 +15,7 @@ android:layout_width="wrap_content" android:ems="20" android:layout_height="wrap_content" - android:text="@string/repo_add_http"/> + android:text="https://"/> + Downloading\n%2$s / %3$s (%4$d%%) from\n%1$s + Processing application\n%2$d of %3$d from\n%1$s + Connecting to\n%1$s + Checking all apps compatibility with your device… + diff --git a/src/org/fdroid/fdroid/AppDetails.java b/src/org/fdroid/fdroid/AppDetails.java index 5e4f75917..624a32cc6 100644 --- a/src/org/fdroid/fdroid/AppDetails.java +++ b/src/org/fdroid/fdroid/AppDetails.java @@ -22,9 +22,9 @@ import java.io.File; import java.security.NoSuchAlgorithmException; import java.util.ArrayList; import java.util.List; -import java.util.Vector; import android.support.v4.view.MenuItemCompat; +import org.fdroid.fdroid.compat.MenuManager; import org.xml.sax.XMLReader; import android.app.AlertDialog; @@ -127,7 +127,7 @@ public class AppDetails extends ListActivity { if (apk.detail_size == 0) { size.setText(""); } else { - size.setText(getFriendlySize(apk.detail_size)); + size.setText(Utils.getFriendlySize(apk.detail_size)); } TextView buildtype = (TextView) v.findViewById(R.id.buildtype); if (apk.srcname != null) { @@ -153,19 +153,6 @@ public class AppDetails extends ListActivity { } } - private static final String[] FRIENDLY_SIZE_FORMAT = { - "%.0f B", "%.0f KiB", "%.1f MiB", "%.2f GiB" }; - - private static String getFriendlySize(int size) { - double s = size; - int i = 0; - while (i < FRIENDLY_SIZE_FORMAT.length - 1 && s >= 1024) { - s = (100 * s / 1024) / 100.0; - i++; - } - return String.format(FRIENDLY_SIZE_FORMAT[i], s); - } - private static final int INSTALL = Menu.FIRST; private static final int UNINSTALL = Menu.FIRST + 1; private static final int WEBSITE = Menu.FIRST + 2; @@ -249,7 +236,9 @@ public class AppDetails extends ListActivity { resetRequired = false; } resetViews(); - invalidateOptionsMenu(); + + MenuManager.create(this).invalidateOptionsMenu(); + if (downloadHandler != null) { downloadHandler.startUpdates(); } @@ -302,7 +291,7 @@ public class AppDetails extends ListActivity { Log.d("FDroid", "Getting application details for " + appid); app = null; - Vector apps = ((FDroidApp) getApplication()).getApps(); + List apps = ((FDroidApp) getApplication()).getApps(); for (DB.App tapp : apps) { if (tapp.id.equals(appid)) { app = tapp; @@ -797,4 +786,5 @@ public class AppDetails extends ListActivity { break; } } + } diff --git a/src/org/fdroid/fdroid/AppListManager.java b/src/org/fdroid/fdroid/AppListManager.java index 59f66f161..99541eeb8 100644 --- a/src/org/fdroid/fdroid/AppListManager.java +++ b/src/org/fdroid/fdroid/AppListManager.java @@ -15,7 +15,7 @@ import java.util.*; */ public class AppListManager { - private Vector allApps = null; + private List allApps = null; private FDroid fdroidActivity; @@ -55,7 +55,7 @@ public class AppListManager { // Needs to be created before createViews(), because that will use the // getCategoriesAdapter() accessor which expects this object... categories = new ArrayAdapter(activity, - android.R.layout.simple_spinner_item, new Vector()); + android.R.layout.simple_spinner_item, new ArrayList()); categories .setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); } @@ -185,7 +185,7 @@ public class AppListManager { Date recentDate = calcMaxHistory(); AppFilter appFilter = new AppFilter(fdroidActivity); - Vector availApps = new Vector(); + List availApps = new ArrayList(); for (DB.App app : allApps) { boolean isInCategory = isInCategory(app, currentCategory, recentDate); @@ -215,7 +215,10 @@ public class AppListManager { } public void setCurrentCategory(String currentCategory) { - this.currentCategory = currentCategory; + if (!this.currentCategory.equals(currentCategory)){ + this.currentCategory = currentCategory; + repopulateLists(); + } } static class WhatsNewComparator implements Comparator { diff --git a/src/org/fdroid/fdroid/DB.java b/src/org/fdroid/fdroid/DB.java index 7a15eb142..2f55a8e94 100644 --- a/src/org/fdroid/fdroid/DB.java +++ b/src/org/fdroid/fdroid/DB.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010-12 Ciaran Gultnieks, ciaran@ciarang.com + * Copyright (C) 2010-13 Ciaran Gultnieks, ciaran@ciarang.com * Copyright (C) 2009 Roberto Jacinto, roberto.jacinto@caixamagica.pt * * This program is free software; you can redistribute it and/or @@ -21,6 +21,7 @@ package org.fdroid.fdroid; import java.io.File; import java.text.SimpleDateFormat; +import java.util.ArrayList; import java.util.Collections; import java.util.Date; import java.util.HashMap; @@ -28,7 +29,6 @@ import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; -import java.util.Vector; import java.util.concurrent.Semaphore; import android.annotation.TargetApi; @@ -41,7 +41,6 @@ import android.content.pm.PackageManager; import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteOpenHelper; -import android.os.Build; import android.os.Environment; import android.preference.PreferenceManager; import android.text.TextUtils.SimpleStringSplitter; @@ -116,7 +115,7 @@ public class DB { updated = false; added = null; lastUpdated = null; - apks = new Vector(); + apks = new ArrayList(); detail_Populated = false; compatible = false; } @@ -181,7 +180,7 @@ public class DB { public boolean updated; // List of apks. - public Vector apks; + public List apks; // Get the current version - this will be one of the Apks from 'apks'. // Can return null if there are no available versions. @@ -278,28 +277,23 @@ public class DB { // check if an APK is compatible with the user's device. public static abstract class CompatibilityChecker { - // Because Build.VERSION.SDK_INT requires API level 5 - @SuppressWarnings("deprecation") - protected final static int SDK_INT = Integer - .parseInt(Build.VERSION.SDK); - public abstract boolean isCompatible(Apk apk); public static CompatibilityChecker getChecker(Context ctx) { CompatibilityChecker checker; - if (SDK_INT >= 5) + if (Utils.hasApi(5)) checker = new EclairChecker(ctx); else checker = new BasicChecker(); Log.d("FDroid", "Compatibility checker for API level " - + SDK_INT + ": " + checker.getClass().getName()); + + Utils.getApi() + ": " + checker.getClass().getName()); return checker; } } private static class BasicChecker extends CompatibilityChecker { public boolean isCompatible(Apk apk) { - return (apk.minSdkVersion <= SDK_INT); + return (apk.minSdkVersion <= Utils.getApi()); } } @@ -329,7 +323,7 @@ public class DB { } public boolean isCompatible(Apk apk) { - if (apk.minSdkVersion > SDK_INT) + if (apk.minSdkVersion > Utils.getApi()) return false; if (apk.features != null) { for (String feat : apk.features) { @@ -379,6 +373,7 @@ public class DB { public static void resetTransient(SQLiteDatabase db) { db.execSQL("drop table " + TABLE_APP); db.execSQL("drop table " + TABLE_APK); + db.execSQL("update " + TABLE_REPO + " set lastetag = NULL"); createAppApk(db); } @@ -412,7 +407,7 @@ public class DB { // Migrate repo list to new structure. (No way to change primary // key in sqlite - table must be recreated) if (oldVersion < 20) { - Vector oldrepos = new Vector(); + List oldrepos = new ArrayList(); Cursor c = db.rawQuery("select address, inuse, pubkey from " + TABLE_REPO, null); c.moveToFirst(); @@ -503,7 +498,7 @@ public class DB { // Get the number of apps that have updates available. This can be a // time consuming operation. public int getNumUpdates() { - Vector apps = getApps(true); + List apps = getApps(true); int count = 0; for (App app : apps) { if (app.hasUpdates) @@ -512,8 +507,8 @@ public class DB { return count; } - public Vector getCategories() { - Vector result = new Vector(); + public List getCategories() { + List result = new ArrayList(); Cursor c = null; try { c = db.rawQuery("select distinct category from " + TABLE_APP @@ -591,14 +586,15 @@ public class DB { // Return a list of apps matching the given criteria. Filtering is // also done based on compatibility and anti-features according to // the user's current preferences. - public Vector getApps(boolean getinstalledinfo) { + public List getApps(boolean getinstalledinfo) { // If we're going to need it, get info in what's currently installed Map systemApks = null; if (getinstalledinfo) { Log.d("FDroid", "Reading installed packages"); systemApks = new HashMap(); - List installedPackages = mContext.getPackageManager().getInstalledPackages(0); + List installedPackages = mContext.getPackageManager() + .getInstalledPackages(0); for (PackageInfo appInfo : installedPackages) { systemApks.put(appInfo.packageName, appInfo); } @@ -696,7 +692,7 @@ public class DB { + (System.currentTimeMillis() - startTime) + " ms)"); } - Vector result = new Vector(apps.values()); + List result = new ArrayList(apps.values()); Collections.sort(result); // Fill in the hasUpdates fields if we have the necessary information... @@ -720,9 +716,9 @@ public class DB { return result; } - public Vector doSearch(String query) { + public List doSearch(String query) { - Vector ids = new Vector(); + List ids = new ArrayList(); Cursor c = null; try { String filter = "%" + query + "%"; @@ -770,11 +766,11 @@ public class DB { } } - private Vector updateApps = null; + private List updateApps = null; // Called before a repo update starts. Returns the number of updates // available beforehand. - public int beginUpdate(Vector apps) { + public int beginUpdate(List apps) { // Get a list of all apps. All the apps and apks in this list will // have 'updated' set to false at this point, and we will only set // it to true when we see the app/apk in a repository. Thus, at the @@ -865,7 +861,7 @@ public class DB { // compatible apk - if it's not, leave it out) // Also keep a list of which were compatible, because they're the // only ones we'll add, unless the showIncompatible preference is set. - Vector compatibleapks = new Vector(); + List compatibleapks = new ArrayList(); for (Apk apk : upapp.apks) { if (compatChecker.isCompatible(apk)) { apk.compatible = true; @@ -1016,8 +1012,8 @@ public class DB { } // Get a list of the configured repositories. - public Vector getRepos() { - Vector repos = new Vector(); + public List getRepos() { + List repos = new ArrayList(); Cursor c = null; try { c = db.rawQuery( @@ -1078,10 +1074,27 @@ public class DB { db.insert(TABLE_REPO, null, values); } - public void removeServers(Vector addresses) { + public void removeRepos(List addresses) { db.beginTransaction(); try { for (String address : addresses) { + + // Before removing the repo, remove any apks that are + // connected to it... + Cursor c = null; + try { + c = db.rawQuery("select id from " + TABLE_REPO + + " where address = '" + address + "'", null); + c.moveToFirst(); + if (!c.isAfterLast()) { + db.delete(TABLE_APK, "repo = ?", + new String[] { Integer.toString(c.getInt(0)) }); + } + } finally { + if (c != null) { + c.close(); + } + } db.delete(TABLE_REPO, "address = ?", new String[] { address }); } db.setTransactionSuccessful(); diff --git a/src/org/fdroid/fdroid/FDroid.java b/src/org/fdroid/fdroid/FDroid.java index c14703eb5..9278bc7d8 100644 --- a/src/org/fdroid/fdroid/FDroid.java +++ b/src/org/fdroid/fdroid/FDroid.java @@ -19,28 +19,17 @@ package org.fdroid.fdroid; -import android.app.ActionBar; -import java.util.Calendar; -import java.util.Collections; -import java.util.Comparator; -import java.util.Date; -import java.util.Vector; - import android.support.v4.view.MenuItemCompat; -import org.fdroid.fdroid.DB.App; import org.fdroid.fdroid.R; -import android.R.drawable; import android.app.AlertDialog; import android.app.AlertDialog.Builder; -import android.app.FragmentTransaction; import android.app.ProgressDialog; import android.content.DialogInterface; import android.content.Intent; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.net.Uri; -import android.os.Build; import android.os.Bundle; import android.os.Handler; import android.os.ResultReceiver; @@ -52,7 +41,7 @@ import android.view.Menu; import android.view.MenuItem; import android.view.View; import android.widget.*; -import android.widget.TabHost.TabSpec; +import org.fdroid.fdroid.compat.TabManager; import org.fdroid.fdroid.views.AppListFragmentPageAdapter; public class FDroid extends FragmentActivity { @@ -75,8 +64,7 @@ public class FDroid extends FragmentActivity { private AppListManager manager = null; - // Used by pre 3.0 devices which don't have an ActionBar... - private TabHost tabHost; + private TabManager tabManager = null; public AppListManager getManager() { return manager; @@ -89,7 +77,7 @@ public class FDroid extends FragmentActivity { manager = new AppListManager(this); setContentView(R.layout.fdroid); createViews(); - createTabs(); + getTabManager().createTabs(); // Must be done *after* createViews, because it will involve a // callback to update the tab label for the "update" tab. This @@ -104,7 +92,7 @@ public class FDroid extends FragmentActivity { } else if (i.hasExtra(EXTRA_TAB_UPDATE)) { boolean showUpdateTab = i.getBooleanExtra(EXTRA_TAB_UPDATE, false); if (showUpdateTab) { - selectTab(2); + getTabManager().selectTab(2); } } } @@ -250,127 +238,7 @@ public class FDroid extends FragmentActivity { viewPager.setAdapter(viewPageAdapter); viewPager.setOnPageChangeListener( new ViewPager.SimpleOnPageChangeListener() { public void onPageSelected(int position) { - selectTab(position); - } - }); - } - - private void createTabs() { - if (Build.VERSION.SDK_INT >= 11) { - createActionBarTabs(); - } else { - createOldTabs(); - } - } - - private void selectTab(int index) { - if (Build.VERSION.SDK_INT >= 11) { - getActionBar().setSelectedNavigationItem(index); - } else { - tabHost.setCurrentTab(index); - } - } - - public void refreshUpdateTabLabel() { - final int INDEX = 2; - CharSequence text = viewPager.getAdapter().getPageTitle(INDEX); - if ( Build.VERSION.SDK_INT >= 11) { - getActionBar().getTabAt(INDEX).setText(text); - } 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); - } - } - - private void createActionBarTabs() { - final ActionBar actionBar = getActionBar(); - final ViewPager pager = viewPager; - actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS); - for (int i = 0; i < viewPager.getAdapter().getCount(); i ++) { - CharSequence label = viewPager.getAdapter().getPageTitle(i); - actionBar.addTab( - actionBar.newTab() - .setText(label) - .setTabListener(new ActionBar.TabListener() { - public void onTabSelected(ActionBar.Tab tab, - FragmentTransaction ft) { - pager.setCurrentItem(tab.getPosition()); - } - - @Override - public void onTabUnselected(ActionBar.Tab tab, FragmentTransaction ft) { - } - - @Override - public void onTabReselected(ActionBar.Tab tab, FragmentTransaction ft) { - } - })); - } - } - - /** - * 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.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()); + getTabManager().selectTab(position); } }); } @@ -384,13 +252,19 @@ public class FDroid extends FragmentActivity { @Override protected void onReceiveResult(int resultCode, Bundle resultData) { - if (resultCode == 1) { - Toast.makeText(FDroid.this, resultData.getString("errmsg"), - Toast.LENGTH_LONG).show(); - } else { + String message = resultData.getString(UpdateService.RESULT_MESSAGE); + boolean finished = false; + if (resultCode == UpdateService.STATUS_ERROR) { + Toast.makeText(FDroid.this, message, Toast.LENGTH_LONG).show(); + finished = true; + } else if (resultCode == UpdateService.STATUS_COMPLETE) { repopulateViews(); + finished = true; + } else if (resultCode == UpdateService.STATUS_INFO) { + pd.setMessage(message); } - if (pd.isShowing()) + + if (finished && pd.isShowing()) pd.dismiss(); } } @@ -409,7 +283,7 @@ public class FDroid extends FragmentActivity { 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."); - getPreferences(MODE_PRIVATE).edit().putBoolean(TRIED_EMPTY_UPDATE, true).apply(); + getPreferences(MODE_PRIVATE).edit().putBoolean(TRIED_EMPTY_UPDATE, true).commit(); updateRepos(); return true; } else { @@ -434,4 +308,15 @@ public class FDroid extends FragmentActivity { startService(intent); } + private TabManager getTabManager() { + if (tabManager == null) { + tabManager = TabManager.create(this, viewPager); + } + return tabManager; + } + + public void refreshUpdateTabLabel() { + getTabManager().refreshTabLabel(TabManager.INDEX_CAN_UPDATE); + } + } diff --git a/src/org/fdroid/fdroid/FDroidApp.java b/src/org/fdroid/fdroid/FDroidApp.java index c4b765cd3..eb8ead6b9 100644 --- a/src/org/fdroid/fdroid/FDroidApp.java +++ b/src/org/fdroid/fdroid/FDroidApp.java @@ -19,7 +19,8 @@ package org.fdroid.fdroid; import java.io.File; -import java.util.Vector; +import java.util.ArrayList; +import java.util.List; import java.util.concurrent.Semaphore; import android.app.Application; @@ -47,7 +48,7 @@ public class FDroidApp extends Application { } // Global list of all known applications. - private Vector apps; + private List apps; // Set when something has changed (database or installed apps) so we know // we should invalidate the apps. @@ -70,7 +71,7 @@ public class FDroidApp extends Application { // Get a list of all known applications. Should not be called when the // database is locked (i.e. between DB.getDB() and db.releaseDB(). The // contents should never be modified, it's for reading only. - public Vector getApps() { + public List getApps() { boolean invalid = false; try { @@ -95,7 +96,7 @@ public class FDroidApp extends Application { } } if (apps == null) - return new Vector(); + return new ArrayList(); return apps; } diff --git a/src/org/fdroid/fdroid/ManageRepo.java b/src/org/fdroid/fdroid/ManageRepo.java index 5953f3f61..d15fc98e6 100644 --- a/src/org/fdroid/fdroid/ManageRepo.java +++ b/src/org/fdroid/fdroid/ManageRepo.java @@ -26,7 +26,6 @@ import java.util.Formatter; import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.Vector; import android.app.AlertDialog; import android.app.AlertDialog.Builder; @@ -55,7 +54,7 @@ public class ManageRepo extends ListActivity { private boolean changed = false; - private Vector repos; + private List repos; @Override protected void onCreate(Bundle savedInstanceState) { @@ -201,7 +200,7 @@ public class ManageRepo extends ListActivity { return true; case REM_REPO: - final Vector rem_lst = new Vector(); + final List rem_lst = new ArrayList(); CharSequence[] b = new CharSequence[repos.size()]; for (int i = 0; i < repos.size(); i++) { b[i] = repos.get(i).address; @@ -215,9 +214,9 @@ public class ManageRepo extends ListActivity { public void onClick(DialogInterface dialog, int whichButton, boolean isChecked) { if (isChecked) { - rem_lst.addElement(repos.get(whichButton).address); + rem_lst.add(repos.get(whichButton).address); } else { - rem_lst.removeElement(repos.get(whichButton).address); + rem_lst.remove(repos.get(whichButton).address); } } }); @@ -227,7 +226,7 @@ public class ManageRepo extends ListActivity { int whichButton) { try { DB db = DB.getDB(); - db.removeServers(rem_lst); + db.removeRepos(rem_lst); } finally { DB.releaseDB(); } diff --git a/src/org/fdroid/fdroid/ProgressListener.java b/src/org/fdroid/fdroid/ProgressListener.java new file mode 100644 index 000000000..d38e342cd --- /dev/null +++ b/src/org/fdroid/fdroid/ProgressListener.java @@ -0,0 +1,54 @@ +package org.fdroid.fdroid; + +import android.os.Bundle; + +public interface ProgressListener { + + public void onProgress(Event event); + + // I went a bit overboard with the overloaded constructors, but they all + // seemed potentially useful and unambiguous, so I just put them in there + // while I'm here. + public static class Event { + + public static final int NO_VALUE = Integer.MIN_VALUE; + + public final int type; + public final Bundle data; + + // These two are not final, so that you can create a template Event, + // pass it into a function which performs something over time, and + // that function can initialize "total" and progressively + // update "progress" + public int progress; + public int total; + + public Event(int type) { + this(type, NO_VALUE, NO_VALUE, null); + } + + public Event(int type, Bundle data) { + this(type, NO_VALUE, NO_VALUE, data); + } + + public Event(int type, int progress) { + this(type, progress, NO_VALUE, null); + } + + public Event(int type, int progress, Bundle data) { + this(type, NO_VALUE, NO_VALUE, data); + } + + public Event(int type, int progress, int total) { + this(type, progress, total, null); + } + + public Event(int type, int progress, int total, Bundle data) { + this.type = type; + this.progress = progress; + this.total = total; + this.data = data == null ? new Bundle() : data; + } + } + +} diff --git a/src/org/fdroid/fdroid/RepoXMLHandler.java b/src/org/fdroid/fdroid/RepoXMLHandler.java index 2ab7360a5..6ad04fd7c 100644 --- a/src/org/fdroid/fdroid/RepoXMLHandler.java +++ b/src/org/fdroid/fdroid/RepoXMLHandler.java @@ -26,14 +26,13 @@ import java.io.FileReader; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; -import java.io.Reader; import java.net.HttpURLConnection; import java.net.MalformedURLException; import java.net.URL; import java.security.cert.Certificate; import java.text.ParseException; import java.text.SimpleDateFormat; -import java.util.Vector; +import java.util.List; import java.util.jar.JarEntry; import java.util.jar.JarFile; @@ -41,6 +40,7 @@ import javax.net.ssl.SSLHandshakeException; import javax.xml.parsers.SAXParser; import javax.xml.parsers.SAXParserFactory; +import android.os.Bundle; import org.xml.sax.Attributes; import org.xml.sax.InputSource; import org.xml.sax.SAXException; @@ -54,10 +54,10 @@ import android.util.Log; public class RepoXMLHandler extends DefaultHandler { - // The ID of the repo we're processing. - private int repo; + // The repo we're processing. + private DB.Repo repo; - private Vector apps; + private List apps; private DB.App curapp = null; private DB.Apk curapk = null; @@ -66,13 +66,23 @@ public class RepoXMLHandler extends DefaultHandler { private String pubkey; private String hashType; + private int progressCounter = 0; + private ProgressListener progressListener; + + public static final int PROGRESS_TYPE_DOWNLOAD = 1; + public static final int PROGRESS_TYPE_PROCESS_XML = 2; + + public static final String PROGRESS_DATA_REPO = "repo"; + // The date format used in the repo XML file. private SimpleDateFormat mXMLDateFormat = new SimpleDateFormat("yyyy-MM-dd"); + private int totalAppCount; - public RepoXMLHandler(int repo, Vector apps) { + public RepoXMLHandler(DB.Repo repo, List apps, ProgressListener listener) { this.repo = repo; this.apps = apps; pubkey = null; + progressListener = listener; } @Override @@ -219,27 +229,37 @@ public class RepoXMLHandler extends DefaultHandler { curapp.requirements = DB.CommaSeparatedList.make(str); } } - } + private static Bundle createProgressData(String repoAddress) { + Bundle data = new Bundle(); + data.putString(PROGRESS_DATA_REPO, repoAddress); + return data; + } + @Override public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException { - super.startElement(uri, localName, qName, attributes); - if (localName == "repo") { + if (localName.equals("repo")) { String pk = attributes.getValue("", "pubkey"); if (pk != null) pubkey = pk; - } else if (localName == "application" && curapp == null) { + } else if (localName.equals("application") && curapp == null) { curapp = new DB.App(); curapp.detail_Populated = true; - } else if (localName == "package" && curapp != null && curapk == null) { + Bundle progressData = createProgressData(repo.address); + progressCounter ++; + progressListener.onProgress( + new ProgressListener.Event( + RepoXMLHandler.PROGRESS_TYPE_PROCESS_XML, progressCounter, + totalAppCount, progressData)); + } else if (localName.equals("package") && curapp != null && curapk == null) { curapk = new DB.Apk(); curapk.id = curapp.id; - curapk.repo = repo; + curapk.repo = repo.id; hashType = null; - } else if (localName == "hash" && curapk != null) { + } else if (localName.equals("hash") && curapk != null) { hashType = attributes.getValue("", "type"); } curchars.setLength(0); @@ -252,29 +272,39 @@ public class RepoXMLHandler extends DefaultHandler { // empty) may contain an etag value for the response, or it may be left // empty if none was available. private static int getRemoteFile(Context ctx, String url, String dest, - String etag, StringBuilder retag) throws MalformedURLException, + String etag, StringBuilder retag, + ProgressListener progressListener, + ProgressListener.Event progressEvent) throws MalformedURLException, IOException { long startTime = System.currentTimeMillis(); URL u = new URL(url); - HttpURLConnection uc = (HttpURLConnection) u.openConnection(); + HttpURLConnection connection = (HttpURLConnection) u.openConnection(); if (etag != null) - uc.setRequestProperty("If-None-Match", etag); + connection.setRequestProperty("If-None-Match", etag); int totalBytes = 0; - int code = uc.getResponseCode(); + int code = connection.getResponseCode(); if (code == 200) { + // Testing in the emulator for me, showed that figuring out the filesize took about 1 to 1.5 seconds. + // To put this in context, downloading a repo of: + // - 400k takes ~6 seconds + // - 5k takes ~3 seconds + // on my connection. I think the 1/1.5 seconds is worth it, because as the repo grows, the tradeoff will + // become more worth it. + progressEvent.total = connection.getContentLength(); + Log.d("FDroid", "Downloading " + progressEvent.total + " bytes from " + url); InputStream input = null; OutputStream output = null; try { - input = new URL(url).openStream(); + input = connection.getInputStream(); output = ctx.openFileOutput(dest, Context.MODE_PRIVATE); - Utils.copy(input, output); + Utils.copy(input, output, progressListener, progressEvent); } finally { Utils.closeQuietly(output); Utils.closeQuietly(input); } - String et = uc.getHeaderField("ETag"); + String et = connection.getHeaderField("ETag"); if (et != null) retag.append(et); } @@ -293,7 +323,8 @@ public class RepoXMLHandler extends DefaultHandler { // value for the index that was successfully processed, or it may contain // null if none was available. public static String doUpdate(Context ctx, DB.Repo repo, - Vector apps, StringBuilder newetag, Vector keeprepos) { + List apps, StringBuilder newetag, List keeprepos, + ProgressListener progressListener) { try { int code = 0; @@ -309,8 +340,11 @@ public class RepoXMLHandler extends DefaultHandler { address += "?" + pi.versionName; } catch (Exception e) { } + Bundle progressData = createProgressData(repo.address); + ProgressListener.Event event = new ProgressListener.Event( + RepoXMLHandler.PROGRESS_TYPE_DOWNLOAD, progressData); code = getRemoteFile(ctx, address, "tempindex.jar", - repo.lastetag, newetag); + repo.lastetag, newetag, progressListener, event ); if (code == 200) { String jarpath = ctx.getFilesDir() + "/tempindex.jar"; JarFile jar = null; @@ -365,8 +399,12 @@ public class RepoXMLHandler extends DefaultHandler { // It's an old-fashioned unsigned repo... Log.d("FDroid", "Getting unsigned index from " + repo.address); + Bundle eventData = createProgressData(repo.address); + ProgressListener.Event event = new ProgressListener.Event( + RepoXMLHandler.PROGRESS_TYPE_DOWNLOAD, eventData); code = getRemoteFile(ctx, repo.address + "/index.xml", - "tempindex.xml", repo.lastetag, newetag); + "tempindex.xml", repo.lastetag, newetag, + progressListener, event); } if (code == 200) { @@ -374,11 +412,22 @@ public class RepoXMLHandler extends DefaultHandler { SAXParserFactory spf = SAXParserFactory.newInstance(); SAXParser sp = spf.newSAXParser(); XMLReader xr = sp.getXMLReader(); - RepoXMLHandler handler = new RepoXMLHandler(repo.id, apps); + RepoXMLHandler handler = new RepoXMLHandler(repo, apps, progressListener); xr.setContentHandler(handler); - Reader r = new BufferedReader(new FileReader(new File( - ctx.getFilesDir() + "/tempindex.xml"))); + File tempIndex = new File(ctx.getFilesDir() + "/tempindex.xml"); + BufferedReader r = new BufferedReader(new FileReader(tempIndex)); + + // A bit of a hack, this might return false positives if an apps description + // or some other part of the XML file contains this, but it is a pretty good + // estimate and makes the progress counter more informative. + // As with asking the server about the size of the index before downloading, + // this also has a time tradeoff. It takes about three seconds to iterate + // through the file and count 600 apps on a slow emulator (v17), but if it is + // taking two minutes to update, the three second wait may be worth it. + final String APPLICATION = " matchingids = new Vector(); + List matchingids = new ArrayList(); try { DB db = DB.getDB(); matchingids = db.doSearch(mQuery); @@ -78,9 +79,9 @@ public class SearchResults extends ListActivity { DB.releaseDB(); } - Vector apps = new Vector(); + List apps = new ArrayList(); AppFilter appfilter = new AppFilter(this); - Vector tapps = ((FDroidApp) getApplication()).getApps(); + List tapps = ((FDroidApp) getApplication()).getApps(); for (DB.App tapp : tapps) { boolean include = false; for (String tid : matchingids) { diff --git a/src/org/fdroid/fdroid/UpdateService.java b/src/org/fdroid/fdroid/UpdateService.java index bfd66bf99..0233f77c9 100644 --- a/src/org/fdroid/fdroid/UpdateService.java +++ b/src/org/fdroid/fdroid/UpdateService.java @@ -24,7 +24,8 @@ import java.io.InputStream; import java.io.OutputStream; import java.net.HttpURLConnection; import java.net.URL; -import java.util.Vector; +import java.util.ArrayList; +import java.util.List; import android.app.AlarmManager; import android.app.IntentService; @@ -41,7 +42,14 @@ import android.os.SystemClock; import android.preference.PreferenceManager; import android.util.Log; -public class UpdateService extends IntentService { +public class UpdateService extends IntentService implements ProgressListener { + + public static final String RESULT_MESSAGE = "msg"; + public static final int STATUS_COMPLETE = 0; + public static final int STATUS_ERROR = 1; + public static final int STATUS_INFO = 2; + + private ResultReceiver receiver = null; public UpdateService() { super("UpdateService"); @@ -71,24 +79,41 @@ public class UpdateService extends IntentService { } } + protected void sendStatus(int statusCode ) { + sendStatus(statusCode, null); + } + + protected void sendStatus(int statusCode, String message ) { + if (receiver != null) { + Bundle resultData = new Bundle(); + if (message != null && message.length() > 0) + resultData.putString(RESULT_MESSAGE, message); + receiver.send( statusCode, resultData ); + } + } + + /** + * We might be doing a scheduled run, or we might have been launched by + * the app in response to a user's request. If we have a receiver, it's + * the latter... + */ + private boolean isScheduledRun() { + return receiver == null; + } + protected void onHandleIntent(Intent intent) { - // We might be doing a scheduled run, or we might have been launched by - // the app in response to a user's request. If we get this receiver, - // it's - // the latter... - ResultReceiver receiver = intent.getParcelableExtra("receiver"); + receiver = intent.getParcelableExtra("receiver"); long startTime = System.currentTimeMillis(); String errmsg = ""; - try { SharedPreferences prefs = PreferenceManager .getDefaultSharedPreferences(getBaseContext()); // See if it's time to actually do anything yet... - if (receiver == null) { + if (isScheduledRun()) { long lastUpdate = prefs.getLong("lastUpdateCheck", 0); String sint = prefs.getString("updateInterval", "0"); int interval = Integer.parseInt(sint); @@ -110,7 +135,7 @@ public class UpdateService extends IntentService { // database while we do all the downloading, etc... int prevUpdates = 0; int newUpdates = 0; - Vector repos; + List repos; try { DB db = DB.getDB(); repos = db.getRepos(); @@ -119,14 +144,17 @@ public class UpdateService extends IntentService { } // Process each repo... - Vector apps = new Vector(); - Vector keeprepos = new Vector(); + List apps = new ArrayList(); + List keeprepos = new ArrayList(); boolean success = true; for (DB.Repo repo : repos) { if (repo.inuse) { + + sendStatus(STATUS_INFO, getString(R.string.status_connecting_to_repo, repo.address)); + StringBuilder newetag = new StringBuilder(); String err = RepoXMLHandler.doUpdate(getBaseContext(), - repo, apps, newetag, keeprepos); + repo, apps, newetag, keeprepos, this); if (err == null) { repo.lastetag = newetag.toString(); } else { @@ -142,9 +170,9 @@ public class UpdateService extends IntentService { } if (success) { - Vector acceptedapps = new Vector(); - Vector prevapps = ((FDroidApp) getApplication()) - .getApps(); + sendStatus(STATUS_INFO, getString(R.string.status_checking_compatibility)); + List acceptedapps = new ArrayList(); + List prevapps = ((FDroidApp) getApplication()).getApps(); DB db = DB.getDB(); try { @@ -234,17 +262,12 @@ public class UpdateService extends IntentService { } } - if (receiver != null) { - Bundle resultData = new Bundle(); - if (!success) { - if (errmsg.length() == 0) - errmsg = "Unknown error"; - resultData.putString("errmsg", errmsg); - receiver.send(1, resultData); - } else { - receiver.send(0, resultData); - } - + if (!success) { + if (errmsg.length() == 0) + errmsg = "Unknown error"; + sendStatus(STATUS_ERROR, errmsg); + } else { + sendStatus(STATUS_COMPLETE); } if(success) { @@ -257,22 +280,18 @@ public class UpdateService extends IntentService { Log.e("FDroid", "Exception during update processing:\n" + Log.getStackTraceString(e)); - if (receiver != null) { - Bundle resultData = new Bundle(); - if (errmsg.length() == 0) - errmsg = "Unknown error"; - resultData.putString("errmsg", errmsg); - receiver.send(1, resultData); - } + if (errmsg.length() == 0) + errmsg = "Unknown error"; + sendStatus(STATUS_ERROR, errmsg); } finally { Log.d("FDroid", "Update took " + ((System.currentTimeMillis() - startTime) / 1000) + " seconds."); + receiver = null; } - } - private void getIcon(DB.App app, Vector repos) { + private void getIcon(DB.App app, List repos) { try { File f = new File(DB.getIconsPath(), app.icon); @@ -306,4 +325,25 @@ public class UpdateService extends IntentService { } } + /** + * Received progress event from the RepoXMLHandler. + * It could be progress downloading from the repo, or perhaps processing the info from the repo. + */ + @Override + public void onProgress(ProgressListener.Event event) { + + String message = ""; + if (event.type == RepoXMLHandler.PROGRESS_TYPE_DOWNLOAD) { + String repoAddress = event.data.getString(RepoXMLHandler.PROGRESS_DATA_REPO); + String downloadedSize = Utils.getFriendlySize( event.progress ); + String totalSize = Utils.getFriendlySize( event.total ); + int percent = (int)((double)event.progress/event.total * 100); + message = getString(R.string.status_download, repoAddress, downloadedSize, totalSize, percent); + } else if (event.type == RepoXMLHandler.PROGRESS_TYPE_PROCESS_XML) { + String repoAddress = event.data.getString(RepoXMLHandler.PROGRESS_DATA_REPO); + message = getString(R.string.status_processing_xml, repoAddress, event.progress, event.total); + } + + sendStatus(STATUS_INFO, message); + } } diff --git a/src/org/fdroid/fdroid/Utils.java b/src/org/fdroid/fdroid/Utils.java index 14dad6be0..d1679cbce 100644 --- a/src/org/fdroid/fdroid/Utils.java +++ b/src/org/fdroid/fdroid/Utils.java @@ -18,25 +18,45 @@ package org.fdroid.fdroid; +import android.os.Build; + +import java.io.BufferedReader; import java.io.Closeable; +import java.io.File; +import java.io.FileReader; import java.io.InputStream; import java.io.IOException; import java.io.OutputStream; public final class Utils { - private Utils() { - } public static final int BUFFER_SIZE = 4096; + private static final String[] FRIENDLY_SIZE_FORMAT = { + "%.0f B", "%.0f KiB", "%.1f MiB", "%.2f GiB" }; + + public static void copy(InputStream input, OutputStream output) throws IOException { + copy(input, output, null, null); + } + + public static void copy(InputStream input, OutputStream output, + ProgressListener progressListener, + ProgressListener.Event templateProgressEvent) + throws IOException { byte[] buffer = new byte[BUFFER_SIZE]; + int bytesRead = 0; while (true) { int count = input.read(buffer); if (count == -1) { break; } + if (progressListener != null) { + bytesRead += count; + templateProgressEvent.progress = bytesRead; + progressListener.onProgress(templateProgressEvent); + } output.write(buffer, 0, count); } output.flush(); @@ -52,4 +72,60 @@ public final class Utils { // ignore } } + + public static boolean hasApi(int apiLevel) { + return Build.VERSION.SDK_INT >= apiLevel; + } + + public static int getApi() { + return Build.VERSION.SDK_INT; + } + + public static String getFriendlySize(int size) { + double s = size; + int i = 0; + while (i < FRIENDLY_SIZE_FORMAT.length - 1 && s >= 1024) { + s = (100 * s / 1024) / 100.0; + i++; + } + return String.format(FRIENDLY_SIZE_FORMAT[i], s); + } + + public static int countSubstringOccurrence(File file, String substring) throws IOException { + int count = 0; + BufferedReader reader = null; + try { + + reader = new BufferedReader(new FileReader(file)); + while(true) { + String line = reader.readLine(); + if (line == null) { + break; + } + count += countSubstringOccurrence(line, substring); + } + + } finally { + closeQuietly(reader); + } + return count; + } + + /** + * Thanks to http://stackoverflow.com/a/767910 + */ + public static int countSubstringOccurrence(String toSearch, String substring) { + int count = 0; + int index = 0; + while (true) { + index = toSearch.indexOf(substring, index); + if (index == -1){ + break; + } + count ++; + index += substring.length(); + } + return count; + } + } diff --git a/src/org/fdroid/fdroid/compat/MenuManager.java b/src/org/fdroid/fdroid/compat/MenuManager.java new file mode 100644 index 000000000..9a60b595e --- /dev/null +++ b/src/org/fdroid/fdroid/compat/MenuManager.java @@ -0,0 +1,48 @@ +package org.fdroid.fdroid.compat; + +import android.app.Activity; +import org.fdroid.fdroid.Utils; + +abstract public class MenuManager { + + public static MenuManager create(Activity activity) { + if (Utils.hasApi(11)) { + return new HoneycombMenuManagerImpl(activity); + } else { + return new OldMenuManagerImpl(activity); + } + } + + protected final Activity activity; + + protected MenuManager(Activity activity) { + this.activity = activity; + } + + abstract public void invalidateOptionsMenu(); + +} + +class OldMenuManagerImpl extends MenuManager { + + protected OldMenuManagerImpl(Activity activity) { + super(activity); + } + + @Override + public void invalidateOptionsMenu() { + } + +} + +class HoneycombMenuManagerImpl extends MenuManager { + + protected HoneycombMenuManagerImpl(Activity activity) { + super(activity); + } + + @Override + public void invalidateOptionsMenu() { + activity.invalidateOptionsMenu(); + } +} \ No newline at end of file diff --git a/src/org/fdroid/fdroid/compat/TabManager.java b/src/org/fdroid/fdroid/compat/TabManager.java new file mode 100644 index 000000000..b8863bedb --- /dev/null +++ b/src/org/fdroid/fdroid/compat/TabManager.java @@ -0,0 +1,173 @@ +package org.fdroid.fdroid.compat; + +import android.app.ActionBar; +import android.app.FragmentTransaction; +import android.support.v4.view.ViewPager; +import android.view.View; +import android.widget.*; +import org.fdroid.fdroid.FDroid; +import org.fdroid.fdroid.R; +import org.fdroid.fdroid.Utils; + +public abstract class TabManager { + + public static final int INDEX_AVAILABLE = 0; + public static final int INDEX_INSTALLED = 1; + public static final int INDEX_CAN_UPDATE = 2; + + public static TabManager create(FDroid parent, ViewPager pager) { + if (Utils.hasApi(11)) { + return new HoneycombTabManagerImpl(parent, pager); + } else { + return new OldTabManagerImpl(parent, pager); + } + } + + protected final ViewPager pager; + protected final FDroid parent; + + protected TabManager(FDroid parent, ViewPager pager) { + this.parent = parent; + this.pager = pager; + } + + abstract public void createTabs(); + abstract public void selectTab(int index); + abstract public void refreshTabLabel(int index); + + protected CharSequence getLabel(int index) { + return pager.getAdapter().getPageTitle(index); + } +} + +class OldTabManagerImpl extends TabManager { + + private TabHost tabHost; + + public OldTabManagerImpl(FDroid parent, ViewPager pager) { + super(parent, pager); + } + + /** + * 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. + */ + public void createTabs() { + tabHost = new TabHost(parent); + tabHost.setLayoutParams(new TabHost.LayoutParams( + TabHost.LayoutParams.MATCH_PARENT, TabHost.LayoutParams.WRAP_CONTENT)); + + TabWidget tabWidget = new TabWidget(parent); + tabWidget.setId(android.R.id.tabs); + tabHost.setLayoutParams(new TabHost.LayoutParams( + TabWidget.LayoutParams.MATCH_PARENT, TabWidget.LayoutParams.WRAP_CONTENT)); + + FrameLayout layout = new FrameLayout(parent); + 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(parent); + } + }; + + TabHost.TabSpec availableTabSpec = tabHost.newTabSpec("available") + .setIndicator( + parent.getString(R.string.tab_noninstalled), + parent.getResources().getDrawable(android.R.drawable.ic_input_add)) + .setContent(factory); + + TabHost.TabSpec installedTabSpec = tabHost.newTabSpec("installed") + .setIndicator( + parent.getString(R.string.tab_installed), + parent.getResources().getDrawable(android.R.drawable.star_off)) + .setContent(factory); + + TabHost.TabSpec canUpdateTabSpec = tabHost.newTabSpec("canUpdate") + .setIndicator( + parent.getString(R.string.tab_updates), + parent.getResources().getDrawable(android.R.drawable.star_on)) + .setContent(factory); + + tabHost.addTab(availableTabSpec); + tabHost.addTab(installedTabSpec); + tabHost.addTab(canUpdateTabSpec); + + LinearLayout contentView = (LinearLayout)parent.findViewById(R.id.fdroid_layout); + contentView.addView(tabHost, 0); + + tabHost.setOnTabChangedListener( new TabHost.OnTabChangeListener() { + @Override + public void onTabChanged(String tabId) { + pager.setCurrentItem(tabHost.getCurrentTab()); + } + }); + } + + + public void selectTab(int index) { + tabHost.setCurrentTab(index); + } + + public void refreshTabLabel(int index) { + CharSequence text = getLabel(index); + + // 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(index) + .findViewById(android.R.id.title); + textView.setText(text); + } + +} + +class HoneycombTabManagerImpl extends TabManager { + + protected final ActionBar actionBar; + + public HoneycombTabManagerImpl(FDroid parent, ViewPager pager) { + super(parent, pager); + actionBar = parent.getActionBar(); + } + + public void createTabs() { + actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS); + for (int i = 0; i < pager.getAdapter().getCount(); i ++) { + CharSequence label = pager.getAdapter().getPageTitle(i); + actionBar.addTab( + actionBar.newTab() + .setText(label) + .setTabListener(new ActionBar.TabListener() { + public void onTabSelected(ActionBar.Tab tab, + FragmentTransaction ft) { + pager.setCurrentItem(tab.getPosition()); + } + + @Override + public void onTabUnselected(ActionBar.Tab tab, FragmentTransaction ft) { + } + + @Override + public void onTabReselected(ActionBar.Tab tab, FragmentTransaction ft) { + } + })); + } + } + + public void selectTab(int index) { + actionBar.setSelectedNavigationItem(index); + } + + public void refreshTabLabel(int index) { + CharSequence text = getLabel(index); + actionBar.getTabAt(index).setText(text); + } +} diff --git a/src/org/fdroid/fdroid/views/AppListFragmentPageAdapter.java b/src/org/fdroid/fdroid/views/AppListFragmentPageAdapter.java index e817ebd29..069fc37fc 100644 --- a/src/org/fdroid/fdroid/views/AppListFragmentPageAdapter.java +++ b/src/org/fdroid/fdroid/views/AppListFragmentPageAdapter.java @@ -1,14 +1,7 @@ package org.fdroid.fdroid.views; -import android.os.Bundle; -import android.os.Parcelable; import android.support.v4.app.Fragment; -import android.support.v4.app.FragmentManager; import android.support.v4.app.FragmentPagerAdapter; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import org.fdroid.fdroid.AppListManager; import org.fdroid.fdroid.FDroid; import org.fdroid.fdroid.R; import org.fdroid.fdroid.views.fragments.AvailableAppsFragment; diff --git a/src/org/fdroid/fdroid/views/AppListView.java b/src/org/fdroid/fdroid/views/AppListView.java index 4fbe716ed..b65f50cc3 100644 --- a/src/org/fdroid/fdroid/views/AppListView.java +++ b/src/org/fdroid/fdroid/views/AppListView.java @@ -27,10 +27,6 @@ public class AppListView extends LinearLayout { super(context, attrs); } - public AppListView(Context context, AttributeSet attrs, int defStyle) { - super(context, attrs, defStyle); - } - public void setAppList(ListView appList) { this.appList = appList; } diff --git a/src/org/fdroid/fdroid/views/fragments/AppListFragment.java b/src/org/fdroid/fdroid/views/fragments/AppListFragment.java index 6ad8f9588..e5728221e 100644 --- a/src/org/fdroid/fdroid/views/fragments/AppListFragment.java +++ b/src/org/fdroid/fdroid/views/fragments/AppListFragment.java @@ -3,7 +3,6 @@ package org.fdroid.fdroid.views.fragments; import android.app.Activity; import android.content.Intent; import android.support.v4.app.Fragment; -import android.util.Log; import android.view.View; import android.view.ViewGroup; import android.widget.AdapterView; @@ -13,7 +12,6 @@ import org.fdroid.fdroid.views.AppListView; abstract class AppListFragment extends Fragment implements AdapterView.OnItemClickListener { - private AppListManager appListManager; private FDroid parent; protected abstract AppListAdapter getAppListAdapter(); diff --git a/src/org/fdroid/fdroid/views/fragments/AvailableAppsFragment.java b/src/org/fdroid/fdroid/views/fragments/AvailableAppsFragment.java index 517476e79..c5955efc4 100644 --- a/src/org/fdroid/fdroid/views/fragments/AvailableAppsFragment.java +++ b/src/org/fdroid/fdroid/views/fragments/AvailableAppsFragment.java @@ -1,13 +1,12 @@ package org.fdroid.fdroid.views.fragments; import android.os.Bundle; -import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.*; + import org.fdroid.fdroid.AppListAdapter; -import org.fdroid.fdroid.AppListManager; import org.fdroid.fdroid.R; import org.fdroid.fdroid.views.AppListView; @@ -51,8 +50,8 @@ public class AvailableAppsFragment extends AppListFragment implements AdapterVie public void onItemSelected(AdapterView parent, View view, int pos, long id) { - getAppListManager().setCurrentCategory(parent.getItemAtPosition(pos).toString()); - getAppListManager().repopulateLists(); + String category = parent.getItemAtPosition(pos).toString(); + getAppListManager().setCurrentCategory(category); } @Override diff --git a/src/org/fdroid/fdroid/views/fragments/InstalledAppsFragment.java b/src/org/fdroid/fdroid/views/fragments/InstalledAppsFragment.java index e6b48f35f..628ddf9b4 100644 --- a/src/org/fdroid/fdroid/views/fragments/InstalledAppsFragment.java +++ b/src/org/fdroid/fdroid/views/fragments/InstalledAppsFragment.java @@ -4,7 +4,6 @@ import android.os.Bundle; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; -import android.widget.AdapterView; import org.fdroid.fdroid.AppListAdapter; public class InstalledAppsFragment extends AppListFragment {