Single activity approach (#30)

* CircleCI config fixes

* Implicit deeplinking via navController navigate

* Removed jacoco.exec files. Closes # 32
This commit is contained in:
Melih Aksoy
2019-08-21 17:15:01 +02:00
parent ac588ad89d
commit 2a90aba88b
36 changed files with 402 additions and 322 deletions

View File

@@ -26,19 +26,66 @@ jobs:
command: fastlane detekt command: fastlane detekt
- store_artifacts: # for display in Artifacts: https://circleci.com/docs/2.0/artifacts/ - store_artifacts: # for display in Artifacts: https://circleci.com/docs/2.0/artifacts/
path: reports/detekt path: reports/detekt
- store_test_results: # for display in Test Summary: https://circleci.com/docs/2.0/collect-test-data/
path: reports
# Tests # Tests
## App
- run: - run:
name: Tests name: Test App
command: | command: |
fastlane test_all fastlane test_app
./gradlew jacocoTestReport ./gradlew app:jacocoTestReport
bash <(curl -s https://codecov.io/bash) bash <(curl -s https://codecov.io/bash)
- store_artifacts: # for display in Artifacts: https://circleci.com/docs/2.0/artifacts/ - store_artifacts: # for display in Artifacts: https://circleci.com/docs/2.0/artifacts/
path: build/reports/tests path: app/build/reports/tests
- store_test_results: # for display in Test Summary: https://circleci.com/docs/2.0/collect-test-data/ - store_test_results: # for display in Test Summary: https://circleci.com/docs/2.0/collect-test-data/
path: reports path: app/build/test-results
## Core
- run:
name: Test Core
command: |
fastlane test_core
./gradlew core:jacocoTestReport
bash <(curl -s https://codecov.io/bash)
- store_artifacts: # for display in Artifacts: https://circleci.com/docs/2.0/artifacts/
path: core/build/reports/tests
- store_test_results: # for display in Test Summary: https://circleci.com/docs/2.0/collect-test-data/
path: core/build/test-results
## Launches
- run:
name: Test Launches
command: |
fastlane test_launches
./gradlew features:launches:jacocoTestReport
bash <(curl -s https://codecov.io/bash)
- store_artifacts: # for display in Artifacts: https://circleci.com/docs/2.0/artifacts/
path: features/launches/build/reports/tests
- store_test_results: # for display in Test Summary: https://circleci.com/docs/2.0/collect-test-data/
path: features/launches/build/test-results
## Detail
- run:
name: Test Detail
command: |
fastlane test_detail
./gradlew features:detail:jacocoTestReport
bash <(curl -s https://codecov.io/bash)
- store_artifacts: # for display in Artifacts: https://circleci.com/docs/2.0/artifacts/
path: features/detail/build/reports/tests
- store_test_results: # for display in Test Summary: https://circleci.com/docs/2.0/collect-test-data/
path: features/detail/build/test-results
## Repository
- run:
name: Test Repository
command: |
fastlane test_repository
./gradlew repository:jacocoTestReport
bash <(curl -s https://codecov.io/bash)
- store_artifacts: # for display in Artifacts: https://circleci.com/docs/2.0/artifacts/
path: repository/build/reports/tests
- store_test_results: # for display in Test Summary: https://circleci.com/docs/2.0/collect-test-data/
path: repository/build/test-results
# See https://circleci.com/docs/2.0/deployment-integrations/ for deploy examples # See https://circleci.com/docs/2.0/deployment-integrations/ for deploy examples

5
.gitignore vendored
View File

@@ -4,6 +4,8 @@
/.idea /.idea
.DS_Store .DS_Store
/build /build
/fastlane/README.md
/fastlane/report.xml
/captures /captures
.externalNativeBuild .externalNativeBuild
/projectFilesBackup /projectFilesBackup
@@ -11,3 +13,6 @@
# Project reports # Project reports
/reports /reports
#jacoco.exec
jacoco.exec

159
Gemfile.lock Normal file
View File

@@ -0,0 +1,159 @@
GEM
remote: https://rubygems.org/
specs:
CFPropertyList (3.0.0)
addressable (2.6.0)
public_suffix (>= 2.0.2, < 4.0)
atomos (0.1.3)
babosa (1.0.2)
claide (1.0.3)
colored (1.2)
colored2 (3.1.2)
commander-fastlane (4.4.6)
highline (~> 1.7.2)
declarative (0.0.10)
declarative-option (0.1.0)
digest-crc (0.4.1)
domain_name (0.5.20190701)
unf (>= 0.0.5, < 1.0.0)
dotenv (2.7.5)
emoji_regex (1.0.1)
excon (0.66.0)
faraday (0.15.4)
multipart-post (>= 1.2, < 3)
faraday-cookie_jar (0.0.6)
faraday (>= 0.7.4)
http-cookie (~> 1.0.0)
faraday_middleware (0.13.1)
faraday (>= 0.7.4, < 1.0)
fastimage (2.1.5)
fastlane (2.129.0)
CFPropertyList (>= 2.3, < 4.0.0)
addressable (>= 2.3, < 3.0.0)
babosa (>= 1.0.2, < 2.0.0)
bundler (>= 1.12.0, < 3.0.0)
colored
commander-fastlane (>= 4.4.6, < 5.0.0)
dotenv (>= 2.1.1, < 3.0.0)
emoji_regex (>= 0.1, < 2.0)
excon (>= 0.45.0, < 1.0.0)
faraday (~> 0.9)
faraday-cookie_jar (~> 0.0.6)
faraday_middleware (~> 0.9)
fastimage (>= 2.1.0, < 3.0.0)
gh_inspector (>= 1.1.2, < 2.0.0)
google-api-client (>= 0.21.2, < 0.24.0)
google-cloud-storage (>= 1.15.0, < 2.0.0)
highline (>= 1.7.2, < 2.0.0)
json (< 3.0.0)
jwt (~> 2.1.0)
mini_magick (>= 4.9.4, < 5.0.0)
multi_xml (~> 0.5)
multipart-post (~> 2.0.0)
plist (>= 3.1.0, < 4.0.0)
public_suffix (~> 2.0.0)
rubyzip (>= 1.2.2, < 2.0.0)
security (= 0.1.3)
simctl (~> 1.6.3)
slack-notifier (>= 2.0.0, < 3.0.0)
terminal-notifier (>= 2.0.0, < 3.0.0)
terminal-table (>= 1.4.5, < 2.0.0)
tty-screen (>= 0.6.3, < 1.0.0)
tty-spinner (>= 0.8.0, < 1.0.0)
word_wrap (~> 1.0.0)
xcodeproj (>= 1.8.1, < 2.0.0)
xcpretty (~> 0.3.0)
xcpretty-travis-formatter (>= 0.0.3)
gh_inspector (1.1.3)
google-api-client (0.23.9)
addressable (~> 2.5, >= 2.5.1)
googleauth (>= 0.5, < 0.7.0)
httpclient (>= 2.8.1, < 3.0)
mime-types (~> 3.0)
representable (~> 3.0)
retriable (>= 2.0, < 4.0)
signet (~> 0.9)
google-cloud-core (1.3.0)
google-cloud-env (~> 1.0)
google-cloud-env (1.2.0)
faraday (~> 0.11)
google-cloud-storage (1.16.0)
digest-crc (~> 0.4)
google-api-client (~> 0.23)
google-cloud-core (~> 1.2)
googleauth (>= 0.6.2, < 0.10.0)
googleauth (0.6.7)
faraday (~> 0.12)
jwt (>= 1.4, < 3.0)
memoist (~> 0.16)
multi_json (~> 1.11)
os (>= 0.9, < 2.0)
signet (~> 0.7)
highline (1.7.10)
http-cookie (1.0.3)
domain_name (~> 0.5)
httpclient (2.8.3)
json (2.2.0)
jwt (2.1.0)
memoist (0.16.0)
mime-types (3.2.2)
mime-types-data (~> 3.2015)
mime-types-data (3.2019.0331)
mini_magick (4.9.5)
multi_json (1.13.1)
multi_xml (0.6.0)
multipart-post (2.0.0)
nanaimo (0.2.6)
naturally (2.2.0)
os (1.0.1)
plist (3.5.0)
public_suffix (2.0.5)
representable (3.0.4)
declarative (< 0.1.0)
declarative-option (< 0.2.0)
uber (< 0.2.0)
retriable (3.1.2)
rouge (2.0.7)
rubyzip (1.2.3)
security (0.1.3)
signet (0.11.0)
addressable (~> 2.3)
faraday (~> 0.9)
jwt (>= 1.5, < 3.0)
multi_json (~> 1.10)
simctl (1.6.5)
CFPropertyList
naturally
slack-notifier (2.3.2)
terminal-notifier (2.0.0)
terminal-table (1.8.0)
unicode-display_width (~> 1.1, >= 1.1.1)
tty-cursor (0.7.0)
tty-screen (0.7.0)
tty-spinner (0.9.1)
tty-cursor (~> 0.7)
uber (0.1.0)
unf (0.1.4)
unf_ext
unf_ext (0.0.7.6)
unicode-display_width (1.6.0)
word_wrap (1.0.0)
xcodeproj (1.12.0)
CFPropertyList (>= 2.3.3, < 4.0)
atomos (~> 0.1.3)
claide (>= 1.0.2, < 2.0)
colored2 (~> 3.1)
nanaimo (~> 0.2.6)
xcpretty (0.3.0)
rouge (~> 2.0.7)
xcpretty-travis-formatter (1.0.0)
xcpretty (~> 0.2, >= 0.0.7)
PLATFORMS
ruby
DEPENDENCIES
fastlane
BUNDLED WITH
2.0.1

View File

@@ -37,4 +37,7 @@ dependencies {
compileOnly libraries.retrofit compileOnly libraries.retrofit
compileOnly libraries.room compileOnly libraries.room
compileOnly libraries.paging compileOnly libraries.paging
// Need for proper renders in xml previews
compileOnly libraries.constraintLayout
} }

View File

@@ -1,4 +1,5 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android" <manifest
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
package="com.melih.rocketscience"> package="com.melih.rocketscience">
@@ -13,5 +14,15 @@
android:roundIcon="@mipmap/ic_launcher_round" android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true" android:supportsRtl="true"
android:theme="@style/AppTheme" android:theme="@style/AppTheme"
tools:ignore="GoogleAppIndexingWarning" /> tools:ignore="GoogleAppIndexingWarning">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<nav-graph android:value="@navigation/nav_main" />
</activity>
</application>
</manifest> </manifest>

View File

@@ -0,0 +1,31 @@
package com.melih.rocketscience
import android.os.Bundle
import androidx.databinding.DataBindingUtil
import androidx.navigation.NavController
import androidx.navigation.findNavController
import androidx.navigation.ui.NavigationUI
import com.melih.rocketscience.databinding.MainActivityBinding
import dagger.android.support.DaggerAppCompatActivity
class MainActivity : DaggerAppCompatActivity() {
private lateinit var binding: MainActivityBinding
private lateinit var navController: NavController
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = DataBindingUtil.setContentView(this, R.layout.activity_main)
navController = findNavController(R.id.nav_host_fragment)
NavigationUI.setupWithNavController(binding.toolbar, navController)
}
override fun onSupportNavigateUp(): Boolean {
if (!NavigationUI.navigateUp(navController, null)) {
onBackPressed()
}
return true
}
}

View File

@@ -1,8 +1,6 @@
package com.melih.rocketscience.di package com.melih.rocketscience.di
import com.melih.core.di.CoreComponent import com.melih.core.di.CoreComponent
import com.melih.detail.di.DetailFeatureModule
import com.melih.list.di.LaunchesFeatureModule
import com.melih.rocketscience.App import com.melih.rocketscience.App
import dagger.Component import dagger.Component
import dagger.android.AndroidInjectionModule import dagger.android.AndroidInjectionModule
@@ -11,8 +9,7 @@ import dagger.android.AndroidInjector
@AppScope @AppScope
@Component( @Component(
modules = [AndroidInjectionModule::class, modules = [AndroidInjectionModule::class,
LaunchesFeatureModule::class, AppModule::class],
DetailFeatureModule::class],
dependencies = [CoreComponent::class] dependencies = [CoreComponent::class]
) )

View File

@@ -0,0 +1,19 @@
package com.melih.rocketscience.di
import com.melih.detail.di.DetailContributor
import com.melih.list.di.LaunchesContributor
import com.melih.rocketscience.MainActivity
import dagger.Module
import dagger.android.ContributesAndroidInjector
@Module
abstract class AppModule {
@ContributesAndroidInjector(
modules = [
LaunchesContributor::class,
DetailContributor::class]
)
abstract fun mainActivity(): MainActivity
}

View File

@@ -1,7 +1,9 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"> <layout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<data class="LaunchesActivityBinding" /> <data class="MainActivityBinding" />
<LinearLayout <LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"
@@ -14,9 +16,12 @@
android:layout_height="?android:actionBarSize" android:layout_height="?android:actionBarSize"
android:background="@color/colorPrimary" /> android:background="@color/colorPrimary" />
<FrameLayout <fragment
android:id="@+id/container" android:id="@+id/nav_host_fragment"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" /> android:layout_height="match_parent"
app:defaultNavHost="true"
app:navGraph="@navigation/nav_main" />
</LinearLayout> </LinearLayout>
</layout> </layout>

View File

@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<navigation
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/nav_main"
app:startDestination="@id/nav_launches">
<include app:graph="@navigation/nav_launches" />
<include app:graph="@navigation/nav_detail" />
</navigation>

View File

@@ -9,7 +9,7 @@ buildscript {
} }
dependencies { dependencies {
classpath 'com.android.tools.build:gradle:3.5.0-rc03' classpath 'com.android.tools.build:gradle:3.5.0'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
classpath "org.jetbrains.dokka:dokka-android-gradle-plugin:0.9.18" classpath "org.jetbrains.dokka:dokka-android-gradle-plugin:0.9.18"
classpath "de.mannodermaus.gradle.plugins:android-junit5:1.5.0.0" classpath "de.mannodermaus.gradle.plugins:android-junit5:1.5.0.0"

Binary file not shown.

View File

@@ -1,5 +1,6 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android" <manifest
package="com.melih.core"> xmlns:android="http://schemas.android.com/apk/res/android"
package="com.melih.core">
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
</manifest> </manifest>

View File

@@ -1,16 +1,12 @@
package com.melih.core.actions package com.melih.core.actions
import android.content.Intent import android.net.Uri
import androidx.fragment.app.Fragment
const val EXTRA_LAUNCH_ID = "extras:detail:launchid" import androidx.navigation.fragment.NavHostFragment
import com.melih.core.R
/** /**
* Navigation actions for navigation between feature activities * Navigation actions for navigation between feature activities
*/ */
object Actions { fun Fragment.openDetail(id: Long) =
NavHostFragment.findNavController(this).navigate(Uri.parse(getString(R.string.detail_uri, id)))
fun openDetailFor(id: Long) =
Intent("action.dashboard.open")
.putExtra(EXTRA_LAUNCH_ID, id)
}

View File

@@ -1,56 +0,0 @@
package com.melih.core.base.lifecycle
import android.os.Bundle
import androidx.annotation.IdRes
import androidx.annotation.LayoutRes
import androidx.databinding.DataBindingUtil
import androidx.databinding.ViewDataBinding
import androidx.navigation.fragment.NavHostFragment
import androidx.navigation.ui.NavigationUI
import dagger.android.support.DaggerAppCompatActivity
const val NAV_HOST_FRAGMENT_TAG = "nav_host_fragment_tag"
/**
* Base class of all Activity classes
*/
abstract class BaseActivity<T : ViewDataBinding> : DaggerAppCompatActivity() {
protected lateinit var binding: T
protected lateinit var navHostFragment: NavHostFragment
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = DataBindingUtil.setContentView(this, getLayoutId())
binding.lifecycleOwner = this
if (savedInstanceState == null) {
navHostFragment = createNavHostFragment()
supportFragmentManager
.beginTransaction()
.add(addNavHostTo(), navHostFragment, NAV_HOST_FRAGMENT_TAG)
.commitNow()
} else {
navHostFragment = supportFragmentManager
.findFragmentByTag(NAV_HOST_FRAGMENT_TAG) as NavHostFragment
}
}
override fun onSupportNavigateUp(): Boolean {
if (!NavigationUI.navigateUp(navHostFragment.navController, null)) {
onBackPressed()
}
return true
}
@LayoutRes
abstract fun getLayoutId(): Int
abstract fun createNavHostFragment(): NavHostFragment
@IdRes
abstract fun addNavHostTo(): Int
}

View File

@@ -8,9 +8,8 @@ import androidx.annotation.LayoutRes
import androidx.databinding.DataBindingUtil import androidx.databinding.DataBindingUtil
import androidx.databinding.ViewDataBinding import androidx.databinding.ViewDataBinding
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.navigation.NavController
import androidx.navigation.fragment.NavHostFragment
import com.google.android.material.snackbar.Snackbar import com.google.android.material.snackbar.Snackbar
import com.melih.core.R
import com.melih.repository.interactors.base.Reason import com.melih.repository.interactors.base.Reason
/** /**
@@ -23,7 +22,6 @@ abstract class BaseFragment<T : ViewDataBinding> : Fragment() {
// region Properties // region Properties
protected lateinit var navController: NavController
protected lateinit var binding: T protected lateinit var binding: T
// endregion // endregion
@@ -34,7 +32,6 @@ abstract class BaseFragment<T : ViewDataBinding> : Fragment() {
container: ViewGroup?, container: ViewGroup?,
savedInstanceState: Bundle? savedInstanceState: Bundle?
): View? { ): View? {
navController = NavHostFragment.findNavController(this)
binding = DataBindingUtil.inflate(inflater, getLayoutId(), container, false) binding = DataBindingUtil.inflate(inflater, getLayoutId(), container, false)
binding.lifecycleOwner = this binding.lifecycleOwner = this
return binding.root return binding.root
@@ -45,7 +42,7 @@ abstract class BaseFragment<T : ViewDataBinding> : Fragment() {
binding.root, binding.root,
resources.getString(reason.messageRes), resources.getString(reason.messageRes),
Snackbar.LENGTH_INDEFINITE Snackbar.LENGTH_INDEFINITE
).setAction(com.melih.core.R.string.retry) { ).setAction(R.string.retry) {
block() block()
}.show() }.show()
} }

View File

@@ -1,15 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE
resources[<!ENTITY deeplinkNamespace "rocket-science://"><!ENTITY deeplinkDetailPath "&deeplinkNamespace;detail"><!ENTITY paramLaunchId "launch-id">]>
<resources> <resources>
<string name="dummy_long_text">Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor <string name="retry">Retry</string>
incididunt ut labore et dolore
magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo
consequat. Duis aute irure dolor in
reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat
non proident, sunt in culpa qui officia
deserunt mollit anim id est laborum
</string>
<string name="retry">Retry</string> <string name="detail_param_launch_id">&paramLaunchId;</string>
<string name="detail_deeplink">&deeplinkDetailPath;/{&paramLaunchId;}</string>
<!--Actions--> <string name="detail_uri">&deeplinkDetailPath;/%1d</string>
<string name="action_detail">action.detail.open</string>
</resources> </resources>

View File

@@ -30,9 +30,29 @@ platform :android do
run_detekt() run_detekt()
end end
desc "Runs all tests in all modules" desc "Runs tests in app module"
lane :test_all do lane :test_app do
run_all_tests() run_app_tests()
end
desc "Runs tests in core module"
lane :test_core do
run_core_tests()
end
desc "Runs tests in launches module"
lane :test_launches do
run_launches_tests()
end
desc "Runs tests in detail module"
lane :test_detail do
run_detail_tests()
end
desc "Runs tests in repository module"
lane :test_repository do
run_repository_tests()
end end
# ================ Gradle tasks ================ # ================ Gradle tasks ================
@@ -45,7 +65,23 @@ platform :android do
gradle(task: "removeReports") gradle(task: "removeReports")
end end
def run_all_tests def run_app_tests
gradle(task: "clean test --continue") gradle(task: "app:test --continue")
end
def run_core_tests
gradle(task: "core:test --continue")
end
def run_launches_tests
gradle(task: "features:launches:test --continue")
end
def run_detail_tests
gradle(task: "features:detail:test --continue")
end
def run_repository_tests
gradle(task: "repository:test --continue")
end end
end end

View File

@@ -1,7 +1,6 @@
apply plugin: 'com.android.library' apply plugin: 'com.android.library'
apply plugin: 'kotlin-android' apply plugin: 'kotlin-android'
apply plugin: 'kotlin-kapt' apply plugin: 'kotlin-kapt'
apply plugin: "androidx.navigation.safeargs"
apply from: "$rootProject.projectDir/scripts/feature_module.gradle" apply from: "$rootProject.projectDir/scripts/feature_module.gradle"

Binary file not shown.

View File

@@ -1,14 +1 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android" <manifest package="com.melih.detail" />
package="com.melih.detail">
<application>
<activity
android:name="com.melih.detail.ui.DetailActivity"
android:theme="@style/AppTheme">
<intent-filter>
<action android:name="action.dashboard.open" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
</application>
</manifest>

View File

@@ -1,24 +0,0 @@
package com.melih.detail.di
import com.melih.detail.ui.DetailActivity
import com.melih.list.di.scopes.DetailScope
import dagger.Module
import dagger.android.ContributesAndroidInjector
/**
* Contributes fragments & view models in this module
*/
@Module
abstract class DetailFeatureModule {
// region Contributes
@ContributesAndroidInjector(
modules = [
DetailContributor::class
]
)
@DetailScope
abstract fun detailActivity(): DetailActivity
// endregion
}

View File

@@ -32,7 +32,7 @@ abstract class DetailFragmentModule {
@Provides @Provides
@JvmStatic @JvmStatic
fun provideGetLaunchDetailParams(fragment: DetailFragment): GetLaunchDetails.Params { fun provideGetLaunchDetailParams(fragment: DetailFragment): GetLaunchDetails.Params {
val args: DetailFragmentArgs by fragment.navArgs() val args by fragment.navArgs<DetailFragmentArgs>()
return GetLaunchDetails.Params(args.launchId) return GetLaunchDetails.Params(args.launchId)
} }
} }

View File

@@ -1,37 +0,0 @@
package com.melih.detail.ui
import android.os.Bundle
import androidx.navigation.fragment.NavHostFragment
import com.melih.core.actions.EXTRA_LAUNCH_ID
import com.melih.core.base.lifecycle.BaseActivity
import com.melih.detail.R
import com.melih.detail.databinding.DetailActivityBinding
const val INVALID_LAUNCH_ID = -1L
class DetailActivity : BaseActivity<DetailActivityBinding>() {
// region Functions
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setSupportActionBar(binding.toolbar)
supportActionBar?.setDisplayHomeAsUpEnabled(true)
supportActionBar?.setDisplayShowHomeEnabled(true)
}
override fun getLayoutId(): Int = R.layout.activity_detail
override fun createNavHostFragment() =
NavHostFragment.create(
R.navigation.nav_detail,
DetailFragmentArgs.Builder()
.setLaunchId(intent?.extras?.getLong(EXTRA_LAUNCH_ID) ?: INVALID_LAUNCH_ID)
.build()
.toBundle()
)
override fun addNavHostTo(): Int = R.id.container
// endregion
}

View File

@@ -1,22 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<data class="DetailActivityBinding" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?android:actionBarSize"
android:background="@color/colorPrimary" />
<FrameLayout
android:id="@+id/container"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>
</layout>

View File

@@ -1,18 +1,22 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<navigation xmlns:android="http://schemas.android.com/apk/res/android" <navigation
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools" xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/nav_detail" xmlns:tools="http://schemas.android.com/tools"
app:startDestination="@id/detailFragment"> android:id="@+id/nav_detail"
app:startDestination="@id/detailFragment">
<fragment <fragment
android:id="@+id/detailFragment" android:id="@+id/detailFragment"
android:name="com.melih.detail.ui.DetailFragment" android:name="com.melih.detail.ui.DetailFragment"
android:label="DetailFragment" android:label="DetailFragment"
tools:layout="@layout/fragment_detail"> tools:layout="@layout/fragment_detail">
<argument <argument
android:name="launchId" android:name="launch-id"
android:defaultValue="-1L" android:defaultValue="0L"
app:argType="long" /> app:argType="long" />
</fragment> <deepLink
android:id="@+id/detailDeepLink"
app:uri="@string/detail_deeplink" />
</fragment>
</navigation> </navigation>

View File

@@ -1,3 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources> <resources>
<string name="cd_rocket_image">Image of the rocket</string> <string name="cd_rocket_image">Image of the rocket</string>
</resources> </resources>

Binary file not shown.

View File

@@ -1,14 +1 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android" <manifest package="com.melih.list" />
package="com.melih.list">
<application>
<activity
android:name="com.melih.list.ui.LaunchesActivity"
android:theme="@style/AppTheme">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>

View File

@@ -1,24 +0,0 @@
package com.melih.list.di
import com.melih.list.di.scopes.LaunchesScope
import com.melih.list.ui.LaunchesActivity
import dagger.Module
import dagger.android.ContributesAndroidInjector
/**
* Contributes fragments & view models in this module
*/
@Module
abstract class LaunchesFeatureModule {
// region Contributes
@ContributesAndroidInjector(
modules = [
LaunchesContributor::class
]
)
@LaunchesScope
abstract fun launchesActivity(): LaunchesActivity
// endregion
}

View File

@@ -1,26 +0,0 @@
package com.melih.list.ui
import android.os.Bundle
import androidx.navigation.fragment.NavHostFragment
import com.melih.core.base.lifecycle.BaseActivity
import com.melih.list.R
import com.melih.list.databinding.LaunchesActivityBinding
class LaunchesActivity : BaseActivity<LaunchesActivityBinding>() {
// region Functions
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setSupportActionBar(binding.toolbar)
}
override fun getLayoutId(): Int = R.layout.activity_launches
override fun createNavHostFragment() =
NavHostFragment.create(R.navigation.nav_launches)
override fun addNavHostTo(): Int = R.id.container
// endregion
}

View File

@@ -3,7 +3,7 @@ package com.melih.list.ui
import android.os.Bundle import android.os.Bundle
import android.view.View import android.view.View
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
import com.melih.core.actions.Actions import com.melih.core.actions.openDetail
import com.melih.core.base.lifecycle.BaseDaggerFragment import com.melih.core.base.lifecycle.BaseDaggerFragment
import com.melih.core.extensions.createFor import com.melih.core.extensions.createFor
import com.melih.core.extensions.observe import com.melih.core.extensions.observe
@@ -36,17 +36,6 @@ class LaunchesFragment : BaseDaggerFragment<ListBinding>(), SwipeRefreshLayout.O
observeDataChanges() observeDataChanges()
} }
//override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
// inflater.inflate(R.menu.menu_rocket_list, menu)
//
// with(menu.findItem(R.id.search)) {
// onExpandOrCollapse(::onSearchExpand, ::onSearchCollapse)
// setSearchQueryListener(actionView as SearchView)
// }
//
// super.onCreateOptionsMenu(menu, inflater)
//}
private fun observeDataChanges() { private fun observeDataChanges() {
// Observing state to show loading // Observing state to show loading
@@ -64,30 +53,12 @@ class LaunchesFragment : BaseDaggerFragment<ListBinding>(), SwipeRefreshLayout.O
observe(viewModel.pagedList) { observe(viewModel.pagedList) {
launchesAdapter.submitList(it) launchesAdapter.submitList(it)
} }
//observe(viewModel.filteredItems) {
// launchesAdapter.submitList(it)
//}
} }
private fun onItemSelected(item: LaunchEntity) { private fun onItemSelected(item: LaunchEntity) {
startActivity(Actions.openDetailFor(item.id)) openDetail(item.id)
} }
//private fun onSearchExpand() {
// binding.swipeRefreshLayout.isEnabled = false
//}
//private fun onSearchCollapse() {
// binding.swipeRefreshLayout.isEnabled = true
//}
//private fun setSearchQueryListener(searchView: SearchView) {
// searchView.setOnQueryChangedListener {
// viewModel.filterItemListBy(it)
// }
//}
override fun onRefresh() { override fun onRefresh() {
viewModel.refresh() viewModel.refresh()
} }

View File

@@ -1,5 +1,6 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<navigation xmlns:android="http://schemas.android.com/apk/res/android" <navigation
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/nav_launches" android:id="@+id/nav_launches"

View File

@@ -10,4 +10,8 @@ android {
versionName "1.0" versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
} }
testOptions.unitTests.all {
jvmArgs "-XX:+UnlockExperimentalVMOptions", "-XX:+UseCGroupMemoryLimitForHeap"
}
} }

View File

@@ -1,3 +1,4 @@
apply plugin: "androidx.navigation.safeargs"
apply from: "$rootProject.projectDir/scripts/module.gradle" apply from: "$rootProject.projectDir/scripts/module.gradle"
android { android {

View File

@@ -1,3 +1,3 @@
apply from: "$rootProject.projectDir/scripts/default_android_config.gradle" apply from: "$rootProject.projectDir/scripts/default_android_config.gradle"
apply from: "$rootProject.projectDir/scripts/sources.gradle" apply from: "$rootProject.projectDir/scripts/sources.gradle"
apply from: "$rootProject.projectDir/scripts/flavors.gradle" apply from: "$rootProject.projectDir/scripts/flavors.gradle"