├── app
├── .gitignore
├── src
│ ├── main
│ │ ├── assets
│ │ │ ├── Raleway-Bold.ttf
│ │ │ ├── Raleway-Medium.ttf
│ │ │ └── Raleway-Regular.ttf
│ │ ├── ic_launcher-playstore.png
│ │ ├── res
│ │ │ ├── drawable-v24
│ │ │ │ ├── ic_done.png
│ │ │ │ ├── ic_edit.png
│ │ │ │ ├── ic_cancel.png
│ │ │ │ ├── ic_delete.png
│ │ │ │ ├── ic_task_image.png
│ │ │ │ ├── pm_logo_white.png
│ │ │ │ ├── intro_background.png
│ │ │ │ ├── ic_splash_background.png
│ │ │ │ ├── white_border_shape_button_rounded.xml
│ │ │ │ ├── shape_button_rounded.xml
│ │ │ │ └── ic_launcher_foreground.xml
│ │ │ ├── mipmap-hdpi
│ │ │ │ ├── ic_launcher.png
│ │ │ │ ├── ic_launcher_round.png
│ │ │ │ └── ic_launcher_foreground.png
│ │ │ ├── mipmap-mdpi
│ │ │ │ ├── ic_launcher.png
│ │ │ │ ├── ic_launcher_round.png
│ │ │ │ └── ic_launcher_foreground.png
│ │ │ ├── mipmap-xhdpi
│ │ │ │ ├── ic_launcher.png
│ │ │ │ ├── ic_launcher_round.png
│ │ │ │ └── ic_launcher_foreground.png
│ │ │ ├── mipmap-xxhdpi
│ │ │ │ ├── ic_launcher.png
│ │ │ │ ├── ic_launcher_round.png
│ │ │ │ └── ic_launcher_foreground.png
│ │ │ ├── mipmap-xxxhdpi
│ │ │ │ ├── ic_launcher.png
│ │ │ │ ├── ic_launcher_round.png
│ │ │ │ └── ic_launcher_foreground.png
│ │ │ ├── drawable-hdpi
│ │ │ │ ├── ic_background.png
│ │ │ │ ├── ic_task_image.png
│ │ │ │ ├── intro_background.png
│ │ │ │ └── ic_splash_background.png
│ │ │ ├── drawable-ldpi
│ │ │ │ ├── ic_background.png
│ │ │ │ ├── ic_task_image.png
│ │ │ │ ├── intro_background.png
│ │ │ │ └── ic_splash_background.png
│ │ │ ├── drawable-mdpi
│ │ │ │ ├── ic_background.png
│ │ │ │ ├── ic_task_image.png
│ │ │ │ ├── intro_background.png
│ │ │ │ └── ic_splash_background.png
│ │ │ ├── drawable-xhdpi
│ │ │ │ ├── ic_background.png
│ │ │ │ ├── ic_task_image.png
│ │ │ │ ├── intro_background.png
│ │ │ │ └── ic_splash_background.png
│ │ │ ├── drawable-xxhdpi
│ │ │ │ ├── ic_background.png
│ │ │ │ ├── ic_task_image.png
│ │ │ │ ├── intro_background.png
│ │ │ │ └── ic_splash_background.png
│ │ │ ├── drawable-xxxhdpi
│ │ │ │ ├── ic_background.png
│ │ │ │ ├── ic_task_image.png
│ │ │ │ ├── intro_background.png
│ │ │ │ └── ic_splash_background.png
│ │ │ ├── values
│ │ │ │ ├── ic_launcher_background.xml
│ │ │ │ ├── strings.xml
│ │ │ │ ├── colors.xml
│ │ │ │ └── styles.xml
│ │ │ ├── drawable
│ │ │ │ ├── dialog_rounded_shape.xml
│ │ │ │ ├── ic_add_member.xml
│ │ │ │ ├── ic_add_white.xml
│ │ │ │ ├── ic_check_blue.xml
│ │ │ │ ├── ic_check_done.xml
│ │ │ │ ├── ic_back_white.xml
│ │ │ │ ├── ic_board_place_holder.xml
│ │ │ │ ├── ic_delete_white.xml
│ │ │ │ ├── ic_back_black.xml
│ │ │ │ ├── ic_navigation_manu.xml
│ │ │ │ ├── ic_user_place_holder.xml
│ │ │ │ ├── ic_nav_sign_out.xml
│ │ │ │ ├── ic_nav_user.xml
│ │ │ │ └── ic_launcher_background.xml
│ │ │ ├── mipmap-anydpi-v26
│ │ │ │ ├── ic_launcher.xml
│ │ │ │ └── ic_launcher_round.xml
│ │ │ ├── menu
│ │ │ │ ├── menu_members.xml
│ │ │ │ ├── menu_add_member.xml
│ │ │ │ ├── menu_delete_card.xml
│ │ │ │ └── activity_main_drawer.xml
│ │ │ └── layout
│ │ │ │ ├── activity_base.xml
│ │ │ │ ├── progress_dialog.xml
│ │ │ │ ├── item_label_color.xml
│ │ │ │ ├── activity_splash.xml
│ │ │ │ ├── activity_main.xml
│ │ │ │ ├── item_card_selected_member.xml
│ │ │ │ ├── app_bar_main.xml
│ │ │ │ ├── activity_task_list.xml
│ │ │ │ ├── nav_header_main.xml
│ │ │ │ ├── main_content.xml
│ │ │ │ ├── activity_members.xml
│ │ │ │ ├── item_card.xml
│ │ │ │ ├── dialog_list.xml
│ │ │ │ ├── item_board.xml
│ │ │ │ ├── item_member.xml
│ │ │ │ ├── activity_login.xml
│ │ │ │ ├── dialog_serach_member.xml
│ │ │ │ ├── activity_create_board.xml
│ │ │ │ ├── activity_sign_in.xml
│ │ │ │ ├── activity_my_profile.xml
│ │ │ │ ├── activity_sign_up.xml
│ │ │ │ └── activity_card_details.xml
│ │ ├── java
│ │ │ └── learning
│ │ │ │ └── self
│ │ │ │ └── kotlin
│ │ │ │ └── projectmanager
│ │ │ │ ├── models
│ │ │ │ ├── SelectedMembers.kt
│ │ │ │ ├── Task.kt
│ │ │ │ ├── Card.kt
│ │ │ │ ├── User.kt
│ │ │ │ └── Board.kt
│ │ │ │ ├── activities
│ │ │ │ ├── LoginActivity.kt
│ │ │ │ ├── SplashActivity.kt
│ │ │ │ ├── BaseActivity.kt
│ │ │ │ ├── SignInActivity.kt
│ │ │ │ ├── SignUpActivity.kt
│ │ │ │ ├── CreateBoardActivity.kt
│ │ │ │ ├── MainActivity.kt
│ │ │ │ ├── TaskListActivity.kt
│ │ │ │ ├── MyProfileActivity.kt
│ │ │ │ └── MembersActivity.kt
│ │ │ │ ├── dialogs
│ │ │ │ ├── LabelColorListDialog.kt
│ │ │ │ └── MembersListDialog.kt
│ │ │ │ ├── adapters
│ │ │ │ ├── LabelColorListItemAdapter.kt
│ │ │ │ ├── CardMemberListItemAdapter.kt
│ │ │ │ ├── BoardItemAdapter.kt
│ │ │ │ ├── MemberItemAdapter.kt
│ │ │ │ └── CardListItemAdapter.kt
│ │ │ │ ├── utils
│ │ │ │ └── Constants.kt
│ │ │ │ ├── fcm
│ │ │ │ └── MyFirebaseMessagingService.kt
│ │ │ │ └── firebase
│ │ │ │ └── FireStoreHandler.kt
│ │ └── AndroidManifest.xml
│ ├── test
│ │ └── java
│ │ │ └── learning
│ │ │ └── self
│ │ │ └── kotlin
│ │ │ └── projectmanager
│ │ │ └── ExampleUnitTest.kt
│ └── androidTest
│ │ └── java
│ │ └── learning
│ │ └── self
│ │ └── kotlin
│ │ └── projectmanager
│ │ └── ExampleInstrumentedTest.kt
├── proguard-rules.pro
├── google-services.json
└── build.gradle
├── settings.gradle
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── .idea
├── codeStyles
│ ├── codeStyleConfig.xml
│ └── Project.xml
├── vcs.xml
├── misc.xml
├── runConfigurations.xml
├── gradle.xml
└── jarRepositories.xml
├── .gitignore
├── gradle.properties
├── gradlew.bat
├── README.md
└── gradlew
/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app'
2 | rootProject.name = "ProjectManager"
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dimatep/ProjectManager/HEAD/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/app/src/main/assets/Raleway-Bold.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dimatep/ProjectManager/HEAD/app/src/main/assets/Raleway-Bold.ttf
--------------------------------------------------------------------------------
/app/src/main/assets/Raleway-Medium.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dimatep/ProjectManager/HEAD/app/src/main/assets/Raleway-Medium.ttf
--------------------------------------------------------------------------------
/app/src/main/assets/Raleway-Regular.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dimatep/ProjectManager/HEAD/app/src/main/assets/Raleway-Regular.ttf
--------------------------------------------------------------------------------
/app/src/main/ic_launcher-playstore.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dimatep/ProjectManager/HEAD/app/src/main/ic_launcher-playstore.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-v24/ic_done.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dimatep/ProjectManager/HEAD/app/src/main/res/drawable-v24/ic_done.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-v24/ic_edit.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dimatep/ProjectManager/HEAD/app/src/main/res/drawable-v24/ic_edit.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-v24/ic_cancel.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dimatep/ProjectManager/HEAD/app/src/main/res/drawable-v24/ic_cancel.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-v24/ic_delete.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dimatep/ProjectManager/HEAD/app/src/main/res/drawable-v24/ic_delete.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dimatep/ProjectManager/HEAD/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dimatep/ProjectManager/HEAD/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dimatep/ProjectManager/HEAD/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-v24/ic_task_image.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dimatep/ProjectManager/HEAD/app/src/main/res/drawable-v24/ic_task_image.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-v24/pm_logo_white.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dimatep/ProjectManager/HEAD/app/src/main/res/drawable-v24/pm_logo_white.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dimatep/ProjectManager/HEAD/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dimatep/ProjectManager/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-hdpi/ic_background.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dimatep/ProjectManager/HEAD/app/src/main/res/drawable-hdpi/ic_background.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-hdpi/ic_task_image.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dimatep/ProjectManager/HEAD/app/src/main/res/drawable-hdpi/ic_task_image.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-ldpi/ic_background.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dimatep/ProjectManager/HEAD/app/src/main/res/drawable-ldpi/ic_background.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-ldpi/ic_task_image.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dimatep/ProjectManager/HEAD/app/src/main/res/drawable-ldpi/ic_task_image.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-mdpi/ic_background.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dimatep/ProjectManager/HEAD/app/src/main/res/drawable-mdpi/ic_background.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-mdpi/ic_task_image.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dimatep/ProjectManager/HEAD/app/src/main/res/drawable-mdpi/ic_task_image.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-v24/intro_background.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dimatep/ProjectManager/HEAD/app/src/main/res/drawable-v24/intro_background.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xhdpi/ic_background.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dimatep/ProjectManager/HEAD/app/src/main/res/drawable-xhdpi/ic_background.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xhdpi/ic_task_image.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dimatep/ProjectManager/HEAD/app/src/main/res/drawable-xhdpi/ic_task_image.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxhdpi/ic_background.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dimatep/ProjectManager/HEAD/app/src/main/res/drawable-xxhdpi/ic_background.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxhdpi/ic_task_image.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dimatep/ProjectManager/HEAD/app/src/main/res/drawable-xxhdpi/ic_task_image.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dimatep/ProjectManager/HEAD/app/src/main/res/mipmap-hdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dimatep/ProjectManager/HEAD/app/src/main/res/mipmap-mdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-hdpi/intro_background.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dimatep/ProjectManager/HEAD/app/src/main/res/drawable-hdpi/intro_background.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-ldpi/intro_background.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dimatep/ProjectManager/HEAD/app/src/main/res/drawable-ldpi/intro_background.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-mdpi/intro_background.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dimatep/ProjectManager/HEAD/app/src/main/res/drawable-mdpi/intro_background.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xhdpi/intro_background.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dimatep/ProjectManager/HEAD/app/src/main/res/drawable-xhdpi/intro_background.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxxhdpi/ic_background.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dimatep/ProjectManager/HEAD/app/src/main/res/drawable-xxxhdpi/ic_background.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxxhdpi/ic_task_image.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dimatep/ProjectManager/HEAD/app/src/main/res/drawable-xxxhdpi/ic_task_image.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dimatep/ProjectManager/HEAD/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dimatep/ProjectManager/HEAD/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-hdpi/ic_splash_background.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dimatep/ProjectManager/HEAD/app/src/main/res/drawable-hdpi/ic_splash_background.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-ldpi/ic_splash_background.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dimatep/ProjectManager/HEAD/app/src/main/res/drawable-ldpi/ic_splash_background.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-mdpi/ic_splash_background.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dimatep/ProjectManager/HEAD/app/src/main/res/drawable-mdpi/ic_splash_background.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-v24/ic_splash_background.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dimatep/ProjectManager/HEAD/app/src/main/res/drawable-v24/ic_splash_background.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxhdpi/intro_background.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dimatep/ProjectManager/HEAD/app/src/main/res/drawable-xxhdpi/intro_background.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxxhdpi/intro_background.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dimatep/ProjectManager/HEAD/app/src/main/res/drawable-xxxhdpi/intro_background.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dimatep/ProjectManager/HEAD/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dimatep/ProjectManager/HEAD/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dimatep/ProjectManager/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xhdpi/ic_splash_background.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dimatep/ProjectManager/HEAD/app/src/main/res/drawable-xhdpi/ic_splash_background.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxhdpi/ic_splash_background.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dimatep/ProjectManager/HEAD/app/src/main/res/drawable-xxhdpi/ic_splash_background.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dimatep/ProjectManager/HEAD/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dimatep/ProjectManager/HEAD/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxxhdpi/ic_splash_background.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dimatep/ProjectManager/HEAD/app/src/main/res/drawable-xxxhdpi/ic_splash_background.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dimatep/ProjectManager/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png
--------------------------------------------------------------------------------
/app/src/main/res/values/ic_launcher_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #FFFFFF
4 |
--------------------------------------------------------------------------------
/.idea/codeStyles/codeStyleConfig.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.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 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Mon Jul 06 10:34:12 IDT 2020
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-6.1.1-all.zip
7 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/dialog_rounded_shape.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/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_add_member.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_add_white.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_check_blue.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_check_done.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_back_white.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/menu/menu_members.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_board_place_holder.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_delete_white.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | Project Manager
3 |
4 | project_manager_notification_channel_id
5 | AAAADHSe490:APA91bFoGC_rYPMY-OeuL-5OvAAXBS3hANCoh0BHGNIedKXdsbbgLrXXF1lVTRfRYhGD1wKTdMCSVihwIH5niZH3qulQgQSBl4V9gUOdeGGWZ3HZarJ7mzxpPSqyL3hG57cDOJfeieDZ
6 |
7 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_back_black.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/menu/menu_add_member.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/app/src/main/res/menu/menu_delete_card.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_navigation_manu.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable-v24/white_border_shape_button_rounded.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable-v24/shape_button_rounded.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
5 |
6 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_base.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
--------------------------------------------------------------------------------
/app/src/test/java/learning/self/kotlin/projectmanager/ExampleUnitTest.kt:
--------------------------------------------------------------------------------
1 | package learning.self.kotlin.projectmanager
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_user_place_holder.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/menu/activity_main_drawer.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_nav_sign_out.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/.idea/runConfigurations.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
11 |
12 |
--------------------------------------------------------------------------------
/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #0C90F1
4 | #0591F8
5 | #0C90F1
6 |
7 | #363A43
8 | #7A8089
9 | #FFFFFF
10 | #F72400
11 |
12 | #E1D6D6
13 | #F7F6FA
14 | #F8F8F8
15 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_nav_user.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
9 |
10 |
11 |
15 |
16 |
18 |
19 |
21 |
--------------------------------------------------------------------------------
/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/learning/self/kotlin/projectmanager/ExampleInstrumentedTest.kt:
--------------------------------------------------------------------------------
1 | package learning.self.kotlin.projectmanager
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("learning.self.kotlin.projectmanager", appContext.packageName)
23 | }
24 | }
--------------------------------------------------------------------------------
/app/src/main/res/layout/progress_dialog.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
13 |
14 |
22 |
23 |
--------------------------------------------------------------------------------
/.idea/gradle.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/app/src/main/java/learning/self/kotlin/projectmanager/models/SelectedMembers.kt:
--------------------------------------------------------------------------------
1 | package learning.self.kotlin.projectmanager.models
2 |
3 | import android.os.Parcel
4 | import android.os.Parcelable
5 |
6 | data class SelectedMembers (
7 | var id : String = "",
8 | var image : String = ""
9 | ) : Parcelable {
10 | constructor(parcel: Parcel) : this(
11 | parcel.readString()!!,
12 | parcel.readString()!!
13 | ) {
14 | }
15 |
16 | override fun writeToParcel(dest: Parcel, flags: Int) = with(dest) {
17 | writeString(id)
18 | writeString(image)
19 | }
20 |
21 | override fun describeContents() = 0
22 |
23 | companion object CREATOR : Parcelable.Creator {
24 | override fun createFromParcel(parcel: Parcel): SelectedMembers {
25 | return SelectedMembers(parcel)
26 | }
27 |
28 | override fun newArray(size: Int): Array {
29 | return arrayOfNulls(size)
30 | }
31 | }
32 | }
--------------------------------------------------------------------------------
/app/src/main/res/layout/item_label_color.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
14 |
15 |
24 |
25 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_splash.xml:
--------------------------------------------------------------------------------
1 |
2 |
11 |
12 |
16 |
17 |
26 |
27 |
--------------------------------------------------------------------------------
/app/src/main/java/learning/self/kotlin/projectmanager/models/Task.kt:
--------------------------------------------------------------------------------
1 | package learning.self.kotlin.projectmanager.models
2 |
3 | import android.os.Parcel
4 | import android.os.Parcelable
5 |
6 | data class Task (
7 | var title : String = "",
8 | val createdBy : String = "",
9 | var cards : ArrayList = ArrayList()
10 | ) : Parcelable {
11 | constructor(parcel: Parcel) : this(
12 | parcel.readString()!!,
13 | parcel.readString()!!,
14 | parcel.createTypedArrayList(Card.CREATOR)!!
15 | )
16 |
17 | override fun writeToParcel(parcel: Parcel, flags: Int) = with(parcel) {
18 | parcel.writeString(title)
19 | parcel.writeString(createdBy)
20 | parcel.writeTypedList(cards)
21 | }
22 |
23 | override fun describeContents() = 0
24 |
25 | companion object CREATOR : Parcelable.Creator {
26 | override fun createFromParcel(parcel: Parcel): Task {
27 | return Task(parcel)
28 | }
29 |
30 | override fun newArray(size: Int): Array {
31 | return arrayOfNulls(size)
32 | }
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
11 |
12 |
16 |
17 |
25 |
26 |
--------------------------------------------------------------------------------
/.idea/jarRepositories.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/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
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 | # Automatically convert third-party libraries to use AndroidX
19 | android.enableJetifier=true
20 | # Kotlin code style for this project: "official" or "obsolete":
21 | kotlin.code.style=official
--------------------------------------------------------------------------------
/app/google-services.json:
--------------------------------------------------------------------------------
1 | {
2 | "project_info": {
3 | "project_number": "53496177629",
4 | "firebase_url": "https://projectmanager-dimatep.firebaseio.com",
5 | "project_id": "projectmanager-dimatep",
6 | "storage_bucket": "projectmanager-dimatep.appspot.com"
7 | },
8 | "client": [
9 | {
10 | "client_info": {
11 | "mobilesdk_app_id": "1:53496177629:android:094e5eb18e57ea88f68c34",
12 | "android_client_info": {
13 | "package_name": "learning.self.kotlin.projectmanager"
14 | }
15 | },
16 | "oauth_client": [
17 | {
18 | "client_id": "53496177629-3eoflm8pce4ubd95kp9okhnu8mtfboiu.apps.googleusercontent.com",
19 | "client_type": 3
20 | }
21 | ],
22 | "api_key": [
23 | {
24 | "current_key": "AIzaSyDN5KnlnXkr39ApqtTs_dq4mv-tTFjbtts"
25 | }
26 | ],
27 | "services": {
28 | "appinvite_service": {
29 | "other_platform_oauth_client": [
30 | {
31 | "client_id": "53496177629-3eoflm8pce4ubd95kp9okhnu8mtfboiu.apps.googleusercontent.com",
32 | "client_type": 3
33 | }
34 | ]
35 | }
36 | }
37 | }
38 | ],
39 | "configuration_version": "1"
40 | }
--------------------------------------------------------------------------------
/app/src/main/java/learning/self/kotlin/projectmanager/models/Card.kt:
--------------------------------------------------------------------------------
1 | package learning.self.kotlin.projectmanager.models
2 |
3 | import android.os.Parcel
4 | import android.os.Parcelable
5 |
6 | data class Card (
7 | val name : String = "",
8 | val createdBy : String = "",
9 | val assignedTo : ArrayList = ArrayList(),
10 | var labelColor : String = "",
11 | val dueDate : Long = 0
12 | ) : Parcelable {
13 | constructor(parcel: Parcel) : this(
14 | parcel.readString()!!,
15 | parcel.readString()!!,
16 | parcel.createStringArrayList()!!,
17 | parcel.readString()!!,
18 | parcel.readLong()!!
19 | ) {
20 | }
21 |
22 | override fun writeToParcel(dest: Parcel, flags: Int) = with(dest){
23 | writeString(name)
24 | writeString(createdBy)
25 | writeStringList(assignedTo)
26 | writeString(labelColor)
27 | writeLong(dueDate)
28 | }
29 |
30 | override fun describeContents() = 0
31 |
32 | companion object CREATOR : Parcelable.Creator {
33 | override fun createFromParcel(parcel: Parcel): Card {
34 | return Card(parcel)
35 | }
36 |
37 | override fun newArray(size: Int): Array {
38 | return arrayOfNulls(size)
39 | }
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/item_card_selected_member.xml:
--------------------------------------------------------------------------------
1 |
2 |
10 |
11 |
21 |
22 |
32 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/app_bar_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
11 |
12 |
18 |
19 |
20 |
21 |
22 |
23 |
30 |
31 |
32 |
--------------------------------------------------------------------------------
/app/src/main/java/learning/self/kotlin/projectmanager/models/User.kt:
--------------------------------------------------------------------------------
1 | package learning.self.kotlin.projectmanager.models
2 |
3 | import android.annotation.SuppressLint
4 | import android.os.Parcel
5 | import android.os.Parcelable
6 |
7 |
8 | data class User (
9 | val id : String = "",
10 | val name : String = "",
11 | val email : String = "",
12 | val image : String = "",
13 | val mobile : Long = 0,
14 | val fcmToken : String = "",
15 | var selected : Boolean = false
16 | ) : Parcelable {
17 | constructor(parcel: Parcel) : this(
18 | parcel.readString()!!,
19 | parcel.readString()!!,
20 | parcel.readString()!!,
21 | parcel.readString()!!,
22 | parcel.readLong()!!,
23 | parcel.readString()!!
24 | ) {}
25 |
26 | override fun writeToParcel(parcel: Parcel, flags: Int) {
27 | parcel.writeString(id)
28 | parcel.writeString(name)
29 | parcel.writeString(email)
30 | parcel.writeString(image)
31 | parcel.writeLong(mobile)
32 | parcel.writeString(fcmToken)
33 | }
34 |
35 | override fun describeContents() = 0
36 |
37 | companion object CREATOR : Parcelable.Creator {
38 | override fun createFromParcel(parcel: Parcel): User {
39 | return User(parcel)
40 | }
41 |
42 | override fun newArray(size: Int): Array {
43 | return arrayOfNulls(size)
44 | }
45 | }
46 | }
--------------------------------------------------------------------------------
/app/src/main/java/learning/self/kotlin/projectmanager/models/Board.kt:
--------------------------------------------------------------------------------
1 | package learning.self.kotlin.projectmanager.models
2 |
3 | import android.os.Parcel
4 | import android.os.Parcelable
5 |
6 | data class Board(
7 | val name: String = "",
8 | val image: String = "",
9 | val createdBy: String = "",
10 | val assignedTo: ArrayList = ArrayList(),
11 | var documentID: String = "",
12 | var taskList : ArrayList = ArrayList()
13 | ) : Parcelable {
14 | constructor(source: Parcel) : this(
15 | source.readString()!!,
16 | source.readString()!!,
17 | source.readString()!!,
18 | source.createStringArrayList()!!,
19 | source.readString()!!,
20 | source.createTypedArrayList(Task.CREATOR)!!
21 | )
22 |
23 | override fun describeContents() = 0
24 |
25 | override fun writeToParcel(dest: Parcel, flags: Int) = with(dest) {
26 | writeString(name)
27 | writeString(image)
28 | writeString(createdBy)
29 | writeStringList(assignedTo)
30 | writeString(documentID)
31 | writeTypedList(taskList)
32 | }
33 |
34 | companion object {
35 | @JvmField
36 | val CREATOR: Parcelable.Creator = object : Parcelable.Creator {
37 | override fun createFromParcel(source: Parcel): Board = Board(source)
38 | override fun newArray(size: Int): Array = arrayOfNulls(size)
39 | }
40 | }
41 | }
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_task_list.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
14 |
15 |
21 |
22 |
23 |
24 |
29 |
30 |
34 |
35 |
36 |
37 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/nav_header_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
10 |
11 |
20 |
21 |
33 |
34 |
38 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/main_content.xml:
--------------------------------------------------------------------------------
1 |
2 |
14 |
15 |
22 |
23 |
28 |
29 |
37 |
38 |
39 |
--------------------------------------------------------------------------------
/app/src/main/java/learning/self/kotlin/projectmanager/activities/LoginActivity.kt:
--------------------------------------------------------------------------------
1 | package learning.self.kotlin.projectmanager.activities
2 |
3 | import android.content.Intent
4 | import android.graphics.Typeface
5 | import android.os.Bundle
6 | import android.view.WindowManager
7 | import kotlinx.android.synthetic.main.activity_login.*
8 | import learning.self.kotlin.projectmanager.R
9 |
10 |
11 | class LoginActivity : BaseActivity() {
12 | override fun onCreate(savedInstanceState: Bundle?) {
13 | super.onCreate(savedInstanceState)
14 | setContentView(R.layout.activity_login)
15 |
16 | // This is used to hide the status bar and make the splash screen as a full screen activity.
17 | window.setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN)
18 | setFonts()
19 |
20 | sign_up_btn.setOnClickListener {
21 | startActivity(Intent(this, SignUpActivity::class.java))
22 | }
23 |
24 | sign_in_btn.setOnClickListener {
25 | startActivity(Intent(this, SignInActivity::class.java))
26 | }
27 |
28 | }
29 |
30 | private fun setFonts(){
31 | // This is used to get the font from the assets folder and set it to the title textView.
32 | val regularFont: Typeface = Typeface.createFromAsset(assets, "Raleway-Regular.ttf")
33 | val mediumFont: Typeface = Typeface.createFromAsset(assets, "Raleway-Medium.ttf")
34 | val boldFont: Typeface = Typeface.createFromAsset(assets, "Raleway-Bold.ttf")
35 | // set font to text views and buttons
36 | app_name_intro_tv.typeface = boldFont
37 | lets_go_tv.typeface = mediumFont
38 | app_description_tv.typeface = regularFont
39 | sign_in_btn.typeface = regularFont
40 | sign_up_btn.typeface = regularFont
41 | }
42 | }
--------------------------------------------------------------------------------
/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/learning/self/kotlin/projectmanager/dialogs/LabelColorListDialog.kt:
--------------------------------------------------------------------------------
1 | package learning.self.kotlin.projectmanager.dialogs
2 |
3 | import android.app.Dialog
4 | import android.content.Context
5 | import android.os.Bundle
6 | import android.view.LayoutInflater
7 | import android.view.View
8 | import androidx.recyclerview.widget.LinearLayoutManager
9 | import kotlinx.android.synthetic.main.dialog_list.view.*
10 | import learning.self.kotlin.projectmanager.R
11 | import learning.self.kotlin.projectmanager.adapters.LabelColorListItemAdapter
12 |
13 | abstract class LabelColorListDialog (
14 | context : Context,
15 | private var list : ArrayList,
16 | private val title : String = "",
17 | private val mSelectedColor : String = ""
18 | ) : Dialog(context){
19 |
20 | private var adapter : LabelColorListItemAdapter? = null
21 |
22 | override fun onCreate(savedInstanceState: Bundle?) {
23 | super.onCreate(savedInstanceState)
24 |
25 | val view = LayoutInflater.from(context).inflate(R.layout.dialog_list, null)
26 | setContentView(view)
27 | setCanceledOnTouchOutside(true)
28 | setCancelable(true)
29 | setUpRecyclerView(view)
30 | }
31 |
32 | private fun setUpRecyclerView(view : View){
33 | view.dialog_title_tv.text = title
34 |
35 | view.dialog_list_rv.layoutManager = LinearLayoutManager(context)
36 | adapter = LabelColorListItemAdapter(context, list, mSelectedColor)
37 | view.dialog_list_rv.adapter = adapter
38 |
39 | adapter!!.onItemClickListener = object : LabelColorListItemAdapter.OnItemClickListener{
40 | override fun onClick(position: Int, color: String) {
41 | dismiss()
42 | onItemSelected(color)
43 | }
44 | }
45 | }
46 |
47 | protected abstract fun onItemSelected(color : String)
48 | }
--------------------------------------------------------------------------------
/app/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.application'
2 | apply plugin: 'kotlin-android'
3 | apply plugin: 'kotlin-android-extensions'
4 | apply plugin: 'com.google.gms.google-services'
5 | android {
6 | compileSdkVersion 29
7 |
8 | defaultConfig {
9 | applicationId "learning.self.kotlin.projectmanager"
10 | minSdkVersion 24
11 | targetSdkVersion 29
12 | versionCode 1
13 | versionName "1.0"
14 |
15 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
16 | }
17 |
18 | buildTypes {
19 | release {
20 | minifyEnabled false
21 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
22 | }
23 | }
24 | }
25 |
26 | dependencies {
27 | implementation fileTree(dir: "libs", include: ["*.jar"])
28 | implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
29 | implementation 'androidx.core:core-ktx:1.1.0'
30 | implementation 'androidx.appcompat:appcompat:1.1.0'
31 | implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
32 | implementation 'com.google.android.material:material:1.1.0'
33 | //firebase
34 | implementation 'com.google.firebase:firebase-auth:19.2.0'
35 | implementation 'com.google.firebase:firebase-firestore:21.4.0'
36 | implementation 'com.google.firebase:firebase-storage:19.1.1'
37 | implementation 'com.google.firebase:firebase-messaging:20.2.3'
38 | //circle image view
39 | implementation 'de.hdodenhof:circleimageview:3.1.0'
40 | //download pictures from url
41 | implementation 'com.github.bumptech.glide:glide:4.11.0'
42 | annotationProcessor 'com.github.bumptech.glide:compiler:4.11.0'
43 | testImplementation 'junit:junit:4.12'
44 | androidTestImplementation 'androidx.test.ext:junit:1.1.1'
45 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
46 |
47 | }
--------------------------------------------------------------------------------
/app/src/main/java/learning/self/kotlin/projectmanager/activities/SplashActivity.kt:
--------------------------------------------------------------------------------
1 | package learning.self.kotlin.projectmanager.activities
2 |
3 | import android.content.Intent
4 | import android.graphics.Typeface
5 | import androidx.appcompat.app.AppCompatActivity
6 | import android.os.Bundle
7 | import android.os.Handler
8 | import android.view.WindowManager
9 | import kotlinx.android.synthetic.main.activity_splash.*
10 | import learning.self.kotlin.projectmanager.R
11 | import learning.self.kotlin.projectmanager.firebase.FireStoreHandler
12 |
13 | class SplashActivity : AppCompatActivity() {
14 | override fun onCreate(savedInstanceState: Bundle?) {
15 | super.onCreate(savedInstanceState)
16 | setContentView(R.layout.activity_splash)
17 |
18 | // This is used to hide the status bar and make the splash screen as a full screen activity.
19 | window.setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN)
20 |
21 | // This is used to get the font from the assets folder and set it to the title textView.
22 | val myFont: Typeface = Typeface.createFromAsset(assets, "Raleway-Bold.ttf")
23 | app_name_tv.typeface = myFont
24 |
25 | // Adding the handler to after the a task after some delay.
26 | Handler().postDelayed({
27 |
28 | var currentUserID = FireStoreHandler().getCurrentUserId()
29 | if(currentUserID.isNotEmpty()){
30 | //auto login - go straight to the main activity
31 | startActivity(Intent(this, MainActivity::class.java))
32 | }else{
33 | // Start the Login Activity
34 | startActivity(Intent(this, LoginActivity::class.java))
35 | }
36 |
37 | finish() // Call this when your activity is done and should be closed
38 | }, 2500) // Here we pass the delay time in milliSeconds after which the splash activity will disappear
39 | }
40 | }
--------------------------------------------------------------------------------
/app/src/main/java/learning/self/kotlin/projectmanager/dialogs/MembersListDialog.kt:
--------------------------------------------------------------------------------
1 | package learning.self.kotlin.projectmanager.dialogs
2 |
3 | import android.app.Dialog
4 | import android.content.Context
5 | import android.os.Bundle
6 | import android.view.LayoutInflater
7 | import android.view.View
8 | import androidx.recyclerview.widget.LinearLayoutManager
9 | import kotlinx.android.synthetic.main.dialog_list.view.*
10 | import learning.self.kotlin.projectmanager.R
11 | import learning.self.kotlin.projectmanager.adapters.MemberItemAdapter
12 | import learning.self.kotlin.projectmanager.models.User
13 |
14 | abstract class MembersListDialog (context : Context,
15 | private var list : ArrayList,
16 | private val title : String = ""
17 | ) : Dialog(context){
18 |
19 | private var adapter : MemberItemAdapter? = null
20 |
21 | override fun onCreate(savedInstanceState: Bundle?) {
22 | super.onCreate(savedInstanceState)
23 |
24 | val view = LayoutInflater.from(context).inflate(R.layout.dialog_list, null)
25 | setContentView(view)
26 | setCanceledOnTouchOutside(true)
27 | setCancelable(true)
28 | setUpRecyclerView(view)
29 | }
30 |
31 | private fun setUpRecyclerView(view : View){
32 | view.dialog_title_tv.text = title
33 |
34 | if(list.size > 0 ){
35 | view.dialog_list_rv.layoutManager = LinearLayoutManager(context)
36 | adapter = MemberItemAdapter(context, list)
37 | view.dialog_list_rv.adapter = adapter
38 | adapter!!.setOnClickListener(object : MemberItemAdapter.OnClickListener{
39 | override fun onClick(position: Int, user: User, action: String) {
40 | dismiss()
41 | onItemSelected(user,action)
42 | }
43 | })
44 | }
45 | }
46 |
47 | protected abstract fun onItemSelected(user : User, color : String)
48 | }
--------------------------------------------------------------------------------
/app/src/main/java/learning/self/kotlin/projectmanager/activities/BaseActivity.kt:
--------------------------------------------------------------------------------
1 | package learning.self.kotlin.projectmanager.activities
2 |
3 | import android.app.Dialog
4 | import androidx.appcompat.app.AppCompatActivity
5 | import android.os.Bundle
6 | import android.os.Handler
7 | import android.widget.Toast
8 | import androidx.core.content.ContextCompat
9 | import com.google.android.material.snackbar.Snackbar
10 | import com.google.firebase.auth.FirebaseAuth
11 | import kotlinx.android.synthetic.main.progress_dialog.*
12 | import learning.self.kotlin.projectmanager.R
13 |
14 | open class BaseActivity : AppCompatActivity() {
15 |
16 | private var doubleBackToExitPressOnce = false
17 |
18 | private lateinit var mProgressDialog : Dialog
19 |
20 | override fun onCreate(savedInstanceState: Bundle?) {
21 | super.onCreate(savedInstanceState)
22 | setContentView(R.layout.activity_base)
23 | }
24 |
25 | fun showProgressDialog(){
26 | mProgressDialog = Dialog(this)
27 | mProgressDialog.setContentView(R.layout.progress_dialog)
28 | mProgressDialog.show()
29 | }
30 |
31 | fun hideProgressDialog(){
32 | mProgressDialog.dismiss()
33 | }
34 |
35 | fun getCurrentUserID() : String {
36 | return FirebaseAuth.getInstance().currentUser!! .uid
37 | }
38 |
39 | fun doubleBackToExit(){
40 | if(doubleBackToExitPressOnce){
41 | super.onBackPressed()
42 | return
43 | }
44 |
45 | this.doubleBackToExitPressOnce = true
46 | Toast.makeText(this,"Please press back once again to exit", Toast.LENGTH_LONG).show()
47 |
48 | Handler().postDelayed({doubleBackToExitPressOnce = false}, 2000)
49 | }
50 |
51 | fun showErrorSnackBar(message : String){
52 | val snackBar = Snackbar.make(findViewById(android.R.id.content), message, Snackbar.LENGTH_LONG)
53 | snackBar.view.setBackgroundColor(ContextCompat.getColor(this, R.color.snack_bar_error_color))
54 | snackBar.show()
55 | }
56 | }
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_members.xml:
--------------------------------------------------------------------------------
1 |
2 |
10 |
11 |
15 |
16 |
22 |
23 |
24 |
31 |
32 |
39 |
40 |
44 |
45 |
46 |
47 |
--------------------------------------------------------------------------------
/app/src/main/java/learning/self/kotlin/projectmanager/adapters/LabelColorListItemAdapter.kt:
--------------------------------------------------------------------------------
1 | package learning.self.kotlin.projectmanager.adapters
2 |
3 | import android.content.Context
4 | import android.graphics.Color
5 | import android.view.LayoutInflater
6 | import android.view.View
7 | import android.view.ViewGroup
8 | import androidx.recyclerview.widget.RecyclerView
9 | import kotlinx.android.synthetic.main.item_label_color.view.*
10 | import learning.self.kotlin.projectmanager.R
11 |
12 | class LabelColorListItemAdapter(private val context: Context, private var list: ArrayList, private val mSelectedColor : String)
13 | : RecyclerView.Adapter(){
14 |
15 | var onItemClickListener : OnItemClickListener? = null
16 |
17 | override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
18 | return MyViewHolder(
19 | LayoutInflater.from(context)
20 | .inflate(R.layout.item_label_color, parent, false))
21 | }
22 |
23 | override fun getItemCount(): Int {
24 | return list.size
25 | }
26 |
27 | override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
28 | val item = list[position]
29 |
30 | if(holder is MyViewHolder){
31 | holder.itemView.view_main.setBackgroundColor(Color.parseColor(item))
32 | if(item == mSelectedColor){
33 | holder.itemView.selected_color_iv.visibility = View.VISIBLE
34 | }else{
35 | holder.itemView.selected_color_iv.visibility = View.GONE
36 | }
37 |
38 | holder.itemView.setOnClickListener {
39 | if(onItemClickListener != null){
40 | onItemClickListener!!.onClick(position, item)
41 | }
42 | }
43 | }
44 | }
45 |
46 |
47 | interface OnItemClickListener{
48 | fun onClick(position : Int, color : String)
49 | }
50 |
51 | private class MyViewHolder(view : View): RecyclerView.ViewHolder(view)
52 | }
--------------------------------------------------------------------------------
/app/src/main/res/layout/item_card.xml:
--------------------------------------------------------------------------------
1 |
2 |
10 |
11 |
15 |
16 |
22 |
23 |
30 |
31 |
38 |
39 |
49 |
50 |
51 |
52 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/dialog_list.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
16 |
17 |
23 |
24 |
34 |
35 |
41 |
42 |
48 |
49 |
50 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @if "%DEBUG%" == "" @echo off
2 | @rem ##########################################################################
3 | @rem
4 | @rem Gradle startup script for Windows
5 | @rem
6 | @rem ##########################################################################
7 |
8 | @rem Set local scope for the variables with windows NT shell
9 | if "%OS%"=="Windows_NT" setlocal
10 |
11 | set DIRNAME=%~dp0
12 | if "%DIRNAME%" == "" set DIRNAME=.
13 | set APP_BASE_NAME=%~n0
14 | set APP_HOME=%DIRNAME%
15 |
16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
17 | set DEFAULT_JVM_OPTS=
18 |
19 | @rem Find java.exe
20 | if defined JAVA_HOME goto findJavaFromJavaHome
21 |
22 | set JAVA_EXE=java.exe
23 | %JAVA_EXE% -version >NUL 2>&1
24 | if "%ERRORLEVEL%" == "0" goto init
25 |
26 | echo.
27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28 | echo.
29 | echo Please set the JAVA_HOME variable in your environment to match the
30 | echo location of your Java installation.
31 |
32 | goto fail
33 |
34 | :findJavaFromJavaHome
35 | set JAVA_HOME=%JAVA_HOME:"=%
36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37 |
38 | if exist "%JAVA_EXE%" goto init
39 |
40 | echo.
41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42 | echo.
43 | echo Please set the JAVA_HOME variable in your environment to match the
44 | echo location of your Java installation.
45 |
46 | goto fail
47 |
48 | :init
49 | @rem Get command-line arguments, handling Windows variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 |
53 | :win9xME_args
54 | @rem Slurp the command line arguments.
55 | set CMD_LINE_ARGS=
56 | set _SKIP=2
57 |
58 | :win9xME_args_slurp
59 | if "x%~1" == "x" goto execute
60 |
61 | set CMD_LINE_ARGS=%*
62 |
63 | :execute
64 | @rem Setup the command line
65 |
66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
67 |
68 | @rem Execute Gradle
69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
70 |
71 | :end
72 | @rem End local scope for the variables with windows NT shell
73 | if "%ERRORLEVEL%"=="0" goto mainEnd
74 |
75 | :fail
76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
77 | rem the _cmd.exe /c_ return code!
78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
79 | exit /b 1
80 |
81 | :mainEnd
82 | if "%OS%"=="Windows_NT" endlocal
83 |
84 | :omega
85 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/item_board.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
16 |
17 |
24 |
25 |
31 |
32 |
40 |
41 |
50 |
51 |
52 |
53 |
54 |
59 |
60 |
--------------------------------------------------------------------------------
/app/src/main/java/learning/self/kotlin/projectmanager/utils/Constants.kt:
--------------------------------------------------------------------------------
1 | package learning.self.kotlin.projectmanager.utils
2 |
3 | import android.app.Activity
4 | import android.content.Intent
5 | import android.content.res.AssetManager
6 | import android.graphics.Typeface
7 | import android.net.Uri
8 | import android.provider.MediaStore
9 | import android.webkit.MimeTypeMap
10 | import learning.self.kotlin.projectmanager.R
11 | import learning.self.kotlin.projectmanager.activities.CreateBoardActivity
12 | import java.security.AccessController.getContext
13 |
14 | object Constants{
15 | const val USERS : String = "users"
16 | const val BOARDS : String = "boards"
17 | const val IMAGE : String = "image"
18 | const val NAME : String = "name"
19 | const val MOBILE : String = "mobile"
20 | const val ASSIGNED_TO : String = "assignedTo"
21 |
22 | const val READ_STORAGE_PERMISSION_CODE = 1
23 | const val PICK_IMAGE_REQUEST_CODE = 2
24 | const val DOCUMENT_ID : String = "documentID"
25 | const val TASK_LIST : String = "taskList"
26 | const val BOARD_DETAIL : String = "board_detail"
27 | const val ID : String = "id"
28 | const val EMAIL : String = "email"
29 | const val BOARD_MEMBERS_LIST :String = "board_members_list"
30 | const val SELECT : String = "select"
31 | const val UN_SELECT : String = "unselect"
32 |
33 | const val TASK_LIST_ITEM_POSITION : String = "task_list_item_position"
34 | const val CARD_LIST_ITEM_POSITION : String = "card_list_item_position"
35 |
36 | const val POJECTMANAGER_PREFERENCES = "Project_manager_preferences"
37 | const val FCM_TOKEN_UPDATED = "fcm_token_updated"
38 | const val FCM_TOKEN = "fcm_token"
39 |
40 | const val FCM_BASE_URL:String = "https://fcm.googleapis.com/fcm/send"
41 | const val FCM_AUTHORIZATION:String = "authorization"
42 | const val FCM_KEY:String = "key"
43 | const val FCM_SERVER_KEY:String = R.string.fcm_key.toString()
44 | const val FCM_KEY_TITLE:String = "title"
45 | const val FCM_KEY_MESSAGE:String = "message"
46 | const val FCM_KEY_DATA:String = "data"
47 | const val FCM_KEY_TO:String = "to"
48 |
49 | fun getFileExtension(activity : Activity, uri : Uri?) : String?{
50 | return MimeTypeMap.getSingleton().getExtensionFromMimeType(activity.contentResolver.getType(uri!!))
51 | }
52 |
53 | fun showImageChooser(activity : Activity){
54 | var galleryIntent = Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI)
55 | activity.startActivityForResult(galleryIntent, PICK_IMAGE_REQUEST_CODE)
56 | }
57 | }
--------------------------------------------------------------------------------
/app/src/main/java/learning/self/kotlin/projectmanager/adapters/CardMemberListItemAdapter.kt:
--------------------------------------------------------------------------------
1 | package learning.self.kotlin.projectmanager.adapters
2 |
3 | import android.content.Context
4 | import android.view.LayoutInflater
5 | import android.view.View
6 | import android.view.ViewGroup
7 | import androidx.recyclerview.widget.RecyclerView
8 | import com.bumptech.glide.Glide
9 | import kotlinx.android.synthetic.main.item_card_selected_member.view.*
10 | import learning.self.kotlin.projectmanager.R
11 | import learning.self.kotlin.projectmanager.models.SelectedMembers
12 |
13 | open class CardMemberListItemAdapter(
14 | private val context: Context,
15 | private var list: ArrayList,
16 | private val assignedMembers : Boolean)
17 | : RecyclerView.Adapter(){
18 |
19 | private var onClickListener : OnClickListener? = null
20 |
21 | override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
22 | return MyViewHolder(LayoutInflater.from(context)
23 | .inflate(R.layout.item_card_selected_member,
24 | parent,
25 | false))
26 | }
27 |
28 | override fun getItemCount(): Int {
29 | return list.size
30 | }
31 |
32 | override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
33 | val model = list[position]
34 |
35 | if(holder is MyViewHolder){
36 | if(position == list.size -1 && assignedMembers){
37 | holder.itemView.add_member_iv.visibility = View.VISIBLE
38 | holder.itemView.selected_member_image_iv.visibility = View.GONE
39 | }else{
40 | holder.itemView.add_member_iv.visibility = View.GONE
41 | holder.itemView.selected_member_image_iv.visibility = View.VISIBLE
42 |
43 | //set users image
44 | Glide
45 | .with(context)
46 | .load(model.image)
47 | .fitCenter()
48 | .placeholder(R.drawable.ic_user_place_holder)
49 | .into(holder.itemView.selected_member_image_iv)
50 | }
51 |
52 | holder.itemView.setOnClickListener {
53 | if(onClickListener != null){
54 | onClickListener!!.onClick()
55 | }
56 | }
57 | }
58 | }
59 |
60 |
61 | fun setOnClickListener(onClickListener: OnClickListener) {
62 | this.onClickListener = onClickListener
63 | }
64 |
65 | interface OnClickListener {
66 | fun onClick()
67 | }
68 |
69 | private class MyViewHolder(view : View): RecyclerView.ViewHolder(view)
70 | }
--------------------------------------------------------------------------------
/app/src/main/java/learning/self/kotlin/projectmanager/adapters/BoardItemAdapter.kt:
--------------------------------------------------------------------------------
1 | package learning.self.kotlin.projectmanager.adapters
2 |
3 | import android.content.Context
4 | import android.graphics.Typeface
5 | import android.view.LayoutInflater
6 | import android.view.View
7 | import android.view.ViewGroup
8 | import androidx.recyclerview.widget.RecyclerView
9 | import com.bumptech.glide.Glide
10 | import kotlinx.android.synthetic.main.item_board.view.*
11 | import learning.self.kotlin.projectmanager.R
12 | import learning.self.kotlin.projectmanager.models.Board
13 |
14 | open class BoardItemAdapter (private val context:Context, private var list : ArrayList)
15 | :RecyclerView.Adapter(){
16 |
17 | private var onClickListener : OnClickListener? = null
18 |
19 | override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
20 | return MyViewHolder(LayoutInflater.from(context)
21 | .inflate(R.layout.item_board, parent, false))
22 | }
23 |
24 | override fun getItemCount(): Int {
25 | return list.size
26 | }
27 |
28 | override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
29 | val model = list[position]
30 |
31 | if(holder is MyViewHolder){
32 | val regularFont: Typeface = Typeface.createFromAsset(holder.itemView.getContext().assets, "Raleway-Regular.ttf")
33 | val boldFont: Typeface = Typeface.createFromAsset(holder.itemView.getContext().assets, "Raleway-Bold.ttf")
34 |
35 | Glide
36 | .with(context)
37 | .load(model.image)
38 | .centerCrop()
39 | .placeholder(R.drawable.ic_board_place_holder)
40 | .into(holder.itemView.item_board_iv)
41 | //set fonts
42 | holder.itemView.item_board_name_tv.typeface = boldFont
43 | holder.itemView.item_board_created_by_tv.typeface = regularFont
44 | //set text
45 | holder.itemView.item_board_name_tv.text = model.name
46 | holder.itemView.item_board_created_by_tv.text = "Created by: " + model.createdBy
47 |
48 | holder.itemView.setOnClickListener {
49 | if(onClickListener != null){
50 | onClickListener!!.onClick(position,model)
51 | }
52 | }
53 | }
54 | }
55 |
56 | fun setOnClickListener(onClickListener : OnClickListener){
57 | this.onClickListener = onClickListener
58 |
59 | }
60 |
61 | interface OnClickListener{
62 | fun onClick(position : Int, model : Board)
63 | }
64 |
65 | private class MyViewHolder(view : View): RecyclerView.ViewHolder(view){
66 |
67 | }
68 | }
--------------------------------------------------------------------------------
/app/src/main/res/layout/item_member.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
16 |
17 |
24 |
25 |
31 |
32 |
39 |
40 |
47 |
48 |
49 |
58 |
59 |
60 |
61 |
66 |
67 |
--------------------------------------------------------------------------------
/app/src/main/java/learning/self/kotlin/projectmanager/adapters/MemberItemAdapter.kt:
--------------------------------------------------------------------------------
1 | package learning.self.kotlin.projectmanager.adapters
2 |
3 | import android.content.Context
4 | import android.graphics.Typeface
5 | import android.view.LayoutInflater
6 | import android.view.View
7 | import android.view.ViewGroup
8 | import androidx.recyclerview.widget.RecyclerView
9 | import com.bumptech.glide.Glide
10 | import kotlinx.android.synthetic.main.item_member.view.*
11 | import learning.self.kotlin.projectmanager.R
12 | import learning.self.kotlin.projectmanager.models.User
13 | import learning.self.kotlin.projectmanager.utils.Constants
14 |
15 | open class MemberItemAdapter (private val context: Context, private var list: ArrayList)
16 | : RecyclerView.Adapter(){
17 |
18 | private var onClickListener : OnClickListener? = null
19 |
20 | override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
21 | return MyViewHolder(
22 | LayoutInflater.from(context)
23 | .inflate(R.layout.item_member, parent, false))
24 | }
25 |
26 | override fun getItemCount(): Int {
27 | return list.size
28 | }
29 |
30 | override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
31 | val model = list[position]
32 |
33 | if(holder is MyViewHolder){
34 | val regularFont: Typeface = Typeface.createFromAsset(holder.itemView.getContext().assets, "Raleway-Regular.ttf")
35 | val boldFont: Typeface = Typeface.createFromAsset(holder.itemView.getContext().assets, "Raleway-Bold.ttf")
36 | holder.itemView.member_name_tv.typeface = boldFont
37 | holder.itemView.member_email_tv.typeface = regularFont
38 | holder.itemView.member_name_tv.text = model.name
39 | holder.itemView.member_email_tv.text = model.email
40 | Glide
41 | .with(context)
42 | .load(model.image)
43 | .fitCenter()
44 | .placeholder(R.drawable.ic_user_place_holder)
45 | .into(holder.itemView.member_image_iv)
46 |
47 | if(model.selected){
48 | holder.itemView.selected_member_iv.visibility = View.VISIBLE
49 | }else{
50 | holder.itemView.selected_member_iv.visibility = View.GONE
51 | }
52 |
53 | holder.itemView.setOnClickListener {
54 | if(onClickListener != null){
55 | if(model.selected){
56 | onClickListener!!.onClick(position,model,Constants.UN_SELECT)
57 | }else{
58 | onClickListener!!.onClick(position,model,Constants.SELECT)
59 | }
60 | }
61 | }
62 | }
63 | }
64 |
65 | fun setOnClickListener(onClickListener: OnClickListener) {
66 | this.onClickListener = onClickListener
67 | }
68 |
69 | interface OnClickListener {
70 | fun onClick(position: Int, user: User, action: String)
71 | }
72 |
73 |
74 | class MyViewHolder(view: View) : RecyclerView.ViewHolder(view)
75 | }
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_login.xml:
--------------------------------------------------------------------------------
1 |
2 |
11 |
12 |
21 |
22 |
27 |
28 |
36 |
37 |
49 |
50 |
66 |
67 |
83 |
84 |
--------------------------------------------------------------------------------
/app/src/main/java/learning/self/kotlin/projectmanager/activities/SignInActivity.kt:
--------------------------------------------------------------------------------
1 | package learning.self.kotlin.projectmanager.activities
2 |
3 | import android.content.Intent
4 | import android.graphics.Typeface
5 | import android.os.Bundle
6 | import android.text.TextUtils
7 | import android.view.WindowManager
8 | import android.widget.Toast
9 | import com.google.firebase.auth.FirebaseAuth
10 | import kotlinx.android.synthetic.main.activity_sign_in.*
11 | import learning.self.kotlin.projectmanager.R
12 | import learning.self.kotlin.projectmanager.firebase.FireStoreHandler
13 | import learning.self.kotlin.projectmanager.models.User
14 |
15 | private lateinit var auth: FirebaseAuth
16 |
17 | class SignInActivity : BaseActivity() {
18 | override fun onCreate(savedInstanceState: Bundle?) {
19 | super.onCreate(savedInstanceState)
20 | setContentView(R.layout.activity_sign_in)
21 | window.setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN)
22 | setActionBar()
23 | setFonts()
24 |
25 | auth = FirebaseAuth.getInstance();
26 |
27 | sign_in_page_btn.setOnClickListener {
28 | signInUser()
29 | }
30 | }
31 |
32 | fun signInSuccess(user : User){
33 | hideProgressDialog()
34 | startActivity(Intent(this, MainActivity::class.java))
35 | finish()
36 | }
37 |
38 | private fun signInUser(){
39 | val email = sign_in_email_et.text.toString().trim { it <= ' '}
40 | val password = sign_in_password_et.text.toString().trim { it <= ' '}
41 |
42 | if(validateForm(email, password)) {
43 | showProgressDialog()
44 | auth.signInWithEmailAndPassword(email, password)
45 | .addOnCompleteListener(this) { task ->
46 | hideProgressDialog()
47 | if (task.isSuccessful) {
48 | FireStoreHandler().loadUserData(this)
49 | } else {
50 | // If sign in fails, display a message to the user.
51 | Toast.makeText(baseContext,
52 | "Authentication failed.",
53 | Toast.LENGTH_SHORT).show()
54 | }
55 | }
56 |
57 | }
58 | }
59 |
60 | private fun validateForm(email:String, password:String) : Boolean{
61 | return when {
62 | TextUtils.isEmpty(email) -> {
63 | showErrorSnackBar("Please enter an email address")
64 | false
65 | }
66 | TextUtils.isEmpty(password) -> {
67 | showErrorSnackBar("Please enter a password")
68 | false
69 | }else->{
70 | true
71 | }
72 | }
73 | }
74 |
75 | private fun setFonts(){
76 | val regularFont: Typeface = Typeface.createFromAsset(assets, "Raleway-Regular.ttf")
77 | val mediumFont: Typeface = Typeface.createFromAsset(assets, "Raleway-Medium.ttf")
78 | sign_in_email_et.typeface = regularFont
79 | sign_in_description_tv.typeface = mediumFont
80 | sign_in_page_btn.typeface = regularFont
81 | }
82 |
83 | private fun setActionBar(){
84 | setSupportActionBar(toolbar_sign_in_activity)
85 | val actionBar = supportActionBar
86 | if(actionBar != null){
87 | actionBar.setDisplayHomeAsUpEnabled(true)
88 | actionBar.setHomeAsUpIndicator(R.drawable.ic_back_black)
89 | }
90 | toolbar_sign_in_activity.setNavigationOnClickListener{onBackPressed()}
91 | }
92 | }
--------------------------------------------------------------------------------
/app/src/main/res/layout/dialog_serach_member.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
13 |
14 |
20 |
21 |
30 |
31 |
37 |
38 |
42 |
43 |
50 |
51 |
52 |
57 |
58 |
69 |
70 |
81 |
82 |
83 |
84 |
--------------------------------------------------------------------------------
/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
9 |
12 |
15 |
16 |
17 |
18 |
19 |
20 |
27 |
30 |
34 |
38 |
42 |
46 |
50 |
54 |
58 |
62 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
76 |
77 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
--------------------------------------------------------------------------------
/app/src/main/java/learning/self/kotlin/projectmanager/fcm/MyFirebaseMessagingService.kt:
--------------------------------------------------------------------------------
1 | package learning.self.kotlin.projectmanager.fcm
2 |
3 | import android.app.NotificationChannel
4 | import android.app.NotificationManager
5 | import android.app.PendingIntent
6 | import android.content.Context
7 | import android.content.Intent
8 | import android.content.SharedPreferences
9 | import android.media.RingtoneManager
10 | import android.os.Build
11 | import android.util.Log
12 | import androidx.core.app.NotificationCompat
13 | import com.google.firebase.messaging.FirebaseMessagingService
14 | import com.google.firebase.messaging.RemoteMessage
15 | import learning.self.kotlin.projectmanager.R
16 | import learning.self.kotlin.projectmanager.activities.MainActivity
17 | import learning.self.kotlin.projectmanager.activities.SignInActivity
18 | import learning.self.kotlin.projectmanager.firebase.FireStoreHandler
19 | import learning.self.kotlin.projectmanager.utils.Constants
20 |
21 | class MyFirebaseMessagingService : FirebaseMessagingService() {
22 |
23 | override fun onMessageReceived(remoteMeesage: RemoteMessage) {
24 | super.onMessageReceived(remoteMeesage)
25 |
26 | Log.d(TAG, "FROM: ${remoteMeesage.from}")
27 |
28 | remoteMeesage.data.isNotEmpty().let {
29 | Log.d(TAG, "Message dada Payload: ${remoteMeesage.data}")
30 | val title = remoteMeesage.data[Constants.FCM_KEY_TITLE]!!
31 | val message = remoteMeesage.data[Constants.FCM_KEY_MESSAGE]!!
32 |
33 | sendNotification(title,message)
34 | }
35 |
36 | remoteMeesage.notification?.let {
37 | Log.d(TAG, "Message notification body : ${it.body}")
38 | }
39 | }
40 |
41 | override fun onNewToken(token: String) {
42 | super.onNewToken(token)
43 | Log.e(TAG, "Refreshed token : $token")
44 | sendRegistrationToServer(token)
45 | }
46 |
47 | private fun sendRegistrationToServer(token: String?){
48 | // Here we have saved the token in the Shared Preferences
49 | val sharedPreferences =
50 | this.getSharedPreferences(Constants.POJECTMANAGER_PREFERENCES, Context.MODE_PRIVATE)
51 | val editor: SharedPreferences.Editor = sharedPreferences.edit()
52 | editor.putString(Constants.FCM_TOKEN, token)
53 | editor.apply()
54 | }
55 |
56 | private fun sendNotification(title : String, message : String){
57 |
58 | val intent = if(FireStoreHandler().getCurrentUserId().isNotEmpty()){
59 | Intent(this, MainActivity::class.java) //if the user already logged in
60 | }else{
61 | Intent(this, SignInActivity::class.java) //the user logged out
62 | }
63 | intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK
64 | or Intent.FLAG_ACTIVITY_CLEAR_TASK
65 | or Intent.FLAG_ACTIVITY_CLEAR_TOP)
66 | val pendingIntent = PendingIntent.getActivity(
67 | this,
68 | 0,
69 | intent,
70 | PendingIntent.FLAG_ONE_SHOT)
71 |
72 | val channelId = "project_manager_notification_channel_id"
73 | val defaultSoundUri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION)
74 |
75 | val notificationBuilder = NotificationCompat
76 | .Builder(this, channelId)
77 | .setSmallIcon(R.mipmap.ic_launcher)
78 | .setContentTitle(title)
79 | .setContentText(message)
80 | .setAutoCancel(true)
81 | .setSound(defaultSoundUri)
82 | .setContentIntent(pendingIntent)
83 |
84 | val notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
85 |
86 | if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O){
87 | val channel = NotificationChannel(channelId, "Channel ProjectManager", NotificationManager.IMPORTANCE_DEFAULT)
88 | notificationManager.createNotificationChannel(channel)
89 | }
90 | notificationManager.notify(0, notificationBuilder.build())
91 | }
92 |
93 | companion object {
94 | private const val TAG = "FCM"
95 | }
96 | }
--------------------------------------------------------------------------------
/.idea/codeStyles/Project.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 | xmlns:android
17 |
18 | ^$
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 | xmlns:.*
28 |
29 | ^$
30 |
31 |
32 | BY_NAME
33 |
34 |
35 |
36 |
37 |
38 |
39 | .*:id
40 |
41 | http://schemas.android.com/apk/res/android
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 | .*:name
51 |
52 | http://schemas.android.com/apk/res/android
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 | name
62 |
63 | ^$
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 | style
73 |
74 | ^$
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 | .*
84 |
85 | ^$
86 |
87 |
88 | BY_NAME
89 |
90 |
91 |
92 |
93 |
94 |
95 | .*
96 |
97 | http://schemas.android.com/apk/res/android
98 |
99 |
100 | ANDROID_ATTRIBUTE_ORDER
101 |
102 |
103 |
104 |
105 |
106 |
107 | .*
108 |
109 | .*
110 |
111 |
112 | BY_NAME
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_create_board.xml:
--------------------------------------------------------------------------------
1 |
2 |
10 |
11 |
15 |
16 |
22 |
23 |
24 |
28 |
29 |
38 |
39 |
45 |
46 |
51 |
52 |
58 |
59 |
66 |
67 |
68 |
84 |
85 |
86 |
87 |
--------------------------------------------------------------------------------
/app/src/main/java/learning/self/kotlin/projectmanager/activities/SignUpActivity.kt:
--------------------------------------------------------------------------------
1 | package learning.self.kotlin.projectmanager.activities
2 |
3 | import android.graphics.Typeface
4 | import android.os.Bundle
5 | import android.text.TextUtils
6 | import android.view.WindowManager
7 | import android.widget.Toast
8 | import com.google.firebase.auth.FirebaseAuth
9 | import com.google.firebase.auth.FirebaseUser
10 | import kotlinx.android.synthetic.main.activity_sign_up.*
11 | import learning.self.kotlin.projectmanager.R
12 | import learning.self.kotlin.projectmanager.firebase.FireStoreHandler
13 | import learning.self.kotlin.projectmanager.models.User
14 |
15 | class SignUpActivity : BaseActivity() {
16 | override fun onCreate(savedInstanceState: Bundle?) {
17 | super.onCreate(savedInstanceState)
18 | setContentView(R.layout.activity_sign_up)
19 | window.setFlags(
20 | WindowManager.LayoutParams.FLAG_FULLSCREEN,
21 | WindowManager.LayoutParams.FLAG_FULLSCREEN
22 | )
23 | setActionBar()
24 | setFonts()
25 |
26 | sign_up_page_btn.setOnClickListener {
27 | registerUser()
28 | }
29 | }
30 |
31 | private fun setFonts() {
32 | val regularFont: Typeface = Typeface.createFromAsset(assets, "Raleway-Regular.ttf")
33 | val mediumFont: Typeface = Typeface.createFromAsset(assets, "Raleway-Medium.ttf")
34 | name_et.typeface = regularFont
35 | email_et.typeface = regularFont
36 | sign_up_description_tv.typeface = mediumFont
37 | sign_up_page_btn.typeface = regularFont
38 | }
39 |
40 | private fun setActionBar() {
41 | setSupportActionBar(toolbar_sign_up_activity)
42 | val actionBar = supportActionBar
43 | if (actionBar != null) {
44 | actionBar.setDisplayHomeAsUpEnabled(true)
45 | actionBar.setHomeAsUpIndicator(R.drawable.ic_back_black)
46 | }
47 | toolbar_sign_up_activity.setNavigationOnClickListener { onBackPressed() }
48 | }
49 |
50 | private fun registerUser() {
51 | val name = name_et.text.toString().trim { it <= ' ' } //remove spaces
52 | val email = email_et.text.toString().trim { it <= ' ' }
53 | val password = password_et.text.toString().trim { it <= ' ' }
54 |
55 | if (validateForm(name, email, password)) {
56 | showProgressDialog()
57 | FirebaseAuth.getInstance()
58 | .createUserWithEmailAndPassword(email, password)
59 | .addOnCompleteListener { task ->
60 | if (task.isSuccessful) {
61 | //send user details for user creation in firebase
62 | val firebaseUser: FirebaseUser = task.result!!.user!!
63 | val registeredEmail = firebaseUser.email!!
64 | val user = User(firebaseUser.uid, name, registeredEmail)
65 | FireStoreHandler().registerUser(this,user)
66 | } else {
67 | Toast.makeText(
68 | this,
69 | "Registration Failed. Try Again",
70 | Toast.LENGTH_SHORT
71 | ).show()
72 | }
73 | }
74 |
75 | }
76 | }
77 |
78 | fun userRegisteredSuccess(){
79 | Toast.makeText(this,
80 | "You have successfully registered",
81 | Toast.LENGTH_SHORT).show()
82 | hideProgressDialog()
83 | FirebaseAuth.getInstance().signOut()
84 | finish()
85 | }
86 |
87 | private fun validateForm(name: String, email: String, password: String): Boolean {
88 | return when {
89 | TextUtils.isEmpty(name) -> {
90 | showErrorSnackBar("Please enter a name")
91 | false
92 | }
93 | TextUtils.isEmpty(email) -> {
94 | showErrorSnackBar("Please enter an email address")
95 | false
96 | }
97 | TextUtils.isEmpty(password) -> {
98 | showErrorSnackBar("Please enter a password")
99 | false
100 | }
101 | else -> {
102 | true
103 | }
104 | }
105 | }
106 | }
--------------------------------------------------------------------------------
/app/src/main/java/learning/self/kotlin/projectmanager/adapters/CardListItemAdapter.kt:
--------------------------------------------------------------------------------
1 | package learning.self.kotlin.projectmanager.adapters
2 |
3 | import android.content.Context
4 | import android.graphics.Color
5 | import android.graphics.Typeface
6 | import android.view.LayoutInflater
7 | import android.view.View
8 | import android.view.ViewGroup
9 | import androidx.recyclerview.widget.GridLayoutManager
10 | import androidx.recyclerview.widget.RecyclerView
11 | import kotlinx.android.synthetic.main.item_card.view.*
12 | import learning.self.kotlin.projectmanager.R
13 | import learning.self.kotlin.projectmanager.activities.TaskListActivity
14 | import learning.self.kotlin.projectmanager.models.Card
15 | import learning.self.kotlin.projectmanager.models.SelectedMembers
16 |
17 | open class CardListItemAdapter(private val context: Context, private var list: ArrayList)
18 | : RecyclerView.Adapter(){
19 |
20 | private var onClickListener : CardListItemAdapter.OnClickListener? = null
21 |
22 | override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
23 | return MyViewHolder(
24 | LayoutInflater.from(context)
25 | .inflate(R.layout.item_card, parent, false))
26 | }
27 |
28 | override fun getItemCount(): Int {
29 | return list.size
30 | }
31 |
32 | override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
33 | val model = list[position]
34 |
35 | if(holder is MyViewHolder){
36 | val regularFont: Typeface = Typeface.createFromAsset(holder.itemView.getContext().assets, "Raleway-Regular.ttf")
37 | holder.itemView.card_name_tv.typeface = regularFont
38 | holder.itemView.card_name_tv.text = model.name
39 |
40 | if(model.labelColor.isNotEmpty()){
41 | holder.itemView.view_label_color.visibility = View.VISIBLE
42 | holder.itemView.view_label_color.setBackgroundColor(
43 | Color.parseColor(model.labelColor))
44 | }else{
45 | holder.itemView.view_label_color.visibility = View.GONE
46 | }
47 |
48 | //set the assigned members under the card name as circle images
49 | if((context as TaskListActivity).mAssignedMembersDetailList.size > 0){
50 |
51 | val selectedMembersList : ArrayList = ArrayList()
52 | val assignedMembers = context.mAssignedMembersDetailList
53 |
54 | for(i in assignedMembers.indices){
55 | for (j in model.assignedTo){
56 | if(assignedMembers[i].id == j){
57 | val selectedMember = SelectedMembers(assignedMembers[i].id, assignedMembers[i].image)
58 | selectedMembersList.add(selectedMember)
59 | }
60 | }
61 | }
62 |
63 | if(selectedMembersList.size > 0){
64 | //if the only member in the list is the creator - do not show the recycler view
65 | if(selectedMembersList.size == 1
66 | && selectedMembersList[0].id == model.createdBy){
67 | holder.itemView.card_selected_members_list_rv.visibility = View.GONE
68 | }else{ //add all the assigned members to the recycler view under the card name create and set the adapter
69 | holder.itemView.card_selected_members_list_rv.visibility = View.VISIBLE
70 | holder.itemView.card_selected_members_list_rv.layoutManager =
71 | GridLayoutManager(context, 4)
72 | val adapter = CardMemberListItemAdapter(context, selectedMembersList, false)
73 | holder.itemView.card_selected_members_list_rv.adapter = adapter
74 | adapter.setOnClickListener(object : CardMemberListItemAdapter.OnClickListener{
75 | override fun onClick() {
76 | if(onClickListener != null){
77 | onClickListener!!.onClick(position)
78 | }
79 | }
80 | })
81 | }
82 | }else{
83 | holder.itemView.card_selected_members_list_rv.visibility = View.GONE
84 | }
85 | }
86 |
87 | holder.itemView.setOnClickListener {
88 | if(onClickListener != null){
89 | onClickListener!!.onClick(position)
90 | }
91 | }
92 | }
93 | }
94 |
95 | fun setOnClickListener(onClickListener : OnClickListener){
96 | this.onClickListener = onClickListener
97 |
98 | }
99 |
100 | interface OnClickListener{
101 | fun onClick(position : Int)
102 | }
103 |
104 | private class MyViewHolder(view : View): RecyclerView.ViewHolder(view){
105 |
106 | }
107 | }
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_sign_in.xml:
--------------------------------------------------------------------------------
1 |
2 |
10 |
11 |
12 |
16 |
24 |
25 |
26 |
32 |
33 |
43 |
44 |
52 |
53 |
58 |
59 |
65 |
66 |
73 |
74 |
75 |
81 |
82 |
89 |
90 |
91 |
107 |
108 |
109 |
110 |
111 |
112 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # ProjectManager - Android App
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 | **Trello Clone Android Application Using Kotlin Languange.
10 | The database stored in Google's Firebase.**
11 |
12 |
13 | ## Features
14 | * Manage your tasks with another members easily.
15 | * Each Member has all the boards he is assigned to.
16 | * Each Board has all the lists with task cards.
17 | * Cards can be moved up and down in the list.
18 | * Easily update user settings - set name, image and phone.
19 | * Easily update cards - set label color, assign members and add due date.
20 | * Get notification from app when somebody assignes you for board.
21 |
22 | ## Screenshots
23 |
24 | [
](https://user-images.githubusercontent.com/51478377/88479812-8397ee80-cf5a-11ea-883b-56ce4041e3dc.jpeg)
26 | [
](https://user-images.githubusercontent.com/51478377/88479813-872b7580-cf5a-11ea-812c-a0af825f8d4f.jpeg)
28 | [
](https://user-images.githubusercontent.com/51478377/88479816-898dcf80-cf5a-11ea-971c-b14a40cc0cec.jpeg)
30 |
31 | [
](https://user-images.githubusercontent.com/51478377/88479817-8abefc80-cf5a-11ea-8707-8ad3dc919382.jpeg)
33 | [
](https://user-images.githubusercontent.com/51478377/88479819-8c88c000-cf5a-11ea-92b0-56e406b0a86c.jpeg)
35 | [
](https://user-images.githubusercontent.com/51478377/88479821-8db9ed00-cf5a-11ea-9750-09eb445a98ba.jpeg)
37 |
38 | [
](https://user-images.githubusercontent.com/51478377/88479823-93afce00-cf5a-11ea-978d-77c4a96f571f.jpeg)
40 | [
](https://user-images.githubusercontent.com/51478377/88479837-afb36f80-cf5a-11ea-81cb-3907d4d9d003.jpeg)
42 | [
](https://user-images.githubusercontent.com/51478377/88479824-95799180-cf5a-11ea-9170-b0a763ac9f63.jpeg)
44 |
45 | [
](https://user-images.githubusercontent.com/51478377/88479826-97435500-cf5a-11ea-9a1d-e898733d3bf1.jpeg)
47 | [
](https://user-images.githubusercontent.com/51478377/88479827-98748200-cf5a-11ea-982f-c2d0f808e380.jpeg)
49 | [
](https://user-images.githubusercontent.com/51478377/88479846-b80baa80-cf5a-11ea-8011-7fa58dfe5e79.jpeg)
51 |
52 | [
](https://user-images.githubusercontent.com/51478377/88479848-b93cd780-cf5a-11ea-89c4-9a1b7651954a.jpeg.jpeg)
54 | [
](https://user-images.githubusercontent.com/51478377/88479851-be018b80-cf5a-11ea-90cc-225a724f9099.jpeg)
56 | [
](https://user-images.githubusercontent.com/51478377/88479852-c0fc7c00-cf5a-11ea-8950-fc60627b63e2.jpeg)
58 |
59 | [
](https://user-images.githubusercontent.com/51478377/88479854-c22da900-cf5a-11ea-8217-b97a52aa1f9c.jpeg)
61 | [
](https://user-images.githubusercontent.com/51478377/88479856-c3f76c80-cf5a-11ea-9603-d7cc20adfbcc.jpeg)
63 |
64 |
65 |
66 |
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env sh
2 |
3 | ##############################################################################
4 | ##
5 | ## Gradle start up script for UN*X
6 | ##
7 | ##############################################################################
8 |
9 | # Attempt to set APP_HOME
10 | # Resolve links: $0 may be a link
11 | PRG="$0"
12 | # Need this for relative symlinks.
13 | while [ -h "$PRG" ] ; do
14 | ls=`ls -ld "$PRG"`
15 | link=`expr "$ls" : '.*-> \(.*\)$'`
16 | if expr "$link" : '/.*' > /dev/null; then
17 | PRG="$link"
18 | else
19 | PRG=`dirname "$PRG"`"/$link"
20 | fi
21 | done
22 | SAVED="`pwd`"
23 | cd "`dirname \"$PRG\"`/" >/dev/null
24 | APP_HOME="`pwd -P`"
25 | cd "$SAVED" >/dev/null
26 |
27 | APP_NAME="Gradle"
28 | APP_BASE_NAME=`basename "$0"`
29 |
30 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
31 | DEFAULT_JVM_OPTS=""
32 |
33 | # Use the maximum available, or set MAX_FD != -1 to use that value.
34 | MAX_FD="maximum"
35 |
36 | warn () {
37 | echo "$*"
38 | }
39 |
40 | die () {
41 | echo
42 | echo "$*"
43 | echo
44 | exit 1
45 | }
46 |
47 | # OS specific support (must be 'true' or 'false').
48 | cygwin=false
49 | msys=false
50 | darwin=false
51 | nonstop=false
52 | case "`uname`" in
53 | CYGWIN* )
54 | cygwin=true
55 | ;;
56 | Darwin* )
57 | darwin=true
58 | ;;
59 | MINGW* )
60 | msys=true
61 | ;;
62 | NONSTOP* )
63 | nonstop=true
64 | ;;
65 | esac
66 |
67 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
68 |
69 | # Determine the Java command to use to start the JVM.
70 | if [ -n "$JAVA_HOME" ] ; then
71 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
72 | # IBM's JDK on AIX uses strange locations for the executables
73 | JAVACMD="$JAVA_HOME/jre/sh/java"
74 | else
75 | JAVACMD="$JAVA_HOME/bin/java"
76 | fi
77 | if [ ! -x "$JAVACMD" ] ; then
78 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
79 |
80 | Please set the JAVA_HOME variable in your environment to match the
81 | location of your Java installation."
82 | fi
83 | else
84 | JAVACMD="java"
85 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
86 |
87 | Please set the JAVA_HOME variable in your environment to match the
88 | location of your Java installation."
89 | fi
90 |
91 | # Increase the maximum file descriptors if we can.
92 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
93 | MAX_FD_LIMIT=`ulimit -H -n`
94 | if [ $? -eq 0 ] ; then
95 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
96 | MAX_FD="$MAX_FD_LIMIT"
97 | fi
98 | ulimit -n $MAX_FD
99 | if [ $? -ne 0 ] ; then
100 | warn "Could not set maximum file descriptor limit: $MAX_FD"
101 | fi
102 | else
103 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
104 | fi
105 | fi
106 |
107 | # For Darwin, add options to specify how the application appears in the dock
108 | if $darwin; then
109 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
110 | fi
111 |
112 | # For Cygwin, switch paths to Windows format before running java
113 | if $cygwin ; then
114 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
115 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
116 | JAVACMD=`cygpath --unix "$JAVACMD"`
117 |
118 | # We build the pattern for arguments to be converted via cygpath
119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
120 | SEP=""
121 | for dir in $ROOTDIRSRAW ; do
122 | ROOTDIRS="$ROOTDIRS$SEP$dir"
123 | SEP="|"
124 | done
125 | OURCYGPATTERN="(^($ROOTDIRS))"
126 | # Add a user-defined pattern to the cygpath arguments
127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
129 | fi
130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
131 | i=0
132 | for arg in "$@" ; do
133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
135 |
136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
138 | else
139 | eval `echo args$i`="\"$arg\""
140 | fi
141 | i=$((i+1))
142 | done
143 | case $i in
144 | (0) set -- ;;
145 | (1) set -- "$args0" ;;
146 | (2) set -- "$args0" "$args1" ;;
147 | (3) set -- "$args0" "$args1" "$args2" ;;
148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
154 | esac
155 | fi
156 |
157 | # Escape application args
158 | save () {
159 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
160 | echo " "
161 | }
162 | APP_ARGS=$(save "$@")
163 |
164 | # Collect all arguments for the java command, following the shell quoting and substitution rules
165 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
166 |
167 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
168 | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
169 | cd "$(dirname "$0")"
170 | fi
171 |
172 | exec "$JAVACMD" "$@"
173 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_my_profile.xml:
--------------------------------------------------------------------------------
1 |
2 |
10 |
11 |
15 |
16 |
22 |
23 |
24 |
28 |
29 |
38 |
39 |
45 |
46 |
51 |
52 |
58 |
59 |
66 |
67 |
68 |
74 |
75 |
84 |
85 |
86 |
92 |
93 |
100 |
101 |
102 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_sign_up.xml:
--------------------------------------------------------------------------------
1 |
2 |
10 |
11 |
15 |
23 |
24 |
25 |
31 |
32 |
42 |
43 |
51 |
52 |
57 |
58 |
63 |
64 |
71 |
72 |
73 |
79 |
80 |
87 |
88 |
89 |
95 |
96 |
103 |
104 |
105 |
121 |
122 |
123 |
124 |
--------------------------------------------------------------------------------
/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/learning/self/kotlin/projectmanager/activities/CreateBoardActivity.kt:
--------------------------------------------------------------------------------
1 | package learning.self.kotlin.projectmanager.activities
2 |
3 | import android.Manifest
4 | import android.app.Activity
5 | import android.content.Intent
6 | import android.content.pm.PackageManager
7 | import android.graphics.Typeface
8 | import android.net.Uri
9 | import androidx.appcompat.app.AppCompatActivity
10 | import android.os.Bundle
11 | import android.provider.MediaStore
12 | import android.util.Log
13 | import android.widget.Toast
14 | import androidx.core.app.ActivityCompat
15 | import androidx.core.content.ContextCompat
16 | import com.bumptech.glide.Glide
17 | import com.google.firebase.storage.FirebaseStorage
18 | import com.google.firebase.storage.StorageReference
19 | import kotlinx.android.synthetic.main.activity_create_board.*
20 | import kotlinx.android.synthetic.main.activity_my_profile.*
21 | import learning.self.kotlin.projectmanager.R
22 | import learning.self.kotlin.projectmanager.firebase.FireStoreHandler
23 | import learning.self.kotlin.projectmanager.models.Board
24 | import learning.self.kotlin.projectmanager.utils.Constants
25 | import java.io.IOException
26 |
27 | class CreateBoardActivity : BaseActivity() {
28 |
29 | private var mSelectedImageFileUri: Uri? = null
30 | private lateinit var mUserName: String
31 | private var mBoardImageURL: String = ""
32 |
33 | override fun onCreate(savedInstanceState: Bundle?) {
34 | super.onCreate(savedInstanceState)
35 | setContentView(R.layout.activity_create_board)
36 | setFonts()
37 | setActionBar()
38 |
39 | if (intent.hasExtra(Constants.NAME)) {
40 | mUserName = intent.getStringExtra(Constants.NAME)
41 | }
42 |
43 | create_board_image.setOnClickListener {
44 | if (ContextCompat.checkSelfPermission(this,
45 | Manifest.permission.READ_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED) {
46 | Constants.showImageChooser(this)
47 | } else {
48 | ActivityCompat.requestPermissions(this,
49 | arrayOf(Manifest.permission.READ_EXTERNAL_STORAGE),
50 | Constants.READ_STORAGE_PERMISSION_CODE)
51 | }
52 | }
53 |
54 | create_board_create_btn.setOnClickListener {
55 | if(mSelectedImageFileUri != null){
56 | uploadBoardImage()
57 | }else{
58 | showProgressDialog()
59 | createBoard()
60 | }
61 | }
62 |
63 |
64 | }
65 |
66 | private fun createBoard() {
67 | val assignedUsers: ArrayList = ArrayList()
68 | assignedUsers.add(getCurrentUserID())
69 |
70 | var board = Board(
71 | create_board_name_et.text.toString(),
72 | mBoardImageURL,
73 | mUserName,
74 | assignedUsers
75 | )
76 |
77 | FireStoreHandler().createBoard(this, board)
78 |
79 | }
80 |
81 | private fun uploadBoardImage() {
82 | showProgressDialog()
83 |
84 | val sRef: StorageReference = FirebaseStorage.getInstance()
85 | .reference
86 | .child(
87 | "BOARD_IMAGE" + System.currentTimeMillis()
88 | + "." + Constants.getFileExtension(this, mSelectedImageFileUri)
89 | )
90 |
91 | sRef.putFile(mSelectedImageFileUri!!).addOnSuccessListener { taskSnapshot ->
92 | Log.e(
93 | "Firebase Image URL",
94 | taskSnapshot.metadata!!.reference!!.downloadUrl!!.toString()
95 | )
96 |
97 | taskSnapshot.metadata!!.reference!!.downloadUrl.addOnSuccessListener { uri ->
98 | Log.e("Downloadable Image URL", uri.toString())
99 | mBoardImageURL = uri.toString()
100 |
101 | createBoard()
102 | }
103 | }.addOnFailureListener { exception ->
104 | Toast.makeText(this, exception.message, Toast.LENGTH_SHORT).show()
105 | hideProgressDialog()
106 | }
107 | }
108 |
109 | override fun onRequestPermissionsResult(
110 | requestCode: Int,
111 | permissions: Array,
112 | grantResults: IntArray
113 | ) {
114 | super.onRequestPermissionsResult(requestCode, permissions, grantResults)
115 | if (requestCode == Constants.READ_STORAGE_PERMISSION_CODE) {
116 | if (grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
117 | Constants.showImageChooser(this)
118 | }
119 | } else {
120 | Toast.makeText(
121 | this, "You just denied permission for storage." +
122 | "You can allow it from the settings.", Toast.LENGTH_SHORT
123 | ).show()
124 | }
125 | }
126 |
127 | override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
128 | super.onActivityResult(requestCode, resultCode, data)
129 | //check if the user select a photo from gallery
130 | if (resultCode == Activity.RESULT_OK
131 | && requestCode == Constants.PICK_IMAGE_REQUEST_CODE
132 | && data!!.data != null
133 | ) {
134 | mSelectedImageFileUri = data.data
135 | // set the new photo to profile image
136 | try {
137 | Glide.with(this)
138 | .load(mSelectedImageFileUri)
139 | .fitCenter()
140 | .placeholder(R.drawable.ic_board_place_holder)
141 | .into(create_board_image)
142 | } catch (e: IOException) {
143 | e.printStackTrace()
144 | }
145 | }
146 | }
147 |
148 | fun boardCreatedSuccessfully() {
149 | hideProgressDialog()
150 | setResult(Activity.RESULT_OK)
151 | finish()
152 | }
153 |
154 | private fun setFonts() {
155 | val regularFont: Typeface = Typeface.createFromAsset(assets, "Raleway-Regular.ttf")
156 | create_board_name_et.typeface = regularFont
157 | create_board_create_btn.typeface = regularFont
158 | }
159 |
160 | private fun setActionBar() {
161 | setSupportActionBar(toolbar_create_board_activity)
162 | val actionBar = supportActionBar
163 | if (actionBar != null) {
164 | actionBar.setDisplayHomeAsUpEnabled(true)
165 | actionBar.setHomeAsUpIndicator(R.drawable.ic_back_white)
166 | actionBar.title = "Create Board"
167 | }
168 | toolbar_create_board_activity.setNavigationOnClickListener { onBackPressed() }
169 | }
170 | }
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_card_details.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
14 |
15 |
21 |
22 |
23 |
29 |
30 |
36 |
37 |
43 |
44 |
47 |
48 |
55 |
56 |
57 |
58 |
59 |
60 |
65 |
66 |
72 |
73 |
80 |
81 |
89 |
90 |
98 |
99 |
107 |
108 |
115 |
116 |
124 |
125 |
133 |
134 |
150 |
151 |
152 |
153 |
--------------------------------------------------------------------------------
/app/src/main/java/learning/self/kotlin/projectmanager/activities/MainActivity.kt:
--------------------------------------------------------------------------------
1 | package learning.self.kotlin.projectmanager.activities
2 |
3 | import android.app.Activity
4 | import android.content.Context
5 | import android.content.Intent
6 | import android.content.SharedPreferences
7 | import android.os.Bundle
8 | import android.util.Log
9 | import android.view.MenuItem
10 | import android.view.View
11 | import androidx.core.view.GravityCompat
12 | import androidx.recyclerview.widget.LinearLayoutManager
13 | import com.bumptech.glide.Glide
14 | import com.google.android.material.navigation.NavigationView
15 | import com.google.firebase.auth.FirebaseAuth
16 | import com.google.firebase.iid.FirebaseInstanceId
17 | import kotlinx.android.synthetic.main.activity_main.*
18 | import kotlinx.android.synthetic.main.app_bar_main.*
19 | import kotlinx.android.synthetic.main.main_content.*
20 | import kotlinx.android.synthetic.main.nav_header_main.*
21 | import learning.self.kotlin.projectmanager.R
22 | import learning.self.kotlin.projectmanager.adapters.BoardItemAdapter
23 | import learning.self.kotlin.projectmanager.firebase.FireStoreHandler
24 | import learning.self.kotlin.projectmanager.models.Board
25 | import learning.self.kotlin.projectmanager.models.User
26 | import learning.self.kotlin.projectmanager.utils.Constants
27 |
28 | class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedListener {
29 | companion object{
30 | const val MY_PROFILE_REQUEST_CODE : Int = 11
31 | const val CREATE_BOARD_REQUEST_CODE : Int = 21
32 | }
33 |
34 | private lateinit var mUserName : String
35 | private lateinit var mSharedPreferences: SharedPreferences
36 |
37 | override fun onCreate(savedInstanceState: Bundle?) {
38 | super.onCreate(savedInstanceState)
39 | setContentView(R.layout.activity_main)
40 |
41 | setActionBar()
42 | navigator_view.setNavigationItemSelectedListener(this)
43 |
44 | mSharedPreferences = this.getSharedPreferences(Constants.POJECTMANAGER_PREFERENCES, Context.MODE_PRIVATE)
45 |
46 | val tokenUpdated = mSharedPreferences.getBoolean(Constants.FCM_TOKEN_UPDATED,false)
47 | if(tokenUpdated){
48 | showProgressDialog()
49 | FireStoreHandler().loadUserData(this,true)
50 | }else{
51 | FirebaseInstanceId.getInstance().instanceId.addOnSuccessListener(this@MainActivity){
52 | instanceResult ->
53 | updateFcmToken(instanceResult.token)
54 | }
55 | }
56 |
57 | FireStoreHandler().loadUserData(this, true)
58 |
59 | create_board_fab.setOnClickListener {
60 | val intent = Intent(this,CreateBoardActivity::class.java)
61 | intent.putExtra(Constants.NAME, mUserName)
62 | startActivityForResult(intent, CREATE_BOARD_REQUEST_CODE)
63 | }
64 | }
65 |
66 | fun populateBoardsToUI(boardsList : ArrayList){
67 | hideProgressDialog()
68 |
69 | if(boardsList.size > 0 ){
70 | boards_list_rv.visibility = View.VISIBLE
71 | no_boards_tv.visibility = View.GONE
72 |
73 | boards_list_rv.layoutManager = LinearLayoutManager(this)
74 | boards_list_rv.setHasFixedSize(true)
75 |
76 | val adapter = BoardItemAdapter(this, boardsList)
77 | boards_list_rv.adapter = adapter
78 |
79 | adapter.setOnClickListener(object : BoardItemAdapter.OnClickListener{
80 | override fun onClick(position: Int, model: Board) {
81 | val intent = Intent(this@MainActivity, TaskListActivity::class.java)
82 | intent.putExtra(Constants.DOCUMENT_ID, model.documentID)
83 | startActivity(intent)
84 | }
85 | })
86 | }else{
87 | boards_list_rv.visibility = View.GONE
88 | no_boards_tv.visibility = View.VISIBLE
89 | }
90 | }
91 |
92 | private fun setActionBar(){
93 | setSupportActionBar(toolbar_main_activity)
94 | toolbar_main_activity.setNavigationIcon(R.drawable.ic_navigation_manu)
95 | toolbar_main_activity.setNavigationOnClickListener {
96 | toggleDrawer()
97 | }
98 | }
99 |
100 | override fun onBackPressed() {
101 | if(drawer_layout.isDrawerOpen(GravityCompat.START)){
102 | drawer_layout.closeDrawer(GravityCompat.START)
103 | }else{
104 | doubleBackToExit()
105 | }
106 | }
107 |
108 | private fun toggleDrawer(){
109 | if(drawer_layout.isDrawerOpen(GravityCompat.START)){
110 | drawer_layout.closeDrawer(GravityCompat.START)
111 | }else{
112 | drawer_layout.openDrawer(GravityCompat.START)
113 | }
114 | }
115 | // set logged user image and name to the navigator
116 | fun updateNavigationUserDetails(user : User, readBoardsList : Boolean){
117 | hideProgressDialog()
118 | mUserName = user.name
119 |
120 | // add image
121 | Glide.with(this)
122 | .load(user.image)
123 | .fitCenter()
124 | .placeholder(R.drawable.ic_user_place_holder)
125 | .into(nav_user_image)
126 | // add name
127 | user_name_tv.text = user.name
128 |
129 | if(readBoardsList){
130 | showProgressDialog()
131 | FireStoreHandler().getBoardsList(this)
132 | }
133 |
134 | }
135 |
136 | override fun onNavigationItemSelected(item: MenuItem): Boolean {
137 | when(item.itemId){
138 | R.id.nav_my_profile -> {
139 | startActivityForResult(Intent(this, MyProfileActivity::class.java), MY_PROFILE_REQUEST_CODE)
140 | }
141 | R.id.nav_sign_out -> {
142 | FirebaseAuth.getInstance().signOut()
143 | mSharedPreferences.edit().clear().apply() //reset the shared prefs - make sure that shared preferences not stored
144 | val intent = Intent(this, LoginActivity::class.java)
145 | intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_NEW_TASK)
146 | startActivity(intent)
147 | finish()
148 | }
149 | }
150 | drawer_layout.closeDrawer(GravityCompat.START)
151 | return true
152 | }
153 |
154 | override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
155 | super.onActivityResult(requestCode, resultCode, data)
156 | if(resultCode == Activity.RESULT_OK && requestCode == MY_PROFILE_REQUEST_CODE){
157 | FireStoreHandler().loadUserData(this)
158 | }else if(resultCode == Activity.RESULT_OK && requestCode == CREATE_BOARD_REQUEST_CODE){
159 | FireStoreHandler().getBoardsList(this)
160 | } else{
161 | Log.e("MainOnActivityResult", "Cancelled")
162 | }
163 | }
164 |
165 | fun tokenUpdateSuccess(){
166 | hideProgressDialog()
167 | val editor : SharedPreferences.Editor = mSharedPreferences.edit()
168 | editor.putBoolean(Constants.FCM_TOKEN_UPDATED, true)
169 | editor.apply()
170 | showProgressDialog()
171 | FireStoreHandler().loadUserData(this,true)
172 | }
173 |
174 | private fun updateFcmToken(token : String){
175 | val userHashMap = HashMap()
176 | userHashMap[Constants.FCM_TOKEN] = token
177 | showProgressDialog()
178 | FireStoreHandler().updateUserProfileData(this,userHashMap)
179 | }
180 | }
--------------------------------------------------------------------------------
/app/src/main/java/learning/self/kotlin/projectmanager/activities/TaskListActivity.kt:
--------------------------------------------------------------------------------
1 | package learning.self.kotlin.projectmanager.activities
2 |
3 | import android.app.Activity
4 | import android.content.Intent
5 | import android.os.Bundle
6 | import android.util.Log
7 | import android.view.Menu
8 | import android.view.MenuItem
9 | import androidx.recyclerview.widget.LinearLayoutManager
10 | import kotlinx.android.synthetic.main.activity_task_list.*
11 | import learning.self.kotlin.projectmanager.R
12 | import learning.self.kotlin.projectmanager.adapters.TaskItemAdapter
13 | import learning.self.kotlin.projectmanager.firebase.FireStoreHandler
14 | import learning.self.kotlin.projectmanager.models.Board
15 | import learning.self.kotlin.projectmanager.models.Card
16 | import learning.self.kotlin.projectmanager.models.Task
17 | import learning.self.kotlin.projectmanager.models.User
18 | import learning.self.kotlin.projectmanager.utils.Constants
19 |
20 | class TaskListActivity : BaseActivity() {
21 |
22 | private lateinit var mBoardDetails: Board
23 | private lateinit var mBoardDocumentID : String
24 | lateinit var mAssignedMembersDetailList : ArrayList
25 |
26 | override fun onCreate(savedInstanceState: Bundle?) {
27 | super.onCreate(savedInstanceState)
28 | setContentView(R.layout.activity_task_list)
29 |
30 | if(intent.hasExtra(Constants.DOCUMENT_ID)){
31 | mBoardDocumentID = intent.getStringExtra(Constants.DOCUMENT_ID)
32 | }
33 | showProgressDialog()
34 | FireStoreHandler().getBoardDetails(this,mBoardDocumentID)
35 | }
36 |
37 | fun boardDetails(board : Board){
38 | mBoardDetails = board
39 | hideProgressDialog()
40 | setActionBar()
41 |
42 | showProgressDialog()
43 | FireStoreHandler().getAssignedMembersListDetails(this, mBoardDetails.assignedTo)
44 | }
45 |
46 | override fun onCreateOptionsMenu(menu: Menu?): Boolean {
47 | menuInflater.inflate(R.menu.menu_members,menu)
48 | return super.onCreateOptionsMenu(menu)
49 | }
50 |
51 | override fun onOptionsItemSelected(item: MenuItem): Boolean {
52 | when(item.itemId){
53 | R.id.action_members -> {
54 | val intent = Intent(this,MembersActivity::class.java)
55 | intent.putExtra(Constants.BOARD_DETAIL, mBoardDetails)
56 | startActivityForResult(intent, MEMBERS_REQUEST_CODE)
57 | return true
58 | }
59 | }
60 | return super.onOptionsItemSelected(item)
61 | }
62 |
63 | override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
64 | super.onActivityResult(requestCode, resultCode, data)
65 | if(resultCode == Activity.RESULT_OK
66 | && requestCode == MEMBERS_REQUEST_CODE
67 | || requestCode == CARD_DETAILS_REQUEST_CODE){
68 | showProgressDialog()
69 | FireStoreHandler().getBoardDetails(this,mBoardDocumentID)
70 | }else{
71 | Log.e("Canceled", " Cancelled")
72 | }
73 | }
74 |
75 | private fun setActionBar(){
76 | setSupportActionBar(task_list_activity_toolbar)
77 | val actionBar = supportActionBar
78 | if(actionBar != null){
79 | actionBar.setDisplayHomeAsUpEnabled(true)
80 | actionBar.setHomeAsUpIndicator(R.drawable.ic_back_white)
81 | actionBar.title = mBoardDetails.name
82 | }
83 | task_list_activity_toolbar.setNavigationOnClickListener{onBackPressed()}
84 | }
85 |
86 | fun addUpdateTaskListSuccess(){
87 | hideProgressDialog()
88 | showProgressDialog()
89 | FireStoreHandler().getBoardDetails(this,mBoardDetails.documentID)
90 | }
91 |
92 | fun createTaskList(taskName : String){
93 | val task = Task(taskName,FireStoreHandler().getCurrentUserId())
94 | mBoardDetails.taskList.add(0,task)
95 | mBoardDetails.taskList.removeAt(mBoardDetails.taskList.size-1)// Remove the last position as we have added the item manually for adding the TaskList.
96 |
97 | showProgressDialog()
98 | FireStoreHandler().addUpdateTaskList(this,mBoardDetails)
99 | }
100 |
101 | fun updateTaskList(position: Int, listName: String, model : Task){
102 | val task = Task(listName,model.createdBy)
103 | mBoardDetails.taskList[position] = task
104 | mBoardDetails.taskList.removeAt(mBoardDetails.taskList.size-1)
105 |
106 | showProgressDialog()
107 | FireStoreHandler().addUpdateTaskList(this,mBoardDetails)
108 | }
109 |
110 | fun deleteTaskList(position : Int){
111 | mBoardDetails.taskList.removeAt(position)
112 | mBoardDetails.taskList.removeAt(mBoardDetails.taskList.size-1)
113 |
114 | showProgressDialog()
115 | FireStoreHandler().addUpdateTaskList(this,mBoardDetails)
116 | }
117 |
118 | fun addCardToTask(position : Int , cardName:String){
119 | mBoardDetails.taskList.removeAt(mBoardDetails.taskList.size-1)
120 |
121 | val cardAssignedUsersList : ArrayList = ArrayList()
122 | val currentUserID = FireStoreHandler().getCurrentUserId()
123 | cardAssignedUsersList.add(currentUserID)
124 | val card = Card(cardName, currentUserID,cardAssignedUsersList)
125 |
126 | val cardList = mBoardDetails.taskList[position].cards
127 | cardList.add(card) //add the card to cads list in the task list
128 |
129 | val task = Task(mBoardDetails.taskList[position].title,
130 | mBoardDetails.taskList[position].createdBy,
131 | cardList)
132 |
133 | mBoardDetails.taskList[position] = task
134 |
135 | showProgressDialog()
136 | FireStoreHandler().addUpdateTaskList(this,mBoardDetails)
137 | }
138 |
139 | fun cardDetails(taskListPosition: Int, cardPosition: Int) {
140 | val intent = Intent(this@TaskListActivity, CardDetailsActivity::class.java)
141 | intent.putExtra(Constants.BOARD_DETAIL, mBoardDetails)
142 | intent.putExtra(Constants.TASK_LIST_ITEM_POSITION, taskListPosition)
143 | intent.putExtra(Constants.CARD_LIST_ITEM_POSITION, cardPosition)
144 | intent.putExtra(Constants.BOARD_MEMBERS_LIST, mAssignedMembersDetailList)
145 | startActivityForResult(intent, CARD_DETAILS_REQUEST_CODE)
146 | }
147 |
148 | fun boardMembersDetailList(list : ArrayList){
149 | mAssignedMembersDetailList = list
150 | hideProgressDialog()
151 |
152 | val addTaskList = Task("Add List")
153 | mBoardDetails.taskList.add(addTaskList)
154 |
155 | task_list_rv.layoutManager = LinearLayoutManager(this, LinearLayoutManager.HORIZONTAL,false)
156 | task_list_rv.setHasFixedSize(true)
157 |
158 | val adapter = TaskItemAdapter(this,mBoardDetails.taskList)
159 | task_list_rv.adapter = adapter
160 |
161 | }
162 |
163 | fun updateCardsInTaskList(taskListPosition: Int, cards : ArrayList){
164 | //remove the 'add card' card
165 | mBoardDetails.taskList.removeAt(mBoardDetails.taskList.size - 1)
166 | mBoardDetails.taskList[taskListPosition].cards = cards
167 | //show progress dialog and update the database with the new order of list
168 | showProgressDialog()
169 | FireStoreHandler().addUpdateTaskList(this,mBoardDetails)
170 | }
171 |
172 | companion object{
173 | const val MEMBERS_REQUEST_CODE : Int = 13
174 | const val CARD_DETAILS_REQUEST_CODE : Int = 14
175 | }
176 | }
--------------------------------------------------------------------------------
/app/src/main/java/learning/self/kotlin/projectmanager/activities/MyProfileActivity.kt:
--------------------------------------------------------------------------------
1 | package learning.self.kotlin.projectmanager.activities
2 |
3 | import android.Manifest
4 | import android.app.Activity
5 | import android.content.Intent
6 | import android.content.pm.PackageManager
7 | import android.graphics.Typeface
8 | import android.net.Uri
9 | import android.os.Bundle
10 | import android.util.Log
11 | import android.widget.Toast
12 | import androidx.core.app.ActivityCompat
13 | import androidx.core.content.ContextCompat
14 | import com.bumptech.glide.Glide
15 | import com.google.firebase.storage.FirebaseStorage
16 | import com.google.firebase.storage.StorageReference
17 | import kotlinx.android.synthetic.main.activity_my_profile.*
18 | import learning.self.kotlin.projectmanager.R
19 | import learning.self.kotlin.projectmanager.firebase.FireStoreHandler
20 | import learning.self.kotlin.projectmanager.models.User
21 | import learning.self.kotlin.projectmanager.utils.Constants
22 | import java.io.IOException
23 |
24 | class MyProfileActivity : BaseActivity() {
25 |
26 | private var mSelectedImageFileUri : Uri? = null
27 | private var mProfileImageURL : String = ""
28 | private lateinit var mUserDetails : User
29 | override fun onCreate(savedInstanceState: Bundle?) {
30 | super.onCreate(savedInstanceState)
31 | setContentView(R.layout.activity_my_profile)
32 | setFonts()
33 | setActionBar()
34 |
35 | my_profile_user_image.setOnClickListener {
36 | if(ContextCompat.checkSelfPermission(this,
37 | Manifest.permission.READ_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED){
38 | Constants.showImageChooser(this)
39 | }else{
40 | ActivityCompat.requestPermissions(this,
41 | arrayOf(Manifest.permission.READ_EXTERNAL_STORAGE),
42 | Constants.READ_STORAGE_PERMISSION_CODE)
43 | }
44 | }
45 | FireStoreHandler().loadUserData(this)
46 |
47 | my_profile_update_btn.setOnClickListener {
48 | if(mSelectedImageFileUri != null){
49 | uploadUserImage()
50 | }else{
51 | showProgressDialog()
52 | updateUserProfileData()
53 | }
54 | }
55 | }
56 |
57 | override fun onRequestPermissionsResult(
58 | requestCode: Int,
59 | permissions: Array,
60 | grantResults: IntArray
61 | ) {
62 | super.onRequestPermissionsResult(requestCode, permissions, grantResults)
63 | if(requestCode == Constants.READ_STORAGE_PERMISSION_CODE){
64 | if(grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED){
65 | Constants.showImageChooser(this)
66 | }
67 | }else{
68 | Toast.makeText(this, "You just denied permission for storage." +
69 | "You can allow it from the settings.", Toast.LENGTH_SHORT).show()
70 | }
71 | }
72 |
73 | override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
74 | super.onActivityResult(requestCode, resultCode, data)
75 | //check if the user select a photo from gallery
76 | if(resultCode == Activity.RESULT_OK
77 | && requestCode == Constants.PICK_IMAGE_REQUEST_CODE
78 | && data!!.data != null){
79 | mSelectedImageFileUri = data.data
80 | // set the new photo to profile image
81 | try{
82 | Glide.with(this)
83 | .load(mSelectedImageFileUri)
84 | .fitCenter()
85 | .placeholder(R.drawable.ic_user_place_holder)
86 | .into(my_profile_user_image)
87 | }catch (e : IOException){
88 | e.printStackTrace()
89 | }
90 | }
91 | }
92 |
93 | private fun setFonts(){
94 | val regularFont: Typeface = Typeface.createFromAsset(assets, "Raleway-Regular.ttf")
95 | my_profile_name_et.typeface = regularFont
96 | my_profile_email_et.typeface = regularFont
97 | my_profile_mobile_et.typeface = regularFont
98 | my_profile_update_btn.typeface = regularFont
99 | }
100 |
101 | private fun setActionBar(){
102 | setSupportActionBar(toolbar_my_profile_activity)
103 | val actionBar = supportActionBar
104 | if(actionBar != null){
105 | actionBar.setDisplayHomeAsUpEnabled(true)
106 | actionBar.setHomeAsUpIndicator(R.drawable.ic_back_white)
107 | actionBar.title = "My Profile"
108 | }
109 | toolbar_my_profile_activity.setNavigationOnClickListener{onBackPressed()}
110 | }
111 |
112 | fun setUserDataInUI(user : User){
113 | mUserDetails = user
114 | //set user image
115 | Glide.with(this)
116 | .load(user.image)
117 | .fitCenter()
118 | .placeholder(R.drawable.ic_user_place_holder)
119 | .into(my_profile_user_image)
120 | //set user details
121 | my_profile_name_et.setText(user.name)
122 | my_profile_email_et.setText(user.email)
123 | if(user.mobile != 0L){
124 | my_profile_mobile_et.setText(user.mobile.toString())
125 | }
126 | }
127 |
128 | // store user image to firebase storage
129 | private fun uploadUserImage(){
130 | showProgressDialog()
131 |
132 | if(mSelectedImageFileUri != null){
133 | val sRef : StorageReference = FirebaseStorage.getInstance()
134 | .reference
135 | .child("USER_IMAGE" + System.currentTimeMillis()
136 | + "." + Constants.getFileExtension(this,mSelectedImageFileUri))
137 |
138 | sRef.putFile(mSelectedImageFileUri!!).addOnSuccessListener {
139 | taskSnapshot ->
140 | Log.e("Firebase Image URL", taskSnapshot.metadata!!.reference!!.downloadUrl!!.toString())
141 |
142 | taskSnapshot.metadata!!.reference!!.downloadUrl.addOnSuccessListener {
143 | uri ->
144 | Log.e("Downloadable Image URL", uri.toString())
145 | mProfileImageURL = uri.toString()
146 |
147 | updateUserProfileData() }
148 | }.addOnFailureListener{
149 | exception ->
150 | Toast.makeText(this,exception.message,Toast.LENGTH_SHORT).show()
151 | hideProgressDialog()
152 | }
153 | }
154 | }
155 |
156 | private fun updateUserProfileData(){
157 | val userHashMap = HashMap()
158 | var anyChangesMade : Boolean = false
159 |
160 | if(mProfileImageURL.isNotEmpty() && mProfileImageURL != mUserDetails.image){
161 | userHashMap[Constants.IMAGE] = mProfileImageURL
162 | anyChangesMade = true
163 | }
164 |
165 | if(my_profile_name_et.text.toString() != mUserDetails.name){
166 | userHashMap[Constants.NAME] = my_profile_name_et.text.toString()
167 | anyChangesMade = true
168 | }
169 |
170 | if(my_profile_mobile_et.text.toString() != mUserDetails.mobile.toString()){
171 | userHashMap[Constants.MOBILE] = my_profile_mobile_et.text.toString().toLong()
172 | anyChangesMade = true
173 | }
174 |
175 | if(anyChangesMade)
176 | FireStoreHandler().updateUserProfileData(this,userHashMap)
177 | else{
178 | Toast.makeText(this,"Nothing as been updated!",Toast.LENGTH_SHORT).show()
179 | hideProgressDialog()
180 | }
181 | }
182 |
183 | fun profileUpdateSuccess(){
184 | hideProgressDialog()
185 | setResult(Activity.RESULT_OK)
186 | finish()
187 | }
188 |
189 |
190 | }
--------------------------------------------------------------------------------
/app/src/main/java/learning/self/kotlin/projectmanager/activities/MembersActivity.kt:
--------------------------------------------------------------------------------
1 | package learning.self.kotlin.projectmanager.activities
2 |
3 | import android.app.Activity
4 | import android.app.Dialog
5 | import android.os.AsyncTask
6 | import androidx.appcompat.app.AppCompatActivity
7 | import android.os.Bundle
8 | import android.view.ContextMenu
9 | import android.view.Menu
10 | import android.view.MenuItem
11 | import android.view.View
12 | import android.widget.Toast
13 | import androidx.recyclerview.widget.LinearLayoutManager
14 | import kotlinx.android.synthetic.main.activity_members.*
15 | import kotlinx.android.synthetic.main.activity_task_list.*
16 | import kotlinx.android.synthetic.main.dialog_serach_member.*
17 | import learning.self.kotlin.projectmanager.R
18 | import learning.self.kotlin.projectmanager.adapters.MemberItemAdapter
19 | import learning.self.kotlin.projectmanager.adapters.TaskItemAdapter
20 | import learning.self.kotlin.projectmanager.firebase.FireStoreHandler
21 | import learning.self.kotlin.projectmanager.models.Board
22 | import learning.self.kotlin.projectmanager.models.User
23 | import learning.self.kotlin.projectmanager.utils.Constants
24 | import org.json.JSONObject
25 | import java.io.*
26 | import java.lang.Exception
27 | import java.lang.StringBuilder
28 | import java.net.HttpURLConnection
29 | import java.net.SocketTimeoutException
30 | import java.net.URL
31 |
32 | class MembersActivity : BaseActivity() {
33 |
34 | private lateinit var mBoardDetails : Board
35 | private lateinit var mAssignedMembersList : ArrayList
36 | private var anyChangesMade : Boolean = false
37 |
38 | override fun onCreate(savedInstanceState: Bundle?) {
39 | super.onCreate(savedInstanceState)
40 | setContentView(R.layout.activity_members)
41 |
42 | if(intent.hasExtra(Constants.BOARD_DETAIL)){
43 | mBoardDetails = intent.getParcelableExtra(Constants.BOARD_DETAIL)
44 | showProgressDialog()
45 | FireStoreHandler().getAssignedMembersListDetails(this,mBoardDetails.assignedTo)
46 | }
47 | setActionBar()
48 | }
49 |
50 | fun memberDetails(user : User){
51 | mBoardDetails.assignedTo.add(user.id)
52 | FireStoreHandler().assignMemberToBoard(this, mBoardDetails, user)
53 | }
54 |
55 | fun setUpMembersList(list : ArrayList){
56 | mAssignedMembersList = list
57 | hideProgressDialog()
58 |
59 | members_list_rv.layoutManager = LinearLayoutManager(this, LinearLayoutManager.VERTICAL,false)
60 | members_list_rv.setHasFixedSize(true)
61 |
62 | val adapter = MemberItemAdapter(this,list)
63 | members_list_rv.adapter = adapter
64 | }
65 |
66 | private fun setActionBar(){
67 | setSupportActionBar(members_activity_toolbar)
68 | val actionBar = supportActionBar
69 | if(actionBar != null){
70 | actionBar.setDisplayHomeAsUpEnabled(true)
71 | actionBar.setHomeAsUpIndicator(R.drawable.ic_back_white)
72 | actionBar.title = mBoardDetails.name + " Members"
73 | }
74 | members_activity_toolbar.setNavigationOnClickListener{onBackPressed()}
75 | }
76 |
77 | override fun onCreateOptionsMenu(menu: Menu?): Boolean {
78 | menuInflater.inflate(R.menu.menu_add_member, menu)
79 | return super.onCreateOptionsMenu(menu)
80 | }
81 |
82 | override fun onOptionsItemSelected(item: MenuItem): Boolean {
83 | when(item.itemId){
84 | R.id.action_add_member ->{
85 | dialogSearchMember()
86 | return true
87 | }
88 | }
89 | return super.onOptionsItemSelected(item)
90 | }
91 |
92 | private fun dialogSearchMember(){
93 | val dialog = Dialog(this)
94 | dialog.setContentView(R.layout.dialog_serach_member)
95 |
96 | dialog.add_tv.setOnClickListener {
97 | val email = dialog.email_search_member_et.text.toString()
98 | if(email.isNotEmpty()){
99 | showProgressDialog()
100 | FireStoreHandler().getMemberDetails(this, email)
101 | dialog.dismiss()
102 | }else{
103 | Toast.makeText(this,"Please enter email address",Toast.LENGTH_SHORT).show()
104 | }
105 | }
106 |
107 | dialog.cancel_tv.setOnClickListener {
108 | dialog.dismiss()
109 | }
110 |
111 | dialog.show()
112 | }
113 |
114 | override fun onBackPressed() {
115 | if(anyChangesMade){
116 | setResult(Activity.RESULT_OK)
117 | }
118 | super.onBackPressed()
119 | }
120 |
121 | fun memberAssignSuccess(user : User){
122 | hideProgressDialog()
123 | mAssignedMembersList.add(user)
124 | anyChangesMade = true
125 | setUpMembersList(mAssignedMembersList)
126 | SendNotificationToUserAsyncTask(mBoardDetails.name,user.fcmToken)
127 | }
128 |
129 | private inner class SendNotificationToUserAsyncTask(val boardName : String, val token : String): AsyncTask (){
130 |
131 | override fun onPreExecute() {
132 | super.onPreExecute()
133 | showProgressDialog()
134 | }
135 |
136 | override fun doInBackground(vararg params: Any?): String {
137 | var result : String
138 | var connection : HttpURLConnection? = null
139 | try{
140 | val url = URL(Constants.FCM_BASE_URL)
141 | connection = url.openConnection() as HttpURLConnection
142 | connection.doOutput = true
143 | connection.doInput = true
144 | connection.instanceFollowRedirects = false
145 | connection.requestMethod = "POST"
146 |
147 | connection.setRequestProperty("Content-Type", "application/json")
148 | connection.setRequestProperty("charset", "utf-8")
149 | connection.setRequestProperty("Accept", "application/json")
150 |
151 | connection.setRequestProperty(Constants.FCM_AUTHORIZATION, "${Constants.FCM_KEY}=${Constants.FCM_SERVER_KEY}")
152 | connection.useCaches = false
153 |
154 | val wr = DataOutputStream(connection.outputStream)
155 | val jsonRequest = JSONObject()
156 | val dataObject = JSONObject()
157 | dataObject.put(Constants.FCM_KEY_TITLE, "Assigned to the board $boardName")
158 | dataObject.put(Constants.FCM_KEY_MESSAGE, "You have been assigned to the Board by ${mAssignedMembersList[0].name}")
159 | jsonRequest.put(Constants.FCM_KEY_DATA, dataObject)
160 | jsonRequest.put(Constants.FCM_KEY_TO, token)
161 |
162 | wr.writeBytes(jsonRequest.toString())
163 | wr.flush()
164 | wr.close()
165 |
166 | val httpResult : Int = connection.responseCode
167 | if(httpResult == HttpURLConnection.HTTP_OK){
168 | val inputStream = connection.inputStream
169 | val reader = BufferedReader(InputStreamReader(inputStream))
170 |
171 | val stringBuilder = StringBuilder()
172 | var line : String?
173 | try{
174 | while(reader.readLine().also {line=it} != null){
175 | stringBuilder.append(line+"\n")
176 | }
177 | }catch (e : IOException){
178 | e.printStackTrace()
179 | }finally {
180 | try{
181 | inputStream.close()
182 | }catch (e : IOException){
183 | e.printStackTrace()
184 | }
185 | }
186 | result = stringBuilder.toString()
187 | }else{
188 | result = connection.responseMessage
189 | }
190 | }catch (e : SocketTimeoutException){
191 | result = "Connection Timeout"
192 | }catch (e : Exception){
193 | result = "Error : " + e.message
194 | }finally {
195 | connection?.disconnect()
196 | }
197 | return result
198 | }
199 |
200 | override fun onPostExecute(result: String?) {
201 | super.onPostExecute(result)
202 | hideProgressDialog()
203 | }
204 | }
205 | }
--------------------------------------------------------------------------------
/app/src/main/java/learning/self/kotlin/projectmanager/firebase/FireStoreHandler.kt:
--------------------------------------------------------------------------------
1 | package learning.self.kotlin.projectmanager.firebase
2 |
3 | import android.app.Activity
4 | import android.util.Log
5 | import android.widget.Toast
6 | import com.google.firebase.auth.FirebaseAuth
7 | import com.google.firebase.firestore.FirebaseFirestore
8 | import com.google.firebase.firestore.SetOptions
9 | import learning.self.kotlin.projectmanager.activities.*
10 | import learning.self.kotlin.projectmanager.models.Board
11 | import learning.self.kotlin.projectmanager.models.User
12 | import learning.self.kotlin.projectmanager.utils.Constants
13 |
14 | class FireStoreHandler {
15 | private val mFireStore = FirebaseFirestore.getInstance()
16 |
17 | //get user details from sign-up activity and create a new user in firebase
18 | fun registerUser(activity: SignUpActivity, userInfo : User){
19 | mFireStore.collection(Constants.USERS)
20 | .document(getCurrentUserId())
21 | .set(userInfo, SetOptions.merge())
22 | .addOnSuccessListener {
23 | activity.userRegisteredSuccess() //show toast to the user
24 | }
25 | }
26 |
27 | fun getBoardDetails(activity : TaskListActivity, documentID : String){
28 | mFireStore.collection(Constants.BOARDS)
29 | .document(documentID)
30 | .get()
31 | .addOnSuccessListener {
32 | document ->
33 | Log.e("GetBoardList", document.toString())
34 | val board = document.toObject(Board::class.java)!!
35 | board.documentID = document.id
36 | activity.boardDetails(board)
37 |
38 | }.addOnFailureListener {
39 | activity.hideProgressDialog()
40 | }
41 | }
42 |
43 | fun createBoard(activity: CreateBoardActivity, boardInfo : Board){
44 | mFireStore.collection(Constants.BOARDS)
45 | .document()
46 | .set(boardInfo, SetOptions.merge())
47 | .addOnSuccessListener {
48 | Toast.makeText(activity,"Board created successfully", Toast.LENGTH_SHORT).show()
49 | activity.boardCreatedSuccessfully() //show toast to the user
50 | }.addOnFailureListener {
51 | activity.hideProgressDialog()
52 | }
53 | }
54 |
55 | fun addUpdateTaskList (activity: Activity, board : Board){
56 |
57 | val taskListHashMap = HashMap()
58 | taskListHashMap[Constants.TASK_LIST] = board.taskList
59 |
60 | mFireStore.collection(Constants.BOARDS)
61 | .document(board.documentID)
62 | .update(taskListHashMap)
63 | .addOnSuccessListener {
64 | Log.e(activity.javaClass.simpleName, "TaskList updated successfully.")
65 |
66 | if (activity is TaskListActivity) {
67 | activity.addUpdateTaskListSuccess()
68 | } else if (activity is CardDetailsActivity) {
69 | activity.addUpdateTaskListSuccess()
70 | }
71 | }
72 | .addOnFailureListener { e ->
73 | if (activity is TaskListActivity) {
74 | activity.hideProgressDialog()
75 | } else if (activity is TaskListActivity) {
76 | activity.hideProgressDialog()
77 | }
78 | Log.e(activity.javaClass.simpleName, "Error while creating a board.", e)
79 | }
80 | }
81 |
82 | fun updateUserProfileData(activity: Activity, userHashMap : HashMap){
83 | mFireStore.collection(Constants.USERS)
84 | .document(getCurrentUserId())
85 | .update(userHashMap)
86 | .addOnSuccessListener {
87 | Toast.makeText(activity,"Profile updated successfully!",Toast.LENGTH_SHORT).show()
88 | when(activity){
89 | is MainActivity -> {
90 | activity.tokenUpdateSuccess()
91 | }
92 |
93 | is MyProfileActivity ->{
94 | activity.profileUpdateSuccess()
95 | }
96 | }
97 | }.addOnFailureListener {
98 | when(activity){
99 | is MainActivity -> {
100 | activity.hideProgressDialog()
101 | }
102 |
103 | is MyProfileActivity ->{
104 | activity.hideProgressDialog()
105 | }
106 | }
107 | Toast.makeText(activity,"Failed updating profile!",Toast.LENGTH_SHORT).show()
108 |
109 | }
110 | }
111 |
112 | fun getBoardsList(activity: MainActivity){
113 | mFireStore.collection(Constants.BOARDS)
114 | .whereArrayContains(Constants.ASSIGNED_TO, getCurrentUserId())
115 | .get()
116 | .addOnSuccessListener {
117 | document ->
118 | Log.e("GetBoardList", document.documents.toString())
119 | val boardsList : ArrayList = ArrayList()
120 | for (i in document.documents){
121 | val board = i.toObject(Board::class.java)!!
122 | board.documentID = i.id
123 | boardsList.add(board);
124 | }
125 | //pass the result to base activity
126 | activity.populateBoardsToUI(boardsList)
127 | }.addOnFailureListener {
128 | activity.hideProgressDialog()
129 | }
130 | }
131 |
132 | fun loadUserData(activity: Activity, readBoardsList : Boolean = false){
133 | mFireStore.collection(Constants.USERS)
134 | .document(getCurrentUserId())
135 | .get()
136 | .addOnSuccessListener { document ->
137 | val loggedInUser = document.toObject(User::class.java)!!
138 | when (activity) {
139 | is SignInActivity -> {
140 | activity.signInSuccess(loggedInUser)
141 | }
142 | is MainActivity -> {
143 | activity.updateNavigationUserDetails(loggedInUser, readBoardsList)
144 | }
145 | is MyProfileActivity -> {
146 | activity.setUserDataInUI(loggedInUser)
147 | }
148 | }
149 |
150 | }.addOnFailureListener {
151 | when(activity) {
152 | is SignInActivity -> {
153 | activity.hideProgressDialog()
154 | }
155 | is MainActivity -> {
156 | activity.hideProgressDialog()
157 | }
158 |
159 | }
160 | }
161 | }
162 |
163 | fun getCurrentUserId() : String {
164 | var currentUser = FirebaseAuth.getInstance().currentUser
165 | var currentUserID = ""
166 | if(currentUser != null){
167 | currentUserID = currentUser.uid
168 | }
169 | return currentUserID
170 | }
171 |
172 | fun getAssignedMembersListDetails(activity: Activity, assignedTo : ArrayList){
173 | mFireStore.collection(Constants.USERS)
174 | .whereIn(Constants.ID, assignedTo)
175 | .get()
176 | .addOnSuccessListener {
177 | document ->
178 | Log.e(activity.javaClass.simpleName, document.documents.toString())
179 |
180 | val usersList : ArrayList = ArrayList()
181 |
182 | for(i in document.documents){
183 | val user = i.toObject(User::class.java)!!
184 | usersList.add(user)
185 | }
186 |
187 | if(activity is MembersActivity){
188 | activity.setUpMembersList(usersList)
189 | }else if(activity is TaskListActivity){
190 | activity.boardMembersDetailList(usersList)
191 | }
192 | }
193 | .addOnFailureListener {
194 | e ->
195 | if(activity is MembersActivity){
196 | activity.hideProgressDialog()
197 | }else if(activity is TaskListActivity){
198 | activity.hideProgressDialog()
199 | }
200 | Log.e(activity.javaClass.simpleName, "Error while creating a List of members", e)
201 | }
202 | }
203 |
204 | fun getMemberDetails(activity: MembersActivity, email : String){
205 | mFireStore.collection(Constants.USERS)
206 | .whereEqualTo(Constants.EMAIL, email)
207 | .get()
208 | .addOnSuccessListener {
209 | document ->
210 | if(document.documents.size > 0){
211 | val user = document.documents[0].toObject(User::class.java)!!
212 | activity.memberDetails(user)
213 | }else{
214 | activity.hideProgressDialog()
215 | activity.showErrorSnackBar("No such member found")
216 | }
217 | }
218 | .addOnFailureListener {
219 | activity.hideProgressDialog()
220 | Log.e(activity.javaClass.simpleName, "Error while getting user details")
221 | }
222 | }
223 |
224 | fun assignMemberToBoard(activity: MembersActivity, board : Board, user : User){
225 | val assignedToHashMap = HashMap()
226 | assignedToHashMap[Constants.ASSIGNED_TO] = board.assignedTo
227 |
228 | mFireStore.collection(Constants.BOARDS)
229 | .document(board.documentID)
230 | .update(assignedToHashMap)
231 | .addOnSuccessListener {
232 | activity.memberAssignSuccess(user)
233 | }
234 | .addOnFailureListener {
235 | e ->
236 | activity.hideProgressDialog()
237 | Log.e(activity.javaClass.simpleName, "Error while creating a board", e)
238 | }
239 | }
240 | }
--------------------------------------------------------------------------------