Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add webp support for reading APK icon that caused "unknown format" before #90

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
69 changes: 69 additions & 0 deletions apk/apk.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,17 @@ package apk
import (
"archive/zip"
"bytes"
"encoding/xml"
"fmt"
"image"
"io"
"log"
"os"
"path/filepath"

"github.com/avast/apkparser"
"github.com/shogo82148/androidbinary"
_ "golang.org/x/image/webp"
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codebase verification

WebP support is not properly integrated

While the golang.org/x/image/webp package is imported in apk.go, there is no evidence of explicit handling for WebP in image decoding functions. This likely causes the "unknown format" error when processing APKs with WebP icons.

🔗 Analysis chain

Verify WebP support and check for explicit format handling

While this change should theoretically add WebP support, it's important to ensure it fully addresses the issue mentioned in #89.

  1. Please verify that the application can now successfully read and display WebP icons from APKs.
  2. Check if there are any other places in the codebase where image formats are explicitly checked or handled. If so, they might need to be updated to include WebP.

Run the following script to search for potential places where image formats are explicitly handled:

If the script returns any relevant results, please review those locations to ensure WebP is properly supported throughout the codebase.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Search for potential explicit image format handling

# Test: Search for image format related keywords
rg --type go -i '\b(image format|png|jpeg|jpg|webp)\b'

Length of output: 256


Script:

#!/bin/bash
# Description: Check if WebP is handled in image decoding functions

# Search for functions that decode images and verify WebP handling
rg --type go 'decode|parse' | rg -i 'webp'

Length of output: 42


_ "image/jpeg" // handle jpeg format
_ "image/png" // handle png format
Expand Down Expand Up @@ -71,6 +76,15 @@ func (k *Apk) Close() error {
return k.f.Close()
}

type AdaptiveIcon struct {
Background struct {
Drawable string `xml:"http://schemas.android.com/apk/res/android drawable,attr"`
} `xml:"background"`
Foreground struct {
Drawable string `xml:"http://schemas.android.com/apk/res/android drawable,attr"`
} `xml:"foreground"`
}

// Icon returns the icon image of the APK.
func (k *Apk) Icon(resConfig *androidbinary.ResTableConfig) (image.Image, error) {
iconPath, err := k.manifest.App.Icon.WithResTableConfig(resConfig).String()
Expand All @@ -84,6 +98,61 @@ func (k *Apk) Icon(resConfig *androidbinary.ResTableConfig) (image.Image, error)
if err != nil {
return nil, err
}

// is it an android XML file (maybe an adaptive-icon)?
if filepath.Ext(iconPath) == ".xml" {
// TODO: I absolutely do not like the below code but it worked for my sample application that used an adaptive icon; not sure if it would also work for others

// read the raw XML data from the android xml file
buf := new(bytes.Buffer)
enc := xml.NewEncoder(buf)
enc.Indent("", " ")
if err := apkparser.ParseXml(bytes.NewReader(imgData), enc, nil); err != nil {
return nil, err
}
tmpStr := buf.String()
log.Println(tmpStr)

xmlfile, err := androidbinary.NewXMLFile(bytes.NewReader(imgData))
if err != nil {
return nil, errorf("failed to parse %q: %w", iconPath, err)
}
var adaptiveIcon AdaptiveIcon
if err = xmlfile.Decode(&adaptiveIcon, k.table, nil); err != nil {
return nil, errorf("failed to decode %q: %w", iconPath, err)
}

var innerForegroundImagePath string
if androidbinary.IsResID(adaptiveIcon.Background.Drawable) {
resID, err := androidbinary.ParseResID(adaptiveIcon.Foreground.Drawable)
if err != nil {
return nil, errorf("failed to parse resID %q: %w", adaptiveIcon.Foreground.Drawable, err)
}

innerForegroundImagePathTmp, err := k.table.GetResource(resID, nil)
switch v := innerForegroundImagePathTmp.(type) {
case string:
innerForegroundImagePath = v
default:
return nil, errorf("failed to get resource %q: %w", adaptiveIcon.Foreground.Drawable, err)
}

innerForegroundImagePath = innerForegroundImagePathTmp.(string)
if err != nil {
return nil, errorf("failed to get resource %q: %w", adaptiveIcon.Foreground.Drawable, err)
}
Comment on lines +132 to +143
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Fix redundant error handling and ineffectual assignment

There are several issues in this code segment:

  1. The assignment at line 140 is ineffectual as the value was already assigned at line 135
  2. The error check at lines 141-143 is redundant with the type switch error handling above

Apply this diff to fix the issues:

       innerForegroundImagePathTmp, err := k.table.GetResource(resID, nil)
       switch v := innerForegroundImagePathTmp.(type) {
       case string:
           innerForegroundImagePath = v
       default:
           return nil, errorf("failed to get resource %q: %w", adaptiveIcon.Foreground.Drawable, err)
       }
-
-      innerForegroundImagePath = innerForegroundImagePathTmp.(string)
-      if err != nil {
-          return nil, errorf("failed to get resource %q: %w", adaptiveIcon.Foreground.Drawable, err)
-      }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
innerForegroundImagePathTmp, err := k.table.GetResource(resID, nil)
switch v := innerForegroundImagePathTmp.(type) {
case string:
innerForegroundImagePath = v
default:
return nil, errorf("failed to get resource %q: %w", adaptiveIcon.Foreground.Drawable, err)
}
innerForegroundImagePath = innerForegroundImagePathTmp.(string)
if err != nil {
return nil, errorf("failed to get resource %q: %w", adaptiveIcon.Foreground.Drawable, err)
}
innerForegroundImagePathTmp, err := k.table.GetResource(resID, nil)
switch v := innerForegroundImagePathTmp.(type) {
case string:
innerForegroundImagePath = v
default:
return nil, errorf("failed to get resource %q: %w", adaptiveIcon.Foreground.Drawable, err)
}
🧰 Tools
🪛 golangci-lint

135-135: ineffectual assignment to innerForegroundImagePath

(ineffassign)

}

if innerForegroundImagePath == "" {
return nil, errorf("failed to find inner foreground in %q", iconPath)
}

// read from the inner foreground image location
imgData, err = k.readZipFile(innerForegroundImagePath)
if err != nil {
return nil, errorf("failed to read %q: %w", adaptiveIcon.Foreground, err)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Fix error message to use the correct path variable

The error message uses adaptiveIcon.Foreground directly instead of innerForegroundImagePath.

Apply this diff to fix the error message:

-            return nil, errorf("failed to read %q: %w", adaptiveIcon.Foreground, err)
+            return nil, errorf("failed to read %q: %w", innerForegroundImagePath, err)

Committable suggestion skipped: line range outside the PR's diff.

}
}
m, _, err := image.Decode(bytes.NewReader(imgData))
return m, err
}
Expand Down
7 changes: 7 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
module github.com/shogo82148/androidbinary

go 1.17

require golang.org/x/image v0.20.0

require (
github.com/avast/apkparser v0.0.0-20240729092610-90591e0804ae // indirect
github.com/klauspost/compress v1.16.6 // indirect
)
67 changes: 67 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
github.com/avast/apkparser v0.0.0-20240729092610-90591e0804ae h1:rDNramK9mnAbvUBJyIRZnzHchM45cXexHIX9pS9da4Q=
github.com/avast/apkparser v0.0.0-20240729092610-90591e0804ae/go.mod h1:GNvprXNmXaDjpHmN3RFxz5QdK5VXTUvmQludCbjoBy4=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/klauspost/compress v1.16.6 h1:91SKEy4K37vkp255cJ8QesJhjyRO0hn9i9G0GoUwLsk=
github.com/klauspost/compress v1.16.6/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc=
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
golang.org/x/image v0.20.0 h1:7cVCUjQwfL18gyBJOmYvptfSHS8Fb3YUDtfLIZ7Nbpw=
golang.org/x/image v0.20.0/go.mod h1:0a88To4CYVBAHp5FXJm8o7QbUl37Vd85ply1vyD8auM=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk=
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU=
golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=
golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/text v0.18.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58=
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
Loading