Browse Source

initial push

Signed-off-by: M M Arif <mmarif@swatian.com>
tags/1.5.0
M M Arif 3 weeks ago
parent
commit
c3563dc861
200 changed files with 21227 additions and 622 deletions
  1. 187
    0
      .gitignore
  2. 2
    0
      CHANGELOG.md
  3. 31
    0
      CONTRIBUTING.md
  4. 668
    620
      LICENSE
  5. 70
    2
      README.md
  6. 1
    0
      app/.gitignore
  7. 67
    0
      app/build.gradle
  8. 21
    0
      app/proguard-rules.pro
  9. 61
    0
      app/src/main/AndroidManifest.xml
  10. BIN
      app/src/main/app_logo-web.png
  11. 161
    0
      app/src/main/java/org/mian/gitnex/actions/CollaboratorActions.java
  12. 164
    0
      app/src/main/java/org/mian/gitnex/actions/IssueActions.java
  13. 144
    0
      app/src/main/java/org/mian/gitnex/activities/AddCollaboratorToRepositoryActivity.java
  14. 290
    0
      app/src/main/java/org/mian/gitnex/activities/AddRemoveAssigneesActivity.java
  15. 303
    0
      app/src/main/java/org/mian/gitnex/activities/AddRemoveLabelsActivity.java
  16. 200
    0
      app/src/main/java/org/mian/gitnex/activities/AdminGetUsersActivity.java
  17. 575
    0
      app/src/main/java/org/mian/gitnex/activities/CreateIssueActivity.java
  18. 435
    0
      app/src/main/java/org/mian/gitnex/activities/CreateLabelActivity.java
  19. 226
    0
      app/src/main/java/org/mian/gitnex/activities/CreateNewUserActivity.java
  20. 310
    0
      app/src/main/java/org/mian/gitnex/activities/CreateReleaseActivity.java
  21. 378
    0
      app/src/main/java/org/mian/gitnex/activities/CreateTeamByOrgActivity.java
  22. 43
    0
      app/src/main/java/org/mian/gitnex/activities/CreditsActivity.java
  23. 367
    0
      app/src/main/java/org/mian/gitnex/activities/EditIssueActivity.java
  24. 501
    0
      app/src/main/java/org/mian/gitnex/activities/IssueDetailActivity.java
  25. 436
    0
      app/src/main/java/org/mian/gitnex/activities/LoginActivity.java
  26. 411
    0
      app/src/main/java/org/mian/gitnex/activities/MainActivity.java
  27. 242
    0
      app/src/main/java/org/mian/gitnex/activities/NewMilestoneActivity.java
  28. 220
    0
      app/src/main/java/org/mian/gitnex/activities/NewOrganizationActivity.java
  29. 314
    0
      app/src/main/java/org/mian/gitnex/activities/NewRepoActivity.java
  30. 134
    0
      app/src/main/java/org/mian/gitnex/activities/OrgDetailActivity.java
  31. 102
    0
      app/src/main/java/org/mian/gitnex/activities/OrgTeamMembersActivity.java
  32. 208
    0
      app/src/main/java/org/mian/gitnex/activities/ProfileEmailActivity.java
  33. 278
    0
      app/src/main/java/org/mian/gitnex/activities/ReplyToIssueActivity.java
  34. 245
    0
      app/src/main/java/org/mian/gitnex/activities/RepoDetailActivity.java
  35. 37
    0
      app/src/main/java/org/mian/gitnex/activities/SponsorsActivity.java
  36. 145
    0
      app/src/main/java/org/mian/gitnex/adapters/AdminGetUsersAdapter.java
  37. 80
    0
      app/src/main/java/org/mian/gitnex/adapters/BranchesAdapter.java
  38. 343
    0
      app/src/main/java/org/mian/gitnex/adapters/ClosedIssuesAdapter.java
  39. 91
    0
      app/src/main/java/org/mian/gitnex/adapters/CollaboratorsAdapter.java
  40. 244
    0
      app/src/main/java/org/mian/gitnex/adapters/IssueCommentsAdapter.java
  41. 343
    0
      app/src/main/java/org/mian/gitnex/adapters/IssuesAdapter.java
  42. 177
    0
      app/src/main/java/org/mian/gitnex/adapters/LabelsAdapter.java
  43. 133
    0
      app/src/main/java/org/mian/gitnex/adapters/MembersByOrgAdapter.java
  44. 265
    0
      app/src/main/java/org/mian/gitnex/adapters/MilestonesAdapter.java
  45. 190
    0
      app/src/main/java/org/mian/gitnex/adapters/MutliSelectAdapter.java
  46. 168
    0
      app/src/main/java/org/mian/gitnex/adapters/MyReposListAdapter.java
  47. 133
    0
      app/src/main/java/org/mian/gitnex/adapters/OrganizationsListAdapter.java
  48. 80
    0
      app/src/main/java/org/mian/gitnex/adapters/ProfileEmailsAdapter.java
  49. 78
    0
      app/src/main/java/org/mian/gitnex/adapters/ProfileFollowersAdapter.java
  50. 77
    0
      app/src/main/java/org/mian/gitnex/adapters/ProfileFollowingAdapter.java
  51. 152
    0
      app/src/main/java/org/mian/gitnex/adapters/ReleasesAdapter.java
  52. 169
    0
      app/src/main/java/org/mian/gitnex/adapters/ReposListAdapter.java
  53. 170
    0
      app/src/main/java/org/mian/gitnex/adapters/RepositoriesByOrgAdapter.java
  54. 170
    0
      app/src/main/java/org/mian/gitnex/adapters/StarredReposListAdapter.java
  55. 91
    0
      app/src/main/java/org/mian/gitnex/adapters/TeamMembersByOrgAdapter.java
  56. 133
    0
      app/src/main/java/org/mian/gitnex/adapters/TeamsByOrgAdapter.java
  57. 207
    0
      app/src/main/java/org/mian/gitnex/adapters/UserSearchAdapter.java
  58. 33
    0
      app/src/main/java/org/mian/gitnex/clients/IssuesService.java
  59. 44
    0
      app/src/main/java/org/mian/gitnex/clients/RetrofitClient.java
  60. 188
    0
      app/src/main/java/org/mian/gitnex/fragments/AboutFragment.java
  61. 56
    0
      app/src/main/java/org/mian/gitnex/fragments/AdminUsersBottomSheetFragment.java
  62. 149
    0
      app/src/main/java/org/mian/gitnex/fragments/BranchesFragment.java
  63. 290
    0
      app/src/main/java/org/mian/gitnex/fragments/ClosedIssuesFragment.java
  64. 117
    0
      app/src/main/java/org/mian/gitnex/fragments/CollaboratorsFragment.java
  65. 289
    0
      app/src/main/java/org/mian/gitnex/fragments/IssuesFragment.java
  66. 166
    0
      app/src/main/java/org/mian/gitnex/fragments/LabelsFragment.java
  67. 150
    0
      app/src/main/java/org/mian/gitnex/fragments/MembersByOrgFragment.java
  68. 205
    0
      app/src/main/java/org/mian/gitnex/fragments/MilestonesFragment.java
  69. 241
    0
      app/src/main/java/org/mian/gitnex/fragments/MyRepositoriesFragment.java
  70. 39
    0
      app/src/main/java/org/mian/gitnex/fragments/NavSubMenuBottomSheetFragment.java
  71. 56
    0
      app/src/main/java/org/mian/gitnex/fragments/OrgBottomSheetFragment.java
  72. 146
    0
      app/src/main/java/org/mian/gitnex/fragments/OrganizationInfoFragment.java
  73. 196
    0
      app/src/main/java/org/mian/gitnex/fragments/OrganizationsFragment.java
  74. 39
    0
      app/src/main/java/org/mian/gitnex/fragments/ProfileBottomSheetFragment.java
  75. 148
    0
      app/src/main/java/org/mian/gitnex/fragments/ProfileEmailsFragment.java
  76. 147
    0
      app/src/main/java/org/mian/gitnex/fragments/ProfileFollowersFragment.java
  77. 147
    0
      app/src/main/java/org/mian/gitnex/fragments/ProfileFollowingFragment.java
  78. 125
    0
      app/src/main/java/org/mian/gitnex/fragments/ProfileFragment.java
  79. 165
    0
      app/src/main/java/org/mian/gitnex/fragments/ReleasesFragment.java
  80. 92
    0
      app/src/main/java/org/mian/gitnex/fragments/RepoBottomSheetFragment.java
  81. 359
    0
      app/src/main/java/org/mian/gitnex/fragments/RepoInfoFragment.java
  82. 193
    0
      app/src/main/java/org/mian/gitnex/fragments/RepositoriesByOrgFragment.java
  83. 199
    0
      app/src/main/java/org/mian/gitnex/fragments/RepositoriesFragment.java
  84. 345
    0
      app/src/main/java/org/mian/gitnex/fragments/SettingsFragment.java
  85. 115
    0
      app/src/main/java/org/mian/gitnex/fragments/SingleIssueBottomSheetFragment.java
  86. 236
    0
      app/src/main/java/org/mian/gitnex/fragments/StarredRepositoriesFragment.java
  87. 194
    0
      app/src/main/java/org/mian/gitnex/fragments/TeamsByOrgFragment.java
  88. 88
    0
      app/src/main/java/org/mian/gitnex/helpers/AlertDialogs.java
  89. 42
    0
      app/src/main/java/org/mian/gitnex/helpers/Authorization.java
  90. 43
    0
      app/src/main/java/org/mian/gitnex/helpers/ClickListener.java
  91. 27
    0
      app/src/main/java/org/mian/gitnex/helpers/ColorInverter.java
  92. 78
    0
      app/src/main/java/org/mian/gitnex/helpers/LabelWidthCalculator.java
  93. 317
    0
      app/src/main/java/org/mian/gitnex/helpers/MultiSelectDialog.java
  94. 77
    0
      app/src/main/java/org/mian/gitnex/helpers/RecyclerViewEmptySupport.java
  95. 49
    0
      app/src/main/java/org/mian/gitnex/helpers/RoundedTransformation.java
  96. 35
    0
      app/src/main/java/org/mian/gitnex/helpers/TimeHelper.java
  97. 31
    0
      app/src/main/java/org/mian/gitnex/helpers/Toasty.java
  98. 42
    0
      app/src/main/java/org/mian/gitnex/helpers/UrlHelper.java
  99. 35
    0
      app/src/main/java/org/mian/gitnex/helpers/UserMentions.java
  100. 207
    0
      app/src/main/java/org/mian/gitnex/interfaces/ApiInterface.java
  101. 17
    0
      app/src/main/java/org/mian/gitnex/models/AddEmail.java
  102. 80
    0
      app/src/main/java/org/mian/gitnex/models/Branches.java
  103. 44
    0
      app/src/main/java/org/mian/gitnex/models/Collaborators.java
  104. 43
    0
      app/src/main/java/org/mian/gitnex/models/CreateIssue.java
  105. 17
    0
      app/src/main/java/org/mian/gitnex/models/CreateLabel.java
  106. 24
    0
      app/src/main/java/org/mian/gitnex/models/Emails.java
  107. 15
    0
      app/src/main/java/org/mian/gitnex/models/GiteaVersion.java
  108. 101
    0
      app/src/main/java/org/mian/gitnex/models/IssueComments.java
  109. 303
    0
      app/src/main/java/org/mian/gitnex/models/Issues.java
  110. 39
    0
      app/src/main/java/org/mian/gitnex/models/Labels.java
  111. 61
    0
      app/src/main/java/org/mian/gitnex/models/Milestones.java
  112. 42
    0
      app/src/main/java/org/mian/gitnex/models/MultiSelectModel.java
  113. 23
    0
      app/src/main/java/org/mian/gitnex/models/OrgOwner.java
  114. 45
    0
      app/src/main/java/org/mian/gitnex/models/Organization.java
  115. 87
    0
      app/src/main/java/org/mian/gitnex/models/OrganizationRepository.java
  116. 14
    0
      app/src/main/java/org/mian/gitnex/models/Permission.java
  117. 170
    0
      app/src/main/java/org/mian/gitnex/models/Releases.java
  118. 39
    0
      app/src/main/java/org/mian/gitnex/models/Teams.java
  119. 17
    0
      app/src/main/java/org/mian/gitnex/models/UpdateIssueAssignee.java
  120. 14
    0
      app/src/main/java/org/mian/gitnex/models/UpdateIssueState.java
  121. 68
    0
      app/src/main/java/org/mian/gitnex/models/UserInfo.java
  122. 42
    0
      app/src/main/java/org/mian/gitnex/models/UserOrganizations.java
  123. 99
    0
      app/src/main/java/org/mian/gitnex/models/UserRepositories.java
  124. 22
    0
      app/src/main/java/org/mian/gitnex/models/UserSearch.java
  125. 32
    0
      app/src/main/java/org/mian/gitnex/models/UserTokens.java
  126. 183
    0
      app/src/main/java/org/mian/gitnex/util/AppUtil.java
  127. 571
    0
      app/src/main/java/org/mian/gitnex/util/TinyDB.java
  128. 86
    0
      app/src/main/java/org/mian/gitnex/viewmodels/AdminGetUsersViewModel.java
  129. 59
    0
      app/src/main/java/org/mian/gitnex/viewmodels/BranchesViewModel.java
  130. 59
    0
      app/src/main/java/org/mian/gitnex/viewmodels/CollaboratorsViewModel.java
  131. 63
    0
      app/src/main/java/org/mian/gitnex/viewmodels/IssueCommentsViewModel.java
  132. 61
    0
      app/src/main/java/org/mian/gitnex/viewmodels/LabelsViewModel.java
  133. 59
    0
      app/src/main/java/org/mian/gitnex/viewmodels/MembersByOrgViewModel.java
  134. 61
    0
      app/src/main/java/org/mian/gitnex/viewmodels/MilestonesViewModel.java
  135. 63
    0
      app/src/main/java/org/mian/gitnex/viewmodels/MyRepositoriesViewModel.java
  136. 63
    0
      app/src/main/java/org/mian/gitnex/viewmodels/OrganizationListViewModel.java
  137. 59
    0
      app/src/main/java/org/mian/gitnex/viewmodels/ProfileEmailsViewModel.java
  138. 59
    0
      app/src/main/java/org/mian/gitnex/viewmodels/ProfileFollowersViewModel.java
  139. 59
    0
      app/src/main/java/org/mian/gitnex/viewmodels/ProfileFollowingViewModel.java
  140. 59
    0
      app/src/main/java/org/mian/gitnex/viewmodels/ReleasesViewModel.java
  141. 61
    0
      app/src/main/java/org/mian/gitnex/viewmodels/RepositoriesByOrgViewModel.java
  142. 63
    0
      app/src/main/java/org/mian/gitnex/viewmodels/RepositoriesListViewModel.java
  143. 61
    0
      app/src/main/java/org/mian/gitnex/viewmodels/StarredRepositoriesViewModel.java
  144. 59
    0
      app/src/main/java/org/mian/gitnex/viewmodels/TeamMembersByOrgViewModel.java
  145. 61
    0
      app/src/main/java/org/mian/gitnex/viewmodels/TeamsByOrgViewModel.java
  146. 8
    0
      app/src/main/res/anim/slide_down.xml
  147. 8
    0
      app/src/main/res/anim/slide_up.xml
  148. 21
    0
      app/src/main/res/drawable-v23/spinner.xml
  149. 34
    0
      app/src/main/res/drawable-v24/ic_launcher_foreground.xml
  150. 46
    0
      app/src/main/res/drawable/app_logo_background.xml
  151. 49
    0
      app/src/main/res/drawable/app_logo_foreground.xml
  152. 14
    0
      app/src/main/res/drawable/badge_background.xml
  153. 10
    0
      app/src/main/res/drawable/border.xml
  154. 5
    0
      app/src/main/res/drawable/circle.xml
  155. 5
    0
      app/src/main/res/drawable/circle_red.xml
  156. 5
    0
      app/src/main/res/drawable/ic_add.xml
  157. 5
    0
      app/src/main/res/drawable/ic_add_person.xml
  158. 5
    0
      app/src/main/res/drawable/ic_android.xml
  159. 5
    0
      app/src/main/res/drawable/ic_arrow_down.xml
  160. 5
    0
      app/src/main/res/drawable/ic_arrow_up.xml
  161. 5
    0
      app/src/main/res/drawable/ic_calendar.xml
  162. 5
    0
      app/src/main/res/drawable/ic_check.xml
  163. 5
    0
      app/src/main/res/drawable/ic_close.xml
  164. 5
    0
      app/src/main/res/drawable/ic_color.xml
  165. 5
    0
      app/src/main/res/drawable/ic_comment.xml
  166. 5
    0
      app/src/main/res/drawable/ic_delete.xml
  167. 5
    0
      app/src/main/res/drawable/ic_done.xml
  168. 5
    0
      app/src/main/res/drawable/ic_dotted_menu.xml
  169. 5
    0
      app/src/main/res/drawable/ic_dotted_menu_horizontal.xml
  170. 5
    0
      app/src/main/res/drawable/ic_edit.xml
  171. 5
    0
      app/src/main/res/drawable/ic_email.xml
  172. 5
    0
      app/src/main/res/drawable/ic_exit_24dp.xml
  173. 5
    0
      app/src/main/res/drawable/ic_home_24dp.xml
  174. 5
    0
      app/src/main/res/drawable/ic_info_24dp.xml
  175. 5
    0
      app/src/main/res/drawable/ic_info_outline_24dp.xml
  176. 5
    0
      app/src/main/res/drawable/ic_issue_closed.xml
  177. 5
    0
      app/src/main/res/drawable/ic_issue_comments.xml
  178. 5
    0
      app/src/main/res/drawable/ic_issue_open.xml
  179. 5
    0
      app/src/main/res/drawable/ic_issue_open_white.xml
  180. 5
    0
      app/src/main/res/drawable/ic_issues.xml
  181. 5
    0
      app/src/main/res/drawable/ic_label.xml
  182. 5
    0
      app/src/main/res/drawable/ic_language.xml
  183. 74
    0
      app/src/main/res/drawable/ic_launcher_background.xml
  184. 5
    0
      app/src/main/res/drawable/ic_link_24dp.xml
  185. 5
    0
      app/src/main/res/drawable/ic_lock_24dp.xml
  186. 5
    0
      app/src/main/res/drawable/ic_lock_bold.xml
  187. 5
    0
      app/src/main/res/drawable/ic_lock_open.xml
  188. 5
    0
      app/src/main/res/drawable/ic_merge.xml
  189. 5
    0
      app/src/main/res/drawable/ic_milestone.xml
  190. 5
    0
      app/src/main/res/drawable/ic_my_repositories.xml
  191. 5
    0
      app/src/main/res/drawable/ic_new_releases.xml
  192. 5
    0
      app/src/main/res/drawable/ic_organizations.xml
  193. 5
    0
      app/src/main/res/drawable/ic_otp.xml
  194. 5
    0
      app/src/main/res/drawable/ic_person_24dp.xml
  195. 5
    0
      app/src/main/res/drawable/ic_person_filled.xml
  196. 5
    0
      app/src/main/res/drawable/ic_public.xml
  197. 5
    0
      app/src/main/res/drawable/ic_remove.xml
  198. 5
    0
      app/src/main/res/drawable/ic_reopen.xml
  199. 5
    0
      app/src/main/res/drawable/ic_reply.xml
  200. 0
    0
      app/src/main/res/drawable/ic_repos.xml

+ 187
- 0
.gitignore View File

@@ -0,0 +1,187 @@
# Created by https://www.gitignore.io/api/android,androidstudio
# Edit at https://www.gitignore.io/?templates=android,androidstudio
### Android ###
# Built application files
*.apk
*.ap_
*.aab
# Files for the ART/Dalvik VM
*.dex
# Java class files
*.class
# Generated files
bin/
gen/
out/
# Gradle files
.gradle/
build/
# Local configuration file (sdk path, etc)
local.properties
# Proguard folder generated by Eclipse
proguard/
# Log Files
*.log
# Android Studio Navigation editor temp files
.navigation/
# Android Studio captures folder
captures/
# IntelliJ
*.iml
.idea/*
.idea/workspace.xml
.idea/tasks.xml
.idea/gradle.xml
.idea/assetWizardSettings.xml
.idea/dictionaries
.idea/libraries
.idea/caches
# Keystore files
# Uncomment the following lines if you do not want to check your keystore files in.
#*.jks
#*.keystore
# External native build folder generated in Android Studio 2.2 and later
.externalNativeBuild
# Google Services (e.g. APIs or Firebase)
google-services.json
# Freeline
freeline.py
freeline/
freeline_project_description.json
# fastlane
fastlane/report.xml
fastlane/Preview.html
fastlane/screenshots
fastlane/test_output
fastlane/readme.md
### Android Patch ###
gen-external-apklibs
### AndroidStudio ###
# Covers files to be ignored for android development using Android Studio.
# Built application files
# Files for the ART/Dalvik VM
# Java class files
# Generated files
# Gradle files
.gradle
# Signing files
.signing/
# Local configuration file (sdk path, etc)
# Proguard folder generated by Eclipse
# Log Files
# Android Studio
/*/build/
/*/local.properties
/*/out
/*/*/build
/*/*/production
*.ipr
*~
*.swp
# Android Patch
# External native build folder generated in Android Studio 2.2 and later
# NDK
obj/
# IntelliJ IDEA
*.iws
/out/
# User-specific configurations
.idea/caches/
.idea/libraries/
.idea/shelf/
.idea/.name
.idea/compiler.xml
.idea/copyright/profiles_settings.xml
.idea/encodings.xml
.idea/misc.xml
.idea/modules.xml
.idea/scopes/scope_settings.xml
.idea/vcs.xml
.idea/jsLibraryMappings.xml
.idea/datasources.xml
.idea/dataSources.ids
.idea/sqlDataSources.xml
.idea/dynamic.xml
.idea/uiDesigner.xml
# OS-specific files
.DS_Store
.DS_Store?
._*
.Spotlight-V100
.Trashes
ehthumbs.db
Thumbs.db
# Legacy Eclipse project files
.classpath
.project
.cproject
.settings/
# Mobile Tools for Java (J2ME)
.mtj.tmp/
# Package Files #
*.war
*.ear
# virtual machine crash logs (Reference: http://www.java.com/en/download/help/error_hotspot.xml)
hs_err_pid*
## Plugin-specific files:
# mpeltonen/sbt-idea plugin
.idea_modules/
# JIRA plugin
atlassian-ide-plugin.xml
# Mongo Explorer plugin
.idea/mongoSettings.xml
# Crashlytics plugin (for Android Studio and IntelliJ)
com_crashlytics_export_strings.xml
crashlytics.properties
crashlytics-build.properties
fabric.properties
### AndroidStudio Patch ###
!/gradle/wrapper/gradle-wrapper.jar
# End of https://www.gitignore.io/api/android,androidstudio

+ 2
- 0
CHANGELOG.md View File

@@ -0,0 +1,2 @@
# Changelog
[Check out the release notes](https://gitlab.com/mmarif4u/gitnex/releases)

+ 31
- 0
CONTRIBUTING.md View File

@@ -0,0 +1,31 @@
# Contributing to GitNex
Please take a few minutes to read this document to make the process of contribution more easy and healthy for all involved.
## Merge Requests
Patches, enhancements, features are always welcome. The MR should focus on the scope of work and avoid many unnecessary commits. Please provide as much detail and context as possible to explain the work submitted.
Please ask if you are not sure about the scope of work to be submitted to avoid waste of time spent on the work.
**How to submit MR**  
Fork this repository. Pull the forked repository from your namespace to your local machine. Create new branch and work on the bug/feature/enhancement you would like to submit. Push it to your forked version. From there create Merge Request(MR) against **master** branch.
**IMPORTANT:** By submitting MR, you agree to allow GitNex to license your work under the same license as that used by GitNex.
## Issues and Reports
*1st of please be polite and gentle while commenting or creating new issue to maintain a healthy environment.*
Before creating an issue please take a moment and search the repository issues(open/closed) to avoid duplicate issues either it's a bug or feature.
In case you want to submit a bug report, please provide as much details as possible to better debug the problem. The important part is how to reproduce the bug and steps to reproduce are appreciated.
**Note:** Please contact the project directly via email(gitnex@swatian.com) if have to share sensitive and security related details.
## Translation
Help us translate GitNex to your native language.
Take a look [here](https://gitlab.com/mmarif4u/gitnex/blob/master/app/src/main/res/values/strings.xml) for strings, please ignore the lines with `translatable="false"`. It is recommended to create a Merge Request with your changes.
Check the structure of other languages for example [French](https://gitlab.com/mmarif4u/gitnex/blob/master/app/src/main/res/values-fr/strings.xml). 
**Note:** Crowdin support will be added soon.

+ 668
- 620
LICENSE
File diff suppressed because it is too large
View File


+ 70
- 2
README.md View File

@@ -1,3 +1,71 @@
# GitNex
[![License: GPL v3](https://img.shields.io/badge/License-GPL%20v3-blue.svg)](https://www.gnu.org/licenses/gpl-3.0)
Android client for Gitea
[<img alt="Become a Patroen" src="https://c5.patreon.com/external/logo/become_a_patron_button@2x.png" height="40"/>](https://www.patreon.com/mmarif) [<img alt="Donate using Liberapay" src="https://liberapay.com/assets/widgets/donate.svg"/>](https://liberapay.com/mmarif/donate)
# GitNex - Android client for Gitea
GitNex is a free, open-source Android client for Git repository management tool Gitea. Gitea is a community managed fork of Gogs, lightweight code hosting solution written in Go.
GitNex is licensed under GPLv3 License. See the LICENSE file for the full license text.
No trackers are used and source code is available here for anyone to audit.
## Downloads
[<img alt='Get it on F-droid' src='https://gitlab.com/fdroid/artwork/raw/master/badge/get-it-on.png' height="80"/>](https://f-droid.org/en/packages/org.mian.gitnex/) [<img alt='Get it on Google Play' src='https://play.google.com/intl/en_us/badges/images/generic/en_badge_web_generic.png' height="80"/>](https://play.google.com/store/apps/details?id=org.mian.gitnex) [Download APK](https://gitlab.com/mmarif4u/gitnex/releases)
## Note about Gitea version
Please make sure that you are on Gitea **1.7.x** stable release or later. Below this may not work as one would expect because of the newly added objects to the API at later versions. Please consider updating your Gitea server.
Check the versions [compatibility page](https://gitlab.com/mmarif4u/gitnex/wikis/Compatibility) which lists all the supported versions with compatibility ratio.
## Build from source
Option 1 - Download the source code, open it in Android Studio and build it there.
Option 2 - Open terminal(Linux) and cd to the project dir. Run `./gradlew build`.
## Features
- My Repositories
- Repositories list
- Organizations list
- Create repository
- Create organization
- Issues list
- [MANY MORE](https://gitlab.com/mmarif4u/gitnex/wikis/Features)
## Contributing
[CONTRIBUTING](https://gitlab.com/mmarif4u/gitnex/blob/master/CONTRIBUTING.md)
## Screenshots:
[Screenshots](https://gitlab.com/mmarif4u/gitnex/wikis/Screenshots)
## FAQ
[Faq](https://gitlab.com/mmarif4u/gitnex/wikis/FAQ)
## Links
[Website](https://gitnex.com)
[Wiki](https://gitlab.com/mmarif4u/gitnex/wikis/home)
[Website Repository](https://gitlab.com/mmarif4u/gitnex-website)
[Troubleshoot Guide](https://gitlab.com/mmarif4u/gitnex/wikis/Troubleshoot-Guide)
[How to](https://gitlab.com/mmarif4u/gitnex/wikis/home#how-to)
## Thanks
Thanks to all the open source libraries, contributors and donators.
Open source libraries
- Retrofit
- Gson
- Okhttp
- ViHtarb/tooltip
- Picasso
- Markwon
- Prettytime
- Amulyakhare/textdrawable
- Vdurmont/emoji-java
- Abumoallim/android-multi-select-dialog
- Pes/materialcolorpicker
- Hendraanggrian/socialview
[Follow me on Fediverse - mastodon.social/@mmarif](https://mastodon.social/@mmarif)

+ 1
- 0
app/.gitignore View File

@@ -0,0 +1 @@
/build

+ 67
- 0
app/build.gradle View File

@@ -0,0 +1,67 @@
apply plugin: 'com.android.application'
android {
    compileSdkVersion 28
    defaultConfig {
        applicationId "org.mian.gitnex"
        minSdkVersion 21
        targetSdkVersion 28
        versionCode 45
        versionName "1.5.0"
        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
    lintOptions {
        //checkReleaseBuilds false
        abortOnError false
    }
}
dependencies {
    def lifecycle_version = "2.2.0-alpha01"
    final def markwon_version = "3.0.0"
    implementation fileTree(include: ['*.jar'], dir: 'libs')
    implementation 'androidx.appcompat:appcompat:1.1.0-alpha05'
    implementation 'com.google.android.material:material:1.1.0-alpha06'
    implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
    implementation 'androidx.legacy:legacy-support-v4:1.0.0'
    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'androidx.test:runner:1.1.1'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.1'
    implementation 'com.github.vihtarb:tooltip:0.2.0'
    implementation 'com.squareup.okhttp3:okhttp:3.12.1'
    implementation 'com.google.code.gson:gson:2.8.5'
    implementation 'com.squareup.picasso:picasso:2.71828'
    implementation 'com.amulyakhare:com.amulyakhare.textdrawable:1.0.1'
    implementation 'com.squareup.retrofit2:retrofit:2.5.0'
    implementation 'com.squareup.retrofit2:converter-gson:2.5.0'
    implementation 'com.squareup.retrofit2:converter-scalars:2.5.0'
    implementation 'com.squareup.okhttp3:logging-interceptor:3.12.1'
    implementation 'org.ocpsoft.prettytime:prettytime:4.0.1.Final'
    implementation "com.vdurmont:emoji-java:4.0.0"
    implementation "com.pes.materialcolorpicker:library:1.2.5"
    implementation "ru.noties.markwon:core:$markwon_version"
    implementation "ru.noties.markwon:ext-strikethrough:$markwon_version"
    implementation "ru.noties.markwon:ext-tables:$markwon_version"
    implementation "ru.noties.markwon:ext-tasklist:$markwon_version"
    implementation "ru.noties.markwon:syntax-highlight:$markwon_version"
    implementation "ru.noties.markwon:image-okhttp:$markwon_version"
    implementation "ru.noties.markwon:html:$markwon_version"
    implementation "ru.noties.markwon:recycler:$markwon_version"
    implementation "ru.noties.markwon:recycler-table:$markwon_version"
    implementation "ru.noties.markwon:image-gif:$markwon_version"
    implementation "ru.noties.markwon:image-svg:$markwon_version"
    implementation "com.hendraanggrian.appcompat:socialview:0.2"
    implementation "com.hendraanggrian.appcompat:socialview-commons:0.2"
    implementation "androidx.lifecycle:lifecycle-extensions:$lifecycle_version"
    implementation "androidx.lifecycle:lifecycle-viewmodel:$lifecycle_version"
}

+ 21
- 0
app/proguard-rules.pro View File

@@ -0,0 +1,21 @@
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
#   http://developer.android.com/guide/developing/tools/proguard.html
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
#   public *;
#}
# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable
# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile

+ 61
- 0
app/src/main/AndroidManifest.xml View File

@@ -0,0 +1,61 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="org.mian.gitnex">
    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
    <application
        android:allowBackup="true"
        android:icon="@mipmap/app_logo"
        android:label="@string/app_name"
        android:networkSecurityConfig="@xml/network_security_config"
        android:roundIcon="@mipmap/app_logo_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name=".activities.AdminGetUsersActivity"></activity>
        <activity
            android:name=".activities.AddRemoveAssigneesActivity"
            android:theme="@style/Theme.AppCompat.Light.Dialog" />
        <activity android:name=".activities.CreateReleaseActivity" />
        <activity android:name=".activities.EditIssueActivity" />
        <activity android:name=".activities.CreateNewUserActivity" />
        <activity
            android:name=".activities.AddRemoveLabelsActivity"
            android:theme="@style/Theme.AppCompat.Light.Dialog" />
        <activity android:name=".activities.ProfileEmailActivity" />
        <activity android:name=".activities.AddCollaboratorToRepositoryActivity" />
        <activity android:name=".activities.CreateTeamByOrgActivity" />
        <activity android:name=".activities.OrgTeamMembersActivity" />
        <activity
            android:name=".activities.OrgDetailActivity"
            android:label="@string/title_activity_org_detail"
            android:theme="@style/AppTheme.NoActionBar" />
        <activity android:name=".activities.SponsorsActivity" />
        <activity android:name=".activities.CreditsActivity" />
        <activity android:name=".activities.CreateLabelActivity" />
        <activity android:name=".activities.CreateIssueActivity" />
        <activity android:name=".activities.NewMilestoneActivity" />
        <activity android:name=".activities.ReplyToIssueActivity" />
        <activity
            android:name=".activities.IssueDetailActivity"
            android:windowSoftInputMode="adjustNothing" />
        <activity
            android:name=".activities.RepoDetailActivity"
            android:label="@string/title_activity_repo_detail"
            android:theme="@style/AppTheme.NoActionBar" />
        <activity android:name=".activities.MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <activity
            android:name=".activities.LoginActivity"
            android:launchMode="singleTask" />
        <activity android:name=".activities.NewRepoActivity" />
        <activity android:name=".activities.NewOrganizationActivity" />
    </application>
</manifest>

BIN
app/src/main/app_logo-web.png View File


+ 161
- 0
app/src/main/java/org/mian/gitnex/actions/CollaboratorActions.java View File

@@ -0,0 +1,161 @@
package org.mian.gitnex.actions;
import android.content.Context;
import android.util.Log;
import org.mian.gitnex.R;
import org.mian.gitnex.activities.AddCollaboratorToRepositoryActivity;
import org.mian.gitnex.clients.RetrofitClient;
import org.mian.gitnex.helpers.AlertDialogs;
import org.mian.gitnex.helpers.Authorization;
import org.mian.gitnex.helpers.Toasty;
import org.mian.gitnex.models.Collaborators;
import org.mian.gitnex.models.Permission;
import org.mian.gitnex.util.TinyDB;
import androidx.annotation.NonNull;
import retrofit2.Call;
import retrofit2.Callback;
/**
 * Author M M Arif
 */
public class CollaboratorActions {
    public static void deleteCollaborator(final Context context, final String searchKeyword, String userName) {
        final TinyDB tinyDb = new TinyDB(context);
        final String instanceUrl = tinyDb.getString("instanceUrl");
        final String loginUid = tinyDb.getString("loginUid");
        final String instanceToken = "token " + tinyDb.getString(loginUid + "-token");
        String repoFullName = tinyDb.getString("repoFullName");
        String[] parts = repoFullName.split("/");
        final String repoOwner = parts[0];
        final String repoName = parts[1];
        Call<Collaborators> call;
        call = RetrofitClient
                .getInstance(instanceUrl)
                .getApiInterface()
                .deleteCollaborator(Authorization.returnAuthentication(context, loginUid, instanceToken), repoOwner, repoName, userName);
        call.enqueue(new Callback<Collaborators>() {
            @Override
            public void onResponse(@NonNull Call<Collaborators> call, @NonNull retrofit2.Response<Collaborators> response) {
                if(response.isSuccessful()) {
                    if(response.code() == 204) {
                        Toasty.info(context, context.getString(R.string.removeCollaboratorToastText));
                        ((AddCollaboratorToRepositoryActivity)context).finish();
                        //Log.i("addCollaboratorSearch", addCollaboratorSearch.getText().toString());
                        //tinyDb.putBoolean("updateDataSet", true);
                        //AddCollaboratorToRepositoryActivity usersSearchData = new AddCollaboratorToRepositoryActivity();
                        //usersSearchData.loadUserSearchList(instanceUrl, instanceToken, searchKeyword, context);
                    }
                }
                else if(response.code() == 401) {
                    AlertDialogs.authorizationTokenRevokedDialog(context, context.getResources().getString(R.string.alertDialogTokenRevokedTitle),
                            context.getResources().getString(R.string.alertDialogTokenRevokedMessage),
                            context.getResources().getString(R.string.alertDialogTokenRevokedCopyNegativeButton),
                            context.getResources().getString(R.string.alertDialogTokenRevokedCopyPositiveButton));
                }
                else if(response.code() == 403) {
                    Toasty.info(context, context.getString(R.string.authorizeError));
                }
                else if(response.code() == 404) {
                    Toasty.info(context, context.getString(R.string.apiNotFound));
                }
                else {
                    Toasty.info(context, context.getString(R.string.genericError));
                }
            }
            @Override
            public void onFailure(@NonNull Call<Collaborators> call, @NonNull Throwable t) {
                Log.e("onFailure", t.toString());
            }
        });
    }
    public static void addCollaborator(final Context context, String permission, String userName) {
        final TinyDB tinyDb = new TinyDB(context);
        final String instanceUrl = tinyDb.getString("instanceUrl");
        final String loginUid = tinyDb.getString("loginUid");
        final String instanceToken = "token " + tinyDb.getString(loginUid + "-token");
        String repoFullName = tinyDb.getString("repoFullName");
        String[] parts = repoFullName.split("/");
        final String repoOwner = parts[0];
        final String repoName = parts[1];
        Permission permissionString = new Permission(permission);
        Call<Permission> call;
        call = RetrofitClient
                .getInstance(instanceUrl)
                .getApiInterface()
                .addCollaborator(Authorization.returnAuthentication(context, loginUid, instanceToken), repoOwner, repoName, userName, permissionString);
        call.enqueue(new Callback<Permission>() {
            @Override
            public void onResponse(@NonNull Call<Permission> call, @NonNull retrofit2.Response<Permission> response) {
                if(response.isSuccessful()) {
                    if(response.code() == 204) {
                        Toasty.info(context, context.getString(R.string.addCollaboratorToastText));
                        ((AddCollaboratorToRepositoryActivity)context).finish();
                        //AddCollaboratorToRepositoryActivity usersSearchData = new AddCollaboratorToRepositoryActivity();
                        //usersSearchData.loadUserSearchList(instanceUrl, instanceToken, searchKeyword, context);
                    }
                }
                else if(response.code() == 401) {
                    AlertDialogs.authorizationTokenRevokedDialog(context, context.getResources().getString(R.string.alertDialogTokenRevokedTitle),
                            context.getResources().getString(R.string.alertDialogTokenRevokedMessage),
                            context.getResources().getString(R.string.alertDialogTokenRevokedCopyNegativeButton),
                            context.getResources().getString(R.string.alertDialogTokenRevokedCopyPositiveButton));
                }
                else if(response.code() == 403) {
                    Toasty.info(context, context.getString(R.string.authorizeError));
                }
                else if(response.code() == 404) {
                    Toasty.info(context, context.getString(R.string.apiNotFound));
                }
                else {
                    Toasty.info(context, context.getString(R.string.genericError));
                }
            }
            @Override
            public void onFailure(@NonNull Call<Permission> call, @NonNull Throwable t) {
                Log.e("onFailure", t.toString());
            }
        });
    }
}

+ 164
- 0
app/src/main/java/org/mian/gitnex/actions/IssueActions.java View File

@@ -0,0 +1,164 @@
package org.mian.gitnex.actions;
import android.content.Context;
import android.util.Log;
import com.google.gson.JsonElement;
import org.mian.gitnex.R;
import org.mian.gitnex.activities.ReplyToIssueActivity;
import org.mian.gitnex.clients.RetrofitClient;
import org.mian.gitnex.helpers.AlertDialogs;
import org.mian.gitnex.helpers.Authorization;
import org.mian.gitnex.helpers.Toasty;
import org.mian.gitnex.models.UpdateIssueState;
import org.mian.gitnex.models.IssueComments;
import org.mian.gitnex.util.TinyDB;
import androidx.annotation.NonNull;
import retrofit2.Call;
import retrofit2.Callback;
/**
 * Author M M Arif
 */
public class IssueActions {
    public static void editIssueComment(final Context context, final int commentId, final String commentBody) {
        final TinyDB tinyDb = new TinyDB(context);
        final String instanceUrl = tinyDb.getString("instanceUrl");
        final String loginUid = tinyDb.getString("loginUid");
        final String instanceToken = "token " + tinyDb.getString(loginUid + "-token");
        String repoFullName = tinyDb.getString("repoFullName");
        String[] parts = repoFullName.split("/");
        final String repoOwner = parts[0];
        final String repoName = parts[1];
        IssueComments commentBodyJson = new IssueComments(commentBody);
        Call<IssueComments> call;
        call = RetrofitClient
                .getInstance(instanceUrl)
                .getApiInterface()
                .patchIssueComment(Authorization.returnAuthentication(context, loginUid, instanceToken), repoOwner, repoName, commentId, commentBodyJson);
        call.enqueue(new Callback<IssueComments>() {
            @Override
            public void onResponse(@NonNull Call<IssueComments> call, @NonNull retrofit2.Response<IssueComments> response) {
                if(response.isSuccessful()) {
                    if(response.code() == 200) {
                        tinyDb.putBoolean("commentEdited", true);
                        Toasty.info(context, context.getString(R.string.editCommentUpdatedText));
                        ((ReplyToIssueActivity)context).finish();
                    }
                }
                else if(response.code() == 401) {
                    AlertDialogs.authorizationTokenRevokedDialog(context, context.getResources().getString(R.string.alertDialogTokenRevokedTitle),
                            context.getResources().getString(R.string.alertDialogTokenRevokedMessage),
                            context.getResources().getString(R.string.alertDialogTokenRevokedCopyNegativeButton),
                            context.getResources().getString(R.string.alertDialogTokenRevokedCopyPositiveButton));
                }
                else if(response.code() == 403) {
                    Toasty.info(context, context.getString(R.string.authorizeError));
                }
                else if(response.code() == 404) {
                    Toasty.info(context, context.getString(R.string.apiNotFound));
                }
                else {
                    Toasty.info(context, context.getString(R.string.genericError));
                }
            }
            @Override
            public void onFailure(@NonNull Call<IssueComments> call, @NonNull Throwable t) {
                Log.e("onFailure", t.toString());
            }
        });
    }
    public static void closeReopenIssue(final Context context, final int issueIndex, final String issueState) {
        final TinyDB tinyDb = new TinyDB(context);
        final String instanceUrl = tinyDb.getString("instanceUrl");
        final String loginUid = tinyDb.getString("loginUid");
        final String instanceToken = "token " + tinyDb.getString(loginUid + "-token");
        String repoFullName = tinyDb.getString("repoFullName");
        String[] parts = repoFullName.split("/");
        final String repoOwner = parts[0];
        final String repoName = parts[1];
        UpdateIssueState issueStatJson = new UpdateIssueState(issueState);
        Call<JsonElement> call;
        call = RetrofitClient
                .getInstance(instanceUrl)
                .getApiInterface()
                .closeReopenIssue(Authorization.returnAuthentication(context, loginUid, instanceToken), repoOwner, repoName, issueIndex, issueStatJson);
        call.enqueue(new Callback<JsonElement>() {
            @Override
            public void onResponse(@NonNull Call<JsonElement> call, @NonNull retrofit2.Response<JsonElement> response) {
                if(response.isSuccessful()) {
                    if(response.code() == 201) {
                        tinyDb.putBoolean("resumeIssues", true);
                        tinyDb.putBoolean("resumeClosedIssues", true);
                        if(issueState.equals("closed")) {
                            Toasty.info(context, context.getString(R.string.issueStateClosed));
                        }
                        else if(issueState.equals("open")) {
                            Toasty.info(context, context.getString(R.string.issueStateReopened));
                        }
                    }
                }
                else if(response.code() == 401) {
                    AlertDialogs.authorizationTokenRevokedDialog(context, context.getResources().getString(R.string.alertDialogTokenRevokedTitle),
                            context.getResources().getString(R.string.alertDialogTokenRevokedMessage),
                            context.getResources().getString(R.string.alertDialogTokenRevokedCopyNegativeButton),
                            context.getResources().getString(R.string.alertDialogTokenRevokedCopyPositiveButton));
                }
                else if(response.code() == 403) {
                    Toasty.info(context, context.getString(R.string.authorizeError));
                }
                else if(response.code() == 404) {
                    Toasty.info(context, context.getString(R.string.apiNotFound));
                }
                else {
                    Toasty.info(context, context.getString(R.string.genericError));
                }
            }
            @Override
            public void onFailure(@NonNull Call<JsonElement> call, @NonNull Throwable t) {
                Log.e("onFailure", t.toString());
            }
        });
    }
}

+ 144
- 0
app/src/main/java/org/mian/gitnex/activities/AddCollaboratorToRepositoryActivity.java View File

@@ -0,0 +1,144 @@
package org.mian.gitnex.activities;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.recyclerview.widget.DividerItemDecoration;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;
import android.content.Context;
import android.os.Bundle;
import android.util.Log;
import android.view.KeyEvent;
import android.view.View;
import android.view.inputmethod.EditorInfo;
import android.widget.ImageView;
import android.widget.ProgressBar;
import android.widget.TextView;
import org.mian.gitnex.R;
import org.mian.gitnex.adapters.UserSearchAdapter;
import org.mian.gitnex.clients.RetrofitClient;
import org.mian.gitnex.helpers.Authorization;
import org.mian.gitnex.models.UserSearch;
import org.mian.gitnex.models.UserInfo;
import org.mian.gitnex.util.AppUtil;
import org.mian.gitnex.util.TinyDB;
import java.util.List;
/**
 * Author M M Arif
 */
public class AddCollaboratorToRepositoryActivity extends AppCompatActivity {
    private View.OnClickListener onClickListener;
    final Context ctx = this;
    private TextView addCollaboratorSearch;
    private TextView noData;
    private ProgressBar mProgressBar;
    private RecyclerView mRecyclerView;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_add_collaborator_to_repository);
        boolean connToInternet = AppUtil.haveNetworkConnection(getApplicationContext());
        TinyDB tinyDb = new TinyDB(getApplicationContext());
        final String instanceUrl = tinyDb.getString("instanceUrl");
        final String loginUid = tinyDb.getString("loginUid");
        String repoFullName = tinyDb.getString("repoFullName");
        String[] parts = repoFullName.split("/");
        final String instanceToken = "token " + tinyDb.getString(loginUid + "-token");
        ImageView closeActivity = findViewById(R.id.close);
        addCollaboratorSearch = findViewById(R.id.addCollaboratorSearch);
        mRecyclerView = findViewById(R.id.recyclerViewUserSearch);
        mProgressBar = findViewById(R.id.progress_bar);
        noData = findViewById(R.id.noData);
        initCloseListener();
        closeActivity.setOnClickListener(onClickListener);
        addCollaboratorSearch.setOnEditorActionListener(new TextView.OnEditorActionListener() {
            @Override
            public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
                if (actionId == EditorInfo.IME_ACTION_SEND) {
                    if(!addCollaboratorSearch.getText().toString().equals("")) {
                        loadUserSearchList(instanceUrl, instanceToken, addCollaboratorSearch.getText().toString(), getApplicationContext(), loginUid);
                    }
                }
                return false;
            }
        });
    }
    public void loadUserSearchList(String instanceUrl, String token, String searchKeyword, final Context context, String loginUid) {
        Call<UserSearch> call = RetrofitClient
                .getInstance(instanceUrl)
                .getApiInterface()
                .getUserBySearch(Authorization.returnAuthentication(getApplicationContext(), loginUid, token), searchKeyword, 10);
        call.enqueue(new Callback<UserSearch>() {
            @Override
            public void onResponse(@NonNull Call<UserSearch> call, @NonNull Response<UserSearch> response) {
                if (response.isSuccessful()) {
                    assert response.body() != null;
                    getUsersList(response.body().getData(), context);
                } else {
                    Log.i("onResponse", String.valueOf(response.code()));
                }
            }
            @Override
            public void onFailure(@NonNull Call<UserSearch> call, @NonNull Throwable t) {
                Log.i("onFailure", t.getMessage());
            }
        });
    }
    private void getUsersList(List<UserInfo> dataList, Context context) {
        UserSearchAdapter adapter = new UserSearchAdapter(dataList, context);
        mRecyclerView.setHasFixedSize(true);
        mRecyclerView.setLayoutManager(new LinearLayoutManager(getApplicationContext()));
        DividerItemDecoration dividerItemDecoration = new DividerItemDecoration(mRecyclerView.getContext(),
                DividerItemDecoration.VERTICAL);
        mRecyclerView.addItemDecoration(dividerItemDecoration);
        mProgressBar.setVisibility(View.VISIBLE);
        if(adapter.getItemCount() > 0) {
            mRecyclerView.setAdapter(adapter);
            noData.setVisibility(View.GONE);
            mProgressBar.setVisibility(View.GONE);
        }
        else {
            noData.setVisibility(View.VISIBLE);
            mProgressBar.setVisibility(View.GONE);
        }
    }
    private void initCloseListener() {
        onClickListener = new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                finish();
            }
        };
    }
}

+ 290
- 0
app/src/main/java/org/mian/gitnex/activities/AddRemoveAssigneesActivity.java View File

@@ -0,0 +1,290 @@
package org.mian.gitnex.activities;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import android.content.Context;
import android.graphics.Color;
import android.graphics.drawable.ColorDrawable;
import android.os.Bundle;
import android.util.Log;
import android.view.Window;
import com.google.gson.JsonElement;
import org.mian.gitnex.R;
import org.mian.gitnex.clients.RetrofitClient;
import org.mian.gitnex.helpers.AlertDialogs;
import org.mian.gitnex.helpers.Authorization;
import org.mian.gitnex.helpers.MultiSelectDialog;
import org.mian.gitnex.helpers.Toasty;
import org.mian.gitnex.models.Collaborators;
import org.mian.gitnex.models.Issues;
import org.mian.gitnex.models.MultiSelectModel;
import org.mian.gitnex.models.UpdateIssueAssignee;
import org.mian.gitnex.util.TinyDB;
import java.util.ArrayList;
import java.util.List;
import retrofit2.Call;
import retrofit2.Callback;
/**
 * Author M M Arif
 */
public class AddRemoveAssigneesActivity extends AppCompatActivity {
    private ArrayList<MultiSelectModel> listOfCollaborators = new ArrayList<>();
    private ArrayList<Integer> issueAssigneesIds = new ArrayList<>();
    private Boolean assigneesFlag = false;
    private MultiSelectDialog multiSelectDialogAssignees;
    final Context ctx = this;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        supportRequestWindowFeature(Window.FEATURE_NO_TITLE);
        setContentView(R.layout.activity_add_remove_assignees);
        getWindow().getDecorView().setBackground(new ColorDrawable(Color.TRANSPARENT));
        TinyDB tinyDb = new TinyDB(getApplicationContext());
        final String instanceUrl = tinyDb.getString("instanceUrl");
        final String loginUid = tinyDb.getString("loginUid");
        String repoFullName = tinyDb.getString("repoFullName");
        String[] parts = repoFullName.split("/");
        final String repoOwner = parts[0];
        final String repoName = parts[1];
        final String instanceToken = "token " + tinyDb.getString(loginUid + "-token");
        final int issueIndex = Integer.parseInt(tinyDb.getString("issueNumber"));
        getAssignees(instanceUrl, instanceToken, repoOwner, repoName, issueIndex, loginUid);
    }
    private void getAssignees(final String instanceUrl, final String instanceToken, final String repoOwner, final String repoName, final int issueIndex, final String loginUid) {
        final TinyDB tinyDb = new TinyDB(getApplicationContext());
        Call<List<Collaborators>> call = RetrofitClient
                .getInstance(instanceUrl)
                .getApiInterface()
                .getCollaborators(Authorization.returnAuthentication(getApplicationContext(), loginUid, instanceToken), repoOwner, repoName);
        call.enqueue(new Callback<List<Collaborators>>() {
            @Override
            public void onResponse(@NonNull final Call<List<Collaborators>> call, @NonNull final retrofit2.Response<List<Collaborators>> response) {
                if(response.isSuccessful()) {
                    if(response.code() == 200) {
                        final List<Collaborators> collaboratorsList_ = response.body();
                        assert collaboratorsList_ != null;
                        if(collaboratorsList_.size() > 0) {
                            for (int i = 0; i < collaboratorsList_.size(); i++) {
                                listOfCollaborators.add(new MultiSelectModel(collaboratorsList_.get(i).getId(), collaboratorsList_.get(i).getUsername().trim()));
                            }
                        }
                        // get current issue assignees
                        Call<Issues> callSingleIssueAssignees = RetrofitClient
                                .getInstance(instanceUrl)
                                .getApiInterface()
                                .getIssueByIndex(Authorization.returnAuthentication(getApplicationContext(), loginUid, instanceToken), repoOwner, repoName, issueIndex);
                        callSingleIssueAssignees.enqueue(new Callback<Issues>() {
                            @Override
                            public void onResponse(@NonNull Call<Issues> call, @NonNull retrofit2.Response<Issues> response) {
                                if(response.code() == 200) {
                                    Issues issueAssigneesList = response.body();
                                    assert issueAssigneesList != null;
                                    if (issueAssigneesList.getAssignees() != null) {
                                        if (issueAssigneesList.getAssignees().size() > 0) {
                                            for (int i = 0; i < issueAssigneesList.getAssignees().size(); i++) {
                                                issueAssigneesIds.add(issueAssigneesList.getAssignees().get(i).getId());
                                                if(issueAssigneesList.getAssignees().get(i).getUsername().equals(loginUid)) {
                                                    listOfCollaborators.add(new MultiSelectModel(issueAssigneesList.getAssignees().get(i).getId(), issueAssigneesList.getAssignees().get(i).getUsername().trim()));
                                                }
                                            }
                                            assigneesFlag = true;
                                        }
                                    }
                                    else {
                                        listOfCollaborators.add(new MultiSelectModel(tinyDb.getInt("userId"), loginUid));
                                    }
                                    if(assigneesFlag) {
                                        multiSelectDialogAssignees = new MultiSelectDialog()
                                                .title(getResources().getString(R.string.newIssueSelectAssigneesListTitle))
                                                .titleSize(25)
                                                .positiveText(getResources().getString(R.string.saveButton))
                                                .negativeText(getResources().getString(R.string.cancelButton))
                                                .setMinSelectionLimit(0)
                                                .preSelectIDsList(issueAssigneesIds)
                                                .setMaxSelectionLimit(listOfCollaborators.size())
                                                .multiSelectList(listOfCollaborators)
                                                .onSubmit(new MultiSelectDialog.SubmitCallbackListener() {
                                                    @Override
                                                    public void onSelected(ArrayList<Integer> selectedIds, ArrayList<String> selectedNames, String dataString) {
                                                        Log.i("selectedNames", String.valueOf(selectedNames));
                                                        updateIssueAssignees(instanceUrl, Authorization.returnAuthentication(getApplicationContext(), loginUid, instanceToken), repoOwner, repoName, loginUid, issueIndex, selectedNames);
                                                        tinyDb.putBoolean("singleIssueUpdate", true);
                                                        CloseActivity();
                                                    }
                                                    @Override
                                                    public void onCancel() {
                                                        CloseActivity();
                                                    }
                                                });
                                    }
                                    else {
                                        multiSelectDialogAssignees = new MultiSelectDialog()
                                                .title(getResources().getString(R.string.newIssueSelectAssigneesListTitle))
                                                .titleSize(25)
                                                .positiveText(getResources().getString(R.string.saveButton))
                                                .negativeText(getResources().getString(R.string.cancelButton))
                                                .setMinSelectionLimit(0)
                                                .setMaxSelectionLimit(listOfCollaborators.size())
                                                .multiSelectList(listOfCollaborators)
                                                .onSubmit(new MultiSelectDialog.SubmitCallbackListener() {
                                                    @Override
                                                    public void onSelected(ArrayList<Integer> selectedIds, ArrayList<String> selectedNames, String dataString) {
                                                        updateIssueAssignees(instanceUrl, Authorization.returnAuthentication(getApplicationContext(), loginUid, instanceToken), repoOwner, repoName, loginUid, issueIndex, selectedNames);
                                                        tinyDb.putBoolean("singleIssueUpdate", true);
                                                        CloseActivity();
                                                    }
                                                    @Override
                                                    public void onCancel() {
                                                        CloseActivity();
                                                    }
                                                });
                                    }
                                    multiSelectDialogAssignees.show(getSupportFragmentManager(), "issueMultiSelectDialog");
                                }
                            }
                            @Override
                            public void onFailure(@NonNull Call<Issues> call, @NonNull Throwable t) {
                                Log.e("onFailure", t.toString());
                            }
                        });
                        // get current issue assignees
                    }
                    else if(response.code() == 401) {
                        AlertDialogs.authorizationTokenRevokedDialog(ctx, getResources().getString(R.string.alertDialogTokenRevokedTitle),
                                getResources().getString(R.string.alertDialogTokenRevokedMessage),
                                getResources().getString(R.string.alertDialogTokenRevokedCopyNegativeButton),
                                getResources().getString(R.string.alertDialogTokenRevokedCopyPositiveButton));
                    }
                    else if(response.code() == 403) {
                        Toasty.info(ctx, ctx.getString(R.string.authorizeError));
                    }
                    else if(response.code() == 404) {
                        Toasty.info(ctx, ctx.getString(R.string.apiNotFound));
                    }
                    else {
                        Toasty.info(getApplicationContext(), getString(R.string.genericError));
                    }
                }
            }
            @Override
            public void onFailure(@NonNull Call<List<Collaborators>> call, @NonNull Throwable t) {
                Log.e("onFailure", t.toString());
            }
        });
    }
    private void CloseActivity() {
        this.finish();
    }
    private void updateIssueAssignees(final String instanceUrl, final String instanceToken, String repoOwner, String repoName, String loginUid, int issueIndex, List<String> issueAssigneesList) {
        UpdateIssueAssignee updateAssigneeJson = new UpdateIssueAssignee(issueAssigneesList);
        Call<JsonElement> call3;
        call3 = RetrofitClient
                .getInstance(instanceUrl)
                .getApiInterface()
                .patchIssueAssignee(Authorization.returnAuthentication(getApplicationContext(), loginUid, instanceToken), repoOwner, repoName, issueIndex, updateAssigneeJson);
        call3.enqueue(new Callback<JsonElement>() {
            @Override
            public void onResponse(@NonNull Call<JsonElement> call, @NonNull retrofit2.Response<JsonElement> response2) {
                if(response2.code() == 201) {
                    Toasty.info(ctx, ctx.getString(R.string.assigneesUpdated));
                }
                else if(response2.code() == 401) {
                    AlertDialogs.authorizationTokenRevokedDialog(ctx, getResources().getString(R.string.alertDialogTokenRevokedTitle),
                            getResources().getString(R.string.alertDialogTokenRevokedMessage),
                            getResources().getString(R.string.alertDialogTokenRevokedCopyNegativeButton),
                            getResources().getString(R.string.alertDialogTokenRevokedCopyPositiveButton));
                }
                else if(response2.code() == 403) {
                    Toasty.info(ctx, ctx.getString(R.string.authorizeError));
                }
                else if(response2.code() == 404) {
                    Toasty.info(ctx, ctx.getString(R.string.apiNotFound));
                }
                else {
                    Toasty.info(getApplicationContext(), getString(R.string.genericError));
                }
            }
            @Override
            public void onFailure(@NonNull Call<JsonElement> call, @NonNull Throwable t) {
                Log.e("onFailure", t.toString());
            }
        });
    }
}

+ 303
- 0
app/src/main/java/org/mian/gitnex/activities/AddRemoveLabelsActivity.java View File

@@ -0,0 +1,303 @@
package org.mian.gitnex.activities;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import retrofit2.Call;
import retrofit2.Callback;
import android.content.Context;
import android.graphics.Color;
import android.graphics.drawable.ColorDrawable;
import android.os.Bundle;
import android.util.Log;
import android.view.Window;
import com.google.gson.JsonElement;
import org.mian.gitnex.R;
import org.mian.gitnex.clients.RetrofitClient;
import org.mian.gitnex.helpers.AlertDialogs;
import org.mian.gitnex.helpers.Authorization;
import org.mian.gitnex.helpers.MultiSelectDialog;
import org.mian.gitnex.helpers.Toasty;
import org.mian.gitnex.models.Labels;
import org.mian.gitnex.models.MultiSelectModel;
import org.mian.gitnex.util.TinyDB;
import java.util.ArrayList;
import java.util.List;
/**
 * Author M M Arif
 */
public class AddRemoveLabelsActivity extends AppCompatActivity {
    private ArrayList<MultiSelectModel> listOfLabels = new ArrayList<>();
    private ArrayList<Integer> issueLabelIds = new ArrayList<>();
    private Boolean labelsFlag = false;
    private MultiSelectDialog multiSelectDialogLabels;
    final Context ctx = this;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        supportRequestWindowFeature(Window.FEATURE_NO_TITLE);
        setContentView(R.layout.activity_add_remove_labels);
        getWindow().getDecorView().setBackground(new ColorDrawable(Color.TRANSPARENT));
        TinyDB tinyDb = new TinyDB(getApplicationContext());
        final String instanceUrl = tinyDb.getString("instanceUrl");
        final String loginUid = tinyDb.getString("loginUid");
        String repoFullName = tinyDb.getString("repoFullName");
        String[] parts = repoFullName.split("/");
        final String repoOwner = parts[0];
        final String repoName = parts[1];
        final String instanceToken = "token " + tinyDb.getString(loginUid + "-token");
        final int issueIndex = Integer.parseInt(tinyDb.getString("issueNumber"));
        getLabels(instanceUrl, instanceToken, repoOwner, repoName, issueIndex, loginUid);
    }
    private void getLabels(final String instanceUrl, final String instanceToken, final String repoOwner, final String repoName, final int issueIndex, final String loginUid) {
        final TinyDB tinyDb = new TinyDB(getApplicationContext());
        Call<List<Labels>> call = RetrofitClient
                .getInstance(instanceUrl)
                .getApiInterface()
                .getlabels(Authorization.returnAuthentication(getApplicationContext(), loginUid, instanceToken), repoOwner, repoName);
        call.enqueue(new Callback<List<Labels>>() {
            @Override
            public void onResponse(@NonNull Call<List<Labels>> call, @NonNull retrofit2.Response<List<Labels>> response) {
                if(response.isSuccessful()) {
                    if(response.code() == 200) {
                        List<Labels> labelsList_ = response.body();
                        assert labelsList_ != null;
                        if(labelsList_.size() > 0) {
                            for (int i = 0; i < labelsList_.size(); i++) {
                                listOfLabels.add(new MultiSelectModel(labelsList_.get(i).getId(), labelsList_.get(i).getName().trim()));
                            }
                        }
                        // get current issue labels
                        Call<List<Labels>> callSingleIssueLabels = RetrofitClient
                                .getInstance(instanceUrl)
                                .getApiInterface()
                                .getIssueLabels(Authorization.returnAuthentication(getApplicationContext(), loginUid, instanceToken), repoOwner, repoName, issueIndex);
                        callSingleIssueLabels.enqueue(new Callback<List<Labels>>() {
                            @Override
                            public void onResponse(@NonNull Call<List<Labels>> call, @NonNull retrofit2.Response<List<Labels>> response) {
                                if(response.code() == 200) {
                                    List<Labels> issueLabelsList = response.body();
                                    assert issueLabelsList != null;
                                    if(issueLabelsList.size() > 0) {
                                        for (int i = 0; i < issueLabelsList.size(); i++) {
                                            issueLabelIds.add(issueLabelsList.get(i).getId());
                                        }
                                        labelsFlag = true;
                                    }
                                    if(labelsFlag) {
                                        multiSelectDialogLabels = new MultiSelectDialog()
                                                .title(getResources().getString(R.string.newIssueSelectLabelsListTitle))
                                                .titleSize(25)
                                                .positiveText(getResources().getString(R.string.saveButton))
                                                .negativeText(getResources().getString(R.string.cancelButton))
                                                .setMinSelectionLimit(0)
                                                .preSelectIDsList(issueLabelIds)
                                                .setMaxSelectionLimit(listOfLabels.size())
                                                .multiSelectList(listOfLabels)
                                                .onSubmit(new MultiSelectDialog.SubmitCallbackListener() {
                                                    @Override
                                                    public void onSelected(ArrayList<Integer> selectedIds, ArrayList<String> selectedNames, String dataString) {
                                                        String labelIds = selectedIds.toString();
                                                        int[] integers;
                                                        if (selectedIds.size() > 0) {
                                                            String[] items = labelIds.replaceAll("\\[", "").replaceAll("\\]", "").replaceAll("\\s", "").split(",");
                                                            integers = new int[items.length];
                                                            for (int i = 0; i < integers.length; i++) {
                                                                integers[i] = Integer.parseInt(items[i]);
                                                            }
                                                        }
                                                        else {
                                                            integers = new int[0];
                                                        }
                                                        updateIssueLabels(instanceUrl, Authorization.returnAuthentication(getApplicationContext(), loginUid, instanceToken), repoOwner, repoName, issueIndex, integers, loginUid);
                                                        tinyDb.putBoolean("singleIssueUpdate", true);
                                                        CloseActivity();
                                                    }
                                                    @Override
                                                    public void onCancel() {
                                                        CloseActivity();
                                                    }
                                                });
                                    }
                                    else {
                                        multiSelectDialogLabels = new MultiSelectDialog()
                                                .title(getResources().getString(R.string.newIssueSelectLabelsListTitle))
                                                .titleSize(25)
                                                .positiveText(getResources().getString(R.string.saveButton))
                                                .negativeText(getResources().getString(R.string.cancelButton))
                                                .setMinSelectionLimit(0)
                                                .setMaxSelectionLimit(listOfLabels.size())
                                                .multiSelectList(listOfLabels)
                                                .onSubmit(new MultiSelectDialog.SubmitCallbackListener() {
                                                    @Override
                                                    public void onSelected(ArrayList<Integer> selectedIds, ArrayList<String> selectedNames, String dataString) {
                                                        String labelIds = selectedIds.toString();
                                                        int[] integers;
                                                        if (selectedIds.size() > 0) {
                                                            String[] items = labelIds.replaceAll("\\[", "").replaceAll("\\]", "").replaceAll("\\s", "").split(",");
                                                            integers = new int[items.length];
                                                            for (int i = 0; i < integers.length; i++) {
                                                                integers[i] = Integer.parseInt(items[i]);
                                                            }
                                                        }
                                                        else {
                                                            integers = new int[0];
                                                        }
                                                        updateIssueLabels(instanceUrl, Authorization.returnAuthentication(getApplicationContext(), loginUid, instanceToken), repoOwner, repoName, issueIndex, integers, loginUid);
                                                        tinyDb.putBoolean("singleIssueUpdate", true);
                                                        CloseActivity();
                                                    }
                                                    @Override
                                                    public void onCancel() {
                                                        CloseActivity();
                                                    }
                                                });
                                    }
                                    multiSelectDialogLabels.show(getSupportFragmentManager(), "issueMultiSelectDialog");
                                }
                            }
                            @Override
                            public void onFailure(@NonNull Call<List<Labels>> call, @NonNull Throwable t) {
                                Log.e("onFailure", t.toString());
                            }
                        });
                        // get current issue labels
                    }
                    else if(response.code() == 401) {
                        AlertDialogs.authorizationTokenRevokedDialog(ctx, getResources().getString(R.string.alertDialogTokenRevokedTitle),
                                getResources().getString(R.string.alertDialogTokenRevokedMessage),
                                getResources().getString(R.string.alertDialogTokenRevokedCopyNegativeButton),
                                getResources().getString(R.string.alertDialogTokenRevokedCopyPositiveButton));
                    }
                    else if(response.code() == 403) {
                        Toasty.info(ctx, ctx.getString(R.string.authorizeError));
                    }
                    else if(response.code() == 404) {
                        Toasty.info(ctx, ctx.getString(R.string.apiNotFound));
                    }
                    else {
                        Toasty.info(getApplicationContext(), getString(R.string.genericError));
                    }
                }
            }
            @Override
            public void onFailure(@NonNull Call<List<Labels>> call, @NonNull Throwable t) {
                Log.e("onFailure", t.toString());
            }
        });
    }
    private void updateIssueLabels(final String instanceUrl, final String instanceToken, String repoOwner, String repoName, int issueIndex, int[] issueLabels, String loginUid) {
        Labels patchIssueLabels = new Labels(issueLabels);
        Call<JsonElement> call = RetrofitClient
                .getInstance(instanceUrl)
                .getApiInterface()
                .updateIssueLabels(Authorization.returnAuthentication(getApplicationContext(), loginUid, instanceToken), repoOwner, repoName, issueIndex, patchIssueLabels);
        call.enqueue(new Callback<JsonElement>() {
            @Override
            public void onResponse(@NonNull Call<JsonElement> call, @NonNull retrofit2.Response<JsonElement> response) {
                if(response.code() == 200) {
                    Toasty.info(ctx, ctx.getString(R.string.labelsUpdated));
                }
                else if(response.code() == 401) {
                    AlertDialogs.authorizationTokenRevokedDialog(ctx, getResources().getString(R.string.alertDialogTokenRevokedTitle),
                            getResources().getString(R.string.alertDialogTokenRevokedMessage),
                            getResources().getString(R.string.alertDialogTokenRevokedCopyNegativeButton),
                            getResources().getString(R.string.alertDialogTokenRevokedCopyPositiveButton));
                }
                else if(response.code() == 403) {
                    Toasty.info(ctx, ctx.getString(R.string.authorizeError));
                }
                else if(response.code() == 404) {
                    Toasty.info(ctx, ctx.getString(R.string.apiNotFound));
                }
                else {
                    Toasty.info(getApplicationContext(), getString(R.string.genericError));
                }
            }
            @Override
            public void onFailure(@NonNull Call<JsonElement> call, @NonNull Throwable t) {
                Log.e("onFailure", t.toString());
            }
        });
    }
    private void CloseActivity() {
        this.finish();
    }
}

+ 200
- 0
app/src/main/java/org/mian/gitnex/activities/AdminGetUsersActivity.java View File

@@ -0,0 +1,200 @@
package org.mian.gitnex.activities;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.Toolbar;
import androidx.lifecycle.Observer;
import androidx.lifecycle.ViewModelProviders;
import androidx.recyclerview.widget.DividerItemDecoration;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.inputmethod.EditorInfo;
import android.widget.ImageView;
import android.widget.TextView;
import org.mian.gitnex.R;
import org.mian.gitnex.adapters.AdminGetUsersAdapter;
import org.mian.gitnex.fragments.AdminUsersBottomSheetFragment;
import org.mian.gitnex.helpers.Authorization;
import org.mian.gitnex.models.UserInfo;
import org.mian.gitnex.util.AppUtil;
import org.mian.gitnex.util.TinyDB;
import org.mian.gitnex.viewmodels.AdminGetUsersViewModel;
import java.util.List;
import java.util.Objects;
/**
 * Author M M Arif
 */
public class AdminGetUsersActivity extends AppCompatActivity implements AdminUsersBottomSheetFragment.BottomSheetListener {
    private View.OnClickListener onClickListener;
    final Context ctx = this;
    private AdminGetUsersAdapter adapter;
    private RecyclerView mRecyclerView;
    private TextView noDataUsers;
    private Boolean searchFilter = false;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_admin_get_users);
        TinyDB tinyDb = new TinyDB(getApplicationContext());
        final String instanceUrl = tinyDb.getString("instanceUrl");
        final String loginUid = tinyDb.getString("loginUid");
        final String instanceToken = "token " + tinyDb.getString(loginUid + "-token");
        ImageView closeActivity = findViewById(R.id.close);
        noDataUsers = findViewById(R.id.noDataUsers);
        mRecyclerView = findViewById(R.id.recyclerView);
        final SwipeRefreshLayout swipeRefresh = findViewById(R.id.pullToRefresh);
        Toolbar toolbar = findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);
        initCloseListener();
        closeActivity.setOnClickListener(onClickListener);
        mRecyclerView.setHasFixedSize(true);
        mRecyclerView.setLayoutManager(new LinearLayoutManager(getApplicationContext()));
        DividerItemDecoration dividerItemDecoration = new DividerItemDecoration(mRecyclerView.getContext(),
                DividerItemDecoration.VERTICAL);
        mRecyclerView.addItemDecoration(dividerItemDecoration);
        swipeRefresh.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
            @Override
            public void onRefresh() {
                new Handler().postDelayed(new Runnable() {
                    @Override
                    public void run() {
                        swipeRefresh.setRefreshing(false);
                        AdminGetUsersViewModel.loadUsersList(getApplicationContext(), instanceUrl, Authorization.returnAuthentication(getApplicationContext(), loginUid, instanceToken));
                    }
                }, 500);
            }
        });
        fetchDataAsync(getApplicationContext(), instanceUrl, Authorization.returnAuthentication(getApplicationContext(), loginUid, instanceToken));
    }
    private void fetchDataAsync(Context ctx, String instanceUrl, String instanceToken) {
        AdminGetUsersViewModel usersModel = ViewModelProviders.of(this).get(AdminGetUsersViewModel.class);
        usersModel.getUsersList(ctx, instanceUrl, instanceToken).observe(this, new Observer<List<UserInfo>>() {
            @Override
            public void onChanged(@Nullable List<UserInfo> usersListMain) {
                adapter = new AdminGetUsersAdapter(getApplicationContext(), usersListMain);
                if(adapter.getItemCount() > 0) {
                    mRecyclerView.setVisibility(View.VISIBLE);
                    mRecyclerView.setAdapter(adapter);
                    noDataUsers.setVisibility(View.GONE);
                    searchFilter = true;
                }
                else {
                    //adapter.notifyDataSetChanged();
                    //mRecyclerView.setAdapter(adapter);
                    mRecyclerView.setVisibility(View.GONE);
                    noDataUsers.setVisibility(View.VISIBLE);
                }
            }
        });
    }
    @Override
    public boolean onCreateOptionsMenu(final Menu menu) {
        final MenuInflater inflater = getMenuInflater();
        inflater.inflate(R.menu.generic_nav_dotted_menu, menu);
        new Handler().postDelayed(new Runnable() {
            @Override
            public void run() {
                if(searchFilter) {
                    boolean connToInternet = AppUtil.haveNetworkConnection(Objects.requireNonNull(getApplicationContext()));
                    inflater.inflate(R.menu.search_menu, menu);
                    MenuItem searchItem = menu.findItem(R.id.action_search);
                    androidx.appcompat.widget.SearchView searchView = (androidx.appcompat.widget.SearchView) searchItem.getActionView();
                    searchView.setImeOptions(EditorInfo.IME_ACTION_DONE);
                    if(!connToInternet) {
                        return;