From cdfc2cfb9ecca53e3aecd7cc6157e80d4b2b7df1 Mon Sep 17 00:00:00 2001 From: M M Arif Date: Sat, 4 Jul 2020 22:51:55 +0200 Subject: [PATCH] Implement drafts, introduce Room persistence library for db (#139) Fix no draft message translation updates format improvements typo update some renaming refactors Use better naming convention remove duplicate source arrange draft titles enhance click listener area Launch drafts from reply screen and clean up Add message draft saved update repositories tasks Update user accounts repository with thread, remove async tasks remove async task in drafts update layout, change async to thread in drafts Merge branch 'master' into pull_139 # Conflicts: # app/build.gradle # app/src/main/java/org/mian/gitnex/activities/LoginActivity.java Merge branch 'master' into pull_139 # Conflicts: # app/src/main/java/org/mian/gitnex/activities/ReplyToIssueActivity.java Merge branch 'pull_139' of codeberg.org:gitnex/GitNex into pull_139 # Conflicts: # app/src/main/java/org/mian/gitnex/adapters/MyReposListAdapter.java Merge branch 'master' into pull_139 # Conflicts: # app/src/main/java/org/mian/gitnex/activities/LoginActivity.java # app/src/main/java/org/mian/gitnex/activities/ReplyToIssueActivity.java # app/src/main/java/org/mian/gitnex/adapters/MyReposListAdapter.java Merge branch 'master' into pull_139 Merge branch 'master' into pull_139 Merge branch 'master' into pull_139 and fix conflicts # Conflicts: # app/build.gradle # app/src/main/java/org/mian/gitnex/activities/LoginActivity.java # app/src/main/java/org/mian/gitnex/activities/MainActivity.java # app/src/main/java/org/mian/gitnex/activities/ReplyToIssueActivity.java # app/src/main/java/org/mian/gitnex/adapters/MyReposListAdapter.java # app/src/main/java/org/mian/gitnex/helpers/StaticGlobalVariables.java # app/src/main/res/values/strings.xml Code Format Merge branch 'master' into 15-comments-draft Merge branch 'master' into 15-comments-draft Merge branch 'master' into 15-comments-draft # Conflicts: # app/src/main/java/org/mian/gitnex/activities/MainActivity.java # app/src/main/res/values/strings.xml Go to draft, save on type and other fixes delete all drafts, added messages where needed delete draft Force logout Merge branch 'master' into 15-comments-draft Merge branch 'master' into 15-comments-draft # Conflicts: # app/src/main/java/org/mian/gitnex/activities/MainActivity.java # app/src/main/java/org/mian/gitnex/helpers/StaticGlobalVariables.java check if account data is null, we need to log the user out for the 1st time. Merge branch 'master' into 15-comments-draft fix repo owner, name sequence Add comments for test, show drafts list Add repos to db Add account to db and other refactors to the code Merge branch 'master' into 15-comments-draft Merge branch 'master' into 15-comments-draft Merge branch 'master' into 15-comments-draft Merge branch 'master' into 15-comments-draft # Conflicts: # app/build.gradle # app/src/main/AndroidManifest.xml Merge branch 'master' into 15-comments-draft merge more queries, added dao repositories, layout update Added queries in dao some refactor. added models, dao, entities (accounts, repositories, drafts) WIP on implementing drafts, introduced Room persistence library for db. Co-authored-by: M M Arif Co-authored-by: 6543 <6543@obermui.de> Reviewed-on: https://codeberg.org/gitnex/GitNex/pulls/139 Reviewed-by: opyale --- app/build.gradle | 2 + .../mian/gitnex/activities/LoginActivity.java | 29 ++ .../mian/gitnex/activities/MainActivity.java | 104 +++- .../activities/ReplyToIssueActivity.java | 464 +++++++++++------- .../mian/gitnex/adapters/DraftsAdapter.java | 127 +++++ .../adapters/ExploreRepositoriesAdapter.java | 30 +- .../gitnex/adapters/MyReposListAdapter.java | 434 ++++++++-------- .../gitnex/adapters/ReposListAdapter.java | 30 +- .../adapters/RepositoriesByOrgAdapter.java | 30 +- .../adapters/StarredReposListAdapter.java | 30 +- .../mian/gitnex/database/api/DraftsApi.java | 109 ++++ .../gitnex/database/api/RepositoriesApi.java | 140 ++++++ .../gitnex/database/api/UserAccountsApi.java | 98 ++++ .../mian/gitnex/database/dao/DraftsDao.java | 53 ++ .../gitnex/database/dao/RepositoriesDao.java | 47 ++ .../gitnex/database/dao/UserAccountsDao.java | 53 ++ .../gitnex/database/db/GitnexDatabase.java | 55 +++ .../mian/gitnex/database/models/Draft.java | 89 ++++ .../database/models/DraftWithRepository.java | 122 +++++ .../gitnex/database/models/Repository.java | 59 +++ .../gitnex/database/models/UserAccount.java | 75 +++ .../fragments/BottomSheetDraftsFragment.java | 58 +++ .../mian/gitnex/fragments/DraftsFragment.java | 138 ++++++ .../org/mian/gitnex/helpers/AlertDialogs.java | 23 + .../gitnex/helpers/StaticGlobalVariables.java | 9 + app/src/main/res/drawable/ic_drafts_24dp.xml | 5 + .../res/layout/activity_reply_to_issue.xml | 10 + .../main/res/layout/bottom_sheet_drafts.xml | 36 ++ app/src/main/res/layout/fragment_drafts.xml | 35 ++ app/src/main/res/layout/list_drafts.xml | 88 ++++ app/src/main/res/menu/drawer_menu.xml | 5 +- app/src/main/res/menu/reply_to_issue.xml | 13 + app/src/main/res/values/strings.xml | 11 + .../metadata/android/en-US/changelogs/24.txt | 34 +- .../metadata/android/en-US/changelogs/70.txt | 2 +- .../metadata/android/en-US/changelogs/80.txt | 2 +- .../android/en-US/full_description.txt | 96 ++-- 37 files changed, 2280 insertions(+), 465 deletions(-) create mode 100644 app/src/main/java/org/mian/gitnex/adapters/DraftsAdapter.java create mode 100644 app/src/main/java/org/mian/gitnex/database/api/DraftsApi.java create mode 100644 app/src/main/java/org/mian/gitnex/database/api/RepositoriesApi.java create mode 100644 app/src/main/java/org/mian/gitnex/database/api/UserAccountsApi.java create mode 100644 app/src/main/java/org/mian/gitnex/database/dao/DraftsDao.java create mode 100644 app/src/main/java/org/mian/gitnex/database/dao/RepositoriesDao.java create mode 100644 app/src/main/java/org/mian/gitnex/database/dao/UserAccountsDao.java create mode 100644 app/src/main/java/org/mian/gitnex/database/db/GitnexDatabase.java create mode 100644 app/src/main/java/org/mian/gitnex/database/models/Draft.java create mode 100644 app/src/main/java/org/mian/gitnex/database/models/DraftWithRepository.java create mode 100644 app/src/main/java/org/mian/gitnex/database/models/Repository.java create mode 100644 app/src/main/java/org/mian/gitnex/database/models/UserAccount.java create mode 100644 app/src/main/java/org/mian/gitnex/fragments/BottomSheetDraftsFragment.java create mode 100644 app/src/main/java/org/mian/gitnex/fragments/DraftsFragment.java create mode 100644 app/src/main/res/drawable/ic_drafts_24dp.xml create mode 100644 app/src/main/res/layout/bottom_sheet_drafts.xml create mode 100644 app/src/main/res/layout/fragment_drafts.xml create mode 100644 app/src/main/res/layout/list_drafts.xml create mode 100644 app/src/main/res/menu/reply_to_issue.xml diff --git a/app/build.gradle b/app/build.gradle index bc0587ce..07e191e4 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -85,6 +85,8 @@ dependencies { implementation "ch.acra:acra-mail:$acra" implementation "ch.acra:acra-limiter:$acra" implementation "ch.acra:acra-notification:$acra" + implementation "androidx.room:room-runtime:2.2.5" + annotationProcessor "androidx.room:room-compiler:2.2.5" implementation "com.eightbitlab:blurview:1.6.3" implementation "io.mikael:urlbuilder:2.0.9" diff --git a/app/src/main/java/org/mian/gitnex/activities/LoginActivity.java b/app/src/main/java/org/mian/gitnex/activities/LoginActivity.java index 8cca6ed9..c2c4c5d8 100644 --- a/app/src/main/java/org/mian/gitnex/activities/LoginActivity.java +++ b/app/src/main/java/org/mian/gitnex/activities/LoginActivity.java @@ -21,6 +21,7 @@ import androidx.appcompat.app.AlertDialog; import com.tooltip.Tooltip; import org.mian.gitnex.R; import org.mian.gitnex.clients.RetrofitClient; +import org.mian.gitnex.database.api.UserAccountsApi; import org.mian.gitnex.helpers.NetworkObserver; import org.mian.gitnex.helpers.PathsHelper; import org.mian.gitnex.helpers.SnackBar; @@ -380,6 +381,15 @@ public class LoginActivity extends BaseActivity { tinyDB.putString("loginUid", userDetails.getLogin()); tinyDB.putString("userLogin", userDetails.getUsername()); + // insert new account to db if does not exist + String accountName = userDetails.getUsername() + "@" + instanceUrl; + UserAccountsApi userAccountsApi = new UserAccountsApi(ctx); + int checkAccount = userAccountsApi.getCount(accountName); + + if(checkAccount == 0) { + userAccountsApi.insertNewAccount(accountName, instanceUrl, userDetails.getUsername(), loginToken, ""); + } + enableProcessButton(); startActivity(new Intent(LoginActivity.this, MainActivity.class)); finish(); @@ -517,6 +527,15 @@ public class LoginActivity extends BaseActivity { tinyDB.putString(loginUid + "-token", newToken.getSha1()); tinyDB.putString(loginUid + "-token-last-eight", appUtil.getLastCharactersOfWord(newToken.getSha1(), 8)); + // insert new account to db if does not exist + String accountName = userDetails.getUsername() + "@" + instanceUrl; + UserAccountsApi userAccountsApi = new UserAccountsApi(ctx); + int checkAccount = userAccountsApi.getCount(accountName); + + if(checkAccount == 0) { + userAccountsApi.insertNewAccount(accountName, instanceUrl, userDetails.getUsername(), newToken.getSha1(), ""); + } + startActivity(new Intent(LoginActivity.this, MainActivity.class)); finish(); break; @@ -581,6 +600,16 @@ public class LoginActivity extends BaseActivity { assert userDetails != null; tinyDB.putString("userLogin", userDetails.getUsername()); tinyDB.putBoolean("loggedInMode", true); + + // insert new account to db if does not exist + String accountName = userDetails.getUsername() + "@" + instanceUrl; + UserAccountsApi userAccountsApi = new UserAccountsApi(ctx); + int checkAccount = userAccountsApi.getCount(accountName); + + if(checkAccount == 0) { + userAccountsApi.insertNewAccount(accountName, instanceUrl, userDetails.getUsername(), instanceToken, ""); + } + startActivity(new Intent(LoginActivity.this, MainActivity.class)); finish(); break; diff --git a/app/src/main/java/org/mian/gitnex/activities/MainActivity.java b/app/src/main/java/org/mian/gitnex/activities/MainActivity.java index 7e95e9f8..5f78c196 100644 --- a/app/src/main/java/org/mian/gitnex/activities/MainActivity.java +++ b/app/src/main/java/org/mian/gitnex/activities/MainActivity.java @@ -17,6 +17,7 @@ import android.widget.ImageView; import android.widget.TextView; import androidx.annotation.NonNull; import androidx.appcompat.app.ActionBarDrawerToggle; +import androidx.appcompat.app.AlertDialog; import androidx.appcompat.widget.Toolbar; import androidx.core.view.GravityCompat; import androidx.drawerlayout.widget.DrawerLayout; @@ -26,7 +27,11 @@ import com.google.android.material.navigation.NavigationView; import org.mian.gitnex.R; import org.mian.gitnex.clients.PicassoService; import org.mian.gitnex.clients.RetrofitClient; +import org.mian.gitnex.database.models.UserAccount; +import org.mian.gitnex.database.api.UserAccountsApi; import org.mian.gitnex.fragments.AboutFragment; +import org.mian.gitnex.fragments.BottomSheetDraftsFragment; +import org.mian.gitnex.fragments.DraftsFragment; import org.mian.gitnex.fragments.AdministrationFragment; import org.mian.gitnex.fragments.ExploreRepositoriesFragment; import org.mian.gitnex.fragments.MyRepositoriesFragment; @@ -46,6 +51,7 @@ import org.mian.gitnex.models.UserInfo; import org.mian.gitnex.util.AppUtil; import org.mian.gitnex.util.TinyDB; import java.util.Objects; +import java.util.concurrent.ExecutionException; import eightbitlab.com.blurview.BlurView; import eightbitlab.com.blurview.RenderScriptBlur; import retrofit2.Call; @@ -55,7 +61,7 @@ import retrofit2.Callback; * Author M M Arif */ -public class MainActivity extends BaseActivity implements NavigationView.OnNavigationItemSelectedListener { +public class MainActivity extends BaseActivity implements NavigationView.OnNavigationItemSelectedListener, BottomSheetDraftsFragment.BottomSheetListener { private DrawerLayout drawer; private BlurView blurView; @@ -85,6 +91,9 @@ public class MainActivity extends BaseActivity implements NavigationView.OnNavig tinyDb.putBoolean("noConnection", false); //userAvatar = findViewById(R.id.userAvatar); + Intent mainIntent = getIntent(); + String launchFragment = mainIntent.getStringExtra("launchFragment"); + final String instanceUrl = tinyDb.getString("instanceUrl"); final String loginUid = tinyDb.getString("loginUid"); final String instanceToken = "token " + tinyDb.getString(loginUid + "-token"); @@ -113,6 +122,14 @@ public class MainActivity extends BaseActivity implements NavigationView.OnNavig return; } + String accountName = loginUid + "@" + instanceUrl; + try { + getAccountData(accountName); + } + catch(ExecutionException | InterruptedException e) { + Log.e("getAccountData", e.toString()); + } + Toolbar toolbar = findViewById(R.id.toolbar); toolbarTitle = toolbar.findViewById(R.id.toolbar_title); @@ -159,6 +176,9 @@ public class MainActivity extends BaseActivity implements NavigationView.OnNavig else if(fragmentById instanceof AboutFragment) { toolbarTitle.setText(getResources().getString(R.string.pageTitleAbout)); } + else if(fragmentById instanceof DraftsFragment) { + toolbarTitle.setText(getResources().getString(R.string.titleDrafts)); + } else if(fragmentById instanceof AdministrationFragment) { toolbarTitle.setText(getResources().getString(R.string.pageTitleAdministration)); } @@ -263,6 +283,18 @@ public class MainActivity extends BaseActivity implements NavigationView.OnNavig toggle.syncState(); + if(launchFragment != null) { + + if(launchFragment.equals("drafts")) { + + getSupportFragmentManager().beginTransaction().replace(R.id.fragment_container, new DraftsFragment()).commit(); + toolbarTitle.setText(getResources().getString(R.string.titleDrafts)); + navigationView.setCheckedItem(R.id.nav_comments_draft); + mainIntent.removeExtra("launchFragment"); + return; + } + } + if(savedInstanceState == null) { switch(tinyDb.getInt("homeScreenId")) { @@ -336,9 +368,55 @@ public class MainActivity extends BaseActivity implements NavigationView.OnNavig } } - public void setActionBarTitle(@NonNull String title) { + @Override + public void onButtonClicked(String text) { + + TinyDB tinyDb = new TinyDB(ctx); + int currentActiveAccountId = tinyDb.getInt("currentActiveAccountId"); + + if("deleteDrafts".equals(text)) { + + if(currentActiveAccountId > 0) { + + FragmentManager fm = getSupportFragmentManager(); + DraftsFragment frag = (DraftsFragment) fm.findFragmentById(R.id.fragment_container); + + if(frag != null) { + + new AlertDialog.Builder(ctx).setTitle(R.string.deleteAllDrafts).setIcon(R.drawable.ic_delete).setCancelable(false).setMessage(R.string.deleteAllDraftsDialogMessage).setPositiveButton(R.string.menuDeleteText, (dialog, which) -> { + + frag.deleteAllDrafts(currentActiveAccountId); + dialog.dismiss(); + + }).setNegativeButton(R.string.cancelButton, (dialog, which) -> dialog.dismiss()).show(); + + } + else { + Toasty.error(ctx, getResources().getString(R.string.genericError)); + } + + } + else { + Toasty.error(ctx, getResources().getString(R.string.genericError)); + } + + } + + } + + public void getAccountData(String accountName) throws ExecutionException, InterruptedException { + + UserAccountsApi accountData = new UserAccountsApi(ctx); + UserAccount data = accountData.getAccountData(accountName); + + if(data != null) { + TinyDB tinyDb = new TinyDB(ctx.getApplicationContext()); + tinyDb.putInt("currentActiveAccountId", data.getAccountId()); + } + else { + AlertDialogs.forceLogoutDialog(ctx, getResources().getString(R.string.forceLogoutDialogHeader), getResources().getString(R.string.forceLogoutDialogDescription), getResources().getString(R.string.alertDialogTokenRevokedCopyPositiveButton)); + } - Objects.requireNonNull(getSupportActionBar()).setTitle(title); } @Override @@ -407,6 +485,11 @@ public class MainActivity extends BaseActivity implements NavigationView.OnNavig getSupportFragmentManager().beginTransaction().replace(R.id.fragment_container, new ExploreRepositoriesFragment()).commit(); break; + case R.id.nav_comments_draft: + toolbarTitle.setText(getResources().getString(R.string.titleDrafts)); + getSupportFragmentManager().beginTransaction().replace(R.id.fragment_container, new DraftsFragment()).commit(); + break; + case R.id.nav_administration: toolbarTitle.setText(getResources().getString(R.string.pageTitleAdministration)); getSupportFragmentManager().beginTransaction().replace(R.id.fragment_container, new AdministrationFragment()).commit(); @@ -440,6 +523,21 @@ public class MainActivity extends BaseActivity implements NavigationView.OnNavig } + @Override + public boolean onOptionsItemSelected(MenuItem item) { + + int id = item.getItemId(); + + if(id == R.id.genericMenu) { + BottomSheetDraftsFragment bottomSheet = new BottomSheetDraftsFragment(); + bottomSheet.show(getSupportFragmentManager(), "draftsBottomSheet"); + return true; + } + + return super.onOptionsItemSelected(item); + + } + private void giteaVersion(final String instanceUrl) { final TinyDB tinyDb = new TinyDB(appCtx); diff --git a/app/src/main/java/org/mian/gitnex/activities/ReplyToIssueActivity.java b/app/src/main/java/org/mian/gitnex/activities/ReplyToIssueActivity.java index 89f18ee0..645fe031 100644 --- a/app/src/main/java/org/mian/gitnex/activities/ReplyToIssueActivity.java +++ b/app/src/main/java/org/mian/gitnex/activities/ReplyToIssueActivity.java @@ -1,33 +1,42 @@ package org.mian.gitnex.activities; -import androidx.annotation.NonNull; -import retrofit2.Call; -import retrofit2.Callback; -import retrofit2.Response; import android.content.Context; +import android.content.Intent; import android.graphics.drawable.GradientDrawable; import android.os.Bundle; +import android.text.Editable; +import android.text.TextWatcher; import android.util.Log; +import android.view.Menu; +import android.view.MenuInflater; +import android.view.MenuItem; import android.view.View; import android.view.inputmethod.InputMethodManager; import android.widget.ArrayAdapter; import android.widget.Button; import android.widget.ImageView; import android.widget.TextView; +import androidx.annotation.NonNull; +import androidx.appcompat.widget.Toolbar; import com.hendraanggrian.appcompat.socialview.Mention; import com.hendraanggrian.appcompat.widget.MentionArrayAdapter; import com.hendraanggrian.appcompat.widget.SocialAutoCompleteTextView; import org.mian.gitnex.R; import org.mian.gitnex.actions.IssueActions; import org.mian.gitnex.clients.RetrofitClient; +import org.mian.gitnex.database.api.DraftsApi; import org.mian.gitnex.helpers.AlertDialogs; import org.mian.gitnex.helpers.Authorization; +import org.mian.gitnex.helpers.StaticGlobalVariables; import org.mian.gitnex.helpers.Toasty; import org.mian.gitnex.models.Collaborators; import org.mian.gitnex.models.Issues; import org.mian.gitnex.util.AppUtil; import org.mian.gitnex.util.TinyDB; import java.util.List; +import retrofit2.Call; +import retrofit2.Callback; +import retrofit2.Response; /** * Author M M Arif @@ -35,264 +44,361 @@ import java.util.List; public class ReplyToIssueActivity extends BaseActivity { - public ImageView closeActivity; - private View.OnClickListener onClickListener; + public ImageView closeActivity; + private View.OnClickListener onClickListener; - final Context ctx = this; - private Context appCtx; + final Context ctx = this; + private Context appCtx; - private SocialAutoCompleteTextView addComment; - private ArrayAdapter defaultMentionAdapter; - private Button replyButton; + private TextView draftSaved; + private SocialAutoCompleteTextView addComment; + private ArrayAdapter defaultMentionAdapter; + private Button replyButton; + private String TAG = StaticGlobalVariables.replyToIssueActivity; - @Override - protected int getLayoutResourceId(){ - return R.layout.activity_reply_to_issue; - } + @Override + protected int getLayoutResourceId(){ + return R.layout.activity_reply_to_issue; + } - @Override - public void onCreate(Bundle savedInstanceState) { + @Override + public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - appCtx = getApplicationContext(); + super.onCreate(savedInstanceState); + appCtx = getApplicationContext(); + Toolbar toolbar = findViewById(R.id.toolbar); + setSupportActionBar(toolbar); - InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE); + InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE); boolean connToInternet = AppUtil.hasNetworkConnection(appCtx); TinyDB tinyDb = new TinyDB(appCtx); - addComment = findViewById(R.id.addComment); - addComment.setShowSoftInputOnFocus(true); + draftSaved = findViewById(R.id.draftSaved); + addComment = findViewById(R.id.addComment); + addComment.setShowSoftInputOnFocus(true); defaultMentionAdapter = new MentionArrayAdapter<>(ctx); - loadCollaboratorsList(); + loadCollaboratorsList(); - addComment.setMentionAdapter(defaultMentionAdapter); + addComment.setMentionAdapter(defaultMentionAdapter); - closeActivity = findViewById(R.id.close); - TextView toolbar_title = findViewById(R.id.toolbar_title); + closeActivity = findViewById(R.id.close); + TextView toolbar_title = findViewById(R.id.toolbar_title); - addComment.requestFocus(); - assert imm != null; - imm.showSoftInput(addComment, InputMethodManager.SHOW_IMPLICIT); + addComment.requestFocus(); + assert imm != null; + imm.showSoftInput(addComment, InputMethodManager.SHOW_IMPLICIT); - if(!tinyDb.getString("issueTitle").isEmpty()) { - toolbar_title.setText(tinyDb.getString("issueTitle")); - } + if(!tinyDb.getString("issueTitle").isEmpty()) { + toolbar_title.setText(tinyDb.getString("issueTitle")); + } - initCloseListener(); - closeActivity.setOnClickListener(onClickListener); + initCloseListener(); + closeActivity.setOnClickListener(onClickListener); - replyButton = findViewById(R.id.replyButton); + replyButton = findViewById(R.id.replyButton); - if(getIntent().getStringExtra("commentBody") != null) { + if(getIntent().getStringExtra("commentBody") != null) { - addComment.setText(getIntent().getStringExtra("commentBody")); + addComment.setText(getIntent().getStringExtra("commentBody")); - if(getIntent().getBooleanExtra("cursorToEnd", false)) { - addComment.setSelection(addComment.length()); - } + if(getIntent().getBooleanExtra("cursorToEnd", false)) { + addComment.setSelection(addComment.length()); + } - } + } - if(getIntent().getStringExtra("commentAction") != null && getIntent().getStringExtra("commentAction").equals("edit")) { + if(getIntent().getStringExtra("draftTitle") != null) { - final String commentId = getIntent().getStringExtra("commentId"); + toolbar_title.setText(getIntent().getStringExtra("draftTitle")); - toolbar_title.setText(getResources().getString(R.string.editCommentTitle)); - replyButton.setText(getResources().getString(R.string.editCommentButtonText)); + } - replyButton.setOnClickListener(v -> { - disableProcessButton(); - IssueActions.editIssueComment(ctx, Integer.parseInt(commentId), addComment.getText().toString()); - }); + if(getIntent().getStringExtra("commentAction") != null && getIntent().getStringExtra("commentAction").equals("edit")) { - return; + final String commentId = getIntent().getStringExtra("commentId"); - } + toolbar_title.setText(getResources().getString(R.string.editCommentTitle)); + replyButton.setText(getResources().getString(R.string.editCommentButtonText)); - if(!connToInternet) { + addComment.addTextChangedListener(new TextWatcher() { - disableProcessButton(); + public void afterTextChanged(Editable s) { - } else { + } - replyButton.setOnClickListener(replyToIssue); + public void beforeTextChanged(CharSequence s, int start, int count, int after) { - } + } - } + public void onTextChanged(CharSequence s, int start, int before, int count) { - public void loadCollaboratorsList() { + saveDraft(addComment.getText().toString()); + draftSaved.setVisibility(View.VISIBLE); - final TinyDB tinyDb = new TinyDB(appCtx); + } - 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> call = RetrofitClient - .getInstance(instanceUrl, ctx) - .getApiInterface() - .getCollaborators(Authorization.returnAuthentication(ctx, loginUid, instanceToken), repoOwner, repoName); + replyButton.setOnClickListener(v -> { - call.enqueue(new Callback>() { + disableProcessButton(); + assert commentId != null; + IssueActions.editIssueComment(ctx, Integer.parseInt(commentId), addComment.getText().toString()); - @Override - public void onResponse(@NonNull Call> call, @NonNull Response> response) { + }); - if (response.isSuccessful()) { + return; - assert response.body() != null; - String fullName = ""; - for (int i = 0; i < response.body().size(); i++) { - if(!response.body().get(i).getFull_name().equals("")) { - fullName = response.body().get(i).getFull_name(); - } - defaultMentionAdapter.add( - new Mention(response.body().get(i).getUsername(), fullName, response.body().get(i).getAvatar_url())); - } + } - } else { + addComment.addTextChangedListener(new TextWatcher() { - Log.i("onResponse", String.valueOf(response.code())); + public void afterTextChanged(Editable s) { - } + } - } + public void beforeTextChanged(CharSequence s, int start, int count, int after) { - @Override - public void onFailure(@NonNull Call> call, @NonNull Throwable t) { - Log.i("onFailure", t.getMessage()); - } + } - }); - } + public void onTextChanged(CharSequence s, int start, int before, int count) { - private void initCloseListener() { - onClickListener = new View.OnClickListener() { - @Override - public void onClick(View view) { - finish(); - } - }; - } + saveDraft(addComment.getText().toString()); + draftSaved.setVisibility(View.VISIBLE); - private View.OnClickListener replyToIssue = new View.OnClickListener() { - public void onClick(View v) { - processNewCommentReply(); - } - }; + } - private void processNewCommentReply() { + }); - String newReplyDT = addComment.getText().toString(); - boolean connToInternet = AppUtil.hasNetworkConnection(appCtx); + if(!connToInternet) { - if(!connToInternet) { + disableProcessButton(); - Toasty.info(ctx, getResources().getString(R.string.checkNetConnection)); - return; + } + else { - } + replyButton.setOnClickListener(replyToIssue); - if(newReplyDT.equals("")) { + } - Toasty.info(ctx, getString(R.string.commentEmptyError)); + } - } - else { + private void saveDraft(String draftText) { - disableProcessButton(); - replyComment(newReplyDT); + TinyDB tinyDb = new TinyDB(getApplicationContext()); - } + int repositoryId = (int) tinyDb.getLong("repositoryId", 0); + int currentActiveAccountId = tinyDb.getInt("currentActiveAccountId"); + int issueNumber = Integer.parseInt(tinyDb.getString("issueNumber")); - } + DraftsApi draftsApi = new DraftsApi(getApplicationContext()); - private void replyComment(String newReplyDT) { + int countDraft = draftsApi.checkDraft(issueNumber, repositoryId); - final TinyDB tinyDb = new TinyDB(appCtx); + if(countDraft == 0) { + long draftId = draftsApi.insertDraft(repositoryId, currentActiveAccountId, issueNumber, draftText, StaticGlobalVariables.draftTypeComment); + } + else { + DraftsApi.updateDraftByIssueIdAsyncTask(draftText, issueNumber, repositoryId); + } - 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]; - final int issueIndex = Integer.parseInt(tinyDb.getString("issueNumber")); + } - Issues issueComment = new Issues(newReplyDT); + public void loadCollaboratorsList() { - Call call = RetrofitClient - .getInstance(instanceUrl, ctx) - .getApiInterface() - .replyCommentToIssue(Authorization.returnAuthentication(ctx, loginUid, instanceToken), repoOwner, repoName, issueIndex, issueComment); + final TinyDB tinyDb = new TinyDB(appCtx); - call.enqueue(new Callback() { + 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]; - @Override - public void onResponse(@NonNull Call call, @NonNull retrofit2.Response response) { + Call> call = RetrofitClient + .getInstance(instanceUrl, ctx) + .getApiInterface() + .getCollaborators(Authorization.returnAuthentication(ctx, loginUid, instanceToken), repoOwner, repoName); - if(response.code() == 201) { + call.enqueue(new Callback>() { - Toasty.info(ctx, getString(R.string.commentSuccess)); - tinyDb.putBoolean("commentPosted", true); - tinyDb.putBoolean("resumeIssues", true); - tinyDb.putBoolean("resumePullRequests", true); - finish(); + @Override + public void onResponse(@NonNull Call> call, @NonNull Response> response) { - } - else if(response.code() == 401) { + if (response.isSuccessful()) { - enableProcessButton(); - AlertDialogs.authorizationTokenRevokedDialog(ctx, getResources().getString(R.string.alertDialogTokenRevokedTitle), - getResources().getString(R.string.alertDialogTokenRevokedMessage), - getResources().getString(R.string.alertDialogTokenRevokedCopyNegativeButton), - getResources().getString(R.string.alertDialogTokenRevokedCopyPositiveButton)); + assert response.body() != null; + String fullName = ""; + for(int i = 0; i < response.body().size(); i++) { + if(!response.body().get(i).getFull_name().equals("")) { + fullName = response.body().get(i).getFull_name(); + } + defaultMentionAdapter.add(new Mention(response.body().get(i).getUsername(), fullName, response.body().get(i).getAvatar_url())); + } - } - else { + } + else { - enableProcessButton(); - Toasty.info(ctx, getString(R.string.commentError)); + Log.i(TAG, String.valueOf(response.code())); - } + } - } + } - @Override - public void onFailure(@NonNull Call call, @NonNull Throwable t) { - Log.e("onFailure", t.toString()); - enableProcessButton(); - } - }); + @Override + public void onFailure(@NonNull Call> call, @NonNull Throwable t) { - } + Log.e(TAG, t.toString()); + } - private void disableProcessButton() { + }); + } - replyButton.setEnabled(false); - GradientDrawable shape = new GradientDrawable(); - shape.setCornerRadius( 8 ); - shape.setColor(getResources().getColor(R.color.hintColor)); - replyButton.setBackground(shape); + private void initCloseListener() { - } + onClickListener = view -> finish(); + } - private void enableProcessButton() { + private View.OnClickListener replyToIssue = v -> processNewCommentReply(); - replyButton.setEnabled(true); - GradientDrawable shape = new GradientDrawable(); - shape.setCornerRadius( 8 ); - shape.setColor(getResources().getColor(R.color.btnBackground)); - replyButton.setBackground(shape); + private void processNewCommentReply() { - } + String newReplyDT = addComment.getText().toString(); + boolean connToInternet = AppUtil.hasNetworkConnection(appCtx); + + if(!connToInternet) { + + Toasty.info(ctx, getResources().getString(R.string.checkNetConnection)); + return; + + } + + if(newReplyDT.equals("")) { + + Toasty.info(ctx, getString(R.string.commentEmptyError)); + + } + else { + + disableProcessButton(); + replyComment(newReplyDT); + + } + + } + + private void replyComment(String newReplyDT) { + + final TinyDB tinyDb = new TinyDB(appCtx); + + 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]; + final int issueIndex = Integer.parseInt(tinyDb.getString("issueNumber")); + + Issues issueComment = new Issues(newReplyDT); + + Call call = RetrofitClient + .getInstance(instanceUrl, ctx) + .getApiInterface() + .replyCommentToIssue(Authorization.returnAuthentication(ctx, loginUid, instanceToken), repoOwner, repoName, issueIndex, issueComment); + + call.enqueue(new Callback() { + + @Override + public void onResponse(@NonNull Call call, @NonNull retrofit2.Response response) { + + if(response.code() == 201) { + + Toasty.info(ctx, getString(R.string.commentSuccess)); + tinyDb.putBoolean("commentPosted", true); + tinyDb.putBoolean("resumeIssues", true); + tinyDb.putBoolean("resumePullRequests", true); + finish(); + + } + else if(response.code() == 401) { + + enableProcessButton(); + AlertDialogs.authorizationTokenRevokedDialog(ctx, getResources().getString(R.string.alertDialogTokenRevokedTitle), + getResources().getString(R.string.alertDialogTokenRevokedMessage), + getResources().getString(R.string.alertDialogTokenRevokedCopyNegativeButton), + getResources().getString(R.string.alertDialogTokenRevokedCopyPositiveButton)); + + } + else { + + enableProcessButton(); + Toasty.info(ctx, getString(R.string.commentError)); + + } + + } + + @Override + public void onFailure(@NonNull Call call, @NonNull Throwable t) { + + Log.e(TAG, t.toString()); + enableProcessButton(); + } + }); + + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + + MenuInflater inflater = getMenuInflater(); + inflater.inflate(R.menu.reply_to_issue, menu); + + return super.onCreateOptionsMenu(menu); + + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + + switch(item.getItemId()) { + + case R.id.replyToIssueMenu: + Intent fragmentIntent = new Intent(ReplyToIssueActivity.this, MainActivity.class); + fragmentIntent.putExtra("launchFragment", "drafts"); + ReplyToIssueActivity.this.startActivity(fragmentIntent); + break; + + default: + return super.onOptionsItemSelected(item); + + } + + return true; + } + + private void disableProcessButton() { + + replyButton.setEnabled(false); + GradientDrawable shape = new GradientDrawable(); + shape.setCornerRadius(8); + shape.setColor(getResources().getColor(R.color.hintColor)); + replyButton.setBackground(shape); + + } + + private void enableProcessButton() { + + replyButton.setEnabled(true); + GradientDrawable shape = new GradientDrawable(); + shape.setCornerRadius(8); + shape.setColor(getResources().getColor(R.color.btnBackground)); + replyButton.setBackground(shape); + + } } diff --git a/app/src/main/java/org/mian/gitnex/adapters/DraftsAdapter.java b/app/src/main/java/org/mian/gitnex/adapters/DraftsAdapter.java new file mode 100644 index 00000000..564df93c --- /dev/null +++ b/app/src/main/java/org/mian/gitnex/adapters/DraftsAdapter.java @@ -0,0 +1,127 @@ +package org.mian.gitnex.adapters; + +import android.annotation.SuppressLint; +import android.content.Context; +import android.content.Intent; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ImageView; +import android.widget.TextView; +import androidx.annotation.NonNull; +import androidx.recyclerview.widget.RecyclerView; +import org.mian.gitnex.R; +import org.mian.gitnex.activities.ReplyToIssueActivity; +import org.mian.gitnex.database.models.DraftWithRepository; +import org.mian.gitnex.database.api.DraftsApi; +import org.mian.gitnex.helpers.Toasty; +import org.mian.gitnex.util.TinyDB; +import java.util.List; + +/** + * Author M M Arif + */ + +public class DraftsAdapter extends RecyclerView.Adapter { + + private List draftsList; + private Context mCtx; + + class DraftsViewHolder extends RecyclerView.ViewHolder { + + private TextView draftText; + private TextView repoInfo; + private TextView repoId; + private TextView draftId; + private TextView issueNumber; + private TextView issueType; + private TextView repoOwner; + private TextView repoName; + + private DraftsViewHolder(View itemView) { + + super(itemView); + + draftText = itemView.findViewById(R.id.draftText); + repoInfo = itemView.findViewById(R.id.repoInfo); + repoId = itemView.findViewById(R.id.repoId); + draftId = itemView.findViewById(R.id.draftId); + issueNumber = itemView.findViewById(R.id.issueNumber); + issueType = itemView.findViewById(R.id.issueType); + repoOwner = itemView.findViewById(R.id.repoOwner); + repoName = itemView.findViewById(R.id.repoName); + ImageView deleteDraft = itemView.findViewById(R.id.deleteDraft); + + deleteDraft.setOnClickListener(itemDelete -> { + + int getDraftId = Integer.parseInt(draftId.getText().toString()); + deleteDraft(getAdapterPosition()); + DraftsApi.deleteSingleDraft(getDraftId); + + }); + + itemView.setOnClickListener(itemEdit -> { + + Intent intent = new Intent(mCtx, ReplyToIssueActivity.class); + intent.putExtra("commentBody", draftText.getText()); + intent.putExtra("issueNumber", issueNumber.getText()); + intent.putExtra("repositoryId", repoId.getText()); + intent.putExtra("draftTitle", repoInfo.getText()); + + TinyDB tinyDb = new TinyDB(mCtx); + tinyDb.putString("issueNumber", issueNumber.getText().toString()); + tinyDb.putLong("repositoryId", Long.parseLong(repoId.getText().toString())); + //tinyDb.putString("issueType", issueType.getText().toString()); + + mCtx.startActivity(intent); + + }); + + } + + } + + public DraftsAdapter(Context mCtx, List draftsListMain) { + this.mCtx = mCtx; + this.draftsList = draftsListMain; + } + + private void deleteDraft(int position) { + + draftsList.remove(position); + notifyItemRemoved(position); + notifyItemRangeChanged(position, draftsList.size()); + Toasty.info(mCtx, mCtx.getResources().getString(R.string.draftsSingleDeleteSuccess)); + + } + + @NonNull + @Override + public DraftsAdapter.DraftsViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { + View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.list_drafts, parent, false); + return new DraftsViewHolder(v); + } + + @SuppressLint("DefaultLocale") + @Override + public void onBindViewHolder(@NonNull DraftsAdapter.DraftsViewHolder holder, int position) { + + DraftWithRepository currentItem = draftsList.get(position); + + holder.repoId.setText(String.valueOf(currentItem.getRepositoryId())); + holder.draftId.setText(String.valueOf(currentItem.getDraftId())); + holder.issueNumber.setText(String.valueOf(currentItem.getIssueId())); + holder.issueType.setText(currentItem.getDraftType()); + holder.repoOwner.setText(currentItem.getRepositoryOwner()); + holder.repoName.setText(currentItem.getRepositoryName()); + holder.draftText.setText(currentItem.getDraftText()); + holder.repoInfo.setText(String.format("%s%d %s / %s", mCtx.getResources().getString(R.string.hash), currentItem.getIssueId(), currentItem.getRepositoryOwner(), currentItem.getRepositoryName())); + + } + + @Override + public int getItemCount() { + return draftsList.size(); + } + +} diff --git a/app/src/main/java/org/mian/gitnex/adapters/ExploreRepositoriesAdapter.java b/app/src/main/java/org/mian/gitnex/adapters/ExploreRepositoriesAdapter.java index a935e57e..1e4180ba 100644 --- a/app/src/main/java/org/mian/gitnex/adapters/ExploreRepositoriesAdapter.java +++ b/app/src/main/java/org/mian/gitnex/adapters/ExploreRepositoriesAdapter.java @@ -23,6 +23,8 @@ import org.mian.gitnex.activities.RepoStargazersActivity; import org.mian.gitnex.activities.RepoWatchersActivity; import org.mian.gitnex.clients.PicassoService; import org.mian.gitnex.clients.RetrofitClient; +import org.mian.gitnex.database.models.Repository; +import org.mian.gitnex.database.api.RepositoriesApi; import org.mian.gitnex.helpers.RoundedTransformation; import org.mian.gitnex.helpers.Toasty; import org.mian.gitnex.models.UserRepositories; @@ -92,12 +94,33 @@ public class ExploreRepositoriesAdapter extends RecyclerView.Adapter implements Filterable { - private List reposList; - private Context mCtx; - private List reposListFull; + private List reposList; + private Context mCtx; + private List reposListFull; - static class MyReposViewHolder extends RecyclerView.ViewHolder { + static class MyReposViewHolder extends RecyclerView.ViewHolder { - private ImageView imageAvatar; - private TextView repoName; - private TextView repoDescription; - private TextView repoFullName; - private ImageView repoPrivatePublic; - private TextView repoStars; - private TextView repoForks; - private TextView repoOpenIssuesCount; - private TextView repoType; - private CheckBox isRepoAdmin; - private LinearLayout archiveRepo; + private ImageView imageAvatar; + private TextView repoName; + private TextView repoDescription; + private TextView repoFullName; + private ImageView repoPrivatePublic; + private TextView repoStars; + private TextView repoForks; + private TextView repoOpenIssuesCount; + private TextView repoType; + private CheckBox isRepoAdmin; + private LinearLayout archiveRepo; - private MyReposViewHolder(View itemView) { - super(itemView); - repoName = itemView.findViewById(R.id.repoName); - repoDescription = itemView.findViewById(R.id.repoDescription); - imageAvatar = itemView.findViewById(R.id.imageAvatar); - repoFullName = itemView.findViewById(R.id.repoFullName); - repoPrivatePublic = itemView.findViewById(R.id.imageRepoType); - repoStars = itemView.findViewById(R.id.repoStars); - repoForks = itemView.findViewById(R.id.repoForks); - repoOpenIssuesCount = itemView.findViewById(R.id.repoOpenIssuesCount); - ImageView reposDropdownMenu = itemView.findViewById(R.id.reposDropdownMenu); - repoType = itemView.findViewById(R.id.repoType); - isRepoAdmin = itemView.findViewById(R.id.repoIsAdmin); - archiveRepo = itemView.findViewById(R.id.archiveRepoFrame); + private MyReposViewHolder(View itemView) { - itemView.setOnClickListener(v -> { + super(itemView); + repoName = itemView.findViewById(R.id.repoName); + repoDescription = itemView.findViewById(R.id.repoDescription); + imageAvatar = itemView.findViewById(R.id.imageAvatar); + repoFullName = itemView.findViewById(R.id.repoFullName); + repoPrivatePublic = itemView.findViewById(R.id.imageRepoType); + repoStars = itemView.findViewById(R.id.repoStars); + repoForks = itemView.findViewById(R.id.repoForks); + repoOpenIssuesCount = itemView.findViewById(R.id.repoOpenIssuesCount); + ImageView reposDropdownMenu = itemView.findViewById(R.id.reposDropdownMenu); + repoType = itemView.findViewById(R.id.repoType); + isRepoAdmin = itemView.findViewById(R.id.repoIsAdmin); + archiveRepo = itemView.findViewById(R.id.archiveRepoFrame); - Context context = v.getContext(); + itemView.setOnClickListener(v -> { - Intent intent = new Intent(context, RepoDetailActivity.class); - intent.putExtra("repoFullName", repoFullName.getText().toString()); + Context context = v.getContext(); - TinyDB tinyDb = new TinyDB(context); - tinyDb.putString("repoFullName", repoFullName.getText().toString()); - tinyDb.putString("repoType", repoType.getText().toString()); - //tinyDb.putBoolean("resumeIssues", true); - tinyDb.putBoolean("isRepoAdmin", isRepoAdmin.isChecked()); + Intent intent = new Intent(context, RepoDetailActivity.class); + intent.putExtra("repoFullName", repoFullName.getText().toString()); - //store if user is watching this repo - { - final String instanceUrl = tinyDb.getString("instanceUrl"); - String[] parts = repoFullName.getText().toString().split("/"); - final String repoOwner = parts[0]; - final String repoName = parts[1]; - final String token = "token " + tinyDb.getString(tinyDb.getString("loginUid") + "-token"); + TinyDB tinyDb = new TinyDB(context); + tinyDb.putString("repoFullName", repoFullName.getText().toString()); + tinyDb.putString("repoType", repoType.getText().toString()); + //tinyDb.putBoolean("resumeIssues", true); + tinyDb.putBoolean("isRepoAdmin", isRepoAdmin.isChecked()); - WatchInfo watch = new WatchInfo(); + String[] parts = repoFullName.getText().toString().split("/"); + final String repoOwner = parts[0]; + final String repoName = parts[1]; - Call call; + int currentActiveAccountId = tinyDb.getInt("currentActiveAccountId"); + RepositoriesApi repositoryData = new RepositoriesApi(context); - call = RetrofitClient.getInstance(instanceUrl, context).getApiInterface().checkRepoWatchStatus(token, repoOwner, repoName); + //RepositoriesRepository.deleteRepositoriesByAccount(currentActiveAccountId); + Integer count = repositoryData.checkRepository(currentActiveAccountId, repoOwner, repoName); - call.enqueue(new Callback() { + if(count == 0) { - @Override - public void onResponse(@NonNull Call call, @NonNull retrofit2.Response response) { + long id = repositoryData.insertRepository(currentActiveAccountId, repoOwner, repoName); + tinyDb.putLong("repositoryId", id); - if(response.isSuccessful()) { + } + else { - assert response.body() != null; - tinyDb.putBoolean("repoWatch", response.body().getSubscribed()); + Repository data = repositoryData.getRepository(currentActiveAccountId, repoOwner, repoName); + tinyDb.putLong("repositoryId", data.getRepositoryId()); - } else { + } - tinyDb.putBoolean("repoWatch", false); + //store if user is watching this repo + { - if(response.code() != 404) { + final String instanceUrl = tinyDb.getString("instanceUrl"); + final String token = "token " + tinyDb.getString(tinyDb.getString("loginUid") + "-token"); - Toasty.info(context, context.getString(R.string.genericApiStatusError)); + WatchInfo watch = new WatchInfo(); - } + Call call; - } + call = RetrofitClient.getInstance(instanceUrl, context).getApiInterface().checkRepoWatchStatus(token, repoOwner, repoName); - } + call.enqueue(new Callback() { - @Override - public void onFailure(@NonNull Call call, @NonNull Throwable t) { + @Override + public void onResponse(@NonNull Call call, @NonNull retrofit2.Response response) { - tinyDb.putBoolean("repoWatch", false); - Toasty.info(context, context.getString(R.string.genericApiStatusError)); + if(response.isSuccessful()) { - } - }); - } + assert response.body() != null; + tinyDb.putBoolean("repoWatch", response.body().getSubscribed()); - context.startActivity(intent); + } + else { - }); + tinyDb.putBoolean("repoWatch", false); - reposDropdownMenu.setOnClickListener(v -> { + if(response.code() != 404) { - final Context context = v.getContext(); + Toasty.info(context, context.getString(R.string.genericApiStatusError)); - @SuppressLint("InflateParams") - View view = LayoutInflater.from(context).inflate(R.layout.bottom_sheet_repository_in_list, null); + } - TextView repoOpenInBrowser = view.findViewById(R.id.repoOpenInBrowser); - TextView repoStargazers = view.findViewById(R.id.repoStargazers); - TextView repoWatchers = view.findViewById(R.id.repoWatchers); - TextView bottomSheetHeader = view.findViewById(R.id.bottomSheetHeader); + } - bottomSheetHeader.setText(String.format("%s / %s", repoFullName.getText().toString().split("/")[0], repoFullName.getText().toString().split("/")[1])); - BottomSheetDialog dialog = new BottomSheetDialog(context); - dialog.setContentView(view); - dialog.show(); + } - repoOpenInBrowser.setOnClickListener(openInBrowser -> { + @Override + public void onFailure(@NonNull Call call, @NonNull Throwable t) { - Intent intentOpenInBrowser = new Intent(context, OpenRepoInBrowserActivity.class); - intentOpenInBrowser.putExtra("repoFullNameBrowser", repoFullName.getText()); - context.startActivity(intentOpenInBrowser); - dialog.dismiss(); + tinyDb.putBoolean("repoWatch", false); + Toasty.info(context, context.getString(R.string.genericApiStatusError)); - }); + } + }); - repoStargazers.setOnClickListener(stargazers -> { + } - Intent intent = new Intent(context, RepoStargazersActivity.class); - intent.putExtra("repoFullNameForStars", repoFullName.getText()); - context.startActivity(intent); - dialog.dismiss(); + context.startActivity(intent); - }); + }); - repoWatchers.setOnClickListener(watchers -> { + reposDropdownMenu.setOnClickListener(v -> { - Intent intentW = new Intent(context, RepoWatchersActivity.class); - intentW.putExtra("repoFullNameForWatchers", repoFullName.getText()); - context.startActivity(intentW); - dialog.dismiss(); + final Context context = v.getContext(); - }); + @SuppressLint("InflateParams") View view = LayoutInflater.from(context).inflate(R.layout.bottom_sheet_repository_in_list, null); - }); + TextView repoOpenInBrowser = view.findViewById(R.id.repoOpenInBrowser); + TextView repoStargazers = view.findViewById(R.id.repoStargazers); + TextView repoWatchers = view.findViewById(R.id.repoWatchers); + TextView bottomSheetHeader = view.findViewById(R.id.bottomSheetHeader); - } - } + bottomSheetHeader.setText(String.format("%s / %s", repoFullName.getText().toString().split("/")[0], repoFullName.getText().toString().split("/")[1])); + BottomSheetDialog dialog = new BottomSheetDialog(context); + dialog.setContentView(view); + dialog.show(); - public MyReposListAdapter(Context mCtx, List reposListMain) { - this.mCtx = mCtx; - this.reposList = reposListMain; - reposListFull = new ArrayList<>(reposList); - } + repoOpenInBrowser.setOnClickListener(openInBrowser -> { - @NonNull - @Override - public MyReposListAdapter.MyReposViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { - View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.list_repositories, parent, false); - return new MyReposListAdapter.MyReposViewHolder(v); - } + Intent intentOpenInBrowser = new Intent(context, OpenRepoInBrowserActivity.class); + intentOpenInBrowser.putExtra("repoFullNameBrowser", repoFullName.getText()); + context.startActivity(intentOpenInBrowser); + dialog.dismiss(); - @Override - public void onBindViewHolder(@NonNull MyReposListAdapter.MyReposViewHolder holder, int position) { + }); - UserRepositories currentItem = reposList.get(position); - holder.repoDescription.setVisibility(View.GONE); + repoStargazers.setOnClickListener(stargazers -> { - ColorGenerator generator = ColorGenerator.MATERIAL; - int color = generator.getColor(currentItem.getName()); - String firstCharacter = String.valueOf(currentItem.getName().charAt(0)); + Intent intent = new Intent(context, RepoStargazersActivity.class); + intent.putExtra("repoFullNameForStars", repoFullName.getText()); + context.startActivity(intent); + dialog.dismiss(); - TextDrawable drawable = TextDrawable.builder() - .beginConfig() - .useFont(Typeface.DEFAULT) - .fontSize(18) - .toUpperCase() - .width(28) - .height(28) - .endConfig() - .buildRoundRect(firstCharacter, color, 3); + }); - if (currentItem.getAvatar_url() != null) { - if (!currentItem.getAvatar_url().equals("")) { - PicassoService.getInstance(mCtx).get().load(currentItem.getAvatar_url()).placeholder(R.drawable.loader_animated).transform(new RoundedTransformation(8, 0)).resize(120, 120).centerCrop().into(holder.imageAvatar); - } else { - holder.imageAvatar.setImageDrawable(drawable); - } - } - else { - holder.imageAvatar.setImageDrawable(drawable); - } + repoWatchers.setOnClickListener(watchers -> { - holder.repoName.setText(currentItem.getName()); - if (!currentItem.getDescription().equals("")) { - holder.repoDescription.setVisibility(View.VISIBLE); - holder.repoDescription.setText(currentItem.getDescription()); - } - holder.repoFullName.setText(currentItem.getFullname()); - if(currentItem.getPrivateFlag()) { - holder.repoPrivatePublic.setImageResource(R.drawable.ic_lock_bold); - holder.repoType.setText(R.string.strPrivate); - } - else { - holder.repoPrivatePublic.setImageResource(R.drawable.ic_public); - holder.repoType.setText(R.string.strPublic); - } - holder.repoStars.setText(currentItem.getStars_count()); - holder.repoForks.setText(currentItem.getForks_count()); - holder.repoOpenIssuesCount.setText(currentItem.getOpen_issues_count()); + Intent intentW = new Intent(context, RepoWatchersActivity.class); + intentW.putExtra("repoFullNameForWatchers", repoFullName.getText()); + context.startActivity(intentW); + dialog.dismiss(); - if(holder.isRepoAdmin == null) { - holder.isRepoAdmin = new CheckBox(mCtx); - } - holder.isRepoAdmin.setChecked(currentItem.getPermissions().isAdmin()); + }); - if(currentItem.isArchived()) { - holder.archiveRepo.setVisibility(View.VISIBLE); - } - else { - holder.archiveRepo.setVisibility(View.GONE); - } + }); - } + } - @Override - public int getItemCount() { - return reposList.size(); - } + } - @Override - public Filter getFilter() { - return myReposFilter; - } + public MyReposListAdapter(Context mCtx, List reposListMain) { - private Filter myReposFilter = new Filter() { - @Override - protected FilterResults performFiltering(CharSequence constraint) { - List filteredList = new ArrayList<>(); + this.mCtx = mCtx; + this.reposList = reposListMain; + reposListFull = new ArrayList<>(reposList); + } - if (constraint == null || constraint.length() == 0) { - filteredList.addAll(reposListFull); - } else { - String filterPattern = constraint.toString().toLowerCase().trim(); + @NonNull + @Override + public MyReposListAdapter.MyReposViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { - for (UserRepositories item : reposListFull) { - if (item.getFullname().toLowerCase().contains(filterPattern) || item.getDescription().toLowerCase().contains(filterPattern)) { - filteredList.add(item); - } - } - } + View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.list_repositories, parent, false); + return new MyReposListAdapter.MyReposViewHolder(v); + } - FilterResults results = new FilterResults(); - results.values = filteredList; + @Override + public void onBindViewHolder(@NonNull MyReposListAdapter.MyReposViewHolder holder, int position) { - return results; - } + UserRepositories currentItem = reposList.get(position); + holder.repoDescription.setVisibility(View.GONE); + + ColorGenerator generator = ColorGenerator.MATERIAL; + int color = generator.getColor(currentItem.getName()); + String firstCharacter = String.valueOf(currentItem.getName().charAt(0)); + + TextDrawable drawable = TextDrawable.builder().beginConfig().useFont(Typeface.DEFAULT).fontSize(18).toUpperCase().width(28).height(28).endConfig().buildRoundRect(firstCharacter, color, 3); + + if(currentItem.getAvatar_url() != null) { + if(!currentItem.getAvatar_url().equals("")) { + PicassoService.getInstance(mCtx).get().load(currentItem.getAvatar_url()).placeholder(R.drawable.loader_animated).transform(new RoundedTransformation(8, 0)).resize(120, 120).centerCrop().into(holder.imageAvatar); + } + else { + holder.imageAvatar.setImageDrawable(drawable); + } + } + else { + holder.imageAvatar.setImageDrawable(drawable); + } + + holder.repoName.setText(currentItem.getName()); + if(!currentItem.getDescription().equals("")) { + holder.repoDescription.setVisibility(View.VISIBLE); + holder.repoDescription.setText(currentItem.getDescription()); + } + holder.repoFullName.setText(currentItem.getFullname()); + if(currentItem.getPrivateFlag()) { + holder.repoPrivatePublic.setImageResource(R.drawable.ic_lock_bold); + holder.repoType.setText(R.string.strPrivate); + } + else { + holder.repoPrivatePublic.setImageResource(R.drawable.ic_public); + holder.repoType.setText(R.string.strPublic); + } + holder.repoStars.setText(currentItem.getStars_count()); + holder.repoForks.setText(currentItem.getForks_count()); + holder.repoOpenIssuesCount.setText(currentItem.getOpen_issues_count()); + + if(holder.isRepoAdmin == null) { + holder.isRepoAdmin = new CheckBox(mCtx); + } + holder.isRepoAdmin.setChecked(currentItem.getPermissions().isAdmin()); + + if(currentItem.isArchived()) { + holder.archiveRepo.setVisibility(View.VISIBLE); + } + else { + holder.archiveRepo.setVisibility(View.GONE); + } + + } + + @Override + public int getItemCount() { + + return reposList.size(); + } + + @Override + public Filter getFilter() { + + return myReposFilter; + } + + private Filter myReposFilter = new Filter() { + + @Override + protected FilterResults performFiltering(CharSequence constraint) { + + List filteredList = new ArrayList<>(); + + if(constraint == null || constraint.length() == 0) { + filteredList.addAll(reposListFull); + } + else { + String filterPattern = constraint.toString().toLowerCase().trim(); + + for(UserRepositories item : reposListFull) { + if(item.getFullname().toLowerCase().contains(filterPattern) || item.getDescription().toLowerCase().contains(filterPattern)) { + filteredList.add(item); + } + } + } + + FilterResults results = new FilterResults(); + results.values = filteredList; + + return results; + } + + @Override + protected void publishResults(CharSequence constraint, FilterResults results) { + + reposList.clear(); + reposList.addAll((List) results.values); + notifyDataSetChanged(); + } + }; - @Override - protected void publishResults(CharSequence constraint, FilterResults results) { - reposList.clear(); - reposList.addAll((List) results.values); - notifyDataSetChanged(); - } - }; } diff --git a/app/src/main/java/org/mian/gitnex/adapters/ReposListAdapter.java b/app/src/main/java/org/mian/gitnex/adapters/ReposListAdapter.java index 24f45c13..b8a5fdee 100644 --- a/app/src/main/java/org/mian/gitnex/adapters/ReposListAdapter.java +++ b/app/src/main/java/org/mian/gitnex/adapters/ReposListAdapter.java @@ -25,6 +25,8 @@ import org.mian.gitnex.activities.RepoStargazersActivity; import org.mian.gitnex.activities.RepoWatchersActivity; import org.mian.gitnex.clients.PicassoService; import org.mian.gitnex.clients.RetrofitClient; +import org.mian.gitnex.database.models.Repository; +import org.mian.gitnex.database.api.RepositoriesApi; import org.mian.gitnex.helpers.RoundedTransformation; import org.mian.gitnex.helpers.Toasty; import org.mian.gitnex.models.UserRepositories; @@ -90,12 +92,33 @@ public class ReposListAdapter extends RecyclerView.Adapter draftId = draftsDao.insertDraft(draft)); + thread.start(); + thread.join(); + } + catch(InterruptedException e) { + + Log.e(StaticGlobalVariables.draftsRepository, e.toString()); + } + + return draftId; + } + + public Integer checkDraft(int issueId, int draftRepositoryId) { + + try { + + Thread thread = new Thread(() -> checkDraftFlag = draftsDao.checkDraftDao(issueId, draftRepositoryId)); + thread.start(); + thread.join(); + } + catch(InterruptedException e) { + + Log.e(StaticGlobalVariables.draftsRepository, e.toString()); + } + + return checkDraftFlag; + } + + public LiveData> getDrafts(int accountId) { + + return draftsDao.fetchAllDrafts(accountId); + } + + public LiveData getDraftByIssueId(int issueId) { + + return draftsDao.fetchDraftByIssueId(issueId); + } + + public static void deleteSingleDraft(final int draftId) { + + final LiveData draft = draftsDao.fetchDraftById(draftId); + + if(draft != null) { + + new Thread(() -> draftsDao.deleteByDraftId(draftId)).start(); + } + } + + public static void deleteAllDrafts(final int accountId) { + + new Thread(() -> draftsDao.deleteAllDrafts(accountId)).start(); + } + + public static void updateDraft(final String draftText, final int draftId) { + + new Thread(() -> draftsDao.updateDraft(draftText, draftId)).start(); + } + + public static void updateDraftByIssueIdAsyncTask(final String draftText, final int issueId, final int draftRepositoryId) { + + new Thread(() -> draftsDao.updateDraftByIssueId(draftText, issueId, draftRepositoryId)).start(); + } + +} diff --git a/app/src/main/java/org/mian/gitnex/database/api/RepositoriesApi.java b/app/src/main/java/org/mian/gitnex/database/api/RepositoriesApi.java new file mode 100644 index 00000000..7ef124df --- /dev/null +++ b/app/src/main/java/org/mian/gitnex/database/api/RepositoriesApi.java @@ -0,0 +1,140 @@ +package org.mian.gitnex.database.api; + +import android.content.Context; +import android.util.Log; +import androidx.lifecycle.LiveData; +import org.mian.gitnex.database.dao.RepositoriesDao; +import org.mian.gitnex.database.db.GitnexDatabase; +import org.mian.gitnex.database.models.Repository; +import org.mian.gitnex.helpers.StaticGlobalVariables; +import java.util.List; + +/** + * Author M M Arif + */ + +public class RepositoriesApi { + + private static RepositoriesDao repositoriesDao; + private static long repositoryId; + private static Repository repository; + private static Integer checkRepository; + + public RepositoriesApi(Context context) { + + GitnexDatabase db; + db = GitnexDatabase.getDatabaseInstance(context); + repositoriesDao = db.repositoriesDao(); + } + + public long insertRepository(int repoAccountId, String repositoryOwner, String repositoryName) { + + Repository repository = new Repository(); + repository.setRepoAccountId(repoAccountId); + repository.setRepositoryOwner(repositoryOwner); + repository.setRepositoryName(repositoryName); + + return insertRepositoryAsyncTask(repository); + } + + public long insertRepositoryAsyncTask(Repository repository) { + + try { + + Thread thread = new Thread(() -> repositoryId = repositoriesDao.newRepository(repository)); + thread.start(); + thread.join(); + } + catch(InterruptedException e) { + + Log.e(StaticGlobalVariables.repositoriesRepository, e.toString()); + } + + return repositoryId; + } + + public Repository getRepository(int repoAccountId, String repositoryOwner, String repositoryName) { + + try { + + Thread thread = new Thread(() -> repository = repositoriesDao.getSingleRepositoryDao(repoAccountId, repositoryOwner, repositoryName)); + thread.start(); + thread.join(); + } + catch(InterruptedException e) { + + Log.e(StaticGlobalVariables.repositoriesRepository, e.toString()); + } + + return repository; + } + + public LiveData> getAllRepositories() { + + return repositoriesDao.fetchAllRepositories(); + } + + public LiveData> getAllRepositoriesByAccount(int repoAccountId) { + + return repositoriesDao.getAllRepositoriesByAccountDao(repoAccountId); + } + + public Integer checkRepository(int repoAccountId, String repositoryOwner, String repositoryName) { + + try { + + Thread thread = new Thread(() -> checkRepository = repositoriesDao.checkRepositoryDao(repoAccountId, repositoryOwner, repositoryName)); + thread.start(); + thread.join(); + } + catch(InterruptedException e) { + + Log.e(StaticGlobalVariables.repositoriesRepository, e.toString()); + } + + return checkRepository; + } + + public Repository fetchRepositoryById(int repositoryId) { + + try { + + Thread thread = new Thread(() -> repository = repositoriesDao.fetchRepositoryByIdDao(repositoryId)); + thread.start(); + thread.join(); + } + catch(InterruptedException e) { + + Log.e(StaticGlobalVariables.repositoriesRepository, e.toString()); + } + + return repository; + } + + public Repository fetchRepositoryByAccountIdByRepositoryId(int repositoryId, int repoAccountId) { + + try { + + Thread thread = new Thread(() -> repository = repositoriesDao.fetchRepositoryByAccountIdByRepositoryIdDao(repositoryId, repoAccountId)); + thread.start(); + thread.join(); + } + catch(InterruptedException e) { + + Log.e(StaticGlobalVariables.repositoriesRepository, e.toString()); + } + + return repository; + } + + public static void deleteRepositoriesByAccount(final int repoAccountId) { + + new Thread(() -> repositoriesDao.deleteRepositoriesByAccount(repoAccountId)).start(); + } + + public static void deleteRepository(final int repositoryId) { + + new Thread(() -> repositoriesDao.deleteRepository(repositoryId)).start(); + } + +} diff --git a/app/src/main/java/org/mian/gitnex/database/api/UserAccountsApi.java b/app/src/main/java/org/mian/gitnex/database/api/UserAccountsApi.java new file mode 100644 index 00000000..3c30ed6e --- /dev/null +++ b/app/src/main/java/org/mian/gitnex/database/api/UserAccountsApi.java @@ -0,0 +1,98 @@ +package org.mian.gitnex.database.api; + +import android.content.Context; +import android.util.Log; +import androidx.lifecycle.LiveData; +import org.mian.gitnex.database.dao.UserAccountsDao; +import org.mian.gitnex.database.db.GitnexDatabase; +import org.mian.gitnex.database.models.UserAccount; +import org.mian.gitnex.helpers.StaticGlobalVariables; +import java.util.List; + +/** + * Author M M Arif + */ + +public class UserAccountsApi { + + private static UserAccountsDao userAccountsDao; + private static UserAccount userAccount; + private static Integer checkAccount; + + public UserAccountsApi(Context context) { + + GitnexDatabase db; + db = GitnexDatabase.getDatabaseInstance(context); + userAccountsDao = db.userAccountsDao(); + } + + public void insertNewAccount(String accountName, String instanceUrl, String userName, String token, String serverVersion) { + + UserAccount userAccount = new UserAccount(); + userAccount.setAccountName(accountName); + userAccount.setInstanceUrl(instanceUrl); + userAccount.setUserName(userName); + userAccount.setToken(token); + userAccount.setServerVersion(serverVersion); + + insertNewAccountAsync(userAccount); + } + + private static void insertNewAccountAsync(final UserAccount userAccount) { + + new Thread(() -> userAccountsDao.newAccount(userAccount)).start(); + } + + public static void updateServerVersion(final String serverVersion, final int accountId) { + + new Thread(() -> userAccountsDao.updateServerVersion(serverVersion, accountId)).start(); + } + + public static void updateToken(final int accountId, final String token) { + + new Thread(() -> userAccountsDao.updateAccountToken(accountId, token)).start(); + } + + public UserAccount getAccountData(String accountName) { + + try { + + Thread thread = new Thread(() -> userAccount = userAccountsDao.fetchRowByAccount_(accountName)); + thread.start(); + thread.join(); + } + catch(InterruptedException e) { + + Log.e(StaticGlobalVariables.userAccountsRepository, e.toString()); + } + + return userAccount; + } + + public Integer getCount(String accountName) { + + try { + + Thread thread = new Thread(() -> checkAccount = userAccountsDao.getCount(accountName)); + thread.start(); + thread.join(); + } + catch(InterruptedException e) { + + Log.e(StaticGlobalVariables.userAccountsRepository, e.toString()); + } + + return checkAccount; + } + + public LiveData> getAllAccounts() { + + return userAccountsDao.fetchAllAccounts(); + } + + public static void deleteAccount(final int accountId) { + + new Thread(() -> userAccountsDao.deleteAccount(accountId)).start(); + } + +} diff --git a/app/src/main/java/org/mian/gitnex/database/dao/DraftsDao.java b/app/src/main/java/org/mian/gitnex/database/dao/DraftsDao.java new file mode 100644 index 00000000..f5c65aad --- /dev/null +++ b/app/src/main/java/org/mian/gitnex/database/dao/DraftsDao.java @@ -0,0 +1,53 @@ +package org.mian.gitnex.database.dao; + +import androidx.lifecycle.LiveData; +import androidx.room.Dao; +import androidx.room.Insert; +import androidx.room.Query; +import org.mian.gitnex.database.models.Draft; +import org.mian.gitnex.database.models.DraftWithRepository; +import java.util.List; + +/** + * Author M M Arif + */ + +@Dao +public interface DraftsDao { + + @Insert + long insertDraft(Draft drafts); + + @Query("SELECT * FROM Drafts JOIN Repositories ON Repositories.repositoryId = Drafts.draftRepositoryId WHERE draftAccountId = :accountId" + + " ORDER BY " + + "draftId DESC") + LiveData> fetchAllDrafts(int accountId); + + @Query("SELECT * FROM Drafts WHERE draftAccountId = :accountId ORDER BY draftId DESC") + LiveData> fetchDrafts(int accountId); + + @Query("SELECT * FROM Drafts WHERE draftAccountId = :accountId and draftRepositoryId = :repositoryId") + LiveData fetchSingleDraftByAccountIdAndRepositoryId(int accountId, int repositoryId); + + @Query("SELECT * FROM Drafts WHERE draftId = :draftId") + LiveData fetchDraftById(int draftId); + + @Query("SELECT * FROM Drafts WHERE issueId = :issueId") + LiveData fetchDraftByIssueId(int issueId); + + @Query("SELECT count(draftId) FROM Drafts WHERE issueId = :issueId AND draftRepositoryId = :draftRepositoryId") + Integer checkDraftDao(int issueId, int draftRepositoryId); + + @Query("UPDATE Drafts SET draftText= :draftText WHERE draftId = :draftId") + void updateDraft(String draftText, int draftId); + + @Query("UPDATE Drafts SET draftText= :draftText WHERE issueId = :issueId AND draftRepositoryId = :draftRepositoryId") + void updateDraftByIssueId(String draftText, int issueId, int draftRepositoryId); + + @Query("DELETE FROM Drafts WHERE draftId = :draftId") + void deleteByDraftId(int draftId); + + @Query("DELETE FROM Drafts WHERE draftAccountId = :accountId") + void deleteAllDrafts(int accountId); + +} diff --git a/app/src/main/java/org/mian/gitnex/database/dao/RepositoriesDao.java b/app/src/main/java/org/mian/gitnex/database/dao/RepositoriesDao.java new file mode 100644 index 00000000..97eebed7 --- /dev/null +++ b/app/src/main/java/org/mian/gitnex/database/dao/RepositoriesDao.java @@ -0,0 +1,47 @@ +package org.mian.gitnex.database.dao; + +import androidx.lifecycle.LiveData; +import androidx.room.Dao; +import androidx.room.Insert; +import androidx.room.Query; +import org.mian.gitnex.database.models.Repository; +import java.util.List; + +/** + * Author M M Arif + */ + +@Dao +public interface RepositoriesDao { + + @Insert + long newRepository(Repository repositories); + + @Query("SELECT * FROM Repositories ORDER BY repositoryId ASC") + LiveData> fetchAllRepositories(); + + @Query("SELECT * FROM Repositories WHERE repoAccountId = :repoAccountId") + LiveData> getAllRepositoriesByAccountDao(int repoAccountId); + + @Query("SELECT count(repositoryId) FROM Repositories WHERE repoAccountId = :repoAccountId AND repositoryOwner = :repositoryOwner AND repositoryName = :repositoryName") + Integer checkRepositoryDao(int repoAccountId, String repositoryOwner, String repositoryName); + + @Query("SELECT * FROM Repositories WHERE repoAccountId = :repoAccountId AND repositoryOwner = :repositoryOwner AND repositoryName = :repositoryName") + Repository getSingleRepositoryDao(int repoAccountId, String repositoryOwner, String repositoryName); + + @Query("SELECT * FROM Repositories WHERE repositoryId = :repositoryId") + Repository fetchRepositoryByIdDao(int repositoryId); + + @Query("SELECT * FROM Repositories WHERE repositoryId = :repositoryId AND repoAccountId = :repoAccountId") + Repository fetchRepositoryByAccountIdByRepositoryIdDao(int repositoryId, int repoAccountId); + + @Query("UPDATE Repositories SET repositoryOwner = :repositoryOwner, repositoryName = :repositoryName WHERE repositoryId = :repositoryId") + void updateRepositoryOwnerAndName(String repositoryOwner, String repositoryName, int repositoryId); + + @Query("DELETE FROM Repositories WHERE repositoryId = :repositoryId") + void deleteRepository(int repositoryId); + + @Query("DELETE FROM Repositories WHERE repoAccountId = :repoAccountId") + void deleteRepositoriesByAccount(int repoAccountId); + +} diff --git a/app/src/main/java/org/mian/gitnex/database/dao/UserAccountsDao.java b/app/src/main/java/org/mian/gitnex/database/dao/UserAccountsDao.java new file mode 100644 index 00000000..6d8a2909 --- /dev/null +++ b/app/src/main/java/org/mian/gitnex/database/dao/UserAccountsDao.java @@ -0,0 +1,53 @@ +package org.mian.gitnex.database.dao; + +import androidx.lifecycle.LiveData; +import androidx.room.Dao; +import androidx.room.Insert; +import androidx.room.Query; +import org.mian.gitnex.database.models.UserAccount; +import java.util.List; + +/** + * Author M M Arif + */ + +@Dao +public interface UserAccountsDao { + + @Insert + void newAccount(UserAccount userAccounts); + + @Query("SELECT * FROM UserAccounts ORDER BY accountId ASC") + LiveData> fetchAllAccounts(); + + @Query("SELECT COUNT(accountId) FROM UserAccounts WHERE accountName = :accountName") + Integer getCount(String accountName); + + @Query("SELECT * FROM UserAccounts WHERE accountName = :accountName") + UserAccount fetchRowByAccount_(String accountName); + + @Query("SELECT * FROM UserAccounts WHERE accountId = :accountId") + UserAccount fetchRowByAccountId(int accountId); + + @Query("UPDATE UserAccounts SET serverVersion = :serverVersion WHERE accountId = :accountId") + void updateServerVersion(String serverVersion, int accountId); + + @Query("UPDATE UserAccounts SET accountName = :accountName WHERE accountId = :accountId") + void updateAccountName(String accountName, int accountId); + + @Query("UPDATE UserAccounts SET token = :token WHERE accountId = :accountId") + void updateAccountToken(int accountId, String token); + + @Query("UPDATE UserAccounts SET instanceUrl = :instanceUrl, token = :token WHERE accountId = :accountId") + void updateHostInfo(String instanceUrl, String token, int accountId); + + @Query("UPDATE UserAccounts SET userName = :userName WHERE accountId = :accountId") + void updateUserName(String userName, int accountId); + + @Query("UPDATE UserAccounts SET instanceUrl = :instanceUrl, token = :token, userName = :userName, serverVersion = :serverVersion WHERE accountId = :accountId") + void updateAll(String instanceUrl, String token, String userName, String serverVersion, int accountId); + + @Query("DELETE FROM UserAccounts WHERE accountId = :accountId") + void deleteAccount(int accountId); + +} diff --git a/app/src/main/java/org/mian/gitnex/database/db/GitnexDatabase.java b/app/src/main/java/org/mian/gitnex/database/db/GitnexDatabase.java new file mode 100644 index 00000000..b200a34c --- /dev/null +++ b/app/src/main/java/org/mian/gitnex/database/db/GitnexDatabase.java @@ -0,0 +1,55 @@ +package org.mian.gitnex.database.db; + +import android.content.Context; +import androidx.annotation.NonNull; +import androidx.room.Database; +import androidx.room.Room; +import androidx.room.RoomDatabase; +import androidx.room.migration.Migration; +import androidx.sqlite.db.SupportSQLiteDatabase; +import org.mian.gitnex.database.dao.DraftsDao; +import org.mian.gitnex.database.dao.RepositoriesDao; +import org.mian.gitnex.database.dao.UserAccountsDao; +import org.mian.gitnex.database.models.Draft; +import org.mian.gitnex.database.models.Repository; +import org.mian.gitnex.database.models.UserAccount; + +/** + * Author M M Arif + */ + +@Database(entities = {Draft.class, Repository.class, UserAccount.class}, + version = 1, exportSchema = false) +public abstract class GitnexDatabase extends RoomDatabase { + + private static GitnexDatabase gitnexDatabase; + + public static GitnexDatabase getDatabaseInstance(Context context) { + + if (gitnexDatabase == null) { + String DB_NAME = "gitnex"; + gitnexDatabase = Room.databaseBuilder(context, GitnexDatabase.class, DB_NAME) + //.fallbackToDestructiveMigration() + //.addMigrations(MIGRATION_1_2) + .build(); + } + + return gitnexDatabase; + } + + public abstract DraftsDao draftsDao(); + + public abstract RepositoriesDao repositoriesDao(); + + public abstract UserAccountsDao userAccountsDao(); + + private static final Migration MIGRATION_1_2 = new Migration(1, 2) { + @Override + public void migrate(@NonNull SupportSQLiteDatabase database) { + + //database.execSQL("DROP TABLE Drafts"); + //database.execSQL("ALTER TABLE 'Drafts' ADD COLUMN 'draftType' TEXT"); + + } + }; +} diff --git a/app/src/main/java/org/mian/gitnex/database/models/Draft.java b/app/src/main/java/org/mian/gitnex/database/models/Draft.java new file mode 100644 index 00000000..1257abb2 --- /dev/null +++ b/app/src/main/java/org/mian/gitnex/database/models/Draft.java @@ -0,0 +1,89 @@ +package org.mian.gitnex.database.models; + +import androidx.annotation.Nullable; +import androidx.room.Entity; +import androidx.room.ForeignKey; +import androidx.room.Index; +import androidx.room.PrimaryKey; +import java.io.Serializable; +import static androidx.room.ForeignKey.CASCADE; + +/** + * Author M M Arif + */ + +@Entity(tableName = "Drafts", foreignKeys = @ForeignKey(entity = Repository.class, parentColumns = "repositoryId", childColumns = "draftRepositoryId", onDelete = CASCADE), indices = {@Index("draftRepositoryId")}) +public class Draft implements Serializable { + + @PrimaryKey(autoGenerate = true) + private int draftId; + + private int draftRepositoryId; + private int draftAccountId; + private int issueId; + private String draftText; + @Nullable + private String draftType; + + public int getDraftId() { + + return draftId; + } + + public void setDraftId(int draftId) { + + this.draftId = draftId; + } + + public int getDraftRepositoryId() { + + return draftRepositoryId; + } + + public void setDraftRepositoryId(int draftRepositoryId) { + + this.draftRepositoryId = draftRepositoryId; + } + + public int getDraftAccountId() { + + return draftAccountId; + } + + public void setDraftAccountId(int draftAccountId) { + + this.draftAccountId = draftAccountId; + } + + public int getIssueId() { + + return issueId; + } + + public void setIssueId(int issueId) { + + this.issueId = issueId; + } + + public String getDraftText() { + + return draftText; + } + + public void setDraftText(String draftText) { + + this.draftText = draftText; + } + + @Nullable + public String getDraftType() { + + return draftType; + } + + public void setDraftType(@Nullable String draftType) { + + this.draftType = draftType; + } + +} diff --git a/app/src/main/java/org/mian/gitnex/database/models/DraftWithRepository.java b/app/src/main/java/org/mian/gitnex/database/models/DraftWithRepository.java new file mode 100644 index 00000000..7048e423 --- /dev/null +++ b/app/src/main/java/org/mian/gitnex/database/models/DraftWithRepository.java @@ -0,0 +1,122 @@ +package org.mian.gitnex.database.models; + +/** + * Author M M Arif + */ + +public class DraftWithRepository { + + private int repositoryId; + private int draftId; + + private int repoAccountId; + private String repositoryOwner; + private String repositoryName; + + private int draftRepositoryId; + private int draftAccountId; + private int issueId; + private String draftText; + private String draftType; + + public int getRepositoryId() { + + return repositoryId; + } + + public void setRepositoryId(int repositoryId) { + + this.repositoryId = repositoryId; + } + + public int getDraftId() { + + return draftId; + } + + public void setDraftId(int draftId) { + + this.draftId = draftId; + } + + public int getRepoAccountId() { + + return repoAccountId; + } + + public void setRepoAccountId(int repoAccountId) { + + this.repoAccountId = repoAccountId; + } + + public String getRepositoryOwner() { + + return repositoryOwner; + } + + public void setRepositoryOwner(String repositoryOwner) { + + this.repositoryOwner = repositoryOwner; + } + + public String getRepositoryName() { + + return repositoryName; + } + + public void setRepositoryName(String repositoryName) { + + this.repositoryName = repositoryName; + } + + public int getDraftRepositoryId() { + + return draftRepositoryId; + } + + public void setDraftRepositoryId(int draftRepositoryId) { + + this.draftRepositoryId = draftRepositoryId; + } + + public int getDraftAccountId() { + + return draftAccountId; + } + + public void setDraftAccountId(int draftAccountId) { + + this.draftAccountId = draftAccountId; + } + + public int getIssueId() { + + return issueId; + } + + public void setIssueId(int issueId) { + + this.issueId = issueId; + } + + public String getDraftText() { + + return draftText; + } + + public void setDraftText(String draftText) { + + this.draftText = draftText; + } + + public String getDraftType() { + + return draftType; + } + + public void setDraftType(String draftType) { + + this.draftType = draftType; + } + +} diff --git a/app/src/main/java/org/mian/gitnex/database/models/Repository.java b/app/src/main/java/org/mian/gitnex/database/models/Repository.java new file mode 100644 index 00000000..8c340818 --- /dev/null +++ b/app/src/main/java/org/mian/gitnex/database/models/Repository.java @@ -0,0 +1,59 @@ +package org.mian.gitnex.database.models; + +import androidx.room.Entity; +import androidx.room.ForeignKey; +import androidx.room.Index; +import androidx.room.PrimaryKey; +import java.io.Serializable; +import static androidx.room.ForeignKey.CASCADE; + +/** + * Author M M Arif + */ + +@Entity(tableName = "Repositories", foreignKeys = @ForeignKey(entity = UserAccount.class, + parentColumns = "accountId", + childColumns = "repoAccountId", + onDelete = CASCADE), + indices = {@Index("repoAccountId")}) +public class Repository implements Serializable { + + @PrimaryKey(autoGenerate = true) + private int repositoryId; + + private int repoAccountId; + private String repositoryOwner; + private String repositoryName; + + public int getRepositoryId() { + return repositoryId; + } + + public void setRepositoryId(int repositoryId) { + this.repositoryId = repositoryId; + } + + public int getRepoAccountId() { + return repoAccountId; + } + + public void setRepoAccountId(int repoAccountId) { + this.repoAccountId = repoAccountId; + } + + public String getRepositoryOwner() { + return repositoryOwner; + } + + public void setRepositoryOwner(String repositoryOwner) { + this.repositoryOwner = repositoryOwner; + } + + public String getRepositoryName() { + return repositoryName; + } + + public void setRepositoryName(String repositoryName) { + this.repositoryName = repositoryName; + } +} diff --git a/app/src/main/java/org/mian/gitnex/database/models/UserAccount.java b/app/src/main/java/org/mian/gitnex/database/models/UserAccount.java new file mode 100644 index 00000000..3ee77b12 --- /dev/null +++ b/app/src/main/java/org/mian/gitnex/database/models/UserAccount.java @@ -0,0 +1,75 @@ +package org.mian.gitnex.database.models; + +import androidx.annotation.Nullable; +import androidx.room.Entity; +import androidx.room.PrimaryKey; +import java.io.Serializable; + +/** + * Author M M Arif + */ + +@Entity(tableName = "userAccounts") +public class UserAccount implements Serializable { + + @PrimaryKey(autoGenerate = true) + private int accountId; + + @Nullable + private String accountName; + private String instanceUrl; + private String userName; + private String token; + @Nullable + private String serverVersion; + + public int getAccountId() { + return accountId; + } + + public void setAccountId(int accountId) { + this.accountId = accountId; + } + + @Nullable + public String getAccountName() { + return accountName; + } + + public void setAccountName(@Nullable String accountName) { + this.accountName = accountName; + } + + public String getInstanceUrl() { + return instanceUrl; + } + + public void setInstanceUrl(String instanceUrl) { + this.instanceUrl = instanceUrl; + } + + public String getUserName() { + return userName; + } + + public void setUserName(String userName) { + this.userName = userName; + } + + public String getToken() { + return token; + } + + public void setToken(String token) { + this.token = token; + } + + @Nullable + public String getServerVersion() { + return serverVersion; + } + + public void setServerVersion(@Nullable String serverVersion) { + this.serverVersion = serverVersion; + } +} diff --git a/app/src/main/java/org/mian/gitnex/fragments/BottomSheetDraftsFragment.java b/app/src/main/java/org/mian/gitnex/fragments/BottomSheetDraftsFragment.java new file mode 100644 index 00000000..f63039aa --- /dev/null +++ b/app/src/main/java/org/mian/gitnex/fragments/BottomSheetDraftsFragment.java @@ -0,0 +1,58 @@ +package org.mian.gitnex.fragments; + +import android.content.Context; +import android.os.Bundle; +import android.util.Log; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.TextView; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import com.google.android.material.bottomsheet.BottomSheetDialogFragment; +import org.mian.gitnex.R; +import org.mian.gitnex.helpers.StaticGlobalVariables; + +/** + * Author M M Arif + */ + +public class BottomSheetDraftsFragment extends BottomSheetDialogFragment { + + private String TAG = StaticGlobalVariables.tagDraftsBottomSheet; + private BottomSheetDraftsFragment.BottomSheetListener bmListener; + + @Nullable + @Override + public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { + View v = inflater.inflate(R.layout.bottom_sheet_drafts, container, false); + + TextView deleteAllDrafts = v.findViewById(R.id.deleteAllDrafts); + + deleteAllDrafts.setOnClickListener(v1 -> { + + dismiss(); + bmListener.onButtonClicked("deleteDrafts"); + + }); + + return v; + } + + public interface BottomSheetListener { + void onButtonClicked(String text); + } + + @Override + public void onAttach(@NonNull Context context) { + super.onAttach(context); + + try { + bmListener = (BottomSheetDraftsFragment.BottomSheetListener) context; + } + catch (ClassCastException e) { + Log.e(TAG, e.toString()); + } + } + +} diff --git a/app/src/main/java/org/mian/gitnex/fragments/DraftsFragment.java b/app/src/main/java/org/mian/gitnex/fragments/DraftsFragment.java new file mode 100644 index 00000000..155264b7 --- /dev/null +++ b/app/src/main/java/org/mian/gitnex/fragments/DraftsFragment.java @@ -0,0 +1,138 @@ +package org.mian.gitnex.fragments; + +import android.content.Context; +import android.os.Bundle; +import androidx.annotation.NonNull; +import androidx.fragment.app.Fragment; +import androidx.recyclerview.widget.DividerItemDecoration; +import androidx.recyclerview.widget.LinearLayoutManager; +import androidx.recyclerview.widget.RecyclerView; +import androidx.swiperefreshlayout.widget.SwipeRefreshLayout; +import android.os.Handler; +import android.view.LayoutInflater; +import android.view.Menu; +import android.view.MenuInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.TextView; +import org.mian.gitnex.R; +import org.mian.gitnex.adapters.DraftsAdapter; +import org.mian.gitnex.database.models.DraftWithRepository; +import org.mian.gitnex.database.api.DraftsApi; +import org.mian.gitnex.helpers.Toasty; +import org.mian.gitnex.util.TinyDB; +import java.util.ArrayList; +import java.util.List; + +/** + * Author M M Arif + */ + +public class DraftsFragment extends Fragment { + + private Context ctx; + private DraftsAdapter adapter; + private RecyclerView mRecyclerView; + private DraftsApi draftsApi; + private TextView noData; + private List draftsList_; + private int currentActiveAccountId; + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + + View v = inflater.inflate(R.layout.fragment_drafts, container, false); + ctx = getContext(); + setHasOptionsMenu(true); + + TinyDB tinyDb = new TinyDB(ctx); + + draftsList_ = new ArrayList<>(); + draftsApi = new DraftsApi(ctx); + + noData = v.findViewById(R.id.noData); + mRecyclerView = v.findViewById(R.id.recyclerView); + final SwipeRefreshLayout swipeRefresh = v.findViewById(R.id.pullToRefresh); + + mRecyclerView.setHasFixedSize(true); + mRecyclerView.setLayoutManager(new LinearLayoutManager(ctx)); + + DividerItemDecoration dividerItemDecoration = new DividerItemDecoration(mRecyclerView.getContext(), + DividerItemDecoration.VERTICAL); + mRecyclerView.addItemDecoration(dividerItemDecoration); + + adapter = new DraftsAdapter(getContext(), draftsList_); + + swipeRefresh.setOnRefreshListener(() -> new Handler().postDelayed(() -> { + + draftsList_.clear(); + swipeRefresh.setRefreshing(false); + fetchDataAsync(1); + + }, 250)); + + currentActiveAccountId = tinyDb.getInt("currentActiveAccountId"); + + fetchDataAsync(currentActiveAccountId); + + return v; + + } + + private void fetchDataAsync(int accountId) { + + draftsApi.getDrafts(accountId).observe(getViewLifecycleOwner(), drafts -> { + + assert drafts != null; + if(drafts.size() > 0) { + + draftsList_.clear(); + noData.setVisibility(View.GONE); + draftsList_.addAll(drafts); + adapter.notifyDataSetChanged(); + mRecyclerView.setAdapter(adapter); + + } + else { + + noData.setVisibility(View.VISIBLE); + + } + + }); + + } + + @Override + public void onResume() { + super.onResume(); + draftsList_.clear(); + fetchDataAsync(currentActiveAccountId); + } + + public void deleteAllDrafts(int accountId) { + + if(draftsList_.size() > 0) { + + DraftsApi.deleteAllDrafts(accountId); + draftsList_.clear(); + adapter.notifyDataSetChanged(); + Toasty.info(ctx, getResources().getString(R.string.draftsDeleteSuccess)); + + } + else { + Toasty.error(ctx, getResources().getString(R.string.draftsListEmpty)); + } + + } + + @Override + public void onCreateOptionsMenu(@NonNull Menu menu, @NonNull MenuInflater inflater) { + + inflater.inflate(R.menu.generic_nav_dotted_menu, menu); + super.onCreateOptionsMenu(menu, inflater); + + } + +} diff --git a/app/src/main/java/org/mian/gitnex/helpers/AlertDialogs.java b/app/src/main/java/org/mian/gitnex/helpers/AlertDialogs.java index f734b8e7..154804a0 100644 --- a/app/src/main/java/org/mian/gitnex/helpers/AlertDialogs.java +++ b/app/src/main/java/org/mian/gitnex/helpers/AlertDialogs.java @@ -41,6 +41,29 @@ public class AlertDialogs { } + public static void forceLogoutDialog(final Context context, String title, String message, String copyPositiveButton) { + + AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(context) + .setTitle(title) + .setMessage(message) + .setCancelable(false) + .setIcon(R.drawable.ic_info_outline_24dp) + .setPositiveButton(copyPositiveButton, (dialog, which) -> { + + final TinyDB tinyDb = new TinyDB(context); + tinyDb.putBoolean("loggedInMode", false); + tinyDb.remove("basicAuthPassword"); + tinyDb.putBoolean("basicAuthFlag", false); + + Intent intent = new Intent(context, LoginActivity.class); + context.startActivity(intent); + dialog.dismiss(); + + }); + + alertDialogBuilder.create().show(); + } + public static void labelDeleteDialog(final Context context, final String labelTitle, final String labelId, String title, String message, String positiveButton, String negativeButton) { new AlertDialog.Builder(context) diff --git a/app/src/main/java/org/mian/gitnex/helpers/StaticGlobalVariables.java b/app/src/main/java/org/mian/gitnex/helpers/StaticGlobalVariables.java index 9e989b1e..7f261578 100644 --- a/app/src/main/java/org/mian/gitnex/helpers/StaticGlobalVariables.java +++ b/app/src/main/java/org/mian/gitnex/helpers/StaticGlobalVariables.java @@ -15,6 +15,11 @@ public interface StaticGlobalVariables { String tagPullRequestsList = "PullRequestsListFragment"; String tagIssuesList = "IssuesListFragment"; String tagMilestonesAdapter = "MilestonesAdapter"; + String draftsRepository = "DraftsRepository"; + String repositoriesRepository = "RepositoriesRepository"; + String replyToIssueActivity = "ReplyToIssueActivity"; + String tagDraftsBottomSheet = "BottomSheetDraftsFragment"; + String userAccountsRepository = "UserAccountsRepository"; // issues variables int issuesPageInit = 1; @@ -26,4 +31,8 @@ public interface StaticGlobalVariables { // milestone int milestonesPageInit = 1; + // drafts + String draftTypeComment = "comment"; + String draftTypeIssue = "issue"; + } diff --git a/app/src/main/res/drawable/ic_drafts_24dp.xml b/app/src/main/res/drawable/ic_drafts_24dp.xml new file mode 100644 index 00000000..d51695e8 --- /dev/null +++ b/app/src/main/res/drawable/ic_drafts_24dp.xml @@ -0,0 +1,5 @@ + + + diff --git a/app/src/main/res/layout/activity_reply_to_issue.xml b/app/src/main/res/layout/activity_reply_to_issue.xml index 08512628..dd7022cd 100644 --- a/app/src/main/res/layout/activity_reply_to_issue.xml +++ b/app/src/main/res/layout/activity_reply_to_issue.xml @@ -87,6 +87,16 @@ android:textColorHighlight="?attr/primaryTextColor" android:inputType="textCapSentences|textMultiLine" /> + +