Browse Source

Merge pull request #8 from relan/abi-sdk-filter

Implement ABI and API level filters
pull/10/head
Daniel Martí 10 years ago
parent
commit
f274bbba69
  1. 2
      README.md
  2. 12
      cmd/fdroidcl/download.go
  3. 4
      cmd/fdroidcl/install.go
  4. 54
      cmd/fdroidcl/search.go
  5. 8
      cmd/fdroidcl/show.go
  6. 7
      cmd/fdroidcl/upgrade.go
  7. 82
      index.go

2
README.md

@ -52,7 +52,7 @@ settings.
* Index verification via jar signature - currently relies on HTTPS
* Interaction with multiple devices at once
* Device compatibility filters (minSdk, maxSdk, arch, hardware features)
* Hardware features filtering
### Advantages over the Android client

12
cmd/fdroidcl/download.go

@ -26,12 +26,14 @@ func runDownload(args []string) {
}
apps := findApps(args)
for _, app := range apps {
apk := app.CurApk()
if apk == nil {
log.Fatalf("No current apk found for %s", app.ID)
apks := app.SuggestedApks()
if len(apks) == 0 {
log.Fatalf("No suggested APKs found for %s", app.ID)
}
for _, apk := range apks {
path := downloadApk(&apk)
fmt.Printf("APK available in %s\n", path)
}
path := downloadApk(apk)
fmt.Printf("APK available in %s\n", path)
}
}

4
cmd/fdroidcl/install.go

@ -43,9 +43,9 @@ func downloadAndDo(apps []*fdroidcl.App, device *adb.Device, doApk func(*adb.Dev
}
toInstall := make([]downloaded, len(apps))
for i, app := range apps {
apk := app.CurApk()
apk := app.SuggestedApk(device)
if apk == nil {
log.Fatalf("No current apk found for %s", app.ID)
log.Fatalf("No suitable APKs found for %s", app.ID)
}
path := downloadApk(apk)
toInstall[i] = downloaded{apk: apk, path: path}

54
cmd/fdroidcl/search.go

@ -48,12 +48,11 @@ func runSearch(args []string) {
device = mustOneDevice()
}
apps := filterAppsSearch(mustLoadIndexes(), args)
instPkgs := mustInstalled(device)
if *installed {
apps = filterAppsInstalled(apps, instPkgs)
apps = filterAppsInstalled(apps, device)
}
if *updates {
apps = filterAppsUpdates(apps, instPkgs)
apps = filterAppsUpdates(apps, device)
}
if *category != "" {
apps = filterAppsCategory(apps, *category)
@ -70,7 +69,7 @@ func runSearch(args []string) {
fmt.Println(app.ID)
}
} else {
printApps(apps, instPkgs)
printApps(apps, device)
}
}
@ -108,44 +107,41 @@ fieldLoop:
return false
}
func printApps(apps []fdroidcl.App, inst map[string]adb.Package) {
func printApps(apps []fdroidcl.App, device *adb.Device) {
maxIDLen := 0
for _, app := range apps {
if len(app.ID) > maxIDLen {
maxIDLen = len(app.ID)
}
}
inst := mustInstalled(device)
for _, app := range apps {
var pkg *adb.Package
p, e := inst[app.ID]
if e {
pkg = &p
}
printApp(app, maxIDLen, pkg)
printApp(app, maxIDLen, pkg, device)
}
}
func descVersion(app fdroidcl.App, inst *adb.Package) string {
cur := app.CurApk()
if cur == nil {
return "(no version available)"
}
if inst == nil {
return fmt.Sprintf("%s (%d)", cur.VName, cur.VCode)
}
if inst.VCode < cur.VCode {
return fmt.Sprintf("%s (%d) -> %s (%d)", inst.VName, inst.VCode,
cur.VName, cur.VCode)
}
if !*installed {
return fmt.Sprintf("%s (%d) [installed]", cur.VName, cur.VCode)
func descVersion(app fdroidcl.App, inst *adb.Package, device *adb.Device) string {
// With "-u" or "-i" option there must be a connected device
if *updates || *installed {
suggested := app.SuggestedApk(device)
if suggested != nil && inst.VCode < suggested.VCode {
return fmt.Sprintf("%s (%d) -> %s (%d)", inst.VName, inst.VCode,
suggested.VName, suggested.VCode)
}
return fmt.Sprintf("%s (%d)", inst.VName, inst.VCode)
}
return fmt.Sprintf("%s (%d)", cur.VName, cur.VCode)
// Without "-u" or "-i" we only have repositories indices
return fmt.Sprintf("%s (%d)", app.CVName, app.CVCode)
}
func printApp(app fdroidcl.App, IDLen int, inst *adb.Package) {
func printApp(app fdroidcl.App, IDLen int, inst *adb.Package, device *adb.Device) {
fmt.Printf("%s%s %s - %s\n", app.ID, strings.Repeat(" ", IDLen-len(app.ID)),
app.Name, descVersion(app, inst))
app.Name, descVersion(app, inst, device))
fmt.Printf(" %s\n", app.Summary)
}
@ -160,8 +156,9 @@ func mustInstalled(device *adb.Device) map[string]adb.Package {
return inst
}
func filterAppsInstalled(apps []fdroidcl.App, inst map[string]adb.Package) []fdroidcl.App {
func filterAppsInstalled(apps []fdroidcl.App, device *adb.Device) []fdroidcl.App {
var result []fdroidcl.App
inst := mustInstalled(device)
for _, app := range apps {
if _, e := inst[app.ID]; !e {
continue
@ -171,18 +168,19 @@ func filterAppsInstalled(apps []fdroidcl.App, inst map[string]adb.Package) []fdr
return result
}
func filterAppsUpdates(apps []fdroidcl.App, inst map[string]adb.Package) []fdroidcl.App {
func filterAppsUpdates(apps []fdroidcl.App, device *adb.Device) []fdroidcl.App {
var result []fdroidcl.App
inst := mustInstalled(device)
for _, app := range apps {
p, e := inst[app.ID]
if !e {
continue
}
cur := app.CurApk()
if cur == nil {
suggested := app.SuggestedApk(device)
if suggested == nil {
continue
}
if p.VCode >= cur.VCode {
if p.VCode >= suggested.VCode {
continue
}
result = append(result, app)

8
cmd/fdroidcl/show.go

@ -69,13 +69,7 @@ func printAppDetailed(app fdroidcl.App) {
p("Summary :", "%s", app.Summary)
p("Added :", "%s", app.Added.String())
p("Last Updated :", "%s", app.Updated.String())
cur := app.CurApk()
if cur != nil {
p("Current Version :", "%s (%d)", cur.VName, cur.VCode)
} else {
p("Current Version :", "(no version available)")
}
p("Upstream Version :", "%s (%d)", app.CVName, app.CVCode)
p("Version :", "%s (%d)", app.CVName, app.CVCode)
p("License :", "%s", app.License)
if app.Categs != nil {
p("Categories :", "%s", strings.Join(app.Categs, ", "))

7
cmd/fdroidcl/upgrade.go

@ -33,8 +33,11 @@ func runUpgrade(args []string) {
if !e {
log.Fatalf("%s is not installed", app.ID)
}
cur := app.CurApk()
if p.VCode >= cur.VCode {
suggested := app.SuggestedApk(device)
if suggested == nil {
log.Fatalf("No suitable APKs found for %s", app.ID)
}
if p.VCode >= suggested.VCode {
log.Fatalf("%s is up to date", app.ID)
}
}

82
index.go

@ -9,6 +9,8 @@ import (
"io"
"sort"
"strings"
"github.com/mvdan/adb"
)
type Index struct {
@ -81,11 +83,11 @@ func getIconsDir(density IconDensity) string {
}
func (a *App) IconURLForDensity(density IconDensity) string {
cur := a.CurApk()
if cur == nil {
if len(a.Apks) == 0 {
return ""
}
return fmt.Sprintf("%s/%s/%s", cur.Repo.URL, getIconsDir(density), a.Icon)
return fmt.Sprintf("%s/%s/%s", a.Apks[0].Repo.URL,
getIconsDir(density), a.Icon)
}
func (a *App) IconURL() string {
@ -209,6 +211,29 @@ func (a *Apk) SrcURL() string {
return fmt.Sprintf("%s/%s", a.Repo.URL, a.SrcName)
}
func (apk *Apk) IsCompatibleABI(ABIs []string) bool {
if len(apk.ABIs) == 0 {
return true // APK does not contain native code
}
for i := range apk.ABIs {
for j := range ABIs {
if apk.ABIs[i] == ABIs[j] {
return true
}
}
}
return false
}
func (apk *Apk) IsCompatibleAPILevel(sdk int) bool {
return sdk >= apk.MinSdk && (apk.MaxSdk == 0 || sdk <= apk.MaxSdk)
}
func (apk *Apk) IsCompatible(device *adb.Device) bool {
return apk.IsCompatibleABI(device.ABIs) &&
apk.IsCompatibleAPILevel(device.APILevel)
}
type AppList []App
func (al AppList) Len() int { return len(al) }
@ -242,15 +267,56 @@ func LoadIndexXML(r io.Reader) (*Index, error) {
return &index, nil
}
func (a *App) CurApk() *Apk {
func (a *App) ApksByVName(vname string) []Apk {
var apks []Apk
for i := range a.Apks {
apk := a.Apks[i]
if vname == a.Apks[i].VName {
apks = append(apks, a.Apks[i])
}
}
return apks
}
func (a *App) SuggestedVName() string {
for i := range a.Apks {
apk := &a.Apks[i]
if a.CVCode >= apk.VCode {
return &apk
return apk.VName
}
}
return ""
}
func (a *App) SuggestedApks() []Apk {
// No APKs => nothing to suggest
if len(a.Apks) == 0 {
return nil
}
// First, try to follow CV
apks := a.ApksByVName(a.SuggestedVName())
if len(apks) > 0 {
return apks
}
// When CV is missing current version code or it's invalid (no APKs
// match it), use heuristic: find all APKs having the same version
// string as the APK with the greatest version code
return a.ApksByVName(a.Apks[0].VName)
}
func (a *App) SuggestedApk(device *adb.Device) *Apk {
for i := range a.Apks {
apk := &a.Apks[i]
if a.CVCode >= apk.VCode && apk.IsCompatible(device) {
return apk
}
}
if len(a.Apks) > 0 {
return &a.Apks[0]
for i := range a.Apks {
apk := &a.Apks[i]
if apk.IsCompatible(device) {
return apk
}
}
return nil
}

Loading…
Cancel
Save