├── .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 | 
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