From 72a2b825585be5aceb54c37759bde065e07c4e2c Mon Sep 17 00:00:00 2001 From: M M Arif Date: Sun, 11 Feb 2024 13:20:23 +0000 Subject: [PATCH] Handle attachments in issue, pr and comments (#1304) Closes #56 Closes #1313 Closes #1215 Closes #926 Closes #1315 Closes #1321 Closes #1322 Reviewed-on: https://codeberg.org/gitnex/GitNex/pulls/1304 Co-authored-by: M M Arif Co-committed-by: M M Arif --- README.md | 2 +- app/build.gradle | 24 +- app/src/main/AndroidManifest.xml | 33 +- .../AccountSettingsEmailActivity.java | 108 ++-- .../activities/AddNewAccountActivity.java | 101 +-- .../mian/gitnex/activities/BaseActivity.java | 2 +- .../gitnex/activities/CreateFileActivity.java | 176 +++--- .../activities/CreateIssueActivity.java | 385 ++++++++---- .../activities/CreateLabelActivity.java | 211 ++++--- .../activities/CreateMilestoneActivity.java | 231 +++---- .../activities/CreateNewUserActivity.java | 164 +++-- .../CreateOrganizationActivity.java | 148 ++--- .../activities/CreatePullRequestActivity.java | 361 +++++++---- .../activities/CreateReleaseActivity.java | 236 +++---- .../gitnex/activities/CreateRepoActivity.java | 237 +++---- .../activities/CreateTeamByOrgActivity.java | 210 +++---- .../gitnex/activities/EditIssueActivity.java | 247 ++++---- .../gitnex/activities/FileViewActivity.java | 29 +- .../activities/IssueDetailActivity.java | 275 +++++++- .../mian/gitnex/activities/LoginActivity.java | 148 +++-- .../mian/gitnex/activities/MainActivity.java | 1 - .../activities/MergePullRequestActivity.java | 118 ++-- .../gitnex/activities/ProfileActivity.java | 3 +- .../gitnex/activities/RepoDetailActivity.java | 2 +- .../SettingsAppearanceActivity.java | 162 +++-- .../SettingsCodeEditorActivity.java | 34 +- .../activities/SettingsGeneralActivity.java | 42 +- .../SettingsNotificationsActivity.java | 152 +---- .../activities/SettingsSecurityActivity.java | 112 ++-- .../gitnex/adapters/AttachmentsAdapter.java | 125 ++++ .../gitnex/adapters/DashboardAdapter.java | 70 ++- .../gitnex/adapters/IssueCommentsAdapter.java | 173 +++++- .../adapters/MostVisitedReposAdapter.java | 6 +- .../mian/gitnex/clients/RetrofitClient.java | 9 + .../fragments/NotificationsFragment.java | 49 +- .../gitnex/fragments/RepoInfoFragment.java | 10 +- .../fragments/profile/DetailFragment.java | 28 +- .../java/org/mian/gitnex/helpers/AppUtil.java | 4 + .../org/mian/gitnex/helpers/Markdown.java | 7 + .../mian/gitnex/helpers/SimpleCallback.java | 10 +- .../org/mian/gitnex/helpers/SnackBar.java | 8 +- .../helpers/attachments/AttachmentUtils.java | 60 ++ .../helpers/attachments/AttachmentsModel.java | 42 ++ .../gitnex/notifications/Notifications.java | 80 ++- .../notifications/NotificationsWorker.java | 139 +++-- app/src/main/res/drawable/ic_attachment.xml | 13 + .../main/res/drawable/ic_file_download.xml | 34 + app/src/main/res/drawable/ic_menu.xml | 42 +- app/src/main/res/drawable/login_bg.jpg | Bin 0 -> 277611 bytes .../shape_bottom_sheet_top_corners.xml | 6 +- .../main/res/drawable/shape_round_corners.xml | 2 +- .../activity_account_settings_email.xml | 107 ++-- ...ctivity_add_collaborator_to_repository.xml | 1 - .../res/layout/activity_add_new_account.xml | 224 +++---- .../main/res/layout/activity_create_file.xml | 76 +-- .../main/res/layout/activity_create_issue.xml | 106 ++-- .../main/res/layout/activity_create_label.xml | 178 +++--- .../res/layout/activity_create_milestone.xml | 75 +-- .../res/layout/activity_create_new_user.xml | 86 +-- .../layout/activity_create_organization.xml | 85 +-- .../main/res/layout/activity_create_pr.xml | 81 +-- .../res/layout/activity_create_release.xml | 100 +-- .../main/res/layout/activity_create_repo.xml | 163 +++-- .../layout/activity_create_team_by_org.xml | 98 ++- .../main/res/layout/activity_edit_issue.xml | 86 +-- .../main/res/layout/activity_issue_detail.xml | 352 ++++++----- app/src/main/res/layout/activity_login.xml | 491 +++++++-------- .../layout/activity_merge_pull_request.xml | 116 ++-- .../layout/activity_settings_appearance.xml | 586 ++++++++---------- .../layout/activity_settings_code_editor.xml | 223 +++---- .../res/layout/activity_settings_general.xml | 399 ++++++------ .../activity_settings_notifications.xml | 271 +++----- .../res/layout/activity_settings_security.xml | 293 ++++----- app/src/main/res/layout/activity_wiki.xml | 2 - .../res/layout/bottom_sheet_attachments.xml | 106 ++++ .../res/layout/custom_image_view_dialog.xml | 15 + .../main/res/layout/fragment_dashboard.xml | 4 +- .../res/layout/fragment_organization_info.xml | 7 +- .../res/layout/fragment_profile_detail.xml | 77 +-- .../main/res/layout/fragment_repo_info.xml | 173 ++---- app/src/main/res/layout/list_attachments.xml | 82 +++ .../res/layout/list_dashboard_activity.xml | 3 +- .../main/res/layout/list_issue_comments.xml | 51 +- .../res/layout/list_most_visited_repos.xml | 55 +- .../main/res/menu/add_new_account_menu.xml | 11 + app/src/main/res/menu/create_issue_menu.xml | 27 + app/src/main/res/menu/create_label_menu.xml | 20 + .../main/res/menu/create_release_tag_menu.xml | 25 + .../main/res/menu/file_create_edit_menu.xml | 27 + app/src/main/res/menu/save.xml | 1 - app/src/main/res/values/dimens.xml | 1 + app/src/main/res/values/settings.xml | 187 ++++++ app/src/main/res/values/strings.xml | 51 +- build.gradle | 4 +- 94 files changed, 5422 insertions(+), 4575 deletions(-) create mode 100644 app/src/main/java/org/mian/gitnex/adapters/AttachmentsAdapter.java create mode 100644 app/src/main/java/org/mian/gitnex/helpers/attachments/AttachmentUtils.java create mode 100644 app/src/main/java/org/mian/gitnex/helpers/attachments/AttachmentsModel.java create mode 100644 app/src/main/res/drawable/ic_attachment.xml create mode 100644 app/src/main/res/drawable/ic_file_download.xml create mode 100644 app/src/main/res/drawable/login_bg.jpg create mode 100644 app/src/main/res/layout/bottom_sheet_attachments.xml create mode 100644 app/src/main/res/layout/custom_image_view_dialog.xml create mode 100644 app/src/main/res/layout/list_attachments.xml create mode 100644 app/src/main/res/menu/add_new_account_menu.xml create mode 100644 app/src/main/res/menu/create_issue_menu.xml create mode 100644 app/src/main/res/menu/create_label_menu.xml create mode 100644 app/src/main/res/menu/create_release_tag_menu.xml create mode 100644 app/src/main/res/menu/file_create_edit_menu.xml diff --git a/README.md b/README.md index bc8be288..eeaedf1a 100644 --- a/README.md +++ b/README.md @@ -84,7 +84,7 @@ Thanks to all the open source libraries, contributors, and donors. - [ocpsoft/prettytime](https://github.com/ocpsoft/prettytime) - [ramseth001/TextDrawable](https://github.com/ramseth001/TextDrawable) - [vdurmont/emoji-java](https://github.com/vdurmont/emoji-java) -- [Pes8/android-material-color-picker-dialog](https://github.com/Pes8/android-material-color-picker-dialog) +- [skydoves/ColorPickerView](https://github.com/skydoves/ColorPickerView) - [HamidrezaAmz/BreadcrumbsView](https://github.com/HamidrezaAmz/BreadcrumbsView) - [Baseflow/PhotoView](https://github.com/Baseflow/PhotoView) - [apache/commons](https://github.com/apache/commons-io) diff --git a/app/build.gradle b/app/build.gradle index 1988c22a..3dc10f92 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -4,7 +4,6 @@ plugins { apply plugin: 'com.android.application' android { - compileSdkVersion 34 defaultConfig { applicationId "org.mian.gitnex" minSdkVersion 23 @@ -13,8 +12,9 @@ android { versionName "5.3.0-dev" multiDexEnabled true testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + compileSdk 34 } - flavorDimensions "default" + flavorDimensions = ["default"] productFlavors { free { applicationId "org.mian.gitnex" @@ -57,13 +57,13 @@ dependencies { implementation fileTree(include: ['*.jar'], dir: 'libs') implementation 'androidx.appcompat:appcompat:1.6.1' - implementation 'com.google.android.material:material:1.11.0-alpha03' - implementation 'androidx.compose.material3:material3:1.2.0-alpha08' - implementation 'androidx.compose.material3:material3-window-size-class:1.2.0-alpha08' + implementation 'com.google.android.material:material:1.11.0' + implementation 'androidx.compose.material3:material3:1.2.0-beta02' + implementation 'androidx.compose.material3:material3-window-size-class:1.2.0-beta02' implementation 'androidx.viewpager2:viewpager2:1.1.0-beta02' implementation 'androidx.constraintlayout:constraintlayout:2.1.4' implementation "androidx.legacy:legacy-support-v4:1.0.0" - implementation "androidx.lifecycle:lifecycle-viewmodel:2.6.2" + implementation "androidx.lifecycle:lifecycle-viewmodel:2.7.0" testImplementation 'junit:junit:4.13.2' androidTestImplementation 'androidx.test.ext:junit:1.1.5' androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1' @@ -76,7 +76,7 @@ dependencies { implementation 'com.squareup.retrofit2:converter-scalars:2.9.0' implementation 'com.squareup.okhttp3:logging-interceptor:5.0.0-alpha.2' implementation 'org.ocpsoft.prettytime:prettytime:5.0.7.Final' - implementation 'com.github.Pes8:android-material-color-picker-dialog:master' + implementation "com.github.skydoves:colorpickerview:2.3.0" implementation "io.noties.markwon:core:4.6.2" implementation "io.noties.markwon:ext-latex:4.6.2" implementation "io.noties.markwon:ext-strikethrough:4.6.2" @@ -99,18 +99,18 @@ dependencies { implementation 'ch.acra:acra-mail:5.11.2' implementation 'ch.acra:acra-limiter:5.11.2' implementation 'ch.acra:acra-notification:5.11.2' - implementation 'androidx.room:room-runtime:2.5.2' - annotationProcessor 'androidx.room:room-compiler:2.5.2' - implementation "androidx.work:work-runtime:2.8.1" + implementation 'androidx.room:room-runtime:2.6.1' + annotationProcessor 'androidx.room:room-compiler:2.6.1' + implementation "androidx.work:work-runtime:2.9.0" implementation "io.mikael:urlbuilder:2.0.9" implementation "org.codeberg.gitnex-garage:emoji-java:v5.1.2" //noinspection GradleDependency coreLibraryDesugaring "com.android.tools:desugar_jdk_libs:1.1.5" implementation 'androidx.biometric:biometric:1.1.0' implementation 'com.github.chrisvest:stormpot:2.4.2' - implementation 'androidx.browser:browser:1.6.0' + implementation 'androidx.browser:browser:1.7.0' implementation 'com.google.android.flexbox:flexbox:3.0.0' - implementation('org.codeberg.gitnex:tea4j-autodeploy:65f700d036') { + implementation('org.codeberg.gitnex:tea4j-autodeploy:4646f53557') { exclude module: 'org.apache.oltu.oauth2.common' } implementation 'io.github.amrdeveloper:codeview:1.3.8' diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 27eafece..9d0af8f6 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -6,6 +6,7 @@ + + android:configChanges="orientation|screenSize|smallestScreenSize|density|screenLayout|keyboard|keyboardHidden|navigation" + android:windowSoftInputMode="adjustResize"/> + android:configChanges="orientation|screenSize|smallestScreenSize|density|screenLayout|keyboard|keyboardHidden|navigation" + android:windowSoftInputMode="adjustResize"/> + android:configChanges="orientation|screenSize|smallestScreenSize|density|screenLayout|keyboard|keyboardHidden|navigation" + android:windowSoftInputMode="adjustResize"/> @@ -69,13 +73,16 @@ android:theme="@style/AppTheme.NoActionBar"/> + android:configChanges="orientation|screenSize|smallestScreenSize|density|screenLayout|keyboard|keyboardHidden|navigation" + android:windowSoftInputMode="adjustResize"/> + android:configChanges="orientation|screenSize|smallestScreenSize|density|screenLayout|keyboard|keyboardHidden|navigation" + android:windowSoftInputMode="adjustResize"/> + android:configChanges="orientation|screenSize|smallestScreenSize|density|screenLayout|keyboard|keyboardHidden|navigation" + android:windowSoftInputMode="adjustResize"/> + android:configChanges="orientation|screenSize|smallestScreenSize|density|screenLayout|keyboard|keyboardHidden|navigation" + android:windowSoftInputMode="adjustResize"/> + android:configChanges="orientation|screenSize|smallestScreenSize|density|screenLayout|keyboard|keyboardHidden|navigation" + android:windowSoftInputMode="adjustResize"/> + android:configChanges="orientation|screenSize|smallestScreenSize|density|screenLayout|keyboard|keyboardHidden|navigation" + android:windowSoftInputMode="adjustResize"/> + android:configChanges="orientation|screenSize|smallestScreenSize|density|screenLayout|keyboard|keyboardHidden|navigation" + android:windowSoftInputMode="adjustResize"/> @@ -165,7 +176,7 @@ + android:windowSoftInputMode="adjustResize"/> processAddNewEmail(); private ActivityAccountSettingsEmailBinding activityAccountSettingsEmailBinding; @Override @@ -41,56 +35,42 @@ public class AccountSettingsEmailActivity extends BaseActivity { ActivityAccountSettingsEmailBinding.inflate(getLayoutInflater()); setContentView(activityAccountSettingsEmailBinding.getRoot()); - boolean connToInternet = AppUtil.hasNetworkConnection(appCtx); + activityAccountSettingsEmailBinding.topAppBar.setNavigationOnClickListener(v -> finish()); - InputMethodManager imm = - (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE); + activityAccountSettingsEmailBinding.topAppBar.setOnMenuItemClickListener( + menuItem -> { + int id = menuItem.getItemId(); - activityAccountSettingsEmailBinding.userEmail.requestFocus(); - assert imm != null; - imm.showSoftInput( - activityAccountSettingsEmailBinding.userEmail, InputMethodManager.SHOW_IMPLICIT); - - initCloseListener(); - activityAccountSettingsEmailBinding.close.setOnClickListener(onClickListener); - - if (!connToInternet) { - - disableProcessButton(); - } else { - - activityAccountSettingsEmailBinding.addEmailButton.setOnClickListener(addEmailListener); - } + if (id == R.id.save) { + processAddNewEmail(); + return true; + } else { + return super.onOptionsItemSelected(menuItem); + } + }); } private void processAddNewEmail() { - boolean connToInternet = AppUtil.hasNetworkConnection(appCtx); - String newUserEmail = Objects.requireNonNull(activityAccountSettingsEmailBinding.userEmail.getText()) .toString() .trim(); - if (!connToInternet) { - - Toasty.error(ctx, getResources().getString(R.string.checkNetConnection)); - return; - } - if (newUserEmail.equals("")) { - Toasty.error(ctx, getString(R.string.emailErrorEmpty)); + SnackBar.error( + ctx, findViewById(android.R.id.content), getString(R.string.emailErrorEmpty)); return; } else if (!Patterns.EMAIL_ADDRESS.matcher(newUserEmail).matches()) { - Toasty.warning(ctx, getString(R.string.emailErrorInvalid)); + SnackBar.error( + ctx, findViewById(android.R.id.content), getString(R.string.emailErrorInvalid)); return; } List newEmailList = new ArrayList<>(Arrays.asList(newUserEmail.split(","))); - disableProcessButton(); addNewEmail(newEmailList); } @@ -111,54 +91,44 @@ public class AccountSettingsEmailActivity extends BaseActivity { if (response.code() == 201) { - Toasty.success(ctx, getString(R.string.emailAddedText)); + SnackBar.info( + ctx, + findViewById(android.R.id.content), + getString(R.string.emailAddedText)); AccountSettingsEmailsFragment.refreshEmails = true; - enableProcessButton(); - finish(); + new Handler().postDelayed(() -> finish(), 3000); } else if (response.code() == 401) { - enableProcessButton(); AlertDialogs.authorizationTokenRevokedDialog(ctx); } else if (response.code() == 403) { - enableProcessButton(); - Toasty.error(ctx, ctx.getString(R.string.authorizeError)); + SnackBar.error( + ctx, + findViewById(android.R.id.content), + getString(R.string.authorizeError)); } else if (response.code() == 404) { - enableProcessButton(); - Toasty.warning(ctx, ctx.getString(R.string.apiNotFound)); + SnackBar.error( + ctx, + findViewById(android.R.id.content), + getString(R.string.apiNotFound)); } else if (response.code() == 422) { - enableProcessButton(); - Toasty.warning(ctx, ctx.getString(R.string.emailErrorInUse)); + SnackBar.error( + ctx, + findViewById(android.R.id.content), + getString(R.string.emailErrorInUse)); } else { - enableProcessButton(); - Toasty.error(ctx, getString(R.string.genericError)); + SnackBar.error( + ctx, + findViewById(android.R.id.content), + getString(R.string.genericError)); } } @Override - public void onFailure(@NonNull Call> call, @NonNull Throwable t) { - - Log.e("onFailure", t.toString()); - enableProcessButton(); - } + public void onFailure(@NonNull Call> call, @NonNull Throwable t) {} }); } - - private void initCloseListener() { - - onClickListener = view -> finish(); - } - - private void disableProcessButton() { - - activityAccountSettingsEmailBinding.addEmailButton.setEnabled(false); - } - - private void enableProcessButton() { - - activityAccountSettingsEmailBinding.addEmailButton.setEnabled(true); - } } diff --git a/app/src/main/java/org/mian/gitnex/activities/AddNewAccountActivity.java b/app/src/main/java/org/mian/gitnex/activities/AddNewAccountActivity.java index 822d7453..66fc19b5 100644 --- a/app/src/main/java/org/mian/gitnex/activities/AddNewAccountActivity.java +++ b/app/src/main/java/org/mian/gitnex/activities/AddNewAccountActivity.java @@ -3,8 +3,7 @@ package org.mian.gitnex.activities; import android.graphics.Color; import android.graphics.drawable.ColorDrawable; import android.os.Bundle; -import android.util.Log; -import android.view.View; +import android.os.Handler; import android.widget.ArrayAdapter; import androidx.annotation.NonNull; import com.google.android.material.dialog.MaterialAlertDialogBuilder; @@ -22,7 +21,7 @@ import org.mian.gitnex.database.models.UserAccount; import org.mian.gitnex.databinding.ActivityAddNewAccountBinding; import org.mian.gitnex.helpers.AppUtil; import org.mian.gitnex.helpers.PathsHelper; -import org.mian.gitnex.helpers.Toasty; +import org.mian.gitnex.helpers.SnackBar; import org.mian.gitnex.helpers.UrlHelper; import org.mian.gitnex.helpers.Version; import org.mian.gitnex.structs.Protocol; @@ -34,7 +33,6 @@ import retrofit2.Callback; */ public class AddNewAccountActivity extends BaseActivity { - private View.OnClickListener onClickListener; private ActivityAddNewAccountBinding viewBinding; private String spinnerSelectedValue; @@ -52,8 +50,6 @@ public class AddNewAccountActivity extends BaseActivity { getWindow().getDecorView().setBackground(new ColorDrawable(Color.TRANSPARENT)); - initCloseListener(); - viewBinding.close.setOnClickListener(onClickListener); viewBinding.instanceUrl.setText(getIntent().getStringExtra("instanceUrl")); viewBinding.loginToken.setText(getIntent().getStringExtra("token")); String scheme = getIntent().getStringExtra("scheme"); @@ -68,20 +64,22 @@ public class AddNewAccountActivity extends BaseActivity { ArrayAdapter adapterProtocols = new ArrayAdapter<>(ctx, R.layout.list_spinner_items, Protocol.values()); + viewBinding.topAppBar.setNavigationOnClickListener(v -> finish()); + viewBinding.protocolSpinner.setAdapter(adapterProtocols); viewBinding.protocolSpinner.setOnItemClickListener( (parent, view1, position, id) -> spinnerSelectedValue = String.valueOf(parent.getItemAtPosition(position))); - viewBinding.addNewAccount.setOnClickListener( - login -> { - boolean connToInternet = AppUtil.hasNetworkConnection(appCtx); - if (!connToInternet) { - - Toasty.error(ctx, getResources().getString(R.string.checkNetConnection)); - } else { + viewBinding.topAppBar.setOnMenuItemClickListener( + menuItem -> { + int id = menuItem.getItemId(); + if (id == R.id.addAccount) { processLogin(); + return true; + } else { + return super.onOptionsItemSelected(menuItem); } }); } @@ -102,19 +100,26 @@ public class AddNewAccountActivity extends BaseActivity { if (protocol == null) { - Toasty.error(ctx, getResources().getString(R.string.protocolEmptyError)); + SnackBar.error( + ctx, + findViewById(android.R.id.content), + getString(R.string.protocolEmptyError)); return; } if (instanceUrlET.equals("")) { - Toasty.error(ctx, getResources().getString(R.string.emptyFieldURL)); + SnackBar.error( + ctx, findViewById(android.R.id.content), getString(R.string.emptyFieldURL)); return; } if (loginToken.equals("")) { - Toasty.error(ctx, getResources().getString(R.string.loginTokenError)); + SnackBar.error( + ctx, + findViewById(android.R.id.content), + getString(R.string.loginTokenError)); return; } @@ -132,7 +137,8 @@ public class AddNewAccountActivity extends BaseActivity { } catch (Exception e) { - Toasty.error(ctx, getResources().getString(R.string.malformedUrl)); + SnackBar.error( + ctx, findViewById(android.R.id.content), getString(R.string.malformedUrl)); } } @@ -157,8 +163,10 @@ public class AddNewAccountActivity extends BaseActivity { if (!Version.valid(version.getVersion())) { - Toasty.error( - ctx, getResources().getString(R.string.versionUnknown)); + SnackBar.error( + ctx, + findViewById(android.R.id.content), + getString(R.string.versionUnknown)); return; } @@ -192,12 +200,18 @@ public class AddNewAccountActivity extends BaseActivity { login(instanceUrl, loginToken); } else { - Toasty.warning( + SnackBar.error( ctx, - getResources().getString(R.string.versionUnsupportedNew)); + findViewById(android.R.id.content), + getString(R.string.versionUnsupportedNew)); login(instanceUrl, loginToken); } + } else if (responseVersion.code() == 401) { + SnackBar.error( + ctx, + findViewById(android.R.id.content), + getString(R.string.unauthorizedApiError)); } else if (responseVersion.code() == 403) { login(instanceUrl, loginToken); @@ -213,9 +227,10 @@ public class AddNewAccountActivity extends BaseActivity { public void onFailure( @NonNull Call callVersion, @NonNull Throwable t) { - Log.e("onFailure-versionCheck", t.toString()); - Toasty.error( - ctx, getResources().getString(R.string.genericServerResponseError)); + SnackBar.error( + ctx, + findViewById(android.R.id.content), + getString(R.string.genericServerResponseError)); } }); } @@ -291,21 +306,20 @@ public class AddNewAccountActivity extends BaseActivity { defaultPagingNumber); UserAccount account = userAccountsApi.getAccountById((int) id); AppUtil.switchToAccount(AddNewAccountActivity.this, account); - Toasty.success( + SnackBar.success( ctx, - getResources().getString(R.string.accountAddedMessage)); + findViewById(android.R.id.content), + getString(R.string.accountAddedMessage)); MainActivity.refActivity = true; - finish(); + new Handler().postDelayed(() -> finish(), 3000); } else { UserAccount account = userAccountsApi.getAccountByName(accountName); if (account.isLoggedIn()) { - Toasty.warning( + SnackBar.error( ctx, - getResources() - .getString( - R.string - .accountAlreadyExistsError)); + findViewById(android.R.id.content), + getString(R.string.accountAlreadyExistsError)); AppUtil.switchToAccount(ctx, account); } else { userAccountsApi.updateTokenByAccountName( @@ -315,34 +329,31 @@ public class AddNewAccountActivity extends BaseActivity { AddNewAccountActivity.this, account); } } - finish(); break; case 401: - Toasty.error( + SnackBar.error( ctx, - getResources().getString(R.string.unauthorizedApiError)); + findViewById(android.R.id.content), + getString(R.string.unauthorizedApiError)); break; default: - Toasty.error( + SnackBar.error( ctx, - getResources() - .getString( - R.string.genericApiError, response.code())); + findViewById(android.R.id.content), + getString(R.string.genericApiError, response.code())); } } @Override public void onFailure(@NonNull Call call, @NonNull Throwable t) { - Toasty.error(ctx, getResources().getString(R.string.genericError)); + SnackBar.error( + ctx, + findViewById(android.R.id.content), + getString(R.string.genericError)); } }); } - - private void initCloseListener() { - - onClickListener = view -> finish(); - } } diff --git a/app/src/main/java/org/mian/gitnex/activities/BaseActivity.java b/app/src/main/java/org/mian/gitnex/activities/BaseActivity.java index 26c2402e..ce8ee3da 100644 --- a/app/src/main/java/org/mian/gitnex/activities/BaseActivity.java +++ b/app/src/main/java/org/mian/gitnex/activities/BaseActivity.java @@ -94,7 +94,7 @@ public abstract class BaseActivity extends AppCompatActivity { AppUtil.setAppLocale(getResources(), locale); } - Notifications.startWorker(appCtx); + Notifications.startWorker(ctx); } public void onResume() { diff --git a/app/src/main/java/org/mian/gitnex/activities/CreateFileActivity.java b/app/src/main/java/org/mian/gitnex/activities/CreateFileActivity.java index 683b716d..1fe379f0 100644 --- a/app/src/main/java/org/mian/gitnex/activities/CreateFileActivity.java +++ b/app/src/main/java/org/mian/gitnex/activities/CreateFileActivity.java @@ -2,15 +2,13 @@ package org.mian.gitnex.activities; import android.annotation.SuppressLint; import android.app.Activity; -import android.content.Context; import android.content.Intent; import android.os.Bundle; -import android.util.Log; +import android.os.Handler; +import android.view.MenuItem; import android.view.MotionEvent; import android.view.View; -import android.view.inputmethod.InputMethodManager; import android.widget.ArrayAdapter; -import android.widget.TextView; import androidx.activity.result.ActivityResultLauncher; import androidx.activity.result.contract.ActivityResultContracts; import androidx.annotation.NonNull; @@ -29,8 +27,7 @@ import org.mian.gitnex.clients.RetrofitClient; import org.mian.gitnex.databinding.ActivityCreateFileBinding; import org.mian.gitnex.helpers.AlertDialogs; import org.mian.gitnex.helpers.AppUtil; -import org.mian.gitnex.helpers.NetworkStatusObserver; -import org.mian.gitnex.helpers.Toasty; +import org.mian.gitnex.helpers.SnackBar; import org.mian.gitnex.helpers.contexts.RepositoryContext; import retrofit2.Call; import retrofit2.Callback; @@ -72,16 +69,14 @@ public class CreateFileActivity extends BaseActivity { repository = RepositoryContext.fromIntent(getIntent()); - TextView toolbarTitle = binding.toolbarTitle; + binding.topAppBar.setNavigationOnClickListener(v -> finish()); - binding.newFileName.requestFocus(); + MenuItem create = binding.topAppBar.getMenu().getItem(0); + MenuItem update = binding.topAppBar.getMenu().getItem(1); + MenuItem delete = binding.topAppBar.getMenu().getItem(2); + update.setVisible(false); + delete.setVisible(false); - InputMethodManager inputMethodManager = - (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE); - assert inputMethodManager != null; - inputMethodManager.showSoftInput(binding.newFileName, InputMethodManager.SHOW_IMPLICIT); - - binding.close.setOnClickListener(view -> finish()); binding.newFileContent.setOnTouchListener( (touchView, motionEvent) -> { touchView.getParent().requestDisallowInterceptTouchEvent(true); @@ -103,12 +98,13 @@ public class CreateFileActivity extends BaseActivity { filePath = getIntent().getStringExtra("filePath"); fileSha = getIntent().getStringExtra("fileSha"); - toolbarTitle.setText(getString(R.string.deleteGenericTitle, filePath)); - - binding.newFileCreate.setText(R.string.deleteFile); + binding.topAppBar.setTitle(getString(R.string.deleteGenericTitle, filePath)); binding.newFileNameLayout.setVisibility(View.GONE); binding.newFileContentLayout.setVisibility(View.GONE); + delete.setVisible(true); + create.setVisible(false); + update.setVisible(false); } if (getIntent().getStringExtra("filePath") != null @@ -118,20 +114,20 @@ public class CreateFileActivity extends BaseActivity { filePath = getIntent().getStringExtra("filePath"); fileSha = getIntent().getStringExtra("fileSha"); - toolbarTitle.setText(getString(R.string.editFileText, filePath)); + binding.topAppBar.setTitle(getString(R.string.editFileText, filePath)); - binding.newFileCreate.setText(R.string.editFile); binding.newFileName.setText(filePath); binding.newFileName.setEnabled(false); binding.newFileName.setFocusable(false); binding.newFileContent.setText(getIntent().getStringExtra("fileContents")); + update.setVisible(true); + create.setVisible(false); + delete.setVisible(false); } getBranches(repository.getOwner(), repository.getName()); - disableProcessButton(); - binding.openCodeEditor.setOnClickListener( v -> launchCodeEditorActivityForResult( @@ -139,13 +135,23 @@ public class CreateFileActivity extends BaseActivity { FilenameUtils.getExtension( String.valueOf(binding.newFileName.getText())))); - NetworkStatusObserver networkStatusObserver = NetworkStatusObserver.getInstance(ctx); - networkStatusObserver.registerNetworkStatusListener( - hasNetworkConnection -> - runOnUiThread( - () -> binding.newFileCreate.setEnabled(hasNetworkConnection))); + binding.topAppBar.setOnMenuItemClickListener( + menuItem -> { + int id = menuItem.getItemId(); - binding.newFileCreate.setOnClickListener(v -> processNewFile()); + if (id == R.id.create) { + processNewFile(); + return true; + } else if (id == R.id.update) { + processNewFile(); + return true; + } else if (id == R.id.delete) { + processNewFile(); + return true; + } else { + return super.onOptionsItemSelected(menuItem); + } + }); } public void launchCodeEditorActivityForResult(String fileContent, String fileExtension) { @@ -175,29 +181,39 @@ public class CreateFileActivity extends BaseActivity { : ""; if (!AppUtil.hasNetworkConnection(appCtx)) { - Toasty.error(ctx, getResources().getString(R.string.checkNetConnection)); + SnackBar.error( + ctx, + findViewById(android.R.id.content), + getString(R.string.checkNetConnection)); return; } if (((newFileName.isEmpty() || newFileContent.isEmpty()) && fileAction != FILE_ACTION_DELETE) || newFileCommitMessage.isEmpty()) { - Toasty.error(ctx, getString(R.string.newFileRequiredFields)); + SnackBar.error( + ctx, + findViewById(android.R.id.content), + getString(R.string.newFileRequiredFields)); return; } if (!AppUtil.checkStringsWithDash(newFileBranchName)) { - Toasty.error(ctx, getString(R.string.newFileInvalidBranchName)); + SnackBar.error( + ctx, + findViewById(android.R.id.content), + getString(R.string.newFileInvalidBranchName)); return; } if (newFileCommitMessage.length() > 255) { - Toasty.warning(ctx, getString(R.string.newFileCommitMessageError)); + SnackBar.error( + ctx, + findViewById(android.R.id.content), + getString(R.string.newFileCommitMessageError)); return; } - disableProcessButton(); - switch (fileAction) { case FILE_ACTION_CREATE: createNewFile( @@ -264,39 +280,40 @@ public class CreateFileActivity extends BaseActivity { switch (response.code()) { case 201: - enableProcessButton(); - Toasty.success(ctx, getString(R.string.newFileSuccessMessage)); + SnackBar.success( + ctx, + findViewById(android.R.id.content), + getString(R.string.newFileSuccessMessage)); Intent result = new Intent(); result.putExtra("fileModified", true); result.putExtra("fileAction", fileAction); setResult(200, result); RepoDetailActivity.updateFABActions = true; - finish(); + new Handler().postDelayed(() -> finish(), 3000); break; case 401: - enableProcessButton(); AlertDialogs.authorizationTokenRevokedDialog(ctx); break; case 404: - enableProcessButton(); - Toasty.warning(ctx, getString(R.string.apiNotFound)); + SnackBar.error( + ctx, + findViewById(android.R.id.content), + getString(R.string.apiNotFound)); break; default: - enableProcessButton(); - Toasty.error(ctx, getString(R.string.genericError)); + SnackBar.error( + ctx, + findViewById(android.R.id.content), + getString(R.string.genericError)); break; } } @Override - public void onFailure(@NonNull Call call, @NonNull Throwable t) { - - Log.e("onFailure", t.toString()); - enableProcessButton(); - } + public void onFailure(@NonNull Call call, @NonNull Throwable t) {} }); } @@ -332,9 +349,9 @@ public class CreateFileActivity extends BaseActivity { switch (response.code()) { case 200: - enableProcessButton(); - Toasty.info( + SnackBar.success( ctx, + findViewById(android.R.id.content), getString( R.string.deleteFileMessage, repository.getBranchRef())); @@ -342,33 +359,32 @@ public class CreateFileActivity extends BaseActivity { result.putExtra("fileModified", true); result.putExtra("fileAction", fileAction); setResult(200, result); - finish(); + new Handler().postDelayed(() -> finish(), 3000); break; case 401: - enableProcessButton(); AlertDialogs.authorizationTokenRevokedDialog(ctx); break; case 404: - enableProcessButton(); - Toasty.info(ctx, getString(R.string.apiNotFound)); + SnackBar.error( + ctx, + findViewById(android.R.id.content), + getString(R.string.apiNotFound)); break; default: - enableProcessButton(); - Toasty.info(ctx, getString(R.string.genericError)); + SnackBar.error( + ctx, + findViewById(android.R.id.content), + getString(R.string.genericError)); break; } } @Override public void onFailure( - @NonNull Call call, @NonNull Throwable t) { - - Log.e("onFailure", t.toString()); - enableProcessButton(); - } + @NonNull Call call, @NonNull Throwable t) {} }); } @@ -406,38 +422,39 @@ public class CreateFileActivity extends BaseActivity { switch (response.code()) { case 200: - enableProcessButton(); - Toasty.info(ctx, getString(R.string.editFileMessage, branchName)); + SnackBar.success( + ctx, + findViewById(android.R.id.content), + getString(R.string.editFileMessage, branchName)); Intent result = new Intent(); result.putExtra("fileModified", true); result.putExtra("fileAction", fileAction); setResult(200, result); - finish(); + new Handler().postDelayed(() -> finish(), 3000); break; case 401: - enableProcessButton(); AlertDialogs.authorizationTokenRevokedDialog(ctx); break; case 404: - enableProcessButton(); - Toasty.info(ctx, getString(R.string.apiNotFound)); + SnackBar.error( + ctx, + findViewById(android.R.id.content), + getString(R.string.apiNotFound)); break; default: - enableProcessButton(); - Toasty.info(ctx, getString(R.string.genericError)); + SnackBar.error( + ctx, + findViewById(android.R.id.content), + getString(R.string.genericError)); break; } } @Override - public void onFailure(@NonNull Call call, @NonNull Throwable t) { - - Log.e("onFailure", t.toString()); - enableProcessButton(); - } + public void onFailure(@NonNull Call call, @NonNull Throwable t) {} }); } @@ -468,27 +485,14 @@ public class CreateFileActivity extends BaseActivity { binding.newFileBranches.setAdapter(adapter); binding.newFileBranches.setText(repository.getBranchRef(), false); - - enableProcessButton(); } } @Override - public void onFailure(@NonNull Call> call, @NonNull Throwable t) { - - Log.e("onFailure", t.toString()); - } + public void onFailure(@NonNull Call> call, @NonNull Throwable t) {} }); } - private void disableProcessButton() { - binding.newFileCreate.setEnabled(false); - } - - private void enableProcessButton() { - binding.newFileCreate.setEnabled(true); - } - @Override public void onResume() { super.onResume(); diff --git a/app/src/main/java/org/mian/gitnex/activities/CreateIssueActivity.java b/app/src/main/java/org/mian/gitnex/activities/CreateIssueActivity.java index 95171c6b..5fa8727f 100644 --- a/app/src/main/java/org/mian/gitnex/activities/CreateIssueActivity.java +++ b/app/src/main/java/org/mian/gitnex/activities/CreateIssueActivity.java @@ -1,27 +1,37 @@ package org.mian.gitnex.activities; import android.annotation.SuppressLint; -import android.app.DatePickerDialog; -import android.content.Context; +import android.app.Activity; +import android.content.Intent; +import android.net.Uri; import android.os.Bundle; +import android.os.Handler; import android.view.LayoutInflater; -import android.view.Menu; -import android.view.MenuInflater; -import android.view.MenuItem; import android.view.MotionEvent; import android.view.View; -import android.view.inputmethod.InputMethodManager; import android.widget.ArrayAdapter; import android.widget.TextView; +import androidx.activity.result.ActivityResultLauncher; +import androidx.activity.result.contract.ActivityResultContracts; import androidx.annotation.NonNull; +import androidx.recyclerview.widget.LinearLayoutManager; +import com.google.android.material.bottomsheet.BottomSheetDialog; +import com.google.android.material.datepicker.MaterialDatePicker; import com.google.android.material.dialog.MaterialAlertDialogBuilder; import com.vdurmont.emoji.EmojiParser; +import java.io.File; +import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Calendar; import java.util.Date; import java.util.LinkedHashMap; import java.util.List; +import java.util.Locale; import java.util.Objects; +import java.util.TimeZone; +import okhttp3.MediaType; +import okhttp3.RequestBody; +import org.gitnex.tea4j.v2.models.Attachment; import org.gitnex.tea4j.v2.models.CreateIssueOption; import org.gitnex.tea4j.v2.models.Issue; import org.gitnex.tea4j.v2.models.Label; @@ -31,17 +41,20 @@ import org.mian.gitnex.R; import org.mian.gitnex.actions.AssigneesActions; import org.mian.gitnex.actions.LabelsActions; import org.mian.gitnex.adapters.AssigneesListAdapter; +import org.mian.gitnex.adapters.AttachmentsAdapter; import org.mian.gitnex.adapters.LabelsListAdapter; import org.mian.gitnex.clients.RetrofitClient; import org.mian.gitnex.databinding.ActivityCreateIssueBinding; +import org.mian.gitnex.databinding.BottomSheetAttachmentsBinding; import org.mian.gitnex.databinding.CustomAssigneesSelectionDialogBinding; import org.mian.gitnex.databinding.CustomLabelsSelectionDialogBinding; import org.mian.gitnex.fragments.IssuesFragment; import org.mian.gitnex.helpers.AlertDialogs; -import org.mian.gitnex.helpers.AppUtil; import org.mian.gitnex.helpers.Constants; import org.mian.gitnex.helpers.Markdown; -import org.mian.gitnex.helpers.Toasty; +import org.mian.gitnex.helpers.SnackBar; +import org.mian.gitnex.helpers.attachments.AttachmentUtils; +import org.mian.gitnex.helpers.attachments.AttachmentsModel; import org.mian.gitnex.helpers.contexts.RepositoryContext; import retrofit2.Call; import retrofit2.Callback; @@ -50,17 +63,15 @@ import retrofit2.Callback; * @author M M Arif */ public class CreateIssueActivity extends BaseActivity - implements View.OnClickListener, - LabelsListAdapter.LabelsListAdapterListener, - AssigneesListAdapter.AssigneesListAdapterListener { + implements LabelsListAdapter.LabelsListAdapterListener, + AssigneesListAdapter.AssigneesListAdapterListener, + AttachmentsAdapter.AttachmentsReceiverListener { private final List