Remove *.orig files
This commit is contained in:
parent
943253084b
commit
5296bf1477
File diff suppressed because it is too large
Load Diff
@ -1,347 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2010-12 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
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 3
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
package org.fdroid.fdroid;
|
||||
|
||||
import android.content.*;
|
||||
import android.content.res.Configuration;
|
||||
import android.support.v4.view.MenuItemCompat;
|
||||
|
||||
import android.app.AlertDialog;
|
||||
import android.app.AlertDialog.Builder;
|
||||
import android.app.NotificationManager;
|
||||
import android.content.pm.PackageInfo;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.support.v4.app.FragmentActivity;
|
||||
import android.support.v4.view.ViewPager;
|
||||
import android.util.Log;
|
||||
import android.view.ContextThemeWrapper;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.widget.*;
|
||||
import org.fdroid.fdroid.compat.TabManager;
|
||||
import org.fdroid.fdroid.views.AppListFragmentPageAdapter;
|
||||
|
||||
public class FDroid extends FragmentActivity {
|
||||
|
||||
public static final int REQUEST_APPDETAILS = 0;
|
||||
public static final int REQUEST_MANAGEREPOS = 1;
|
||||
public static final int REQUEST_PREFS = 2;
|
||||
|
||||
public static final String EXTRA_TAB_UPDATE = "extraTab";
|
||||
|
||||
private static final int UPDATE_REPO = Menu.FIRST;
|
||||
private static final int MANAGE_REPO = Menu.FIRST + 1;
|
||||
private static final int PREFERENCES = Menu.FIRST + 2;
|
||||
private static final int ABOUT = Menu.FIRST + 3;
|
||||
private static final int SEARCH = Menu.FIRST + 4;
|
||||
|
||||
private ViewPager viewPager;
|
||||
|
||||
private AppListManager manager = null;
|
||||
|
||||
private TabManager tabManager = null;
|
||||
|
||||
public AppListManager getManager() {
|
||||
return manager;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
|
||||
((FDroidApp) getApplication()).applyTheme(this);
|
||||
|
||||
super.onCreate(savedInstanceState);
|
||||
manager = new AppListManager(this);
|
||||
setContentView(R.layout.fdroid);
|
||||
createViews();
|
||||
getTabManager().createTabs();
|
||||
|
||||
// Start a search by just typing
|
||||
setDefaultKeyMode(DEFAULT_KEYS_SEARCH_LOCAL);
|
||||
|
||||
Intent i = getIntent();
|
||||
Uri data = i.getData();
|
||||
String appid = null;
|
||||
if (data != null) {
|
||||
if (data.isHierarchical()) {
|
||||
// http(s)://f-droid.org/repository/browse?fdid=app.id
|
||||
appid = data.getQueryParameter("fdid");
|
||||
}
|
||||
} else if (i.hasExtra(EXTRA_TAB_UPDATE)) {
|
||||
boolean showUpdateTab = i.getBooleanExtra(EXTRA_TAB_UPDATE, false);
|
||||
if (showUpdateTab) {
|
||||
getTabManager().selectTab(2);
|
||||
}
|
||||
}
|
||||
if (appid != null && appid.length() > 0) {
|
||||
Intent call = new Intent(this, AppDetails.class);
|
||||
call.putExtra("appid", appid);
|
||||
startActivityForResult(call, REQUEST_APPDETAILS);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onResume() {
|
||||
super.onResume();
|
||||
repopulateViews();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
protected void repopulateViews() {
|
||||
manager.repopulateLists();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onConfigurationChanged(Configuration newConfig) {
|
||||
super.onConfigurationChanged(newConfig);
|
||||
getTabManager().onConfigurationChanged(newConfig);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCreateOptionsMenu(Menu menu) {
|
||||
|
||||
super.onCreateOptionsMenu(menu);
|
||||
<<<<<<< HEAD
|
||||
=======
|
||||
menu.add(Menu.NONE, UPDATE_REPO, 1, R.string.menu_update_repo).setIcon(
|
||||
android.R.drawable.ic_menu_rotate);
|
||||
>>>>>>> master
|
||||
menu.add(Menu.NONE, MANAGE_REPO, 2, R.string.menu_manage).setIcon(
|
||||
android.R.drawable.ic_menu_agenda);
|
||||
MenuItem search = menu.add(Menu.NONE, SEARCH, 3, R.string.menu_search).setIcon(
|
||||
android.R.drawable.ic_menu_search);
|
||||
menu.add(Menu.NONE, PREFERENCES, 4, R.string.menu_preferences).setIcon(
|
||||
android.R.drawable.ic_menu_preferences);
|
||||
menu.add(Menu.NONE, ABOUT, 5, R.string.menu_about).setIcon(
|
||||
android.R.drawable.ic_menu_help);
|
||||
MenuItemCompat.setShowAsAction(search, MenuItemCompat.SHOW_AS_ACTION_ALWAYS);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
|
||||
switch (item.getItemId()) {
|
||||
|
||||
case UPDATE_REPO:
|
||||
updateRepos();
|
||||
return true;
|
||||
|
||||
case MANAGE_REPO:
|
||||
Intent i = new Intent(this, ManageRepo.class);
|
||||
startActivityForResult(i, REQUEST_MANAGEREPOS);
|
||||
return true;
|
||||
|
||||
case PREFERENCES:
|
||||
Intent prefs = new Intent(getBaseContext(), PreferencesActivity.class);
|
||||
startActivityForResult(prefs, REQUEST_PREFS);
|
||||
return true;
|
||||
|
||||
case SEARCH:
|
||||
onSearchRequested();
|
||||
return true;
|
||||
|
||||
case ABOUT:
|
||||
View view = null;
|
||||
if (Build.VERSION.SDK_INT >= 11) {
|
||||
LayoutInflater li = LayoutInflater.from(this);
|
||||
view = li.inflate(R.layout.about, null);
|
||||
} else {
|
||||
view = View.inflate(
|
||||
new ContextThemeWrapper(this, R.style.AboutDialogLight),
|
||||
R.layout.about, null);
|
||||
}
|
||||
|
||||
// Fill in the version...
|
||||
try {
|
||||
PackageInfo pi = getPackageManager()
|
||||
.getPackageInfo(getApplicationContext()
|
||||
.getPackageName(), 0);
|
||||
((TextView) view.findViewById(R.id.version))
|
||||
.setText(pi.versionName);
|
||||
} catch (Exception e) {
|
||||
}
|
||||
|
||||
Builder p = null;
|
||||
if (Build.VERSION.SDK_INT >= 11) {
|
||||
p = new AlertDialog.Builder(this).setView(view);
|
||||
} else {
|
||||
p = new AlertDialog.Builder(
|
||||
new ContextThemeWrapper(
|
||||
this, R.style.AboutDialogLight)
|
||||
).setView(view);
|
||||
}
|
||||
final AlertDialog alrt = p.create();
|
||||
alrt.setIcon(R.drawable.ic_launcher);
|
||||
alrt.setTitle(getString(R.string.about_title));
|
||||
alrt.setButton(AlertDialog.BUTTON_NEUTRAL,
|
||||
getString(R.string.about_website),
|
||||
new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog,
|
||||
int whichButton) {
|
||||
Uri uri = Uri.parse("https://f-droid.org");
|
||||
startActivity(new Intent(Intent.ACTION_VIEW, uri));
|
||||
}
|
||||
});
|
||||
alrt.setButton(AlertDialog.BUTTON_NEGATIVE, getString(R.string.ok),
|
||||
new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog,
|
||||
int whichButton) {
|
||||
}
|
||||
});
|
||||
alrt.show();
|
||||
return true;
|
||||
}
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||
|
||||
switch (requestCode) {
|
||||
case REQUEST_APPDETAILS:
|
||||
break;
|
||||
case REQUEST_MANAGEREPOS:
|
||||
if (data.hasExtra(ManageRepo.REQUEST_UPDATE)) {
|
||||
AlertDialog.Builder ask_alrt = new AlertDialog.Builder(this);
|
||||
ask_alrt.setTitle(getString(R.string.repo_update_title));
|
||||
ask_alrt.setIcon(android.R.drawable.ic_menu_rotate);
|
||||
ask_alrt.setMessage(getString(R.string.repo_alrt));
|
||||
ask_alrt.setPositiveButton(getString(R.string.yes),
|
||||
new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog,
|
||||
int whichButton) {
|
||||
updateRepos();
|
||||
}
|
||||
});
|
||||
ask_alrt.setNegativeButton(getString(R.string.no),
|
||||
new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog,
|
||||
int whichButton) {
|
||||
// do nothing
|
||||
}
|
||||
});
|
||||
AlertDialog alert = ask_alrt.create();
|
||||
alert.show();
|
||||
}
|
||||
break;
|
||||
case REQUEST_PREFS:
|
||||
// The automatic update settings may have changed, so reschedule (or
|
||||
// unschedule) the service accordingly. It's cheap, so no need to
|
||||
// check if the particular setting has actually been changed.
|
||||
UpdateService.schedule(getBaseContext());
|
||||
|
||||
if ((resultCode & PreferencesActivity.RESULT_RELOAD) != 0) {
|
||||
((FDroidApp) getApplication()).invalidateAllApps();
|
||||
} else if ((resultCode & PreferencesActivity.RESULT_REFILTER) != 0) {
|
||||
((FDroidApp) getApplication()).filterApps();
|
||||
}
|
||||
|
||||
if ((resultCode & PreferencesActivity.RESULT_RESTART) != 0) {
|
||||
((FDroidApp) getApplication()).reloadTheme();
|
||||
final Intent intent = getIntent();
|
||||
overridePendingTransition(0, 0);
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION);
|
||||
finish();
|
||||
overridePendingTransition(0, 0);
|
||||
startActivity(intent);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void createViews() {
|
||||
viewPager = (ViewPager)findViewById(R.id.main_pager);
|
||||
AppListFragmentPageAdapter viewPageAdapter = new AppListFragmentPageAdapter(this);
|
||||
viewPager.setAdapter(viewPageAdapter);
|
||||
viewPager.setOnPageChangeListener( new ViewPager.SimpleOnPageChangeListener() {
|
||||
@Override
|
||||
public void onPageSelected(int position) {
|
||||
getTabManager().selectTab(position);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.");
|
||||
getPreferences(MODE_PRIVATE).edit().putBoolean(TRIED_EMPTY_UPDATE, true).commit();
|
||||
updateRepos();
|
||||
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
|
||||
// is told to do the update, which will result in the database changing. The
|
||||
// UpdateReceiver class should get told when this is finished.
|
||||
public void updateRepos() {
|
||||
UpdateService.updateNow(this).setListener(new ProgressListener() {
|
||||
@Override
|
||||
public void onProgress(Event event) {
|
||||
if (event.type == UpdateService.STATUS_COMPLETE_WITH_CHANGES){
|
||||
repopulateViews();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private TabManager getTabManager() {
|
||||
if (tabManager == null) {
|
||||
tabManager = TabManager.create(this, viewPager);
|
||||
}
|
||||
return tabManager;
|
||||
}
|
||||
|
||||
public void refreshUpdateTabLabel() {
|
||||
getTabManager().refreshTabLabel(TabManager.INDEX_CAN_UPDATE);
|
||||
}
|
||||
|
||||
public void removeNotification(int id) {
|
||||
NotificationManager nMgr = (NotificationManager) getBaseContext()
|
||||
.getSystemService(Context.NOTIFICATION_SERVICE);
|
||||
nMgr.cancel(id);
|
||||
}
|
||||
|
||||
}
|
@ -1,541 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2010-12 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
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 3
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
package org.fdroid.fdroid;
|
||||
|
||||
import android.os.Bundle;
|
||||
import org.fdroid.fdroid.updater.RepoUpdater;
|
||||
import org.xml.sax.Attributes;
|
||||
import org.xml.sax.SAXException;
|
||||
import org.xml.sax.helpers.DefaultHandler;
|
||||
|
||||
import java.text.ParseException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class RepoXMLHandler extends DefaultHandler {
|
||||
|
||||
// The repo we're processing.
|
||||
private DB.Repo repo;
|
||||
|
||||
private Map<String, DB.App> apps;
|
||||
private List<DB.App> appsList;
|
||||
|
||||
private DB.App curapp = null;
|
||||
private DB.Apk curapk = null;
|
||||
private StringBuilder curchars = new StringBuilder();
|
||||
|
||||
// After processing the XML, these will be -1 if the index didn't specify
|
||||
// them - otherwise it will be the value specified.
|
||||
private int version = -1;
|
||||
private int maxage = -1;
|
||||
|
||||
// After processing the XML, this will be null if the index specified a
|
||||
// public key - otherwise a public key. This is used for TOFU where an
|
||||
// index.xml is read on the first connection, and a signed index.jar is
|
||||
// expected on all subsequent connections.
|
||||
private String pubkey;
|
||||
|
||||
private String name;
|
||||
private String description;
|
||||
private String hashType;
|
||||
|
||||
private int progressCounter = 0;
|
||||
private ProgressListener progressListener;
|
||||
|
||||
|
||||
// The date format used in the repo XML file.
|
||||
private SimpleDateFormat mXMLDateFormat = new SimpleDateFormat("yyyy-MM-dd");
|
||||
|
||||
private int totalAppCount;
|
||||
|
||||
public RepoXMLHandler(DB.Repo repo, List<DB.App> appsList, ProgressListener listener) {
|
||||
this.repo = repo;
|
||||
this.apps = new HashMap<String, DB.App>();
|
||||
for (DB.App app : appsList) this.apps.put(app.id, app);
|
||||
this.appsList = appsList;
|
||||
pubkey = null;
|
||||
name = null;
|
||||
description = null;
|
||||
progressListener = listener;
|
||||
}
|
||||
|
||||
public int getMaxAge() { return maxage; }
|
||||
|
||||
public int getVersion() { return version; }
|
||||
|
||||
public String getDescription() { return description; }
|
||||
|
||||
public String getName() { return name; }
|
||||
|
||||
public String getPubKey() {
|
||||
return pubkey;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void characters(char[] ch, int start, int length) {
|
||||
curchars.append(ch, start, length);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void endElement(String uri, String localName, String qName)
|
||||
throws SAXException {
|
||||
|
||||
super.endElement(uri, localName, qName);
|
||||
String curel = localName;
|
||||
String str = curchars.toString();
|
||||
if (str != null) {
|
||||
str = str.trim();
|
||||
}
|
||||
|
||||
if (curel.equals("application") && curapp != null) {
|
||||
|
||||
// If we already have this application (must be from scanning a
|
||||
// different repo) then just merge in the apks.
|
||||
DB.App app = apps.get(curapp.id);
|
||||
if (app != null) {
|
||||
app.apks.addAll(curapp.apks);
|
||||
} else {
|
||||
appsList.add(curapp);
|
||||
apps.put(curapp.id, curapp);
|
||||
}
|
||||
|
||||
curapp = null;
|
||||
|
||||
} else if (curel.equals("package") && curapk != null && curapp != null) {
|
||||
curapp.apks.add(curapk);
|
||||
curapk = null;
|
||||
} else if (curapk != null && str != null) {
|
||||
if (curel.equals("version")) {
|
||||
curapk.version = str;
|
||||
} else if (curel.equals("versioncode")) {
|
||||
try {
|
||||
curapk.vercode = Integer.parseInt(str);
|
||||
} catch (NumberFormatException ex) {
|
||||
curapk.vercode = -1;
|
||||
}
|
||||
} else if (curel.equals("size")) {
|
||||
try {
|
||||
curapk.detail_size = Integer.parseInt(str);
|
||||
} catch (NumberFormatException ex) {
|
||||
curapk.detail_size = 0;
|
||||
}
|
||||
} else if (curel.equals("hash")) {
|
||||
if (hashType == null || hashType.equals("md5")) {
|
||||
if (curapk.detail_hash == null) {
|
||||
curapk.detail_hash = str;
|
||||
curapk.detail_hashType = "MD5";
|
||||
}
|
||||
} else if (hashType.equals("sha256")) {
|
||||
curapk.detail_hash = str;
|
||||
curapk.detail_hashType = "SHA-256";
|
||||
}
|
||||
} else if (curel.equals("sig")) {
|
||||
curapk.sig = str;
|
||||
} else if (curel.equals("srcname")) {
|
||||
curapk.srcname = str;
|
||||
} else if (curel.equals("apkname")) {
|
||||
curapk.apkName = str;
|
||||
} else if (curel.equals("sdkver")) {
|
||||
try {
|
||||
curapk.minSdkVersion = Integer.parseInt(str);
|
||||
} catch (NumberFormatException ex) {
|
||||
curapk.minSdkVersion = 0;
|
||||
}
|
||||
} else if (curel.equals("added")) {
|
||||
try {
|
||||
curapk.added = str.length() == 0 ? null : mXMLDateFormat
|
||||
.parse(str);
|
||||
} catch (ParseException e) {
|
||||
curapk.added = null;
|
||||
}
|
||||
} else if (curel.equals("permissions")) {
|
||||
curapk.detail_permissions = DB.CommaSeparatedList.make(str);
|
||||
} else if (curel.equals("features")) {
|
||||
curapk.features = DB.CommaSeparatedList.make(str);
|
||||
} else if (curel.equals("nativecode")) {
|
||||
curapk.nativecode = DB.CommaSeparatedList.make(str);
|
||||
}
|
||||
} else if (curapp != null && str != null) {
|
||||
if (curel.equals("name")) {
|
||||
curapp.name = str;
|
||||
} else if (curel.equals("icon")) {
|
||||
curapp.icon = str;
|
||||
} else if (curel.equals("description")) {
|
||||
// This is the old-style description. We'll read it
|
||||
// if present, to support old repos, but in newer
|
||||
// repos it will get overwritten straight away!
|
||||
curapp.detail_description = "<p>" + str + "</p>";
|
||||
} else if (curel.equals("desc")) {
|
||||
// New-style description.
|
||||
curapp.detail_description = str;
|
||||
} else if (curel.equals("summary")) {
|
||||
curapp.summary = str;
|
||||
} else if (curel.equals("license")) {
|
||||
curapp.license = str;
|
||||
} else if (curel.equals("source")) {
|
||||
curapp.detail_sourceURL = str;
|
||||
} else if (curel.equals("donate")) {
|
||||
curapp.detail_donateURL = str;
|
||||
} else if (curel.equals("bitcoin")) {
|
||||
curapp.detail_bitcoinAddr = str;
|
||||
} else if (curel.equals("litecoin")) {
|
||||
curapp.detail_litecoinAddr = str;
|
||||
} else if (curel.equals("dogecoin")) {
|
||||
curapp.detail_dogecoinAddr = str;
|
||||
} else if (curel.equals("flattr")) {
|
||||
curapp.detail_flattrID = str;
|
||||
} else if (curel.equals("web")) {
|
||||
curapp.detail_webURL = str;
|
||||
} else if (curel.equals("tracker")) {
|
||||
curapp.detail_trackerURL = str;
|
||||
} else if (curel.equals("added")) {
|
||||
try {
|
||||
curapp.added = str.length() == 0 ? null : mXMLDateFormat
|
||||
.parse(str);
|
||||
} catch (ParseException e) {
|
||||
curapp.added = null;
|
||||
}
|
||||
} else if (curel.equals("lastupdated")) {
|
||||
try {
|
||||
curapp.lastUpdated = str.length() == 0 ? null
|
||||
: mXMLDateFormat.parse(str);
|
||||
} catch (ParseException e) {
|
||||
curapp.lastUpdated = null;
|
||||
}
|
||||
} else if (curel.equals("marketversion")) {
|
||||
curapp.curVersion = str;
|
||||
} else if (curel.equals("marketvercode")) {
|
||||
try {
|
||||
curapp.curVercode = Integer.parseInt(str);
|
||||
} catch (NumberFormatException ex) {
|
||||
curapp.curVercode = -1;
|
||||
}
|
||||
} else if (curel.equals("categories")) {
|
||||
curapp.categories = DB.CommaSeparatedList.make(str);
|
||||
} else if (curel.equals("antifeatures")) {
|
||||
curapp.antiFeatures = DB.CommaSeparatedList.make(str);
|
||||
} else if (curel.equals("requirements")) {
|
||||
curapp.requirements = DB.CommaSeparatedList.make(str);
|
||||
}
|
||||
} else if (curel.equals("description")) {
|
||||
description = str;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startElement(String uri, String localName, String qName,
|
||||
Attributes attributes) throws SAXException {
|
||||
super.startElement(uri, localName, qName, attributes);
|
||||
|
||||
if (localName.equals("repo")) {
|
||||
String pk = attributes.getValue("", "pubkey");
|
||||
if (pk != null)
|
||||
pubkey = pk;
|
||||
|
||||
String maxAgeAttr = attributes.getValue("", "maxage");
|
||||
if (maxAgeAttr != null) {
|
||||
try {
|
||||
maxage = Integer.parseInt(maxAgeAttr);
|
||||
} catch (NumberFormatException nfe) {}
|
||||
}
|
||||
|
||||
String versionAttr = attributes.getValue("", "version");
|
||||
if (versionAttr != null) {
|
||||
try {
|
||||
version = Integer.parseInt(versionAttr);
|
||||
} catch (NumberFormatException nfe) {}
|
||||
}
|
||||
|
||||
String nm = attributes.getValue("", "name");
|
||||
if (nm != null)
|
||||
name = nm;
|
||||
String dc = attributes.getValue("", "description");
|
||||
if (dc != null)
|
||||
description = dc;
|
||||
|
||||
} else if (localName.equals("application") && curapp == null) {
|
||||
curapp = new DB.App();
|
||||
curapp.detail_Populated = true;
|
||||
curapp.id = attributes.getValue("", "id");
|
||||
Bundle progressData = RepoUpdater.createProgressData(repo.address);
|
||||
progressCounter ++;
|
||||
progressListener.onProgress(
|
||||
new ProgressListener.Event(
|
||||
RepoUpdater.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.id;
|
||||
hashType = null;
|
||||
|
||||
} else if (localName.equals("hash") && curapk != null) {
|
||||
hashType = attributes.getValue("", "type");
|
||||
}
|
||||
curchars.setLength(0);
|
||||
}
|
||||
|
||||
<<<<<<< HEAD
|
||||
=======
|
||||
// Get a remote file. Returns the HTTP response code.
|
||||
// If 'etag' is not null, it's passed to the server as an If-None-Match
|
||||
// header, in which case expect a 304 response if nothing changed.
|
||||
// In the event of a 200 response ONLY, 'retag' (which should be passed
|
||||
// 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,
|
||||
ProgressListener progressListener,
|
||||
ProgressListener.Event progressEvent) throws MalformedURLException,
|
||||
IOException {
|
||||
|
||||
long startTime = System.currentTimeMillis();
|
||||
URL u = new URL(url);
|
||||
HttpURLConnection connection = (HttpURLConnection) u.openConnection();
|
||||
if (etag != null)
|
||||
connection.setRequestProperty("If-None-Match", etag);
|
||||
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 = connection.getInputStream();
|
||||
output = ctx.openFileOutput(dest, Context.MODE_PRIVATE);
|
||||
Utils.copy(input, output, progressListener, progressEvent);
|
||||
} finally {
|
||||
Utils.closeQuietly(output);
|
||||
Utils.closeQuietly(input);
|
||||
}
|
||||
|
||||
String et = connection.getHeaderField("ETag");
|
||||
if (et != null)
|
||||
retag.append(et);
|
||||
}
|
||||
Log.d("FDroid", "Fetched " + url + " (" + progressEvent.total +
|
||||
" bytes) in " + (System.currentTimeMillis() - startTime) +
|
||||
"ms");
|
||||
return code;
|
||||
|
||||
}
|
||||
|
||||
// Do an update from the given repo. All applications found, and their
|
||||
// APKs, are added to 'apps'. (If 'apps' already contains an app, its
|
||||
// APKs are merged into the existing one).
|
||||
// Returns null if successful, otherwise an error message to be displayed
|
||||
// to the user (if there is an interactive user!)
|
||||
// 'newetag' should be passed empty. On success, it may contain an etag
|
||||
// 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,
|
||||
List<DB.App> appsList, StringBuilder newetag, List<Integer> keeprepos,
|
||||
ProgressListener progressListener) {
|
||||
try {
|
||||
|
||||
int code = 0;
|
||||
if (repo.pubkey != null) {
|
||||
|
||||
// This is a signed repo - we download the jar file,
|
||||
// check the signature, and extract the index...
|
||||
Log.d("FDroid", "Getting signed index from " + repo.address + " at " +
|
||||
logDateFormat.format(new Date(System.currentTimeMillis())));
|
||||
String address = repo.address + "/index.jar?client_version="
|
||||
+ ctx.getString(R.string.version_name);
|
||||
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, progressListener, event );
|
||||
if (code == 200) {
|
||||
String jarpath = ctx.getFilesDir() + "/tempindex.jar";
|
||||
JarFile jar = null;
|
||||
JarEntry je;
|
||||
Certificate[] certs;
|
||||
try {
|
||||
jar = new JarFile(jarpath, true);
|
||||
je = (JarEntry) jar.getEntry("index.xml");
|
||||
File efile = new File(ctx.getFilesDir(),
|
||||
"/tempindex.xml");
|
||||
InputStream input = null;
|
||||
OutputStream output = null;
|
||||
try {
|
||||
input = jar.getInputStream(je);
|
||||
output = new FileOutputStream(efile);
|
||||
Utils.copy(input, output);
|
||||
} finally {
|
||||
Utils.closeQuietly(output);
|
||||
Utils.closeQuietly(input);
|
||||
}
|
||||
certs = je.getCertificates();
|
||||
} catch (SecurityException e) {
|
||||
Log.e("FDroid", "Invalid hash for index file");
|
||||
return "Invalid hash for index file";
|
||||
} finally {
|
||||
if (jar != null) {
|
||||
jar.close();
|
||||
}
|
||||
}
|
||||
if (certs == null) {
|
||||
Log.d("FDroid", "No signature found in index");
|
||||
return "No signature found in index";
|
||||
}
|
||||
Log.d("FDroid", "Index has " + certs.length + " signature"
|
||||
+ (certs.length > 1 ? "s." : "."));
|
||||
|
||||
boolean match = false;
|
||||
for (Certificate cert : certs) {
|
||||
String certdata = Hasher.hex(cert.getEncoded());
|
||||
if (repo.pubkey.equals(certdata)) {
|
||||
match = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!match) {
|
||||
Log.d("FDroid", "Index signature mismatch");
|
||||
return "Index signature mismatch";
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
// 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,
|
||||
progressListener, event);
|
||||
}
|
||||
|
||||
if (code == 200) {
|
||||
// Process the index...
|
||||
SAXParserFactory spf = SAXParserFactory.newInstance();
|
||||
SAXParser sp = spf.newSAXParser();
|
||||
XMLReader xr = sp.getXMLReader();
|
||||
RepoXMLHandler handler = new RepoXMLHandler(repo, appsList, progressListener);
|
||||
xr.setContentHandler(handler);
|
||||
|
||||
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 = "<application";
|
||||
handler.setTotalAppCount(Utils.countSubstringOccurrence(tempIndex, APPLICATION));
|
||||
|
||||
InputSource is = new InputSource(r);
|
||||
xr.parse(is);
|
||||
|
||||
if (handler.pubkey != null && repo.pubkey == null) {
|
||||
// We read an unsigned index, but that indicates that
|
||||
// a signed version is now available...
|
||||
Log.d("FDroid",
|
||||
"Public key found - switching to signed repo for future updates");
|
||||
repo.pubkey = handler.pubkey;
|
||||
try {
|
||||
DB db = DB.getDB();
|
||||
db.updateRepoByAddress(repo);
|
||||
} finally {
|
||||
DB.releaseDB();
|
||||
}
|
||||
}
|
||||
boolean updateRepo = false;
|
||||
|
||||
if (handler.version != null) {
|
||||
int version = Integer.parseInt(handler.version);
|
||||
if (version != repo.version) {
|
||||
Log.d("FDroid", "Repo specified a new version: from "
|
||||
+ repo.version + " to " + version);
|
||||
repo.version = version;
|
||||
updateRepo = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (handler.maxage != null) {
|
||||
int maxage = Integer.parseInt(handler.maxage);
|
||||
if (maxage != repo.maxage) {
|
||||
Log.d("FDroid",
|
||||
"Repo specified a new maximum age - updated");
|
||||
repo.maxage = maxage;
|
||||
updateRepo = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (updateRepo) {
|
||||
try {
|
||||
DB db = DB.getDB();
|
||||
db.updateRepoByAddress(repo);
|
||||
} finally {
|
||||
DB.releaseDB();
|
||||
}
|
||||
}
|
||||
|
||||
} else if (code == 304) {
|
||||
// The index is unchanged since we last read it. We just mark
|
||||
// everything that came from this repo as being updated.
|
||||
Log.d("FDroid", "Repo index for " + repo.address
|
||||
+ " is up to date (by etag)");
|
||||
keeprepos.add(repo.id);
|
||||
// Make sure we give back the same etag. (The 200 route will
|
||||
// have supplied a new one.
|
||||
newetag.append(repo.lastetag);
|
||||
|
||||
} else {
|
||||
return "Failed to read index - HTTP response "
|
||||
+ Integer.toString(code);
|
||||
}
|
||||
|
||||
} catch (SSLHandshakeException sslex) {
|
||||
Log.e("FDroid", "SSLHandShakeException updating from "
|
||||
+ repo.address + ":\n" + Log.getStackTraceString(sslex));
|
||||
return "A problem occurred while establishing an SSL connection. If this problem persists, AND you have a very old device, you could try using http instead of https for the repo URL.";
|
||||
} catch (Exception e) {
|
||||
Log.e("FDroid", "Exception updating from " + repo.address + ":\n"
|
||||
+ Log.getStackTraceString(e));
|
||||
return "Failed to update - " + e.getMessage();
|
||||
} finally {
|
||||
ctx.deleteFile("tempindex.xml");
|
||||
ctx.deleteFile("tempindex.jar");
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
>>>>>>> master
|
||||
public void setTotalAppCount(int totalAppCount) {
|
||||
this.totalAppCount = totalAppCount;
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user