Skip to content
GitLab
Menu
Projects
Groups
Snippets
Help
What's new
4
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Switch to GitLab Next
Sign in / Register
Toggle navigation
Menu
Open sidebar
Mark Murphy
cw-andexplore
Commits
90fa737f
Commit
90fa737f
authored
May 05, 2019
by
Mark Murphy
Browse files
new tutorials
parent
988b9dc6
Changes
978
Hide whitespace changes
Inline
Side-by-side
T19-DI/ToDo/.gitignore
0 → 100644
View file @
90fa737f
*.iml
.gradle
/local.properties
/.idea/caches
/.idea/libraries
/.idea/modules.xml
/.idea/workspace.xml
/.idea/navEditor.xml
/.idea/assetWizardSettings.xml
.DS_Store
/build
/captures
.externalNativeBuild
T19-DI/ToDo/app/.gitignore
0 → 100644
View file @
90fa737f
/build
T19-DI/ToDo/app/build.gradle
0 → 100644
View file @
90fa737f
apply
plugin:
'com.android.application'
apply
plugin:
'kotlin-android'
apply
plugin:
'kotlin-android-extensions'
apply
plugin:
'kotlin-kapt'
apply
plugin:
'androidx.navigation.safeargs.kotlin'
android
{
compileSdkVersion
28
defaultConfig
{
applicationId
"com.commonsware.todo"
minSdkVersion
21
targetSdkVersion
28
versionCode
1
versionName
"1.0"
testInstrumentationRunner
"androidx.test.runner.AndroidJUnitRunner"
}
buildTypes
{
release
{
minifyEnabled
false
proguardFiles
getDefaultProguardFile
(
'proguard-android-optimize.txt'
),
'proguard-rules.pro'
}
}
androidExtensions
{
experimental
=
true
}
dataBinding
{
enabled
=
true
}
}
dependencies
{
implementation
"org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
implementation
'androidx.appcompat:appcompat:1.0.2'
implementation
'androidx.core:core-ktx:1.0.1'
implementation
'androidx.constraintlayout:constraintlayout:1.1.3'
implementation
'androidx.recyclerview:recyclerview:1.0.0'
implementation
'androidx.fragment:fragment-ktx:1.0.0'
implementation
"androidx.navigation:navigation-fragment-ktx:$nav_version"
implementation
"androidx.navigation:navigation-ui-ktx:$nav_version"
implementation
"org.koin:koin-core:$koin_version"
implementation
"org.koin:koin-android:$koin_version"
testImplementation
'junit:junit:4.12'
androidTestImplementation
'androidx.test:runner:1.1.1'
androidTestImplementation
'androidx.test.espresso:espresso-core:3.1.1'
}
T19-DI/ToDo/app/proguard-rules.pro
0 → 100644
View file @
90fa737f
# Add project specific ProGuard rules here.
#
You
can
control
the
set
of
applied
configuration
files
using
the
#
proguardFiles
setting
in
build
.
gradle
.
#
#
For
more
details
,
see
#
http
://
developer
.
android
.
com
/
guide
/
developing
/
tools
/
proguard
.
html
#
If
your
project
uses
WebView
with
JS
,
uncomment
the
following
#
and
specify
the
fully
qualified
class
name
to
the
JavaScript
interface
#
class
:
#-
keepclassmembers
class
fqcn
.
of
.
javascript
.
interface
.
for
.
webview
{
#
public
*
;
#
}
#
Uncomment
this
to
preserve
the
line
number
information
for
#
debugging
stack
traces
.
#-
keepattributes
SourceFile
,
LineNumberTable
#
If
you
keep
the
line
number
information
,
uncomment
this
to
#
hide
the
original
source
file
name
.
#-
renamesourcefileattribute
SourceFile
T19-DI/ToDo/app/src/androidTest/java/com/commonsware/todo/ExampleInstrumentedTest.kt
0 → 100644
View file @
90fa737f
package
com.commonsware.todo
import
androidx.test.InstrumentationRegistry
import
androidx.test.runner.AndroidJUnit4
import
org.junit.Test
import
org.junit.runner.RunWith
import
org.junit.Assert.*
/**
* Instrumented test, which will execute on an Android device.
*
* See [testing documentation](http://d.android.com/tools/testing).
*/
@RunWith
(
AndroidJUnit4
::
class
)
class
ExampleInstrumentedTest
{
@Test
fun
useAppContext
()
{
// Context of the app under test.
val
appContext
=
InstrumentationRegistry
.
getTargetContext
()
assertEquals
(
"com.commonsware.todo"
,
appContext
.
packageName
)
}
}
T19-DI/ToDo/app/src/main/AndroidManifest.xml
0 → 100644
View file @
90fa737f
<?xml version="1.0" encoding="utf-8"?>
<manifest
package=
"com.commonsware.todo"
xmlns:android=
"http://schemas.android.com/apk/res/android"
>
<supports-screens
android:largeScreens=
"true"
android:normalScreens=
"true"
android:smallScreens=
"true"
android:xlargeScreens=
"true"
/>
<application
android:name=
".ToDoApp"
android:allowBackup=
"false"
android:icon=
"@mipmap/ic_launcher"
android:label=
"@string/app_name"
android:roundIcon=
"@mipmap/ic_launcher_round"
android:supportsRtl=
"true"
android:theme=
"@style/AppTheme"
>
<activity
android:name=
".AboutActivity"
></activity>
<activity
android:name=
".MainActivity"
>
<intent-filter>
<action
android:name=
"android.intent.action.MAIN"
/>
<category
android:name=
"android.intent.category.LAUNCHER"
/>
</intent-filter>
</activity>
</application>
</manifest>
\ No newline at end of file
T19-DI/ToDo/app/src/main/assets/about.html
0 → 100644
View file @
90fa737f
<h1>
About This App
</h1>
<p>
This app is cool!
</p>
<p>
No, really
—
this app is awesome!
</p>
<div>
.
<br/>
.
<br/>
.
<br/>
.
</div>
<p>
OK, this app isn't all that much. But, hey, it's mine!
</p>
\ No newline at end of file
T19-DI/ToDo/app/src/main/java/com/commonsware/todo/AboutActivity.kt
0 → 100644
View file @
90fa737f
package
com.commonsware.todo
import
androidx.appcompat.app.AppCompatActivity
import
android.os.Bundle
import
kotlinx.android.synthetic.main.activity_about.*
class
AboutActivity
:
AppCompatActivity
()
{
override
fun
onCreate
(
savedInstanceState
:
Bundle
?)
{
super
.
onCreate
(
savedInstanceState
)
setContentView
(
R
.
layout
.
activity_about
)
toolbar
.
title
=
getString
(
R
.
string
.
app_name
)
about
.
loadUrl
(
"file:///android_asset/about.html"
)
}
}
T19-DI/ToDo/app/src/main/java/com/commonsware/todo/BindingAdapters.kt
0 → 100644
View file @
90fa737f
package
com.commonsware.todo
import
android.text.format.DateUtils
import
android.widget.TextView
import
androidx.databinding.BindingAdapter
import
java.util.*
@BindingAdapter
(
"formattedDate"
)
fun
TextView
.
formattedDate
(
date
:
Calendar
?)
{
date
?.
let
{
text
=
DateUtils
.
getRelativeDateTimeString
(
context
,
date
.
timeInMillis
,
DateUtils
.
MINUTE_IN_MILLIS
,
DateUtils
.
WEEK_IN_MILLIS
,
0
)
}
}
T19-DI/ToDo/app/src/main/java/com/commonsware/todo/DisplayFragment.kt
0 → 100644
View file @
90fa737f
package
com.commonsware.todo
import
android.os.Bundle
import
android.view.*
import
androidx.fragment.app.Fragment
import
androidx.navigation.fragment.findNavController
import
androidx.navigation.fragment.navArgs
import
com.commonsware.todo.databinding.TodoDisplayBinding
import
org.koin.android.ext.android.inject
class
DisplayFragment
:
Fragment
()
{
private
val
args
:
DisplayFragmentArgs
by
navArgs
()
private
lateinit
var
binding
:
TodoDisplayBinding
private
val
repo
:
ToDoRepository
by
inject
()
override
fun
onCreate
(
savedInstanceState
:
Bundle
?)
{
super
.
onCreate
(
savedInstanceState
)
setHasOptionsMenu
(
true
)
}
override
fun
onCreateView
(
inflater
:
LayoutInflater
,
container
:
ViewGroup
?,
savedInstanceState
:
Bundle
?
)
=
TodoDisplayBinding
.
inflate
(
inflater
,
container
,
false
)
.
apply
{
binding
=
this
}
.
root
override
fun
onViewCreated
(
view
:
View
,
savedInstanceState
:
Bundle
?)
{
binding
.
model
=
repo
.
find
(
args
.
modelId
)
}
override
fun
onCreateOptionsMenu
(
menu
:
Menu
,
inflater
:
MenuInflater
)
{
inflater
.
inflate
(
R
.
menu
.
actions_display
,
menu
)
super
.
onCreateOptionsMenu
(
menu
,
inflater
)
}
override
fun
onOptionsItemSelected
(
item
:
MenuItem
):
Boolean
{
when
(
item
.
itemId
)
{
R
.
id
.
edit
->
{
edit
();
return
true
;
}
}
return
super
.
onOptionsItemSelected
(
item
)
}
private
fun
edit
()
{
findNavController
().
navigate
(
DisplayFragmentDirections
.
editModel
(
args
.
modelId
))
}
}
\ No newline at end of file
T19-DI/ToDo/app/src/main/java/com/commonsware/todo/EditFragment.kt
0 → 100644
View file @
90fa737f
package
com.commonsware.todo
import
android.os.Bundle
import
android.view.*
import
android.view.inputmethod.InputMethodManager
import
androidx.core.content.getSystemService
import
androidx.fragment.app.Fragment
import
androidx.navigation.fragment.findNavController
import
androidx.navigation.fragment.navArgs
import
com.commonsware.todo.databinding.TodoEditBinding
import
org.koin.android.ext.android.inject
class
EditFragment
:
Fragment
()
{
private
lateinit
var
binding
:
TodoEditBinding
private
val
args
:
EditFragmentArgs
by
navArgs
()
private
val
repo
:
ToDoRepository
by
inject
()
override
fun
onCreate
(
savedInstanceState
:
Bundle
?)
{
super
.
onCreate
(
savedInstanceState
)
setHasOptionsMenu
(
true
)
}
override
fun
onCreateView
(
inflater
:
LayoutInflater
,
container
:
ViewGroup
?,
savedInstanceState
:
Bundle
?
)
=
TodoEditBinding
.
inflate
(
inflater
,
container
,
false
)
.
apply
{
binding
=
this
}
.
root
override
fun
onViewCreated
(
view
:
View
,
savedInstanceState
:
Bundle
?)
{
binding
.
model
=
repo
.
find
(
args
.
modelId
)
}
override
fun
onCreateOptionsMenu
(
menu
:
Menu
,
inflater
:
MenuInflater
)
{
inflater
.
inflate
(
R
.
menu
.
actions_edit
,
menu
)
menu
.
findItem
(
R
.
id
.
delete
).
isVisible
=
args
.
modelId
!=
null
super
.
onCreateOptionsMenu
(
menu
,
inflater
)
}
override
fun
onOptionsItemSelected
(
item
:
MenuItem
):
Boolean
{
when
(
item
.
itemId
)
{
R
.
id
.
save
->
{
save
();
return
true
;
}
R
.
id
.
delete
->
{
delete
();
return
true
;
}
}
return
super
.
onOptionsItemSelected
(
item
)
}
private
fun
save
()
{
val
edited
=
if
(
binding
.
model
==
null
)
{
ToDoModel
(
description
=
binding
.
desc
.
text
.
toString
(),
isCompleted
=
binding
.
isCompleted
.
isChecked
,
notes
=
binding
.
notes
.
text
.
toString
()
)
}
else
{
binding
.
model
?.
copy
(
description
=
binding
.
desc
.
text
.
toString
(),
isCompleted
=
binding
.
isCompleted
.
isChecked
,
notes
=
binding
.
notes
.
text
.
toString
()
)
}
edited
?.
let
{
repo
.
save
(
it
)
}
navToDisplay
()
}
private
fun
delete
()
{
binding
.
model
?.
let
{
repo
.
delete
(
it
)
}
navToList
()
}
private
fun
navToDisplay
()
{
hideKeyboard
()
findNavController
().
popBackStack
()
}
private
fun
navToList
()
{
hideKeyboard
()
findNavController
().
popBackStack
(
R
.
id
.
rosterListFragment
,
false
)
}
private
fun
hideKeyboard
()
{
view
?.
let
{
val
imm
=
context
?.
getSystemService
<
InputMethodManager
>()
imm
?.
hideSoftInputFromWindow
(
it
.
windowToken
,
InputMethodManager
.
HIDE_NOT_ALWAYS
)
}
}
}
\ No newline at end of file
T19-DI/ToDo/app/src/main/java/com/commonsware/todo/MainActivity.kt
0 → 100644
View file @
90fa737f
package
com.commonsware.todo
import
android.content.Intent
import
android.os.Bundle
import
android.view.Menu
import
android.view.MenuItem
import
androidx.appcompat.app.AppCompatActivity
import
androidx.navigation.findNavController
import
androidx.navigation.ui.AppBarConfiguration
import
androidx.navigation.ui.NavigationUI.navigateUp
import
androidx.navigation.ui.setupActionBarWithNavController
import
kotlinx.android.synthetic.main.activity_main.*
class
MainActivity
:
AppCompatActivity
()
{
private
lateinit
var
appBarConfiguration
:
AppBarConfiguration
override
fun
onCreate
(
savedInstanceState
:
Bundle
?)
{
super
.
onCreate
(
savedInstanceState
)
setContentView
(
R
.
layout
.
activity_main
)
setSupportActionBar
(
toolbar
)
findNavController
(
R
.
id
.
nav_host
).
let
{
nav
->
appBarConfiguration
=
AppBarConfiguration
(
nav
.
graph
)
setupActionBarWithNavController
(
nav
,
appBarConfiguration
)
}
}
override
fun
onCreateOptionsMenu
(
menu
:
Menu
):
Boolean
{
menuInflater
.
inflate
(
R
.
menu
.
actions
,
menu
)
return
super
.
onCreateOptionsMenu
(
menu
)
}
override
fun
onOptionsItemSelected
(
item
:
MenuItem
):
Boolean
{
if
(
item
.
itemId
==
R
.
id
.
about
)
{
startActivity
(
Intent
(
this
,
AboutActivity
::
class
.
java
))
return
true
}
return
super
.
onOptionsItemSelected
(
item
)
}
override
fun
onSupportNavigateUp
()
=
navigateUp
(
findNavController
(
R
.
id
.
nav_host
),
appBarConfiguration
)
}
T19-DI/ToDo/app/src/main/java/com/commonsware/todo/RosterAdapter.kt
0 → 100644
View file @
90fa737f
package
com.commonsware.todo
import
android.view.LayoutInflater
import
android.view.ViewGroup
import
androidx.recyclerview.widget.DiffUtil
import
androidx.recyclerview.widget.ListAdapter
import
com.commonsware.todo.databinding.TodoRowBinding
class
RosterAdapter
(
private
val
inflater
:
LayoutInflater
,
private
val
onCheckboxToggle
:
(
ToDoModel
)
->
Unit
,
private
val
onRowClick
:
(
ToDoModel
)
->
Unit
)
:
ListAdapter
<
ToDoModel
,
RosterRowHolder
>(
DiffCallback
)
{
override
fun
onCreateViewHolder
(
parent
:
ViewGroup
,
viewType
:
Int
)
=
RosterRowHolder
(
TodoRowBinding
.
inflate
(
inflater
,
parent
,
false
),
onCheckboxToggle
,
onRowClick
)
override
fun
onBindViewHolder
(
holder
:
RosterRowHolder
,
position
:
Int
)
{
holder
.
bind
(
getItem
(
position
))
}
}
private
object
DiffCallback
:
DiffUtil
.
ItemCallback
<
ToDoModel
>()
{
override
fun
areItemsTheSame
(
oldItem
:
ToDoModel
,
newItem
:
ToDoModel
)
=
oldItem
.
id
==
newItem
.
id
override
fun
areContentsTheSame
(
oldItem
:
ToDoModel
,
newItem
:
ToDoModel
)
=
oldItem
.
isCompleted
==
newItem
.
isCompleted
&&
oldItem
.
description
==
newItem
.
description
}
\ No newline at end of file
T19-DI/ToDo/app/src/main/java/com/commonsware/todo/RosterListFragment.kt
0 → 100644
View file @
90fa737f
package
com.commonsware.todo
import
android.os.Bundle
import
android.view.*
import
androidx.fragment.app.Fragment
import
androidx.navigation.fragment.findNavController
import
androidx.recyclerview.widget.DividerItemDecoration
import
androidx.recyclerview.widget.LinearLayoutManager
import
kotlinx.android.synthetic.main.todo_roster.*
import
kotlinx.android.synthetic.main.todo_roster.view.*
import
org.koin.android.ext.android.inject
class
RosterListFragment
:
Fragment
()
{
private
val
repo
:
ToDoRepository
by
inject
()
override
fun
onCreate
(
savedInstanceState
:
Bundle
?)
{
super
.
onCreate
(
savedInstanceState
)
setHasOptionsMenu
(
true
)
}
override
fun
onCreateView
(
inflater
:
LayoutInflater
,
container
:
ViewGroup
?,
savedInstanceState
:
Bundle
?
):
View
?
=
inflater
.
inflate
(
R
.
layout
.
todo_roster
,
container
,
false
)
override
fun
onViewCreated
(
view
:
View
,
savedInstanceState
:
Bundle
?)
{
super
.
onViewCreated
(
view
,
savedInstanceState
)
val
adapter
=
RosterAdapter
(
inflater
=
layoutInflater
,
onCheckboxToggle
=
{
model
->
repo
.
save
(
model
.
copy
(
isCompleted
=
!
model
.
isCompleted
))
},
onRowClick
=
{
model
->
display
(
model
)
})
view
.
items
.
apply
{
setAdapter
(
adapter
)
layoutManager
=
LinearLayoutManager
(
context
)
addItemDecoration
(
DividerItemDecoration
(
activity
,
DividerItemDecoration
.
VERTICAL
)
)
}
adapter
.
submitList
(
repo
.
items
)
empty
.
visibility
=
if
(
repo
.
items
.
isEmpty
())
View
.
VISIBLE
else
View
.
GONE
}
override
fun
onCreateOptionsMenu
(
menu
:
Menu
,
inflater
:
MenuInflater
)
{
inflater
.
inflate
(
R
.
menu
.
actions_roster
,
menu
)
super
.
onCreateOptionsMenu
(
menu
,
inflater
)
}
override
fun
onOptionsItemSelected
(
item
:
MenuItem
):
Boolean
{
when
(
item
.
itemId
)
{
R
.
id
.
add
->
{
add
();
return
true
;
}
}
return
super
.
onOptionsItemSelected
(
item
)
}
private
fun
display
(
model
:
ToDoModel
)
{
findNavController
().
navigate
(
RosterListFragmentDirections
.
displayModel
(
model
.
id
))
}
private
fun
add
()
{
findNavController
().
navigate
(
RosterListFragmentDirections
.
createModel
())
}
}
\ No newline at end of file
T19-DI/ToDo/app/src/main/java/com/commonsware/todo/RosterRowHolder.kt
0 → 100644
View file @
90fa737f
package
com.commonsware.todo
import
androidx.recyclerview.widget.RecyclerView
import
com.commonsware.todo.databinding.TodoRowBinding
class
RosterRowHolder
(
private
val
binding
:
TodoRowBinding
,
val
onCheckboxToggle
:
(
ToDoModel
)
->
Unit
,
val
onRowClick
:
(
ToDoModel
)
->
Unit
)
:
RecyclerView
.
ViewHolder
(
binding
.
root
)
{
fun
bind
(
model
:
ToDoModel
)
{
binding
.
model
=
model
binding
.
holder
=
this
binding
.
executePendingBindings
()
}
}
\ No newline at end of file
T19-DI/ToDo/app/src/main/java/com/commonsware/todo/ToDoApp.kt
0 → 100644
View file @
90fa737f
package
com.commonsware.todo
import
android.app.Application
import
org.koin.android.ext.android.startKoin
import
org.koin.dsl.module.module
class
ToDoApp
:
Application
()
{
private
val
koinModule
=
module
{
single
{
ToDoRepository
()
}
}
override
fun
onCreate
()
{
super
.
onCreate
()
startKoin
(
this
,
listOf
(
koinModule
))
}
}
\ No newline at end of file
T19-DI/ToDo/app/src/main/java/com/commonsware/todo/ToDoModel.kt
0 → 100644
View file @
90fa737f
package
com.commonsware.todo
import
java.util.*
data class
ToDoModel
(
val
description
:
String
,
val
id
:
String
=
UUID
.
randomUUID
().
toString
(),
val
isCompleted
:
Boolean
=
false
,
val
notes
:
String
=
""
,
val
createdOn
:
Calendar
=
Calendar
.
getInstance
()
)
{
}
\ No newline at end of file
T19-DI/ToDo/app/src/main/java/com/commonsware/todo/ToDoRepository.kt
0 → 100644
View file @
90fa737f
package
com.commonsware.todo
class
ToDoRepository
{
var
items
=
listOf
<
ToDoModel
>()
fun
save
(
model
:
ToDoModel
)
{
items
=
if
(
items
.
any
{
it
.
id
==
model
.
id
})
{
items
.
map
{
if
(
it
.
id
==
model
.
id
)
model
else
it
}
}
else
{
items
+
model
}
}
fun
delete
(
model
:
ToDoModel
)
{
items
=
items
.
filter
{
it
.
id
!=
model
.
id
}
}
fun
find
(
modelId
:
String
?)
=
items
.
find
{
it
.
id
==
modelId
}
}
\ No newline at end of file
T19-DI/ToDo/app/src/main/res/drawable-v24/ic_launcher_foreground.xml
0 → 100644
View file @
90fa737f
<vector
xmlns:aapt=
"http://schemas.android.com/aapt"
xmlns:android=
"http://schemas.android.com/apk/res/android"
android:width=
"108dp"
android:height=
"108dp"
android:viewportHeight=
"108"
<