diff --git a/.gitignore b/.gitignore index ad4cf3e..0fe447f 100644 --- a/.gitignore +++ b/.gitignore @@ -4,10 +4,12 @@ /local.properties /.idea/caches /.idea/libraries +/.idea/.name /.idea/modules.xml /.idea/workspace.xml /.idea/navEditor.xml /.idea/assetWizardSettings.xml +/.idea/deploymentTargetDropDown.xml .DS_Store /build /captures @@ -39,4 +41,3 @@ local.properties # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml hs_err_pid* replay_pid* - diff --git a/.idea/.name b/.idea/.name deleted file mode 100644 index 460bbdd..0000000 --- a/.idea/.name +++ /dev/null @@ -1 +0,0 @@ -Apic \ No newline at end of file diff --git a/.idea/compiler.xml b/.idea/compiler.xml index b589d56..fb7f4a8 100644 --- a/.idea/compiler.xml +++ b/.idea/compiler.xml @@ -1,6 +1,6 @@ - + \ No newline at end of file diff --git a/.idea/gradle.xml b/.idea/gradle.xml index ae388c2..6d4d622 100644 --- a/.idea/gradle.xml +++ b/.idea/gradle.xml @@ -7,11 +7,12 @@ diff --git a/.idea/misc.xml b/.idea/misc.xml index 4412b1a..a460dc7 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -1,7 +1,7 @@ - - + + diff --git a/.idea/shelf/Uncommitted_changes_before_Checkout_at_2023-11-25__2_01__Changes_.xml b/.idea/shelf/Uncommitted_changes_before_Checkout_at_2023-11-25__2_01__Changes_.xml new file mode 100644 index 0000000..0358942 --- /dev/null +++ b/.idea/shelf/Uncommitted_changes_before_Checkout_at_2023-11-25__2_01__Changes_.xml @@ -0,0 +1,4 @@ + + \ No newline at end of file diff --git a/.idea/shelf/Uncommitted_changes_before_Checkout_at_2023-11-25__2_02__Changes_.xml b/.idea/shelf/Uncommitted_changes_before_Checkout_at_2023-11-25__2_02__Changes_.xml new file mode 100644 index 0000000..4c35eed --- /dev/null +++ b/.idea/shelf/Uncommitted_changes_before_Checkout_at_2023-11-25__2_02__Changes_.xml @@ -0,0 +1,4 @@ + + \ No newline at end of file diff --git "a/.idea/shelf/Uncommitted_changes_before_Checkout_at_2023-11-25_\354\230\244\355\233\204_2_01_[Changes]/shelved.patch" "b/.idea/shelf/Uncommitted_changes_before_Checkout_at_2023-11-25_\354\230\244\355\233\204_2_01_[Changes]/shelved.patch" new file mode 100644 index 0000000..e69de29 diff --git "a/.idea/shelf/Uncommitted_changes_before_Checkout_at_2023-11-25_\354\230\244\355\233\204_2_02_[Changes]/shelved.patch" "b/.idea/shelf/Uncommitted_changes_before_Checkout_at_2023-11-25_\354\230\244\355\233\204_2_02_[Changes]/shelved.patch" new file mode 100644 index 0000000..57efc7a --- /dev/null +++ "b/.idea/shelf/Uncommitted_changes_before_Checkout_at_2023-11-25_\354\230\244\355\233\204_2_02_[Changes]/shelved.patch" @@ -0,0 +1,19 @@ +Index: .idea/misc.xml +IDEA additional info: +Subsystem: com.intellij.openapi.diff.impl.patch.BaseRevisionTextPatchEP +<+>\r\n\r\n \r\n \r\n \r\n \r\n \r\n \r\n +Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP +<+>UTF-8 +=================================================================== +diff --git a/.idea/misc.xml b/.idea/misc.xml +--- a/.idea/misc.xml (revision 559aba0c29d96e6d12bd8e1c8df4f32f10d34323) ++++ b/.idea/misc.xml (date 1700888503945) +@@ -1,7 +1,7 @@ + + + +- ++ + + + diff --git "a/.idea/shelf/Uncommitted_changes_before_Update_at_11_22_23,_1_39\342\200\257PM_[Changes]/shelved.patch" "b/.idea/shelf/Uncommitted_changes_before_Update_at_11_22_23,_1_39\342\200\257PM_[Changes]/shelved.patch" new file mode 100644 index 0000000..e146616 --- /dev/null +++ "b/.idea/shelf/Uncommitted_changes_before_Update_at_11_22_23,_1_39\342\200\257PM_[Changes]/shelved.patch" @@ -0,0 +1,23 @@ +Index: app/src/main/java/com/Apic/apic/MainActivity.kt +IDEA additional info: +Subsystem: com.intellij.openapi.diff.impl.patch.BaseRevisionTextPatchEP +<+>package com.Apic.apic\n\n\nimport android.content.Intent\nimport androidx.appcompat.app.AppCompatActivity\nimport android.os.Bundle\nimport com.Apic.apic.databinding.ActivityMainBinding\nimport androidx.fragment.app.FragmentManager\nimport androidx.fragment.app.FragmentTransaction\nimport com.google.android.material.bottomnavigation.BottomNavigationView\n\nclass MainActivity : AppCompatActivity() {\n lateinit var binding: ActivityMainBinding\n\n private val fragmentManager: FragmentManager = supportFragmentManager\n private val fragmentCalendar = CalendarFragment()\n private val fragmentFriend = FriendFragment()\n private val GroupListActivity = GroupListActivity()\n\n override fun onCreate(savedInstanceState: Bundle?) {\n super.onCreate(savedInstanceState)\n\n binding = ActivityMainBinding.inflate(layoutInflater)\n setContentView(binding.root)\n\n setSupportActionBar(findViewById(R.id.toolbar)) // 뒤로가기 메뉴\n\n val transaction: FragmentTransaction = fragmentManager.beginTransaction()\n transaction.replace(R.id.menu_frame_view, fragmentCalendar).commitAllowingStateLoss()\n\n val bottomNavigationView = findViewById(R.id.bottom_navigationview)\n bottomNavigationView.setOnNavigationItemSelectedListener { menuItem ->\n val transaction = fragmentManager.beginTransaction()\n\n when (menuItem.itemId) {\n R.id.menu_home -> transaction.replace(R.id.menu_frame_view, fragmentCalendar).commitAllowingStateLoss()\n R.id.menu_friend -> transaction.replace(R.id.menu_frame_view, fragmentFriend).commitAllowingStateLoss()\n R.id.menu_share -> {\n val intent = Intent(this, GroupListActivity::class.java)\n startActivity(intent)\n }\n }\n true\n }\n\n // group activity test code\n val intent = Intent(this, GroupActivity::class.java)\n startActivity(intent)\n finish()\n\n }\n\n} +Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP +<+>UTF-8 +=================================================================== +diff --git a/app/src/main/java/com/Apic/apic/MainActivity.kt b/app/src/main/java/com/Apic/apic/MainActivity.kt +--- a/app/src/main/java/com/Apic/apic/MainActivity.kt (revision 0085522fd05b6d49900a62f19fbd11bdf05424da) ++++ b/app/src/main/java/com/Apic/apic/MainActivity.kt (date 1700627154050) +@@ -44,9 +44,9 @@ + } + + // group activity test code +- val intent = Intent(this, GroupActivity::class.java) +- startActivity(intent) +- finish() ++ // val intent = Intent(this, GroupActivity::class.java) ++ // startActivity(intent) ++ // finish() + + } + diff --git a/.idea/shelf/Uncommitted_changes_before_Update_at_11_22_23__1_39PM__Changes_.xml b/.idea/shelf/Uncommitted_changes_before_Update_at_11_22_23__1_39PM__Changes_.xml new file mode 100644 index 0000000..e1d87da --- /dev/null +++ b/.idea/shelf/Uncommitted_changes_before_Update_at_11_22_23__1_39PM__Changes_.xml @@ -0,0 +1,4 @@ + + \ No newline at end of file diff --git a/README.md b/README.md index 735b550..a2e860a 100644 --- a/README.md +++ b/README.md @@ -1 +1,70 @@ -# AppPicProj \ No newline at end of file +# AppPicProj +
+image +
+ +# AppPic +> **덕성여대 소프트웨어공학 3팀**
**개발기간: 2023.09 ~ 2023.11** + +## 깃헙 산출물 +
+PicTogether +
+ +> **개발 버전** : [https://github.com/BaileyChoi/AppPicProj.git](https://github.com/BaileyChoi/AppPicProj.git)
+ +## 앱개발팀 소개 + +| 헤이 | 깡지 | 말이 | 도도 | 호요요 | +|:-------------------------------------------------------------------------------------------------------------------------:|:-------------------------------------------------------------------------------------------------------------------------:|:-------------------------------------------------------------------------------------------------------------------------:|:-------------------------------------------------------------------------------------------------------------------------:|:-------------------------------------------------------------------------------------------------------------------------:| +| | | | | | +| [@BaileyChoi](https://github.com/BaileyChoi) | [@dontworrywony](https://github.com/dontworrywony) | [@nar0ng](https://github.com/nar0ng) | [@hyunn0121](https://github.com/hyunn0121) | [@19013na](https://github.com/19013na) | +| PM | Design | Infra | Developer | Developer | + +## 프로젝트 소개 + +저희 AppPic에서 만든 PicTogether라는 어플은 기존의 모임어플에서의 불편함을 느껴 사용자들이 더 편리하게 모임을 관리하고 사진을 공유할 수 있도록 하기 위해 개발되었습니다. + +--- + +## Stacks 🐈 + +### Environment +![Android Studio](https://img.shields.io/badge/AndroidStudio-3DDC84?style=for-the-badge&logo=AndroidStudio&logoColor=white) +![Git](https://img.shields.io/badge/Git-F05032?style=for-the-badge&logo=Git&logoColor=white) +![Github](https://img.shields.io/badge/GitHub-181717?style=for-the-badge&logo=GitHub&logoColor=white) +![Firebase](https://img.shields.io/badge/Firebase-FFCA28?style=for-the-badge&logo=firebase&logoColor=black) +![OpenCV](https://img.shields.io/badge/opencv-5C3EE8?style=for-the-badge&logo=opencv&logoColor=black) +![Figma](https://img.shields.io/badge/figma-F24E1E.svg?style=for-the-badge&logo=figma&logoColor=white) + +### Development +![Kotlin](https://img.shields.io/badge/Kotlin-7F52FF?style=for-the-badge&logo=Kotlin&logoColor=white) +![Java](https://img.shields.io/badge/java-007396?style=for-the-badge&logo=OpenJDK&logoColor=white") + +### Communication +![Notion](https://img.shields.io/badge/Notion-000000?style=for-the-badge&logo=Notion&logoColor=white) +![GoogleMeet](https://img.shields.io/badge/GoogleMeet-00897B?style=for-the-badge&logo=Google%20Meet&logoColor=white) + +--- +## 화면 구성 📺 +| 홈 페이지 | 친구 페이지 | +|:------------------------------------------------------------------------------------------------------------------------------:|:------------------------------------------------------------------------------------------------------------------------------:| +| | | +| 모임 페이지 | 네컷 페이지 | +| | | + +--- +## 주요 기능 📦 + +### ⭐️ 약속 캘린더 기능 +- 캘린더에 약속을 추가하여 언제 어떤 약속이 있는지 한눈에 확인 가능 + +### ⭐️ 친구 검색 및 추가 기능 +- PicTogether을 사용하는 회원 중 친구를 검색하여 친구 목록에 추가 가능 + +### ⭐️ 모임 관리 및 모임 공유 사진첩 기능 +- 친구목록에서 원하는 친구를 골라 모임명과 함께 모임을 생성하고 목록에서 관리 가능 +- 공유하고 싶은 사진을 갤러리에서 골라 모임 공유 사진첩에 업로드 가능 + +### ⭐️ 사진 편집 기능 +- 네컷사진의 프레임 색상을 변경하거나 다양한 효과를 적용하여 저장 가능 \ No newline at end of file diff --git a/app/build.gradle b/app/build.gradle index f5ad898..f333a77 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -4,6 +4,9 @@ plugins { id 'com.google.gms.google-services' + id 'kotlin-android' + id 'kotlin-kapt' + } android { @@ -44,6 +47,7 @@ android { } buildFeatures { compose true + viewBinding = true } composeOptions { kotlinCompilerExtensionVersion '1.4.3' @@ -52,15 +56,26 @@ android { resources { excludes += '/META-INF/{AL2.0,LGPL2.1}' } + pickFirst 'lib/arm64-v8a/libc++_shared.so' + pickFirst 'lib/x86/libc++_shared.so' + pickFirst 'lib/x86_64/libc++_shared.so' + pickFirst 'lib/armeabi -v7a/libc++_shared.so' } + } dependencies { + // OpenCV 4.5.1 + implementation project(":opencv") + implementation 'androidx.core:core-ktx:1.7.0' + implementation 'androidx.appcompat:appcompat:1.6.1' implementation 'com.google.android.material:material:1.10.0' implementation 'androidx.constraintlayout:constraintlayout:2.1.4' + + implementation 'androidx.viewpager2:viewpager2:1.0.0' implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.6.2' implementation 'androidx.activity:activity-compose:1.8.0' @@ -81,9 +96,26 @@ dependencies { debugImplementation 'androidx.compose.ui:ui-tooling' debugImplementation 'androidx.compose.ui:ui-test-manifest' + // glide + implementation 'com.github.bumptech.glide:glide:4.11.0' + annotationProcessor 'com.github.bumptech.glide:compiler:4.11.0' + implementation(platform("com.google.firebase:firebase-bom:32.6.0")) implementation 'com.google.firebase:firebase-auth-ktx:22.3.0' + implementation 'com.google.firebase:firebase-firestore:24.9.1' // implementation 'com.google.android.gms:play-services-auth:20.7.0' // 구글 인증 + implementation("androidx.lifecycle:lifecycle-runtime:2.6.0") + implementation "androidx.room:room-runtime:2.6.0" + kapt("androidx.room:room-compiler:2.6.0") + implementation("androidx.room:room-ktx:2.6.0") + + implementation "androidx.lifecycle:lifecycle-viewmodel:2.3.1" + implementation "androidx.lifecycle:lifecycle-livedata:2.3.1" + kapt "androidx.lifecycle:lifecycle-compiler:2.3.1" +} + +kapt { + generateStubs = true } \ No newline at end of file diff --git a/app/src/androidTest/java/com/example/appapicprj/ExampleInstrumentedTest.kt b/app/src/androidTest/java/com/example/appapicprj/ExampleInstrumentedTest.kt new file mode 100644 index 0000000..ca7915c --- /dev/null +++ b/app/src/androidTest/java/com/example/appapicprj/ExampleInstrumentedTest.kt @@ -0,0 +1,24 @@ +package com.example.appapicprj + +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.appapicprj", appContext.packageName) + } +} \ No newline at end of file diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 893347d..88e67d1 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -2,6 +2,11 @@ xmlns:tools="http://schemas.android.com/tools" package="com.Apic.apic"> + + + + + - - - - - - - + \ No newline at end of file diff --git a/app/src/main/assets/KakaoTalk_20231128_191806227.jpg b/app/src/main/assets/KakaoTalk_20231128_191806227.jpg new file mode 100644 index 0000000..fd70212 Binary files /dev/null and b/app/src/main/assets/KakaoTalk_20231128_191806227.jpg differ diff --git a/app/src/main/assets/KakaoTalk_20231128_191806227_01.jpg b/app/src/main/assets/KakaoTalk_20231128_191806227_01.jpg new file mode 100644 index 0000000..5fc1d76 Binary files /dev/null and b/app/src/main/assets/KakaoTalk_20231128_191806227_01.jpg differ diff --git a/app/src/main/assets/KakaoTalk_20231128_191806227_02.jpg b/app/src/main/assets/KakaoTalk_20231128_191806227_02.jpg new file mode 100644 index 0000000..131cd44 Binary files /dev/null and b/app/src/main/assets/KakaoTalk_20231128_191806227_02.jpg differ diff --git a/app/src/main/assets/KakaoTalk_20231128_191806227_03.jpg b/app/src/main/assets/KakaoTalk_20231128_191806227_03.jpg new file mode 100644 index 0000000..9c957fb Binary files /dev/null and b/app/src/main/assets/KakaoTalk_20231128_191806227_03.jpg differ diff --git a/app/src/main/assets/KakaoTalk_20231128_191806227_04.jpg b/app/src/main/assets/KakaoTalk_20231128_191806227_04.jpg new file mode 100644 index 0000000..dfe985f Binary files /dev/null and b/app/src/main/assets/KakaoTalk_20231128_191806227_04.jpg differ diff --git a/app/src/main/assets/KakaoTalk_20231128_191806227_05.jpg b/app/src/main/assets/KakaoTalk_20231128_191806227_05.jpg new file mode 100644 index 0000000..dd5a052 Binary files /dev/null and b/app/src/main/assets/KakaoTalk_20231128_191806227_05.jpg differ diff --git a/app/src/main/assets/KakaoTalk_20231128_191806227_06.jpg b/app/src/main/assets/KakaoTalk_20231128_191806227_06.jpg new file mode 100644 index 0000000..03a6305 Binary files /dev/null and b/app/src/main/assets/KakaoTalk_20231128_191806227_06.jpg differ diff --git a/app/src/main/assets/KakaoTalk_20231128_191806227_07.jpg b/app/src/main/assets/KakaoTalk_20231128_191806227_07.jpg new file mode 100644 index 0000000..464f0ca Binary files /dev/null and b/app/src/main/assets/KakaoTalk_20231128_191806227_07.jpg differ diff --git a/app/src/main/assets/KakaoTalk_20231128_191806227_08.jpg b/app/src/main/assets/KakaoTalk_20231128_191806227_08.jpg new file mode 100644 index 0000000..6625ae3 Binary files /dev/null and b/app/src/main/assets/KakaoTalk_20231128_191806227_08.jpg differ diff --git a/app/src/main/assets/KakaoTalk_20231128_191806227_10.jpg b/app/src/main/assets/KakaoTalk_20231128_191806227_10.jpg new file mode 100644 index 0000000..d2e71e9 Binary files /dev/null and b/app/src/main/assets/KakaoTalk_20231128_191806227_10.jpg differ diff --git a/app/src/main/assets/KakaoTalk_20231128_191806227_11.jpg b/app/src/main/assets/KakaoTalk_20231128_191806227_11.jpg new file mode 100644 index 0000000..910f25a Binary files /dev/null and b/app/src/main/assets/KakaoTalk_20231128_191806227_11.jpg differ diff --git a/app/src/main/assets/KakaoTalk_20231128_191806227_12.jpg b/app/src/main/assets/KakaoTalk_20231128_191806227_12.jpg new file mode 100644 index 0000000..bebc299 Binary files /dev/null and b/app/src/main/assets/KakaoTalk_20231128_191806227_12.jpg differ diff --git a/app/src/main/assets/KakaoTalk_20231128_191806227_13.jpg b/app/src/main/assets/KakaoTalk_20231128_191806227_13.jpg new file mode 100644 index 0000000..8d30829 Binary files /dev/null and b/app/src/main/assets/KakaoTalk_20231128_191806227_13.jpg differ diff --git a/app/src/main/assets/KakaoTalk_20231128_191806227_14.jpg b/app/src/main/assets/KakaoTalk_20231128_191806227_14.jpg new file mode 100644 index 0000000..98bc55a Binary files /dev/null and b/app/src/main/assets/KakaoTalk_20231128_191806227_14.jpg differ diff --git a/app/src/main/assets/KakaoTalk_20231128_191806227_15.jpg b/app/src/main/assets/KakaoTalk_20231128_191806227_15.jpg new file mode 100644 index 0000000..8593810 Binary files /dev/null and b/app/src/main/assets/KakaoTalk_20231128_191806227_15.jpg differ diff --git a/app/src/main/assets/KakaoTalk_20231128_191806227_16.jpg b/app/src/main/assets/KakaoTalk_20231128_191806227_16.jpg new file mode 100644 index 0000000..fc6d78a Binary files /dev/null and b/app/src/main/assets/KakaoTalk_20231128_191806227_16.jpg differ diff --git a/app/src/main/assets/iv_fourcut_frame.png b/app/src/main/assets/iv_fourcut_frame.png new file mode 100644 index 0000000..b094fa4 Binary files /dev/null and b/app/src/main/assets/iv_fourcut_frame.png differ diff --git a/app/src/main/assets/iv_fourcut_frame_white.png b/app/src/main/assets/iv_fourcut_frame_white.png new file mode 100644 index 0000000..8a0f733 Binary files /dev/null and b/app/src/main/assets/iv_fourcut_frame_white.png differ diff --git a/app/src/main/assets/iv_fourcut_test_img.jpg b/app/src/main/assets/iv_fourcut_test_img.jpg new file mode 100644 index 0000000..0cf83aa Binary files /dev/null and b/app/src/main/assets/iv_fourcut_test_img.jpg differ diff --git a/app/src/main/assets/shrimp_kang.jpg b/app/src/main/assets/shrimp_kang.jpg new file mode 100644 index 0000000..7a7acbf Binary files /dev/null and b/app/src/main/assets/shrimp_kang.jpg differ diff --git a/app/src/main/java/com/Apic/apic/AddFriendFragment.kt b/app/src/main/java/com/Apic/apic/AddFriendFragment.kt new file mode 100644 index 0000000..a919bb2 --- /dev/null +++ b/app/src/main/java/com/Apic/apic/AddFriendFragment.kt @@ -0,0 +1,185 @@ +package com.Apic.apic + +import android.content.Context +import android.os.Bundle +import android.text.Editable +import android.text.TextWatcher +import android.util.Log +import androidx.fragment.app.Fragment +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.view.inputmethod.InputMethodManager +import android.widget.EditText +import android.widget.Toast +import androidx.recyclerview.widget.LinearLayoutManager +import com.Apic.apic.databinding.FragmentAddFriendBinding +import com.google.firebase.auth.FirebaseAuth +import com.google.firebase.firestore.FirebaseFirestore + +private const val ARG_PARAM1 = "param1" +private const val ARG_PARAM2 = "param2" + +class AddFriendFragment : Fragment(), DialogAddFriendAdapter.OnAddFriendClickListener { + // TODO: Rename and change types of parameters + private var param1: String? = null + private var param2: String? = null + + private lateinit var auth: FirebaseAuth // 친구 리스트와 자신 email 비교를 위해 가져옴. + private lateinit var binding: FragmentAddFriendBinding + private val db = FirebaseFirestore.getInstance() + //private val itemList = arrayListOf() + private lateinit var editText: EditText + private val originalList: ArrayList = ArrayList() + private val searchList: ArrayList = ArrayList() + private var adapter = DialogAddFriendAdapter(originalList) + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + arguments?.let { + param1 = it.getString(ARG_PARAM1) + param2 = it.getString(ARG_PARAM2) + } + } + + override fun onCreateView( + inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle? + ): View? { + binding = FragmentAddFriendBinding.inflate(inflater, container, false) + auth = FirebaseAuth.getInstance() + + // Close Button click listener -> FriendFragment + binding.closeBtn.setOnClickListener { + (activity as? MainActivity)?.setFragment(0) + } + + // Button click listener : search Friend email + binding.searchBtn.setOnClickListener{ + val imm = requireActivity().getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager + imm.hideSoftInputFromWindow(binding.searchView.windowToken, 0) + } + + editText = binding.searchView // 검색어 변수로 받음. + + editText.addTextChangedListener(object : TextWatcher { + override fun beforeTextChanged(charSequence: CharSequence, i: Int, i1: Int, i2: Int) { + // Optional: Add any functionality needed before text changes + } + + override fun onTextChanged(charSequence: CharSequence, i: Int, i1: Int, i2: Int) { + // Optional: Add any functionality needed during text changes + } + + + override fun afterTextChanged(editable: Editable) { + //Log.d("db", "addTextChangedListener") + val searchText = editText.text.toString().trim() //editable.toString() + //Log.d("db", "$searchText") + searchList.clear() // 검색 결과 갱신 + + if (searchText.isEmpty()) { + adapter.setFriendList(originalList) + } else { + // 검색 단어를 포함하는지 확인 + for (a in 0 until originalList.size) { + //Log.d("db", "for loop: $a") + if (originalList[a].getName().toLowerCase().contains(searchText.toLowerCase())) { + searchList.add(originalList[a]) + } + } + + //dialogFriendAdapter.setFriendList(originalList) + adapter.setFriendList(searchList) + adapter.notifyDataSetChanged() + } + } + }) + + return binding.root + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + + binding.recyclerView.layoutManager = LinearLayoutManager(requireContext(), LinearLayoutManager.VERTICAL, false) + binding.recyclerView.adapter = adapter + + // 친구 찾기에서 친구 리스트로 recyclerview로 뜨기 + db.collection("memberDB") // Collection to work with + .get() // Get documents + .addOnSuccessListener { result -> + // On success + originalList.clear() + for (document in result) { + val item = MemberData( + document["email"] as String, + document["name"] as String, + //document["password"] as String + ) + if (!auth.currentUser?.email.toString().equals(document["email"])) { // 현재 유저가 아닐때 + originalList.add(item) + } + } + adapter.notifyDataSetChanged() // Update RecyclerView + } + .addOnFailureListener { exception -> + // On failure + Log.w("AddFriendFragment", "Error getting documents: $exception") + } + + + // add + //val adapter = DialogAddFriendAdapter(itemList) + adapter.setOnAddFriendClickListener(this) // Set the listener here + } + + // checkBtn누르면 firestore에 친구 추가하기. + override fun onAddFriendClick(position: Int) { + val clickedItem = originalList[position] + Log.d("db", "Button clicked for ${clickedItem.name}") // add : 오류 확인을 위함 + var name = clickedItem.name + var email = clickedItem.email + val reqFriendList = FriendList(email, name) // + //val resFriendList = FriendList(auth.currentUser?.email, auth.currentUser?.name) // + addFriend(reqFriendList) + + Toast.makeText(requireContext(), "${clickedItem.name} : 친구 추가", Toast.LENGTH_SHORT).show() + } + + // 친구 추가 하위 컬렉션 추가 : addFriend(friendData) + private fun addFriend(data: FriendList){ + Log.d("db", "addFriend") + val db = FirebaseFirestore.getInstance() + db.collection("memberDB") + .document(auth.currentUser?.email.toString()) + .collection("FriendList") + .document(data.email) // .add(data)해도 상관없음 + .set(data) + .addOnSuccessListener { + Log.d("db", "success : addFriend") + }.addOnFailureListener{ + Log.d("db", "fail : addFriend") + } + } + // + +companion object { + /** + * Use this factory method to create a new instance of + * this fragment using the provided parameters. + * + * @param param1 Parameter 1. + * @param param2 Parameter 2. + * @return A new instance of fragment fragment_add_friend. + */ + // TODO: Rename and change types and number of parameters + @JvmStatic + fun newInstance(param1: String, param2: String) = + AddFriendFragment().apply { + arguments = Bundle().apply { + putString(ARG_PARAM1, param1) + putString(ARG_PARAM2, param2) + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/Apic/apic/AddGroupFragment.kt b/app/src/main/java/com/Apic/apic/AddGroupFragment.kt index d435388..531ce1d 100644 --- a/app/src/main/java/com/Apic/apic/AddGroupFragment.kt +++ b/app/src/main/java/com/Apic/apic/AddGroupFragment.kt @@ -1,16 +1,17 @@ package com.Apic.apic -import android.content.Intent import android.os.Bundle -import android.text.Editable -import android.text.TextWatcher +import android.util.Log import androidx.fragment.app.Fragment import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import android.widget.Toast +import androidx.recyclerview.widget.LinearLayoutManager +import androidx.recyclerview.widget.RecyclerView import com.Apic.apic.databinding.FragmentAddGroupBinding -import com.Apic.apic.databinding.FragmentGroupListBinding +import com.google.firebase.auth.FirebaseAuth +import com.google.firebase.firestore.FirebaseFirestore // TODO: Rename parameter arguments, choose names that match // the fragment initialization parameters, e.g. ARG_ITEM_NUMBER @@ -28,7 +29,15 @@ class AddGroupFragment : Fragment() { private var param2: String? = null private lateinit var binding: FragmentAddGroupBinding + private val db = FirebaseFirestore.getInstance() + private lateinit var auth: FirebaseAuth + private lateinit var groupParticipantsAdapter: GroupParticipantsAdapter + + private val friendList = mutableListOf() + + private val participantsList = mutableListOf() + private val participantsNameList = ArrayList() override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) @@ -38,6 +47,21 @@ class AddGroupFragment : Fragment() { } } + // 친구 리스트에서 참여자 선택 + private fun setOnClickEvent() { + groupParticipantsAdapter.setItemClickListener(object: GroupParticipantsAdapter.OnItemClickListener { + override fun onClick(view: View, position: Int) { + super.onClick(view, position) + Toast.makeText(view.context, "테스트 - ${friendList[position].f_name} 클릭", Toast.LENGTH_SHORT).show() + participantsList.add(friendList[position]) + participantsNameList.add(friendList[position].f_name) + binding.groupParticipants.text = participantsNameList.toString() + binding.groupMemberNum.text = participantsNameList.size.toString() + } + }) + } + + override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? @@ -45,22 +69,42 @@ class AddGroupFragment : Fragment() { // Inflate the layout for this fragment binding = FragmentAddGroupBinding.inflate(inflater, container, false) + auth = FirebaseAuth.getInstance() + + val recyclerView: RecyclerView = binding.groupParticipantsRecyclerView + recyclerView.layoutManager = LinearLayoutManager(context,LinearLayoutManager.HORIZONTAL,false) + groupParticipantsAdapter = GroupParticipantsAdapter(friendList) + recyclerView.adapter = groupParticipantsAdapter + + getFriendData() // 친구 목록 불러오기 + + setOnClickEvent() // 참여자 선택 // 체크 버튼 binding.checkBtn.setOnClickListener { - Toast.makeText(context, "모임 ${binding.EtGoupName.text}(${binding.groupMemberNum.text}명)가 생성되었습니다.", Toast.LENGTH_SHORT).show() - // AddMeetingFragment 테스트용 - val transaction = fragmentManager?.beginTransaction() - if (transaction != null) { - transaction.replace(R.id.menu_frame_view, AddMeetingFragment()).commitAllowingStateLoss() - } + // 그룹 추가 + val gName = binding.EtGoupName.text.toString() + val gParticipants = participantsNameList.size.toString() + + val groupData = GroupData(gName, gParticipants) + addGroup(groupData) + + // 참여자 추가 + for (participant in participantsList) { + val fName = participant.f_name + val fEmail = participant.f_email + val participantsData = FriendData(fName, fEmail) + addParticipants(groupData, participantsData) + } + + Toast.makeText(context, "그룹 생성 완료", Toast.LENGTH_SHORT).show() // 그룹 리스트로 돌아가기 -// val transaction = fragmentManager?.beginTransaction() -// if (transaction != null) { -// transaction.replace(R.id.menu_frame_view, GroupListFragment()).commitAllowingStateLoss() -// } + val transaction = fragmentManager?.beginTransaction() + if (transaction != null) { + transaction.replace(R.id.menu_frame_view, GroupListFragment()).commitAllowingStateLoss() + } } // 엑스 버튼 @@ -75,8 +119,58 @@ class AddGroupFragment : Fragment() { return binding.root } - private fun setOnClickListener() { - val btnCheck = binding.checkBtn + private fun getFriendData() { + db.collection("memberDB") + .document(auth.currentUser!!.email.toString()) + .collection("FriendList") + .get() + .addOnSuccessListener { result -> + friendList.clear() + for (document in result) { + val friend = FriendData ( + document["email"] as String, + document["name"] as String + ) + friendList.add(friend) + } + groupParticipantsAdapter.notifyDataSetChanged() + Log.d("db", "success") + } + .addOnFailureListener { + Log.d("db", "fail") + } + } + + private fun addGroup(groupData: GroupData) { + + db.collection("memberDB") + .document(auth.currentUser!!.email.toString()) + .collection("groups") + .document(groupData.g_name) + .set(groupData) + .addOnCompleteListener { + Log.d("db", "success") + } + .addOnFailureListener { + Log.d("db", "fail") + } + } + + private fun addParticipants(groupData: GroupData, participantsData: FriendData) { + + db.collection("memberDB") + .document(auth.currentUser!!.email.toString()) + .collection("groups") + .document(groupData.g_name) + .collection("participants") + .document(participantsData.f_email) + .set(participantsData) + .addOnCompleteListener { + Log.d("db", "success") + } + .addOnFailureListener { + Log.d("db", "fail") + } } companion object { diff --git a/app/src/main/java/com/Apic/apic/AddMeetingFragment.kt b/app/src/main/java/com/Apic/apic/AddMeetingFragment.kt index 962de3d..e9efe91 100644 --- a/app/src/main/java/com/Apic/apic/AddMeetingFragment.kt +++ b/app/src/main/java/com/Apic/apic/AddMeetingFragment.kt @@ -7,7 +7,6 @@ import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import android.widget.Toast -import com.Apic.apic.databinding.FragmentAddGroupBinding import com.Apic.apic.databinding.FragmentAddMeetingBinding // TODO: Rename parameter arguments, choose names that match diff --git a/app/src/main/java/com/Apic/apic/AlbumAdapter.kt b/app/src/main/java/com/Apic/apic/AlbumAdapter.kt new file mode 100644 index 0000000..fb6b15f --- /dev/null +++ b/app/src/main/java/com/Apic/apic/AlbumAdapter.kt @@ -0,0 +1,45 @@ +package com.Apic.apic + +import android.content.Context +import android.net.Uri +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.ImageView +import androidx.recyclerview.widget.RecyclerView +import com.bumptech.glide.Glide + +class AlbumAdapter() : RecyclerView.Adapter() { + lateinit var imageList: ArrayList + lateinit var context: Context + + constructor(imageList: ArrayList, context: Context): this(){ + this.imageList = imageList + this.context = context + } + + class ViewHolder(view: View) : RecyclerView.ViewHolder(view) { + val albumView: ImageView = view.findViewById(R.id.albumView) + } + + // 화면 설정 + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { + val inflater: LayoutInflater = LayoutInflater.from(parent.context) + val view: View = inflater.inflate(R.layout.item_picture, parent, false) + return ViewHolder(view) + } + + // 데이터 설정 + override fun onBindViewHolder(holder: ViewHolder, position: Int) { + Glide.with(context) + .load(imageList[position]) // 사진 위치 + .into(holder.albumView) // 보여줄 위치 + } + + // 아이템 갯수 + override fun getItemCount(): Int { + return imageList.size + } + + +} diff --git a/app/src/main/java/com/Apic/apic/AlbumFragment.kt b/app/src/main/java/com/Apic/apic/AlbumFragment.kt new file mode 100644 index 0000000..2c3de26 --- /dev/null +++ b/app/src/main/java/com/Apic/apic/AlbumFragment.kt @@ -0,0 +1,89 @@ +package com.Apic.apic + +import android.content.Intent +import android.net.Uri +import android.os.Bundle +import android.util.Log +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.fragment.app.Fragment +import androidx.recyclerview.widget.GridLayoutManager +import androidx.recyclerview.widget.RecyclerView +import com.Apic.apic.databinding.FragmentAlbumBinding + +class AlbumFragment : Fragment() { + + private lateinit var binding: FragmentAlbumBinding + private lateinit var albumRecyclerView: RecyclerView + + private var albumAdapter: AlbumAdapter? = null + private var imageList: ArrayList? = null + + override fun onCreateView( + inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle? + ): View? { + // ViewBinding을 사용하여 프래그먼트의 레이아웃 설정 + binding = FragmentAlbumBinding.inflate(inflater, container, false) + val rootView = binding.root + + // RecyclerView 초기화 및 그리드 레이아웃 매니저 설정 + albumRecyclerView = binding.albumRecyclerView + albumRecyclerView.layoutManager = GridLayoutManager(requireContext(), 3) + + // 이미지 리스트가 null이면 새로 생성, 이미 있다면 기존 리스트 사용 + imageList = imageList ?: ArrayList() + + // AlbumAdapter 초기화 및 RecyclerView에 연결 + albumAdapter = AlbumAdapter(imageList!!, requireContext()) + albumRecyclerView.adapter = albumAdapter + + Log.d("AlbumFragment", "onCreateView called") + + return rootView + } + + // MainActivity에서 호출하여 이미지 리스트 업데이트 + fun updateImageList(data: Intent?) { + Log.d("AlbumFragment", "updateImageList called") + + // 이미지 리스트가 null인 경우 새로 생성 + if (imageList == null) { + imageList = ArrayList() + } + + if (data?.clipData != null) { // 다중 이미지 선택 시 + // 선택한 이미지의 갯수 + Log.d("AlbumFragment", "ClipData exists with itemCount: ${data.clipData!!.itemCount}") + val count = data.clipData!!.itemCount + + // 각 이미지를 리스트에 추가 + for (index in 0 until count) { + val imageUri = data.clipData!!.getItemAt(index).uri + imageList?.add(imageUri) + Log.d("AlbumFragment", "Added image to imageList: $imageUri") + } + } else { // 단일 이미지 선택 시 + Log.d("AlbumFragment", "ClipData is null") + val imageUri = data?.data + imageUri?.let { + imageList?.add(it) + } + } + + // 이미지 리스트 업데이트 + updateAdapter() + } + + // RecyclerView 어댑터 업데이트 및 갱신 + private fun updateAdapter() { + imageList?.let { + Log.d("AlbumFragment", "imageList size: ${it.size}") + + // AlbumAdapter가 초기화되었는지 확인 후 notifyDataSetChanged 호출 + albumAdapter?.notifyDataSetChanged() + Log.d("AlbumFragment", "notifyDataSetChanged") + } + } +} diff --git a/app/src/main/java/com/Apic/apic/CalendarFragment.kt b/app/src/main/java/com/Apic/apic/CalendarFragment.kt index f4536ec..323f920 100644 --- a/app/src/main/java/com/Apic/apic/CalendarFragment.kt +++ b/app/src/main/java/com/Apic/apic/CalendarFragment.kt @@ -1,59 +1,90 @@ package com.Apic.apic + import android.os.Bundle -import androidx.fragment.app.Fragment import android.view.LayoutInflater import android.view.View import android.view.ViewGroup +import android.widget.Button +import android.widget.CalendarView +import android.widget.EditText +import androidx.fragment.app.Fragment +import androidx.lifecycle.ViewModelProvider +import androidx.lifecycle.lifecycleScope +import androidx.recyclerview.widget.LinearLayoutManager +import androidx.recyclerview.widget.RecyclerView +import com.Apic.apic.data.Todo -// TODO: Rename parameter arguments, choose names that match -// the fragment initialization parameters, e.g. ARG_ITEM_NUMBER -private const val ARG_PARAM1 = "param1" -private const val ARG_PARAM2 = "param2" +import kotlinx.coroutines.launch +import java.util.Calendar -/** - * A simple [Fragment] subclass. - * Use the [CalendarFragment.newInstance] factory method to - * create an instance of this fragment. - */ class CalendarFragment : Fragment() { - // TODO: Rename and change types of parameters - private var param1: String? = null - private var param2: String? = null - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - arguments?.let { - param1 = it.getString(ARG_PARAM1) - param2 = it.getString(ARG_PARAM2) - } - } + + private lateinit var recyclerView: RecyclerView + private lateinit var todoEditText: EditText + private lateinit var calendarView: CalendarView + private lateinit var addButton: Button + + private lateinit var viewModel: MainViewModel + private var selectedDate: Long = 0 override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View? { - // Inflate the layout for this fragment - return inflater.inflate(R.layout.fragment_calendar, container, false) - } + val view = inflater.inflate(R.layout.fragment_calendar, container, false) + + // 뷰 초기화 + recyclerView = view.findViewById(R.id.recyclerView) + todoEditText = view.findViewById(R.id.todoEditText) + calendarView = view.findViewById(R.id.calendar) + addButton = view.findViewById(R.id.btnAdd) + + // MainViewModel 초기화 + viewModel = ViewModelProvider(requireActivity())[MainViewModel::class.java] + + // RecyclerView 초기화 + val layoutManager = LinearLayoutManager(requireContext()) + layoutManager.reverseLayout = true + layoutManager.stackFromEnd = true + recyclerView.layoutManager = layoutManager - companion object { - /** - * Use this factory method to create a new instance of - * this fragment using the provided parameters. - * - * @param param1 Parameter 1. - * @param param2 Parameter 2. - * @return A new instance of fragment CalendarFragment. - */ - // TODO: Rename and change types and number of parameters - @JvmStatic - fun newInstance(param1: String, param2: String) = - CalendarFragment().apply { - arguments = Bundle().apply { - putString(ARG_PARAM1, param1) - putString(ARG_PARAM2, param2) - } + // Adapter 설정 부분은 그대로 유지 + val adapter = TodoListAdapter { todo -> viewModel.deleteTodo(todo.id) } + recyclerView.adapter = adapter + + // CalendarView 이벤트 핸들링 + calendarView.setOnDateChangeListener { _, year, month, dayOfMonth -> + val calendar = Calendar.getInstance() + calendar.set(year, month, dayOfMonth) + selectedDate = calendar.timeInMillis + } + + // 초기에 오늘 날짜로 선택되도록 설정 + val today = Calendar.getInstance() + selectedDate = today.timeInMillis + calendarView.date = selectedDate + + // "추가" 버튼 클릭 이벤트 처리 + addButton.setOnClickListener { + val todoText = todoEditText.text.toString() + if (selectedDate != 0L && todoText.isNotBlank()) { + // Todo 객체 생성 및 저장 + val newTodo = Todo(todoText, selectedDate) + viewModel.addTodo(newTodo) + + // EditText 초기화 + todoEditText.text.clear() } + } + + // ViewModel에서 데이터 관찰 + viewLifecycleOwner.lifecycleScope.launch { + viewModel.items.collect { todos -> + adapter.submitList(todos) + } + } + + return view } } \ No newline at end of file diff --git a/app/src/main/java/com/Apic/apic/DateFragment.kt b/app/src/main/java/com/Apic/apic/DateFragment.kt index 1e69712..fd8e429 100644 --- a/app/src/main/java/com/Apic/apic/DateFragment.kt +++ b/app/src/main/java/com/Apic/apic/DateFragment.kt @@ -1,6 +1,5 @@ package com.Apic.apic -import android.content.Intent import android.os.Bundle import androidx.fragment.app.Fragment import android.view.LayoutInflater diff --git a/app/src/main/java/com/Apic/apic/DialogAddFriendAdapter.kt b/app/src/main/java/com/Apic/apic/DialogAddFriendAdapter.kt new file mode 100644 index 0000000..f73f224 --- /dev/null +++ b/app/src/main/java/com/Apic/apic/DialogAddFriendAdapter.kt @@ -0,0 +1,61 @@ +package com.Apic.apic + +import android.util.Log +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.TextView +import androidx.appcompat.widget.AppCompatImageButton +import androidx.recyclerview.widget.RecyclerView + +class DialogAddFriendAdapter(var itemList: ArrayList) : RecyclerView.Adapter() { + + // recyclerview의 chekButton을 위한 함수 + interface OnAddFriendClickListener { + fun onAddFriendClick(position: Int) + } + private var listener: OnAddFriendClickListener? = null + fun setOnAddFriendClickListener(listener: OnAddFriendClickListener) { + this.listener = listener + } + // + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): DialogAddFriendAdapter.ViewHolder { + val view = LayoutInflater.from(parent.context).inflate(R.layout.friend_add_recyclerview, parent, false) + return ViewHolder(view) + } + override fun getItemCount(): Int { + return itemList.size + } + override fun onBindViewHolder(holder: DialogAddFriendAdapter.ViewHolder, position: Int) { + Log.d("db", "Adapter1") + holder.name.text = itemList[position].name // adapter에 있는 이름에 MemberFriendData의 이름 데이터를 넣는다. + holder.emailId.text = itemList[position].email + } + + fun setFriendList(list: ArrayList) { + itemList = list + notifyDataSetChanged() + //Log.d("db", "notifyDataSetChanged called") + } + + // inner 추가 + inner class ViewHolder(itemView: View): RecyclerView.ViewHolder(itemView){ + val name: TextView = itemView.findViewById(R.id.name) + val emailId: TextView = itemView.findViewById(R.id.emailId) + + + // add : recyclerview의 checkBtn눌렀을 때 반응 + val checkButton: AppCompatImageButton = itemView.findViewById(R.id.checkButton) + init { + checkButton?.setOnClickListener { + // 반응 + listener?.onAddFriendClick(adapterPosition) + } + } + + } + + + +} \ No newline at end of file diff --git a/app/src/main/java/com/Apic/apic/DialogFriendAdapter.kt b/app/src/main/java/com/Apic/apic/DialogFriendAdapter.kt new file mode 100644 index 0000000..010d0c8 --- /dev/null +++ b/app/src/main/java/com/Apic/apic/DialogFriendAdapter.kt @@ -0,0 +1,48 @@ +package com.Apic.apic + +import android.util.Log +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.TextView +import androidx.annotation.NonNull +import androidx.recyclerview.widget.RecyclerView + +class DialogFriendAdapter(var itemList: ArrayList) : RecyclerView.Adapter() { + + private var mFriendList: ArrayList = ArrayList() + + @NonNull + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { + val view = LayoutInflater.from(parent.context).inflate(R.layout.friend_recyclerview, parent, false) + return ViewHolder(view) + } + + override fun getItemCount(): Int { + return itemList.size + } + + // Item을 하나, 하나 보여주는(bind 되는) 함수입니다. + override fun onBindViewHolder(holder: DialogFriendAdapter.ViewHolder, position: Int) { + Log.d("db", "Adapter1") + holder.name.text = itemList[position].name // adapter에 있는 이름에 MemberFriendData의 이름 데이터를 넣는다. + holder.emailId.text = itemList[position].email + } + + + fun setFriendList(list: ArrayList) { + mFriendList = list + itemList = mFriendList + for (m in mFriendList) { // 확인용 코드 + val mname = m.name + Log.d("db", mname) + } + notifyDataSetChanged() + //Log.d("db", "notifyDataSetChanged called") + } + + inner class ViewHolder(itemView: View): RecyclerView.ViewHolder(itemView) { + val name: TextView = itemView.findViewById(R.id.name) + val emailId: TextView = itemView.findViewById(R.id.emailId) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/Apic/apic/FirebaseData.kt b/app/src/main/java/com/Apic/apic/FirebaseData.kt index 6ba69de..f8dce64 100644 --- a/app/src/main/java/com/Apic/apic/FirebaseData.kt +++ b/app/src/main/java/com/Apic/apic/FirebaseData.kt @@ -2,8 +2,7 @@ package com.Apic.apic data class FirebaseData( val Num : Int, - val name : String, + val Name : String, val Email : String, val Password : String -) - +) \ No newline at end of file diff --git a/app/src/main/java/com/Apic/apic/FirstFragment.kt b/app/src/main/java/com/Apic/apic/FirstFragment.kt deleted file mode 100644 index 79afec4..0000000 --- a/app/src/main/java/com/Apic/apic/FirstFragment.kt +++ /dev/null @@ -1,59 +0,0 @@ -package com.Apic.apic - -import android.os.Bundle -import androidx.fragment.app.Fragment -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup - -// TODO: Rename parameter arguments, choose names that match -// the fragment initialization parameters, e.g. ARG_ITEM_NUMBER -private const val ARG_PARAM1 = "param1" -private const val ARG_PARAM2 = "param2" - -/** - * A simple [Fragment] subclass. - * Use the [FirstFragment.newInstance] factory method to - * create an instance of this fragment. - */ -class FirstFragment : Fragment() { - // TODO: Rename and change types of parameters - private var param1: String? = null - private var param2: String? = null - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - arguments?.let { - param1 = it.getString(ARG_PARAM1) - param2 = it.getString(ARG_PARAM2) - } - } - - override fun onCreateView( - inflater: LayoutInflater, container: ViewGroup?, - savedInstanceState: Bundle? - ): View? { - // Inflate the layout for this fragment - return inflater.inflate(R.layout.fragment_first, container, false) - } - - companion object { - /** - * Use this factory method to create a new instance of - * this fragment using the provided parameters. - * - * @param param1 Parameter 1. - * @param param2 Parameter 2. - * @return A new instance of fragment BoardFragment. - */ - // TODO: Rename and change types and number of parameters - @JvmStatic - fun newInstance(param1: String, param2: String) = - FirstFragment().apply { - arguments = Bundle().apply { - putString(ARG_PARAM1, param1) - putString(ARG_PARAM2, param2) - } - } - } -} \ No newline at end of file diff --git a/app/src/main/java/com/Apic/apic/FourcutFragment.kt b/app/src/main/java/com/Apic/apic/FourcutFragment.kt new file mode 100644 index 0000000..ed5f678 --- /dev/null +++ b/app/src/main/java/com/Apic/apic/FourcutFragment.kt @@ -0,0 +1,415 @@ +package com.Apic.apic + +import android.content.Context +import android.content.Intent +import android.graphics.Bitmap +import android.graphics.BitmapFactory +import android.graphics.Canvas +import android.graphics.drawable.BitmapDrawable +import android.net.Uri +import android.os.Bundle +import android.os.Environment +import android.provider.MediaStore +import android.util.Log +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.activity.result.contract.ActivityResultContracts +import androidx.fragment.app.Fragment +import androidx.lifecycle.MutableLiveData +import com.Apic.apic.databinding.FragmentFourcutBinding +import org.opencv.android.Utils +import org.opencv.core.Core +import org.opencv.core.Mat +import org.opencv.core.Size +import org.opencv.imgproc.Imgproc +import org.opencv.photo.Photo.pencilSketch +import java.io.File +import java.io.FileOutputStream +import java.io.IOException +import java.io.InputStream +import java.text.SimpleDateFormat +import java.util.* + +/* +import android.provider.MediaStore +import androidx.activity.result.contract.ActivityResultContracts +import com.example.my12application.databinding.ActivityAddBinding + */ + + +// TODO: Rename parameter arguments, choose names that match +// the fragment initialization parameters, e.g. ARG_ITEM_NUMBER +private const val ARG_PARAM1 = "param1" +private const val ARG_PARAM2 = "param2" + +/** + * A simple [Fragment] subclass. + * Use the [FourcutFragment.newInstance] factory method to + * create an instance of this fragment. + */ + +class FourcutFragment : Fragment() { + // TODO: Rename and change types of parameters + private var param1: String? = null + private var param2: String? = null + + //뷰에 나타낼 값들. 라이브데이터 형식 (사진 저장) + var image = MutableLiveData() + var background = MutableLiveData() + val content = MutableLiveData() + + lateinit var binding : FourcutFragment + private lateinit var targetView: View + + lateinit var originImg: Bitmap + init { + System.loadLibrary("opencv_java4") + } + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + arguments?.let { + param1 = it.getString(ARG_PARAM1) + param2 = it.getString(ARG_PARAM2) + } + } + + override fun onCreateView( + inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle? + ): View? { + var binding = FragmentFourcutBinding.inflate(inflater, container, false) + + // 프레임 색 변경 + // 주황 버튼 + binding.btnChangeFrameOrange.setOnClickListener { + binding.fourcutFrame.setImageResource(R.drawable.fourcut_orange_img) + } + + // 초록 버튼 + binding.btnChangeFrameGreen.setOnClickListener { + binding.fourcutFrame.setImageResource(R.drawable.fourcut_green_img) + } + + // 파랑 버튼 + binding.btnChangeFrameBlue.setOnClickListener { + binding.fourcutFrame.setImageResource(R.drawable.fourcut_blue_img) + } + + // 사진 불러오기 + val requestGalleryLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()){ + try{ + val calRatio = calculateInSampleSize(it.data!!.data!!, resources.getDimensionPixelSize(R.dimen.imgSize), resources.getDimensionPixelSize(R.dimen.imgSize) ) + val option = BitmapFactory.Options() + option.inSampleSize = calRatio + var inputStream = context?.contentResolver?.openInputStream(it.data!!.data!!) + val bitmap = BitmapFactory.decodeStream(inputStream, null, option) + inputStream!!.close() // .close()로 파일을 종료(해주는 습관 필요) + inputStream = null + // 여기까지가 읽어오는 작업 + + bitmap?.let { + binding.fourcutFrame.setImageBitmap(bitmap) + } ?: let{ Log.d("mobileApp", "bitmap NULL")} + } catch (e:Exception) { e.printStackTrace() } + } + + binding.btn4cutphoto.setOnClickListener { + // 사진 가져오기 + val intent = Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI) // intent 변수에 Intent() 호출해서 사진 하나를 가져오기 위해서 ACTION_PICK 사용, MediaStore를 통해서 uri를 가져오기 + // val intent = Intent(Intent.ACTION_PICK) + intent.type = "image/*" // 가져오기 전에 가져올 타입을 image만 가져오도록 타입을 지정 + requestGalleryLauncher.launch(intent) + } + + // 버튼 1) 그레이 사진 효과 적용 + binding.btnChangeEffect1.setOnClickListener { + +// val assetManager = resources.assets +// val inputStream: InputStream = assetManager.open("iv_fourcut_test_img.jpg") +// val bitmap: Bitmap = BitmapFactory.decodeStream(inputStream) + + // 불러온 원본 이미지 delete 기능을 위해 저장 (첫 프레임으로 돌아감 -> 추후 수정 예정) + if (binding.fourcutFrame.drawable is BitmapDrawable) { + originImg = (binding.fourcutFrame.drawable as BitmapDrawable).bitmap + } else { + val drawable = binding.fourcutFrame.drawable + originImg = Bitmap.createBitmap(drawable.intrinsicWidth, drawable.intrinsicHeight, Bitmap.Config.ARGB_8888) + } + + lateinit var bitmap : Bitmap + + if (binding.fourcutFrame.drawable is BitmapDrawable) { + bitmap = (binding.fourcutFrame.drawable as BitmapDrawable).bitmap + } else { + val drawable = binding.fourcutFrame.drawable + bitmap = Bitmap.createBitmap(drawable.intrinsicWidth, drawable.intrinsicHeight, Bitmap.Config.ARGB_8888) + } + + binding.fourcutFrame.setImageBitmap(bitmap) + + val gray = Mat() + Utils.bitmapToMat(bitmap, gray) + + Imgproc.cvtColor(gray, gray, Imgproc.COLOR_RGBA2GRAY) + + val grayBitmap = Bitmap.createBitmap(gray.cols(), gray.rows(), Bitmap.Config.ARGB_8888) + Utils.matToBitmap(gray, grayBitmap) + + binding.fourcutFrame.setImageBitmap(grayBitmap) + Log.d("4cut", "그레이 이미지 설정 완료") + } + + binding.btnChangeEffect2.setOnClickListener { + // 불러온 원본 이미지 delete 기능을 위해 저장 (첫 프레임으로 돌아감 -> 추후 수정 예정) + if (binding.fourcutFrame.drawable is BitmapDrawable) { + originImg = (binding.fourcutFrame.drawable as BitmapDrawable).bitmap + } else { + val drawable = binding.fourcutFrame.drawable + originImg = Bitmap.createBitmap(drawable.intrinsicWidth, drawable.intrinsicHeight, Bitmap.Config.ARGB_8888) + } + + val assetManager = resources.assets + val inputStream: InputStream = assetManager.open("iv_fourcut_test_img.jpg") + // decode 비트맵 변수 + val bitmap: Bitmap = BitmapFactory.decodeStream(inputStream) + + binding.fourcutFrame.setImageBitmap(bitmap) + + // 비트맵 to mat할 Mat 변수 + val gray = Mat() + Utils.bitmapToMat(bitmap, gray) + Log.d("4cut", "1) bitmap to mat 성공") + + Imgproc.cvtColor(gray, gray, Imgproc.COLOR_BGRA2BGR) + Log.d("4cut", "Imgproc.COLOR_BGRA2BGR 성공") + + var cartoon = cartoon(gray) + Log.d("4cut", "카툰 효과 처리 성공") + + val grayBitmap = Bitmap.createBitmap(gray.cols(), gray.rows(), Bitmap.Config.ARGB_8888) + Utils.matToBitmap(cartoon, grayBitmap) + // Utils.matToBitmap(cartoon, bitmap) + + binding.fourcutFrame.setImageBitmap(grayBitmap) + Log.d("4cut", "카툰 효과 이미지 설정 완료") + } + + // 3번째 버튼 : 스케치 + binding.btnChangeEffect3.setOnClickListener { + // 불러온 원본 이미지 delete 기능을 위해 저장 (첫 프레임으로 돌아감 -> 추후 수정 예정) + if (binding.fourcutFrame.drawable is BitmapDrawable) { + originImg = (binding.fourcutFrame.drawable as BitmapDrawable).bitmap + } else { + val drawable = binding.fourcutFrame.drawable + originImg = Bitmap.createBitmap(drawable.intrinsicWidth, drawable.intrinsicHeight, Bitmap.Config.ARGB_8888) + } + + val assetManager = resources.assets + val inputStream: InputStream = assetManager.open("iv_fourcut_test_img.jpg") + // decode 비트맵 변수 + val bitmap: Bitmap = BitmapFactory.decodeStream(inputStream) + binding.fourcutFrame.setImageBitmap(bitmap) + + + binding.fourcutFrame.setImageBitmap(bitmap) + + // 비트맵 to mat할 Mat 변수 + val gray = Mat() + + Utils.bitmapToMat(bitmap, gray) + Log.d("4cut", "1) bitmap to mat 성공") + + Imgproc.cvtColor(gray, gray, Imgproc.COLOR_BGRA2BGR) + Log.d("4cut", "Imgproc.COLOR_BGRA2BGR 성공") + + var sketch = Mat() + sketch = sketch(gray) + Log.d("4cut", "그레이 스케치 효과 처리 성공") + + val grayBitmap = Bitmap.createBitmap(gray.cols(), gray.rows(), Bitmap.Config.ARGB_8888) + Utils.matToBitmap(sketch, grayBitmap) + + binding.fourcutFrame.setImageBitmap(grayBitmap) + Log.d("4cut", "그레이 스케치 효과 이미지 설정 완료") + } + + binding.btnChangeEffect4.setOnClickListener { + // 불러온 원본 이미지 delete 기능을 위해 저장 (첫 프레임으로 돌아감 -> 추후 수정 예정) + if (binding.fourcutFrame.drawable is BitmapDrawable) { + originImg = (binding.fourcutFrame.drawable as BitmapDrawable).bitmap + } else { + val drawable = binding.fourcutFrame.drawable + originImg = Bitmap.createBitmap(drawable.intrinsicWidth, drawable.intrinsicHeight, Bitmap.Config.ARGB_8888) + } + + val assetManager = resources.assets + val inputStream: InputStream = assetManager.open("iv_fourcut_test_img.jpg") + // decode 비트맵 변수 + val bitmap: Bitmap = BitmapFactory.decodeStream(inputStream) + binding.fourcutFrame.setImageBitmap(bitmap) + + binding.fourcutFrame.setImageBitmap(bitmap) + + // 비트맵 to mat할 Mat 변수 + val gray = Mat() + + Utils.bitmapToMat(bitmap, gray) + Log.d("4cut", "1) bitmap to mat 성공") + + Imgproc.cvtColor(gray, gray, Imgproc.COLOR_BGRA2BGR) + Log.d("4cut", "Imgproc.COLOR_BGRA2BGR 성공") + + var sketch = Mat() + pencilSketch(gray, sketch, sketch, 60F, 0.07F, 0.02F) + Log.d("4cut", "펜슬 스케치 효과 처리 성공") + + Utils.matToBitmap(sketch, bitmap) + + binding.fourcutFrame.setImageBitmap(bitmap) + Log.d("4cut", "펜슬 스케치 효과 이미지 설정 완료") + } + + // save 버튼 누를 시 + binding.btn4cutSave.setOnClickListener { + // 사진 저장 + targetView = binding.fourcutFrame + + Log.d("4cut", "targetView 가져오기") + viewSave(targetView) + Log.d("4cut", "viewSave() 함수 통과") + } + + // Delete 버튼 누를 시 + binding.btn4cutDelete.setOnClickListener { + // 초기화 + binding.fourcutFrame.setImageBitmap(originImg) + } + return binding.root + } + +// private fun getFilePath() : String { +// +// } + + private fun getViewBitmap(view: View): Bitmap { + val bitmap = Bitmap.createBitmap( + view.measuredWidth, view.measuredHeight, Bitmap.Config.ARGB_8888 + ) + val canvas = Canvas(bitmap) + view.draw(canvas) + return bitmap + } + + private fun getSaveFilePathName(): String { + val folder = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).toString() + val fileName = SimpleDateFormat("yyyyMMddHHmmss").format(Date()) + return "$folder/$fileName.jpg" + } + + private fun bitmapFileSave(bitmap: Bitmap, path: String) { + val fos: FileOutputStream + try{ + fos = FileOutputStream(File(path)) + bitmap.compress(Bitmap.CompressFormat.JPEG,100,fos) + fos.close() + }catch (e: IOException) { + e.printStackTrace() + } + } + + private fun viewSave(view: View) { + val bitmap = getViewBitmap(view) + val filePath = getSaveFilePathName() + bitmapFileSave(bitmap, filePath) + } + + + fun sketch(src: Mat): Mat { + //회색조 이미지 만들기 + val gray = Mat() + Imgproc.cvtColor(src, gray, Imgproc.COLOR_BGR2GRAY) + // 회색조 이미지를 블러시키기 + val blur = Mat() + Imgproc.GaussianBlur(gray, blur, Size(0.0, 0.0), 12.0) + // 엣지만 남고 평탄한 부분은 흰색으로 바뀜 + val dst = Mat() + Core.divide(gray, blur, dst, 255.0) + return dst + } + + fun cartoon(src: Mat): Mat { + val width = src.width() + val height = src.height() + // 이미지의 크기를 줄이면 효과적으로 뭉개고, 연산량을 빨리 하는 효과가 있음. + val resizedSrc = Mat() + Imgproc.resize(src, resizedSrc, Size(width / 8.0, height / 8.0)) + // 블러 적용 + val blur = Mat() + Imgproc.bilateralFilter(resizedSrc, blur, -1, 20.0, 7.0) + // 엣지 검출한 뒤, 이미지를 반전시킨다. + val edge = Mat() + Imgproc.Canny(resizedSrc, edge, 100.0, 150.0) + Core.bitwise_not(edge, edge) + Imgproc.cvtColor(edge, edge, Imgproc.COLOR_GRAY2BGR) + //블러시킨 이미지와 반전된 edge를 and연산자로 합치면 edge부분은 검정색으로 나오고, 나머지는 많이 뭉개지고 블러처리된 이미지로 나옴, 카툰효과 + val dst = Mat() + Core.bitwise_and(blur, edge, dst) + Imgproc.resize(dst, dst, Size(width.toDouble(), height.toDouble()), 1.0, 1.0, Imgproc.INTER_NEAREST) + return dst + } + + + // 갤러리에서 사진을 불러오는 것 (+ 불러온 이미지 크기 조정) + private fun calculateInSampleSize(fileUri: Uri, reqWidth: Int, reqHeight: Int): Int { + val options = BitmapFactory.Options() + options.inJustDecodeBounds = true + try { + var inputStream = context?.contentResolver?.openInputStream(fileUri) + + //inJustDecodeBounds 값을 true 로 설정한 상태에서 decodeXXX() 를 호출. + //로딩 하고자 하는 이미지의 각종 정보가 options 에 설정 된다. + BitmapFactory.decodeStream(inputStream, null, options) + inputStream!!.close() + inputStream = null + } catch (e: Exception) { + e.printStackTrace() + } + //비율 계산........................ + val (height: Int, width: Int) = options.run { outHeight to outWidth } + var inSampleSize = 1 + //inSampleSize 비율 계산 + if (height > reqHeight || width > reqWidth) { + + val halfHeight: Int = height / 2 + val halfWidth: Int = width / 2 + + while (halfHeight / inSampleSize >= reqHeight && halfWidth / inSampleSize >= reqWidth) { + inSampleSize *= 2 + } + } + return inSampleSize + } + + companion object { + /** + * Use this factory method to create a new instance of + * this fragment using the provided parameters. + * + * @param param1 Parameter 1. + * @param param2 Parameter 2. + * @return A new instance of fragment FourcutFragment. + */ + // TODO: Rename and change types and number of parameters + @JvmStatic + fun newInstance(param1: String, param2: String) = + FourcutFragment().apply { + arguments = Bundle().apply { + putString(ARG_PARAM1, param1) + putString(ARG_PARAM2, param2) + } + } + } +} + diff --git a/app/src/main/java/com/Apic/apic/FragmentPagerAdapter.kt b/app/src/main/java/com/Apic/apic/FragmentPagerAdapter.kt index ec58665..2b684a5 100644 --- a/app/src/main/java/com/Apic/apic/FragmentPagerAdapter.kt +++ b/app/src/main/java/com/Apic/apic/FragmentPagerAdapter.kt @@ -16,8 +16,11 @@ class FragmentPagerAdapter ( override fun createFragment(position: Int): Fragment { return if (position == 0) - DateFragment() + AlbumFragment() else MemberFragment() -}} \ No newline at end of file +} + + +} \ No newline at end of file diff --git a/app/src/main/java/com/Apic/apic/FriendData.kt b/app/src/main/java/com/Apic/apic/FriendData.kt new file mode 100644 index 0000000..64b6fca --- /dev/null +++ b/app/src/main/java/com/Apic/apic/FriendData.kt @@ -0,0 +1,6 @@ +package com.Apic.apic + +data class FriendData( + val f_email: String, + val f_name: String +) diff --git a/app/src/main/java/com/Apic/apic/FriendFragment.kt b/app/src/main/java/com/Apic/apic/FriendFragment.kt index 4d0d9bb..4ca67ae 100644 --- a/app/src/main/java/com/Apic/apic/FriendFragment.kt +++ b/app/src/main/java/com/Apic/apic/FriendFragment.kt @@ -1,10 +1,23 @@ package com.Apic.apic +import android.content.Context import android.os.Bundle +import android.text.Editable +import android.text.TextWatcher +import android.util.Log import androidx.fragment.app.Fragment import android.view.LayoutInflater import android.view.View import android.view.ViewGroup +import android.view.inputmethod.InputMethodManager +import android.widget.EditText +import androidx.recyclerview.widget.LinearLayoutManager +import androidx.recyclerview.widget.RecyclerView +import com.Apic.apic.databinding.FragmentFriendBinding +import com.google.firebase.auth.FirebaseAuth +import com.google.firebase.firestore.FirebaseFirestore + + private const val ARG_PARAM1 = "param1" private const val ARG_PARAM2 = "param2" @@ -13,31 +26,133 @@ class FriendFragment : Fragment() { private var param1: String? = null private var param2: String? = null + private lateinit var mRecyclerView: RecyclerView + private lateinit var mRecyclerAdapter: DialogFriendAdapter + private lateinit var editText: EditText + private val originalList: ArrayList = ArrayList() + private val searchList: ArrayList = ArrayList() + + // firestore에서 data가져올때 사용 + private lateinit var auth: FirebaseAuth // 친구 리스트와 현재 로그인 user email 비교를 위해 가져옴. + private val db = FirebaseFirestore.getInstance() + private var adapter = DialogFriendAdapter(originalList) override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) + arguments?.let { param1 = it.getString(ARG_PARAM1) param2 = it.getString(ARG_PARAM2) } } - override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View? { - // Inflate the layout for this fragment - return inflater.inflate(R.layout.fragment_friend, container, false) + val binding = FragmentFriendBinding.inflate(inflater, container, false) + auth = FirebaseAuth.getInstance() + + mRecyclerView = binding.recyclerView + + // initiate adapter & recyclerview + mRecyclerAdapter = DialogFriendAdapter(originalList) + mRecyclerView.adapter = mRecyclerAdapter + mRecyclerView.layoutManager = LinearLayoutManager(requireContext()) + + // 친구 firestore 'memberDB'의 friendlist를 가져옴. + // 친구 찾기에서 친구 리스트로 recyclerview로 뜨기 + db.collection("memberDB") + .document(auth.currentUser?.email.toString()) + .collection("FriendList") // friendlist 항목 넣어야함. + .get() // Get documents + .addOnSuccessListener { result -> + // On success + originalList.clear() + for (document in result) { + val item = MemberData( + document["email"] as String, + document["name"] as String, + ) + // 현재 유저가 아닐때 + if (!auth.currentUser?.email.toString().equals(document["email"])) { + originalList.add(item) + } + } + + // 데이터 로드가 완료된 후에 어댑터 초기화와 업데이트 + adapter.notifyDataSetChanged() + } + .addOnFailureListener { exception -> + Log.w("AddFriendFragment", "Error getting documents: $exception") + } + + mRecyclerAdapter.setFriendList(originalList) + + // Button click listener : AddFriendFragment로 이동 + binding.addButton.setOnClickListener { + (activity as? MainActivity)?.setFragment(1) + } + + // Button click listener : search Friend email + binding.searchButton.setOnClickListener{ + val imm = requireActivity().getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager + imm.hideSoftInputFromWindow(binding.searchView.windowToken, 0) + } + + editText = binding.searchView // 검색어 변수로 받음. + + editText.addTextChangedListener(object : TextWatcher { + override fun beforeTextChanged(charSequence: CharSequence, i: Int, i1: Int, i2: Int) { + // Optional: Add any functionality needed before text changes + } + override fun onTextChanged(charSequence: CharSequence, i: Int, i1: Int, i2: Int) { + // Optional: Add any functionality needed during text changes + } + + override fun afterTextChanged(editable: Editable) { + //Log.d("db", "addTextChangedListener") + val searchText = editText.text.toString().trim() // trim()추가 : 검색 편리화, 띄어쓰기해도 검색 가능 + //Log.d("db", "$searchText") + searchList.clear() // 검색 결과 갱신 + + if (searchText.isEmpty()) { + adapter.setFriendList(originalList) + } else { + // 검색 단어를 포함하는지 확인 + for (a in 0 until originalList.size) { + //Log.d("db", "for loop: $a") + if (originalList[a].getName().toLowerCase().contains(searchText.toLowerCase(), ignoreCase=true)) { // ignoreCase=true : 검색 편리화, 대문자=소문자 + searchList.add(originalList[a]) + } + } + /*for (original in originalList){ // for문의 또다른 방식 - 간단해서 추천 삭제해도 됨. + if(searchText.contains(original.getName(), ignoreCase=true)){ + val fname = original.name + val femail = original.email + Log.d("db", fname) + Log.d("db", femail) + val search = MemberData(femail. fname) + searchList.add(search) + } + }*/ + adapter.setFriendList(searchList) + adapter.notifyDataSetChanged() + } + } + }) + + // 리사이클러뷰, 어댑터 연결 + val recyclerView: RecyclerView = binding.recyclerView + recyclerView.layoutManager = LinearLayoutManager(requireContext()) + + //어댑터 연결 확인 + //adapter = DialogFriendAdapter(originalList) + adapter.setFriendList(originalList) + recyclerView.adapter = adapter + return binding.root } + companion object { - /** - * Use this factory method to create a new instance of - * this fragment using the provided parameters. - * - * @param param1 Parameter 1. - * @param param2 Parameter 2. - * @return A new instance of fragment FriendFragment. - */ // TODO: Rename and change types and number of parameters @JvmStatic fun newInstance(param1: String, param2: String) = diff --git a/app/src/main/java/com/Apic/apic/FriendList.kt b/app/src/main/java/com/Apic/apic/FriendList.kt new file mode 100644 index 0000000..8f6cb33 --- /dev/null +++ b/app/src/main/java/com/Apic/apic/FriendList.kt @@ -0,0 +1,11 @@ +package com.Apic.apic + +class FriendList ( + val email : String, + val name : String +){ + @JvmName("callFromString") + fun getName(): String { + return name + } +} \ No newline at end of file diff --git a/app/src/main/java/com/Apic/apic/GroupActivity.kt b/app/src/main/java/com/Apic/apic/GroupActivity.kt index 1a41e39..bddaebf 100644 --- a/app/src/main/java/com/Apic/apic/GroupActivity.kt +++ b/app/src/main/java/com/Apic/apic/GroupActivity.kt @@ -1,42 +1,57 @@ package com.Apic.apic import android.animation.ObjectAnimator +import android.content.Intent +import android.net.Uri import android.os.Bundle -import androidx.fragment.app.Fragment -import android.view.LayoutInflater import android.view.View -import android.view.ViewGroup import android.widget.ImageButton import android.widget.PopupMenu +import android.widget.TextView import android.widget.Toast +import androidx.activity.result.ActivityResultLauncher +import androidx.activity.result.contract.ActivityResultContracts import androidx.appcompat.app.AppCompatActivity import androidx.fragment.app.FragmentManager -import androidx.fragment.app.FragmentTransaction import androidx.viewpager2.widget.ViewPager2 +import com.Apic.apic.databinding.ActivityGroupBinding import com.google.android.material.floatingactionbutton.FloatingActionButton import com.google.android.material.tabs.TabLayout class GroupActivity : AppCompatActivity() { + // 뷰 변수 선언 private lateinit var tabLayout: TabLayout private lateinit var viewPager2: ViewPager2 private lateinit var adapter: FragmentPagerAdapter - private var isFabOpen = false - + private lateinit var albumAdapter: AlbumAdapter private lateinit var fabMain: FloatingActionButton private lateinit var fabCamera: FloatingActionButton private lateinit var fabCreate: FloatingActionButton private lateinit var groupLikedButton: ImageButton - private var isLiked = false - private val fragmentAddMeeting = AddMeetingFragment() + private lateinit var menuIcon: ImageButton + lateinit var binding: ActivityGroupBinding + + // 상태 변수 선언 + private var isFabOpen = false + private var isLiked = false + var imageList: ArrayList = ArrayList() private val fragmentManager: FragmentManager = supportFragmentManager + private lateinit var groupName: TextView + private lateinit var groupMemberNum: TextView + override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - setContentView(R.layout.acticity_group) + // ViewBinding을 사용하여 콘텐츠 뷰 설정 + binding = ActivityGroupBinding.inflate(layoutInflater) + setContentView(binding.root) + + // albumAdapter 초기화 + albumAdapter = AlbumAdapter(imageList, this) // 뷰 초기화 initializeViews() @@ -57,19 +72,34 @@ class GroupActivity : AppCompatActivity() { fabCreate = findViewById(R.id.fab_create) groupLikedButton = findViewById(R.id.group_liked) menuIcon = findViewById(R.id.menu_icon) + groupName = findViewById(R.id.group_name) + groupMemberNum = findViewById(R.id.group_member_num) + + // 그룹 정보 반영 + val gName = intent.getStringExtra("g_name") + val gParticipants = intent.getStringExtra("g_participants") + groupName.text = gName + groupMemberNum.text = gParticipants + + // MemberFragment에 그룹 정보 전달 + var memberFragment = MemberFragment() + var bundle = Bundle() + bundle.putString("g_name", gName) + memberFragment.arguments = bundle } // 탭 레이아웃 설정 private fun setupTabLayout() { adapter = FragmentPagerAdapter(supportFragmentManager, lifecycle) - tabLayout.addTab(tabLayout.newTab().setText("date")) + tabLayout.addTab(tabLayout.newTab().setText("album")) tabLayout.addTab(tabLayout.newTab().setText("member")) viewPager2.adapter = adapter } - // 뷰페이저 설정 + // 뷰페이저와 콜백을 설정 private fun setupViewPager() { tabLayout.addOnTabSelectedListener(object : TabLayout.OnTabSelectedListener { + // 탭 선택 이벤트 처리 override fun onTabSelected(tab: TabLayout.Tab?) { tab?.let { viewPager2.currentItem = it.position } } @@ -83,6 +113,7 @@ class GroupActivity : AppCompatActivity() { } }) + // 뷰페이저 페이지 변경 콜백 설정 viewPager2.registerOnPageChangeCallback(object : ViewPager2.OnPageChangeCallback() { override fun onPageSelected(position: Int) { super.onPageSelected(position) @@ -91,19 +122,33 @@ class GroupActivity : AppCompatActivity() { }) } - // 리스너 설정 + // FAB, 버튼 및 메뉴 아이콘의 클릭 리스너 설정 private fun setupListeners() { - fabMain.setOnClickListener { - // toggleFab() - // viewpager AddMeetingFragment로 전환 - + fabMain.setOnClickListener { toggleFab() } + fabCamera.setOnClickListener { + showToast("카메라 버튼 클릭!") + + // 이미지 선택 액티비티 시작 + val intent = Intent(Intent.ACTION_PICK) + intent.type = "image/*" + intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true) // 다중 이미지 선택 활성화 + activityResult.launch(intent) } - fabCamera.setOnClickListener { showToast("카메라 버튼 클릭!") } fabCreate.setOnClickListener { showToast("버튼 클릭!") } groupLikedButton.setOnClickListener { toggleLikedState() } menuIcon.setOnClickListener { showPopupMenu(it) } } + // 이미지 선택 액티비티 결과 처리 + private val activityResult: ActivityResultLauncher = registerForActivityResult( + ActivityResultContracts.StartActivityForResult()) { result -> + if (result.resultCode == RESULT_OK && result.data != null) { + // 선택한 이미지를 가져와 현재 프래그먼트의 이미지 목록 업데이트 + val albumFragment = supportFragmentManager.findFragmentByTag("f${viewPager2.currentItem}") as? AlbumFragment + albumFragment?.updateImageList(result.data) + } + } + // 플로팅 버튼 토글 private fun toggleFab() { showToast("메인 플로팅 버튼 클릭!") @@ -111,22 +156,21 @@ class GroupActivity : AppCompatActivity() { val translateYCamera = if (isFabOpen) 0f else -200f val translateYCreate = if (isFabOpen) 0f else -400f + // FAB 애니메이션 ObjectAnimator.ofFloat(fabCamera, "translationY", translateYCamera).start() ObjectAnimator.ofFloat(fabCreate, "translationY", translateYCreate).start() + // 메인 FAB 아이콘 변경 및 열린 상태 업데이트 fabMain.setImageResource(R.drawable.ic_add) isFabOpen = !isFabOpen } // 즐겨찾기 상태 토글 private fun toggleLikedState() { - isLiked = !isLiked - // 즐겨찾기 상태에 따라 아이콘 변경 groupLikedButton.setImageResource(if (isLiked) R.drawable.ic_liked else R.drawable.ic_unliked) - - // 즐겨찾기 상태에 따라 토스트 메시지 출력 + // 즐겨찾기 상태에 따라 토스트 메세지 출력 showToast(if (isLiked) "즐겨찾기 등록!" else "즐겨찾기 해제!") } @@ -136,6 +180,7 @@ class GroupActivity : AppCompatActivity() { val inflater = popupMenu.menuInflater inflater.inflate(R.menu.menu_group, popupMenu.menu) + // 메뉴 아이템 클릭 처리 popupMenu.setOnMenuItemClickListener { item -> when (item.itemId) { R.id.menu_item1 -> { @@ -146,7 +191,8 @@ class GroupActivity : AppCompatActivity() { R.id.menu_item2 -> { showToast("메뉴 아이템 2 클릭!") true - }// 추가적인 메뉴 아이템이 필요한 경우 여기에 추가 + } + // 추가적인 메뉴 아이템 필요한 경우 여기에 추가 else -> false } } diff --git a/app/src/main/java/com/Apic/apic/GroupData.kt b/app/src/main/java/com/Apic/apic/GroupData.kt new file mode 100644 index 0000000..eacc729 --- /dev/null +++ b/app/src/main/java/com/Apic/apic/GroupData.kt @@ -0,0 +1,9 @@ +package com.Apic.apic + +data class GroupData ( + val g_name : String = "", + val g_participants : String + ) + + + diff --git a/app/src/main/java/com/Apic/apic/GroupDateAdapter.kt b/app/src/main/java/com/Apic/apic/GroupDateAdapter.kt index 05d2dcb..1091c26 100644 --- a/app/src/main/java/com/Apic/apic/GroupDateAdapter.kt +++ b/app/src/main/java/com/Apic/apic/GroupDateAdapter.kt @@ -3,8 +3,6 @@ package com.Apic.apic import android.view.LayoutInflater import android.view.View import android.view.ViewGroup -import android.widget.TextView -import android.widget.Toast import androidx.recyclerview.widget.RecyclerView import com.Apic.apic.databinding.RvGroupDateBinding diff --git a/app/src/main/java/com/Apic/apic/GroupListAdapter.kt b/app/src/main/java/com/Apic/apic/GroupListAdapter.kt index ae5fcff..e05f4c7 100644 --- a/app/src/main/java/com/Apic/apic/GroupListAdapter.kt +++ b/app/src/main/java/com/Apic/apic/GroupListAdapter.kt @@ -1,27 +1,20 @@ package com.Apic.apic -import android.content.Context -import android.content.Intent import android.view.LayoutInflater import android.view.View import android.view.ViewGroup -import android.widget.Toast -import androidx.core.content.ContextCompat.startActivity import androidx.recyclerview.widget.RecyclerView import com.Apic.apic.databinding.RvGroupListBinding -class GroupListAdapter : RecyclerView.Adapter() { +class GroupListAdapter(var items:MutableList) : RecyclerView.Adapter() { private lateinit var itemClickListener : OnItemClickListener - private var data = ArrayList() + + private var mGroupList: MutableList = ArrayList() inner class ViewHolder(val binding: RvGroupListBinding): RecyclerView.ViewHolder(binding.root) { - fun onBind(item: GroupListItem) { - binding.groupName.text = item.getGroupName() // 그룹 이름 - binding.groupNum.text = item.getGroupNum().toString() // 그룹 멤버명수 - binding.groupLiked.isActivated = item.isGroupLiked() // 그룹 즐겨찾기 (안됨) -// Glide.with(binding) // 그룹 사진 -// .load(item.group_photo) -// .into(binding.groupPhoto) + fun onBind(items: GroupData) { + binding.groupName.text = items.g_name // 그룹 이름 + binding.groupNum.text = items.g_participants // 그룹 멤버명수 } } // 아이템 클릭리스너 @@ -41,23 +34,20 @@ class GroupListAdapter : RecyclerView.Adapter() { } override fun getItemCount(): Int { - return data.size + return items.size } - override fun onBindViewHolder(holder: ViewHolder, position: Int) { - holder.onBind(data[position]) + override fun onBindViewHolder(holder: GroupListAdapter.ViewHolder, position: Int) { + holder.onBind(items[position]) holder.itemView.setOnClickListener { itemClickListener.onClick(it, position) } } - fun replaceList(newList: ArrayList) { - data = newList - // 어댑터에 데이터가 변했다는 notify를 날린다 + fun setGroupList(list: MutableList) { + mGroupList = list + items = mGroupList notifyDataSetChanged() } - - - } diff --git a/app/src/main/java/com/Apic/apic/GroupListFragment.kt b/app/src/main/java/com/Apic/apic/GroupListFragment.kt index 65215f1..c252529 100644 --- a/app/src/main/java/com/Apic/apic/GroupListFragment.kt +++ b/app/src/main/java/com/Apic/apic/GroupListFragment.kt @@ -2,15 +2,18 @@ package com.Apic.apic import android.content.Intent import android.os.Bundle +import android.text.Editable +import android.text.TextWatcher +import android.util.Log import android.view.* +import android.widget.EditText import android.widget.Toast -import androidx.appcompat.app.AppCompatActivity import androidx.fragment.app.Fragment -import androidx.fragment.app.FragmentManager import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView -import com.Apic.apic.databinding.FragmentAddGroupBinding import com.Apic.apic.databinding.FragmentGroupListBinding +import com.google.firebase.auth.FirebaseAuth +import com.google.firebase.firestore.FirebaseFirestore // TODO: Rename parameter arguments, choose names that match // the fragment initialization parameters, e.g. ARG_ITEM_NUMBER @@ -27,16 +30,34 @@ class GroupListFragment : Fragment() { private var param1: String? = null private var param2: String? = null - private val group_datas : ArrayList = ArrayList() + private val groupList = mutableListOf() private lateinit var groupListAdapter: GroupListAdapter + private val searchList: ArrayList = ArrayList() + private lateinit var Etsearch: EditText + + private lateinit var auth: FirebaseAuth + private val db = FirebaseFirestore.getInstance() + + private fun setOnClickEvent() { - groupListAdapter.setItemClickListener(object:GroupListAdapter.OnItemClickListener { + groupListAdapter.setItemClickListener(object: GroupListAdapter.OnItemClickListener { override fun onClick(view:View, position:Int) { super.onClick(view, position) - Toast.makeText(view.context, "테스트 - ${group_datas[position].getGroupName()}클릭", Toast.LENGTH_SHORT).show() - val intent = Intent(getActivity(), GroupActivity::class.java) + Toast.makeText(view.context, "테스트 - ${groupList[position].g_name}클릭", Toast.LENGTH_SHORT).show() + + // 그룹 정보 넘기기 -> groupActivity + val intent = Intent(activity, GroupActivity::class.java) + intent.putExtra("g_name", groupList[position].g_name) + intent.putExtra("g_participants", groupList[position].g_participants) startActivity(intent) + + // 그룹 정보 넘기기 -> memberFragment + var memberFragment = MemberFragment() + var bundle = Bundle() + bundle.putString("g_name", groupList[position].g_name) + Log.d("groupname", bundle.toString()) + memberFragment.arguments = bundle } }) } @@ -55,15 +76,11 @@ class GroupListFragment : Fragment() { // Inflate the layout for this fragment val binding = FragmentGroupListBinding.inflate(inflater, container, false) - group_datas.add(GroupListItem("GroupA", 3, true)) - group_datas.add(GroupListItem("GroupB", 5, true)) - group_datas.add(GroupListItem("GroupC", 6, true)) - group_datas.add(GroupListItem("GroupD", 9, true)) + auth = FirebaseAuth.getInstance() val recyclerView: RecyclerView = binding.groupRecyclerView recyclerView.layoutManager = LinearLayoutManager(requireContext()) - groupListAdapter = GroupListAdapter() - groupListAdapter.replaceList(group_datas) + groupListAdapter = GroupListAdapter(groupList) recyclerView.adapter = groupListAdapter setOnClickEvent() @@ -74,14 +91,67 @@ class GroupListFragment : Fragment() { if (transaction != null) { transaction.replace(R.id.menu_frame_view, AddGroupFragment()).commitAllowingStateLoss() } - } + // 그룹 리스트 + getGroupData() //(activity as AppCompatActivity).setSupportActionBar(R.id.back) // 뒤로가기 메뉴 + // 그룹 검색 + Etsearch = binding.Etsearch + Etsearch.addTextChangedListener(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(p0: Editable?) { + val searchText = Etsearch.text.toString().trim() + searchList.clear() + + if (searchText.isEmpty()) { + groupListAdapter.setGroupList(groupList) + } + else { + for (a in 0 until groupList.size) { + if (groupList[a].g_name.toLowerCase().contains(searchText.toLowerCase())) { + searchList.add(groupList[a]) + } + } + } + groupListAdapter.setGroupList(searchList) + groupListAdapter.notifyDataSetChanged() + } + }) + return binding.root } + private fun getGroupData() { + db.collection("memberDB") + .document(auth.currentUser!!.email.toString()) + .collection("groups") + .get() + .addOnSuccessListener { result -> + groupList.clear() + for (document in result) { + val group = GroupData ( + document["g_name"] as String, + document["g_participants"] as String + ) + groupList.add(group) + } + groupListAdapter.notifyDataSetChanged() + Log.d("db", "success") + } + .addOnFailureListener { + Log.d("db", "fail") + } + } + // 뒤로가기 버튼 // override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) { // inflater.inflate(R.menu.toolbar_menu, menu) diff --git a/app/src/main/java/com/Apic/apic/GroupListItem.kt b/app/src/main/java/com/Apic/apic/GroupListItem.kt deleted file mode 100644 index 0a4deab..0000000 --- a/app/src/main/java/com/Apic/apic/GroupListItem.kt +++ /dev/null @@ -1,19 +0,0 @@ -package com.Apic.apic - -data class GroupListItem( -// val group_photo : String = "", // 그룹 사진 - val group_name: String = "", // 그룹 이름 - val group_num: Int = 0, // 그룹 멤버명수 - val group_liked : Boolean = false // 그룹 즐겨찾기 -) -{ - fun getGroupName(): String { - return group_name - } - fun getGroupNum() : Int { - return group_num - } - fun isGroupLiked() : Boolean { - return group_liked - } - } \ No newline at end of file diff --git a/app/src/main/java/com/Apic/apic/GroupMemberAdapter.kt b/app/src/main/java/com/Apic/apic/GroupMemberAdapter.kt index afb85be..4c8ab7d 100644 --- a/app/src/main/java/com/Apic/apic/GroupMemberAdapter.kt +++ b/app/src/main/java/com/Apic/apic/GroupMemberAdapter.kt @@ -5,12 +5,11 @@ import android.view.ViewGroup import androidx.recyclerview.widget.RecyclerView import com.Apic.apic.databinding.RvGroupMemberBinding -class GroupMemberAdapter: RecyclerView.Adapter() { - private var data = ArrayList() +class GroupMemberAdapter(val items:MutableList): RecyclerView.Adapter() { inner class ViewHolder(val binding: RvGroupMemberBinding): RecyclerView.ViewHolder(binding.root) { - fun onBind(item : String) { - binding.memberName.text = item + fun onBind(items : GroupMemberData) { + binding.memberName.text = items.gm_name } } @@ -20,17 +19,12 @@ class GroupMemberAdapter: RecyclerView.Adapter() return ViewHolder(binding) } - override fun onBindViewHolder(holder: ViewHolder, position: Int) { - holder.onBind(data[position]) + override fun onBindViewHolder(holder: GroupMemberAdapter.ViewHolder, position: Int) { + holder.onBind(items[position]) } override fun getItemCount(): Int { - return data.size + return items.size } - fun replaceList(newList:ArrayList) { - data = newList - // 어댑터에 데이터가 변했다는 notify를 날린다 - notifyDataSetChanged() - } } \ No newline at end of file diff --git a/app/src/main/java/com/Apic/apic/GroupMemberData.kt b/app/src/main/java/com/Apic/apic/GroupMemberData.kt new file mode 100644 index 0000000..23f9219 --- /dev/null +++ b/app/src/main/java/com/Apic/apic/GroupMemberData.kt @@ -0,0 +1,5 @@ +package com.Apic.apic + +data class GroupMemberData ( + val gm_name:String +) diff --git a/app/src/main/java/com/Apic/apic/GroupParticipantsAdapter.kt b/app/src/main/java/com/Apic/apic/GroupParticipantsAdapter.kt new file mode 100644 index 0000000..0f964d3 --- /dev/null +++ b/app/src/main/java/com/Apic/apic/GroupParticipantsAdapter.kt @@ -0,0 +1,45 @@ +package com.Apic.apic + +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.recyclerview.widget.RecyclerView +import com.Apic.apic.databinding.RvGroupParticipantsBinding + +class GroupParticipantsAdapter(val items:MutableList) : RecyclerView.Adapter() { + private lateinit var itemClickListener : OnItemClickListener + + inner class ViewHolder(val binding: RvGroupParticipantsBinding): RecyclerView.ViewHolder(binding.root) { + fun onBind(items : FriendData) { + binding.memberName.text = items.f_name + binding.memberId.text = items.f_email + } + } + + interface OnItemClickListener { + fun onClick(view: View, position: Int) { + // Toast.makeText(view.context, "테스트 - ${position}클릭", Toast.LENGTH_SHORT).show() + } + } + fun setItemClickListener(onItemClickListener: OnItemClickListener) { + this.itemClickListener = onItemClickListener + } + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { + var layoutInflater = LayoutInflater.from(parent.context) + val binding = RvGroupParticipantsBinding.inflate(layoutInflater,parent,false) + return ViewHolder(binding) + } + + override fun onBindViewHolder(holder: GroupParticipantsAdapter.ViewHolder, position: Int) { + holder.onBind(items[position]) + holder.itemView.setOnClickListener { + itemClickListener.onClick(it, position) + } + } + + override fun getItemCount(): Int { + return items.size + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/Apic/apic/LoginActivity.kt b/app/src/main/java/com/Apic/apic/LoginActivity.kt index 98b92a2..23a83f6 100644 --- a/app/src/main/java/com/Apic/apic/LoginActivity.kt +++ b/app/src/main/java/com/Apic/apic/LoginActivity.kt @@ -4,26 +4,18 @@ import android.content.Intent import androidx.appcompat.app.AppCompatActivity import android.os.Bundle import android.util.Log -import android.view.View import android.widget.Button import android.widget.EditText -import android.widget.ImageView -import android.widget.TextView import android.widget.Toast -import androidx.core.content.ContextCompat.startActivity -import androidx.core.view.isVisible -import com.Apic.apic.databinding.ActivityLoginBinding import com.google.firebase.auth.FirebaseAuth import com.google.firebase.firestore.FirebaseFirestore -import java.util.jar.Attributes +import com.Apic.apic.databinding.ActivityLoginBinding class LoginActivity : AppCompatActivity() { - lateinit var registerBinding: ActivityLoginBinding - private var _binding: ActivityLoginBinding?= null - private val binding get() =_binding!! + private lateinit var binding: ActivityLoginBinding - lateinit var nameEt : EditText //이름 + lateinit var nameEt: EditText //이름 lateinit var emailEt: EditText //이메일 lateinit var passwordEt: EditText //pw lateinit var loginBtn: Button @@ -35,7 +27,7 @@ class LoginActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - _binding = ActivityLoginBinding.inflate(layoutInflater) + binding = ActivityLoginBinding.inflate(layoutInflater) setContentView(binding.root) //topbar에 있는 회원페이지 안 보이게 하기 (사실상 로그인 페이지에 더이상 topbar가 필요 없어서 topbar자체를 삭제) @@ -50,15 +42,16 @@ class LoginActivity : AppCompatActivity() { nameEt = findViewById(R.id.user_name) emailEt = findViewById(R.id.user_id) + // 로그인 passwordEt = findViewById(R.id.user_pw) loginBtn = findViewById(R.id.button) - //로그인 loginBtn.setOnClickListener { var email = emailEt.text.toString() var password = passwordEt.text.toString() var name = nameEt.text.toString() + auth.createUserWithEmailAndPassword(email, password) // 회원 가입 .addOnCompleteListener(this) { result -> if (result.isSuccessful) { @@ -69,6 +62,9 @@ class LoginActivity : AppCompatActivity() { //data를 firebase에 저장 val data = FirebaseData(sampleNumber, name, email, password) setDocument(data) // 데이터 Firestore에 저장 + // val memberData = MemberData(email, name, password) + val memberData = MemberData(email, name) + setMember(memberData) var intent = Intent(this, MainActivity::class.java) intent.putExtra("name", name) //name도 함께 전달 startActivity(intent) @@ -85,16 +81,35 @@ class LoginActivity : AppCompatActivity() { } fun login(email:String,password:String){ + nameEt = findViewById(R.id.user_name) + var name = nameEt.text.toString() + auth.signInWithEmailAndPassword(email,password) // 로그인 .addOnCompleteListener { result-> if(result.isSuccessful){ var intent = Intent(this, MainActivity::class.java) + intent.putExtra("name", name) //name도 함께 전달 startActivity(intent) } } } + + // Firestore의 memberDB에 데이터 저장 함수 + private fun setMember(data : MemberData){ + Log.d("db", "firebaseStore") + val db = FirebaseFirestore.getInstance() + db.collection("memberDB") + .document(data.email) + .set(data) + .addOnSuccessListener { + Log.d("db", "success") + }.addOnFailureListener{ + Log.d("db", "fail") + } + } + // Firestore에 데이터를 저장하는 함수 private fun setDocument(data: FirebaseData) { Log.d("DB", "firebaseStore") diff --git a/app/src/main/java/com/Apic/apic/MainActivity.kt b/app/src/main/java/com/Apic/apic/MainActivity.kt index 0945f1c..865dd75 100644 --- a/app/src/main/java/com/Apic/apic/MainActivity.kt +++ b/app/src/main/java/com/Apic/apic/MainActivity.kt @@ -1,47 +1,40 @@ package com.Apic.apic - +import android.app.Activity import android.content.Intent +import android.net.Uri import androidx.appcompat.app.AppCompatActivity import android.os.Bundle -import android.os.PersistableBundle import android.util.Log -import android.view.Gravity import android.view.Menu import android.view.MenuItem -import android.view.Window -import android.widget.Button -import android.widget.EditText import android.widget.ImageView import android.widget.TextView -import android.widget.Toolbar -import androidx.appcompat.app.ActionBarDrawerToggle import androidx.core.view.GravityCompat import androidx.drawerlayout.widget.DrawerLayout import com.Apic.apic.databinding.ActivityMainBinding import androidx.fragment.app.FragmentManager import androidx.fragment.app.FragmentTransaction -import com.Apic.apic.databinding.TopbarBinding import com.google.android.material.bottomnavigation.BottomNavigationView import com.google.android.material.navigation.NavigationView -import com.google.android.material.navigation.NavigationView.OnNavigationItemSelectedListener import com.google.firebase.auth.FirebaseAuth -import com.google.firebase.firestore.DocumentReference -import com.google.firebase.firestore.FirebaseFirestore -import org.w3c.dom.Text -import kotlin.math.log class MainActivity : AppCompatActivity(), NavigationView.OnNavigationItemSelectedListener { lateinit var binding: ActivityMainBinding - private val fragmentManager: FragmentManager = supportFragmentManager + private lateinit var fragmentTransaction: FragmentTransaction // + private val fragmentAddFriend = AddFriendFragment() + private var fragmentManager: FragmentManager = supportFragmentManager private val fragmentCalendar = CalendarFragment() private val fragmentFriend = FriendFragment() private val fragmentGroupList = GroupListFragment() + private val fragmentFourcut = FourcutFragment() private lateinit var auth: FirebaseAuth - var MenuAuth : MenuItem ?= null + companion object { + const val PICK_IMAGE_REQUEST_CODE = 1000 // 아무 숫자나 상관없지만 중복되지 않도록 정의 + } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) @@ -49,12 +42,9 @@ class MainActivity : AppCompatActivity(), NavigationView.OnNavigationItemSelecte setContentView(binding.root) auth = FirebaseAuth.getInstance() -// email = findViewById(R.id.userEmail) -// email.text = auth.currentUser?.email // 회원 이메일 표시 val transaction: FragmentTransaction = fragmentManager.beginTransaction() - transaction.replace(R.id.menu_frame_view, fragmentCalendar).commitAllowingStateLoss() - + transaction.replace(R.id.menu_frame_view,fragmentCalendar).commitAllowingStateLoss() //Login// -> menu_nav로 옮겨서 아래 Item에 있어요 @@ -83,11 +73,13 @@ class MainActivity : AppCompatActivity(), NavigationView.OnNavigationItemSelecte .commitAllowingStateLoss() R.id.menu_share -> transaction.replace(R.id.menu_frame_view, fragmentGroupList) .commitAllowingStateLoss() - + R.id.menu_fourcut -> transaction.replace(R.id.menu_frame_view, fragmentFourcut) + .commitAllowingStateLoss() } true } + //drawer-> navigation_hear에 있는 textView에 현재 로그인 정보 전달 val navigationView: NavigationView = findViewById(R.id.main_drawer) navigationView.setNavigationItemSelectedListener(this) @@ -98,7 +90,6 @@ class MainActivity : AppCompatActivity(), NavigationView.OnNavigationItemSelecte val auth_email: TextView = headerView.findViewById(R.id.auth_email) //firebaseAuth 데이터 가져오기 val user = auth.currentUser - val db = FirebaseFirestore.getInstance() user?.let { currentUser -> user?.let { @@ -130,31 +121,63 @@ class MainActivity : AppCompatActivity(), NavigationView.OnNavigationItemSelecte } - override fun onNavigationItemSelected(item: MenuItem): Boolean { - return when (item.itemId) { - R.id.logOut -> { - Log.d("logout", "로그아웃 버튼 클릭") - - auth.signOut() - val intent = Intent(this, LoginActivity::class.java) - startActivity(intent) - finish() - true - } - else -> super.onOptionsItemSelected(item) + override fun onNavigationItemSelected(item: MenuItem): Boolean { + return when (item.itemId) { + R.id.galleryButton -> { + Log.d("item", "사진 불러오기") + + val intent = Intent(Intent.ACTION_PICK) + intent.type = "image/*" + startActivityForResult(intent, PICK_IMAGE_REQUEST_CODE) + + true + } + R.id.logOut -> { + Log.d("logout", "로그아웃 버튼 클릭") + + auth.signOut() + val intent = Intent(this, LoginActivity::class.java) + startActivity(intent) + finish() + true } - } + else -> super.onOptionsItemSelected(item) + } + } //옵션메뉴 -> 메뉴바 보여지도록 가시화(명시) //옵션 멘 만들기 - override fun onCreateOptionsMenu(menu: Menu?): Boolean { - menuInflater.inflate(R.menu.menu_navigation, menu) - return super.onCreateOptionsMenu(menu) - } + override fun onCreateOptionsMenu(menu: Menu?): Boolean { + menuInflater.inflate(R.menu.menu_navigation, menu) + return super.onCreateOptionsMenu(menu) + } -} + fun setFragment(n: Int) { + fragmentManager = supportFragmentManager + fragmentTransaction = fragmentManager.beginTransaction() + when (n) { + 1 -> fragmentTransaction.replace(R.id.menu_frame_view, fragmentAddFriend).commit() // FriendFragment, addButton눌렀을 때 + } + when (n) { + 0 -> fragmentTransaction.replace(R.id.menu_frame_view, fragmentFriend).commit() // FriendFragment, addButton눌렀을 때 + } + + } + + override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { + super.onActivityResult(requestCode, resultCode, data) + if (requestCode == PICK_IMAGE_REQUEST_CODE && resultCode == Activity.RESULT_OK) { + if (data != null) { + val selectedImageUri: Uri? = data.data + // 선택한 이미지 URI를 auth_img에 설정 + val auth_img: ImageView = findViewById(R.id.userImageView) + auth_img.setImageURI(selectedImageUri) + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/Apic/apic/MainViewModel.kt b/app/src/main/java/com/Apic/apic/MainViewModel.kt new file mode 100644 index 0000000..8a5d88d --- /dev/null +++ b/app/src/main/java/com/Apic/apic/MainViewModel.kt @@ -0,0 +1,61 @@ +// MainViewModel.kt +package com.Apic.apic + +import android.app.Application +import androidx.lifecycle.AndroidViewModel +import androidx.lifecycle.viewModelScope +import androidx.room.Room +import com.Apic.apic.data.Todo +import com.Apic.apic.data.TodoDatabase +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.launch + +class MainViewModel(application: Application) : AndroidViewModel(application) { + // 데이터베이스 + private val db = Room.databaseBuilder( + application, + TodoDatabase::class.java, "todo" + ).build() + + // db의 결과를 관찰 + private val _items = MutableStateFlow>(emptyList()) + val items: StateFlow> = _items + + // 초기화시 모든 데이터를 읽어 옴 + init { + viewModelScope.launch { + db.todoDao().getAll().collect { todos -> + _items.value = todos + } + } + } + + // 추가 + fun addTodo(todo: Todo) { // Modify this line + viewModelScope.launch { + db.todoDao().insert(todo) // Modify this line + } + } + + fun deleteTodo(id: Long) { + _items.value + .find { todo -> todo.id == id } + ?.let { todo -> + viewModelScope.launch { + db.todoDao().delete(todo) + } + } + } + + private val _selectedDateTodos = MutableStateFlow>(emptyList()) + val selectedDateTodos: StateFlow> = _selectedDateTodos + + fun getTodosBySelectedDate(selectedDate: Long) { + viewModelScope.launch { + db.todoDao().getTodosByDate(selectedDate).collect { todos -> + _selectedDateTodos.value = todos + } + } + } +} diff --git a/app/src/main/java/com/Apic/apic/MemberData.kt b/app/src/main/java/com/Apic/apic/MemberData.kt new file mode 100644 index 0000000..53614c8 --- /dev/null +++ b/app/src/main/java/com/Apic/apic/MemberData.kt @@ -0,0 +1,12 @@ +package com.Apic.apic + +class MemberData ( + val email : String, + val name : String, + //val password : String, +){ + @JvmName("callFromString") + fun getName(): String { + return name + } +} diff --git a/app/src/main/java/com/Apic/apic/MemberFragment.kt b/app/src/main/java/com/Apic/apic/MemberFragment.kt index 21f000d..ca275af 100644 --- a/app/src/main/java/com/Apic/apic/MemberFragment.kt +++ b/app/src/main/java/com/Apic/apic/MemberFragment.kt @@ -1,14 +1,16 @@ package com.Apic.apic import android.os.Bundle +import android.util.Log import androidx.fragment.app.Fragment import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import androidx.recyclerview.widget.GridLayoutManager import androidx.recyclerview.widget.RecyclerView -import com.Apic.apic.databinding.FragmentDateBinding import com.Apic.apic.databinding.FragmentMemberBinding +import com.google.firebase.auth.FirebaseAuth +import com.google.firebase.firestore.FirebaseFirestore // TODO: Rename parameter arguments, choose names that match // the fragment initialization parameters, e.g. ARG_ITEM_NUMBER @@ -25,8 +27,14 @@ class MemberFragment : Fragment() { private var param1: String? = null private var param2: String? = null + private lateinit var binding: FragmentMemberBinding + private val db = FirebaseFirestore.getInstance() + private lateinit var auth: FirebaseAuth + private lateinit var groupMemberAdapter: GroupMemberAdapter + private val memberList = mutableListOf() + override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) arguments?.let { @@ -40,19 +48,46 @@ class MemberFragment : Fragment() { savedInstanceState: Bundle? ): View? { // Inflate the layout for this fragment - val binding = FragmentMemberBinding.inflate(inflater, container, false) - var list = arrayListOf("Name_A","Name_B","Name_C","Name_D","Name_E","Name_F","Name_G") + binding = FragmentMemberBinding.inflate(inflater, container, false) + + auth = FirebaseAuth.getInstance() val recyclerView: RecyclerView = binding.groupMemberRecyclerView recyclerView.layoutManager = GridLayoutManager(context, 4) - groupMemberAdapter = GroupMemberAdapter() - groupMemberAdapter.replaceList(list) + groupMemberAdapter = GroupMemberAdapter(memberList) recyclerView.adapter = groupMemberAdapter +// val groupName = arguments?.getString("g_name") + // 오류사항 - 시연때 그룹이름 픽스 + val groupName = "appic" + getMemberData(groupName) return binding.root } + private fun getMemberData(g_name:String) { + db.collection("memberDB") + .document(auth.currentUser!!.email.toString()) + .collection("groups") + .document(g_name) + .collection("participants") + .get() + .addOnSuccessListener { result -> + memberList.clear() + for (document in result) { + val member = GroupMemberData ( + document["f_email"] as String, + ) + memberList.add(member) + } + groupMemberAdapter.notifyDataSetChanged() + Log.d("db", "success") + } + .addOnFailureListener { + Log.d("db", "fail") + } + } + companion object { /** * Use this factory method to create a new instance of diff --git a/app/src/main/java/com/Apic/apic/SecondFragment.kt b/app/src/main/java/com/Apic/apic/SecondFragment.kt deleted file mode 100644 index 65282fa..0000000 --- a/app/src/main/java/com/Apic/apic/SecondFragment.kt +++ /dev/null @@ -1,59 +0,0 @@ -package com.Apic.apic - -import android.os.Bundle -import androidx.fragment.app.Fragment -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup - -// TODO: Rename parameter arguments, choose names that match -// the fragment initialization parameters, e.g. ARG_ITEM_NUMBER -private const val ARG_PARAM1 = "param1" -private const val ARG_PARAM2 = "param2" - -/** - * A simple [Fragment] subclass. - * Use the [SecondFragment.newInstance] factory method to - * create an instance of this fragment. - */ -class SecondFragment : Fragment() { - // TODO: Rename and change types of parameters - private var param1: String? = null - private var param2: String? = null - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - arguments?.let { - param1 = it.getString(ARG_PARAM1) - param2 = it.getString(ARG_PARAM2) - } - } - - override fun onCreateView( - inflater: LayoutInflater, container: ViewGroup?, - savedInstanceState: Bundle? - ): View? { - // Inflate the layout for this fragment - return inflater.inflate(R.layout.fragment_second, container, false) - } - - companion object { - /** - * Use this factory method to create a new instance of - * this fragment using the provided parameters. - * - * @param param1 Parameter 1. - * @param param2 Parameter 2. - * @return A new instance of fragment FreindsFragment. - */ - // TODO: Rename and change types and number of parameters - @JvmStatic - fun newInstance(param1: String, param2: String) = - SecondFragment().apply { - arguments = Bundle().apply { - putString(ARG_PARAM1, param1) - putString(ARG_PARAM2, param2) - } - } - } -} \ No newline at end of file diff --git a/app/src/main/java/com/Apic/apic/TodoDiffUtilCallback.kt b/app/src/main/java/com/Apic/apic/TodoDiffUtilCallback.kt new file mode 100644 index 0000000..a613b0d --- /dev/null +++ b/app/src/main/java/com/Apic/apic/TodoDiffUtilCallback.kt @@ -0,0 +1,14 @@ +package com.Apic.apic + +import androidx.recyclerview.widget.DiffUtil +import com.Apic.apic.data.Todo +class TodoDiffUtilCallback : DiffUtil.ItemCallback() { + override fun areItemsTheSame(oldItem: Todo, newItem: Todo): Boolean { + return oldItem.id == newItem.id + } + + override fun areContentsTheSame(oldItem: Todo, newItem: Todo): Boolean { + // 내용이 변경되었을 때만 false를 반환하도록 수정 + return oldItem == newItem + } +} \ No newline at end of file diff --git a/app/src/main/java/com/Apic/apic/TodoListAdapter.kt b/app/src/main/java/com/Apic/apic/TodoListAdapter.kt new file mode 100644 index 0000000..9d1d688 --- /dev/null +++ b/app/src/main/java/com/Apic/apic/TodoListAdapter.kt @@ -0,0 +1,45 @@ +package com.Apic.apic + +import android.text.format.DateFormat +import android.view.LayoutInflater +import android.view.ViewGroup +import androidx.recyclerview.widget.ListAdapter +import com.Apic.apic.data.Todo +import com.Apic.apic.databinding.ItemTodoBinding +import androidx.recyclerview.widget.RecyclerView + + +class TodoListAdapter( + private val onClick: (Todo) -> Unit +) : ListAdapter(TodoDiffUtilCallback()) { + + // ViewHolder 생성, 반환 + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): TodoViewHolder { + val binding = ItemTodoBinding.inflate(LayoutInflater.from(parent.context), parent, false) + return TodoViewHolder(binding, onClick) + } + + // ViewHolder와 데이터 바인딩 + override fun onBindViewHolder(holder: TodoViewHolder, position: Int) { + holder.bind(getItem(position)) + holder.setOnClickListener(getItem(position)) + } + + // ViewHolder + class TodoViewHolder( + private val binding: ItemTodoBinding, + private val onClick: (Todo) -> Unit + ) : RecyclerView.ViewHolder(binding.root) { + + fun bind(todo: Todo) { + binding.text1.text = todo.title + binding.text2.text = DateFormat.format("yyyy/MM/dd", todo.date) + } + + fun setOnClickListener(todo: Todo) { + binding.root.setOnClickListener { + onClick(todo) + } + } + } +} diff --git a/app/src/main/java/com/Apic/apic/data/Todo.kt b/app/src/main/java/com/Apic/apic/data/Todo.kt new file mode 100644 index 0000000..436a1e3 --- /dev/null +++ b/app/src/main/java/com/Apic/apic/data/Todo.kt @@ -0,0 +1,14 @@ +package com.Apic.apic.data + +import androidx.room.Entity +import androidx.room.PrimaryKey +import java.util.Calendar + +@Entity +data class Todo( + val title:String, + val date: Long = Calendar.getInstance().timeInMillis +){ + @PrimaryKey(autoGenerate = true) + var id: Long = 0 +} \ No newline at end of file diff --git a/app/src/main/java/com/Apic/apic/data/TodoDao.kt b/app/src/main/java/com/Apic/apic/data/TodoDao.kt new file mode 100644 index 0000000..e2697cd --- /dev/null +++ b/app/src/main/java/com/Apic/apic/data/TodoDao.kt @@ -0,0 +1,24 @@ +package com.Apic.apic.data + + +import androidx.room.* +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.StateFlow + +@Dao +interface TodoDao { + @Query("SELECT * FROM todo WHERE date") + fun getAll(): Flow> + + @Insert(onConflict = OnConflictStrategy.REPLACE) + suspend fun insert(entity: Todo) + + @Delete + suspend fun delete(entity: Todo) + + @Query("SELECT * FROM todo WHERE date = :selectedDate ORDER BY date DESC") + fun getByDate(selectedDate: Long): List + + @Query("SELECT * FROM todo WHERE date = :selectedDate ORDER BY date DESC") + fun getTodosByDate(selectedDate: Long): Flow> +} \ No newline at end of file diff --git a/app/src/main/java/com/Apic/apic/data/TodoDao_Impl.java b/app/src/main/java/com/Apic/apic/data/TodoDao_Impl.java new file mode 100644 index 0000000..e64f41b --- /dev/null +++ b/app/src/main/java/com/Apic/apic/data/TodoDao_Impl.java @@ -0,0 +1,236 @@ +package com.Apic.apic.data; + +import android.database.Cursor; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.room.CoroutinesRoom; +import androidx.room.EntityDeletionOrUpdateAdapter; +import androidx.room.EntityInsertionAdapter; +import androidx.room.RoomDatabase; +import androidx.room.RoomSQLiteQuery; +import androidx.room.util.CursorUtil; +import androidx.room.util.DBUtil; +import androidx.sqlite.db.SupportSQLiteStatement; +import java.lang.Class; +import java.lang.Exception; +import java.lang.Object; +import java.lang.Override; +import java.lang.String; +import java.lang.SuppressWarnings; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.concurrent.Callable; +import kotlin.Unit; +import kotlin.coroutines.Continuation; +import kotlinx.coroutines.flow.Flow; + +@SuppressWarnings({"unchecked", "deprecation"}) +public final class TodoDao_Impl implements TodoDao { + private final RoomDatabase __db; + + private final EntityInsertionAdapter __insertionAdapterOfTodo; + + private final EntityDeletionOrUpdateAdapter __deletionAdapterOfTodo; + + public TodoDao_Impl(@NonNull final RoomDatabase __db) { + this.__db = __db; + this.__insertionAdapterOfTodo = new EntityInsertionAdapter(__db) { + @Override + @NonNull + protected String createQuery() { + return "INSERT OR REPLACE INTO `Todo` (`title`,`date`,`id`) VALUES (?,?,nullif(?, 0))"; + } + + @Override + protected void bind(@NonNull final SupportSQLiteStatement statement, + @Nullable final Todo entity) { + if (entity.getTitle() == null) { + statement.bindNull(1); + } else { + statement.bindString(1, entity.getTitle()); + } + statement.bindLong(2, entity.getDate()); + statement.bindLong(3, entity.getId()); + } + }; + this.__deletionAdapterOfTodo = new EntityDeletionOrUpdateAdapter(__db) { + @Override + @NonNull + protected String createQuery() { + return "DELETE FROM `Todo` WHERE `id` = ?"; + } + + @Override + protected void bind(@NonNull final SupportSQLiteStatement statement, + @Nullable final Todo entity) { + statement.bindLong(1, entity.getId()); + } + }; + } + + @Override + public Object insert(final Todo entity, final Continuation continuation) { + return CoroutinesRoom.execute(__db, true, new Callable() { + @Override + @NonNull + public Unit call() throws Exception { + __db.beginTransaction(); + try { + __insertionAdapterOfTodo.insert(entity); + __db.setTransactionSuccessful(); + return Unit.INSTANCE; + } finally { + __db.endTransaction(); + } + } + }, continuation); + } + + @Override + public Object delete(final Todo entity, final Continuation continuation) { + return CoroutinesRoom.execute(__db, true, new Callable() { + @Override + @NonNull + public Unit call() throws Exception { + __db.beginTransaction(); + try { + __deletionAdapterOfTodo.handle(entity); + __db.setTransactionSuccessful(); + return Unit.INSTANCE; + } finally { + __db.endTransaction(); + } + } + }, continuation); + } + + @Override + public Flow> getAll() { + final String _sql = "SELECT * FROM todo WHERE date"; + final RoomSQLiteQuery _statement = RoomSQLiteQuery.acquire(_sql, 0); + return CoroutinesRoom.createFlow(__db, false, new String[] {"todo"}, new Callable>() { + @Override + @NonNull + public List call() throws Exception { + final Cursor _cursor = DBUtil.query(__db, _statement, false, null); + try { + final int _cursorIndexOfTitle = CursorUtil.getColumnIndexOrThrow(_cursor, "title"); + final int _cursorIndexOfDate = CursorUtil.getColumnIndexOrThrow(_cursor, "date"); + final int _cursorIndexOfId = CursorUtil.getColumnIndexOrThrow(_cursor, "id"); + final List _result = new ArrayList(_cursor.getCount()); + while (_cursor.moveToNext()) { + final Todo _item; + final String _tmpTitle; + if (_cursor.isNull(_cursorIndexOfTitle)) { + _tmpTitle = null; + } else { + _tmpTitle = _cursor.getString(_cursorIndexOfTitle); + } + final long _tmpDate; + _tmpDate = _cursor.getLong(_cursorIndexOfDate); + _item = new Todo(_tmpTitle,_tmpDate); + final long _tmpId; + _tmpId = _cursor.getLong(_cursorIndexOfId); + _item.setId(_tmpId); + _result.add(_item); + } + return _result; + } finally { + _cursor.close(); + } + } + + @Override + protected void finalize() { + _statement.release(); + } + }); + } + + @Override + public List getByDate(final long selectedDate) { + final String _sql = "SELECT * FROM todo WHERE date = ? ORDER BY date DESC"; + final RoomSQLiteQuery _statement = RoomSQLiteQuery.acquire(_sql, 1); + int _argIndex = 1; + _statement.bindLong(_argIndex, selectedDate); + __db.assertNotSuspendingTransaction(); + final Cursor _cursor = DBUtil.query(__db, _statement, false, null); + try { + final int _cursorIndexOfTitle = CursorUtil.getColumnIndexOrThrow(_cursor, "title"); + final int _cursorIndexOfDate = CursorUtil.getColumnIndexOrThrow(_cursor, "date"); + final int _cursorIndexOfId = CursorUtil.getColumnIndexOrThrow(_cursor, "id"); + final List _result = new ArrayList(_cursor.getCount()); + while (_cursor.moveToNext()) { + final Todo _item; + final String _tmpTitle; + if (_cursor.isNull(_cursorIndexOfTitle)) { + _tmpTitle = null; + } else { + _tmpTitle = _cursor.getString(_cursorIndexOfTitle); + } + final long _tmpDate; + _tmpDate = _cursor.getLong(_cursorIndexOfDate); + _item = new Todo(_tmpTitle,_tmpDate); + final long _tmpId; + _tmpId = _cursor.getLong(_cursorIndexOfId); + _item.setId(_tmpId); + _result.add(_item); + } + return _result; + } finally { + _cursor.close(); + _statement.release(); + } + } + + @Override + public Flow> getTodosByDate(final long selectedDate) { + final String _sql = "SELECT * FROM todo WHERE date = ? ORDER BY date DESC"; + final RoomSQLiteQuery _statement = RoomSQLiteQuery.acquire(_sql, 1); + int _argIndex = 1; + _statement.bindLong(_argIndex, selectedDate); + return CoroutinesRoom.createFlow(__db, false, new String[] {"todo"}, new Callable>() { + @Override + @NonNull + public List call() throws Exception { + final Cursor _cursor = DBUtil.query(__db, _statement, false, null); + try { + final int _cursorIndexOfTitle = CursorUtil.getColumnIndexOrThrow(_cursor, "title"); + final int _cursorIndexOfDate = CursorUtil.getColumnIndexOrThrow(_cursor, "date"); + final int _cursorIndexOfId = CursorUtil.getColumnIndexOrThrow(_cursor, "id"); + final List _result = new ArrayList(_cursor.getCount()); + while (_cursor.moveToNext()) { + final Todo _item; + final String _tmpTitle; + if (_cursor.isNull(_cursorIndexOfTitle)) { + _tmpTitle = null; + } else { + _tmpTitle = _cursor.getString(_cursorIndexOfTitle); + } + final long _tmpDate; + _tmpDate = _cursor.getLong(_cursorIndexOfDate); + _item = new Todo(_tmpTitle,_tmpDate); + final long _tmpId; + _tmpId = _cursor.getLong(_cursorIndexOfId); + _item.setId(_tmpId); + _result.add(_item); + } + return _result; + } finally { + _cursor.close(); + } + } + + @Override + protected void finalize() { + _statement.release(); + } + }); + } + + @NonNull + public static List> getRequiredConverters() { + return Collections.emptyList(); + } +} diff --git a/app/src/main/java/com/Apic/apic/data/TodoDatabase.kt b/app/src/main/java/com/Apic/apic/data/TodoDatabase.kt new file mode 100644 index 0000000..fb9869d --- /dev/null +++ b/app/src/main/java/com/Apic/apic/data/TodoDatabase.kt @@ -0,0 +1,10 @@ +package com.Apic.apic.data + +import androidx.room.Database +import androidx.room.RoomDatabase + +@Database(entities = [Todo::class], version = 1, exportSchema = false) +abstract class TodoDatabase : RoomDatabase(){ + abstract fun todoDao(): TodoDao + +} \ No newline at end of file diff --git a/app/src/main/java/com/Apic/apic/data/TodoDatabase_Impl.java b/app/src/main/java/com/Apic/apic/data/TodoDatabase_Impl.java new file mode 100644 index 0000000..2536682 --- /dev/null +++ b/app/src/main/java/com/Apic/apic/data/TodoDatabase_Impl.java @@ -0,0 +1,169 @@ +package com.Apic.apic.data; + +import androidx.annotation.NonNull; +import androidx.room.DatabaseConfiguration; +import androidx.room.InvalidationTracker; +import androidx.room.RoomDatabase; +import androidx.room.RoomOpenHelper; +import androidx.room.migration.AutoMigrationSpec; +import androidx.room.migration.Migration; +import androidx.room.util.DBUtil; +import androidx.room.util.TableInfo; +import androidx.sqlite.db.SupportSQLiteDatabase; +import androidx.sqlite.db.SupportSQLiteOpenHelper; +import java.lang.Class; +import java.lang.Override; +import java.lang.String; +import java.lang.SuppressWarnings; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +@SuppressWarnings({"unchecked", "deprecation"}) +public final class TodoDatabase_Impl extends TodoDatabase { + private volatile TodoDao _todoDao; + + @Override + @NonNull + protected SupportSQLiteOpenHelper createOpenHelper(@NonNull final DatabaseConfiguration config) { + final SupportSQLiteOpenHelper.Callback _openCallback = new RoomOpenHelper(config, new RoomOpenHelper.Delegate(1) { + @Override + public void createAllTables(@NonNull final SupportSQLiteDatabase db) { + db.execSQL("CREATE TABLE IF NOT EXISTS `Todo` (`title` TEXT NOT NULL, `date` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)"); + db.execSQL("CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)"); + db.execSQL("INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'b612f5e0e508cf78cb0e0ab87953963f')"); + } + + @Override + public void dropAllTables(@NonNull final SupportSQLiteDatabase db) { + db.execSQL("DROP TABLE IF EXISTS `Todo`"); + final List _callbacks = mCallbacks; + if (_callbacks != null) { + for (RoomDatabase.Callback _callback : _callbacks) { + _callback.onDestructiveMigration(db); + } + } + } + + @Override + public void onCreate(@NonNull final SupportSQLiteDatabase db) { + final List _callbacks = mCallbacks; + if (_callbacks != null) { + for (RoomDatabase.Callback _callback : _callbacks) { + _callback.onCreate(db); + } + } + } + + @Override + public void onOpen(@NonNull final SupportSQLiteDatabase db) { + mDatabase = db; + internalInitInvalidationTracker(db); + final List _callbacks = mCallbacks; + if (_callbacks != null) { + for (RoomDatabase.Callback _callback : _callbacks) { + _callback.onOpen(db); + } + } + } + + @Override + public void onPreMigrate(@NonNull final SupportSQLiteDatabase db) { + DBUtil.dropFtsSyncTriggers(db); + } + + @Override + public void onPostMigrate(@NonNull final SupportSQLiteDatabase db) { + } + + @Override + @NonNull + public RoomOpenHelper.ValidationResult onValidateSchema( + @NonNull final SupportSQLiteDatabase db) { + final HashMap _columnsTodo = new HashMap(3); + _columnsTodo.put("title", new TableInfo.Column("title", "TEXT", true, 0, null, TableInfo.CREATED_FROM_ENTITY)); + _columnsTodo.put("date", new TableInfo.Column("date", "INTEGER", true, 0, null, TableInfo.CREATED_FROM_ENTITY)); + _columnsTodo.put("id", new TableInfo.Column("id", "INTEGER", true, 1, null, TableInfo.CREATED_FROM_ENTITY)); + final HashSet _foreignKeysTodo = new HashSet(0); + final HashSet _indicesTodo = new HashSet(0); + final TableInfo _infoTodo = new TableInfo("Todo", _columnsTodo, _foreignKeysTodo, _indicesTodo); + final TableInfo _existingTodo = TableInfo.read(db, "Todo"); + if (!_infoTodo.equals(_existingTodo)) { + return new RoomOpenHelper.ValidationResult(false, "Todo(com.Apic.apic.data.Todo).\n" + + " Expected:\n" + _infoTodo + "\n" + + " Found:\n" + _existingTodo); + } + return new RoomOpenHelper.ValidationResult(true, null); + } + }, "b612f5e0e508cf78cb0e0ab87953963f", "6583cdf08b8a1eba9b85d22d5b209782"); + final SupportSQLiteOpenHelper.Configuration _sqliteConfig = SupportSQLiteOpenHelper.Configuration.builder(config.context).name(config.name).callback(_openCallback).build(); + final SupportSQLiteOpenHelper _helper = config.sqliteOpenHelperFactory.create(_sqliteConfig); + return _helper; + } + + @Override + @NonNull + protected InvalidationTracker createInvalidationTracker() { + final HashMap _shadowTablesMap = new HashMap(0); + final HashMap> _viewTables = new HashMap>(0); + return new InvalidationTracker(this, _shadowTablesMap, _viewTables, "Todo"); + } + + @Override + public void clearAllTables() { + super.assertNotMainThread(); + final SupportSQLiteDatabase _db = super.getOpenHelper().getWritableDatabase(); + try { + super.beginTransaction(); + _db.execSQL("DELETE FROM `Todo`"); + super.setTransactionSuccessful(); + } finally { + super.endTransaction(); + _db.query("PRAGMA wal_checkpoint(FULL)").close(); + if (!_db.inTransaction()) { + _db.execSQL("VACUUM"); + } + } + } + + @Override + @NonNull + protected Map, List>> getRequiredTypeConverters() { + final HashMap, List>> _typeConvertersMap = new HashMap, List>>(); + _typeConvertersMap.put(TodoDao.class, TodoDao_Impl.getRequiredConverters()); + return _typeConvertersMap; + } + + @Override + @NonNull + public Set> getRequiredAutoMigrationSpecs() { + final HashSet> _autoMigrationSpecsSet = new HashSet>(); + return _autoMigrationSpecsSet; + } + + @Override + @NonNull + public List getAutoMigrations( + @NonNull final Map, AutoMigrationSpec> autoMigrationSpecs) { + final List _autoMigrations = new ArrayList(); + return _autoMigrations; + } + + @Override + public TodoDao todoDao() { + if (_todoDao != null) { + return _todoDao; + } else { + synchronized(this) { + if(_todoDao == null) { + _todoDao = new TodoDao_Impl(this); + } + return _todoDao; + } + } + } +} + diff --git a/app/src/main/res/drawable/black_circle_button.xml b/app/src/main/res/drawable/black_circle_button.xml new file mode 100644 index 0000000..0fb0e59 --- /dev/null +++ b/app/src/main/res/drawable/black_circle_button.xml @@ -0,0 +1,9 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/button_round.xml b/app/src/main/res/drawable/button_round.xml new file mode 100644 index 0000000..acf1dba --- /dev/null +++ b/app/src/main/res/drawable/button_round.xml @@ -0,0 +1,12 @@ + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/fourcut_blue_img.png b/app/src/main/res/drawable/fourcut_blue_img.png new file mode 100644 index 0000000..d128c0c Binary files /dev/null and b/app/src/main/res/drawable/fourcut_blue_img.png differ diff --git a/app/src/main/res/drawable/fourcut_green_img.png b/app/src/main/res/drawable/fourcut_green_img.png new file mode 100644 index 0000000..e521263 Binary files /dev/null and b/app/src/main/res/drawable/fourcut_green_img.png differ diff --git a/app/src/main/res/drawable/fourcut_orange_img.png b/app/src/main/res/drawable/fourcut_orange_img.png new file mode 100644 index 0000000..d6a2e0e Binary files /dev/null and b/app/src/main/res/drawable/fourcut_orange_img.png differ diff --git a/app/src/main/res/drawable/gray_circle_button.xml b/app/src/main/res/drawable/gray_circle_button.xml new file mode 100644 index 0000000..9489672 --- /dev/null +++ b/app/src/main/res/drawable/gray_circle_button.xml @@ -0,0 +1,9 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_cancel_circle_close_delete_discard_icon.png b/app/src/main/res/drawable/ic_cancel_circle_close_delete_discard_icon.png new file mode 100644 index 0000000..fcbf8f5 Binary files /dev/null and b/app/src/main/res/drawable/ic_cancel_circle_close_delete_discard_icon.png differ diff --git a/app/src/main/res/drawable/ic_check_circle_icon.png b/app/src/main/res/drawable/ic_check_circle_icon.png new file mode 100644 index 0000000..7060b8e Binary files /dev/null and b/app/src/main/res/drawable/ic_check_circle_icon.png differ diff --git a/app/src/main/res/drawable/ic_close_icon.png b/app/src/main/res/drawable/ic_close_icon.png new file mode 100644 index 0000000..60dcc07 Binary files /dev/null and b/app/src/main/res/drawable/ic_close_icon.png differ diff --git a/app/src/main/res/drawable/ic_dot_list_menu_icon.png b/app/src/main/res/drawable/ic_dot_list_menu_icon.png new file mode 100644 index 0000000..75d8f8c Binary files /dev/null and b/app/src/main/res/drawable/ic_dot_list_menu_icon.png differ diff --git a/app/src/main/res/drawable/ic_friend.png b/app/src/main/res/drawable/ic_friend.png new file mode 100644 index 0000000..2a67a0a Binary files /dev/null and b/app/src/main/res/drawable/ic_friend.png differ diff --git a/app/src/main/res/drawable/ic_group.png b/app/src/main/res/drawable/ic_group.png new file mode 100644 index 0000000..c6f89c3 Binary files /dev/null and b/app/src/main/res/drawable/ic_group.png differ diff --git a/app/src/main/res/drawable/ic_search_window.xml b/app/src/main/res/drawable/ic_search_window.xml new file mode 100644 index 0000000..6d444cd --- /dev/null +++ b/app/src/main/res/drawable/ic_search_window.xml @@ -0,0 +1,10 @@ + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_send_icon.png b/app/src/main/res/drawable/ic_send_icon.png new file mode 100644 index 0000000..d4d5d9f Binary files /dev/null and b/app/src/main/res/drawable/ic_send_icon.png differ diff --git a/app/src/main/res/drawable/iv_fourcut_frame.png b/app/src/main/res/drawable/iv_fourcut_frame.png new file mode 100644 index 0000000..b094fa4 Binary files /dev/null and b/app/src/main/res/drawable/iv_fourcut_frame.png differ diff --git a/app/src/main/res/drawable/iv_fourcut_frame_white.png b/app/src/main/res/drawable/iv_fourcut_frame_white.png new file mode 100644 index 0000000..8a0f733 Binary files /dev/null and b/app/src/main/res/drawable/iv_fourcut_frame_white.png differ diff --git a/app/src/main/res/drawable/radius20.xml b/app/src/main/res/drawable/radius20.xml index 6b5c39d..24b8595 100644 --- a/app/src/main/res/drawable/radius20.xml +++ b/app/src/main/res/drawable/radius20.xml @@ -4,4 +4,5 @@ + \ No newline at end of file diff --git a/app/src/main/res/drawable/shrimp_kang.jpg b/app/src/main/res/drawable/shrimp_kang.jpg new file mode 100644 index 0000000..7a7acbf Binary files /dev/null and b/app/src/main/res/drawable/shrimp_kang.jpg differ diff --git a/app/src/main/res/drawable/white_circle_button.xml b/app/src/main/res/drawable/white_circle_button.xml new file mode 100644 index 0000000..fbd1790 --- /dev/null +++ b/app/src/main/res/drawable/white_circle_button.xml @@ -0,0 +1,9 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/acticity_group.xml b/app/src/main/res/layout/activity_group.xml similarity index 100% rename from app/src/main/res/layout/acticity_group.xml rename to app/src/main/res/layout/activity_group.xml diff --git a/app/src/main/res/layout/activity_login.xml b/app/src/main/res/layout/activity_login.xml index 5a220d5..b5fbbaa 100644 --- a/app/src/main/res/layout/activity_login.xml +++ b/app/src/main/res/layout/activity_login.xml @@ -67,5 +67,4 @@ android:text="login / sign up" /> - \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_add_friend.xml b/app/src/main/res/layout/fragment_add_friend.xml new file mode 100644 index 0000000..fe6466a --- /dev/null +++ b/app/src/main/res/layout/fragment_add_friend.xml @@ -0,0 +1,90 @@ + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_add_group.xml b/app/src/main/res/layout/fragment_add_group.xml index d4fe810..dd95538 100644 --- a/app/src/main/res/layout/fragment_add_group.xml +++ b/app/src/main/res/layout/fragment_add_group.xml @@ -44,14 +44,14 @@ android:layout_marginLeft="8dp"/> - @@ -85,7 +85,7 @@ android:layout_marginBottom="10dp"> + android:text="" + android:inputType="number"/> + - + android:text="친구목록에서 친구를 선택하여 참여자를 추가해주세요."/> - + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_album.xml b/app/src/main/res/layout/fragment_album.xml new file mode 100644 index 0000000..305b63a --- /dev/null +++ b/app/src/main/res/layout/fragment_album.xml @@ -0,0 +1,24 @@ + + + + + + diff --git a/app/src/main/res/layout/fragment_calendar.xml b/app/src/main/res/layout/fragment_calendar.xml index 61796f9..a12e0bf 100644 --- a/app/src/main/res/layout/fragment_calendar.xml +++ b/app/src/main/res/layout/fragment_calendar.xml @@ -1,30 +1,61 @@ - + android:layout_height="match_parent"> + app:layout_constraintEnd_toEndOf="parent" + android:layout_marginTop="16dp" + tools:ignore="MissingConstraints" /> - + +