Skip to content
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

[부나] 1, 2단계 쇼핑 장바구니 제출합니다. #25

Merged
merged 75 commits into from
May 16, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
75 commits
Select commit Hold shift + click to select a range
9d6bcf9
feat: 기초 세팅
2chang5 May 9, 2023
7acd935
docs(README): 기능목록 작성
2chang5 May 9, 2023
ebc91bd
feat: 추가 기초세팅
2chang5 May 9, 2023
c331711
feat(Price): 상품 가격 검증 로직 작성
2chang5 May 9, 2023
b0dac86
feat(Product): 상품 클래스 구현
2chang5 May 9, 2023
264cc0f
feat(Basket): 장바구니에 상품을 담는 기능 구현
2chang5 May 9, 2023
1874c63
feat(Basket): 장바구니 삭제 기능 구현
2chang5 May 9, 2023
17266e8
feat(BasketRepository): 장바구니 레퍼지토리 인터페이스 추가
2chang5 May 9, 2023
5afa9a0
feat(ProductRepository): 상품 레퍼지토리 인터페이스 추가
2chang5 May 9, 2023
988be31
feat(RecentProductRepository): 최근 상품 레퍼지토리 인터페이스 추가
2chang5 May 9, 2023
a0f9010
feat(themes): 액션바 제거
2chang5 May 9, 2023
5e23362
feat(ShoppingActivity): Toolbar 제작
2chang5 May 9, 2023
ab9cdff
feat(item_shopping): 제품화면 리사이클러뷰 아이템 생성
2chang5 May 9, 2023
d7d84df
feat(ShoppingActivity): 제품 목록을 보여주는 기능 구현
2chang5 May 9, 2023
19a8257
feat(ShoppingActivity): 제품 상세 화면 구현
2chang5 May 9, 2023
59b3760
feat(ShoppingActivity): 인텐트 팩토리함수 작성
2chang5 May 9, 2023
8175c6d
feat(BasketActivity): 장바구니 화면 구현
2chang5 May 9, 2023
b23cad3
feat(ShoppingActivity): 리사이클러뷰 첫번쨰 아이템 뷰작업
2chang5 May 10, 2023
f08fb31
feat(ShoppingActivity): 멀티뷰 아이템 어댑터 생성
2chang5 May 10, 2023
25466a3
feat(ShoppingActivity): 리사이클러뷰 멀티뷰 수용하도록 로직 작성
2chang5 May 10, 2023
439f5c8
feat(ProductDataBase): product 저장 및 불러오기 로직 작성
2chang5 May 10, 2023
69262c9
gitignore 설정
2chang5 May 10, 2023
9d28546
feat(RecentProductDataBase): 최근 목록 관련 DB로직 작성
2chang5 May 10, 2023
136286b
feat(RecentProductRepository): 최근 본 상품 저장 로직 작성
2chang5 May 10, 2023
3b2314b
feat(RecentProduct): 최근 본 상품 어댑터 최신화 로직
2chang5 May 10, 2023
cc4c1d1
feat(ShoppingActivity): 상품목록 불러오는 기능 구현
2chang5 May 10, 2023
87f5f76
feat(ProductDetailActivity): 화면간 이동로직 작성
2chang5 May 11, 2023
a98db38
feat(BasketRepository): 장바구니 테이블 구현
2chang5 May 11, 2023
a0596c4
feat(BasketActivity): 장바구니 목록 불러오는 기능구현
2chang5 May 11, 2023
053ec27
feat(ProductDetailActivity): 화면간 이동로직 작성
2chang5 May 11, 2023
eb03bed
feat(Basket): 장바구니 데이터 삭제로직 작성
2chang5 May 11, 2023
8264d4e
feat(diffutil): areItemsTheSame 함수 수정(리사이클러뷰 아이템 blink issue)
2chang5 May 11, 2023
bafef24
feat(ShoppingActivity): 장바구니 버튼 클릭리스너 작성
2chang5 May 11, 2023
2b51c34
feat(BasketActivity): 툴바 백버튼 설정
2chang5 May 11, 2023
c1cbfb9
feat(ProductDetail): 툴바 백버튼 설정
2chang5 May 11, 2023
06b713a
feat(ShoppingActivity):페이징시 다음 아이템 존재하는지 판단로직 작성
2chang5 May 11, 2023
531eefd
feat(ShoppingActivity): Pagination 구현
2chang5 May 11, 2023
62822f2
feat(BasketActivity): 장바구니 페이징 UI 네비게이터 생성
2chang5 May 11, 2023
2f53154
feat(BasketActivity): 장바구니 페이지네이션 구현
2chang5 May 11, 2023
23722c4
refactor(ShoppingActivity): 리사이클러뷰 어댑터를 ConcatAdapter로 변경
2chang5 May 12, 2023
2f7b5f4
refactor(ProductDetailActivity): Toolbar에 menu를 사용하도록 변경
tmdgh1592 May 13, 2023
e2f546b
refactor(uimodel): 패키지 이동
tmdgh1592 May 13, 2023
ed00994
feat(RecyclerViewBindingAdapter): setHasFixedSize를 Databinding으로 구현
tmdgh1592 May 14, 2023
baa3887
refactor: ShoppingGridLayoutManager 클래스로 분리
tmdgh1592 May 14, 2023
a2433f9
refactor(ShoppingActivity): RecyclerView 관련 로직을 메서드로 분리
tmdgh1592 May 14, 2023
4a4807a
refactor(ShoppingActivity): RecyclerView에 필요한 객체 초기화 로직을 BindingAdapt…
tmdgh1592 May 14, 2023
abcb6e6
refactor: 코드 포맷팅
tmdgh1592 May 14, 2023
2a70d12
feat(ShoppingActivity): 아이템을 클릭하면 최근 본 상품 리스트에 보여주는 기능 구현
tmdgh1592 May 14, 2023
2e93379
feat(RecentProducts): 최근 본 상품 도메인 클래스 구현
tmdgh1592 May 14, 2023
4ab5fb0
fix(ShoppingActivity): 더 보기 버튼 안 보이는 버그 수정
tmdgh1592 May 14, 2023
a1b2a15
fix(ShoppingActivity): 처음 최근 본 목록에 아이템 추가시 업데이트 안 되는 버그 수정
tmdgh1592 May 14, 2023
2f3fd15
refactor(BasketActivity): RecyclerView adapter listener를 데이터 바인딩으로 분리
tmdgh1592 May 14, 2023
d1bd8eb
fix(RecentProductWrapperAdapter): 최근 본 목록이 여러개 보이는 버그 수정
tmdgh1592 May 14, 2023
d6030cf
refactor(ToolbarBindingAdapter): NavigationIconClickListener 데이터 바인딩 구현
tmdgh1592 May 14, 2023
640169b
feat(BasketActivity): 장바구니 페이지네이션 구현
tmdgh1592 May 14, 2023
055c283
refactor(Database): Database close()를 Activity에서 하도록 변경
tmdgh1592 May 14, 2023
8238b87
fix(BasketActivity): 페이지네이션 비정상적으로 동작하는 버그 수정
tmdgh1592 May 14, 2023
d1f4cd5
refactor(ShoppingActivity): fetchAll() 메서드 구현
tmdgh1592 May 14, 2023
92cd63c
refactor(Presenter): 모든 Presenter 팩토리 함수 구현
tmdgh1592 May 14, 2023
f4449cb
refactor(Presenter): Thread를 사용하지 않도록 변경
tmdgh1592 May 14, 2023
1db5c86
refactor(ShoppingActivity): 불필요한 함수 제거
tmdgh1592 May 14, 2023
c8045db
refactor(Presenter): 추상 클래스로 변경
tmdgh1592 May 14, 2023
ce89319
refactor(BasketPresenter): 페이지와 제품 목록을 생성자로 이동
tmdgh1592 May 15, 2023
71038f2
test(BasketPresenterTest): 프레젠터 테스트 코드 작성
tmdgh1592 May 15, 2023
a0358ba
test(ShoppingPresenterTest): 테스트 코드 작성
tmdgh1592 May 15, 2023
c7b10ca
test(ProductDetailPresenterTest): 테스트 코드 작성
tmdgh1592 May 15, 2023
12a78fe
feat(PageNumber): 페이지 번호 검증 기능 구현
tmdgh1592 May 15, 2023
e1a75ee
feat(PageNumberTest): 페이지 번호 도메인 테스트 코드 작성
tmdgh1592 May 15, 2023
8f06583
test(BasketProductTest): 장바구니 도메인 모델 테스트 코드 작성
tmdgh1592 May 15, 2023
c22b7bb
fix(Products): 불러오기 단위가 1일 때, 항상 더 불러올 수 없는 상태인 버그 수정
tmdgh1592 May 15, 2023
5ce0130
test(ProductsTest): 제품 목록 도메인 모델 테스트 코드 작성
tmdgh1592 May 15, 2023
c072c6d
test(RecentProductsTest): 최근 조회 목록 도메인 모델 테스트 코드 작성
tmdgh1592 May 15, 2023
bafa0a9
refactor: View 네이밍 축약어를 사용하지 않도록 변경
tmdgh1592 May 15, 2023
047bad6
refactor: 코드 포맷팅
tmdgh1592 May 15, 2023
fdeecba
Merge remote-tracking branch 'origin/step1' into step1
tmdgh1592 May 15, 2023
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
26 changes: 25 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1 +1,25 @@
# android-shopping-cart
# android-shopping-cart

## Domain
### 개념 관점

- 상품
- 가격
- 장바구니

### 명세 관점
- 장바구니에 상품을 담는다.
- 장바구니에서 상품을 삭제한다.
- 상품을 조회하면 최근 본 상품 목록에 추가한다.
- 최근 본 상품은 최대 10개까지 가져온다.
- 상품 데이터를 불러온다.
- 최근 본 상품 데이터를 불러온다.
- 장바구니 항목을 불러온다.


## UI
- 상품을 클릭하면 상품 상세로 이동한다.
- 장바구니 목록을 조회할 수 있다.
- 최근 본 상품을 조회할 수 있다.
- 상품 목록을 보여준다.
- 최근 본 상품이 있는 경우 상품 목록 상단에서 10개까지 확인할 수 있다.
17 changes: 17 additions & 0 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
plugins {
id("com.android.application")
id("org.jetbrains.kotlin.android")
id("kotlin-kapt")
id("kotlin-parcelize")
}

android {
Expand Down Expand Up @@ -33,14 +35,29 @@ android {
kotlinOptions {
jvmTarget = "11"
}
dataBinding {
enable = true
}
}

dependencies {
// 프로젝트내 의존성
implementation(project(":domain"))

implementation("androidx.core:core-ktx:1.9.0")
implementation("androidx.appcompat:appcompat:1.6.0")
implementation("com.google.android.material:material:1.7.0")
implementation("androidx.constraintlayout:constraintlayout:2.1.4")
testImplementation("junit:junit:4.13.2")
androidTestImplementation("androidx.test.ext:junit:1.1.5")
androidTestImplementation("androidx.test.espresso:espresso-core:3.5.1")

// Glide
implementation("com.github.bumptech.glide:glide:4.15.1")

// Mockk
testImplementation("io.mockk:mockk:1.13.5")

// concatAdapter
implementation("androidx.recyclerview:recyclerview:1.3.0")
}
Empty file.
10 changes: 9 additions & 1 deletion app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">

<uses-permission android:name="android.permission.INTERNET" />

<application
android:allowBackup="true"
android:dataExtractionRules="@xml/data_extraction_rules"
Expand All @@ -12,7 +14,13 @@
android:theme="@style/Theme.Shopping"
tools:targetApi="31">
<activity
android:name=".MainActivity"
android:name=".ui.basket.BasketActivity"
android:exported="true" />
<activity
android:name=".ui.productdetail.ProductDetailActivity"
android:exported="false" />
<activity
android:name=".ui.shopping.ShoppingActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
Expand Down
11 changes: 0 additions & 11 deletions app/src/main/java/woowacourse/shopping/MainActivity.kt

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package woowacourse.shopping.data.database

import android.content.Context
import android.database.sqlite.SQLiteDatabase
import android.database.sqlite.SQLiteOpenHelper
import woowacourse.shopping.data.database.contract.BasketContract
import woowacourse.shopping.data.database.contract.ProductContract
import woowacourse.shopping.data.database.contract.RecentProductContract

const val DATABASE_NAME = "ShoppingDatabase.db"
const val DATABASE_VERSION = 5

class ShoppingDatabase(context: Context) :
SQLiteOpenHelper(context, DATABASE_NAME, null, DATABASE_VERSION) {
override fun onCreate(db: SQLiteDatabase?) {
db?.execSQL(ProductContract.CREATE_TABLE_QUERY)
db?.execSQL(RecentProductContract.CREATE_TABLE_QUERY)
db?.execSQL(BasketContract.CREATE_TABLE_QUERY)
}

override fun onUpgrade(db: SQLiteDatabase?, old: Int, new: Int) {
db?.execSQL(ProductContract.DELETE_TABLE_QUERY)
db?.execSQL(RecentProductContract.DELETE_TABLE_QUERY)
db?.execSQL(BasketContract.DELETE_TABLE_QUERY)
onCreate(db)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package woowacourse.shopping.data.database.contract

import android.provider.BaseColumns

object BasketContract {
internal const val TABLE_NAME = "BASKET_TABLE"
internal const val COLUMN_NAME = "name"
internal const val COLUMN_PRICE = "price"
internal const val COLUMN_IMAGE_URL = "image_url"
internal const val COLUMN_CREATED = "created"

internal val CREATE_TABLE_QUERY = """
CREATE TABLE IF NOT EXISTS $TABLE_NAME (
${BaseColumns._ID} INTEGER PRIMARY KEY AUTOINCREMENT,
$COLUMN_NAME TEXT,
$COLUMN_PRICE INTEGER,
$COLUMN_IMAGE_URL TEXT,
$COLUMN_CREATED LONG
)
""".trimIndent()

internal val DELETE_TABLE_QUERY = """
DROP TABLE IF EXISTS $TABLE_NAME
""".trimIndent()
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package woowacourse.shopping.data.database.contract

import android.provider.BaseColumns

object ProductContract {
internal const val TABLE_NAME = "PRODUCT_TABLE"
internal const val COLUMN_NAME = "name"
internal const val COLUMN_PRICE = "price"
internal const val COLUMN_IMAGE_URL = "image_url"

internal val CREATE_TABLE_QUERY = """
CREATE TABLE IF NOT EXISTS $TABLE_NAME (
${BaseColumns._ID} INTEGER PRIMARY KEY AUTOINCREMENT,
$COLUMN_NAME TEXT,
$COLUMN_PRICE INTEGER,
$COLUMN_IMAGE_URL TEXT
)
""".trimIndent()

internal val DELETE_TABLE_QUERY = """
DROP TABLE IF EXISTS $TABLE_NAME
""".trimIndent()
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package woowacourse.shopping.data.database.contract

import android.provider.BaseColumns

object RecentProductContract {
internal const val TABLE_NAME = "RECENT_PRODUCT_TABLE"
internal const val COLUMN_NAME = "name"
internal const val COLUMN_PRICE = "price"
internal const val COLUMN_IMAGE_URL = "image_url"

internal val CREATE_TABLE_QUERY = """
CREATE TABLE IF NOT EXISTS $TABLE_NAME (
${BaseColumns._ID} INTEGER PRIMARY KEY AUTOINCREMENT,
${ProductContract.TABLE_NAME}${BaseColumns._ID},
$COLUMN_NAME TEXT,
$COLUMN_PRICE INTEGER,
$COLUMN_IMAGE_URL TEXT
)
""".trimIndent()

internal val DELETE_TABLE_QUERY = """
DROP TABLE IF EXISTS $TABLE_NAME
""".trimIndent()
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package woowacourse.shopping.data.database.dao.basket

import woowacourse.shopping.data.model.DataPageNumber
import woowacourse.shopping.data.model.DataProduct

interface BasketDao {
fun getPartially(page: DataPageNumber): List<DataProduct>
fun add(product: DataProduct)
fun remove(product: DataProduct)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
package woowacourse.shopping.data.database.dao.basket

import android.annotation.SuppressLint
import android.content.ContentValues
import android.provider.BaseColumns
import woowacourse.shopping.data.database.ShoppingDatabase
import woowacourse.shopping.data.database.contract.BasketContract
import woowacourse.shopping.data.model.DataPageNumber
import woowacourse.shopping.data.model.DataPrice
import woowacourse.shopping.data.model.DataProduct
import woowacourse.shopping.util.extension.safeSubList

class BasketDaoImpl(private val database: ShoppingDatabase) : BasketDao {
@SuppressLint("Range")
override fun getPartially(page: DataPageNumber): List<DataProduct> {
val products = mutableListOf<DataProduct>()

val db = database.writableDatabase
val cursor = db.rawQuery(GET_ALL_QUERY, null)

while (cursor.moveToNext()) {
val id: Int = cursor.getInt(cursor.getColumnIndex(BaseColumns._ID))
val name: String =
cursor.getString(cursor.getColumnIndex(BasketContract.COLUMN_NAME))
val price: DataPrice =
DataPrice(cursor.getInt(cursor.getColumnIndex(BasketContract.COLUMN_PRICE)))
val imageUrl: String =
cursor.getString(cursor.getColumnIndex(BasketContract.COLUMN_IMAGE_URL))
products.add(DataProduct(id, name, price, imageUrl))
}
cursor.close()

return products.safeSubList(page.start, page.end)
}

override fun add(product: DataProduct) {
val contentValues = ContentValues().apply {
put(BasketContract.COLUMN_NAME, product.name)
put(BasketContract.COLUMN_PRICE, product.price.value)
put(BasketContract.COLUMN_IMAGE_URL, product.imageUrl)
put(BasketContract.COLUMN_CREATED, System.currentTimeMillis())
}

database.writableDatabase.insert(BasketContract.TABLE_NAME, null, contentValues)
}

override fun remove(product: DataProduct) {
database.writableDatabase.delete(
BasketContract.TABLE_NAME,
"${BaseColumns._ID} = ?",
arrayOf(product.id.toString())
)
}

companion object {
private val GET_ALL_QUERY = """
SELECT * FROM ${BasketContract.TABLE_NAME}
""".trimIndent()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package woowacourse.shopping.data.database.dao.product

import woowacourse.shopping.data.model.DataProduct

interface ProductDao {
fun getPartially(size: Int, lastId: Int): List<DataProduct>
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package woowacourse.shopping.data.database.dao.product

import android.annotation.SuppressLint
import android.content.ContentValues
import android.database.sqlite.SQLiteOpenHelper
import android.provider.BaseColumns
import woowacourse.shopping.data.database.contract.ProductContract
import woowacourse.shopping.data.model.DataPrice
import woowacourse.shopping.data.model.DataProduct

class ProductDaoImpl(private val database: SQLiteOpenHelper) : ProductDao {
@SuppressLint("Range")
override fun getPartially(size: Int, lastId: Int): List<DataProduct> {
val products = mutableListOf<DataProduct>()
val db = database.writableDatabase
val cursor =
db.rawQuery(GET_PARTIALLY_QUERY, arrayOf(lastId.toString(), size.toString()))
while (cursor.moveToNext()) {
val id: Int = cursor.getInt(cursor.getColumnIndex(BaseColumns._ID))
val name: String =
cursor.getString(cursor.getColumnIndex(ProductContract.COLUMN_NAME))
val price: DataPrice =
DataPrice(cursor.getInt(cursor.getColumnIndex(ProductContract.COLUMN_PRICE)))
val imageUrl: String =
cursor.getString(cursor.getColumnIndex(ProductContract.COLUMN_IMAGE_URL))
products.add(DataProduct(id, name, price, imageUrl))
}
cursor.close()
return products
}

fun add(product: DataProduct) {
val contentValues = ContentValues().apply {
put(ProductContract.COLUMN_NAME, product.name)
put(ProductContract.COLUMN_PRICE, product.price.value)
put(ProductContract.COLUMN_IMAGE_URL, product.imageUrl)
}

database.writableDatabase.insert(ProductContract.TABLE_NAME, null, contentValues)
}

companion object {
private val GET_PARTIALLY_QUERY = """
SELECT * FROM ${ProductContract.TABLE_NAME}
WHERE ${BaseColumns._ID} > ?
ORDER BY ${BaseColumns._ID} LIMIT ?
""".trimIndent()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package woowacourse.shopping.data.database.dao.recentproduct

import woowacourse.shopping.data.model.DataRecentProduct

interface RecentProductDao {
fun getSize(): Int
fun getPartially(size: Int): List<DataRecentProduct>
fun add(recentProduct: DataRecentProduct)
fun removeLast()
}
Loading