-
Notifications
You must be signed in to change notification settings - Fork 33
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
경북대 Android_권성찬 2주차 과제 Step2 #46
Open
ksc1008
wants to merge
45
commits into
kakao-tech-campus-2nd-step2:ksc1008
Choose a base branch
from
ksc1008:step2
base: ksc1008
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
45 commits
Select commit
Hold shift + click to select a range
70bd3e5
docs: Init README.md
ksc1008 3ea0b44
refactor: setup MVVM directories
ksc1008 6aaaa3b
layout: setup base layout (mainActivity, SearchResultFragment)
ksc1008 5b027bc
refactor: renamed package names according to the convention
ksc1008 6539fae
feat: Add SearchDBHelper, SearchResultContract
ksc1008 dd540d9
feat: now db adds initial data from predefined dataset
ksc1008 a1f1dc2
feat: add method to print all data from database
ksc1008 e6c879f
docs: updated README.md
ksc1008 394cc16
feat: added SearchResultRepository
ksc1008 b1d384d
fix: used postValue method instead of directly assigning to liveData …
ksc1008 9b587f0
feat: Add SearchActivityViewModel
ksc1008 2399d3b
feat: implement SearchResultFragment
ksc1008 1da4ecc
docs: update README.md for further requirements
ksc1008 1790b38
docs: fixed typo
ksc1008 86cee37
feat: add SearchKeywordContract, renamed method names to prevent conf…
ksc1008 d268733
feat: add SearchKeywordRepository
ksc1008 2f88f73
feat: add keyword modifying methods to SearchActivityViewModel
ksc1008 878f270
refactor($SearchResultFragment): separated several procedures to indi…
ksc1008 f66c191
refactor($MainActivity): separated LiveDataObservation procedure to i…
ksc1008 6d84183
fix($SearchActivityViewModel): now viewModel do not add keyword on se…
ksc1008 95a206a
feat: Added onClickListener to viewHolders of recyclerView
ksc1008 5e8b36f
feat: Added SearchKeywordList in MainActivity
ksc1008 ba601de
fix: now get all data from db on startup
ksc1008 21db596
fix: prevented redundant keyword to be added to DB
ksc1008 b2d2429
refactor: moved Adapter classes to "adapters" package
ksc1008 7d2e743
feat: added keyword delete button listener
ksc1008 32e03a8
refactor: refactored views and viewModels to make them less coupled
ksc1008 950b274
refactor: minor reformatting
ksc1008 3dd0e79
layout($SearchResultFragment): added divider, added no result text
ksc1008 e612ef2
refactor($MainActivity): renamed 'savedKeywordListView' to 'keywordRe…
ksc1008 b4b9928
layout($MainActivity): now keywordRecyclerView automatically collapse…
ksc1008 3088c7f
refactor: changed all exposed LiveData property to immutable wrapping…
ksc1008 1d88f49
layout($MainActivity): changed type of searchInput from 'EditText' to…
ksc1008 f70f161
feat: attached DiffUtils to recyclerView adapters
ksc1008 88cd01a
refactor: refactored codes for readability
ksc1008 cf59999
refactor($InitialDataset): changed to object type
ksc1008 9a44c12
refactor($SearchResultContract): removed index constant values
ksc1008 6a01110
refactor($SearchDbHelper): renamed "datas" to "dataList"
ksc1008 9453384
refactor($MainActivityLayout): changed 'Left', 'Right' notation to 'S…
ksc1008 7bc7521
Merge branch 'step1' into step2
ksc1008 47c70c6
fix($SearchDbHelper): fixed import compile error
ksc1008 2b4c664
refactor: changed '!!' notation to 'as Type'
ksc1008 a674dc1
refactor: changed 'left', 'right' notation to 'start', 'end'
ksc1008 ec59f2d
refactor($SearchKeywordContract): remove index constant values
ksc1008 07ff8a6
refactor(SearchResultAdapter): now get layout inflater using parent v…
ksc1008 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,23 @@ | ||
# android-map-keyword | ||
|
||
## 구현 기능 목록 | ||
1. MVVM 아키텍처 구조 설립 | ||
2. 기본 레이아웃 구현 | ||
3. SQLite 활용하여 데이터베이스 구축 | ||
4. 로컬 데이터베이스에 데이터 생성 | ||
|
||
## Step 2 구현 기능 목록 | ||
- [x] SQLiteOpenHelper을 통해 DB를 업데이트 하거나 요청받은 LiveData 데이터를 가져오는 Repository 클래스 구현 | ||
- [x] 유저 입력에 따라 Repository로 검색 결과 데이터 조회를 요청하는 ViewModel 클래스 구현 | ||
- [x] 검색 결과 Fragment에 리스트를 출력하는 RecyclerView 추가 | ||
- [x] RecyclerView Adapter에서 ViewModel의 데이터를 Observe하여 ViewModel의 검색 결과 데이터의 변화에 따라 리스트의 값을 갱신하도록 구현 | ||
- [x] SearchInput 에서 텍스트가 변화할 때마다 ViewModel에서 검색 결과를 갱신하도록 구현 | ||
- [ ] SearchKeyword 테이블 스키마 및 Contract 생성 | ||
- [ ] SearchKeyword 테이블 관련 헬퍼 메소드 작성 | ||
- [ ] Keyword를 저장하는 레포지토리 클래스 생성 | ||
- [ ] 검색 바 아래 KeywordList 생성 | ||
- [ ] 검색 바 Text값을 ViewModel에 바인딩 | ||
- [ ] ViewModel에 Keyword 클릭 시 해당 검색어 검색하는 메소드 생성 | ||
- [ ] ViewModel에 Keyword 제거하는 메소드 생성 | ||
- [ ] ViewModel의 검색 메소드에 KeywordRepository로 새로운 Keyword 저장하는 기능 추가 | ||
- [ ] MainActivity에서 Keyword 목록을 Observe하여 자동으로 갱신되는 KeywordList Layout 추가 (ListView 활용) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file was deleted.
Oops, something went wrong.
27 changes: 27 additions & 0 deletions
27
app/src/main/java/campus/tech/kakao/map/models/InitialDataset.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
package campus.tech.kakao.map.models | ||
|
||
object InitialDataset { | ||
val initialData:List<SearchResult> = listOf( | ||
SearchResult("카페1", "대구 달서구 복현동 1", "카페"), | ||
SearchResult("카페2", "대구 달서구 복현동 2", "카페"), | ||
SearchResult("카페3", "대구 달서구 복현동 3", "카페"), | ||
SearchResult("카페4", "대구 달서구 복현동 4", "카페"), | ||
SearchResult("카페5", "대구 달서구 복현동 5", "카페"), | ||
SearchResult("카페6", "대구 달서구 복현동 6", "카페"), | ||
SearchResult("카페7", "대구 달서구 복현동 7", "카페"), | ||
SearchResult("음식점1", "대구 달서구 송현동 1", "음식점"), | ||
SearchResult("음식점2", "대구 달서구 송현동 2", "음식점"), | ||
SearchResult("음식점3", "대구 달서구 송현동 3", "음식점"), | ||
SearchResult("음식점4", "대구 달서구 송현동 4", "음식점"), | ||
SearchResult("음식점5", "대구 달서구 송현동 5", "음식점"), | ||
SearchResult("음식점6", "대구 달서구 송현동 6", "음식점"), | ||
SearchResult("음식점7", "대구 달서구 송현동 7", "음식점"), | ||
SearchResult("음식점8", "대구 달서구 송현동 8", "음식점"), | ||
SearchResult("음식점9", "대구 달서구 송현동 9", "음식점"), | ||
SearchResult("음식점10", "대구 달서구 송현동 10", "음식점"), | ||
SearchResult("음식점11", "대구 달서구 송현동 11", "음식점"), | ||
SearchResult("음식점12", "대구 달서구 송현동 12", "음식점"), | ||
SearchResult("음식점13", "대구 달서구 송현동 13", "음식점"), | ||
SearchResult("음식점14", "대구 달서구 송현동 14", "음식점") | ||
) | ||
} |
181 changes: 181 additions & 0 deletions
181
app/src/main/java/campus/tech/kakao/map/models/SearchDbHelper.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,181 @@ | ||
package campus.tech.kakao.map.models | ||
|
||
import android.content.ContentValues | ||
import android.content.Context | ||
import android.database.Cursor | ||
import android.database.sqlite.SQLiteDatabase | ||
import android.database.sqlite.SQLiteOpenHelper | ||
import android.provider.BaseColumns | ||
import campus.tech.kakao.map.models.contracts.SearchKeywordContract | ||
import campus.tech.kakao.map.models.contracts.SearchResultContract | ||
|
||
data class SearchResult(val name: String, val address: String, val type: String) | ||
|
||
class SearchDbHelper(context: Context) : | ||
SQLiteOpenHelper(context, DATABASE_NAME, null, DATABASE_VERSION) { | ||
override fun onCreate(db: SQLiteDatabase?) { | ||
db?.execSQL(SearchResultContract.CREATE_QUERY) | ||
|
||
if (db != null) { | ||
insertInitialData(db) | ||
} | ||
} | ||
|
||
override fun onUpgrade(db: SQLiteDatabase?, oldVersion: Int, newVersion: Int) { | ||
if (oldVersion <= 1) { | ||
db?.execSQL(SearchKeywordContract.CREATE_QUERY) | ||
} | ||
if (oldVersion in 2..2) { | ||
db?.execSQL(SearchKeywordContract.DROP_QUERY) | ||
db?.execSQL(SearchKeywordContract.CREATE_QUERY) | ||
} | ||
} | ||
|
||
private fun insertInitialData(db: SQLiteDatabase) { | ||
val dataList = InitialDataset.initialData | ||
for (data in dataList) { | ||
val contentValues = ContentValues().apply { | ||
put(SearchResultContract.COLUMN_NAME, data.name) | ||
put(SearchResultContract.COLUMN_ADDRESS, data.address) | ||
put(SearchResultContract.COLUMN_TYPE, data.type) | ||
} | ||
db.insert(SearchResultContract.TABLE_NAME, null, contentValues) | ||
} | ||
} | ||
|
||
fun insertSearchResult(name: String, address: String, type: String) { | ||
val db = writableDatabase | ||
val contentValues = ContentValues().apply { | ||
put(SearchResultContract.COLUMN_NAME, name) | ||
put(SearchResultContract.COLUMN_ADDRESS, address) | ||
put(SearchResultContract.COLUMN_TYPE, type) | ||
} | ||
db.insert(SearchResultContract.TABLE_NAME, null, contentValues) | ||
} | ||
|
||
fun insertSearchResult(searchResult: SearchResult) { | ||
insertSearchResult(searchResult.name, searchResult.address, searchResult.type) | ||
} | ||
|
||
fun insertOrReplaceKeyword(keyword: String) { | ||
val db = writableDatabase | ||
val contentValues = ContentValues().apply { | ||
put(SearchKeywordContract.COLUMN_KEYWORD, keyword) | ||
} | ||
|
||
db.replace(SearchKeywordContract.TABLE_NAME, null, contentValues) | ||
} | ||
|
||
fun updateSearchResult(id: String, name: String, address: String, type: String) { | ||
val db = writableDatabase | ||
val contentValues = ContentValues().apply { | ||
put(BaseColumns._ID, id) | ||
put(SearchResultContract.COLUMN_NAME, name) | ||
put(SearchResultContract.COLUMN_ADDRESS, address) | ||
put(SearchResultContract.COLUMN_TYPE, type) | ||
} | ||
db.update( | ||
SearchResultContract.TABLE_NAME, | ||
contentValues, | ||
"${BaseColumns._ID} = ?", | ||
arrayOf(id) | ||
) | ||
} | ||
|
||
fun updateSearchResult(id: String, searchResult: SearchResult) { | ||
updateSearchResult(id, searchResult.name, searchResult.address, searchResult.type) | ||
} | ||
|
||
fun deleteSearchResult(id: String) { | ||
val db = writableDatabase | ||
db.delete(SearchResultContract.TABLE_NAME, "${BaseColumns._ID} = ?", arrayOf(id)) | ||
} | ||
|
||
fun deleteKeyword(keyword: String) { | ||
val db = writableDatabase | ||
db.delete( | ||
SearchKeywordContract.TABLE_NAME, | ||
"${SearchKeywordContract.COLUMN_KEYWORD} = ?", | ||
arrayOf(keyword) | ||
) | ||
} | ||
|
||
private fun getAllSearchResultFromCursor(cursor: Cursor?): List<SearchResult> { | ||
val result = mutableListOf<SearchResult>() | ||
|
||
try { | ||
while (cursor?.moveToNext() == true) { | ||
result.add( | ||
SearchResult( | ||
cursor.getString(1), | ||
cursor.getString(2), | ||
cursor.getString(3) | ||
) | ||
) | ||
} | ||
} catch (e: Exception) { | ||
e.printStackTrace() | ||
} finally { | ||
if (cursor != null && !cursor.isClosed) { | ||
cursor.close() | ||
} | ||
} | ||
|
||
return result | ||
} | ||
|
||
private fun getAllSearchKeywordFromCursor(cursor: Cursor?): List<String> { | ||
val result = mutableListOf<String>() | ||
|
||
try { | ||
while (cursor?.moveToNext() == true) { | ||
result.add(cursor.getString(1)) | ||
} | ||
} catch (e: Exception) { | ||
e.printStackTrace() | ||
} finally { | ||
if (cursor != null && !cursor.isClosed) { | ||
cursor.close() | ||
} | ||
} | ||
|
||
return result | ||
} | ||
|
||
fun queryAllSearchResults(): List<SearchResult> { | ||
val db = readableDatabase | ||
val cursor = db.rawQuery("SELECT * FROM ${SearchResultContract.TABLE_NAME}", null) | ||
|
||
return getAllSearchResultFromCursor(cursor) | ||
} | ||
|
||
fun querySearchResultsByName(name: String): List<SearchResult> { | ||
val db = readableDatabase | ||
val cursor = db.rawQuery( | ||
"SELECT * FROM ${SearchResultContract.TABLE_NAME} WHERE ${SearchResultContract.COLUMN_NAME} LIKE \"%$name%\"", | ||
null | ||
) | ||
|
||
return getAllSearchResultFromCursor(cursor) | ||
} | ||
|
||
fun queryAllSearchKeywords(): List<String> { | ||
val db = readableDatabase | ||
val cursor = db.rawQuery("SELECT * FROM ${SearchKeywordContract.TABLE_NAME}", null) | ||
|
||
return getAllSearchKeywordFromCursor(cursor) | ||
} | ||
|
||
companion object { | ||
private var instance: SearchDbHelper? = null | ||
const val DATABASE_VERSION = 3 | ||
const val DATABASE_NAME = "MapSearch" | ||
|
||
fun getInstance(context: Context): SearchDbHelper { | ||
if (instance == null) { | ||
instance = SearchDbHelper(context) | ||
} | ||
return instance as SearchDbHelper | ||
} | ||
} | ||
} |
53 changes: 53 additions & 0 deletions
53
app/src/main/java/campus/tech/kakao/map/models/SearchKeywordRepository.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
package campus.tech.kakao.map.models | ||
|
||
import android.content.Context | ||
import androidx.lifecycle.LiveData | ||
import androidx.lifecycle.MutableLiveData | ||
import kotlinx.coroutines.CoroutineScope | ||
import kotlinx.coroutines.Dispatchers | ||
import kotlinx.coroutines.launch | ||
|
||
class SearchKeywordRepository(context: Context) { | ||
private val _keywords: MutableLiveData<List<String>> = MutableLiveData(listOf()) | ||
val keywords: LiveData<List<String>> | ||
get() = _keywords | ||
|
||
private lateinit var searchDb: SearchDbHelper | ||
init { | ||
searchDb = SearchDbHelper(context) | ||
} | ||
|
||
fun addKeyword(keyword: String) { | ||
CoroutineScope(Dispatchers.IO).launch { | ||
searchDb.insertOrReplaceKeyword(keyword) | ||
val newData = searchDb.queryAllSearchKeywords() | ||
_keywords.postValue(newData) | ||
} | ||
} | ||
|
||
fun deleteKeyword(keyword: String) { | ||
CoroutineScope(Dispatchers.IO).launch { | ||
searchDb.deleteKeyword(keyword) | ||
val newData = searchDb.queryAllSearchKeywords() | ||
_keywords.postValue(newData) | ||
} | ||
} | ||
|
||
fun getKeywords() { | ||
CoroutineScope(Dispatchers.IO).launch { | ||
val newData = searchDb.queryAllSearchKeywords() | ||
_keywords.postValue(newData) | ||
} | ||
} | ||
|
||
companion object { | ||
private var instance: SearchKeywordRepository? = null | ||
|
||
fun getInstance(context: Context): SearchKeywordRepository { | ||
if (instance == null) { | ||
instance = SearchKeywordRepository(context) | ||
} | ||
return instance as SearchKeywordRepository | ||
} | ||
} | ||
} |
38 changes: 38 additions & 0 deletions
38
app/src/main/java/campus/tech/kakao/map/models/SearchResultRepository.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
package campus.tech.kakao.map.models | ||
|
||
import android.content.Context | ||
import androidx.lifecycle.LiveData | ||
import androidx.lifecycle.MutableLiveData | ||
import kotlinx.coroutines.CoroutineScope | ||
import kotlinx.coroutines.Dispatchers | ||
import kotlinx.coroutines.launch | ||
|
||
class SearchResultRepository(context: Context) { | ||
private val _searchResult: MutableLiveData<List<SearchResult>> = MutableLiveData(listOf()) | ||
val searchResult: LiveData<List<SearchResult>> | ||
get() = _searchResult | ||
|
||
private lateinit var searchDb: SearchDbHelper | ||
|
||
init { | ||
searchDb = SearchDbHelper(context) | ||
} | ||
|
||
fun search(text: String) { | ||
CoroutineScope(Dispatchers.IO).launch { | ||
val result = searchDb.querySearchResultsByName(text) | ||
_searchResult.postValue(result) | ||
} | ||
} | ||
|
||
companion object { | ||
private var instance: SearchResultRepository? = null | ||
|
||
fun getInstance(context: Context): SearchResultRepository { | ||
if (instance == null) { | ||
instance = SearchResultRepository(context) | ||
} | ||
return instance as SearchResultRepository | ||
} | ||
} | ||
} |
15 changes: 15 additions & 0 deletions
15
app/src/main/java/campus/tech/kakao/map/models/contracts/SearchKeywordContract.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
package campus.tech.kakao.map.models.contracts | ||
|
||
import android.provider.BaseColumns | ||
|
||
object SearchKeywordContract : BaseColumns { | ||
const val TABLE_NAME = "SEARCH_KEYWORD" | ||
const val COLUMN_KEYWORD = "keyword" | ||
|
||
const val CREATE_QUERY = "CREATE TABLE IF NOT EXISTS $TABLE_NAME (" + | ||
"${BaseColumns._ID} INTEGER PRIMARY KEY AUTOINCREMENT, " + | ||
"$COLUMN_KEYWORD TEXT," + | ||
"UNIQUE($COLUMN_KEYWORD))" | ||
|
||
const val DROP_QUERY = "DROP TABLE IF EXISTS $TABLE_NAME" | ||
} |
18 changes: 18 additions & 0 deletions
18
app/src/main/java/campus/tech/kakao/map/models/contracts/SearchResultContract.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
package campus.tech.kakao.map.models.contracts | ||
|
||
import android.provider.BaseColumns._ID | ||
|
||
object SearchResultContract { | ||
const val TABLE_NAME = "SEARCH_RESULT" | ||
const val COLUMN_NAME = "name" | ||
const val COLUMN_ADDRESS = "address" | ||
const val COLUMN_TYPE = "type" | ||
|
||
const val CREATE_QUERY = "CREATE TABLE IF NOT EXISTS $TABLE_NAME (" + | ||
"$_ID INTEGER PRIMARY KEY AUTOINCREMENT, " + | ||
"$COLUMN_NAME TEXT, " + | ||
"$COLUMN_ADDRESS TEXT, " + | ||
"$COLUMN_TYPE TEXT)" | ||
|
||
const val DROP_QUERY = "DROP TABLE IF EXISTS $TABLE_NAME" | ||
} |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
반복되는 동작같은데 메서드를 따로 빼도 괜찮을것 같습니다