├── .gitignore ├── LICENSE ├── README.md ├── app ├── .gitignore ├── build.gradle ├── libs │ ├── auto-common-1.0-20150122.001631-7.jar │ ├── dagger-2.0-20150122.022617-13.jar │ ├── dagger-compiler-2.0-20150122.022637-13-jar-with-dependencies.jar │ └── dagger-producers-2.0-20150122.022620-1.jar ├── proguard-rules.pro └── src │ ├── androidTest │ ├── assets │ │ └── events_response.json │ └── java │ │ └── org │ │ └── andydyer │ │ └── androidtestdemo │ │ ├── ApplicationTest.java │ │ ├── LoginActivityTest.java │ │ ├── MainActivityTest.java │ │ ├── api │ │ └── ApiServiceTest.java │ │ └── test │ │ ├── CustomMatchers.java │ │ └── CustomViewActions.java │ ├── debug │ └── java │ │ └── org │ │ └── andydyer │ │ └── androidtestdemo │ │ ├── Graph.java │ │ ├── InjectedActivityTest.java │ │ ├── InjectedInstrumentationTest.java │ │ └── api │ │ ├── DebugApiServiceModule.java │ │ └── MockApiService.java │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── org │ │ │ └── andydyer │ │ │ └── androidtestdemo │ │ │ ├── DemoApplication.java │ │ │ ├── api │ │ │ ├── Actor.java │ │ │ ├── ApiService.java │ │ │ ├── ApiServiceModule.java │ │ │ ├── AuthenticationService.java │ │ │ ├── Event.java │ │ │ ├── Events.java │ │ │ └── Repo.java │ │ │ └── ui │ │ │ ├── LoginActivity.java │ │ │ ├── MainActivity.java │ │ │ ├── WebViewActivity.java │ │ │ ├── fragments │ │ │ └── EventListFragment.java │ │ │ └── widgets │ │ │ └── SimpleDividerItemDecoration.java │ └── res │ │ ├── drawable-hdpi │ │ └── ic_launcher.png │ │ ├── drawable-mdpi │ │ └── ic_launcher.png │ │ ├── drawable-xhdpi │ │ └── ic_launcher.png │ │ ├── drawable-xxhdpi │ │ └── ic_launcher.png │ │ ├── drawable │ │ └── line_divider.xml │ │ ├── layout │ │ ├── activity_login.xml │ │ ├── activity_main.xml │ │ ├── activity_webview.xml │ │ ├── event_list_item.xml │ │ └── fragment_event_list.xml │ │ ├── menu │ │ └── main.xml │ │ ├── values-w820dp │ │ └── dimens.xml │ │ └── values │ │ ├── colors.xml │ │ ├── dimens.xml │ │ ├── strings.xml │ │ └── styles.xml │ └── release │ └── java │ └── org │ └── andydyer │ └── androidtestdemo │ └── Graph.java ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── presentation ├── Big Android BBQ 2014 - Automated Testing for Modern Android Applications.md └── images │ ├── alexanderplatz.jpg │ ├── berlin_sunset.jpg │ ├── c1.png │ ├── jon_snow.jpg │ ├── music_library.png │ ├── test_source_code.png │ ├── wrist_presenter.png │ └── ygritte.jpg └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | .gradle 2 | /local.properties 3 | /.idea/* 4 | .DS_Store 5 | /build 6 | 7 | *.iml 8 | 9 | presentation/*.pdf 10 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Andrew Dyer 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | android-test-demo 2 | ================= 3 | 4 | A sample application to demonstrate dependency injection and unit testing in a modern Android application. 5 | 6 | ## Presentation 7 | To keep the repo size down, the large PDF file of the presentation is not included. Instead, it is available [on Google Drive](http://goo.gl/ZiaX7p). 8 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | repositories { 3 | mavenCentral() 4 | } 5 | 6 | dependencies { 7 | // This plugin helps Android Studio find Dagger's generated classes 8 | classpath 'com.neenbedankt.gradle.plugins:android-apt:1.4' 9 | } 10 | } 11 | 12 | apply plugin: 'com.android.application' 13 | apply plugin: 'com.neenbedankt.android-apt' 14 | 15 | android { 16 | compileSdkVersion 21 17 | buildToolsVersion "21.1.2" 18 | 19 | packagingOptions { 20 | exclude 'META-INF/services/javax.annotation.processing.Processor' 21 | exclude 'META-INF/LICENSE.txt' 22 | exclude 'LICENSE.txt' 23 | exclude 'LICENSE' 24 | } 25 | 26 | defaultConfig { 27 | applicationId "org.andydyer.androidtestdemo" 28 | minSdkVersion 15 29 | targetSdkVersion 21 30 | versionCode 2 31 | versionName "1.1" 32 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 33 | } 34 | 35 | buildTypes { 36 | release { 37 | minifyEnabled false 38 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 39 | } 40 | } 41 | } 42 | 43 | repositories { 44 | // Currently Dagger 2 is published to Sonatype's maven repo 45 | maven { url "https://oss.sonatype.org/content/repositories/snapshots/" } 46 | } 47 | 48 | dependencies { 49 | compile 'com.squareup.retrofit:retrofit:1.9.0' 50 | compile 'com.squareup.picasso:picasso:2.4.0' 51 | compile 'com.google.code.gson:gson:2.3.1' 52 | provided 'org.projectlombok:lombok:1.14.8' 53 | apt 'org.projectlombok:lombok:1.14.8' 54 | compile 'com.jakewharton:butterknife:6.0.0' 55 | compile 'com.android.support:appcompat-v7:21.0.+' 56 | compile 'com.android.support:recyclerview-v7:21.0.+' 57 | 58 | // Dagger 2 dependencies 59 | compile files('libs/dagger-2.0-20150122.022617-13.jar') 60 | apt files('libs/dagger-compiler-2.0-20150122.022637-13-jar-with-dependencies.jar') 61 | apt files('libs/dagger-producers-2.0-20150122.022620-1.jar') 62 | provided 'org.glassfish:javax.annotation:10.0-b28' 63 | compile 'javax.inject:javax.inject:1' 64 | 65 | debugCompile('org.mockito:mockito-core:1.10.19') { 66 | exclude group: 'org.hamcrest' 67 | } 68 | debugCompile('com.google.dexmaker:dexmaker:1.2') { 69 | exclude group: 'org.hamcrest' 70 | } 71 | debugCompile('com.google.dexmaker:dexmaker-mockito:1.2') { 72 | exclude group: 'org.hamcrest' 73 | } 74 | debugCompile 'commons-io:commons-io:2.4' 75 | 76 | androidTestCompile('com.android.support.test.espresso:espresso-core:2.0') { 77 | exclude group: 'javax.inject' 78 | } 79 | androidTestCompile 'com.android.support.test:testing-support-lib:0.1' 80 | androidTestCompile('com.android.support.test.espresso:espresso-contrib:2.0') { 81 | exclude group: 'javax.inject' 82 | exclude group: 'com.android.support' 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /app/libs/auto-common-1.0-20150122.001631-7.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abdyer/android-test-demo/77d5fd47c44d7527dc6b095baa87ed61438c6aff/app/libs/auto-common-1.0-20150122.001631-7.jar -------------------------------------------------------------------------------- /app/libs/dagger-2.0-20150122.022617-13.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abdyer/android-test-demo/77d5fd47c44d7527dc6b095baa87ed61438c6aff/app/libs/dagger-2.0-20150122.022617-13.jar -------------------------------------------------------------------------------- /app/libs/dagger-compiler-2.0-20150122.022637-13-jar-with-dependencies.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abdyer/android-test-demo/77d5fd47c44d7527dc6b095baa87ed61438c6aff/app/libs/dagger-compiler-2.0-20150122.022637-13-jar-with-dependencies.jar -------------------------------------------------------------------------------- /app/libs/dagger-producers-2.0-20150122.022620-1.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abdyer/android-test-demo/77d5fd47c44d7527dc6b095baa87ed61438c6aff/app/libs/dagger-producers-2.0-20150122.022620-1.jar -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in /Applications/Android Studio.app/sdk/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | -------------------------------------------------------------------------------- /app/src/androidTest/assets/events_response.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "id": "2263128997", 4 | "type": "ForkEvent", 5 | "actor": { 6 | "id": 1996715, 7 | "login": "karthikraj-duraisamy", 8 | "gravatar_id": "4ec3af837e4f0a0e0c3893d897dcda1e", 9 | "url": "https://api.github.com/users/karthikraj-duraisamy", 10 | "avatar_url": "https://avatars.githubusercontent.com/u/1996715?" 11 | }, 12 | "repo": { 13 | "id": 18347476, 14 | "name": "google/iosched", 15 | "url": "https://api.github.com/repos/google/iosched" 16 | }, 17 | "payload": { 18 | "forkee": { 19 | "id": 23520190, 20 | "name": "iosched", 21 | "full_name": "karthikraj-duraisamy/iosched", 22 | "owner": { 23 | "login": "karthikraj-duraisamy", 24 | "id": 1996715, 25 | "avatar_url": "https://avatars.githubusercontent.com/u/1996715?v=2", 26 | "gravatar_id": "4ec3af837e4f0a0e0c3893d897dcda1e", 27 | "url": "https://api.github.com/users/karthikraj-duraisamy", 28 | "html_url": "https://github.com/karthikraj-duraisamy", 29 | "followers_url": "https://api.github.com/users/karthikraj-duraisamy/followers", 30 | "following_url": "https://api.github.com/users/karthikraj-duraisamy/following{/other_user}", 31 | "gists_url": "https://api.github.com/users/karthikraj-duraisamy/gists{/gist_id}", 32 | "starred_url": "https://api.github.com/users/karthikraj-duraisamy/starred{/owner}{/repo}", 33 | "subscriptions_url": "https://api.github.com/users/karthikraj-duraisamy/subscriptions", 34 | "organizations_url": "https://api.github.com/users/karthikraj-duraisamy/orgs", 35 | "repos_url": "https://api.github.com/users/karthikraj-duraisamy/repos", 36 | "events_url": "https://api.github.com/users/karthikraj-duraisamy/events{/privacy}", 37 | "received_events_url": "https://api.github.com/users/karthikraj-duraisamy/received_events", 38 | "type": "User", 39 | "site_admin": false 40 | }, 41 | "private": false, 42 | "html_url": "https://github.com/karthikraj-duraisamy/iosched", 43 | "description": null, 44 | "fork": true, 45 | "url": "https://api.github.com/repos/karthikraj-duraisamy/iosched", 46 | "forks_url": "https://api.github.com/repos/karthikraj-duraisamy/iosched/forks", 47 | "keys_url": "https://api.github.com/repos/karthikraj-duraisamy/iosched/keys{/key_id}", 48 | "collaborators_url": "https://api.github.com/repos/karthikraj-duraisamy/iosched/collaborators{/collaborator}", 49 | "teams_url": "https://api.github.com/repos/karthikraj-duraisamy/iosched/teams", 50 | "hooks_url": "https://api.github.com/repos/karthikraj-duraisamy/iosched/hooks", 51 | "issue_events_url": "https://api.github.com/repos/karthikraj-duraisamy/iosched/issues/events{/number}", 52 | "events_url": "https://api.github.com/repos/karthikraj-duraisamy/iosched/events", 53 | "assignees_url": "https://api.github.com/repos/karthikraj-duraisamy/iosched/assignees{/user}", 54 | "branches_url": "https://api.github.com/repos/karthikraj-duraisamy/iosched/branches{/branch}", 55 | "tags_url": "https://api.github.com/repos/karthikraj-duraisamy/iosched/tags", 56 | "blobs_url": "https://api.github.com/repos/karthikraj-duraisamy/iosched/git/blobs{/sha}", 57 | "git_tags_url": "https://api.github.com/repos/karthikraj-duraisamy/iosched/git/tags{/sha}", 58 | "git_refs_url": "https://api.github.com/repos/karthikraj-duraisamy/iosched/git/refs{/sha}", 59 | "trees_url": "https://api.github.com/repos/karthikraj-duraisamy/iosched/git/trees{/sha}", 60 | "statuses_url": "https://api.github.com/repos/karthikraj-duraisamy/iosched/statuses/{sha}", 61 | "languages_url": "https://api.github.com/repos/karthikraj-duraisamy/iosched/languages", 62 | "stargazers_url": "https://api.github.com/repos/karthikraj-duraisamy/iosched/stargazers", 63 | "contributors_url": "https://api.github.com/repos/karthikraj-duraisamy/iosched/contributors", 64 | "subscribers_url": "https://api.github.com/repos/karthikraj-duraisamy/iosched/subscribers", 65 | "subscription_url": "https://api.github.com/repos/karthikraj-duraisamy/iosched/subscription", 66 | "commits_url": "https://api.github.com/repos/karthikraj-duraisamy/iosched/commits{/sha}", 67 | "git_commits_url": "https://api.github.com/repos/karthikraj-duraisamy/iosched/git/commits{/sha}", 68 | "comments_url": "https://api.github.com/repos/karthikraj-duraisamy/iosched/comments{/number}", 69 | "issue_comment_url": "https://api.github.com/repos/karthikraj-duraisamy/iosched/issues/comments/{number}", 70 | "contents_url": "https://api.github.com/repos/karthikraj-duraisamy/iosched/contents/{+path}", 71 | "compare_url": "https://api.github.com/repos/karthikraj-duraisamy/iosched/compare/{base}...{head}", 72 | "merges_url": "https://api.github.com/repos/karthikraj-duraisamy/iosched/merges", 73 | "archive_url": "https://api.github.com/repos/karthikraj-duraisamy/iosched/{archive_format}{/ref}", 74 | "downloads_url": "https://api.github.com/repos/karthikraj-duraisamy/iosched/downloads", 75 | "issues_url": "https://api.github.com/repos/karthikraj-duraisamy/iosched/issues{/number}", 76 | "pulls_url": "https://api.github.com/repos/karthikraj-duraisamy/iosched/pulls{/number}", 77 | "milestones_url": "https://api.github.com/repos/karthikraj-duraisamy/iosched/milestones{/number}", 78 | "notifications_url": "https://api.github.com/repos/karthikraj-duraisamy/iosched/notifications{?since,all,participating}", 79 | "labels_url": "https://api.github.com/repos/karthikraj-duraisamy/iosched/labels{/name}", 80 | "releases_url": "https://api.github.com/repos/karthikraj-duraisamy/iosched/releases{/id}", 81 | "created_at": "2014-08-31T18:31:22Z", 82 | "updated_at": "2014-08-31T18:31:21Z", 83 | "pushed_at": "2014-08-19T18:34:02Z", 84 | "git_url": "git://github.com/karthikraj-duraisamy/iosched.git", 85 | "ssh_url": "git@github.com:karthikraj-duraisamy/iosched.git", 86 | "clone_url": "https://github.com/karthikraj-duraisamy/iosched.git", 87 | "svn_url": "https://github.com/karthikraj-duraisamy/iosched", 88 | "homepage": null, 89 | "size": 19325, 90 | "stargazers_count": 0, 91 | "watchers_count": 0, 92 | "language": null, 93 | "has_issues": false, 94 | "has_downloads": true, 95 | "has_wiki": false, 96 | "forks_count": 0, 97 | "mirror_url": null, 98 | "open_issues_count": 0, 99 | "forks": 0, 100 | "open_issues": 0, 101 | "watchers": 0, 102 | "default_branch": "master", 103 | "public": true 104 | } 105 | }, 106 | "public": true, 107 | "created_at": "2014-08-31T18:31:22Z", 108 | "org": { 109 | "id": 1342004, 110 | "login": "google", 111 | "gravatar_id": "24ba30616d2a20673f54c2aee36d159e", 112 | "url": "https://api.github.com/orgs/google", 113 | "avatar_url": "https://avatars.githubusercontent.com/u/1342004?" 114 | } 115 | }, 116 | { 117 | "id": "2263128978", 118 | "type": "WatchEvent", 119 | "actor": { 120 | "id": 1996715, 121 | "login": "karthikraj-duraisamy", 122 | "gravatar_id": "4ec3af837e4f0a0e0c3893d897dcda1e", 123 | "url": "https://api.github.com/users/karthikraj-duraisamy", 124 | "avatar_url": "https://avatars.githubusercontent.com/u/1996715?" 125 | }, 126 | "repo": { 127 | "id": 18347476, 128 | "name": "google/iosched", 129 | "url": "https://api.github.com/repos/google/iosched" 130 | }, 131 | "payload": { 132 | "action": "started" 133 | }, 134 | "public": true, 135 | "created_at": "2014-08-31T18:31:21Z", 136 | "org": { 137 | "id": 1342004, 138 | "login": "google", 139 | "gravatar_id": "24ba30616d2a20673f54c2aee36d159e", 140 | "url": "https://api.github.com/orgs/google", 141 | "avatar_url": "https://avatars.githubusercontent.com/u/1342004?" 142 | } 143 | }, 144 | { 145 | "id": "2263123699", 146 | "type": "IssuesEvent", 147 | "actor": { 148 | "id": 2864371, 149 | "login": "IonicaBizau", 150 | "gravatar_id": "84352b749bca68ff058e27fca784d2d5", 151 | "url": "https://api.github.com/users/IonicaBizau", 152 | "avatar_url": "https://avatars.githubusercontent.com/u/2864371?" 153 | }, 154 | "repo": { 155 | "id": 8608480, 156 | "name": "google/google-api-nodejs-client", 157 | "url": "https://api.github.com/repos/google/google-api-nodejs-client" 158 | }, 159 | "payload": { 160 | "action": "opened", 161 | "issue": { 162 | "url": "https://api.github.com/repos/google/google-api-nodejs-client/issues/272", 163 | "labels_url": "https://api.github.com/repos/google/google-api-nodejs-client/issues/272/labels{/name}", 164 | "comments_url": "https://api.github.com/repos/google/google-api-nodejs-client/issues/272/comments", 165 | "events_url": "https://api.github.com/repos/google/google-api-nodejs-client/issues/272/events", 166 | "html_url": "https://github.com/google/google-api-nodejs-client/issues/272", 167 | "id": 41592521, 168 | "number": 272, 169 | "title": "What text editor/editor plugin do you use for JSDoc comments?", 170 | "user": { 171 | "login": "IonicaBizau", 172 | "id": 2864371, 173 | "avatar_url": "https://avatars.githubusercontent.com/u/2864371?v=2", 174 | "gravatar_id": "84352b749bca68ff058e27fca784d2d5", 175 | "url": "https://api.github.com/users/IonicaBizau", 176 | "html_url": "https://github.com/IonicaBizau", 177 | "followers_url": "https://api.github.com/users/IonicaBizau/followers", 178 | "following_url": "https://api.github.com/users/IonicaBizau/following{/other_user}", 179 | "gists_url": "https://api.github.com/users/IonicaBizau/gists{/gist_id}", 180 | "starred_url": "https://api.github.com/users/IonicaBizau/starred{/owner}{/repo}", 181 | "subscriptions_url": "https://api.github.com/users/IonicaBizau/subscriptions", 182 | "organizations_url": "https://api.github.com/users/IonicaBizau/orgs", 183 | "repos_url": "https://api.github.com/users/IonicaBizau/repos", 184 | "events_url": "https://api.github.com/users/IonicaBizau/events{/privacy}", 185 | "received_events_url": "https://api.github.com/users/IonicaBizau/received_events", 186 | "type": "User", 187 | "site_admin": false 188 | }, 189 | "labels": [ 190 | 191 | ], 192 | "state": "open", 193 | "locked": false, 194 | "assignee": null, 195 | "milestone": null, 196 | "comments": 0, 197 | "created_at": "2014-08-31T18:21:07Z", 198 | "updated_at": "2014-08-31T18:21:07Z", 199 | "closed_at": null, 200 | "body": "I'm interested what tools you use for adding such comments:\r\n\r\n```js\r\n/**\r\n * Foo\r\n * @param {Object} fooParam Description\r\n * @constructor\r\n */\r\n...\r\n```" 201 | } 202 | }, 203 | "public": true, 204 | "created_at": "2014-08-31T18:21:07Z", 205 | "org": { 206 | "id": 1342004, 207 | "login": "google", 208 | "gravatar_id": "24ba30616d2a20673f54c2aee36d159e", 209 | "url": "https://api.github.com/orgs/google", 210 | "avatar_url": "https://avatars.githubusercontent.com/u/1342004?" 211 | } 212 | }, 213 | { 214 | "id": "2263120263", 215 | "type": "WatchEvent", 216 | "actor": { 217 | "id": 849044, 218 | "login": "cstrouse", 219 | "gravatar_id": "c1c3365169107669a2756f3ad35bd076", 220 | "url": "https://api.github.com/users/cstrouse", 221 | "avatar_url": "https://avatars.githubusercontent.com/u/849044?" 222 | }, 223 | "repo": { 224 | "id": 12169108, 225 | "name": "google/closure-library", 226 | "url": "https://api.github.com/repos/google/closure-library" 227 | }, 228 | "payload": { 229 | "action": "started" 230 | }, 231 | "public": true, 232 | "created_at": "2014-08-31T18:14:39Z", 233 | "org": { 234 | "id": 1342004, 235 | "login": "google", 236 | "gravatar_id": "24ba30616d2a20673f54c2aee36d159e", 237 | "url": "https://api.github.com/orgs/google", 238 | "avatar_url": "https://avatars.githubusercontent.com/u/1342004?" 239 | } 240 | }, 241 | { 242 | "id": "2263119429", 243 | "type": "WatchEvent", 244 | "actor": { 245 | "id": 2356870, 246 | "login": "DaleWebb", 247 | "gravatar_id": "9f82fd4acb53eff8a7506c60aae175b9", 248 | "url": "https://api.github.com/users/DaleWebb", 249 | "avatar_url": "https://avatars.githubusercontent.com/u/2356870?" 250 | }, 251 | "repo": { 252 | "id": 18511024, 253 | "name": "google/web-starter-kit", 254 | "url": "https://api.github.com/repos/google/web-starter-kit" 255 | }, 256 | "payload": { 257 | "action": "started" 258 | }, 259 | "public": true, 260 | "created_at": "2014-08-31T18:13:04Z", 261 | "org": { 262 | "id": 1342004, 263 | "login": "google", 264 | "gravatar_id": "24ba30616d2a20673f54c2aee36d159e", 265 | "url": "https://api.github.com/orgs/google", 266 | "avatar_url": "https://avatars.githubusercontent.com/u/1342004?" 267 | } 268 | }, 269 | { 270 | "id": "2263114107", 271 | "type": "WatchEvent", 272 | "actor": { 273 | "id": 1296901, 274 | "login": "scribeGriff", 275 | "gravatar_id": "be83de65aa6e5e52c762adc41220f495", 276 | "url": "https://api.github.com/users/scribeGriff", 277 | "avatar_url": "https://avatars.githubusercontent.com/u/1296901?" 278 | }, 279 | "repo": { 280 | "id": 18511024, 281 | "name": "google/web-starter-kit", 282 | "url": "https://api.github.com/repos/google/web-starter-kit" 283 | }, 284 | "payload": { 285 | "action": "started" 286 | }, 287 | "public": true, 288 | "created_at": "2014-08-31T18:02:38Z", 289 | "org": { 290 | "id": 1342004, 291 | "login": "google", 292 | "gravatar_id": "24ba30616d2a20673f54c2aee36d159e", 293 | "url": "https://api.github.com/orgs/google", 294 | "avatar_url": "https://avatars.githubusercontent.com/u/1342004?" 295 | } 296 | }, 297 | { 298 | "id": "2263107088", 299 | "type": "WatchEvent", 300 | "actor": { 301 | "id": 21374, 302 | "login": "eggcaker", 303 | "gravatar_id": "b9c6b9667c5b69a559f8a1686ac35366", 304 | "url": "https://api.github.com/users/eggcaker", 305 | "avatar_url": "https://avatars.githubusercontent.com/u/21374?" 306 | }, 307 | "repo": { 308 | "id": 18511024, 309 | "name": "google/web-starter-kit", 310 | "url": "https://api.github.com/repos/google/web-starter-kit" 311 | }, 312 | "payload": { 313 | "action": "started" 314 | }, 315 | "public": true, 316 | "created_at": "2014-08-31T17:49:08Z", 317 | "org": { 318 | "id": 1342004, 319 | "login": "google", 320 | "gravatar_id": "24ba30616d2a20673f54c2aee36d159e", 321 | "url": "https://api.github.com/orgs/google", 322 | "avatar_url": "https://avatars.githubusercontent.com/u/1342004?" 323 | } 324 | }, 325 | { 326 | "id": "2263098694", 327 | "type": "WatchEvent", 328 | "actor": { 329 | "id": 222763, 330 | "login": "nyxtom", 331 | "gravatar_id": "e2cd690765781180062c095b0e05fca0", 332 | "url": "https://api.github.com/users/nyxtom", 333 | "avatar_url": "https://avatars.githubusercontent.com/u/222763?" 334 | }, 335 | "repo": { 336 | "id": 18511024, 337 | "name": "google/web-starter-kit", 338 | "url": "https://api.github.com/repos/google/web-starter-kit" 339 | }, 340 | "payload": { 341 | "action": "started" 342 | }, 343 | "public": true, 344 | "created_at": "2014-08-31T17:32:05Z", 345 | "org": { 346 | "id": 1342004, 347 | "login": "google", 348 | "gravatar_id": "24ba30616d2a20673f54c2aee36d159e", 349 | "url": "https://api.github.com/orgs/google", 350 | "avatar_url": "https://avatars.githubusercontent.com/u/1342004?" 351 | } 352 | }, 353 | { 354 | "id": "2263095666", 355 | "type": "WatchEvent", 356 | "actor": { 357 | "id": 473894, 358 | "login": "sneakersgames", 359 | "gravatar_id": "e9f7df8901b29f4b5178b52f92319e5a", 360 | "url": "https://api.github.com/users/sneakersgames", 361 | "avatar_url": "https://avatars.githubusercontent.com/u/473894?" 362 | }, 363 | "repo": { 364 | "id": 18511024, 365 | "name": "google/web-starter-kit", 366 | "url": "https://api.github.com/repos/google/web-starter-kit" 367 | }, 368 | "payload": { 369 | "action": "started" 370 | }, 371 | "public": true, 372 | "created_at": "2014-08-31T17:25:59Z", 373 | "org": { 374 | "id": 1342004, 375 | "login": "google", 376 | "gravatar_id": "24ba30616d2a20673f54c2aee36d159e", 377 | "url": "https://api.github.com/orgs/google", 378 | "avatar_url": "https://avatars.githubusercontent.com/u/1342004?" 379 | } 380 | }, 381 | { 382 | "id": "2263094394", 383 | "type": "WatchEvent", 384 | "actor": { 385 | "id": 2965242, 386 | "login": "donlion", 387 | "gravatar_id": "92c84dd35ed762d97e4ca2857f402a71", 388 | "url": "https://api.github.com/users/donlion", 389 | "avatar_url": "https://avatars.githubusercontent.com/u/2965242?" 390 | }, 391 | "repo": { 392 | "id": 18511024, 393 | "name": "google/web-starter-kit", 394 | "url": "https://api.github.com/repos/google/web-starter-kit" 395 | }, 396 | "payload": { 397 | "action": "started" 398 | }, 399 | "public": true, 400 | "created_at": "2014-08-31T17:23:22Z", 401 | "org": { 402 | "id": 1342004, 403 | "login": "google", 404 | "gravatar_id": "24ba30616d2a20673f54c2aee36d159e", 405 | "url": "https://api.github.com/orgs/google", 406 | "avatar_url": "https://avatars.githubusercontent.com/u/1342004?" 407 | } 408 | }, 409 | { 410 | "id": "2263081155", 411 | "type": "WatchEvent", 412 | "actor": { 413 | "id": 8014396, 414 | "login": "BrooksPatton", 415 | "gravatar_id": "df5dbd7543098d0249dee0b2a1fd3344", 416 | "url": "https://api.github.com/users/BrooksPatton", 417 | "avatar_url": "https://avatars.githubusercontent.com/u/8014396?" 418 | }, 419 | "repo": { 420 | "id": 18511024, 421 | "name": "google/web-starter-kit", 422 | "url": "https://api.github.com/repos/google/web-starter-kit" 423 | }, 424 | "payload": { 425 | "action": "started" 426 | }, 427 | "public": true, 428 | "created_at": "2014-08-31T16:57:56Z", 429 | "org": { 430 | "id": 1342004, 431 | "login": "google", 432 | "gravatar_id": "24ba30616d2a20673f54c2aee36d159e", 433 | "url": "https://api.github.com/orgs/google", 434 | "avatar_url": "https://avatars.githubusercontent.com/u/1342004?" 435 | } 436 | }, 437 | { 438 | "id": "2263080173", 439 | "type": "WatchEvent", 440 | "actor": { 441 | "id": 6818190, 442 | "login": "RobinDeMug", 443 | "gravatar_id": "03149949a9044d2ddb2ba6639b0cd557", 444 | "url": "https://api.github.com/users/RobinDeMug", 445 | "avatar_url": "https://avatars.githubusercontent.com/u/6818190?" 446 | }, 447 | "repo": { 448 | "id": 12166778, 449 | "name": "google/google-api-php-client", 450 | "url": "https://api.github.com/repos/google/google-api-php-client" 451 | }, 452 | "payload": { 453 | "action": "started" 454 | }, 455 | "public": true, 456 | "created_at": "2014-08-31T16:56:02Z", 457 | "org": { 458 | "id": 1342004, 459 | "login": "google", 460 | "gravatar_id": "24ba30616d2a20673f54c2aee36d159e", 461 | "url": "https://api.github.com/orgs/google", 462 | "avatar_url": "https://avatars.githubusercontent.com/u/1342004?" 463 | } 464 | }, 465 | { 466 | "id": "2263080006", 467 | "type": "WatchEvent", 468 | "actor": { 469 | "id": 2363636, 470 | "login": "mattgstevens", 471 | "gravatar_id": "0333a253d02cb7b56fd26c6b19f00bfa", 472 | "url": "https://api.github.com/users/mattgstevens", 473 | "avatar_url": "https://avatars.githubusercontent.com/u/2363636?" 474 | }, 475 | "repo": { 476 | "id": 18511024, 477 | "name": "google/web-starter-kit", 478 | "url": "https://api.github.com/repos/google/web-starter-kit" 479 | }, 480 | "payload": { 481 | "action": "started" 482 | }, 483 | "public": true, 484 | "created_at": "2014-08-31T16:55:42Z", 485 | "org": { 486 | "id": 1342004, 487 | "login": "google", 488 | "gravatar_id": "24ba30616d2a20673f54c2aee36d159e", 489 | "url": "https://api.github.com/orgs/google", 490 | "avatar_url": "https://avatars.githubusercontent.com/u/1342004?" 491 | } 492 | }, 493 | { 494 | "id": "2263075474", 495 | "type": "ForkEvent", 496 | "actor": { 497 | "id": 2546, 498 | "login": "skalnik", 499 | "gravatar_id": "9eabab0dc683ac153d5b5a44df59424d", 500 | "url": "https://api.github.com/users/skalnik", 501 | "avatar_url": "https://avatars.githubusercontent.com/u/2546?" 502 | }, 503 | "repo": { 504 | "id": 23474151, 505 | "name": "google/recki-ct", 506 | "url": "https://api.github.com/repos/google/recki-ct" 507 | }, 508 | "payload": { 509 | "forkee": { 510 | "id": 23518244, 511 | "name": "recki-ct", 512 | "full_name": "skalnik/recki-ct", 513 | "owner": { 514 | "login": "skalnik", 515 | "id": 2546, 516 | "avatar_url": "https://avatars.githubusercontent.com/u/2546?v=2", 517 | "gravatar_id": "9eabab0dc683ac153d5b5a44df59424d", 518 | "url": "https://api.github.com/users/skalnik", 519 | "html_url": "https://github.com/skalnik", 520 | "followers_url": "https://api.github.com/users/skalnik/followers", 521 | "following_url": "https://api.github.com/users/skalnik/following{/other_user}", 522 | "gists_url": "https://api.github.com/users/skalnik/gists{/gist_id}", 523 | "starred_url": "https://api.github.com/users/skalnik/starred{/owner}{/repo}", 524 | "subscriptions_url": "https://api.github.com/users/skalnik/subscriptions", 525 | "organizations_url": "https://api.github.com/users/skalnik/orgs", 526 | "repos_url": "https://api.github.com/users/skalnik/repos", 527 | "events_url": "https://api.github.com/users/skalnik/events{/privacy}", 528 | "received_events_url": "https://api.github.com/users/skalnik/received_events", 529 | "type": "User", 530 | "site_admin": true 531 | }, 532 | "private": false, 533 | "html_url": "https://github.com/skalnik/recki-ct", 534 | "description": null, 535 | "fork": true, 536 | "url": "https://api.github.com/repos/skalnik/recki-ct", 537 | "forks_url": "https://api.github.com/repos/skalnik/recki-ct/forks", 538 | "keys_url": "https://api.github.com/repos/skalnik/recki-ct/keys{/key_id}", 539 | "collaborators_url": "https://api.github.com/repos/skalnik/recki-ct/collaborators{/collaborator}", 540 | "teams_url": "https://api.github.com/repos/skalnik/recki-ct/teams", 541 | "hooks_url": "https://api.github.com/repos/skalnik/recki-ct/hooks", 542 | "issue_events_url": "https://api.github.com/repos/skalnik/recki-ct/issues/events{/number}", 543 | "events_url": "https://api.github.com/repos/skalnik/recki-ct/events", 544 | "assignees_url": "https://api.github.com/repos/skalnik/recki-ct/assignees{/user}", 545 | "branches_url": "https://api.github.com/repos/skalnik/recki-ct/branches{/branch}", 546 | "tags_url": "https://api.github.com/repos/skalnik/recki-ct/tags", 547 | "blobs_url": "https://api.github.com/repos/skalnik/recki-ct/git/blobs{/sha}", 548 | "git_tags_url": "https://api.github.com/repos/skalnik/recki-ct/git/tags{/sha}", 549 | "git_refs_url": "https://api.github.com/repos/skalnik/recki-ct/git/refs{/sha}", 550 | "trees_url": "https://api.github.com/repos/skalnik/recki-ct/git/trees{/sha}", 551 | "statuses_url": "https://api.github.com/repos/skalnik/recki-ct/statuses/{sha}", 552 | "languages_url": "https://api.github.com/repos/skalnik/recki-ct/languages", 553 | "stargazers_url": "https://api.github.com/repos/skalnik/recki-ct/stargazers", 554 | "contributors_url": "https://api.github.com/repos/skalnik/recki-ct/contributors", 555 | "subscribers_url": "https://api.github.com/repos/skalnik/recki-ct/subscribers", 556 | "subscription_url": "https://api.github.com/repos/skalnik/recki-ct/subscription", 557 | "commits_url": "https://api.github.com/repos/skalnik/recki-ct/commits{/sha}", 558 | "git_commits_url": "https://api.github.com/repos/skalnik/recki-ct/git/commits{/sha}", 559 | "comments_url": "https://api.github.com/repos/skalnik/recki-ct/comments{/number}", 560 | "issue_comment_url": "https://api.github.com/repos/skalnik/recki-ct/issues/comments/{number}", 561 | "contents_url": "https://api.github.com/repos/skalnik/recki-ct/contents/{+path}", 562 | "compare_url": "https://api.github.com/repos/skalnik/recki-ct/compare/{base}...{head}", 563 | "merges_url": "https://api.github.com/repos/skalnik/recki-ct/merges", 564 | "archive_url": "https://api.github.com/repos/skalnik/recki-ct/{archive_format}{/ref}", 565 | "downloads_url": "https://api.github.com/repos/skalnik/recki-ct/downloads", 566 | "issues_url": "https://api.github.com/repos/skalnik/recki-ct/issues{/number}", 567 | "pulls_url": "https://api.github.com/repos/skalnik/recki-ct/pulls{/number}", 568 | "milestones_url": "https://api.github.com/repos/skalnik/recki-ct/milestones{/number}", 569 | "notifications_url": "https://api.github.com/repos/skalnik/recki-ct/notifications{?since,all,participating}", 570 | "labels_url": "https://api.github.com/repos/skalnik/recki-ct/labels{/name}", 571 | "releases_url": "https://api.github.com/repos/skalnik/recki-ct/releases{/id}", 572 | "created_at": "2014-08-31T16:47:14Z", 573 | "updated_at": "2014-08-31T16:16:43Z", 574 | "pushed_at": "2014-08-29T21:16:46Z", 575 | "git_url": "git://github.com/skalnik/recki-ct.git", 576 | "ssh_url": "git@github.com:skalnik/recki-ct.git", 577 | "clone_url": "https://github.com/skalnik/recki-ct.git", 578 | "svn_url": "https://github.com/skalnik/recki-ct", 579 | "homepage": null, 580 | "size": 0, 581 | "stargazers_count": 0, 582 | "watchers_count": 0, 583 | "language": null, 584 | "has_issues": false, 585 | "has_downloads": false, 586 | "has_wiki": false, 587 | "forks_count": 0, 588 | "mirror_url": null, 589 | "open_issues_count": 0, 590 | "forks": 0, 591 | "open_issues": 0, 592 | "watchers": 0, 593 | "default_branch": "master", 594 | "public": true 595 | } 596 | }, 597 | "public": true, 598 | "created_at": "2014-08-31T16:47:14Z", 599 | "org": { 600 | "id": 1342004, 601 | "login": "google", 602 | "gravatar_id": "24ba30616d2a20673f54c2aee36d159e", 603 | "url": "https://api.github.com/orgs/google", 604 | "avatar_url": "https://avatars.githubusercontent.com/u/1342004?" 605 | } 606 | }, 607 | { 608 | "id": "2263068042", 609 | "type": "WatchEvent", 610 | "actor": { 611 | "id": 919444, 612 | "login": "Valloric", 613 | "gravatar_id": "48f306c887fd9c7b32a2fc352194e0db", 614 | "url": "https://api.github.com/users/Valloric", 615 | "avatar_url": "https://avatars.githubusercontent.com/u/919444?" 616 | }, 617 | "repo": { 618 | "id": 18511024, 619 | "name": "google/web-starter-kit", 620 | "url": "https://api.github.com/repos/google/web-starter-kit" 621 | }, 622 | "payload": { 623 | "action": "started" 624 | }, 625 | "public": true, 626 | "created_at": "2014-08-31T16:32:13Z", 627 | "org": { 628 | "id": 1342004, 629 | "login": "google", 630 | "gravatar_id": "24ba30616d2a20673f54c2aee36d159e", 631 | "url": "https://api.github.com/orgs/google", 632 | "avatar_url": "https://avatars.githubusercontent.com/u/1342004?" 633 | } 634 | }, 635 | { 636 | "id": "2263063121", 637 | "type": "IssuesEvent", 638 | "actor": { 639 | "id": 1727302, 640 | "login": "pflannery", 641 | "gravatar_id": "7f2a377aaeb6cea73145b33975a030b1", 642 | "url": "https://api.github.com/users/pflannery", 643 | "avatar_url": "https://avatars.githubusercontent.com/u/1727302?" 644 | }, 645 | "repo": { 646 | "id": 9060347, 647 | "name": "google/traceur-compiler", 648 | "url": "https://api.github.com/repos/google/traceur-compiler" 649 | }, 650 | "payload": { 651 | "action": "closed", 652 | "issue": { 653 | "url": "https://api.github.com/repos/google/traceur-compiler/issues/1300", 654 | "labels_url": "https://api.github.com/repos/google/traceur-compiler/issues/1300/labels{/name}", 655 | "comments_url": "https://api.github.com/repos/google/traceur-compiler/issues/1300/comments", 656 | "events_url": "https://api.github.com/repos/google/traceur-compiler/issues/1300/events", 657 | "html_url": "https://github.com/google/traceur-compiler/issues/1300", 658 | "id": 41589319, 659 | "number": 1300, 660 | "title": "module myModule as './path/to/module'; outputs \"Semi-colon expected\" error", 661 | "user": { 662 | "login": "pflannery", 663 | "id": 1727302, 664 | "avatar_url": "https://avatars.githubusercontent.com/u/1727302?v=2", 665 | "gravatar_id": "7f2a377aaeb6cea73145b33975a030b1", 666 | "url": "https://api.github.com/users/pflannery", 667 | "html_url": "https://github.com/pflannery", 668 | "followers_url": "https://api.github.com/users/pflannery/followers", 669 | "following_url": "https://api.github.com/users/pflannery/following{/other_user}", 670 | "gists_url": "https://api.github.com/users/pflannery/gists{/gist_id}", 671 | "starred_url": "https://api.github.com/users/pflannery/starred{/owner}{/repo}", 672 | "subscriptions_url": "https://api.github.com/users/pflannery/subscriptions", 673 | "organizations_url": "https://api.github.com/users/pflannery/orgs", 674 | "repos_url": "https://api.github.com/users/pflannery/repos", 675 | "events_url": "https://api.github.com/users/pflannery/events{/privacy}", 676 | "received_events_url": "https://api.github.com/users/pflannery/received_events", 677 | "type": "User", 678 | "site_admin": false 679 | }, 680 | "labels": [ 681 | 682 | ], 683 | "state": "closed", 684 | "locked": false, 685 | "assignee": null, 686 | "milestone": null, 687 | "comments": 2, 688 | "created_at": "2014-08-31T15:49:04Z", 689 | "updated_at": "2014-08-31T16:22:51Z", 690 | "closed_at": "2014-08-31T16:22:51Z", 691 | "body": "I've been trying to bring in a module as a single object using \r\n```js\r\nmodule myModule as './path/to/module';\r\n```\r\nbut I get the following error ```Semi-colon expected```\r\n\r\nIs this because it's not implemented yet or am I doing something wrong?\r\n\r\nmy current work around is to replace the line with \r\n```js\r\nvar myModule = System.get(\"relative/path/to/module\");\r\n```" 692 | } 693 | }, 694 | "public": true, 695 | "created_at": "2014-08-31T16:22:51Z", 696 | "org": { 697 | "id": 1342004, 698 | "login": "google", 699 | "gravatar_id": "24ba30616d2a20673f54c2aee36d159e", 700 | "url": "https://api.github.com/orgs/google", 701 | "avatar_url": "https://avatars.githubusercontent.com/u/1342004?" 702 | } 703 | }, 704 | { 705 | "id": "2263063120", 706 | "type": "IssueCommentEvent", 707 | "actor": { 708 | "id": 1727302, 709 | "login": "pflannery", 710 | "gravatar_id": "7f2a377aaeb6cea73145b33975a030b1", 711 | "url": "https://api.github.com/users/pflannery", 712 | "avatar_url": "https://avatars.githubusercontent.com/u/1727302?" 713 | }, 714 | "repo": { 715 | "id": 9060347, 716 | "name": "google/traceur-compiler", 717 | "url": "https://api.github.com/repos/google/traceur-compiler" 718 | }, 719 | "payload": { 720 | "action": "created", 721 | "issue": { 722 | "url": "https://api.github.com/repos/google/traceur-compiler/issues/1300", 723 | "labels_url": "https://api.github.com/repos/google/traceur-compiler/issues/1300/labels{/name}", 724 | "comments_url": "https://api.github.com/repos/google/traceur-compiler/issues/1300/comments", 725 | "events_url": "https://api.github.com/repos/google/traceur-compiler/issues/1300/events", 726 | "html_url": "https://github.com/google/traceur-compiler/issues/1300", 727 | "id": 41589319, 728 | "number": 1300, 729 | "title": "module myModule as './path/to/module'; outputs \"Semi-colon expected\" error", 730 | "user": { 731 | "login": "pflannery", 732 | "id": 1727302, 733 | "avatar_url": "https://avatars.githubusercontent.com/u/1727302?v=2", 734 | "gravatar_id": "7f2a377aaeb6cea73145b33975a030b1", 735 | "url": "https://api.github.com/users/pflannery", 736 | "html_url": "https://github.com/pflannery", 737 | "followers_url": "https://api.github.com/users/pflannery/followers", 738 | "following_url": "https://api.github.com/users/pflannery/following{/other_user}", 739 | "gists_url": "https://api.github.com/users/pflannery/gists{/gist_id}", 740 | "starred_url": "https://api.github.com/users/pflannery/starred{/owner}{/repo}", 741 | "subscriptions_url": "https://api.github.com/users/pflannery/subscriptions", 742 | "organizations_url": "https://api.github.com/users/pflannery/orgs", 743 | "repos_url": "https://api.github.com/users/pflannery/repos", 744 | "events_url": "https://api.github.com/users/pflannery/events{/privacy}", 745 | "received_events_url": "https://api.github.com/users/pflannery/received_events", 746 | "type": "User", 747 | "site_admin": false 748 | }, 749 | "labels": [ 750 | 751 | ], 752 | "state": "closed", 753 | "locked": false, 754 | "assignee": null, 755 | "milestone": null, 756 | "comments": 2, 757 | "created_at": "2014-08-31T15:49:04Z", 758 | "updated_at": "2014-08-31T16:22:51Z", 759 | "closed_at": "2014-08-31T16:22:51Z", 760 | "body": "I've been trying to bring in a module as a single object using \r\n```js\r\nmodule myModule as './path/to/module';\r\n```\r\nbut I get the following error ```Semi-colon expected```\r\n\r\nIs this because it's not implemented yet or am I doing something wrong?\r\n\r\nmy current work around is to replace the line with \r\n```js\r\nvar myModule = System.get(\"relative/path/to/module\");\r\n```" 761 | }, 762 | "comment": { 763 | "url": "https://api.github.com/repos/google/traceur-compiler/issues/comments/53992985", 764 | "html_url": "https://github.com/google/traceur-compiler/issues/1300#issuecomment-53992985", 765 | "issue_url": "https://api.github.com/repos/google/traceur-compiler/issues/1300", 766 | "id": 53992985, 767 | "user": { 768 | "login": "pflannery", 769 | "id": 1727302, 770 | "avatar_url": "https://avatars.githubusercontent.com/u/1727302?v=2", 771 | "gravatar_id": "7f2a377aaeb6cea73145b33975a030b1", 772 | "url": "https://api.github.com/users/pflannery", 773 | "html_url": "https://github.com/pflannery", 774 | "followers_url": "https://api.github.com/users/pflannery/followers", 775 | "following_url": "https://api.github.com/users/pflannery/following{/other_user}", 776 | "gists_url": "https://api.github.com/users/pflannery/gists{/gist_id}", 777 | "starred_url": "https://api.github.com/users/pflannery/starred{/owner}{/repo}", 778 | "subscriptions_url": "https://api.github.com/users/pflannery/subscriptions", 779 | "organizations_url": "https://api.github.com/users/pflannery/orgs", 780 | "repos_url": "https://api.github.com/users/pflannery/repos", 781 | "events_url": "https://api.github.com/users/pflannery/events{/privacy}", 782 | "received_events_url": "https://api.github.com/users/pflannery/received_events", 783 | "type": "User", 784 | "site_admin": false 785 | }, 786 | "created_at": "2014-08-31T16:22:51Z", 787 | "updated_at": "2014-08-31T16:22:51Z", 788 | "body": "many places refer to the older syntax.\r\n\r\nbut great that works thanks." 789 | } 790 | }, 791 | "public": true, 792 | "created_at": "2014-08-31T16:22:51Z", 793 | "org": { 794 | "id": 1342004, 795 | "login": "google", 796 | "gravatar_id": "24ba30616d2a20673f54c2aee36d159e", 797 | "url": "https://api.github.com/orgs/google", 798 | "avatar_url": "https://avatars.githubusercontent.com/u/1342004?" 799 | } 800 | }, 801 | { 802 | "id": "2263059840", 803 | "type": "WatchEvent", 804 | "actor": { 805 | "id": 530406, 806 | "login": "liuggio", 807 | "gravatar_id": "662a52953053d94a145a00df9da9f0d2", 808 | "url": "https://api.github.com/users/liuggio", 809 | "avatar_url": "https://avatars.githubusercontent.com/u/530406?" 810 | }, 811 | "repo": { 812 | "id": 23474151, 813 | "name": "google/recki-ct", 814 | "url": "https://api.github.com/repos/google/recki-ct" 815 | }, 816 | "payload": { 817 | "action": "started" 818 | }, 819 | "public": true, 820 | "created_at": "2014-08-31T16:16:44Z", 821 | "org": { 822 | "id": 1342004, 823 | "login": "google", 824 | "gravatar_id": "24ba30616d2a20673f54c2aee36d159e", 825 | "url": "https://api.github.com/orgs/google", 826 | "avatar_url": "https://avatars.githubusercontent.com/u/1342004?" 827 | } 828 | }, 829 | { 830 | "id": "2263054492", 831 | "type": "WatchEvent", 832 | "actor": { 833 | "id": 7867821, 834 | "login": "mykhaelpierce", 835 | "gravatar_id": "d3ca7f5a26249dc2b0e80bf4bf5fe14b", 836 | "url": "https://api.github.com/users/mykhaelpierce", 837 | "avatar_url": "https://avatars.githubusercontent.com/u/7867821?" 838 | }, 839 | "repo": { 840 | "id": 18511024, 841 | "name": "google/web-starter-kit", 842 | "url": "https://api.github.com/repos/google/web-starter-kit" 843 | }, 844 | "payload": { 845 | "action": "started" 846 | }, 847 | "public": true, 848 | "created_at": "2014-08-31T16:06:36Z", 849 | "org": { 850 | "id": 1342004, 851 | "login": "google", 852 | "gravatar_id": "24ba30616d2a20673f54c2aee36d159e", 853 | "url": "https://api.github.com/orgs/google", 854 | "avatar_url": "https://avatars.githubusercontent.com/u/1342004?" 855 | } 856 | }, 857 | { 858 | "id": "2263054490", 859 | "type": "PullRequestEvent", 860 | "actor": { 861 | "id": 1078123, 862 | "login": "plnice", 863 | "gravatar_id": "5840be9b646085a32e785c3ea07125ef", 864 | "url": "https://api.github.com/users/plnice", 865 | "avatar_url": "https://avatars.githubusercontent.com/u/1078123?" 866 | }, 867 | "repo": { 868 | "id": 18347476, 869 | "name": "google/iosched", 870 | "url": "https://api.github.com/repos/google/iosched" 871 | }, 872 | "payload": { 873 | "action": "opened", 874 | "number": 47, 875 | "pull_request": { 876 | "url": "https://api.github.com/repos/google/iosched/pulls/47", 877 | "id": 20546887, 878 | "html_url": "https://github.com/google/iosched/pull/47", 879 | "diff_url": "https://github.com/google/iosched/pull/47.diff", 880 | "patch_url": "https://github.com/google/iosched/pull/47.patch", 881 | "issue_url": "https://api.github.com/repos/google/iosched/issues/47", 882 | "number": 47, 883 | "state": "open", 884 | "locked": false, 885 | "title": "Wearable: fixes for feedback notification.", 886 | "user": { 887 | "login": "plnice", 888 | "id": 1078123, 889 | "avatar_url": "https://avatars.githubusercontent.com/u/1078123?v=2", 890 | "gravatar_id": "5840be9b646085a32e785c3ea07125ef", 891 | "url": "https://api.github.com/users/plnice", 892 | "html_url": "https://github.com/plnice", 893 | "followers_url": "https://api.github.com/users/plnice/followers", 894 | "following_url": "https://api.github.com/users/plnice/following{/other_user}", 895 | "gists_url": "https://api.github.com/users/plnice/gists{/gist_id}", 896 | "starred_url": "https://api.github.com/users/plnice/starred{/owner}{/repo}", 897 | "subscriptions_url": "https://api.github.com/users/plnice/subscriptions", 898 | "organizations_url": "https://api.github.com/users/plnice/orgs", 899 | "repos_url": "https://api.github.com/users/plnice/repos", 900 | "events_url": "https://api.github.com/users/plnice/events{/privacy}", 901 | "received_events_url": "https://api.github.com/users/plnice/received_events", 902 | "type": "User", 903 | "site_admin": false 904 | }, 905 | "body": "* Moved \"Rate this session\" action from notification Activity's onClick to the notification Action - I think this is more intuitive and in line with Wear platform; additionally shows how to add actions to the Wear notification.\r\n* Set custom size preset for the feedback notification (SIZE_LARGE). Before that, in case of session title in two lines, room number and speaker name were not visible.", 906 | "created_at": "2014-08-31T16:06:33Z", 907 | "updated_at": "2014-08-31T16:06:33Z", 908 | "closed_at": null, 909 | "merged_at": null, 910 | "merge_commit_sha": "4986639dff646add7014345dbb68b0861003fe23", 911 | "assignee": null, 912 | "milestone": null, 913 | "commits_url": "https://api.github.com/repos/google/iosched/pulls/47/commits", 914 | "review_comments_url": "https://api.github.com/repos/google/iosched/pulls/47/comments", 915 | "review_comment_url": "https://api.github.com/repos/google/iosched/pulls/comments/{number}", 916 | "comments_url": "https://api.github.com/repos/google/iosched/issues/47/comments", 917 | "statuses_url": "https://api.github.com/repos/google/iosched/statuses/ea244ed4d2664370a9975b4008eb7b3071a8bf29", 918 | "head": { 919 | "label": "plnice:gdg-wear-improv", 920 | "ref": "gdg-wear-improv", 921 | "sha": "ea244ed4d2664370a9975b4008eb7b3071a8bf29", 922 | "user": { 923 | "login": "plnice", 924 | "id": 1078123, 925 | "avatar_url": "https://avatars.githubusercontent.com/u/1078123?v=2", 926 | "gravatar_id": "5840be9b646085a32e785c3ea07125ef", 927 | "url": "https://api.github.com/users/plnice", 928 | "html_url": "https://github.com/plnice", 929 | "followers_url": "https://api.github.com/users/plnice/followers", 930 | "following_url": "https://api.github.com/users/plnice/following{/other_user}", 931 | "gists_url": "https://api.github.com/users/plnice/gists{/gist_id}", 932 | "starred_url": "https://api.github.com/users/plnice/starred{/owner}{/repo}", 933 | "subscriptions_url": "https://api.github.com/users/plnice/subscriptions", 934 | "organizations_url": "https://api.github.com/users/plnice/orgs", 935 | "repos_url": "https://api.github.com/users/plnice/repos", 936 | "events_url": "https://api.github.com/users/plnice/events{/privacy}", 937 | "received_events_url": "https://api.github.com/users/plnice/received_events", 938 | "type": "User", 939 | "site_admin": false 940 | }, 941 | "repo": { 942 | "id": 22748955, 943 | "name": "iosched", 944 | "full_name": "plnice/iosched", 945 | "owner": { 946 | "login": "plnice", 947 | "id": 1078123, 948 | "avatar_url": "https://avatars.githubusercontent.com/u/1078123?v=2", 949 | "gravatar_id": "5840be9b646085a32e785c3ea07125ef", 950 | "url": "https://api.github.com/users/plnice", 951 | "html_url": "https://github.com/plnice", 952 | "followers_url": "https://api.github.com/users/plnice/followers", 953 | "following_url": "https://api.github.com/users/plnice/following{/other_user}", 954 | "gists_url": "https://api.github.com/users/plnice/gists{/gist_id}", 955 | "starred_url": "https://api.github.com/users/plnice/starred{/owner}{/repo}", 956 | "subscriptions_url": "https://api.github.com/users/plnice/subscriptions", 957 | "organizations_url": "https://api.github.com/users/plnice/orgs", 958 | "repos_url": "https://api.github.com/users/plnice/repos", 959 | "events_url": "https://api.github.com/users/plnice/events{/privacy}", 960 | "received_events_url": "https://api.github.com/users/plnice/received_events", 961 | "type": "User", 962 | "site_admin": false 963 | }, 964 | "private": false, 965 | "html_url": "https://github.com/plnice/iosched", 966 | "description": null, 967 | "fork": true, 968 | "url": "https://api.github.com/repos/plnice/iosched", 969 | "forks_url": "https://api.github.com/repos/plnice/iosched/forks", 970 | "keys_url": "https://api.github.com/repos/plnice/iosched/keys{/key_id}", 971 | "collaborators_url": "https://api.github.com/repos/plnice/iosched/collaborators{/collaborator}", 972 | "teams_url": "https://api.github.com/repos/plnice/iosched/teams", 973 | "hooks_url": "https://api.github.com/repos/plnice/iosched/hooks", 974 | "issue_events_url": "https://api.github.com/repos/plnice/iosched/issues/events{/number}", 975 | "events_url": "https://api.github.com/repos/plnice/iosched/events", 976 | "assignees_url": "https://api.github.com/repos/plnice/iosched/assignees{/user}", 977 | "branches_url": "https://api.github.com/repos/plnice/iosched/branches{/branch}", 978 | "tags_url": "https://api.github.com/repos/plnice/iosched/tags", 979 | "blobs_url": "https://api.github.com/repos/plnice/iosched/git/blobs{/sha}", 980 | "git_tags_url": "https://api.github.com/repos/plnice/iosched/git/tags{/sha}", 981 | "git_refs_url": "https://api.github.com/repos/plnice/iosched/git/refs{/sha}", 982 | "trees_url": "https://api.github.com/repos/plnice/iosched/git/trees{/sha}", 983 | "statuses_url": "https://api.github.com/repos/plnice/iosched/statuses/{sha}", 984 | "languages_url": "https://api.github.com/repos/plnice/iosched/languages", 985 | "stargazers_url": "https://api.github.com/repos/plnice/iosched/stargazers", 986 | "contributors_url": "https://api.github.com/repos/plnice/iosched/contributors", 987 | "subscribers_url": "https://api.github.com/repos/plnice/iosched/subscribers", 988 | "subscription_url": "https://api.github.com/repos/plnice/iosched/subscription", 989 | "commits_url": "https://api.github.com/repos/plnice/iosched/commits{/sha}", 990 | "git_commits_url": "https://api.github.com/repos/plnice/iosched/git/commits{/sha}", 991 | "comments_url": "https://api.github.com/repos/plnice/iosched/comments{/number}", 992 | "issue_comment_url": "https://api.github.com/repos/plnice/iosched/issues/comments/{number}", 993 | "contents_url": "https://api.github.com/repos/plnice/iosched/contents/{+path}", 994 | "compare_url": "https://api.github.com/repos/plnice/iosched/compare/{base}...{head}", 995 | "merges_url": "https://api.github.com/repos/plnice/iosched/merges", 996 | "archive_url": "https://api.github.com/repos/plnice/iosched/{archive_format}{/ref}", 997 | "downloads_url": "https://api.github.com/repos/plnice/iosched/downloads", 998 | "issues_url": "https://api.github.com/repos/plnice/iosched/issues{/number}", 999 | "pulls_url": "https://api.github.com/repos/plnice/iosched/pulls{/number}", 1000 | "milestones_url": "https://api.github.com/repos/plnice/iosched/milestones{/number}", 1001 | "notifications_url": "https://api.github.com/repos/plnice/iosched/notifications{?since,all,participating}", 1002 | "labels_url": "https://api.github.com/repos/plnice/iosched/labels{/name}", 1003 | "releases_url": "https://api.github.com/repos/plnice/iosched/releases{/id}", 1004 | "created_at": "2014-08-08T07:01:19Z", 1005 | "updated_at": "2014-08-23T14:50:29Z", 1006 | "pushed_at": "2014-08-24T13:37:03Z", 1007 | "git_url": "git://github.com/plnice/iosched.git", 1008 | "ssh_url": "git@github.com:plnice/iosched.git", 1009 | "clone_url": "https://github.com/plnice/iosched.git", 1010 | "svn_url": "https://github.com/plnice/iosched", 1011 | "homepage": null, 1012 | "size": 18439, 1013 | "stargazers_count": 0, 1014 | "watchers_count": 0, 1015 | "language": "Java", 1016 | "has_issues": false, 1017 | "has_downloads": true, 1018 | "has_wiki": false, 1019 | "forks_count": 0, 1020 | "mirror_url": null, 1021 | "open_issues_count": 0, 1022 | "forks": 0, 1023 | "open_issues": 0, 1024 | "watchers": 0, 1025 | "default_branch": "master" 1026 | } 1027 | }, 1028 | "base": { 1029 | "label": "google:master", 1030 | "ref": "master", 1031 | "sha": "73dd4d3fbd80a336abb594e29b152dfc4134b239", 1032 | "user": { 1033 | "login": "google", 1034 | "id": 1342004, 1035 | "avatar_url": "https://avatars.githubusercontent.com/u/1342004?v=2", 1036 | "gravatar_id": "24ba30616d2a20673f54c2aee36d159e", 1037 | "url": "https://api.github.com/users/google", 1038 | "html_url": "https://github.com/google", 1039 | "followers_url": "https://api.github.com/users/google/followers", 1040 | "following_url": "https://api.github.com/users/google/following{/other_user}", 1041 | "gists_url": "https://api.github.com/users/google/gists{/gist_id}", 1042 | "starred_url": "https://api.github.com/users/google/starred{/owner}{/repo}", 1043 | "subscriptions_url": "https://api.github.com/users/google/subscriptions", 1044 | "organizations_url": "https://api.github.com/users/google/orgs", 1045 | "repos_url": "https://api.github.com/users/google/repos", 1046 | "events_url": "https://api.github.com/users/google/events{/privacy}", 1047 | "received_events_url": "https://api.github.com/users/google/received_events", 1048 | "type": "Organization", 1049 | "site_admin": false 1050 | }, 1051 | "repo": { 1052 | "id": 18347476, 1053 | "name": "iosched", 1054 | "full_name": "google/iosched", 1055 | "owner": { 1056 | "login": "google", 1057 | "id": 1342004, 1058 | "avatar_url": "https://avatars.githubusercontent.com/u/1342004?v=2", 1059 | "gravatar_id": "24ba30616d2a20673f54c2aee36d159e", 1060 | "url": "https://api.github.com/users/google", 1061 | "html_url": "https://github.com/google", 1062 | "followers_url": "https://api.github.com/users/google/followers", 1063 | "following_url": "https://api.github.com/users/google/following{/other_user}", 1064 | "gists_url": "https://api.github.com/users/google/gists{/gist_id}", 1065 | "starred_url": "https://api.github.com/users/google/starred{/owner}{/repo}", 1066 | "subscriptions_url": "https://api.github.com/users/google/subscriptions", 1067 | "organizations_url": "https://api.github.com/users/google/orgs", 1068 | "repos_url": "https://api.github.com/users/google/repos", 1069 | "events_url": "https://api.github.com/users/google/events{/privacy}", 1070 | "received_events_url": "https://api.github.com/users/google/received_events", 1071 | "type": "Organization", 1072 | "site_admin": false 1073 | }, 1074 | "private": false, 1075 | "html_url": "https://github.com/google/iosched", 1076 | "description": null, 1077 | "fork": false, 1078 | "url": "https://api.github.com/repos/google/iosched", 1079 | "forks_url": "https://api.github.com/repos/google/iosched/forks", 1080 | "keys_url": "https://api.github.com/repos/google/iosched/keys{/key_id}", 1081 | "collaborators_url": "https://api.github.com/repos/google/iosched/collaborators{/collaborator}", 1082 | "teams_url": "https://api.github.com/repos/google/iosched/teams", 1083 | "hooks_url": "https://api.github.com/repos/google/iosched/hooks", 1084 | "issue_events_url": "https://api.github.com/repos/google/iosched/issues/events{/number}", 1085 | "events_url": "https://api.github.com/repos/google/iosched/events", 1086 | "assignees_url": "https://api.github.com/repos/google/iosched/assignees{/user}", 1087 | "branches_url": "https://api.github.com/repos/google/iosched/branches{/branch}", 1088 | "tags_url": "https://api.github.com/repos/google/iosched/tags", 1089 | "blobs_url": "https://api.github.com/repos/google/iosched/git/blobs{/sha}", 1090 | "git_tags_url": "https://api.github.com/repos/google/iosched/git/tags{/sha}", 1091 | "git_refs_url": "https://api.github.com/repos/google/iosched/git/refs{/sha}", 1092 | "trees_url": "https://api.github.com/repos/google/iosched/git/trees{/sha}", 1093 | "statuses_url": "https://api.github.com/repos/google/iosched/statuses/{sha}", 1094 | "languages_url": "https://api.github.com/repos/google/iosched/languages", 1095 | "stargazers_url": "https://api.github.com/repos/google/iosched/stargazers", 1096 | "contributors_url": "https://api.github.com/repos/google/iosched/contributors", 1097 | "subscribers_url": "https://api.github.com/repos/google/iosched/subscribers", 1098 | "subscription_url": "https://api.github.com/repos/google/iosched/subscription", 1099 | "commits_url": "https://api.github.com/repos/google/iosched/commits{/sha}", 1100 | "git_commits_url": "https://api.github.com/repos/google/iosched/git/commits{/sha}", 1101 | "comments_url": "https://api.github.com/repos/google/iosched/comments{/number}", 1102 | "issue_comment_url": "https://api.github.com/repos/google/iosched/issues/comments/{number}", 1103 | "contents_url": "https://api.github.com/repos/google/iosched/contents/{+path}", 1104 | "compare_url": "https://api.github.com/repos/google/iosched/compare/{base}...{head}", 1105 | "merges_url": "https://api.github.com/repos/google/iosched/merges", 1106 | "archive_url": "https://api.github.com/repos/google/iosched/{archive_format}{/ref}", 1107 | "downloads_url": "https://api.github.com/repos/google/iosched/downloads", 1108 | "issues_url": "https://api.github.com/repos/google/iosched/issues{/number}", 1109 | "pulls_url": "https://api.github.com/repos/google/iosched/pulls{/number}", 1110 | "milestones_url": "https://api.github.com/repos/google/iosched/milestones{/number}", 1111 | "notifications_url": "https://api.github.com/repos/google/iosched/notifications{?since,all,participating}", 1112 | "labels_url": "https://api.github.com/repos/google/iosched/labels{/name}", 1113 | "releases_url": "https://api.github.com/repos/google/iosched/releases{/id}", 1114 | "created_at": "2014-04-01T22:40:40Z", 1115 | "updated_at": "2014-08-31T13:58:24Z", 1116 | "pushed_at": "2014-08-19T18:34:02Z", 1117 | "git_url": "git://github.com/google/iosched.git", 1118 | "ssh_url": "git@github.com:google/iosched.git", 1119 | "clone_url": "https://github.com/google/iosched.git", 1120 | "svn_url": "https://github.com/google/iosched", 1121 | "homepage": null, 1122 | "size": 19325, 1123 | "stargazers_count": 4049, 1124 | "watchers_count": 4049, 1125 | "language": "Java", 1126 | "has_issues": true, 1127 | "has_downloads": true, 1128 | "has_wiki": false, 1129 | "forks_count": 1195, 1130 | "mirror_url": null, 1131 | "open_issues_count": 12, 1132 | "forks": 1195, 1133 | "open_issues": 12, 1134 | "watchers": 4049, 1135 | "default_branch": "master" 1136 | } 1137 | }, 1138 | "_links": { 1139 | "self": { 1140 | "href": "https://api.github.com/repos/google/iosched/pulls/47" 1141 | }, 1142 | "html": { 1143 | "href": "https://github.com/google/iosched/pull/47" 1144 | }, 1145 | "issue": { 1146 | "href": "https://api.github.com/repos/google/iosched/issues/47" 1147 | }, 1148 | "comments": { 1149 | "href": "https://api.github.com/repos/google/iosched/issues/47/comments" 1150 | }, 1151 | "review_comments": { 1152 | "href": "https://api.github.com/repos/google/iosched/pulls/47/comments" 1153 | }, 1154 | "review_comment": { 1155 | "href": "https://api.github.com/repos/google/iosched/pulls/comments/{number}" 1156 | }, 1157 | "commits": { 1158 | "href": "https://api.github.com/repos/google/iosched/pulls/47/commits" 1159 | }, 1160 | "statuses": { 1161 | "href": "https://api.github.com/repos/google/iosched/statuses/ea244ed4d2664370a9975b4008eb7b3071a8bf29" 1162 | } 1163 | }, 1164 | "merged": false, 1165 | "mergeable": true, 1166 | "mergeable_state": "clean", 1167 | "merged_by": null, 1168 | "comments": 0, 1169 | "review_comments": 0, 1170 | "commits": 2, 1171 | "additions": 12, 1172 | "deletions": 19, 1173 | "changed_files": 3 1174 | } 1175 | }, 1176 | "public": true, 1177 | "created_at": "2014-08-31T16:06:35Z", 1178 | "org": { 1179 | "id": 1342004, 1180 | "login": "google", 1181 | "gravatar_id": "24ba30616d2a20673f54c2aee36d159e", 1182 | "url": "https://api.github.com/orgs/google", 1183 | "avatar_url": "https://avatars.githubusercontent.com/u/1342004?" 1184 | } 1185 | }, 1186 | { 1187 | "id": "2263053661", 1188 | "type": "ForkEvent", 1189 | "actor": { 1190 | "id": 4126534, 1191 | "login": "ALEXGUOQ", 1192 | "gravatar_id": "04ab7fb03094abb92db66880059286aa", 1193 | "url": "https://api.github.com/users/ALEXGUOQ", 1194 | "avatar_url": "https://avatars.githubusercontent.com/u/4126534?" 1195 | }, 1196 | "repo": { 1197 | "id": 18347476, 1198 | "name": "google/iosched", 1199 | "url": "https://api.github.com/repos/google/iosched" 1200 | }, 1201 | "payload": { 1202 | "forkee": { 1203 | "id": 23517329, 1204 | "name": "iosched", 1205 | "full_name": "ALEXGUOQ/iosched", 1206 | "owner": { 1207 | "login": "ALEXGUOQ", 1208 | "id": 4126534, 1209 | "avatar_url": "https://avatars.githubusercontent.com/u/4126534?v=2", 1210 | "gravatar_id": "04ab7fb03094abb92db66880059286aa", 1211 | "url": "https://api.github.com/users/ALEXGUOQ", 1212 | "html_url": "https://github.com/ALEXGUOQ", 1213 | "followers_url": "https://api.github.com/users/ALEXGUOQ/followers", 1214 | "following_url": "https://api.github.com/users/ALEXGUOQ/following{/other_user}", 1215 | "gists_url": "https://api.github.com/users/ALEXGUOQ/gists{/gist_id}", 1216 | "starred_url": "https://api.github.com/users/ALEXGUOQ/starred{/owner}{/repo}", 1217 | "subscriptions_url": "https://api.github.com/users/ALEXGUOQ/subscriptions", 1218 | "organizations_url": "https://api.github.com/users/ALEXGUOQ/orgs", 1219 | "repos_url": "https://api.github.com/users/ALEXGUOQ/repos", 1220 | "events_url": "https://api.github.com/users/ALEXGUOQ/events{/privacy}", 1221 | "received_events_url": "https://api.github.com/users/ALEXGUOQ/received_events", 1222 | "type": "User", 1223 | "site_admin": false 1224 | }, 1225 | "private": false, 1226 | "html_url": "https://github.com/ALEXGUOQ/iosched", 1227 | "description": null, 1228 | "fork": true, 1229 | "url": "https://api.github.com/repos/ALEXGUOQ/iosched", 1230 | "forks_url": "https://api.github.com/repos/ALEXGUOQ/iosched/forks", 1231 | "keys_url": "https://api.github.com/repos/ALEXGUOQ/iosched/keys{/key_id}", 1232 | "collaborators_url": "https://api.github.com/repos/ALEXGUOQ/iosched/collaborators{/collaborator}", 1233 | "teams_url": "https://api.github.com/repos/ALEXGUOQ/iosched/teams", 1234 | "hooks_url": "https://api.github.com/repos/ALEXGUOQ/iosched/hooks", 1235 | "issue_events_url": "https://api.github.com/repos/ALEXGUOQ/iosched/issues/events{/number}", 1236 | "events_url": "https://api.github.com/repos/ALEXGUOQ/iosched/events", 1237 | "assignees_url": "https://api.github.com/repos/ALEXGUOQ/iosched/assignees{/user}", 1238 | "branches_url": "https://api.github.com/repos/ALEXGUOQ/iosched/branches{/branch}", 1239 | "tags_url": "https://api.github.com/repos/ALEXGUOQ/iosched/tags", 1240 | "blobs_url": "https://api.github.com/repos/ALEXGUOQ/iosched/git/blobs{/sha}", 1241 | "git_tags_url": "https://api.github.com/repos/ALEXGUOQ/iosched/git/tags{/sha}", 1242 | "git_refs_url": "https://api.github.com/repos/ALEXGUOQ/iosched/git/refs{/sha}", 1243 | "trees_url": "https://api.github.com/repos/ALEXGUOQ/iosched/git/trees{/sha}", 1244 | "statuses_url": "https://api.github.com/repos/ALEXGUOQ/iosched/statuses/{sha}", 1245 | "languages_url": "https://api.github.com/repos/ALEXGUOQ/iosched/languages", 1246 | "stargazers_url": "https://api.github.com/repos/ALEXGUOQ/iosched/stargazers", 1247 | "contributors_url": "https://api.github.com/repos/ALEXGUOQ/iosched/contributors", 1248 | "subscribers_url": "https://api.github.com/repos/ALEXGUOQ/iosched/subscribers", 1249 | "subscription_url": "https://api.github.com/repos/ALEXGUOQ/iosched/subscription", 1250 | "commits_url": "https://api.github.com/repos/ALEXGUOQ/iosched/commits{/sha}", 1251 | "git_commits_url": "https://api.github.com/repos/ALEXGUOQ/iosched/git/commits{/sha}", 1252 | "comments_url": "https://api.github.com/repos/ALEXGUOQ/iosched/comments{/number}", 1253 | "issue_comment_url": "https://api.github.com/repos/ALEXGUOQ/iosched/issues/comments/{number}", 1254 | "contents_url": "https://api.github.com/repos/ALEXGUOQ/iosched/contents/{+path}", 1255 | "compare_url": "https://api.github.com/repos/ALEXGUOQ/iosched/compare/{base}...{head}", 1256 | "merges_url": "https://api.github.com/repos/ALEXGUOQ/iosched/merges", 1257 | "archive_url": "https://api.github.com/repos/ALEXGUOQ/iosched/{archive_format}{/ref}", 1258 | "downloads_url": "https://api.github.com/repos/ALEXGUOQ/iosched/downloads", 1259 | "issues_url": "https://api.github.com/repos/ALEXGUOQ/iosched/issues{/number}", 1260 | "pulls_url": "https://api.github.com/repos/ALEXGUOQ/iosched/pulls{/number}", 1261 | "milestones_url": "https://api.github.com/repos/ALEXGUOQ/iosched/milestones{/number}", 1262 | "notifications_url": "https://api.github.com/repos/ALEXGUOQ/iosched/notifications{?since,all,participating}", 1263 | "labels_url": "https://api.github.com/repos/ALEXGUOQ/iosched/labels{/name}", 1264 | "releases_url": "https://api.github.com/repos/ALEXGUOQ/iosched/releases{/id}", 1265 | "created_at": "2014-08-31T16:05:13Z", 1266 | "updated_at": "2014-08-31T13:58:24Z", 1267 | "pushed_at": "2014-08-19T18:34:02Z", 1268 | "git_url": "git://github.com/ALEXGUOQ/iosched.git", 1269 | "ssh_url": "git@github.com:ALEXGUOQ/iosched.git", 1270 | "clone_url": "https://github.com/ALEXGUOQ/iosched.git", 1271 | "svn_url": "https://github.com/ALEXGUOQ/iosched", 1272 | "homepage": null, 1273 | "size": 19325, 1274 | "stargazers_count": 0, 1275 | "watchers_count": 0, 1276 | "language": null, 1277 | "has_issues": false, 1278 | "has_downloads": true, 1279 | "has_wiki": false, 1280 | "forks_count": 0, 1281 | "mirror_url": null, 1282 | "open_issues_count": 0, 1283 | "forks": 0, 1284 | "open_issues": 0, 1285 | "watchers": 0, 1286 | "default_branch": "master", 1287 | "public": true 1288 | } 1289 | }, 1290 | "public": true, 1291 | "created_at": "2014-08-31T16:05:15Z", 1292 | "org": { 1293 | "id": 1342004, 1294 | "login": "google", 1295 | "gravatar_id": "24ba30616d2a20673f54c2aee36d159e", 1296 | "url": "https://api.github.com/orgs/google", 1297 | "avatar_url": "https://avatars.githubusercontent.com/u/1342004?" 1298 | } 1299 | }, 1300 | { 1301 | "id": "2263052579", 1302 | "type": "IssueCommentEvent", 1303 | "actor": { 1304 | "id": 205570, 1305 | "login": "johnjbarton", 1306 | "gravatar_id": "88457ac5fd091ba1fad54a189348a4b1", 1307 | "url": "https://api.github.com/users/johnjbarton", 1308 | "avatar_url": "https://avatars.githubusercontent.com/u/205570?" 1309 | }, 1310 | "repo": { 1311 | "id": 9060347, 1312 | "name": "google/traceur-compiler", 1313 | "url": "https://api.github.com/repos/google/traceur-compiler" 1314 | }, 1315 | "payload": { 1316 | "action": "created", 1317 | "issue": { 1318 | "url": "https://api.github.com/repos/google/traceur-compiler/issues/1300", 1319 | "labels_url": "https://api.github.com/repos/google/traceur-compiler/issues/1300/labels{/name}", 1320 | "comments_url": "https://api.github.com/repos/google/traceur-compiler/issues/1300/comments", 1321 | "events_url": "https://api.github.com/repos/google/traceur-compiler/issues/1300/events", 1322 | "html_url": "https://github.com/google/traceur-compiler/issues/1300", 1323 | "id": 41589319, 1324 | "number": 1300, 1325 | "title": "module myModule as './path/to/module'; outputs \"Semi-colon expected\" error", 1326 | "user": { 1327 | "login": "pflannery", 1328 | "id": 1727302, 1329 | "avatar_url": "https://avatars.githubusercontent.com/u/1727302?v=2", 1330 | "gravatar_id": "7f2a377aaeb6cea73145b33975a030b1", 1331 | "url": "https://api.github.com/users/pflannery", 1332 | "html_url": "https://github.com/pflannery", 1333 | "followers_url": "https://api.github.com/users/pflannery/followers", 1334 | "following_url": "https://api.github.com/users/pflannery/following{/other_user}", 1335 | "gists_url": "https://api.github.com/users/pflannery/gists{/gist_id}", 1336 | "starred_url": "https://api.github.com/users/pflannery/starred{/owner}{/repo}", 1337 | "subscriptions_url": "https://api.github.com/users/pflannery/subscriptions", 1338 | "organizations_url": "https://api.github.com/users/pflannery/orgs", 1339 | "repos_url": "https://api.github.com/users/pflannery/repos", 1340 | "events_url": "https://api.github.com/users/pflannery/events{/privacy}", 1341 | "received_events_url": "https://api.github.com/users/pflannery/received_events", 1342 | "type": "User", 1343 | "site_admin": false 1344 | }, 1345 | "labels": [ 1346 | 1347 | ], 1348 | "state": "open", 1349 | "locked": false, 1350 | "assignee": null, 1351 | "milestone": null, 1352 | "comments": 1, 1353 | "created_at": "2014-08-31T15:49:04Z", 1354 | "updated_at": "2014-08-31T16:03:15Z", 1355 | "closed_at": null, 1356 | "body": "I've been trying to bring in a module as a single object using \r\n```js\r\nmodule myModule as './path/to/module';\r\n```\r\nbut I get the following error ```Semi-colon expected```\r\n\r\nIs this because it's not implemented yet or am I doing something wrong?\r\n\r\nmy current work around is to replace the line with \r\n```js\r\nvar myModule = System.get(\"relative/path/to/module\");\r\n```" 1357 | }, 1358 | "comment": { 1359 | "url": "https://api.github.com/repos/google/traceur-compiler/issues/comments/53992343", 1360 | "html_url": "https://github.com/google/traceur-compiler/issues/1300#issuecomment-53992343", 1361 | "issue_url": "https://api.github.com/repos/google/traceur-compiler/issues/1300", 1362 | "id": 53992343, 1363 | "user": { 1364 | "login": "johnjbarton", 1365 | "id": 205570, 1366 | "avatar_url": "https://avatars.githubusercontent.com/u/205570?v=2", 1367 | "gravatar_id": "88457ac5fd091ba1fad54a189348a4b1", 1368 | "url": "https://api.github.com/users/johnjbarton", 1369 | "html_url": "https://github.com/johnjbarton", 1370 | "followers_url": "https://api.github.com/users/johnjbarton/followers", 1371 | "following_url": "https://api.github.com/users/johnjbarton/following{/other_user}", 1372 | "gists_url": "https://api.github.com/users/johnjbarton/gists{/gist_id}", 1373 | "starred_url": "https://api.github.com/users/johnjbarton/starred{/owner}{/repo}", 1374 | "subscriptions_url": "https://api.github.com/users/johnjbarton/subscriptions", 1375 | "organizations_url": "https://api.github.com/users/johnjbarton/orgs", 1376 | "repos_url": "https://api.github.com/users/johnjbarton/repos", 1377 | "events_url": "https://api.github.com/users/johnjbarton/events{/privacy}", 1378 | "received_events_url": "https://api.github.com/users/johnjbarton/received_events", 1379 | "type": "User", 1380 | "site_admin": false 1381 | }, 1382 | "created_at": "2014-08-31T16:03:15Z", 1383 | "updated_at": "2014-08-31T16:03:15Z", 1384 | "body": "You are doing something wrong. The new syntax is\r\n```js\r\nimport * as myModule from './path/to/module';\r\n```\r\nWhere did you read about the older syntax? " 1385 | } 1386 | }, 1387 | "public": true, 1388 | "created_at": "2014-08-31T16:03:18Z", 1389 | "org": { 1390 | "id": 1342004, 1391 | "login": "google", 1392 | "gravatar_id": "24ba30616d2a20673f54c2aee36d159e", 1393 | "url": "https://api.github.com/orgs/google", 1394 | "avatar_url": "https://avatars.githubusercontent.com/u/1342004?" 1395 | } 1396 | }, 1397 | { 1398 | "id": "2263052286", 1399 | "type": "WatchEvent", 1400 | "actor": { 1401 | "id": 6113842, 1402 | "login": "balysv", 1403 | "gravatar_id": "8067174fa3afcc5d140ed8e43a2b6278", 1404 | "url": "https://api.github.com/users/balysv", 1405 | "avatar_url": "https://avatars.githubusercontent.com/u/6113842?" 1406 | }, 1407 | "repo": { 1408 | "id": 7968417, 1409 | "name": "google/dagger", 1410 | "url": "https://api.github.com/repos/google/dagger" 1411 | }, 1412 | "payload": { 1413 | "action": "started" 1414 | }, 1415 | "public": true, 1416 | "created_at": "2014-08-31T16:02:44Z", 1417 | "org": { 1418 | "id": 1342004, 1419 | "login": "google", 1420 | "gravatar_id": "24ba30616d2a20673f54c2aee36d159e", 1421 | "url": "https://api.github.com/orgs/google", 1422 | "avatar_url": "https://avatars.githubusercontent.com/u/1342004?" 1423 | } 1424 | }, 1425 | { 1426 | "id": "2263049185", 1427 | "type": "WatchEvent", 1428 | "actor": { 1429 | "id": 1239359, 1430 | "login": "jmargeta", 1431 | "gravatar_id": "a7259256f02482925032f8534dbe1d1c", 1432 | "url": "https://api.github.com/users/jmargeta", 1433 | "avatar_url": "https://avatars.githubusercontent.com/u/1239359?" 1434 | }, 1435 | "repo": { 1436 | "id": 18511024, 1437 | "name": "google/web-starter-kit", 1438 | "url": "https://api.github.com/repos/google/web-starter-kit" 1439 | }, 1440 | "payload": { 1441 | "action": "started" 1442 | }, 1443 | "public": true, 1444 | "created_at": "2014-08-31T15:57:29Z", 1445 | "org": { 1446 | "id": 1342004, 1447 | "login": "google", 1448 | "gravatar_id": "24ba30616d2a20673f54c2aee36d159e", 1449 | "url": "https://api.github.com/orgs/google", 1450 | "avatar_url": "https://avatars.githubusercontent.com/u/1342004?" 1451 | } 1452 | }, 1453 | { 1454 | "id": "2263048295", 1455 | "type": "WatchEvent", 1456 | "actor": { 1457 | "id": 733158, 1458 | "login": "bobaaaaa", 1459 | "gravatar_id": "7bdc72c2f9aa1f3958d17aaa9429ae92", 1460 | "url": "https://api.github.com/users/bobaaaaa", 1461 | "avatar_url": "https://avatars.githubusercontent.com/u/733158?" 1462 | }, 1463 | "repo": { 1464 | "id": 18511024, 1465 | "name": "google/web-starter-kit", 1466 | "url": "https://api.github.com/repos/google/web-starter-kit" 1467 | }, 1468 | "payload": { 1469 | "action": "started" 1470 | }, 1471 | "public": true, 1472 | "created_at": "2014-08-31T15:55:45Z", 1473 | "org": { 1474 | "id": 1342004, 1475 | "login": "google", 1476 | "gravatar_id": "24ba30616d2a20673f54c2aee36d159e", 1477 | "url": "https://api.github.com/orgs/google", 1478 | "avatar_url": "https://avatars.githubusercontent.com/u/1342004?" 1479 | } 1480 | }, 1481 | { 1482 | "id": "2263044828", 1483 | "type": "IssuesEvent", 1484 | "actor": { 1485 | "id": 1727302, 1486 | "login": "pflannery", 1487 | "gravatar_id": "7f2a377aaeb6cea73145b33975a030b1", 1488 | "url": "https://api.github.com/users/pflannery", 1489 | "avatar_url": "https://avatars.githubusercontent.com/u/1727302?" 1490 | }, 1491 | "repo": { 1492 | "id": 9060347, 1493 | "name": "google/traceur-compiler", 1494 | "url": "https://api.github.com/repos/google/traceur-compiler" 1495 | }, 1496 | "payload": { 1497 | "action": "opened", 1498 | "issue": { 1499 | "url": "https://api.github.com/repos/google/traceur-compiler/issues/1300", 1500 | "labels_url": "https://api.github.com/repos/google/traceur-compiler/issues/1300/labels{/name}", 1501 | "comments_url": "https://api.github.com/repos/google/traceur-compiler/issues/1300/comments", 1502 | "events_url": "https://api.github.com/repos/google/traceur-compiler/issues/1300/events", 1503 | "html_url": "https://github.com/google/traceur-compiler/issues/1300", 1504 | "id": 41589319, 1505 | "number": 1300, 1506 | "title": "module myModule as './path/to/module'; outputs \"Semi-colon expected\" error", 1507 | "user": { 1508 | "login": "pflannery", 1509 | "id": 1727302, 1510 | "avatar_url": "https://avatars.githubusercontent.com/u/1727302?v=2", 1511 | "gravatar_id": "7f2a377aaeb6cea73145b33975a030b1", 1512 | "url": "https://api.github.com/users/pflannery", 1513 | "html_url": "https://github.com/pflannery", 1514 | "followers_url": "https://api.github.com/users/pflannery/followers", 1515 | "following_url": "https://api.github.com/users/pflannery/following{/other_user}", 1516 | "gists_url": "https://api.github.com/users/pflannery/gists{/gist_id}", 1517 | "starred_url": "https://api.github.com/users/pflannery/starred{/owner}{/repo}", 1518 | "subscriptions_url": "https://api.github.com/users/pflannery/subscriptions", 1519 | "organizations_url": "https://api.github.com/users/pflannery/orgs", 1520 | "repos_url": "https://api.github.com/users/pflannery/repos", 1521 | "events_url": "https://api.github.com/users/pflannery/events{/privacy}", 1522 | "received_events_url": "https://api.github.com/users/pflannery/received_events", 1523 | "type": "User", 1524 | "site_admin": false 1525 | }, 1526 | "labels": [ 1527 | 1528 | ], 1529 | "state": "open", 1530 | "locked": false, 1531 | "assignee": null, 1532 | "milestone": null, 1533 | "comments": 0, 1534 | "created_at": "2014-08-31T15:49:04Z", 1535 | "updated_at": "2014-08-31T15:49:04Z", 1536 | "closed_at": null, 1537 | "body": "I've been trying to bring in a module as a single object using \r\n```js\r\nmodule myModule as './path/to/module';\r\n```\r\nbut I get the following error ```Semi-colon expected```\r\n\r\nIs this because it's not implemented yet or am I doing something wrong?\r\n\r\nmy current work around is to replace the line with \r\n```js\r\nvar myModule = System.get(\"relative/path/to/module\");\r\n```" 1538 | } 1539 | }, 1540 | "public": true, 1541 | "created_at": "2014-08-31T15:49:04Z", 1542 | "org": { 1543 | "id": 1342004, 1544 | "login": "google", 1545 | "gravatar_id": "24ba30616d2a20673f54c2aee36d159e", 1546 | "url": "https://api.github.com/orgs/google", 1547 | "avatar_url": "https://avatars.githubusercontent.com/u/1342004?" 1548 | } 1549 | }, 1550 | { 1551 | "id": "2263044542", 1552 | "type": "IssuesEvent", 1553 | "actor": { 1554 | "id": 72422, 1555 | "login": "pihvi", 1556 | "gravatar_id": "d4e5de1f46b26e98f175f0e2b9c6226b", 1557 | "url": "https://api.github.com/users/pihvi", 1558 | "avatar_url": "https://avatars.githubusercontent.com/u/72422?" 1559 | }, 1560 | "repo": { 1561 | "id": 18511024, 1562 | "name": "google/web-starter-kit", 1563 | "url": "https://api.github.com/repos/google/web-starter-kit" 1564 | }, 1565 | "payload": { 1566 | "action": "opened", 1567 | "issue": { 1568 | "url": "https://api.github.com/repos/google/web-starter-kit/issues/413", 1569 | "labels_url": "https://api.github.com/repos/google/web-starter-kit/issues/413/labels{/name}", 1570 | "comments_url": "https://api.github.com/repos/google/web-starter-kit/issues/413/comments", 1571 | "events_url": "https://api.github.com/repos/google/web-starter-kit/issues/413/events", 1572 | "html_url": "https://github.com/google/web-starter-kit/issues/413", 1573 | "id": 41589302, 1574 | "number": 413, 1575 | "title": "gulp fails in vinyl-fs", 1576 | "user": { 1577 | "login": "pihvi", 1578 | "id": 72422, 1579 | "avatar_url": "https://avatars.githubusercontent.com/u/72422?v=2", 1580 | "gravatar_id": "d4e5de1f46b26e98f175f0e2b9c6226b", 1581 | "url": "https://api.github.com/users/pihvi", 1582 | "html_url": "https://github.com/pihvi", 1583 | "followers_url": "https://api.github.com/users/pihvi/followers", 1584 | "following_url": "https://api.github.com/users/pihvi/following{/other_user}", 1585 | "gists_url": "https://api.github.com/users/pihvi/gists{/gist_id}", 1586 | "starred_url": "https://api.github.com/users/pihvi/starred{/owner}{/repo}", 1587 | "subscriptions_url": "https://api.github.com/users/pihvi/subscriptions", 1588 | "organizations_url": "https://api.github.com/users/pihvi/orgs", 1589 | "repos_url": "https://api.github.com/users/pihvi/repos", 1590 | "events_url": "https://api.github.com/users/pihvi/events{/privacy}", 1591 | "received_events_url": "https://api.github.com/users/pihvi/received_events", 1592 | "type": "User", 1593 | "site_admin": false 1594 | }, 1595 | "labels": [ 1596 | 1597 | ], 1598 | "state": "open", 1599 | "locked": false, 1600 | "assignee": null, 1601 | "milestone": null, 1602 | "comments": 0, 1603 | "created_at": "2014-08-31T15:48:29Z", 1604 | "updated_at": "2014-08-31T15:48:29Z", 1605 | "closed_at": null, 1606 | "body": "I made a clean chekout of https://github.com/google/web-starter-kit/commit/266ee0620dc5e2f81a8e7902a6f40c41665af6e0 and running gulp fails. Same thing happened for v0.5.0\r\n\r\n➜ web-starter-kit git:(master) ✗ gulp\r\n[18:41:03] Using gulpfile /private/tmp/web-starter-kit/gulpfile.js\r\n[18:41:03] Starting 'clean'...\r\n[18:41:03] Finished 'clean' after 7.76 ms\r\n[18:41:03] Starting 'default'...\r\n[18:41:03] Starting 'styles'...\r\n[18:41:04] gulp-ruby-sass: directory\r\n[18:41:05] gulp-ruby-sass: write components.css\r\n write components.css.map\r\n[18:41:06] gulp-ruby-sass: write main.css\r\n write main.css.map\r\n[18:41:08] gulp-size: 'styles' total 183.59 kB\r\n[18:41:08] Finished 'styles' after 4.66 s\r\n[18:41:08] Starting 'jshint'...\r\n[18:41:08] Starting 'html'...\r\n[18:41:08] Starting 'images'...\r\n[18:41:09] Starting 'fonts'...\r\n[18:41:09] Starting 'copy'...\r\n[18:41:09] Finished 'jshint' after 992 ms\r\nPossibly unhandled Error: No path specified! Can not get relative.\r\n at File.Object.defineProperty.get (/private/tmp/web-starter-kit/node_modules/gulp/node_modules/vinyl-fs/node_modules/vinyl/index.js:150:27)\r\n at DestroyableTransform.saveFile [as _transform] (/private/tmp/web-starter-kit/node_modules/gulp/node_modules/vinyl-fs/lib/dest/index.js:40:48)\r\n at DestroyableTransform.Transform._read (/private/tmp/web-starter-kit/node_modules/gulp/node_modules/vinyl-fs/node_modules/through2/node_modules/readable-stream/lib/_stream_transform.js:184:10)\r\n at DestroyableTransform.Transform._write (/private/tmp/web-starter-kit/node_modules/gulp/node_modules/vinyl-fs/node_modules/through2/node_modules/readable-stream/lib/_stream_transform.js:172:12)\r\n at doWrite (/private/tmp/web-starter-kit/node_modules/gulp/node_modules/vinyl-fs/node_modules/through2/node_modules/readable-stream/lib/_stream_writable.js:237:10)\r\n at writeOrBuffer (/private/tmp/web-starter-kit/node_modules/gulp/node_modules/vinyl-fs/node_modules/through2/node_modules/readable-stream/lib/_stream_writable.js:227:5)\r\n at DestroyableTransform.Writable.write (/private/tmp/web-starter-kit/node_modules/gulp/node_modules/vinyl-fs/node_modules/through2/node_modules/readable-stream/lib/_stream_writable.js:194:11)\r\n at Stream.ondata (stream.js:51:26)\r\n at Stream.EventEmitter.emit (events.js:95:17)\r\n at queueData (/private/tmp/web-starter-kit/node_modules/gulp-cache/node_modules/map-stream/index.js:43:21)\r\n at next (/private/tmp/web-starter-kit/node_modules/gulp-cache/node_modules/map-stream/index.js:71:7)\r\n at /private/tmp/web-starter-kit/node_modules/gulp-cache/node_modules/map-stream/index.js:85:7\r\n at /private/tmp/web-starter-kit/node_modules/gulp-cache/index.js:93:13\r\n[18:41:09] gulp-size: 'html' total 44.14 kB\r\n[18:41:09] Finished 'html' after 935 ms\r\n[18:41:09] gulp-size: 'copy' total 41.35 kB\r\n[18:41:09] Finished 'copy' after 171 ms\r\n[18:41:09] gulp-size: 'fonts' total 1.09 MB\r\n[18:41:09] Finished 'fonts' after 177 ms\r\n➜ web-starter-kit git:(master) ✗ node --version\r\nv0.10.25\r\n➜ web-starter-kit git:(master) ✗ ruby --version\r\nruby 1.9.3p392 (2013-02-22 revision 39386) [x86_64-darwin12.3.0]\r\n➜ web-starter-kit git:(master) ✗ sass --version\r\nSass 3.4.2 (Selective Steve)\r\n➜ web-starter-kit git:(master) ✗ gulp --version\r\n[18:42:07] CLI version 3.8.6\r\n[18:42:07] Local version 3.8.7\r\n" 1607 | } 1608 | }, 1609 | "public": true, 1610 | "created_at": "2014-08-31T15:48:29Z", 1611 | "org": { 1612 | "id": 1342004, 1613 | "login": "google", 1614 | "gravatar_id": "24ba30616d2a20673f54c2aee36d159e", 1615 | "url": "https://api.github.com/orgs/google", 1616 | "avatar_url": "https://avatars.githubusercontent.com/u/1342004?" 1617 | } 1618 | }, 1619 | { 1620 | "id": "2263032302", 1621 | "type": "WatchEvent", 1622 | "actor": { 1623 | "id": 8606300, 1624 | "login": "whosluke", 1625 | "gravatar_id": "b24094d4e093ac2e32fb5981923d333b", 1626 | "url": "https://api.github.com/users/whosluke", 1627 | "avatar_url": "https://avatars.githubusercontent.com/u/8606300?" 1628 | }, 1629 | "repo": { 1630 | "id": 18511024, 1631 | "name": "google/web-starter-kit", 1632 | "url": "https://api.github.com/repos/google/web-starter-kit" 1633 | }, 1634 | "payload": { 1635 | "action": "started" 1636 | }, 1637 | "public": true, 1638 | "created_at": "2014-08-31T15:26:04Z", 1639 | "org": { 1640 | "id": 1342004, 1641 | "login": "google", 1642 | "gravatar_id": "24ba30616d2a20673f54c2aee36d159e", 1643 | "url": "https://api.github.com/orgs/google", 1644 | "avatar_url": "https://avatars.githubusercontent.com/u/1342004?" 1645 | } 1646 | }, 1647 | { 1648 | "id": "2263030192", 1649 | "type": "IssuesEvent", 1650 | "actor": { 1651 | "id": 110953, 1652 | "login": "addyosmani", 1653 | "gravatar_id": "96270e4c3e5e9806cf7245475c00b275", 1654 | "url": "https://api.github.com/users/addyosmani", 1655 | "avatar_url": "https://avatars.githubusercontent.com/u/110953?" 1656 | }, 1657 | "repo": { 1658 | "id": 18511024, 1659 | "name": "google/web-starter-kit", 1660 | "url": "https://api.github.com/repos/google/web-starter-kit" 1661 | }, 1662 | "payload": { 1663 | "action": "reopened", 1664 | "issue": { 1665 | "url": "https://api.github.com/repos/google/web-starter-kit/issues/97", 1666 | "labels_url": "https://api.github.com/repos/google/web-starter-kit/issues/97/labels{/name}", 1667 | "comments_url": "https://api.github.com/repos/google/web-starter-kit/issues/97/comments", 1668 | "events_url": "https://api.github.com/repos/google/web-starter-kit/issues/97/events", 1669 | "html_url": "https://github.com/google/web-starter-kit/issues/97", 1670 | "id": 36131766, 1671 | "number": 97, 1672 | "title": "Live stage for demo", 1673 | "user": { 1674 | "login": "mangini", 1675 | "id": 852626, 1676 | "avatar_url": "https://avatars.githubusercontent.com/u/852626?v=2", 1677 | "gravatar_id": "61ffaf1af13c5626d3cd975275b2ddfd", 1678 | "url": "https://api.github.com/users/mangini", 1679 | "html_url": "https://github.com/mangini", 1680 | "followers_url": "https://api.github.com/users/mangini/followers", 1681 | "following_url": "https://api.github.com/users/mangini/following{/other_user}", 1682 | "gists_url": "https://api.github.com/users/mangini/gists{/gist_id}", 1683 | "starred_url": "https://api.github.com/users/mangini/starred{/owner}{/repo}", 1684 | "subscriptions_url": "https://api.github.com/users/mangini/subscriptions", 1685 | "organizations_url": "https://api.github.com/users/mangini/orgs", 1686 | "repos_url": "https://api.github.com/users/mangini/repos", 1687 | "events_url": "https://api.github.com/users/mangini/events{/privacy}", 1688 | "received_events_url": "https://api.github.com/users/mangini/received_events", 1689 | "type": "User", 1690 | "site_admin": false 1691 | }, 1692 | "labels": [ 1693 | { 1694 | "url": "https://api.github.com/repos/google/web-starter-kit/labels/documentation", 1695 | "name": "documentation", 1696 | "color": "006b75" 1697 | } 1698 | ], 1699 | "state": "open", 1700 | "locked": false, 1701 | "assignee": null, 1702 | "milestone": null, 1703 | "comments": 10, 1704 | "created_at": "2014-06-20T00:39:43Z", 1705 | "updated_at": "2014-08-31T15:22:07Z", 1706 | "closed_at": null, 1707 | "body": "\r\nCongrats, awesome project.\r\n\r\nTo increase adoption, I suggest you stage a live demo of [each layout](https://developers.google.com/web/fundamentals/tools/setup/setup_kit#pick-a-layout), quickly accessible from https://developers.google.com/web/starter-kit/, so developers can look at the it before downloading/installing/etc.\r\n" 1708 | } 1709 | }, 1710 | "public": true, 1711 | "created_at": "2014-08-31T15:22:07Z", 1712 | "org": { 1713 | "id": 1342004, 1714 | "login": "google", 1715 | "gravatar_id": "24ba30616d2a20673f54c2aee36d159e", 1716 | "url": "https://api.github.com/orgs/google", 1717 | "avatar_url": "https://avatars.githubusercontent.com/u/1342004?" 1718 | } 1719 | }, 1720 | { 1721 | "id": "2263030186", 1722 | "type": "IssueCommentEvent", 1723 | "actor": { 1724 | "id": 110953, 1725 | "login": "addyosmani", 1726 | "gravatar_id": "96270e4c3e5e9806cf7245475c00b275", 1727 | "url": "https://api.github.com/users/addyosmani", 1728 | "avatar_url": "https://avatars.githubusercontent.com/u/110953?" 1729 | }, 1730 | "repo": { 1731 | "id": 18511024, 1732 | "name": "google/web-starter-kit", 1733 | "url": "https://api.github.com/repos/google/web-starter-kit" 1734 | }, 1735 | "payload": { 1736 | "action": "created", 1737 | "issue": { 1738 | "url": "https://api.github.com/repos/google/web-starter-kit/issues/97", 1739 | "labels_url": "https://api.github.com/repos/google/web-starter-kit/issues/97/labels{/name}", 1740 | "comments_url": "https://api.github.com/repos/google/web-starter-kit/issues/97/comments", 1741 | "events_url": "https://api.github.com/repos/google/web-starter-kit/issues/97/events", 1742 | "html_url": "https://github.com/google/web-starter-kit/issues/97", 1743 | "id": 36131766, 1744 | "number": 97, 1745 | "title": "Live stage for demo", 1746 | "user": { 1747 | "login": "mangini", 1748 | "id": 852626, 1749 | "avatar_url": "https://avatars.githubusercontent.com/u/852626?v=2", 1750 | "gravatar_id": "61ffaf1af13c5626d3cd975275b2ddfd", 1751 | "url": "https://api.github.com/users/mangini", 1752 | "html_url": "https://github.com/mangini", 1753 | "followers_url": "https://api.github.com/users/mangini/followers", 1754 | "following_url": "https://api.github.com/users/mangini/following{/other_user}", 1755 | "gists_url": "https://api.github.com/users/mangini/gists{/gist_id}", 1756 | "starred_url": "https://api.github.com/users/mangini/starred{/owner}{/repo}", 1757 | "subscriptions_url": "https://api.github.com/users/mangini/subscriptions", 1758 | "organizations_url": "https://api.github.com/users/mangini/orgs", 1759 | "repos_url": "https://api.github.com/users/mangini/repos", 1760 | "events_url": "https://api.github.com/users/mangini/events{/privacy}", 1761 | "received_events_url": "https://api.github.com/users/mangini/received_events", 1762 | "type": "User", 1763 | "site_admin": false 1764 | }, 1765 | "labels": [ 1766 | { 1767 | "url": "https://api.github.com/repos/google/web-starter-kit/labels/documentation", 1768 | "name": "documentation", 1769 | "color": "006b75" 1770 | } 1771 | ], 1772 | "state": "open", 1773 | "locked": false, 1774 | "assignee": null, 1775 | "milestone": null, 1776 | "comments": 10, 1777 | "created_at": "2014-06-20T00:39:43Z", 1778 | "updated_at": "2014-08-31T15:22:07Z", 1779 | "closed_at": null, 1780 | "body": "\r\nCongrats, awesome project.\r\n\r\nTo increase adoption, I suggest you stage a live demo of [each layout](https://developers.google.com/web/fundamentals/tools/setup/setup_kit#pick-a-layout), quickly accessible from https://developers.google.com/web/starter-kit/, so developers can look at the it before downloading/installing/etc.\r\n" 1781 | }, 1782 | "comment": { 1783 | "url": "https://api.github.com/repos/google/web-starter-kit/issues/comments/53991052", 1784 | "html_url": "https://github.com/google/web-starter-kit/issues/97#issuecomment-53991052", 1785 | "issue_url": "https://api.github.com/repos/google/web-starter-kit/issues/97", 1786 | "id": 53991052, 1787 | "user": { 1788 | "login": "addyosmani", 1789 | "id": 110953, 1790 | "avatar_url": "https://avatars.githubusercontent.com/u/110953?v=2", 1791 | "gravatar_id": "96270e4c3e5e9806cf7245475c00b275", 1792 | "url": "https://api.github.com/users/addyosmani", 1793 | "html_url": "https://github.com/addyosmani", 1794 | "followers_url": "https://api.github.com/users/addyosmani/followers", 1795 | "following_url": "https://api.github.com/users/addyosmani/following{/other_user}", 1796 | "gists_url": "https://api.github.com/users/addyosmani/gists{/gist_id}", 1797 | "starred_url": "https://api.github.com/users/addyosmani/starred{/owner}{/repo}", 1798 | "subscriptions_url": "https://api.github.com/users/addyosmani/subscriptions", 1799 | "organizations_url": "https://api.github.com/users/addyosmani/orgs", 1800 | "repos_url": "https://api.github.com/users/addyosmani/repos", 1801 | "events_url": "https://api.github.com/users/addyosmani/events{/privacy}", 1802 | "received_events_url": "https://api.github.com/users/addyosmani/received_events", 1803 | "type": "User", 1804 | "site_admin": false 1805 | }, 1806 | "created_at": "2014-08-31T15:22:07Z", 1807 | "updated_at": "2014-08-31T15:22:07Z", 1808 | "body": "Good idea. Re-opening so we can get this done early next week." 1809 | } 1810 | }, 1811 | "public": true, 1812 | "created_at": "2014-08-31T15:22:07Z", 1813 | "org": { 1814 | "id": 1342004, 1815 | "login": "google", 1816 | "gravatar_id": "24ba30616d2a20673f54c2aee36d159e", 1817 | "url": "https://api.github.com/orgs/google", 1818 | "avatar_url": "https://avatars.githubusercontent.com/u/1342004?" 1819 | } 1820 | } 1821 | ] 1822 | -------------------------------------------------------------------------------- /app/src/androidTest/java/org/andydyer/androidtestdemo/ApplicationTest.java: -------------------------------------------------------------------------------- 1 | package org.andydyer.androidtestdemo; 2 | 3 | import android.app.Application; 4 | import android.test.ApplicationTestCase; 5 | 6 | /** 7 | * Testing Fundamentals 8 | */ 9 | public class ApplicationTest extends ApplicationTestCase { 10 | public ApplicationTest() { 11 | super(Application.class); 12 | } 13 | } -------------------------------------------------------------------------------- /app/src/androidTest/java/org/andydyer/androidtestdemo/LoginActivityTest.java: -------------------------------------------------------------------------------- 1 | package org.andydyer.androidtestdemo; 2 | 3 | import org.andydyer.androidtestdemo.ui.LoginActivity; 4 | import org.mockito.ArgumentCaptor; 5 | 6 | import retrofit.Callback; 7 | import retrofit.RetrofitError; 8 | 9 | import static android.support.test.espresso.Espresso.onView; 10 | import static android.support.test.espresso.action.ViewActions.click; 11 | import static android.support.test.espresso.action.ViewActions.closeSoftKeyboard; 12 | import static android.support.test.espresso.action.ViewActions.typeText; 13 | import static android.support.test.espresso.assertion.ViewAssertions.matches; 14 | import static android.support.test.espresso.matcher.ViewMatchers.withId; 15 | import static org.andydyer.androidtestdemo.test.CustomMatchers.withError; 16 | import static org.mockito.Matchers.anyString; 17 | import static org.mockito.Mockito.verify; 18 | 19 | /** 20 | * Created by andy on 9/6/14. 21 | */ 22 | public class LoginActivityTest extends InjectedActivityTest { 23 | 24 | public LoginActivityTest() { 25 | super(LoginActivity.class); 26 | } 27 | 28 | @Override 29 | protected void setUp() throws Exception { 30 | super.setUp(); 31 | getActivity(); 32 | } 33 | 34 | public void testEmptyEmailShowsError() { 35 | onView(withId(R.id.email_sign_in_button)).perform(click()); 36 | onView(withId(R.id.email)).check(matches(withError( 37 | getActivity().getString(R.string.error_field_required)))); 38 | } 39 | 40 | public void testInvalidEmailShowsError() { 41 | onView(withId(R.id.email)).perform(typeText("abc"), closeSoftKeyboard()); 42 | onView(withId(R.id.email_sign_in_button)).perform(click()); 43 | onView(withId(R.id.email)).check(matches(withError( 44 | getActivity().getString(R.string.error_invalid_email)))); 45 | } 46 | 47 | public void testShortPasswordShowsError() { 48 | onView(withId(R.id.email)).perform(typeText("email@host.com"), closeSoftKeyboard()); 49 | onView(withId(R.id.password)).perform(typeText("a"), closeSoftKeyboard()); 50 | onView(withId(R.id.email_sign_in_button)).perform(click()); 51 | onView(withId(R.id.password)).check(matches(withError( 52 | getActivity().getString(R.string.error_invalid_password)))); 53 | } 54 | 55 | public void testIncorrectPasswordShowsError() { 56 | onView(withId(R.id.email)).perform(typeText("email@host.com"), closeSoftKeyboard()); 57 | onView(withId(R.id.password)).perform(typeText("abcde"), closeSoftKeyboard()); 58 | onView(withId(R.id.email_sign_in_button)).perform(click()); 59 | 60 | final ArgumentCaptor captor = ArgumentCaptor.forClass(Callback.class); 61 | verify(authenticationService).login(anyString(), anyString(), anyString(), captor.capture()); 62 | getActivity().runOnUiThread(new Runnable() { 63 | @Override 64 | public void run() { 65 | captor.getValue().failure(RetrofitError.unexpectedError("", new Exception())); 66 | } 67 | }); 68 | 69 | onView(withId(R.id.password)).check(matches(withError( 70 | getActivity().getString(R.string.error_incorrect_password)))); 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /app/src/androidTest/java/org/andydyer/androidtestdemo/MainActivityTest.java: -------------------------------------------------------------------------------- 1 | package org.andydyer.androidtestdemo; 2 | 3 | import android.support.test.espresso.contrib.RecyclerViewActions; 4 | import android.test.ActivityInstrumentationTestCase2; 5 | 6 | import org.andydyer.androidtestdemo.ui.MainActivity; 7 | 8 | import static android.support.test.espresso.Espresso.onView; 9 | import static android.support.test.espresso.action.ViewActions.click; 10 | import static android.support.test.espresso.assertion.ViewAssertions.matches; 11 | import static android.support.test.espresso.matcher.ViewMatchers.withId; 12 | import static org.andydyer.androidtestdemo.test.CustomMatchers.hasData; 13 | import static org.andydyer.androidtestdemo.test.CustomMatchers.showsUrl; 14 | import static org.andydyer.androidtestdemo.test.CustomViewActions.clickEventItemAvatar; 15 | 16 | /** 17 | * Created by andy on 8/18/14. 18 | */ 19 | public class MainActivityTest extends ActivityInstrumentationTestCase2 { 20 | 21 | public MainActivityTest() { 22 | super(MainActivity.class); 23 | } 24 | 25 | @Override 26 | protected void setUp() throws Exception { 27 | super.setUp(); 28 | DemoApplication.getInstance().setMockMode(true); 29 | getActivity(); 30 | } 31 | 32 | @Override 33 | protected void tearDown() throws Exception { 34 | DemoApplication.getInstance().setMockMode(false); 35 | } 36 | 37 | public void testHasData() throws Exception { 38 | onView(withId(android.R.id.list)).check(matches(hasData())); 39 | } 40 | 41 | public void testRowClickLaunchesRepoUrl() throws Exception { 42 | onView(withId(android.R.id.list)).perform( 43 | RecyclerViewActions.actionOnItemAtPosition(0, click())); 44 | onView(withId(R.id.webview)).check(matches(showsUrl("github.com/google/iosched"))); 45 | } 46 | 47 | public void testImageClickLaunchesProfileUrl() throws Exception { 48 | onView(withId(android.R.id.list)).perform( 49 | RecyclerViewActions.actionOnItemAtPosition(0, clickEventItemAvatar())); 50 | onView(withId(R.id.webview)).check(matches(showsUrl("github.com/karthikraj-duraisamy"))); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /app/src/androidTest/java/org/andydyer/androidtestdemo/api/ApiServiceTest.java: -------------------------------------------------------------------------------- 1 | package org.andydyer.androidtestdemo.api; 2 | 3 | import org.andydyer.androidtestdemo.InjectedInstrumentationTest; 4 | 5 | import retrofit.Callback; 6 | import retrofit.RetrofitError; 7 | import retrofit.client.Response; 8 | 9 | /** 10 | * Created by andy on 8/30/14. 11 | */ 12 | public class ApiServiceTest extends InjectedInstrumentationTest { 13 | 14 | public void testEventsRequest() { 15 | getApiService().getEvents("google", new Callback() { 16 | @Override 17 | public void success(Events events, Response response) { 18 | assertNotNull(events); 19 | assertFalse(events.isEmpty()); 20 | } 21 | 22 | @Override 23 | public void failure(RetrofitError error) { 24 | 25 | } 26 | }); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /app/src/androidTest/java/org/andydyer/androidtestdemo/test/CustomMatchers.java: -------------------------------------------------------------------------------- 1 | package org.andydyer.androidtestdemo.test; 2 | 3 | import android.support.v7.widget.RecyclerView; 4 | import android.view.View; 5 | import android.webkit.WebView; 6 | import android.widget.EditText; 7 | 8 | import org.hamcrest.Description; 9 | import org.hamcrest.Matcher; 10 | import org.hamcrest.TypeSafeMatcher; 11 | 12 | /** 13 | * Created by andy on 9/6/14. 14 | */ 15 | public class CustomMatchers { 16 | 17 | public static Matcher hasData() { 18 | return new TypeSafeMatcher() { 19 | 20 | @Override 21 | public void describeTo(Description description) { 22 | } 23 | 24 | @Override 25 | public boolean matchesSafely(View view) { 26 | if (!(view instanceof RecyclerView)) { 27 | return false; 28 | } 29 | return ((RecyclerView) view).getAdapter().getItemCount() > 0; 30 | } 31 | }; 32 | } 33 | 34 | public static Matcher showsUrl(final String url) { 35 | return new TypeSafeMatcher() { 36 | 37 | @Override 38 | public void describeTo(Description description) { 39 | } 40 | 41 | @Override 42 | public boolean matchesSafely(View view) { 43 | if (!(view instanceof WebView)) { 44 | return false; 45 | } 46 | WebView webView = (WebView) view; 47 | return webView.getUrl().contains(url); 48 | } 49 | }; 50 | } 51 | 52 | public static Matcher withError(final String expected) { 53 | return new TypeSafeMatcher() { 54 | 55 | @Override 56 | public boolean matchesSafely(View view) { 57 | if (!(view instanceof EditText)) { 58 | return false; 59 | } 60 | EditText editText = (EditText) view; 61 | return editText.getError().toString().equals(expected); 62 | } 63 | 64 | @Override 65 | public void describeTo(Description description) { 66 | 67 | } 68 | }; 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /app/src/androidTest/java/org/andydyer/androidtestdemo/test/CustomViewActions.java: -------------------------------------------------------------------------------- 1 | package org.andydyer.androidtestdemo.test; 2 | 3 | import android.support.test.espresso.UiController; 4 | import android.support.test.espresso.ViewAction; 5 | import android.view.View; 6 | 7 | import org.andydyer.androidtestdemo.R; 8 | import org.hamcrest.Matcher; 9 | 10 | /** 11 | * Created by andy on 1/10/15. 12 | */ 13 | public class CustomViewActions { 14 | 15 | public static ViewAction clickEventItemAvatar() { 16 | return new ViewAction() { 17 | @Override 18 | public Matcher getConstraints() { 19 | return null; 20 | } 21 | 22 | @Override 23 | public String getDescription() { 24 | return null; 25 | } 26 | 27 | @Override 28 | public void perform(UiController uiController, View view) { 29 | view.findViewById(R.id.event_list_item_avatar).callOnClick(); 30 | } 31 | }; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /app/src/debug/java/org/andydyer/androidtestdemo/Graph.java: -------------------------------------------------------------------------------- 1 | package org.andydyer.androidtestdemo; 2 | 3 | import org.andydyer.androidtestdemo.api.DebugApiServiceModule; 4 | import org.andydyer.androidtestdemo.ui.LoginActivity; 5 | import org.andydyer.androidtestdemo.ui.fragments.EventListFragment; 6 | 7 | import javax.inject.Singleton; 8 | 9 | import dagger.Component; 10 | 11 | /** 12 | * Created by andy on 1/15/15. 13 | */ 14 | @Singleton 15 | @Component(modules = {DebugApiServiceModule.class}) 16 | public interface Graph { 17 | 18 | // In a real app, we'd minimize the number of inject overloads by using base classes for fragments, activities, etc. 19 | void inject(InjectedActivityTest activity); 20 | void inject(InjectedInstrumentationTest testCase); 21 | void inject(LoginActivity activity); 22 | void inject(EventListFragment fragment); 23 | 24 | public final static class Initializer { 25 | public static Graph init(boolean mockMode) { 26 | return Dagger_Graph.builder() 27 | .debugApiServiceModule(new DebugApiServiceModule(mockMode)) 28 | .build(); 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /app/src/debug/java/org/andydyer/androidtestdemo/InjectedActivityTest.java: -------------------------------------------------------------------------------- 1 | package org.andydyer.androidtestdemo; 2 | 3 | import android.test.ActivityInstrumentationTestCase2; 4 | 5 | import org.andydyer.androidtestdemo.api.AuthenticationService; 6 | 7 | import javax.inject.Inject; 8 | 9 | /** 10 | * Created by andy on 1/16/15. 11 | */ 12 | public class InjectedActivityTest extends ActivityInstrumentationTestCase2 { 13 | 14 | @Inject AuthenticationService authenticationService; 15 | 16 | public InjectedActivityTest(Class activityClass) { super(activityClass); } 17 | 18 | @Override 19 | protected void setUp() throws Exception { 20 | super.setUp(); 21 | DemoApplication.getInstance().setMockMode(true); 22 | DemoApplication.getInstance().getGraph().inject(this); 23 | } 24 | 25 | @Override 26 | protected void tearDown() throws Exception { 27 | DemoApplication.getInstance().setMockMode(false); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /app/src/debug/java/org/andydyer/androidtestdemo/InjectedInstrumentationTest.java: -------------------------------------------------------------------------------- 1 | package org.andydyer.androidtestdemo; 2 | 3 | import android.test.InstrumentationTestCase; 4 | 5 | import org.andydyer.androidtestdemo.api.ApiService; 6 | 7 | import javax.inject.Inject; 8 | 9 | import lombok.Getter; 10 | 11 | /** 12 | * Created by andy on 1/16/15. 13 | */ 14 | public class InjectedInstrumentationTest extends InstrumentationTestCase { 15 | 16 | @Inject @Getter ApiService apiService; 17 | 18 | @Override 19 | protected void setUp() throws Exception { 20 | DemoApplication.getInstance().setMockMode(true); 21 | DemoApplication.getInstance().getGraph().inject(this); 22 | } 23 | 24 | @Override 25 | protected void tearDown() throws Exception { 26 | DemoApplication.getInstance().setMockMode(false); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /app/src/debug/java/org/andydyer/androidtestdemo/api/DebugApiServiceModule.java: -------------------------------------------------------------------------------- 1 | package org.andydyer.androidtestdemo.api; 2 | 3 | import com.google.gson.FieldNamingPolicy; 4 | import com.google.gson.Gson; 5 | import com.google.gson.GsonBuilder; 6 | 7 | import javax.inject.Singleton; 8 | 9 | import dagger.Module; 10 | import dagger.Provides; 11 | import retrofit.RestAdapter; 12 | import retrofit.android.AndroidLog; 13 | import retrofit.converter.GsonConverter; 14 | 15 | import static org.mockito.Mockito.mock; 16 | 17 | /** 18 | * Created by andy on 1/15/15. 19 | */ 20 | @Module 21 | public final class DebugApiServiceModule { 22 | 23 | private final boolean mockMode; 24 | 25 | public DebugApiServiceModule(boolean provideMocks) { 26 | mockMode = provideMocks; 27 | } 28 | 29 | @Provides @Singleton 30 | public ApiService provideApiService() { 31 | if(mockMode) { 32 | return new MockApiService(); 33 | } 34 | else { 35 | Gson gson = new GsonBuilder() 36 | .setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES) 37 | .create(); 38 | return new RestAdapter.Builder() 39 | .setEndpoint(ApiService.API_URL) 40 | .setConverter(new GsonConverter(gson)) 41 | .setLogLevel(RestAdapter.LogLevel.FULL).setLog(new AndroidLog("API")) 42 | .build() 43 | .create(ApiService.class); 44 | } 45 | } 46 | 47 | @Provides @Singleton 48 | public AuthenticationService provideAuthenticationService() { 49 | if(mockMode) { 50 | return mock(AuthenticationService.class); // not implemented 51 | } 52 | else { 53 | return null; // not implemented 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /app/src/debug/java/org/andydyer/androidtestdemo/api/MockApiService.java: -------------------------------------------------------------------------------- 1 | package org.andydyer.androidtestdemo.api; 2 | 3 | import com.google.gson.FieldNamingPolicy; 4 | import com.google.gson.Gson; 5 | import com.google.gson.GsonBuilder; 6 | 7 | import org.apache.commons.io.IOUtils; 8 | 9 | import java.io.BufferedReader; 10 | import java.io.IOException; 11 | import java.io.InputStream; 12 | import java.io.InputStreamReader; 13 | import java.util.Collections; 14 | 15 | import retrofit.Callback; 16 | import retrofit.client.Response; 17 | import retrofit.http.Path; 18 | import retrofit.mime.TypedByteArray; 19 | 20 | /** 21 | * Implement ApiService interface to return test data from JSON files and avoid making network requests. 22 | */ 23 | public class MockApiService implements ApiService { 24 | 25 | private static final int HTTP_OK_STATUS = 200; 26 | 27 | private static final String EVENTS_RESPONSE_FILE = "events_response.json"; 28 | 29 | @Override 30 | public void getEvents(@Path("organization") String organization, Callback callback) { 31 | try { 32 | String json = readFileFromAssets(EVENTS_RESPONSE_FILE); 33 | Response response = getMockResponse(HTTP_OK_STATUS, json); 34 | Events events = getMockResponseData(json, Events.class); 35 | callback.success(events, response); 36 | } catch (IOException e) { 37 | e.printStackTrace(); 38 | } 39 | } 40 | 41 | private String readFileFromAssets(String fileName) throws IOException { 42 | InputStream stream = getClass().getClassLoader().getResourceAsStream("assets/" + fileName); 43 | BufferedReader reader = new BufferedReader(new InputStreamReader(stream, "UTF-8")); 44 | return IOUtils.toString(reader); 45 | } 46 | 47 | private T getMockResponseData(String json, Class klass) { 48 | Gson gson = new GsonBuilder() 49 | .setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES) 50 | .create(); 51 | return gson.fromJson(json, klass); 52 | } 53 | 54 | private Response getMockResponse(int httpCode, String json) throws IOException { 55 | return new Response("url", httpCode, "", Collections.EMPTY_LIST, 56 | new TypedByteArray("application/json", json.getBytes())); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 13 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /app/src/main/java/org/andydyer/androidtestdemo/DemoApplication.java: -------------------------------------------------------------------------------- 1 | package org.andydyer.androidtestdemo; 2 | 3 | import android.app.Application; 4 | 5 | import lombok.Getter; 6 | 7 | /** 8 | * Created by andy on 8/23/14. 9 | */ 10 | public class DemoApplication extends Application { 11 | 12 | @Getter Graph graph; 13 | 14 | @Getter static DemoApplication instance; 15 | 16 | @Override 17 | public void onCreate() { 18 | super.onCreate(); 19 | instance = this; 20 | graph = Graph.Initializer.init(false); 21 | } 22 | 23 | public void setMockMode(boolean useMock) { 24 | graph = Graph.Initializer.init(useMock); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /app/src/main/java/org/andydyer/androidtestdemo/api/Actor.java: -------------------------------------------------------------------------------- 1 | package org.andydyer.androidtestdemo.api; 2 | 3 | import lombok.Getter; 4 | 5 | /** 6 | * Created by andy on 8/23/14. 7 | */ 8 | public class Actor { 9 | 10 | @Getter long id; 11 | @Getter String login; 12 | @Getter String gravatarId; 13 | @Getter String url; 14 | @Getter String avatarUrl; 15 | } 16 | -------------------------------------------------------------------------------- /app/src/main/java/org/andydyer/androidtestdemo/api/ApiService.java: -------------------------------------------------------------------------------- 1 | package org.andydyer.androidtestdemo.api; 2 | 3 | import retrofit.Callback; 4 | import retrofit.http.GET; 5 | import retrofit.http.Path; 6 | 7 | /** 8 | * Created by andy on 8/23/14. 9 | */ 10 | public interface ApiService { 11 | 12 | public static final String API_URL = "https://api.github.com/"; 13 | 14 | @GET("/orgs/{organization}/events") 15 | public void getEvents(@Path("organization") String organization, Callback callback); 16 | } 17 | -------------------------------------------------------------------------------- /app/src/main/java/org/andydyer/androidtestdemo/api/ApiServiceModule.java: -------------------------------------------------------------------------------- 1 | package org.andydyer.androidtestdemo.api; 2 | 3 | import com.google.gson.FieldNamingPolicy; 4 | import com.google.gson.Gson; 5 | import com.google.gson.GsonBuilder; 6 | 7 | import org.andydyer.androidtestdemo.ui.fragments.EventListFragment; 8 | import org.andydyer.androidtestdemo.ui.LoginActivity; 9 | 10 | import javax.inject.Singleton; 11 | 12 | import dagger.Module; 13 | import dagger.Provides; 14 | import retrofit.RestAdapter; 15 | import retrofit.android.AndroidLog; 16 | import retrofit.converter.GsonConverter; 17 | 18 | /** 19 | * Created by andy on 8/23/14. 20 | */ 21 | @Module 22 | public final class ApiServiceModule { 23 | 24 | @Provides @Singleton 25 | public ApiService provideApiService() { 26 | Gson gson = new GsonBuilder() 27 | .setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES) 28 | .create(); 29 | return new RestAdapter.Builder() 30 | .setEndpoint(ApiService.API_URL) 31 | .setConverter(new GsonConverter(gson)) 32 | .setLogLevel(RestAdapter.LogLevel.FULL).setLog(new AndroidLog("API")) 33 | .build() 34 | .create(ApiService.class); 35 | } 36 | 37 | @Provides @Singleton 38 | public AuthenticationService provideAuthenticationService() { 39 | return null; // not implemented 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /app/src/main/java/org/andydyer/androidtestdemo/api/AuthenticationService.java: -------------------------------------------------------------------------------- 1 | package org.andydyer.androidtestdemo.api; 2 | 3 | import retrofit.Callback; 4 | import retrofit.http.POST; 5 | import retrofit.http.Query; 6 | 7 | /** 8 | * Created by andy on 9/7/14. 9 | */ 10 | public interface AuthenticationService { 11 | 12 | // Authentication is not implemented in this project. this endpoint is here to demonstrate 13 | // mocking a response for testing. 14 | @POST("/login/oauth/access_token") 15 | public void login(@Query("client_id") String clientId, @Query("client_secret") String clientSecret, 16 | @Query("code") String code, Callback callback); 17 | } 18 | -------------------------------------------------------------------------------- /app/src/main/java/org/andydyer/androidtestdemo/api/Event.java: -------------------------------------------------------------------------------- 1 | package org.andydyer.androidtestdemo.api; 2 | 3 | import android.text.format.DateUtils; 4 | 5 | import java.text.SimpleDateFormat; 6 | import java.util.GregorianCalendar; 7 | import java.util.TimeZone; 8 | 9 | import lombok.Getter; 10 | 11 | /** 12 | * Created by andy on 8/23/14. 13 | */ 14 | public class Event { 15 | 16 | @Getter long id; 17 | @Getter String type; 18 | @Getter Actor actor; 19 | @Getter Repo repo; 20 | @Getter String createdAt; 21 | 22 | public String getCreatedAtRelativeTime() { 23 | try { 24 | TimeZone utc = TimeZone.getTimeZone("UTC"); 25 | SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'"); 26 | format.setTimeZone(utc); 27 | GregorianCalendar calendar = new GregorianCalendar(utc); 28 | calendar.setTime(format.parse(createdAt)); 29 | return DateUtils.getRelativeTimeSpanString(calendar.getTimeInMillis(), 30 | System.currentTimeMillis(), DateUtils.SECOND_IN_MILLIS).toString(); 31 | } catch (Exception e) { 32 | return createdAt; 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /app/src/main/java/org/andydyer/androidtestdemo/api/Events.java: -------------------------------------------------------------------------------- 1 | package org.andydyer.androidtestdemo.api; 2 | 3 | import java.util.ArrayList; 4 | 5 | /** 6 | * Created by andy on 8/31/14. 7 | */ 8 | public class Events extends ArrayList { 9 | } 10 | -------------------------------------------------------------------------------- /app/src/main/java/org/andydyer/androidtestdemo/api/Repo.java: -------------------------------------------------------------------------------- 1 | package org.andydyer.androidtestdemo.api; 2 | 3 | import lombok.Getter; 4 | 5 | /** 6 | * Created by andy on 8/23/14. 7 | */ 8 | public class Repo { 9 | 10 | @Getter long id; 11 | @Getter String name; 12 | @Getter String url; 13 | } 14 | -------------------------------------------------------------------------------- /app/src/main/java/org/andydyer/androidtestdemo/ui/LoginActivity.java: -------------------------------------------------------------------------------- 1 | package org.andydyer.androidtestdemo.ui; 2 | 3 | import android.app.LoaderManager.LoaderCallbacks; 4 | import android.content.CursorLoader; 5 | import android.content.Intent; 6 | import android.content.Loader; 7 | import android.database.Cursor; 8 | import android.net.Uri; 9 | import android.os.Bundle; 10 | import android.provider.ContactsContract; 11 | import android.support.v7.app.ActionBarActivity; 12 | import android.text.TextUtils; 13 | import android.view.KeyEvent; 14 | import android.view.View; 15 | import android.view.View.OnClickListener; 16 | import android.view.inputmethod.EditorInfo; 17 | import android.widget.ArrayAdapter; 18 | import android.widget.AutoCompleteTextView; 19 | import android.widget.Button; 20 | import android.widget.EditText; 21 | import android.widget.TextView; 22 | 23 | import org.andydyer.androidtestdemo.DemoApplication; 24 | import org.andydyer.androidtestdemo.R; 25 | import org.andydyer.androidtestdemo.api.AuthenticationService; 26 | 27 | import java.util.ArrayList; 28 | import java.util.List; 29 | 30 | import javax.inject.Inject; 31 | 32 | import retrofit.Callback; 33 | import retrofit.RetrofitError; 34 | import retrofit.client.Response; 35 | 36 | 37 | /** 38 | * A login screen that offers login via email/password. 39 | 40 | */ 41 | public class LoginActivity extends ActionBarActivity implements LoaderCallbacks, Callback { 42 | 43 | @Inject AuthenticationService authenticationService; 44 | 45 | // UI references. 46 | private AutoCompleteTextView mEmailView; 47 | private EditText mPasswordView; 48 | private View mProgressView; 49 | private View mLoginFormView; 50 | 51 | @Override 52 | protected void onCreate(Bundle savedInstanceState) { 53 | super.onCreate(savedInstanceState); 54 | setContentView(R.layout.activity_login); 55 | DemoApplication.getInstance().getGraph().inject(this); 56 | 57 | // Set up the login form. 58 | mEmailView = (AutoCompleteTextView) findViewById(R.id.email); 59 | populateAutoComplete(); 60 | 61 | mPasswordView = (EditText) findViewById(R.id.password); 62 | mPasswordView.setOnEditorActionListener(new TextView.OnEditorActionListener() { 63 | @Override 64 | public boolean onEditorAction(TextView textView, int id, KeyEvent keyEvent) { 65 | if (id == R.id.login || id == EditorInfo.IME_NULL) { 66 | attemptLogin(); 67 | return true; 68 | } 69 | return false; 70 | } 71 | }); 72 | 73 | Button mEmailSignInButton = (Button) findViewById(R.id.email_sign_in_button); 74 | mEmailSignInButton.setOnClickListener(new OnClickListener() { 75 | @Override 76 | public void onClick(View view) { 77 | attemptLogin(); 78 | } 79 | }); 80 | 81 | mLoginFormView = findViewById(R.id.login_form); 82 | mProgressView = findViewById(R.id.login_progress); 83 | } 84 | 85 | private void populateAutoComplete() { 86 | getLoaderManager().initLoader(0, null, this); 87 | } 88 | 89 | 90 | /** 91 | * Attempts to sign in or register the account specified by the login form. 92 | * If there are form errors (invalid email, missing fields, etc.), the 93 | * errors are presented and no actual login attempt is made. 94 | */ 95 | public void attemptLogin() { 96 | // Reset errors. 97 | mEmailView.setError(null); 98 | mPasswordView.setError(null); 99 | 100 | // Store values at the time of the login attempt. 101 | String email = mEmailView.getText().toString(); 102 | String password = mPasswordView.getText().toString(); 103 | 104 | boolean cancel = false; 105 | View focusView = null; 106 | 107 | 108 | // Check for a valid password, if the user entered one. 109 | if (!TextUtils.isEmpty(password) && !isPasswordValid(password)) { 110 | mPasswordView.setError(getString(R.string.error_invalid_password)); 111 | focusView = mPasswordView; 112 | cancel = true; 113 | } 114 | 115 | // Check for a valid email address. 116 | if (TextUtils.isEmpty(email)) { 117 | mEmailView.setError(getString(R.string.error_field_required)); 118 | focusView = mEmailView; 119 | cancel = true; 120 | } else if (!isEmailValid(email)) { 121 | mEmailView.setError(getString(R.string.error_invalid_email)); 122 | focusView = mEmailView; 123 | cancel = true; 124 | } 125 | 126 | if (cancel) { 127 | // There was an error; don't attempt login and focus the first 128 | // form field with an error. 129 | focusView.requestFocus(); 130 | } else { 131 | // Kick off a background task to perform the user login attempt. 132 | authenticationService.login("test", "test", "test", this); 133 | } 134 | } 135 | private boolean isEmailValid(String email) { 136 | //TODO: Replace this with your own logic 137 | return email.contains("@"); 138 | } 139 | 140 | private boolean isPasswordValid(String password) { 141 | //TODO: Replace this with your own logic 142 | return password.length() > 4; 143 | } 144 | 145 | @Override 146 | public Loader onCreateLoader(int i, Bundle bundle) { 147 | return new CursorLoader(this, 148 | // Retrieve data rows for the device user's 'profile' contact. 149 | Uri.withAppendedPath(ContactsContract.Profile.CONTENT_URI, 150 | ContactsContract.Contacts.Data.CONTENT_DIRECTORY), ProfileQuery.PROJECTION, 151 | 152 | // Select only email addresses. 153 | ContactsContract.Contacts.Data.MIMETYPE + 154 | " = ?", new String[]{ContactsContract.CommonDataKinds.Email 155 | .CONTENT_ITEM_TYPE}, 156 | 157 | // Show primary email addresses first. Note that there won't be 158 | // a primary email address if the user hasn't specified one. 159 | ContactsContract.Contacts.Data.IS_PRIMARY + " DESC"); 160 | } 161 | 162 | @Override 163 | public void onLoadFinished(Loader cursorLoader, Cursor cursor) { 164 | List emails = new ArrayList(); 165 | cursor.moveToFirst(); 166 | while (!cursor.isAfterLast()) { 167 | emails.add(cursor.getString(ProfileQuery.ADDRESS)); 168 | cursor.moveToNext(); 169 | } 170 | 171 | addEmailsToAutoComplete(emails); 172 | } 173 | 174 | @Override 175 | public void onLoaderReset(Loader cursorLoader) { 176 | 177 | } 178 | 179 | private interface ProfileQuery { 180 | String[] PROJECTION = { 181 | ContactsContract.CommonDataKinds.Email.ADDRESS, 182 | ContactsContract.CommonDataKinds.Email.IS_PRIMARY, 183 | }; 184 | 185 | int ADDRESS = 0; 186 | int IS_PRIMARY = 1; 187 | } 188 | 189 | 190 | private void addEmailsToAutoComplete(List emailAddressCollection) { 191 | //Create adapter to tell the AutoCompleteTextView what to show in its dropdown list. 192 | ArrayAdapter adapter = 193 | new ArrayAdapter(LoginActivity.this, 194 | android.R.layout.simple_dropdown_item_1line, emailAddressCollection); 195 | 196 | mEmailView.setAdapter(adapter); 197 | } 198 | 199 | @Override 200 | public void success(Boolean success, Response response) { 201 | if (success) { 202 | startActivity(new Intent(this, MainActivity.class)); 203 | finish(); 204 | } else { 205 | mPasswordView.setError(getString(R.string.error_incorrect_password)); 206 | mPasswordView.requestFocus(); 207 | } 208 | } 209 | 210 | @Override 211 | public void failure(RetrofitError error) { 212 | mPasswordView.setError(getString(R.string.error_incorrect_password)); 213 | mPasswordView.requestFocus(); 214 | } 215 | } 216 | 217 | 218 | 219 | -------------------------------------------------------------------------------- /app/src/main/java/org/andydyer/androidtestdemo/ui/MainActivity.java: -------------------------------------------------------------------------------- 1 | package org.andydyer.androidtestdemo.ui; 2 | 3 | import android.os.Bundle; 4 | import android.support.v7.app.ActionBarActivity; 5 | import android.view.Menu; 6 | import android.view.MenuItem; 7 | 8 | import org.andydyer.androidtestdemo.R; 9 | import org.andydyer.androidtestdemo.ui.fragments.EventListFragment; 10 | 11 | public class MainActivity extends ActionBarActivity { 12 | 13 | @Override 14 | protected void onCreate(Bundle savedInstanceState) { 15 | super.onCreate(savedInstanceState); 16 | setContentView(R.layout.activity_main); 17 | 18 | if (savedInstanceState == null) { 19 | getSupportFragmentManager().beginTransaction() 20 | .replace(R.id.container, new EventListFragment()) 21 | .commit(); 22 | } 23 | } 24 | 25 | @Override 26 | public boolean onCreateOptionsMenu(Menu menu) { 27 | // Inflate the menu; this adds items to the action bar if it is present. 28 | getMenuInflater().inflate(R.menu.main, menu); 29 | return true; 30 | } 31 | 32 | @Override 33 | public boolean onOptionsItemSelected(MenuItem item) { 34 | // Handle action bar item clicks here. The action bar will 35 | // automatically handle clicks on the Home/Up button, so long 36 | // as you specify a parent activity in AndroidManifest.xml. 37 | int id = item.getItemId(); 38 | if (id == R.id.action_settings) { 39 | return true; 40 | } 41 | return super.onOptionsItemSelected(item); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /app/src/main/java/org/andydyer/androidtestdemo/ui/WebViewActivity.java: -------------------------------------------------------------------------------- 1 | package org.andydyer.androidtestdemo.ui; 2 | 3 | import android.os.Bundle; 4 | import android.support.v7.app.ActionBarActivity; 5 | import android.view.MenuItem; 6 | import android.webkit.WebView; 7 | import android.webkit.WebViewClient; 8 | 9 | import org.andydyer.androidtestdemo.R; 10 | 11 | import butterknife.ButterKnife; 12 | import butterknife.InjectView; 13 | 14 | /** 15 | * Created by andy on 8/23/14. 16 | */ 17 | public class WebViewActivity extends ActionBarActivity { 18 | 19 | public static final String EXTRA_URL = "url"; 20 | public static final String EXTRA_TITLE = "title"; 21 | 22 | @InjectView(R.id.webview) WebView webview; 23 | 24 | @Override 25 | protected void onCreate(Bundle savedInstanceState) { 26 | super.onCreate(savedInstanceState); 27 | setContentView(R.layout.activity_webview); 28 | ButterKnife.inject(this); 29 | getSupportActionBar().setTitle(getIntent().getStringExtra(EXTRA_TITLE)); 30 | getSupportActionBar().setDisplayHomeAsUpEnabled(true); 31 | loadUrl(); 32 | } 33 | 34 | private void loadUrl() { 35 | String url = getIntent().getStringExtra(EXTRA_URL); 36 | webview.setWebViewClient(new WebViewClient() { 37 | public boolean shouldOverrideUrlLoading(WebView view, String url) { 38 | return false; 39 | } 40 | }); 41 | webview.loadUrl(url); 42 | } 43 | 44 | @Override 45 | public boolean onOptionsItemSelected(MenuItem item) { 46 | switch (item.getItemId()) { 47 | case android.R.id.home: 48 | finish(); 49 | return true; 50 | default: 51 | return super.onOptionsItemSelected(item); 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /app/src/main/java/org/andydyer/androidtestdemo/ui/fragments/EventListFragment.java: -------------------------------------------------------------------------------- 1 | package org.andydyer.androidtestdemo.ui.fragments; 2 | 3 | import android.content.Intent; 4 | import android.os.Bundle; 5 | import android.support.annotation.Nullable; 6 | import android.support.v4.app.Fragment; 7 | import android.support.v7.widget.DefaultItemAnimator; 8 | import android.support.v7.widget.LinearLayoutManager; 9 | import android.support.v7.widget.RecyclerView; 10 | import android.view.LayoutInflater; 11 | import android.view.View; 12 | import android.view.ViewGroup; 13 | import android.widget.ImageView; 14 | import android.widget.TextView; 15 | import android.widget.Toast; 16 | 17 | import com.squareup.picasso.Picasso; 18 | 19 | import org.andydyer.androidtestdemo.DemoApplication; 20 | import org.andydyer.androidtestdemo.R; 21 | import org.andydyer.androidtestdemo.ui.WebViewActivity; 22 | import org.andydyer.androidtestdemo.api.ApiService; 23 | import org.andydyer.androidtestdemo.api.Event; 24 | import org.andydyer.androidtestdemo.api.Events; 25 | import org.andydyer.androidtestdemo.ui.widgets.SimpleDividerItemDecoration; 26 | 27 | import java.util.ArrayList; 28 | import java.util.List; 29 | 30 | import javax.inject.Inject; 31 | 32 | import butterknife.ButterKnife; 33 | import butterknife.InjectView; 34 | import retrofit.Callback; 35 | import retrofit.RetrofitError; 36 | import retrofit.client.Response; 37 | 38 | /** 39 | * Created by andy on 8/23/14. 40 | */ 41 | public class EventListFragment extends Fragment implements Callback { 42 | 43 | @Inject ApiService apiService; 44 | 45 | @InjectView(android.R.id.list) RecyclerView recyclerView; 46 | 47 | private EventsAdapter adapter; 48 | 49 | @Override 50 | public void onCreate(Bundle savedInstanceState) { 51 | super.onCreate(savedInstanceState); 52 | setRetainInstance(true); 53 | } 54 | 55 | @Override 56 | public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { 57 | View view = inflater.inflate(R.layout.fragment_event_list, container, false); 58 | ButterKnife.inject(this, view); 59 | 60 | if(savedInstanceState == null) { 61 | adapter = new EventsAdapter(); 62 | } 63 | 64 | recyclerView.setLayoutManager(new LinearLayoutManager(getActivity())); 65 | recyclerView.setItemAnimator(new DefaultItemAnimator()); 66 | recyclerView.addItemDecoration(new SimpleDividerItemDecoration(getActivity())); 67 | recyclerView.setAdapter(adapter); 68 | 69 | return view; 70 | } 71 | 72 | @Override 73 | public void onViewCreated(View view, Bundle savedInstanceState) { 74 | DemoApplication.getInstance().getGraph().inject(this); 75 | 76 | if(savedInstanceState == null) { 77 | //TODO: Show progress spinner while loading 78 | apiService.getEvents("google", this); 79 | } 80 | } 81 | 82 | @Override 83 | public void success(Events events, Response response) { 84 | adapter.addAll(events); 85 | } 86 | 87 | @Override 88 | public void failure(RetrofitError error) { 89 | Toast.makeText(getActivity(), R.string.unable_to_fech_events, Toast.LENGTH_SHORT).show(); 90 | } 91 | 92 | private class EventsAdapter extends RecyclerView.Adapter { 93 | 94 | private List events = new ArrayList<>(); 95 | 96 | @Override 97 | public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { 98 | View view = LayoutInflater.from(parent.getContext()) 99 | .inflate(R.layout.event_list_item, parent, false); 100 | return new ViewHolder(view); 101 | } 102 | 103 | @Override 104 | public void onBindViewHolder(ViewHolder holder, int position) { 105 | final Event event = events.get(position); 106 | String line1 = String.format("%s by %s", event.getType(), event.getActor().getLogin()); 107 | holder.textLine1.setText(line1); 108 | holder.textLine2.setText(event.getCreatedAtRelativeTime()); 109 | Picasso.with(getActivity()).load(event.getActor().getAvatarUrl()).into(holder.avatar); 110 | holder.avatar.setOnClickListener(new View.OnClickListener() { 111 | @Override 112 | public void onClick(View v) { 113 | launchWebViewActivity(event.getActor().getLogin()); 114 | } 115 | }); 116 | holder.itemView.setOnClickListener(new View.OnClickListener() { 117 | @Override 118 | public void onClick(View v) { 119 | launchWebViewActivity(event.getRepo().getName()); 120 | } 121 | }); 122 | } 123 | 124 | @Override 125 | public int getItemCount() { 126 | return events.size(); 127 | } 128 | 129 | public void addAll(List items) { 130 | int startPosition = events.size(); 131 | events.addAll(items); 132 | notifyItemRangeInserted(startPosition, items.size()); 133 | } 134 | } 135 | 136 | public class ViewHolder extends RecyclerView.ViewHolder { 137 | 138 | @InjectView(R.id.event_list_item_avatar) ImageView avatar; 139 | @InjectView(R.id.event_list_item_text_line1) TextView textLine1; 140 | @InjectView(R.id.event_list_item_text_line2) TextView textLine2; 141 | 142 | public ViewHolder(View view) { 143 | super(view); 144 | ButterKnife.inject(this, view); 145 | } 146 | } 147 | 148 | private void launchWebViewActivity(String path) { 149 | String url = String.format("http://github.com/%s", path); 150 | Intent intent = new Intent(getActivity(), WebViewActivity.class); 151 | intent.putExtra(WebViewActivity.EXTRA_TITLE, path); 152 | intent.putExtra(WebViewActivity.EXTRA_URL, url); 153 | startActivity(intent); 154 | } 155 | } 156 | -------------------------------------------------------------------------------- /app/src/main/java/org/andydyer/androidtestdemo/ui/widgets/SimpleDividerItemDecoration.java: -------------------------------------------------------------------------------- 1 | package org.andydyer.androidtestdemo.ui.widgets; 2 | 3 | import android.content.Context; 4 | import android.graphics.Canvas; 5 | import android.graphics.drawable.Drawable; 6 | import android.support.v7.widget.RecyclerView; 7 | import android.view.View; 8 | 9 | import org.andydyer.androidtestdemo.R; 10 | 11 | /** 12 | * https://gist.github.com/polbins/e37206fbc444207c0e92 13 | */ 14 | public class SimpleDividerItemDecoration extends RecyclerView.ItemDecoration { 15 | private Drawable mDivider; 16 | 17 | public SimpleDividerItemDecoration(Context context) { 18 | mDivider = context.getResources().getDrawable(R.drawable.line_divider); 19 | } 20 | 21 | @Override 22 | public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) { 23 | int left = parent.getPaddingLeft(); 24 | int right = parent.getWidth() - parent.getPaddingRight(); 25 | 26 | int childCount = parent.getChildCount(); 27 | for (int i = 0; i < childCount; i++) { 28 | View child = parent.getChildAt(i); 29 | 30 | RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child.getLayoutParams(); 31 | 32 | int top = child.getBottom() + params.bottomMargin; 33 | int bottom = top + mDivider.getIntrinsicHeight(); 34 | 35 | mDivider.setBounds(left, top, right, bottom); 36 | mDivider.draw(c); 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abdyer/android-test-demo/77d5fd47c44d7527dc6b095baa87ed61438c6aff/app/src/main/res/drawable-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abdyer/android-test-demo/77d5fd47c44d7527dc6b095baa87ed61438c6aff/app/src/main/res/drawable-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abdyer/android-test-demo/77d5fd47c44d7527dc6b095baa87ed61438c6aff/app/src/main/res/drawable-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abdyer/android-test-demo/77d5fd47c44d7527dc6b095baa87ed61438c6aff/app/src/main/res/drawable-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/line_divider.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_login.xml: -------------------------------------------------------------------------------- 1 | 12 | 13 | 14 | 21 | 22 | 27 | 28 | 33 | 34 | 42 | 43 | 54 | 55 |