Permalink
Cannot retrieve contributors at this time
Fetching contributors…
| package main | |
| import ( | |
| "launchpad.net/go-unityscopes/v2" | |
| "log" | |
| "encoding/json" | |
| "net/http" | |
| "fmt" | |
| "math" | |
| "strings" | |
| "net/url" | |
| ) | |
| const searchCategoryTemplate = `{ | |
| "schema-version": 1, | |
| "template": { | |
| "category-layout": "grid", | |
| "card-size": "large", | |
| "card-layout": "horizontal" | |
| }, | |
| "components": { | |
| "title": "title", | |
| "subtitle": "subtitle", | |
| "overlay-color": "#FFFFFFFF", | |
| "art": { | |
| "field": "art", | |
| "aspect-ratio": 1 | |
| } | |
| } | |
| }` | |
| const uappexplorer = "<%= api %>"; | |
| type App struct { | |
| Author string `json:"author"` | |
| BayesianAverage float64 `json:"bayesian_average"` | |
| Description string `json:"description"` | |
| IconHash string `json:"icon_hash"` | |
| License string `json:"license"` | |
| Name string `json:"name"` | |
| Permissions []string `json:"permissions"` | |
| Points int64 `json:"points"` | |
| Screenshots []string `json:"screenshots"` | |
| Tagline string `json:"tagline"` | |
| Title string `json:"title"` | |
| Types []string `json:"types"` | |
| Version string `json:"version"` | |
| } | |
| type Data struct { | |
| Apps []App `json:"apps"` | |
| } | |
| type Response struct { | |
| Data Data `json:"data"` | |
| } | |
| type EResponse struct { | |
| Apps []App `json:"data"` | |
| } | |
| type ActionInfo struct { | |
| Id string `json:"id"` | |
| Label string `json:"label"` | |
| Icon string `json:"icon,omitempty"` | |
| Uri string `json:"uri,omitempty"` | |
| } | |
| var scope_interface scopes.Scope | |
| type UAppExplorerScope struct { | |
| base *scopes.ScopeBase | |
| } | |
| func (s *UAppExplorerScope) getEssentials(result interface {}) error { | |
| resp, err := http.Get(fmt.Sprintf("%s/api/apps/essentials", uappexplorer)) | |
| if err != nil { | |
| return err | |
| } | |
| defer resp.Body.Close() | |
| decoder := json.NewDecoder(resp.Body) | |
| return decoder.Decode(result) | |
| } | |
| func (s *UAppExplorerScope) get(search string, sort string, limit int, category string, typ string, result interface{}) error { | |
| var uri string = fmt.Sprintf("%s/api/apps?limit=%d", uappexplorer, limit) | |
| if search != "" { | |
| uri = fmt.Sprintf("%s&search=%s", uri, strings.Replace(search, " ", "%20", -1)) | |
| sort = "relevance" | |
| } | |
| if sort != "" { | |
| uri = fmt.Sprintf("%s&sort=%s", uri, sort) | |
| } | |
| query := map[string] string {} | |
| if category != "" { | |
| query["categories"] = category | |
| } | |
| if typ != "" { | |
| query["types"] = typ | |
| } | |
| if category != "" || typ != "" { | |
| jsonQuery, _ := json.Marshal(query) | |
| uri = fmt.Sprintf("%s&query=%s", uri, url.QueryEscape(string(jsonQuery))) | |
| } | |
| log.Println(uri) | |
| resp, err := http.Get(uri) | |
| if err != nil { | |
| return err | |
| } | |
| defer resp.Body.Close() | |
| decoder := json.NewDecoder(resp.Body) | |
| return decoder.Decode(result) | |
| } | |
| func (s *UAppExplorerScope) Preview(result *scopes.Result, metadata *scopes.ActionMetadata, reply *scopes.PreviewReply, cancelled <-chan bool) error { | |
| typeMap := map[string] string { | |
| "application": "App", | |
| "scope": "Scope", | |
| "webapp": "Web App", | |
| "snappy": "Snap", | |
| "snappy_oem": "OEM Snap", | |
| "snappy_os": "OS Snap", | |
| "snappy_kernel": "Kernel Snap", | |
| "snappy_gadget": "Gadget Snap", | |
| "snappy_framework": "Framework Snap", | |
| "snappy_application": "Snap", | |
| "unknown": "Unknown", | |
| } | |
| var types string = "" | |
| var permissions string = "" | |
| var app App | |
| if err := result.Get("app", &app); err != nil { | |
| log.Println(err) | |
| } | |
| for _, typ := range app.Types { | |
| if types == "" { | |
| types = typeMap[typ] | |
| } else { | |
| types = fmt.Sprintf("%s, %s", types, typeMap[typ]) | |
| } | |
| } | |
| for _, permission := range app.Permissions { | |
| permission = strings.Replace(permission, "-", " ", -1) | |
| permission = strings.Replace(permission, "_", " ", -1) | |
| permission = strings.Title(permission) | |
| if permissions == "" { | |
| permissions = permission | |
| } else { | |
| permissions = fmt.Sprintf("%s, %s", permissions, permission) | |
| } | |
| } | |
| columnLayout1 := scopes.NewColumnLayout(1) | |
| columnLayout1.AddColumn("header", "icon", "author", "hearts", "stars", | |
| "types", "actions", "more_apps", "actions2", "screenshots", "description", | |
| "permissions", "license", "version",) | |
| columnLayout2 := scopes.NewColumnLayout(2) | |
| columnLayout2.AddColumn("header", "icon", "screenshots", "permissions", | |
| "license", "version") | |
| columnLayout2.AddColumn("author", "hearts", "stars", "types", "actions", | |
| "more_apps", "actions2", "description") | |
| reply.RegisterLayout(columnLayout1, columnLayout2) | |
| headerWidget := scopes.NewPreviewWidget("header", "header") | |
| headerWidget.AddAttributeValue("title", app.Title) | |
| iconWidget := scopes.NewPreviewWidget("icon", "image") | |
| iconWidget.AddAttributeMapping("source", "art") | |
| typesWidget := scopes.NewPreviewWidget("types", "text") | |
| typesWidget.AddAttributeValue("text", fmt.Sprintf("<b>Type:</b> %s", types)) | |
| authorWidget := scopes.NewPreviewWidget("author", "text") | |
| authorWidget.AddAttributeValue("text", fmt.Sprintf("<b>Author:</b> %s", app.Author)) | |
| heartsWidget := scopes.NewPreviewWidget("hearts", "text") | |
| heartsWidget.AddAttributeValue("text", fmt.Sprintf("<b>Hearts:</b> %d", app.Points)) | |
| //Very hacky looking round to the nearest 0.5 | |
| starRating := map[string] float64 { | |
| "rating": math.Floor(app.BayesianAverage * 2 + 0.5) / 2, | |
| } | |
| var reviews []map[string] float64 | |
| reviews = append(reviews, starRating) | |
| starsWidget := scopes.NewPreviewWidget("stars", "reviews") | |
| starsWidget.AddAttributeValue("reviews", reviews) | |
| var buttons []ActionInfo | |
| buttons = append(buttons, ActionInfo{Id: "view", Uri: fmt.Sprintf("%s/app/%s", uappexplorer, app.Name), Label: "View"}) | |
| buttons = append(buttons, ActionInfo{Id: "install", Uri: "scope://com.canonical.scopes.clickstore?q=" + app.Title, Label: "Install"}) | |
| actionsWidget := scopes.NewPreviewWidget("actions", "actions") | |
| actionsWidget.AddAttributeValue("actions", buttons) | |
| moreWidget := scopes.NewPreviewWidget("more_apps", "text") | |
| moreWidget.AddAttributeValue("text", fmt.Sprintf("More apps by %s:", app.Author)) | |
| var buttons2 []ActionInfo | |
| uri := fmt.Sprintf("scope://uappexplorer-scope.bhdouglass_uappexplorer-scope?q=author:%s", strings.Replace(app.Author, " ", "%20", -1)) | |
| buttons2 = append(buttons2, ActionInfo{Id: "more", Uri: uri, Label: "More"}) | |
| actionsWidget2 := scopes.NewPreviewWidget("actions2", "actions") | |
| actionsWidget2.AddAttributeValue("actions", buttons2) | |
| screenshotsWidget := scopes.NewPreviewWidget("screenshots", "gallery") | |
| screenshotsWidget.AddAttributeValue("sources", app.Screenshots) | |
| descriptionWidget := scopes.NewPreviewWidget("description", "text") | |
| descriptionWidget.AddAttributeValue("text", app.Description) | |
| licenseWidget := scopes.NewPreviewWidget("license", "text") | |
| licenseWidget.AddAttributeValue("text", fmt.Sprintf("<b>License:</b> %s", app.License)) | |
| versionWidget := scopes.NewPreviewWidget("version", "text") | |
| versionWidget.AddAttributeValue("text", fmt.Sprintf("<b>Version:</b> %s", app.Version)) | |
| permissionsWidget := scopes.NewPreviewWidget("permissions", "text") | |
| permissionsWidget.AddAttributeValue("text", fmt.Sprintf("<b>Permissions:</b> %s", permissions)) | |
| return reply.PushWidgets(headerWidget, iconWidget, typesWidget, authorWidget, | |
| heartsWidget, starsWidget, actionsWidget, moreWidget, actionsWidget2, | |
| screenshotsWidget, descriptionWidget, licenseWidget, versionWidget, | |
| permissionsWidget) | |
| } | |
| func (s *UAppExplorerScope) addApps(apps []App, category *scopes. Category, reply *scopes.SearchReply) error { | |
| for _, app := range apps { | |
| result := scopes.NewCategorisedResult(category) | |
| result.SetURI(fmt.Sprintf("%s/app/%s", uappexplorer, app.Name)) | |
| result.SetTitle(app.Title) | |
| result.Set("subtitle", app.Tagline) | |
| result.Set("app", app) | |
| result.SetArt(fmt.Sprintf("%s/api/icon/%s/%s.png", uappexplorer, app.IconHash, app.Name)) | |
| if err := reply.Push(result); err != nil { | |
| return err | |
| } | |
| } | |
| return nil | |
| } | |
| func (s *UAppExplorerScope) CreateDepartments(query *scopes.CannedQuery, metadata *scopes.SearchMetadata, reply *scopes.SearchReply) error { | |
| category_dept, err := scopes.NewDepartment("", query, "Category") | |
| if err == nil { | |
| dept0, _ := scopes.NewDepartment("books-comics", query, "Books & Comics") | |
| category_dept.AddSubdepartment(dept0) | |
| dept1, _ := scopes.NewDepartment("business", query, "Business") | |
| category_dept.AddSubdepartment(dept1) | |
| dept2, _ := scopes.NewDepartment("communication", query, "Communication") | |
| category_dept.AddSubdepartment(dept2) | |
| dept3, _ := scopes.NewDepartment("developer-tools", query, "Developer Tools") | |
| category_dept.AddSubdepartment(dept3) | |
| dept4, _ := scopes.NewDepartment("education", query, "Education") | |
| category_dept.AddSubdepartment(dept4) | |
| dept5, _ := scopes.NewDepartment("entertainment", query, "Entertainment") | |
| category_dept.AddSubdepartment(dept5) | |
| dept6, _ := scopes.NewDepartment("finance", query, "Finance") | |
| category_dept.AddSubdepartment(dept6) | |
| dept7, _ := scopes.NewDepartment("food-drink", query, "Food & Drink") | |
| category_dept.AddSubdepartment(dept7) | |
| dept8, _ := scopes.NewDepartment("games", query, "Games") | |
| category_dept.AddSubdepartment(dept8) | |
| dept9, _ := scopes.NewDepartment("graphics", query, "Graphics") | |
| category_dept.AddSubdepartment(dept9) | |
| dept10, _ := scopes.NewDepartment("health-fitness", query, "Health & Fitness") | |
| category_dept.AddSubdepartment(dept10) | |
| dept11, _ := scopes.NewDepartment("lifestyle", query, "Lifestyle") | |
| category_dept.AddSubdepartment(dept11) | |
| dept12, _ := scopes.NewDepartment("media-video", query, "Media & Video") | |
| category_dept.AddSubdepartment(dept12) | |
| dept13, _ := scopes.NewDepartment("medical", query, "Medical") | |
| category_dept.AddSubdepartment(dept13) | |
| dept14, _ := scopes.NewDepartment("music-audio", query, "Music & Audio") | |
| category_dept.AddSubdepartment(dept14) | |
| dept15, _ := scopes.NewDepartment("news-magazines", query, "News & Magazines") | |
| category_dept.AddSubdepartment(dept15) | |
| dept16, _ := scopes.NewDepartment("personalisation", query, "Personalisation") | |
| category_dept.AddSubdepartment(dept16) | |
| dept17, _ := scopes.NewDepartment("productivity", query, "Productivity") | |
| category_dept.AddSubdepartment(dept17) | |
| dept18, _ := scopes.NewDepartment("reference", query, "Reference") | |
| category_dept.AddSubdepartment(dept18) | |
| dept19, _ := scopes.NewDepartment("science-engineering", query, "Science & Engineering") | |
| category_dept.AddSubdepartment(dept19) | |
| dept20, _ := scopes.NewDepartment("shopping", query, "Shopping") | |
| category_dept.AddSubdepartment(dept20) | |
| dept21, _ := scopes.NewDepartment("social-networking", query, "Social Networking") | |
| category_dept.AddSubdepartment(dept21) | |
| dept22, _ := scopes.NewDepartment("sports", query, "Sports") | |
| category_dept.AddSubdepartment(dept22) | |
| dept23, _ := scopes.NewDepartment("travel-local", query, "Travel & Local") | |
| category_dept.AddSubdepartment(dept23) | |
| dept24, _ := scopes.NewDepartment("universal-accessaccessibility", query, "Universal Access/Accessibility") | |
| category_dept.AddSubdepartment(dept24) | |
| dept25, _ := scopes.NewDepartment("accessories", query, "Utilities") | |
| category_dept.AddSubdepartment(dept25) | |
| dept26, _ := scopes.NewDepartment("weather", query, "Weather") | |
| category_dept.AddSubdepartment(dept26) | |
| reply.RegisterDepartments(category_dept) | |
| } | |
| return err | |
| } | |
| func (s *UAppExplorerScope) Search(query *scopes.CannedQuery, metadata *scopes.SearchMetadata, reply *scopes.SearchReply, cancelled <-chan bool) error { | |
| q := query.QueryString() | |
| log.Println(fmt.Sprintf("query: %s", q)) | |
| log.Println(fmt.Sprintf("dept id: %s", query.DepartmentID())) | |
| typeMap := map[string] string { | |
| "all": "All Phone Apps & Scopes", | |
| "application": "Apps", | |
| "scope": "Scopes", | |
| "webapp": "Web Apps", | |
| "snappy": "All Snaps", | |
| "snappy_oem": "OEM Snaps", | |
| "snappy_os": "OS Snaps", | |
| "snappy_kernel": "Kernel Snaps", | |
| "snappy_gadget": "Gadget Snaps", | |
| "snappy_framework": "Framework Snaps", | |
| "snappy_application": "App Snaps", | |
| } | |
| var res Response | |
| var eres EResponse | |
| type_filter := scopes.NewOptionSelectorFilter("types", "Types", false) | |
| type_filter.DisplayHints = 1 //scopes.FilterDisplayPrimary doesn't work here? | |
| for key, value := range typeMap { | |
| type_filter.AddOption(key, value) | |
| } | |
| //type_filter.ActiveOptions(query.FilterState()) ends in a panic, using this ugly hack instead | |
| var typ string = ""; | |
| if query.FilterState()["types"] != nil { | |
| typ = fmt.Sprintf("%s", query.FilterState()["types"]) | |
| typ = strings.Replace(typ, "[", "", -1) | |
| typ = strings.Replace(typ, "]", "", -1) | |
| } | |
| log.Println(fmt.Sprintf("type: %s", typ)) | |
| if (typ == "all") { | |
| typ = "" //treat "all" as if it were the default query | |
| } | |
| var name string = "Apps" | |
| var newName string = "New Apps" | |
| if typ != "" { | |
| name = typeMap[typ] | |
| } | |
| if q == "" { | |
| name = "Top Apps" | |
| if typ == "snappy" { | |
| name = "Top Snaps" | |
| newName = "New Snaps" | |
| } else if typ != "" { | |
| name = fmt.Sprintf("Top %s", typeMap[typ]) | |
| newName = fmt.Sprintf("New %s", typeMap[typ]) | |
| } | |
| } | |
| if err := reply.PushFilters([]scopes.Filter{type_filter}, query.FilterState()); err != nil { | |
| log.Println(err) | |
| } | |
| if err := s.CreateDepartments(query, metadata, reply); err != nil { | |
| log.Println(err) | |
| } | |
| top := reply.RegisterCategory("apps", name, "", searchCategoryTemplate) | |
| if err := s.get(q, "-points", 20, query.DepartmentID(), typ, &res); err != nil { | |
| log.Println(err) | |
| return err | |
| } | |
| if err := s.addApps(res.Data.Apps, top, reply); err != nil { | |
| log.Println(err) | |
| return err | |
| } | |
| if q == "" { | |
| n := reply.RegisterCategory("new", newName, "", searchCategoryTemplate) | |
| if err := s.get("", "-published_date", 10, query.DepartmentID(), typ, &res); err != nil { | |
| log.Println(err) | |
| return err | |
| } | |
| if err := s.addApps(res.Data.Apps, n, reply); err != nil { | |
| log.Println(err) | |
| return err | |
| } | |
| if (query.DepartmentID() == "" && typ == "") { | |
| essentials := reply.RegisterCategory("essentials", "Essentials", "", searchCategoryTemplate) | |
| if err := s.getEssentials(&eres); err != nil { | |
| log.Println(err) | |
| return err | |
| } | |
| if err := s.addApps(eres.Apps, essentials, reply); err != nil { | |
| log.Println(err) | |
| return err | |
| } | |
| } | |
| } | |
| return nil | |
| } | |
| func (s *UAppExplorerScope) SetScopeBase(base *scopes.ScopeBase) { | |
| s.base = base | |
| } | |
| func main() { | |
| log.Println("starting up") | |
| scope := &UAppExplorerScope{} | |
| if err := scopes.Run(scope); err != nil { | |
| log.Fatalln(err) | |
| } | |
| } |