Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 27 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,28 @@
# todo-list
그룹 프로젝트 #1

## 팀 구성원

<table align = "center">
<thead>
<th align = "center">stitch</th>
<th align = "center">스타크</th>
<th align = "center">Hwi</th>
<th align = "center">루이</th>
</thead>
<tbody>
<td align = "center"><a href="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/bn-tw2020"><img height="100px" width="110px" src="https://avatars.githubusercontent.com/u/66770613?s=460&u=282042bf415e9b361e2b804c554389593b2ff760&v=4" /></a></td>
<td align = "center"><a href="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/jminie-o8o"><img height="100px" width="110px" src="https://avatars.githubusercontent.com/u/79504043?v=4" /></a></td>
<td align = "center"><a href="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/hwicode"><img height="100px" width="110px" src="https://avatars.githubusercontent.com/u/95541996?v=4" /></a></td>
<td align = "center"><a href="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/Louie-03"><img height="100px" width="110px" src="https://avatars.githubusercontent.com/u/92966772?v=4" /></a></td>
</tbody>
<tr>
<td align = "center">Android</td>
<td align = "center">Android</td>
<td align = "center">Server</td>
<td align = "center">Server</td>
</tr>
</table>

## Document

[📑 Wiki](https://github.com/bn-tw2020/todo-list/wiki/Team)
10 changes: 10 additions & 0 deletions android/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
*.iml
.gradle
/local.properties
/.idea
.DS_Store
/build
/captures
.externalNativeBuild
.cxx
local.properties
1 change: 1 addition & 0 deletions android/app/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/build
53 changes: 53 additions & 0 deletions android/app/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
plugins {
id 'com.android.application'
id 'org.jetbrains.kotlin.android'
id 'kotlin-kapt'
}

android {
compileSdk 32

defaultConfig {
applicationId "com.example.todolist"
minSdk 21
targetSdk 32
versionCode 1
versionName "1.0"

testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}

dataBinding {
enabled = true
}

buildFeatures {
viewBinding true
}

buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = '1.8'
}
}

dependencies {

implementation 'androidx.core:core-ktx:1.7.0'
implementation 'androidx.appcompat:appcompat:1.4.1'
implementation 'com.google.android.material:material:1.5.0'
implementation 'androidx.constraintlayout:constraintlayout:2.1.3'
testImplementation 'junit:junit:4.13.2'
androidTestImplementation 'androidx.test.ext:junit:1.1.3'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
implementation 'androidx.fragment:fragment-ktx:1.4.1'
}
21 changes: 21 additions & 0 deletions android/app/proguard-rules.pro
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# 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
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package com.example.todolist

import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.ext.junit.runners.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.getInstrumentation().targetContext
assertEquals("com.example.todolist", appContext.packageName)
}
}
23 changes: 23 additions & 0 deletions android/app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.todolist">

<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.Todolist">
<activity
android:name=".ui.MainActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />

<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>

</manifest>
29 changes: 29 additions & 0 deletions android/app/src/main/java/com/example/todolist/TimeUtils.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package com.example.todolist.ui.common

import java.util.*

enum class TimeValue(val value: Int, val maximum: Int, val message: String) {
SEC(60, 60, "분 전"),
MIN(60, 24, "시간 전"),
HOUR(24, 30, "일 전"),
DAY(30, 12, "달 전"),
MONTH(12, Int.MAX_VALUE, "년 전")
Comment on lines +6 to +10

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

하드 코딩된 문자열의 사용은 지양하는게 좋아요
예를 들어 "분 전" 이라는 문자열이 여러 화면에서 사용될때 "분 전"을 "m ago" 처럼 바꾼다고 생각해보세요 ㅎㅎ;
단순한 작업인데 꽤 유지 보수하는데 꽤 많은 시간들이 걸리겠죠?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

const val을 이용하여 변수로 작업할 수 있도록 하겠습니다.

}

fun calculateTime(date: Date): String? {
val currentTime = System.currentTimeMillis()
val registerTime = date.time
var differenceTime = (currentTime - registerTime) / 1000

if (differenceTime < TimeValue.SEC.value) {
return "${differenceTime}초 전"
} else {
for (timeValue in TimeValue.values()) {
differenceTime /= timeValue.value
if (differenceTime < timeValue.maximum) {
return "${differenceTime}${timeValue.message}"
}
}
}
return null
}
17 changes: 17 additions & 0 deletions android/app/src/main/java/com/example/todolist/model/History.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package com.example.todolist.model

data class History(
val id: Int,
val action: ActionType,
val title: String,
val nowStatus: String,
val beforeStatus: String?,
val createData: String,
)

enum class ActionType(val action: String) {
ADD("add"),
REMOVE("remove"),
UPDATE("update"),
MOVE("move")
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package com.example.todolist.model

enum class Status(val status: String) {
TODO("todo"),
IN_PROGRESS("inProgress"),
DONE("done")
}
8 changes: 8 additions & 0 deletions android/app/src/main/java/com/example/todolist/model/Task.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package com.example.todolist.model

data class Task(
val title: String?,
val content: String?,
val status: Status,
val author: String = "Android"
)
90 changes: 90 additions & 0 deletions android/app/src/main/java/com/example/todolist/ui/Dialog.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
package com.example.todolist.ui

import android.graphics.Color
import android.graphics.drawable.ColorDrawable
import android.os.Bundle
import android.text.Editable
import android.text.TextWatcher
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.view.Window
import androidx.fragment.app.DialogFragment
import androidx.fragment.app.activityViewModels
import com.example.todolist.databinding.DialogNewCardBinding
import com.example.todolist.model.Status
import com.example.todolist.model.Task

class Dialog : DialogFragment() {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jminie-o8o
다이얼로그를 DialogFragment 로 구현하셨던 이유가 무엇인가요?

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@renovatio0424
DialogFragment 는 말 그대로 Fragment 이기 때문에 Fragment 의 생명주기를 활용할 수 있습니다.
Dialog를 활용할 때 Activity가 파괴되더라도 dialog가 존재하여 생기는 메모리 leak 등을 방지할 수 있는 장점이 있기 때문에 DialogFragment 로 구현했습니다.

private var _binding: DialogNewCardBinding? = null
private val binding get() = _binding
private val viewModel: ViewModel by activityViewModels()
private var titleFlag = false
private var contentsFlag = false

override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
_binding = DialogNewCardBinding.inflate(inflater, container, false)
dialog?.window?.setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT)) // 다이얼로그의 곡선 주변에 배경색을 맞춰주는 코드
dialog?.window?.requestFeature(Window.FEATURE_NO_TITLE)
dialog?.setCanceledOnTouchOutside(false) // 다이얼로그 외부의 영역 터치 시 취소 불가능

return binding?.root
}

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
binding?.btnCancel?.setOnClickListener { dismiss() }

binding?.etTitle?.addTextChangedListener(titleListener)
binding?.etContents?.addTextChangedListener(contentsListener)

binding?.btnRegister?.setOnClickListener {
val task = Task(
binding?.etTitle?.text?.toString(),
binding?.etContents?.text?.toString(),
Status.TODO
)
viewModel.addTask(task)
dismiss()
}
}

private val titleListener = object : TextWatcher {
override fun beforeTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {}

override fun onTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {}

override fun afterTextChanged(s: Editable?) {
if (s != null) {
titleFlag = when {
s.isEmpty() -> false
else -> true
}
}
flagCheck()
}
}

private val contentsListener = object : TextWatcher {
override fun beforeTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {}

override fun onTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {}

override fun afterTextChanged(s: Editable?) {
if (s != null) {
contentsFlag = when {
s.isEmpty() -> false
else -> true
}
}
flagCheck()
}
}

fun flagCheck() {
binding?.btnRegister?.isEnabled = titleFlag && contentsFlag
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package com.example.todolist.ui

import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.ListAdapter
import androidx.recyclerview.widget.RecyclerView
import com.example.todolist.databinding.ItemHistoryBinding
import com.example.todolist.model.History

class HistoryAdapter : ListAdapter<History, HistoryAdapter.HistoryViewHolder>(HistoryDiffCallback) {

override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): HistoryViewHolder {
val binding = ItemHistoryBinding.inflate(LayoutInflater.from(parent.context), parent, false)
return HistoryViewHolder(binding)
}

override fun onBindViewHolder(holder: HistoryViewHolder, position: Int) {
holder.bind(getItem(position))
}

class HistoryViewHolder(private val binding: ItemHistoryBinding) :
RecyclerView.ViewHolder(binding.root) {
fun bind(history: History) {
binding.history = history
binding.executePendingBindings()
}
}
}

object HistoryDiffCallback : DiffUtil.ItemCallback<History>() {
override fun areItemsTheSame(oldItem: History, newItem: History): Boolean {
return oldItem.id == newItem.id
}

override fun areContentsTheSame(oldItem: History, newItem: History): Boolean {
return oldItem == newItem
}

}
Loading