├── app
├── .gitignore
├── src
│ ├── main
│ │ ├── res
│ │ │ ├── drawable
│ │ │ │ ├── van.jpg
│ │ │ │ ├── instagramlogo.png
│ │ │ │ ├── ic_home.xml
│ │ │ │ ├── ic_check.xml
│ │ │ │ ├── ic_home_black_24dp.xml
│ │ │ │ ├── login_input_bg.xml
│ │ │ │ ├── ic_close.xml
│ │ │ │ ├── bg_map.xml
│ │ │ │ ├── border_bg.xml
│ │ │ │ ├── dialog_bg.xml
│ │ │ │ ├── ic_comment.xml
│ │ │ │ ├── ic_share.xml
│ │ │ │ ├── ic_baseline_close_24.xml
│ │ │ │ ├── ic_dashboard_black_24dp.xml
│ │ │ │ ├── ic_profile.xml
│ │ │ │ ├── button_bg.xml
│ │ │ │ ├── ic_fav.xml
│ │ │ │ ├── ic_exit.xml
│ │ │ │ ├── ic_fav_red.xml
│ │ │ │ ├── ic_more.xml
│ │ │ │ ├── button_border.xml
│ │ │ │ ├── ic_notifications_black_24dp.xml
│ │ │ │ ├── ic_search.xml
│ │ │ │ ├── ic_baseline_account_circle_24.xml
│ │ │ │ ├── ic_contacts.xml
│ │ │ │ ├── ic_like.xml
│ │ │ │ ├── login_btn.xml
│ │ │ │ ├── ic_baseline_share_24.xml
│ │ │ │ └── ic_launcher_background.xml
│ │ │ ├── van_picture.jpg
│ │ │ ├── mipmap-hdpi
│ │ │ │ ├── ic_launcher.webp
│ │ │ │ └── ic_launcher_round.webp
│ │ │ ├── mipmap-mdpi
│ │ │ │ ├── ic_launcher.webp
│ │ │ │ └── ic_launcher_round.webp
│ │ │ ├── values
│ │ │ │ ├── dimen.xml
│ │ │ │ ├── dimens.xml
│ │ │ │ ├── strings.xml
│ │ │ │ ├── colors.xml
│ │ │ │ ├── themes.xml
│ │ │ │ └── styles.xml
│ │ │ ├── mipmap-xhdpi
│ │ │ │ ├── ic_launcher.webp
│ │ │ │ └── ic_launcher_round.webp
│ │ │ ├── mipmap-xxhdpi
│ │ │ │ ├── ic_launcher.webp
│ │ │ │ └── ic_launcher_round.webp
│ │ │ ├── mipmap-xxxhdpi
│ │ │ │ ├── ic_launcher.webp
│ │ │ │ └── ic_launcher_round.webp
│ │ │ ├── color
│ │ │ │ └── login_btn_color.xml
│ │ │ ├── mipmap-anydpi-v26
│ │ │ │ ├── ic_launcher.xml
│ │ │ │ └── ic_launcher_round.xml
│ │ │ ├── layout
│ │ │ │ ├── activity_second_host.xml
│ │ │ │ ├── my_posts_item.xml
│ │ │ │ ├── fragment_contacts.xml
│ │ │ │ ├── stories_recycler_row.xml
│ │ │ │ ├── bottom_navigation_bar.xml
│ │ │ │ ├── dialog_password.xml
│ │ │ │ ├── activity_main.xml
│ │ │ │ ├── top_bar.xml
│ │ │ │ ├── contacts_item.xml
│ │ │ │ ├── following_item.xml
│ │ │ │ ├── fragment_followers.xml
│ │ │ │ ├── followers_item.xml
│ │ │ │ ├── fragment_get_user_location.xml
│ │ │ │ ├── dialog_view.xml
│ │ │ │ ├── fragment_home.xml
│ │ │ │ ├── likes_item.xml
│ │ │ │ ├── fragment_following.xml
│ │ │ │ ├── fragment_likes.xml
│ │ │ │ ├── add_friends_item.xml
│ │ │ │ ├── fragment_search.xml
│ │ │ │ ├── fragment_login.xml
│ │ │ │ ├── feed_recycler_row.xml
│ │ │ │ ├── fragment_share.xml
│ │ │ │ ├── fragment_register.xml
│ │ │ │ └── fragment_edit.xml
│ │ │ ├── animator
│ │ │ │ └── login_btn_animator.xml
│ │ │ ├── menu
│ │ │ │ └── menu.xml
│ │ │ ├── values-night
│ │ │ │ └── themes.xml
│ │ │ ├── drawable-v24
│ │ │ │ └── ic_launcher_foreground.xml
│ │ │ └── navigation
│ │ │ │ └── graph.xml
│ │ ├── java
│ │ │ └── com
│ │ │ │ └── suveybesena
│ │ │ │ └── instagramclone
│ │ │ │ ├── model
│ │ │ │ ├── LikesModel.kt
│ │ │ │ ├── FollowModel.kt
│ │ │ │ ├── MyPostsModel.kt
│ │ │ │ ├── FollowersModel.kt
│ │ │ │ ├── User.kt
│ │ │ │ ├── RegisterModel.kt
│ │ │ │ └── Firebase.kt
│ │ │ │ ├── presentation
│ │ │ │ ├── likes
│ │ │ │ │ ├── LikesInterface.kt
│ │ │ │ │ ├── UnlikeInterface.kt
│ │ │ │ │ ├── SwipeGesture.kt
│ │ │ │ │ ├── LikesAdapter.kt
│ │ │ │ │ ├── LikesViewModel.kt
│ │ │ │ │ └── LikesFragment.kt
│ │ │ │ ├── search
│ │ │ │ │ ├── FollowInterface.kt
│ │ │ │ │ ├── UnfollowInterface.kt
│ │ │ │ │ ├── GetUserInterface.kt
│ │ │ │ │ ├── FriendsAdapter.kt
│ │ │ │ │ └── SearchFragment.kt
│ │ │ │ ├── home
│ │ │ │ │ ├── FeedLikesInterface.kt
│ │ │ │ │ ├── FeedAdapter.kt
│ │ │ │ │ └── HomeFragment.kt
│ │ │ │ ├── profile
│ │ │ │ │ ├── MyPostsAdapter.kt
│ │ │ │ │ ├── ProfileViewModel.kt
│ │ │ │ │ └── ProfileFragment.kt
│ │ │ │ ├── following
│ │ │ │ │ ├── FollowingAdapter.kt
│ │ │ │ │ ├── FollowingFragment.kt
│ │ │ │ │ └── FollowingViewModel.kt
│ │ │ │ ├── followers
│ │ │ │ │ ├── FollowersAdapter.kt
│ │ │ │ │ ├── FollowersFragment.kt
│ │ │ │ │ └── FollowerViewModel.kt
│ │ │ │ ├── contacts
│ │ │ │ │ ├── ContactsAdapter.kt
│ │ │ │ │ └── ContactsFragment.kt
│ │ │ │ ├── login
│ │ │ │ │ ├── LoginViewModel.kt
│ │ │ │ │ └── LoginFragment.kt
│ │ │ │ ├── edit
│ │ │ │ │ ├── EditViewModel.kt
│ │ │ │ │ └── EditFragment.kt
│ │ │ │ ├── map
│ │ │ │ │ └── GetUserLocationFragment.kt
│ │ │ │ ├── share
│ │ │ │ │ ├── ShareViewModel.kt
│ │ │ │ │ └── ShareFragment.kt
│ │ │ │ └── register
│ │ │ │ │ ├── RegisterViewModel.kt
│ │ │ │ │ └── RegisterFragment.kt
│ │ │ │ ├── di
│ │ │ │ ├── MyApplication.kt
│ │ │ │ └── FirebaseModule.kt
│ │ │ │ ├── utils
│ │ │ │ └── extensions
│ │ │ │ │ ├── ImageViewExt.kt
│ │ │ │ │ └── Resources.kt
│ │ │ │ └── MainActivity.kt
│ │ └── AndroidManifest.xml
│ ├── test
│ │ └── java
│ │ │ └── com
│ │ │ └── suveybesena
│ │ │ └── instagramclone
│ │ │ └── ExampleUnitTest.kt
│ └── androidTest
│ │ └── java
│ │ └── com
│ │ └── suveybesena
│ │ └── instagramclone
│ │ └── ExampleInstrumentedTest.kt
├── proguard-rules.pro
├── google-services.json
└── build.gradle
├── .idea
├── .gitignore
├── compiler.xml
├── vcs.xml
├── render.experimental.xml
└── gradle.xml
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── .gitignore
├── settings.gradle
├── gradle.properties
├── README.md
├── gradlew.bat
└── gradlew
/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
--------------------------------------------------------------------------------
/.idea/.gitignore:
--------------------------------------------------------------------------------
1 | # Default ignored files
2 | /shelf/
3 | /workspace.xml
4 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/van.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/suveybesena/InstagramClone/HEAD/app/src/main/res/drawable/van.jpg
--------------------------------------------------------------------------------
/app/src/main/res/van_picture.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/suveybesena/InstagramClone/HEAD/app/src/main/res/van_picture.jpg
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/suveybesena/InstagramClone/HEAD/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/app/src/main/res/drawable/instagramlogo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/suveybesena/InstagramClone/HEAD/app/src/main/res/drawable/instagramlogo.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/suveybesena/InstagramClone/HEAD/app/src/main/res/mipmap-hdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/suveybesena/InstagramClone/HEAD/app/src/main/res/mipmap-mdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/app/src/main/res/values/dimen.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 29f
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/suveybesena/InstagramClone/HEAD/app/src/main/res/mipmap-xhdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/suveybesena/InstagramClone/HEAD/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/suveybesena/InstagramClone/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/suveybesena/InstagramClone/HEAD/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/suveybesena/InstagramClone/HEAD/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/suveybesena/InstagramClone/HEAD/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/suveybesena/InstagramClone/HEAD/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/suveybesena/InstagramClone/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/app/src/main/res/values/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 | 16dp
3 | 16dp
4 |
--------------------------------------------------------------------------------
/.idea/compiler.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/java/com/suveybesena/instagramclone/model/LikesModel.kt:
--------------------------------------------------------------------------------
1 | package com.suveybesena.instagramclone.model
2 |
3 | data class LikesModel (
4 | var postImage : String
5 |
6 |
7 | ){
8 | }
--------------------------------------------------------------------------------
/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/java/com/suveybesena/instagramclone/model/FollowModel.kt:
--------------------------------------------------------------------------------
1 | package com.suveybesena.instagramclone.model
2 |
3 | data class FollowModel(
4 | var name: String?,
5 | var image: String?
6 | )
7 |
--------------------------------------------------------------------------------
/app/src/main/java/com/suveybesena/instagramclone/model/MyPostsModel.kt:
--------------------------------------------------------------------------------
1 | package com.suveybesena.instagramclone.model
2 |
3 | class MyPostsModel
4 | (
5 | var image: String,
6 | var id: String
7 | ) {
8 | }
--------------------------------------------------------------------------------
/.idea/render.experimental.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/java/com/suveybesena/instagramclone/model/FollowersModel.kt:
--------------------------------------------------------------------------------
1 | package com.suveybesena.instagramclone.model
2 |
3 | data class FollowersModel(
4 | var name: String?,
5 | var image: String?
6 | )
7 |
8 |
--------------------------------------------------------------------------------
/app/src/main/java/com/suveybesena/instagramclone/model/User.kt:
--------------------------------------------------------------------------------
1 | package com.suveybesena.instagramclone.model
2 |
3 | data class User(
4 | var username: String?,
5 | var image: String?,
6 | var uid: String?
7 | )
--------------------------------------------------------------------------------
/app/src/main/java/com/suveybesena/instagramclone/presentation/likes/LikesInterface.kt:
--------------------------------------------------------------------------------
1 | package com.suveybesena.instagramclone.presentation.likes
2 |
3 |
4 |
5 | interface LikesInterface {
6 | fun likeItemClick( image : String)
7 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/suveybesena/instagramclone/presentation/likes/UnlikeInterface.kt:
--------------------------------------------------------------------------------
1 | package com.suveybesena.instagramclone.presentation.likes
2 |
3 | interface UnlikeInterface {
4 |
5 | fun unlikeItemClick( image : String)
6 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/suveybesena/instagramclone/presentation/search/FollowInterface.kt:
--------------------------------------------------------------------------------
1 | package com.suveybesena.instagramclone.presentation.search
2 |
3 | interface FollowInterface {
4 |
5 | fun itemOnClick(userId : String)
6 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/suveybesena/instagramclone/presentation/search/UnfollowInterface.kt:
--------------------------------------------------------------------------------
1 | package com.suveybesena.instagramclone.presentation.search
2 |
3 | interface UnfollowInterface {
4 |
5 | fun onItemClick(userId : String)
6 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/suveybesena/instagramclone/di/MyApplication.kt:
--------------------------------------------------------------------------------
1 | package com.suveybesena.instagramclone.di
2 |
3 | import android.app.Application
4 | import dagger.hilt.android.HiltAndroidApp
5 |
6 | @HiltAndroidApp
7 | class MyApplication : Application() {
8 | }
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Mon Feb 14 20:17:03 TRT 2022
2 | distributionBase=GRADLE_USER_HOME
3 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-bin.zip
4 | distributionPath=wrapper/dists
5 | zipStorePath=wrapper/dists
6 | zipStoreBase=GRADLE_USER_HOME
7 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.iml
2 | .gradle
3 | /local.properties
4 | /.idea/caches
5 | /.idea/libraries
6 | /.idea/modules.xml
7 | /.idea/workspace.xml
8 | /.idea/navEditor.xml
9 | /.idea/assetWizardSettings.xml
10 | .DS_Store
11 | /build
12 | /captures
13 | .externalNativeBuild
14 | .cxx
15 | local.properties
16 |
--------------------------------------------------------------------------------
/app/src/main/java/com/suveybesena/instagramclone/presentation/home/FeedLikesInterface.kt:
--------------------------------------------------------------------------------
1 | package com.suveybesena.instagramclone.presentation.home
2 |
3 | import android.widget.ImageView
4 |
5 | interface FeedLikesInterface {
6 |
7 | fun getLikes (image : String, like :ImageView, unlike :ImageView)
8 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/suveybesena/instagramclone/presentation/search/GetUserInterface.kt:
--------------------------------------------------------------------------------
1 | package com.suveybesena.instagramclone.presentation.search
2 |
3 | import android.widget.Button
4 |
5 | interface GetUserInterface {
6 |
7 | fun getUserList (userId : String, follow : Button, unfollow : Button)
8 | }
--------------------------------------------------------------------------------
/app/src/main/res/color/login_btn_color.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_home.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_check.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/java/com/suveybesena/instagramclone/model/RegisterModel.kt:
--------------------------------------------------------------------------------
1 | package com.suveybesena.instagramclone.model
2 |
3 | import android.net.Uri
4 | import android.view.View
5 |
6 | data class RegisterModel (
7 | var mail: String,
8 | var password: String,
9 | var pickedImage: Uri,
10 | var view: View,
11 | var username: String,
12 | var name : String
13 | ){
14 | }
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_home_black_24dp.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/login_input_bg.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | -
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | pluginManagement {
2 | repositories {
3 | gradlePluginPortal()
4 | google()
5 | mavenCentral()
6 | }
7 | }
8 | dependencyResolutionManagement {
9 | repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
10 | repositories {
11 | google()
12 | mavenCentral()
13 | }
14 | }
15 | rootProject.name = "InstagramClone"
16 | include ':app'
17 |
--------------------------------------------------------------------------------
/app/src/main/java/com/suveybesena/instagramclone/model/Firebase.kt:
--------------------------------------------------------------------------------
1 | package com.suveybesena.instagramclone.model
2 |
3 | data class Firebase (var image: String,
4 | var comment : String,
5 | var user : String,
6 | var userImage: String,
7 | var location : String,
8 | var allInOne : HashMap) {
9 |
10 | }
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_close.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/java/com/suveybesena/instagramclone/utils/extensions/ImageViewExt.kt:
--------------------------------------------------------------------------------
1 | package com.suveybesena.instagramclone.utils.extensions
2 |
3 | import android.widget.ImageView
4 | import com.bumptech.glide.Glide
5 |
6 | fun ImageView.downloadImage(imageUrl:String){
7 | try {
8 | Glide.with(this.context).load(imageUrl).into(this)
9 | } catch (e:Exception){
10 | println(e.localizedMessage)
11 | }
12 | }
--------------------------------------------------------------------------------
/app/src/main/res/drawable/bg_map.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | -
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/border_bg.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | -
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/dialog_bg.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | -
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_comment.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_share.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_baseline_close_24.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_dashboard_black_24dp.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/java/com/suveybesena/instagramclone/utils/extensions/Resources.kt:
--------------------------------------------------------------------------------
1 | package com.suveybesena.instagramclone.utils.extensions
2 |
3 | sealed class Resources (
4 | val data :T? = null,
5 | val message : String? =null
6 | ) {
7 | class Success (data: T) :Resources(data)
8 | class error(message: String, data: T? = null) : Resources(data, message)
9 | class loading : Resources()
10 |
11 | }
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_profile.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/button_bg.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 | -
5 |
6 |
7 |
8 |
9 |
10 | >
11 |
--------------------------------------------------------------------------------
/app/src/test/java/com/suveybesena/instagramclone/ExampleUnitTest.kt:
--------------------------------------------------------------------------------
1 | package com.suveybesena.instagramclone
2 |
3 | import org.junit.Test
4 |
5 | import org.junit.Assert.*
6 |
7 | /**
8 | * Example local unit test, which will execute on the development machine (host).
9 | *
10 | * See [testing documentation](http://d.android.com/tools/testing).
11 | */
12 | class ExampleUnitTest {
13 | @Test
14 | fun addition_isCorrect() {
15 | assertEquals(4, 2 + 2)
16 | }
17 | }
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_fav.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_exit.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_fav_red.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_second_host.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_more.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/button_border.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 | -
5 |
7 |
9 |
12 |
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_notifications_black_24dp.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_search.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_baseline_account_circle_24.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | InstagramClone
3 |
4 | Hello blank fragment
5 | suveybesena
6 | MainActivity2
7 | Home
8 | Dashboard
9 | Notifications
10 | MapsActivity
11 |
12 |
--------------------------------------------------------------------------------
/app/src/main/res/animator/login_btn_animator.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | -
4 |
8 |
9 |
10 | -
11 |
15 |
16 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/my_posts_item.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
15 |
--------------------------------------------------------------------------------
/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #FFBB86FC
4 | #FF6200EE
5 | #FF3700B3
6 | #FF03DAC5
7 | #FF018786
8 | #FF000000
9 | #FFFFFFFF
10 | #E7E7E7
11 | #1767F1
12 | #7C7C7C
13 | #A11B1B
14 |
15 |
16 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_contacts.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_like.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/fragment_contacts.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
9 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/login_btn.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 | -
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 | -
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_baseline_share_24.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/java/com/suveybesena/instagramclone/presentation/likes/SwipeGesture.kt:
--------------------------------------------------------------------------------
1 | package com.suveybesena.instagramclone.presentation.likes
2 |
3 |
4 | import androidx.recyclerview.widget.ItemTouchHelper
5 | import androidx.recyclerview.widget.RecyclerView
6 |
7 |
8 | abstract class SwipeToDeleteCallBack :ItemTouchHelper.Callback(){
9 | override fun getMovementFlags(
10 | recyclerView: RecyclerView,
11 | viewHolder: RecyclerView.ViewHolder
12 | ): Int {
13 | val swipeFlag = ItemTouchHelper.LEFT
14 | return makeMovementFlags(0, swipeFlag)
15 | }
16 |
17 | override fun onMove(
18 | recyclerView: RecyclerView,
19 | viewHolder: RecyclerView.ViewHolder,
20 | target: RecyclerView.ViewHolder
21 | ): Boolean {
22 | return false
23 | }
24 | }
--------------------------------------------------------------------------------
/.idea/gradle.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/app/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
--------------------------------------------------------------------------------
/app/src/androidTest/java/com/suveybesena/instagramclone/ExampleInstrumentedTest.kt:
--------------------------------------------------------------------------------
1 | package com.suveybesena.instagramclone
2 |
3 | import androidx.test.platform.app.InstrumentationRegistry
4 | import androidx.test.ext.junit.runners.AndroidJUnit4
5 |
6 | import org.junit.Test
7 | import org.junit.runner.RunWith
8 |
9 | import org.junit.Assert.*
10 |
11 | /**
12 | * Instrumented test, which will execute on an Android device.
13 | *
14 | * See [testing documentation](http://d.android.com/tools/testing).
15 | */
16 | @RunWith(AndroidJUnit4::class)
17 | class ExampleInstrumentedTest {
18 | @Test
19 | fun useAppContext() {
20 | // Context of the app under test.
21 | val appContext = InstrumentationRegistry.getInstrumentation().targetContext
22 | assertEquals("com.suveybesena.instagramclone", appContext.packageName)
23 | }
24 | }
--------------------------------------------------------------------------------
/app/src/main/res/layout/stories_recycler_row.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
11 |
12 |
20 |
21 |
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/bottom_navigation_bar.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
19 |
20 |
--------------------------------------------------------------------------------
/app/src/main/res/values/themes.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
16 |
--------------------------------------------------------------------------------
/app/src/main/res/menu/menu.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
8 |
13 |
18 |
23 |
28 |
--------------------------------------------------------------------------------
/app/src/main/res/values-night/themes.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
16 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/dialog_password.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
19 |
20 |
--------------------------------------------------------------------------------
/app/src/main/java/com/suveybesena/instagramclone/di/FirebaseModule.kt:
--------------------------------------------------------------------------------
1 | package com.suveybesena.instagramclone.di
2 |
3 | import com.google.firebase.auth.FirebaseAuth
4 | import com.google.firebase.firestore.FirebaseFirestore
5 | import com.google.firebase.storage.FirebaseStorage
6 | import dagger.Module
7 | import dagger.Provides
8 | import dagger.hilt.InstallIn
9 | import dagger.hilt.components.SingletonComponent
10 | import javax.inject.Singleton
11 |
12 | @Module
13 | @InstallIn(SingletonComponent::class)
14 |
15 | object FirebaseModule {
16 |
17 | @Provides
18 | @Singleton
19 | fun provideFirebaseAuthInstance() : FirebaseAuth = FirebaseAuth.getInstance()
20 |
21 | @Provides
22 | @Singleton
23 | fun provideFirebaseFirestoreInstance() : FirebaseFirestore = FirebaseFirestore.getInstance()
24 |
25 | @Provides
26 | @Singleton
27 | fun provideFirebaseStorageInstance() : FirebaseStorage= FirebaseStorage.getInstance()
28 |
29 |
30 |
31 | }
--------------------------------------------------------------------------------
/app/google-services.json:
--------------------------------------------------------------------------------
1 | {
2 | "project_info": {
3 | "project_number": "797543189652",
4 | "project_id": "instagramclone-1b368",
5 | "storage_bucket": "instagramclone-1b368.appspot.com"
6 | },
7 | "client": [
8 | {
9 | "client_info": {
10 | "mobilesdk_app_id": "1:797543189652:android:e0db1639087bc2ae4cf881",
11 | "android_client_info": {
12 | "package_name": "com.suveybesena.instagramclone"
13 | }
14 | },
15 | "oauth_client": [
16 | {
17 | "client_id": "797543189652-5v92v3q52u4coh5n167u0c7ddfqkj281.apps.googleusercontent.com",
18 | "client_type": 3
19 | }
20 | ],
21 | "api_key": [
22 | {
23 | "current_key": "AIzaSyBef6JdgQrKRlwOvajDilt9HymY9N2ZoZA"
24 | }
25 | ],
26 | "services": {
27 | "appinvite_service": {
28 | "other_platform_oauth_client": [
29 | {
30 | "client_id": "797543189652-5v92v3q52u4coh5n167u0c7ddfqkj281.apps.googleusercontent.com",
31 | "client_type": 3
32 | }
33 | ]
34 | }
35 | }
36 | }
37 | ],
38 | "configuration_version": "1"
39 | }
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
10 |
11 |
12 |
24 |
25 |
26 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/app/src/main/java/com/suveybesena/instagramclone/presentation/profile/MyPostsAdapter.kt:
--------------------------------------------------------------------------------
1 | package com.suveybesena.instagramclone.presentation.profile
2 |
3 | import android.view.LayoutInflater
4 | import android.view.View
5 | import android.view.ViewGroup
6 | import androidx.recyclerview.widget.RecyclerView
7 | import com.bumptech.glide.Glide
8 | import com.suveybesena.instagramclone.R
9 | import com.suveybesena.instagramclone.model.MyPostsModel
10 | import kotlinx.android.synthetic.main.my_posts_item.view.*
11 |
12 |
13 | class MyPostsAdapter(var myPostList: List) :
14 | RecyclerView.Adapter() {
15 | class MyPostsVH(itemView: View) : RecyclerView.ViewHolder(itemView) {
16 | }
17 |
18 | override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyPostsVH {
19 | val inflater = LayoutInflater.from(parent.context)
20 | val view = inflater.inflate(R.layout.my_posts_item, parent, false)
21 | return MyPostsVH(view)
22 | }
23 |
24 | override fun onBindViewHolder(holder: MyPostsVH, position: Int) {
25 |
26 | Glide.with(holder.itemView).load(myPostList[position].image)
27 | .into(holder.itemView.iw_my_posts)
28 |
29 |
30 | }
31 |
32 | override fun getItemCount(): Int {
33 | return myPostList.size
34 | }
35 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/suveybesena/instagramclone/presentation/likes/LikesAdapter.kt:
--------------------------------------------------------------------------------
1 | package com.suveybesena.instagramclone.presentation.likes
2 |
3 | import android.view.LayoutInflater
4 | import android.view.View
5 | import android.view.ViewGroup
6 | import androidx.recyclerview.widget.RecyclerView
7 | import com.bumptech.glide.Glide
8 | import com.suveybesena.instagramclone.R
9 | import com.suveybesena.instagramclone.model.LikesModel
10 | import com.suveybesena.instagramclone.utils.extensions.downloadImage
11 | import kotlinx.android.synthetic.main.likes_item.view.*
12 |
13 | class LikesAdapter(
14 | var likesList: List,
15 | ): RecyclerView.Adapter() {
16 | class LikesVH (itemView : View) : RecyclerView.ViewHolder(itemView) {
17 | }
18 |
19 | override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): LikesVH {
20 | val inflater = LayoutInflater.from(parent.context)
21 | val view = inflater.inflate(R.layout.likes_item, parent, false)
22 | return LikesVH(view)
23 | }
24 |
25 | override fun onBindViewHolder(holder: LikesVH, position: Int) {
26 |
27 | holder.itemView.iw_likes.downloadImage(likesList[position].postImage)
28 |
29 |
30 | }
31 |
32 | override fun getItemCount(): Int {
33 | return likesList.size
34 | }
35 |
36 |
37 |
38 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/suveybesena/instagramclone/presentation/following/FollowingAdapter.kt:
--------------------------------------------------------------------------------
1 | package com.suveybesena.instagramclone.presentation.following
2 |
3 | import android.view.LayoutInflater
4 | import android.view.View
5 | import android.view.ViewGroup
6 | import androidx.recyclerview.widget.RecyclerView
7 | import com.bumptech.glide.Glide
8 | import com.suveybesena.instagramclone.R
9 | import com.suveybesena.instagramclone.model.FollowModel
10 | import com.suveybesena.instagramclone.utils.extensions.downloadImage
11 | import kotlinx.android.synthetic.main.add_friends_item.view.*
12 |
13 | class FollowingAdapter(var list: List) : RecyclerView.Adapter() {
14 | class VH(itemView: View) : RecyclerView.ViewHolder(itemView) {
15 |
16 | }
17 |
18 | override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): VH {
19 | val view =
20 | LayoutInflater.from(parent.context).inflate(R.layout.following_item, parent, false)
21 | return VH(view)
22 | }
23 |
24 | override fun onBindViewHolder(holder: VH, position: Int) {
25 | val user = list[position]
26 | holder.itemView.apply {
27 | tw_name.text = user.name
28 | user.image?.let { iw_user.downloadImage(it) }
29 | }
30 |
31 | }
32 |
33 | override fun getItemCount(): Int {
34 | return list.size
35 | }
36 | }
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 | # IDE (e.g. Android Studio) users:
3 | # Gradle settings configured through the IDE *will override*
4 | # any settings specified in this file.
5 | # For more details on how to configure your build environment visit
6 | # http://www.gradle.org/docs/current/userguide/build_environment.html
7 | # Specifies the JVM arguments used for the daemon process.
8 | # The setting is particularly useful for tweaking memory settings.
9 | org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
10 | # When configured, Gradle will run in incubating parallel mode.
11 | # This option should only be used with decoupled projects. More details, visit
12 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
13 | # org.gradle.parallel=true
14 | # AndroidX package structure to make it clearer which packages are bundled with the
15 | # Android operating system, and which are packaged with your app"s APK
16 | # https://developer.android.com/topic/libraries/support-library/androidx-rn
17 | android.useAndroidX=true
18 | # Kotlin code style for this project: "official" or "obsolete":
19 | kotlin.code.style=official
20 | # Enables namespacing of each library's R class so that its R class includes only the
21 | # resources declared in the library itself and none from the library's dependencies,
22 | # thereby reducing the size of the R class for that library
23 | android.nonTransitiveRClass=true
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | InstagramClone
2 |
3 | In this project, I made an Instagram clone. I used firebase auth, firestore database and storage, I wrote it according to mvvm architecture.
4 |
5 |
6 | Libraries Used
7 |
8 | Firebase Authentication
9 | Firebase Storage
10 | Firebase Firestore
11 | Viewbinding
12 | Hilt
13 | Bottom navigation bar
14 | Glide
15 | Google Maps SDK
16 |
17 | Architecture
18 |
19 | ViewModel
20 | LiveData
21 | Navigation
22 |
23 | App Images
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
--------------------------------------------------------------------------------
/app/src/main/java/com/suveybesena/instagramclone/presentation/followers/FollowersAdapter.kt:
--------------------------------------------------------------------------------
1 | package com.suveybesena.instagramclone.presentation.followers
2 |
3 | import android.view.LayoutInflater
4 | import android.view.View
5 | import android.view.ViewGroup
6 | import androidx.recyclerview.widget.RecyclerView
7 | import com.suveybesena.instagramclone.R
8 | import com.suveybesena.instagramclone.model.FollowersModel
9 | import com.suveybesena.instagramclone.utils.extensions.downloadImage
10 | import kotlinx.android.synthetic.main.add_friends_item.view.*
11 |
12 | class FollowersAdapter(var list: List) :
13 | RecyclerView.Adapter() {
14 | class FollowersVH(itemView: View) : RecyclerView.ViewHolder(itemView) {
15 |
16 | }
17 |
18 | override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): FollowersVH {
19 | val inflater = LayoutInflater.from(parent.context)
20 | val view = inflater.inflate(R.layout.followers_item, parent, false)
21 | return FollowersVH(view)
22 | }
23 |
24 | override fun onBindViewHolder(holder: FollowersVH, position: Int) {
25 | val user = list[position]
26 | holder.itemView.apply {
27 | tw_name.text = user.name
28 | user.image.toString()?.let { iw_user.downloadImage(it) }
29 | }
30 |
31 | }
32 |
33 | override fun getItemCount(): Int {
34 | return list.size
35 | }
36 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/suveybesena/instagramclone/presentation/contacts/ContactsAdapter.kt:
--------------------------------------------------------------------------------
1 | package com.suveybesena.instagramclone.presentation.contacts
2 |
3 | import android.view.LayoutInflater
4 | import android.view.View
5 | import android.view.ViewGroup
6 | import androidx.recyclerview.widget.RecyclerView
7 | import com.suveybesena.instagramclone.R
8 | import kotlinx.android.synthetic.main.add_friends_item.view.*
9 |
10 | class ContactsAdapter ( var list : ArrayList) :RecyclerView.Adapter() {
11 | class ContactsVH(itemView : View) :RecyclerView.ViewHolder(itemView){
12 | }
13 | override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ContactsVH {
14 | val inflater = LayoutInflater.from(parent.context)
15 | val view = inflater.inflate(R.layout.contacts_item, parent, false)
16 | return ContactsVH(view)
17 | }
18 |
19 | override fun onBindViewHolder(holder: ContactsVH, position: Int) {
20 | val contacts = list[position]
21 |
22 | holder.itemView.apply {
23 | tw_name.text = contacts
24 | setOnClickListener {
25 | onItemClickListener?.let { it(contacts) }
26 | }
27 | }
28 | }
29 |
30 | override fun getItemCount(): Int {
31 | return list.size
32 | }
33 |
34 | private var onItemClickListener: ((String) -> Unit)? = null
35 | fun setOnItemClickListener(listener: (String) -> Unit) {
36 | onItemClickListener = listener
37 | }
38 |
39 | }
--------------------------------------------------------------------------------
/app/src/main/res/layout/top_bar.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
13 |
17 |
25 |
26 |
35 |
36 |
37 |
38 |
39 |
--------------------------------------------------------------------------------
/app/src/main/java/com/suveybesena/instagramclone/presentation/login/LoginViewModel.kt:
--------------------------------------------------------------------------------
1 | package com.suveybesena.instagramclone.presentation.login
2 |
3 |
4 | import androidx.lifecycle.MutableLiveData
5 | import androidx.lifecycle.ViewModel
6 | import com.google.firebase.auth.FirebaseAuth
7 | import com.google.firebase.firestore.FirebaseFirestore
8 | import com.google.firebase.storage.FirebaseStorage
9 | import com.suveybesena.instagramclone.di.FirebaseModule
10 | import dagger.hilt.android.lifecycle.HiltViewModel
11 | import javax.inject.Inject
12 |
13 | @HiltViewModel
14 | class LoginViewModel @Inject
15 | constructor(
16 | var authInstance: FirebaseAuth
17 | ) : ViewModel() {
18 |
19 | private val loadingState = MutableLiveData()
20 | val _loadingState = loadingState
21 | private val errorState = MutableLiveData()
22 | val _errorState = errorState
23 |
24 |
25 | fun login(email: String, password: String) {
26 | loadingState.value = true
27 | if (validate(email, password)) {
28 | authInstance.signInWithEmailAndPassword(email, password)
29 | .addOnSuccessListener {
30 | loadingState.value = false
31 | }
32 | .addOnFailureListener { error ->
33 | loadingState.value = false
34 | errorState.value = error.localizedMessage
35 | }
36 | }
37 | }
38 |
39 | private fun validate(email: String, password: String): Boolean {
40 | email.isNotEmpty() && password.isNotEmpty()
41 | return true
42 | }
43 |
44 | }
45 |
46 |
47 |
48 |
49 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/contacts_item.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
16 |
17 |
18 |
27 |
28 |
29 |
37 |
38 |
39 |
40 |
41 |
42 |
--------------------------------------------------------------------------------
/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
7 |
8 |
9 |
13 |
14 |
19 |
20 |
26 |
27 |
33 |
34 |
35 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/following_item.xml:
--------------------------------------------------------------------------------
1 |
2 |
10 |
11 |
17 |
23 |
24 |
25 |
26 |
35 |
36 |
44 |
45 |
46 |
47 |
48 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/fragment_followers.xml:
--------------------------------------------------------------------------------
1 |
2 |
10 |
15 |
16 |
22 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
42 |
--------------------------------------------------------------------------------
/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
22 |
23 |
26 |
27 |
28 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/followers_item.xml:
--------------------------------------------------------------------------------
1 |
2 |
10 |
16 |
22 |
23 |
32 |
33 |
41 |
42 |
43 |
44 |
45 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/fragment_get_user_location.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
15 |
27 |
28 |
39 |
40 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable-v24/ic_launcher_foreground.xml:
--------------------------------------------------------------------------------
1 |
7 |
8 |
9 |
15 |
18 |
21 |
22 |
23 |
24 |
30 |
--------------------------------------------------------------------------------
/app/src/main/java/com/suveybesena/instagramclone/MainActivity.kt:
--------------------------------------------------------------------------------
1 | package com.suveybesena.instagramclone
2 |
3 |
4 | import androidx.appcompat.app.AppCompatActivity
5 | import android.os.Bundle
6 | import android.view.Menu
7 | import android.view.MenuItem
8 | import androidx.navigation.NavController
9 | import androidx.navigation.Navigation
10 | import androidx.navigation.findNavController
11 | import androidx.navigation.fragment.NavHostFragment
12 | import androidx.navigation.ui.AppBarConfiguration
13 | import androidx.navigation.ui.setupActionBarWithNavController
14 | import androidx.navigation.ui.setupWithNavController
15 | import com.google.android.material.bottomnavigation.BottomNavigationView
16 | import com.google.firebase.auth.FirebaseAuth
17 | import dagger.hilt.android.AndroidEntryPoint
18 | import kotlinx.android.synthetic.main.top_bar.*
19 | @AndroidEntryPoint
20 | class MainActivity : AppCompatActivity() {
21 |
22 | override fun onCreate(savedInstanceState: Bundle?) {
23 | super.onCreate(savedInstanceState)
24 | setContentView(R.layout.activity_main)
25 |
26 | this.supportActionBar?.hide()
27 |
28 | val navHostFragment =
29 | supportFragmentManager.findFragmentById(R.id.nav_host_fragment) as NavHostFragment
30 | val navController: NavController = navHostFragment.navController
31 |
32 | val bottomNavigationView = findViewById(R.id.bottom_navigation_view)
33 | val appBarConfiguration = AppBarConfiguration(
34 | setOf(
35 | R.id.shareFragment, R.id.profileFragment,R.id.searchFragment, R.id.homeFragment, R.id.likesFragment
36 | )
37 | )
38 | setupActionBarWithNavController(navController, appBarConfiguration)
39 | bottomNavigationView.setupWithNavController(navController)
40 |
41 |
42 |
43 |
44 |
45 | }
46 |
47 |
48 |
49 |
50 |
51 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/suveybesena/instagramclone/presentation/followers/FollowersFragment.kt:
--------------------------------------------------------------------------------
1 | package com.suveybesena.instagramclone.presentation.followers
2 |
3 | import android.os.Bundle
4 | import androidx.fragment.app.Fragment
5 | import android.view.LayoutInflater
6 | import android.view.View
7 | import android.view.ViewGroup
8 | import androidx.fragment.app.viewModels
9 | import com.google.android.material.snackbar.Snackbar
10 | import com.suveybesena.instagramclone.databinding.FragmentFollowersBinding
11 | import com.suveybesena.instagramclone.model.FollowersModel
12 | import dagger.hilt.android.AndroidEntryPoint
13 |
14 | @AndroidEntryPoint
15 | class FollowersFragment : Fragment() {
16 | private val viewModel: FollowerViewModel by viewModels()
17 | private lateinit var followersAdapter: FollowersAdapter
18 | private lateinit var binding: FragmentFollowersBinding
19 |
20 |
21 | override fun onCreate(savedInstanceState: Bundle?) {
22 | super.onCreate(savedInstanceState)
23 | }
24 |
25 | override fun onCreateView(
26 | inflater: LayoutInflater, container: ViewGroup?,
27 | savedInstanceState: Bundle?
28 | ): View? {
29 | binding = FragmentFollowersBinding.inflate(inflater)
30 | return binding.root
31 | }
32 |
33 | override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
34 | super.onViewCreated(view, savedInstanceState)
35 |
36 | viewModel.getFollowersId()
37 | observeLiveData()
38 | }
39 |
40 | private fun observeLiveData() {
41 | viewModel._errorState.observe(viewLifecycleOwner) { error ->
42 | if (error != null) Snackbar.make(requireView(), error, Snackbar.LENGTH_LONG).show()
43 | }
44 | viewModel._loadingState.observe(viewLifecycleOwner) { loading ->
45 | //
46 | }
47 |
48 | viewModel._followerState.observe(viewLifecycleOwner) { list ->
49 | adapter(list)
50 | }
51 |
52 | }
53 |
54 | private fun adapter(list: List) {
55 | followersAdapter = FollowersAdapter(list)
56 | binding.followersRecyclerView.adapter = followersAdapter
57 | }
58 |
59 |
60 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/suveybesena/instagramclone/presentation/search/FriendsAdapter.kt:
--------------------------------------------------------------------------------
1 | package com.suveybesena.instagramclone.presentation.search
2 |
3 | import android.view.LayoutInflater
4 | import android.view.View
5 | import android.view.ViewGroup
6 | import android.widget.Button
7 | import androidx.recyclerview.widget.RecyclerView
8 | import com.suveybesena.instagramclone.R
9 | import com.suveybesena.instagramclone.model.User
10 | import com.suveybesena.instagramclone.utils.extensions.downloadImage
11 | import kotlinx.android.synthetic.main.add_friends_item.view.*
12 |
13 | class FriendsAdapter(
14 | var list: List,
15 | var followInterface: FollowInterface,
16 | var unfollowInterface: UnfollowInterface,
17 | var getUserInterface : GetUserInterface
18 | ) :
19 | RecyclerView.Adapter() {
20 | class VH(itemView: View) : RecyclerView.ViewHolder(itemView) {
21 |
22 | }
23 |
24 | override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): VH {
25 | val view =
26 | LayoutInflater.from(parent.context).inflate(R.layout.add_friends_item, parent, false)
27 | return VH(view)
28 | }
29 |
30 | override fun onBindViewHolder(holder: VH, position: Int) {
31 | val user = list[position]
32 | val userId = user.uid.toString()
33 | val image = user.image
34 | holder.itemView.apply {
35 | tw_name.text = user.username
36 | user.image?.let { imageString -> iw_user.downloadImage(imageString) }
37 |
38 | checkFollowingStatus(bw_follow, userId, bw_unfollow)
39 |
40 | bw_follow.setOnClickListener {
41 | if (image != null) {
42 | followInterface.itemOnClick(userId)
43 | }
44 | }
45 | bw_unfollow.setOnClickListener {
46 | unfollowInterface.onItemClick(userId)
47 |
48 | }
49 | }
50 | }
51 |
52 |
53 | fun checkFollowingStatus(followButton : Button,userId : String, unfollowButton : Button ){
54 | getUserInterface.getUserList(userId, followButton, unfollowButton)
55 | }
56 |
57 | override fun getItemCount(): Int {
58 | return list.size
59 | }
60 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/suveybesena/instagramclone/presentation/following/FollowingFragment.kt:
--------------------------------------------------------------------------------
1 | package com.suveybesena.instagramclone.presentation.following
2 |
3 | import android.os.Bundle
4 | import androidx.fragment.app.Fragment
5 | import android.view.LayoutInflater
6 | import android.view.View
7 | import android.view.ViewGroup
8 | import androidx.fragment.app.viewModels
9 | import com.google.android.material.snackbar.Snackbar
10 | import com.suveybesena.instagramclone.databinding.FragmentFollowingBinding
11 | import com.suveybesena.instagramclone.model.FollowModel
12 | import dagger.hilt.android.AndroidEntryPoint
13 |
14 | @AndroidEntryPoint
15 | class FollowingFragment : Fragment() {
16 |
17 | private val viewModel: FollowingViewModel by viewModels()
18 | private lateinit var followingadapter: FollowingAdapter
19 | private lateinit var binding : FragmentFollowingBinding
20 |
21 | override fun onCreate(savedInstanceState: Bundle?) {
22 | super.onCreate(savedInstanceState)
23 |
24 | }
25 |
26 | override fun onCreateView(
27 | inflater: LayoutInflater, container: ViewGroup?,
28 | savedInstanceState: Bundle?
29 | ): View {
30 | binding = FragmentFollowingBinding.inflate(inflater)
31 | return binding.root
32 | }
33 |
34 | override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
35 | super.onViewCreated(view, savedInstanceState)
36 |
37 | viewModel.getFollowersId()
38 | observeLiveData()
39 | }
40 |
41 | private fun observeLiveData() {
42 | viewModel._userList.observe(viewLifecycleOwner) { list ->
43 | feedAdapter(list)
44 | }
45 | viewModel._errorState.observe(viewLifecycleOwner) { error ->
46 | if (error != null) Snackbar.make(requireView(), error, Snackbar.LENGTH_LONG).show()
47 | }
48 | viewModel._loadingState.observe(viewLifecycleOwner) { loading ->
49 | binding.followingProgressBar.visibility = if (loading == true) View.VISIBLE else View.GONE
50 | }
51 |
52 |
53 | }
54 | private fun feedAdapter(list: List) {
55 | followingadapter = FollowingAdapter(list)
56 | binding.followRecyclerView.adapter = followingadapter
57 | }
58 |
59 |
60 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/suveybesena/instagramclone/presentation/likes/LikesViewModel.kt:
--------------------------------------------------------------------------------
1 | package com.suveybesena.instagramclone.presentation.likes
2 |
3 | import androidx.lifecycle.MutableLiveData
4 | import androidx.lifecycle.ViewModel
5 | import com.google.firebase.auth.FirebaseAuth
6 | import com.google.firebase.firestore.FirebaseFirestore
7 | import com.google.firebase.storage.FirebaseStorage
8 | import com.suveybesena.instagramclone.model.LikesModel
9 | import com.suveybesena.instagramclone.di.FirebaseModule
10 | import dagger.hilt.android.lifecycle.HiltViewModel
11 | import javax.inject.Inject
12 | import kotlin.collections.ArrayList
13 | @HiltViewModel
14 | class LikesViewModel @Inject
15 | constructor(var firebaseInstance : FirebaseFirestore,
16 | var authInstance : FirebaseAuth
17 | ): ViewModel() {
18 |
19 | private var likesList = MutableLiveData?>()
20 | var _likesList = likesList
21 | private var errorState = MutableLiveData()
22 | var _errorState = errorState
23 | private var loadingState = MutableLiveData()
24 | var _loadingState = loadingState
25 |
26 |
27 |
28 | var tempList = ArrayList()
29 |
30 |
31 | fun loadImage() {
32 | loadingState.value = true
33 |
34 | tempList.clear()
35 | firebaseInstance.collection("UsersName").document(authInstance.currentUser?.uid.toString())
36 | .addSnapshotListener { value, exception ->
37 | if (exception != null) {
38 | likesList.value = null
39 | loadingState.value = false
40 | errorState.value = exception.localizedMessage
41 | } else {
42 | errorState.value = null
43 | val image = value?.get("likes") as List?
44 | image?.forEach { images->
45 | val list = LikesModel(images)
46 | tempList.add(list)
47 | }
48 | likesList.value = tempList
49 | loadingState.value = false
50 | }
51 | }
52 |
53 |
54 | }
55 |
56 |
57 |
58 |
59 | }
60 |
--------------------------------------------------------------------------------
/app/src/main/java/com/suveybesena/instagramclone/presentation/edit/EditViewModel.kt:
--------------------------------------------------------------------------------
1 | package com.suveybesena.instagramclone.presentation.edit
2 |
3 |
4 | import android.net.Uri
5 | import androidx.lifecycle.MutableLiveData
6 | import androidx.lifecycle.ViewModel
7 | import com.google.firebase.auth.FirebaseAuth
8 | import com.google.firebase.firestore.FirebaseFirestore
9 | import com.google.firebase.storage.FirebaseStorage
10 | import com.suveybesena.instagramclone.di.FirebaseModule
11 | import dagger.hilt.android.lifecycle.HiltViewModel
12 | import javax.inject.Inject
13 |
14 | @HiltViewModel
15 | class EditViewModel @Inject constructor(var firebaseInstance : FirebaseFirestore,
16 | var storageInstance : FirebaseStorage,
17 | var authInstance : FirebaseAuth) : ViewModel() {
18 |
19 |
20 | private val loadingState = MutableLiveData()
21 | var _loadingState = loadingState
22 | private val errorState = MutableLiveData()
23 | val _errorState = errorState
24 |
25 |
26 | fun saveEdit(name: String, surname: String, bio: String, webSite: String, pickedImage: Uri) {
27 | var currentUserId = authInstance.currentUser?.uid.toString()
28 | storageInstance.reference.child("images").child(currentUserId).delete()
29 | val storage =
30 | storageInstance.reference.child("images").child(currentUserId)
31 | storage.putFile(pickedImage)
32 | storage.downloadUrl.addOnSuccessListener { uri ->
33 | val imageUrl = uri.toString()
34 |
35 |
36 |
37 | firebaseInstance.collection("UsersName").document(currentUserId)
38 | .update(
39 | mapOf(
40 | "name" to name,
41 | "surname" to surname,
42 | "bio" to bio,
43 | "website" to webSite,
44 | "image" to imageUrl
45 |
46 | )
47 | ).addOnCompleteListener { loadingState.value = false }
48 | .addOnFailureListener { error ->
49 | loadingState.value = false
50 | errorState.value = error.localizedMessage
51 | }
52 | }
53 | }
54 | }
55 |
56 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/dialog_view.xml:
--------------------------------------------------------------------------------
1 |
2 |
10 |
11 |
12 |
25 |
26 |
38 |
39 |
40 |
41 |
52 |
53 |
54 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/fragment_home.xml:
--------------------------------------------------------------------------------
1 |
2 |
10 |
11 |
12 |
13 |
17 |
18 |
29 |
30 |
40 |
41 |
49 |
50 |
51 |
52 |
--------------------------------------------------------------------------------
/app/src/main/java/com/suveybesena/instagramclone/presentation/home/FeedAdapter.kt:
--------------------------------------------------------------------------------
1 | package com.suveybesena.instagramclone.presentation.home
2 |
3 | import android.view.LayoutInflater
4 | import android.view.View
5 | import android.view.ViewGroup
6 | import androidx.recyclerview.widget.RecyclerView
7 | import com.suveybesena.instagramclone.presentation.likes.LikesInterface
8 | import com.suveybesena.instagramclone.R
9 | import com.suveybesena.instagramclone.model.Firebase
10 | import com.suveybesena.instagramclone.presentation.likes.UnlikeInterface
11 | import com.suveybesena.instagramclone.utils.extensions.downloadImage
12 | import kotlinx.android.synthetic.main.feed_recycler_row.view.*
13 |
14 |
15 | class FeedAdapter(
16 | var list: List,
17 | var likeInterface: LikesInterface,
18 | var getLikesInterface: FeedLikesInterface,
19 | var unlikeInterface: UnlikeInterface
20 | ) : RecyclerView.Adapter() {
21 | class FeedVH(itemView: View) : RecyclerView.ViewHolder(itemView) {
22 |
23 | }
24 |
25 | override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): FeedVH {
26 | val inflater = LayoutInflater.from(parent.context)
27 | val view = inflater.inflate(R.layout.feed_recycler_row, parent, false)
28 | return FeedVH(view)
29 | }
30 |
31 | override fun onBindViewHolder(holder: FeedVH, position: Int) {
32 | val user = list[position]
33 | val image = user.image
34 |
35 | holder.itemView.apply {
36 | tw_comment.text = user.comment
37 | tw_username_comment.text = user.user
38 | tw_username_feed.text = user.location
39 | imw_post.downloadImage(user.image)
40 | iw_user_profile_feed.downloadImage(user.allInOne[user.image].toString())
41 |
42 | }
43 | val like = holder.itemView.imw_like
44 | val unlike = holder.itemView.imw_unLike
45 | getLikesInterface.getLikes(list.get(position).image, like, unlike)
46 |
47 | holder.itemView.imw_like.setOnClickListener {
48 | likeInterface.likeItemClick(image)
49 | }
50 | holder.itemView.imw_unLike.setOnClickListener {
51 | unlikeInterface.unlikeItemClick(image)
52 | }
53 |
54 | }
55 |
56 | override fun getItemCount(): Int {
57 | return list.size
58 | }
59 |
60 |
61 | }
--------------------------------------------------------------------------------
/app/src/main/res/layout/likes_item.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
17 |
24 |
35 |
45 |
46 |
47 |
48 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/fragment_following.xml:
--------------------------------------------------------------------------------
1 |
2 |
10 |
19 |
20 |
26 |
35 |
36 |
37 |
38 |
39 |
40 |
49 |
50 |
59 |
60 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/fragment_likes.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
19 |
20 |
25 |
26 |
35 |
36 |
37 |
38 |
39 |
40 |
49 |
50 |
62 |
63 |
64 |
--------------------------------------------------------------------------------
/app/src/main/java/com/suveybesena/instagramclone/presentation/map/GetUserLocationFragment.kt:
--------------------------------------------------------------------------------
1 | package com.suveybesena.instagramclone.presentation.map
2 |
3 | import android.location.Geocoder
4 | import android.os.Bundle
5 | import android.view.LayoutInflater
6 | import android.view.View
7 | import android.view.ViewGroup
8 | import androidx.fragment.app.Fragment
9 | import androidx.navigation.Navigation
10 | import com.google.android.gms.maps.GoogleMap
11 | import com.google.android.gms.maps.OnMapReadyCallback
12 | import com.google.android.gms.maps.SupportMapFragment
13 | import com.google.android.gms.maps.model.LatLng
14 | import com.suveybesena.instagramclone.R
15 | import com.suveybesena.instagramclone.databinding.FragmentGetUserLocationBinding
16 | import kotlinx.android.synthetic.main.fragment_get_user_location.*
17 | import java.util.*
18 |
19 | class GetUserLocationFragment : Fragment(), OnMapReadyCallback {
20 |
21 | private lateinit var mMap: GoogleMap
22 | private lateinit var binding : FragmentGetUserLocationBinding
23 |
24 | override fun onCreateView(
25 | inflater: LayoutInflater,
26 | container: ViewGroup?,
27 | savedInstanceState: Bundle?
28 | ): View? {
29 | binding =FragmentGetUserLocationBinding.inflate(inflater)
30 | return binding.root
31 | }
32 |
33 | override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
34 | super.onViewCreated(view, savedInstanceState)
35 | val mapFragment = childFragmentManager.findFragmentById(R.id.map) as SupportMapFragment?
36 | mapFragment?.getMapAsync(this)
37 | }
38 |
39 | override fun onMapReady(googleMap: GoogleMap) {
40 | mMap = googleMap
41 | mMap.setOnMapLongClickListener { latLng ->
42 | mapClickListener(latLng)
43 | }
44 | }
45 |
46 | private fun mapClickListener(location: LatLng) {
47 | mMap.clear()
48 | val geocoder = Geocoder(requireContext(), Locale.getDefault())
49 | var address = ""
50 | try {
51 | val adressList = geocoder.getFromLocation(location.latitude, location.longitude, 1)
52 | if (adressList.get(0).subAdminArea != null) {
53 | address += adressList.get(0).subAdminArea
54 | if (adressList.get(0).adminArea != null) {
55 | address += adressList.get(0).adminArea
56 | }
57 | }
58 |
59 | } catch (e: Exception) {
60 | e.printStackTrace()
61 | }
62 | binding.twLocation.text = address
63 | binding.bwCheck.setOnClickListener {
64 | val action =
65 | GetUserLocationFragmentDirections.actionGetUserLocationFragmentToShareFragment()
66 | action.location = address
67 | Navigation.findNavController(requireView()).navigate(action)
68 | }
69 | }
70 |
71 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/suveybesena/instagramclone/presentation/likes/LikesFragment.kt:
--------------------------------------------------------------------------------
1 | package com.suveybesena.instagramclone.presentation.likes
2 |
3 | import android.os.Bundle
4 | import androidx.fragment.app.Fragment
5 | import android.view.LayoutInflater
6 | import android.view.View
7 | import android.view.ViewGroup
8 | import androidx.fragment.app.viewModels
9 | import androidx.recyclerview.widget.ItemTouchHelper
10 | import androidx.recyclerview.widget.RecyclerView
11 | import com.google.android.material.snackbar.Snackbar
12 | import com.suveybesena.instagramclone.databinding.FragmentLikesBinding
13 | import com.suveybesena.instagramclone.model.LikesModel
14 | import dagger.hilt.android.AndroidEntryPoint
15 | import kotlinx.android.synthetic.main.fragment_likes.*
16 |
17 | @AndroidEntryPoint
18 | class LikesFragment : Fragment() {
19 |
20 | private val viewModel: LikesViewModel by viewModels()
21 | private lateinit var likesAdapter: LikesAdapter
22 | private lateinit var binding: FragmentLikesBinding
23 |
24 | override fun onCreate(savedInstanceState: Bundle?) {
25 | super.onCreate(savedInstanceState)
26 | }
27 |
28 | override fun onCreateView(
29 | inflater: LayoutInflater, container: ViewGroup?,
30 | savedInstanceState: Bundle?
31 | ): View? {
32 | binding = FragmentLikesBinding.inflate(inflater)
33 | return binding.root
34 | }
35 |
36 | override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
37 | super.onViewCreated(view, savedInstanceState)
38 | viewModel.loadImage()
39 | observeLiveData()
40 | }
41 |
42 | private fun observeLiveData() {
43 | viewModel._likesList.observe(viewLifecycleOwner) { list ->
44 | if (list != null) adapterInit(list)
45 | }
46 | viewModel._errorState.observe(viewLifecycleOwner) { error ->
47 | if (error != null) {
48 | Snackbar.make(requireView(), error, Snackbar.LENGTH_LONG).show()
49 | }
50 | }
51 | viewModel._loadingState.observe(viewLifecycleOwner) { loading ->
52 | binding.likesProgressBar.visibility = if (loading == true) View.VISIBLE else View.GONE
53 |
54 | }
55 | }
56 |
57 | private fun adapterInit(list: List) {
58 | likesAdapter = LikesAdapter(list)
59 | binding.likesRecyclerView.adapter = likesAdapter
60 |
61 | val swipe = object : SwipeToDeleteCallBack() {
62 | override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) {
63 | val position = viewHolder.adapterPosition
64 |
65 | binding.likesRecyclerView.adapter?.notifyItemRemoved(position)
66 | }
67 |
68 | }
69 |
70 | val itemTouchHelper = ItemTouchHelper(swipe)
71 | itemTouchHelper.attachToRecyclerView(likes_recyclerView)
72 |
73 |
74 | }
75 |
76 |
77 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/suveybesena/instagramclone/presentation/share/ShareViewModel.kt:
--------------------------------------------------------------------------------
1 | package com.suveybesena.instagramclone.presentation.share
2 |
3 | import android.net.Uri
4 | import androidx.lifecycle.MutableLiveData
5 | import androidx.lifecycle.ViewModel
6 | import com.google.firebase.auth.FirebaseAuth
7 | import com.google.firebase.firestore.FirebaseFirestore
8 | import com.google.firebase.storage.FirebaseStorage
9 | import com.suveybesena.instagramclone.di.FirebaseModule
10 | import dagger.hilt.android.lifecycle.HiltViewModel
11 | import javax.inject.Inject
12 |
13 | @HiltViewModel
14 | class ShareViewModel @Inject
15 | constructor(var firebaseInstance : FirebaseFirestore,
16 | var storageInstance : FirebaseStorage,
17 | var authInstance : FirebaseAuth
18 | )
19 | : ViewModel() {
20 |
21 |
22 | private val loadingState = MutableLiveData()
23 | val _loadingState = loadingState
24 | private val errorState = MutableLiveData()
25 | val _errorState = errorState
26 |
27 | fun imageDownloader(pickedImage: Uri, comment: String, location: String) {
28 | loadingState.value = true
29 | val currentUserId = authInstance.currentUser?.uid.toString()
30 |
31 | val imageReference = storageInstance.reference.child("feedImages").child(currentUserId)
32 |
33 | imageReference.putFile(pickedImage).addOnSuccessListener { taskSnapshot ->
34 |
35 | val uploadedImageReference =
36 | FirebaseStorage.getInstance().reference.child("feedImages").child(currentUserId)
37 | uploadedImageReference.downloadUrl.addOnSuccessListener { uri ->
38 | val imageUrl = uri.toString()
39 | val currentUserMail = authInstance.currentUser?.email.toString()
40 | val date = com.google.firebase.Timestamp.now()
41 |
42 | val postHashmap = hashMapOf()
43 | postHashmap.put("downloadUri", imageUrl)
44 | postHashmap.put("currentUserMail", currentUserMail)
45 | postHashmap.put("comment", comment)
46 | postHashmap.put("date", date)
47 | postHashmap.put("uid", currentUserId)
48 | postHashmap.put("location", location)
49 |
50 | firebaseInstance.collection("feedImages").add(postHashmap).addOnCompleteListener { task ->
51 | if (task.isSuccessful) {
52 | loadingState.value = false
53 | }
54 | }.addOnFailureListener { exception ->
55 | loadingState.value = false
56 | errorState.value = exception.localizedMessage
57 | }
58 |
59 | }.addOnFailureListener { exception ->
60 | loadingState.value = false
61 | errorState.value = exception.localizedMessage
62 | }
63 | }
64 | }
65 |
66 |
67 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/suveybesena/instagramclone/presentation/following/FollowingViewModel.kt:
--------------------------------------------------------------------------------
1 | package com.suveybesena.instagramclone.presentation.following
2 |
3 | import androidx.lifecycle.MutableLiveData
4 | import androidx.lifecycle.ViewModel
5 | import com.google.firebase.auth.FirebaseAuth
6 | import com.google.firebase.firestore.FirebaseFirestore
7 | import com.google.firebase.storage.FirebaseStorage
8 | import com.suveybesena.instagramclone.model.FollowModel
9 | import com.suveybesena.instagramclone.di.FirebaseModule
10 | import dagger.hilt.android.lifecycle.HiltViewModel
11 | import javax.inject.Inject
12 |
13 | @HiltViewModel
14 | class FollowingViewModel
15 | @Inject constructor(
16 | var firebaseInstance : FirebaseFirestore,
17 | var authInstance : FirebaseAuth
18 | )
19 | : ViewModel() {
20 |
21 |
22 | private val userList = MutableLiveData>()
23 | val _userList = userList
24 | private val errorState = MutableLiveData()
25 | val _errorState = errorState
26 | private val loadingState = MutableLiveData()
27 | val _loadingState = loadingState
28 |
29 |
30 | var tempList = ArrayList()
31 | var name = ""
32 | var image = ""
33 |
34 | fun getFollowersId() {
35 | var currentUserId = authInstance.currentUser?.uid
36 |
37 | if (currentUserId!= null) {
38 | firebaseInstance.collection("UsersName").document(currentUserId.toString())
39 | .addSnapshotListener { value, error ->
40 | if (error != null) {
41 | errorState.value = error.localizedMessage
42 | }
43 | val doc = value?.get("following") as List?
44 | if (doc != null) {
45 | if (doc.isNotEmpty()) {
46 | getFollowersProfiles(doc)
47 | }
48 | }
49 | }
50 | }
51 |
52 | }
53 |
54 | private fun getFollowersProfiles(followerIdList: List) {
55 | loadingState.value = true
56 | tempList.clear()
57 | followerIdList.forEach { followerId ->
58 | firebaseInstance.collection("UsersName").document(followerId)
59 | .addSnapshotListener { value, error ->
60 | if (error != null) {
61 | loadingState.value = false
62 | errorState.value = error.localizedMessage
63 | }
64 | if (value != null) {
65 | val image = value.getString("image")
66 | val name = value.getString("name")
67 | tempList.add(FollowModel(name, image))
68 | userList.postValue(tempList)
69 | loadingState.value = false
70 | }
71 | }
72 |
73 | }
74 |
75 | }
76 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/suveybesena/instagramclone/presentation/followers/FollowerViewModel.kt:
--------------------------------------------------------------------------------
1 | package com.suveybesena.instagramclone.presentation.followers
2 |
3 |
4 | import androidx.lifecycle.MutableLiveData
5 | import androidx.lifecycle.ViewModel
6 | import com.google.firebase.auth.FirebaseAuth
7 | import com.google.firebase.firestore.FirebaseFirestore
8 | import com.google.firebase.storage.FirebaseStorage
9 | import com.suveybesena.instagramclone.model.FollowersModel
10 | import com.suveybesena.instagramclone.di.FirebaseModule
11 | import dagger.hilt.android.lifecycle.HiltViewModel
12 | import javax.inject.Inject
13 | @HiltViewModel
14 | class FollowerViewModel @Inject constructor(
15 | var firebaseInstance : FirebaseFirestore,
16 | var authInstance : FirebaseAuth
17 | ): ViewModel() {
18 |
19 | private val followerState = MutableLiveData>()
20 | var _followerState = followerState
21 | private val loadingState = MutableLiveData()
22 | val _loadingState = loadingState
23 | private val errorState = MutableLiveData()
24 | val _errorState = errorState
25 |
26 |
27 |
28 | var tempList = ArrayList()
29 | var name = ""
30 | var image = ""
31 |
32 | fun getFollowersId() {
33 |
34 | val currentUserId = authInstance.currentUser?.uid
35 | if (currentUserId != null) {
36 | firebaseInstance.collection("UsersName").document(currentUserId.toString())
37 | .addSnapshotListener { value, error ->
38 | if (error != null) {
39 | errorState.value = error.localizedMessage
40 | }
41 | val doc = value?.get("followers") as List?
42 | if (doc != null) {
43 | getFollowersProfiles(doc)
44 | } else {
45 |
46 | errorState.value = error?.localizedMessage
47 | }
48 | }
49 |
50 | }
51 |
52 | }
53 |
54 | private fun getFollowersProfiles(followerIdList: List) {
55 | loadingState.value = true
56 | tempList.clear()
57 | followerIdList.forEach { followerId ->
58 | firebaseInstance.collection("UsersName").document(followerId)
59 | .addSnapshotListener { value, error ->
60 | if (error != null) {
61 | loadingState.value = false
62 | errorState.value = error.localizedMessage
63 | }
64 | if (value != null) {
65 | val image = value.getString("image")
66 | val name = value.getString("name")
67 | tempList.add(FollowersModel(name, image))
68 | followerState.postValue(tempList)
69 | loadingState.value = false
70 | }
71 | }
72 |
73 | }
74 |
75 | }
76 |
77 |
78 | }
79 |
80 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @rem
2 | @rem Copyright 2015 the original author or authors.
3 | @rem
4 | @rem Licensed under the Apache License, Version 2.0 (the "License");
5 | @rem you may not use this file except in compliance with the License.
6 | @rem You may obtain a copy of the License at
7 | @rem
8 | @rem https://www.apache.org/licenses/LICENSE-2.0
9 | @rem
10 | @rem Unless required by applicable law or agreed to in writing, software
11 | @rem distributed under the License is distributed on an "AS IS" BASIS,
12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | @rem See the License for the specific language governing permissions and
14 | @rem limitations under the License.
15 | @rem
16 |
17 | @if "%DEBUG%" == "" @echo off
18 | @rem ##########################################################################
19 | @rem
20 | @rem Gradle startup script for Windows
21 | @rem
22 | @rem ##########################################################################
23 |
24 | @rem Set local scope for the variables with windows NT shell
25 | if "%OS%"=="Windows_NT" setlocal
26 |
27 | set DIRNAME=%~dp0
28 | if "%DIRNAME%" == "" set DIRNAME=.
29 | set APP_BASE_NAME=%~n0
30 | set APP_HOME=%DIRNAME%
31 |
32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter.
33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
34 |
35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
37 |
38 | @rem Find java.exe
39 | if defined JAVA_HOME goto findJavaFromJavaHome
40 |
41 | set JAVA_EXE=java.exe
42 | %JAVA_EXE% -version >NUL 2>&1
43 | if "%ERRORLEVEL%" == "0" goto execute
44 |
45 | echo.
46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
47 | echo.
48 | echo Please set the JAVA_HOME variable in your environment to match the
49 | echo location of your Java installation.
50 |
51 | goto fail
52 |
53 | :findJavaFromJavaHome
54 | set JAVA_HOME=%JAVA_HOME:"=%
55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
56 |
57 | if exist "%JAVA_EXE%" goto execute
58 |
59 | echo.
60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
61 | echo.
62 | echo Please set the JAVA_HOME variable in your environment to match the
63 | echo location of your Java installation.
64 |
65 | goto fail
66 |
67 | :execute
68 | @rem Setup the command line
69 |
70 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
71 |
72 |
73 | @rem Execute Gradle
74 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
75 |
76 | :end
77 | @rem End local scope for the variables with windows NT shell
78 | if "%ERRORLEVEL%"=="0" goto mainEnd
79 |
80 | :fail
81 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
82 | rem the _cmd.exe /c_ return code!
83 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
84 | exit /b 1
85 |
86 | :mainEnd
87 | if "%OS%"=="Windows_NT" endlocal
88 |
89 | :omega
90 |
--------------------------------------------------------------------------------
/app/build.gradle:
--------------------------------------------------------------------------------
1 | plugins {
2 | id 'com.android.application'
3 | id 'org.jetbrains.kotlin.android'
4 | id 'kotlin-android-extensions'
5 | id 'androidx.navigation.safeargs'
6 | id 'com.google.gms.google-services'
7 | id 'kotlin-kapt'
8 | id 'dagger.hilt.android.plugin'
9 | id 'com.google.android.libraries.mapsplatform.secrets-gradle-plugin'
10 | }
11 |
12 | android {
13 | compileSdk 32
14 |
15 | defaultConfig {
16 | applicationId "com.suveybesena.instagramclone"
17 | minSdk 23
18 | targetSdk 32
19 | versionCode 1
20 | versionName "1.0"
21 |
22 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
23 | }
24 |
25 | buildTypes {
26 | release {
27 | minifyEnabled false
28 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
29 | }
30 | }
31 | compileOptions {
32 | sourceCompatibility JavaVersion.VERSION_1_8
33 | targetCompatibility JavaVersion.VERSION_1_8
34 | }
35 | kotlinOptions {
36 | jvmTarget = '1.8'
37 | }
38 | buildFeatures {
39 | viewBinding true
40 | }
41 | }
42 |
43 | dependencies {
44 |
45 | implementation 'androidx.core:core-ktx:1.7.0'
46 | implementation 'androidx.appcompat:appcompat:1.4.1'
47 | implementation 'com.google.android.material:material:1.5.0'
48 | implementation 'androidx.constraintlayout:constraintlayout:2.1.3'
49 | implementation 'androidx.legacy:legacy-support-v4:1.0.0'
50 | implementation 'androidx.lifecycle:lifecycle-livedata-ktx:2.4.1'
51 | implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.4.1'
52 | implementation 'com.google.android.gms:play-services-maps:18.0.2'
53 | testImplementation 'junit:junit:4.13.2'
54 | androidTestImplementation 'androidx.test.ext:junit:1.1.3'
55 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
56 |
57 | //Navigation
58 | def nav_version = "2.4.1"
59 | implementation("androidx.navigation:navigation-fragment-ktx:$nav_version")
60 | implementation("androidx.navigation:navigation-ui-ktx:$nav_version")
61 |
62 | //bottom navigation
63 | implementation 'com.google.android.material:material:'
64 |
65 | //glide
66 | implementation 'com.github.bumptech.glide:glide:4.11.0'
67 | annotationProcessor 'com.github.bumptech.glide:compiler:4.11.0'
68 | implementation 'de.hdodenhof:circleimageview:2.2.0'
69 |
70 | //firebase
71 | implementation platform('com.google.firebase:firebase-bom:29.1.0')
72 | implementation 'com.google.firebase:firebase-auth-ktx'
73 | implementation 'com.google.firebase:firebase-firestore-ktx'
74 | implementation 'com.google.firebase:firebase-storage-ktx'
75 |
76 | //Picasso
77 | implementation 'com.squareup.picasso:picasso:2.71828'
78 |
79 | //dagger-hilt
80 | implementation "com.google.dagger:hilt-android:2.38.1"
81 | kapt "com.google.dagger:hilt-compiler:2.38.1"
82 | implementation "androidx.hilt:hilt-lifecycle-viewmodel:1.0.0-alpha03"
83 | kapt "androidx.hilt:hilt-compiler:1.0.0"
84 |
85 |
86 |
87 | }
--------------------------------------------------------------------------------
/app/src/main/res/layout/add_friends_item.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
17 |
18 |
28 |
29 |
38 |
39 |
55 |
56 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
--------------------------------------------------------------------------------
/app/src/main/java/com/suveybesena/instagramclone/presentation/contacts/ContactsFragment.kt:
--------------------------------------------------------------------------------
1 | package com.suveybesena.instagramclone.presentation.contacts
2 |
3 | import android.Manifest
4 | import android.content.pm.PackageManager
5 | import android.os.Bundle
6 | import android.provider.ContactsContract
7 | import androidx.fragment.app.Fragment
8 | import android.view.LayoutInflater
9 | import android.view.View
10 | import android.view.ViewGroup
11 | import androidx.core.app.ActivityCompat
12 | import androidx.navigation.fragment.findNavController
13 | import androidx.recyclerview.widget.LinearLayoutManager
14 | import com.suveybesena.instagramclone.R
15 | import kotlinx.android.synthetic.main.fragment_contacts.*
16 |
17 |
18 | class ContactsFragment : Fragment() {
19 |
20 | lateinit var contactsAdapter: ContactsAdapter
21 | var list = ArrayList()
22 |
23 |
24 | override fun onCreateView(
25 | inflater: LayoutInflater, container: ViewGroup?,
26 | savedInstanceState: Bundle?
27 | ): View? {
28 | // Inflate the layout for this fragment
29 | return inflater.inflate(R.layout.fragment_contacts, container, false)
30 | }
31 |
32 | override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
33 | super.onViewCreated(view, savedInstanceState)
34 |
35 | getContactsPermission()
36 | setupRecyclerView()
37 | }
38 |
39 |
40 | private fun getContactsPermission() {
41 | if (ActivityCompat.checkSelfPermission(
42 | this.requireContext(),
43 | Manifest.permission.READ_CONTACTS
44 | ) != PackageManager.PERMISSION_GRANTED
45 | ) {
46 | ActivityCompat.requestPermissions(
47 | this.requireActivity(),
48 | Array(1) { Manifest.permission.READ_CONTACTS },
49 | 111
50 | )
51 | } else {
52 | getAllContact()
53 | }
54 | }
55 |
56 | override fun onRequestPermissionsResult(
57 | requestCode: Int,
58 | permissions: Array,
59 | grantResults: IntArray
60 | ) {
61 | super.onRequestPermissionsResult(requestCode, permissions, grantResults)
62 | if (requestCode == 111 && grantResults[0] == PackageManager.PERMISSION_GRANTED) getAllContact()
63 | }
64 |
65 | private fun getAllContact() {
66 | val uri = ContactsContract.CommonDataKinds.Phone.CONTENT_URI
67 | val cursor = requireActivity().contentResolver?.query(uri, null, null, null, null)
68 | if (cursor != null && cursor.count > 0) {
69 | while (cursor.moveToNext()) {
70 | val name = cursor.getString(
71 | cursor.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME).toInt()
72 | )
73 | list.add(name)
74 | }
75 |
76 |
77 | }
78 | }
79 |
80 |
81 | private fun setupRecyclerView() {
82 | contactsAdapter = ContactsAdapter(list)
83 |
84 | contactsRecyclerView.apply {
85 | adapter = contactsAdapter
86 | layoutManager = LinearLayoutManager(activity)
87 | }
88 | contactsAdapter.setOnItemClickListener {
89 | val bundle = Bundle().apply {
90 | putString("name", it)
91 | println(it)
92 | }
93 | findNavController().navigate(R.id.action_contactsFragment_to_searchFragment, bundle)
94 | }
95 |
96 | }
97 |
98 |
99 | }
--------------------------------------------------------------------------------
/app/src/main/res/layout/fragment_search.xml:
--------------------------------------------------------------------------------
1 |
2 |
11 |
12 |
21 |
22 |
23 |
32 |
33 |
41 |
42 |
51 |
52 |
53 |
65 |
66 |
67 |
68 |
80 |
81 |
82 |
--------------------------------------------------------------------------------
/app/src/main/java/com/suveybesena/instagramclone/presentation/profile/ProfileViewModel.kt:
--------------------------------------------------------------------------------
1 | package com.suveybesena.instagramclone.presentation.profile
2 |
3 | import androidx.lifecycle.MutableLiveData
4 | import androidx.lifecycle.ViewModel
5 | import com.google.firebase.auth.FirebaseAuth
6 | import com.google.firebase.firestore.FirebaseFirestore
7 | import com.google.firebase.storage.FirebaseStorage
8 | import com.suveybesena.instagramclone.model.MyPostsModel
9 | import com.suveybesena.instagramclone.di.FirebaseModule
10 | import dagger.hilt.android.lifecycle.HiltViewModel
11 | import javax.inject.Inject
12 |
13 | @HiltViewModel
14 | class ProfileViewModel @Inject
15 | constructor(var firebaseInstance : FirebaseFirestore,
16 | var authInstance : FirebaseAuth
17 | )
18 | : ViewModel() {
19 |
20 |
21 | private val nameState = MutableLiveData()
22 | val _nameState = nameState
23 | private val imageState = MutableLiveData()
24 | val _imageState = imageState
25 | private val loadingState = MutableLiveData()
26 | val _loadingState = loadingState
27 | private val errorState = MutableLiveData()
28 | val _errorState = errorState
29 |
30 | var tempList = ArrayList()
31 | private val myPostList = MutableLiveData>()
32 | val _myPostList = myPostList
33 | val currentUserId = authInstance.currentUser?.uid.toString()
34 |
35 | fun getDataFromFirebase() {
36 | loadingState.value = true
37 |
38 | firebaseInstance.collection("UsersName").whereEqualTo("uid", currentUserId)
39 | .addSnapshotListener { snapshot, exception ->
40 | if (exception != null) {
41 | loadingState.value = false
42 | errorState.value = exception.localizedMessage
43 | } else {
44 | if (snapshot != null) {
45 | if (!snapshot.isEmpty) {
46 |
47 | val documentList = snapshot.documents
48 |
49 | for (document in documentList) {
50 |
51 | val user = document.get("name") as String
52 | val downloadUri = document.get("image") as String
53 | nameState.value = user
54 | imageState.value = downloadUri
55 |
56 | }
57 | }
58 | }
59 | }
60 | }
61 |
62 |
63 |
64 | firebaseInstance.collection("feedImages").whereEqualTo("uid", currentUserId)
65 | .orderBy("date")
66 | .addSnapshotListener { value, error ->
67 | if (error!=null){
68 | loadingState.value = false
69 | errorState.value = error.localizedMessage
70 | }else{
71 | tempList.clear()
72 | if (value != null){
73 | val documentList = value.documents
74 | for (document in documentList){
75 | val image = document.get("downloadUri") as String
76 | val uid = document.get("uid") as String
77 | var myPostList = MyPostsModel(image, uid)
78 | tempList.add(myPostList)
79 | }
80 | myPostList.value = tempList
81 | loadingState.value= false
82 | }
83 | }
84 | }
85 |
86 |
87 | }
88 |
89 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/suveybesena/instagramclone/presentation/login/LoginFragment.kt:
--------------------------------------------------------------------------------
1 | package com.suveybesena.instagramclone.presentation.login
2 |
3 | import android.os.Bundle
4 | import android.text.Editable
5 | import android.text.TextWatcher
6 | import androidx.fragment.app.Fragment
7 | import android.view.LayoutInflater
8 | import android.view.View
9 | import android.view.ViewGroup
10 | import androidx.fragment.app.viewModels
11 | import androidx.navigation.Navigation
12 | import com.google.android.material.snackbar.Snackbar
13 | import com.google.firebase.auth.FirebaseAuth
14 | import com.suveybesena.instagramclone.R
15 | import com.suveybesena.instagramclone.databinding.FragmentLoginBinding
16 | import dagger.hilt.android.AndroidEntryPoint
17 | import kotlinx.android.synthetic.main.fragment_login.*
18 |
19 | @AndroidEntryPoint
20 | class LoginFragment : Fragment(), TextWatcher {
21 |
22 | private var mAuth = FirebaseAuth.getInstance()
23 | private val viewModel: LoginViewModel by viewModels()
24 | private lateinit var binding: FragmentLoginBinding
25 |
26 |
27 | override fun onCreate(savedInstanceState: Bundle?) {
28 | super.onCreate(savedInstanceState)
29 | }
30 |
31 | override fun onCreateView(
32 | inflater: LayoutInflater, container: ViewGroup?,
33 | savedInstanceState: Bundle?
34 | ): View {
35 | binding = FragmentLoginBinding.inflate(inflater)
36 | return binding.root
37 | }
38 |
39 | override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
40 | super.onViewCreated(view, savedInstanceState)
41 |
42 | listeners()
43 |
44 | }
45 |
46 | override fun onStart() {
47 | super.onStart()
48 | if (mAuth.currentUser != null) {
49 | view?.let {
50 | Navigation.findNavController(it).navigate(R.id.action_loginFragment_to_homeFragment)
51 | }
52 | }
53 | }
54 |
55 | private fun listeners() {
56 |
57 | binding.btwLogin.isEnabled = false
58 | binding.edtLoginMail.addTextChangedListener(this)
59 | binding.edtLoginPassword.addTextChangedListener(this)
60 |
61 | btw_login.setOnClickListener {
62 | login()
63 | }
64 | tw_sign_up.setOnClickListener {
65 | view?.let { it1 ->
66 | Navigation.findNavController(it1)
67 | .navigate(R.id.action_loginFragment_to_registerFragment)
68 | }
69 | }
70 | }
71 |
72 | fun login() {
73 | val email = binding.edtLoginMail.text.toString()
74 | val password = binding.edtLoginPassword.text.toString()
75 | viewModel.login(email, password)
76 | Navigation.findNavController(requireView())
77 | .navigate(R.id.action_loginFragment_to_homeFragment)
78 | viewModel._errorState.observe(viewLifecycleOwner) { error ->
79 | if (error != null) Snackbar.make(requireView(), error, Snackbar.LENGTH_LONG).show()
80 | }
81 |
82 | viewModel._loadingState.observe(viewLifecycleOwner) { loading ->
83 | binding.loginProgressBar.visibility = if (loading == true) View.VISIBLE else View.GONE
84 | }
85 |
86 | }
87 |
88 | override fun beforeTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {
89 | }
90 |
91 | override fun onTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {
92 | }
93 |
94 | override fun afterTextChanged(p0: Editable?) {
95 | binding.btwLogin.isEnabled =
96 | binding.edtLoginMail.text.toString()
97 | .isNotEmpty() && binding.edtLoginPassword.text.toString()
98 | .isNotEmpty()
99 | }
100 |
101 |
102 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/suveybesena/instagramclone/presentation/register/RegisterViewModel.kt:
--------------------------------------------------------------------------------
1 | package com.suveybesena.instagramclone.presentation.register
2 |
3 |
4 | import android.net.Uri
5 | import android.view.View
6 | import androidx.lifecycle.MutableLiveData
7 | import androidx.lifecycle.ViewModel
8 | import com.google.firebase.auth.FirebaseAuth
9 | import com.google.firebase.firestore.FirebaseFirestore
10 | import com.google.firebase.storage.FirebaseStorage
11 | import com.suveybesena.instagramclone.di.FirebaseModule
12 | import dagger.hilt.android.lifecycle.HiltViewModel
13 | import javax.inject.Inject
14 |
15 | @HiltViewModel
16 | class RegisterViewModel @Inject
17 | constructor(var firebaseInstance : FirebaseFirestore,
18 | var storageInstance : FirebaseStorage,
19 | var authInstance : FirebaseAuth
20 | )
21 | : ViewModel() {
22 |
23 | private val loadingState = MutableLiveData()
24 | val _loadingState = loadingState
25 | private val errorState = MutableLiveData()
26 | val _errorState = errorState
27 | private val authState = MutableLiveData()
28 | val _authState = authState
29 |
30 |
31 |
32 |
33 |
34 |
35 | fun register(
36 | mail: String,
37 | password: String,
38 | pickedImage: Uri,
39 | view: View,
40 | username: String,
41 | name: String
42 | ) {
43 |
44 | loadingState.value = true
45 | val user = hashMapOf()
46 | user.put("name", username)
47 | user.put("password", password)
48 | user.put("surname", name)
49 | user.put("bio", "")
50 | user.put("website", "www.test.com")
51 | user.put("image", pickedImage)
52 |
53 | authInstance.createUserWithEmailAndPassword(mail, password).addOnCompleteListener { task ->
54 | if (task.isSuccessful) {
55 | authInstance.currentUser?.let { user.put("uid", it.uid) }
56 | uploadPhoto(pickedImage, username)
57 | view.let { view ->
58 | authState.value = true
59 | }
60 | }
61 | }.addOnFailureListener { exception ->
62 | loadingState.value = false
63 | errorState.value = exception.localizedMessage
64 | }
65 |
66 | }
67 |
68 | fun uploadPhoto(pickedImage: Uri, username: String) {
69 |
70 | val currentUserId = authInstance.currentUser?.uid.toString()
71 | val reference = storageInstance.reference
72 | val imageReference = reference.child("images").child(currentUserId)
73 |
74 | imageReference.putFile(pickedImage).addOnSuccessListener { taskSnapshot ->
75 |
76 | val uploadedImageReference =
77 | FirebaseStorage.getInstance().reference.child("images").child(currentUserId)
78 | uploadedImageReference.downloadUrl.addOnSuccessListener { uri ->
79 | val imageUrl = uri.toString()
80 | val postHashmap = hashMapOf()
81 | postHashmap.put("image", imageUrl)
82 | postHashmap.put("name", username)
83 | postHashmap.put("uid", currentUserId)
84 |
85 | firebaseInstance.collection("UsersName").document(currentUserId).set(postHashmap)
86 | .addOnCompleteListener { task ->
87 | if (task.isSuccessful) {
88 | loadingState.value = false
89 | }
90 | }.addOnFailureListener { exception ->
91 | errorState.value = exception.localizedMessage
92 |
93 | }
94 | }
95 |
96 | }.addOnFailureListener { exception ->
97 | errorState.value = exception.localizedMessage
98 | }
99 |
100 | }
101 |
102 |
103 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/suveybesena/instagramclone/presentation/profile/ProfileFragment.kt:
--------------------------------------------------------------------------------
1 | package com.suveybesena.instagramclone.presentation.profile
2 |
3 | import android.os.Bundle
4 | import androidx.fragment.app.Fragment
5 | import android.view.LayoutInflater
6 | import android.view.View
7 | import android.view.ViewGroup
8 | import androidx.fragment.app.viewModels
9 | import androidx.navigation.Navigation
10 | import androidx.recyclerview.widget.GridLayoutManager
11 | import com.bumptech.glide.Glide
12 | import com.google.android.material.snackbar.Snackbar
13 | import com.suveybesena.instagramclone.R
14 | import com.suveybesena.instagramclone.databinding.FragmentProfileBinding
15 | import com.suveybesena.instagramclone.model.FollowModel
16 | import com.suveybesena.instagramclone.model.MyPostsModel
17 | import dagger.hilt.android.AndroidEntryPoint
18 |
19 | @AndroidEntryPoint
20 | class ProfileFragment : Fragment() {
21 |
22 | lateinit var list: List
23 | private val viewModel: ProfileViewModel by viewModels()
24 | private lateinit var myPostsAdapter: MyPostsAdapter
25 | private lateinit var binding: FragmentProfileBinding
26 |
27 |
28 | override fun onCreate(savedInstanceState: Bundle?) {
29 | super.onCreate(savedInstanceState)
30 | }
31 |
32 | override fun onCreateView(
33 | inflater: LayoutInflater, container: ViewGroup?,
34 | savedInstanceState: Bundle?
35 | ): View? {
36 | binding = FragmentProfileBinding.inflate(inflater)
37 | return binding.root
38 | }
39 |
40 | override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
41 | super.onViewCreated(view, savedInstanceState)
42 |
43 | viewModel.getDataFromFirebase()
44 | initList()
45 | observeLiveData()
46 | }
47 |
48 | private fun adapter(list: List) {
49 | myPostsAdapter = MyPostsAdapter(list)
50 | binding.myPostsRecycler.apply {
51 | adapter = myPostsAdapter
52 | layoutManager = GridLayoutManager(context, 3)
53 | }
54 |
55 | }
56 |
57 | private fun observeLiveData() {
58 | val userName = binding.twUserName
59 | val imageView = binding.imwProfile
60 |
61 | viewModel._nameState.observe(viewLifecycleOwner) { name ->
62 | userName.setText(name)
63 | }
64 | viewModel._imageState.observe(viewLifecycleOwner) { image ->
65 | Glide.with(this).load(image)
66 | .into(imageView)
67 | }
68 | viewModel._errorState.observe(viewLifecycleOwner) { error ->
69 | if (error != null) Snackbar.make(requireView(), error, Snackbar.LENGTH_LONG).show()
70 | }
71 | viewModel._loadingState.observe(viewLifecycleOwner) { loading ->
72 | //profile_progress_bar.visibility = if (loading!= null) View.VISIBLE else View.GONE
73 | }
74 |
75 | viewModel._myPostList.observe(viewLifecycleOwner) { list ->
76 | binding.postCount.text = list.size.toString()
77 | adapter(list)
78 | }
79 |
80 |
81 | }
82 |
83 | private fun initList() {
84 | binding.btEditProfile.setOnClickListener {
85 | onclickEditProfile()
86 | }
87 | binding.layoutFollowing.setOnClickListener {
88 | following()
89 | }
90 | binding.layoutFollowers.setOnClickListener {
91 | followers()
92 | }
93 | }
94 |
95 | private fun followers() {
96 | view?.let {
97 | Navigation.findNavController(it)
98 | .navigate(R.id.action_profileFragment_to_followersFragment)
99 | }
100 | }
101 |
102 | private fun following() {
103 | view?.let {
104 | Navigation.findNavController(it)
105 | .navigate(R.id.action_profileFragment_to_followingFragment)
106 | }
107 | }
108 |
109 | private fun onclickEditProfile() {
110 | view?.let {
111 | Navigation.findNavController(it).navigate(R.id.action_profileFragment_to_editFragment)
112 | }
113 | }
114 |
115 | }
116 |
117 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/fragment_login.xml:
--------------------------------------------------------------------------------
1 |
2 |
10 |
11 |
12 |
13 |
25 |
26 |
38 |
39 |
57 |
58 |
59 |
60 |
73 |
74 |
75 |
76 |
89 |
90 |
100 |
101 |
102 |
103 |
104 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/feed_recycler_row.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
14 |
21 |
22 |
23 |
31 |
32 |
40 |
41 |
42 |
49 |
56 |
57 |
69 |
70 |
71 |
82 |
83 |
84 |
85 |
86 |
87 |
97 |
107 |
108 |
109 |
--------------------------------------------------------------------------------
/app/src/main/java/com/suveybesena/instagramclone/presentation/home/HomeFragment.kt:
--------------------------------------------------------------------------------
1 | package com.suveybesena.instagramclone.presentation.home
2 |
3 | import android.app.Dialog
4 | import android.os.Bundle
5 | import android.view.*
6 | import android.widget.ImageView
7 | import androidx.fragment.app.Fragment
8 | import androidx.fragment.app.viewModels
9 | import androidx.navigation.Navigation
10 | import com.google.android.material.snackbar.Snackbar
11 | import com.google.firebase.auth.FirebaseAuth
12 | import com.suveybesena.instagramclone.presentation.likes.LikesInterface
13 | import com.suveybesena.instagramclone.R
14 | import com.suveybesena.instagramclone.databinding.FragmentHomeBinding
15 | import com.suveybesena.instagramclone.model.Firebase
16 | import com.suveybesena.instagramclone.presentation.likes.LikesViewModel
17 | import com.suveybesena.instagramclone.presentation.likes.UnlikeInterface
18 | import dagger.hilt.android.AndroidEntryPoint
19 | import kotlinx.android.synthetic.main.dialog_view.*
20 | import kotlinx.android.synthetic.main.feed_recycler_row.*
21 | import kotlinx.android.synthetic.main.top_bar.*
22 |
23 | @AndroidEntryPoint
24 | class HomeFragment : Fragment() {
25 |
26 | private val viewModel: HomeViewModel by viewModels()
27 | private lateinit var feed: FeedAdapter
28 | private val auth= FirebaseAuth.getInstance()
29 | private lateinit var binding : FragmentHomeBinding
30 |
31 |
32 | override fun onCreate(savedInstanceState: Bundle?) {
33 | super.onCreate(savedInstanceState)
34 | }
35 |
36 | override fun onCreateView(
37 | inflater: LayoutInflater, container: ViewGroup?,
38 | savedInstanceState: Bundle?
39 | ): View? {
40 | binding = FragmentHomeBinding.inflate(inflater)
41 | return binding.root
42 | }
43 |
44 | override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
45 | super.onViewCreated(view, savedInstanceState)
46 |
47 | observeLiveData()
48 | initList()
49 | }
50 |
51 | private fun feedAdapter(userListFirebase: List) {
52 | feed = FeedAdapter(userListFirebase,
53 | object : LikesInterface {
54 | override fun likeItemClick(image: String) {
55 | viewModel.getLikesImage( image)
56 | }
57 | },object : FeedLikesInterface{
58 | override fun getLikes(image: String, like :ImageView, unlike :ImageView) {
59 | viewModel.checkLikeStatus(image, like, unlike)
60 | viewModel._likeState.observe(viewLifecycleOwner){ likeStatus->
61 | if (likeStatus == true){
62 | imw_unLike.visibility = View.VISIBLE
63 | imw_like.visibility = View.INVISIBLE
64 | }else{
65 | imw_like.visibility = View.VISIBLE
66 | imw_unLike.visibility = View.INVISIBLE
67 | }
68 |
69 | }
70 | }
71 | } ,
72 | object :UnlikeInterface{
73 | override fun unlikeItemClick(image: String) {
74 | viewModel.removeLike(image)
75 | }
76 |
77 | })
78 | binding.feedRecycler.adapter = feed
79 |
80 | }
81 |
82 | private fun observeLiveData() {
83 |
84 | viewModel.readUserData()
85 | viewModel._firebaseList.observe(viewLifecycleOwner){arrayListFirebase ->
86 | feedAdapter(arrayListFirebase)
87 | }
88 | viewModel._errorState.observe(viewLifecycleOwner){ error->
89 | if (!error.isNullOrEmpty()) {
90 | Snackbar.make(requireView(), error, Snackbar.LENGTH_LONG).show()
91 | }
92 | }
93 | viewModel._loadingState.observe(viewLifecycleOwner){loading ->
94 | binding.homeProgressBar.visibility = if (loading == true) View.VISIBLE else View.GONE
95 | }
96 | }
97 |
98 | private fun initList() {
99 | imw_options.setOnClickListener {
100 | options()
101 | }
102 | }
103 |
104 | private fun options() {
105 |
106 | val dialog = Dialog(requireContext())
107 | dialog.setContentView(R.layout.dialog_view)
108 | dialog.setCancelable(false)
109 | dialog.dialog_yes.setOnClickListener {
110 | auth.signOut()
111 | Navigation.findNavController(requireView()).navigate(R.id.action_homeFragment_to_loginFragment)
112 | dialog.dismiss()
113 | }
114 | dialog.dialog_no.setOnClickListener {
115 | dialog.dismiss()
116 | }
117 | dialog.show()
118 |
119 | }
120 |
121 |
122 | }
--------------------------------------------------------------------------------
/app/src/main/res/layout/fragment_share.xml:
--------------------------------------------------------------------------------
1 |
2 |
11 |
16 |
17 |
23 |
24 |
34 |
35 |
44 |
54 |
55 |
56 |
57 |
58 |
59 |
70 |
73 |
74 |
84 |
85 |
86 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
107 |
108 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
--------------------------------------------------------------------------------
/app/src/main/java/com/suveybesena/instagramclone/presentation/search/SearchFragment.kt:
--------------------------------------------------------------------------------
1 | package com.suveybesena.instagramclone.presentation.search
2 |
3 |
4 | import android.os.Bundle
5 | import android.view.LayoutInflater
6 | import android.view.View
7 | import android.view.ViewGroup
8 | import android.widget.Button
9 | import androidx.core.widget.doAfterTextChanged
10 | import androidx.fragment.app.Fragment
11 | import androidx.fragment.app.viewModels
12 | import androidx.navigation.Navigation
13 | import androidx.navigation.fragment.navArgs
14 | import com.google.android.material.snackbar.Snackbar
15 | import com.suveybesena.instagramclone.R
16 | import com.suveybesena.instagramclone.databinding.FragmentSearchBinding
17 | import com.suveybesena.instagramclone.model.User
18 | import com.suveybesena.instagramclone.presentation.contacts.ContactsFragment
19 | import dagger.hilt.android.AndroidEntryPoint
20 | import kotlinx.android.synthetic.main.add_friends_item.*
21 | import kotlinx.android.synthetic.main.add_friends_item.view.*
22 |
23 | @AndroidEntryPoint
24 | class SearchFragment : Fragment() {
25 |
26 | private var friendsAdapter: FriendsAdapter? = null
27 | private val viewModel: SearchViewModel by viewModels()
28 | private lateinit var binding: FragmentSearchBinding
29 |
30 | override fun onCreate(savedInstanceState: Bundle?) {
31 | super.onCreate(savedInstanceState)
32 | }
33 |
34 | override fun onCreateView(
35 | inflater: LayoutInflater, container: ViewGroup?,
36 | savedInstanceState: Bundle?
37 | ): View? {
38 | binding = FragmentSearchBinding.inflate(inflater)
39 | return binding.root
40 | }
41 |
42 | override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
43 | super.onViewCreated(view, savedInstanceState)
44 |
45 | getContacts()
46 | observeLiveData()
47 | initListeners()
48 | }
49 |
50 | private fun feedAdapter(userList: ArrayList) {
51 | friendsAdapter = FriendsAdapter(userList, object : FollowInterface {
52 | override fun itemOnClick(userId: String) {
53 | viewModel.follow(userId)
54 | bw_follow.visibility = View.GONE
55 | bw_unfollow.visibility = View.VISIBLE
56 | }
57 | }, object : UnfollowInterface {
58 | override fun onItemClick(userId: String) {
59 | viewModel.unFollow(userId)
60 | bw_follow.visibility = View.VISIBLE
61 | bw_unfollow.visibility = View.GONE
62 | }
63 |
64 | }, object : GetUserInterface {
65 | override fun getUserList(userId: String, follow: Button, unfollow: Button) {
66 | viewModel.checkFollowingStatus(userId, follow, unfollow)
67 | viewModel._followState.observe(viewLifecycleOwner) { followStatus ->
68 | if (followStatus != true) {
69 | follow.visibility = View.VISIBLE
70 | unfollow.visibility = View.GONE
71 | } else {
72 | unfollow.visibility = View.VISIBLE
73 | follow.visibility = View.GONE
74 | }
75 | }
76 | }
77 |
78 | })
79 |
80 | binding.searchRecyclerView.adapter = friendsAdapter
81 | }
82 |
83 | private fun initListeners() {
84 | binding.apply {
85 | edtSearch.doAfterTextChanged { text ->
86 | viewModel.retrieveUsers(text.toString())
87 | binding.searchRecyclerView.visibility = View.VISIBLE
88 | }
89 | iwClose.setOnClickListener {
90 | clearRecycler()
91 | }
92 |
93 | contacts.setOnClickListener {
94 | Navigation.findNavController(it).navigate(R.id.action_searchFragment_to_contactsFragment)
95 | }
96 | }
97 | }
98 |
99 | private fun getContacts(){
100 | val args = this.arguments
101 | val getContact = args?.get("name").toString()
102 | try {
103 | binding.apply {
104 | viewModel.getContacts(getContact)
105 | binding.searchRecyclerView.visibility = View.VISIBLE
106 |
107 | }
108 | }catch (e: Exception) {
109 | println(e.localizedMessage)
110 | }
111 |
112 | }
113 |
114 | private fun clearRecycler() {
115 | binding.apply {
116 | searchRecyclerView.visibility = View.INVISIBLE
117 | edtSearch.text.clear()
118 | }
119 |
120 | }
121 |
122 | private fun observeLiveData() {
123 | viewModel._userList.observe(viewLifecycleOwner) { arrayListUser ->
124 | arrayListUser?.let {
125 | feedAdapter(arrayListUser)
126 | }
127 | }
128 | viewModel._contactList.observe(viewLifecycleOwner){contactList->
129 | contactList?.let {
130 | feedAdapter(contactList)
131 | }
132 | }
133 |
134 |
135 | viewModel._loadingState.observe(viewLifecycleOwner) { loading ->
136 | binding.pbSearchScreen.visibility = if (loading == true) View.VISIBLE else View.GONE
137 | }
138 | viewModel._errorState.observe(viewLifecycleOwner) { error ->
139 | error?.let { errorMessage ->
140 | Snackbar.make(requireView(), errorMessage, Snackbar.LENGTH_LONG).show()
141 | }
142 | }
143 |
144 | }
145 |
146 |
147 | }
--------------------------------------------------------------------------------
/app/src/main/res/layout/fragment_register.xml:
--------------------------------------------------------------------------------
1 |
2 |
14 |
15 |
16 |
29 |
30 |
45 |
46 |
47 |
48 |
65 |
66 |
67 |
68 |
69 |
84 |
85 |
86 |
87 |
101 |
102 |
103 |
104 |
119 |
120 |
132 |
133 |
142 |
143 |
144 |
--------------------------------------------------------------------------------
/app/src/main/java/com/suveybesena/instagramclone/presentation/register/RegisterFragment.kt:
--------------------------------------------------------------------------------
1 | package com.suveybesena.instagramclone.presentation.register
2 |
3 | import android.app.Activity
4 | import android.content.ContentResolver
5 | import android.content.Intent
6 | import android.content.pm.PackageManager
7 | import android.graphics.Bitmap
8 | import android.graphics.ImageDecoder
9 | import android.net.Uri
10 | import android.os.Build
11 | import android.os.Bundle
12 | import android.provider.MediaStore
13 | import android.view.LayoutInflater
14 | import android.view.View
15 | import android.view.ViewGroup
16 | import androidx.core.app.ActivityCompat
17 | import androidx.core.content.ContextCompat
18 | import androidx.fragment.app.Fragment
19 | import androidx.fragment.app.viewModels
20 | import androidx.navigation.Navigation
21 | import com.google.android.material.snackbar.Snackbar
22 | import com.suveybesena.instagramclone.R
23 | import com.suveybesena.instagramclone.databinding.FragmentRegisterBinding
24 | import dagger.hilt.android.AndroidEntryPoint
25 | import kotlinx.android.synthetic.main.fragment_register.*
26 |
27 | @AndroidEntryPoint
28 | class RegisterFragment : Fragment() {
29 | private val viewModel: RegisterViewModel by viewModels()
30 | private lateinit var contentResolver: ContentResolver
31 | var pickedImage: Uri? = null
32 | var bitmap: Bitmap? = null
33 |
34 | private lateinit var binding: FragmentRegisterBinding
35 |
36 |
37 | override fun onCreate(savedInstanceState: Bundle?) {
38 | super.onCreate(savedInstanceState)
39 | }
40 |
41 | override fun onCreateView(
42 | inflater: LayoutInflater, container: ViewGroup?,
43 | savedInstanceState: Bundle?
44 | ): View? {
45 | binding = FragmentRegisterBinding.inflate(inflater)
46 | return binding.root
47 |
48 | }
49 |
50 | override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
51 | super.onViewCreated(view, savedInstanceState)
52 | contentResolver = requireActivity().contentResolver
53 | initListeners()
54 |
55 |
56 | }
57 |
58 | private fun initListeners() {
59 | binding.btwRegister.setOnClickListener {
60 | register()
61 | }
62 | binding.imwRegister.setOnClickListener {
63 | imwPickedImage()
64 | }
65 | binding.twSignIn.setOnClickListener {
66 | view?.let { view ->
67 | Navigation.findNavController(view)
68 | .navigate(R.id.action_registerFragment_to_loginFragment)
69 | }
70 | }
71 | }
72 |
73 |
74 | private fun register() {
75 | val register_mail = binding.edtRegisterMail.text.toString()
76 | val register_password = binding.edtRegisterPassword.toString()
77 | val username = binding.edtRegisterUsername.text.toString()
78 | val name = binding.edtRegisterName.text.toString()
79 | pickedImage?.let { image ->
80 | view?.let { view ->
81 | viewModel.register(
82 | register_mail, register_password,
83 | image, view, username, name
84 | )
85 | }
86 | }
87 | viewModel._errorState.observe(viewLifecycleOwner) { error ->
88 | if (error != null) Snackbar.make(requireView(), error, Snackbar.LENGTH_LONG).show()
89 | }
90 |
91 | viewModel._loadingState.observe(viewLifecycleOwner) { loading ->
92 | register_progress_bar.visibility = if (loading == true) View.VISIBLE else View.GONE
93 | }
94 |
95 | viewModel._authState.observe(viewLifecycleOwner) { auth ->
96 | if (auth != false) {
97 | view?.let { view ->
98 | Navigation.findNavController(view)
99 | .navigate(R.id.action_registerFragment_to_homeFragment)
100 | }
101 | }
102 | }
103 |
104 |
105 | }
106 |
107 | private fun imwPickedImage() {
108 | if (ContextCompat.checkSelfPermission(
109 | this.requireContext(),
110 | android.Manifest.permission.READ_EXTERNAL_STORAGE
111 | ) != PackageManager.PERMISSION_GRANTED
112 | ) {
113 | ActivityCompat.requestPermissions(
114 | this.requireActivity(),
115 | arrayOf(android.Manifest.permission.READ_EXTERNAL_STORAGE),
116 | 1
117 | )
118 | } else {
119 | val intent = Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI)
120 | startActivityForResult(intent, 2)
121 | }
122 | }
123 |
124 | override fun onRequestPermissionsResult(
125 | requestCode: Int,
126 | permissions: Array,
127 | grantResults: IntArray
128 | ) {
129 |
130 | if (requestCode == 1) {
131 | if (grantResults.size > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
132 | val intent =
133 | Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI)
134 | startActivityForResult(intent, 2)
135 |
136 | }
137 | }
138 | super.onRequestPermissionsResult(requestCode, permissions, grantResults)
139 | }
140 |
141 | override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
142 | if (requestCode == 2 && resultCode == Activity.RESULT_OK && data != null) {
143 |
144 | pickedImage = data.data
145 |
146 | if (pickedImage != null) {
147 |
148 | if (Build.VERSION.SDK_INT >= 28) {
149 | val source = ImageDecoder.createSource(this.contentResolver, pickedImage!!)
150 | bitmap = ImageDecoder.decodeBitmap(source)
151 | imw_register.setImageBitmap(bitmap)
152 |
153 | } else {
154 | bitmap =
155 | MediaStore.Images.Media.getBitmap(this.contentResolver, pickedImage)
156 | imw_register.setImageBitmap(bitmap)
157 | }
158 | }
159 | }
160 | super.onActivityResult(requestCode, resultCode, data)
161 | }
162 |
163 | }
--------------------------------------------------------------------------------
/app/src/main/res/layout/fragment_edit.xml:
--------------------------------------------------------------------------------
1 |
2 |
10 |
11 |
17 |
18 |
25 |
26 |
36 |
37 |
46 |
56 |
57 |
58 |
59 |
60 |
61 |
66 |
67 |
79 |
80 |
81 |
82 |
83 |
84 |
95 |
96 |
103 |
104 |
108 |
109 |
113 |
114 |
118 |
119 |
123 |
124 |
128 |
129 |
133 |
134 |
138 |
139 |
143 |
144 |
153 |
154 |
155 |
156 |
157 |
158 |
159 |
--------------------------------------------------------------------------------
/app/src/main/res/navigation/graph.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
13 |
16 |
17 |
20 |
23 |
24 |
29 |
32 |
38 |
41 |
42 |
47 | />
48 |
49 |
54 |
57 |
60 |
61 |
66 |
69 |
72 |
73 |
78 |
81 |
84 |
85 |
90 |
93 |
94 |
98 |
101 |
104 |
105 |
106 |
111 |
114 |
115 |
120 |
123 |
124 |
128 |
131 |
132 |
136 |
139 |
140 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_launcher_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
10 |
15 |
20 |
25 |
30 |
35 |
40 |
45 |
50 |
55 |
60 |
65 |
70 |
75 |
80 |
85 |
90 |
95 |
100 |
105 |
110 |
115 |
120 |
125 |
130 |
135 |
140 |
145 |
150 |
155 |
160 |
165 |
170 |
171 |
--------------------------------------------------------------------------------
/app/src/main/java/com/suveybesena/instagramclone/presentation/edit/EditFragment.kt:
--------------------------------------------------------------------------------
1 | package com.suveybesena.instagramclone.presentation.edit
2 |
3 |
4 | import android.app.Activity
5 | import android.content.ContentResolver
6 | import android.content.Intent
7 | import android.content.pm.PackageManager
8 | import android.graphics.Bitmap
9 | import android.graphics.ImageDecoder
10 | import android.net.Uri
11 | import android.os.Build
12 | import android.os.Bundle
13 | import android.provider.MediaStore
14 | import androidx.fragment.app.Fragment
15 | import android.view.LayoutInflater
16 | import android.view.View
17 | import android.view.ViewGroup
18 | import androidx.core.app.ActivityCompat
19 | import androidx.core.content.ContextCompat
20 | import androidx.fragment.app.viewModels
21 | import androidx.navigation.Navigation
22 | import com.google.android.material.snackbar.Snackbar
23 | import com.google.firebase.auth.FirebaseAuth
24 | import com.google.firebase.firestore.FirebaseFirestore
25 | import com.google.firebase.storage.FirebaseStorage
26 | import com.suveybesena.instagramclone.R
27 | import com.suveybesena.instagramclone.databinding.FragmentEditBinding
28 | import dagger.hilt.android.AndroidEntryPoint
29 | import kotlinx.android.synthetic.main.fragment_edit.*
30 |
31 | @AndroidEntryPoint
32 | class EditFragment : Fragment() {
33 | private lateinit var storage: FirebaseStorage
34 | private lateinit var auth: FirebaseAuth
35 | private lateinit var database: FirebaseFirestore
36 |
37 | private lateinit var contentResolver: ContentResolver
38 | var pickedImage: Uri? = null
39 | var bitmap: Bitmap? = null
40 |
41 | private lateinit var binding: FragmentEditBinding
42 |
43 | private val viewModel: EditViewModel by viewModels()
44 |
45 | override fun onCreate(savedInstanceState: Bundle?) {
46 | super.onCreate(savedInstanceState)
47 | }
48 |
49 | override fun onCreateView(
50 | inflater: LayoutInflater, container: ViewGroup?,
51 | savedInstanceState: Bundle?
52 | ): View? {
53 | binding = FragmentEditBinding.inflate(inflater)
54 | return binding.root
55 | }
56 |
57 | override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
58 | super.onViewCreated(view, savedInstanceState)
59 | contentResolver = requireActivity().contentResolver
60 | initList()
61 | getInstance()
62 | }
63 |
64 | private fun getInstance() {
65 | storage = FirebaseStorage.getInstance()
66 | auth = FirebaseAuth.getInstance()
67 | database = FirebaseFirestore.getInstance()
68 | }
69 |
70 | private fun initList() {
71 |
72 | binding.apply {
73 | btwCloseEdit.setOnClickListener {
74 | closeEdit()
75 | }
76 | btwSaveEdit.setOnClickListener {
77 | saveEdit()
78 | }
79 | iwProfile.setOnClickListener {
80 | getProfileImage()
81 | }
82 | }
83 |
84 | }
85 |
86 | private fun saveEdit() {
87 |
88 | val name = binding.edtName.text.toString()
89 | val surname = binding.edtSurname.text.toString()
90 | val bio = binding.edtBio.text.toString()
91 | val webSite = binding.edtWebsite.text.toString()
92 |
93 | pickedImage?.let { viewModel.saveEdit(name, surname, bio, webSite, it) }
94 | viewModel._loadingState.observe(viewLifecycleOwner) { loading ->
95 | edit_progress_bar.visibility = if (loading == true) View.VISIBLE else View.GONE
96 | }
97 | viewModel._errorState.observe(viewLifecycleOwner) { error ->
98 | if (!error.isNullOrEmpty()) Snackbar.make(requireView(), error, Snackbar.LENGTH_LONG)
99 | .show()
100 | }
101 | view?.let {
102 | Navigation.findNavController(it).navigate(R.id.action_editFragment_to_profileFragment)
103 | }
104 |
105 | }
106 |
107 | private fun closeEdit() {
108 | view?.let {
109 | Navigation.findNavController(it).navigate(R.id.action_editFragment_to_profileFragment)
110 |
111 | }
112 | }
113 |
114 | private fun getProfileImage() {
115 | if (ContextCompat.checkSelfPermission(
116 | this.requireContext(),
117 | android.Manifest.permission.READ_EXTERNAL_STORAGE
118 | ) != PackageManager.PERMISSION_GRANTED
119 | ) {
120 | ActivityCompat.requestPermissions(
121 | this.requireActivity(),
122 | arrayOf(android.Manifest.permission.READ_EXTERNAL_STORAGE),
123 | 1
124 | )
125 | } else {
126 | val intent = Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI)
127 | startActivityForResult(intent, 2)
128 | }
129 | }
130 |
131 | override fun onRequestPermissionsResult(
132 | requestCode: Int,
133 | permissions: Array,
134 | grantResults: IntArray
135 | ) {
136 |
137 | if (requestCode == 1) {
138 | if (grantResults.size > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
139 | val intent =
140 | Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI)
141 | startActivityForResult(intent, 2)
142 |
143 | }
144 | }
145 | super.onRequestPermissionsResult(requestCode, permissions, grantResults)
146 | }
147 |
148 | override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
149 | if (requestCode == 2 && resultCode == Activity.RESULT_OK && data != null) {
150 |
151 | pickedImage = data.data
152 |
153 | if (pickedImage != null) {
154 |
155 | if (Build.VERSION.SDK_INT >= 28) {
156 | val source = ImageDecoder.createSource(this.contentResolver, pickedImage!!)
157 | bitmap = ImageDecoder.decodeBitmap(source)
158 | binding.iwProfile.setImageBitmap(bitmap)
159 |
160 | } else {
161 | bitmap =
162 | MediaStore.Images.Media.getBitmap(this.contentResolver, pickedImage)
163 | binding.iwProfile.setImageBitmap(bitmap)
164 | }
165 | }
166 | }
167 | super.onActivityResult(requestCode, resultCode, data)
168 | }
169 |
170 |
171 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/suveybesena/instagramclone/presentation/share/ShareFragment.kt:
--------------------------------------------------------------------------------
1 | package com.suveybesena.instagramclone.presentation.share
2 |
3 | import android.app.Activity
4 | import android.content.ContentResolver
5 | import android.content.Intent
6 | import android.content.pm.PackageManager
7 | import android.graphics.Bitmap
8 | import android.graphics.ImageDecoder
9 | import android.net.Uri
10 | import android.os.Build
11 | import android.os.Bundle
12 | import android.provider.MediaStore
13 | import androidx.fragment.app.Fragment
14 | import android.view.LayoutInflater
15 | import android.view.View
16 | import android.view.ViewGroup
17 | import androidx.core.app.ActivityCompat
18 | import androidx.core.content.ContextCompat
19 | import androidx.fragment.app.viewModels
20 | import androidx.navigation.Navigation
21 | import androidx.navigation.fragment.navArgs
22 | import com.google.android.material.snackbar.Snackbar
23 | import com.suveybesena.instagramclone.R
24 | import com.suveybesena.instagramclone.databinding.FragmentShareBinding
25 | import dagger.hilt.android.AndroidEntryPoint
26 |
27 | @AndroidEntryPoint
28 | class ShareFragment() : Fragment() {
29 |
30 | private lateinit var contentResolver: ContentResolver
31 | private val viewModel: ShareViewModel by viewModels()
32 | private lateinit var binding: FragmentShareBinding
33 |
34 | var pickedImage: Uri? = null
35 | var bitmap: Bitmap? = null
36 | private val args: ShareFragmentArgs by navArgs()
37 |
38 | override fun onCreate(savedInstanceState: Bundle?) {
39 | super.onCreate(savedInstanceState)
40 | }
41 |
42 | override fun onCreateView(
43 | inflater: LayoutInflater, container: ViewGroup?,
44 | savedInstanceState: Bundle?
45 | ): View? {
46 | binding = FragmentShareBinding.inflate(inflater)
47 | return binding.root
48 | }
49 |
50 | override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
51 | super.onViewCreated(view, savedInstanceState)
52 | contentResolver = requireActivity().contentResolver
53 |
54 | initList()
55 |
56 | }
57 |
58 | private fun savePost() {
59 | if (!args.location.isNullOrEmpty()) {
60 | val location = args.location
61 | val comment = binding.edtComment.toString()
62 | pickedImage?.let { pickedImage ->
63 | if (location != null) {
64 | viewModel.imageDownloader(
65 | pickedImage,
66 | comment,
67 | location
68 | )
69 | }
70 |
71 | }
72 | Navigation.findNavController(requireView())
73 | .navigate(R.id.action_shareFragment_to_homeFragment)
74 | }
75 |
76 | viewModel._errorState.observe(viewLifecycleOwner) { error ->
77 | if (error != null) Snackbar.make(requireView(), error, Snackbar.LENGTH_LONG).show()
78 | }
79 |
80 | viewModel._loadingState.observe(viewLifecycleOwner) { loading ->
81 | binding.shareProgressBar.visibility = if (loading == true) View.VISIBLE else View.GONE
82 | }
83 |
84 | }
85 |
86 | private fun initList() {
87 | if (!args.location.isNullOrEmpty()) {
88 | binding.twGetLocation.text = args.location
89 | }
90 |
91 | binding.apply {
92 | imwShare.setOnClickListener {
93 | imwPickedImage()
94 | }
95 | btwSavePost.setOnClickListener {
96 | savePost()
97 | }
98 | btwClosePost.setOnClickListener {
99 | closePost()
100 | }
101 | twGetLocation.setOnClickListener {
102 | getLocation()
103 | }
104 | }
105 |
106 | }
107 |
108 | private fun getLocation() {
109 | view?.let { Navigation.findNavController(it).navigate(R.id.getUserLocationFragment) }
110 | }
111 |
112 | private fun closePost() {
113 | view?.let {
114 | Navigation.findNavController(it).navigate(R.id.action_shareFragment_to_homeFragment)
115 | }
116 | }
117 |
118 | private fun imwPickedImage() {
119 | if (ContextCompat.checkSelfPermission(
120 | this.requireContext(),
121 | android.Manifest.permission.READ_EXTERNAL_STORAGE
122 | ) != PackageManager.PERMISSION_GRANTED
123 | ) {
124 | ActivityCompat.requestPermissions(
125 | this.requireActivity(),
126 | arrayOf(android.Manifest.permission.READ_EXTERNAL_STORAGE),
127 | 1
128 | )
129 | } else {
130 | val intent = Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI)
131 | startActivityForResult(intent, 2)
132 | }
133 | }
134 |
135 | override fun onRequestPermissionsResult(
136 | requestCode: Int,
137 | permissions: Array,
138 | grantResults: IntArray
139 | ) {
140 |
141 | if (requestCode == 1) {
142 | if (grantResults.size > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
143 | val intent =
144 | Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI)
145 | startActivityForResult(intent, 2)
146 |
147 | }
148 | }
149 | super.onRequestPermissionsResult(requestCode, permissions, grantResults)
150 | }
151 |
152 | override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
153 | if (requestCode == 2 && resultCode == Activity.RESULT_OK && data != null) {
154 |
155 | pickedImage = data.data
156 |
157 | if (pickedImage != null) {
158 |
159 | if (Build.VERSION.SDK_INT >= 28) {
160 | val source = ImageDecoder.createSource(this.contentResolver, pickedImage!!)
161 | bitmap = ImageDecoder.decodeBitmap(source)
162 | binding.imwShare.setImageBitmap(bitmap)
163 |
164 | } else {
165 | bitmap =
166 | MediaStore.Images.Media.getBitmap(this.contentResolver, pickedImage)
167 | binding.imwShare.setImageBitmap(bitmap)
168 | }
169 | }
170 | }
171 | super.onActivityResult(requestCode, resultCode, data)
172 | }
173 | }
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env sh
2 |
3 | #
4 | # Copyright 2015 the original author or authors.
5 | #
6 | # Licensed under the Apache License, Version 2.0 (the "License");
7 | # you may not use this file except in compliance with the License.
8 | # You may obtain a copy of the License at
9 | #
10 | # https://www.apache.org/licenses/LICENSE-2.0
11 | #
12 | # Unless required by applicable law or agreed to in writing, software
13 | # distributed under the License is distributed on an "AS IS" BASIS,
14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | # See the License for the specific language governing permissions and
16 | # limitations under the License.
17 | #
18 |
19 | ##############################################################################
20 | ##
21 | ## Gradle start up script for UN*X
22 | ##
23 | ##############################################################################
24 |
25 | # Attempt to set APP_HOME
26 | # Resolve links: $0 may be a link
27 | PRG="$0"
28 | # Need this for relative symlinks.
29 | while [ -h "$PRG" ] ; do
30 | ls=`ls -ld "$PRG"`
31 | link=`expr "$ls" : '.*-> \(.*\)$'`
32 | if expr "$link" : '/.*' > /dev/null; then
33 | PRG="$link"
34 | else
35 | PRG=`dirname "$PRG"`"/$link"
36 | fi
37 | done
38 | SAVED="`pwd`"
39 | cd "`dirname \"$PRG\"`/" >/dev/null
40 | APP_HOME="`pwd -P`"
41 | cd "$SAVED" >/dev/null
42 |
43 | APP_NAME="Gradle"
44 | APP_BASE_NAME=`basename "$0"`
45 |
46 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
47 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
48 |
49 | # Use the maximum available, or set MAX_FD != -1 to use that value.
50 | MAX_FD="maximum"
51 |
52 | warn () {
53 | echo "$*"
54 | }
55 |
56 | die () {
57 | echo
58 | echo "$*"
59 | echo
60 | exit 1
61 | }
62 |
63 | # OS specific support (must be 'true' or 'false').
64 | cygwin=false
65 | msys=false
66 | darwin=false
67 | nonstop=false
68 | case "`uname`" in
69 | CYGWIN* )
70 | cygwin=true
71 | ;;
72 | Darwin* )
73 | darwin=true
74 | ;;
75 | MINGW* )
76 | msys=true
77 | ;;
78 | NONSTOP* )
79 | nonstop=true
80 | ;;
81 | esac
82 |
83 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
84 |
85 |
86 | # Determine the Java command to use to start the JVM.
87 | if [ -n "$JAVA_HOME" ] ; then
88 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
89 | # IBM's JDK on AIX uses strange locations for the executables
90 | JAVACMD="$JAVA_HOME/jre/sh/java"
91 | else
92 | JAVACMD="$JAVA_HOME/bin/java"
93 | fi
94 | if [ ! -x "$JAVACMD" ] ; then
95 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
96 |
97 | Please set the JAVA_HOME variable in your environment to match the
98 | location of your Java installation."
99 | fi
100 | else
101 | JAVACMD="java"
102 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
103 |
104 | Please set the JAVA_HOME variable in your environment to match the
105 | location of your Java installation."
106 | fi
107 |
108 | # Increase the maximum file descriptors if we can.
109 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
110 | MAX_FD_LIMIT=`ulimit -H -n`
111 | if [ $? -eq 0 ] ; then
112 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
113 | MAX_FD="$MAX_FD_LIMIT"
114 | fi
115 | ulimit -n $MAX_FD
116 | if [ $? -ne 0 ] ; then
117 | warn "Could not set maximum file descriptor limit: $MAX_FD"
118 | fi
119 | else
120 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
121 | fi
122 | fi
123 |
124 | # For Darwin, add options to specify how the application appears in the dock
125 | if $darwin; then
126 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
127 | fi
128 |
129 | # For Cygwin or MSYS, switch paths to Windows format before running java
130 | if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
131 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
132 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
133 |
134 | JAVACMD=`cygpath --unix "$JAVACMD"`
135 |
136 | # We build the pattern for arguments to be converted via cygpath
137 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
138 | SEP=""
139 | for dir in $ROOTDIRSRAW ; do
140 | ROOTDIRS="$ROOTDIRS$SEP$dir"
141 | SEP="|"
142 | done
143 | OURCYGPATTERN="(^($ROOTDIRS))"
144 | # Add a user-defined pattern to the cygpath arguments
145 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
146 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
147 | fi
148 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
149 | i=0
150 | for arg in "$@" ; do
151 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
152 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
153 |
154 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
155 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
156 | else
157 | eval `echo args$i`="\"$arg\""
158 | fi
159 | i=`expr $i + 1`
160 | done
161 | case $i in
162 | 0) set -- ;;
163 | 1) set -- "$args0" ;;
164 | 2) set -- "$args0" "$args1" ;;
165 | 3) set -- "$args0" "$args1" "$args2" ;;
166 | 4) set -- "$args0" "$args1" "$args2" "$args3" ;;
167 | 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
168 | 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
169 | 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
170 | 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
171 | 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
172 | esac
173 | fi
174 |
175 | # Escape application args
176 | save () {
177 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
178 | echo " "
179 | }
180 | APP_ARGS=`save "$@"`
181 |
182 | # Collect all arguments for the java command, following the shell quoting and substitution rules
183 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
184 |
185 | exec "$JAVACMD" "$@"
186 |
--------------------------------------------------------------------------------