+Grunge (sometimes referred to as the Seattle sound) is a subgenre of alternative rock that emerged during the mid-1980s in the American state of Washington, particularly in the Seattle area. The early Grunge movement revolved around Seattle's independent record labelSub Pop, but by the early 1990s its popularity had spread, with Grunge acts in California and other parts of the U.S. building strong followings and signing major record deals.
+
Inspired by hardcore punk and heavy metal, grunge is generally characterized by heavily distortedelectric guitars, contrasting song dynamics, "growling" vocals and apathetic or angst-filled lyrics. The grunge aesthetic is stripped-down compared with other forms of rock music, and many grunge musicians were noted for their unkempt appearances and rejection of theatrics.
+
Grunge became commercially successful in the first half of the 1990s, due mainly to the release of Nirvana's Nevermind, Pearl Jam's Ten, Soundgarden's Badmotorfinger, Alice in Chains' Dirt, and Stone Temple Pilots' Core. The success of these bands boosted the popularity of alternative rock and made grunge the most popular form of hard rock music at the time. Although most grunge bands had disbanded or faded from view by the late 1990s, their influence continues to affect modern rock music.
+
Grunge is generally characterized by a sludgy guitar sound that uses a high level of distortion, fuzz and feedback effects. Grunge fuses elements of hardcore punk and heavy metal, although some bands performed with more emphasis on one or the other. The music shares with punk a raw sound and similar lyrical concerns. However, it also involves much slower tempos, dissonant harmonies, and more complex instrumentation—which is reminiscent of heavy metal. Lyrics are typically angst-filled, often addressing themes such as social alienation, apathy, confinement, and a desire for freedom.
+
Grunge bands had made inroads to the musical mainstream in the late 1980s. Soundgarden was the first grunge band to sign to a major label when they joined the roster of A&M Records in 1989. A number of factors contributed to grunge's decline in prominence. During the mid-1990s many grunge bands broke up or became less visible. Nirvana's Kurt Cobain, labeled by Time as "the John Lennon of the swinging Northwest", appeared "unusually tortured by success" and struggled with an addiction to heroin before he committed suicide at the age of 27 in 1994.
Although writer Paul Ramball used "grunge" in a 1978 NME article to describe mainstream guitar rock, Mark Arm, the vocalist for the Seattle band Green River—and later Mudhoney—is generally credited as being the first to use the term grunge to describe this genre of music. Arm first used the term .in 1981, when he wrote a letter under his given name Mark McLaughlin to the Seattle zineDesperate Times, criticizing his band Mr. Epp and the Calculations as "Pure grunge! Pure noise! Pure shit!". Clark Humphrey, editor of Desperate Times, cites this as the earliest use of the term to refer to a Seattle band, and mentions that Bruce Pavitt of Sub Pop popularized the term as a musical label in 1987–88, using it on several occasions to describe Green River.
+
Arm said years later, "Obviously, I didn't make grunge up. I got it from someone else. The term was already being thrown around in Australia in the mid-'80s to describe bands like King Snake Roost, The Scientists, Salamander Jim, and Beasts of Bourbon." Arm used grunge as a descriptive term rather than a genre term, but it eventually came to describe the punk/metal hybrid sound of the Seattle music scene.
+
+Characteristics
+
+
Grunge is generally characterized by a sludgy guitar sound that uses a high level of distortion, fuzz, and feedback effects. Grunge fuses elements of hardcore punk and heavy metal, although some bands performed with more emphasis on one or the other. The music shares with punk a raw sound and similar lyrical concerns. However, it also involves much slower tempos, dissonant harmonies, and more complex instrumentation—which is reminiscent of heavy metal. Some individuals associated with the development of grunge, including Sub Pop producer Jack Endino and the Melvins, explained grunge's incorporation of heavy rock influences such as Kiss as "musical provocation". Grunge artists considered these bands "cheesy" but nonetheless enjoyed them; Buzz Osborne of the Melvins described it as an attempt to see what ridiculous things bands could do and get away with. In the early 1990s, Nirvana's signature "stop-start" song format became a genre convention. Allmusic calls grunge a "hybrid of heavy metal and punk". Although keyboards are generally not used in grunge, Seattle band Gorrilla created controversy by breaking the "guitars only" approach and using a 1960s-style Vox organ in their group.
+
Lyrics are typically angst-filled, often addressing themes such as social alienation, apathy, confinement, and a desire for freedom. A number of factors influenced the focus on such subject matter. Many grunge musicians displayed a general disenchantment with the state of society, as well as a discomfort with social prejudices. Such themes bear similarities to those addressed by punk rock musicians. Music critic Simon Reynolds said in 1992 that "there's a feeling of burnout in the culture at large. Kids are depressed about the future". Humor in grunge often satirized glam metal—for example, Soundgarden's "Big Dumb Sex"—and other forms of popular rock music during the 1980s.
+
Grunge concerts were known for being straightforward, high-energy performances. Grunge bands rejected the complex and high budget presentations of many musical genres, including the use of complex light arrays, pyrotechnics, and other visual effects unrelated to playing the music. Stage acting was generally avoided. Instead the bands presented themselves as no different from minor local bands. Jack Endino said in the 1996 documentary Hype! that Seattle bands were inconsistent live performers, since their primary objective was not to be entertainers, but simply to "rock out".
+
Clothing commonly worn by grunge musicians in Washington consisted of thrift store items and the typical outdoor clothing (most notably flannel shirts) of the region, as well as a generally unkempt appearance. The style did not evolve out of a conscious attempt to create an appealing fashion; music journalist Charles R. Cross said, "[Nirvana frontman] Kurt Cobain was just too lazy to shampoo", and Sub Pop's Jonathan Poneman said, "This [clothing] is cheap, it's durable, and it's kind of timeless. It also runs against the grain of the whole flashy aesthetic that existed in the 80s."
+
One of the philosophies of the grunge scene was authenticity. Dave Rimmer writes that with the revival of punk ideals of stripped-down music in the early 1990s, with "Cobain, and lots of kids like him, rock & roll...threw down a dare: Can you be pure enough, day after day, year after year, to prove your authenticity, to live up to the music ... And if you can't, can you live with being a poseur, a phony, a sellout?"
+
+History
+
+
+Roots and influences
+
+
Grunge's sound partly results from Seattle's isolation from other music scenes. As Sub Pop's Jonathan Poneman noted, "Seattle was a perfect example of a secondary city with an active music scene that was completely ignored by an American media fixated on Los Angeles and New York." Mark Arm claimed that the isolation meant, "this one corner of the map was being really inbred and ripping off each other's ideas". Grunge evolved from the local punk rock scene, and was inspired by bands such as The Fartz, The U-Men, 10 Minute Warning, The Accüsed, and the Fastbacks. Additionally, the slow, heavy, and sludgy style of the Melvins was a significant influence on the grunge sound.
+
Outside the Pacific Northwest, a number of artists and music scenes influenced grunge. Alternative rock bands from the Northeastern United States, including Sonic Youth, Pixies, and Dinosaur Jr., are important influences on the genre. Through their patronage of Seattle bands, Sonic Youth "inadvertently nurtured" the grunge scene, and reinforced the fiercely independent attitudes of its musicians. The influence of Pixies on Nirvana was noted by Kurt Cobain, who commented in a Rolling Stone interview, "I connected with that band so heavily that I should have been in that band—or at least a Pixies cover band. We used their sense of dynamics, being soft and quiet and then loud and hard." On August 1997, in an interview with Guitar World, Dave Grohl said: "From Kurt, Krist and I liking the Knack, Bay City Rollers, Beatles and Abba just as much as we liked Flipper and Black Flag...You listen to any Pixies record and it's all over there. Or even Black Sabbath's "War Pigs"-it's there: the power of the dynamic. We just sort of abused it with pop songs and got sick with it."
+
+
+
+
+
+
+
+
+
+
+
+
+
Seattle punk/metal band The U-Men performing in Seattle.
+
+
+
Aside from the genre's punk and alternative rock roots, many grunge bands were equally influenced by heavy metal of the early 1970s. Clinton Heylin, author of Babylon's Burning: From Punk to Grunge, cited Black Sabbath as "perhaps the most ubiquitous pre-punk influence on the northwest scene". Black Sabbath played a role in shaping the grunge sound, through their own records and the records they inspired. Musicologist Bob Gulla asserted that Black Sabbath's sound "shows up in virtually all of grunge's most popular bands, including Nirvana, Soundgarden, and Alice in Chains". The influence of Led Zeppelin is also evident, particularly in the work of Soundgarden, whom Q magazine noted were "in thrall to '70s rock, but contemptuous of the genre's overt sexism and machismo". Jon Wiederhorn of Guitar World wrote: "So what exactly is grunge?...Picture a supergroup made up of Creedence Clearwater Revival, Black Sabbath and the Stooges, and you're pretty close."
+
The Los Angeles hardcore punk band Black Flag's 1984 record My War, on which the band combined heavy metal with their traditional sound, made a strong impact in Seattle. Mudhoney's Steve Turner commented, "A lot of other people around the country hated the fact that Black Flag slowed down...but up here it was really great...we were like 'Yay!' They were weird and fucked-up sounding." Turner explained grunge's integration of metal influences, noting, "Hard rock and metal was never that much of an enemy of punk like it was for other scenes. Here, it was like, 'There's only twenty people here, you can't really find a group to hate.'" Bands began to mix metal and punk in the Seattle music scene around 1984, with much of the credit for this fusion going to The U-Men.
+
The raw, distorted and feedback-intensive sound of some noise rock bands had an influence on grunge. Among them are Wisconsin's Killdozer, and most notably San Francisco's Flipper, a band known for its slowed-down and murky "noise punk". The Butthole Surfers' mix of punk, heavy metal and noise rock was a major influence, particularly on the early work of Soundgarden.
+
After Neil Young played a few concerts with Pearl Jam and recorded the album Mirror Ball with them, some members of the media gave Young the title "Godfather of Grunge". This was grounded on his work with his band Crazy Horse and his regular use of distorted guitar, most notably on the album Rust Never Sleeps. A similarly influential yet often overlooked album is Neurotica by Redd Kross, about which the co-founder of Sub Pop said, "Neurotica was a life changer for me and for a lot of people in the Seattle music community."
+
+Early development
+
+
A seminal release in the development of grunge was the Deep Six compilation, released by C/Z Records in 1986. The record featured multiple tracks by six bands: Green River, Soundgarden, Melvins, Malfunkshun, Skin Yard, and The U-Men. For many of them it was their first appearance on record. The artists had "a mostly heavy, aggressive sound that melded the slower tempos of heavy metal with the intensity of hardcore". As Jack Endino recalled, "People just said, 'Well, what kind of music is this? This isn't metal, it's not punk, What is it?' [...] People went 'Eureka! These bands all have something in common.'"
+
Later that year Bruce Pavitt released the Sub Pop 100 compilation and Green River's Dry As a Bone EP as part of his new label, Sub Pop. An early Sub Pop catalog described the Green River EP as "ultra-loose GRUNGE that destroyed the morals of a generation". Sub Pop's Bruce Pavitt and Jonathan Poneman, inspired by other regional music scenes in music history, worked to ensure that their label projected a "Seattle sound", reinforced by a similar style of production and album packaging. While music writer Michael Azerrad acknowledged that early grunge bands like Mudhoney, Soundgarden, and Tad had disparate sounds, he noted "to the objective observer, there were some distinct similarities."
+
Early grunge concerts were sparsely attended (many by fewer than a dozen people) but Sub Pop photographer Charles Peterson's pictures helped create the impression that such concerts were major events. Mudhoney, which was formed by former members of Green River, served as the flagship band of Sub Pop during their entire time with the label and spearheaded the Seattle grunge movement. Other record labels in the Pacific Northwest that helped promote grunge included C/Z Records, Estrus Records, EMpTy Records and PopLlama Records.
+
Grunge attracted media attention in the United Kingdom after Pavitt and Poneman asked journalist Everett True from the British magazine Melody Maker to write an article on the local music scene. This exposure helped to make grunge known outside of the local area during the late 1980s and drew more people to local shows. The appeal of grunge to the music press was that it "promised the return to a notion of a regional, authorial vision for American rock". Grunge's popularity in the underground music scene was such that bands began to move to Seattle and approximate the look and sound of the original grunge bands. Mudhoney's Steve Turner said, "It was really bad. Pretend bands were popping up here, things weren't coming from where we were coming from." As a reaction, many grunge bands diversified their sound, with Nirvana and Tad in particular creating more melodic songs. Dawn Anderson of the Seattle fanzine Backlash recalled that by 1990 many locals had tired of the hype surrounding the Seattle scene and hoped that media exposure had dissipated.
Grunge bands had made inroads to the musical mainstream in the late 1980s. Soundgarden was the first grunge band to sign to a major label when they joined the roster of A&M Records in 1989. Soundgarden, along with other major label signings Alice in Chains and Screaming Trees, performed "okay" with their initial major label releases, according to Jack Endino. Nirvana, originally from Aberdeen, Washington, was also courted by major labels, finally signing with Geffen Records in 1990. In September 1991, the band released its major label debut, Nevermind. The album was at best hoped to be a minor success on par with Sonic Youth's Goo, which Geffen had released a year earlier. It was the release of the album's first single "Smells Like Teen Spirit" that "marked the instigation of the grunge music phenomenon". Due to constant airplay of the song's music video on MTV, Nevermind was selling 400,000 copies a week by Christmas 1991. In January 1992, Nevermind replaced pop superstar Michael Jackson's Dangerous at number one on the Billboard 200.
+
The success of Nevermind surprised the music industry. Nevermind not only popularized grunge, but also established "the cultural and commercial viability of alternative rock in general." Michael Azerrad asserted that Nevermind symbolized "a sea-change in rock music" in which the glam metal that had dominated rock music at that time fell out of favor in the face of music that was perceived as authentic and culturally relevant. Grunge made it possible for genres thought to be of a niche audience, no matter how radical, to prove their marketability and be co-opted by the mainstream, cementing the formation of an individualist, fragmented culture. Other grunge bands subsequently replicated Nirvana's success. Pearl Jam, which featured former Mother Love Bone members Jeff Ament and Stone Gossard, had released its debut album Ten in August 1991, a month before Nevermind, but album sales only picked up a year later. By the second half of 1992 Ten had become a breakthrough success, being certified gold and reaching number two on the Billboard charts.
+
Soundgarden's album Badmotorfinger and Alice in Chains' Dirt, along with the Temple of the Dog album collaboration featuring members of Pearl Jam and Soundgarden, were also among the 100 top selling albums of 1992. The popular breakthrough of these grunge bands prompted Rolling Stone to nickname Seattle "the new Liverpool". Major record labels signed most of the prominent grunge bands in Seattle, while a second influx of bands moved to the city in hopes of success. The grunge scene was the backdrop in the 1992 Cameron Crowe film Singles. There were several small roles, performances, and cameos in the film by popular Seattle grunge bands including Pearl Jam, Soundgarden, and Alice in Chains. Filmed in and around Seattle in 1991, the film was not released until 1992 during the height of grunge popularity.
+
The popularity of grunge resulted in a large interest in the Seattle music scene's perceived cultural traits. While the Seattle music scene in the late 1980s and early 1990s in actuality consisted of various styles and genres of music, its representation in the media "served to depict Seattle as a music 'community' in which the focus was upon the ongoing exploration of one musical idiom, namely grunge". The fashion industry marketed "grunge fashion" to consumers, charging premium prices for items such as knit ski hats. Critics asserted that advertising was co-opting elements of grunge and turning it into a fad. Entertainment Weekly commented in a 1993 article, "There hasn't been this kind of exploitation of a subculture since the media discovered hippies in the '60s". The New York Times compared the "grunging of America" to the mass-marketing of punk rock, disco, and hip hop in previous years. Ironically the New York Times was tricked into printing a fake list of slang terms that were supposedly used in the grunge scene; often referred to as the grunge speak hoax. This media hype surrounding grunge was documented in the 1996 documentary Hype!
+
+
A backlash against grunge began to develop in Seattle; in late 1992 Jonathan Poneman said that in the city, "All things grunge are treated with the utmost cynicism and amusement [. . .] Because the whole thing is a fabricated movement and always has been." Many grunge artists were uncomfortable with their success and the resulting attention it brought. Nirvana's Kurt Cobain told Michael Azerrad, "Famous is the last thing I wanted to be." Pearl Jam also felt the burden of success, with much of the attention falling on frontman Eddie Vedder.
+
Nirvana's follow-up album In Utero (1993) was an intentionally abrasive album that Nirvana bassist Krist Novoselic described as a "wild aggressive sound, a true alternative record" Nevertheless, upon its release in September 1993 In Utero topped the Billboard charts. Pearl Jam also continued to perform well commercially with its second album, Vs. (1993). The album sold a record 950,378 copies in its first week of release, topped the Billboard charts, and outperformed all other entries in the top ten that week combined.
+
During this period, acts with a "Grunge sound" that were not from Seattle were often panned by critics, who accused them of being copycat bands. Stone Temple Pilots in particular fell victim to this. In a January 1994 Rolling Stone poll, Stone Temple Pilots was simultaneously voted Best New Band by Rolling Stone's readers and Worst New Band by the magazine's music critics, highlighting the disparity between critics and fans.
+
+Decline of mainstream popularity
+
+
A number of factors contributed to grunge's decline in prominence. During the latter half of the 1990s, grunge was supplanted by post-grunge, which remained commercially viable into the start of the 21st century. Post-grunge bands such as Candlebox and Bush emerged soon after grunge's breakthrough. These artists lacked the underground roots of grunge and were largely influenced by what grunge had become, namely "a wildly popular form of inward-looking, serious-minded hard rock". Post-grunge was a more commercially viable genre that tempered the distorted guitars of grunge with polished, radio-ready production.
+
Conversely, another alternative rock genre, Britpop, emerged in part as a reaction against the dominance of grunge in the United Kingdom. In contrast to the dourness of grunge, Britpop was defined by "youthful exuberance and desire for recognition". Britpop artists were vocal about their disdain for grunge. In a 1993 NME interview, Damon Albarn of Britpop band Blur agreed with interviewer John Harris' assertion that Blur was an "anti-grunge band", and said, "Well, that's good. If punk was about getting rid of hippies, then I'm getting rid of grunge". Noel Gallagher of Oasis, while a fan of Nirvana, wrote music that refuted the pessimistic nature of grunge. Gallagher noted in 2006 that the 1994 Oasis single "Live Forever" "was written in the middle of grunge and all that, and I remember Nirvana had a tune called 'I Hate Myself and I Want to Die,' and I was like...'Well, I'm not fucking having that.' As much as I fucking like him [Cobain] and all that shit, I'm not having that. I can't have people like that coming over here, on smack, fucking saying that they hate themselves and they wanna die. That's fucking rubbish."
During the mid-1990s many grunge bands broke up or became less visible. Kurt Cobain, labeled by Time as "the John Lennon of the swinging Northwest", appeared "unusually tortured by success" and struggled with an addiction to heroin. Rumors surfaced in early 1994 that Cobain suffered a drug overdose and that Nirvana was breaking up. On April 8, 1994, Cobain was found dead in his Seattle home from an apparently self-inflicted gunshot wound; Nirvana summarily disbanded. That same year Pearl Jam canceled its summer tour in protest of what it regarded as ticket vendor Ticketmaster's unfair business practices. Pearl Jam then began a boycott of the company; however, Pearl Jam's initiative to play only at non-Ticketmaster venues effectively, with a few exceptions, prevented the band from playing shows in the United States for the next three years. In 1996 Alice in Chains gave their final performances with their ailing estranged lead singer, Layne Staley, who subsequently died from an overdose of cocaine and heroin in 2002. That same year Soundgarden and Screaming Trees released their final studio albums of the 1990s, Down on the Upside and Dust, respectively.
+
+21st century
+
+
Some grunge bands have continued recording and touring with success, including, most significantly, Pearl Jam. While in 2006 Rolling Stone writer Brian Hiatt described Pearl Jam as having "spent much of the past decade deliberately tearing apart their own fame", he noted the band developed a loyal concert following akin to that of the Grateful Dead. Despite Nirvana's demise, the band has continued to be successful posthumously. Due to the high sales for Kurt Cobain's Journals and the band's best-of compilation Nirvana upon their releases in 2002, The New York Times argued Nirvana "are having more success now than at any point since Mr. Cobain's suicide in 1994." The Nirvana song "You Know You're Right" reached #1 on the Hot Mainstream Rock Tracks chart. In the middle of the first decade of the 21st century, The Seattle Times made note of grunge-influenced groups emerging in Seattle, including Post Stardom Depression, The Valley, and The Weapons. Similarly, The Guardian reported of grunge-influenced groups from Yorkshire, including Dinosaur Pile-Up, The Old Romantic Killer Band, The Tempus, Above Them, Pulled Apart by Horses, and Wonderswan. Also, in 2003, the New York Times noted a resurgence in grunge fashion.
+
Alice In Chains reformed for a handful of reunion dates in 2005 with several different vocalists filing in for the late Layne Staley, eventually aligning with William Duvall and released their first record in 15 years in 2009 Black Gives Way To Blue a return to grunge form with a doom metal sensibility and cementing a new era without their original singer, while releasing another record in 2013 The Devil Put Dinosaurs Here, reaching #2 on the Billboard 200 and displaying more stoner rock and sludge metal influence. Soundgarden re-formed in 2010 and released King Animal two years later which reached the top five of the national albums charts in Denmark, New Zealand, and the United States.
+
+Prominent music acts
+
+
See Figure 4.1. (Bands labelled as 'grunge' by respondents).
+^Ramball, Paul (April 1, 1978). "he American Midwest: Akron and Cleveland--Exploring alternative hives of industry in Akron, City of Rubber, and Cleveland, City of Steel,". NME.|accessdate= requires |url= (help)
+
+
+^Humphrey, Clark. Loser: The Real Seattle Music Story. New York: Harry N. Abrams, 1999. ISBN 1-929069-24-3, p. 63.
+
+^ Marin, Rick. "Grunge: A Success Story". The New York Times. 15 November 1992.
+
+
+^Freind, Bill (29 January 2002). "Grunge". St. James Encyclopedia of Pop Culture. Archived from the original on 28 Jun 2012. Retrieved 29 September 2013.
+
+
+^Marsh, Dave. "LIVE THROUGH THIS....". Rock & Rap Archives 124.
+
+
+^Aston, Martin. "Freak Scene". Q: Nirvana and the Story of Grunge. December 2005. p. 12.
+
+
+^Wall, Mick. "Northwest Passage". Q: Nirvana and the Story of Grunge. December 2005. p. 9.
+
+
+^Wall, Mick. "Northwest Passage". Q: Nirvana and the Story of Grunge. December 2005. p. 8.
+
+
+^Everley, Dave. "Daydream Nation". Q: Nirvana and the Story of Grunge. December 2005. p. 39.
+
+
+^Fricke, David. "Kurt Cobain: The Rolling Stone Interview". Rolling Stone. 27 January 1994.
+
+^MacDonald, Patrick (31 July 1992). "Willard Is Saying 'Rats!' To Seattle's Grunge Profusion". The Seattle Times. \(.*\)$'`
+ if expr "$link" : '/.*' > /dev/null; then
+ PRG="$link"
+ else
+ PRG=`dirname "$PRG"`"/$link"
+ fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >&-
+APP_HOME="`pwd -P`"
+cd "$SAVED" >&-
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD="$JAVA_HOME/jre/sh/java"
+ else
+ JAVACMD="$JAVA_HOME/bin/java"
+ fi
+ if [ ! -x "$JAVACMD" ] ; then
+ die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+ fi
+else
+ JAVACMD="java"
+ which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
+ MAX_FD_LIMIT=`ulimit -H -n`
+ if [ $? -eq 0 ] ; then
+ if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+ MAX_FD="$MAX_FD_LIMIT"
+ fi
+ ulimit -n $MAX_FD
+ if [ $? -ne 0 ] ; then
+ warn "Could not set maximum file descriptor limit: $MAX_FD"
+ fi
+ else
+ warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+ fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+ GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin, switch paths to Windows format before running java
+if $cygwin ; then
+ APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+ CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+
+ # We build the pattern for arguments to be converted via cygpath
+ ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+ SEP=""
+ for dir in $ROOTDIRSRAW ; do
+ ROOTDIRS="$ROOTDIRS$SEP$dir"
+ SEP="|"
+ done
+ OURCYGPATTERN="(^($ROOTDIRS))"
+ # Add a user-defined pattern to the cygpath arguments
+ if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+ OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+ fi
+ # Now convert the arguments - kludge to limit ourselves to /bin/sh
+ i=0
+ for arg in "$@" ; do
+ CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+ CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
+
+ if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
+ eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+ else
+ eval `echo args$i`="\"$arg\""
+ fi
+ i=$((i+1))
+ done
+ case $i in
+ (0) set -- ;;
+ (1) set -- "$args0" ;;
+ (2) set -- "$args0" "$args1" ;;
+ (3) set -- "$args0" "$args1" "$args2" ;;
+ (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+ (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+ (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+ (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+ (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+ (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+ esac
+fi
+
+# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
+function splitJvmOpts() {
+ JVM_OPTS=("$@")
+}
+eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
+JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
+
+exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
diff --git a/gradlew.bat b/gradlew.bat
new file mode 100644
index 0000000..aec9973
--- /dev/null
+++ b/gradlew.bat
@@ -0,0 +1,90 @@
+@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS=
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto init
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto init
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:init
+@rem Get command-line arguments, handling Windowz variants
+
+if not "%OS%" == "Windows_NT" goto win9xME_args
+if "%@eval[2+2]" == "4" goto 4NT_args
+
+:win9xME_args
+@rem Slurp the command line arguments.
+set CMD_LINE_ARGS=
+set _SKIP=2
+
+:win9xME_args_slurp
+if "x%~1" == "x" goto execute
+
+set CMD_LINE_ARGS=%*
+goto execute
+
+:4NT_args
+@rem Get arguments from the 4NT Shell from JP Software
+set CMD_LINE_ARGS=%$
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/mobile/.gitignore b/mobile/.gitignore
new file mode 100644
index 0000000..796b96d
--- /dev/null
+++ b/mobile/.gitignore
@@ -0,0 +1 @@
+/build
diff --git a/mobile/build.gradle b/mobile/build.gradle
new file mode 100644
index 0000000..6a0951d
--- /dev/null
+++ b/mobile/build.gradle
@@ -0,0 +1,28 @@
+apply plugin: 'com.android.application'
+
+android {
+ compileSdkVersion 19
+ buildToolsVersion "20.0.0"
+
+ defaultConfig {
+ applicationId "co.r3labs.wearipedia"
+ minSdkVersion 18
+ targetSdkVersion 19
+ versionCode 1
+ versionName "1.0"
+ }
+ buildTypes {
+ release {
+ runProguard false
+ proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
+ }
+ }
+}
+
+dependencies {
+ compile fileTree(dir: 'libs', include: ['*.jar'])
+ compile 'com.squareup.okhttp:okhttp:2.0.0'
+ compile 'com.google.android.gms:play-services-wearable:+'
+ compile 'jtidy:jtidy:+'
+ compile 'com.google.guava:guava:+'
+}
diff --git a/mobile/mobile.iml b/mobile/mobile.iml
new file mode 100644
index 0000000..d55e497
--- /dev/null
+++ b/mobile/mobile.iml
@@ -0,0 +1,72 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/mobile/proguard-rules.pro b/mobile/proguard-rules.pro
new file mode 100644
index 0000000..4b5feba
--- /dev/null
+++ b/mobile/proguard-rules.pro
@@ -0,0 +1,17 @@
+# Add project specific ProGuard rules here.
+# By default, the flags in this file are appended to flags specified
+# in /home/haldean/apps/android-studio/sdk/tools/proguard/proguard-android.txt
+# You can edit the include path and order by changing the proguardFiles
+# directive in build.gradle.
+#
+# For more details, see
+# http://developer.android.com/guide/developing/tools/proguard.html
+
+# Add any project specific keep options here:
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+# public *;
+#}
diff --git a/mobile/src/androidTest/java/co/r3labs/wearipedia/ApplicationTest.java b/mobile/src/androidTest/java/co/r3labs/wearipedia/ApplicationTest.java
new file mode 100644
index 0000000..fc60813
--- /dev/null
+++ b/mobile/src/androidTest/java/co/r3labs/wearipedia/ApplicationTest.java
@@ -0,0 +1,13 @@
+package co.r3labs.wearipedia;
+
+import android.app.Application;
+import android.test.ApplicationTestCase;
+
+/**
+ * Testing Fundamentals
+ */
+public class ApplicationTest extends ApplicationTestCase {
+ public ApplicationTest() {
+ super(Application.class);
+ }
+}
\ No newline at end of file
diff --git a/mobile/src/main/AndroidManifest.xml b/mobile/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..e536e31
--- /dev/null
+++ b/mobile/src/main/AndroidManifest.xml
@@ -0,0 +1,28 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/mobile/src/main/java/co/r3labs/wearipedia/HtmlCleaner.java b/mobile/src/main/java/co/r3labs/wearipedia/HtmlCleaner.java
new file mode 100644
index 0000000..f45df77
--- /dev/null
+++ b/mobile/src/main/java/co/r3labs/wearipedia/HtmlCleaner.java
@@ -0,0 +1,107 @@
+package co.r3labs.wearipedia;
+
+import android.util.Log;
+
+import com.google.common.collect.ImmutableSet;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.NamedNodeMap;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+import org.w3c.tidy.Tidy;
+
+import java.io.ByteArrayInputStream;
+import java.io.StringWriter;
+
+import javax.xml.transform.Transformer;
+import javax.xml.transform.TransformerConfigurationException;
+import javax.xml.transform.TransformerException;
+import javax.xml.transform.TransformerFactory;
+import javax.xml.transform.dom.DOMSource;
+import javax.xml.transform.stream.StreamResult;
+
+public class HtmlCleaner {
+ private static final String TAG = "HtmlCleaner";
+
+ private static final ImmutableSet REMOVE_TAGS =
+ ImmutableSet.of("table", "img", "figure", "sup", "audio");
+ private static final ImmutableSet REMOVE_IDS =
+ ImmutableSet.of("good-star");
+ private static final ImmutableSet REMOVE_CLASSES =
+ ImmutableSet.of("haudio", "hatnote", "metadata");
+
+ public static String clean(String input) {
+ Tidy parser = new Tidy();
+ parser.setQuiet(true);
+ parser.setRawOut(true);
+ parser.setSmartIndent(true);
+ parser.setTidyMark(false);
+ Document document = parser.parseDOM(new ByteArrayInputStream(input.getBytes()), null);
+ walk(document, document);
+ walk(document, document);
+
+ Transformer transformer = null;
+ StringWriter writer = new StringWriter();
+ // Jesus this API is ugly.
+ try {
+ transformer = TransformerFactory.newInstance().newTransformer();
+ } catch (TransformerConfigurationException e) {
+ Log.e(TAG, "couldn't create transformer", e);
+ return input;
+ }
+ try {
+ transformer.transform(new DOMSource(document), new StreamResult(writer));
+ } catch (TransformerException e) {
+ Log.e(TAG, "couldn't perform transform", e);
+ return input;
+ }
+ return writer.toString();
+ }
+
+ private static void walk(Document d, Node n) {
+ if (n == null) {
+ return;
+ }
+ String name = n.getNodeName().toLowerCase();
+ if (REMOVE_TAGS.contains(name)) {
+ Log.d(TAG, " has remove-tag " + name);
+ removeNode(n);
+ return;
+ }
+ if (name.equals("li")) {
+ d.renameNode(n, "", "p");
+ }
+ if (n.hasAttributes()) {
+ NamedNodeMap attrs = n.getAttributes();
+ Node id = attrs.getNamedItem("id");
+ if (id != null && REMOVE_IDS.contains(id.getNodeValue().toLowerCase())) {
+ Log.d(TAG, " has remove-id " + id.getNodeValue());
+ removeNode(n);
+ return;
+ }
+ Node classes = attrs.getNamedItem("class");
+ if (classes != null) {
+ for (String cls : classes.getNodeValue().split(" ")) {
+ if (REMOVE_CLASSES.contains(cls.toLowerCase())) {
+ Log.d(TAG, " has remove-class " + cls);
+ removeNode(n);
+ return;
+ }
+ }
+ }
+ }
+ NodeList children = n.getChildNodes();
+ for (int i = 0, N = children.getLength(); i < N; i++) {
+ walk(d, children.item(i));
+ }
+ }
+
+ private static void removeNode(Node n) {
+ if (n.getParentNode() == null) {
+ Log.w(TAG, "couldn't remove " + n.getNodeName() + " tag");
+ return;
+ }
+ Log.d(TAG, "removing " + n.getNodeName() + " tag");
+ n.getParentNode().removeChild(n);
+ }
+}
diff --git a/mobile/src/main/java/co/r3labs/wearipedia/WearListener.java b/mobile/src/main/java/co/r3labs/wearipedia/WearListener.java
new file mode 100644
index 0000000..a64b569
--- /dev/null
+++ b/mobile/src/main/java/co/r3labs/wearipedia/WearListener.java
@@ -0,0 +1,192 @@
+package co.r3labs.wearipedia;
+
+import android.app.Service;
+import android.content.Intent;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.Message;
+import android.util.Log;
+
+import com.google.android.gms.common.ConnectionResult;
+import com.google.android.gms.common.api.GoogleApiClient;
+import com.google.android.gms.common.api.PendingResult;
+import com.google.android.gms.wearable.Asset;
+import com.google.android.gms.wearable.DataApi;
+import com.google.android.gms.wearable.DataMap;
+import com.google.android.gms.wearable.MessageEvent;
+import com.google.android.gms.wearable.PutDataMapRequest;
+import com.google.android.gms.wearable.Wearable;
+import com.google.android.gms.wearable.WearableListenerService;
+import com.squareup.okhttp.OkHttpClient;
+import com.squareup.okhttp.Request;
+import com.squareup.okhttp.Response;
+
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStreamWriter;
+import java.io.UnsupportedEncodingException;
+import java.net.URLEncoder;
+import java.nio.charset.UnsupportedCharsetException;
+
+public class WearListener extends WearableListenerService {
+ private static final String TAG = "Wearipedia";
+
+ private static final int MSG_INIT = 0;
+ private static final int MSG_PUT_DATA = 1;
+
+ private static final String PATH_PATTERN_DATA = "/result/%s";
+ private static final String PATH_SEARCH = "/search";
+
+ private static final String KEY_SEARCH_TERM = "q";
+ private static final String KEY_DATA = "data";
+
+ private static final String URL_PATTERN = "http://en.wikipedia.org/w/index.php?action=render&title=%s";
+
+ private OkHttpClient mHttpClient;
+ private GoogleApiClient mGoogleClient;
+
+ private Handler mHandler;
+
+ @Override
+ public void onCreate() {
+ super.onCreate();
+
+ HandlerThread ht = new HandlerThread("WearipediaWorker");
+ ht.start();
+ mHandler = new WorkerHandler(ht.getLooper());
+
+ mHttpClient = new OkHttpClient();
+ mGoogleClient = new GoogleApiClient.Builder(this)
+ .addConnectionCallbacks(new GoogleApiClient.ConnectionCallbacks() {
+ @Override
+ public void onConnected(Bundle connectionHint) {
+ Log.d(TAG, "onConnected: " + connectionHint);
+ }
+ @Override
+ public void onConnectionSuspended(int cause) {
+ Log.d(TAG, "onConnectionSuspended: " + cause);
+ }
+ })
+ .addOnConnectionFailedListener(new GoogleApiClient.OnConnectionFailedListener() {
+ @Override
+ public void onConnectionFailed(ConnectionResult result) {
+ Log.d(TAG, "onConnectionFailed: " + result);
+ }
+ })
+ .addApi(Wearable.API)
+ .build();
+
+ mHandler.obtainMessage(MSG_INIT).sendToTarget();
+ }
+
+ @Override
+ public int onStartCommand(Intent intent, int flags, int startId) {
+ Log.d(TAG, "got start command with intent " + intent);
+ if ("co.r3labs.wearipedia.TEST".equals(intent.getAction())) {
+ lookup(intent.getStringExtra("q"));
+ return START_NOT_STICKY;
+ }
+ return super.onStartCommand(intent, flags, startId);
+ }
+
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+ mGoogleClient.disconnect();
+ }
+
+ @Override
+ public void onMessageReceived(MessageEvent event) {
+ Log.d(TAG, "got event " + event);
+ if (!PATH_SEARCH.equals(event.getPath())) {
+ return;
+ }
+ DataMap data = DataMap.fromByteArray(event.getData());
+ String q = data.getString(KEY_SEARCH_TERM, null);
+ if (q == null) {
+ Log.d(TAG, "no search term provided");
+ return;
+ }
+ lookup(q);
+ }
+
+ private void lookup(String q) {
+ Log.d(TAG, "looking up " + q);
+ mHandler.obtainMessage(MSG_PUT_DATA, q).sendToTarget();
+ }
+
+ private class WorkerHandler extends Handler {
+ public WorkerHandler(Looper looper) {
+ super(looper);
+ }
+
+ @Override
+ public void handleMessage(Message m) {
+ Log.d(TAG, "got message " + m);
+ switch (m.what) {
+ case MSG_INIT:
+ mGoogleClient.blockingConnect();
+ return;
+
+ case MSG_PUT_DATA:
+ String q = (String) m.obj;
+ String url = String.format(URL_PATTERN, q);
+
+ Request netReq = new Request.Builder().url(url).build();
+ String body = null;
+ try {
+ Response response = mHttpClient.newCall(netReq).execute();
+ body = response.body().string();
+ } catch (IOException e) {
+ Log.w(TAG, "unable to fetch wikipedia data from URL " + url, e);
+ // TODO: write error to data item
+ return;
+ }
+
+ body = HtmlCleaner.clean(body);
+ writeDebugFile(body);
+
+ String path = String.format(PATH_PATTERN_DATA, q);
+ PutDataMapRequest req = PutDataMapRequest.create(path);
+
+ req.getDataMap().putString(KEY_SEARCH_TERM, q);
+ Asset asset = Asset.createFromBytes(body.getBytes());
+ req.getDataMap().putAsset(KEY_DATA, asset);
+
+ DataApi.DataItemResult dataResult =
+ Wearable.DataApi.putDataItem(mGoogleClient, req.asPutDataRequest()).await();
+ if (!dataResult.getStatus().isSuccess()) {
+ Log.e(TAG, "couldn't set data item: " + dataResult.getStatus().toString());
+ } else {
+ Log.i(TAG, "storing " + body.length() + " chars of data for query " + q + " at path " + path);
+ }
+ return;
+ }
+ }
+ }
+
+ private void writeDebugFile(String contents) {
+ FileOutputStream stream = null;
+ try {
+ stream = openFileOutput("debug.html", MODE_WORLD_READABLE);
+ new OutputStreamWriter(stream).write(contents);
+ stream.flush();
+ } catch (FileNotFoundException e) {
+ Log.w(TAG, "can't save debug file", e);
+ } catch (IOException e) {
+ Log.w(TAG, "couldn't write to debug file", e);
+ } finally {
+ if (stream != null) {
+ try {
+ stream.close();
+ } catch (IOException e) {
+ }
+ }
+ }
+ }
+}
diff --git a/mobile/src/main/res/drawable-hdpi/ic_launcher.png b/mobile/src/main/res/drawable-hdpi/ic_launcher.png
new file mode 100644
index 0000000..96a442e
Binary files /dev/null and b/mobile/src/main/res/drawable-hdpi/ic_launcher.png differ
diff --git a/mobile/src/main/res/drawable-mdpi/ic_launcher.png b/mobile/src/main/res/drawable-mdpi/ic_launcher.png
new file mode 100644
index 0000000..359047d
Binary files /dev/null and b/mobile/src/main/res/drawable-mdpi/ic_launcher.png differ
diff --git a/mobile/src/main/res/drawable-xhdpi/ic_launcher.png b/mobile/src/main/res/drawable-xhdpi/ic_launcher.png
new file mode 100644
index 0000000..71c6d76
Binary files /dev/null and b/mobile/src/main/res/drawable-xhdpi/ic_launcher.png differ
diff --git a/mobile/src/main/res/drawable-xxhdpi/ic_launcher.png b/mobile/src/main/res/drawable-xxhdpi/ic_launcher.png
new file mode 100644
index 0000000..4df1894
Binary files /dev/null and b/mobile/src/main/res/drawable-xxhdpi/ic_launcher.png differ
diff --git a/mobile/src/main/res/values/dimens.xml b/mobile/src/main/res/values/dimens.xml
new file mode 100644
index 0000000..47c8224
--- /dev/null
+++ b/mobile/src/main/res/values/dimens.xml
@@ -0,0 +1,5 @@
+
+
+ 16dp
+ 16dp
+
diff --git a/mobile/src/main/res/values/strings.xml b/mobile/src/main/res/values/strings.xml
new file mode 100644
index 0000000..0360608
--- /dev/null
+++ b/mobile/src/main/res/values/strings.xml
@@ -0,0 +1,4 @@
+
+
+ Wearipedia
+
diff --git a/mobile/src/main/res/values/styles.xml b/mobile/src/main/res/values/styles.xml
new file mode 100644
index 0000000..e0c5aae
--- /dev/null
+++ b/mobile/src/main/res/values/styles.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
diff --git a/mobile/src/main/res/values-w820dp/dimens.xml b/mobile/src/main/res/values-w820dp/dimens.xml
new file mode 100644
index 0000000..63fc816
--- /dev/null
+++ b/mobile/src/main/res/values-w820dp/dimens.xml
@@ -0,0 +1,6 @@
+
+
+ 64dp
+
diff --git a/settings.gradle b/settings.gradle
new file mode 100644
index 0000000..6a4e79f
--- /dev/null
+++ b/settings.gradle
@@ -0,0 +1 @@
+include ':mobile', ':wear'
diff --git a/wear/.gitignore b/wear/.gitignore
new file mode 100644
index 0000000..796b96d
--- /dev/null
+++ b/wear/.gitignore
@@ -0,0 +1 @@
+/build
diff --git a/wear/build.gradle b/wear/build.gradle
new file mode 100644
index 0000000..8cee6f2
--- /dev/null
+++ b/wear/build.gradle
@@ -0,0 +1,35 @@
+apply plugin: 'com.android.application'
+
+
+android {
+ compileSdkVersion 20
+ buildToolsVersion "20.0.0"
+
+ defaultConfig {
+ applicationId "co.r3labs.wearipedia"
+ minSdkVersion 20
+ targetSdkVersion 20
+ versionCode 1
+ versionName "1.0"
+ }
+
+ buildTypes {
+ debug {
+ runProguard true
+ proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
+ }
+ release {
+ runProguard true
+ proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
+ }
+ }
+}
+
+dependencies {
+ compile fileTree(dir: 'libs', include: ['*.jar'])
+ // You must install or update the Support Repository through the SDK manager to use this dependency.
+ // You must install or update the Support Repository through the SDK manager to use this dependency.
+ compile 'com.android.support:support-v13:+'
+ compile 'com.google.android.support:wearable:+'
+ compile 'com.google.android.gms:play-services-wearable:+'
+}
diff --git a/wear/proguard-rules.pro b/wear/proguard-rules.pro
new file mode 100644
index 0000000..d682151
--- /dev/null
+++ b/wear/proguard-rules.pro
@@ -0,0 +1,19 @@
+# Add project specific ProGuard rules here.
+# By default, the flags in this file are appended to flags specified
+# in /home/haldean/apps/android-studio/sdk/tools/proguard/proguard-android.txt
+# You can edit the include path and order by changing the proguardFiles
+# directive in build.gradle.
+#
+# For more details, see
+# http://developer.android.com/guide/developing/tools/proguard.html
+
+# Add any project specific keep options here:
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+# public *;
+#}
+-dontobfuscate
+-optimizations !field/*,!class/merging/*,!code/allocation/variable
diff --git a/wear/src/main/AndroidManifest.xml b/wear/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..0e69c85
--- /dev/null
+++ b/wear/src/main/AndroidManifest.xml
@@ -0,0 +1,27 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/wear/src/main/ic_wikipedia_white.zip b/wear/src/main/ic_wikipedia_white.zip
new file mode 100644
index 0000000..c7657e5
Binary files /dev/null and b/wear/src/main/ic_wikipedia_white.zip differ
diff --git a/wear/src/main/java/co/r3labs/wearipedia/SearchFragment.java b/wear/src/main/java/co/r3labs/wearipedia/SearchFragment.java
new file mode 100644
index 0000000..3a651e8
--- /dev/null
+++ b/wear/src/main/java/co/r3labs/wearipedia/SearchFragment.java
@@ -0,0 +1,25 @@
+package co.r3labs.wearipedia;
+
+import android.os.Bundle;
+import android.app.Fragment;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+
+public class SearchFragment extends Fragment {
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container,
+ Bundle savedInstanceState) {
+ View v = inflater.inflate(R.layout.fragment_search, container, false);
+ v.findViewById(R.id.search_button).setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ if (getActivity() == null) {
+ return;
+ }
+ ((WearipediaActivity) getActivity()).search();
+ }
+ });
+ return v;
+ }
+}
diff --git a/wear/src/main/java/co/r3labs/wearipedia/TextFragment.java b/wear/src/main/java/co/r3labs/wearipedia/TextFragment.java
new file mode 100644
index 0000000..5944a78
--- /dev/null
+++ b/wear/src/main/java/co/r3labs/wearipedia/TextFragment.java
@@ -0,0 +1,51 @@
+package co.r3labs.wearipedia;
+
+import android.app.Activity;
+import android.net.Uri;
+import android.os.Bundle;
+import android.app.Fragment;
+import android.text.Spanned;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.TextView;
+
+public class TextFragment extends Fragment {
+ TextView mTextView;
+ View mProgressView;
+ View mContentContainer;
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedState) {
+ View v = inflater.inflate(R.layout.fragment_text, container, false);
+ mTextView = (TextView) v.findViewById(R.id.content);
+ mProgressView = v.findViewById(R.id.progress);
+ mContentContainer = v.findViewById(R.id.content_container);
+ return v;
+ }
+
+ @Override
+ public void onAttach(Activity activity) {
+ super.onAttach(activity);
+ WearipediaActivity wa = (WearipediaActivity) activity;
+ wa.setTextDisplay(this);
+ }
+
+ @Override
+ public void onDetach() {
+ WearipediaActivity wa = (WearipediaActivity) getActivity();
+ wa.setTextDisplay(this);
+ super.onDetach();
+ }
+
+ public void setText(CharSequence text) {
+ if (text == null) {
+ mProgressView.setVisibility(View.VISIBLE);
+ mContentContainer.setVisibility(View.GONE);
+ return;
+ }
+ mProgressView.setVisibility(View.GONE);
+ mTextView.setText(text);
+ mContentContainer.setVisibility(View.VISIBLE);
+ }
+}
diff --git a/wear/src/main/java/co/r3labs/wearipedia/WearipediaActivity.java b/wear/src/main/java/co/r3labs/wearipedia/WearipediaActivity.java
new file mode 100644
index 0000000..d2830a8
--- /dev/null
+++ b/wear/src/main/java/co/r3labs/wearipedia/WearipediaActivity.java
@@ -0,0 +1,256 @@
+package co.r3labs.wearipedia;
+
+import android.app.Activity;
+import android.app.Fragment;
+import android.app.FragmentManager;
+import android.content.Intent;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Handler;
+import android.speech.RecognizerIntent;
+import android.support.v13.app.FragmentPagerAdapter;
+import android.support.v4.view.ViewPager;
+import android.text.Html;
+import android.util.Log;
+
+import com.google.android.gms.common.api.GoogleApiClient;
+import com.google.android.gms.common.api.GoogleApiClient.ConnectionCallbacks;
+import com.google.android.gms.common.api.GoogleApiClient.OnConnectionFailedListener;
+import com.google.android.gms.common.api.PendingResult;
+import com.google.android.gms.common.api.ResultCallback;
+import com.google.android.gms.common.ConnectionResult;
+import com.google.android.gms.wearable.*;
+
+import java.io.UnsupportedEncodingException;
+import java.net.URLEncoder;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Scanner;
+
+public class WearipediaActivity extends Activity {
+ private static final String TAG = "Wearipedia";
+
+ private static final int RECOGNIZE_ID = 0;
+
+ private static final String PATH_PATTERN_DATA = "/result/%s";
+ private static final String PATH_SEARCH = "/search";
+
+ private static final String KEY_SEARCH_TERM = "q";
+ private static final String KEY_DATA = "data";
+
+ private GoogleApiClient mClient;
+ private String mOtherNode = "other";
+ private volatile String mCurrentQ;
+ private Handler mHandler = new Handler();
+ private TextFragment mTextDisplay;
+ private DataMap mCachedDataMap;
+ private ViewPager mPager;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_wearipedia);
+
+ mPager = (ViewPager) findViewById(R.id.pager);
+ mPager.setAdapter(new Fragdapter(getFragmentManager()));
+
+ mClient = new GoogleApiClient.Builder(this)
+ .addConnectionCallbacks(new ConnectionCallbacks() {
+ @Override
+ public void onConnected(Bundle connectionHint) {
+ Log.d(TAG, "onConnected: " + connectionHint);
+ }
+ @Override
+ public void onConnectionSuspended(int cause) {
+ Log.d(TAG, "onConnectionSuspended: " + cause);
+ }
+ })
+ .addOnConnectionFailedListener(new OnConnectionFailedListener() {
+ @Override
+ public void onConnectionFailed(ConnectionResult result) {
+ Log.d(TAG, "onConnectionFailed: " + result);
+ }
+ })
+ .addApi(Wearable.API)
+ .build();
+
+ PendingResult res =
+ Wearable.NodeApi.getConnectedNodes(mClient);
+ res.setResultCallback(new ResultCallback() {
+ @Override
+ public void onResult(NodeApi.GetConnectedNodesResult res) {
+ if (!res.getStatus().isSuccess()) {
+ Log.e(TAG, "couldn't get connected nodes");
+ return;
+ }
+ List nodes = res.getNodes();
+ if (nodes.size() == 0) {
+ Log.w(TAG, "no node connected");
+ return;
+ }
+ Log.d(TAG, "got other node " + mOtherNode);
+ mOtherNode = nodes.get(0).getId();
+ }
+ });
+
+ mClient.connect();
+ Wearable.DataApi.addListener(mClient, mDataListener);
+
+ search();
+ }
+
+ @Override
+ public void onDestroy() {
+ Wearable.DataApi.removeListener(mClient, mDataListener);
+ mClient.disconnect();
+ super.onDestroy();
+ }
+
+ void setTextDisplay(TextFragment frag) {
+ mTextDisplay = frag;
+ if (mCachedDataMap != null) {
+ showData(mCachedDataMap);
+ }
+ }
+
+ void search() {
+ startActivityForResult(
+ new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH)
+ .putExtra(
+ RecognizerIntent.EXTRA_LANGUAGE_MODEL,
+ RecognizerIntent.LANGUAGE_MODEL_WEB_SEARCH)
+ .putExtra(RecognizerIntent.EXTRA_PROMPT, getString(R.string.prompt)),
+ RECOGNIZE_ID);
+ }
+
+ @Override
+ public void onActivityResult(int req, int resultCode, Intent data) {
+ if (req != RECOGNIZE_ID) {
+ return;
+ }
+ if (resultCode != RESULT_OK) {
+ Log.w(TAG, "speech recognizer returned code " + resultCode);
+ return;
+ }
+ ArrayList results = data.getStringArrayListExtra(RecognizerIntent.EXTRA_RESULTS);
+ if (results.size() == 0) {
+ Log.w(TAG, "speech recognizer returned no results");
+ return;
+ }
+
+ if (mTextDisplay != null) {
+ mTextDisplay.setText(null);
+ }
+ mPager.setCurrentItem(0);
+
+ String q = results.get(0);
+ q = q.replace(' ', '_');
+ try {
+ q = URLEncoder.encode(q, "utf-8");
+ } catch (UnsupportedEncodingException e) {
+ Log.i(TAG, "doesn't support utf-8?", e);
+ }
+ Log.w(TAG, "result: " + q);
+ mCurrentQ = q;
+
+ DataMap msg = new DataMap();
+ msg.putString(KEY_SEARCH_TERM, q);
+
+ PendingResult dataRes =
+ Wearable.DataApi.getDataItems(mClient, new Uri.Builder()
+ .scheme("wear")
+ .path(String.format(PATH_PATTERN_DATA, q))
+ .build());
+ dataRes.setResultCallback(new ResultCallback() {
+ @Override
+ public void onResult(DataItemBuffer results) {
+ for (DataItem data : results) {
+ Log.d(TAG, "already had data for page, loading");
+ showData(DataMapItem.fromDataItem(data).getDataMap());
+ return;
+ }
+ }
+ });
+
+ Log.d(TAG, "sending to node " + mOtherNode);
+ PendingResult res = Wearable.MessageApi.sendMessage(
+ mClient, mOtherNode, PATH_SEARCH, msg.toByteArray());
+ res.setResultCallback(new ResultCallback() {
+ @Override
+ public void onResult(MessageApi.SendMessageResult res) {
+ if (!res.getStatus().isSuccess()) {
+ Log.d(TAG, "couldn't send message: " + res.getStatus());
+ } else {
+ Log.d(TAG, "success");
+ }
+ }
+ });
+ }
+
+ void showData(DataMap data) {
+ mCachedDataMap = data;
+ if (mTextDisplay == null) {
+ return;
+ }
+ Asset asset = data.getAsset(KEY_DATA);
+ Log.d(TAG, "got asset " + asset);
+ PendingResult res =
+ Wearable.DataApi.getFdForAsset(mClient, asset);
+ res.setResultCallback(new ResultCallback() {
+ @Override
+ public void onResult(final DataApi.GetFdForAssetResult getFdForAssetResult) {
+ String data = convertStreamToString(getFdForAssetResult.getInputStream());
+ final CharSequence formatted = Html.fromHtml(data);
+ mHandler.post(new Runnable() {
+ @Override public void run() {
+ if (mTextDisplay != null) {
+ mTextDisplay.setText(formatted);
+ }
+ }
+ });
+ }
+ });
+ }
+
+ static String convertStreamToString(java.io.InputStream is) {
+ Scanner s = new Scanner(is).useDelimiter("\\A");
+ return s.hasNext() ? s.next() : "";
+ }
+
+ private DataApi.DataListener mDataListener = new DataApi.DataListener() {
+ @Override
+ public void onDataChanged(DataEventBuffer dataEvents) {
+ String targetPath = String.format(PATH_PATTERN_DATA, mCurrentQ);
+ Log.d(TAG, "got data events: " + dataEvents + ", looking for path " + targetPath);
+ for (DataEvent ev : dataEvents) {
+ String path = ev.getDataItem().getUri().getPath();
+ Log.d(TAG, "got path: " + path);
+ if (path.equals(targetPath)) {
+ showData(DataMapItem.fromDataItem(ev.getDataItem()).getDataMap());
+ }
+ }
+ }
+ };
+
+ private static class Fragdapter extends FragmentPagerAdapter {
+ public Fragdapter(FragmentManager fm) {
+ super(fm);
+ }
+
+ @Override
+ public Fragment getItem(int i) {
+ switch (i) {
+ case 0:
+ return new TextFragment();
+ case 1:
+ return new SearchFragment();
+ }
+ return null;
+ }
+
+ @Override
+ public int getCount() {
+ return 2;
+ }
+ }
+}
diff --git a/wear/src/main/res/drawable/search_button_bg.xml b/wear/src/main/res/drawable/search_button_bg.xml
new file mode 100644
index 0000000..6268899
--- /dev/null
+++ b/wear/src/main/res/drawable/search_button_bg.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/wear/src/main/res/drawable-hdpi/ic_launcher.png b/wear/src/main/res/drawable-hdpi/ic_launcher.png
new file mode 100644
index 0000000..55621cc
Binary files /dev/null and b/wear/src/main/res/drawable-hdpi/ic_launcher.png differ
diff --git a/wear/src/main/res/drawable-hdpi/ic_wikipedia.png b/wear/src/main/res/drawable-hdpi/ic_wikipedia.png
new file mode 100644
index 0000000..9a4c94b
Binary files /dev/null and b/wear/src/main/res/drawable-hdpi/ic_wikipedia.png differ
diff --git a/wear/src/main/res/drawable-hdpi/ic_wikipedia_white.png b/wear/src/main/res/drawable-hdpi/ic_wikipedia_white.png
new file mode 100644
index 0000000..bbb8440
Binary files /dev/null and b/wear/src/main/res/drawable-hdpi/ic_wikipedia_white.png differ
diff --git a/wear/src/main/res/drawable-mdpi/ic_launcher.png b/wear/src/main/res/drawable-mdpi/ic_launcher.png
new file mode 100644
index 0000000..11ec206
Binary files /dev/null and b/wear/src/main/res/drawable-mdpi/ic_launcher.png differ
diff --git a/wear/src/main/res/drawable-mdpi/ic_wikipedia.png b/wear/src/main/res/drawable-mdpi/ic_wikipedia.png
new file mode 100644
index 0000000..0405dad
Binary files /dev/null and b/wear/src/main/res/drawable-mdpi/ic_wikipedia.png differ
diff --git a/wear/src/main/res/drawable-mdpi/ic_wikipedia_white.png b/wear/src/main/res/drawable-mdpi/ic_wikipedia_white.png
new file mode 100644
index 0000000..b3e73b4
Binary files /dev/null and b/wear/src/main/res/drawable-mdpi/ic_wikipedia_white.png differ
diff --git a/wear/src/main/res/drawable-xhdpi/ic_launcher.png b/wear/src/main/res/drawable-xhdpi/ic_launcher.png
new file mode 100644
index 0000000..7c02b78
Binary files /dev/null and b/wear/src/main/res/drawable-xhdpi/ic_launcher.png differ
diff --git a/wear/src/main/res/drawable-xhdpi/ic_wikipedia.png b/wear/src/main/res/drawable-xhdpi/ic_wikipedia.png
new file mode 100644
index 0000000..0b6b6ac
Binary files /dev/null and b/wear/src/main/res/drawable-xhdpi/ic_wikipedia.png differ
diff --git a/wear/src/main/res/drawable-xhdpi/ic_wikipedia_white.png b/wear/src/main/res/drawable-xhdpi/ic_wikipedia_white.png
new file mode 100644
index 0000000..f5a813f
Binary files /dev/null and b/wear/src/main/res/drawable-xhdpi/ic_wikipedia_white.png differ
diff --git a/wear/src/main/res/drawable-xxhdpi/ic_launcher.png b/wear/src/main/res/drawable-xxhdpi/ic_launcher.png
new file mode 100644
index 0000000..915d914
Binary files /dev/null and b/wear/src/main/res/drawable-xxhdpi/ic_launcher.png differ
diff --git a/wear/src/main/res/drawable-xxhdpi/ic_wikipedia.png b/wear/src/main/res/drawable-xxhdpi/ic_wikipedia.png
new file mode 100644
index 0000000..af36f97
Binary files /dev/null and b/wear/src/main/res/drawable-xxhdpi/ic_wikipedia.png differ
diff --git a/wear/src/main/res/drawable-xxhdpi/ic_wikipedia_white.png b/wear/src/main/res/drawable-xxhdpi/ic_wikipedia_white.png
new file mode 100644
index 0000000..45accc2
Binary files /dev/null and b/wear/src/main/res/drawable-xxhdpi/ic_wikipedia_white.png differ
diff --git a/wear/src/main/res/layout/activity_wearipedia.xml b/wear/src/main/res/layout/activity_wearipedia.xml
new file mode 100644
index 0000000..33a1895
--- /dev/null
+++ b/wear/src/main/res/layout/activity_wearipedia.xml
@@ -0,0 +1,10 @@
+
+
\ No newline at end of file
diff --git a/wear/src/main/res/layout/fragment_search.xml b/wear/src/main/res/layout/fragment_search.xml
new file mode 100644
index 0000000..0eb9503
--- /dev/null
+++ b/wear/src/main/res/layout/fragment_search.xml
@@ -0,0 +1,43 @@
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/wear/src/main/res/layout/fragment_text.xml b/wear/src/main/res/layout/fragment_text.xml
new file mode 100644
index 0000000..c31f75e
--- /dev/null
+++ b/wear/src/main/res/layout/fragment_text.xml
@@ -0,0 +1,36 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/wear/src/main/res/values/colors.xml b/wear/src/main/res/values/colors.xml
new file mode 100644
index 0000000..3f230f2
--- /dev/null
+++ b/wear/src/main/res/values/colors.xml
@@ -0,0 +1,5 @@
+
+
+ #212121
+ #434343
+
\ No newline at end of file
diff --git a/wear/src/main/res/values/strings.xml b/wear/src/main/res/values/strings.xml
new file mode 100644
index 0000000..75a8b1a
--- /dev/null
+++ b/wear/src/main/res/values/strings.xml
@@ -0,0 +1,9 @@
+
+
+
+ Wikipedia
+ Search
+ Search Wikipedia
+ Hello blank fragment
+
+
diff --git a/wear/wear.iml b/wear/wear.iml
new file mode 100644
index 0000000..0f45d0d
--- /dev/null
+++ b/wear/wear.iml
@@ -0,0 +1,71 @@
+
+
+
+
+
+