├── app
├── .gitignore
├── src
│ ├── main
│ │ ├── gfd_logo-web.png
│ │ ├── res
│ │ │ ├── drawable
│ │ │ │ ├── gfd_logo.png
│ │ │ │ ├── icon_bio.png
│ │ │ │ ├── gfd_app_logo.png
│ │ │ │ ├── gfd_launcher.png
│ │ │ │ ├── icon_location.png
│ │ │ │ ├── btn_bottom_help.png
│ │ │ │ ├── btn_bottom_home.png
│ │ │ │ ├── btn_bottom_rank.png
│ │ │ │ ├── gfd_logo_foreground.png
│ │ │ │ ├── rounding_trans_background.xml
│ │ │ │ ├── rect_background.xml
│ │ │ │ ├── github_login_background.xml
│ │ │ │ ├── rounding_background.xml
│ │ │ │ ├── guest_login_background.xml
│ │ │ │ ├── profile_gradation.xml
│ │ │ │ ├── ic_close_gray.xml
│ │ │ │ ├── github_login_users_background.xml
│ │ │ │ ├── shadow.xml
│ │ │ │ ├── gfd_logo_background.xml
│ │ │ │ └── ic_launcher_background.xml
│ │ │ ├── mipmap-hdpi
│ │ │ │ ├── gfd_logo.png
│ │ │ │ ├── ic_launcher.png
│ │ │ │ ├── gfd_logo_round.png
│ │ │ │ ├── ic_launcher_round.png
│ │ │ │ ├── gfd_logo_background.png
│ │ │ │ └── gfd_logo_foreground.png
│ │ │ ├── mipmap-mdpi
│ │ │ │ ├── gfd_logo.png
│ │ │ │ ├── ic_launcher.png
│ │ │ │ ├── gfd_logo_round.png
│ │ │ │ ├── ic_launcher_round.png
│ │ │ │ ├── gfd_logo_background.png
│ │ │ │ └── gfd_logo_foreground.png
│ │ │ ├── mipmap-xhdpi
│ │ │ │ ├── gfd_logo.png
│ │ │ │ ├── ic_launcher.png
│ │ │ │ ├── gfd_logo_round.png
│ │ │ │ ├── ic_launcher_round.png
│ │ │ │ ├── gfd_logo_background.png
│ │ │ │ └── gfd_logo_foreground.png
│ │ │ ├── mipmap-xxhdpi
│ │ │ │ ├── gfd_logo.png
│ │ │ │ ├── ic_launcher.png
│ │ │ │ ├── gfd_logo_round.png
│ │ │ │ ├── gfd_logo_background.png
│ │ │ │ ├── gfd_logo_foreground.png
│ │ │ │ └── ic_launcher_round.png
│ │ │ ├── mipmap-xxxhdpi
│ │ │ │ ├── gfd_logo.png
│ │ │ │ ├── ic_launcher.png
│ │ │ │ ├── gfd_logo_round.png
│ │ │ │ ├── ic_launcher_round.png
│ │ │ │ ├── gfd_logo_background.png
│ │ │ │ └── gfd_logo_foreground.png
│ │ │ ├── values
│ │ │ │ ├── ic_launcher_background_gfd.xml
│ │ │ │ ├── styles.xml
│ │ │ │ ├── colors.xml
│ │ │ │ └── strings.xml
│ │ │ ├── xml
│ │ │ │ ├── backup_descriptor.xml
│ │ │ │ └── network_security_config.xml
│ │ │ ├── mipmap-anydpi-v26
│ │ │ │ ├── gfd_logo.xml
│ │ │ │ ├── gfd_logo_round.xml
│ │ │ │ ├── ic_launcher.xml
│ │ │ │ └── ic_launcher_round.xml
│ │ │ ├── font
│ │ │ │ └── font.xml
│ │ │ ├── anim
│ │ │ │ ├── fade_in.xml
│ │ │ │ ├── fade_out.xml
│ │ │ │ ├── bottom_to_top.xml
│ │ │ │ ├── left_to_right.xml
│ │ │ │ ├── right_to_left.xml
│ │ │ │ └── top_to_bottom.xml
│ │ │ ├── menu
│ │ │ │ └── bottom_navigation_menu.xml
│ │ │ ├── layout
│ │ │ │ ├── progressbar_dialog.xml
│ │ │ │ ├── rowlayout.xml
│ │ │ │ ├── activity_main.xml
│ │ │ │ ├── activity_launcher.xml
│ │ │ │ ├── contributions_layout.xml
│ │ │ │ ├── fragment_main_sub.xml
│ │ │ │ ├── activity_main2.xml
│ │ │ │ ├── item_search_github_id.xml
│ │ │ │ ├── rv_rank_item.xml
│ │ │ │ ├── activity_search.xml
│ │ │ │ ├── fragment_main.xml
│ │ │ │ └── fragment_rank.xml
│ │ │ └── drawable-v24
│ │ │ │ └── ic_launcher_foreground.xml
│ │ ├── java
│ │ │ └── com
│ │ │ │ └── seok
│ │ │ │ └── gfd
│ │ │ │ ├── retrofit
│ │ │ │ ├── domain
│ │ │ │ │ ├── request
│ │ │ │ │ │ ├── GUserRequestDto.kt
│ │ │ │ │ │ └── CommitRequestDto.kt
│ │ │ │ │ ├── MultiResponseDto.kt
│ │ │ │ │ ├── TRCommitItem.kt
│ │ │ │ │ ├── SingleResponseDto.kt
│ │ │ │ │ ├── CommonResponseDto.kt
│ │ │ │ │ ├── resopnse
│ │ │ │ │ │ ├── CommitResponseDto.kt
│ │ │ │ │ │ ├── UserCount.kt
│ │ │ │ │ │ ├── CommitResponse.kt
│ │ │ │ │ │ ├── CommitsResponseDto.kt
│ │ │ │ │ │ └── NestedContributionsResponseDto.kt
│ │ │ │ │ ├── GfdUser.kt
│ │ │ │ │ ├── Token.kt
│ │ │ │ │ ├── YearContributionDto.kt
│ │ │ │ │ └── User.kt
│ │ │ │ ├── repository
│ │ │ │ │ ├── CommitsRepository.kt
│ │ │ │ │ └── UserRepository.kt
│ │ │ │ ├── service
│ │ │ │ │ ├── GithubContributionService.kt
│ │ │ │ │ ├── GithubApiService.kt
│ │ │ │ │ ├── UserService.kt
│ │ │ │ │ └── CommitService.kt
│ │ │ │ └── RetrofitClient.kt
│ │ │ │ ├── views
│ │ │ │ ├── MainActivity.kt
│ │ │ │ ├── LauncherActivity.kt
│ │ │ │ └── SearchActivity.kt
│ │ │ │ ├── utils
│ │ │ │ ├── ValidationCheck.kt
│ │ │ │ ├── Contribution.kt
│ │ │ │ ├── ProgressBarDialog.kt
│ │ │ │ ├── GithubCrawler.kt
│ │ │ │ └── SharedPreference.kt
│ │ │ │ ├── room
│ │ │ │ ├── converter
│ │ │ │ │ └── DateConverter.kt
│ │ │ │ ├── entity
│ │ │ │ │ └── GithubId.kt
│ │ │ │ ├── dao
│ │ │ │ │ └── SearchGithubIdDao.kt
│ │ │ │ └── AppDatabase.kt
│ │ │ │ ├── database
│ │ │ │ ├── Commits.kt
│ │ │ │ ├── CommitsDatabaseDao.kt
│ │ │ │ └── CommitsDatabase.kt
│ │ │ │ ├── viewmodel
│ │ │ │ ├── GithubIdViewModel.kt
│ │ │ │ ├── GithubContributionViewModel.kt
│ │ │ │ ├── RankFragmentViewModel.kt
│ │ │ │ ├── GithubCommitDataViewModel.kt
│ │ │ │ └── UserViewModel.kt
│ │ │ │ ├── adapter
│ │ │ │ ├── CustomAdapter.kt
│ │ │ │ ├── GithubIdAdapter.kt
│ │ │ │ ├── ContributionsAdapter.kt
│ │ │ │ └── CommitsAdapter.kt
│ │ │ │ └── v1
│ │ │ │ └── views
│ │ │ │ ├── Main2Activity.kt
│ │ │ │ ├── LoginActivity.kt
│ │ │ │ ├── RankFragment.kt
│ │ │ │ ├── MainFragment.kt
│ │ │ │ ├── MainSub.kt
│ │ │ │ ├── OptionFragment.kt
│ │ │ │ └── GuestMain.kt
│ │ └── AndroidManifest.xml
│ ├── test
│ │ └── java
│ │ │ └── com
│ │ │ └── seok
│ │ │ └── gfd
│ │ │ └── ExampleUnitTest.kt
│ └── androidTest
│ │ └── java
│ │ └── com
│ │ └── seok
│ │ └── gfd
│ │ ├── ExampleInstrumentedTest.kt
│ │ └── room
│ │ └── dao
│ │ └── GithubIdDaoTest.kt
├── proguard-rules.pro
└── build.gradle
├── settings.gradle
├── img
├── preview01.png
├── preview02.png
├── preview03.png
├── preview04.png
├── preview05.png
├── preview06.png
├── preview07.png
├── preview1_01.png
├── preview1_02.png
├── preview1_03.png
├── preview1_1_01.png
├── preview1_1_02.png
└── preview1_1_03.png
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── .idea
├── encodings.xml
├── codeStyles
│ ├── codeStyleConfig.xml
│ └── Project.xml
├── compiler.xml
├── vcs.xml
├── render.experimental.xml
├── misc.xml
├── modules.xml
├── runConfigurations.xml
├── gradle.xml
├── jarRepositories.xml
└── navEditor.xml
├── Makefile
├── gradle.properties
├── README.md
├── .gitignore
├── .travis.yml
├── gradlew.bat
└── gradlew
/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app'
2 |
--------------------------------------------------------------------------------
/img/preview01.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/msnodeve/Github-for-Developer/HEAD/img/preview01.png
--------------------------------------------------------------------------------
/img/preview02.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/msnodeve/Github-for-Developer/HEAD/img/preview02.png
--------------------------------------------------------------------------------
/img/preview03.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/msnodeve/Github-for-Developer/HEAD/img/preview03.png
--------------------------------------------------------------------------------
/img/preview04.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/msnodeve/Github-for-Developer/HEAD/img/preview04.png
--------------------------------------------------------------------------------
/img/preview05.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/msnodeve/Github-for-Developer/HEAD/img/preview05.png
--------------------------------------------------------------------------------
/img/preview06.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/msnodeve/Github-for-Developer/HEAD/img/preview06.png
--------------------------------------------------------------------------------
/img/preview07.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/msnodeve/Github-for-Developer/HEAD/img/preview07.png
--------------------------------------------------------------------------------
/img/preview1_01.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/msnodeve/Github-for-Developer/HEAD/img/preview1_01.png
--------------------------------------------------------------------------------
/img/preview1_02.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/msnodeve/Github-for-Developer/HEAD/img/preview1_02.png
--------------------------------------------------------------------------------
/img/preview1_03.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/msnodeve/Github-for-Developer/HEAD/img/preview1_03.png
--------------------------------------------------------------------------------
/img/preview1_1_01.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/msnodeve/Github-for-Developer/HEAD/img/preview1_1_01.png
--------------------------------------------------------------------------------
/img/preview1_1_02.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/msnodeve/Github-for-Developer/HEAD/img/preview1_1_02.png
--------------------------------------------------------------------------------
/img/preview1_1_03.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/msnodeve/Github-for-Developer/HEAD/img/preview1_1_03.png
--------------------------------------------------------------------------------
/app/src/main/gfd_logo-web.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/msnodeve/Github-for-Developer/HEAD/app/src/main/gfd_logo-web.png
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/msnodeve/Github-for-Developer/HEAD/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/app/src/main/res/drawable/gfd_logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/msnodeve/Github-for-Developer/HEAD/app/src/main/res/drawable/gfd_logo.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable/icon_bio.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/msnodeve/Github-for-Developer/HEAD/app/src/main/res/drawable/icon_bio.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/gfd_logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/msnodeve/Github-for-Developer/HEAD/app/src/main/res/mipmap-hdpi/gfd_logo.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/gfd_logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/msnodeve/Github-for-Developer/HEAD/app/src/main/res/mipmap-mdpi/gfd_logo.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable/gfd_app_logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/msnodeve/Github-for-Developer/HEAD/app/src/main/res/drawable/gfd_app_logo.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable/gfd_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/msnodeve/Github-for-Developer/HEAD/app/src/main/res/drawable/gfd_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable/icon_location.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/msnodeve/Github-for-Developer/HEAD/app/src/main/res/drawable/icon_location.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/msnodeve/Github-for-Developer/HEAD/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/msnodeve/Github-for-Developer/HEAD/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/gfd_logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/msnodeve/Github-for-Developer/HEAD/app/src/main/res/mipmap-xhdpi/gfd_logo.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/gfd_logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/msnodeve/Github-for-Developer/HEAD/app/src/main/res/mipmap-xxhdpi/gfd_logo.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/gfd_logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/msnodeve/Github-for-Developer/HEAD/app/src/main/res/mipmap-xxxhdpi/gfd_logo.png
--------------------------------------------------------------------------------
/app/src/main/java/com/seok/gfd/retrofit/domain/request/GUserRequestDto.kt:
--------------------------------------------------------------------------------
1 | package com.seok.gfd.retrofit.domain.request
2 |
3 | class GUserRequestDto {
4 | }
--------------------------------------------------------------------------------
/app/src/main/res/drawable/btn_bottom_help.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/msnodeve/Github-for-Developer/HEAD/app/src/main/res/drawable/btn_bottom_help.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable/btn_bottom_home.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/msnodeve/Github-for-Developer/HEAD/app/src/main/res/drawable/btn_bottom_home.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable/btn_bottom_rank.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/msnodeve/Github-for-Developer/HEAD/app/src/main/res/drawable/btn_bottom_rank.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/msnodeve/Github-for-Developer/HEAD/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/msnodeve/Github-for-Developer/HEAD/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable/gfd_logo_foreground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/msnodeve/Github-for-Developer/HEAD/app/src/main/res/drawable/gfd_logo_foreground.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/gfd_logo_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/msnodeve/Github-for-Developer/HEAD/app/src/main/res/mipmap-hdpi/gfd_logo_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/gfd_logo_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/msnodeve/Github-for-Developer/HEAD/app/src/main/res/mipmap-mdpi/gfd_logo_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/gfd_logo_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/msnodeve/Github-for-Developer/HEAD/app/src/main/res/mipmap-xhdpi/gfd_logo_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/gfd_logo_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/msnodeve/Github-for-Developer/HEAD/app/src/main/res/mipmap-xxhdpi/gfd_logo_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/msnodeve/Github-for-Developer/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/.idea/encodings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/msnodeve/Github-for-Developer/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/msnodeve/Github-for-Developer/HEAD/app/src/main/res/mipmap-mdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/msnodeve/Github-for-Developer/HEAD/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/gfd_logo_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/msnodeve/Github-for-Developer/HEAD/app/src/main/res/mipmap-xxxhdpi/gfd_logo_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/gfd_logo_background.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/msnodeve/Github-for-Developer/HEAD/app/src/main/res/mipmap-hdpi/gfd_logo_background.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/gfd_logo_foreground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/msnodeve/Github-for-Developer/HEAD/app/src/main/res/mipmap-hdpi/gfd_logo_foreground.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/gfd_logo_background.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/msnodeve/Github-for-Developer/HEAD/app/src/main/res/mipmap-mdpi/gfd_logo_background.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/gfd_logo_foreground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/msnodeve/Github-for-Developer/HEAD/app/src/main/res/mipmap-mdpi/gfd_logo_foreground.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/gfd_logo_background.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/msnodeve/Github-for-Developer/HEAD/app/src/main/res/mipmap-xhdpi/gfd_logo_background.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/gfd_logo_foreground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/msnodeve/Github-for-Developer/HEAD/app/src/main/res/mipmap-xhdpi/gfd_logo_foreground.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/gfd_logo_background.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/msnodeve/Github-for-Developer/HEAD/app/src/main/res/mipmap-xxhdpi/gfd_logo_background.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/gfd_logo_foreground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/msnodeve/Github-for-Developer/HEAD/app/src/main/res/mipmap-xxhdpi/gfd_logo_foreground.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/msnodeve/Github-for-Developer/HEAD/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/msnodeve/Github-for-Developer/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/gfd_logo_background.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/msnodeve/Github-for-Developer/HEAD/app/src/main/res/mipmap-xxxhdpi/gfd_logo_background.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/gfd_logo_foreground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/msnodeve/Github-for-Developer/HEAD/app/src/main/res/mipmap-xxxhdpi/gfd_logo_foreground.png
--------------------------------------------------------------------------------
/app/src/main/res/values/ic_launcher_background_gfd.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #FFFFFF
4 |
--------------------------------------------------------------------------------
/.idea/codeStyles/codeStyleConfig.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/java/com/seok/gfd/retrofit/domain/MultiResponseDto.kt:
--------------------------------------------------------------------------------
1 | package com.seok.gfd.retrofit.domain
2 |
3 | class MultiResponseDto: CommonResponseDto() {
4 | var list: List? = null
5 | }
--------------------------------------------------------------------------------
/.idea/compiler.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.idea/render.experimental.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/java/com/seok/gfd/retrofit/domain/TRCommitItem.kt:
--------------------------------------------------------------------------------
1 | package com.seok.gfd.retrofit.domain
2 |
3 | class TRCommitItem(
4 | val userProfile: String,
5 | val userId : String,
6 | val dataCount : Int
7 | )
--------------------------------------------------------------------------------
/app/src/main/res/xml/backup_descriptor.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/java/com/seok/gfd/retrofit/domain/SingleResponseDto.kt:
--------------------------------------------------------------------------------
1 | package com.seok.gfd.retrofit.domain
2 |
3 | import lombok.Getter
4 | import lombok.Setter
5 |
6 | @Getter
7 | @Setter
8 | class SingleResponseDto : CommonResponseDto() {
9 | var data: T? = null
10 | }
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Tue Dec 01 01:23:26 KST 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.5-all.zip
7 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/rounding_trans_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-anydpi-v26/gfd_logo.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/font/font.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
7 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-anydpi-v26/gfd_logo_round.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/rect_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
7 |
8 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/java/com/seok/gfd/retrofit/domain/CommonResponseDto.kt:
--------------------------------------------------------------------------------
1 | package com.seok.gfd.retrofit.domain
2 |
3 | import lombok.Getter
4 | import lombok.Setter
5 |
6 | @Setter
7 | @Getter
8 | open class CommonResponseDto {
9 | var success: Boolean? = false
10 |
11 | var code : Int? = 0
12 |
13 | var msg : String? = ""
14 | }
--------------------------------------------------------------------------------
/app/src/main/res/drawable/github_login_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
8 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/rounding_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/anim/fade_in.xml:
--------------------------------------------------------------------------------
1 |
3 |
4 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/guest_login_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/xml/network_security_config.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 10.0.2.2
5 | 52.78.188.192
6 |
7 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/profile_gradation.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
8 |
--------------------------------------------------------------------------------
/app/src/test/java/com/seok/gfd/ExampleUnitTest.kt:
--------------------------------------------------------------------------------
1 | package com.seok.gfd
2 |
3 | import org.junit.Test
4 |
5 | /**
6 | * Example local unit test, which will execute on the development machine (host).
7 | *
8 | * See [testing documentation](http://d.android.com/tools/testing).
9 | */
10 | class ExampleUnitTest {
11 | @Test
12 | fun envTest(){
13 |
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/app/src/main/java/com/seok/gfd/retrofit/domain/resopnse/CommitResponseDto.kt:
--------------------------------------------------------------------------------
1 | package com.seok.gfd.retrofit.domain.resopnse
2 |
3 | import lombok.AllArgsConstructor
4 | import lombok.Getter
5 | import lombok.NoArgsConstructor
6 | import lombok.Setter
7 |
8 | @Getter
9 | @Setter
10 | @NoArgsConstructor
11 | @AllArgsConstructor
12 | class CommitResponseDto {
13 | var data: Object? = null
14 | }
--------------------------------------------------------------------------------
/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/app/src/main/java/com/seok/gfd/views/MainActivity.kt:
--------------------------------------------------------------------------------
1 | package com.seok.gfd.views
2 |
3 | import androidx.appcompat.app.AppCompatActivity
4 | import android.os.Bundle
5 | import com.seok.gfd.R
6 |
7 | class MainActivity : AppCompatActivity() {
8 | override fun onCreate(savedInstanceState: Bundle?) {
9 | super.onCreate(savedInstanceState)
10 | setContentView(R.layout.activity_main)
11 | }
12 | }
--------------------------------------------------------------------------------
/app/src/main/res/anim/fade_out.xml:
--------------------------------------------------------------------------------
1 |
5 |
6 |
11 |
12 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_close_gray.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/java/com/seok/gfd/utils/ValidationCheck.kt:
--------------------------------------------------------------------------------
1 | package com.seok.gfd.utils
2 |
3 | class ValidationCheck {
4 | companion object {
5 | fun validIsEmptyString(str: String): Boolean {
6 | return str.isEmpty() || str == ""
7 | }
8 |
9 | fun isExistSite(str: String): Boolean{
10 | val url = "https://github.com/$str"
11 |
12 |
13 |
14 | return true
15 | }
16 | }
17 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/seok/gfd/room/converter/DateConverter.kt:
--------------------------------------------------------------------------------
1 | package com.seok.gfd.room.converter
2 |
3 | import androidx.room.TypeConverter
4 | import java.util.*
5 |
6 | class DateConverter {
7 | @TypeConverter
8 | fun fromTimestamp(value: Long?): Date? {
9 | return value?.let { Date(it) }
10 | }
11 |
12 | @TypeConverter
13 | fun dateToTimestamp(date: Date?): Long? {
14 | return date?.time?.toLong()
15 | }
16 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/seok/gfd/retrofit/domain/resopnse/UserCount.kt:
--------------------------------------------------------------------------------
1 | package com.seok.gfd.retrofit.domain.resopnse
2 |
3 | import lombok.AllArgsConstructor
4 | import lombok.Getter
5 | import lombok.NoArgsConstructor
6 | import lombok.Setter
7 |
8 | @Setter
9 | @Getter
10 | @NoArgsConstructor
11 | @AllArgsConstructor
12 | class UserCount {
13 | var success: String = ""
14 | var code: Int = 0
15 | var msg: String = ""
16 | var data :Long = 0
17 | }
--------------------------------------------------------------------------------
/app/src/main/res/drawable/github_login_users_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
8 |
9 |
10 |
11 |
14 |
--------------------------------------------------------------------------------
/app/src/main/java/com/seok/gfd/database/Commits.kt:
--------------------------------------------------------------------------------
1 | package com.seok.gfd.database
2 |
3 | import androidx.room.ColumnInfo
4 | import androidx.room.Entity
5 | import androidx.room.PrimaryKey
6 |
7 | @Entity(tableName = "commits")
8 | data class Commits(
9 | @PrimaryKey
10 | @ColumnInfo(name = "data_date")
11 | val dataDate: String,
12 |
13 | @ColumnInfo(name = "data_count")
14 | val dataCount: Int,
15 |
16 | @ColumnInfo(name = "fill")
17 | val fill: String
18 | )
--------------------------------------------------------------------------------
/app/src/main/res/anim/bottom_to_top.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
8 |
13 |
--------------------------------------------------------------------------------
/app/src/main/res/anim/left_to_right.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
8 |
13 |
--------------------------------------------------------------------------------
/app/src/main/res/anim/right_to_left.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
8 |
13 |
--------------------------------------------------------------------------------
/app/src/main/res/anim/top_to_bottom.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
8 |
13 |
--------------------------------------------------------------------------------
/.idea/modules.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/app/src/main/java/com/seok/gfd/retrofit/domain/resopnse/CommitResponse.kt:
--------------------------------------------------------------------------------
1 | package com.seok.gfd.retrofit.domain.resopnse
2 |
3 | import lombok.AllArgsConstructor
4 | import lombok.Getter
5 | import lombok.NoArgsConstructor
6 | import lombok.Setter
7 |
8 | @Getter
9 | @Setter
10 | @NoArgsConstructor
11 | @AllArgsConstructor
12 | class CommitResponse {
13 | var user_url: String = ""
14 |
15 | var data_date: String = ""
16 |
17 | var data_count: Int = 0
18 |
19 | var user_image: String = ""
20 |
21 | var user_id: String = ""
22 |
23 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/seok/gfd/retrofit/domain/request/CommitRequestDto.kt:
--------------------------------------------------------------------------------
1 | package com.seok.gfd.retrofit.domain.request
2 |
3 | import lombok.AllArgsConstructor
4 | import lombok.Getter
5 | import lombok.NoArgsConstructor
6 | import lombok.Setter
7 |
8 | @Getter
9 | @Setter
10 | @NoArgsConstructor
11 | @AllArgsConstructor
12 | class CommitRequestDto {
13 | var userId: String = ""
14 | var dataCount: Int = 0
15 |
16 | constructor(userId: String, dataCount: Int) {
17 | this.userId = userId
18 | this.dataCount = dataCount
19 | }
20 | }
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | set_env:
2 | echo "GITHUB_CLIENT_ID=\"client_id\"" >> app/gradle.properties
3 | echo "GITHUB_CLIENT_SECRET=\"client_secret\"" >> app/gradle.properties
4 | echo "REDIRECT_CALLBACK_URL=\"gfd://github.for.developer\"" >> app/gradle.properties
5 | echo "PREFERENCES_FILE=\"file_name\"" >> app/gradle.properties
6 | echo "PREFERENCES_TOKEN_KEY=\"token_key\"" >> app/gradle.properties
7 | echo "BASIC_AUTH_KEY=\"basic_auth_key\"" >> app/gradle.properties
8 | echo "GFD_API_URL=\"gfd_api_url\"" >> app/gradle.properties
9 | echo "GITHUB_OAUTH_URL=\"gfd_oauth_url\"" >> app/gradle.properties
--------------------------------------------------------------------------------
/app/src/main/java/com/seok/gfd/room/entity/GithubId.kt:
--------------------------------------------------------------------------------
1 | package com.seok.gfd.room.entity
2 |
3 | import androidx.room.ColumnInfo
4 | import androidx.room.Entity
5 | import androidx.room.PrimaryKey
6 | import java.util.*
7 |
8 | private const val TABLE_NAME = "search_github_id"
9 |
10 | @Entity(tableName = TABLE_NAME)
11 | data class GithubId(
12 | @ColumnInfo(name = "github_id") val githubId: String?
13 | ) {
14 | @PrimaryKey(autoGenerate = true)
15 | var id: Int = 0
16 | @ColumnInfo(name = "created", defaultValue = "CURRENT_TIMESTAMP")
17 | var created: Date? = Date()
18 | }
--------------------------------------------------------------------------------
/.idea/runConfigurations.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
11 |
12 |
--------------------------------------------------------------------------------
/app/src/main/java/com/seok/gfd/retrofit/domain/GfdUser.kt:
--------------------------------------------------------------------------------
1 | package com.seok.gfd.retrofit.domain
2 |
3 | import lombok.AllArgsConstructor
4 | import lombok.Getter
5 | import lombok.NoArgsConstructor
6 | import lombok.Setter
7 |
8 | @Getter
9 | @Setter
10 | @NoArgsConstructor
11 | @AllArgsConstructor
12 | class GfdUser {
13 | var userId: String = ""
14 |
15 | var userUrl: String = ""
16 |
17 | var userImage: String = ""
18 |
19 | constructor(userId : String, userUrl : String, userImage: String){
20 | this.userId = userId
21 | this.userUrl = userUrl
22 | this.userImage = userImage
23 | }
24 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/seok/gfd/retrofit/repository/CommitsRepository.kt:
--------------------------------------------------------------------------------
1 | package com.seok.gfd.retrofit.repository
2 |
3 | import android.app.Application
4 | import com.seok.gfd.database.Commits
5 | import com.seok.gfd.database.CommitsDatabase
6 | import com.seok.gfd.database.CommitsDatabaseDao
7 |
8 | class CommitsRepository(application: Application) {
9 | private val commitsDatabaseDao : CommitsDatabaseDao
10 |
11 | init{
12 | val commitsDatabase = CommitsDatabase.getInstance(application)
13 | commitsDatabaseDao = commitsDatabase.commitsDatabaseDao()
14 | }
15 |
16 | fun insert(commits: Commits){
17 |
18 | }
19 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/seok/gfd/utils/Contribution.kt:
--------------------------------------------------------------------------------
1 | package com.seok.gfd.utils
2 |
3 | import lombok.AllArgsConstructor
4 | import lombok.Getter
5 | import lombok.NoArgsConstructor
6 | import lombok.Setter
7 |
8 | @Getter
9 | @Setter
10 | @NoArgsConstructor
11 | @AllArgsConstructor
12 | class Contribution {
13 | var year : String? = null
14 | var total : Int? = 0
15 | var list : ArrayList? = ArrayList()
16 |
17 | class ContributionInfo(date : String, count: Int, color : String, intensity: Int){
18 | var date : String = date
19 | var count : Int = count
20 | var color : String = color
21 | var intensity : Int = intensity
22 | }
23 | }
--------------------------------------------------------------------------------
/app/src/androidTest/java/com/seok/gfd/ExampleInstrumentedTest.kt:
--------------------------------------------------------------------------------
1 | package com.seok.gfd
2 |
3 | import androidx.test.ext.junit.runners.AndroidJUnit4
4 |
5 | import org.junit.Test
6 | import org.junit.runner.RunWith
7 |
8 | /**
9 | * Instrumented test, which will execute on an Android device.
10 | *
11 | * See [testing documentation](http://d.android.com/tools/testing).
12 | */
13 | @RunWith(AndroidJUnit4::class)
14 | class ExampleInstrumentedTest {
15 | @Test
16 | fun useAppContext() {
17 | // Context of the app under test.
18 | // val appContext = InstrumentationRegistry.getTargetContext()
19 | // assertEquals("com.seok.gitfordeveloper", appContext.packageName)
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/app/src/main/java/com/seok/gfd/retrofit/domain/Token.kt:
--------------------------------------------------------------------------------
1 | package com.seok.gfd.retrofit.domain
2 |
3 | import lombok.AllArgsConstructor
4 | import lombok.Getter
5 | import lombok.Setter
6 |
7 | @Getter
8 | @Setter
9 | @AllArgsConstructor
10 | class Token {
11 | var access_token : String = ""
12 | var scope : String = ""
13 | var token_type : String = ""
14 | var code : Int = 0
15 | constructor(accessToken : String, scope: String, tokenType : String, code : Int){
16 | this.access_token = accessToken
17 | this.scope = scope
18 | this.token_type = tokenType
19 | this.code = code
20 | }
21 | constructor(code : Int){
22 | this.code = code
23 | }
24 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/seok/gfd/retrofit/service/GithubContributionService.kt:
--------------------------------------------------------------------------------
1 | package com.seok.gfd.retrofit.service
2 |
3 | import com.seok.gfd.retrofit.domain.resopnse.CommitsResponseDto
4 | import com.seok.gfd.retrofit.domain.resopnse.NestedContributionsResponseDto
5 | import retrofit2.Call
6 | import retrofit2.http.GET
7 | import retrofit2.http.Path
8 | import retrofit2.http.Query
9 |
10 | interface GithubContributionService {
11 | @GET("{userId}")
12 | fun getContributions(@Path("userId") userId: String): Call
13 |
14 | @GET("{userId}")
15 | fun getNestedContributions(
16 | @Path("userId") userId: String,
17 | @Query("format") format: String
18 | ): Call
19 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/seok/gfd/utils/ProgressBarDialog.kt:
--------------------------------------------------------------------------------
1 | package com.seok.gfd.utils
2 |
3 | import android.app.Dialog
4 | import android.content.Context
5 | import android.graphics.Color
6 | import android.graphics.drawable.ColorDrawable
7 | import com.seok.gfd.R
8 |
9 | class ProgressbarDialog(context: Context){
10 | private val dialog = Dialog(context)
11 |
12 | init{
13 | dialog.setContentView(R.layout.progressbar_dialog)
14 | dialog.window.setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT))
15 | dialog.setCanceledOnTouchOutside(false)
16 | }
17 | fun show(){
18 | dialog.show()
19 | }
20 | fun hide(){
21 | dialog.hide()
22 | dialog.dismiss()
23 | }
24 |
25 | }
26 |
--------------------------------------------------------------------------------
/app/src/main/java/com/seok/gfd/database/CommitsDatabaseDao.kt:
--------------------------------------------------------------------------------
1 | package com.seok.gfd.database
2 |
3 | import androidx.room.*
4 |
5 | @Dao
6 | interface CommitsDatabaseDao{
7 | @Insert(onConflict = OnConflictStrategy.REPLACE)
8 | fun insert(commits : Commits)
9 |
10 | @Update
11 | fun update(commits: Commits)
12 |
13 | @Query("SELECT * FROM commits ORDER BY data_count DESC LIMIT 1")
14 | fun maxCommit() : Commits
15 |
16 | @Query("SELECT * FROM commits")
17 | fun getAllCommits() : List
18 |
19 | @Query("SELECT * FROM commits WHERE data_date = :dataDate")
20 | fun getCommits(dataDate: String) : Commits
21 |
22 | @Query("SELECT * FROM commits ORDER BY data_date DESC")
23 | fun getYearCommits() : List
24 |
25 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/seok/gfd/views/LauncherActivity.kt:
--------------------------------------------------------------------------------
1 | package com.seok.gfd.views
2 |
3 | import android.content.Intent
4 | import android.os.Bundle
5 | import android.os.Handler
6 | import androidx.appcompat.app.AppCompatActivity
7 | import com.seok.gfd.R
8 |
9 | class LauncherActivity : AppCompatActivity() {
10 |
11 | override fun onCreate(savedInstanceState: Bundle?) {
12 | super.onCreate(savedInstanceState)
13 | setContentView(R.layout.activity_launcher)
14 |
15 | startLoading()
16 | }
17 |
18 | private fun startLoading(){
19 | Handler().postDelayed({
20 | val intent = Intent(this, SearchActivity::class.java)
21 | startActivity(intent)
22 | finish()
23 | }, 50)
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/app/src/main/java/com/seok/gfd/retrofit/domain/YearContributionDto.kt:
--------------------------------------------------------------------------------
1 | package com.seok.gfd.retrofit.domain
2 |
3 | import com.seok.gfd.retrofit.domain.resopnse.CommitsResponseDto
4 | import lombok.AllArgsConstructor
5 | import lombok.Getter
6 | import lombok.NoArgsConstructor
7 | import lombok.Setter
8 |
9 | @Getter
10 | @Setter
11 | @NoArgsConstructor
12 | @AllArgsConstructor
13 | class YearContributionDto {
14 | var year: String? = null
15 |
16 | var contributions: ArrayList? = null
17 |
18 | constructor() {
19 | this.contributions = ArrayList()
20 | }
21 |
22 | class Contribution{
23 | var date : String = ""
24 | var count : Int = 0
25 | var color : String = ""
26 | var intensity : Int = 0
27 | }
28 |
29 |
30 | }
--------------------------------------------------------------------------------
/app/src/main/res/menu/bottom_navigation_menu.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/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
22 |
--------------------------------------------------------------------------------
/app/src/main/java/com/seok/gfd/retrofit/service/GithubApiService.kt:
--------------------------------------------------------------------------------
1 | package com.seok.gfd.retrofit.service
2 |
3 | import com.seok.gfd.retrofit.domain.SingleResponseDto
4 | import com.seok.gfd.retrofit.domain.Token
5 | import com.seok.gfd.retrofit.domain.User
6 | import retrofit2.Call
7 | import retrofit2.http.*
8 |
9 | interface GithubApiService {
10 | @GET("user")
11 | fun getUserInfoFromGithubApi(@Header("Authorization") token: String): Call
12 |
13 | @Headers("Accept: application/json")
14 | @POST("access_token")
15 | fun getAccessTokenFromGithubApi(
16 | @Query("client_id") clientId : String,
17 | @Query("client_secret") clientSecret : String,
18 | @Query("code") code : String) : Call
19 |
20 | @GET("users/{userId}")
21 | fun getUserInfo(
22 | @Path("userId") userId: String
23 | ):Call
24 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/seok/gfd/retrofit/service/UserService.kt:
--------------------------------------------------------------------------------
1 | package com.seok.gfd.retrofit.service
2 |
3 | import com.seok.gfd.retrofit.domain.GfdUser
4 | import com.seok.gfd.retrofit.domain.SingleResponseDto
5 | import retrofit2.Call
6 | import retrofit2.http.Body
7 | import retrofit2.http.GET
8 | import retrofit2.http.Header
9 | import retrofit2.http.POST
10 |
11 | interface UserService {
12 | @GET("users")
13 | fun getUserList(
14 | @Header("Authorization") authKey: String
15 | ): Call>
16 |
17 | @POST("users")
18 | fun signUpUser(
19 | @Header("Authorization") authKey: String,
20 | @Body gfdUser: GfdUser
21 | ): Call>
22 |
23 | @GET("users/count")
24 | fun getUsersCount(
25 | @Header("Authorization") authKey: String
26 | ): Call>
27 |
28 | }
--------------------------------------------------------------------------------
/app/src/main/res/layout/progressbar_dialog.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
18 |
19 |
--------------------------------------------------------------------------------
/.idea/gradle.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/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=-Xmx1536m
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 | # Kotlin code style for this project: "official" or "obsolete":
15 | kotlin.code.style=official
16 | android.useAndroidX=true
17 | android.enableJetifier=true
--------------------------------------------------------------------------------
/app/src/main/java/com/seok/gfd/room/dao/SearchGithubIdDao.kt:
--------------------------------------------------------------------------------
1 | package com.seok.gfd.room.dao
2 |
3 | import androidx.room.*
4 | import com.seok.gfd.room.entity.GithubId
5 |
6 | private const val TABLE_NAME = "search_github_id"
7 |
8 | @Dao
9 | interface GithubIdDao {
10 | @Insert(onConflict = OnConflictStrategy.REPLACE)
11 | suspend fun insert(githubId: GithubId): Long
12 |
13 | @Delete
14 | suspend fun delete(githubId: GithubId)
15 |
16 | @Query("SELECT * FROM $TABLE_NAME WHERE github_id LIKE '%' || :githubId || '%' ORDER BY created DESC")
17 | suspend fun selectAll(githubId: String): List
18 |
19 | @Query("SELECT * FROM $TABLE_NAME ORDER BY created DESC")
20 | suspend fun selectAll(): List
21 |
22 | @Query("SELECT COUNT(*) FROM $TABLE_NAME WHERE github_id = :githubId")
23 | suspend fun selectById(githubId: String): Int
24 |
25 | @Query("delete from $TABLE_NAME")
26 | suspend fun deleteAll()
27 | }
--------------------------------------------------------------------------------
/app/src/main/res/layout/rowlayout.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
14 |
23 |
--------------------------------------------------------------------------------
/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
9 |
10 |
15 |
16 |
21 |
22 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
10 |
19 |
20 |
--------------------------------------------------------------------------------
/app/src/main/java/com/seok/gfd/retrofit/domain/resopnse/CommitsResponseDto.kt:
--------------------------------------------------------------------------------
1 | package com.seok.gfd.retrofit.domain.resopnse
2 |
3 | import lombok.AllArgsConstructor
4 | import lombok.Getter
5 | import lombok.NoArgsConstructor
6 | import lombok.Setter
7 |
8 | @Getter
9 | @Setter
10 | @NoArgsConstructor
11 | @AllArgsConstructor
12 | class CommitsResponseDto {
13 |
14 | var year : String? = null
15 |
16 | var years: List? = null
17 |
18 | var contributions: List? = null
19 |
20 | constructor(year: String?) {
21 | this.year = year
22 | this.contributions = ArrayList()
23 | }
24 |
25 |
26 | class Year{
27 | var year: String = ""
28 | var total: Int = 0
29 | var range : Range? = null
30 |
31 | class Range{
32 | var start: String = ""
33 | var end: String = ""
34 | }
35 | }
36 |
37 | class Contribution{
38 | var date : String = ""
39 | var count : Int = 0
40 | var color : String = ""
41 | var intensity : Int = 0
42 | }
43 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/seok/gfd/utils/GithubCrawler.kt:
--------------------------------------------------------------------------------
1 | package com.seok.gfd.utils
2 |
3 | import com.seok.gfd.database.Commits
4 | import org.jsoup.Jsoup
5 | import org.jsoup.nodes.Element
6 |
7 | class GithubCrawler {
8 |
9 | fun getCommitCrawler(url:String) : ArrayList {
10 | val doc = Jsoup.connect(url).get()
11 | val regex = """fill=\"#[a-zA-Z0-9]{6}\" data-count=\"\d+\" data-date=\"([12]\d{3}-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01]))\"""".toRegex()
12 | val element = doc.select("g rect")
13 | val commits = ArrayList()
14 | for(e : Element in element) {
15 | val matchResult = regex.find(e.toString())?.value?.replace("\"", "")
16 | val split: List = matchResult?.split(" ")!!
17 | commits.add(
18 | Commits(
19 | split[2].split("=")[1],
20 | split[1].split("=")[1].toInt(),
21 | split[0].split("=")[1]
22 | )
23 | )
24 | }
25 | return commits
26 | }
27 |
28 |
29 | }
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_launcher.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
21 |
22 |
--------------------------------------------------------------------------------
/.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 |
--------------------------------------------------------------------------------
/app/src/main/java/com/seok/gfd/database/CommitsDatabase.kt:
--------------------------------------------------------------------------------
1 | package com.seok.gfd.database
2 |
3 | import android.content.Context
4 | import androidx.room.Database
5 | import androidx.room.Room
6 | import androidx.room.RoomDatabase
7 |
8 | @Database(entities = [Commits::class], version = 1, exportSchema = false)
9 | abstract class CommitsDatabase : RoomDatabase(){
10 | abstract fun commitsDatabaseDao(): CommitsDatabaseDao
11 | companion object{
12 | @Volatile
13 | private var INSTANCE: CommitsDatabase? = null
14 |
15 | fun getInstance(context: Context): CommitsDatabase{
16 | synchronized(this){
17 | var instance = INSTANCE
18 | if (instance == null){
19 | instance = Room.databaseBuilder(
20 | context.applicationContext,
21 | CommitsDatabase::class.java,
22 | "commits_db")
23 | .fallbackToDestructiveMigration()
24 | .build()
25 | INSTANCE = instance
26 | }
27 | return instance
28 | }
29 | }
30 | }
31 | }
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Github For Developer
2 | 
3 |
4 |
5 |
6 | ***
7 |
8 |
9 | # Release
10 |
11 | v3.4.3 릴리즈 [Google Paly Store](https://play.google.com/store/apps/details?id=com.seok.gfd)
12 |
13 |
14 |
15 | # Preview
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 | # Notice
24 |
25 | v1.6.2 릴리즈 작업 완료
26 |
27 | - Backend 작업
28 | - UX/UI 개선 작업
29 | - 자동 로그인 구현
30 |
31 | v2.4.1 릴리즈 작업 완료
32 |
33 | - 금일 커밋 랭킹 서비스 구축
34 | - 프로필, 랭킹 프래그먼트로 분리
35 |
36 | v2.10.4 릴리즈 작업 완료
37 |
38 | - 대대적인 UI/UX 개선
39 | - 도움을 주신 분 탭 추가
40 | - 앱 성능 최적화
41 |
42 | v2.11.1
43 |
44 | - Github Contribution 그래프 UI 변경
45 | - 랭킹 서비스 오류로 인해 수정 진행 중
46 |
47 | v3.4.3
48 |
49 | - Guest 사용자 서비스
50 | - UI 추가
--------------------------------------------------------------------------------
/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #008577
4 | #239a3b
5 | #D81B60
6 |
7 |
8 | #ebedf0
9 | #c6e48b
10 | #7bc96f
11 | #239a3b
12 | #196127
13 |
14 |
15 | #282828
16 | #444444
17 | #e5e1e1
18 | #e4f9f5
19 |
20 |
21 | #00b1e5
22 | #08e68d
23 |
24 |
25 | #0F0F0F
26 | #898989
27 | #646464
28 | #C8C8C8
29 | #3842FF
30 | #38EEFF
31 |
32 |
--------------------------------------------------------------------------------
/app/src/main/java/com/seok/gfd/retrofit/service/CommitService.kt:
--------------------------------------------------------------------------------
1 | package com.seok.gfd.retrofit.service
2 |
3 | import com.seok.gfd.retrofit.domain.MultiResponseDto
4 | import com.seok.gfd.retrofit.domain.SingleResponseDto
5 | import com.seok.gfd.retrofit.domain.resopnse.CommitResponse
6 | import com.seok.gfd.retrofit.domain.request.CommitRequestDto
7 | import com.seok.gfd.retrofit.domain.resopnse.CommitResponseDto
8 | import retrofit2.Call
9 | import retrofit2.http.*
10 |
11 | interface CommitService {
12 | @GET("commits")
13 | fun getCommitsRank(
14 | @Header("Authorization") authKey: String,
15 | @Query("page") page: Int,
16 | @Query("size") size: Int
17 | ): Call
18 |
19 | @GET("commits/{dataDate}")
20 | fun getCommitList(
21 | @Header("Authorization") authKey: String,
22 | @Path("dataDate") dataDate: String
23 | ): Call>
24 | @POST("commits")
25 | fun enrollCommit(
26 | @Header("Authorization") authKey: String,
27 | @Body commitRequestDto: CommitRequestDto
28 | ): Call>
29 |
30 | @GET("trc/{date}")
31 | fun getTRCommitList(
32 | @Header("Authorization") authKey: String,
33 | @Path("date") date: String
34 | ): Call>
35 |
36 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/seok/gfd/room/AppDatabase.kt:
--------------------------------------------------------------------------------
1 | package com.seok.gfd.room
2 |
3 | import android.content.Context
4 | import androidx.room.Database
5 | import androidx.room.Room
6 | import androidx.room.RoomDatabase
7 | import androidx.room.TypeConverters
8 | import com.seok.gfd.room.converter.DateConverter
9 | import com.seok.gfd.room.dao.GithubIdDao
10 | import com.seok.gfd.room.entity.GithubId
11 |
12 | @Database(entities = [GithubId::class], version = 2)
13 | @TypeConverters(DateConverter::class)
14 | abstract class AppDatabase : RoomDatabase() {
15 | abstract fun githubIdDao(): GithubIdDao
16 |
17 | companion object {
18 | private var INSTANCE: AppDatabase? = null
19 |
20 | fun getInstance(context: Context): AppDatabase {
21 | if (INSTANCE == null) {
22 | synchronized(AppDatabase::class) {
23 | INSTANCE = Room.databaseBuilder(
24 | context.applicationContext,
25 | AppDatabase::class.java, "cata.db"
26 | )
27 | .fallbackToDestructiveMigration()
28 | .build()
29 | }
30 | }
31 | return INSTANCE!!
32 | }
33 |
34 | fun destroyInstance(){
35 | INSTANCE = null
36 | }
37 | }
38 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/seok/gfd/retrofit/domain/resopnse/NestedContributionsResponseDto.kt:
--------------------------------------------------------------------------------
1 | package com.seok.gfd.retrofit.domain.resopnse
2 |
3 | import lombok.AllArgsConstructor
4 | import lombok.Getter
5 | import lombok.NoArgsConstructor
6 | import lombok.Setter
7 |
8 | @Getter
9 | @Setter
10 | @NoArgsConstructor
11 | @AllArgsConstructor
12 | class NestedContributionsResponseDto {
13 |
14 | var years: HashMap? = null
15 |
16 | var contributions : Contributions? = null
17 |
18 | inner class Years{
19 | var year: String? = null
20 | var total : Int? = 0
21 | var range: Range? = null
22 |
23 | inner class Range{
24 | var start: String = ""
25 | var end: String = ""
26 | }
27 | }
28 |
29 | inner class Contributions{
30 | var year: String? = null
31 | var total : Int? = 0
32 | var range: Range? = null
33 | var contributions : HashMap>>? = null
34 |
35 | inner class Range{
36 | var start: String = ""
37 | var end: String = ""
38 | }
39 |
40 | inner class Contribution{
41 | var date : String = ""
42 | var count : Int = 0
43 | var color : String = ""
44 | var intensity : Int = 0
45 | }
46 | }
47 | }
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Built application files
2 | *.apk
3 | *.ap_
4 | *.aab
5 |
6 | # Files for the ART/Dalvik VM
7 | *.dex
8 |
9 | # Java class files
10 | *.class
11 |
12 | # Generated files
13 | bin/
14 | gen/
15 | out/
16 |
17 | # Gradle files
18 | .gradle/
19 | build/
20 |
21 | # Local configuration file (sdk path, etc)
22 | local.properties
23 | app/gradle.properties
24 |
25 | # Proguard folder generated by Eclipse
26 | proguard/
27 |
28 | # Log Files
29 | *.log
30 |
31 | # Android Studio Navigation editor temp files
32 | .navigation/
33 |
34 | # Android Studio captures folder
35 | captures/
36 |
37 | # IntelliJ
38 | *.iml
39 | .idea/workspace.xml
40 | .idea/tasks.xml
41 | .idea/gradle.xml
42 | .idea/assetWizardSettings.xml
43 | .idea/dictionaries
44 | .idea/libraries
45 | .idea/caches
46 |
47 | # Keystore files
48 | # Uncomment the following line if you do not want to check your keystore files in.
49 | #*.jks
50 |
51 | # External native build folder generated in Android Studio 2.2 and later
52 | .externalNativeBuild
53 |
54 | # Google Services (e.g. APIs or Firebase)
55 | google-services.json
56 |
57 | # Freeline
58 | freeline.py
59 | freeline/
60 | freeline_project_description.json
61 |
62 | # fastlane
63 | fastlane/report.xml
64 | fastlane/Preview.html
65 | fastlane/screenshots
66 | fastlane/test_output
67 | fastlane/readme.md
68 |
69 | # mac
70 | .DS_Store
71 | .idea/runConfigurations.xml
72 |
73 | .settings
74 | release
75 | .project
76 | .classpath
77 |
78 | network_security_config.xml
--------------------------------------------------------------------------------
/app/src/main/java/com/seok/gfd/utils/SharedPreference.kt:
--------------------------------------------------------------------------------
1 | package com.seok.gfd.utils
2 |
3 | import android.app.Application
4 | import androidx.appcompat.app.AppCompatActivity
5 | import com.google.gson.GsonBuilder
6 | import com.seok.gfd.BuildConfig
7 | import com.seok.gfd.R
8 | import com.seok.gfd.retrofit.domain.User
9 |
10 | class SharedPreference(private val application: Application) {
11 | private val pref = application.getSharedPreferences(
12 | "",
13 | AppCompatActivity.MODE_PRIVATE
14 | )
15 |
16 | private val editor = pref.edit()
17 |
18 | fun setValue(key: String, value: String) {
19 | editor.putString(key, value)
20 | editor.commit()
21 | }
22 |
23 | fun getValue(key: String): String {
24 | return if(pref.contains(key)){
25 | @Suppress("NULLABILITY_MISMATCH_BASED_ON_JAVA_ANNOTATIONS")
26 | pref.getString(key, application.getString(R.string.no_token))
27 | }else{
28 | application.getString(R.string.no_token)
29 | }
30 | }
31 |
32 | fun setValueObject(key: String, user:User){
33 | val gson = GsonBuilder().create()
34 | // 객체 -> json 저장
35 | val userInfoJson = gson.toJson(user, User::class.java)
36 | editor.putString(key, userInfoJson)
37 | editor.commit()
38 | }
39 |
40 | fun getValueObject(key:String):User{
41 | val gson = GsonBuilder().create()
42 | val user = pref.getString(key, null)
43 | // json -> 객체 변환
44 | return gson.fromJson(user, User::class.java)
45 | }
46 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/seok/gfd/viewmodel/GithubIdViewModel.kt:
--------------------------------------------------------------------------------
1 | package com.seok.gfd.viewmodel
2 |
3 | import android.app.Application
4 | import androidx.lifecycle.AndroidViewModel
5 | import androidx.lifecycle.LiveData
6 | import androidx.lifecycle.MutableLiveData
7 | import com.seok.gfd.room.AppDatabase
8 | import com.seok.gfd.room.entity.GithubId
9 | import kotlinx.coroutines.runBlocking
10 |
11 | class GithubIdViewModel(val context: Application) : AndroidViewModel(context) {
12 | private val TAG = this.javaClass.toString()
13 | private val database = AppDatabase.getInstance(context)
14 |
15 | private val _githubIds = MutableLiveData>()
16 |
17 | val githubIds: LiveData>
18 | get() = _githubIds
19 |
20 | fun insertGithubId(githubId: GithubId) {
21 | runBlocking {
22 | if (database.githubIdDao().selectById(githubId.githubId.toString()) == 0) {
23 | database.githubIdDao().insert(githubId)
24 | }
25 | }
26 | }
27 |
28 | fun getGithubId(name: String) {
29 | runBlocking {
30 | if (name == "" || name.isEmpty()) {
31 | _githubIds.value = database.githubIdDao().selectAll()
32 | } else {
33 | _githubIds.value = database.githubIdDao().selectAll(name)
34 | }
35 | }
36 | }
37 |
38 | fun deleteGithubId(githubId: GithubId) {
39 | runBlocking {
40 | database.githubIdDao().delete(githubId)
41 | }
42 | }
43 |
44 | fun closeDatabase() {
45 | database.close()
46 | }
47 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/seok/gfd/adapter/CustomAdapter.kt:
--------------------------------------------------------------------------------
1 | package com.seok.gfd.adapter
2 |
3 | import android.content.Context
4 | import android.view.LayoutInflater
5 | import android.view.View
6 | import android.view.ViewGroup
7 | import android.widget.ImageView
8 | import android.widget.TextView
9 | import android.widget.Toast
10 | import androidx.recyclerview.widget.RecyclerView
11 | import com.seok.gfd.R
12 |
13 | class CustomAdapter(personNames : ArrayList, personImages : ArrayList, context: Context) : RecyclerView.Adapter() {
14 | var personNames =personNames
15 | var personImages : ArrayList = personImages
16 | var context: Context = context
17 |
18 | override fun getItemCount() = personNames.size
19 |
20 | override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder {
21 | val v = LayoutInflater.from(parent.context).inflate(R.layout.rowlayout, parent, false)
22 | return MyViewHolder(v)
23 | }
24 |
25 |
26 | override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
27 | holder.name.text = personNames[position]
28 | holder.image.setImageResource(personImages[position])
29 | holder.itemView.setOnClickListener {
30 | Toast.makeText(context, personNames[position], Toast.LENGTH_SHORT).show()
31 | }
32 | }
33 |
34 | inner class MyViewHolder(itemView: View): RecyclerView.ViewHolder(itemView){
35 | var name : TextView = itemView.findViewById(R.id.name)
36 | var image : ImageView = itemView.findViewById(R.id.image)
37 | }
38 |
39 | }
40 |
41 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/contributions_layout.xml:
--------------------------------------------------------------------------------
1 |
2 |
10 |
11 |
18 |
19 |
20 |
24 |
25 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
--------------------------------------------------------------------------------
/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
26 |
27 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
--------------------------------------------------------------------------------
/app/src/androidTest/java/com/seok/gfd/room/dao/GithubIdDaoTest.kt:
--------------------------------------------------------------------------------
1 | package com.seok.gfd.room.dao
2 |
3 | import androidx.room.Room
4 | import androidx.test.ext.junit.runners.AndroidJUnit4
5 | import androidx.test.platform.app.InstrumentationRegistry
6 | import com.seok.gfd.room.AppDatabase
7 | import com.seok.gfd.room.entity.GithubId
8 | import kotlinx.coroutines.runBlocking
9 | import org.junit.After
10 | import org.junit.Before
11 |
12 | import org.junit.Assert.*
13 | import org.junit.Test
14 | import org.junit.runner.RunWith
15 |
16 | @RunWith(AndroidJUnit4::class)
17 | class GithubIdDaoTest {
18 | private lateinit var db : AppDatabase
19 |
20 | @Before
21 | fun createDb() {
22 | val context = InstrumentationRegistry.getInstrumentation().targetContext
23 | assertEquals("com.seok.gfd", context.packageName)
24 |
25 | db = Room.inMemoryDatabaseBuilder(
26 | context,
27 | AppDatabase::class.java
28 | ).build()
29 | }
30 |
31 | @After
32 | fun closeDb() = runBlocking {
33 | db.githubIdDao().deleteAll()
34 | db.close()
35 | }
36 |
37 | @Test
38 | fun iWantToKnowTheDatabaseIsFind() = runBlocking{
39 | val searchGithubId = GithubId(githubId = "github")
40 |
41 | db.githubIdDao().insert(searchGithubId)
42 | var entity = db.githubIdDao().selectAll("g")[0]
43 | assertHabitEquals(searchGithubId, entity)
44 |
45 | db.githubIdDao().delete(entity)
46 | assertEquals(0, db.githubIdDao().selectAll("g").size)
47 | }
48 |
49 | private fun assertHabitEquals(expected: GithubId, actual: GithubId) {
50 | // id 는 자동생성되므로, 검증을 위해서 id의 동일성은 무시
51 | assertEquals(expected.copy(), actual.copy())
52 | }
53 |
54 | }
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: java
2 |
3 | jdk:
4 | - openjdk8
5 |
6 | before_cache:
7 | # Do not cache a few Gradle files/directories (see https://docs.travis-ci.com/user/languages/java/#Caching)
8 | - rm -f $HOME/.gradle/caches/modules-2/modules-2.lock
9 | - rm -fr $HOME/.gradle/caches/*/plugin-resolution/
10 |
11 | cache:
12 | directories:
13 | # Android SDK
14 | - $HOME/android-sdk-dl
15 | - $HOME/android-sdk
16 |
17 | # Gradle dependencies
18 | - $HOME/.gradle/caches/
19 | - $HOME/.gradle/wrapper/
20 |
21 | # Android build cache (see http://tools.android.com/tech-docs/build-cache)
22 | - $HOME/.android/build-cache
23 |
24 | install:
25 | # Download and unzip the Android SDK tools (if not already there thanks to the cache mechanism)
26 | # Latest version available here: https://developer.android.com/studio/#command-tools
27 | - if test ! -e $HOME/android-sdk-dl/sdk-tools.zip ; then curl https://dl.google.com/android/repository/sdk-tools-linux-4333796.zip > $HOME/android-sdk-dl/sdk-tools.zip ; fi
28 | - unzip -qq -n $HOME/android-sdk-dl/sdk-tools.zip -d $HOME/android-sdk
29 |
30 | # Install or update Android SDK components (will not do anything if already up to date thanks to the cache mechanism)
31 | - echo y | $HOME/android-sdk/tools/bin/sdkmanager 'platform-tools' > /dev/null
32 | - echo y | $HOME/android-sdk/tools/bin/sdkmanager 'build-tools;28.0.3' > /dev/null
33 | - echo y | $HOME/android-sdk/tools/bin/sdkmanager 'platforms;android-28' > /dev/null
34 |
35 | # https://github.com/kt3k/coveralls-gradle-plugin
36 | env:
37 | - ANDROID_HOME=$HOME/android-sdk
38 |
39 | before_script:
40 | - echo $CI_NAME
41 | - echo $CI_BUILD_NUMBER
42 | - echo $CI_BUILD_URL
43 | - echo $CI_BRANCH
44 | - make set_env
45 |
46 | script:
47 | - ./gradlew --no-daemon --parallel lintDebug testDebug
48 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/fragment_main_sub.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
10 |
13 |
14 |
18 |
19 |
26 |
27 |
34 |
35 |
36 |
37 |
38 |
43 |
44 |
45 |
46 |
47 |
48 |
--------------------------------------------------------------------------------
/app/src/main/java/com/seok/gfd/retrofit/domain/User.kt:
--------------------------------------------------------------------------------
1 | package com.seok.gfd.retrofit.domain
2 |
3 | import lombok.Getter
4 | import lombok.NoArgsConstructor
5 | import lombok.Setter
6 |
7 | @Getter
8 | @Setter
9 | @NoArgsConstructor
10 | class User {
11 | var login: String = ""
12 | var id: Long = 0
13 | var node_id: String = ""
14 | var avatar_url: String = ""
15 | var gravatar_id: String = ""
16 | var url: String = ""
17 | var html_url: String = ""
18 | var followers_url: String = ""
19 | var following_url: String = ""
20 | var gists_url: String = ""
21 | var starred_url: String = ""
22 | var subscriptions_url: String = ""
23 | var organizations_url: String = ""
24 | var repos_url: String = ""
25 | var events_url: String = ""
26 | var receivedEvents_url: String = ""
27 | var type: String = ""
28 | var site_admin: Boolean = false
29 | var name: String = ""
30 | var company: String = ""
31 | var blog: String = ""
32 | var location: String = ""
33 | var email: Any? = null
34 | var hireable: Any? = null
35 | var bio: String = ""
36 | var public_repos: Long = 0
37 | var public_gists: Long = 0
38 | var followers: Long = 0
39 | var following: Long = 0
40 | var created_at: String = ""
41 | var updated_at: String = ""
42 | var code: Int = 0
43 |
44 | constructor(login: String, html_url: String, avatar_url: String, code : Int) {
45 | this.login = login
46 | this.html_url = html_url
47 | this.avatar_url = avatar_url
48 | this.code = code
49 | }
50 | constructor(login: String, html_url: String, avatar_url: String, id: Long, code : Int) {
51 | this.login = login
52 | this.html_url = html_url
53 | this.avatar_url = avatar_url
54 | this.id = id
55 | this.code = code
56 | }
57 | constructor(login: String, html_url: String, avatar_url: String){
58 | this.login = login
59 | this.html_url = html_url
60 | this.avatar_url = avatar_url
61 | }
62 | constructor(code : Int){
63 | this.code = code
64 | }
65 | }
--------------------------------------------------------------------------------
/app/src/main/res/drawable-v24/ic_launcher_foreground.xml:
--------------------------------------------------------------------------------
1 |
7 |
12 |
13 |
19 |
22 |
25 |
26 |
27 |
28 |
34 |
35 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_main2.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
16 |
17 |
30 |
31 |
32 |
33 |
42 |
43 |
44 |
--------------------------------------------------------------------------------
/app/src/main/java/com/seok/gfd/adapter/GithubIdAdapter.kt:
--------------------------------------------------------------------------------
1 | package com.seok.gfd.adapter
2 |
3 | import android.view.LayoutInflater
4 | import android.view.View
5 | import android.view.ViewGroup
6 | import android.widget.EditText
7 | import androidx.fragment.app.FragmentActivity
8 | import androidx.lifecycle.ViewModelProviders
9 | import androidx.recyclerview.widget.RecyclerView
10 | import com.seok.gfd.R
11 | import com.seok.gfd.room.entity.GithubId
12 | import com.seok.gfd.viewmodel.GithubIdViewModel
13 | import kotlinx.android.synthetic.main.item_search_github_id.view.*
14 |
15 | class GithubIdAdapter(list: ArrayList, edtText : EditText) : RecyclerView.Adapter() {
16 | private val githubIds: ArrayList = list
17 | private val searchEditText = edtText
18 | private lateinit var githubIdsViewModel: GithubIdViewModel
19 |
20 | inner class SearchGithubIdViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView){
21 | fun bind(githubId: GithubId){
22 | itemView.item_search_txt_github_id.text = githubId.githubId
23 | itemView.item_search_img_close.setOnClickListener {
24 | githubIdsViewModel.deleteGithubId(githubId)
25 | githubIds.removeAt(this.adapterPosition)
26 | notifyItemRemoved(this.adapterPosition)
27 | notifyDataSetChanged()
28 | }
29 | itemView.item_search_card_view.setOnClickListener {
30 | searchEditText.setText(githubId.githubId)
31 | }
32 | }
33 | }
34 |
35 | override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
36 | val view = LayoutInflater.from(parent.context).inflate(R.layout.item_search_github_id, parent, false)
37 | githubIdsViewModel = ViewModelProviders.of(parent.context as FragmentActivity).get(GithubIdViewModel::class.java)
38 | return SearchGithubIdViewHolder(view)
39 | }
40 |
41 | override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
42 | val viewHolder = holder as SearchGithubIdViewHolder
43 | viewHolder.bind(githubIds[position])
44 | }
45 |
46 | override fun getItemCount(): Int {
47 | return githubIds.size
48 | }
49 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/seok/gfd/v1/views/Main2Activity.kt:
--------------------------------------------------------------------------------
1 | package com.seok.gfd.v1.views
2 |
3 | import android.os.Bundle
4 | import androidx.appcompat.app.AppCompatActivity
5 | import androidx.fragment.app.Fragment
6 | import com.google.android.gms.ads.AdRequest
7 | import com.google.android.gms.ads.MobileAds
8 | import com.seok.gfd.R
9 | import kotlinx.android.synthetic.main.activity_main2.*
10 |
11 | class Main2Activity : AppCompatActivity() {
12 | private val fragmentManager = supportFragmentManager
13 |
14 | private val mainFragment = MainFragment()
15 | // private val rankFragment = RankFragment()
16 | private val optionFragment = OptionFragment()
17 |
18 | private var activeFragment : Fragment = mainFragment
19 |
20 | override fun onCreate(savedInstanceState: Bundle?) {
21 | super.onCreate(savedInstanceState)
22 | setContentView(R.layout.activity_main2)
23 |
24 | fragmentManager.beginTransaction().add(R.id.host_fragment, mainFragment, "1").commit()
25 | // fragmentManager.beginTransaction().add(R.id.host_fragment, rankFragment, "2").hide(rankFragment).commit()
26 | fragmentManager.beginTransaction().add(R.id.host_fragment, optionFragment, "3").hide(optionFragment).commit()
27 | bottom_navigation.setOnNavigationItemSelectedListener {
28 | when(it.itemId){
29 | R.id.nav_menu_m -> {
30 | fragmentManager.beginTransaction().hide(activeFragment).show(mainFragment).commit()
31 | activeFragment = mainFragment
32 | }
33 | // R.id.nav_menu_r -> {
34 | // fragmentManager.beginTransaction().hide(activeFragment).show(rankFragment).commit()
35 | // activeFragment = rankFragment
36 | // }
37 | R.id.nav_menu_o -> {
38 | fragmentManager.beginTransaction().hide(activeFragment).show(optionFragment).commit()
39 | activeFragment = optionFragment
40 | }
41 | }
42 | true
43 | }
44 |
45 | MobileAds.initialize(
46 | this.application,
47 | getString(R.string.admob_app_id)
48 | )
49 | val adRequest = AdRequest.Builder().build()
50 | github_login_ads_view.loadAd(adRequest)
51 | }
52 | }
--------------------------------------------------------------------------------
/app/src/main/res/drawable/shadow.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | -
6 |
7 |
12 |
13 |
14 |
15 |
16 |
17 |
18 | -
19 |
20 |
25 |
26 |
27 |
28 |
29 |
30 |
31 | -
32 |
33 |
38 |
39 |
40 |
41 |
42 |
43 |
44 | -
45 |
46 |
51 |
52 |
53 |
54 |
55 |
56 |
57 | -
58 |
59 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 | -
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/item_search_github_id.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
19 |
20 |
23 |
24 |
35 |
36 |
45 |
46 |
47 |
--------------------------------------------------------------------------------
/app/src/main/java/com/seok/gfd/retrofit/RetrofitClient.kt:
--------------------------------------------------------------------------------
1 | package com.seok.gfd.retrofit
2 |
3 | import com.google.gson.GsonBuilder
4 | import com.seok.gfd.BuildConfig
5 | import com.seok.gfd.retrofit.service.UserService
6 | import com.seok.gfd.retrofit.service.CommitService
7 | import com.seok.gfd.retrofit.service.GithubApiService
8 | import com.seok.gfd.retrofit.service.GithubContributionService
9 | import retrofit2.Retrofit
10 | import retrofit2.converter.gson.GsonConverterFactory
11 |
12 | class RetrofitClient {
13 | companion object {
14 | lateinit var retrofit: Retrofit
15 | fun githubApiService(): GithubApiService {
16 | val gson = GsonBuilder().setLenient().create()
17 | retrofit = Retrofit.Builder()
18 | .baseUrl("https://api.github.com/")
19 | .addConverterFactory(GsonConverterFactory.create(gson))
20 | .build()
21 | return retrofit.create(GithubApiService::class.java)
22 | }
23 | fun githubAuthService(): GithubApiService{
24 | retrofit = Retrofit.Builder()
25 | .baseUrl("https://github.com/login/oauth/")
26 | .addConverterFactory(GsonConverterFactory.create())
27 | .build()
28 | return retrofit.create(GithubApiService::class.java)
29 | }
30 | fun userService() : UserService{
31 | val gson = GsonBuilder().setLenient().create()
32 | retrofit = Retrofit.Builder()
33 | .baseUrl("")
34 | .addConverterFactory(GsonConverterFactory.create(gson))
35 | .build()
36 | return retrofit.create(UserService::class.java)
37 | }
38 | fun commitService() : CommitService{
39 | val gson = GsonBuilder().setLenient().create()
40 | retrofit = Retrofit.Builder()
41 | .baseUrl("")
42 | .addConverterFactory(GsonConverterFactory.create(gson))
43 | .build()
44 | return retrofit.create(CommitService::class.java)
45 | }
46 | fun githubContributionService(): GithubContributionService{
47 | val gson = GsonBuilder().setLenient().create()
48 | retrofit = Retrofit.Builder()
49 | .baseUrl("https://github-contributions-api.now.sh/v1/")
50 | .addConverterFactory(GsonConverterFactory.create(gson))
51 | .build()
52 | return retrofit.create(GithubContributionService::class.java)
53 | }
54 | }
55 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/seok/gfd/viewmodel/GithubContributionViewModel.kt:
--------------------------------------------------------------------------------
1 | package com.seok.gfd.viewmodel
2 |
3 | import android.util.Log
4 | import androidx.lifecycle.LiveData
5 | import androidx.lifecycle.MutableLiveData
6 | import androidx.lifecycle.ViewModel
7 | import com.seok.gfd.retrofit.RetrofitClient
8 | import com.seok.gfd.retrofit.domain.resopnse.CommitsResponseDto
9 | import com.seok.gfd.retrofit.domain.resopnse.NestedContributionsResponseDto
10 | import retrofit2.Call
11 | import retrofit2.Response
12 |
13 | class GithubContributionViewModel : ViewModel() {
14 | private val _contributions = MutableLiveData()
15 | private val _nestedContributions = MutableLiveData()
16 |
17 | val contributions: LiveData
18 | get() = _contributions
19 | val nestedContributions: LiveData
20 | get() = _nestedContributions
21 |
22 |
23 | fun getContributions(userId: String) {
24 | val getContributionService = RetrofitClient.githubContributionService()
25 | val getContributionCall = getContributionService.getContributions(userId)
26 | getContributionCall.enqueue(object : retrofit2.Callback {
27 |
28 | override fun onResponse(
29 | call: Call,
30 | response: Response
31 | ) {
32 | _contributions.value = response.body()
33 | }
34 |
35 | override fun onFailure(call: Call, t: Throwable) {
36 | Log.e(this.javaClass.simpleName, t.message.toString())
37 | }
38 | })
39 | }
40 |
41 | fun getNestedContributions(userId: String) {
42 | val nestedContributionService = RetrofitClient.githubContributionService()
43 | val nestedContributionCall =
44 | nestedContributionService.getNestedContributions(userId, "nested")
45 | nestedContributionCall.enqueue(object : retrofit2.Callback {
46 |
47 | override fun onResponse(
48 | call: Call,
49 | response: Response
50 | ) {
51 | val t = response.body()
52 | t
53 | }
54 |
55 | override fun onFailure(call: Call, t: Throwable) {
56 | Log.e(this.javaClass.simpleName, t.message.toString())
57 | }
58 | })
59 | }
60 |
61 | }
--------------------------------------------------------------------------------
/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/rv_rank_item.xml:
--------------------------------------------------------------------------------
1 |
2 |
10 |
11 |
21 |
22 |
31 |
32 |
43 |
44 |
55 |
56 |
--------------------------------------------------------------------------------
/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | Daily Commit - 잔디
3 |
4 |
5 | Daily Commit - 잔디\n환영합니다
6 | Daily Commit - 잔디앱에 오신것을 환영합니다.\n로그인 버튼을 눌러주세요
7 | 로그인 토큰 발급을 실패하였습니다\n다시 시도 해주세요
8 | Today
9 | Max
10 | Year
11 | Home
12 | Rank
13 | Help
14 | SUN
15 | MON
16 | TUE
17 | WEN
18 | THU
19 | FRI
20 | SAT
21 | User ID
22 | https://github/gfd
23 | 금일 컨트리뷰션 랭킹
24 | 0
25 | 등
26 | 0
27 | Ctb
28 | 0
29 | gfd
30 | 0
31 | Github
32 | Login
33 | 누적 회원 수
34 | 도움을 주신 분
35 |
36 |
37 |
38 | empty token
39 | Empty key
40 | Empty user id
41 | Empty user url
42 | Empty user image
43 | user_id
44 | user_url
45 | user_today
46 | user_year
47 | user_max
48 | user_image
49 | user_gid
50 | user_info
51 |
52 |
53 | https://api.github.com/
54 | https://github.com/login/oauth/authorize/
55 | "https://github.com/login/oauth/"
56 |
57 |
58 | ca-app-pub-2766237193476584~2069127933
59 | ca-app-pub-2766237193476584/5214166022
60 | ca-app-pub-3940256099942544/6300978111
61 |
62 | 확인
63 |
64 |
65 |
66 |
--------------------------------------------------------------------------------
/.idea/navEditor.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
81 |
82 |
83 |
--------------------------------------------------------------------------------
/app/src/main/java/com/seok/gfd/v1/views/LoginActivity.kt:
--------------------------------------------------------------------------------
1 | package com.seok.gfd.v1.views
2 |
3 |
4 | import android.content.Intent
5 | import android.net.Uri
6 | import android.os.Bundle
7 | import android.os.Handler
8 | import android.view.View
9 | import androidx.appcompat.app.AppCompatActivity
10 | import androidx.lifecycle.Observer
11 | import androidx.lifecycle.ViewModelProviders
12 | import com.seok.gfd.R
13 | import com.seok.gfd.utils.SharedPreference
14 | import com.seok.gfd.viewmodel.UserViewModel
15 | import kotlinx.android.synthetic.main.activity_login.*
16 | import java.net.HttpURLConnection
17 |
18 | class LoginActivity : AppCompatActivity() {
19 |
20 | private lateinit var userViewModel: UserViewModel
21 | private lateinit var sharedPreference: SharedPreference
22 |
23 | override fun onCreate(savedInstanceState: Bundle?) {
24 | super.onCreate(savedInstanceState)
25 | setContentView(R.layout.activity_login)
26 |
27 | init()
28 | initViewModelFun()
29 | setOnClickFun()
30 |
31 | }
32 |
33 | // ViewModel 세팅 및 초기화
34 | private fun init() {
35 | sharedPreference = SharedPreference(application)
36 | userViewModel = ViewModelProviders.of(this).get(UserViewModel::class.java)
37 | userViewModel.getUsersCount()
38 | }
39 |
40 | // ViewModel 구현
41 | private fun initViewModelFun() {
42 | // 현재 사용자 수
43 | userViewModel.userCount.observe(this, Observer {
44 | login_tv_users_count.text = it.toString()
45 | })
46 | // Github 접속을 위한 access_token 요청
47 | userViewModel.accessToken.observe(this, Observer {
48 | sharedPreference.setValue("", it)
49 | userViewModel.getUserInfoAndSignInGithub(it)
50 | })
51 | // Github 로그인 성공 코드 200 / 401
52 | userViewModel.code.observe(this, Observer {
53 | if (it == HttpURLConnection.HTTP_OK) {
54 | goToMainActivity()
55 | } else {
56 | login_progress_bar.visibility = View.INVISIBLE
57 | }
58 | })
59 | userViewModel.userInfo.observe(this, Observer {
60 | sharedPreference.setValueObject(application.getString(R.string.user_info), it)
61 | })
62 | }
63 |
64 | override fun onNewIntent(intent: Intent?) {
65 | super.onNewIntent(intent)
66 | val uri = intent!!.data
67 | // 1회용 접속 Code
68 | val code = uri.toString().split("=")[1]
69 | userViewModel.getAccessTokenFromGithubApi(code)
70 | }
71 |
72 | private fun goToMainActivity() {
73 | startActivity(Intent(this, Main2Activity::class.java))
74 | overridePendingTransition(R.anim.fade_in, R.anim.fade_out)
75 | }
76 |
77 | private fun setOnClickFun(){
78 | // Guest 로그인 버튼을 눌렀을 경우
79 | login_img_guest.setOnClickListener {
80 | syncUI()
81 | startActivity(Intent(this, GuestMain::class.java))
82 | overridePendingTransition(R.anim.fade_in, R.anim.fade_out)
83 | }
84 |
85 | // 로그인 버튼 눌렀을 경우 Github login 창으로 넘김
86 | login_img_login.setOnClickListener {
87 | syncUI()
88 | val intent = Intent(
89 | Intent.ACTION_VIEW,
90 | Uri.parse("")
91 | )
92 | // onNewIntent() 리다이렉트
93 | startActivityForResult(intent, HttpURLConnection.HTTP_OK)
94 | }
95 | }
96 |
97 | private fun syncUI(){
98 | login_progress_bar.visibility = View.VISIBLE
99 | login_img_login.isClickable = false
100 | login_img_guest.isClickable = false
101 | Handler().postDelayed({
102 | login_progress_bar.visibility = View.INVISIBLE
103 | login_img_login.isClickable = true
104 | login_img_guest.isClickable = true
105 | }, 3000)
106 | }
107 | }
108 |
--------------------------------------------------------------------------------
/app/src/main/java/com/seok/gfd/v1/views/RankFragment.kt:
--------------------------------------------------------------------------------
1 | package com.seok.gfd.v1.views
2 |
3 |
4 | import android.annotation.SuppressLint
5 | import android.os.Bundle
6 | import android.util.Log
7 | import android.view.LayoutInflater
8 | import android.view.View
9 | import android.view.ViewGroup
10 | import androidx.fragment.app.Fragment
11 | import androidx.lifecycle.Observer
12 | import androidx.lifecycle.ViewModelProviders
13 | import androidx.recyclerview.widget.LinearLayoutManager
14 | import androidx.recyclerview.widget.RecyclerView
15 | import com.bumptech.glide.Glide
16 | import com.bumptech.glide.request.RequestOptions
17 | import com.seok.gfd.R
18 | import com.seok.gfd.adapter.CommitsAdapter
19 | import com.seok.gfd.retrofit.domain.resopnse.CommitResponse
20 | import com.seok.gfd.utils.SharedPreference
21 | import com.seok.gfd.viewmodel.UserViewModel
22 | import kotlinx.android.synthetic.main.fragment_rank.*
23 | import java.lang.Exception
24 |
25 | /**
26 | * A simple [Fragment] subclass.
27 | */
28 | class RankFragment : Fragment() {
29 | private lateinit var userViewModel: UserViewModel
30 | private lateinit var linearLayoutManager: LinearLayoutManager
31 | private lateinit var adapter: CommitsAdapter
32 | private var lastViesibleItemPosition: Int = 0
33 | get() = linearLayoutManager.findLastVisibleItemPosition()
34 |
35 | private lateinit var sharedPreference: SharedPreference
36 |
37 | override fun onCreateView(
38 | inflater: LayoutInflater,
39 | container: ViewGroup?,
40 | savedInstanceState: Bundle?
41 | ): View? {
42 | // Inflate the layout for this fragment
43 | return inflater.inflate(R.layout.fragment_rank, container, false)
44 | }
45 |
46 | override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
47 | super.onViewCreated(view, savedInstanceState)
48 | init()
49 | initViewModelFun()
50 | setRecyclerViewScrollListener()
51 | }
52 |
53 | private fun setRecyclerViewScrollListener() {
54 | rv_rank.addOnScrollListener(object : RecyclerView.OnScrollListener() {
55 | override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) {
56 | super.onScrollStateChanged(recyclerView, newState)
57 | val totalItemCount = rv_rank.layoutManager!!.itemCount
58 | }
59 | })
60 | }
61 |
62 | @SuppressLint("NewApi")
63 | private fun init() {
64 | sharedPreference = SharedPreference(this.activity!!.application)
65 | linearLayoutManager = LinearLayoutManager(this.context)
66 | userViewModel = ViewModelProviders.of(this).get(UserViewModel::class.java)
67 |
68 | val user = sharedPreference.getValueObject(getString(R.string.user_info))
69 | Glide.with(this).load(user.avatar_url).apply(RequestOptions.circleCropTransform())
70 | .into(img_rv_profile)
71 | user.avatar_url
72 | rv_rank.layoutManager = linearLayoutManager
73 | tv_rv_commit.text = sharedPreference.getValue(getString(R.string.user_today))
74 | userViewModel.getCommitsRank()
75 |
76 | img_rank_sync.setOnClickListener {
77 | userViewModel.getCommitsRank()
78 | }
79 |
80 | }
81 |
82 | private fun initViewModelFun() {
83 | val user = sharedPreference.getValueObject(getString(R.string.user_info))
84 | userViewModel.commitList.observe(this, Observer {
85 | try {
86 | val user = it.find { it.user_id == user.login }
87 | Glide.with(this).load(user?.user_image).apply(RequestOptions.circleCropTransform())
88 | .into(img_rv_profile)
89 | tv_rv_commit.text = user?.data_count.toString()
90 | adapter = CommitsAdapter(it as ArrayList)
91 | rv_rank.adapter = adapter
92 | } catch (e: Exception) {
93 | Log.e(this.javaClass.simpleName, e.toString())
94 | }
95 | })
96 | }
97 | }
98 |
--------------------------------------------------------------------------------
/app/src/main/java/com/seok/gfd/retrofit/repository/UserRepository.kt:
--------------------------------------------------------------------------------
1 | package com.seok.gfd.retrofit.repository
2 |
3 | import android.app.Application
4 | import android.util.Log
5 | import androidx.lifecycle.MutableLiveData
6 | import com.seok.gfd.BuildConfig
7 | import com.seok.gfd.retrofit.RetrofitClient
8 | import com.seok.gfd.retrofit.domain.GfdUser
9 | import com.seok.gfd.retrofit.domain.Token
10 | import com.seok.gfd.retrofit.domain.User
11 | import retrofit2.Call
12 | import retrofit2.Callback
13 | import retrofit2.Response
14 | import java.net.HttpURLConnection
15 |
16 | class UserRepository(application: Application) {
17 | private lateinit var user: User
18 | private lateinit var token: Token
19 | private val userMutableData = MutableLiveData()
20 | private val codeMutableData = MutableLiveData()
21 | private var application: Application = application
22 |
23 | fun getUserMutableData(token: String): MutableLiveData {
24 | val userService = RetrofitClient.githubApiService()
25 | val call = userService.getUserInfoFromGithubApi("token $token")
26 | // call.enqueue(object : Callback {
27 | // override fun onResponse(call: Call, response: Response) {
28 | // if (response.isSuccessful) {
29 | // val body = response.body()!!
30 | // user =
31 | // User(body.login, body.html_url, body.avatar_url, HttpURLConnection.HTTP_OK)
32 | // val signUpService = RetrofitClient.userService()
33 | // val signUpCall = signUpService.signUpUser(
34 | // BuildConfig.BASIC_AUTH_KEY
35 | // , GfdUser(body.login, body.html_url, body.avatar_url)
36 | // )
37 | // signUpCall.enqueue(object :Callback{
38 | // override fun onResponse(call: Call, response: Response) {
39 | // Log.d(this.javaClass.simpleName, "Success signUp")
40 | // }
41 | // override fun onFailure(call: Call, t: Throwable) {
42 | // Log.e(this.javaClass.simpleName, t.message.toString())
43 | // }
44 | // })
45 | // } else {
46 | // user = User(HttpURLConnection.HTTP_UNAUTHORIZED)
47 | // }
48 | // userMutableData.value = user
49 | // }
50 | //
51 | // override fun onFailure(call: Call, t: Throwable) {
52 | // Log.e(this.javaClass.simpleName, t.message.toString())
53 | // }
54 | // })
55 | return userMutableData
56 | }
57 |
58 | // fun getCodeMutableData(
59 | // clientId: String,
60 | // clientSecret: String,
61 | // code: String
62 | // ): MutableLiveData {
63 | // val codeService = RetrofitClient.githubAuthService()
64 | // val call = codeService.getAccessTokenFromGithubApi(clientId, clientSecret, code)
65 | // call.enqueue(object : Callback {
66 | // override fun onResponse(call: Call, response: Response) {
67 | // token = if (response.isSuccessful) {
68 | // val body = response.body()!!
69 | // Token(
70 | // body.access_token,
71 | // body.scope,
72 | // body.token_type,
73 | // HttpURLConnection.HTTP_OK
74 | // )
75 | // } else {
76 | // Token(HttpURLConnection.HTTP_NOT_FOUND)
77 | // }
78 | // codeMutableData.value = token
79 | // }
80 | //
81 | // override fun onFailure(call: Call, t: Throwable) {
82 | // Log.e(this.javaClass.simpleName, t.message.toString())
83 | // }
84 | // })
85 | // return codeMutableData
86 | // }
87 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/seok/gfd/viewmodel/RankFragmentViewModel.kt:
--------------------------------------------------------------------------------
1 | package com.seok.gfd.viewmodel
2 |
3 | import android.annotation.SuppressLint
4 | import android.app.Application
5 | import android.util.Log
6 | import androidx.lifecycle.AndroidViewModel
7 | import androidx.lifecycle.LiveData
8 | import androidx.lifecycle.MutableLiveData
9 | import com.seok.gfd.BuildConfig
10 | import com.seok.gfd.retrofit.RetrofitClient
11 | import com.seok.gfd.retrofit.domain.request.CommitRequestDto
12 | import com.seok.gfd.retrofit.domain.resopnse.CommitResponse
13 | import retrofit2.Call
14 | import retrofit2.Callback
15 | import retrofit2.Response
16 | import java.text.SimpleDateFormat
17 | import java.util.*
18 |
19 | class RankFragmentViewModel(application: Application) : AndroidViewModel(application) {
20 | private val app = application
21 |
22 | private val _rankList = MutableLiveData>()
23 | val rankList: LiveData>
24 | get() = _rankList
25 |
26 | private val _serverResult = MutableLiveData()
27 | val serverResult: LiveData
28 | get() = _serverResult
29 |
30 | @SuppressLint("SimpleDateFormat")
31 | fun getTodayRankList() {
32 | val dateFormat = SimpleDateFormat("yyyy-MM-dd")
33 |
34 | val getTodayRankListService = RetrofitClient.commitService()
35 | val getTodayRankListCall = getTodayRankListService.getTRCommitList(
36 | "",
37 | dateFormat.format(Date())
38 | )
39 | getTodayRankListCall.enqueue(object : Callback> {
40 | override fun onResponse(
41 | call: Call>,
42 | response: Response>
43 | ) {
44 | val body = if (response.isSuccessful) {
45 | response.body()
46 | } else {
47 | null
48 | }
49 | _rankList.postValue(body)
50 | }
51 |
52 | override fun onFailure(call: Call>, t: Throwable) {
53 | Log.e(this.javaClass.simpleName, t.message.toString())
54 | }
55 | })
56 | }
57 |
58 | fun updateTodayRankCommit(userId: String, dataCount: Int) {
59 | val updateTodayRankCommitService = RetrofitClient.commitService()
60 | val updateTodayRankCommitCall = updateTodayRankCommitService.enrollCommit(
61 | "",
62 | CommitRequestDto(userId, dataCount)
63 | )
64 | // updateTodayRankCommitCall.enqueue(object : Callback {
65 | // override fun onResponse(call: Call, response: Response) {
66 | // if (response.body().equals("CREATE")) {
67 | // _serverResult.postValue(true)
68 | // }
69 | // }
70 | //
71 | // override fun onFailure(call: Call, t: Throwable) {
72 | // Log.e(this.javaClass.simpleName, t.message.toString())
73 | // }
74 | // })
75 | }
76 | fun getTodayRankCommitList(){
77 | val dateFormat = SimpleDateFormat("yyyy-MM-dd")
78 | val getTodayRankListService = RetrofitClient.commitService()
79 | val getTodayRankListCall = getTodayRankListService.getTRCommitList(
80 | "",
81 | dateFormat.format(Date()))
82 | getTodayRankListCall.enqueue(object : Callback>{
83 | override fun onResponse(
84 | call: Call>,
85 | response: Response>
86 | ) {
87 | val body = if (response.isSuccessful) {
88 | response.body()
89 | } else {
90 | null
91 | }
92 | _rankList.postValue(body)
93 | }
94 | override fun onFailure(call: Call>, t: Throwable) {
95 | Log.e(this.javaClass.simpleName, t.message.toString())
96 | }
97 | })
98 | }
99 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/seok/gfd/viewmodel/GithubCommitDataViewModel.kt:
--------------------------------------------------------------------------------
1 | package com.seok.gfd.viewmodel
2 |
3 | import android.annotation.SuppressLint
4 | import android.util.Log
5 | import androidx.lifecycle.LiveData
6 | import androidx.lifecycle.MutableLiveData
7 | import androidx.lifecycle.ViewModel
8 | import com.seok.gfd.database.Commits
9 | import com.seok.gfd.retrofit.RetrofitClient
10 | import com.seok.gfd.retrofit.domain.resopnse.CommitsResponseDto
11 | import org.jsoup.Connection
12 | import org.jsoup.Jsoup
13 | import retrofit2.Call
14 | import retrofit2.Response
15 | import java.time.LocalDate
16 |
17 | class GithubCommitDataViewModel: ViewModel() {
18 | private val _commit = MutableLiveData()
19 | private val _commits = MutableLiveData>()
20 | private val _yearCommit = MutableLiveData()
21 | private val _todayCommit = MutableLiveData()
22 | private val _maxCommit = MutableLiveData()
23 |
24 | val commit: LiveData
25 | get() = _commit
26 | val commits: LiveData>
27 | get() = _commits
28 | val yearCommit: LiveData
29 | get() = _yearCommit
30 | val todayCommit: LiveData
31 | get() = _todayCommit
32 | val maxCommit: LiveData
33 | get() = _maxCommit
34 |
35 | // 사용자 깃허브에서 데이터 크롤링
36 | fun getCommitFromGithub(uri : String){
37 | // Crawling 백그라운드 실행
38 | // doAsync {
39 | // val soup = Jsoup.connect(uri).method(Connection.Method.GET).execute().parse()
40 | // val partOfContributionData = soup.select("div[class=js-yearly-contributions]").first()
41 | // val lines = partOfContributionData.select("rect[class=day]")
42 | //
43 | // val contributions = ArrayList()
44 | // for (line in lines) {
45 | // val attrDataDate = line.attr("data-date")
46 | // val attrDataCount = line.attr("data-count")
47 | // val attrFill = line.attr("fill")
48 | // contributions.add(Commits(attrDataDate, attrDataCount.toInt(), attrFill))
49 | // }
50 | //// _commits.postValue(contributions)
51 | // _commit.postValue(contributions[contributions.size-1])
52 | // _maxCommit.postValue(contributions.maxBy { it.dataCount }?.dataCount.toString())
53 | // }
54 | }
55 |
56 | // 사용자 1년 동안 커밋 데이 크롤링
57 | fun getYearCommitFromGithub(uri : String){
58 | // Crawling 백그라운드 실행
59 | // doAsync {
60 | // val soup = Jsoup.connect(uri).method(Connection.Method.GET).execute().parse()
61 | // val contributionYearData = soup.select("h2[class=f4 text-normal mb-2]").first()
62 | // val contribution = contributionYearData.text().split(" ")
63 | // _yearCommit.postValue(contribution[0])
64 | // }
65 | }
66 |
67 | // 깃허브 사용자 커밋 데이터 가져오기
68 | fun getCommitsInfo(userId : String){
69 | val getCommitsService = RetrofitClient.githubContributionService()
70 | val getCommitsCall = getCommitsService.getContributions(userId)
71 | getCommitsCall.enqueue(object : retrofit2.Callback {
72 | @SuppressLint("NewApi")
73 | override fun onResponse(call: Call, response: Response) {
74 | val body = response.body()
75 | val max = body?.contributions?.maxBy {
76 | it.count
77 | }
78 | val today = body?.contributions?.find {
79 | it.date == LocalDate.now().toString()
80 | }
81 | _maxCommit.value = max?.count.toString()
82 | _yearCommit.value = body?.years?.get(0)?.total.toString()
83 | _todayCommit.value = today?.count.toString()
84 | _commits.value = body?.contributions
85 | }
86 |
87 | override fun onFailure(call: Call, t: Throwable) {
88 | Log.e(this.javaClass.simpleName, t.message.toString())
89 | }
90 | })
91 | }
92 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/seok/gfd/v1/views/MainFragment.kt:
--------------------------------------------------------------------------------
1 | package com.seok.gfd.v1.views
2 |
3 |
4 | import android.os.Bundle
5 | import android.view.LayoutInflater
6 | import android.view.View
7 | import android.view.ViewGroup
8 | import androidx.fragment.app.Fragment
9 | import androidx.lifecycle.Observer
10 | import androidx.lifecycle.ViewModelProviders
11 | import com.bumptech.glide.Glide
12 | import com.bumptech.glide.load.resource.bitmap.CenterCrop
13 | import com.bumptech.glide.load.resource.bitmap.RoundedCorners
14 | import com.bumptech.glide.request.RequestOptions
15 | import com.ogaclejapan.smarttablayout.utils.v4.FragmentPagerItemAdapter
16 | import com.ogaclejapan.smarttablayout.utils.v4.FragmentPagerItems
17 | import com.seok.gfd.R
18 | import com.seok.gfd.retrofit.domain.User
19 | import com.seok.gfd.retrofit.domain.resopnse.CommitsResponseDto
20 | import com.seok.gfd.utils.ProgressbarDialog
21 | import com.seok.gfd.utils.SharedPreference
22 | import com.seok.gfd.utils.ValidationCheck
23 | import com.seok.gfd.viewmodel.GithubContributionViewModel
24 | import kotlinx.android.synthetic.main.fragment_main.*
25 | import java.time.LocalDate
26 |
27 | class MainFragment : Fragment() {
28 | private lateinit var sharedPreference: SharedPreference
29 | private lateinit var progressbar: ProgressbarDialog
30 | private lateinit var githubContributionViewModel: GithubContributionViewModel
31 |
32 | private lateinit var user: User
33 |
34 | override fun onCreateView(
35 | inflater: LayoutInflater,
36 | container: ViewGroup?,
37 | savedInstanceState: Bundle?
38 | ): View? {
39 | return inflater.inflate(R.layout.fragment_main, container, false)
40 | }
41 |
42 | override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
43 | super.onViewCreated(view, savedInstanceState)
44 | init()
45 | initSetUI()
46 | initViewModelFun()
47 | }
48 |
49 | private fun init() {
50 | progressbar = ProgressbarDialog(context!!)
51 | progressbar.show()
52 | sharedPreference = SharedPreference(this.activity!!.application)
53 | user = sharedPreference.getValueObject(getString(R.string.user_info))
54 | githubContributionViewModel =
55 | ViewModelProviders.of(this).get(GithubContributionViewModel::class.java)
56 | githubContributionViewModel.getContributions(user.login)
57 | }
58 |
59 | private fun initViewModelFun() {
60 | githubContributionViewModel.contributions.observe(this, Observer {
61 | val fragmentPagerItems = FragmentPagerItems.with(activity)
62 | for (element in it.years!!) {
63 | val commitResponseDto = getYearContributionData(element.year, it)
64 | fragmentPagerItems.add(element.year + "(" + element.total + ")", MainSub::class.java,
65 | MainSub.arguments(commitResponseDto)
66 | )
67 | }
68 | val adapter = FragmentPagerItemAdapter(
69 | activity?.supportFragmentManager,
70 | fragmentPagerItems.create()
71 | )
72 | main_view_pager.adapter = adapter
73 | main_tab_smart_layout.setViewPager(main_view_pager)
74 | progressbar.hide()
75 | })
76 | }
77 |
78 | private fun initSetUI() {
79 | main_tv_today.text = LocalDate.now().toString()
80 | main_tv_user_name.text = user.login
81 |
82 | val requestOptions = RequestOptions().transform(CenterCrop(), RoundedCorners(50))
83 | Glide.with(this).load(user.avatar_url).apply(requestOptions).into(main_image_profile)
84 | main_tv_user_bio.text = user.bio
85 | }
86 |
87 | private fun getYearContributionData(year: String, commitsResponseDto: CommitsResponseDto): CommitsResponseDto {
88 | var yearContributionDtoItem = CommitsResponseDto(year)
89 | var resultList = yearContributionDtoItem.contributions as ArrayList
90 | for (element in commitsResponseDto.contributions!!) {
91 | if (element.date.contains(year)) {
92 | resultList.add(element)
93 | }
94 | }
95 | yearContributionDtoItem.contributions = resultList
96 | return yearContributionDtoItem
97 | }
98 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/seok/gfd/adapter/ContributionsAdapter.kt:
--------------------------------------------------------------------------------
1 | package com.seok.gfd.adapter
2 |
3 | import android.annotation.SuppressLint
4 | import android.content.Context
5 | import android.graphics.Color
6 | import android.view.Gravity
7 | import android.view.LayoutInflater
8 | import android.view.View
9 | import android.view.ViewGroup
10 | import android.widget.LinearLayout
11 | import android.widget.TextView
12 | import androidx.recyclerview.widget.RecyclerView
13 | import com.seok.gfd.R
14 | import com.seok.gfd.utils.Contribution
15 | import java.time.LocalDate
16 | import java.time.Month
17 |
18 | class ContributionsAdapter(list: ArrayList, context: Context) :
19 | RecyclerView.Adapter() {
20 | var list = list
21 | var context: Context = context
22 |
23 | override fun getItemCount() = list.size
24 |
25 | override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder {
26 | val v = LayoutInflater.from(parent.context)
27 | .inflate(R.layout.contributions_layout, parent, false)
28 | return MyViewHolder(v)
29 | }
30 |
31 |
32 | @SuppressLint("SetTextI18n")
33 | override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
34 | val contribution = list[position]
35 | val formatYear =
36 | String.format("%s: %d Contributions", contribution.year, contribution.total)
37 | holder.yearContribution.text = formatYear
38 |
39 | var monthFlag = ""
40 | val size = contribution.list!!.size
41 | var lineLayout = LinearLayout(this.context)
42 | lineLayout.orientation = LinearLayout.VERTICAL
43 | val params = LinearLayout.LayoutParams(35, 35)
44 | // params.margin = 4
45 | params.gravity = Gravity.CENTER
46 | for (index in 0 until size) {
47 | var tempLayout = LinearLayout(this.context)
48 | if (index % 7 == 0) {
49 | val monthDataStr = getDayStartCount(LocalDate.parse(contribution.list!![index].date).month)
50 | lineLayout = LinearLayout(this.context)
51 | lineLayout.orientation = LinearLayout.VERTICAL
52 | // tempLayout.backgroundColor = Color.WHITE
53 | tempLayout.layoutParams = params
54 | if(monthFlag != monthDataStr){
55 | val month = TextView(this.context)
56 | month.textSize = 7f
57 | month.text = monthDataStr
58 | tempLayout.addView(month)
59 | monthFlag = monthDataStr
60 | }
61 | lineLayout.addView(tempLayout)
62 | tempLayout = LinearLayout(this.context)
63 | // tempLayout.backgroundColor = Color.parseColor(contribution.list!![index].color)
64 | tempLayout.layoutParams = params
65 | lineLayout.addView(tempLayout)
66 | } else {
67 | // tempLayout.backgroundColor = Color.parseColor(contribution.list!![index].color)
68 | tempLayout.layoutParams = params
69 | lineLayout.addView(tempLayout)
70 | }
71 | if (index % 7 == 0) {
72 | // holder.contributionCanvas.addView(lineLayout)
73 | }
74 | if (index == size) {
75 | // holder.contributionCanvas.addView(lineLayout)
76 | }
77 | }
78 | }
79 |
80 | inner class MyViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
81 | var yearContribution: TextView = itemView.findViewById(R.id.guest_year_contribution)
82 | // var contributionCanvas: GridLayout = itemView.findViewById(R.id.canvas)
83 | }
84 |
85 | private fun getDayStartCount(dayOfMonth: Month): String = when (dayOfMonth) {
86 | Month.JANUARY -> "Jan"
87 | Month.FEBRUARY -> "Feb"
88 | Month.MARCH -> "Mar"
89 | Month.APRIL -> "Apr"
90 | Month.MAY -> "May"
91 | Month.JUNE -> "Jun"
92 | Month.JULY -> "Jul"
93 | Month.AUGUST-> "Aug"
94 | Month.SEPTEMBER-> "Sep"
95 | Month.OCTOBER -> "Oct"
96 | Month.NOVEMBER-> "Nov"
97 | Month.DECEMBER -> "Dec"
98 | else -> "X"
99 | }
100 | }
101 |
102 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_search.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
19 |
20 |
27 |
28 |
38 |
39 |
40 |
52 |
53 |
64 |
65 |
82 |
83 |
91 |
--------------------------------------------------------------------------------
/app/src/main/java/com/seok/gfd/v1/views/MainSub.kt:
--------------------------------------------------------------------------------
1 | package com.seok.gfd.v1.views
2 |
3 | import android.graphics.Color
4 | import android.os.Bundle
5 | import android.view.LayoutInflater
6 | import android.view.View
7 | import android.view.ViewGroup
8 | import android.widget.LinearLayout
9 | import android.widget.TextView
10 | import android.widget.Toast
11 | import androidx.fragment.app.Fragment
12 | import com.ogaclejapan.smarttablayout.utils.v4.Bundler
13 | import com.seok.gfd.R
14 | import com.seok.gfd.retrofit.domain.resopnse.CommitsResponseDto
15 | import kotlinx.android.synthetic.main.fragment_main_sub.*
16 | import java.time.DayOfWeek
17 | import java.time.LocalDate
18 | import java.time.format.DateTimeFormatter
19 | import kotlin.math.ceil
20 |
21 | class MainSub : Fragment() {
22 |
23 | private lateinit var commitResponse: CommitsResponseDto
24 |
25 | companion object {
26 | fun arguments(param: CommitsResponseDto): Bundle {
27 | // val str = CommonUtils.gson.toJson(param)
28 | return Bundler().putString("year", "str").get()
29 | }
30 | }
31 |
32 | override fun onCreateView(
33 | inflater: LayoutInflater,
34 | container: ViewGroup?,
35 | savedInstanceState: Bundle?
36 | ): View? {
37 | return inflater.inflate(R.layout.fragment_main_sub, container, false)
38 | }
39 |
40 | override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
41 | super.onViewCreated(view, savedInstanceState)
42 |
43 | initUI()
44 |
45 | }
46 |
47 |
48 | override fun onCreate(savedInstanceState: Bundle?) {
49 | super.onCreate(savedInstanceState)
50 | init()
51 | }
52 |
53 | private fun init() {
54 | val yearContribution = arguments?.getString("year")
55 | }
56 |
57 | private fun createContributionUI(commitSize: Int, startDay: Int, commits : List) {
58 | // Week 단위 표시
59 | var weekPosTop = 20f
60 | for (index in 1..ceil(commitSize.toDouble() / 7).toInt() step 2) {
61 | val weekText = TextView(activity)
62 | weekText.text = index.toString() + "W"
63 | main_sub_scalable_layout.addView(weekText, 20f, weekPosTop, 100f, 50f)
64 | main_sub_scalable_layout.setScale_TextSize(weekText, 35f)
65 | weekPosTop += 55f
66 | }
67 | weekPosTop = 20f
68 | for (index in 2..commitSize / 7 step 2) {
69 | val weekText = TextView(activity)
70 | weekText.text = index.toString() + "W"
71 | main_sub_scalable_layout.addView(weekText, 570f, weekPosTop, 100f, 50f)
72 | main_sub_scalable_layout.setScale_TextSize(weekText, 35f)
73 | weekPosTop += 55f
74 | }
75 |
76 | // Contribution 표시
77 | var layoutSize = 45f
78 | var layoutPScaleTop = 30f
79 | var layoutPScaleLeft = 110f
80 | var lineChange = 0
81 |
82 | layoutPScaleLeft += 55f * startDay
83 | lineChange += startDay
84 |
85 | for (index in commitSize - 1 downTo 0) {
86 | val commit = commits[index]
87 | val linearLayout = LinearLayout(activity)
88 | main_sub_scalable_layout.addView(linearLayout, layoutPScaleLeft, layoutPScaleTop, layoutSize, layoutSize)
89 | layoutPScaleLeft += 55f
90 | lineChange++
91 | if (lineChange % 14 == 0) {
92 | layoutPScaleTop += 55f
93 | layoutPScaleLeft = 110f
94 | } else if (lineChange % 7 == 0) {
95 | layoutPScaleLeft += 170f
96 | }
97 | linearLayout.setOnClickListener { Toast.makeText(activity, String.format("%s (%d)", commit.date, commit.count) , Toast.LENGTH_SHORT).show() }
98 | }
99 | }
100 |
101 | private fun initUI() {
102 | val commits = commitResponse.contributions!!
103 | val commitSize = commits.size
104 | val date = commits[commitSize - 1].date
105 | val localDate = LocalDate.parse(date, DateTimeFormatter.ISO_DATE)
106 | val startDay = getDayStartCount(localDate.dayOfWeek)
107 | createContributionUI(commitSize, startDay, commits)
108 | }
109 |
110 | private fun getDayStartCount(dayOfWeek: DayOfWeek): Int = when (dayOfWeek) {
111 | DayOfWeek.MONDAY -> 0
112 | DayOfWeek.TUESDAY -> 1
113 | DayOfWeek.WEDNESDAY -> 2
114 | DayOfWeek.THURSDAY -> 3
115 | DayOfWeek.FRIDAY -> 4
116 | DayOfWeek.SATURDAY -> 5
117 | DayOfWeek.SUNDAY -> 6
118 | else -> 0
119 | }
120 | }
121 |
--------------------------------------------------------------------------------
/app/src/main/java/com/seok/gfd/v1/views/OptionFragment.kt:
--------------------------------------------------------------------------------
1 | package com.seok.gfd.v1.views
2 |
3 | import android.os.Bundle
4 | import android.view.LayoutInflater
5 | import android.view.View
6 | import android.view.ViewGroup
7 | import androidx.fragment.app.Fragment
8 | import com.bumptech.glide.Glide
9 | import com.bumptech.glide.request.RequestOptions
10 | import com.seok.gfd.R
11 | import kotlinx.android.synthetic.main.fragment_option.*
12 |
13 | class OptionFragment : Fragment() {
14 | override fun onCreateView(
15 | inflater: LayoutInflater,
16 | container: ViewGroup?,
17 | savedInstanceState: Bundle?
18 | ): View? {
19 | return inflater.inflate(R.layout.fragment_option, container, false)
20 | }
21 |
22 | override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
23 | super.onViewCreated(view, savedInstanceState)
24 | setUI()
25 | }
26 |
27 | private fun init() {
28 |
29 | }
30 | private fun initViewModelFun() {
31 |
32 | }
33 | private fun setUI() {
34 | Glide.with(activity!!.applicationContext).load("https://avatars0.githubusercontent.com/u/32855880?v=4").apply(RequestOptions.circleCropTransform()).into(ot_img_user_1)
35 | ot_tv_user_name_1.text = "ellapresso"
36 | ot_tv_user_github_1.text = "https://github.com/ellapresso"
37 | ot_tv_user_etc_1.text = "Seoul"
38 |
39 | // val getUserInfoService = RetrofitClient.githubApiService()
40 | // var getUserInfoCall1 = getUserInfoService.getUserInfo("ellapresso")
41 | // getUserInfoCall1.enqueue(object : retrofit2.Callback{
42 | // override fun onResponse(call: Call, response: Response) {
43 | // val userInfo = response.body()
44 | // Glide.with(activity!!.applicationContext).load(userInfo?.avatar_url).apply(RequestOptions.circleCropTransform()).into(ot_img_user_1)
45 | // ot_tv_user_name_1.text = userInfo?.login
46 | // ot_tv_user_github_1.text = userInfo?.html_url
47 | // ot_tv_user_etc_1.text = userInfo?.location
48 | // }
49 | // override fun onFailure(call: Call, t: Throwable) {
50 | // Log.e(this.javaClass.simpleName, t.message.toString())
51 | // }
52 | // })
53 |
54 | Glide.with(activity!!.applicationContext).load("https://avatars0.githubusercontent.com/u/28593727?v=4").apply(RequestOptions.circleCropTransform()).into(ot_img_user_2)
55 | ot_tv_user_name_2.text = "9992"
56 | ot_tv_user_github_2.text = "https://github.com/9992"
57 | ot_tv_user_etc_2.text = "Seoul"
58 |
59 | // val getUserInfoCall2 = getUserInfoService.getUserInfo("9992")
60 | // getUserInfoCall2.enqueue(object : retrofit2.Callback{
61 | // override fun onResponse(call: Call, response: Response) {
62 | // val userInfo = response.body()
63 | // Glide.with(activity!!.applicationContext).load(userInfo?.avatar_url).apply(RequestOptions.circleCropTransform()).into(ot_img_user_2)
64 | // ot_tv_user_name_2.text = userInfo?.login
65 | // ot_tv_user_github_2.text = userInfo?.html_url
66 | // ot_tv_user_etc_2.text = userInfo?.location
67 | // }
68 | // override fun onFailure(call: Call, t: Throwable) {
69 | // Log.e(this.javaClass.simpleName, t.message.toString())
70 | // }
71 | // })
72 |
73 | Glide.with(activity!!.applicationContext).load("https://avatars1.githubusercontent.com/u/41931979?v=4").apply(RequestOptions.circleCropTransform()).into(ot_img_user_3)
74 | ot_tv_user_name_3.text = "dogcolley"
75 | ot_tv_user_github_3.text = "https://github.com/dogcolley"
76 | ot_tv_user_etc_3.text = "Seoul"
77 |
78 | // val getUserInfoCall3 = getUserInfoService.getUserInfo("dogcolley")
79 | // getUserInfoCall3.enqueue(object : retrofit2.Callback{
80 | // override fun onResponse(call: Call, response: Response) {
81 | // val userInfo = response.body()
82 | // Glide.with(activity!!.applicationContext).load(userInfo?.avatar_url).apply(RequestOptions.circleCropTransform()).into(ot_img_user_3)
83 | // ot_tv_user_name_3.text = userInfo?.login
84 | // ot_tv_user_github_3.text = userInfo?.html_url
85 | // ot_tv_user_etc_3.text = userInfo?.location
86 | // }
87 | // override fun onFailure(call: Call, t: Throwable) {
88 | // Log.e(this.javaClass.simpleName, t.message.toString())
89 | // }
90 | // })
91 | }
92 | }
93 |
--------------------------------------------------------------------------------
/app/src/main/java/com/seok/gfd/views/SearchActivity.kt:
--------------------------------------------------------------------------------
1 | package com.seok.gfd.views
2 |
3 | import android.os.Bundle
4 | import android.text.Editable
5 | import android.text.TextWatcher
6 | import android.view.WindowManager
7 | import android.view.animation.AnimationUtils
8 | import androidx.appcompat.app.AppCompatActivity
9 | import androidx.lifecycle.Observer
10 | import androidx.lifecycle.ViewModelProviders
11 | import androidx.recyclerview.widget.DividerItemDecoration
12 | import androidx.recyclerview.widget.GridLayoutManager
13 | import androidx.recyclerview.widget.LinearLayoutManager
14 | import com.google.android.material.snackbar.Snackbar
15 | import com.seok.gfd.R
16 | import com.seok.gfd.adapter.GithubIdAdapter
17 | import com.seok.gfd.room.entity.GithubId
18 | import com.seok.gfd.utils.ValidationCheck
19 | import com.seok.gfd.viewmodel.GithubIdViewModel
20 | import kotlinx.android.synthetic.main.activity_search.*
21 |
22 |
23 | class SearchActivity : AppCompatActivity() {
24 | private lateinit var githubIdsViewModel: GithubIdViewModel
25 | private lateinit var gridLayoutManager: GridLayoutManager
26 | private lateinit var githubIdAdapter: GithubIdAdapter
27 | private lateinit var githubIds: ArrayList
28 |
29 | override fun onCreate(savedInstanceState: Bundle?) {
30 | super.onCreate(savedInstanceState)
31 | setContentView(R.layout.activity_search)
32 | window.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE)
33 |
34 | // 초기 설정
35 | init()
36 | initViewModel()
37 | setAnimation()
38 | setListener()
39 | }
40 |
41 | private fun setListener() {
42 | search_edt_id.addTextChangedListener(object : TextWatcher {
43 | override fun onTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {
44 | githubIdsViewModel.getGithubId(search_edt_id.text.toString())
45 | }
46 |
47 | override fun beforeTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {
48 | }
49 |
50 | override fun afterTextChanged(p0: Editable?) {
51 | }
52 | })
53 |
54 | search_btn_ok.setOnClickListener {
55 | val githubId = search_edt_id.text.toString()
56 | if(ValidationCheck.validIsEmptyString(githubId)){
57 | Snackbar.make(search_main_layout, "아이디칸이 비어있네요!", Snackbar.LENGTH_SHORT).show()
58 | }else{
59 | if(ValidationCheck.isExistSite(githubId)){
60 | val githubIdDto = GithubId(githubId)
61 | githubIdsViewModel.insertGithubId(githubIdDto)
62 | }else{
63 | Snackbar.make(search_main_layout, "존재하지 않는 아이디 같아요.\n문제가 생겼다면 개발자에게 문의해주세요!", Snackbar.LENGTH_SHORT).show()
64 | }
65 | }
66 | }
67 |
68 | // githubIdsViewModel.closeDatabase() db 컨넥션 끊기
69 | }
70 |
71 | private fun init() {
72 | githubIds = ArrayList()
73 | githubIdAdapter = GithubIdAdapter(githubIds, search_edt_id)
74 | gridLayoutManager = GridLayoutManager(this, 1)
75 | search_recycler_view.layoutManager = gridLayoutManager
76 | search_recycler_view.adapter = githubIdAdapter
77 | search_recycler_view.addItemDecoration(
78 | DividerItemDecoration(
79 | this,
80 | LinearLayoutManager.VERTICAL
81 | )
82 | )
83 | }
84 |
85 | private fun initViewModel() {
86 | githubIdsViewModel = ViewModelProviders.of(this).get(GithubIdViewModel::class.java)
87 | githubIdsViewModel.githubIds.observe(this, Observer {
88 | githubIds.clear()
89 | githubIds.addAll(it)
90 | githubIdAdapter.notifyDataSetChanged()
91 | })
92 | // 전체 검색해놓은 것 가져오기
93 | githubIdsViewModel.getGithubId("")
94 | }
95 |
96 | private fun setAnimation() {
97 | val bottomToTop = AnimationUtils.loadAnimation(this, R.anim.bottom_to_top)
98 | search_txt_info2.startAnimation(bottomToTop)
99 | val topToBottom = AnimationUtils.loadAnimation(this, R.anim.top_to_bottom)
100 | topToBottom.startOffset = 300
101 | search_txt_info1.startAnimation(topToBottom)
102 | val leftToRight = AnimationUtils.loadAnimation(this, R.anim.left_to_right)
103 | leftToRight.startOffset = 800
104 | search_layout_id.startAnimation(leftToRight)
105 | val rightToLeft = AnimationUtils.loadAnimation(this, R.anim.right_to_left)
106 | rightToLeft.startOffset = 1000
107 | search_recycler_view.startAnimation(rightToLeft)
108 | }
109 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/seok/gfd/adapter/CommitsAdapter.kt:
--------------------------------------------------------------------------------
1 | package com.seok.gfd.adapter
2 |
3 | import android.util.Log
4 | import android.view.LayoutInflater
5 | import android.view.View
6 | import android.view.ViewGroup
7 | import androidx.annotation.LayoutRes
8 | import androidx.recyclerview.widget.RecyclerView
9 | import com.bumptech.glide.Glide
10 | import com.bumptech.glide.request.RequestOptions
11 | import com.seok.gfd.R
12 | import com.seok.gfd.retrofit.domain.resopnse.CommitResponse
13 | import kotlinx.android.synthetic.main.rv_rank_item.view.*
14 |
15 | class CommitsAdapter(private val commit: ArrayList) :
16 | RecyclerView.Adapter() {
17 | private var index = 1
18 | override fun getItemCount() = commit.size
19 | override fun onCreateViewHolder(
20 | parent: ViewGroup,
21 | viewType: Int
22 | ): CommitsHolder {
23 | val inflatedView = parent.inflate(R.layout.rv_rank_item, false)
24 | return CommitsHolder(inflatedView)
25 | }
26 |
27 | fun ViewGroup.inflate(@LayoutRes layoutRes: Int, attachToRoot: Boolean = false): View {
28 | return LayoutInflater.from(context).inflate(layoutRes, this, attachToRoot)
29 | }
30 |
31 | override fun onBindViewHolder(holder: CommitsHolder, position: Int) {
32 | val itemCommit = commit[position]
33 | holder.bindCommit(itemCommit)
34 | }
35 |
36 | class CommitsHolder(v: View) : RecyclerView.ViewHolder(v), View.OnClickListener {
37 | private var view: View = v
38 | private var commit: CommitResponse? = null
39 | init {
40 | v.setOnClickListener(this)
41 | }
42 |
43 | override fun onClick(v: View) {
44 | Log.d("RecyclerView", v.id.toString())
45 | }
46 |
47 | companion object {
48 | private val COMMIT_KEY = "COMMIT"
49 | }
50 |
51 | fun bindCommit(commitResponse: CommitResponse) {
52 | this.commit = commitResponse
53 | view.tv_rank_commit.text = commitResponse.data_count.toString()
54 | view.tv_rv_rank_username.text = commitResponse.user_id
55 | Glide.with(view).load(commitResponse.user_image).apply(RequestOptions.circleCropTransform()).into(view.img_rv_user_profile)
56 | // view.tv_rv_rank_username.text = commitResponse.uid
57 | // view.tv_rv_rank_num.text = rank
58 | // Glide.with(view).load(commitResponse.profile_image)
59 | // .apply(RequestOptions.circleCropTransform()).into(view.img_rv_user_profile)
60 | }
61 | }
62 |
63 | }
64 | //class CommitsAdapter(private val application: Application, private val items: List, private val fragment: Fragment) :
65 | // RecyclerView.Adapter() {
66 | //
67 | // private lateinit var sharedPreferencesForUser: SharedPreferencesForUser
68 | //
69 | // override fun getItemCount() = items.size
70 | //
71 | // override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): TRCommitRankViewHolder {
72 | // sharedPreferencesForUser = SharedPreferencesForUser(application)
73 | // val inflatedView = LayoutInflater.from(parent.context).inflate(R.layout.rv_rank_item, parent, false)
74 | // return TRCommitRankViewHolder(inflatedView)
75 | // }
76 | //
77 | // override fun onBindViewHolder(holder: TRCommitRankViewHolder, position: Int) {
78 | // val item = items[position]
79 | // val rank = (position+1).toString()
80 | // if(item.uid == sharedPreferencesForUser.getValue(application.getString(R.string.user_id))){
81 | // fragment.tv_rv_rank.text = rank
82 | // fragment.tv_rv_commit.text = item.data_count.toString()
83 | // Glide.with(fragment).load(item.profile_image).apply(RequestOptions.circleCropTransform()).into(fragment.img_rv_profile)
84 | // }
85 | // holder.apply {
86 | // bind(rank, item, sharedPreferencesForUser.getValue(application.getString(R.string.user_id)))
87 | // itemView.tag = item
88 | // }
89 | // }
90 | //
91 | // class TRCommitRankViewHolder(private var view: View) : RecyclerView.ViewHolder(view) {
92 | // fun bind(rank: String, item: TRCommitResponseDto, userId: String) {
93 | // view.tv_rv_rank_num.text = rank
94 | // view.tv_rv_rank_username.text = item.uid
95 | // view.tv_rank_commit.text = item.data_count.toString()
96 | // Glide.with(view.context).load(item.profile_image).apply(RequestOptions.circleCropTransform()).into(view.img_rv_user_profile)
97 | // if(item.uid == userId){
98 | // view.layout_rv_back.setBackgroundResource(R.color.nonCommit)
99 | // }
100 | // }
101 | // }
102 | //}
--------------------------------------------------------------------------------
/.idea/codeStyles/Project.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 |
26 |
27 |
28 |
29 | xmlns:android
30 |
31 | ^$
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 | xmlns:.*
41 |
42 | ^$
43 |
44 |
45 | BY_NAME
46 |
47 |
48 |
49 |
50 |
51 |
52 | .*:id
53 |
54 | http://schemas.android.com/apk/res/android
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 | .*:name
64 |
65 | http://schemas.android.com/apk/res/android
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 | name
75 |
76 | ^$
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 | style
86 |
87 | ^$
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 | .*
97 |
98 | ^$
99 |
100 |
101 | BY_NAME
102 |
103 |
104 |
105 |
106 |
107 |
108 | .*
109 |
110 | http://schemas.android.com/apk/res/android
111 |
112 |
113 | ANDROID_ATTRIBUTE_ORDER
114 |
115 |
116 |
117 |
118 |
119 |
120 | .*
121 |
122 | .*
123 |
124 |
125 | BY_NAME
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 |
135 |
--------------------------------------------------------------------------------
/app/src/main/java/com/seok/gfd/v1/views/GuestMain.kt:
--------------------------------------------------------------------------------
1 | package com.seok.gfd.v1.views
2 |
3 | import android.os.Bundle
4 | import android.view.View
5 | import androidx.appcompat.app.AppCompatActivity
6 | import androidx.lifecycle.Observer
7 | import androidx.lifecycle.ViewModelProviders
8 | import androidx.recyclerview.widget.DividerItemDecoration
9 | import androidx.recyclerview.widget.LinearLayoutManager
10 | import com.google.android.gms.ads.AdRequest
11 | import com.google.android.gms.ads.MobileAds
12 | import com.seok.gfd.R
13 | import com.seok.gfd.adapter.ContributionsAdapter
14 | import com.seok.gfd.retrofit.domain.resopnse.CommitsResponseDto
15 | import com.seok.gfd.utils.Contribution
16 | import com.seok.gfd.viewmodel.GithubContributionViewModel
17 | import kotlinx.android.synthetic.main.activity_guest_main.*
18 |
19 | class GuestMain : AppCompatActivity() {
20 | private lateinit var githubContributionViewModel: GithubContributionViewModel
21 |
22 | private var contributionList = ArrayList()
23 |
24 |
25 | override fun onCreate(savedInstanceState: Bundle?) {
26 | super.onCreate(savedInstanceState)
27 | setContentView(R.layout.activity_guest_main)
28 |
29 | ads()
30 | init()
31 | initViewModelFun()
32 | }
33 |
34 | private fun refreshContributionsData() {
35 | contributionList = ArrayList()
36 | val userName = guest_main_search_edit.text.toString()
37 | githubContributionViewModel.getContributions(userName)
38 | val customAdapter = ContributionsAdapter(contributionList, this)
39 | recyclerView.adapter = customAdapter
40 | }
41 |
42 | private fun init() {
43 | githubContributionViewModel =
44 | ViewModelProviders.of(this).get(GithubContributionViewModel::class.java)
45 | val linearLayoutManager = LinearLayoutManager(applicationContext)
46 | recyclerView.layoutManager = linearLayoutManager
47 | recyclerView.addItemDecoration(DividerItemDecoration(this, 1))
48 | simpleSwipeRefreshLayout.setOnRefreshListener {
49 | simpleSwipeRefreshLayout.isRefreshing = false
50 | refreshContributionsData()
51 | }
52 | guest_main_search_btn.setOnClickListener {
53 | contributionList = ArrayList()
54 | guest_main_recycler_layout.visibility = View.VISIBLE
55 | val userName = guest_main_search_edit.text.toString()
56 | githubContributionViewModel.getContributions(userName)
57 | }
58 | }
59 |
60 | private fun initViewModelFun() {
61 | githubContributionViewModel.contributions.observe(this, Observer {
62 | if (it != null) {
63 | if (it.years?.size != 0) {
64 | val helloName =
65 | String.format("@%s on Github", guest_main_search_edit.text.toString())
66 | guest_main_tv_username.text = helloName
67 | getContributions(it)
68 | val customAdapter = ContributionsAdapter(contributionList, this)
69 | recyclerView.adapter = customAdapter
70 | guest_main_recycler_layout.visibility = View.INVISIBLE
71 | } else {
72 | guest_main_tv_username.text = "찾지 못했습니다."
73 | }
74 | } else {
75 | guest_main_tv_username.text = "찾지 못했습니다."
76 | }
77 | guest_main_tv_username.visibility = View.VISIBLE
78 | })
79 | }
80 |
81 | private fun ads() {
82 | MobileAds.initialize(
83 | this.application,
84 | getString(R.string.admob_app_id)
85 | )
86 | val adRequest = AdRequest.Builder().build()
87 | guest_login_ads_view.loadAd(adRequest)
88 | }
89 |
90 |
91 | private fun getContributions(commitsResponseDto: CommitsResponseDto) {
92 | for (year in commitsResponseDto.years!!) {
93 | var contribution = Contribution()
94 | contribution.year = year.year
95 | contribution.total = year.total
96 | var contributionList = ArrayList()
97 | for (index in commitsResponseDto.contributions!!.size - 1 downTo 0) {
98 | val commit = commitsResponseDto.contributions!![index]
99 | if (commit.date.contains(year.year)) {
100 | val contributionInfo = Contribution.ContributionInfo(
101 | commit.date,
102 | commit.count,
103 | commit.color,
104 | commit.intensity
105 | )
106 | contributionList.add(contributionInfo)
107 | }
108 | }
109 | contribution.list = contributionList
110 | this.contributionList.add(contribution)
111 | }
112 | }
113 | }
114 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/gfd_logo_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
10 |
12 |
14 |
16 |
18 |
20 |
22 |
24 |
26 |
28 |
30 |
32 |
34 |
36 |
38 |
40 |
42 |
44 |
46 |
48 |
50 |
52 |
54 |
56 |
58 |
60 |
62 |
64 |
66 |
68 |
70 |
72 |
74 |
75 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_launcher_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
10 |
12 |
14 |
16 |
18 |
20 |
22 |
24 |
26 |
28 |
30 |
32 |
34 |
36 |
38 |
40 |
42 |
44 |
46 |
48 |
50 |
52 |
54 |
56 |
58 |
60 |
62 |
64 |
66 |
68 |
70 |
72 |
74 |
75 |
--------------------------------------------------------------------------------
/app/build.gradle:
--------------------------------------------------------------------------------
1 | //noinspection LifecycleAnnotationProcessorWithJava8
2 | apply plugin: 'com.android.application'
3 | apply plugin: 'kotlin-android'
4 | apply plugin: 'kotlin-kapt'
5 | apply plugin: 'kotlin-android-extensions'
6 | // https://blog.yena.io/studynote/2018/09/08/Android-Kotlin-Room.html
7 | // https://kotlinlang.org/docs/reference/using-gradle.html
8 |
9 | android {
10 | compileSdkVersion 28
11 | defaultConfig {
12 | applicationId "com.seok.gfd"
13 | minSdkVersion 26
14 | targetSdkVersion 28
15 | versionCode 10
16 | versionName "3.20.12"
17 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
18 | }
19 | buildTypes {
20 | release {
21 | minifyEnabled false
22 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
23 | testCoverageEnabled false
24 | }
25 | debug {
26 | testCoverageEnabled true
27 | }
28 | }
29 | compileOptions {
30 | sourceCompatibility JavaVersion.VERSION_1_8
31 | targetCompatibility JavaVersion.VERSION_1_8
32 | }
33 | testOptions {
34 | unitTests {
35 | includeAndroidResources = true
36 | returnDefaultValues = true
37 | }
38 | }
39 | lintOptions {
40 | abortOnError false
41 | disable 'GradleDependency' // noinspection GradleDependency 경고 비활성화
42 | }
43 | buildToolsVersion '28.0.3'
44 | ndkVersion '20.1.5948944'
45 | }
46 |
47 | dependencies {
48 | implementation fileTree(dir: 'libs', include: ['*.jar'])
49 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
50 | implementation 'com.google.android.material:material:1.3.0-beta01'
51 | implementation 'androidx.appcompat:appcompat:1.2.0'
52 | implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
53 | implementation "androidx.recyclerview:recyclerview:1.1.0"
54 |
55 | // 머티리얼 디자인 라이브러리
56 | implementation 'com.rengwuxian.materialedittext:library:2.1.4'
57 |
58 | // room
59 | implementation "androidx.room:room-runtime:$room_version"
60 | implementation "androidx.room:room-ktx:$room_version"
61 | kapt "androidx.room:room-compiler:$room_version"
62 | androidTestImplementation "androidx.room:room-testing:$room_version"
63 |
64 | // jsoup
65 | implementation 'org.jsoup:jsoup:1.13.1'
66 |
67 | // test implementation
68 | testImplementation 'junit:junit:4.12'
69 | androidTestImplementation 'androidx.test:runner:1.2.0'
70 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
71 | androidTestImplementation 'androidx.test:rules:1.2.0'
72 | androidTestImplementation 'androidx.test:core:1.2.0'
73 | androidTestImplementation 'androidx.test.ext:junit:1.1.1'
74 | androidTestImplementation 'androidx.test.espresso:espresso-intents:3.2.0'
75 | // implementation 'androidx.legacy:legacy-support-v4:1.0.0'
76 | // testImplementation 'org.robolectric:robolectric:4.3'
77 | // testImplementation 'org.powermock:powermock-api-mockito:1.4.12'
78 | // testImplementation 'org.powermock:powermock-module-junit4:1.6.2'
79 | // testImplementation 'androidx.arch.core:core-testing:2.1.0'
80 |
81 |
82 | // Google Ads
83 | implementation 'com.google.android.gms:play-services-ads:18.2.0'
84 |
85 | // OkHttp && logger
86 | implementation 'com.squareup.okhttp3:okhttp:3.12.0'
87 | implementation 'com.squareup.okhttp3:logging-interceptor:3.4.2'
88 |
89 | // Retrofit2
90 | implementation 'com.squareup.retrofit2:retrofit:2.6.1'
91 | implementation 'com.squareup.retrofit2:converter-gson:2.4.0'
92 | implementation 'com.google.code.gson:gson:2.8.5'
93 |
94 | // Lifecycle components
95 | implementation 'androidx.lifecycle:lifecycle-extensions:2.1.0'
96 | implementation 'android.arch.lifecycle:viewmodel:1.1.1'
97 | annotationProcessor 'androidx.lifecycle:lifecycle-compiler:2.1.0'
98 | implementation 'android.arch.lifecycle:extensions:1.0.0';
99 | //noinspection LifecycleAnnotationProcessorWithJava8
100 | annotationProcessor 'android.arch.lifecycle:compiler:1.0.0';
101 |
102 | // Coroutines
103 | implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutine_version"
104 | implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutine_version"
105 |
106 | // Using the Glide Library for Image loading
107 | implementation 'com.github.bumptech.glide:glide:4.9.0'
108 | kapt 'com.github.bumptech.glide:compiler:4.9.0'
109 |
110 | // jsoup, anko
111 | // implementation "org.jetbrains.anko:anko:$rootProject.anko_version"
112 | // implementation 'androidx.gridlayout:gridlayout:1.0.0'
113 |
114 | // lombok
115 | compileOnly 'org.projectlombok:lombok:1.18.8'
116 | annotationProcessor 'org.projectlombok:lombok:1.18.8'
117 |
118 | // ProgressBar
119 | implementation 'com.wang.avi:library:2.1.3'
120 |
121 | // NavigationBar
122 | implementation 'androidx.navigation:navigation-fragment-ktx:2.1.0'
123 | implementation 'androidx.navigation:navigation-ui-ktx:2.1.0'
124 |
125 | // UI Scale
126 | implementation 'com.ssomai:android.scalablelayout:2.1.6'
127 |
128 | // Tab Layout
129 | implementation 'com.ogaclejapan.smarttablayout:library:2.0.0@aar'
130 | implementation 'com.ogaclejapan.smarttablayout:utils-v4:2.0.0@aar'
131 | }
132 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/fragment_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
10 |
17 |
18 |
27 |
28 |
29 |
41 |
42 |
54 |
55 |
64 |
65 |
76 |
77 |
78 |
84 |
116 |
117 |
125 |
126 |
--------------------------------------------------------------------------------
/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/java/com/seok/gfd/viewmodel/UserViewModel.kt:
--------------------------------------------------------------------------------
1 | package com.seok.gfd.viewmodel
2 |
3 | import android.os.Build
4 | import android.util.Log
5 | import androidx.annotation.RequiresApi
6 | import androidx.lifecycle.LiveData
7 | import androidx.lifecycle.MutableLiveData
8 | import androidx.lifecycle.ViewModel
9 | import com.seok.gfd.BuildConfig
10 | import com.seok.gfd.retrofit.RetrofitClient
11 | import com.seok.gfd.retrofit.domain.*
12 | import com.seok.gfd.retrofit.domain.request.CommitRequestDto
13 | import com.seok.gfd.retrofit.domain.resopnse.CommitResponse
14 | import retrofit2.Call
15 | import retrofit2.Response
16 | import java.net.HttpURLConnection
17 | import java.time.LocalDate
18 |
19 | class UserViewModel : ViewModel() {
20 | private val _usersCount = MutableLiveData()
21 | private val _accessToken = MutableLiveData()
22 | private val _code = MutableLiveData()
23 | private val _userInfo = MutableLiveData()
24 | private val _commitList = MutableLiveData>()
25 |
26 | val userCount: LiveData
27 | get() = _usersCount
28 | val accessToken: LiveData
29 | get() = _accessToken
30 | val code: LiveData
31 | get() = _code
32 | val userInfo: LiveData
33 | get() = _userInfo
34 | val commitList: LiveData>
35 | get() = _commitList
36 |
37 | // User 인원 수 가져오기
38 | fun getUsersCount() {
39 | val userService = RetrofitClient.userService()
40 | val userCall = userService.getUsersCount("")
41 | userCall.enqueue(object : retrofit2.Callback> {
42 | override fun onResponse(
43 | call: Call>,
44 | response: Response>
45 | ) {
46 | _usersCount.value = response.body()?.data
47 | }
48 |
49 | override fun onFailure(call: Call>, t: Throwable) {
50 | Log.e(this.javaClass.simpleName, t.message.toString())
51 | }
52 | })
53 | }
54 |
55 | // Access Token 가져오기
56 | fun getAccessTokenFromGithubApi(code: String) {
57 | val githubAuthService = RetrofitClient.githubAuthService()
58 | val githubAuthCall = githubAuthService.getAccessTokenFromGithubApi(
59 | "",
60 | "",
61 | code
62 | )
63 | githubAuthCall.enqueue(object : retrofit2.Callback {
64 | override fun onResponse(call: Call, response: Response) {
65 | _accessToken.value = response.body()?.access_token
66 | }
67 |
68 | override fun onFailure(call: Call, t: Throwable) {
69 | Log.e(this.javaClass.simpleName, t.message.toString())
70 | }
71 | })
72 | }
73 |
74 | // Github 로그인 및 User 정보 가져오기
75 | fun getUserInfoAndSignInGithub(accessToken: String) {
76 | val githubApiService = RetrofitClient.githubApiService()
77 | val githubApiCall = githubApiService.getUserInfoFromGithubApi("token $accessToken")
78 | githubApiCall.enqueue(object : retrofit2.Callback {
79 | override fun onResponse(call: Call, response: Response) {
80 | _code.value = response.code()
81 | if (response.code() == HttpURLConnection.HTTP_OK) {
82 | _userInfo.value = response.body()
83 | }
84 | }
85 |
86 | override fun onFailure(call: Call, t: Throwable) {
87 | Log.e(this.javaClass.simpleName, t.message.toString())
88 | }
89 | })
90 | }
91 |
92 | fun signInUserInfo(user: User){
93 | val gfdSignInService = RetrofitClient.userService()
94 | val requestUserDto = GfdUser(user.login, user.html_url, user.avatar_url)
95 | val gfdSignInCall = gfdSignInService.signUpUser("", requestUserDto)
96 | gfdSignInCall.enqueue(object : retrofit2.Callback>{
97 | override fun onResponse(
98 | call: Call>,
99 | response: Response>
100 | ) {
101 | response
102 | }
103 | override fun onFailure(call: Call>, t: Throwable) {
104 | Log.e(this.javaClass.simpleName, t.message.toString())
105 | }
106 |
107 | })
108 | }
109 |
110 | // 금일 커밋 랭킹 가져오기
111 | @RequiresApi(Build.VERSION_CODES.O)
112 | fun getCommitsRank() {
113 | val dataDate = LocalDate.now().toString()
114 | val getCommitsService = RetrofitClient.commitService()
115 | val getCommitCall = getCommitsService.getCommitList("", dataDate)
116 | getCommitCall.enqueue(object : retrofit2.Callback> {
117 | override fun onResponse(call: Call>, response: Response>) {
118 | _commitList.value = response.body()?.list
119 | }
120 |
121 | override fun onFailure(call: Call>, t: Throwable) {
122 | Log.e(this.javaClass.simpleName, t.message.toString())
123 | }
124 |
125 | })
126 | }
127 |
128 | // 금일 커밋 등록하기
129 | fun enrollCommit(commit: CommitRequestDto){
130 | val enrollCommitService = RetrofitClient.commitService()
131 | val enrollCommitCall = enrollCommitService.enrollCommit("", commit)
132 | enrollCommitCall.enqueue(object : retrofit2.Callback>{
133 | override fun onResponse(
134 | call: Call>,
135 | response: Response>
136 | ) {
137 |
138 | }
139 |
140 | override fun onFailure(call: Call>, t: Throwable) {
141 |
142 | }
143 | })
144 | }
145 | }
--------------------------------------------------------------------------------
/app/src/main/res/layout/fragment_rank.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
13 |
14 |
19 |
20 |
30 |
31 |
40 |
41 |
54 |
55 |
67 |
68 |
76 |
77 |
88 |
89 |
98 |
99 |
100 |
101 |
105 |
106 |
116 |
117 |
118 |
119 |
120 |
--------------------------------------------------------------------------------