Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
bd83a7e
내가 찜한 메뉴 내비게이션 구현
nyunn2 Aug 21, 2025
b4e45be
알림 받을 메뉴 레이아웃 생성
nyunn2 Aug 23, 2025
c377b73
내가 찜한 메뉴 api 연결 (작동 X)
nyunn2 Sep 10, 2025
1b7fb30
알림 받기 api 연결 (작동 확인 X)
nyunn2 Sep 10, 2025
70802e5
찜한 메뉴 -> 알림 메뉴 연결
nyunn2 Sep 10, 2025
8732763
알림 프래그먼트 UI 수정
nyunn2 Sep 10, 2025
4a8a532
내가 찜한 메뉴 UI 수정
nyunn2 Sep 11, 2025
b43cee5
알림 받을 메뉴 UI 수정
nyunn2 Sep 12, 2025
f57100e
스케일, 여백 조정
nyunn2 Sep 12, 2025
16021f5
찜한 메뉴, 알림 설정 UI 수정
nyunn2 Sep 26, 2025
4216873
기본 UI 구현
nyunn2 Sep 28, 2025
7af5350
찜한 메뉴 모달 생성
nyunn2 Oct 2, 2025
b6a1b42
여백 수정
nyunn2 Oct 2, 2025
dd55cc9
Merge branch 'develop' of https://github.com/wafflestudio/siksha-andr…
nyunn2 Oct 5, 2025
1dca9c6
develop 병합
nyunn2 Oct 5, 2025
55113e7
api 연결
nyunn2 Oct 12, 2025
efa2f42
ui 수정
nyunn2 Oct 14, 2025
a92df5f
Merge remote-tracking branch 'origin/develop' into nyunn2/favorite-menu
nyunn2 Oct 24, 2025
f38c544
내찜메 1차 QA
nyunn2 Nov 4, 2025
b845f48
알림 api 연결
nyunn2 Nov 23, 2025
135748c
2차 QA
nyunn2 Dec 3, 2025
9dc5767
푸시 알림 권한 팝업 추가, 채널 수정
nyunn2 Dec 22, 2025
c8ed49d
모든 메뉴 알림 api 추가
nyunn2 Dec 27, 2025
3272852
식당 즐겨찾기 오류 수정
nyunn2 Dec 29, 2025
b2971a5
버그, 푸시 아이콘 수정
nyunn2 Jan 2, 2026
e1e150b
토글 스위치 터치 오류 수정
nyunn2 Jan 4, 2026
fe2dc9b
토스트 시점, UI 변경 시점 수정
nyunn2 Jan 4, 2026
5a78836
conflict 해결
nyunn2 Jan 7, 2026
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
1 change: 1 addition & 0 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,7 @@ dependencies {
implementation("com.google.firebase:firebase-crashlytics-ktx")
implementation("com.google.firebase:firebase-analytics-ktx")
implementation("com.google.firebase:firebase-config-ktx")
implementation("com.google.firebase:firebase-messaging-ktx")

// Glide
implementation("com.github.bumptech.glide:glide:4.15.1")
Expand Down
17 changes: 17 additions & 0 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
<uses-permission
android:name="android.permission.READ_EXTERNAL_STORAGE"
android:maxSdkVersion="32" />
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />

<queries>
<package android:name="com.google.android.apps.maps" />
Expand Down Expand Up @@ -45,6 +46,14 @@
android:name="com.google.android.geo.API_KEY"
android:value="@string/google_maps_key" />

<meta-data
android:name="com.google.firebase.messaging.default_notification_channel_id"
android:value="siksha_channel" />

<meta-data
android:name="com.google.firebase.messaging.default_notification_icon"
android:resource="@drawable/ic_notification" />

<activity
android:name="com.wafflestudio.siksha2.ui.RootActivity"
android:screenOrientation="portrait"
Expand Down Expand Up @@ -123,6 +132,14 @@
android:name=".ui.common.ImageViewerActivity"
android:exported="false"
android:theme="@style/Theme.Siksha.ImageViewer" />

<service
android:name=".fcm.SikshaFirebaseService"
android:exported="false">
<intent-filter>
<action android:name="com.google.firebase.MESSAGING_EVENT" />
</intent-filter>
</service>
</application>

</manifest>
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ import com.wafflestudio.siksha2.databinding.ItemSettingRowBinding
import com.wafflestudio.siksha2.utils.setVisibleOrGone

class SettingItemRow : LinearLayout {

private val binding = ItemSettingRowBinding.inflate(LayoutInflater.from(context), this)
var checked: Boolean = false
get() = binding.checkbox.isSelected
Expand Down Expand Up @@ -54,6 +53,30 @@ class SettingItemRow : LinearLayout {
invalidate()
}

fun setShowCheckSimple(visible: Boolean) {
binding.checkSimple.setVisibleOrGone(visible)
requestLayout()
invalidate()
}

fun setShowToggleSwitch(visible: Boolean) {
binding.toggleSwitch.setVisibleOrGone(visible)

isClickable = false
isFocusable = false
}

fun setToggleState(active: Boolean) {
binding.toggleSwitch.setActive(active)
}

fun setOnToggleClicked(listener: (Boolean) -> Unit) {
binding.toggleSwitch.setOnClickListener {
binding.toggleSwitch.toggle()
listener(binding.toggleSwitch.isActive)
}
}

private fun init(attr: AttributeSet?) {
gravity = Gravity.CENTER_VERTICAL
orientation = HORIZONTAL
Expand All @@ -68,6 +91,9 @@ class SettingItemRow : LinearLayout {
setArrowIcon(getBoolean(R.styleable.SettingItem_showArrowIcon, true))
setNewIcon(getBoolean(R.styleable.SettingItem_showNewIcon, false))
setShowCheckbox(getBoolean(R.styleable.SettingItem_showCheckbox, false))
setShowCheckSimple(getBoolean(R.styleable.SettingItem_showCheckSimple, false))
setShowToggleSwitch(getBoolean(R.styleable.SettingItem_showToggleSwitch, false))

binding.settingRowText.text = getString(R.styleable.SettingItem_itemText)
binding.settingRowText.setTextColor(context.obtainStyledAttributes(attr, R.styleable.SettingItem).getColor(R.styleable.SettingItem_textColor, ContextCompat.getColor(context, R.color.black)))
} finally {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
package com.wafflestudio.siksha2.components

import android.content.Context
import android.graphics.Rect
import android.util.AttributeSet
import android.view.LayoutInflater
import android.view.MotionEvent
import android.view.TouchDelegate
import android.view.View
import android.view.animation.AccelerateDecelerateInterpolator
import android.widget.FrameLayout
import android.widget.ImageView
import com.wafflestudio.siksha2.R

class ToggleSwitchView @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0
) : FrameLayout(context, attrs, defStyleAttr) {

private val bgActive: ImageView
private val bgInactive: ImageView
private val knob: ImageView

var isActive = false
private set

init {
LayoutInflater.from(context).inflate(R.layout.item_toggle_switch, this, true)
bgActive = findViewById(R.id.bg_active)
bgInactive = findViewById(R.id.bg_inactive)
knob = findViewById(R.id.toggle_knob)
descendantFocusability = FOCUS_BLOCK_DESCENDANTS

post {
val minTouchSize = (48 * resources.displayMetrics.density).toInt()
val parent = this.parent as? View ?: return@post
parent.post {
val rect = Rect()
getHitRect(rect)
val widthDiff = (minTouchSize - rect.width()).coerceAtLeast(0) / 2
val heightDiff = (minTouchSize - rect.height()).coerceAtLeast(0) / 2
rect.inset(-widthDiff, -heightDiff)
parent.touchDelegate = TouchDelegate(rect, this)
}
}

isClickable = true
isFocusable = true
setOnClickListener { toggle() }
updateAppearance(animated = false)
}

fun toggle() {
isActive = !isActive
updateAppearance(animated = true)
}

fun setActive(active: Boolean, animated: Boolean = false) {
if (isActive != active) {
isActive = active
updateAppearance(animated)
}
}

private fun updateAppearance(animated: Boolean) {
bgActive.visibility = if (isActive) VISIBLE else GONE
bgInactive.visibility = if (isActive) GONE else VISIBLE

post {
val knobMarginPx = 2f.dp

val maxTravel = (width - knob.width - knobMarginPx * 2)

val targetX = if (isActive) {
knobMarginPx + maxTravel
} else {
knobMarginPx
}

if (animated) {
knob.animate()
.x(targetX)
.setInterpolator(AccelerateDecelerateInterpolator())
.setDuration(150)
.start()
} else {
knob.x = targetX
}
}
}

private val Float.dp: Float
get() = this * resources.displayMetrics.density

override fun onTouchEvent(event: MotionEvent): Boolean {
return super.onTouchEvent(event)
}

override fun dispatchTouchEvent(ev: MotionEvent): Boolean {
return super.dispatchTouchEvent(ev)
}

override fun onInterceptTouchEvent(ev: MotionEvent): Boolean {
return true
}
}
24 changes: 23 additions & 1 deletion app/src/main/java/com/wafflestudio/siksha2/di/NetworkModule.kt
Original file line number Diff line number Diff line change
Expand Up @@ -24,14 +24,36 @@ import javax.inject.Singleton
object NetworkModule {
@Provides
@Singleton
fun provideHttpClient(sikshaPrefObjects: SikshaPrefObjects): OkHttpClient {
fun provideHttpClient(
@ApplicationContext context: Context,
sikshaPrefObjects: SikshaPrefObjects
): OkHttpClient {
return OkHttpClient.Builder()
/*
.addInterceptor { chain ->
val newRequest = chain.request().newBuilder()
.header(AUTH_TOKEN_HEADER_KEY, sikshaPrefObjects.accessToken.getValue())
.build()
chain.proceed(newRequest)
}

*/
.addInterceptor { chain ->
val request = chain.request()
val builder = request.newBuilder()

val path = request.url.encodedPath
val isLoginRequest = path.contains("/auth/login")

if (!isLoginRequest) {
val newRequest = builder
.header(AUTH_TOKEN_HEADER_KEY, sikshaPrefObjects.accessToken.getValue())
.build()
chain.proceed(newRequest)
} else {
chain.proceed(request)
}
}
.addInterceptor(
HttpLoggingInterceptor().apply {
level =
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
package com.wafflestudio.siksha2.fcm

import android.graphics.Bitmap
import android.graphics.BitmapFactory
import android.graphics.Canvas
import android.graphics.Paint
import android.graphics.RectF
import android.util.Log
import androidx.core.app.NotificationCompat
import com.google.firebase.messaging.FirebaseMessagingService
import com.google.firebase.messaging.RemoteMessage
import com.wafflestudio.siksha2.R
import com.wafflestudio.siksha2.preferences.SikshaPrefObjects
import dagger.hilt.android.AndroidEntryPoint
import javax.inject.Inject

@AndroidEntryPoint
class SikshaFirebaseService : FirebaseMessagingService() {

@Inject lateinit var prefs: SikshaPrefObjects

override fun onNewToken(token: String) {
super.onNewToken(token)
Log.d("FCM", "New FCM token generated: $token")
prefs.fcmToken.setValue(token)
}

override fun onMessageReceived(remoteMessage: RemoteMessage) {
Log.d("FCM", "notification=${remoteMessage.notification}")
Log.d("FCM", "data=${remoteMessage.data}")

val title = remoteMessage.notification?.title
?: remoteMessage.data["title"]
?: "식샤"

val body = remoteMessage.notification?.body
?: remoteMessage.data["body"]
?: ""

val notificationManager =
getSystemService(NOTIFICATION_SERVICE) as android.app.NotificationManager

val builder = NotificationCompat.Builder(this, "siksha_channel")
// .setSmallIcon(R.drawable.siksha_rice_bowl)
.setSmallIcon(R.drawable.ic_notification)
// .setLargeIcon(createNotificationLargeIcon())
.setContentTitle(title)
.setContentText(body)
.setAutoCancel(true)

notificationManager.notify(
System.currentTimeMillis().toInt(),
builder.build()
)
}

private fun createNotificationLargeIcon(): Bitmap {
val size = 39
val radius = 8.5f

val bitmap = Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_8888)
val canvas = Canvas(bitmap)

val paintBg = Paint().apply {
color = resources.getColor(R.color.orange_500, null)
isAntiAlias = true
}

val rect = RectF(0f, 0f, size.toFloat(), size.toFloat())
canvas.drawRoundRect(rect, radius, radius, paintBg)

val riceBmp = BitmapFactory.decodeResource(resources, R.drawable.siksha_rice_bowl)

val iconSize = (size * 0.55).toInt()
val left = (size - iconSize) / 2
val top = (size - iconSize) / 2
val resized = Bitmap.createScaledBitmap(riceBmp, iconSize, iconSize, true)

canvas.drawBitmap(resized, left.toFloat(), top.toFloat(), null)

return bitmap
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ data class RestaurantInfo(
@Parcelize
@JsonClass(generateAdapter = true)
data class Extra(
@Json(name = "operating_hours") val operatingHours: OperatingHour
@Json(name = "operating_hours") val operatingHours: OperatingHour? = null
) : Parcelable

@Parcelize
Expand Down
Loading
Loading