├── .gitignore ├── README.md ├── app ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── example │ │ └── kotlinandroidhandson │ │ └── ExampleInstrumentedTest.java │ ├── main │ ├── AndroidManifest.xml │ ├── kotlin │ │ └── com │ │ │ └── example │ │ │ └── kotlinandroidhandson │ │ │ ├── App.kt │ │ │ ├── MainActivity.kt │ │ │ ├── RepositoryActivity.kt │ │ │ ├── RepositoryListAdapter.kt │ │ │ ├── RepositoryView.kt │ │ │ ├── client │ │ │ └── GithubClient.kt │ │ │ ├── extensions.kt │ │ │ └── model │ │ │ ├── Page.kt │ │ │ ├── Repository.kt │ │ │ └── User.kt │ └── res │ │ ├── layout │ │ ├── activity_main.xml │ │ ├── activity_repository.xml │ │ └── view_repository.xml │ │ ├── mipmap-hdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-mdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxxhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ └── values │ │ ├── colors.xml │ │ ├── dimens.xml │ │ ├── strings.xml │ │ └── styles.xml │ └── test │ └── java │ └── com │ └── example │ └── kotlinandroidhandson │ └── ExampleUnitTest.java ├── art └── capture.gif ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/workspace.xml 5 | /.idea/libraries 6 | .DS_Store 7 | /build 8 | /captures 9 | .externalNativeBuild 10 | .idea -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Kotlin Android Hands-on 2 | 3 | ## 題材 4 | 5 | GitHubのリポジトリをキーワード検索するアプリを作成します。 6 | 7 | ![rocket](https://raw.github.com/ntaro/kotlin-android-hands-on/master/art/capture.gif) 8 | 9 | ## 事前準備とスタート 10 | 11 | ハンズオンは[masterブランチ](https://github.com/ntaro/kotlin-android-hands-on)から始めます。 12 | 13 | このブランチは、コードや各種設定が記述済みです。 14 | ハンズオン開始までに **ビルド** と **実行** をしておいてください。 15 | 16 | コードは未完成で、ハンズオンの中で完成させていきます。 17 | 18 | ## 完成形 19 | 20 | [completedブランチ](https://github.com/ntaro/kotlin-android-hands-on/tree/completed)は、完成形です。 21 | 22 | ## ハンズオン課題 23 | 24 | ### `Repository`クラスの定義 25 | 26 | GitHub上に存在するリポジトリを表現するクラスを定義しましょう。 27 | 28 | プロパティ名|型|説明 29 | ---|---|--- 30 | `id`|`Long`|ID 31 | `fullName`|`String`|フルネーム=「ユーザ名/リポジトリ名」 32 | `htmlUrl`|`String`|詳細URL 33 | `stargazersCount`|`Int`|スター数 34 | `owner`|`User`|リポジトリの所有者。Userクラスは定義済み 35 | `description`|`String?`|リポジトリの説明 36 | `language`|`String?`|言語 37 | 38 | ### ダミーデータのリスト表示 39 | 40 | ### インテント生成関数 + リポジトリ詳細画面 41 | 42 | ### Retrofitを使ってAPIアクセス 43 | 44 | ### async/awaitの体験 45 | 46 | ### スター付けた人のリスト表示機能を作る -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | apply plugin: 'kotlin-android' 3 | apply plugin: 'kotlin-android-extensions' 4 | 5 | android { 6 | compileSdkVersion 27 7 | buildToolsVersion "27.0.3" 8 | defaultConfig { 9 | applicationId "com.example.kotlinandroidhandson" 10 | minSdkVersion 21 11 | targetSdkVersion 27 12 | versionCode 1 13 | versionName "1.0" 14 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 15 | } 16 | buildTypes { 17 | release { 18 | minifyEnabled false 19 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 20 | } 21 | } 22 | sourceSets { 23 | main.java.srcDirs += 'src/main/kotlin' 24 | } 25 | androidExtensions { 26 | experimental = true 27 | } 28 | } 29 | 30 | dependencies { 31 | implementation fileTree(dir: 'libs', include: ['*.jar']) 32 | androidTestImplementation('com.android.support.test.espresso:espresso-core:2.2.2', { 33 | exclude group: 'com.android.support', module: 'support-annotations' 34 | }) 35 | implementation 'com.android.support:appcompat-v7:27.1.1' 36 | implementation 'com.android.support.constraint:constraint-layout:1.1.0' 37 | androidTestImplementation 'junit:junit:4.12' 38 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" 39 | 40 | implementation 'com.facebook.fresco:fresco:1.9.0' 41 | 42 | def retrofitVersion = '2.3.0' 43 | implementation "com.squareup.retrofit2:retrofit:$retrofitVersion" 44 | implementation "com.squareup.retrofit2:converter-gson:$retrofitVersion" 45 | implementation "com.squareup.retrofit2:adapter-rxjava2:$retrofitVersion" 46 | 47 | implementation 'io.reactivex.rxjava2:rxkotlin:2.1.0' 48 | implementation 'io.reactivex.rxjava2:rxandroid:2.0.1' 49 | 50 | def kodeinVersion = '4.1.0' 51 | implementation "com.github.salomonbrys.kodein:kodein:$kodeinVersion" 52 | implementation "com.github.salomonbrys.kodein:kodein-android:$kodeinVersion" 53 | 54 | def coroutinesVersion = '0.18' 55 | implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutinesVersion" 56 | implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutinesVersion" 57 | implementation "org.jetbrains.kotlinx:kotlinx-coroutines-rx2:$coroutinesVersion" 58 | } 59 | repositories { 60 | mavenCentral() 61 | } 62 | -------------------------------------------------------------------------------- /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 /usr/local/opt/android-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 | 19 | # Uncomment this to preserve the line number information for 20 | # debugging stack traces. 21 | #-keepattributes SourceFile,LineNumberTable 22 | 23 | # If you keep the line number information, uncomment this to 24 | # hide the original source file name. 25 | #-renamesourcefileattribute SourceFile 26 | -------------------------------------------------------------------------------- /app/src/androidTest/java/com/example/kotlinandroidhandson/ExampleInstrumentedTest.java: -------------------------------------------------------------------------------- 1 | package com.example.kotlinandroidhandson; 2 | 3 | import android.content.Context; 4 | import android.support.test.InstrumentationRegistry; 5 | import android.support.test.runner.AndroidJUnit4; 6 | 7 | import org.junit.Test; 8 | import org.junit.runner.RunWith; 9 | 10 | import static org.junit.Assert.*; 11 | 12 | /** 13 | * Instrumentation test, which will execute on an Android device. 14 | * 15 | * @see Testing documentation 16 | */ 17 | @RunWith(AndroidJUnit4.class) 18 | public class ExampleInstrumentedTest { 19 | @Test 20 | public void useAppContext() throws Exception { 21 | // Context of the app under test. 22 | Context appContext = InstrumentationRegistry.getTargetContext(); 23 | 24 | assertEquals("com.example.kotlinandroidhandson", appContext.getPackageName()); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /app/src/main/kotlin/com/example/kotlinandroidhandson/App.kt: -------------------------------------------------------------------------------- 1 | package com.example.kotlinandroidhandson 2 | 3 | import android.app.Application 4 | import com.example.kotlinandroidhandson.client.GithubClient 5 | import com.example.kotlinandroidhandson.model.Page 6 | import com.example.kotlinandroidhandson.model.Repository 7 | import com.facebook.drawee.backends.pipeline.Fresco 8 | import com.github.salomonbrys.kodein.Kodein 9 | import com.github.salomonbrys.kodein.KodeinAware 10 | import com.github.salomonbrys.kodein.bind 11 | import com.github.salomonbrys.kodein.instance 12 | import com.github.salomonbrys.kodein.lazy 13 | import com.github.salomonbrys.kodein.singleton 14 | import com.google.gson.FieldNamingPolicy 15 | import com.google.gson.Gson 16 | import com.google.gson.GsonBuilder 17 | import io.reactivex.Single 18 | import retrofit2.Retrofit 19 | import retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory 20 | import retrofit2.converter.gson.GsonConverterFactory 21 | 22 | class App : Application(), KodeinAware { 23 | 24 | override val kodein: Kodein by Kodein.lazy { 25 | bind() with singleton { 26 | GsonBuilder() 27 | .setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES) 28 | .create() 29 | } 30 | 31 | bind() with singleton { 32 | Retrofit.Builder() 33 | .baseUrl("https://api.github.com") 34 | .addConverterFactory(GsonConverterFactory.create(instance())) 35 | .addCallAdapterFactory(RxJava2CallAdapterFactory.create()) 36 | .build() 37 | } 38 | 39 | // GithubClientの実装をDIに設定 40 | bind() with singleton { 41 | // ここから 42 | object : GithubClient { 43 | override fun search(query: String): Single> = 44 | Single.just(Page(1, listOf(Repository(id = 1)))) 45 | } 46 | // ここまでを削除して、Retrofitで生成したクライアントを返す 47 | } 48 | } 49 | 50 | override fun onCreate() { 51 | super.onCreate() 52 | Fresco.initialize(this) 53 | } 54 | } -------------------------------------------------------------------------------- /app/src/main/kotlin/com/example/kotlinandroidhandson/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package com.example.kotlinandroidhandson 2 | 3 | import android.os.Bundle 4 | import android.support.v7.app.AppCompatActivity 5 | import android.widget.Button 6 | import android.widget.EditText 7 | import android.widget.ListView 8 | import com.example.kotlinandroidhandson.client.GithubClient 9 | import com.github.salomonbrys.kodein.KodeinInjected 10 | import com.github.salomonbrys.kodein.KodeinInjector 11 | import com.github.salomonbrys.kodein.instance 12 | import io.reactivex.disposables.Disposable 13 | import kotlinx.coroutines.experimental.Job 14 | 15 | class MainActivity : AppCompatActivity(), KodeinInjected { 16 | 17 | override val injector: KodeinInjector = KodeinInjector() 18 | 19 | val githubClient: GithubClient by instance() 20 | 21 | var disposable: Disposable? = null 22 | 23 | var job: Job? = null 24 | 25 | override fun onCreate(savedInstanceState: Bundle?) { 26 | super.onCreate(savedInstanceState) 27 | inject(app.kodein) 28 | setContentView(R.layout.activity_main) 29 | 30 | val listAdapter = RepositoryListAdapter(this) 31 | val listView = findViewById(R.id.list_view) 32 | listView.adapter = listAdapter 33 | 34 | // リポジトリリストのアイテムをタップしたときの処理 35 | listView.setOnItemClickListener { _, _, position, _ -> 36 | // タップした位置のリポジトリを取得 37 | val repository = listAdapter.repositories[position] 38 | 39 | /* ここでリポジトリ詳細画面を起動する */ 40 | } 41 | 42 | val searchEditText = findViewById(R.id.search_edit_text) 43 | val searchButton = findViewById