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" /> + +